From 6adc3477c9d08d2cfa0e6902b3d241a9193e50d4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 3 Aug 2014 19:42:29 +0200 Subject: Moved C++ code into new libslic3r directory --- xs/MANIFEST | 117 +-- xs/src/BoundingBox.cpp | 197 ----- xs/src/BoundingBox.hpp | 76 -- xs/src/ClipperUtils.cpp | 568 ------------- xs/src/ClipperUtils.hpp | 111 --- xs/src/Config.cpp | 377 --------- xs/src/Config.hpp | 521 ------------ xs/src/ExPolygon.cpp | 428 ---------- xs/src/ExPolygon.hpp | 154 ---- xs/src/ExPolygonCollection.cpp | 90 -- xs/src/ExPolygonCollection.hpp | 32 - xs/src/Extruder.cpp | 128 --- xs/src/Extruder.hpp | 46 - xs/src/ExtrusionEntity.cpp | 335 -------- xs/src/ExtrusionEntity.hpp | 104 --- xs/src/ExtrusionEntityCollection.cpp | 115 --- xs/src/ExtrusionEntityCollection.hpp | 29 - xs/src/Flow.cpp | 120 --- xs/src/Flow.hpp | 53 -- xs/src/GCode.hpp | 35 - xs/src/Geometry.cpp | 334 -------- xs/src/Geometry.hpp | 41 - xs/src/Layer.cpp | 144 ---- xs/src/Layer.hpp | 124 --- xs/src/LayerRegion.cpp | 45 - xs/src/Line.cpp | 181 ---- xs/src/Line.hpp | 68 -- xs/src/Model.cpp | 369 -------- xs/src/Model.hpp | 181 ---- xs/src/MotionPlanner.cpp | 278 ------ xs/src/MotionPlanner.hpp | 61 -- xs/src/MultiPoint.cpp | 161 ---- xs/src/MultiPoint.hpp | 44 - xs/src/PlaceholderParser.cpp | 120 --- xs/src/PlaceholderParser.hpp | 32 - xs/src/Point.cpp | 337 -------- xs/src/Point.hpp | 141 ---- xs/src/Polygon.cpp | 225 ----- xs/src/Polygon.hpp | 130 --- xs/src/Polyline.cpp | 172 ---- xs/src/Polyline.hpp | 33 - xs/src/PolylineCollection.cpp | 57 -- xs/src/PolylineCollection.hpp | 20 - xs/src/Print.cpp | 345 -------- xs/src/Print.hpp | 187 ----- xs/src/PrintConfig.cpp | 967 --------------------- xs/src/PrintConfig.hpp | 567 ------------- xs/src/PrintObject.cpp | 254 ------ xs/src/PrintRegion.cpp | 70 -- xs/src/SVG.cpp | 51 -- xs/src/SVG.hpp | 25 - xs/src/Surface.cpp | 56 -- xs/src/Surface.hpp | 35 - xs/src/SurfaceCollection.cpp | 75 -- xs/src/SurfaceCollection.hpp | 22 - xs/src/TriangleMesh.cpp | 1070 ------------------------ xs/src/TriangleMesh.hpp | 110 --- xs/src/libslic3r/BoundingBox.cpp | 197 +++++ xs/src/libslic3r/BoundingBox.hpp | 76 ++ xs/src/libslic3r/ClipperUtils.cpp | 568 +++++++++++++ xs/src/libslic3r/ClipperUtils.hpp | 111 +++ xs/src/libslic3r/Config.cpp | 377 +++++++++ xs/src/libslic3r/Config.hpp | 521 ++++++++++++ xs/src/libslic3r/ExPolygon.cpp | 428 ++++++++++ xs/src/libslic3r/ExPolygon.hpp | 154 ++++ xs/src/libslic3r/ExPolygonCollection.cpp | 90 ++ xs/src/libslic3r/ExPolygonCollection.hpp | 32 + xs/src/libslic3r/Extruder.cpp | 128 +++ xs/src/libslic3r/Extruder.hpp | 46 + xs/src/libslic3r/ExtrusionEntity.cpp | 335 ++++++++ xs/src/libslic3r/ExtrusionEntity.hpp | 104 +++ xs/src/libslic3r/ExtrusionEntityCollection.cpp | 115 +++ xs/src/libslic3r/ExtrusionEntityCollection.hpp | 29 + xs/src/libslic3r/Flow.cpp | 120 +++ xs/src/libslic3r/Flow.hpp | 53 ++ xs/src/libslic3r/GCode.hpp | 35 + xs/src/libslic3r/Geometry.cpp | 334 ++++++++ xs/src/libslic3r/Geometry.hpp | 41 + xs/src/libslic3r/Layer.cpp | 144 ++++ xs/src/libslic3r/Layer.hpp | 124 +++ xs/src/libslic3r/LayerRegion.cpp | 45 + xs/src/libslic3r/Line.cpp | 181 ++++ xs/src/libslic3r/Line.hpp | 68 ++ xs/src/libslic3r/Model.cpp | 369 ++++++++ xs/src/libslic3r/Model.hpp | 181 ++++ xs/src/libslic3r/MotionPlanner.cpp | 278 ++++++ xs/src/libslic3r/MotionPlanner.hpp | 61 ++ xs/src/libslic3r/MultiPoint.cpp | 161 ++++ xs/src/libslic3r/MultiPoint.hpp | 44 + xs/src/libslic3r/PlaceholderParser.cpp | 120 +++ xs/src/libslic3r/PlaceholderParser.hpp | 32 + xs/src/libslic3r/Point.cpp | 337 ++++++++ xs/src/libslic3r/Point.hpp | 141 ++++ xs/src/libslic3r/Polygon.cpp | 225 +++++ xs/src/libslic3r/Polygon.hpp | 130 +++ xs/src/libslic3r/Polyline.cpp | 172 ++++ xs/src/libslic3r/Polyline.hpp | 33 + xs/src/libslic3r/PolylineCollection.cpp | 57 ++ xs/src/libslic3r/PolylineCollection.hpp | 20 + xs/src/libslic3r/Print.cpp | 345 ++++++++ xs/src/libslic3r/Print.hpp | 187 +++++ xs/src/libslic3r/PrintConfig.cpp | 967 +++++++++++++++++++++ xs/src/libslic3r/PrintConfig.hpp | 567 +++++++++++++ xs/src/libslic3r/PrintObject.cpp | 254 ++++++ xs/src/libslic3r/PrintRegion.cpp | 70 ++ xs/src/libslic3r/SVG.cpp | 51 ++ xs/src/libslic3r/SVG.hpp | 25 + xs/src/libslic3r/Surface.cpp | 56 ++ xs/src/libslic3r/Surface.hpp | 35 + xs/src/libslic3r/SurfaceCollection.cpp | 75 ++ xs/src/libslic3r/SurfaceCollection.hpp | 22 + xs/src/libslic3r/TriangleMesh.cpp | 1070 ++++++++++++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 110 +++ xs/src/libslic3r/libslic3r.h | 26 + xs/src/libslic3r/utils.cpp | 28 + xs/src/myinit.h | 17 +- xs/src/utils.cpp | 28 - xs/xsp/BoundingBox.xsp | 4 +- xs/xsp/Clipper.xsp | 2 +- xs/xsp/Config.xsp | 2 +- xs/xsp/ExPolygon.xsp | 2 +- xs/xsp/ExPolygonCollection.xsp | 2 +- xs/xsp/Extruder.xsp | 2 +- xs/xsp/ExtrusionEntityCollection.xsp | 2 +- xs/xsp/ExtrusionLoop.xsp | 2 +- xs/xsp/ExtrusionPath.xsp | 4 +- xs/xsp/Flow.xsp | 2 +- xs/xsp/Geometry.xsp | 2 +- xs/xsp/Layer.xsp | 3 +- xs/xsp/Line.xsp | 4 +- xs/xsp/Model.xsp | 4 +- xs/xsp/MotionPlanner.xsp | 2 +- xs/xsp/PlaceholderParser.xsp | 2 +- xs/xsp/Point.xsp | 6 +- xs/xsp/Polygon.xsp | 6 +- xs/xsp/Polyline.xsp | 6 +- xs/xsp/PolylineCollection.xsp | 2 +- xs/xsp/Print.xsp | 4 +- xs/xsp/Surface.xsp | 4 +- xs/xsp/SurfaceCollection.xsp | 2 +- xs/xsp/TriangleMesh.xsp | 2 +- 141 files changed, 10801 insertions(+), 10790 deletions(-) delete mode 100644 xs/src/BoundingBox.cpp delete mode 100644 xs/src/BoundingBox.hpp delete mode 100644 xs/src/ClipperUtils.cpp delete mode 100644 xs/src/ClipperUtils.hpp delete mode 100644 xs/src/Config.cpp delete mode 100644 xs/src/Config.hpp delete mode 100644 xs/src/ExPolygon.cpp delete mode 100644 xs/src/ExPolygon.hpp delete mode 100644 xs/src/ExPolygonCollection.cpp delete mode 100644 xs/src/ExPolygonCollection.hpp delete mode 100644 xs/src/Extruder.cpp delete mode 100644 xs/src/Extruder.hpp delete mode 100644 xs/src/ExtrusionEntity.cpp delete mode 100644 xs/src/ExtrusionEntity.hpp delete mode 100644 xs/src/ExtrusionEntityCollection.cpp delete mode 100644 xs/src/ExtrusionEntityCollection.hpp delete mode 100644 xs/src/Flow.cpp delete mode 100644 xs/src/Flow.hpp delete mode 100644 xs/src/GCode.hpp delete mode 100644 xs/src/Geometry.cpp delete mode 100644 xs/src/Geometry.hpp delete mode 100644 xs/src/Layer.cpp delete mode 100644 xs/src/Layer.hpp delete mode 100644 xs/src/LayerRegion.cpp delete mode 100644 xs/src/Line.cpp delete mode 100644 xs/src/Line.hpp delete mode 100644 xs/src/Model.cpp delete mode 100644 xs/src/Model.hpp delete mode 100644 xs/src/MotionPlanner.cpp delete mode 100644 xs/src/MotionPlanner.hpp delete mode 100644 xs/src/MultiPoint.cpp delete mode 100644 xs/src/MultiPoint.hpp delete mode 100644 xs/src/PlaceholderParser.cpp delete mode 100644 xs/src/PlaceholderParser.hpp delete mode 100644 xs/src/Point.cpp delete mode 100644 xs/src/Point.hpp delete mode 100644 xs/src/Polygon.cpp delete mode 100644 xs/src/Polygon.hpp delete mode 100644 xs/src/Polyline.cpp delete mode 100644 xs/src/Polyline.hpp delete mode 100644 xs/src/PolylineCollection.cpp delete mode 100644 xs/src/PolylineCollection.hpp delete mode 100644 xs/src/Print.cpp delete mode 100644 xs/src/Print.hpp delete mode 100644 xs/src/PrintConfig.cpp delete mode 100644 xs/src/PrintConfig.hpp delete mode 100644 xs/src/PrintObject.cpp delete mode 100644 xs/src/PrintRegion.cpp delete mode 100644 xs/src/SVG.cpp delete mode 100644 xs/src/SVG.hpp delete mode 100644 xs/src/Surface.cpp delete mode 100644 xs/src/Surface.hpp delete mode 100644 xs/src/SurfaceCollection.cpp delete mode 100644 xs/src/SurfaceCollection.hpp delete mode 100644 xs/src/TriangleMesh.cpp delete mode 100644 xs/src/TriangleMesh.hpp create mode 100644 xs/src/libslic3r/BoundingBox.cpp create mode 100644 xs/src/libslic3r/BoundingBox.hpp create mode 100644 xs/src/libslic3r/ClipperUtils.cpp create mode 100644 xs/src/libslic3r/ClipperUtils.hpp create mode 100644 xs/src/libslic3r/Config.cpp create mode 100644 xs/src/libslic3r/Config.hpp create mode 100644 xs/src/libslic3r/ExPolygon.cpp create mode 100644 xs/src/libslic3r/ExPolygon.hpp create mode 100644 xs/src/libslic3r/ExPolygonCollection.cpp create mode 100644 xs/src/libslic3r/ExPolygonCollection.hpp create mode 100644 xs/src/libslic3r/Extruder.cpp create mode 100644 xs/src/libslic3r/Extruder.hpp create mode 100644 xs/src/libslic3r/ExtrusionEntity.cpp create mode 100644 xs/src/libslic3r/ExtrusionEntity.hpp create mode 100644 xs/src/libslic3r/ExtrusionEntityCollection.cpp create mode 100644 xs/src/libslic3r/ExtrusionEntityCollection.hpp create mode 100644 xs/src/libslic3r/Flow.cpp create mode 100644 xs/src/libslic3r/Flow.hpp create mode 100644 xs/src/libslic3r/GCode.hpp create mode 100644 xs/src/libslic3r/Geometry.cpp create mode 100644 xs/src/libslic3r/Geometry.hpp create mode 100644 xs/src/libslic3r/Layer.cpp create mode 100644 xs/src/libslic3r/Layer.hpp create mode 100644 xs/src/libslic3r/LayerRegion.cpp create mode 100644 xs/src/libslic3r/Line.cpp create mode 100644 xs/src/libslic3r/Line.hpp create mode 100644 xs/src/libslic3r/Model.cpp create mode 100644 xs/src/libslic3r/Model.hpp create mode 100644 xs/src/libslic3r/MotionPlanner.cpp create mode 100644 xs/src/libslic3r/MotionPlanner.hpp create mode 100644 xs/src/libslic3r/MultiPoint.cpp create mode 100644 xs/src/libslic3r/MultiPoint.hpp create mode 100644 xs/src/libslic3r/PlaceholderParser.cpp create mode 100644 xs/src/libslic3r/PlaceholderParser.hpp create mode 100644 xs/src/libslic3r/Point.cpp create mode 100644 xs/src/libslic3r/Point.hpp create mode 100644 xs/src/libslic3r/Polygon.cpp create mode 100644 xs/src/libslic3r/Polygon.hpp create mode 100644 xs/src/libslic3r/Polyline.cpp create mode 100644 xs/src/libslic3r/Polyline.hpp create mode 100644 xs/src/libslic3r/PolylineCollection.cpp create mode 100644 xs/src/libslic3r/PolylineCollection.hpp create mode 100644 xs/src/libslic3r/Print.cpp create mode 100644 xs/src/libslic3r/Print.hpp create mode 100644 xs/src/libslic3r/PrintConfig.cpp create mode 100644 xs/src/libslic3r/PrintConfig.hpp create mode 100644 xs/src/libslic3r/PrintObject.cpp create mode 100644 xs/src/libslic3r/PrintRegion.cpp create mode 100644 xs/src/libslic3r/SVG.cpp create mode 100644 xs/src/libslic3r/SVG.hpp create mode 100644 xs/src/libslic3r/Surface.cpp create mode 100644 xs/src/libslic3r/Surface.hpp create mode 100644 xs/src/libslic3r/SurfaceCollection.cpp create mode 100644 xs/src/libslic3r/SurfaceCollection.hpp create mode 100644 xs/src/libslic3r/TriangleMesh.cpp create mode 100644 xs/src/libslic3r/TriangleMesh.hpp create mode 100644 xs/src/libslic3r/libslic3r.h create mode 100644 xs/src/libslic3r/utils.cpp delete mode 100644 xs/src/utils.cpp (limited to 'xs') diff --git a/xs/MANIFEST b/xs/MANIFEST index c38096709..6a44597da 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -1,6 +1,6 @@ Build.PL lib/Slic3r/XS.pm -MANIFEST This list of files +MANIFEST src/admesh/connect.c src/admesh/normals.c src/admesh/shared.c @@ -1648,46 +1648,68 @@ src/boost/utility/swap.hpp src/boost/utility/value_init.hpp src/boost/version.hpp src/boost/visit_each.hpp -src/BoundingBox.cpp -src/BoundingBox.hpp src/clipper.cpp src/clipper.hpp -src/ClipperUtils.cpp -src/ClipperUtils.hpp -src/Config.cpp -src/Config.hpp -src/ExPolygon.cpp -src/ExPolygon.hpp -src/ExPolygonCollection.cpp -src/ExPolygonCollection.hpp -src/Extruder.cpp -src/Extruder.hpp -src/ExtrusionEntity.cpp -src/ExtrusionEntity.hpp -src/ExtrusionEntityCollection.cpp -src/ExtrusionEntityCollection.hpp -src/Flow.cpp -src/Flow.hpp -src/GCode.hpp -src/Geometry.cpp -src/Geometry.hpp -src/Layer.cpp -src/Layer.hpp -src/LayerRegion.cpp -src/Line.cpp -src/Line.hpp -src/Model.cpp -src/Model.hpp -src/MotionPlanner.cpp -src/MotionPlanner.hpp -src/MultiPoint.cpp -src/MultiPoint.hpp +src/libslic3r/BoundingBox.cpp +src/libslic3r/BoundingBox.hpp +src/libslic3r/ClipperUtils.cpp +src/libslic3r/ClipperUtils.hpp +src/libslic3r/Config.cpp +src/libslic3r/Config.hpp +src/libslic3r/ExPolygon.cpp +src/libslic3r/ExPolygon.hpp +src/libslic3r/ExPolygonCollection.cpp +src/libslic3r/ExPolygonCollection.hpp +src/libslic3r/Extruder.cpp +src/libslic3r/Extruder.hpp +src/libslic3r/ExtrusionEntity.cpp +src/libslic3r/ExtrusionEntity.hpp +src/libslic3r/ExtrusionEntityCollection.cpp +src/libslic3r/ExtrusionEntityCollection.hpp +src/libslic3r/Flow.cpp +src/libslic3r/Flow.hpp +src/libslic3r/GCode.hpp +src/libslic3r/Geometry.cpp +src/libslic3r/Geometry.hpp +src/libslic3r/Layer.cpp +src/libslic3r/Layer.hpp +src/libslic3r/LayerRegion.cpp +src/libslic3r/libslic3r.h +src/libslic3r/Line.cpp +src/libslic3r/Line.hpp +src/libslic3r/Model.cpp +src/libslic3r/Model.hpp +src/libslic3r/MotionPlanner.cpp +src/libslic3r/MotionPlanner.hpp +src/libslic3r/MultiPoint.cpp +src/libslic3r/MultiPoint.hpp +src/libslic3r/PlaceholderParser.cpp +src/libslic3r/PlaceholderParser.hpp +src/libslic3r/Point.cpp +src/libslic3r/Point.hpp +src/libslic3r/Polygon.cpp +src/libslic3r/Polygon.hpp +src/libslic3r/Polyline.cpp +src/libslic3r/Polyline.hpp +src/libslic3r/PolylineCollection.cpp +src/libslic3r/PolylineCollection.hpp +src/libslic3r/Print.cpp +src/libslic3r/Print.hpp +src/libslic3r/PrintConfig.cpp +src/libslic3r/PrintConfig.hpp +src/libslic3r/PrintObject.cpp +src/libslic3r/PrintRegion.cpp +src/libslic3r/Surface.cpp +src/libslic3r/Surface.hpp +src/libslic3r/SurfaceCollection.cpp +src/libslic3r/SurfaceCollection.hpp +src/libslic3r/SVG.cpp +src/libslic3r/SVG.hpp +src/libslic3r/TriangleMesh.cpp +src/libslic3r/TriangleMesh.hpp +src/libslic3r/utils.cpp src/myinit.h src/perlglue.hpp -src/PlaceholderParser.cpp -src/PlaceholderParser.hpp -src/Point.cpp -src/Point.hpp src/poly2tri/common/shapes.cc src/poly2tri/common/shapes.h src/poly2tri/common/utils.h @@ -1700,30 +1722,9 @@ src/poly2tri/sweep/sweep.cc src/poly2tri/sweep/sweep.h src/poly2tri/sweep/sweep_context.cc src/poly2tri/sweep/sweep_context.h -src/Polygon.cpp -src/Polygon.hpp -src/Polyline.cpp -src/Polyline.hpp -src/PolylineCollection.cpp -src/PolylineCollection.hpp src/polypartition.cpp src/polypartition.h src/ppport.h -src/Print.cpp -src/Print.hpp -src/PrintConfig.cpp -src/PrintConfig.hpp -src/PrintObject.cpp -src/PrintRegion.cpp -src/Surface.cpp -src/Surface.hpp -src/SurfaceCollection.cpp -src/SurfaceCollection.hpp -src/SVG.cpp -src/SVG.hpp -src/TriangleMesh.cpp -src/TriangleMesh.hpp -src/utils.cpp t/01_trianglemesh.t t/03_point.t t/04_expolygon.t diff --git a/xs/src/BoundingBox.cpp b/xs/src/BoundingBox.cpp deleted file mode 100644 index 55c6abbe0..000000000 --- a/xs/src/BoundingBox.cpp +++ /dev/null @@ -1,197 +0,0 @@ -#include "BoundingBox.hpp" -#include - -namespace Slic3r { - -template -BoundingBoxBase::BoundingBoxBase(const std::vector &points) -{ - if (points.empty()) CONFESS("Empty point set supplied to BoundingBoxBase constructor"); - typename std::vector::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); - } -} -template BoundingBoxBase::BoundingBoxBase(const std::vector &points); -template BoundingBoxBase::BoundingBoxBase(const std::vector &points); - -template -BoundingBox3Base::BoundingBox3Base(const std::vector &points) - : BoundingBoxBase(points) -{ - if (points.empty()) CONFESS("Empty point set supplied to BoundingBox3Base constructor"); - typename std::vector::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); - } -} -template BoundingBox3Base::BoundingBox3Base(const std::vector &points); - -BoundingBox::BoundingBox(const Lines &lines) -{ - Points points; - for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { - points.push_back(line->a); - points.push_back(line->b); - } - *this = BoundingBox(points); -} - -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 -BoundingBox::polygon() const -{ - Polygon p; - this->polygon(&p); - return p; -} - -template void -BoundingBoxBase::scale(double factor) -{ - this->min.scale(factor); - this->max.scale(factor); -} -template void BoundingBoxBase::scale(double factor); -template void BoundingBoxBase::scale(double factor); -template void BoundingBoxBase::scale(double factor); - -template void -BoundingBoxBase::merge(const PointClass &point) -{ - 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); -} -template void BoundingBoxBase::merge(const Point &point); -template void BoundingBoxBase::merge(const Pointf &point); - -template void -BoundingBoxBase::merge(const BoundingBoxBase &bb) -{ - 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); -} -template void BoundingBoxBase::merge(const BoundingBoxBase &bb); -template void BoundingBoxBase::merge(const BoundingBoxBase &bb); - -template void -BoundingBox3Base::merge(const PointClass &point) -{ - BoundingBoxBase::merge(point); - this->min.z = std::min(point.z, this->min.z); - this->max.z = std::max(point.z, this->max.z); -} -template void BoundingBox3Base::merge(const Pointf3 &point); - -template void -BoundingBox3Base::merge(const BoundingBox3Base &bb) -{ - BoundingBoxBase::merge(bb); - this->min.z = std::min(bb.min.z, this->min.z); - this->max.z = std::max(bb.max.z, this->max.z); -} -template void BoundingBox3Base::merge(const BoundingBox3Base &bb); - -template PointClass -BoundingBoxBase::size() const -{ - return PointClass(this->max.x - this->min.x, this->max.y - this->min.y); -} -template Point BoundingBoxBase::size() const; -template Pointf BoundingBoxBase::size() const; - -template PointClass -BoundingBox3Base::size() const -{ - return PointClass(this->max.x - this->min.x, this->max.y - this->min.y, this->max.z - this->min.z); -} -template Pointf3 BoundingBox3Base::size() const; - -template void -BoundingBoxBase::translate(coordf_t x, coordf_t y) -{ - this->min.translate(x, y); - this->max.translate(x, y); -} -template void BoundingBoxBase::translate(coordf_t x, coordf_t y); -template void BoundingBoxBase::translate(coordf_t x, coordf_t y); - -template void -BoundingBox3Base::translate(coordf_t x, coordf_t y, coordf_t z) -{ - this->min.translate(x, y, z); - this->max.translate(x, y, z); -} -template void BoundingBox3Base::translate(coordf_t x, coordf_t y, coordf_t z); - -template void -BoundingBoxBase::offset(coordf_t delta) -{ - this->min.translate(-delta, -delta); - this->max.translate(delta, delta); -} -template void BoundingBoxBase::offset(coordf_t delta); -template void BoundingBoxBase::offset(coordf_t delta); - -template void -BoundingBox3Base::offset(coordf_t delta) -{ - this->min.translate(-delta, -delta, -delta); - this->max.translate(delta, delta, delta); -} -template void BoundingBox3Base::offset(coordf_t delta); - -template PointClass -BoundingBoxBase::center() const -{ - return PointClass( - (this->max.x + this->min.x)/2, - (this->max.y + this->min.y)/2 - ); -} -template Point BoundingBoxBase::center() const; -template Pointf BoundingBoxBase::center() const; - -template PointClass -BoundingBox3Base::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 - ); -} -template Pointf3 BoundingBox3Base::center() const; - -#ifdef SLIC3RXS -REGISTER_CLASS(BoundingBox, "Geometry::BoundingBox"); -REGISTER_CLASS(BoundingBoxf, "Geometry::BoundingBoxf"); -REGISTER_CLASS(BoundingBoxf3, "Geometry::BoundingBoxf3"); -#endif - -} diff --git a/xs/src/BoundingBox.hpp b/xs/src/BoundingBox.hpp deleted file mode 100644 index 0ea81b831..000000000 --- a/xs/src/BoundingBox.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef slic3r_BoundingBox_hpp_ -#define slic3r_BoundingBox_hpp_ - -#include -#include "Point.hpp" -#include "Polygon.hpp" - -namespace Slic3r { - -typedef Point Size; -typedef Point3 Size3; -typedef Pointf Sizef; -typedef Pointf3 Sizef3; - -template -class BoundingBoxBase -{ - public: - PointClass min; - PointClass max; - - BoundingBoxBase() {}; - BoundingBoxBase(const std::vector &points); - void merge(const PointClass &point); - void merge(const BoundingBoxBase &bb); - void scale(double factor); - PointClass size() const; - void translate(coordf_t x, coordf_t y); - void offset(coordf_t delta); - PointClass center() const; -}; - -template -class BoundingBox3Base : public BoundingBoxBase -{ - public: - BoundingBox3Base() {}; - BoundingBox3Base(const std::vector &points); - void merge(const PointClass &point); - void merge(const BoundingBox3Base &bb); - PointClass size() const; - void translate(coordf_t x, coordf_t y, coordf_t z); - void offset(coordf_t delta); - PointClass center() const; -}; - -class BoundingBox : public BoundingBoxBase -{ - public: - void polygon(Polygon* polygon) const; - Polygon polygon() const; - - BoundingBox() {}; - BoundingBox(const Points &points) : BoundingBoxBase(points) {}; - BoundingBox(const Lines &lines); -}; - -/* -class BoundingBox3 : public BoundingBox3Base {}; -*/ - -class BoundingBoxf : public BoundingBoxBase { - public: - BoundingBoxf() {}; - BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; -}; - -class BoundingBoxf3 : public BoundingBox3Base { - public: - BoundingBoxf3() {}; - BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; -}; - -} - -#endif diff --git a/xs/src/ClipperUtils.cpp b/xs/src/ClipperUtils.cpp deleted file mode 100644 index 240cf3b68..000000000 --- a/xs/src/ClipperUtils.cpp +++ /dev/null @@ -1,568 +0,0 @@ -#include "ClipperUtils.hpp" -#include "Geometry.hpp" - -namespace Slic3r { - -//----------------------------------------------------------- -// legacy code from Clipper documentation -void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons) -{ - size_t cnt = expolygons.size(); - expolygons.resize(cnt + 1); - ClipperPath_to_Slic3rMultiPoint(polynode.Contour, expolygons[cnt].contour); - expolygons[cnt].holes.resize(polynode.ChildCount()); - for (int i = 0; i < polynode.ChildCount(); ++i) - { - ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, expolygons[cnt].holes[i]); - //Add outer polygons contained by (nested within) holes ... - for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) - AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); - } -} - -void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons) -{ - expolygons.clear(); - for (int i = 0; i < polytree.ChildCount(); ++i) - AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons); -} -//----------------------------------------------------------- - -template -void -ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output) -{ - output.points.clear(); - for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) { - output.points.push_back(Slic3r::Point( (*pit).X, (*pit).Y )); - } -} - -template -void -ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output) -{ - output.clear(); - for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { - typename T::value_type p; - ClipperPath_to_Slic3rMultiPoint(*it, p); - output.push_back(p); - } -} - -void -ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output) -{ - // init Clipper - ClipperLib::Clipper clipper; - clipper.Clear(); - - // perform union - clipper.AddPaths(input, ClipperLib::ptSubject, true); - ClipperLib::PolyTree polytree; - clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero - - // write to ExPolygons object - output.clear(); - PolyTreeToExPolygons(polytree, output); -} - -void -Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output) -{ - output.clear(); - for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) { - output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); - } -} - -template -void -Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output) -{ - output.clear(); - for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) { - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*it, p); - output.push_back(p); - } -} - -void -scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale) -{ - for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) { - for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { - (*pit).X *= scale; - (*pit).Y *= scale; - } - } -} - -void -offset(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, input); - - // scale input - scaleClipperPolygons(input, scale); - - // perform offset - ClipperLib::ClipperOffset co; - if (joinType == jtRound) { - co.ArcTolerance = miterLimit; - } else { - co.MiterLimit = miterLimit; - } - co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); - co.Execute(retval, (delta*scale)); - - // unscale output - scaleClipperPolygons(retval, 1/scale); -} - -void -offset(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(polygons, output, delta, scale, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void -offset(const Slic3r::Polylines &polylines, ClipperLib::Paths &retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polylines, input); - - // scale input - scaleClipperPolygons(input, scale); - - // perform offset - ClipperLib::ClipperOffset co; - if (joinType == jtRound) { - co.ArcTolerance = miterLimit; - } else { - co.MiterLimit = miterLimit; - } - co.AddPaths(input, joinType, ClipperLib::etOpenButt); - co.Execute(retval, (delta*scale)); - - // unscale output - scaleClipperPolygons(retval, 1/scale); -} - -void -offset(const Slic3r::Polylines &polylines, Slic3r::Polygons &retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(polylines, output, delta, scale, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void -offset(const Slic3r::Surface &surface, Slic3r::Surfaces &retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - Slic3r::ExPolygons expp; - offset_ex(surface.expolygon, expp, delta, scale, joinType, miterLimit); - - // clone the input surface for each expolygon we got - retval.clear(); - retval.reserve(expp.size()); - for (ExPolygons::iterator it = expp.begin(); it != expp.end(); ++it) { - Surface s = surface; // clone - s.expolygon = *it; - retval.push_back(s); - } -} - -void -offset_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(polygons, output, delta, scale, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rExPolygons(output, retval); -} - -void -offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) -{ - // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, input); - - // scale input - scaleClipperPolygons(input, scale); - - // prepare ClipperOffset object - ClipperLib::ClipperOffset co; - if (joinType == jtRound) { - co.ArcTolerance = miterLimit; - } else { - co.MiterLimit = miterLimit; - } - - // perform first offset - ClipperLib::Paths output1; - co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); - co.Execute(output1, (delta1*scale)); - - // perform second offset - co.Clear(); - co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); - co.Execute(retval, (delta2*scale)); - - // unscale output - scaleClipperPolygons(retval, 1/scale); -} - -void -offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset2(polygons, output, delta1, delta2, scale, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void -offset2_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset2(polygons, output, delta1, delta2, scale, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rExPolygons(output, retval); -} - -template -void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, T &retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_) -{ - // read input - ClipperLib::Paths input_subject, input_clip; - Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); - Slic3rMultiPoints_to_ClipperPaths(clip, input_clip); - - // perform safety offset - if (safety_offset_) { - if (clipType == ClipperLib::ctUnion) { - safety_offset(&input_subject); - } else { - safety_offset(&input_clip); - } - } - - // init Clipper - ClipperLib::Clipper clipper; - clipper.Clear(); - - // add polygons - clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); - - // perform operation - clipper.Execute(clipType, retval, fillType, fillType); -} - -void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType, - const bool safety_offset_) -{ - // read input - ClipperLib::Paths input_subject, input_clip; - Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); - Slic3rMultiPoints_to_ClipperPaths(clip, input_clip); - - // perform safety offset - if (safety_offset_) safety_offset(&input_clip); - - // init Clipper - ClipperLib::Clipper clipper; - clipper.Clear(); - - // add polygons - clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); - - // perform operation - clipper.Execute(clipType, retval, fillType, fillType); -} - -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_) -{ - // perform operation - ClipperLib::Paths output; - _clipper_do(clipType, subject, clip, output, ClipperLib::pftNonZero, safety_offset_); - - // convert into Polygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_) -{ - // perform operation - ClipperLib::PolyTree polytree; - _clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_); - - // convert into ExPolygons - PolyTreeToExPolygons(polytree, retval); -} - -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_) -{ - // perform operation - ClipperLib::PolyTree polytree; - _clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_); - - // convert into Polylines - ClipperLib::Paths output; - ClipperLib::PolyTreeToPaths(polytree, output); - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_) -{ - // transform input polygons into polylines - Slic3r::Polylines polylines; - polylines.reserve(subject.size()); - for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) - polylines.push_back(*polygon); // implicit call to split_at_first_point() - - // perform clipping - _clipper(clipType, polylines, clip, retval, safety_offset_); - - /* If the split_at_first_point() call above happens to split the polygon inside the clipping area - we would get two consecutive polylines instead of a single one, so we go through them in order - 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 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())) { - /* 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())) { - /* 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())) { - /* 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(); - retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); - retval.erase(retval.begin() + j); - --j; - } - } - } -} - -template -void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_) -{ - _clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); -} -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); -template void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); - -template -void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_) -{ - _clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); -} -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); -template void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); - -void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, - bool safety_offset_) -{ - _clipper(ClipperLib::ctXor, subject, clip, retval, safety_offset_); -} - -template -void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_) -{ - Slic3r::Polygons p; - _clipper(ClipperLib::ctUnion, subject, p, retval, safety_offset_); -} -template void union_(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool safety_offset_); -template void union_(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_); - -void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_) -{ - Slic3r::Polygons clip; - _clipper_do(ClipperLib::ctUnion, subject, clip, retval, ClipperLib::pftEvenOdd, safety_offset_); -} - -void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_) -{ - ClipperLib::PolyTree pt; - union_pt(subject, pt, safety_offset_); - traverse_pt(pt.Childs, retval); -} - -static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval) -{ - /* use a nearest neighbor search to order these children - TODO: supply start_near to chained_path() too? */ - - // collect ordering points - Points ordering_points; - ordering_points.reserve(nodes.size()); - for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { - Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); - ordering_points.push_back(p); - } - - // perform the ordering - ClipperLib::PolyNodes ordered_nodes; - Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes); - - // push results recursively - for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { - // traverse the next depth - traverse_pt((*it)->Childs, retval); - - Polygon p; - ClipperPath_to_Slic3rMultiPoint((*it)->Contour, p); - retval.push_back(p); - if ((*it)->IsHole()) retval.back().reverse(); // ccw - } -} - -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear) -{ - // convert into Clipper polygons - ClipperLib::Paths input_subject, output; - Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); - - if (preserve_collinear) { - ClipperLib::Clipper c; - c.PreserveCollinear(true); - c.StrictlySimple(true); - c.AddPaths(input_subject, ClipperLib::ptSubject, true); - c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - } else { - ClipperLib::SimplifyPolygons(input_subject, output, ClipperLib::pftNonZero); - } - - // convert into Slic3r polygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear) -{ - if (!preserve_collinear) { - Polygons polygons; - simplify_polygons(subject, polygons, preserve_collinear); - union_(polygons, retval); - return; - } - - // convert into Clipper polygons - ClipperLib::Paths input_subject; - Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); - - ClipperLib::PolyTree polytree; - - ClipperLib::Clipper c; - c.PreserveCollinear(true); - c.StrictlySimple(true); - c.AddPaths(input_subject, ClipperLib::ptSubject, true); - c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); - - // convert into ExPolygons - PolyTreeToExPolygons(polytree, retval); -} - -void safety_offset(ClipperLib::Paths* paths) -{ - // scale input - scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE); - - // perform offset (delta = scale 1e-05) - ClipperLib::ClipperOffset co; - co.MiterLimit = 2; - co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); - co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE); - - // unscale output - scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE); -} - -/////////////////////// - -#ifdef SLIC3RXS -SV* -polynode_children_2_perl(const ClipperLib::PolyNode& node) -{ - AV* av = newAV(); - const unsigned int len = node.ChildCount(); - if (len > 0) av_extend(av, len-1); - for (int i = 0; i < len; ++i) { - av_store(av, i, polynode2perl(*node.Childs[i])); - } - return (SV*)newRV_noinc((SV*)av); -} - -SV* -polynode2perl(const ClipperLib::PolyNode& node) -{ - HV* hv = newHV(); - Slic3r::Polygon p; - ClipperPath_to_Slic3rMultiPoint(node.Contour, p); - if (node.IsHole()) { - (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); - } else { - (void)hv_stores( hv, "outer", Slic3r::perl_to_SV_clone_ref(p) ); - } - (void)hv_stores( hv, "children", polynode_children_2_perl(node) ); - return (SV*)newRV_noinc((SV*)hv); -} -#endif - -} diff --git a/xs/src/ClipperUtils.hpp b/xs/src/ClipperUtils.hpp deleted file mode 100644 index 2ab3ff775..000000000 --- a/xs/src/ClipperUtils.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef slic3r_ClipperUtils_hpp_ -#define slic3r_ClipperUtils_hpp_ - -#include -#include "clipper.hpp" -#include "ExPolygon.hpp" -#include "Polygon.hpp" -#include "Surface.hpp" - -// import these wherever we're included -using ClipperLib::jtMiter; -using ClipperLib::jtRound; -using ClipperLib::jtSquare; - -namespace Slic3r { - -#define CLIPPER_OFFSET_SCALE 100000.0 - -//----------------------------------------------------------- -// legacy code from Clipper documentation -void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); -void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); -//----------------------------------------------------------- - -void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output); -template -void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output); -template -void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output); -template -void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output); -void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output); - -void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); - -// offset Polygons -void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); - -// offset Polylines -void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths &retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, - double miterLimit = 3); -void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons &retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, - double miterLimit = 3); -void offset(const Slic3r::Surface &surface, Slic3r::Surfaces &retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, - double miterLimit = 3); - -void offset_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); - -void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset2_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); - -template -void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, T &retval, bool safety_offset_); -void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, ClipperLib::Paths &retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines &retval); - -template -void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false); - -template -void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false); - -void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, - bool safety_offset_ = false); - -template -void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_ = false); - -void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_ = false); -void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_ = false); -static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval); - -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear = false); -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear = false); - -void safety_offset(ClipperLib::Paths* paths); - -///////////////// - -#ifdef SLIC3RXS -SV* polynode_children_2_perl(const ClipperLib::PolyNode& node); -SV* polynode2perl(const ClipperLib::PolyNode& node); -#endif - -} - -#endif diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp deleted file mode 100644 index 4d03b36a0..000000000 --- a/xs/src/Config.cpp +++ /dev/null @@ -1,377 +0,0 @@ -#include "Config.hpp" - -namespace Slic3r { - -bool -ConfigBase::has(const t_config_option_key opt_key) { - return (this->option(opt_key, false) != NULL); -} - -void -ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) { - // get list of option keys to apply - t_config_option_keys opt_keys; - other.keys(&opt_keys); - - // loop through options and apply them - for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) { - ConfigOption* my_opt = this->option(*it, true); - if (my_opt == NULL) { - if (ignore_nonexistent == false) throw "Attempt to apply non-existent option"; - continue; - } - - // not the most efficient way, but easier than casting pointers to subclasses - bool res = my_opt->deserialize( other.option(*it)->serialize() ); - if (!res) CONFESS("Unexpected failure when deserializing serialized value"); - } -} - -std::string -ConfigBase::serialize(const t_config_option_key opt_key) { - ConfigOption* opt = this->option(opt_key); - assert(opt != NULL); - return opt->serialize(); -} - -bool -ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) { - if (this->def->count(opt_key) == 0) throw "Calling set_deserialize() on unknown option"; - ConfigOptionDef* optdef = &(*this->def)[opt_key]; - if (!optdef->shortcut.empty()) { - for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) { - if (!this->set_deserialize(*it, str)) return false; - } - return true; - } - - ConfigOption* opt = this->option(opt_key, true); - assert(opt != NULL); - return opt->deserialize(str); -} - -double -ConfigBase::get_abs_value(const t_config_option_key opt_key) { - ConfigOption* opt = this->option(opt_key, false); - if (ConfigOptionFloatOrPercent* optv = dynamic_cast(opt)) { - // get option definition - assert(this->def->count(opt_key) != 0); - ConfigOptionDef* def = &(*this->def)[opt_key]; - - // compute absolute value over the absolute value of the base option - return optv->get_abs_value(this->get_abs_value(def->ratio_over)); - } else if (ConfigOptionFloat* optv = dynamic_cast(opt)) { - return optv->value; - } else { - throw "Not a valid option type for get_abs_value()"; - } -} - -double -ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over) { - // get stored option value - ConfigOptionFloatOrPercent* opt = dynamic_cast(this->option(opt_key)); - assert(opt != NULL); - - // compute absolute value - return opt->get_abs_value(ratio_over); -} - -#ifdef SLIC3RXS -SV* -ConfigBase::as_hash() { - HV* hv = newHV(); - - t_config_option_keys opt_keys; - this->keys(&opt_keys); - - for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) - (void)hv_store( hv, it->c_str(), it->length(), this->get(*it), 0 ); - - return newRV_noinc((SV*)hv); -} - -SV* -ConfigBase::get(t_config_option_key opt_key) { - ConfigOption* opt = this->option(opt_key); - if (opt == NULL) return &PL_sv_undef; - if (ConfigOptionFloat* optv = dynamic_cast(opt)) { - return newSVnv(optv->value); - } else if (ConfigOptionPercent* optv = dynamic_cast(opt)) { - return newSVnv(optv->value); - } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { - AV* av = newAV(); - av_fill(av, optv->values.size()-1); - for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) - av_store(av, it - optv->values.begin(), newSVnv(*it)); - return newRV_noinc((SV*)av); - } else if (ConfigOptionInt* optv = dynamic_cast(opt)) { - return newSViv(optv->value); - } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { - AV* av = newAV(); - av_fill(av, optv->values.size()-1); - for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) - av_store(av, it - optv->values.begin(), newSViv(*it)); - return newRV_noinc((SV*)av); - } else if (ConfigOptionString* optv = dynamic_cast(opt)) { - // we don't serialize() because that would escape newlines - return newSVpvn_utf8(optv->value.c_str(), optv->value.length(), true); - } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { - AV* av = newAV(); - av_fill(av, optv->values.size()-1); - for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) - av_store(av, it - optv->values.begin(), newSVpvn_utf8(it->c_str(), it->length(), true)); - return newRV_noinc((SV*)av); - } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { - return optv->point.to_SV_pureperl(); - } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { - AV* av = newAV(); - av_fill(av, optv->values.size()-1); - for (Pointfs::iterator it = optv->values.begin(); it != optv->values.end(); ++it) - av_store(av, it - optv->values.begin(), it->to_SV_pureperl()); - return newRV_noinc((SV*)av); - } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { - return newSViv(optv->value ? 1 : 0); - } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { - AV* av = newAV(); - av_fill(av, optv->values.size()-1); - for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) - av_store(av, it - optv->values.begin(), newSViv(*it ? 1 : 0)); - return newRV_noinc((SV*)av); - } else { - std::string serialized = opt->serialize(); - return newSVpvn_utf8(serialized.c_str(), serialized.length(), true); - } -} - -SV* -ConfigBase::get_at(t_config_option_key opt_key, size_t i) { - ConfigOption* opt = this->option(opt_key); - if (opt == NULL) return &PL_sv_undef; - - if (ConfigOptionFloats* optv = dynamic_cast(opt)) { - return newSVnv(optv->get_at(i)); - } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { - return newSViv(optv->get_at(i)); - } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { - // we don't serialize() because that would escape newlines - std::string val = optv->get_at(i); - return newSVpvn_utf8(val.c_str(), val.length(), true); - } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { - return optv->get_at(i).to_SV_pureperl(); - } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { - return newSViv(optv->get_at(i) ? 1 : 0); - } else { - return &PL_sv_undef; - } -} - -bool -ConfigBase::set(t_config_option_key opt_key, SV* value) { - ConfigOption* opt = this->option(opt_key, true); - if (opt == NULL) CONFESS("Trying to set non-existing option"); - - if (ConfigOptionFloat* optv = dynamic_cast(opt)) { - if (!looks_like_number(value)) return false; - optv->value = SvNV(value); - } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { - std::vector values; - AV* av = (AV*)SvRV(value); - const size_t len = av_len(av)+1; - for (size_t i = 0; i < len; i++) { - SV** elem = av_fetch(av, i, 0); - if (elem == NULL || !looks_like_number(*elem)) return false; - values.push_back(SvNV(*elem)); - } - optv->values = values; - } else if (ConfigOptionInt* optv = dynamic_cast(opt)) { - if (!looks_like_number(value)) return false; - optv->value = SvIV(value); - } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { - std::vector values; - AV* av = (AV*)SvRV(value); - const size_t len = av_len(av)+1; - for (size_t i = 0; i < len; i++) { - SV** elem = av_fetch(av, i, 0); - if (elem == NULL || !looks_like_number(*elem)) return false; - values.push_back(SvIV(*elem)); - } - optv->values = values; - } else if (ConfigOptionString* optv = dynamic_cast(opt)) { - optv->value = std::string(SvPV_nolen(value), SvCUR(value)); - } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { - optv->values.clear(); - AV* av = (AV*)SvRV(value); - const size_t len = av_len(av)+1; - for (size_t i = 0; i < len; i++) { - SV** elem = av_fetch(av, i, 0); - if (elem == NULL) return false; - optv->values.push_back(std::string(SvPV_nolen(*elem), SvCUR(*elem))); - } - } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { - return optv->point.from_SV(value); - } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { - std::vector values; - AV* av = (AV*)SvRV(value); - const size_t len = av_len(av)+1; - for (size_t i = 0; i < len; i++) { - SV** elem = av_fetch(av, i, 0); - Pointf point; - if (elem == NULL || !point.from_SV(*elem)) return false; - values.push_back(point); - } - optv->values = values; - } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { - optv->value = SvTRUE(value); - } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { - optv->values.clear(); - AV* av = (AV*)SvRV(value); - const size_t len = av_len(av)+1; - for (size_t i = 0; i < len; i++) { - SV** elem = av_fetch(av, i, 0); - if (elem == NULL) return false; - optv->values.push_back(SvTRUE(*elem)); - } - } else { - if (!opt->deserialize( std::string(SvPV_nolen(value)) )) return false; - } - return true; -} - -/* This method is implemented as a workaround for this typemap bug: - https://rt.cpan.org/Public/Bug/Display.html?id=94110 */ -bool -ConfigBase::set_deserialize(const t_config_option_key opt_key, SV* str) { - size_t len; - const char * c = SvPV(str, len); - std::string value(c, len); - - return this->set_deserialize(opt_key, value); -} -#endif - -DynamicConfig& DynamicConfig::operator= (DynamicConfig other) -{ - this->swap(other); - return *this; -} - -void -DynamicConfig::swap(DynamicConfig &other) -{ - std::swap(this->options, other.options); -} - -DynamicConfig::~DynamicConfig () { - for (t_options_map::iterator it = this->options.begin(); it != this->options.end(); ++it) { - ConfigOption* opt = it->second; - if (opt != NULL) delete opt; - } -} - -DynamicConfig::DynamicConfig (const DynamicConfig& other) { - this->def = other.def; - this->apply(other, false); -} - -ConfigOption* -DynamicConfig::option(const t_config_option_key opt_key, bool create) { - if (this->options.count(opt_key) == 0) { - if (create) { - ConfigOptionDef* optdef = &(*this->def)[opt_key]; - ConfigOption* opt; - if (optdef->type == coFloat) { - opt = new ConfigOptionFloat (); - } else if (optdef->type == coFloats) { - opt = new ConfigOptionFloats (); - } else if (optdef->type == coInt) { - opt = new ConfigOptionInt (); - } else if (optdef->type == coInts) { - opt = new ConfigOptionInts (); - } else if (optdef->type == coString) { - opt = new ConfigOptionString (); - } else if (optdef->type == coStrings) { - opt = new ConfigOptionStrings (); - } else if (optdef->type == coPercent) { - opt = new ConfigOptionPercent (); - } else if (optdef->type == coFloatOrPercent) { - opt = new ConfigOptionFloatOrPercent (); - } else if (optdef->type == coPoint) { - opt = new ConfigOptionPoint (); - } else if (optdef->type == coPoints) { - opt = new ConfigOptionPoints (); - } else if (optdef->type == coBool) { - opt = new ConfigOptionBool (); - } else if (optdef->type == coBools) { - opt = new ConfigOptionBools (); - } else if (optdef->type == coEnum) { - ConfigOptionEnumGeneric* optv = new ConfigOptionEnumGeneric (); - optv->keys_map = &optdef->enum_keys_map; - opt = static_cast(optv); - } else { - throw "Unknown option type"; - } - this->options[opt_key] = opt; - return opt; - } else { - return NULL; - } - } - return this->options[opt_key]; -} - -template -T* -DynamicConfig::opt(const t_config_option_key opt_key, bool create) { - return dynamic_cast(this->option(opt_key, create)); -} -template ConfigOptionInt* DynamicConfig::opt(const t_config_option_key opt_key, bool create); -template ConfigOptionBool* DynamicConfig::opt(const t_config_option_key opt_key, bool create); -template ConfigOptionBools* DynamicConfig::opt(const t_config_option_key opt_key, bool create); - -const ConfigOption* -DynamicConfig::option(const t_config_option_key opt_key) const { - return const_cast(this)->option(opt_key, false); -} - -void -DynamicConfig::keys(t_config_option_keys *keys) const { - for (t_options_map::const_iterator it = this->options.begin(); it != this->options.end(); ++it) - keys->push_back(it->first); -} - -void -DynamicConfig::erase(const t_config_option_key opt_key) { - this->options.erase(opt_key); -} - -void -StaticConfig::keys(t_config_option_keys *keys) const { - for (t_optiondef_map::const_iterator it = this->def->begin(); it != this->def->end(); ++it) { - const ConfigOption* opt = this->option(it->first); - if (opt != NULL) keys->push_back(it->first); - } -} - -const ConfigOption* -StaticConfig::option(const t_config_option_key opt_key) const -{ - return const_cast(this)->option(opt_key, false); -} - -#ifdef SLIC3RXS -bool -StaticConfig::set(t_config_option_key opt_key, SV* value) { - ConfigOptionDef* optdef = &(*this->def)[opt_key]; - if (!optdef->shortcut.empty()) { - for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) { - if (!this->set(*it, value)) return false; - } - return true; - } - - return static_cast(this)->set(opt_key, value); -} -#endif - -} diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp deleted file mode 100644 index 42d19c830..000000000 --- a/xs/src/Config.hpp +++ /dev/null @@ -1,521 +0,0 @@ -#ifndef slic3r_Config_hpp_ -#define slic3r_Config_hpp_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "Point.hpp" - -namespace Slic3r { - -typedef std::string t_config_option_key; -typedef std::vector t_config_option_keys; - -class ConfigOption { - public: - virtual ~ConfigOption() {}; - virtual std::string serialize() const = 0; - virtual bool deserialize(std::string str) = 0; - virtual int getInt() const { return 0; }; - virtual void setInt(int val) {}; -}; - -template -class ConfigOptionVector -{ - public: - virtual ~ConfigOptionVector() {}; - std::vector values; - - T get_at(size_t i) const { - try { - return this->values.at(i); - } catch (const std::out_of_range& oor) { - return this->values.front(); - } - }; -}; - -class ConfigOptionFloat : public ConfigOption -{ - public: - double value; // use double instead of float for preserving compatibility with values coming from Perl - ConfigOptionFloat() : value(0) {}; - - operator double() const { return this->value; }; - - std::string serialize() const { - std::ostringstream ss; - ss << this->value; - return ss.str(); - }; - - bool deserialize(std::string str) { - this->value = ::atof(str.c_str()); - return true; - }; -}; - -class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector -{ - public: - - std::string serialize() const { - std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << *it; - } - return ss.str(); - }; - - bool deserialize(std::string str) { - this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ',')) { - this->values.push_back(::atof(item_str.c_str())); - } - return true; - }; -}; - -class ConfigOptionInt : public ConfigOption -{ - public: - int value; - ConfigOptionInt() : value(0) {}; - - operator int() const { return this->value; }; - int getInt() const { return this->value; }; - void setInt(int val) { this->value = val; }; - - std::string serialize() const { - std::ostringstream ss; - ss << this->value; - return ss.str(); - }; - - bool deserialize(std::string str) { - this->value = ::atoi(str.c_str()); - return true; - }; -}; - -class ConfigOptionInts : public ConfigOption, public ConfigOptionVector -{ - public: - - std::string serialize() const { - std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << *it; - } - return ss.str(); - }; - - bool deserialize(std::string str) { - this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ',')) { - this->values.push_back(::atoi(item_str.c_str())); - } - return true; - }; -}; - -class ConfigOptionString : public ConfigOption -{ - public: - std::string value; - ConfigOptionString() : value("") {}; - - operator std::string() const { return this->value; }; - - std::string serialize() const { - std::string str = this->value; - - // s/\R/\\n/g - size_t pos = 0; - while ((pos = str.find("\n", pos)) != std::string::npos || (pos = str.find("\r", pos)) != std::string::npos) { - str.replace(pos, 1, "\\n"); - pos += 2; // length of "\\n" - } - - return str; - }; - - bool deserialize(std::string str) { - // s/\\n/\n/g - size_t pos = 0; - while ((pos = str.find("\\n", pos)) != std::string::npos) { - str.replace(pos, 2, "\n"); - pos += 1; // length of "\n" - } - - this->value = str; - return true; - }; -}; - -// semicolon-separated strings -class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector -{ - public: - - std::string serialize() const { - std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ";"; - ss << *it; - } - return ss.str(); - }; - - bool deserialize(std::string str) { - this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ';')) { - this->values.push_back(item_str); - } - return true; - }; -}; - -class ConfigOptionPercent : public ConfigOption -{ - public: - double value; - ConfigOptionPercent() : value(0) {}; - - double get_abs_value(double ratio_over) const { - return ratio_over * this->value / 100; - }; - - std::string serialize() const { - std::ostringstream ss; - ss << this->value; - std::string s(ss.str()); - s += "%"; - return s; - }; - - bool deserialize(std::string str) { - // don't try to parse the trailing % since it's optional - int res = sscanf(str.c_str(), "%lf", &this->value); - return res == 1; - }; -}; - -class ConfigOptionFloatOrPercent : public ConfigOption -{ - public: - double value; - bool percent; - ConfigOptionFloatOrPercent() : value(0), percent(false) {}; - - double get_abs_value(double ratio_over) const { - if (this->percent) { - return ratio_over * this->value / 100; - } else { - return this->value; - } - }; - - std::string serialize() const { - std::ostringstream ss; - ss << this->value; - std::string s(ss.str()); - if (this->percent) s += "%"; - return s; - }; - - bool deserialize(std::string str) { - if (str.find_first_of("%") != std::string::npos) { - int res = sscanf(str.c_str(), "%lf%%", &this->value); - if (res == 0) return false; - this->percent = true; - } else { - this->value = ::atof(str.c_str()); - this->percent = false; - } - return true; - }; -}; - -class ConfigOptionPoint : public ConfigOption -{ - public: - Pointf point; - ConfigOptionPoint() : point(Pointf(0,0)) {}; - - operator Pointf() const { return this->point; }; - - std::string serialize() const { - std::ostringstream ss; - ss << this->point.x; - ss << ","; - ss << this->point.y; - return ss.str(); - }; - - bool deserialize(std::string str) { - if (strncmp(str.c_str(), "0x", 2) == 0) { - this->point.x = 0; - int res = sscanf(str.c_str()+2, "%lf", &this->point.y); - return res == 1; - } else { - int res = sscanf(str.c_str(), "%lf%*1[,x]%lf", &this->point.x, &this->point.y); - return res == 2; - } - }; -}; - -class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector -{ - public: - - std::string serialize() const { - 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 << "x"; - ss << it->y; - } - return ss.str(); - }; - - bool deserialize(std::string str) { - std::vector values; - std::istringstream is(str); - std::string point_str; - while (std::getline(is, point_str, ',')) { - Pointf point; - if (strncmp(point_str.c_str(), "0x", 2) == 0) { - // if string starts with "0x", only apply sscanf() to the second coordinate - // otherwise it would parse the string as a hex number - point.x = 0; - int res = sscanf(point_str.c_str()+2, "%lf", &point.y); - if (res != 1) return false; - } else { - int res = sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y); - if (res != 2) return false; - } - values.push_back(point); - } - this->values = values; - return true; - }; -}; - -class ConfigOptionBool : public ConfigOption -{ - public: - bool value; - ConfigOptionBool() : value(false) {}; - - operator bool() const { return this->value; }; - - std::string serialize() const { - return std::string(this->value ? "1" : "0"); - }; - - bool deserialize(std::string str) { - this->value = (str.compare("1") == 0); - return true; - }; -}; - -class ConfigOptionBools : public ConfigOption, public ConfigOptionVector -{ - public: - - std::string serialize() const { - std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ","; - ss << (*it ? "1" : "0"); - } - return ss.str(); - }; - - bool deserialize(std::string str) { - this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ',')) { - this->values.push_back(item_str.compare("1") == 0); - } - return true; - }; -}; - -typedef std::map t_config_enum_values; - -template -class ConfigOptionEnum : public ConfigOption -{ - public: - T value; - - operator T() const { return this->value; }; - - std::string serialize() const { - t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); - for (t_config_enum_values::iterator it = enum_keys_map.begin(); it != enum_keys_map.end(); ++it) { - if (it->second == static_cast(this->value)) return it->first; - } - return ""; - }; - - bool deserialize(std::string str) { - t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); - if (enum_keys_map.count(str) == 0) return false; - this->value = static_cast(enum_keys_map[str]); - return true; - }; - - static t_config_enum_values get_enum_values(); -}; - -/* We use this one in DynamicConfig objects, otherwise it's better to use - the specialized ConfigOptionEnum containers. */ -class ConfigOptionEnumGeneric : public ConfigOption -{ - public: - int value; - t_config_enum_values* keys_map; - - operator int() const { return this->value; }; - - std::string serialize() const { - for (t_config_enum_values::iterator it = this->keys_map->begin(); it != this->keys_map->end(); ++it) { - if (it->second == this->value) return it->first; - } - return ""; - }; - - bool deserialize(std::string str) { - if (this->keys_map->count(str) == 0) return false; - this->value = (*this->keys_map)[str]; - return true; - }; -}; - -enum ConfigOptionType { - coFloat, - coFloats, - coInt, - coInts, - coString, - coStrings, - coPercent, - coFloatOrPercent, - coPoint, - coPoints, - coBool, - coBools, - coEnum, -}; - -class ConfigOptionDef -{ - public: - ConfigOptionType type; - std::string gui_type; - std::string gui_flags; - std::string label; - std::string full_label; - std::string category; - std::string tooltip; - std::string sidetext; - std::string cli; - t_config_option_key ratio_over; - bool multiline; - bool full_width; - bool readonly; - int height; - int width; - int min; - int max; - std::vector aliases; - std::vector shortcut; - std::vector enum_values; - std::vector enum_labels; - t_config_enum_values enum_keys_map; - - ConfigOptionDef() : multiline(false), full_width(false), readonly(false), - height(-1), width(-1), min(INT_MIN), max(INT_MAX) {}; -}; - -typedef std::map t_optiondef_map; - -class ConfigBase -{ - public: - t_optiondef_map* def; - - ConfigBase() : def(NULL) {}; - bool has(const t_config_option_key opt_key); - virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0; - virtual const ConfigOption* option(const t_config_option_key opt_key) const = 0; - virtual void keys(t_config_option_keys *keys) const = 0; - void apply(const ConfigBase &other, bool ignore_nonexistent = false); - std::string serialize(const t_config_option_key opt_key); - bool set_deserialize(const t_config_option_key opt_key, std::string str); - double get_abs_value(const t_config_option_key opt_key); - double get_abs_value(const t_config_option_key opt_key, double ratio_over); - - #ifdef SLIC3RXS - SV* as_hash(); - SV* get(t_config_option_key opt_key); - SV* get_at(t_config_option_key opt_key, size_t i); - bool set(t_config_option_key opt_key, SV* value); - bool set_deserialize(const t_config_option_key opt_key, SV* str); - #endif -}; - -class DynamicConfig : public ConfigBase -{ - public: - DynamicConfig() {}; - DynamicConfig(const DynamicConfig& other); - DynamicConfig& operator= (DynamicConfig other); - void swap(DynamicConfig &other); - ~DynamicConfig(); - template T* opt(const t_config_option_key opt_key, bool create = false); - ConfigOption* option(const t_config_option_key opt_key, bool create = false); - const ConfigOption* option(const t_config_option_key opt_key) const; - void keys(t_config_option_keys *keys) const; - void erase(const t_config_option_key opt_key); - - private: - typedef std::map t_options_map; - t_options_map options; -}; - -class StaticConfig : public ConfigBase -{ - public: - void keys(t_config_option_keys *keys) const; - virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0; - const ConfigOption* option(const t_config_option_key opt_key) const; - - #ifdef SLIC3RXS - bool set(t_config_option_key opt_key, SV* value); - #endif -}; - -} - -#endif diff --git a/xs/src/ExPolygon.cpp b/xs/src/ExPolygon.cpp deleted file mode 100644 index a51e55a2d..000000000 --- a/xs/src/ExPolygon.cpp +++ /dev/null @@ -1,428 +0,0 @@ -#include "BoundingBox.hpp" -#include "ExPolygon.hpp" -#include "Geometry.hpp" -#include "Polygon.hpp" -#include "Line.hpp" -#include "ClipperUtils.hpp" -#include "polypartition.h" -#include "poly2tri/poly2tri.h" - -#include -#include - -namespace Slic3r { - -ExPolygon::operator Points() const -{ - Points points; - Polygons pp = *this; - for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { - for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) - points.push_back(*point); - } - return points; -} - -ExPolygon::operator Polygons() const -{ - Polygons polygons; - polygons.reserve(this->holes.size() + 1); - polygons.push_back(this->contour); - for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { - polygons.push_back(*it); - } - return polygons; -} - -void -ExPolygon::scale(double factor) -{ - contour.scale(factor); - for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { - (*it).scale(factor); - } -} - -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); - } -} - -void -ExPolygon::rotate(double angle, const Point ¢er) -{ - contour.rotate(angle, center); - for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { - (*it).rotate(angle, center); - } -} - -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 - } - return a; -} - -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) { - if (!(*it).is_valid() || (*it).is_counter_clockwise()) return false; - } - return true; -} - -bool -ExPolygon::contains_line(const Line &line) const -{ - return this->contains_polyline(line); -} - -bool -ExPolygon::contains_polyline(const Polyline &polyline) const -{ - Polylines pl_out; - diff((Polylines)polyline, *this, pl_out); - return pl_out.empty(); -} - -bool -ExPolygon::contains_point(const Point &point) const -{ - if (!this->contour.contains_point(point)) return false; - for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { - if (it->contains_point(point)) return false; - } - return true; -} - -Polygons -ExPolygon::simplify_p(double tolerance) const -{ - Polygons pp; - pp.reserve(this->holes.size() + 1); - - // contour - Polygon p = this->contour; - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - pp.push_back(p); - - // holes - for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { - p = *it; - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - pp.push_back(p); - } - simplify_polygons(pp, pp); - return pp; -} - -ExPolygons -ExPolygon::simplify(double tolerance) const -{ - Polygons pp = this->simplify_p(tolerance); - ExPolygons expp; - union_(pp, expp); - return expp; -} - -void -ExPolygon::simplify(double tolerance, ExPolygons &expolygons) const -{ - ExPolygons ep = this->simplify(tolerance); - expolygons.reserve(expolygons.size() + ep.size()); - expolygons.insert(expolygons.end(), ep.begin(), ep.end()); -} - -void -ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const -{ - // init helper object - Slic3r::Geometry::MedialAxis ma(max_width, min_width); - - // populate list of segments for the Voronoi diagram - this->contour.lines(&ma.lines); - for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) - hole->lines(&ma.lines); - - // compute the Voronoi diagram - ma.build(polylines); - - // clip segments to our expolygon area - // (do this before extending endpoints as external segments coule be extended into - // expolygon, this leaving wrong things inside) - intersection(*polylines, *this, *polylines); - - // extend initial and final segments of each polyline (they will be clipped) - // unless they represent closed loops - for (Polylines::iterator polyline = polylines->begin(); polyline != polylines->end(); ++polyline) { - if (polyline->points.front().distance_to(polyline->points.back()) < min_width) continue; - polyline->extend_start(max_width); - polyline->extend_end(max_width); - } - - // clip again after extending endpoints to prevent them from exceeding the expolygon boundaries - intersection(*polylines, *this, *polylines); -} - -void -ExPolygon::get_trapezoids(Polygons* polygons) const -{ - ExPolygons expp; - expp.push_back(*this); - boost::polygon::get_trapezoids(*polygons, expp); -} - -void -ExPolygon::get_trapezoids(Polygons* polygons, double angle) const -{ - ExPolygon clone = *this; - clone.rotate(PI/2 - angle, Point(0,0)); - clone.get_trapezoids(polygons); - for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon) - polygon->rotate(-(PI/2 - angle), Point(0,0)); -} - -// This algorithm may return more trapezoids than necessary -// (i.e. it may break a single trapezoid in several because -// other parts of the object have x coordinates in the middle) -void -ExPolygon::get_trapezoids2(Polygons* polygons) const -{ - // get all points of this ExPolygon - Points pp = *this; - - // build our bounding box - BoundingBox bb(pp); - - // get all x coordinates - std::vector xx; - xx.reserve(pp.size()); - for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) - xx.push_back(p->x); - std::sort(xx.begin(), xx.end()); - - // find trapezoids by looping from first to next-to-last coordinate - for (std::vector::const_iterator x = xx.begin(); x != xx.end()-1; ++x) { - coord_t next_x = *(x + 1); - if (*x == next_x) continue; - - // 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; - - // intersect with this expolygon - Polygons trapezoids; - intersection(poly, *this, trapezoids); - - // append results to return value - polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end()); - } -} - -void -ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const -{ - ExPolygon clone = *this; - clone.rotate(PI/2 - angle, Point(0,0)); - clone.get_trapezoids2(polygons); - for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon) - polygon->rotate(-(PI/2 - angle), Point(0,0)); -} - -// While this triangulates successfully, it's NOT a constrained triangulation -// as it will create more vertices on the boundaries than the ones supplied. -void -ExPolygon::triangulate(Polygons* polygons) const -{ - // first make trapezoids - Polygons trapezoids; - this->get_trapezoids2(&trapezoids); - - // then triangulate each trapezoid - for (Polygons::iterator polygon = trapezoids.begin(); polygon != trapezoids.end(); ++polygon) - polygon->triangulate_convex(polygons); -} - -void -ExPolygon::triangulate_pp(Polygons* polygons) const -{ - // convert polygons - std::list input; - - Polygons pp = *this; - simplify_polygons(pp, pp, true); - ExPolygons expp; - union_(pp, expp); - - for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { - // contour - { - TPPLPoly p; - p.Init(ex->contour.points.size()); - //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); - } - p.SetHole(false); - input.push_back(p); - } - - // holes - for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) { - TPPLPoly p; - p.Init(hole->points.size()); - //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); - } - p.SetHole(true); - input.push_back(p); - } - } - - // perform triangulation - std::list output; - int res = TPPLPartition().Triangulate_MONO(&input, &output); - if (res != 1) CONFESS("Triangulation failed"); - - // convert output polygons - for (std::list::iterator poly = output.begin(); poly != output.end(); ++poly) { - long num_points = poly->GetNumPoints(); - Polygon p; - p.points.resize(num_points); - for (long i = 0; i < num_points; ++i) { - p.points[i].x = (*poly)[i].x; - p.points[i].y = (*poly)[i].y; - } - polygons->push_back(p); - } -} - -void -ExPolygon::triangulate_p2t(Polygons* polygons) const -{ - ExPolygons expp; - simplify_polygons(*this, expp, true); - - for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { - p2t::CDT* cdt; - - // TODO: prevent duplicate points - - // contour - { - std::vector points; - for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { - points.push_back(new p2t::Point(point->x, point->y)); - } - cdt = new p2t::CDT(points); - } - - // holes - for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) { - std::vector points; - for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) { - points.push_back(new p2t::Point(point->x, point->y)); - } - cdt->AddHole(points); - } - - // perform triangulation - cdt->Triangulate(); - std::vector triangles = cdt->GetTriangles(); - - for (std::vector::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) { - Polygon p; - for (int i = 0; i <= 2; ++i) { - p2t::Point* point = (*triangle)->GetPoint(i); - p.points.push_back(Point(point->x, point->y)); - } - polygons->push_back(p); - } - } -} - -#ifdef SLIC3RXS - -REGISTER_CLASS(ExPolygon, "ExPolygon"); - -SV* -ExPolygon::to_AV() { - const unsigned int num_holes = this->holes.size(); - AV* av = newAV(); - av_extend(av, num_holes); // -1 +1 - - av_store(av, 0, perl_to_SV_ref(this->contour)); - - for (unsigned int i = 0; i < num_holes; i++) { - av_store(av, i+1, perl_to_SV_ref(this->holes[i])); - } - return newRV_noinc((SV*)av); -} - -SV* -ExPolygon::to_SV_pureperl() const -{ - const unsigned int num_holes = this->holes.size(); - AV* av = newAV(); - av_extend(av, num_holes); // -1 +1 - av_store(av, 0, this->contour.to_SV_pureperl()); - for (unsigned int i = 0; i < num_holes; i++) { - av_store(av, i+1, this->holes[i].to_SV_pureperl()); - } - return newRV_noinc((SV*)av); -} - -void -ExPolygon::from_SV(SV* expoly_sv) -{ - AV* expoly_av = (AV*)SvRV(expoly_sv); - const unsigned int num_polygons = av_len(expoly_av)+1; - this->holes.resize(num_polygons-1); - - SV** polygon_sv = av_fetch(expoly_av, 0, 0); - this->contour.from_SV(*polygon_sv); - for (unsigned int i = 0; i < num_polygons-1; i++) { - polygon_sv = av_fetch(expoly_av, i+1, 0); - this->holes[i].from_SV(*polygon_sv); - } -} - -void -ExPolygon::from_SV_check(SV* expoly_sv) -{ - if (sv_isobject(expoly_sv) && (SvTYPE(SvRV(expoly_sv)) == SVt_PVMG)) { - if (!sv_isa(expoly_sv, perl_class_name(this)) && !sv_isa(expoly_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object", perl_class_name(this)); - // a XS ExPolygon was supplied - *this = *(ExPolygon *)SvIV((SV*)SvRV( expoly_sv )); - } else { - // a Perl arrayref was supplied - this->from_SV(expoly_sv); - } -} -#endif - -} diff --git a/xs/src/ExPolygon.hpp b/xs/src/ExPolygon.hpp deleted file mode 100644 index be6065914..000000000 --- a/xs/src/ExPolygon.hpp +++ /dev/null @@ -1,154 +0,0 @@ -#ifndef slic3r_ExPolygon_hpp_ -#define slic3r_ExPolygon_hpp_ - -#include "Polygon.hpp" -#include "Polyline.hpp" -#include - -namespace Slic3r { - -class ExPolygon; -typedef std::vector ExPolygons; - -class ExPolygon -{ - public: - Polygon contour; - Polygons holes; - operator Points() const; - operator Polygons() const; - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Point ¢er); - double area() const; - bool is_valid() const; - bool contains_line(const Line &line) const; - bool contains_polyline(const Polyline &polyline) const; - bool contains_point(const Point &point) const; - Polygons simplify_p(double tolerance) const; - ExPolygons simplify(double tolerance) const; - void simplify(double tolerance, ExPolygons &expolygons) const; - void medial_axis(double max_width, double min_width, Polylines* polylines) const; - void get_trapezoids(Polygons* polygons) const; - void get_trapezoids(Polygons* polygons, double angle) const; - void get_trapezoids2(Polygons* polygons) const; - void get_trapezoids2(Polygons* polygons, double angle) const; - void triangulate(Polygons* polygons) const; - void triangulate_pp(Polygons* polygons) const; - void triangulate_p2t(Polygons* polygons) const; - - #ifdef SLIC3RXS - void from_SV(SV* poly_sv); - void from_SV_check(SV* poly_sv); - SV* to_AV(); - SV* to_SV_pureperl() const; - #endif -}; - -} - -// start Boost -#include -namespace boost { namespace polygon { - template <> - struct polygon_traits { - typedef coord_t coordinate_type; - typedef Points::const_iterator iterator_type; - typedef Point point_type; - - // Get the begin iterator - static inline iterator_type begin_points(const ExPolygon& t) { - return t.contour.points.begin(); - } - - // Get the end iterator - static inline iterator_type end_points(const ExPolygon& t) { - return t.contour.points.end(); - } - - // Get the number of sides of the polygon - static inline std::size_t size(const ExPolygon& t) { - return t.contour.points.size(); - } - - // Get the winding direction of the polygon - static inline winding_direction winding(const ExPolygon& t) { - return unknown_winding; - } - }; - - template <> - struct polygon_mutable_traits { - //expects stl style iterators - template - static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) { - expolygon.contour.points.assign(input_begin, input_end); - // skip last point since Boost will set last point = first point - expolygon.contour.points.pop_back(); - return expolygon; - } - }; - - - template <> - struct geometry_concept { typedef polygon_with_holes_concept type; }; - - template <> - struct polygon_with_holes_traits { - typedef Polygons::const_iterator iterator_holes_type; - typedef Polygon hole_type; - static inline iterator_holes_type begin_holes(const ExPolygon& t) { - return t.holes.begin(); - } - static inline iterator_holes_type end_holes(const ExPolygon& t) { - return t.holes.end(); - } - static inline unsigned int size_holes(const ExPolygon& t) { - return t.holes.size(); - } - }; - - template <> - struct polygon_with_holes_mutable_traits { - template - static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) { - t.holes.assign(inputBegin, inputEnd); - return t; - } - }; - - //first we register CPolygonSet as a polygon set - template <> - struct geometry_concept { typedef polygon_set_concept type; }; - - //next we map to the concept through traits - template <> - struct polygon_set_traits { - typedef coord_t coordinate_type; - typedef ExPolygons::const_iterator iterator_type; - typedef ExPolygons operator_arg_type; - - static inline iterator_type begin(const ExPolygons& polygon_set) { - return polygon_set.begin(); - } - - static inline iterator_type end(const ExPolygons& polygon_set) { - return polygon_set.end(); - } - - //don't worry about these, just return false from them - static inline bool clean(const ExPolygons& polygon_set) { return false; } - static inline bool sorted(const ExPolygons& polygon_set) { return false; } - }; - - template <> - struct polygon_set_mutable_traits { - template - static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) { - expolygons.assign(input_begin, input_end); - } - }; -} } -// end Boost - -#endif diff --git a/xs/src/ExPolygonCollection.cpp b/xs/src/ExPolygonCollection.cpp deleted file mode 100644 index 3de86e7c6..000000000 --- a/xs/src/ExPolygonCollection.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "ExPolygonCollection.hpp" -#include "Geometry.hpp" - -namespace Slic3r { - -ExPolygonCollection::operator Points() const -{ - Points points; - Polygons pp = *this; - for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { - for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) - points.push_back(*point); - } - return points; -} - -ExPolygonCollection::operator Polygons() const -{ - Polygons polygons; - for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { - polygons.push_back(it->contour); - for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) { - polygons.push_back(*ith); - } - } - return polygons; -} - -ExPolygonCollection::operator ExPolygons&() -{ - return this->expolygons; -} - -void -ExPolygonCollection::scale(double factor) -{ - for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) { - (*it).scale(factor); - } -} - -void -ExPolygonCollection::translate(double x, double y) -{ - for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) { - (*it).translate(x, y); - } -} - -void -ExPolygonCollection::rotate(double angle, const Point ¢er) -{ - for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) { - (*it).rotate(angle, center); - } -} - -bool -ExPolygonCollection::contains_point(const Point &point) const -{ - for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { - if (it->contains_point(point)) return true; - } - return false; -} - -void -ExPolygonCollection::simplify(double tolerance) -{ - ExPolygons expp; - for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { - it->simplify(tolerance, expp); - } - this->expolygons = expp; -} - -void -ExPolygonCollection::convex_hull(Polygon* hull) const -{ - Points pp; - for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) - pp.insert(pp.end(), it->contour.points.begin(), it->contour.points.end()); - Slic3r::Geometry::convex_hull(pp, hull); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection"); -#endif - -} diff --git a/xs/src/ExPolygonCollection.hpp b/xs/src/ExPolygonCollection.hpp deleted file mode 100644 index f6a27284f..000000000 --- a/xs/src/ExPolygonCollection.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef slic3r_ExPolygonCollection_hpp_ -#define slic3r_ExPolygonCollection_hpp_ - -#include -#include "ExPolygon.hpp" - -namespace Slic3r { - -class ExPolygonCollection; -typedef std::vector ExPolygonCollections; - -class ExPolygonCollection -{ - public: - ExPolygons expolygons; - - ExPolygonCollection() {}; - ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}; - operator Points() const; - operator Polygons() const; - operator ExPolygons&(); - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Point ¢er); - bool contains_point(const Point &point) const; - void simplify(double tolerance); - void convex_hull(Polygon* hull) const; -}; - -} - -#endif diff --git a/xs/src/Extruder.cpp b/xs/src/Extruder.cpp deleted file mode 100644 index d327276f8..000000000 --- a/xs/src/Extruder.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "Extruder.hpp" - -namespace Slic3r { - -Extruder::Extruder(int id, PrintConfig *config) -: id(id), - config(config) -{ - reset(); -} - -void -Extruder::reset() -{ - this->E = 0; - this->absolute_E = 0; - this->retracted = 0; - this->restart_extra = 0; -} - -double -Extruder::extrude(double dE) -{ - if (this->config->use_relative_e_distances) { - this->E = 0; - } - - this->E += dE; - this->absolute_E += dE; - return this->E; -} - -Pointf -Extruder::extruder_offset() const -{ - return this->config->extruder_offset.get_at(this->id); -} - -double -Extruder::nozzle_diameter() const -{ - return this->config->nozzle_diameter.get_at(this->id); -} - -double -Extruder::filament_diameter() const -{ - return this->config->filament_diameter.get_at(this->id); -} - -double -Extruder::extrusion_multiplier() const -{ - return this->config->extrusion_multiplier.get_at(this->id); -} - -int -Extruder::temperature() const -{ - return this->config->temperature.get_at(this->id); -} - -int -Extruder::first_layer_temperature() const -{ - return this->config->first_layer_temperature.get_at(this->id); -} - -double -Extruder::retract_length() const -{ - return this->config->retract_length.get_at(this->id); -} - -double -Extruder::retract_lift() const -{ - return this->config->retract_lift.get_at(this->id); -} - -int -Extruder::retract_speed() const -{ - return this->config->retract_speed.get_at(this->id); -} - -double -Extruder::retract_restart_extra() const -{ - return this->config->retract_restart_extra.get_at(this->id); -} - -double -Extruder::retract_before_travel() const -{ - return this->config->retract_before_travel.get_at(this->id); -} - -bool -Extruder::retract_layer_change() const -{ - return this->config->retract_layer_change.get_at(this->id); -} - -double -Extruder::retract_length_toolchange() const -{ - return this->config->retract_length_toolchange.get_at(this->id); -} - -double -Extruder::retract_restart_extra_toolchange() const -{ - return this->config->retract_restart_extra_toolchange.get_at(this->id); -} - -bool -Extruder::wipe() const -{ - return this->config->wipe.get_at(this->id); -} - - -#ifdef SLIC3RXS -REGISTER_CLASS(Extruder, "Extruder"); -#endif - -} diff --git a/xs/src/Extruder.hpp b/xs/src/Extruder.hpp deleted file mode 100644 index 9caa38c89..000000000 --- a/xs/src/Extruder.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef slic3r_Extruder_hpp_ -#define slic3r_Extruder_hpp_ - -#include -#include "Point.hpp" -#include "PrintConfig.hpp" - -namespace Slic3r { - -class Extruder -{ - public: - Extruder(int id, PrintConfig *config); - virtual ~Extruder() {} - void reset(); - double extrude(double dE); - - - Pointf extruder_offset() const; - double nozzle_diameter() const; - double filament_diameter() const; - double extrusion_multiplier() const; - int temperature() const; - int first_layer_temperature() const; - double retract_length() const; - double retract_lift() const; - int retract_speed() const; - double retract_restart_extra() const; - double retract_before_travel() const; - bool retract_layer_change() const; - double retract_length_toolchange() const; - double retract_restart_extra_toolchange() const; - bool wipe() const; - - int id; - double E; - double absolute_E; - double retracted; - double restart_extra; - - PrintConfig *config; -}; - -} - -#endif diff --git a/xs/src/ExtrusionEntity.cpp b/xs/src/ExtrusionEntity.cpp deleted file mode 100644 index fd39fdc9f..000000000 --- a/xs/src/ExtrusionEntity.cpp +++ /dev/null @@ -1,335 +0,0 @@ -#include "ExtrusionEntity.hpp" -#include "ExtrusionEntityCollection.hpp" -#include "ExPolygonCollection.hpp" -#include "ClipperUtils.hpp" -#include "Extruder.hpp" -#include - -namespace Slic3r { - -ExtrusionPath* -ExtrusionPath::clone() const -{ - return new ExtrusionPath (*this); -} - -void -ExtrusionPath::reverse() -{ - this->polyline.reverse(); -} - -Point -ExtrusionPath::first_point() const -{ - return this->polyline.points.front(); -} - -Point -ExtrusionPath::last_point() const -{ - return this->polyline.points.back(); -} - -void -ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const -{ - // perform clipping - Polylines clipped; - intersection(this->polyline, collection, clipped); - return this->_inflate_collection(clipped, retval); -} - -void -ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const -{ - // perform clipping - Polylines clipped; - diff(this->polyline, collection, clipped); - return this->_inflate_collection(clipped, retval); -} - -void -ExtrusionPath::clip_end(double distance) -{ - this->polyline.clip_end(distance); -} - -void -ExtrusionPath::simplify(double tolerance) -{ - this->polyline.simplify(tolerance); -} - -double -ExtrusionPath::length() const -{ - return this->polyline.length(); -} - -bool -ExtrusionPath::is_perimeter() const -{ - return this->role == erPerimeter - || this->role == erExternalPerimeter - || this->role == erOverhangPerimeter; -} - -bool -ExtrusionPath::is_fill() const -{ - return this->role == erInternalInfill - || this->role == erSolidInfill - || this->role == erTopSolidInfill; -} - -bool -ExtrusionPath::is_bridge() const -{ - return this->role == erBridgeInfill - || this->role == erOverhangPerimeter; -} - -void -ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const -{ - for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) { - ExtrusionPath* path = this->clone(); - path->polyline = *it; - collection->entities.push_back(path); - } -} - -#ifdef SLIC3RXS -REGISTER_CLASS(ExtrusionPath, "ExtrusionPath"); -#endif - -std::string -ExtrusionPath::gcode(Extruder* extruder, double e, double F, - double xofs, double yofs, std::string extrusion_axis, - std::string gcode_line_suffix) const -{ - dSP; - - std::stringstream stream; - stream.setf(std::ios::fixed); - - double local_F = F; - - Lines lines = this->polyline.lines(); - for (Lines::const_iterator line_it = lines.begin(); - line_it != lines.end(); ++line_it) - { - const double line_length = line_it->length() * SCALING_FACTOR; - - // calculate extrusion length for this line - double E = (e == 0) ? 0 : extruder->extrude(e * line_length); - - // compose G-code line - - Point point = line_it->b; - const double x = point.x * SCALING_FACTOR + xofs; - const double y = point.y * SCALING_FACTOR + yofs; - stream.precision(3); - stream << "G1 X" << x << " Y" << y; - - if (E != 0) { - stream.precision(5); - stream << " " << extrusion_axis << E; - } - - if (local_F != 0) { - stream.precision(3); - stream << " F" << local_F; - local_F = 0; - } - - stream << gcode_line_suffix; - stream << "\n"; - } - - return stream.str(); -} - -ExtrusionLoop::operator Polygon() const -{ - Polygon polygon; - this->polygon(&polygon); - return polygon; -} - -ExtrusionLoop* -ExtrusionLoop::clone() const -{ - return new ExtrusionLoop (*this); -} - -bool -ExtrusionLoop::make_clockwise() -{ - Polygon polygon = *this; - bool was_ccw = polygon.is_counter_clockwise(); - if (was_ccw) this->reverse(); - return was_ccw; -} - -bool -ExtrusionLoop::make_counter_clockwise() -{ - Polygon polygon = *this; - bool was_cw = polygon.is_clockwise(); - if (was_cw) this->reverse(); - return was_cw; -} - -void -ExtrusionLoop::reverse() -{ - for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->reverse(); - std::reverse(this->paths.begin(), this->paths.end()); -} - -Point -ExtrusionLoop::first_point() const -{ - return this->paths.front().polyline.points.front(); -} - -Point -ExtrusionLoop::last_point() const -{ - return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way -} - -void -ExtrusionLoop::polygon(Polygon* polygon) const -{ - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - // for each polyline, append all points except the last one (because it coincides with the first one of the next polyline) - polygon->points.insert(polygon->points.end(), path->polyline.points.begin(), path->polyline.points.end()-1); - } -} - -double -ExtrusionLoop::length() const -{ - double len = 0; - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - len += path->polyline.length(); - return len; -} - -void -ExtrusionLoop::split_at_vertex(const Point &point) -{ - for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - int idx = path->polyline.find_point(point); - if (idx != -1) { - if (this->paths.size() == 1) { - // just change the order of points - path->polyline.points.insert(path->polyline.points.end(), path->polyline.points.begin() + 1, path->polyline.points.begin() + idx + 1); - path->polyline.points.erase(path->polyline.points.begin(), path->polyline.points.begin() + idx); - } else { - // if we have multiple paths we assume they have different types, so no need to - // check for continuity as we do for the single path case above - - // new paths list starts with the second half of current path - ExtrusionPaths new_paths; - { - ExtrusionPath p = *path; - p.polyline.points.erase(p.polyline.points.begin(), p.polyline.points.begin() + idx); - if (p.polyline.is_valid()) new_paths.push_back(p); - } - - // then we add all paths until the end of current path list - new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path - - // then we add all paths since the beginning of current list up to the previous one - new_paths.insert(new_paths.end(), path+1, this->paths.end()); // not including this path - - // finally we add the first half of current path - { - ExtrusionPath p = *path; - p.polyline.points.erase(p.polyline.points.begin() + idx + 1, p.polyline.points.end()); - if (p.polyline.is_valid()) new_paths.push_back(p); - } - // we can now override the old path list with the new one and stop looping - this->paths = new_paths; - } - return; - } - } - CONFESS("Point not found"); -} - -void -ExtrusionLoop::split_at(const Point &point) -{ - if (this->paths.empty()) return; - - // find the closest path and closest point - size_t path_idx = 0; - Point p = this->paths.front().first_point(); - double min = point.distance_to(p); - 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); - if (dist < min) { - p = p_tmp; - min = dist; - path_idx = path - this->paths.begin(); - } - } - - // now split path_idx in two parts - ExtrusionPath p1 = this->paths[path_idx]; - ExtrusionPath p2 = p1; - this->paths[path_idx].polyline.split_at(p, &p1.polyline, &p2.polyline); - - // install the two paths - this->paths.erase(this->paths.begin() + path_idx); - if (p2.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p2); - if (p1.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p1); - - // split at the new vertex - this->split_at_vertex(p); -} - -void -ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const -{ - *paths = this->paths; - - while (distance > 0 && !paths->empty()) { - ExtrusionPath &last = paths->back(); - double len = last.length(); - if (len <= distance) { - paths->pop_back(); - distance -= len; - } else { - last.polyline.clip_end(distance); - break; - } - } -} - -bool -ExtrusionLoop::has_overhang_point(const Point &point) const -{ - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - int pos = path->polyline.find_point(point); - if (pos != -1) { - // point belongs to this path - // we consider it overhang only if it's not an endpoint - return (path->is_bridge() && pos > 0 && pos != path->polyline.points.size()-1); - } - } - return false; -} - -#ifdef SLIC3RXS -REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); -#endif - -} diff --git a/xs/src/ExtrusionEntity.hpp b/xs/src/ExtrusionEntity.hpp deleted file mode 100644 index ccd85a689..000000000 --- a/xs/src/ExtrusionEntity.hpp +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef slic3r_ExtrusionEntity_hpp_ -#define slic3r_ExtrusionEntity_hpp_ - -#include -#include "Polygon.hpp" -#include "Polyline.hpp" - -namespace Slic3r { - -class ExPolygonCollection; -class ExtrusionEntityCollection; -class Extruder; - -/* Each ExtrusionRole value identifies a distinct set of { extruder, speed } */ -enum ExtrusionRole { - erPerimeter, - erExternalPerimeter, - erOverhangPerimeter, - erInternalInfill, - erSolidInfill, - erTopSolidInfill, - erBridgeInfill, - erGapFill, - erSkirt, - erSupportMaterial, - erSupportMaterialInterface, -}; - -/* Special flags describing loop */ -enum ExtrusionLoopRole { - elrDefault, - elrExternalPerimeter, - elrContourInternalPerimeter, -}; - -class ExtrusionEntity -{ - public: - virtual ExtrusionEntity* clone() const = 0; - virtual ~ExtrusionEntity() {}; - virtual void reverse() = 0; - virtual Point first_point() const = 0; - virtual Point last_point() const = 0; -}; - -typedef std::vector ExtrusionEntitiesPtr; - -class ExtrusionPath : public ExtrusionEntity -{ - public: - Polyline polyline; - ExtrusionRole role; - double mm3_per_mm; // mm^3 of plastic per mm of linear head motion - float width; - float height; - - ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; - ExtrusionPath* clone() const; - void reverse(); - Point first_point() const; - Point last_point() const; - void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; - void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; - void clip_end(double distance); - void simplify(double tolerance); - double length() const; - bool is_perimeter() const; - bool is_fill() const; - bool is_bridge() const; - std::string gcode(Extruder* extruder, double e, double F, - double xofs, double yofs, std::string extrusion_axis, - std::string gcode_line_suffix) const; - - private: - void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; -}; - -typedef std::vector ExtrusionPaths; - -class ExtrusionLoop : public ExtrusionEntity -{ - public: - ExtrusionPaths paths; - ExtrusionLoopRole role; - - ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {}; - operator Polygon() const; - ExtrusionLoop* clone() const; - bool make_clockwise(); - bool make_counter_clockwise(); - void reverse(); - Point first_point() const; - Point last_point() const; - void polygon(Polygon* polygon) const; - double length() const; - void split_at_vertex(const Point &point); - void split_at(const Point &point); - void clip_end(double distance, ExtrusionPaths* paths) const; - bool has_overhang_point(const Point &point) const; -}; - -} - -#endif diff --git a/xs/src/ExtrusionEntityCollection.cpp b/xs/src/ExtrusionEntityCollection.cpp deleted file mode 100644 index a958e53cf..000000000 --- a/xs/src/ExtrusionEntityCollection.cpp +++ /dev/null @@ -1,115 +0,0 @@ -#include "ExtrusionEntityCollection.hpp" -#include -#include - -namespace Slic3r { - -ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionEntityCollection& collection) - : no_sort(collection.no_sort), orig_indices(collection.orig_indices) -{ - this->entities.reserve(collection.entities.size()); - for (ExtrusionEntitiesPtr::const_iterator it = collection.entities.begin(); it != collection.entities.end(); ++it) - this->entities.push_back((*it)->clone()); -} - -ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other) -{ - ExtrusionEntityCollection tmp(other); - this->swap(tmp); - return *this; -} - -void -ExtrusionEntityCollection::swap (ExtrusionEntityCollection &c) -{ - std::swap(this->entities, c.entities); - std::swap(this->orig_indices, c.orig_indices); - std::swap(this->no_sort, c.no_sort); -} - -ExtrusionEntityCollection* -ExtrusionEntityCollection::clone() const -{ - return new ExtrusionEntityCollection(*this); -} - -void -ExtrusionEntityCollection::reverse() -{ - for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) { - (*it)->reverse(); - } - std::reverse(this->entities.begin(), this->entities.end()); -} - -Point -ExtrusionEntityCollection::first_point() const -{ - return this->entities.front()->first_point(); -} - -Point -ExtrusionEntityCollection::last_point() const -{ - return this->entities.back()->last_point(); -} - -void -ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, std::vector* orig_indices) const -{ - if (this->entities.empty()) return; - this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, orig_indices); -} - -void -ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, std::vector* orig_indices) const -{ - if (this->no_sort) { - *retval = *this; - return; - } - retval->entities.reserve(this->entities.size()); - retval->orig_indices.reserve(this->entities.size()); - - // if we're asked to return the original indices, build a map - std::map indices_map; - - ExtrusionEntitiesPtr my_paths; - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { - ExtrusionEntity* entity = (*it)->clone(); - my_paths.push_back(entity); - if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin(); - } - - Points endpoints; - for (ExtrusionEntitiesPtr::iterator it = my_paths.begin(); it != my_paths.end(); ++it) { - endpoints.push_back((*it)->first_point()); - if (no_reverse) { - endpoints.push_back((*it)->first_point()); - } else { - endpoints.push_back((*it)->last_point()); - } - } - - while (!my_paths.empty()) { - // find nearest point - int start_index = start_near.nearest_point_index(endpoints); - int path_index = start_index/2; - ExtrusionEntity* entity = my_paths.at(path_index); - if (start_index % 2 && !no_reverse) { - entity->reverse(); - } - retval->entities.push_back(my_paths.at(path_index)); - if (orig_indices != NULL) orig_indices->push_back(indices_map[entity]); - my_paths.erase(my_paths.begin() + path_index); - endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); - start_near = retval->entities.back()->last_point(); - } -} - -#ifdef SLIC3RXS -// there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection -REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); -#endif - -} diff --git a/xs/src/ExtrusionEntityCollection.hpp b/xs/src/ExtrusionEntityCollection.hpp deleted file mode 100644 index bc660611b..000000000 --- a/xs/src/ExtrusionEntityCollection.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef slic3r_ExtrusionEntityCollection_hpp_ -#define slic3r_ExtrusionEntityCollection_hpp_ - -#include -#include "ExtrusionEntity.hpp" - -namespace Slic3r { - -class ExtrusionEntityCollection : public ExtrusionEntity -{ - public: - ExtrusionEntityCollection* clone() const; - ExtrusionEntitiesPtr entities; - std::vector orig_indices; // handy for XS - bool no_sort; - ExtrusionEntityCollection(): no_sort(false) {}; - ExtrusionEntityCollection(const ExtrusionEntityCollection &collection); - ExtrusionEntityCollection& operator= (const ExtrusionEntityCollection &other); - void swap (ExtrusionEntityCollection &c); - void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; - void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; - void reverse(); - Point first_point() const; - Point last_point() const; -}; - -} - -#endif diff --git a/xs/src/Flow.cpp b/xs/src/Flow.cpp deleted file mode 100644 index 1592e0c35..000000000 --- a/xs/src/Flow.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "Flow.hpp" -#include - -namespace Slic3r { - -/* This constructor builds a Flow object from an extrusion width config setting - and other context properties. */ -Flow -Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) { - // we need layer height unless it's a bridge - if (height <= 0 && bridge_flow_ratio == 0) CONFESS("Invalid flow height supplied to new_from_config_width()"); - - float w; - if (bridge_flow_ratio > 0) { - // if bridge flow was requested, calculate bridge width - w = Flow::_bridge_width(nozzle_diameter, bridge_flow_ratio); - } else if (!width.percent && width.value == 0) { - // if user left option to 0, calculate a sane default width - w = Flow::_auto_width(role, nozzle_diameter, height); - } else { - // if user set a manual value, use it - w = width.get_abs_value(height); - } - - return Flow(w, height, nozzle_diameter, bridge_flow_ratio > 0); -} - -/* This constructor builds a Flow object from a given centerline spacing. */ -Flow -Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { - // we need layer height unless it's a bridge - if (height <= 0 && !bridge) CONFESS("Invalid flow height supplied to new_from_spacing()"); - - float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge); - return Flow(w, height, nozzle_diameter, bridge); -} - -/* This method returns the centerline spacing between two adjacent extrusions - having the same extrusion width (and other properties). */ -float -Flow::spacing() const { - if (this->bridge) { - return this->width + BRIDGE_EXTRA_SPACING; - } - - // rectangle with semicircles at the ends - float min_flow_spacing = this->width - this->height * (1 - PI/4.0); - return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing); -} - -/* This method returns the centerline spacing between an extrusion using this - flow and another one using another flow. - this->spacing(other) shall return the same value as other.spacing(*this) */ -float -Flow::spacing(const Flow &other) const { - assert(this->height == other.height); - assert(this->bridge == other.bridge); - - if (this->bridge) { - return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING; - } - - return this->spacing()/2 + other.spacing()/2; -} - -/* This method returns extrusion volume per head move unit. */ -double -Flow::mm3_per_mm() const { - if (this->bridge) { - return (this->width * this->width) * PI/4.0; - } - - // rectangle with semicircles at the ends - return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0); -} - -/* This static method returns bridge width for a given nozzle diameter. */ -float -Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) { - if (bridge_flow_ratio == 1) return nozzle_diameter; // optimization to avoid sqrt() - return sqrt(bridge_flow_ratio * (nozzle_diameter*nozzle_diameter)); -} - -/* This static method returns a sane extrusion width default. */ -float -Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { - // here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate - // shape: rectangle with semicircles at the ends - float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height); - - float min = nozzle_diameter * 1.05; - float max = -1; - if (role == frExternalPerimeter || role == frSupportMaterial) { - min = max = nozzle_diameter; - } else if (role != frInfill) { - // do not limit width for sparse infill so that we use full native flow for it - max = nozzle_diameter * 1.7; - } - if (max != -1 && width > max) width = max; - if (width < min) width = min; - - return width; -} - -/* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */ -float -Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { - if (bridge) { - return spacing - BRIDGE_EXTRA_SPACING; - } - - // rectangle with semicircles at the ends - return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(Flow, "Flow"); -#endif - -} diff --git a/xs/src/Flow.hpp b/xs/src/Flow.hpp deleted file mode 100644 index 88cae2477..000000000 --- a/xs/src/Flow.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef slic3r_Flow_hpp_ -#define slic3r_Flow_hpp_ - -#include -#include "Config.hpp" -#include "ExtrusionEntity.hpp" - -namespace Slic3r { - -#define BRIDGE_EXTRA_SPACING 0.05 -#define OVERLAP_FACTOR 1.0 - -enum FlowRole { - frExternalPerimeter, - frPerimeter, - frInfill, - frSolidInfill, - frTopSolidInfill, - frSupportMaterial, - frSupportMaterialInterface, -}; - -class Flow -{ - public: - float width, height, nozzle_diameter; - bool bridge; - - Flow(float _w, float _h, float _nd, bool _bridge = false) - : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; - float spacing() const; - float spacing(const Flow &other) const; - double mm3_per_mm() const; - coord_t scaled_width() const { - return scale_(this->width); - }; - coord_t scaled_spacing() const { - return scale_(this->spacing()); - }; - - static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); - static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); - - private: - static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio); - static float _auto_width(FlowRole role, float nozzle_diameter, float height); - static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); - static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio); -}; - -} - -#endif diff --git a/xs/src/GCode.hpp b/xs/src/GCode.hpp deleted file mode 100644 index a889532ea..000000000 --- a/xs/src/GCode.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef slic3r_GCode_hpp_ -#define slic3r_GCode_hpp_ - -#include -#include - -namespace Slic3r { - -// draft for a binary representation of a G-code line - -enum GCodeCmdType { - gcctSyncMotion, - gcctExtrude, - gcctResetE, - gcctSetTemp, - gcctSetTempWait, - gcctToolchange, - gcctCustom -}; - -class GCodeCmd { - public: - GCodeCmdType type; - float X, Y, Z, E, F; - unsigned short T, S; - std::string custom, comment; - float xy_dist; // cache - - GCodeCmd(GCodeCmdType type) - : type(type), X(0), Y(0), Z(0), E(0), F(0), T(-1), S(0), xy_dist(-1) {}; -}; - -} - -#endif diff --git a/xs/src/Geometry.cpp b/xs/src/Geometry.cpp deleted file mode 100644 index 8e083360b..000000000 --- a/xs/src/Geometry.cpp +++ /dev/null @@ -1,334 +0,0 @@ -#include "Geometry.hpp" -#include "Line.hpp" -#include "PolylineCollection.hpp" -#include "clipper.hpp" -#include -#include -#include -#include -#include -#include - -#ifdef SLIC3R_DEBUG -#include "SVG.hpp" -#endif - -using namespace boost::polygon; // provides also high() and low() - -namespace Slic3r { namespace Geometry { - -static bool -sort_points (Point a, Point b) -{ - return (a.x < b.x) || (a.x == b.x && a.y < b.y); -} - -/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */ -void -convex_hull(Points &points, Polygon* hull) -{ - assert(points.size() >= 3); - // sort input points - std::sort(points.begin(), points.end(), sort_points); - - int n = points.size(), k = 0; - 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]; - } - - // 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]; - } - - hull->points.resize(k); - - assert( hull->points.front().coincides_with(hull->points.back()) ); - hull->points.pop_back(); -} - -/* accepts an arrayref of points and returns a list of indices - according to a nearest-neighbor walk */ -void -chained_path(Points &points, std::vector &retval, Point start_near) -{ - PointPtrs my_points; - std::map indices; - my_points.reserve(points.size()); - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - my_points.push_back(&*it); - indices[&*it] = it - points.begin(); - } - - retval.reserve(points.size()); - while (!my_points.empty()) { - Points::size_type idx = start_near.nearest_point_index(my_points); - start_near = *my_points[idx]; - retval.push_back(indices[ my_points[idx] ]); - my_points.erase(my_points.begin() + idx); - } -} - -void -chained_path(Points &points, std::vector &retval) -{ - if (points.empty()) return; // can't call front() on empty vector - chained_path(points, retval, points.front()); -} - -/* retval and items must be different containers */ -template -void -chained_path_items(Points &points, T &items, T &retval) -{ - std::vector indices; - chained_path(points, indices); - for (std::vector::const_iterator it = indices.begin(); it != indices.end(); ++it) - retval.push_back(items[*it]); -} -template void chained_path_items(Points &points, ClipperLib::PolyNodes &items, ClipperLib::PolyNodes &retval); - -bool -directions_parallel(double angle1, double angle2, double max_diff) -{ - double diff = fabs(angle1 - angle2); - max_diff += EPSILON; - return diff < max_diff || fabs(diff - PI) < max_diff; -} - -Line -MedialAxis::edge_to_line(const VD::edge_type &edge) const -{ - Line line; - line.a.x = edge.vertex0()->x(); - line.a.y = edge.vertex0()->y(); - line.b.x = edge.vertex1()->x(); - line.b.y = edge.vertex1()->y(); - return line; -} - -void -MedialAxis::build(Polylines* polylines) -{ - /* - // build bounding box (we use it for clipping infinite segments) - // --> we have no infinite segments - this->bb = BoundingBox(this->lines); - */ - - construct_voronoi(this->lines.begin(), this->lines.end(), &this->vd); - - /* - // DEBUG: dump all Voronoi edges - { - for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { - if (edge->is_infinite()) continue; - - Polyline polyline; - polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); - polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); - polylines->push_back(polyline); - } - return; - } - */ - - // collect valid edges (i.e. prune those not belonging to MAT) - // note: this keeps twins, so it contains twice the number of the valid edges - this->edges.clear(); - for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { - // if we only process segments representing closed loops, none if the - // infinite edges (if any) would be part of our MAT anyway - if (edge->is_secondary() || edge->is_infinite()) continue; - this->edges.insert(&*edge); - } - - // count valid segments for each vertex - std::map< const VD::vertex_type*,std::set > vertex_edges; - std::set entry_nodes; - for (VD::const_vertex_iterator vertex = this->vd.vertices().begin(); vertex != this->vd.vertices().end(); ++vertex) { - // get a reference to the list of valid edges originating from this vertex - std::set& edges = vertex_edges[&*vertex]; - - // get one random edge originating from this vertex - const VD::edge_type* edge = vertex->incident_edge(); - do { - if (this->edges.count(edge) > 0) // only count valid edges - edges.insert(edge); - edge = edge->rot_next(); // next edge originating from this vertex - } while (edge != vertex->incident_edge()); - - // if there's only one edge starting at this vertex then it's a leaf - size_t edge_count = edges.size(); - if (edge_count == 1) { - entry_nodes.insert(&*vertex); - } - } - - // prune recursively - while (!entry_nodes.empty()) { - // get a random entry node - const VD::vertex_type* v = *entry_nodes.begin(); - - // get edge starting from v - assert(!vertex_edges[v].empty()); - const VD::edge_type* edge = *vertex_edges[v].begin(); - - if (!this->is_valid_edge(*edge)) { - // if edge is not valid, erase it from edge list - (void)this->edges.erase(edge); - (void)this->edges.erase(edge->twin()); - - // decrement edge counters for the affected nodes - const VD::vertex_type* v1 = edge->vertex1(); - (void)vertex_edges[v].erase(edge); - (void)vertex_edges[v1].erase(edge->twin()); - - // also, check whether the end vertex is a new leaf - if (vertex_edges[v1].size() == 1) { - entry_nodes.insert(v1); - } else if (vertex_edges[v1].empty()) { - entry_nodes.erase(v1); - } - } - - // remove node from the set to prevent it from being visited again - entry_nodes.erase(v); - } - - // iterate through the valid edges to build polylines - while (!this->edges.empty()) { - const VD::edge_type& edge = **this->edges.begin(); - - // start a polyline - Polyline polyline; - polyline.points.push_back(Point( edge.vertex0()->x(), edge.vertex0()->y() )); - polyline.points.push_back(Point( edge.vertex1()->x(), edge.vertex1()->y() )); - - // remove this edge and its twin from the available edges - (void)this->edges.erase(&edge); - (void)this->edges.erase(edge.twin()); - - // get next points - this->process_edge_neighbors(edge, &polyline.points); - - // get previous points - Points pp; - this->process_edge_neighbors(*edge.twin(), &pp); - polyline.points.insert(polyline.points.begin(), pp.rbegin(), pp.rend()); - - // append polyline to result if it's not too small - if (polyline.length() > this->max_width) - polylines->push_back(polyline); - } -} - -void -MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points) -{ - // Since rot_next() works on the edge starting point but we want - // to find neighbors on the ending point, we just swap edge with - // its twin. - const VD::edge_type& twin = *edge.twin(); - - // count neighbors for this edge - std::vector neighbors; - for (const VD::edge_type* neighbor = twin.rot_next(); neighbor != &twin; neighbor = neighbor->rot_next()) { - if (this->edges.count(neighbor) > 0) neighbors.push_back(neighbor); - } - - // if we have a single neighbor then we can continue recursively - if (neighbors.size() == 1) { - const VD::edge_type& neighbor = *neighbors.front(); - points->push_back(Point( neighbor.vertex1()->x(), neighbor.vertex1()->y() )); - (void)this->edges.erase(&neighbor); - (void)this->edges.erase(neighbor.twin()); - this->process_edge_neighbors(neighbor, points); - } -} - -bool -MedialAxis::is_valid_edge(const VD::edge_type& edge) const -{ - /* If the cells sharing this edge have a common vertex, we're not interested - in this edge. Why? Because it means that the edge lies on the bisector of - two contiguous input lines and it was included in the Voronoi graph because - it's the locus of centers of circles tangent to both vertices. Due to the - "thin" nature of our input, these edges will be very short and not part of - our wanted output. */ - - const VD::cell_type &cell1 = *edge.cell(); - const VD::cell_type &cell2 = *edge.twin()->cell(); - if (cell1.contains_segment() && cell2.contains_segment()) { - Line segment1 = this->retrieve_segment(cell1); - Line segment2 = this->retrieve_segment(cell2); - if (segment1.a == segment2.b || segment1.b == segment2.a) return false; - - // calculate relative angle between the two boundary segments - double angle = fabs(segment2.orientation() - segment1.orientation()); - - // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction) - // we're interested only in segments close to the second case (facing segments) - // so we allow some tolerance (say, 30°) - if (angle < PI*2/3 ) { - return false; - } - - // each vertex is equidistant to both cell segments - // but such distance might differ between the two vertices; - // in this case it means the shape is getting narrow (like a corner) - // and we might need to skip the edge since it's not really part of - // our skeleton - Point v0( edge.vertex0()->x(), edge.vertex0()->y() ); - Point v1( edge.vertex1()->x(), edge.vertex1()->y() ); - double dist0 = v0.distance_to(segment1); - double dist1 = v1.distance_to(segment1); - - /* - double diff = fabs(dist1 - dist0); - double dist_between_segments1 = segment1.a.distance_to(segment2); - double dist_between_segments2 = segment1.b.distance_to(segment2); - printf("w = %f/%f, dist0 = %f, dist1 = %f, diff = %f, seg1len = %f, seg2len = %f, edgelen = %f, s2s = %f / %f\n", - unscale(this->max_width), unscale(this->min_width), - unscale(dist0), unscale(dist1), unscale(diff), - unscale(segment1.length()), unscale(segment2.length()), - unscale(this->edge_to_line(edge).length()), - unscale(dist_between_segments1), unscale(dist_between_segments2) - ); - */ - - // if this segment is the centerline for a very thin area, we might want to skip it - // in case the area is too thin - if (dist0 < this->min_width/2 || dist1 < this->min_width/2) { - //printf(" => too thin, skipping\n"); - return false; - } - - /* - // if distance between this edge and the thin area boundary is greater - // than half the max width, then it's not a true medial axis segment - if (dist1 > this->width*2) { - printf(" => too fat, skipping\n"); - //return false; - } - */ - - return true; - } - - return false; -} - -Line -MedialAxis::retrieve_segment(const VD::cell_type& cell) const -{ - VD::cell_type::source_index_type index = cell.source_index() - this->points.size(); - return this->lines[index]; -} - -} } diff --git a/xs/src/Geometry.hpp b/xs/src/Geometry.hpp deleted file mode 100644 index 2dc183f82..000000000 --- a/xs/src/Geometry.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef slic3r_Geometry_hpp_ -#define slic3r_Geometry_hpp_ - -#include "BoundingBox.hpp" -#include "Polygon.hpp" -#include "Polyline.hpp" - -#include "boost/polygon/voronoi.hpp" -using boost::polygon::voronoi_builder; -using boost::polygon::voronoi_diagram; - -namespace Slic3r { namespace Geometry { - -void convex_hull(Points &points, Polygon* hull); -void chained_path(Points &points, std::vector &retval, Point start_near); -void chained_path(Points &points, std::vector &retval); -template void chained_path_items(Points &points, T &items, T &retval); -bool directions_parallel(double angle1, double angle2, double max_diff = 0); - -class MedialAxis { - public: - Points points; - Lines lines; - double max_width; - double min_width; - MedialAxis(double _max_width, double _min_width) : max_width(_max_width), min_width(_min_width) {}; - void build(Polylines* polylines); - - private: - typedef voronoi_diagram VD; - VD vd; - std::set edges; - Line edge_to_line(const VD::edge_type &edge) const; - void process_edge_neighbors(const voronoi_diagram::edge_type& edge, Points* points); - bool is_valid_edge(const voronoi_diagram::edge_type& edge) const; - Line retrieve_segment(const voronoi_diagram::cell_type& cell) const; -}; - -} } - -#endif diff --git a/xs/src/Layer.cpp b/xs/src/Layer.cpp deleted file mode 100644 index 689077d46..000000000 --- a/xs/src/Layer.cpp +++ /dev/null @@ -1,144 +0,0 @@ -#include "Layer.hpp" -#include "ClipperUtils.hpp" -#include "Geometry.hpp" -#include "Print.hpp" - - -namespace Slic3r { - -Layer::Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, - coordf_t slice_z) -: _id(id), - _object(object), - upper_layer(NULL), - lower_layer(NULL), - regions(), - slicing_errors(false), - slice_z(slice_z), - print_z(print_z), - height(height), - slices() -{ -} - -Layer::~Layer() -{ - // remove references to self - if (NULL != this->upper_layer) { - this->upper_layer->lower_layer = NULL; - } - - if (NULL != this->lower_layer) { - this->lower_layer->upper_layer = NULL; - } - - this->clear_regions(); -} - -int -Layer::id() -{ - return this->_id; -} - -PrintObject* -Layer::object() -{ - return this->_object; -} - - -size_t -Layer::region_count() -{ - return this->regions.size(); -} - -void -Layer::clear_regions() -{ - for (int i = this->regions.size()-1; i >= 0; --i) - this->delete_region(i); -} - -LayerRegion* -Layer::get_region(int idx) -{ - return this->regions.at(idx); -} - -LayerRegion* -Layer::add_region(PrintRegion* print_region) -{ - LayerRegion* region = new LayerRegion(this, print_region); - this->regions.push_back(region); - return region; -} - -void -Layer::delete_region(int idx) -{ - LayerRegionPtrs::iterator i = this->regions.begin() + idx; - LayerRegion* item = *i; - this->regions.erase(i); - delete item; -} - -// merge all regions' slices to get islands -void -Layer::make_slices() -{ - ExPolygons slices; - if (this->regions.size() == 1) { - // optimization: if we only have one region, take its slices - slices = this->regions.front()->slices; - } else { - Polygons slices_p; - FOREACH_LAYERREGION(this, layerm) { - Polygons region_slices_p = (*layerm)->slices; - slices_p.insert(slices_p.end(), region_slices_p.begin(), region_slices_p.end()); - } - union_(slices_p, slices); - } - - this->slices.expolygons.clear(); - this->slices.expolygons.reserve(slices.size()); - - // prepare ordering points - Points ordering_points; - ordering_points.reserve(slices.size()); - for (ExPolygons::const_iterator ex = slices.begin(); ex != slices.end(); ++ex) - ordering_points.push_back(ex->contour.first_point()); - - // sort slices - std::vector order; - Slic3r::Geometry::chained_path(ordering_points, order); - - // populate slices vector - for (std::vector::const_iterator it = order.begin(); it != order.end(); ++it) { - this->slices.expolygons.push_back(slices[*it]); - } -} - - -#ifdef SLIC3RXS -REGISTER_CLASS(Layer, "Layer"); -#endif - - -SupportLayer::SupportLayer(int id, PrintObject *object, coordf_t height, - coordf_t print_z, coordf_t slice_z) -: Layer(id, object, height, print_z, slice_z) -{ -} - -SupportLayer::~SupportLayer() -{ -} - -#ifdef SLIC3RXS -REGISTER_CLASS(SupportLayer, "Layer::Support"); -#endif - - -} diff --git a/xs/src/Layer.hpp b/xs/src/Layer.hpp deleted file mode 100644 index cea1535b0..000000000 --- a/xs/src/Layer.hpp +++ /dev/null @@ -1,124 +0,0 @@ -#ifndef slic3r_Layer_hpp_ -#define slic3r_Layer_hpp_ - -#include -#include "Flow.hpp" -#include "SurfaceCollection.hpp" -#include "ExtrusionEntityCollection.hpp" -#include "ExPolygonCollection.hpp" -#include "PolylineCollection.hpp" - - -namespace Slic3r { - -typedef std::pair t_layer_height_range; -typedef std::map t_layer_height_ranges; - -class Layer; -class PrintRegion; -class PrintObject; - - -// TODO: make stuff private -class LayerRegion -{ - friend class Layer; - - public: - Layer* layer(); - PrintRegion* region(); - - // collection of surfaces generated by slicing the original geometry - // divided by type top/bottom/internal - SurfaceCollection slices; - - // collection of extrusion paths/loops filling gaps - ExtrusionEntityCollection thin_fills; - - // collection of surfaces for infill generation - SurfaceCollection fill_surfaces; - - // collection of expolygons representing the bridged areas (thus not - // needing support material) - ExPolygonCollection bridged; - - // collection of polylines representing the unsupported bridge edges - PolylineCollection unsupported_bridge_edges; - - // ordered collection of extrusion paths/loops to build all perimeters - ExtrusionEntityCollection perimeters; - - // ordered collection of extrusion paths to fill surfaces - ExtrusionEntityCollection fills; - - Flow flow(FlowRole role, bool bridge = false, double width = -1) const; - - private: - Layer *_layer; - PrintRegion *_region; - - LayerRegion(Layer *layer, PrintRegion *region); - ~LayerRegion(); -}; - - -typedef std::vector LayerRegionPtrs; - -class Layer { - friend class PrintObject; - - public: - int id(); - PrintObject* object(); - - Layer *upper_layer; - Layer *lower_layer; - LayerRegionPtrs regions; - bool slicing_errors; - coordf_t slice_z; // Z used for slicing in unscaled coordinates - coordf_t print_z; // Z used for printing in unscaled coordinates - coordf_t height; // layer height in unscaled coordinates - - // collection of expolygons generated by slicing the original geometry; - // also known as 'islands' (all regions and surface types are merged here) - ExPolygonCollection slices; - - - size_t region_count(); - LayerRegion* get_region(int idx); - LayerRegion* add_region(PrintRegion* print_region); - - void make_slices(); - - protected: - int _id; // sequential number of layer, 0-based - PrintObject *_object; - - - Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, - coordf_t slice_z); - virtual ~Layer(); - - void clear_regions(); - void delete_region(int idx); -}; - - -class SupportLayer : public Layer { - friend class PrintObject; - - public: - ExPolygonCollection support_islands; - ExtrusionEntityCollection support_fills; - ExtrusionEntityCollection support_interface_fills; - - protected: - SupportLayer(int id, PrintObject *object, coordf_t height, coordf_t print_z, - coordf_t slice_z); - virtual ~SupportLayer(); -}; - - -} - -#endif diff --git a/xs/src/LayerRegion.cpp b/xs/src/LayerRegion.cpp deleted file mode 100644 index cb92c4ee3..000000000 --- a/xs/src/LayerRegion.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include "Layer.hpp" -#include "Print.hpp" - -namespace Slic3r { - -LayerRegion::LayerRegion(Layer *layer, PrintRegion *region) -: _layer(layer), - _region(region) -{ -} - -LayerRegion::~LayerRegion() -{ -} - -Layer* -LayerRegion::layer() -{ - return this->_layer; -} - -PrintRegion* -LayerRegion::region() -{ - return this->_region; -} - -Flow -LayerRegion::flow(FlowRole role, bool bridge, double width) const -{ - return this->_region->flow( - role, - this->_layer->height, - bridge, - this->_layer->id() == 0, - width, - *this->_layer->object() - ); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(LayerRegion, "Layer::Region"); -#endif - -} diff --git a/xs/src/Line.cpp b/xs/src/Line.cpp deleted file mode 100644 index 86efceddf..000000000 --- a/xs/src/Line.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "Geometry.hpp" -#include "Line.hpp" -#include "Polyline.hpp" -#include -#include -#include - -namespace Slic3r { - -std::string -Line::wkt() const -{ - std::ostringstream ss; - ss << "LINESTRING(" << this->a.x << " " << this->a.y << "," - << this->b.x << " " << this->b.y << ")"; - return ss.str(); -} - -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 ¢er) -{ - this->a.rotate(angle, center); - this->b.rotate(angle, center); -} - -void -Line::reverse() -{ - std::swap(this->a, this->b); -} - -double -Line::length() const -{ - return this->a.distance_to(this->b); -} - -Point* -Line::midpoint() const -{ - return new Point ((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0); -} - -void -Line::point_at(double distance, 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::coincides_with(const Line &line) const -{ - return this->a.coincides_with(line.a) && this->b.coincides_with(line.b); -} - -double -Line::distance_to(const Point &point) const -{ - return point.distance_to(*this); -} - -double -Line::atan2_() const -{ - return atan2(this->b.y - this->a.y, this->b.x - this->a.x); -} - -double -Line::orientation() const -{ - double angle = this->atan2_(); - if (angle < 0) angle = 2*PI + angle; - return angle; -} - -double -Line::direction() const -{ - double atan2 = this->atan2_(); - return (atan2 == PI) ? 0 - : (atan2 < 0) ? (atan2 + PI) - : 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 -{ - return Vector(this->b.x - this->a.x, this->b.y - this->a.y); -} - -#ifdef SLIC3RXS - -REGISTER_CLASS(Line, "Line"); - -void -Line::from_SV(SV* line_sv) -{ - AV* line_av = (AV*)SvRV(line_sv); - this->a.from_SV_check(*av_fetch(line_av, 0, 0)); - this->b.from_SV_check(*av_fetch(line_av, 1, 0)); -} - -void -Line::from_SV_check(SV* line_sv) -{ - if (sv_isobject(line_sv) && (SvTYPE(SvRV(line_sv)) == SVt_PVMG)) { - if (!sv_isa(line_sv, perl_class_name(this)) && !sv_isa(line_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object", perl_class_name(this)); - *this = *(Line*)SvIV((SV*)SvRV( line_sv )); - } else { - this->from_SV(line_sv); - } -} - -SV* -Line::to_AV() { - AV* av = newAV(); - av_extend(av, 1); - - av_store(av, 0, perl_to_SV_ref(this->a)); - av_store(av, 1, perl_to_SV_ref(this->b)); - - return newRV_noinc((SV*)av); -} - -SV* -Line::to_SV_pureperl() const { - AV* av = newAV(); - av_extend(av, 1); - av_store(av, 0, this->a.to_SV_pureperl()); - av_store(av, 1, this->b.to_SV_pureperl()); - return newRV_noinc((SV*)av); -} -#endif - -} diff --git a/xs/src/Line.hpp b/xs/src/Line.hpp deleted file mode 100644 index 3f86ed4a7..000000000 --- a/xs/src/Line.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef slic3r_Line_hpp_ -#define slic3r_Line_hpp_ - -#include -#include "Point.hpp" - -namespace Slic3r { - -class Line; -class Polyline; - -class Line -{ - public: - Point a; - Point b; - Line() {}; - explicit Line(Point _a, Point _b): a(_a), b(_b) {}; - std::string wkt() const; - operator Polyline() const; - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Point ¢er); - void reverse(); - double length() const; - Point* midpoint() const; - void point_at(double distance, Point* point) const; - Point point_at(double distance) const; - bool coincides_with(const Line &line) const; - double distance_to(const Point &point) const; - bool parallel_to(double angle) const; - bool parallel_to(const Line &line) const; - double atan2_() const; - double orientation() const; - double direction() const; - Vector vector() const; - - #ifdef SLIC3RXS - void from_SV(SV* line_sv); - void from_SV_check(SV* line_sv); - SV* to_AV(); - SV* to_SV_pureperl() const; - #endif -}; - -typedef std::vector Lines; - -} - -// start Boost -#include -namespace boost { namespace polygon { - template <> - struct geometry_concept { typedef segment_concept type; }; - - template <> - struct segment_traits { - typedef coord_t coordinate_type; - typedef Point point_type; - - static inline point_type get(const Line& line, direction_1d dir) { - return dir.to_int() ? line.b : line.a; - } - }; -} } -// end Boost - -#endif diff --git a/xs/src/Model.cpp b/xs/src/Model.cpp deleted file mode 100644 index ee8dc1057..000000000 --- a/xs/src/Model.cpp +++ /dev/null @@ -1,369 +0,0 @@ -#include "Model.hpp" - -namespace Slic3r { - -Model::Model() {} - -Model::Model(const Model &other) -{ - // copy materials - for (ModelMaterialMap::const_iterator i = other.materials.begin(); i != other.materials.end(); ++i) - this->add_material(i->first, *i->second); - - // copy objects - this->objects.reserve(other.objects.size()); - for (ModelObjectPtrs::const_iterator i = other.objects.begin(); i != other.objects.end(); ++i) - this->add_object(**i); -} - -Model& Model::operator= (Model other) -{ - this->swap(other); - return *this; -} - -void -Model::swap(Model &other) -{ - std::swap(this->materials, other.materials); - std::swap(this->objects, other.objects); -} - -Model::~Model() -{ - this->clear_objects(); - this->clear_materials(); -} - -ModelObject* -Model::add_object() -{ - ModelObject* new_object = new ModelObject(this); - this->objects.push_back(new_object); - return new_object; -} - -ModelObject* -Model::add_object(const ModelObject &other) -{ - ModelObject* new_object = new ModelObject(this, other); - this->objects.push_back(new_object); - return new_object; -} - -void -Model::delete_object(size_t idx) -{ - ModelObjectPtrs::iterator i = this->objects.begin() + idx; - delete *i; - this->objects.erase(i); -} - -void -Model::clear_objects() -{ - // int instead of size_t because it can be -1 when vector is empty - for (int i = this->objects.size()-1; i >= 0; --i) - this->delete_object(i); -} - -void -Model::delete_material(t_model_material_id material_id) -{ - ModelMaterialMap::iterator i = this->materials.find(material_id); - if (i != this->materials.end()) { - delete i->second; - this->materials.erase(i); - } -} - -void -Model::clear_materials() -{ - while (!this->materials.empty()) - this->delete_material( this->materials.begin()->first ); -} - -ModelMaterial* -Model::add_material(t_model_material_id material_id) -{ - ModelMaterial* material = this->get_material(material_id); - if (material == NULL) { - material = this->materials[material_id] = new ModelMaterial(this); - } - return material; -} - -ModelMaterial* -Model::add_material(t_model_material_id material_id, const ModelMaterial &other) -{ - // delete existing material if any - ModelMaterial* material = this->get_material(material_id); - if (material != NULL) { - delete material; - } - - // set new material - material = new ModelMaterial(this, other); - this->materials[material_id] = material; - return material; -} - -ModelMaterial* -Model::get_material(t_model_material_id material_id) -{ - ModelMaterialMap::iterator i = this->materials.find(material_id); - if (i == this->materials.end()) { - return NULL; - } else { - return i->second; - } -} - -/* -void -Model::duplicate_objects_grid(unsigned int x, unsigned int y, coordf_t distance) -{ - if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; - if (this->objects.empty()) throw "No objects!"; - - ModelObject* object = this->objects.front(); - object->clear_instances(); - - BoundingBoxf3 bb; - object->bounding_box(&bb); - Sizef3 size = bb.size(); - - for (unsigned int x_copy = 1; x_copy <= x; ++x_copy) { - for (unsigned int y_copy = 1; y_copy <= y; ++y_copy) { - ModelInstance* instance = object->add_instance(); - instance->offset.x = (size.x + distance) * (x_copy-1); - instance->offset.y = (size.y + distance) * (y_copy-1); - } - } -} -*/ - -bool -Model::has_objects_with_no_instances() const -{ - for (ModelObjectPtrs::const_iterator i = this->objects.begin(); - i != this->objects.end(); ++i) - { - if ((*i)->instances.empty()) { - return true; - } - } - - return false; -} - -#ifdef SLIC3RXS -REGISTER_CLASS(Model, "Model"); -#endif - - -ModelMaterial::ModelMaterial(Model *model) : model(model) {} -ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other) - : model(model), config(other.config), attributes(other.attributes) -{} - -void -ModelMaterial::apply(const t_model_material_attributes &attributes) -{ - this->attributes.insert(attributes.begin(), attributes.end()); -} - - -#ifdef SLIC3RXS -REGISTER_CLASS(ModelMaterial, "Model::Material"); -#endif - - -ModelObject::ModelObject(Model *model) - : model(model) -{} - -ModelObject::ModelObject(Model *model, const ModelObject &other) -: model(model), - name(other.name), - input_file(other.input_file), - instances(), - volumes(), - config(other.config), - layer_height_ranges(other.layer_height_ranges), - origin_translation(other.origin_translation), - _bounding_box(other._bounding_box), - _bounding_box_valid(other._bounding_box_valid) -{ - - this->volumes.reserve(other.volumes.size()); - for (ModelVolumePtrs::const_iterator i = other.volumes.begin(); i != other.volumes.end(); ++i) - this->add_volume(**i); - - this->instances.reserve(other.instances.size()); - for (ModelInstancePtrs::const_iterator i = other.instances.begin(); i != other.instances.end(); ++i) - this->add_instance(**i); -} - -ModelObject& ModelObject::operator= (ModelObject other) -{ - this->swap(other); - return *this; -} - -void -ModelObject::swap(ModelObject &other) -{ - std::swap(this->input_file, other.input_file); - std::swap(this->instances, other.instances); - std::swap(this->volumes, other.volumes); - std::swap(this->config, other.config); - std::swap(this->layer_height_ranges, other.layer_height_ranges); - std::swap(this->origin_translation, other.origin_translation); - std::swap(this->_bounding_box, other._bounding_box); - std::swap(this->_bounding_box_valid, other._bounding_box_valid); -} - -ModelObject::~ModelObject() -{ - this->clear_volumes(); - this->clear_instances(); -} - -ModelVolume* -ModelObject::add_volume(const TriangleMesh &mesh) -{ - ModelVolume* v = new ModelVolume(this, mesh); - this->volumes.push_back(v); - this->invalidate_bounding_box(); - return v; -} - -ModelVolume* -ModelObject::add_volume(const ModelVolume &other) -{ - ModelVolume* v = new ModelVolume(this, other); - this->volumes.push_back(v); - this->invalidate_bounding_box(); - return v; -} - -void -ModelObject::delete_volume(size_t idx) -{ - ModelVolumePtrs::iterator i = this->volumes.begin() + idx; - delete *i; - this->volumes.erase(i); - this->invalidate_bounding_box(); -} - -void -ModelObject::clear_volumes() -{ - // int instead of size_t because it can be -1 when vector is empty - for (int i = this->volumes.size()-1; i >= 0; --i) - this->delete_volume(i); -} - -ModelInstance* -ModelObject::add_instance() -{ - ModelInstance* i = new ModelInstance(this); - this->instances.push_back(i); - this->invalidate_bounding_box(); - return i; -} - -ModelInstance* -ModelObject::add_instance(const ModelInstance &other) -{ - ModelInstance* i = new ModelInstance(this, other); - this->instances.push_back(i); - this->invalidate_bounding_box(); - return i; -} - -void -ModelObject::delete_instance(size_t idx) -{ - ModelInstancePtrs::iterator i = this->instances.begin() + idx; - delete *i; - this->instances.erase(i); - this->invalidate_bounding_box(); -} - -void -ModelObject::delete_last_instance() -{ - this->delete_instance(this->instances.size() - 1); -} - -void -ModelObject::clear_instances() -{ - for (size_t i = 0; i < this->instances.size(); ++i) - this->delete_instance(i); -} - -void -ModelObject::invalidate_bounding_box() -{ - this->_bounding_box_valid = false; -} - -#ifdef SLIC3RXS -REGISTER_CLASS(ModelObject, "Model::Object"); -#endif - - -ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh) -: object(object), mesh(mesh), modifier(false) -{} - -ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other) -: object(object), name(other.name), mesh(other.mesh), config(other.config), - modifier(other.modifier) -{ - this->material_id(other.material_id()); -} - -t_model_material_id -ModelVolume::material_id() const -{ - return this->_material_id; -} - -void -ModelVolume::material_id(t_model_material_id material_id) -{ - this->_material_id = material_id; - - // ensure this->_material_id references an existing material - (void)this->object->get_model()->add_material(material_id); -} - -ModelMaterial* -ModelVolume::material() const -{ - return this->object->get_model()->get_material(this->_material_id); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(ModelVolume, "Model::Volume"); -#endif - - -ModelInstance::ModelInstance(ModelObject *object) -: object(object), rotation(0), scaling_factor(1) -{} - -ModelInstance::ModelInstance(ModelObject *object, const ModelInstance &other) -: object(object), rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset) -{} - -#ifdef SLIC3RXS -REGISTER_CLASS(ModelInstance, "Model::Instance"); -#endif - -} diff --git a/xs/src/Model.hpp b/xs/src/Model.hpp deleted file mode 100644 index dfcd75b4c..000000000 --- a/xs/src/Model.hpp +++ /dev/null @@ -1,181 +0,0 @@ -#ifndef slic3r_Model_hpp_ -#define slic3r_Model_hpp_ - -#include -#include "PrintConfig.hpp" -#include "Layer.hpp" -#include "Point.hpp" -#include "TriangleMesh.hpp" -#include -#include -#include -#include - -namespace Slic3r { - -class ModelInstance; -class ModelMaterial; -class ModelObject; -class ModelVolume; - -typedef std::string t_model_material_id; -typedef std::string t_model_material_attribute; -typedef std::map t_model_material_attributes; - -typedef std::map ModelMaterialMap; -typedef std::vector ModelObjectPtrs; -typedef std::vector ModelVolumePtrs; -typedef std::vector ModelInstancePtrs; - -class Model -{ - public: - ModelMaterialMap materials; - ModelObjectPtrs objects; - - Model(); - Model(const Model &other); - Model& operator= (Model other); - void swap(Model &other); - ~Model(); - ModelObject* add_object(); - ModelObject* add_object(const ModelObject &other); - void delete_object(size_t idx); - void clear_objects(); - - ModelMaterial* add_material(t_model_material_id material_id); - ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other); - ModelMaterial* get_material(t_model_material_id material_id); - void delete_material(t_model_material_id material_id); - void clear_materials(); - // void duplicate_objects_grid(unsigned int x, unsigned int y, coordf_t distance); - // void duplicate_objects(size_t copies_num, coordf_t distance, const BoundingBox &bb); - // void arrange_objects(coordf_t distance, const BoundingBox &bb); - // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); - bool has_objects_with_no_instances() const; - // void bounding_box(BoundingBox* bb) const; - // void align_to_origin(); - // void center_instances_around_point(const Pointf &point); - // void translate(coordf_t x, coordf_t y, coordf_t z); - // void mesh(TriangleMesh* mesh) const; - // void split_meshes(); - // std::string get_material_name(t_model_material_id material_id); - - - private: - void _arrange(const std::vector &sizes, coordf_t distance, const BoundingBox &bb) const; -}; - -class ModelMaterial -{ - friend class Model; - public: - t_model_material_attributes attributes; - DynamicPrintConfig config; - - Model* get_model() const { return this->model; }; - void apply(const t_model_material_attributes &attributes); - - private: - Model* model; - - ModelMaterial(Model *model); - ModelMaterial(Model *model, const ModelMaterial &other); -}; - -class ModelObject -{ - friend class Model; - public: - std::string name; - std::string input_file; - ModelInstancePtrs instances; - ModelVolumePtrs volumes; - DynamicPrintConfig config; - t_layer_height_ranges layer_height_ranges; - Pointf origin_translation; - - // these should be private but we need to expose them via XS until all methods are ported - BoundingBoxf3 _bounding_box; - bool _bounding_box_valid; - - Model* get_model() const { return this->model; }; - - ModelVolume* add_volume(const TriangleMesh &mesh); - ModelVolume* add_volume(const ModelVolume &volume); - void delete_volume(size_t idx); - void clear_volumes(); - - ModelInstance* add_instance(); - ModelInstance* add_instance(const ModelInstance &instance); - void delete_instance(size_t idx); - void delete_last_instance(); - void clear_instances(); - - void invalidate_bounding_box(); - - //void raw_mesh(TriangleMesh* mesh) const; - //void mesh(TriangleMesh* mesh) const; - //void instance_bounding_box(size_t instance_idx, BoundingBox* bb) const; - //void center_around_origin(); - //void translate(coordf_t x, coordf_t y, coordf_t z); - //size_t materials_count() const; - //void unique_materials(std::vector* materials) const; - //size_t facets_count() const; - //bool needed_repair() const; - - private: - Model* model; - - ModelObject(Model *model); - ModelObject(Model *model, const ModelObject &other); - ModelObject& operator= (ModelObject other); - void swap(ModelObject &other); - ~ModelObject(); - void update_bounding_box(); -}; - -class ModelVolume -{ - friend class ModelObject; - public: - std::string name; - TriangleMesh mesh; - DynamicPrintConfig config; - bool modifier; - - ModelObject* get_object() const { return this->object; }; - t_model_material_id material_id() const; - void material_id(t_model_material_id material_id); - ModelMaterial* material() const; - - private: - ModelObject* object; - t_model_material_id _material_id; - - ModelVolume(ModelObject *object, const TriangleMesh &mesh); - ModelVolume(ModelObject *object, const ModelVolume &other); -}; - -class ModelInstance -{ - friend class ModelObject; - public: - double rotation; // around mesh center point - double scaling_factor; - Pointf offset; // in unscaled coordinates - - ModelObject* get_object() const { return this->object; }; - void transform_mesh(TriangleMesh* mesh, bool dont_translate) const; - void transform_polygon(Polygon* polygon) const; - - private: - ModelObject* object; - - ModelInstance(ModelObject *object); - ModelInstance(ModelObject *object, const ModelInstance &other); -}; - -} - -#endif diff --git a/xs/src/MotionPlanner.cpp b/xs/src/MotionPlanner.cpp deleted file mode 100644 index 82108afd9..000000000 --- a/xs/src/MotionPlanner.cpp +++ /dev/null @@ -1,278 +0,0 @@ -#include "BoundingBox.hpp" -#include "MotionPlanner.hpp" -#include // for numeric_limits - -#include "boost/polygon/voronoi.hpp" -using boost::polygon::voronoi_builder; -using boost::polygon::voronoi_diagram; - -namespace Slic3r { - -MotionPlanner::MotionPlanner(const ExPolygons &islands) - : islands(islands), initialized(false) -{} - -MotionPlanner::~MotionPlanner() -{ - for (std::vector::iterator graph = this->graphs.begin(); graph != this->graphs.end(); ++graph) - delete *graph; -} - -void -MotionPlanner::initialize() -{ - if (this->initialized) return; - - ExPolygons expp; - for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) { - island->simplify(SCALED_EPSILON, expp); - } - this->islands = expp; - - // loop through islands in order to create inner expolygons and collect their contours - this->inner.reserve(this->islands.size()); - Polygons outer_holes; - for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) { - this->inner.push_back(ExPolygonCollection()); - offset_ex(*island, this->inner.back(), -MP_INNER_MARGIN); - - outer_holes.push_back(island->contour); - } - - // grow island contours in order to prepare holes of the outer environment - // This is actually wrong because it might merge contours that are close, - // thus confusing the island check in shortest_path() below - //offset(outer_holes, outer_holes, +MP_OUTER_MARGIN); - - // generate outer contour as bounding box of everything - Points points; - for (Polygons::const_iterator contour = outer_holes.begin(); contour != outer_holes.end(); ++contour) - points.insert(points.end(), contour->points.begin(), contour->points.end()); - BoundingBox bb(points); - - // grow outer contour - Polygons contour; - offset(bb.polygon(), contour, +MP_OUTER_MARGIN); - assert(contour.size() == 1); - - // make expolygon for outer environment - ExPolygons outer; - diff(contour, outer_holes, outer); - assert(outer.size() == 1); - this->outer = outer.front(); - - this->graphs.resize(this->islands.size() + 1, NULL); - this->initialized = true; -} - -void -MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline) -{ - if (!this->initialized) this->initialize(); - - // Are both points in the same island? - int island_idx = -1; - for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) { - if (island->contains_point(from) && island->contains_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->contains_line(Line(from, to))) { - polyline->points.push_back(from); - polyline->points.push_back(to); - return; - } - island_idx = island - this->islands.begin(); - break; - } - } - - // Now check whether points are inside the environment. - Point inner_from = from; - Point inner_to = to; - bool from_is_inside, to_is_inside; - if (island_idx == -1) { - if (!(from_is_inside = this->outer.contains_point(from))) { - // Find the closest inner point to start from. - from.nearest_point(this->outer, &inner_from); - } - if (!(to_is_inside = this->outer.contains_point(to))) { - // Find the closest inner point to start from. - to.nearest_point(this->outer, &inner_to); - } - } else { - if (!(from_is_inside = this->inner[island_idx].contains_point(from))) { - // Find the closest inner point to start from. - from.nearest_point(this->inner[island_idx], &inner_from); - } - if (!(to_is_inside = this->inner[island_idx].contains_point(to))) { - // Find the closest inner point to start from. - to.nearest_point(this->inner[island_idx], &inner_to); - } - } - - // perform actual path search - MotionPlannerGraph* graph = this->init_graph(island_idx); - graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to), polyline); - - polyline->points.insert(polyline->points.begin(), from); - polyline->points.push_back(to); -} - -MotionPlannerGraph* -MotionPlanner::init_graph(int island_idx) -{ - if (this->graphs[island_idx + 1] == NULL) { - Polygons pp; - if (island_idx == -1) { - pp = this->outer; - } else { - pp = this->inner[island_idx]; - } - - MotionPlannerGraph* graph = this->graphs[island_idx + 1] = new MotionPlannerGraph(); - - // add polygon boundaries as edges - size_t node_idx = 0; - Lines lines; - for (Polygons::const_iterator polygon = pp.begin(); polygon != pp.end(); ++polygon) { - graph->nodes.push_back(polygon->points.back()); - node_idx++; - for (Points::const_iterator p = polygon->points.begin(); p != polygon->points.end(); ++p) { - graph->nodes.push_back(*p); - double dist = graph->nodes[node_idx-1].distance_to(*p); - graph->add_edge(node_idx-1, node_idx, dist); - graph->add_edge(node_idx, node_idx-1, dist); - node_idx++; - } - polygon->lines(&lines); - } - - // add Voronoi edges as internal edges - { - typedef voronoi_diagram VD; - typedef std::map t_vd_vertices; - VD vd; - t_vd_vertices vd_vertices; - - boost::polygon::construct_voronoi(lines.begin(), lines.end(), &vd); - for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) { - if (edge->is_infinite()) continue; - - const VD::vertex_type* v0 = edge->vertex0(); - const VD::vertex_type* v1 = edge->vertex1(); - Point p0 = Point(v0->x(), v0->y()); - Point p1 = Point(v1->x(), v1->y()); - // contains_point() should probably be faster than contains_line(), - // and should it fail on any boundary points it's not a big problem - if (island_idx == -1) { - if (!this->outer.contains_point(p0) || !this->outer.contains_point(p1)) continue; - } else { - if (!this->inner[island_idx].contains_point(p0) || !this->inner[island_idx].contains_point(p1)) continue; - } - - t_vd_vertices::const_iterator i_v0 = vd_vertices.find(v0); - size_t v0_idx; - if (i_v0 == vd_vertices.end()) { - graph->nodes.push_back(p0); - v0_idx = node_idx; - vd_vertices[v0] = node_idx; - node_idx++; - } else { - v0_idx = i_v0->second; - } - - t_vd_vertices::const_iterator i_v1 = vd_vertices.find(v1); - size_t v1_idx; - if (i_v1 == vd_vertices.end()) { - graph->nodes.push_back(p1); - v1_idx = node_idx; - vd_vertices[v1] = node_idx; - node_idx++; - } else { - v1_idx = i_v1->second; - } - - double dist = graph->nodes[v0_idx].distance_to(graph->nodes[v1_idx]); - graph->add_edge(v0_idx, v1_idx, dist); - } - } - - return graph; - } - return this->graphs[island_idx + 1]; -} - -void -MotionPlannerGraph::add_edge(size_t from, size_t to, double weight) -{ - // extend adjacency list until this start node - if (this->adjacency_list.size() < from+1) - this->adjacency_list.resize(from+1); - - this->adjacency_list[from].push_back(neighbor(to, weight)); -} - -size_t -MotionPlannerGraph::find_node(const Point &point) const -{ - /* - for (Points::const_iterator p = this->nodes.begin(); p != this->nodes.end(); ++p) { - if (p->coincides_with(point)) return p - this->nodes.begin(); - } - */ - return point.nearest_point_index(this->nodes); -} - -void -MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline) -{ - const weight_t max_weight = std::numeric_limits::infinity(); - - std::vector min_distance; - std::vector previous; - { - int n = this->adjacency_list.size(); - min_distance.clear(); - min_distance.resize(n, max_weight); - min_distance[from] = 0; - previous.clear(); - previous.resize(n, -1); - std::set > vertex_queue; - vertex_queue.insert(std::make_pair(min_distance[from], from)); - - while (!vertex_queue.empty()) - { - weight_t dist = vertex_queue.begin()->first; - node_t u = vertex_queue.begin()->second; - vertex_queue.erase(vertex_queue.begin()); - - // Visit each edge exiting u - const std::vector &neighbors = this->adjacency_list[u]; - for (std::vector::const_iterator neighbor_iter = neighbors.begin(); - neighbor_iter != neighbors.end(); - neighbor_iter++) - { - node_t v = neighbor_iter->target; - weight_t weight = neighbor_iter->weight; - weight_t distance_through_u = dist + weight; - if (distance_through_u < min_distance[v]) { - vertex_queue.erase(std::make_pair(min_distance[v], v)); - min_distance[v] = distance_through_u; - previous[v] = u; - vertex_queue.insert(std::make_pair(min_distance[v], v)); - } - - } - } - } - - for (node_t vertex = to; vertex != -1; vertex = previous[vertex]) - polyline->points.push_back(this->nodes[vertex]); - polyline->reverse(); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(MotionPlanner, "MotionPlanner"); -#endif - -} diff --git a/xs/src/MotionPlanner.hpp b/xs/src/MotionPlanner.hpp deleted file mode 100644 index 78fd5c72b..000000000 --- a/xs/src/MotionPlanner.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef slic3r_MotionPlanner_hpp_ -#define slic3r_MotionPlanner_hpp_ - -#include -#include "ClipperUtils.hpp" -#include "ExPolygonCollection.hpp" -#include "Polyline.hpp" -#include -#include -#include - -#define MP_INNER_MARGIN scale_(1.0) -#define MP_OUTER_MARGIN scale_(2.0) - -namespace Slic3r { - -class MotionPlannerGraph; - -class MotionPlanner -{ - public: - MotionPlanner(const ExPolygons &islands); - ~MotionPlanner(); - void shortest_path(const Point &from, const Point &to, Polyline* polyline); - - private: - ExPolygons islands; - bool initialized; - ExPolygon outer; - ExPolygonCollections inner; - std::vector graphs; - - void initialize(); - MotionPlannerGraph* init_graph(int island_idx); -}; - -class MotionPlannerGraph -{ - private: - typedef size_t node_t; - typedef double weight_t; - struct neighbor { - node_t target; - weight_t weight; - neighbor(node_t arg_target, weight_t arg_weight) - : target(arg_target), weight(arg_weight) { } - }; - typedef std::vector< std::vector > adjacency_list_t; - adjacency_list_t adjacency_list; - - public: - Points nodes; - //std::map, double> edges; - void add_edge(size_t from, size_t to, double weight); - size_t find_node(const Point &point) const; - void shortest_path(size_t from, size_t to, Polyline* polyline); -}; - -} - -#endif diff --git a/xs/src/MultiPoint.cpp b/xs/src/MultiPoint.cpp deleted file mode 100644 index 5da3cb499..000000000 --- a/xs/src/MultiPoint.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "MultiPoint.hpp" -#include "BoundingBox.hpp" - -namespace Slic3r { - -MultiPoint::operator Points() const -{ - return this->points; -} - -void -MultiPoint::scale(double factor) -{ - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).scale(factor); - } -} - -void -MultiPoint::translate(double x, double y) -{ - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).translate(x, y); - } -} - -void -MultiPoint::rotate(double angle, const Point ¢er) -{ - for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).rotate(angle, center); - } -} - -void -MultiPoint::reverse() -{ - std::reverse(this->points.begin(), this->points.end()); -} - -Point -MultiPoint::first_point() const -{ - return this->points.front(); -} - -double -MultiPoint::length() const -{ - Lines lines = this->lines(); - double len = 0; - for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) { - len += it->length(); - } - return len; -} - -bool -MultiPoint::is_valid() const -{ - return this->points.size() >= 2; -} - -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(); - } - return -1; // not found -} - -void -MultiPoint::bounding_box(BoundingBox* bb) const -{ - *bb = BoundingBox(this->points); -} - -Points -MultiPoint::_douglas_peucker(const Points &points, const double tolerance) -{ - Points results; - double dmax = 0; - size_t index = 0; - Line full(points.front(), points.back()); - for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) { - double d = it->distance_to(full); - if (d > dmax) { - index = it - points.begin(); - dmax = d; - } - } - if (dmax >= tolerance) { - Points dp0; - dp0.reserve(index + 1); - dp0.insert(dp0.end(), points.begin(), points.begin() + index + 1); - Points dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); - results.reserve(results.size() + dp1.size() - 1); - results.insert(results.end(), dp1.begin(), dp1.end() - 1); - - dp0.clear(); - dp0.reserve(points.size() - index + 1); - dp0.insert(dp0.end(), points.begin() + index, points.end()); - dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); - results.reserve(results.size() + dp1.size()); - results.insert(results.end(), dp1.begin(), dp1.end()); - } else { - results.push_back(points.front()); - results.push_back(points.back()); - } - return results; -} - -#ifdef SLIC3RXS -void -MultiPoint::from_SV(SV* poly_sv) -{ - AV* poly_av = (AV*)SvRV(poly_sv); - const unsigned int num_points = av_len(poly_av)+1; - this->points.resize(num_points); - - for (unsigned int i = 0; i < num_points; i++) { - SV** point_sv = av_fetch(poly_av, i, 0); - this->points[i].from_SV_check(*point_sv); - } -} - -void -MultiPoint::from_SV_check(SV* poly_sv) -{ - if (sv_isobject(poly_sv) && (SvTYPE(SvRV(poly_sv)) == SVt_PVMG)) { - *this = *(MultiPoint*)SvIV((SV*)SvRV( poly_sv )); - } else { - this->from_SV(poly_sv); - } -} - -SV* -MultiPoint::to_AV() { - const unsigned int num_points = this->points.size(); - AV* av = newAV(); - if (num_points > 0) av_extend(av, num_points-1); - for (unsigned int i = 0; i < num_points; i++) { - av_store(av, i, perl_to_SV_ref(this->points[i])); - } - return newRV_noinc((SV*)av); -} - -SV* -MultiPoint::to_SV_pureperl() const { - const unsigned int num_points = this->points.size(); - AV* av = newAV(); - if (num_points > 0) av_extend(av, num_points-1); - for (unsigned int i = 0; i < num_points; i++) { - av_store(av, i, this->points[i].to_SV_pureperl()); - } - return newRV_noinc((SV*)av); -} -#endif - -} diff --git a/xs/src/MultiPoint.hpp b/xs/src/MultiPoint.hpp deleted file mode 100644 index 075b0dbdb..000000000 --- a/xs/src/MultiPoint.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef slic3r_MultiPoint_hpp_ -#define slic3r_MultiPoint_hpp_ - -#include -#include -#include -#include "Line.hpp" -#include "Point.hpp" - -namespace Slic3r { - -class BoundingBox; - -class MultiPoint -{ - public: - Points points; - - operator Points() const; - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Point ¢er); - void reverse(); - Point first_point() const; - virtual Point last_point() const = 0; - virtual Lines lines() const = 0; - double length() const; - bool is_valid() const; - int find_point(const Point &point) const; - void bounding_box(BoundingBox* bb) const; - - static Points _douglas_peucker(const Points &points, const double tolerance); - - #ifdef SLIC3RXS - void from_SV(SV* poly_sv); - void from_SV_check(SV* poly_sv); - SV* to_AV(); - SV* to_SV_pureperl() const; - #endif -}; - -} - -#endif diff --git a/xs/src/PlaceholderParser.cpp b/xs/src/PlaceholderParser.cpp deleted file mode 100644 index c384afa32..000000000 --- a/xs/src/PlaceholderParser.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "PlaceholderParser.hpp" - - -namespace Slic3r { - - -PlaceholderParser::PlaceholderParser() -{ - // TODO: port these methods to C++, then call them here - // this->apply_env_variables(); - // this->update_timestamp(); -} - -PlaceholderParser::~PlaceholderParser() -{ -} - -void PlaceholderParser::apply_config(DynamicPrintConfig &config) -{ - // options that are set and aren't text-boxes - t_config_option_keys opt_keys; - for (t_optiondef_map::iterator i = config.def->begin(); - i != config.def->end(); ++i) - { - const t_config_option_key &key = i->first; - const ConfigOptionDef &def = i->second; - - if (config.has(key) && !def.multiline) { - opt_keys.push_back(key); - } - } - - for (t_config_option_keys::iterator i = opt_keys.begin(); - i != opt_keys.end(); ++i) - { - const t_config_option_key &key = *i; - - // set placeholders for options with multiple values - const ConfigOptionDef &def = (*config.def)[key]; - switch (def.type) { - case coFloats: - this->set_multiple_from_vector(key, - *(ConfigOptionFloats*)config.option(key)); - break; - - case coInts: - this->set_multiple_from_vector(key, - *(ConfigOptionInts*)config.option(key)); - break; - - case coStrings: - this->set_multiple_from_vector(key, - *(ConfigOptionStrings*)config.option(key)); - break; - - case coPoints: - this->set_multiple_from_vector(key, - *(ConfigOptionPoints*)config.option(key)); - break; - - case coBools: - this->set_multiple_from_vector(key, - *(ConfigOptionBools*)config.option(key)); - break; - - case coPoint: - { - const ConfigOptionPoint &opt = - *(ConfigOptionPoint*)config.option(key); - - this->_single[key] = opt.serialize(); - - Pointf val = opt; - this->_multiple[key + "_X"] = val.x; - this->_multiple[key + "_Y"] = val.y; - } - - break; - - default: - // set single-value placeholders - this->_single[key] = config.serialize(key); - break; - } - } -} - -std::ostream& operator<<(std::ostream &stm, const Pointf &pointf) -{ - return stm << pointf.x << "," << pointf.y; -} - -template -void PlaceholderParser::set_multiple_from_vector(const std::string &key, - ConfigOptionVector &opt) -{ - const std::vector &vals = opt.values; - - for (size_t i = 0; i < vals.size(); ++i) { - std::stringstream multikey_stm; - multikey_stm << key << "_" << i; - - std::stringstream val_stm; - val_stm << vals[i]; - - this->_multiple[multikey_stm.str()] = val_stm.str(); - } - - if (vals.size() > 0) { - std::stringstream val_stm; - val_stm << vals[0]; - this->_multiple[key] = val_stm.str(); - } -} - -#ifdef SLIC3RXS -REGISTER_CLASS(PlaceholderParser, "GCode::PlaceholderParser"); -#endif - -} diff --git a/xs/src/PlaceholderParser.hpp b/xs/src/PlaceholderParser.hpp deleted file mode 100644 index e69d6ed93..000000000 --- a/xs/src/PlaceholderParser.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef slic3r_PlaceholderParser_hpp_ -#define slic3r_PlaceholderParser_hpp_ - - -#include -#include -#include -#include "PrintConfig.hpp" - - -namespace Slic3r { - -class PlaceholderParser -{ - public: - std::map _single; - std::map _multiple; - - PlaceholderParser(); - ~PlaceholderParser(); - - void apply_config(DynamicPrintConfig &config); - - private: - template - void set_multiple_from_vector( - const std::string &key, ConfigOptionVector &opt); -}; - -} - -#endif diff --git a/xs/src/Point.cpp b/xs/src/Point.cpp deleted file mode 100644 index 2515d41a6..000000000 --- a/xs/src/Point.cpp +++ /dev/null @@ -1,337 +0,0 @@ -#include "Point.hpp" -#include "Line.hpp" -#include "MultiPoint.hpp" -#include -#include - -namespace Slic3r { - -Point::Point(double x, double y) -{ - this->x = lrint(x); - this->y = lrint(y); -} - -bool -Point::operator==(const Point& rhs) const -{ - return this->coincides_with(rhs); -} - -std::string -Point::wkt() const -{ - std::ostringstream ss; - ss << "POINT(" << this->x << " " << this->y << ")"; - return ss.str(); -} - -void -Point::scale(double factor) -{ - this->x *= factor; - this->y *= factor; -} - -void -Point::translate(double x, double y) -{ - this->x += x; - this->y += y; -} - -void -Point::rotate(double angle, const Point ¢er) -{ - double cur_x = (double)this->x; - double cur_y = (double)this->y; - this->x = (coord_t)round( (double)center.x + cos(angle) * (cur_x - (double)center.x) - sin(angle) * (cur_y - (double)center.y) ); - this->y = (coord_t)round( (double)center.y + cos(angle) * (cur_y - (double)center.y) + sin(angle) * (cur_x - (double)center.x) ); -} - -bool -Point::coincides_with(const Point &point) const -{ - return this->x == point.x && this->y == point.y; -} - -bool -Point::coincides_with_epsilon(const Point &point) const -{ - return std::abs(this->x - point.x) < SCALED_EPSILON && std::abs(this->y - point.y) < SCALED_EPSILON; -} - -int -Point::nearest_point_index(const Points &points) const -{ - PointConstPtrs p; - p.reserve(points.size()); - for (Points::const_iterator it = points.begin(); it != points.end(); ++it) - p.push_back(&*it); - return this->nearest_point_index(p); -} - -int -Point::nearest_point_index(const PointConstPtrs &points) const -{ - int idx = -1; - double distance = -1; // double because long is limited to 2147483647 on some platforms and it's not enough - - 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 = pow(this->x - (*it)->x, 2); - 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 += pow(this->y - (*it)->y, 2); - if (distance != -1 && d > distance) continue; - - idx = it - points.begin(); - distance = d; - - if (distance < EPSILON) break; - } - - return idx; -} - -int -Point::nearest_point_index(const PointPtrs &points) const -{ - PointConstPtrs p; - p.reserve(points.size()); - for (PointPtrs::const_iterator it = points.begin(); it != points.end(); ++it) - p.push_back(*it); - return this->nearest_point_index(p); -} - -void -Point::nearest_point(const Points &points, Point* point) const -{ - *point = points.at(this->nearest_point_index(points)); -} - -double -Point::distance_to(const Point &point) const -{ - double dx = ((double)point.x - this->x); - double dy = ((double)point.y - this->y); - return sqrt(dx*dx + dy*dy); -} - -double -Point::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. - * In other words it is the 2D cross product of p1-p2 and p1-this, i.e. - * 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 -{ - return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x); -} - -double -Point::ccw(const Line &line) const -{ - return this->ccw(line.a, line.b); -} - -Point -Point::projection_onto(const MultiPoint &poly) const -{ - Point running_projection = poly.first_point(); - double running_min = this->distance_to(running_projection); - - 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) { - running_projection = point_temp; - running_min = this->distance_to(running_projection); - } - } - return running_projection; -} - -Point -Point::projection_onto(const Line &line) const -{ - if (line.a.coincides_with(line.b)) return line.a; - - /* - (Ported from VisiLibity by Karl J. Obermeyer) - The projection of point_temp onto the line determined by - line_segment_temp can be represented as an affine combination - expressed in the form projection of - Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second. - If theta is outside the interval [0,1], then one of the Line_Segment's endpoints - must be closest to calling Point. - */ - double theta = ( (double)(line.b.x - this->x)*(double)(line.b.x - line.a.x) + (double)(line.b.y- this->y)*(double)(line.b.y - line.a.y) ) - / ( (double)pow(line.b.x - line.a.x, 2) + (double)pow(line.b.y - line.a.y, 2) ); - - if (0.0 <= theta && theta <= 1.0) - return theta * line.a + (1.0-theta) * line.b; - - // 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); -} - -Point -operator+(const Point& point1, const Point& point2) -{ - return Point(point1.x + point2.x, point1.y + point2.y); -} - -Point -operator*(double scalar, const Point& point2) -{ - return Point(scalar * point2.x, scalar * point2.y); -} - -#ifdef SLIC3RXS - -REGISTER_CLASS(Point, "Point"); - -SV* -Point::to_SV_pureperl() const { - AV* av = newAV(); - av_fill(av, 1); - av_store(av, 0, newSViv(this->x)); - av_store(av, 1, newSViv(this->y)); - return newRV_noinc((SV*)av); -} - -void -Point::from_SV(SV* point_sv) -{ - AV* point_av = (AV*)SvRV(point_sv); - // get a double from Perl and round it, otherwise - // it would get truncated - this->x = lrint(SvNV(*av_fetch(point_av, 0, 0))); - this->y = lrint(SvNV(*av_fetch(point_av, 1, 0))); -} - -void -Point::from_SV_check(SV* point_sv) -{ - if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) { - if (!sv_isa(point_sv, perl_class_name(this)) && !sv_isa(point_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object (got %s)", perl_class_name(this), HvNAME(SvSTASH(SvRV(point_sv)))); - *this = *(Point*)SvIV((SV*)SvRV( point_sv )); - } else { - this->from_SV(point_sv); - } -} - - -REGISTER_CLASS(Point3, "Point3"); - -#endif - -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::rotate(double angle, const Pointf ¢er) -{ - double cur_x = this->x; - double cur_y = this->y; - this->x = center.x + cos(angle) * (cur_x - center.x) - sin(angle) * (cur_y - center.y); - this->y = center.y + cos(angle) * (cur_y - center.y) + sin(angle) * (cur_x - center.x); -} - -#ifdef SLIC3RXS - -REGISTER_CLASS(Pointf, "Pointf"); - -SV* -Pointf::to_SV_pureperl() const { - AV* av = newAV(); - av_fill(av, 1); - av_store(av, 0, newSVnv(this->x)); - av_store(av, 1, newSVnv(this->y)); - return newRV_noinc((SV*)av); -} - -bool -Pointf::from_SV(SV* point_sv) -{ - AV* point_av = (AV*)SvRV(point_sv); - SV* sv_x = *av_fetch(point_av, 0, 0); - SV* sv_y = *av_fetch(point_av, 1, 0); - if (!looks_like_number(sv_x) || !looks_like_number(sv_y)) return false; - - this->x = SvNV(sv_x); - this->y = SvNV(sv_y); - return true; -} - -void -Pointf::from_SV_check(SV* point_sv) -{ - if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) { - if (!sv_isa(point_sv, perl_class_name(this)) && !sv_isa(point_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object (got %s)", perl_class_name(this), HvNAME(SvSTASH(SvRV(point_sv)))); - *this = *(Pointf*)SvIV((SV*)SvRV( point_sv )); - } else { - this->from_SV(point_sv); - } -} -#endif - -void -Pointf3::scale(double factor) -{ - Pointf::scale(factor); - this->z *= factor; -} - -void -Pointf3::translate(double x, double y, double z) -{ - Pointf::translate(x, y); - this->z += z; -} - -#ifdef SLIC3RXS -REGISTER_CLASS(Pointf3, "Pointf3"); -#endif - -} diff --git a/xs/src/Point.hpp b/xs/src/Point.hpp deleted file mode 100644 index 28d216df2..000000000 --- a/xs/src/Point.hpp +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef slic3r_Point_hpp_ -#define slic3r_Point_hpp_ - -#include -#include -#include -#include - -namespace Slic3r { - -class Line; -class MultiPoint; -class Point; -class Pointf; -typedef Point Vector; -typedef std::vector Points; -typedef std::vector PointPtrs; -typedef std::vector PointConstPtrs; -typedef std::vector Pointfs; - -class Point -{ - public: - coord_t x; - coord_t y; - Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {}; - Point(int _x, int _y): x(_x), y(_y) {}; - Point(long long _x, long long _y): x(_x), y(_y) {}; // for Clipper - Point(double x, double y); - bool operator==(const Point& rhs) const; - std::string wkt() const; - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Point ¢er); - bool coincides_with(const Point &point) const; - 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; - void nearest_point(const Points &points, Point* point) const; - double distance_to(const Point &point) const; - double distance_to(const Line &line) const; - double ccw(const Point &p1, const Point &p2) const; - double ccw(const Line &line) const; - Point projection_onto(const MultiPoint &poly) const; - Point projection_onto(const Line &line) const; - Point negative() const; - - #ifdef SLIC3RXS - void from_SV(SV* point_sv); - void from_SV_check(SV* point_sv); - SV* to_SV_pureperl() const; - #endif -}; - -Point operator+(const Point& point1, const Point& point2); -Point operator*(double scalar, const Point& point2); - -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) {}; -}; - -class Pointf -{ - public: - coordf_t x; - coordf_t y; - explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {}; - void scale(double factor); - void translate(double x, double y); - void rotate(double angle, const Pointf ¢er); - - #ifdef SLIC3RXS - bool from_SV(SV* point_sv); - void from_SV_check(SV* point_sv); - SV* to_SV_pureperl() const; - #endif -}; - -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) {}; - void scale(double factor); - void translate(double x, double y, double z); -}; - -} - -// start Boost -#include -namespace boost { namespace polygon { - template <> - struct geometry_concept { typedef coordinate_concept type; }; - - template <> - struct coordinate_traits { - typedef coord_t coordinate_type; - typedef long double area_type; - typedef long long manhattan_area_type; - typedef unsigned long long unsigned_area_type; - typedef long long coordinate_difference; - typedef long double coordinate_distance; - }; - - template <> - struct geometry_concept { typedef point_concept type; }; - - template <> - struct point_traits { - typedef coord_t coordinate_type; - - static inline coordinate_type get(const Point& point, orientation_2d orient) { - return (orient == HORIZONTAL) ? point.x : point.y; - } - }; - - template <> - struct point_mutable_traits { - typedef coord_t coordinate_type; - static inline void set(Point& point, orientation_2d orient, coord_t value) { - if (orient == HORIZONTAL) - point.x = value; - else - point.y = value; - } - static inline Point construct(coord_t x_value, coord_t y_value) { - Point retval; - retval.x = x_value; - retval.y = y_value; - return retval; - } - }; -} } -// end Boost - -#endif diff --git a/xs/src/Polygon.cpp b/xs/src/Polygon.cpp deleted file mode 100644 index 3fe16bc70..000000000 --- a/xs/src/Polygon.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include -#include "ClipperUtils.hpp" -#include "Polygon.hpp" -#include "Polyline.hpp" - -namespace Slic3r { - -Polygon::operator Polygons() const -{ - Polygons pp; - pp.push_back(*this); - return pp; -} - -Polygon::operator Polyline() const -{ - Polyline polyline; - this->split_at_first_point(&polyline); - return polyline; -} - -Point& -Polygon::operator[](Points::size_type idx) -{ - return this->points[idx]; -} - -const Point& -Polygon::operator[](Points::size_type idx) const -{ - return this->points[idx]; -} - -Point -Polygon::last_point() const -{ - return this->points.front(); // last point == first point for polygons -} - -Lines -Polygon::lines() const -{ - Lines lines; - this->lines(&lines); - return lines; -} - -void -Polygon::lines(Lines* lines) const -{ - lines->reserve(lines->size() + this->points.size()); - for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) { - lines->push_back(Line(*it, *(it + 1))); - } - lines->push_back(Line(this->points.back(), this->points.front())); -} - -void -Polygon::split_at_vertex(const Point &point, Polyline* polyline) const -{ - // find index of point - for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { - if (it->coincides_with(point)) { - this->split_at_index(it - this->points.begin(), polyline); - return; - } - } - CONFESS("Point not found"); -} - -void -Polygon::split_at_index(int index, Polyline* polyline) const -{ - polyline->points.reserve(this->points.size() + 1); - for (Points::const_iterator it = this->points.begin() + index; it != this->points.end(); ++it) - polyline->points.push_back(*it); - for (Points::const_iterator it = this->points.begin(); it != this->points.begin() + index + 1; ++it) - polyline->points.push_back(*it); -} - -void -Polygon::split_at_first_point(Polyline* polyline) const -{ - this->split_at_index(0, polyline); -} - -void -Polygon::equally_spaced_points(double distance, Points* points) const -{ - Polyline polyline; - this->split_at_first_point(&polyline); - polyline.equally_spaced_points(distance, points); -} - -double -Polygon::area() const -{ - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*this, p); - return ClipperLib::Area(p); -} - -bool -Polygon::is_counter_clockwise() const -{ - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*this, p); - return ClipperLib::Orientation(p); -} - -bool -Polygon::is_clockwise() const -{ - return !this->is_counter_clockwise(); -} - -bool -Polygon::make_counter_clockwise() -{ - if (!this->is_counter_clockwise()) { - this->reverse(); - return true; - } - return false; -} - -bool -Polygon::make_clockwise() -{ - if (this->is_counter_clockwise()) { - this->reverse(); - return true; - } - return false; -} - -bool -Polygon::is_valid() const -{ - return this->points.size() >= 3; -} - -bool -Polygon::contains_point(const Point &point) const -{ - // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html - bool result = false; - Points::const_iterator i = this->points.begin(); - Points::const_iterator j = this->points.end() - 1; - for (; i != this->points.end(); j = i++) { - 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) ) - result = !result; - } - return result; -} - -Polygons -Polygon::simplify(double tolerance) const -{ - Polygon p = *this; - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - - Polygons pp; - pp.push_back(p); - simplify_polygons(pp, pp); - return pp; -} - -void -Polygon::simplify(double tolerance, Polygons &polygons) const -{ - Polygons pp = this->simplify(tolerance); - polygons.reserve(polygons.size() + pp.size()); - polygons.insert(polygons.end(), pp.begin(), pp.end()); -} - -// Only call this on convex polygons or it will return invalid results -void -Polygon::triangulate_convex(Polygons* polygons) const -{ - for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) { - Polygon p; - p.points.reserve(3); - p.points.push_back(this->points.front()); - p.points.push_back(*(it-1)); - p.points.push_back(*it); - - // this should be replaced with a more efficient call to a merge_collinear_segments() method - if (p.area() > 0) polygons->push_back(p); - } -} - -// center of mass -Point -Polygon::centroid() const -{ - double area_temp = this->area(); - double x_temp = 0; - double y_temp = 0; - - Polyline polyline; - this->split_at_first_point(&polyline); - 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 ); - } - - return Point(x_temp/(6*area_temp), y_temp/(6*area_temp)); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(Polygon, "Polygon"); - -void -Polygon::from_SV_check(SV* poly_sv) -{ - if (sv_isobject(poly_sv) && !sv_isa(poly_sv, perl_class_name(this)) && !sv_isa(poly_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object", perl_class_name(this)); - - MultiPoint::from_SV_check(poly_sv); -} -#endif - -} diff --git a/xs/src/Polygon.hpp b/xs/src/Polygon.hpp deleted file mode 100644 index 816b6be18..000000000 --- a/xs/src/Polygon.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#ifndef slic3r_Polygon_hpp_ -#define slic3r_Polygon_hpp_ - -#include -#include -#include "Line.hpp" -#include "MultiPoint.hpp" -#include "Polyline.hpp" - -namespace Slic3r { - -class Polygon; -typedef std::vector Polygons; - -class Polygon : public MultiPoint { - public: - operator Polygons() const; - operator Polyline() const; - Point& operator[](Points::size_type idx); - const Point& operator[](Points::size_type idx) const; - Point last_point() const; - Lines lines() const; - void lines(Lines* lines) const; - void split_at_vertex(const Point &point, Polyline* polyline) const; - void split_at_index(int index, Polyline* polyline) const; - void split_at_first_point(Polyline* polyline) const; - void equally_spaced_points(double distance, Points* points) const; - double area() const; - bool is_counter_clockwise() const; - bool is_clockwise() const; - bool make_counter_clockwise(); - bool make_clockwise(); - bool is_valid() const; - bool contains_point(const Point &point) const; - Polygons simplify(double tolerance) const; - void simplify(double tolerance, Polygons &polygons) const; - void triangulate_convex(Polygons* polygons) const; - Point centroid() const; - - #ifdef SLIC3RXS - void from_SV_check(SV* poly_sv); - #endif -}; - -} - -// start Boost -#include -namespace boost { namespace polygon { - template <> - struct geometry_concept{ typedef polygon_concept type; }; - - template <> - struct polygon_traits { - typedef coord_t coordinate_type; - typedef Points::const_iterator iterator_type; - typedef Point point_type; - - // Get the begin iterator - static inline iterator_type begin_points(const Polygon& t) { - return t.points.begin(); - } - - // Get the end iterator - static inline iterator_type end_points(const Polygon& t) { - return t.points.end(); - } - - // Get the number of sides of the polygon - static inline std::size_t size(const Polygon& t) { - return t.points.size(); - } - - // Get the winding direction of the polygon - static inline winding_direction winding(const Polygon& t) { - return unknown_winding; - } - }; - - template <> - struct polygon_mutable_traits { - // expects stl style iterators - template - static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) { - polygon.points.clear(); - while (input_begin != input_end) { - polygon.points.push_back(Point()); - boost::polygon::assign(polygon.points.back(), *input_begin); - ++input_begin; - } - // skip last point since Boost will set last point = first point - polygon.points.pop_back(); - return polygon; - } - }; - - template <> - struct geometry_concept { typedef polygon_set_concept type; }; - - //next we map to the concept through traits - template <> - struct polygon_set_traits { - typedef coord_t coordinate_type; - typedef Polygons::const_iterator iterator_type; - typedef Polygons operator_arg_type; - - static inline iterator_type begin(const Polygons& polygon_set) { - return polygon_set.begin(); - } - - static inline iterator_type end(const Polygons& polygon_set) { - return polygon_set.end(); - } - - //don't worry about these, just return false from them - static inline bool clean(const Polygons& polygon_set) { return false; } - static inline bool sorted(const Polygons& polygon_set) { return false; } - }; - - template <> - struct polygon_set_mutable_traits { - template - static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) { - polygons.assign(input_begin, input_end); - } - }; -} } -// end Boost - -#endif diff --git a/xs/src/Polyline.cpp b/xs/src/Polyline.cpp deleted file mode 100644 index 724ae4740..000000000 --- a/xs/src/Polyline.cpp +++ /dev/null @@ -1,172 +0,0 @@ -#include "Polyline.hpp" -#include "Polygon.hpp" - -namespace Slic3r { - -Polyline::operator Polylines() const -{ - Polylines polylines; - polylines.push_back(*this); - return polylines; -} - -Point -Polyline::last_point() const -{ - return this->points.back(); -} - -Point -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; - } - return p; -} - -Lines -Polyline::lines() const -{ - Lines lines; - if (this->points.size() >= 2) { - lines.reserve(this->points.size() - 1); - for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) { - lines.push_back(Line(*it, *(it + 1))); - } - } - return lines; -} - -// removes the given distance from the end of the polyline -void -Polyline::clip_end(double distance) -{ - while (distance > 0) { - Point last_point = this->last_point(); - 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; - } - - Line segment(last_point, this->last_point()); - this->points.push_back(segment.point_at(distance)); - distance = 0; - } -} - -// removes the given distance from the start of the polyline -void -Polyline::clip_start(double distance) -{ - this->reverse(); - this->clip_end(distance); - if (this->points.size() >= 2) this->reverse(); -} - -void -Polyline::extend_end(double distance) -{ - // relocate last point by extending the last segment by the specified length - Line line(this->points[ this->points.size()-2 ], this->points.back()); - this->points.pop_back(); - this->points.push_back(line.point_at(line.length() + distance)); -} - -void -Polyline::extend_start(double distance) -{ - // relocate first point by extending the first segment by the specified length - Line line(this->points[1], this->points.front()); - this->points[0] = line.point_at(line.length() + distance); -} - -/* this method returns a collection of points picked on the polygon contour - so that they are evenly spaced according to the input distance */ -void -Polyline::equally_spaced_points(double distance, Points* points) const -{ - points->push_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)); - len += segment_length; - if (len < distance) continue; - - if (len == distance) { - points->push_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; - } -} - -void -Polyline::simplify(double tolerance) -{ - this->points = MultiPoint::_douglas_peucker(this->points, tolerance); -} - -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); - 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) { - p = p_tmp; - min = point.distance_to(p); - 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); - } - // 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); - - // create second half - p2->points.clear(); - p2->points.push_back(point); - for (Lines::const_iterator line = lines.begin() + line_idx; line != lines.end(); ++line) { - if (!line->b.coincides_with(p)) p2->points.push_back(line->b); - } -} - - -#ifdef SLIC3RXS -REGISTER_CLASS(Polyline, "Polyline"); - -void -Polyline::from_SV_check(SV* poly_sv) -{ - if (!sv_isa(poly_sv, perl_class_name(this)) && !sv_isa(poly_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object",perl_class_name(this)); - - MultiPoint::from_SV_check(poly_sv); -} -#endif - -} diff --git a/xs/src/Polyline.hpp b/xs/src/Polyline.hpp deleted file mode 100644 index 5462425cf..000000000 --- a/xs/src/Polyline.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef slic3r_Polyline_hpp_ -#define slic3r_Polyline_hpp_ - -#include "Line.hpp" -#include "MultiPoint.hpp" - -namespace Slic3r { - -class Polyline; -typedef std::vector Polylines; - -class Polyline : public MultiPoint { - public: - operator Polylines() const; - Point last_point() const; - Point leftmost_point() const; - Lines lines() const; - void clip_end(double distance); - void clip_start(double distance); - void extend_end(double distance); - void extend_start(double distance); - void equally_spaced_points(double distance, Points* points) const; - void simplify(double tolerance); - void split_at(const Point &point, Polyline* p1, Polyline* p2) const; - - #ifdef SLIC3RXS - void from_SV_check(SV* poly_sv); - #endif -}; - -} - -#endif diff --git a/xs/src/PolylineCollection.cpp b/xs/src/PolylineCollection.cpp deleted file mode 100644 index c2a142cf3..000000000 --- a/xs/src/PolylineCollection.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "PolylineCollection.hpp" - -namespace Slic3r { - -void -PolylineCollection::chained_path(PolylineCollection* retval, bool no_reverse) const -{ - if (this->polylines.empty()) return; - this->chained_path_from(this->polylines.front().first_point(), retval, no_reverse); -} - -void -PolylineCollection::chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse) const -{ - Polylines my_paths = this->polylines; - - Points endpoints; - for (Polylines::const_iterator it = my_paths.begin(); it != my_paths.end(); ++it) { - endpoints.push_back(it->first_point()); - if (no_reverse) { - endpoints.push_back(it->first_point()); - } else { - endpoints.push_back(it->last_point()); - } - } - - while (!my_paths.empty()) { - // find nearest point - int start_index = start_near.nearest_point_index(endpoints); - int path_index = start_index/2; - if (start_index % 2 && !no_reverse) { - my_paths.at(path_index).reverse(); - } - retval->polylines.push_back(my_paths.at(path_index)); - my_paths.erase(my_paths.begin() + path_index); - endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); - start_near = retval->polylines.back().last_point(); - } -} - -Point -PolylineCollection::leftmost_point() const -{ - if (this->polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection"); - Point p = this->polylines.front().leftmost_point(); - for (Polylines::const_iterator it = this->polylines.begin() + 1; it != this->polylines.end(); ++it) { - Point p2 = it->leftmost_point(); - if (p2.x < p.x) p = p2; - } - return p; -} - -#ifdef SLIC3RXS -REGISTER_CLASS(PolylineCollection, "Polyline::Collection"); -#endif - -} diff --git a/xs/src/PolylineCollection.hpp b/xs/src/PolylineCollection.hpp deleted file mode 100644 index ace03ad37..000000000 --- a/xs/src/PolylineCollection.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef slic3r_PolylineCollection_hpp_ -#define slic3r_PolylineCollection_hpp_ - -#include -#include "Polyline.hpp" - -namespace Slic3r { - -class PolylineCollection -{ - public: - Polylines polylines; - void chained_path(PolylineCollection* retval, bool no_reverse = false) const; - void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const; - Point leftmost_point() const; -}; - -} - -#endif diff --git a/xs/src/Print.cpp b/xs/src/Print.cpp deleted file mode 100644 index d71cb4349..000000000 --- a/xs/src/Print.cpp +++ /dev/null @@ -1,345 +0,0 @@ -#include "Print.hpp" -#include "BoundingBox.hpp" -#include - -namespace Slic3r { - -template -bool -PrintState::is_started(StepClass step) const -{ - return this->started.find(step) != this->started.end(); -} - -template -bool -PrintState::is_done(StepClass step) const -{ - return this->done.find(step) != this->done.end(); -} - -template -void -PrintState::set_started(StepClass step) -{ - this->started.insert(step); -} - -template -void -PrintState::set_done(StepClass step) -{ - this->done.insert(step); -} - -template -bool -PrintState::invalidate(StepClass step) -{ - bool invalidated = this->started.erase(step) > 0; - this->done.erase(step); - return invalidated; -} - -template class PrintState; -template class PrintState; - - -Print::Print() -: total_used_filament(0), - total_extruded_volume(0) -{ -} - -Print::~Print() -{ - clear_objects(); - clear_regions(); -} - -void -Print::clear_objects() -{ - for (int i = this->objects.size()-1; i >= 0; --i) - this->delete_object(i); - - this->clear_regions(); -} - -PrintObject* -Print::get_object(size_t idx) -{ - return objects.at(idx); -} - -PrintObject* -Print::add_object(ModelObject *model_object, const BoundingBoxf3 &modobj_bbox) -{ - PrintObject *object = new PrintObject(this, model_object, modobj_bbox); - objects.push_back(object); - - // invalidate steps - this->invalidate_step(psSkirt); - this->invalidate_step(psBrim); - - return object; -} - -PrintObject* -Print::set_new_object(size_t idx, ModelObject *model_object, const BoundingBoxf3 &modobj_bbox) -{ - if (idx >= this->objects.size()) throw "bad idx"; - - PrintObjectPtrs::iterator old_it = this->objects.begin() + idx; - // before deleting object, invalidate all of its steps in order to - // invalidate all of the dependent ones in Print - (*old_it)->invalidate_all_steps(); - delete *old_it; - - PrintObject *object = new PrintObject(this, model_object, modobj_bbox); - this->objects[idx] = object; - return object; -} - -void -Print::delete_object(size_t idx) -{ - PrintObjectPtrs::iterator i = this->objects.begin() + idx; - delete *i; - this->objects.erase(i); - - // TODO: purge unused regions - - this->state.invalidate(psSkirt); - this->state.invalidate(psBrim); -} - -void -Print::clear_regions() -{ - for (int i = this->regions.size()-1; i >= 0; --i) - this->delete_region(i); -} - -PrintRegion* -Print::get_region(size_t idx) -{ - return regions.at(idx); -} - -PrintRegion* -Print::add_region() -{ - PrintRegion *region = new PrintRegion(this); - regions.push_back(region); - return region; -} - -void -Print::delete_region(size_t idx) -{ - PrintRegionPtrs::iterator i = this->regions.begin() + idx; - delete *i; - this->regions.erase(i); -} - -bool -Print::invalidate_state_by_config_options(const std::vector &opt_keys) -{ - std::set steps; - - // this method only accepts PrintConfig option keys - for (std::vector::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) { - if (*opt_key == "skirts" - || *opt_key == "skirt_height" - || *opt_key == "skirt_distance" - || *opt_key == "min_skirt_length") { - steps.insert(psSkirt); - } else if (*opt_key == "brim_width") { - steps.insert(psBrim); - steps.insert(psSkirt); - } else if (*opt_key == "nozzle_diameter") { - steps.insert(psInitExtruders); - } else if (*opt_key == "avoid_crossing_perimeters" - || *opt_key == "bed_shape" - || *opt_key == "bed_temperature" - || *opt_key == "bridge_acceleration" - || *opt_key == "bridge_fan_speed" - || *opt_key == "complete_objects" - || *opt_key == "cooling" - || *opt_key == "default_acceleration" - || *opt_key == "disable_fan_first_layers" - || *opt_key == "duplicate_distance" - || *opt_key == "end_gcode" - || *opt_key == "extruder_clearance_height" - || *opt_key == "extruder_clearance_radius" - || *opt_key == "extruder_offset" - || *opt_key == "extrusion_axis" - || *opt_key == "extrusion_multiplier" - || *opt_key == "fan_always_on" - || *opt_key == "fan_below_layer_time" - || *opt_key == "filament_diameter" - || *opt_key == "first_layer_acceleration" - || *opt_key == "first_layer_bed_temperature" - || *opt_key == "first_layer_speed" - || *opt_key == "first_layer_temperature" - || *opt_key == "g0" - || *opt_key == "gcode_arcs" - || *opt_key == "gcode_comments" - || *opt_key == "gcode_flavor" - || *opt_key == "infill_acceleration" - || *opt_key == "infill_first" - || *opt_key == "layer_gcode" - || *opt_key == "min_fan_speed" - || *opt_key == "max_fan_speed" - || *opt_key == "min_print_speed" - || *opt_key == "notes" - || *opt_key == "only_retract_when_crossing_perimeters" - || *opt_key == "output_filename_format" - || *opt_key == "perimeter_acceleration" - || *opt_key == "post_process" - || *opt_key == "retract_before_travel" - || *opt_key == "retract_layer_change" - || *opt_key == "retract_length" - || *opt_key == "retract_length_toolchange" - || *opt_key == "retract_lift" - || *opt_key == "retract_restart_extra" - || *opt_key == "retract_restart_extra_toolchange" - || *opt_key == "retract_speed" - || *opt_key == "slowdown_below_layer_time" - || *opt_key == "spiral_vase" - || *opt_key == "standby_temperature_delta" - || *opt_key == "start_gcode" - || *opt_key == "temperature" - || *opt_key == "threads" - || *opt_key == "toolchange_gcode" - || *opt_key == "travel_speed" - || *opt_key == "use_firmware_retraction" - || *opt_key == "use_relative_e_distances" - || *opt_key == "vibration_limit" - || *opt_key == "wipe" - || *opt_key == "z_offset") { - // 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 - return this->invalidate_all_steps(); - } - } - - bool invalidated = false; - for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { - if (this->invalidate_step(*step)) invalidated = true; - } - - return invalidated; -} - -bool -Print::invalidate_step(PrintStep step) -{ - bool invalidated = this->state.invalidate(step); - - // propagate to dependent steps - if (step == psSkirt) { - this->invalidate_step(psBrim); - } else if (step == psInitExtruders) { - FOREACH_OBJECT(this, object) { - (*object)->invalidate_step(posPerimeters); - (*object)->invalidate_step(posSupportMaterial); - } - } - - return invalidated; -} - -bool -Print::invalidate_all_steps() -{ - // make a copy because when invalidating steps the iterators are not working anymore - std::set steps = this->state.started; - - bool invalidated = false; - for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { - if (this->invalidate_step(*step)) invalidated = true; - } - return invalidated; -} - -// returns 0-based indices of used extruders -std::set -Print::extruders() const -{ - std::set extruders; - - FOREACH_REGION(this, region) { - extruders.insert((*region)->config.perimeter_extruder - 1); - extruders.insert((*region)->config.infill_extruder - 1); - } - FOREACH_OBJECT(this, object) { - extruders.insert((*object)->config.support_material_extruder - 1); - extruders.insert((*object)->config.support_material_interface_extruder - 1); - } - - return extruders; -} - -void -Print::_simplify_slices(double distance) -{ - FOREACH_OBJECT(this, object) { - FOREACH_LAYER(*object, layer) { - (*layer)->slices.simplify(distance); - FOREACH_LAYERREGION(*layer, layerm) { - (*layerm)->slices.simplify(distance); - } - } - } -} - -double -Print::max_allowed_layer_height() const -{ - std::vector nozzle_diameter; - - std::set extruders = this->extruders(); - for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { - nozzle_diameter.push_back(this->config.nozzle_diameter.get_at(*e)); - } - - return *std::max_element(nozzle_diameter.begin(), nozzle_diameter.end()); -} - -void -Print::init_extruders() -{ - if (this->state.is_done(psInitExtruders)) return; - this->state.set_done(psInitExtruders); - - // enforce tall skirt if using ooze_prevention - // FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings) - if (this->config.ooze_prevention && this->extruders().size() > 1) { - this->config.skirt_height.value = -1; - if (this->config.skirts == 0) this->config.skirts.value = 1; - } - - this->state.set_done(psInitExtruders); -} - -bool -Print::has_support_material() const -{ - FOREACH_OBJECT(this, object) { - PrintObjectConfig &config = (*object)->config; - if (config.support_material || config.raft_layers > 0 || config.support_material_enforce_layers > 0) - return true; - } - return false; -} - - -#ifdef SLIC3RXS -REGISTER_CLASS(Print, "Print"); -#endif - - -} diff --git a/xs/src/Print.hpp b/xs/src/Print.hpp deleted file mode 100644 index 8b2a45044..000000000 --- a/xs/src/Print.hpp +++ /dev/null @@ -1,187 +0,0 @@ -#ifndef slic3r_Print_hpp_ -#define slic3r_Print_hpp_ - -#include -#include -#include -#include "Flow.hpp" -#include "PrintConfig.hpp" -#include "Point.hpp" -#include "Layer.hpp" -#include "PlaceholderParser.hpp" - - -namespace Slic3r { - -class Print; -class PrintObject; -class ModelObject; - - -enum PrintStep { - psInitExtruders, psSkirt, psBrim, -}; -enum PrintObjectStep { - posSlice, posPerimeters, posPrepareInfill, - posInfill, posSupportMaterial, -}; - -template -class PrintState -{ - public: - std::set started, done; - - bool is_started(StepType step) const; - bool is_done(StepType step) const; - void set_started(StepType step); - void set_done(StepType step); - bool invalidate(StepType step); -}; - -// A PrintRegion object represents a group of volumes to print -// sharing the same config (including the same assigned extruder(s)) -class PrintRegion -{ - friend class Print; - - public: - PrintRegionConfig config; - - Print* print(); - Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; - - private: - Print* _print; - - PrintRegion(Print* print); - ~PrintRegion(); -}; - - -typedef std::vector LayerPtrs; -typedef std::vector SupportLayerPtrs; -class BoundingBoxf3; // TODO: for temporary constructor parameter - -class PrintObject -{ - friend class Print; - - public: - // vector of (vectors of volume ids), indexed by region_id - std::vector > region_volumes; - Points copies; // Slic3r::Point objects in scaled G-code coordinates - PrintObjectConfig config; - t_layer_height_ranges layer_height_ranges; - - // 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; - - 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; - - LayerPtrs layers; - SupportLayerPtrs support_layers; - // TODO: Fill* fill_maker => (is => 'lazy'); - PrintState state; - - Print* print(); - ModelObject* model_object(); - - // adds region_id, too, if necessary - void add_region_volume(int region_id, int volume_id); - - size_t layer_count(); - void clear_layers(); - Layer* get_layer(int idx); - Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); - void delete_layer(int idx); - - size_t support_layer_count(); - void clear_support_layers(); - SupportLayer* get_support_layer(int idx); - SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); - void delete_support_layer(int idx); - - // methods for handling state - bool invalidate_state_by_config_options(const std::vector &opt_keys); - bool invalidate_step(PrintObjectStep step); - bool invalidate_all_steps(); - - private: - Print* _print; - ModelObject* _model_object; - - // TODO: call model_object->get_bounding_box() instead of accepting - // parameter - PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); - ~PrintObject(); -}; - -typedef std::vector PrintObjectPtrs; -typedef std::vector PrintRegionPtrs; - -class Print -{ - public: - PrintConfig config; - PrintObjectConfig default_object_config; - PrintRegionConfig default_region_config; - PrintObjectPtrs objects; - PrintRegionPtrs regions; - PlaceholderParser placeholder_parser; - // TODO: status_cb - double total_used_filament, total_extruded_volume; - PrintState state; - - // ordered collections of extrusion paths to build skirt loops and brim - ExtrusionEntityCollection skirt, brim; - - Print(); - ~Print(); - - // methods for handling objects - void clear_objects(); - PrintObject* get_object(size_t idx); - PrintObject* add_object(ModelObject *model_object, const BoundingBoxf3 &modobj_bbox); - PrintObject* set_new_object(size_t idx, ModelObject *model_object, const BoundingBoxf3 &modobj_bbox); - void delete_object(size_t idx); - - // methods for handling regions - PrintRegion* get_region(size_t idx); - PrintRegion* add_region(); - - // methods for handling state - bool invalidate_state_by_config_options(const std::vector &opt_keys); - bool invalidate_step(PrintStep step); - bool invalidate_all_steps(); - - void init_extruders(); - - std::set extruders() const; - void _simplify_slices(double distance); - double max_allowed_layer_height() const; - bool has_support_material() const; - - private: - void clear_regions(); - void delete_region(size_t idx); -}; - -#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) -#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region) -#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object) -#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->layers, layer) -#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->regions, layerm) - -} - -#endif diff --git a/xs/src/PrintConfig.cpp b/xs/src/PrintConfig.cpp deleted file mode 100644 index 2e9d8a6bf..000000000 --- a/xs/src/PrintConfig.cpp +++ /dev/null @@ -1,967 +0,0 @@ -#include "PrintConfig.hpp" - -namespace Slic3r { - -t_optiondef_map -PrintConfigDef::build_def() { - t_optiondef_map Options; - - Options["avoid_crossing_perimeters"].type = coBool; - Options["avoid_crossing_perimeters"].label = "Avoid crossing perimeters"; - Options["avoid_crossing_perimeters"].tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation."; - Options["avoid_crossing_perimeters"].cli = "avoid-crossing-perimeters!"; - - Options["bed_shape"].type = coPoints; - Options["bed_shape"].label = "Bed shape"; - - Options["bed_temperature"].type = coInt; - Options["bed_temperature"].label = "Other layers"; - Options["bed_temperature"].tooltip = "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output."; - Options["bed_temperature"].cli = "bed-temperature=i"; - Options["bed_temperature"].full_label = "Bed temperature"; - Options["bed_temperature"].min = 0; - Options["bed_temperature"].max = 300; - - Options["bottom_solid_layers"].type = coInt; - Options["bottom_solid_layers"].label = "Bottom"; - Options["bottom_solid_layers"].category = "Layers and Perimeters"; - Options["bottom_solid_layers"].tooltip = "Number of solid layers to generate on bottom surfaces."; - Options["bottom_solid_layers"].cli = "bottom-solid-layers=i"; - Options["bottom_solid_layers"].full_label = "Bottom solid layers"; - Options["bottom_solid_layers"].min = 0; - - Options["bridge_acceleration"].type = coFloat; - Options["bridge_acceleration"].label = "Bridge"; - Options["bridge_acceleration"].tooltip = "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges."; - Options["bridge_acceleration"].sidetext = "mm/s²"; - Options["bridge_acceleration"].cli = "bridge-acceleration=f"; - Options["bridge_acceleration"].min = 0; - - Options["bridge_fan_speed"].type = coInt; - Options["bridge_fan_speed"].label = "Bridges fan speed"; - Options["bridge_fan_speed"].tooltip = "This fan speed is enforced during all bridges and overhangs."; - Options["bridge_fan_speed"].sidetext = "%"; - Options["bridge_fan_speed"].cli = "bridge-fan-speed=i"; - Options["bridge_fan_speed"].min = 0; - Options["bridge_fan_speed"].max = 100; - - Options["bridge_flow_ratio"].type = coFloat; - Options["bridge_flow_ratio"].label = "Bridge flow ratio"; - Options["bridge_flow_ratio"].category = "Advanced"; - Options["bridge_flow_ratio"].tooltip = "This factor affects the amount of plastic for bridging. You can decrease it slightly to pull the extrudates and prevent sagging, although default settings are usually good and you should experiment with cooling (use a fan) before tweaking this."; - Options["bridge_flow_ratio"].cli = "bridge-flow-ratio=f"; - Options["bridge_flow_ratio"].min = 0; - - Options["bridge_speed"].type = coFloat; - Options["bridge_speed"].label = "Bridges"; - Options["bridge_speed"].category = "Speed"; - Options["bridge_speed"].tooltip = "Speed for printing bridges."; - Options["bridge_speed"].sidetext = "mm/s"; - Options["bridge_speed"].cli = "bridge-speed=f"; - Options["bridge_speed"].aliases.push_back("bridge_feed_rate"); - Options["bridge_speed"].min = 0; - - Options["brim_width"].type = coFloat; - Options["brim_width"].label = "Brim width"; - Options["brim_width"].tooltip = "Horizontal width of the brim that will be printed around each object on the first layer."; - Options["brim_width"].sidetext = "mm"; - Options["brim_width"].cli = "brim-width=f"; - Options["brim_width"].min = 0; - - Options["complete_objects"].type = coBool; - Options["complete_objects"].label = "Complete individual objects"; - Options["complete_objects"].tooltip = "When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware."; - Options["complete_objects"].cli = "complete-objects!"; - - Options["cooling"].type = coBool; - Options["cooling"].label = "Enable auto cooling"; - Options["cooling"].tooltip = "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time."; - Options["cooling"].cli = "cooling!"; - - Options["default_acceleration"].type = coFloat; - Options["default_acceleration"].label = "Default"; - Options["default_acceleration"].tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all."; - Options["default_acceleration"].sidetext = "mm/s²"; - Options["default_acceleration"].cli = "default-acceleration=f"; - Options["default_acceleration"].min = 0; - - Options["disable_fan_first_layers"].type = coInt; - Options["disable_fan_first_layers"].label = "Disable fan for the first"; - Options["disable_fan_first_layers"].tooltip = "You can set this to a positive value to disable fan at all during the first layers, so that it does not make adhesion worse."; - Options["disable_fan_first_layers"].sidetext = "layers"; - Options["disable_fan_first_layers"].cli = "disable-fan-first-layers=i"; - Options["disable_fan_first_layers"].min = 0; - Options["disable_fan_first_layers"].max = 1000; - - Options["dont_support_bridges"].type = coBool; - Options["dont_support_bridges"].label = "Don't support bridges"; - Options["dont_support_bridges"].category = "Support material"; - Options["dont_support_bridges"].tooltip = "Experimental option for preventing support material from being generated under bridged areas."; - Options["dont_support_bridges"].cli = "dont-support-bridges!"; - - Options["duplicate_distance"].type = coFloat; - Options["duplicate_distance"].label = "Distance between copies"; - Options["duplicate_distance"].tooltip = "Distance used for the auto-arrange feature of the plater."; - Options["duplicate_distance"].sidetext = "mm"; - Options["duplicate_distance"].cli = "duplicate-distance=f"; - Options["duplicate_distance"].aliases.push_back("multiply_distance"); - Options["duplicate_distance"].min = 0; - - Options["end_gcode"].type = coString; - Options["end_gcode"].label = "End G-code"; - Options["end_gcode"].tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings."; - Options["end_gcode"].cli = "end-gcode=s"; - Options["end_gcode"].multiline = true; - Options["end_gcode"].full_width = true; - Options["end_gcode"].height = 120; - - Options["external_perimeter_extrusion_width"].type = coFloatOrPercent; - Options["external_perimeter_extrusion_width"].label = "External perimeters"; - Options["external_perimeter_extrusion_width"].category = "Extrusion Width"; - Options["external_perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for external perimeters. If left zero, an automatic value will be used that maximizes accuracy of the external visible surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; - Options["external_perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["external_perimeter_extrusion_width"].cli = "external-perimeter-extrusion-width=s"; - - Options["external_perimeter_speed"].type = coFloatOrPercent; - Options["external_perimeter_speed"].label = "External perimeters"; - Options["external_perimeter_speed"].category = "Speed"; - Options["external_perimeter_speed"].tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above."; - Options["external_perimeter_speed"].sidetext = "mm/s or %"; - Options["external_perimeter_speed"].cli = "external-perimeter-speed=s"; - Options["external_perimeter_speed"].ratio_over = "perimeter_speed"; - - Options["external_perimeters_first"].type = coBool; - Options["external_perimeters_first"].label = "External perimeters first"; - Options["external_perimeters_first"].category = "Layers and Perimeters"; - Options["external_perimeters_first"].tooltip = "Print contour perimeters from the outermost one to the innermost one instead of the default inverse order."; - Options["external_perimeters_first"].cli = "external-perimeters-first!"; - - Options["extra_perimeters"].type = coBool; - Options["extra_perimeters"].label = "Extra perimeters if needed"; - Options["extra_perimeters"].category = "Layers and Perimeters"; - Options["extra_perimeters"].tooltip = "Add more perimeters when needed for avoiding gaps in sloping walls."; - Options["extra_perimeters"].cli = "extra-perimeters!"; - - Options["extruder"].type = coInt; - Options["extruder"].gui_type = "i_enum_open"; - Options["extruder"].label = "Extruder"; - Options["extruder"].category = "Extruders"; - Options["extruder"].tooltip = "The extruder to use (unless more specific extruder settings are specified)."; - Options["extruder"].cli = "extruder=i"; - Options["extruder"].min = 0; // 0 = inherit defaults - Options["extruder"].enum_labels.push_back("default"); // override label for item 0 - Options["extruder"].enum_labels.push_back("1"); - Options["extruder"].enum_labels.push_back("2"); - Options["extruder"].enum_labels.push_back("3"); - Options["extruder"].enum_labels.push_back("4"); - - Options["extruder_clearance_height"].type = coFloat; - Options["extruder_clearance_height"].label = "Height"; - Options["extruder_clearance_height"].tooltip = "Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. In other words, this is the height of the clearance cylinder around your extruder, and it represents the maximum depth the extruder can peek before colliding with other printed objects."; - Options["extruder_clearance_height"].sidetext = "mm"; - Options["extruder_clearance_height"].cli = "extruder-clearance-height=f"; - Options["extruder_clearance_height"].min = 0; - - Options["extruder_clearance_radius"].type = coFloat; - Options["extruder_clearance_radius"].label = "Radius"; - Options["extruder_clearance_radius"].tooltip = "Set this to the clearance radius around your extruder. If the extruder is not centered, choose the largest value for safety. This setting is used to check for collisions and to display the graphical preview in the plater."; - Options["extruder_clearance_radius"].sidetext = "mm"; - Options["extruder_clearance_radius"].cli = "extruder-clearance-radius=f"; - Options["extruder_clearance_radius"].min = 0; - - Options["extruder_offset"].type = coPoints; - Options["extruder_offset"].label = "Extruder offset"; - Options["extruder_offset"].tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code to take it into account. This option lets you specify the displacement of each extruder with respect to the first one. It expects positive coordinates (they will be subtracted from the XY coordinate)."; - Options["extruder_offset"].sidetext = "mm"; - Options["extruder_offset"].cli = "extruder-offset=s@"; - - Options["extrusion_axis"].type = coString; - Options["extrusion_axis"].label = "Extrusion axis"; - Options["extrusion_axis"].tooltip = "Use this option to set the axis letter associated to your printer's extruder (usually E but some printers use A)."; - Options["extrusion_axis"].cli = "extrusion-axis=s"; - - Options["extrusion_multiplier"].type = coFloats; - Options["extrusion_multiplier"].label = "Extrusion multiplier"; - Options["extrusion_multiplier"].tooltip = "This factor changes the amount of flow proportionally. You may need to tweak this setting to get nice surface finish and correct single wall widths. Usual values are between 0.9 and 1.1. If you think you need to change this more, check filament diameter and your firmware E steps."; - Options["extrusion_multiplier"].cli = "extrusion-multiplier=f@"; - - Options["extrusion_width"].type = coFloatOrPercent; - Options["extrusion_width"].label = "Default extrusion width"; - Options["extrusion_width"].category = "Extrusion Width"; - Options["extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) it will be computed over layer height."; - Options["extrusion_width"].sidetext = "mm or % (leave 0 for auto)"; - Options["extrusion_width"].cli = "extrusion-width=s"; - - Options["fan_always_on"].type = coBool; - Options["fan_always_on"].label = "Keep fan always on"; - Options["fan_always_on"].tooltip = "If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS."; - Options["fan_always_on"].cli = "fan-always-on!"; - - Options["fan_below_layer_time"].type = coInt; - Options["fan_below_layer_time"].label = "Enable fan if layer print time is below"; - Options["fan_below_layer_time"].tooltip = "If layer print time is estimated below this number of seconds, fan will be enabled and its speed will be calculated by interpolating the minimum and maximum speeds."; - Options["fan_below_layer_time"].sidetext = "approximate seconds"; - Options["fan_below_layer_time"].cli = "fan-below-layer-time=i"; - Options["fan_below_layer_time"].width = 60; - Options["fan_below_layer_time"].min = 0; - Options["fan_below_layer_time"].max = 1000; - - Options["filament_diameter"].type = coFloats; - Options["filament_diameter"].label = "Diameter"; - Options["filament_diameter"].tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."; - Options["filament_diameter"].sidetext = "mm"; - Options["filament_diameter"].cli = "filament-diameter=f@"; - Options["filament_diameter"].min = 0; - - Options["fill_angle"].type = coInt; - Options["fill_angle"].label = "Fill angle"; - Options["fill_angle"].category = "Infill"; - Options["fill_angle"].tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them."; - Options["fill_angle"].sidetext = "°"; - Options["fill_angle"].cli = "fill-angle=i"; - Options["fill_angle"].min = 0; - Options["fill_angle"].max = 359; - - Options["fill_density"].type = coPercent; - Options["fill_density"].gui_type = "f_enum_open"; - Options["fill_density"].gui_flags = "show_value"; - Options["fill_density"].label = "Fill density"; - Options["fill_density"].category = "Infill"; - Options["fill_density"].tooltip = "Density of internal infill, expressed in the range 0% - 100%."; - Options["fill_density"].sidetext = "%"; - Options["fill_density"].cli = "fill-density=s"; - Options["fill_density"].min = 0; - Options["fill_density"].max = 100; - Options["fill_density"].enum_values.push_back("0"); - Options["fill_density"].enum_values.push_back("5"); - Options["fill_density"].enum_values.push_back("10"); - Options["fill_density"].enum_values.push_back("15"); - Options["fill_density"].enum_values.push_back("20"); - Options["fill_density"].enum_values.push_back("25"); - Options["fill_density"].enum_values.push_back("30"); - Options["fill_density"].enum_values.push_back("40"); - Options["fill_density"].enum_values.push_back("50"); - Options["fill_density"].enum_values.push_back("60"); - Options["fill_density"].enum_values.push_back("70"); - Options["fill_density"].enum_values.push_back("80"); - Options["fill_density"].enum_values.push_back("90"); - Options["fill_density"].enum_values.push_back("100"); - Options["fill_density"].enum_labels.push_back("0%"); - Options["fill_density"].enum_labels.push_back("5%"); - Options["fill_density"].enum_labels.push_back("10%"); - Options["fill_density"].enum_labels.push_back("15%"); - Options["fill_density"].enum_labels.push_back("20%"); - Options["fill_density"].enum_labels.push_back("25%"); - Options["fill_density"].enum_labels.push_back("30%"); - Options["fill_density"].enum_labels.push_back("40%"); - Options["fill_density"].enum_labels.push_back("50%"); - Options["fill_density"].enum_labels.push_back("60%"); - Options["fill_density"].enum_labels.push_back("70%"); - Options["fill_density"].enum_labels.push_back("80%"); - Options["fill_density"].enum_labels.push_back("90%"); - Options["fill_density"].enum_labels.push_back("100%"); - - Options["fill_pattern"].type = coEnum; - Options["fill_pattern"].label = "Fill pattern"; - Options["fill_pattern"].category = "Infill"; - Options["fill_pattern"].tooltip = "Fill pattern for general low-density infill."; - Options["fill_pattern"].cli = "fill-pattern=s"; - Options["fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); - Options["fill_pattern"].enum_values.push_back("rectilinear"); - Options["fill_pattern"].enum_values.push_back("line"); - Options["fill_pattern"].enum_values.push_back("concentric"); - Options["fill_pattern"].enum_values.push_back("honeycomb"); - Options["fill_pattern"].enum_values.push_back("3dhoneycomb"); - Options["fill_pattern"].enum_values.push_back("hilbertcurve"); - Options["fill_pattern"].enum_values.push_back("archimedeanchords"); - Options["fill_pattern"].enum_values.push_back("octagramspiral"); - Options["fill_pattern"].enum_labels.push_back("rectilinear"); - Options["fill_pattern"].enum_labels.push_back("line"); - Options["fill_pattern"].enum_labels.push_back("concentric"); - Options["fill_pattern"].enum_labels.push_back("honeycomb"); - Options["fill_pattern"].enum_labels.push_back("3D honeycomb"); - Options["fill_pattern"].enum_labels.push_back("hilbertcurve (slow)"); - Options["fill_pattern"].enum_labels.push_back("archimedeanchords (slow)"); - Options["fill_pattern"].enum_labels.push_back("octagramspiral (slow)"); - - Options["first_layer_acceleration"].type = coFloat; - Options["first_layer_acceleration"].label = "First layer"; - Options["first_layer_acceleration"].tooltip = "This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer."; - Options["first_layer_acceleration"].sidetext = "mm/s²"; - Options["first_layer_acceleration"].cli = "first-layer-acceleration=f"; - Options["first_layer_acceleration"].min = 0; - - Options["first_layer_bed_temperature"].type = coInt; - Options["first_layer_bed_temperature"].label = "First layer"; - Options["first_layer_bed_temperature"].tooltip = "Heated build plate temperature for the first layer. Set this to zero to disable bed temperature control commands in the output."; - Options["first_layer_bed_temperature"].cli = "first-layer-bed-temperature=i"; - Options["first_layer_bed_temperature"].max = 0; - Options["first_layer_bed_temperature"].max = 300; - - Options["first_layer_extrusion_width"].type = coFloatOrPercent; - Options["first_layer_extrusion_width"].label = "First layer"; - Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height."; - Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s"; - Options["first_layer_extrusion_width"].ratio_over = "first_layer_height"; - - Options["first_layer_height"].type = coFloatOrPercent; - Options["first_layer_height"].label = "First layer height"; - Options["first_layer_height"].category = "Layers and Perimeters"; - Options["first_layer_height"].tooltip = "When printing with very low layer heights, you might still want to print a thicker bottom layer to improve adhesion and tolerance for non perfect build plates. This can be expressed as an absolute value or as a percentage (for example: 150%) over the default layer height."; - Options["first_layer_height"].sidetext = "mm or %"; - Options["first_layer_height"].cli = "first-layer-height=s"; - Options["first_layer_height"].ratio_over = "layer_height"; - - Options["first_layer_speed"].type = coFloatOrPercent; - Options["first_layer_speed"].label = "First layer speed"; - Options["first_layer_speed"].tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds."; - Options["first_layer_speed"].sidetext = "mm/s or %"; - Options["first_layer_speed"].cli = "first-layer-speed=s"; - - Options["first_layer_temperature"].type = coInts; - Options["first_layer_temperature"].label = "First layer"; - Options["first_layer_temperature"].tooltip = "Extruder temperature for first layer. If you want to control temperature manually during print, set this to zero to disable temperature control commands in the output file."; - Options["first_layer_temperature"].cli = "first-layer-temperature=i@"; - Options["first_layer_temperature"].min = 0; - Options["first_layer_temperature"].max = 400; - - Options["g0"].type = coBool; - Options["g0"].label = "Use G0 for travel moves"; - Options["g0"].tooltip = "Only enable this if your firmware supports G0 properly (thus decouples all axes using their maximum speeds instead of synchronizing them). Travel moves and retractions will be combined in single commands, speeding them print up."; - Options["g0"].cli = "g0!"; - - Options["gap_fill_speed"].type = coFloat; - Options["gap_fill_speed"].label = "Gap fill"; - Options["gap_fill_speed"].category = "Speed"; - Options["gap_fill_speed"].tooltip = "Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues. Set zero to disable gaps filling."; - Options["gap_fill_speed"].sidetext = "mm/s"; - Options["gap_fill_speed"].cli = "gap-fill-speed=f"; - Options["gap_fill_speed"].min = 0; - - Options["gcode_arcs"].type = coBool; - Options["gcode_arcs"].label = "Use native G-code arcs"; - Options["gcode_arcs"].tooltip = "This experimental feature tries to detect arcs from segments and generates G2/G3 arc commands instead of multiple straight G1 commands."; - Options["gcode_arcs"].cli = "gcode-arcs!"; - - Options["gcode_comments"].type = coBool; - Options["gcode_comments"].label = "Verbose G-code"; - Options["gcode_comments"].tooltip = "Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down."; - Options["gcode_comments"].cli = "gcode-comments!"; - - Options["gcode_flavor"].type = coEnum; - Options["gcode_flavor"].label = "G-code flavor"; - Options["gcode_flavor"].tooltip = "Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer's firmware to get a compatible output. The \"No extrusion\" flavor prevents Slic3r from exporting any extrusion value at all."; - Options["gcode_flavor"].cli = "gcode-flavor=s"; - Options["gcode_flavor"].enum_keys_map = ConfigOptionEnum::get_enum_values(); - Options["gcode_flavor"].enum_values.push_back("reprap"); - Options["gcode_flavor"].enum_values.push_back("teacup"); - Options["gcode_flavor"].enum_values.push_back("makerware"); - Options["gcode_flavor"].enum_values.push_back("sailfish"); - Options["gcode_flavor"].enum_values.push_back("mach3"); - Options["gcode_flavor"].enum_values.push_back("no-extrusion"); - Options["gcode_flavor"].enum_labels.push_back("RepRap (Marlin/Sprinter/Repetier)"); - Options["gcode_flavor"].enum_labels.push_back("Teacup"); - Options["gcode_flavor"].enum_labels.push_back("MakerWare (MakerBot)"); - Options["gcode_flavor"].enum_labels.push_back("Sailfish (MakerBot)"); - Options["gcode_flavor"].enum_labels.push_back("Mach3/LinuxCNC"); - Options["gcode_flavor"].enum_labels.push_back("No extrusion"); - - Options["infill_acceleration"].type = coFloat; - Options["infill_acceleration"].label = "Infill"; - Options["infill_acceleration"].tooltip = "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill."; - Options["infill_acceleration"].sidetext = "mm/s²"; - Options["infill_acceleration"].cli = "infill-acceleration=f"; - Options["infill_acceleration"].min = 0; - - Options["infill_every_layers"].type = coInt; - Options["infill_every_layers"].label = "Combine infill every"; - Options["infill_every_layers"].category = "Infill"; - Options["infill_every_layers"].tooltip = "This feature allows to combine infill and speed up your print by extruding thicker infill layers while preserving thin perimeters, thus accuracy."; - Options["infill_every_layers"].sidetext = "layers"; - Options["infill_every_layers"].cli = "infill-every-layers=i"; - Options["infill_every_layers"].full_label = "Combine infill every n layers"; - Options["infill_every_layers"].min = 1; - - Options["infill_extruder"].type = coInt; - Options["infill_extruder"].label = "Infill extruder"; - Options["infill_extruder"].category = "Extruders"; - Options["infill_extruder"].tooltip = "The extruder to use when printing infill."; - Options["infill_extruder"].cli = "infill-extruder=i"; - Options["infill_extruder"].min = 1; - - Options["infill_extrusion_width"].type = coFloatOrPercent; - Options["infill_extrusion_width"].label = "Infill"; - Options["infill_extrusion_width"].category = "Extrusion Width"; - Options["infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. You may want to use fatter extrudates to speed up the infill and make your parts stronger. If expressed as percentage (for example 90%) it will be computed over layer height."; - Options["infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["infill_extrusion_width"].cli = "infill-extrusion-width=s"; - - Options["infill_first"].type = coBool; - Options["infill_first"].label = "Infill before perimeters"; - Options["infill_first"].tooltip = "This option will switch the print order of perimeters and infill, making the latter first."; - Options["infill_first"].cli = "infill-first!"; - - Options["infill_only_where_needed"].type = coBool; - Options["infill_only_where_needed"].label = "Only infill where needed"; - Options["infill_only_where_needed"].category = "Infill"; - Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material)."; - Options["infill_only_where_needed"].cli = "infill-only-where-needed!"; - - Options["infill_speed"].type = coFloat; - Options["infill_speed"].label = "Infill"; - Options["infill_speed"].category = "Speed"; - Options["infill_speed"].tooltip = "Speed for printing the internal fill."; - Options["infill_speed"].sidetext = "mm/s"; - Options["infill_speed"].cli = "infill-speed=f"; - Options["infill_speed"].aliases.push_back("print_feed_rate"); - Options["infill_speed"].aliases.push_back("infill_feed_rate"); - Options["infill_speed"].min = 0; - - Options["interface_shells"].type = coBool; - Options["interface_shells"].label = "Interface shells"; - Options["interface_shells"].tooltip = "Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material."; - Options["interface_shells"].cli = "interface-shells!"; - Options["interface_shells"].category = "Layers and Perimeters"; - - Options["layer_gcode"].type = coString; - Options["layer_gcode"].label = "Layer change G-code"; - Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings."; - Options["layer_gcode"].cli = "layer-gcode=s"; - Options["layer_gcode"].multiline = true; - Options["layer_gcode"].full_width = true; - Options["layer_gcode"].height = 50; - - Options["layer_height"].type = coFloat; - Options["layer_height"].label = "Layer height"; - Options["layer_height"].category = "Layers and Perimeters"; - Options["layer_height"].tooltip = "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."; - Options["layer_height"].sidetext = "mm"; - Options["layer_height"].cli = "layer-height=f"; - Options["layer_height"].min = 0; - - Options["max_fan_speed"].type = coInt; - Options["max_fan_speed"].label = "Max"; - Options["max_fan_speed"].tooltip = "This setting represents the maximum speed of your fan."; - Options["max_fan_speed"].sidetext = "%"; - Options["max_fan_speed"].cli = "max-fan-speed=i"; - Options["max_fan_speed"].min = 0; - Options["max_fan_speed"].max = 100; - - Options["min_fan_speed"].type = coInt; - Options["min_fan_speed"].label = "Min"; - Options["min_fan_speed"].tooltip = "This setting represents the minimum PWM your fan needs to work."; - Options["min_fan_speed"].sidetext = "%"; - Options["min_fan_speed"].cli = "min-fan-speed=i"; - Options["min_fan_speed"].min = 0; - Options["min_fan_speed"].max = 100; - - Options["min_print_speed"].type = coInt; - Options["min_print_speed"].label = "Min print speed"; - Options["min_print_speed"].tooltip = "Slic3r will not scale speed down below this speed."; - Options["min_print_speed"].sidetext = "mm/s"; - Options["min_print_speed"].cli = "min-print-speed=f"; - Options["min_print_speed"].min = 0; - Options["min_print_speed"].max = 1000; - - Options["min_skirt_length"].type = coFloat; - Options["min_skirt_length"].label = "Minimum extrusion length"; - Options["min_skirt_length"].tooltip = "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."; - Options["min_skirt_length"].sidetext = "mm"; - Options["min_skirt_length"].cli = "min-skirt-length=f"; - Options["min_skirt_length"].min = 0; - - Options["notes"].type = coString; - Options["notes"].label = "Configuration notes"; - Options["notes"].tooltip = "You can put here your personal notes. This text will be added to the G-code header comments."; - Options["notes"].cli = "notes=s"; - Options["notes"].multiline = true; - Options["notes"].full_width = true; - Options["notes"].height = 130; - - Options["nozzle_diameter"].type = coFloats; - Options["nozzle_diameter"].label = "Nozzle diameter"; - Options["nozzle_diameter"].tooltip = "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)"; - Options["nozzle_diameter"].sidetext = "mm"; - Options["nozzle_diameter"].cli = "nozzle-diameter=f@"; - - Options["only_retract_when_crossing_perimeters"].type = coBool; - Options["only_retract_when_crossing_perimeters"].label = "Only retract when crossing perimeters"; - Options["only_retract_when_crossing_perimeters"].tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible)."; - Options["only_retract_when_crossing_perimeters"].cli = "only-retract-when-crossing-perimeters!"; - - Options["ooze_prevention"].type = coBool; - Options["ooze_prevention"].label = "Enable"; - Options["ooze_prevention"].tooltip = "This option will drop the temperature of the inactive extruders to prevent oozing. It will enable a tall skirt automatically and move extruders outside such skirt when changing temperatures."; - Options["ooze_prevention"].cli = "ooze-prevention!"; - - Options["output_filename_format"].type = coString; - Options["output_filename_format"].label = "Output filename format"; - Options["output_filename_format"].tooltip = "You can use all configuration options as variables inside this template. For example: [layer_height], [fill_density] etc. You can also use [timestamp], [year], [month], [day], [hour], [minute], [second], [version], [input_filename], [input_filename_base]."; - Options["output_filename_format"].cli = "output-filename-format=s"; - Options["output_filename_format"].full_width = true; - - Options["overhangs"].type = coBool; - Options["overhangs"].label = "Detect bridging perimeters"; - Options["overhangs"].category = "Layers and Perimeters"; - Options["overhangs"].tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan."; - Options["overhangs"].cli = "overhangs!"; - - Options["perimeter_acceleration"].type = coFloat; - Options["perimeter_acceleration"].label = "Perimeters"; - Options["perimeter_acceleration"].tooltip = "This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters."; - Options["perimeter_acceleration"].sidetext = "mm/s²"; - Options["perimeter_acceleration"].cli = "perimeter-acceleration=f"; - - Options["perimeter_extruder"].type = coInt; - Options["perimeter_extruder"].label = "Perimeter extruder"; - Options["perimeter_extruder"].category = "Extruders"; - Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters. First extruder is 1."; - Options["perimeter_extruder"].cli = "perimeter-extruder=i"; - Options["perimeter_extruder"].aliases.push_back("perimeters_extruder"); - Options["perimeter_extruder"].min = 1; - - Options["perimeter_extrusion_width"].type = coFloatOrPercent; - Options["perimeter_extrusion_width"].label = "Perimeters"; - Options["perimeter_extrusion_width"].category = "Extrusion Width"; - Options["perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; - Options["perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["perimeter_extrusion_width"].cli = "perimeter-extrusion-width=s"; - Options["perimeter_extrusion_width"].aliases.push_back("perimeters_extrusion_width"); - - Options["perimeter_speed"].type = coFloat; - Options["perimeter_speed"].label = "Perimeters"; - Options["perimeter_speed"].category = "Speed"; - Options["perimeter_speed"].tooltip = "Speed for perimeters (contours, aka vertical shells)."; - Options["perimeter_speed"].sidetext = "mm/s"; - Options["perimeter_speed"].cli = "perimeter-speed=f"; - Options["perimeter_speed"].aliases.push_back("perimeter_feed_rate"); - Options["perimeter_speed"].min = 0; - - Options["perimeters"].type = coInt; - Options["perimeters"].label = "Perimeters (minimum)"; - Options["perimeters"].category = "Layers and Perimeters"; - Options["perimeters"].tooltip = "This option sets the number of perimeters to generate for each layer. Note that Slic3r may increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters if the Extra Perimeters option is enabled."; - Options["perimeters"].cli = "perimeters=i"; - Options["perimeters"].aliases.push_back("perimeter_offsets"); - Options["perimeters"].min = 0; - - Options["post_process"].type = coStrings; - Options["post_process"].label = "Post-processing scripts"; - Options["post_process"].tooltip = "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."; - Options["post_process"].cli = "post-process=s@"; - Options["post_process"].gui_flags = "serialized"; - Options["post_process"].multiline = true; - Options["post_process"].full_width = true; - Options["post_process"].height = 60; - - Options["raft_layers"].type = coInt; - Options["raft_layers"].label = "Raft layers"; - Options["raft_layers"].category = "Support material"; - Options["raft_layers"].tooltip = "The object will be raised by this number of layers, and support material will be generated under it."; - Options["raft_layers"].sidetext = "layers"; - Options["raft_layers"].cli = "raft-layers=i"; - Options["raft_layers"].min = 0; - - Options["resolution"].type = coFloat; - Options["resolution"].label = "Resolution"; - Options["resolution"].tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input."; - Options["resolution"].sidetext = "mm"; - Options["resolution"].cli = "resolution=f"; - Options["resolution"].min = 0; - - Options["retract_before_travel"].type = coFloats; - Options["retract_before_travel"].label = "Minimum travel after retraction"; - Options["retract_before_travel"].tooltip = "Retraction is not triggered when travel moves are shorter than this length."; - Options["retract_before_travel"].sidetext = "mm"; - Options["retract_before_travel"].cli = "retract-before-travel=f@"; - - Options["retract_layer_change"].type = coBools; - Options["retract_layer_change"].label = "Retract on layer change"; - Options["retract_layer_change"].tooltip = "This flag enforces a retraction whenever a Z move is done."; - Options["retract_layer_change"].cli = "retract-layer-change!"; - - Options["retract_length"].type = coFloats; - Options["retract_length"].label = "Length"; - Options["retract_length"].tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; - Options["retract_length"].sidetext = "mm (zero to disable)"; - Options["retract_length"].cli = "retract-length=f@"; - - Options["retract_length_toolchange"].type = coFloats; - Options["retract_length_toolchange"].label = "Length"; - Options["retract_length_toolchange"].tooltip = "When retraction is triggered before changing tool, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; - Options["retract_length_toolchange"].sidetext = "mm (zero to disable)"; - Options["retract_length_toolchange"].cli = "retract-length-toolchange=f@"; - - Options["retract_lift"].type = coFloats; - Options["retract_lift"].label = "Lift Z"; - Options["retract_lift"].tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered."; - Options["retract_lift"].sidetext = "mm"; - Options["retract_lift"].cli = "retract-lift=f@"; - - Options["retract_restart_extra"].type = coFloats; - Options["retract_restart_extra"].label = "Extra length on restart"; - Options["retract_restart_extra"].tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."; - Options["retract_restart_extra"].sidetext = "mm"; - Options["retract_restart_extra"].cli = "retract-restart-extra=f@"; - - Options["retract_restart_extra_toolchange"].type = coFloats; - Options["retract_restart_extra_toolchange"].label = "Extra length on restart"; - Options["retract_restart_extra_toolchange"].tooltip = "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."; - Options["retract_restart_extra_toolchange"].sidetext = "mm"; - Options["retract_restart_extra_toolchange"].cli = "retract-restart-extra-toolchange=f@"; - - Options["retract_speed"].type = coInts; - Options["retract_speed"].label = "Speed"; - Options["retract_speed"].tooltip = "The speed for retractions (it only applies to the extruder motor)."; - Options["retract_speed"].sidetext = "mm/s"; - Options["retract_speed"].cli = "retract-speed=f@"; - Options["retract_speed"].max = 1000; - - Options["seam_position"].type = coEnum; - Options["seam_position"].label = "Seam position"; - Options["seam_position"].category = "Layers and perimeters"; - Options["seam_position"].tooltip = "Position of perimeters starting points."; - Options["seam_position"].cli = "seam-position=s"; - Options["seam_position"].enum_keys_map = ConfigOptionEnum::get_enum_values(); - Options["seam_position"].enum_values.push_back("random"); - Options["seam_position"].enum_values.push_back("nearest"); - Options["seam_position"].enum_values.push_back("aligned"); - Options["seam_position"].enum_labels.push_back("Random"); - Options["seam_position"].enum_labels.push_back("Nearest"); - Options["seam_position"].enum_labels.push_back("Aligned"); - - Options["skirt_distance"].type = coFloat; - Options["skirt_distance"].label = "Distance from object"; - Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion."; - Options["skirt_distance"].sidetext = "mm"; - Options["skirt_distance"].cli = "skirt-distance=f"; - Options["skirt_distance"].min = 0; - - Options["skirt_height"].type = coInt; - Options["skirt_height"].label = "Skirt height"; - Options["skirt_height"].tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt as a shield against drafts."; - Options["skirt_height"].sidetext = "layers"; - Options["skirt_height"].cli = "skirt-height=i"; - - Options["skirts"].type = coInt; - Options["skirts"].label = "Loops"; - Options["skirts"].tooltip = "Number of loops for this skirt, in other words its thickness. Set this to zero to disable skirt."; - Options["skirts"].cli = "skirts=i"; - Options["skirts"].min = 0; - - Options["slowdown_below_layer_time"].type = coInt; - Options["slowdown_below_layer_time"].label = "Slow down if layer print time is below"; - Options["slowdown_below_layer_time"].tooltip = "If layer print time is estimated below this number of seconds, print moves speed will be scaled down to extend duration to this value."; - Options["slowdown_below_layer_time"].sidetext = "approximate seconds"; - Options["slowdown_below_layer_time"].cli = "slowdown-below-layer-time=i"; - Options["slowdown_below_layer_time"].width = 60; - Options["slowdown_below_layer_time"].min = 0; - Options["slowdown_below_layer_time"].max = 1000; - - Options["small_perimeter_speed"].type = coFloatOrPercent; - Options["small_perimeter_speed"].label = "Small perimeters"; - Options["small_perimeter_speed"].category = "Speed"; - Options["small_perimeter_speed"].tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above."; - Options["small_perimeter_speed"].sidetext = "mm/s or %"; - Options["small_perimeter_speed"].cli = "small-perimeter-speed=s"; - Options["small_perimeter_speed"].ratio_over = "perimeter_speed"; - - Options["solid_fill_pattern"].type = coEnum; - Options["solid_fill_pattern"].label = "Top/bottom fill pattern"; - Options["solid_fill_pattern"].category = "Infill"; - Options["solid_fill_pattern"].tooltip = "Fill pattern for top/bottom infill."; - Options["solid_fill_pattern"].cli = "solid-fill-pattern=s"; - Options["solid_fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); - Options["solid_fill_pattern"].enum_values.push_back("rectilinear"); - Options["solid_fill_pattern"].enum_values.push_back("concentric"); - Options["solid_fill_pattern"].enum_values.push_back("hilbertcurve"); - Options["solid_fill_pattern"].enum_values.push_back("archimedeanchords"); - Options["solid_fill_pattern"].enum_values.push_back("octagramspiral"); - Options["solid_fill_pattern"].enum_labels.push_back("rectilinear"); - Options["solid_fill_pattern"].enum_labels.push_back("concentric"); - Options["solid_fill_pattern"].enum_labels.push_back("hilbertcurve (slow)"); - Options["solid_fill_pattern"].enum_labels.push_back("archimedeanchords (slow)"); - Options["solid_fill_pattern"].enum_labels.push_back("octagramspiral (slow)"); - - Options["solid_infill_below_area"].type = coFloat; - Options["solid_infill_below_area"].label = "Solid infill threshold area"; - Options["solid_infill_below_area"].category = "Infill"; - Options["solid_infill_below_area"].tooltip = "Force solid infill for regions having a smaller area than the specified threshold."; - Options["solid_infill_below_area"].sidetext = "mm²"; - Options["solid_infill_below_area"].cli = "solid-infill-below-area=f"; - Options["solid_infill_below_area"].min = 0; - - Options["solid_infill_every_layers"].type = coInt; - Options["solid_infill_every_layers"].label = "Solid infill every"; - Options["solid_infill_every_layers"].category = "Infill"; - Options["solid_infill_every_layers"].tooltip = "This feature allows to force a solid layer every given number of layers. Zero to disable. You can set this to any value (for example 9999); Slic3r will automatically choose the maximum possible number of layers to combine according to nozzle diameter and layer height."; - Options["solid_infill_every_layers"].sidetext = "layers"; - Options["solid_infill_every_layers"].cli = "solid-infill-every-layers=i"; - Options["solid_infill_every_layers"].min = 0; - - Options["solid_infill_extrusion_width"].type = coFloatOrPercent; - Options["solid_infill_extrusion_width"].label = "Solid infill"; - Options["solid_infill_extrusion_width"].category = "Extrusion Width"; - Options["solid_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. If expressed as percentage (for example 90%) it will be computed over layer height."; - Options["solid_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["solid_infill_extrusion_width"].cli = "solid-infill-extrusion-width=s"; - - Options["solid_infill_speed"].type = coFloatOrPercent; - Options["solid_infill_speed"].label = "Solid infill"; - Options["solid_infill_speed"].category = "Speed"; - Options["solid_infill_speed"].tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). This can be expressed as a percentage (for example: 80%) over the default infill speed above."; - Options["solid_infill_speed"].sidetext = "mm/s or %"; - Options["solid_infill_speed"].cli = "solid-infill-speed=s"; - Options["solid_infill_speed"].ratio_over = "infill_speed"; - Options["solid_infill_speed"].aliases.push_back("solid_infill_feed_rate"); - - Options["solid_layers"].type = coInt; - Options["solid_layers"].label = "Solid layers"; - Options["solid_layers"].tooltip = "Number of solid layers to generate on top and bottom surfaces."; - Options["solid_layers"].cli = "solid-layers=i"; - Options["solid_layers"].shortcut.push_back("top_solid_layers"); - Options["solid_layers"].shortcut.push_back("bottom_solid_layers"); - Options["solid_layers"].min = 0; - - Options["spiral_vase"].type = coBool; - Options["spiral_vase"].label = "Spiral vase"; - Options["spiral_vase"].tooltip = "This feature will raise Z gradually while printing a single-walled object in order to remove any visible seam. This option requires a single perimeter, no infill, no top solid layers and no support material. You can still set any number of bottom solid layers as well as skirt/brim loops. It won't work when printing more than an object."; - Options["spiral_vase"].cli = "spiral-vase!"; - - Options["standby_temperature_delta"].type = coInt; - Options["standby_temperature_delta"].label = "Temperature variation"; - Options["standby_temperature_delta"].tooltip = "Temperature difference to be applied when an extruder is not active."; - Options["standby_temperature_delta"].sidetext = "∆°C"; - Options["standby_temperature_delta"].cli = "standby-temperature-delta=i"; - Options["standby_temperature_delta"].min = -400; - Options["standby_temperature_delta"].max = 400; - - Options["start_gcode"].type = coString; - Options["start_gcode"].label = "Start G-code"; - Options["start_gcode"].tooltip = "This start procedure is inserted at the beginning, after bed has reached the target temperature and extruder just started heating, and before extruder has finished heating. If Slic3r detects M104 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want."; - Options["start_gcode"].cli = "start-gcode=s"; - Options["start_gcode"].multiline = true; - Options["start_gcode"].full_width = true; - Options["start_gcode"].height = 120; - - Options["support_material"].type = coBool; - Options["support_material"].label = "Generate support material"; - Options["support_material"].category = "Support material"; - Options["support_material"].tooltip = "Enable support material generation."; - Options["support_material"].cli = "support-material!"; - - Options["support_material_angle"].type = coInt; - Options["support_material_angle"].label = "Pattern angle"; - Options["support_material_angle"].category = "Support material"; - Options["support_material_angle"].tooltip = "Use this setting to rotate the support material pattern on the horizontal plane."; - Options["support_material_angle"].sidetext = "°"; - Options["support_material_angle"].cli = "support-material-angle=i"; - Options["support_material_angle"].min = 0; - Options["support_material_angle"].max = 359; - - Options["support_material_enforce_layers"].type = coInt; - Options["support_material_enforce_layers"].label = "Enforce support for the first"; - Options["support_material_enforce_layers"].category = "Support material"; - Options["support_material_enforce_layers"].tooltip = "Generate support material for the specified number of layers counting from bottom, regardless of whether normal support material is enabled or not and regardless of any angle threshold. This is useful for getting more adhesion of objects having a very thin or poor footprint on the build plate."; - Options["support_material_enforce_layers"].sidetext = "layers"; - Options["support_material_enforce_layers"].cli = "support-material-enforce-layers=f"; - Options["support_material_enforce_layers"].full_label = "Enforce support for the first n layers"; - Options["support_material_enforce_layers"].min = 0; - - Options["support_material_extruder"].type = coInt; - Options["support_material_extruder"].label = "Support material extruder"; - Options["support_material_extruder"].category = "Extruders"; - Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too."; - Options["support_material_extruder"].cli = "support-material-extruder=i"; - Options["support_material_extruder"].min = 1; - - Options["support_material_extrusion_width"].type = coFloatOrPercent; - Options["support_material_extrusion_width"].label = "Support material"; - Options["support_material_extrusion_width"].category = "Extrusion Width"; - Options["support_material_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. If expressed as percentage (for example 90%) it will be computed over layer height."; - Options["support_material_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["support_material_extrusion_width"].cli = "support-material-extrusion-width=s"; - - Options["support_material_interface_extruder"].type = coInt; - Options["support_material_interface_extruder"].label = "Support material interface extruder"; - Options["support_material_interface_extruder"].category = "Extruders"; - Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too."; - Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i"; - Options["support_material_interface_extruder"].min = 1; - - Options["support_material_interface_layers"].type = coInt; - Options["support_material_interface_layers"].label = "Interface layers"; - Options["support_material_interface_layers"].category = "Support material"; - Options["support_material_interface_layers"].tooltip = "Number of interface layers to insert between the object(s) and support material."; - Options["support_material_interface_layers"].sidetext = "layers"; - Options["support_material_interface_layers"].cli = "support-material-interface-layers=i"; - Options["support_material_interface_layers"].min = 0; - - Options["support_material_interface_spacing"].type = coFloat; - Options["support_material_interface_spacing"].label = "Interface pattern spacing"; - Options["support_material_interface_spacing"].category = "Support material"; - Options["support_material_interface_spacing"].tooltip = "Spacing between interface lines. Set zero to get a solid interface."; - Options["support_material_interface_spacing"].sidetext = "mm"; - Options["support_material_interface_spacing"].cli = "support-material-interface-spacing=f"; - Options["support_material_interface_spacing"].min = 0; - - Options["support_material_interface_speed"].type = coFloatOrPercent; - Options["support_material_interface_speed"].label = "Support material interface"; - Options["support_material_interface_speed"].category = "Support material"; - Options["support_material_interface_speed"].tooltip = "Speed for printing support material interface layers. If expressed as percentage (for example 50%) it will be calculated over support material speed."; - Options["support_material_interface_speed"].sidetext = "mm/s or %"; - Options["support_material_interface_speed"].cli = "support-material-interface-speed=s"; - Options["support_material_interface_speed"].ratio_over = "support_material_speed"; - - Options["support_material_pattern"].type = coEnum; - Options["support_material_pattern"].label = "Pattern"; - Options["support_material_pattern"].category = "Support material"; - Options["support_material_pattern"].tooltip = "Pattern used to generate support material."; - Options["support_material_pattern"].cli = "support-material-pattern=s"; - Options["support_material_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); - Options["support_material_pattern"].enum_values.push_back("rectilinear"); - Options["support_material_pattern"].enum_values.push_back("rectilinear-grid"); - Options["support_material_pattern"].enum_values.push_back("honeycomb"); - Options["support_material_pattern"].enum_values.push_back("pillars"); - Options["support_material_pattern"].enum_labels.push_back("rectilinear"); - Options["support_material_pattern"].enum_labels.push_back("rectilinear grid"); - Options["support_material_pattern"].enum_labels.push_back("honeycomb"); - Options["support_material_pattern"].enum_labels.push_back("pillars"); - - Options["support_material_spacing"].type = coFloat; - Options["support_material_spacing"].label = "Pattern spacing"; - Options["support_material_spacing"].category = "Support material"; - Options["support_material_spacing"].tooltip = "Spacing between support material lines."; - Options["support_material_spacing"].sidetext = "mm"; - Options["support_material_spacing"].cli = "support-material-spacing=f"; - Options["support_material_spacing"].min = 0; - - Options["support_material_speed"].type = coFloat; - Options["support_material_speed"].label = "Support material"; - Options["support_material_speed"].category = "Support material"; - Options["support_material_speed"].tooltip = "Speed for printing support material."; - Options["support_material_speed"].sidetext = "mm/s"; - Options["support_material_speed"].cli = "support-material-speed=f"; - Options["support_material_speed"].min = 0; - - Options["support_material_threshold"].type = coInt; - Options["support_material_threshold"].label = "Overhang threshold"; - Options["support_material_threshold"].category = "Support material"; - Options["support_material_threshold"].tooltip = "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represent the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended)."; - Options["support_material_threshold"].sidetext = "°"; - Options["support_material_threshold"].cli = "support-material-threshold=i"; - Options["support_material_threshold"].min = 0; - Options["support_material_threshold"].max = 90; - - Options["temperature"].type = coInts; - Options["temperature"].label = "Other layers"; - Options["temperature"].tooltip = "Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output."; - Options["temperature"].cli = "temperature=i@"; - Options["temperature"].full_label = "Temperature"; - Options["temperature"].max = 0; - Options["temperature"].max = 400; - - Options["thin_walls"].type = coBool; - Options["thin_walls"].label = "Detect thin walls"; - Options["thin_walls"].category = "Layers and Perimeters"; - Options["thin_walls"].tooltip = "Detect single-width walls (parts where two extrusions don't fit and we need to collapse them into a single trace)."; - Options["thin_walls"].cli = "thin-walls!"; - - Options["threads"].type = coInt; - Options["threads"].label = "Threads"; - Options["threads"].tooltip = "Threads are used to parallelize long-running tasks. Optimal threads number is slightly above the number of available cores/processors. Beware that more threads consume more memory."; - Options["threads"].sidetext = "(more speed but more memory usage)"; - Options["threads"].cli = "threads|j=i"; - Options["threads"].readonly = true; - Options["threads"].min = 1; - Options["threads"].max = 16; - - Options["toolchange_gcode"].type = coString; - Options["toolchange_gcode"].label = "Tool change G-code"; - Options["toolchange_gcode"].tooltip = "This custom code is inserted at every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; - Options["toolchange_gcode"].cli = "toolchange-gcode=s"; - Options["toolchange_gcode"].multiline = true; - Options["toolchange_gcode"].full_width = true; - Options["toolchange_gcode"].height = 50; - - Options["top_infill_extrusion_width"].type = coFloatOrPercent; - Options["top_infill_extrusion_width"].label = "Top solid infill"; - Options["top_infill_extrusion_width"].category = "Extrusion Width"; - Options["top_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) it will be computed over layer height."; - Options["top_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; - Options["top_infill_extrusion_width"].cli = "top-infill-extrusion-width=s"; - - Options["top_solid_infill_speed"].type = coFloatOrPercent; - Options["top_solid_infill_speed"].label = "Top solid infill"; - Options["top_solid_infill_speed"].category = "Speed"; - Options["top_solid_infill_speed"].tooltip = "Speed for printing top solid layers (it only applies to the uppermost external layers and not to their internal solid layers). You may want to slow down this to get a nicer surface finish. This can be expressed as a percentage (for example: 80%) over the solid infill speed above."; - Options["top_solid_infill_speed"].sidetext = "mm/s or %"; - Options["top_solid_infill_speed"].cli = "top-solid-infill-speed=s"; - Options["top_solid_infill_speed"].ratio_over = "solid_infill_speed"; - - Options["top_solid_layers"].type = coInt; - Options["top_solid_layers"].label = "Top"; - Options["top_solid_layers"].category = "Layers and Perimeters"; - Options["top_solid_layers"].tooltip = "Number of solid layers to generate on top surfaces."; - Options["top_solid_layers"].cli = "top-solid-layers=i"; - Options["top_solid_layers"].full_label = "Top solid layers"; - Options["top_solid_layers"].min = 0; - - Options["travel_speed"].type = coFloat; - Options["travel_speed"].label = "Travel"; - Options["travel_speed"].tooltip = "Speed for travel moves (jumps between distant extrusion points)."; - Options["travel_speed"].sidetext = "mm/s"; - Options["travel_speed"].cli = "travel-speed=f"; - Options["travel_speed"].aliases.push_back("travel_feed_rate"); - Options["travel_speed"].min = 0; - - Options["use_firmware_retraction"].type = coBool; - Options["use_firmware_retraction"].label = "Use firmware retraction"; - Options["use_firmware_retraction"].tooltip = "This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin."; - Options["use_firmware_retraction"].cli = "use-firmware-retraction!"; - - Options["use_relative_e_distances"].type = coBool; - Options["use_relative_e_distances"].label = "Use relative E distances"; - Options["use_relative_e_distances"].tooltip = "If your firmware requires relative E values, check this, otherwise leave it unchecked. Most firmwares use absolute values."; - Options["use_relative_e_distances"].cli = "use-relative-e-distances!"; - - Options["vibration_limit"].type = coFloat; - Options["vibration_limit"].label = "Vibration limit"; - Options["vibration_limit"].tooltip = "This experimental option will slow down those moves hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance. Set zero to disable."; - Options["vibration_limit"].sidetext = "Hz"; - Options["vibration_limit"].cli = "vibration-limit=f"; - Options["vibration_limit"].min = 0; - - Options["wipe"].type = coBools; - Options["wipe"].label = "Wipe while retracting"; - Options["wipe"].tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders."; - Options["wipe"].cli = "wipe!"; - - Options["xy_size_compensation"].type = coFloat; - Options["xy_size_compensation"].label = "XY Size Compensation"; - Options["xy_size_compensation"].category = "Advanced"; - Options["xy_size_compensation"].tooltip = "The object will be grown/shrunk in the XY plane by the configured value (negative = inwards, positive = outwards). This might be useful for fine-tuning hole sizes."; - Options["xy_size_compensation"].sidetext = "mm"; - Options["xy_size_compensation"].cli = "xy-size-compensation=f"; - - Options["z_offset"].type = coFloat; - Options["z_offset"].label = "Z offset"; - Options["z_offset"].tooltip = "This value will be added (or subtracted) from all the Z coordinates in the output G-code. It is used to compensate for bad Z endstop position: for example, if your endstop zero actually leaves the nozzle 0.3mm far from the print bed, set this to -0.3 (or fix your endstop)."; - Options["z_offset"].sidetext = "mm"; - Options["z_offset"].cli = "z-offset=f"; - - return Options; -}; - -t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def(); - -#ifdef SLIC3RXS -REGISTER_CLASS(DynamicPrintConfig, "Config"); -REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject"); -REGISTER_CLASS(PrintRegionConfig, "Config::PrintRegion"); -REGISTER_CLASS(PrintConfig, "Config::Print"); -REGISTER_CLASS(FullPrintConfig, "Config::Full"); -#endif - -} diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp deleted file mode 100644 index cdc3d7173..000000000 --- a/xs/src/PrintConfig.hpp +++ /dev/null @@ -1,567 +0,0 @@ -#ifndef slic3r_PrintConfig_hpp_ -#define slic3r_PrintConfig_hpp_ - -#include "Config.hpp" - -namespace Slic3r { - -enum GCodeFlavor { - gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfNoExtrusion, -}; - -enum InfillPattern { - ipRectilinear, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, -}; - -enum SupportMaterialPattern { - smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars, -}; - -enum SeamPosition { - spRandom, spNearest, spAligned -}; - -template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { - t_config_enum_values keys_map; - keys_map["reprap"] = gcfRepRap; - keys_map["teacup"] = gcfTeacup; - keys_map["makerware"] = gcfMakerWare; - keys_map["sailfish"] = gcfSailfish; - keys_map["mach3"] = gcfMach3; - keys_map["no-extrusion"] = gcfNoExtrusion; - return keys_map; -} - -template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { - t_config_enum_values keys_map; - keys_map["rectilinear"] = ipRectilinear; - keys_map["line"] = ipLine; - keys_map["concentric"] = ipConcentric; - keys_map["honeycomb"] = ipHoneycomb; - keys_map["3dhoneycomb"] = ip3DHoneycomb; - keys_map["hilbertcurve"] = ipHilbertCurve; - keys_map["archimedeanchords"] = ipArchimedeanChords; - keys_map["octagramspiral"] = ipOctagramSpiral; - return keys_map; -} - -template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { - t_config_enum_values keys_map; - keys_map["rectilinear"] = smpRectilinear; - keys_map["rectilinear-grid"] = smpRectilinearGrid; - keys_map["honeycomb"] = smpHoneycomb; - keys_map["pillars"] = smpPillars; - return keys_map; -} - -template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { - t_config_enum_values keys_map; - keys_map["random"] = spRandom; - keys_map["nearest"] = spNearest; - keys_map["aligned"] = spAligned; - return keys_map; -} - -class PrintConfigDef -{ - public: - static t_optiondef_map def; - - static t_optiondef_map build_def(); -}; - -class DynamicPrintConfig : public DynamicConfig -{ - public: - DynamicPrintConfig() { - this->def = &PrintConfigDef::def; - }; - - void normalize() { - if (this->has("extruder")) { - int extruder = this->option("extruder")->getInt(); - this->erase("extruder"); - if (extruder != 0) { - if (!this->has("infill_extruder")) - this->option("infill_extruder", true)->setInt(extruder); - if (!this->has("perimeter_extruder")) - this->option("perimeter_extruder", true)->setInt(extruder); - if (!this->has("support_material_extruder")) - this->option("support_material_extruder", true)->setInt(extruder); - if (!this->has("support_material_interface_extruder")) - this->option("support_material_interface_extruder", true)->setInt(extruder); - } - } - if (this->has("spiral_vase") && this->opt("spiral_vase", true)->value) { - { - // this should be actually done only on the spiral layers instead of all - ConfigOptionBools* opt = this->opt("retract_layer_change", true); - opt->values.assign(opt->values.size(), false); // set all values to false - } - } - }; -}; - -class StaticPrintConfig : public virtual StaticConfig -{ - public: - StaticPrintConfig() { - this->def = &PrintConfigDef::def; - }; -}; - -class PrintObjectConfig : public virtual StaticPrintConfig -{ - public: - ConfigOptionBool dont_support_bridges; - ConfigOptionFloatOrPercent extrusion_width; - ConfigOptionFloatOrPercent first_layer_height; - ConfigOptionBool infill_only_where_needed; - ConfigOptionBool interface_shells; - ConfigOptionFloat layer_height; - ConfigOptionInt raft_layers; - ConfigOptionEnum seam_position; - ConfigOptionBool support_material; - ConfigOptionInt support_material_angle; - ConfigOptionInt support_material_enforce_layers; - ConfigOptionInt support_material_extruder; - ConfigOptionFloatOrPercent support_material_extrusion_width; - ConfigOptionInt support_material_interface_extruder; - ConfigOptionInt support_material_interface_layers; - ConfigOptionFloat support_material_interface_spacing; - ConfigOptionFloatOrPercent support_material_interface_speed; - ConfigOptionEnum support_material_pattern; - ConfigOptionFloat support_material_spacing; - ConfigOptionFloat support_material_speed; - ConfigOptionInt support_material_threshold; - ConfigOptionFloat xy_size_compensation; - - PrintObjectConfig() : StaticPrintConfig() { - this->dont_support_bridges.value = true; - this->extrusion_width.value = 0; - this->extrusion_width.percent = false; - this->first_layer_height.value = 0.35; - this->first_layer_height.percent = false; - this->infill_only_where_needed.value = false; - this->interface_shells.value = false; - this->layer_height.value = 0.3; - this->raft_layers.value = 0; - this->seam_position.value = spAligned; - this->support_material.value = false; - this->support_material_angle.value = 0; - this->support_material_enforce_layers.value = 0; - this->support_material_extruder.value = 1; - this->support_material_extrusion_width.value = 0; - this->support_material_extrusion_width.percent = false; - this->support_material_interface_extruder.value = 1; - this->support_material_interface_layers.value = 3; - this->support_material_interface_spacing.value = 0; - this->support_material_interface_speed.value = 100; - this->support_material_interface_speed.percent = true; - this->support_material_pattern.value = smpPillars; - this->support_material_spacing.value = 2.5; - this->support_material_speed.value = 60; - this->support_material_threshold.value = 0; - this->xy_size_compensation.value = 0; - }; - - ConfigOption* option(const t_config_option_key opt_key, bool create = false) { - if (opt_key == "dont_support_bridges") return &this->dont_support_bridges; - if (opt_key == "extrusion_width") return &this->extrusion_width; - if (opt_key == "first_layer_height") return &this->first_layer_height; - if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; - if (opt_key == "interface_shells") return &this->interface_shells; - if (opt_key == "layer_height") return &this->layer_height; - if (opt_key == "raft_layers") return &this->raft_layers; - if (opt_key == "seam_position") return &this->seam_position; - if (opt_key == "support_material") return &this->support_material; - if (opt_key == "support_material_angle") return &this->support_material_angle; - if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers; - if (opt_key == "support_material_extruder") return &this->support_material_extruder; - if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width; - if (opt_key == "support_material_interface_extruder") return &this->support_material_interface_extruder; - if (opt_key == "support_material_interface_layers") return &this->support_material_interface_layers; - if (opt_key == "support_material_interface_spacing") return &this->support_material_interface_spacing; - if (opt_key == "support_material_interface_speed") return &this->support_material_interface_speed; - if (opt_key == "support_material_pattern") return &this->support_material_pattern; - if (opt_key == "support_material_spacing") return &this->support_material_spacing; - if (opt_key == "support_material_speed") return &this->support_material_speed; - if (opt_key == "support_material_threshold") return &this->support_material_threshold; - if (opt_key == "xy_size_compensation") return &this->xy_size_compensation; - - return NULL; - }; -}; - -class PrintRegionConfig : public virtual StaticPrintConfig -{ - public: - ConfigOptionInt bottom_solid_layers; - ConfigOptionFloat bridge_flow_ratio; - ConfigOptionFloat bridge_speed; - ConfigOptionFloatOrPercent external_perimeter_extrusion_width; - ConfigOptionFloatOrPercent external_perimeter_speed; - ConfigOptionBool external_perimeters_first; - ConfigOptionBool extra_perimeters; - ConfigOptionInt fill_angle; - ConfigOptionPercent fill_density; - ConfigOptionEnum fill_pattern; - ConfigOptionFloat gap_fill_speed; - ConfigOptionInt infill_extruder; - ConfigOptionFloatOrPercent infill_extrusion_width; - ConfigOptionInt infill_every_layers; - ConfigOptionFloat infill_speed; - ConfigOptionBool overhangs; - ConfigOptionInt perimeter_extruder; - ConfigOptionFloatOrPercent perimeter_extrusion_width; - ConfigOptionFloat perimeter_speed; - ConfigOptionInt perimeters; - ConfigOptionFloatOrPercent small_perimeter_speed; - ConfigOptionEnum solid_fill_pattern; - ConfigOptionFloat solid_infill_below_area; - ConfigOptionFloatOrPercent solid_infill_extrusion_width; - ConfigOptionInt solid_infill_every_layers; - ConfigOptionFloatOrPercent solid_infill_speed; - ConfigOptionBool thin_walls; - ConfigOptionFloatOrPercent top_infill_extrusion_width; - ConfigOptionInt top_solid_layers; - ConfigOptionFloatOrPercent top_solid_infill_speed; - - PrintRegionConfig() : StaticPrintConfig() { - this->bottom_solid_layers.value = 3; - this->bridge_flow_ratio.value = 1; - this->bridge_speed.value = 60; - this->external_perimeter_extrusion_width.value = 0; - this->external_perimeter_extrusion_width.percent = false; - this->external_perimeter_speed.value = 70; - this->external_perimeter_speed.percent = true; - this->external_perimeters_first.value = false; - this->extra_perimeters.value = true; - this->fill_angle.value = 45; - this->fill_density.value = 40; - this->fill_pattern.value = ipHoneycomb; - this->gap_fill_speed.value = 20; - this->infill_extruder.value = 1; - this->infill_extrusion_width.value = 0; - this->infill_extrusion_width.percent = false; - this->infill_every_layers.value = 1; - this->infill_speed.value = 60; - this->overhangs.value = true; - this->perimeter_extruder.value = 1; - this->perimeter_extrusion_width.value = 0; - this->perimeter_extrusion_width.percent = false; - this->perimeter_speed.value = 30; - this->perimeters.value = 3; - this->small_perimeter_speed.value = 30; - this->small_perimeter_speed.percent = false; - this->solid_fill_pattern.value = ipRectilinear; - this->solid_infill_below_area.value = 70; - this->solid_infill_extrusion_width.value = 0; - this->solid_infill_extrusion_width.percent = false; - this->solid_infill_every_layers.value = 0; - this->solid_infill_speed.value = 60; - this->solid_infill_speed.percent = false; - this->thin_walls.value = true; - this->top_infill_extrusion_width.value = 0; - this->top_infill_extrusion_width.percent = false; - this->top_solid_infill_speed.value = 50; - this->top_solid_infill_speed.percent = false; - this->top_solid_layers.value = 3; - }; - - ConfigOption* option(const t_config_option_key opt_key, bool create = false) { - if (opt_key == "bottom_solid_layers") return &this->bottom_solid_layers; - if (opt_key == "bridge_flow_ratio") return &this->bridge_flow_ratio; - if (opt_key == "bridge_speed") return &this->bridge_speed; - if (opt_key == "external_perimeter_extrusion_width") return &this->external_perimeter_extrusion_width; - if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed; - if (opt_key == "external_perimeters_first") return &this->external_perimeters_first; - if (opt_key == "extra_perimeters") return &this->extra_perimeters; - if (opt_key == "fill_angle") return &this->fill_angle; - if (opt_key == "fill_density") return &this->fill_density; - if (opt_key == "fill_pattern") return &this->fill_pattern; - if (opt_key == "gap_fill_speed") return &this->gap_fill_speed; - if (opt_key == "infill_extruder") return &this->infill_extruder; - if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; - if (opt_key == "infill_every_layers") return &this->infill_every_layers; - if (opt_key == "infill_speed") return &this->infill_speed; - if (opt_key == "overhangs") return &this->overhangs; - if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; - if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; - if (opt_key == "perimeter_speed") return &this->perimeter_speed; - if (opt_key == "perimeters") return &this->perimeters; - if (opt_key == "small_perimeter_speed") return &this->small_perimeter_speed; - if (opt_key == "solid_fill_pattern") return &this->solid_fill_pattern; - if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area; - if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width; - if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers; - if (opt_key == "solid_infill_speed") return &this->solid_infill_speed; - if (opt_key == "thin_walls") return &this->thin_walls; - if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width; - if (opt_key == "top_solid_infill_speed") return &this->top_solid_infill_speed; - if (opt_key == "top_solid_layers") return &this->top_solid_layers; - - return NULL; - }; -}; - -class PrintConfig : public virtual StaticPrintConfig -{ - public: - ConfigOptionBool avoid_crossing_perimeters; - ConfigOptionPoints bed_shape; - ConfigOptionInt bed_temperature; - ConfigOptionFloat bridge_acceleration; - ConfigOptionInt bridge_fan_speed; - ConfigOptionFloat brim_width; - ConfigOptionBool complete_objects; - ConfigOptionBool cooling; - ConfigOptionFloat default_acceleration; - ConfigOptionInt disable_fan_first_layers; - ConfigOptionFloat duplicate_distance; - ConfigOptionString end_gcode; - ConfigOptionFloat extruder_clearance_height; - ConfigOptionFloat extruder_clearance_radius; - ConfigOptionPoints extruder_offset; - ConfigOptionString extrusion_axis; - ConfigOptionFloats extrusion_multiplier; - ConfigOptionBool fan_always_on; - ConfigOptionInt fan_below_layer_time; - ConfigOptionFloats filament_diameter; - ConfigOptionFloat first_layer_acceleration; - ConfigOptionInt first_layer_bed_temperature; - ConfigOptionFloatOrPercent first_layer_extrusion_width; - ConfigOptionFloatOrPercent first_layer_speed; - ConfigOptionInts first_layer_temperature; - ConfigOptionBool g0; - ConfigOptionBool gcode_arcs; - ConfigOptionBool gcode_comments; - ConfigOptionEnum gcode_flavor; - ConfigOptionFloat infill_acceleration; - ConfigOptionBool infill_first; - ConfigOptionString layer_gcode; - ConfigOptionInt max_fan_speed; - ConfigOptionInt min_fan_speed; - ConfigOptionInt min_print_speed; - ConfigOptionFloat min_skirt_length; - ConfigOptionString notes; - ConfigOptionFloats nozzle_diameter; - ConfigOptionBool only_retract_when_crossing_perimeters; - ConfigOptionBool ooze_prevention; - ConfigOptionString output_filename_format; - ConfigOptionFloat perimeter_acceleration; - ConfigOptionStrings post_process; - ConfigOptionFloat resolution; - ConfigOptionFloats retract_before_travel; - ConfigOptionBools retract_layer_change; - ConfigOptionFloats retract_length; - ConfigOptionFloats retract_length_toolchange; - ConfigOptionFloats retract_lift; - ConfigOptionFloats retract_restart_extra; - ConfigOptionFloats retract_restart_extra_toolchange; - ConfigOptionInts retract_speed; - ConfigOptionFloat skirt_distance; - ConfigOptionInt skirt_height; - ConfigOptionInt skirts; - ConfigOptionInt slowdown_below_layer_time; - ConfigOptionBool spiral_vase; - ConfigOptionInt standby_temperature_delta; - ConfigOptionString start_gcode; - ConfigOptionInts temperature; - ConfigOptionInt threads; - ConfigOptionString toolchange_gcode; - ConfigOptionFloat travel_speed; - ConfigOptionBool use_firmware_retraction; - ConfigOptionBool use_relative_e_distances; - ConfigOptionFloat vibration_limit; - ConfigOptionBools wipe; - ConfigOptionFloat z_offset; - - PrintConfig() : StaticPrintConfig() { - this->avoid_crossing_perimeters.value = false; - this->bed_shape.values.push_back(Pointf(0,0)); - this->bed_shape.values.push_back(Pointf(200,0)); - this->bed_shape.values.push_back(Pointf(200,200)); - this->bed_shape.values.push_back(Pointf(0,200)); - this->bed_temperature.value = 0; - this->bridge_acceleration.value = 0; - this->bridge_fan_speed.value = 100; - this->brim_width.value = 0; - this->complete_objects.value = false; - this->cooling.value = true; - this->default_acceleration.value = 0; - this->disable_fan_first_layers.value = 1; - this->duplicate_distance.value = 6; - this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"; - this->extruder_clearance_height.value = 20; - this->extruder_clearance_radius.value = 20; - this->extruder_offset.values.resize(1); - this->extruder_offset.values[0] = Pointf(0,0); - this->extrusion_axis.value = "E"; - this->extrusion_multiplier.values.resize(1); - this->extrusion_multiplier.values[0] = 1; - this->fan_always_on.value = false; - this->fan_below_layer_time.value = 60; - this->filament_diameter.values.resize(1); - this->filament_diameter.values[0] = 3; - this->first_layer_acceleration.value = 0; - this->first_layer_bed_temperature.value = 0; - this->first_layer_extrusion_width.value = 200; - this->first_layer_extrusion_width.percent = true; - this->first_layer_speed.value = 30; - this->first_layer_speed.percent = true; - this->first_layer_temperature.values.resize(1); - this->first_layer_temperature.values[0] = 200; - this->g0.value = false; - this->gcode_arcs.value = false; - this->gcode_comments.value = false; - this->gcode_flavor.value = gcfRepRap; - this->infill_acceleration.value = 0; - this->infill_first.value = false; - this->layer_gcode.value = ""; - this->max_fan_speed.value = 100; - this->min_fan_speed.value = 35; - this->min_print_speed.value = 10; - this->min_skirt_length.value = 0; - this->notes.value = ""; - this->nozzle_diameter.values.resize(1); - this->nozzle_diameter.values[0] = 0.5; - this->only_retract_when_crossing_perimeters.value = true; - this->ooze_prevention.value = false; - this->output_filename_format.value = "[input_filename_base].gcode"; - this->perimeter_acceleration.value = 0; - this->resolution.value = 0; - this->retract_before_travel.values.resize(1); - this->retract_before_travel.values[0] = 2; - this->retract_layer_change.values.resize(1); - this->retract_layer_change.values[0] = true; - this->retract_length.values.resize(1); - this->retract_length.values[0] = 1; - this->retract_length_toolchange.values.resize(1); - this->retract_length_toolchange.values[0] = 10; - this->retract_lift.values.resize(1); - this->retract_lift.values[0] = 0; - this->retract_restart_extra.values.resize(1); - this->retract_restart_extra.values[0] = 0; - this->retract_restart_extra_toolchange.values.resize(1); - this->retract_restart_extra_toolchange.values[0] = 0; - this->retract_speed.values.resize(1); - this->retract_speed.values[0] = 30; - this->skirt_distance.value = 6; - this->skirt_height.value = 1; - this->skirts.value = 1; - this->slowdown_below_layer_time.value = 30; - this->spiral_vase.value = false; - this->standby_temperature_delta.value = -5; - this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"; - this->temperature.values.resize(1); - this->temperature.values[0] = 200; - this->threads.value = 2; - this->toolchange_gcode.value = ""; - this->travel_speed.value = 130; - this->use_firmware_retraction.value = false; - this->use_relative_e_distances.value = false; - this->vibration_limit.value = 0; - this->wipe.values.resize(1); - this->wipe.values[0] = false; - this->z_offset.value = 0; - }; - - ConfigOption* option(const t_config_option_key opt_key, bool create = false) { - if (opt_key == "avoid_crossing_perimeters") return &this->avoid_crossing_perimeters; - if (opt_key == "bed_shape") return &this->bed_shape; - if (opt_key == "bed_temperature") return &this->bed_temperature; - if (opt_key == "bridge_acceleration") return &this->bridge_acceleration; - if (opt_key == "bridge_fan_speed") return &this->bridge_fan_speed; - if (opt_key == "brim_width") return &this->brim_width; - if (opt_key == "complete_objects") return &this->complete_objects; - if (opt_key == "cooling") return &this->cooling; - if (opt_key == "default_acceleration") return &this->default_acceleration; - if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers; - if (opt_key == "duplicate_distance") return &this->duplicate_distance; - if (opt_key == "end_gcode") return &this->end_gcode; - if (opt_key == "extruder_clearance_height") return &this->extruder_clearance_height; - if (opt_key == "extruder_clearance_radius") return &this->extruder_clearance_radius; - if (opt_key == "extruder_offset") return &this->extruder_offset; - if (opt_key == "extrusion_axis") return &this->extrusion_axis; - if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier; - if (opt_key == "fan_always_on") return &this->fan_always_on; - if (opt_key == "fan_below_layer_time") return &this->fan_below_layer_time; - if (opt_key == "filament_diameter") return &this->filament_diameter; - if (opt_key == "first_layer_acceleration") return &this->first_layer_acceleration; - if (opt_key == "first_layer_bed_temperature") return &this->first_layer_bed_temperature; - if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width; - if (opt_key == "first_layer_speed") return &this->first_layer_speed; - if (opt_key == "first_layer_temperature") return &this->first_layer_temperature; - if (opt_key == "g0") return &this->g0; - if (opt_key == "gcode_arcs") return &this->gcode_arcs; - if (opt_key == "gcode_comments") return &this->gcode_comments; - if (opt_key == "gcode_flavor") return &this->gcode_flavor; - if (opt_key == "infill_acceleration") return &this->infill_acceleration; - if (opt_key == "infill_first") return &this->infill_first; - if (opt_key == "layer_gcode") return &this->layer_gcode; - if (opt_key == "max_fan_speed") return &this->max_fan_speed; - if (opt_key == "min_fan_speed") return &this->min_fan_speed; - if (opt_key == "min_print_speed") return &this->min_print_speed; - if (opt_key == "min_skirt_length") return &this->min_skirt_length; - if (opt_key == "notes") return &this->notes; - if (opt_key == "nozzle_diameter") return &this->nozzle_diameter; - if (opt_key == "only_retract_when_crossing_perimeters") return &this->only_retract_when_crossing_perimeters; - if (opt_key == "ooze_prevention") return &this->ooze_prevention; - if (opt_key == "output_filename_format") return &this->output_filename_format; - if (opt_key == "perimeter_acceleration") return &this->perimeter_acceleration; - if (opt_key == "post_process") return &this->post_process; - if (opt_key == "resolution") return &this->resolution; - if (opt_key == "retract_before_travel") return &this->retract_before_travel; - if (opt_key == "retract_layer_change") return &this->retract_layer_change; - if (opt_key == "retract_length") return &this->retract_length; - if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange; - if (opt_key == "retract_lift") return &this->retract_lift; - if (opt_key == "retract_restart_extra") return &this->retract_restart_extra; - if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange; - if (opt_key == "retract_speed") return &this->retract_speed; - if (opt_key == "skirt_distance") return &this->skirt_distance; - if (opt_key == "skirt_height") return &this->skirt_height; - if (opt_key == "skirts") return &this->skirts; - if (opt_key == "slowdown_below_layer_time") return &this->slowdown_below_layer_time; - if (opt_key == "spiral_vase") return &this->spiral_vase; - if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta; - if (opt_key == "start_gcode") return &this->start_gcode; - if (opt_key == "temperature") return &this->temperature; - if (opt_key == "threads") return &this->threads; - if (opt_key == "toolchange_gcode") return &this->toolchange_gcode; - if (opt_key == "travel_speed") return &this->travel_speed; - if (opt_key == "use_firmware_retraction") return &this->use_firmware_retraction; - if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances; - if (opt_key == "vibration_limit") return &this->vibration_limit; - if (opt_key == "wipe") return &this->wipe; - if (opt_key == "z_offset") return &this->z_offset; - - return NULL; - }; -}; - -class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig { - public: - ConfigOption* option(const t_config_option_key opt_key, bool create = false) { - ConfigOption* opt; - if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt; - if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt; - if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt; - return NULL; - }; - - std::string get_extrusion_axis() { - if (this->gcode_flavor == gcfMach3) { - return std::string("A"); - } else if (this->gcode_flavor == gcfNoExtrusion) { - return std::string(""); - } - return this->extrusion_axis; - } -}; - -} - -#endif diff --git a/xs/src/PrintObject.cpp b/xs/src/PrintObject.cpp deleted file mode 100644 index 4dac94cf6..000000000 --- a/xs/src/PrintObject.cpp +++ /dev/null @@ -1,254 +0,0 @@ -#include "Print.hpp" -#include "BoundingBox.hpp" - -namespace Slic3r { - -PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) -: _print(print), - _model_object(model_object), - typed_slices(false) -{ - region_volumes.resize(this->_print->regions.size()); - - // Compute the translation to be applied to our meshes so that we work with smaller coordinates - { - // Translate meshes so that our toolpath generation algorithms work with smaller - // XY coordinates; this translation is an optimization and not strictly required. - // A cloned mesh will be aligned to 0 before slicing in _slice_region() since we - // 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( - scale_(modobj_bbox.min.x), scale_(modobj_bbox.min.y)); - - // TODO: $self->_trigger_copies; - - // Scale the object size and store it - Pointf3 size = modobj_bbox.size(); - this->size = Point3(scale_(size.x), scale_(size.y), scale_(size.z)); - } -} - -PrintObject::~PrintObject() -{ -} - -Print* -PrintObject::print() -{ - return this->_print; -} - -ModelObject* -PrintObject::model_object() -{ - return this->_model_object; -} - -void -PrintObject::add_region_volume(int region_id, int volume_id) -{ - if (region_id >= region_volumes.size()) { - region_volumes.resize(region_id + 1); - } - - region_volumes[region_id].push_back(volume_id); -} - -size_t -PrintObject::layer_count() -{ - return this->layers.size(); -} - -void -PrintObject::clear_layers() -{ - for (int i = this->layers.size()-1; i >= 0; --i) - this->delete_layer(i); -} - -Layer* -PrintObject::get_layer(int idx) -{ - return this->layers.at(idx); -} - -Layer* -PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z) -{ - Layer* layer = new Layer(id, this, height, print_z, slice_z); - layers.push_back(layer); - return layer; -} - -void -PrintObject::delete_layer(int idx) -{ - LayerPtrs::iterator i = this->layers.begin() + idx; - delete *i; - this->layers.erase(i); -} - -size_t -PrintObject::support_layer_count() -{ - return this->support_layers.size(); -} - -void -PrintObject::clear_support_layers() -{ - for (int i = this->support_layers.size()-1; i >= 0; --i) - this->delete_support_layer(i); -} - -SupportLayer* -PrintObject::get_support_layer(int idx) -{ - return this->support_layers.at(idx); -} - -SupportLayer* -PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z, - coordf_t slice_z) -{ - SupportLayer* layer = new SupportLayer(id, this, height, print_z, slice_z); - support_layers.push_back(layer); - return layer; -} - -void -PrintObject::delete_support_layer(int idx) -{ - SupportLayerPtrs::iterator i = this->support_layers.begin() + idx; - delete *i; - this->support_layers.erase(i); -} - -bool -PrintObject::invalidate_state_by_config_options(const std::vector &opt_keys) -{ - std::set steps; - - // this method only accepts PrintObjectConfig and PrintRegionConfig option keys - for (std::vector::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) { - if (*opt_key == "perimeters" - || *opt_key == "extra_perimeters" - || *opt_key == "gap_fill_speed" - || *opt_key == "overhangs" - || *opt_key == "perimeter_extrusion_width" - || *opt_key == "thin_walls" - || *opt_key == "external_perimeters_first") { - steps.insert(posPerimeters); - } else if (*opt_key == "resolution" - || *opt_key == "layer_height" - || *opt_key == "first_layer_height" - || *opt_key == "xy_size_compensation" - || *opt_key == "raft_layers") { - steps.insert(posSlice); - } else if (*opt_key == "support_material" - || *opt_key == "support_material_angle" - || *opt_key == "support_material_extruder" - || *opt_key == "support_material_extrusion_width" - || *opt_key == "support_material_interface_layers" - || *opt_key == "support_material_interface_extruder" - || *opt_key == "support_material_interface_spacing" - || *opt_key == "support_material_interface_speed" - || *opt_key == "support_material_pattern" - || *opt_key == "support_material_spacing" - || *opt_key == "support_material_threshold" - || *opt_key == "dont_support_bridges") { - steps.insert(posSupportMaterial); - } else if (*opt_key == "interface_shells" - || *opt_key == "infill_only_where_needed" - || *opt_key == "bottom_solid_layers" - || *opt_key == "top_solid_layers" - || *opt_key == "infill_extruder" - || *opt_key == "infill_extrusion_width") { - steps.insert(posPrepareInfill); - } else if (*opt_key == "fill_angle" - || *opt_key == "fill_pattern" - || *opt_key == "solid_fill_pattern" - || *opt_key == "infill_every_layers" - || *opt_key == "solid_infill_below_area" - || *opt_key == "solid_infill_every_layers" - || *opt_key == "top_infill_extrusion_width") { - steps.insert(posInfill); - } else if (*opt_key == "fill_density" - || *opt_key == "solid_infill_extrusion_width") { - steps.insert(posPerimeters); - steps.insert(posPrepareInfill); - } else if (*opt_key == "external_perimeter_extrusion_width" - || *opt_key == "perimeter_extruder") { - steps.insert(posPerimeters); - steps.insert(posSupportMaterial); - } else if (*opt_key == "bridge_flow_ratio") { - steps.insert(posPerimeters); - steps.insert(posInfill); - } else if (*opt_key == "seam_position" - || *opt_key == "support_material_speed" - || *opt_key == "bridge_speed" - || *opt_key == "external_perimeter_speed" - || *opt_key == "infill_speed" - || *opt_key == "perimeter_speed" - || *opt_key == "small_perimeter_speed" - || *opt_key == "solid_infill_speed" - || *opt_key == "top_solid_infill_speed") { - // 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 - return this->invalidate_all_steps(); - } - } - - bool invalidated = false; - for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { - if (this->invalidate_step(*step)) invalidated = true; - } - - return invalidated; -} - -bool -PrintObject::invalidate_step(PrintObjectStep step) -{ - bool invalidated = this->state.invalidate(step); - - // propagate to dependent steps - if (step == posPerimeters) { - this->invalidate_step(posPrepareInfill); - this->_print->invalidate_step(psSkirt); - this->_print->invalidate_step(psBrim); - } else if (step == posPrepareInfill) { - this->invalidate_step(posInfill); - } else if (step == posInfill) { - this->_print->invalidate_step(psSkirt); - this->_print->invalidate_step(psBrim); - } else if (step == posSlice) { - this->invalidate_step(posPerimeters); - this->invalidate_step(posSupportMaterial); - } - - return invalidated; -} - -bool -PrintObject::invalidate_all_steps() -{ - // make a copy because when invalidating steps the iterators are not working anymore - std::set steps = this->state.started; - - bool invalidated = false; - for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { - if (this->invalidate_step(*step)) invalidated = true; - } - return invalidated; -} - - -#ifdef SLIC3RXS -REGISTER_CLASS(PrintObject, "Print::Object"); -#endif - -} diff --git a/xs/src/PrintRegion.cpp b/xs/src/PrintRegion.cpp deleted file mode 100644 index d651b57ab..000000000 --- a/xs/src/PrintRegion.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "Print.hpp" - -namespace Slic3r { - -PrintRegion::PrintRegion(Print* print) - : _print(print) -{ -} - -PrintRegion::~PrintRegion() -{ -} - -Print* -PrintRegion::print() -{ - return this->_print; -} - -Flow -PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const -{ - ConfigOptionFloatOrPercent config_width; - if (width != -1) { - // use the supplied custom width, if any - config_width.value = width; - config_width.percent = false; - } else { - // otherwise, get extrusion width from configuration - // (might be an absolute value, or a percent value, or zero for auto) - if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { - config_width = this->_print->config.first_layer_extrusion_width; - } else if (role == frExternalPerimeter) { - config_width = this->config.external_perimeter_extrusion_width; - } else if (role == frPerimeter) { - config_width = this->config.perimeter_extrusion_width; - } else if (role == frInfill) { - config_width = this->config.infill_extrusion_width; - } else if (role == frSolidInfill) { - config_width = this->config.solid_infill_extrusion_width; - } else if (role == frTopSolidInfill) { - config_width = this->config.top_infill_extrusion_width; - } else { - CONFESS("Unknown role"); - } - } - if (config_width.value == 0) { - config_width = object.config.extrusion_width; - } - - // get the configured nozzle_diameter for the extruder associated - // to the flow role requested - size_t extruder; // 1-based - if (role == frPerimeter || role == frExternalPerimeter) { - extruder = this->config.perimeter_extruder; - } else if (role == frInfill || role == frSolidInfill || role == frTopSolidInfill) { - extruder = this->config.infill_extruder; - } else { - CONFESS("Unknown role $role"); - } - double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1); - - return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? this->config.bridge_flow_ratio : 0); -} - -#ifdef SLIC3RXS -REGISTER_CLASS(PrintRegion, "Print::Region"); -#endif - -} diff --git a/xs/src/SVG.cpp b/xs/src/SVG.cpp deleted file mode 100644 index db5ec7293..000000000 --- a/xs/src/SVG.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "SVG.hpp" - -namespace Slic3r { - -SVG::SVG(const char* filename) -{ - this->f = fopen(filename, "w"); - fprintf(this->f, - "\n" - "\n" - "\n" - " \n" - " \n" - " \n" - ); - this->arrows = true; -} - -float -SVG::coordinate(long c) -{ - return (float)unscale(c)*10; -} - -void -SVG::AddLine(const Line &line) -{ - fprintf(this->f, - " coordinate(line.a.x), this->coordinate(line.a.y), this->coordinate(line.b.x), this->coordinate(line.b.y) - ); - if (this->arrows) - fprintf(this->f, " marker-end=\"url(#endArrow)\""); - fprintf(this->f, "/>\n"); -} - -void -SVG::AddLine(const IntersectionLine &line) -{ - this->AddLine(Line(line.a, line.b)); -} - -void -SVG::Close() -{ - fprintf(this->f, "\n"); - fclose(this->f); - printf("SVG file written.\n"); -} - -} diff --git a/xs/src/SVG.hpp b/xs/src/SVG.hpp deleted file mode 100644 index 5d4cfd56e..000000000 --- a/xs/src/SVG.hpp +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef slic3r_SVG_hpp_ -#define slic3r_SVG_hpp_ - -#include -#include "Line.hpp" -#include "TriangleMesh.hpp" - -namespace Slic3r { - -class SVG -{ - private: - FILE* f; - float coordinate(long c); - public: - bool arrows; - SVG(const char* filename); - void AddLine(const Line &line); - void AddLine(const IntersectionLine &line); - void Close(); -}; - -} - -#endif diff --git a/xs/src/Surface.cpp b/xs/src/Surface.cpp deleted file mode 100644 index a53cb2513..000000000 --- a/xs/src/Surface.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "Surface.hpp" - -namespace Slic3r { - -double -Surface::area() const -{ - return this->expolygon.area(); -} - -bool -Surface::is_solid() const -{ - return this->surface_type == stTop - || this->surface_type == stBottom - || this->surface_type == stBottomBridge - || this->surface_type == stInternalSolid; -} - -bool -Surface::is_external() const -{ - return this->surface_type == stTop - || this->surface_type == stBottom - || this->surface_type == stBottomBridge; -} - -bool -Surface::is_bottom() const -{ - return this->surface_type == stBottom - || this->surface_type == stBottomBridge; -} - -bool -Surface::is_bridge() const -{ - return this->surface_type == stBottomBridge - || this->surface_type == stInternalBridge; -} - -#ifdef SLIC3RXS - -REGISTER_CLASS(Surface, "Surface"); - -void -Surface::from_SV_check(SV* surface_sv) -{ - if (!sv_isa(surface_sv, perl_class_name(this)) && !sv_isa(surface_sv, perl_class_name_ref(this))) - CONFESS("Not a valid %s object", perl_class_name(this)); - // a XS Surface was supplied - *this = *(Surface *)SvIV((SV*)SvRV( surface_sv )); -} -#endif - -} diff --git a/xs/src/Surface.hpp b/xs/src/Surface.hpp deleted file mode 100644 index ce0f02477..000000000 --- a/xs/src/Surface.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef slic3r_Surface_hpp_ -#define slic3r_Surface_hpp_ - -#include "ExPolygon.hpp" - -namespace Slic3r { - -enum SurfaceType { stTop, stBottom, stBottomBridge, stInternal, stInternalSolid, stInternalBridge, stInternalVoid }; - -class Surface -{ - public: - ExPolygon expolygon; - SurfaceType surface_type; - double thickness; // in mm - unsigned short thickness_layers; // in layers - double bridge_angle; // in radians, ccw, 0 = East, only 0+ (negative means undefined) - unsigned short extra_perimeters; - double area() const; - bool is_solid() const; - bool is_external() const; - bool is_bottom() const; - bool is_bridge() const; - - #ifdef SLIC3RXS - void from_SV_check(SV* surface_sv); - #endif -}; - -typedef std::vector Surfaces; -typedef std::vector SurfacesPtr; - -} - -#endif diff --git a/xs/src/SurfaceCollection.cpp b/xs/src/SurfaceCollection.cpp deleted file mode 100644 index 1590e7a21..000000000 --- a/xs/src/SurfaceCollection.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "SurfaceCollection.hpp" -#include - -namespace Slic3r { - -SurfaceCollection::operator Polygons() const -{ - Polygons polygons; - for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - Polygons surface_p = surface->expolygon; - polygons.insert(polygons.end(), surface_p.begin(), surface_p.end()); - } - return polygons; -} - -SurfaceCollection::operator ExPolygons() const -{ - ExPolygons expp; - expp.reserve(this->surfaces.size()); - for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - expp.push_back(surface->expolygon); - } - return expp; -} - -void -SurfaceCollection::simplify(double tolerance) -{ - Surfaces ss; - for (Surfaces::const_iterator it_s = this->surfaces.begin(); it_s != this->surfaces.end(); ++it_s) { - ExPolygons expp; - it_s->expolygon.simplify(tolerance, expp); - for (ExPolygons::const_iterator it_e = expp.begin(); it_e != expp.end(); ++it_e) { - Surface s = *it_s; - s.expolygon = *it_e; - ss.push_back(s); - } - } - this->surfaces = ss; -} - -/* group surfaces by common properties */ -void -SurfaceCollection::group(std::vector *retval) -{ - for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) { - // find a group with the same properties - SurfacesPtr* group = NULL; - for (std::vector::iterator git = retval->begin(); git != retval->end(); ++git) { - Surface* gkey = git->front(); - if ( gkey->surface_type == it->surface_type - && gkey->thickness == it->thickness - && gkey->thickness_layers == it->thickness_layers - && gkey->bridge_angle == it->bridge_angle) { - group = &*git; - break; - } - } - - // if no group with these properties exists, add one - if (group == NULL) { - retval->resize(retval->size() + 1); - group = &retval->back(); - } - - // append surface to group - group->push_back(&*it); - } -} - -#ifdef SLIC3RXS -REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); -#endif - -} diff --git a/xs/src/SurfaceCollection.hpp b/xs/src/SurfaceCollection.hpp deleted file mode 100644 index fe3fae8c6..000000000 --- a/xs/src/SurfaceCollection.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef slic3r_SurfaceCollection_hpp_ -#define slic3r_SurfaceCollection_hpp_ - -#include "Surface.hpp" -#include - -namespace Slic3r { - -class SurfaceCollection -{ - public: - Surfaces surfaces; - - operator Polygons() const; - operator ExPolygons() const; - void simplify(double tolerance); - void group(std::vector *retval); -}; - -} - -#endif diff --git a/xs/src/TriangleMesh.cpp b/xs/src/TriangleMesh.cpp deleted file mode 100644 index 77b11c5f8..000000000 --- a/xs/src/TriangleMesh.cpp +++ /dev/null @@ -1,1070 +0,0 @@ -#include "TriangleMesh.hpp" -#include "ClipperUtils.hpp" -#include "Geometry.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef SLIC3R_DEBUG -#include "SVG.hpp" -#endif - -namespace Slic3r { - -TriangleMesh::TriangleMesh() - : repaired(false) -{ - stl_initialize(&this->stl); -} - -TriangleMesh::TriangleMesh(const TriangleMesh &other) - : stl(other.stl), repaired(other.repaired) -{ - this->stl.heads = NULL; - this->stl.tail = NULL; - if (other.stl.facet_start != NULL) { - this->stl.facet_start = (stl_facet*)calloc(other.stl.stats.number_of_facets, sizeof(stl_facet)); - std::copy(other.stl.facet_start, other.stl.facet_start + other.stl.stats.number_of_facets, this->stl.facet_start); - } - if (other.stl.neighbors_start != NULL) { - this->stl.neighbors_start = (stl_neighbors*)calloc(other.stl.stats.number_of_facets, sizeof(stl_neighbors)); - std::copy(other.stl.neighbors_start, other.stl.neighbors_start + other.stl.stats.number_of_facets, this->stl.neighbors_start); - } - if (other.stl.v_indices != NULL) { - this->stl.v_indices = (v_indices_struct*)calloc(other.stl.stats.number_of_facets, sizeof(v_indices_struct)); - std::copy(other.stl.v_indices, other.stl.v_indices + other.stl.stats.number_of_facets, this->stl.v_indices); - } - if (other.stl.v_shared != NULL) { - this->stl.v_shared = (stl_vertex*)calloc(other.stl.stats.shared_vertices, sizeof(stl_vertex)); - std::copy(other.stl.v_shared, other.stl.v_shared + other.stl.stats.shared_vertices, this->stl.v_shared); - } -} - -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); - std::swap(this->stl.facet_start, other.stl.facet_start); - std::swap(this->stl.neighbors_start, other.stl.neighbors_start); - std::swap(this->stl.v_indices, other.stl.v_indices); - std::swap(this->stl.v_shared, other.stl.v_shared); -} - -TriangleMesh::~TriangleMesh() { - stl_close(&this->stl); -} - -void -TriangleMesh::ReadSTLFile(char* input_file) { - stl_open(&stl, input_file); -} - -void -TriangleMesh::write_ascii(char* output_file) -{ - stl_write_ascii(&this->stl, output_file, ""); -} - -void -TriangleMesh::write_binary(char* output_file) -{ - stl_write_binary(&this->stl, output_file, ""); -} - -void -TriangleMesh::repair() { - if (this->repaired) return; - - // admesh fails when repairing empty meshes - if (this->stl.stats.number_of_facets == 0) return; - - // checking exact - stl_check_facets_exact(&stl); - stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); - stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge); - stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge); - - // checking nearby - int last_edges_fixed = 0; - float tolerance = stl.stats.shortest_edge; - float increment = stl.stats.bounding_diameter / 10000.0; - int iterations = 2; - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { - for (int i = 0; i < iterations; i++) { - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { - //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); - stl_check_facets_nearby(&stl, tolerance); - //printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed); - last_edges_fixed = stl.stats.edges_fixed; - tolerance += increment; - } else { - break; - } - } - } - - // remove_unconnected - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { - stl_remove_unconnected_facets(&stl); - } - - // fill_holes - if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { - stl_fill_holes(&stl); - } - - // normal_directions - stl_fix_normal_directions(&stl); - - // normal_values - stl_fix_normal_values(&stl); - - // always calculate the volume and reverse all normals if volume is negative - stl_calculate_volume(&stl); - - // neighbors - stl_verify_neighbors(&stl); - - this->repaired = true; -} - -void -TriangleMesh::reset_repair_stats() { - this->stl.stats.degenerate_facets = 0; - this->stl.stats.edges_fixed = 0; - this->stl.stats.facets_removed = 0; - this->stl.stats.facets_added = 0; - this->stl.stats.facets_reversed = 0; - this->stl.stats.backwards_edges = 0; - this->stl.stats.normals_fixed = 0; -} - -void -TriangleMesh::WriteOBJFile(char* output_file) { - stl_generate_shared_vertices(&stl); - stl_write_obj(&stl, output_file); -} - -void TriangleMesh::scale(float factor) -{ - stl_scale(&(this->stl), factor); -} - -void TriangleMesh::scale(std::vector versor) -{ - float fversor[3]; - fversor[0] = versor[0]; - fversor[1] = versor[1]; - fversor[2] = versor[2]; - stl_scale_versor(&this->stl, fversor); -} - -void TriangleMesh::translate(float x, float y, float z) -{ - stl_translate_relative(&(this->stl), x, y, z); -} - -void TriangleMesh::rotate_x(float angle) -{ - stl_rotate_x(&(this->stl), angle); -} - -void TriangleMesh::rotate_y(float angle) -{ - stl_rotate_y(&(this->stl), angle); -} - -void TriangleMesh::rotate_z(float angle) -{ - stl_rotate_z(&(this->stl), angle); -} - -void TriangleMesh::flip_x() -{ - stl_mirror_yz(&this->stl); -} - -void TriangleMesh::flip_y() -{ - stl_mirror_xz(&this->stl); -} - -void TriangleMesh::flip_z() -{ - stl_mirror_xy(&this->stl); -} - -void TriangleMesh::align_to_origin() -{ - this->translate( - -(this->stl.stats.min.x), - -(this->stl.stats.min.y), - -(this->stl.stats.min.z) - ); -} - -void TriangleMesh::rotate(double angle, Point* center) -{ - this->translate(-center->x, -center->y, 0); - stl_rotate_z(&(this->stl), (float)angle); - this->translate(+center->x, +center->y, 0); -} - -TriangleMeshPtrs -TriangleMesh::split() const -{ - TriangleMeshPtrs meshes; - std::set seen_facets; - - // we need neighbors - if (!this->repaired) CONFESS("split() requires repair()"); - - // loop while we have remaining facets - while (1) { - // get the first facet - std::queue facet_queue; - std::deque 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 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()) { - 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]); - } - seen_facets.insert(facet_idx); - } - - TriangleMesh* mesh = new TriangleMesh; - meshes.push_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_allocate(&mesh->stl); - - int first = 1; - for (std::deque::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) -{ - // reset stats and metadata - int number_of_facets = this->stl.stats.number_of_facets; - stl_invalidate_shared_vertices(&this->stl); - this->repaired = false; - - // update facet count and allocate more memory - this->stl.stats.number_of_facets = number_of_facets + mesh->stl.stats.number_of_facets; - this->stl.stats.original_num_facets = this->stl.stats.number_of_facets; - stl_reallocate(&this->stl); - - // copy facets - for (int i = 0; i < mesh->stl.stats.number_of_facets; i++) { - this->stl.facet_start[number_of_facets + i] = mesh->stl.facet_start[i]; - } - - // update size - stl_get_size(&this->stl); -} - -/* this will return scaled ExPolygons */ -void -TriangleMesh::horizontal_projection(ExPolygons &retval) const -{ - Polygons pp; - pp.reserve(this->stl.stats.number_of_facets); - for (int i = 0; i < this->stl.stats.number_of_facets; i++) { - stl_facet* facet = &this->stl.facet_start[i]; - Polygon p; - p.points.resize(3); - p.points[0] = Point(facet->vertex[0].x / SCALING_FACTOR, facet->vertex[0].y / SCALING_FACTOR); - p.points[1] = Point(facet->vertex[1].x / SCALING_FACTOR, facet->vertex[1].y / SCALING_FACTOR); - p.points[2] = Point(facet->vertex[2].x / SCALING_FACTOR, facet->vertex[2].y / SCALING_FACTOR); - p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that - pp.push_back(p); - } - - // the offset factor was tuned using groovemount.stl - offset(pp, pp, 0.01 / SCALING_FACTOR); - union_(pp, retval, true); -} - -void -TriangleMesh::convex_hull(Polygon* 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.push_back(Point(v->x / SCALING_FACTOR, v->y / SCALING_FACTOR)); - } - Slic3r::Geometry::convex_hull(pp, hull); -} - -void -TriangleMesh::bounding_box(BoundingBoxf3* bb) const -{ - 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; -} - -void -TriangleMesh::require_shared_vertices() -{ - if (!this->repaired) this->repair(); - if (this->stl.v_shared == NULL) stl_generate_shared_vertices(&(this->stl)); -} - -#ifdef SLIC3RXS - -REGISTER_CLASS(TriangleMesh, "TriangleMesh"); - -SV* -TriangleMesh::to_SV() { - SV* sv = newSV(0); - sv_setref_pv( sv, perl_class_name(this), (void*)this ); - return sv; -} - -void TriangleMesh::ReadFromPerl(SV* vertices, SV* facets) -{ - stl.stats.type = inmemory; - - // count facets and allocate memory - AV* facets_av = (AV*)SvRV(facets); - stl.stats.number_of_facets = av_len(facets_av)+1; - stl.stats.original_num_facets = stl.stats.number_of_facets; - stl_allocate(&stl); - - // read geometry - AV* vertices_av = (AV*)SvRV(vertices); - for (unsigned int i = 0; i < stl.stats.number_of_facets; i++) { - AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0)); - stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = 0; - for (unsigned int v = 0; v <= 2; v++) { - AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, SvIV(*av_fetch(facet_av, v, 0)), 0)); - facet.vertex[v].x = SvNV(*av_fetch(vertex_av, 0, 0)); - facet.vertex[v].y = SvNV(*av_fetch(vertex_av, 1, 0)); - facet.vertex[v].z = SvNV(*av_fetch(vertex_av, 2, 0)); - } - facet.extra[0] = 0; - facet.extra[1] = 0; - - stl.facet_start[i] = facet; - } - - stl_get_size(&(this->stl)); -} -#endif - -void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) -{ - /* - This method gets called with a list of unscaled Z coordinates and outputs - a vector pointer having the same number of items as the original list. - Each item is a vector of polygons created by slicing our mesh at the - given heights. - - This method should basically combine the behavior of the existing - Perl methods defined in lib/Slic3r/TriangleMesh.pm: - - - analyze(): this creates the 'facets_edges' and the 'edges_facets' - tables (we don't need the 'edges' table) - - - slice_facet(): this has to be done for each facet. It generates - intersection lines with each plane identified by the Z list. - The get_layer_range() binary search used to identify the Z range - of the facet is already ported to C++ (see Object.xsp) - - - make_loops(): this has to be done for each layer. It creates polygons - from the lines generated by the previous step. - - At the end, we free the tables generated by analyze() as we don't - need them anymore. - FUTURE: parallelize slice_facet() and make_loops() - - NOTE: this method accepts a vector of floats because the mesh coordinate - type is float. - */ - - std::vector lines(z.size()); - - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { - stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; - - // find facet extents - float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); - - #ifdef SLIC3R_DEBUG - printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z, - facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z, - facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z); - printf("z: min = %.2f, max = %.2f\n", min_z, max_z); - #endif - - // find layer extents - std::vector::const_iterator min_layer, max_layer; - min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z - max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z - #ifdef SLIC3R_DEBUG - printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); - #endif - - for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { - std::vector::size_type layer_idx = it - z.begin(); - this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]); - } - } - - // v_scaled_shared could be freed here - - // build loops - layers->resize(z.size()); - for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { - int layer_idx = it - lines.begin(); - #ifdef SLIC3R_DEBUG - printf("Layer %d:\n", layer_idx); - #endif - - this->make_loops(*it, &(*layers)[layer_idx]); - } -} - -void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) -{ - std::vector layers_p; - this->slice(z, &layers_p); - - layers->resize(z.size()); - for (std::vector::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) { - #ifdef SLIC3R_DEBUG - size_t layer_id = loops - layers_p.begin(); - printf("Layer %zu (slice_z = %.2f): ", layer_id, z[layer_id]); - #endif - - this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]); - } -} - -void -TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const -{ - std::vector points; - std::vector< std::vector::size_type > points_on_layer; - bool found_horizontal_edge = false; - - /* 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 = 0; - if (facet.vertex[1].z == min_z) { - // vertex 1 has lowest Z - i = 1; - } else if (facet.vertex[2].z == min_z) { - // vertex 2 has lowest Z - i = 2; - } - for (int j = i; (j-i) < 3; j++) { // loop through facet edges - int edge_id = this->facets_edges[facet_idx][j % 3]; - int a_id = this->mesh->stl.v_indices[facet_idx].vertex[j % 3]; - int b_id = this->mesh->stl.v_indices[facet_idx].vertex[(j+1) % 3]; - stl_vertex* a = &this->v_scaled_shared[a_id]; - stl_vertex* b = &this->v_scaled_shared[b_id]; - - if (a->z == b->z && a->z == slice_z) { - // edge is horizontal and belongs to the current layer - - /* We assume that this method is never being called for horizontal - facets, so no other edge is going to be on this layer. */ - stl_vertex* v0 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; - stl_vertex* v1 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ]; - stl_vertex* v2 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ]; - IntersectionLine line; - if (min_z == max_z) { - line.edge_type = feHorizontal; - } else if (v0->z < slice_z || v1->z < slice_z || v2->z < slice_z) { - line.edge_type = feTop; - std::swap(a, b); - std::swap(a_id, b_id); - } else { - line.edge_type = feBottom; - } - line.a.x = a->x; - line.a.y = a->y; - line.b.x = b->x; - line.b.y = b->y; - line.a_id = a_id; - line.b_id = b_id; - lines->push_back(line); - - found_horizontal_edge = true; - - // if this is a top or bottom edge, we can stop looping through edges - // because we won't find anything interesting - - if (line.edge_type != feHorizontal) return; - } else if (a->z == slice_z) { - IntersectionPoint point; - point.x = a->x; - point.y = a->y; - point.point_id = a_id; - points.push_back(point); - points_on_layer.push_back(points.size()-1); - } else if (b->z == slice_z) { - IntersectionPoint point; - point.x = b->x; - point.y = b->y; - point.point_id = b_id; - points.push_back(point); - points_on_layer.push_back(points.size()-1); - } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { - // edge intersects the current layer; calculate intersection - - IntersectionPoint point; - 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.edge_id = edge_id; - points.push_back(point); - } - } - if (found_horizontal_edge) return; - - if (!points_on_layer.empty()) { - // we can't have only one point on layer because each vertex gets detected - // twice (once for each edge), and we can't have three points on layer because - // we assume this code is not getting called for horizontal facets - assert(points_on_layer.size() == 2); - assert( points[ points_on_layer[0] ].point_id == points[ points_on_layer[1] ].point_id ); - if (points.size() < 3) return; // no intersection point, this is a V-shaped facet tangent to plane - points.erase( points.begin() + points_on_layer[1] ); - } - - if (!points.empty()) { - assert(points.size() == 2); // facets must intersect each plane 0 or 2 times - IntersectionLine line; - line.a.x = points[1].x; - line.a.y = points[1].y; - line.b.x = points[0].x; - line.b.y = points[0].y; - line.a_id = points[1].point_id; - line.b_id = points[0].point_id; - line.edge_a_id = points[1].edge_id; - line.edge_b_id = points[0].edge_id; - lines->push_back(line); - return; - } -} - -void -TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) -{ - - /* - SVG svg("lines.svg"); - for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { - svg.AddLine(*line); - } - svg.Close(); - */ - - // remove tangent edges - for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { - if (line->skip || line->edge_type == feNone) continue; - - /* if the line is a facet edge, find another facet edge - having the same endpoints but in reverse order */ - for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++line2) { - if (line2->skip || line2->edge_type == feNone) continue; - - // are these facets adjacent? (sharing a common edge on this layer) - if (line->a_id == line2->a_id && line->b_id == line2->b_id) { - line2->skip = true; - - /* if they are both oriented upwards or downwards (like a 'V') - then we can remove both edges from this layer since it won't - affect the sliced shape */ - /* if one of them is oriented upwards and the other is oriented - downwards, let's only keep one of them (it doesn't matter which - one since all 'top' lines were reversed at slicing) */ - if (line->edge_type == line2->edge_type) { - line->skip = true; - break; - } - } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) { - /* if this edge joins two horizontal facets, remove both of them */ - if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) { - line->skip = true; - line2->skip = true; - break; - } - } - } - } - - // build a map of lines by edge_a_id and a_id - std::vector by_edge_a_id, by_a_id; - by_edge_a_id.resize(this->mesh->stl.stats.number_of_facets * 3); - by_a_id.resize(this->mesh->stl.stats.shared_vertices); - for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { - if (line->skip) continue; - if (line->edge_a_id != -1) by_edge_a_id[line->edge_a_id].push_back(&(*line)); - if (line->a_id != -1) by_a_id[line->a_id].push_back(&(*line)); - } - - CYCLE: while (1) { - // take first spare line and start a new loop - IntersectionLine* first_line = NULL; - for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { - if (line->skip) continue; - first_line = &(*line); - break; - } - if (first_line == NULL) break; - first_line->skip = true; - IntersectionLinePtrs loop; - loop.push_back(first_line); - - /* - printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", - first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id, - first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y); - */ - - while (1) { - // find a line starting where last one finishes - IntersectionLine* next_line = NULL; - if (loop.back()->edge_b_id != -1) { - IntersectionLinePtrs* candidates = &(by_edge_a_id[loop.back()->edge_b_id]); - for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) { - if ((*lineptr)->skip) continue; - next_line = *lineptr; - break; - } - } - if (next_line == NULL && loop.back()->b_id != -1) { - IntersectionLinePtrs* candidates = &(by_a_id[loop.back()->b_id]); - for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) { - if ((*lineptr)->skip) continue; - next_line = *lineptr; - break; - } - } - - if (next_line == NULL) { - // check whether we closed this loop - if ((loop.front()->edge_a_id != -1 && loop.front()->edge_a_id == loop.back()->edge_b_id) - || (loop.front()->a_id != -1 && loop.front()->a_id == loop.back()->b_id)) { - // loop is complete - Polygon p; - p.points.reserve(loop.size()); - for (IntersectionLinePtrs::iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) { - p.points.push_back((*lineptr)->a); - } - loops->push_back(p); - - #ifdef SLIC3R_DEBUG - printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); - #endif - - goto CYCLE; - } - - // we can't close this loop! - //// push @failed_loops, [@loop]; - //#ifdef SLIC3R_DEBUG - printf(" Unable to close this loop having %d points\n", (int)loop.size()); - //#endif - goto CYCLE; - } - /* - printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", - next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id, - next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y); - */ - loop.push_back(next_line); - next_line->skip = true; - } - } -} - -class _area_comp { - public: - _area_comp(std::vector* _aa) : abs_area(_aa) {}; - bool operator() (const size_t &a, const size_t &b) { - return (*this->abs_area)[a] > (*this->abs_area)[b]; - } - - private: - std::vector* abs_area; -}; - -void -TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) -{ - Polygons loops; - this->make_loops(lines, &loops); - - Polygons cw; - for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++loop) { - if (loop->area() >= 0) { - ExPolygon ex; - ex.contour = *loop; - slices->push_back(ex); - } else { - cw.push_back(*loop); - } - } - - // assign holes to contours - for (Polygons::const_iterator loop = cw.begin(); loop != cw.end(); ++loop) { - int slice_idx = -1; - double current_contour_area = -1; - for (ExPolygons::iterator slice = slices->begin(); slice != slices->end(); ++slice) { - if (slice->contour.contains_point(loop->points.front())) { - double area = slice->contour.area(); - if (area < current_contour_area || current_contour_area == -1) { - slice_idx = slice - slices->begin(); - current_contour_area = area; - } - } - } - (*slices)[slice_idx].holes.push_back(*loop); - } -} - -void -TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) -{ - /* - Input loops are not suitable for evenodd nor nonzero fill types, as we might get - two consecutive concentric loops having the same winding order - and we have to - respect such order. In that case, evenodd would create wrong inversions, and nonzero - would ignore holes inside two concentric contours. - So we're ordering loops and collapse consecutive concentric loops having the same - winding order. - TODO: find a faster algorithm for this, maybe with some sort of binary search. - If we computed a "nesting tree" we could also just remove the consecutive loops - having the same winding order, and remove the extra one(s) so that we could just - supply everything to offset_ex() instead of performing several union/diff calls. - - we sort by area assuming that the outermost loops have larger area; - the previous sorting method, based on $b->contains_point($a->[0]), failed to nest - loops correctly in some edge cases when original model had overlapping facets - */ - - std::vector area; - std::vector abs_area; - std::vector sorted_area; // vector of indices - for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++loop) { - double a = loop->area(); - area.push_back(a); - abs_area.push_back(std::fabs(a)); - sorted_area.push_back(loop - loops.begin()); - } - - std::sort(sorted_area.begin(), sorted_area.end(), _area_comp(&abs_area)); // outer first - - // we don't perform a safety offset now because it might reverse cw loops - Polygons p_slices; - for (std::vector::const_iterator loop_idx = sorted_area.begin(); loop_idx != sorted_area.end(); ++loop_idx) { - /* we rely on the already computed area to determine the winding order - of the loops, since the Orientation() function provided by Clipper - would do the same, thus repeating the calculation */ - Polygons::const_iterator loop = loops.begin() + *loop_idx; - if (area[*loop_idx] >= 0) { - p_slices.push_back(*loop); - } else { - diff(p_slices, *loop, p_slices); - } - } - - // perform a safety offset to merge very close facets (TODO: find test case for this) - double safety_offset = scale_(0.0499); - ExPolygons ex_slices; - offset2_ex(p_slices, ex_slices, +safety_offset, -safety_offset); - - #ifdef SLIC3R_DEBUG - size_t holes_count = 0; - for (ExPolygons::const_iterator e = ex_slices.begin(); e != ex_slices.end(); ++e) { - holes_count += e->holes.size(); - } - printf("%zu surface(s) having %zu holes detected from %zu polylines\n", - ex_slices.size(), holes_count, loops.size()); - #endif - - // append to the supplied collection - slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); -} - -void -TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) -{ - Polygons pp; - this->make_loops(lines, &pp); - this->make_expolygons(pp, slices); -} - -void -TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) -{ - std::vector upper_lines, lower_lines; - - float scaled_z = scale_(z); - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { - stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; - - // find facet extents - float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); - - // intersect facet with cutting plane - std::vector lines; - this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &lines); - - // save intersection lines for generating correct triangulations - for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { - if (it->edge_type == feTop) { - lower_lines.push_back(*it); - } else if (it->edge_type == feBottom) { - upper_lines.push_back(*it); - } else if (it->edge_type != feHorizontal) { - lower_lines.push_back(*it); - upper_lines.push_back(*it); - } - } - - if (min_z > z || (min_z == z && max_z > min_z)) { - // facet is above the cut plane and does not belong to it - if (upper != NULL) stl_add_facet(&upper->stl, facet); - } else if (max_z < z || (max_z == z && max_z > min_z)) { - // facet is below the cut plane and does not belong to it - if (lower != NULL) stl_add_facet(&lower->stl, facet); - } else if (min_z < z && max_z > z) { - // facet is cut by the slicing plane - - // 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) ) { - isolated_vertex = 2; - } else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > 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]; - - // 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; - - // build the triangular facet - stl_facet triangle; - triangle.normal = facet->normal; - 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[2] = v0v1; - quadrilateral[1].normal = facet->normal; - quadrilateral[1].vertex[0] = *v2; - quadrilateral[1].vertex[1] = v2v0; - quadrilateral[1].vertex[2] = v0v1; - - if (v0->z > z) { - if (upper != NULL) stl_add_facet(&upper->stl, &triangle); - if (lower != NULL) { - stl_add_facet(&lower->stl, &quadrilateral[0]); - stl_add_facet(&lower->stl, &quadrilateral[1]); - } - } else { - if (upper != NULL) { - stl_add_facet(&upper->stl, &quadrilateral[0]); - stl_add_facet(&upper->stl, &quadrilateral[1]); - } - if (lower != NULL) stl_add_facet(&lower->stl, &triangle); - } - } - } - - // triangulate holes of upper mesh - if (upper != NULL) { - // compute shape of section - ExPolygons section; - this->make_expolygons_simple(upper_lines, §ion); - - // triangulate section - Polygons triangles; - for (ExPolygons::const_iterator expolygon = section.begin(); expolygon != section.end(); ++expolygon) - expolygon->triangulate_p2t(&triangles); - - // convert triangles to facets and append them to mesh - for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { - Polygon p = *polygon; - p.reverse(); - stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = -1; - 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; - } - stl_add_facet(&upper->stl, &facet); - } - } - - // triangulate holes of lower mesh - if (lower != NULL) { - // compute shape of section - ExPolygons section; - this->make_expolygons_simple(lower_lines, §ion); - - // triangulate section - Polygons triangles; - for (ExPolygons::const_iterator expolygon = section.begin(); expolygon != section.end(); ++expolygon) - expolygon->triangulate_p2t(&triangles); - - // 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; - 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; - } - stl_add_facet(&lower->stl, &facet); - } - } - - - stl_get_size(&(upper->stl)); - stl_get_size(&(lower->stl)); - -} - -TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) -{ - // build a table to map a facet_idx to its three edge indices - this->mesh->require_shared_vertices(); - typedef std::pair t_edge; - typedef std::vector t_edges; // edge_idx => a_id,b_id - typedef std::map t_edges_map; // a_id,b_id => edge_idx - - this->facets_edges.resize(this->mesh->stl.stats.number_of_facets); - - { - t_edges edges; - // reserve() instad of resize() because otherwise we couldn't read .size() below to assign edge_idx - edges.reserve(this->mesh->stl.stats.number_of_facets * 3); // number of edges = number of facets * 3 - t_edges_map edges_map; - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { - this->facets_edges[facet_idx].resize(3); - for (int i = 0; i <= 2; i++) { - int a_id = this->mesh->stl.v_indices[facet_idx].vertex[i]; - int b_id = this->mesh->stl.v_indices[facet_idx].vertex[(i+1) % 3]; - - int edge_idx; - t_edges_map::const_iterator my_edge = edges_map.find(std::make_pair(b_id,a_id)); - if (my_edge != edges_map.end()) { - edge_idx = my_edge->second; - } else { - /* admesh can assign the same edge ID to more than two facets (which is - still topologically correct), so we have to search for a duplicate of - this edge too in case it was already seen in this orientation */ - my_edge = edges_map.find(std::make_pair(a_id,b_id)); - - if (my_edge != edges_map.end()) { - edge_idx = my_edge->second; - } else { - // edge isn't listed in table, so we insert it - edge_idx = edges.size(); - edges.push_back(std::make_pair(a_id,b_id)); - edges_map[ edges[edge_idx] ] = edge_idx; - } - } - this->facets_edges[facet_idx][i] = edge_idx; - - #ifdef SLIC3R_DEBUG - printf(" [facet %d, edge %d] a_id = %d, b_id = %d --> edge %d\n", facet_idx, i, a_id, b_id, edge_idx); - #endif - } - } - } - - // clone shared vertices coordinates and scale them - this->v_scaled_shared = (stl_vertex*)calloc(this->mesh->stl.stats.shared_vertices, sizeof(stl_vertex)); - std::copy(this->mesh->stl.v_shared, this->mesh->stl.v_shared + this->mesh->stl.stats.shared_vertices, this->v_scaled_shared); - for (int i = 0; i < this->mesh->stl.stats.shared_vertices; i++) { - this->v_scaled_shared[i].x /= SCALING_FACTOR; - this->v_scaled_shared[i].y /= SCALING_FACTOR; - this->v_scaled_shared[i].z /= SCALING_FACTOR; - } -} - -TriangleMeshSlicer::~TriangleMeshSlicer() -{ - if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); -} - -} diff --git a/xs/src/TriangleMesh.hpp b/xs/src/TriangleMesh.hpp deleted file mode 100644 index b16f867eb..000000000 --- a/xs/src/TriangleMesh.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef slic3r_TriangleMesh_hpp_ -#define slic3r_TriangleMesh_hpp_ - -#include -#include -#include -#include "BoundingBox.hpp" -#include "Point.hpp" -#include "Polygon.hpp" -#include "ExPolygon.hpp" - -namespace Slic3r { - -class TriangleMesh; -class TriangleMeshSlicer; -typedef std::vector TriangleMeshPtrs; - -class TriangleMesh -{ - public: - TriangleMesh(); - TriangleMesh(const TriangleMesh &other); - TriangleMesh& operator= (TriangleMesh other); - void swap(TriangleMesh &other); - ~TriangleMesh(); - void ReadSTLFile(char* input_file); - void write_ascii(char* output_file); - void write_binary(char* output_file); - void repair(); - void WriteOBJFile(char* output_file); - void scale(float factor); - void scale(std::vector versor); - void translate(float x, float y, float z); - void rotate_x(float angle); - void rotate_y(float angle); - void rotate_z(float angle); - void flip_x(); - void flip_y(); - void flip_z(); - void align_to_origin(); - void rotate(double angle, Point* center); - TriangleMeshPtrs split() const; - void merge(const TriangleMesh* mesh); - void horizontal_projection(ExPolygons &retval) const; - void convex_hull(Polygon* hull); - void bounding_box(BoundingBoxf3* bb) const; - void reset_repair_stats(); - stl_file stl; - bool repaired; - - #ifdef SLIC3RXS - SV* to_SV(); - void ReadFromPerl(SV* vertices, SV* facets); - #endif - - private: - void require_shared_vertices(); - friend class TriangleMeshSlicer; -}; - -enum FacetEdgeType { feNone, feTop, feBottom, feHorizontal }; - -class IntersectionPoint : public Point -{ - public: - int point_id; - int edge_id; - IntersectionPoint() : point_id(-1), edge_id(-1) {}; -}; - -class IntersectionLine -{ - public: - Point a; - Point b; - int a_id; - int b_id; - int edge_a_id; - int edge_b_id; - FacetEdgeType edge_type; - bool skip; - IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {}; -}; -typedef std::vector IntersectionLines; -typedef std::vector IntersectionLinePtrs; - -class TriangleMeshSlicer -{ - public: - TriangleMesh* mesh; - TriangleMeshSlicer(TriangleMesh* _mesh); - ~TriangleMeshSlicer(); - void slice(const std::vector &z, std::vector* layers); - void slice(const std::vector &z, std::vector* layers); - void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const; - void cut(float z, TriangleMesh* upper, TriangleMesh* lower); - - private: - typedef std::vector< std::vector > t_facets_edges; - t_facets_edges facets_edges; - stl_vertex* v_scaled_shared; - void make_loops(std::vector &lines, Polygons* loops); - void make_expolygons(const Polygons &loops, ExPolygons* slices); - void make_expolygons_simple(std::vector &lines, ExPolygons* slices); - void make_expolygons(std::vector &lines, ExPolygons* slices); -}; - -} - -#endif diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp new file mode 100644 index 000000000..55c6abbe0 --- /dev/null +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -0,0 +1,197 @@ +#include "BoundingBox.hpp" +#include + +namespace Slic3r { + +template +BoundingBoxBase::BoundingBoxBase(const std::vector &points) +{ + if (points.empty()) CONFESS("Empty point set supplied to BoundingBoxBase constructor"); + typename std::vector::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); + } +} +template BoundingBoxBase::BoundingBoxBase(const std::vector &points); +template BoundingBoxBase::BoundingBoxBase(const std::vector &points); + +template +BoundingBox3Base::BoundingBox3Base(const std::vector &points) + : BoundingBoxBase(points) +{ + if (points.empty()) CONFESS("Empty point set supplied to BoundingBox3Base constructor"); + typename std::vector::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); + } +} +template BoundingBox3Base::BoundingBox3Base(const std::vector &points); + +BoundingBox::BoundingBox(const Lines &lines) +{ + Points points; + for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { + points.push_back(line->a); + points.push_back(line->b); + } + *this = BoundingBox(points); +} + +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 +BoundingBox::polygon() const +{ + Polygon p; + this->polygon(&p); + return p; +} + +template void +BoundingBoxBase::scale(double factor) +{ + this->min.scale(factor); + this->max.scale(factor); +} +template void BoundingBoxBase::scale(double factor); +template void BoundingBoxBase::scale(double factor); +template void BoundingBoxBase::scale(double factor); + +template void +BoundingBoxBase::merge(const PointClass &point) +{ + 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); +} +template void BoundingBoxBase::merge(const Point &point); +template void BoundingBoxBase::merge(const Pointf &point); + +template void +BoundingBoxBase::merge(const BoundingBoxBase &bb) +{ + 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); +} +template void BoundingBoxBase::merge(const BoundingBoxBase &bb); +template void BoundingBoxBase::merge(const BoundingBoxBase &bb); + +template void +BoundingBox3Base::merge(const PointClass &point) +{ + BoundingBoxBase::merge(point); + this->min.z = std::min(point.z, this->min.z); + this->max.z = std::max(point.z, this->max.z); +} +template void BoundingBox3Base::merge(const Pointf3 &point); + +template void +BoundingBox3Base::merge(const BoundingBox3Base &bb) +{ + BoundingBoxBase::merge(bb); + this->min.z = std::min(bb.min.z, this->min.z); + this->max.z = std::max(bb.max.z, this->max.z); +} +template void BoundingBox3Base::merge(const BoundingBox3Base &bb); + +template PointClass +BoundingBoxBase::size() const +{ + return PointClass(this->max.x - this->min.x, this->max.y - this->min.y); +} +template Point BoundingBoxBase::size() const; +template Pointf BoundingBoxBase::size() const; + +template PointClass +BoundingBox3Base::size() const +{ + return PointClass(this->max.x - this->min.x, this->max.y - this->min.y, this->max.z - this->min.z); +} +template Pointf3 BoundingBox3Base::size() const; + +template void +BoundingBoxBase::translate(coordf_t x, coordf_t y) +{ + this->min.translate(x, y); + this->max.translate(x, y); +} +template void BoundingBoxBase::translate(coordf_t x, coordf_t y); +template void BoundingBoxBase::translate(coordf_t x, coordf_t y); + +template void +BoundingBox3Base::translate(coordf_t x, coordf_t y, coordf_t z) +{ + this->min.translate(x, y, z); + this->max.translate(x, y, z); +} +template void BoundingBox3Base::translate(coordf_t x, coordf_t y, coordf_t z); + +template void +BoundingBoxBase::offset(coordf_t delta) +{ + this->min.translate(-delta, -delta); + this->max.translate(delta, delta); +} +template void BoundingBoxBase::offset(coordf_t delta); +template void BoundingBoxBase::offset(coordf_t delta); + +template void +BoundingBox3Base::offset(coordf_t delta) +{ + this->min.translate(-delta, -delta, -delta); + this->max.translate(delta, delta, delta); +} +template void BoundingBox3Base::offset(coordf_t delta); + +template PointClass +BoundingBoxBase::center() const +{ + return PointClass( + (this->max.x + this->min.x)/2, + (this->max.y + this->min.y)/2 + ); +} +template Point BoundingBoxBase::center() const; +template Pointf BoundingBoxBase::center() const; + +template PointClass +BoundingBox3Base::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 + ); +} +template Pointf3 BoundingBox3Base::center() const; + +#ifdef SLIC3RXS +REGISTER_CLASS(BoundingBox, "Geometry::BoundingBox"); +REGISTER_CLASS(BoundingBoxf, "Geometry::BoundingBoxf"); +REGISTER_CLASS(BoundingBoxf3, "Geometry::BoundingBoxf3"); +#endif + +} diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp new file mode 100644 index 000000000..0ea81b831 --- /dev/null +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -0,0 +1,76 @@ +#ifndef slic3r_BoundingBox_hpp_ +#define slic3r_BoundingBox_hpp_ + +#include +#include "Point.hpp" +#include "Polygon.hpp" + +namespace Slic3r { + +typedef Point Size; +typedef Point3 Size3; +typedef Pointf Sizef; +typedef Pointf3 Sizef3; + +template +class BoundingBoxBase +{ + public: + PointClass min; + PointClass max; + + BoundingBoxBase() {}; + BoundingBoxBase(const std::vector &points); + void merge(const PointClass &point); + void merge(const BoundingBoxBase &bb); + void scale(double factor); + PointClass size() const; + void translate(coordf_t x, coordf_t y); + void offset(coordf_t delta); + PointClass center() const; +}; + +template +class BoundingBox3Base : public BoundingBoxBase +{ + public: + BoundingBox3Base() {}; + BoundingBox3Base(const std::vector &points); + void merge(const PointClass &point); + void merge(const BoundingBox3Base &bb); + PointClass size() const; + void translate(coordf_t x, coordf_t y, coordf_t z); + void offset(coordf_t delta); + PointClass center() const; +}; + +class BoundingBox : public BoundingBoxBase +{ + public: + void polygon(Polygon* polygon) const; + Polygon polygon() const; + + BoundingBox() {}; + BoundingBox(const Points &points) : BoundingBoxBase(points) {}; + BoundingBox(const Lines &lines); +}; + +/* +class BoundingBox3 : public BoundingBox3Base {}; +*/ + +class BoundingBoxf : public BoundingBoxBase { + public: + BoundingBoxf() {}; + BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; +}; + +class BoundingBoxf3 : public BoundingBox3Base { + public: + BoundingBoxf3() {}; + BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; +}; + +} + +#endif diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp new file mode 100644 index 000000000..240cf3b68 --- /dev/null +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -0,0 +1,568 @@ +#include "ClipperUtils.hpp" +#include "Geometry.hpp" + +namespace Slic3r { + +//----------------------------------------------------------- +// legacy code from Clipper documentation +void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons) +{ + size_t cnt = expolygons.size(); + expolygons.resize(cnt + 1); + ClipperPath_to_Slic3rMultiPoint(polynode.Contour, expolygons[cnt].contour); + expolygons[cnt].holes.resize(polynode.ChildCount()); + for (int i = 0; i < polynode.ChildCount(); ++i) + { + ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, expolygons[cnt].holes[i]); + //Add outer polygons contained by (nested within) holes ... + for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) + AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); + } +} + +void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons) +{ + expolygons.clear(); + for (int i = 0; i < polytree.ChildCount(); ++i) + AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons); +} +//----------------------------------------------------------- + +template +void +ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output) +{ + output.points.clear(); + for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) { + output.points.push_back(Slic3r::Point( (*pit).X, (*pit).Y )); + } +} + +template +void +ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output) +{ + output.clear(); + for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { + typename T::value_type p; + ClipperPath_to_Slic3rMultiPoint(*it, p); + output.push_back(p); + } +} + +void +ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output) +{ + // init Clipper + ClipperLib::Clipper clipper; + clipper.Clear(); + + // perform union + clipper.AddPaths(input, ClipperLib::ptSubject, true); + ClipperLib::PolyTree polytree; + clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero + + // write to ExPolygons object + output.clear(); + PolyTreeToExPolygons(polytree, output); +} + +void +Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output) +{ + output.clear(); + for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) { + output.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); + } +} + +template +void +Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output) +{ + output.clear(); + for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) { + ClipperLib::Path p; + Slic3rMultiPoint_to_ClipperPath(*it, p); + output.push_back(p); + } +} + +void +scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale) +{ + for (ClipperLib::Paths::iterator it = polygons.begin(); it != polygons.end(); ++it) { + for (ClipperLib::Path::iterator pit = (*it).begin(); pit != (*it).end(); ++pit) { + (*pit).X *= scale; + (*pit).Y *= scale; + } + } +} + +void +offset(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // read input + ClipperLib::Paths input; + Slic3rMultiPoints_to_ClipperPaths(polygons, input); + + // scale input + scaleClipperPolygons(input, scale); + + // perform offset + ClipperLib::ClipperOffset co; + if (joinType == jtRound) { + co.ArcTolerance = miterLimit; + } else { + co.MiterLimit = miterLimit; + } + co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); + co.Execute(retval, (delta*scale)); + + // unscale output + scaleClipperPolygons(retval, 1/scale); +} + +void +offset(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // perform offset + ClipperLib::Paths output; + offset(polygons, output, delta, scale, joinType, miterLimit); + + // convert into ExPolygons + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + +void +offset(const Slic3r::Polylines &polylines, ClipperLib::Paths &retval, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // read input + ClipperLib::Paths input; + Slic3rMultiPoints_to_ClipperPaths(polylines, input); + + // scale input + scaleClipperPolygons(input, scale); + + // perform offset + ClipperLib::ClipperOffset co; + if (joinType == jtRound) { + co.ArcTolerance = miterLimit; + } else { + co.MiterLimit = miterLimit; + } + co.AddPaths(input, joinType, ClipperLib::etOpenButt); + co.Execute(retval, (delta*scale)); + + // unscale output + scaleClipperPolygons(retval, 1/scale); +} + +void +offset(const Slic3r::Polylines &polylines, Slic3r::Polygons &retval, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // perform offset + ClipperLib::Paths output; + offset(polylines, output, delta, scale, joinType, miterLimit); + + // convert into ExPolygons + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + +void +offset(const Slic3r::Surface &surface, Slic3r::Surfaces &retval, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // perform offset + Slic3r::ExPolygons expp; + offset_ex(surface.expolygon, expp, delta, scale, joinType, miterLimit); + + // clone the input surface for each expolygon we got + retval.clear(); + retval.reserve(expp.size()); + for (ExPolygons::iterator it = expp.begin(); it != expp.end(); ++it) { + Surface s = surface; // clone + s.expolygon = *it; + retval.push_back(s); + } +} + +void +offset_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // perform offset + ClipperLib::Paths output; + offset(polygons, output, delta, scale, joinType, miterLimit); + + // convert into ExPolygons + ClipperPaths_to_Slic3rExPolygons(output, retval); +} + +void +offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta1, + const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +{ + // read input + ClipperLib::Paths input; + Slic3rMultiPoints_to_ClipperPaths(polygons, input); + + // scale input + scaleClipperPolygons(input, scale); + + // prepare ClipperOffset object + ClipperLib::ClipperOffset co; + if (joinType == jtRound) { + co.ArcTolerance = miterLimit; + } else { + co.MiterLimit = miterLimit; + } + + // perform first offset + ClipperLib::Paths output1; + co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); + co.Execute(output1, (delta1*scale)); + + // perform second offset + co.Clear(); + co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); + co.Execute(retval, (delta2*scale)); + + // unscale output + scaleClipperPolygons(retval, 1/scale); +} + +void +offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1, + const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +{ + // perform offset + ClipperLib::Paths output; + offset2(polygons, output, delta1, delta2, scale, joinType, miterLimit); + + // convert into ExPolygons + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + +void +offset2_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta1, + const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +{ + // perform offset + ClipperLib::Paths output; + offset2(polygons, output, delta1, delta2, scale, joinType, miterLimit); + + // convert into ExPolygons + ClipperPaths_to_Slic3rExPolygons(output, retval); +} + +template +void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, T &retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_) +{ + // read input + ClipperLib::Paths input_subject, input_clip; + Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); + Slic3rMultiPoints_to_ClipperPaths(clip, input_clip); + + // perform safety offset + if (safety_offset_) { + if (clipType == ClipperLib::ctUnion) { + safety_offset(&input_subject); + } else { + safety_offset(&input_clip); + } + } + + // init Clipper + ClipperLib::Clipper clipper; + clipper.Clear(); + + // add polygons + clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); + clipper.AddPaths(input_clip, ClipperLib::ptClip, true); + + // perform operation + clipper.Execute(clipType, retval, fillType, fillType); +} + +void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, ClipperLib::PolyTree &retval, const ClipperLib::PolyFillType fillType, + const bool safety_offset_) +{ + // read input + ClipperLib::Paths input_subject, input_clip; + Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); + Slic3rMultiPoints_to_ClipperPaths(clip, input_clip); + + // perform safety offset + if (safety_offset_) safety_offset(&input_clip); + + // init Clipper + ClipperLib::Clipper clipper; + clipper.Clear(); + + // add polygons + clipper.AddPaths(input_subject, ClipperLib::ptSubject, false); + clipper.AddPaths(input_clip, ClipperLib::ptClip, true); + + // perform operation + clipper.Execute(clipType, retval, fillType, fillType); +} + +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_) +{ + // perform operation + ClipperLib::Paths output; + _clipper_do(clipType, subject, clip, output, ClipperLib::pftNonZero, safety_offset_); + + // convert into Polygons + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_) +{ + // perform operation + ClipperLib::PolyTree polytree; + _clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_); + + // convert into ExPolygons + PolyTreeToExPolygons(polytree, retval); +} + +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_) +{ + // perform operation + ClipperLib::PolyTree polytree; + _clipper_do(clipType, subject, clip, polytree, ClipperLib::pftNonZero, safety_offset_); + + // convert into Polylines + ClipperLib::Paths output; + ClipperLib::PolyTreeToPaths(polytree, output); + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_) +{ + // transform input polygons into polylines + Slic3r::Polylines polylines; + polylines.reserve(subject.size()); + for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) + polylines.push_back(*polygon); // implicit call to split_at_first_point() + + // perform clipping + _clipper(clipType, polylines, clip, retval, safety_offset_); + + /* If the split_at_first_point() call above happens to split the polygon inside the clipping area + we would get two consecutive polylines instead of a single one, so we go through them in order + 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 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())) { + /* 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())) { + /* 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())) { + /* 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(); + retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); + retval.erase(retval.begin() + j); + --j; + } + } + } +} + +template +void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_) +{ + _clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); +} +template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); +template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); +template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); +template void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); + +template +void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_) +{ + _clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); +} +template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); +template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); +template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); +template void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines &retval, bool safety_offset_); + +void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, + bool safety_offset_) +{ + _clipper(ClipperLib::ctXor, subject, clip, retval, safety_offset_); +} + +template +void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_) +{ + Slic3r::Polygons p; + _clipper(ClipperLib::ctUnion, subject, p, retval, safety_offset_); +} +template void union_(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool safety_offset_); +template void union_(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_); + +void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_) +{ + Slic3r::Polygons clip; + _clipper_do(ClipperLib::ctUnion, subject, clip, retval, ClipperLib::pftEvenOdd, safety_offset_); +} + +void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_) +{ + ClipperLib::PolyTree pt; + union_pt(subject, pt, safety_offset_); + traverse_pt(pt.Childs, retval); +} + +static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval) +{ + /* use a nearest neighbor search to order these children + TODO: supply start_near to chained_path() too? */ + + // collect ordering points + Points ordering_points; + ordering_points.reserve(nodes.size()); + for (ClipperLib::PolyNodes::const_iterator it = nodes.begin(); it != nodes.end(); ++it) { + Point p((*it)->Contour.front().X, (*it)->Contour.front().Y); + ordering_points.push_back(p); + } + + // perform the ordering + ClipperLib::PolyNodes ordered_nodes; + Slic3r::Geometry::chained_path_items(ordering_points, nodes, ordered_nodes); + + // push results recursively + for (ClipperLib::PolyNodes::iterator it = ordered_nodes.begin(); it != ordered_nodes.end(); ++it) { + // traverse the next depth + traverse_pt((*it)->Childs, retval); + + Polygon p; + ClipperPath_to_Slic3rMultiPoint((*it)->Contour, p); + retval.push_back(p); + if ((*it)->IsHole()) retval.back().reverse(); // ccw + } +} + +void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear) +{ + // convert into Clipper polygons + ClipperLib::Paths input_subject, output; + Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); + + if (preserve_collinear) { + ClipperLib::Clipper c; + c.PreserveCollinear(true); + c.StrictlySimple(true); + c.AddPaths(input_subject, ClipperLib::ptSubject, true); + c.Execute(ClipperLib::ctUnion, output, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + } else { + ClipperLib::SimplifyPolygons(input_subject, output, ClipperLib::pftNonZero); + } + + // convert into Slic3r polygons + ClipperPaths_to_Slic3rMultiPoints(output, retval); +} + +void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear) +{ + if (!preserve_collinear) { + Polygons polygons; + simplify_polygons(subject, polygons, preserve_collinear); + union_(polygons, retval); + return; + } + + // convert into Clipper polygons + ClipperLib::Paths input_subject; + Slic3rMultiPoints_to_ClipperPaths(subject, input_subject); + + ClipperLib::PolyTree polytree; + + ClipperLib::Clipper c; + c.PreserveCollinear(true); + c.StrictlySimple(true); + c.AddPaths(input_subject, ClipperLib::ptSubject, true); + c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); + + // convert into ExPolygons + PolyTreeToExPolygons(polytree, retval); +} + +void safety_offset(ClipperLib::Paths* paths) +{ + // scale input + scaleClipperPolygons(*paths, CLIPPER_OFFSET_SCALE); + + // perform offset (delta = scale 1e-05) + ClipperLib::ClipperOffset co; + co.MiterLimit = 2; + co.AddPaths(*paths, ClipperLib::jtMiter, ClipperLib::etClosedPolygon); + co.Execute(*paths, 10.0 * CLIPPER_OFFSET_SCALE); + + // unscale output + scaleClipperPolygons(*paths, 1.0/CLIPPER_OFFSET_SCALE); +} + +/////////////////////// + +#ifdef SLIC3RXS +SV* +polynode_children_2_perl(const ClipperLib::PolyNode& node) +{ + AV* av = newAV(); + const unsigned int len = node.ChildCount(); + if (len > 0) av_extend(av, len-1); + for (int i = 0; i < len; ++i) { + av_store(av, i, polynode2perl(*node.Childs[i])); + } + return (SV*)newRV_noinc((SV*)av); +} + +SV* +polynode2perl(const ClipperLib::PolyNode& node) +{ + HV* hv = newHV(); + Slic3r::Polygon p; + ClipperPath_to_Slic3rMultiPoint(node.Contour, p); + if (node.IsHole()) { + (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); + } else { + (void)hv_stores( hv, "outer", Slic3r::perl_to_SV_clone_ref(p) ); + } + (void)hv_stores( hv, "children", polynode_children_2_perl(node) ); + return (SV*)newRV_noinc((SV*)hv); +} +#endif + +} diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp new file mode 100644 index 000000000..2ab3ff775 --- /dev/null +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -0,0 +1,111 @@ +#ifndef slic3r_ClipperUtils_hpp_ +#define slic3r_ClipperUtils_hpp_ + +#include +#include "clipper.hpp" +#include "ExPolygon.hpp" +#include "Polygon.hpp" +#include "Surface.hpp" + +// import these wherever we're included +using ClipperLib::jtMiter; +using ClipperLib::jtRound; +using ClipperLib::jtSquare; + +namespace Slic3r { + +#define CLIPPER_OFFSET_SCALE 100000.0 + +//----------------------------------------------------------- +// legacy code from Clipper documentation +void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons); +void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); +//----------------------------------------------------------- + +void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path &output); +template +void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths &output); +template +void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T &output); +template +void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T &output); +void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons &output); + +void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); + +// offset Polygons +void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); +void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); + +// offset Polylines +void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths &retval, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + double miterLimit = 3); +void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons &retval, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + double miterLimit = 3); +void offset(const Slic3r::Surface &surface, Slic3r::Surfaces &retval, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + double miterLimit = 3); + +void offset_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta, + double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); + +void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths &retval, const float delta1, + const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); +void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons &retval, const float delta1, + const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); +void offset2_ex(const Slic3r::Polygons &polygons, Slic3r::ExPolygons &retval, const float delta1, + const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double miterLimit = 3); + +template +void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, T &retval, bool safety_offset_); +void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, ClipperLib::Paths &retval, bool safety_offset_); +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, Slic3r::Polygons &retval, bool safety_offset_); +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, bool safety_offset_); +void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, Slic3r::Polylines &retval); + +template +void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false); + +template +void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType &retval, bool safety_offset_ = false); + +void xor_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons &retval, + bool safety_offset_ = false); + +template +void union_(const Slic3r::Polygons &subject, T &retval, bool safety_offset_ = false); + +void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree &retval, bool safety_offset_ = false); +void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool safety_offset_ = false); +static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons &retval); + +void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons &retval, bool preserve_collinear = false); +void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons &retval, bool preserve_collinear = false); + +void safety_offset(ClipperLib::Paths* paths); + +///////////////// + +#ifdef SLIC3RXS +SV* polynode_children_2_perl(const ClipperLib::PolyNode& node); +SV* polynode2perl(const ClipperLib::PolyNode& node); +#endif + +} + +#endif diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp new file mode 100644 index 000000000..4d03b36a0 --- /dev/null +++ b/xs/src/libslic3r/Config.cpp @@ -0,0 +1,377 @@ +#include "Config.hpp" + +namespace Slic3r { + +bool +ConfigBase::has(const t_config_option_key opt_key) { + return (this->option(opt_key, false) != NULL); +} + +void +ConfigBase::apply(const ConfigBase &other, bool ignore_nonexistent) { + // get list of option keys to apply + t_config_option_keys opt_keys; + other.keys(&opt_keys); + + // loop through options and apply them + for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) { + ConfigOption* my_opt = this->option(*it, true); + if (my_opt == NULL) { + if (ignore_nonexistent == false) throw "Attempt to apply non-existent option"; + continue; + } + + // not the most efficient way, but easier than casting pointers to subclasses + bool res = my_opt->deserialize( other.option(*it)->serialize() ); + if (!res) CONFESS("Unexpected failure when deserializing serialized value"); + } +} + +std::string +ConfigBase::serialize(const t_config_option_key opt_key) { + ConfigOption* opt = this->option(opt_key); + assert(opt != NULL); + return opt->serialize(); +} + +bool +ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) { + if (this->def->count(opt_key) == 0) throw "Calling set_deserialize() on unknown option"; + ConfigOptionDef* optdef = &(*this->def)[opt_key]; + if (!optdef->shortcut.empty()) { + for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) { + if (!this->set_deserialize(*it, str)) return false; + } + return true; + } + + ConfigOption* opt = this->option(opt_key, true); + assert(opt != NULL); + return opt->deserialize(str); +} + +double +ConfigBase::get_abs_value(const t_config_option_key opt_key) { + ConfigOption* opt = this->option(opt_key, false); + if (ConfigOptionFloatOrPercent* optv = dynamic_cast(opt)) { + // get option definition + assert(this->def->count(opt_key) != 0); + ConfigOptionDef* def = &(*this->def)[opt_key]; + + // compute absolute value over the absolute value of the base option + return optv->get_abs_value(this->get_abs_value(def->ratio_over)); + } else if (ConfigOptionFloat* optv = dynamic_cast(opt)) { + return optv->value; + } else { + throw "Not a valid option type for get_abs_value()"; + } +} + +double +ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over) { + // get stored option value + ConfigOptionFloatOrPercent* opt = dynamic_cast(this->option(opt_key)); + assert(opt != NULL); + + // compute absolute value + return opt->get_abs_value(ratio_over); +} + +#ifdef SLIC3RXS +SV* +ConfigBase::as_hash() { + HV* hv = newHV(); + + t_config_option_keys opt_keys; + this->keys(&opt_keys); + + for (t_config_option_keys::const_iterator it = opt_keys.begin(); it != opt_keys.end(); ++it) + (void)hv_store( hv, it->c_str(), it->length(), this->get(*it), 0 ); + + return newRV_noinc((SV*)hv); +} + +SV* +ConfigBase::get(t_config_option_key opt_key) { + ConfigOption* opt = this->option(opt_key); + if (opt == NULL) return &PL_sv_undef; + if (ConfigOptionFloat* optv = dynamic_cast(opt)) { + return newSVnv(optv->value); + } else if (ConfigOptionPercent* optv = dynamic_cast(opt)) { + return newSVnv(optv->value); + } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSVnv(*it)); + return newRV_noinc((SV*)av); + } else if (ConfigOptionInt* optv = dynamic_cast(opt)) { + return newSViv(optv->value); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSViv(*it)); + return newRV_noinc((SV*)av); + } else if (ConfigOptionString* optv = dynamic_cast(opt)) { + // we don't serialize() because that would escape newlines + return newSVpvn_utf8(optv->value.c_str(), optv->value.length(), true); + } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSVpvn_utf8(it->c_str(), it->length(), true)); + return newRV_noinc((SV*)av); + } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { + return optv->point.to_SV_pureperl(); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (Pointfs::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), it->to_SV_pureperl()); + return newRV_noinc((SV*)av); + } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { + return newSViv(optv->value ? 1 : 0); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + AV* av = newAV(); + av_fill(av, optv->values.size()-1); + for (std::vector::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), newSViv(*it ? 1 : 0)); + return newRV_noinc((SV*)av); + } else { + std::string serialized = opt->serialize(); + return newSVpvn_utf8(serialized.c_str(), serialized.length(), true); + } +} + +SV* +ConfigBase::get_at(t_config_option_key opt_key, size_t i) { + ConfigOption* opt = this->option(opt_key); + if (opt == NULL) return &PL_sv_undef; + + if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + return newSVnv(optv->get_at(i)); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + return newSViv(optv->get_at(i)); + } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { + // we don't serialize() because that would escape newlines + std::string val = optv->get_at(i); + return newSVpvn_utf8(val.c_str(), val.length(), true); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + return optv->get_at(i).to_SV_pureperl(); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + return newSViv(optv->get_at(i) ? 1 : 0); + } else { + return &PL_sv_undef; + } +} + +bool +ConfigBase::set(t_config_option_key opt_key, SV* value) { + ConfigOption* opt = this->option(opt_key, true); + if (opt == NULL) CONFESS("Trying to set non-existing option"); + + if (ConfigOptionFloat* optv = dynamic_cast(opt)) { + if (!looks_like_number(value)) return false; + optv->value = SvNV(value); + } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + std::vector values; + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + if (elem == NULL || !looks_like_number(*elem)) return false; + values.push_back(SvNV(*elem)); + } + optv->values = values; + } else if (ConfigOptionInt* optv = dynamic_cast(opt)) { + if (!looks_like_number(value)) return false; + optv->value = SvIV(value); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + std::vector values; + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + if (elem == NULL || !looks_like_number(*elem)) return false; + values.push_back(SvIV(*elem)); + } + optv->values = values; + } else if (ConfigOptionString* optv = dynamic_cast(opt)) { + optv->value = std::string(SvPV_nolen(value), SvCUR(value)); + } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { + optv->values.clear(); + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + if (elem == NULL) return false; + optv->values.push_back(std::string(SvPV_nolen(*elem), SvCUR(*elem))); + } + } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { + return optv->point.from_SV(value); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + std::vector values; + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + Pointf point; + if (elem == NULL || !point.from_SV(*elem)) return false; + values.push_back(point); + } + optv->values = values; + } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { + optv->value = SvTRUE(value); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + optv->values.clear(); + AV* av = (AV*)SvRV(value); + const size_t len = av_len(av)+1; + for (size_t i = 0; i < len; i++) { + SV** elem = av_fetch(av, i, 0); + if (elem == NULL) return false; + optv->values.push_back(SvTRUE(*elem)); + } + } else { + if (!opt->deserialize( std::string(SvPV_nolen(value)) )) return false; + } + return true; +} + +/* This method is implemented as a workaround for this typemap bug: + https://rt.cpan.org/Public/Bug/Display.html?id=94110 */ +bool +ConfigBase::set_deserialize(const t_config_option_key opt_key, SV* str) { + size_t len; + const char * c = SvPV(str, len); + std::string value(c, len); + + return this->set_deserialize(opt_key, value); +} +#endif + +DynamicConfig& DynamicConfig::operator= (DynamicConfig other) +{ + this->swap(other); + return *this; +} + +void +DynamicConfig::swap(DynamicConfig &other) +{ + std::swap(this->options, other.options); +} + +DynamicConfig::~DynamicConfig () { + for (t_options_map::iterator it = this->options.begin(); it != this->options.end(); ++it) { + ConfigOption* opt = it->second; + if (opt != NULL) delete opt; + } +} + +DynamicConfig::DynamicConfig (const DynamicConfig& other) { + this->def = other.def; + this->apply(other, false); +} + +ConfigOption* +DynamicConfig::option(const t_config_option_key opt_key, bool create) { + if (this->options.count(opt_key) == 0) { + if (create) { + ConfigOptionDef* optdef = &(*this->def)[opt_key]; + ConfigOption* opt; + if (optdef->type == coFloat) { + opt = new ConfigOptionFloat (); + } else if (optdef->type == coFloats) { + opt = new ConfigOptionFloats (); + } else if (optdef->type == coInt) { + opt = new ConfigOptionInt (); + } else if (optdef->type == coInts) { + opt = new ConfigOptionInts (); + } else if (optdef->type == coString) { + opt = new ConfigOptionString (); + } else if (optdef->type == coStrings) { + opt = new ConfigOptionStrings (); + } else if (optdef->type == coPercent) { + opt = new ConfigOptionPercent (); + } else if (optdef->type == coFloatOrPercent) { + opt = new ConfigOptionFloatOrPercent (); + } else if (optdef->type == coPoint) { + opt = new ConfigOptionPoint (); + } else if (optdef->type == coPoints) { + opt = new ConfigOptionPoints (); + } else if (optdef->type == coBool) { + opt = new ConfigOptionBool (); + } else if (optdef->type == coBools) { + opt = new ConfigOptionBools (); + } else if (optdef->type == coEnum) { + ConfigOptionEnumGeneric* optv = new ConfigOptionEnumGeneric (); + optv->keys_map = &optdef->enum_keys_map; + opt = static_cast(optv); + } else { + throw "Unknown option type"; + } + this->options[opt_key] = opt; + return opt; + } else { + return NULL; + } + } + return this->options[opt_key]; +} + +template +T* +DynamicConfig::opt(const t_config_option_key opt_key, bool create) { + return dynamic_cast(this->option(opt_key, create)); +} +template ConfigOptionInt* DynamicConfig::opt(const t_config_option_key opt_key, bool create); +template ConfigOptionBool* DynamicConfig::opt(const t_config_option_key opt_key, bool create); +template ConfigOptionBools* DynamicConfig::opt(const t_config_option_key opt_key, bool create); + +const ConfigOption* +DynamicConfig::option(const t_config_option_key opt_key) const { + return const_cast(this)->option(opt_key, false); +} + +void +DynamicConfig::keys(t_config_option_keys *keys) const { + for (t_options_map::const_iterator it = this->options.begin(); it != this->options.end(); ++it) + keys->push_back(it->first); +} + +void +DynamicConfig::erase(const t_config_option_key opt_key) { + this->options.erase(opt_key); +} + +void +StaticConfig::keys(t_config_option_keys *keys) const { + for (t_optiondef_map::const_iterator it = this->def->begin(); it != this->def->end(); ++it) { + const ConfigOption* opt = this->option(it->first); + if (opt != NULL) keys->push_back(it->first); + } +} + +const ConfigOption* +StaticConfig::option(const t_config_option_key opt_key) const +{ + return const_cast(this)->option(opt_key, false); +} + +#ifdef SLIC3RXS +bool +StaticConfig::set(t_config_option_key opt_key, SV* value) { + ConfigOptionDef* optdef = &(*this->def)[opt_key]; + if (!optdef->shortcut.empty()) { + for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) { + if (!this->set(*it, value)) return false; + } + return true; + } + + return static_cast(this)->set(opt_key, value); +} +#endif + +} diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp new file mode 100644 index 000000000..42d19c830 --- /dev/null +++ b/xs/src/libslic3r/Config.hpp @@ -0,0 +1,521 @@ +#ifndef slic3r_Config_hpp_ +#define slic3r_Config_hpp_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Point.hpp" + +namespace Slic3r { + +typedef std::string t_config_option_key; +typedef std::vector t_config_option_keys; + +class ConfigOption { + public: + virtual ~ConfigOption() {}; + virtual std::string serialize() const = 0; + virtual bool deserialize(std::string str) = 0; + virtual int getInt() const { return 0; }; + virtual void setInt(int val) {}; +}; + +template +class ConfigOptionVector +{ + public: + virtual ~ConfigOptionVector() {}; + std::vector values; + + T get_at(size_t i) const { + try { + return this->values.at(i); + } catch (const std::out_of_range& oor) { + return this->values.front(); + } + }; +}; + +class ConfigOptionFloat : public ConfigOption +{ + public: + double value; // use double instead of float for preserving compatibility with values coming from Perl + ConfigOptionFloat() : value(0) {}; + + operator double() const { return this->value; }; + + std::string serialize() const { + std::ostringstream ss; + ss << this->value; + return ss.str(); + }; + + bool deserialize(std::string str) { + this->value = ::atof(str.c_str()); + return true; + }; +}; + +class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector +{ + public: + + std::string serialize() const { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; + ss << *it; + } + return ss.str(); + }; + + bool deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ',')) { + this->values.push_back(::atof(item_str.c_str())); + } + return true; + }; +}; + +class ConfigOptionInt : public ConfigOption +{ + public: + int value; + ConfigOptionInt() : value(0) {}; + + operator int() const { return this->value; }; + int getInt() const { return this->value; }; + void setInt(int val) { this->value = val; }; + + std::string serialize() const { + std::ostringstream ss; + ss << this->value; + return ss.str(); + }; + + bool deserialize(std::string str) { + this->value = ::atoi(str.c_str()); + return true; + }; +}; + +class ConfigOptionInts : public ConfigOption, public ConfigOptionVector +{ + public: + + std::string serialize() const { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; + ss << *it; + } + return ss.str(); + }; + + bool deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ',')) { + this->values.push_back(::atoi(item_str.c_str())); + } + return true; + }; +}; + +class ConfigOptionString : public ConfigOption +{ + public: + std::string value; + ConfigOptionString() : value("") {}; + + operator std::string() const { return this->value; }; + + std::string serialize() const { + std::string str = this->value; + + // s/\R/\\n/g + size_t pos = 0; + while ((pos = str.find("\n", pos)) != std::string::npos || (pos = str.find("\r", pos)) != std::string::npos) { + str.replace(pos, 1, "\\n"); + pos += 2; // length of "\\n" + } + + return str; + }; + + bool deserialize(std::string str) { + // s/\\n/\n/g + size_t pos = 0; + while ((pos = str.find("\\n", pos)) != std::string::npos) { + str.replace(pos, 2, "\n"); + pos += 1; // length of "\n" + } + + this->value = str; + return true; + }; +}; + +// semicolon-separated strings +class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector +{ + public: + + std::string serialize() const { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ";"; + ss << *it; + } + return ss.str(); + }; + + bool deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ';')) { + this->values.push_back(item_str); + } + return true; + }; +}; + +class ConfigOptionPercent : public ConfigOption +{ + public: + double value; + ConfigOptionPercent() : value(0) {}; + + double get_abs_value(double ratio_over) const { + return ratio_over * this->value / 100; + }; + + std::string serialize() const { + std::ostringstream ss; + ss << this->value; + std::string s(ss.str()); + s += "%"; + return s; + }; + + bool deserialize(std::string str) { + // don't try to parse the trailing % since it's optional + int res = sscanf(str.c_str(), "%lf", &this->value); + return res == 1; + }; +}; + +class ConfigOptionFloatOrPercent : public ConfigOption +{ + public: + double value; + bool percent; + ConfigOptionFloatOrPercent() : value(0), percent(false) {}; + + double get_abs_value(double ratio_over) const { + if (this->percent) { + return ratio_over * this->value / 100; + } else { + return this->value; + } + }; + + std::string serialize() const { + std::ostringstream ss; + ss << this->value; + std::string s(ss.str()); + if (this->percent) s += "%"; + return s; + }; + + bool deserialize(std::string str) { + if (str.find_first_of("%") != std::string::npos) { + int res = sscanf(str.c_str(), "%lf%%", &this->value); + if (res == 0) return false; + this->percent = true; + } else { + this->value = ::atof(str.c_str()); + this->percent = false; + } + return true; + }; +}; + +class ConfigOptionPoint : public ConfigOption +{ + public: + Pointf point; + ConfigOptionPoint() : point(Pointf(0,0)) {}; + + operator Pointf() const { return this->point; }; + + std::string serialize() const { + std::ostringstream ss; + ss << this->point.x; + ss << ","; + ss << this->point.y; + return ss.str(); + }; + + bool deserialize(std::string str) { + if (strncmp(str.c_str(), "0x", 2) == 0) { + this->point.x = 0; + int res = sscanf(str.c_str()+2, "%lf", &this->point.y); + return res == 1; + } else { + int res = sscanf(str.c_str(), "%lf%*1[,x]%lf", &this->point.x, &this->point.y); + return res == 2; + } + }; +}; + +class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector +{ + public: + + std::string serialize() const { + 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 << "x"; + ss << it->y; + } + return ss.str(); + }; + + bool deserialize(std::string str) { + std::vector values; + std::istringstream is(str); + std::string point_str; + while (std::getline(is, point_str, ',')) { + Pointf point; + if (strncmp(point_str.c_str(), "0x", 2) == 0) { + // if string starts with "0x", only apply sscanf() to the second coordinate + // otherwise it would parse the string as a hex number + point.x = 0; + int res = sscanf(point_str.c_str()+2, "%lf", &point.y); + if (res != 1) return false; + } else { + int res = sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y); + if (res != 2) return false; + } + values.push_back(point); + } + this->values = values; + return true; + }; +}; + +class ConfigOptionBool : public ConfigOption +{ + public: + bool value; + ConfigOptionBool() : value(false) {}; + + operator bool() const { return this->value; }; + + std::string serialize() const { + return std::string(this->value ? "1" : "0"); + }; + + bool deserialize(std::string str) { + this->value = (str.compare("1") == 0); + return true; + }; +}; + +class ConfigOptionBools : public ConfigOption, public ConfigOptionVector +{ + public: + + std::string serialize() const { + std::ostringstream ss; + for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; + ss << (*it ? "1" : "0"); + } + return ss.str(); + }; + + bool deserialize(std::string str) { + this->values.clear(); + std::istringstream is(str); + std::string item_str; + while (std::getline(is, item_str, ',')) { + this->values.push_back(item_str.compare("1") == 0); + } + return true; + }; +}; + +typedef std::map t_config_enum_values; + +template +class ConfigOptionEnum : public ConfigOption +{ + public: + T value; + + operator T() const { return this->value; }; + + std::string serialize() const { + t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); + for (t_config_enum_values::iterator it = enum_keys_map.begin(); it != enum_keys_map.end(); ++it) { + if (it->second == static_cast(this->value)) return it->first; + } + return ""; + }; + + bool deserialize(std::string str) { + t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); + if (enum_keys_map.count(str) == 0) return false; + this->value = static_cast(enum_keys_map[str]); + return true; + }; + + static t_config_enum_values get_enum_values(); +}; + +/* We use this one in DynamicConfig objects, otherwise it's better to use + the specialized ConfigOptionEnum containers. */ +class ConfigOptionEnumGeneric : public ConfigOption +{ + public: + int value; + t_config_enum_values* keys_map; + + operator int() const { return this->value; }; + + std::string serialize() const { + for (t_config_enum_values::iterator it = this->keys_map->begin(); it != this->keys_map->end(); ++it) { + if (it->second == this->value) return it->first; + } + return ""; + }; + + bool deserialize(std::string str) { + if (this->keys_map->count(str) == 0) return false; + this->value = (*this->keys_map)[str]; + return true; + }; +}; + +enum ConfigOptionType { + coFloat, + coFloats, + coInt, + coInts, + coString, + coStrings, + coPercent, + coFloatOrPercent, + coPoint, + coPoints, + coBool, + coBools, + coEnum, +}; + +class ConfigOptionDef +{ + public: + ConfigOptionType type; + std::string gui_type; + std::string gui_flags; + std::string label; + std::string full_label; + std::string category; + std::string tooltip; + std::string sidetext; + std::string cli; + t_config_option_key ratio_over; + bool multiline; + bool full_width; + bool readonly; + int height; + int width; + int min; + int max; + std::vector aliases; + std::vector shortcut; + std::vector enum_values; + std::vector enum_labels; + t_config_enum_values enum_keys_map; + + ConfigOptionDef() : multiline(false), full_width(false), readonly(false), + height(-1), width(-1), min(INT_MIN), max(INT_MAX) {}; +}; + +typedef std::map t_optiondef_map; + +class ConfigBase +{ + public: + t_optiondef_map* def; + + ConfigBase() : def(NULL) {}; + bool has(const t_config_option_key opt_key); + virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0; + virtual const ConfigOption* option(const t_config_option_key opt_key) const = 0; + virtual void keys(t_config_option_keys *keys) const = 0; + void apply(const ConfigBase &other, bool ignore_nonexistent = false); + std::string serialize(const t_config_option_key opt_key); + bool set_deserialize(const t_config_option_key opt_key, std::string str); + double get_abs_value(const t_config_option_key opt_key); + double get_abs_value(const t_config_option_key opt_key, double ratio_over); + + #ifdef SLIC3RXS + SV* as_hash(); + SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, size_t i); + bool set(t_config_option_key opt_key, SV* value); + bool set_deserialize(const t_config_option_key opt_key, SV* str); + #endif +}; + +class DynamicConfig : public ConfigBase +{ + public: + DynamicConfig() {}; + DynamicConfig(const DynamicConfig& other); + DynamicConfig& operator= (DynamicConfig other); + void swap(DynamicConfig &other); + ~DynamicConfig(); + template T* opt(const t_config_option_key opt_key, bool create = false); + ConfigOption* option(const t_config_option_key opt_key, bool create = false); + const ConfigOption* option(const t_config_option_key opt_key) const; + void keys(t_config_option_keys *keys) const; + void erase(const t_config_option_key opt_key); + + private: + typedef std::map t_options_map; + t_options_map options; +}; + +class StaticConfig : public ConfigBase +{ + public: + void keys(t_config_option_keys *keys) const; + virtual ConfigOption* option(const t_config_option_key opt_key, bool create = false) = 0; + const ConfigOption* option(const t_config_option_key opt_key) const; + + #ifdef SLIC3RXS + bool set(t_config_option_key opt_key, SV* value); + #endif +}; + +} + +#endif diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp new file mode 100644 index 000000000..a51e55a2d --- /dev/null +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -0,0 +1,428 @@ +#include "BoundingBox.hpp" +#include "ExPolygon.hpp" +#include "Geometry.hpp" +#include "Polygon.hpp" +#include "Line.hpp" +#include "ClipperUtils.hpp" +#include "polypartition.h" +#include "poly2tri/poly2tri.h" + +#include +#include + +namespace Slic3r { + +ExPolygon::operator Points() const +{ + Points points; + Polygons pp = *this; + for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { + for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) + points.push_back(*point); + } + return points; +} + +ExPolygon::operator Polygons() const +{ + Polygons polygons; + polygons.reserve(this->holes.size() + 1); + polygons.push_back(this->contour); + for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { + polygons.push_back(*it); + } + return polygons; +} + +void +ExPolygon::scale(double factor) +{ + contour.scale(factor); + for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { + (*it).scale(factor); + } +} + +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); + } +} + +void +ExPolygon::rotate(double angle, const Point ¢er) +{ + contour.rotate(angle, center); + for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { + (*it).rotate(angle, center); + } +} + +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 + } + return a; +} + +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) { + if (!(*it).is_valid() || (*it).is_counter_clockwise()) return false; + } + return true; +} + +bool +ExPolygon::contains_line(const Line &line) const +{ + return this->contains_polyline(line); +} + +bool +ExPolygon::contains_polyline(const Polyline &polyline) const +{ + Polylines pl_out; + diff((Polylines)polyline, *this, pl_out); + return pl_out.empty(); +} + +bool +ExPolygon::contains_point(const Point &point) const +{ + if (!this->contour.contains_point(point)) return false; + for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { + if (it->contains_point(point)) return false; + } + return true; +} + +Polygons +ExPolygon::simplify_p(double tolerance) const +{ + Polygons pp; + pp.reserve(this->holes.size() + 1); + + // contour + Polygon p = this->contour; + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + pp.push_back(p); + + // holes + for (Polygons::const_iterator it = this->holes.begin(); it != this->holes.end(); ++it) { + p = *it; + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + pp.push_back(p); + } + simplify_polygons(pp, pp); + return pp; +} + +ExPolygons +ExPolygon::simplify(double tolerance) const +{ + Polygons pp = this->simplify_p(tolerance); + ExPolygons expp; + union_(pp, expp); + return expp; +} + +void +ExPolygon::simplify(double tolerance, ExPolygons &expolygons) const +{ + ExPolygons ep = this->simplify(tolerance); + expolygons.reserve(expolygons.size() + ep.size()); + expolygons.insert(expolygons.end(), ep.begin(), ep.end()); +} + +void +ExPolygon::medial_axis(double max_width, double min_width, Polylines* polylines) const +{ + // init helper object + Slic3r::Geometry::MedialAxis ma(max_width, min_width); + + // populate list of segments for the Voronoi diagram + this->contour.lines(&ma.lines); + for (Polygons::const_iterator hole = this->holes.begin(); hole != this->holes.end(); ++hole) + hole->lines(&ma.lines); + + // compute the Voronoi diagram + ma.build(polylines); + + // clip segments to our expolygon area + // (do this before extending endpoints as external segments coule be extended into + // expolygon, this leaving wrong things inside) + intersection(*polylines, *this, *polylines); + + // extend initial and final segments of each polyline (they will be clipped) + // unless they represent closed loops + for (Polylines::iterator polyline = polylines->begin(); polyline != polylines->end(); ++polyline) { + if (polyline->points.front().distance_to(polyline->points.back()) < min_width) continue; + polyline->extend_start(max_width); + polyline->extend_end(max_width); + } + + // clip again after extending endpoints to prevent them from exceeding the expolygon boundaries + intersection(*polylines, *this, *polylines); +} + +void +ExPolygon::get_trapezoids(Polygons* polygons) const +{ + ExPolygons expp; + expp.push_back(*this); + boost::polygon::get_trapezoids(*polygons, expp); +} + +void +ExPolygon::get_trapezoids(Polygons* polygons, double angle) const +{ + ExPolygon clone = *this; + clone.rotate(PI/2 - angle, Point(0,0)); + clone.get_trapezoids(polygons); + for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon) + polygon->rotate(-(PI/2 - angle), Point(0,0)); +} + +// This algorithm may return more trapezoids than necessary +// (i.e. it may break a single trapezoid in several because +// other parts of the object have x coordinates in the middle) +void +ExPolygon::get_trapezoids2(Polygons* polygons) const +{ + // get all points of this ExPolygon + Points pp = *this; + + // build our bounding box + BoundingBox bb(pp); + + // get all x coordinates + std::vector xx; + xx.reserve(pp.size()); + for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) + xx.push_back(p->x); + std::sort(xx.begin(), xx.end()); + + // find trapezoids by looping from first to next-to-last coordinate + for (std::vector::const_iterator x = xx.begin(); x != xx.end()-1; ++x) { + coord_t next_x = *(x + 1); + if (*x == next_x) continue; + + // 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; + + // intersect with this expolygon + Polygons trapezoids; + intersection(poly, *this, trapezoids); + + // append results to return value + polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end()); + } +} + +void +ExPolygon::get_trapezoids2(Polygons* polygons, double angle) const +{ + ExPolygon clone = *this; + clone.rotate(PI/2 - angle, Point(0,0)); + clone.get_trapezoids2(polygons); + for (Polygons::iterator polygon = polygons->begin(); polygon != polygons->end(); ++polygon) + polygon->rotate(-(PI/2 - angle), Point(0,0)); +} + +// While this triangulates successfully, it's NOT a constrained triangulation +// as it will create more vertices on the boundaries than the ones supplied. +void +ExPolygon::triangulate(Polygons* polygons) const +{ + // first make trapezoids + Polygons trapezoids; + this->get_trapezoids2(&trapezoids); + + // then triangulate each trapezoid + for (Polygons::iterator polygon = trapezoids.begin(); polygon != trapezoids.end(); ++polygon) + polygon->triangulate_convex(polygons); +} + +void +ExPolygon::triangulate_pp(Polygons* polygons) const +{ + // convert polygons + std::list input; + + Polygons pp = *this; + simplify_polygons(pp, pp, true); + ExPolygons expp; + union_(pp, expp); + + for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { + // contour + { + TPPLPoly p; + p.Init(ex->contour.points.size()); + //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); + } + p.SetHole(false); + input.push_back(p); + } + + // holes + for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) { + TPPLPoly p; + p.Init(hole->points.size()); + //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); + } + p.SetHole(true); + input.push_back(p); + } + } + + // perform triangulation + std::list output; + int res = TPPLPartition().Triangulate_MONO(&input, &output); + if (res != 1) CONFESS("Triangulation failed"); + + // convert output polygons + for (std::list::iterator poly = output.begin(); poly != output.end(); ++poly) { + long num_points = poly->GetNumPoints(); + Polygon p; + p.points.resize(num_points); + for (long i = 0; i < num_points; ++i) { + p.points[i].x = (*poly)[i].x; + p.points[i].y = (*poly)[i].y; + } + polygons->push_back(p); + } +} + +void +ExPolygon::triangulate_p2t(Polygons* polygons) const +{ + ExPolygons expp; + simplify_polygons(*this, expp, true); + + for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { + p2t::CDT* cdt; + + // TODO: prevent duplicate points + + // contour + { + std::vector points; + for (Points::const_iterator point = ex->contour.points.begin(); point != ex->contour.points.end(); ++point) { + points.push_back(new p2t::Point(point->x, point->y)); + } + cdt = new p2t::CDT(points); + } + + // holes + for (Polygons::const_iterator hole = ex->holes.begin(); hole != ex->holes.end(); ++hole) { + std::vector points; + for (Points::const_iterator point = hole->points.begin(); point != hole->points.end(); ++point) { + points.push_back(new p2t::Point(point->x, point->y)); + } + cdt->AddHole(points); + } + + // perform triangulation + cdt->Triangulate(); + std::vector triangles = cdt->GetTriangles(); + + for (std::vector::const_iterator triangle = triangles.begin(); triangle != triangles.end(); ++triangle) { + Polygon p; + for (int i = 0; i <= 2; ++i) { + p2t::Point* point = (*triangle)->GetPoint(i); + p.points.push_back(Point(point->x, point->y)); + } + polygons->push_back(p); + } + } +} + +#ifdef SLIC3RXS + +REGISTER_CLASS(ExPolygon, "ExPolygon"); + +SV* +ExPolygon::to_AV() { + const unsigned int num_holes = this->holes.size(); + AV* av = newAV(); + av_extend(av, num_holes); // -1 +1 + + av_store(av, 0, perl_to_SV_ref(this->contour)); + + for (unsigned int i = 0; i < num_holes; i++) { + av_store(av, i+1, perl_to_SV_ref(this->holes[i])); + } + return newRV_noinc((SV*)av); +} + +SV* +ExPolygon::to_SV_pureperl() const +{ + const unsigned int num_holes = this->holes.size(); + AV* av = newAV(); + av_extend(av, num_holes); // -1 +1 + av_store(av, 0, this->contour.to_SV_pureperl()); + for (unsigned int i = 0; i < num_holes; i++) { + av_store(av, i+1, this->holes[i].to_SV_pureperl()); + } + return newRV_noinc((SV*)av); +} + +void +ExPolygon::from_SV(SV* expoly_sv) +{ + AV* expoly_av = (AV*)SvRV(expoly_sv); + const unsigned int num_polygons = av_len(expoly_av)+1; + this->holes.resize(num_polygons-1); + + SV** polygon_sv = av_fetch(expoly_av, 0, 0); + this->contour.from_SV(*polygon_sv); + for (unsigned int i = 0; i < num_polygons-1; i++) { + polygon_sv = av_fetch(expoly_av, i+1, 0); + this->holes[i].from_SV(*polygon_sv); + } +} + +void +ExPolygon::from_SV_check(SV* expoly_sv) +{ + if (sv_isobject(expoly_sv) && (SvTYPE(SvRV(expoly_sv)) == SVt_PVMG)) { + if (!sv_isa(expoly_sv, perl_class_name(this)) && !sv_isa(expoly_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object", perl_class_name(this)); + // a XS ExPolygon was supplied + *this = *(ExPolygon *)SvIV((SV*)SvRV( expoly_sv )); + } else { + // a Perl arrayref was supplied + this->from_SV(expoly_sv); + } +} +#endif + +} diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp new file mode 100644 index 000000000..be6065914 --- /dev/null +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -0,0 +1,154 @@ +#ifndef slic3r_ExPolygon_hpp_ +#define slic3r_ExPolygon_hpp_ + +#include "Polygon.hpp" +#include "Polyline.hpp" +#include + +namespace Slic3r { + +class ExPolygon; +typedef std::vector ExPolygons; + +class ExPolygon +{ + public: + Polygon contour; + Polygons holes; + operator Points() const; + operator Polygons() const; + void scale(double factor); + void translate(double x, double y); + void rotate(double angle, const Point ¢er); + double area() const; + bool is_valid() const; + bool contains_line(const Line &line) const; + bool contains_polyline(const Polyline &polyline) const; + bool contains_point(const Point &point) const; + Polygons simplify_p(double tolerance) const; + ExPolygons simplify(double tolerance) const; + void simplify(double tolerance, ExPolygons &expolygons) const; + void medial_axis(double max_width, double min_width, Polylines* polylines) const; + void get_trapezoids(Polygons* polygons) const; + void get_trapezoids(Polygons* polygons, double angle) const; + void get_trapezoids2(Polygons* polygons) const; + void get_trapezoids2(Polygons* polygons, double angle) const; + void triangulate(Polygons* polygons) const; + void triangulate_pp(Polygons* polygons) const; + void triangulate_p2t(Polygons* polygons) const; + + #ifdef SLIC3RXS + void from_SV(SV* poly_sv); + void from_SV_check(SV* poly_sv); + SV* to_AV(); + SV* to_SV_pureperl() const; + #endif +}; + +} + +// start Boost +#include +namespace boost { namespace polygon { + template <> + struct polygon_traits { + typedef coord_t coordinate_type; + typedef Points::const_iterator iterator_type; + typedef Point point_type; + + // Get the begin iterator + static inline iterator_type begin_points(const ExPolygon& t) { + return t.contour.points.begin(); + } + + // Get the end iterator + static inline iterator_type end_points(const ExPolygon& t) { + return t.contour.points.end(); + } + + // Get the number of sides of the polygon + static inline std::size_t size(const ExPolygon& t) { + return t.contour.points.size(); + } + + // Get the winding direction of the polygon + static inline winding_direction winding(const ExPolygon& t) { + return unknown_winding; + } + }; + + template <> + struct polygon_mutable_traits { + //expects stl style iterators + template + static inline ExPolygon& set_points(ExPolygon& expolygon, iT input_begin, iT input_end) { + expolygon.contour.points.assign(input_begin, input_end); + // skip last point since Boost will set last point = first point + expolygon.contour.points.pop_back(); + return expolygon; + } + }; + + + template <> + struct geometry_concept { typedef polygon_with_holes_concept type; }; + + template <> + struct polygon_with_holes_traits { + typedef Polygons::const_iterator iterator_holes_type; + typedef Polygon hole_type; + static inline iterator_holes_type begin_holes(const ExPolygon& t) { + return t.holes.begin(); + } + static inline iterator_holes_type end_holes(const ExPolygon& t) { + return t.holes.end(); + } + static inline unsigned int size_holes(const ExPolygon& t) { + return t.holes.size(); + } + }; + + template <> + struct polygon_with_holes_mutable_traits { + template + static inline ExPolygon& set_holes(ExPolygon& t, iT inputBegin, iT inputEnd) { + t.holes.assign(inputBegin, inputEnd); + return t; + } + }; + + //first we register CPolygonSet as a polygon set + template <> + struct geometry_concept { typedef polygon_set_concept type; }; + + //next we map to the concept through traits + template <> + struct polygon_set_traits { + typedef coord_t coordinate_type; + typedef ExPolygons::const_iterator iterator_type; + typedef ExPolygons operator_arg_type; + + static inline iterator_type begin(const ExPolygons& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const ExPolygons& polygon_set) { + return polygon_set.end(); + } + + //don't worry about these, just return false from them + static inline bool clean(const ExPolygons& polygon_set) { return false; } + static inline bool sorted(const ExPolygons& polygon_set) { return false; } + }; + + template <> + struct polygon_set_mutable_traits { + template + static inline void set(ExPolygons& expolygons, input_iterator_type input_begin, input_iterator_type input_end) { + expolygons.assign(input_begin, input_end); + } + }; +} } +// end Boost + +#endif diff --git a/xs/src/libslic3r/ExPolygonCollection.cpp b/xs/src/libslic3r/ExPolygonCollection.cpp new file mode 100644 index 000000000..3de86e7c6 --- /dev/null +++ b/xs/src/libslic3r/ExPolygonCollection.cpp @@ -0,0 +1,90 @@ +#include "ExPolygonCollection.hpp" +#include "Geometry.hpp" + +namespace Slic3r { + +ExPolygonCollection::operator Points() const +{ + Points points; + Polygons pp = *this; + for (Polygons::const_iterator poly = pp.begin(); poly != pp.end(); ++poly) { + for (Points::const_iterator point = poly->points.begin(); point != poly->points.end(); ++point) + points.push_back(*point); + } + return points; +} + +ExPolygonCollection::operator Polygons() const +{ + Polygons polygons; + for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { + polygons.push_back(it->contour); + for (Polygons::const_iterator ith = it->holes.begin(); ith != it->holes.end(); ++ith) { + polygons.push_back(*ith); + } + } + return polygons; +} + +ExPolygonCollection::operator ExPolygons&() +{ + return this->expolygons; +} + +void +ExPolygonCollection::scale(double factor) +{ + for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) { + (*it).scale(factor); + } +} + +void +ExPolygonCollection::translate(double x, double y) +{ + for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) { + (*it).translate(x, y); + } +} + +void +ExPolygonCollection::rotate(double angle, const Point ¢er) +{ + for (ExPolygons::iterator it = expolygons.begin(); it != expolygons.end(); ++it) { + (*it).rotate(angle, center); + } +} + +bool +ExPolygonCollection::contains_point(const Point &point) const +{ + for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { + if (it->contains_point(point)) return true; + } + return false; +} + +void +ExPolygonCollection::simplify(double tolerance) +{ + ExPolygons expp; + for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) { + it->simplify(tolerance, expp); + } + this->expolygons = expp; +} + +void +ExPolygonCollection::convex_hull(Polygon* hull) const +{ + Points pp; + for (ExPolygons::const_iterator it = this->expolygons.begin(); it != this->expolygons.end(); ++it) + pp.insert(pp.end(), it->contour.points.begin(), it->contour.points.end()); + Slic3r::Geometry::convex_hull(pp, hull); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(ExPolygonCollection, "ExPolygon::Collection"); +#endif + +} diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp new file mode 100644 index 000000000..f6a27284f --- /dev/null +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_ExPolygonCollection_hpp_ +#define slic3r_ExPolygonCollection_hpp_ + +#include +#include "ExPolygon.hpp" + +namespace Slic3r { + +class ExPolygonCollection; +typedef std::vector ExPolygonCollections; + +class ExPolygonCollection +{ + public: + ExPolygons expolygons; + + ExPolygonCollection() {}; + ExPolygonCollection(const ExPolygons &expolygons) : expolygons(expolygons) {}; + operator Points() const; + operator Polygons() const; + operator ExPolygons&(); + void scale(double factor); + void translate(double x, double y); + void rotate(double angle, const Point ¢er); + bool contains_point(const Point &point) const; + void simplify(double tolerance); + void convex_hull(Polygon* hull) const; +}; + +} + +#endif diff --git a/xs/src/libslic3r/Extruder.cpp b/xs/src/libslic3r/Extruder.cpp new file mode 100644 index 000000000..d327276f8 --- /dev/null +++ b/xs/src/libslic3r/Extruder.cpp @@ -0,0 +1,128 @@ +#include "Extruder.hpp" + +namespace Slic3r { + +Extruder::Extruder(int id, PrintConfig *config) +: id(id), + config(config) +{ + reset(); +} + +void +Extruder::reset() +{ + this->E = 0; + this->absolute_E = 0; + this->retracted = 0; + this->restart_extra = 0; +} + +double +Extruder::extrude(double dE) +{ + if (this->config->use_relative_e_distances) { + this->E = 0; + } + + this->E += dE; + this->absolute_E += dE; + return this->E; +} + +Pointf +Extruder::extruder_offset() const +{ + return this->config->extruder_offset.get_at(this->id); +} + +double +Extruder::nozzle_diameter() const +{ + return this->config->nozzle_diameter.get_at(this->id); +} + +double +Extruder::filament_diameter() const +{ + return this->config->filament_diameter.get_at(this->id); +} + +double +Extruder::extrusion_multiplier() const +{ + return this->config->extrusion_multiplier.get_at(this->id); +} + +int +Extruder::temperature() const +{ + return this->config->temperature.get_at(this->id); +} + +int +Extruder::first_layer_temperature() const +{ + return this->config->first_layer_temperature.get_at(this->id); +} + +double +Extruder::retract_length() const +{ + return this->config->retract_length.get_at(this->id); +} + +double +Extruder::retract_lift() const +{ + return this->config->retract_lift.get_at(this->id); +} + +int +Extruder::retract_speed() const +{ + return this->config->retract_speed.get_at(this->id); +} + +double +Extruder::retract_restart_extra() const +{ + return this->config->retract_restart_extra.get_at(this->id); +} + +double +Extruder::retract_before_travel() const +{ + return this->config->retract_before_travel.get_at(this->id); +} + +bool +Extruder::retract_layer_change() const +{ + return this->config->retract_layer_change.get_at(this->id); +} + +double +Extruder::retract_length_toolchange() const +{ + return this->config->retract_length_toolchange.get_at(this->id); +} + +double +Extruder::retract_restart_extra_toolchange() const +{ + return this->config->retract_restart_extra_toolchange.get_at(this->id); +} + +bool +Extruder::wipe() const +{ + return this->config->wipe.get_at(this->id); +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(Extruder, "Extruder"); +#endif + +} diff --git a/xs/src/libslic3r/Extruder.hpp b/xs/src/libslic3r/Extruder.hpp new file mode 100644 index 000000000..9caa38c89 --- /dev/null +++ b/xs/src/libslic3r/Extruder.hpp @@ -0,0 +1,46 @@ +#ifndef slic3r_Extruder_hpp_ +#define slic3r_Extruder_hpp_ + +#include +#include "Point.hpp" +#include "PrintConfig.hpp" + +namespace Slic3r { + +class Extruder +{ + public: + Extruder(int id, PrintConfig *config); + virtual ~Extruder() {} + void reset(); + double extrude(double dE); + + + Pointf extruder_offset() const; + double nozzle_diameter() const; + double filament_diameter() const; + double extrusion_multiplier() const; + int temperature() const; + int first_layer_temperature() const; + double retract_length() const; + double retract_lift() const; + int retract_speed() const; + double retract_restart_extra() const; + double retract_before_travel() const; + bool retract_layer_change() const; + double retract_length_toolchange() const; + double retract_restart_extra_toolchange() const; + bool wipe() const; + + int id; + double E; + double absolute_E; + double retracted; + double restart_extra; + + PrintConfig *config; +}; + +} + +#endif diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp new file mode 100644 index 000000000..fd39fdc9f --- /dev/null +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -0,0 +1,335 @@ +#include "ExtrusionEntity.hpp" +#include "ExtrusionEntityCollection.hpp" +#include "ExPolygonCollection.hpp" +#include "ClipperUtils.hpp" +#include "Extruder.hpp" +#include + +namespace Slic3r { + +ExtrusionPath* +ExtrusionPath::clone() const +{ + return new ExtrusionPath (*this); +} + +void +ExtrusionPath::reverse() +{ + this->polyline.reverse(); +} + +Point +ExtrusionPath::first_point() const +{ + return this->polyline.points.front(); +} + +Point +ExtrusionPath::last_point() const +{ + return this->polyline.points.back(); +} + +void +ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const +{ + // perform clipping + Polylines clipped; + intersection(this->polyline, collection, clipped); + return this->_inflate_collection(clipped, retval); +} + +void +ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const +{ + // perform clipping + Polylines clipped; + diff(this->polyline, collection, clipped); + return this->_inflate_collection(clipped, retval); +} + +void +ExtrusionPath::clip_end(double distance) +{ + this->polyline.clip_end(distance); +} + +void +ExtrusionPath::simplify(double tolerance) +{ + this->polyline.simplify(tolerance); +} + +double +ExtrusionPath::length() const +{ + return this->polyline.length(); +} + +bool +ExtrusionPath::is_perimeter() const +{ + return this->role == erPerimeter + || this->role == erExternalPerimeter + || this->role == erOverhangPerimeter; +} + +bool +ExtrusionPath::is_fill() const +{ + return this->role == erInternalInfill + || this->role == erSolidInfill + || this->role == erTopSolidInfill; +} + +bool +ExtrusionPath::is_bridge() const +{ + return this->role == erBridgeInfill + || this->role == erOverhangPerimeter; +} + +void +ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const +{ + for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) { + ExtrusionPath* path = this->clone(); + path->polyline = *it; + collection->entities.push_back(path); + } +} + +#ifdef SLIC3RXS +REGISTER_CLASS(ExtrusionPath, "ExtrusionPath"); +#endif + +std::string +ExtrusionPath::gcode(Extruder* extruder, double e, double F, + double xofs, double yofs, std::string extrusion_axis, + std::string gcode_line_suffix) const +{ + dSP; + + std::stringstream stream; + stream.setf(std::ios::fixed); + + double local_F = F; + + Lines lines = this->polyline.lines(); + for (Lines::const_iterator line_it = lines.begin(); + line_it != lines.end(); ++line_it) + { + const double line_length = line_it->length() * SCALING_FACTOR; + + // calculate extrusion length for this line + double E = (e == 0) ? 0 : extruder->extrude(e * line_length); + + // compose G-code line + + Point point = line_it->b; + const double x = point.x * SCALING_FACTOR + xofs; + const double y = point.y * SCALING_FACTOR + yofs; + stream.precision(3); + stream << "G1 X" << x << " Y" << y; + + if (E != 0) { + stream.precision(5); + stream << " " << extrusion_axis << E; + } + + if (local_F != 0) { + stream.precision(3); + stream << " F" << local_F; + local_F = 0; + } + + stream << gcode_line_suffix; + stream << "\n"; + } + + return stream.str(); +} + +ExtrusionLoop::operator Polygon() const +{ + Polygon polygon; + this->polygon(&polygon); + return polygon; +} + +ExtrusionLoop* +ExtrusionLoop::clone() const +{ + return new ExtrusionLoop (*this); +} + +bool +ExtrusionLoop::make_clockwise() +{ + Polygon polygon = *this; + bool was_ccw = polygon.is_counter_clockwise(); + if (was_ccw) this->reverse(); + return was_ccw; +} + +bool +ExtrusionLoop::make_counter_clockwise() +{ + Polygon polygon = *this; + bool was_cw = polygon.is_clockwise(); + if (was_cw) this->reverse(); + return was_cw; +} + +void +ExtrusionLoop::reverse() +{ + for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) + path->reverse(); + std::reverse(this->paths.begin(), this->paths.end()); +} + +Point +ExtrusionLoop::first_point() const +{ + return this->paths.front().polyline.points.front(); +} + +Point +ExtrusionLoop::last_point() const +{ + return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way +} + +void +ExtrusionLoop::polygon(Polygon* polygon) const +{ + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { + // for each polyline, append all points except the last one (because it coincides with the first one of the next polyline) + polygon->points.insert(polygon->points.end(), path->polyline.points.begin(), path->polyline.points.end()-1); + } +} + +double +ExtrusionLoop::length() const +{ + double len = 0; + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + len += path->polyline.length(); + return len; +} + +void +ExtrusionLoop::split_at_vertex(const Point &point) +{ + for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) { + int idx = path->polyline.find_point(point); + if (idx != -1) { + if (this->paths.size() == 1) { + // just change the order of points + path->polyline.points.insert(path->polyline.points.end(), path->polyline.points.begin() + 1, path->polyline.points.begin() + idx + 1); + path->polyline.points.erase(path->polyline.points.begin(), path->polyline.points.begin() + idx); + } else { + // if we have multiple paths we assume they have different types, so no need to + // check for continuity as we do for the single path case above + + // new paths list starts with the second half of current path + ExtrusionPaths new_paths; + { + ExtrusionPath p = *path; + p.polyline.points.erase(p.polyline.points.begin(), p.polyline.points.begin() + idx); + if (p.polyline.is_valid()) new_paths.push_back(p); + } + + // then we add all paths until the end of current path list + new_paths.insert(new_paths.end(), this->paths.begin(), path); // not including this path + + // then we add all paths since the beginning of current list up to the previous one + new_paths.insert(new_paths.end(), path+1, this->paths.end()); // not including this path + + // finally we add the first half of current path + { + ExtrusionPath p = *path; + p.polyline.points.erase(p.polyline.points.begin() + idx + 1, p.polyline.points.end()); + if (p.polyline.is_valid()) new_paths.push_back(p); + } + // we can now override the old path list with the new one and stop looping + this->paths = new_paths; + } + return; + } + } + CONFESS("Point not found"); +} + +void +ExtrusionLoop::split_at(const Point &point) +{ + if (this->paths.empty()) return; + + // find the closest path and closest point + size_t path_idx = 0; + Point p = this->paths.front().first_point(); + double min = point.distance_to(p); + 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); + if (dist < min) { + p = p_tmp; + min = dist; + path_idx = path - this->paths.begin(); + } + } + + // now split path_idx in two parts + ExtrusionPath p1 = this->paths[path_idx]; + ExtrusionPath p2 = p1; + this->paths[path_idx].polyline.split_at(p, &p1.polyline, &p2.polyline); + + // install the two paths + this->paths.erase(this->paths.begin() + path_idx); + if (p2.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p2); + if (p1.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p1); + + // split at the new vertex + this->split_at_vertex(p); +} + +void +ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const +{ + *paths = this->paths; + + while (distance > 0 && !paths->empty()) { + ExtrusionPath &last = paths->back(); + double len = last.length(); + if (len <= distance) { + paths->pop_back(); + distance -= len; + } else { + last.polyline.clip_end(distance); + break; + } + } +} + +bool +ExtrusionLoop::has_overhang_point(const Point &point) const +{ + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { + int pos = path->polyline.find_point(point); + if (pos != -1) { + // point belongs to this path + // we consider it overhang only if it's not an endpoint + return (path->is_bridge() && pos > 0 && pos != path->polyline.points.size()-1); + } + } + return false; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); +#endif + +} diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp new file mode 100644 index 000000000..ccd85a689 --- /dev/null +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -0,0 +1,104 @@ +#ifndef slic3r_ExtrusionEntity_hpp_ +#define slic3r_ExtrusionEntity_hpp_ + +#include +#include "Polygon.hpp" +#include "Polyline.hpp" + +namespace Slic3r { + +class ExPolygonCollection; +class ExtrusionEntityCollection; +class Extruder; + +/* Each ExtrusionRole value identifies a distinct set of { extruder, speed } */ +enum ExtrusionRole { + erPerimeter, + erExternalPerimeter, + erOverhangPerimeter, + erInternalInfill, + erSolidInfill, + erTopSolidInfill, + erBridgeInfill, + erGapFill, + erSkirt, + erSupportMaterial, + erSupportMaterialInterface, +}; + +/* Special flags describing loop */ +enum ExtrusionLoopRole { + elrDefault, + elrExternalPerimeter, + elrContourInternalPerimeter, +}; + +class ExtrusionEntity +{ + public: + virtual ExtrusionEntity* clone() const = 0; + virtual ~ExtrusionEntity() {}; + virtual void reverse() = 0; + virtual Point first_point() const = 0; + virtual Point last_point() const = 0; +}; + +typedef std::vector ExtrusionEntitiesPtr; + +class ExtrusionPath : public ExtrusionEntity +{ + public: + Polyline polyline; + ExtrusionRole role; + double mm3_per_mm; // mm^3 of plastic per mm of linear head motion + float width; + float height; + + ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; + ExtrusionPath* clone() const; + void reverse(); + Point first_point() const; + Point last_point() const; + void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; + void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; + void clip_end(double distance); + void simplify(double tolerance); + double length() const; + bool is_perimeter() const; + bool is_fill() const; + bool is_bridge() const; + std::string gcode(Extruder* extruder, double e, double F, + double xofs, double yofs, std::string extrusion_axis, + std::string gcode_line_suffix) const; + + private: + void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; +}; + +typedef std::vector ExtrusionPaths; + +class ExtrusionLoop : public ExtrusionEntity +{ + public: + ExtrusionPaths paths; + ExtrusionLoopRole role; + + ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : role(role) {}; + operator Polygon() const; + ExtrusionLoop* clone() const; + bool make_clockwise(); + bool make_counter_clockwise(); + void reverse(); + Point first_point() const; + Point last_point() const; + void polygon(Polygon* polygon) const; + double length() const; + void split_at_vertex(const Point &point); + void split_at(const Point &point); + void clip_end(double distance, ExtrusionPaths* paths) const; + bool has_overhang_point(const Point &point) const; +}; + +} + +#endif diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp new file mode 100644 index 000000000..a958e53cf --- /dev/null +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -0,0 +1,115 @@ +#include "ExtrusionEntityCollection.hpp" +#include +#include + +namespace Slic3r { + +ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionEntityCollection& collection) + : no_sort(collection.no_sort), orig_indices(collection.orig_indices) +{ + this->entities.reserve(collection.entities.size()); + for (ExtrusionEntitiesPtr::const_iterator it = collection.entities.begin(); it != collection.entities.end(); ++it) + this->entities.push_back((*it)->clone()); +} + +ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const ExtrusionEntityCollection &other) +{ + ExtrusionEntityCollection tmp(other); + this->swap(tmp); + return *this; +} + +void +ExtrusionEntityCollection::swap (ExtrusionEntityCollection &c) +{ + std::swap(this->entities, c.entities); + std::swap(this->orig_indices, c.orig_indices); + std::swap(this->no_sort, c.no_sort); +} + +ExtrusionEntityCollection* +ExtrusionEntityCollection::clone() const +{ + return new ExtrusionEntityCollection(*this); +} + +void +ExtrusionEntityCollection::reverse() +{ + for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + (*it)->reverse(); + } + std::reverse(this->entities.begin(), this->entities.end()); +} + +Point +ExtrusionEntityCollection::first_point() const +{ + return this->entities.front()->first_point(); +} + +Point +ExtrusionEntityCollection::last_point() const +{ + return this->entities.back()->last_point(); +} + +void +ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, std::vector* orig_indices) const +{ + if (this->entities.empty()) return; + this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, orig_indices); +} + +void +ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse, std::vector* orig_indices) const +{ + if (this->no_sort) { + *retval = *this; + return; + } + retval->entities.reserve(this->entities.size()); + retval->orig_indices.reserve(this->entities.size()); + + // if we're asked to return the original indices, build a map + std::map indices_map; + + ExtrusionEntitiesPtr my_paths; + for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + ExtrusionEntity* entity = (*it)->clone(); + my_paths.push_back(entity); + if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin(); + } + + Points endpoints; + for (ExtrusionEntitiesPtr::iterator it = my_paths.begin(); it != my_paths.end(); ++it) { + endpoints.push_back((*it)->first_point()); + if (no_reverse) { + endpoints.push_back((*it)->first_point()); + } else { + endpoints.push_back((*it)->last_point()); + } + } + + while (!my_paths.empty()) { + // find nearest point + int start_index = start_near.nearest_point_index(endpoints); + int path_index = start_index/2; + ExtrusionEntity* entity = my_paths.at(path_index); + if (start_index % 2 && !no_reverse) { + entity->reverse(); + } + retval->entities.push_back(my_paths.at(path_index)); + if (orig_indices != NULL) orig_indices->push_back(indices_map[entity]); + my_paths.erase(my_paths.begin() + path_index); + endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); + start_near = retval->entities.back()->last_point(); + } +} + +#ifdef SLIC3RXS +// there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection +REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); +#endif + +} diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp new file mode 100644 index 000000000..bc660611b --- /dev/null +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -0,0 +1,29 @@ +#ifndef slic3r_ExtrusionEntityCollection_hpp_ +#define slic3r_ExtrusionEntityCollection_hpp_ + +#include +#include "ExtrusionEntity.hpp" + +namespace Slic3r { + +class ExtrusionEntityCollection : public ExtrusionEntity +{ + public: + ExtrusionEntityCollection* clone() const; + ExtrusionEntitiesPtr entities; + std::vector orig_indices; // handy for XS + bool no_sort; + ExtrusionEntityCollection(): no_sort(false) {}; + ExtrusionEntityCollection(const ExtrusionEntityCollection &collection); + ExtrusionEntityCollection& operator= (const ExtrusionEntityCollection &other); + void swap (ExtrusionEntityCollection &c); + void chained_path(ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; + void chained_path_from(Point start_near, ExtrusionEntityCollection* retval, bool no_reverse = false, std::vector* orig_indices = NULL) const; + void reverse(); + Point first_point() const; + Point last_point() const; +}; + +} + +#endif diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp new file mode 100644 index 000000000..1592e0c35 --- /dev/null +++ b/xs/src/libslic3r/Flow.cpp @@ -0,0 +1,120 @@ +#include "Flow.hpp" +#include + +namespace Slic3r { + +/* This constructor builds a Flow object from an extrusion width config setting + and other context properties. */ +Flow +Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) { + // we need layer height unless it's a bridge + if (height <= 0 && bridge_flow_ratio == 0) CONFESS("Invalid flow height supplied to new_from_config_width()"); + + float w; + if (bridge_flow_ratio > 0) { + // if bridge flow was requested, calculate bridge width + w = Flow::_bridge_width(nozzle_diameter, bridge_flow_ratio); + } else if (!width.percent && width.value == 0) { + // if user left option to 0, calculate a sane default width + w = Flow::_auto_width(role, nozzle_diameter, height); + } else { + // if user set a manual value, use it + w = width.get_abs_value(height); + } + + return Flow(w, height, nozzle_diameter, bridge_flow_ratio > 0); +} + +/* This constructor builds a Flow object from a given centerline spacing. */ +Flow +Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { + // we need layer height unless it's a bridge + if (height <= 0 && !bridge) CONFESS("Invalid flow height supplied to new_from_spacing()"); + + float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge); + return Flow(w, height, nozzle_diameter, bridge); +} + +/* This method returns the centerline spacing between two adjacent extrusions + having the same extrusion width (and other properties). */ +float +Flow::spacing() const { + if (this->bridge) { + return this->width + BRIDGE_EXTRA_SPACING; + } + + // rectangle with semicircles at the ends + float min_flow_spacing = this->width - this->height * (1 - PI/4.0); + return this->width - OVERLAP_FACTOR * (this->width - min_flow_spacing); +} + +/* This method returns the centerline spacing between an extrusion using this + flow and another one using another flow. + this->spacing(other) shall return the same value as other.spacing(*this) */ +float +Flow::spacing(const Flow &other) const { + assert(this->height == other.height); + assert(this->bridge == other.bridge); + + if (this->bridge) { + return this->width/2 + other.width/2 + BRIDGE_EXTRA_SPACING; + } + + return this->spacing()/2 + other.spacing()/2; +} + +/* This method returns extrusion volume per head move unit. */ +double +Flow::mm3_per_mm() const { + if (this->bridge) { + return (this->width * this->width) * PI/4.0; + } + + // rectangle with semicircles at the ends + return this->width * this->height + (this->height*this->height) / 4.0 * (PI-4.0); +} + +/* This static method returns bridge width for a given nozzle diameter. */ +float +Flow::_bridge_width(float nozzle_diameter, float bridge_flow_ratio) { + if (bridge_flow_ratio == 1) return nozzle_diameter; // optimization to avoid sqrt() + return sqrt(bridge_flow_ratio * (nozzle_diameter*nozzle_diameter)); +} + +/* This static method returns a sane extrusion width default. */ +float +Flow::_auto_width(FlowRole role, float nozzle_diameter, float height) { + // here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate + // shape: rectangle with semicircles at the ends + float width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height); + + float min = nozzle_diameter * 1.05; + float max = -1; + if (role == frExternalPerimeter || role == frSupportMaterial) { + min = max = nozzle_diameter; + } else if (role != frInfill) { + // do not limit width for sparse infill so that we use full native flow for it + max = nozzle_diameter * 1.7; + } + if (max != -1 && width > max) width = max; + if (width < min) width = min; + + return width; +} + +/* This static method returns the extrusion width value corresponding to the supplied centerline spacing. */ +float +Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { + if (bridge) { + return spacing - BRIDGE_EXTRA_SPACING; + } + + // rectangle with semicircles at the ends + return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(Flow, "Flow"); +#endif + +} diff --git a/xs/src/libslic3r/Flow.hpp b/xs/src/libslic3r/Flow.hpp new file mode 100644 index 000000000..88cae2477 --- /dev/null +++ b/xs/src/libslic3r/Flow.hpp @@ -0,0 +1,53 @@ +#ifndef slic3r_Flow_hpp_ +#define slic3r_Flow_hpp_ + +#include +#include "Config.hpp" +#include "ExtrusionEntity.hpp" + +namespace Slic3r { + +#define BRIDGE_EXTRA_SPACING 0.05 +#define OVERLAP_FACTOR 1.0 + +enum FlowRole { + frExternalPerimeter, + frPerimeter, + frInfill, + frSolidInfill, + frTopSolidInfill, + frSupportMaterial, + frSupportMaterialInterface, +}; + +class Flow +{ + public: + float width, height, nozzle_diameter; + bool bridge; + + Flow(float _w, float _h, float _nd, bool _bridge = false) + : width(_w), height(_h), nozzle_diameter(_nd), bridge(_bridge) {}; + float spacing() const; + float spacing(const Flow &other) const; + double mm3_per_mm() const; + coord_t scaled_width() const { + return scale_(this->width); + }; + coord_t scaled_spacing() const { + return scale_(this->spacing()); + }; + + static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); + static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + + private: + static float _bridge_width(float nozzle_diameter, float bridge_flow_ratio); + static float _auto_width(FlowRole role, float nozzle_diameter, float height); + static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio); +}; + +} + +#endif diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp new file mode 100644 index 000000000..a889532ea --- /dev/null +++ b/xs/src/libslic3r/GCode.hpp @@ -0,0 +1,35 @@ +#ifndef slic3r_GCode_hpp_ +#define slic3r_GCode_hpp_ + +#include +#include + +namespace Slic3r { + +// draft for a binary representation of a G-code line + +enum GCodeCmdType { + gcctSyncMotion, + gcctExtrude, + gcctResetE, + gcctSetTemp, + gcctSetTempWait, + gcctToolchange, + gcctCustom +}; + +class GCodeCmd { + public: + GCodeCmdType type; + float X, Y, Z, E, F; + unsigned short T, S; + std::string custom, comment; + float xy_dist; // cache + + GCodeCmd(GCodeCmdType type) + : type(type), X(0), Y(0), Z(0), E(0), F(0), T(-1), S(0), xy_dist(-1) {}; +}; + +} + +#endif diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp new file mode 100644 index 000000000..8e083360b --- /dev/null +++ b/xs/src/libslic3r/Geometry.cpp @@ -0,0 +1,334 @@ +#include "Geometry.hpp" +#include "Line.hpp" +#include "PolylineCollection.hpp" +#include "clipper.hpp" +#include +#include +#include +#include +#include +#include + +#ifdef SLIC3R_DEBUG +#include "SVG.hpp" +#endif + +using namespace boost::polygon; // provides also high() and low() + +namespace Slic3r { namespace Geometry { + +static bool +sort_points (Point a, Point b) +{ + return (a.x < b.x) || (a.x == b.x && a.y < b.y); +} + +/* This implementation is based on Andrew's monotone chain 2D convex hull algorithm */ +void +convex_hull(Points &points, Polygon* hull) +{ + assert(points.size() >= 3); + // sort input points + std::sort(points.begin(), points.end(), sort_points); + + int n = points.size(), k = 0; + 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]; + } + + // 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]; + } + + hull->points.resize(k); + + assert( hull->points.front().coincides_with(hull->points.back()) ); + hull->points.pop_back(); +} + +/* accepts an arrayref of points and returns a list of indices + according to a nearest-neighbor walk */ +void +chained_path(Points &points, std::vector &retval, Point start_near) +{ + PointPtrs my_points; + std::map indices; + my_points.reserve(points.size()); + for (Points::iterator it = points.begin(); it != points.end(); ++it) { + my_points.push_back(&*it); + indices[&*it] = it - points.begin(); + } + + retval.reserve(points.size()); + while (!my_points.empty()) { + Points::size_type idx = start_near.nearest_point_index(my_points); + start_near = *my_points[idx]; + retval.push_back(indices[ my_points[idx] ]); + my_points.erase(my_points.begin() + idx); + } +} + +void +chained_path(Points &points, std::vector &retval) +{ + if (points.empty()) return; // can't call front() on empty vector + chained_path(points, retval, points.front()); +} + +/* retval and items must be different containers */ +template +void +chained_path_items(Points &points, T &items, T &retval) +{ + std::vector indices; + chained_path(points, indices); + for (std::vector::const_iterator it = indices.begin(); it != indices.end(); ++it) + retval.push_back(items[*it]); +} +template void chained_path_items(Points &points, ClipperLib::PolyNodes &items, ClipperLib::PolyNodes &retval); + +bool +directions_parallel(double angle1, double angle2, double max_diff) +{ + double diff = fabs(angle1 - angle2); + max_diff += EPSILON; + return diff < max_diff || fabs(diff - PI) < max_diff; +} + +Line +MedialAxis::edge_to_line(const VD::edge_type &edge) const +{ + Line line; + line.a.x = edge.vertex0()->x(); + line.a.y = edge.vertex0()->y(); + line.b.x = edge.vertex1()->x(); + line.b.y = edge.vertex1()->y(); + return line; +} + +void +MedialAxis::build(Polylines* polylines) +{ + /* + // build bounding box (we use it for clipping infinite segments) + // --> we have no infinite segments + this->bb = BoundingBox(this->lines); + */ + + construct_voronoi(this->lines.begin(), this->lines.end(), &this->vd); + + /* + // DEBUG: dump all Voronoi edges + { + for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { + if (edge->is_infinite()) continue; + + Polyline polyline; + polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); + polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); + polylines->push_back(polyline); + } + return; + } + */ + + // collect valid edges (i.e. prune those not belonging to MAT) + // note: this keeps twins, so it contains twice the number of the valid edges + this->edges.clear(); + for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { + // if we only process segments representing closed loops, none if the + // infinite edges (if any) would be part of our MAT anyway + if (edge->is_secondary() || edge->is_infinite()) continue; + this->edges.insert(&*edge); + } + + // count valid segments for each vertex + std::map< const VD::vertex_type*,std::set > vertex_edges; + std::set entry_nodes; + for (VD::const_vertex_iterator vertex = this->vd.vertices().begin(); vertex != this->vd.vertices().end(); ++vertex) { + // get a reference to the list of valid edges originating from this vertex + std::set& edges = vertex_edges[&*vertex]; + + // get one random edge originating from this vertex + const VD::edge_type* edge = vertex->incident_edge(); + do { + if (this->edges.count(edge) > 0) // only count valid edges + edges.insert(edge); + edge = edge->rot_next(); // next edge originating from this vertex + } while (edge != vertex->incident_edge()); + + // if there's only one edge starting at this vertex then it's a leaf + size_t edge_count = edges.size(); + if (edge_count == 1) { + entry_nodes.insert(&*vertex); + } + } + + // prune recursively + while (!entry_nodes.empty()) { + // get a random entry node + const VD::vertex_type* v = *entry_nodes.begin(); + + // get edge starting from v + assert(!vertex_edges[v].empty()); + const VD::edge_type* edge = *vertex_edges[v].begin(); + + if (!this->is_valid_edge(*edge)) { + // if edge is not valid, erase it from edge list + (void)this->edges.erase(edge); + (void)this->edges.erase(edge->twin()); + + // decrement edge counters for the affected nodes + const VD::vertex_type* v1 = edge->vertex1(); + (void)vertex_edges[v].erase(edge); + (void)vertex_edges[v1].erase(edge->twin()); + + // also, check whether the end vertex is a new leaf + if (vertex_edges[v1].size() == 1) { + entry_nodes.insert(v1); + } else if (vertex_edges[v1].empty()) { + entry_nodes.erase(v1); + } + } + + // remove node from the set to prevent it from being visited again + entry_nodes.erase(v); + } + + // iterate through the valid edges to build polylines + while (!this->edges.empty()) { + const VD::edge_type& edge = **this->edges.begin(); + + // start a polyline + Polyline polyline; + polyline.points.push_back(Point( edge.vertex0()->x(), edge.vertex0()->y() )); + polyline.points.push_back(Point( edge.vertex1()->x(), edge.vertex1()->y() )); + + // remove this edge and its twin from the available edges + (void)this->edges.erase(&edge); + (void)this->edges.erase(edge.twin()); + + // get next points + this->process_edge_neighbors(edge, &polyline.points); + + // get previous points + Points pp; + this->process_edge_neighbors(*edge.twin(), &pp); + polyline.points.insert(polyline.points.begin(), pp.rbegin(), pp.rend()); + + // append polyline to result if it's not too small + if (polyline.length() > this->max_width) + polylines->push_back(polyline); + } +} + +void +MedialAxis::process_edge_neighbors(const VD::edge_type& edge, Points* points) +{ + // Since rot_next() works on the edge starting point but we want + // to find neighbors on the ending point, we just swap edge with + // its twin. + const VD::edge_type& twin = *edge.twin(); + + // count neighbors for this edge + std::vector neighbors; + for (const VD::edge_type* neighbor = twin.rot_next(); neighbor != &twin; neighbor = neighbor->rot_next()) { + if (this->edges.count(neighbor) > 0) neighbors.push_back(neighbor); + } + + // if we have a single neighbor then we can continue recursively + if (neighbors.size() == 1) { + const VD::edge_type& neighbor = *neighbors.front(); + points->push_back(Point( neighbor.vertex1()->x(), neighbor.vertex1()->y() )); + (void)this->edges.erase(&neighbor); + (void)this->edges.erase(neighbor.twin()); + this->process_edge_neighbors(neighbor, points); + } +} + +bool +MedialAxis::is_valid_edge(const VD::edge_type& edge) const +{ + /* If the cells sharing this edge have a common vertex, we're not interested + in this edge. Why? Because it means that the edge lies on the bisector of + two contiguous input lines and it was included in the Voronoi graph because + it's the locus of centers of circles tangent to both vertices. Due to the + "thin" nature of our input, these edges will be very short and not part of + our wanted output. */ + + const VD::cell_type &cell1 = *edge.cell(); + const VD::cell_type &cell2 = *edge.twin()->cell(); + if (cell1.contains_segment() && cell2.contains_segment()) { + Line segment1 = this->retrieve_segment(cell1); + Line segment2 = this->retrieve_segment(cell2); + if (segment1.a == segment2.b || segment1.b == segment2.a) return false; + + // calculate relative angle between the two boundary segments + double angle = fabs(segment2.orientation() - segment1.orientation()); + + // fabs(angle) ranges from 0 (collinear, same direction) to PI (collinear, opposite direction) + // we're interested only in segments close to the second case (facing segments) + // so we allow some tolerance (say, 30°) + if (angle < PI*2/3 ) { + return false; + } + + // each vertex is equidistant to both cell segments + // but such distance might differ between the two vertices; + // in this case it means the shape is getting narrow (like a corner) + // and we might need to skip the edge since it's not really part of + // our skeleton + Point v0( edge.vertex0()->x(), edge.vertex0()->y() ); + Point v1( edge.vertex1()->x(), edge.vertex1()->y() ); + double dist0 = v0.distance_to(segment1); + double dist1 = v1.distance_to(segment1); + + /* + double diff = fabs(dist1 - dist0); + double dist_between_segments1 = segment1.a.distance_to(segment2); + double dist_between_segments2 = segment1.b.distance_to(segment2); + printf("w = %f/%f, dist0 = %f, dist1 = %f, diff = %f, seg1len = %f, seg2len = %f, edgelen = %f, s2s = %f / %f\n", + unscale(this->max_width), unscale(this->min_width), + unscale(dist0), unscale(dist1), unscale(diff), + unscale(segment1.length()), unscale(segment2.length()), + unscale(this->edge_to_line(edge).length()), + unscale(dist_between_segments1), unscale(dist_between_segments2) + ); + */ + + // if this segment is the centerline for a very thin area, we might want to skip it + // in case the area is too thin + if (dist0 < this->min_width/2 || dist1 < this->min_width/2) { + //printf(" => too thin, skipping\n"); + return false; + } + + /* + // if distance between this edge and the thin area boundary is greater + // than half the max width, then it's not a true medial axis segment + if (dist1 > this->width*2) { + printf(" => too fat, skipping\n"); + //return false; + } + */ + + return true; + } + + return false; +} + +Line +MedialAxis::retrieve_segment(const VD::cell_type& cell) const +{ + VD::cell_type::source_index_type index = cell.source_index() - this->points.size(); + return this->lines[index]; +} + +} } diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp new file mode 100644 index 000000000..2dc183f82 --- /dev/null +++ b/xs/src/libslic3r/Geometry.hpp @@ -0,0 +1,41 @@ +#ifndef slic3r_Geometry_hpp_ +#define slic3r_Geometry_hpp_ + +#include "BoundingBox.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" + +#include "boost/polygon/voronoi.hpp" +using boost::polygon::voronoi_builder; +using boost::polygon::voronoi_diagram; + +namespace Slic3r { namespace Geometry { + +void convex_hull(Points &points, Polygon* hull); +void chained_path(Points &points, std::vector &retval, Point start_near); +void chained_path(Points &points, std::vector &retval); +template void chained_path_items(Points &points, T &items, T &retval); +bool directions_parallel(double angle1, double angle2, double max_diff = 0); + +class MedialAxis { + public: + Points points; + Lines lines; + double max_width; + double min_width; + MedialAxis(double _max_width, double _min_width) : max_width(_max_width), min_width(_min_width) {}; + void build(Polylines* polylines); + + private: + typedef voronoi_diagram VD; + VD vd; + std::set edges; + Line edge_to_line(const VD::edge_type &edge) const; + void process_edge_neighbors(const voronoi_diagram::edge_type& edge, Points* points); + bool is_valid_edge(const voronoi_diagram::edge_type& edge) const; + Line retrieve_segment(const voronoi_diagram::cell_type& cell) const; +}; + +} } + +#endif diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp new file mode 100644 index 000000000..689077d46 --- /dev/null +++ b/xs/src/libslic3r/Layer.cpp @@ -0,0 +1,144 @@ +#include "Layer.hpp" +#include "ClipperUtils.hpp" +#include "Geometry.hpp" +#include "Print.hpp" + + +namespace Slic3r { + +Layer::Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + coordf_t slice_z) +: _id(id), + _object(object), + upper_layer(NULL), + lower_layer(NULL), + regions(), + slicing_errors(false), + slice_z(slice_z), + print_z(print_z), + height(height), + slices() +{ +} + +Layer::~Layer() +{ + // remove references to self + if (NULL != this->upper_layer) { + this->upper_layer->lower_layer = NULL; + } + + if (NULL != this->lower_layer) { + this->lower_layer->upper_layer = NULL; + } + + this->clear_regions(); +} + +int +Layer::id() +{ + return this->_id; +} + +PrintObject* +Layer::object() +{ + return this->_object; +} + + +size_t +Layer::region_count() +{ + return this->regions.size(); +} + +void +Layer::clear_regions() +{ + for (int i = this->regions.size()-1; i >= 0; --i) + this->delete_region(i); +} + +LayerRegion* +Layer::get_region(int idx) +{ + return this->regions.at(idx); +} + +LayerRegion* +Layer::add_region(PrintRegion* print_region) +{ + LayerRegion* region = new LayerRegion(this, print_region); + this->regions.push_back(region); + return region; +} + +void +Layer::delete_region(int idx) +{ + LayerRegionPtrs::iterator i = this->regions.begin() + idx; + LayerRegion* item = *i; + this->regions.erase(i); + delete item; +} + +// merge all regions' slices to get islands +void +Layer::make_slices() +{ + ExPolygons slices; + if (this->regions.size() == 1) { + // optimization: if we only have one region, take its slices + slices = this->regions.front()->slices; + } else { + Polygons slices_p; + FOREACH_LAYERREGION(this, layerm) { + Polygons region_slices_p = (*layerm)->slices; + slices_p.insert(slices_p.end(), region_slices_p.begin(), region_slices_p.end()); + } + union_(slices_p, slices); + } + + this->slices.expolygons.clear(); + this->slices.expolygons.reserve(slices.size()); + + // prepare ordering points + Points ordering_points; + ordering_points.reserve(slices.size()); + for (ExPolygons::const_iterator ex = slices.begin(); ex != slices.end(); ++ex) + ordering_points.push_back(ex->contour.first_point()); + + // sort slices + std::vector order; + Slic3r::Geometry::chained_path(ordering_points, order); + + // populate slices vector + for (std::vector::const_iterator it = order.begin(); it != order.end(); ++it) { + this->slices.expolygons.push_back(slices[*it]); + } +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(Layer, "Layer"); +#endif + + +SupportLayer::SupportLayer(int id, PrintObject *object, coordf_t height, + coordf_t print_z, coordf_t slice_z) +: Layer(id, object, height, print_z, slice_z) +{ +} + +SupportLayer::~SupportLayer() +{ +} + +#ifdef SLIC3RXS +REGISTER_CLASS(SupportLayer, "Layer::Support"); +#endif + + +} diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp new file mode 100644 index 000000000..cea1535b0 --- /dev/null +++ b/xs/src/libslic3r/Layer.hpp @@ -0,0 +1,124 @@ +#ifndef slic3r_Layer_hpp_ +#define slic3r_Layer_hpp_ + +#include +#include "Flow.hpp" +#include "SurfaceCollection.hpp" +#include "ExtrusionEntityCollection.hpp" +#include "ExPolygonCollection.hpp" +#include "PolylineCollection.hpp" + + +namespace Slic3r { + +typedef std::pair t_layer_height_range; +typedef std::map t_layer_height_ranges; + +class Layer; +class PrintRegion; +class PrintObject; + + +// TODO: make stuff private +class LayerRegion +{ + friend class Layer; + + public: + Layer* layer(); + PrintRegion* region(); + + // collection of surfaces generated by slicing the original geometry + // divided by type top/bottom/internal + SurfaceCollection slices; + + // collection of extrusion paths/loops filling gaps + ExtrusionEntityCollection thin_fills; + + // collection of surfaces for infill generation + SurfaceCollection fill_surfaces; + + // collection of expolygons representing the bridged areas (thus not + // needing support material) + ExPolygonCollection bridged; + + // collection of polylines representing the unsupported bridge edges + PolylineCollection unsupported_bridge_edges; + + // ordered collection of extrusion paths/loops to build all perimeters + ExtrusionEntityCollection perimeters; + + // ordered collection of extrusion paths to fill surfaces + ExtrusionEntityCollection fills; + + Flow flow(FlowRole role, bool bridge = false, double width = -1) const; + + private: + Layer *_layer; + PrintRegion *_region; + + LayerRegion(Layer *layer, PrintRegion *region); + ~LayerRegion(); +}; + + +typedef std::vector LayerRegionPtrs; + +class Layer { + friend class PrintObject; + + public: + int id(); + PrintObject* object(); + + Layer *upper_layer; + Layer *lower_layer; + LayerRegionPtrs regions; + bool slicing_errors; + coordf_t slice_z; // Z used for slicing in unscaled coordinates + coordf_t print_z; // Z used for printing in unscaled coordinates + coordf_t height; // layer height in unscaled coordinates + + // collection of expolygons generated by slicing the original geometry; + // also known as 'islands' (all regions and surface types are merged here) + ExPolygonCollection slices; + + + size_t region_count(); + LayerRegion* get_region(int idx); + LayerRegion* add_region(PrintRegion* print_region); + + void make_slices(); + + protected: + int _id; // sequential number of layer, 0-based + PrintObject *_object; + + + Layer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + coordf_t slice_z); + virtual ~Layer(); + + void clear_regions(); + void delete_region(int idx); +}; + + +class SupportLayer : public Layer { + friend class PrintObject; + + public: + ExPolygonCollection support_islands; + ExtrusionEntityCollection support_fills; + ExtrusionEntityCollection support_interface_fills; + + protected: + SupportLayer(int id, PrintObject *object, coordf_t height, coordf_t print_z, + coordf_t slice_z); + virtual ~SupportLayer(); +}; + + +} + +#endif diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp new file mode 100644 index 000000000..cb92c4ee3 --- /dev/null +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -0,0 +1,45 @@ +#include "Layer.hpp" +#include "Print.hpp" + +namespace Slic3r { + +LayerRegion::LayerRegion(Layer *layer, PrintRegion *region) +: _layer(layer), + _region(region) +{ +} + +LayerRegion::~LayerRegion() +{ +} + +Layer* +LayerRegion::layer() +{ + return this->_layer; +} + +PrintRegion* +LayerRegion::region() +{ + return this->_region; +} + +Flow +LayerRegion::flow(FlowRole role, bool bridge, double width) const +{ + return this->_region->flow( + role, + this->_layer->height, + bridge, + this->_layer->id() == 0, + width, + *this->_layer->object() + ); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(LayerRegion, "Layer::Region"); +#endif + +} diff --git a/xs/src/libslic3r/Line.cpp b/xs/src/libslic3r/Line.cpp new file mode 100644 index 000000000..86efceddf --- /dev/null +++ b/xs/src/libslic3r/Line.cpp @@ -0,0 +1,181 @@ +#include "Geometry.hpp" +#include "Line.hpp" +#include "Polyline.hpp" +#include +#include +#include + +namespace Slic3r { + +std::string +Line::wkt() const +{ + std::ostringstream ss; + ss << "LINESTRING(" << this->a.x << " " << this->a.y << "," + << this->b.x << " " << this->b.y << ")"; + return ss.str(); +} + +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 ¢er) +{ + this->a.rotate(angle, center); + this->b.rotate(angle, center); +} + +void +Line::reverse() +{ + std::swap(this->a, this->b); +} + +double +Line::length() const +{ + return this->a.distance_to(this->b); +} + +Point* +Line::midpoint() const +{ + return new Point ((this->a.x + this->b.x) / 2.0, (this->a.y + this->b.y) / 2.0); +} + +void +Line::point_at(double distance, 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::coincides_with(const Line &line) const +{ + return this->a.coincides_with(line.a) && this->b.coincides_with(line.b); +} + +double +Line::distance_to(const Point &point) const +{ + return point.distance_to(*this); +} + +double +Line::atan2_() const +{ + return atan2(this->b.y - this->a.y, this->b.x - this->a.x); +} + +double +Line::orientation() const +{ + double angle = this->atan2_(); + if (angle < 0) angle = 2*PI + angle; + return angle; +} + +double +Line::direction() const +{ + double atan2 = this->atan2_(); + return (atan2 == PI) ? 0 + : (atan2 < 0) ? (atan2 + PI) + : 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 +{ + return Vector(this->b.x - this->a.x, this->b.y - this->a.y); +} + +#ifdef SLIC3RXS + +REGISTER_CLASS(Line, "Line"); + +void +Line::from_SV(SV* line_sv) +{ + AV* line_av = (AV*)SvRV(line_sv); + this->a.from_SV_check(*av_fetch(line_av, 0, 0)); + this->b.from_SV_check(*av_fetch(line_av, 1, 0)); +} + +void +Line::from_SV_check(SV* line_sv) +{ + if (sv_isobject(line_sv) && (SvTYPE(SvRV(line_sv)) == SVt_PVMG)) { + if (!sv_isa(line_sv, perl_class_name(this)) && !sv_isa(line_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object", perl_class_name(this)); + *this = *(Line*)SvIV((SV*)SvRV( line_sv )); + } else { + this->from_SV(line_sv); + } +} + +SV* +Line::to_AV() { + AV* av = newAV(); + av_extend(av, 1); + + av_store(av, 0, perl_to_SV_ref(this->a)); + av_store(av, 1, perl_to_SV_ref(this->b)); + + return newRV_noinc((SV*)av); +} + +SV* +Line::to_SV_pureperl() const { + AV* av = newAV(); + av_extend(av, 1); + av_store(av, 0, this->a.to_SV_pureperl()); + av_store(av, 1, this->b.to_SV_pureperl()); + return newRV_noinc((SV*)av); +} +#endif + +} diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp new file mode 100644 index 000000000..3f86ed4a7 --- /dev/null +++ b/xs/src/libslic3r/Line.hpp @@ -0,0 +1,68 @@ +#ifndef slic3r_Line_hpp_ +#define slic3r_Line_hpp_ + +#include +#include "Point.hpp" + +namespace Slic3r { + +class Line; +class Polyline; + +class Line +{ + public: + Point a; + Point b; + Line() {}; + explicit Line(Point _a, Point _b): a(_a), b(_b) {}; + std::string wkt() const; + operator Polyline() const; + void scale(double factor); + void translate(double x, double y); + void rotate(double angle, const Point ¢er); + void reverse(); + double length() const; + Point* midpoint() const; + void point_at(double distance, Point* point) const; + Point point_at(double distance) const; + bool coincides_with(const Line &line) const; + double distance_to(const Point &point) const; + bool parallel_to(double angle) const; + bool parallel_to(const Line &line) const; + double atan2_() const; + double orientation() const; + double direction() const; + Vector vector() const; + + #ifdef SLIC3RXS + void from_SV(SV* line_sv); + void from_SV_check(SV* line_sv); + SV* to_AV(); + SV* to_SV_pureperl() const; + #endif +}; + +typedef std::vector Lines; + +} + +// start Boost +#include +namespace boost { namespace polygon { + template <> + struct geometry_concept { typedef segment_concept type; }; + + template <> + struct segment_traits { + typedef coord_t coordinate_type; + typedef Point point_type; + + static inline point_type get(const Line& line, direction_1d dir) { + return dir.to_int() ? line.b : line.a; + } + }; +} } +// end Boost + +#endif diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp new file mode 100644 index 000000000..ee8dc1057 --- /dev/null +++ b/xs/src/libslic3r/Model.cpp @@ -0,0 +1,369 @@ +#include "Model.hpp" + +namespace Slic3r { + +Model::Model() {} + +Model::Model(const Model &other) +{ + // copy materials + for (ModelMaterialMap::const_iterator i = other.materials.begin(); i != other.materials.end(); ++i) + this->add_material(i->first, *i->second); + + // copy objects + this->objects.reserve(other.objects.size()); + for (ModelObjectPtrs::const_iterator i = other.objects.begin(); i != other.objects.end(); ++i) + this->add_object(**i); +} + +Model& Model::operator= (Model other) +{ + this->swap(other); + return *this; +} + +void +Model::swap(Model &other) +{ + std::swap(this->materials, other.materials); + std::swap(this->objects, other.objects); +} + +Model::~Model() +{ + this->clear_objects(); + this->clear_materials(); +} + +ModelObject* +Model::add_object() +{ + ModelObject* new_object = new ModelObject(this); + this->objects.push_back(new_object); + return new_object; +} + +ModelObject* +Model::add_object(const ModelObject &other) +{ + ModelObject* new_object = new ModelObject(this, other); + this->objects.push_back(new_object); + return new_object; +} + +void +Model::delete_object(size_t idx) +{ + ModelObjectPtrs::iterator i = this->objects.begin() + idx; + delete *i; + this->objects.erase(i); +} + +void +Model::clear_objects() +{ + // int instead of size_t because it can be -1 when vector is empty + for (int i = this->objects.size()-1; i >= 0; --i) + this->delete_object(i); +} + +void +Model::delete_material(t_model_material_id material_id) +{ + ModelMaterialMap::iterator i = this->materials.find(material_id); + if (i != this->materials.end()) { + delete i->second; + this->materials.erase(i); + } +} + +void +Model::clear_materials() +{ + while (!this->materials.empty()) + this->delete_material( this->materials.begin()->first ); +} + +ModelMaterial* +Model::add_material(t_model_material_id material_id) +{ + ModelMaterial* material = this->get_material(material_id); + if (material == NULL) { + material = this->materials[material_id] = new ModelMaterial(this); + } + return material; +} + +ModelMaterial* +Model::add_material(t_model_material_id material_id, const ModelMaterial &other) +{ + // delete existing material if any + ModelMaterial* material = this->get_material(material_id); + if (material != NULL) { + delete material; + } + + // set new material + material = new ModelMaterial(this, other); + this->materials[material_id] = material; + return material; +} + +ModelMaterial* +Model::get_material(t_model_material_id material_id) +{ + ModelMaterialMap::iterator i = this->materials.find(material_id); + if (i == this->materials.end()) { + return NULL; + } else { + return i->second; + } +} + +/* +void +Model::duplicate_objects_grid(unsigned int x, unsigned int y, coordf_t distance) +{ + if (this->objects.size() > 1) throw "Grid duplication is not supported with multiple objects"; + if (this->objects.empty()) throw "No objects!"; + + ModelObject* object = this->objects.front(); + object->clear_instances(); + + BoundingBoxf3 bb; + object->bounding_box(&bb); + Sizef3 size = bb.size(); + + for (unsigned int x_copy = 1; x_copy <= x; ++x_copy) { + for (unsigned int y_copy = 1; y_copy <= y; ++y_copy) { + ModelInstance* instance = object->add_instance(); + instance->offset.x = (size.x + distance) * (x_copy-1); + instance->offset.y = (size.y + distance) * (y_copy-1); + } + } +} +*/ + +bool +Model::has_objects_with_no_instances() const +{ + for (ModelObjectPtrs::const_iterator i = this->objects.begin(); + i != this->objects.end(); ++i) + { + if ((*i)->instances.empty()) { + return true; + } + } + + return false; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(Model, "Model"); +#endif + + +ModelMaterial::ModelMaterial(Model *model) : model(model) {} +ModelMaterial::ModelMaterial(Model *model, const ModelMaterial &other) + : model(model), config(other.config), attributes(other.attributes) +{} + +void +ModelMaterial::apply(const t_model_material_attributes &attributes) +{ + this->attributes.insert(attributes.begin(), attributes.end()); +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(ModelMaterial, "Model::Material"); +#endif + + +ModelObject::ModelObject(Model *model) + : model(model) +{} + +ModelObject::ModelObject(Model *model, const ModelObject &other) +: model(model), + name(other.name), + input_file(other.input_file), + instances(), + volumes(), + config(other.config), + layer_height_ranges(other.layer_height_ranges), + origin_translation(other.origin_translation), + _bounding_box(other._bounding_box), + _bounding_box_valid(other._bounding_box_valid) +{ + + this->volumes.reserve(other.volumes.size()); + for (ModelVolumePtrs::const_iterator i = other.volumes.begin(); i != other.volumes.end(); ++i) + this->add_volume(**i); + + this->instances.reserve(other.instances.size()); + for (ModelInstancePtrs::const_iterator i = other.instances.begin(); i != other.instances.end(); ++i) + this->add_instance(**i); +} + +ModelObject& ModelObject::operator= (ModelObject other) +{ + this->swap(other); + return *this; +} + +void +ModelObject::swap(ModelObject &other) +{ + std::swap(this->input_file, other.input_file); + std::swap(this->instances, other.instances); + std::swap(this->volumes, other.volumes); + std::swap(this->config, other.config); + std::swap(this->layer_height_ranges, other.layer_height_ranges); + std::swap(this->origin_translation, other.origin_translation); + std::swap(this->_bounding_box, other._bounding_box); + std::swap(this->_bounding_box_valid, other._bounding_box_valid); +} + +ModelObject::~ModelObject() +{ + this->clear_volumes(); + this->clear_instances(); +} + +ModelVolume* +ModelObject::add_volume(const TriangleMesh &mesh) +{ + ModelVolume* v = new ModelVolume(this, mesh); + this->volumes.push_back(v); + this->invalidate_bounding_box(); + return v; +} + +ModelVolume* +ModelObject::add_volume(const ModelVolume &other) +{ + ModelVolume* v = new ModelVolume(this, other); + this->volumes.push_back(v); + this->invalidate_bounding_box(); + return v; +} + +void +ModelObject::delete_volume(size_t idx) +{ + ModelVolumePtrs::iterator i = this->volumes.begin() + idx; + delete *i; + this->volumes.erase(i); + this->invalidate_bounding_box(); +} + +void +ModelObject::clear_volumes() +{ + // int instead of size_t because it can be -1 when vector is empty + for (int i = this->volumes.size()-1; i >= 0; --i) + this->delete_volume(i); +} + +ModelInstance* +ModelObject::add_instance() +{ + ModelInstance* i = new ModelInstance(this); + this->instances.push_back(i); + this->invalidate_bounding_box(); + return i; +} + +ModelInstance* +ModelObject::add_instance(const ModelInstance &other) +{ + ModelInstance* i = new ModelInstance(this, other); + this->instances.push_back(i); + this->invalidate_bounding_box(); + return i; +} + +void +ModelObject::delete_instance(size_t idx) +{ + ModelInstancePtrs::iterator i = this->instances.begin() + idx; + delete *i; + this->instances.erase(i); + this->invalidate_bounding_box(); +} + +void +ModelObject::delete_last_instance() +{ + this->delete_instance(this->instances.size() - 1); +} + +void +ModelObject::clear_instances() +{ + for (size_t i = 0; i < this->instances.size(); ++i) + this->delete_instance(i); +} + +void +ModelObject::invalidate_bounding_box() +{ + this->_bounding_box_valid = false; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(ModelObject, "Model::Object"); +#endif + + +ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh) +: object(object), mesh(mesh), modifier(false) +{} + +ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other) +: object(object), name(other.name), mesh(other.mesh), config(other.config), + modifier(other.modifier) +{ + this->material_id(other.material_id()); +} + +t_model_material_id +ModelVolume::material_id() const +{ + return this->_material_id; +} + +void +ModelVolume::material_id(t_model_material_id material_id) +{ + this->_material_id = material_id; + + // ensure this->_material_id references an existing material + (void)this->object->get_model()->add_material(material_id); +} + +ModelMaterial* +ModelVolume::material() const +{ + return this->object->get_model()->get_material(this->_material_id); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(ModelVolume, "Model::Volume"); +#endif + + +ModelInstance::ModelInstance(ModelObject *object) +: object(object), rotation(0), scaling_factor(1) +{} + +ModelInstance::ModelInstance(ModelObject *object, const ModelInstance &other) +: object(object), rotation(other.rotation), scaling_factor(other.scaling_factor), offset(other.offset) +{} + +#ifdef SLIC3RXS +REGISTER_CLASS(ModelInstance, "Model::Instance"); +#endif + +} diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp new file mode 100644 index 000000000..dfcd75b4c --- /dev/null +++ b/xs/src/libslic3r/Model.hpp @@ -0,0 +1,181 @@ +#ifndef slic3r_Model_hpp_ +#define slic3r_Model_hpp_ + +#include +#include "PrintConfig.hpp" +#include "Layer.hpp" +#include "Point.hpp" +#include "TriangleMesh.hpp" +#include +#include +#include +#include + +namespace Slic3r { + +class ModelInstance; +class ModelMaterial; +class ModelObject; +class ModelVolume; + +typedef std::string t_model_material_id; +typedef std::string t_model_material_attribute; +typedef std::map t_model_material_attributes; + +typedef std::map ModelMaterialMap; +typedef std::vector ModelObjectPtrs; +typedef std::vector ModelVolumePtrs; +typedef std::vector ModelInstancePtrs; + +class Model +{ + public: + ModelMaterialMap materials; + ModelObjectPtrs objects; + + Model(); + Model(const Model &other); + Model& operator= (Model other); + void swap(Model &other); + ~Model(); + ModelObject* add_object(); + ModelObject* add_object(const ModelObject &other); + void delete_object(size_t idx); + void clear_objects(); + + ModelMaterial* add_material(t_model_material_id material_id); + ModelMaterial* add_material(t_model_material_id material_id, const ModelMaterial &other); + ModelMaterial* get_material(t_model_material_id material_id); + void delete_material(t_model_material_id material_id); + void clear_materials(); + // void duplicate_objects_grid(unsigned int x, unsigned int y, coordf_t distance); + // void duplicate_objects(size_t copies_num, coordf_t distance, const BoundingBox &bb); + // void arrange_objects(coordf_t distance, const BoundingBox &bb); + // void duplicate(size_t copies_num, coordf_t distance, const BoundingBox &bb); + bool has_objects_with_no_instances() const; + // void bounding_box(BoundingBox* bb) const; + // void align_to_origin(); + // void center_instances_around_point(const Pointf &point); + // void translate(coordf_t x, coordf_t y, coordf_t z); + // void mesh(TriangleMesh* mesh) const; + // void split_meshes(); + // std::string get_material_name(t_model_material_id material_id); + + + private: + void _arrange(const std::vector &sizes, coordf_t distance, const BoundingBox &bb) const; +}; + +class ModelMaterial +{ + friend class Model; + public: + t_model_material_attributes attributes; + DynamicPrintConfig config; + + Model* get_model() const { return this->model; }; + void apply(const t_model_material_attributes &attributes); + + private: + Model* model; + + ModelMaterial(Model *model); + ModelMaterial(Model *model, const ModelMaterial &other); +}; + +class ModelObject +{ + friend class Model; + public: + std::string name; + std::string input_file; + ModelInstancePtrs instances; + ModelVolumePtrs volumes; + DynamicPrintConfig config; + t_layer_height_ranges layer_height_ranges; + Pointf origin_translation; + + // these should be private but we need to expose them via XS until all methods are ported + BoundingBoxf3 _bounding_box; + bool _bounding_box_valid; + + Model* get_model() const { return this->model; }; + + ModelVolume* add_volume(const TriangleMesh &mesh); + ModelVolume* add_volume(const ModelVolume &volume); + void delete_volume(size_t idx); + void clear_volumes(); + + ModelInstance* add_instance(); + ModelInstance* add_instance(const ModelInstance &instance); + void delete_instance(size_t idx); + void delete_last_instance(); + void clear_instances(); + + void invalidate_bounding_box(); + + //void raw_mesh(TriangleMesh* mesh) const; + //void mesh(TriangleMesh* mesh) const; + //void instance_bounding_box(size_t instance_idx, BoundingBox* bb) const; + //void center_around_origin(); + //void translate(coordf_t x, coordf_t y, coordf_t z); + //size_t materials_count() const; + //void unique_materials(std::vector* materials) const; + //size_t facets_count() const; + //bool needed_repair() const; + + private: + Model* model; + + ModelObject(Model *model); + ModelObject(Model *model, const ModelObject &other); + ModelObject& operator= (ModelObject other); + void swap(ModelObject &other); + ~ModelObject(); + void update_bounding_box(); +}; + +class ModelVolume +{ + friend class ModelObject; + public: + std::string name; + TriangleMesh mesh; + DynamicPrintConfig config; + bool modifier; + + ModelObject* get_object() const { return this->object; }; + t_model_material_id material_id() const; + void material_id(t_model_material_id material_id); + ModelMaterial* material() const; + + private: + ModelObject* object; + t_model_material_id _material_id; + + ModelVolume(ModelObject *object, const TriangleMesh &mesh); + ModelVolume(ModelObject *object, const ModelVolume &other); +}; + +class ModelInstance +{ + friend class ModelObject; + public: + double rotation; // around mesh center point + double scaling_factor; + Pointf offset; // in unscaled coordinates + + ModelObject* get_object() const { return this->object; }; + void transform_mesh(TriangleMesh* mesh, bool dont_translate) const; + void transform_polygon(Polygon* polygon) const; + + private: + ModelObject* object; + + ModelInstance(ModelObject *object); + ModelInstance(ModelObject *object, const ModelInstance &other); +}; + +} + +#endif diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp new file mode 100644 index 000000000..82108afd9 --- /dev/null +++ b/xs/src/libslic3r/MotionPlanner.cpp @@ -0,0 +1,278 @@ +#include "BoundingBox.hpp" +#include "MotionPlanner.hpp" +#include // for numeric_limits + +#include "boost/polygon/voronoi.hpp" +using boost::polygon::voronoi_builder; +using boost::polygon::voronoi_diagram; + +namespace Slic3r { + +MotionPlanner::MotionPlanner(const ExPolygons &islands) + : islands(islands), initialized(false) +{} + +MotionPlanner::~MotionPlanner() +{ + for (std::vector::iterator graph = this->graphs.begin(); graph != this->graphs.end(); ++graph) + delete *graph; +} + +void +MotionPlanner::initialize() +{ + if (this->initialized) return; + + ExPolygons expp; + for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) { + island->simplify(SCALED_EPSILON, expp); + } + this->islands = expp; + + // loop through islands in order to create inner expolygons and collect their contours + this->inner.reserve(this->islands.size()); + Polygons outer_holes; + for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) { + this->inner.push_back(ExPolygonCollection()); + offset_ex(*island, this->inner.back(), -MP_INNER_MARGIN); + + outer_holes.push_back(island->contour); + } + + // grow island contours in order to prepare holes of the outer environment + // This is actually wrong because it might merge contours that are close, + // thus confusing the island check in shortest_path() below + //offset(outer_holes, outer_holes, +MP_OUTER_MARGIN); + + // generate outer contour as bounding box of everything + Points points; + for (Polygons::const_iterator contour = outer_holes.begin(); contour != outer_holes.end(); ++contour) + points.insert(points.end(), contour->points.begin(), contour->points.end()); + BoundingBox bb(points); + + // grow outer contour + Polygons contour; + offset(bb.polygon(), contour, +MP_OUTER_MARGIN); + assert(contour.size() == 1); + + // make expolygon for outer environment + ExPolygons outer; + diff(contour, outer_holes, outer); + assert(outer.size() == 1); + this->outer = outer.front(); + + this->graphs.resize(this->islands.size() + 1, NULL); + this->initialized = true; +} + +void +MotionPlanner::shortest_path(const Point &from, const Point &to, Polyline* polyline) +{ + if (!this->initialized) this->initialize(); + + // Are both points in the same island? + int island_idx = -1; + for (ExPolygons::const_iterator island = this->islands.begin(); island != this->islands.end(); ++island) { + if (island->contains_point(from) && island->contains_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->contains_line(Line(from, to))) { + polyline->points.push_back(from); + polyline->points.push_back(to); + return; + } + island_idx = island - this->islands.begin(); + break; + } + } + + // Now check whether points are inside the environment. + Point inner_from = from; + Point inner_to = to; + bool from_is_inside, to_is_inside; + if (island_idx == -1) { + if (!(from_is_inside = this->outer.contains_point(from))) { + // Find the closest inner point to start from. + from.nearest_point(this->outer, &inner_from); + } + if (!(to_is_inside = this->outer.contains_point(to))) { + // Find the closest inner point to start from. + to.nearest_point(this->outer, &inner_to); + } + } else { + if (!(from_is_inside = this->inner[island_idx].contains_point(from))) { + // Find the closest inner point to start from. + from.nearest_point(this->inner[island_idx], &inner_from); + } + if (!(to_is_inside = this->inner[island_idx].contains_point(to))) { + // Find the closest inner point to start from. + to.nearest_point(this->inner[island_idx], &inner_to); + } + } + + // perform actual path search + MotionPlannerGraph* graph = this->init_graph(island_idx); + graph->shortest_path(graph->find_node(inner_from), graph->find_node(inner_to), polyline); + + polyline->points.insert(polyline->points.begin(), from); + polyline->points.push_back(to); +} + +MotionPlannerGraph* +MotionPlanner::init_graph(int island_idx) +{ + if (this->graphs[island_idx + 1] == NULL) { + Polygons pp; + if (island_idx == -1) { + pp = this->outer; + } else { + pp = this->inner[island_idx]; + } + + MotionPlannerGraph* graph = this->graphs[island_idx + 1] = new MotionPlannerGraph(); + + // add polygon boundaries as edges + size_t node_idx = 0; + Lines lines; + for (Polygons::const_iterator polygon = pp.begin(); polygon != pp.end(); ++polygon) { + graph->nodes.push_back(polygon->points.back()); + node_idx++; + for (Points::const_iterator p = polygon->points.begin(); p != polygon->points.end(); ++p) { + graph->nodes.push_back(*p); + double dist = graph->nodes[node_idx-1].distance_to(*p); + graph->add_edge(node_idx-1, node_idx, dist); + graph->add_edge(node_idx, node_idx-1, dist); + node_idx++; + } + polygon->lines(&lines); + } + + // add Voronoi edges as internal edges + { + typedef voronoi_diagram VD; + typedef std::map t_vd_vertices; + VD vd; + t_vd_vertices vd_vertices; + + boost::polygon::construct_voronoi(lines.begin(), lines.end(), &vd); + for (VD::const_edge_iterator edge = vd.edges().begin(); edge != vd.edges().end(); ++edge) { + if (edge->is_infinite()) continue; + + const VD::vertex_type* v0 = edge->vertex0(); + const VD::vertex_type* v1 = edge->vertex1(); + Point p0 = Point(v0->x(), v0->y()); + Point p1 = Point(v1->x(), v1->y()); + // contains_point() should probably be faster than contains_line(), + // and should it fail on any boundary points it's not a big problem + if (island_idx == -1) { + if (!this->outer.contains_point(p0) || !this->outer.contains_point(p1)) continue; + } else { + if (!this->inner[island_idx].contains_point(p0) || !this->inner[island_idx].contains_point(p1)) continue; + } + + t_vd_vertices::const_iterator i_v0 = vd_vertices.find(v0); + size_t v0_idx; + if (i_v0 == vd_vertices.end()) { + graph->nodes.push_back(p0); + v0_idx = node_idx; + vd_vertices[v0] = node_idx; + node_idx++; + } else { + v0_idx = i_v0->second; + } + + t_vd_vertices::const_iterator i_v1 = vd_vertices.find(v1); + size_t v1_idx; + if (i_v1 == vd_vertices.end()) { + graph->nodes.push_back(p1); + v1_idx = node_idx; + vd_vertices[v1] = node_idx; + node_idx++; + } else { + v1_idx = i_v1->second; + } + + double dist = graph->nodes[v0_idx].distance_to(graph->nodes[v1_idx]); + graph->add_edge(v0_idx, v1_idx, dist); + } + } + + return graph; + } + return this->graphs[island_idx + 1]; +} + +void +MotionPlannerGraph::add_edge(size_t from, size_t to, double weight) +{ + // extend adjacency list until this start node + if (this->adjacency_list.size() < from+1) + this->adjacency_list.resize(from+1); + + this->adjacency_list[from].push_back(neighbor(to, weight)); +} + +size_t +MotionPlannerGraph::find_node(const Point &point) const +{ + /* + for (Points::const_iterator p = this->nodes.begin(); p != this->nodes.end(); ++p) { + if (p->coincides_with(point)) return p - this->nodes.begin(); + } + */ + return point.nearest_point_index(this->nodes); +} + +void +MotionPlannerGraph::shortest_path(size_t from, size_t to, Polyline* polyline) +{ + const weight_t max_weight = std::numeric_limits::infinity(); + + std::vector min_distance; + std::vector previous; + { + int n = this->adjacency_list.size(); + min_distance.clear(); + min_distance.resize(n, max_weight); + min_distance[from] = 0; + previous.clear(); + previous.resize(n, -1); + std::set > vertex_queue; + vertex_queue.insert(std::make_pair(min_distance[from], from)); + + while (!vertex_queue.empty()) + { + weight_t dist = vertex_queue.begin()->first; + node_t u = vertex_queue.begin()->second; + vertex_queue.erase(vertex_queue.begin()); + + // Visit each edge exiting u + const std::vector &neighbors = this->adjacency_list[u]; + for (std::vector::const_iterator neighbor_iter = neighbors.begin(); + neighbor_iter != neighbors.end(); + neighbor_iter++) + { + node_t v = neighbor_iter->target; + weight_t weight = neighbor_iter->weight; + weight_t distance_through_u = dist + weight; + if (distance_through_u < min_distance[v]) { + vertex_queue.erase(std::make_pair(min_distance[v], v)); + min_distance[v] = distance_through_u; + previous[v] = u; + vertex_queue.insert(std::make_pair(min_distance[v], v)); + } + + } + } + } + + for (node_t vertex = to; vertex != -1; vertex = previous[vertex]) + polyline->points.push_back(this->nodes[vertex]); + polyline->reverse(); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(MotionPlanner, "MotionPlanner"); +#endif + +} diff --git a/xs/src/libslic3r/MotionPlanner.hpp b/xs/src/libslic3r/MotionPlanner.hpp new file mode 100644 index 000000000..78fd5c72b --- /dev/null +++ b/xs/src/libslic3r/MotionPlanner.hpp @@ -0,0 +1,61 @@ +#ifndef slic3r_MotionPlanner_hpp_ +#define slic3r_MotionPlanner_hpp_ + +#include +#include "ClipperUtils.hpp" +#include "ExPolygonCollection.hpp" +#include "Polyline.hpp" +#include +#include +#include + +#define MP_INNER_MARGIN scale_(1.0) +#define MP_OUTER_MARGIN scale_(2.0) + +namespace Slic3r { + +class MotionPlannerGraph; + +class MotionPlanner +{ + public: + MotionPlanner(const ExPolygons &islands); + ~MotionPlanner(); + void shortest_path(const Point &from, const Point &to, Polyline* polyline); + + private: + ExPolygons islands; + bool initialized; + ExPolygon outer; + ExPolygonCollections inner; + std::vector graphs; + + void initialize(); + MotionPlannerGraph* init_graph(int island_idx); +}; + +class MotionPlannerGraph +{ + private: + typedef size_t node_t; + typedef double weight_t; + struct neighbor { + node_t target; + weight_t weight; + neighbor(node_t arg_target, weight_t arg_weight) + : target(arg_target), weight(arg_weight) { } + }; + typedef std::vector< std::vector > adjacency_list_t; + adjacency_list_t adjacency_list; + + public: + Points nodes; + //std::map, double> edges; + void add_edge(size_t from, size_t to, double weight); + size_t find_node(const Point &point) const; + void shortest_path(size_t from, size_t to, Polyline* polyline); +}; + +} + +#endif diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp new file mode 100644 index 000000000..5da3cb499 --- /dev/null +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -0,0 +1,161 @@ +#include "MultiPoint.hpp" +#include "BoundingBox.hpp" + +namespace Slic3r { + +MultiPoint::operator Points() const +{ + return this->points; +} + +void +MultiPoint::scale(double factor) +{ + for (Points::iterator it = points.begin(); it != points.end(); ++it) { + (*it).scale(factor); + } +} + +void +MultiPoint::translate(double x, double y) +{ + for (Points::iterator it = points.begin(); it != points.end(); ++it) { + (*it).translate(x, y); + } +} + +void +MultiPoint::rotate(double angle, const Point ¢er) +{ + for (Points::iterator it = points.begin(); it != points.end(); ++it) { + (*it).rotate(angle, center); + } +} + +void +MultiPoint::reverse() +{ + std::reverse(this->points.begin(), this->points.end()); +} + +Point +MultiPoint::first_point() const +{ + return this->points.front(); +} + +double +MultiPoint::length() const +{ + Lines lines = this->lines(); + double len = 0; + for (Lines::iterator it = lines.begin(); it != lines.end(); ++it) { + len += it->length(); + } + return len; +} + +bool +MultiPoint::is_valid() const +{ + return this->points.size() >= 2; +} + +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(); + } + return -1; // not found +} + +void +MultiPoint::bounding_box(BoundingBox* bb) const +{ + *bb = BoundingBox(this->points); +} + +Points +MultiPoint::_douglas_peucker(const Points &points, const double tolerance) +{ + Points results; + double dmax = 0; + size_t index = 0; + Line full(points.front(), points.back()); + for (Points::const_iterator it = points.begin() + 1; it != points.end(); ++it) { + double d = it->distance_to(full); + if (d > dmax) { + index = it - points.begin(); + dmax = d; + } + } + if (dmax >= tolerance) { + Points dp0; + dp0.reserve(index + 1); + dp0.insert(dp0.end(), points.begin(), points.begin() + index + 1); + Points dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); + results.reserve(results.size() + dp1.size() - 1); + results.insert(results.end(), dp1.begin(), dp1.end() - 1); + + dp0.clear(); + dp0.reserve(points.size() - index + 1); + dp0.insert(dp0.end(), points.begin() + index, points.end()); + dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); + results.reserve(results.size() + dp1.size()); + results.insert(results.end(), dp1.begin(), dp1.end()); + } else { + results.push_back(points.front()); + results.push_back(points.back()); + } + return results; +} + +#ifdef SLIC3RXS +void +MultiPoint::from_SV(SV* poly_sv) +{ + AV* poly_av = (AV*)SvRV(poly_sv); + const unsigned int num_points = av_len(poly_av)+1; + this->points.resize(num_points); + + for (unsigned int i = 0; i < num_points; i++) { + SV** point_sv = av_fetch(poly_av, i, 0); + this->points[i].from_SV_check(*point_sv); + } +} + +void +MultiPoint::from_SV_check(SV* poly_sv) +{ + if (sv_isobject(poly_sv) && (SvTYPE(SvRV(poly_sv)) == SVt_PVMG)) { + *this = *(MultiPoint*)SvIV((SV*)SvRV( poly_sv )); + } else { + this->from_SV(poly_sv); + } +} + +SV* +MultiPoint::to_AV() { + const unsigned int num_points = this->points.size(); + AV* av = newAV(); + if (num_points > 0) av_extend(av, num_points-1); + for (unsigned int i = 0; i < num_points; i++) { + av_store(av, i, perl_to_SV_ref(this->points[i])); + } + return newRV_noinc((SV*)av); +} + +SV* +MultiPoint::to_SV_pureperl() const { + const unsigned int num_points = this->points.size(); + AV* av = newAV(); + if (num_points > 0) av_extend(av, num_points-1); + for (unsigned int i = 0; i < num_points; i++) { + av_store(av, i, this->points[i].to_SV_pureperl()); + } + return newRV_noinc((SV*)av); +} +#endif + +} diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp new file mode 100644 index 000000000..075b0dbdb --- /dev/null +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -0,0 +1,44 @@ +#ifndef slic3r_MultiPoint_hpp_ +#define slic3r_MultiPoint_hpp_ + +#include +#include +#include +#include "Line.hpp" +#include "Point.hpp" + +namespace Slic3r { + +class BoundingBox; + +class MultiPoint +{ + public: + Points points; + + operator Points() const; + void scale(double factor); + void translate(double x, double y); + void rotate(double angle, const Point ¢er); + void reverse(); + Point first_point() const; + virtual Point last_point() const = 0; + virtual Lines lines() const = 0; + double length() const; + bool is_valid() const; + int find_point(const Point &point) const; + void bounding_box(BoundingBox* bb) const; + + static Points _douglas_peucker(const Points &points, const double tolerance); + + #ifdef SLIC3RXS + void from_SV(SV* poly_sv); + void from_SV_check(SV* poly_sv); + SV* to_AV(); + SV* to_SV_pureperl() const; + #endif +}; + +} + +#endif diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp new file mode 100644 index 000000000..c384afa32 --- /dev/null +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -0,0 +1,120 @@ +#include "PlaceholderParser.hpp" + + +namespace Slic3r { + + +PlaceholderParser::PlaceholderParser() +{ + // TODO: port these methods to C++, then call them here + // this->apply_env_variables(); + // this->update_timestamp(); +} + +PlaceholderParser::~PlaceholderParser() +{ +} + +void PlaceholderParser::apply_config(DynamicPrintConfig &config) +{ + // options that are set and aren't text-boxes + t_config_option_keys opt_keys; + for (t_optiondef_map::iterator i = config.def->begin(); + i != config.def->end(); ++i) + { + const t_config_option_key &key = i->first; + const ConfigOptionDef &def = i->second; + + if (config.has(key) && !def.multiline) { + opt_keys.push_back(key); + } + } + + for (t_config_option_keys::iterator i = opt_keys.begin(); + i != opt_keys.end(); ++i) + { + const t_config_option_key &key = *i; + + // set placeholders for options with multiple values + const ConfigOptionDef &def = (*config.def)[key]; + switch (def.type) { + case coFloats: + this->set_multiple_from_vector(key, + *(ConfigOptionFloats*)config.option(key)); + break; + + case coInts: + this->set_multiple_from_vector(key, + *(ConfigOptionInts*)config.option(key)); + break; + + case coStrings: + this->set_multiple_from_vector(key, + *(ConfigOptionStrings*)config.option(key)); + break; + + case coPoints: + this->set_multiple_from_vector(key, + *(ConfigOptionPoints*)config.option(key)); + break; + + case coBools: + this->set_multiple_from_vector(key, + *(ConfigOptionBools*)config.option(key)); + break; + + case coPoint: + { + const ConfigOptionPoint &opt = + *(ConfigOptionPoint*)config.option(key); + + this->_single[key] = opt.serialize(); + + Pointf val = opt; + this->_multiple[key + "_X"] = val.x; + this->_multiple[key + "_Y"] = val.y; + } + + break; + + default: + // set single-value placeholders + this->_single[key] = config.serialize(key); + break; + } + } +} + +std::ostream& operator<<(std::ostream &stm, const Pointf &pointf) +{ + return stm << pointf.x << "," << pointf.y; +} + +template +void PlaceholderParser::set_multiple_from_vector(const std::string &key, + ConfigOptionVector &opt) +{ + const std::vector &vals = opt.values; + + for (size_t i = 0; i < vals.size(); ++i) { + std::stringstream multikey_stm; + multikey_stm << key << "_" << i; + + std::stringstream val_stm; + val_stm << vals[i]; + + this->_multiple[multikey_stm.str()] = val_stm.str(); + } + + if (vals.size() > 0) { + std::stringstream val_stm; + val_stm << vals[0]; + this->_multiple[key] = val_stm.str(); + } +} + +#ifdef SLIC3RXS +REGISTER_CLASS(PlaceholderParser, "GCode::PlaceholderParser"); +#endif + +} diff --git a/xs/src/libslic3r/PlaceholderParser.hpp b/xs/src/libslic3r/PlaceholderParser.hpp new file mode 100644 index 000000000..e69d6ed93 --- /dev/null +++ b/xs/src/libslic3r/PlaceholderParser.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_PlaceholderParser_hpp_ +#define slic3r_PlaceholderParser_hpp_ + + +#include +#include +#include +#include "PrintConfig.hpp" + + +namespace Slic3r { + +class PlaceholderParser +{ + public: + std::map _single; + std::map _multiple; + + PlaceholderParser(); + ~PlaceholderParser(); + + void apply_config(DynamicPrintConfig &config); + + private: + template + void set_multiple_from_vector( + const std::string &key, ConfigOptionVector &opt); +}; + +} + +#endif diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp new file mode 100644 index 000000000..2515d41a6 --- /dev/null +++ b/xs/src/libslic3r/Point.cpp @@ -0,0 +1,337 @@ +#include "Point.hpp" +#include "Line.hpp" +#include "MultiPoint.hpp" +#include +#include + +namespace Slic3r { + +Point::Point(double x, double y) +{ + this->x = lrint(x); + this->y = lrint(y); +} + +bool +Point::operator==(const Point& rhs) const +{ + return this->coincides_with(rhs); +} + +std::string +Point::wkt() const +{ + std::ostringstream ss; + ss << "POINT(" << this->x << " " << this->y << ")"; + return ss.str(); +} + +void +Point::scale(double factor) +{ + this->x *= factor; + this->y *= factor; +} + +void +Point::translate(double x, double y) +{ + this->x += x; + this->y += y; +} + +void +Point::rotate(double angle, const Point ¢er) +{ + double cur_x = (double)this->x; + double cur_y = (double)this->y; + this->x = (coord_t)round( (double)center.x + cos(angle) * (cur_x - (double)center.x) - sin(angle) * (cur_y - (double)center.y) ); + this->y = (coord_t)round( (double)center.y + cos(angle) * (cur_y - (double)center.y) + sin(angle) * (cur_x - (double)center.x) ); +} + +bool +Point::coincides_with(const Point &point) const +{ + return this->x == point.x && this->y == point.y; +} + +bool +Point::coincides_with_epsilon(const Point &point) const +{ + return std::abs(this->x - point.x) < SCALED_EPSILON && std::abs(this->y - point.y) < SCALED_EPSILON; +} + +int +Point::nearest_point_index(const Points &points) const +{ + PointConstPtrs p; + p.reserve(points.size()); + for (Points::const_iterator it = points.begin(); it != points.end(); ++it) + p.push_back(&*it); + return this->nearest_point_index(p); +} + +int +Point::nearest_point_index(const PointConstPtrs &points) const +{ + int idx = -1; + double distance = -1; // double because long is limited to 2147483647 on some platforms and it's not enough + + 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 = pow(this->x - (*it)->x, 2); + 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 += pow(this->y - (*it)->y, 2); + if (distance != -1 && d > distance) continue; + + idx = it - points.begin(); + distance = d; + + if (distance < EPSILON) break; + } + + return idx; +} + +int +Point::nearest_point_index(const PointPtrs &points) const +{ + PointConstPtrs p; + p.reserve(points.size()); + for (PointPtrs::const_iterator it = points.begin(); it != points.end(); ++it) + p.push_back(*it); + return this->nearest_point_index(p); +} + +void +Point::nearest_point(const Points &points, Point* point) const +{ + *point = points.at(this->nearest_point_index(points)); +} + +double +Point::distance_to(const Point &point) const +{ + double dx = ((double)point.x - this->x); + double dy = ((double)point.y - this->y); + return sqrt(dx*dx + dy*dy); +} + +double +Point::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. + * In other words it is the 2D cross product of p1-p2 and p1-this, i.e. + * 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 +{ + return (double)(p2.x - p1.x)*(double)(this->y - p1.y) - (double)(p2.y - p1.y)*(double)(this->x - p1.x); +} + +double +Point::ccw(const Line &line) const +{ + return this->ccw(line.a, line.b); +} + +Point +Point::projection_onto(const MultiPoint &poly) const +{ + Point running_projection = poly.first_point(); + double running_min = this->distance_to(running_projection); + + 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) { + running_projection = point_temp; + running_min = this->distance_to(running_projection); + } + } + return running_projection; +} + +Point +Point::projection_onto(const Line &line) const +{ + if (line.a.coincides_with(line.b)) return line.a; + + /* + (Ported from VisiLibity by Karl J. Obermeyer) + The projection of point_temp onto the line determined by + line_segment_temp can be represented as an affine combination + expressed in the form projection of + Point = theta*line_segment_temp.first + (1.0-theta)*line_segment_temp.second. + If theta is outside the interval [0,1], then one of the Line_Segment's endpoints + must be closest to calling Point. + */ + double theta = ( (double)(line.b.x - this->x)*(double)(line.b.x - line.a.x) + (double)(line.b.y- this->y)*(double)(line.b.y - line.a.y) ) + / ( (double)pow(line.b.x - line.a.x, 2) + (double)pow(line.b.y - line.a.y, 2) ); + + if (0.0 <= theta && theta <= 1.0) + return theta * line.a + (1.0-theta) * line.b; + + // 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); +} + +Point +operator+(const Point& point1, const Point& point2) +{ + return Point(point1.x + point2.x, point1.y + point2.y); +} + +Point +operator*(double scalar, const Point& point2) +{ + return Point(scalar * point2.x, scalar * point2.y); +} + +#ifdef SLIC3RXS + +REGISTER_CLASS(Point, "Point"); + +SV* +Point::to_SV_pureperl() const { + AV* av = newAV(); + av_fill(av, 1); + av_store(av, 0, newSViv(this->x)); + av_store(av, 1, newSViv(this->y)); + return newRV_noinc((SV*)av); +} + +void +Point::from_SV(SV* point_sv) +{ + AV* point_av = (AV*)SvRV(point_sv); + // get a double from Perl and round it, otherwise + // it would get truncated + this->x = lrint(SvNV(*av_fetch(point_av, 0, 0))); + this->y = lrint(SvNV(*av_fetch(point_av, 1, 0))); +} + +void +Point::from_SV_check(SV* point_sv) +{ + if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) { + if (!sv_isa(point_sv, perl_class_name(this)) && !sv_isa(point_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object (got %s)", perl_class_name(this), HvNAME(SvSTASH(SvRV(point_sv)))); + *this = *(Point*)SvIV((SV*)SvRV( point_sv )); + } else { + this->from_SV(point_sv); + } +} + + +REGISTER_CLASS(Point3, "Point3"); + +#endif + +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::rotate(double angle, const Pointf ¢er) +{ + double cur_x = this->x; + double cur_y = this->y; + this->x = center.x + cos(angle) * (cur_x - center.x) - sin(angle) * (cur_y - center.y); + this->y = center.y + cos(angle) * (cur_y - center.y) + sin(angle) * (cur_x - center.x); +} + +#ifdef SLIC3RXS + +REGISTER_CLASS(Pointf, "Pointf"); + +SV* +Pointf::to_SV_pureperl() const { + AV* av = newAV(); + av_fill(av, 1); + av_store(av, 0, newSVnv(this->x)); + av_store(av, 1, newSVnv(this->y)); + return newRV_noinc((SV*)av); +} + +bool +Pointf::from_SV(SV* point_sv) +{ + AV* point_av = (AV*)SvRV(point_sv); + SV* sv_x = *av_fetch(point_av, 0, 0); + SV* sv_y = *av_fetch(point_av, 1, 0); + if (!looks_like_number(sv_x) || !looks_like_number(sv_y)) return false; + + this->x = SvNV(sv_x); + this->y = SvNV(sv_y); + return true; +} + +void +Pointf::from_SV_check(SV* point_sv) +{ + if (sv_isobject(point_sv) && (SvTYPE(SvRV(point_sv)) == SVt_PVMG)) { + if (!sv_isa(point_sv, perl_class_name(this)) && !sv_isa(point_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object (got %s)", perl_class_name(this), HvNAME(SvSTASH(SvRV(point_sv)))); + *this = *(Pointf*)SvIV((SV*)SvRV( point_sv )); + } else { + this->from_SV(point_sv); + } +} +#endif + +void +Pointf3::scale(double factor) +{ + Pointf::scale(factor); + this->z *= factor; +} + +void +Pointf3::translate(double x, double y, double z) +{ + Pointf::translate(x, y); + this->z += z; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(Pointf3, "Pointf3"); +#endif + +} diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp new file mode 100644 index 000000000..28d216df2 --- /dev/null +++ b/xs/src/libslic3r/Point.hpp @@ -0,0 +1,141 @@ +#ifndef slic3r_Point_hpp_ +#define slic3r_Point_hpp_ + +#include +#include +#include +#include + +namespace Slic3r { + +class Line; +class MultiPoint; +class Point; +class Pointf; +typedef Point Vector; +typedef std::vector Points; +typedef std::vector PointPtrs; +typedef std::vector PointConstPtrs; +typedef std::vector Pointfs; + +class Point +{ + public: + coord_t x; + coord_t y; + Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {}; + Point(int _x, int _y): x(_x), y(_y) {}; + Point(long long _x, long long _y): x(_x), y(_y) {}; // for Clipper + Point(double x, double y); + bool operator==(const Point& rhs) const; + std::string wkt() const; + void scale(double factor); + void translate(double x, double y); + void rotate(double angle, const Point ¢er); + bool coincides_with(const Point &point) const; + 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; + void nearest_point(const Points &points, Point* point) const; + double distance_to(const Point &point) const; + double distance_to(const Line &line) const; + double ccw(const Point &p1, const Point &p2) const; + double ccw(const Line &line) const; + Point projection_onto(const MultiPoint &poly) const; + Point projection_onto(const Line &line) const; + Point negative() const; + + #ifdef SLIC3RXS + void from_SV(SV* point_sv); + void from_SV_check(SV* point_sv); + SV* to_SV_pureperl() const; + #endif +}; + +Point operator+(const Point& point1, const Point& point2); +Point operator*(double scalar, const Point& point2); + +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) {}; +}; + +class Pointf +{ + public: + coordf_t x; + coordf_t y; + explicit Pointf(coordf_t _x = 0, coordf_t _y = 0): x(_x), y(_y) {}; + void scale(double factor); + void translate(double x, double y); + void rotate(double angle, const Pointf ¢er); + + #ifdef SLIC3RXS + bool from_SV(SV* point_sv); + void from_SV_check(SV* point_sv); + SV* to_SV_pureperl() const; + #endif +}; + +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) {}; + void scale(double factor); + void translate(double x, double y, double z); +}; + +} + +// start Boost +#include +namespace boost { namespace polygon { + template <> + struct geometry_concept { typedef coordinate_concept type; }; + + template <> + struct coordinate_traits { + typedef coord_t coordinate_type; + typedef long double area_type; + typedef long long manhattan_area_type; + typedef unsigned long long unsigned_area_type; + typedef long long coordinate_difference; + typedef long double coordinate_distance; + }; + + template <> + struct geometry_concept { typedef point_concept type; }; + + template <> + struct point_traits { + typedef coord_t coordinate_type; + + static inline coordinate_type get(const Point& point, orientation_2d orient) { + return (orient == HORIZONTAL) ? point.x : point.y; + } + }; + + template <> + struct point_mutable_traits { + typedef coord_t coordinate_type; + static inline void set(Point& point, orientation_2d orient, coord_t value) { + if (orient == HORIZONTAL) + point.x = value; + else + point.y = value; + } + static inline Point construct(coord_t x_value, coord_t y_value) { + Point retval; + retval.x = x_value; + retval.y = y_value; + return retval; + } + }; +} } +// end Boost + +#endif diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp new file mode 100644 index 000000000..3fe16bc70 --- /dev/null +++ b/xs/src/libslic3r/Polygon.cpp @@ -0,0 +1,225 @@ +#include +#include "ClipperUtils.hpp" +#include "Polygon.hpp" +#include "Polyline.hpp" + +namespace Slic3r { + +Polygon::operator Polygons() const +{ + Polygons pp; + pp.push_back(*this); + return pp; +} + +Polygon::operator Polyline() const +{ + Polyline polyline; + this->split_at_first_point(&polyline); + return polyline; +} + +Point& +Polygon::operator[](Points::size_type idx) +{ + return this->points[idx]; +} + +const Point& +Polygon::operator[](Points::size_type idx) const +{ + return this->points[idx]; +} + +Point +Polygon::last_point() const +{ + return this->points.front(); // last point == first point for polygons +} + +Lines +Polygon::lines() const +{ + Lines lines; + this->lines(&lines); + return lines; +} + +void +Polygon::lines(Lines* lines) const +{ + lines->reserve(lines->size() + this->points.size()); + for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) { + lines->push_back(Line(*it, *(it + 1))); + } + lines->push_back(Line(this->points.back(), this->points.front())); +} + +void +Polygon::split_at_vertex(const Point &point, Polyline* polyline) const +{ + // find index of point + for (Points::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { + if (it->coincides_with(point)) { + this->split_at_index(it - this->points.begin(), polyline); + return; + } + } + CONFESS("Point not found"); +} + +void +Polygon::split_at_index(int index, Polyline* polyline) const +{ + polyline->points.reserve(this->points.size() + 1); + for (Points::const_iterator it = this->points.begin() + index; it != this->points.end(); ++it) + polyline->points.push_back(*it); + for (Points::const_iterator it = this->points.begin(); it != this->points.begin() + index + 1; ++it) + polyline->points.push_back(*it); +} + +void +Polygon::split_at_first_point(Polyline* polyline) const +{ + this->split_at_index(0, polyline); +} + +void +Polygon::equally_spaced_points(double distance, Points* points) const +{ + Polyline polyline; + this->split_at_first_point(&polyline); + polyline.equally_spaced_points(distance, points); +} + +double +Polygon::area() const +{ + ClipperLib::Path p; + Slic3rMultiPoint_to_ClipperPath(*this, p); + return ClipperLib::Area(p); +} + +bool +Polygon::is_counter_clockwise() const +{ + ClipperLib::Path p; + Slic3rMultiPoint_to_ClipperPath(*this, p); + return ClipperLib::Orientation(p); +} + +bool +Polygon::is_clockwise() const +{ + return !this->is_counter_clockwise(); +} + +bool +Polygon::make_counter_clockwise() +{ + if (!this->is_counter_clockwise()) { + this->reverse(); + return true; + } + return false; +} + +bool +Polygon::make_clockwise() +{ + if (this->is_counter_clockwise()) { + this->reverse(); + return true; + } + return false; +} + +bool +Polygon::is_valid() const +{ + return this->points.size() >= 3; +} + +bool +Polygon::contains_point(const Point &point) const +{ + // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + bool result = false; + Points::const_iterator i = this->points.begin(); + Points::const_iterator j = this->points.end() - 1; + for (; i != this->points.end(); j = i++) { + 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) ) + result = !result; + } + return result; +} + +Polygons +Polygon::simplify(double tolerance) const +{ + Polygon p = *this; + p.points = MultiPoint::_douglas_peucker(p.points, tolerance); + + Polygons pp; + pp.push_back(p); + simplify_polygons(pp, pp); + return pp; +} + +void +Polygon::simplify(double tolerance, Polygons &polygons) const +{ + Polygons pp = this->simplify(tolerance); + polygons.reserve(polygons.size() + pp.size()); + polygons.insert(polygons.end(), pp.begin(), pp.end()); +} + +// Only call this on convex polygons or it will return invalid results +void +Polygon::triangulate_convex(Polygons* polygons) const +{ + for (Points::const_iterator it = this->points.begin() + 2; it != this->points.end(); ++it) { + Polygon p; + p.points.reserve(3); + p.points.push_back(this->points.front()); + p.points.push_back(*(it-1)); + p.points.push_back(*it); + + // this should be replaced with a more efficient call to a merge_collinear_segments() method + if (p.area() > 0) polygons->push_back(p); + } +} + +// center of mass +Point +Polygon::centroid() const +{ + double area_temp = this->area(); + double x_temp = 0; + double y_temp = 0; + + Polyline polyline; + this->split_at_first_point(&polyline); + 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 ); + } + + return Point(x_temp/(6*area_temp), y_temp/(6*area_temp)); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(Polygon, "Polygon"); + +void +Polygon::from_SV_check(SV* poly_sv) +{ + if (sv_isobject(poly_sv) && !sv_isa(poly_sv, perl_class_name(this)) && !sv_isa(poly_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object", perl_class_name(this)); + + MultiPoint::from_SV_check(poly_sv); +} +#endif + +} diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp new file mode 100644 index 000000000..816b6be18 --- /dev/null +++ b/xs/src/libslic3r/Polygon.hpp @@ -0,0 +1,130 @@ +#ifndef slic3r_Polygon_hpp_ +#define slic3r_Polygon_hpp_ + +#include +#include +#include "Line.hpp" +#include "MultiPoint.hpp" +#include "Polyline.hpp" + +namespace Slic3r { + +class Polygon; +typedef std::vector Polygons; + +class Polygon : public MultiPoint { + public: + operator Polygons() const; + operator Polyline() const; + Point& operator[](Points::size_type idx); + const Point& operator[](Points::size_type idx) const; + Point last_point() const; + Lines lines() const; + void lines(Lines* lines) const; + void split_at_vertex(const Point &point, Polyline* polyline) const; + void split_at_index(int index, Polyline* polyline) const; + void split_at_first_point(Polyline* polyline) const; + void equally_spaced_points(double distance, Points* points) const; + double area() const; + bool is_counter_clockwise() const; + bool is_clockwise() const; + bool make_counter_clockwise(); + bool make_clockwise(); + bool is_valid() const; + bool contains_point(const Point &point) const; + Polygons simplify(double tolerance) const; + void simplify(double tolerance, Polygons &polygons) const; + void triangulate_convex(Polygons* polygons) const; + Point centroid() const; + + #ifdef SLIC3RXS + void from_SV_check(SV* poly_sv); + #endif +}; + +} + +// start Boost +#include +namespace boost { namespace polygon { + template <> + struct geometry_concept{ typedef polygon_concept type; }; + + template <> + struct polygon_traits { + typedef coord_t coordinate_type; + typedef Points::const_iterator iterator_type; + typedef Point point_type; + + // Get the begin iterator + static inline iterator_type begin_points(const Polygon& t) { + return t.points.begin(); + } + + // Get the end iterator + static inline iterator_type end_points(const Polygon& t) { + return t.points.end(); + } + + // Get the number of sides of the polygon + static inline std::size_t size(const Polygon& t) { + return t.points.size(); + } + + // Get the winding direction of the polygon + static inline winding_direction winding(const Polygon& t) { + return unknown_winding; + } + }; + + template <> + struct polygon_mutable_traits { + // expects stl style iterators + template + static inline Polygon& set_points(Polygon& polygon, iT input_begin, iT input_end) { + polygon.points.clear(); + while (input_begin != input_end) { + polygon.points.push_back(Point()); + boost::polygon::assign(polygon.points.back(), *input_begin); + ++input_begin; + } + // skip last point since Boost will set last point = first point + polygon.points.pop_back(); + return polygon; + } + }; + + template <> + struct geometry_concept { typedef polygon_set_concept type; }; + + //next we map to the concept through traits + template <> + struct polygon_set_traits { + typedef coord_t coordinate_type; + typedef Polygons::const_iterator iterator_type; + typedef Polygons operator_arg_type; + + static inline iterator_type begin(const Polygons& polygon_set) { + return polygon_set.begin(); + } + + static inline iterator_type end(const Polygons& polygon_set) { + return polygon_set.end(); + } + + //don't worry about these, just return false from them + static inline bool clean(const Polygons& polygon_set) { return false; } + static inline bool sorted(const Polygons& polygon_set) { return false; } + }; + + template <> + struct polygon_set_mutable_traits { + template + static inline void set(Polygons& polygons, input_iterator_type input_begin, input_iterator_type input_end) { + polygons.assign(input_begin, input_end); + } + }; +} } +// end Boost + +#endif diff --git a/xs/src/libslic3r/Polyline.cpp b/xs/src/libslic3r/Polyline.cpp new file mode 100644 index 000000000..724ae4740 --- /dev/null +++ b/xs/src/libslic3r/Polyline.cpp @@ -0,0 +1,172 @@ +#include "Polyline.hpp" +#include "Polygon.hpp" + +namespace Slic3r { + +Polyline::operator Polylines() const +{ + Polylines polylines; + polylines.push_back(*this); + return polylines; +} + +Point +Polyline::last_point() const +{ + return this->points.back(); +} + +Point +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; + } + return p; +} + +Lines +Polyline::lines() const +{ + Lines lines; + if (this->points.size() >= 2) { + lines.reserve(this->points.size() - 1); + for (Points::const_iterator it = this->points.begin(); it != this->points.end()-1; ++it) { + lines.push_back(Line(*it, *(it + 1))); + } + } + return lines; +} + +// removes the given distance from the end of the polyline +void +Polyline::clip_end(double distance) +{ + while (distance > 0) { + Point last_point = this->last_point(); + 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; + } + + Line segment(last_point, this->last_point()); + this->points.push_back(segment.point_at(distance)); + distance = 0; + } +} + +// removes the given distance from the start of the polyline +void +Polyline::clip_start(double distance) +{ + this->reverse(); + this->clip_end(distance); + if (this->points.size() >= 2) this->reverse(); +} + +void +Polyline::extend_end(double distance) +{ + // relocate last point by extending the last segment by the specified length + Line line(this->points[ this->points.size()-2 ], this->points.back()); + this->points.pop_back(); + this->points.push_back(line.point_at(line.length() + distance)); +} + +void +Polyline::extend_start(double distance) +{ + // relocate first point by extending the first segment by the specified length + Line line(this->points[1], this->points.front()); + this->points[0] = line.point_at(line.length() + distance); +} + +/* this method returns a collection of points picked on the polygon contour + so that they are evenly spaced according to the input distance */ +void +Polyline::equally_spaced_points(double distance, Points* points) const +{ + points->push_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)); + len += segment_length; + if (len < distance) continue; + + if (len == distance) { + points->push_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; + } +} + +void +Polyline::simplify(double tolerance) +{ + this->points = MultiPoint::_douglas_peucker(this->points, tolerance); +} + +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); + 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) { + p = p_tmp; + min = point.distance_to(p); + 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); + } + // 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); + + // create second half + p2->points.clear(); + p2->points.push_back(point); + for (Lines::const_iterator line = lines.begin() + line_idx; line != lines.end(); ++line) { + if (!line->b.coincides_with(p)) p2->points.push_back(line->b); + } +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(Polyline, "Polyline"); + +void +Polyline::from_SV_check(SV* poly_sv) +{ + if (!sv_isa(poly_sv, perl_class_name(this)) && !sv_isa(poly_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object",perl_class_name(this)); + + MultiPoint::from_SV_check(poly_sv); +} +#endif + +} diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp new file mode 100644 index 000000000..5462425cf --- /dev/null +++ b/xs/src/libslic3r/Polyline.hpp @@ -0,0 +1,33 @@ +#ifndef slic3r_Polyline_hpp_ +#define slic3r_Polyline_hpp_ + +#include "Line.hpp" +#include "MultiPoint.hpp" + +namespace Slic3r { + +class Polyline; +typedef std::vector Polylines; + +class Polyline : public MultiPoint { + public: + operator Polylines() const; + Point last_point() const; + Point leftmost_point() const; + Lines lines() const; + void clip_end(double distance); + void clip_start(double distance); + void extend_end(double distance); + void extend_start(double distance); + void equally_spaced_points(double distance, Points* points) const; + void simplify(double tolerance); + void split_at(const Point &point, Polyline* p1, Polyline* p2) const; + + #ifdef SLIC3RXS + void from_SV_check(SV* poly_sv); + #endif +}; + +} + +#endif diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp new file mode 100644 index 000000000..c2a142cf3 --- /dev/null +++ b/xs/src/libslic3r/PolylineCollection.cpp @@ -0,0 +1,57 @@ +#include "PolylineCollection.hpp" + +namespace Slic3r { + +void +PolylineCollection::chained_path(PolylineCollection* retval, bool no_reverse) const +{ + if (this->polylines.empty()) return; + this->chained_path_from(this->polylines.front().first_point(), retval, no_reverse); +} + +void +PolylineCollection::chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse) const +{ + Polylines my_paths = this->polylines; + + Points endpoints; + for (Polylines::const_iterator it = my_paths.begin(); it != my_paths.end(); ++it) { + endpoints.push_back(it->first_point()); + if (no_reverse) { + endpoints.push_back(it->first_point()); + } else { + endpoints.push_back(it->last_point()); + } + } + + while (!my_paths.empty()) { + // find nearest point + int start_index = start_near.nearest_point_index(endpoints); + int path_index = start_index/2; + if (start_index % 2 && !no_reverse) { + my_paths.at(path_index).reverse(); + } + retval->polylines.push_back(my_paths.at(path_index)); + my_paths.erase(my_paths.begin() + path_index); + endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); + start_near = retval->polylines.back().last_point(); + } +} + +Point +PolylineCollection::leftmost_point() const +{ + if (this->polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection"); + Point p = this->polylines.front().leftmost_point(); + for (Polylines::const_iterator it = this->polylines.begin() + 1; it != this->polylines.end(); ++it) { + Point p2 = it->leftmost_point(); + if (p2.x < p.x) p = p2; + } + return p; +} + +#ifdef SLIC3RXS +REGISTER_CLASS(PolylineCollection, "Polyline::Collection"); +#endif + +} diff --git a/xs/src/libslic3r/PolylineCollection.hpp b/xs/src/libslic3r/PolylineCollection.hpp new file mode 100644 index 000000000..ace03ad37 --- /dev/null +++ b/xs/src/libslic3r/PolylineCollection.hpp @@ -0,0 +1,20 @@ +#ifndef slic3r_PolylineCollection_hpp_ +#define slic3r_PolylineCollection_hpp_ + +#include +#include "Polyline.hpp" + +namespace Slic3r { + +class PolylineCollection +{ + public: + Polylines polylines; + void chained_path(PolylineCollection* retval, bool no_reverse = false) const; + void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const; + Point leftmost_point() const; +}; + +} + +#endif diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp new file mode 100644 index 000000000..d71cb4349 --- /dev/null +++ b/xs/src/libslic3r/Print.cpp @@ -0,0 +1,345 @@ +#include "Print.hpp" +#include "BoundingBox.hpp" +#include + +namespace Slic3r { + +template +bool +PrintState::is_started(StepClass step) const +{ + return this->started.find(step) != this->started.end(); +} + +template +bool +PrintState::is_done(StepClass step) const +{ + return this->done.find(step) != this->done.end(); +} + +template +void +PrintState::set_started(StepClass step) +{ + this->started.insert(step); +} + +template +void +PrintState::set_done(StepClass step) +{ + this->done.insert(step); +} + +template +bool +PrintState::invalidate(StepClass step) +{ + bool invalidated = this->started.erase(step) > 0; + this->done.erase(step); + return invalidated; +} + +template class PrintState; +template class PrintState; + + +Print::Print() +: total_used_filament(0), + total_extruded_volume(0) +{ +} + +Print::~Print() +{ + clear_objects(); + clear_regions(); +} + +void +Print::clear_objects() +{ + for (int i = this->objects.size()-1; i >= 0; --i) + this->delete_object(i); + + this->clear_regions(); +} + +PrintObject* +Print::get_object(size_t idx) +{ + return objects.at(idx); +} + +PrintObject* +Print::add_object(ModelObject *model_object, const BoundingBoxf3 &modobj_bbox) +{ + PrintObject *object = new PrintObject(this, model_object, modobj_bbox); + objects.push_back(object); + + // invalidate steps + this->invalidate_step(psSkirt); + this->invalidate_step(psBrim); + + return object; +} + +PrintObject* +Print::set_new_object(size_t idx, ModelObject *model_object, const BoundingBoxf3 &modobj_bbox) +{ + if (idx >= this->objects.size()) throw "bad idx"; + + PrintObjectPtrs::iterator old_it = this->objects.begin() + idx; + // before deleting object, invalidate all of its steps in order to + // invalidate all of the dependent ones in Print + (*old_it)->invalidate_all_steps(); + delete *old_it; + + PrintObject *object = new PrintObject(this, model_object, modobj_bbox); + this->objects[idx] = object; + return object; +} + +void +Print::delete_object(size_t idx) +{ + PrintObjectPtrs::iterator i = this->objects.begin() + idx; + delete *i; + this->objects.erase(i); + + // TODO: purge unused regions + + this->state.invalidate(psSkirt); + this->state.invalidate(psBrim); +} + +void +Print::clear_regions() +{ + for (int i = this->regions.size()-1; i >= 0; --i) + this->delete_region(i); +} + +PrintRegion* +Print::get_region(size_t idx) +{ + return regions.at(idx); +} + +PrintRegion* +Print::add_region() +{ + PrintRegion *region = new PrintRegion(this); + regions.push_back(region); + return region; +} + +void +Print::delete_region(size_t idx) +{ + PrintRegionPtrs::iterator i = this->regions.begin() + idx; + delete *i; + this->regions.erase(i); +} + +bool +Print::invalidate_state_by_config_options(const std::vector &opt_keys) +{ + std::set steps; + + // this method only accepts PrintConfig option keys + for (std::vector::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) { + if (*opt_key == "skirts" + || *opt_key == "skirt_height" + || *opt_key == "skirt_distance" + || *opt_key == "min_skirt_length") { + steps.insert(psSkirt); + } else if (*opt_key == "brim_width") { + steps.insert(psBrim); + steps.insert(psSkirt); + } else if (*opt_key == "nozzle_diameter") { + steps.insert(psInitExtruders); + } else if (*opt_key == "avoid_crossing_perimeters" + || *opt_key == "bed_shape" + || *opt_key == "bed_temperature" + || *opt_key == "bridge_acceleration" + || *opt_key == "bridge_fan_speed" + || *opt_key == "complete_objects" + || *opt_key == "cooling" + || *opt_key == "default_acceleration" + || *opt_key == "disable_fan_first_layers" + || *opt_key == "duplicate_distance" + || *opt_key == "end_gcode" + || *opt_key == "extruder_clearance_height" + || *opt_key == "extruder_clearance_radius" + || *opt_key == "extruder_offset" + || *opt_key == "extrusion_axis" + || *opt_key == "extrusion_multiplier" + || *opt_key == "fan_always_on" + || *opt_key == "fan_below_layer_time" + || *opt_key == "filament_diameter" + || *opt_key == "first_layer_acceleration" + || *opt_key == "first_layer_bed_temperature" + || *opt_key == "first_layer_speed" + || *opt_key == "first_layer_temperature" + || *opt_key == "g0" + || *opt_key == "gcode_arcs" + || *opt_key == "gcode_comments" + || *opt_key == "gcode_flavor" + || *opt_key == "infill_acceleration" + || *opt_key == "infill_first" + || *opt_key == "layer_gcode" + || *opt_key == "min_fan_speed" + || *opt_key == "max_fan_speed" + || *opt_key == "min_print_speed" + || *opt_key == "notes" + || *opt_key == "only_retract_when_crossing_perimeters" + || *opt_key == "output_filename_format" + || *opt_key == "perimeter_acceleration" + || *opt_key == "post_process" + || *opt_key == "retract_before_travel" + || *opt_key == "retract_layer_change" + || *opt_key == "retract_length" + || *opt_key == "retract_length_toolchange" + || *opt_key == "retract_lift" + || *opt_key == "retract_restart_extra" + || *opt_key == "retract_restart_extra_toolchange" + || *opt_key == "retract_speed" + || *opt_key == "slowdown_below_layer_time" + || *opt_key == "spiral_vase" + || *opt_key == "standby_temperature_delta" + || *opt_key == "start_gcode" + || *opt_key == "temperature" + || *opt_key == "threads" + || *opt_key == "toolchange_gcode" + || *opt_key == "travel_speed" + || *opt_key == "use_firmware_retraction" + || *opt_key == "use_relative_e_distances" + || *opt_key == "vibration_limit" + || *opt_key == "wipe" + || *opt_key == "z_offset") { + // 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 + return this->invalidate_all_steps(); + } + } + + bool invalidated = false; + for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { + if (this->invalidate_step(*step)) invalidated = true; + } + + return invalidated; +} + +bool +Print::invalidate_step(PrintStep step) +{ + bool invalidated = this->state.invalidate(step); + + // propagate to dependent steps + if (step == psSkirt) { + this->invalidate_step(psBrim); + } else if (step == psInitExtruders) { + FOREACH_OBJECT(this, object) { + (*object)->invalidate_step(posPerimeters); + (*object)->invalidate_step(posSupportMaterial); + } + } + + return invalidated; +} + +bool +Print::invalidate_all_steps() +{ + // make a copy because when invalidating steps the iterators are not working anymore + std::set steps = this->state.started; + + bool invalidated = false; + for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { + if (this->invalidate_step(*step)) invalidated = true; + } + return invalidated; +} + +// returns 0-based indices of used extruders +std::set +Print::extruders() const +{ + std::set extruders; + + FOREACH_REGION(this, region) { + extruders.insert((*region)->config.perimeter_extruder - 1); + extruders.insert((*region)->config.infill_extruder - 1); + } + FOREACH_OBJECT(this, object) { + extruders.insert((*object)->config.support_material_extruder - 1); + extruders.insert((*object)->config.support_material_interface_extruder - 1); + } + + return extruders; +} + +void +Print::_simplify_slices(double distance) +{ + FOREACH_OBJECT(this, object) { + FOREACH_LAYER(*object, layer) { + (*layer)->slices.simplify(distance); + FOREACH_LAYERREGION(*layer, layerm) { + (*layerm)->slices.simplify(distance); + } + } + } +} + +double +Print::max_allowed_layer_height() const +{ + std::vector nozzle_diameter; + + std::set extruders = this->extruders(); + for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { + nozzle_diameter.push_back(this->config.nozzle_diameter.get_at(*e)); + } + + return *std::max_element(nozzle_diameter.begin(), nozzle_diameter.end()); +} + +void +Print::init_extruders() +{ + if (this->state.is_done(psInitExtruders)) return; + this->state.set_done(psInitExtruders); + + // enforce tall skirt if using ooze_prevention + // FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings) + if (this->config.ooze_prevention && this->extruders().size() > 1) { + this->config.skirt_height.value = -1; + if (this->config.skirts == 0) this->config.skirts.value = 1; + } + + this->state.set_done(psInitExtruders); +} + +bool +Print::has_support_material() const +{ + FOREACH_OBJECT(this, object) { + PrintObjectConfig &config = (*object)->config; + if (config.support_material || config.raft_layers > 0 || config.support_material_enforce_layers > 0) + return true; + } + return false; +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(Print, "Print"); +#endif + + +} diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp new file mode 100644 index 000000000..8b2a45044 --- /dev/null +++ b/xs/src/libslic3r/Print.hpp @@ -0,0 +1,187 @@ +#ifndef slic3r_Print_hpp_ +#define slic3r_Print_hpp_ + +#include +#include +#include +#include "Flow.hpp" +#include "PrintConfig.hpp" +#include "Point.hpp" +#include "Layer.hpp" +#include "PlaceholderParser.hpp" + + +namespace Slic3r { + +class Print; +class PrintObject; +class ModelObject; + + +enum PrintStep { + psInitExtruders, psSkirt, psBrim, +}; +enum PrintObjectStep { + posSlice, posPerimeters, posPrepareInfill, + posInfill, posSupportMaterial, +}; + +template +class PrintState +{ + public: + std::set started, done; + + bool is_started(StepType step) const; + bool is_done(StepType step) const; + void set_started(StepType step); + void set_done(StepType step); + bool invalidate(StepType step); +}; + +// A PrintRegion object represents a group of volumes to print +// sharing the same config (including the same assigned extruder(s)) +class PrintRegion +{ + friend class Print; + + public: + PrintRegionConfig config; + + Print* print(); + Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; + + private: + Print* _print; + + PrintRegion(Print* print); + ~PrintRegion(); +}; + + +typedef std::vector LayerPtrs; +typedef std::vector SupportLayerPtrs; +class BoundingBoxf3; // TODO: for temporary constructor parameter + +class PrintObject +{ + friend class Print; + + public: + // vector of (vectors of volume ids), indexed by region_id + std::vector > region_volumes; + Points copies; // Slic3r::Point objects in scaled G-code coordinates + PrintObjectConfig config; + t_layer_height_ranges layer_height_ranges; + + // 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; + + 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; + + LayerPtrs layers; + SupportLayerPtrs support_layers; + // TODO: Fill* fill_maker => (is => 'lazy'); + PrintState state; + + Print* print(); + ModelObject* model_object(); + + // adds region_id, too, if necessary + void add_region_volume(int region_id, int volume_id); + + size_t layer_count(); + void clear_layers(); + Layer* get_layer(int idx); + Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + void delete_layer(int idx); + + size_t support_layer_count(); + void clear_support_layers(); + SupportLayer* get_support_layer(int idx); + SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + void delete_support_layer(int idx); + + // methods for handling state + bool invalidate_state_by_config_options(const std::vector &opt_keys); + bool invalidate_step(PrintObjectStep step); + bool invalidate_all_steps(); + + private: + Print* _print; + ModelObject* _model_object; + + // TODO: call model_object->get_bounding_box() instead of accepting + // parameter + PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); + ~PrintObject(); +}; + +typedef std::vector PrintObjectPtrs; +typedef std::vector PrintRegionPtrs; + +class Print +{ + public: + PrintConfig config; + PrintObjectConfig default_object_config; + PrintRegionConfig default_region_config; + PrintObjectPtrs objects; + PrintRegionPtrs regions; + PlaceholderParser placeholder_parser; + // TODO: status_cb + double total_used_filament, total_extruded_volume; + PrintState state; + + // ordered collections of extrusion paths to build skirt loops and brim + ExtrusionEntityCollection skirt, brim; + + Print(); + ~Print(); + + // methods for handling objects + void clear_objects(); + PrintObject* get_object(size_t idx); + PrintObject* add_object(ModelObject *model_object, const BoundingBoxf3 &modobj_bbox); + PrintObject* set_new_object(size_t idx, ModelObject *model_object, const BoundingBoxf3 &modobj_bbox); + void delete_object(size_t idx); + + // methods for handling regions + PrintRegion* get_region(size_t idx); + PrintRegion* add_region(); + + // methods for handling state + bool invalidate_state_by_config_options(const std::vector &opt_keys); + bool invalidate_step(PrintStep step); + bool invalidate_all_steps(); + + void init_extruders(); + + std::set extruders() const; + void _simplify_slices(double distance); + double max_allowed_layer_height() const; + bool has_support_material() const; + + private: + void clear_regions(); + void delete_region(size_t idx); +}; + +#define FOREACH_BASE(type, container, iterator) for (type::const_iterator iterator = (container).begin(); iterator != (container).end(); ++iterator) +#define FOREACH_REGION(print, region) FOREACH_BASE(PrintRegionPtrs, (print)->regions, region) +#define FOREACH_OBJECT(print, object) FOREACH_BASE(PrintObjectPtrs, (print)->objects, object) +#define FOREACH_LAYER(object, layer) FOREACH_BASE(LayerPtrs, (object)->layers, layer) +#define FOREACH_LAYERREGION(layer, layerm) FOREACH_BASE(LayerRegionPtrs, (layer)->regions, layerm) + +} + +#endif diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp new file mode 100644 index 000000000..2e9d8a6bf --- /dev/null +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -0,0 +1,967 @@ +#include "PrintConfig.hpp" + +namespace Slic3r { + +t_optiondef_map +PrintConfigDef::build_def() { + t_optiondef_map Options; + + Options["avoid_crossing_perimeters"].type = coBool; + Options["avoid_crossing_perimeters"].label = "Avoid crossing perimeters"; + Options["avoid_crossing_perimeters"].tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation."; + Options["avoid_crossing_perimeters"].cli = "avoid-crossing-perimeters!"; + + Options["bed_shape"].type = coPoints; + Options["bed_shape"].label = "Bed shape"; + + Options["bed_temperature"].type = coInt; + Options["bed_temperature"].label = "Other layers"; + Options["bed_temperature"].tooltip = "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output."; + Options["bed_temperature"].cli = "bed-temperature=i"; + Options["bed_temperature"].full_label = "Bed temperature"; + Options["bed_temperature"].min = 0; + Options["bed_temperature"].max = 300; + + Options["bottom_solid_layers"].type = coInt; + Options["bottom_solid_layers"].label = "Bottom"; + Options["bottom_solid_layers"].category = "Layers and Perimeters"; + Options["bottom_solid_layers"].tooltip = "Number of solid layers to generate on bottom surfaces."; + Options["bottom_solid_layers"].cli = "bottom-solid-layers=i"; + Options["bottom_solid_layers"].full_label = "Bottom solid layers"; + Options["bottom_solid_layers"].min = 0; + + Options["bridge_acceleration"].type = coFloat; + Options["bridge_acceleration"].label = "Bridge"; + Options["bridge_acceleration"].tooltip = "This is the acceleration your printer will use for bridges. Set zero to disable acceleration control for bridges."; + Options["bridge_acceleration"].sidetext = "mm/s²"; + Options["bridge_acceleration"].cli = "bridge-acceleration=f"; + Options["bridge_acceleration"].min = 0; + + Options["bridge_fan_speed"].type = coInt; + Options["bridge_fan_speed"].label = "Bridges fan speed"; + Options["bridge_fan_speed"].tooltip = "This fan speed is enforced during all bridges and overhangs."; + Options["bridge_fan_speed"].sidetext = "%"; + Options["bridge_fan_speed"].cli = "bridge-fan-speed=i"; + Options["bridge_fan_speed"].min = 0; + Options["bridge_fan_speed"].max = 100; + + Options["bridge_flow_ratio"].type = coFloat; + Options["bridge_flow_ratio"].label = "Bridge flow ratio"; + Options["bridge_flow_ratio"].category = "Advanced"; + Options["bridge_flow_ratio"].tooltip = "This factor affects the amount of plastic for bridging. You can decrease it slightly to pull the extrudates and prevent sagging, although default settings are usually good and you should experiment with cooling (use a fan) before tweaking this."; + Options["bridge_flow_ratio"].cli = "bridge-flow-ratio=f"; + Options["bridge_flow_ratio"].min = 0; + + Options["bridge_speed"].type = coFloat; + Options["bridge_speed"].label = "Bridges"; + Options["bridge_speed"].category = "Speed"; + Options["bridge_speed"].tooltip = "Speed for printing bridges."; + Options["bridge_speed"].sidetext = "mm/s"; + Options["bridge_speed"].cli = "bridge-speed=f"; + Options["bridge_speed"].aliases.push_back("bridge_feed_rate"); + Options["bridge_speed"].min = 0; + + Options["brim_width"].type = coFloat; + Options["brim_width"].label = "Brim width"; + Options["brim_width"].tooltip = "Horizontal width of the brim that will be printed around each object on the first layer."; + Options["brim_width"].sidetext = "mm"; + Options["brim_width"].cli = "brim-width=f"; + Options["brim_width"].min = 0; + + Options["complete_objects"].type = coBool; + Options["complete_objects"].label = "Complete individual objects"; + Options["complete_objects"].tooltip = "When printing multiple objects or copies, this feature will complete each object before moving onto next one (and starting it from its bottom layer). This feature is useful to avoid the risk of ruined prints. Slic3r should warn and prevent you from extruder collisions, but beware."; + Options["complete_objects"].cli = "complete-objects!"; + + Options["cooling"].type = coBool; + Options["cooling"].label = "Enable auto cooling"; + Options["cooling"].tooltip = "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time."; + Options["cooling"].cli = "cooling!"; + + Options["default_acceleration"].type = coFloat; + Options["default_acceleration"].label = "Default"; + Options["default_acceleration"].tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all."; + Options["default_acceleration"].sidetext = "mm/s²"; + Options["default_acceleration"].cli = "default-acceleration=f"; + Options["default_acceleration"].min = 0; + + Options["disable_fan_first_layers"].type = coInt; + Options["disable_fan_first_layers"].label = "Disable fan for the first"; + Options["disable_fan_first_layers"].tooltip = "You can set this to a positive value to disable fan at all during the first layers, so that it does not make adhesion worse."; + Options["disable_fan_first_layers"].sidetext = "layers"; + Options["disable_fan_first_layers"].cli = "disable-fan-first-layers=i"; + Options["disable_fan_first_layers"].min = 0; + Options["disable_fan_first_layers"].max = 1000; + + Options["dont_support_bridges"].type = coBool; + Options["dont_support_bridges"].label = "Don't support bridges"; + Options["dont_support_bridges"].category = "Support material"; + Options["dont_support_bridges"].tooltip = "Experimental option for preventing support material from being generated under bridged areas."; + Options["dont_support_bridges"].cli = "dont-support-bridges!"; + + Options["duplicate_distance"].type = coFloat; + Options["duplicate_distance"].label = "Distance between copies"; + Options["duplicate_distance"].tooltip = "Distance used for the auto-arrange feature of the plater."; + Options["duplicate_distance"].sidetext = "mm"; + Options["duplicate_distance"].cli = "duplicate-distance=f"; + Options["duplicate_distance"].aliases.push_back("multiply_distance"); + Options["duplicate_distance"].min = 0; + + Options["end_gcode"].type = coString; + Options["end_gcode"].label = "End G-code"; + Options["end_gcode"].tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings."; + Options["end_gcode"].cli = "end-gcode=s"; + Options["end_gcode"].multiline = true; + Options["end_gcode"].full_width = true; + Options["end_gcode"].height = 120; + + Options["external_perimeter_extrusion_width"].type = coFloatOrPercent; + Options["external_perimeter_extrusion_width"].label = "External perimeters"; + Options["external_perimeter_extrusion_width"].category = "Extrusion Width"; + Options["external_perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for external perimeters. If left zero, an automatic value will be used that maximizes accuracy of the external visible surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; + Options["external_perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["external_perimeter_extrusion_width"].cli = "external-perimeter-extrusion-width=s"; + + Options["external_perimeter_speed"].type = coFloatOrPercent; + Options["external_perimeter_speed"].label = "External perimeters"; + Options["external_perimeter_speed"].category = "Speed"; + Options["external_perimeter_speed"].tooltip = "This separate setting will affect the speed of external perimeters (the visible ones). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above."; + Options["external_perimeter_speed"].sidetext = "mm/s or %"; + Options["external_perimeter_speed"].cli = "external-perimeter-speed=s"; + Options["external_perimeter_speed"].ratio_over = "perimeter_speed"; + + Options["external_perimeters_first"].type = coBool; + Options["external_perimeters_first"].label = "External perimeters first"; + Options["external_perimeters_first"].category = "Layers and Perimeters"; + Options["external_perimeters_first"].tooltip = "Print contour perimeters from the outermost one to the innermost one instead of the default inverse order."; + Options["external_perimeters_first"].cli = "external-perimeters-first!"; + + Options["extra_perimeters"].type = coBool; + Options["extra_perimeters"].label = "Extra perimeters if needed"; + Options["extra_perimeters"].category = "Layers and Perimeters"; + Options["extra_perimeters"].tooltip = "Add more perimeters when needed for avoiding gaps in sloping walls."; + Options["extra_perimeters"].cli = "extra-perimeters!"; + + Options["extruder"].type = coInt; + Options["extruder"].gui_type = "i_enum_open"; + Options["extruder"].label = "Extruder"; + Options["extruder"].category = "Extruders"; + Options["extruder"].tooltip = "The extruder to use (unless more specific extruder settings are specified)."; + Options["extruder"].cli = "extruder=i"; + Options["extruder"].min = 0; // 0 = inherit defaults + Options["extruder"].enum_labels.push_back("default"); // override label for item 0 + Options["extruder"].enum_labels.push_back("1"); + Options["extruder"].enum_labels.push_back("2"); + Options["extruder"].enum_labels.push_back("3"); + Options["extruder"].enum_labels.push_back("4"); + + Options["extruder_clearance_height"].type = coFloat; + Options["extruder_clearance_height"].label = "Height"; + Options["extruder_clearance_height"].tooltip = "Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. In other words, this is the height of the clearance cylinder around your extruder, and it represents the maximum depth the extruder can peek before colliding with other printed objects."; + Options["extruder_clearance_height"].sidetext = "mm"; + Options["extruder_clearance_height"].cli = "extruder-clearance-height=f"; + Options["extruder_clearance_height"].min = 0; + + Options["extruder_clearance_radius"].type = coFloat; + Options["extruder_clearance_radius"].label = "Radius"; + Options["extruder_clearance_radius"].tooltip = "Set this to the clearance radius around your extruder. If the extruder is not centered, choose the largest value for safety. This setting is used to check for collisions and to display the graphical preview in the plater."; + Options["extruder_clearance_radius"].sidetext = "mm"; + Options["extruder_clearance_radius"].cli = "extruder-clearance-radius=f"; + Options["extruder_clearance_radius"].min = 0; + + Options["extruder_offset"].type = coPoints; + Options["extruder_offset"].label = "Extruder offset"; + Options["extruder_offset"].tooltip = "If your firmware doesn't handle the extruder displacement you need the G-code to take it into account. This option lets you specify the displacement of each extruder with respect to the first one. It expects positive coordinates (they will be subtracted from the XY coordinate)."; + Options["extruder_offset"].sidetext = "mm"; + Options["extruder_offset"].cli = "extruder-offset=s@"; + + Options["extrusion_axis"].type = coString; + Options["extrusion_axis"].label = "Extrusion axis"; + Options["extrusion_axis"].tooltip = "Use this option to set the axis letter associated to your printer's extruder (usually E but some printers use A)."; + Options["extrusion_axis"].cli = "extrusion-axis=s"; + + Options["extrusion_multiplier"].type = coFloats; + Options["extrusion_multiplier"].label = "Extrusion multiplier"; + Options["extrusion_multiplier"].tooltip = "This factor changes the amount of flow proportionally. You may need to tweak this setting to get nice surface finish and correct single wall widths. Usual values are between 0.9 and 1.1. If you think you need to change this more, check filament diameter and your firmware E steps."; + Options["extrusion_multiplier"].cli = "extrusion-multiplier=f@"; + + Options["extrusion_width"].type = coFloatOrPercent; + Options["extrusion_width"].label = "Default extrusion width"; + Options["extrusion_width"].category = "Extrusion Width"; + Options["extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width. If left to zero, Slic3r calculates a width automatically. If expressed as percentage (for example: 230%) it will be computed over layer height."; + Options["extrusion_width"].sidetext = "mm or % (leave 0 for auto)"; + Options["extrusion_width"].cli = "extrusion-width=s"; + + Options["fan_always_on"].type = coBool; + Options["fan_always_on"].label = "Keep fan always on"; + Options["fan_always_on"].tooltip = "If this is enabled, fan will never be disabled and will be kept running at least at its minimum speed. Useful for PLA, harmful for ABS."; + Options["fan_always_on"].cli = "fan-always-on!"; + + Options["fan_below_layer_time"].type = coInt; + Options["fan_below_layer_time"].label = "Enable fan if layer print time is below"; + Options["fan_below_layer_time"].tooltip = "If layer print time is estimated below this number of seconds, fan will be enabled and its speed will be calculated by interpolating the minimum and maximum speeds."; + Options["fan_below_layer_time"].sidetext = "approximate seconds"; + Options["fan_below_layer_time"].cli = "fan-below-layer-time=i"; + Options["fan_below_layer_time"].width = 60; + Options["fan_below_layer_time"].min = 0; + Options["fan_below_layer_time"].max = 1000; + + Options["filament_diameter"].type = coFloats; + Options["filament_diameter"].label = "Diameter"; + Options["filament_diameter"].tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."; + Options["filament_diameter"].sidetext = "mm"; + Options["filament_diameter"].cli = "filament-diameter=f@"; + Options["filament_diameter"].min = 0; + + Options["fill_angle"].type = coInt; + Options["fill_angle"].label = "Fill angle"; + Options["fill_angle"].category = "Infill"; + Options["fill_angle"].tooltip = "Default base angle for infill orientation. Cross-hatching will be applied to this. Bridges will be infilled using the best direction Slic3r can detect, so this setting does not affect them."; + Options["fill_angle"].sidetext = "°"; + Options["fill_angle"].cli = "fill-angle=i"; + Options["fill_angle"].min = 0; + Options["fill_angle"].max = 359; + + Options["fill_density"].type = coPercent; + Options["fill_density"].gui_type = "f_enum_open"; + Options["fill_density"].gui_flags = "show_value"; + Options["fill_density"].label = "Fill density"; + Options["fill_density"].category = "Infill"; + Options["fill_density"].tooltip = "Density of internal infill, expressed in the range 0% - 100%."; + Options["fill_density"].sidetext = "%"; + Options["fill_density"].cli = "fill-density=s"; + Options["fill_density"].min = 0; + Options["fill_density"].max = 100; + Options["fill_density"].enum_values.push_back("0"); + Options["fill_density"].enum_values.push_back("5"); + Options["fill_density"].enum_values.push_back("10"); + Options["fill_density"].enum_values.push_back("15"); + Options["fill_density"].enum_values.push_back("20"); + Options["fill_density"].enum_values.push_back("25"); + Options["fill_density"].enum_values.push_back("30"); + Options["fill_density"].enum_values.push_back("40"); + Options["fill_density"].enum_values.push_back("50"); + Options["fill_density"].enum_values.push_back("60"); + Options["fill_density"].enum_values.push_back("70"); + Options["fill_density"].enum_values.push_back("80"); + Options["fill_density"].enum_values.push_back("90"); + Options["fill_density"].enum_values.push_back("100"); + Options["fill_density"].enum_labels.push_back("0%"); + Options["fill_density"].enum_labels.push_back("5%"); + Options["fill_density"].enum_labels.push_back("10%"); + Options["fill_density"].enum_labels.push_back("15%"); + Options["fill_density"].enum_labels.push_back("20%"); + Options["fill_density"].enum_labels.push_back("25%"); + Options["fill_density"].enum_labels.push_back("30%"); + Options["fill_density"].enum_labels.push_back("40%"); + Options["fill_density"].enum_labels.push_back("50%"); + Options["fill_density"].enum_labels.push_back("60%"); + Options["fill_density"].enum_labels.push_back("70%"); + Options["fill_density"].enum_labels.push_back("80%"); + Options["fill_density"].enum_labels.push_back("90%"); + Options["fill_density"].enum_labels.push_back("100%"); + + Options["fill_pattern"].type = coEnum; + Options["fill_pattern"].label = "Fill pattern"; + Options["fill_pattern"].category = "Infill"; + Options["fill_pattern"].tooltip = "Fill pattern for general low-density infill."; + Options["fill_pattern"].cli = "fill-pattern=s"; + Options["fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); + Options["fill_pattern"].enum_values.push_back("rectilinear"); + Options["fill_pattern"].enum_values.push_back("line"); + Options["fill_pattern"].enum_values.push_back("concentric"); + Options["fill_pattern"].enum_values.push_back("honeycomb"); + Options["fill_pattern"].enum_values.push_back("3dhoneycomb"); + Options["fill_pattern"].enum_values.push_back("hilbertcurve"); + Options["fill_pattern"].enum_values.push_back("archimedeanchords"); + Options["fill_pattern"].enum_values.push_back("octagramspiral"); + Options["fill_pattern"].enum_labels.push_back("rectilinear"); + Options["fill_pattern"].enum_labels.push_back("line"); + Options["fill_pattern"].enum_labels.push_back("concentric"); + Options["fill_pattern"].enum_labels.push_back("honeycomb"); + Options["fill_pattern"].enum_labels.push_back("3D honeycomb"); + Options["fill_pattern"].enum_labels.push_back("hilbertcurve (slow)"); + Options["fill_pattern"].enum_labels.push_back("archimedeanchords (slow)"); + Options["fill_pattern"].enum_labels.push_back("octagramspiral (slow)"); + + Options["first_layer_acceleration"].type = coFloat; + Options["first_layer_acceleration"].label = "First layer"; + Options["first_layer_acceleration"].tooltip = "This is the acceleration your printer will use for first layer. Set zero to disable acceleration control for first layer."; + Options["first_layer_acceleration"].sidetext = "mm/s²"; + Options["first_layer_acceleration"].cli = "first-layer-acceleration=f"; + Options["first_layer_acceleration"].min = 0; + + Options["first_layer_bed_temperature"].type = coInt; + Options["first_layer_bed_temperature"].label = "First layer"; + Options["first_layer_bed_temperature"].tooltip = "Heated build plate temperature for the first layer. Set this to zero to disable bed temperature control commands in the output."; + Options["first_layer_bed_temperature"].cli = "first-layer-bed-temperature=i"; + Options["first_layer_bed_temperature"].max = 0; + Options["first_layer_bed_temperature"].max = 300; + + Options["first_layer_extrusion_width"].type = coFloatOrPercent; + Options["first_layer_extrusion_width"].label = "First layer"; + Options["first_layer_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for first layer. You can use this to force fatter extrudates for better adhesion. If expressed as percentage (for example 120%) it will be computed over first layer height."; + Options["first_layer_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["first_layer_extrusion_width"].cli = "first-layer-extrusion-width=s"; + Options["first_layer_extrusion_width"].ratio_over = "first_layer_height"; + + Options["first_layer_height"].type = coFloatOrPercent; + Options["first_layer_height"].label = "First layer height"; + Options["first_layer_height"].category = "Layers and Perimeters"; + Options["first_layer_height"].tooltip = "When printing with very low layer heights, you might still want to print a thicker bottom layer to improve adhesion and tolerance for non perfect build plates. This can be expressed as an absolute value or as a percentage (for example: 150%) over the default layer height."; + Options["first_layer_height"].sidetext = "mm or %"; + Options["first_layer_height"].cli = "first-layer-height=s"; + Options["first_layer_height"].ratio_over = "layer_height"; + + Options["first_layer_speed"].type = coFloatOrPercent; + Options["first_layer_speed"].label = "First layer speed"; + Options["first_layer_speed"].tooltip = "If expressed as absolute value in mm/s, this speed will be applied to all the print moves of the first layer, regardless of their type. If expressed as a percentage (for example: 40%) it will scale the default speeds."; + Options["first_layer_speed"].sidetext = "mm/s or %"; + Options["first_layer_speed"].cli = "first-layer-speed=s"; + + Options["first_layer_temperature"].type = coInts; + Options["first_layer_temperature"].label = "First layer"; + Options["first_layer_temperature"].tooltip = "Extruder temperature for first layer. If you want to control temperature manually during print, set this to zero to disable temperature control commands in the output file."; + Options["first_layer_temperature"].cli = "first-layer-temperature=i@"; + Options["first_layer_temperature"].min = 0; + Options["first_layer_temperature"].max = 400; + + Options["g0"].type = coBool; + Options["g0"].label = "Use G0 for travel moves"; + Options["g0"].tooltip = "Only enable this if your firmware supports G0 properly (thus decouples all axes using their maximum speeds instead of synchronizing them). Travel moves and retractions will be combined in single commands, speeding them print up."; + Options["g0"].cli = "g0!"; + + Options["gap_fill_speed"].type = coFloat; + Options["gap_fill_speed"].label = "Gap fill"; + Options["gap_fill_speed"].category = "Speed"; + Options["gap_fill_speed"].tooltip = "Speed for filling small gaps using short zigzag moves. Keep this reasonably low to avoid too much shaking and resonance issues. Set zero to disable gaps filling."; + Options["gap_fill_speed"].sidetext = "mm/s"; + Options["gap_fill_speed"].cli = "gap-fill-speed=f"; + Options["gap_fill_speed"].min = 0; + + Options["gcode_arcs"].type = coBool; + Options["gcode_arcs"].label = "Use native G-code arcs"; + Options["gcode_arcs"].tooltip = "This experimental feature tries to detect arcs from segments and generates G2/G3 arc commands instead of multiple straight G1 commands."; + Options["gcode_arcs"].cli = "gcode-arcs!"; + + Options["gcode_comments"].type = coBool; + Options["gcode_comments"].label = "Verbose G-code"; + Options["gcode_comments"].tooltip = "Enable this to get a commented G-code file, with each line explained by a descriptive text. If you print from SD card, the additional weight of the file could make your firmware slow down."; + Options["gcode_comments"].cli = "gcode-comments!"; + + Options["gcode_flavor"].type = coEnum; + Options["gcode_flavor"].label = "G-code flavor"; + Options["gcode_flavor"].tooltip = "Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer's firmware to get a compatible output. The \"No extrusion\" flavor prevents Slic3r from exporting any extrusion value at all."; + Options["gcode_flavor"].cli = "gcode-flavor=s"; + Options["gcode_flavor"].enum_keys_map = ConfigOptionEnum::get_enum_values(); + Options["gcode_flavor"].enum_values.push_back("reprap"); + Options["gcode_flavor"].enum_values.push_back("teacup"); + Options["gcode_flavor"].enum_values.push_back("makerware"); + Options["gcode_flavor"].enum_values.push_back("sailfish"); + Options["gcode_flavor"].enum_values.push_back("mach3"); + Options["gcode_flavor"].enum_values.push_back("no-extrusion"); + Options["gcode_flavor"].enum_labels.push_back("RepRap (Marlin/Sprinter/Repetier)"); + Options["gcode_flavor"].enum_labels.push_back("Teacup"); + Options["gcode_flavor"].enum_labels.push_back("MakerWare (MakerBot)"); + Options["gcode_flavor"].enum_labels.push_back("Sailfish (MakerBot)"); + Options["gcode_flavor"].enum_labels.push_back("Mach3/LinuxCNC"); + Options["gcode_flavor"].enum_labels.push_back("No extrusion"); + + Options["infill_acceleration"].type = coFloat; + Options["infill_acceleration"].label = "Infill"; + Options["infill_acceleration"].tooltip = "This is the acceleration your printer will use for infill. Set zero to disable acceleration control for infill."; + Options["infill_acceleration"].sidetext = "mm/s²"; + Options["infill_acceleration"].cli = "infill-acceleration=f"; + Options["infill_acceleration"].min = 0; + + Options["infill_every_layers"].type = coInt; + Options["infill_every_layers"].label = "Combine infill every"; + Options["infill_every_layers"].category = "Infill"; + Options["infill_every_layers"].tooltip = "This feature allows to combine infill and speed up your print by extruding thicker infill layers while preserving thin perimeters, thus accuracy."; + Options["infill_every_layers"].sidetext = "layers"; + Options["infill_every_layers"].cli = "infill-every-layers=i"; + Options["infill_every_layers"].full_label = "Combine infill every n layers"; + Options["infill_every_layers"].min = 1; + + Options["infill_extruder"].type = coInt; + Options["infill_extruder"].label = "Infill extruder"; + Options["infill_extruder"].category = "Extruders"; + Options["infill_extruder"].tooltip = "The extruder to use when printing infill."; + Options["infill_extruder"].cli = "infill-extruder=i"; + Options["infill_extruder"].min = 1; + + Options["infill_extrusion_width"].type = coFloatOrPercent; + Options["infill_extrusion_width"].label = "Infill"; + Options["infill_extrusion_width"].category = "Extrusion Width"; + Options["infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill. You may want to use fatter extrudates to speed up the infill and make your parts stronger. If expressed as percentage (for example 90%) it will be computed over layer height."; + Options["infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["infill_extrusion_width"].cli = "infill-extrusion-width=s"; + + Options["infill_first"].type = coBool; + Options["infill_first"].label = "Infill before perimeters"; + Options["infill_first"].tooltip = "This option will switch the print order of perimeters and infill, making the latter first."; + Options["infill_first"].cli = "infill-first!"; + + Options["infill_only_where_needed"].type = coBool; + Options["infill_only_where_needed"].label = "Only infill where needed"; + Options["infill_only_where_needed"].category = "Infill"; + Options["infill_only_where_needed"].tooltip = "This option will limit infill to the areas actually needed for supporting ceilings (it will act as internal support material)."; + Options["infill_only_where_needed"].cli = "infill-only-where-needed!"; + + Options["infill_speed"].type = coFloat; + Options["infill_speed"].label = "Infill"; + Options["infill_speed"].category = "Speed"; + Options["infill_speed"].tooltip = "Speed for printing the internal fill."; + Options["infill_speed"].sidetext = "mm/s"; + Options["infill_speed"].cli = "infill-speed=f"; + Options["infill_speed"].aliases.push_back("print_feed_rate"); + Options["infill_speed"].aliases.push_back("infill_feed_rate"); + Options["infill_speed"].min = 0; + + Options["interface_shells"].type = coBool; + Options["interface_shells"].label = "Interface shells"; + Options["interface_shells"].tooltip = "Force the generation of solid shells between adjacent materials/volumes. Useful for multi-extruder prints with translucent materials or manual soluble support material."; + Options["interface_shells"].cli = "interface-shells!"; + Options["interface_shells"].category = "Layers and Perimeters"; + + Options["layer_gcode"].type = coString; + Options["layer_gcode"].label = "Layer change G-code"; + Options["layer_gcode"].tooltip = "This custom code is inserted at every layer change, right after the Z move and before the extruder moves to the first layer point. Note that you can use placeholder variables for all Slic3r settings."; + Options["layer_gcode"].cli = "layer-gcode=s"; + Options["layer_gcode"].multiline = true; + Options["layer_gcode"].full_width = true; + Options["layer_gcode"].height = 50; + + Options["layer_height"].type = coFloat; + Options["layer_height"].label = "Layer height"; + Options["layer_height"].category = "Layers and Perimeters"; + Options["layer_height"].tooltip = "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."; + Options["layer_height"].sidetext = "mm"; + Options["layer_height"].cli = "layer-height=f"; + Options["layer_height"].min = 0; + + Options["max_fan_speed"].type = coInt; + Options["max_fan_speed"].label = "Max"; + Options["max_fan_speed"].tooltip = "This setting represents the maximum speed of your fan."; + Options["max_fan_speed"].sidetext = "%"; + Options["max_fan_speed"].cli = "max-fan-speed=i"; + Options["max_fan_speed"].min = 0; + Options["max_fan_speed"].max = 100; + + Options["min_fan_speed"].type = coInt; + Options["min_fan_speed"].label = "Min"; + Options["min_fan_speed"].tooltip = "This setting represents the minimum PWM your fan needs to work."; + Options["min_fan_speed"].sidetext = "%"; + Options["min_fan_speed"].cli = "min-fan-speed=i"; + Options["min_fan_speed"].min = 0; + Options["min_fan_speed"].max = 100; + + Options["min_print_speed"].type = coInt; + Options["min_print_speed"].label = "Min print speed"; + Options["min_print_speed"].tooltip = "Slic3r will not scale speed down below this speed."; + Options["min_print_speed"].sidetext = "mm/s"; + Options["min_print_speed"].cli = "min-print-speed=f"; + Options["min_print_speed"].min = 0; + Options["min_print_speed"].max = 1000; + + Options["min_skirt_length"].type = coFloat; + Options["min_skirt_length"].label = "Minimum extrusion length"; + Options["min_skirt_length"].tooltip = "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."; + Options["min_skirt_length"].sidetext = "mm"; + Options["min_skirt_length"].cli = "min-skirt-length=f"; + Options["min_skirt_length"].min = 0; + + Options["notes"].type = coString; + Options["notes"].label = "Configuration notes"; + Options["notes"].tooltip = "You can put here your personal notes. This text will be added to the G-code header comments."; + Options["notes"].cli = "notes=s"; + Options["notes"].multiline = true; + Options["notes"].full_width = true; + Options["notes"].height = 130; + + Options["nozzle_diameter"].type = coFloats; + Options["nozzle_diameter"].label = "Nozzle diameter"; + Options["nozzle_diameter"].tooltip = "This is the diameter of your extruder nozzle (for example: 0.5, 0.35 etc.)"; + Options["nozzle_diameter"].sidetext = "mm"; + Options["nozzle_diameter"].cli = "nozzle-diameter=f@"; + + Options["only_retract_when_crossing_perimeters"].type = coBool; + Options["only_retract_when_crossing_perimeters"].label = "Only retract when crossing perimeters"; + Options["only_retract_when_crossing_perimeters"].tooltip = "Disables retraction when the travel path does not exceed the upper layer's perimeters (and thus any ooze will be probably invisible)."; + Options["only_retract_when_crossing_perimeters"].cli = "only-retract-when-crossing-perimeters!"; + + Options["ooze_prevention"].type = coBool; + Options["ooze_prevention"].label = "Enable"; + Options["ooze_prevention"].tooltip = "This option will drop the temperature of the inactive extruders to prevent oozing. It will enable a tall skirt automatically and move extruders outside such skirt when changing temperatures."; + Options["ooze_prevention"].cli = "ooze-prevention!"; + + Options["output_filename_format"].type = coString; + Options["output_filename_format"].label = "Output filename format"; + Options["output_filename_format"].tooltip = "You can use all configuration options as variables inside this template. For example: [layer_height], [fill_density] etc. You can also use [timestamp], [year], [month], [day], [hour], [minute], [second], [version], [input_filename], [input_filename_base]."; + Options["output_filename_format"].cli = "output-filename-format=s"; + Options["output_filename_format"].full_width = true; + + Options["overhangs"].type = coBool; + Options["overhangs"].label = "Detect bridging perimeters"; + Options["overhangs"].category = "Layers and Perimeters"; + Options["overhangs"].tooltip = "Experimental option to adjust flow for overhangs (bridge flow will be used), to apply bridge speed to them and enable fan."; + Options["overhangs"].cli = "overhangs!"; + + Options["perimeter_acceleration"].type = coFloat; + Options["perimeter_acceleration"].label = "Perimeters"; + Options["perimeter_acceleration"].tooltip = "This is the acceleration your printer will use for perimeters. A high value like 9000 usually gives good results if your hardware is up to the job. Set zero to disable acceleration control for perimeters."; + Options["perimeter_acceleration"].sidetext = "mm/s²"; + Options["perimeter_acceleration"].cli = "perimeter-acceleration=f"; + + Options["perimeter_extruder"].type = coInt; + Options["perimeter_extruder"].label = "Perimeter extruder"; + Options["perimeter_extruder"].category = "Extruders"; + Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters. First extruder is 1."; + Options["perimeter_extruder"].cli = "perimeter-extruder=i"; + Options["perimeter_extruder"].aliases.push_back("perimeters_extruder"); + Options["perimeter_extruder"].min = 1; + + Options["perimeter_extrusion_width"].type = coFloatOrPercent; + Options["perimeter_extrusion_width"].label = "Perimeters"; + Options["perimeter_extrusion_width"].category = "Extrusion Width"; + Options["perimeter_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for perimeters. You may want to use thinner extrudates to get more accurate surfaces. If expressed as percentage (for example 200%) it will be computed over layer height."; + Options["perimeter_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["perimeter_extrusion_width"].cli = "perimeter-extrusion-width=s"; + Options["perimeter_extrusion_width"].aliases.push_back("perimeters_extrusion_width"); + + Options["perimeter_speed"].type = coFloat; + Options["perimeter_speed"].label = "Perimeters"; + Options["perimeter_speed"].category = "Speed"; + Options["perimeter_speed"].tooltip = "Speed for perimeters (contours, aka vertical shells)."; + Options["perimeter_speed"].sidetext = "mm/s"; + Options["perimeter_speed"].cli = "perimeter-speed=f"; + Options["perimeter_speed"].aliases.push_back("perimeter_feed_rate"); + Options["perimeter_speed"].min = 0; + + Options["perimeters"].type = coInt; + Options["perimeters"].label = "Perimeters (minimum)"; + Options["perimeters"].category = "Layers and Perimeters"; + Options["perimeters"].tooltip = "This option sets the number of perimeters to generate for each layer. Note that Slic3r may increase this number automatically when it detects sloping surfaces which benefit from a higher number of perimeters if the Extra Perimeters option is enabled."; + Options["perimeters"].cli = "perimeters=i"; + Options["perimeters"].aliases.push_back("perimeter_offsets"); + Options["perimeters"].min = 0; + + Options["post_process"].type = coStrings; + Options["post_process"].label = "Post-processing scripts"; + Options["post_process"].tooltip = "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."; + Options["post_process"].cli = "post-process=s@"; + Options["post_process"].gui_flags = "serialized"; + Options["post_process"].multiline = true; + Options["post_process"].full_width = true; + Options["post_process"].height = 60; + + Options["raft_layers"].type = coInt; + Options["raft_layers"].label = "Raft layers"; + Options["raft_layers"].category = "Support material"; + Options["raft_layers"].tooltip = "The object will be raised by this number of layers, and support material will be generated under it."; + Options["raft_layers"].sidetext = "layers"; + Options["raft_layers"].cli = "raft-layers=i"; + Options["raft_layers"].min = 0; + + Options["resolution"].type = coFloat; + Options["resolution"].label = "Resolution"; + Options["resolution"].tooltip = "Minimum detail resolution, used to simplify the input file for speeding up the slicing job and reducing memory usage. High-resolution models often carry more detail than printers can render. Set to zero to disable any simplification and use full resolution from input."; + Options["resolution"].sidetext = "mm"; + Options["resolution"].cli = "resolution=f"; + Options["resolution"].min = 0; + + Options["retract_before_travel"].type = coFloats; + Options["retract_before_travel"].label = "Minimum travel after retraction"; + Options["retract_before_travel"].tooltip = "Retraction is not triggered when travel moves are shorter than this length."; + Options["retract_before_travel"].sidetext = "mm"; + Options["retract_before_travel"].cli = "retract-before-travel=f@"; + + Options["retract_layer_change"].type = coBools; + Options["retract_layer_change"].label = "Retract on layer change"; + Options["retract_layer_change"].tooltip = "This flag enforces a retraction whenever a Z move is done."; + Options["retract_layer_change"].cli = "retract-layer-change!"; + + Options["retract_length"].type = coFloats; + Options["retract_length"].label = "Length"; + Options["retract_length"].tooltip = "When retraction is triggered, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; + Options["retract_length"].sidetext = "mm (zero to disable)"; + Options["retract_length"].cli = "retract-length=f@"; + + Options["retract_length_toolchange"].type = coFloats; + Options["retract_length_toolchange"].label = "Length"; + Options["retract_length_toolchange"].tooltip = "When retraction is triggered before changing tool, filament is pulled back by the specified amount (the length is measured on raw filament, before it enters the extruder)."; + Options["retract_length_toolchange"].sidetext = "mm (zero to disable)"; + Options["retract_length_toolchange"].cli = "retract-length-toolchange=f@"; + + Options["retract_lift"].type = coFloats; + Options["retract_lift"].label = "Lift Z"; + Options["retract_lift"].tooltip = "If you set this to a positive value, Z is quickly raised every time a retraction is triggered. When using multiple extruders, only the setting for the first extruder will be considered."; + Options["retract_lift"].sidetext = "mm"; + Options["retract_lift"].cli = "retract-lift=f@"; + + Options["retract_restart_extra"].type = coFloats; + Options["retract_restart_extra"].label = "Extra length on restart"; + Options["retract_restart_extra"].tooltip = "When the retraction is compensated after the travel move, the extruder will push this additional amount of filament. This setting is rarely needed."; + Options["retract_restart_extra"].sidetext = "mm"; + Options["retract_restart_extra"].cli = "retract-restart-extra=f@"; + + Options["retract_restart_extra_toolchange"].type = coFloats; + Options["retract_restart_extra_toolchange"].label = "Extra length on restart"; + Options["retract_restart_extra_toolchange"].tooltip = "When the retraction is compensated after changing tool, the extruder will push this additional amount of filament."; + Options["retract_restart_extra_toolchange"].sidetext = "mm"; + Options["retract_restart_extra_toolchange"].cli = "retract-restart-extra-toolchange=f@"; + + Options["retract_speed"].type = coInts; + Options["retract_speed"].label = "Speed"; + Options["retract_speed"].tooltip = "The speed for retractions (it only applies to the extruder motor)."; + Options["retract_speed"].sidetext = "mm/s"; + Options["retract_speed"].cli = "retract-speed=f@"; + Options["retract_speed"].max = 1000; + + Options["seam_position"].type = coEnum; + Options["seam_position"].label = "Seam position"; + Options["seam_position"].category = "Layers and perimeters"; + Options["seam_position"].tooltip = "Position of perimeters starting points."; + Options["seam_position"].cli = "seam-position=s"; + Options["seam_position"].enum_keys_map = ConfigOptionEnum::get_enum_values(); + Options["seam_position"].enum_values.push_back("random"); + Options["seam_position"].enum_values.push_back("nearest"); + Options["seam_position"].enum_values.push_back("aligned"); + Options["seam_position"].enum_labels.push_back("Random"); + Options["seam_position"].enum_labels.push_back("Nearest"); + Options["seam_position"].enum_labels.push_back("Aligned"); + + Options["skirt_distance"].type = coFloat; + Options["skirt_distance"].label = "Distance from object"; + Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion."; + Options["skirt_distance"].sidetext = "mm"; + Options["skirt_distance"].cli = "skirt-distance=f"; + Options["skirt_distance"].min = 0; + + Options["skirt_height"].type = coInt; + Options["skirt_height"].label = "Skirt height"; + Options["skirt_height"].tooltip = "Height of skirt expressed in layers. Set this to a tall value to use skirt as a shield against drafts."; + Options["skirt_height"].sidetext = "layers"; + Options["skirt_height"].cli = "skirt-height=i"; + + Options["skirts"].type = coInt; + Options["skirts"].label = "Loops"; + Options["skirts"].tooltip = "Number of loops for this skirt, in other words its thickness. Set this to zero to disable skirt."; + Options["skirts"].cli = "skirts=i"; + Options["skirts"].min = 0; + + Options["slowdown_below_layer_time"].type = coInt; + Options["slowdown_below_layer_time"].label = "Slow down if layer print time is below"; + Options["slowdown_below_layer_time"].tooltip = "If layer print time is estimated below this number of seconds, print moves speed will be scaled down to extend duration to this value."; + Options["slowdown_below_layer_time"].sidetext = "approximate seconds"; + Options["slowdown_below_layer_time"].cli = "slowdown-below-layer-time=i"; + Options["slowdown_below_layer_time"].width = 60; + Options["slowdown_below_layer_time"].min = 0; + Options["slowdown_below_layer_time"].max = 1000; + + Options["small_perimeter_speed"].type = coFloatOrPercent; + Options["small_perimeter_speed"].label = "Small perimeters"; + Options["small_perimeter_speed"].category = "Speed"; + Options["small_perimeter_speed"].tooltip = "This separate setting will affect the speed of perimeters having radius <= 6.5mm (usually holes). If expressed as percentage (for example: 80%) it will be calculated on the perimeters speed setting above."; + Options["small_perimeter_speed"].sidetext = "mm/s or %"; + Options["small_perimeter_speed"].cli = "small-perimeter-speed=s"; + Options["small_perimeter_speed"].ratio_over = "perimeter_speed"; + + Options["solid_fill_pattern"].type = coEnum; + Options["solid_fill_pattern"].label = "Top/bottom fill pattern"; + Options["solid_fill_pattern"].category = "Infill"; + Options["solid_fill_pattern"].tooltip = "Fill pattern for top/bottom infill."; + Options["solid_fill_pattern"].cli = "solid-fill-pattern=s"; + Options["solid_fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); + Options["solid_fill_pattern"].enum_values.push_back("rectilinear"); + Options["solid_fill_pattern"].enum_values.push_back("concentric"); + Options["solid_fill_pattern"].enum_values.push_back("hilbertcurve"); + Options["solid_fill_pattern"].enum_values.push_back("archimedeanchords"); + Options["solid_fill_pattern"].enum_values.push_back("octagramspiral"); + Options["solid_fill_pattern"].enum_labels.push_back("rectilinear"); + Options["solid_fill_pattern"].enum_labels.push_back("concentric"); + Options["solid_fill_pattern"].enum_labels.push_back("hilbertcurve (slow)"); + Options["solid_fill_pattern"].enum_labels.push_back("archimedeanchords (slow)"); + Options["solid_fill_pattern"].enum_labels.push_back("octagramspiral (slow)"); + + Options["solid_infill_below_area"].type = coFloat; + Options["solid_infill_below_area"].label = "Solid infill threshold area"; + Options["solid_infill_below_area"].category = "Infill"; + Options["solid_infill_below_area"].tooltip = "Force solid infill for regions having a smaller area than the specified threshold."; + Options["solid_infill_below_area"].sidetext = "mm²"; + Options["solid_infill_below_area"].cli = "solid-infill-below-area=f"; + Options["solid_infill_below_area"].min = 0; + + Options["solid_infill_every_layers"].type = coInt; + Options["solid_infill_every_layers"].label = "Solid infill every"; + Options["solid_infill_every_layers"].category = "Infill"; + Options["solid_infill_every_layers"].tooltip = "This feature allows to force a solid layer every given number of layers. Zero to disable. You can set this to any value (for example 9999); Slic3r will automatically choose the maximum possible number of layers to combine according to nozzle diameter and layer height."; + Options["solid_infill_every_layers"].sidetext = "layers"; + Options["solid_infill_every_layers"].cli = "solid-infill-every-layers=i"; + Options["solid_infill_every_layers"].min = 0; + + Options["solid_infill_extrusion_width"].type = coFloatOrPercent; + Options["solid_infill_extrusion_width"].label = "Solid infill"; + Options["solid_infill_extrusion_width"].category = "Extrusion Width"; + Options["solid_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for solid surfaces. If expressed as percentage (for example 90%) it will be computed over layer height."; + Options["solid_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["solid_infill_extrusion_width"].cli = "solid-infill-extrusion-width=s"; + + Options["solid_infill_speed"].type = coFloatOrPercent; + Options["solid_infill_speed"].label = "Solid infill"; + Options["solid_infill_speed"].category = "Speed"; + Options["solid_infill_speed"].tooltip = "Speed for printing solid regions (top/bottom/internal horizontal shells). This can be expressed as a percentage (for example: 80%) over the default infill speed above."; + Options["solid_infill_speed"].sidetext = "mm/s or %"; + Options["solid_infill_speed"].cli = "solid-infill-speed=s"; + Options["solid_infill_speed"].ratio_over = "infill_speed"; + Options["solid_infill_speed"].aliases.push_back("solid_infill_feed_rate"); + + Options["solid_layers"].type = coInt; + Options["solid_layers"].label = "Solid layers"; + Options["solid_layers"].tooltip = "Number of solid layers to generate on top and bottom surfaces."; + Options["solid_layers"].cli = "solid-layers=i"; + Options["solid_layers"].shortcut.push_back("top_solid_layers"); + Options["solid_layers"].shortcut.push_back("bottom_solid_layers"); + Options["solid_layers"].min = 0; + + Options["spiral_vase"].type = coBool; + Options["spiral_vase"].label = "Spiral vase"; + Options["spiral_vase"].tooltip = "This feature will raise Z gradually while printing a single-walled object in order to remove any visible seam. This option requires a single perimeter, no infill, no top solid layers and no support material. You can still set any number of bottom solid layers as well as skirt/brim loops. It won't work when printing more than an object."; + Options["spiral_vase"].cli = "spiral-vase!"; + + Options["standby_temperature_delta"].type = coInt; + Options["standby_temperature_delta"].label = "Temperature variation"; + Options["standby_temperature_delta"].tooltip = "Temperature difference to be applied when an extruder is not active."; + Options["standby_temperature_delta"].sidetext = "∆°C"; + Options["standby_temperature_delta"].cli = "standby-temperature-delta=i"; + Options["standby_temperature_delta"].min = -400; + Options["standby_temperature_delta"].max = 400; + + Options["start_gcode"].type = coString; + Options["start_gcode"].label = "Start G-code"; + Options["start_gcode"].tooltip = "This start procedure is inserted at the beginning, after bed has reached the target temperature and extruder just started heating, and before extruder has finished heating. If Slic3r detects M104 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all Slic3r settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want."; + Options["start_gcode"].cli = "start-gcode=s"; + Options["start_gcode"].multiline = true; + Options["start_gcode"].full_width = true; + Options["start_gcode"].height = 120; + + Options["support_material"].type = coBool; + Options["support_material"].label = "Generate support material"; + Options["support_material"].category = "Support material"; + Options["support_material"].tooltip = "Enable support material generation."; + Options["support_material"].cli = "support-material!"; + + Options["support_material_angle"].type = coInt; + Options["support_material_angle"].label = "Pattern angle"; + Options["support_material_angle"].category = "Support material"; + Options["support_material_angle"].tooltip = "Use this setting to rotate the support material pattern on the horizontal plane."; + Options["support_material_angle"].sidetext = "°"; + Options["support_material_angle"].cli = "support-material-angle=i"; + Options["support_material_angle"].min = 0; + Options["support_material_angle"].max = 359; + + Options["support_material_enforce_layers"].type = coInt; + Options["support_material_enforce_layers"].label = "Enforce support for the first"; + Options["support_material_enforce_layers"].category = "Support material"; + Options["support_material_enforce_layers"].tooltip = "Generate support material for the specified number of layers counting from bottom, regardless of whether normal support material is enabled or not and regardless of any angle threshold. This is useful for getting more adhesion of objects having a very thin or poor footprint on the build plate."; + Options["support_material_enforce_layers"].sidetext = "layers"; + Options["support_material_enforce_layers"].cli = "support-material-enforce-layers=f"; + Options["support_material_enforce_layers"].full_label = "Enforce support for the first n layers"; + Options["support_material_enforce_layers"].min = 0; + + Options["support_material_extruder"].type = coInt; + Options["support_material_extruder"].label = "Support material extruder"; + Options["support_material_extruder"].category = "Extruders"; + Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too."; + Options["support_material_extruder"].cli = "support-material-extruder=i"; + Options["support_material_extruder"].min = 1; + + Options["support_material_extrusion_width"].type = coFloatOrPercent; + Options["support_material_extrusion_width"].label = "Support material"; + Options["support_material_extrusion_width"].category = "Extrusion Width"; + Options["support_material_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for support material. If expressed as percentage (for example 90%) it will be computed over layer height."; + Options["support_material_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["support_material_extrusion_width"].cli = "support-material-extrusion-width=s"; + + Options["support_material_interface_extruder"].type = coInt; + Options["support_material_interface_extruder"].label = "Support material interface extruder"; + Options["support_material_interface_extruder"].category = "Extruders"; + Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too."; + Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i"; + Options["support_material_interface_extruder"].min = 1; + + Options["support_material_interface_layers"].type = coInt; + Options["support_material_interface_layers"].label = "Interface layers"; + Options["support_material_interface_layers"].category = "Support material"; + Options["support_material_interface_layers"].tooltip = "Number of interface layers to insert between the object(s) and support material."; + Options["support_material_interface_layers"].sidetext = "layers"; + Options["support_material_interface_layers"].cli = "support-material-interface-layers=i"; + Options["support_material_interface_layers"].min = 0; + + Options["support_material_interface_spacing"].type = coFloat; + Options["support_material_interface_spacing"].label = "Interface pattern spacing"; + Options["support_material_interface_spacing"].category = "Support material"; + Options["support_material_interface_spacing"].tooltip = "Spacing between interface lines. Set zero to get a solid interface."; + Options["support_material_interface_spacing"].sidetext = "mm"; + Options["support_material_interface_spacing"].cli = "support-material-interface-spacing=f"; + Options["support_material_interface_spacing"].min = 0; + + Options["support_material_interface_speed"].type = coFloatOrPercent; + Options["support_material_interface_speed"].label = "Support material interface"; + Options["support_material_interface_speed"].category = "Support material"; + Options["support_material_interface_speed"].tooltip = "Speed for printing support material interface layers. If expressed as percentage (for example 50%) it will be calculated over support material speed."; + Options["support_material_interface_speed"].sidetext = "mm/s or %"; + Options["support_material_interface_speed"].cli = "support-material-interface-speed=s"; + Options["support_material_interface_speed"].ratio_over = "support_material_speed"; + + Options["support_material_pattern"].type = coEnum; + Options["support_material_pattern"].label = "Pattern"; + Options["support_material_pattern"].category = "Support material"; + Options["support_material_pattern"].tooltip = "Pattern used to generate support material."; + Options["support_material_pattern"].cli = "support-material-pattern=s"; + Options["support_material_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); + Options["support_material_pattern"].enum_values.push_back("rectilinear"); + Options["support_material_pattern"].enum_values.push_back("rectilinear-grid"); + Options["support_material_pattern"].enum_values.push_back("honeycomb"); + Options["support_material_pattern"].enum_values.push_back("pillars"); + Options["support_material_pattern"].enum_labels.push_back("rectilinear"); + Options["support_material_pattern"].enum_labels.push_back("rectilinear grid"); + Options["support_material_pattern"].enum_labels.push_back("honeycomb"); + Options["support_material_pattern"].enum_labels.push_back("pillars"); + + Options["support_material_spacing"].type = coFloat; + Options["support_material_spacing"].label = "Pattern spacing"; + Options["support_material_spacing"].category = "Support material"; + Options["support_material_spacing"].tooltip = "Spacing between support material lines."; + Options["support_material_spacing"].sidetext = "mm"; + Options["support_material_spacing"].cli = "support-material-spacing=f"; + Options["support_material_spacing"].min = 0; + + Options["support_material_speed"].type = coFloat; + Options["support_material_speed"].label = "Support material"; + Options["support_material_speed"].category = "Support material"; + Options["support_material_speed"].tooltip = "Speed for printing support material."; + Options["support_material_speed"].sidetext = "mm/s"; + Options["support_material_speed"].cli = "support-material-speed=f"; + Options["support_material_speed"].min = 0; + + Options["support_material_threshold"].type = coInt; + Options["support_material_threshold"].label = "Overhang threshold"; + Options["support_material_threshold"].category = "Support material"; + Options["support_material_threshold"].tooltip = "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represent the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended)."; + Options["support_material_threshold"].sidetext = "°"; + Options["support_material_threshold"].cli = "support-material-threshold=i"; + Options["support_material_threshold"].min = 0; + Options["support_material_threshold"].max = 90; + + Options["temperature"].type = coInts; + Options["temperature"].label = "Other layers"; + Options["temperature"].tooltip = "Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output."; + Options["temperature"].cli = "temperature=i@"; + Options["temperature"].full_label = "Temperature"; + Options["temperature"].max = 0; + Options["temperature"].max = 400; + + Options["thin_walls"].type = coBool; + Options["thin_walls"].label = "Detect thin walls"; + Options["thin_walls"].category = "Layers and Perimeters"; + Options["thin_walls"].tooltip = "Detect single-width walls (parts where two extrusions don't fit and we need to collapse them into a single trace)."; + Options["thin_walls"].cli = "thin-walls!"; + + Options["threads"].type = coInt; + Options["threads"].label = "Threads"; + Options["threads"].tooltip = "Threads are used to parallelize long-running tasks. Optimal threads number is slightly above the number of available cores/processors. Beware that more threads consume more memory."; + Options["threads"].sidetext = "(more speed but more memory usage)"; + Options["threads"].cli = "threads|j=i"; + Options["threads"].readonly = true; + Options["threads"].min = 1; + Options["threads"].max = 16; + + Options["toolchange_gcode"].type = coString; + Options["toolchange_gcode"].label = "Tool change G-code"; + Options["toolchange_gcode"].tooltip = "This custom code is inserted at every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; + Options["toolchange_gcode"].cli = "toolchange-gcode=s"; + Options["toolchange_gcode"].multiline = true; + Options["toolchange_gcode"].full_width = true; + Options["toolchange_gcode"].height = 50; + + Options["top_infill_extrusion_width"].type = coFloatOrPercent; + Options["top_infill_extrusion_width"].label = "Top solid infill"; + Options["top_infill_extrusion_width"].category = "Extrusion Width"; + Options["top_infill_extrusion_width"].tooltip = "Set this to a non-zero value to set a manual extrusion width for infill for top surfaces. You may want to use thinner extrudates to fill all narrow regions and get a smoother finish. If expressed as percentage (for example 90%) it will be computed over layer height."; + Options["top_infill_extrusion_width"].sidetext = "mm or % (leave 0 for default)"; + Options["top_infill_extrusion_width"].cli = "top-infill-extrusion-width=s"; + + Options["top_solid_infill_speed"].type = coFloatOrPercent; + Options["top_solid_infill_speed"].label = "Top solid infill"; + Options["top_solid_infill_speed"].category = "Speed"; + Options["top_solid_infill_speed"].tooltip = "Speed for printing top solid layers (it only applies to the uppermost external layers and not to their internal solid layers). You may want to slow down this to get a nicer surface finish. This can be expressed as a percentage (for example: 80%) over the solid infill speed above."; + Options["top_solid_infill_speed"].sidetext = "mm/s or %"; + Options["top_solid_infill_speed"].cli = "top-solid-infill-speed=s"; + Options["top_solid_infill_speed"].ratio_over = "solid_infill_speed"; + + Options["top_solid_layers"].type = coInt; + Options["top_solid_layers"].label = "Top"; + Options["top_solid_layers"].category = "Layers and Perimeters"; + Options["top_solid_layers"].tooltip = "Number of solid layers to generate on top surfaces."; + Options["top_solid_layers"].cli = "top-solid-layers=i"; + Options["top_solid_layers"].full_label = "Top solid layers"; + Options["top_solid_layers"].min = 0; + + Options["travel_speed"].type = coFloat; + Options["travel_speed"].label = "Travel"; + Options["travel_speed"].tooltip = "Speed for travel moves (jumps between distant extrusion points)."; + Options["travel_speed"].sidetext = "mm/s"; + Options["travel_speed"].cli = "travel-speed=f"; + Options["travel_speed"].aliases.push_back("travel_feed_rate"); + Options["travel_speed"].min = 0; + + Options["use_firmware_retraction"].type = coBool; + Options["use_firmware_retraction"].label = "Use firmware retraction"; + Options["use_firmware_retraction"].tooltip = "This experimental setting uses G10 and G11 commands to have the firmware handle the retraction. This is only supported in recent Marlin."; + Options["use_firmware_retraction"].cli = "use-firmware-retraction!"; + + Options["use_relative_e_distances"].type = coBool; + Options["use_relative_e_distances"].label = "Use relative E distances"; + Options["use_relative_e_distances"].tooltip = "If your firmware requires relative E values, check this, otherwise leave it unchecked. Most firmwares use absolute values."; + Options["use_relative_e_distances"].cli = "use-relative-e-distances!"; + + Options["vibration_limit"].type = coFloat; + Options["vibration_limit"].label = "Vibration limit"; + Options["vibration_limit"].tooltip = "This experimental option will slow down those moves hitting the configured frequency limit. The purpose of limiting vibrations is to avoid mechanical resonance. Set zero to disable."; + Options["vibration_limit"].sidetext = "Hz"; + Options["vibration_limit"].cli = "vibration-limit=f"; + Options["vibration_limit"].min = 0; + + Options["wipe"].type = coBools; + Options["wipe"].label = "Wipe while retracting"; + Options["wipe"].tooltip = "This flag will move the nozzle while retracting to minimize the possible blob on leaky extruders."; + Options["wipe"].cli = "wipe!"; + + Options["xy_size_compensation"].type = coFloat; + Options["xy_size_compensation"].label = "XY Size Compensation"; + Options["xy_size_compensation"].category = "Advanced"; + Options["xy_size_compensation"].tooltip = "The object will be grown/shrunk in the XY plane by the configured value (negative = inwards, positive = outwards). This might be useful for fine-tuning hole sizes."; + Options["xy_size_compensation"].sidetext = "mm"; + Options["xy_size_compensation"].cli = "xy-size-compensation=f"; + + Options["z_offset"].type = coFloat; + Options["z_offset"].label = "Z offset"; + Options["z_offset"].tooltip = "This value will be added (or subtracted) from all the Z coordinates in the output G-code. It is used to compensate for bad Z endstop position: for example, if your endstop zero actually leaves the nozzle 0.3mm far from the print bed, set this to -0.3 (or fix your endstop)."; + Options["z_offset"].sidetext = "mm"; + Options["z_offset"].cli = "z-offset=f"; + + return Options; +}; + +t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def(); + +#ifdef SLIC3RXS +REGISTER_CLASS(DynamicPrintConfig, "Config"); +REGISTER_CLASS(PrintObjectConfig, "Config::PrintObject"); +REGISTER_CLASS(PrintRegionConfig, "Config::PrintRegion"); +REGISTER_CLASS(PrintConfig, "Config::Print"); +REGISTER_CLASS(FullPrintConfig, "Config::Full"); +#endif + +} diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp new file mode 100644 index 000000000..cdc3d7173 --- /dev/null +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -0,0 +1,567 @@ +#ifndef slic3r_PrintConfig_hpp_ +#define slic3r_PrintConfig_hpp_ + +#include "Config.hpp" + +namespace Slic3r { + +enum GCodeFlavor { + gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfNoExtrusion, +}; + +enum InfillPattern { + ipRectilinear, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, + ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, +}; + +enum SupportMaterialPattern { + smpRectilinear, smpRectilinearGrid, smpHoneycomb, smpPillars, +}; + +enum SeamPosition { + spRandom, spNearest, spAligned +}; + +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["reprap"] = gcfRepRap; + keys_map["teacup"] = gcfTeacup; + keys_map["makerware"] = gcfMakerWare; + keys_map["sailfish"] = gcfSailfish; + keys_map["mach3"] = gcfMach3; + keys_map["no-extrusion"] = gcfNoExtrusion; + return keys_map; +} + +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["rectilinear"] = ipRectilinear; + keys_map["line"] = ipLine; + keys_map["concentric"] = ipConcentric; + keys_map["honeycomb"] = ipHoneycomb; + keys_map["3dhoneycomb"] = ip3DHoneycomb; + keys_map["hilbertcurve"] = ipHilbertCurve; + keys_map["archimedeanchords"] = ipArchimedeanChords; + keys_map["octagramspiral"] = ipOctagramSpiral; + return keys_map; +} + +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["rectilinear"] = smpRectilinear; + keys_map["rectilinear-grid"] = smpRectilinearGrid; + keys_map["honeycomb"] = smpHoneycomb; + keys_map["pillars"] = smpPillars; + return keys_map; +} + +template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { + t_config_enum_values keys_map; + keys_map["random"] = spRandom; + keys_map["nearest"] = spNearest; + keys_map["aligned"] = spAligned; + return keys_map; +} + +class PrintConfigDef +{ + public: + static t_optiondef_map def; + + static t_optiondef_map build_def(); +}; + +class DynamicPrintConfig : public DynamicConfig +{ + public: + DynamicPrintConfig() { + this->def = &PrintConfigDef::def; + }; + + void normalize() { + if (this->has("extruder")) { + int extruder = this->option("extruder")->getInt(); + this->erase("extruder"); + if (extruder != 0) { + if (!this->has("infill_extruder")) + this->option("infill_extruder", true)->setInt(extruder); + if (!this->has("perimeter_extruder")) + this->option("perimeter_extruder", true)->setInt(extruder); + if (!this->has("support_material_extruder")) + this->option("support_material_extruder", true)->setInt(extruder); + if (!this->has("support_material_interface_extruder")) + this->option("support_material_interface_extruder", true)->setInt(extruder); + } + } + if (this->has("spiral_vase") && this->opt("spiral_vase", true)->value) { + { + // this should be actually done only on the spiral layers instead of all + ConfigOptionBools* opt = this->opt("retract_layer_change", true); + opt->values.assign(opt->values.size(), false); // set all values to false + } + } + }; +}; + +class StaticPrintConfig : public virtual StaticConfig +{ + public: + StaticPrintConfig() { + this->def = &PrintConfigDef::def; + }; +}; + +class PrintObjectConfig : public virtual StaticPrintConfig +{ + public: + ConfigOptionBool dont_support_bridges; + ConfigOptionFloatOrPercent extrusion_width; + ConfigOptionFloatOrPercent first_layer_height; + ConfigOptionBool infill_only_where_needed; + ConfigOptionBool interface_shells; + ConfigOptionFloat layer_height; + ConfigOptionInt raft_layers; + ConfigOptionEnum seam_position; + ConfigOptionBool support_material; + ConfigOptionInt support_material_angle; + ConfigOptionInt support_material_enforce_layers; + ConfigOptionInt support_material_extruder; + ConfigOptionFloatOrPercent support_material_extrusion_width; + ConfigOptionInt support_material_interface_extruder; + ConfigOptionInt support_material_interface_layers; + ConfigOptionFloat support_material_interface_spacing; + ConfigOptionFloatOrPercent support_material_interface_speed; + ConfigOptionEnum support_material_pattern; + ConfigOptionFloat support_material_spacing; + ConfigOptionFloat support_material_speed; + ConfigOptionInt support_material_threshold; + ConfigOptionFloat xy_size_compensation; + + PrintObjectConfig() : StaticPrintConfig() { + this->dont_support_bridges.value = true; + this->extrusion_width.value = 0; + this->extrusion_width.percent = false; + this->first_layer_height.value = 0.35; + this->first_layer_height.percent = false; + this->infill_only_where_needed.value = false; + this->interface_shells.value = false; + this->layer_height.value = 0.3; + this->raft_layers.value = 0; + this->seam_position.value = spAligned; + this->support_material.value = false; + this->support_material_angle.value = 0; + this->support_material_enforce_layers.value = 0; + this->support_material_extruder.value = 1; + this->support_material_extrusion_width.value = 0; + this->support_material_extrusion_width.percent = false; + this->support_material_interface_extruder.value = 1; + this->support_material_interface_layers.value = 3; + this->support_material_interface_spacing.value = 0; + this->support_material_interface_speed.value = 100; + this->support_material_interface_speed.percent = true; + this->support_material_pattern.value = smpPillars; + this->support_material_spacing.value = 2.5; + this->support_material_speed.value = 60; + this->support_material_threshold.value = 0; + this->xy_size_compensation.value = 0; + }; + + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "dont_support_bridges") return &this->dont_support_bridges; + if (opt_key == "extrusion_width") return &this->extrusion_width; + if (opt_key == "first_layer_height") return &this->first_layer_height; + if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; + if (opt_key == "interface_shells") return &this->interface_shells; + if (opt_key == "layer_height") return &this->layer_height; + if (opt_key == "raft_layers") return &this->raft_layers; + if (opt_key == "seam_position") return &this->seam_position; + if (opt_key == "support_material") return &this->support_material; + if (opt_key == "support_material_angle") return &this->support_material_angle; + if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers; + if (opt_key == "support_material_extruder") return &this->support_material_extruder; + if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width; + if (opt_key == "support_material_interface_extruder") return &this->support_material_interface_extruder; + if (opt_key == "support_material_interface_layers") return &this->support_material_interface_layers; + if (opt_key == "support_material_interface_spacing") return &this->support_material_interface_spacing; + if (opt_key == "support_material_interface_speed") return &this->support_material_interface_speed; + if (opt_key == "support_material_pattern") return &this->support_material_pattern; + if (opt_key == "support_material_spacing") return &this->support_material_spacing; + if (opt_key == "support_material_speed") return &this->support_material_speed; + if (opt_key == "support_material_threshold") return &this->support_material_threshold; + if (opt_key == "xy_size_compensation") return &this->xy_size_compensation; + + return NULL; + }; +}; + +class PrintRegionConfig : public virtual StaticPrintConfig +{ + public: + ConfigOptionInt bottom_solid_layers; + ConfigOptionFloat bridge_flow_ratio; + ConfigOptionFloat bridge_speed; + ConfigOptionFloatOrPercent external_perimeter_extrusion_width; + ConfigOptionFloatOrPercent external_perimeter_speed; + ConfigOptionBool external_perimeters_first; + ConfigOptionBool extra_perimeters; + ConfigOptionInt fill_angle; + ConfigOptionPercent fill_density; + ConfigOptionEnum fill_pattern; + ConfigOptionFloat gap_fill_speed; + ConfigOptionInt infill_extruder; + ConfigOptionFloatOrPercent infill_extrusion_width; + ConfigOptionInt infill_every_layers; + ConfigOptionFloat infill_speed; + ConfigOptionBool overhangs; + ConfigOptionInt perimeter_extruder; + ConfigOptionFloatOrPercent perimeter_extrusion_width; + ConfigOptionFloat perimeter_speed; + ConfigOptionInt perimeters; + ConfigOptionFloatOrPercent small_perimeter_speed; + ConfigOptionEnum solid_fill_pattern; + ConfigOptionFloat solid_infill_below_area; + ConfigOptionFloatOrPercent solid_infill_extrusion_width; + ConfigOptionInt solid_infill_every_layers; + ConfigOptionFloatOrPercent solid_infill_speed; + ConfigOptionBool thin_walls; + ConfigOptionFloatOrPercent top_infill_extrusion_width; + ConfigOptionInt top_solid_layers; + ConfigOptionFloatOrPercent top_solid_infill_speed; + + PrintRegionConfig() : StaticPrintConfig() { + this->bottom_solid_layers.value = 3; + this->bridge_flow_ratio.value = 1; + this->bridge_speed.value = 60; + this->external_perimeter_extrusion_width.value = 0; + this->external_perimeter_extrusion_width.percent = false; + this->external_perimeter_speed.value = 70; + this->external_perimeter_speed.percent = true; + this->external_perimeters_first.value = false; + this->extra_perimeters.value = true; + this->fill_angle.value = 45; + this->fill_density.value = 40; + this->fill_pattern.value = ipHoneycomb; + this->gap_fill_speed.value = 20; + this->infill_extruder.value = 1; + this->infill_extrusion_width.value = 0; + this->infill_extrusion_width.percent = false; + this->infill_every_layers.value = 1; + this->infill_speed.value = 60; + this->overhangs.value = true; + this->perimeter_extruder.value = 1; + this->perimeter_extrusion_width.value = 0; + this->perimeter_extrusion_width.percent = false; + this->perimeter_speed.value = 30; + this->perimeters.value = 3; + this->small_perimeter_speed.value = 30; + this->small_perimeter_speed.percent = false; + this->solid_fill_pattern.value = ipRectilinear; + this->solid_infill_below_area.value = 70; + this->solid_infill_extrusion_width.value = 0; + this->solid_infill_extrusion_width.percent = false; + this->solid_infill_every_layers.value = 0; + this->solid_infill_speed.value = 60; + this->solid_infill_speed.percent = false; + this->thin_walls.value = true; + this->top_infill_extrusion_width.value = 0; + this->top_infill_extrusion_width.percent = false; + this->top_solid_infill_speed.value = 50; + this->top_solid_infill_speed.percent = false; + this->top_solid_layers.value = 3; + }; + + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "bottom_solid_layers") return &this->bottom_solid_layers; + if (opt_key == "bridge_flow_ratio") return &this->bridge_flow_ratio; + if (opt_key == "bridge_speed") return &this->bridge_speed; + if (opt_key == "external_perimeter_extrusion_width") return &this->external_perimeter_extrusion_width; + if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed; + if (opt_key == "external_perimeters_first") return &this->external_perimeters_first; + if (opt_key == "extra_perimeters") return &this->extra_perimeters; + if (opt_key == "fill_angle") return &this->fill_angle; + if (opt_key == "fill_density") return &this->fill_density; + if (opt_key == "fill_pattern") return &this->fill_pattern; + if (opt_key == "gap_fill_speed") return &this->gap_fill_speed; + if (opt_key == "infill_extruder") return &this->infill_extruder; + if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; + if (opt_key == "infill_every_layers") return &this->infill_every_layers; + if (opt_key == "infill_speed") return &this->infill_speed; + if (opt_key == "overhangs") return &this->overhangs; + if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; + if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; + if (opt_key == "perimeter_speed") return &this->perimeter_speed; + if (opt_key == "perimeters") return &this->perimeters; + if (opt_key == "small_perimeter_speed") return &this->small_perimeter_speed; + if (opt_key == "solid_fill_pattern") return &this->solid_fill_pattern; + if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area; + if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width; + if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers; + if (opt_key == "solid_infill_speed") return &this->solid_infill_speed; + if (opt_key == "thin_walls") return &this->thin_walls; + if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width; + if (opt_key == "top_solid_infill_speed") return &this->top_solid_infill_speed; + if (opt_key == "top_solid_layers") return &this->top_solid_layers; + + return NULL; + }; +}; + +class PrintConfig : public virtual StaticPrintConfig +{ + public: + ConfigOptionBool avoid_crossing_perimeters; + ConfigOptionPoints bed_shape; + ConfigOptionInt bed_temperature; + ConfigOptionFloat bridge_acceleration; + ConfigOptionInt bridge_fan_speed; + ConfigOptionFloat brim_width; + ConfigOptionBool complete_objects; + ConfigOptionBool cooling; + ConfigOptionFloat default_acceleration; + ConfigOptionInt disable_fan_first_layers; + ConfigOptionFloat duplicate_distance; + ConfigOptionString end_gcode; + ConfigOptionFloat extruder_clearance_height; + ConfigOptionFloat extruder_clearance_radius; + ConfigOptionPoints extruder_offset; + ConfigOptionString extrusion_axis; + ConfigOptionFloats extrusion_multiplier; + ConfigOptionBool fan_always_on; + ConfigOptionInt fan_below_layer_time; + ConfigOptionFloats filament_diameter; + ConfigOptionFloat first_layer_acceleration; + ConfigOptionInt first_layer_bed_temperature; + ConfigOptionFloatOrPercent first_layer_extrusion_width; + ConfigOptionFloatOrPercent first_layer_speed; + ConfigOptionInts first_layer_temperature; + ConfigOptionBool g0; + ConfigOptionBool gcode_arcs; + ConfigOptionBool gcode_comments; + ConfigOptionEnum gcode_flavor; + ConfigOptionFloat infill_acceleration; + ConfigOptionBool infill_first; + ConfigOptionString layer_gcode; + ConfigOptionInt max_fan_speed; + ConfigOptionInt min_fan_speed; + ConfigOptionInt min_print_speed; + ConfigOptionFloat min_skirt_length; + ConfigOptionString notes; + ConfigOptionFloats nozzle_diameter; + ConfigOptionBool only_retract_when_crossing_perimeters; + ConfigOptionBool ooze_prevention; + ConfigOptionString output_filename_format; + ConfigOptionFloat perimeter_acceleration; + ConfigOptionStrings post_process; + ConfigOptionFloat resolution; + ConfigOptionFloats retract_before_travel; + ConfigOptionBools retract_layer_change; + ConfigOptionFloats retract_length; + ConfigOptionFloats retract_length_toolchange; + ConfigOptionFloats retract_lift; + ConfigOptionFloats retract_restart_extra; + ConfigOptionFloats retract_restart_extra_toolchange; + ConfigOptionInts retract_speed; + ConfigOptionFloat skirt_distance; + ConfigOptionInt skirt_height; + ConfigOptionInt skirts; + ConfigOptionInt slowdown_below_layer_time; + ConfigOptionBool spiral_vase; + ConfigOptionInt standby_temperature_delta; + ConfigOptionString start_gcode; + ConfigOptionInts temperature; + ConfigOptionInt threads; + ConfigOptionString toolchange_gcode; + ConfigOptionFloat travel_speed; + ConfigOptionBool use_firmware_retraction; + ConfigOptionBool use_relative_e_distances; + ConfigOptionFloat vibration_limit; + ConfigOptionBools wipe; + ConfigOptionFloat z_offset; + + PrintConfig() : StaticPrintConfig() { + this->avoid_crossing_perimeters.value = false; + this->bed_shape.values.push_back(Pointf(0,0)); + this->bed_shape.values.push_back(Pointf(200,0)); + this->bed_shape.values.push_back(Pointf(200,200)); + this->bed_shape.values.push_back(Pointf(0,200)); + this->bed_temperature.value = 0; + this->bridge_acceleration.value = 0; + this->bridge_fan_speed.value = 100; + this->brim_width.value = 0; + this->complete_objects.value = false; + this->cooling.value = true; + this->default_acceleration.value = 0; + this->disable_fan_first_layers.value = 1; + this->duplicate_distance.value = 6; + this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"; + this->extruder_clearance_height.value = 20; + this->extruder_clearance_radius.value = 20; + this->extruder_offset.values.resize(1); + this->extruder_offset.values[0] = Pointf(0,0); + this->extrusion_axis.value = "E"; + this->extrusion_multiplier.values.resize(1); + this->extrusion_multiplier.values[0] = 1; + this->fan_always_on.value = false; + this->fan_below_layer_time.value = 60; + this->filament_diameter.values.resize(1); + this->filament_diameter.values[0] = 3; + this->first_layer_acceleration.value = 0; + this->first_layer_bed_temperature.value = 0; + this->first_layer_extrusion_width.value = 200; + this->first_layer_extrusion_width.percent = true; + this->first_layer_speed.value = 30; + this->first_layer_speed.percent = true; + this->first_layer_temperature.values.resize(1); + this->first_layer_temperature.values[0] = 200; + this->g0.value = false; + this->gcode_arcs.value = false; + this->gcode_comments.value = false; + this->gcode_flavor.value = gcfRepRap; + this->infill_acceleration.value = 0; + this->infill_first.value = false; + this->layer_gcode.value = ""; + this->max_fan_speed.value = 100; + this->min_fan_speed.value = 35; + this->min_print_speed.value = 10; + this->min_skirt_length.value = 0; + this->notes.value = ""; + this->nozzle_diameter.values.resize(1); + this->nozzle_diameter.values[0] = 0.5; + this->only_retract_when_crossing_perimeters.value = true; + this->ooze_prevention.value = false; + this->output_filename_format.value = "[input_filename_base].gcode"; + this->perimeter_acceleration.value = 0; + this->resolution.value = 0; + this->retract_before_travel.values.resize(1); + this->retract_before_travel.values[0] = 2; + this->retract_layer_change.values.resize(1); + this->retract_layer_change.values[0] = true; + this->retract_length.values.resize(1); + this->retract_length.values[0] = 1; + this->retract_length_toolchange.values.resize(1); + this->retract_length_toolchange.values[0] = 10; + this->retract_lift.values.resize(1); + this->retract_lift.values[0] = 0; + this->retract_restart_extra.values.resize(1); + this->retract_restart_extra.values[0] = 0; + this->retract_restart_extra_toolchange.values.resize(1); + this->retract_restart_extra_toolchange.values[0] = 0; + this->retract_speed.values.resize(1); + this->retract_speed.values[0] = 30; + this->skirt_distance.value = 6; + this->skirt_height.value = 1; + this->skirts.value = 1; + this->slowdown_below_layer_time.value = 30; + this->spiral_vase.value = false; + this->standby_temperature_delta.value = -5; + this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"; + this->temperature.values.resize(1); + this->temperature.values[0] = 200; + this->threads.value = 2; + this->toolchange_gcode.value = ""; + this->travel_speed.value = 130; + this->use_firmware_retraction.value = false; + this->use_relative_e_distances.value = false; + this->vibration_limit.value = 0; + this->wipe.values.resize(1); + this->wipe.values[0] = false; + this->z_offset.value = 0; + }; + + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "avoid_crossing_perimeters") return &this->avoid_crossing_perimeters; + if (opt_key == "bed_shape") return &this->bed_shape; + if (opt_key == "bed_temperature") return &this->bed_temperature; + if (opt_key == "bridge_acceleration") return &this->bridge_acceleration; + if (opt_key == "bridge_fan_speed") return &this->bridge_fan_speed; + if (opt_key == "brim_width") return &this->brim_width; + if (opt_key == "complete_objects") return &this->complete_objects; + if (opt_key == "cooling") return &this->cooling; + if (opt_key == "default_acceleration") return &this->default_acceleration; + if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers; + if (opt_key == "duplicate_distance") return &this->duplicate_distance; + if (opt_key == "end_gcode") return &this->end_gcode; + if (opt_key == "extruder_clearance_height") return &this->extruder_clearance_height; + if (opt_key == "extruder_clearance_radius") return &this->extruder_clearance_radius; + if (opt_key == "extruder_offset") return &this->extruder_offset; + if (opt_key == "extrusion_axis") return &this->extrusion_axis; + if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier; + if (opt_key == "fan_always_on") return &this->fan_always_on; + if (opt_key == "fan_below_layer_time") return &this->fan_below_layer_time; + if (opt_key == "filament_diameter") return &this->filament_diameter; + if (opt_key == "first_layer_acceleration") return &this->first_layer_acceleration; + if (opt_key == "first_layer_bed_temperature") return &this->first_layer_bed_temperature; + if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width; + if (opt_key == "first_layer_speed") return &this->first_layer_speed; + if (opt_key == "first_layer_temperature") return &this->first_layer_temperature; + if (opt_key == "g0") return &this->g0; + if (opt_key == "gcode_arcs") return &this->gcode_arcs; + if (opt_key == "gcode_comments") return &this->gcode_comments; + if (opt_key == "gcode_flavor") return &this->gcode_flavor; + if (opt_key == "infill_acceleration") return &this->infill_acceleration; + if (opt_key == "infill_first") return &this->infill_first; + if (opt_key == "layer_gcode") return &this->layer_gcode; + if (opt_key == "max_fan_speed") return &this->max_fan_speed; + if (opt_key == "min_fan_speed") return &this->min_fan_speed; + if (opt_key == "min_print_speed") return &this->min_print_speed; + if (opt_key == "min_skirt_length") return &this->min_skirt_length; + if (opt_key == "notes") return &this->notes; + if (opt_key == "nozzle_diameter") return &this->nozzle_diameter; + if (opt_key == "only_retract_when_crossing_perimeters") return &this->only_retract_when_crossing_perimeters; + if (opt_key == "ooze_prevention") return &this->ooze_prevention; + if (opt_key == "output_filename_format") return &this->output_filename_format; + if (opt_key == "perimeter_acceleration") return &this->perimeter_acceleration; + if (opt_key == "post_process") return &this->post_process; + if (opt_key == "resolution") return &this->resolution; + if (opt_key == "retract_before_travel") return &this->retract_before_travel; + if (opt_key == "retract_layer_change") return &this->retract_layer_change; + if (opt_key == "retract_length") return &this->retract_length; + if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange; + if (opt_key == "retract_lift") return &this->retract_lift; + if (opt_key == "retract_restart_extra") return &this->retract_restart_extra; + if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange; + if (opt_key == "retract_speed") return &this->retract_speed; + if (opt_key == "skirt_distance") return &this->skirt_distance; + if (opt_key == "skirt_height") return &this->skirt_height; + if (opt_key == "skirts") return &this->skirts; + if (opt_key == "slowdown_below_layer_time") return &this->slowdown_below_layer_time; + if (opt_key == "spiral_vase") return &this->spiral_vase; + if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta; + if (opt_key == "start_gcode") return &this->start_gcode; + if (opt_key == "temperature") return &this->temperature; + if (opt_key == "threads") return &this->threads; + if (opt_key == "toolchange_gcode") return &this->toolchange_gcode; + if (opt_key == "travel_speed") return &this->travel_speed; + if (opt_key == "use_firmware_retraction") return &this->use_firmware_retraction; + if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances; + if (opt_key == "vibration_limit") return &this->vibration_limit; + if (opt_key == "wipe") return &this->wipe; + if (opt_key == "z_offset") return &this->z_offset; + + return NULL; + }; +}; + +class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig { + public: + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + ConfigOption* opt; + if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt; + if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt; + if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt; + return NULL; + }; + + std::string get_extrusion_axis() { + if (this->gcode_flavor == gcfMach3) { + return std::string("A"); + } else if (this->gcode_flavor == gcfNoExtrusion) { + return std::string(""); + } + return this->extrusion_axis; + } +}; + +} + +#endif diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp new file mode 100644 index 000000000..4dac94cf6 --- /dev/null +++ b/xs/src/libslic3r/PrintObject.cpp @@ -0,0 +1,254 @@ +#include "Print.hpp" +#include "BoundingBox.hpp" + +namespace Slic3r { + +PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) +: _print(print), + _model_object(model_object), + typed_slices(false) +{ + region_volumes.resize(this->_print->regions.size()); + + // Compute the translation to be applied to our meshes so that we work with smaller coordinates + { + // Translate meshes so that our toolpath generation algorithms work with smaller + // XY coordinates; this translation is an optimization and not strictly required. + // A cloned mesh will be aligned to 0 before slicing in _slice_region() since we + // 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( + scale_(modobj_bbox.min.x), scale_(modobj_bbox.min.y)); + + // TODO: $self->_trigger_copies; + + // Scale the object size and store it + Pointf3 size = modobj_bbox.size(); + this->size = Point3(scale_(size.x), scale_(size.y), scale_(size.z)); + } +} + +PrintObject::~PrintObject() +{ +} + +Print* +PrintObject::print() +{ + return this->_print; +} + +ModelObject* +PrintObject::model_object() +{ + return this->_model_object; +} + +void +PrintObject::add_region_volume(int region_id, int volume_id) +{ + if (region_id >= region_volumes.size()) { + region_volumes.resize(region_id + 1); + } + + region_volumes[region_id].push_back(volume_id); +} + +size_t +PrintObject::layer_count() +{ + return this->layers.size(); +} + +void +PrintObject::clear_layers() +{ + for (int i = this->layers.size()-1; i >= 0; --i) + this->delete_layer(i); +} + +Layer* +PrintObject::get_layer(int idx) +{ + return this->layers.at(idx); +} + +Layer* +PrintObject::add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z) +{ + Layer* layer = new Layer(id, this, height, print_z, slice_z); + layers.push_back(layer); + return layer; +} + +void +PrintObject::delete_layer(int idx) +{ + LayerPtrs::iterator i = this->layers.begin() + idx; + delete *i; + this->layers.erase(i); +} + +size_t +PrintObject::support_layer_count() +{ + return this->support_layers.size(); +} + +void +PrintObject::clear_support_layers() +{ + for (int i = this->support_layers.size()-1; i >= 0; --i) + this->delete_support_layer(i); +} + +SupportLayer* +PrintObject::get_support_layer(int idx) +{ + return this->support_layers.at(idx); +} + +SupportLayer* +PrintObject::add_support_layer(int id, coordf_t height, coordf_t print_z, + coordf_t slice_z) +{ + SupportLayer* layer = new SupportLayer(id, this, height, print_z, slice_z); + support_layers.push_back(layer); + return layer; +} + +void +PrintObject::delete_support_layer(int idx) +{ + SupportLayerPtrs::iterator i = this->support_layers.begin() + idx; + delete *i; + this->support_layers.erase(i); +} + +bool +PrintObject::invalidate_state_by_config_options(const std::vector &opt_keys) +{ + std::set steps; + + // this method only accepts PrintObjectConfig and PrintRegionConfig option keys + for (std::vector::const_iterator opt_key = opt_keys.begin(); opt_key != opt_keys.end(); ++opt_key) { + if (*opt_key == "perimeters" + || *opt_key == "extra_perimeters" + || *opt_key == "gap_fill_speed" + || *opt_key == "overhangs" + || *opt_key == "perimeter_extrusion_width" + || *opt_key == "thin_walls" + || *opt_key == "external_perimeters_first") { + steps.insert(posPerimeters); + } else if (*opt_key == "resolution" + || *opt_key == "layer_height" + || *opt_key == "first_layer_height" + || *opt_key == "xy_size_compensation" + || *opt_key == "raft_layers") { + steps.insert(posSlice); + } else if (*opt_key == "support_material" + || *opt_key == "support_material_angle" + || *opt_key == "support_material_extruder" + || *opt_key == "support_material_extrusion_width" + || *opt_key == "support_material_interface_layers" + || *opt_key == "support_material_interface_extruder" + || *opt_key == "support_material_interface_spacing" + || *opt_key == "support_material_interface_speed" + || *opt_key == "support_material_pattern" + || *opt_key == "support_material_spacing" + || *opt_key == "support_material_threshold" + || *opt_key == "dont_support_bridges") { + steps.insert(posSupportMaterial); + } else if (*opt_key == "interface_shells" + || *opt_key == "infill_only_where_needed" + || *opt_key == "bottom_solid_layers" + || *opt_key == "top_solid_layers" + || *opt_key == "infill_extruder" + || *opt_key == "infill_extrusion_width") { + steps.insert(posPrepareInfill); + } else if (*opt_key == "fill_angle" + || *opt_key == "fill_pattern" + || *opt_key == "solid_fill_pattern" + || *opt_key == "infill_every_layers" + || *opt_key == "solid_infill_below_area" + || *opt_key == "solid_infill_every_layers" + || *opt_key == "top_infill_extrusion_width") { + steps.insert(posInfill); + } else if (*opt_key == "fill_density" + || *opt_key == "solid_infill_extrusion_width") { + steps.insert(posPerimeters); + steps.insert(posPrepareInfill); + } else if (*opt_key == "external_perimeter_extrusion_width" + || *opt_key == "perimeter_extruder") { + steps.insert(posPerimeters); + steps.insert(posSupportMaterial); + } else if (*opt_key == "bridge_flow_ratio") { + steps.insert(posPerimeters); + steps.insert(posInfill); + } else if (*opt_key == "seam_position" + || *opt_key == "support_material_speed" + || *opt_key == "bridge_speed" + || *opt_key == "external_perimeter_speed" + || *opt_key == "infill_speed" + || *opt_key == "perimeter_speed" + || *opt_key == "small_perimeter_speed" + || *opt_key == "solid_infill_speed" + || *opt_key == "top_solid_infill_speed") { + // 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 + return this->invalidate_all_steps(); + } + } + + bool invalidated = false; + for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { + if (this->invalidate_step(*step)) invalidated = true; + } + + return invalidated; +} + +bool +PrintObject::invalidate_step(PrintObjectStep step) +{ + bool invalidated = this->state.invalidate(step); + + // propagate to dependent steps + if (step == posPerimeters) { + this->invalidate_step(posPrepareInfill); + this->_print->invalidate_step(psSkirt); + this->_print->invalidate_step(psBrim); + } else if (step == posPrepareInfill) { + this->invalidate_step(posInfill); + } else if (step == posInfill) { + this->_print->invalidate_step(psSkirt); + this->_print->invalidate_step(psBrim); + } else if (step == posSlice) { + this->invalidate_step(posPerimeters); + this->invalidate_step(posSupportMaterial); + } + + return invalidated; +} + +bool +PrintObject::invalidate_all_steps() +{ + // make a copy because when invalidating steps the iterators are not working anymore + std::set steps = this->state.started; + + bool invalidated = false; + for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { + if (this->invalidate_step(*step)) invalidated = true; + } + return invalidated; +} + + +#ifdef SLIC3RXS +REGISTER_CLASS(PrintObject, "Print::Object"); +#endif + +} diff --git a/xs/src/libslic3r/PrintRegion.cpp b/xs/src/libslic3r/PrintRegion.cpp new file mode 100644 index 000000000..d651b57ab --- /dev/null +++ b/xs/src/libslic3r/PrintRegion.cpp @@ -0,0 +1,70 @@ +#include "Print.hpp" + +namespace Slic3r { + +PrintRegion::PrintRegion(Print* print) + : _print(print) +{ +} + +PrintRegion::~PrintRegion() +{ +} + +Print* +PrintRegion::print() +{ + return this->_print; +} + +Flow +PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const +{ + ConfigOptionFloatOrPercent config_width; + if (width != -1) { + // use the supplied custom width, if any + config_width.value = width; + config_width.percent = false; + } else { + // otherwise, get extrusion width from configuration + // (might be an absolute value, or a percent value, or zero for auto) + if (first_layer && this->_print->config.first_layer_extrusion_width.value > 0) { + config_width = this->_print->config.first_layer_extrusion_width; + } else if (role == frExternalPerimeter) { + config_width = this->config.external_perimeter_extrusion_width; + } else if (role == frPerimeter) { + config_width = this->config.perimeter_extrusion_width; + } else if (role == frInfill) { + config_width = this->config.infill_extrusion_width; + } else if (role == frSolidInfill) { + config_width = this->config.solid_infill_extrusion_width; + } else if (role == frTopSolidInfill) { + config_width = this->config.top_infill_extrusion_width; + } else { + CONFESS("Unknown role"); + } + } + if (config_width.value == 0) { + config_width = object.config.extrusion_width; + } + + // get the configured nozzle_diameter for the extruder associated + // to the flow role requested + size_t extruder; // 1-based + if (role == frPerimeter || role == frExternalPerimeter) { + extruder = this->config.perimeter_extruder; + } else if (role == frInfill || role == frSolidInfill || role == frTopSolidInfill) { + extruder = this->config.infill_extruder; + } else { + CONFESS("Unknown role $role"); + } + double nozzle_diameter = this->_print->config.nozzle_diameter.get_at(extruder-1); + + return Flow::new_from_config_width(role, config_width, nozzle_diameter, layer_height, bridge ? this->config.bridge_flow_ratio : 0); +} + +#ifdef SLIC3RXS +REGISTER_CLASS(PrintRegion, "Print::Region"); +#endif + +} diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp new file mode 100644 index 000000000..db5ec7293 --- /dev/null +++ b/xs/src/libslic3r/SVG.cpp @@ -0,0 +1,51 @@ +#include "SVG.hpp" + +namespace Slic3r { + +SVG::SVG(const char* filename) +{ + this->f = fopen(filename, "w"); + fprintf(this->f, + "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + ); + this->arrows = true; +} + +float +SVG::coordinate(long c) +{ + return (float)unscale(c)*10; +} + +void +SVG::AddLine(const Line &line) +{ + fprintf(this->f, + " coordinate(line.a.x), this->coordinate(line.a.y), this->coordinate(line.b.x), this->coordinate(line.b.y) + ); + if (this->arrows) + fprintf(this->f, " marker-end=\"url(#endArrow)\""); + fprintf(this->f, "/>\n"); +} + +void +SVG::AddLine(const IntersectionLine &line) +{ + this->AddLine(Line(line.a, line.b)); +} + +void +SVG::Close() +{ + fprintf(this->f, "\n"); + fclose(this->f); + printf("SVG file written.\n"); +} + +} diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp new file mode 100644 index 000000000..5d4cfd56e --- /dev/null +++ b/xs/src/libslic3r/SVG.hpp @@ -0,0 +1,25 @@ +#ifndef slic3r_SVG_hpp_ +#define slic3r_SVG_hpp_ + +#include +#include "Line.hpp" +#include "TriangleMesh.hpp" + +namespace Slic3r { + +class SVG +{ + private: + FILE* f; + float coordinate(long c); + public: + bool arrows; + SVG(const char* filename); + void AddLine(const Line &line); + void AddLine(const IntersectionLine &line); + void Close(); +}; + +} + +#endif diff --git a/xs/src/libslic3r/Surface.cpp b/xs/src/libslic3r/Surface.cpp new file mode 100644 index 000000000..a53cb2513 --- /dev/null +++ b/xs/src/libslic3r/Surface.cpp @@ -0,0 +1,56 @@ +#include "Surface.hpp" + +namespace Slic3r { + +double +Surface::area() const +{ + return this->expolygon.area(); +} + +bool +Surface::is_solid() const +{ + return this->surface_type == stTop + || this->surface_type == stBottom + || this->surface_type == stBottomBridge + || this->surface_type == stInternalSolid; +} + +bool +Surface::is_external() const +{ + return this->surface_type == stTop + || this->surface_type == stBottom + || this->surface_type == stBottomBridge; +} + +bool +Surface::is_bottom() const +{ + return this->surface_type == stBottom + || this->surface_type == stBottomBridge; +} + +bool +Surface::is_bridge() const +{ + return this->surface_type == stBottomBridge + || this->surface_type == stInternalBridge; +} + +#ifdef SLIC3RXS + +REGISTER_CLASS(Surface, "Surface"); + +void +Surface::from_SV_check(SV* surface_sv) +{ + if (!sv_isa(surface_sv, perl_class_name(this)) && !sv_isa(surface_sv, perl_class_name_ref(this))) + CONFESS("Not a valid %s object", perl_class_name(this)); + // a XS Surface was supplied + *this = *(Surface *)SvIV((SV*)SvRV( surface_sv )); +} +#endif + +} diff --git a/xs/src/libslic3r/Surface.hpp b/xs/src/libslic3r/Surface.hpp new file mode 100644 index 000000000..ce0f02477 --- /dev/null +++ b/xs/src/libslic3r/Surface.hpp @@ -0,0 +1,35 @@ +#ifndef slic3r_Surface_hpp_ +#define slic3r_Surface_hpp_ + +#include "ExPolygon.hpp" + +namespace Slic3r { + +enum SurfaceType { stTop, stBottom, stBottomBridge, stInternal, stInternalSolid, stInternalBridge, stInternalVoid }; + +class Surface +{ + public: + ExPolygon expolygon; + SurfaceType surface_type; + double thickness; // in mm + unsigned short thickness_layers; // in layers + double bridge_angle; // in radians, ccw, 0 = East, only 0+ (negative means undefined) + unsigned short extra_perimeters; + double area() const; + bool is_solid() const; + bool is_external() const; + bool is_bottom() const; + bool is_bridge() const; + + #ifdef SLIC3RXS + void from_SV_check(SV* surface_sv); + #endif +}; + +typedef std::vector Surfaces; +typedef std::vector SurfacesPtr; + +} + +#endif diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp new file mode 100644 index 000000000..1590e7a21 --- /dev/null +++ b/xs/src/libslic3r/SurfaceCollection.cpp @@ -0,0 +1,75 @@ +#include "SurfaceCollection.hpp" +#include + +namespace Slic3r { + +SurfaceCollection::operator Polygons() const +{ + Polygons polygons; + for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { + Polygons surface_p = surface->expolygon; + polygons.insert(polygons.end(), surface_p.begin(), surface_p.end()); + } + return polygons; +} + +SurfaceCollection::operator ExPolygons() const +{ + ExPolygons expp; + expp.reserve(this->surfaces.size()); + for (Surfaces::const_iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { + expp.push_back(surface->expolygon); + } + return expp; +} + +void +SurfaceCollection::simplify(double tolerance) +{ + Surfaces ss; + for (Surfaces::const_iterator it_s = this->surfaces.begin(); it_s != this->surfaces.end(); ++it_s) { + ExPolygons expp; + it_s->expolygon.simplify(tolerance, expp); + for (ExPolygons::const_iterator it_e = expp.begin(); it_e != expp.end(); ++it_e) { + Surface s = *it_s; + s.expolygon = *it_e; + ss.push_back(s); + } + } + this->surfaces = ss; +} + +/* group surfaces by common properties */ +void +SurfaceCollection::group(std::vector *retval) +{ + for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) { + // find a group with the same properties + SurfacesPtr* group = NULL; + for (std::vector::iterator git = retval->begin(); git != retval->end(); ++git) { + Surface* gkey = git->front(); + if ( gkey->surface_type == it->surface_type + && gkey->thickness == it->thickness + && gkey->thickness_layers == it->thickness_layers + && gkey->bridge_angle == it->bridge_angle) { + group = &*git; + break; + } + } + + // if no group with these properties exists, add one + if (group == NULL) { + retval->resize(retval->size() + 1); + group = &retval->back(); + } + + // append surface to group + group->push_back(&*it); + } +} + +#ifdef SLIC3RXS +REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); +#endif + +} diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp new file mode 100644 index 000000000..fe3fae8c6 --- /dev/null +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -0,0 +1,22 @@ +#ifndef slic3r_SurfaceCollection_hpp_ +#define slic3r_SurfaceCollection_hpp_ + +#include "Surface.hpp" +#include + +namespace Slic3r { + +class SurfaceCollection +{ + public: + Surfaces surfaces; + + operator Polygons() const; + operator ExPolygons() const; + void simplify(double tolerance); + void group(std::vector *retval); +}; + +} + +#endif diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp new file mode 100644 index 000000000..77b11c5f8 --- /dev/null +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -0,0 +1,1070 @@ +#include "TriangleMesh.hpp" +#include "ClipperUtils.hpp" +#include "Geometry.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SLIC3R_DEBUG +#include "SVG.hpp" +#endif + +namespace Slic3r { + +TriangleMesh::TriangleMesh() + : repaired(false) +{ + stl_initialize(&this->stl); +} + +TriangleMesh::TriangleMesh(const TriangleMesh &other) + : stl(other.stl), repaired(other.repaired) +{ + this->stl.heads = NULL; + this->stl.tail = NULL; + if (other.stl.facet_start != NULL) { + this->stl.facet_start = (stl_facet*)calloc(other.stl.stats.number_of_facets, sizeof(stl_facet)); + std::copy(other.stl.facet_start, other.stl.facet_start + other.stl.stats.number_of_facets, this->stl.facet_start); + } + if (other.stl.neighbors_start != NULL) { + this->stl.neighbors_start = (stl_neighbors*)calloc(other.stl.stats.number_of_facets, sizeof(stl_neighbors)); + std::copy(other.stl.neighbors_start, other.stl.neighbors_start + other.stl.stats.number_of_facets, this->stl.neighbors_start); + } + if (other.stl.v_indices != NULL) { + this->stl.v_indices = (v_indices_struct*)calloc(other.stl.stats.number_of_facets, sizeof(v_indices_struct)); + std::copy(other.stl.v_indices, other.stl.v_indices + other.stl.stats.number_of_facets, this->stl.v_indices); + } + if (other.stl.v_shared != NULL) { + this->stl.v_shared = (stl_vertex*)calloc(other.stl.stats.shared_vertices, sizeof(stl_vertex)); + std::copy(other.stl.v_shared, other.stl.v_shared + other.stl.stats.shared_vertices, this->stl.v_shared); + } +} + +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); + std::swap(this->stl.facet_start, other.stl.facet_start); + std::swap(this->stl.neighbors_start, other.stl.neighbors_start); + std::swap(this->stl.v_indices, other.stl.v_indices); + std::swap(this->stl.v_shared, other.stl.v_shared); +} + +TriangleMesh::~TriangleMesh() { + stl_close(&this->stl); +} + +void +TriangleMesh::ReadSTLFile(char* input_file) { + stl_open(&stl, input_file); +} + +void +TriangleMesh::write_ascii(char* output_file) +{ + stl_write_ascii(&this->stl, output_file, ""); +} + +void +TriangleMesh::write_binary(char* output_file) +{ + stl_write_binary(&this->stl, output_file, ""); +} + +void +TriangleMesh::repair() { + if (this->repaired) return; + + // admesh fails when repairing empty meshes + if (this->stl.stats.number_of_facets == 0) return; + + // checking exact + stl_check_facets_exact(&stl); + stl.stats.facets_w_1_bad_edge = (stl.stats.connected_facets_2_edge - stl.stats.connected_facets_3_edge); + stl.stats.facets_w_2_bad_edge = (stl.stats.connected_facets_1_edge - stl.stats.connected_facets_2_edge); + stl.stats.facets_w_3_bad_edge = (stl.stats.number_of_facets - stl.stats.connected_facets_1_edge); + + // checking nearby + int last_edges_fixed = 0; + float tolerance = stl.stats.shortest_edge; + float increment = stl.stats.bounding_diameter / 10000.0; + int iterations = 2; + if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + for (int i = 0; i < iterations; i++) { + if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + //printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations); + stl_check_facets_nearby(&stl, tolerance); + //printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed); + last_edges_fixed = stl.stats.edges_fixed; + tolerance += increment; + } else { + break; + } + } + } + + // remove_unconnected + if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + stl_remove_unconnected_facets(&stl); + } + + // fill_holes + if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + stl_fill_holes(&stl); + } + + // normal_directions + stl_fix_normal_directions(&stl); + + // normal_values + stl_fix_normal_values(&stl); + + // always calculate the volume and reverse all normals if volume is negative + stl_calculate_volume(&stl); + + // neighbors + stl_verify_neighbors(&stl); + + this->repaired = true; +} + +void +TriangleMesh::reset_repair_stats() { + this->stl.stats.degenerate_facets = 0; + this->stl.stats.edges_fixed = 0; + this->stl.stats.facets_removed = 0; + this->stl.stats.facets_added = 0; + this->stl.stats.facets_reversed = 0; + this->stl.stats.backwards_edges = 0; + this->stl.stats.normals_fixed = 0; +} + +void +TriangleMesh::WriteOBJFile(char* output_file) { + stl_generate_shared_vertices(&stl); + stl_write_obj(&stl, output_file); +} + +void TriangleMesh::scale(float factor) +{ + stl_scale(&(this->stl), factor); +} + +void TriangleMesh::scale(std::vector versor) +{ + float fversor[3]; + fversor[0] = versor[0]; + fversor[1] = versor[1]; + fversor[2] = versor[2]; + stl_scale_versor(&this->stl, fversor); +} + +void TriangleMesh::translate(float x, float y, float z) +{ + stl_translate_relative(&(this->stl), x, y, z); +} + +void TriangleMesh::rotate_x(float angle) +{ + stl_rotate_x(&(this->stl), angle); +} + +void TriangleMesh::rotate_y(float angle) +{ + stl_rotate_y(&(this->stl), angle); +} + +void TriangleMesh::rotate_z(float angle) +{ + stl_rotate_z(&(this->stl), angle); +} + +void TriangleMesh::flip_x() +{ + stl_mirror_yz(&this->stl); +} + +void TriangleMesh::flip_y() +{ + stl_mirror_xz(&this->stl); +} + +void TriangleMesh::flip_z() +{ + stl_mirror_xy(&this->stl); +} + +void TriangleMesh::align_to_origin() +{ + this->translate( + -(this->stl.stats.min.x), + -(this->stl.stats.min.y), + -(this->stl.stats.min.z) + ); +} + +void TriangleMesh::rotate(double angle, Point* center) +{ + this->translate(-center->x, -center->y, 0); + stl_rotate_z(&(this->stl), (float)angle); + this->translate(+center->x, +center->y, 0); +} + +TriangleMeshPtrs +TriangleMesh::split() const +{ + TriangleMeshPtrs meshes; + std::set seen_facets; + + // we need neighbors + if (!this->repaired) CONFESS("split() requires repair()"); + + // loop while we have remaining facets + while (1) { + // get the first facet + std::queue facet_queue; + std::deque 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 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()) { + 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]); + } + seen_facets.insert(facet_idx); + } + + TriangleMesh* mesh = new TriangleMesh; + meshes.push_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_allocate(&mesh->stl); + + int first = 1; + for (std::deque::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) +{ + // reset stats and metadata + int number_of_facets = this->stl.stats.number_of_facets; + stl_invalidate_shared_vertices(&this->stl); + this->repaired = false; + + // update facet count and allocate more memory + this->stl.stats.number_of_facets = number_of_facets + mesh->stl.stats.number_of_facets; + this->stl.stats.original_num_facets = this->stl.stats.number_of_facets; + stl_reallocate(&this->stl); + + // copy facets + for (int i = 0; i < mesh->stl.stats.number_of_facets; i++) { + this->stl.facet_start[number_of_facets + i] = mesh->stl.facet_start[i]; + } + + // update size + stl_get_size(&this->stl); +} + +/* this will return scaled ExPolygons */ +void +TriangleMesh::horizontal_projection(ExPolygons &retval) const +{ + Polygons pp; + pp.reserve(this->stl.stats.number_of_facets); + for (int i = 0; i < this->stl.stats.number_of_facets; i++) { + stl_facet* facet = &this->stl.facet_start[i]; + Polygon p; + p.points.resize(3); + p.points[0] = Point(facet->vertex[0].x / SCALING_FACTOR, facet->vertex[0].y / SCALING_FACTOR); + p.points[1] = Point(facet->vertex[1].x / SCALING_FACTOR, facet->vertex[1].y / SCALING_FACTOR); + p.points[2] = Point(facet->vertex[2].x / SCALING_FACTOR, facet->vertex[2].y / SCALING_FACTOR); + p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that + pp.push_back(p); + } + + // the offset factor was tuned using groovemount.stl + offset(pp, pp, 0.01 / SCALING_FACTOR); + union_(pp, retval, true); +} + +void +TriangleMesh::convex_hull(Polygon* 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.push_back(Point(v->x / SCALING_FACTOR, v->y / SCALING_FACTOR)); + } + Slic3r::Geometry::convex_hull(pp, hull); +} + +void +TriangleMesh::bounding_box(BoundingBoxf3* bb) const +{ + 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; +} + +void +TriangleMesh::require_shared_vertices() +{ + if (!this->repaired) this->repair(); + if (this->stl.v_shared == NULL) stl_generate_shared_vertices(&(this->stl)); +} + +#ifdef SLIC3RXS + +REGISTER_CLASS(TriangleMesh, "TriangleMesh"); + +SV* +TriangleMesh::to_SV() { + SV* sv = newSV(0); + sv_setref_pv( sv, perl_class_name(this), (void*)this ); + return sv; +} + +void TriangleMesh::ReadFromPerl(SV* vertices, SV* facets) +{ + stl.stats.type = inmemory; + + // count facets and allocate memory + AV* facets_av = (AV*)SvRV(facets); + stl.stats.number_of_facets = av_len(facets_av)+1; + stl.stats.original_num_facets = stl.stats.number_of_facets; + stl_allocate(&stl); + + // read geometry + AV* vertices_av = (AV*)SvRV(vertices); + for (unsigned int i = 0; i < stl.stats.number_of_facets; i++) { + AV* facet_av = (AV*)SvRV(*av_fetch(facets_av, i, 0)); + stl_facet facet; + facet.normal.x = 0; + facet.normal.y = 0; + facet.normal.z = 0; + for (unsigned int v = 0; v <= 2; v++) { + AV* vertex_av = (AV*)SvRV(*av_fetch(vertices_av, SvIV(*av_fetch(facet_av, v, 0)), 0)); + facet.vertex[v].x = SvNV(*av_fetch(vertex_av, 0, 0)); + facet.vertex[v].y = SvNV(*av_fetch(vertex_av, 1, 0)); + facet.vertex[v].z = SvNV(*av_fetch(vertex_av, 2, 0)); + } + facet.extra[0] = 0; + facet.extra[1] = 0; + + stl.facet_start[i] = facet; + } + + stl_get_size(&(this->stl)); +} +#endif + +void +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) +{ + /* + This method gets called with a list of unscaled Z coordinates and outputs + a vector pointer having the same number of items as the original list. + Each item is a vector of polygons created by slicing our mesh at the + given heights. + + This method should basically combine the behavior of the existing + Perl methods defined in lib/Slic3r/TriangleMesh.pm: + + - analyze(): this creates the 'facets_edges' and the 'edges_facets' + tables (we don't need the 'edges' table) + + - slice_facet(): this has to be done for each facet. It generates + intersection lines with each plane identified by the Z list. + The get_layer_range() binary search used to identify the Z range + of the facet is already ported to C++ (see Object.xsp) + + - make_loops(): this has to be done for each layer. It creates polygons + from the lines generated by the previous step. + + At the end, we free the tables generated by analyze() as we don't + need them anymore. + FUTURE: parallelize slice_facet() and make_loops() + + NOTE: this method accepts a vector of floats because the mesh coordinate + type is float. + */ + + std::vector lines(z.size()); + + for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { + stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; + + // find facet extents + float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); + float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); + + #ifdef SLIC3R_DEBUG + printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, + facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z, + facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z, + facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z); + printf("z: min = %.2f, max = %.2f\n", min_z, max_z); + #endif + + // find layer extents + std::vector::const_iterator min_layer, max_layer; + min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z + max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z + #ifdef SLIC3R_DEBUG + printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); + #endif + + for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { + std::vector::size_type layer_idx = it - z.begin(); + this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]); + } + } + + // v_scaled_shared could be freed here + + // build loops + layers->resize(z.size()); + for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { + int layer_idx = it - lines.begin(); + #ifdef SLIC3R_DEBUG + printf("Layer %d:\n", layer_idx); + #endif + + this->make_loops(*it, &(*layers)[layer_idx]); + } +} + +void +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) +{ + std::vector layers_p; + this->slice(z, &layers_p); + + layers->resize(z.size()); + for (std::vector::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) { + #ifdef SLIC3R_DEBUG + size_t layer_id = loops - layers_p.begin(); + printf("Layer %zu (slice_z = %.2f): ", layer_id, z[layer_id]); + #endif + + this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]); + } +} + +void +TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const +{ + std::vector points; + std::vector< std::vector::size_type > points_on_layer; + bool found_horizontal_edge = false; + + /* 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 = 0; + if (facet.vertex[1].z == min_z) { + // vertex 1 has lowest Z + i = 1; + } else if (facet.vertex[2].z == min_z) { + // vertex 2 has lowest Z + i = 2; + } + for (int j = i; (j-i) < 3; j++) { // loop through facet edges + int edge_id = this->facets_edges[facet_idx][j % 3]; + int a_id = this->mesh->stl.v_indices[facet_idx].vertex[j % 3]; + int b_id = this->mesh->stl.v_indices[facet_idx].vertex[(j+1) % 3]; + stl_vertex* a = &this->v_scaled_shared[a_id]; + stl_vertex* b = &this->v_scaled_shared[b_id]; + + if (a->z == b->z && a->z == slice_z) { + // edge is horizontal and belongs to the current layer + + /* We assume that this method is never being called for horizontal + facets, so no other edge is going to be on this layer. */ + stl_vertex* v0 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; + stl_vertex* v1 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ]; + stl_vertex* v2 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ]; + IntersectionLine line; + if (min_z == max_z) { + line.edge_type = feHorizontal; + } else if (v0->z < slice_z || v1->z < slice_z || v2->z < slice_z) { + line.edge_type = feTop; + std::swap(a, b); + std::swap(a_id, b_id); + } else { + line.edge_type = feBottom; + } + line.a.x = a->x; + line.a.y = a->y; + line.b.x = b->x; + line.b.y = b->y; + line.a_id = a_id; + line.b_id = b_id; + lines->push_back(line); + + found_horizontal_edge = true; + + // if this is a top or bottom edge, we can stop looping through edges + // because we won't find anything interesting + + if (line.edge_type != feHorizontal) return; + } else if (a->z == slice_z) { + IntersectionPoint point; + point.x = a->x; + point.y = a->y; + point.point_id = a_id; + points.push_back(point); + points_on_layer.push_back(points.size()-1); + } else if (b->z == slice_z) { + IntersectionPoint point; + point.x = b->x; + point.y = b->y; + point.point_id = b_id; + points.push_back(point); + points_on_layer.push_back(points.size()-1); + } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { + // edge intersects the current layer; calculate intersection + + IntersectionPoint point; + 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.edge_id = edge_id; + points.push_back(point); + } + } + if (found_horizontal_edge) return; + + if (!points_on_layer.empty()) { + // we can't have only one point on layer because each vertex gets detected + // twice (once for each edge), and we can't have three points on layer because + // we assume this code is not getting called for horizontal facets + assert(points_on_layer.size() == 2); + assert( points[ points_on_layer[0] ].point_id == points[ points_on_layer[1] ].point_id ); + if (points.size() < 3) return; // no intersection point, this is a V-shaped facet tangent to plane + points.erase( points.begin() + points_on_layer[1] ); + } + + if (!points.empty()) { + assert(points.size() == 2); // facets must intersect each plane 0 or 2 times + IntersectionLine line; + line.a.x = points[1].x; + line.a.y = points[1].y; + line.b.x = points[0].x; + line.b.y = points[0].y; + line.a_id = points[1].point_id; + line.b_id = points[0].point_id; + line.edge_a_id = points[1].edge_id; + line.edge_b_id = points[0].edge_id; + lines->push_back(line); + return; + } +} + +void +TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) +{ + + /* + SVG svg("lines.svg"); + for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { + svg.AddLine(*line); + } + svg.Close(); + */ + + // remove tangent edges + for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { + if (line->skip || line->edge_type == feNone) continue; + + /* if the line is a facet edge, find another facet edge + having the same endpoints but in reverse order */ + for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++line2) { + if (line2->skip || line2->edge_type == feNone) continue; + + // are these facets adjacent? (sharing a common edge on this layer) + if (line->a_id == line2->a_id && line->b_id == line2->b_id) { + line2->skip = true; + + /* if they are both oriented upwards or downwards (like a 'V') + then we can remove both edges from this layer since it won't + affect the sliced shape */ + /* if one of them is oriented upwards and the other is oriented + downwards, let's only keep one of them (it doesn't matter which + one since all 'top' lines were reversed at slicing) */ + if (line->edge_type == line2->edge_type) { + line->skip = true; + break; + } + } else if (line->a_id == line2->b_id && line->b_id == line2->a_id) { + /* if this edge joins two horizontal facets, remove both of them */ + if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) { + line->skip = true; + line2->skip = true; + break; + } + } + } + } + + // build a map of lines by edge_a_id and a_id + std::vector by_edge_a_id, by_a_id; + by_edge_a_id.resize(this->mesh->stl.stats.number_of_facets * 3); + by_a_id.resize(this->mesh->stl.stats.shared_vertices); + for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { + if (line->skip) continue; + if (line->edge_a_id != -1) by_edge_a_id[line->edge_a_id].push_back(&(*line)); + if (line->a_id != -1) by_a_id[line->a_id].push_back(&(*line)); + } + + CYCLE: while (1) { + // take first spare line and start a new loop + IntersectionLine* first_line = NULL; + for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { + if (line->skip) continue; + first_line = &(*line); + break; + } + if (first_line == NULL) break; + first_line->skip = true; + IntersectionLinePtrs loop; + loop.push_back(first_line); + + /* + printf("first_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", + first_line->edge_a_id, first_line->edge_b_id, first_line->a_id, first_line->b_id, + first_line->a.x, first_line->a.y, first_line->b.x, first_line->b.y); + */ + + while (1) { + // find a line starting where last one finishes + IntersectionLine* next_line = NULL; + if (loop.back()->edge_b_id != -1) { + IntersectionLinePtrs* candidates = &(by_edge_a_id[loop.back()->edge_b_id]); + for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) { + if ((*lineptr)->skip) continue; + next_line = *lineptr; + break; + } + } + if (next_line == NULL && loop.back()->b_id != -1) { + IntersectionLinePtrs* candidates = &(by_a_id[loop.back()->b_id]); + for (IntersectionLinePtrs::iterator lineptr = candidates->begin(); lineptr != candidates->end(); ++lineptr) { + if ((*lineptr)->skip) continue; + next_line = *lineptr; + break; + } + } + + if (next_line == NULL) { + // check whether we closed this loop + if ((loop.front()->edge_a_id != -1 && loop.front()->edge_a_id == loop.back()->edge_b_id) + || (loop.front()->a_id != -1 && loop.front()->a_id == loop.back()->b_id)) { + // loop is complete + Polygon p; + p.points.reserve(loop.size()); + for (IntersectionLinePtrs::iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) { + p.points.push_back((*lineptr)->a); + } + loops->push_back(p); + + #ifdef SLIC3R_DEBUG + printf(" Discovered %s polygon of %d points\n", (p.is_counter_clockwise() ? "ccw" : "cw"), (int)p.points.size()); + #endif + + goto CYCLE; + } + + // we can't close this loop! + //// push @failed_loops, [@loop]; + //#ifdef SLIC3R_DEBUG + printf(" Unable to close this loop having %d points\n", (int)loop.size()); + //#endif + goto CYCLE; + } + /* + printf("next_line edge_a_id = %d, edge_b_id = %d, a_id = %d, b_id = %d, a = %d,%d, b = %d,%d\n", + next_line->edge_a_id, next_line->edge_b_id, next_line->a_id, next_line->b_id, + next_line->a.x, next_line->a.y, next_line->b.x, next_line->b.y); + */ + loop.push_back(next_line); + next_line->skip = true; + } + } +} + +class _area_comp { + public: + _area_comp(std::vector* _aa) : abs_area(_aa) {}; + bool operator() (const size_t &a, const size_t &b) { + return (*this->abs_area)[a] > (*this->abs_area)[b]; + } + + private: + std::vector* abs_area; +}; + +void +TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) +{ + Polygons loops; + this->make_loops(lines, &loops); + + Polygons cw; + for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++loop) { + if (loop->area() >= 0) { + ExPolygon ex; + ex.contour = *loop; + slices->push_back(ex); + } else { + cw.push_back(*loop); + } + } + + // assign holes to contours + for (Polygons::const_iterator loop = cw.begin(); loop != cw.end(); ++loop) { + int slice_idx = -1; + double current_contour_area = -1; + for (ExPolygons::iterator slice = slices->begin(); slice != slices->end(); ++slice) { + if (slice->contour.contains_point(loop->points.front())) { + double area = slice->contour.area(); + if (area < current_contour_area || current_contour_area == -1) { + slice_idx = slice - slices->begin(); + current_contour_area = area; + } + } + } + (*slices)[slice_idx].holes.push_back(*loop); + } +} + +void +TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) +{ + /* + Input loops are not suitable for evenodd nor nonzero fill types, as we might get + two consecutive concentric loops having the same winding order - and we have to + respect such order. In that case, evenodd would create wrong inversions, and nonzero + would ignore holes inside two concentric contours. + So we're ordering loops and collapse consecutive concentric loops having the same + winding order. + TODO: find a faster algorithm for this, maybe with some sort of binary search. + If we computed a "nesting tree" we could also just remove the consecutive loops + having the same winding order, and remove the extra one(s) so that we could just + supply everything to offset_ex() instead of performing several union/diff calls. + + we sort by area assuming that the outermost loops have larger area; + the previous sorting method, based on $b->contains_point($a->[0]), failed to nest + loops correctly in some edge cases when original model had overlapping facets + */ + + std::vector area; + std::vector abs_area; + std::vector sorted_area; // vector of indices + for (Polygons::const_iterator loop = loops.begin(); loop != loops.end(); ++loop) { + double a = loop->area(); + area.push_back(a); + abs_area.push_back(std::fabs(a)); + sorted_area.push_back(loop - loops.begin()); + } + + std::sort(sorted_area.begin(), sorted_area.end(), _area_comp(&abs_area)); // outer first + + // we don't perform a safety offset now because it might reverse cw loops + Polygons p_slices; + for (std::vector::const_iterator loop_idx = sorted_area.begin(); loop_idx != sorted_area.end(); ++loop_idx) { + /* we rely on the already computed area to determine the winding order + of the loops, since the Orientation() function provided by Clipper + would do the same, thus repeating the calculation */ + Polygons::const_iterator loop = loops.begin() + *loop_idx; + if (area[*loop_idx] >= 0) { + p_slices.push_back(*loop); + } else { + diff(p_slices, *loop, p_slices); + } + } + + // perform a safety offset to merge very close facets (TODO: find test case for this) + double safety_offset = scale_(0.0499); + ExPolygons ex_slices; + offset2_ex(p_slices, ex_slices, +safety_offset, -safety_offset); + + #ifdef SLIC3R_DEBUG + size_t holes_count = 0; + for (ExPolygons::const_iterator e = ex_slices.begin(); e != ex_slices.end(); ++e) { + holes_count += e->holes.size(); + } + printf("%zu surface(s) having %zu holes detected from %zu polylines\n", + ex_slices.size(), holes_count, loops.size()); + #endif + + // append to the supplied collection + slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); +} + +void +TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) +{ + Polygons pp; + this->make_loops(lines, &pp); + this->make_expolygons(pp, slices); +} + +void +TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) +{ + std::vector upper_lines, lower_lines; + + float scaled_z = scale_(z); + for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { + stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; + + // find facet extents + float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); + float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); + + // intersect facet with cutting plane + std::vector lines; + this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &lines); + + // save intersection lines for generating correct triangulations + for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { + if (it->edge_type == feTop) { + lower_lines.push_back(*it); + } else if (it->edge_type == feBottom) { + upper_lines.push_back(*it); + } else if (it->edge_type != feHorizontal) { + lower_lines.push_back(*it); + upper_lines.push_back(*it); + } + } + + if (min_z > z || (min_z == z && max_z > min_z)) { + // facet is above the cut plane and does not belong to it + if (upper != NULL) stl_add_facet(&upper->stl, facet); + } else if (max_z < z || (max_z == z && max_z > min_z)) { + // facet is below the cut plane and does not belong to it + if (lower != NULL) stl_add_facet(&lower->stl, facet); + } else if (min_z < z && max_z > z) { + // facet is cut by the slicing plane + + // 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) ) { + isolated_vertex = 2; + } else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > 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]; + + // 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; + + // build the triangular facet + stl_facet triangle; + triangle.normal = facet->normal; + 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[2] = v0v1; + quadrilateral[1].normal = facet->normal; + quadrilateral[1].vertex[0] = *v2; + quadrilateral[1].vertex[1] = v2v0; + quadrilateral[1].vertex[2] = v0v1; + + if (v0->z > z) { + if (upper != NULL) stl_add_facet(&upper->stl, &triangle); + if (lower != NULL) { + stl_add_facet(&lower->stl, &quadrilateral[0]); + stl_add_facet(&lower->stl, &quadrilateral[1]); + } + } else { + if (upper != NULL) { + stl_add_facet(&upper->stl, &quadrilateral[0]); + stl_add_facet(&upper->stl, &quadrilateral[1]); + } + if (lower != NULL) stl_add_facet(&lower->stl, &triangle); + } + } + } + + // triangulate holes of upper mesh + if (upper != NULL) { + // compute shape of section + ExPolygons section; + this->make_expolygons_simple(upper_lines, §ion); + + // triangulate section + Polygons triangles; + for (ExPolygons::const_iterator expolygon = section.begin(); expolygon != section.end(); ++expolygon) + expolygon->triangulate_p2t(&triangles); + + // convert triangles to facets and append them to mesh + for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { + Polygon p = *polygon; + p.reverse(); + stl_facet facet; + facet.normal.x = 0; + facet.normal.y = 0; + facet.normal.z = -1; + 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; + } + stl_add_facet(&upper->stl, &facet); + } + } + + // triangulate holes of lower mesh + if (lower != NULL) { + // compute shape of section + ExPolygons section; + this->make_expolygons_simple(lower_lines, §ion); + + // triangulate section + Polygons triangles; + for (ExPolygons::const_iterator expolygon = section.begin(); expolygon != section.end(); ++expolygon) + expolygon->triangulate_p2t(&triangles); + + // 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; + 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; + } + stl_add_facet(&lower->stl, &facet); + } + } + + + stl_get_size(&(upper->stl)); + stl_get_size(&(lower->stl)); + +} + +TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) +{ + // build a table to map a facet_idx to its three edge indices + this->mesh->require_shared_vertices(); + typedef std::pair t_edge; + typedef std::vector t_edges; // edge_idx => a_id,b_id + typedef std::map t_edges_map; // a_id,b_id => edge_idx + + this->facets_edges.resize(this->mesh->stl.stats.number_of_facets); + + { + t_edges edges; + // reserve() instad of resize() because otherwise we couldn't read .size() below to assign edge_idx + edges.reserve(this->mesh->stl.stats.number_of_facets * 3); // number of edges = number of facets * 3 + t_edges_map edges_map; + for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { + this->facets_edges[facet_idx].resize(3); + for (int i = 0; i <= 2; i++) { + int a_id = this->mesh->stl.v_indices[facet_idx].vertex[i]; + int b_id = this->mesh->stl.v_indices[facet_idx].vertex[(i+1) % 3]; + + int edge_idx; + t_edges_map::const_iterator my_edge = edges_map.find(std::make_pair(b_id,a_id)); + if (my_edge != edges_map.end()) { + edge_idx = my_edge->second; + } else { + /* admesh can assign the same edge ID to more than two facets (which is + still topologically correct), so we have to search for a duplicate of + this edge too in case it was already seen in this orientation */ + my_edge = edges_map.find(std::make_pair(a_id,b_id)); + + if (my_edge != edges_map.end()) { + edge_idx = my_edge->second; + } else { + // edge isn't listed in table, so we insert it + edge_idx = edges.size(); + edges.push_back(std::make_pair(a_id,b_id)); + edges_map[ edges[edge_idx] ] = edge_idx; + } + } + this->facets_edges[facet_idx][i] = edge_idx; + + #ifdef SLIC3R_DEBUG + printf(" [facet %d, edge %d] a_id = %d, b_id = %d --> edge %d\n", facet_idx, i, a_id, b_id, edge_idx); + #endif + } + } + } + + // clone shared vertices coordinates and scale them + this->v_scaled_shared = (stl_vertex*)calloc(this->mesh->stl.stats.shared_vertices, sizeof(stl_vertex)); + std::copy(this->mesh->stl.v_shared, this->mesh->stl.v_shared + this->mesh->stl.stats.shared_vertices, this->v_scaled_shared); + for (int i = 0; i < this->mesh->stl.stats.shared_vertices; i++) { + this->v_scaled_shared[i].x /= SCALING_FACTOR; + this->v_scaled_shared[i].y /= SCALING_FACTOR; + this->v_scaled_shared[i].z /= SCALING_FACTOR; + } +} + +TriangleMeshSlicer::~TriangleMeshSlicer() +{ + if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); +} + +} diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp new file mode 100644 index 000000000..b16f867eb --- /dev/null +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -0,0 +1,110 @@ +#ifndef slic3r_TriangleMesh_hpp_ +#define slic3r_TriangleMesh_hpp_ + +#include +#include +#include +#include "BoundingBox.hpp" +#include "Point.hpp" +#include "Polygon.hpp" +#include "ExPolygon.hpp" + +namespace Slic3r { + +class TriangleMesh; +class TriangleMeshSlicer; +typedef std::vector TriangleMeshPtrs; + +class TriangleMesh +{ + public: + TriangleMesh(); + TriangleMesh(const TriangleMesh &other); + TriangleMesh& operator= (TriangleMesh other); + void swap(TriangleMesh &other); + ~TriangleMesh(); + void ReadSTLFile(char* input_file); + void write_ascii(char* output_file); + void write_binary(char* output_file); + void repair(); + void WriteOBJFile(char* output_file); + void scale(float factor); + void scale(std::vector versor); + void translate(float x, float y, float z); + void rotate_x(float angle); + void rotate_y(float angle); + void rotate_z(float angle); + void flip_x(); + void flip_y(); + void flip_z(); + void align_to_origin(); + void rotate(double angle, Point* center); + TriangleMeshPtrs split() const; + void merge(const TriangleMesh* mesh); + void horizontal_projection(ExPolygons &retval) const; + void convex_hull(Polygon* hull); + void bounding_box(BoundingBoxf3* bb) const; + void reset_repair_stats(); + stl_file stl; + bool repaired; + + #ifdef SLIC3RXS + SV* to_SV(); + void ReadFromPerl(SV* vertices, SV* facets); + #endif + + private: + void require_shared_vertices(); + friend class TriangleMeshSlicer; +}; + +enum FacetEdgeType { feNone, feTop, feBottom, feHorizontal }; + +class IntersectionPoint : public Point +{ + public: + int point_id; + int edge_id; + IntersectionPoint() : point_id(-1), edge_id(-1) {}; +}; + +class IntersectionLine +{ + public: + Point a; + Point b; + int a_id; + int b_id; + int edge_a_id; + int edge_b_id; + FacetEdgeType edge_type; + bool skip; + IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {}; +}; +typedef std::vector IntersectionLines; +typedef std::vector IntersectionLinePtrs; + +class TriangleMeshSlicer +{ + public: + TriangleMesh* mesh; + TriangleMeshSlicer(TriangleMesh* _mesh); + ~TriangleMeshSlicer(); + void slice(const std::vector &z, std::vector* layers); + void slice(const std::vector &z, std::vector* layers); + void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const; + void cut(float z, TriangleMesh* upper, TriangleMesh* lower); + + private: + typedef std::vector< std::vector > t_facets_edges; + t_facets_edges facets_edges; + stl_vertex* v_scaled_shared; + void make_loops(std::vector &lines, Polygons* loops); + void make_expolygons(const Polygons &loops, ExPolygons* slices); + void make_expolygons_simple(std::vector &lines, ExPolygons* slices); + void make_expolygons(std::vector &lines, ExPolygons* slices); +}; + +} + +#endif diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h new file mode 100644 index 000000000..072e97ffb --- /dev/null +++ b/xs/src/libslic3r/libslic3r.h @@ -0,0 +1,26 @@ +#ifndef _libslic3r_h_ +#define _libslic3r_h_ + +// this needs to be included early for MSVC (listing it in Build.PL is not enough) +#include +#include +#include + +#define EPSILON 1e-4 +#define SCALING_FACTOR 0.000001 +#define PI 3.141592653589793238 +#define scale_(val) (val / SCALING_FACTOR) +#define unscale(val) (val * SCALING_FACTOR) +#define SCALED_EPSILON scale_(EPSILON) +typedef long coord_t; +typedef double coordf_t; + +namespace Slic3r {} +using namespace Slic3r; + +/* Implementation of CONFESS("foo"): */ +#define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) +void confess_at(const char *file, int line, const char *func, const char *pat, ...); +/* End implementation of CONFESS("foo"): */ + +#endif diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp new file mode 100644 index 000000000..d0b1e879b --- /dev/null +++ b/xs/src/libslic3r/utils.cpp @@ -0,0 +1,28 @@ +#include + +void +confess_at(const char *file, int line, const char *func, + const char *pat, ...) +{ + #ifdef SLIC3RXS + va_list args; + SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func, + file, line); + + va_start(args, pat); + sv_vcatpvf(error_sv, pat, &args); + va_end(args); + + sv_catpvn(error_sv, "\n\t", 2); + + dSP; + ENTER; + SAVETMPS; + PUSHMARK(SP); + XPUSHs( sv_2mortal(error_sv) ); + PUTBACK; + call_pv("Carp::confess", G_DISCARD); + FREETMPS; + LEAVE; + #endif +} diff --git a/xs/src/myinit.h b/xs/src/myinit.h index c3c8c6ff3..f98d94020 100644 --- a/xs/src/myinit.h +++ b/xs/src/myinit.h @@ -20,21 +20,6 @@ extern "C" { #include "perlglue.hpp" #endif -#define EPSILON 1e-4 -#define SCALING_FACTOR 0.000001 -#define PI 3.141592653589793238 -#define scale_(val) (val / SCALING_FACTOR) -#define unscale(val) (val * SCALING_FACTOR) -#define SCALED_EPSILON scale_(EPSILON) -typedef long coord_t; -typedef double coordf_t; - -namespace Slic3r {} -using namespace Slic3r; - -/* Implementation of CONFESS("foo"): */ -#define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) -void confess_at(const char *file, int line, const char *func, const char *pat, ...); -/* End implementation of CONFESS("foo"): */ +#include "libslic3r/libslic3r.h" #endif diff --git a/xs/src/utils.cpp b/xs/src/utils.cpp deleted file mode 100644 index d0b1e879b..000000000 --- a/xs/src/utils.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -void -confess_at(const char *file, int line, const char *func, - const char *pat, ...) -{ - #ifdef SLIC3RXS - va_list args; - SV *error_sv = newSVpvf("Error in function %s at %s:%d: ", func, - file, line); - - va_start(args, pat); - sv_vcatpvf(error_sv, pat, &args); - va_end(args); - - sv_catpvn(error_sv, "\n\t", 2); - - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - XPUSHs( sv_2mortal(error_sv) ); - PUTBACK; - call_pv("Carp::confess", G_DISCARD); - FREETMPS; - LEAVE; - #endif -} diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index 5a4368b88..c368c0c18 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -2,8 +2,8 @@ %{ #include -#include "BoundingBox.hpp" -#include "Point.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/Point.hpp" %} %name{Slic3r::Geometry::BoundingBox} class BoundingBox { diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp index 43a30a406..fc82ed88a 100644 --- a/xs/xsp/Clipper.xsp +++ b/xs/xsp/Clipper.xsp @@ -3,7 +3,7 @@ %{ #include #include "clipper.hpp" -#include "ClipperUtils.hpp" +#include "libslic3r/ClipperUtils.hpp" %} %package{Slic3r::Geometry::Clipper}; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index f96f45b8a..4c56e10c8 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -2,7 +2,7 @@ %{ #include -#include "PrintConfig.hpp" +#include "libslic3r/PrintConfig.hpp" %} %name{Slic3r::Config} class DynamicPrintConfig { diff --git a/xs/xsp/ExPolygon.xsp b/xs/xsp/ExPolygon.xsp index aefbd1aed..ef43e3514 100644 --- a/xs/xsp/ExPolygon.xsp +++ b/xs/xsp/ExPolygon.xsp @@ -2,7 +2,7 @@ %{ #include -#include "ExPolygon.hpp" +#include "libslic3r/ExPolygon.hpp" %} %name{Slic3r::ExPolygon} class ExPolygon { diff --git a/xs/xsp/ExPolygonCollection.xsp b/xs/xsp/ExPolygonCollection.xsp index c718c7ebc..a93b27dda 100644 --- a/xs/xsp/ExPolygonCollection.xsp +++ b/xs/xsp/ExPolygonCollection.xsp @@ -2,7 +2,7 @@ %{ #include -#include "ExPolygonCollection.hpp" +#include "libslic3r/ExPolygonCollection.hpp" %} %name{Slic3r::ExPolygon::Collection} class ExPolygonCollection { diff --git a/xs/xsp/Extruder.xsp b/xs/xsp/Extruder.xsp index d80b34d62..582b39b99 100644 --- a/xs/xsp/Extruder.xsp +++ b/xs/xsp/Extruder.xsp @@ -2,7 +2,7 @@ %{ #include -#include "Extruder.hpp" +#include "libslic3r/Extruder.hpp" %} %name{Slic3r::Extruder} class Extruder { diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp index b8c65c87f..fa1d26eed 100644 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ b/xs/xsp/ExtrusionEntityCollection.xsp @@ -2,7 +2,7 @@ %{ #include -#include "ExtrusionEntityCollection.hpp" +#include "libslic3r/ExtrusionEntityCollection.hpp" %} %name{Slic3r::ExtrusionPath::Collection} class ExtrusionEntityCollection { diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 489bca03a..1256d7593 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -2,7 +2,7 @@ %{ #include -#include "ExtrusionEntity.hpp" +#include "libslic3r/ExtrusionEntity.hpp" %} %name{Slic3r::ExtrusionLoop} class ExtrusionLoop { diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp index df3813d51..d3a48ac24 100644 --- a/xs/xsp/ExtrusionPath.xsp +++ b/xs/xsp/ExtrusionPath.xsp @@ -2,8 +2,8 @@ %{ #include -#include "ExtrusionEntity.hpp" -#include "ExtrusionEntityCollection.hpp" +#include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/ExtrusionEntityCollection.hpp" %} %name{Slic3r::ExtrusionPath} class ExtrusionPath { diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp index 09c6fd576..805c1a7a1 100644 --- a/xs/xsp/Flow.xsp +++ b/xs/xsp/Flow.xsp @@ -2,7 +2,7 @@ %{ #include -#include "Flow.hpp" +#include "libslic3r/Flow.hpp" %} %name{Slic3r::Flow} class Flow { diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index acbfb0875..ddfa0a369 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -2,7 +2,7 @@ %{ #include -#include "Geometry.hpp" +#include "libslic3r/Geometry.hpp" %} diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index 8bdbef54f..b758989e6 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -2,8 +2,7 @@ %{ #include -#include "Layer.hpp" -#include "perlglue.hpp" +#include "libslic3r/Layer.hpp" %} %name{Slic3r::Layer::Region} class LayerRegion { diff --git a/xs/xsp/Line.xsp b/xs/xsp/Line.xsp index 22bd8e9e9..d0552315f 100644 --- a/xs/xsp/Line.xsp +++ b/xs/xsp/Line.xsp @@ -2,8 +2,8 @@ %{ #include -#include "Line.hpp" -#include "Polyline.hpp" +#include "libslic3r/Line.hpp" +#include "libslic3r/Polyline.hpp" %} %name{Slic3r::Line} class Line { diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index aaf5c1e75..8947558eb 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -2,8 +2,8 @@ %{ #include -#include "Model.hpp" -#include "PrintConfig.hpp" +#include "libslic3r/Model.hpp" +#include "libslic3r/PrintConfig.hpp" %} %name{Slic3r::Model} class Model { diff --git a/xs/xsp/MotionPlanner.xsp b/xs/xsp/MotionPlanner.xsp index 57abef937..ad29bc0f7 100644 --- a/xs/xsp/MotionPlanner.xsp +++ b/xs/xsp/MotionPlanner.xsp @@ -2,7 +2,7 @@ %{ #include -#include "MotionPlanner.hpp" +#include "libslic3r/MotionPlanner.hpp" %} %name{Slic3r::MotionPlanner} class MotionPlanner { diff --git a/xs/xsp/PlaceholderParser.xsp b/xs/xsp/PlaceholderParser.xsp index 1f027a0d7..4b508f77a 100644 --- a/xs/xsp/PlaceholderParser.xsp +++ b/xs/xsp/PlaceholderParser.xsp @@ -3,7 +3,7 @@ %{ #include #include -#include "PlaceholderParser.hpp" +#include "libslic3r/PlaceholderParser.hpp" %} %name{Slic3r::GCode::PlaceholderParser} class PlaceholderParser { diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index 21c8d9ab1..774b2fc1a 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -2,9 +2,9 @@ %{ #include -#include "Point.hpp" -#include "Polygon.hpp" -#include "Polyline.hpp" +#include "libslic3r/Point.hpp" +#include "libslic3r/Polygon.hpp" +#include "libslic3r/Polyline.hpp" %} %name{Slic3r::Point} class Point { diff --git a/xs/xsp/Polygon.xsp b/xs/xsp/Polygon.xsp index cabf62adc..653691e49 100644 --- a/xs/xsp/Polygon.xsp +++ b/xs/xsp/Polygon.xsp @@ -2,9 +2,9 @@ %{ #include -#include "BoundingBox.hpp" -#include "Polygon.hpp" -#include "BoundingBox.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/Polygon.hpp" +#include "libslic3r/BoundingBox.hpp" %} %name{Slic3r::Polygon} class Polygon { diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index e43405df5..e97897719 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -2,9 +2,9 @@ %{ #include -#include "BoundingBox.hpp" -#include "ClipperUtils.hpp" -#include "Polyline.hpp" +#include "libslic3r/BoundingBox.hpp" +#include "libslic3r/ClipperUtils.hpp" +#include "libslic3r/Polyline.hpp" %} %name{Slic3r::Polyline} class Polyline { diff --git a/xs/xsp/PolylineCollection.xsp b/xs/xsp/PolylineCollection.xsp index d5ad4f77f..f512bbb63 100644 --- a/xs/xsp/PolylineCollection.xsp +++ b/xs/xsp/PolylineCollection.xsp @@ -2,7 +2,7 @@ %{ #include -#include "PolylineCollection.hpp" +#include "libslic3r/PolylineCollection.hpp" %} %name{Slic3r::Polyline::Collection} class PolylineCollection { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 39a094e55..330bc6b9a 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -2,8 +2,8 @@ %{ #include -#include "Print.hpp" -#include "PlaceholderParser.hpp" +#include "libslic3r/Print.hpp" +#include "libslic3r/PlaceholderParser.hpp" %} %package{Slic3r::Print::State}; diff --git a/xs/xsp/Surface.xsp b/xs/xsp/Surface.xsp index 9aaa8a2fb..11c7ec744 100644 --- a/xs/xsp/Surface.xsp +++ b/xs/xsp/Surface.xsp @@ -2,8 +2,8 @@ %{ #include -#include "Surface.hpp" -#include "ClipperUtils.hpp" +#include "libslic3r/Surface.hpp" +#include "libslic3r/ClipperUtils.hpp" %} %name{Slic3r::Surface} class Surface { diff --git a/xs/xsp/SurfaceCollection.xsp b/xs/xsp/SurfaceCollection.xsp index 77295e881..ea6a6b7bf 100644 --- a/xs/xsp/SurfaceCollection.xsp +++ b/xs/xsp/SurfaceCollection.xsp @@ -2,7 +2,7 @@ %{ #include -#include "SurfaceCollection.hpp" +#include "libslic3r/SurfaceCollection.hpp" %} %name{Slic3r::Surface::Collection} class SurfaceCollection { diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 7baa50644..c6f7f7d04 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -2,7 +2,7 @@ %{ #include -#include "TriangleMesh.hpp" +#include "libslic3r/TriangleMesh.hpp" %} %name{Slic3r::TriangleMesh} class TriangleMesh { -- cgit v1.2.3