Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/supermerill/SuperSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbubnikv <bubnikv@gmail.com>2018-09-12 12:59:02 +0300
committerbubnikv <bubnikv@gmail.com>2018-09-12 12:59:02 +0300
commit0235f1a8211cfecf75ac0a585295bc1378747972 (patch)
tree9c1d06a43a691d9691c7eff6f21646b0dcc8cc74 /xs/src/libslic3r
parent41ce69f327a2fe3324dc47756b64a6fdb001cfa3 (diff)
parenta97df55592f524eb73bd7d04ef8b7c1b3525d27d (diff)
Merged with dev
Diffstat (limited to 'xs/src/libslic3r')
-rw-r--r--xs/src/libslic3r/BoundingBox.cpp187
-rw-r--r--xs/src/libslic3r/BoundingBox.hpp95
-rw-r--r--xs/src/libslic3r/BridgeDetector.cpp26
-rw-r--r--xs/src/libslic3r/ClipperUtils.cpp45
-rw-r--r--xs/src/libslic3r/ClipperUtils.hpp3
-rw-r--r--xs/src/libslic3r/Config.cpp92
-rw-r--r--xs/src/libslic3r/Config.hpp90
-rw-r--r--xs/src/libslic3r/EdgeGrid.cpp298
-rw-r--r--xs/src/libslic3r/ExPolygon.cpp193
-rw-r--r--xs/src/libslic3r/ExPolygon.hpp1
-rw-r--r--xs/src/libslic3r/ExtrusionEntity.cpp2
-rw-r--r--xs/src/libslic3r/ExtrusionEntity.hpp4
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.cpp1
-rw-r--r--xs/src/libslic3r/ExtrusionEntityCollection.hpp12
-rw-r--r--xs/src/libslic3r/ExtrusionSimulator.cpp44
-rw-r--r--xs/src/libslic3r/FileParserError.hpp52
-rw-r--r--xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp18
-rw-r--r--xs/src/libslic3r/Fill/FillBase.hpp4
-rw-r--r--xs/src/libslic3r/Fill/FillConcentric.cpp4
-rw-r--r--xs/src/libslic3r/Fill/FillGyroid.cpp168
-rw-r--r--xs/src/libslic3r/Fill/FillGyroid.hpp4
-rw-r--r--xs/src/libslic3r/Fill/FillHoneycomb.cpp8
-rw-r--r--xs/src/libslic3r/Fill/FillPlanePath.cpp60
-rw-r--r--xs/src/libslic3r/Fill/FillRectilinear.cpp28
-rw-r--r--xs/src/libslic3r/Fill/FillRectilinear2.cpp72
-rw-r--r--xs/src/libslic3r/Fill/FillRectilinear3.cpp132
-rw-r--r--xs/src/libslic3r/Format/3mf.cpp447
-rw-r--r--xs/src/libslic3r/Format/3mf.hpp2
-rw-r--r--xs/src/libslic3r/Format/AMF.cpp183
-rw-r--r--xs/src/libslic3r/Format/AMF.hpp2
-rw-r--r--xs/src/libslic3r/Format/OBJ.cpp34
-rw-r--r--xs/src/libslic3r/Format/PRUS.cpp22
-rw-r--r--xs/src/libslic3r/GCode.cpp760
-rw-r--r--xs/src/libslic3r/GCode.hpp55
-rw-r--r--xs/src/libslic3r/GCode/Analyzer.cpp123
-rw-r--r--xs/src/libslic3r/GCode/Analyzer.hpp34
-rw-r--r--xs/src/libslic3r/GCode/CoolingBuffer.cpp929
-rw-r--r--xs/src/libslic3r/GCode/CoolingBuffer.hpp28
-rw-r--r--xs/src/libslic3r/GCode/PreviewData.cpp103
-rw-r--r--xs/src/libslic3r/GCode/PreviewData.hpp35
-rw-r--r--xs/src/libslic3r/GCode/PrintExtents.cpp55
-rw-r--r--xs/src/libslic3r/GCode/ToolOrdering.cpp327
-rw-r--r--xs/src/libslic3r/GCode/ToolOrdering.hpp142
-rw-r--r--xs/src/libslic3r/GCode/WipeTower.hpp36
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp1344
-rw-r--r--xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp305
-rw-r--r--xs/src/libslic3r/GCodeReader.cpp22
-rw-r--r--xs/src/libslic3r/GCodeReader.hpp1
-rw-r--r--xs/src/libslic3r/GCodeSender.cpp51
-rw-r--r--xs/src/libslic3r/GCodeSender.hpp2
-rw-r--r--xs/src/libslic3r/GCodeTimeEstimator.cpp513
-rw-r--r--xs/src/libslic3r/GCodeTimeEstimator.hpp111
-rw-r--r--xs/src/libslic3r/GCodeWriter.cpp64
-rw-r--r--xs/src/libslic3r/GCodeWriter.hpp17
-rw-r--r--xs/src/libslic3r/Geometry.cpp243
-rw-r--r--xs/src/libslic3r/Geometry.hpp38
-rw-r--r--xs/src/libslic3r/I18N.hpp18
-rw-r--r--xs/src/libslic3r/Int128.hpp17
-rw-r--r--xs/src/libslic3r/Layer.cpp8
-rw-r--r--xs/src/libslic3r/LayerRegion.cpp8
-rw-r--r--xs/src/libslic3r/Line.cpp265
-rw-r--r--xs/src/libslic3r/Line.hpp103
-rw-r--r--xs/src/libslic3r/Model.cpp392
-rw-r--r--xs/src/libslic3r/Model.hpp103
-rw-r--r--xs/src/libslic3r/ModelArrange.hpp788
-rw-r--r--xs/src/libslic3r/MotionPlanner.cpp10
-rw-r--r--xs/src/libslic3r/MultiPoint.cpp125
-rw-r--r--xs/src/libslic3r/MultiPoint.hpp12
-rw-r--r--xs/src/libslic3r/PerimeterGenerator.cpp414
-rw-r--r--xs/src/libslic3r/PerimeterGenerator.hpp5
-rw-r--r--xs/src/libslic3r/PlaceholderParser.cpp43
-rw-r--r--xs/src/libslic3r/Point.cpp318
-rw-r--r--xs/src/libslic3r/Point.hpp286
-rw-r--r--xs/src/libslic3r/Polygon.cpp67
-rw-r--r--xs/src/libslic3r/Polygon.hpp12
-rw-r--r--xs/src/libslic3r/Polyline.cpp126
-rw-r--r--xs/src/libslic3r/Polyline.hpp27
-rw-r--r--xs/src/libslic3r/PolylineCollection.cpp10
-rw-r--r--xs/src/libslic3r/Print.cpp326
-rw-r--r--xs/src/libslic3r/Print.hpp56
-rw-r--r--xs/src/libslic3r/PrintConfig.cpp688
-rw-r--r--xs/src/libslic3r/PrintConfig.hpp257
-rw-r--r--xs/src/libslic3r/PrintExport.hpp383
-rw-r--r--xs/src/libslic3r/PrintObject.cpp185
-rw-r--r--xs/src/libslic3r/Rasterizer/Rasterizer.cpp214
-rw-r--r--xs/src/libslic3r/Rasterizer/Rasterizer.hpp86
-rw-r--r--xs/src/libslic3r/SLABasePool.cpp531
-rw-r--r--xs/src/libslic3r/SLABasePool.hpp32
-rw-r--r--xs/src/libslic3r/SVG.cpp52
-rw-r--r--xs/src/libslic3r/Slicing.cpp54
-rw-r--r--xs/src/libslic3r/SlicingAdaptive.cpp6
-rw-r--r--xs/src/libslic3r/SupportMaterial.cpp60
-rw-r--r--xs/src/libslic3r/Surface.cpp6
-rw-r--r--xs/src/libslic3r/SurfaceCollection.cpp4
-rw-r--r--xs/src/libslic3r/TriangleMesh.cpp619
-rw-r--r--xs/src/libslic3r/TriangleMesh.hpp48
-rw-r--r--xs/src/libslic3r/Utils.hpp28
-rw-r--r--xs/src/libslic3r/libslic3r.h17
-rw-r--r--xs/src/libslic3r/utils.cpp256
99 files changed, 9844 insertions, 4892 deletions
diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp
index 91ba88d84..d3cca7ff2 100644
--- a/xs/src/libslic3r/BoundingBox.cpp
+++ b/xs/src/libslic3r/BoundingBox.cpp
@@ -2,12 +2,14 @@
#include <algorithm>
#include <assert.h>
+#include <Eigen/Dense>
+
namespace Slic3r {
template BoundingBoxBase<Point>::BoundingBoxBase(const std::vector<Point> &points);
-template BoundingBoxBase<Pointf>::BoundingBoxBase(const std::vector<Pointf> &points);
+template BoundingBoxBase<Vec2d>::BoundingBoxBase(const std::vector<Vec2d> &points);
-template BoundingBox3Base<Pointf3>::BoundingBox3Base(const std::vector<Pointf3> &points);
+template BoundingBox3Base<Vec3d>::BoundingBox3Base(const std::vector<Vec3d> &points);
BoundingBox::BoundingBox(const Lines &lines)
{
@@ -20,23 +22,21 @@ BoundingBox::BoundingBox(const Lines &lines)
*this = BoundingBox(points);
}
-void
-BoundingBox::polygon(Polygon* polygon) const
+void BoundingBox::polygon(Polygon* polygon) const
{
polygon->points.clear();
polygon->points.resize(4);
- polygon->points[0].x = this->min.x;
- polygon->points[0].y = this->min.y;
- polygon->points[1].x = this->max.x;
- polygon->points[1].y = this->min.y;
- polygon->points[2].x = this->max.x;
- polygon->points[2].y = this->max.y;
- polygon->points[3].x = this->min.x;
- polygon->points[3].y = this->max.y;
+ polygon->points[0](0) = this->min(0);
+ polygon->points[0](1) = this->min(1);
+ polygon->points[1](0) = this->max(0);
+ polygon->points[1](1) = this->min(1);
+ polygon->points[2](0) = this->max(0);
+ polygon->points[2](1) = this->max(1);
+ polygon->points[3](0) = this->min(0);
+ polygon->points[3](1) = this->max(1);
}
-Polygon
-BoundingBox::polygon() const
+Polygon BoundingBox::polygon() const
{
Polygon p;
this->polygon(&p);
@@ -48,8 +48,8 @@ BoundingBox BoundingBox::rotated(double angle) const
BoundingBox out;
out.merge(this->min.rotated(angle));
out.merge(this->max.rotated(angle));
- out.merge(Point(this->min.x, this->max.y).rotated(angle));
- out.merge(Point(this->max.x, this->min.y).rotated(angle));
+ out.merge(Point(this->min(0), this->max(1)).rotated(angle));
+ out.merge(Point(this->max(0), this->min(1)).rotated(angle));
return out;
}
@@ -58,36 +58,35 @@ BoundingBox BoundingBox::rotated(double angle, const Point &center) const
BoundingBox out;
out.merge(this->min.rotated(angle, center));
out.merge(this->max.rotated(angle, center));
- out.merge(Point(this->min.x, this->max.y).rotated(angle, center));
- out.merge(Point(this->max.x, this->min.y).rotated(angle, center));
+ out.merge(Point(this->min(0), this->max(1)).rotated(angle, center));
+ out.merge(Point(this->max(0), this->min(1)).rotated(angle, center));
return out;
}
template <class PointClass> void
BoundingBoxBase<PointClass>::scale(double factor)
{
- this->min.scale(factor);
- this->max.scale(factor);
+ this->min *= factor;
+ this->max *= factor;
}
template void BoundingBoxBase<Point>::scale(double factor);
-template void BoundingBoxBase<Pointf>::scale(double factor);
-template void BoundingBoxBase<Pointf3>::scale(double factor);
+template void BoundingBoxBase<Vec2d>::scale(double factor);
+template void BoundingBoxBase<Vec3d>::scale(double factor);
template <class PointClass> void
BoundingBoxBase<PointClass>::merge(const PointClass &point)
{
if (this->defined) {
- this->min.x = std::min(point.x, this->min.x);
- this->min.y = std::min(point.y, this->min.y);
- this->max.x = std::max(point.x, this->max.x);
- this->max.y = std::max(point.y, this->max.y);
+ this->min = this->min.cwiseMin(point);
+ this->max = this->max.cwiseMax(point);
} else {
- this->min = this->max = point;
+ this->min = point;
+ this->max = point;
this->defined = true;
}
}
template void BoundingBoxBase<Point>::merge(const Point &point);
-template void BoundingBoxBase<Pointf>::merge(const Pointf &point);
+template void BoundingBoxBase<Vec2d>::merge(const Vec2d &point);
template <class PointClass> void
BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
@@ -95,18 +94,16 @@ BoundingBoxBase<PointClass>::merge(const std::vector<PointClass> &points)
this->merge(BoundingBoxBase(points));
}
template void BoundingBoxBase<Point>::merge(const Points &points);
-template void BoundingBoxBase<Pointf>::merge(const Pointfs &points);
+template void BoundingBoxBase<Vec2d>::merge(const Pointfs &points);
template <class PointClass> void
BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
{
- assert(bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y);
+ assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1));
if (bb.defined) {
if (this->defined) {
- this->min.x = std::min(bb.min.x, this->min.x);
- this->min.y = std::min(bb.min.y, this->min.y);
- this->max.x = std::max(bb.max.x, this->max.x);
- this->max.y = std::max(bb.max.y, this->max.y);
+ this->min = this->min.cwiseMin(bb.min);
+ this->max = this->max.cwiseMax(bb.max);
} else {
this->min = bb.min;
this->max = bb.max;
@@ -115,112 +112,121 @@ BoundingBoxBase<PointClass>::merge(const BoundingBoxBase<PointClass> &bb)
}
}
template void BoundingBoxBase<Point>::merge(const BoundingBoxBase<Point> &bb);
-template void BoundingBoxBase<Pointf>::merge(const BoundingBoxBase<Pointf> &bb);
+template void BoundingBoxBase<Vec2d>::merge(const BoundingBoxBase<Vec2d> &bb);
template <class PointClass> void
BoundingBox3Base<PointClass>::merge(const PointClass &point)
{
if (this->defined) {
- this->min.z = std::min(point.z, this->min.z);
- this->max.z = std::max(point.z, this->max.z);
+ this->min = this->min.cwiseMin(point);
+ this->max = this->max.cwiseMax(point);
+ } else {
+ this->min = point;
+ this->max = point;
+ this->defined = true;
}
- BoundingBoxBase<PointClass>::merge(point);
}
-template void BoundingBox3Base<Pointf3>::merge(const Pointf3 &point);
+template void BoundingBox3Base<Vec3d>::merge(const Vec3d &point);
template <class PointClass> void
BoundingBox3Base<PointClass>::merge(const std::vector<PointClass> &points)
{
this->merge(BoundingBox3Base(points));
}
-template void BoundingBox3Base<Pointf3>::merge(const Pointf3s &points);
+template void BoundingBox3Base<Vec3d>::merge(const Pointf3s &points);
template <class PointClass> void
BoundingBox3Base<PointClass>::merge(const BoundingBox3Base<PointClass> &bb)
{
- assert(bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y || bb.min.z >= bb.max.z);
+ assert(bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2));
if (bb.defined) {
if (this->defined) {
- this->min.z = std::min(bb.min.z, this->min.z);
- this->max.z = std::max(bb.max.z, this->max.z);
+ this->min = this->min.cwiseMin(bb.min);
+ this->max = this->max.cwiseMax(bb.max);
+ } else {
+ this->min = bb.min;
+ this->max = bb.max;
+ this->defined = true;
}
- BoundingBoxBase<PointClass>::merge(bb);
}
}
-template void BoundingBox3Base<Pointf3>::merge(const BoundingBox3Base<Pointf3> &bb);
+template void BoundingBox3Base<Vec3d>::merge(const BoundingBox3Base<Vec3d> &bb);
template <class PointClass> PointClass
BoundingBoxBase<PointClass>::size() const
{
- return PointClass(this->max.x - this->min.x, this->max.y - this->min.y);
+ return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1));
}
template Point BoundingBoxBase<Point>::size() const;
-template Pointf BoundingBoxBase<Pointf>::size() const;
+template Vec2d BoundingBoxBase<Vec2d>::size() const;
template <class PointClass> PointClass
BoundingBox3Base<PointClass>::size() const
{
- return PointClass(this->max.x - this->min.x, this->max.y - this->min.y, this->max.z - this->min.z);
+ return PointClass(this->max(0) - this->min(0), this->max(1) - this->min(1), this->max(2) - this->min(2));
}
-template Pointf3 BoundingBox3Base<Pointf3>::size() const;
+template Vec3d BoundingBox3Base<Vec3d>::size() const;
template <class PointClass> double BoundingBoxBase<PointClass>::radius() const
{
assert(this->defined);
- double x = this->max.x - this->min.x;
- double y = this->max.y - this->min.y;
+ double x = this->max(0) - this->min(0);
+ double y = this->max(1) - this->min(1);
return 0.5 * sqrt(x*x+y*y);
}
template double BoundingBoxBase<Point>::radius() const;
-template double BoundingBoxBase<Pointf>::radius() const;
+template double BoundingBoxBase<Vec2d>::radius() const;
template <class PointClass> double BoundingBox3Base<PointClass>::radius() const
{
- double x = this->max.x - this->min.x;
- double y = this->max.y - this->min.y;
- double z = this->max.z - this->min.z;
+ double x = this->max(0) - this->min(0);
+ double y = this->max(1) - this->min(1);
+ double z = this->max(2) - this->min(2);
return 0.5 * sqrt(x*x+y*y+z*z);
}
-template double BoundingBox3Base<Pointf3>::radius() const;
+template double BoundingBox3Base<Vec3d>::radius() const;
template <class PointClass> void
BoundingBoxBase<PointClass>::offset(coordf_t delta)
{
- this->min.translate(-delta, -delta);
- this->max.translate(delta, delta);
+ PointClass v(delta, delta);
+ this->min -= v;
+ this->max += v;
}
template void BoundingBoxBase<Point>::offset(coordf_t delta);
-template void BoundingBoxBase<Pointf>::offset(coordf_t delta);
+template void BoundingBoxBase<Vec2d>::offset(coordf_t delta);
template <class PointClass> void
BoundingBox3Base<PointClass>::offset(coordf_t delta)
{
- this->min.translate(-delta, -delta, -delta);
- this->max.translate(delta, delta, delta);
+ PointClass v(delta, delta, delta);
+ this->min -= v;
+ this->max += v;
}
-template void BoundingBox3Base<Pointf3>::offset(coordf_t delta);
+template void BoundingBox3Base<Vec3d>::offset(coordf_t delta);
template <class PointClass> PointClass
BoundingBoxBase<PointClass>::center() const
{
- return PointClass(
- (this->max.x + this->min.x)/2,
- (this->max.y + this->min.y)/2
- );
+ return (this->min + this->max) / 2;
}
template Point BoundingBoxBase<Point>::center() const;
-template Pointf BoundingBoxBase<Pointf>::center() const;
+template Vec2d BoundingBoxBase<Vec2d>::center() const;
template <class PointClass> PointClass
BoundingBox3Base<PointClass>::center() const
{
- return PointClass(
- (this->max.x + this->min.x)/2,
- (this->max.y + this->min.y)/2,
- (this->max.z + this->min.z)/2
- );
+ return (this->min + this->max) / 2;
+}
+template Vec3d BoundingBox3Base<Vec3d>::center() const;
+
+template <class PointClass> coordf_t
+BoundingBox3Base<PointClass>::max_size() const
+{
+ PointClass s = size();
+ return std::max(s(0), std::max(s(1), s(2)));
}
-template Pointf3 BoundingBox3Base<Pointf3>::center() const;
+template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
// Align a coordinate to a grid. The coordinate may be negative,
// the aligned value will never be bigger than the original one.
@@ -238,9 +244,40 @@ static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing)
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);
+ min(0) = _align_to_grid(min(0), cell_size);
+ min(1) = _align_to_grid(min(1), cell_size);
}
}
+BoundingBoxf3 BoundingBoxf3::transformed(const Transform3d& matrix) const
+{
+ typedef Eigen::Matrix<double, 3, 8, Eigen::DontAlign> Vertices;
+
+ Vertices src_vertices;
+ src_vertices(0, 0) = min(0); src_vertices(1, 0) = min(1); src_vertices(2, 0) = min(2);
+ src_vertices(0, 1) = max(0); src_vertices(1, 1) = min(1); src_vertices(2, 1) = min(2);
+ src_vertices(0, 2) = max(0); src_vertices(1, 2) = max(1); src_vertices(2, 2) = min(2);
+ src_vertices(0, 3) = min(0); src_vertices(1, 3) = max(1); src_vertices(2, 3) = min(2);
+ src_vertices(0, 4) = min(0); src_vertices(1, 4) = min(1); src_vertices(2, 4) = max(2);
+ src_vertices(0, 5) = max(0); src_vertices(1, 5) = min(1); src_vertices(2, 5) = max(2);
+ src_vertices(0, 6) = max(0); src_vertices(1, 6) = max(1); src_vertices(2, 6) = max(2);
+ src_vertices(0, 7) = min(0); src_vertices(1, 7) = max(1); src_vertices(2, 7) = max(2);
+
+ Vertices dst_vertices = matrix * src_vertices.colwise().homogeneous();
+
+ Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0));
+ Vec3d v_max = v_min;
+
+ for (int i = 1; i < 8; ++i)
+ {
+ for (int j = 0; j < 3; ++j)
+ {
+ v_min(j) = std::min(v_min(j), dst_vertices(j, i));
+ v_max(j) = std::max(v_max(j), dst_vertices(j, i));
+ }
+ }
+
+ return BoundingBoxf3(v_min, v_max);
+}
+
}
diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp
index 92a2bd451..db56c765c 100644
--- a/xs/src/libslic3r/BoundingBox.hpp
+++ b/xs/src/libslic3r/BoundingBox.hpp
@@ -7,11 +7,6 @@
namespace Slic3r {
-typedef Point Size;
-typedef Point3 Size3;
-typedef Pointf Sizef;
-typedef Pointf3 Sizef3;
-
template <class PointClass>
class BoundingBoxBase
{
@@ -20,25 +15,22 @@ public:
PointClass max;
bool defined;
- BoundingBoxBase() : defined(false) {};
+ BoundingBoxBase() : defined(false), min(PointClass::Zero()), max(PointClass::Zero()) {}
BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) :
- min(pmin), max(pmax), defined(pmin.x < pmax.x && pmin.y < pmax.y) {}
- BoundingBoxBase(const std::vector<PointClass>& points)
+ min(pmin), max(pmax), defined(pmin(0) < pmax(0) && pmin(1) < pmax(1)) {}
+ BoundingBoxBase(const std::vector<PointClass>& points) : min(PointClass::Zero()), max(PointClass::Zero())
{
if (points.empty())
CONFESS("Empty point set supplied to BoundingBoxBase constructor");
typename std::vector<PointClass>::const_iterator it = points.begin();
- this->min.x = this->max.x = it->x;
- this->min.y = this->max.y = it->y;
- for (++it; it != points.end(); ++it)
- {
- this->min.x = std::min(it->x, this->min.x);
- this->min.y = std::min(it->y, this->min.y);
- this->max.x = std::max(it->x, this->max.x);
- this->max.y = std::max(it->y, this->max.y);
+ this->min = *it;
+ this->max = *it;
+ for (++ it; it != points.end(); ++ it) {
+ this->min = this->min.cwiseMin(*it);
+ this->max = this->max.cwiseMax(*it);
}
- this->defined = (this->min.x < this->max.x) && (this->min.y < this->max.y);
+ this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1));
}
void merge(const PointClass &point);
void merge(const std::vector<PointClass> &points);
@@ -46,17 +38,17 @@ public:
void scale(double factor);
PointClass size() const;
double radius() const;
- void translate(coordf_t x, coordf_t y) { assert(this->defined); this->min.translate(x, y); this->max.translate(x, y); }
- void translate(const Pointf &pos) { this->translate(pos.x, pos.y); }
+ void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; }
+ void translate(const Vec2d &v) { this->min += v; this->max += v; }
void offset(coordf_t delta);
PointClass center() const;
bool contains(const PointClass &point) const {
- return point.x >= this->min.x && point.x <= this->max.x
- && point.y >= this->min.y && point.y <= this->max.y;
+ return point(0) >= this->min(0) && point(0) <= this->max(0)
+ && point(1) >= this->min(1) && point(1) <= this->max(1);
}
bool overlap(const BoundingBoxBase<PointClass> &other) const {
- return ! (this->max.x < other.min.x || this->min.x > other.max.x ||
- this->max.y < other.min.y || this->min.y > other.max.y);
+ return ! (this->max(0) < other.min(0) || this->min(0) > other.max(0) ||
+ this->max(1) < other.min(1) || this->min(1) > other.max(1));
}
bool operator==(const BoundingBoxBase<PointClass> &rhs) { return this->min == rhs.min && this->max == rhs.max; }
bool operator!=(const BoundingBoxBase<PointClass> &rhs) { return ! (*this == rhs); }
@@ -69,39 +61,42 @@ public:
BoundingBox3Base() : BoundingBoxBase<PointClass>() {};
BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) :
BoundingBoxBase<PointClass>(pmin, pmax)
- { if (pmin.z >= pmax.z) BoundingBoxBase<PointClass>::defined = false; }
+ { if (pmin(2) >= pmax(2)) BoundingBoxBase<PointClass>::defined = false; }
BoundingBox3Base(const std::vector<PointClass>& points)
- : BoundingBoxBase<PointClass>(points)
{
if (points.empty())
CONFESS("Empty point set supplied to BoundingBox3Base constructor");
-
typename std::vector<PointClass>::const_iterator it = points.begin();
- this->min.z = this->max.z = it->z;
- for (++it; it != points.end(); ++it)
- {
- this->min.z = std::min(it->z, this->min.z);
- this->max.z = std::max(it->z, this->max.z);
+ this->min = *it;
+ this->max = *it;
+ for (++ it; it != points.end(); ++ it) {
+ this->min = this->min.cwiseMin(*it);
+ this->max = this->max.cwiseMax(*it);
}
- this->defined &= (this->min.z < this->max.z);
+ this->defined = (this->min(0) < this->max(0)) && (this->min(1) < this->max(1)) && (this->min(2) < this->max(2));
}
void merge(const PointClass &point);
void merge(const std::vector<PointClass> &points);
void merge(const BoundingBox3Base<PointClass> &bb);
PointClass size() const;
double radius() const;
- void translate(coordf_t x, coordf_t y, coordf_t z) { this->min.translate(x, y, z); this->max.translate(x, y, z); }
- void translate(const Pointf3 &pos) { this->translate(pos.x, pos.y, pos.z); }
+ void translate(coordf_t x, coordf_t y, coordf_t z) { assert(this->defined); PointClass v(x, y, z); this->min += v; this->max += v; }
+ void translate(const Vec3d &v) { this->min += v; this->max += v; }
void offset(coordf_t delta);
PointClass center() const;
+ coordf_t max_size() const;
bool contains(const PointClass &point) const {
- return BoundingBoxBase<PointClass>::contains(point) && point.z >= this->min.z && point.z <= this->max.z;
+ return BoundingBoxBase<PointClass>::contains(point) && point(2) >= this->min(2) && point(2) <= this->max(2);
}
bool contains(const BoundingBox3Base<PointClass>& other) const {
return contains(other.min) && contains(other.max);
}
+
+ bool intersects(const BoundingBox3Base<PointClass>& other) const {
+ return (this->min(0) < other.max(0)) && (this->max(0) > other.min(0)) && (this->min(1) < other.max(1)) && (this->max(1) > other.min(1)) && (this->min(2) < other.max(2)) && (this->max(2) > other.min(2));
+ }
};
class BoundingBox : public BoundingBoxBase<Point>
@@ -125,40 +120,42 @@ public:
friend BoundingBox get_extents_rotated(const Points &points, double angle);
};
-class BoundingBox3 : public BoundingBox3Base<Point3>
+class BoundingBox3 : public BoundingBox3Base<Vec3crd>
{
public:
- BoundingBox3() : BoundingBox3Base<Point3>() {};
- BoundingBox3(const Point3 &pmin, const Point3 &pmax) : BoundingBox3Base<Point3>(pmin, pmax) {};
- BoundingBox3(const Points3& points) : BoundingBox3Base<Point3>(points) {};
+ BoundingBox3() : BoundingBox3Base<Vec3crd>() {};
+ BoundingBox3(const Vec3crd &pmin, const Vec3crd &pmax) : BoundingBox3Base<Vec3crd>(pmin, pmax) {};
+ BoundingBox3(const Points3& points) : BoundingBox3Base<Vec3crd>(points) {};
};
-class BoundingBoxf : public BoundingBoxBase<Pointf>
+class BoundingBoxf : public BoundingBoxBase<Vec2d>
{
public:
- BoundingBoxf() : BoundingBoxBase<Pointf>() {};
- BoundingBoxf(const Pointf &pmin, const Pointf &pmax) : BoundingBoxBase<Pointf>(pmin, pmax) {};
- BoundingBoxf(const std::vector<Pointf> &points) : BoundingBoxBase<Pointf>(points) {};
+ BoundingBoxf() : BoundingBoxBase<Vec2d>() {};
+ BoundingBoxf(const Vec2d &pmin, const Vec2d &pmax) : BoundingBoxBase<Vec2d>(pmin, pmax) {};
+ BoundingBoxf(const std::vector<Vec2d> &points) : BoundingBoxBase<Vec2d>(points) {};
};
-class BoundingBoxf3 : public BoundingBox3Base<Pointf3>
+class BoundingBoxf3 : public BoundingBox3Base<Vec3d>
{
public:
- BoundingBoxf3() : BoundingBox3Base<Pointf3>() {};
- BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base<Pointf3>(pmin, pmax) {};
- BoundingBoxf3(const std::vector<Pointf3> &points) : BoundingBox3Base<Pointf3>(points) {};
+ BoundingBoxf3() : BoundingBox3Base<Vec3d>() {};
+ BoundingBoxf3(const Vec3d &pmin, const Vec3d &pmax) : BoundingBox3Base<Vec3d>(pmin, pmax) {};
+ BoundingBoxf3(const std::vector<Vec3d> &points) : BoundingBox3Base<Vec3d>(points) {};
+
+ BoundingBoxf3 transformed(const Transform3d& matrix) const;
};
template<typename VT>
inline bool empty(const BoundingBoxBase<VT> &bb)
{
- return ! bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y;
+ return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1);
}
template<typename VT>
inline bool empty(const BoundingBox3Base<VT> &bb)
{
- return ! bb.defined || bb.min.x >= bb.max.x || bb.min.y >= bb.max.y || bb.min.z >= bb.max.z;
+ return ! bb.defined || bb.min(0) >= bb.max(0) || bb.min(1) >= bb.max(1) || bb.min(2) >= bb.max(2);
}
} // namespace Slic3r
diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp
index a5272683f..ccc3505ce 100644
--- a/xs/src/libslic3r/BridgeDetector.cpp
+++ b/xs/src/libslic3r/BridgeDetector.cpp
@@ -102,16 +102,16 @@ bool BridgeDetector::detect_angle(double bridge_direction_override)
// Get an oriented bounding box around _anchor_regions.
BoundingBox bbox = get_extents_rotated(this->_anchor_regions, - angle);
// Cover the region with line segments.
- lines.reserve((bbox.max.y - bbox.min.y + this->spacing) / this->spacing);
+ lines.reserve((bbox.max(1) - bbox.min(1) + this->spacing) / this->spacing);
double s = sin(angle);
double c = cos(angle);
//FIXME Vojtech: The lines shall be spaced half the line width from the edge, but then
// some of the test cases fail. Need to adjust the test cases then?
-// for (coord_t y = bbox.min.y + this->spacing / 2; y <= bbox.max.y; y += this->spacing)
- for (coord_t y = bbox.min.y; y <= bbox.max.y; y += this->spacing)
+// for (coord_t y = bbox.min(1) + this->spacing / 2; y <= bbox.max(1); y += this->spacing)
+ for (coord_t y = bbox.min(1); y <= bbox.max(1); y += this->spacing)
lines.push_back(Line(
- Point((coord_t)round(c * bbox.min.x - s * y), (coord_t)round(c * y + s * bbox.min.x)),
- Point((coord_t)round(c * bbox.max.x - s * y), (coord_t)round(c * y + s * bbox.max.x))));
+ Point((coord_t)round(c * bbox.min(0) - s * y), (coord_t)round(c * y + s * bbox.min(0))),
+ Point((coord_t)round(c * bbox.max(0) - s * y), (coord_t)round(c * y + s * bbox.max(0)))));
}
double total_length = 0;
@@ -182,9 +182,9 @@ std::vector<double> BridgeDetector::bridge_direction_candidates() const
/* we also test angles of each open supporting edge
(this finds the optimal angle for C-shaped supports) */
- for (Polylines::const_iterator edge = this->_edges.begin(); edge != this->_edges.end(); ++edge)
- if (! edge->first_point().coincides_with(edge->last_point()))
- angles.push_back(Line(edge->first_point(), edge->last_point()).direction());
+ for (const Polyline &edge : this->_edges)
+ if (edge.first_point() != edge.last_point())
+ angles.push_back(Line(edge.first_point(), edge.last_point()).direction());
// remove duplicates
double min_resolution = PI/180.0; // 1 degree
@@ -282,10 +282,12 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const
extrusions would be anchored within such length (i.e. a slightly non-parallel bridging
direction might still benefit from anchors if long enough)
double angle_tolerance = PI / 180.0 * 5.0; */
- for (Lines::const_iterator line = unsupported_lines.begin(); line != unsupported_lines.end(); ++line) {
- if (!Slic3r::Geometry::directions_parallel(line->direction(), angle))
- unsupported->push_back(*line);
- }
+ for (const Line &line : unsupported_lines)
+ if (! Slic3r::Geometry::directions_parallel(line.direction(), angle)) {
+ unsupported->emplace_back(Polyline());
+ unsupported->back().points.emplace_back(line.a);
+ unsupported->back().points.emplace_back(line.b);
+ }
}
/*
diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp
index 86123b23c..f00e908ce 100644
--- a/xs/src/libslic3r/ClipperUtils.cpp
+++ b/xs/src/libslic3r/ClipperUtils.cpp
@@ -171,7 +171,7 @@ Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input)
{
ClipperLib::Path retval;
for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit)
- retval.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
+ retval.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) ));
return retval;
}
@@ -181,7 +181,7 @@ Slic3rMultiPoint_to_ClipperPath_reversed(const Slic3r::MultiPoint &input)
ClipperLib::Path output;
output.reserve(input.points.size());
for (Slic3r::Points::const_reverse_iterator pit = input.points.rbegin(); pit != input.points.rend(); ++pit)
- output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y ));
+ output.push_back(ClipperLib::IntPoint( (*pit)(0), (*pit)(1) ));
return output;
}
@@ -458,6 +458,19 @@ offset2_ex(const Polygons &polygons, const float delta1, const float delta2,
return ClipperPaths_to_Slic3rExPolygons(output);
}
+//FIXME Vojtech: This functon may likely be optimized to avoid some of the Slic3r to Clipper
+// conversions and unnecessary Clipper calls.
+ExPolygons offset2_ex(const ExPolygons &expolygons, const float delta1,
+ const float delta2, ClipperLib::JoinType joinType, double miterLimit)
+{
+ Polygons polys;
+ for (const ExPolygon &expoly : expolygons)
+ append(polys,
+ offset(offset_ex(expoly, delta1, joinType, miterLimit),
+ delta2, joinType, miterLimit));
+ return union_ex(polys);
+}
+
template <class T>
T
_clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject,
@@ -582,26 +595,26 @@ Polylines _clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, co
to recombine continuous polylines. */
for (size_t i = 0; i < retval.size(); ++i) {
for (size_t j = i+1; j < retval.size(); ++j) {
- if (retval[i].points.back().coincides_with(retval[j].points.front())) {
+ if (retval[i].points.back() == retval[j].points.front()) {
/* If last point of i coincides with first point of j,
append points of j to i and delete j */
retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end());
retval.erase(retval.begin() + j);
--j;
- } else if (retval[i].points.front().coincides_with(retval[j].points.back())) {
+ } else if (retval[i].points.front() == retval[j].points.back()) {
/* If first point of i coincides with last point of j,
prepend points of j to i and delete j */
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
retval.erase(retval.begin() + j);
--j;
- } else if (retval[i].points.front().coincides_with(retval[j].points.front())) {
+ } else if (retval[i].points.front() == retval[j].points.front()) {
/* Since Clipper does not preserve orientation of polylines,
also check the case when first point of i coincides with first point of j. */
retval[j].reverse();
retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1);
retval.erase(retval.begin() + j);
--j;
- } else if (retval[i].points.back().coincides_with(retval[j].points.back())) {
+ } else if (retval[i].points.back() == retval[j].points.back()) {
/* Since Clipper does not preserve orientation of polylines,
also check the case when last point of i coincides with last point of j. */
retval[j].reverse();
@@ -621,8 +634,8 @@ _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons
// convert Lines to Polylines
Polylines polylines;
polylines.reserve(subject.size());
- for (Lines::const_iterator line = subject.begin(); line != subject.end(); ++line)
- polylines.push_back(*line);
+ for (const Line &line : subject)
+ polylines.emplace_back(Polyline(line.a, line.b));
// perform operation
polylines = _clipper_pl(clipType, polylines, clip, safety_offset_);
@@ -650,8 +663,7 @@ union_pt_chained(const Polygons &subject, bool safety_offset_)
return retval;
}
-void
-traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
+void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
{
/* use a nearest neighbor search to order these children
TODO: supply start_near to chained_path() too? */
@@ -677,8 +689,7 @@ traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval)
}
}
-Polygons
-simplify_polygons(const Polygons &subject, bool preserve_collinear)
+Polygons simplify_polygons(const Polygons &subject, bool preserve_collinear)
{
// convert into Clipper polygons
ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject);
@@ -698,13 +709,11 @@ simplify_polygons(const Polygons &subject, bool preserve_collinear)
return ClipperPaths_to_Slic3rPolygons(output);
}
-ExPolygons
-simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
+ExPolygons simplify_polygons_ex(const Polygons &subject, bool preserve_collinear)
{
- if (!preserve_collinear) {
- return union_ex(simplify_polygons(subject, preserve_collinear));
- }
-
+ if (! preserve_collinear)
+ return union_ex(simplify_polygons(subject, false));
+
// convert into Clipper polygons
ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject);
diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp
index eb83c31a3..b065cfe07 100644
--- a/xs/src/libslic3r/ClipperUtils.hpp
+++ b/xs/src/libslic3r/ClipperUtils.hpp
@@ -80,6 +80,9 @@ Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
double miterLimit = 3);
+Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1,
+ const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
+ double miterLimit = 3);
Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp
index 1a0b8fb4a..e7442d773 100644
--- a/xs/src/libslic3r/Config.cpp
+++ b/xs/src/libslic3r/Config.cpp
@@ -20,6 +20,7 @@
namespace Slic3r {
+// Escape \n, \r and backslash
std::string escape_string_cstyle(const std::string &str)
{
// Allocate a buffer twice the input string length,
@@ -28,9 +29,15 @@ std::string escape_string_cstyle(const std::string &str)
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
- if (c == '\n' || c == '\r') {
+ if (c == '\r') {
+ (*outptr ++) = '\\';
+ (*outptr ++) = 'r';
+ } else if (c == '\n') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
+ } else if (c == '\\') {
+ (*outptr ++) = '\\';
+ (*outptr ++) = '\\';
} else
(*outptr ++) = c;
}
@@ -69,7 +76,10 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
if (c == '\\' || c == '"') {
(*outptr ++) = '\\';
(*outptr ++) = c;
- } else if (c == '\n' || c == '\r') {
+ } else if (c == '\r') {
+ (*outptr ++) = '\\';
+ (*outptr ++) = 'r';
+ } else if (c == '\n') {
(*outptr ++) = '\\';
(*outptr ++) = 'n';
} else
@@ -84,6 +94,7 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
return std::string(out.data(), outptr - out.data());
}
+// Unescape \n, \r and backslash
bool unescape_string_cstyle(const std::string &str, std::string &str_out)
{
std::vector<char> out(str.size(), 0);
@@ -94,8 +105,12 @@ bool unescape_string_cstyle(const std::string &str, std::string &str_out)
if (++ i == str.size())
return false;
c = str[i];
- if (c == 'n')
+ if (c == 'r')
+ (*outptr ++) = '\r';
+ else if (c == 'n')
(*outptr ++) = '\n';
+ else
+ (*outptr ++) = c;
} else
(*outptr ++) = c;
}
@@ -134,7 +149,9 @@ bool unescape_strings_cstyle(const std::string &str, std::vector<std::string> &o
if (++ i == str.size())
return false;
c = str[i];
- if (c == 'n')
+ if (c == 'r')
+ c = '\r';
+ else if (c == 'n')
c = '\n';
}
buf.push_back(c);
@@ -185,10 +202,13 @@ void ConfigBase::apply_only(const ConfigBase &other, const t_config_option_keys
// This is only possible if other is of DynamicConfig type.
if (ignore_nonexistent)
continue;
- throw UnknownOptionException();
+ throw UnknownOptionException(opt_key);
}
const ConfigOption *other_opt = other.option(opt_key);
- if (other_opt != nullptr)
+ if (other_opt == nullptr) {
+ // The key was not found in the source config, therefore it will not be initialized!
+// printf("Not found, therefore not initialized: %s\n", opt_key.c_str());
+ } else
my_opt->set(other_opt);
}
}
@@ -206,6 +226,56 @@ t_config_option_keys ConfigBase::diff(const ConfigBase &other) const
return diff;
}
+template<class T>
+void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys& vec, const ConfigBase &other, const ConfigBase *this_c)
+{
+ const T* opt_init = static_cast<const T*>(other.option(opt_key));
+ const T* opt_cur = static_cast<const T*>(this_c->option(opt_key));
+ int opt_init_max_id = opt_init->values.size() - 1;
+ for (int i = 0; i < opt_cur->values.size(); i++)
+ {
+ int init_id = i <= opt_init_max_id ? i : 0;
+ if (opt_cur->values[i] != opt_init->values[init_id])
+ vec.emplace_back(opt_key + "#" + std::to_string(i));
+ }
+}
+
+t_config_option_keys ConfigBase::deep_diff(const ConfigBase &other) const
+{
+ t_config_option_keys diff;
+ for (const t_config_option_key &opt_key : this->keys()) {
+ const ConfigOption *this_opt = this->option(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
+ {
+ if (opt_key == "bed_shape"){ diff.emplace_back(opt_key); continue; }
+ switch (other_opt->type())
+ {
+ case coInts: add_correct_opts_to_diff<ConfigOptionInts >(opt_key, diff, other, this); break;
+ case coBools: add_correct_opts_to_diff<ConfigOptionBools >(opt_key, diff, other, this); break;
+ case coFloats: add_correct_opts_to_diff<ConfigOptionFloats >(opt_key, diff, other, this); break;
+ case coStrings: add_correct_opts_to_diff<ConfigOptionStrings >(opt_key, diff, other, this); break;
+ case coPercents:add_correct_opts_to_diff<ConfigOptionPercents >(opt_key, diff, other, this); break;
+ case coPoints: add_correct_opts_to_diff<ConfigOptionPoints >(opt_key, diff, other, this); break;
+ default: diff.emplace_back(opt_key); break;
+ }
+ }
+ }
+ return diff;
+}
+
+t_config_option_keys ConfigBase::equal(const ConfigBase &other) const
+{
+ t_config_option_keys equal;
+ for (const t_config_option_key &opt_key : this->keys()) {
+ const ConfigOption *this_opt = this->option(opt_key);
+ const ConfigOption *other_opt = other.option(opt_key);
+ if (this_opt != nullptr && other_opt != nullptr && *this_opt == *other_opt)
+ equal.emplace_back(opt_key);
+ }
+ return equal;
+}
+
std::string ConfigBase::serialize(const t_config_option_key &opt_key) const
{
const ConfigOption* opt = this->option(opt_key);
@@ -232,7 +302,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
// Try to deserialize the option by its name.
const ConfigDef *def = this->def();
if (def == nullptr)
- throw NoDefinitionException();
+ throw NoDefinitionException(opt_key);
const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr) {
// If we didn't find an option, look for any other option having this as an alias.
@@ -248,7 +318,7 @@ bool ConfigBase::set_deserialize_raw(const t_config_option_key &opt_key_src, con
break;
}
if (optdef == nullptr)
- throw UnknownOptionException();
+ throw UnknownOptionException(opt_key);
}
if (! optdef->shortcut.empty()) {
@@ -278,7 +348,7 @@ double ConfigBase::get_abs_value(const t_config_option_key &opt_key) const
// Get option definition.
const ConfigDef *def = this->def();
if (def == nullptr)
- throw NoDefinitionException();
+ throw NoDefinitionException(opt_key);
const ConfigOptionDef *opt_def = def->get(opt_key);
assert(opt_def != nullptr);
// Compute absolute value over the absolute value of the base option.
@@ -468,7 +538,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre
// Try to create a new ConfigOption.
const ConfigDef *def = this->def();
if (def == nullptr)
- throw NoDefinitionException();
+ throw NoDefinitionException(opt_key);
const ConfigOptionDef *optdef = def->get(opt_key);
if (optdef == nullptr)
// throw std::runtime_error(std::string("Invalid option name: ") + opt_key);
@@ -522,7 +592,7 @@ void StaticConfig::set_defaults()
t_config_option_keys StaticConfig::keys() const
{
t_config_option_keys keys;
- assert(this->def != nullptr);
+ assert(this->def() != nullptr);
for (const auto &opt_def : this->def()->options)
if (this->option(opt_def.first) != nullptr)
keys.push_back(opt_def.first);
diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp
index 3dccedbf0..e3cd14335 100644
--- a/xs/src/libslic3r/Config.hpp
+++ b/xs/src/libslic3r/Config.hpp
@@ -49,9 +49,9 @@ enum ConfigOptionType {
coPercents = coPercent + coVectorType,
// a fraction or an absolute value
coFloatOrPercent = 5,
- // single 2d point. Currently not used.
+ // single 2d point (Point2f). Currently not used.
coPoint = 6,
- // vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets.
+ // vector of 2d points (Point2f). Currently used for the definition of the print bed and for the extruder offsets.
coPoints = coPoint + coVectorType,
// single boolean value
coBool = 7,
@@ -291,6 +291,8 @@ public:
ConfigOptionFloats() : ConfigOptionVector<double>() {}
explicit ConfigOptionFloats(size_t n, double value) : ConfigOptionVector<double>(n, value) {}
explicit ConfigOptionFloats(std::initializer_list<double> il) : ConfigOptionVector<double>(std::move(il)) {}
+ explicit ConfigOptionFloats(const std::vector<double> &vec) : ConfigOptionVector<double>(vec) {}
+ explicit ConfigOptionFloats(std::vector<double> &&vec) : ConfigOptionVector<double>(std::move(vec)) {}
static ConfigOptionType static_type() { return coFloats; }
ConfigOptionType type() const override { return static_type(); }
@@ -620,11 +622,11 @@ public:
}
};
-class ConfigOptionPoint : public ConfigOptionSingle<Pointf>
+class ConfigOptionPoint : public ConfigOptionSingle<Vec2d>
{
public:
- ConfigOptionPoint() : ConfigOptionSingle<Pointf>(Pointf(0,0)) {}
- explicit ConfigOptionPoint(const Pointf &value) : ConfigOptionSingle<Pointf>(value) {}
+ ConfigOptionPoint() : ConfigOptionSingle<Vec2d>(Vec2d(0,0)) {}
+ explicit ConfigOptionPoint(const Vec2d &value) : ConfigOptionSingle<Vec2d>(value) {}
static ConfigOptionType static_type() { return coPoint; }
ConfigOptionType type() const override { return static_type(); }
@@ -635,30 +637,28 @@ public:
std::string serialize() const override
{
std::ostringstream ss;
- ss << this->value.x;
+ ss << this->value(0);
ss << ",";
- ss << this->value.y;
+ ss << this->value(1);
return ss.str();
}
bool deserialize(const std::string &str, bool append = false) override
{
UNUSED(append);
- std::istringstream iss(str);
- iss >> this->value.x;
- iss.ignore(std::numeric_limits<std::streamsize>::max(), ',');
- iss.ignore(std::numeric_limits<std::streamsize>::max(), 'x');
- iss >> this->value.y;
- return true;
+ char dummy;
+ return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 ||
+ sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2;
}
};
-class ConfigOptionPoints : public ConfigOptionVector<Pointf>
+class ConfigOptionPoints : public ConfigOptionVector<Vec2d>
{
public:
- ConfigOptionPoints() : ConfigOptionVector<Pointf>() {}
- explicit ConfigOptionPoints(size_t n, const Pointf &value) : ConfigOptionVector<Pointf>(n, value) {}
- explicit ConfigOptionPoints(std::initializer_list<Pointf> il) : ConfigOptionVector<Pointf>(std::move(il)) {}
+ ConfigOptionPoints() : ConfigOptionVector<Vec2d>() {}
+ explicit ConfigOptionPoints(size_t n, const Vec2d &value) : ConfigOptionVector<Vec2d>(n, value) {}
+ explicit ConfigOptionPoints(std::initializer_list<Vec2d> il) : ConfigOptionVector<Vec2d>(std::move(il)) {}
+ explicit ConfigOptionPoints(const std::vector<Vec2d> &values) : ConfigOptionVector<Vec2d>(values) {}
static ConfigOptionType static_type() { return coPoints; }
ConfigOptionType type() const override { return static_type(); }
@@ -671,9 +671,9 @@ public:
std::ostringstream ss;
for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) {
if (it - this->values.begin() != 0) ss << ",";
- ss << it->x;
+ ss << (*it)(0);
ss << "x";
- ss << it->y;
+ ss << (*it)(1);
}
return ss.str();
}
@@ -696,13 +696,13 @@ public:
std::istringstream is(str);
std::string point_str;
while (std::getline(is, point_str, ',')) {
- Pointf point;
+ Vec2d point(Vec2d::Zero());
std::istringstream iss(point_str);
std::string coord_str;
if (std::getline(iss, coord_str, 'x')) {
- std::istringstream(coord_str) >> point.x;
+ std::istringstream(coord_str) >> point(0);
if (std::getline(iss, coord_str, 'x')) {
- std::istringstream(coord_str) >> point.y;
+ std::istringstream(coord_str) >> point(1);
}
}
this->values.push_back(point);
@@ -821,12 +821,7 @@ public:
bool deserialize(const std::string &str, bool append = false) override
{
UNUSED(append);
- const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
- auto it = enum_keys_map.find(str);
- if (it == enum_keys_map.end())
- return false;
- this->value = static_cast<T>(it->second);
- return true;
+ return from_string(str, this->value);
}
static bool has(T value)
@@ -838,7 +833,7 @@ public:
}
// Map from an enum name to an enum integer value.
- static t_config_enum_names& get_enum_names()
+ static const t_config_enum_names& get_enum_names()
{
static t_config_enum_names names;
if (names.empty()) {
@@ -855,7 +850,17 @@ public:
return names;
}
// Map from an enum name to an enum integer value.
- static t_config_enum_values& get_enum_values();
+ static const t_config_enum_values& get_enum_values();
+
+ static bool from_string(const std::string &str, T &value)
+ {
+ const t_config_enum_values &enum_keys_map = ConfigOptionEnum<T>::get_enum_values();
+ auto it = enum_keys_map.find(str);
+ if (it == enum_keys_map.end())
+ return false;
+ value = static_cast<T>(it->second);
+ return true;
+ }
};
// Generic enum configuration value.
@@ -900,7 +905,7 @@ public:
// What type? bool, int, string etc.
ConfigOptionType type = coNone;
// Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor.
- ConfigOption *default_value = nullptr;
+ const ConfigOption *default_value = nullptr;
// Usually empty.
// Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection,
@@ -958,7 +963,7 @@ public:
std::vector<std::string> enum_labels;
// For enums (when type == coEnum). Maps enum_values to enums.
// Initialized by ConfigOptionEnum<xxx>::get_enum_values()
- t_config_enum_values *enum_keys_map = nullptr;
+ const t_config_enum_values *enum_keys_map = nullptr;
bool has_enum_value(const std::string &value) const {
for (const std::string &v : enum_values)
@@ -1030,7 +1035,7 @@ public:
TYPE* option(const t_config_option_key &opt_key, bool create = false)
{
ConfigOption *opt = this->optptr(opt_key, create);
- assert(opt == nullptr || opt->type() == TYPE::static_type());
+// assert(opt == nullptr || opt->type() == TYPE::static_type());
return (opt == nullptr || opt->type() != TYPE::static_type()) ? nullptr : static_cast<TYPE*>(opt);
}
template<typename TYPE>
@@ -1046,6 +1051,10 @@ public:
void apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false);
bool equals(const ConfigBase &other) const { return this->diff(other).empty(); }
t_config_option_keys diff(const ConfigBase &other) const;
+ // Use deep_diff to correct return of changed options,
+ // considering individual options for each extruder
+ t_config_option_keys deep_diff(const ConfigBase &other) const;
+ t_config_option_keys equal(const ConfigBase &other) const;
std::string serialize(const t_config_option_key &opt_key) const;
// Set a configuration value from a string, it will call an overridable handle_legacy()
// to resolve renamed and removed configuration keys.
@@ -1232,17 +1241,22 @@ protected:
};
/// Specialization of std::exception to indicate that an unknown config option has been encountered.
-class UnknownOptionException : public std::exception
-{
+class UnknownOptionException : public std::runtime_error {
public:
- const char* what() const noexcept override { return "Unknown config option"; }
+ UnknownOptionException() :
+ std::runtime_error("Unknown option exception") {}
+ UnknownOptionException(const std::string &opt_key) :
+ std::runtime_error(std::string("Unknown option exception: ") + opt_key) {}
};
/// Indicate that the ConfigBase derived class does not provide config definition (the method def() returns null).
-class NoDefinitionException : public std::exception
+class NoDefinitionException : public std::runtime_error
{
public:
- const char* what() const noexcept override { return "No config definition"; }
+ NoDefinitionException() :
+ std::runtime_error("No definition exception") {}
+ NoDefinitionException(const std::string &opt_key) :
+ std::runtime_error(std::string("No definition exception: ") + opt_key) {}
};
}
diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp
index 752243407..50f424e6d 100644
--- a/xs/src/libslic3r/EdgeGrid.cpp
+++ b/xs/src/libslic3r/EdgeGrid.cpp
@@ -117,15 +117,15 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
m_bbox.merge(pts[j]);
}
coord_t eps = 16;
- m_bbox.min.x -= eps;
- m_bbox.min.y -= eps;
- m_bbox.max.x += eps;
- m_bbox.max.y += eps;
+ m_bbox.min(0) -= eps;
+ m_bbox.min(1) -= eps;
+ m_bbox.max(0) += eps;
+ m_bbox.max(1) += eps;
// 2) Initialize the edge grid.
m_resolution = resolution;
- m_cols = (m_bbox.max.x - m_bbox.min.x + m_resolution - 1) / m_resolution;
- m_rows = (m_bbox.max.y - m_bbox.min.y + m_resolution - 1) / m_resolution;
+ m_cols = (m_bbox.max(0) - m_bbox.min(0) + m_resolution - 1) / m_resolution;
+ m_rows = (m_bbox.max(1) - m_bbox.min(1) + m_resolution - 1) / m_resolution;
m_cells.assign(m_rows * m_cols, Cell());
// 3) First round of contour rasterization, count the edges per grid cell.
@@ -135,15 +135,15 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
// End points of the line segment.
Slic3r::Point p1(pts[j]);
Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1];
- p1.x -= m_bbox.min.x;
- p1.y -= m_bbox.min.y;
- p2.x -= m_bbox.min.x;
- p2.y -= m_bbox.min.y;
+ p1(0) -= m_bbox.min(0);
+ p1(1) -= m_bbox.min(1);
+ p2(0) -= m_bbox.min(0);
+ p2(1) -= m_bbox.min(1);
// Get the cells of the end points.
- coord_t ix = p1.x / m_resolution;
- coord_t iy = p1.y / m_resolution;
- coord_t ixb = p2.x / m_resolution;
- coord_t iyb = p2.y / m_resolution;
+ coord_t ix = p1(0) / m_resolution;
+ coord_t iy = p1(1) / m_resolution;
+ coord_t ixb = p2(0) / m_resolution;
+ coord_t iyb = p2(1) / m_resolution;
assert(ix >= 0 && ix < m_cols);
assert(iy >= 0 && iy < m_rows);
assert(ixb >= 0 && ixb < m_cols);
@@ -154,13 +154,13 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
// Both ends fall into the same cell.
continue;
// Raster the centeral part of the line.
- coord_t dx = std::abs(p2.x - p1.x);
- coord_t dy = std::abs(p2.y - p1.y);
- if (p1.x < p2.x) {
- int64_t ex = int64_t((ix + 1)*m_resolution - p1.x) * int64_t(dy);
- if (p1.y < p2.y) {
+ coord_t dx = std::abs(p2(0) - p1(0));
+ coord_t dy = std::abs(p2(1) - p1(1));
+ if (p1(0) < p2(0)) {
+ int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy);
+ if (p1(1) < p2(1)) {
// x positive, y positive
- int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx);
+ int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix <= ixb && iy <= iyb);
if (ex < ey) {
@@ -185,7 +185,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
}
else {
// x positive, y non positive
- int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx);
+ int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix <= ixb && iy >= iyb);
if (ex <= ey) {
@@ -203,10 +203,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
}
}
else {
- int64_t ex = int64_t(p1.x - ix*m_resolution) * int64_t(dy);
- if (p1.y < p2.y) {
+ int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy);
+ if (p1(1) < p2(1)) {
// x non positive, y positive
- int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx);
+ int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix >= ixb && iy <= iyb);
if (ex < ey) {
@@ -225,7 +225,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
}
else {
// x non positive, y non positive
- int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx);
+ int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix >= ixb && iy >= iyb);
if (ex < ey) {
@@ -279,15 +279,15 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
// End points of the line segment.
Slic3r::Point p1(pts[j]);
Slic3r::Point p2 = pts[(j + 1 == pts.size()) ? 0 : j + 1];
- p1.x -= m_bbox.min.x;
- p1.y -= m_bbox.min.y;
- p2.x -= m_bbox.min.x;
- p2.y -= m_bbox.min.y;
+ p1(0) -= m_bbox.min(0);
+ p1(1) -= m_bbox.min(1);
+ p2(0) -= m_bbox.min(0);
+ p2(1) -= m_bbox.min(1);
// Get the cells of the end points.
- coord_t ix = p1.x / m_resolution;
- coord_t iy = p1.y / m_resolution;
- coord_t ixb = p2.x / m_resolution;
- coord_t iyb = p2.y / m_resolution;
+ coord_t ix = p1(0) / m_resolution;
+ coord_t iy = p1(1) / m_resolution;
+ coord_t ixb = p2(0) / m_resolution;
+ coord_t iyb = p2(1) / m_resolution;
assert(ix >= 0 && ix < m_cols);
assert(iy >= 0 && iy < m_rows);
assert(ixb >= 0 && ixb < m_cols);
@@ -298,13 +298,13 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
// Both ends fall into the same cell.
continue;
// Raster the centeral part of the line.
- coord_t dx = std::abs(p2.x - p1.x);
- coord_t dy = std::abs(p2.y - p1.y);
- if (p1.x < p2.x) {
- int64_t ex = int64_t((ix + 1)*m_resolution - p1.x) * int64_t(dy);
- if (p1.y < p2.y) {
+ coord_t dx = std::abs(p2(0) - p1(0));
+ coord_t dy = std::abs(p2(1) - p1(1));
+ if (p1(0) < p2(0)) {
+ int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy);
+ if (p1(1) < p2(1)) {
// x positive, y positive
- int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx);
+ int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix <= ixb && iy <= iyb);
if (ex < ey) {
@@ -329,7 +329,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
}
else {
// x positive, y non positive
- int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx);
+ int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix <= ixb && iy >= iyb);
if (ex <= ey) {
@@ -347,10 +347,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
}
}
else {
- int64_t ex = int64_t(p1.x - ix*m_resolution) * int64_t(dy);
- if (p1.y < p2.y) {
+ int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy);
+ if (p1(1) < p2(1)) {
// x non positive, y positive
- int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx);
+ int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix >= ixb && iy <= iyb);
if (ex < ey) {
@@ -369,7 +369,7 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution)
}
else {
// x non positive, y non positive
- int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx);
+ int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix >= ixb && iy >= iyb);
if (ex < ey) {
@@ -429,15 +429,15 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed)
Point p1 = p1src;
Point p2 = p2src;
// Discretize the line segment p1, p2.
- p1.x -= m_bbox.min.x;
- p1.y -= m_bbox.min.y;
- p2.x -= m_bbox.min.x;
- p2.y -= m_bbox.min.y;
+ p1(0) -= m_bbox.min(0);
+ p1(1) -= m_bbox.min(1);
+ p2(0) -= m_bbox.min(0);
+ p2(1) -= m_bbox.min(1);
// Get the cells of the end points.
- coord_t ix = div_floor(p1.x, m_resolution);
- coord_t iy = div_floor(p1.y, m_resolution);
- coord_t ixb = div_floor(p2.x, m_resolution);
- coord_t iyb = div_floor(p2.y, m_resolution);
+ coord_t ix = div_floor(p1(0), m_resolution);
+ coord_t iy = div_floor(p1(1), m_resolution);
+ coord_t ixb = div_floor(p2(0), m_resolution);
+ coord_t iyb = div_floor(p2(1), m_resolution);
// assert(ix >= 0 && ix < m_cols);
// assert(iy >= 0 && iy < m_rows);
// assert(ixb >= 0 && ixb < m_cols);
@@ -449,12 +449,12 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed)
// Both ends fall into the same cell.
continue;
// Raster the centeral part of the line.
- coord_t dx = std::abs(p2.x - p1.x);
- coord_t dy = std::abs(p2.y - p1.y);
- if (p1.x < p2.x) {
- int64_t ex = int64_t((ix + 1)*m_resolution - p1.x) * int64_t(dy);
- if (p1.y < p2.y) {
- int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx);
+ coord_t dx = std::abs(p2(0) - p1(0));
+ coord_t dy = std::abs(p2(1) - p1(1));
+ if (p1(0) < p2(0)) {
+ int64_t ex = int64_t((ix + 1)*m_resolution - p1(0)) * int64_t(dy);
+ if (p1(1) < p2(1)) {
+ int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix <= ixb && iy <= iyb);
if (ex < ey) {
@@ -479,7 +479,7 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed)
} while (ix != ixb || iy != iyb);
}
else {
- int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx);
+ int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix <= ixb && iy >= iyb);
if (ex <= ey) {
@@ -498,9 +498,9 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed)
}
}
else {
- int64_t ex = int64_t(p1.x - ix*m_resolution) * int64_t(dy);
- if (p1.y < p2.y) {
- int64_t ey = int64_t((iy + 1)*m_resolution - p1.y) * int64_t(dx);
+ int64_t ex = int64_t(p1(0) - ix*m_resolution) * int64_t(dy);
+ if (p1(1) < p2(1)) {
+ int64_t ey = int64_t((iy + 1)*m_resolution - p1(1)) * int64_t(dx);
do {
assert(ix >= ixb && iy <= iyb);
if (ex < ey) {
@@ -519,7 +519,7 @@ bool EdgeGrid::Grid::intersect(const MultiPoint &polyline, bool closed)
} while (ix != ixb || iy != iyb);
}
else {
- int64_t ey = int64_t(p1.y - iy*m_resolution) * int64_t(dx);
+ int64_t ey = int64_t(p1(1) - iy*m_resolution) * int64_t(dx);
do {
assert(ix >= ixb && iy >= iyb);
if (ex < ey) {
@@ -556,8 +556,8 @@ bool EdgeGrid::Grid::line_cell_intersect(const Point &p1a, const Point &p2a, con
{
BoundingBox bbox(p1a, p1a);
bbox.merge(p2a);
- int64_t va_x = p2a.x - p1a.x;
- int64_t va_y = p2a.y - p1a.y;
+ int64_t va_x = p2a(0) - p1a(0);
+ int64_t va_y = p2a(1) - p1a(1);
for (size_t i = cell.begin; i != cell.end; ++ i) {
const std::pair<size_t, size_t> &cell_data = m_cell_data[i];
// Contour indexed by the ith line of this cell.
@@ -576,21 +576,21 @@ bool EdgeGrid::Grid::line_cell_intersect(const Point &p1a, const Point &p2a, con
if (! bbox.overlap(bbox2))
continue;
// Now intersect the two line segments using exact arithmetics.
- int64_t w1_x = p1b.x - p1a.x;
- int64_t w1_y = p1b.y - p1a.y;
- int64_t w2_x = p2b.x - p1a.x;
- int64_t w2_y = p2b.y - p1a.y;
+ int64_t w1_x = p1b(0) - p1a(0);
+ int64_t w1_y = p1b(1) - p1a(1);
+ int64_t w2_x = p2b(0) - p1a(0);
+ int64_t w2_y = p2b(1) - p1a(1);
int64_t side1 = va_x * w1_y - va_y * w1_x;
int64_t side2 = va_x * w2_y - va_y * w2_x;
if (side1 == side2 && side1 != 0)
// The line segments don't intersect.
continue;
- w1_x = p1a.x - p1b.x;
- w1_y = p1a.y - p1b.y;
- w2_x = p2a.x - p1b.x;
- w2_y = p2a.y - p1b.y;
- int64_t vb_x = p2b.x - p1b.x;
- int64_t vb_y = p2b.y - p1b.y;
+ w1_x = p1a(0) - p1b(0);
+ w1_y = p1a(1) - p1b(1);
+ w2_x = p2a(0) - p1b(0);
+ w2_y = p2a(1) - p1b(1);
+ int64_t vb_x = p2b(0) - p1b(0);
+ int64_t vb_y = p2b(1) - p1b(1);
side1 = vb_x * w1_y - vb_y * w1_x;
side2 = vb_x * w2_y - vb_y * w2_x;
if (side1 == side2 && side1 != 0)
@@ -607,14 +607,14 @@ bool EdgeGrid::Grid::line_cell_intersect(const Point &p1a, const Point &p2a, con
bool EdgeGrid::Grid::inside(const Point &pt_src)
{
Point p = pt_src;
- p.x -= m_bbox.min.x;
- p.y -= m_bbox.min.y;
+ p(0) -= m_bbox.min(0);
+ p(1) -= m_bbox.min(1);
// Get the cell of the point.
- if (p.x < 0 || p.y < 0)
+ if (p(0) < 0 || p(1) < 0)
return false;
- coord_t ix = p.x / m_resolution;
- coord_t iy = p.y / m_resolution;
- if (ix >= m_cols || iy >= m_rows)
+ coord_t ix = p(0) / m_resolution;
+ coord_t iy = p(1) / m_resolution;
+ if (ix >= this->m_cols || iy >= this->m_rows)
return false;
size_t i_closest = (size_t)-1;
@@ -634,21 +634,21 @@ bool EdgeGrid::Grid::inside(const Point &pt_src)
idx2 = 0;
const Point &p1 = contour[idx1];
const Point &p2 = contour[idx2];
- if (p1.y < p2.y) {
- if (p.y < p1.y || p.y > p2.y)
+ if (p1(1) < p2(1)) {
+ if (p(1) < p1(1) || p(1) > p2(1))
continue;
//FIXME finish this!
int64_t vx = 0;// pt_src
//FIXME finish this!
int64_t det = 0;
- } else if (p1.y != p2.y) {
- assert(p1.y > p2.y);
- if (p.y < p2.y || p.y > p1.y)
+ } else if (p1(1) != p2(1)) {
+ assert(p1(1) > p2(1));
+ if (p(1) < p2(1) || p(1) > p1(1))
continue;
} else {
- assert(p1.y == p2.y);
- if (p1.y == p.y) {
- if (p.x >= p1.x && p.x <= p2.x)
+ assert(p1(1) == p2(1));
+ if (p1(1) == p(1)) {
+ if (p(0) >= p1(0) && p(0) <= p2(0))
// On the segment.
return true;
// Before or after the segment.
@@ -767,9 +767,9 @@ void EdgeGrid::Grid::calculate_sdf()
const Slic3r::Point &p1 = pts[ipt];
const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1];
// Segment vector
- const Slic3r::Point v_seg = p1.vector_to(p2);
+ const Slic3r::Point v_seg = p2 - p1;
// l2 of v_seg
- const int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y);
+ const int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1));
// For each corner of this cell and its 1 ring neighbours:
for (int corner_y = -1; corner_y < 3; ++ corner_y) {
coord_t corner_r = r + corner_y;
@@ -780,28 +780,28 @@ void EdgeGrid::Grid::calculate_sdf()
if (corner_c < 0 || corner_c >= ncols)
continue;
float &d_min = m_signed_distance_field[corner_r * ncols + corner_c];
- Slic3r::Point pt(m_bbox.min.x + corner_c * m_resolution, m_bbox.min.y + corner_r * m_resolution);
- Slic3r::Point v_pt = p1.vector_to(pt);
+ Slic3r::Point pt(m_bbox.min(0) + corner_c * m_resolution, m_bbox.min(1) + corner_r * m_resolution);
+ Slic3r::Point v_pt = pt - p1;
// dot(p2-p1, pt-p1)
- int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y);
+ int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1));
if (t_pt < 0) {
// Closest to p1.
- double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y));
+ double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1)));
if (dabs < d_min) {
// Previous point.
const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1];
- Slic3r::Point v_seg_prev = p0.vector_to(p1);
- int64_t t2_pt = int64_t(v_seg_prev.x) * int64_t(v_pt.x) + int64_t(v_seg_prev.y) * int64_t(v_pt.y);
+ Slic3r::Point v_seg_prev = p1 - p0;
+ int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1));
if (t2_pt > 0) {
// Inside the wedge between the previous and the next segment.
// Set the signum depending on whether the vertex is convex or reflex.
- int64_t det = int64_t(v_seg_prev.x) * int64_t(v_seg.y) - int64_t(v_seg_prev.y) * int64_t(v_seg.x);
+ int64_t det = int64_t(v_seg_prev(0)) * int64_t(v_seg(1)) - int64_t(v_seg_prev(1)) * int64_t(v_seg(0));
assert(det != 0);
d_min = dabs;
// Fill in an unsigned vector towards the zero iso surface.
float *l = &L[(corner_r * ncols + corner_c) << 1];
- l[0] = std::abs(v_pt.x);
- l[1] = std::abs(v_pt.y);
+ l[0] = std::abs(v_pt(0));
+ l[1] = std::abs(v_pt(1));
#ifdef _DEBUG
double dabs2 = sqrt(l[0]*l[0]+l[1]*l[1]);
assert(std::abs(dabs-dabs2) < 1e-4 * std::max(dabs, dabs2));
@@ -816,7 +816,7 @@ void EdgeGrid::Grid::calculate_sdf()
} else {
// Closest to the segment.
assert(t_pt >= 0 && t_pt <= l2_seg);
- int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y);
+ int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1));
double d = double(d_seg) / sqrt(double(l2_seg));
double dabs = std::abs(d);
if (dabs < d_min) {
@@ -824,8 +824,8 @@ void EdgeGrid::Grid::calculate_sdf()
// Fill in an unsigned vector towards the zero iso surface.
float *l = &L[(corner_r * ncols + corner_c) << 1];
float linv = float(d_seg) / float(l2_seg);
- l[0] = std::abs(float(v_seg.y) * linv);
- l[1] = std::abs(float(v_seg.x) * linv);
+ l[0] = std::abs(float(v_seg(1)) * linv);
+ l[1] = std::abs(float(v_seg(0)) * linv);
#ifdef _DEBUG
double dabs2 = sqrt(l[0]*l[0]+l[1]*l[1]);
assert(std::abs(dabs-dabs2) <= 1e-4 * std::max(dabs, dabs2));
@@ -1059,8 +1059,8 @@ void EdgeGrid::Grid::calculate_sdf()
float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
{
- coord_t x = pt.x - m_bbox.min.x;
- coord_t y = pt.y - m_bbox.min.y;
+ coord_t x = pt(0) - m_bbox.min(0);
+ coord_t y = pt(1) - m_bbox.min(1);
coord_t w = m_resolution * m_cols;
coord_t h = m_resolution * m_rows;
bool clamped = false;
@@ -1124,39 +1124,39 @@ float EdgeGrid::Grid::signed_distance_bilinear(const Point &pt) const
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);
+ bbox.min = bbox.max = Point(pt(0) - m_bbox.min(0), pt(1) - m_bbox.min(1));
bbox.defined = true;
// Upper boundary, round to grid and test validity.
- bbox.max.x += search_radius;
- bbox.max.y += search_radius;
- if (bbox.max.x < 0 || bbox.max.y < 0)
+ bbox.max(0) += search_radius;
+ bbox.max(1) += search_radius;
+ if (bbox.max(0) < 0 || bbox.max(1) < 0)
return false;
- bbox.max.x /= m_resolution;
- bbox.max.y /= m_resolution;
- if (bbox.max.x >= m_cols)
- bbox.max.x = m_cols - 1;
- if (bbox.max.y >= m_rows)
- bbox.max.y = m_rows - 1;
+ bbox.max(0) /= m_resolution;
+ bbox.max(1) /= m_resolution;
+ if (bbox.max(0) >= m_cols)
+ bbox.max(0) = m_cols - 1;
+ if (bbox.max(1) >= m_rows)
+ bbox.max(1) = m_rows - 1;
// Lower boundary, round to grid and test validity.
- bbox.min.x -= search_radius;
- bbox.min.y -= search_radius;
- if (bbox.min.x < 0)
- bbox.min.x = 0;
- if (bbox.min.y < 0)
- bbox.min.y = 0;
- bbox.min.x /= m_resolution;
- bbox.min.y /= m_resolution;
+ bbox.min(0) -= search_radius;
+ bbox.min(1) -= search_radius;
+ if (bbox.min(0) < 0)
+ bbox.min(0) = 0;
+ if (bbox.min(1) < 0)
+ bbox.min(1) = 0;
+ bbox.min(0) /= m_resolution;
+ bbox.min(1) /= m_resolution;
// Is the interval empty?
- if (bbox.min.x > bbox.max.x ||
- bbox.min.y > bbox.max.y)
+ if (bbox.min(0) > bbox.max(0) ||
+ bbox.min(1) > bbox.max(1))
return false;
// Traverse all cells in the bounding box.
float d_min = search_radius;
// Signum of the distance field at pt.
int sign_min = 0;
bool on_segment = false;
- for (int r = bbox.min.y; r <= bbox.max.y; ++ r) {
- for (int c = bbox.min.x; c <= bbox.max.x; ++ c) {
+ for (int r = bbox.min(1); r <= bbox.max(1); ++ r) {
+ for (int c = bbox.min(0); c <= bbox.max(0); ++ c) {
const Cell &cell = m_cells[r * m_cols + c];
for (size_t i = cell.begin; i < cell.end; ++ i) {
const Slic3r::Points &pts = *m_contours[m_cell_data[i].first];
@@ -1164,25 +1164,25 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu
// End points of the line segment.
const Slic3r::Point &p1 = pts[ipt];
const Slic3r::Point &p2 = pts[(ipt + 1 == pts.size()) ? 0 : ipt + 1];
- Slic3r::Point v_seg = p1.vector_to(p2);
- Slic3r::Point v_pt = p1.vector_to(pt);
+ Slic3r::Point v_seg = p2 - p1;
+ Slic3r::Point v_pt = pt - p1;
// dot(p2-p1, pt-p1)
- int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y);
+ int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1));
// l2 of seg
- int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y);
+ int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1));
if (t_pt < 0) {
// Closest to p1.
- double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y));
+ double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1)));
if (dabs < d_min) {
// Previous point.
const Slic3r::Point &p0 = pts[(ipt == 0) ? (pts.size() - 1) : ipt - 1];
- Slic3r::Point v_seg_prev = p0.vector_to(p1);
- int64_t t2_pt = int64_t(v_seg_prev.x) * int64_t(v_pt.x) + int64_t(v_seg_prev.y) * int64_t(v_pt.y);
+ Slic3r::Point v_seg_prev = p1 - p0;
+ int64_t t2_pt = int64_t(v_seg_prev(0)) * int64_t(v_pt(0)) + int64_t(v_seg_prev(1)) * int64_t(v_pt(1));
if (t2_pt > 0) {
// Inside the wedge between the previous and the next segment.
d_min = dabs;
// Set the signum depending on whether the vertex is convex or reflex.
- int64_t det = int64_t(v_seg_prev.x) * int64_t(v_seg.y) - int64_t(v_seg_prev.y) * int64_t(v_seg.x);
+ int64_t det = int64_t(v_seg_prev(0)) * int64_t(v_seg(1)) - int64_t(v_seg_prev(1)) * int64_t(v_seg(0));
assert(det != 0);
sign_min = (det > 0) ? 1 : -1;
on_segment = false;
@@ -1195,7 +1195,7 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu
} else {
// Closest to the segment.
assert(t_pt >= 0 && t_pt <= l2_seg);
- int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y);
+ int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1));
double d = double(d_seg) / sqrt(double(l2_seg));
double dabs = std::abs(d);
if (dabs < d_min) {
@@ -1307,7 +1307,7 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
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);
+ int64_t cross = int64_t(v1(0)) * int64_t(v2(1)) - int64_t(v2(0)) * int64_t(v1(1));
if (cross > 0) {
// This has to be a convex right angle. There is no better next line.
i_next = it->second;
@@ -1328,10 +1328,10 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
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;
+ p(0) *= m_resolution;
+ p(1) *= m_resolution;
+ p(0) += m_bbox.min(0);
+ p(1) += m_bbox.min(1);
}
// Shrink the contour slightly, so if the same contour gets discretized and simplified again, one will get the same result.
// Remove collineaer points.
@@ -1341,11 +1341,11 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
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) {
+ if (v(0) != 0 && v(1) != 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;
+ p(1) += (v(0) < 0) ? - offset : offset;
+ p(0) += (v(1) > 0) ? - offset : offset;
pts.push_back(p);
}
}
@@ -1357,8 +1357,8 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset) const
#if 0
void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path)
{
- unsigned int w = (bbox.max.x - bbox.min.x + resolution - 1) / resolution;
- unsigned int h = (bbox.max.y - bbox.min.y + resolution - 1) / resolution;
+ unsigned int w = (bbox.max(0) - bbox.min(0) + resolution - 1) / resolution;
+ unsigned int h = (bbox.max(1) - bbox.min(1) + resolution - 1) / resolution;
wxImage img(w, h);
unsigned char *data = img.GetData();
memset(data, 0, w * h * 3);
@@ -1371,7 +1371,7 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
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);
+ Point pt(c * resolution + bbox.min(0), r * resolution + bbox.min(1));
coordf_t min_dist;
bool on_segment = true;
#if 0
@@ -1409,8 +1409,8 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo
pxl[2] = 0;
}
- float gridx = float(pt.x - grid.bbox().min.x) / float(grid.resolution());
- float gridy = float(pt.y - grid.bbox().min.y) / float(grid.resolution());
+ float gridx = float(pt(0) - grid.bbox().min(0)) / float(grid.resolution());
+ float gridy = float(pt(1) - grid.bbox().min(1)) / float(grid.resolution());
if (gridx >= -0.4f && gridy >= -0.4f && gridx <= grid.cols() + 0.4f && gridy <= grid.rows() + 0.4f) {
int ix = int(floor(gridx + 0.5f));
int iy = int(floor(gridy + 0.5f));
diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp
index 1d4bac50b..4294fe543 100644
--- a/xs/src/libslic3r/ExPolygon.cpp
+++ b/xs/src/libslic3r/ExPolygon.cpp
@@ -34,54 +34,43 @@ ExPolygon::operator Polylines() const
return to_polylines(*this);
}
-void
-ExPolygon::scale(double factor)
+void ExPolygon::scale(double factor)
{
contour.scale(factor);
- for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
- (*it).scale(factor);
- }
+ for (Polygon &hole : holes)
+ hole.scale(factor);
}
-void
-ExPolygon::translate(double x, double y)
+void ExPolygon::translate(double x, double y)
{
contour.translate(x, y);
- for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
- (*it).translate(x, y);
- }
+ for (Polygon &hole : holes)
+ hole.translate(x, y);
}
-void
-ExPolygon::rotate(double angle)
+void ExPolygon::rotate(double angle)
{
contour.rotate(angle);
- for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
- (*it).rotate(angle);
- }
+ for (Polygon &hole : holes)
+ hole.rotate(angle);
}
-void
-ExPolygon::rotate(double angle, const Point &center)
+void ExPolygon::rotate(double angle, const Point &center)
{
contour.rotate(angle, center);
- for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) {
- (*it).rotate(angle, center);
- }
+ for (Polygon &hole : holes)
+ hole.rotate(angle, center);
}
-double
-ExPolygon::area() const
+double ExPolygon::area() const
{
double a = this->contour.area();
- for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
- a -= -(*it).area(); // holes have negative area
- }
+ for (const Polygon &hole : holes)
+ a -= - hole.area(); // holes have negative area
return a;
}
-bool
-ExPolygon::is_valid() const
+bool ExPolygon::is_valid() const
{
if (!this->contour.is_valid() || !this->contour.is_counter_clockwise()) return false;
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
@@ -90,20 +79,17 @@ ExPolygon::is_valid() const
return true;
}
-bool
-ExPolygon::contains(const Line &line) const
+bool ExPolygon::contains(const Line &line) const
{
- return this->contains((Polyline)line);
+ return this->contains(Polyline(line.a, line.b));
}
-bool
-ExPolygon::contains(const Polyline &polyline) const
+bool ExPolygon::contains(const Polyline &polyline) const
{
return diff_pl((Polylines)polyline, *this).empty();
}
-bool
-ExPolygon::contains(const Polylines &polylines) const
+bool ExPolygon::contains(const Polylines &polylines) const
{
#if 0
BoundingBox bbox = get_extents(polylines);
@@ -120,8 +106,7 @@ ExPolygon::contains(const Polylines &polylines) const
return pl_out.empty();
}
-bool
-ExPolygon::contains(const Point &point) const
+bool ExPolygon::contains(const Point &point) const
{
if (!this->contour.contains(point)) return false;
for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
@@ -131,8 +116,7 @@ ExPolygon::contains(const Point &point) const
}
// inclusive version of contains() that also checks whether point is on boundaries
-bool
-ExPolygon::contains_b(const Point &point) const
+bool ExPolygon::contains_b(const Point &point) const
{
return this->contains(point) || this->has_boundary_point(point);
}
@@ -168,52 +152,42 @@ ExPolygon::overlaps(const ExPolygon &other) const
return ! other.contour.points.empty() && this->contains_b(other.contour.points.front());
}
-void
-ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
+void ExPolygon::simplify_p(double tolerance, Polygons* polygons) const
{
Polygons pp = this->simplify_p(tolerance);
polygons->insert(polygons->end(), pp.begin(), pp.end());
}
-Polygons
-ExPolygon::simplify_p(double tolerance) const
+Polygons ExPolygon::simplify_p(double tolerance) const
{
Polygons pp;
pp.reserve(this->holes.size() + 1);
-
// contour
{
Polygon p = this->contour;
p.points.push_back(p.points.front());
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points.pop_back();
- pp.push_back(p);
+ pp.emplace_back(std::move(p));
}
-
// holes
- for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) {
- Polygon p = *it;
+ for (Polygon p : this->holes) {
p.points.push_back(p.points.front());
p.points = MultiPoint::_douglas_peucker(p.points, tolerance);
p.points.pop_back();
- pp.push_back(p);
+ pp.emplace_back(std::move(p));
}
- pp = simplify_polygons(pp);
- return pp;
+ return simplify_polygons(pp);
}
-ExPolygons
-ExPolygon::simplify(double tolerance) const
+ExPolygons ExPolygon::simplify(double tolerance) const
{
- Polygons pp = this->simplify_p(tolerance);
- return union_ex(pp);
+ return union_ex(this->simplify_p(tolerance));
}
-void
-ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
+void ExPolygon::simplify(double tolerance, ExPolygons* expolygons) const
{
- ExPolygons ep = this->simplify(tolerance);
- expolygons->insert(expolygons->end(), ep.begin(), ep.end());
+ append(*expolygons, this->simplify(tolerance));
}
void
@@ -253,25 +227,24 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
Point new_front = polyline.points.front();
Point new_back = polyline.points.back();
if (polyline.endpoints.first && !this->has_boundary_point(new_front)) {
- Line line(polyline.points.front(), polyline.points[1]);
-
+ Vec2d p1 = polyline.points.front().cast<double>();
+ Vec2d p2 = polyline.points[1].cast<double>();
// prevent the line from touching on the other side, otherwise intersection() might return that solution
- if (polyline.points.size() == 2) line.b = line.midpoint();
-
- line.extend_start(max_width);
- (void)this->contour.intersection(line, &new_front);
+ if (polyline.points.size() == 2)
+ p2 = (p1 + p2) * 0.5;
+ // Extend the start of the segment.
+ p1 -= (p2 - p1).normalized() * max_width;
+ this->contour.intersection(Line(p1.cast<coord_t>(), p2.cast<coord_t>()), &new_front);
}
if (polyline.endpoints.second && !this->has_boundary_point(new_back)) {
- Line line(
- *(polyline.points.end() - 2),
- polyline.points.back()
- );
-
+ Vec2d p1 = (polyline.points.end() - 2)->cast<double>();
+ Vec2d p2 = polyline.points.back().cast<double>();
// prevent the line from touching on the other side, otherwise intersection() might return that solution
- if (polyline.points.size() == 2) line.a = line.midpoint();
- line.extend_end(max_width);
-
- (void)this->contour.intersection(line, &new_back);
+ if (polyline.points.size() == 2)
+ p1 = (p1 + p2) * 0.5;
+ // Extend the start of the segment.
+ p2 += (p2 - p1).normalized() * max_width;
+ this->contour.intersection(Line(p1.cast<coord_t>(), p2.cast<coord_t>()), &new_back);
}
polyline.points.front() = new_front;
polyline.points.back() = new_back;
@@ -304,14 +277,14 @@ ExPolygon::medial_axis(double max_width, double min_width, ThickPolylines* polyl
// find another polyline starting here
for (size_t j = i+1; j < pp.size(); ++j) {
ThickPolyline& other = pp[j];
- if (polyline.last_point().coincides_with(other.last_point())) {
+ if (polyline.last_point() == other.last_point()) {
other.reverse();
- } else if (polyline.first_point().coincides_with(other.last_point())) {
+ } else if (polyline.first_point() == other.last_point()) {
polyline.reverse();
other.reverse();
- } else if (polyline.first_point().coincides_with(other.first_point())) {
+ } else if (polyline.first_point() == other.first_point()) {
polyline.reverse();
- } else if (!polyline.last_point().coincides_with(other.first_point())) {
+ } else if (polyline.last_point() != other.first_point()) {
continue;
}
@@ -371,7 +344,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
std::vector<coord_t> xx;
xx.reserve(pp.size());
for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p)
- xx.push_back(p->x);
+ xx.push_back(p->x());
std::sort(xx.begin(), xx.end());
// find trapezoids by looping from first to next-to-last coordinate
@@ -382,14 +355,14 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const
// build rectangle
Polygon poly;
poly.points.resize(4);
- poly[0].x = *x;
- poly[0].y = bb.min.y;
- poly[1].x = next_x;
- poly[1].y = bb.min.y;
- poly[2].x = next_x;
- poly[2].y = bb.max.y;
- poly[3].x = *x;
- poly[3].y = bb.max.y;
+ poly[0](0) = *x;
+ poly[0](1) = bb.min(1);
+ poly[1](0) = next_x;
+ poly[1](1) = bb.min(1);
+ poly[2](0) = next_x;
+ poly[2](1) = bb.max(1);
+ poly[3](0) = *x;
+ poly[3](1) = bb.max(1);
// intersect with this expolygon
// append results to return value
@@ -435,10 +408,11 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
TPPLPoly p;
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;
- p[ point-ex->contour.points.begin() ].y = point->y;
- //printf("%ld %ld\n", point->x, point->y);
+ for (const Point &point : ex->contour.points) {
+ size_t i = &point - &ex->contour.points.front();
+ p[i].x = point(0);
+ p[i].y = point(1);
+ //printf("%ld %ld\n", point->x(), point->y());
}
p.SetHole(false);
input.push_back(p);
@@ -449,10 +423,11 @@ ExPolygon::triangulate_pp(Polygons* polygons) const
TPPLPoly p;
p.Init(hole->points.size());
//printf(PRINTF_ZU "\n1\n", hole->points.size());
- for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) {
- p[ point-hole->points.begin() ].x = point->x;
- p[ point-hole->points.begin() ].y = point->y;
- //printf("%ld %ld\n", point->x, point->y);
+ for (const Point &point : hole->points) {
+ size_t i = &point - &hole->points.front();
+ p[i].x = point(0);
+ p[i].y = point(1);
+ //printf("%ld %ld\n", point->x(), point->y());
}
p.SetHole(true);
input.push_back(p);
@@ -470,8 +445,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 = coord_t((*poly)[i].x);
- p.points[i].y = coord_t((*poly)[i].y);
+ p.points[i](0) = coord_t((*poly)[i].x);
+ p.points[i](1) = coord_t((*poly)[i].y);
}
polygons->push_back(p);
}
@@ -487,19 +462,17 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
// contour
std::vector<p2t::Point*> ContourPoints;
- for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) {
+ for (const Point &pt : ex->contour.points)
// We should delete each p2t::Point object
- ContourPoints.push_back(new p2t::Point(point->x, point->y));
- }
+ ContourPoints.push_back(new p2t::Point(pt(0), pt(1)));
p2t::CDT cdt(ContourPoints);
// holes
for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) {
std::vector<p2t::Point*> points;
- for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) {
+ for (const Point &pt : hole->points)
// will be destructed in SweepContext::~SweepContext
- points.push_back(new p2t::Point(point->x, point->y));
- }
+ points.push_back(new p2t::Point(pt(0), pt(1)));
cdt.AddHole(points);
}
@@ -516,9 +489,8 @@ ExPolygon::triangulate_p2t(Polygons* polygons) const
polygons->push_back(p);
}
- for(std::vector<p2t::Point*>::iterator it = ContourPoints.begin(); it != ContourPoints.end(); ++it) {
- delete *it;
- }
+ for (p2t::Point *ptr : ContourPoints)
+ delete ptr;
}
}
@@ -533,17 +505,6 @@ ExPolygon::lines() const
return lines;
}
-std::string
-ExPolygon::dump_perl() const
-{
- std::ostringstream ret;
- ret << "[" << this->contour.dump_perl();
- for (Polygons::const_iterator h = this->holes.begin(); h != this->holes.end(); ++h)
- ret << "," << h->dump_perl();
- ret << "]";
- return ret.str();
-}
-
BoundingBox get_extents(const ExPolygon &expolygon)
{
return get_extents(expolygon.contour);
diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp
index f4782ba55..4833ee49e 100644
--- a/xs/src/libslic3r/ExPolygon.hpp
+++ b/xs/src/libslic3r/ExPolygon.hpp
@@ -63,7 +63,6 @@ public:
void triangulate_pp(Polygons* polygons) const;
void triangulate_p2t(Polygons* polygons) const;
Lines lines() const;
- std::string dump_perl() const;
};
// Count a nuber of polygons stored inside the vector of expolygons.
diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp
index c6f67b169..92f0d3669 100644
--- a/xs/src/libslic3r/ExtrusionEntity.cpp
+++ b/xs/src/libslic3r/ExtrusionEntity.cpp
@@ -220,7 +220,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang)
double min_non_overhang = std::numeric_limits<double>::max();
for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) {
Point p_tmp = point.projection_onto(path->polyline);
- double dist = point.distance_to(p_tmp);
+ double dist = (p_tmp - point).cast<double>().norm();
if (dist < min) {
p = p_tmp;
min = dist;
diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp
index 9d34aced8..2bd03600c 100644
--- a/xs/src/libslic3r/ExtrusionEntity.hpp
+++ b/xs/src/libslic3r/ExtrusionEntity.hpp
@@ -92,6 +92,7 @@ public:
virtual double min_mm3_per_mm() const = 0;
virtual Polyline as_polyline() const = 0;
virtual double length() const = 0;
+ virtual double total_volume() const = 0;
};
typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr;
@@ -148,6 +149,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const { return this->mm3_per_mm; }
Polyline as_polyline() const { return this->polyline; }
+ virtual double total_volume() const { return mm3_per_mm * unscale<double>(length()); }
private:
void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const;
@@ -194,6 +196,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const;
+ virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
};
// Single continuous extrusion loop, possibly with varying extrusion thickness, extrusion height or bridging / non bridging.
@@ -241,6 +244,7 @@ public:
// Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm.
double min_mm3_per_mm() const;
Polyline as_polyline() const { return this->polygon().split_at_first_point(); }
+ virtual double total_volume() const { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; }
private:
ExtrusionLoopRole m_loop_role;
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
index 4513139e2..7a086bcbf 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp
@@ -125,6 +125,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt
continue;
}
}
+
ExtrusionEntity* entity = (*it)->clone();
my_paths.push_back(entity);
if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin();
diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
index 03bd2ba97..3b34145f8 100644
--- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp
+++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp
@@ -50,10 +50,15 @@ public:
src.clear();
}
}
- void append(const ExtrusionPaths &paths) {
+ void append(const ExtrusionPaths &paths) {
this->entities.reserve(this->entities.size() + paths.size());
- for (ExtrusionPaths::const_iterator path = paths.begin(); path != paths.end(); ++path)
- this->entities.push_back(path->clone());
+ for (const ExtrusionPath &path : paths)
+ this->entities.emplace_back(path.clone());
+ }
+ void append(ExtrusionPaths &&paths) {
+ this->entities.reserve(this->entities.size() + paths.size());
+ for (ExtrusionPath &path : paths)
+ this->entities.emplace_back(new ExtrusionPath(std::move(path)));
}
void replace(size_t i, const ExtrusionEntity &entity);
void remove(size_t i);
@@ -79,6 +84,7 @@ public:
void flatten(ExtrusionEntityCollection* retval) const;
ExtrusionEntityCollection flatten() const;
double min_mm3_per_mm() const;
+ virtual double total_volume() const {double volume=0.; for (const auto& ent : entities) volume+=ent->total_volume(); return volume; }
// Following methods shall never be called on an ExtrusionEntityCollection.
Polyline as_polyline() const {
diff --git a/xs/src/libslic3r/ExtrusionSimulator.cpp b/xs/src/libslic3r/ExtrusionSimulator.cpp
index daecbc0d1..fcb2fe825 100644
--- a/xs/src/libslic3r/ExtrusionSimulator.cpp
+++ b/xs/src/libslic3r/ExtrusionSimulator.cpp
@@ -893,24 +893,24 @@ ExtrusionSimulator::~ExtrusionSimulator()
void ExtrusionSimulator::set_image_size(const Point &image_size)
{
// printf("ExtrusionSimulator::set_image_size()\n");
- if (this->image_size.x == image_size.x &&
- this->image_size.y == image_size.y)
+ if (this->image_size.x() == image_size.x() &&
+ this->image_size.y() == image_size.y())
return;
// printf("Setting image size: %d, %d\n", image_size.x, image_size.y);
this->image_size = image_size;
// Allocate the image data in an RGBA format.
// printf("Allocating image data, size %d\n", image_size.x * image_size.y * 4);
- pimpl->image_data.assign(image_size.x * image_size.y * 4, 0);
+ pimpl->image_data.assign(image_size.x() * image_size.y() * 4, 0);
// printf("Allocating image data, allocated\n");
//FIXME fill the image with red vertical lines.
- for (size_t r = 0; r < image_size.y; ++ r) {
- for (size_t c = 0; c < image_size.x; c += 2) {
+ for (size_t r = 0; r < image_size.y(); ++ r) {
+ for (size_t c = 0; c < image_size.x(); c += 2) {
// Color red
- pimpl->image_data[r * image_size.x * 4 + c * 4] = 255;
+ pimpl->image_data[r * image_size.x() * 4 + c * 4] = 255;
// Opacity full
- pimpl->image_data[r * image_size.x * 4 + c * 4 + 3] = 255;
+ pimpl->image_data[r * image_size.x() * 4 + c * 4 + 3] = 255;
}
}
// printf("Allocating image data, set\n");
@@ -922,8 +922,8 @@ void ExtrusionSimulator::set_viewport(const BoundingBox &viewport)
if (this->viewport != viewport) {
this->viewport = viewport;
Point sz = viewport.size();
- pimpl->accumulator.resize(boost::extents[sz.y][sz.x]);
- pimpl->bitmap.resize(boost::extents[sz.y*pimpl->bitmap_oversampled][sz.x*pimpl->bitmap_oversampled]);
+ pimpl->accumulator.resize(boost::extents[sz.y()][sz.x()]);
+ pimpl->bitmap.resize(boost::extents[sz.y()*pimpl->bitmap_oversampled][sz.x()*pimpl->bitmap_oversampled]);
// printf("Accumulator size: %d, %d\n", sz.y, sz.x);
}
}
@@ -943,8 +943,8 @@ void ExtrusionSimulator::reset_accumulator()
// printf("ExtrusionSimulator::reset_accumulator()\n");
Point sz = viewport.size();
// printf("Reset accumulator, Accumulator size: %d, %d\n", sz.y, sz.x);
- memset(&pimpl->accumulator[0][0], 0, sizeof(float) * sz.x * sz.y);
- memset(&pimpl->bitmap[0][0], 0, sz.x * sz.y * pimpl->bitmap_oversampled * pimpl->bitmap_oversampled);
+ memset(&pimpl->accumulator[0][0], 0, sizeof(float) * sz.x() * sz.y());
+ memset(&pimpl->bitmap[0][0], 0, sz.x() * sz.y() * pimpl->bitmap_oversampled * pimpl->bitmap_oversampled);
pimpl->extrusion_points.clear();
// printf("Reset accumulator, done.\n");
}
@@ -955,17 +955,17 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const
// Convert the path to V2f points, shift and scale them to the viewport.
std::vector<V2f> polyline;
polyline.reserve(path.polyline.points.size());
- float scalex = float(viewport.size().x) / float(bbox.size().x);
- float scaley = float(viewport.size().y) / float(bbox.size().y);
+ float scalex = float(viewport.size().x()) / float(bbox.size().x());
+ float scaley = float(viewport.size().y()) / float(bbox.size().y());
float w = scale_(path.width) * scalex;
float h = scale_(path.height) * scalex;
w = scale_(path.mm3_per_mm / path.height) * scalex;
// printf("scalex: %f, scaley: %f\n", scalex, scaley);
- // printf("bbox: %d,%d %d,%d\n", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y);
+ // printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y);
for (Points::const_iterator it = path.polyline.points.begin(); it != path.polyline.points.end(); ++ it) {
- // printf("point %d,%d\n", it->x+shift.x, it->y+shift.y);
+ // printf("point %d,%d\n", it->x+shift.x(), it->y+shift.y);
ExtrusionPoint ept;
- ept.center = V2f(float(it->x+shift.x-bbox.min.x) * scalex, float(it->y+shift.y-bbox.min.y) * scaley);
+ ept.center = V2f(float((*it)(0)+shift.x()-bbox.min.x()) * scalex, float((*it)(1)+shift.y()-bbox.min.y()) * scaley);
ept.radius = w/2.f;
ept.height = 0.5f;
polyline.push_back(ept.center);
@@ -989,9 +989,9 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation
if (simulationType > ExtrusionSimulationDontSpread) {
// Average the cells of a bitmap into a lower resolution floating point mask.
- A2f mask(boost::extents[sz.y][sz.x]);
- for (int r = 0; r < sz.y; ++r) {
- for (int c = 0; c < sz.x; ++c) {
+ A2f mask(boost::extents[sz.y()][sz.x()]);
+ for (int r = 0; r < sz.y(); ++r) {
+ for (int c = 0; c < sz.x(); ++c) {
float p = 0;
for (int j = 0; j < pimpl->bitmap_oversampled; ++ j) {
for (int i = 0; i < pimpl->bitmap_oversampled; ++ i) {
@@ -1009,9 +1009,9 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation
}
// Color map the accumulator.
- for (int r = 0; r < sz.y; ++r) {
- unsigned char *ptr = &pimpl->image_data[(image_size.x * (viewport.min.y + r) + viewport.min.x) * 4];
- for (int c = 0; c < sz.x; ++c) {
+ for (int r = 0; r < sz.y(); ++r) {
+ unsigned char *ptr = &pimpl->image_data[(image_size.x() * (viewport.min.y() + r) + viewport.min.x()) * 4];
+ for (int c = 0; c < sz.x(); ++c) {
#if 1
float p = pimpl->accumulator[r][c];
#else
diff --git a/xs/src/libslic3r/FileParserError.hpp b/xs/src/libslic3r/FileParserError.hpp
new file mode 100644
index 000000000..3f560fa4f
--- /dev/null
+++ b/xs/src/libslic3r/FileParserError.hpp
@@ -0,0 +1,52 @@
+#ifndef slic3r_FileParserError_hpp_
+#define slic3r_FileParserError_hpp_
+
+#include "libslic3r.h"
+
+#include <string>
+#include <boost/filesystem/path.hpp>
+#include <stdexcept>
+
+namespace Slic3r {
+
+// Generic file parser error, mostly copied from boost::property_tree::file_parser_error
+class file_parser_error: public std::runtime_error
+{
+public:
+ file_parser_error(const std::string &msg, const std::string &file, unsigned long line = 0) :
+ std::runtime_error(format_what(msg, file, line)),
+ m_message(msg), m_filename(file), m_line(line) {}
+ file_parser_error(const std::string &msg, const boost::filesystem::path &file, unsigned long line = 0) :
+ std::runtime_error(format_what(msg, file.string(), line)),
+ m_message(msg), m_filename(file.string()), m_line(line) {}
+ // gcc 3.4.2 complains about lack of throw specifier on compiler
+ // generated dtor
+ ~file_parser_error() throw() {}
+
+ // Get error message (without line and file - use what() to get full message)
+ std::string message() const { return m_message; }
+ // Get error filename
+ std::string filename() const { return m_filename; }
+ // Get error line number
+ unsigned long line() const { return m_line; }
+
+private:
+ std::string m_message;
+ std::string m_filename;
+ unsigned long m_line;
+
+ // Format error message to be returned by std::runtime_error::what()
+ static std::string format_what(const std::string &msg, const std::string &file, unsigned long l)
+ {
+ std::stringstream stream;
+ stream << (file.empty() ? "<unspecified file>" : file.c_str());
+ if (l > 0)
+ stream << '(' << l << ')';
+ stream << ": " << msg;
+ return stream.str();
+ }
+};
+
+}; // Slic3r
+
+#endif // slic3r_FileParserError_hpp_
diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
index aa9774784..6a37e4369 100644
--- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp
@@ -54,9 +54,9 @@ static std::vector<coordf_t> perpendPoints(const coordf_t offset, const size_t b
// components that are outside these limits are set to the limits.
static inline void trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY)
{
- for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) {
- it->x = clamp(minX, maxX, it->x);
- it->y = clamp(minY, maxY, it->y);
+ for (Vec2d &pt : pts) {
+ pt(0) = clamp(minX, maxX, pt(0));
+ pt(1) = clamp(minY, maxY, pt(1));
}
}
@@ -66,7 +66,7 @@ static inline Pointfs zip(const std::vector<coordf_t> &x, const std::vector<coor
Pointfs out;
out.reserve(x.size());
for (size_t i = 0; i < x.size(); ++ i)
- out.push_back(Pointf(x[i], y[i]));
+ out.push_back(Vec2d(x[i], y[i]));
return out;
}
@@ -128,7 +128,7 @@ static Polylines makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t
result.push_back(Polyline());
Polyline &polyline = result.back();
for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it)
- polyline.points.push_back(Point(coord_t(it->x * scaleFactor), coord_t(it->y * scaleFactor)));
+ polyline.points.push_back(Point(coord_t((*it)(0) * scaleFactor), coord_t((*it)(1) * scaleFactor)));
}
return result;
}
@@ -153,13 +153,13 @@ void Fill3DHoneycomb::_fill_surface_single(
Polylines polylines = makeGrid(
scale_(this->z),
distance,
- ceil(bb.size().x / distance) + 1,
- ceil(bb.size().y / distance) + 1,
+ ceil(bb.size()(0) / distance) + 1,
+ ceil(bb.size()(1) / distance) + 1,
((this->layer_id/thickness_layers) % 2) + 1);
// move pattern in place
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it)
- it->translate(bb.min.x, bb.min.y);
+ it->translate(bb.min(0), bb.min(1));
// clip pattern to boundaries
polylines = intersection_pl(polylines, (Polygons)expolygon);
@@ -187,7 +187,7 @@ void Fill3DHoneycomb::_fill_surface_single(
const Point &last_point = pts_end.back();
// TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions
- if (first_point.distance_to(last_point) <= 1.5 * distance &&
+ if ((last_point - first_point).cast<double>().norm() <= 1.5 * distance &&
expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/xs/src/libslic3r/Fill/FillBase.hpp
index 62d18e518..b67d14339 100644
--- a/xs/src/libslic3r/Fill/FillBase.hpp
+++ b/xs/src/libslic3r/Fill/FillBase.hpp
@@ -121,11 +121,11 @@ public:
return aligned;
}
static Point _align_to_grid(Point coord, Point spacing)
- { return Point(_align_to_grid(coord.x, spacing.x), _align_to_grid(coord.y, spacing.y)); }
+ { return Point(_align_to_grid(coord(0), spacing(0)), _align_to_grid(coord(1), spacing(1))); }
static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base)
{ return base + _align_to_grid(coord - base, spacing); }
static Point _align_to_grid(Point coord, Point spacing, Point base)
- { return Point(_align_to_grid(coord.x, spacing.x, base.x), _align_to_grid(coord.y, spacing.y, base.y)); }
+ { return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); }
};
} // namespace Slic3r
diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp
index b21ad2799..8a3a7ea89 100644
--- a/xs/src/libslic3r/Fill/FillConcentric.cpp
+++ b/xs/src/libslic3r/Fill/FillConcentric.cpp
@@ -20,8 +20,8 @@ void FillConcentric::_fill_surface_single(
coord_t distance = coord_t(min_spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
- distance = this->_adjust_solid_spacing(bounding_box.size().x, distance);
- this->spacing = unscale(distance);
+ distance = this->_adjust_solid_spacing(bounding_box.size()(0), distance);
+ this->spacing = unscale<double>(distance);
}
Polygons loops = (Polygons)expolygon;
diff --git a/xs/src/libslic3r/Fill/FillGyroid.cpp b/xs/src/libslic3r/Fill/FillGyroid.cpp
index e63ce0bfd..d6bf03ce6 100644
--- a/xs/src/libslic3r/Fill/FillGyroid.cpp
+++ b/xs/src/libslic3r/Fill/FillGyroid.cpp
@@ -9,74 +9,117 @@
namespace Slic3r {
-static inline Polyline make_wave_vertical(
- double width, double height, double x0,
- double segmentSize, double scaleFactor,
- double z_cos, double z_sin, bool flip)
+static inline double f(double x, double z_sin, double z_cos, bool vertical, bool flip)
{
- Polyline polyline;
- polyline.points.emplace_back(Point(coord_t(clamp(0., width, x0) * scaleFactor), 0));
- double phase_offset_sin = (z_cos < 0 ? M_PI : 0) + M_PI;
- double phase_offset_cos = (z_cos < 0 ? M_PI : 0) + M_PI + (flip ? M_PI : 0.);
- for (double y = 0.; y < height + segmentSize; y += segmentSize) {
- y = std::min(y, height);
- double a = sin(y + phase_offset_sin);
+ if (vertical) {
+ double phase_offset = (z_cos < 0 ? M_PI : 0) + M_PI;
+ double a = sin(x + phase_offset);
double b = - z_cos;
- double res = z_sin * cos(y + phase_offset_cos);
+ double res = z_sin * cos(x + phase_offset + (flip ? M_PI : 0.));
double r = sqrt(sqr(a) + sqr(b));
- double x = clamp(0., width, asin(a/r) + asin(res/r) + M_PI + x0);
- polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
+ return asin(a/r) + asin(res/r) + M_PI;
+ }
+ else {
+ double phase_offset = z_sin < 0 ? M_PI : 0.;
+ double a = cos(x + phase_offset);
+ double b = - z_sin;
+ double res = z_cos * sin(x + phase_offset + (flip ? 0 : M_PI));
+ double r = sqrt(sqr(a) + sqr(b));
+ return (asin(a/r) + asin(res/r) + 0.5 * M_PI);
}
- if (flip)
- std::reverse(polyline.points.begin(), polyline.points.end());
- return polyline;
}
-static inline Polyline make_wave_horizontal(
- double width, double height, double y0,
- double segmentSize, double scaleFactor,
- double z_cos, double z_sin, bool flip)
+static inline Polyline make_wave(
+ const std::vector<Vec2d>& one_period, double width, double height, double offset, double scaleFactor,
+ double z_cos, double z_sin, bool vertical)
{
+ std::vector<Vec2d> points = one_period;
+ double period = points.back()(0);
+ points.pop_back();
+ int n = points.size();
+ do {
+ points.emplace_back(Vec2d(points[points.size()-n](0) + period, points[points.size()-n](1)));
+ } while (points.back()(0) < width);
+ points.back()(0) = width;
+
+ // and construct the final polyline to return:
Polyline polyline;
- polyline.points.emplace_back(Point(0, coord_t(clamp(0., height, y0) * scaleFactor)));
- double phase_offset_sin = (z_sin < 0 ? M_PI : 0) + (flip ? 0 : M_PI);
- double phase_offset_cos = z_sin < 0 ? M_PI : 0.;
- for (double x=0.; x < width + segmentSize; x += segmentSize) {
- x = std::min(x, width);
- double a = cos(x + phase_offset_cos);
- double b = - z_sin;
- double res = z_cos * sin(x + phase_offset_sin);
- double r = sqrt(sqr(a) + sqr(b));
- double y = clamp(0., height, asin(a/r) + asin(res/r) + 0.5 * M_PI + y0);
- polyline.points.emplace_back(convert_to<Point>(Pointf(x, y) * scaleFactor));
+ for (auto& point : points) {
+ point(1) += offset;
+ point(1) = clamp(0., height, double(point(1)));
+ if (vertical)
+ std::swap(point(0), point(1));
+ polyline.points.emplace_back((point * scaleFactor).cast<coord_t>());
}
- if (flip)
- std::reverse(polyline.points.begin(), polyline.points.end());
+
return polyline;
}
-static Polylines make_gyroid_waves(double gridZ, double density, double layer_width, double width, double height)
+static std::vector<Vec2d> make_one_period(double width, double scaleFactor, double z_cos, double z_sin, bool vertical, bool flip)
{
- double scaleFactor = scale_(layer_width) / density;
- double segmentSize = 0.5 * density;
+ std::vector<Vec2d> points;
+ double dx = M_PI_4; // very coarse spacing to begin with
+ double limit = std::min(2*M_PI, width);
+ for (double x = 0.; x < limit + EPSILON; x += dx) { // so the last point is there too
+ x = std::min(x, limit);
+ points.emplace_back(Vec2d(x,f(x, z_sin,z_cos, vertical, flip)));
+ }
+
+ // now we will check all internal points and in case some are too far from the line connecting its neighbours,
+ // we will add one more point on each side:
+ const double tolerance = .1;
+ for (unsigned int i=1;i<points.size()-1;++i) {
+ auto& lp = points[i-1]; // left point
+ auto& tp = points[i]; // this point
+ Vec2d lrv = tp - lp;
+ auto& rp = points[i+1]; // right point
+ // calculate distance of the point to the line:
+ double dist_mm = unscale<double>(scaleFactor) * std::abs(cross2(rp, lp) - cross2(rp - lp, tp)) / lrv.norm();
+ if (dist_mm > tolerance) { // if the difference from straight line is more than this
+ double x = 0.5f * (points[i-1](0) + points[i](0));
+ points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
+ x = 0.5f * (points[i+1](0) + points[i](0));
+ points.emplace_back(Vec2d(x, f(x, z_sin, z_cos, vertical, flip)));
+ // we added the points to the end, but need them all in order
+ std::sort(points.begin(), points.end(), [](const Vec2d &lhs, const Vec2d &rhs){ return lhs < rhs; });
+ // decrement i so we also check the first newly added point
+ --i;
+ }
+ }
+ return points;
+}
+
+static Polylines make_gyroid_waves(double gridZ, double density_adjusted, double line_spacing, double width, double height)
+{
+ const double scaleFactor = scale_(line_spacing) / density_adjusted;
//scale factor for 5% : 8 712 388
// 1z = 10^-6 mm ?
- double z = gridZ / scaleFactor;
- double z_sin = sin(z);
- double z_cos = cos(z);
- Polylines result;
- if (abs(z_sin) <= abs(z_cos)) {
- // Vertical wave
- double x0 = M_PI * (int)((- 0.5 * M_PI) / M_PI - 1.);
- bool flip = ((int)(x0 / M_PI + 1.) & 1) != 0;
- for (; x0 < width - 0.5 * M_PI; x0 += M_PI, flip = ! flip)
- result.emplace_back(make_wave_vertical(width, height, x0, segmentSize, scaleFactor, z_cos, z_sin, flip));
- } else {
- // Horizontal wave
- bool flip = true;
- for (double y0 = 0.; y0 < width; y0 += M_PI, flip = !flip)
- result.emplace_back(make_wave_horizontal(width, height, y0, segmentSize, scaleFactor, z_cos, z_sin, flip));
+ const double z = gridZ / scaleFactor;
+ const double z_sin = sin(z);
+ const double z_cos = cos(z);
+
+ bool vertical = (std::abs(z_sin) <= std::abs(z_cos));
+ double lower_bound = 0.;
+ double upper_bound = height;
+ bool flip = true;
+ if (vertical) {
+ flip = false;
+ lower_bound = -M_PI;
+ upper_bound = width - M_PI_2;
+ std::swap(width,height);
}
+
+ std::vector<Vec2d> one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // creates one period of the waves, so it doesn't have to be recalculated all the time
+ Polylines result;
+
+ for (double y0 = lower_bound; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates odd polylines
+ result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
+
+ flip = !flip; // even polylines are a bit shifted
+ one_period = make_one_period(width, scaleFactor, z_cos, z_sin, vertical, flip); // updates the one period sample
+ for (double y0 = lower_bound + M_PI; y0 < upper_bound+EPSILON; y0 += 2*M_PI) // creates even polylines
+ result.emplace_back(make_wave(one_period, width, height, y0, scaleFactor, z_cos, z_sin, vertical));
+
return result;
}
@@ -87,24 +130,27 @@ void FillGyroid::_fill_surface_single(
ExPolygon &expolygon,
Polylines &polylines_out)
{
- // no rotation is supported for this infill pattern
+ // no rotation is supported for this infill pattern (yet)
BoundingBox bb = expolygon.contour.bounding_box();
- coord_t distance = coord_t(scale_(this->spacing) / (params.density*this->scaling));
+ // Density adjusted to have a good %of weight.
+ double density_adjusted = std::max(0., params.density * 2.);
+ // Distance between the gyroid waves in scaled coordinates.
+ coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
// align bounding box to a multiple of our grid module
- bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
-
+ bb.merge(_align_to_grid(bb.min, Point(2.*M_PI*distance, 2.*M_PI*distance)));
+
// generate pattern
Polylines polylines = make_gyroid_waves(
scale_(this->z),
- params.density*this->scaling,
+ density_adjusted,
this->spacing,
- ceil(bb.size().x / distance) + 1.,
- ceil(bb.size().y / distance) + 1.);
+ ceil(bb.size()(0) / distance) + 1.,
+ ceil(bb.size()(1) / distance) + 1.);
// move pattern in place
for (Polyline &polyline : polylines)
- polyline.translate(bb.min.x, bb.min.y);
+ polyline.translate(bb.min(0), bb.min(1));
// clip pattern to boundaries
polylines = intersection_pl(polylines, (Polygons)expolygon);
@@ -133,7 +179,7 @@ void FillGyroid::_fill_surface_single(
// TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions
// TODO: avoid crossing current infill path
- if (first_point.distance_to(last_point) <= 5 * distance &&
+ if ((last_point - first_point).cast<double>().norm() <= 5 * distance &&
expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline.
pts_end.insert(pts_end.end(), polyline.points.begin(), polyline.points.end());
diff --git a/xs/src/libslic3r/Fill/FillGyroid.hpp b/xs/src/libslic3r/Fill/FillGyroid.hpp
index 758652a5c..17924b5ab 100644
--- a/xs/src/libslic3r/Fill/FillGyroid.hpp
+++ b/xs/src/libslic3r/Fill/FillGyroid.hpp
@@ -17,10 +17,6 @@ public:
virtual bool use_bridge_flow() const { return true; }
protected:
-
- // mult of density, to have a good %of weight for each density parameter
- float scaling = 1.75;
-
virtual void _fill_surface_single(
const FillParams &params,
unsigned int thickness_layers,
diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
index aa0e0f6b0..6f26167a2 100644
--- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp
+++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp
@@ -50,13 +50,13 @@ void FillHoneycomb::_fill_surface_single(
bounding_box.merge(_align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height)));
}
- coord_t x = bounding_box.min.x;
- while (x <= bounding_box.max.x) {
+ coord_t x = bounding_box.min(0);
+ while (x <= bounding_box.max(0)) {
Polygon p;
coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset };
for (size_t i = 0; i < 2; ++ i) {
std::reverse(p.points.begin(), p.points.end()); // turn first half upside down
- for (coord_t y = bounding_box.min.y; y <= bounding_box.max.y; y += m.y_short + m.hex_side + m.y_short + m.hex_side) {
+ for (coord_t y = bounding_box.min(1); y <= bounding_box.max(1); y += m.y_short + m.hex_side + m.y_short + m.hex_side) {
p.points.push_back(Point(ax[1], y + m.y_offset));
p.points.push_back(Point(ax[0], y + m.y_short - m.y_offset));
p.points.push_back(Point(ax[0], y + m.y_short + m.hex_side + m.y_offset));
@@ -101,7 +101,7 @@ void FillHoneycomb::_fill_surface_single(
for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) {
if (! paths.empty()) {
// distance between first point of this path and last point of last path
- double distance = paths.back().last_point().distance_to(it_path->first_point());
+ double distance = (it_path->first_point() - paths.back().last_point()).cast<double>().norm();
if (distance <= m.hex_width) {
paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end());
continue;
diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp
index f71ef95a1..615cc6efe 100644
--- a/xs/src/libslic3r/Fill/FillPlanePath.cpp
+++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp
@@ -24,14 +24,14 @@ void FillPlanePath::_fill_surface_single(
Point shift = this->_centered() ?
bounding_box.center() :
bounding_box.min;
- expolygon.translate(-shift.x, -shift.y);
- bounding_box.translate(-shift.x, -shift.y);
+ expolygon.translate(-shift(0), -shift(1));
+ bounding_box.translate(-shift(0), -shift(1));
Pointfs pts = _generate(
- coord_t(ceil(coordf_t(bounding_box.min.x) / distance_between_lines)),
- coord_t(ceil(coordf_t(bounding_box.min.y) / distance_between_lines)),
- coord_t(ceil(coordf_t(bounding_box.max.x) / distance_between_lines)),
- coord_t(ceil(coordf_t(bounding_box.max.y) / distance_between_lines)));
+ coord_t(ceil(coordf_t(bounding_box.min(0)) / distance_between_lines)),
+ coord_t(ceil(coordf_t(bounding_box.min(1)) / distance_between_lines)),
+ coord_t(ceil(coordf_t(bounding_box.max(0)) / distance_between_lines)),
+ coord_t(ceil(coordf_t(bounding_box.max(1)) / distance_between_lines)));
Polylines polylines;
if (pts.size() >= 2) {
@@ -41,8 +41,8 @@ void FillPlanePath::_fill_surface_single(
polyline.points.reserve(pts.size());
for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it)
polyline.points.push_back(Point(
- coord_t(floor(it->x * distance_between_lines + 0.5)),
- coord_t(floor(it->y * distance_between_lines + 0.5))));
+ coord_t(floor((*it)(0) * distance_between_lines + 0.5)),
+ coord_t(floor((*it)(1) * distance_between_lines + 0.5))));
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
polylines = intersection_pl(polylines, to_polygons(expolygon));
@@ -62,7 +62,7 @@ void FillPlanePath::_fill_surface_single(
// paths must be repositioned and rotated back
for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) {
- it->translate(shift.x, shift.y);
+ it->translate(shift(0), shift(1));
it->rotate(direction.first);
}
}
@@ -86,12 +86,12 @@ Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t m
coordf_t r = 1;
Pointfs out;
//FIXME Vojtech: If used as a solid infill, there is a gap left at the center.
- out.push_back(Pointf(0, 0));
- out.push_back(Pointf(1, 0));
+ out.push_back(Vec2d(0, 0));
+ out.push_back(Vec2d(1, 0));
while (r < rmax) {
theta += 1. / r;
r = a + b * theta;
- out.push_back(Pointf(r * cos(theta), r * sin(theta)));
+ out.push_back(Vec2d(r * cos(theta), r * sin(theta)));
}
return out;
}
@@ -162,7 +162,7 @@ Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x,
line.reserve(sz2);
for (size_t i = 0; i < sz2; ++ i) {
Point p = hilbert_n_to_xy(i);
- line.push_back(Pointf(p.x + min_x, p.y + min_y));
+ line.push_back(Vec2d(p(0) + min_x, p(1) + min_y));
}
return line;
}
@@ -175,27 +175,27 @@ Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_
coordf_t r = 0;
coordf_t r_inc = sqrt(2.);
Pointfs out;
- out.push_back(Pointf(0, 0));
+ out.push_back(Vec2d(0, 0));
while (r < rmax) {
r += r_inc;
coordf_t rx = r / sqrt(2.);
coordf_t r2 = r + rx;
- out.push_back(Pointf( r, 0.));
- out.push_back(Pointf( r2, rx));
- out.push_back(Pointf( rx, rx));
- out.push_back(Pointf( rx, r2));
- out.push_back(Pointf(0., r));
- out.push_back(Pointf(-rx, r2));
- out.push_back(Pointf(-rx, rx));
- out.push_back(Pointf(-r2, rx));
- out.push_back(Pointf(-r, 0.));
- out.push_back(Pointf(-r2, -rx));
- out.push_back(Pointf(-rx, -rx));
- out.push_back(Pointf(-rx, -r2));
- out.push_back(Pointf(0., -r));
- out.push_back(Pointf( rx, -r2));
- out.push_back(Pointf( rx, -rx));
- out.push_back(Pointf( r2+r_inc, -rx));
+ out.push_back(Vec2d( r, 0.));
+ out.push_back(Vec2d( r2, rx));
+ out.push_back(Vec2d( rx, rx));
+ out.push_back(Vec2d( rx, r2));
+ out.push_back(Vec2d(0., r));
+ out.push_back(Vec2d(-rx, r2));
+ out.push_back(Vec2d(-rx, rx));
+ out.push_back(Vec2d(-r2, rx));
+ out.push_back(Vec2d(-r, 0.));
+ out.push_back(Vec2d(-r2, -rx));
+ out.push_back(Vec2d(-rx, -rx));
+ out.push_back(Vec2d(-rx, -r2));
+ out.push_back(Vec2d(0., -r));
+ out.push_back(Vec2d( rx, -r2));
+ out.push_back(Vec2d( rx, -rx));
+ out.push_back(Vec2d( r2+r_inc, -rx));
}
return out;
}
diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp
index 5ba30ba51..205eb1b66 100644
--- a/xs/src/libslic3r/Fill/FillRectilinear.cpp
+++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp
@@ -26,8 +26,8 @@ void FillRectilinear::_fill_surface_single(
// define flow spacing according to requested density
if (params.density > 0.9999f && !params.dont_adjust) {
- this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size().x, this->_line_spacing);
- this->spacing = unscale(this->_line_spacing);
+ this->_line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), this->_line_spacing);
+ this->spacing = unscale<double>(this->_line_spacing);
} else {
// extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system.
@@ -38,14 +38,14 @@ void FillRectilinear::_fill_surface_single(
}
// generate the basic pattern
- coord_t x_max = bounding_box.max.x + SCALED_EPSILON;
+ coord_t x_max = bounding_box.max(0) + SCALED_EPSILON;
Lines lines;
- for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing)
- lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y));
+ for (coord_t x = bounding_box.min(0); x <= x_max; x += this->_line_spacing)
+ lines.push_back(this->_line(lines.size(), x, bounding_box.min(1), bounding_box.max(1)));
if (this->_horizontal_lines()) {
- coord_t y_max = bounding_box.max.y + SCALED_EPSILON;
- for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing)
- lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y)));
+ coord_t y_max = bounding_box.max(1) + SCALED_EPSILON;
+ for (coord_t y = bounding_box.min(1); y <= y_max; y += this->_line_spacing)
+ lines.push_back(Line(Point(bounding_box.min(0), y), Point(bounding_box.max(0), y)));
}
// clip paths against a slightly larger expolygon, so that the first and last paths
@@ -72,10 +72,10 @@ void FillRectilinear::_fill_surface_single(
for (Polylines::iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) {
Point *first_point = &it_polyline->points.front();
Point *last_point = &it_polyline->points.back();
- if (first_point->y > last_point->y)
+ if (first_point->y() > last_point->y())
std::swap(first_point, last_point);
- first_point->y -= extra;
- last_point->y += extra;
+ first_point->y() -= extra;
+ last_point->y() += extra;
}
size_t n_polylines_out_old = polylines_out.size();
@@ -103,10 +103,10 @@ void FillRectilinear::_fill_surface_single(
const Point &first_point = it_polyline->points.front();
const Point &last_point = pts_end.back();
// Distance in X, Y.
- const Vector distance = first_point.vector_to(last_point);
+ const Vector distance = last_point - first_point;
// TODO: we should also check that both points are on a fill_boundary to avoid
// connecting paths on the boundaries of internal regions
- if (this->_can_connect(std::abs(distance.x), std::abs(distance.y)) &&
+ if (this->_can_connect(std::abs(distance(0)), std::abs(distance(1))) &&
expolygon_off.contains(Line(last_point, first_point))) {
// Append the polyline.
pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end());
@@ -122,7 +122,7 @@ void FillRectilinear::_fill_surface_single(
// paths must be rotated back
for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_old; it != polylines_out.end(); ++ it) {
// No need to translate, the absolute position is irrelevant.
- // it->translate(- direction.second.x, - direction.second.y);
+ // it->translate(- direction.second(0), - direction.second(1));
it->rotate(direction.first);
}
}
diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp
index ddd785101..65440d0ef 100644
--- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp
+++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp
@@ -42,12 +42,12 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po
Point px = (i == 0) ? p1 : p2;
Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1];
Point pb = poly.points[seg];
- if (pa.x > pb.x)
- std::swap(pa.x, pb.x);
- if (pa.y > pb.y)
- std::swap(pa.y, pb.y);
- assert(px.x >= pa.x && px.x <= pb.x);
- assert(px.y >= pa.y && px.y <= pb.y);
+ if (pa(0) > pb(0))
+ std::swap(pa(0), pb(0));
+ if (pa(1) > pb(1))
+ std::swap(pa(1), pb(1));
+ assert(px(0) >= pa(0) && px(0) <= pb(0));
+ assert(px(1) >= pa(1) && px(1) <= pb(1));
}
#endif /* SLIC3R_DEBUG */
const Point *pPrev = &p1;
@@ -55,14 +55,14 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po
coordf_t len = 0;
if (seg1 <= seg2) {
for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis)
- len += pPrev->distance_to(*(pThis = &poly.points[i]));
+ len += (*pPrev - *(pThis = &poly.points[i])).cast<double>().norm();
} else {
for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis)
- len += pPrev->distance_to(*(pThis = &poly.points[i]));
+ len += (*pPrev - *(pThis = &poly.points[i])).cast<double>().norm();
for (size_t i = 0; i < seg2; ++ i, pPrev = pThis)
- len += pPrev->distance_to(*(pThis = &poly.points[i]));
+ len += (*pPrev - *(pThis = &poly.points[i])).cast<double>().norm();
}
- len += pPrev->distance_to(p2);
+ len += (*pPrev - p2).cast<double>().norm();
return len;
}
@@ -791,15 +791,15 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// define flow spacing according to requested density
if (params.full_infill() && !params.dont_adjust) {
- line_spacing = this->_adjust_solid_spacing(bounding_box.size().x, line_spacing);
- this->spacing = unscale(line_spacing);
+ line_spacing = this->_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
+ this->spacing = unscale<double>(line_spacing);
} else {
// extend bounding box so that our pattern will be aligned with other layers
// Transform the reference point to the rotated coordinate system.
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(0) -= (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),
@@ -808,8 +808,8 @@ 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;
+ size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing;
+ coord_t x0 = bounding_box.min(0);
if (params.full_infill())
x0 += (line_spacing + SCALED_EPSILON) / 2;
@@ -842,8 +842,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
const Point &p1 = contour[iPrev];
const Point &p2 = contour[iSegment];
// Which of the equally spaced vertical lines is intersected by this segment?
- coord_t l = p1.x;
- coord_t r = p2.x;
+ coord_t l = p1(0);
+ coord_t r = p2(0);
if (l > r)
std::swap(l, r);
// il, ir are the left / right indices of vertical lines intersecting a segment
@@ -869,33 +869,33 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
assert(l <= this_x);
assert(r >= this_x);
// Calculate the intersection position in y axis. x is known.
- if (p1.x == this_x) {
- if (p2.x == this_x) {
+ if (p1(0) == this_x) {
+ if (p2(0) == this_x) {
// Ignore strictly vertical segments.
continue;
}
- is.pos_p = p1.y;
+ is.pos_p = p1(1);
is.pos_q = 1;
- } else if (p2.x == this_x) {
- is.pos_p = p2.y;
+ } else if (p2(0) == this_x) {
+ is.pos_p = p2(1);
is.pos_q = 1;
} else {
// First calculate the intersection parameter 't' as a rational number with non negative denominator.
- if (p2.x > p1.x) {
- is.pos_p = this_x - p1.x;
- is.pos_q = p2.x - p1.x;
+ if (p2(0) > p1(0)) {
+ is.pos_p = this_x - p1(0);
+ is.pos_q = p2(0) - p1(0);
} else {
- is.pos_p = p1.x - this_x;
- is.pos_q = p1.x - p2.x;
+ is.pos_p = p1(0) - this_x;
+ is.pos_q = p1(0) - p2(0);
}
assert(is.pos_p >= 0 && is.pos_p <= is.pos_q);
// Make an intersection point from the 't'.
- is.pos_p *= int64_t(p2.y - p1.y);
- is.pos_p += p1.y * int64_t(is.pos_q);
+ is.pos_p *= int64_t(p2(1) - p1(1));
+ is.pos_p += p1(1) * int64_t(is.pos_q);
}
// +-1 to take rounding into account.
- assert(is.pos() + 1 >= std::min(p1.y, p2.y));
- assert(is.pos() <= std::max(p1.y, p2.y) + 1);
+ assert(is.pos() + 1 >= std::min(p1(1), p2(1)));
+ assert(is.pos() <= std::max(p1(1), p2(1)) + 1);
segs[i].intersections.push_back(is);
}
}
@@ -919,7 +919,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
const Points &contour = poly_with_offset.contour(iContour).points;
size_t iSegment = sil.intersections[i].iSegment;
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
- coord_t dir = contour[iSegment].x - contour[iPrev].x;
+ coord_t dir = contour[iSegment](0) - contour[iPrev](0);
bool low = dir > 0;
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
@@ -1066,7 +1066,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
intrsctn.consumed_vertical_up :
seg.intersections[i-1].consumed_vertical_up;
if (! consumed) {
- coordf_t dist2 = sqr(coordf_t(pointLast.x - seg.pos)) + sqr(coordf_t(pointLast.y - intrsctn.pos()));
+ coordf_t dist2 = sqr(coordf_t(pointLast(0) - seg.pos)) + sqr(coordf_t(pointLast(1) - intrsctn.pos()));
if (dist2 < dist2min) {
dist2min = dist2;
i_vline = i_vline2;
@@ -1356,8 +1356,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// Handle nearly zero length edges.
if (polyline_current->points.size() <= 1 ||
(polyline_current->points.size() == 2 &&
- std::abs(polyline_current->points.front().x - polyline_current->points.back().x) < SCALED_EPSILON &&
- std::abs(polyline_current->points.front().y - polyline_current->points.back().y) < SCALED_EPSILON))
+ std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON &&
+ std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON))
polylines_out.pop_back();
intrsctn = NULL;
i_intersection = -1;
@@ -1383,7 +1383,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP
// paths must be rotated back
for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_initial; it != polylines_out.end(); ++ it) {
// No need to translate, the absolute position is irrelevant.
- // it->translate(- rotate_vector.second.x, - rotate_vector.second.y);
+ // it->translate(- rotate_vector.second(0), - rotate_vector.second(1));
assert(! it->has_duplicate_points());
it->rotate(rotate_vector.first);
//FIXME rather simplify the paths to avoid very short edges?
diff --git a/xs/src/libslic3r/Fill/FillRectilinear3.cpp b/xs/src/libslic3r/Fill/FillRectilinear3.cpp
index 1e7e3a1cd..8fc129eac 100644
--- a/xs/src/libslic3r/Fill/FillRectilinear3.cpp
+++ b/xs/src/libslic3r/Fill/FillRectilinear3.cpp
@@ -217,30 +217,30 @@ Point SegmentIntersection::pos() const
const Point &seg_start = poly.points[(this->iSegment == 0) ? poly.points.size() - 1 : this->iSegment - 1];
const Point &seg_end = poly.points[this->iSegment];
// Point, vector of the segment.
- const Pointf p1 = convert_to<Pointf>(seg_start);
- const Pointf v1 = convert_to<Pointf>(seg_end - seg_start);
+ const Vec2d p1(seg_start.cast<coordf_t>());
+ const Vec2d v1((seg_end - seg_start).cast<coordf_t>());
// Point, vector of this hatching line.
- const Pointf p2 = convert_to<Pointf>(line->pos);
- const Pointf v2 = convert_to<Pointf>(line->dir);
+ const Vec2d p2(line->pos.cast<coordf_t>());
+ const Vec2d v2(line->dir.cast<coordf_t>());
// Intersect the two rays.
- double denom = v1.x * v2.y - v2.x * v1.y;
+ double denom = v1(0) * v2(1) - v2(0) * v1(1);
Point out;
if (denom == 0.) {
// Lines are collinear. As the pos() method is not supposed to be called on collinear vectors,
// the source vectors are not quite collinear. Return the center of the contour segment.
out = seg_start + seg_end;
- out.x >>= 1;
- out.y >>= 1;
+ out(0) >>= 1;
+ out(1) >>= 1;
} else {
// Find the intersection point.
- double t = (v2.x * (p1.y - p2.y) - v2.y * (p1.x - p2.x)) / denom;
+ double t = (v2(0) * (p1(1) - p2(1)) - v2(1) * (p1(0) - p2(0))) / denom;
if (t < 0.)
out = seg_start;
else if (t > 1.)
out = seg_end;
else {
- out.x = coord_t(floor(p1.x + t * v1.x + 0.5));
- out.y = coord_t(floor(p1.y + t * v1.y + 0.5));
+ out(0) = coord_t(floor(p1(0) + t * v1(0) + 0.5));
+ out(1) = coord_t(floor(p1(1) + t * v1(1) + 0.5));
}
}
return out;
@@ -276,13 +276,13 @@ int SegmentIntersection::ordering_along_line(const SegmentIntersection &other) c
// other.iSegment succeeds this->iSegment
assert(seg_end_a == seg_start_b);
// Avoid calling the 128bit x 128bit multiplication below if this->line intersects the common point.
- if (cross(this->line->dir, seg_end_b - this->line->pos) == 0)
+ if (cross2(Vec2i64(this->line->dir.cast<int64_t>()), (seg_end_b - this->line->pos).cast<int64_t>()) == 0)
return 0;
} else if ((other.iSegment + 1) % poly_a.points.size() == this->iSegment) {
// this->iSegment succeeds other.iSegment
assert(seg_start_a == seg_end_b);
// Avoid calling the 128bit x 128bit multiplication below if this->line intersects the common point.
- if (cross(this->line->dir, seg_start_a - this->line->pos) == 0)
+ if (cross2(Vec2i64(this->line->dir.cast<int64_t>()), (seg_start_a - this->line->pos).cast<int64_t>()) == 0)
return 0;
} else {
// General case.
@@ -290,35 +290,35 @@ int SegmentIntersection::ordering_along_line(const SegmentIntersection &other) c
}
// First test, whether both points of one segment are completely in one half-plane of the other line.
- const Point vec_b = seg_end_b - seg_start_b;
- int side_start = signum(cross(vec_b, seg_start_a - seg_start_b));
- int side_end = signum(cross(vec_b, seg_end_a - seg_start_b));
+ const Vec2i64 vec_b = (seg_end_b - seg_start_b).cast<int64_t>();
+ int side_start = signum(cross2(vec_b, (seg_start_a - seg_start_b).cast<int64_t>()));
+ int side_end = signum(cross2(vec_b, (seg_end_a - seg_start_b).cast<int64_t>()));
int side = side_start * side_end;
if (side > 0)
// This segment is completely inside one half-plane of the other line, therefore the ordering is trivial.
- return signum(cross(vec_b, this->line->dir)) * side_start;
+ return signum(cross2(vec_b, this->line->dir.cast<int64_t>())) * side_start;
- const Point vec_a = seg_end_a - seg_start_a;
- int side_start2 = signum(cross(vec_a, seg_start_b - seg_start_a));
- int side_end2 = signum(cross(vec_a, seg_end_b - seg_start_a));
+ const Vec2i64 vec_a = (seg_end_a - seg_start_a).cast<int64_t>();
+ int side_start2 = signum(cross2(vec_a, (seg_start_b - seg_start_a).cast<int64_t>()));
+ int side_end2 = signum(cross2(vec_a, (seg_end_b - seg_start_a).cast<int64_t>()));
int side2 = side_start2 * side_end2;
//if (side == 0 && side2 == 0)
// The segments share one of their end points.
if (side2 > 0)
// This segment is completely inside one half-plane of the other line, therefore the ordering is trivial.
- return signum(cross(this->line->dir, vec_a)) * side_start2;
+ return signum(cross2(this->line->dir.cast<int64_t>(), vec_a)) * side_start2;
// The two segments intersect and they are not sucessive segments of the same contour.
// Ordering of the points depends on the position of the segment intersection (left / right from this->line),
// therefore a simple test over the input segment end points is not sufficient.
// Find the parameters of intersection of the two segmetns with this->line.
- int64_t denom1 = cross(this->line->dir, vec_a);
- int64_t denom2 = cross(this->line->dir, vec_b);
- Point vx_a = seg_start_a - this->line->pos;
- Point vx_b = seg_start_b - this->line->pos;
- int64_t t1_times_denom1 = int64_t(vx_a.x) * int64_t(vec_a.y) - int64_t(vx_a.y) * int64_t(vec_a.x);
- int64_t t2_times_denom2 = int64_t(vx_b.x) * int64_t(vec_b.y) - int64_t(vx_b.y) * int64_t(vec_b.x);
+ int64_t denom1 = cross2(this->line->dir.cast<int64_t>(), vec_a);
+ int64_t denom2 = cross2(this->line->dir.cast<int64_t>(), vec_b);
+ Vec2i64 vx_a = (seg_start_a - this->line->pos).cast<int64_t>();
+ Vec2i64 vx_b = (seg_start_b - this->line->pos).cast<int64_t>();
+ int64_t t1_times_denom1 = vx_a(0) * vec_a(1) - vx_a(1) * vec_a(0);
+ int64_t t2_times_denom2 = vx_b(0) * vec_b(1) - vx_b(1) * vec_b(0);
assert(denom1 != 0);
assert(denom2 != 0);
return Int128::compare_rationals_filtered(t1_times_denom1, denom1, t2_times_denom2, denom2);
@@ -330,7 +330,7 @@ bool SegmentIntersection::operator<(const SegmentIntersection &other) const
#ifdef _DEBUG
Point p1 = this->pos();
Point p2 = other.pos();
- int64_t d = dot(this->line->dir, p2 - p1);
+ int64_t d = this->line->dir.cast<int64_t>().dot((p2 - p1).cast<int64_t>());
#endif /* _DEBUG */
int ordering = this->ordering_along_line(other);
#ifdef _DEBUG
@@ -389,16 +389,16 @@ static bool prepare_infill_hatching_segments(
// Define the flow spacing according to requested density.
if (params.full_infill() && ! params.dont_adjust) {
// Full infill, adjust the line spacing to fit an integer number of lines.
- out.line_spacing = Fill::_adjust_solid_spacing(bounding_box.size().x, line_spacing);
+ out.line_spacing = Fill::_adjust_solid_spacing(bounding_box.size()(0), line_spacing);
// Report back the adjusted line spacing.
- fill_dir_params.spacing = float(unscale(line_spacing));
+ fill_dir_params.spacing = unscale<double>(line_spacing);
} else {
// Extend bounding box so that our pattern will be aligned with the other layers.
// Transform the reference point to the rotated coordinate system.
Point refpt = rotate_vector.second.rotated(- out.angle);
// _align_to_grid will not work correctly with positive pattern_shift.
coord_t pattern_shift_scaled = coord_t(scale_(fill_dir_params.pattern_shift)) % line_spacing;
- refpt.x -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
+ refpt(0) -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
bounding_box.merge(Fill::_align_to_grid(
bounding_box.min,
Point(line_spacing, line_spacing),
@@ -407,13 +407,13 @@ static bool prepare_infill_hatching_segments(
// 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;
+ size_t n_vlines = (bounding_box.max(0) - bounding_box.min(0) + line_spacing - 1) / line_spacing;
+ coord_t x0 = bounding_box.min(0);
if (params.full_infill())
x0 += coord_t((line_spacing + SCALED_EPSILON) / 2);
out.line_spacing = line_spacing;
- out.start_point = Point(x0, bounding_box.min.y);
+ out.start_point = Point(x0, bounding_box.min(1));
out.start_point.rotate(out.angle);
#ifdef SLIC3R_DEBUG
@@ -436,10 +436,10 @@ static bool prepare_infill_hatching_segments(
for (size_t i = 0; i < n_vlines; ++ i) {
auto &seg = out.segs[i];
seg.idx = i;
- // seg.x = x0 + coord_t(i) * line_spacing;
+ // seg(0) = x0 + coord_t(i) * line_spacing;
coord_t x = x0 + coord_t(i) * line_spacing;
- seg.pos.x = coord_t(floor(cos_a * x - sin_a * bounding_box.min.y + 0.5));
- seg.pos.y = coord_t(floor(cos_a * bounding_box.min.y + sin_a * x + 0.5));
+ seg.pos(0) = coord_t(floor(cos_a * x - sin_a * bounding_box.min(1) + 0.5));
+ seg.pos(1) = coord_t(floor(cos_a * bounding_box.min(1) + sin_a * x + 0.5));
seg.dir = out.direction;
}
@@ -454,7 +454,7 @@ static bool prepare_infill_hatching_segments(
const Point *pr = &contour[iSegment];
// Orient the segment to the direction vector.
const Point v = *pr - *pl;
- int orientation = Int128::sign_determinant_2x2_filtered(v.x, v.y, out.direction.x, out.direction.y);
+ int orientation = Int128::sign_determinant_2x2_filtered(v(0), v(1), out.direction(0), out.direction(1));
if (orientation == 0)
// Ignore strictly vertical segments.
continue;
@@ -462,26 +462,26 @@ static bool prepare_infill_hatching_segments(
// Always orient the input segment consistently towards the hatching direction.
std::swap(pl, pr);
// Which of the equally spaced vertical lines is intersected by this segment?
- coord_t l = (coord_t)floor(cos_a * pl->x + sin_a * pl->y - SCALED_EPSILON);
- coord_t r = (coord_t)ceil (cos_a * pr->x + sin_a * pr->y + SCALED_EPSILON);
+ coord_t l = (coord_t)floor(cos_a * (*pl)(0) + sin_a * (*pl)(1) - SCALED_EPSILON);
+ coord_t r = (coord_t)ceil (cos_a * (*pr)(0) + sin_a * (*pr)(1) + SCALED_EPSILON);
assert(l < r - SCALED_EPSILON);
// il, ir are the left / right indices of vertical lines intersecting a segment
int il = std::max<int>(0, (l - x0 + line_spacing) / line_spacing);
int ir = std::min<int>(int(out.segs.size()) - 1, (r - x0) / line_spacing);
// The previous tests were done with floating point arithmetics over an epsilon-extended interval.
// Now do the same tests with exact arithmetics over the exact interval.
- while (il <= ir && Int128::orient(out.segs[il].pos, out.segs[il].pos + out.direction, *pl) < 0)
+ while (il <= ir && int128::orient(out.segs[il].pos, out.segs[il].pos + out.direction, *pl) < 0)
++ il;
- while (il <= ir && Int128::orient(out.segs[ir].pos, out.segs[ir].pos + out.direction, *pr) > 0)
+ while (il <= ir && int128::orient(out.segs[ir].pos, out.segs[ir].pos + out.direction, *pr) > 0)
-- ir;
// Here it is ensured, that
// 1) out.seg is not parallel to (pl, pr)
// 2) all lines from il to ir intersect <pl, pr>.
assert(il >= 0 && ir < int(out.segs.size()));
for (int i = il; i <= ir; ++ i) {
- // assert(out.segs[i].x == i * line_spacing + x0);
- // assert(l <= out.segs[i].x);
- // assert(r >= out.segs[i].x);
+ // assert(out.segs[i](0) == i * line_spacing + x0);
+ // assert(l <= out.segs[i](0));
+ // assert(r >= out.segs[i](0));
SegmentIntersection is;
is.line = &out.segs[i];
is.expoly_with_offset = &poly_with_offset;
@@ -489,12 +489,12 @@ static bool prepare_infill_hatching_segments(
is.iSegment = iSegment;
// Test whether the calculated intersection point falls into the bounding box of the input segment.
// +-1 to take rounding into account.
- assert(Int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0);
- assert(Int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0);
- assert(is.pos().x + 1 >= std::min(pl->x, pr->x));
- assert(is.pos().y + 1 >= std::min(pl->y, pr->y));
- assert(is.pos().x <= std::max(pl->x, pr->x) + 1);
- assert(is.pos().y <= std::max(pl->y, pr->y) + 1);
+ assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pl) >= 0);
+ assert(int128::orient(out.segs[i].pos, out.segs[i].pos + out.direction, *pr) <= 0);
+ assert(is.pos()(0) + 1 >= std::min((*pl)(0), (*pr)(0)));
+ assert(is.pos()(1) + 1 >= std::min((*pl)(1), (*pr)(1)));
+ assert(is.pos()(0) <= std::max((*pl)(0), (*pr)(0)) + 1);
+ assert(is.pos()(1) <= std::max((*pl)(1), (*pr)(1)) + 1);
out.segs[i].intersections.push_back(is);
}
}
@@ -510,7 +510,7 @@ static bool prepare_infill_hatching_segments(
for (size_t i = 1; i < sil.intersections.size(); ++ i) {
Point p1 = sil.intersections[i - 1].pos();
Point p2 = sil.intersections[i].pos();
- int64_t d = dot(sil.dir, p2 - p1);
+ int64_t d = sil.dir.cast<int64_t>().dot((p2 - p1).cast<int64_t>());
assert(d >= - int64_t(SCALED_EPSILON));
}
#endif /* _DEBUG */
@@ -527,7 +527,7 @@ static bool prepare_infill_hatching_segments(
const Points &contour = poly_with_offset.contour(iContour).points;
size_t iSegment = sil.intersections[i].iSegment;
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
- int dir = Int128::cross(contour[iSegment] - contour[iPrev], sil.dir);
+ int dir = int128::cross(contour[iSegment] - contour[iPrev], sil.dir);
bool low = dir > 0;
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
@@ -659,12 +659,12 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po
Point px = (i == 0) ? p1 : p2;
Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1];
Point pb = poly.points[seg];
- if (pa.x > pb.x)
- std::swap(pa.x, pb.x);
- if (pa.y > pb.y)
- std::swap(pa.y, pb.y);
- assert(px.x >= pa.x && px.x <= pb.x);
- assert(px.y >= pa.y && px.y <= pb.y);
+ if (pa(0) > pb(0))
+ std::swap(pa(0), pb(0));
+ if (pa(1) > pb(1))
+ std::swap(pa(1), pb(1));
+ assert(px(0) >= pa(0) && px(0) <= pb(0));
+ assert(px(1) >= pa(1) && px(1) <= pb(1));
}
#endif /* SLIC3R_DEBUG */
const Point *pPrev = &p1;
@@ -672,14 +672,14 @@ static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Po
coordf_t len = 0;
if (seg1 <= seg2) {
for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis)
- len += pPrev->distance_to(*(pThis = &poly.points[i]));
+ len += (*pPrev - *(pThis = &poly.points[i])).cast<double>().norm();
} else {
for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis)
- len += pPrev->distance_to(*(pThis = &poly.points[i]));
+ len += (*pPrev - *(pThis = &poly.points[i])).cast<double>().norm();
for (size_t i = 0; i < seg2; ++ i, pPrev = pThis)
- len += pPrev->distance_to(*(pThis = &poly.points[i]));
+ len += (*pPrev - *(pThis = &poly.points[i])).cast<double>().norm();
}
- len += pPrev->distance_to(p2);
+ len += (*pPrev - p2).cast<double>().norm();
return len;
}
@@ -1191,7 +1191,7 @@ static bool fill_hatching_segments_legacy(
intrsctn.consumed_vertical_up :
seg.intersections[i-1].consumed_vertical_up;
if (! consumed) {
- coordf_t dist2 = pointLast.distance_to(intrsctn.pos());
+ coordf_t dist2 = (intrsctn.pos() - pointLast).cast<double>().norm();
if (dist2 < dist2min) {
dist2min = dist2;
i_vline = i_vline2;
@@ -1481,8 +1481,8 @@ static bool fill_hatching_segments_legacy(
// Handle nearly zero length edges.
if (polyline_current->points.size() <= 1 ||
(polyline_current->points.size() == 2 &&
- std::abs(polyline_current->points.front().x - polyline_current->points.back().x) < SCALED_EPSILON &&
- std::abs(polyline_current->points.front().y - polyline_current->points.back().y) < SCALED_EPSILON))
+ std::abs(polyline_current->points.front()(0) - polyline_current->points.back()(0)) < SCALED_EPSILON &&
+ std::abs(polyline_current->points.front()(1) - polyline_current->points.back()(1)) < SCALED_EPSILON))
polylines_out.pop_back();
intrsctn = NULL;
i_intersection = -1;
@@ -1510,7 +1510,7 @@ static bool fill_hatching_segments_legacy(
// paths must be rotated back
for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_initial; it != polylines_out.end(); ++ it) {
// No need to translate, the absolute position is irrelevant.
- // it->translate(- rotate_vector.second.x, - rotate_vector.second.y);
+ // it->translate(- rotate_vector.second(0), - rotate_vector.second(1));
assert(! it->has_duplicate_points());
//it->rotate(rotate_vector.first);
//FIXME rather simplify the paths to avoid very short edges?
diff --git a/xs/src/libslic3r/Format/3mf.cpp b/xs/src/libslic3r/Format/3mf.cpp
index b34b8989e..464cbb8e6 100644
--- a/xs/src/libslic3r/Format/3mf.cpp
+++ b/xs/src/libslic3r/Format/3mf.cpp
@@ -16,6 +16,12 @@
#include <Eigen/Dense>
#include <miniz/miniz_zip.h>
+// VERSION NUMBERS
+// 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
+// 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
+const unsigned int VERSION_3MF = 1;
+const char* SLIC3RPE_3MF_VERSION = "slic3rpe:Version3mf"; // definition of the metadata name saved into .model file
+
const std::string MODEL_FOLDER = "3D/";
const std::string MODEL_EXTENSION = ".model";
const std::string MODEL_FILE = "3D/3dmodel.model"; // << this is the only format of the string which works with CURA
@@ -23,6 +29,7 @@ const std::string CONTENT_TYPES_FILE = "[Content_Types].xml";
const std::string RELATIONSHIPS_FILE = "_rels/.rels";
const std::string PRINT_CONFIG_FILE = "Metadata/Slic3r_PE.config";
const std::string MODEL_CONFIG_FILE = "Metadata/Slic3r_PE_model.config";
+const std::string LAYER_HEIGHTS_PROFILE_FILE = "Metadata/Slic3r_PE_layer_heights_profile.txt";
const char* MODEL_TAG = "model";
const char* RESOURCES_TAG = "resources";
@@ -36,9 +43,9 @@ const char* COMPONENTS_TAG = "components";
const char* COMPONENT_TAG = "component";
const char* BUILD_TAG = "build";
const char* ITEM_TAG = "item";
+const char* METADATA_TAG = "metadata";
const char* CONFIG_TAG = "config";
-const char* METADATA_TAG = "metadata";
const char* VOLUME_TAG = "volume";
const char* UNIT_ATTR = "unit";
@@ -80,8 +87,6 @@ const char* INVALID_OBJECT_TYPES[] =
"other"
};
-typedef Eigen::Matrix<float, 4, 4, Eigen::RowMajor> Matrix4x4;
-
const char* get_attribute_value_charptr(const char** attributes, unsigned int attributes_size, const char* attribute_key)
{
if ((attributes == nullptr) || (attributes_size == 0) || (attributes_size % 2 != 0) || (attribute_key == nullptr))
@@ -114,11 +119,11 @@ int get_attribute_value_int(const char** attributes, unsigned int attributes_siz
return (text != nullptr) ? ::atoi(text) : 0;
}
-Matrix4x4 get_matrix_from_string(const std::string& mat_str)
+Slic3r::Transform3d get_transform_from_string(const std::string& mat_str)
{
if (mat_str.empty())
// empty string means default identity matrix
- return Matrix4x4::Identity();
+ return Slic3r::Transform3d::Identity();
std::vector<std::string> mat_elements_str;
boost::split(mat_elements_str, mat_str, boost::is_any_of(" "), boost::token_compress_on);
@@ -126,9 +131,9 @@ Matrix4x4 get_matrix_from_string(const std::string& mat_str)
unsigned int size = (unsigned int)mat_elements_str.size();
if (size != 12)
// invalid data, return identity matrix
- return Matrix4x4::Identity();
+ return Slic3r::Transform3d::Identity();
- Matrix4x4 ret = Matrix4x4::Identity();
+ Slic3r::Transform3d ret = Slic3r::Transform3d::Identity();
unsigned int i = 0;
// matrices are stored into 3mf files as 4x3
// we need to transpose them
@@ -136,7 +141,7 @@ Matrix4x4 get_matrix_from_string(const std::string& mat_str)
{
for (unsigned int r = 0; r < 3; ++r)
{
- ret(r, c) = (float)::atof(mat_elements_str[i++].c_str());
+ ret(r, c) = ::atof(mat_elements_str[i++].c_str());
}
}
return ret;
@@ -202,17 +207,17 @@ namespace Slic3r {
struct Component
{
int object_id;
- Matrix4x4 matrix;
+ Transform3d transform;
explicit Component(int object_id)
: object_id(object_id)
- , matrix(Matrix4x4::Identity())
+ , transform(Transform3d::Identity())
{
}
- Component(int object_id, const Matrix4x4& matrix)
+ Component(int object_id, const Transform3d& transform)
: object_id(object_id)
- , matrix(matrix)
+ , transform(transform)
{
}
};
@@ -266,11 +271,11 @@ namespace Slic3r {
struct Instance
{
ModelInstance* instance;
- Matrix4x4 matrix;
+ Transform3d transform;
- Instance(ModelInstance* instance, const Matrix4x4& matrix)
+ Instance(ModelInstance* instance, const Transform3d& transform)
: instance(instance)
- , matrix(matrix)
+ , transform(transform)
{
}
};
@@ -315,6 +320,10 @@ namespace Slic3r {
typedef std::vector<Instance> InstancesList;
typedef std::map<int, ObjectMetadata> IdToMetadataMap;
typedef std::map<int, Geometry> IdToGeometryMap;
+ typedef std::map<int, std::vector<coordf_t>> IdToLayerHeightsProfileMap;
+
+ // Version of the 3mf file
+ unsigned int m_version;
XML_Parser m_xml_parser;
Model* m_model;
@@ -326,6 +335,9 @@ namespace Slic3r {
IdToGeometryMap m_geometries;
CurrentConfig m_curr_config;
IdToMetadataMap m_objects_metadata;
+ IdToLayerHeightsProfileMap m_layer_heights_profiles;
+ std::string m_curr_metadata_name;
+ std::string m_curr_characters;
public:
_3MF_Importer();
@@ -339,12 +351,14 @@ namespace Slic3r {
bool _load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle);
bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
- bool _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename);
+ void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat);
+ void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename);
bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model);
// handlers to parse the .model file
void _handle_start_model_xml_element(const char* name, const char** attributes);
void _handle_end_model_xml_element(const char* name);
+ void _handle_model_xml_characters(const XML_Char* s, int len);
// handlers to parse the MODEL_CONFIG_FILE file
void _handle_start_config_xml_element(const char* name, const char** attributes);
@@ -386,10 +400,12 @@ namespace Slic3r {
bool _handle_start_item(const char** attributes, unsigned int num_attributes);
bool _handle_end_item();
- bool _create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter);
+ bool _handle_start_metadata(const char** attributes, unsigned int num_attributes);
+ bool _handle_end_metadata();
- void _apply_transform(ModelObject& object, const Matrix4x4& matrix);
- void _apply_transform(ModelInstance& instance, const Matrix4x4& matrix);
+ bool _create_object_instance(int object_id, const Transform3d& transform, unsigned int recur_counter);
+
+ void _apply_transform(ModelInstance& instance, const Transform3d& transform);
bool _handle_start_config(const char** attributes, unsigned int num_attributes);
bool _handle_end_config();
@@ -408,6 +424,7 @@ namespace Slic3r {
// callbacks to parse the .model file
static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes);
static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name);
+ static void XMLCALL _handle_model_xml_characters(void* userData, const XML_Char* s, int len);
// callbacks to parse the MODEL_CONFIG_FILE file
static void XMLCALL _handle_start_config_xml_element(void* userData, const char* name, const char** attributes);
@@ -415,9 +432,12 @@ namespace Slic3r {
};
_3MF_Importer::_3MF_Importer()
- : m_xml_parser(nullptr)
+ : m_version(0)
+ , m_xml_parser(nullptr)
, m_model(nullptr)
, m_unit_factor(1.0f)
+ , m_curr_metadata_name("")
+ , m_curr_characters("")
{
}
@@ -428,6 +448,7 @@ namespace Slic3r {
bool _3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PresetBundle& bundle)
{
+ m_version = 0;
m_model = &model;
m_unit_factor = 1.0f;
m_curr_object.reset();
@@ -438,6 +459,9 @@ namespace Slic3r {
m_curr_config.object_id = -1;
m_curr_config.volume_id = -1;
m_objects_metadata.clear();
+ m_layer_heights_profiles.clear();
+ m_curr_metadata_name.clear();
+ m_curr_characters.clear();
clear_errors();
return _load_model_from_file(filename, model, bundle);
@@ -473,6 +497,8 @@ namespace Slic3r {
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
mz_zip_archive_file_stat stat;
+
+ // we first loop the entries to read from the archive the .model file only, in order to extract the version from it
for (mz_uint i = 0; i < num_entries; ++i)
{
if (mz_zip_reader_file_stat(&archive, i, &stat))
@@ -490,15 +516,26 @@ namespace Slic3r {
return false;
}
}
+ }
+ }
+
+ // we then loop again the entries to read other files stored in the archive
+ for (mz_uint i = 0; i < num_entries; ++i)
+ {
+ if (mz_zip_reader_file_stat(&archive, i, &stat))
+ {
+ std::string name(stat.m_filename);
+ std::replace(name.begin(), name.end(), '\\', '/');
+
+ if (boost::algorithm::iequals(name, LAYER_HEIGHTS_PROFILE_FILE))
+ {
+ // extract slic3r lazer heights profile file
+ _extract_layer_heights_profile_config_from_archive(archive, stat);
+ }
else if (boost::algorithm::iequals(name, PRINT_CONFIG_FILE))
{
// extract slic3r print config file
- if (!_extract_print_config_from_archive(archive, stat, bundle, filename))
- {
- mz_zip_reader_end(&archive);
- add_error("Archive does not contain a valid print config");
- return false;
- }
+ _extract_print_config_from_archive(archive, stat, bundle, filename);
}
else if (boost::algorithm::iequals(name, MODEL_CONFIG_FILE))
{
@@ -527,6 +564,13 @@ namespace Slic3r {
return false;
}
+ IdToLayerHeightsProfileMap::iterator obj_layer_heights_profile = m_layer_heights_profiles.find(object.first);
+ if (obj_layer_heights_profile != m_layer_heights_profiles.end())
+ {
+ object.second->layer_height_profile = obj_layer_heights_profile->second;
+ object.second->layer_height_profile_valid = true;
+ }
+
IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first);
if (obj_metadata != m_objects_metadata.end())
{
@@ -557,19 +601,6 @@ namespace Slic3r {
if (!_generate_volumes(*object.second, obj_geometry->second, *volumes_ptr))
return false;
-
- // apply transformation if the object contains a single instance
- if (object.second->instances.size() == 1)
- {
- for (const Instance& instance : m_instances)
- {
- if (object.second->instances[0] == instance.instance)
- {
- _apply_transform(*object.second, instance.matrix);
- break;
- }
- }
- }
}
// fixes the min z of the model if negative
@@ -597,6 +628,7 @@ namespace Slic3r {
XML_SetUserData(m_xml_parser, (void*)this);
XML_SetElementHandler(m_xml_parser, _3MF_Importer::_handle_start_model_xml_element, _3MF_Importer::_handle_end_model_xml_element);
+ XML_SetCharacterDataHandler(m_xml_parser, _3MF_Importer::_handle_model_xml_characters);
void* parser_buffer = XML_GetBuffer(m_xml_parser, (int)stat.m_uncomp_size);
if (parser_buffer == nullptr)
@@ -623,23 +655,90 @@ namespace Slic3r {
return true;
}
- bool _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename)
+ void _3MF_Importer::_extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, PresetBundle& bundle, const std::string& archive_filename)
{
if (stat.m_uncomp_size > 0)
{
- std::vector<char> buffer((size_t)stat.m_uncomp_size + 1, 0);
+ std::string buffer((size_t)stat.m_uncomp_size, 0);
mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
if (res == 0)
{
add_error("Error while reading config data to buffer");
- return false;
+ return;
}
-
- buffer.back() = '\0';
bundle.load_config_string(buffer.data(), archive_filename.c_str());
}
+ }
- return true;
+ void _3MF_Importer::_extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat)
+ {
+ if (stat.m_uncomp_size > 0)
+ {
+ std::string buffer((size_t)stat.m_uncomp_size, 0);
+ mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (void*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
+ if (res == 0)
+ {
+ add_error("Error while reading layer heights profile data to buffer");
+ return;
+ }
+
+ if (buffer.back() == '\n')
+ buffer.pop_back();
+
+ std::vector<std::string> objects;
+ boost::split(objects, buffer, boost::is_any_of("\n"), boost::token_compress_off);
+
+ for (const std::string& object : objects)
+ {
+ std::vector<std::string> object_data;
+ boost::split(object_data, object, boost::is_any_of("|"), boost::token_compress_off);
+ if (object_data.size() != 2)
+ {
+ add_error("Error while reading object data");
+ continue;
+ }
+
+ std::vector<std::string> object_data_id;
+ boost::split(object_data_id, object_data[0], boost::is_any_of("="), boost::token_compress_off);
+ if (object_data_id.size() != 2)
+ {
+ add_error("Error while reading object id");
+ continue;
+ }
+
+ int object_id = std::atoi(object_data_id[1].c_str());
+ if (object_id == 0)
+ {
+ add_error("Found invalid object id");
+ continue;
+ }
+
+ IdToLayerHeightsProfileMap::iterator object_item = m_layer_heights_profiles.find(object_id);
+ if (object_item != m_layer_heights_profiles.end())
+ {
+ add_error("Found duplicated layer heights profile");
+ continue;
+ }
+
+ std::vector<std::string> object_data_profile;
+ boost::split(object_data_profile, object_data[1], boost::is_any_of(";"), boost::token_compress_off);
+ if ((object_data_profile.size() <= 4) || (object_data_profile.size() % 2 != 0))
+ {
+ add_error("Found invalid layer heights profile");
+ continue;
+ }
+
+ std::vector<coordf_t> profile;
+ profile.reserve(object_data_profile.size());
+
+ for (const std::string& value : object_data_profile)
+ {
+ profile.push_back((coordf_t)std::atof(value.c_str()));
+ }
+
+ m_layer_heights_profiles.insert(IdToLayerHeightsProfileMap::value_type(object_id, profile));
+ }
+ }
}
bool _3MF_Importer::_extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model)
@@ -719,6 +818,8 @@ namespace Slic3r {
res = _handle_start_build(attributes, num_attributes);
else if (::strcmp(ITEM_TAG, name) == 0)
res = _handle_start_item(attributes, num_attributes);
+ else if (::strcmp(METADATA_TAG, name) == 0)
+ res = _handle_start_metadata(attributes, num_attributes);
if (!res)
_stop_xml_parser();
@@ -755,11 +856,18 @@ namespace Slic3r {
res = _handle_end_build();
else if (::strcmp(ITEM_TAG, name) == 0)
res = _handle_end_item();
+ else if (::strcmp(METADATA_TAG, name) == 0)
+ res = _handle_end_metadata();
if (!res)
_stop_xml_parser();
}
+ void _3MF_Importer::_handle_model_xml_characters(const XML_Char* s, int len)
+ {
+ m_curr_characters.append(s, len);
+ }
+
void _3MF_Importer::_handle_start_config_xml_element(const char* name, const char** attributes)
{
if (m_xml_parser == nullptr)
@@ -822,11 +930,10 @@ namespace Slic3r {
if (instance.instance != nullptr)
{
ModelObject* object = instance.instance->get_object();
- if ((object != nullptr) && (object->instances.size() > 1))
+ if (object != nullptr)
{
- // multiple instances -> apply the matrix to the instance
- // (for single instance the transformation can be applied only after the volumes have been generated)
- _apply_transform(*instance.instance, instance.matrix);
+ // apply the transform to the instance
+ _apply_transform(*instance.instance, instance.transform);
}
}
}
@@ -1010,7 +1117,7 @@ namespace Slic3r {
bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes)
{
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
- Matrix4x4 matrix = get_matrix_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
+ Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
IdToModelObjectMap::iterator object_item = m_objects.find(object_id);
if (object_item == m_objects.end())
@@ -1023,7 +1130,7 @@ namespace Slic3r {
}
}
- m_curr_object.components.emplace_back(object_id, matrix);
+ m_curr_object.components.emplace_back(object_id, transform);
return true;
}
@@ -1056,9 +1163,9 @@ namespace Slic3r {
// see specifications
int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR);
- Matrix4x4 matrix = get_matrix_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
+ Transform3d transform = get_transform_from_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR));
- return _create_object_instance(object_id, matrix, 1);
+ return _create_object_instance(object_id, transform, 1);
}
bool _3MF_Importer::_handle_end_item()
@@ -1067,7 +1174,26 @@ namespace Slic3r {
return true;
}
- bool _3MF_Importer::_create_object_instance(int object_id, const Matrix4x4& matrix, unsigned int recur_counter)
+ bool _3MF_Importer::_handle_start_metadata(const char** attributes, unsigned int num_attributes)
+ {
+ m_curr_characters.clear();
+
+ std::string name = get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
+ if (!name.empty())
+ m_curr_metadata_name = name;
+
+ return true;
+ }
+
+ bool _3MF_Importer::_handle_end_metadata()
+ {
+ if (m_curr_metadata_name == SLIC3RPE_3MF_VERSION)
+ m_version = (unsigned int)atoi(m_curr_characters.c_str());
+
+ return true;
+ }
+
+ bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, unsigned int recur_counter)
{
static const unsigned int MAX_RECURSIONS = 10;
@@ -1104,7 +1230,7 @@ namespace Slic3r {
return false;
}
- m_instances.emplace_back(instance, matrix);
+ m_instances.emplace_back(instance, transform);
}
}
else
@@ -1112,7 +1238,7 @@ namespace Slic3r {
// recursively process nested components
for (const Component& component : it->second)
{
- if (!_create_object_instance(component.object_id, matrix * component.matrix, recur_counter + 1))
+ if (!_create_object_instance(component.object_id, transform * component.transform, recur_counter + 1))
return false;
}
}
@@ -1120,29 +1246,20 @@ namespace Slic3r {
return true;
}
- void _3MF_Importer::_apply_transform(ModelObject& object, const Matrix4x4& matrix)
- {
- float matrix3x4[12] = { matrix(0, 0), matrix(0, 1), matrix(0, 2), matrix(0, 3),
- matrix(1, 0), matrix(1, 1), matrix(1, 2), matrix(1, 3),
- matrix(2, 0), matrix(2, 1), matrix(2, 2), matrix(2, 3) };
-
- object.transform(matrix3x4);
- }
-
- void _3MF_Importer::_apply_transform(ModelInstance& instance, const Matrix4x4& matrix)
+ void _3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform)
{
// slic3r ModelInstance cannot be transformed using a matrix
// we extract from the given matrix only the values currently used
// translation
- double offset_x = (double)matrix(0, 3);
- double offset_y = (double)matrix(1, 3);
- double offset_z = (double)matrix(2, 3);
+ double offset_x = transform(0, 3);
+ double offset_y = transform(1, 3);
+ double offset_z = transform(2, 3);
// scale
- double sx = ::sqrt(sqr((double)matrix(0, 0)) + sqr((double)matrix(1, 0)) + sqr((double)matrix(2, 0)));
- double sy = ::sqrt(sqr((double)matrix(0, 1)) + sqr((double)matrix(1, 1)) + sqr((double)matrix(2, 1)));
- double sz = ::sqrt(sqr((double)matrix(0, 2)) + sqr((double)matrix(1, 2)) + sqr((double)matrix(2, 2)));
+ double sx = ::sqrt(sqr(transform(0, 0)) + sqr(transform(1, 0)) + sqr(transform(2, 0)));
+ double sy = ::sqrt(sqr(transform(0, 1)) + sqr(transform(1, 1)) + sqr(transform(2, 1)));
+ double sz = ::sqrt(sqr(transform(0, 2)) + sqr(transform(1, 2)) + sqr(transform(2, 2)));
// invalid scale value, return
if ((sx == 0.0) || (sy == 0.0) || (sz == 0.0))
@@ -1152,69 +1269,26 @@ namespace Slic3r {
if ((std::abs(sx - sy) > 0.00001) || (std::abs(sx - sz) > 0.00001))
return;
- // rotations (extracted using quaternion)
double inv_sx = 1.0 / sx;
double inv_sy = 1.0 / sy;
double inv_sz = 1.0 / sz;
-
- Eigen::Matrix<double, 3, 3, Eigen::RowMajor> m3x3;
- m3x3 << (double)matrix(0, 0) * inv_sx, (double)matrix(0, 1) * inv_sy, (double)matrix(0, 2) * inv_sz,
- (double)matrix(1, 0) * inv_sx, (double)matrix(1, 1) * inv_sy, (double)matrix(1, 2) * inv_sz,
- (double)matrix(2, 0) * inv_sx, (double)matrix(2, 1) * inv_sy, (double)matrix(2, 2) * inv_sz;
- double qw = 0.5 * ::sqrt(std::max(0.0, 1.0 + m3x3(0, 0) + m3x3(1, 1) + m3x3(2, 2)));
- double qx = 0.5 * ::sqrt(std::max(0.0, 1.0 + m3x3(0, 0) - m3x3(1, 1) - m3x3(2, 2)));
- double qy = 0.5 * ::sqrt(std::max(0.0, 1.0 - m3x3(0, 0) + m3x3(1, 1) - m3x3(2, 2)));
- double qz = 0.5 * ::sqrt(std::max(0.0, 1.0 - m3x3(0, 0) - m3x3(1, 1) + m3x3(2, 2)));
+ Eigen::Matrix3d m3x3;
+ m3x3 << transform(0, 0) * inv_sx, transform(0, 1) * inv_sy, transform(0, 2) * inv_sz,
+ transform(1, 0) * inv_sx, transform(1, 1) * inv_sy, transform(1, 2) * inv_sz,
+ transform(2, 0) * inv_sx, transform(2, 1) * inv_sy, transform(2, 2) * inv_sz;
- double q_magnitude = ::sqrt(sqr(qw) + sqr(qx) + sqr(qy) + sqr(qz));
+ Eigen::AngleAxisd rotation;
+ rotation.fromRotationMatrix(m3x3);
- // invalid length, return
- if (q_magnitude == 0.0)
+ // invalid rotation axis, we currently handle only rotations around Z axis
+ if ((rotation.angle() != 0.0) && (rotation.axis() != Vec3d::UnitZ()) && (rotation.axis() != -Vec3d::UnitZ()))
return;
- double inv_q_magnitude = 1.0 / q_magnitude;
+ double angle_z = (rotation.axis() == Vec3d::UnitZ()) ? rotation.angle() : -rotation.angle();
- qw *= inv_q_magnitude;
- qx *= inv_q_magnitude;
- qy *= inv_q_magnitude;
- qz *= inv_q_magnitude;
-
- double test = qx * qy + qz * qw;
- double angle_x, angle_y, angle_z;
-
- if (test > 0.499)
- {
- // singularity at north pole
- angle_x = 0.0;
- angle_y = 2.0 * ::atan2(qx, qw);
- angle_z = 0.5 * PI;
- }
- else if (test < -0.499)
- {
- // singularity at south pole
- angle_x = 0.0;
- angle_y = -2.0 * ::atan2(qx, qw);
- angle_z = -0.5 * PI;
- }
- else
- {
- angle_x = ::atan2(2.0 * qx * qw - 2.0 * qy * qz, 1.0 - 2.0 * sqr(qx) - 2.0 * sqr(qz));
- angle_y = ::atan2(2.0 * qy * qw - 2.0 * qx * qz, 1.0 - 2.0 * sqr(qy) - 2.0 * sqr(qz));
- angle_z = ::asin(2.0 * qx * qy + 2.0 * qz * qw);
-
- if (angle_x < 0.0)
- angle_x += 2.0 * PI;
-
- if (angle_y < 0.0)
- angle_y += 2.0 * PI;
-
- if (angle_z < 0.0)
- angle_z += 2.0 * PI;
- }
-
- instance.offset.x = offset_x;
- instance.offset.y = offset_y;
+ instance.offset(0) = offset_x;
+ instance.offset(1) = offset_y;
instance.scaling_factor = sx;
instance.rotation = angle_z;
}
@@ -1346,12 +1420,13 @@ namespace Slic3r {
stl_facet& facet = stl.facet_start[i];
for (unsigned int v = 0; v < 3; ++v)
{
- ::memcpy((void*)&facet.vertex[v].x, (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float));
+ ::memcpy(facet.vertex[v].data(), (const void*)&geometry.vertices[geometry.triangles[src_start_id + ii + v] * 3], 3 * sizeof(float));
}
}
stl_get_size(&stl);
volume->mesh.repair();
+ volume->calculate_convex_hull();
// apply volume's name and config data
for (const Metadata& metadata : volume_data.metadata)
@@ -1382,6 +1457,13 @@ namespace Slic3r {
importer->_handle_end_model_xml_element(name);
}
+ void XMLCALL _3MF_Importer::_handle_model_xml_characters(void* userData, const XML_Char* s, int len)
+ {
+ _3MF_Importer* importer = (_3MF_Importer*)userData;
+ if (importer != nullptr)
+ importer->_handle_model_xml_characters(s, len);
+ }
+
void XMLCALL _3MF_Importer::_handle_start_config_xml_element(void* userData, const char* name, const char** attributes)
{
_3MF_Importer* importer = (_3MF_Importer*)userData;
@@ -1401,11 +1483,11 @@ namespace Slic3r {
struct BuildItem
{
unsigned int id;
- Matrix4x4 matrix;
+ Transform3d transform;
- BuildItem(unsigned int id, const Matrix4x4& matrix)
+ BuildItem(unsigned int id, const Transform3d& transform)
: id(id)
- , matrix(matrix)
+ , transform(transform)
{
}
};
@@ -1443,27 +1525,28 @@ namespace Slic3r {
IdToObjectDataMap m_objects_data;
public:
- bool save_model_to_file(const std::string& filename, Model& model, const Print& print);
+ bool save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config);
private:
- bool _save_model_to_file(const std::string& filename, Model& model, const Print& print);
+ bool _save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config);
bool _add_content_types_file_to_archive(mz_zip_archive& archive);
bool _add_relationships_file_to_archive(mz_zip_archive& archive);
bool _add_model_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_object_to_model_stream(std::stringstream& stream, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
bool _add_mesh_to_object_stream(std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
+ bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
bool _add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print);
bool _add_model_config_file_to_archive(mz_zip_archive& archive, const Model& model);
};
- bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print)
+ bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config)
{
clear_errors();
- return _save_model_to_file(filename, model, print);
+ return _save_model_to_file(filename, model, print, export_print_config);
}
- bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print)
+ bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const Print& print, bool export_print_config)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
@@ -1501,14 +1584,25 @@ namespace Slic3r {
return false;
}
- // adds slic3r print config file
- if (!_add_print_config_file_to_archive(archive, print))
+ // adds layer height profile file
+ if (!_add_layer_height_profile_file_to_archive(archive, model))
{
mz_zip_writer_end(&archive);
boost::filesystem::remove(filename);
return false;
}
+ // adds slic3r print config file
+ if (export_print_config)
+ {
+ if (!_add_print_config_file_to_archive(archive, print))
+ {
+ mz_zip_writer_end(&archive);
+ boost::filesystem::remove(filename);
+ return false;
+ }
+ }
+
// adds slic3r model config file
if (!_add_model_config_file_to_archive(archive, model))
{
@@ -1573,7 +1667,8 @@ namespace Slic3r {
{
std::stringstream stream;
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
- stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\">\n";
+ stream << "<" << MODEL_TAG << " unit=\"millimeter\" xml:lang=\"en-US\" xmlns=\"http://schemas.microsoft.com/3dmanufacturing/core/2015/02\" xmlns:slic3rpe=\"http://schemas.slic3r.org/3mf/2017/06\">\n";
+ stream << " <" << METADATA_TAG << " name=\"" << SLIC3RPE_3MF_VERSION << "\">" << VERSION_3MF << "</" << METADATA_TAG << ">\n";
stream << " <" << RESOURCES_TAG << ">\n";
BuildItemsList build_items;
@@ -1641,11 +1736,8 @@ namespace Slic3r {
stream << " </" << COMPONENTS_TAG << ">\n";
}
- Eigen::Affine3f transform;
- transform = Eigen::Translation3f((float)(instance->offset.x + object.origin_translation.x), (float)(instance->offset.y + object.origin_translation.y), (float)object.origin_translation.z)
- * Eigen::AngleAxisf((float)instance->rotation, Eigen::Vector3f::UnitZ())
- * Eigen::Scaling((float)instance->scaling_factor);
- build_items.emplace_back(instance_id, transform.matrix());
+ Transform3d t = instance->world_matrix();
+ build_items.emplace_back(instance_id, t);
stream << " </" << OBJECT_TAG << ">\n";
@@ -1687,10 +1779,9 @@ namespace Slic3r {
for (int i = 0; i < stl.stats.shared_vertices; ++i)
{
stream << " <" << VERTEX_TAG << " ";
- // Subtract origin_translation in order to restore the original local coordinates
- stream << "x=\"" << (stl.v_shared[i].x - object.origin_translation.x) << "\" ";
- stream << "y=\"" << (stl.v_shared[i].y - object.origin_translation.y) << "\" ";
- stream << "z=\"" << (stl.v_shared[i].z - object.origin_translation.z) << "\" />\n";
+ stream << "x=\"" << stl.v_shared[i](0) << "\" ";
+ stream << "y=\"" << stl.v_shared[i](1) << "\" ";
+ stream << "z=\"" << stl.v_shared[i](2) << "\" />\n";
}
}
@@ -1747,7 +1838,7 @@ namespace Slic3r {
{
for (unsigned r = 0; r < 3; ++r)
{
- stream << item.matrix(r, c);
+ stream << item.transform(r, c);
if ((r != 2) || (c != 3))
stream << " ";
}
@@ -1760,6 +1851,44 @@ namespace Slic3r {
return true;
}
+ bool _3MF_Exporter::_add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model)
+ {
+ std::string out = "";
+ char buffer[1024];
+
+ unsigned int count = 0;
+ for (const ModelObject* object : model.objects)
+ {
+ ++count;
+ std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
+ if ((layer_height_profile.size() >= 4) && ((layer_height_profile.size() % 2) == 0))
+ {
+ sprintf(buffer, "object_id=%d|", count);
+ out += buffer;
+
+ // Store the layer height profile as a single semicolon separated list.
+ for (size_t i = 0; i < layer_height_profile.size(); ++i)
+ {
+ sprintf(buffer, (i == 0) ? "%f" : ";%f", layer_height_profile[i]);
+ out += buffer;
+ }
+
+ out += "\n";
+ }
+ }
+
+ if (!out.empty())
+ {
+ if (!mz_zip_writer_add_mem(&archive, LAYER_HEIGHTS_PROFILE_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
+ {
+ add_error("Unable to add layer heights profile file to archive");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
bool _3MF_Exporter::_add_print_config_file_to_archive(mz_zip_archive& archive, const Print& print)
{
char buffer[1024];
@@ -1768,10 +1897,13 @@ namespace Slic3r {
GCode::append_full_config(print, out);
- if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
+ if (!out.empty())
{
- add_error("Unable to add print config file to archive");
- return false;
+ if (!mz_zip_writer_add_mem(&archive, PRINT_CONFIG_FILE.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
+ {
+ add_error("Unable to add print config file to archive");
+ return false;
+ }
}
return true;
@@ -1792,7 +1924,7 @@ namespace Slic3r {
// stores object's name
if (!obj->name.empty())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << obj->name << "\"/>\n";
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"name\" " << VALUE_ATTR << "=\"" << xml_escape(obj->name) << "\"/>\n";
// stores object's config data
for (const std::string& key : obj->config.keys())
@@ -1815,7 +1947,7 @@ namespace Slic3r {
// stores volume's name
if (!volume->name.empty())
- stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << volume->name << "\"/>\n";
+ stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << NAME_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->name) << "\"/>\n";
// stores volume's modifier field
if (volume->modifier)
@@ -1856,20 +1988,17 @@ namespace Slic3r {
_3MF_Importer importer;
bool res = importer.load_model_from_file(path, *model, *bundle);
-
- if (!res)
- importer.log_errors();
-
+ importer.log_errors();
return res;
}
- bool store_3mf(const char* path, Model* model, Print* print)
+ bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
return false;
_3MF_Exporter exporter;
- bool res = exporter.save_model_to_file(path, *model, *print);
+ bool res = exporter.save_model_to_file(path, *model, *print, export_print_config);
if (!res)
exporter.log_errors();
diff --git a/xs/src/libslic3r/Format/3mf.hpp b/xs/src/libslic3r/Format/3mf.hpp
index 9b48c860b..85bc812e3 100644
--- a/xs/src/libslic3r/Format/3mf.hpp
+++ b/xs/src/libslic3r/Format/3mf.hpp
@@ -12,7 +12,7 @@ namespace Slic3r {
// Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
- extern bool store_3mf(const char* path, Model* model, Print* print);
+ extern bool store_3mf(const char* path, Model* model, Print* print, bool export_print_config);
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/Format/AMF.cpp b/xs/src/libslic3r/Format/AMF.cpp
index a52dd532a..81617ad72 100644
--- a/xs/src/libslic3r/Format/AMF.cpp
+++ b/xs/src/libslic3r/Format/AMF.cpp
@@ -8,11 +8,13 @@
#include "../libslic3r.h"
#include "../Model.hpp"
#include "../GCode.hpp"
+#include "../Utils.hpp"
#include "../slic3r/GUI/PresetBundle.hpp"
#include "AMF.hpp"
#include <boost/filesystem/operations.hpp>
#include <boost/algorithm/string.hpp>
+#include <boost/nowide/fstream.hpp>
#include <miniz/miniz_zip.h>
#if 0
@@ -24,6 +26,12 @@
#include <assert.h>
+// VERSION NUMBERS
+// 0 : .amf, .amf.xml and .zip.amf files saved by older slic3r. No version definition in them.
+// 1 : Introduction of amf versioning. No other change in data saved into amf files.
+const unsigned int VERSION_AMF = 1;
+const char* SLIC3RPE_AMF_VERSION = "slic3rpe_amf_version";
+
const char* SLIC3R_CONFIG_TYPE = "slic3rpe_config";
namespace Slic3r
@@ -32,6 +40,7 @@ namespace Slic3r
struct AMFParserContext
{
AMFParserContext(XML_Parser parser, const std::string& archive_filename, PresetBundle* preset_bundle, Model *model) :
+ m_version(0),
m_parser(parser),
m_model(*model),
m_object(nullptr),
@@ -137,6 +146,8 @@ struct AMFParserContext
std::vector<Instance> instances;
};
+ // Version of the amf file
+ unsigned int m_version;
// Current Expat XML parser instance.
XML_Parser m_parser;
// Model to receive objects extracted from an AMF file.
@@ -360,9 +371,9 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX:
assert(m_object);
// Parse the vertex data
- m_object_vertices.emplace_back(atof(m_value[0].c_str()));
- m_object_vertices.emplace_back(atof(m_value[1].c_str()));
- m_object_vertices.emplace_back(atof(m_value[2].c_str()));
+ m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
+ m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
+ m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@@ -391,10 +402,11 @@ void AMFParserContext::endElement(const char * /* name */)
for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++ v)
- memcpy(&facet.vertex[v].x, &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
+ memcpy(facet.vertex[v].data(), &m_object_vertices[m_volume_facets[i ++] * 3], 3 * sizeof(float));
}
stl_get_size(&stl);
m_volume->mesh.repair();
+ m_volume->calculate_convex_hull();
m_volume_facets.clear();
m_volume = nullptr;
break;
@@ -462,6 +474,10 @@ void AMFParserContext::endElement(const char * /* name */)
if (m_volume && m_value[0] == "name")
m_volume->name = std::move(m_value[1]);
}
+ else if (strncmp(m_value[0].c_str(), SLIC3RPE_AMF_VERSION, strlen(SLIC3RPE_AMF_VERSION)) == 0) {
+ m_version = (unsigned int)atoi(m_value[1].c_str());
+ }
+
m_value[0].clear();
m_value[1].clear();
break;
@@ -482,8 +498,8 @@ void AMFParserContext::endDocument()
for (const Instance &instance : object.second.instances)
if (instance.deltax_set && instance.deltay_set) {
ModelInstance *mi = m_model.objects[object.second.idx]->add_instance();
- mi->offset.x = instance.deltax;
- mi->offset.y = instance.deltay;
+ mi->offset(0) = instance.deltax;
+ mi->offset(1) = instance.deltay;
mi->rotation = instance.rz_set ? instance.rz : 0.f;
mi->scaling_factor = instance.scale_set ? instance.scale : 1.f;
}
@@ -543,47 +559,8 @@ bool load_amf_file(const char *path, PresetBundle* bundle, Model *model)
return result;
}
-// Load an AMF archive into a provided model.
-bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
+bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, const char* path, PresetBundle* bundle, Model* model, unsigned int& version)
{
- if ((path == nullptr) || (model == nullptr))
- return false;
-
- mz_zip_archive archive;
- mz_zip_zero_struct(&archive);
-
- mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
- if (res == 0)
- {
- printf("Unable to init zip reader\n");
- return false;
- }
-
- mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
- if (num_entries != 1)
- {
- printf("Found invalid number of entries\n");
- mz_zip_reader_end(&archive);
- return false;
- }
-
- mz_zip_archive_file_stat stat;
- res = mz_zip_reader_file_stat(&archive, 0, &stat);
- if (res == 0)
- {
- printf("Unable to extract entry statistics\n");
- mz_zip_reader_end(&archive);
- return false;
- }
-
- std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf");
- if (internal_amf_filename != stat.m_filename)
- {
- printf("Found invalid internal filename\n");
- mz_zip_reader_end(&archive);
- return false;
- }
-
if (stat.m_uncomp_size == 0)
{
printf("Found invalid size\n");
@@ -611,7 +588,7 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
return false;
}
- res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
+ mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t)stat.m_uncomp_size, 0);
if (res == 0)
{
printf("Error while reading model data to buffer\n");
@@ -628,6 +605,62 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
ctx.endDocument();
+ version = ctx.m_version;
+
+ return true;
+}
+
+// Load an AMF archive into a provided model.
+bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
+{
+ if ((path == nullptr) || (model == nullptr))
+ return false;
+
+ unsigned int version = 0;
+
+ mz_zip_archive archive;
+ mz_zip_zero_struct(&archive);
+
+ mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
+ if (res == 0)
+ {
+ printf("Unable to init zip reader\n");
+ return false;
+ }
+
+ mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
+
+ mz_zip_archive_file_stat stat;
+ // we first loop the entries to read from the archive the .amf file only, in order to extract the version from it
+ for (mz_uint i = 0; i < num_entries; ++i)
+ {
+ if (mz_zip_reader_file_stat(&archive, i, &stat))
+ {
+ if (boost::iends_with(stat.m_filename, ".amf"))
+ {
+ if (!extract_model_from_archive(archive, stat, path, bundle, model, version))
+ {
+ mz_zip_reader_end(&archive);
+ printf("Archive does not contain a valid model");
+ return false;
+ }
+
+ break;
+ }
+ }
+ }
+
+#if 0 // forward compatibility
+ // we then loop again the entries to read other files stored in the archive
+ for (mz_uint i = 0; i < num_entries; ++i)
+ {
+ if (mz_zip_reader_file_stat(&archive, i, &stat))
+ {
+ // add code to extract the file
+ }
+ }
+#endif // forward compatibility
+
mz_zip_reader_end(&archive);
return true;
}
@@ -636,23 +669,39 @@ bool load_amf_archive(const char *path, PresetBundle* bundle, Model *model)
// If bundle is not a null pointer, updates it if the amf file/archive contains config data
bool load_amf(const char *path, PresetBundle* bundle, Model *model)
{
- if (boost::iends_with(path, ".zip.amf"))
- return load_amf_archive(path, bundle, model);
- else if (boost::iends_with(path, ".amf") || boost::iends_with(path, ".amf.xml"))
+ if (boost::iends_with(path, ".amf.xml"))
+ // backward compatibility with older slic3r output
return load_amf_file(path, bundle, model);
+ else if (boost::iends_with(path, ".amf"))
+ {
+ boost::nowide::ifstream file(path, boost::nowide::ifstream::binary);
+ if (!file.good())
+ return false;
+
+ std::string zip_mask(2, '\0');
+ file.read(const_cast<char*>(zip_mask.data()), 2);
+ file.close();
+
+ return (zip_mask == "PK") ? load_amf_archive(path, bundle, model) : load_amf_file(path, bundle, model);
+ }
else
return false;
}
-bool store_amf(const char *path, Model *model, Print* print)
+bool store_amf(const char *path, Model *model, Print* print, bool export_print_config)
{
if ((path == nullptr) || (model == nullptr) || (print == nullptr))
return false;
+ // forces ".zip.amf" extension
+ std::string export_path = path;
+ if (!boost::iends_with(export_path, ".zip.amf"))
+ export_path = boost::filesystem::path(export_path).replace_extension(".zip.amf").string();
+
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
- mz_bool res = mz_zip_writer_init_file(&archive, path, 0);
+ mz_bool res = mz_zip_writer_init_file(&archive, export_path.c_str(), 0);
if (res == 0)
return false;
@@ -660,10 +709,14 @@ bool store_amf(const char *path, Model *model, Print* print)
stream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
stream << "<amf unit=\"millimeter\">\n";
stream << "<metadata type=\"cad\">Slic3r " << SLIC3R_VERSION << "</metadata>\n";
+ stream << "<metadata type=\"" << SLIC3RPE_AMF_VERSION << "\">" << VERSION_AMF << "</metadata>\n";
- std::string config = "\n";
- GCode::append_full_config(*print, config);
- stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << config << "</metadata>\n";
+ if (export_print_config)
+ {
+ std::string config = "\n";
+ GCode::append_full_config(*print, config);
+ stream << "<metadata type=\"" << SLIC3R_CONFIG_TYPE << "\">" << xml_escape(config) << "</metadata>\n";
+ }
for (const auto &material : model->materials) {
if (material.first.empty())
@@ -683,7 +736,7 @@ bool store_amf(const char *path, Model *model, Print* print)
for (const std::string &key : object->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << object->config.serialize(key) << "</metadata>\n";
if (!object->name.empty())
- stream << " <metadata type=\"name\">" << object->name << "</metadata>\n";
+ stream << " <metadata type=\"name\">" << xml_escape(object->name) << "</metadata>\n";
std::vector<double> layer_height_profile = object->layer_height_profile_valid ? object->layer_height_profile : std::vector<double>();
if (layer_height_profile.size() >= 4 && (layer_height_profile.size() % 2) == 0) {
// Store the layer height profile as a single semicolon separated list.
@@ -708,9 +761,9 @@ bool store_amf(const char *path, Model *model, Print* print)
for (size_t i = 0; i < stl.stats.shared_vertices; ++ i) {
stream << " <vertex>\n";
stream << " <coordinates>\n";
- stream << " <x>" << stl.v_shared[i].x << "</x>\n";
- stream << " <y>" << stl.v_shared[i].y << "</y>\n";
- stream << " <z>" << stl.v_shared[i].z << "</z>\n";
+ stream << " <x>" << stl.v_shared[i](0) << "</x>\n";
+ stream << " <y>" << stl.v_shared[i](1) << "</y>\n";
+ stream << " <z>" << stl.v_shared[i](2) << "</z>\n";
stream << " </coordinates>\n";
stream << " </vertex>\n";
}
@@ -727,7 +780,7 @@ bool store_amf(const char *path, Model *model, Print* print)
for (const std::string &key : volume->config.keys())
stream << " <metadata type=\"slic3r." << key << "\">" << volume->config.serialize(key) << "</metadata>\n";
if (!volume->name.empty())
- stream << " <metadata type=\"name\">" << volume->name << "</metadata>\n";
+ stream << " <metadata type=\"name\">" << xml_escape(volume->name) << "</metadata>\n";
if (volume->modifier)
stream << " <metadata type=\"slic3r.modifier\">1</metadata>\n";
for (int i = 0; i < volume->mesh.stl.stats.number_of_facets; ++i) {
@@ -751,8 +804,8 @@ bool store_amf(const char *path, Model *model, Print* print)
" <scale>%lf</scale>\n"
" </instance>\n",
object_id,
- instance->offset.x,
- instance->offset.y,
+ instance->offset(0),
+ instance->offset(1),
instance->rotation,
instance->scaling_factor);
//FIXME missing instance->scaling_factor
@@ -767,20 +820,20 @@ bool store_amf(const char *path, Model *model, Print* print)
}
stream << "</amf>\n";
- std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(path).filename().string(), ".zip.amf", ".amf");
+ std::string internal_amf_filename = boost::ireplace_last_copy(boost::filesystem::path(export_path).filename().string(), ".zip.amf", ".amf");
std::string out = stream.str();
if (!mz_zip_writer_add_mem(&archive, internal_amf_filename.c_str(), (const void*)out.data(), out.length(), MZ_DEFAULT_COMPRESSION))
{
mz_zip_writer_end(&archive);
- boost::filesystem::remove(path);
+ boost::filesystem::remove(export_path);
return false;
}
if (!mz_zip_writer_finalize_archive(&archive))
{
mz_zip_writer_end(&archive);
- boost::filesystem::remove(path);
+ boost::filesystem::remove(export_path);
return false;
}
diff --git a/xs/src/libslic3r/Format/AMF.hpp b/xs/src/libslic3r/Format/AMF.hpp
index 027ebdab3..4779e9a51 100644
--- a/xs/src/libslic3r/Format/AMF.hpp
+++ b/xs/src/libslic3r/Format/AMF.hpp
@@ -12,7 +12,7 @@ extern bool load_amf(const char *path, PresetBundle* bundle, Model *model);
// Save the given model and the config data contained in the given Print into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
-extern bool store_amf(const char *path, Model *model, Print* print);
+extern bool store_amf(const char *path, Model *model, Print* print, bool export_print_config);
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/Format/OBJ.cpp b/xs/src/libslic3r/Format/OBJ.cpp
index ea6b5604c..ee5756083 100644
--- a/xs/src/libslic3r/Format/OBJ.cpp
+++ b/xs/src/libslic3r/Format/OBJ.cpp
@@ -57,14 +57,14 @@ bool load_obj(const char *path, Model *model, const char *object_name_in)
continue;
stl_facet &facet = stl.facet_start[i_face ++];
size_t num_normals = 0;
- stl_normal normal = { 0.f };
+ stl_normal normal(stl_normal::Zero());
for (unsigned int v = 0; v < 3; ++ v) {
const ObjParser::ObjVertex &vertex = data.vertices[i++];
- memcpy(&facet.vertex[v].x, &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
+ memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
if (vertex.normalIdx != -1) {
- normal.x += data.normals[vertex.normalIdx*3];
- normal.y += data.normals[vertex.normalIdx*3+1];
- normal.z += data.normals[vertex.normalIdx*3+2];
+ normal(0) += data.normals[vertex.normalIdx*3];
+ normal(1) += data.normals[vertex.normalIdx*3+1];
+ normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals;
}
}
@@ -74,33 +74,27 @@ bool load_obj(const char *path, Model *model, const char *object_name_in)
facet2.vertex[0] = facet.vertex[0];
facet2.vertex[1] = facet.vertex[2];
const ObjParser::ObjVertex &vertex = data.vertices[i++];
- memcpy(&facet2.vertex[2].x, &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
+ memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
if (vertex.normalIdx != -1) {
- normal.x += data.normals[vertex.normalIdx*3];
- normal.y += data.normals[vertex.normalIdx*3+1];
- normal.z += data.normals[vertex.normalIdx*3+2];
+ normal(0) += data.normals[vertex.normalIdx*3];
+ normal(1) += data.normals[vertex.normalIdx*3+1];
+ normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals;
}
if (num_normals == 4) {
// Normalize an average normal of a quad.
- float len = sqrt(facet.normal.x*facet.normal.x + facet.normal.y*facet.normal.y + facet.normal.z*facet.normal.z);
+ float len = facet.normal.norm();
if (len > EPSILON) {
- normal.x /= len;
- normal.y /= len;
- normal.z /= len;
+ normal /= len;
facet.normal = normal;
facet2.normal = normal;
}
}
} else if (num_normals == 3) {
// Normalize an average normal of a triangle.
- float len = sqrt(facet.normal.x*facet.normal.x + facet.normal.y*facet.normal.y + facet.normal.z*facet.normal.z);
- if (len > EPSILON) {
- normal.x /= len;
- normal.y /= len;
- normal.z /= len;
- facet.normal = normal;
- }
+ float len = facet.normal.norm();
+ if (len > EPSILON)
+ facet.normal = normal / len;
}
}
stl_get_size(&stl);
diff --git a/xs/src/libslic3r/Format/PRUS.cpp b/xs/src/libslic3r/Format/PRUS.cpp
index 1809eaead..3cf3fc075 100644
--- a/xs/src/libslic3r/Format/PRUS.cpp
+++ b/xs/src/libslic3r/Format/PRUS.cpp
@@ -166,7 +166,7 @@ bool load_prus(const char *path, Model *model)
float trafo[3][4] = { 0 };
double instance_rotation = 0.;
double instance_scaling_factor = 1.f;
- Pointf instance_offset(0., 0.);
+ Vec2d instance_offset(0., 0.);
bool trafo_set = false;
unsigned int group_id = (unsigned int)-1;
unsigned int extruder_id = (unsigned int)-1;
@@ -207,8 +207,8 @@ bool load_prus(const char *path, Model *model)
for (size_t c = 0; c < 3; ++ c)
trafo[r][c] += mat_trafo(r, c);
}
- instance_offset.x = position[0] - zero[0];
- instance_offset.y = position[1] - zero[1];
+ instance_offset(0) = position[0] - zero[0];
+ instance_offset(1) = position[1] - zero[1];
trafo[2][3] = position[2] / instance_scaling_factor;
trafo_set = true;
}
@@ -260,8 +260,8 @@ bool load_prus(const char *path, Model *model)
mesh.repair();
// Transform the model.
stl_transform(&stl, &trafo[0][0]);
- if (std::abs(stl.stats.min.z) < EPSILON)
- stl.stats.min.z = 0.;
+ if (std::abs(stl.stats.min(2)) < EPSILON)
+ stl.stats.min(2) = 0.;
// Add a mesh to a model.
if (mesh.facets_count() > 0)
mesh_valid = true;
@@ -309,11 +309,11 @@ bool load_prus(const char *path, Model *model)
assert(res_normal == 3);
int res_outer_loop = line_reader.next_line_scanf(" outer loop");
assert(res_outer_loop == 0);
- int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0].x, &facet.vertex[0].y, &facet.vertex[0].z);
+ int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
assert(res_vertex1 == 3);
- int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1].x, &facet.vertex[1].y, &facet.vertex[1].z);
+ int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
assert(res_vertex2 == 3);
- int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2].x, &facet.vertex[2].y, &facet.vertex[2].z);
+ int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
assert(res_vertex3 == 3);
int res_endloop = line_reader.next_line_scanf(" endloop");
assert(res_endloop == 0);
@@ -324,9 +324,9 @@ bool load_prus(const char *path, Model *model)
break;
}
// 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) {
+ if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
+ sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
+ sscanf(normal_buf[2], "%f", &facet.normal(2)) != 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));
diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp
index e55404845..0ade0de61 100644
--- a/xs/src/libslic3r/GCode.cpp
+++ b/xs/src/libslic3r/GCode.cpp
@@ -49,11 +49,11 @@ Polyline AvoidCrossingPerimeters::travel_to(const GCode &gcodegen, const Point &
// If use_external, then perform the path planning in the world coordinate system (correcting for the gcodegen offset).
// Otherwise perform the path planning in the coordinate system of the active object.
bool use_external = this->use_external_mp || this->use_external_mp_once;
- Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin().x, gcodegen.origin().y) : Point(0, 0);
+ Point scaled_origin = use_external ? Point::new_scale(gcodegen.origin()(0), gcodegen.origin()(1)) : Point(0, 0);
Polyline result = (use_external ? m_external_mp.get() : m_layer_mp.get())->
shortest_path(gcodegen.last_pos() + scaled_origin, point + scaled_origin);
if (use_external)
- result.translate(scaled_origin.negative());
+ result.translate(- scaled_origin);
return result;
}
@@ -64,8 +64,8 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
// move to the nearest standby point
if (!this->standby_points.empty()) {
// get current position in print coordinates
- Pointf3 writer_pos = gcodegen.writer().get_position();
- Point pos = Point::new_scale(writer_pos.x, writer_pos.y);
+ Vec3d writer_pos = gcodegen.writer().get_position();
+ Point pos = Point::new_scale(writer_pos(0), writer_pos(1));
// find standby point
Point standby_point;
@@ -74,7 +74,7 @@ std::string OozePrevention::pre_toolchange(GCode &gcodegen)
/* We don't call gcodegen.travel_to() because we don't need retraction (it was already
triggered by the caller) nor avoid_crossing_perimeters and also because the coordinates
of the destination point must not be transformed by origin nor current extruder offset. */
- gcode += gcodegen.writer().travel_to_xy(Pointf::new_unscale(standby_point),
+ gcode += gcodegen.writer().travel_to_xy(unscale(standby_point),
"move to standby position");
}
@@ -160,13 +160,25 @@ Wipe::wipe(GCode &gcodegen, bool toolchange)
static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt)
{
- return Point(scale_(wipe_tower_pt.x - gcodegen.origin().x), scale_(wipe_tower_pt.y - gcodegen.origin().y));
+ return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1)));
}
std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const
{
std::string gcode;
+ // Toolchangeresult.gcode assumes the wipe tower corner is at the origin
+ // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position
+ float alpha = m_wipe_tower_rotation/180.f * M_PI;
+ WipeTower::xy start_pos = tcr.start_pos;
+ WipeTower::xy end_pos = tcr.end_pos;
+ start_pos.rotate(alpha);
+ start_pos.translate(m_wipe_tower_pos);
+ end_pos.rotate(alpha);
+ end_pos.translate(m_wipe_tower_pos);
+ std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha);
+
+
// Disable linear advance for the wipe tower operations.
gcode += "M900 K0\n";
// Move over the wipe tower.
@@ -174,14 +186,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
gcode += gcodegen.retract(true);
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true;
gcode += gcodegen.travel_to(
- wipe_tower_point_to_object_point(gcodegen, tcr.start_pos),
+ wipe_tower_point_to_object_point(gcodegen, start_pos),
erMixed,
"Travel to a Wipe Tower");
gcode += gcodegen.unretract();
// Let the tool change be executed by the wipe tower class.
// Inform the G-code writer about the changes done behind its back.
- gcode += tcr.gcode;
+ gcode += tcr_rotated_gcode;
// Let the m_writer know the current extruder_id, but ignore the generated G-code.
if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))
gcodegen.writer().toolchange(new_extruder_id);
@@ -195,18 +207,18 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
check_add_eol(gcode);
}
// A phony move to the end position at the wipe tower.
- gcodegen.writer().travel_to_xy(Pointf(tcr.end_pos.x, tcr.end_pos.y));
- gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
+ gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y));
+ gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
if (new_extruder_id >= 0) {
// Start the wipe at the current position.
- gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, tcr.end_pos));
+ gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos));
// Wipe end point: Wipe direction away from the closer tower edge to the further tower edge.
gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen,
- WipeTower::xy((std::abs(m_left - tcr.end_pos.x) < std::abs(m_right - tcr.end_pos.x)) ? m_right : m_left,
- tcr.end_pos.y)));
+ WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left,
+ end_pos.y)));
}
// Let the planner know we are traveling between objects.
@@ -214,6 +226,57 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T
return gcode;
}
+// This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode
+// Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate)
+std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const
+{
+ std::istringstream gcode_str(gcode_original);
+ std::string gcode_out;
+ std::string line;
+ WipeTower::xy pos = start_pos;
+ WipeTower::xy transformed_pos;
+ WipeTower::xy old_pos(-1000.1f, -1000.1f);
+
+ while (gcode_str) {
+ std::getline(gcode_str, line); // we read the gcode line by line
+ if (line.find("G1 ") == 0) {
+ std::ostringstream line_out;
+ std::istringstream line_str(line);
+ line_str >> std::noskipws; // don't skip whitespace
+ char ch = 0;
+ while (line_str >> ch) {
+ if (ch == 'X')
+ line_str >> pos.x;
+ else
+ if (ch == 'Y')
+ line_str >> pos.y;
+ else
+ line_out << ch;
+ }
+
+ transformed_pos = pos;
+ transformed_pos.rotate(angle);
+ transformed_pos.translate(translation);
+
+ if (transformed_pos != old_pos) {
+ line = line_out.str();
+ char buf[2048] = "G1";
+ if (transformed_pos.x != old_pos.x)
+ sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x);
+ if (transformed_pos.y != old_pos.y)
+ sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y);
+
+ line.replace(line.find("G1 "), 3, buf);
+ old_pos = transformed_pos;
+ }
+ }
+ gcode_out += line + "\n";
+ }
+ return gcode_out;
+}
+
+
+
std::string WipeTowerIntegration::prime(GCode &gcodegen)
{
assert(m_layer_idx == 0);
@@ -230,7 +293,7 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen)
gcodegen.writer().toolchange(current_extruder_id);
gcodegen.placeholder_parser().set("current_extruder", current_extruder_id);
// A phony move to the end position at the wipe tower.
- gcodegen.writer().travel_to_xy(Pointf(m_priming.end_pos.x, m_priming.end_pos.y));
+ gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y));
gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos));
// Prepare a future wipe.
gcodegen.m_wipe.path.points.clear();
@@ -262,7 +325,7 @@ std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id,
std::string WipeTowerIntegration::finalize(GCode &gcodegen)
{
std::string gcode;
- if (std::abs(gcodegen.writer().get_position().z - m_final_purge.print_z) > EPSILON)
+ if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON)
gcode += gcodegen.change_layer(m_final_purge.print_z);
gcode += append_tcr(gcodegen, m_final_purge, -1);
return gcode;
@@ -309,10 +372,12 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
size_t object_idx;
size_t layer_idx;
};
- std::vector<std::vector<LayerToPrint>> per_object(print.objects().size(), std::vector<LayerToPrint>());
+
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
+ std::vector<std::vector<LayerToPrint>> per_object(printable_objects.size(), std::vector<LayerToPrint>());
std::vector<OrderingItem> ordering;
- for (size_t i = 0; i < print.objects().size(); ++ i) {
- per_object[i] = collect_layers_to_print(*print.objects()[i]);
+ for (size_t i = 0; i < printable_objects.size(); ++i) {
+ per_object[i] = collect_layers_to_print(*printable_objects[i]);
OrderingItem ordering_item;
ordering_item.object_idx = i;
ordering.reserve(ordering.size() + per_object[i].size());
@@ -337,8 +402,8 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
std::pair<coordf_t, std::vector<LayerToPrint>> merged;
// Assign an average print_z to the set of layers with nearly equal print_z.
merged.first = 0.5 * (ordering[i].print_z + ordering[j-1].print_z);
- merged.second.assign(print.objects().size(), LayerToPrint());
- for (; i < j; ++ i) {
+ merged.second.assign(printable_objects.size(), LayerToPrint());
+ for (; i < j; ++i) {
const OrderingItem &oi = ordering[i];
assert(merged.second[oi.object_idx].layer() == nullptr);
merged.second[oi.object_idx] = std::move(per_object[oi.object_idx][oi.layer_idx]);
@@ -382,6 +447,13 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
throw;
}
fclose(file);
+
+ if (print->config().remaining_times.value) {
+ m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
+ if (m_silent_time_estimator_enabled)
+ m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f);
+ }
+
if (! m_placeholder_parser_failed_templates.empty()) {
// G-code export proceeded, but some of the PlaceholderParser substitutions failed.
std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
@@ -413,9 +485,69 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
print.set_started(psGCodeExport);
- // resets time estimator
- m_time_estimator.reset();
- m_time_estimator.set_dialect(print.config().gcode_flavor);
+ // resets time estimators
+ m_normal_time_estimator.reset();
+ m_normal_time_estimator.set_dialect(print.config().gcode_flavor);
+ m_silent_time_estimator_enabled = (print.config().gcode_flavor == gcfMarlin) && print.config().silent_mode;
+
+ // Until we have a UI support for the other firmwares than the Marlin, use the hardcoded default values
+ // and let the user to enter the G-code limits into the start G-code.
+ // If the following block is enabled for other firmwares than the Marlin, then the function
+ // this->print_machine_envelope(file, print);
+ // shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
+ if (print.config().gcode_flavor.value == gcfMarlin) {
+ m_normal_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[0]);
+ m_normal_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[0]);
+ m_normal_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[0]);
+ m_normal_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[0]);
+ m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[0]);
+ m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[0]);
+ m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[0]);
+
+ if (m_silent_time_estimator_enabled)
+ {
+ m_silent_time_estimator.reset();
+ m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
+ m_silent_time_estimator.set_max_acceleration(print.config().machine_max_acceleration_extruding.values[1]);
+ m_silent_time_estimator.set_retract_acceleration(print.config().machine_max_acceleration_retracting.values[1]);
+ m_silent_time_estimator.set_minimum_feedrate(print.config().machine_min_extruding_rate.values[1]);
+ m_silent_time_estimator.set_minimum_travel_feedrate(print.config().machine_min_travel_rate.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, print.config().machine_max_acceleration_x.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, print.config().machine_max_acceleration_y.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, print.config().machine_max_acceleration_z.values[1]);
+ m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, print.config().machine_max_acceleration_e.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, print.config().machine_max_feedrate_x.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, print.config().machine_max_feedrate_y.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, print.config().machine_max_feedrate_z.values[1]);
+ m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, print.config().machine_max_feedrate_e.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, print.config().machine_max_jerk_x.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, print.config().machine_max_jerk_y.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, print.config().machine_max_jerk_z.values[1]);
+ m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, print.config().machine_max_jerk_e.values[1]);
+ if (print.config().single_extruder_multi_material) {
+ // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
+ // are considered to be active for the single extruder multi-material printers only.
+ m_silent_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
+ m_silent_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
+ }
+ }
+ }
+ // Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
+ if (print.config().single_extruder_multi_material) {
+ // As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
+ // are considered to be active for the single extruder multi-material printers only.
+ m_normal_time_estimator.set_filament_load_times(print.config().filament_load_time.values);
+ m_normal_time_estimator.set_filament_unload_times(print.config().filament_unload_time.values);
+ }
// resets analyzer
m_analyzer.reset();
@@ -429,9 +561,10 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// How many times will be change_layer() called?
// change_layer() in turn increments the progress bar status.
m_layer_count = 0;
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
if (print.config().complete_objects.value) {
// Add each of the object's layers separately.
- for (auto object : print.objects()) {
+ for (auto object : printable_objects) {
std::vector<coordf_t> zs;
zs.reserve(object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
@@ -444,7 +577,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
} else {
// Print all objects with the same print_z together.
std::vector<coordf_t> zs;
- for (auto object : print.objects()) {
+ for (auto object : printable_objects) {
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.push_back(layer->print_z);
@@ -464,8 +597,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
{
// get the minimum cross-section used in the print
std::vector<double> mm3_per_mm;
- for (auto object : print.objects()) {
- for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
+ for (auto object : printable_objects) {
+ for (size_t region_id = 0; region_id < print.regions().size(); ++region_id) {
auto region = print.regions()[region_id];
for (auto layer : object->layers()) {
auto layerm = layer->regions()[region_id];
@@ -529,7 +662,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
print.throw_if_canceled();
// Write some terse information on the slicing parameters.
- const PrintObject *first_object = print.objects().front();
+ const PrintObject *first_object = printable_objects.front();
const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
for (size_t region_id = 0; region_id < print.regions().size(); ++ region_id) {
@@ -547,6 +680,14 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
print.throw_if_canceled();
+ // adds tags for time estimators
+ if (print.config().remaining_times.value)
+ {
+ _writeln(file, GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag);
+ if (m_silent_time_estimator_enabled)
+ _writeln(file, GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag);
+ }
+
// Prepare the helper object for replacing placeholders in custom G-code and output filename.
m_placeholder_parser = print.placeholder_parser();
m_placeholder_parser.update_timestamp();
@@ -559,20 +700,24 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
size_t initial_print_object_id = 0;
bool has_wipe_tower = false;
if (print.config().complete_objects.value) {
- // Find the 1st printing object, find its tool ordering and the initial extruder ID.
- for (; initial_print_object_id < print.objects().size(); ++initial_print_object_id) {
- tool_ordering = ToolOrdering(*print.objects()[initial_print_object_id], initial_extruder_id);
- if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
- break;
- }
- } else {
+ // Find the 1st printing object, find its tool ordering and the initial extruder ID.
+ for (; initial_print_object_id < printable_objects.size(); ++initial_print_object_id) {
+ tool_ordering = ToolOrdering(*printable_objects[initial_print_object_id], initial_extruder_id);
+ if ((initial_extruder_id = tool_ordering.first_extruder()) != (unsigned int)-1)
+ break;
+ }
+ } else {
// Find tool ordering for all the objects at once, and the initial extruder ID.
// If the tool ordering has been pre-calculated by Print class for wipe tower already, reuse it.
tool_ordering = print.wipe_tower_data().tool_ordering.empty() ?
ToolOrdering(print, initial_extruder_id) :
print.wipe_tower_data().tool_ordering;
- initial_extruder_id = tool_ordering.first_extruder();
has_wipe_tower = print.has_wipe_tower() && tool_ordering.has_wipe_tower();
+ initial_extruder_id = (has_wipe_tower && ! print.config().single_extruder_multi_material_priming) ?
+ // The priming towers will be skipped.
+ tool_ordering.all_extruders().back() :
+ // Don't skip the priming towers.
+ tool_ordering.first_extruder();
}
if (initial_extruder_id == (unsigned int)-1) {
// Nothing to print!
@@ -586,6 +731,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_cooling_buffer->set_current_extruder(initial_extruder_id);
+ // Emit machine envelope limits for the Marlin firmware.
+ this->print_machine_envelope(file, print);
+
// Disable fan.
if (! print.config().cooling.get_at(initial_extruder_id) || print.config().disable_fan_first_layers.get_at(initial_extruder_id))
_write(file, m_writer.set_fan(0, true));
@@ -598,8 +746,8 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
m_placeholder_parser.set("current_object_idx", 0);
// For the start / end G-code to do the priming and final filament pull in case there is no wipe tower provided.
m_placeholder_parser.set("has_wipe_tower", has_wipe_tower);
+ m_placeholder_parser.set("has_single_extruder_multi_material_priming", has_wipe_tower && print.config().single_extruder_multi_material_priming);
std::string start_gcode = this->placeholder_parser_process("start_gcode", print.config().start_gcode.value, initial_extruder_id);
-
// Set bed temperature if the start G-code does not contain any bed temp control G-codes.
this->_print_first_layer_bed_temperature(file, print, start_gcode, initial_extruder_id, true);
// Set extruder(s) temperature before and after start G-code.
@@ -638,12 +786,12 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Collect outer contours of all objects over all layers.
// Discard objects only containing thin walls (offset would fail on an empty polygon).
Polygons islands;
- for (const PrintObject *object : print.objects())
+ for (const PrintObject *object : printable_objects)
for (const Layer *layer : object->layers())
for (const ExPolygon &expoly : layer->slices.expolygons)
- for (const Point &copy : object->_shifted_copies) {
+ for (const Point &copy : object->copies()) {
islands.emplace_back(expoly.contour);
- islands.back().translate(copy);
+ islands.back().translate(- copy);
}
//FIXME Mege the islands in parallel.
m_avoid_crossing_perimeters.init_external_mp(union_ex(islands));
@@ -660,9 +808,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
Polygon outer_skirt = Slic3r::Geometry::convex_hull(skirt_points);
Polygons skirts;
for (unsigned int extruder_id : print.extruders()) {
- const Pointf &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
+ const Vec2d &extruder_offset = print.config().extruder_offset.get_at(extruder_id);
Polygon s(outer_skirt);
- s.translate(-scale_(extruder_offset.x), -scale_(extruder_offset.y));
+ s.translate(Point::new_scale(- extruder_offset(0), - extruder_offset(1)));
skirts.emplace_back(std::move(s));
}
m_ooze_prevention.enable = true;
@@ -681,21 +829,24 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
print.throw_if_canceled();
}
- // Set initial extruder only after custom start G-code.
- _write(file, this->set_extruder(initial_extruder_id));
+ if (! (has_wipe_tower && print.config().single_extruder_multi_material_priming)) {
+ // Set initial extruder only after custom start G-code.
+ // Ugly hack: Do not set the initial extruder if the extruder is primed using the MMU priming towers at the edge of the print bed.
+ _write(file, this->set_extruder(initial_extruder_id));
+ }
// Do all objects for each layer.
if (print.config().complete_objects.value) {
// Print objects from the smallest to the tallest to avoid collisions
// when moving onto next object starting point.
- std::vector<PrintObject*> objects(print.objects());
- std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size.z < po2->size.z; });
+ std::vector<PrintObject*> objects(printable_objects);
+ std::sort(objects.begin(), objects.end(), [](const PrintObject* po1, const PrintObject* po2) { return po1->size(2) < po2->size(2); });
size_t finished_objects = 0;
for (size_t object_id = initial_print_object_id; object_id < objects.size(); ++ object_id) {
const PrintObject &object = *objects[object_id];
- for (const Point &copy : object._shifted_copies) {
+ for (const Point &copy : object.copies()) {
// Get optimal tool ordering to minimize tool switches of a multi-exruder print.
- if (object_id != initial_print_object_id || &copy != object._shifted_copies.data()) {
+ if (object_id != initial_print_object_id || &copy != object.copies().data()) {
// Don't initialize for the first object and first copy.
tool_ordering = ToolOrdering(object, final_extruder_id);
unsigned int new_extruder_id = tool_ordering.first_extruder();
@@ -707,7 +858,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
assert(final_extruder_id != (unsigned int)-1);
}
print.throw_if_canceled();
- this->set_origin(unscale(copy.x), unscale(copy.y));
+ this->set_origin(unscale(copy));
if (finished_objects > 0) {
// Move to the origin position for the copy we're going to print.
// This happens before Z goes down to layer 0 again, so that no collision happens hopefully.
@@ -736,7 +887,7 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
for (const LayerToPrint &ltp : layers_to_print) {
std::vector<LayerToPrint> lrs;
lrs.emplace_back(std::move(ltp));
- this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object._shifted_copies.data());
+ this->process_layer(file, print, lrs, tool_ordering.tools_for_layer(ltp.print_z()), &copy - object.copies().data());
print.throw_if_canceled();
}
if (m_pressure_equalizer)
@@ -751,8 +902,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
// Order objects using a nearest neighbor search.
std::vector<size_t> object_indices;
Points object_reference_points;
- for (PrintObject *object : print.objects())
- object_reference_points.push_back(object->_shifted_copies.front());
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
+ for (PrintObject *object : printable_objects)
+ object_reference_points.push_back(object->copies().front());
Slic3r::Geometry::chained_path(object_reference_points, object_indices);
// Sort layers by Z.
// All extrusion moves with the same top layer height are extruded uninterrupted.
@@ -761,33 +913,35 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
if (has_wipe_tower && ! layers_to_print.empty()) {
m_wipe_tower.reset(new WipeTowerIntegration(print.config(), *print.wipe_tower_data().priming.get(), print.wipe_tower_data().tool_changes, *print.wipe_tower_data().final_purge.get()));
_write(file, m_writer.travel_to_z(first_layer_height + m_config.z_offset.value, "Move to the first layer height"));
- _write(file, m_wipe_tower->prime(*this));
- // Verify, whether the print overaps the priming extrusions.
- BoundingBoxf bbox_print(get_print_extrusions_extents(print));
- coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
- for (const PrintObject *print_object : print.objects())
- bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
- bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
- BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
- bbox_prime.offset(0.5f);
- // Beep for 500ms, tone 800Hz. Yet better, play some Morse.
- _write(file, this->retract());
- _write(file, "M300 S800 P500\n");
- if (bbox_prime.overlap(bbox_print)) {
- // Wait for the user to remove the priming extrusions, otherwise they would
- // get covered by the print.
- _write(file, "M1 Remove priming towers and click button.\n");
- }
- else {
- // Just wait for a bit to let the user check, that the priming succeeded.
- //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
- _write(file, "M1 S10\n");
+ if (print.config().single_extruder_multi_material_priming) {
+ _write(file, m_wipe_tower->prime(*this));
+ // Verify, whether the print overaps the priming extrusions.
+ BoundingBoxf bbox_print(get_print_extrusions_extents(print));
+ coordf_t twolayers_printz = ((layers_to_print.size() == 1) ? layers_to_print.front() : layers_to_print[1]).first + EPSILON;
+ for (const PrintObject *print_object : printable_objects)
+ bbox_print.merge(get_print_object_extrusions_extents(*print_object, twolayers_printz));
+ bbox_print.merge(get_wipe_tower_extrusions_extents(print, twolayers_printz));
+ BoundingBoxf bbox_prime(get_wipe_tower_priming_extrusions_extents(print));
+ bbox_prime.offset(0.5f);
+ // Beep for 500ms, tone 800Hz. Yet better, play some Morse.
+ _write(file, this->retract());
+ _write(file, "M300 S800 P500\n");
+ if (bbox_prime.overlap(bbox_print)) {
+ // Wait for the user to remove the priming extrusions, otherwise they would
+ // get covered by the print.
+ _write(file, "M1 Remove priming towers and click button.\n");
+ }
+ else {
+ // Just wait for a bit to let the user check, that the priming succeeded.
+ //TODO Add a message explaining what the printer is waiting for. This needs a firmware fix.
+ _write(file, "M1 S10\n");
+ }
}
print.throw_if_canceled();
}
// Extrude the layers.
for (auto &layer : layers_to_print) {
- const ToolOrdering::LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
+ const LayerTools &layer_tools = tool_ordering.tools_for_layer(layer.first);
if (m_wipe_tower && layer_tools.has_wipe_tower)
m_wipe_tower->next_layer();
this->process_layer(file, print, layer.second, layer_tools, size_t(-1));
@@ -813,30 +967,38 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
}
// Process filament-specific gcode in extruder order.
- if (print.config().single_extruder_multi_material) {
- // Process the end_filament_gcode for the active filament only.
- _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id()));
- } else {
- for (const std::string &end_gcode : print.config().end_filament_gcode.values)
- _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front())));
+ {
+ DynamicConfig config;
+ config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index));
+ config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value));
+ if (print.config().single_extruder_multi_material) {
+ // Process the end_filament_gcode for the active filament only.
+ _writeln(file, this->placeholder_parser_process("end_filament_gcode", print.config().end_filament_gcode.get_at(m_writer.extruder()->id()), m_writer.extruder()->id(), &config));
+ } else {
+ for (const std::string &end_gcode : print.config().end_filament_gcode.values)
+ _writeln(file, this->placeholder_parser_process("end_filament_gcode", end_gcode, (unsigned int)(&end_gcode - &print.config().end_filament_gcode.values.front()), &config));
+ }
+ _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id(), &config));
}
- _writeln(file, this->placeholder_parser_process("end_gcode", print.config().end_gcode, m_writer.extruder()->id()));
_write(file, m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100%
_write(file, m_writer.postamble());
print.throw_if_canceled();
// calculates estimated printing time
- m_time_estimator.calculate_time();
+ m_normal_time_estimator.calculate_time(false);
+ if (m_silent_time_estimator_enabled)
+ m_silent_time_estimator.calculate_time(false);
// Get filament stats.
print.m_print_statistics.clear();
- print.m_print_statistics.estimated_print_time = m_time_estimator.get_time_hms();
+ print.m_print_statistics.estimated_normal_print_time = m_normal_time_estimator.get_time_dhms();
+ print.m_print_statistics.estimated_silent_print_time = m_silent_time_estimator_enabled ? m_silent_time_estimator.get_time_dhms() : "N/A";
for (const Extruder &extruder : m_writer.extruders()) {
double used_filament = extruder.used_filament();
double extruded_volume = extruder.extruded_volume();
double filament_weight = extruded_volume * extruder.filament_density() * 0.001;
double filament_cost = filament_weight * extruder.filament_cost() * 0.001;
- print.m_print_statistics.filament_stats.insert(std::pair<size_t,float>(extruder.id(), used_filament));
+ print.m_print_statistics.filament_stats.insert(std::pair<size_t, float>(extruder.id(), (float)used_filament));
_write_format(file, "; filament used = %.1lfmm (%.1lfcm3)\n", used_filament, extruded_volume * 0.001);
if (filament_weight > 0.) {
print.m_print_statistics.total_weight = print.m_print_statistics.total_weight + filament_weight;
@@ -850,7 +1012,9 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data)
print.m_print_statistics.total_extruded_volume = print.m_print_statistics.total_extruded_volume + extruded_volume;
}
_write_format(file, "; total filament cost = %.1lf\n", print.m_print_statistics.total_cost);
- _write_format(file, "; estimated printing time = %s\n", m_time_estimator.get_time_hms().c_str());
+ _write_format(file, "; estimated printing time (normal mode) = %s\n", m_normal_time_estimator.get_time_dhms().c_str());
+ if (m_silent_time_estimator_enabled)
+ _write_format(file, "; estimated printing time (silent mode) = %s\n", m_silent_time_estimator.get_time_dhms().c_str());
// Append full config.
_write(file, "\n");
@@ -938,6 +1102,36 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
return temp_set_by_gcode;
}
+// Print the machine envelope G-code for the Marlin firmware based on the "machine_max_xxx" parameters.
+// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
+void GCode::print_machine_envelope(FILE *file, Print &print)
+{
+ if (print.config().gcode_flavor.value == gcfMarlin) {
+ fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
+ int(print.config().machine_max_acceleration_x.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_y.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_z.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_e.values.front() + 0.5));
+ fprintf(file, "M203 X%d Y%d Z%d E%d ; sets maximum feedrates, mm/sec\n",
+ int(print.config().machine_max_feedrate_x.values.front() + 0.5),
+ int(print.config().machine_max_feedrate_y.values.front() + 0.5),
+ int(print.config().machine_max_feedrate_z.values.front() + 0.5),
+ int(print.config().machine_max_feedrate_e.values.front() + 0.5));
+ fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
+ int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
+ int(print.config().machine_max_acceleration_extruding.values.front() + 0.5));
+ fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
+ print.config().machine_max_jerk_x.values.front(),
+ print.config().machine_max_jerk_y.values.front(),
+ print.config().machine_max_jerk_z.values.front(),
+ print.config().machine_max_jerk_e.values.front());
+ fprintf(file, "M205 S%d T%d ; sets the minimum extruding and travel feed rate, mm/sec\n",
+ int(print.config().machine_min_extruding_rate.values.front() + 0.5),
+ int(print.config().machine_min_travel_rate.values.front() + 0.5));
+ }
+}
+
// Write 1st layer bed temperatures into the G-code.
// Only do that if the start G-code does not already contain any M-code controlling an extruder temperature.
// M140 - Set Extruder Temperature
@@ -1028,7 +1222,7 @@ void GCode::process_layer(
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
- const ToolOrdering::LayerTools &layer_tools,
+ const LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx)
@@ -1166,7 +1360,6 @@ void GCode::process_layer(
// Group extrusions by an extruder, then by an object, an island and a region.
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
-
for (const LayerToPrint &layer_to_print : layers) {
if (layer_to_print.support_layer != nullptr) {
const SupportLayer &support_layer = *layer_to_print.support_layer;
@@ -1233,8 +1426,8 @@ void GCode::process_layer(
layer_surface_bboxes.push_back(get_extents(expoly.contour));
auto point_inside_surface = [&layer, &layer_surface_bboxes](const size_t i, const Point &point) {
const BoundingBox &bbox = layer_surface_bboxes[i];
- return point.x >= bbox.min.x && point.x < bbox.max.x &&
- point.y >= bbox.min.y && point.y < bbox.max.y &&
+ return point(0) >= bbox.min(0) && point(0) < bbox.max(0) &&
+ point(1) >= bbox.min(1) && point(1) < bbox.max(1) &&
layer.slices.expolygons[i].contour.contains(point);
};
@@ -1243,74 +1436,77 @@ void GCode::process_layer(
if (layerm == nullptr)
continue;
const PrintRegion &region = *print.regions()[region_id];
-
- // process perimeters
- for (const ExtrusionEntity *ee : layerm->perimeters.entities) {
- // perimeter_coll represents perimeter extrusions of a single island.
- const auto *perimeter_coll = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (perimeter_coll->entities.empty())
- // This shouldn't happen but first_point() would fail.
- continue;
- // Init by_extruder item only if we actually use the extruder.
- std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
- by_extruder,
- std::max<int>(region.config().perimeter_extruder.value - 1, 0),
- &layer_to_print - layers.data(),
- layers.size(), n_slices+1);
- for (size_t i = 0; i <= n_slices; ++ i)
- if (// perimeter_coll->first_point does not fit inside any slice
- i == n_slices ||
- // perimeter_coll->first_point fits inside ith slice
- point_inside_surface(i, perimeter_coll->first_point())) {
- if (islands[i].by_region.empty())
- islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
- islands[i].by_region[region_id].perimeters.append(perimeter_coll->entities);
- break;
- }
- }
-
- // process infill
- // layerm->fills is a collection of Slic3r::ExtrusionPath::Collection objects (C++ class ExtrusionEntityCollection),
- // each one containing the ExtrusionPath objects of a certain infill "group" (also called "surface"
- // throughout the code). We can redefine the order of such Collections but we have to
- // do each one completely at once.
- for (const ExtrusionEntity *ee : layerm->fills.entities) {
- // fill represents infill extrusions of a single island.
- const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
- if (fill->entities.empty())
- // This shouldn't happen but first_point() would fail.
- continue;
- // init by_extruder item only if we actually use the extruder
- int extruder_id = std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1);
- // Init by_extruder item only if we actually use the extruder.
- std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
- by_extruder,
- extruder_id,
- &layer_to_print - layers.data(),
- layers.size(), n_slices+1);
- for (size_t i = 0; i <= n_slices; ++i)
- if (// fill->first_point does not fit inside any slice
- i == n_slices ||
- // fill->first_point fits inside ith slice
- point_inside_surface(i, fill->first_point())) {
- if (islands[i].by_region.empty())
- islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
- islands[i].by_region[region_id].infills.append(fill->entities);
- break;
+
+
+ // Now we must process perimeters and infills and create islands of extrusions in by_region std::map.
+ // It is also necessary to save which extrusions are part of MM wiping and which are not.
+ // The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
+ for (std::string entity_type("infills") ; entity_type != "done" ; entity_type = entity_type=="infills" ? "perimeters" : "done") {
+
+ const ExtrusionEntitiesPtr& source_entities = entity_type=="infills" ? layerm->fills.entities : layerm->perimeters.entities;
+
+ for (const ExtrusionEntity *ee : source_entities) {
+ // fill represents infill extrusions of a single island.
+ const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (fill->entities.empty()) // This shouldn't happen but first_point() would fail.
+ continue;
+
+ // This extrusion is part of certain Region, which tells us which extruder should be used for it:
+ int correct_extruder_id = Print::get_extruder(*fill, region);
+ //FIXME what is this?
+ entity_type=="infills" ?
+ std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
+ std::max<int>(region.config().perimeter_extruder.value - 1, 0);
+
+ // Let's recover vector of extruder overrides:
+ const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size());
+
+ // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it:
+ for (unsigned int extruder : layer_tools.extruders)
+ {
+ // Init by_extruder item only if we actually use the extruder:
+ if (std::find(entity_overrides->begin(), entity_overrides->end(), extruder) != entity_overrides->end() || // at least one copy is overridden to use this extruder
+ std::find(entity_overrides->begin(), entity_overrides->end(), -extruder-1) != entity_overrides->end() || // at least one copy would normally be printed with this extruder (see get_extruder_overrides function for explanation)
+ (std::find(layer_tools.extruders.begin(), layer_tools.extruders.end(), correct_extruder_id) == layer_tools.extruders.end() && extruder == layer_tools.extruders.back())) // this entity is not overridden, but its extruder is not in layer_tools - we'll print it
+ //by last extruder on this layer (could happen e.g. when a wiping object is taller than others - dontcare extruders are eradicated from layer_tools)
+ {
+ std::vector<ObjectByExtruder::Island> &islands = object_islands_by_extruder(
+ by_extruder,
+ extruder,
+ &layer_to_print - layers.data(),
+ layers.size(), n_slices+1);
+ for (size_t i = 0; i <= n_slices; ++i)
+ if (// fill->first_point does not fit inside any slice
+ i == n_slices ||
+ // fill->first_point fits inside ith slice
+ point_inside_surface(i, fill->first_point())) {
+ if (islands[i].by_region.empty())
+ islands[i].by_region.assign(print.regions().size(), ObjectByExtruder::Island::Region());
+ islands[i].by_region[region_id].append(entity_type, fill, entity_overrides, layer_to_print.object()->copies().size());
+ break;
+ }
+ }
}
+ }
}
} // for regions
}
} // for objects
+
+
// Extrude the skirt, brim, support, perimeters, infill ordered by the extruders.
std::vector<std::unique_ptr<EdgeGrid::Grid>> lower_layer_edge_grids(layers.size());
for (unsigned int extruder_id : layer_tools.extruders)
- {
+ {
gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ?
m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) :
this->set_extruder(extruder_id);
+ // let analyzer tag generator aware of a role type change
+ if (m_enable_analyzer && layer_tools.has_wipe_tower && m_wipe_tower)
+ m_last_analyzer_extrusion_role = erWipeTower;
+
if (extrude_skirt) {
auto loops_it = skirt_loops_per_extruder.find(extruder_id);
if (loops_it != skirt_loops_per_extruder.end()) {
@@ -1327,7 +1523,7 @@ void GCode::process_layer(
for (ExtrusionPath &path : loop.paths) {
path.height = (float)layer.height;
path.mm3_per_mm = mm3_per_mm;
- }
+ }
gcode += this->extrude_loop(loop, "skirt", m_config.support_material_speed.value);
}
m_avoid_crossing_perimeters.use_external_mp = false;
@@ -1336,7 +1532,7 @@ void GCode::process_layer(
m_avoid_crossing_perimeters.disable_once = true;
}
}
-
+
// Extrude brim with the extruder of the 1st region.
if (! m_brim_done) {
this->set_origin(0., 0.);
@@ -1349,49 +1545,60 @@ void GCode::process_layer(
m_avoid_crossing_perimeters.disable_once = true;
}
+
auto objects_by_extruder_it = by_extruder.find(extruder_id);
if (objects_by_extruder_it == by_extruder.end())
continue;
- for (const ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
- const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
- const PrintObject *print_object = layers[layer_id].object();
- if (print_object == nullptr)
- // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
- continue;
-
- m_config.apply(print_object->config(), true);
- m_layer = layers[layer_id].layer();
- if (m_config.avoid_crossing_perimeters)
- m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
- Points copies;
- if (single_object_idx == size_t(-1))
- copies = print_object->_shifted_copies;
- else
- copies.push_back(print_object->_shifted_copies[single_object_idx]);
- // Sort the copies by the closest point starting with the current print position.
-
- for (const Point &copy : copies) {
- // When starting a new object, use the external motion planner for the first travel move.
- std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
- if (m_last_obj_copy != this_object_copy)
- m_avoid_crossing_perimeters.use_external_mp_once = true;
- m_last_obj_copy = this_object_copy;
- this->set_origin(unscale(copy.x), unscale(copy.y));
- if (object_by_extruder.support != nullptr) {
- m_layer = layers[layer_id].support_layer;
- gcode += this->extrude_support(
- // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
- object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
- m_layer = layers[layer_id].layer();
- }
- for (const ObjectByExtruder::Island &island : object_by_extruder.islands) {
- if (print.config().infill_first) {
- gcode += this->extrude_infill(print, island.by_region);
- gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
- } else {
- gcode += this->extrude_perimeters(print, island.by_region, lower_layer_edge_grids[layer_id]);
- gcode += this->extrude_infill(print, island.by_region);
+ // We are almost ready to print. However, we must go through all the objects twice to print the the overridden extrusions first (infill/perimeter wiping feature):
+ for (int print_wipe_extrusions=const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden(); print_wipe_extrusions>=0; --print_wipe_extrusions) {
+ if (print_wipe_extrusions == 0)
+ gcode+="; PURGING FINISHED\n";
+
+ for (ObjectByExtruder &object_by_extruder : objects_by_extruder_it->second) {
+ const size_t layer_id = &object_by_extruder - objects_by_extruder_it->second.data();
+ const PrintObject *print_object = layers[layer_id].object();
+ if (print_object == nullptr)
+ // This layer is empty for this particular object, it has neither object extrusions nor support extrusions at this print_z.
+ continue;
+
+ m_config.apply(print_object->config(), true);
+ m_layer = layers[layer_id].layer();
+ if (m_config.avoid_crossing_perimeters)
+ m_avoid_crossing_perimeters.init_layer_mp(union_ex(m_layer->slices, true));
+ Points copies;
+ if (single_object_idx == size_t(-1))
+ copies = print_object->copies();
+ else
+ copies.push_back(print_object->copies()[single_object_idx]);
+ // Sort the copies by the closest point starting with the current print position.
+
+ unsigned int copy_id = 0;
+ for (const Point &copy : copies) {
+ // When starting a new object, use the external motion planner for the first travel move.
+ std::pair<const PrintObject*, Point> this_object_copy(print_object, copy);
+ if (m_last_obj_copy != this_object_copy)
+ m_avoid_crossing_perimeters.use_external_mp_once = true;
+ m_last_obj_copy = this_object_copy;
+ this->set_origin(unscale(copy));
+ if (object_by_extruder.support != nullptr && !print_wipe_extrusions) {
+ m_layer = layers[layer_id].support_layer;
+ gcode += this->extrude_support(
+ // support_extrusion_role is erSupportMaterial, erSupportMaterialInterface or erMixed for all extrusion paths.
+ object_by_extruder.support->chained_path_from(m_last_pos, false, object_by_extruder.support_extrusion_role));
+ m_layer = layers[layer_id].layer();
+ }
+ for (ObjectByExtruder::Island &island : object_by_extruder.islands) {
+ const auto& by_region_specific = const_cast<LayerTools&>(layer_tools).wiping_extrusions().is_anything_overridden() ? island.by_region_per_copy(copy_id, extruder_id, print_wipe_extrusions) : island.by_region;
+
+ if (print.config().infill_first) {
+ gcode += this->extrude_infill(print, by_region_specific);
+ gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+ } else {
+ gcode += this->extrude_perimeters(print, by_region_specific, lower_layer_edge_grids[layer_id]);
+ gcode += this->extrude_infill(print,by_region_specific);
+ }
}
+ ++copy_id;
}
}
}
@@ -1414,7 +1621,7 @@ void GCode::process_layer(
if (m_pressure_equalizer)
gcode = m_pressure_equalizer->process(gcode.c_str(), false);
// printf("G-code after filter:\n%s\n", out.c_str());
-
+
_write(file, gcode);
}
@@ -1426,15 +1633,22 @@ void GCode::apply_print_config(const PrintConfig &print_config)
void GCode::append_full_config(const Print& print, std::string& str)
{
- const StaticPrintConfig *configs[] = { &print.config(), &print.default_object_config(), &print.default_region_config() };
+ const StaticPrintConfig *configs[] = { static_cast<const GCodeConfig*>(&print.config()), &print.default_object_config(), &print.default_region_config() };
for (size_t i = 0; i < sizeof(configs) / sizeof(configs[0]); ++i) {
const StaticPrintConfig *cfg = configs[i];
for (const std::string &key : cfg->keys())
- {
if (key != "compatible_printers")
str += "; " + key + " = " + cfg->serialize(key) + "\n";
- }
}
+ const DynamicConfig &full_config = print.placeholder_parser().config();
+ for (const char *key : {
+ "print_settings_id", "filament_settings_id", "printer_settings_id",
+ "printer_model", "printer_variant", "default_print_profile", "default_filament_profile",
+ "compatible_printers_condition_cummulative", "inherits_cummulative" }) {
+ const ConfigOption *opt = full_config.option(key);
+ if (opt != nullptr)
+ str += std::string("; ") + key + " = " + opt->serialize() + "\n";
+ }
}
void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
@@ -1450,14 +1664,14 @@ void GCode::set_extruders(const std::vector<unsigned int> &extruder_ids)
}
}
-void GCode::set_origin(const Pointf &pointf)
+void GCode::set_origin(const Vec2d &pointf)
{
// if origin increases (goes towards right), last_pos decreases because it goes towards left
const Point translate(
- scale_(m_origin.x - pointf.x),
- scale_(m_origin.y - pointf.y)
+ scale_(m_origin(0) - pointf(0)),
+ scale_(m_origin(1) - pointf(1))
);
- m_last_pos.translate(translate);
+ m_last_pos += translate;
m_wipe.path.translate(translate);
m_origin = pointf;
}
@@ -1588,13 +1802,13 @@ static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, co
j = 0;
const Point &p1 = polygon.points[i];
const Point &p2 = polygon.points[j];
- const Slic3r::Point v_seg = p1.vector_to(p2);
- const Slic3r::Point v_pt = p1.vector_to(pt);
- const int64_t l2_seg = int64_t(v_seg.x) * int64_t(v_seg.x) + int64_t(v_seg.y) * int64_t(v_seg.y);
- int64_t t_pt = int64_t(v_seg.x) * int64_t(v_pt.x) + int64_t(v_seg.y) * int64_t(v_pt.y);
+ const Slic3r::Point v_seg = p2 - p1;
+ const Slic3r::Point v_pt = pt - p1;
+ const int64_t l2_seg = int64_t(v_seg(0)) * int64_t(v_seg(0)) + int64_t(v_seg(1)) * int64_t(v_seg(1));
+ int64_t t_pt = int64_t(v_seg(0)) * int64_t(v_pt(0)) + int64_t(v_seg(1)) * int64_t(v_pt(1));
if (t_pt < 0) {
// Closest to p1.
- double dabs = sqrt(int64_t(v_pt.x) * int64_t(v_pt.x) + int64_t(v_pt.y) * int64_t(v_pt.y));
+ double dabs = sqrt(int64_t(v_pt(0)) * int64_t(v_pt(0)) + int64_t(v_pt(1)) * int64_t(v_pt(1)));
if (dabs < d_min) {
d_min = dabs;
i_min = i;
@@ -1607,7 +1821,7 @@ static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, co
} else {
// Closest to the segment.
assert(t_pt >= 0 && t_pt <= l2_seg);
- int64_t d_seg = int64_t(v_seg.y) * int64_t(v_pt.x) - int64_t(v_seg.x) * int64_t(v_pt.y);
+ int64_t d_seg = int64_t(v_seg(1)) * int64_t(v_pt(0)) - int64_t(v_seg(0)) * int64_t(v_pt(1));
double d = double(d_seg) / sqrt(double(l2_seg));
double dabs = std::abs(d);
if (dabs < d_min) {
@@ -1616,15 +1830,15 @@ static Points::iterator project_point_to_polygon_and_insert(Polygon &polygon, co
// Evaluate the foot point.
pt_min = p1;
double linv = double(d_seg) / double(l2_seg);
- pt_min.x = pt.x - coord_t(floor(double(v_seg.y) * linv + 0.5));
- pt_min.y = pt.y + coord_t(floor(double(v_seg.x) * linv + 0.5));
+ pt_min(0) = pt(0) - coord_t(floor(double(v_seg(1)) * linv + 0.5));
+ pt_min(1) = pt(1) + coord_t(floor(double(v_seg(0)) * linv + 0.5));
assert(Line(p1, p2).distance_to(pt_min) < scale_(1e-5));
}
}
}
assert(i_min != size_t(-1));
- if (pt_min.distance_to(polygon.points[i_min]) > eps) {
+ if ((pt_min - polygon.points[i_min]).cast<double>().norm() > eps) {
// Insert a new point on the segment i_min, i_min+1.
return polygon.points.insert(polygon.points.begin() + (i_min + 1), pt_min);
}
@@ -1636,8 +1850,8 @@ std::vector<float> polygon_parameter_by_length(const Polygon &polygon)
// Parametrize the polygon by its length.
std::vector<float> lengths(polygon.points.size()+1, 0.);
for (size_t i = 1; i < polygon.points.size(); ++ i)
- lengths[i] = lengths[i-1] + float(polygon.points[i].distance_to(polygon.points[i-1]));
- lengths.back() = lengths[lengths.size()-2] + float(polygon.points.front().distance_to(polygon.points.back()));
+ lengths[i] = lengths[i-1] + (polygon.points[i] - polygon.points[i-1]).cast<float>().norm();
+ lengths.back() = lengths[lengths.size()-2] + (polygon.points.front() - polygon.points.back()).cast<float>().norm();
return lengths;
}
@@ -1685,10 +1899,10 @@ std::vector<float> polygon_angles_at_vertices(const Polygon &polygon, const std:
const Point &p0 = polygon.points[idx_prev];
const Point &p1 = polygon.points[idx_curr];
const Point &p2 = polygon.points[idx_next];
- const Point v1 = p0.vector_to(p1);
- const Point v2 = p1.vector_to(p2);
- int64_t dot = int64_t(v1.x)*int64_t(v2.x) + int64_t(v1.y)*int64_t(v2.y);
- int64_t cross = int64_t(v1.x)*int64_t(v2.y) - int64_t(v1.y)*int64_t(v2.x);
+ const Point v1 = p1 - p0;
+ const Point v2 = p2 - p1;
+ int64_t dot = int64_t(v1(0))*int64_t(v2(0)) + int64_t(v1(1))*int64_t(v2(1));
+ int64_t cross = int64_t(v1(0))*int64_t(v2(1)) - int64_t(v1(1))*int64_t(v2(0));
float angle = float(atan2(double(cross), double(dot)));
angles[idx_curr] = angle;
}
@@ -1712,10 +1926,10 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
{
static int iRun = 0;
BoundingBox bbox = (*lower_layer_edge_grid)->bbox();
- bbox.min.x -= scale_(5.f);
- bbox.min.y -= scale_(5.f);
- bbox.max.x += scale_(5.f);
- bbox.max.y += scale_(5.f);
+ bbox.min(0) -= scale_(5.f);
+ bbox.min(1) -= scale_(5.f);
+ bbox.max(0) += scale_(5.f);
+ bbox.max(1) += scale_(5.f);
EdgeGrid::save_png(*(*lower_layer_edge_grid), bbox, scale_(0.1f), debug_out_path("GCode_extrude_loop_edge_grid-%d.png", iRun++));
}
#endif
@@ -1751,7 +1965,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
break;
case spRear:
last_pos = m_layer->object()->bounding_box().center();
- last_pos.y += coord_t(3. * m_layer->object()->bounding_box().radius());
+ last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius());
last_pos_weight = 5.f;
break;
}
@@ -1884,7 +2098,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
//FIXME Better parametrize the loop by its length.
Polygon polygon = loop.polygon();
Point centroid = polygon.centroid();
- last_pos = Point(polygon.bounding_box().max.x, centroid.y);
+ last_pos = Point(polygon.bounding_box().max(0), centroid(1));
last_pos.rotate(fmod((float)rand()/16.0, 2.0*PI), centroid);
}
// Find the closest point, avoid overhangs.
@@ -1941,19 +2155,17 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
// create the destination point along the first segment and rotate it
// we make sure we don't exceed the segment length because we don't know
// the rotation of the second segment so we might cross the object boundary
- Line first_segment(
- paths.front().polyline.points[0],
- paths.front().polyline.points[1]
- );
- double distance = std::min<double>(
- scale_(EXTRUDER_CONFIG(nozzle_diameter)),
- first_segment.length()
- );
- Point point = first_segment.point_at(distance);
- point.rotate(angle, first_segment.a);
-
+ Vec2d p1 = paths.front().polyline.points.front().cast<double>();
+ Vec2d p2 = paths.front().polyline.points[1].cast<double>();
+ Vec2d v = p2 - p1;
+ double nd = scale_(EXTRUDER_CONFIG(nozzle_diameter));
+ double l2 = v.squaredNorm();
+ // Shift by no more than a nozzle diameter.
+ //FIXME Hiding the seams will not work nicely for very densely discretized contours!
+ Point pt = ((nd * nd >= l2) ? p2 : (p1 + v * (nd / sqrt(l2)))).cast<coord_t>();
+ pt.rotate(angle, paths.front().polyline.points.front());
// generate the travel move
- gcode += m_writer.travel_to_xy(this->point_to_gcode(point), "move inwards before travel");
+ gcode += m_writer.travel_to_xy(this->point_to_gcode(pt), "move inwards before travel");
}
return gcode;
@@ -2074,7 +2286,9 @@ void GCode::_write(FILE* file, const char *what)
// writes string to file
fwrite(gcode, 1, ::strlen(gcode), file);
// updates time estimator and gcode lines vector
- m_time_estimator.add_gcode_block(gcode);
+ m_normal_time_estimator.add_gcode_block(gcode);
+ if (m_silent_time_estimator_enabled)
+ m_silent_time_estimator.add_gcode_block(gcode);
}
}
@@ -2121,7 +2335,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
std::string gcode;
// go to first point of extrusion path
- if (!m_last_pos_defined || !m_last_pos.coincides_with(path.first_point())) {
+ if (!m_last_pos_defined || m_last_pos != path.first_point()) {
gcode += this->travel_to(
path.first_point(),
path.role(),
@@ -2194,7 +2408,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
double F = speed * 60; // convert mm/sec to mm/min
// extrude arc or line
- if (m_enable_extrusion_role_markers || m_enable_analyzer)
+ if (m_enable_extrusion_role_markers)
{
if (path.role() != m_last_extrusion_role)
{
@@ -2205,18 +2419,20 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
sprintf(buf, ";_EXTRUSION_ROLE:%d\n", int(m_last_extrusion_role));
gcode += buf;
}
- if (m_enable_analyzer)
- {
- char buf[32];
- sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_extrusion_role));
- gcode += buf;
- }
}
}
// adds analyzer tags and updates analyzer's tracking data
if (m_enable_analyzer)
{
+ if (path.role() != m_last_analyzer_extrusion_role)
+ {
+ m_last_analyzer_extrusion_role = path.role();
+ char buf[32];
+ sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), int(m_last_analyzer_extrusion_role));
+ gcode += buf;
+ }
+
if (m_last_mm3_per_mm != path.mm3_per_mm)
{
m_last_mm3_per_mm = path.mm3_per_mm;
@@ -2254,6 +2470,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
if (path.role() == erExternalPerimeter)
comment += ";_EXTERNAL_PERIMETER";
}
+
// F is mm per minute.
gcode += m_writer.set_speed(F, "", comment);
double path_length = 0.;
@@ -2433,21 +2650,76 @@ std::string GCode::set_extruder(unsigned int extruder_id)
}
// convert a model-space scaled point into G-code coordinates
-Pointf GCode::point_to_gcode(const Point &point) const
+Vec2d GCode::point_to_gcode(const Point &point) const
{
- Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset);
- return Pointf(
- unscale(point.x) + m_origin.x - extruder_offset.x,
- unscale(point.y) + m_origin.y - extruder_offset.y);
+ Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
+ return unscale(point) + m_origin - extruder_offset;
}
// convert a model-space scaled point into G-code coordinates
-Point GCode::gcode_to_point(const Pointf &point) const
+Point GCode::gcode_to_point(const Vec2d &point) const
{
- Pointf extruder_offset = EXTRUDER_CONFIG(extruder_offset);
+ Vec2d extruder_offset = EXTRUDER_CONFIG(extruder_offset);
return Point(
- scale_(point.x - m_origin.x + extruder_offset.x),
- scale_(point.y - m_origin.y + extruder_offset.y));
+ scale_(point(0) - m_origin(0) + extruder_offset(0)),
+ scale_(point(1) - m_origin(1) + extruder_offset(1)));
}
+// Goes through by_region std::vector and returns reference to a subvector of entities, that are to be printed
+// during infill/perimeter wiping, or normally (depends on wiping_entities parameter)
+// Returns a reference to member to avoid copying.
+const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtruder::Island::by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities)
+{
+ by_region_per_copy_cache.clear();
+
+ for (const auto& reg : by_region) {
+ by_region_per_copy_cache.push_back(ObjectByExtruder::Island::Region()); // creates a region in the newly created Island
+
+ // Now we are going to iterate through perimeters and infills and pick ones that are supposed to be printed
+ // References are used so that we don't have to repeat the same code
+ for (int iter = 0; iter < 2; ++iter) {
+ const ExtrusionEntitiesPtr& entities = (iter ? reg.infills.entities : reg.perimeters.entities);
+ ExtrusionEntityCollection& target_eec = (iter ? by_region_per_copy_cache.back().infills : by_region_per_copy_cache.back().perimeters);
+ const std::vector<const ExtruderPerCopy*>& overrides = (iter ? reg.infills_overrides : reg.perimeters_overrides);
+
+ // Now the most important thing - which extrusion should we print.
+ // See function ToolOrdering::get_extruder_overrides for details about the negative numbers hack.
+ int this_extruder_mark = wiping_entities ? extruder : -extruder-1;
+
+ for (unsigned int i=0;i<entities.size();++i)
+ if (overrides[i]->at(copy) == this_extruder_mark) // this copy should be printed with this extruder
+ target_eec.append((*entities[i]));
+ }
+ }
+ return by_region_per_copy_cache;
}
+
+
+
+// This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter)
+// It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy.
+void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num)
+{
+ // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves:
+ ExtrusionEntityCollection* perimeters_or_infills = &infills;
+ std::vector<const ExtruderPerCopy*>* perimeters_or_infills_overrides = &infills_overrides;
+
+ if (type == "perimeters") {
+ perimeters_or_infills = &perimeters;
+ perimeters_or_infills_overrides = &perimeters_overrides;
+ }
+ else
+ if (type != "infills") {
+ CONFESS("Unknown parameter!");
+ return;
+ }
+
+
+ // First we append the entities, there are eec->entities.size() of them:
+ perimeters_or_infills->append(eec->entities);
+
+ for (unsigned int i=0;i<eec->entities.size();++i)
+ perimeters_or_infills_overrides->push_back(copies_extruder);
+}
+
+} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp
index 0e5305cb5..34183012a 100644
--- a/xs/src/libslic3r/GCode.hpp
+++ b/xs/src/libslic3r/GCode.hpp
@@ -83,8 +83,10 @@ public:
const WipeTower::ToolChangeResult &priming,
const std::vector<std::vector<WipeTower::ToolChangeResult>> &tool_changes,
const WipeTower::ToolChangeResult &final_purge) :
- m_left(float(print_config.wipe_tower_x.value)),
- m_right(float(print_config.wipe_tower_x.value + print_config.wipe_tower_width.value)),
+ m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f),
+ m_right(float(/*print_config.wipe_tower_x.value +*/ print_config.wipe_tower_width.value)),
+ m_wipe_tower_pos(float(print_config.wipe_tower_x.value), float(print_config.wipe_tower_y.value)),
+ m_wipe_tower_rotation(float(print_config.wipe_tower_rotation_angle)),
m_priming(priming),
m_tool_changes(tool_changes),
m_final_purge(final_purge),
@@ -101,9 +103,14 @@ private:
WipeTowerIntegration& operator=(const WipeTowerIntegration&);
std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const;
+ // Postprocesses gcode: rotates and moves all G1 extrusions and returns result
+ std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const;
+
// Left / right edges of the wipe tower, for the planning of wipe moves.
const float m_left;
const float m_right;
+ const WipeTower::xy m_wipe_tower_pos;
+ const float m_wipe_tower_rotation;
// Reference to cached values at the Printer class.
const WipeTower::ToolChangeResult &m_priming;
const std::vector<std::vector<WipeTower::ToolChangeResult>> &m_tool_changes;
@@ -112,15 +119,18 @@ private:
int m_layer_idx;
int m_tool_change_idx;
bool m_brim_done;
+ bool i_have_brim = false;
};
class GCode {
public:
GCode() :
+ m_origin(Vec2d::Zero()),
m_enable_loop_clipping(true),
m_enable_cooling_markers(false),
m_enable_extrusion_role_markers(false),
m_enable_analyzer(false),
+ m_last_analyzer_extrusion_role(erNone),
m_layer_count(0),
m_layer_index(-1),
m_layer(nullptr),
@@ -132,6 +142,9 @@ public:
m_last_height(GCodeAnalyzer::Default_Height),
m_brim_done(false),
m_second_layer_things_done(false),
+ m_normal_time_estimator(GCodeTimeEstimator::Normal),
+ m_silent_time_estimator(GCodeTimeEstimator::Silent),
+ m_silent_time_estimator_enabled(false),
m_last_obj_copy(nullptr, Point(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max()))
{}
~GCode() {}
@@ -141,12 +154,12 @@ public:
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
- const Pointf& origin() const { return m_origin; }
- void set_origin(const Pointf &pointf);
- void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Pointf(x, y)); }
+ const Vec2d& origin() const { return m_origin; }
+ void set_origin(const Vec2d &pointf);
+ void set_origin(const coordf_t x, const coordf_t y) { this->set_origin(Vec2d(x, y)); }
const Point& last_pos() const { return m_last_pos; }
- Pointf point_to_gcode(const Point &point) const;
- Point gcode_to_point(const Pointf &point) const;
+ Vec2d point_to_gcode(const Point &point) const;
+ Point gcode_to_point(const Vec2d &point) const;
const FullPrintConfig &config() const { return m_config; }
const Layer* layer() const { return m_layer; }
GCodeWriter& writer() { return m_writer; }
@@ -186,7 +199,7 @@ protected:
const Print &print,
// Set of object & print layers of the same PrintObject and with the same print_z.
const std::vector<LayerToPrint> &layers,
- const ToolOrdering::LayerTools &layer_tools,
+ const LayerTools &layer_tools,
// If set to size_t(-1), then print all copies of all objects.
// Otherwise print a single copy of a single object.
const size_t single_object_idx = size_t(-1));
@@ -201,6 +214,7 @@ protected:
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
+ typedef std::vector<int> ExtruderPerCopy;
// Extruding multiple objects with soluble / non-soluble / combined supports
// on a multi-material printer, trying to minimize tool switches.
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
@@ -216,11 +230,24 @@ protected:
struct Region {
ExtrusionEntityCollection perimeters;
ExtrusionEntityCollection infills;
+
+ std::vector<const ExtruderPerCopy*> infills_overrides;
+ std::vector<const ExtruderPerCopy*> perimeters_overrides;
+
+ // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
+ void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num);
};
- std::vector<Region> by_region;
+
+ std::vector<Region> by_region; // all extrusions for this island, grouped by regions
+ const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
+
+ private:
+ std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating
};
std::vector<Island> islands;
};
+
+
std::string extrude_perimeters(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region, std::unique_ptr<EdgeGrid::Grid> &lower_layer_edge_grid);
std::string extrude_infill(const Print &print, const std::vector<ObjectByExtruder::Island::Region> &by_region);
std::string extrude_support(const ExtrusionEntityCollection &support_fills);
@@ -234,7 +261,7 @@ protected:
/* Origin of print coordinates expressed in unscaled G-code coordinates.
This affects the input arguments supplied to the extrude*() and travel_to()
methods. */
- Pointf m_origin;
+ Vec2d m_origin;
FullPrintConfig m_config;
GCodeWriter m_writer;
PlaceholderParser m_placeholder_parser;
@@ -255,6 +282,7 @@ protected:
// Extended markers will be added during G-code generation.
// The G-code Analyzer will remove these comments from the final G-code.
bool m_enable_analyzer;
+ ExtrusionRole m_last_analyzer_extrusion_role;
// How many times will change_layer() be called?
// change_layer() will update the progress bar.
unsigned int m_layer_count;
@@ -289,8 +317,10 @@ protected:
// Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy;
- // Time estimator
- GCodeTimeEstimator m_time_estimator;
+ // Time estimators
+ GCodeTimeEstimator m_normal_time_estimator;
+ GCodeTimeEstimator m_silent_time_estimator;
+ bool m_silent_time_estimator_enabled;
// Analyzer
GCodeAnalyzer m_analyzer;
@@ -308,6 +338,7 @@ protected:
void _write_format(FILE* file, const char* format, ...);
std::string _extrude(const ExtrusionPath &path, std::string description = "", double speed = -1);
+ void print_machine_envelope(FILE *file, Print &print);
void _print_first_layer_bed_temperature(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
void _print_first_layer_extruder_temperatures(FILE *file, Print &print, const std::string &gcode, unsigned int first_printing_extruder_id, bool wait);
// this flag triggers first layer speeds
diff --git a/xs/src/libslic3r/GCode/Analyzer.cpp b/xs/src/libslic3r/GCode/Analyzer.cpp
index 6530806c4..51d5b1a06 100644
--- a/xs/src/libslic3r/GCode/Analyzer.cpp
+++ b/xs/src/libslic3r/GCode/Analyzer.cpp
@@ -14,7 +14,7 @@ static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float INCHES_TO_MM = 25.4f;
static const float DEFAULT_FEEDRATE = 0.0f;
static const unsigned int DEFAULT_EXTRUDER_ID = 0;
-static const Slic3r::Pointf3 DEFAULT_START_POSITION = Slic3r::Pointf3(0.0f, 0.0f, 0.0f);
+static const Slic3r::Vec3d DEFAULT_START_POSITION = Slic3r::Vec3d(0.0f, 0.0f, 0.0f);
static const float DEFAULT_START_EXTRUSION = 0.0f;
namespace Slic3r {
@@ -71,7 +71,7 @@ bool GCodeAnalyzer::Metadata::operator != (const GCodeAnalyzer::Metadata& other)
return false;
}
-GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder)
+GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder)
: type(type)
, data(extrusion_role, extruder_id, mm3_per_mm, width, height, feedrate)
, start_position(start_position)
@@ -80,7 +80,7 @@ GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, ExtrusionRole extrusi
{
}
-GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder)
+GCodeAnalyzer::GCodeMove::GCodeMove(GCodeMove::EType type, const GCodeAnalyzer::Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder)
: type(type)
, data(data)
, start_position(start_position)
@@ -97,8 +97,8 @@ GCodeAnalyzer::GCodeAnalyzer()
void GCodeAnalyzer::reset()
{
_set_units(Millimeters);
- _set_positioning_xyz_type(Absolute);
- _set_positioning_e_type(Relative);
+ _set_global_positioning_type(Absolute);
+ _set_e_local_positioning_type(Absolute);
_set_extrusion_role(erNone);
_set_extruder_id(DEFAULT_EXTRUDER_ID);
_set_mm3_per_mm(Default_mm3_per_mm);
@@ -177,6 +177,16 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
_processG1(line);
break;
}
+ case 10: // Retract
+ {
+ _processG10(line);
+ break;
+ }
+ case 11: // Unretract
+ {
+ _processG11(line);
+ break;
+ }
case 22: // Firmware controlled Retract
{
_processG22(line);
@@ -237,13 +247,13 @@ void GCodeAnalyzer::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLi
}
// Returns the new absolute position on the given axis in dependence of the given parameters
-float axis_absolute_position_from_G1_line(GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeAnalyzer::EUnits units, GCodeAnalyzer::EPositioningType type, float current_absolute_position)
+float axis_absolute_position_from_G1_line(GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeAnalyzer::EUnits units, bool is_relative, float current_absolute_position)
{
float lengthsScaleFactor = (units == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
if (lineG1.has(Slic3r::Axis(axis)))
{
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
- return (type == GCodeAnalyzer::Absolute) ? ret : current_absolute_position + ret;
+ return is_relative ? current_absolute_position + ret : ret;
}
else
return current_absolute_position;
@@ -256,7 +266,11 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
float new_pos[Num_Axis];
for (unsigned char a = X; a < Num_Axis; ++a)
{
- new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, (a == E) ? _get_positioning_e_type() : _get_positioning_xyz_type(), _get_axis_position((EAxis)a));
+ bool is_relative = (_get_global_positioning_type() == Relative);
+ if (a == E)
+ is_relative |= (_get_e_local_positioning_type() == Relative);
+
+ new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, _get_axis_position((EAxis)a));
}
// updates feedrate from line, if present
@@ -305,6 +319,18 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
_store_move(type);
}
+void GCodeAnalyzer::_processG10(const GCodeReader::GCodeLine& line)
+{
+ // stores retract move
+ _store_move(GCodeMove::Retract);
+}
+
+void GCodeAnalyzer::_processG11(const GCodeReader::GCodeLine& line)
+{
+ // stores unretract move
+ _store_move(GCodeMove::Unretract);
+}
+
void GCodeAnalyzer::_processG22(const GCodeReader::GCodeLine& line)
{
// stores retract move
@@ -319,12 +345,12 @@ void GCodeAnalyzer::_processG23(const GCodeReader::GCodeLine& line)
void GCodeAnalyzer::_processG90(const GCodeReader::GCodeLine& line)
{
- _set_positioning_xyz_type(Absolute);
+ _set_global_positioning_type(Absolute);
}
void GCodeAnalyzer::_processG91(const GCodeReader::GCodeLine& line)
{
- _set_positioning_xyz_type(Relative);
+ _set_global_positioning_type(Relative);
}
void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line)
@@ -367,12 +393,12 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line)
void GCodeAnalyzer::_processM82(const GCodeReader::GCodeLine& line)
{
- _set_positioning_e_type(Absolute);
+ _set_e_local_positioning_type(Absolute);
}
void GCodeAnalyzer::_processM83(const GCodeReader::GCodeLine& line)
{
- _set_positioning_e_type(Relative);
+ _set_e_local_positioning_type(Relative);
}
void GCodeAnalyzer::_processT(const GCodeReader::GCodeLine& line)
@@ -466,24 +492,24 @@ GCodeAnalyzer::EUnits GCodeAnalyzer::_get_units() const
return m_state.units;
}
-void GCodeAnalyzer::_set_positioning_xyz_type(GCodeAnalyzer::EPositioningType type)
+void GCodeAnalyzer::_set_global_positioning_type(GCodeAnalyzer::EPositioningType type)
{
- m_state.positioning_xyz_type = type;
+ m_state.global_positioning_type = type;
}
-GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_positioning_xyz_type() const
+GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_global_positioning_type() const
{
- return m_state.positioning_xyz_type;
+ return m_state.global_positioning_type;
}
-void GCodeAnalyzer::_set_positioning_e_type(GCodeAnalyzer::EPositioningType type)
+void GCodeAnalyzer::_set_e_local_positioning_type(GCodeAnalyzer::EPositioningType type)
{
- m_state.positioning_e_type = type;
+ m_state.e_local_positioning_type = type;
}
-GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_positioning_e_type() const
+GCodeAnalyzer::EPositioningType GCodeAnalyzer::_get_e_local_positioning_type() const
{
- return m_state.positioning_e_type;
+ return m_state.e_local_positioning_type;
}
void GCodeAnalyzer::_set_extrusion_role(ExtrusionRole extrusion_role)
@@ -561,12 +587,12 @@ void GCodeAnalyzer::_reset_axes_position()
::memset((void*)m_state.position, 0, Num_Axis * sizeof(float));
}
-void GCodeAnalyzer::_set_start_position(const Pointf3& position)
+void GCodeAnalyzer::_set_start_position(const Vec3d& position)
{
m_state.start_position = position;
}
-const Pointf3& GCodeAnalyzer::_get_start_position() const
+const Vec3d& GCodeAnalyzer::_get_start_position() const
{
return m_state.start_position;
}
@@ -586,9 +612,9 @@ float GCodeAnalyzer::_get_delta_extrusion() const
return _get_axis_position(E) - m_state.start_extrusion;
}
-Pointf3 GCodeAnalyzer::_get_end_position() const
+Vec3d GCodeAnalyzer::_get_end_position() const
{
- return Pointf3(m_state.position[X], m_state.position[Y], m_state.position[Z]);
+ return Vec3d(m_state.position[X], m_state.position[Y], m_state.position[Z]);
}
void GCodeAnalyzer::_store_move(GCodeAnalyzer::GCodeMove::EType type)
@@ -647,15 +673,17 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
Metadata data;
float z = FLT_MAX;
Polyline polyline;
- Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX);
+ Vec3d position(FLT_MAX, FLT_MAX, FLT_MAX);
+ float volumetric_rate = FLT_MAX;
GCodePreviewData::Range height_range;
GCodePreviewData::Range width_range;
GCodePreviewData::Range feedrate_range;
+ GCodePreviewData::Range volumetric_rate_range;
// constructs the polylines while traversing the moves
for (const GCodeMove& move : extrude_moves->second)
{
- if ((data != move.data) || (data.feedrate != move.data.feedrate) || (z != move.start_position.z) || (position != move.start_position))
+ if ((data != move.data) || (z != move.start_position.z()) || (position != move.start_position) || (volumetric_rate != move.data.feedrate * (float)move.data.mm3_per_mm))
{
// store current polyline
polyline.remove_duplicate_points();
@@ -665,19 +693,21 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
polyline = Polyline();
// add both vertices of the move
- polyline.append(Point(scale_(move.start_position.x), scale_(move.start_position.y)));
- polyline.append(Point(scale_(move.end_position.x), scale_(move.end_position.y)));
+ polyline.append(Point(scale_(move.start_position.x()), scale_(move.start_position.y())));
+ polyline.append(Point(scale_(move.end_position.x()), scale_(move.end_position.y())));
// update current values
data = move.data;
- z = move.start_position.z;
+ z = move.start_position.z();
+ volumetric_rate = move.data.feedrate * (float)move.data.mm3_per_mm;
height_range.update_from(move.data.height);
width_range.update_from(move.data.width);
feedrate_range.update_from(move.data.feedrate);
+ volumetric_rate_range.update_from(volumetric_rate);
}
else
// append end vertex of the move to current polyline
- polyline.append(Point(scale_(move.end_position.x), scale_(move.end_position.y)));
+ polyline.append(Point(scale_(move.end_position.x()), scale_(move.end_position.y())));
// update current values
position = move.end_position;
@@ -688,9 +718,10 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
Helper::store_polyline(polyline, data, z, preview_data);
// updates preview ranges data
- preview_data.extrusion.ranges.height.set_from(height_range);
- preview_data.extrusion.ranges.width.set_from(width_range);
- preview_data.extrusion.ranges.feedrate.set_from(feedrate_range);
+ preview_data.ranges.height.update_from(height_range);
+ preview_data.ranges.width.update_from(width_range);
+ preview_data.ranges.feedrate.update_from(feedrate_range);
+ preview_data.ranges.volumetric_rate.update_from(volumetric_rate_range);
}
void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
@@ -711,17 +742,21 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
return;
Polyline3 polyline;
- Pointf3 position(FLT_MAX, FLT_MAX, FLT_MAX);
+ Vec3d position(FLT_MAX, FLT_MAX, FLT_MAX);
GCodePreviewData::Travel::EType type = GCodePreviewData::Travel::Num_Types;
GCodePreviewData::Travel::Polyline::EDirection direction = GCodePreviewData::Travel::Polyline::Num_Directions;
float feedrate = FLT_MAX;
unsigned int extruder_id = -1;
+ GCodePreviewData::Range height_range;
+ GCodePreviewData::Range width_range;
+ GCodePreviewData::Range feedrate_range;
+
// constructs the polylines while traversing the moves
for (const GCodeMove& move : travel_moves->second)
{
GCodePreviewData::Travel::EType move_type = (move.delta_extruder < 0.0f) ? GCodePreviewData::Travel::Retract : ((move.delta_extruder > 0.0f) ? GCodePreviewData::Travel::Extrude : GCodePreviewData::Travel::Move);
- GCodePreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x != move.end_position.x) || (move.start_position.y != move.end_position.y)) ? GCodePreviewData::Travel::Polyline::Generic : GCodePreviewData::Travel::Polyline::Vertical;
+ GCodePreviewData::Travel::Polyline::EDirection move_direction = ((move.start_position.x() != move.end_position.x()) || (move.start_position.y() != move.end_position.y())) ? GCodePreviewData::Travel::Polyline::Generic : GCodePreviewData::Travel::Polyline::Vertical;
if ((type != move_type) || (direction != move_direction) || (feedrate != move.data.feedrate) || (position != move.start_position) || (extruder_id != move.data.extruder_id))
{
@@ -733,23 +768,31 @@ void GCodeAnalyzer::_calc_gcode_preview_travel(GCodePreviewData& preview_data)
polyline = Polyline3();
// add both vertices of the move
- polyline.append(Point3(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z)));
- polyline.append(Point3(scale_(move.end_position.x), scale_(move.end_position.y), scale_(move.end_position.z)));
+ polyline.append(Vec3crd(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z())));
+ polyline.append(Vec3crd(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z())));
}
else
// append end vertex of the move to current polyline
- polyline.append(Point3(scale_(move.end_position.x), scale_(move.end_position.y), scale_(move.end_position.z)));
+ polyline.append(Vec3crd(scale_(move.end_position.x()), scale_(move.end_position.y()), scale_(move.end_position.z())));
// update current values
position = move.end_position;
type = move_type;
feedrate = move.data.feedrate;
extruder_id = move.data.extruder_id;
+ height_range.update_from(move.data.height);
+ width_range.update_from(move.data.width);
+ feedrate_range.update_from(move.data.feedrate);
}
// store last polyline
polyline.remove_duplicate_points();
Helper::store_polyline(polyline, type, direction, feedrate, extruder_id, preview_data);
+
+ // updates preview ranges data
+ preview_data.ranges.height.update_from(height_range);
+ preview_data.ranges.width.update_from(width_range);
+ preview_data.ranges.feedrate.update_from(feedrate_range);
}
void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_data)
@@ -761,7 +804,7 @@ void GCodeAnalyzer::_calc_gcode_preview_retractions(GCodePreviewData& preview_da
for (const GCodeMove& move : retraction_moves->second)
{
// store position
- Point3 position(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z));
+ Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.retraction.positions.emplace_back(position, move.data.width, move.data.height);
}
}
@@ -775,7 +818,7 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_
for (const GCodeMove& move : unretraction_moves->second)
{
// store position
- Point3 position(scale_(move.start_position.x), scale_(move.start_position.y), scale_(move.start_position.z));
+ Vec3crd position(scale_(move.start_position.x()), scale_(move.start_position.y()), scale_(move.start_position.z()));
preview_data.unretraction.positions.emplace_back(position, move.data.width, move.data.height);
}
}
diff --git a/xs/src/libslic3r/GCode/Analyzer.hpp b/xs/src/libslic3r/GCode/Analyzer.hpp
index 7939d432d..27a49b869 100644
--- a/xs/src/libslic3r/GCode/Analyzer.hpp
+++ b/xs/src/libslic3r/GCode/Analyzer.hpp
@@ -75,12 +75,12 @@ public:
EType type;
Metadata data;
- Pointf3 start_position;
- Pointf3 end_position;
+ Vec3d start_position;
+ Vec3d end_position;
float delta_extruder;
- GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder);
- GCodeMove(EType type, const Metadata& data, const Pointf3& start_position, const Pointf3& end_position, float delta_extruder);
+ GCodeMove(EType type, ExtrusionRole extrusion_role, unsigned int extruder_id, double mm3_per_mm, float width, float height, float feedrate, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder);
+ GCodeMove(EType type, const Metadata& data, const Vec3d& start_position, const Vec3d& end_position, float delta_extruder);
};
typedef std::vector<GCodeMove> GCodeMovesList;
@@ -90,10 +90,10 @@ private:
struct State
{
EUnits units;
- EPositioningType positioning_xyz_type;
- EPositioningType positioning_e_type;
+ EPositioningType global_positioning_type;
+ EPositioningType e_local_positioning_type;
Metadata data;
- Pointf3 start_position;
+ Vec3d start_position = Vec3d::Zero();
float start_extrusion;
float position[Num_Axis];
};
@@ -127,6 +127,12 @@ private:
// Move
void _processG1(const GCodeReader::GCodeLine& line);
+ // Retract
+ void _processG10(const GCodeReader::GCodeLine& line);
+
+ // Unretract
+ void _processG11(const GCodeReader::GCodeLine& line);
+
// Firmware controlled Retract
void _processG22(const GCodeReader::GCodeLine& line);
@@ -170,11 +176,11 @@ private:
void _set_units(EUnits units);
EUnits _get_units() const;
- void _set_positioning_xyz_type(EPositioningType type);
- EPositioningType _get_positioning_xyz_type() const;
+ void _set_global_positioning_type(EPositioningType type);
+ EPositioningType _get_global_positioning_type() const;
- void _set_positioning_e_type(EPositioningType type);
- EPositioningType _get_positioning_e_type() const;
+ void _set_e_local_positioning_type(EPositioningType type);
+ EPositioningType _get_e_local_positioning_type() const;
void _set_extrusion_role(ExtrusionRole extrusion_role);
ExtrusionRole _get_extrusion_role() const;
@@ -200,15 +206,15 @@ private:
// Sets axes position to zero
void _reset_axes_position();
- void _set_start_position(const Pointf3& position);
- const Pointf3& _get_start_position() const;
+ void _set_start_position(const Vec3d& position);
+ const Vec3d& _get_start_position() const;
void _set_start_extrusion(float extrusion);
float _get_start_extrusion() const;
float _get_delta_extrusion() const;
// Returns current xyz position (from m_state.position[])
- Pointf3 _get_end_position() const;
+ Vec3d _get_end_position() const;
// Adds a new move with the given data
void _store_move(GCodeMove::EType type);
diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/xs/src/libslic3r/GCode/CoolingBuffer.cpp
index cd2baeffb..40ccc7b09 100644
--- a/xs/src/libslic3r/GCode/CoolingBuffer.cpp
+++ b/xs/src/libslic3r/GCode/CoolingBuffer.cpp
@@ -23,366 +23,586 @@ CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_gcodegen(gcodegen), m_current_
void CoolingBuffer::reset()
{
m_current_pos.assign(5, 0.f);
- Pointf3 pos = m_gcodegen.writer().get_position();
- m_current_pos[0] = float(pos.x);
- m_current_pos[1] = float(pos.y);
- m_current_pos[2] = float(pos.z);
+ Vec3d pos = m_gcodegen.writer().get_position();
+ m_current_pos[0] = float(pos(0));
+ m_current_pos[1] = float(pos(1));
+ m_current_pos[2] = float(pos(2));
m_current_pos[4] = float(m_gcodegen.config().travel_speed.value);
}
-#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder)
-
-std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_id)
+struct CoolingLine
{
- const FullPrintConfig &config = m_gcodegen.config();
- const std::vector<Extruder> &extruders = m_gcodegen.writer().extruders();
- const size_t num_extruders = extruders.size();
+ enum Type {
+ TYPE_SET_TOOL = 1 << 0,
+ TYPE_EXTRUDE_END = 1 << 1,
+ TYPE_BRIDGE_FAN_START = 1 << 2,
+ TYPE_BRIDGE_FAN_END = 1 << 3,
+ TYPE_G0 = 1 << 4,
+ TYPE_G1 = 1 << 5,
+ TYPE_ADJUSTABLE = 1 << 6,
+ TYPE_EXTERNAL_PERIMETER = 1 << 7,
+ // The line sets a feedrate.
+ TYPE_HAS_F = 1 << 8,
+ TYPE_WIPE = 1 << 9,
+ TYPE_G4 = 1 << 10,
+ TYPE_G92 = 1 << 11,
+ };
+
+ CoolingLine(unsigned int type, size_t line_start, size_t line_end) :
+ type(type), line_start(line_start), line_end(line_end),
+ length(0.f), feedrate(0.f), time(0.f), time_max(0.f), slowdown(false) {}
+
+ bool adjustable(bool slowdown_external_perimeters) const {
+ return (this->type & TYPE_ADJUSTABLE) &&
+ (! (this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) &&
+ this->time < this->time_max;
+ }
+
+ bool adjustable() const {
+ return (this->type & TYPE_ADJUSTABLE) && this->time < this->time_max;
+ }
- // Calculate the required per extruder time stretches.
- struct Adjustment {
- Adjustment(unsigned int extruder_id = 0) : extruder_id(extruder_id) {}
- // Calculate the total elapsed time per this extruder, adjusted for the slowdown.
- float elapsed_time_total() {
- float time_total = 0.f;
- for (const Line &line : lines)
+ size_t type;
+ // Start of this line at the G-code snippet.
+ size_t line_start;
+ // End of this line at the G-code snippet.
+ size_t line_end;
+ // XY Euclidian length of this segment.
+ float length;
+ // Current feedrate, possibly adjusted.
+ float feedrate;
+ // Current duration of this segment.
+ float time;
+ // Maximum duration of this segment.
+ float time_max;
+ // If marked with the "slowdown" flag, the line has been slowed down.
+ bool slowdown;
+};
+
+// Calculate the required per extruder time stretches.
+struct PerExtruderAdjustments
+{
+ // Calculate the total elapsed time per this extruder, adjusted for the slowdown.
+ float elapsed_time_total() {
+ float time_total = 0.f;
+ for (const CoolingLine &line : lines)
+ time_total += line.time;
+ return time_total;
+ }
+ // Calculate the total elapsed time when slowing down
+ // to the minimum extrusion feed rate defined for the current material.
+ float maximum_time_after_slowdown(bool slowdown_external_perimeters) {
+ float time_total = 0.f;
+ for (const CoolingLine &line : lines)
+ if (line.adjustable(slowdown_external_perimeters)) {
+ if (line.time_max == FLT_MAX)
+ return FLT_MAX;
+ else
+ time_total += line.time_max;
+ } else
time_total += line.time;
- return time_total;
- }
- // Calculate the maximum time when slowing down.
- float maximum_time(bool slowdown_external_perimeters) {
- float time_total = 0.f;
- for (const Line &line : lines)
- if (line.adjustable(slowdown_external_perimeters)) {
- if (line.time_max == FLT_MAX)
- return FLT_MAX;
- else
- time_total += line.time_max;
- } else
- time_total += line.time;
- return time_total;
- }
- // Calculate the non-adjustable part of the total time.
- float non_adjustable_time(bool slowdown_external_perimeters) {
- float time_total = 0.f;
- for (const Line &line : lines)
- if (! line.adjustable(slowdown_external_perimeters))
- time_total += line.time;
- return time_total;
- }
- float slow_down_maximum(bool slowdown_external_perimeters) {
- float time_total = 0.f;
- for (Line &line : lines) {
- if (line.adjustable(slowdown_external_perimeters)) {
- assert(line.time_max >= 0.f && line.time_max < FLT_MAX);
- line.slowdown = true;
- line.time = line.time_max;
- }
+ return time_total;
+ }
+ // Calculate the adjustable part of the total time.
+ float adjustable_time(bool slowdown_external_perimeters) {
+ float time_total = 0.f;
+ for (const CoolingLine &line : lines)
+ if (line.adjustable(slowdown_external_perimeters))
+ time_total += line.time;
+ return time_total;
+ }
+ // Calculate the non-adjustable part of the total time.
+ float non_adjustable_time(bool slowdown_external_perimeters) {
+ float time_total = 0.f;
+ for (const CoolingLine &line : lines)
+ if (! line.adjustable(slowdown_external_perimeters))
time_total += line.time;
+ return time_total;
+ }
+ // Slow down the adjustable extrusions to the minimum feedrate allowed for the current extruder material.
+ // Used by both proportional and non-proportional slow down.
+ float slowdown_to_minimum_feedrate(bool slowdown_external_perimeters) {
+ float time_total = 0.f;
+ for (CoolingLine &line : lines) {
+ if (line.adjustable(slowdown_external_perimeters)) {
+ assert(line.time_max >= 0.f && line.time_max < FLT_MAX);
+ line.slowdown = true;
+ line.time = line.time_max;
+ line.feedrate = line.length / line.time;
}
- return time_total;
+ time_total += line.time;
}
- float slow_down_proportional(float factor, bool slowdown_external_perimeters) {
- assert(factor >= 1.f);
- float time_total = 0.f;
- for (Line &line : lines) {
- if (line.adjustable(slowdown_external_perimeters)) {
- line.slowdown = true;
- line.time = std::min(line.time_max, line.time * factor);
- }
- time_total += line.time;
+ return time_total;
+ }
+ // Slow down each adjustable G-code line proportionally by a factor.
+ // Used by the proportional slow down.
+ float slow_down_proportional(float factor, bool slowdown_external_perimeters) {
+ assert(factor >= 1.f);
+ float time_total = 0.f;
+ for (CoolingLine &line : lines) {
+ if (line.adjustable(slowdown_external_perimeters)) {
+ line.slowdown = true;
+ line.time = std::min(line.time_max, line.time * factor);
+ line.feedrate = line.length / line.time;
}
- return time_total;
+ time_total += line.time;
}
+ return time_total;
+ }
- bool operator<(const Adjustment &rhs) const { return this->extruder_id < rhs.extruder_id; }
-
- struct Line
- {
- enum Type {
- TYPE_SET_TOOL = 1 << 0,
- TYPE_EXTRUDE_END = 1 << 1,
- TYPE_BRIDGE_FAN_START = 1 << 2,
- TYPE_BRIDGE_FAN_END = 1 << 3,
- TYPE_G0 = 1 << 4,
- TYPE_G1 = 1 << 5,
- TYPE_ADJUSTABLE = 1 << 6,
- TYPE_EXTERNAL_PERIMETER = 1 << 7,
- // The line sets a feedrate.
- TYPE_HAS_F = 1 << 8,
- TYPE_WIPE = 1 << 9,
- TYPE_G4 = 1 << 10,
- TYPE_G92 = 1 << 11,
- };
+ // Sort the lines, adjustable first, higher feedrate first.
+ // Used by non-proportional slow down.
+ void sort_lines_by_decreasing_feedrate() {
+ std::sort(lines.begin(), lines.end(), [](const CoolingLine &l1, const CoolingLine &l2) {
+ bool adj1 = l1.adjustable();
+ bool adj2 = l2.adjustable();
+ return (adj1 == adj2) ? l1.feedrate > l2.feedrate : adj1;
+ });
+ for (n_lines_adjustable = 0;
+ n_lines_adjustable < lines.size() && this->lines[n_lines_adjustable].adjustable();
+ ++ n_lines_adjustable);
+ time_non_adjustable = 0.f;
+ for (size_t i = n_lines_adjustable; i < lines.size(); ++ i)
+ time_non_adjustable += lines[i].time;
+ }
- Line(unsigned int type, size_t line_start, size_t line_end) :
- type(type), line_start(line_start), line_end(line_end),
- length(0.f), time(0.f), time_max(0.f), slowdown(false) {}
+ // Calculate the maximum time stretch when slowing down to min_feedrate.
+ // Slowdown to min_feedrate shall be allowed for this extruder's material.
+ // Used by non-proportional slow down.
+ float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) {
+ float time_stretch = 0.f;
+ assert(this->min_print_speed < min_feedrate + EPSILON);
+ for (size_t i = 0; i < n_lines_adjustable; ++ i) {
+ const CoolingLine &line = lines[i];
+ if (line.feedrate > min_feedrate)
+ time_stretch += line.time * (line.feedrate / min_feedrate - 1.f);
+ }
+ return time_stretch;
+ }
- bool adjustable(bool slowdown_external_perimeters) const {
- return (this->type & TYPE_ADJUSTABLE) &&
- (! (this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) &&
- this->time < this->time_max;
+ // Slow down all adjustable lines down to min_feedrate.
+ // Slowdown to min_feedrate shall be allowed for this extruder's material.
+ // Used by non-proportional slow down.
+ void slow_down_to_feedrate(float min_feedrate) {
+ assert(this->min_print_speed < min_feedrate + EPSILON);
+ for (size_t i = 0; i < n_lines_adjustable; ++ i) {
+ CoolingLine &line = lines[i];
+ if (line.feedrate > min_feedrate) {
+ line.time *= std::max(1.f, line.feedrate / min_feedrate);
+ line.feedrate = min_feedrate;
+ line.slowdown = true;
}
+ }
+ }
- size_t type;
- // Start of this line at the G-code snippet.
- size_t line_start;
- // End of this line at the G-code snippet.
- size_t line_end;
- // XY Euclidian length of this segment.
- float length;
- // Current duration of this segment.
- float time;
- // Maximum duration of this segment.
- float time_max;
- // If marked with the "slowdown" flag, the line has been slowed down.
- bool slowdown;
- };
+ // Extruder, for which the G-code will be adjusted.
+ unsigned int extruder_id = 0;
+ // Is the cooling slow down logic enabled for this extruder's material?
+ bool cooling_slow_down_enabled = false;
+ // Slow down the print down to min_print_speed if the total layer time is below slowdown_below_layer_time.
+ float slowdown_below_layer_time = 0.f;
+ // Minimum print speed allowed for this extruder.
+ float min_print_speed = 0.f;
- // Extruder, for which the G-code will be adjusted.
- unsigned int extruder_id;
- // Parsed lines.
- std::vector<Line> lines;
- };
- std::vector<Adjustment> adjustments(num_extruders, Adjustment());
- for (size_t i = 0; i < num_extruders; ++ i)
- adjustments[i].extruder_id = extruders[i].id();
- const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix();
- // Parse the layer G-code for the moves, which could be adjusted.
+ // Parsed lines.
+ std::vector<CoolingLine> lines;
+ // The following two values are set by sort_lines_by_decreasing_feedrate():
+ // Number of adjustable lines, at the start of lines.
+ size_t n_lines_adjustable = 0;
+ // Non-adjustable time of lines starting with n_lines_adjustable.
+ float time_non_adjustable = 0;
+ // Current total time for this extruder.
+ float time_total = 0;
+ // Maximum time for this extruder, when the maximum slow down is applied.
+ float time_maximum = 0;
+
+ // Temporaries for processing the slow down. Both thresholds go from 0 to n_lines_adjustable.
+ size_t idx_line_begin = 0;
+ size_t idx_line_end = 0;
+};
+
+std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_id)
+{
+ std::vector<PerExtruderAdjustments> per_extruder_adjustments = this->parse_layer_gcode(gcode, m_current_pos);
+ float layer_time_stretched = this->calculate_layer_slowdown(per_extruder_adjustments);
+ return this->apply_layer_cooldown(gcode, layer_id, layer_time_stretched, per_extruder_adjustments);
+}
+
+// Parse the layer G-code for the moves, which could be adjusted.
+// Return the list of parsed lines, bucketed by an extruder.
+std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector<float> &current_pos) const
+{
+ const FullPrintConfig &config = m_gcodegen.config();
+ const std::vector<Extruder> &extruders = m_gcodegen.writer().extruders();
+ unsigned int num_extruders = 0;
+ for (const Extruder &ex : extruders)
+ num_extruders = std::max(ex.id() + 1, num_extruders);
+
+ std::vector<PerExtruderAdjustments> per_extruder_adjustments(extruders.size());
+ std::vector<size_t> map_extruder_to_per_extruder_adjustment(num_extruders, 0);
+ for (size_t i = 0; i < extruders.size(); ++ i) {
+ PerExtruderAdjustments &adj = per_extruder_adjustments[i];
+ unsigned int extruder_id = extruders[i].id();
+ adj.extruder_id = extruder_id;
+ adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id);
+ adj.slowdown_below_layer_time = config.slowdown_below_layer_time.get_at(extruder_id);
+ adj.min_print_speed = config.min_print_speed.get_at(extruder_id);
+ map_extruder_to_per_extruder_adjustment[extruder_id] = i;
+ }
+
+ const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix();
+ unsigned int current_extruder = m_current_extruder;
+ PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
+ const char *line_start = gcode.c_str();
+ const char *line_end = line_start;
+ const char extrusion_axis = config.get_extrusion_axis()[0];
+ // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
+ // for a sequence of extrusion moves.
+ size_t active_speed_modifier = size_t(-1);
+
+ for (; *line_start != 0; line_start = line_end)
{
- float min_print_speed = float(EXTRUDER_CONFIG(min_print_speed));
- auto adjustment = std::lower_bound(adjustments.begin(), adjustments.end(), Adjustment(m_current_extruder));
- unsigned int initial_extruder = m_current_extruder;
- const char *line_start = gcode.c_str();
- const char *line_end = line_start;
- const char extrusion_axis = config.get_extrusion_axis()[0];
- // Index of an existing Adjustment::Line of the current adjustment, which holds the feedrate setting command
- // for a sequence of extrusion moves.
- size_t active_speed_modifier = size_t(-1);
- for (; *line_start != 0; line_start = line_end) {
- while (*line_end != '\n' && *line_end != 0)
- ++ line_end;
- // sline will not contain the trailing '\n'.
- std::string sline(line_start, line_end);
- // Adjustment::Line will contain the trailing '\n'.
- if (*line_end == '\n')
- ++ line_end;
- Adjustment::Line line(0, line_start - gcode.c_str(), line_end - gcode.c_str());
- if (boost::starts_with(sline, "G0 "))
- line.type = Adjustment::Line::TYPE_G0;
- else if (boost::starts_with(sline, "G1 "))
- line.type = Adjustment::Line::TYPE_G1;
- else if (boost::starts_with(sline, "G92 "))
- line.type = Adjustment::Line::TYPE_G92;
- if (line.type) {
- // G0, G1 or G92
- // Parse the G-code line.
- std::vector<float> new_pos(m_current_pos);
- const char *c = sline.data() + 3;
- for (;;) {
- // Skip whitespaces.
- for (; *c == ' ' || *c == '\t'; ++ c);
- if (*c == 0 || *c == ';')
- break;
- // Parse the axis.
- size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') :
- (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
- if (axis != size_t(-1)) {
- new_pos[axis] = float(atof(++c));
- if (axis == 4) {
- // Convert mm/min to mm/sec.
- new_pos[4] /= 60.f;
- if ((line.type & Adjustment::Line::TYPE_G92) == 0)
- // This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls.
- line.type |= Adjustment::Line::TYPE_HAS_F;
- }
- }
- // Skip this word.
- for (; *c != ' ' && *c != '\t' && *c != 0; ++ c);
+ while (*line_end != '\n' && *line_end != 0)
+ ++ line_end;
+ // sline will not contain the trailing '\n'.
+ std::string sline(line_start, line_end);
+ // CoolingLine will contain the trailing '\n'.
+ if (*line_end == '\n')
+ ++ line_end;
+ CoolingLine line(0, line_start - gcode.c_str(), line_end - gcode.c_str());
+ if (boost::starts_with(sline, "G0 "))
+ line.type = CoolingLine::TYPE_G0;
+ else if (boost::starts_with(sline, "G1 "))
+ line.type = CoolingLine::TYPE_G1;
+ else if (boost::starts_with(sline, "G92 "))
+ line.type = CoolingLine::TYPE_G92;
+ if (line.type) {
+ // G0, G1 or G92
+ // Parse the G-code line.
+ std::vector<float> new_pos(current_pos);
+ const char *c = sline.data() + 3;
+ for (;;) {
+ // Skip whitespaces.
+ for (; *c == ' ' || *c == '\t'; ++ c);
+ if (*c == 0 || *c == ';')
+ break;
+ // Parse the axis.
+ size_t axis = (*c >= 'X' && *c <= 'Z') ? (*c - 'X') :
+ (*c == extrusion_axis) ? 3 : (*c == 'F') ? 4 : size_t(-1);
+ if (axis != size_t(-1)) {
+ new_pos[axis] = float(atof(++c));
+ if (axis == 4) {
+ // Convert mm/min to mm/sec.
+ new_pos[4] /= 60.f;
+ if ((line.type & CoolingLine::TYPE_G92) == 0)
+ // This is G0 or G1 line and it sets the feedrate. This mark is used for reducing the duplicate F calls.
+ line.type |= CoolingLine::TYPE_HAS_F;
+ }
}
- bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER");
- bool wipe = boost::contains(sline, ";_WIPE");
- if (external_perimeter)
- line.type |= Adjustment::Line::TYPE_EXTERNAL_PERIMETER;
- if (wipe)
- line.type |= Adjustment::Line::TYPE_WIPE;
- if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
- line.type |= Adjustment::Line::TYPE_ADJUSTABLE;
- active_speed_modifier = adjustment->lines.size();
+ // Skip this word.
+ for (; *c != ' ' && *c != '\t' && *c != 0; ++ c);
+ }
+ bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER");
+ bool wipe = boost::contains(sline, ";_WIPE");
+ if (external_perimeter)
+ line.type |= CoolingLine::TYPE_EXTERNAL_PERIMETER;
+ if (wipe)
+ line.type |= CoolingLine::TYPE_WIPE;
+ if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
+ line.type |= CoolingLine::TYPE_ADJUSTABLE;
+ active_speed_modifier = adjustment->lines.size();
+ }
+ if ((line.type & CoolingLine::TYPE_G92) == 0) {
+ // G0 or G1. Calculate the duration.
+ if (config.use_relative_e_distances.value)
+ // Reset extruder accumulator.
+ current_pos[3] = 0.f;
+ float dif[4];
+ for (size_t i = 0; i < 4; ++ i)
+ dif[i] = new_pos[i] - current_pos[i];
+ float dxy2 = dif[0] * dif[0] + dif[1] * dif[1];
+ float dxyz2 = dxy2 + dif[2] * dif[2];
+ if (dxyz2 > 0.f) {
+ // Movement in xyz, calculate time from the xyz Euclidian distance.
+ line.length = sqrt(dxyz2);
+ } else if (std::abs(dif[3]) > 0.f) {
+ // Movement in the extruder axis.
+ line.length = std::abs(dif[3]);
}
- if ((line.type & Adjustment::Line::TYPE_G92) == 0) {
- // G0 or G1. Calculate the duration.
- if (config.use_relative_e_distances.value)
- // Reset extruder accumulator.
- m_current_pos[3] = 0.f;
- float dif[4];
- for (size_t i = 0; i < 4; ++ i)
- dif[i] = new_pos[i] - m_current_pos[i];
- float dxy2 = dif[0] * dif[0] + dif[1] * dif[1];
- float dxyz2 = dxy2 + dif[2] * dif[2];
- if (dxyz2 > 0.f) {
- // Movement in xyz, calculate time from the xyz Euclidian distance.
- line.length = sqrt(dxyz2);
- } else if (std::abs(dif[3]) > 0.f) {
- // Movement in the extruder axis.
- line.length = std::abs(dif[3]);
+ line.feedrate = new_pos[4];
+ assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f);
+ if (line.length > 0)
+ line.time = line.length / line.feedrate;
+ line.time_max = line.time;
+ if ((line.type & CoolingLine::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
+ line.time_max = (adjustment->min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->min_print_speed);
+ if (active_speed_modifier < adjustment->lines.size() && (line.type & CoolingLine::TYPE_G1)) {
+ // Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry.
+ assert((line.type & CoolingLine::TYPE_HAS_F) == 0);
+ CoolingLine &sm = adjustment->lines[active_speed_modifier];
+ assert(sm.feedrate > 0.f);
+ sm.length += line.length;
+ sm.time += line.time;
+ if (sm.time_max != FLT_MAX) {
+ if (line.time_max == FLT_MAX)
+ sm.time_max = FLT_MAX;
+ else
+ sm.time_max += line.time_max;
}
- if (line.length > 0)
- line.time = line.length / new_pos[4]; // current F
- line.time_max = line.time;
- if ((line.type & Adjustment::Line::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
- line.time_max = (min_print_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / min_print_speed);
- if (active_speed_modifier < adjustment->lines.size() && (line.type & Adjustment::Line::TYPE_G1)) {
- // Inside the ";_EXTRUDE_SET_SPEED" blocks, there must not be a G1 Fxx entry.
- assert((line.type & Adjustment::Line::TYPE_HAS_F) == 0);
- Adjustment::Line &sm = adjustment->lines[active_speed_modifier];
- sm.length += line.length;
- sm.time += line.time;
- if (sm.time_max != FLT_MAX) {
- if (line.time_max == FLT_MAX)
- sm.time_max = FLT_MAX;
- else
- sm.time_max += line.time_max;
- }
- // Don't store this line.
- line.type = 0;
- }
- }
- m_current_pos = std::move(new_pos);
- } else if (boost::starts_with(sline, ";_EXTRUDE_END")) {
- line.type = Adjustment::Line::TYPE_EXTRUDE_END;
- active_speed_modifier = size_t(-1);
- } else if (boost::starts_with(sline, toolchange_prefix)) {
- // Switch the tool.
- line.type = Adjustment::Line::TYPE_SET_TOOL;
- unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size());
- if (new_extruder != m_current_extruder) {
- m_current_extruder = new_extruder;
- min_print_speed = float(EXTRUDER_CONFIG(min_print_speed));
- adjustment = std::lower_bound(adjustments.begin(), adjustments.end(), Adjustment(m_current_extruder));
+ // Don't store this line.
+ line.type = 0;
}
- } else if (boost::starts_with(sline, ";_BRIDGE_FAN_START")) {
- line.type = Adjustment::Line::TYPE_BRIDGE_FAN_START;
- } else if (boost::starts_with(sline, ";_BRIDGE_FAN_END")) {
- line.type = Adjustment::Line::TYPE_BRIDGE_FAN_END;
- } else if (boost::starts_with(sline, "G4 ")) {
- // Parse the wait time.
- line.type = Adjustment::Line::TYPE_G4;
- size_t pos_S = sline.find('S', 3);
- size_t pos_P = sline.find('P', 3);
- line.time = line.time_max = float(
- (pos_S > 0) ? atof(sline.c_str() + pos_S + 1) :
- (pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.);
}
- if (line.type != 0)
- adjustment->lines.emplace_back(std::move(line));
- }
- m_current_extruder = initial_extruder;
+ current_pos = std::move(new_pos);
+ } else if (boost::starts_with(sline, ";_EXTRUDE_END")) {
+ line.type = CoolingLine::TYPE_EXTRUDE_END;
+ active_speed_modifier = size_t(-1);
+ } else if (boost::starts_with(sline, toolchange_prefix)) {
+ // Switch the tool.
+ line.type = CoolingLine::TYPE_SET_TOOL;
+ unsigned int new_extruder = (unsigned int)atoi(sline.c_str() + toolchange_prefix.size());
+ if (new_extruder != current_extruder) {
+ current_extruder = new_extruder;
+ adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
+ }
+ } else if (boost::starts_with(sline, ";_BRIDGE_FAN_START")) {
+ line.type = CoolingLine::TYPE_BRIDGE_FAN_START;
+ } else if (boost::starts_with(sline, ";_BRIDGE_FAN_END")) {
+ line.type = CoolingLine::TYPE_BRIDGE_FAN_END;
+ } else if (boost::starts_with(sline, "G4 ")) {
+ // Parse the wait time.
+ line.type = CoolingLine::TYPE_G4;
+ size_t pos_S = sline.find('S', 3);
+ size_t pos_P = sline.find('P', 3);
+ line.time = line.time_max = float(
+ (pos_S > 0) ? atof(sline.c_str() + pos_S + 1) :
+ (pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.);
+ }
+ if (line.type != 0)
+ adjustment->lines.emplace_back(std::move(line));
}
- // Sort the extruders by the increasing slowdown_below_layer_time.
- std::vector<size_t> by_slowdown_layer_time;
- by_slowdown_layer_time.reserve(num_extruders);
+ return per_extruder_adjustments;
+}
+
+// Slow down an extruder range proportionally down to slowdown_below_layer_time.
+// Return the total time for the complete layer.
+static inline float extruder_range_slow_down_proportional(
+ std::vector<PerExtruderAdjustments*>::iterator it_begin,
+ std::vector<PerExtruderAdjustments*>::iterator it_end,
+ // Elapsed time for the extruders already processed.
+ float elapsed_time_total0,
+ // Initial total elapsed time before slow down.
+ float elapsed_time_before_slowdown,
+ // Target time for the complete layer (all extruders applied).
+ float slowdown_below_layer_time)
+{
+ // Total layer time after the slow down has been applied.
+ float total_after_slowdown = elapsed_time_before_slowdown;
+ // Now decide, whether the external perimeters shall be slowed down as well.
+ float max_time_nep = elapsed_time_total0;
+ for (auto it = it_begin; it != it_end; ++ it)
+ max_time_nep += (*it)->maximum_time_after_slowdown(false);
+ if (max_time_nep > slowdown_below_layer_time) {
+ // It is sufficient to slow down the non-external perimeter moves to reach the target layer time.
+ // Slow down the non-external perimeters proportionally.
+ float non_adjustable_time = elapsed_time_total0;
+ for (auto it = it_begin; it != it_end; ++ it)
+ non_adjustable_time += (*it)->non_adjustable_time(false);
+ // The following step is a linear programming task due to the minimum movement speeds of the print moves.
+ // Run maximum 5 iterations until a good enough approximation is reached.
+ for (size_t iter = 0; iter < 5; ++ iter) {
+ float factor = (slowdown_below_layer_time - non_adjustable_time) / (total_after_slowdown - non_adjustable_time);
+ assert(factor > 1.f);
+ total_after_slowdown = elapsed_time_total0;
+ for (auto it = it_begin; it != it_end; ++ it)
+ total_after_slowdown += (*it)->slow_down_proportional(factor, false);
+ if (total_after_slowdown > 0.95f * slowdown_below_layer_time)
+ break;
+ }
+ } else {
+ // Slow down everything. First slow down the non-external perimeters to maximum.
+ for (auto it = it_begin; it != it_end; ++ it)
+ (*it)->slowdown_to_minimum_feedrate(false);
+ // Slow down the external perimeters proportionally.
+ float non_adjustable_time = elapsed_time_total0;
+ for (auto it = it_begin; it != it_end; ++ it)
+ non_adjustable_time += (*it)->non_adjustable_time(true);
+ for (size_t iter = 0; iter < 5; ++ iter) {
+ float factor = (slowdown_below_layer_time - non_adjustable_time) / (total_after_slowdown - non_adjustable_time);
+ assert(factor > 1.f);
+ total_after_slowdown = elapsed_time_total0;
+ for (auto it = it_begin; it != it_end; ++ it)
+ total_after_slowdown += (*it)->slow_down_proportional(factor, true);
+ if (total_after_slowdown > 0.95f * slowdown_below_layer_time)
+ break;
+ }
+ }
+ return total_after_slowdown;
+}
+
+// Slow down an extruder range to slowdown_below_layer_time.
+// Return the total time for the complete layer.
+static inline void extruder_range_slow_down_non_proportional(
+ std::vector<PerExtruderAdjustments*>::iterator it_begin,
+ std::vector<PerExtruderAdjustments*>::iterator it_end,
+ float time_stretch)
+{
+ // Slow down. Try to equalize the feedrates.
+ std::vector<PerExtruderAdjustments*> by_min_print_speed(it_begin, it_end);
+ // Find the next highest adjustable feedrate among the extruders.
+ float feedrate = 0;
+ for (PerExtruderAdjustments *adj : by_min_print_speed) {
+ adj->idx_line_begin = 0;
+ adj->idx_line_end = 0;
+ assert(adj->idx_line_begin < adj->n_lines_adjustable);
+ if (adj->lines[adj->idx_line_begin].feedrate > feedrate)
+ feedrate = adj->lines[adj->idx_line_begin].feedrate;
+ }
+ assert(feedrate > 0.f);
+ // Sort by min_print_speed, maximum speed first.
+ std::sort(by_min_print_speed.begin(), by_min_print_speed.end(),
+ [](const PerExtruderAdjustments *p1, const PerExtruderAdjustments *p2){ return p1->min_print_speed > p2->min_print_speed; });
+ // Slow down, fast moves first.
+ for (;;) {
+ // For each extruder, find the span of lines with a feedrate close to feedrate.
+ for (PerExtruderAdjustments *adj : by_min_print_speed) {
+ for (adj->idx_line_end = adj->idx_line_begin;
+ adj->idx_line_end < adj->n_lines_adjustable && adj->lines[adj->idx_line_end].feedrate > feedrate - EPSILON;
+ ++ adj->idx_line_end) ;
+ }
+ // Find the next highest adjustable feedrate among the extruders.
+ float feedrate_next = 0.f;
+ for (PerExtruderAdjustments *adj : by_min_print_speed)
+ if (adj->idx_line_end < adj->n_lines_adjustable && adj->lines[adj->idx_line_end].feedrate > feedrate_next)
+ feedrate_next = adj->lines[adj->idx_line_end].feedrate;
+ // Slow down, limited by max(feedrate_next, min_print_speed).
+ for (auto adj = by_min_print_speed.begin(); adj != by_min_print_speed.end();) {
+ // Slow down at most by time_stretch.
+ if ((*adj)->min_print_speed == 0.f) {
+ // All the adjustable speeds are now lowered to the same speed,
+ // and the minimum speed is set to zero.
+ float time_adjustable = 0.f;
+ for (auto it = adj; it != by_min_print_speed.end(); ++ it)
+ time_adjustable += (*it)->adjustable_time(true);
+ float rate = (time_adjustable + time_stretch) / time_adjustable;
+ for (auto it = adj; it != by_min_print_speed.end(); ++ it)
+ (*it)->slow_down_proportional(rate, true);
+ return;
+ } else {
+ float feedrate_limit = std::max(feedrate_next, (*adj)->min_print_speed);
+ bool done = false;
+ float time_stretch_max = 0.f;
+ for (auto it = adj; it != by_min_print_speed.end(); ++ it)
+ time_stretch_max += (*it)->time_stretch_when_slowing_down_to_feedrate(feedrate_limit);
+ if (time_stretch_max >= time_stretch) {
+ feedrate_limit = feedrate - (feedrate - feedrate_limit) * time_stretch / time_stretch_max;
+ done = true;
+ } else
+ time_stretch -= time_stretch_max;
+ for (auto it = adj; it != by_min_print_speed.end(); ++ it)
+ (*it)->slow_down_to_feedrate(feedrate_limit);
+ if (done)
+ return;
+ }
+ // Skip the other extruders with nearly the same min_print_speed, as they have been processed already.
+ auto next = adj;
+ for (++ next; next != by_min_print_speed.end() && (*next)->min_print_speed > (*adj)->min_print_speed - EPSILON; ++ next);
+ adj = next;
+ }
+ if (feedrate_next == 0.f)
+ // There are no other extrusions available for slow down.
+ break;
+ for (PerExtruderAdjustments *adj : by_min_print_speed) {
+ adj->idx_line_begin = adj->idx_line_end;
+ feedrate = feedrate_next;
+ }
+ }
+}
+
+// Calculate slow down for all the extruders.
+float CoolingBuffer::calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &per_extruder_adjustments)
+{
+ // Sort the extruders by an increasing slowdown_below_layer_time.
+ // The layers with a lower slowdown_below_layer_time are slowed down
+ // together with all the other layers with slowdown_below_layer_time above.
+ std::vector<PerExtruderAdjustments*> by_slowdown_time;
+ by_slowdown_time.reserve(per_extruder_adjustments.size());
// Only insert entries, which are adjustable (have cooling enabled and non-zero stretchable time).
// Collect total print time of non-adjustable extruders.
- float elapsed_time_total_non_adjustable = 0.f;
- for (size_t i = 0; i < num_extruders; ++ i) {
- if (config.cooling.get_at(extruders[i].id()))
- by_slowdown_layer_time.emplace_back(i);
- else
- elapsed_time_total_non_adjustable += adjustments[i].elapsed_time_total();
+ float elapsed_time_total0 = 0.f;
+ for (PerExtruderAdjustments &adj : per_extruder_adjustments) {
+ // Curren total time for this extruder.
+ adj.time_total = adj.elapsed_time_total();
+ // Maximum time for this extruder, when all extrusion moves are slowed down to min_extrusion_speed.
+ adj.time_maximum = adj.maximum_time_after_slowdown(true);
+ if (adj.cooling_slow_down_enabled && adj.lines.size() > 0) {
+ by_slowdown_time.emplace_back(&adj);
+ if (! m_cooling_logic_proportional)
+ // sorts the lines, also sets adj.time_non_adjustable
+ adj.sort_lines_by_decreasing_feedrate();
+ } else
+ elapsed_time_total0 += adj.elapsed_time_total();
}
- std::sort(by_slowdown_layer_time.begin(), by_slowdown_layer_time.end(),
- [&config, &extruders](const size_t idx1, const size_t idx2){
- return config.slowdown_below_layer_time.get_at(extruders[idx1].id()) <
- config.slowdown_below_layer_time.get_at(extruders[idx2].id());
- });
+ std::sort(by_slowdown_time.begin(), by_slowdown_time.end(),
+ [](const PerExtruderAdjustments *adj1, const PerExtruderAdjustments *adj2)
+ { return adj1->slowdown_below_layer_time < adj2->slowdown_below_layer_time; });
- // Elapsed time after adjustment.
- float elapsed_time_total = 0.f;
- {
- // Elapsed time for the already adjusted extruders.
- float elapsed_time_total0 = elapsed_time_total_non_adjustable;
- for (size_t i_by_slowdown_layer_time = 0; i_by_slowdown_layer_time < by_slowdown_layer_time.size(); ++ i_by_slowdown_layer_time) {
- // Idx in adjustments.
- size_t idx = by_slowdown_layer_time[i_by_slowdown_layer_time];
- // Macro to sum or adjust all sections starting with i_by_slowdown_layer_time.
- #define FORALL_UNPROCESSED(ACCUMULATOR, ACTION) \
- ACCUMULATOR = elapsed_time_total0;\
- for (size_t j = i_by_slowdown_layer_time; j < by_slowdown_layer_time.size(); ++ j) \
- ACCUMULATOR += adjustments[by_slowdown_layer_time[j]].ACTION
- // Calculate the current adjusted elapsed_time_total over the non-finalized extruders.
- float total;
- FORALL_UNPROCESSED(total, elapsed_time_total());
- float slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(adjustments[idx].extruder_id)) * 1.001f;
- if (total > slowdown_below_layer_time) {
- // The current total time is above the minimum threshold of the rest of the extruders, don't adjust anything.
+ for (auto cur_begin = by_slowdown_time.begin(); cur_begin != by_slowdown_time.end(); ++ cur_begin) {
+ PerExtruderAdjustments &adj = *(*cur_begin);
+ // Calculate the current adjusted elapsed_time_total over the non-finalized extruders.
+ float total = elapsed_time_total0;
+ for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
+ total += (*it)->time_total;
+ float slowdown_below_layer_time = adj.slowdown_below_layer_time * 1.001f;
+ if (total > slowdown_below_layer_time) {
+ // The current total time is above the minimum threshold of the rest of the extruders, don't adjust anything.
+ } else {
+ // Adjust this and all the following (higher config.slowdown_below_layer_time) extruders.
+ // Sum maximum slow down time as if everything was slowed down including the external perimeters.
+ float max_time = elapsed_time_total0;
+ for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
+ max_time += (*it)->time_maximum;
+ if (max_time > slowdown_below_layer_time) {
+ if (m_cooling_logic_proportional)
+ extruder_range_slow_down_proportional(cur_begin, by_slowdown_time.end(), elapsed_time_total0, total, slowdown_below_layer_time);
+ else
+ extruder_range_slow_down_non_proportional(cur_begin, by_slowdown_time.end(), slowdown_below_layer_time - total);
} else {
- // Adjust this and all the following (higher config.slowdown_below_layer_time) extruders.
- // Sum maximum slow down time as if everything was slowed down including the external perimeters.
- float max_time;
- FORALL_UNPROCESSED(max_time, maximum_time(true));
- if (max_time > slowdown_below_layer_time) {
- // By slowing every possible movement, the layer time could be reached. Now decide
- // whether the external perimeters shall be slowed down as well.
- float max_time_nep;
- FORALL_UNPROCESSED(max_time_nep, maximum_time(false));
- if (max_time_nep > slowdown_below_layer_time) {
- // It is sufficient to slow down the non-external perimeter moves to reach the target layer time.
- // Slow down the non-external perimeters proportionally.
- float non_adjustable_time;
- FORALL_UNPROCESSED(non_adjustable_time, non_adjustable_time(false));
- // The following step is a linear programming task due to the minimum movement speeds of the print moves.
- // Run maximum 5 iterations until a good enough approximation is reached.
- for (size_t iter = 0; iter < 5; ++ iter) {
- float factor = (slowdown_below_layer_time - non_adjustable_time) / (total - non_adjustable_time);
- assert(factor > 1.f);
- FORALL_UNPROCESSED(total, slow_down_proportional(factor, false));
- if (total > 0.95f * slowdown_below_layer_time)
- break;
- }
- } else {
- // Slow down everything. First slow down the non-external perimeters to maximum.
- FORALL_UNPROCESSED(total, slow_down_maximum(false));
- // Slow down the external perimeters proportionally.
- float non_adjustable_time;
- FORALL_UNPROCESSED(non_adjustable_time, non_adjustable_time(true));
- for (size_t iter = 0; iter < 5; ++ iter) {
- float factor = (slowdown_below_layer_time - non_adjustable_time) / (total - non_adjustable_time);
- assert(factor > 1.f);
- FORALL_UNPROCESSED(total, slow_down_proportional(factor, true));
- if (total > 0.95f * slowdown_below_layer_time)
- break;
- }
- }
- } else {
- // Slow down to maximum possible.
- FORALL_UNPROCESSED(total, slow_down_maximum(true));
- }
+ // Slow down to maximum possible.
+ for (auto it = cur_begin; it != by_slowdown_time.end(); ++ it)
+ (*it)->slowdown_to_minimum_feedrate(true);
}
- #undef FORALL_UNPROCESSED
- // Sum the final elapsed time for all extruders up to i_by_slowdown_layer_time.
- if (i_by_slowdown_layer_time + 1 == by_slowdown_layer_time.size())
- // Optimization for single extruder prints.
- elapsed_time_total0 = total;
- else
- elapsed_time_total0 += adjustments[idx].elapsed_time_total();
}
- elapsed_time_total = elapsed_time_total0;
+ elapsed_time_total0 += adj.elapsed_time_total();
}
- // Transform the G-code.
- // First sort the adjustment lines by their position in the source G-code.
- std::vector<const Adjustment::Line*> lines;
+ return elapsed_time_total0;
+}
+
+// Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed.
+// Returns the adjusted G-code.
+std::string CoolingBuffer::apply_layer_cooldown(
+ // Source G-code for the current layer.
+ const std::string &gcode,
+ // ID of the current layer, used to disable fan for the first n layers.
+ size_t layer_id,
+ // Total time of this layer after slow down, used to control the fan.
+ float layer_time,
+ // Per extruder list of G-code lines and their cool down attributes.
+ std::vector<PerExtruderAdjustments> &per_extruder_adjustments)
+{
+ // First sort the adjustment lines by of multiple extruders by their position in the source G-code.
+ std::vector<const CoolingLine*> lines;
{
size_t n_lines = 0;
- for (const Adjustment &adj : adjustments)
+ for (const PerExtruderAdjustments &adj : per_extruder_adjustments)
n_lines += adj.lines.size();
lines.reserve(n_lines);
- for (const Adjustment &adj : adjustments)
- for (const Adjustment::Line &line : adj.lines)
+ for (const PerExtruderAdjustments &adj : per_extruder_adjustments)
+ for (const CoolingLine &line : adj.lines)
lines.emplace_back(&line);
- std::sort(lines.begin(), lines.end(), [](const Adjustment::Line *ln1, const Adjustment::Line *ln2) { return ln1->line_start < ln2->line_start; } );
+ std::sort(lines.begin(), lines.end(), [](const CoolingLine *ln1, const CoolingLine *ln2) { return ln1->line_start < ln2->line_start; } );
}
// Second generate the adjusted G-code.
std::string new_gcode;
@@ -390,8 +610,9 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
int fan_speed = -1;
bool bridge_fan_control = false;
int bridge_fan_speed = 0;
- auto change_extruder_set_fan = [ this, layer_id, elapsed_time_total, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed ]() {
+ auto change_extruder_set_fan = [ this, layer_id, layer_time, &new_gcode, &fan_speed, &bridge_fan_control, &bridge_fan_speed ]() {
const FullPrintConfig &config = m_gcodegen.config();
+#define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder)
int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed);
int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0;
if (layer_id >= EXTRUDER_CONFIG(disable_fan_first_layers)) {
@@ -399,17 +620,18 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time));
float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time));
if (EXTRUDER_CONFIG(cooling)) {
- if (elapsed_time_total < slowdown_below_layer_time) {
+ if (layer_time < slowdown_below_layer_time) {
// Layer time very short. Enable the fan to a full throttle.
fan_speed_new = max_fan_speed;
- } else if (elapsed_time_total < fan_below_layer_time) {
+ } else if (layer_time < fan_below_layer_time) {
// Layer time quite short. Enable the fan proportionally according to the current layer time.
- assert(elapsed_time_total >= slowdown_below_layer_time);
- double t = (elapsed_time_total - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time);
+ assert(layer_time >= slowdown_below_layer_time);
+ double t = (layer_time - slowdown_below_layer_time) / (fan_below_layer_time - slowdown_below_layer_time);
fan_speed_new = int(floor(t * min_fan_speed + (1. - t) * max_fan_speed) + 0.5);
}
}
bridge_fan_speed = EXTRUDER_CONFIG(bridge_fan_speed);
+#undef EXTRUDER_CONFIG
bridge_fan_control = bridge_fan_speed > fan_speed_new;
} else {
bridge_fan_control = false;
@@ -421,49 +643,50 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
new_gcode += m_gcodegen.writer().set_fan(fan_speed);
}
};
- change_extruder_set_fan();
- const char *pos = gcode.c_str();
- int current_feedrate = 0;
- for (const Adjustment::Line *line : lines) {
+ const char *pos = gcode.c_str();
+ int current_feedrate = 0;
+ const std::string toolchange_prefix = m_gcodegen.writer().toolchange_prefix();
+ change_extruder_set_fan();
+ for (const CoolingLine *line : lines) {
const char *line_start = gcode.c_str() + line->line_start;
const char *line_end = gcode.c_str() + line->line_end;
if (line_start > pos)
new_gcode.append(pos, line_start - pos);
- if (line->type & Adjustment::Line::TYPE_SET_TOOL) {
+ if (line->type & CoolingLine::TYPE_SET_TOOL) {
unsigned int new_extruder = (unsigned int)atoi(line_start + toolchange_prefix.size());
if (new_extruder != m_current_extruder) {
m_current_extruder = new_extruder;
change_extruder_set_fan();
}
new_gcode.append(line_start, line_end - line_start);
- } else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_START) {
+ } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_START) {
if (bridge_fan_control)
new_gcode += m_gcodegen.writer().set_fan(bridge_fan_speed, true);
- } else if (line->type & Adjustment::Line::TYPE_BRIDGE_FAN_END) {
+ } else if (line->type & CoolingLine::TYPE_BRIDGE_FAN_END) {
if (bridge_fan_control)
new_gcode += m_gcodegen.writer().set_fan(fan_speed, true);
- } else if (line->type & Adjustment::Line::TYPE_EXTRUDE_END) {
+ } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) {
// Just remove this comment.
- } else if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE | Adjustment::Line::TYPE_HAS_F)) {
+ } else if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE | CoolingLine::TYPE_HAS_F)) {
// Find the start of a comment, or roll to the end of line.
- const char *end = line_start;
- for (; end < line_end && *end != ';'; ++ end);
- // Find the 'F' word.
+ const char *end = line_start;
+ for (; end < line_end && *end != ';'; ++ end);
+ // Find the 'F' word.
const char *fpos = strstr(line_start + 2, " F") + 2;
int new_feedrate = current_feedrate;
bool modify = false;
assert(fpos != nullptr);
if (line->slowdown) {
modify = true;
- new_feedrate = int(floor(60. * (line->length / line->time) + 0.5));
+ new_feedrate = int(floor(60. * line->feedrate + 0.5));
} else {
new_feedrate = atoi(fpos);
if (new_feedrate != current_feedrate) {
// Append the line without the comment.
new_gcode.append(line_start, end - line_start);
current_feedrate = new_feedrate;
- } else if ((line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) || line->length == 0.) {
+ } else if ((line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE)) || line->length == 0.) {
// Feedrate does not change and this line does not move the print head. Skip the complete G-code line including the G-code comment.
end = line_end;
} else {
@@ -488,7 +711,7 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
new_gcode.append(line_start, f - line_start + 1);
}
// Skip the non-whitespaces of the F parameter up the comment or end of line.
- for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++fpos);
+ for (; fpos != end && *fpos != ' ' && *fpos != ';' && *fpos != '\n'; ++fpos);
// Append the rest of the line without the comment.
if (fpos < end)
new_gcode.append(fpos, end - fpos);
@@ -497,22 +720,22 @@ std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_
}
// Process the rest of the line.
if (end < line_end) {
- if (line->type & (Adjustment::Line::TYPE_ADJUSTABLE | Adjustment::Line::TYPE_EXTERNAL_PERIMETER | Adjustment::Line::TYPE_WIPE)) {
- // Process comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
- std::string comment(end, line_end);
- boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
- if (line->type & Adjustment::Line::TYPE_EXTERNAL_PERIMETER)
+ if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE)) {
+ // Process comments, remove ";_EXTRUDE_SET_SPEED", ";_EXTERNAL_PERIMETER", ";_WIPE"
+ std::string comment(end, line_end);
+ boost::replace_all(comment, ";_EXTRUDE_SET_SPEED", "");
+ if (line->type & CoolingLine::TYPE_EXTERNAL_PERIMETER)
boost::replace_all(comment, ";_EXTERNAL_PERIMETER", "");
- if (line->type & Adjustment::Line::TYPE_WIPE)
+ if (line->type & CoolingLine::TYPE_WIPE)
boost::replace_all(comment, ";_WIPE", "");
- new_gcode += comment;
- } else {
- // Just attach the rest of the source line.
- new_gcode.append(end, line_end - end);
- }
+ new_gcode += comment;
+ } else {
+ // Just attach the rest of the source line.
+ new_gcode.append(end, line_end - end);
+ }
}
} else {
- new_gcode.append(line_start, line_end - line_start);
+ new_gcode.append(line_start, line_end - line_start);
}
pos = line_end;
}
diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.hpp b/xs/src/libslic3r/GCode/CoolingBuffer.hpp
index f85c470b3..bf4b082e2 100644
--- a/xs/src/libslic3r/GCode/CoolingBuffer.hpp
+++ b/xs/src/libslic3r/GCode/CoolingBuffer.hpp
@@ -9,13 +9,17 @@ namespace Slic3r {
class GCode;
class Layer;
-
-/*
-A standalone G-code filter, to control cooling of the print.
-The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
-and the print is modified to stretch over a minimum layer time.
-*/
-
+class PerExtruderAdjustments;
+
+// A standalone G-code filter, to control cooling of the print.
+// The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
+// and the print is modified to stretch over a minimum layer time.
+//
+// The simple it sounds, the actual implementation is significantly more complex.
+// Namely, for a multi-extruder print, each material may require a different cooling logic.
+// For example, some materials may not like to print too slowly, while with some materials
+// we may slow down significantly.
+//
class CoolingBuffer {
public:
CoolingBuffer(GCode &gcodegen);
@@ -25,7 +29,12 @@ public:
GCode* gcodegen() { return &m_gcodegen; }
private:
- CoolingBuffer& operator=(const CoolingBuffer&);
+ CoolingBuffer& operator=(const CoolingBuffer&) = delete;
+ std::vector<PerExtruderAdjustments> parse_layer_gcode(const std::string &gcode, std::vector<float> &current_pos) const;
+ float calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
+ // Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed.
+ // Returns the adjusted G-code.
+ std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
GCode& m_gcodegen;
std::string m_gcode;
@@ -34,6 +43,9 @@ private:
std::vector<char> m_axis;
std::vector<float> m_current_pos;
unsigned int m_current_extruder;
+
+ // Old logic: proportional.
+ bool m_cooling_logic_proportional = false;
};
}
diff --git a/xs/src/libslic3r/GCode/PreviewData.cpp b/xs/src/libslic3r/GCode/PreviewData.cpp
index 1923505e4..9cf9716e0 100644
--- a/xs/src/libslic3r/GCode/PreviewData.cpp
+++ b/xs/src/libslic3r/GCode/PreviewData.cpp
@@ -2,7 +2,12 @@
#include "PreviewData.hpp"
#include <float.h>
#include <wx/intl.h>
-#include "slic3r/GUI/GUI.hpp"
+#include <I18N.hpp>
+
+#include <boost/format.hpp>
+
+//! macro used to mark string used at localization,
+#define L(s) (s)
namespace Slic3r {
@@ -85,6 +90,12 @@ void GCodePreviewData::Range::update_from(float value)
max = std::max(max, value);
}
+void GCodePreviewData::Range::update_from(const Range& other)
+{
+ min = std::min(min, other.min);
+ max = std::max(max, other.max);
+}
+
void GCodePreviewData::Range::set_from(const Range& other)
{
min = other.min;
@@ -93,17 +104,31 @@ void GCodePreviewData::Range::set_from(const Range& other)
float GCodePreviewData::Range::step_size() const
{
- return (max - min) / (float)Colors_Count;
+ return (max - min) / (float)(Colors_Count - 1);
}
-const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at_max() const
+GCodePreviewData::Color GCodePreviewData::Range::get_color_at(float value) const
{
- return colors[Colors_Count - 1];
-}
+ if (empty())
+ return Color::Dummy;
-const GCodePreviewData::Color& GCodePreviewData::Range::get_color_at(float value) const
-{
- return empty() ? get_color_at_max() : colors[clamp((unsigned int)0, Colors_Count - 1, (unsigned int)((value - min) / step_size()))];
+ float global_t = (value - min) / step_size();
+
+ unsigned int low = (unsigned int)global_t;
+ unsigned int high = clamp((unsigned int)0, Colors_Count - 1, low + 1);
+
+ Color color_low = colors[low];
+ Color color_high = colors[high];
+
+ float local_t = global_t - (float)low;
+
+ // interpolate in RGB space
+ Color ret;
+ for (unsigned int i = 0; i < 4; ++i)
+ {
+ ret.rgba[i] = lerp(color_low.rgba[i], color_high.rgba[i], local_t);
+ }
+ return ret;
}
GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePreviewData::Color& color)
@@ -158,9 +183,6 @@ void GCodePreviewData::Extrusion::set_default()
view_type = Default_View_Type;
::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, Num_Extrusion_Roles * sizeof(Color));
- ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
- ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
- ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i)
{
@@ -198,12 +220,13 @@ void GCodePreviewData::Travel::set_default()
width = Default_Width;
height = Default_Height;
::memcpy((void*)type_colors, (const void*)Default_Type_Colors, Num_Types * sizeof(Color));
+
is_visible = false;
}
const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f);
-GCodePreviewData::Retraction::Position::Position(const Point3& position, float width, float height)
+GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height)
: position(position)
, width(width)
, height(height)
@@ -228,6 +251,11 @@ GCodePreviewData::GCodePreviewData()
void GCodePreviewData::set_default()
{
+ ::memcpy((void*)ranges.height.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+ ::memcpy((void*)ranges.width.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+ ::memcpy((void*)ranges.feedrate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+ ::memcpy((void*)ranges.volumetric_rate.colors, (const void*)Range::Default_Colors, Range::Colors_Count * sizeof(Color));
+
extrusion.set_default();
travel.set_default();
retraction.set_default();
@@ -237,6 +265,10 @@ void GCodePreviewData::set_default()
void GCodePreviewData::reset()
{
+ ranges.width.reset();
+ ranges.height.reset();
+ ranges.feedrate.reset();
+ ranges.volumetric_rate.reset();
extrusion.layers.clear();
travel.polylines.clear();
retraction.positions.clear();
@@ -248,24 +280,29 @@ bool GCodePreviewData::empty() const
return extrusion.layers.empty() && travel.polylines.empty() && retraction.positions.empty() && unretraction.positions.empty();
}
-const GCodePreviewData::Color& GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
+GCodePreviewData::Color GCodePreviewData::get_extrusion_role_color(ExtrusionRole role) const
{
return extrusion.role_colors[role];
}
-const GCodePreviewData::Color& GCodePreviewData::get_extrusion_height_color(float height) const
+GCodePreviewData::Color GCodePreviewData::get_height_color(float height) const
+{
+ return ranges.height.get_color_at(height);
+}
+
+GCodePreviewData::Color GCodePreviewData::get_width_color(float width) const
{
- return extrusion.ranges.height.get_color_at(height);
+ return ranges.width.get_color_at(width);
}
-const GCodePreviewData::Color& GCodePreviewData::get_extrusion_width_color(float width) const
+GCodePreviewData::Color GCodePreviewData::get_feedrate_color(float feedrate) const
{
- return extrusion.ranges.width.get_color_at(width);
+ return ranges.feedrate.get_color_at(feedrate);
}
-const GCodePreviewData::Color& GCodePreviewData::get_extrusion_feedrate_color(float feedrate) const
+GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) const
{
- return extrusion.ranges.feedrate.get_color_at(feedrate);
+ return ranges.volumetric_rate.get_color_at(rate);
}
void GCodePreviewData::set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha)
@@ -334,6 +371,8 @@ std::string GCodePreviewData::get_legend_title() const
return L("Width (mm)");
case Extrusion::Feedrate:
return L("Speed (mm/s)");
+ case Extrusion::VolumetricRate:
+ return L("Volumetric flow rate (mm3/s)");
case Extrusion::Tool:
return L("Tool");
}
@@ -348,11 +387,12 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
static void FillListFromRange(LegendItemsList& list, const Range& range, unsigned int decimals, float scale_factor)
{
list.reserve(Range::Colors_Count);
+
float step = range.step_size();
- for (unsigned int i = 0; i < Range::Colors_Count; ++i)
+ for (int i = Range::Colors_Count - 1; i >= 0; --i)
{
- char buf[32];
- sprintf(buf, "%.*f/%.*f", decimals, scale_factor * (range.min + (float)i * step), decimals, scale_factor * (range.min + (float)(i + 1) * step));
+ char buf[1024];
+ sprintf(buf, "%.*f", decimals, scale_factor * (range.min + (float)i * step));
list.emplace_back(buf, range.colors[i]);
}
}
@@ -370,24 +410,29 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.reserve(last_valid - first_valid + 1);
for (unsigned int i = (unsigned int)first_valid; i <= (unsigned int)last_valid; ++i)
{
- items.emplace_back(_CHB(extrusion.role_names[i].c_str()).data(), extrusion.role_colors[i]);
+ items.emplace_back(Slic3r::I18N::translate(extrusion.role_names[i]), extrusion.role_colors[i]);
}
break;
}
case Extrusion::Height:
{
- Helper::FillListFromRange(items, extrusion.ranges.height, 3, 1.0f);
+ Helper::FillListFromRange(items, ranges.height, 3, 1.0f);
break;
}
case Extrusion::Width:
{
- Helper::FillListFromRange(items, extrusion.ranges.width, 3, 1.0f);
+ Helper::FillListFromRange(items, ranges.width, 3, 1.0f);
break;
}
case Extrusion::Feedrate:
{
- Helper::FillListFromRange(items, extrusion.ranges.feedrate, 0, 1.0f);
+ Helper::FillListFromRange(items, ranges.feedrate, 1, 1.0f);
+ break;
+ }
+ case Extrusion::VolumetricRate:
+ {
+ Helper::FillListFromRange(items, ranges.volumetric_rate, 3, 1.0f);
break;
}
case Extrusion::Tool:
@@ -396,13 +441,9 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std::
items.reserve(tools_colors_count);
for (unsigned int i = 0; i < tools_colors_count; ++i)
{
- char buf[MIN_BUF_LENGTH_FOR_L];
- sprintf(buf, _CHB(L("Extruder %d")), i + 1);
-
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + i * 4), 4 * sizeof(float));
-
- items.emplace_back(buf, color);
+ items.emplace_back((boost::format(Slic3r::I18N::translate(L("Extruder %d"))) % (i + 1)).str(), color);
}
break;
diff --git a/xs/src/libslic3r/GCode/PreviewData.hpp b/xs/src/libslic3r/GCode/PreviewData.hpp
index 9fb2dc464..ab74993f5 100644
--- a/xs/src/libslic3r/GCode/PreviewData.hpp
+++ b/xs/src/libslic3r/GCode/PreviewData.hpp
@@ -37,11 +37,19 @@ public:
void reset();
bool empty() const;
void update_from(float value);
+ void update_from(const Range& other);
void set_from(const Range& other);
float step_size() const;
- const Color& get_color_at(float value) const;
- const Color& get_color_at_max() const;
+ Color get_color_at(float value) const;
+ };
+
+ struct Ranges
+ {
+ Range height;
+ Range width;
+ Range feedrate;
+ Range volumetric_rate;
};
struct LegendItem
@@ -62,6 +70,7 @@ public:
Height,
Width,
Feedrate,
+ VolumetricRate,
Tool,
Num_View_Types
};
@@ -71,13 +80,6 @@ public:
static const std::string Default_Extrusion_Role_Names[Num_Extrusion_Roles];
static const EViewType Default_View_Type;
- struct Ranges
- {
- Range height;
- Range width;
- Range feedrate;
- };
-
struct Layer
{
float z;
@@ -91,7 +93,6 @@ public:
EViewType view_type;
Color role_colors[Num_Extrusion_Roles];
std::string role_names[Num_Extrusion_Roles];
- Ranges ranges;
LayersList layers;
unsigned int role_flags;
@@ -150,11 +151,11 @@ public:
struct Position
{
- Point3 position;
+ Vec3crd position;
float width;
float height;
- Position(const Point3& position, float width, float height);
+ Position(const Vec3crd& position, float width, float height);
};
typedef std::vector<Position> PositionsList;
@@ -178,6 +179,7 @@ public:
Retraction retraction;
Retraction unretraction;
Shell shell;
+ Ranges ranges;
GCodePreviewData();
@@ -185,10 +187,11 @@ public:
void reset();
bool empty() const;
- const Color& get_extrusion_role_color(ExtrusionRole role) const;
- const Color& get_extrusion_height_color(float height) const;
- const Color& get_extrusion_width_color(float width) const;
- const Color& get_extrusion_feedrate_color(float feedrate) const;
+ Color get_extrusion_role_color(ExtrusionRole role) const;
+ Color get_height_color(float height) const;
+ Color get_width_color(float width) const;
+ Color get_feedrate_color(float feedrate) const;
+ Color get_volumetric_rate_color(float rate) const;
void set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha);
void set_extrusion_paths_colors(const std::vector<std::string>& colors);
diff --git a/xs/src/libslic3r/GCode/PrintExtents.cpp b/xs/src/libslic3r/GCode/PrintExtents.cpp
index 43352ee82..00b8838b6 100644
--- a/xs/src/libslic3r/GCode/PrintExtents.cpp
+++ b/xs/src/libslic3r/GCode/PrintExtents.cpp
@@ -19,10 +19,10 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c
if (! polyline.points.empty())
bbox.merge(polyline.points.front());
for (const Point &pt : polyline.points) {
- bbox.min.x = std::min(bbox.min.x, pt.x - radius);
- bbox.min.y = std::min(bbox.min.y, pt.y - radius);
- bbox.max.x = std::max(bbox.max.x, pt.x + radius);
- bbox.max.y = std::max(bbox.max.y, pt.y + radius);
+ bbox.min(0) = std::min(bbox.min(0), pt(0) - radius);
+ bbox.min(1) = std::min(bbox.min(1), pt(1) - radius);
+ bbox.max(0) = std::max(bbox.max(0), pt(0) + radius);
+ bbox.max(1) = std::max(bbox.max(1), pt(1) + radius);
}
return bbox;
}
@@ -32,8 +32,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusio
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width));
BoundingBoxf bboxf;
if (! empty(bbox)) {
- bboxf.min = Pointf::new_unscale(bbox.min);
- bboxf.max = Pointf::new_unscale(bbox.max);
+ bboxf.min = unscale(bbox.min);
+ bboxf.max = unscale(bbox.max);
bboxf.defined = true;
}
return bboxf;
@@ -46,8 +46,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf;
if (! empty(bbox)) {
- bboxf.min = Pointf::new_unscale(bbox.min);
- bboxf.max = Pointf::new_unscale(bbox.max);
+ bboxf.min = unscale(bbox.min);
+ bboxf.max = unscale(bbox.max);
bboxf.defined = true;
}
return bboxf;
@@ -60,8 +60,8 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf;
if (! empty(bbox)) {
- bboxf.min = Pointf::new_unscale(bbox.min);
- bboxf.max = Pointf::new_unscale(bbox.max);
+ bboxf.min = unscale(bbox.min);
+ bboxf.max = unscale(bbox.max);
bboxf.defined = true;
}
return bboxf;
@@ -121,9 +121,9 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
if (support_layer)
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
bbox_this.merge(extrusionentity_extents(extrusion_entity));
- for (const Point &offset : print_object._shifted_copies) {
+ for (const Point &offset : print_object.copies()) {
BoundingBoxf bbox_translated(bbox_this);
- bbox_translated.translate(Pointf::new_unscale(offset));
+ bbox_translated.translate(unscale(offset));
bbox.merge(bbox_translated);
}
}
@@ -134,6 +134,12 @@ BoundingBoxf get_print_object_extrusions_extents(const PrintObject &print_object
// The projection does not contain the priming regions.
BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_t max_print_z)
{
+ // Wipe tower extrusions are saved as if the tower was at the origin with no rotation
+ // We need to get position and angle of the wipe tower to transform them to actual position.
+ Transform2d trafo =
+ Eigen::Translation2d(print.config().wipe_tower_x.value, print.config().wipe_tower_y.value) *
+ Eigen::Rotation2Dd(print.config().wipe_tower_rotation_angle.value);
+
BoundingBoxf bbox;
for (const std::vector<WipeTower::ToolChangeResult> &tool_changes : print.wipe_tower_data().tool_changes) {
if (! tool_changes.empty() && tool_changes.front().print_z > max_print_z)
@@ -142,14 +148,11 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
- Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y);
- Pointf p2(e.pos.x, e.pos.y);
- bbox.merge(p1);
- coordf_t radius = 0.5 * e.width;
- bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius);
- bbox.min.y = std::min(bbox.min.y, std::min(p1.y, p2.y) - radius);
- bbox.max.x = std::max(bbox.max.x, std::max(p1.x, p2.x) + radius);
- bbox.max.y = std::max(bbox.max.y, std::max(p1.y, p2.y) + radius);
+ Vec2d delta = 0.5 * Vec2d(e.width, e.width);
+ Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y);
+ Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y);
+ bbox.merge(p1.cwiseMin(p2) - delta);
+ bbox.merge(p1.cwiseMax(p2) + delta);
}
}
}
@@ -166,14 +169,14 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print)
for (size_t i = 1; i < tcr.extrusions.size(); ++ i) {
const WipeTower::Extrusion &e = tcr.extrusions[i];
if (e.width > 0) {
- Pointf p1((&e - 1)->pos.x, (&e - 1)->pos.y);
- Pointf p2(e.pos.x, e.pos.y);
+ Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y);
+ Vec2d p2(e.pos.x, e.pos.y);
bbox.merge(p1);
coordf_t radius = 0.5 * e.width;
- bbox.min.x = std::min(bbox.min.x, std::min(p1.x, p2.x) - radius);
- bbox.min.y = std::min(bbox.min.y, std::min(p1.y, p2.y) - radius);
- bbox.max.x = std::max(bbox.max.x, std::max(p1.x, p2.x) + radius);
- bbox.max.y = std::max(bbox.max.y, std::max(p1.y, p2.y) + radius);
+ bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius);
+ bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius);
+ bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius);
+ bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius);
}
}
}
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.cpp b/xs/src/libslic3r/GCode/ToolOrdering.cpp
index 6e03d89c9..175b69447 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.cpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.cpp
@@ -15,6 +15,24 @@
namespace Slic3r {
+
+// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
+bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
+{
+ if (a==b)
+ return false;
+
+ for (auto extruder : extruders) {
+ if (extruder == a)
+ return true;
+ if (extruder == b)
+ return false;
+ }
+
+ return false;
+}
+
+
// For the use case when each object is printed separately
// (print.config().complete_objects is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
@@ -48,11 +66,14 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// (print.config().complete_objects is false).
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
{
+ m_print_config_ptr = &print.config();
+
+ PrintObjectPtrs objects = print.get_printable_objects();
// Initialize the print layers for all objects and all layers.
coordf_t object_bottom_z = 0.;
{
std::vector<coordf_t> zs;
- for (auto object : print.objects()) {
+ for (auto object : objects) {
zs.reserve(zs.size() + object->layers().size() + object->support_layers().size());
for (auto layer : object->layers())
zs.emplace_back(layer->print_z);
@@ -65,7 +86,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
}
// Collect extruders reuqired to print the layers.
- for (auto object : print.objects())
+ for (auto object : objects)
this->collect_extruders(*object);
// Reorder the extruders to minimize tool switches.
@@ -76,9 +97,10 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->collect_extruder_statistics(prime_multi_material);
}
-ToolOrdering::LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
+
+LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z)
{
- auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), ToolOrdering::LayerTools(print_z - EPSILON));
+ auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
assert(it_layer_tools != m_layer_tools.end());
coordf_t dist_min = std::abs(it_layer_tools->print_z - print_z);
for (++ it_layer_tools; it_layer_tools != m_layer_tools.end(); ++it_layer_tools) {
@@ -102,7 +124,7 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
coordf_t zmax = zs[i] + EPSILON;
for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
// Assign an average print_z to the set of layers with nearly equal print_z.
- m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
+ m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr));
i = j;
}
}
@@ -134,12 +156,29 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (layerm == nullptr)
continue;
const PrintRegion &region = *object.print()->regions()[region_id];
+
if (! layerm->perimeters.entities.empty()) {
- layer_tools.extruders.push_back(region.config().perimeter_extruder.value);
+ bool something_nonoverriddable = true;
+
+ if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
+ something_nonoverriddable = false;
+ for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
+ if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
+ something_nonoverriddable = true;
+ break;
+ }
+ }
+
+ if (something_nonoverriddable)
+ layer_tools.extruders.push_back(region.config().perimeter_extruder.value);
+
layer_tools.has_object = true;
}
+
+
bool has_infill = false;
bool has_solid_infill = false;
+ bool something_nonoverriddable = false;
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
@@ -148,19 +187,33 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
has_solid_infill = true;
else if (role != erNone)
has_infill = true;
+
+ if (m_print_config_ptr) {
+ if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region))
+ something_nonoverriddable = true;
+ }
+ }
+
+ if (something_nonoverriddable || !m_print_config_ptr)
+ {
+ if (has_solid_infill)
+ layer_tools.extruders.push_back(region.config().solid_infill_extruder);
+ if (has_infill)
+ layer_tools.extruders.push_back(region.config().infill_extruder);
}
- if (has_solid_infill)
- layer_tools.extruders.push_back(region.config().solid_infill_extruder);
- if (has_infill)
- layer_tools.extruders.push_back(region.config().infill_extruder);
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
}
}
- // Sort and remove duplicates
- for (LayerTools &lt : m_layer_tools)
- sort_remove_duplicates(lt.extruders);
+ for (auto& layer : m_layer_tools) {
+ // Sort and remove duplicates
+ sort_remove_duplicates(layer.extruders);
+
+ // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
+ if (layer.extruders.empty() && layer.has_object)
+ layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
+ }
}
// Reorder extruders to minimize layer changes.
@@ -217,6 +270,8 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
}
}
+
+
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
{
if (m_layer_tools.empty())
@@ -327,4 +382,250 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
}
}
+
+
+// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
+void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies)
+{
+ something_overridden = true;
+
+ auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator
+ auto& copies_vector = entity_map_it->second;
+ if (copies_vector.size() < num_of_copies)
+ copies_vector.resize(num_of_copies, -1);
+
+ if (copies_vector[copy_id] != -1)
+ std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen.
+
+ copies_vector[copy_id] = extruder;
+}
+
+
+// Finds first non-soluble extruder on the layer
+int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
+{
+ const LayerTools& lt = *m_layer_tools;
+ for (auto extruders_it = lt.extruders.begin(); extruders_it != lt.extruders.end(); ++extruders_it)
+ if (!print_config.filament_soluble.get_at(*extruders_it))
+ return (*extruders_it);
+
+ return (-1);
+}
+
+// Finds last non-soluble extruder on the layer
+int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
+{
+ const LayerTools& lt = *m_layer_tools;
+ for (auto extruders_it = lt.extruders.rbegin(); extruders_it != lt.extruders.rend(); ++extruders_it)
+ if (!print_config.filament_soluble.get_at(*extruders_it))
+ return (*extruders_it);
+
+ return (-1);
+}
+
+
+// Decides whether this entity could be overridden
+bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
+{
+ if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region)))
+ return false;
+
+ if (object.config().wipe_into_objects)
+ return true;
+
+ if (!region.config().wipe_into_infill || eec.role() != erInternalInfill)
+ return false;
+
+ return true;
+}
+
+
+// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
+// and returns volume that is left to be wiped on the wipe tower.
+float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
+{
+ const LayerTools& lt = *m_layer_tools;
+ const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
+
+ if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
+ return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it
+
+ // we will sort objects so that dedicated for wiping are at the beginning:
+ PrintObjectPtrs object_list = print.get_printable_objects();
+ std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().wipe_into_objects; });
+
+ // We will now iterate through
+ // - first the dedicated objects to mark perimeters or infills (depending on infill_first)
+ // - second through the dedicated ones again to mark infills or perimeters (depending on infill_first)
+ // - then all the others to mark infills (in case that !infill_first, we must also check that the perimeter is finished already
+ // this is controlled by the following variable:
+ bool perimeters_done = false;
+
+ for (int i=0 ; i<(int)object_list.size() + (perimeters_done ? 0 : 1); ++i) {
+ if (!perimeters_done && (i==(int)object_list.size() || !object_list[i]->config().wipe_into_objects)) { // we passed the last dedicated object in list
+ perimeters_done = true;
+ i=-1; // let's go from the start again
+ continue;
+ }
+
+ const auto& object = object_list[i];
+
+ // Finds this layer:
+ auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
+ if (this_layer_it == object->layers().end())
+ continue;
+ const Layer* this_layer = *this_layer_it;
+ unsigned int num_of_copies = object->copies().size();
+
+ for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+
+ for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
+ const auto& region = *object->print()->regions()[region_id];
+
+ if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
+ continue;
+
+
+ if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) {
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+ if (!is_overriddable(*fill, print.config(), *object, region))
+ continue;
+
+ // What extruder would this normally be printed with?
+ unsigned int correct_extruder = Print::get_extruder(*fill, region);
+
+ if (volume_to_wipe<=0)
+ continue;
+
+ if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
+ // In this case we must check that the original extruder is used on this layer before the one we are overridding
+ // (and the perimeters will be finished before the infill is printed):
+ if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder))
+ continue;
+
+ if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
+ set_extruder_override(fill, copy, new_extruder, num_of_copies);
+ volume_to_wipe -= fill->total_volume();
+ }
+ }
+ }
+
+ // Now the same for perimeters - see comments above for explanation:
+ if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done))
+ {
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!is_overriddable(*fill, print.config(), *object, region))
+ continue;
+
+ if (volume_to_wipe<=0)
+ continue;
+
+ if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
+ set_extruder_override(fill, copy, new_extruder, num_of_copies);
+ volume_to_wipe -= fill->total_volume();
+ }
+ }
+ }
+ }
+ }
+ }
+ return std::max(0.f, volume_to_wipe);
+}
+
+
+
+// Called after all toolchanges on a layer were mark_infill_overridden. There might still be overridable entities,
+// that were not actually overridden. If they are part of a dedicated object, printing them with the extruder
+// they were initially assigned to might mean violating the perimeter-infill order. We will therefore go through
+// them again and make sure we override it.
+void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
+{
+ const LayerTools& lt = *m_layer_tools;
+ unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
+ unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
+
+ PrintObjectPtrs printable_objects = print.get_printable_objects();
+ for (const PrintObject* object : printable_objects) {
+ // Finds this layer:
+ auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; });
+ if (this_layer_it == object->layers().end())
+ continue;
+ const Layer* this_layer = *this_layer_it;
+ unsigned int num_of_copies = object->copies().size();
+
+ for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
+ for (size_t region_id = 0; region_id < object->print()->regions().size(); ++ region_id) {
+ const auto& region = *object->print()->regions()[region_id];
+
+ if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
+ continue;
+
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+
+ if (!is_overriddable(*fill, print.config(), *object, region)
+ || is_entity_overridden(fill, copy) )
+ continue;
+
+ // This infill could have been overridden but was not - unless we do something, it could be
+ // printed before its perimeter, or not be printed at all (in case its original extruder has
+ // not been added to LayerTools
+ // Either way, we will now force-override it with something suitable:
+ if (print.config().infill_first
+ || object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
+ || lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
+ || std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME)
+ )
+ set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
+ else {
+ // In this case we can (and should) leave it to be printed normally.
+ // Force overriding would mean it gets printed before its perimeter.
+ }
+ }
+
+ // Now the same for perimeters - see comments above for explanation:
+ for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
+ auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
+ if (!is_overriddable(*fill, print.config(), *object, region)
+ || is_entity_overridden(fill, copy) )
+ continue;
+
+ set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
+// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
+// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
+// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
+// so -1 was used as "print as usual".
+// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
+// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
+const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies)
+{
+ auto entity_map_it = entity_map.find(entity);
+ if (entity_map_it == entity_map.end())
+ entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first;
+
+ // Now the entity_map_it should be valid, let's make sure the vector is long enough:
+ entity_map_it->second.resize(num_of_copies, -1);
+
+ // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
+ std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
+
+ return &(entity_map_it->second);
+}
+
+
} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/ToolOrdering.hpp b/xs/src/libslic3r/GCode/ToolOrdering.hpp
index c92806b19..4dcf6516a 100644
--- a/xs/src/libslic3r/GCode/ToolOrdering.hpp
+++ b/xs/src/libslic3r/GCode/ToolOrdering.hpp
@@ -9,38 +9,99 @@ namespace Slic3r {
class Print;
class PrintObject;
+class LayerTools;
-class ToolOrdering
+
+
+// Object of this class holds information about whether an extrusion is printed immediately
+// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
+// of several copies - this has to be taken into account.
+class WipingExtrusions
+{
+public:
+ bool is_anything_overridden() const { // if there are no overrides, all the agenda can be skipped - this function can tell us if that's the case
+ return something_overridden;
+ }
+
+ // This is called from GCode::process_layer - see implementation for further comments:
+ const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies);
+
+ // This function goes through all infill entities, decides which ones will be used for wiping and
+ // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
+ float mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe);
+
+ void ensure_perimeters_infills_order(const Print& print);
+
+ bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
+
+ void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
+
+private:
+ int first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+ int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
+
+ // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
+ void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies);
+
+ // Returns true in case that entity is not printed with its usual extruder for a given copy:
+ bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const {
+ return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1);
+ }
+
+ std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what
+ bool something_overridden = false;
+ const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to
+};
+
+
+
+class LayerTools
{
public:
- struct LayerTools
- {
- LayerTools(const coordf_t z) :
- print_z(z),
- has_object(false),
- has_support(false),
- has_wipe_tower(false),
- wipe_tower_partitions(0),
- wipe_tower_layer_height(0.) {}
-
- bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
- bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
-
- coordf_t print_z;
- bool has_object;
- bool has_support;
- // Zero based extruder IDs, ordered to minimize tool switches.
- std::vector<unsigned int> extruders;
- // Will there be anything extruded on this layer for the wipe tower?
- // Due to the support layers possibly interleaving the object layers,
- // wipe tower will be disabled for some support only layers.
- bool has_wipe_tower;
- // Number of wipe tower partitions to support the required number of tool switches
- // and to support the wipe tower partitions above this one.
- size_t wipe_tower_partitions;
- coordf_t wipe_tower_layer_height;
- };
+ LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) :
+ print_z(z),
+ has_object(false),
+ has_support(false),
+ has_wipe_tower(false),
+ wipe_tower_partitions(0),
+ wipe_tower_layer_height(0.) {}
+
+ // Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
+ // In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
+ bool operator< (const LayerTools &rhs) const { return print_z < rhs.print_z; }
+ bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
+
+ bool is_extruder_order(unsigned int a, unsigned int b) const;
+
+ coordf_t print_z;
+ bool has_object;
+ bool has_support;
+ // Zero based extruder IDs, ordered to minimize tool switches.
+ std::vector<unsigned int> extruders;
+ // Will there be anything extruded on this layer for the wipe tower?
+ // Due to the support layers possibly interleaving the object layers,
+ // wipe tower will be disabled for some support only layers.
+ bool has_wipe_tower;
+ // Number of wipe tower partitions to support the required number of tool switches
+ // and to support the wipe tower partitions above this one.
+ size_t wipe_tower_partitions;
+ coordf_t wipe_tower_layer_height;
+
+ WipingExtrusions& wiping_extrusions() {
+ m_wiping_extrusions.set_layer_tools_ptr(this);
+ return m_wiping_extrusions;
+ }
+
+private:
+ // This object holds list of extrusion that will be used for extruder wiping
+ WipingExtrusions m_wiping_extrusions;
+};
+
+
+class ToolOrdering
+{
+public:
ToolOrdering() {}
// For the use case when each object is printed separately
@@ -72,7 +133,7 @@ public:
std::vector<LayerTools>::const_iterator begin() const { return m_layer_tools.begin(); }
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); }
- const std::vector<LayerTools>& layer_tools() const { return m_layer_tools; }
+ std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
private:
@@ -80,17 +141,22 @@ private:
void collect_extruders(const PrintObject &object);
void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
- void collect_extruder_statistics(bool prime_multi_material);
-
- std::vector<LayerTools> m_layer_tools;
- // First printing extruder, including the multi-material priming sequence.
- unsigned int m_first_printing_extruder = (unsigned int)-1;
- // Final printing extruder.
- unsigned int m_last_printing_extruder = (unsigned int)-1;
- // All extruders, which extrude some material over m_layer_tools.
- std::vector<unsigned int> m_all_printing_extruders;
+ void collect_extruder_statistics(bool prime_multi_material);
+
+ std::vector<LayerTools> m_layer_tools;
+ // First printing extruder, including the multi-material priming sequence.
+ unsigned int m_first_printing_extruder = (unsigned int)-1;
+ // Final printing extruder.
+ unsigned int m_last_printing_extruder = (unsigned int)-1;
+ // All extruders, which extrude some material over m_layer_tools.
+ std::vector<unsigned int> m_all_printing_extruders;
+
+
+ const PrintConfig* m_print_config_ptr = nullptr;
};
+
+
} // namespace SLic3r
#endif /* slic3r_ToolOrdering_hpp_ */
diff --git a/xs/src/libslic3r/GCode/WipeTower.hpp b/xs/src/libslic3r/GCode/WipeTower.hpp
index 2c92e16e6..c35a0feb3 100644
--- a/xs/src/libslic3r/GCode/WipeTower.hpp
+++ b/xs/src/libslic3r/GCode/WipeTower.hpp
@@ -17,12 +17,37 @@ public:
struct xy
{
xy(float x = 0.f, float y = 0.f) : x(x), y(y) {}
+ xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {}
xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; }
xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; }
xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; }
xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; }
bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; }
bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; }
+
+ // Rotate the point around center of the wipe tower about given angle (in degrees)
+ xy rotate(float width, float depth, float angle) const {
+ xy out(0,0);
+ float temp_x = x - width / 2.f;
+ float temp_y = y - depth / 2.f;
+ angle *= float(M_PI/180.);
+ out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f;
+ out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f;
+ return out;
+ }
+
+ // Rotate the point around origin about given angle in degrees
+ void rotate(float angle) {
+ float temp_x = x * cos(angle) - y * sin(angle);
+ y = x * sin(angle) + y * cos(angle);
+ x = temp_x;
+ }
+
+ void translate(const xy& vect) {
+ x += vect.x;
+ y += vect.y;
+ }
+
float x;
float y;
};
@@ -90,6 +115,9 @@ public:
// This is useful not only for the print time estimation, but also for the control of layer cooling.
float elapsed_time;
+ // Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later)
+ bool priming;
+
// Sum the total length of the extrusion.
float total_extrusion_length_in_plane() {
float e_length = 0.f;
@@ -112,17 +140,15 @@ public:
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
- bool last_wipe_inside_wipe_tower,
- // May be used by a stand alone post processor.
- Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
+ bool last_wipe_inside_wipe_tower) = 0;
// Returns gcode for toolchange and the end position.
// if new_tool == -1, just unload the current filament over the wipe tower.
- virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
+ virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0;
// Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
// Call this method only if layer_finished() is false.
- virtual ToolChangeResult finish_layer(Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) = 0;
+ virtual ToolChangeResult finish_layer() = 0;
// Is the current layer finished? A layer is finished if either the wipe tower is finished, or
// the wipe tower has been completely covered by the tool change extrusions,
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
index 069f975dd..2c96cce7b 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.cpp
@@ -1,10 +1,25 @@
+/*
+
+TODO LIST
+---------
+
+1. cooling moves - DONE
+2. account for perimeter and finish_layer extrusions and subtract it from last wipe - DONE
+3. priming extrusions (last wipe must clear the color) - DONE
+4. Peter's wipe tower - layer's are not exactly square
+5. Peter's wipe tower - variable width for higher levels
+6. Peter's wipe tower - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
+7. Peter's wipe tower - enable enhanced first layer adhesion
+
+*/
+
#include "WipeTowerPrusaMM.hpp"
#include <assert.h>
#include <math.h>
-#include <fstream>
#include <iostream>
#include <vector>
+#include <numeric>
#include "Analyzer.hpp"
@@ -16,6 +31,7 @@
#define strcasecmp _stricmp
#endif
+
namespace Slic3r
{
@@ -24,17 +40,38 @@ namespace PrusaMultiMaterial {
class Writer
{
public:
- Writer() :
+ Writer(float layer_height, float line_width) :
m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
m_current_z(0.f),
m_current_feedrate(0.f),
+ m_layer_height(layer_height),
m_extrusion_flow(0.f),
- m_layer_height(0.f),
m_preview_suppressed(false),
- m_elapsed_time(0.f) {}
-
- Writer& set_initial_position(const WipeTower::xy &pos) {
- m_start_pos = pos;
+ m_elapsed_time(0.f),
+ m_default_analyzer_line_width(line_width)
+ {
+ // adds tag for analyzer:
+ char buf[64];
+ sprintf(buf, ";%s%f\n", GCodeAnalyzer::Height_Tag.c_str(), m_layer_height); // don't rely on GCodeAnalyzer knowing the layer height - it knows nothing at priming
+ m_gcode += buf;
+ sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
+ m_gcode += buf;
+ change_analyzer_line_width(line_width);
+ }
+
+ Writer& change_analyzer_line_width(float line_width) {
+ // adds tag for analyzer:
+ char buf[64];
+ sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width);
+ m_gcode += buf;
+ return *this;
+ }
+
+ Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) {
+ m_wipe_tower_width = width;
+ m_wipe_tower_depth = depth;
+ m_internal_angle = internal_angle;
+ m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle);
m_current_pos = pos;
return *this;
}
@@ -44,17 +81,20 @@ public:
Writer& set_z(float z)
{ m_current_z = z; return *this; }
- Writer& set_layer_height(float layer_height)
- { m_layer_height = layer_height; return *this; }
-
Writer& set_extrusion_flow(float flow)
{ m_extrusion_flow = flow; return *this; }
+ Writer& set_y_shift(float shift) {
+ m_current_pos.y -= shift-m_y_shift;
+ m_y_shift = shift;
+ return (*this);
+ }
+
// Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various
// filament loading and cooling moves from normal extrusion moves. Therefore the writer
// is asked to suppres output of some lines, which look like extrusions.
- Writer& suppress_preview() { m_preview_suppressed = true; return *this; }
- Writer& resume_preview() { m_preview_suppressed = false; return *this; }
+ Writer& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; }
+ Writer& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; }
Writer& feedrate(float f)
{
@@ -67,8 +107,9 @@ public:
const std::vector<WipeTower::Extrusion>& extrusions() const { return m_extrusions; }
float x() const { return m_current_pos.x; }
float y() const { return m_current_pos.y; }
- const WipeTower::xy& start_pos() const { return m_start_pos; }
const WipeTower::xy& pos() const { return m_current_pos; }
+ const WipeTower::xy start_pos_rotated() const { return m_start_pos; }
+ const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); }
float elapsed_time() const { return m_elapsed_time; }
// Extrude with an explicitely provided amount of extrusion.
@@ -81,22 +122,30 @@ public:
float dx = x - m_current_pos.x;
float dy = y - m_current_pos.y;
double len = sqrt(dx*dx+dy*dy);
+
+
+ // Now do the "internal rotation" with respect to the wipe tower center
+ WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are
+ WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go
+
if (! m_preview_suppressed && e > 0.f && len > 0.) {
// Width of a squished extrusion, corrected for the roundings of the squished extrusions.
// This is left zero if it is a travel move.
- float width = float(double(e) * m_filament_area / (len * m_layer_height));
+ float width = float(double(e) * /*Filament_Area*/2.40528 / (len * m_layer_height));
// Correct for the roundings of a squished extrusion.
- width += float(m_layer_height * (1. - M_PI / 4.));
- if (m_extrusions.empty() || m_extrusions.back().pos != m_current_pos)
- m_extrusions.emplace_back(WipeTower::Extrusion(m_current_pos, 0, m_current_tool));
- m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(x, y), width, m_current_tool));
+ width += m_layer_height * float(1. - M_PI / 4.);
+ if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos)
+ m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool));
+ m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool));
}
m_gcode += "G1";
- if (x != m_current_pos.x)
- m_gcode += set_format_X(x);
- if (y != m_current_pos.y)
- m_gcode += set_format_Y(y);
+ if (std::abs(rot.x - rotated_current_pos.x) > EPSILON)
+ m_gcode += set_format_X(rot.x);
+
+ if (std::abs(rot.y - rotated_current_pos.y) > EPSILON)
+ m_gcode += set_format_Y(rot.y);
+
if (e != 0.f)
m_gcode += set_format_E(e);
@@ -104,6 +153,9 @@ public:
if (f != 0.f && f != m_current_feedrate)
m_gcode += set_format_F(f);
+ m_current_pos.x = x;
+ m_current_pos.y = y;
+
// Update the elapsed time with a rough estimate.
m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f;
m_gcode += "\n";
@@ -130,6 +182,31 @@ public:
Writer& extrude(const WipeTower::xy &dest, const float f = 0.f)
{ return extrude(dest.x, dest.y, f); }
+
+ Writer& rectangle(const WipeTower::xy& ld,float width,float height,const float f = 0.f)
+ {
+ WipeTower::xy corners[4];
+ corners[0] = ld;
+ corners[1] = WipeTower::xy(ld,width,0.f);
+ corners[2] = WipeTower::xy(ld,width,height);
+ corners[3] = WipeTower::xy(ld,0.f,height);
+ int index_of_closest = 0;
+ if (x()-ld.x > ld.x+width-x()) // closer to the right
+ index_of_closest = 1;
+ if (y()-ld.y > ld.y+height-y()) // closer to the top
+ index_of_closest = (index_of_closest==0 ? 3 : 2);
+
+ travel(corners[index_of_closest].x, y()); // travel to the closest corner
+ travel(x(),corners[index_of_closest].y);
+
+ int i = index_of_closest;
+ do {
+ ++i;
+ if (i==4) i=0;
+ extrude(corners[i], f);
+ } while (i != index_of_closest);
+ return (*this);
+ }
Writer& load(float e, float f = 0.f)
{
@@ -143,7 +220,7 @@ public:
m_gcode += "\n";
return *this;
}
-
+
// Derectract while moving in the X direction.
// If |x| > 0, the feed rate relates to the x distance,
// otherwise the feed rate relates to the e distance.
@@ -153,6 +230,17 @@ public:
Writer& retract(float e, float f = 0.f)
{ return load(-e, f); }
+// Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary)
+ Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f)
+ {
+ float time = std::abs(loading_dist / loading_speed);
+ float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time);
+ float feedrate = 60.f * std::hypot(x_speed, loading_speed);
+
+ float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time;
+ return extrude_explicit(end_point, y(), loading_dist, feedrate);
+ }
+
// Elevate the extruder head above the current print_z position.
Writer& z_hop(float hop, float f = 0.f)
{
@@ -198,8 +286,19 @@ public:
// Set extruder temperature, don't wait by default.
Writer& set_extruder_temp(int temperature, bool wait = false)
{
+ char buf[128];
+ sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
+ m_gcode += buf;
+ return *this;
+ };
+
+ // Wait for a period of time (seconds).
+ Writer& wait(float time)
+ {
+ if (time==0)
+ return *this;
char buf[128];
- sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature);
+ sprintf(buf, "G4 S%.3f\n", time);
m_gcode += buf;
return *this;
};
@@ -243,6 +342,25 @@ public:
return *this;
};
+
+ Writer& set_fan(unsigned int speed)
+ {
+ if (speed == m_last_fan_speed)
+ return *this;
+
+ if (speed == 0)
+ m_gcode += "M107\n";
+ else
+ {
+ m_gcode += "M106 S";
+ char buf[128];
+ sprintf(buf,"%u\n",(unsigned int)(255.0 * speed / 100.0));
+ m_gcode += buf;
+ }
+ m_last_fan_speed = speed;
+ return *this;
+ }
+
Writer& comment_material(WipeTowerPrusaMM::material_type material)
{
m_gcode += "; material : ";
@@ -279,9 +397,16 @@ private:
std::string m_gcode;
std::vector<WipeTower::Extrusion> m_extrusions;
float m_elapsed_time;
- const double m_filament_area = 0.25*M_PI*1.75*1.75;
-
- std::string set_format_X(float x) {
+ float m_internal_angle = 0.f;
+ float m_y_shift = 0.f;
+ float m_wipe_tower_width = 0.f;
+ float m_wipe_tower_depth = 0.f;
+ float m_last_fan_speed = 0.f;
+ int current_temp = -1;
+ const float m_default_analyzer_line_width;
+
+ std::string set_format_X(float x)
+ {
char buf[64];
sprintf(buf, " X%.3f", x);
m_current_pos.x = x;
@@ -315,32 +440,11 @@ private:
}
Writer& operator=(const Writer &rhs);
-};
-
-/*
-class Material
-{
-public:
- std::string name;
- std::string type;
-
- struct RammingStep {
-// float length;
- float extrusion_multiplier; // sirka linky
- float extrusion;
- float speed;
- };
- std::vector<RammingStep> ramming_sequence;
+}; // class Writer
- // Number and speed of the cooling moves.
- std::vector<float> cooling_moves;
+}; // namespace PrusaMultiMaterial
- // Percentage of the speed overide, in pairs of <z, percentage>
- std::vector<std::pair<float, int>> speed_override;
-};
-*/
-} // namespace PrusaMultiMaterial
WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name)
{
@@ -365,6 +469,7 @@ WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *nam
return INVALID;
}
+
// Returns gcode to prime the nozzles at the front edge of the print bed.
WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
// print_z of the first layer.
@@ -373,90 +478,52 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
- bool last_wipe_inside_wipe_tower,
- // May be used by a stand alone post processor.
- Purpose purpose)
+ bool last_wipe_inside_wipe_tower)
{
this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
-
- float wipe_area = m_wipe_area;
- // Calculate the amount of wipe over the wipe tower brim following the prime, decrease wipe_area
- // with the amount of material extruded over the brim.
- {
- // Simulate the brim extrusions, summ the length of the extrusion.
- float e_length = this->tool_change(0, false, PURPOSE_EXTRUDE).total_extrusion_length_in_plane();
- // Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
- // Y stepping of the wipe extrusions.
- float dy = m_perimeter_width * 0.8f;
- // Number of whole wipe lines, that would be extruded to wipe as much material as the finish_layer().
- // Minimum wipe area is 5mm wide.
- //FIXME calculate the purge_lines_width precisely.
- float purge_lines_width = 1.3f;
- wipe_area = std::max(5.f, m_wipe_area - float(floor(e_length / m_wipe_tower_width)) * dy - purge_lines_width);
- }
-
- this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false);
- m_num_layer_changes = 0;
- m_current_tool = tools.front();
-
+ this->m_current_tool = tools.front();
+
// The Prusa i3 MK2 has a working space of [0, -2.2] to [250, 210].
// Due to the XYZ calibration, this working space may shrink slightly from all directions,
// therefore the homing position is shifted inside the bed by 0.2 in the firmware to [0.2, -2.0].
// box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area);
- box_coordinates cleaning_box(xy(5.f, 0.f), m_wipe_tower_width, wipe_area);
- PrusaMultiMaterial::Writer writer;
+ const float prime_section_width = std::min(240.f / tools.size(), 60.f);
+ box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
+
+ PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
- .set_layer_height(m_layer_height)
.set_initial_tool(m_current_tool)
.append(";--------------------\n"
"; CP PRIMING START\n")
.append(";--------------------\n")
.speed_override(100);
- // Always move to the starting position.
- writer.set_initial_position(xy(0.f, 0.f))
- .travel(cleaning_box.ld, 7200)
- // Increase the extruder driver current to allow fast ramming.
- .set_extruder_trimpot(750);
-
- // adds tag for analyzer
- char buf[32];
- sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
- writer.append(buf);
-
- if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
- float y_end = 0.f;
- for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
- unsigned int tool = tools[idx_tool];
- // Select the tool, set a speed override for soluble and flex materials.
- toolchange_Change(writer, tool, m_material[tool]);
- // Prime the tool.
- toolchange_Load(writer, cleaning_box);
- if (idx_tool + 1 == tools.size()) {
- // Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
- if (last_wipe_inside_wipe_tower) {
- // Shrink the last wipe area to the area of the other purge areas,
- // remember the last initial wipe width to be purged into the 1st layer of the wipe tower.
- m_initial_extra_wipe = std::max(0.f, wipe_area - (y_end + 0.5f * 0.85f * m_perimeter_width - cleaning_box.ld.y));
- cleaning_box.lu.y -= m_initial_extra_wipe;
- cleaning_box.ru.y -= m_initial_extra_wipe;
- }
- toolchange_Wipe(writer, cleaning_box, false);
- } else {
- // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
- writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
- // Change the extruder temperature to the temperature of the next filament before starting the cooling moves.
- toolchange_Unload(writer, cleaning_box, m_material[m_current_tool], m_first_layer_temperature[tools[idx_tool+1]]);
- // Save the y end of the non-last priming area.
- y_end = writer.y();
- cleaning_box.translate(m_wipe_tower_width, 0.f);
- writer.travel(cleaning_box.ld, 7200);
- }
- ++ m_num_tool_changes;
- }
- }
+ writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position
+ .travel(cleaning_box.ld, 7200)
+ .set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming.
+
+ for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
+ unsigned int tool = tools[idx_tool];
+ m_left_to_right = true;
+ toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials.
+ toolchange_Load(writer, cleaning_box); // Prime the tool.
+ if (idx_tool + 1 == tools.size()) {
+ // Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
+ toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool]);
+ } else {
+ // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
+ //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
+ toolchange_Wipe(writer, cleaning_box , 20.f);
+ box_coordinates box = cleaning_box;
+ box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width);
+ toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature);
+ cleaning_box.translate(prime_section_width, 0.f);
+ writer.travel(cleaning_box.ld, 7200);
+ }
+ ++ m_num_tool_changes;
+ }
// Reset the extruder current to a normal value.
writer.set_extruder_trimpot(550)
@@ -467,267 +534,169 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime(
";------------------\n"
"\n\n");
- // Force m_idx_tool_change_in_layer to -1, so that tool_change() will know to extrude the wipe tower brim.
- m_idx_tool_change_in_layer = (unsigned int)(-1);
+ // so that tool_change() will know to extrude the wipe tower brim:
+ m_print_brim = true;
ToolChangeResult result;
- result.print_z = m_z_pos;
- result.layer_height = m_layer_height;
+ result.priming = true;
+ result.print_z = this->m_z_pos;
+ result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
result.elapsed_time = writer.elapsed_time();
result.extrusions = writer.extrusions();
- result.start_pos = writer.start_pos();
- result.end_pos = writer.pos();
+ result.start_pos = writer.start_pos_rotated();
+ result.end_pos = writer.pos_rotated();
return result;
}
-WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer, Purpose purpose)
+WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer)
{
- // Either it is the last tool unload,
- // or there must be a nonzero wipe tower partitions available.
-// assert(tool < 0 || it_layer_tools->wipe_tower_partitions > 0);
-
- if (m_idx_tool_change_in_layer == (unsigned int)(-1)) {
- // First layer, prime the extruder.
- return toolchange_Brim(purpose);
- }
-
- float wipe_area = m_wipe_area;
- if (++ m_idx_tool_change_in_layer < (unsigned int)m_max_color_changes && last_in_layer) {
- // This tool_change() call will be followed by a finish_layer() call.
- // Try to shrink the wipe_area to save material, as less than usual wipe is required
- // if this step is foolowed by finish_layer() extrusions wiping the same extruder.
- for (size_t iter = 0; iter < 3; ++ iter) {
- // Simulate the finish_layer() extrusions, summ the length of the extrusion.
- float e_length = 0.f;
- {
- unsigned int old_idx_tool_change = m_idx_tool_change_in_layer;
- float old_wipe_start_y = m_current_wipe_start_y;
- m_current_wipe_start_y += wipe_area;
- e_length = this->finish_layer(PURPOSE_EXTRUDE).total_extrusion_length_in_plane();
- m_idx_tool_change_in_layer = old_idx_tool_change;
- m_current_wipe_start_y = old_wipe_start_y;
- }
- // Shrink wipe_area by the amount of extrusion extruded by the finish_layer().
- // Y stepping of the wipe extrusions.
- float dy = m_perimeter_width * 0.8f;
- // Number of whole wipe lines, that would be extruded to wipe as much material as the finish_layer().
- float num_lines_extruded = floor(e_length / m_wipe_tower_width);
- // Minimum wipe area is 5mm wide.
- wipe_area = m_wipe_area - num_lines_extruded * dy;
- if (wipe_area < 5.) {
- wipe_area = 5.;
+ if ( m_print_brim )
+ return toolchange_Brim();
+
+ float wipe_area = 0.f;
+ bool last_change_in_layer = false;
+ float wipe_volume = 0.f;
+
+ // Finds this toolchange info
+ if (tool != (unsigned int)(-1))
+ {
+ for (const auto &b : m_layer_info->tool_changes)
+ if ( b.new_tool == tool ) {
+ wipe_volume = b.wipe_volume;
+ if (tool == m_layer_info->tool_changes.back().new_tool)
+ last_change_in_layer = true;
+ wipe_area = b.required_depth * m_layer_info->extra_spacing;
break;
}
- }
+ }
+ else {
+ // Otherwise we are going to Unload only. And m_layer_info would be invalid.
}
box_coordinates cleaning_box(
- m_wipe_tower_pos + xy(0.f, m_current_wipe_start_y + 0.5f * m_perimeter_width),
- m_wipe_tower_width,
- wipe_area - m_perimeter_width);
+ xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
+ m_wipe_tower_width - m_perimeter_width,
+ (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width
+ : m_wipe_tower_depth-m_perimeter_width));
- PrusaMultiMaterial::Writer writer;
+ PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
writer.set_extrusion_flow(m_extrusion_flow)
- .set_z(m_z_pos)
- .set_layer_height(m_layer_height)
- .set_initial_tool(m_current_tool)
- .append(";--------------------\n"
- "; CP TOOLCHANGE START\n")
- .comment_with_value(" toolchange #", m_num_tool_changes)
- .comment_material(m_material[m_current_tool])
- .append(";--------------------\n")
- .speed_override(100);
-
- xy initial_position = ((m_current_shape == SHAPE_NORMAL) ? cleaning_box.ld : cleaning_box.lu) +
- xy(m_perimeter_width, ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width);
-
- if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
- // Scaffold leaks terribly, reduce leaking by a full retract when going to the wipe tower.
- float initial_retract = ((m_material[m_current_tool] == SCAFF) ? 1.f : 0.5f) * m_retract;
- writer // Lift for a Z hop.
- .z_hop(m_zhop, 7200)
- // Additional retract on move to tower.
- .retract(initial_retract, 3600)
- // Move to a starting position, one perimeter width inside the cleaning box.
- .travel(initial_position, 7200)
- // Unlift for a Z hop.
- .z_hop_reset(7200)
- // Additional retract on move to tower.
- .load(initial_retract, 3600)
- .load(m_retract, 1500);
- } else {
- // Already at the initial position.
- writer.set_initial_position(initial_position);
- }
-
- // adds tag for analyzer
- char buf[32];
- sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
- writer.append(buf);
-
- if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
- // Increase the extruder driver current to allow fast ramming.
- writer.set_extruder_trimpot(750);
- // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
-
- if (tool != (unsigned int)-1) {
- toolchange_Unload(writer, cleaning_box, m_material[m_current_tool],
- m_is_first_layer ? m_first_layer_temperature[tool] : m_temperature[tool]);
- // This is not the last change.
- // Change the tool, set a speed override for soluble and flex materials.
- toolchange_Change(writer, tool, m_material[tool]);
- toolchange_Load(writer, cleaning_box);
- // Wipe the newly loaded filament until the end of the assigned wipe area.
- toolchange_Wipe(writer, cleaning_box, false);
- // Draw a perimeter around cleaning_box and wipe.
- box_coordinates box = cleaning_box;
- if (m_current_shape == SHAPE_REVERSED) {
- std::swap(box.lu, box.ld);
- std::swap(box.ru, box.rd);
- }
- // Draw a perimeter around cleaning_box.
- writer.travel(box.lu, 7000)
- .extrude(box.ld, 3200).extrude(box.rd)
- .extrude(box.ru).extrude(box.lu);
- // Wipe the nozzle.
- //if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
- // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
- writer.travel(box.ru, 7200)
- .travel(box.lu);
- } else
- toolchange_Unload(writer, cleaning_box, m_material[m_current_tool], m_temperature[m_current_tool]);
-
- // Reset the extruder current to a normal value.
- writer.set_extruder_trimpot(550)
- .feedrate(6000)
- .flush_planner_queue()
- .reset_extruder()
- .append("; CP TOOLCHANGE END\n"
- ";------------------\n"
- "\n\n");
-
- ++ m_num_tool_changes;
- m_current_wipe_start_y += wipe_area;
- }
+ .set_z(m_z_pos)
+ .set_initial_tool(m_current_tool)
+ .set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
+ .append(";--------------------\n"
+ "; CP TOOLCHANGE START\n")
+ .comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based
+ .comment_material(m_filpar[m_current_tool].material)
+ .append(";--------------------\n")
+ .speed_override(100);
+
+ xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed);
+ writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
+
+ // Increase the extruder driver current to allow fast ramming.
+ writer.set_extruder_trimpot(750);
+
+ // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
+ if (tool != (unsigned int)-1){ // This is not the last change.
+ toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
+ m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
+ toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
+ toolchange_Load(writer, cleaning_box);
+ writer.travel(writer.x(),writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
+ toolchange_Wipe(writer, cleaning_box, wipe_volume); // Wipe the newly loaded filament until the end of the assigned wipe area.
+ } else
+ toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].temperature);
+
+ ++ m_num_tool_changes;
+ m_depth_traversed += wipe_area;
+
+ if (last_change_in_layer) {// draw perimeter line
+ writer.set_y_shift(m_y_shift);
+ if (m_peters_wipe_tower)
+ writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth);
+ else {
+ writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
+ if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
+ writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
+ }
+ }
+ }
+
+ writer.set_extruder_trimpot(550) // Reset the extruder current to a normal value.
+ .feedrate(6000)
+ .flush_planner_queue()
+ .reset_extruder()
+ .append("; CP TOOLCHANGE END\n"
+ ";------------------\n"
+ "\n\n");
ToolChangeResult result;
- result.print_z = m_z_pos;
- result.layer_height = m_layer_height;
+ result.priming = false;
+ result.print_z = this->m_z_pos;
+ result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
result.elapsed_time = writer.elapsed_time();
result.extrusions = writer.extrusions();
- result.start_pos = writer.start_pos();
- result.end_pos = writer.pos();
+ result.start_pos = writer.start_pos_rotated();
+ result.end_pos = writer.pos_rotated();
return result;
}
-WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(Purpose purpose, bool sideOnly, float y_offset)
+WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset)
{
const box_coordinates wipeTower_box(
- m_wipe_tower_pos,
+ WipeTower::xy(0.f, 0.f),
m_wipe_tower_width,
- m_wipe_area * float(m_max_color_changes) - m_perimeter_width / 2);
+ m_wipe_tower_depth);
- PrusaMultiMaterial::Writer writer;
+ PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
- // Let the writer know the current Z position as a base for Z-hop.
- .set_z(m_z_pos)
- .set_layer_height(m_layer_height)
+ .set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
.set_initial_tool(m_current_tool)
- .append(
- ";-------------------------------------\n"
- "; CP WIPE TOWER FIRST LAYER BRIM START\n");
+ .append(";-------------------------------------\n"
+ "; CP WIPE TOWER FIRST LAYER BRIM START\n");
xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0);
-
- if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
- // Move with Z hop.
- writer.z_hop(m_zhop, 7200)
- .travel(initial_position, 6000)
- .z_hop_reset(7200);
- else
- writer.set_initial_position(initial_position);
-
- // adds tag for analyzer
- char buf[32];
- sprintf(buf, ";%s%d\n", GCodeAnalyzer::Extrusion_Role_Tag.c_str(), erWipeTower);
- writer.append(buf);
-
- if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
- // Prime the extruder 10*m_perimeter_width left along the vertical edge of the wipe tower.
- writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0),
- 1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
-
- // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
- // toolchange_Change(writer, int(tool), m_material[tool]);
-
- if (sideOnly) {
- float x_offset = m_perimeter_width;
- for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
- writer.travel (wipeTower_box.ld + xy(- x_offset, y_offset), 7000)
- .extrude(wipeTower_box.lu + xy(- x_offset, - y_offset), 2100);
- writer.travel(wipeTower_box.rd + xy(x_offset, y_offset), 7000);
- x_offset = m_perimeter_width;
- for (size_t i = 0; i < 4; ++ i, x_offset += m_perimeter_width)
- writer.travel (wipeTower_box.rd + xy(x_offset, y_offset), 7000)
- .extrude(wipeTower_box.ru + xy(x_offset, - y_offset), 2100);
- } else {
- // Extrude 4 rounds of a brim around the future wipe tower.
- box_coordinates box(wipeTower_box);
- //FIXME why is the box shifted in +Y by 0.5f * m_perimeter_width?
- box.translate(0.f, 0.5f * m_perimeter_width);
- box.expand(0.5f * m_perimeter_width);
- for (size_t i = 0; i < 4; ++ i) {
- writer.travel (box.ld, 7000)
- .extrude(box.lu, 2100).extrude(box.ru)
- .extrude(box.rd ).extrude(box.ld);
- box.expand(m_perimeter_width);
- }
- }
-
- if (m_initial_extra_wipe > m_perimeter_width * 1.9f) {
- box_coordinates cleaning_box(
- m_wipe_tower_pos + xy(0.f, 0.5f * m_perimeter_width),
- m_wipe_tower_width,
- m_initial_extra_wipe - m_perimeter_width);
- writer.travel(cleaning_box.ld + xy(m_perimeter_width, 0.5f * m_perimeter_width), 6000);
- // Wipe the newly loaded filament until the end of the assigned wipe area.
- toolchange_Wipe(writer, cleaning_box, true);
- // Draw a perimeter around cleaning_box.
- writer.travel(cleaning_box.lu, 7000)
- .extrude(cleaning_box.ld, 3200).extrude(cleaning_box.rd)
- .extrude(cleaning_box.ru).extrude(cleaning_box.lu);
- m_current_wipe_start_y = m_initial_extra_wipe;
- }
-
- // Move to the front left corner.
- writer.travel(wipeTower_box.ld, 7000);
-
- //if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
- // Wipe along the front edge.
- // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
- writer.travel(wipeTower_box.rd)
- .travel(wipeTower_box.ld);
-
- writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
- ";-----------------------------------\n");
-
- // Mark the brim as extruded.
- m_idx_tool_change_in_layer = 0;
- }
+ writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
+
+ writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower.
+ 1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400);
+
+ // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
+ // Extrude 4 rounds of a brim around the future wipe tower.
+ box_coordinates box(wipeTower_box);
+ box.expand(m_perimeter_width);
+ for (size_t i = 0; i < 4; ++ i) {
+ writer.travel (box.ld, 7000)
+ .extrude(box.lu, 2100).extrude(box.ru)
+ .extrude(box.rd ).extrude(box.ld);
+ box.expand(m_perimeter_width);
+ }
+
+ writer.travel(wipeTower_box.ld, 7000); // Move to the front left corner.
+ writer.travel(wipeTower_box.rd) // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
+ .travel(wipeTower_box.ld);
+ writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
+ ";-----------------------------------\n");
+
+ m_print_brim = false; // Mark the brim as extruded
ToolChangeResult result;
- result.print_z = m_z_pos;
- result.layer_height = m_layer_height;
+ result.priming = false;
+ result.print_z = this->m_z_pos;
+ result.layer_height = this->m_layer_height;
result.gcode = writer.gcode();
result.elapsed_time = writer.elapsed_time();
result.extrusions = writer.extrusions();
- result.start_pos = writer.start_pos();
- result.end_pos = writer.pos();
+ result.start_pos = writer.start_pos_rotated();
+ result.end_pos = writer.pos_rotated();
return result;
}
+
+
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
void WipeTowerPrusaMM::toolchange_Unload(
PrusaMultiMaterial::Writer &writer,
@@ -735,87 +704,146 @@ void WipeTowerPrusaMM::toolchange_Unload(
const material_type current_material,
const int new_temperature)
{
- float xl = cleaning_box.ld.x + 0.5f * m_perimeter_width;
- float xr = cleaning_box.rd.x - 0.5f * m_perimeter_width;
- float y_step = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width;
-
- writer.append("; CP TOOLCHANGE UNLOAD\n");
-
- // Ram the hot material out of the extruder melt zone.
- // Current extruder position is on the left, one perimeter inside the cleaning box in both X and Y.
- float e0 = m_perimeter_width * m_extrusion_flow;
- float e = (xr - xl) * m_extrusion_flow;
- switch (current_material)
- {
- case ABS:
- // ramming start end y increment amount feedrate
- writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 0.2f, 0, 1.2f * e, 4000)
- .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.2f, e0, 1.6f * e, 4600)
- .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.8f * e, 5000)
- .ram(xr - m_perimeter_width * 2, xl + m_perimeter_width * 2, y_step * 1.2f, e0, 1.8f * e, 5000);
- break;
- case PVA:
- // Used for the PrimaSelect PVA
- writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 0.2f, 0, 1.75f * e, 4000)
- .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.5f, 0, 1.75f * e, 4500)
- .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.5f, 0, 1.75f * e, 4800)
- .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.5f, 0, 1.75f * e, 5000);
- break;
- case SCAFF:
- writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 2.f, 0, 1.75f * e, 4000)
- .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 3.f, 0, 2.34f * e, 4600)
- .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 3.f, 0, 2.63f * e, 5200);
- break;
- default:
- // PLA, PLA/PHA and others
- // Used for the Verbatim BVOH, PET, NGEN, co-polyesters
- writer.ram(xl + m_perimeter_width * 2, xr - m_perimeter_width, y_step * 0.2f, 0, 1.60f * e, 4000)
- .ram(xr - m_perimeter_width, xl + m_perimeter_width, y_step * 1.2f, e0, 1.65f * e, 4600)
- .ram(xl + m_perimeter_width * 2, xr - m_perimeter_width * 2, y_step * 1.2f, e0, 1.74f * e, 5200);
+ float xl = cleaning_box.ld.x + 1.f * m_perimeter_width;
+ float xr = cleaning_box.rd.x - 1.f * m_perimeter_width;
+
+ const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness
+ const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm
+
+ writer.append("; CP TOOLCHANGE UNLOAD\n")
+ .change_analyzer_line_width(line_width);
+
+ unsigned i = 0; // iterates through ramming_speed
+ m_left_to_right = true; // current direction of ramming
+ float remaining = xr - xl ; // keeps track of distance to the next turnaround
+ float e_done = 0; // measures E move done from each segment
+
+ writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position
+
+ // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower:
+ if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) {
+
+ // this is y of the center of previous sparse infill border
+ float sparse_beginning_y = 0.f;
+ if (m_current_shape == SHAPE_REVERSED)
+ sparse_beginning_y += ((m_layer_info-1)->depth - (m_layer_info-1)->toolchanges_depth())
+ - ((m_layer_info)->depth-(m_layer_info)->toolchanges_depth()) ;
+ else
+ sparse_beginning_y += (m_layer_info-1)->toolchanges_depth() + m_perimeter_width;
+
+ //debugging:
+ /* float oldx = writer.x();
+ float oldy = writer.y();
+ writer.travel(xr,sparse_beginning_y);
+ writer.extrude(xr+5,writer.y());
+ writer.travel(oldx,oldy);*/
+
+ float sum_of_depths = 0.f;
+ for (const auto& tch : m_layer_info->tool_changes) { // let's find this toolchange
+ if (tch.old_tool == m_current_tool) {
+ sum_of_depths += tch.ramming_depth;
+ float ramming_end_y = sum_of_depths;
+ ramming_end_y -= (y_step/m_extra_spacing-m_perimeter_width) / 2.f; // center of final ramming line
+
+ // debugging:
+ /*float oldx = writer.x();
+ float oldy = writer.y();
+ writer.travel(xl,ramming_end_y);
+ writer.extrude(xl-15,writer.y());
+ writer.travel(oldx,oldy);*/
+
+ if ( (m_current_shape == SHAPE_REVERSED && ramming_end_y < sparse_beginning_y - 0.5f*m_perimeter_width ) ||
+ (m_current_shape == SHAPE_NORMAL && ramming_end_y > sparse_beginning_y + 0.5f*m_perimeter_width ) )
+ {
+ writer.extrude(xl + tch.first_wipe_line-1.f*m_perimeter_width,writer.y());
+ remaining -= tch.first_wipe_line-1.f*m_perimeter_width;
+ }
+ break;
+ }
+ sum_of_depths += tch.required_depth;
+ }
+ }
+
+ // now the ramming itself:
+ while (i < m_filpar[m_current_tool].ramming_speed.size())
+ {
+ const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height);
+ const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move;
+ const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround
+ const float actual_time = dist/x * 0.25;
+ writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.));
+ remaining -= dist;
+
+ if (remaining < WT_EPSILON) { // we reached a turning point
+ writer.travel(writer.x(), writer.y() + y_step, 7200);
+ m_left_to_right = !m_left_to_right;
+ remaining = xr - xl;
+ }
+ e_done += dist; // subtract what was actually done
+ if (e_done > x - WT_EPSILON) { // current segment finished
+ ++i;
+ e_done = 0;
+ }
}
-
- // Pull the filament end into a cooling tube.
- writer.retract(15, 5000).retract(50, 5400).retract(15, 3000).retract(12, 2000);
-
- if (new_temperature != 0)
- // Set the extruder temperature, but don't wait.
+ WipeTower::xy end_of_ramming(writer.x(),writer.y());
+ writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier
+
+ // Retraction:
+ float old_x = writer.x();
+ float turning_point = (!m_left_to_right ? xl : xr );
+ float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming
+ writer.suppress_preview()
+ .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s
+ .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f)
+ .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f)
+ .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f)
+
+ /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed
+ .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed)
+ .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed)
+ .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed)
+ .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/
+ .resume_preview();
+
+ if (new_temperature != 0 && new_temperature != m_old_temperature ) { // Set the extruder temperature, but don't wait.
writer.set_extruder_temp(new_temperature, false);
-
- // In case the current print head position is closer to the left edge, reverse the direction.
- if (std::abs(writer.x() - xl) < std::abs(writer.x() - xr))
- std::swap(xl, xr);
- // Horizontal cooling moves will be performed at the following Y coordinate:
- writer.travel(xr, writer.y() + y_step * 0.8f, 7200)
- .suppress_preview();
- switch (current_material)
- {
- case PVA:
- writer.cool(xl, xr, 3, -5, 1600)
- .cool(xl, xr, 5, -5, 2000)
- .cool(xl, xr, 5, -5, 2200)
- .cool(xl, xr, 5, -5, 2400)
- .cool(xl, xr, 5, -5, 2400)
- .cool(xl, xr, 5, -3, 2400);
- break;
- case SCAFF:
- writer.cool(xl, xr, 3, -5, 1600)
- .cool(xl, xr, 5, -5, 2000)
- .cool(xl, xr, 5, -5, 2200)
- .cool(xl, xr, 5, -5, 2200)
- .cool(xl, xr, 5, -3, 2400);
- break;
- default:
- writer.cool(xl, xr, 3, -5, 1600)
- .cool(xl, xr, 5, -5, 2000)
- .cool(xl, xr, 5, -5, 2400)
- .cool(xl, xr, 5, -3, 2400);
- }
+ m_old_temperature = new_temperature;
+ }
+
+ // Cooling:
+ const int& number_of_moves = m_filpar[m_current_tool].cooling_moves;
+ if (number_of_moves > 0) {
+ const float& initial_speed = m_filpar[m_current_tool].cooling_initial_speed;
+ const float& final_speed = m_filpar[m_current_tool].cooling_final_speed;
+
+ float speed_inc = (final_speed - initial_speed) / (2.f * number_of_moves - 1.f);
+
+ writer.suppress_preview()
+ .travel(writer.x(), writer.y() + y_step);
+ old_x = writer.x();
+ turning_point = xr-old_x > old_x-xl ? xr : xl;
+ for (int i=0; i<number_of_moves; ++i) {
+ float speed = initial_speed + speed_inc * 2*i;
+ writer.load_move_x_advanced(turning_point, m_cooling_tube_length, speed);
+ speed += speed_inc;
+ writer.load_move_x_advanced(old_x, -m_cooling_tube_length, speed);
+ }
+ }
+
+ // let's wait is necessary:
+ writer.wait(m_filpar[m_current_tool].delay);
+ // we should be at the beginning of the cooling tube again - let's move to parking position:
+ writer.retract(-m_cooling_tube_length/2.f+m_parking_pos_retraction-m_cooling_tube_retraction, 2000);
+
+ // this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start:
+ // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material
+ writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f);
writer.resume_preview()
.flush_planner_queue();
}
-// Change the tool, set a speed override for solube and flex materials.
+// Change the tool, set a speed override for soluble and flex materials.
void WipeTowerPrusaMM::toolchange_Change(
PrusaMultiMaterial::Writer &writer,
const unsigned int new_tool,
@@ -835,219 +863,373 @@ void WipeTowerPrusaMM::toolchange_Change(
m_current_tool = new_tool;
}
+
+
void WipeTowerPrusaMM::toolchange_Load(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box)
-{
- float xl = cleaning_box.ld.x + m_perimeter_width;
- float xr = cleaning_box.rd.x - m_perimeter_width;
- //FIXME flipping left / right side, so that the following toolchange_Wipe will start
- // where toolchange_Load ends.
- std::swap(xl, xr);
-
- writer.append("; CP TOOLCHANGE LOAD\n")
- // Load the filament while moving left / right,
- // so the excess material will not create a blob at a single position.
+{
+ float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f;
+ float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f;
+ float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position
+
+ // Load the filament while moving left / right, so the excess material will not create a blob at a single position.
+ float turning_point = ( oldx-xl < xr-oldx ? xr : xl );
+ float edist = m_parking_pos_retraction+m_extra_loading_move;
+
+ writer.append("; CP TOOLCHANGE LOAD\n")
.suppress_preview()
- // Accelerate the filament loading
- .load_move_x(xr, 20, 1400)
- // Fast loading phase
- .load_move_x(xl, 40, 3000)
- // Slowing down
- .load_move_x(xr, 20, 1600)
- .load_move_x(xl, 10, 1000)
- .resume_preview();
+ /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration
+ .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
+ .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
- // Extrude first five lines (just three lines if colorInit is set).
- writer.extrude(xr, writer.y(), 1600);
- bool colorInit = false;
- size_t pass = colorInit ? 1 : 2;
- float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * 0.85f;
- for (int i = 0; i < pass; ++ i) {
- writer.travel (xr, writer.y() + dy, 7200);
- writer.extrude(xl, writer.y(), 2200);
- writer.travel (xl, writer.y() + dy, 7200);
- writer.extrude(xr, writer.y(), 2200);
- }
+ .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start)
+ .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase
+ .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/
+
+ .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate
+ .resume_preview();
// Reset the extruder current to the normal value.
writer.set_extruder_trimpot(550);
}
+
+
+
// Wipe the newly loaded filament until the end of the assigned wipe area.
void WipeTowerPrusaMM::toolchange_Wipe(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
- bool skip_initial_y_move)
+ float wipe_volume)
{
// Increase flow on first layer, slow down print.
writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f))
.append("; CP TOOLCHANGE WIPE\n");
float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
- float xl = cleaning_box.ld.x + 2.f * m_perimeter_width;
- float xr = cleaning_box.rd.x - 2.f * m_perimeter_width;
- // Wipe speed will increase up to 4800.
- float wipe_speed = 4200.f;
- float wipe_speed_inc = 50.f;
- float wipe_speed_max = 4800.f;
- // Y increment per wipe line.
- float dy = ((m_current_shape == SHAPE_NORMAL) ? 1.f : -1.f) * m_perimeter_width * 0.8f;
- for (bool p = true;
- // Next wipe line fits the cleaning box.
- ((m_current_shape == SHAPE_NORMAL) ?
- (writer.y() <= cleaning_box.lu.y - m_perimeter_width) :
- (writer.y() >= cleaning_box.ld.y + m_perimeter_width));
- p = ! p)
- {
- wipe_speed = std::min(wipe_speed_max, wipe_speed + wipe_speed_inc);
- if (skip_initial_y_move)
- skip_initial_y_move = false;
+ const float& xl = cleaning_box.ld.x;
+ const float& xr = cleaning_box.rd.x;
+
+
+ // Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least
+ // the ordered volume, even if it means violating the box. This can later be removed and simply
+ // wipe until the end of the assigned area.
+
+ float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height);
+ float dy = m_extra_spacing*m_perimeter_width;
+ float wipe_speed = 1600.f;
+
+ // if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
+ if ((m_left_to_right ? xr-writer.x() : writer.x()-xl) < 2.5f*m_perimeter_width) {
+ writer.travel((m_left_to_right ? xr-m_perimeter_width : xl+m_perimeter_width),writer.y()+dy);
+ m_left_to_right = !m_left_to_right;
+ }
+
+
+ // now the wiping itself:
+ for (int i = 0; true; ++i) {
+ if (i!=0) {
+ if (wipe_speed < 1610.f) wipe_speed = 1800.f;
+ else if (wipe_speed < 1810.f) wipe_speed = 2200.f;
+ else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
+ else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
+ }
+
+ float traversed_x = writer.x();
+ if (m_left_to_right)
+ writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
else
- writer.extrude(xl - (p ? m_perimeter_width / 2 : m_perimeter_width), writer.y() + dy, wipe_speed * wipe_coeff);
- writer.extrude(xr + (p ? m_perimeter_width : m_perimeter_width * 2), writer.y(), wipe_speed * wipe_coeff);
- // Next wipe line fits the cleaning box.
- if ((m_current_shape == SHAPE_NORMAL) ?
- (writer.y() > cleaning_box.lu.y - m_perimeter_width) :
- (writer.y() < cleaning_box.ld.y + m_perimeter_width))
+ writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
+
+ if (writer.y()+EPSILON > cleaning_box.lu.y-0.5f*m_perimeter_width)
+ break; // in case next line would not fit
+
+ traversed_x -= writer.x();
+ x_to_wipe -= fabs(traversed_x);
+ if (x_to_wipe < WT_EPSILON) {
+ writer.travel(m_left_to_right ? xl + 1.5*m_perimeter_width : xr - 1.5*m_perimeter_width, writer.y(), 7200);
break;
- wipe_speed = std::min(wipe_speed_max, wipe_speed + wipe_speed_inc);
- writer.extrude(xr + m_perimeter_width, writer.y() + dy, wipe_speed * wipe_coeff);
- writer.extrude(xl - m_perimeter_width, writer.y());
+ }
+ // stepping to the next line:
+ writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5*m_perimeter_width, writer.y() + dy);
+ m_left_to_right = !m_left_to_right;
}
- // Reset the extrusion flow.
- writer.set_extrusion_flow(m_extrusion_flow);
+
+ // this is neither priming nor not the last toolchange on this layer - we are going back to the model - wipe the nozzle
+ if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
+ m_left_to_right = !m_left_to_right;
+ writer.travel(writer.x(), writer.y() - dy)
+ .travel(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y());
+ }
+
+ writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
}
-WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer(Purpose purpose)
+
+
+
+WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer()
{
// This should only be called if the layer is not finished yet.
// Otherwise the caller would likely travel to the wipe tower in vain.
assert(! this->layer_finished());
- PrusaMultiMaterial::Writer writer;
+ PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width);
writer.set_extrusion_flow(m_extrusion_flow)
- .set_z(m_z_pos)
- .set_layer_height(m_layer_height)
- .set_initial_tool(m_current_tool)
- .append(";--------------------\n"
- "; CP EMPTY GRID START\n")
- // m_num_layer_changes is incremented by set_z, so it is 1 based.
- .comment_with_value(" layer #", m_num_layer_changes - 1);
+ .set_z(m_z_pos)
+ .set_initial_tool(m_current_tool)
+ .set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
+ .append(";--------------------\n"
+ "; CP EMPTY GRID START\n")
+ .comment_with_value(" layer #", m_num_layer_changes + 1);
// Slow down on the 1st layer.
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
+ float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
+ box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width),
+ m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
+
+
+ writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
+ m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
+
+ box_coordinates box = fill_box;
+ for (int i=0;i<2;++i) {
+ if (m_layer_info->toolchanges_depth() < WT_EPSILON) { // there were no toolchanges on this layer
+ if (i==0) box.expand(m_perimeter_width);
+ else box.expand(-m_perimeter_width);
+ }
+ else i=2; // only draw the inner perimeter, outer has been already drawn by tool_change(...)
+ writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y,2900*speed_factor);
+ }
+
+ // we are in one of the corners, travel to ld along the perimeter:
+ if (writer.x() > fill_box.ld.x+EPSILON) writer.travel(fill_box.ld.x,writer.y());
+ if (writer.y() > fill_box.ld.y+EPSILON) writer.travel(writer.x(),fill_box.ld.y);
+
+ if (m_is_first_layer && m_adhesion) {
+ // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
+ box.expand(-m_perimeter_width/2.f);
+ int nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width)));
+ float step = (box.lu.y - box.ld.y) / nsteps;
+ writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f));
+ if (nsteps >= 0)
+ for (int i = 0; i < nsteps; ++i) {
+ writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step);
+ writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y());
+ writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
+ writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y());
+ }
+ writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle
+ }
+ else { // Extrude a sparse infill to support the material to be printed above.
+ const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width);
+ const float left = fill_box.lu.x+2*m_perimeter_width;
+ const float right = fill_box.ru.x - 2 * m_perimeter_width;
+ if (dy > m_perimeter_width)
+ {
+ // Extrude an inverse U at the left of the region.
+ writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f))
+ .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
+
+ const int n = 1+(right-left)/(m_bridging);
+ const float dx = (right-left)/n;
+ for (int i=1;i<=n;++i) {
+ float x=left+dx*i;
+ writer.travel(x,writer.y());
+ writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y);
+ }
+ writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
+ }
+ else
+ writer.travel(right,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower
+ }
+ writer.append("; CP EMPTY GRID END\n"
+ ";------------------\n\n\n\n\n\n\n");
+
+ m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
- box_coordinates fill_box(m_wipe_tower_pos + xy(0.f, m_current_wipe_start_y),
- m_wipe_tower_width, float(m_max_color_changes) * m_wipe_area - m_current_wipe_start_y);
- fill_box.expand(0.f, - 0.5f * m_perimeter_width);
- {
- float firstLayerOffset = 0.f;
- fill_box.ld.y += firstLayerOffset;
- fill_box.rd.y += firstLayerOffset;
+ ToolChangeResult result;
+ result.priming = false;
+ result.print_z = this->m_z_pos;
+ result.layer_height = this->m_layer_height;
+ result.gcode = writer.gcode();
+ result.elapsed_time = writer.elapsed_time();
+ result.extrusions = writer.extrusions();
+ result.start_pos = writer.start_pos_rotated();
+ result.end_pos = writer.pos_rotated();
+ return result;
+}
+
+// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
+void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
+{
+ assert(m_plan.back().z <= z_par + WT_EPSILON ); // refuses to add a layer below the last one
+
+ if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
+ m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
+
+ if (brim) { // this toolchange prints brim - we must add it to m_plan, but not to count its depth
+ m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool));
+ return;
}
- if (purpose == PURPOSE_MOVE_TO_TOWER || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
- if (m_idx_tool_change_in_layer == 0) {
- // There were no tool changes at all in this layer.
- writer.retract(m_retract * 1.5f, 3600)
- // Jump with retract to fill_box.ld + a random shift in +x.
- .z_hop(m_zhop, 7200)
- .travel(fill_box.ld + xy(5.f + 15.f * float(rand()) / RAND_MAX, 0.f), 7000)
- .z_hop_reset(7200)
- // Prime the extruder.
- .load_move_x(fill_box.ld.x, m_retract * 1.5f, 3600);
- } else {
- // Otherwise the extruder is already over the wipe tower.
+ if (old_tool==new_tool) // new layer without toolchanges - we are done
+ return;
+
+ // this is an actual toolchange - let's calculate depth to reserve on the wipe tower
+ float depth = 0.f;
+ float width = m_wipe_tower_width - 3*m_perimeter_width;
+ float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
+ m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
+ layer_height_par);
+ depth = (int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator);
+ float ramming_depth = depth;
+ length_to_extrude = width*((length_to_extrude / width)-int(length_to_extrude / width)) - width;
+ float first_wipe_line = -length_to_extrude;
+ length_to_extrude += volume_to_length(wipe_volume, m_perimeter_width, layer_height_par);
+ length_to_extrude = std::max(length_to_extrude,0.f);
+
+ depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
+ depth *= m_extra_spacing;
+
+ m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, depth, ramming_depth, first_wipe_line, wipe_volume));
+}
+
+
+
+void WipeTowerPrusaMM::plan_tower()
+{
+ // Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards
+ m_wipe_tower_depth = 0.f;
+ for (auto& layer : m_plan)
+ layer.depth = 0.f;
+
+ for (int layer_index = m_plan.size() - 1; layer_index >= 0; --layer_index)
+ {
+ float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth());
+ m_plan[layer_index].depth = this_layer_depth;
+
+ if (this_layer_depth > m_wipe_tower_depth - m_perimeter_width)
+ m_wipe_tower_depth = this_layer_depth + m_perimeter_width;
+
+ for (int i = layer_index - 1; i >= 0 ; i--)
+ {
+ if (m_plan[i].depth - this_layer_depth < 2*m_perimeter_width )
+ m_plan[i].depth = this_layer_depth;
}
- } else {
- // The print head is inside the wipe tower. Rather move to the start of the following extrusion.
- // writer.set_initial_position(fill_box.ld);
- writer.set_initial_position(fill_box.ld);
}
+}
- if (purpose == PURPOSE_EXTRUDE || purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE) {
- // Extrude the first perimeter.
- box_coordinates box = fill_box;
- writer.extrude(box.lu, 2400 * speed_factor)
- .extrude(box.ru)
- .extrude(box.rd)
- .extrude(box.ld + xy(m_perimeter_width / 2, 0));
-
- // Extrude second perimeter.
- box.expand(- m_perimeter_width / 2);
- writer.extrude(box.lu, 3200 * speed_factor)
- .extrude(box.ru)
- .extrude(box.rd)
- .extrude(box.ld + xy(m_perimeter_width / 2, 0));
-
- if (m_is_first_layer) {
- // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
- box.expand(- m_perimeter_width / 2);
- box.ld.y -= 0.5f * m_perimeter_width;
- box.rd.y = box.ld.y;
- int nsteps = int(floor((box.lu.y - box.ld.y) / (2. * (1.0 * m_perimeter_width))));
- float step = (box.lu.y - box.ld.y) / nsteps;
- for (size_t i = 0; i < nsteps; ++ i) {
- writer.extrude(box.ld.x, writer.y() + 0.5f * step);
- writer.extrude(box.rd.x, writer.y());
- writer.extrude(box.rd.x, writer.y() + 0.5f * step);
- writer.extrude(box.ld.x, writer.y());
- }
- } else {
- // Extrude a sparse infill to support the material to be printed above.
-
- // Extrude an inverse U at the left of the region.
- writer.extrude(box.ld + xy(m_perimeter_width / 2, m_perimeter_width / 2))
- .extrude(fill_box.ld + xy(m_perimeter_width * 3, m_perimeter_width), 2900 * speed_factor)
- .extrude(fill_box.lu + xy(m_perimeter_width * 3, - m_perimeter_width))
- .extrude(fill_box.lu + xy(m_perimeter_width * 6, - m_perimeter_width))
- .extrude(fill_box.ld + xy(m_perimeter_width * 6, m_perimeter_width));
-
- if (fill_box.lu.y - fill_box.ld.y > 4.f) {
- // Extrude three zig-zags.
- float step = (m_wipe_tower_width - m_perimeter_width * 12.f) / 12.f;
- for (size_t i = 0; i < 3; ++ i) {
- writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width * 8, 3200 * speed_factor);
- writer.extrude(writer.x() , fill_box.lu.y - m_perimeter_width * 8);
- writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width );
- writer.extrude(writer.x() + step, fill_box.lu.y - m_perimeter_width * 8);
- writer.extrude(writer.x() , fill_box.ld.y + m_perimeter_width * 8);
- writer.extrude(writer.x() + step, fill_box.ld.y + m_perimeter_width );
- }
- }
+void WipeTowerPrusaMM::save_on_last_wipe()
+{
+ for (m_layer_info=m_plan.begin();m_layer_info<m_plan.end();++m_layer_info) {
+ set_layer(m_layer_info->z, m_layer_info->height, 0, m_layer_info->z == m_plan.front().z, m_layer_info->z == m_plan.back().z);
+ if (m_layer_info->tool_changes.size()==0) // we have no way to save anything on an empty layer
+ continue;
- // Extrude an inverse U at the left of the region.
- writer.extrude(fill_box.ru + xy(- m_perimeter_width * 6, - m_perimeter_width), 2900 * speed_factor)
- .extrude(fill_box.ru + xy(- m_perimeter_width * 3, - m_perimeter_width))
- .extrude(fill_box.rd + xy(- m_perimeter_width * 3, m_perimeter_width))
- .extrude(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width));
- }
+ for (const auto &toolchange : m_layer_info->tool_changes)
+ tool_change(toolchange.new_tool, false);
+
+ float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
+ float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
+ float length_to_wipe = volume_to_length(m_layer_info->tool_changes.back().wipe_volume,
+ m_perimeter_width,m_layer_info->height) - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
+
+ length_to_wipe = std::max(length_to_wipe,0.f);
+ float depth_to_wipe = m_perimeter_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
- // if (purpose == PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE)
- if (true)
- // Wipe along the front side of the current wiping box.
- // Always wipe the nozzle with a long wipe to reduce stringing when moving away from the wipe tower.
- writer.travel(fill_box.ld + xy( m_perimeter_width, m_perimeter_width / 2), 7200)
- .travel(fill_box.rd + xy(- m_perimeter_width, m_perimeter_width / 2));
+ //depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
+ m_layer_info->tool_changes.back().required_depth = m_layer_info->tool_changes.back().ramming_depth + depth_to_wipe;
+ }
+}
+
+// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
+// Resulting ToolChangeResults are appended into vector "result"
+void WipeTowerPrusaMM::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
+{
+ if (m_plan.empty())
+
+ return;
+
+ m_extra_spacing = 1.f;
+
+ plan_tower();
+ for (int i=0;i<5;++i) {
+ save_on_last_wipe();
+ plan_tower();
+ }
+
+ if (m_peters_wipe_tower)
+ make_wipe_tower_square();
+
+ m_layer_info = m_plan.begin();
+ m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange
+
+ std::vector<WipeTower::ToolChangeResult> layer_result;
+ for (auto layer : m_plan)
+ {
+ set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
+ if (m_peters_wipe_tower)
+ m_internal_rotation += 90.f;
else
- writer.feedrate(7200);
+ m_internal_rotation += 180.f;
+
+ if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
+ m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
+
+ for (const auto &toolchange : layer.tool_changes) {
+ if (m_current_tool == (unsigned int)(-2))
+ m_current_tool = toolchange.old_tool;
+ layer_result.emplace_back(tool_change(toolchange.new_tool, false));
+ }
+
+ if (! layer_finished()) {
+ auto finish_layer_toolchange = finish_layer();
+ if ( ! layer.tool_changes.empty() ) { // we will merge it to the last toolchange
+ auto& last_toolchange = layer_result.back();
+ if (last_toolchange.end_pos != finish_layer_toolchange.start_pos) {
+ char buf[2048]; // Add a travel move from tc1.end_pos to tc2.start_pos.
+ sprintf(buf, "G1 X%.3f Y%.3f F7200\n", finish_layer_toolchange.start_pos.x, finish_layer_toolchange.start_pos.y);
+ last_toolchange.gcode += buf;
+ }
+ last_toolchange.gcode += finish_layer_toolchange.gcode;
+ last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
+ last_toolchange.end_pos = finish_layer_toolchange.end_pos;
+ }
+ else
+ layer_result.emplace_back(std::move(finish_layer_toolchange));
+ }
+
+ result.emplace_back(std::move(layer_result));
+ m_is_first_layer = false;
+ }
+}
- writer.append("; CP EMPTY GRID END\n"
- ";------------------\n\n\n\n\n\n\n");
+void WipeTowerPrusaMM::make_wipe_tower_square()
+{
+ const float width = m_wipe_tower_width - 3 * m_perimeter_width;
+ const float depth = m_wipe_tower_depth - m_perimeter_width;
+ // area that we actually print into is width*depth
+ float side = sqrt(depth * width);
+
+ m_wipe_tower_width = side + 3 * m_perimeter_width;
+ m_wipe_tower_depth = side + 2 * m_perimeter_width;
+ // For all layers, find how depth changed and update all toolchange depths
+ for (auto &lay : m_plan)
+ {
+ side = sqrt(lay.depth * width);
+ float width_ratio = width / side;
- // Indicate that this wipe tower layer is fully covered.
- m_idx_tool_change_in_layer = (unsigned int)m_max_color_changes;
+ //lay.extra_spacing = width_ratio;
+ for (auto &tch : lay.tool_changes)
+ tch.required_depth *= width_ratio;
}
- ToolChangeResult result;
- result.print_z = m_z_pos;
- result.layer_height = m_layer_height;
- result.gcode = writer.gcode();
- result.elapsed_time = writer.elapsed_time();
- result.extrusions = writer.extrusions();
- result.start_pos = writer.start_pos();
- result.end_pos = writer.pos();
- return result;
+ plan_tower(); // propagates depth downwards again (width has changed)
+ for (auto& lay : m_plan) // depths set, now the spacing
+ lay.extra_spacing = lay.depth / lay.toolchanges_depth();
}
}; // namespace Slic3r
diff --git a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
index b8c7ab31f..305dbc40a 100644
--- a/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
+++ b/xs/src/libslic3r/GCode/WipeTowerPrusaMM.hpp
@@ -1,13 +1,15 @@
#ifndef WipeTowerPrusaMM_hpp_
#define WipeTowerPrusaMM_hpp_
-#include <algorithm>
#include <cmath>
#include <string>
+#include <sstream>
#include <utility>
+#include <algorithm>
#include "WipeTower.hpp"
+
namespace Slic3r
{
@@ -15,6 +17,8 @@ namespace PrusaMultiMaterial {
class Writer;
};
+
+
class WipeTowerPrusaMM : public WipeTower
{
public:
@@ -39,65 +43,103 @@ public:
// y -- y coordinates of wipe tower in mm ( left bottom corner )
// width -- width of wipe tower in mm ( default 60 mm - leave as it is )
// wipe_area -- space available for one toolchange in mm
- WipeTowerPrusaMM(float x, float y, float width, float wipe_area, unsigned int initial_tool) :
+ WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction,
+ float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging,
+ const std::vector<std::vector<float>>& wiping_matrix, unsigned int initial_tool) :
m_wipe_tower_pos(x, y),
m_wipe_tower_width(width),
- m_wipe_area(wipe_area),
+ m_wipe_tower_rotation_angle(rotation_angle),
+ m_y_shift(0.f),
m_z_pos(0.f),
- m_current_tool(initial_tool)
- {
- for (size_t i = 0; i < 4; ++ i) {
- // Extruder specific parameters.
- m_material[i] = PLA;
- m_temperature[i] = 0;
- m_first_layer_temperature[i] = 0;
- }
- }
+ m_is_first_layer(false),
+ m_cooling_tube_retraction(cooling_tube_retraction),
+ m_cooling_tube_length(cooling_tube_length),
+ m_parking_pos_retraction(parking_pos_retraction),
+ m_extra_loading_move(extra_loading_move),
+ m_bridging(bridging),
+ m_current_tool(initial_tool),
+ wipe_volumes(wiping_matrix)
+ {}
virtual ~WipeTowerPrusaMM() {}
- // _retract - retract value in mm
- void set_retract(float retract) { m_retract = retract; }
-
- // _zHop - z hop value in mm
- void set_zhop(float zhop) { m_zhop = zhop; }
// Set the extruder properties.
- void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp)
+ void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start,
+ float unloading_speed, float unloading_speed_start, float delay, int cooling_moves,
+ float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter)
{
- m_material[idx] = material;
- m_temperature[idx] = temp;
- m_first_layer_temperature[idx] = first_layer_temp;
+ //while (m_filpar.size() < idx+1) // makes sure the required element is in the vector
+ m_filpar.push_back(FilamentParameters());
+
+ m_filpar[idx].material = material;
+ m_filpar[idx].temperature = temp;
+ m_filpar[idx].first_layer_temperature = first_layer_temp;
+ m_filpar[idx].loading_speed = loading_speed;
+ m_filpar[idx].loading_speed_start = loading_speed_start;
+ m_filpar[idx].unloading_speed = unloading_speed;
+ m_filpar[idx].unloading_speed_start = unloading_speed_start;
+ m_filpar[idx].delay = delay;
+ m_filpar[idx].cooling_moves = cooling_moves;
+ m_filpar[idx].cooling_initial_speed = cooling_initial_speed;
+ m_filpar[idx].cooling_final_speed = cooling_final_speed;
+ m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM
+
+ m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter
+
+ std::stringstream stream{ramming_parameters};
+ float speed = 0.f;
+ stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator;
+ m_filpar[idx].ramming_line_width_multiplicator /= 100;
+ m_filpar[idx].ramming_step_multiplicator /= 100;
+ while (stream >> speed)
+ m_filpar[idx].ramming_speed.push_back(speed);
}
+
+ // Appends into internal structure m_plan containing info about the future wipe tower
+ // to be used before building begins. The entries must be added ordered in z.
+ void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
+
+ // Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
+ void generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result);
+
+ float get_depth() const { return m_wipe_tower_depth; }
+
+
+
// Switch to a next layer.
virtual void set_layer(
// Print height of this layer.
- float print_z,
- // Layer height, used to calculate extrusion the rate.
- float layer_height,
+ float print_z,
+ // Layer height, used to calculate extrusion the rate.
+ float layer_height,
// Maximum number of tool changes on this layer or the layers below.
- size_t max_tool_changes,
+ size_t max_tool_changes,
// Is this the first layer of the print? In that case print the brim first.
- bool is_first_layer,
+ bool is_first_layer,
// Is this the last layer of the waste tower?
- bool is_last_layer)
+ bool is_last_layer)
{
m_z_pos = print_z;
m_layer_height = layer_height;
- m_max_color_changes = max_tool_changes;
m_is_first_layer = is_first_layer;
- m_is_last_layer = is_last_layer;
- // Start counting the color changes from zero. Special case: -1 - extrude a brim first.
- m_idx_tool_change_in_layer = is_first_layer ? (unsigned int)(-1) : 0;
- m_current_wipe_start_y = 0.f;
+ m_print_brim = is_first_layer;
+ m_depth_traversed = 0.f;
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
- ++ m_num_layer_changes;
- // Extrusion rate for an extrusion aka perimeter width 0.35mm.
- // Clamp the extrusion height to a 0.2mm layer height, independent of the nozzle diameter.
-// m_extrusion_flow = std::min(0.2f, layer_height) * 0.145f;
- // Use a strictly
- m_extrusion_flow = layer_height * 0.145f;
+ if (is_first_layer) {
+ this->m_num_layer_changes = 0;
+ this->m_num_tool_changes = 0;
+ }
+ else
+ ++ m_num_layer_changes;
+
+ // Calculate extrusion flow from desired line width, nozzle diameter, filament diameter and layer_height:
+ m_extrusion_flow = extrusion_flow(layer_height);
+
+ // Advance m_layer_info iterator, making sure we got it right
+ while (!m_plan.empty() && m_layer_info->z < print_z - WT_EPSILON && m_layer_info+1 != m_plan.end())
+ ++m_layer_info;
}
// Return the wipe tower position.
@@ -115,79 +157,120 @@ public:
const std::vector<unsigned int> &tools,
// If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower.
// If false, the last priming are will be large enough to wipe the last extruder sufficiently.
- bool last_wipe_inside_wipe_tower,
- // May be used by a stand alone post processor.
- Purpose purpose = PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE);
+ bool last_wipe_inside_wipe_tower);
// Returns gcode for a toolchange and a final print head position.
// On the first layer, extrude a brim around the future wipe tower first.
- virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer, Purpose purpose);
+ virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer);
- // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag.
+ // Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
- virtual ToolChangeResult finish_layer(Purpose purpose);
+ virtual ToolChangeResult finish_layer();
+
+ // Is the current layer finished?
+ virtual bool layer_finished() const {
+ return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
+ }
- // Is the current layer finished? A layer is finished if either the wipe tower is finished, or
- // the wipe tower has been completely covered by the tool change extrusions,
- // or the rest of the tower has been filled by a sparse infill with the finish_layer() method.
- virtual bool layer_finished() const
- { return m_idx_tool_change_in_layer == m_max_color_changes; }
private:
WipeTowerPrusaMM();
- // A fill-in direction (positive Y, negative Y) alternates with each layer.
- enum wipe_shape
+ enum wipe_shape // A fill-in direction
{
- SHAPE_NORMAL = 1,
+ SHAPE_NORMAL = 1,
SHAPE_REVERSED = -1
};
- // Left front corner of the wipe tower in mm.
- xy m_wipe_tower_pos;
- // Width of the wipe tower.
- float m_wipe_tower_width;
- // Per color Y span.
- float m_wipe_area;
- // Current Z position.
- float m_z_pos = 0.f;
- // Current layer height.
- float m_layer_height = 0.f;
- // Maximum number of color changes per layer.
- size_t m_max_color_changes = 0;
- // Is this the 1st layer of the print? If so, print the brim around the waste tower.
- bool m_is_first_layer = false;
- // Is this the last layer of this waste tower?
- bool m_is_last_layer = false;
+
+ const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
+ const float Filament_Area = M_PI * 1.75f * 1.75f / 4.f; // filament area in mm^2
+ const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
+ const float WT_EPSILON = 1e-3f;
+
+
+ xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
+ float m_wipe_tower_width; // Width of the wipe tower.
+ float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
+ float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis)
+ float m_internal_rotation = 0.f;
+ float m_y_shift = 0.f; // y shift passed to writer
+ float m_z_pos = 0.f; // Current Z position.
+ float m_layer_height = 0.f; // Current layer height.
+ size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
+ bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
+ int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
// G-code generator parameters.
- float m_zhop = 0.5f;
- float m_retract = 4.f;
- // Width of an extrusion line, also a perimeter spacing for 100% infill.
- float m_perimeter_width = 0.5f;
- // Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
- float m_extrusion_flow = 0.029f;
+ float m_cooling_tube_retraction = 0.f;
+ float m_cooling_tube_length = 0.f;
+ float m_parking_pos_retraction = 0.f;
+ float m_extra_loading_move = 0.f;
+ float m_bridging = 0.f;
+ bool m_adhesion = true;
+
+ float m_perimeter_width = 0.4 * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill.
+ float m_extrusion_flow = 0.038; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter.
+
+
+ struct FilamentParameters {
+ material_type material = PLA;
+ int temperature = 0;
+ int first_layer_temperature = 0;
+ float loading_speed = 0.f;
+ float loading_speed_start = 0.f;
+ float unloading_speed = 0.f;
+ float unloading_speed_start = 0.f;
+ float delay = 0.f ;
+ int cooling_moves = 0;
+ float cooling_initial_speed = 0.f;
+ float cooling_final_speed = 0.f;
+ float ramming_line_width_multiplicator = 0.f;
+ float ramming_step_multiplicator = 0.f;
+ std::vector<float> ramming_speed;
+ float nozzle_diameter;
+ };
// Extruder specific parameters.
- material_type m_material[4];
- int m_temperature[4];
- int m_first_layer_temperature[4];
-
- // State of the wiper tower generator.
- // Layer change counter for the output statistics.
- unsigned int m_num_layer_changes = 0;
- // Tool change change counter for the output statistics.
- unsigned int m_num_tool_changes = 0;
- // Layer change counter in this layer. Counting up to m_max_color_changes.
- unsigned int m_idx_tool_change_in_layer = 0;
+ std::vector<FilamentParameters> m_filpar;
+
+
+ // State of the wipe tower generator.
+ unsigned int m_num_layer_changes = 0; // Layer change counter for the output statistics.
+ unsigned int m_num_tool_changes = 0; // Tool change change counter for the output statistics.
+ ///unsigned int m_idx_tool_change_in_layer = 0; // Layer change counter in this layer. Counting up to m_max_color_changes.
+ bool m_print_brim = true;
// A fill-in direction (positive Y, negative Y) alternates with each layer.
wipe_shape m_current_shape = SHAPE_NORMAL;
unsigned int m_current_tool = 0;
- // Current y position at the wipe tower.
- float m_current_wipe_start_y = 0.f;
- // How much to wipe the 1st extruder over the wipe tower at the 1st layer
- // after the wipe tower brim has been extruded?
- float m_initial_extra_wipe = 0.f;
+ const std::vector<std::vector<float>> wipe_volumes;
+
+ float m_depth_traversed = 0.f; // Current y position at the wipe tower.
+ bool m_left_to_right = true;
+ float m_extra_spacing = 1.f;
+
+ // Calculates extrusion flow needed to produce required line width for given layer height
+ float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
+ {
+ if ( layer_height < 0 )
+ return m_extrusion_flow;
+ return layer_height * ( m_perimeter_width - layer_height * (1-M_PI/4.f)) / Filament_Area;
+ }
+
+ // Calculates length of extrusion line to extrude given volume
+ float volume_to_length(float volume, float line_width, float layer_height) const {
+ return std::max(0., volume / (layer_height * (line_width - layer_height * (1. - M_PI / 4.))));
+ }
+
+ // Calculates depth for all layers and propagates them downwards
+ void plan_tower();
+
+ // Goes through m_plan and recalculates depths and width of the WT to make it exactly square - experimental
+ void make_wipe_tower_square();
+
+ // Goes through m_plan, calculates border and finish_layer extrusions and subtracts them from last wipe
+ void save_on_last_wipe();
+
struct box_coordinates
{
@@ -216,14 +299,43 @@ private:
}
xy ld; // left down
xy lu; // left upper
- xy ru; // right upper
xy rd; // right lower
+ xy ru; // right upper
+ };
+
+
+ // to store information about tool changes for a given layer
+ struct WipeTowerInfo{
+ struct ToolChange {
+ unsigned int old_tool;
+ unsigned int new_tool;
+ float required_depth;
+ float ramming_depth;
+ float first_wipe_line;
+ float wipe_volume;
+ ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f)
+ : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {}
+ };
+ float z; // z position of the layer
+ float height; // layer height
+ float depth; // depth of the layer based on all layers above
+ float extra_spacing;
+ float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
+
+ std::vector<ToolChange> tool_changes;
+
+ WipeTowerInfo(float z_par, float layer_height_par)
+ : z{z_par}, height{layer_height_par}, depth{0}, extra_spacing{1.f} {}
};
+ std::vector<WipeTowerInfo> m_plan; // Stores information about all layers and toolchanges for the future wipe tower (filled by plan_toolchange(...))
+ std::vector<WipeTowerInfo>::iterator m_layer_info = m_plan.end();
+
+
// Returns gcode for wipe tower brim
- // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
+ // sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
- ToolChangeResult toolchange_Brim(Purpose purpose, bool sideOnly = false, float y_offset = 0.f);
+ ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
void toolchange_Unload(
PrusaMultiMaterial::Writer &writer,
@@ -243,11 +355,12 @@ private:
void toolchange_Wipe(
PrusaMultiMaterial::Writer &writer,
const box_coordinates &cleaning_box,
- bool skip_initial_y_move);
-
- void toolchange_Perimeter();
+ float wipe_volume);
};
+
+
+
}; // namespace Slic3r
#endif /* WipeTowerPrusaMM_hpp_ */
diff --git a/xs/src/libslic3r/GCodeReader.cpp b/xs/src/libslic3r/GCodeReader.cpp
index c1e1e98be..51853e9fa 100644
--- a/xs/src/libslic3r/GCodeReader.cpp
+++ b/xs/src/libslic3r/GCodeReader.cpp
@@ -114,6 +114,28 @@ void GCodeReader::parse_file(const std::string &file, callback_t callback)
this->parse_line(line, callback);
}
+bool GCodeReader::GCodeLine::has(char axis) const
+{
+ const char *c = m_raw.c_str();
+ // Skip the whitespaces.
+ c = skip_whitespaces(c);
+ // Skip the command.
+ c = skip_word(c);
+ // Up to the end of line or comment.
+ while (! is_end_of_gcode_line(*c)) {
+ // Skip whitespaces.
+ c = skip_whitespaces(c);
+ if (is_end_of_gcode_line(*c))
+ break;
+ // Check the name of the axis.
+ if (*c == axis)
+ return true;
+ // Skip the rest of the word.
+ c = skip_word(c);
+ }
+ return false;
+}
+
bool GCodeReader::GCodeLine::has_value(char axis, float &value) const
{
const char *c = m_raw.c_str();
diff --git a/xs/src/libslic3r/GCodeReader.hpp b/xs/src/libslic3r/GCodeReader.hpp
index 102cbd27a..84ed89a7c 100644
--- a/xs/src/libslic3r/GCodeReader.hpp
+++ b/xs/src/libslic3r/GCodeReader.hpp
@@ -27,6 +27,7 @@ public:
bool has(Axis axis) const { return (m_mask & (1 << int(axis))) != 0; }
float value(Axis axis) const { return m_axis[axis]; }
+ bool has(char axis) const;
bool has_value(char axis, float &value) const;
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
diff --git a/xs/src/libslic3r/GCodeSender.cpp b/xs/src/libslic3r/GCodeSender.cpp
index bbeaf836d..0988091ce 100644
--- a/xs/src/libslic3r/GCodeSender.cpp
+++ b/xs/src/libslic3r/GCodeSender.cpp
@@ -2,6 +2,7 @@
#include <iostream>
#include <istream>
#include <string>
+#include <thread>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@@ -41,6 +42,7 @@ struct termios2 {
//#define DEBUG_SERIAL
#ifdef DEBUG_SERIAL
+#include <cstdlib>
#include <fstream>
std::fstream fs;
#endif
@@ -52,7 +54,11 @@ namespace Slic3r {
GCodeSender::GCodeSender()
: io(), serial(io), can_send(false), sent(0), open(false), error(false),
connected(false), queue_paused(false)
-{}
+{
+#ifdef DEBUG_SERIAL
+ std::srand(std::time(nullptr));
+#endif
+}
GCodeSender::~GCodeSender()
{
@@ -358,15 +364,23 @@ GCodeSender::on_read(const boost::system::error_code& error,
// extract the first number from line
boost::algorithm::trim_left_if(line, !boost::algorithm::is_digit());
size_t toresend = boost::lexical_cast<size_t>(line.substr(0, line.find_first_not_of("0123456789")));
- ++ toresend; // N is 0-based
- if (toresend >= this->sent - this->last_sent.size() && toresend < this->last_sent.size()) {
+
+#ifdef DEBUG_SERIAL
+ fs << "!! line num out of sync: toresend = " << toresend << ", sent = " << sent << ", last_sent.size = " << last_sent.size() << std::endl;
+#endif
+
+ if (toresend > this->sent - this->last_sent.size() && toresend <= this->sent) {
{
boost::lock_guard<boost::mutex> l(this->queue_mutex);
+ const auto lines_to_resend = this->sent - toresend + 1;
+#ifdef DEBUG_SERIAL
+ fs << "!! resending " << lines_to_resend << " lines" << std::endl;
+#endif
// move the unsent lines to priqueue
this->priqueue.insert(
this->priqueue.begin(), // insert at the beginning
- this->last_sent.begin() + toresend - (this->sent - this->last_sent.size()) - 1,
+ this->last_sent.begin() + this->last_sent.size() - lines_to_resend,
this->last_sent.end()
);
@@ -477,8 +491,14 @@ GCodeSender::do_send()
if (line.empty()) return;
// compute full line
- std::string full_line = "N" + boost::lexical_cast<std::string>(this->sent) + " " + line;
++ this->sent;
+#ifndef DEBUG_SERIAL
+ const auto line_num = this->sent;
+#else
+ // In DEBUG_SERIAL mode, test line re-synchronization by sending bad line number 1/4 of the time
+ const auto line_num = std::rand() < RAND_MAX/4 ? 0 : this->sent;
+#endif
+ std::string full_line = "N" + boost::lexical_cast<std::string>(line_num) + " " + line;
// calculate checksum
int cs = 0;
@@ -497,8 +517,9 @@ GCodeSender::do_send()
this->last_sent.push_back(line);
this->can_send = false;
- if (this->last_sent.size() > KEEP_SENT)
- this->last_sent.erase(this->last_sent.begin(), this->last_sent.end() - KEEP_SENT);
+ while (this->last_sent.size() > KEEP_SENT) {
+ this->last_sent.pop_front();
+ }
// we can't supply boost::asio::buffer(full_line) to async_write() because full_line is on the
// stack and the buffer would lose its underlying storage causing memory corruption
@@ -548,16 +569,12 @@ GCodeSender::set_DTR(bool on)
void
GCodeSender::reset()
{
- this->set_DTR(false);
- boost::this_thread::sleep(boost::posix_time::milliseconds(200));
- this->set_DTR(true);
- boost::this_thread::sleep(boost::posix_time::milliseconds(200));
- this->set_DTR(false);
- boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
- {
- boost::lock_guard<boost::mutex> l(this->queue_mutex);
- this->can_send = true;
- }
+ set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ set_DTR(true);
+ std::this_thread::sleep_for(std::chrono::milliseconds(200));
+ set_DTR(false);
+ std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
} // namespace Slic3r
diff --git a/xs/src/libslic3r/GCodeSender.hpp b/xs/src/libslic3r/GCodeSender.hpp
index 3022993cb..d7663ca55 100644
--- a/xs/src/libslic3r/GCodeSender.hpp
+++ b/xs/src/libslic3r/GCodeSender.hpp
@@ -51,7 +51,7 @@ class GCodeSender : private boost::noncopyable {
bool can_send;
bool queue_paused;
size_t sent;
- std::vector<std::string> last_sent;
+ std::deque<std::string> last_sent;
// this mutex guards log, T, B
mutable boost::mutex log_mutex;
diff --git a/xs/src/libslic3r/GCodeTimeEstimator.cpp b/xs/src/libslic3r/GCodeTimeEstimator.cpp
index 912799ca9..7471367fe 100644
--- a/xs/src/libslic3r/GCodeTimeEstimator.cpp
+++ b/xs/src/libslic3r/GCodeTimeEstimator.cpp
@@ -4,15 +4,20 @@
#include <Shiny/Shiny.h>
+#include <boost/nowide/fstream.hpp>
+#include <boost/nowide/cstdio.hpp>
+#include <boost/algorithm/string/predicate.hpp>
+
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
static const float MILLISEC_TO_SEC = 0.001f;
static const float INCHES_TO_MM = 25.4f;
+
static const float DEFAULT_FEEDRATE = 1500.0f; // from Prusa Firmware (Marlin_main.cpp)
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
static const float DEFAULT_RETRACT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
static const float DEFAULT_AXIS_MAX_FEEDRATE[] = { 500.0f, 500.0f, 12.0f, 120.0f }; // Prusa Firmware 1_75mm_MK2
static const float DEFAULT_AXIS_MAX_ACCELERATION[] = { 9000.0f, 9000.0f, 500.0f, 10000.0f }; // Prusa Firmware 1_75mm_MK2
-static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.2f, 2.5f }; // from Prusa Firmware (Configuration.h)
+static const float DEFAULT_AXIS_MAX_JERK[] = { 10.0f, 10.0f, 0.4f, 2.5f }; // from Prusa Firmware (Configuration.h)
static const float DEFAULT_MINIMUM_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h)
static const float DEFAULT_MINIMUM_TRAVEL_FEEDRATE = 0.0f; // from Prusa Firmware (Configuration_adv.h)
static const float DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE = 1.0f; // 100 percent
@@ -73,6 +78,10 @@ namespace Slic3r {
return ::sqrt(value);
}
+ GCodeTimeEstimator::Block::Block()
+ {
+ }
+
float GCodeTimeEstimator::Block::move_length() const
{
float length = ::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
@@ -159,12 +168,51 @@ namespace Slic3r {
}
#endif // ENABLE_MOVE_STATS
- GCodeTimeEstimator::GCodeTimeEstimator()
+ const std::string GCodeTimeEstimator::Normal_First_M73_Output_Placeholder_Tag = "; NORMAL_FIRST_M73_OUTPUT_PLACEHOLDER";
+ const std::string GCodeTimeEstimator::Silent_First_M73_Output_Placeholder_Tag = "; SILENT_FIRST_M73_OUTPUT_PLACEHOLDER";
+
+ GCodeTimeEstimator::GCodeTimeEstimator(EMode mode)
+ : _mode(mode)
{
reset();
set_default();
}
+ void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
+ {
+ PROFILE_FUNC();
+ _parser.parse_line(gcode_line,
+ [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
+ { this->_process_gcode_line(reader, line); });
+ }
+
+ void GCodeTimeEstimator::add_gcode_block(const char *ptr)
+ {
+ PROFILE_FUNC();
+ GCodeReader::GCodeLine gline;
+ auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
+ { this->_process_gcode_line(reader, line); };
+ for (; *ptr != 0;) {
+ gline.reset();
+ ptr = _parser.parse_line(ptr, gline, action);
+ }
+ }
+
+ void GCodeTimeEstimator::calculate_time(bool start_from_beginning)
+ {
+ PROFILE_FUNC();
+ if (start_from_beginning)
+ {
+ _reset_time();
+ _last_st_synchronized_block_id = -1;
+ }
+ _calculate_time();
+
+#if ENABLE_MOVE_STATS
+ _log_moves_stats();
+#endif // ENABLE_MOVE_STATS
+ }
+
void GCodeTimeEstimator::calculate_time_from_text(const std::string& gcode)
{
reset();
@@ -178,9 +226,6 @@ namespace Slic3r {
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
-
- _reset_blocks();
- _reset();
}
void GCodeTimeEstimator::calculate_time_from_file(const std::string& file)
@@ -193,9 +238,6 @@ namespace Slic3r {
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
-
- _reset_blocks();
- _reset();
}
void GCodeTimeEstimator::calculate_time_from_lines(const std::vector<std::string>& gcode_lines)
@@ -211,42 +253,126 @@ namespace Slic3r {
#if ENABLE_MOVE_STATS
_log_moves_stats();
#endif // ENABLE_MOVE_STATS
-
- _reset_blocks();
- _reset();
}
- void GCodeTimeEstimator::add_gcode_line(const std::string& gcode_line)
+ bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval)
{
- PROFILE_FUNC();
- _parser.parse_line(gcode_line,
- [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
- { this->_process_gcode_line(reader, line); });
- }
+ boost::nowide::ifstream in(filename);
+ if (!in.good())
+ throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n"));
- void GCodeTimeEstimator::add_gcode_block(const char *ptr)
- {
- PROFILE_FUNC();
- GCodeReader::GCodeLine gline;
- auto action = [this](GCodeReader &reader, const GCodeReader::GCodeLine &line)
- { this->_process_gcode_line(reader, line); };
- for (; *ptr != 0;) {
- gline.reset();
- ptr = _parser.parse_line(ptr, gline, action);
+ std::string path_tmp = filename + ".times";
+
+ FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb");
+ if (out == nullptr)
+ throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n"));
+
+ std::string time_mask;
+ switch (_mode)
+ {
+ default:
+ case Normal:
+ {
+ time_mask = "M73 P%s R%s\n";
+ break;
+ }
+ case Silent:
+ {
+ time_mask = "M73 Q%s S%s\n";
+ break;
+ }
}
- }
- void GCodeTimeEstimator::calculate_time()
- {
- PROFILE_FUNC();
- _calculate_time();
+ unsigned int g1_lines_count = 0;
+ float last_recorded_time = 0.0f;
+ std::string gcode_line;
+ // buffer line to export only when greater than 64K to reduce writing calls
+ std::string export_line;
+ char time_line[64];
+ while (std::getline(in, gcode_line))
+ {
+ if (!in.good())
+ {
+ fclose(out);
+ throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n"));
+ }
-#if ENABLE_MOVE_STATS
- _log_moves_stats();
-#endif // ENABLE_MOVE_STATS
+ // replaces placeholders for initial line M73 with the real lines
+ if (((_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) ||
+ ((_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)))
+ {
+ sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(_time).c_str());
+ gcode_line = time_line;
+ }
+ else
+ gcode_line += "\n";
- _reset_blocks();
- _reset();
+ // add remaining time lines where needed
+ _parser.parse_line(gcode_line,
+ [this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line)
+ {
+ if (line.cmd_is("G1"))
+ {
+ ++g1_lines_count;
+
+ if (!line.has_e())
+ return;
+
+ G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count);
+ if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size()))
+ {
+ const Block& block = _blocks[it->second];
+ if (block.elapsed_time != -1.0f)
+ {
+ float block_remaining_time = _time - block.elapsed_time;
+ if (std::abs(last_recorded_time - block_remaining_time) > interval)
+ {
+ sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str());
+ gcode_line += time_line;
+
+ last_recorded_time = block_remaining_time;
+ }
+ }
+ }
+ }
+ });
+
+ export_line += gcode_line;
+ if (export_line.length() > 65535)
+ {
+ fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
+ if (ferror(out))
+ {
+ in.close();
+ fclose(out);
+ boost::nowide::remove(path_tmp.c_str());
+ throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n"));
+ }
+ export_line.clear();
+ }
+ }
+
+ if (export_line.length() > 0)
+ {
+ fwrite((const void*)export_line.c_str(), 1, export_line.length(), out);
+ if (ferror(out))
+ {
+ in.close();
+ fclose(out);
+ boost::nowide::remove(path_tmp.c_str());
+ throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n"));
+ }
+ }
+
+ fclose(out);
+ in.close();
+
+ boost::nowide::remove(filename.c_str());
+ if (boost::nowide::rename(path_tmp.c_str(), filename.c_str()) != 0)
+ throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' +
+ "Is " + path_tmp + " locked?" + '\n');
+
+ return true;
}
void GCodeTimeEstimator::set_axis_position(EAxis axis, float position)
@@ -301,7 +427,10 @@ namespace Slic3r {
void GCodeTimeEstimator::set_acceleration(float acceleration_mm_sec2)
{
- _state.acceleration = acceleration_mm_sec2;
+ _state.acceleration = (_state.max_acceleration == 0) ?
+ acceleration_mm_sec2 :
+ // Clamp the acceleration with the maximum.
+ std::min(_state.max_acceleration, acceleration_mm_sec2);
}
float GCodeTimeEstimator::get_acceleration() const
@@ -309,6 +438,18 @@ namespace Slic3r {
return _state.acceleration;
}
+ void GCodeTimeEstimator::set_max_acceleration(float acceleration_mm_sec2)
+ {
+ _state.max_acceleration = acceleration_mm_sec2;
+ if (acceleration_mm_sec2 > 0)
+ _state.acceleration = acceleration_mm_sec2;
+ }
+
+ float GCodeTimeEstimator::get_max_acceleration() const
+ {
+ return _state.max_acceleration;
+ }
+
void GCodeTimeEstimator::set_retract_acceleration(float acceleration_mm_sec2)
{
_state.retract_acceleration = acceleration_mm_sec2;
@@ -339,6 +480,40 @@ namespace Slic3r {
return _state.minimum_travel_feedrate;
}
+ void GCodeTimeEstimator::set_filament_load_times(const std::vector<double> &filament_load_times)
+ {
+ _state.filament_load_times.clear();
+ for (double t : filament_load_times)
+ _state.filament_load_times.push_back(t);
+ }
+
+ void GCodeTimeEstimator::set_filament_unload_times(const std::vector<double> &filament_unload_times)
+ {
+ _state.filament_unload_times.clear();
+ for (double t : filament_unload_times)
+ _state.filament_unload_times.push_back(t);
+ }
+
+ float GCodeTimeEstimator::get_filament_load_time(unsigned int id_extruder)
+ {
+ return
+ (_state.filament_load_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
+ 0 :
+ (_state.filament_load_times.size() <= id_extruder) ?
+ _state.filament_load_times.front() :
+ _state.filament_load_times[id_extruder];
+ }
+
+ float GCodeTimeEstimator::get_filament_unload_time(unsigned int id_extruder)
+ {
+ return
+ (_state.filament_unload_times.empty() || id_extruder == _state.extruder_id_unloaded) ?
+ 0 :
+ (_state.filament_unload_times.size() <= id_extruder) ?
+ _state.filament_unload_times.front() :
+ _state.filament_unload_times[id_extruder];
+ }
+
void GCodeTimeEstimator::set_extrude_factor_override_percentage(float percentage)
{
_state.extrude_factor_override_percentage = percentage;
@@ -356,6 +531,7 @@ namespace Slic3r {
GCodeFlavor GCodeTimeEstimator::get_dialect() const
{
+ PROFILE_FUNC();
return _state.dialect;
}
@@ -369,28 +545,61 @@ namespace Slic3r {
return _state.units;
}
- void GCodeTimeEstimator::set_positioning_xyz_type(GCodeTimeEstimator::EPositioningType type)
+ void GCodeTimeEstimator::set_global_positioning_type(GCodeTimeEstimator::EPositioningType type)
+ {
+ _state.global_positioning_type = type;
+ }
+
+ GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_global_positioning_type() const
+ {
+ return _state.global_positioning_type;
+ }
+
+ void GCodeTimeEstimator::set_e_local_positioning_type(GCodeTimeEstimator::EPositioningType type)
+ {
+ _state.e_local_positioning_type = type;
+ }
+
+ GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_e_local_positioning_type() const
+ {
+ return _state.e_local_positioning_type;
+ }
+
+ int GCodeTimeEstimator::get_g1_line_id() const
{
- _state.positioning_xyz_type = type;
+ return _state.g1_line_id;
}
- GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_positioning_xyz_type() const
+ void GCodeTimeEstimator::increment_g1_line_id()
{
- return _state.positioning_xyz_type;
+ ++_state.g1_line_id;
}
- void GCodeTimeEstimator::set_positioning_e_type(GCodeTimeEstimator::EPositioningType type)
+ void GCodeTimeEstimator::reset_g1_line_id()
{
- _state.positioning_e_type = type;
+ _state.g1_line_id = 0;
}
- GCodeTimeEstimator::EPositioningType GCodeTimeEstimator::get_positioning_e_type() const
+ void GCodeTimeEstimator::set_extruder_id(unsigned int id)
{
- return _state.positioning_e_type;
+ _state.extruder_id = id;
+ }
+
+ unsigned int GCodeTimeEstimator::get_extruder_id() const
+ {
+ return _state.extruder_id;
+ }
+
+ void GCodeTimeEstimator::reset_extruder_id()
+ {
+ // Set the initial extruder ID to unknown. For the multi-material setup it means
+ // that all the filaments are parked in the MMU and no filament is loaded yet.
+ _state.extruder_id = _state.extruder_id_unloaded;
}
void GCodeTimeEstimator::add_additional_time(float timeSec)
{
+ PROFILE_FUNC();
_state.additional_time += timeSec;
}
@@ -408,16 +617,19 @@ namespace Slic3r {
{
set_units(Millimeters);
set_dialect(gcfRepRap);
- set_positioning_xyz_type(Absolute);
- set_positioning_e_type(Relative);
+ set_global_positioning_type(Absolute);
+ set_e_local_positioning_type(Absolute);
set_feedrate(DEFAULT_FEEDRATE);
+ // Setting the maximum acceleration to zero means that the there is no limit and the G-code
+ // is allowed to set excessive values.
+ set_max_acceleration(0);
set_acceleration(DEFAULT_ACCELERATION);
set_retract_acceleration(DEFAULT_RETRACT_ACCELERATION);
set_minimum_feedrate(DEFAULT_MINIMUM_FEEDRATE);
set_minimum_travel_feedrate(DEFAULT_MINIMUM_TRAVEL_FEEDRATE);
set_extrude_factor_override_percentage(DEFAULT_EXTRUDE_FACTOR_OVERRIDE_PERCENTAGE);
-
+
for (unsigned char a = X; a < Num_Axis; ++a)
{
EAxis axis = (EAxis)a;
@@ -425,11 +637,14 @@ namespace Slic3r {
set_axis_max_acceleration(axis, DEFAULT_AXIS_MAX_ACCELERATION[a]);
set_axis_max_jerk(axis, DEFAULT_AXIS_MAX_JERK[a]);
}
+
+ _state.filament_load_times.clear();
+ _state.filament_unload_times.clear();
}
void GCodeTimeEstimator::reset()
{
- _time = 0.0f;
+ _reset_time();
#if ENABLE_MOVE_STATS
_moves_stats.clear();
#endif // ENABLE_MOVE_STATS
@@ -442,23 +657,14 @@ namespace Slic3r {
return _time;
}
- std::string GCodeTimeEstimator::get_time_hms() const
+ std::string GCodeTimeEstimator::get_time_dhms() const
{
- float timeinsecs = get_time();
- int hours = (int)(timeinsecs / 3600.0f);
- timeinsecs -= (float)hours * 3600.0f;
- int minutes = (int)(timeinsecs / 60.0f);
- timeinsecs -= (float)minutes * 60.0f;
-
- char buffer[64];
- if (hours > 0)
- ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)timeinsecs);
- else if (minutes > 0)
- ::sprintf(buffer, "%dm %ds", minutes, (int)timeinsecs);
- else
- ::sprintf(buffer, "%ds", (int)timeinsecs);
+ return _get_time_dhms(get_time());
+ }
- return buffer;
+ std::string GCodeTimeEstimator::get_time_minutes() const
+ {
+ return _get_time_minutes(get_time());
}
void GCodeTimeEstimator::_reset()
@@ -471,6 +677,17 @@ namespace Slic3r {
set_axis_position(Z, 0.0f);
set_additional_time(0.0f);
+
+ reset_extruder_id();
+ reset_g1_line_id();
+ _g1_line_ids.clear();
+
+ _last_st_synchronized_block_id = -1;
+ }
+
+ void GCodeTimeEstimator::_reset_time()
+ {
+ _time = 0.0f;
}
void GCodeTimeEstimator::_reset_blocks()
@@ -478,22 +695,27 @@ namespace Slic3r {
_blocks.clear();
}
+
void GCodeTimeEstimator::_calculate_time()
{
+ PROFILE_FUNC();
_forward_pass();
_reverse_pass();
_recalculate_trapezoids();
_time += get_additional_time();
- for (const Block& block : _blocks)
+ for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
{
+ Block& block = _blocks[i];
+
#if ENABLE_MOVE_STATS
float block_time = 0.0f;
block_time += block.acceleration_time();
block_time += block.cruise_time();
block_time += block.deceleration_time();
_time += block_time;
+ block.elapsed_time = _time;
MovesStatsMap::iterator it = _moves_stats.find(block.move_type);
if (it == _moves_stats.end())
@@ -505,8 +727,13 @@ namespace Slic3r {
_time += block.acceleration_time();
_time += block.cruise_time();
_time += block.deceleration_time();
+ block.elapsed_time = _time;
#endif // ENABLE_MOVE_STATS
}
+
+ _last_st_synchronized_block_id = _blocks.size() - 1;
+ // The additional time has been consumed (added to the total time), reset it to zero.
+ set_additional_time(0.);
}
void GCodeTimeEstimator::_process_gcode_line(GCodeReader&, const GCodeReader::GCodeLine& line)
@@ -619,22 +846,32 @@ namespace Slic3r {
_processM566(line);
break;
}
+ case 702: // MK3 MMU2: Process the final filament unload.
+ {
+ _processM702(line);
+ break;
+ }
}
break;
}
+ case 'T': // Select Tools
+ {
+ _processT(line);
+ break;
+ }
}
}
}
// Returns the new absolute position on the given axis in dependence of the given parameters
- float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, GCodeTimeEstimator::EPositioningType type, float current_absolute_position)
+ float axis_absolute_position_from_G1_line(GCodeTimeEstimator::EAxis axis, const GCodeReader::GCodeLine& lineG1, GCodeTimeEstimator::EUnits units, bool is_relative, float current_absolute_position)
{
float lengthsScaleFactor = (units == GCodeTimeEstimator::Inches) ? INCHES_TO_MM : 1.0f;
if (lineG1.has(Slic3r::Axis(axis)))
{
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
- return (type == GCodeTimeEstimator::Absolute) ? ret : current_absolute_position + ret;
+ return is_relative ? current_absolute_position + ret : ret;
}
else
return current_absolute_position;
@@ -642,12 +879,19 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG1(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
+ increment_g1_line_id();
+
// updates axes positions from line
EUnits units = get_units();
float new_pos[Num_Axis];
for (unsigned char a = X; a < Num_Axis; ++a)
{
- new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, (a == E) ? get_positioning_e_type() : get_positioning_xyz_type(), get_axis_position((EAxis)a));
+ bool is_relative = (get_global_positioning_type() == Relative);
+ if (a == E)
+ is_relative |= (get_e_local_positioning_type() == Relative);
+
+ new_pos[a] = axis_absolute_position_from_G1_line((EAxis)a, line, units, is_relative, get_axis_position((EAxis)a));
}
// updates feedrate from line, if present
@@ -686,13 +930,16 @@ namespace Slic3r {
if (_curr.abs_axis_feedrate[a] > 0.0f)
min_feedrate_factor = std::min(min_feedrate_factor, get_axis_max_feedrate((EAxis)a) / _curr.abs_axis_feedrate[a]);
}
-
+
block.feedrate.cruise = min_feedrate_factor * _curr.feedrate;
- for (unsigned char a = X; a < Num_Axis; ++a)
+ if (min_feedrate_factor < 1.0f)
{
- _curr.axis_feedrate[a] *= min_feedrate_factor;
- _curr.abs_axis_feedrate[a] *= min_feedrate_factor;
+ for (unsigned char a = X; a < Num_Axis; ++a)
+ {
+ _curr.axis_feedrate[a] *= min_feedrate_factor;
+ _curr.abs_axis_feedrate[a] *= min_feedrate_factor;
+ }
}
// calculates block acceleration
@@ -825,10 +1072,12 @@ namespace Slic3r {
// adds block to blocks list
_blocks.emplace_back(block);
+ _g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1));
}
void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
float value;
@@ -850,33 +1099,37 @@ namespace Slic3r {
void GCodeTimeEstimator::_processG20(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_units(Inches);
}
void GCodeTimeEstimator::_processG21(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
set_units(Millimeters);
}
void GCodeTimeEstimator::_processG28(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
// TODO
}
void GCodeTimeEstimator::_processG90(const GCodeReader::GCodeLine& line)
{
- set_positioning_xyz_type(Absolute);
+ PROFILE_FUNC();
+ set_global_positioning_type(Absolute);
}
void GCodeTimeEstimator::_processG91(const GCodeReader::GCodeLine& line)
{
- // TODO: THERE ARE DIALECT VARIANTS
-
- set_positioning_xyz_type(Relative);
+ PROFILE_FUNC();
+ set_global_positioning_type(Relative);
}
void GCodeTimeEstimator::_processG92(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
float lengthsScaleFactor = (get_units() == Inches) ? INCHES_TO_MM : 1.0f;
bool anyFound = false;
@@ -917,26 +1170,31 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM1(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
_simulate_st_synchronize();
}
void GCodeTimeEstimator::_processM82(const GCodeReader::GCodeLine& line)
{
- set_positioning_e_type(Absolute);
+ PROFILE_FUNC();
+ set_e_local_positioning_type(Absolute);
}
void GCodeTimeEstimator::_processM83(const GCodeReader::GCodeLine& line)
{
- set_positioning_e_type(Relative);
+ PROFILE_FUNC();
+ set_e_local_positioning_type(Relative);
}
void GCodeTimeEstimator::_processM109(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
// TODO
}
void GCodeTimeEstimator::_processM201(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
// see http://reprap.org/wiki/G-code#M201:_Set_max_printing_acceleration
@@ -957,6 +1215,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM203(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
GCodeFlavor dialect = get_dialect();
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
@@ -981,16 +1240,32 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM204(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
float value;
- if (line.has_value('S', value))
+ if (line.has_value('S', value)) {
+ // Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
+ // and it is also generated by Slic3r to control acceleration per extrusion type
+ // (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
set_acceleration(value);
-
- if (line.has_value('T', value))
- set_retract_acceleration(value);
+ if (line.has_value('T', value))
+ set_retract_acceleration(value);
+ } else {
+ // New acceleration format, compatible with the upstream Marlin.
+ if (line.has_value('P', value))
+ set_acceleration(value);
+ if (line.has_value('R', value))
+ set_retract_acceleration(value);
+ if (line.has_value('T', value)) {
+ // Interpret the T value as the travel acceleration in the new Marlin format.
+ //FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
+ // set_travel_acceleration(value);
+ }
+ }
}
void GCodeTimeEstimator::_processM205(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
if (line.has_x())
{
float max_jerk = line.x();
@@ -1017,6 +1292,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM221(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
float value_s;
float value_t;
if (line.has_value('S', value_s) && !line.has_value('T', value_t))
@@ -1025,6 +1301,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_processM566(const GCodeReader::GCodeLine& line)
{
+ PROFILE_FUNC();
if (line.has_x())
set_axis_max_jerk(X, line.x() * MMMIN_TO_MMSEC);
@@ -1038,17 +1315,49 @@ namespace Slic3r {
set_axis_max_jerk(E, line.e() * MMMIN_TO_MMSEC);
}
+ void GCodeTimeEstimator::_processM702(const GCodeReader::GCodeLine& line)
+ {
+ PROFILE_FUNC();
+ if (line.has('C')) {
+ // MK3 MMU2 specific M code:
+ // M702 C is expected to be sent by the custom end G-code when finalizing a print.
+ // The MK3 unit shall unload and park the active filament into the MMU2 unit.
+ add_additional_time(get_filament_unload_time(get_extruder_id()));
+ reset_extruder_id();
+ _simulate_st_synchronize();
+ }
+ }
+
+ void GCodeTimeEstimator::_processT(const GCodeReader::GCodeLine& line)
+ {
+ std::string cmd = line.cmd();
+ if (cmd.length() > 1)
+ {
+ unsigned int id = (unsigned int)::strtol(cmd.substr(1).c_str(), nullptr, 10);
+ if (get_extruder_id() != id)
+ {
+ // Specific to the MK3 MMU2: The initial extruder ID is set to -1 indicating
+ // that the filament is parked in the MMU2 unit and there is nothing to be unloaded yet.
+ add_additional_time(get_filament_unload_time(get_extruder_id()));
+ set_extruder_id(id);
+ add_additional_time(get_filament_load_time(get_extruder_id()));
+ _simulate_st_synchronize();
+ }
+ }
+ }
+
void GCodeTimeEstimator::_simulate_st_synchronize()
{
+ PROFILE_FUNC();
_calculate_time();
- _reset_blocks();
}
void GCodeTimeEstimator::_forward_pass()
{
+ PROFILE_FUNC();
if (_blocks.size() > 1)
{
- for (unsigned int i = 0; i < (unsigned int)_blocks.size() - 1; ++i)
+ for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size() - 1; ++i)
{
_planner_forward_pass_kernel(_blocks[i], _blocks[i + 1]);
}
@@ -1057,9 +1366,10 @@ namespace Slic3r {
void GCodeTimeEstimator::_reverse_pass()
{
+ PROFILE_FUNC();
if (_blocks.size() > 1)
{
- for (int i = (int)_blocks.size() - 1; i >= 1; --i)
+ for (int i = (int)_blocks.size() - 1; i >= _last_st_synchronized_block_id + 2; --i)
{
_planner_reverse_pass_kernel(_blocks[i - 1], _blocks[i]);
}
@@ -1068,6 +1378,7 @@ namespace Slic3r {
void GCodeTimeEstimator::_planner_forward_pass_kernel(Block& prev, Block& curr)
{
+ PROFILE_FUNC();
// If the previous block is an acceleration block, but it is not long enough to complete the
// full speed change within the block, we need to adjust the entry speed accordingly. Entry
// speeds have already been reset, maximized, and reverse planned by reverse planner.
@@ -1108,11 +1419,14 @@ namespace Slic3r {
void GCodeTimeEstimator::_recalculate_trapezoids()
{
+ PROFILE_FUNC();
Block* curr = nullptr;
Block* next = nullptr;
- for (Block& b : _blocks)
+ for (int i = _last_st_synchronized_block_id + 1; i < (int)_blocks.size(); ++i)
{
+ Block& b = _blocks[i];
+
curr = next;
next = &b;
@@ -1142,6 +1456,33 @@ namespace Slic3r {
}
}
+ std::string GCodeTimeEstimator::_get_time_dhms(float time_in_secs)
+ {
+ int days = (int)(time_in_secs / 86400.0f);
+ time_in_secs -= (float)days * 86400.0f;
+ int hours = (int)(time_in_secs / 3600.0f);
+ time_in_secs -= (float)hours * 3600.0f;
+ int minutes = (int)(time_in_secs / 60.0f);
+ time_in_secs -= (float)minutes * 60.0f;
+
+ char buffer[64];
+ if (days > 0)
+ ::sprintf(buffer, "%dd %dh %dm %ds", days, hours, minutes, (int)time_in_secs);
+ else if (hours > 0)
+ ::sprintf(buffer, "%dh %dm %ds", hours, minutes, (int)time_in_secs);
+ else if (minutes > 0)
+ ::sprintf(buffer, "%dm %ds", minutes, (int)time_in_secs);
+ else
+ ::sprintf(buffer, "%ds", (int)time_in_secs);
+
+ return buffer;
+ }
+
+ std::string GCodeTimeEstimator::_get_time_minutes(float time_in_secs)
+ {
+ return std::to_string((int)(::roundf(time_in_secs / 60.0f)));
+ }
+
#if ENABLE_MOVE_STATS
void GCodeTimeEstimator::_log_moves_stats() const
{
diff --git a/xs/src/libslic3r/GCodeTimeEstimator.hpp b/xs/src/libslic3r/GCodeTimeEstimator.hpp
index 5ad5b8d0c..e9da584c3 100644
--- a/xs/src/libslic3r/GCodeTimeEstimator.hpp
+++ b/xs/src/libslic3r/GCodeTimeEstimator.hpp
@@ -17,6 +17,15 @@ namespace Slic3r {
class GCodeTimeEstimator
{
public:
+ static const std::string Normal_First_M73_Output_Placeholder_Tag;
+ static const std::string Silent_First_M73_Output_Placeholder_Tag;
+
+ enum EMode : unsigned char
+ {
+ Normal,
+ Silent
+ };
+
enum EUnits : unsigned char
{
Millimeters,
@@ -61,16 +70,27 @@ namespace Slic3r {
{
GCodeFlavor dialect;
EUnits units;
- EPositioningType positioning_xyz_type;
- EPositioningType positioning_e_type;
+ EPositioningType global_positioning_type;
+ EPositioningType e_local_positioning_type;
Axis axis[Num_Axis];
float feedrate; // mm/s
float acceleration; // mm/s^2
+ // hard limit for the acceleration, to which the firmware will clamp.
+ float max_acceleration; // mm/s^2
float retract_acceleration; // mm/s^2
float additional_time; // s
float minimum_feedrate; // mm/s
float minimum_travel_feedrate; // mm/s
- float extrude_factor_override_percentage;
+ float extrude_factor_override_percentage;
+ // Additional load / unload times for a filament exchange sequence.
+ std::vector<float> filament_load_times;
+ std::vector<float> filament_unload_times;
+ unsigned int g1_line_id;
+ // extruder_id is currently used to correctly calculate filament load / unload times
+ // into the total print time. This is currently only really used by the MK3 MMU2:
+ // Extruder id (-1) means no filament is loaded yet, all the filaments are parked in the MK3 MMU2 unit.
+ static const unsigned int extruder_id_unloaded = (unsigned int)-1;
+ unsigned int extruder_id;
};
public:
@@ -121,7 +141,6 @@ namespace Slic3r {
bool nominal_length;
};
-
#if ENABLE_MOVE_STATS
EMoveType move_type;
#endif // ENABLE_MOVE_STATS
@@ -134,6 +153,9 @@ namespace Slic3r {
FeedrateProfile feedrate;
Trapezoid trapezoid;
+ float elapsed_time;
+
+ Block();
// Returns the length of the move covered by this block, in mm
float move_length() const;
@@ -187,19 +209,39 @@ namespace Slic3r {
typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap;
#endif // ENABLE_MOVE_STATS
+ typedef std::map<unsigned int, unsigned int> G1LineIdToBlockIdMap;
+
private:
+ EMode _mode;
GCodeReader _parser;
State _state;
Feedrates _curr;
Feedrates _prev;
BlocksList _blocks;
+ // Map between g1 line id and blocks id, used to speed up export of remaining times
+ G1LineIdToBlockIdMap _g1_line_ids;
+ // Index of the last block already st_synchronized
+ int _last_st_synchronized_block_id;
float _time; // s
+
#if ENABLE_MOVE_STATS
MovesStatsMap _moves_stats;
#endif // ENABLE_MOVE_STATS
public:
- GCodeTimeEstimator();
+ explicit GCodeTimeEstimator(EMode mode);
+
+ // Adds the given gcode line
+ void add_gcode_line(const std::string& gcode_line);
+
+ void add_gcode_block(const char *ptr);
+ void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
+
+ // Calculates the time estimate from the gcode lines added using add_gcode_line() or add_gcode_block()
+ // start_from_beginning:
+ // if set to true all blocks will be used to calculate the time estimate,
+ // if set to false only the blocks not yet processed will be used and the calculated time will be added to the current calculated time
+ void calculate_time(bool start_from_beginning);
// Calculates the time estimate from the given gcode in string format
void calculate_time_from_text(const std::string& gcode);
@@ -210,14 +252,12 @@ namespace Slic3r {
// Calculates the time estimate from the gcode contained in given list of gcode lines
void calculate_time_from_lines(const std::vector<std::string>& gcode_lines);
- // Adds the given gcode line
- void add_gcode_line(const std::string& gcode_line);
-
- void add_gcode_block(const char *ptr);
- void add_gcode_block(const std::string &str) { this->add_gcode_block(str.c_str()); }
-
- // Calculates the time estimate from the gcode lines added using add_gcode_line()
- void calculate_time();
+ // Process the gcode contained in the file with the given filename,
+ // placing in it new lines (M73) containing the remaining time, at the given interval in seconds
+ // and saving the result back in the same file
+ // This time estimator should have been already used to calculate the time estimate for the gcode
+ // contained in the given file before to call this method
+ bool post_process_remaining_times(const std::string& filename, float interval_sec);
// Set current position on the given axis with the given value
void set_axis_position(EAxis axis, float position);
@@ -239,6 +279,10 @@ namespace Slic3r {
void set_acceleration(float acceleration_mm_sec2);
float get_acceleration() const;
+ // Maximum acceleration for the machine. The firmware simulator will clamp the M204 Sxxx to this maximum.
+ void set_max_acceleration(float acceleration_mm_sec2);
+ float get_max_acceleration() const;
+
void set_retract_acceleration(float acceleration_mm_sec2);
float get_retract_acceleration() const;
@@ -248,6 +292,11 @@ namespace Slic3r {
void set_minimum_travel_feedrate(float feedrate_mm_sec);
float get_minimum_travel_feedrate() const;
+ void set_filament_load_times(const std::vector<double> &filament_load_times);
+ void set_filament_unload_times(const std::vector<double> &filament_unload_times);
+ float get_filament_load_time(unsigned int id_extruder);
+ float get_filament_unload_time(unsigned int id_extruder);
+
void set_extrude_factor_override_percentage(float percentage);
float get_extrude_factor_override_percentage() const;
@@ -257,11 +306,19 @@ namespace Slic3r {
void set_units(EUnits units);
EUnits get_units() const;
- void set_positioning_xyz_type(EPositioningType type);
- EPositioningType get_positioning_xyz_type() const;
+ void set_global_positioning_type(EPositioningType type);
+ EPositioningType get_global_positioning_type() const;
- void set_positioning_e_type(EPositioningType type);
- EPositioningType get_positioning_e_type() const;
+ void set_e_local_positioning_type(EPositioningType type);
+ EPositioningType get_e_local_positioning_type() const;
+
+ int get_g1_line_id() const;
+ void increment_g1_line_id();
+ void reset_g1_line_id();
+
+ void set_extruder_id(unsigned int id);
+ unsigned int get_extruder_id() const;
+ void reset_extruder_id();
void add_additional_time(float timeSec);
void set_additional_time(float timeSec);
@@ -275,11 +332,15 @@ namespace Slic3r {
// Returns the estimated time, in seconds
float get_time() const;
- // Returns the estimated time, in format HHh MMm SSs
- std::string get_time_hms() const;
+ // Returns the estimated time, in format DDd HHh MMm SSs
+ std::string get_time_dhms() const;
+
+ // Returns the estimated time, in minutes (integer)
+ std::string get_time_minutes() const;
private:
void _reset();
+ void _reset_time();
void _reset_blocks();
// Calculates the time estimate
@@ -342,6 +403,12 @@ namespace Slic3r {
// Set allowable instantaneous speed change
void _processM566(const GCodeReader::GCodeLine& line);
+ // Unload the current filament into the MK3 MMU2 unit at the end of print.
+ void _processM702(const GCodeReader::GCodeLine& line);
+
+ // Processes T line (Select Tool)
+ void _processT(const GCodeReader::GCodeLine& line);
+
// Simulates firmware st_synchronize() call
void _simulate_st_synchronize();
@@ -353,6 +420,12 @@ namespace Slic3r {
void _recalculate_trapezoids();
+ // Returns the given time is seconds in format DDd HHh MMm SSs
+ static std::string _get_time_dhms(float time_in_secs);
+
+ // Returns the given, in minutes (integer)
+ static std::string _get_time_minutes(float time_in_secs);
+
#if ENABLE_MOVE_STATS
void _log_moves_stats() const;
#endif // ENABLE_MOVE_STATS
diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp
index 9876a3fac..6ef17f4f4 100644
--- a/xs/src/libslic3r/GCodeWriter.cpp
+++ b/xs/src/libslic3r/GCodeWriter.cpp
@@ -19,6 +19,8 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis();
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
+ m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ?
+ print_config.machine_max_acceleration_extruding.values.front() : 0;
}
void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids)
@@ -170,6 +172,10 @@ std::string GCodeWriter::set_fan(unsigned int speed, bool dont_save)
std::string GCodeWriter::set_acceleration(unsigned int acceleration)
{
+ // Clamp the acceleration to the allowed maximum.
+ if (m_max_acceleration > 0 && acceleration > m_max_acceleration)
+ acceleration = m_max_acceleration;
+
if (acceleration == 0 || acceleration == m_last_acceleration)
return std::string();
@@ -270,30 +276,30 @@ std::string GCodeWriter::set_speed(double F, const std::string &comment, const s
return gcode.str();
}
-std::string GCodeWriter::travel_to_xy(const Pointf &point, const std::string &comment)
+std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
{
- m_pos.x = point.x;
- m_pos.y = point.y;
+ m_pos(0) = point(0);
+ m_pos(1) = point(1);
std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point.x)
- << " Y" << XYZF_NUM(point.y)
+ gcode << "G1 X" << XYZF_NUM(point(0))
+ << " Y" << XYZF_NUM(point(1))
<< " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
COMMENT(comment);
gcode << "\n";
return gcode.str();
}
-std::string GCodeWriter::travel_to_xyz(const Pointf3 &point, const std::string &comment)
+std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment)
{
/* If target Z is lower than current Z but higher than nominal Z we
don't perform the Z move but we only move in the XY plane and
adjust the nominal Z by reducing the lift amount that will be
used for unlift. */
- if (!this->will_move_z(point.z)) {
- double nominal_z = m_pos.z - m_lifted;
- m_lifted = m_lifted - (point.z - nominal_z);
- return this->travel_to_xy(point);
+ if (!this->will_move_z(point(2))) {
+ double nominal_z = m_pos(2) - m_lifted;
+ m_lifted = m_lifted - (point(2) - nominal_z);
+ return this->travel_to_xy(to_2d(point));
}
/* In all the other cases, we perform an actual XYZ move and cancel
@@ -302,9 +308,9 @@ std::string GCodeWriter::travel_to_xyz(const Pointf3 &point, const std::string &
m_pos = point;
std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point.x)
- << " Y" << XYZF_NUM(point.y)
- << " Z" << XYZF_NUM(point.z)
+ gcode << "G1 X" << XYZF_NUM(point(0))
+ << " Y" << XYZF_NUM(point(1))
+ << " Z" << XYZF_NUM(point(2))
<< " F" << XYZF_NUM(this->config.travel_speed.value * 60.0);
COMMENT(comment);
gcode << "\n";
@@ -317,7 +323,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
we don't perform the move but we only adjust the nominal Z by
reducing the lift amount that will be used for unlift. */
if (!this->will_move_z(z)) {
- double nominal_z = m_pos.z - m_lifted;
+ double nominal_z = m_pos(2) - m_lifted;
m_lifted = m_lifted - (z - nominal_z);
return "";
}
@@ -330,7 +336,7 @@ std::string GCodeWriter::travel_to_z(double z, const std::string &comment)
std::string GCodeWriter::_travel_to_z(double z, const std::string &comment)
{
- m_pos.z = z;
+ m_pos(2) = z;
std::ostringstream gcode;
gcode << "G1 Z" << XYZF_NUM(z)
@@ -345,38 +351,38 @@ bool GCodeWriter::will_move_z(double z) const
/* If target Z is lower than current Z but higher than nominal Z
we don't perform an actual Z move. */
if (m_lifted > 0) {
- double nominal_z = m_pos.z - m_lifted;
- if (z >= nominal_z && z <= m_pos.z)
+ double nominal_z = m_pos(2) - m_lifted;
+ if (z >= nominal_z && z <= m_pos(2))
return false;
}
return true;
}
-std::string GCodeWriter::extrude_to_xy(const Pointf &point, double dE, const std::string &comment)
+std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment)
{
- m_pos.x = point.x;
- m_pos.y = point.y;
+ m_pos(0) = point(0);
+ m_pos(1) = point(1);
m_extruder->extrude(dE);
std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point.x)
- << " Y" << XYZF_NUM(point.y)
+ gcode << "G1 X" << XYZF_NUM(point(0))
+ << " Y" << XYZF_NUM(point(1))
<< " " << m_extrusion_axis << E_NUM(m_extruder->E());
COMMENT(comment);
gcode << "\n";
return gcode.str();
}
-std::string GCodeWriter::extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment)
+std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment)
{
m_pos = point;
m_lifted = 0;
m_extruder->extrude(dE);
std::ostringstream gcode;
- gcode << "G1 X" << XYZF_NUM(point.x)
- << " Y" << XYZF_NUM(point.y)
- << " Z" << XYZF_NUM(point.z)
+ gcode << "G1 X" << XYZF_NUM(point(0))
+ << " Y" << XYZF_NUM(point(1))
+ << " Z" << XYZF_NUM(point(2))
<< " " << m_extrusion_axis << E_NUM(m_extruder->E());
COMMENT(comment);
gcode << "\n";
@@ -480,12 +486,12 @@ std::string GCodeWriter::lift()
{
double above = this->config.retract_lift_above.get_at(m_extruder->id());
double below = this->config.retract_lift_below.get_at(m_extruder->id());
- if (m_pos.z >= above && (below == 0 || m_pos.z <= below))
+ if (m_pos(2) >= above && (below == 0 || m_pos(2) <= below))
target_lift = this->config.retract_lift.get_at(m_extruder->id());
}
if (m_lifted == 0 && target_lift > 0) {
m_lifted = target_lift;
- return this->_travel_to_z(m_pos.z + target_lift, "lift Z");
+ return this->_travel_to_z(m_pos(2) + target_lift, "lift Z");
}
return "";
}
@@ -494,7 +500,7 @@ std::string GCodeWriter::unlift()
{
std::string gcode;
if (m_lifted > 0) {
- gcode += this->_travel_to_z(m_pos.z - m_lifted, "restore layer Z");
+ gcode += this->_travel_to_z(m_pos(2) - m_lifted, "restore layer Z");
m_lifted = 0;
}
return gcode;
diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/xs/src/libslic3r/GCodeWriter.hpp
index 78e9c9323..664b0e3a1 100644
--- a/xs/src/libslic3r/GCodeWriter.hpp
+++ b/xs/src/libslic3r/GCodeWriter.hpp
@@ -18,7 +18,7 @@ public:
GCodeWriter() :
multiple_extruders(false), m_extrusion_axis("E"), m_extruder(nullptr),
m_single_extruder_multi_material(false),
- m_last_acceleration(0), m_last_fan_speed(0),
+ m_last_acceleration(0), m_max_acceleration(0), m_last_fan_speed(0),
m_last_bed_temperature(0), m_last_bed_temperature_reached(true),
m_lifted(0)
{}
@@ -55,18 +55,18 @@ public:
std::string toolchange_prefix() const;
std::string toolchange(unsigned int extruder_id);
std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()) const;
- std::string travel_to_xy(const Pointf &point, const std::string &comment = std::string());
- std::string travel_to_xyz(const Pointf3 &point, const std::string &comment = std::string());
+ std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string());
+ std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string());
std::string travel_to_z(double z, const std::string &comment = std::string());
bool will_move_z(double z) const;
- std::string extrude_to_xy(const Pointf &point, double dE, const std::string &comment = std::string());
- std::string extrude_to_xyz(const Pointf3 &point, double dE, const std::string &comment = std::string());
+ std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string());
+ std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string());
std::string retract(bool before_wipe = false);
std::string retract_for_toolchange(bool before_wipe = false);
std::string unretract();
std::string lift();
std::string unlift();
- Pointf3 get_position() const { return m_pos; }
+ Vec3d get_position() const { return m_pos; }
private:
std::vector<Extruder> m_extruders;
@@ -74,11 +74,14 @@ private:
bool m_single_extruder_multi_material;
Extruder* m_extruder;
unsigned int m_last_acceleration;
+ // Limit for setting the acceleration, to respect the machine limits set for the Marlin firmware.
+ // If set to zero, the limit is not in action.
+ unsigned int m_max_acceleration;
unsigned int m_last_fan_speed;
unsigned int m_last_bed_temperature;
bool m_last_bed_temperature_reached;
double m_lifted;
- Pointf3 m_pos;
+ Vec3d m_pos = Vec3d::Zero();
std::string _travel_to_z(double z, const std::string &comment);
std::string _retract(double length, double restart_extra, const std::string &comment);
diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp
index 39b626ee3..b0ded2d04 100644
--- a/xs/src/libslic3r/Geometry.cpp
+++ b/xs/src/libslic3r/Geometry.cpp
@@ -195,47 +195,110 @@ using namespace boost::polygon; // provides also high() and low()
namespace Slic3r { namespace Geometry {
-static bool
-sort_points (Point a, Point b)
+static bool sort_points(const Point& a, const Point& b)
{
- return (a.x < b.x) || (a.x == b.x && a.y < b.y);
+ return (a(0) < b(0)) || (a(0) == b(0) && a(1) < b(1));
}
-/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */
+static bool sort_pointfs(const Vec3d& a, const Vec3d& b)
+{
+ return (a(0) < b(0)) || (a(0) == b(0) && a(1) < b(1));
+}
+
+// This implementation is based on Andrew's monotone chain 2D convex hull algorithm
Polygon
convex_hull(Points points)
{
assert(points.size() >= 3);
// sort input points
std::sort(points.begin(), points.end(), sort_points);
-
+
int n = points.size(), k = 0;
Polygon hull;
if (n >= 3) {
- hull.points.resize(2*n);
+ hull.points.resize(2 * n);
// Build lower hull
for (int i = 0; i < n; i++) {
- while (k >= 2 && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
- hull.points[k++] = points[i];
+ while (k >= 2 && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
+ hull[k++] = points[i];
}
// Build upper hull
for (int i = n-2, t = k+1; i >= 0; i--) {
- while (k >= t && points[i].ccw(hull.points[k-2], hull.points[k-1]) <= 0) k--;
- hull.points[k++] = points[i];
+ while (k >= t && points[i].ccw(hull[k-2], hull[k-1]) <= 0) k--;
+ hull[k++] = points[i];
}
hull.points.resize(k);
-
- assert( hull.points.front().coincides_with(hull.points.back()) );
+
+ assert(hull.points.front() == hull.points.back());
hull.points.pop_back();
}
return hull;
}
+Pointf3s
+convex_hull(Pointf3s points)
+{
+ assert(points.size() >= 3);
+ // sort input points
+ std::sort(points.begin(), points.end(), sort_pointfs);
+
+ int n = points.size(), k = 0;
+ Pointf3s hull;
+
+ if (n >= 3)
+ {
+ hull.resize(2 * n);
+
+ // Build lower hull
+ for (int i = 0; i < n; ++i)
+ {
+ Point p = Point::new_scale(points[i](0), points[i](1));
+ while (k >= 2)
+ {
+ Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
+ Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
+
+ if (p.ccw(k2, k1) <= 0)
+ --k;
+ else
+ break;
+ }
+
+ hull[k++] = points[i];
+ }
+
+ // Build upper hull
+ for (int i = n - 2, t = k + 1; i >= 0; --i)
+ {
+ Point p = Point::new_scale(points[i](0), points[i](1));
+ while (k >= t)
+ {
+ Point k1 = Point::new_scale(hull[k - 1](0), hull[k - 1](1));
+ Point k2 = Point::new_scale(hull[k - 2](0), hull[k - 2](1));
+
+ if (p.ccw(k2, k1) <= 0)
+ --k;
+ else
+ break;
+ }
+
+ hull[k++] = points[i];
+ }
+
+ hull.resize(k);
+
+ assert(hull.front() == hull.back());
+ hull.pop_back();
+ }
+
+ return hull;
+}
+
Polygon
convex_hull(const Polygons &polygons)
{
@@ -243,7 +306,7 @@ convex_hull(const Polygons &polygons)
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {
pp.insert(pp.end(), p->points.begin(), p->points.end());
}
- return convex_hull(pp);
+ return convex_hull(std::move(pp));
}
/* accepts an arrayref of points and returns a list of indices
@@ -345,54 +408,54 @@ linint(double value, double oldmin, double oldmax, double newmin, double newmax)
// If the points have the same weight, sort them lexicographically by their positions.
struct ArrangeItem {
ArrangeItem() {}
- Pointf pos;
+ Vec2d pos;
coordf_t weight;
bool operator<(const ArrangeItem &other) const {
return weight < other.weight ||
- ((weight == other.weight) && (pos.y < other.pos.y || (pos.y == other.pos.y && pos.x < other.pos.x)));
+ ((weight == other.weight) && (pos(1) < other.pos(1) || (pos(1) == other.pos(1) && pos(0) < other.pos(0))));
}
};
-Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box)
+Pointfs arrange(size_t num_parts, const Vec2d &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box)
{
// Use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm.
- const Pointf cell_size(part_size.x + gap, part_size.y + gap);
+ const Vec2d cell_size(part_size(0) + gap, part_size(1) + gap);
const BoundingBoxf bed_bbox = (bed_bounding_box != NULL && bed_bounding_box->defined) ?
*bed_bounding_box :
// Bogus bed size, large enough not to trigger the unsufficient bed size error.
BoundingBoxf(
- Pointf(0, 0),
- Pointf(cell_size.x * num_parts, cell_size.y * num_parts));
+ Vec2d(0, 0),
+ Vec2d(cell_size(0) * num_parts, cell_size(1) * num_parts));
// This is how many cells we have available into which to put parts.
- size_t cellw = size_t(floor((bed_bbox.size().x + gap) / cell_size.x));
- size_t cellh = size_t(floor((bed_bbox.size().y + gap) / cell_size.y));
+ size_t cellw = size_t(floor((bed_bbox.size()(0) + gap) / cell_size(0)));
+ size_t cellh = size_t(floor((bed_bbox.size()(1) + gap) / cell_size(1)));
if (num_parts > cellw * cellh)
CONFESS(PRINTF_ZU " parts won't fit in your print area!\n", num_parts);
// Get a bounding box of cellw x cellh cells, centered at the center of the bed.
- Pointf cells_size(cellw * cell_size.x - gap, cellh * cell_size.y - gap);
- Pointf cells_offset(bed_bbox.center() - 0.5 * cells_size);
+ Vec2d cells_size(cellw * cell_size(0) - gap, cellh * cell_size(1) - gap);
+ Vec2d cells_offset(bed_bbox.center() - 0.5 * cells_size);
BoundingBoxf cells_bb(cells_offset, cells_size + cells_offset);
// List of cells, sorted by distance from center.
std::vector<ArrangeItem> cellsorder(cellw * cellh, ArrangeItem());
for (size_t j = 0; j < cellh; ++ j) {
// Center of the jth row on the bed.
- coordf_t cy = linint(j + 0.5, 0., double(cellh), cells_bb.min.y, cells_bb.max.y);
+ coordf_t cy = linint(j + 0.5, 0., double(cellh), cells_bb.min(1), cells_bb.max(1));
// Offset from the bed center.
- coordf_t yd = cells_bb.center().y - cy;
+ coordf_t yd = cells_bb.center()(1) - cy;
for (size_t i = 0; i < cellw; ++ i) {
// Center of the ith column on the bed.
- coordf_t cx = linint(i + 0.5, 0., double(cellw), cells_bb.min.x, cells_bb.max.x);
+ coordf_t cx = linint(i + 0.5, 0., double(cellw), cells_bb.min(0), cells_bb.max(0));
// Offset from the bed center.
- coordf_t xd = cells_bb.center().x - cx;
+ coordf_t xd = cells_bb.center()(0) - cx;
// Cell with a distance from the bed center.
ArrangeItem &ci = cellsorder[j * cellw + i];
// Cell center
- ci.pos.x = cx;
- ci.pos.y = cy;
+ ci.pos(0) = cx;
+ ci.pos(1) = cy;
// Square distance of the cell center to the bed center.
ci.weight = xd * xd + yd * yd;
}
@@ -405,61 +468,61 @@ Pointfs arrange(size_t num_parts, const Pointf &part_size, coordf_t gap, const B
Pointfs positions;
positions.reserve(num_parts);
for (std::vector<ArrangeItem>::const_iterator it = cellsorder.begin(); it != cellsorder.end(); ++ it)
- positions.push_back(Pointf(it->pos.x - 0.5 * part_size.x, it->pos.y - 0.5 * part_size.y));
+ positions.push_back(Vec2d(it->pos(0) - 0.5 * part_size(0), it->pos(1) - 0.5 * part_size(1)));
return positions;
}
#else
class ArrangeItem {
- public:
- Pointf pos;
+public:
+ Vec2d pos = Vec2d::Zero();
size_t index_x, index_y;
coordf_t dist;
};
class ArrangeItemIndex {
- public:
+public:
coordf_t index;
ArrangeItem item;
ArrangeItemIndex(coordf_t _index, ArrangeItem _item) : index(_index), item(_item) {};
};
bool
-arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions)
+arrange(size_t total_parts, const Vec2d &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions)
{
positions.clear();
- Pointf part = part_size;
+ Vec2d part = part_size;
// use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm
- part.x += dist;
- part.y += dist;
+ part(0) += dist;
+ part(1) += dist;
- Pointf area;
+ Vec2d area(Vec2d::Zero());
if (bb != NULL && bb->defined) {
area = bb->size();
} else {
// bogus area size, large enough not to trigger the error below
- area.x = part.x * total_parts;
- area.y = part.y * total_parts;
+ area(0) = part(0) * total_parts;
+ area(1) = part(1) * total_parts;
}
// this is how many cells we have available into which to put parts
- size_t cellw = floor((area.x + dist) / part.x);
- size_t cellh = floor((area.y + dist) / part.y);
+ size_t cellw = floor((area(0) + dist) / part(0));
+ size_t cellh = floor((area(1) + dist) / part(1));
if (total_parts > (cellw * cellh))
return false;
// total space used by cells
- Pointf cells(cellw * part.x, cellh * part.y);
+ Vec2d cells(cellw * part(0), cellh * part(1));
// bounding box of total space used by cells
BoundingBoxf cells_bb;
- cells_bb.merge(Pointf(0,0)); // min
+ cells_bb.merge(Vec2d(0,0)); // min
cells_bb.merge(cells); // max
// center bounding box to area
cells_bb.translate(
- (area.x - cells.x) / 2,
- (area.y - cells.y) / 2
+ (area(0) - cells(0)) / 2,
+ (area(1) - cells(1)) / 2
);
// list of cells, sorted by distance from center
@@ -468,15 +531,15 @@ arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const Boundi
// work out distance for all cells, sort into list
for (size_t i = 0; i <= cellw-1; ++i) {
for (size_t j = 0; j <= cellh-1; ++j) {
- coordf_t cx = linint(i + 0.5, 0, cellw, cells_bb.min.x, cells_bb.max.x);
- coordf_t cy = linint(j + 0.5, 0, cellh, cells_bb.min.y, cells_bb.max.y);
+ coordf_t cx = linint(i + 0.5, 0, cellw, cells_bb.min(0), cells_bb.max(0));
+ coordf_t cy = linint(j + 0.5, 0, cellh, cells_bb.min(1), cells_bb.max(1));
- coordf_t xd = fabs((area.x / 2) - cx);
- coordf_t yd = fabs((area.y / 2) - cy);
+ coordf_t xd = fabs((area(0) / 2) - cx);
+ coordf_t yd = fabs((area(1) / 2) - cy);
ArrangeItem c;
- c.pos.x = cx;
- c.pos.y = cy;
+ c.pos(0) = cx;
+ c.pos(1) = cy;
c.index_x = i;
c.index_y = j;
c.dist = xd * xd + yd * yd - fabs((cellw / 2) - (i + 0.5));
@@ -533,13 +596,13 @@ arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const Boundi
coordf_t cx = c.item.index_x - lx;
coordf_t cy = c.item.index_y - ty;
- positions.push_back(Pointf(cx * part.x, cy * part.y));
+ positions.push_back(Vec2d(cx * part(0), cy * part(1)));
}
if (bb != NULL && bb->defined) {
for (Pointfs::iterator p = positions.begin(); p != positions.end(); ++p) {
- p->x += bb->min.x;
- p->y += bb->min.y;
+ p->x() += bb->min(0);
+ p->y() += bb->min(1);
}
}
@@ -608,15 +671,15 @@ namespace Voronoi { namespace Internal {
if (cell1.contains_point() && cell2.contains_point()) {
point_type p1 = retrieve_point(segments, cell1);
point_type p2 = retrieve_point(segments, cell2);
- origin.x((p1.x() + p2.x()) * 0.5);
- origin.y((p1.y() + p2.y()) * 0.5);
- direction.x(p1.y() - p2.y());
- direction.y(p2.x() - p1.x());
+ origin.x((p1(0) + p2(0)) * 0.5);
+ origin.y((p1(1) + p2(1)) * 0.5);
+ direction.x(p1(1) - p2(1));
+ direction.y(p2(0) - p1(0));
} else {
origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1);
segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()];
- coordinate_type dx = high(segment).x() - low(segment).x();
- coordinate_type dy = high(segment).y() - low(segment).y();
+ coordinate_type dx = high(segment)(0) - low(segment)(0);
+ coordinate_type dy = high(segment)(1) - low(segment)(1);
if ((low(segment) == origin) ^ cell1.contains_point()) {
direction.x(dy);
direction.y(-dx);
@@ -625,19 +688,19 @@ namespace Voronoi { namespace Internal {
direction.y(dx);
}
}
- coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y()));
+ coordinate_type koef = bbox_max_size / (std::max)(fabs(direction(0)), fabs(direction(1)));
if (edge.vertex0() == NULL) {
clipped_edge->push_back(point_type(
- origin.x() - direction.x() * koef,
- origin.y() - direction.y() * koef));
+ origin(0) - direction(0) * koef,
+ origin(1) - direction(1) * koef));
} else {
clipped_edge->push_back(
point_type(edge.vertex0()->x(), edge.vertex0()->y()));
}
if (edge.vertex1() == NULL) {
clipped_edge->push_back(point_type(
- origin.x() + direction.x() * koef,
- origin.y() + direction.y() * koef));
+ origin(0) + direction(0) * koef,
+ origin(1) + direction(1) * koef));
} else {
clipped_edge->push_back(
point_type(edge.vertex1()->x(), edge.vertex1()->y()));
@@ -676,10 +739,10 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
const bool primaryEdgesOnly = false;
BoundingBox bbox = BoundingBox(lines);
- bbox.min.x -= coord_t(1. / SCALING_FACTOR);
- bbox.min.y -= coord_t(1. / SCALING_FACTOR);
- bbox.max.x += coord_t(1. / SCALING_FACTOR);
- bbox.max.y += coord_t(1. / SCALING_FACTOR);
+ bbox.min(0) -= coord_t(1. / SCALING_FACTOR);
+ bbox.min(1) -= coord_t(1. / SCALING_FACTOR);
+ bbox.max(0) += coord_t(1. / SCALING_FACTOR);
+ bbox.max(1) += coord_t(1. / SCALING_FACTOR);
::Slic3r::SVG svg(path, bbox);
@@ -689,7 +752,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
// bbox.scale(1.2);
// For clipping of half-lines to some reasonable value.
// The line will then be clipped by the SVG viewer anyway.
- const double bbox_dim_max = double(bbox.max.x - bbox.min.x) + double(bbox.max.y - bbox.min.y);
+ const double bbox_dim_max = double(bbox.max(0) - bbox.min(0)) + double(bbox.max(1) - bbox.min(1));
// For the discretization of the Voronoi parabolic segments.
const double discretization_step = 0.0005 * bbox_dim_max;
@@ -697,8 +760,8 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
std::vector<Voronoi::Internal::segment_type> segments;
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it)
segments.push_back(Voronoi::Internal::segment_type(
- Voronoi::Internal::point_type(double(it->a.x), double(it->a.y)),
- Voronoi::Internal::point_type(double(it->b.x), double(it->b.y))));
+ Voronoi::Internal::point_type(double(it->a(0)), double(it->a(1))),
+ Voronoi::Internal::point_type(double(it->b(0)), double(it->b(1)))));
// Color exterior edges.
for (voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it)
@@ -712,13 +775,13 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
}
// Draw the input polygon.
for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
- svg.draw(Line(Point(coord_t(it->a.x), coord_t(it->a.y)), Point(coord_t(it->b.x), coord_t(it->b.y))), inputSegmentColor, inputSegmentLineWidth);
+ svg.draw(Line(Point(coord_t(it->a(0)), coord_t(it->a(1))), Point(coord_t(it->b(0)), coord_t(it->b(1)))), inputSegmentColor, inputSegmentLineWidth);
#if 1
// Draw voronoi vertices.
for (voronoi_diagram<double>::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it)
if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR)
- svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius);
+ svg.draw(Point(coord_t((*it)(0)), coord_t((*it)(1))), voronoiPointColor, voronoiPointRadius);
for (voronoi_diagram<double>::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) {
if (primaryEdgesOnly && !it->is_primary())
@@ -743,7 +806,7 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
color = voronoiLineColorSecondary;
}
for (std::size_t i = 0; i + 1 < samples.size(); ++i)
- svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth);
+ svg.draw(Line(Point(coord_t(samples[i](0)), coord_t(samples[i](1))), Point(coord_t(samples[i+1](0)), coord_t(samples[i+1](1)))), color, voronoiLineWidth);
}
#endif
@@ -758,8 +821,8 @@ static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_d
template<typename T>
T dist(const boost::polygon::point_data<T> &p1,const boost::polygon::point_data<T> &p2)
{
- T dx = p2.x() - p1.x();
- T dy = p2.y() - p1.y();
+ T dx = p2(0) - p1(0);
+ T dy = p2(1) - p1(1);
return sqrt(dx*dx+dy*dy);
}
@@ -770,11 +833,11 @@ inline point_type project_point_to_segment(segment_type &seg, point_type &px)
typedef typename point_type::coordinate_type T;
const point_type &p0 = low(seg);
const point_type &p1 = high(seg);
- const point_type dir(p1.x()-p0.x(), p1.y()-p0.y());
- const point_type dproj(px.x()-p0.x(), px.y()-p0.y());
- const T t = (dir.x()*dproj.x() + dir.y()*dproj.y()) / (dir.x()*dir.x() + dir.y()*dir.y());
+ const point_type dir(p1(0)-p0(0), p1(1)-p0(1));
+ const point_type dproj(px(0)-p0(0), px(1)-p0(1));
+ const T t = (dir(0)*dproj(0) + dir(1)*dproj(1)) / (dir(0)*dir(0) + dir(1)*dir(1));
assert(t >= T(-1e-6) && t <= T(1. + 1e-6));
- return point_type(p0.x() + t*dir.x(), p0.y() + t*dir.y());
+ return point_type(p0(0) + t*dir(0), p0(1) + t*dir(1));
}
template<typename VD, typename SEGMENTS>
@@ -828,8 +891,8 @@ public:
Lines2VDSegments(const Lines &alines) : lines(alines) {}
typename VD::segment_type operator[](size_t idx) const {
return typename VD::segment_type(
- typename VD::point_type(typename VD::coord_type(lines[idx].a.x), typename VD::coord_type(lines[idx].a.y)),
- typename VD::point_type(typename VD::coord_type(lines[idx].b.x), typename VD::coord_type(lines[idx].b.y)));
+ typename VD::point_type(typename VD::coord_type(lines[idx].a(0)), typename VD::coord_type(lines[idx].a(1))),
+ typename VD::point_type(typename VD::coord_type(lines[idx].b(0)), typename VD::coord_type(lines[idx].b(1))));
}
private:
const Lines &lines;
@@ -910,7 +973,7 @@ MedialAxis::build(ThickPolylines* polylines)
assert(polyline.width.size() == polyline.points.size()*2 - 2);
// prevent loop endpoints from being extended
- if (polyline.first_point().coincides_with(polyline.last_point())) {
+ if (polyline.first_point() == polyline.last_point()) {
polyline.endpoints.first = false;
polyline.endpoints.second = false;
}
@@ -1003,7 +1066,7 @@ MedialAxis::validate_edge(const VD::edge_type* edge)
// this could maybe be optimized (checking inclusion of the endpoints
// might give false positives as they might belong to the contour itself)
if (this->expolygon != NULL) {
- if (line.a.coincides_with(line.b)) {
+ if (line.a == line.b) {
// in this case, contains(line) returns a false positive
if (!this->expolygon->contains(line.a)) return false;
} else {
@@ -1042,12 +1105,12 @@ MedialAxis::validate_edge(const VD::edge_type* edge)
calculate the distance to that endpoint instead. */
coordf_t w0 = cell_r->contains_segment()
- ? line.a.distance_to(segment_r)*2
- : line.a.distance_to(this->retrieve_endpoint(cell_r))*2;
+ ? segment_r.distance_to(line.a)*2
+ : (this->retrieve_endpoint(cell_r) - line.a).cast<double>().norm()*2;
coordf_t w1 = cell_l->contains_segment()
- ? line.b.distance_to(segment_l)*2
- : line.b.distance_to(this->retrieve_endpoint(cell_l))*2;
+ ? segment_l.distance_to(line.b)*2
+ : (this->retrieve_endpoint(cell_l) - line.b).cast<double>().norm()*2;
if (cell_l->contains_segment() && cell_r->contains_segment()) {
// calculate the relative angle between the two boundary segments
diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp
index c2c3dc8b7..3698b996f 100644
--- a/xs/src/libslic3r/Geometry.hpp
+++ b/xs/src/libslic3r/Geometry.hpp
@@ -30,9 +30,9 @@ enum Orientation
static inline Orientation orient(const Point &a, const Point &b, const Point &c)
{
// BOOST_STATIC_ASSERT(sizeof(coord_t) * 2 == sizeof(int64_t));
- int64_t u = int64_t(b.x) * int64_t(c.y) - int64_t(b.y) * int64_t(c.x);
- int64_t v = int64_t(a.x) * int64_t(c.y) - int64_t(a.y) * int64_t(c.x);
- int64_t w = int64_t(a.x) * int64_t(b.y) - int64_t(a.y) * int64_t(b.x);
+ int64_t u = int64_t(b(0)) * int64_t(c(1)) - int64_t(b(1)) * int64_t(c(0));
+ int64_t v = int64_t(a(0)) * int64_t(c(1)) - int64_t(a(1)) * int64_t(c(0));
+ int64_t w = int64_t(a(0)) * int64_t(b(1)) - int64_t(a(1)) * int64_t(b(0));
int64_t d = u - v + w;
return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW);
}
@@ -52,7 +52,7 @@ static inline bool is_ccw(const Polygon &poly)
for (unsigned int i = 1; i < poly.points.size(); ++ i) {
const Point &pmin = poly.points[imin];
const Point &p = poly.points[i];
- if (p.x < pmin.x || (p.x == pmin.x && p.y < pmin.y))
+ if (p(0) < pmin(0) || (p(0) == pmin(0) && p(1) < pmin(1)))
imin = i;
}
@@ -66,26 +66,26 @@ static inline bool is_ccw(const Polygon &poly)
return o == ORIENTATION_CCW;
}
-inline bool ray_ray_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res)
+inline bool ray_ray_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res)
{
- double denom = v1.x * v2.y - v2.x * v1.y;
+ double denom = v1(0) * v2(1) - v2(0) * v1(1);
if (std::abs(denom) < EPSILON)
return false;
- double t = (v2.x * (p1.y - p2.y) - v2.y * (p1.x - p2.x)) / denom;
- res.x = p1.x + t * v1.x;
- res.y = p1.y + t * v1.y;
+ double t = (v2(0) * (p1(1) - p2(1)) - v2(1) * (p1(0) - p2(0))) / denom;
+ res(0) = p1(0) + t * v1(0);
+ res(1) = p1(1) + t * v1(1);
return true;
}
-inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, const Pointf &p2, const Vectorf &v2, Pointf &res)
+inline bool segment_segment_intersection(const Vec2d &p1, const Vec2d &v1, const Vec2d &p2, const Vec2d &v2, Vec2d &res)
{
- double denom = v1.x * v2.y - v2.x * v1.y;
+ double denom = v1(0) * v2(1) - v2(0) * v1(1);
if (std::abs(denom) < EPSILON)
// Lines are collinear.
return false;
- double s12_x = p1.x - p2.x;
- double s12_y = p1.y - p2.y;
- double s_numer = v1.x * s12_y - v1.y * s12_x;
+ double s12_x = p1(0) - p2(0);
+ double s12_y = p1(1) - p2(1);
+ double s_numer = v1(0) * s12_y - v1(1) * s12_x;
bool denom_is_positive = false;
if (denom < 0.) {
denom_is_positive = true;
@@ -95,7 +95,7 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co
if (s_numer < 0.)
// Intersection outside of the 1st segment.
return false;
- double t_numer = v2.x * s12_y - v2.y * s12_x;
+ double t_numer = v2(0) * s12_y - v2(1) * s12_x;
if (! denom_is_positive)
t_numer = - t_numer;
if (t_numer < 0. || s_numer > denom || t_numer > denom)
@@ -103,13 +103,15 @@ inline bool segment_segment_intersection(const Pointf &p1, const Vectorf &v1, co
return false;
// Intersection inside both of the segments.
double t = t_numer / denom;
- res.x = p1.x + t * v1.x;
- res.y = p1.y + t * v1.y;
+ res(0) = p1(0) + t * v1(0);
+ res(1) = p1(1) + t * v1(1);
return true;
}
+Pointf3s convex_hull(Pointf3s points);
Polygon convex_hull(Points points);
Polygon convex_hull(const Polygons &polygons);
+
void chained_path(const Points &points, std::vector<Points::size_type> &retval, Point start_near);
void chained_path(const Points &points, std::vector<Points::size_type> &retval);
template<class T> void chained_path_items(Points &points, T &items, T &retval);
@@ -123,7 +125,7 @@ void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* ret
double linint(double value, double oldmin, double oldmax, double newmin, double newmax);
bool arrange(
// input
- size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box,
+ size_t num_parts, const Vec2d &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box,
// output
Pointfs &positions);
diff --git a/xs/src/libslic3r/I18N.hpp b/xs/src/libslic3r/I18N.hpp
new file mode 100644
index 000000000..db4fd22df
--- /dev/null
+++ b/xs/src/libslic3r/I18N.hpp
@@ -0,0 +1,18 @@
+#ifndef slic3r_I18N_hpp_
+#define slic3r_I18N_hpp_
+
+#include <string>
+
+namespace Slic3r {
+
+namespace I18N {
+ typedef std::string (*translate_fn_type)(const char*);
+ extern translate_fn_type translate_fn;
+ inline void set_translate_callback(translate_fn_type fn) { translate_fn = fn; }
+ inline std::string translate(const std::string &s) { return (translate_fn == nullptr) ? s : (*translate_fn)(s.c_str()); }
+ inline std::string translate(const char *ptr) { return (translate_fn == nullptr) ? std::string(ptr) : (*translate_fn)(ptr); }
+} // namespace I18N
+
+} // namespace Slic3r
+
+#endif /* slic3r_I18N_hpp_ */
diff --git a/xs/src/libslic3r/Int128.hpp b/xs/src/libslic3r/Int128.hpp
index 7bf0c87b4..d54b75342 100644
--- a/xs/src/libslic3r/Int128.hpp
+++ b/xs/src/libslic3r/Int128.hpp
@@ -48,7 +48,6 @@
#endif
#include <cassert>
-#include "Point.hpp"
#if ! defined(_MSC_VER) && defined(__SIZEOF_INT128__)
#define HAS_INTRINSIC_128_TYPE
@@ -288,20 +287,4 @@ public:
}
return sign_determinant_2x2(p1, q1, p2, q2) * invert;
}
-
- // Exact orientation predicate,
- // returns +1: CCW, 0: collinear, -1: CW.
- static int orient(const Slic3r::Point &p1, const Slic3r::Point &p2, const Slic3r::Point &p3)
- {
- Slic3r::Vector v1(p2 - p1);
- Slic3r::Vector v2(p3 - p1);
- return sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
- }
-
- // Exact orientation predicate,
- // returns +1: CCW, 0: collinear, -1: CW.
- static int cross(const Slic3r::Point &v1, const Slic3r::Point &v2)
- {
- return sign_determinant_2x2_filtered(v1.x, v1.y, v2.x, v2.y);
- }
};
diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp
index 72ac371a2..6c2bd0da9 100644
--- a/xs/src/libslic3r/Layer.cpp
+++ b/xs/src/libslic3r/Layer.cpp
@@ -167,8 +167,8 @@ void Layer::export_region_slices_to_svg(const char *path) const
for (const auto &surface : region->slices.surfaces)
bbox.merge(get_extents(surface.expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
@@ -193,8 +193,8 @@ void Layer::export_region_fill_surfaces_to_svg(const char *path) const
for (const auto &surface : region->slices.surfaces)
bbox.merge(get_extents(surface.expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp
index c576d7eec..65184c9d1 100644
--- a/xs/src/libslic3r/LayerRegion.cpp
+++ b/xs/src/libslic3r/LayerRegion.cpp
@@ -392,8 +392,8 @@ void LayerRegion::export_region_slices_to_svg(const char *path) const
for (Surfaces::const_iterator surface = this->slices.surfaces.begin(); surface != this->slices.surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
@@ -419,8 +419,8 @@ void LayerRegion::export_region_fill_surfaces_to_svg(const char *path) const
for (Surfaces::const_iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp
index e9d5d7742..35cfa2b76 100644
--- a/xs/src/libslic3r/Line.cpp
+++ b/xs/src/libslic3r/Line.cpp
@@ -7,133 +7,70 @@
namespace Slic3r {
-std::string
-Line::wkt() const
+Linef3 transform(const Linef3& line, const Transform3d& t)
{
- std::ostringstream ss;
- ss << "LINESTRING(" << this->a.x << " " << this->a.y << ","
- << this->b.x << " " << this->b.y << ")";
- return ss.str();
-}
-
-Line::operator Lines() const
-{
- Lines lines;
- lines.push_back(*this);
- return lines;
-}
-
-Line::operator Polyline() const
-{
- Polyline pl;
- pl.points.push_back(this->a);
- pl.points.push_back(this->b);
- return pl;
-}
-
-void
-Line::scale(double factor)
-{
- this->a.scale(factor);
- this->b.scale(factor);
-}
-
-void
-Line::translate(double x, double y)
-{
- this->a.translate(x, y);
- this->b.translate(x, y);
-}
-
-void
-Line::rotate(double angle, const Point &center)
-{
- this->a.rotate(angle, center);
- this->b.rotate(angle, center);
-}
-
-void
-Line::reverse()
-{
- std::swap(this->a, this->b);
-}
+ typedef Eigen::Matrix<double, 3, 2> LineInMatrixForm;
-double
-Line::length() const
-{
- return this->a.distance_to(this->b);
-}
+ LineInMatrixForm world_line;
+ ::memcpy((void*)world_line.col(0).data(), (const void*)line.a.data(), 3 * sizeof(double));
+ ::memcpy((void*)world_line.col(1).data(), (const void*)line.b.data(), 3 * sizeof(double));
-Point
-Line::midpoint() const
-{
- return Point((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0);
+ LineInMatrixForm local_line = t * world_line.colwise().homogeneous();
+ return Linef3(Vec3d(local_line(0, 0), local_line(1, 0), local_line(2, 0)), Vec3d(local_line(0, 1), local_line(1, 1), local_line(2, 1)));
}
-void
-Line::point_at(double distance, Point* point) const
+bool Line::intersection_infinite(const Line &other, Point* point) const
{
- double len = this->length();
- *point = this->a;
- if (this->a.x != this->b.x)
- point->x = this->a.x + (this->b.x - this->a.x) * distance / len;
- if (this->a.y != this->b.y)
- point->y = this->a.y + (this->b.y - this->a.y) * distance / len;
-}
-
-Point
-Line::point_at(double distance) const
-{
- Point p;
- this->point_at(distance, &p);
- return p;
-}
-
-bool
-Line::intersection_infinite(const Line &other, Point* point) const
-{
- Vector x = this->a.vector_to(other.a);
- Vector d1 = this->vector();
- Vector d2 = other.vector();
-
- double cross = d1.x * d2.y - d1.y * d2.x;
- if (std::fabs(cross) < EPSILON)
+ Vec2d a1 = this->a.cast<double>();
+ Vec2d a2 = other.a.cast<double>();
+ Vec2d v12 = (other.a - this->a).cast<double>();
+ Vec2d v1 = (this->b - this->a).cast<double>();
+ Vec2d v2 = (other.b - other.a).cast<double>();
+ double denom = cross2(v1, v2);
+ if (std::fabs(denom) < EPSILON)
return false;
-
- double t1 = (x.x * d2.y - x.y * d2.x)/cross;
- point->x = this->a.x + d1.x * t1;
- point->y = this->a.y + d1.y * t1;
+ double t1 = cross2(v12, v2) / denom;
+ *point = (a1 + t1 * v1).cast<coord_t>();
return true;
}
-bool
-Line::coincides_with(const Line &line) const
+/* distance to the closest point of line */
+double Line::distance_to(const Point &point) const
{
- return this->a.coincides_with(line.a) && this->b.coincides_with(line.b);
+ const Line &line = *this;
+ const Vec2d v = (line.b - line.a).cast<double>();
+ const Vec2d va = (point - line.a).cast<double>();
+ const double l2 = v.squaredNorm(); // avoid a sqrt
+ if (l2 == 0.0)
+ // line.a == line.b case
+ return va.norm();
+ // Consider the line extending the segment, parameterized as line.a + t (line.b - line.a).
+ // We find projection of this point onto the line.
+ // It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2
+ const double t = va.dot(v) / l2;
+ if (t < 0.0) return va.norm(); // beyond the 'a' end of the segment
+ else if (t > 1.0) return (point - line.b).cast<double>().norm(); // beyond the 'b' end of the segment
+ return (t * v - va).norm();
}
-double
-Line::distance_to(const Point &point) const
+double Line::perp_distance_to(const Point &point) const
{
- return point.distance_to(*this);
+ const Line &line = *this;
+ const Vec2d v = (line.b - line.a).cast<double>();
+ const Vec2d va = (point - line.a).cast<double>();
+ if (line.a == line.b)
+ return va.norm();
+ return std::abs(cross2(v, va)) / v.norm();
}
-double
-Line::atan2_() const
-{
- return atan2(this->b.y - this->a.y, this->b.x - this->a.x);
-}
-
-double
-Line::orientation() const
+double Line::orientation() const
{
double angle = this->atan2_();
if (angle < 0) angle = 2*PI + angle;
return angle;
}
-double
-Line::direction() const
+double Line::direction() const
{
double atan2 = this->atan2_();
return (fabs(atan2 - PI) < EPSILON) ? 0
@@ -141,108 +78,42 @@ Line::direction() const
: atan2;
}
-bool
-Line::parallel_to(double angle) const {
- return Slic3r::Geometry::directions_parallel(this->direction(), angle);
-}
-
-bool
-Line::parallel_to(const Line &line) const {
- return this->parallel_to(line.direction());
-}
-
-Vector
-Line::vector() const
+bool Line::parallel_to(double angle) const
{
- return Vector(this->b.x - this->a.x, this->b.y - this->a.y);
-}
-
-Vector
-Line::normal() const
-{
- return Vector((this->b.y - this->a.y), -(this->b.x - this->a.x));
-}
-
-void
-Line::extend_end(double distance)
-{
- // relocate last point by extending the segment by the specified length
- Line line = *this;
- line.reverse();
- this->b = line.point_at(-distance);
-}
-
-void
-Line::extend_start(double distance)
-{
- // relocate first point by extending the first segment by the specified length
- this->a = this->point_at(-distance);
+ return Slic3r::Geometry::directions_parallel(this->direction(), angle);
}
-bool
-Line::intersection(const Line& line, Point* intersection) const
-{
- double denom = ((double)(line.b.y - line.a.y)*(this->b.x - this->a.x)) -
- ((double)(line.b.x - line.a.x)*(this->b.y - this->a.y));
-
- double nume_a = ((double)(line.b.x - line.a.x)*(this->a.y - line.a.y)) -
- ((double)(line.b.y - line.a.y)*(this->a.x - line.a.x));
-
- double nume_b = ((double)(this->b.x - this->a.x)*(this->a.y - line.a.y)) -
- ((double)(this->b.y - this->a.y)*(this->a.x - line.a.x));
-
- if (fabs(denom) < EPSILON) {
- if (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON) {
- return false; // coincident
- }
- return false; // parallel
- }
-
- double ua = nume_a / denom;
- double ub = nume_b / denom;
-
- if (ua >= 0 && ua <= 1.0f && ub >= 0 && ub <= 1.0f)
- {
+bool Line::intersection(const Line &l2, Point *intersection) const
+{
+ const Line &l1 = *this;
+ const Vec2d v1 = (l1.b - l1.a).cast<double>();
+ const Vec2d v2 = (l2.b - l2.a).cast<double>();
+ const Vec2d v12 = (l1.a - l2.a).cast<double>();
+ double denom = cross2(v1, v2);
+ double nume_a = cross2(v2, v12);
+ double nume_b = cross2(v1, v12);
+ if (fabs(denom) < EPSILON)
+#if 0
+ // Lines are collinear. Return true if they are coincident (overlappign).
+ return ! (fabs(nume_a) < EPSILON && fabs(nume_b) < EPSILON);
+#else
+ return false;
+#endif
+ double t1 = nume_a / denom;
+ double t2 = nume_b / denom;
+ if (t1 >= 0 && t1 <= 1.0f && t2 >= 0 && t2 <= 1.0f) {
// Get the intersection point.
- intersection->x = this->a.x + ua*(this->b.x - this->a.x);
- intersection->y = this->a.y + ua*(this->b.y - this->a.y);
+ (*intersection) = (l1.a.cast<double>() + t1 * v1).cast<coord_t>();
return true;
}
-
return false; // not intersecting
}
-double
-Line::ccw(const Point& point) const
-{
- return point.ccw(*this);
-}
-
-double Line3::length() const
-{
- return a.distance_to(b);
-}
-
-Vector3 Line3::vector() const
-{
- return Vector3(b.x - a.x, b.y - a.y, b.z - a.z);
-}
-
-Pointf3
-Linef3::intersect_plane(double z) const
-{
- return Pointf3(
- this->a.x + (this->b.x - this->a.x) * (z - this->a.z) / (this->b.z - this->a.z),
- this->a.y + (this->b.y - this->a.y) * (z - this->a.z) / (this->b.z - this->a.z),
- z
- );
-}
-
-void
-Linef3::scale(double factor)
+Vec3d Linef3::intersect_plane(double z) const
{
- this->a.scale(factor);
- this->b.scale(factor);
+ auto v = (this->b - this->a).cast<double>();
+ double t = (z - this->a(2)) / v(2);
+ return Vec3d(this->a(0) + v(0) * t, this->a(1) + v(1) * t, z);
}
}
diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp
index 4826017ab..36e02247c 100644
--- a/xs/src/libslic3r/Line.hpp
+++ b/xs/src/libslic3r/Line.hpp
@@ -15,80 +15,85 @@ typedef std::vector<Line> Lines;
typedef std::vector<Line3> Lines3;
typedef std::vector<ThickLine> ThickLines;
+Linef3 transform(const Linef3& line, const Transform3d& t);
+
class Line
{
public:
- Point a;
- Point b;
- Line() {};
- explicit Line(Point _a, Point _b): a(_a), b(_b) {};
- std::string wkt() const;
- operator Lines() const;
- operator Polyline() const;
- void scale(double factor);
- void translate(double x, double y);
- void rotate(double angle, const Point &center);
- void reverse();
- double length() const;
- Point midpoint() const;
- void point_at(double distance, Point* point) const;
- Point point_at(double distance) const;
- bool intersection_infinite(const Line &other, Point* point) const;
- bool coincides_with(const Line &line) const;
+ Line() {}
+ Line(const Point& _a, const Point& _b) : a(_a), b(_b) {}
+ explicit operator Lines() const { Lines lines; lines.emplace_back(*this); return lines; }
+ void scale(double factor) { this->a *= factor; this->b *= factor; }
+ void translate(double x, double y) { Vector v(x, y); this->a += v; this->b += v; }
+ void rotate(double angle, const Point &center) { this->a.rotate(angle, center); this->b.rotate(angle, center); }
+ void reverse() { std::swap(this->a, this->b); }
+ double length() const { return (b - a).cast<double>().norm(); }
+ Point midpoint() const { return (this->a + this->b) / 2; }
+ bool intersection_infinite(const Line &other, Point* point) const;
+ bool operator==(const Line &rhs) const { return this->a == rhs.a && this->b == rhs.b; }
double distance_to(const Point &point) const;
- bool parallel_to(double angle) const;
- bool parallel_to(const Line &line) const;
- double atan2_() const;
+ double perp_distance_to(const Point &point) const;
+ bool parallel_to(double angle) const;
+ bool parallel_to(const Line &line) const { return this->parallel_to(line.direction()); }
+ double atan2_() const { return atan2(this->b(1) - this->a(1), this->b(0) - this->a(0)); }
double orientation() const;
double direction() const;
- Vector vector() const;
- Vector normal() const;
- void extend_end(double distance);
- void extend_start(double distance);
- bool intersection(const Line& line, Point* intersection) const;
- double ccw(const Point& point) const;
+ Vector vector() const { return this->b - this->a; }
+ Vector normal() const { return Vector((this->b(1) - this->a(1)), -(this->b(0) - this->a(0))); }
+ bool intersection(const Line& line, Point* intersection) const;
+ double ccw(const Point& point) const { return point.ccw(*this); }
+
+ Point a;
+ Point b;
};
class ThickLine : public Line
{
- public:
- coordf_t a_width, b_width;
-
- ThickLine() : a_width(0), b_width(0) {};
- ThickLine(Point _a, Point _b) : Line(_a, _b), a_width(0), b_width(0) {};
+public:
+ ThickLine() : a_width(0), b_width(0) {}
+ ThickLine(const Point& a, const Point& b) : Line(a, b), a_width(0), b_width(0) {}
+ ThickLine(const Point& a, const Point& b, double wa, double wb) : Line(a, b), a_width(wa), b_width(wb) {}
+
+ double a_width, b_width;
};
class Line3
{
public:
- Point3 a;
- Point3 b;
+ Line3() : a(Vec3crd::Zero()), b(Vec3crd::Zero()) {}
+ Line3(const Vec3crd& _a, const Vec3crd& _b) : a(_a), b(_b) {}
- Line3() {}
- Line3(const Point3& _a, const Point3& _b) : a(_a), b(_b) {}
+ double length() const { return (this->a - this->b).cast<double>().norm(); }
+ Vec3crd vector() const { return this->b - this->a; }
- double length() const;
- Vector3 vector() const;
+ Vec3crd a;
+ Vec3crd b;
};
class Linef
{
- public:
- Pointf a;
- Pointf b;
- Linef() {};
- explicit Linef(Pointf _a, Pointf _b): a(_a), b(_b) {};
+public:
+ Linef() : a(Vec2d::Zero()), b(Vec2d::Zero()) {}
+ Linef(const Vec2d& _a, const Vec2d& _b) : a(_a), b(_b) {}
+
+ Vec2d a;
+ Vec2d b;
};
class Linef3
{
- public:
- Pointf3 a;
- Pointf3 b;
- Linef3() {};
- explicit Linef3(Pointf3 _a, Pointf3 _b): a(_a), b(_b) {};
- Pointf3 intersect_plane(double z) const;
- void scale(double factor);
+public:
+ Linef3() : a(Vec3d::Zero()), b(Vec3d::Zero()) {}
+ Linef3(const Vec3d& _a, const Vec3d& _b) : a(_a), b(_b) {}
+
+ Vec3d intersect_plane(double z) const;
+ void scale(double factor) { this->a *= factor; this->b *= factor; }
+ Vec3d vector() const { return this->b - this->a; }
+ Vec3d unit_vector() const { return (length() == 0.0) ? Vec3d::Zero() : vector().normalized(); }
+ double length() const { return vector().norm(); }
+
+ Vec3d a;
+ Vec3d b;
};
} // namespace Slic3r
diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp
index 6f1ce1ce5..19c474cad 100644
--- a/xs/src/libslic3r/Model.cpp
+++ b/xs/src/libslic3r/Model.cpp
@@ -14,8 +14,13 @@
#include <boost/nowide/iostream.hpp>
#include <boost/algorithm/string/replace.hpp>
+#include "SVG.hpp"
+#include <Eigen/Dense>
+
namespace Slic3r {
+ unsigned int Model::s_auto_extruder_id = 1;
+
Model::Model(const Model &other)
{
// copy materials
@@ -230,28 +235,17 @@ BoundingBoxf3 Model::bounding_box() const
return bb;
}
-BoundingBoxf3 Model::transformed_bounding_box() const
-{
- BoundingBoxf3 bb;
- for (const ModelObject* obj : this->objects)
- bb.merge(obj->tight_bounding_box(false));
- return bb;
-}
-
-void Model::center_instances_around_point(const Pointf &point)
+void Model::center_instances_around_point(const Vec2d &point)
{
-// BoundingBoxf3 bb = this->bounding_box();
BoundingBoxf3 bb;
for (ModelObject *o : this->objects)
for (size_t i = 0; i < o->instances.size(); ++ i)
bb.merge(o->instance_bounding_box(i, false));
- Sizef3 size = bb.size();
- coordf_t shift_x = -bb.min.x + point.x - size.x/2;
- coordf_t shift_y = -bb.min.y + point.y - size.y/2;
+ Vec2d shift = point - 0.5 * to_2d(bb.size()) - to_2d(bb.min);
for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances)
- i->offset.translate(shift_x, shift_y);
+ i->offset += shift;
o->invalidate_bounding_box();
}
}
@@ -267,6 +261,10 @@ TriangleMesh Model::mesh() const
static bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out)
{
+ if (sizes.empty())
+ // return if the list is empty or the following call to BoundingBoxf constructor will lead to a crash
+ return true;
+
// we supply unscaled data to arrange()
bool result = Slic3r::Geometry::arrange(
sizes.size(), // number of parts
@@ -302,29 +300,30 @@ bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb)
for (size_t i = 0; i < o->instances.size(); ++ i) {
// an accurate snug bounding box around the transformed mesh.
BoundingBoxf3 bbox(o->instance_bounding_box(i, true));
- instance_sizes.push_back(bbox.size());
- instance_centers.push_back(bbox.center());
+ instance_sizes.emplace_back(to_2d(bbox.size()));
+ instance_centers.emplace_back(to_2d(bbox.center()));
}
Pointfs positions;
if (! _arrange(instance_sizes, dist, bb, positions))
return false;
-
- size_t idx = 0;
- for (ModelObject *o : this->objects) {
+
+ size_t idx = 0;
+ for (ModelObject *o : this->objects) {
for (ModelInstance *i : o->instances) {
i->offset = positions[idx] - instance_centers[idx];
++ idx;
}
o->invalidate_bounding_box();
}
+
return true;
}
// Duplicate the entire model preserving instance relative positions.
void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
{
- Pointfs model_sizes(copies_num-1, this->bounding_box().size());
+ Pointfs model_sizes(copies_num-1, to_2d(this->bounding_box().size()));
Pointfs positions;
if (! _arrange(model_sizes, dist, bb, positions))
CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n");
@@ -335,9 +334,9 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb)
// make a copy of the pointers in order to avoid recursion when appending their copies
ModelInstancePtrs instances = o->instances;
for (const ModelInstance *i : instances) {
- for (const Pointf &pos : positions) {
+ for (const Vec2d &pos : positions) {
ModelInstance *instance = o->add_instance(*i);
- instance->offset.translate(pos);
+ instance->offset += pos;
}
}
o->invalidate_bounding_box();
@@ -367,13 +366,13 @@ void Model::duplicate_objects_grid(size_t x, size_t y, coordf_t dist)
ModelObject* object = this->objects.front();
object->clear_instances();
- Sizef3 size = object->bounding_box().size();
+ Vec3d size = object->bounding_box().size();
for (size_t x_copy = 1; x_copy <= x; ++x_copy) {
for (size_t y_copy = 1; y_copy <= y; ++y_copy) {
ModelInstance* instance = object->add_instance();
- instance->offset.x = (size.x + dist) * (x_copy-1);
- instance->offset.y = (size.y + dist) * (y_copy-1);
+ instance->offset(0) = (size(0) + dist) * (x_copy-1);
+ instance->offset(1) = (size(1) + dist) * (y_copy-1);
}
}
}
@@ -387,7 +386,7 @@ bool Model::looks_like_multipart_object() const
if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
return false;
for (const ModelVolume *vol : obj->volumes) {
- double zmin_this = vol->mesh.bounding_box().min.z;
+ double zmin_this = vol->mesh.bounding_box().min(2);
if (zmin == std::numeric_limits<double>::max())
zmin = zmin_this;
else if (std::abs(zmin - zmin_this) > EPSILON)
@@ -398,17 +397,26 @@ bool Model::looks_like_multipart_object() const
return false;
}
-void Model::convert_multipart_object()
+void Model::convert_multipart_object(unsigned int max_extruders)
{
if (this->objects.empty())
return;
ModelObject* object = new ModelObject(this);
object->input_file = this->objects.front()->input_file;
-
+
+ reset_auto_extruder_id();
+
for (const ModelObject* o : this->objects)
for (const ModelVolume* v : o->volumes)
- object->add_volume(*v)->name = o->name;
+ {
+ ModelVolume* new_v = object->add_volume(*v);
+ if (new_v != nullptr)
+ {
+ new_v->name = o->name;
+ new_v->config.set_deserialize("extruder", get_auto_extruder_id_as_string(max_extruders));
+ }
+ }
for (const ModelInstance* i : this->objects.front()->instances)
object->add_instance(*i);
@@ -422,13 +430,13 @@ void Model::adjust_min_z()
if (objects.empty())
return;
- if (bounding_box().min.z < 0.0)
+ if (bounding_box().min(2) < 0.0)
{
for (ModelObject* obj : objects)
{
if (obj != nullptr)
{
- coordf_t obj_min_z = obj->bounding_box().min.z;
+ coordf_t obj_min_z = obj->bounding_box().min(2);
if (obj_min_z < 0.0)
obj->translate(0.0, 0.0, -obj_min_z);
}
@@ -436,30 +444,26 @@ void Model::adjust_min_z()
}
}
-bool Model::fits_print_volume(const DynamicPrintConfig* config) const
+unsigned int Model::get_auto_extruder_id(unsigned int max_extruders)
{
- if (config == nullptr)
- return false;
+ unsigned int id = s_auto_extruder_id;
- if (objects.empty())
- return true;
+ if (++s_auto_extruder_id > max_extruders)
+ reset_auto_extruder_id();
- const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(config->option("bed_shape"));
- if (opt == nullptr)
- return false;
+ return id;
+}
- BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
- BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config->opt_float("max_print_height")));
- return print_volume.contains(transformed_bounding_box());
+std::string Model::get_auto_extruder_id_as_string(unsigned int max_extruders)
+{
+ char str_extruder[64];
+ sprintf(str_extruder, "%ud", get_auto_extruder_id(max_extruders));
+ return str_extruder;
}
-bool Model::fits_print_volume(const FullPrintConfig &config) const
+void Model::reset_auto_extruder_id()
{
- if (objects.empty())
- return true;
- BoundingBox bed_box_2D = get_extents(Polygon::new_scale(config.bed_shape.values));
- BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), config.max_print_height));
- return print_volume.contains(transformed_bounding_box());
+ s_auto_extruder_id = 1;
}
ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) :
@@ -592,7 +596,7 @@ void ModelObject::clear_instances()
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
-const BoundingBoxf3& ModelObject::bounding_box()
+const BoundingBoxf3& ModelObject::bounding_box() const
{
if (! m_bounding_box_valid) {
BoundingBoxf3 raw_bbox;
@@ -608,54 +612,6 @@ const BoundingBoxf3& ModelObject::bounding_box()
return m_bounding_box;
}
-BoundingBoxf3 ModelObject::tight_bounding_box(bool include_modifiers) const
-{
- BoundingBoxf3 bb;
-
- for (const ModelVolume* vol : this->volumes)
- {
- if (include_modifiers || !vol->modifier)
- {
- for (const ModelInstance* inst : this->instances)
- {
- double c = cos(inst->rotation);
- double s = sin(inst->rotation);
-
- for (int f = 0; f < vol->mesh.stl.stats.number_of_facets; ++f)
- {
- const stl_facet& facet = vol->mesh.stl.facet_start[f];
-
- for (int i = 0; i < 3; ++i)
- {
- // original point
- const stl_vertex& v = facet.vertex[i];
- Pointf3 p((double)v.x, (double)v.y, (double)v.z);
-
- // scale
- p.x *= inst->scaling_factor;
- p.y *= inst->scaling_factor;
- p.z *= inst->scaling_factor;
-
- // rotate Z
- double x = p.x;
- double y = p.y;
- p.x = c * x - s * y;
- p.y = s * x + c * y;
-
- // translate
- p.x += inst->offset.x;
- p.y += inst->offset.y;
-
- bb.merge(p);
- }
- }
- }
- }
- }
-
- return bb;
-}
-
// A mesh containing all transformed instances of this object.
TriangleMesh ModelObject::mesh() const
{
@@ -713,25 +669,19 @@ void ModelObject::center_around_origin()
if (! v->modifier)
bb.merge(v->mesh.bounding_box());
- // first align to origin on XYZ
- Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z);
-
- // then center it on XY
- Sizef3 size = bb.size();
- vector.x -= size.x/2;
- vector.y -= size.y/2;
-
- this->translate(vector);
- this->origin_translation.translate(vector);
-
+ // Shift is the vector from the center of the bottom face of the bounding box to the origin
+ Vec3d shift = -bb.center();
+ shift(2) = -bb.min(2);
+
+ this->translate(shift);
+ this->origin_translation += shift;
+
if (!this->instances.empty()) {
for (ModelInstance *i : this->instances) {
// apply rotation and scaling to vector as well before translating instance,
// in order to leave final position unaltered
- Vectorf3 v = vector.negative();
- v.rotate(i->rotation);
- v.scale(i->scaling_factor);
- i->offset.translate(v.x, v.y);
+ Vec3d i_shift = i->world_matrix(true) * shift;
+ i->offset -= to_2d(i_shift);
}
this->invalidate_bounding_box();
}
@@ -740,47 +690,64 @@ void ModelObject::center_around_origin()
void ModelObject::translate(coordf_t x, coordf_t y, coordf_t z)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.translate(float(x), float(y), float(z));
- if (m_bounding_box_valid)
+ v->m_convex_hull.translate(float(x), float(y), float(z));
+ }
+
+ if (m_bounding_box_valid)
m_bounding_box.translate(x, y, z);
}
-void ModelObject::scale(const Pointf3 &versor)
+void ModelObject::scale(const Vec3d &versor)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.scale(versor);
+ v->m_convex_hull.scale(versor);
+ }
// reset origin translation since it doesn't make sense anymore
- this->origin_translation = Pointf3(0,0,0);
+ this->origin_translation = Vec3d::Zero();
this->invalidate_bounding_box();
}
-void ModelObject::rotate(float angle, const Axis &axis)
+void ModelObject::rotate(float angle, const Axis& axis)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.rotate(angle, axis);
- this->origin_translation = Pointf3(0,0,0);
+ v->m_convex_hull.rotate(angle, axis);
+ }
+
+ center_around_origin();
+
+ this->origin_translation = Vec3d::Zero();
this->invalidate_bounding_box();
}
-void ModelObject::transform(const float* matrix3x4)
+void ModelObject::rotate(float angle, const Vec3d& axis)
{
- if (matrix3x4 == nullptr)
- return;
-
- for (ModelVolume* v : volumes)
+ for (ModelVolume *v : this->volumes)
{
- v->mesh.transform(matrix3x4);
+ v->mesh.rotate(angle, axis);
+ v->m_convex_hull.rotate(angle, axis);
}
- origin_translation = Pointf3(0.0, 0.0, 0.0);
- invalidate_bounding_box();
+ center_around_origin();
+
+ this->origin_translation = Vec3d::Zero();
+ this->invalidate_bounding_box();
}
void ModelObject::mirror(const Axis &axis)
{
for (ModelVolume *v : this->volumes)
+ {
v->mesh.mirror(axis);
- this->origin_translation = Pointf3(0,0,0);
+ v->m_convex_hull.mirror(axis);
+ }
+
+ this->origin_translation = Vec3d::Zero();
this->invalidate_bounding_box();
}
@@ -826,33 +793,8 @@ void ModelObject::cut(coordf_t z, Model* model) const
lower->add_volume(*volume);
} else {
TriangleMesh upper_mesh, lower_mesh;
- // TODO: shouldn't we use object bounding box instead of per-volume bb?
- coordf_t cut_z = z + volume->mesh.bounding_box().min.z;
- if (false) {
-// if (volume->mesh.has_multiple_patches()) {
- // Cutting algorithm does not work on intersecting meshes.
- // As we are not sure whether the meshes don't intersect,
- // we rather split the mesh into multiple non-intersecting pieces.
- TriangleMeshPtrs meshptrs = volume->mesh.split();
- for (TriangleMeshPtrs::iterator mesh = meshptrs.begin(); mesh != meshptrs.end(); ++mesh) {
- printf("Cutting mesh patch %d of %d\n", int(mesh - meshptrs.begin()), int(meshptrs.size()));
- (*mesh)->repair();
- TriangleMeshSlicer tms(*mesh);
- if (mesh == meshptrs.begin()) {
- tms.cut(cut_z, &upper_mesh, &lower_mesh);
- } else {
- TriangleMesh upper_mesh_this, lower_mesh_this;
- tms.cut(cut_z, &upper_mesh_this, &lower_mesh_this);
- upper_mesh.merge(upper_mesh_this);
- lower_mesh.merge(lower_mesh_this);
- }
- delete *mesh;
- }
- } else {
- printf("Cutting mesh patch\n");
- TriangleMeshSlicer tms(&volume->mesh);
- tms.cut(cut_z, &upper_mesh, &lower_mesh);
- }
+ TriangleMeshSlicer tms(&volume->mesh);
+ tms.cut(z, &upper_mesh, &lower_mesh);
upper_mesh.repair();
lower_mesh.repair();
@@ -907,6 +849,27 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
return;
}
+void ModelObject::check_instances_print_volume_state(const BoundingBoxf3& print_volume)
+{
+ for (const ModelVolume* vol : this->volumes)
+ {
+ if (!vol->modifier)
+ {
+ for (ModelInstance* inst : this->instances)
+ {
+ BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(inst->world_matrix());
+
+ if (print_volume.contains(bb))
+ inst->print_volume_state = ModelInstance::PVS_Inside;
+ else if (print_volume.intersects(bb))
+ inst->print_volume_state = ModelInstance::PVS_Partly_Outside;
+ else
+ inst->print_volume_state = ModelInstance::PVS_Fully_Outside;
+ }
+ }
+ }
+}
+
void ModelObject::print_info() const
{
using namespace std;
@@ -916,16 +879,16 @@ void ModelObject::print_info() const
TriangleMesh mesh = this->raw_mesh();
mesh.check_topology();
BoundingBoxf3 bb = mesh.bounding_box();
- Sizef3 size = bb.size();
- cout << "size_x = " << size.x << endl;
- cout << "size_y = " << size.y << endl;
- cout << "size_z = " << size.z << endl;
- cout << "min_x = " << bb.min.x << endl;
- cout << "min_y = " << bb.min.y << endl;
- cout << "min_z = " << bb.min.z << endl;
- cout << "max_x = " << bb.max.x << endl;
- cout << "max_y = " << bb.max.y << endl;
- cout << "max_z = " << bb.max.z << endl;
+ Vec3d size = bb.size();
+ cout << "size_x = " << size(0) << endl;
+ cout << "size_y = " << size(1) << endl;
+ cout << "size_z = " << size(2) << endl;
+ cout << "min_x = " << bb.min(0) << endl;
+ cout << "min_y = " << bb.min(1) << endl;
+ cout << "min_z = " << bb.min(2) << endl;
+ cout << "max_x = " << bb.max(0) << endl;
+ cout << "max_y = " << bb.max(1) << endl;
+ cout << "max_z = " << bb.max(2) << endl;
cout << "number_of_facets = " << mesh.stl.stats.number_of_facets << endl;
cout << "manifold = " << (mesh.is_manifold() ? "yes" : "no") << endl;
@@ -977,10 +940,20 @@ ModelMaterial* ModelVolume::assign_unique_material()
return model->add_material(this->_material_id);
}
+void ModelVolume::calculate_convex_hull()
+{
+ m_convex_hull = mesh.convex_hull_3d();
+}
+
+const TriangleMesh& ModelVolume::get_convex_hull() const
+{
+ return m_convex_hull;
+}
+
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
-size_t ModelVolume::split()
+size_t ModelVolume::split(unsigned int max_extruders)
{
TriangleMeshPtrs meshptrs = this->mesh.split();
if (meshptrs.size() <= 1) {
@@ -991,6 +964,9 @@ size_t ModelVolume::split()
size_t idx = 0;
size_t ivolume = std::find(this->object->volumes.begin(), this->object->volumes.end(), this) - this->object->volumes.begin();
std::string name = this->name;
+
+ Model::reset_auto_extruder_id();
+
for (TriangleMesh *mesh : meshptrs) {
mesh->repair();
if (idx == 0)
@@ -1000,6 +976,7 @@ size_t ModelVolume::split()
char str_idx[64];
sprintf(str_idx, "_%d", idx + 1);
this->object->volumes[ivolume]->name = name + str_idx;
+ this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders));
delete mesh;
++ idx;
}
@@ -1009,45 +986,26 @@ size_t ModelVolume::split()
void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const
{
- mesh->rotate_z(this->rotation); // rotate around mesh origin
- mesh->scale(this->scaling_factor); // scale around mesh origin
- if (!dont_translate)
- mesh->translate(this->offset.x, this->offset.y, 0);
+ mesh->transform(world_matrix(dont_translate).cast<float>());
}
BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const
{
// Rotate around mesh origin.
- double c = cos(this->rotation);
- double s = sin(this->rotation);
- BoundingBoxf3 bbox;
- for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) {
- const stl_facet &facet = mesh->stl.facet_start[i];
- for (int j = 0; j < 3; ++ j) {
- stl_vertex v = facet.vertex[j];
- double xold = v.x;
- double yold = v.y;
- v.x = float(c * xold - s * yold);
- v.y = float(s * xold + c * yold);
- bbox.merge(Pointf3(v.x, v.y, v.z));
- }
- }
- if (! empty(bbox)) {
+ TriangleMesh copy(*mesh);
+ copy.transform(world_matrix(true, false, true).cast<float>());
+ BoundingBoxf3 bbox = copy.bounding_box();
+
+ if (!empty(bbox)) {
// Scale the bounding box uniformly.
if (std::abs(this->scaling_factor - 1.) > EPSILON) {
- bbox.min.x *= float(this->scaling_factor);
- bbox.min.y *= float(this->scaling_factor);
- bbox.min.z *= float(this->scaling_factor);
- bbox.max.x *= float(this->scaling_factor);
- bbox.max.y *= float(this->scaling_factor);
- bbox.max.z *= float(this->scaling_factor);
+ bbox.min *= this->scaling_factor;
+ bbox.max *= this->scaling_factor;
}
// Translate the bounding box.
if (! dont_translate) {
- bbox.min.x += float(this->offset.x);
- bbox.min.y += float(this->offset.y);
- bbox.max.x += float(this->offset.x);
- bbox.max.y += float(this->offset.y);
+ Eigen::Map<Vec2d>(bbox.min.data()) += this->offset;
+ Eigen::Map<Vec2d>(bbox.max.data()) += this->offset;
}
}
return bbox;
@@ -1055,32 +1013,12 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mes
BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const
{
- // rotate around mesh origin
- double c = cos(this->rotation);
- double s = sin(this->rotation);
- Pointf3 pts[4] = {
- bbox.min,
- bbox.max,
- Pointf3(bbox.min.x, bbox.max.y, bbox.min.z),
- Pointf3(bbox.max.x, bbox.min.y, bbox.max.z)
- };
- BoundingBoxf3 out;
- for (int i = 0; i < 4; ++ i) {
- Pointf3 &v = pts[i];
- double xold = v.x;
- double yold = v.y;
- v.x = float(c * xold - s * yold);
- v.y = float(s * xold + c * yold);
- v.x *= this->scaling_factor;
- v.y *= this->scaling_factor;
- v.z *= this->scaling_factor;
- if (! dont_translate) {
- v.x += this->offset.x;
- v.y += this->offset.y;
- }
- out.merge(v);
- }
- return out;
+ return bbox.transformed(world_matrix(dont_translate));
+}
+
+Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const
+{
+ return world_matrix(dont_translate) * v;
}
void ModelInstance::transform_polygon(Polygon* polygon) const
@@ -1089,4 +1027,20 @@ void ModelInstance::transform_polygon(Polygon* polygon) const
polygon->scale(this->scaling_factor); // scale around polygon origin
}
+Transform3d ModelInstance::world_matrix(bool dont_translate, bool dont_rotate, bool dont_scale) const
+{
+ Transform3d m = Transform3d::Identity();
+
+ if (!dont_translate)
+ m.translate(Vec3d(offset(0), offset(1), 0.0));
+
+ if (!dont_rotate)
+ m.rotate(Eigen::AngleAxisd(rotation, Vec3d::UnitZ()));
+
+ if (!dont_scale)
+ m.scale(scaling_factor);
+
+ return m;
+}
+
}
diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp
index bf7f60e36..8a8af481c 100644
--- a/xs/src/libslic3r/Model.hpp
+++ b/xs/src/libslic3r/Model.hpp
@@ -84,7 +84,7 @@ public:
center_around_origin() method. Callers might want to apply the same translation
to new volumes before adding them to this object in order to preserve alignment
when user expects that. */
- Pointf3 origin_translation;
+ Vec3d origin_translation;
Model* get_model() const { return m_model; };
@@ -103,11 +103,8 @@ public:
// Returns the bounding box of the transformed instances.
// This bounding box is approximate and not snug.
// This bounding box is being cached.
- const BoundingBoxf3& bounding_box();
+ const BoundingBoxf3& bounding_box() const;
void invalidate_bounding_box() { m_bounding_box_valid = false; }
- // Returns a snug bounding box of the transformed instances.
- // This bounding box is not being cached.
- BoundingBoxf3 tight_bounding_box(bool include_modifiers) const;
// A mesh containing all transformed instances of this object.
TriangleMesh mesh() const;
@@ -120,11 +117,11 @@ public:
// A snug bounding box around the transformed non-modifier object volumes.
BoundingBoxf3 instance_bounding_box(size_t instance_idx, bool dont_translate = false) const;
void center_around_origin();
- void translate(const Vectorf3 &vector) { this->translate(vector.x, vector.y, vector.z); }
+ void translate(const Vec3d &vector) { this->translate(vector(0), vector(1), vector(2)); }
void translate(coordf_t x, coordf_t y, coordf_t z);
- void scale(const Pointf3 &versor);
+ void scale(const Vec3d &versor);
void rotate(float angle, const Axis &axis);
- void transform(const float* matrix3x4);
+ void rotate(float angle, const Vec3d& axis);
void mirror(const Axis &axis);
size_t materials_count() const;
size_t facets_count() const;
@@ -132,11 +129,13 @@ public:
void cut(coordf_t z, Model* model) const;
void split(ModelObjectPtrs* new_objects);
+ void check_instances_print_volume_state(const BoundingBoxf3& print_volume);
+
// Print object statistics to console.
void print_info() const;
private:
- ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), m_bounding_box_valid(false) {}
+ ModelObject(Model *model) : layer_height_profile_valid(false), m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false) {}
ModelObject(Model *model, const ModelObject &other, bool copy_volumes = true);
ModelObject& operator= (ModelObject other);
void swap(ModelObject &other);
@@ -145,8 +144,9 @@ private:
// Parent object, owning this ModelObject.
Model *m_model;
// Bounding box, cached.
- BoundingBoxf3 m_bounding_box;
- bool m_bounding_box_valid;
+
+ mutable BoundingBoxf3 m_bounding_box;
+ mutable bool m_bounding_box_valid;
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
@@ -154,6 +154,10 @@ private:
class ModelVolume
{
friend class ModelObject;
+
+ // The convex hull of this model's mesh.
+ TriangleMesh m_convex_hull;
+
public:
std::string name;
// The triangular model.
@@ -173,36 +177,61 @@ public:
// Split this volume, append the result to the object owning this volume.
// Return the number of volumes created from this one.
// This is useful to assign different materials to different volumes of an object.
- size_t split();
-
+ size_t split(unsigned int max_extruders);
+
ModelMaterial* assign_unique_material();
+ void calculate_convex_hull();
+ const TriangleMesh& get_convex_hull() const;
+
private:
// Parent object owning this ModelVolume.
ModelObject* object;
t_model_material_id _material_id;
- ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object) {}
- ModelVolume(ModelObject *object, TriangleMesh &&mesh) : mesh(std::move(mesh)), modifier(false), object(object) {}
- ModelVolume(ModelObject *object, const ModelVolume &other) :
- name(other.name), mesh(other.mesh), config(other.config), modifier(other.modifier), object(object)
- { this->material_id(other.material_id()); }
- ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
+ ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), modifier(false), object(object)
+ {
+ if (mesh.stl.stats.number_of_facets > 1)
+ calculate_convex_hull();
+ }
+ ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), modifier(false), object(object) {}
+ ModelVolume(ModelObject *object, const ModelVolume &other) :
+ name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), modifier(other.modifier), object(object)
+ {
+ this->material_id(other.material_id());
+ }
+ ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) :
name(other.name), mesh(std::move(mesh)), config(other.config), modifier(other.modifier), object(object)
- { this->material_id(other.material_id()); }
+ {
+ this->material_id(other.material_id());
+ if (mesh.stl.stats.number_of_facets > 1)
+ calculate_convex_hull();
+ }
};
// A single instance of a ModelObject.
// Knows the affine transformation of an object.
class ModelInstance
{
- friend class ModelObject;
public:
+ enum EPrintVolumeState : unsigned char
+ {
+ PVS_Inside,
+ PVS_Partly_Outside,
+ PVS_Fully_Outside,
+ Num_BedStates
+ };
+
+ friend class ModelObject;
+
double rotation; // Rotation around the Z axis, in radians around mesh center point
double scaling_factor;
- Pointf offset; // in unscaled coordinates
+ Vec2d offset; // in unscaled coordinates
- ModelObject* get_object() const { return this->object; };
+ // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state())
+ EPrintVolumeState print_volume_state;
+
+ ModelObject* get_object() const { return this->object; }
// To be called on an external mesh
void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const;
@@ -210,16 +239,22 @@ public:
BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const;
// Transform an external bounding box.
BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const;
+ // Transform an external vector.
+ Vec3d transform_vector(const Vec3d& v, bool dont_translate = false) const;
// To be called on an external polygon. It does not translate the polygon, only rotates and scales.
void transform_polygon(Polygon* polygon) const;
-
+
+ Transform3d world_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false) const;
+
+ bool is_printable() const { return print_volume_state == PVS_Inside; }
+
private:
// Parent object, owning this instance.
ModelObject* object;
- ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), object(object) {}
+ ModelInstance(ModelObject *object) : rotation(0), scaling_factor(1), offset(Vec2d::Zero()), object(object), print_volume_state(PVS_Inside) {}
ModelInstance(ModelObject *object, const ModelInstance &other) :
- rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object) {}
+ rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset), object(object), print_volume_state(PVS_Inside) {}
};
@@ -230,6 +265,8 @@ private:
// all objects may share mutliple materials.
class Model
{
+ static unsigned int s_auto_extruder_id;
+
public:
// Materials are owned by a model and referenced by objects through t_model_material_id.
// Single material may be shared by multiple models.
@@ -266,9 +303,7 @@ public:
bool add_default_instances();
// Returns approximate axis aligned bounding box of this model
BoundingBoxf3 bounding_box() const;
- // Returns tight axis aligned bounding box of this model
- BoundingBoxf3 transformed_bounding_box() const;
- void center_instances_around_point(const Pointf &point);
+ void center_instances_around_point(const Vec2d &point);
void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); }
TriangleMesh mesh() const;
bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL);
@@ -278,16 +313,16 @@ public:
void duplicate_objects_grid(size_t x, size_t y, coordf_t dist);
bool looks_like_multipart_object() const;
- void convert_multipart_object();
+ void convert_multipart_object(unsigned int max_extruders);
// Ensures that the min z of the model is not negative
void adjust_min_z();
- // Returs true if this model is contained into the print volume defined inside the given config
- bool fits_print_volume(const DynamicPrintConfig* config) const;
- bool fits_print_volume(const FullPrintConfig &config) const;
-
void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); }
+
+ static unsigned int get_auto_extruder_id(unsigned int max_extruders);
+ static std::string get_auto_extruder_id_as_string(unsigned int max_extruders);
+ static void reset_auto_extruder_id();
};
}
diff --git a/xs/src/libslic3r/ModelArrange.hpp b/xs/src/libslic3r/ModelArrange.hpp
new file mode 100644
index 000000000..36bd8ed97
--- /dev/null
+++ b/xs/src/libslic3r/ModelArrange.hpp
@@ -0,0 +1,788 @@
+#ifndef MODELARRANGE_HPP
+#define MODELARRANGE_HPP
+
+#include "Model.hpp"
+#include "SVG.hpp"
+#include <libnest2d.h>
+
+#include <numeric>
+#include <ClipperUtils.hpp>
+
+#include <boost/geometry/index/rtree.hpp>
+
+namespace Slic3r {
+namespace arr {
+
+using namespace libnest2d;
+
+std::string toString(const Model& model, bool holes = true) {
+ std::stringstream ss;
+
+ ss << "{\n";
+
+ for(auto objptr : model.objects) {
+ if(!objptr) continue;
+
+ auto rmesh = objptr->raw_mesh();
+
+ for(auto objinst : objptr->instances) {
+ if(!objinst) continue;
+
+ Slic3r::TriangleMesh tmpmesh = rmesh;
+ tmpmesh.scale(objinst->scaling_factor);
+ objinst->transform_mesh(&tmpmesh);
+ ExPolygons expolys = tmpmesh.horizontal_projection();
+ for(auto& expoly_complex : expolys) {
+
+ auto tmp = expoly_complex.simplify(1.0/SCALING_FACTOR);
+ if(tmp.empty()) continue;
+ auto expoly = tmp.front();
+ expoly.contour.make_clockwise();
+ for(auto& h : expoly.holes) h.make_counter_clockwise();
+
+ ss << "\t{\n";
+ ss << "\t\t{\n";
+
+ for(auto v : expoly.contour.points) ss << "\t\t\t{"
+ << v(0) << ", "
+ << v(1) << "},\n";
+ {
+ auto v = expoly.contour.points.front();
+ ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n";
+ }
+ ss << "\t\t},\n";
+
+ // Holes:
+ ss << "\t\t{\n";
+ if(holes) for(auto h : expoly.holes) {
+ ss << "\t\t\t{\n";
+ for(auto v : h.points) ss << "\t\t\t\t{"
+ << v(0) << ", "
+ << v(1) << "},\n";
+ {
+ auto v = h.points.front();
+ ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n";
+ }
+ ss << "\t\t\t},\n";
+ }
+ ss << "\t\t},\n";
+
+ ss << "\t},\n";
+ }
+ }
+ }
+
+ ss << "}\n";
+
+ return ss.str();
+}
+
+void toSVG(SVG& svg, const Model& model) {
+ for(auto objptr : model.objects) {
+ if(!objptr) continue;
+
+ auto rmesh = objptr->raw_mesh();
+
+ for(auto objinst : objptr->instances) {
+ if(!objinst) continue;
+
+ Slic3r::TriangleMesh tmpmesh = rmesh;
+ tmpmesh.scale(objinst->scaling_factor);
+ objinst->transform_mesh(&tmpmesh);
+ ExPolygons expolys = tmpmesh.horizontal_projection();
+ svg.draw(expolys);
+ }
+ }
+}
+
+namespace bgi = boost::geometry::index;
+
+using SpatElement = std::pair<Box, unsigned>;
+using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
+using ItemGroup = std::vector<std::reference_wrapper<Item>>;
+template<class TBin>
+using TPacker = typename placers::_NofitPolyPlacer<PolygonImpl, TBin>;
+
+const double BIG_ITEM_TRESHOLD = 0.02;
+
+Box boundingBox(const Box& pilebb, const Box& ibb ) {
+ auto& pminc = pilebb.minCorner();
+ auto& pmaxc = pilebb.maxCorner();
+ auto& iminc = ibb.minCorner();
+ auto& imaxc = ibb.maxCorner();
+ PointImpl minc, maxc;
+
+ setX(minc, std::min(getX(pminc), getX(iminc)));
+ setY(minc, std::min(getY(pminc), getY(iminc)));
+
+ setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
+ setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
+ return Box(minc, maxc);
+}
+
+std::tuple<double /*score*/, Box /*farthest point from bin center*/>
+objfunc(const PointImpl& bincenter,
+ const shapelike::Shapes<PolygonImpl>& merged_pile,
+ const Box& pilebb,
+ const ItemGroup& items,
+ const Item &item,
+ double bin_area,
+ double norm, // A norming factor for physical dimensions
+ // a spatial index to quickly get neighbors of the candidate item
+ const SpatIndex& spatindex,
+ const ItemGroup& remaining
+ )
+{
+ using Coord = TCoord<PointImpl>;
+
+ static const double ROUNDNESS_RATIO = 0.5;
+ static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO;
+
+ // We will treat big items (compared to the print bed) differently
+ auto isBig = [bin_area](double a) {
+ return a/bin_area > BIG_ITEM_TRESHOLD ;
+ };
+
+ // Candidate item bounding box
+ auto ibb = sl::boundingBox(item.transformedShape());
+
+ // Calculate the full bounding box of the pile with the candidate item
+ auto fullbb = boundingBox(pilebb, ibb);
+
+ // The bounding box of the big items (they will accumulate in the center
+ // of the pile
+ Box bigbb;
+ if(spatindex.empty()) bigbb = fullbb;
+ else {
+ auto boostbb = spatindex.bounds();
+ boost::geometry::convert(boostbb, bigbb);
+ }
+
+ // Will hold the resulting score
+ double score = 0;
+
+ if(isBig(item.area())) {
+ // This branch is for the bigger items..
+
+ auto minc = ibb.minCorner(); // bottom left corner
+ auto maxc = ibb.maxCorner(); // top right corner
+
+ // top left and bottom right corners
+ auto top_left = PointImpl{getX(minc), getY(maxc)};
+ auto bottom_right = PointImpl{getX(maxc), getY(minc)};
+
+ // Now the distance of the gravity center will be calculated to the
+ // five anchor points and the smallest will be chosen.
+ std::array<double, 5> dists;
+ auto cc = fullbb.center(); // The gravity center
+ dists[0] = pl::distance(minc, cc);
+ dists[1] = pl::distance(maxc, cc);
+ dists[2] = pl::distance(ibb.center(), cc);
+ dists[3] = pl::distance(top_left, cc);
+ dists[4] = pl::distance(bottom_right, cc);
+
+ // The smalles distance from the arranged pile center:
+ auto dist = *(std::min_element(dists.begin(), dists.end())) / norm;
+
+ // Density is the pack density: how big is the arranged pile
+ double density = 0;
+
+ if(remaining.empty()) {
+
+ auto mp = merged_pile;
+ mp.emplace_back(item.transformedShape());
+ auto chull = sl::convexHull(mp);
+
+ placers::EdgeCache<PolygonImpl> ec(chull);
+
+ double circ = ec.circumference() / norm;
+ double bcirc = 2.0*(fullbb.width() + fullbb.height()) / norm;
+ score = 0.5*circ + 0.5*bcirc;
+
+ } else {
+ // Prepare a variable for the alignment score.
+ // This will indicate: how well is the candidate item aligned with
+ // its neighbors. We will check the alignment with all neighbors and
+ // return the score for the best alignment. So it is enough for the
+ // candidate to be aligned with only one item.
+ auto alignment_score = 1.0;
+
+ density = (fullbb.width()*fullbb.height()) / (norm*norm);
+ auto querybb = item.boundingBox();
+
+ // Query the spatial index for the neighbors
+ std::vector<SpatElement> result;
+ result.reserve(spatindex.size());
+ spatindex.query(bgi::intersects(querybb),
+ std::back_inserter(result));
+
+ for(auto& e : result) { // now get the score for the best alignment
+ auto idx = e.second;
+ Item& p = items[idx];
+ auto parea = p.area();
+ if(std::abs(1.0 - parea/item.area()) < 1e-6) {
+ auto bb = boundingBox(p.boundingBox(), ibb);
+ auto bbarea = bb.area();
+ auto ascore = 1.0 - (item.area() + parea)/bbarea;
+
+ if(ascore < alignment_score) alignment_score = ascore;
+ }
+ }
+
+ // The final mix of the score is the balance between the distance
+ // from the full pile center, the pack density and the
+ // alignment with the neighbors
+ if(result.empty())
+ score = 0.5 * dist + 0.5 * density;
+ else
+ score = 0.45 * dist + 0.45 * density + 0.1 * alignment_score;
+ }
+ } else if( !isBig(item.area()) && spatindex.empty()) {
+ auto bindist = pl::distance(ibb.center(), bincenter) / norm;
+ // Bindist is surprisingly enough...
+ score = bindist;
+ } else {
+ // Here there are the small items that should be placed around the
+ // already processed bigger items.
+ // No need to play around with the anchor points, the center will be
+ // just fine for small items
+ score = pl::distance(ibb.center(), bigbb.center()) / norm;
+ }
+
+ return std::make_tuple(score, fullbb);
+}
+
+template<class PConf>
+void fillConfig(PConf& pcfg) {
+
+ // Align the arranged pile into the center of the bin
+ pcfg.alignment = PConf::Alignment::CENTER;
+
+ // Start placing the items from the center of the print bed
+ pcfg.starting_point = PConf::Alignment::CENTER;
+
+ // TODO cannot use rotations until multiple objects of same geometry can
+ // handle different rotations
+ // arranger.useMinimumBoundigBoxRotation();
+ pcfg.rotations = { 0.0 };
+
+ // The accuracy of optimization.
+ // Goes from 0.0 to 1.0 and scales performance as well
+ pcfg.accuracy = 0.65f;
+
+ pcfg.parallel = true;
+}
+
+template<class TBin>
+class AutoArranger {};
+
+template<class TBin>
+class _ArrBase {
+protected:
+
+ using Placer = TPacker<TBin>;
+ using Selector = FirstFitSelection;
+ using Packer = Nester<Placer, Selector>;
+ using PConfig = typename Packer::PlacementConfig;
+ using Distance = TCoord<PointImpl>;
+ using Pile = sl::Shapes<PolygonImpl>;
+
+ Packer pck_;
+ PConfig pconf_; // Placement configuration
+ double bin_area_;
+ SpatIndex rtree_;
+ double norm_;
+ Pile merged_pile_;
+ Box pilebb_;
+ ItemGroup remaining_;
+ ItemGroup items_;
+public:
+
+ _ArrBase(const TBin& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ pck_(bin, dist), bin_area_(sl::area(bin)),
+ norm_(std::sqrt(sl::area(bin)))
+ {
+ fillConfig(pconf_);
+
+ pconf_.before_packing =
+ [this](const Pile& merged_pile, // merged pile
+ const ItemGroup& items, // packed items
+ const ItemGroup& remaining) // future items to be packed
+ {
+ items_ = items;
+ merged_pile_ = merged_pile;
+ remaining_ = remaining;
+
+ pilebb_ = sl::boundingBox(merged_pile);
+
+ rtree_.clear();
+
+ // We will treat big items (compared to the print bed) differently
+ auto isBig = [this](double a) {
+ return a/bin_area_ > BIG_ITEM_TRESHOLD ;
+ };
+
+ for(unsigned idx = 0; idx < items.size(); ++idx) {
+ Item& itm = items[idx];
+ if(isBig(itm.area())) rtree_.insert({itm.boundingBox(), idx});
+ }
+ };
+
+ pck_.progressIndicator(progressind);
+ }
+
+ template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
+ rtree_.clear();
+ return pck_.executeIndexed(std::forward<Args>(args)...);
+ }
+};
+
+template<>
+class AutoArranger<Box>: public _ArrBase<Box> {
+public:
+
+ AutoArranger(const Box& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ _ArrBase<Box>(bin, dist, progressind)
+ {
+
+ pconf_.object_function = [this, bin] (const Item &item) {
+
+ auto result = objfunc(bin.center(),
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ bin_area_,
+ norm_,
+ rtree_,
+ remaining_);
+
+ double score = std::get<0>(result);
+ auto& fullbb = std::get<1>(result);
+
+ double miss = Placer::overfit(fullbb, bin);
+ miss = miss > 0? miss : 0;
+ score += miss*miss;
+
+ return score;
+ };
+
+ pck_.configure(pconf_);
+ }
+};
+
+using lnCircle = libnest2d::_Circle<libnest2d::PointImpl>;
+
+template<>
+class AutoArranger<lnCircle>: public _ArrBase<lnCircle> {
+public:
+
+ AutoArranger(const lnCircle& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ _ArrBase<lnCircle>(bin, dist, progressind) {
+
+ pconf_.object_function = [this, &bin] (const Item &item) {
+
+ auto result = objfunc(bin.center(),
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ bin_area_,
+ norm_,
+ rtree_,
+ remaining_);
+
+ double score = std::get<0>(result);
+
+ auto isBig = [this](const Item& itm) {
+ return itm.area()/bin_area_ > BIG_ITEM_TRESHOLD ;
+ };
+
+ if(isBig(item)) {
+ auto mp = merged_pile_;
+ mp.push_back(item.transformedShape());
+ auto chull = sl::convexHull(mp);
+ double miss = Placer::overfit(chull, bin);
+ if(miss < 0) miss = 0;
+ score += miss*miss;
+ }
+
+ return score;
+ };
+
+ pck_.configure(pconf_);
+ }
+};
+
+template<>
+class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
+public:
+ AutoArranger(const PolygonImpl& bin, Distance dist,
+ std::function<void(unsigned)> progressind):
+ _ArrBase<PolygonImpl>(bin, dist, progressind)
+ {
+ pconf_.object_function = [this, &bin] (const Item &item) {
+
+ auto binbb = sl::boundingBox(bin);
+ auto result = objfunc(binbb.center(),
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ bin_area_,
+ norm_,
+ rtree_,
+ remaining_);
+ double score = std::get<0>(result);
+
+ return score;
+ };
+
+ pck_.configure(pconf_);
+ }
+};
+
+template<> // Specialization with no bin
+class AutoArranger<bool>: public _ArrBase<Box> {
+public:
+
+ AutoArranger(Distance dist, std::function<void(unsigned)> progressind):
+ _ArrBase<Box>(Box(0, 0), dist, progressind)
+ {
+ this->pconf_.object_function = [this] (const Item &item) {
+
+ auto result = objfunc({0, 0},
+ merged_pile_,
+ pilebb_,
+ items_,
+ item,
+ 0,
+ norm_,
+ rtree_,
+ remaining_);
+ return std::get<0>(result);
+ };
+
+ this->pck_.configure(pconf_);
+ }
+};
+
+// A container which stores a pointer to the 3D object and its projected
+// 2D shape from top view.
+using ShapeData2D =
+ std::vector<std::pair<Slic3r::ModelInstance*, Item>>;
+
+ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
+ ShapeData2D ret;
+
+ auto s = std::accumulate(model.objects.begin(), model.objects.end(), 0,
+ [](size_t s, ModelObject* o){
+ return s + o->instances.size();
+ });
+
+ ret.reserve(s);
+
+ for(auto objptr : model.objects) {
+ if(objptr) {
+
+ auto rmesh = objptr->raw_mesh();
+
+ for(auto objinst : objptr->instances) {
+ if(objinst) {
+ Slic3r::TriangleMesh tmpmesh = rmesh;
+ ClipperLib::PolygonImpl pn;
+
+ tmpmesh.scale(objinst->scaling_factor);
+
+ // TODO export the exact 2D projection
+ auto p = tmpmesh.convex_hull();
+
+ p.make_clockwise();
+ p.append(p.first_point());
+ pn.Contour = Slic3rMultiPoint_to_ClipperPath( p );
+
+ // Efficient conversion to item.
+ Item item(std::move(pn));
+
+ // Invalid geometries would throw exceptions when arranging
+ if(item.vertexCount() > 3) {
+ item.rotation(objinst->rotation);
+ item.translation( {
+ ClipperLib::cInt(objinst->offset(0)/SCALING_FACTOR),
+ ClipperLib::cInt(objinst->offset(1)/SCALING_FACTOR)
+ });
+ ret.emplace_back(objinst, item);
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+class Circle {
+ Point center_;
+ double radius_;
+public:
+
+ inline Circle(): center_(0, 0), radius_(std::nan("")) {}
+ inline Circle(const Point& c, double r): center_(c), radius_(r) {}
+
+ inline double radius() const { return radius_; }
+ inline const Point& center() const { return center_; }
+ inline operator bool() { return !std::isnan(radius_); }
+ inline operator lnCircle() {
+ return lnCircle({center_(0), center_(1)}, radius_);
+ }
+};
+
+enum class BedShapeType {
+ BOX,
+ CIRCLE,
+ IRREGULAR,
+ WHO_KNOWS
+};
+
+struct BedShapeHint {
+ BedShapeType type;
+ /*union*/ struct { // I know but who cares...
+ Circle circ;
+ BoundingBox box;
+ Polyline polygon;
+ } shape;
+};
+
+BedShapeHint bedShape(const Polyline& bed) {
+ BedShapeHint ret;
+
+ auto x = [](const Point& p) { return p(0); };
+ auto y = [](const Point& p) { return p(1); };
+
+ auto width = [x](const BoundingBox& box) {
+ return x(box.max) - x(box.min);
+ };
+
+ auto height = [y](const BoundingBox& box) {
+ return y(box.max) - y(box.min);
+ };
+
+ auto area = [&width, &height](const BoundingBox& box) {
+ double w = width(box);
+ double h = height(box);
+ return w*h;
+ };
+
+ auto poly_area = [](Polyline p) {
+ Polygon pp; pp.points.reserve(p.points.size() + 1);
+ pp.points = std::move(p.points);
+ pp.points.emplace_back(pp.points.front());
+ return std::abs(pp.area());
+ };
+
+ auto distance_to = [x, y](const Point& p1, const Point& p2) {
+ double dx = x(p2) - x(p1);
+ double dy = y(p2) - y(p1);
+ return std::sqrt(dx*dx + dy*dy);
+ };
+
+ auto bb = bed.bounding_box();
+
+ auto isCircle = [bb, distance_to](const Polyline& polygon) {
+ auto center = bb.center();
+ std::vector<double> vertex_distances;
+ double avg_dist = 0;
+ for (auto pt: polygon.points)
+ {
+ double distance = distance_to(center, pt);
+ vertex_distances.push_back(distance);
+ avg_dist += distance;
+ }
+
+ avg_dist /= vertex_distances.size();
+
+ Circle ret(center, avg_dist);
+ for (auto el: vertex_distances)
+ {
+ if (abs(el - avg_dist) > 10 * SCALED_EPSILON)
+ ret = Circle();
+ break;
+ }
+
+ return ret;
+ };
+
+ auto parea = poly_area(bed);
+
+ if( (1.0 - parea/area(bb)) < 1e-3 ) {
+ ret.type = BedShapeType::BOX;
+ ret.shape.box = bb;
+ }
+ else if(auto c = isCircle(bed)) {
+ ret.type = BedShapeType::CIRCLE;
+ ret.shape.circ = c;
+ } else {
+ ret.type = BedShapeType::IRREGULAR;
+ ret.shape.polygon = bed;
+ }
+
+ // Determine the bed shape by hand
+ return ret;
+}
+
+void applyResult(
+ IndexedPackGroup::value_type& group,
+ Coord batch_offset,
+ ShapeData2D& shapemap)
+{
+ for(auto& r : group) {
+ auto idx = r.first; // get the original item index
+ Item& item = r.second; // get the item itself
+
+ // Get the model instance from the shapemap using the index
+ ModelInstance *inst_ptr = shapemap[idx].first;
+
+ // Get the tranformation data from the item object and scale it
+ // appropriately
+ auto off = item.translation();
+ Radians rot = item.rotation();
+ Vec2d foff(off.X*SCALING_FACTOR + batch_offset, off.Y*SCALING_FACTOR);
+
+ // write the tranformation data into the model instance
+ inst_ptr->rotation = rot;
+ inst_ptr->offset = foff;
+ }
+}
+
+
+/**
+ * \brief Arranges the model objects on the screen.
+ *
+ * The arrangement considers multiple bins (aka. print beds) for placing all
+ * the items provided in the model argument. If the items don't fit on one
+ * print bed, the remaining will be placed onto newly created print beds.
+ * The first_bin_only parameter, if set to true, disables this behaviour and
+ * makes sure that only one print bed is filled and the remaining items will be
+ * untouched. When set to false, the items which could not fit onto the
+ * print bed will be placed next to the print bed so the user should see a
+ * pile of items on the print bed and some other piles outside the print
+ * area that can be dragged later onto the print bed as a group.
+ *
+ * \param model The model object with the 3D content.
+ * \param dist The minimum distance which is allowed for any pair of items
+ * on the print bed in any direction.
+ * \param bb The bounding box of the print bed. It corresponds to the 'bin'
+ * for bin packing.
+ * \param first_bin_only This parameter controls whether to place the
+ * remaining items which do not fit onto the print area next to the print
+ * bed or leave them untouched (let the user arrange them by hand or remove
+ * them).
+ */
+bool arrange(Model &model, coordf_t min_obj_distance,
+ const Slic3r::Polyline& bed,
+ BedShapeHint bedhint,
+ bool first_bin_only,
+ std::function<void(unsigned)> progressind)
+{
+ using ArrangeResult = _IndexedPackGroup<PolygonImpl>;
+
+ bool ret = true;
+
+ // Get the 2D projected shapes with their 3D model instance pointers
+ auto shapemap = arr::projectModelFromTop(model);
+
+ // Copy the references for the shapes only as the arranger expects a
+ // sequence of objects convertible to Item or ClipperPolygon
+ std::vector<std::reference_wrapper<Item>> shapes;
+ shapes.reserve(shapemap.size());
+ std::for_each(shapemap.begin(), shapemap.end(),
+ [&shapes] (ShapeData2D::value_type& it)
+ {
+ shapes.push_back(std::ref(it.second));
+ });
+
+ IndexedPackGroup result;
+
+ if(bedhint.type == BedShapeType::WHO_KNOWS) bedhint = bedShape(bed);
+
+ BoundingBox bbb(bed);
+
+ auto binbb = Box({
+ static_cast<libnest2d::Coord>(bbb.min(0)),
+ static_cast<libnest2d::Coord>(bbb.min(1))
+ },
+ {
+ static_cast<libnest2d::Coord>(bbb.max(0)),
+ static_cast<libnest2d::Coord>(bbb.max(1))
+ });
+
+ switch(bedhint.type) {
+ case BedShapeType::BOX: {
+
+ // Create the arranger for the box shaped bed
+ AutoArranger<Box> arrange(binbb, min_obj_distance, progressind);
+
+ // Arrange and return the items with their respective indices within the
+ // input sequence.
+ result = arrange(shapes.begin(), shapes.end());
+ break;
+ }
+ case BedShapeType::CIRCLE: {
+
+ auto c = bedhint.shape.circ;
+ auto cc = lnCircle(c);
+
+ AutoArranger<lnCircle> arrange(cc, min_obj_distance, progressind);
+ result = arrange(shapes.begin(), shapes.end());
+ break;
+ }
+ case BedShapeType::IRREGULAR:
+ case BedShapeType::WHO_KNOWS: {
+
+ using P = libnest2d::PolygonImpl;
+
+ auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
+ P irrbed = sl::create<PolygonImpl>(std::move(ctour));
+
+ AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
+
+ // Arrange and return the items with their respective indices within the
+ // input sequence.
+ result = arrange(shapes.begin(), shapes.end());
+ break;
+ }
+ };
+
+ if(result.empty()) return false;
+
+ if(first_bin_only) {
+ applyResult(result.front(), 0, shapemap);
+ } else {
+
+ const auto STRIDE_PADDING = 1.2;
+
+ Coord stride = static_cast<Coord>(STRIDE_PADDING*
+ binbb.width()*SCALING_FACTOR);
+ Coord batch_offset = 0;
+
+ for(auto& group : result) {
+ applyResult(group, batch_offset, shapemap);
+
+ // Only the first pack group can be placed onto the print bed. The
+ // other objects which could not fit will be placed next to the
+ // print bed
+ batch_offset += stride;
+ }
+ }
+
+ for(auto objptr : model.objects) objptr->invalidate_bounding_box();
+
+ return ret && result.size() == 1;
+}
+
+}
+}
+#endif // MODELARRANGE_HPP
diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp
index e8605d68c..ff3475ed8 100644
--- a/xs/src/libslic3r/MotionPlanner.cpp
+++ b/xs/src/libslic3r/MotionPlanner.cpp
@@ -58,7 +58,7 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to)
{
// If we have an empty configuration space, return a straight move.
if (m_islands.empty())
- return Line(from, to);
+ return Polyline(from, to);
// Are both points in the same island?
int island_idx_from = -1;
@@ -74,7 +74,7 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to)
// Since both points are in the same island, is a direct move possible?
// If so, we avoid generating the visibility environment.
if (island.m_island.contains(Line(from, to)))
- return Line(from, to);
+ return Polyline(from, to);
// Both points are inside a single island, but the straight line crosses the island boundary.
island_idx = idx;
break;
@@ -90,7 +90,7 @@ Polyline MotionPlanner::shortest_path(const Point &from, const Point &to)
if (env.m_env.expolygons.empty()) {
// if this environment is empty (probably because it's too small), perform straight move
// and avoid running the algorithms on empty dataset
- return Line(from, to);
+ return Polyline(from, to);
}
// Now check whether points are inside the environment.
@@ -224,7 +224,7 @@ const MotionPlannerGraph& MotionPlanner::init_graph(int island_idx)
else
v1_idx = i_v1->second;
// Euclidean distance is used as weight for the graph edge
- graph->add_edge(v0_idx, v1_idx, p0.distance_to(p1));
+ graph->add_edge(v0_idx, v1_idx, (p1 - p0).cast<double>().norm());
}
}
}
@@ -238,7 +238,7 @@ static inline size_t nearest_waypoint_index(const Point &start_point, const Poin
size_t idx = size_t(-1);
double dmin = std::numeric_limits<double>::infinity();
for (const Point &p : middle_points) {
- double d = start_point.distance_to(p) + p.distance_to(end_point);
+ double d = (p - start_point).cast<double>().norm() + (end_point - p).cast<double>().norm();
if (d < dmin) {
idx = &p - middle_points.data();
dmin = d;
diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp
index 2e65492cd..f44897a04 100644
--- a/xs/src/libslic3r/MultiPoint.cpp
+++ b/xs/src/libslic3r/MultiPoint.cpp
@@ -8,59 +8,52 @@ MultiPoint::operator Points() const
return this->points;
}
-void
-MultiPoint::scale(double factor)
+void MultiPoint::scale(double factor)
{
- for (Points::iterator it = points.begin(); it != points.end(); ++it) {
- (*it).scale(factor);
- }
+ for (Point &pt : points)
+ pt *= factor;
}
-void
-MultiPoint::translate(double x, double y)
+void MultiPoint::translate(double x, double y)
{
- for (Points::iterator it = points.begin(); it != points.end(); ++it) {
- (*it).translate(x, y);
- }
+ Vector v(x, y);
+ for (Point &pt : points)
+ pt += v;
}
-void
-MultiPoint::translate(const Point &vector)
+void MultiPoint::translate(const Point &v)
{
- this->translate(vector.x, vector.y);
+ for (Point &pt : points)
+ pt += v;
}
void MultiPoint::rotate(double cos_angle, double sin_angle)
{
for (Point &pt : this->points) {
- double cur_x = double(pt.x);
- double cur_y = double(pt.y);
- pt.x = coord_t(round(cos_angle * cur_x - sin_angle * cur_y));
- pt.y = coord_t(round(cos_angle * cur_y + sin_angle * cur_x));
+ double cur_x = double(pt(0));
+ double cur_y = double(pt(1));
+ pt(0) = coord_t(round(cos_angle * cur_x - sin_angle * cur_y));
+ pt(1) = coord_t(round(cos_angle * cur_y + sin_angle * cur_x));
}
}
-void
-MultiPoint::rotate(double angle, const Point &center)
+void MultiPoint::rotate(double angle, const Point &center)
{
double s = sin(angle);
double c = cos(angle);
- for (Points::iterator it = points.begin(); it != points.end(); ++it) {
- double dx = double(it->x - center.x);
- double dy = double(it->y - center.y);
- it->x = (coord_t)round(double(center.x) + c * dx - s * dy);
- it->y = (coord_t)round(double(center.y) + c * dy + s * dx);
+ for (Point &pt : points) {
+ Vec2crd v(pt - center);
+ pt(0) = (coord_t)round(double(center(0)) + c * v[0] - s * v[1]);
+ pt(1) = (coord_t)round(double(center(1)) + c * v[1] + s * v[0]);
}
}
-void
-MultiPoint::reverse()
+void MultiPoint::reverse()
{
std::reverse(this->points.begin(), this->points.end());
}
-Point
-MultiPoint::first_point() const
+Point MultiPoint::first_point() const
{
return this->points.front();
}
@@ -79,16 +72,16 @@ MultiPoint::length() const
int
MultiPoint::find_point(const Point &point) const
{
- for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
- if (it->coincides_with(point)) return it - this->points.begin();
- }
+ for (const Point &pt : this->points)
+ if (pt == point)
+ return &pt - &this->points.front();
return -1; // not found
}
bool
MultiPoint::has_boundary_point(const Point &point) const
{
- double dist = point.distance_to(point.projection_onto(*this));
+ double dist = (point.projection_onto(*this) - point).cast<double>().norm();
return dist < SCALED_EPSILON;
}
@@ -102,7 +95,7 @@ bool
MultiPoint::has_duplicate_points() const
{
for (size_t i = 1; i < points.size(); ++i)
- if (points[i-1].coincides_with(points[i]))
+ if (points[i-1] == points[i])
return true;
return false;
}
@@ -112,7 +105,7 @@ MultiPoint::remove_duplicate_points()
{
size_t j = 0;
for (size_t i = 1; i < points.size(); ++i) {
- if (points[j].coincides_with(points[i])) {
+ if (points[j] == points[i]) {
// Just increase index i.
} else {
++ j;
@@ -146,10 +139,10 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
if (l.intersection(line, &ip)) {
if (! found) {
found = true;
- dmin = ip.distance_to(line.a);
+ dmin = (line.a - ip).cast<double>().norm();
*intersection = ip;
} else {
- double d = ip.distance_to(line.a);
+ double d = (line.a - ip).cast<double>().norm();
if (d < dmin) {
dmin = d;
*intersection = ip;
@@ -160,19 +153,6 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const
return found;
}
-std::string
-MultiPoint::dump_perl() const
-{
- std::ostringstream ret;
- ret << "[";
- for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) {
- ret << p->dump_perl();
- if (p != this->points.end()-1) ret << ",";
- }
- ret << "]";
- return ret.str();
-}
-
//FIXME This is very inefficient in term of memory use.
// The recursive algorithm shall run in place, not allocating temporary data in each recursion.
Points
@@ -185,7 +165,7 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
Line full(points.front(), points.back());
for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) {
// we use shortest distance, not perpendicular distance
- double d = it->distance_to(full);
+ double d = full.distance_to(*it);
if (d > dmax) {
index = it - points.begin();
dmax = d;
@@ -216,25 +196,22 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance)
void MultiPoint3::translate(double x, double y)
{
- for (Point3& p : points)
- {
- p.translate(x, y);
+ for (Vec3crd &p : points) {
+ p(0) += x;
+ p(1) += y;
}
}
void MultiPoint3::translate(const Point& vector)
{
- translate(vector.x, vector.y);
+ this->translate(vector(0), vector(1));
}
double MultiPoint3::length() const
{
- Lines3 lines = this->lines();
double len = 0.0;
- for (const Line3& line : lines)
- {
+ for (const Line3& line : this->lines())
len += line.length();
- }
return len;
}
@@ -246,15 +223,11 @@ BoundingBox3 MultiPoint3::bounding_box() const
bool MultiPoint3::remove_duplicate_points()
{
size_t j = 0;
- for (size_t i = 1; i < points.size(); ++i)
- {
- if (points[j].coincides_with(points[i]))
- {
+ for (size_t i = 1; i < points.size(); ++i) {
+ if (points[j] == points[i]) {
// Just increase index i.
- }
- else
- {
- ++j;
+ } else {
+ ++ j;
if (j < i)
points[j] = points[i];
}
@@ -281,19 +254,19 @@ BoundingBox get_extents_rotated(const Points &points, double angle)
double s = sin(angle);
double c = cos(angle);
Points::const_iterator it = points.begin();
- double cur_x = (double)it->x;
- double cur_y = (double)it->y;
- bbox.min.x = bbox.max.x = (coord_t)round(c * cur_x - s * cur_y);
- bbox.min.y = bbox.max.y = (coord_t)round(c * cur_y + s * cur_x);
+ double cur_x = (double)(*it)(0);
+ double cur_y = (double)(*it)(1);
+ bbox.min(0) = bbox.max(0) = (coord_t)round(c * cur_x - s * cur_y);
+ bbox.min(1) = bbox.max(1) = (coord_t)round(c * cur_y + s * cur_x);
for (++it; it != points.end(); ++it) {
- double cur_x = (double)it->x;
- double cur_y = (double)it->y;
+ double cur_x = (double)(*it)(0);
+ double cur_y = (double)(*it)(1);
coord_t x = (coord_t)round(c * cur_x - s * cur_y);
coord_t y = (coord_t)round(c * cur_y + s * cur_x);
- bbox.min.x = std::min(x, bbox.min.x);
- bbox.min.y = std::min(y, bbox.min.y);
- bbox.max.x = std::max(x, bbox.max.x);
- bbox.max.y = std::max(y, bbox.max.y);
+ bbox.min(0) = std::min(x, bbox.min(0));
+ bbox.min(1) = std::min(y, bbox.min(1));
+ bbox.max(0) = std::max(x, bbox.max(0));
+ bbox.max(1) = std::max(y, bbox.max(1));
}
bbox.defined = true;
}
diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp
index 0970e9a67..1fef4083b 100644
--- a/xs/src/libslic3r/MultiPoint.hpp
+++ b/xs/src/libslic3r/MultiPoint.hpp
@@ -18,10 +18,11 @@ public:
Points points;
operator Points() const;
- MultiPoint() {};
+ MultiPoint() {}
MultiPoint(const MultiPoint &other) : points(other.points) {}
MultiPoint(MultiPoint &&other) : points(std::move(other.points)) {}
- explicit MultiPoint(const Points &_points): points(_points) {}
+ MultiPoint(std::initializer_list<Point> list) : points(list) {}
+ explicit MultiPoint(const Points &_points) : points(_points) {}
MultiPoint& operator=(const MultiPoint &other) { points = other.points; return *this; }
MultiPoint& operator=(MultiPoint &&other) { points = std::move(other.points); return *this; }
void scale(double factor);
@@ -43,9 +44,9 @@ public:
int idx = -1;
if (! this->points.empty()) {
idx = 0;
- double dist_min = this->points.front().distance_to(point);
+ double dist_min = (point - this->points.front()).cast<double>().norm();
for (int i = 1; i < int(this->points.size()); ++ i) {
- double d = this->points[i].distance_to(point);
+ double d = (this->points[i] - point).cast<double>().norm();
if (d < dist_min) {
dist_min = d;
idx = i;
@@ -75,7 +76,6 @@ public:
bool intersection(const Line& line, Point* intersection) const;
bool first_intersection(const Line& line, Point* intersection) const;
- std::string dump_perl() const;
static Points _douglas_peucker(const Points &points, const double tolerance);
};
@@ -85,7 +85,7 @@ class MultiPoint3
public:
Points3 points;
- void append(const Point3& point) { this->points.push_back(point); }
+ void append(const Vec3crd& point) { this->points.push_back(point); }
void translate(double x, double y);
void translate(const Point& vector);
diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp
index 176f28926..de8aeeb2a 100644
--- a/xs/src/libslic3r/PerimeterGenerator.cpp
+++ b/xs/src/libslic3r/PerimeterGenerator.cpp
@@ -44,7 +44,6 @@ void PerimeterGenerator::process()
// lower layer, so we take lower slices and offset them by half the nozzle diameter used
// in the current layer
double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1);
-
this->_lower_slices_p = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2)));
}
@@ -52,149 +51,115 @@ void PerimeterGenerator::process()
// extra perimeters for each one
for (const Surface &surface : this->slices->surfaces) {
// detect how many perimeters must be generated for this island
- const int loop_number = this->config->perimeters + surface.extra_perimeters -1; // 0-indexed loops
-
- Polygons gaps;
-
- Polygons last = surface.expolygon.simplify_p(SCALED_RESOLUTION);
- if (loop_number >= 0) { // no loops = -1
-
+ int loop_number = this->config->perimeters + surface.extra_perimeters - 1; // 0-indexed loops
+ ExPolygons last = union_ex(surface.expolygon.simplify_p(SCALED_RESOLUTION));
+ ExPolygons gaps;
+ if (loop_number >= 0) {
+ // In case no perimeters are to be generated, loop_number will equal to -1.
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
std::vector<PerimeterGeneratorLoops> holes(loop_number+1); // depth => loops
ThickPolylines thin_walls;
-
// we loop one time more than needed in order to find gaps after the last perimeter was applied
- for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0
- Polygons offsets;
+ for (int i = 0;; ++ i) { // outer loop is 0
+ // Calculate next onion shell of perimeters.
+ ExPolygons offsets;
if (i == 0) {
// the minimum thickness of a single loop is:
// ext_width/2 + ext_spacing/2 + spacing/2 + width/2
- if (this->config->thin_walls) {
- offsets = offset2(
+ offsets = this->config->thin_walls ?
+ offset2_ex(
last,
-(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1),
- +(ext_min_spacing/2 - 1)
- );
- } else {
- offsets = offset(last, - ext_perimeter_width / 2);
- }
-
+ +(ext_min_spacing / 2 - 1)) :
+ offset_ex(last, - ext_perimeter_width / 2);
// look for thin walls
if (this->config->thin_walls) {
- Polygons diffpp = diff(
- last,
- offset(offsets, ext_perimeter_width / 2),
- true // medial axis requires non-overlapping geometry
- );
-
// the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width
// (actually, something larger than that still may exist due to mitering or other causes)
coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3);
- ExPolygons expp = offset2_ex(diffpp, -min_width/2, +min_width/2);
-
+ ExPolygons expp = offset2_ex(
+ // medial axis requires non-overlapping geometry
+ diff_ex(to_polygons(last),
+ offset(offsets, ext_perimeter_width / 2),
+ true),
+ - min_width / 2, min_width / 2);
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
- for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex)
- ex->medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls);
-
- #ifdef DEBUG
- printf(" " PRINTF_ZU " thin walls detected\n", thin_walls.size());
- #endif
-
- /*
- if (false) {
- require "Slic3r/SVG.pm";
- Slic3r::SVG::output(
- "medial_axis.svg",
- no_arrows => 1,
- #expolygons => \@expp,
- polylines => \@thin_walls,
- );
- }
- */
+ for (ExPolygon &ex : expp)
+ ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls);
}
} else {
//FIXME Is this offset correct if the line width of the inner perimeters differs
// from the line width of the infill?
coord_t distance = (i == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
-
- if (this->config->thin_walls) {
+ offsets = this->config->thin_walls ?
// This path will ensure, that the perimeters do not overfill, as in
// prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters
// excessively, creating gaps, which then need to be filled in by the not very
// reliable gap fill algorithm.
// Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than
// the original.
- offsets = offset2(
- last,
- -(distance + min_spacing/2 - 1),
- +(min_spacing/2 - 1)
- );
- } else {
+ offset2_ex(last,
+ - (distance + min_spacing / 2 - 1),
+ min_spacing / 2 - 1) :
// If "detect thin walls" is not enabled, this paths will be entered, which
// leads to overflows, as in prusa3d/Slic3r GH #32
- offsets = offset(
- last,
- -distance
- );
- }
-
+ offset_ex(last, - distance);
// look for gaps
- if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0) {
+ if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0)
// not using safety offset here would "detect" very narrow gaps
// (but still long enough to escape the area threshold) that gap fill
// won't be able to fill but we'd still remove from infill area
- Polygons diff_pp = diff(
- offset(last, -0.5*distance),
- offset(offsets, +0.5*distance + 10) // safety offset
- );
- gaps.insert(gaps.end(), diff_pp.begin(), diff_pp.end());
- }
+ append(gaps, diff_ex(
+ offset(last, -0.5 * distance),
+ offset(offsets, 0.5 * distance + 10))); // safety offset
}
-
- if (offsets.empty()) break;
- if (i > loop_number) break; // we were only looking for gaps this time
-
- last = offsets;
- for (Polygons::const_iterator polygon = offsets.begin(); polygon != offsets.end(); ++polygon) {
- PerimeterGeneratorLoop loop(*polygon, i);
- loop.is_contour = polygon->is_counter_clockwise();
- if (loop.is_contour) {
- contours[i].push_back(loop);
- } else {
- holes[i].push_back(loop);
+ if (offsets.empty()) {
+ // Store the number of loops actually generated.
+ loop_number = i - 1;
+ // No region left to be filled in.
+ last.clear();
+ break;
+ } else if (i > loop_number) {
+ // If i > loop_number, we were looking just for gaps.
+ break;
+ }
+ for (const ExPolygon &expolygon : offsets) {
+ contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true));
+ if (! expolygon.holes.empty()) {
+ holes[i].reserve(holes[i].size() + expolygon.holes.size());
+ for (const Polygon &hole : expolygon.holes)
+ holes[i].emplace_back(PerimeterGeneratorLoop(hole, i, false));
}
}
+ last = std::move(offsets);
}
-
+
// nest loops: holes first
- for (int 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 (int i = 0; i < (int)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 (int t = d+1; t <= loop_number; ++t) {
- for (int j = 0; j < (int)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);
holes_d.erase(holes_d.begin() + i);
- --i;
+ -- i;
goto NEXT_LOOP;
}
}
}
-
// if no hole contains this hole, find the contour loop that contains it
- for (int t = loop_number; t >= 0; --t) {
- for (int j = 0; j < (int)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);
holes_d.erase(holes_d.begin() + i);
- --i;
+ -- i;
goto NEXT_LOOP;
}
}
@@ -202,75 +167,57 @@ void PerimeterGenerator::process()
NEXT_LOOP: ;
}
}
-
// nest contour loops
- for (int 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 (int i = 0; i < (int)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 (int t = d-1; t >= 0; --t) {
- for (int 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);
contours_d.erase(contours_d.begin() + i);
- --i;
+ -- i;
goto NEXT_CONTOUR;
}
}
}
-
NEXT_CONTOUR: ;
}
}
-
// at this point, all loops should be in contours[0]
-
ExtrusionEntityCollection entities = this->_traverse_loops(contours.front(), thin_walls);
-
// if brim will be printed, reverse the order of perimeters so that
// we continue inwards after having finished the brim
// TODO: add test for perimeter order
- if (this->config->external_perimeters_first
- || (this->layer_id == 0 && this->print_config->brim_width.value > 0))
- entities.reverse();
-
+ if (this->config->external_perimeters_first ||
+ (this->layer_id == 0 && this->print_config->brim_width.value > 0))
+ entities.reverse();
// append perimeters for this slice as a collection
- if (!entities.empty())
+ if (! entities.empty())
this->loops->append(entities);
} // for each loop of an island
// fill gaps
- if (!gaps.empty()) {
- /*
- SVG svg("gaps.svg");
- svg.draw(union_ex(gaps));
- svg.Close();
- */
-
+ if (! gaps.empty()) {
// collapse
double min = 0.2 * perimeter_width * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2. * perimeter_spacing;
ExPolygons gaps_ex = diff_ex(
- offset2(gaps, -min/2, +min/2),
- offset2(gaps, -max/2, +max/2),
- true
- );
-
+ //FIXME offset2 would be enough and cheaper.
+ offset2_ex(gaps, -min/2, +min/2),
+ offset2_ex(gaps, -max/2, +max/2),
+ true);
ThickPolylines polylines;
- for (ExPolygons::const_iterator ex = gaps_ex.begin(); ex != gaps_ex.end(); ++ex)
- ex->medial_axis(max, min, &polylines);
-
- if (!polylines.empty()) {
+ for (const ExPolygon &ex : gaps_ex)
+ ex.medial_axis(max, min, &polylines);
+ if (! polylines.empty()) {
ExtrusionEntityCollection gap_fill = this->_variable_width(polylines,
erGapFill, this->solid_infill_flow);
-
this->gap_fill->append(gap_fill.entities);
-
/* Make sure we don't infill narrow parts that are already gap-filled
(we only consider this surface's gaps to reduce the diff() complexity).
Growing actual extrusions ensures that gaps not filled by medial axis
@@ -279,7 +226,7 @@ void PerimeterGenerator::process()
and use zigzag). */
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
// therefore it may cover the area, but no the volume.
- last = diff(last, gap_fill.polygons_covered_by_width(10.f));
+ last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f));
}
}
@@ -287,36 +234,34 @@ void PerimeterGenerator::process()
// we offset by half the perimeter spacing (to get to the actual infill boundary)
// and then we offset back and forth by half the infill spacing to only consider the
// non-collapsing regions
- coord_t inset = 0;
- if (loop_number == 0) {
- // one loop
- inset += ext_perimeter_spacing / 2;
- } else if (loop_number > 0) {
- // two or more loops
- inset += perimeter_spacing / 2;
- }
+ coord_t inset =
+ (loop_number < 0) ? 0 :
+ (loop_number == 0) ?
+ // one loop
+ ext_perimeter_spacing / 2 :
+ // two or more loops?
+ perimeter_spacing / 2;
// only apply infill overlap if we actually have one perimeter
if (inset > 0)
- inset -= this->config->get_abs_value("infill_overlap", inset + solid_infill_spacing / 2);
+ inset -= scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2)));
// simplify infill contours according to resolution
Polygons pp;
- for (ExPolygon &ex : union_ex(last))
+ for (ExPolygon &ex : last)
ex.simplify_p(SCALED_RESOLUTION, &pp);
// collapse too narrow infill areas
- coord_t min_perimeter_infill_spacing = solid_infill_spacing * (1 - INSET_OVERLAP_TOLERANCE);
+ coord_t min_perimeter_infill_spacing = solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE);
// append infill areas to fill_surfaces
this->fill_surfaces->append(
offset2_ex(
- pp,
- -inset -min_perimeter_infill_spacing/2,
- +min_perimeter_infill_spacing/2),
+ union_ex(pp),
+ - inset - min_perimeter_infill_spacing / 2,
+ min_perimeter_infill_spacing / 2),
stInternal);
} // for each island
}
-ExtrusionEntityCollection
-PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
- ThickPolylines &thin_walls) const
+ExtrusionEntityCollection PerimeterGenerator::_traverse_loops(
+ const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const
{
// loops is an arrayref of ::Loop objects
// turn each one into an ExtrusionLoop object
@@ -421,120 +366,115 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops,
return entities;
}
-ExtrusionEntityCollection
-PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const
+static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance)
{
- // this value determines granularity of adaptive width, as G-code does not allow
- // variable extrusion within a single move; this value shall only affect the amount
- // of segments, and any pruning shall be performed before we apply this tolerance
- const double tolerance = scale_(0.05);
+ ExtrusionPaths paths;
+ ExtrusionPath path(role);
+ ThickLines lines = thick_polyline.thicklines();
- ExtrusionEntityCollection coll;
- for (ThickPolylines::const_iterator p = polylines.begin(); p != polylines.end(); ++p) {
- ExtrusionPaths paths;
- ExtrusionPath path(role);
- ThickLines lines = p->thicklines();
+ for (int i = 0; i < (int)lines.size(); ++i) {
+ const ThickLine& line = lines[i];
- for (int i = 0; i < (int)lines.size(); ++i) {
- const ThickLine& line = lines[i];
-
- const coordf_t line_len = line.length();
- if (line_len < SCALED_EPSILON) continue;
-
- double thickness_delta = fabs(line.a_width - line.b_width);
- if (thickness_delta > tolerance) {
- const unsigned short segments = ceil(thickness_delta / tolerance);
- const coordf_t seg_len = line_len / segments;
- Points pp;
- std::vector<coordf_t> width;
- {
- pp.push_back(line.a);
- width.push_back(line.a_width);
- for (size_t j = 1; j < segments; ++j) {
- pp.push_back(line.point_at(j*seg_len));
-
- coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
- width.push_back(w);
- width.push_back(w);
- }
- pp.push_back(line.b);
- width.push_back(line.b_width);
+ const coordf_t line_len = line.length();
+ if (line_len < SCALED_EPSILON) continue;
+
+ double thickness_delta = fabs(line.a_width - line.b_width);
+ if (thickness_delta > tolerance) {
+ const unsigned short segments = ceil(thickness_delta / tolerance);
+ const coordf_t seg_len = line_len / segments;
+ Points pp;
+ std::vector<coordf_t> width;
+ {
+ pp.push_back(line.a);
+ width.push_back(line.a_width);
+ for (size_t j = 1; j < segments; ++j) {
+ pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>());
- assert(pp.size() == segments + 1);
- assert(width.size() == segments*2);
- }
-
- // delete this line and insert new ones
- lines.erase(lines.begin() + i);
- for (size_t j = 0; j < segments; ++j) {
- ThickLine new_line(pp[j], pp[j+1]);
- new_line.a_width = width[2*j];
- new_line.b_width = width[2*j+1];
- lines.insert(lines.begin() + i + j, new_line);
+ coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len;
+ width.push_back(w);
+ width.push_back(w);
}
+ pp.push_back(line.b);
+ width.push_back(line.b_width);
- --i;
- continue;
+ assert(pp.size() == segments + 1);
+ assert(width.size() == segments*2);
}
- const double w = fmax(line.a_width, line.b_width);
+ // delete this line and insert new ones
+ lines.erase(lines.begin() + i);
+ for (size_t j = 0; j < segments; ++j) {
+ ThickLine new_line(pp[j], pp[j+1]);
+ new_line.a_width = width[2*j];
+ new_line.b_width = width[2*j+1];
+ lines.insert(lines.begin() + i + j, new_line);
+ }
- if (path.polyline.points.empty()) {
- path.polyline.append(line.a);
+ -- i;
+ continue;
+ }
+
+ const double w = fmax(line.a_width, line.b_width);
+ if (path.polyline.points.empty()) {
+ path.polyline.append(line.a);
+ path.polyline.append(line.b);
+ // Convert from spacing to extrusion width based on the extrusion model
+ // of a square extrusion ended with semi circles.
+ flow.width = unscale<float>(w) + flow.height * (1. - 0.25 * PI);
+ #ifdef SLIC3R_DEBUG
+ printf(" filling %f gap\n", flow.width);
+ #endif
+ path.mm3_per_mm = flow.mm3_per_mm();
+ path.width = flow.width;
+ path.height = flow.height;
+ } else {
+ thickness_delta = fabs(scale_(flow.width) - w);
+ if (thickness_delta <= tolerance) {
+ // the width difference between this line and the current flow width is
+ // within the accepted tolerance
path.polyline.append(line.b);
- // Convert from spacing to extrusion width based on the extrusion model
- // of a square extrusion ended with semi circles.
- flow.width = unscale(w) + flow.height * (1. - 0.25 * PI);
- #ifdef SLIC3R_DEBUG
- printf(" filling %f gap\n", flow.width);
- #endif
- path.mm3_per_mm = flow.mm3_per_mm();
- path.width = flow.width;
- path.height = flow.height;
} else {
- thickness_delta = fabs(scale_(flow.width) - w);
- if (thickness_delta <= tolerance) {
- // the width difference between this line and the current flow width is
- // within the accepted tolerance
-
- path.polyline.append(line.b);
- } else {
- // we need to initialize a new line
- paths.push_back(path);
- path = ExtrusionPath(role);
- --i;
- }
+ // we need to initialize a new line
+ paths.emplace_back(std::move(path));
+ path = ExtrusionPath(role);
+ -- i;
}
}
- if (path.polyline.is_valid())
- paths.push_back(path);
-
- // append paths to collection
- if (!paths.empty()) {
- if (paths.front().first_point().coincides_with(paths.back().last_point()))
- coll.append(ExtrusionLoop(paths));
+ }
+ if (path.polyline.is_valid())
+ paths.emplace_back(std::move(path));
+ return paths;
+}
+
+ExtrusionEntityCollection PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const
+{
+ // This value determines granularity of adaptive width, as G-code does not allow
+ // variable extrusion within a single move; this value shall only affect the amount
+ // of segments, and any pruning shall be performed before we apply this tolerance.
+ ExtrusionEntityCollection coll;
+ const double tolerance = scale_(0.05);
+ for (const ThickPolyline &p : polylines) {
+ ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance);
+ // Append paths to collection.
+ if (! paths.empty()) {
+ if (paths.front().first_point() == paths.back().last_point())
+ coll.append(ExtrusionLoop(std::move(paths)));
else
- coll.append(paths);
+ coll.append(std::move(paths));
}
}
-
return coll;
}
-bool
-PerimeterGeneratorLoop::is_internal_contour() const
+bool PerimeterGeneratorLoop::is_internal_contour() const
{
- if (this->is_contour) {
- // an internal contour is a contour containing no other contours
- for (std::vector<PerimeterGeneratorLoop>::const_iterator loop = this->children.begin();
- loop != this->children.end(); ++loop) {
- if (loop->is_contour) {
- return false;
- }
- }
- return true;
- }
- return false;
+ // An internal contour is a contour containing no other contours
+ if (! this->is_contour)
+ return false;
+ for (const PerimeterGeneratorLoop &loop : this->children)
+ if (loop.is_contour)
+ return false;
+ return true;
}
}
diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp
index dd1f9b0ec..44af8c8be 100644
--- a/xs/src/libslic3r/PerimeterGenerator.hpp
+++ b/xs/src/libslic3r/PerimeterGenerator.hpp
@@ -24,9 +24,8 @@ public:
// Children contour, may be both CCW and CW oriented (outer contours or holes).
std::vector<PerimeterGeneratorLoop> children;
- PerimeterGeneratorLoop(Polygon polygon, unsigned short depth)
- : polygon(polygon), is_contour(false), depth(depth)
- {};
+ PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour) :
+ polygon(polygon), is_contour(is_contour), depth(depth) {}
// External perimeter. It may be CCW or CW oriented (outer contour or hole contour).
bool is_external() const { return this->depth == 0; }
// An island, which may have holes, but it does not have another internal island.
diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp
index 848e7fd2a..cc6f1c75e 100644
--- a/xs/src/libslic3r/PlaceholderParser.cpp
+++ b/xs/src/libslic3r/PlaceholderParser.cpp
@@ -414,6 +414,7 @@ namespace client
lhs.type = TYPE_BOOL;
lhs.data.b = invert ? ! value : value;
}
+ // Compare operators, store the result into lhs.
static void equal (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '=', false); }
static void not_equal(expr &lhs, expr &rhs) { compare_op(lhs, rhs, '=', true ); }
static void lower (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', false); }
@@ -421,6 +422,40 @@ namespace client
static void leq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '>', true ); }
static void geq (expr &lhs, expr &rhs) { compare_op(lhs, rhs, '<', true ); }
+ enum Function2ParamsType {
+ FUNCTION_MIN,
+ FUNCTION_MAX,
+ };
+ // Store the result into param1.
+ static void function_2params(expr &param1, expr &param2, Function2ParamsType fun)
+ {
+ const char *err_msg = "Not a numeric type.";
+ param1.throw_if_not_numeric(err_msg);
+ param2.throw_if_not_numeric(err_msg);
+ if (param1.type == TYPE_DOUBLE || param2.type == TYPE_DOUBLE) {
+ double d = 0.;
+ switch (fun) {
+ case FUNCTION_MIN: d = std::min(param1.as_d(), param2.as_d()); break;
+ case FUNCTION_MAX: d = std::max(param1.as_d(), param2.as_d()); break;
+ default: param1.throw_exception("Internal error: invalid function");
+ }
+ param1.data.d = d;
+ param1.type = TYPE_DOUBLE;
+ } else {
+ int i = 0.;
+ switch (fun) {
+ case FUNCTION_MIN: i = std::min(param1.as_i(), param2.as_i()); break;
+ case FUNCTION_MAX: i = std::max(param1.as_i(), param2.as_i()); break;
+ default: param1.throw_exception("Internal error: invalid function");
+ }
+ param1.data.i = i;
+ param1.type = TYPE_INT;
+ }
+ }
+ // Store the result into param1.
+ static void min(expr &param1, expr &param2) { function_2params(param1, param2, FUNCTION_MIN); }
+ static void max(expr &param1, expr &param2) { function_2params(param1, param2, FUNCTION_MAX); }
+
static void regex_op(expr &lhs, boost::iterator_range<Iterator> &rhs, char op)
{
const std::string *subject = nullptr;
@@ -658,7 +693,7 @@ namespace client
case coInts: output.set_i(static_cast<const ConfigOptionInts *>(opt.opt)->values[idx]); break;
case coStrings: output.set_s(static_cast<const ConfigOptionStrings *>(opt.opt)->values[idx]); break;
case coPercents: output.set_d(static_cast<const ConfigOptionPercents*>(opt.opt)->values[idx]); break;
- case coPoints: output.set_s(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx].dump_perl()); break;
+ case coPoints: output.set_s(to_string(static_cast<const ConfigOptionPoints *>(opt.opt)->values[idx])); break;
case coBools: output.set_b(static_cast<const ConfigOptionBools *>(opt.opt)->values[idx] != 0); break;
default:
ctx->throw_exception("Unknown vector variable type", opt.it_range);
@@ -1019,6 +1054,10 @@ namespace client
| (lit('-') > unary_expression(_r1) ) [ px::bind(&FactorActions::minus_, _1, _val) ]
| (lit('+') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::expr_, _1, _2, _val) ]
| ((kw["not"] | '!') > unary_expression(_r1) > iter_pos) [ px::bind(&FactorActions::not_, _1, _val) ]
+ | (kw["min"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
+ [ px::bind(&expr<Iterator>::min, _val, _2) ]
+ | (kw["max"] > '(' > conditional_expression(_r1) [_val = _1] > ',' > conditional_expression(_r1) > ')')
+ [ px::bind(&expr<Iterator>::max, _val, _2) ]
| (strict_double > iter_pos) [ px::bind(&FactorActions::double_, _1, _2, _val) ]
| (int_ > iter_pos) [ px::bind(&FactorActions::int_, _1, _2, _val) ]
| (kw[bool_] > iter_pos) [ px::bind(&FactorActions::bool_, _1, _2, _val) ]
@@ -1051,6 +1090,8 @@ namespace client
("elsif")
("endif")
("false")
+ ("min")
+ ("max")
("not")
("or")
("true");
diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp
index 7c1dc91f5..c2417d0dc 100644
--- a/xs/src/libslic3r/Point.cpp
+++ b/xs/src/libslic3r/Point.cpp
@@ -1,85 +1,72 @@
#include "Point.hpp"
#include "Line.hpp"
#include "MultiPoint.hpp"
+#include "Int128.hpp"
#include <algorithm>
-#include <cmath>
namespace Slic3r {
-Point::Point(double x, double y)
+std::vector<Vec3f> transform(const std::vector<Vec3f>& points, const Transform3f& t)
{
- this->x = lrint(x);
- this->y = lrint(y);
-}
+ unsigned int vertices_count = (unsigned int)points.size();
+ if (vertices_count == 0)
+ return std::vector<Vec3f>();
-std::string
-Point::wkt() const
-{
- std::ostringstream ss;
- ss << "POINT(" << this->x << " " << this->y << ")";
- return ss.str();
-}
+ unsigned int data_size = 3 * vertices_count * sizeof(float);
-std::string
-Point::dump_perl() const
-{
- std::ostringstream ss;
- ss << "[" << this->x << "," << this->y << "]";
- return ss.str();
-}
+ Eigen::MatrixXf src(3, vertices_count);
+ ::memcpy((void*)src.data(), (const void*)points.data(), data_size);
-void
-Point::scale(double factor)
-{
- this->x *= factor;
- this->y *= factor;
-}
+ Eigen::MatrixXf dst(3, vertices_count);
+ dst = t * src.colwise().homogeneous();
-void
-Point::translate(double x, double y)
-{
- this->x += x;
- this->y += y;
+ std::vector<Vec3f> ret_points(vertices_count, Vec3f::Zero());
+ ::memcpy((void*)ret_points.data(), (const void*)dst.data(), data_size);
+ return ret_points;
}
-void
-Point::translate(const Vector &vector)
+Pointf3s transform(const Pointf3s& points, const Transform3d& t)
{
- this->translate(vector.x, vector.y);
-}
+ unsigned int vertices_count = (unsigned int)points.size();
+ if (vertices_count == 0)
+ return Pointf3s();
-void
-Point::rotate(double angle)
-{
- double cur_x = (double)this->x;
- double cur_y = (double)this->y;
- double s = sin(angle);
- double c = cos(angle);
- this->x = (coord_t)round(c * cur_x - s * cur_y);
- this->y = (coord_t)round(c * cur_y + s * cur_x);
+ unsigned int data_size = 3 * vertices_count * sizeof(double);
+
+ Eigen::MatrixXd src(3, vertices_count);
+ ::memcpy((void*)src.data(), (const void*)points.data(), data_size);
+
+ Eigen::MatrixXd dst(3, vertices_count);
+ dst = t * src.colwise().homogeneous();
+
+ Pointf3s ret_points(vertices_count, Vec3d::Zero());
+ ::memcpy((void*)ret_points.data(), (const void*)dst.data(), data_size);
+ return ret_points;
}
-void
-Point::rotate(double angle, const Point &center)
+void Point::rotate(double angle)
{
- double cur_x = (double)this->x;
- double cur_y = (double)this->y;
- double s = sin(angle);
- double c = cos(angle);
- double dx = cur_x - (double)center.x;
- double dy = cur_y - (double)center.y;
- this->x = (coord_t)round( (double)center.x + c * dx - s * dy );
- this->y = (coord_t)round( (double)center.y + c * dy + s * dx );
+ double cur_x = (double)(*this)(0);
+ double cur_y = (double)(*this)(1);
+ double s = ::sin(angle);
+ double c = ::cos(angle);
+ (*this)(0) = (coord_t)round(c * cur_x - s * cur_y);
+ (*this)(1) = (coord_t)round(c * cur_y + s * cur_x);
}
-bool
-Point::coincides_with_epsilon(const Point &point) const
+void Point::rotate(double angle, const Point &center)
{
- return std::abs(this->x - point.x) < SCALED_EPSILON && std::abs(this->y - point.y) < SCALED_EPSILON;
+ double cur_x = (double)(*this)(0);
+ double cur_y = (double)(*this)(1);
+ double s = ::sin(angle);
+ double c = ::cos(angle);
+ double dx = cur_x - (double)center(0);
+ double dy = cur_y - (double)center(1);
+ (*this)(0) = (coord_t)round( (double)center(0) + c * dx - s * dy );
+ (*this)(1) = (coord_t)round( (double)center(1) + c * dy + s * dx );
}
-int
-Point::nearest_point_index(const Points &points) const
+int Point::nearest_point_index(const Points &points) const
{
PointConstPtrs p;
p.reserve(points.size());
@@ -96,12 +83,12 @@ int Point::nearest_point_index(const PointConstPtrs &points) const
for (PointConstPtrs::const_iterator it = points.begin(); it != points.end(); ++it) {
/* If the X distance of the candidate is > than the total distance of the
best previous candidate, we know we don't want it */
- double d = sqr<double>(this->x - (*it)->x);
+ double d = sqr<double>((*this)(0) - (*it)->x());
if (distance != -1 && d > distance) continue;
/* If the Y distance of the candidate is > than the total distance of the
best previous candidate, we know we don't want it */
- d += sqr<double>(this->y - (*it)->y);
+ d += sqr<double>((*this)(1) - (*it)->y());
if (distance != -1 && d > distance) continue;
idx = it - points.begin();
@@ -113,8 +100,7 @@ int Point::nearest_point_index(const PointConstPtrs &points) const
return idx;
}
-int
-Point::nearest_point_index(const PointPtrs &points) const
+int Point::nearest_point_index(const PointPtrs &points) const
{
PointConstPtrs p;
p.reserve(points.size());
@@ -123,8 +109,7 @@ Point::nearest_point_index(const PointPtrs &points) const
return this->nearest_point_index(p);
}
-bool
-Point::nearest_point(const Points &points, Point* point) const
+bool Point::nearest_point(const Points &points, Point* point) const
{
int idx = this->nearest_point_index(points);
if (idx == -1) return false;
@@ -132,40 +117,6 @@ Point::nearest_point(const Points &points, Point* point) const
return true;
}
-/* distance to the closest point of line */
-double
-Point::distance_to(const Line &line) const
-{
- const double dx = line.b.x - line.a.x;
- const double dy = line.b.y - line.a.y;
-
- const double l2 = dx*dx + dy*dy; // avoid a sqrt
- if (l2 == 0.0) return this->distance_to(line.a); // line.a == line.b case
-
- // Consider the line extending the segment, parameterized as line.a + t (line.b - line.a).
- // We find projection of this point onto the line.
- // It falls where t = [(this-line.a) . (line.b-line.a)] / |line.b-line.a|^2
- const double t = ((this->x - line.a.x) * dx + (this->y - line.a.y) * dy) / l2;
- if (t < 0.0) return this->distance_to(line.a); // beyond the 'a' end of the segment
- else if (t > 1.0) return this->distance_to(line.b); // beyond the 'b' end of the segment
- Point projection(
- line.a.x + t * dx,
- line.a.y + t * dy
- );
- return this->distance_to(projection);
-}
-
-double
-Point::perp_distance_to(const Line &line) const
-{
- if (line.a.coincides_with(line.b)) return this->distance_to(line.a);
-
- double n = (double)(line.b.x - line.a.x) * (double)(line.a.y - this->y)
- - (double)(line.a.x - this->x) * (double)(line.b.y - line.a.y);
-
- return std::abs(n) / line.length();
-}
-
/* Three points are a counter-clockwise turn if ccw > 0, clockwise if
* ccw < 0, and collinear if ccw = 0 because ccw is a determinant that
* gives the signed area of the triangle formed by p1, p2 and this point.
@@ -173,51 +124,46 @@ Point::perp_distance_to(const Line &line) const
* z-component of their 3D cross product.
* We return double because it must be big enough to hold 2*max(|coordinate|)^2
*/
-double
-Point::ccw(const Point &p1, const Point &p2) const
+double Point::ccw(const Point &p1, const Point &p2) const
{
- return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x);
+ return (double)(p2(0) - p1(0))*(double)((*this)(1) - p1(1)) - (double)(p2(1) - p1(1))*(double)((*this)(0) - p1(0));
}
-double
-Point::ccw(const Line &line) const
+double Point::ccw(const Line &line) const
{
return this->ccw(line.a, line.b);
}
// returns the CCW angle between this-p1 and this-p2
// i.e. this assumes a CCW rotation from p1 to p2 around this
-double
-Point::ccw_angle(const Point &p1, const Point &p2) const
+double Point::ccw_angle(const Point &p1, const Point &p2) const
{
- double angle = atan2(p1.x - this->x, p1.y - this->y)
- - atan2(p2.x - this->x, p2.y - this->y);
+ double angle = atan2(p1(0) - (*this)(0), p1(1) - (*this)(1))
+ - atan2(p2(0) - (*this)(0), p2(1) - (*this)(1));
// we only want to return only positive angles
return angle <= 0 ? angle + 2*PI : angle;
}
-Point
-Point::projection_onto(const MultiPoint &poly) const
+Point Point::projection_onto(const MultiPoint &poly) const
{
Point running_projection = poly.first_point();
- double running_min = this->distance_to(running_projection);
+ double running_min = (running_projection - *this).cast<double>().norm();
Lines lines = poly.lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
Point point_temp = this->projection_onto(*line);
- if (this->distance_to(point_temp) < running_min) {
+ if ((point_temp - *this).cast<double>().norm() < running_min) {
running_projection = point_temp;
- running_min = this->distance_to(running_projection);
+ running_min = (running_projection - *this).cast<double>().norm();
}
}
return running_projection;
}
-Point
-Point::projection_onto(const Line &line) const
+Point Point::projection_onto(const Line &line) const
{
- if (line.a.coincides_with(line.b)) return line.a;
+ if (line.a == line.b) return line.a;
/*
(Ported from VisiLibity by Karl J. Obermeyer)
@@ -228,151 +174,37 @@ Point::projection_onto(const Line &line) const
If theta is outside the interval [0,1], then one of the Line_Segment's endpoints
must be closest to calling Point.
*/
- double lx = (double)(line.b.x - line.a.x);
- double ly = (double)(line.b.y - line.a.y);
- double theta = ( (double)(line.b.x - this->x)*lx + (double)(line.b.y- this->y)*ly )
+ double lx = (double)(line.b(0) - line.a(0));
+ double ly = (double)(line.b(1) - line.a(1));
+ double theta = ( (double)(line.b(0) - (*this)(0))*lx + (double)(line.b(1)- (*this)(1))*ly )
/ ( sqr<double>(lx) + sqr<double>(ly) );
if (0.0 <= theta && theta <= 1.0)
- return theta * line.a + (1.0-theta) * line.b;
+ return (theta * line.a.cast<coordf_t>() + (1.0-theta) * line.b.cast<coordf_t>()).cast<coord_t>();
// Else pick closest endpoint.
- if (this->distance_to(line.a) < this->distance_to(line.b)) {
- return line.a;
- } else {
- return line.b;
- }
-}
-
-Point
-Point::negative() const
-{
- return Point(-this->x, -this->y);
-}
-
-Vector
-Point::vector_to(const Point &point) const
-{
- return Vector(point.x - this->x, point.y - this->y);
-}
-
-std::ostream&
-operator<<(std::ostream &stm, const Pointf &pointf)
-{
- return stm << pointf.x << "," << pointf.y;
+ return ((line.a - *this).cast<double>().squaredNorm() < (line.b - *this).cast<double>().squaredNorm()) ? line.a : line.b;
}
-std::string
-Pointf::wkt() const
+std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf)
{
- std::ostringstream ss;
- ss << "POINT(" << this->x << " " << this->y << ")";
- return ss.str();
+ return stm << pointf(0) << "," << pointf(1);
}
-std::string
-Pointf::dump_perl() const
-{
- std::ostringstream ss;
- ss << "[" << this->x << "," << this->y << "]";
- return ss.str();
-}
-
-void
-Pointf::scale(double factor)
-{
- this->x *= factor;
- this->y *= factor;
-}
-
-void
-Pointf::translate(double x, double y)
-{
- this->x += x;
- this->y += y;
-}
-
-void
-Pointf::translate(const Vectorf &vector)
-{
- this->translate(vector.x, vector.y);
-}
-
-void
-Pointf::rotate(double angle)
-{
- double cur_x = this->x;
- double cur_y = this->y;
- double s = sin(angle);
- double c = cos(angle);
- this->x = c * cur_x - s * cur_y;
- this->y = c * cur_y + s * cur_x;
-}
-
-void
-Pointf::rotate(double angle, const Pointf &center)
-{
- double cur_x = this->x;
- double cur_y = this->y;
- double s = sin(angle);
- double c = cos(angle);
- double dx = cur_x - center.x;
- double dy = cur_y - center.y;
- this->x = center.x + c * dx - s * dy;
- this->y = center.y + c * dy + s * dx;
-}
+namespace int128 {
-Pointf
-Pointf::negative() const
+int orient(const Vec2crd &p1, const Vec2crd &p2, const Vec2crd &p3)
{
- return Pointf(-this->x, -this->y);
+ Slic3r::Vector v1(p2 - p1);
+ Slic3r::Vector v2(p3 - p1);
+ return Int128::sign_determinant_2x2_filtered(v1(0), v1(1), v2(0), v2(1));
}
-Vectorf
-Pointf::vector_to(const Pointf &point) const
+int cross(const Vec2crd &v1, const Vec2crd &v2)
{
- return Vectorf(point.x - this->x, point.y - this->y);
+ return Int128::sign_determinant_2x2_filtered(v1(0), v1(1), v2(0), v2(1));
}
-void
-Pointf3::scale(double factor)
-{
- Pointf::scale(factor);
- this->z *= factor;
-}
-
-void
-Pointf3::translate(const Vectorf3 &vector)
-{
- this->translate(vector.x, vector.y, vector.z);
-}
-
-void
-Pointf3::translate(double x, double y, double z)
-{
- Pointf::translate(x, y);
- this->z += z;
-}
-
-double
-Pointf3::distance_to(const Pointf3 &point) const
-{
- double dx = ((double)point.x - this->x);
- double dy = ((double)point.y - this->y);
- double dz = ((double)point.z - this->z);
- return sqrt(dx*dx + dy*dy + dz*dz);
-}
-
-Pointf3
-Pointf3::negative() const
-{
- return Pointf3(-this->x, -this->y, -this->z);
-}
-
-Vectorf3
-Pointf3::vector_to(const Pointf3 &point) const
-{
- return Vectorf3(point.x - this->x, point.y - this->y, point.z - this->z);
}
}
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index 6c9096a3d..c1aff561f 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -2,89 +2,131 @@
#define slic3r_Point_hpp_
#include "libslic3r.h"
+#include <cstddef>
#include <vector>
-#include <math.h>
+#include <cmath>
#include <string>
#include <sstream>
#include <unordered_map>
+#include <Eigen/Geometry>
+
namespace Slic3r {
class Line;
-class Linef;
class MultiPoint;
class Point;
-class Point3;
-class Pointf;
-class Pointf3;
typedef Point Vector;
-typedef Point3 Vector3;
-typedef Pointf Vectorf;
-typedef Pointf3 Vectorf3;
-typedef std::vector<Point> Points;
-typedef std::vector<Point*> PointPtrs;
-typedef std::vector<const Point*> PointConstPtrs;
-typedef std::vector<Point3> Points3;
-typedef std::vector<Pointf> Pointfs;
-typedef std::vector<Pointf3> Pointf3s;
-class Point
+// Eigen types, to replace the Slic3r's own types in the future.
+// Vector types with a fixed point coordinate base type.
+typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
+typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
+typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
+typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
+
+// Vector types with a double coordinate base type.
+typedef Eigen::Matrix<float, 2, 1, Eigen::DontAlign> Vec2f;
+typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f;
+typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vec2d;
+typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
+
+typedef std::vector<Point> Points;
+typedef std::vector<Point*> PointPtrs;
+typedef std::vector<const Point*> PointConstPtrs;
+typedef std::vector<Vec3crd> Points3;
+typedef std::vector<Vec2d> Pointfs;
+typedef std::vector<Vec3d> Pointf3s;
+
+typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f;
+typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d;
+typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f;
+typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d;
+
+inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
+
+inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
+inline coord_t cross2(const Vec2crd &v1, const Vec2crd &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
+inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
+inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
+
+inline Vec2crd to_2d(const Vec3crd &pt3) { return Vec2crd(pt3(0), pt3(1)); }
+inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); }
+inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); }
+inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); }
+
+inline Vec2d unscale(coord_t x, coord_t y) { return Vec2d(unscale<double>(x), unscale<double>(y)); }
+inline Vec2d unscale(const Vec2crd &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }
+inline Vec2d unscale(const Vec2d &pt) { return Vec2d(unscale<double>(pt(0)), unscale<double>(pt(1))); }
+inline Vec3d unscale(coord_t x, coord_t y, coord_t z) { return Vec3d(unscale<double>(x), unscale<double>(y), unscale<double>(z)); }
+inline Vec3d unscale(const Vec3crd &pt) { return Vec3d(unscale<double>(pt(0)), unscale<double>(pt(1)), unscale<double>(pt(2))); }
+inline Vec3d unscale(const Vec3d &pt) { return Vec3d(unscale<double>(pt(0)), unscale<double>(pt(1)), unscale<double>(pt(2))); }
+
+inline std::string to_string(const Vec2crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; }
+inline std::string to_string(const Vec2d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + "]"; }
+inline std::string to_string(const Vec3crd &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; }
+inline std::string to_string(const Vec3d &pt) { return std::string("[") + std::to_string(pt(0)) + ", " + std::to_string(pt(1)) + ", " + std::to_string(pt(2)) + "]"; }
+
+std::vector<Vec3f> transform(const std::vector<Vec3f>& points, const Transform3f& t);
+Pointf3s transform(const Pointf3s& points, const Transform3d& t);
+
+class Point : public Vec2crd
{
public:
typedef coord_t coord_type;
- coord_t x;
- coord_t y;
- Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {};
- Point(int64_t _x, int64_t _y): x(coord_t(_x)), y(coord_t(_y)) {}; // for Clipper
- Point(double x, double y);
+
+ Point() : Vec2crd() { (*this)(0) = 0; (*this)(1) = 0; }
+ Point(coord_t x, coord_t y) { (*this)(0) = x; (*this)(1) = y; }
+ Point(int64_t x, int64_t y) { (*this)(0) = coord_t(x); (*this)(1) = coord_t(y); } // for Clipper
+ Point(double x, double y) { (*this)(0) = coord_t(lrint(x)); (*this)(1) = coord_t(lrint(y)); }
+ Point(const Point &rhs) { *this = rhs; }
+ // This constructor allows you to construct Point from Eigen expressions
+ template<typename OtherDerived>
+ Point(const Eigen::MatrixBase<OtherDerived> &other) : Vec2crd(other) {}
static Point new_scale(coordf_t x, coordf_t y) { return Point(coord_t(scale_(x)), coord_t(scale_(y))); }
- bool operator==(const Point& rhs) const { return this->x == rhs.x && this->y == rhs.y; }
- bool operator!=(const Point& rhs) const { return ! (*this == rhs); }
- bool operator<(const Point& rhs) const { return this->x < rhs.x || (this->x == rhs.x && this->y < rhs.y); }
+ // This method allows you to assign Eigen expressions to MyVectorType
+ template<typename OtherDerived>
+ Point& operator=(const Eigen::MatrixBase<OtherDerived> &other)
+ {
+ this->Vec2crd::operator=(other);
+ return *this;
+ }
- Point& operator+=(const Point& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; }
- Point& operator-=(const Point& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; }
- Point& operator*=(const coord_t& rhs) { this->x *= rhs; this->y *= rhs; return *this; }
+ bool operator< (const Point& rhs) const { return (*this)(0) < rhs(0) || ((*this)(0) == rhs(0) && (*this)(1) < rhs(1)); }
- std::string wkt() const;
- std::string dump_perl() const;
- void scale(double factor);
- void translate(double x, double y);
- void translate(const Vector &vector);
- void rotate(double angle);
- void rotate(double angle, const Point &center);
- Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; }
- Point rotated(double angle, const Point &center) const { Point res(*this); res.rotate(angle, center); return res; }
- bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; }
- bool coincides_with_epsilon(const Point &point) const;
- int nearest_point_index(const Points &points) const;
- int nearest_point_index(const PointConstPtrs &points) const;
- int nearest_point_index(const PointPtrs &points) const;
- bool nearest_point(const Points &points, Point* point) const;
- double distance_to(const Point &point) const { return sqrt(distance_to_sq(point)); }
- double distance_to_sq(const Point &point) const { double dx = double(point.x - this->x); double dy = double(point.y - this->y); return dx*dx + dy*dy; }
- double distance_to(const Line &line) const;
- double perp_distance_to(const Line &line) const;
+ Point& operator+=(const Point& rhs) { (*this)(0) += rhs(0); (*this)(1) += rhs(1); return *this; }
+ Point& operator-=(const Point& rhs) { (*this)(0) -= rhs(0); (*this)(1) -= rhs(1); return *this; }
+ Point& operator*=(const double &rhs) { (*this)(0) *= rhs; (*this)(1) *= rhs; return *this; }
+
+ void rotate(double angle);
+ void rotate(double angle, const Point &center);
+ Point rotated(double angle) const { Point res(*this); res.rotate(angle); return res; }
+ Point rotated(double angle, const Point &center) const { Point res(*this); res.rotate(angle, center); return res; }
+ int nearest_point_index(const Points &points) const;
+ int nearest_point_index(const PointConstPtrs &points) const;
+ int nearest_point_index(const PointPtrs &points) const;
+ bool nearest_point(const Points &points, Point* point) const;
double ccw(const Point &p1, const Point &p2) const;
double ccw(const Line &line) const;
double ccw_angle(const Point &p1, const Point &p2) const;
- Point projection_onto(const MultiPoint &poly) const;
- Point projection_onto(const Line &line) const;
- Point negative() const;
- Vector vector_to(const Point &point) const;
+ Point projection_onto(const MultiPoint &poly) const;
+ Point projection_onto(const Line &line) const;
};
-inline Point operator+(const Point& point1, const Point& point2) { return Point(point1.x + point2.x, point1.y + point2.y); }
-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); }
-inline int64_t cross(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.y) - int64_t(v1.y) * int64_t(v2.x); }
-inline int64_t dot(const Point &v1, const Point &v2) { return int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); }
+namespace int128 {
+ // Exact orientation predicate,
+ // returns +1: CCW, 0: collinear, -1: CW.
+ int orient(const Vec2crd &p1, const Vec2crd &p2, const Vec2crd &p3);
+ // Exact orientation predicate,
+ // returns +1: CCW, 0: collinear, -1: CW.
+ int cross(const Vec2crd &v1, const Vec2crd &v2);
+}
// To be used by std::unordered_map, std::unordered_multimap and friends.
struct PointHash {
- size_t operator()(const Point &pt) const {
- return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y);
+ size_t operator()(const Vec2crd &pt) const {
+ return std::hash<coord_t>()(pt(0)) ^ std::hash<coord_t>()(pt(1));
}
};
@@ -128,36 +170,36 @@ public:
}
void insert(const ValueType &value) {
- const Point *pt = m_point_accessor(value);
+ const Vec2crd *pt = m_point_accessor(value);
if (pt != nullptr)
- m_map.emplace(std::make_pair(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2), value));
+ m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), value));
}
void insert(ValueType &&value) {
- const Point *pt = m_point_accessor(value);
+ const Vec2crd *pt = m_point_accessor(value);
if (pt != nullptr)
- m_map.emplace(std::make_pair(Point(pt->x>>m_grid_log2, pt->y>>m_grid_log2), std::move(value)));
+ m_map.emplace(std::make_pair(Vec2crd(pt->x()>>m_grid_log2, pt->y()>>m_grid_log2), std::move(value)));
}
// Return a pair of <ValueType*, distance_squared>
- std::pair<const ValueType*, double> find(const Point &pt) {
+ std::pair<const ValueType*, double> find(const Vec2crd &pt) {
// Iterate over 4 closest grid cells around pt,
// find the closest start point inside these cells to pt.
const ValueType *value_min = nullptr;
double dist_min = std::numeric_limits<double>::max();
// Round pt to a closest grid_cell corner.
- Point grid_corner((pt.x+(m_grid_resolution>>1))>>m_grid_log2, (pt.y+(m_grid_resolution>>1))>>m_grid_log2);
+ Vec2crd grid_corner((pt(0)+(m_grid_resolution>>1))>>m_grid_log2, (pt(1)+(m_grid_resolution>>1))>>m_grid_log2);
// For four neighbors of grid_corner:
for (coord_t neighbor_y = -1; neighbor_y < 1; ++ neighbor_y) {
for (coord_t neighbor_x = -1; neighbor_x < 1; ++ neighbor_x) {
// Range of fragment starts around grid_corner, close to pt.
- auto range = m_map.equal_range(Point(grid_corner.x + neighbor_x, grid_corner.y + neighbor_y));
+ auto range = m_map.equal_range(Vec2crd(grid_corner(0) + neighbor_x, grid_corner(1) + neighbor_y));
// Find the map entry closest to pt.
for (auto it = range.first; it != range.second; ++it) {
const ValueType &value = it->second;
- const Point *pt2 = m_point_accessor(value);
+ const Vec2crd *pt2 = m_point_accessor(value);
if (pt2 != nullptr) {
- const double d2 = pt.distance_to_sq(*pt2);
+ const double d2 = (pt - *pt2).squaredNorm();
if (d2 < dist_min) {
dist_min = d2;
value_min = &value;
@@ -172,7 +214,7 @@ public:
}
private:
- typedef typename std::unordered_multimap<Point, ValueType, PointHash> map_type;
+ typedef typename std::unordered_multimap<Vec2crd, ValueType, PointHash> map_type;
PointAccessor m_point_accessor;
map_type m_map;
coord_t m_search_radius;
@@ -180,107 +222,7 @@ private:
coord_t m_grid_log2;
};
-class Point3 : public Point
-{
-public:
- coord_t z;
- explicit Point3(coord_t _x = 0, coord_t _y = 0, coord_t _z = 0): Point(_x, _y), z(_z) {};
- static Point3 new_scale(coordf_t x, coordf_t y, coordf_t z) { return Point3(coord_t(scale_(x)), coord_t(scale_(y)), coord_t(scale_(z))); }
- bool operator==(const Point3 &rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; }
- bool operator!=(const Point3 &rhs) const { return ! (*this == rhs); }
- bool coincides_with(const Point3& rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; }
-private:
- // Hide the following inherited methods:
- bool operator==(const Point &rhs) const;
- bool operator!=(const Point &rhs) const;
-};
-
-std::ostream& operator<<(std::ostream &stm, const Pointf &pointf);
-
-class Pointf
-{
-public:
- typedef coordf_t coord_type;
- coordf_t x;
- coordf_t y;
- explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {};
- static Pointf new_unscale(coord_t x, coord_t y) {
- return Pointf(unscale(x), unscale(y));
- };
- static Pointf new_unscale(const Point &p) {
- return Pointf(unscale(p.x), unscale(p.y));
- };
- std::string wkt() const;
- std::string dump_perl() const;
- void scale(double factor);
- void translate(double x, double y);
- void translate(const Vectorf &vector);
- void rotate(double angle);
- void rotate(double angle, const Pointf &center);
- Pointf negative() const;
- Vectorf vector_to(const Pointf &point) const;
-
- Pointf& operator+=(const Pointf& rhs) { this->x += rhs.x; this->y += rhs.y; return *this; }
- Pointf& operator-=(const Pointf& rhs) { this->x -= rhs.x; this->y -= rhs.y; return *this; }
- Pointf& operator*=(const coordf_t& rhs) { this->x *= rhs; this->y *= rhs; return *this; }
-
- bool operator==(const Pointf &rhs) const { return this->x == rhs.x && this->y == rhs.y; }
- bool operator!=(const Pointf &rhs) const { return ! (*this == rhs); }
- bool operator< (const Pointf& rhs) const { return this->x < rhs.x || (this->x == rhs.x && this->y < rhs.y); }
-};
-
-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 * v2.x + v1.y * v2.y; }
-inline coordf_t dot(const Pointf &v) { return v.x * v.x + v.y * v.y; }
-inline double length(const Vectorf &v) { return sqrt(dot(v)); }
-inline double l2(const Vectorf &v) { return dot(v); }
-
-class Pointf3 : public Pointf
-{
-public:
- coordf_t z;
- explicit Pointf3(coordf_t _x = 0, coordf_t _y = 0, coordf_t _z = 0): Pointf(_x, _y), z(_z) {};
- static Pointf3 new_unscale(coord_t x, coord_t y, coord_t z) {
- return Pointf3(unscale(x), unscale(y), unscale(z));
- };
- static Pointf3 new_unscale(const Point3& p) { return Pointf3(unscale(p.x), unscale(p.y), unscale(p.z)); }
- void scale(double factor);
- void translate(const Vectorf3 &vector);
- void translate(double x, double y, double z);
- double distance_to(const Pointf3 &point) const;
- Pointf3 negative() const;
- Vectorf3 vector_to(const Pointf3 &point) const;
-
- bool operator==(const Pointf3 &rhs) const { return this->x == rhs.x && this->y == rhs.y && this->z == rhs.z; }
- bool operator!=(const Pointf3 &rhs) const { return ! (*this == rhs); }
-
-private:
- // Hide the following inherited methods:
- bool operator==(const Pointf &rhs) const;
- bool operator!=(const Pointf &rhs) const;
-};
-
-inline Pointf3 operator+(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x + p2.x, p1.y + p2.y, p1.z + p2.z); }
-inline Pointf3 operator-(const Pointf3& p1, const Pointf3& p2) { return Pointf3(p1.x - p2.x, p1.y - p2.y, p1.z - p2.z); }
-inline Pointf3 operator-(const Pointf3& p) { return Pointf3(-p.x, -p.y, -p.z); }
-inline Pointf3 operator*(double scalar, const Pointf3& p) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); }
-inline Pointf3 operator*(const Pointf3& p, double scalar) { return Pointf3(scalar * p.x, scalar * p.y, scalar * p.z); }
-inline Pointf3 cross(const Pointf3& v1, const Pointf3& v2) { return Pointf3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); }
-inline coordf_t dot(const Pointf3& v1, const Pointf3& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }
-inline Pointf3 normalize(const Pointf3& v)
-{
- coordf_t len = ::sqrt(sqr(v.x) + sqr(v.y) + sqr(v.z));
- return (len != 0.0) ? 1.0 / len * v : Pointf3(0.0, 0.0, 0.0);
-}
-
-template<typename TO> inline TO convert_to(const Point &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); }
-template<typename TO> inline TO convert_to(const Pointf &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y)); }
-template<typename TO> inline TO convert_to(const Point3 &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y), typename TO::coord_type(src.z)); }
-template<typename TO> inline TO convert_to(const Pointf3 &src) { return TO(typename TO::coord_type(src.x), typename TO::coord_type(src.y), typename TO::coord_type(src.z)); }
+std::ostream& operator<<(std::ostream &stm, const Vec2d &pointf);
} // namespace Slic3r
@@ -296,7 +238,7 @@ namespace boost { namespace polygon {
typedef coord_t coordinate_type;
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
- return (orient == HORIZONTAL) ? point.x : point.y;
+ return (orient == HORIZONTAL) ? (coordinate_type)point(0) : (coordinate_type)point(1);
}
};
@@ -305,14 +247,14 @@ namespace boost { namespace polygon {
typedef coord_t coordinate_type;
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
if (orient == HORIZONTAL)
- point.x = value;
+ point(0) = value;
else
- point.y = value;
+ point(1) = value;
}
static inline Slic3r::Point construct(coord_t x_value, coord_t y_value) {
Slic3r::Point retval;
- retval.x = x_value;
- retval.y = y_value;
+ retval(0) = x_value;
+ retval(1) = y_value;
return retval;
}
};
diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp
index 27f9a2ca1..14248d84f 100644
--- a/xs/src/libslic3r/Polygon.cpp
+++ b/xs/src/libslic3r/Polygon.cpp
@@ -44,11 +44,9 @@ Polyline
Polygon::split_at_vertex(const Point &point) const
{
// find index of point
- for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) {
- if (it->coincides_with(point)) {
- return this->split_at_index(it - this->points.begin());
- }
- }
+ for (const Point &pt : this->points)
+ if (pt == point)
+ return this->split_at_index(&pt - &this->points.front());
CONFESS("Point not found");
return Polyline();
}
@@ -88,7 +86,7 @@ int64_t Polygon::area2x() const
int64_t a = 0;
for (size_t i = 0, j = n - 1; i < n; ++i)
- a += int64_t(poly[j].x + poly[i].x) * int64_t(poly[j].y - poly[i].y);
+ a += int64_t(poly[j](0) + poly[i](0)) * int64_t(poly[j](1) - poly[i](1));
j = i;
}
return -a * 0.5;
@@ -103,7 +101,7 @@ double Polygon::area() const
double a = 0.;
for (size_t i = 0, j = n - 1; i < n; ++i) {
- a += double(points[j].x + points[i].x) * double(points[i].y - points[j].y);
+ a += ((double)points[j](0) + (double)points[i](0)) * ((double)points[i](1) - (double)points[j](1));
j = i;
}
return 0.5 * a;
@@ -157,17 +155,17 @@ Polygon::contains(const Point &point) const
Points::const_iterator i = this->points.begin();
Points::const_iterator j = this->points.end() - 1;
for (; i != this->points.end(); j = i++) {
- //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point.y well.
- // Does the ray with y == point.y intersect this line segment?
+ //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
+ // Does the ray with y == point(1) intersect this line segment?
#if 1
- if ( ((i->y > point.y) != (j->y > point.y))
- && ((double)point.x < (double)(j->x - i->x) * (double)(point.y - i->y) / (double)(j->y - i->y) + (double)i->x) )
+ if ( (((*i)(1) > point(1)) != ((*j)(1) > point(1)))
+ && ((double)point(0) < (double)((*j)(0) - (*i)(0)) * (double)(point(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)) + (double)(*i)(0)) )
result = !result;
#else
- if ((i->y > point.y) != (j->y > point.y)) {
+ if (((*i)(1) > point(1)) != ((*j)(1) > point(1))) {
// Orientation predicated relative to i-th point.
- double orient = (double)(point.x - i->x) * (double)(j->y - i->y) - (double)(point.y - i->y) * (double)(j->x - i->x);
- if ((i->y > j->y) ? (orient > 0.) : (orient < 0.))
+ double orient = (double)(point(0) - (*i)(0)) * (double)((*j)(1) - (*i)(1)) - (double)(point(1) - (*i)(1)) * (double)((*j)(0) - (*i)(0));
+ if (((*i)(1) > (*j)(1)) ? (orient > 0.) : (orient < 0.))
result = !result;
}
#endif
@@ -225,26 +223,13 @@ Polygon::centroid() const
Polyline polyline = this->split_at_first_point();
for (Points::const_iterator point = polyline.points.begin(); point != polyline.points.end() - 1; ++point) {
- x_temp += (double)( point->x + (point+1)->x ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
- y_temp += (double)( point->y + (point+1)->y ) * ( (double)point->x*(point+1)->y - (double)(point+1)->x*point->y );
+ x_temp += (double)( point->x() + (point+1)->x() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() );
+ y_temp += (double)( point->y() + (point+1)->y() ) * ( (double)point->x()*(point+1)->y() - (double)(point+1)->x()*point->y() );
}
return Point(x_temp/(6*area_temp), y_temp/(6*area_temp));
}
-std::string
-Polygon::wkt() const
-{
- std::ostringstream wkt;
- wkt << "POLYGON((";
- for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) {
- wkt << p->x << " " << p->y;
- if (p != this->points.end()-1) wkt << ",";
- }
- wkt << "))";
- return wkt.str();
-}
-
// find all concave vertices (i.e. having an internal angle greater than the supplied angle)
// (external = right side, thus we consider ccw orientation)
Points
@@ -302,24 +287,24 @@ Point Polygon::point_projection(const Point &point) const
for (size_t i = 0; i < this->points.size(); ++ i) {
const Point &pt0 = this->points[i];
const Point &pt1 = this->points[(i + 1 == this->points.size()) ? 0 : i + 1];
- double d = pt0.distance_to(point);
+ double d = (point - pt0).cast<double>().norm();
if (d < dmin) {
dmin = d;
proj = pt0;
}
- d = pt1.distance_to(point);
+ d = (point - pt1).cast<double>().norm();
if (d < dmin) {
dmin = d;
proj = pt1;
}
- Pointf v1(coordf_t(pt1.x - pt0.x), coordf_t(pt1.y - pt0.y));
- coordf_t div = dot(v1);
+ Vec2d v1(coordf_t(pt1(0) - pt0(0)), coordf_t(pt1(1) - pt0(1)));
+ coordf_t div = v1.squaredNorm();
if (div > 0.) {
- Pointf v2(coordf_t(point.x - pt0.x), coordf_t(point.y - pt0.y));
- coordf_t t = dot(v1, v2) / div;
+ Vec2d v2(coordf_t(point(0) - pt0(0)), coordf_t(point(1) - pt0(1)));
+ coordf_t t = v1.dot(v2) / div;
if (t > 0. && t < 1.) {
- Point foot(coord_t(floor(coordf_t(pt0.x) + t * v1.x + 0.5)), coord_t(floor(coordf_t(pt0.y) + t * v1.y + 0.5)));
- d = foot.distance_to(point);
+ Point foot(coord_t(floor(coordf_t(pt0(0)) + t * v1(0) + 0.5)), coord_t(floor(coordf_t(pt0(1)) + t * v1(1) + 0.5)));
+ d = (point - foot).cast<double>().norm();
if (d < dmin) {
dmin = d;
proj = foot;
@@ -376,12 +361,12 @@ static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3)
{
Point v1 = p2 - p1;
Point v2 = p3 - p2;
- int64_t dir = int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y);
+ int64_t dir = int64_t(v1(0)) * int64_t(v2(0)) + int64_t(v1(1)) * int64_t(v2(1));
if (dir > 0)
// p3 does not turn back to p1. Do not remove p2.
return false;
- double l2_1 = double(v1.x) * double(v1.x) + double(v1.y) * double(v1.y);
- double l2_2 = double(v2.x) * double(v2.x) + double(v2.y) * double(v2.y);
+ double l2_1 = double(v1(0)) * double(v1(0)) + double(v1(1)) * double(v1(1));
+ double l2_2 = double(v2(0)) * double(v2(0)) + double(v2(1)) * double(v2(1));
if (dir == 0)
// p1, p2, p3 may make a perpendicular corner, or there is a zero edge length.
// Remove p2 if it is coincident with p1 or p2.
@@ -389,7 +374,7 @@ static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3)
// p3 turns back to p1 after p2. Are p1, p2, p3 collinear?
// Calculate distance from p3 to a segment (p1, p2) or from p1 to a segment(p2, p3),
// whichever segment is longer
- double cross = double(v1.x) * double(v2.y) - double(v2.x) * double(v1.y);
+ double cross = double(v1(0)) * double(v2(1)) - double(v2(0)) * double(v1(1));
double dist2 = cross * cross / std::max(l2_1, l2_2);
return dist2 < EPSILON * EPSILON;
}
diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp
index 1a02d78b7..54909352c 100644
--- a/xs/src/libslic3r/Polygon.hpp
+++ b/xs/src/libslic3r/Polygon.hpp
@@ -24,11 +24,12 @@ public:
explicit Polygon(const Points &points): MultiPoint(points) {}
Polygon(const Polygon &other) : MultiPoint(other.points) {}
Polygon(Polygon &&other) : MultiPoint(std::move(other.points)) {}
- static Polygon new_scale(std::vector<Pointf> points) {
- Points int_points;
- for (auto pt : points)
- int_points.push_back(Point::new_scale(pt.x, pt.y));
- return Polygon(int_points);
+ static Polygon new_scale(const std::vector<Vec2d> &points) {
+ Polygon pgn;
+ pgn.points.reserve(points.size());
+ for (const Vec2d &pt : points)
+ pgn.points.emplace_back(Point::new_scale(pt(0), pt(1)));
+ return pgn;
}
Polygon& operator=(const Polygon &other) { points = other.points; return *this; }
Polygon& operator=(Polygon &&other) { points = std::move(other.points); return *this; }
@@ -54,7 +55,6 @@ public:
void simplify(double tolerance, Polygons &polygons) const;
void triangulate_convex(Polygons* polygons) const;
Point centroid() const;
- std::string wkt() const;
Points concave_points(double angle = PI) const;
Points convex_points(double angle = PI) const;
// Projection of a point onto the polygon.
diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp
index 3432506c6..b2e50bca3 100644
--- a/xs/src/libslic3r/Polyline.cpp
+++ b/xs/src/libslic3r/Polyline.cpp
@@ -33,7 +33,7 @@ Polyline::leftmost_point() const
{
Point p = this->points.front();
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
- if (it->x < p.x) p = *it;
+ if ((*it)(0) < p(0)) p = *it;
}
return p;
}
@@ -52,92 +52,82 @@ Polyline::lines() const
}
// removes the given distance from the end of the polyline
-void
-Polyline::clip_end(double distance)
+void Polyline::clip_end(double distance)
{
while (distance > 0) {
- Point last_point = this->last_point();
+ Vec2d last_point = this->last_point().cast<double>();
this->points.pop_back();
- if (this->points.empty()) break;
-
- double last_segment_length = last_point.distance_to(this->last_point());
- if (last_segment_length <= distance) {
- distance -= last_segment_length;
- continue;
+ if (this->points.empty())
+ break;
+ Vec2d v = this->last_point().cast<double>() - last_point;
+ double lsqr = v.squaredNorm();
+ if (lsqr > distance * distance) {
+ this->points.emplace_back((last_point + v * (distance / sqrt(lsqr))).cast<coord_t>());
+ return;
}
-
- Line segment(last_point, this->last_point());
- this->points.push_back(segment.point_at(distance));
- distance = 0;
+ distance -= sqrt(lsqr);
}
}
// removes the given distance from the start of the polyline
-void
-Polyline::clip_start(double distance)
+void Polyline::clip_start(double distance)
{
this->reverse();
this->clip_end(distance);
- if (this->points.size() >= 2) this->reverse();
+ if (this->points.size() >= 2)
+ this->reverse();
}
-void
-Polyline::extend_end(double distance)
+void Polyline::extend_end(double distance)
{
// relocate last point by extending the last segment by the specified length
- Line line(
- this->points.back(),
- *(this->points.end() - 2)
- );
- this->points.back() = line.point_at(-distance);
+ Vec2d v = (this->points.back() - *(this->points.end() - 2)).cast<double>().normalized();
+ this->points.back() += (v * distance).cast<coord_t>();
}
-void
-Polyline::extend_start(double distance)
+void Polyline::extend_start(double distance)
{
// relocate first point by extending the first segment by the specified length
- this->points.front() = Line(this->points.front(), this->points[1]).point_at(-distance);
+ Vec2d v = (this->points.front() - this->points[1]).cast<double>().normalized();
+ this->points.front() += (v * distance).cast<coord_t>();
}
/* this method returns a collection of points picked on the polygon contour
so that they are evenly spaced according to the input distance */
-Points
-Polyline::equally_spaced_points(double distance) const
+Points Polyline::equally_spaced_points(double distance) const
{
Points points;
- points.push_back(this->first_point());
+ points.emplace_back(this->first_point());
double len = 0;
for (Points::const_iterator it = this->points.begin() + 1; it != this->points.end(); ++it) {
- double segment_length = it->distance_to(*(it-1));
+ Vec2d p1 = (it-1)->cast<double>();
+ Vec2d v = it->cast<double>() - p1;
+ double segment_length = v.norm();
len += segment_length;
- if (len < distance) continue;
-
+ if (len < distance)
+ continue;
if (len == distance) {
- points.push_back(*it);
+ points.emplace_back(*it);
len = 0;
continue;
}
-
double take = segment_length - (len - distance); // how much we take of this segment
- Line segment(*(it-1), *it);
- points.push_back(segment.point_at(take));
- --it;
- len = -take;
+ points.emplace_back((p1 + v * (take / v.norm())).cast<coord_t>());
+ -- it;
+ len = - take;
}
return points;
}
-void
-Polyline::simplify(double tolerance)
+void Polyline::simplify(double tolerance)
{
this->points = MultiPoint::_douglas_peucker(this->points, tolerance);
}
/* This method simplifies all *lines* contained in the supplied area */
template <class T>
-void
-Polyline::simplify_by_visibility(const T &area)
+void Polyline::simplify_by_visibility(const T &area)
{
Points &pp = this->points;
@@ -157,30 +147,29 @@ Polyline::simplify_by_visibility(const T &area)
template void Polyline::simplify_by_visibility<ExPolygon>(const ExPolygon &area);
template void Polyline::simplify_by_visibility<ExPolygonCollection>(const ExPolygonCollection &area);
-void
-Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
+void Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
{
if (this->points.empty()) return;
// find the line to split at
size_t line_idx = 0;
Point p = this->first_point();
- double min = point.distance_to(p);
+ double min = (p - point).cast<double>().norm();
Lines lines = this->lines();
for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) {
Point p_tmp = point.projection_onto(*line);
- if (point.distance_to(p_tmp) < min) {
+ if ((p_tmp - point).cast<double>().norm() < min) {
p = p_tmp;
- min = point.distance_to(p);
+ min = (p - point).cast<double>().norm();
line_idx = line - lines.begin();
}
}
// create first half
p1->points.clear();
- for (Lines::const_iterator line = lines.begin(); line != lines.begin() + line_idx + 1; ++line) {
- if (!line->a.coincides_with(p)) p1->points.push_back(line->a);
- }
+ for (Lines::const_iterator line = lines.begin(); line != lines.begin() + line_idx + 1; ++line)
+ if (line->a != p)
+ p1->points.push_back(line->a);
// we add point instead of p because they might differ because of numerical issues
// and caller might want to rely on point belonging to result polylines
p1->points.push_back(point);
@@ -193,8 +182,7 @@ Polyline::split_at(const Point &point, Polyline* p1, Polyline* p2) const
}
}
-bool
-Polyline::is_straight() const
+bool Polyline::is_straight() const
{
/* Check that each segment's direction is equal to the line connecting
first point and last point. (Checking each line against the previous
@@ -208,19 +196,6 @@ Polyline::is_straight() const
return true;
}
-std::string
-Polyline::wkt() const
-{
- std::ostringstream wkt;
- wkt << "LINESTRING((";
- for (Points::const_iterator p = this->points.begin(); p != this->points.end(); ++p) {
- wkt << p->x << " " << p->y;
- if (p != this->points.end()-1) wkt << ",";
- }
- wkt << "))";
- return wkt.str();
-}
-
BoundingBox get_extents(const Polyline &polyline)
{
return polyline.bounding_box();
@@ -254,30 +229,17 @@ bool remove_degenerate(Polylines &polylines)
return modified;
}
-ThickLines
-ThickPolyline::thicklines() const
+ThickLines ThickPolyline::thicklines() const
{
ThickLines lines;
if (this->points.size() >= 2) {
lines.reserve(this->points.size() - 1);
- for (size_t i = 0; i < this->points.size()-1; ++i) {
- ThickLine line(this->points[i], this->points[i+1]);
- line.a_width = this->width[2*i];
- line.b_width = this->width[2*i+1];
- lines.push_back(line);
- }
+ for (size_t i = 0; i + 1 < this->points.size(); ++ i)
+ lines.emplace_back(this->points[i], this->points[i + 1], this->width[2 * i], this->width[2 * i + 1]);
}
return lines;
}
-void
-ThickPolyline::reverse()
-{
- Polyline::reverse();
- std::reverse(this->width.begin(), this->width.end());
- std::swap(this->endpoints.first, this->endpoints.second);
-}
-
Lines3 Polyline3::lines() const
{
Lines3 lines;
diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp
index b64743d84..0c934e074 100644
--- a/xs/src/libslic3r/Polyline.hpp
+++ b/xs/src/libslic3r/Polyline.hpp
@@ -19,14 +19,15 @@ public:
Polyline() {};
Polyline(const Polyline &other) : MultiPoint(other.points) {}
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
+ Polyline(std::initializer_list<Point> list) : MultiPoint(list) {}
+ explicit Polyline(const Point &p1, const Point &p2) { points.reserve(2); points.emplace_back(p1); points.emplace_back(p2); }
Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
- static Polyline new_scale(std::vector<Pointf> points) {
+ static Polyline new_scale(const std::vector<Vec2d> &points) {
Polyline pl;
- Points int_points;
- for (auto pt : points)
- int_points.push_back(Point::new_scale(pt.x, pt.y));
- pl.append(int_points);
+ pl.points.reserve(points.size());
+ for (const Vec2d &pt : points)
+ pl.points.emplace_back(Point::new_scale(pt(0), pt(1)));
return pl;
}
@@ -71,7 +72,6 @@ public:
template <class T> void simplify_by_visibility(const T &area);
void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
bool is_straight() const;
- std::string wkt() const;
};
extern BoundingBox get_extents(const Polyline &polyline);
@@ -129,12 +129,17 @@ inline void polylines_append(Polylines &dst, Polylines &&src)
bool remove_degenerate(Polylines &polylines);
class ThickPolyline : public Polyline {
- public:
- std::vector<coordf_t> width;
- std::pair<bool,bool> endpoints;
- ThickPolyline() : endpoints(std::make_pair(false, false)) {};
+public:
+ ThickPolyline() : endpoints(std::make_pair(false, false)) {}
ThickLines thicklines() const;
- void reverse();
+ void reverse() {
+ Polyline::reverse();
+ std::reverse(this->width.begin(), this->width.end());
+ std::swap(this->endpoints.first, this->endpoints.second);
+ }
+
+ std::vector<coordf_t> width;
+ std::pair<bool,bool> endpoints;
};
class Polyline3 : public MultiPoint3
diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp
index ca9c64d23..3f65ea699 100644
--- a/xs/src/libslic3r/PolylineCollection.cpp
+++ b/xs/src/libslic3r/PolylineCollection.cpp
@@ -15,9 +15,9 @@ inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &
T dmin = std::numeric_limits<T>::max();
int idx = 0;
for (std::vector<Chaining>::const_iterator it = pairs.begin(); it != pairs.end(); ++it) {
- T d = sqr(T(start_near.x - it->first.x));
+ T d = sqr(T(start_near(0) - it->first(0)));
if (d <= dmin) {
- d += sqr(T(start_near.y - it->first.y));
+ d += sqr(T(start_near(1) - it->first(1)));
if (d < dmin) {
idx = (it - pairs.begin()) * 2;
dmin = d;
@@ -26,9 +26,9 @@ inline int nearest_point_index(const std::vector<Chaining> &pairs, const Point &
}
}
if (! no_reverse) {
- d = sqr(T(start_near.x - it->last.x));
+ d = sqr(T(start_near(0) - it->last(0)));
if (d <= dmin) {
- d += sqr(T(start_near.y - it->last.y));
+ d += sqr(T(start_near(1) - it->last(1)));
if (d < dmin) {
idx = (it - pairs.begin()) * 2 + 1;
dmin = d;
@@ -82,7 +82,7 @@ Point PolylineCollection::leftmost_point(const Polylines &polylines)
Point p = it->leftmost_point();
for (++ it; it != polylines.end(); ++it) {
Point p2 = it->leftmost_point();
- if (p2.x < p.x)
+ if (p2(0) < p(0))
p = p2;
}
return p;
diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp
index e8a010a79..ae02216df 100644
--- a/xs/src/libslic3r/Print.cpp
+++ b/xs/src/libslic3r/Print.cpp
@@ -4,6 +4,7 @@
#include "Extruder.hpp"
#include "Flow.hpp"
#include "Geometry.hpp"
+#include "I18N.hpp"
#include "SupportMaterial.hpp"
#include "GCode.hpp"
#include "GCode/WipeTowerPrusaMM.hpp"
@@ -13,6 +14,13 @@
#include <boost/lexical_cast.hpp>
#include <boost/log/trivial.hpp>
+//#include "slic3r/ProgressIndicator.hpp"
+#include "PrintExport.hpp"
+
+//! macro used to mark string used at localization,
+//! return same string
+#define L(s) Slic3r::I18N::translate(s)
+
namespace Slic3r {
template class PrintState<PrintStep, psCount>;
@@ -71,6 +79,13 @@ bool Print::reload_model_instances()
return invalidated;
}
+PrintObjectPtrs Print::get_printable_objects() const
+{
+ PrintObjectPtrs printable_objects(m_objects);
+ printable_objects.erase(std::remove_if(printable_objects.begin(), printable_objects.end(), [](PrintObject* o) { return !o->is_printable(); }), printable_objects.end());
+ return printable_objects;
+}
+
PrintRegion* Print::add_region()
{
m_regions.emplace_back(new PrintRegion(this));
@@ -127,7 +142,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"gcode_comments",
"gcode_flavor",
"infill_acceleration",
- "infill_first",
"layer_gcode",
"min_fan_speed",
"max_fan_speed",
@@ -154,6 +168,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"retract_restart_extra",
"retract_restart_extra_toolchange",
"retract_speed",
+ "single_extruder_multi_material_priming",
"slowdown_below_layer_time",
"standby_temperature_delta",
"start_gcode",
@@ -165,7 +180,10 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
"use_relative_e_distances",
"use_volumetric_e",
"variable_layer_height",
- "wipe"
+ "wipe",
+ "wipe_tower_x",
+ "wipe_tower_y",
+ "wipe_tower_rotation_angle"
};
static std::unordered_set<std::string> steps_ignore;
@@ -173,6 +191,7 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
std::vector<PrintStep> steps;
std::vector<PrintObjectStep> osteps;
bool invalidated = false;
+
for (const t_config_option_key &opt_key : opt_keys) {
if (steps_gcode.find(opt_key) != steps_gcode.end()) {
// These options only affect G-code export or they are just notes without influence on the generated G-code,
@@ -199,15 +218,29 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "filament_type"
|| opt_key == "filament_soluble"
|| opt_key == "first_layer_temperature"
+ || opt_key == "filament_loading_speed"
+ || opt_key == "filament_loading_speed_start"
+ || opt_key == "filament_unloading_speed"
+ || opt_key == "filament_unloading_speed_start"
+ || opt_key == "filament_toolchange_delay"
+ || opt_key == "filament_cooling_moves"
+ || opt_key == "filament_minimal_purge_on_wipe_tower"
+ || opt_key == "filament_cooling_initial_speed"
+ || opt_key == "filament_cooling_final_speed"
+ || opt_key == "filament_ramming_parameters"
|| opt_key == "gcode_flavor"
+ || opt_key == "infill_first"
|| opt_key == "single_extruder_multi_material"
|| opt_key == "spiral_vase"
|| opt_key == "temperature"
|| opt_key == "wipe_tower"
- || opt_key == "wipe_tower_x"
- || opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_width"
- || opt_key == "wipe_tower_per_color_wipe"
+ || opt_key == "wipe_tower_bridging"
+ || opt_key == "wiping_volumes_matrix"
+ || opt_key == "parking_pos_retraction"
+ || opt_key == "cooling_tube_retraction"
+ || opt_key == "cooling_tube_length"
+ || opt_key == "extra_loading_move"
|| opt_key == "z_offset") {
steps.emplace_back(psWipeTower);
} else if (
@@ -219,7 +252,6 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
osteps.emplace_back(posSupportMaterial);
steps.emplace_back(psSkirt);
steps.emplace_back(psBrim);
- steps.emplace_back(psWipeTower);
} else {
// for legacy, if we can't handle this option let's invalidate all steps
//FIXME invalidate all steps of all objects as well?
@@ -361,7 +393,7 @@ void Print::add_model_object(ModelObject* model_object, int idx)
}
// If no region exists with the same config, create a new one.
if (region_id == size_t(-1)) {
- region_id = this->regions().size();
+ region_id = m_regions.size();
this->add_region(config);
}
// Assign volume to a region.
@@ -456,7 +488,7 @@ bool Print::apply_config(DynamicPrintConfig config)
const ModelVolume &volume = *object->model_object()->volumes[volume_id];
if (this_region_config_set) {
// If the new config for this volume differs from the other
- // volume configs currently associated to this region, it means
+ // volume configs currently associated to this region, it means
// the region subdivision does not make sense anymore.
if (! this_region_config.equals(this->_region_config_from_model_volume(volume))) {
rearrange_regions = true;
@@ -530,14 +562,20 @@ bool Print::has_skirt() const
std::string Print::validate() const
{
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(m_config.bed_shape.values));
- BoundingBoxf3 print_volume(Pointf3(unscale(bed_box_2D.min.x), unscale(bed_box_2D.min.y), 0.0), Pointf3(unscale(bed_box_2D.max.x), unscale(bed_box_2D.max.y), m_config.max_print_height));
+ BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(m_config.max_print_height)));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
- print_volume.min.z = -1e10;
+ print_volume.min(2) = -1e10;
+ unsigned int printable_count = 0;
for (PrintObject *po : m_objects) {
- if (! print_volume.contains(po->model_object()->tight_bounding_box(false)))
- return "Some objects are outside of the print volume.";
+ po->model_object()->check_instances_print_volume_state(print_volume);
+ po->reload_model_instances();
+ if (po->is_printable())
+ ++printable_count;
}
+ if (printable_count == 0)
+ return L("All objects are outside of the print volume.");
+
if (m_config.complete_objects) {
// Check horizontal clearance.
{
@@ -558,11 +596,11 @@ std::string Print::validate() const
// Grow convex hull with the clearance margin.
convex_hull = offset(convex_hull, scale_(m_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 (const Point &copy : object->_shifted_copies) {
+ for (const Point &copy : object->m_copies) {
Polygon p = convex_hull;
p.translate(copy);
if (! intersection(convex_hulls_other, p).empty())
- return "Some objects are too close; your extruder will collide with them.";
+ return L("Some objects are too close; your extruder will collide with them.");
polygons_append(convex_hulls_other, p);
}
}
@@ -571,13 +609,13 @@ std::string Print::validate() const
{
std::vector<coord_t> object_height;
for (const PrintObject *object : m_objects)
- object_height.insert(object_height.end(), object->copies().size(), object->size.z);
+ object_height.insert(object_height.end(), object->copies().size(), object->size(2));
std::sort(object_height.begin(), object_height.end());
// Ignore the tallest *copy* (this is why we repeat height for all of them):
// it will be printed as last one so its height doesn't matter.
object_height.pop_back();
if (! object_height.empty() && object_height.back() > scale_(m_config.extruder_clearance_height.value))
- return "Some objects are too tall and cannot be printed without extruder collisions.";
+ return L("Some objects are too tall and cannot be printed without extruder collisions.");
}
} // end if (m_config.complete_objects)
@@ -587,40 +625,65 @@ std::string Print::validate() const
total_copies_count += object->copies().size();
// #4043
if (total_copies_count > 1 && ! m_config.complete_objects.value)
- return "The Spiral Vase option can only be used when printing a single object.";
+ return L("The Spiral Vase option can only be used when printing a single object.");
if (m_regions.size() > 1)
- return "The Spiral Vase option can only be used when printing single material objects.";
+ return L("The Spiral Vase option can only be used when printing single material objects.");
+ }
+
+ if (m_config.single_extruder_multi_material) {
+ for (size_t i=1; i<m_config.nozzle_diameter.values.size(); ++i)
+ if (m_config.nozzle_diameter.values[i] != m_config.nozzle_diameter.values[i-1])
+ return L("All extruders must have the same diameter for single extruder multimaterial printer.");
}
if (this->has_wipe_tower() && ! m_objects.empty()) {
- #if 0
- for (auto dmr : m_config.nozzle_diameter.values)
- if (std::abs(dmr - 0.4) > EPSILON)
- return "The Wipe Tower is currently only supported for the 0.4mm nozzle diameter.";
- #endif
if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfMarlin)
- return "The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.";
+ return L("The Wipe Tower is currently only supported for the Marlin and RepRap/Sprinter G-code flavors.");
if (! m_config.use_relative_e_distances)
- return "The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).";
+ return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
SlicingParameters slicing_params0 = m_objects.front()->slicing_parameters();
+
+ const PrintObject* tallest_object = m_objects.front(); // let's find the tallest object
+ for (const auto* object : m_objects)
+ if (*(object->layer_height_profile.end()-2) > *(tallest_object->layer_height_profile.end()-2) )
+ tallest_object = object;
+
for (PrintObject *object : m_objects) {
SlicingParameters slicing_params = object->slicing_parameters();
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
- return "The Wipe Tower is only supported for multiple objects if they have equal layer heigths";
+ return L("The Wipe Tower is only supported for multiple objects if they have equal layer heigths");
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
- return "The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers";
+ return L("The Wipe Tower is only supported for multiple objects if they are printed over an equal number of raft layers");
if (object->config().support_material_contact_distance != m_objects.front()->config().support_material_contact_distance)
- return "The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance";
+ return L("The Wipe Tower is only supported for multiple objects if they are printed with the same support_material_contact_distance");
if (! equal_layering(slicing_params, slicing_params0))
- return "The Wipe Tower is only supported for multiple objects if they are sliced equally.";
+ return L("The Wipe Tower is only supported for multiple objects if they are sliced equally.");
bool was_layer_height_profile_valid = object->layer_height_profile_valid;
object->update_layer_height_profile();
object->layer_height_profile_valid = was_layer_height_profile_valid;
- for (size_t i = 5; i < object->layer_height_profile.size(); i += 2)
- if (object->layer_height_profile[i-1] > slicing_params.object_print_z_min + EPSILON &&
- std::abs(object->layer_height_profile[i] - object->config().layer_height) > EPSILON)
- return "The Wipe Tower is currently only supported with constant Z layer spacing. Layer editing is not allowed.";
+
+ if ( m_config.variable_layer_height ) { // comparing layer height profiles
+ bool failed = false;
+ if (tallest_object->layer_height_profile.size() >= object->layer_height_profile.size() ) {
+ int i = 0;
+ while ( i < object->layer_height_profile.size() && i < tallest_object->layer_height_profile.size()) {
+ if (std::abs(tallest_object->layer_height_profile[i] - object->layer_height_profile[i])) {
+ failed = true;
+ break;
+ }
+ ++i;
+ if (i == object->layer_height_profile.size()-2) // this element contains this objects max z
+ if (tallest_object->layer_height_profile[i] > object->layer_height_profile[i]) // the difference does not matter in this case
+ ++i;
+ }
+ }
+ else
+ failed = true;
+
+ if (failed)
+ return L("The Wipe tower is only supported if all objects have the same layer height profile");
+ }
}
}
@@ -628,13 +691,17 @@ std::string Print::validate() const
// find the smallest nozzle diameter
std::vector<unsigned int> extruders = this->extruders();
if (extruders.empty())
- return "The supplied settings will cause an empty print.";
+ return L("The supplied settings will cause an empty print.");
std::vector<double> nozzle_diameters;
for (unsigned int extruder_id : extruders)
nozzle_diameters.push_back(m_config.nozzle_diameter.get_at(extruder_id));
double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end());
-
+ unsigned int total_extruders_count = m_config.nozzle_diameter.size();
+ for (const auto& extruder_idx : extruders)
+ if ( extruder_idx >= total_extruders_count )
+ return L("One or more object were assigned an extruder that the printer does not have.");
+
for (PrintObject *object : m_objects) {
if ((object->config().support_material_extruder == -1 || object->config().support_material_interface_extruder == -1) &&
(object->config().raft_layers > 0 || object->config().support_material.value)) {
@@ -642,13 +709,13 @@ std::string Print::validate() const
// will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
// are of the same diameter.
if (nozzle_diameters.size() > 1)
- return "Printing with multiple extruders of differing nozzle diameters. "
+ return L("Printing with multiple extruders of differing nozzle diameters. "
"If support is to be printed with the current extruder (support_material_extruder == 0 or support_material_interface_extruder == 0), "
- "all nozzles have to be of the same diameter.";
+ "all nozzles have to be of the same diameter.");
}
// validate first_layer_height
- double first_layer_height = object->config().get_abs_value("first_layer_height");
+ double first_layer_height = object->config().get_abs_value(L("first_layer_height"));
double first_layer_min_nozzle_diameter;
if (object->config().raft_layers > 0) {
// if we have raft layers, only support material extruder is used on first layer
@@ -663,11 +730,11 @@ std::string Print::validate() const
first_layer_min_nozzle_diameter = min_nozzle_diameter;
}
if (first_layer_height > first_layer_min_nozzle_diameter)
- return "First layer height can't be greater than nozzle diameter";
+ return L("First layer height can't be greater than nozzle diameter");
// validate layer_height
if (object->config().layer_height.value > min_nozzle_diameter)
- return "Layer height can't be greater than nozzle diameter";
+ return L("Layer height can't be greater than nozzle diameter");
}
}
@@ -680,9 +747,9 @@ BoundingBox Print::bounding_box() const
{
BoundingBox bb;
for (const PrintObject *object : m_objects)
- for (Point copy : object->_shifted_copies) {
+ for (Point copy : object->m_copies) {
bb.merge(copy);
- copy.translate(object->size);
+ copy += to_2d(object->size);
bb.merge(copy);
}
return bb;
@@ -897,8 +964,9 @@ void Print::_make_skirt()
// prepended to the first 'n' layers (with 'n' = skirt_height).
// $skirt_height_z in this case is the highest possible skirt height for safety.
coordf_t skirt_height_z = 0.;
- for (const PrintObject *object : m_objects) {
- size_t skirt_layers = this->has_infinite_skirt() ?
+ PrintObjectPtrs printable_objects = get_printable_objects();
+ for (const PrintObject *object : printable_objects) {
+ size_t skirt_layers = this->has_infinite_skirt() ?
object->layer_count() :
std::min(size_t(m_config.skirt_height.value), object->layer_count());
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
@@ -906,8 +974,7 @@ void Print::_make_skirt()
// Collect points from all layers contained in skirt height.
Points points;
- for (const PrintObject *object : m_objects) {
- this->throw_if_canceled();
+ for (const PrintObject *object : printable_objects) {
Points object_points;
// Get object layers up to skirt_height_z.
for (const Layer *layer : object->m_layers) {
@@ -925,10 +992,10 @@ void Print::_make_skirt()
append(object_points, extrusion_entity->as_polyline().points);
}
// Repeat points for each object copy.
- for (const Point &shift : object->_shifted_copies) {
+ for (const Point &shift : object->m_copies) {
Points copy_points = object_points;
for (Point &pt : copy_points)
- pt.translate(shift);
+ pt += shift;
append(points, copy_points);
}
}
@@ -967,7 +1034,9 @@ void Print::_make_skirt()
// Initial offset of the brim inner edge from the object (possible with a support & raft).
// The skirt will touch the brim if the brim is extruded.
- coord_t distance = scale_(std::max(m_config.skirt_distance.value, m_config.brim_width.value));
+ Flow brim_flow = this->brim_flow();
+ double actual_brim_width = brim_flow.spacing() * floor(m_config.brim_width.value / brim_flow.spacing());
+ coord_t distance = scale_(std::max(m_config.skirt_distance.value, actual_brim_width) - spacing/2.);
// Draw outlines from outside to inside.
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
@@ -995,7 +1064,7 @@ void Print::_make_skirt()
m_skirt.append(eloop);
if (m_config.min_skirt_length.value > 0) {
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
- extruded_length[extruder_idx] += unscale(loop.length()) * extruders_e_per_mm[extruder_idx];
+ extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
// Not extruded enough yet with the current extruder. Add another loop.
if (i == 1)
@@ -1020,22 +1089,22 @@ void Print::_make_brim()
// Brim is only printed on first layer and uses perimeter extruder.
Flow flow = this->brim_flow();
Polygons islands;
- for (PrintObject *object : m_objects) {
- this->throw_if_canceled();
+ PrintObjectPtrs printable_objects = get_printable_objects();
+ for (PrintObject *object : printable_objects) {
Polygons object_islands;
for (ExPolygon &expoly : object->m_layers.front()->slices.expolygons)
object_islands.push_back(expoly.contour);
if (! object->support_layers().empty())
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
- islands.reserve(islands.size() + object_islands.size() * object->_shifted_copies.size());
- for (const Point &pt : object->_shifted_copies)
+ islands.reserve(islands.size() + object_islands.size() * object->m_copies.size());
+ for (const Point &pt : object->m_copies)
for (Polygon &poly : object_islands) {
islands.push_back(poly);
islands.back().translate(pt);
}
}
Polygons loops;
- size_t num_loops = size_t(floor(m_config.brim_width.value / flow.width));
+ size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing()));
for (size_t i = 0; i < num_loops; ++ i) {
this->throw_if_canceled();
islands = offset(islands, float(flow.scaled_spacing()), jtSquare);
@@ -1066,6 +1135,18 @@ bool Print::has_wipe_tower() const
void Print::_make_wipe_tower()
{
+ m_wipe_tower_data.clear();
+ if (! this->has_wipe_tower())
+ return;
+
+ // Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
+ std::vector<float> wiping_matrix(cast<float>(m_config.wiping_volumes_matrix.values));
+ // Extract purging volumes for each extruder pair:
+ std::vector<std::vector<float>> wipe_volumes;
+ const unsigned int number_of_extruders = (unsigned int)(sqrt(wiping_matrix.size())+EPSILON);
+ for (unsigned int i = 0; i<number_of_extruders; ++i)
+ wipe_volumes.push_back(std::vector<float>(wiping_matrix.begin()+i*number_of_extruders, wiping_matrix.begin()+(i+1)*number_of_extruders));
+
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
if (! m_wipe_tower_data.tool_ordering.has_wipe_tower())
@@ -1081,7 +1162,7 @@ void Print::_make_wipe_tower()
size_t idx_end = m_wipe_tower_data.tool_ordering.layer_tools().size();
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
for (size_t i = 0; i < idx_end; ++ i) {
- const ToolOrdering::LayerTools &lt = m_wipe_tower_data.tool_ordering.layer_tools()[i];
+ const LayerTools &lt = m_wipe_tower_data.tool_ordering.layer_tools()[i];
if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
idx_begin = i;
break;
@@ -1095,7 +1176,7 @@ void Print::_make_wipe_tower()
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
for (size_t i = idx_begin; i < idx_end; ++ i) {
- ToolOrdering::LayerTools &lt = const_cast<ToolOrdering::LayerTools&>(m_wipe_tower_data.tool_ordering.layer_tools()[i]);
+ LayerTools &lt = const_cast<LayerTools&>(m_wipe_tower_data.tool_ordering.layer_tools()[i]);
if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
break;
lt.has_support = true;
@@ -1112,78 +1193,73 @@ void Print::_make_wipe_tower()
// Initialize the wipe tower.
WipeTowerPrusaMM wipe_tower(
float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value),
- float(m_config.wipe_tower_width.value), float(m_config.wipe_tower_per_color_wipe.value),
+ float(m_config.wipe_tower_width.value),
+ float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value),
+ float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value),
+ float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), wipe_volumes,
m_wipe_tower_data.tool_ordering.first_extruder());
-
+
//wipe_tower.set_retract();
//wipe_tower.set_zhop();
// Set the extruder & material properties at the wipe tower object.
- for (size_t i = 0; i < 4; ++ i)
+ for (size_t i = 0; i < number_of_extruders; ++ i)
wipe_tower.set_extruder(
i,
WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()),
m_config.temperature.get_at(i),
- m_config.first_layer_temperature.get_at(i));
-
- // When printing the first layer's wipe tower, the first extruder is expected to be active and primed.
- // Therefore the number of wipe sections at the wipe tower will be (m_wipe_tower_data.tool_ordering.front().extruders-1) at the 1st layer.
- // The following variable is true if the last priming section cannot be squeezed inside the wipe tower.
- bool last_priming_wipe_full = m_wipe_tower_data.tool_ordering.front().extruders.size() > m_wipe_tower_data.tool_ordering.front().wipe_tower_partitions;
+ m_config.first_layer_temperature.get_at(i),
+ m_config.filament_loading_speed.get_at(i),
+ m_config.filament_loading_speed_start.get_at(i),
+ m_config.filament_unloading_speed.get_at(i),
+ m_config.filament_unloading_speed_start.get_at(i),
+ m_config.filament_toolchange_delay.get_at(i),
+ m_config.filament_cooling_moves.get_at(i),
+ m_config.filament_cooling_initial_speed.get_at(i),
+ m_config.filament_cooling_final_speed.get_at(i),
+ m_config.filament_ramming_parameters.get_at(i),
+ m_config.nozzle_diameter.get_at(i));
m_wipe_tower_data.priming = Slic3r::make_unique<WipeTower::ToolChangeResult>(
- wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), ! last_priming_wipe_full, WipeTower::PURPOSE_EXTRUDE));
+ wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
- // Generate the wipe tower layers.
- m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
- // Set current_extruder_id to the last extruder primed.
- unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back();
- for (const ToolOrdering::LayerTools &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) {
- this->throw_if_canceled();
- if (! layer_tools.has_wipe_tower)
- // This is a support only layer, or the wipe tower does not reach to this height.
- continue;
- bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
- bool last_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0;
- wipe_tower.set_layer(
- float(layer_tools.print_z),
- float(layer_tools.wipe_tower_layer_height),
- layer_tools.wipe_tower_partitions,
- first_layer,
- last_layer);
- std::vector<WipeTower::ToolChangeResult> tool_changes;
- for (unsigned int extruder_id : layer_tools.extruders)
- // Call the wipe_tower.tool_change() at the first layer for the initial extruder
- // to extrude the wipe tower brim,
- if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) ||
- // or when an extruder shall be switched.
- extruder_id != current_extruder_id) {
- tool_changes.emplace_back(wipe_tower.tool_change(extruder_id, extruder_id == layer_tools.extruders.back(), WipeTower::PURPOSE_EXTRUDE));
- current_extruder_id = extruder_id;
- }
- if (! wipe_tower.layer_finished()) {
- tool_changes.emplace_back(wipe_tower.finish_layer(WipeTower::PURPOSE_EXTRUDE));
- if (tool_changes.size() > 1) {
- // Merge the two last tool changes into one.
- WipeTower::ToolChangeResult &tc1 = tool_changes[tool_changes.size() - 2];
- WipeTower::ToolChangeResult &tc2 = tool_changes.back();
- if (tc1.end_pos != tc2.start_pos) {
- // Add a travel move from tc1.end_pos to tc2.start_pos.
- char buf[2048];
- sprintf(buf, "G1 X%.3f Y%.3f F7200\n", tc2.start_pos.x, tc2.start_pos.y);
- tc1.gcode += buf;
+ // Lets go through the wipe tower layers and determine pairs of extruder changes for each
+ // to pass to wipe_tower (so that it can use it for planning the layout of the tower)
+ {
+ unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.all_extruders().back();
+ for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
+ if (!layer_tools.has_wipe_tower) continue;
+ bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
+ wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id,false);
+ for (const auto extruder_id : layer_tools.extruders) {
+ if ((first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || extruder_id != current_extruder_id) {
+ float volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
+ // Not all of that can be used for infill purging:
+ volume_to_wipe -= m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+
+ // try to assign some infills/objects for the wiping:
+ volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id, volume_to_wipe);
+
+ // add back the minimal amount toforce on the wipe tower:
+ volume_to_wipe += m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
+
+ // request a toolchange at the wipe tower with at least volume_to_wipe purging amount
+ wipe_tower.plan_toolchange(layer_tools.print_z, layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
+ first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
+ current_extruder_id = extruder_id;
}
- tc1.gcode += tc2.gcode;
- append(tc1.extrusions, tc2.extrusions);
- tc1.end_pos = tc2.end_pos;
- tool_changes.pop_back();
}
+ layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
+ if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
+ break;
}
- m_wipe_tower_data.tool_changes.emplace_back(std::move(tool_changes));
- if (last_layer)
- break;
}
-
+
+ // Generate the wipe tower layers.
+ m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
+ wipe_tower.generate(m_wipe_tower_data.tool_changes);
+ m_wipe_tower_data.depth = wipe_tower.get_depth();
+
// Unload the current filament over the purge tower.
coordf_t layer_height = m_objects.front()->config().layer_height.value;
if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
@@ -1201,7 +1277,7 @@ void Print::_make_wipe_tower()
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
}
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(
- wipe_tower.tool_change((unsigned int)-1, false, WipeTower::PURPOSE_EXTRUDE));
+ wipe_tower.tool_change((unsigned int)-1, false));
}
std::string Print::output_filename() const
@@ -1211,7 +1287,7 @@ std::string Print::output_filename() const
try {
return this->placeholder_parser().process(m_config.output_filename_format.value, 0, &cfg_timestamp);
} catch (std::runtime_error &err) {
- throw std::runtime_error(std::string("Failed processing of the output_filename_format template.\n") + err.what());
+ throw std::runtime_error(L("Failed processing of the output_filename_format template.") + "\n" + err.what());
}
}
@@ -1238,4 +1314,24 @@ std::string Print::output_filepath(const std::string &path) const
return path;
}
+void Print::print_to_png(const std::string &dirpath)
+{
+ print_to<FilePrinterFormat::PNG>(*this,
+ dirpath,
+ float(m_config.bed_size_x.value),
+ float(m_config.bed_size_y.value),
+ int(m_config.pixel_width.value),
+ int(m_config.pixel_height.value),
+ float(m_config.exp_time.value),
+ float(m_config.exp_time_first.value));
+}
+
+// Returns extruder this eec should be printed with, according to PrintRegion config
+int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
+{
+ return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
+ std::max<int>(region.config().perimeter_extruder.value - 1, 0);
}
+
+} // namespace Slic3r
+
diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp
index 71711b31e..d72c1fef5 100644
--- a/xs/src/libslic3r/Print.hpp
+++ b/xs/src/libslic3r/Print.hpp
@@ -158,31 +158,23 @@ class PrintObject
public:
// vector of (vectors of volume ids), indexed by region_id
std::vector<std::vector<int>> region_volumes;
- t_layer_height_ranges layer_height_ranges;
+ 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.
// layer_height_profile must not be set by the background thread.
- std::vector<coordf_t> layer_height_profile;
+ std::vector<coordf_t> layer_height_profile;
// There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject
// is used for interactive editing and for loading / storing into a project file (AMF file as of today).
// This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it.
// This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running.
- bool layer_height_profile_valid;
+ bool layer_height_profile_valid;
// 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
- bool typed_slices;
+ bool typed_slices;
- Point3 size; // XYZ in scaled coordinates
-
- // scaled coordinates to add to copies (to compensate for the alignment
- // operated when creating the object but still preserving a coherent API
- // for external callers)
- Point _copies_shift;
-
- // Slic3r::Point objects in scaled G-code coordinates in our coordinates
- Points _shifted_copies;
+ Vec3crd size; // XYZ in scaled coordinates
Print* print() { return m_print; }
const Print* print() const { return m_print; }
@@ -195,13 +187,13 @@ public:
const SupportLayerPtrs& support_layers() const { return m_support_layers; }
const Points& copies() const { return m_copies; }
- bool add_copy(const Pointf &point);
+ bool add_copy(const Vec2d &point);
bool delete_last_copy();
bool delete_all_copies() { return this->set_copies(Points()); }
bool set_copies(const Points &points);
bool reload_model_instances();
// since the object is aligned to origin, bounding box coincides with size
- BoundingBox bounding_box() const { return BoundingBox(Point(0,0), this->size); }
+ BoundingBox bounding_box() const { return BoundingBox(Point(0,0), to_2d(this->size)); }
// adds region_id, too, if necessary
void add_region_volume(unsigned int region_id, int volume_id) {
@@ -244,6 +236,8 @@ public:
void reset_layer_height_profile();
+ void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action);
+
// 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
@@ -262,6 +256,7 @@ private:
void _slice();
std::string _fix_slicing_errors();
void _simplify_slices(double distance);
+ void _make_perimeters();
bool has_support_material() const;
void detect_surfaces_type();
void process_external_surfaces();
@@ -272,11 +267,17 @@ private:
void combine_infill();
void _generate_support_material();
+ bool is_printable() const { return ! m_copies.empty(); }
+
Print *m_print;
ModelObject *m_model_object;
PrintObjectConfig m_config;
// Slic3r::Point objects in scaled G-code coordinates
Points m_copies;
+ // scaled coordinates to add to copies (to compensate for the alignment
+ // operated when creating the object but still preserving a coherent API
+ // for external callers)
+ Point m_copies_shift;
LayerPtrs m_layers;
SupportLayerPtrs m_support_layers;
@@ -308,18 +309,23 @@ struct WipeTowerData
std::vector<std::vector<WipeTower::ToolChangeResult>> tool_changes;
std::unique_ptr<WipeTower::ToolChangeResult> final_purge;
+ // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box:
+ float depth;
+
void clear() {
tool_ordering.clear();
priming.reset(nullptr);
tool_changes.clear();
final_purge.reset(nullptr);
+ depth = 0.f;
}
};
struct PrintStatistics
{
PrintStatistics() { clear(); }
- std::string estimated_print_time;
+ std::string estimated_normal_print_time;
+ std::string estimated_silent_print_time;
double total_used_filament;
double total_extruded_volume;
double total_cost;
@@ -327,11 +333,12 @@ struct PrintStatistics
std::map<size_t, float> filament_stats;
void clear() {
- estimated_print_time.clear();
+ estimated_normal_print_time.clear();
+ estimated_silent_print_time.clear();
total_used_filament = 0.;
total_extruded_volume = 0.;
- total_weight = 0.;
total_cost = 0.;
+ total_weight = 0.;
filament_stats.clear();
}
};
@@ -345,7 +352,7 @@ class Print
public:
Print() { restart(); }
~Print() { clear_objects(); }
-
+
// Methods, which change the state of Print / PrintObject / PrintRegion.
// The following methods are synchronized with process() and export_gcode(),
// so that process() and export_gcode() may be called from a background thread.
@@ -359,6 +366,8 @@ public:
bool apply_config(DynamicPrintConfig config);
void process();
void export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
+ // SLA export, temporary.
+ void print_to_png(const std::string &dirpath);
// methods for handling state
bool is_step_done(PrintStep step) const { return m_state.is_done(step); }
@@ -366,6 +375,9 @@ public:
bool has_infinite_skirt() const;
bool has_skirt() const;
+ PrintObjectPtrs get_printable_objects() const;
+ float get_wipe_tower_depth() const { return m_wipe_tower_data.depth; }
+
// Returns an empty string if valid, otherwise returns an error message.
std::string validate() const;
BoundingBox bounding_box() const;
@@ -386,9 +398,13 @@ public:
const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
const PrintObjectPtrs& objects() const { return m_objects; }
+ const PrintObject* get_object(int idx) const { return m_objects[idx]; }
const PrintRegionPtrs& regions() const { return m_regions; }
const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser; }
+ // Returns extruder this eec should be printed with, according to PrintRegion config:
+ static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
+
const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; }
@@ -458,6 +474,7 @@ private:
// The mutex will be used to guard the worker thread against entering a stage
// while the data influencing the stage is modified.
tbb::mutex m_mutex;
+
// Has the calculation been canceled?
tbb::atomic<bool> m_canceled;
// Callback to be evoked regularly to update state of the UI thread.
@@ -489,6 +506,7 @@ private:
friend class PrintObject;
};
+
#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator)
#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->m_objects, object)
#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->m_layers, layer)
diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp
index 298fe9207..a3e84a356 100644
--- a/xs/src/libslic3r/PrintConfig.cpp
+++ b/xs/src/libslic3r/PrintConfig.cpp
@@ -1,7 +1,10 @@
#include "PrintConfig.hpp"
+#include "I18N.hpp"
#include <set>
#include <boost/algorithm/string/replace.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
+#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
@@ -11,12 +14,54 @@ namespace Slic3r {
//! macro used to mark string used at localization,
//! return same string
-#define L(s) s
+#define L(s) Slic3r::I18N::translate(s)
PrintConfigDef::PrintConfigDef()
{
+ this->init_common_params();
+ this->init_fff_params();
+ this->init_sla_params();
+}
+
+void PrintConfigDef::init_common_params()
+{
t_optiondef_map &Options = this->options;
+ ConfigOptionDef* def;
+
+ def = this->add("printer_technology", coEnum);
+ def->label = L("Printer technology");
+ def->tooltip = L("Printer technology");
+ def->cli = "printer-technology=s";
+ def->enum_keys_map = &ConfigOptionEnum<PrinterTechnology>::get_enum_values();
+ def->enum_values.push_back("FFF");
+ def->enum_values.push_back("SLA");
+ def->default_value = new ConfigOptionEnum<PrinterTechnology>(ptFFF);
+
+ def = this->add("bed_shape", coPoints);
+ def->label = L("Bed shape");
+ def->default_value = new ConfigOptionPoints{ Vec2d(0, 0), Vec2d(200, 0), Vec2d(200, 200), Vec2d(0, 200) };
+ def = this->add("layer_height", coFloat);
+ def->label = L("Layer height");
+ def->category = L("Layers and Perimeters");
+ def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. "
+ "Thinner layers give better accuracy but take more time to print.");
+ def->sidetext = L("mm");
+ def->cli = "layer-height=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(0.3);
+
+ def = this->add("max_print_height", coFloat);
+ def->label = L("Max print height");
+ def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
+ def->sidetext = L("mm");
+ def->cli = "max-print-height=f";
+ def->default_value = new ConfigOptionFloat(200.0);
+}
+
+void PrintConfigDef::init_fff_params()
+{
+ t_optiondef_map &Options = this->options;
ConfigOptionDef* def;
// Maximum extruder temperature, bumped to 1500 to support printing of glass.
@@ -30,10 +75,6 @@ PrintConfigDef::PrintConfigDef()
def->cli = "avoid-crossing-perimeters!";
def->default_value = new ConfigOptionBool(false);
- def = this->add("bed_shape", coPoints);
- def->label = L("Bed shape");
- def->default_value = new ConfigOptionPoints { Pointf(0,0), Pointf(200,0), Pointf(200,200), Pointf(0,200) };
-
def = this->add("bed_temperature", coInts);
def->label = L("Other layers");
def->tooltip = L("Bed temperature for layers after the first one. "
@@ -111,8 +152,8 @@ PrintConfigDef::PrintConfigDef()
"with cooling (use a fan) before tweaking this.");
def->cli = "bridge-flow-ratio=f";
def->min = 0;
- def->max = 2;
- def->default_value = new ConfigOptionFloat(1);
+ def->max = 2;
+ def->default_value = new ConfigOptionFloat(1);
def = this->add("bridge_speed", coFloat);
def->label = L("Bridges");
@@ -151,6 +192,11 @@ PrintConfigDef::PrintConfigDef()
"with the active printer profile.");
def->default_value = new ConfigOptionString();
+ // The following value is to be stored into the project file (AMF, 3MF, Config ...)
+ // and it contains a sum of "compatible_printers_condition" values over the print and filament profiles.
+ def = this->add("compatible_printers_condition_cummulative", coStrings);
+ def->default_value = new ConfigOptionStrings();
+
def = this->add("complete_objects", coBool);
def->label = L("Complete individual objects");
def->tooltip = L("When printing multiple objects or copies, this feature will complete "
@@ -167,6 +213,22 @@ PrintConfigDef::PrintConfigDef()
def->cli = "cooling!";
def->default_value = new ConfigOptionBools { true };
+ def = this->add("cooling_tube_retraction", coFloat);
+ def->label = L("Cooling tube position");
+ def->tooltip = L("Distance of the center-point of the cooling tube from the extruder tip ");
+ def->sidetext = L("mm");
+ def->cli = "cooling_tube_retraction=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(91.5f);
+
+ def = this->add("cooling_tube_length", coFloat);
+ def->label = L("Cooling tube length");
+ def->tooltip = L("Length of the cooling tube to limit space for cooling moves inside it ");
+ def->sidetext = L("mm");
+ def->cli = "cooling_tube_length=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(5.f);
+
def = this->add("default_acceleration", coFloat);
def->label = L("Default");
def->tooltip = L("This is the acceleration your printer will be reset to after "
@@ -177,6 +239,18 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(0);
+ def = this->add("default_filament_profile", coStrings);
+ def->label = L("Default filament profile");
+ def->tooltip = L("Default filament profile associated with the current printer profile. "
+ "On selection of the current printer profile, this filament profile will be activated.");
+ def->default_value = new ConfigOptionStrings();
+
+ def = this->add("default_print_profile", coString);
+ def->label = L("Default print profile");
+ def->tooltip = L("Default print profile associated with the current printer profile. "
+ "On selection of the current printer profile, this print profile will be activated.");
+ def->default_value = new ConfigOptionString();
+
def = this->add("disable_fan_first_layers", coInts);
def->label = L("Disable fan for the first");
def->tooltip = L("You can set this to a positive value to disable fan at all "
@@ -255,11 +329,11 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
- def->enum_labels.push_back("Rectilinear");
- def->enum_labels.push_back("Concentric");
- def->enum_labels.push_back("Hilbert Curve");
- def->enum_labels.push_back("Archimedean Chords");
- def->enum_labels.push_back("Octagram Spiral");
+ def->enum_labels.push_back(L("Rectilinear"));
+ def->enum_labels.push_back(L("Concentric"));
+ def->enum_labels.push_back(L("Hilbert Curve"));
+ def->enum_labels.push_back(L("Archimedean Chords"));
+ def->enum_labels.push_back(L("Octagram Spiral"));
// solid_fill_pattern is an obsolete equivalent to external_fill_pattern.
def->aliases.push_back("solid_fill_pattern");
def->default_value = new ConfigOptionEnum<InfillPattern>(ipRectilinear);
@@ -316,6 +390,7 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("2");
def->enum_labels.push_back("3");
def->enum_labels.push_back("4");
+ def->enum_labels.push_back("5");
def = this->add("extruder_clearance_height", coFloat);
def->label = L("Height");
@@ -355,7 +430,7 @@ PrintConfigDef::PrintConfigDef()
"from the XY coordinate).");
def->sidetext = L("mm");
def->cli = "extruder-offset=s@";
- def->default_value = new ConfigOptionPoints { Pointf(0,0) };
+ def->default_value = new ConfigOptionPoints { Vec2d(0,0) };
def = this->add("extrusion_axis", coString);
def->label = L("Extrusion axis");
@@ -407,7 +482,7 @@ PrintConfigDef::PrintConfigDef()
def->tooltip = L("This is only used in the Slic3r interface as a visual help.");
def->cli = "filament-color=s@";
def->gui_type = "color";
- def->default_value = new ConfigOptionStrings { "#29b2b2" };
+ def->default_value = new ConfigOptionStrings { "#29B2B2" };
def = this->add("filament_notes", coStrings);
def->label = L("Filament notes");
@@ -428,6 +503,108 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 0. };
+ def = this->add("filament_loading_speed", coFloats);
+ def->label = L("Loading speed");
+ def->tooltip = L("Speed used for loading the filament on the wipe tower. ");
+ def->sidetext = L("mm/s");
+ def->cli = "filament-loading-speed=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 28. };
+
+ def = this->add("filament_loading_speed_start", coFloats);
+ def->label = L("Loading speed at the start");
+ def->tooltip = L("Speed used at the very beginning of loading phase. ");
+ def->sidetext = L("mm/s");
+ def->cli = "filament-loading-speed-start=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 3. };
+
+ def = this->add("filament_unloading_speed", coFloats);
+ def->label = L("Unloading speed");
+ def->tooltip = L("Speed used for unloading the filament on the wipe tower (does not affect "
+ " initial part of unloading just after ramming). ");
+ def->sidetext = L("mm/s");
+ def->cli = "filament-unloading-speed=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 90. };
+
+ def = this->add("filament_unloading_speed_start", coFloats);
+ def->label = L("Unloading speed at the start");
+ def->tooltip = L("Speed used for unloading the tip of the filament immediately after ramming. ");
+ def->sidetext = L("mm/s");
+ def->cli = "filament-unloading-speed-start=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 100. };
+
+ def = this->add("filament_toolchange_delay", coFloats);
+ def->label = L("Delay after unloading");
+ def->tooltip = L("Time to wait after the filament is unloaded. "
+ "May help to get reliable toolchanges with flexible materials "
+ "that may need more time to shrink to original dimensions. ");
+ def->sidetext = L("s");
+ def->cli = "filament-toolchange-delay=f@";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 0. };
+
+ def = this->add("filament_cooling_moves", coInts);
+ def->label = L("Number of cooling moves");
+ def->tooltip = L("Filament is cooled by being moved back and forth in the "
+ "cooling tubes. Specify desired number of these moves ");
+ def->cli = "filament-cooling-moves=i@";
+ def->max = 0;
+ def->max = 20;
+ def->default_value = new ConfigOptionInts { 4 };
+
+ def = this->add("filament_cooling_initial_speed", coFloats);
+ def->label = L("Speed of the first cooling move");
+ def->tooltip = L("Cooling moves are gradually accelerating beginning at this speed. ");
+ def->cli = "filament-cooling-initial-speed=f@";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 2.2f };
+
+ def = this->add("filament_minimal_purge_on_wipe_tower", coFloats);
+ def->label = L("Minimal purge on wipe tower");
+ def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
+ "the nozzle may not be known, and the filament pressure is likely not yet stable. "
+ "Before purging the print head into an infill or a sacrificial object, Slic3r will always prime "
+ "this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
+ def->cli = "filament-minimal-purge-on-wipe-tower=f@";
+ def->sidetext = L("mm³");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 15.f };
+
+ def = this->add("filament_cooling_final_speed", coFloats);
+ def->label = L("Speed of the last cooling move");
+ def->tooltip = L("Cooling moves are gradually accelerating towards this speed. ");
+ def->cli = "filament-cooling-final-speed=f@";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 3.4f };
+
+ def = this->add("filament_load_time", coFloats);
+ def->label = L("Filament load time");
+ def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to load a new filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
+ def->cli = "filament-load-time=i@";
+ def->sidetext = L("s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 0.0f };
+
+ def = this->add("filament_ramming_parameters", coStrings);
+ def->label = L("Ramming parameters");
+ def->tooltip = L("This string is edited by RammingDialog and contains ramming specific parameters ");
+ def->cli = "filament-ramming-parameters=s@";
+ def->default_value = new ConfigOptionStrings { "120 100 6.6 6.8 7.2 7.6 7.9 8.2 8.7 9.4 9.9 10.0|"
+ " 0.05 6.6 0.45 6.8 0.95 7.8 1.45 8.3 1.95 9.7 2.45 10 2.95 7.6 3.45 7.6 3.95 7.6 4.45 7.6 4.95 7.6" };
+
+ def = this->add("filament_unload_time", coFloats);
+ def->label = L("Filament unload time");
+ def->tooltip = L("Time for the printer firmware (or the Multi Material Unit 2.0) to unload a filament during a tool change (when executing the T code). This time is added to the total print time by the G-code time estimator.");
+ def->cli = "filament-unload-time=i@";
+ def->sidetext = L("s");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats { 0.0f };
+
def = this->add("filament_diameter", coFloats);
def->label = L("Diameter");
def->tooltip = L("Enter your filament diameter here. Good precision is required, so use a caliper "
@@ -449,10 +626,7 @@ PrintConfigDef::PrintConfigDef()
def = this->add("filament_type", coStrings);
def->label = L("Filament type");
- def->tooltip = L("If you want to process the output G-code through custom scripts, just list their "
- "absolute paths here. Separate multiple scripts with a semicolon. Scripts will be passed "
- "the absolute path to the G-code file as the first argument, and they can access "
- "the Slic3r config settings by reading environment variables.");
+ def->tooltip = L("The filament material type for use in custom G-codes.");
def->cli = "filament_type=s@";
def->gui_type = "f_enum_open";
def->gui_flags = "show_value";
@@ -555,19 +729,19 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("archimedeanchords");
def->enum_values.push_back("octagramspiral");
- def->enum_labels.push_back("Rectilinear");
- def->enum_labels.push_back("Grid");
- def->enum_labels.push_back("Triangles");
- def->enum_labels.push_back("Stars");
- def->enum_labels.push_back("Cubic");
- def->enum_labels.push_back("Line");
- def->enum_labels.push_back("Concentric");
- def->enum_labels.push_back("Honeycomb");
- def->enum_labels.push_back("3D Honeycomb");
- def->enum_labels.push_back("Gyroid");
- def->enum_labels.push_back("Hilbert Curve");
- def->enum_labels.push_back("Archimedean Chords");
- def->enum_labels.push_back("Octagram Spiral");
+ def->enum_labels.push_back(L("Rectilinear"));
+ def->enum_labels.push_back(L("Grid"));
+ def->enum_labels.push_back(L("Triangles"));
+ def->enum_labels.push_back(L("Stars"));
+ def->enum_labels.push_back(L("Cubic"));
+ def->enum_labels.push_back(L("Line"));
+ def->enum_labels.push_back(L("Concentric"));
+ def->enum_labels.push_back(L("Honeycomb"));
+ def->enum_labels.push_back(L("3D Honeycomb"));
+ def->enum_labels.push_back(L("Gyroid"));
+ def->enum_labels.push_back(L("Hilbert Curve"));
+ def->enum_labels.push_back(L("Archimedean Chords"));
+ def->enum_labels.push_back(L("Octagram Spiral"));
def->default_value = new ConfigOptionEnum<InfillPattern>(ipStars);
def = this->add("first_layer_acceleration", coFloat);
@@ -675,8 +849,8 @@ PrintConfigDef::PrintConfigDef()
def->enum_labels.push_back("Mach3/LinuxCNC");
def->enum_labels.push_back("Machinekit");
def->enum_labels.push_back("Smoothie");
- def->enum_labels.push_back("No extrusion");
- def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfMarlin);
+ def->enum_labels.push_back(L("No extrusion"));
+ def->default_value = new ConfigOptionEnum<GCodeFlavor>(gcfRepRap);
def = this->add("infill_acceleration", coFloat);
def->label = L("Infill");
@@ -754,6 +928,18 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloat(80);
+ def = this->add("inherits", coString);
+ def->label = L("Inherits profile");
+ def->tooltip = L("Name of the profile, from which this profile inherits.");
+ def->full_width = true;
+ def->height = 50;
+ def->default_value = new ConfigOptionString();
+
+ // The following value is to be stored into the project file (AMF, 3MF, Config ...)
+ // and it contains a sum of "inherits" values over the print and filament profiles.
+ def = this->add("inherits_cummulative", coStrings);
+ def->default_value = new ConfigOptionStrings();
+
def = this->add("interface_shells", coBool);
def->label = L("Interface shells");
def->tooltip = L("Force the generation of solid shells between adjacent materials/volumes. "
@@ -774,15 +960,105 @@ PrintConfigDef::PrintConfigDef()
def->height = 50;
def->default_value = new ConfigOptionString("");
- def = this->add("layer_height", coFloat);
- def->label = L("Layer height");
- def->category = L("Layers and Perimeters");
- def->tooltip = L("This setting controls the height (and thus the total number) of the slices/layers. "
- "Thinner layers give better accuracy but take more time to print.");
- def->sidetext = L("mm");
- def->cli = "layer-height=f";
+ def = this->add("remaining_times", coBool);
+ def->label = L("Supports remaining times");
+ def->tooltip = L("Emit M73 P[percent printed] R[remaining time in minutes] at 1 minute"
+ " intervals into the G-code to let the firmware show accurate remaining time."
+ " As of now only the Prusa i3 MK3 firmware recognizes M73."
+ " Also the i3 MK3 firmware supports M73 Qxx Sxx for the silent mode.");
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("silent_mode", coBool);
+ def->label = L("Supports silent mode");
+ def->tooltip = L("Set silent mode for the G-code flavor");
+ def->default_value = new ConfigOptionBool(true);
+
+ const int machine_limits_opt_width = 70;
+ {
+ struct AxisDefault {
+ std::string name;
+ std::vector<double> max_feedrate;
+ std::vector<double> max_acceleration;
+ std::vector<double> max_jerk;
+ };
+ std::vector<AxisDefault> axes {
+ // name, max_feedrate, max_acceleration, max_jerk
+ { "x", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } },
+ { "y", { 500., 200. }, { 9000., 1000. }, { 10. , 10. } },
+ { "z", { 12., 12. }, { 500., 200. }, { 0.2, 0.4 } },
+ { "e", { 120., 120. }, { 10000., 5000. }, { 2.5, 2.5 } }
+ };
+ for (const AxisDefault &axis : axes) {
+ std::string axis_upper = boost::to_upper_copy<std::string>(axis.name);
+ // Add the machine feedrate limits for XYZE axes. (M203)
+ def = this->add("machine_max_feedrate_" + axis.name, coFloats);
+ def->full_label = (boost::format(L("Maximum feedrate %1%")) % axis_upper).str();
+ def->category = L("Machine limits");
+ def->tooltip = (boost::format(L("Maximum feedrate of the %1% axis")) % axis_upper).str();
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats(axis.max_feedrate);
+ // Add the machine acceleration limits for XYZE axes (M201)
+ def = this->add("machine_max_acceleration_" + axis.name, coFloats);
+ def->full_label = (boost::format(L("Maximum acceleration %1%")) % axis_upper).str();
+ def->category = L("Machine limits");
+ def->tooltip = (boost::format(L("Maximum acceleration of the %1% axis")) % axis_upper).str();
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats(axis.max_acceleration);
+ // Add the machine jerk limits for XYZE axes (M205)
+ def = this->add("machine_max_jerk_" + axis.name, coFloats);
+ def->full_label = (boost::format(L("Maximum jerk %1%")) % axis_upper).str();
+ def->category = L("Machine limits");
+ def->tooltip = (boost::format(L("Maximum jerk of the %1% axis")) % axis_upper).str();
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats(axis.max_jerk);
+ }
+ }
+
+ // M205 S... [mm/sec]
+ def = this->add("machine_min_extruding_rate", coFloats);
+ def->full_label = L("Minimum feedrate when extruding");
+ def->category = L("Machine limits");
+ def->tooltip = L("Minimum feedrate when extruding") + " (M205 S)";
+ def->sidetext = L("mm/s");
def->min = 0;
- def->default_value = new ConfigOptionFloat(0.3);
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 0., 0. };
+
+ // M205 T... [mm/sec]
+ def = this->add("machine_min_travel_rate", coFloats);
+ def->full_label = L("Minimum travel feedrate");
+ def->category = L("Machine limits");
+ def->tooltip = L("Minimum travel feedrate") + " (M205 T)";
+ def->sidetext = L("mm/s");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 0., 0. };
+
+ // M204 S... [mm/sec^2]
+ def = this->add("machine_max_acceleration_extruding", coFloats);
+ def->full_label = L("Maximum acceleration when extruding");
+ def->category = L("Machine limits");
+ def->tooltip = L("Maximum acceleration when extruding") + " (M204 S)";
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 1500., 1250. };
+
+ // M204 T... [mm/sec^2]
+ def = this->add("machine_max_acceleration_retracting", coFloats);
+ def->full_label = L("Maximum acceleration when retracting");
+ def->category = L("Machine limits");
+ def->tooltip = L("Maximum acceleration when retracting") + " (M204 T)";
+ def->sidetext = L("mm/s²");
+ def->min = 0;
+ def->width = machine_limits_opt_width;
+ def->default_value = new ConfigOptionFloats{ 1500., 1250. };
def = this->add("max_fan_speed", coInts);
def->label = L("Max");
@@ -804,13 +1080,6 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->default_value = new ConfigOptionFloats { 0. };
- def = this->add("max_print_height", coFloat);
- def->label = L("Max print height");
- def->tooltip = L("Set this to the maximum height that can be reached by your extruder while printing.");
- def->sidetext = L("mm");
- def->cli = "max-print-height=f";
- def->default_value = new ConfigOptionFloat(200.0);
-
def = this->add("max_print_speed", coFloat);
def->label = L("Max print speed");
def->tooltip = L("When setting other speed settings to 0 Slic3r will autocalculate the optimal speed "
@@ -879,7 +1148,7 @@ PrintConfigDef::PrintConfigDef()
def->default_value = new ConfigOptionFloats { 10. };
def = this->add("min_skirt_length", coFloat);
- def->label = L("Minimum extrusion length");
+ def->label = L("Minimal filament extrusion length");
def->tooltip = L("Generate no less than the number of skirt loops required to consume "
"the specified amount of filament on the bottom layer. For multi-extruder machines, "
"this minimum applies to each extruder.");
@@ -905,25 +1174,37 @@ PrintConfigDef::PrintConfigDef()
def->cli = "nozzle-diameter=f@";
def->default_value = new ConfigOptionFloats { 0.5 };
- def = this->add("octoprint_apikey", coString);
- def->label = L("API Key");
- def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
- "the API Key required for authentication.");
- def->cli = "octoprint-apikey=s";
+ def = this->add("host_type", coEnum);
+ def->label = L("Host Type");
+ def->tooltip = L("Slic3r can upload G-code files to a printer host. This field must contain "
+ "the kind of the host.");
+ def->cli = "host-type=s";
+ def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
+ def->enum_values.push_back("octoprint");
+ def->enum_values.push_back("duet");
+ def->enum_labels.push_back("OctoPrint");
+ def->enum_labels.push_back("Duet");
+ def->default_value = new ConfigOptionEnum<PrintHostType>(htOctoPrint);
+
+ def = this->add("printhost_apikey", coString);
+ def->label = L("API Key / Password");
+ def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
+ "the API Key or the password required for authentication.");
+ def->cli = "printhost-apikey=s";
def->default_value = new ConfigOptionString("");
- def = this->add("octoprint_cafile", coString);
+ def = this->add("printhost_cafile", coString);
def->label = "HTTPS CA file";
def->tooltip = "Custom CA certificate file can be specified for HTTPS OctoPrint connections, in crt/pem format. "
"If left blank, the default OS CA certificate repository is used.";
- def->cli = "octoprint-cafile=s";
+ def->cli = "printhost-cafile=s";
def->default_value = new ConfigOptionString("");
- def = this->add("octoprint_host", coString);
+ def = this->add("print_host", coString);
def->label = L("Hostname, IP or URL");
- def->tooltip = L("Slic3r can upload G-code files to OctoPrint. This field should contain "
- "the hostname, IP address or URL of the OctoPrint instance.");
- def->cli = "octoprint-host=s";
+ def->tooltip = L("Slic3r can upload G-code files to a printer host. This field should contain "
+ "the hostname, IP address or URL of the printer host instance.");
+ def->cli = "print-host=s";
def->default_value = new ConfigOptionString("");
def = this->add("only_retract_when_crossing_perimeters", coBool);
@@ -959,6 +1240,24 @@ PrintConfigDef::PrintConfigDef()
def->cli = "overhangs!";
def->default_value = new ConfigOptionBool(true);
+ def = this->add("parking_pos_retraction", coFloat);
+ def->label = L("Filament parking position");
+ def->tooltip = L("Distance of the extruder tip from the position where the filament is parked "
+ "when unloaded. This should match the value in printer firmware. ");
+ def->sidetext = L("mm");
+ def->cli = "parking_pos_retraction=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(92.f);
+
+ def = this->add("extra_loading_move", coFloat);
+ def->label = L("Extra loading distance");
+ def->tooltip = L("When set to zero, the distance the filament is moved from parking position during load "
+ "is exactly the same as it was moved back during unload. When positive, it is loaded further, "
+ " if negative, the loading move is shorter than unloading. ");
+ def->sidetext = L("mm");
+ def->cli = "extra_loading_move=f";
+ def->default_value = new ConfigOptionFloat(-2.f);
+
def = this->add("perimeter_acceleration", coFloat);
def->label = L("Perimeters");
def->tooltip = L("This is the acceleration your printer will use for perimeters. "
@@ -1023,7 +1322,12 @@ PrintConfigDef::PrintConfigDef()
def->multiline = true;
def->full_width = true;
def->height = 60;
- def->default_value = new ConfigOptionStrings{ "" };
+ def->default_value = new ConfigOptionStrings();
+
+ def = this->add("printer_model", coString);
+ def->label = L("Printer type");
+ def->tooltip = L("Type of the printer.");
+ def->default_value = new ConfigOptionString();
def = this->add("printer_notes", coString);
def->label = L("Printer notes");
@@ -1034,6 +1338,16 @@ PrintConfigDef::PrintConfigDef()
def->height = 130;
def->default_value = new ConfigOptionString("");
+ def = this->add("printer_vendor", coString);
+ def->label = L("Printer vendor");
+ def->tooltip = L("Name of the printer vendor.");
+ def->default_value = new ConfigOptionString();
+
+ def = this->add("printer_variant", coString);
+ def->label = L("Printer variant");
+ def->tooltip = L("Name of the printer variant. For example, the printer variants may be differentiated by a nozzle diameter.");
+ def->default_value = new ConfigOptionString();
+
def = this->add("print_settings_id", coString);
def->default_value = new ConfigOptionString("");
@@ -1172,10 +1486,10 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("nearest");
def->enum_values.push_back("aligned");
def->enum_values.push_back("rear");
- def->enum_labels.push_back("Random");
- def->enum_labels.push_back("Nearest");
- def->enum_labels.push_back("Aligned");
- def->enum_labels.push_back("Rear");
+ def->enum_labels.push_back(L("Random"));
+ def->enum_labels.push_back(L("Nearest"));
+ def->enum_labels.push_back(L("Aligned"));
+ def->enum_labels.push_back(L("Rear"));
def->default_value = new ConfigOptionEnum<SeamPosition>(spAligned);
#if 0
@@ -1388,7 +1702,13 @@ PrintConfigDef::PrintConfigDef()
def->label = L("Single Extruder Multi Material");
def->tooltip = L("The printer multiplexes filaments into a single hot end.");
def->cli = "single-extruder-multi-material!";
- def->default_value = new ConfigOptionBool(false);
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("single_extruder_multi_material_priming", coBool);
+ def->label = L("Prime all printing extruders");
+ def->tooltip = L("If enabled, all printing extruders will be primed at the front edge of the print bed at the start of the print.");
+ def->cli = "single-extruder-multi-material-priming!";
+ def->default_value = new ConfigOptionBool(true);
def = this->add("support_material", coBool);
def->label = L("Generate support material");
@@ -1438,8 +1758,8 @@ PrintConfigDef::PrintConfigDef()
def->min = 0;
def->enum_values.push_back("0");
def->enum_values.push_back("0.2");
- def->enum_labels.push_back("0 (soluble)");
- def->enum_labels.push_back("0.2 (detachable)");
+ def->enum_labels.push_back((boost::format("0 (%1%)") % L("soluble")).str());
+ def->enum_labels.push_back((boost::format("0.2 (%1%)") % L("detachable")).str());
def->default_value = new ConfigOptionFloat(0.2);
def = this->add("support_material_enforce_layers", coInt);
@@ -1528,9 +1848,9 @@ PrintConfigDef::PrintConfigDef()
def->enum_values.push_back("rectilinear");
def->enum_values.push_back("rectilinear-grid");
def->enum_values.push_back("honeycomb");
- def->enum_labels.push_back("rectilinear");
- def->enum_labels.push_back("rectilinear grid");
- def->enum_labels.push_back("honeycomb");
+ def->enum_labels.push_back(L("Rectilinear"));
+ def->enum_labels.push_back(L("Rectilinear grid"));
+ def->enum_labels.push_back(L("Honeycomb"));
def->default_value = new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear);
def = this->add("support_material_spacing", coFloat);
@@ -1587,7 +1907,7 @@ PrintConfigDef::PrintConfigDef()
"temperature control commands in the output.");
def->cli = "temperature=i@";
def->full_label = L("Temperature");
- def->max = 0;
+ def->min = 0;
def->max = max_temp;
def->default_value = new ConfigOptionInts { 200 };
@@ -1711,6 +2031,25 @@ PrintConfigDef::PrintConfigDef()
def->cli = "wipe-tower!";
def->default_value = new ConfigOptionBool(false);
+ def = this->add("wiping_volumes_extruders", coFloats);
+ def->label = L("Purging volumes - load/unload volumes");
+ def->tooltip = L("This vector saves required volumes to change from/to each tool used on the "
+ "wipe tower. These values are used to simplify creation of the full purging "
+ "volumes below. ");
+ def->cli = "wiping-volumes-extruders=f@";
+ def->default_value = new ConfigOptionFloats { 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f, 70.f };
+
+ def = this->add("wiping_volumes_matrix", coFloats);
+ def->label = L("Purging volumes - matrix");
+ def->tooltip = L("This matrix describes volumes (in cubic milimetres) required to purge the"
+ " new filament on the wipe tower for any given pair of tools. ");
+ def->cli = "wiping-volumes-matrix=f@";
+ def->default_value = new ConfigOptionFloats { 0.f, 140.f, 140.f, 140.f, 140.f,
+ 140.f, 0.f, 140.f, 140.f, 140.f,
+ 140.f, 140.f, 0.f, 140.f, 140.f,
+ 140.f, 140.f, 140.f, 0.f, 140.f,
+ 140.f, 140.f, 140.f, 140.f, 0.f };
+
def = this->add("wipe_tower_x", coFloat);
def->label = L("Position X");
def->tooltip = L("X coordinate of the left front corner of a wipe tower");
@@ -1732,14 +2071,37 @@ PrintConfigDef::PrintConfigDef()
def->cli = "wipe-tower-width=f";
def->default_value = new ConfigOptionFloat(60.);
- def = this->add("wipe_tower_per_color_wipe", coFloat);
- def->label = L("Per color change depth");
- def->tooltip = L("Depth of a wipe color per color change. For N colors, there will be "
- "maximum (N-1) tool switches performed, therefore the total depth "
- "of the wipe tower will be (N-1) times this value.");
+ def = this->add("wipe_tower_rotation_angle", coFloat);
+ def->label = L("Wipe tower rotation angle");
+ def->tooltip = L("Wipe tower rotation angle with respect to x-axis ");
+ def->sidetext = L("degrees");
+ def->cli = "wipe-tower-rotation-angle=f";
+ def->default_value = new ConfigOptionFloat(0.);
+
+ def = this->add("wipe_into_infill", coBool);
+ def->category = L("Extruders");
+ def->label = L("Wipe into this object's infill");
+ def->tooltip = L("Purging after toolchange will done inside this object's infills. "
+ "This lowers the amount of waste but may result in longer print time "
+ " due to additional travel moves.");
+ def->cli = "wipe-into-infill!";
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("wipe_into_objects", coBool);
+ def->category = L("Extruders");
+ def->label = L("Wipe into this object");
+ def->tooltip = L("Object will be used to purge the nozzle after a toolchange to save material "
+ "that would otherwise end up in the wipe tower and decrease print time. "
+ "Colours of the objects will be mixed as a result.");
+ def->cli = "wipe-into-objects!";
+ def->default_value = new ConfigOptionBool(false);
+
+ def = this->add("wipe_tower_bridging", coFloat);
+ def->label = L("Maximal bridging distance");
+ def->tooltip = L("Maximal distance between supports on sparse infill sections. ");
def->sidetext = L("mm");
- def->cli = "wipe-tower-per-color-wipe=f";
- def->default_value = new ConfigOptionFloat(15.);
+ def->cli = "wipe-tower-bridging=f";
+ def->default_value = new ConfigOptionFloat(10.);
def = this->add("xy_size_compensation", coFloat);
def->label = L("XY Size Compensation");
@@ -1760,6 +2122,149 @@ PrintConfigDef::PrintConfigDef()
def->sidetext = L("mm");
def->cli = "z-offset=f";
def->default_value = new ConfigOptionFloat(0);
+
+ def = this->add("bed_size_x", coFloat);
+ def->label = L("Bed size X");
+ def->category = L("Dwarf");
+ def->sidetext = L("mm");
+ def->cli = "bed-size-x=f";
+ def->default_value = new ConfigOptionFloat(68.);
+
+ def = this->add("bed_size_y", coFloat);
+ def->label = L("Bed size Y");
+ def->category = L("Dwarf");
+ def->sidetext = L("mm");
+ def->cli = "bed-size-y=f";
+ def->default_value = new ConfigOptionFloat(120.);
+
+ def = this->add("pixel_width", coInt);
+ def->label = L("Picture resolution X");
+ def->category = L("Dwarf");
+ def->sidetext = L("px");
+ def->cli = "pixel-width=i";
+ def->min = 1;
+ def->default_value = new ConfigOptionInt(1440);
+
+ def = this->add("pixel_height", coInt);
+ def->label = L("Picture resolution Y");
+ def->category = L("Dwarf");
+ def->sidetext = L("px");
+ def->cli = "pixel-height=i";
+ def->min = 1;
+ def->default_value = new ConfigOptionInt(2560);
+
+ def = this->add("exp_time", coFloat);
+ def->label = L("Exposure time");
+ def->category = L("Dwarf");
+ def->sidetext = L("s");
+ def->cli = "exp-time=f";
+ def->min = 1;
+ def->default_value = new ConfigOptionFloat(8.);
+
+ def = this->add("exp_time_first", coFloat);
+ def->label = L("Exposure time first layers");
+ def->category = L("Dwarf");
+ def->sidetext = L("s");
+ def->cli = "exp-time-first=f";
+ def->min = 1;
+ def->default_value = new ConfigOptionFloat(35.);
+}
+
+void PrintConfigDef::init_sla_params()
+{
+ t_optiondef_map &Options = this->options;
+ ConfigOptionDef* def;
+
+ // SLA Printer settings
+ def = this->add("display_width", coFloat);
+ def->label = L("Display width");
+ def->tooltip = L("Width of the display");
+ def->cli = "display-width=f";
+ def->min = 1;
+ def->default_value = new ConfigOptionFloat(150.);
+
+ def = this->add("display_height", coFloat);
+ def->label = L("Display height");
+ def->tooltip = L("Height of the display");
+ def->cli = "display-height=f";
+ def->min = 1;
+ def->default_value = new ConfigOptionFloat(100.);
+
+ def = this->add("display_pixels_x", coInt);
+ def->full_label = L("Number of pixels in");
+ def->label = ("X");
+ def->tooltip = L("Number of pixels in X");
+ def->cli = "display-pixels-x=i";
+ def->min = 100;
+ def->default_value = new ConfigOptionInt(2000);
+
+ def = this->add("display_pixels_y", coInt);
+ def->label = ("Y");
+ def->tooltip = L("Number of pixels in Y");
+ def->cli = "display-pixels-y=i";
+ def->min = 100;
+ def->default_value = new ConfigOptionInt(1000);
+
+ def = this->add("printer_correction", coFloats);
+ def->full_label = L("Printer scaling correction");
+ def->tooltip = L("Printer scaling correction");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats( { 1., 1., 1. } );
+
+ // SLA Material settings.
+ def = this->add("initial_layer_height", coFloat);
+ def->label = L("Initial layer height");
+ def->tooltip = L("Initial layer height");
+ def->sidetext = L("mm");
+ def->cli = "initial-layer-height=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(0.3);
+
+ def = this->add("exposure_time", coFloat);
+ def->label = L("Exposure time");
+ def->tooltip = L("Exposure time");
+ def->sidetext = L("s");
+ def->cli = "exposure-time=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(10);
+
+ def = this->add("initial_exposure_time", coFloat);
+ def->label = L("Initial exposure time");
+ def->tooltip = L("Initial exposure time");
+ def->sidetext = L("s");
+ def->cli = "initial-exposure-time=f";
+ def->min = 0;
+ def->default_value = new ConfigOptionFloat(15);
+
+ def = this->add("material_correction_printing", coFloats);
+ def->full_label = L("Correction for expansion when printing");
+ def->tooltip = L("Correction for expansion when printing");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } );
+
+ def = this->add("material_correction_curing", coFloats);
+ def->full_label = L("Correction for expansion after curing");
+ def->tooltip = L("Correction for expansion after curing");
+ def->min = 0;
+ def->default_value = new ConfigOptionFloats( { 1. , 1., 1. } );
+
+ def = this->add("material_notes", coString);
+ def->label = L("SLA print material notes");
+ def->tooltip = L("You can put your notes regarding the SLA print material here.");
+ def->cli = "material-notes=s";
+ def->multiline = true;
+ def->full_width = true;
+ def->height = 130;
+ def->default_value = new ConfigOptionString("");
+
+ def = this->add("default_sla_material_profile", coString);
+ def->label = L("Default SLA material profile");
+ def->tooltip = L("Default print profile associated with the current printer profile. "
+ "On selection of the current printer profile, this print profile will be activated.");
+ def->default_value = new ConfigOptionString();
+
+ def = this->add("sla_material_settings_id", coString);
+ def->default_value = new ConfigOptionString("");
}
void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value)
@@ -1792,10 +2297,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
ConfigOptionPoint p;
p.deserialize(value);
std::ostringstream oss;
- oss << "0x0," << p.value.x << "x0," << p.value.x << "x" << p.value.y << ",0x" << p.value.y;
+ oss << "0x0," << p.value(0) << "x0," << p.value(0) << "x" << p.value(1) << ",0x" << p.value(1);
value = oss.str();
- } else if (opt_key == "octoprint_host" && !value.empty()) {
- opt_key = "print_host";
} else if ((opt_key == "perimeter_acceleration" && value == "25")
|| (opt_key == "infill_acceleration" && value == "50")) {
/* For historical reasons, the world's full of configs having these very low values;
@@ -1806,10 +2309,12 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
} else if (opt_key == "support_material_pattern" && value == "pillars") {
// Slic3r PE does not support the pillars. They never worked well.
value = "rectilinear";
- } else if (opt_key == "support_material_threshold" && value == "0") {
- // 0 used to be automatic threshold, but we introduced percent values so let's
- // transform it into the default value
- value = "60%";
+ } else if (opt_key == "octoprint_host") {
+ opt_key = "print_host";
+ } else if (opt_key == "octoprint_cafile") {
+ opt_key = "printhost_cafile";
+ } else if (opt_key == "octoprint_apikey") {
+ opt_key = "printhost_apikey";
}
// Ignore the following obsolete configuration keys:
@@ -1818,16 +2323,16 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"support_material_tool", "acceleration", "adjust_overhang_flow",
"standby_temperature", "scale", "rotate", "duplicate", "duplicate_grid",
"start_perimeters_at_concave_points", "start_perimeters_at_non_overhang", "randomize_start",
- "seal_position", "vibration_limit", "bed_size", "octoprint_host",
- "print_center", "g0", "threads", "pressure_advance"
+ "seal_position", "vibration_limit", "bed_size",
+ "print_center", "g0", "threads", "pressure_advance", "wipe_tower_per_color_wipe"
};
+
if (ignore.find(opt_key) != ignore.end()) {
opt_key = "";
return;
}
if (! print_config_def.has(opt_key)) {
- //printf("Unknown option %s\n", opt_key.c_str());
opt_key = "";
return;
}
@@ -2079,9 +2584,14 @@ std::string FullPrintConfig::validate()
// Declare the static caches for each StaticPrintConfig derived class.
StaticPrintConfig::StaticCache<class Slic3r::PrintObjectConfig> PrintObjectConfig::s_cache_PrintObjectConfig;
StaticPrintConfig::StaticCache<class Slic3r::PrintRegionConfig> PrintRegionConfig::s_cache_PrintRegionConfig;
+StaticPrintConfig::StaticCache<class Slic3r::MachineEnvelopeConfig> MachineEnvelopeConfig::s_cache_MachineEnvelopeConfig;
StaticPrintConfig::StaticCache<class Slic3r::GCodeConfig> GCodeConfig::s_cache_GCodeConfig;
StaticPrintConfig::StaticCache<class Slic3r::PrintConfig> PrintConfig::s_cache_PrintConfig;
StaticPrintConfig::StaticCache<class Slic3r::HostConfig> HostConfig::s_cache_HostConfig;
StaticPrintConfig::StaticCache<class Slic3r::FullPrintConfig> FullPrintConfig::s_cache_FullPrintConfig;
+StaticPrintConfig::StaticCache<class Slic3r::SLAMaterialConfig> SLAMaterialConfig::s_cache_SLAMaterialConfig;
+StaticPrintConfig::StaticCache<class Slic3r::SLAPrinterConfig> SLAPrinterConfig::s_cache_SLAPrinterConfig;
+StaticPrintConfig::StaticCache<class Slic3r::SLAFullPrintConfig> SLAFullPrintConfig::s_cache_SLAFullPrintConfig;
+
}
diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp
index cb94a7921..5bc99a51a 100644
--- a/xs/src/libslic3r/PrintConfig.hpp
+++ b/xs/src/libslic3r/PrintConfig.hpp
@@ -22,11 +22,23 @@
namespace Slic3r {
+enum PrinterTechnology
+{
+ // Fused Filament Fabrication
+ ptFFF,
+ // Stereolitography
+ ptSLA,
+};
+
enum GCodeFlavor {
gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit,
gcfSmoothie, gcfNoExtrusion,
};
+enum PrintHostType {
+ htOctoPrint, htDuet,
+};
+
enum InfillPattern {
ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb,
ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral,
@@ -44,7 +56,16 @@ enum FilamentType {
ftPLA, ftABS, ftPET, ftHIPS, ftFLEX, ftSCAFF, ftEDGE, ftNGEN, ftPVA
};
-template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
+ static t_config_enum_values keys_map;
+ if (keys_map.empty()) {
+ keys_map["FFF"] = ptFFF;
+ keys_map["SLA"] = ptSLA;
+ }
+ return keys_map;
+}
+
+template<> inline const t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["reprap"] = gcfRepRap;
@@ -61,7 +82,16 @@ template<> inline t_config_enum_values& ConfigOptionEnum<GCodeFlavor>::get_enum_
return keys_map;
}
-template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<PrintHostType>::get_enum_values() {
+ static t_config_enum_values keys_map;
+ if (keys_map.empty()) {
+ keys_map["octoprint"] = htOctoPrint;
+ keys_map["duet"] = htDuet;
+ }
+ return keys_map;
+}
+
+template<> inline const t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["rectilinear"] = ipRectilinear;
@@ -81,7 +111,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<InfillPattern>::get_enu
return keys_map;
}
-template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["rectilinear"] = smpRectilinear;
@@ -91,7 +121,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SupportMaterialPattern>
return keys_map;
}
-template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["random"] = spRandom;
@@ -102,7 +132,7 @@ template<> inline t_config_enum_values& ConfigOptionEnum<SeamPosition>::get_enum
return keys_map;
}
-template<> inline t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() {
+template<> inline const t_config_enum_values& ConfigOptionEnum<FilamentType>::get_enum_values() {
static t_config_enum_values keys_map;
if (keys_map.empty()) {
keys_map["PLA"] = ftPLA;
@@ -126,6 +156,11 @@ public:
PrintConfigDef();
static void handle_legacy(t_config_option_key &opt_key, std::string &value);
+
+private:
+ void init_common_params();
+ void init_fff_params();
+ void init_sla_params();
};
// The one and only global definition of SLic3r configuration options.
@@ -154,6 +189,13 @@ public:
// Validate the PrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
+
+ // Verify whether the opt_key has not been obsoleted or renamed.
+ // Both opt_key and value may be modified by handle_legacy().
+ // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
+ // handle_legacy() is called internally by set_deserialize().
+ void handle_legacy(t_config_option_key &opt_key, std::string &value) const override
+ { PrintConfigDef::handle_legacy(opt_key, value); }
};
template<typename CONFIG>
@@ -329,7 +371,8 @@ public:
ConfigOptionBool support_material_with_sheath;
ConfigOptionFloatOrPercent support_material_xy_spacing;
ConfigOptionFloat xy_size_compensation;
-
+ ConfigOptionBool wipe_into_objects;
+
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@@ -365,6 +408,7 @@ protected:
OPT_PTR(support_material_threshold);
OPT_PTR(support_material_with_sheath);
OPT_PTR(xy_size_compensation);
+ OPT_PTR(wipe_into_objects);
}
};
@@ -407,7 +451,8 @@ public:
ConfigOptionFloatOrPercent top_infill_extrusion_width;
ConfigOptionInt top_solid_layers;
ConfigOptionFloatOrPercent top_solid_infill_speed;
-
+ ConfigOptionBool wipe_into_infill;
+
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
@@ -445,6 +490,57 @@ protected:
OPT_PTR(top_infill_extrusion_width);
OPT_PTR(top_solid_infill_speed);
OPT_PTR(top_solid_layers);
+ OPT_PTR(wipe_into_infill);
+ }
+};
+
+class MachineEnvelopeConfig : public StaticPrintConfig
+{
+ STATIC_PRINT_CONFIG_CACHE(MachineEnvelopeConfig)
+public:
+ // M201 X... Y... Z... E... [mm/sec^2]
+ ConfigOptionFloats machine_max_acceleration_x;
+ ConfigOptionFloats machine_max_acceleration_y;
+ ConfigOptionFloats machine_max_acceleration_z;
+ ConfigOptionFloats machine_max_acceleration_e;
+ // M203 X... Y... Z... E... [mm/sec]
+ ConfigOptionFloats machine_max_feedrate_x;
+ ConfigOptionFloats machine_max_feedrate_y;
+ ConfigOptionFloats machine_max_feedrate_z;
+ ConfigOptionFloats machine_max_feedrate_e;
+ // M204 S... [mm/sec^2]
+ ConfigOptionFloats machine_max_acceleration_extruding;
+ // M204 T... [mm/sec^2]
+ ConfigOptionFloats machine_max_acceleration_retracting;
+ // M205 X... Y... Z... E... [mm/sec]
+ ConfigOptionFloats machine_max_jerk_x;
+ ConfigOptionFloats machine_max_jerk_y;
+ ConfigOptionFloats machine_max_jerk_z;
+ ConfigOptionFloats machine_max_jerk_e;
+ // M205 T... [mm/sec]
+ ConfigOptionFloats machine_min_travel_rate;
+ // M205 S... [mm/sec]
+ ConfigOptionFloats machine_min_extruding_rate;
+
+protected:
+ void initialize(StaticCacheBase &cache, const char *base_ptr)
+ {
+ OPT_PTR(machine_max_acceleration_x);
+ OPT_PTR(machine_max_acceleration_y);
+ OPT_PTR(machine_max_acceleration_z);
+ OPT_PTR(machine_max_acceleration_e);
+ OPT_PTR(machine_max_feedrate_x);
+ OPT_PTR(machine_max_feedrate_y);
+ OPT_PTR(machine_max_feedrate_z);
+ OPT_PTR(machine_max_feedrate_e);
+ OPT_PTR(machine_max_acceleration_extruding);
+ OPT_PTR(machine_max_acceleration_retracting);
+ OPT_PTR(machine_max_jerk_x);
+ OPT_PTR(machine_max_jerk_y);
+ OPT_PTR(machine_max_jerk_z);
+ OPT_PTR(machine_max_jerk_e);
+ OPT_PTR(machine_min_travel_rate);
+ OPT_PTR(machine_min_extruding_rate);
}
};
@@ -466,6 +562,18 @@ public:
ConfigOptionBools filament_soluble;
ConfigOptionFloats filament_cost;
ConfigOptionFloats filament_max_volumetric_speed;
+ ConfigOptionFloats filament_loading_speed;
+ ConfigOptionFloats filament_loading_speed_start;
+ ConfigOptionFloats filament_load_time;
+ ConfigOptionFloats filament_unloading_speed;
+ ConfigOptionFloats filament_unloading_speed_start;
+ ConfigOptionFloats filament_toolchange_delay;
+ ConfigOptionFloats filament_unload_time;
+ ConfigOptionInts filament_cooling_moves;
+ ConfigOptionFloats filament_cooling_initial_speed;
+ ConfigOptionFloats filament_minimal_purge_on_wipe_tower;
+ ConfigOptionFloats filament_cooling_final_speed;
+ ConfigOptionStrings filament_ramming_parameters;
ConfigOptionBool gcode_comments;
ConfigOptionEnum<GCodeFlavor> gcode_flavor;
ConfigOptionString layer_gcode;
@@ -485,13 +593,20 @@ public:
ConfigOptionString start_gcode;
ConfigOptionStrings start_filament_gcode;
ConfigOptionBool single_extruder_multi_material;
+ ConfigOptionBool single_extruder_multi_material_priming;
ConfigOptionString toolchange_gcode;
ConfigOptionFloat travel_speed;
ConfigOptionBool use_firmware_retraction;
ConfigOptionBool use_relative_e_distances;
ConfigOptionBool use_volumetric_e;
ConfigOptionBool variable_layer_height;
-
+ ConfigOptionFloat cooling_tube_retraction;
+ ConfigOptionFloat cooling_tube_length;
+ ConfigOptionFloat parking_pos_retraction;
+ ConfigOptionBool remaining_times;
+ ConfigOptionBool silent_mode;
+ ConfigOptionFloat extra_loading_move;
+
std::string get_extrusion_axis() const
{
return
@@ -515,6 +630,18 @@ protected:
OPT_PTR(filament_soluble);
OPT_PTR(filament_cost);
OPT_PTR(filament_max_volumetric_speed);
+ OPT_PTR(filament_loading_speed);
+ OPT_PTR(filament_loading_speed_start);
+ OPT_PTR(filament_load_time);
+ OPT_PTR(filament_unloading_speed);
+ OPT_PTR(filament_unloading_speed_start);
+ OPT_PTR(filament_unload_time);
+ OPT_PTR(filament_toolchange_delay);
+ OPT_PTR(filament_cooling_moves);
+ OPT_PTR(filament_cooling_initial_speed);
+ OPT_PTR(filament_minimal_purge_on_wipe_tower);
+ OPT_PTR(filament_cooling_final_speed);
+ OPT_PTR(filament_ramming_parameters);
OPT_PTR(gcode_comments);
OPT_PTR(gcode_flavor);
OPT_PTR(layer_gcode);
@@ -532,6 +659,7 @@ protected:
OPT_PTR(retract_restart_extra_toolchange);
OPT_PTR(retract_speed);
OPT_PTR(single_extruder_multi_material);
+ OPT_PTR(single_extruder_multi_material_priming);
OPT_PTR(start_gcode);
OPT_PTR(start_filament_gcode);
OPT_PTR(toolchange_gcode);
@@ -540,11 +668,17 @@ protected:
OPT_PTR(use_relative_e_distances);
OPT_PTR(use_volumetric_e);
OPT_PTR(variable_layer_height);
+ OPT_PTR(cooling_tube_retraction);
+ OPT_PTR(cooling_tube_length);
+ OPT_PTR(parking_pos_retraction);
+ OPT_PTR(remaining_times);
+ OPT_PTR(silent_mode);
+ OPT_PTR(extra_loading_move);
}
};
// This object is mapped to Perl as Slic3r::Config::Print.
-class PrintConfig : public GCodeConfig
+class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig
{
STATIC_PRINT_CONFIG_CACHE_DERIVED(PrintConfig)
PrintConfig() : GCodeConfig(0) { initialize_cache(); *this = s_cache_PrintConfig.defaults(); }
@@ -592,6 +726,7 @@ public:
ConfigOptionString output_filename_format;
ConfigOptionFloat perimeter_acceleration;
ConfigOptionStrings post_process;
+ ConfigOptionString printer_model;
ConfigOptionString printer_notes;
ConfigOptionFloat resolution;
ConfigOptionFloats retract_before_travel;
@@ -610,12 +745,23 @@ public:
ConfigOptionFloat wipe_tower_y;
ConfigOptionFloat wipe_tower_width;
ConfigOptionFloat wipe_tower_per_color_wipe;
+ ConfigOptionFloat wipe_tower_rotation_angle;
+ ConfigOptionFloat wipe_tower_bridging;
+ ConfigOptionFloats wiping_volumes_matrix;
+ ConfigOptionFloats wiping_volumes_extruders;
ConfigOptionFloat z_offset;
+ ConfigOptionFloat bed_size_x;
+ ConfigOptionFloat bed_size_y;
+ ConfigOptionInt pixel_width;
+ ConfigOptionInt pixel_height;
+ ConfigOptionFloat exp_time;
+ ConfigOptionFloat exp_time_first;
protected:
PrintConfig(int) : GCodeConfig(1) {}
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
+ this->MachineEnvelopeConfig::initialize(cache, base_ptr);
this->GCodeConfig::initialize(cache, base_ptr);
OPT_PTR(avoid_crossing_perimeters);
OPT_PTR(bed_shape);
@@ -657,6 +803,7 @@ protected:
OPT_PTR(output_filename_format);
OPT_PTR(perimeter_acceleration);
OPT_PTR(post_process);
+ OPT_PTR(printer_model);
OPT_PTR(printer_notes);
OPT_PTR(resolution);
OPT_PTR(retract_before_travel);
@@ -675,7 +822,17 @@ protected:
OPT_PTR(wipe_tower_y);
OPT_PTR(wipe_tower_width);
OPT_PTR(wipe_tower_per_color_wipe);
+ OPT_PTR(wipe_tower_rotation_angle);
+ OPT_PTR(wipe_tower_bridging);
+ OPT_PTR(wiping_volumes_matrix);
+ OPT_PTR(wiping_volumes_extruders);
OPT_PTR(z_offset);
+ OPT_PTR(bed_size_x);
+ OPT_PTR(bed_size_y);
+ OPT_PTR(pixel_width);
+ OPT_PTR(pixel_height);
+ OPT_PTR(exp_time);
+ OPT_PTR(exp_time_first);
}
};
@@ -683,18 +840,20 @@ class HostConfig : public StaticPrintConfig
{
STATIC_PRINT_CONFIG_CACHE(HostConfig)
public:
- ConfigOptionString octoprint_host;
- ConfigOptionString octoprint_apikey;
- ConfigOptionString octoprint_cafile;
+ ConfigOptionEnum<PrintHostType> host_type;
+ ConfigOptionString print_host;
+ ConfigOptionString printhost_apikey;
+ ConfigOptionString printhost_cafile;
ConfigOptionString serial_port;
ConfigOptionInt serial_speed;
protected:
void initialize(StaticCacheBase &cache, const char *base_ptr)
{
- OPT_PTR(octoprint_host);
- OPT_PTR(octoprint_apikey);
- OPT_PTR(octoprint_cafile);
+ OPT_PTR(host_type);
+ OPT_PTR(print_host);
+ OPT_PTR(printhost_apikey);
+ OPT_PTR(printhost_cafile);
OPT_PTR(serial_port);
OPT_PTR(serial_speed);
}
@@ -713,6 +872,7 @@ class FullPrintConfig :
public:
// Validate the FullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
std::string validate();
+
protected:
// Protected constructor to be called to initialize ConfigCache::m_default.
FullPrintConfig(int) : PrintObjectConfig(0), PrintRegionConfig(0), PrintConfig(0), HostConfig(0) {}
@@ -725,6 +885,73 @@ protected:
}
};
+class SLAMaterialConfig : public StaticPrintConfig
+{
+ STATIC_PRINT_CONFIG_CACHE(SLAMaterialConfig)
+public:
+ ConfigOptionFloat layer_height;
+ ConfigOptionFloat initial_layer_height;
+ ConfigOptionFloat exposure_time;
+ ConfigOptionFloat initial_exposure_time;
+ ConfigOptionFloats material_correction_printing;
+ ConfigOptionFloats material_correction_curing;
+protected:
+ void initialize(StaticCacheBase &cache, const char *base_ptr)
+ {
+ OPT_PTR(layer_height);
+ OPT_PTR(initial_layer_height);
+ OPT_PTR(exposure_time);
+ OPT_PTR(initial_exposure_time);
+ OPT_PTR(material_correction_printing);
+ OPT_PTR(material_correction_curing);
+ }
+};
+
+class SLAPrinterConfig : public StaticPrintConfig
+{
+ STATIC_PRINT_CONFIG_CACHE(SLAPrinterConfig)
+public:
+ ConfigOptionEnum<PrinterTechnology> printer_technology;
+ ConfigOptionPoints bed_shape;
+ ConfigOptionFloat max_print_height;
+ ConfigOptionFloat display_width;
+ ConfigOptionFloat display_height;
+ ConfigOptionInt display_pixels_x;
+ ConfigOptionInt display_pixels_y;
+ ConfigOptionFloats printer_correction;
+protected:
+ void initialize(StaticCacheBase &cache, const char *base_ptr)
+ {
+ OPT_PTR(printer_technology);
+ OPT_PTR(bed_shape);
+ OPT_PTR(max_print_height);
+ OPT_PTR(display_width);
+ OPT_PTR(display_height);
+ OPT_PTR(display_pixels_x);
+ OPT_PTR(display_pixels_y);
+ OPT_PTR(printer_correction);
+ }
+};
+
+class SLAFullPrintConfig : public SLAPrinterConfig, public SLAMaterialConfig
+{
+ STATIC_PRINT_CONFIG_CACHE_DERIVED(SLAFullPrintConfig)
+ SLAFullPrintConfig() : SLAPrinterConfig(0), SLAMaterialConfig(0) { initialize_cache(); *this = s_cache_SLAFullPrintConfig.defaults(); }
+
+public:
+ // Validate the SLAFullPrintConfig. Returns an empty string on success, otherwise an error message is returned.
+// std::string validate();
+
+protected:
+ // Protected constructor to be called to initialize ConfigCache::m_default.
+ SLAFullPrintConfig(int) : SLAPrinterConfig(0), SLAMaterialConfig(0) {}
+ void initialize(StaticCacheBase &cache, const char *base_ptr)
+ {
+ this->SLAPrinterConfig ::initialize(cache, base_ptr);
+ this->SLAMaterialConfig::initialize(cache, base_ptr);
+ }
+};
+
#undef STATIC_PRINT_CONFIG_CACHE
#undef STATIC_PRINT_CONFIG_CACHE_BASE
#undef STATIC_PRINT_CONFIG_CACHE_DERIVED
diff --git a/xs/src/libslic3r/PrintExport.hpp b/xs/src/libslic3r/PrintExport.hpp
new file mode 100644
index 000000000..7c3871251
--- /dev/null
+++ b/xs/src/libslic3r/PrintExport.hpp
@@ -0,0 +1,383 @@
+#ifndef PRINTEXPORT_HPP
+#define PRINTEXPORT_HPP
+
+#include "Print.hpp"
+
+// For png export of the sliced model
+#include <fstream>
+#include <sstream>
+
+#include <wx/stdstream.h>
+#include <wx/wfstream.h>
+#include <wx/zipstrm.h>
+
+#include <boost/log/trivial.hpp>
+
+#include "Rasterizer/Rasterizer.hpp"
+#include <tbb/parallel_for.h>
+#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
+
+namespace Slic3r {
+
+enum class FilePrinterFormat {
+ PNG,
+ SVG
+};
+
+/*
+ * Interface for a file printer of the slices. Implementation can be an SVG
+ * or PNG printer or any other format.
+ *
+ * The format argument specifies the output format of the printer and it enables
+ * different implementations of this class template for each supported format.
+ *
+ */
+template<FilePrinterFormat format>
+class FilePrinter {
+public:
+
+ void printConfig(const Print&);
+
+ // Draw an ExPolygon which is a polygon inside a slice on the specified layer.
+ void drawPolygon(const ExPolygon& p, unsigned lyr);
+
+ // Tell the printer how many layers should it consider.
+ void layers(unsigned layernum);
+
+ // Get the number of layers in the print.
+ unsigned layers() const;
+
+ /* Switch to a particular layer. If there where less layers then the
+ * specified layer number than an appropriate number of layers will be
+ * allocated in the printer.
+ */
+ void beginLayer(unsigned layer);
+
+ // Allocate a new layer on top of the last and switch to it.
+ void beginLayer();
+
+ /*
+ * Finish the selected layer. It means that no drawing is allowed on that
+ * layer anymore. This fact can be used to prepare the file system output
+ * data like png comprimation and so on.
+ */
+ void finishLayer(unsigned layer);
+
+ // Finish the top layer.
+ void finishLayer();
+
+ // Save all the layers into the file (or dir) specified in the path argument
+ void save(const std::string& path);
+
+ // Save only the selected layer to the file specified in path argument.
+ void saveLayer(unsigned lyr, const std::string& path);
+};
+
+// Implementation for PNG raster output
+// Be aware that if a large number of layers are allocated, it can very well
+// exhaust the available memory especially on 32 bit platform.
+template<> class FilePrinter<FilePrinterFormat::PNG> {
+
+ struct Layer {
+ Raster first;
+ std::stringstream second;
+
+ Layer() {}
+
+ Layer(const Layer&) = delete;
+ Layer(Layer&& m):
+ first(std::move(m.first))/*, second(std::move(m.second))*/ {}
+ };
+
+ // We will save the compressed PNG data into stringstreams which can be done
+ // in parallel. Later we can write every layer to the disk sequentially.
+ std::vector<Layer> layers_rst_;
+ Raster::Resolution res_;
+ Raster::PixelDim pxdim_;
+ const Print *print_ = nullptr;
+ double exp_time_s_ = .0, exp_time_first_s_ = .0;
+
+ std::string createIniContent(const std::string& projectname) {
+ double layer_height = print_?
+ print_->default_object_config().layer_height.getFloat() :
+ 0.05;
+
+ using std::string;
+ using std::to_string;
+
+ auto expt_str = to_string(exp_time_s_);
+ auto expt_first_str = to_string(exp_time_first_s_);
+ auto stepnum_str = to_string(static_cast<unsigned>(800*layer_height));
+ auto layerh_str = to_string(layer_height);
+
+ return string(
+ "action = print\n"
+ "jobDir = ") + projectname + "\n" +
+ "expTime = " + expt_str + "\n"
+ "expTimeFirst = " + expt_first_str + "\n"
+ "stepNum = " + stepnum_str + "\n"
+ "wifiOn = 1\n"
+ "tiltSlow = 60\n"
+ "tiltFast = 15\n"
+ "numFade = 10\n"
+ "startdelay = 0\n"
+ "layerHeight = " + layerh_str + "\n"
+ "noteInfo = "
+ "expTime="+expt_str+"+resinType=generic+layerHeight="
+ +layerh_str+"+printer=DWARF3\n";
+ }
+
+ // Change this to TOP_LEFT if you want correct PNG orientation
+ static const Raster::Origin ORIGIN = Raster::Origin::BOTTOM_LEFT;
+
+public:
+ inline FilePrinter(double width_mm, double height_mm,
+ unsigned width_px, unsigned height_px,
+ double exp_time, double exp_time_first):
+ res_(width_px, height_px),
+ pxdim_(width_mm/width_px, height_mm/height_px),
+ exp_time_s_(exp_time),
+ exp_time_first_s_(exp_time_first)
+ {
+ }
+
+ FilePrinter(const FilePrinter& ) = delete;
+ FilePrinter(FilePrinter&& m):
+ layers_rst_(std::move(m.layers_rst_)),
+ res_(m.res_),
+ pxdim_(m.pxdim_) {}
+
+ inline void layers(unsigned cnt) { if(cnt > 0) layers_rst_.resize(cnt); }
+ inline unsigned layers() const { return layers_rst_.size(); }
+
+ void printConfig(const Print& printconf) { print_ = &printconf; }
+
+ inline void drawPolygon(const ExPolygon& p, unsigned lyr) {
+ assert(lyr < layers_rst_.size());
+ layers_rst_[lyr].first.draw(p);
+ }
+
+ inline void beginLayer(unsigned lyr) {
+ if(layers_rst_.size() <= lyr) layers_rst_.resize(lyr+1);
+ layers_rst_[lyr].first.reset(res_, pxdim_, ORIGIN);
+ }
+
+ inline void beginLayer() {
+ layers_rst_.emplace_back();
+ layers_rst_.front().first.reset(res_, pxdim_, ORIGIN);
+ }
+
+ inline void finishLayer(unsigned lyr_id) {
+ assert(lyr_id < layers_rst_.size());
+ layers_rst_[lyr_id].first.save(layers_rst_[lyr_id].second,
+ Raster::Compression::PNG);
+ layers_rst_[lyr_id].first.reset();
+ }
+
+ inline void finishLayer() {
+ if(!layers_rst_.empty()) {
+ layers_rst_.back().first.save(layers_rst_.back().second,
+ Raster::Compression::PNG);
+ layers_rst_.back().first.reset();
+ }
+ }
+
+ inline void save(const std::string& path) {
+
+ wxFileName filepath(path);
+
+ wxFFileOutputStream zipfile(path);
+
+ std::string project = filepath.GetName().ToStdString();
+
+ if(!zipfile.IsOk()) {
+ BOOST_LOG_TRIVIAL(error) << "Can't create zip file for layers! "
+ << path;
+ return;
+ }
+
+ wxZipOutputStream zipstream(zipfile);
+ wxStdOutputStream pngstream(zipstream);
+
+ zipstream.PutNextEntry("config.ini");
+ pngstream << createIniContent(project);
+
+ for(unsigned i = 0; i < layers_rst_.size(); i++) {
+ if(layers_rst_[i].second.rdbuf()->in_avail() > 0) {
+ char lyrnum[6];
+ std::sprintf(lyrnum, "%.5d", i);
+ auto zfilename = project + lyrnum + ".png";
+ zipstream.PutNextEntry(zfilename);
+ pngstream << layers_rst_[i].second.rdbuf();
+ layers_rst_[i].second.str("");
+ }
+ }
+
+ zipstream.Close();
+ zipfile.Close();
+ }
+
+ void saveLayer(unsigned lyr, const std::string& path) {
+ unsigned i = lyr;
+ assert(i < layers_rst_.size());
+
+ char lyrnum[6];
+ std::sprintf(lyrnum, "%.5d", lyr);
+ std::string loc = path + "layer" + lyrnum + ".png";
+
+ std::fstream out(loc, std::fstream::out | std::fstream::binary);
+ if(out.good()) {
+ layers_rst_[i].first.save(out, Raster::Compression::PNG);
+ } else {
+ BOOST_LOG_TRIVIAL(error) << "Can't create file for layer";
+ }
+
+ out.close();
+ layers_rst_[i].first.reset();
+ }
+};
+
+// Let's shadow this eigen interface
+inline coord_t px(const Point& p) { return p(0); }
+inline coord_t py(const Point& p) { return p(1); }
+inline coordf_t px(const Vec2d& p) { return p(0); }
+inline coordf_t py(const Vec2d& p) { return p(1); }
+
+template<FilePrinterFormat format, class...Args>
+void print_to(Print& print,
+ std::string dirpath,
+ double width_mm,
+ double height_mm,
+ Args&&...args)
+{
+
+ std::string& dir = dirpath;
+
+ // This map will hold the layers sorted by z coordinate. Layers on the
+ // same height (from different objects) will be mapped to the same key and
+ // rasterized to the same image.
+ std::map<long long, LayerPtrs> layers;
+
+ auto& objects = print.objects();
+
+ // Merge the sliced layers with the support layers
+ std::for_each(objects.cbegin(), objects.cend(), [&layers](const PrintObject *o) {
+ for(const auto l : o->layers()) {
+ auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
+ lyrs.push_back(l);
+ }
+
+ for(const auto l : o->support_layers()) {
+ auto& lyrs = layers[static_cast<long long>(scale_(l->print_z))];
+ lyrs.push_back(l);
+ }
+ });
+
+ auto print_bb = print.bounding_box();
+ Vec2d punsc = unscale(print_bb.size());
+
+ // If the print does not fit into the print area we should cry about it.
+ if(px(punsc) > width_mm || py(punsc) > height_mm) {
+ BOOST_LOG_TRIVIAL(warning) << "Warning: Print will not fit!" << "\n"
+ << "Width needed: " << px(punsc) << "\n"
+ << "Height needed: " << py(punsc) << "\n";
+ }
+
+ // Offset for centering the print onto the print area
+ auto cx = scale_(width_mm)/2 - (px(print_bb.center()) - px(print_bb.min));
+ auto cy = scale_(height_mm)/2 - (py(print_bb.center()) - py(print_bb.min));
+
+ // Create the actual printer, forward any additional arguments to it.
+ FilePrinter<format> printer(width_mm, height_mm,
+ std::forward<Args>(args)...);
+
+ printer.printConfig(print);
+
+ printer.layers(layers.size()); // Allocate space for all the layers
+
+ int st_prev = 0;
+ const std::string jobdesc = "Rasterizing and compressing sliced layers";
+ tbb::spin_mutex m;
+
+ std::vector<long long> keys;
+ keys.reserve(layers.size());
+ for(auto& e : layers) keys.push_back(e.first);
+
+ //FIXME
+ int initstatus = //print.progressindicator? print.progressindicator->state() :
+ 0;
+ print.set_status(initstatus, jobdesc);
+
+ // Method that prints one layer
+ auto process_layer = [&layers, &keys, &printer, &st_prev, &m,
+ &jobdesc, print_bb, dir, cx, cy, &print, initstatus]
+ (unsigned layer_id)
+ {
+ LayerPtrs lrange = layers[keys[layer_id]];
+
+ printer.beginLayer(layer_id); // Switch to the appropriate layer
+
+ for(Layer *lp : lrange) {
+ Layer& l = *lp;
+
+ ExPolygonCollection slices = l.slices; // Copy the layer slices
+
+ // Sort the polygons in the layer
+ std::stable_sort(slices.expolygons.begin(), slices.expolygons.end(),
+ [](const ExPolygon& a, const ExPolygon& b) {
+ return a.contour.contains(b.contour.first_point()) ? false :
+ true;
+ });
+
+ // Draw all the polygons in the slice to the actual layer.
+ for (const Point &d : l.object()->copies())
+ for (ExPolygon slice : slices.expolygons) {
+ slice.translate(px(d), py(d));
+ slice.translate(-px(print_bb.min) + cx,
+ -py(print_bb.min) + cy);
+
+ printer.drawPolygon(slice, layer_id);
+ }
+
+ /*if(print.has_support_material() && layer_id > 0) {
+ BOOST_LOG_TRIVIAL(warning) << "support material for layer "
+ << layer_id
+ << " defined but export is "
+ "not yet implemented.";
+
+ }*/
+
+ }
+
+ printer.finishLayer(layer_id); // Finish the layer for later saving it.
+
+ auto st = static_cast<int>(layer_id*80.0/layers.size());
+ m.lock();
+ if( st - st_prev > 10) {
+ print.set_status(initstatus + st, jobdesc);
+ st_prev = st;
+ }
+ m.unlock();
+
+ // printer.saveLayer(layer_id, dir); We could save the layer immediately
+ };
+
+ // Print all the layers in parallel
+ tbb::parallel_for<size_t, decltype(process_layer)>(0,
+ layers.size(),
+ process_layer);
+
+ // Sequential version (for testing)
+ // for(unsigned l = 0; l < layers.size(); ++l) process_layer(l);
+
+// print.set_status(100, jobdesc);
+
+ // Save the print into the file system.
+ print.set_status(initstatus + 90, "Writing layers to disk");
+ printer.save(dir);
+ print.set_status(initstatus + 100, "Writing layers completed");
+}
+
+}
+
+#endif // PRINTEXPORT_HPP
diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp
index ba2521cd7..96c831157 100644
--- a/xs/src/libslic3r/PrintObject.cpp
+++ b/xs/src/libslic3r/PrintObject.cpp
@@ -4,6 +4,7 @@
#include "Geometry.hpp"
#include "SupportMaterial.hpp"
#include "Surface.hpp"
+#include "Slicing.hpp"
#include <utility>
#include <boost/log/trivial.hpp>
@@ -37,6 +38,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
typed_slices(false),
m_print(print),
m_model_object(model_object),
+ size(Vec3crd::Zero()),
layer_height_profile_valid(false)
{
// Compute the translation to be applied to our meshes so that we work with smaller coordinates
@@ -47,10 +49,9 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
// don't assume it's already aligned and we don't alter the original position in model.
// We store the XY translation so that we can place copies correctly in the output G-code
// (copies are expressed in G-code coordinates and this translation is not publicly exposed).
- this->_copies_shift = Point::new_scale(modobj_bbox.min.x, modobj_bbox.min.y);
+ m_copies_shift = Point::new_scale(modobj_bbox.min(0), modobj_bbox.min(1));
// Scale the object size and store it
- Pointf3 size = modobj_bbox.size();
- this->size = Point3::new_scale(size.x, size.y, size.z);
+ this->size = (modobj_bbox.size() * (1. / SCALING_FACTOR)).cast<coord_t>();
}
this->reload_model_instances();
@@ -58,10 +59,10 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, const Bounding
this->layer_height_profile = model_object->layer_height_profile;
}
-bool PrintObject::add_copy(const Pointf &point)
+bool PrintObject::add_copy(const Vec2d &point)
{
Points points = m_copies;
- points.push_back(Point::new_scale(point.x, point.y));
+ points.push_back(Point::new_scale(point(0), point(1)));
return this->set_copies(points);
}
@@ -74,24 +75,23 @@ bool PrintObject::delete_last_copy()
bool PrintObject::set_copies(const Points &points)
{
- m_copies = points;
+ bool copies_num_changed = m_copies.size() != points.size();
// order copies with a nearest neighbor search and translate them by _copies_shift
- this->_shifted_copies.clear();
- this->_shifted_copies.reserve(points.size());
+ m_copies.clear();
+ m_copies.reserve(points.size());
// order copies with a nearest-neighbor search
std::vector<Points::size_type> ordered_copies;
Slic3r::Geometry::chained_path(points, ordered_copies);
- for (size_t point_idx : ordered_copies) {
- Point copy = points[point_idx];
- copy.translate(this->_copies_shift);
- this->_shifted_copies.push_back(copy);
- }
+ for (size_t point_idx : ordered_copies)
+ m_copies.push_back(points[point_idx] + m_copies_shift);
bool invalidated = m_print->invalidate_step(psSkirt);
invalidated |= m_print->invalidate_step(psBrim);
+ if (copies_num_changed)
+ invalidated |= m_print->invalidate_step(psWipeTower);
return invalidated;
}
@@ -100,7 +100,8 @@ bool PrintObject::reload_model_instances()
Points copies;
copies.reserve(m_model_object->instances.size());
for (const ModelInstance *mi : m_model_object->instances)
- copies.emplace_back(Point::new_scale(mi->offset.x, mi->offset.y));
+ if (mi->is_printable())
+ copies.emplace_back(Point::new_scale(mi->offset(0), mi->offset(1)));
return this->set_copies(copies);
}
@@ -115,7 +116,7 @@ bool PrintObject::reload_model_instances()
// this should be idempotent
void PrintObject::slice()
{
- if (m_state.is_done(posSlice))
+ if (this->is_step_done(posSlice))
return;
this->set_started(posSlice);
m_print->set_status(10, "Processing triangulated mesh");
@@ -143,7 +144,7 @@ void PrintObject::make_perimeters()
// prerequisites
this->slice();
- if (m_state.is_done(posPerimeters))
+ if (this->is_step_done(posPerimeters))
return;
this->set_started(posPerimeters);
@@ -168,12 +169,8 @@ void PrintObject::make_perimeters()
// inside the object - infill_only_where_needed should be the method of choice for printing
// hollow objects
for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
- const PrintRegion &region = *m_print->regions()[region_id];
-
- if (!region.config().extra_perimeters
- || region.config().perimeters == 0
- || region.config().fill_density == 0
- || this->layer_count() < 2)
+ const PrintRegion &region = *m_print->regions()[region_id];
+ if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
continue;
BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
@@ -259,7 +256,7 @@ void PrintObject::make_perimeters()
void PrintObject::prepare_infill()
{
- if (m_state.is_done(posPrepareInfill))
+ if (this->is_step_done(posPrepareInfill))
return;
this->set_started(posPrepareInfill);
@@ -375,10 +372,13 @@ void PrintObject::prepare_infill()
void PrintObject::infill()
{
+ if (! this->is_printable())
+ return;
+
// prerequisites
this->prepare_infill();
- if (! m_state.is_done(posInfill)) {
+ if (! this->is_step_done(posInfill)) {
this->set_started(posInfill);
BOOST_LOG_TRIVIAL(debug) << "Filling layers in parallel - start";
tbb::parallel_for(
@@ -401,7 +401,7 @@ void PrintObject::infill()
void PrintObject::generate_support_material()
{
- if (! m_state.is_done(posSupportMaterial)) {
+ if (! this->is_step_done(posSupportMaterial)) {
this->set_started(posSupportMaterial);
this->clear_support_layers();
if ((m_config.support_material || m_config.raft_layers > 0) && m_layers.size() > 1) {
@@ -545,7 +545,10 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_
|| opt_key == "perimeter_speed"
|| opt_key == "small_perimeter_speed"
|| opt_key == "solid_infill_speed"
- || opt_key == "top_solid_infill_speed") {
+ || opt_key == "top_solid_infill_speed"
+ || opt_key == "wipe_into_infill" // when these these two are changed, we only need to invalidate the wipe tower,
+ || opt_key == "wipe_into_objects" // which we already did at the very beginning - nothing more to be done
+ ) {
// these options only affect G-code export, so nothing to invalidate
} else {
// for legacy, if we can't handle this option let's invalidate all steps
@@ -585,6 +588,8 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
}
// Wipe tower depends on the ordering of extruders, which in turn depends on everything.
+ // It also decides about what the wipe_into_infill / wipe_into_object features will do,
+ // and that too depends on many of the settings.
invalidated |= m_print->invalidate_step(psWipeTower);
return invalidated;
}
@@ -1341,7 +1346,7 @@ SlicingParameters PrintObject::slicing_parameters() const
{
return SlicingParameters::create_from_config(
this->print()->config(), m_config,
- unscale(this->size.z), this->print()->object_extruders());
+ unscale<double>(this->size(2)), this->print()->object_extruders());
}
bool PrintObject::update_layer_height_profile(std::vector<coordf_t> &layer_height_profile) const
@@ -1402,8 +1407,8 @@ void PrintObject::_slice()
this->typed_slices = false;
-#if 0
- // Disable parallelization for debugging purposes.
+#ifdef SLIC3R_PROFILE
+ // Disable parallelization so the Shiny profiler works
static tbb::task_scheduler_init *tbb_init = nullptr;
tbb_init = new tbb::task_scheduler_init(1);
#endif
@@ -1461,7 +1466,7 @@ void PrintObject::_slice()
if (region_id == other_region_id)
continue;
for (size_t layer_id = 0; layer_id < expolygons_by_layer.size(); ++ layer_id) {
- Layer *layer = this->layers()[layer_id];
+ Layer *layer = m_layers[layer_id];
LayerRegion *layerm = layer->m_regions[region_id];
LayerRegion *other_layerm = layer->m_regions[other_region_id];
if (layerm == nullptr || other_layerm == nullptr)
@@ -1563,7 +1568,7 @@ std::vector<ExPolygons> PrintObject::_slice_region(size_t region_id, const std::
// 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(- float(unscale(this->_copies_shift.x)), - float(unscale(this->_copies_shift.y)), -float(this->model_object()->bounding_box().min.z));
+ mesh.translate(- unscale<float>(m_copies_shift(0)), - unscale<float>(m_copies_shift(1)), - float(this->model_object()->bounding_box().min(2)));
// perform actual slicing
TriangleMeshSlicer mslicer;
Print *print = this->print();
@@ -1680,6 +1685,113 @@ void PrintObject::_simplify_slices(double distance)
BOOST_LOG_TRIVIAL(debug) << "Slicing objects - siplifying slices in parallel - end";
}
+void PrintObject::_make_perimeters()
+{
+ if (!this->is_printable())
+ return;
+
+ if (this->is_step_done(posPerimeters))
+ return;
+ this->set_started(posPerimeters);
+
+ BOOST_LOG_TRIVIAL(info) << "Generating perimeters...";
+
+ // 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->invalidate_step(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
+ for (size_t region_id = 0; region_id < m_print->regions().size(); ++ region_id) {
+ const PrintRegion &region = *m_print->regions()[region_id];
+ if (! region.config().extra_perimeters || region.config().perimeters == 0 || region.config().fill_density == 0 || this->layer_count() < 2)
+ continue;
+
+ BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - start";
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, m_layers.size() - 1),
+ [this, &region, region_id](const tbb::blocked_range<size_t>& range) {
+ for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) {
+ LayerRegion &layerm = *m_layers[layer_idx]->regions()[region_id];
+ const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->regions()[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} ];
+ const double total_loop_length = total_length(upper_layerm_polygons);
+ 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 (Surface &slice : layerm.slices.surfaces) {
+ for (;;) {
+ // compute the total thickness of perimeters
+ const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2
+ + (region.config().perimeters-1 + slice.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 = coord_t(perimeter_spacing * 1.5);
+ const Polygons critical_area = diff(
+ offset(slice.expolygon, float(- perimeters_thickness)),
+ offset(slice.expolygon, float(- 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
+ if (total_length(intersection) <= 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, layer_idx);
+ #endif
+ }
+ }
+ });
+ BOOST_LOG_TRIVIAL(debug) << "Generating extra perimeters for region " << region_id << " in parallel - end";
+ }
+
+ BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - start";
+ tbb::parallel_for(
+ tbb::blocked_range<size_t>(0, m_layers.size()),
+ [this](const tbb::blocked_range<size_t>& range) {
+ for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx)
+ m_layers[layer_idx]->make_perimeters();
+ }
+ );
+ BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
+
+ /*
+ 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->set_done(posPerimeters);
+}
+
// Only active if config->infill_only_where_needed. This step trims the sparse infill,
// so it acts as an internal support. It maintains all other infill types intact.
// Here the internal surfaces and perimeters have to be supported by the sparse infill.
@@ -2069,6 +2181,9 @@ void PrintObject::combine_infill()
void PrintObject::_generate_support_material()
{
+ if (!this->is_printable())
+ return;
+
PrintObjectSupportMaterial support_material(this, PrintObject::slicing_parameters());
support_material.generate(*this);
}
@@ -2083,4 +2198,12 @@ void PrintObject::reset_layer_height_profile()
this->model_object()->layer_height_profile_valid = false;
}
+void PrintObject::adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action)
+{
+ update_layer_height_profile(m_model_object->layer_height_profile);
+ Slic3r::adjust_layer_height_profile(slicing_parameters(), m_model_object->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
+ m_model_object->layer_height_profile_valid = true;
+ layer_height_profile_valid = false;
+}
+
} // namespace Slic3r
diff --git a/xs/src/libslic3r/Rasterizer/Rasterizer.cpp b/xs/src/libslic3r/Rasterizer/Rasterizer.cpp
new file mode 100644
index 000000000..b0bf04343
--- /dev/null
+++ b/xs/src/libslic3r/Rasterizer/Rasterizer.cpp
@@ -0,0 +1,214 @@
+#include "Rasterizer.hpp"
+#include <ExPolygon.hpp>
+
+#include <cstdint>
+
+// For rasterizing
+#include <agg/agg_basics.h>
+#include <agg/agg_rendering_buffer.h>
+#include <agg/agg_pixfmt_gray.h>
+#include <agg/agg_pixfmt_rgb.h>
+#include <agg/agg_renderer_base.h>
+#include <agg/agg_renderer_scanline.h>
+
+#include <agg/agg_scanline_p.h>
+#include <agg/agg_rasterizer_scanline_aa.h>
+#include <agg/agg_path_storage.h>
+
+// For png compression
+#include <png/writer.hpp>
+
+namespace Slic3r {
+
+class Raster::Impl {
+public:
+ using TPixelRenderer = agg::pixfmt_gray8; // agg::pixfmt_rgb24;
+ using TRawRenderer = agg::renderer_base<TPixelRenderer>;
+ using TPixel = TPixelRenderer::color_type;
+ using TRawBuffer = agg::rendering_buffer;
+
+ using TBuffer = std::vector<TPixelRenderer::pixel_type>;
+
+ using TRendererAA = agg::renderer_scanline_aa_solid<TRawRenderer>;
+
+ static const TPixel ColorWhite;
+ static const TPixel ColorBlack;
+
+ using Origin = Raster::Origin;
+
+private:
+ Raster::Resolution resolution_;
+ Raster::PixelDim pxdim_;
+ TBuffer buf_;
+ TRawBuffer rbuf_;
+ TPixelRenderer pixfmt_;
+ TRawRenderer raw_renderer_;
+ TRendererAA renderer_;
+ Origin o_;
+ std::function<void(agg::path_storage&)> flipy_ = [](agg::path_storage&) {};
+public:
+ inline Impl(const Raster::Resolution& res, const Raster::PixelDim &pd,
+ Origin o):
+ resolution_(res), pxdim_(pd),
+ buf_(res.pixels()),
+ rbuf_(reinterpret_cast<TPixelRenderer::value_type*>(buf_.data()),
+ res.width_px, res.height_px,
+ res.width_px*TPixelRenderer::num_components),
+ pixfmt_(rbuf_),
+ raw_renderer_(pixfmt_),
+ renderer_(raw_renderer_),
+ o_(o)
+ {
+ renderer_.color(ColorWhite);
+
+ // If we would like to play around with gamma
+ // ras.gamma(agg::gamma_power(1.0));
+
+ clear();
+
+ if(o_ == Origin::TOP_LEFT) flipy_ = [this](agg::path_storage& path) {
+ path.flip_y(0, resolution_.height_px);
+ };
+ }
+
+ void draw(const ExPolygon &poly) {
+ agg::rasterizer_scanline_aa<> ras;
+ agg::scanline_p8 scanlines;
+
+ auto&& path = to_path(poly.contour);
+ flipy_(path);
+ ras.add_path(path);
+
+ for(auto h : poly.holes) {
+ auto&& holepath = to_path(h);
+ flipy_(holepath);
+ ras.add_path(holepath);
+ }
+
+ agg::render_scanlines(ras, scanlines, renderer_);
+ }
+
+ inline void clear() {
+ raw_renderer_.clear(ColorBlack);
+ }
+
+ inline TBuffer& buffer() { return buf_; }
+
+ inline const Raster::Resolution resolution() { return resolution_; }
+
+ inline Origin origin() const /*noexcept*/ { return o_; }
+
+private:
+ double getPx(const Point& p) {
+ return p(0) * SCALING_FACTOR/pxdim_.w_mm;
+ }
+
+ double getPy(const Point& p) {
+ return p(1) * SCALING_FACTOR/pxdim_.h_mm;
+ }
+
+ agg::path_storage to_path(const Polygon& poly) {
+ agg::path_storage path;
+ auto it = poly.points.begin();
+ path.move_to(getPx(*it), getPy(*it));
+ while(++it != poly.points.end())
+ path.line_to(getPx(*it), getPy(*it));
+
+ path.line_to(getPx(poly.points.front()), getPy(poly.points.front()));
+ return path;
+ }
+
+};
+
+const Raster::Impl::TPixel Raster::Impl::ColorWhite = Raster::Impl::TPixel(255);
+const Raster::Impl::TPixel Raster::Impl::ColorBlack = Raster::Impl::TPixel(0);
+
+Raster::Raster(const Resolution &r, const PixelDim &pd, Origin o):
+ impl_(new Impl(r, pd, o)) {}
+
+Raster::Raster() {}
+
+Raster::~Raster() {}
+
+Raster::Raster(Raster &&m):
+ impl_(std::move(m.impl_)) {}
+
+void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd)
+{
+ // Free up the unnecessary memory and make sure it stays clear after
+ // an exception
+ auto o = impl_? impl_->origin() : Origin::TOP_LEFT;
+ reset(r, pd, o);
+}
+
+void Raster::reset(const Raster::Resolution &r, const Raster::PixelDim &pd,
+ Raster::Origin o)
+{
+ impl_.reset();
+ impl_.reset(new Impl(r, pd, o));
+}
+
+void Raster::reset()
+{
+ impl_.reset();
+}
+
+Raster::Resolution Raster::resolution() const
+{
+ if(impl_) return impl_->resolution();
+
+ return Resolution(0, 0);
+}
+
+void Raster::clear()
+{
+ assert(impl_);
+ impl_->clear();
+}
+
+void Raster::draw(const ExPolygon &poly)
+{
+ assert(impl_);
+ impl_->draw(poly);
+}
+
+void Raster::save(std::ostream& stream, Compression comp)
+{
+ assert(impl_);
+ switch(comp) {
+ case Compression::PNG: {
+
+ png::writer<std::ostream> wr(stream);
+
+ wr.set_bit_depth(8);
+ wr.set_color_type(png::color_type_gray);
+ wr.set_width(resolution().width_px);
+ wr.set_height(resolution().height_px);
+ wr.set_compression_type(png::compression_type_default);
+
+ wr.write_info();
+
+ auto& b = impl_->buffer();
+ auto ptr = reinterpret_cast<png::byte*>( b.data() );
+ unsigned stride =
+ sizeof(Impl::TBuffer::value_type) * resolution().width_px;
+
+ for(unsigned r = 0; r < resolution().height_px; r++, ptr+=stride) {
+ wr.write_row(ptr);
+ }
+
+ break;
+ }
+ case Compression::RAW: {
+ stream << "P5 "
+ << impl_->resolution().width_px << " "
+ << impl_->resolution().height_px << " "
+ << "255 ";
+
+ stream.write(reinterpret_cast<const char*>(impl_->buffer().data()),
+ impl_->buffer().size()*sizeof(Impl::TBuffer::value_type));
+ }
+ }
+}
+
+}
diff --git a/xs/src/libslic3r/Rasterizer/Rasterizer.hpp b/xs/src/libslic3r/Rasterizer/Rasterizer.hpp
new file mode 100644
index 000000000..cbb39bc6b
--- /dev/null
+++ b/xs/src/libslic3r/Rasterizer/Rasterizer.hpp
@@ -0,0 +1,86 @@
+#ifndef RASTERIZER_HPP
+#define RASTERIZER_HPP
+
+#include <ostream>
+#include <memory>
+
+namespace Slic3r {
+
+class ExPolygon;
+
+/**
+ * @brief Raster captures an anti-aliased monochrome canvas where vectorial
+ * polygons can be rasterized. Fill color is always white and the background is
+ * black. Contours are anti-aliased.
+ *
+ * It also supports saving the raster data into a standard output stream in raw
+ * or PNG format.
+ */
+class Raster {
+ class Impl;
+ std::unique_ptr<Impl> impl_;
+public:
+
+ /// Supported compression types
+ enum class Compression {
+ RAW, //!> Uncompressed pixel data
+ PNG //!> PNG compression
+ };
+
+ enum class Origin {
+ TOP_LEFT,
+ BOTTOM_LEFT
+ };
+
+ /// Type that represents a resolution in pixels.
+ struct Resolution {
+ unsigned width_px;
+ unsigned height_px;
+ inline Resolution(unsigned w, unsigned h): width_px(w), height_px(h) {}
+ inline unsigned pixels() const /*noexcept*/ {
+ return width_px * height_px;
+ }
+ };
+
+ /// Types that represents the dimension of a pixel in millimeters.
+ struct PixelDim {
+ double w_mm;
+ double h_mm;
+ inline PixelDim(double px_width_mm, double px_height_mm ):
+ w_mm(px_width_mm), h_mm(px_height_mm) {}
+ };
+
+ /// Constructor taking the resolution and the pixel dimension.
+ explicit Raster(const Resolution& r, const PixelDim& pd,
+ Origin o = Origin::BOTTOM_LEFT );
+ Raster();
+ Raster(const Raster& cpy) = delete;
+ Raster& operator=(const Raster& cpy) = delete;
+ Raster(Raster&& m);
+ ~Raster();
+
+ /// Reallocated everything for the given resolution and pixel dimension.
+ void reset(const Resolution& r, const PixelDim& pd);
+ void reset(const Resolution& r, const PixelDim& pd, Origin o);
+
+ /**
+ * Release the allocated resources. Drawing in this state ends in
+ * unspecified behaviour.
+ */
+ void reset();
+
+ /// Get the resolution of the raster.
+ Resolution resolution() const;
+
+ /// Clear the raster with black color.
+ void clear();
+
+ /// Draw a polygon with holes.
+ void draw(const ExPolygon& poly);
+
+ /// Save the raster on the specified stream.
+ void save(std::ostream& stream, Compression comp = Compression::RAW);
+};
+
+}
+#endif // RASTERIZER_HPP
diff --git a/xs/src/libslic3r/SLABasePool.cpp b/xs/src/libslic3r/SLABasePool.cpp
new file mode 100644
index 000000000..f3683865c
--- /dev/null
+++ b/xs/src/libslic3r/SLABasePool.cpp
@@ -0,0 +1,531 @@
+#include <functional>
+#include <numeric>
+
+#include "SLABasePool.hpp"
+#include "ExPolygon.hpp"
+#include "TriangleMesh.hpp"
+#include "ClipperUtils.hpp"
+#include "boost/log/trivial.hpp"
+
+//#include "SVG.hpp"
+
+namespace Slic3r { namespace sla {
+
+namespace {
+
+using coord_t = Point::coord_type;
+
+/// get the scaled clipper units for a millimeter value
+inline coord_t mm(double v) { return coord_t(v/SCALING_FACTOR); }
+
+/// Get x and y coordinates (because we are eigenizing...)
+inline coord_t x(const Point& p) { return p(0); }
+inline coord_t y(const Point& p) { return p(1); }
+inline coord_t& x(Point& p) { return p(0); }
+inline coord_t& y(Point& p) { return p(1); }
+
+inline coordf_t x(const Vec3d& p) { return p(0); }
+inline coordf_t y(const Vec3d& p) { return p(1); }
+inline coordf_t z(const Vec3d& p) { return p(2); }
+inline coordf_t& x(Vec3d& p) { return p(0); }
+inline coordf_t& y(Vec3d& p) { return p(1); }
+inline coordf_t& z(Vec3d& p) { return p(2); }
+
+inline coord_t& x(Vec3crd& p) { return p(0); }
+inline coord_t& y(Vec3crd& p) { return p(1); }
+inline coord_t& z(Vec3crd& p) { return p(2); }
+inline coord_t x(const Vec3crd& p) { return p(0); }
+inline coord_t y(const Vec3crd& p) { return p(1); }
+inline coord_t z(const Vec3crd& p) { return p(2); }
+
+inline void triangulate(const ExPolygon& expoly, Polygons& triangles) {
+ expoly.triangulate_p2t(&triangles);
+}
+
+inline Polygons triangulate(const ExPolygon& expoly) {
+ Polygons tri; triangulate(expoly, tri); return tri;
+}
+
+using Indices = std::vector<Vec3crd>;
+
+/// Intermediate struct for a 3D mesh
+struct Contour3D {
+ Pointf3s points;
+ Indices indices;
+
+ void merge(const Contour3D& ctr) {
+ auto s3 = coord_t(points.size());
+ auto s = coord_t(indices.size());
+
+ points.insert(points.end(), ctr.points.begin(), ctr.points.end());
+ indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end());
+
+ for(auto n = s; n < indices.size(); n++) {
+ auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3;
+ }
+ }
+};
+
+/// Convert the triangulation output to an intermediate mesh.
+inline Contour3D convert(const Polygons& triangles, coord_t z, bool dir) {
+
+ Pointf3s points;
+ points.reserve(3*triangles.size());
+ Indices indices;
+ indices.reserve(points.size());
+
+ for(auto& tr : triangles) {
+ auto c = coord_t(points.size()), b = c++, a = c++;
+ if(dir) indices.emplace_back(a, b, c);
+ else indices.emplace_back(c, b, a);
+ for(auto& p : tr.points) {
+ points.emplace_back(unscale(x(p), y(p), z));
+ }
+ }
+
+ return {points, indices};
+}
+
+/// Only a debug function to generate top and bottom plates from a 2D shape.
+/// It is not used in the algorithm directly.
+inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) {
+ Polygons triangles = triangulate(poly);
+
+ auto lower = convert(triangles, 0, false);
+ auto upper = convert(triangles, z_distance, true);
+ lower.merge(upper);
+ return lower;
+}
+
+inline Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling,
+ double floor_z_mm, double ceiling_z_mm) {
+ using std::transform; using std::back_inserter;
+
+ ExPolygon poly;
+ poly.contour.points = floor_plate.contour.points;
+ poly.holes.emplace_back(ceiling.contour);
+ auto& h = poly.holes.front();
+ std::reverse(h.points.begin(), h.points.end());
+ Polygons tri = triangulate(poly);
+
+ Contour3D ret;
+ ret.points.reserve(tri.size() * 3);
+
+ double fz = floor_z_mm;
+ double cz = ceiling_z_mm;
+ auto& rp = ret.points;
+ auto& rpi = ret.indices;
+ ret.indices.reserve(tri.size() * 3);
+
+ coord_t idx = 0;
+
+ auto hlines = h.lines();
+ auto is_upper = [&hlines](const Point& p) {
+ return std::any_of(hlines.begin(), hlines.end(),
+ [&p](const Line& l) {
+ return l.distance_to(p) < mm(0.01);
+ });
+ };
+
+ std::for_each(tri.begin(), tri.end(),
+ [&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp)
+ {
+ for(auto& p : pp.points)
+ if(is_upper(p))
+ rp.emplace_back(unscale(x(p), y(p), mm(cz)));
+ else rp.emplace_back(unscale(x(p), y(p), mm(fz)));
+
+ coord_t a = idx++, b = idx++, c = idx++;
+ if(fz > cz) rpi.emplace_back(c, b, a);
+ else rpi.emplace_back(a, b, c);
+ });
+
+ return ret;
+}
+
+/// Mesh from an existing contour.
+inline TriangleMesh mesh(const Contour3D& ctour) {
+ return {ctour.points, ctour.indices};
+}
+
+/// Mesh from an evaporating 3D contour
+inline TriangleMesh mesh(Contour3D&& ctour) {
+ return {std::move(ctour.points), std::move(ctour.indices)};
+}
+
+/// Offsetting with clipper and smoothing the edges into a curvature.
+inline void offset(ExPolygon& sh, coord_t distance) {
+ using ClipperLib::ClipperOffset;
+ using ClipperLib::jtRound;
+ using ClipperLib::etClosedPolygon;
+ using ClipperLib::Paths;
+ using ClipperLib::Path;
+
+ auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh.contour);
+ auto&& holes = Slic3rMultiPoints_to_ClipperPaths(sh.holes);
+
+ // If the input is not at least a triangle, we can not do this algorithm
+ if(ctour.size() < 3 ||
+ std::any_of(holes.begin(), holes.end(),
+ [](const Path& p) { return p.size() < 3; })
+ ) {
+ BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
+ return;
+ }
+
+ ClipperOffset offs;
+ offs.ArcTolerance = 0.01*mm(1);
+ Paths result;
+ offs.AddPath(ctour, jtRound, etClosedPolygon);
+ offs.AddPaths(holes, jtRound, etClosedPolygon);
+ offs.Execute(result, static_cast<double>(distance));
+
+ // Offsetting reverts the orientation and also removes the last vertex
+ // so boost will not have a closed polygon.
+
+ bool found_the_contour = false;
+ sh.holes.clear();
+ for(auto& r : result) {
+ if(ClipperLib::Orientation(r)) {
+ // We don't like if the offsetting generates more than one contour
+ // but throwing would be an overkill. Instead, we should warn the
+ // caller about the inability to create correct geometries
+ if(!found_the_contour) {
+ auto rr = ClipperPath_to_Slic3rPolygon(r);
+ sh.contour.points.swap(rr.points);
+ found_the_contour = true;
+ } else {
+ BOOST_LOG_TRIVIAL(warning)
+ << "Warning: offsetting result is invalid!";
+ }
+ } else {
+ // TODO If there are multiple contours we can't be sure which hole
+ // belongs to the first contour. (But in this case the situation is
+ // bad enough to let it go...)
+ sh.holes.emplace_back(ClipperPath_to_Slic3rPolygon(r));
+ }
+ }
+}
+
+template<class ExP, class D>
+inline Contour3D round_edges(const ExPolygon& base_plate,
+ double radius_mm,
+ double degrees,
+ double ceilheight_mm,
+ bool dir,
+ ExP&& last_offset = ExP(), D&& last_height = D())
+{
+ auto ob = base_plate;
+ auto ob_prev = ob;
+ double wh = ceilheight_mm, wh_prev = wh;
+ Contour3D curvedwalls;
+
+ const size_t steps = 6; // steps for 180 degrees
+ degrees = std::fmod(degrees, 180);
+ const int portion = int(steps*degrees / 90);
+ const double ystep_mm = radius_mm/steps;
+ coord_t s = dir? 1 : -1;
+ double xxprev = 0;
+ for(int i = 0; i < portion; i++) {
+ ob = base_plate;
+
+ // The offset is given by the equation: x = sqrt(r^2 - y^2)
+ // which can be derived from the circle equation. y is the current
+ // height for which the offset is calculated and x is the offset itself
+ // r is the radius of the circle that is used to smooth the edges
+
+ double r2 = radius_mm * radius_mm;
+ double y2 = steps*ystep_mm - i*ystep_mm;
+ y2 *= y2;
+
+ double xx = sqrt(r2 - y2);
+
+ offset(ob, s*mm(xx));
+ wh = ceilheight_mm - i*ystep_mm;
+
+ Contour3D pwalls;
+ if(xxprev < xx) pwalls = walls(ob, ob_prev, wh, wh_prev);
+ else pwalls = walls(ob_prev, ob, wh_prev, wh);
+
+ curvedwalls.merge(pwalls);
+ ob_prev = ob;
+ wh_prev = wh;
+ xxprev = xx;
+ }
+
+ last_offset = std::move(ob);
+ last_height = wh;
+
+ return curvedwalls;
+}
+
+/// Generating the concave part of the 3D pool with the bottom plate and the
+/// side walls.
+inline Contour3D inner_bed(const ExPolygon& poly, double depth_mm,
+ double begin_h_mm = 0) {
+
+ Polygons triangles = triangulate(poly);
+
+ coord_t depth = mm(depth_mm);
+ coord_t begin_h = mm(begin_h_mm);
+
+ auto bottom = convert(triangles, -depth + begin_h, false);
+ auto lines = poly.lines();
+
+ // Generate outer walls
+ auto fp = [](const Point& p, Point::coord_type z) {
+ return unscale(x(p), y(p), z);
+ };
+
+ for(auto& l : lines) {
+ auto s = coord_t(bottom.points.size());
+
+ bottom.points.emplace_back(fp(l.a, -depth + begin_h));
+ bottom.points.emplace_back(fp(l.b, -depth + begin_h));
+ bottom.points.emplace_back(fp(l.a, begin_h));
+ bottom.points.emplace_back(fp(l.b, begin_h));
+
+ bottom.indices.emplace_back(s + 3, s + 1, s);
+ bottom.indices.emplace_back(s + 2, s + 3, s);
+ }
+
+ return bottom;
+}
+
+/// Unification of polygons (with clipper) preserving holes as well.
+inline ExPolygons unify(const ExPolygons& shapes) {
+ using ClipperLib::ptSubject;
+
+ ExPolygons retv;
+
+ bool closed = true;
+ bool valid = true;
+
+ ClipperLib::Clipper clipper;
+
+ for(auto& path : shapes) {
+ auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path.contour);
+
+ if(!clipperpath.empty())
+ valid &= clipper.AddPath(clipperpath, ptSubject, closed);
+
+ auto clipperholes = Slic3rMultiPoints_to_ClipperPaths(path.holes);
+
+ for(auto& hole : clipperholes) {
+ if(!hole.empty())
+ valid &= clipper.AddPath(hole, ptSubject, closed);
+ }
+ }
+
+ if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!";
+
+ ClipperLib::PolyTree result;
+ clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero);
+
+ retv.reserve(static_cast<size_t>(result.Total()));
+
+ // Now we will recursively traverse the polygon tree and serialize it
+ // into an ExPolygon with holes. The polygon tree has the clipper-ish
+ // PolyTree structure which alternates its nodes as contours and holes
+
+ // A "declaration" of function for traversing leafs which are holes
+ std::function<void(ClipperLib::PolyNode*, ExPolygon&)> processHole;
+
+ // Process polygon which calls processHoles which than calls processPoly
+ // again until no leafs are left.
+ auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
+ ExPolygon poly;
+ poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour);
+ for(auto h : pptr->Childs) { processHole(h, poly); }
+ retv.push_back(poly);
+ };
+
+ // Body of the processHole function
+ processHole = [&processPoly](ClipperLib::PolyNode *pptr, ExPolygon& poly)
+ {
+ poly.holes.emplace_back();
+ poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour);
+ for(auto c : pptr->Childs) processPoly(c);
+ };
+
+ // Wrapper for traversing.
+ auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
+ {
+ for(auto ch : node->Childs) {
+ processPoly(ch);
+ }
+ };
+
+ // Here is the actual traverse
+ traverse(&result);
+
+ return retv;
+}
+
+inline Point centroid(Points& pp) {
+ Point c;
+ switch(pp.size()) {
+ case 0: break;
+ case 1: c = pp.front(); break;
+ case 2: c = (pp[0] + pp[1]) / 2; break;
+ default: {
+ Polygon p;
+ p.points.swap(pp);
+ c = p.centroid();
+ pp.swap(p.points);
+ break;
+ }
+ }
+
+ return c;
+}
+
+inline Point centroid(const ExPolygon& poly) {
+ return poly.contour.centroid();
+}
+
+/// A fake concave hull that is constructed by connecting separate shapes
+/// with explicit bridges. Bridges are generated from each shape's centroid
+/// to the center of the "scene" which is the centroid calculated from the shape
+/// centroids (a star is created...)
+inline ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50)
+{
+ if(polys.empty()) return ExPolygons();
+
+ ExPolygons punion = unify(polys); // could be redundant
+
+ if(punion.size() == 1) return punion;
+
+ // We get the centroids of all the islands in the 2D slice
+ Points centroids; centroids.reserve(punion.size());
+ std::transform(punion.begin(), punion.end(), std::back_inserter(centroids),
+ [](const ExPolygon& poly) { return centroid(poly); });
+
+ // Centroid of the centroids of islands. This is where the additional
+ // connector sticks are routed.
+ Point cc = centroid(centroids);
+
+ punion.reserve(punion.size() + centroids.size());
+
+ std::transform(centroids.begin(), centroids.end(),
+ std::back_inserter(punion),
+ [cc, max_dist_mm](const Point& c) {
+
+ double dx = x(c) - x(cc), dy = y(c) - y(cc);
+ double l = std::sqrt(dx * dx + dy * dy);
+ double nx = dx / l, ny = dy / l;
+ double max_dist = mm(max_dist_mm);
+
+ if(l > max_dist) return ExPolygon();
+
+ ExPolygon r;
+ auto& ctour = r.contour.points;
+
+ ctour.reserve(3);
+ ctour.emplace_back(cc);
+
+ Point d(coord_t(mm(1)*nx), coord_t(mm(1)*ny));
+ ctour.emplace_back(c + Point( -y(d), x(d) ));
+ ctour.emplace_back(c + Point( y(d), -x(d) ));
+ offset(r, mm(1));
+
+ return r;
+ });
+
+ punion = unify(punion);
+
+ return punion;
+}
+
+}
+
+void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h)
+{
+ TriangleMesh m = mesh;
+ TriangleMeshSlicer slicer(&m);
+
+ std::vector<ExPolygons> tmp;
+
+ slicer.slice({h}, &tmp, [](){});
+
+ output = tmp.front();
+}
+
+void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out,
+ double min_wall_thickness_mm,
+ double min_wall_height_mm,
+ double max_merge_distance_mm)
+{
+ auto concavehs = concave_hull(ground_layer, max_merge_distance_mm);
+ for(ExPolygon& concaveh : concavehs) {
+ if(concaveh.contour.points.empty()) return;
+ concaveh.holes.clear();
+
+ BoundingBox bb(concaveh);
+ coord_t w = x(bb.max) - x(bb.min);
+ coord_t h = y(bb.max) - y(bb.min);
+
+ auto wall_thickness = coord_t((w+h)*0.01);
+
+ const coord_t WALL_THICKNESS = mm(min_wall_thickness_mm) +
+ wall_thickness;
+
+ const coord_t WALL_DISTANCE = coord_t(0.3*WALL_THICKNESS);
+ const coord_t HEIGHT = mm(min_wall_height_mm);
+
+ auto outer_base = concaveh;
+ offset(outer_base, WALL_THICKNESS+WALL_DISTANCE);
+ auto inner_base = outer_base;
+ offset(inner_base, -WALL_THICKNESS);
+ inner_base.holes.clear(); outer_base.holes.clear();
+
+ ExPolygon top_poly;
+ top_poly.contour = outer_base.contour;
+ top_poly.holes.emplace_back(inner_base.contour);
+ auto& tph = top_poly.holes.back().points;
+ std::reverse(tph.begin(), tph.end());
+
+ Contour3D pool;
+
+ ExPolygon ob = outer_base; double wh = 0;
+ auto curvedwalls = round_edges(ob,
+ 1, // radius 1 mm
+ 170, // 170 degrees
+ 0, // z position of the input plane
+ true,
+ ob, wh);
+ pool.merge(curvedwalls);
+
+ ExPolygon ob_contr = ob;
+ ob_contr.holes.clear();
+
+ auto pwalls = walls(ob_contr, inner_base, wh, -min_wall_height_mm);
+ pool.merge(pwalls);
+
+ Polygons top_triangles, bottom_triangles;
+ triangulate(top_poly, top_triangles);
+ triangulate(inner_base, bottom_triangles);
+ auto top_plate = convert(top_triangles, 0, false);
+ auto bottom_plate = convert(bottom_triangles, -HEIGHT, true);
+
+ ob = inner_base; wh = 0;
+ curvedwalls = round_edges(ob,
+ 1, // radius 1 mm
+ 90, // 170 degrees
+ 0, // z position of the input plane
+ false,
+ ob, wh);
+ pool.merge(curvedwalls);
+
+ auto innerbed = inner_bed(ob, min_wall_height_mm/2 + wh, wh);
+
+ pool.merge(top_plate);
+ pool.merge(bottom_plate);
+ pool.merge(innerbed);
+
+ out.merge(mesh(pool));
+ }
+}
+
+}
+}
diff --git a/xs/src/libslic3r/SLABasePool.hpp b/xs/src/libslic3r/SLABasePool.hpp
new file mode 100644
index 000000000..55c94df07
--- /dev/null
+++ b/xs/src/libslic3r/SLABasePool.hpp
@@ -0,0 +1,32 @@
+#ifndef SLASUPPORTPOOL_HPP
+#define SLASUPPORTPOOL_HPP
+
+#include <vector>
+
+namespace Slic3r {
+
+class ExPolygon;
+class TriangleMesh;
+
+namespace sla {
+
+using ExPolygons = std::vector<ExPolygon>;
+
+/// Calculate the polygon representing the slice of the lowest layer of mesh
+void ground_layer(const TriangleMesh& mesh,
+ ExPolygons& output,
+ float height = 0.1f);
+
+/// Calculate the pool for the mesh for SLA printing
+void create_base_pool(const ExPolygons& ground_layer,
+ TriangleMesh& output_mesh,
+ double min_wall_thickness_mm = 2,
+ double min_wall_height_mm = 5,
+ double max_merge_distance_mm = 50
+ );
+
+}
+
+}
+
+#endif // SLASUPPORTPOOL_HPP
diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp
index c94db8e74..03f55802e 100644
--- a/xs/src/libslic3r/SVG.cpp
+++ b/xs/src/libslic3r/SVG.cpp
@@ -3,7 +3,7 @@
#include <boost/nowide/cstdio.hpp>
-#define COORD(x) ((float)unscale((x))*10)
+#define COORD(x) (unscale<float>((x))*10)
namespace Slic3r {
@@ -32,8 +32,8 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo
this->f = boost::nowide::fopen(afilename, "w");
if (f == NULL)
return false;
- float w = COORD(bbox.max.x - bbox.min.x + 2 * bbox_offset);
- float h = COORD(bbox.max.y - bbox.min.y + 2 * bbox_offset);
+ float w = COORD(bbox.max(0) - bbox.min(0) + 2 * bbox_offset);
+ float h = COORD(bbox.max(1) - bbox.min(1) + 2 * bbox_offset);
fprintf(this->f,
"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n"
"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.0//EN\" \"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd\">\n"
@@ -50,7 +50,7 @@ SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
{
fprintf(this->f,
" <line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" style=\"stroke: %s; stroke-width: %f\"",
- COORD(line.a.x - origin.x), COORD(line.a.y - origin.y), COORD(line.b.x - origin.x), COORD(line.b.y - origin.y), stroke.c_str(), (stroke_width == 0) ? 1.f : COORD(stroke_width));
+ COORD(line.a(0) - origin(0)), COORD(line.a(1) - origin(1)), COORD(line.b(0) - origin(0)), COORD(line.b(1) - origin(1)), stroke.c_str(), (stroke_width == 0) ? 1.f : COORD(stroke_width));
if (this->arrows)
fprintf(this->f, " marker-end=\"url(#endArrow)\"");
fprintf(this->f, "/>\n");
@@ -58,21 +58,21 @@ SVG::draw(const Line &line, std::string stroke, coordf_t stroke_width)
void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coordf_t stroke_width)
{
- Pointf dir(line.b.x-line.a.x, line.b.y-line.a.y);
- Pointf perp(-dir.y, dir.x);
- coordf_t len = sqrt(perp.x*perp.x + perp.y*perp.y);
+ Vec2d dir(line.b(0)-line.a(0), line.b(1)-line.a(1));
+ Vec2d perp(-dir(1), dir(0));
+ coordf_t len = sqrt(perp(0)*perp(0) + perp(1)*perp(1));
coordf_t da = coordf_t(0.5)*line.a_width/len;
coordf_t db = coordf_t(0.5)*line.b_width/len;
fprintf(this->f,
" <polygon points=\"%f,%f %f,%f %f,%f %f,%f\" style=\"fill:%s; stroke: %s; stroke-width: %f\"/>\n",
- COORD(line.a.x-da*perp.x-origin.x),
- COORD(line.a.y-da*perp.y-origin.y),
- COORD(line.b.x-db*perp.x-origin.x),
- COORD(line.b.y-db*perp.y-origin.y),
- COORD(line.b.x+db*perp.x-origin.x),
- COORD(line.b.y+db*perp.y-origin.y),
- COORD(line.a.x+da*perp.x-origin.x),
- COORD(line.a.y+da*perp.y-origin.y),
+ COORD(line.a(0)-da*perp(0)-origin(0)),
+ COORD(line.a(1)-da*perp(1)-origin(1)),
+ COORD(line.b(0)-db*perp(0)-origin(0)),
+ COORD(line.b(1)-db*perp(1)-origin(1)),
+ COORD(line.b(0)+db*perp(0)-origin(0)),
+ COORD(line.b(1)+db*perp(1)-origin(1)),
+ COORD(line.a(0)+da*perp(0)-origin(0)),
+ COORD(line.a(1)+da*perp(1)-origin(1)),
fill.c_str(), stroke.c_str(),
(stroke_width == 0) ? 1.f : COORD(stroke_width));
}
@@ -220,7 +220,7 @@ SVG::draw(const Point &point, std::string fill, coord_t iradius)
{
float radius = (iradius == 0) ? 3.f : COORD(iradius);
std::ostringstream svg;
- svg << " <circle cx=\"" << COORD(point.x - origin.x) << "\" cy=\"" << COORD(point.y - origin.y)
+ svg << " <circle cx=\"" << COORD(point(0) - origin(0)) << "\" cy=\"" << COORD(point(1) - origin(1))
<< "\" r=\"" << radius << "\" "
<< "style=\"stroke: none; fill: " << fill << "\" />";
@@ -287,8 +287,8 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const
std::ostringstream d;
d << "M ";
for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) {
- d << COORD(p->x - origin.x) << " ";
- d << COORD(p->y - origin.y) << " ";
+ d << COORD((*p)(0) - origin(0)) << " ";
+ d << COORD((*p)(1) - origin(1)) << " ";
}
if (closed) d << "z";
return d.str();
@@ -300,8 +300,8 @@ SVG::get_path_d(const ClipperLib::Path &path, double scale, bool closed) const
std::ostringstream d;
d << "M ";
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
- d << COORD(scale * p->X - origin.x) << " ";
- d << COORD(scale * p->Y - origin.y) << " ";
+ d << COORD(scale * p->X - origin(0)) << " ";
+ d << COORD(scale * p->Y - origin(1)) << " ";
}
if (closed) d << "z";
return d.str();
@@ -311,8 +311,8 @@ void SVG::draw_text(const Point &pt, const char *text, const char *color)
{
fprintf(this->f,
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"20px\" fill=\"%s\">%s</text>",
- COORD(pt.x-origin.x),
- COORD(pt.y-origin.y),
+ COORD(pt(0)-origin(0)),
+ COORD(pt(1)-origin(1)),
color, text);
}
@@ -320,13 +320,13 @@ void SVG::draw_legend(const Point &pt, const char *text, const char *color)
{
fprintf(this->f,
"<circle cx=\"%f\" cy=\"%f\" r=\"10\" fill=\"%s\"/>",
- COORD(pt.x-origin.x),
- COORD(pt.y-origin.y),
+ COORD(pt(0)-origin(0)),
+ COORD(pt(1)-origin(1)),
color);
fprintf(this->f,
"<text x=\"%f\" y=\"%f\" font-family=\"sans-serif\" font-size=\"10px\" fill=\"%s\">%s</text>",
- COORD(pt.x-origin.x) + 20.f,
- COORD(pt.y-origin.y),
+ COORD(pt(0)-origin(0)) + 20.f,
+ COORD(pt(1)-origin(1)),
"black", text);
}
diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp
index e9295d1e3..1bc38502b 100644
--- a/xs/src/libslic3r/Slicing.cpp
+++ b/xs/src/libslic3r/Slicing.cpp
@@ -561,15 +561,15 @@ int generate_layer_height_texture(
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(0x01A, 0x098, 0x050));
- palette_raw.push_back(Point3(0x066, 0x0BD, 0x063));
- palette_raw.push_back(Point3(0x0A6, 0x0D9, 0x06A));
- palette_raw.push_back(Point3(0x0D9, 0x0F1, 0x0EB));
- palette_raw.push_back(Point3(0x0FE, 0x0E6, 0x0EB));
- palette_raw.push_back(Point3(0x0FD, 0x0AE, 0x061));
- palette_raw.push_back(Point3(0x0F4, 0x06D, 0x043));
- palette_raw.push_back(Point3(0x0D7, 0x030, 0x027));
+ std::vector<Vec3crd> palette_raw;
+ palette_raw.push_back(Vec3crd(0x01A, 0x098, 0x050));
+ palette_raw.push_back(Vec3crd(0x066, 0x0BD, 0x063));
+ palette_raw.push_back(Vec3crd(0x0A6, 0x0D9, 0x06A));
+ palette_raw.push_back(Vec3crd(0x0D9, 0x0F1, 0x0EB));
+ palette_raw.push_back(Vec3crd(0x0FE, 0x0E6, 0x0EB));
+ palette_raw.push_back(Vec3crd(0x0FD, 0x0AE, 0x061));
+ palette_raw.push_back(Vec3crd(0x0F4, 0x06D, 0x043));
+ palette_raw.push_back(Vec3crd(0x0D7, 0x030, 0x027));
// Clear the main texture and the 2nd LOD level.
// memset(data, 0, rows * cols * (level_of_detail_2nd_level ? 5 : 4));
@@ -600,25 +600,25 @@ int generate_layer_height_texture(
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];
+ const Vec3crd &color1 = palette_raw[idx1];
+ const Vec3crd &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));
+ Vec3d color(
+ intensity * lerp(coordf_t(color1(0)), coordf_t(color2(0)), t),
+ intensity * lerp(coordf_t(color1(1)), coordf_t(color2(1)), t),
+ intensity * lerp(coordf_t(color1(2)), coordf_t(color2(2)), 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] = (unsigned char)clamp<int>(0, 255, int(floor(color.x + 0.5)));
- ptr[1] = (unsigned char)clamp<int>(0, 255, int(floor(color.y + 0.5)));
- ptr[2] = (unsigned char)clamp<int>(0, 255, int(floor(color.z + 0.5)));
+ ptr[0] = (unsigned char)clamp<int>(0, 255, int(floor(color(0) + 0.5)));
+ ptr[1] = (unsigned char)clamp<int>(0, 255, int(floor(color(1) + 0.5)));
+ ptr[2] = (unsigned char)clamp<int>(0, 255, int(floor(color(2) + 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.
@@ -636,21 +636,21 @@ int generate_layer_height_texture(
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];
+ const Vec3crd &color1 = palette_raw[idx1];
+ const Vec3crd &color2 = palette_raw[idx2];
// 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));
+ Vec3d color(
+ lerp(coordf_t(color1(0)), coordf_t(color2(0)), t),
+ lerp(coordf_t(color1(1)), coordf_t(color2(1)), t),
+ lerp(coordf_t(color1(2)), coordf_t(color2(2)), 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] = (unsigned char)clamp<int>(0, 255, int(floor(color.x + 0.5)));
- ptr[1] = (unsigned char)clamp<int>(0, 255, int(floor(color.y + 0.5)));
- ptr[2] = (unsigned char)clamp<int>(0, 255, int(floor(color.z + 0.5)));
+ ptr[0] = (unsigned char)clamp<int>(0, 255, int(floor(color(0) + 0.5)));
+ ptr[1] = (unsigned char)clamp<int>(0, 255, int(floor(color(1) + 0.5)));
+ ptr[2] = (unsigned char)clamp<int>(0, 255, int(floor(color(2) + 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.
diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp
index ff0da7636..2ef4aec8c 100644
--- a/xs/src/libslic3r/SlicingAdaptive.cpp
+++ b/xs/src/libslic3r/SlicingAdaptive.cpp
@@ -15,8 +15,8 @@ void SlicingAdaptive::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));
+ std::min(std::min(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)),
+ std::max(std::max(f->vertex[0](2), f->vertex[1](2)), f->vertex[2](2)));
}
void SlicingAdaptive::prepare()
@@ -40,7 +40,7 @@ void SlicingAdaptive::prepare()
// 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;
+ m_face_normal_z[iface] = m_faces[iface]->normal(2);
}
float SlicingAdaptive::cusp_height(float z, float cusp_value, int &current_facet)
diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp
index d18f9d710..a6b6c1bb8 100644
--- a/xs/src/libslic3r/SupportMaterial.cpp
+++ b/xs/src/libslic3r/SupportMaterial.cpp
@@ -67,9 +67,9 @@ Point export_support_surface_type_legend_to_svg_box_size()
void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos)
{
// 1st row
- coord_t pos_x0 = pos.x + scale_(1.);
+ coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
- coord_t pos_y = pos.y + scale_(1.5);
+ coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
svg.draw_legend(Point(pos_x, pos_y), "top contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltTopContact));
pos_x += step_x;
@@ -82,7 +82,7 @@ void export_support_surface_type_legend_to_svg(SVG &svg, const Point &pos)
svg.draw_legend(Point(pos_x, pos_y), "bottom contact" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltBottomContact));
// 2nd row
pos_x = pos_x0;
- pos_y = pos.y+scale_(2.8);
+ pos_y = pos(1)+scale_(2.8);
svg.draw_legend(Point(pos_x, pos_y), "raft interface" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftInterface));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "raft base" , support_surface_type_to_color_name(PrintObjectSupportMaterial::sltRaftBase));
@@ -98,8 +98,8 @@ void export_print_z_polygons_to_svg(const char *path, PrintObjectSupportMaterial
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
@@ -120,8 +120,8 @@ void export_print_z_polygons_and_extrusions_to_svg(
for (int i = 0; i < n_layers; ++ i)
bbox.merge(get_extents(layers[i]->polygons));
Point legend_size = export_support_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
for (int i = 0; i < n_layers; ++ i)
@@ -506,8 +506,8 @@ public:
for (ExPolygon &island : islands) {
BoundingBox bbox = get_extents(island.contour);
- auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), bbox.min - Point(1, 1));
- auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), bbox.max + Point(1, 1));
+ auto it_lower = std::lower_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.min - Point(1, 1)));
+ auto it_upper = std::upper_bound(m_island_samples.begin(), m_island_samples.end(), Point(bbox.max + Point(1, 1)));
samples_inside.clear();
for (auto it = it_lower; it != it_upper; ++ it)
if (bbox.contains(*it))
@@ -519,12 +519,12 @@ public:
Points::const_iterator i = contour.points.begin();
Points::const_iterator j = contour.points.end() - 1;
for (; i != contour.points.end(); j = i ++) {
- //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point.y well.
- // Does the ray with y == point.y intersect this line segment?
+ //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well.
+ // Does the ray with y == point(1) intersect this line segment?
for (auto &sample_inside : samples_inside) {
- if ((i->y > sample_inside.first.y) != (j->y > sample_inside.first.y)) {
- double x1 = (double)sample_inside.first.x;
- double x2 = (double)i->x + (double)(j->x - i->x) * (double)(sample_inside.first.y - i->y) / (double)(j->y - i->y);
+ if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) {
+ double x1 = (double)sample_inside.first(0);
+ double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1));
if (x1 < x2)
sample_inside.second = !sample_inside.second;
}
@@ -585,11 +585,11 @@ private:
const Point &p3 = (pt_min == &expoly.contour.points.back()) ? expoly.contour.points.front() : *(pt_min + 1);
Vector v = (p3 - p2) + (p1 - p2);
- double l2 = double(v.x)*double(v.x)+double(v.y)*double(v.y);
+ double l2 = double(v(0))*double(v(0))+double(v(1))*double(v(1));
if (l2 == 0.)
return p2;
double coef = 20. / sqrt(l2);
- return Point(p2.x + coef * v.x, p2.y + coef * v.y);
+ return Point(p2(0) + coef * v(0), p2(1) + coef * v(1));
}
static Points island_samples(const ExPolygons &expolygons)
@@ -789,7 +789,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
// workaround for Clipper bug, see Slic3r::Polygon::clip_as_polyline()
for (Polyline &polyline : overhang_perimeters)
- polyline.points[0].x += 1;
+ polyline.points[0](0) += 1;
// Trim the perimeters of this layer by the lower layer to get the unsupported pieces of perimeters.
overhang_perimeters = diff_pl(overhang_perimeters, lower_grown_slices);
@@ -2057,11 +2057,11 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
const Point &p1 = *(it-1);
const Point &p2 = *it;
// Intersection of a ray (p1, p2) with a circle placed at center_last, with radius of circle_distance.
- const Pointf v_seg(coordf_t(p2.x) - coordf_t(p1.x), coordf_t(p2.y) - coordf_t(p1.y));
- const Pointf v_cntr(coordf_t(p1.x - center_last.x), coordf_t(p1.y - center_last.y));
- coordf_t a = dot(v_seg);
- coordf_t b = 2. * dot(v_seg, v_cntr);
- coordf_t c = dot(v_cntr) - circle_distance * circle_distance;
+ const Vec2d v_seg(coordf_t(p2(0)) - coordf_t(p1(0)), coordf_t(p2(1)) - coordf_t(p1(1)));
+ const Vec2d v_cntr(coordf_t(p1(0) - center_last(0)), coordf_t(p1(1) - center_last(1)));
+ coordf_t a = v_seg.squaredNorm();
+ coordf_t b = 2. * v_seg.dot(v_cntr);
+ coordf_t c = v_cntr.squaredNorm() - circle_distance * circle_distance;
coordf_t disc = b * b - 4. * a * c;
if (disc > 0.) {
// The circle intersects a ray. Avoid the parts of the segment inside the circle.
@@ -2081,7 +2081,7 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
}
seg_current_pt = &p1;
seg_current_t = t;
- center_last = Point(p1.x + coord_t(v_seg.x * t), p1.y + coord_t(v_seg.y * t));
+ center_last = Point(p1(0) + coord_t(v_seg(0) * t), p1(1) + coord_t(v_seg(1) * t));
// It has been verified that the new point is far enough from center_last.
// Ensure, that it is far enough from all the centers.
std::pair<const Point*, coordf_t> circle_closest = circle_centers_lookup.find(center_last);
@@ -2100,9 +2100,9 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const
circle_centers.push_back(center_last);
}
external_loops.push_back(std::move(contour));
- for (Points::const_iterator it_center = circle_centers.begin(); it_center != circle_centers.end(); ++ it_center) {
+ for (const Point &center : circle_centers) {
circles.push_back(circle);
- circles.back().translate(*it_center);
+ circles.back().translate(center);
}
}
}
@@ -2359,7 +2359,7 @@ void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
}
private:
- ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&);
+ ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) = delete;
const std::vector<ExtrusionPathFragment> &m_path_fragments;
};
const coord_t search_radius = 7;
@@ -2392,7 +2392,7 @@ void modulate_extrusion_by_overlapping_layers(
if (end_and_dist2.first == nullptr) {
// New fragment connecting to pt_current was not found.
// Verify that the last point found is close to the original end point of the unfragmented path.
- //const double d2 = pt_end.distance_to_sq(pt_current);
+ //const double d2 = (pt_end - pt_current).squaredNorm();
//assert(d2 < coordf_t(search_radius * search_radius));
// End of the path.
break;
@@ -2887,9 +2887,9 @@ void PrintObjectSupportMaterial::clip_by_pillars(
BoundingBox bbox;
for (LayersPtr::const_iterator it = top_contacts.begin(); it != top_contacts.end(); ++ it)
bbox.merge(get_extents((*it)->polygons));
- grid.reserve(size_t(ceil(bb.size().x / pillar_spacing)) * size_t(ceil(bb.size().y / pillar_spacing)));
- for (coord_t x = bb.min.x; x <= bb.max.x - pillar_size; x += pillar_spacing) {
- for (coord_t y = bb.min.y; y <= bb.max.y - pillar_size; y += pillar_spacing) {
+ grid.reserve(size_t(ceil(bb.size()(0) / pillar_spacing)) * size_t(ceil(bb.size()(1) / pillar_spacing)));
+ for (coord_t x = bb.min(0); x <= bb.max(0) - pillar_size; x += pillar_spacing) {
+ for (coord_t y = bb.min(1); y <= bb.max(1) - pillar_size; y += pillar_spacing) {
grid.push_back(pillar);
for (size_t i = 0; i < pillar.points.size(); ++ i)
grid.back().points[i].translate(Point(x, y));
diff --git a/xs/src/libslic3r/Surface.cpp b/xs/src/libslic3r/Surface.cpp
index 384540d87..0e9eca7fd 100644
--- a/xs/src/libslic3r/Surface.cpp
+++ b/xs/src/libslic3r/Surface.cpp
@@ -106,9 +106,9 @@ Point export_surface_type_legend_to_svg_box_size()
void export_surface_type_legend_to_svg(SVG &svg, const Point &pos)
{
// 1st row
- coord_t pos_x0 = pos.x + scale_(1.);
+ coord_t pos_x0 = pos(0) + scale_(1.);
coord_t pos_x = pos_x0;
- coord_t pos_y = pos.y + scale_(1.5);
+ coord_t pos_y = pos(1) + scale_(1.5);
coord_t step_x = scale_(10.);
svg.draw_legend(Point(pos_x, pos_y), "perimeter" , surface_type_to_color_name(stPerimeter));
pos_x += step_x;
@@ -121,7 +121,7 @@ void export_surface_type_legend_to_svg(SVG &svg, const Point &pos)
svg.draw_legend(Point(pos_x, pos_y), "invalid" , surface_type_to_color_name(SurfaceType(-1)));
// 2nd row
pos_x = pos_x0;
- pos_y = pos.y+scale_(2.8);
+ pos_y = pos(1)+scale_(2.8);
svg.draw_legend(Point(pos_x, pos_y), "internal" , surface_type_to_color_name(stInternal));
pos_x += step_x;
svg.draw_legend(Point(pos_x, pos_y), "internal solid" , surface_type_to_color_name(stInternalSolid));
diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp
index 42ddf9574..6db599306 100644
--- a/xs/src/libslic3r/SurfaceCollection.cpp
+++ b/xs/src/libslic3r/SurfaceCollection.cpp
@@ -170,8 +170,8 @@ void SurfaceCollection::export_to_svg(const char *path, bool show_labels)
for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface)
bbox.merge(get_extents(surface->expolygon));
Point legend_size = export_surface_type_legend_to_svg_box_size();
- Point legend_pos(bbox.min.x, bbox.max.y);
- bbox.merge(Point(std::max(bbox.min.x + legend_size.x, bbox.max.x), bbox.max.y + legend_size.y));
+ Point legend_pos(bbox.min(0), bbox.max(1));
+ bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1)));
SVG svg(path, bbox);
const float transparency = 0.5f;
diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp
index 17b3d98f9..7b8f85b6b 100644
--- a/xs/src/libslic3r/TriangleMesh.cpp
+++ b/xs/src/libslic3r/TriangleMesh.cpp
@@ -1,6 +1,9 @@
#include "TriangleMesh.hpp"
#include "ClipperUtils.hpp"
#include "Geometry.hpp"
+#include "qhull/src/libqhullcpp/Qhull.h"
+#include "qhull/src/libqhullcpp/QhullFacetList.h"
+#include "qhull/src/libqhullcpp/QhullVertexSet.h"
#include <cmath>
#include <deque>
#include <queue>
@@ -10,11 +13,14 @@
#include <utility>
#include <algorithm>
#include <math.h>
+#include <type_traits>
#include <boost/log/trivial.hpp>
#include <tbb/parallel_for.h>
+#include <Eigen/Dense>
+
#if 0
#define DEBUG
#define _DEBUG
@@ -30,13 +36,7 @@
namespace Slic3r {
-TriangleMesh::TriangleMesh()
- : repaired(false)
-{
- stl_initialize(&this->stl);
-}
-
-TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& facets )
+TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& facets )
: repaired(false)
{
stl_initialize(&this->stl);
@@ -51,47 +51,22 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Point3>& fa
for (int i = 0; i < stl.stats.number_of_facets; i++) {
stl_facet facet;
- facet.normal.x = 0;
- facet.normal.y = 0;
- facet.normal.z = 0;
-
- const Pointf3& ref_f1 = points[facets[i].x];
- facet.vertex[0].x = ref_f1.x;
- facet.vertex[0].y = ref_f1.y;
- facet.vertex[0].z = ref_f1.z;
-
- const Pointf3& ref_f2 = points[facets[i].y];
- facet.vertex[1].x = ref_f2.x;
- facet.vertex[1].y = ref_f2.y;
- facet.vertex[1].z = ref_f2.z;
-
- const Pointf3& ref_f3 = points[facets[i].z];
- facet.vertex[2].x = ref_f3.x;
- facet.vertex[2].y = ref_f3.y;
- facet.vertex[2].z = ref_f3.z;
-
+ facet.vertex[0] = points[facets[i](0)].cast<float>();
+ facet.vertex[1] = points[facets[i](1)].cast<float>();
+ facet.vertex[2] = points[facets[i](2)].cast<float>();
facet.extra[0] = 0;
facet.extra[1] = 0;
+ stl_normal normal;
+ stl_calculate_normal(normal, &facet);
+ stl_normalize_vector(normal);
+ facet.normal = normal;
+
stl.facet_start[i] = facet;
}
stl_get_size(&stl);
}
-TriangleMesh::TriangleMesh(const TriangleMesh &other) :
- repaired(false)
-{
- stl_initialize(&this->stl);
- *this = other;
-}
-
-TriangleMesh::TriangleMesh(TriangleMesh &&other) :
- repaired(false)
-{
- stl_initialize(&this->stl);
- this->swap(other);
-}
-
TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
{
stl_close(&this->stl);
@@ -119,42 +94,8 @@ TriangleMesh& TriangleMesh::operator=(const TriangleMesh &other)
return *this;
}
-TriangleMesh& TriangleMesh::operator=(TriangleMesh &&other)
-{
- this->swap(other);
- return *this;
-}
-
-void
-TriangleMesh::swap(TriangleMesh &other)
-{
- std::swap(this->stl, other.stl);
- std::swap(this->repaired, other.repaired);
-}
-
-TriangleMesh::~TriangleMesh() {
- stl_close(&this->stl);
-}
-
-void
-TriangleMesh::ReadSTLFile(const char* input_file) {
- stl_open(&stl, input_file);
-}
-
-void
-TriangleMesh::write_ascii(const char* output_file)
+void TriangleMesh::repair()
{
- stl_write_ascii(&this->stl, output_file, "");
-}
-
-void
-TriangleMesh::write_binary(const char* output_file)
-{
- stl_write_binary(&this->stl, output_file, "");
-}
-
-void
-TriangleMesh::repair() {
if (this->repaired) return;
// admesh fails when repairing empty meshes
@@ -251,13 +192,7 @@ void TriangleMesh::check_topology()
}
}
-bool TriangleMesh::is_manifold() const
-{
- return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets;
-}
-
-void
-TriangleMesh::reset_repair_stats() {
+void TriangleMesh::reset_repair_stats() {
this->stl.stats.degenerate_facets = 0;
this->stl.stats.edges_fixed = 0;
this->stl.stats.facets_removed = 0;
@@ -267,8 +202,7 @@ TriangleMesh::reset_repair_stats() {
this->stl.stats.normals_fixed = 0;
}
-bool
-TriangleMesh::needed_repair() const
+bool TriangleMesh::needed_repair() const
{
return this->stl.stats.degenerate_facets > 0
|| this->stl.stats.edges_fixed > 0
@@ -278,14 +212,8 @@ TriangleMesh::needed_repair() const
|| this->stl.stats.backwards_edges > 0;
}
-size_t
-TriangleMesh::facets_count() const
+void TriangleMesh::WriteOBJFile(char* output_file)
{
- return this->stl.stats.number_of_facets;
-}
-
-void
-TriangleMesh::WriteOBJFile(char* output_file) {
stl_generate_shared_vertices(&stl);
stl_write_obj(&stl, output_file);
}
@@ -296,13 +224,9 @@ void TriangleMesh::scale(float factor)
stl_invalidate_shared_vertices(&this->stl);
}
-void TriangleMesh::scale(const Pointf3 &versor)
+void TriangleMesh::scale(const Vec3d &versor)
{
- float fversor[3];
- fversor[0] = versor.x;
- fversor[1] = versor.y;
- fversor[2] = versor.z;
- stl_scale_versor(&this->stl, fversor);
+ stl_scale_versor(&this->stl, versor.cast<float>());
stl_invalidate_shared_vertices(&this->stl);
}
@@ -332,19 +256,15 @@ void TriangleMesh::rotate(float angle, const Axis &axis)
stl_invalidate_shared_vertices(&this->stl);
}
-void TriangleMesh::rotate_x(float angle)
+void TriangleMesh::rotate(float angle, const Vec3d& axis)
{
- this->rotate(angle, X);
-}
-
-void TriangleMesh::rotate_y(float angle)
-{
- this->rotate(angle, Y);
-}
+ if (angle == 0.f)
+ return;
-void TriangleMesh::rotate_z(float angle)
-{
- this->rotate(angle, Z);
+ Vec3f axis_norm = axis.cast<float>().normalized();
+ Transform3f m = Transform3f::Identity();
+ m.rotate(Eigen::AngleAxisf(angle, axis_norm));
+ stl_transform(&stl, m);
}
void TriangleMesh::mirror(const Axis &axis)
@@ -359,46 +279,27 @@ void TriangleMesh::mirror(const Axis &axis)
stl_invalidate_shared_vertices(&this->stl);
}
-void TriangleMesh::mirror_x()
-{
- this->mirror(X);
-}
-
-void TriangleMesh::mirror_y()
-{
- this->mirror(Y);
-}
-
-void TriangleMesh::mirror_z()
+void TriangleMesh::transform(const Transform3f& t)
{
- this->mirror(Z);
-}
-
-void TriangleMesh::transform(const float* matrix3x4)
-{
- if (matrix3x4 == nullptr)
- return;
-
- stl_transform(&stl, const_cast<float*>(matrix3x4));
- stl_invalidate_shared_vertices(&stl);
+ stl_transform(&stl, t);
}
void TriangleMesh::align_to_origin()
{
this->translate(
- -(this->stl.stats.min.x),
- -(this->stl.stats.min.y),
- -(this->stl.stats.min.z)
- );
+ - this->stl.stats.min(0),
+ - this->stl.stats.min(1),
+ - this->stl.stats.min(2));
}
void TriangleMesh::rotate(double angle, Point* center)
{
if (angle == 0.)
return;
- this->translate(float(-center->x), float(-center->y), 0);
+ Vec2f c = center->cast<float>();
+ this->translate(-c(0), -c(1), 0);
stl_rotate_z(&(this->stl), (float)angle);
- this->translate(float(+center->x), float(+center->y), 0);
+ this->translate(c(0), c(1), 0);
}
bool TriangleMesh::has_multiple_patches() const
@@ -471,14 +372,14 @@ size_t TriangleMesh::number_of_patches() const
return num_bodies;
}
-TriangleMeshPtrs
-TriangleMesh::split() const
+TriangleMeshPtrs TriangleMesh::split() const
{
- TriangleMeshPtrs meshes;
- std::set<int> seen_facets;
+ TriangleMeshPtrs meshes;
+ std::vector<unsigned char> facet_visited(this->stl.stats.number_of_facets, false);
// we need neighbors
- if (!this->repaired) CONFESS("split() requires repair()");
+ if (!this->repaired)
+ CONFESS("split() requires repair()");
// loop while we have remaining facets
for (;;) {
@@ -486,46 +387,45 @@ TriangleMesh::split() const
std::queue<int> facet_queue;
std::deque<int> facets;
for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; facet_idx++) {
- if (seen_facets.find(facet_idx) == seen_facets.end()) {
+ if (! facet_visited[facet_idx]) {
// if facet was not seen put it into queue and start searching
facet_queue.push(facet_idx);
break;
}
}
- if (facet_queue.empty()) break;
-
- while (!facet_queue.empty()) {
+ if (facet_queue.empty())
+ break;
+
+ while (! facet_queue.empty()) {
int facet_idx = facet_queue.front();
facet_queue.pop();
- if (seen_facets.find(facet_idx) != seen_facets.end()) continue;
- facets.push_back(facet_idx);
- for (int j = 0; j <= 2; j++) {
- facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]);
+ if (! facet_visited[facet_idx]) {
+ facets.emplace_back(facet_idx);
+ for (int j = 0; j < 3; ++ j)
+ facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]);
+ facet_visited[facet_idx] = true;
}
- seen_facets.insert(facet_idx);
}
-
+
TriangleMesh* mesh = new TriangleMesh;
- meshes.push_back(mesh);
+ meshes.emplace_back(mesh);
mesh->stl.stats.type = inmemory;
mesh->stl.stats.number_of_facets = facets.size();
mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
stl_clear_error(&mesh->stl);
stl_allocate(&mesh->stl);
- int first = 1;
- for (std::deque<int>::const_iterator facet = facets.begin(); facet != facets.end(); ++facet) {
+ bool first = true;
+ for (std::deque<int>::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) {
mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
- first = 0;
}
}
return meshes;
}
-void
-TriangleMesh::merge(const TriangleMesh &mesh)
+void TriangleMesh::merge(const TriangleMesh &mesh)
{
// reset stats and metadata
int number_of_facets = this->stl.stats.number_of_facets;
@@ -556,45 +456,165 @@ ExPolygons TriangleMesh::horizontal_projection() const
stl_facet* facet = &this->stl.facet_start[i];
Polygon p;
p.points.resize(3);
- p.points[0] = Point::new_scale(facet->vertex[0].x, facet->vertex[0].y);
- p.points[1] = Point::new_scale(facet->vertex[1].x, facet->vertex[1].y);
- p.points[2] = Point::new_scale(facet->vertex[2].x, facet->vertex[2].y);
+ p.points[0] = Point::new_scale(facet->vertex[0](0), facet->vertex[0](1));
+ p.points[1] = Point::new_scale(facet->vertex[1](0), facet->vertex[1](1));
+ p.points[2] = Point::new_scale(facet->vertex[2](0), facet->vertex[2](1));
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
- pp.push_back(p);
+ pp.emplace_back(p);
}
// the offset factor was tuned using groovemount.stl
return union_ex(offset(pp, scale_(0.01)), true);
}
+const float* TriangleMesh::first_vertex() const
+{
+ return this->stl.facet_start ? &this->stl.facet_start->vertex[0](0) : nullptr;
+}
+
Polygon TriangleMesh::convex_hull()
{
this->require_shared_vertices();
Points pp;
pp.reserve(this->stl.stats.shared_vertices);
for (int i = 0; i < this->stl.stats.shared_vertices; ++ i) {
- stl_vertex* v = &this->stl.v_shared[i];
- pp.emplace_back(Point::new_scale(v->x, v->y));
+ const stl_vertex &v = this->stl.v_shared[i];
+ pp.emplace_back(Point::new_scale(v(0), v(1)));
}
return Slic3r::Geometry::convex_hull(pp);
}
-BoundingBoxf3
-TriangleMesh::bounding_box() const
+BoundingBoxf3 TriangleMesh::bounding_box() const
{
BoundingBoxf3 bb;
bb.defined = true;
- bb.min.x = this->stl.stats.min.x;
- bb.min.y = this->stl.stats.min.y;
- bb.min.z = this->stl.stats.min.z;
- bb.max.x = this->stl.stats.max.x;
- bb.max.y = this->stl.stats.max.y;
- bb.max.z = this->stl.stats.max.z;
+ bb.min = this->stl.stats.min.cast<double>();
+ bb.max = this->stl.stats.max.cast<double>();
return bb;
}
-void
-TriangleMesh::require_shared_vertices()
+BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d& t) const
+{
+ bool has_shared = (stl.v_shared != nullptr);
+ if (!has_shared)
+ stl_generate_shared_vertices(&stl);
+
+ unsigned int vertices_count = (stl.stats.shared_vertices > 0) ? (unsigned int)stl.stats.shared_vertices : 3 * (unsigned int)stl.stats.number_of_facets;
+
+ if (vertices_count == 0)
+ return BoundingBoxf3();
+
+ Eigen::MatrixXd src_vertices(3, vertices_count);
+
+ if (stl.stats.shared_vertices > 0)
+ {
+ stl_vertex* vertex_ptr = stl.v_shared;
+ for (int i = 0; i < stl.stats.shared_vertices; ++i)
+ {
+ src_vertices(0, i) = (double)(*vertex_ptr)(0);
+ src_vertices(1, i) = (double)(*vertex_ptr)(1);
+ src_vertices(2, i) = (double)(*vertex_ptr)(2);
+ vertex_ptr += 1;
+ }
+ }
+ else
+ {
+ stl_facet* facet_ptr = stl.facet_start;
+ unsigned int v_id = 0;
+ while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
+ {
+ for (int i = 0; i < 3; ++i)
+ {
+ src_vertices(0, v_id) = (double)facet_ptr->vertex[i](0);
+ src_vertices(1, v_id) = (double)facet_ptr->vertex[i](1);
+ src_vertices(2, v_id) = (double)facet_ptr->vertex[i](2);
+ ++v_id;
+ }
+ facet_ptr += 1;
+ }
+ }
+
+ if (!has_shared && (stl.stats.shared_vertices > 0))
+ stl_invalidate_shared_vertices(&stl);
+
+ Eigen::MatrixXd dst_vertices(3, vertices_count);
+ dst_vertices = t * src_vertices.colwise().homogeneous();
+
+ Vec3d v_min(dst_vertices(0, 0), dst_vertices(1, 0), dst_vertices(2, 0));
+ Vec3d v_max = v_min;
+
+ for (int i = 1; i < vertices_count; ++i)
+ {
+ for (int j = 0; j < 3; ++j)
+ {
+ v_min(j) = std::min(v_min(j), dst_vertices(j, i));
+ v_max(j) = std::max(v_max(j), dst_vertices(j, i));
+ }
+ }
+
+ return BoundingBoxf3(v_min, v_max);
+}
+
+TriangleMesh TriangleMesh::convex_hull_3d() const
+{
+ // Helper struct for qhull:
+ struct PointForQHull{
+ PointForQHull(float x_p, float y_p, float z_p) : x((realT)x_p), y((realT)y_p), z((realT)z_p) {}
+ realT x, y, z;
+ };
+ std::vector<PointForQHull> src_vertices;
+
+ // We will now fill the vector with input points for computation:
+ stl_facet* facet_ptr = stl.facet_start;
+ while (facet_ptr < stl.facet_start + stl.stats.number_of_facets)
+ {
+ for (int i = 0; i < 3; ++i)
+ {
+ const stl_vertex& v = facet_ptr->vertex[i];
+ src_vertices.emplace_back(v(0), v(1), v(2));
+ }
+
+ facet_ptr += 1;
+ }
+
+ // The qhull call:
+ orgQhull::Qhull qhull;
+ qhull.disableOutputStream(); // we want qhull to be quiet
+ try
+ {
+ qhull.runQhull("", 3, (int)src_vertices.size(), (const realT*)(src_vertices.data()), "Qt");
+ }
+ catch (...)
+ {
+ std::cout << "Unable to create convex hull" << std::endl;
+ return TriangleMesh();
+ }
+
+ // Let's collect results:
+ Pointf3s dst_vertices;
+ std::vector<Vec3crd> facets;
+ auto facet_list = qhull.facetList().toStdVector();
+ for (const orgQhull::QhullFacet& facet : facet_list)
+ { // iterate through facets
+ orgQhull::QhullVertexSet vertices = facet.vertices();
+ for (int i = 0; i < 3; ++i)
+ { // iterate through facet's vertices
+
+ orgQhull::QhullPoint p = vertices[i].point();
+ const float* coords = p.coordinates();
+ dst_vertices.emplace_back(coords[0], coords[1], coords[2]);
+ }
+ unsigned int size = (unsigned int)dst_vertices.size();
+ facets.emplace_back(size - 3, size - 2, size - 1);
+ }
+
+ TriangleMesh output_mesh(dst_vertices, facets);
+ output_mesh.repair();
+ output_mesh.require_shared_vertices();
+ return output_mesh;
+}
+
+void TriangleMesh::require_shared_vertices()
{
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
if (!this->repaired)
@@ -614,11 +634,8 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
facets_edges.assign(_mesh->stl.stats.number_of_facets * 3, -1);
v_scaled_shared.assign(_mesh->stl.v_shared, _mesh->stl.v_shared + _mesh->stl.stats.shared_vertices);
// Scale the copied vertices.
- for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i) {
- this->v_scaled_shared[i].x /= float(SCALING_FACTOR);
- this->v_scaled_shared[i].y /= float(SCALING_FACTOR);
- this->v_scaled_shared[i].z /= float(SCALING_FACTOR);
- }
+ for (int i = 0; i < this->mesh->stl.stats.shared_vertices; ++ i)
+ this->v_scaled_shared[i] *= float(1. / SCALING_FACTOR);
// Create a mapping from triangle edge into face.
struct EdgeToFace {
@@ -783,14 +800,14 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
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));
+ const float min_z = fminf(facet.vertex[0](2), fminf(facet.vertex[1](2), facet.vertex[2](2)));
+ const float max_z = fmaxf(facet.vertex[0](2), fmaxf(facet.vertex[1](2), facet.vertex[2](2)));
#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);
+ facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0](2),
+ facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1](2),
+ facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2](2));
printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif
@@ -810,24 +827,24 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
if (il.edge_type == feHorizontal) {
// Insert all three edges of the face.
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
- const bool reverse = this->mesh->stl.facet_start[facet_idx].normal.z < 0;
+ const bool reverse = this->mesh->stl.facet_start[facet_idx].normal(2) < 0;
for (int j = 0; j < 3; ++ j) {
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
if (reverse)
std::swap(a_id, b_id);
- const stl_vertex *a = &this->v_scaled_shared[a_id];
- const stl_vertex *b = &this->v_scaled_shared[b_id];
- il.a.x = a->x;
- il.a.y = a->y;
- il.b.x = b->x;
- il.b.y = b->y;
+ const stl_vertex &a = this->v_scaled_shared[a_id];
+ const stl_vertex &b = this->v_scaled_shared[b_id];
+ il.a(0) = a(0);
+ il.a(1) = a(1);
+ il.b(0) = b(0);
+ il.b(1) = b(1);
il.a_id = a_id;
il.b_id = b_id;
- (*lines)[layer_idx].push_back(il);
+ (*lines)[layer_idx].emplace_back(il);
}
} else
- (*lines)[layer_idx].push_back(il);
+ (*lines)[layer_idx].emplace_back(il);
}
}
}
@@ -867,66 +884,63 @@ bool TriangleMeshSlicer::slice_facet(
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
- int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
+ int i = (facet.vertex[1](2) == min_z) ? 1 : ((facet.vertex[2](2) == min_z) ? 2 : 0);
for (int j = i; j - i < 3; ++ j) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
- const stl_vertex *a = &this->v_scaled_shared[a_id];
- const stl_vertex *b = &this->v_scaled_shared[b_id];
+ const stl_vertex &a = this->v_scaled_shared[a_id];
+ const stl_vertex &b = this->v_scaled_shared[b_id];
// Is edge or face aligned with the cutting plane?
- if (a->z == slice_z && b->z == slice_z) {
+ if (a(2) == slice_z && b(2) == slice_z) {
// Edge is horizontal and belongs to the current layer.
const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
+ bool swap = false;
if (min_z == max_z) {
// All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal;
- if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) {
+ if (this->mesh->stl.facet_start[facet_idx].normal(2) < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
- std::swap(a, b);
- std::swap(a_id, b_id);
+ swap = true;
}
- } else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) {
+ } else if (v0(2) < slice_z || v1(2) < slice_z || v2(2) < slice_z) {
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
line_out->edge_type = feTop;
- std::swap(a, b);
- std::swap(a_id, b_id);
+ swap = true;
} else {
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
line_out->edge_type = feBottom;
}
- line_out->a.x = a->x;
- line_out->a.y = a->y;
- line_out->b.x = b->x;
- line_out->b.y = b->y;
- line_out->a_id = a_id;
- line_out->b_id = b_id;
+ line_out->a = to_2d(swap ? b : a).cast<coord_t>();
+ line_out->b = to_2d(swap ? a : b).cast<coord_t>();
+ line_out->a_id = swap ? b_id : a_id;
+ line_out->b_id = swap ? a_id : b_id;
return true;
}
- if (a->z == slice_z) {
+ if (a(2) == slice_z) {
// Only point a alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points;
IntersectionPoint &point = points[num_points ++];
- point.x = a->x;
- point.y = a->y;
+ point(0) = a(0);
+ point(1) = a(1);
point.point_id = a_id;
- } else if (b->z == slice_z) {
+ } else if (b(2) == slice_z) {
// Only point b alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points;
IntersectionPoint &point = points[num_points ++];
- point.x = b->x;
- point.y = b->y;
+ point(0) = b(0);
+ point(1) = b(1);
point.point_id = b_id;
- } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) {
+ } else if ((a(2) < slice_z && b(2) > slice_z) || (b(2) < slice_z && a(2) > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
IntersectionPoint &point = points[num_points ++];
- point.x = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z);
- point.y = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z);
+ point(0) = b(0) + (a(0) - b(0)) * (slice_z - b(2)) / (a(2) - b(2));
+ point(1) = b(1) + (a(1) - b(1)) * (slice_z - b(2)) / (a(2) - b(2));
point.edge_id = edge_id;
}
}
@@ -1197,8 +1211,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
// The current loop is complete. Add it to the output.
- assert(opl.points.front().point_id == opl.points.back().point_id);
- assert(opl.points.front().edge_id == opl.points.back().edge_id);
+ /*assert(opl.points.front().point_id == opl.points.back().point_id);
+ assert(opl.points.front().edge_id == opl.points.back().edge_id);*/
// Remove the duplicate last point.
opl.points.pop_back();
if (opl.points.size() >= 3) {
@@ -1207,7 +1221,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
// Orient the patched up polygons CCW. This heuristic may close some holes and cavities.
double area = 0.;
for (size_t i = 0, j = opl.points.size() - 1; i < opl.points.size(); j = i ++)
- area += double(opl.points[j].x + opl.points[i].x) * double(opl.points[i].y - opl.points[j].y);
+ area += double(opl.points[j](0) + opl.points[i](0)) * double(opl.points[i](1) - opl.points[j](1));
if (area < 0)
std::reverse(opl.points.begin(), opl.points.end());
loops->emplace_back(std::move(opl.points));
@@ -1234,9 +1248,9 @@ void TriangleMeshSlicer::make_expolygons_simple(std::vector<IntersectionLine> &l
if (loop->area() >= 0.) {
ExPolygon ex;
ex.contour = *loop;
- slices->push_back(ex);
+ slices->emplace_back(ex);
} else {
- holes.push_back(*loop);
+ holes.emplace_back(*loop);
}
}
@@ -1327,8 +1341,8 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
//std::vector<double> area;
//std::vector<size_t> sorted_area; // vector of indices
//for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++ loop) {
- // area.push_back(loop->area());
- // sorted_area.push_back(loop - loops.begin());
+ // area.emplace_back(loop->area());
+ // sorted_area.emplace_back(loop - loops.begin());
//}
//
//// outer first
@@ -1343,7 +1357,7 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slic
// would do the same, thus repeating the calculation */
// Polygons::const_iterator loop = loops.begin() + *loop_idx;
// if (area[*loop_idx] > +EPSILON)
- // p_slices.push_back(*loop);
+ // p_slices.emplace_back(*loop);
// else if (area[*loop_idx] < -EPSILON)
// //FIXME This is arbitrary and possibly very slow.
// // If the hole is inside a polygon, then there is no need to diff.
@@ -1393,20 +1407,20 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// find facet extents
- float min_z = std::min(facet->vertex[0].z, std::min(facet->vertex[1].z, facet->vertex[2].z));
- float max_z = std::max(facet->vertex[0].z, std::max(facet->vertex[1].z, facet->vertex[2].z));
+ float min_z = std::min(facet->vertex[0](2), std::min(facet->vertex[1](2), facet->vertex[2](2)));
+ float max_z = std::max(facet->vertex[0](2), std::max(facet->vertex[1](2), facet->vertex[2](2)));
// intersect facet with cutting plane
IntersectionLine line;
if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) {
// Save intersection lines for generating correct triangulations.
if (line.edge_type == feTop) {
- lower_lines.push_back(line);
+ lower_lines.emplace_back(line);
} else if (line.edge_type == feBottom) {
- upper_lines.push_back(line);
+ upper_lines.emplace_back(line);
} else if (line.edge_type != feHorizontal) {
- lower_lines.push_back(line);
- upper_lines.push_back(line);
+ lower_lines.emplace_back(line);
+ upper_lines.emplace_back(line);
}
}
@@ -1421,47 +1435,47 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// look for the vertex on whose side of the slicing plane there are no other vertices
int isolated_vertex;
- if ( (facet->vertex[0].z > z) == (facet->vertex[1].z > z) ) {
+ if ( (facet->vertex[0](2) > z) == (facet->vertex[1](2) > z) ) {
isolated_vertex = 2;
- } else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > z) ) {
+ } else if ( (facet->vertex[1](2) > z) == (facet->vertex[2](2) > z) ) {
isolated_vertex = 0;
} else {
isolated_vertex = 1;
}
// get vertices starting from the isolated one
- stl_vertex* v0 = &facet->vertex[isolated_vertex];
- stl_vertex* v1 = &facet->vertex[(isolated_vertex+1) % 3];
- stl_vertex* v2 = &facet->vertex[(isolated_vertex+2) % 3];
+ const stl_vertex &v0 = facet->vertex[isolated_vertex];
+ const stl_vertex &v1 = facet->vertex[(isolated_vertex+1) % 3];
+ const stl_vertex &v2 = facet->vertex[(isolated_vertex+2) % 3];
// intersect v0-v1 and v2-v0 with cutting plane and make new vertices
stl_vertex v0v1, v2v0;
- v0v1.x = v1->x + (v0->x - v1->x) * (z - v1->z) / (v0->z - v1->z);
- v0v1.y = v1->y + (v0->y - v1->y) * (z - v1->z) / (v0->z - v1->z);
- v0v1.z = z;
- v2v0.x = v2->x + (v0->x - v2->x) * (z - v2->z) / (v0->z - v2->z);
- v2v0.y = v2->y + (v0->y - v2->y) * (z - v2->z) / (v0->z - v2->z);
- v2v0.z = z;
+ v0v1(0) = v1(0) + (v0(0) - v1(0)) * (z - v1(2)) / (v0(2) - v1(2));
+ v0v1(1) = v1(1) + (v0(1) - v1(1)) * (z - v1(2)) / (v0(2) - v1(2));
+ v0v1(2) = z;
+ v2v0(0) = v2(0) + (v0(0) - v2(0)) * (z - v2(2)) / (v0(2) - v2(2));
+ v2v0(1) = v2(1) + (v0(1) - v2(1)) * (z - v2(2)) / (v0(2) - v2(2));
+ v2v0(2) = z;
// build the triangular facet
stl_facet triangle;
triangle.normal = facet->normal;
- triangle.vertex[0] = *v0;
+ triangle.vertex[0] = v0;
triangle.vertex[1] = v0v1;
triangle.vertex[2] = v2v0;
// build the facets forming a quadrilateral on the other side
stl_facet quadrilateral[2];
quadrilateral[0].normal = facet->normal;
- quadrilateral[0].vertex[0] = *v1;
- quadrilateral[0].vertex[1] = *v2;
+ quadrilateral[0].vertex[0] = v1;
+ quadrilateral[0].vertex[1] = v2;
quadrilateral[0].vertex[2] = v0v1;
quadrilateral[1].normal = facet->normal;
- quadrilateral[1].vertex[0] = *v2;
+ quadrilateral[1].vertex[0] = v2;
quadrilateral[1].vertex[1] = v2v0;
quadrilateral[1].vertex[2] = v0v1;
- if (v0->z > z) {
+ if (v0(2) > z) {
if (upper != NULL) stl_add_facet(&upper->stl, &triangle);
if (lower != NULL) {
stl_add_facet(&lower->stl, &quadrilateral[0]);
@@ -1493,13 +1507,11 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
Polygon p = *polygon;
p.reverse();
stl_facet facet;
- facet.normal.x = 0;
- facet.normal.y = 0;
- facet.normal.z = -1;
+ facet.normal = stl_normal(0, 0, -1.f);
for (size_t i = 0; i <= 2; ++i) {
- facet.vertex[i].x = unscale(p.points[i].x);
- facet.vertex[i].y = unscale(p.points[i].y);
- facet.vertex[i].z = z;
+ facet.vertex[i](0) = unscale<float>(p.points[i](0));
+ facet.vertex[i](1) = unscale<float>(p.points[i](1));
+ facet.vertex[i](2) = z;
}
stl_add_facet(&upper->stl, &facet);
}
@@ -1519,13 +1531,11 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// convert triangles to facets and append them to mesh
for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) {
stl_facet facet;
- facet.normal.x = 0;
- facet.normal.y = 0;
- facet.normal.z = 1;
+ facet.normal = stl_normal(0, 0, 1.f);
for (size_t i = 0; i <= 2; ++i) {
- facet.vertex[i].x = unscale(polygon->points[i].x);
- facet.vertex[i].y = unscale(polygon->points[i].y);
- facet.vertex[i].z = z;
+ facet.vertex[i](0) = unscale<float>(polygon->points[i](0));
+ facet.vertex[i](1) = unscale<float>(polygon->points[i](1));
+ facet.vertex[i](2) = z;
}
stl_add_facet(&lower->stl, &facet);
}
@@ -1538,19 +1548,19 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z.
TriangleMesh make_cube(double x, double y, double z) {
- Pointf3 pv[8] = {
- Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0),
- Pointf3(0, y, 0), Pointf3(x, y, z), Pointf3(0, y, z),
- Pointf3(0, 0, z), Pointf3(x, 0, z)
+ Vec3d pv[8] = {
+ Vec3d(x, y, 0), Vec3d(x, 0, 0), Vec3d(0, 0, 0),
+ Vec3d(0, y, 0), Vec3d(x, y, z), Vec3d(0, y, z),
+ Vec3d(0, 0, z), Vec3d(x, 0, z)
};
- Point3 fv[12] = {
- Point3(0, 1, 2), Point3(0, 2, 3), Point3(4, 5, 6),
- Point3(4, 6, 7), Point3(0, 4, 7), Point3(0, 7, 1),
- Point3(1, 7, 6), Point3(1, 6, 2), Point3(2, 6, 5),
- Point3(2, 5, 3), Point3(4, 0, 3), Point3(4, 3, 5)
+ Vec3crd fv[12] = {
+ Vec3crd(0, 1, 2), Vec3crd(0, 2, 3), Vec3crd(4, 5, 6),
+ Vec3crd(4, 6, 7), Vec3crd(0, 4, 7), Vec3crd(0, 7, 1),
+ Vec3crd(1, 7, 6), Vec3crd(1, 6, 2), Vec3crd(2, 6, 5),
+ Vec3crd(2, 5, 3), Vec3crd(4, 0, 3), Vec3crd(4, 3, 5)
};
- std::vector<Point3> facets(&fv[0], &fv[0]+12);
+ std::vector<Vec3crd> facets(&fv[0], &fv[0]+12);
Pointf3s vertices(&pv[0], &pv[0]+8);
TriangleMesh mesh(vertices ,facets);
@@ -1562,11 +1572,11 @@ TriangleMesh make_cube(double x, double y, double z) {
// Default is 360 sides, angle fa is in radians.
TriangleMesh make_cylinder(double r, double h, double fa) {
Pointf3s vertices;
- std::vector<Point3> facets;
+ std::vector<Vec3crd> facets;
// 2 special vertices, top and bottom center, rest are relative to this
- vertices.push_back(Pointf3(0.0, 0.0, 0.0));
- vertices.push_back(Pointf3(0.0, 0.0, h));
+ vertices.emplace_back(Vec3d(0.0, 0.0, 0.0));
+ vertices.emplace_back(Vec3d(0.0, 0.0, h));
// adjust via rounding to get an even multiple for any provided angle.
double angle = (2*PI / floor(2*PI / fa));
@@ -1576,26 +1586,23 @@ TriangleMesh make_cylinder(double r, double h, double fa) {
// top and bottom.
// Special case: Last line shares 2 vertices with the first line.
unsigned id = vertices.size() - 1;
- vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0));
- vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h));
+ vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, 0));
+ vertices.emplace_back(Vec3d(sin(0) * r , cos(0) * r, h));
for (double i = 0; i < 2*PI; i+=angle) {
- Pointf3 b(0, r, 0);
- Pointf3 t(0, r, h);
- b.rotate(i, Pointf3(0,0,0));
- t.rotate(i, Pointf3(0,0,h));
- vertices.push_back(b);
- vertices.push_back(t);
+ Vec2d p = Eigen::Rotation2Dd(i) * Eigen::Vector2d(0, r);
+ vertices.emplace_back(Vec3d(p(0), p(1), 0.));
+ vertices.emplace_back(Vec3d(p(0), p(1), h));
id = vertices.size() - 1;
- facets.push_back(Point3( 0, id - 1, id - 3)); // top
- facets.push_back(Point3(id, 1, id - 2)); // bottom
- facets.push_back(Point3(id, id - 2, id - 3)); // upper-right of side
- facets.push_back(Point3(id, id - 3, id - 1)); // bottom-left of side
+ facets.emplace_back(Vec3crd( 0, id - 1, id - 3)); // top
+ facets.emplace_back(Vec3crd(id, 1, id - 2)); // bottom
+ facets.emplace_back(Vec3crd(id, id - 2, id - 3)); // upper-right of side
+ facets.emplace_back(Vec3crd(id, id - 3, id - 1)); // bottom-left of side
}
// Connect the last set of vertices with the first.
- facets.push_back(Point3( 2, 0, id - 1));
- facets.push_back(Point3( 1, 3, id));
- facets.push_back(Point3(id, 3, 2));
- facets.push_back(Point3(id, 2, id - 1));
+ facets.emplace_back(Vec3crd( 2, 0, id - 1));
+ facets.emplace_back(Vec3crd( 1, 3, id));
+ facets.emplace_back(Vec3crd(id, 3, 2));
+ facets.emplace_back(Vec3crd(id, 2, id - 1));
TriangleMesh mesh(vertices, facets);
return mesh;
@@ -1606,7 +1613,7 @@ TriangleMesh make_cylinder(double r, double h, double fa) {
// Default angle is 1 degree.
TriangleMesh make_sphere(double rho, double fa) {
Pointf3s vertices;
- std::vector<Point3> facets;
+ std::vector<Vec3crd> facets;
// Algorithm:
// Add points one-by-one to the sphere grid and form facets using relative coordinates.
@@ -1618,29 +1625,24 @@ TriangleMesh make_sphere(double rho, double fa) {
// Ring to be scaled to generate the steps of the sphere
std::vector<double> ring;
for (double i = 0; i < 2*PI; i+=angle) {
- ring.push_back(i);
+ ring.emplace_back(i);
}
const size_t steps = ring.size();
const double increment = (double)(1.0 / (double)steps);
// special case: first ring connects to 0,0,0
// insert and form facets.
- vertices.push_back(Pointf3(0.0, 0.0, -rho));
+ vertices.emplace_back(Vec3d(0.0, 0.0, -rho));
size_t id = vertices.size();
for (size_t i = 0; i < ring.size(); i++) {
// Fixed scaling
const double z = -rho + increment*rho*2.0;
// radius of the circle for this step.
const double r = sqrt(abs(rho*rho - z*z));
- Pointf3 b(0, r, z);
- b.rotate(ring[i], Pointf3(0,0,z));
- vertices.push_back(b);
- if (i == 0) {
- facets.push_back(Point3(1, 0, ring.size()));
- } else {
- facets.push_back(Point3(id, 0, id - 1));
- }
- id++;
+ Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
+ vertices.emplace_back(Vec3d(b(0), b(1), z));
+ facets.emplace_back((i == 0) ? Vec3crd(1, 0, ring.size()) : Vec3crd(id, 0, id - 1));
+ ++ id;
}
// General case: insert and form facets for each step, joining it to the ring below it.
@@ -1649,16 +1651,15 @@ TriangleMesh make_sphere(double rho, double fa) {
const double r = sqrt(abs(rho*rho - z*z));
for (size_t i = 0; i < ring.size(); i++) {
- Pointf3 b(0, r, z);
- b.rotate(ring[i], Pointf3(0,0,z));
- vertices.push_back(b);
+ Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
+ vertices.emplace_back(Vec3d(b(0), b(1), z));
if (i == 0) {
// wrap around
- facets.push_back(Point3(id + ring.size() - 1 , id, id - 1));
- facets.push_back(Point3(id, id - ring.size(), id - 1));
+ facets.emplace_back(Vec3crd(id + ring.size() - 1 , id, id - 1));
+ facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1));
} else {
- facets.push_back(Point3(id , id - ring.size(), (id - 1) - ring.size()));
- facets.push_back(Point3(id, id - 1 - ring.size() , id - 1));
+ facets.emplace_back(Vec3crd(id , id - ring.size(), (id - 1) - ring.size()));
+ facets.emplace_back(Vec3crd(id, id - 1 - ring.size() , id - 1));
}
id++;
}
@@ -1667,13 +1668,13 @@ TriangleMesh make_sphere(double rho, double fa) {
// special case: last ring connects to 0,0,rho*2.0
// only form facets.
- vertices.push_back(Pointf3(0.0, 0.0, rho));
+ vertices.emplace_back(Vec3d(0.0, 0.0, rho));
for (size_t i = 0; i < ring.size(); i++) {
if (i == 0) {
// third vertex is on the other side of the ring.
- facets.push_back(Point3(id, id - ring.size(), id - 1));
+ facets.emplace_back(Vec3crd(id, id - ring.size(), id - 1));
} else {
- facets.push_back(Point3(id, id - ring.size() + i, id - ring.size() + (i - 1)));
+ facets.emplace_back(Vec3crd(id, id - ring.size() + i, id - ring.size() + (i - 1)));
}
}
id++;
diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp
index 9a09975cd..ed3e6022d 100644
--- a/xs/src/libslic3r/TriangleMesh.hpp
+++ b/xs/src/libslic3r/TriangleMesh.hpp
@@ -21,44 +21,50 @@ typedef std::vector<TriangleMesh*> TriangleMeshPtrs;
class TriangleMesh
{
public:
- TriangleMesh();
- TriangleMesh(const Pointf3s &points, const std::vector<Point3> &facets);
- TriangleMesh(const TriangleMesh &other);
- TriangleMesh(TriangleMesh &&other);
+ TriangleMesh() : repaired(false) { stl_initialize(&this->stl); }
+ TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd> &facets);
+ TriangleMesh(const TriangleMesh &other) : repaired(false) { stl_initialize(&this->stl); *this = other; }
+ TriangleMesh(TriangleMesh &&other) : repaired(false) { stl_initialize(&this->stl); this->swap(other); }
+ ~TriangleMesh() { stl_close(&this->stl); }
TriangleMesh& operator=(const TriangleMesh &other);
- TriangleMesh& operator=(TriangleMesh &&other);
- void swap(TriangleMesh &other);
- ~TriangleMesh();
- void ReadSTLFile(const char* input_file);
- void write_ascii(const char* output_file);
- void write_binary(const char* output_file);
+ TriangleMesh& operator=(TriangleMesh &&other) { this->swap(other); return *this; }
+ void swap(TriangleMesh &other) { std::swap(this->stl, other.stl); std::swap(this->repaired, other.repaired); }
+ void ReadSTLFile(const char* input_file) { stl_open(&stl, input_file); }
+ void write_ascii(const char* output_file) { stl_write_ascii(&this->stl, output_file, ""); }
+ void write_binary(const char* output_file) { stl_write_binary(&this->stl, output_file, ""); }
void repair();
float volume();
void check_topology();
- bool is_manifold() const;
+ bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets; }
void WriteOBJFile(char* output_file);
void scale(float factor);
- void scale(const Pointf3 &versor);
+ void scale(const Vec3d &versor);
void translate(float x, float y, float z);
void rotate(float angle, const Axis &axis);
- void rotate_x(float angle);
- void rotate_y(float angle);
- void rotate_z(float angle);
+ void rotate(float angle, const Vec3d& axis);
+ void rotate_x(float angle) { this->rotate(angle, X); }
+ void rotate_y(float angle) { this->rotate(angle, Y); }
+ void rotate_z(float angle) { this->rotate(angle, Z); }
void mirror(const Axis &axis);
- void mirror_x();
- void mirror_y();
- void mirror_z();
- void transform(const float* matrix3x4);
+ void mirror_x() { this->mirror(X); }
+ void mirror_y() { this->mirror(Y); }
+ void mirror_z() { this->mirror(Z); }
+ void transform(const Transform3f& t);
void align_to_origin();
void rotate(double angle, Point* center);
TriangleMeshPtrs split() const;
void merge(const TriangleMesh &mesh);
ExPolygons horizontal_projection() const;
+ const float* first_vertex() const;
Polygon convex_hull();
BoundingBoxf3 bounding_box() const;
+ // Returns the bbox of this TriangleMesh transformed by the given transformation
+ BoundingBoxf3 transformed_bounding_box(const Transform3d& t) const;
+ // Returns the convex hull of this TriangleMesh
+ TriangleMesh convex_hull_3d() const;
void reset_repair_stats();
bool needed_repair() const;
- size_t facets_count() const;
+ size_t facets_count() const { return this->stl.stats.number_of_facets; }
// Returns true, if there are two and more connected patches in the mesh.
// Returns false, if one or zero connected patch is in the mesh.
@@ -67,7 +73,7 @@ public:
// Count disconnected triangle patches.
size_t number_of_patches() const;
- stl_file stl;
+ mutable stl_file stl;
bool repaired;
private:
diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp
index 27e7fad6b..f1390b8a2 100644
--- a/xs/src/libslic3r/Utils.hpp
+++ b/xs/src/libslic3r/Utils.hpp
@@ -3,6 +3,8 @@
#include <locale>
+#include "libslic3r.h"
+
namespace Slic3r {
extern void set_logging_level(unsigned int level);
@@ -60,8 +62,8 @@ extern std::string timestamp_str();
// to be placed at the top of Slic3r generated files.
inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); }
-// Encode a file into a multi-part HTTP response with a given boundary.
-std::string octoprint_encode_file_send_request_content(const char *path, bool select, bool print, const char *boundary);
+// getpid platform wrapper
+extern unsigned get_current_pid();
// Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html
@@ -82,6 +84,28 @@ inline T next_highest_power_of_2(T v)
return ++ v;
}
+extern std::string xml_escape(std::string text);
+
+class PerlCallback {
+public:
+ PerlCallback(void *sv) : m_callback(nullptr) { this->register_callback(sv); }
+ PerlCallback() : m_callback(nullptr) {}
+ ~PerlCallback() { this->deregister_callback(); }
+ void register_callback(void *sv);
+ void deregister_callback();
+ void call() const;
+ void call(int i) const;
+ void call(int i, int j) const;
+ void call(const std::vector<int>& ints) const;
+ void call(double a) const;
+ void call(double a, double b) const;
+ void call(double a, double b, double c) const;
+ void call(double a, double b, double c, double d) const;
+ void call(bool b) const;
+private:
+ void *m_callback;
+};
+
} // namespace Slic3r
#endif // slic3r_Utils_hpp_
diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h
index 0f192c37c..925e93031 100644
--- a/xs/src/libslic3r/libslic3r.h
+++ b/xs/src/libslic3r/libslic3r.h
@@ -14,7 +14,7 @@
#include <boost/thread.hpp>
#define SLIC3R_FORK_NAME "Slic3r Prusa Edition"
-#define SLIC3R_VERSION "1.39.0"
+#define SLIC3R_VERSION "1.41.0"
#define SLIC3R_BUILD "UNKNOWN"
typedef int32_t coord_t;
@@ -45,7 +45,6 @@ typedef double coordf_t;
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)
-#define unscale(val) ((val) * SCALING_FACTOR)
#define SCALED_EPSILON scale_(EPSILON)
/* Implementation of CONFESS("foo"): */
#ifdef _MSC_VER
@@ -102,6 +101,9 @@ inline std::string debug_out_path(const char *name, ...)
namespace Slic3r {
+template<typename T, typename Q>
+inline T unscale(Q v) { return T(v) * T(SCALING_FACTOR); }
+
enum Axis { X=0, Y, Z, E, F, NUM_AXES };
template <class T>
@@ -130,6 +132,17 @@ inline void append(std::vector<T>& dest, std::vector<T>&& src)
src.shrink_to_fit();
}
+// Casting an std::vector<> from one type to another type without warnings about a loss of accuracy.
+template<typename T_TO, typename T_FROM>
+std::vector<T_TO> cast(const std::vector<T_FROM> &src)
+{
+ std::vector<T_TO> dst;
+ dst.reserve(src.size());
+ for (const T_FROM &a : src)
+ dst.emplace_back((T_TO)a);
+ return dst;
+}
+
template <typename T>
inline void remove_nulls(std::vector<T*> &vec)
{
diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp
index 34b9eaa9f..95aaf5453 100644
--- a/xs/src/libslic3r/utils.cpp
+++ b/xs/src/libslic3r/utils.cpp
@@ -1,6 +1,15 @@
+#include "Utils.hpp"
+#include "I18N.hpp"
+
#include <locale>
#include <ctime>
+#ifdef WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
@@ -87,7 +96,7 @@ const std::string& var_dir()
std::string var(const std::string &file_name)
{
- auto file = boost::filesystem::canonical(boost::filesystem::path(g_var_dir) / file_name).make_preferred();
+ auto file = (boost::filesystem::path(g_var_dir) / file_name).make_preferred();
return file.string();
}
@@ -115,6 +124,9 @@ const std::string& localization_dir()
return g_local_dir;
}
+// Translate function callback, to call wxWidgets translate function to convert non-localized UTF8 string to a localized one.
+Slic3r::I18N::translate_fn_type Slic3r::I18N::translate_fn = nullptr;
+
static std::string g_data_dir;
void set_data_dir(const std::string &dir)
@@ -129,44 +141,6 @@ const std::string& data_dir()
} // namespace Slic3r
-#ifdef SLIC3R_HAS_BROKEN_CROAK
-
-// Some Strawberry Perl builds (mainly the latest 64bit builds) have a broken mechanism
-// for emiting Perl exception after handling a C++ exception. Perl interpreter
-// simply hangs. Better to show a message box in that case and stop the application.
-
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#ifdef WIN32
-#include <Windows.h>
-#endif
-
-void confess_at(const char *file, int line, const char *func, const char *format, ...)
-{
- char dest[1024*8];
- va_list argptr;
- va_start(argptr, format);
- vsprintf(dest, format, argptr);
- va_end(argptr);
-
- char filelinefunc[1024*8];
- sprintf(filelinefunc, "\r\nin function: %s\r\nfile: %s\r\nline: %d\r\n", func, file, line);
- strcat(dest, filelinefunc);
- strcat(dest, "\r\n Closing the application.\r\n");
- #ifdef WIN32
- ::MessageBoxA(NULL, dest, "Slic3r Prusa Edition", MB_OK | MB_ICONERROR);
- #endif
-
- // Give up.
- printf(dest);
- exit(-1);
-}
-
-#else
-
#include <xsinit.h>
void
@@ -196,7 +170,157 @@ confess_at(const char *file, int line, const char *func,
#endif
}
-#endif
+void PerlCallback::register_callback(void *sv)
+{
+ if (! SvROK((SV*)sv) || SvTYPE(SvRV((SV*)sv)) != SVt_PVCV)
+ croak("Not a Callback %_ for PerlFunction", (SV*)sv);
+ if (m_callback)
+ SvSetSV((SV*)m_callback, (SV*)sv);
+ else
+ m_callback = newSVsv((SV*)sv);
+}
+
+void PerlCallback::deregister_callback()
+{
+ if (m_callback) {
+ sv_2mortal((SV*)m_callback);
+ m_callback = nullptr;
+ }
+}
+
+void PerlCallback::call() const
+{
+ if (! m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(int i) const
+{
+ if (! m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSViv(i)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(int i, int j) const
+{
+ if (! m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSViv(i)));
+ XPUSHs(sv_2mortal(newSViv(j)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(const std::vector<int>& ints) const
+{
+ if (! m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ for (int i : ints)
+ {
+ XPUSHs(sv_2mortal(newSViv(i)));
+ }
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double a) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double a, double b) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double a, double b, double c) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ XPUSHs(sv_2mortal(newSVnv(c)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(double a, double b, double c, double d) const
+{
+ if (!m_callback)
+ return;
+ dSP;
+ ENTER;
+ SAVETMPS;
+ PUSHMARK(SP);
+ XPUSHs(sv_2mortal(newSVnv(a)));
+ XPUSHs(sv_2mortal(newSVnv(b)));
+ XPUSHs(sv_2mortal(newSVnv(c)));
+ XPUSHs(sv_2mortal(newSVnv(d)));
+ PUTBACK;
+ perl_call_sv(SvRV((SV*)m_callback), G_DISCARD);
+ FREETMPS;
+ LEAVE;
+}
+
+void PerlCallback::call(bool b) const
+{
+ call(b ? 1 : 0);
+}
#ifdef WIN32
#ifndef NOMINMAX
@@ -263,7 +387,6 @@ namespace PerlUtils {
std::string timestamp_str()
{
const auto now = boost::posix_time::second_clock::local_time();
- const auto date = now.date();
char buf[2048];
sprintf(buf, "on %04d-%02d-%02d at %02d:%02d:%02d",
// Local date in an ANSII format.
@@ -272,31 +395,40 @@ std::string timestamp_str()
return buf;
}
-std::string octoprint_encode_file_send_request_content(const char *cpath, bool select, bool print, const char *boundary)
+unsigned get_current_pid()
+{
+#ifdef WIN32
+ return GetCurrentProcessId();
+#else
+ return ::getpid();
+#endif
+}
+
+std::string xml_escape(std::string text)
{
- // Read the complete G-code string into a string buffer.
- // It will throw if the file cannot be open or read.
- std::stringstream str_stream;
+ std::string::size_type pos = 0;
+ for (;;)
{
- boost::nowide::ifstream ifs(cpath);
- str_stream << ifs.rdbuf();
+ pos = text.find_first_of("\"\'&<>", pos);
+ if (pos == std::string::npos)
+ break;
+
+ std::string replacement;
+ switch (text[pos])
+ {
+ case '\"': replacement = "&quot;"; break;
+ case '\'': replacement = "&apos;"; break;
+ case '&': replacement = "&amp;"; break;
+ case '<': replacement = "&lt;"; break;
+ case '>': replacement = "&gt;"; break;
+ default: break;
+ }
+
+ text.replace(pos, 1, replacement);
+ pos += replacement.size();
}
- boost::filesystem::path path(cpath);
- std::string request = boundary + '\n';
- request += "Content-Disposition: form-data; name=\"";
- request += path.stem().string() + "\"; filename=\"" + path.filename().string() + "\"\n";
- request += "Content-Type: application/octet-stream\n\n";
- request += str_stream.str();
- request += boundary + '\n';
- request += "Content-Disposition: form-data; name=\"select\"\n\n";
- request += select ? "true\n" : "false\n";
- request += boundary + '\n';
- request += "Content-Disposition: form-data; name=\"print\"\n\n";
- request += print ? "true\n" : "false\n";
- request += boundary + '\n';
-
- return request;
+ return text;
}
}; // namespace Slic3r