mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-23 14:44:19 -06:00
Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_project_dirty_state
This commit is contained in:
commit
ede14251b1
199 changed files with 11027 additions and 6477 deletions
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "BoundingBox.hpp"
|
||||
|
||||
#include <libnest2d/backends/clipper/geometries.hpp>
|
||||
#include <libnest2d/backends/libslic3r/geometries.hpp>
|
||||
#include <libnest2d/optimizers/nlopt/subplex.hpp>
|
||||
#include <libnest2d/placers/nfpplacer.hpp>
|
||||
#include <libnest2d/selections/firstfit.hpp>
|
||||
|
@ -54,23 +54,22 @@ namespace Slic3r {
|
|||
|
||||
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
|
||||
inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
|
||||
const ClipperLib::IntPoint &v) noexcept
|
||||
const Slic3r::ClipperLib::IntPoint &v) noexcept
|
||||
{
|
||||
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X),
|
||||
unscaled<Tout>(v.Y)};
|
||||
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.x()),
|
||||
unscaled<Tout>(v.y())};
|
||||
}
|
||||
|
||||
namespace arrangement {
|
||||
|
||||
using namespace libnest2d;
|
||||
namespace clppr = ClipperLib;
|
||||
|
||||
// Get the libnest2d types for clipper backend
|
||||
using Item = _Item<clppr::Polygon>;
|
||||
using Box = _Box<clppr::IntPoint>;
|
||||
using Circle = _Circle<clppr::IntPoint>;
|
||||
using Segment = _Segment<clppr::IntPoint>;
|
||||
using MultiPolygon = TMultiShape<clppr::Polygon>;
|
||||
using Item = _Item<ExPolygon>;
|
||||
using Box = _Box<Point>;
|
||||
using Circle = _Circle<Point>;
|
||||
using Segment = _Segment<Point>;
|
||||
using MultiPolygon = ExPolygons;
|
||||
|
||||
// Summon the spatial indexing facilities from boost
|
||||
namespace bgi = boost::geometry::index;
|
||||
|
@ -127,8 +126,8 @@ template<class TBin>
|
|||
class AutoArranger {
|
||||
public:
|
||||
// Useful type shortcuts...
|
||||
using Placer = typename placers::_NofitPolyPlacer<clppr::Polygon, TBin>;
|
||||
using Selector = selections::_FirstFitSelection<clppr::Polygon>;
|
||||
using Placer = typename placers::_NofitPolyPlacer<ExPolygon, TBin>;
|
||||
using Selector = selections::_FirstFitSelection<ExPolygon>;
|
||||
using Packer = _Nester<Placer, Selector>;
|
||||
using PConfig = typename Packer::PlacementConfig;
|
||||
using Distance = TCoord<PointImpl>;
|
||||
|
@ -168,7 +167,7 @@ protected:
|
|||
// as it possibly can be but at the same time, it has to provide
|
||||
// reasonable results.
|
||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||
objfunc(const Item &item, const clppr::IntPoint &bincenter)
|
||||
objfunc(const Item &item, const Point &bincenter)
|
||||
{
|
||||
const double bin_area = m_bin_area;
|
||||
const SpatIndex& spatindex = m_rtree;
|
||||
|
@ -220,12 +219,12 @@ protected:
|
|||
|
||||
switch (compute_case) {
|
||||
case BIG_ITEM: {
|
||||
const clppr::IntPoint& minc = ibb.minCorner(); // bottom left corner
|
||||
const clppr::IntPoint& maxc = ibb.maxCorner(); // top right corner
|
||||
const Point& minc = ibb.minCorner(); // bottom left corner
|
||||
const Point& maxc = ibb.maxCorner(); // top right corner
|
||||
|
||||
// top left and bottom right corners
|
||||
clppr::IntPoint top_left{getX(minc), getY(maxc)};
|
||||
clppr::IntPoint bottom_right{getX(maxc), getY(minc)};
|
||||
Point top_left{getX(minc), getY(maxc)};
|
||||
Point bottom_right{getX(maxc), getY(minc)};
|
||||
|
||||
// Now the distance of the gravity center will be calculated to the
|
||||
// five anchor points and the smallest will be chosen.
|
||||
|
@ -452,7 +451,7 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
|
|||
// Specialization for a generalized polygon.
|
||||
// Warning: this is unfinished business. It may or may not work.
|
||||
template<>
|
||||
std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn()
|
||||
std::function<double(const Item &)> AutoArranger<ExPolygon>::get_objfn()
|
||||
{
|
||||
auto bincenter = sl::boundingBox(m_bin).center();
|
||||
return [this, bincenter](const Item &item) {
|
||||
|
@ -521,7 +520,7 @@ void _arrange(
|
|||
|
||||
inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
|
||||
inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
|
||||
inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create<clppr::Polygon>(Slic3rMultiPoint_to_ClipperPath(p)); }
|
||||
inline ExPolygon to_nestbin(const Polygon &p) { return ExPolygon{p}; }
|
||||
inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
|
||||
|
||||
inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
|
||||
|
@ -568,19 +567,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
|
|||
const Vec2crd &offs = arrpoly.translation;
|
||||
double rotation = arrpoly.rotation;
|
||||
|
||||
if (p.is_counter_clockwise()) p.reverse();
|
||||
|
||||
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
|
||||
|
||||
// This fixes:
|
||||
// https://github.com/prusa3d/PrusaSlicer/issues/2209
|
||||
if (clpath.Contour.size() < 3)
|
||||
if (p.points.size() < 3)
|
||||
return;
|
||||
|
||||
auto firstp = clpath.Contour.front();
|
||||
clpath.Contour.emplace_back(firstp);
|
||||
|
||||
outp.emplace_back(std::move(clpath));
|
||||
outp.emplace_back(std::move(p));
|
||||
outp.back().rotation(rotation);
|
||||
outp.back().translation({offs.x(), offs.y()});
|
||||
outp.back().binId(arrpoly.bed_idx);
|
||||
|
@ -624,7 +616,7 @@ void arrange(ArrangePolygons & arrangables,
|
|||
const BedT & bed,
|
||||
const ArrangeParams & params)
|
||||
{
|
||||
namespace clppr = ClipperLib;
|
||||
namespace clppr = Slic3r::ClipperLib;
|
||||
|
||||
std::vector<Item> items, fixeditems;
|
||||
items.reserve(arrangables.size());
|
||||
|
@ -643,8 +635,8 @@ void arrange(ArrangePolygons & arrangables,
|
|||
_arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn);
|
||||
|
||||
for(size_t i = 0; i < items.size(); ++i) {
|
||||
clppr::IntPoint tr = items[i].translation();
|
||||
arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)};
|
||||
Point tr = items[i].translation();
|
||||
arrangables[i].translation = {coord_t(tr.x()), coord_t(tr.y())};
|
||||
arrangables[i].rotation = items[i].rotation();
|
||||
arrangables[i].bed_idx = items[i].binId();
|
||||
}
|
||||
|
|
|
@ -225,24 +225,11 @@ BoundingBox3Base<PointClass>::max_size() const
|
|||
template coordf_t BoundingBox3Base<Vec3f>::max_size() const;
|
||||
template coordf_t BoundingBox3Base<Vec3d>::max_size() const;
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
// the aligned value will never be bigger than the original one.
|
||||
static inline coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
|
||||
// Current C++ standard defines the result of integer division to be rounded to zero,
|
||||
// for both positive and negative numbers. Here we want to round down for negative
|
||||
// numbers as well.
|
||||
coord_t aligned = (coord < 0) ?
|
||||
((coord - spacing + 1) / spacing) * spacing :
|
||||
(coord / spacing) * spacing;
|
||||
assert(aligned <= coord);
|
||||
return aligned;
|
||||
}
|
||||
|
||||
void BoundingBox::align_to_grid(const coord_t cell_size)
|
||||
{
|
||||
if (this->defined) {
|
||||
min(0) = _align_to_grid(min(0), cell_size);
|
||||
min(1) = _align_to_grid(min(1), cell_size);
|
||||
min(0) = Slic3r::align_to_grid(min(0), cell_size);
|
||||
min(1) = Slic3r::align_to_grid(min(1), cell_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ void BridgeDetector::initialize()
|
|||
this->angle = -1.;
|
||||
|
||||
// Outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors.
|
||||
Polygons grown = offset(to_polygons(this->expolygons), float(this->spacing));
|
||||
Polygons grown = offset(this->expolygons, float(this->spacing));
|
||||
|
||||
// Detect possible anchoring edges of this bridging region.
|
||||
// Detect what edges lie on lower slices by turning bridge contour and holes
|
||||
|
@ -227,29 +227,33 @@ void ExPolygon::get_trapezoids(ExPolygon clone, Polygons* polygons, double angle
|
|||
// 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)
|
||||
static void get_trapezoids2(const ExPolygon &expoly, Polygons* polygons)
|
||||
static void get_trapezoids2(const ExPolygon& expoly, Polygons* polygons)
|
||||
{
|
||||
Polygons src_polygons = to_polygons(expoly);
|
||||
// get all points of this ExPolygon
|
||||
const Points pp = to_points(src_polygons);
|
||||
|
||||
const Points pp = to_points(src_polygons);
|
||||
|
||||
// build our bounding box
|
||||
BoundingBox bb(pp);
|
||||
|
||||
|
||||
// get all x coordinates
|
||||
std::vector<coord_t> xx;
|
||||
xx.reserve(pp.size());
|
||||
for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p)
|
||||
xx.push_back(p->x());
|
||||
std::sort(xx.begin(), xx.end());
|
||||
|
||||
|
||||
// find trapezoids by looping from first to next-to-last coordinate
|
||||
Polygons rectangle;
|
||||
rectangle.emplace_back(Polygon());
|
||||
for (std::vector<coord_t>::const_iterator x = xx.begin(); x != xx.end()-1; ++x) {
|
||||
coord_t next_x = *(x + 1);
|
||||
if (*x != next_x)
|
||||
if (*x != next_x) {
|
||||
// intersect with rectangle
|
||||
// append results to return value
|
||||
polygons_append(*polygons, intersection({ { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } } }, src_polygons));
|
||||
rectangle.front() = { { *x, bb.min.y() }, { next_x, bb.min.y() }, { next_x, bb.max.y() }, { *x, bb.max.y() } };
|
||||
polygons_append(*polygons, intersection(rectangle, src_polygons));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,7 +306,7 @@ Polygons BridgeDetector::coverage(double angle) const
|
|||
covered = union_(covered);
|
||||
// Intersect trapezoids with actual bridge area to remove extra margins and append it to result.
|
||||
polygons_rotate(covered, -(PI/2.0 - angle));
|
||||
covered = intersection(covered, to_polygons(this->expolygons));
|
||||
covered = intersection(this->expolygons, covered);
|
||||
#if 0
|
||||
{
|
||||
my @lines = map @{$_->lines}, @$trapezoids;
|
||||
|
|
|
@ -78,7 +78,7 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print)
|
|||
// Assign the maximum Z from four points. This values is valid index of the island
|
||||
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
|
||||
const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
|
||||
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z));
|
||||
pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
|
||||
});
|
||||
// Add islands
|
||||
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
|
||||
|
@ -90,9 +90,9 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print)
|
|||
ConstPrintObjectPtrs top_level_objects_with_brim;
|
||||
for (int i = 0; i < islands_polytree.ChildCount(); ++i) {
|
||||
for (const ClipperLib_Z::IntPoint &point : islands_polytree.Childs[i]->Contour) {
|
||||
if (point.Z != 0 && processed_objects_idx.find(island_to_object[point.Z - 1]->id().id) == processed_objects_idx.end()) {
|
||||
top_level_objects_with_brim.emplace_back(island_to_object[point.Z - 1]);
|
||||
processed_objects_idx.insert(island_to_object[point.Z - 1]->id().id);
|
||||
if (point.z() != 0 && processed_objects_idx.find(island_to_object[point.z() - 1]->id().id) == processed_objects_idx.end()) {
|
||||
top_level_objects_with_brim.emplace_back(island_to_object[point.z() - 1]);
|
||||
processed_objects_idx.insert(island_to_object[point.z() - 1]->id().id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint
|
|||
Polygons no_brim_area_object;
|
||||
for (const ExPolygon &ex_poly : object->layers().front()->lslices) {
|
||||
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim)
|
||||
append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset)));
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
|
||||
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset(ex_poly.holes, -no_brim_offset));
|
||||
|
@ -156,7 +156,7 @@ static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrint
|
|||
}
|
||||
}
|
||||
|
||||
return diff_ex(to_polygons(std::move(brim_area)), no_brim_area);
|
||||
return diff_ex(brim_area, no_brim_area);
|
||||
}
|
||||
|
||||
static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset)
|
||||
|
@ -183,14 +183,14 @@ static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs
|
|||
if (top_outer_brim)
|
||||
no_brim_area_object.emplace_back(ex_poly);
|
||||
else
|
||||
append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset)));
|
||||
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_offset), offset(ex_poly.contour, brim_offset)));
|
||||
}
|
||||
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
|
||||
append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
|
||||
|
||||
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset_ex(ex_poly.contour, no_brim_offset));
|
||||
append(no_brim_area_object, to_expolygons(offset(ex_poly.contour, no_brim_offset)));
|
||||
|
||||
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
||||
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
|
||||
|
@ -317,7 +317,7 @@ static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_
|
|||
islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare);
|
||||
}
|
||||
|
||||
loops = union_pt_chained_outside_in(loops, false);
|
||||
loops = union_pt_chained_outside_in(loops);
|
||||
std::reverse(loops.begin(), loops.end());
|
||||
extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()),
|
||||
float(flow.width()), float(print.skirt_first_layer_height()));
|
||||
|
@ -342,7 +342,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||
poly.douglas_peucker(SCALED_RESOLUTION);
|
||||
polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing())));
|
||||
}
|
||||
loops = union_pt_chained_outside_in(loops, false);
|
||||
loops = union_pt_chained_outside_in(loops);
|
||||
|
||||
std::vector<Polylines> loops_pl_by_levels;
|
||||
{
|
||||
|
@ -456,7 +456,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
|
||||
// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
|
||||
// hat the Z coordinate not set to the contour coordinate.
|
||||
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z));
|
||||
pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
|
||||
});
|
||||
// add polygons
|
||||
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
|
||||
|
@ -474,8 +474,8 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||
for (const ClipperLib_Z::Path &path : loops_trimmed) {
|
||||
size_t input_idx = 0;
|
||||
for (const ClipperLib_Z::IntPoint &pt : path)
|
||||
if (pt.Z > 0) {
|
||||
input_idx = (size_t)pt.Z;
|
||||
if (pt.z() > 0) {
|
||||
input_idx = (size_t)pt.z();
|
||||
break;
|
||||
}
|
||||
assert(input_idx != 0);
|
||||
|
@ -492,14 +492,14 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||
size_t j = i + 1;
|
||||
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ;
|
||||
const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
|
||||
if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) {
|
||||
if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) {
|
||||
auto *loop = new ExtrusionLoop();
|
||||
brim.entities.emplace_back(loop);
|
||||
loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
|
||||
Points &points = loop->paths.front().polyline.points;
|
||||
points.reserve(first_path.size());
|
||||
for (const ClipperLib_Z::IntPoint &pt : first_path)
|
||||
points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
|
||||
points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
|
||||
i = j;
|
||||
} else {
|
||||
//FIXME The path chaining here may not be optimal.
|
||||
|
@ -511,7 +511,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
|
|||
Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
|
||||
points.reserve(path.size());
|
||||
for (const ClipperLib_Z::IntPoint &pt : path)
|
||||
points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
|
||||
points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
|
||||
}
|
||||
chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
|
||||
brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size());
|
||||
|
|
|
@ -23,6 +23,8 @@ add_library(libslic3r STATIC
|
|||
BridgeDetector.hpp
|
||||
Brim.cpp
|
||||
Brim.hpp
|
||||
clipper.cpp
|
||||
clipper.hpp
|
||||
ClipperUtils.cpp
|
||||
ClipperUtils.hpp
|
||||
Config.cpp
|
||||
|
@ -219,6 +221,9 @@ add_library(libslic3r STATIC
|
|||
SimplifyMeshImpl.hpp
|
||||
SimplifyMesh.cpp
|
||||
MarchingSquares.hpp
|
||||
Execution/Execution.hpp
|
||||
Execution/ExecutionSeq.hpp
|
||||
Execution/ExecutionTBB.hpp
|
||||
Optimize/Optimizer.hpp
|
||||
Optimize/NLoptOptimizer.hpp
|
||||
Optimize/BruteforceOptimizer.hpp
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,218 +8,354 @@
|
|||
#include "Surface.hpp"
|
||||
|
||||
// import these wherever we're included
|
||||
using ClipperLib::jtMiter;
|
||||
using ClipperLib::jtRound;
|
||||
using ClipperLib::jtSquare;
|
||||
using Slic3r::ClipperLib::jtMiter;
|
||||
using Slic3r::ClipperLib::jtRound;
|
||||
using Slic3r::ClipperLib::jtSquare;
|
||||
|
||||
// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library
|
||||
// for general offsetting (the offset(), offset2(), offset_ex() functions) and for the safety offset,
|
||||
// which is optionally executed by other functions (union, intersection, diff).
|
||||
// This scaling (cca 130t) is applied over the usual SCALING_FACTOR.
|
||||
// By the way, is the scalling for offset needed at all?
|
||||
// The reason to apply this scaling may be to match the resolution of the double mantissa.
|
||||
#define CLIPPER_OFFSET_POWER_OF_2 17
|
||||
// 2^17=131072
|
||||
#define CLIPPER_OFFSET_SCALE (1 << CLIPPER_OFFSET_POWER_OF_2)
|
||||
#define CLIPPER_OFFSET_SCALE_ROUNDING_DELTA ((1 << (CLIPPER_OFFSET_POWER_OF_2 - 1)) - 1)
|
||||
#define CLIPPER_MAX_COORD_UNSCALED (ClipperLib::hiRange / CLIPPER_OFFSET_SCALE)
|
||||
static constexpr const float ClipperSafetyOffset = 10.f;
|
||||
|
||||
#define CLIPPERUTILS_UNSAFE_OFFSET
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// legacy code from Clipper documentation
|
||||
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons *expolygons);
|
||||
Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree);
|
||||
//-----------------------------------------------------------
|
||||
namespace ClipperUtils {
|
||||
class PathsProviderIteratorBase {
|
||||
public:
|
||||
using value_type = Points;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const Points*;
|
||||
using reference = const Points&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
};
|
||||
|
||||
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
|
||||
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polygons &input);
|
||||
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const ExPolygons &input);
|
||||
ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const Polylines &input);
|
||||
Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input);
|
||||
Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input);
|
||||
Slic3r::Polygons ClipperPaths_to_Slic3rPolygons(const ClipperLib::Paths &input);
|
||||
Slic3r::Polylines ClipperPaths_to_Slic3rPolylines(const ClipperLib::Paths &input);
|
||||
Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
|
||||
class EmptyPathsProvider {
|
||||
public:
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
const Points& operator*() { assert(false); return s_empty_points; }
|
||||
// all iterators point to end.
|
||||
constexpr bool operator==(const iterator &rhs) const { return true; }
|
||||
constexpr bool operator!=(const iterator &rhs) const { return false; }
|
||||
const Points& operator++(int) { assert(false); return s_empty_points; }
|
||||
constexpr iterator& operator++() { assert(false); return *this; }
|
||||
};
|
||||
|
||||
constexpr EmptyPathsProvider() {}
|
||||
static constexpr iterator cend() throw() { return iterator{}; }
|
||||
static constexpr iterator end() throw() { return cend(); }
|
||||
static constexpr iterator cbegin() throw() { return cend(); }
|
||||
static constexpr iterator begin() throw() { return cend(); }
|
||||
static constexpr size_t size() throw() { return 0; }
|
||||
|
||||
static Points s_empty_points;
|
||||
};
|
||||
|
||||
class SinglePathProvider {
|
||||
public:
|
||||
SinglePathProvider(const Points &points) : m_points(points) {}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
explicit iterator(const Points &points) : m_ptr(&points) {}
|
||||
const Points& operator*() const { return *m_ptr; }
|
||||
bool operator==(const iterator &rhs) const { return m_ptr == rhs.m_ptr; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
const Points& operator++(int) { auto out = m_ptr; m_ptr = &s_end; return *out; }
|
||||
iterator& operator++() { m_ptr = &s_end; return *this; }
|
||||
private:
|
||||
const Points *m_ptr;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_points); }
|
||||
iterator begin() const { return this->cbegin(); }
|
||||
iterator cend() const { return iterator(s_end); }
|
||||
iterator end() const { return this->cend(); }
|
||||
size_t size() const { return 1; }
|
||||
|
||||
private:
|
||||
const Points &m_points;
|
||||
static Points s_end;
|
||||
};
|
||||
|
||||
template<typename MultiPointType>
|
||||
class MultiPointsProvider {
|
||||
public:
|
||||
MultiPointsProvider(const std::vector<MultiPointType> &multipoints) : m_multipoints(multipoints) {}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
explicit iterator(typename std::vector<MultiPointType>::const_iterator it) : m_it(it) {}
|
||||
const Points& operator*() const { return m_it->points; }
|
||||
bool operator==(const iterator &rhs) const { return m_it == rhs.m_it; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
const Points& operator++(int) { return (m_it ++)->points; }
|
||||
iterator& operator++() { ++ m_it; return *this; }
|
||||
private:
|
||||
typename std::vector<MultiPointType>::const_iterator m_it;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_multipoints.begin()); }
|
||||
iterator begin() const { return this->cbegin(); }
|
||||
iterator cend() const { return iterator(m_multipoints.end()); }
|
||||
iterator end() const { return this->cend(); }
|
||||
size_t size() const { return m_multipoints.size(); }
|
||||
|
||||
private:
|
||||
const std::vector<MultiPointType> &m_multipoints;
|
||||
};
|
||||
|
||||
using PolygonsProvider = MultiPointsProvider<Polygon>;
|
||||
using PolylinesProvider = MultiPointsProvider<Polyline>;
|
||||
|
||||
struct ExPolygonProvider {
|
||||
ExPolygonProvider(const ExPolygon &expoly) : m_expoly(expoly) {}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
explicit iterator(const ExPolygon &expoly, int idx) : m_expoly(expoly), m_idx(idx) {}
|
||||
const Points& operator*() const { return (m_idx == 0) ? m_expoly.contour.points : m_expoly.holes[m_idx - 1].points; }
|
||||
bool operator==(const iterator &rhs) const { assert(m_expoly == rhs.m_expoly); return m_idx == rhs.m_idx; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
const Points& operator++(int) { const Points &out = **this; ++ m_idx; return out; }
|
||||
iterator& operator++() { ++ m_idx; return *this; }
|
||||
private:
|
||||
const ExPolygon &m_expoly;
|
||||
int m_idx;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_expoly, 0); }
|
||||
iterator begin() const { return this->cbegin(); }
|
||||
iterator cend() const { return iterator(m_expoly, m_expoly.holes.size() + 1); }
|
||||
iterator end() const { return this->cend(); }
|
||||
size_t size() const { return m_expoly.holes.size() + 1; }
|
||||
|
||||
private:
|
||||
const ExPolygon &m_expoly;
|
||||
};
|
||||
|
||||
struct ExPolygonsProvider {
|
||||
ExPolygonsProvider(const ExPolygons &expolygons) : m_expolygons(expolygons) {
|
||||
m_size = 0;
|
||||
for (const ExPolygon &expoly : expolygons)
|
||||
m_size += expoly.holes.size() + 1;
|
||||
}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
explicit iterator(ExPolygons::const_iterator it) : m_it_expolygon(it), m_idx_contour(0) {}
|
||||
const Points& operator*() const { return (m_idx_contour == 0) ? m_it_expolygon->contour.points : m_it_expolygon->holes[m_idx_contour - 1].points; }
|
||||
bool operator==(const iterator &rhs) const { return m_it_expolygon == rhs.m_it_expolygon && m_idx_contour == rhs.m_idx_contour; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
iterator& operator++() {
|
||||
if (++ m_idx_contour == m_it_expolygon->holes.size() + 1) {
|
||||
++ m_it_expolygon;
|
||||
m_idx_contour = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
const Points& operator++(int) {
|
||||
const Points &out = **this;
|
||||
++ (*this);
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
ExPolygons::const_iterator m_it_expolygon;
|
||||
size_t m_idx_contour;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_expolygons.cbegin()); }
|
||||
iterator begin() const { return this->cbegin(); }
|
||||
iterator cend() const { return iterator(m_expolygons.cend()); }
|
||||
iterator end() const { return this->cend(); }
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
private:
|
||||
const ExPolygons &m_expolygons;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
struct SurfacesProvider {
|
||||
SurfacesProvider(const Surfaces &surfaces) : m_surfaces(surfaces) {
|
||||
m_size = 0;
|
||||
for (const Surface &surface : surfaces)
|
||||
m_size += surface.expolygon.holes.size() + 1;
|
||||
}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
explicit iterator(Surfaces::const_iterator it) : m_it_surface(it), m_idx_contour(0) {}
|
||||
const Points& operator*() const { return (m_idx_contour == 0) ? m_it_surface->expolygon.contour.points : m_it_surface->expolygon.holes[m_idx_contour - 1].points; }
|
||||
bool operator==(const iterator &rhs) const { return m_it_surface == rhs.m_it_surface && m_idx_contour == rhs.m_idx_contour; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
iterator& operator++() {
|
||||
if (++ m_idx_contour == m_it_surface->expolygon.holes.size() + 1) {
|
||||
++ m_it_surface;
|
||||
m_idx_contour = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
const Points& operator++(int) {
|
||||
const Points &out = **this;
|
||||
++ (*this);
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
Surfaces::const_iterator m_it_surface;
|
||||
size_t m_idx_contour;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_surfaces.cbegin()); }
|
||||
iterator begin() const { return this->cbegin(); }
|
||||
iterator cend() const { return iterator(m_surfaces.cend()); }
|
||||
iterator end() const { return this->cend(); }
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
private:
|
||||
const Surfaces &m_surfaces;
|
||||
size_t m_size;
|
||||
};
|
||||
|
||||
struct SurfacesPtrProvider {
|
||||
SurfacesPtrProvider(const SurfacesPtr &surfaces) : m_surfaces(surfaces) {
|
||||
m_size = 0;
|
||||
for (const Surface *surface : surfaces)
|
||||
m_size += surface->expolygon.holes.size() + 1;
|
||||
}
|
||||
|
||||
struct iterator : public PathsProviderIteratorBase {
|
||||
public:
|
||||
explicit iterator(SurfacesPtr::const_iterator it) : m_it_surface(it), m_idx_contour(0) {}
|
||||
const Points& operator*() const { return (m_idx_contour == 0) ? (*m_it_surface)->expolygon.contour.points : (*m_it_surface)->expolygon.holes[m_idx_contour - 1].points; }
|
||||
bool operator==(const iterator &rhs) const { return m_it_surface == rhs.m_it_surface && m_idx_contour == rhs.m_idx_contour; }
|
||||
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
|
||||
iterator& operator++() {
|
||||
if (++ m_idx_contour == (*m_it_surface)->expolygon.holes.size() + 1) {
|
||||
++ m_it_surface;
|
||||
m_idx_contour = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
const Points& operator++(int) {
|
||||
const Points &out = **this;
|
||||
++ (*this);
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
SurfacesPtr::const_iterator m_it_surface;
|
||||
size_t m_idx_contour;
|
||||
};
|
||||
|
||||
iterator cbegin() const { return iterator(m_surfaces.cbegin()); }
|
||||
iterator begin() const { return this->cbegin(); }
|
||||
iterator cend() const { return iterator(m_surfaces.cend()); }
|
||||
iterator end() const { return this->cend(); }
|
||||
size_t size() const { return m_size; }
|
||||
|
||||
private:
|
||||
const SurfacesPtr &m_surfaces;
|
||||
size_t m_size;
|
||||
};
|
||||
}
|
||||
|
||||
ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input);
|
||||
|
||||
// offset Polygons
|
||||
ClipperLib::Paths _offset(ClipperLib::Path &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
ClipperLib::Paths _offset(ClipperLib::Paths &&input, ClipperLib::EndType endType, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
Slic3r::Polygons offset(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
|
||||
#ifdef CLIPPERUTILS_UNSAFE_OFFSET
|
||||
Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
#endif // CLIPPERUTILS_UNSAFE_OFFSET
|
||||
|
||||
// offset Polylines
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polyline), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polylines), ClipperLib::etOpenButt, delta, joinType, miterLimit)); }
|
||||
Slic3r::Polygons offset(const Slic3r::Polyline &polyline, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::Polygons offset(const Slic3r::SurfacesPtr &surfaces, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::Surfaces &surfaces, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
|
||||
// offset expolygons and surfaces
|
||||
ClipperLib::Paths _offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
ClipperLib::Paths _offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType, double miterLimit);
|
||||
inline Slic3r::Polygons offset(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::Polygons offset(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygon &polygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoint_to_ClipperPath(polygon), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(Slic3rMultiPoints_to_ClipperPaths(polygons), ClipperLib::etClosedPolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygon &expolygon, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygon, delta, joinType, miterLimit)); }
|
||||
inline Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3)
|
||||
{ return ClipperPaths_to_Slic3rExPolygons(_offset(expolygons, delta, joinType, miterLimit)); }
|
||||
Slic3r::Polygons offset2(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
|
||||
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::ExPolygons &expolygons, const float delta1,
|
||||
const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter,
|
||||
double miterLimit = 3);
|
||||
#ifdef CLIPPERUTILS_UNSAFE_OFFSET
|
||||
Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3);
|
||||
#endif // CLIPPERUTILS_UNSAFE_OFFSET
|
||||
|
||||
Slic3r::Polygons _clipper(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType,
|
||||
const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false);
|
||||
Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
|
||||
// diff
|
||||
inline Slic3r::Polygons
|
||||
diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Surfaces &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Polygon &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::Surfaces &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons diff_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygon &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
|
||||
inline Slic3r::Lines diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
return _clipper_ln(ClipperLib::ctDifference, subject, clip, do_safety_offset);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygon &subject, const Slic3r::ExPolygon &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons intersection(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polygons intersection(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygon &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::Surfaces &subject, const Slic3r::Surfaces &clip, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons intersection_ex(const Slic3r::SurfacesPtr &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r::ExPolygons &clip, bool do_safety_offset = false);
|
||||
Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false);
|
||||
|
||||
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
return _clipper_ln(ClipperLib::ctIntersection, subject, clip, do_safety_offset);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polygons
|
||||
diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polylines
|
||||
diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polylines
|
||||
diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Lines
|
||||
diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
// intersection
|
||||
inline Slic3r::Polygons
|
||||
intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons
|
||||
intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polygons
|
||||
intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polylines
|
||||
intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Polylines
|
||||
intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false)
|
||||
inline Slic3r::Lines intersection_ln(const Slic3r::Line &subject, const Slic3r::Polygons &clip, bool do_safety_offset = false)
|
||||
{
|
||||
Slic3r::Lines lines;
|
||||
lines.emplace_back(subject);
|
||||
return _clipper_ln(ClipperLib::ctIntersection, lines, clip, safety_offset_);
|
||||
return _clipper_ln(ClipperLib::ctIntersection, lines, clip, do_safety_offset);
|
||||
}
|
||||
|
||||
// union
|
||||
inline Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool do_safety_offset = false);
|
||||
Slic3r::Polygons union_(const Slic3r::ExPolygons &subject, bool do_safety_offset = false);
|
||||
Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool do_safety_offset = false);
|
||||
Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject);
|
||||
Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject);
|
||||
|
||||
inline Slic3r::Polygons union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_);
|
||||
}
|
||||
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject);
|
||||
ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject);
|
||||
|
||||
inline Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
inline Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false)
|
||||
{
|
||||
return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_);
|
||||
}
|
||||
|
||||
ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false);
|
||||
ClipperLib::PolyTree union_pt(const Slic3r::ExPolygons &subject, bool safety_offset_ = false);
|
||||
ClipperLib::PolyTree union_pt(Slic3r::Polygons &&subject, bool safety_offset_ = false);
|
||||
ClipperLib::PolyTree union_pt(Slic3r::ExPolygons &&subject, bool safety_offset_ = false);
|
||||
|
||||
Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject, bool safety_offset_ = false);
|
||||
Slic3r::Polygons union_pt_chained_outside_in(const Slic3r::Polygons &subject);
|
||||
|
||||
ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes);
|
||||
|
||||
|
@ -267,7 +403,7 @@ void traverse_pt(const ClipperLib::PolyNode *tree, Polygons *out)
|
|||
if (!tree) return; // terminates recursion
|
||||
|
||||
// Push the contour of the current level
|
||||
out->emplace_back(ClipperPath_to_Slic3rPolygon(tree->Contour));
|
||||
out->emplace_back(tree->Contour);
|
||||
|
||||
// Do the recursion for all the children.
|
||||
traverse_pt<ordering>(tree->Childs, out);
|
||||
|
@ -286,13 +422,13 @@ void traverse_pt(const ClipperLib::PolyNode *tree, ExPolygons *out)
|
|||
}
|
||||
|
||||
ExPolygon level;
|
||||
level.contour = ClipperPath_to_Slic3rPolygon(tree->Contour);
|
||||
level.contour.points = tree->Contour;
|
||||
|
||||
foreach_node<ordering>(tree->Childs,
|
||||
[out, &level] (const ClipperLib::PolyNode *node) {
|
||||
|
||||
// Holes are collected here.
|
||||
level.holes.emplace_back(ClipperPath_to_Slic3rPolygon(node->Contour));
|
||||
level.holes.emplace_back(node->Contour);
|
||||
|
||||
// By doing a recursion, a new level expoly is created with the contour
|
||||
// and holes of the lower level. Doing this for all the childs.
|
||||
|
@ -315,10 +451,9 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval)
|
|||
Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);
|
||||
Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false);
|
||||
|
||||
void safety_offset(ClipperLib::Paths* paths);
|
||||
|
||||
Polygons top_level_islands(const Slic3r::Polygons &polygons);
|
||||
|
||||
ClipperLib::Path mittered_offset_path_scaled(const Points &contour, const std::vector<float> &deltas, double miter_limit);
|
||||
Polygons variable_offset_inner(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
Polygons variable_offset_outer(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
ExPolygons variable_offset_outer_ex(const ExPolygon &expoly, const std::vector<std::vector<float>> &deltas, double miter_limit = 2.);
|
||||
|
|
|
@ -471,8 +471,8 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
|
|||
{
|
||||
t_config_option_key opt_key = opt_key_src;
|
||||
std::string value = value_src;
|
||||
// Both opt_key and value may be modified by _handle_legacy().
|
||||
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by _handle_legacy().
|
||||
// Both opt_key and value may be modified by handle_legacy().
|
||||
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
|
||||
this->handle_legacy(opt_key, value);
|
||||
if (opt_key.empty())
|
||||
// Ignore the option.
|
||||
|
|
|
@ -18,11 +18,53 @@
|
|||
|
||||
#include <boost/algorithm/string/trim.hpp>
|
||||
#include <boost/format/format_fwd.hpp>
|
||||
#include <boost/functional/hash.hpp>
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
|
||||
#include <cereal/access.hpp>
|
||||
#include <cereal/types/base_class.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
struct FloatOrPercent
|
||||
{
|
||||
double value;
|
||||
bool percent;
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive& ar) { ar(this->value); ar(this->percent); }
|
||||
};
|
||||
|
||||
inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; }
|
||||
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); }
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<> struct hash<Slic3r::FloatOrPercent> {
|
||||
std::size_t operator()(const Slic3r::FloatOrPercent& v) const noexcept {
|
||||
std::size_t seed = std::hash<double>{}(v.value);
|
||||
return v.percent ? seed ^ 0x9e3779b9 : seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct hash<Slic3r::Vec2d> {
|
||||
std::size_t operator()(const Slic3r::Vec2d& v) const noexcept {
|
||||
std::size_t seed = std::hash<double>{}(v.x());
|
||||
boost::hash_combine(seed, std::hash<double>{}(v.y()));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct hash<Slic3r::Vec3d> {
|
||||
std::size_t operator()(const Slic3r::Vec3d& v) const noexcept {
|
||||
std::size_t seed = std::hash<double>{}(v.x());
|
||||
boost::hash_combine(seed, std::hash<double>{}(v.y()));
|
||||
boost::hash_combine(seed, std::hash<double>{}(v.z()));
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Name of the configuration option.
|
||||
|
@ -137,6 +179,7 @@ public:
|
|||
virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); }
|
||||
virtual bool operator==(const ConfigOption &rhs) const = 0;
|
||||
bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
|
||||
virtual size_t hash() const throw() = 0;
|
||||
bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
|
||||
bool is_vector() const { return ! this->is_scalar(); }
|
||||
// If this option is nullable, then it may have its value or values set to nil.
|
||||
|
@ -185,8 +228,10 @@ public:
|
|||
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
|
||||
}
|
||||
|
||||
bool operator==(const T &rhs) const { return this->value == rhs; }
|
||||
bool operator!=(const T &rhs) const { return this->value != rhs; }
|
||||
bool operator==(const T &rhs) const throw() { return this->value == rhs; }
|
||||
bool operator!=(const T &rhs) const throw() { return this->value != rhs; }
|
||||
|
||||
size_t hash() const throw() override { return std::hash<T>{}(this->value); }
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
|
@ -339,8 +384,16 @@ public:
|
|||
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
|
||||
}
|
||||
|
||||
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; }
|
||||
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; }
|
||||
bool operator==(const std::vector<T> &rhs) const throw() { return this->values == rhs; }
|
||||
bool operator!=(const std::vector<T> &rhs) const throw() { return this->values != rhs; }
|
||||
|
||||
size_t hash() const throw() override {
|
||||
std::hash<T> hasher;
|
||||
size_t seed = 0;
|
||||
for (const auto &v : this->values)
|
||||
boost::hash_combine(seed, hasher(v));
|
||||
return seed;
|
||||
}
|
||||
|
||||
// Is this option overridden by another option?
|
||||
// An option overrides another option if it is not nil and not equal.
|
||||
|
@ -413,7 +466,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
double getFloat() const override { return this->value; }
|
||||
ConfigOption* clone() const override { return new ConfigOptionFloat(*this); }
|
||||
bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -454,7 +507,7 @@ public:
|
|||
static ConfigOptionType static_type() { return coFloats; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); }
|
||||
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
||||
bool operator==(const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); }
|
||||
bool operator==(const ConfigOption &rhs) const override {
|
||||
if (rhs.type() != this->type())
|
||||
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types");
|
||||
|
@ -566,7 +619,7 @@ public:
|
|||
int getInt() const override { return this->value; }
|
||||
void setInt(int val) override { this->value = val; }
|
||||
ConfigOption* clone() const override { return new ConfigOptionInt(*this); }
|
||||
bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionInt &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -606,7 +659,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); }
|
||||
ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionIntsTempl &rhs) const { return this->values == rhs.values; }
|
||||
bool operator==(const ConfigOptionIntsTempl &rhs) const throw() { return this->values == rhs.values; }
|
||||
// Could a special "nil" value be stored inside the vector, indicating undefined value?
|
||||
bool nullable() const override { return NULLABLE; }
|
||||
// Special "nil" value to be stored into the vector if this->supports_nil().
|
||||
|
@ -689,7 +742,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionString(*this); }
|
||||
ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionString &rhs) const throw() { return this->value == rhs.value; }
|
||||
bool empty() const { return this->value.empty(); }
|
||||
|
||||
std::string serialize() const override
|
||||
|
@ -722,7 +775,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
|
||||
ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; }
|
||||
bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->values == rhs.values; }
|
||||
bool is_nil(size_t) const override { return false; }
|
||||
|
||||
std::string serialize() const override
|
||||
|
@ -757,7 +810,8 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionPercent(*this); }
|
||||
ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionPercent &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; }
|
||||
|
||||
std::string serialize() const override
|
||||
|
@ -796,8 +850,8 @@ public:
|
|||
static ConfigOptionType static_type() { return coPercents; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); }
|
||||
ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPercentsTempl &rhs) const { return this->values == rhs.values; }
|
||||
ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPercentsTempl &rhs) const throw() { return this->values == rhs.values; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -856,8 +910,12 @@ public:
|
|||
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
|
||||
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
|
||||
}
|
||||
bool operator==(const ConfigOptionFloatOrPercent &rhs) const
|
||||
bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw()
|
||||
{ return this->value == rhs.value && this->percent == rhs.percent; }
|
||||
size_t hash() const throw() override {
|
||||
size_t seed = std::hash<double>{}(this->value);
|
||||
return this->percent ? seed ^ 0x9e3779b9 : seed;
|
||||
}
|
||||
double get_abs_value(double ratio_over) const
|
||||
{ return this->percent ? (ratio_over * this->value / 100) : this->value; }
|
||||
|
||||
|
@ -891,27 +949,6 @@ private:
|
|||
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
|
||||
};
|
||||
|
||||
|
||||
struct FloatOrPercent
|
||||
{
|
||||
double value;
|
||||
bool percent;
|
||||
|
||||
private:
|
||||
friend class cereal::access;
|
||||
template<class Archive> void serialize(Archive & ar) { ar(this->value); ar(this->percent); }
|
||||
};
|
||||
|
||||
inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r)
|
||||
{
|
||||
return l.value == r.value && l.percent == r.percent;
|
||||
}
|
||||
|
||||
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r)
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
template<bool NULLABLE>
|
||||
class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent>
|
||||
{
|
||||
|
@ -925,13 +962,14 @@ public:
|
|||
static ConfigOptionType static_type() { return coFloatsOrPercents; }
|
||||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); }
|
||||
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); }
|
||||
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); }
|
||||
bool operator==(const ConfigOption &rhs) const override {
|
||||
if (rhs.type() != this->type())
|
||||
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
|
||||
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
|
||||
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
|
||||
}
|
||||
|
||||
// Could a special "nil" value be stored inside the vector, indicating undefined value?
|
||||
bool nullable() const override { return NULLABLE; }
|
||||
// Special "nil" value to be stored into the vector if this->supports_nil().
|
||||
|
@ -1038,7 +1076,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionPoint(*this); }
|
||||
ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionPoint &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -1074,7 +1112,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
|
||||
ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; }
|
||||
bool operator==(const ConfigOptionPoints &rhs) const throw() { return this->values == rhs.values; }
|
||||
bool is_nil(size_t) const override { return false; }
|
||||
|
||||
std::string serialize() const override
|
||||
|
@ -1146,7 +1184,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); }
|
||||
ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionPoint3 &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionPoint3 &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -1183,7 +1221,7 @@ public:
|
|||
bool getBool() const override { return this->value; }
|
||||
ConfigOption* clone() const override { return new ConfigOptionBool(*this); }
|
||||
ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionBool &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
std::string serialize() const override
|
||||
{
|
||||
|
@ -1217,7 +1255,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); }
|
||||
ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionBoolsTempl &rhs) const { return this->values == rhs.values; }
|
||||
bool operator==(const ConfigOptionBoolsTempl &rhs) const throw() { return this->values == rhs.values; }
|
||||
// Could a special "nil" value be stored inside the vector, indicating undefined value?
|
||||
bool nullable() const override { return NULLABLE; }
|
||||
// Special "nil" value to be stored into the vector if this->supports_nil().
|
||||
|
@ -1311,7 +1349,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); }
|
||||
ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionEnum<T> &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionEnum<T> &rhs) const throw() { return this->value == rhs.value; }
|
||||
int getInt() const override { return (int)this->value; }
|
||||
void setInt(int val) override { this->value = T(val); }
|
||||
|
||||
|
@ -1397,7 +1435,7 @@ public:
|
|||
ConfigOptionType type() const override { return static_type(); }
|
||||
ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); }
|
||||
ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
|
||||
bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; }
|
||||
bool operator==(const ConfigOptionEnumGeneric &rhs) const throw() { return this->value == rhs.value; }
|
||||
|
||||
bool operator==(const ConfigOption &rhs) const override
|
||||
{
|
||||
|
@ -1967,8 +2005,9 @@ public:
|
|||
int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(this->option(opt_key))->get_at(idx); }
|
||||
|
||||
// In ConfigManipulation::toggle_print_fff_options, it is called on option with type ConfigOptionEnumGeneric* and also ConfigOptionEnum*.
|
||||
// Thus the virtual method getInt() is used to retrieve the enum value.
|
||||
template<typename ENUM>
|
||||
ENUM opt_enum(const t_config_option_key &opt_key) const { return this->option<ConfigOptionEnum<ENUM>>(opt_key)->value; }
|
||||
ENUM opt_enum(const t_config_option_key &opt_key) const { return static_cast<ENUM>(this->option(opt_key)->getInt()); }
|
||||
|
||||
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
|
||||
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }
|
||||
|
|
|
@ -83,8 +83,8 @@ inline bool operator!=(const ExPolygon &lhs, const ExPolygon &rhs) { return lhs.
|
|||
inline size_t number_polygons(const ExPolygons &expolys)
|
||||
{
|
||||
size_t n_polygons = 0;
|
||||
for (ExPolygons::const_iterator it = expolys.begin(); it != expolys.end(); ++ it)
|
||||
n_polygons += it->holes.size() + 1;
|
||||
for (const ExPolygon &ex : expolys)
|
||||
n_polygons += ex.holes.size() + 1;
|
||||
return n_polygons;
|
||||
}
|
||||
|
||||
|
@ -217,6 +217,28 @@ inline Polygons to_polygons(const ExPolygons &src)
|
|||
return polygons;
|
||||
}
|
||||
|
||||
inline ConstPolygonPtrs to_polygon_ptrs(const ExPolygon &src)
|
||||
{
|
||||
ConstPolygonPtrs polygons;
|
||||
polygons.reserve(src.holes.size() + 1);
|
||||
polygons.emplace_back(&src.contour);
|
||||
for (const Polygon &hole : src.holes)
|
||||
polygons.emplace_back(&hole);
|
||||
return polygons;
|
||||
}
|
||||
|
||||
inline ConstPolygonPtrs to_polygon_ptrs(const ExPolygons &src)
|
||||
{
|
||||
ConstPolygonPtrs polygons;
|
||||
polygons.reserve(number_polygons(src));
|
||||
for (const ExPolygon &expoly : src) {
|
||||
polygons.emplace_back(&expoly.contour);
|
||||
for (const Polygon &hole : expoly.holes)
|
||||
polygons.emplace_back(&hole);
|
||||
}
|
||||
return polygons;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(ExPolygon &&src)
|
||||
{
|
||||
Polygons polygons;
|
||||
|
@ -338,6 +360,8 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
|
|||
extern bool remove_sticks(ExPolygon &poly);
|
||||
extern void keep_largest_contour_only(ExPolygons &polygons);
|
||||
|
||||
inline double area(const ExPolygon &poly) { return poly.area(); }
|
||||
|
||||
inline double area(const ExPolygons &polys)
|
||||
{
|
||||
double s = 0.;
|
||||
|
|
128
src/libslic3r/Execution/Execution.hpp
Normal file
128
src/libslic3r/Execution/Execution.hpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
#ifndef EXECUTION_HPP
|
||||
#define EXECUTION_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Override for valid execution policies
|
||||
template<class EP> struct IsExecutionPolicy_ : public std::false_type {};
|
||||
|
||||
template<class EP> constexpr bool IsExecutionPolicy =
|
||||
IsExecutionPolicy_<remove_cvref_t<EP>>::value;
|
||||
|
||||
template<class EP, class T = void>
|
||||
using ExecutionPolicyOnly = std::enable_if_t<IsExecutionPolicy<EP>, T>;
|
||||
|
||||
namespace execution {
|
||||
|
||||
// This struct needs to be specialized for each execution policy.
|
||||
// See ExecutionSeq.hpp and ExecutionTBB.hpp for example.
|
||||
template<class EP, class En = void> struct Traits {};
|
||||
|
||||
template<class EP> using AsTraits = Traits<remove_cvref_t<EP>>;
|
||||
|
||||
// Each execution policy should declare two types of mutexes. A a spin lock and
|
||||
// a blocking mutex. These types should satisfy the BasicLockable concept.
|
||||
template<class EP> using SpinningMutex = typename Traits<EP>::SpinningMutex;
|
||||
template<class EP> using BlockingMutex = typename Traits<EP>::BlockingMutex;
|
||||
|
||||
// Query the available threads for concurrency.
|
||||
template<class EP, class = ExecutionPolicyOnly<EP> >
|
||||
size_t max_concurrency(const EP &ep)
|
||||
{
|
||||
return AsTraits<EP>::max_concurrency(ep);
|
||||
}
|
||||
|
||||
// foreach loop with the execution policy passed as argument. Granularity can
|
||||
// be specified explicitly. max_concurrency() can be used for optimal results.
|
||||
template<class EP, class It, class Fn, class = ExecutionPolicyOnly<EP>>
|
||||
void for_each(const EP &ep, It from, It to, Fn &&fn, size_t granularity = 1)
|
||||
{
|
||||
AsTraits<EP>::for_each(ep, from, to, std::forward<Fn>(fn), granularity);
|
||||
}
|
||||
|
||||
// A reduce operation with the execution policy passed as argument.
|
||||
// mergefn has T(const T&, const T&) signature
|
||||
// accessfn has T(I) signature if I is an integral type and
|
||||
// T(const I::value_type &) if I is an iterator type.
|
||||
template<class EP,
|
||||
class I,
|
||||
class MergeFn,
|
||||
class T,
|
||||
class AccessFn,
|
||||
class = ExecutionPolicyOnly<EP> >
|
||||
T reduce(const EP & ep,
|
||||
I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn && mergefn,
|
||||
AccessFn &&accessfn,
|
||||
size_t granularity = 1)
|
||||
{
|
||||
return AsTraits<EP>::reduce(ep, from, to, init,
|
||||
std::forward<MergeFn>(mergefn),
|
||||
std::forward<AccessFn>(accessfn),
|
||||
granularity);
|
||||
}
|
||||
|
||||
// An overload of reduce method to be used with iterators as 'from' and 'to'
|
||||
// arguments. Access functor is omitted here.
|
||||
template<class EP,
|
||||
class I,
|
||||
class MergeFn,
|
||||
class T,
|
||||
class = ExecutionPolicyOnly<EP> >
|
||||
T reduce(const EP &ep,
|
||||
I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn &&mergefn,
|
||||
size_t granularity = 1)
|
||||
{
|
||||
return reduce(
|
||||
ep, from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](const auto &i) { return i; }, granularity);
|
||||
}
|
||||
|
||||
template<class EP,
|
||||
class I,
|
||||
class T,
|
||||
class AccessFn,
|
||||
class = ExecutionPolicyOnly<EP>>
|
||||
T accumulate(const EP & ep,
|
||||
I from,
|
||||
I to,
|
||||
const T & init,
|
||||
AccessFn &&accessfn,
|
||||
size_t granularity = 1)
|
||||
{
|
||||
return reduce(ep, from, to, init, std::plus<T>{},
|
||||
std::forward<AccessFn>(accessfn), granularity);
|
||||
}
|
||||
|
||||
|
||||
template<class EP,
|
||||
class I,
|
||||
class T,
|
||||
class = ExecutionPolicyOnly<EP> >
|
||||
T accumulate(const EP &ep,
|
||||
I from,
|
||||
I to,
|
||||
const T & init,
|
||||
size_t granularity = 1)
|
||||
{
|
||||
return reduce(
|
||||
ep, from, to, init, std::plus<T>{}, [](const auto &i) { return i; },
|
||||
granularity);
|
||||
}
|
||||
|
||||
} // namespace execution_policy
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // EXECUTION_HPP
|
84
src/libslic3r/Execution/ExecutionSeq.hpp
Normal file
84
src/libslic3r/Execution/ExecutionSeq.hpp
Normal file
|
@ -0,0 +1,84 @@
|
|||
#ifndef EXECUTIONSEQ_HPP
|
||||
#define EXECUTIONSEQ_HPP
|
||||
|
||||
#ifdef PRUSASLICER_USE_EXECUTION_STD // Conflicts with our version of TBB
|
||||
#include <execution>
|
||||
#endif
|
||||
|
||||
#include "Execution.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Execution policy implementing dummy sequential algorithms
|
||||
struct ExecutionSeq {};
|
||||
|
||||
template<> struct IsExecutionPolicy_<ExecutionSeq> : public std::true_type {};
|
||||
|
||||
static constexpr ExecutionSeq ex_seq = {};
|
||||
|
||||
template<class EP> struct IsSequentialEP_ { static constexpr bool value = false; };
|
||||
|
||||
template<> struct IsSequentialEP_<ExecutionSeq>: public std::true_type {};
|
||||
#ifdef PRUSASLICER_USE_EXECUTION_STD
|
||||
template<> struct IsExecutionPolicy_<std::execution::sequenced_policy>: public std::true_type {};
|
||||
template<> struct IsSequentialEP_<std::execution::sequenced_policy>: public std::true_type {};
|
||||
#endif
|
||||
|
||||
template<class EP>
|
||||
constexpr bool IsSequentialEP = IsSequentialEP_<remove_cvref_t<EP>>::value;
|
||||
|
||||
template<class EP, class R = EP>
|
||||
using SequentialEPOnly = std::enable_if_t<IsSequentialEP<EP>, R>;
|
||||
|
||||
template<class EP>
|
||||
struct execution::Traits<EP, SequentialEPOnly<EP, void>> {
|
||||
private:
|
||||
struct _Mtx { inline void lock() {} inline void unlock() {} };
|
||||
|
||||
template<class Fn, class It>
|
||||
static IteratorOnly<It, void> loop_(It from, It to, Fn &&fn)
|
||||
{
|
||||
for (auto it = from; it != to; ++it) fn(*it);
|
||||
}
|
||||
|
||||
template<class Fn, class I>
|
||||
static IntegerOnly<I, void> loop_(I from, I to, Fn &&fn)
|
||||
{
|
||||
for (I i = from; i < to; ++i) fn(i);
|
||||
}
|
||||
|
||||
public:
|
||||
using SpinningMutex = _Mtx;
|
||||
using BlockingMutex = _Mtx;
|
||||
|
||||
template<class It, class Fn>
|
||||
static void for_each(const EP &,
|
||||
It from,
|
||||
It to,
|
||||
Fn &&fn,
|
||||
size_t /* ignore granularity */ = 1)
|
||||
{
|
||||
loop_(from, to, std::forward<Fn>(fn));
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T, class AccessFn>
|
||||
static T reduce(const EP &,
|
||||
I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn &&mergefn,
|
||||
AccessFn &&access,
|
||||
size_t /*granularity*/ = 1
|
||||
)
|
||||
{
|
||||
T acc = init;
|
||||
loop_(from, to, [&](auto &i) { acc = mergefn(acc, access(i)); });
|
||||
return acc;
|
||||
}
|
||||
|
||||
static size_t max_concurrency(const EP &) { return 1; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // EXECUTIONSEQ_HPP
|
77
src/libslic3r/Execution/ExecutionTBB.hpp
Normal file
77
src/libslic3r/Execution/ExecutionTBB.hpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#ifndef EXECUTIONTBB_HPP
|
||||
#define EXECUTIONTBB_HPP
|
||||
|
||||
#include <tbb/spin_mutex.h>
|
||||
#include <tbb/mutex.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_reduce.h>
|
||||
#include <tbb/task_arena.h>
|
||||
|
||||
#include "Execution.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct ExecutionTBB {};
|
||||
template<> struct IsExecutionPolicy_<ExecutionTBB> : public std::true_type {};
|
||||
|
||||
// Execution policy using Intel TBB library under the hood.
|
||||
static constexpr ExecutionTBB ex_tbb = {};
|
||||
|
||||
template<> struct execution::Traits<ExecutionTBB> {
|
||||
private:
|
||||
|
||||
template<class Fn, class It>
|
||||
static IteratorOnly<It, void> loop_(const tbb::blocked_range<It> &range, Fn &&fn)
|
||||
{
|
||||
for (auto &el : range) fn(el);
|
||||
}
|
||||
|
||||
template<class Fn, class I>
|
||||
static IntegerOnly<I, void> loop_(const tbb::blocked_range<I> &range, Fn &&fn)
|
||||
{
|
||||
for (I i = range.begin(); i < range.end(); ++i) fn(i);
|
||||
}
|
||||
|
||||
public:
|
||||
using SpinningMutex = tbb::spin_mutex;
|
||||
using BlockingMutex = tbb::mutex;
|
||||
|
||||
template<class It, class Fn>
|
||||
static void for_each(const ExecutionTBB &,
|
||||
It from, It to, Fn &&fn, size_t granularity)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range{from, to, granularity},
|
||||
[&fn](const auto &range) {
|
||||
loop_(range, std::forward<Fn>(fn));
|
||||
});
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T, class AccessFn>
|
||||
static T reduce(const ExecutionTBB &,
|
||||
I from,
|
||||
I to,
|
||||
const T &init,
|
||||
MergeFn &&mergefn,
|
||||
AccessFn &&access,
|
||||
size_t granularity = 1
|
||||
)
|
||||
{
|
||||
return tbb::parallel_reduce(
|
||||
tbb::blocked_range{from, to, granularity}, init,
|
||||
[&](const auto &range, T subinit) {
|
||||
T acc = subinit;
|
||||
loop_(range, [&](auto &i) { acc = mergefn(acc, access(i)); });
|
||||
return acc;
|
||||
},
|
||||
std::forward<MergeFn>(mergefn));
|
||||
}
|
||||
|
||||
static size_t max_concurrency(const ExecutionTBB &)
|
||||
{
|
||||
return tbb::this_task_arena::max_concurrency();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EXECUTIONTBB_HPP
|
|
@ -14,12 +14,12 @@ namespace Slic3r {
|
|||
|
||||
void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
this->_inflate_collection(intersection_pl((Polylines)polyline, to_polygons(collection.expolygons)), retval);
|
||||
this->_inflate_collection(intersection_pl(Polylines{ polyline }, collection.expolygons), retval);
|
||||
}
|
||||
|
||||
void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const
|
||||
{
|
||||
this->_inflate_collection(diff_pl((Polylines)this->polyline, to_polygons(collection.expolygons)), retval);
|
||||
this->_inflate_collection(diff_pl(Polylines{ this->polyline }, collection.expolygons), retval);
|
||||
}
|
||||
|
||||
void ExtrusionPath::clip_end(double distance)
|
||||
|
@ -318,7 +318,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
|
|||
case erIroning : return L("Ironing");
|
||||
case erBridgeInfill : return L("Bridge infill");
|
||||
case erGapFill : return L("Gap fill");
|
||||
case erSkirt : return L("Skirt");
|
||||
case erSkirt : return L("Skirt/Brim");
|
||||
case erSupportMaterial : return L("Support material");
|
||||
case erSupportMaterialInterface : return L("Support material interface");
|
||||
case erWipeTower : return L("Wipe tower");
|
||||
|
@ -349,7 +349,7 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role)
|
|||
return erBridgeInfill;
|
||||
else if (role == L("Gap fill"))
|
||||
return erGapFill;
|
||||
else if (role == L("Skirt"))
|
||||
else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier
|
||||
return erSkirt;
|
||||
else if (role == L("Support material"))
|
||||
return erSupportMaterial;
|
||||
|
|
|
@ -203,6 +203,8 @@ public:
|
|||
void reverse() override;
|
||||
const Point& first_point() const override { return this->paths.front().polyline.points.front(); }
|
||||
const Point& last_point() const override { return this->paths.back().polyline.points.back(); }
|
||||
size_t size() const { return this->paths.size(); }
|
||||
bool empty() const { return this->paths.empty(); }
|
||||
double length() const override;
|
||||
ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); }
|
||||
// Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width.
|
||||
|
|
|
@ -147,7 +147,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
// align bounding box to a multiple of our honeycomb grid module
|
||||
// (a module is 2*$distance since one $distance half-module is
|
||||
// growing while the other $distance half-module is shrinking)
|
||||
bb.merge(_align_to_grid(bb.min, Point(2*distance, 2*distance)));
|
||||
bb.merge(align_to_grid(bb.min, Point(2*distance, 2*distance)));
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines = makeGrid(
|
||||
|
@ -162,7 +162,7 @@ void Fill3DHoneycomb::_fill_surface_single(
|
|||
pl.translate(bb.min);
|
||||
|
||||
// clip pattern to boundaries, chain the clipped polylines
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
polylines = intersection_pl(polylines, expolygon);
|
||||
|
||||
// connect lines if needed
|
||||
if (params.dont_connect() || polylines.size() <= 1)
|
||||
|
|
|
@ -1368,7 +1368,7 @@ void Filler::_fill_surface_single(
|
|||
all_polylines.reserve(lines.size());
|
||||
std::transform(lines.begin(), lines.end(), std::back_inserter(all_polylines), [](const Line& l) { return Polyline{ l.a, l.b }; });
|
||||
// Crop all polylines
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
all_polylines = intersection_pl(std::move(all_polylines), expolygon);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -133,26 +133,10 @@ public:
|
|||
static void connect_infill(Polylines &&infill_ordered, const Polygons &boundary, const BoundingBox& bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
static void connect_infill(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary, const BoundingBox &bbox, Polylines &polylines_out, double spacing, const FillParams ¶ms);
|
||||
|
||||
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||
static void connect_base_support(Polylines &&infill_ordered, const std::vector<const Polygon*> &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
static void connect_base_support(Polylines &&infill_ordered, const Polygons &boundary_src, const BoundingBox &bbox, Polylines &polylines_out, const double spacing, const FillParams ¶ms);
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
// the aligned value will never be bigger than the original one.
|
||||
static coord_t _align_to_grid(const coord_t coord, const coord_t spacing) {
|
||||
// Current C++ standard defines the result of integer division to be rounded to zero,
|
||||
// for both positive and negative numbers. Here we want to round down for negative
|
||||
// numbers as well.
|
||||
coord_t aligned = (coord < 0) ?
|
||||
((coord - spacing + 1) / spacing) * spacing :
|
||||
(coord / spacing) * spacing;
|
||||
assert(aligned <= coord);
|
||||
return aligned;
|
||||
}
|
||||
static Point _align_to_grid(Point coord, Point spacing)
|
||||
{ return Point(_align_to_grid(coord(0), spacing(0)), _align_to_grid(coord(1), spacing(1))); }
|
||||
static coord_t _align_to_grid(coord_t coord, coord_t spacing, coord_t base)
|
||||
{ return base + _align_to_grid(coord - base, spacing); }
|
||||
static Point _align_to_grid(Point coord, Point spacing, Point base)
|
||||
{ return Point(_align_to_grid(coord(0), spacing(0), base(0)), _align_to_grid(coord(1), spacing(1), base(1))); }
|
||||
static coord_t _adjust_solid_spacing(const coord_t width, const coord_t distance);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -24,16 +24,16 @@ void FillConcentric::_fill_surface_single(
|
|||
this->spacing = unscale<double>(distance);
|
||||
}
|
||||
|
||||
Polygons loops = to_polygons(std::move(expolygon));
|
||||
Polygons last = loops;
|
||||
Polygons loops = to_polygons(expolygon);
|
||||
ExPolygons last { std::move(expolygon) };
|
||||
while (! last.empty()) {
|
||||
last = offset2(last, -(distance + min_spacing/2), +min_spacing/2);
|
||||
append(loops, last);
|
||||
last = offset2_ex(last, -(distance + min_spacing/2), +min_spacing/2);
|
||||
append(loops, to_polygons(last));
|
||||
}
|
||||
|
||||
// generate paths from the outermost to the innermost, to avoid
|
||||
// adhesion problems of the first central tiny loops
|
||||
loops = union_pt_chained_outside_in(loops, false);
|
||||
loops = union_pt_chained_outside_in(loops);
|
||||
|
||||
// split paths using a nearest neighbor search
|
||||
size_t iPathFirst = polylines_out.size();
|
||||
|
|
|
@ -166,7 +166,7 @@ void FillGyroid::_fill_surface_single(
|
|||
coord_t distance = coord_t(scale_(this->spacing) / density_adjusted);
|
||||
|
||||
// align bounding box to a multiple of our grid module
|
||||
bb.merge(_align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||
bb.merge(align_to_grid(bb.min, Point(2*M_PI*distance, 2*M_PI*distance)));
|
||||
|
||||
// generate pattern
|
||||
Polylines polylines = make_gyroid_waves(
|
||||
|
@ -180,7 +180,7 @@ void FillGyroid::_fill_surface_single(
|
|||
for (Polyline &pl : polylines)
|
||||
pl.translate(bb.min);
|
||||
|
||||
polylines = intersection_pl(polylines, to_polygons(expolygon));
|
||||
polylines = intersection_pl(polylines, expolygon);
|
||||
|
||||
if (! polylines.empty()) {
|
||||
// Remove very small bits, but be careful to not remove infill lines connecting thin walls!
|
||||
|
|
|
@ -47,7 +47,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one
|
||||
// The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough.
|
||||
bounding_box.merge(_align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height)));
|
||||
bounding_box.merge(align_to_grid(bounding_box.min, Point(m.hex_width, m.pattern_height)));
|
||||
}
|
||||
|
||||
coord_t x = bounding_box.min(0);
|
||||
|
@ -73,7 +73,7 @@ void FillHoneycomb::_fill_surface_single(
|
|||
}
|
||||
}
|
||||
|
||||
all_polylines = intersection_pl(std::move(all_polylines), to_polygons(expolygon));
|
||||
all_polylines = intersection_pl(std::move(all_polylines), expolygon);
|
||||
if (params.dont_connect() || all_polylines.size() <= 1)
|
||||
append(polylines_out, chain_polylines(std::move(all_polylines)));
|
||||
else
|
||||
|
|
|
@ -31,7 +31,7 @@ void FillLine::_fill_surface_single(
|
|||
} else {
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// Transform the reference point to the rotated coordinate system.
|
||||
bounding_box.merge(_align_to_grid(
|
||||
bounding_box.merge(align_to_grid(
|
||||
bounding_box.min,
|
||||
Point(this->_line_spacing, this->_line_spacing),
|
||||
direction.second.rotated(- direction.first)));
|
||||
|
@ -58,7 +58,7 @@ void FillLine::_fill_surface_single(
|
|||
pts.push_back(it->a);
|
||||
pts.push_back(it->b);
|
||||
}
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(to_polygons(expolygon), scale_(0.02)), false);
|
||||
Polylines polylines = intersection_pl(polylines_src, offset(expolygon, scale_(0.02)), false);
|
||||
|
||||
// FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines!
|
||||
const float INFILL_OVERLAP_OVER_SPACING = 0.3f;
|
||||
|
|
|
@ -44,7 +44,7 @@ void FillPlanePath::_fill_surface_single(
|
|||
coord_t(floor(pt.x() * distance_between_lines + 0.5)),
|
||||
coord_t(floor(pt.y() * distance_between_lines + 0.5))));
|
||||
// intersection(polylines_src, offset((Polygons)expolygon, scale_(0.02)), &polylines);
|
||||
polylines = intersection_pl(std::move(polylines), to_polygons(expolygon));
|
||||
polylines = intersection_pl(std::move(polylines), expolygon);
|
||||
Polylines chained;
|
||||
if (params.dont_connect() || params.density > 0.5 || polylines.size() <= 1)
|
||||
chained = chain_polylines(std::move(polylines));
|
||||
|
|
|
@ -798,33 +798,44 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
|||
assert(l <= this_x);
|
||||
assert(r >= this_x);
|
||||
// Calculate the intersection position in y axis. x is known.
|
||||
if (p1(0) == this_x) {
|
||||
if (p2(0) == this_x) {
|
||||
if (p1.x() == this_x) {
|
||||
if (p2.x() == this_x) {
|
||||
// Ignore strictly vertical segments.
|
||||
continue;
|
||||
}
|
||||
is.pos_p = p1(1);
|
||||
const Point &p0 = prev_value_modulo(iPrev, contour);
|
||||
if (int64_t(p0.x() - p1.x()) * int64_t(p2.x() - p1.x()) > 0) {
|
||||
// Ignore points of a contour touching the infill line from one side.
|
||||
continue;
|
||||
}
|
||||
is.pos_p = p1.y();
|
||||
is.pos_q = 1;
|
||||
} else if (p2(0) == this_x) {
|
||||
is.pos_p = p2(1);
|
||||
} else if (p2.x() == this_x) {
|
||||
const Point &p3 = next_value_modulo(iSegment, contour);
|
||||
if (int64_t(p3.x() - p2.x()) * int64_t(p1.x() - p2.x()) > 0) {
|
||||
// Ignore points of a contour touching the infill line from one side.
|
||||
continue;
|
||||
}
|
||||
is.pos_p = p2.y();
|
||||
is.pos_q = 1;
|
||||
} else {
|
||||
// First calculate the intersection parameter 't' as a rational number with non negative denominator.
|
||||
if (p2(0) > p1(0)) {
|
||||
is.pos_p = this_x - p1(0);
|
||||
is.pos_q = p2(0) - p1(0);
|
||||
if (p2.x() > p1.x()) {
|
||||
is.pos_p = this_x - p1.x();
|
||||
is.pos_q = p2.x() - p1.x();
|
||||
} else {
|
||||
is.pos_p = p1(0) - this_x;
|
||||
is.pos_q = p1(0) - p2(0);
|
||||
is.pos_p = p1.x() - this_x;
|
||||
is.pos_q = p1.x() - p2.x();
|
||||
}
|
||||
assert(is.pos_p >= 0 && is.pos_p <= is.pos_q);
|
||||
assert(is.pos_q > 1);
|
||||
assert(is.pos_p > 0 && is.pos_p < is.pos_q);
|
||||
// Make an intersection point from the 't'.
|
||||
is.pos_p *= int64_t(p2(1) - p1(1));
|
||||
is.pos_p += p1(1) * int64_t(is.pos_q);
|
||||
is.pos_p *= int64_t(p2.y() - p1.y());
|
||||
is.pos_p += p1.y() * int64_t(is.pos_q);
|
||||
}
|
||||
// +-1 to take rounding into account.
|
||||
assert(is.pos() + 1 >= std::min(p1(1), p2(1)));
|
||||
assert(is.pos() <= std::max(p1(1), p2(1)) + 1);
|
||||
assert(is.pos() + 1 >= std::min(p1.y(), p2.y()));
|
||||
assert(is.pos() <= std::max(p1.y(), p2.y()) + 1);
|
||||
segs[i].intersections.push_back(is);
|
||||
}
|
||||
}
|
||||
|
@ -844,55 +855,46 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
|||
size_t j = 0;
|
||||
for (size_t i = 0; i < sil.intersections.size(); ++ i) {
|
||||
// What is the orientation of the segment at the intersection point?
|
||||
size_t iContour = sil.intersections[i].iContour;
|
||||
const Points &contour = poly_with_offset.contour(iContour).points;
|
||||
size_t iSegment = sil.intersections[i].iSegment;
|
||||
size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1;
|
||||
coord_t dir = contour[iSegment](0) - contour[iPrev](0);
|
||||
bool low = dir > 0;
|
||||
sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ?
|
||||
SegmentIntersection &is = sil.intersections[i];
|
||||
const size_t iContour = is.iContour;
|
||||
const Points &contour = poly_with_offset.contour(iContour).points;
|
||||
const size_t iSegment = is.iSegment;
|
||||
const size_t iPrev = prev_idx_modulo(iSegment, contour);
|
||||
const coord_t dir = contour[iSegment].x() - contour[iPrev].x();
|
||||
const bool low = dir > 0;
|
||||
is.type = poly_with_offset.is_contour_outer(iContour) ?
|
||||
(low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) :
|
||||
(low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH);
|
||||
if (j > 0 && sil.intersections[i].iContour == sil.intersections[j-1].iContour) {
|
||||
// Two successive intersection points on a vertical line with the same contour. This may be a special case.
|
||||
if (sil.intersections[i].pos() == sil.intersections[j-1].pos()) {
|
||||
// Two successive segments meet exactly at the vertical line.
|
||||
#ifdef SLIC3R_DEBUG
|
||||
// Verify that the segments of sil.intersections[i] and sil.intersections[j-1] are adjoint.
|
||||
size_t iSegment2 = sil.intersections[j-1].iSegment;
|
||||
size_t iPrev2 = ((iSegment2 == 0) ? contour.size() : iSegment2) - 1;
|
||||
assert(iSegment == iPrev2 || iSegment2 == iPrev);
|
||||
#endif /* SLIC3R_DEBUG */
|
||||
if (sil.intersections[i].type == sil.intersections[j-1].type) {
|
||||
bool take_next = true;
|
||||
if (j > 0) {
|
||||
SegmentIntersection &is2 = sil.intersections[j - 1];
|
||||
if (iContour == is2.iContour && is.pos_q == 1 && is2.pos_q == 1) {
|
||||
// Two successive intersection points on a vertical line with the same contour, both points are end points of their respective contour segments.
|
||||
if (is.pos_p == is2.pos_p) {
|
||||
// Two successive segments meet exactly at the vertical line.
|
||||
// Verify that the segments of sil.intersections[i] and sil.intersections[j-1] are adjoint.
|
||||
assert(iSegment == prev_idx_modulo(is2.iSegment, contour) || is2.iSegment == iPrev);
|
||||
assert(is.type == is2.type);
|
||||
// Two successive segments of the same direction (both to the right or both to the left)
|
||||
// meet exactly at the vertical line.
|
||||
// Remove the second intersection point.
|
||||
} else {
|
||||
// This is a loop returning to the same point.
|
||||
// It may as well be a vertex of a loop touching this vertical line.
|
||||
// Remove both the lines.
|
||||
-- j;
|
||||
take_next = false;
|
||||
} else if (is.type == is2.type) {
|
||||
// Two non successive segments of the same direction (both to the right or both to the left)
|
||||
// meet exactly at the vertical line. That means there is a Z shaped path, where the center segment
|
||||
// of the Z shaped path is aligned with this vertical line.
|
||||
// Remove one of the intersection points while maximizing the vertical segment length.
|
||||
if (low) {
|
||||
// Remove the second intersection point, keep the first intersection point.
|
||||
} else {
|
||||
// Remove the first intersection point, keep the second intersection point.
|
||||
sil.intersections[j-1] = sil.intersections[i];
|
||||
}
|
||||
take_next = false;
|
||||
}
|
||||
} else if (sil.intersections[i].type == sil.intersections[j-1].type) {
|
||||
// Two non successive segments of the same direction (both to the right or both to the left)
|
||||
// meet exactly at the vertical line. That means there is a Z shaped path, where the center segment
|
||||
// of the Z shaped path is aligned with this vertical line.
|
||||
// Remove one of the intersection points while maximizing the vertical segment length.
|
||||
if (low) {
|
||||
// Remove the second intersection point, keep the first intersection point.
|
||||
} else {
|
||||
// Remove the first intersection point, keep the second intersection point.
|
||||
sil.intersections[j-1] = sil.intersections[i];
|
||||
}
|
||||
} else {
|
||||
// Vertical line intersects a contour segment at a general position (not at one of its end points).
|
||||
// or the contour just touches this vertical line with a vertical segment or a sequence of vertical segments.
|
||||
// Keep both intersection points.
|
||||
if (j < i)
|
||||
sil.intersections[j] = sil.intersections[i];
|
||||
++ j;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
if (take_next) {
|
||||
// Vertical line intersects a contour segment at a general position (not at one of its end points).
|
||||
if (j < i)
|
||||
sil.intersections[j] = sil.intersections[i];
|
||||
|
@ -905,7 +907,13 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
|||
}
|
||||
|
||||
// Verify the segments. If something is wrong, give up.
|
||||
#define ASSERT_THROW(CONDITION) do { assert(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0)
|
||||
#ifdef INFILL_DEBUG_OUTPUT
|
||||
#define INFILL_DEBUG_ASSERT(CONDITION)
|
||||
try {
|
||||
#else // INFILL_DEBUG_OUTPUT
|
||||
#define INFILL_DEBUG_ASSERT(CONDITION) assert(CONDITION)
|
||||
#endif // INFILL_DEBUG_OUTPUT
|
||||
#define ASSERT_THROW(CONDITION) do { INFILL_DEBUG_ASSERT(CONDITION); if (! (CONDITION)) throw InfillFailedException(); } while (0)
|
||||
for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) {
|
||||
SegmentedIntersectionLine &sil = segs[i_seg];
|
||||
// The intersection points have to be even.
|
||||
|
@ -925,6 +933,56 @@ static std::vector<SegmentedIntersectionLine> slice_region_by_vertical_lines(con
|
|||
}
|
||||
}
|
||||
#undef ASSERT_THROW
|
||||
#undef INFILL_DEBUG_ASSERT
|
||||
#ifdef INFILL_DEBUG_OUTPUT
|
||||
} catch (const InfillFailedException & /* ex */) {
|
||||
// Export the buggy result into an SVG file.
|
||||
static int iRun = 0;
|
||||
BoundingBox bbox = get_extents(poly_with_offset.polygons_src);
|
||||
bbox.offset(scale_(3.));
|
||||
::Slic3r::SVG svg(debug_out_path("slice_region_by_vertical_lines-failed-%d.svg", iRun ++), bbox);
|
||||
svg.draw(poly_with_offset.polygons_src);
|
||||
svg.draw_outline(poly_with_offset.polygons_src, "green");
|
||||
svg.draw_outline(poly_with_offset.polygons_outer, "green");
|
||||
svg.draw_outline(poly_with_offset.polygons_inner, "green");
|
||||
for (size_t i_seg = 0; i_seg < segs.size(); ++i_seg) {
|
||||
SegmentedIntersectionLine &sil = segs[i_seg];
|
||||
for (size_t i = 0; i < sil.intersections.size();) {
|
||||
// An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times.
|
||||
if (sil.intersections[i].type != SegmentIntersection::OUTER_LOW) {
|
||||
svg.draw(Point(sil.pos, sil.intersections[i].pos()), "red");
|
||||
break;
|
||||
}
|
||||
size_t j = i + 1;
|
||||
if (j == sil.intersections.size()) {
|
||||
svg.draw(Point(sil.pos, sil.intersections[i].pos()), "magenta");
|
||||
break;
|
||||
}
|
||||
if (! (sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH)) {
|
||||
svg.draw(Point(sil.pos, sil.intersections[j].pos()), "blue");
|
||||
break;
|
||||
}
|
||||
for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++j);
|
||||
if (j == sil.intersections.size()) {
|
||||
svg.draw(Point(sil.pos, sil.intersections[j - 1].pos()), "magenta");
|
||||
break;
|
||||
}
|
||||
if ((j & 1) != 1 || sil.intersections[j].type != SegmentIntersection::OUTER_HIGH) {
|
||||
svg.draw(Point(sil.pos, sil.intersections[j].pos()), "red");
|
||||
break;
|
||||
}
|
||||
if (! (i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH)) {
|
||||
svg.draw(Point(sil.pos, sil.intersections[j].pos()), "red");
|
||||
break;
|
||||
}
|
||||
svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[j].pos())), "black");
|
||||
i = j + 1;
|
||||
}
|
||||
}
|
||||
assert(false);
|
||||
throw;
|
||||
}
|
||||
#endif //INFILL_DEBUG_OUTPUT
|
||||
|
||||
return segs;
|
||||
}
|
||||
|
@ -2714,10 +2772,10 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
|||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// Transform the reference point to the rotated coordinate system.
|
||||
Point refpt = rotate_vector.second.rotated(- rotate_vector.first);
|
||||
// _align_to_grid will not work correctly with positive pattern_shift.
|
||||
// align_to_grid will not work correctly with positive pattern_shift.
|
||||
coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing;
|
||||
refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
|
||||
bounding_box.merge(_align_to_grid(
|
||||
bounding_box.merge(align_to_grid(
|
||||
bounding_box.min,
|
||||
Point(line_spacing, line_spacing),
|
||||
refpt));
|
||||
|
@ -2825,6 +2883,45 @@ bool FillRectilinear::fill_surface_by_lines(const Surface *surface, const FillPa
|
|||
return true;
|
||||
}
|
||||
|
||||
void make_fill_lines(const ExPolygonWithOffset &poly_with_offset, Point refpt, double angle, coord_t x_margin, coord_t line_spacing, coord_t pattern_shift, Polylines &fill_lines)
|
||||
{
|
||||
BoundingBox bounding_box = poly_with_offset.bounding_box_src();
|
||||
// Don't produce infill lines, which fully overlap with the infill perimeter.
|
||||
coord_t x_min = bounding_box.min.x() + x_margin;
|
||||
coord_t x_max = bounding_box.max.x() - x_margin;
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// align_to_grid will not work correctly with positive pattern_shift.
|
||||
coord_t pattern_shift_scaled = pattern_shift % line_spacing;
|
||||
refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
|
||||
bounding_box.merge(Slic3r::align_to_grid(bounding_box.min, Point(line_spacing, line_spacing), refpt));
|
||||
|
||||
// Intersect a set of euqally spaced vertical lines wiht expolygon.
|
||||
// n_vlines = ceil(bbox_width / line_spacing)
|
||||
const size_t n_vlines = (bounding_box.max.x() - bounding_box.min.x() + line_spacing - 1) / line_spacing;
|
||||
const double cos_a = cos(angle);
|
||||
const double sin_a = sin(angle);
|
||||
for (const SegmentedIntersectionLine &vline : slice_region_by_vertical_lines(poly_with_offset, n_vlines, bounding_box.min.x(), line_spacing))
|
||||
if (vline.pos >= x_min) {
|
||||
if (vline.pos > x_max)
|
||||
break;
|
||||
for (auto it = vline.intersections.begin(); it != vline.intersections.end();) {
|
||||
auto it_low = it ++;
|
||||
assert(it_low->type == SegmentIntersection::OUTER_LOW);
|
||||
if (it_low->type != SegmentIntersection::OUTER_LOW)
|
||||
continue;
|
||||
auto it_high = it;
|
||||
assert(it_high->type == SegmentIntersection::OUTER_HIGH);
|
||||
if (it_high->type == SegmentIntersection::OUTER_HIGH) {
|
||||
if (angle == 0.)
|
||||
fill_lines.emplace_back(Point(vline.pos, it_low->pos()), Point(vline.pos, it_high->pos()));
|
||||
else
|
||||
fill_lines.emplace_back(Point(vline.pos, it_low->pos()).rotated(cos_a, sin_a), Point(vline.pos, it_high->pos()).rotated(cos_a, sin_a));
|
||||
++ it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillParams params, const std::initializer_list<SweepParams> &sweep_params, Polylines &polylines_out)
|
||||
{
|
||||
assert(sweep_params.size() > 1);
|
||||
|
@ -2843,42 +2940,8 @@ bool FillRectilinear::fill_surface_by_multilines(const Surface *surface, FillPar
|
|||
std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
|
||||
for (const SweepParams &sweep : sweep_params) {
|
||||
// Rotate polygons so that we can work with vertical lines here
|
||||
double angle = rotate_vector.first + sweep.angle_base;
|
||||
ExPolygonWithOffset poly_with_offset(poly_with_offset_base, - angle);
|
||||
BoundingBox bounding_box = poly_with_offset.bounding_box_src();
|
||||
// Don't produce infill lines, which fully overlap with the infill perimeter.
|
||||
coord_t x_min = bounding_box.min.x() + line_width + coord_t(SCALED_EPSILON);
|
||||
coord_t x_max = bounding_box.max.x() - line_width - coord_t(SCALED_EPSILON);
|
||||
// extend bounding box so that our pattern will be aligned with other layers
|
||||
// Transform the reference point to the rotated coordinate system.
|
||||
Point refpt = rotate_vector.second.rotated(- angle);
|
||||
// _align_to_grid will not work correctly with positive pattern_shift.
|
||||
coord_t pattern_shift_scaled = coord_t(scale_(sweep.pattern_shift)) % line_spacing;
|
||||
refpt.x() -= (pattern_shift_scaled >= 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled);
|
||||
bounding_box.merge(_align_to_grid(bounding_box.min, Point(line_spacing, line_spacing), refpt));
|
||||
|
||||
// Intersect a set of euqally spaced vertical lines wiht expolygon.
|
||||
// n_vlines = ceil(bbox_width / line_spacing)
|
||||
const size_t n_vlines = (bounding_box.max.x() - bounding_box.min.x() + line_spacing - 1) / line_spacing;
|
||||
const double cos_a = cos(angle);
|
||||
const double sin_a = sin(angle);
|
||||
for (const SegmentedIntersectionLine &vline : slice_region_by_vertical_lines(poly_with_offset, n_vlines, bounding_box.min.x(), line_spacing))
|
||||
if (vline.pos > x_min) {
|
||||
if (vline.pos >= x_max)
|
||||
break;
|
||||
for (auto it = vline.intersections.begin(); it != vline.intersections.end();) {
|
||||
auto it_low = it ++;
|
||||
assert(it_low->type == SegmentIntersection::OUTER_LOW);
|
||||
if (it_low->type != SegmentIntersection::OUTER_LOW)
|
||||
continue;
|
||||
auto it_high = it;
|
||||
assert(it_high->type == SegmentIntersection::OUTER_HIGH);
|
||||
if (it_high->type == SegmentIntersection::OUTER_HIGH) {
|
||||
fill_lines.emplace_back(Point(vline.pos, it_low->pos()).rotated(cos_a, sin_a), Point(vline.pos, it_high->pos()).rotated(cos_a, sin_a));
|
||||
++ it;
|
||||
}
|
||||
}
|
||||
}
|
||||
float angle = rotate_vector.first + sweep.angle_base;
|
||||
make_fill_lines(ExPolygonWithOffset(poly_with_offset_base, - angle), rotate_vector.second.rotated(-angle), angle, line_width + coord_t(SCALED_EPSILON), line_spacing, coord_t(scale_(sweep.pattern_shift)), fill_lines);
|
||||
}
|
||||
|
||||
if (params.dont_connect() || fill_lines.size() <= 1) {
|
||||
|
@ -2954,4 +3017,29 @@ Polylines FillCubic::fill_surface(const Surface *surface, const FillParams ¶
|
|||
return polylines_out;
|
||||
}
|
||||
|
||||
Polylines FillSupportBase::fill_surface(const Surface *surface, const FillParams ¶ms)
|
||||
{
|
||||
assert(! params.full_infill());
|
||||
|
||||
Polylines polylines_out;
|
||||
std::pair<float, Point> rotate_vector = this->_infill_direction(surface);
|
||||
ExPolygonWithOffset poly_with_offset(surface->expolygon, - rotate_vector.first, float(scale_(this->overlap - 0.5 * this->spacing)));
|
||||
if (poly_with_offset.n_contours > 0) {
|
||||
Polylines fill_lines;
|
||||
coord_t line_spacing = coord_t(scale_(this->spacing) / params.density);
|
||||
// Create infill lines, keep them vertical.
|
||||
make_fill_lines(poly_with_offset, rotate_vector.second.rotated(- rotate_vector.first), 0, 0, line_spacing, 0, fill_lines);
|
||||
// Both the poly_with_offset and polylines_out are rotated, so the infill lines are strictly vertical.
|
||||
connect_base_support(std::move(fill_lines), poly_with_offset.polygons_outer, poly_with_offset.bounding_box_outer(), polylines_out, this->spacing, params);
|
||||
// Rotate back by rotate_vector.first
|
||||
const double cos_a = cos(rotate_vector.first);
|
||||
const double sin_a = sin(rotate_vector.first);
|
||||
for (Polyline &pl : polylines_out)
|
||||
for (Point &pt : pl.points)
|
||||
pt.rotate(cos_a, sin_a);
|
||||
}
|
||||
return polylines_out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -97,6 +97,17 @@ protected:
|
|||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
class FillSupportBase : public FillRectilinear
|
||||
{
|
||||
public:
|
||||
Fill* clone() const override { return new FillSupportBase(*this); }
|
||||
~FillSupportBase() override = default;
|
||||
Polylines fill_surface(const Surface *surface, const FillParams ¶ms) override;
|
||||
|
||||
protected:
|
||||
// The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill.
|
||||
float _layer_angle(size_t idx) const override { return 0.f; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
|
|
|
@ -89,18 +89,11 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
|
|||
|
||||
if (opt->percent) {
|
||||
auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height";
|
||||
auto opt_layer_height = config.option(opt_key_layer_height);
|
||||
auto opt_layer_height = config.option(opt_key_layer_height);
|
||||
if (opt_layer_height == nullptr)
|
||||
throw_on_missing_variable(opt_key, opt_key_layer_height);
|
||||
double layer_height = opt_layer_height->getFloat();
|
||||
if (first_layer && static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent) {
|
||||
// first_layer_height depends on layer_height.
|
||||
opt_layer_height = config.option("layer_height");
|
||||
if (opt_layer_height == nullptr)
|
||||
throw_on_missing_variable(opt_key, "layer_height");
|
||||
layer_height *= 0.01 * opt_layer_height->getFloat();
|
||||
}
|
||||
return opt->get_abs_value(layer_height);
|
||||
assert(! first_layer || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent);
|
||||
return opt->get_abs_value(opt_layer_height->getFloat());
|
||||
}
|
||||
|
||||
if (opt->value == 0.) {
|
||||
|
@ -238,13 +231,14 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
|
|||
|
||||
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
|
||||
{
|
||||
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width;
|
||||
const PrintConfig &print_config = object->print()->config();
|
||||
const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width;
|
||||
return Flow::new_from_config_width(
|
||||
frSupportMaterial,
|
||||
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
|
||||
(width.value > 0) ? width : object->config().extrusion_width,
|
||||
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)),
|
||||
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value)));
|
||||
float(print_config.nozzle_diameter.get_at(object->config().support_material_extruder-1)),
|
||||
(layer_height > 0.f) ? layer_height : float(print_config.first_layer_height.get_abs_value(object->config().layer_height.value)));
|
||||
}
|
||||
|
||||
Flow support_material_interface_flow(const PrintObject *object, float layer_height)
|
||||
|
|
|
@ -87,7 +87,7 @@ PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
|
|||
}
|
||||
|
||||
ArchiveData extract_sla_archive(const std::string &zipfname,
|
||||
const std::string &exclude)
|
||||
const std::string &exclude)
|
||||
{
|
||||
ArchiveData arch;
|
||||
|
||||
|
@ -248,7 +248,7 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
|
|||
{
|
||||
double incr, val, prev;
|
||||
bool stop = false;
|
||||
tbb::spin_mutex mutex;
|
||||
tbb::spin_mutex mutex = {};
|
||||
} st {100. / slices.size(), 0., 0.};
|
||||
|
||||
tbb::parallel_for(size_t(0), arch.images.size(),
|
||||
|
@ -371,6 +371,13 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print)
|
|||
m["numSlow"] = std::to_string(stats.slow_layers_count);
|
||||
m["numFast"] = std::to_string(stats.fast_layers_count);
|
||||
m["printTime"] = std::to_string(stats.estimated_print_time);
|
||||
|
||||
bool hollow_en = false;
|
||||
auto it = print.objects().begin();
|
||||
while (!hollow_en && it != print.objects().end())
|
||||
hollow_en = (*it++)->config().hollowing_enable;
|
||||
|
||||
m["hollow"] = hollow_en ? "1" : "0";
|
||||
|
||||
m["action"] = "print";
|
||||
}
|
||||
|
|
|
@ -435,19 +435,18 @@ namespace Slic3r {
|
|||
{
|
||||
std::string gcode;
|
||||
assert(m_layer_idx >= 0);
|
||||
if (!m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
|
||||
if (gcodegen.writer().need_toolchange(extruder_id) || finish_layer) {
|
||||
if (m_layer_idx < (int)m_tool_changes.size()) {
|
||||
if (!(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()))
|
||||
throw Slic3r::RuntimeError("Wipe tower generation failed, possibly due to empty first layer.");
|
||||
|
||||
|
||||
// Calculate where the wipe tower layer will be printed. -1 means that print z will not change,
|
||||
// resulting in a wipe tower with sparse layers.
|
||||
double wipe_tower_z = -1;
|
||||
bool ignore_sparse = false;
|
||||
if (gcodegen.config().wipe_tower_no_sparse_layers.value) {
|
||||
wipe_tower_z = m_last_wipe_tower_print_z;
|
||||
ignore_sparse = (m_brim_done && m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool);
|
||||
ignore_sparse = (m_tool_changes[m_layer_idx].size() == 1 && m_tool_changes[m_layer_idx].front().initial_tool == m_tool_changes[m_layer_idx].front().new_tool);
|
||||
if (m_tool_change_idx == 0 && !ignore_sparse)
|
||||
wipe_tower_z = m_last_wipe_tower_print_z + m_tool_changes[m_layer_idx].front().layer_height;
|
||||
}
|
||||
|
@ -457,7 +456,6 @@ namespace Slic3r {
|
|||
m_last_wipe_tower_print_z = wipe_tower_z;
|
||||
}
|
||||
}
|
||||
m_brim_done = true;
|
||||
}
|
||||
return gcode;
|
||||
}
|
||||
|
@ -786,7 +784,8 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
|
|||
namespace DoExport {
|
||||
static void init_gcode_processor(const PrintConfig& config, GCodeProcessor& processor, bool& silent_time_estimator_enabled)
|
||||
{
|
||||
silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlin) && config.silent_mode;
|
||||
silent_time_estimator_enabled = (config.gcode_flavor == gcfMarlinLegacy || config.gcode_flavor == gcfMarlinFirmware)
|
||||
&& config.silent_mode;
|
||||
processor.reset();
|
||||
processor.apply_config(config);
|
||||
processor.enable_stealth_time_estimator(silent_time_estimator_enabled);
|
||||
|
@ -1112,7 +1111,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
// Write some terse information on the slicing parameters.
|
||||
const PrintObject *first_object = print.objects().front();
|
||||
const double layer_height = first_object->config().layer_height.value;
|
||||
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height);
|
||||
assert(! print.config().first_layer_height.percent);
|
||||
const double first_layer_height = print.config().first_layer_height.value;
|
||||
for (const PrintRegion* region : print.regions()) {
|
||||
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width());
|
||||
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width());
|
||||
|
@ -1357,7 +1357,7 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
|
|||
bbox_prime.offset(0.5f);
|
||||
bool overlap = bbox_prime.overlap(bbox_print);
|
||||
|
||||
if (print.config().gcode_flavor == gcfMarlin) {
|
||||
if (print.config().gcode_flavor == gcfMarlinLegacy || print.config().gcode_flavor == gcfMarlinFirmware) {
|
||||
_write(file, this->retract());
|
||||
_write(file, "M300 S800 P500\n"); // Beep for 500ms, tone 800Hz.
|
||||
if (overlap) {
|
||||
|
@ -1560,7 +1560,8 @@ static bool custom_gcode_sets_temperature(const std::string &gcode, const int mc
|
|||
// Do not process this piece of G-code by the time estimator, it already knows the values through another sources.
|
||||
void GCode::print_machine_envelope(FILE *file, Print &print)
|
||||
{
|
||||
if (print.config().gcode_flavor.value == gcfMarlin && print.config().machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) {
|
||||
if ((print.config().gcode_flavor.value == gcfMarlinLegacy || print.config().gcode_flavor.value == gcfMarlinFirmware)
|
||||
&& print.config().machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) {
|
||||
fprintf(file, "M201 X%d Y%d Z%d E%d ; sets maximum accelerations, mm/sec^2\n",
|
||||
int(print.config().machine_max_acceleration_x.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_y.values.front() + 0.5),
|
||||
|
@ -1571,10 +1572,20 @@ void GCode::print_machine_envelope(FILE *file, Print &print)
|
|||
int(print.config().machine_max_feedrate_y.values.front() + 0.5),
|
||||
int(print.config().machine_max_feedrate_z.values.front() + 0.5),
|
||||
int(print.config().machine_max_feedrate_e.values.front() + 0.5));
|
||||
|
||||
// Now M204 - acceleration. This one is quite hairy thanks to how Marlin guys care about
|
||||
// backwards compatibility: https://github.com/prusa3d/PrusaSlicer/issues/1089
|
||||
// Legacy Marlin should export travel acceleration the same as printing acceleration.
|
||||
// MarlinFirmware has the two separated.
|
||||
int travel_acc = print.config().gcode_flavor == gcfMarlinLegacy
|
||||
? int(print.config().machine_max_acceleration_extruding.values.front() + 0.5)
|
||||
: int(print.config().machine_max_acceleration_travel.values.front() + 0.5);
|
||||
fprintf(file, "M204 P%d R%d T%d ; sets acceleration (P, T) and retract acceleration (R), mm/sec^2\n",
|
||||
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_retracting.values.front() + 0.5),
|
||||
int(print.config().machine_max_acceleration_extruding.values.front() + 0.5));
|
||||
travel_acc);
|
||||
|
||||
|
||||
fprintf(file, "M205 X%.2lf Y%.2lf Z%.2lf E%.2lf ; sets the jerk limits, mm/sec\n",
|
||||
print.config().machine_max_jerk_x.values.front(),
|
||||
print.config().machine_max_jerk_y.values.front(),
|
||||
|
@ -1768,6 +1779,10 @@ namespace ProcessLayer
|
|||
else {
|
||||
gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id);
|
||||
gcode += "\n";
|
||||
//FIXME Tell G-code writer that M600 filled the extruder, thus the G-code writer shall reset the extruder to unretracted state after
|
||||
// return from M600. Thus the G-code generated by the following line is ignored.
|
||||
// see GH issue #6362
|
||||
gcodegen.writer().unretract();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -75,8 +75,8 @@ public:
|
|||
m_tool_changes(tool_changes),
|
||||
m_final_purge(final_purge),
|
||||
m_layer_idx(-1),
|
||||
m_tool_change_idx(0),
|
||||
m_brim_done(false) {}
|
||||
m_tool_change_idx(0)
|
||||
{}
|
||||
|
||||
std::string prime(GCode &gcodegen);
|
||||
void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; }
|
||||
|
@ -105,8 +105,6 @@ private:
|
|||
// Current layer index.
|
||||
int m_layer_idx;
|
||||
int m_tool_change_idx;
|
||||
bool m_brim_done;
|
||||
bool i_have_brim = false;
|
||||
double m_last_wipe_tower_print_z = 0.f;
|
||||
};
|
||||
|
||||
|
|
|
@ -326,7 +326,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||
PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
|
||||
const char *line_start = gcode.c_str();
|
||||
const char *line_end = line_start;
|
||||
const char extrusion_axis = config.get_extrusion_axis()[0];
|
||||
const char extrusion_axis = get_extrusion_axis(config)[0];
|
||||
// Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
|
||||
// for a sequence of extrusion moves.
|
||||
size_t active_speed_modifier = size_t(-1);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
static const float INCHES_TO_MM = 25.4f;
|
||||
static const float MMMIN_TO_MMSEC = 1.0f / 60.0f;
|
||||
static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
||||
static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -190,6 +191,8 @@ void GCodeProcessor::TimeMachine::reset()
|
|||
enabled = false;
|
||||
acceleration = 0.0f;
|
||||
max_acceleration = 0.0f;
|
||||
travel_acceleration = 0.0f;
|
||||
max_travel_acceleration = 0.0f;
|
||||
extrude_factor_override_percentage = 1.0f;
|
||||
time = 0.0f;
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
|
@ -823,8 +826,13 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
|
||||
}
|
||||
|
||||
if (m_flavor == gcfMarlin && config.machine_limits_usage.value != MachineLimitsUsage::Ignore)
|
||||
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && config.machine_limits_usage.value != MachineLimitsUsage::Ignore) {
|
||||
m_time_processor.machine_limits = reinterpret_cast<const MachineEnvelopeConfig&>(config);
|
||||
if (m_flavor == gcfMarlinLegacy) {
|
||||
// Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead.
|
||||
m_time_processor.machine_limits.machine_max_acceleration_travel = m_time_processor.machine_limits.machine_max_acceleration_extruding;
|
||||
}
|
||||
}
|
||||
|
||||
// Filament load / unload times are not specific to a firmware flavor. Let anybody use it if they find it useful.
|
||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||
|
@ -842,10 +850,19 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
|||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
|
||||
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
|
||||
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
||||
}
|
||||
|
||||
m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
|
||||
m_use_volumetric_e = config.use_volumetric_e;
|
||||
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
|
||||
if (first_layer_height != nullptr)
|
||||
m_first_layer_height = std::abs(first_layer_height->value);
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
}
|
||||
|
||||
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
|
@ -934,7 +951,7 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
if (m_flavor == gcfMarlin) {
|
||||
if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) {
|
||||
const ConfigOptionFloats* machine_max_acceleration_x = config.option<ConfigOptionFloats>("machine_max_acceleration_x");
|
||||
if (machine_max_acceleration_x != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_x.values = machine_max_acceleration_x->values;
|
||||
|
@ -991,6 +1008,15 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
if (machine_max_acceleration_retracting != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_retracting.values = machine_max_acceleration_retracting->values;
|
||||
|
||||
|
||||
// Legacy Marlin does not have separate travel acceleration, it uses the 'extruding' value instead.
|
||||
const ConfigOptionFloats* machine_max_acceleration_travel = config.option<ConfigOptionFloats>(m_flavor == gcfMarlinLegacy
|
||||
? "machine_max_acceleration_extruding"
|
||||
: "machine_max_acceleration_travel");
|
||||
if (machine_max_acceleration_travel != nullptr)
|
||||
m_time_processor.machine_limits.machine_max_acceleration_travel.values = machine_max_acceleration_travel->values;
|
||||
|
||||
|
||||
const ConfigOptionFloats* machine_min_extruding_rate = config.option<ConfigOptionFloats>("machine_min_extruding_rate");
|
||||
if (machine_min_extruding_rate != nullptr)
|
||||
m_time_processor.machine_limits.machine_min_extruding_rate.values = machine_min_extruding_rate->values;
|
||||
|
@ -1004,6 +1030,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
float max_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_extruding, i);
|
||||
m_time_processor.machines[i].max_acceleration = max_acceleration;
|
||||
m_time_processor.machines[i].acceleration = (max_acceleration > 0.0f) ? max_acceleration : DEFAULT_ACCELERATION;
|
||||
float max_travel_acceleration = get_option_value(m_time_processor.machine_limits.machine_max_acceleration_travel, i);
|
||||
m_time_processor.machines[i].max_travel_acceleration = max_travel_acceleration;
|
||||
m_time_processor.machines[i].travel_acceleration = (max_travel_acceleration > 0.0f) ? max_travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
||||
}
|
||||
|
||||
if (m_time_processor.machine_limits.machine_max_acceleration_x.values.size() > 1)
|
||||
|
@ -1012,6 +1041,12 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e");
|
||||
if (use_volumetric_e != nullptr)
|
||||
m_use_volumetric_e = use_volumetric_e->value;
|
||||
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
|
||||
if (first_layer_height != nullptr)
|
||||
m_first_layer_height = std::abs(first_layer_height->value);
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
}
|
||||
|
||||
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
|
||||
|
@ -1037,6 +1072,9 @@ void GCodeProcessor::reset()
|
|||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
m_line_id = 0;
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_last_line_id = 0;
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
m_feedrate = 0.0f;
|
||||
m_width = 0.0f;
|
||||
|
@ -1059,6 +1097,10 @@ void GCodeProcessor::reset()
|
|||
|
||||
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
|
||||
m_extruded_last_z = 0.0f;
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
m_first_layer_height = 0.0f;
|
||||
m_processing_start_custom_gcode = false;
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
m_g1_line_id = 0;
|
||||
m_layer_id = 0;
|
||||
m_cp_color.reset();
|
||||
|
@ -1420,6 +1462,13 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
// extrusion role tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
|
||||
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()));
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
if (m_extrusion_role == erExternalPerimeter)
|
||||
m_seams_detector.activate(true);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
m_processing_start_custom_gcode = (m_extrusion_role == erCustom && m_g1_line_id == 0);
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1646,23 +1695,23 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment)
|
|||
if (pos != comment.npos) {
|
||||
const std::string_view flavor = comment.substr(pos + tag.length());
|
||||
if (flavor == "BFB")
|
||||
m_flavor = gcfMarlin; // << ???????????????????????
|
||||
m_flavor = gcfMarlinLegacy; // is this correct ?
|
||||
else if (flavor == "Mach3")
|
||||
m_flavor = gcfMach3;
|
||||
else if (flavor == "Makerbot")
|
||||
m_flavor = gcfMakerWare;
|
||||
else if (flavor == "UltiGCode")
|
||||
m_flavor = gcfMarlin; // << ???????????????????????
|
||||
m_flavor = gcfMarlinLegacy; // is this correct ?
|
||||
else if (flavor == "Marlin(Volumetric)")
|
||||
m_flavor = gcfMarlin; // << ???????????????????????
|
||||
m_flavor = gcfMarlinLegacy; // is this correct ?
|
||||
else if (flavor == "Griffin")
|
||||
m_flavor = gcfMarlin; // << ???????????????????????
|
||||
m_flavor = gcfMarlinLegacy; // is this correct ?
|
||||
else if (flavor == "Repetier")
|
||||
m_flavor = gcfRepetier;
|
||||
else if (flavor == "RepRap")
|
||||
m_flavor = gcfRepRapFirmware;
|
||||
else if (flavor == "Marlin")
|
||||
m_flavor = gcfMarlin;
|
||||
m_flavor = gcfMarlinLegacy;
|
||||
else
|
||||
BOOST_LOG_TRIVIAL(warning) << "GCodeProcessor found unknown flavor: " << flavor;
|
||||
|
||||
|
@ -2164,7 +2213,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
}
|
||||
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
|
||||
#else
|
||||
if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f))
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
type = EMoveType::Travel;
|
||||
|
||||
// time estimate section
|
||||
|
@ -2226,9 +2279,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
}
|
||||
|
||||
// calculates block acceleration
|
||||
float acceleration = is_extrusion_only_move(delta_pos) ?
|
||||
get_retract_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
|
||||
get_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i));
|
||||
float acceleration =
|
||||
(type == EMoveType::Travel) ? get_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
|
||||
(is_extrusion_only_move(delta_pos) ?
|
||||
get_retract_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)) :
|
||||
get_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i)));
|
||||
|
||||
for (unsigned char a = X; a <= E; ++a) {
|
||||
float axis_max_acceleration = get_axis_max_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), static_cast<Axis>(a));
|
||||
|
@ -2278,13 +2333,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
// Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
|
||||
float jerk =
|
||||
(v_exit > v_entry) ?
|
||||
(((v_entry > 0.0f) || (v_exit < 0.0f)) ?
|
||||
((v_entry > 0.0f || v_exit < 0.0f) ?
|
||||
// coasting
|
||||
(v_exit - v_entry) :
|
||||
// axis reversal
|
||||
std::max(v_exit, -v_entry)) :
|
||||
// v_exit <= v_entry
|
||||
(((v_entry < 0.0f) || (v_exit > 0.0f)) ?
|
||||
((v_entry < 0.0f || v_exit > 0.0f) ?
|
||||
// coasting
|
||||
(v_entry - v_exit) :
|
||||
// axis reversal
|
||||
|
@ -2305,7 +2360,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
float vmax_junction_threshold = vmax_junction * 0.99f;
|
||||
|
||||
// Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
|
||||
if ((prev.safe_feedrate > vmax_junction_threshold) && (curr.safe_feedrate > vmax_junction_threshold))
|
||||
if (prev.safe_feedrate > vmax_junction_threshold && curr.safe_feedrate > vmax_junction_threshold)
|
||||
vmax_junction = curr.safe_feedrate;
|
||||
}
|
||||
|
||||
|
@ -2329,6 +2384,31 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
machine.calculate_time(TimeProcessor::Planner::queue_size);
|
||||
}
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
// check for seam starting vertex
|
||||
if (type == EMoveType::Extrude && m_extrusion_role == erExternalPerimeter && m_seams_detector.is_active() && !m_seams_detector.has_first_vertex())
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[m_extruder_id]);
|
||||
// check for seam ending vertex and store the resulting move
|
||||
else if ((type != EMoveType::Extrude || m_extrusion_role != erExternalPerimeter) && m_seams_detector.is_active()) {
|
||||
auto set_end_position = [this](const Vec3f& pos) {
|
||||
m_end_position[X] = pos.x(); m_end_position[Y] = pos.y(); m_end_position[Z] = pos.z();
|
||||
};
|
||||
|
||||
assert(m_seams_detector.has_first_vertex());
|
||||
const Vec3f curr_pos(m_end_position[X], m_end_position[Y], m_end_position[Z]);
|
||||
const Vec3f new_pos = m_result.moves.back().position - m_extruder_offsets[m_extruder_id];
|
||||
const std::optional<Vec3f> first_vertex = m_seams_detector.get_first_vertex();
|
||||
// the threshold value = 0.25 is arbitrary, we may find some smarter condition later
|
||||
if ((new_pos - *first_vertex).norm() < 0.25f) {
|
||||
set_end_position(0.5f * (new_pos + *first_vertex));
|
||||
store_move_vertex(EMoveType::Seam);
|
||||
set_end_position(curr_pos);
|
||||
}
|
||||
|
||||
m_seams_detector.activate(false);
|
||||
}
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
// store move
|
||||
store_move_vertex(type);
|
||||
}
|
||||
|
@ -2575,7 +2655,7 @@ void GCodeProcessor::process_M203(const GCodeReader::GCodeLine& line)
|
|||
|
||||
// see http://reprap.org/wiki/G-code#M203:_Set_maximum_feedrate
|
||||
// http://smoothieware.org/supported-g-codes
|
||||
float factor = (m_flavor == gcfMarlin || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
|
||||
float factor = (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfSmoothie) ? 1.0f : MMMIN_TO_MMSEC;
|
||||
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
||||
|
@ -2602,10 +2682,11 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
|||
if (static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i) == PrintEstimatedTimeStatistics::ETimeMode::Normal ||
|
||||
m_time_processor.machine_envelope_processing_enabled) {
|
||||
if (line.has_value('S', value)) {
|
||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware,
|
||||
// and it is also generated by Slic3r to control acceleration per extrusion type
|
||||
// (there is a separate acceleration settings in Slicer for perimeter, first layer etc).
|
||||
// Legacy acceleration format. This format is used by the legacy Marlin, MK2 or MK3 firmware
|
||||
// It is also generated by PrusaSlicer to control acceleration per extrusion type
|
||||
// (perimeters, first layer etc) when 'Marlin (legacy)' flavor is used.
|
||||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('T', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
}
|
||||
|
@ -2615,11 +2696,9 @@ void GCodeProcessor::process_M204(const GCodeReader::GCodeLine& line)
|
|||
set_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
if (line.has_value('R', value))
|
||||
set_option_value(m_time_processor.machine_limits.machine_max_acceleration_retracting, i, value);
|
||||
if (line.has_value('T', value)) {
|
||||
if (line.has_value('T', value))
|
||||
// Interpret the T value as the travel acceleration in the new Marlin format.
|
||||
//FIXME Prusa3D firmware currently does not support travel acceleration value independent from the extruding acceleration value.
|
||||
// set_travel_acceleration(value);
|
||||
}
|
||||
set_travel_acceleration(static_cast<PrintEstimatedTimeStatistics::ETimeMode>(i), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2749,7 +2828,7 @@ void GCodeProcessor::process_T(const std::string_view command)
|
|||
int eid = 0;
|
||||
if (! parse_number(command.substr(1), eid) || eid < 0 || eid > 255) {
|
||||
// Specific to the MMU2 V2 (see https://www.help.prusa3d.com/en/article/prusa-specific-g-codes_112173):
|
||||
if (m_flavor == gcfMarlin && (command == "Tx" || command == "Tc" || command == "T?"))
|
||||
if ((m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware) && (command == "Tx" || command == "Tc" || command == "T?"))
|
||||
return;
|
||||
|
||||
// T-1 is a valid gcode line for RepRap Firmwares (used to deselects all tools) see https://github.com/prusa3d/PrusaSlicer/issues/5677
|
||||
|
@ -2783,15 +2862,29 @@ void GCodeProcessor::process_T(const std::string_view command)
|
|||
|
||||
void GCodeProcessor::store_move_vertex(EMoveType type)
|
||||
{
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
|
||||
m_line_id + 1 :
|
||||
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
MoveVertex vertex = {
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
m_last_line_id,
|
||||
#else
|
||||
(type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id,
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
type,
|
||||
m_extrusion_role,
|
||||
m_extruder_id,
|
||||
m_cp_color.current,
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
|
||||
#else
|
||||
Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
m_end_position[E] - m_start_position[E],
|
||||
m_feedrate,
|
||||
m_width,
|
||||
|
@ -2890,6 +2983,22 @@ void GCodeProcessor::set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mo
|
|||
}
|
||||
}
|
||||
|
||||
float GCodeProcessor::get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const
|
||||
{
|
||||
size_t id = static_cast<size_t>(mode);
|
||||
return (id < m_time_processor.machines.size()) ? m_time_processor.machines[id].travel_acceleration : DEFAULT_TRAVEL_ACCELERATION;
|
||||
}
|
||||
|
||||
void GCodeProcessor::set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value)
|
||||
{
|
||||
size_t id = static_cast<size_t>(mode);
|
||||
if (id < m_time_processor.machines.size()) {
|
||||
m_time_processor.machines[id].travel_acceleration = (m_time_processor.machines[id].max_travel_acceleration == 0.0f) ? value :
|
||||
// Clamp the acceleration with the maximum.
|
||||
std::min(value, m_time_processor.machines[id].max_travel_acceleration);
|
||||
}
|
||||
}
|
||||
|
||||
float GCodeProcessor::get_filament_load_time(size_t extruder_id)
|
||||
{
|
||||
return (m_time_processor.filament_load_times.empty() || m_time_processor.extruder_unloaded) ?
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
#include <optional>
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -20,6 +23,9 @@ namespace Slic3r {
|
|||
Noop,
|
||||
Retract,
|
||||
Unretract,
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
Seam,
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
Tool_change,
|
||||
Color_change,
|
||||
Pause_Print,
|
||||
|
@ -251,6 +257,9 @@ namespace Slic3r {
|
|||
float acceleration; // mm/s^2
|
||||
// hard limit for the acceleration, to which the firmware will clamp.
|
||||
float max_acceleration; // mm/s^2
|
||||
float travel_acceleration; // mm/s^2
|
||||
// hard limit for the travel acceleration, to which the firmware will clamp.
|
||||
float max_travel_acceleration; // mm/s^2
|
||||
float extrude_factor_override_percentage;
|
||||
float time; // s
|
||||
#if ENABLE_EXTENDED_M73_LINES
|
||||
|
@ -367,8 +376,7 @@ namespace Slic3r {
|
|||
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
int64_t time{ 0 };
|
||||
void reset()
|
||||
{
|
||||
void reset() {
|
||||
time = 0;
|
||||
moves = std::vector<MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
|
@ -377,8 +385,7 @@ namespace Slic3r {
|
|||
settings_ids.reset();
|
||||
}
|
||||
#else
|
||||
void reset()
|
||||
{
|
||||
void reset() {
|
||||
moves = std::vector<MoveVertex>();
|
||||
bed_shape = Pointfs();
|
||||
extruder_colors = std::vector<std::string>();
|
||||
|
@ -388,6 +395,29 @@ namespace Slic3r {
|
|||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
};
|
||||
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
class SeamsDetector
|
||||
{
|
||||
bool m_active{ false };
|
||||
std::optional<Vec3f> m_first_vertex;
|
||||
|
||||
public:
|
||||
void activate(bool active) {
|
||||
if (m_active != active) {
|
||||
m_active = active;
|
||||
if (m_active)
|
||||
m_first_vertex.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Vec3f> get_first_vertex() const { return m_first_vertex; }
|
||||
void set_first_vertex(const Vec3f& vertex) { m_first_vertex = vertex; }
|
||||
|
||||
bool is_active() const { return m_active; }
|
||||
bool has_first_vertex() const { return m_first_vertex.has_value(); }
|
||||
};
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
struct DataChecker
|
||||
{
|
||||
|
@ -473,6 +503,9 @@ namespace Slic3r {
|
|||
|
||||
#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
unsigned int m_line_id;
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
unsigned int m_last_line_id;
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
|
@ -487,10 +520,17 @@ namespace Slic3r {
|
|||
ExtruderTemps m_extruder_temps;
|
||||
std::vector<float> m_filament_diameters;
|
||||
float m_extruded_last_z;
|
||||
#if ENABLE_START_GCODE_VISUALIZATION
|
||||
float m_first_layer_height; // mm
|
||||
bool m_processing_start_custom_gcode;
|
||||
#endif // ENABLE_START_GCODE_VISUALIZATION
|
||||
unsigned int m_g1_line_id;
|
||||
unsigned int m_layer_id;
|
||||
CpColor m_cp_color;
|
||||
bool m_use_volumetric_e;
|
||||
#if ENABLE_SEAMS_VISUALIZATION
|
||||
SeamsDetector m_seams_detector;
|
||||
#endif // ENABLE_SEAMS_VISUALIZATION
|
||||
|
||||
enum class EProducer
|
||||
{
|
||||
|
@ -668,7 +708,9 @@ namespace Slic3r {
|
|||
float get_axis_max_jerk(PrintEstimatedTimeStatistics::ETimeMode mode, Axis axis) const;
|
||||
float get_retract_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
float get_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
||||
void set_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
||||
float get_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode) const;
|
||||
void set_travel_acceleration(PrintEstimatedTimeStatistics::ETimeMode mode, float value);
|
||||
float get_filament_load_time(size_t extruder_id);
|
||||
float get_filament_unload_time(size_t extruder_id);
|
||||
|
||||
|
|
|
@ -329,6 +329,16 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
|
|||
lt.extruders.front() = last_extruder_id;
|
||||
break;
|
||||
}
|
||||
|
||||
// On first layer with wipe tower, prefer a soluble extruder
|
||||
// at the beginning, so it is not wiped on the first layer.
|
||||
if (lt == m_layer_tools[0] && m_print_config_ptr && m_print_config_ptr->wipe_tower) {
|
||||
for (size_t i = 0; i<lt.extruders.size(); ++i)
|
||||
if (m_print_config_ptr->filament_soluble.get_at(lt.extruders[i]-1)) { // 1-based...
|
||||
std::swap(lt.extruders[i], lt.extruders.front());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
last_extruder_id = lt.extruders.back();
|
||||
}
|
||||
|
|
|
@ -9,17 +9,6 @@
|
|||
#include "BoundingBox.hpp"
|
||||
|
||||
|
||||
// Experimental "Peter's wipe tower" feature was partially implemented, inspired by
|
||||
// PJR's idea of alternating two perpendicular wiping directions on a square tower.
|
||||
// It is probably never going to be finished, there are multiple remaining issues
|
||||
// and there is probably no need to go down this way. m_peters_wipe_tower variable
|
||||
// turns this on, maybe it should just be removed. Anyway, the issues are
|
||||
// - layer's are not exactly square
|
||||
// - variable width for higher levels
|
||||
// - make sure it is not too sparse (apply max_bridge_distance and make last wipe longer)
|
||||
// - enable enhanced first layer adhesion
|
||||
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
|
@ -122,8 +111,10 @@ public:
|
|||
|
||||
WipeTowerWriter& feedrate(float f)
|
||||
{
|
||||
if (f != m_current_feedrate)
|
||||
if (f != m_current_feedrate) {
|
||||
m_gcode += "G1" + set_format_F(f) + "\n";
|
||||
m_current_feedrate = f;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -216,7 +207,7 @@ public:
|
|||
|
||||
WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
|
||||
{ return extrude(dest.x(), dest.y(), f); }
|
||||
|
||||
|
||||
WipeTowerWriter& rectangle(const Vec2f& ld,float width,float height,const float f = 0.f)
|
||||
{
|
||||
Vec2f corners[4];
|
||||
|
@ -242,6 +233,14 @@ public:
|
|||
return (*this);
|
||||
}
|
||||
|
||||
WipeTowerWriter& rectangle(const WipeTower::box_coordinates& box, const float f = 0.f)
|
||||
{
|
||||
rectangle(Vec2f(box.ld.x(), box.ld.y()),
|
||||
box.ru.x() - box.lu.x(),
|
||||
box.ru.y() - box.rd.y(), f);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
WipeTowerWriter& load(float e, float f = 0.f)
|
||||
{
|
||||
if (e == 0.f && (f == 0.f || f == m_current_feedrate))
|
||||
|
@ -343,7 +342,7 @@ public:
|
|||
WipeTowerWriter& speed_override_backup()
|
||||
{
|
||||
// This is only supported by Prusa at this point (https://github.com/prusa3d/PrusaSlicer/issues/3114)
|
||||
if (m_gcode_flavor == gcfMarlin)
|
||||
if (m_gcode_flavor == gcfMarlinLegacy || m_gcode_flavor == gcfMarlinFirmware)
|
||||
m_gcode += "M220 B\n";
|
||||
return *this;
|
||||
}
|
||||
|
@ -351,7 +350,7 @@ public:
|
|||
// Let the firmware restore the active speed override value.
|
||||
WipeTowerWriter& speed_override_restore()
|
||||
{
|
||||
if (m_gcode_flavor == gcfMarlin)
|
||||
if (m_gcode_flavor == gcfMarlinLegacy || m_gcode_flavor == gcfMarlinFirmware)
|
||||
m_gcode += "M220 R\n";
|
||||
return *this;
|
||||
}
|
||||
|
@ -523,13 +522,21 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
|
|||
m_wipe_tower_brim_width(float(config.wipe_tower_brim_width)),
|
||||
m_y_shift(0.f),
|
||||
m_z_pos(0.f),
|
||||
m_is_first_layer(false),
|
||||
m_bridging(float(config.wipe_tower_bridging)),
|
||||
m_no_sparse_layers(config.wipe_tower_no_sparse_layers),
|
||||
m_gcode_flavor(config.gcode_flavor),
|
||||
m_travel_speed(config.travel_speed),
|
||||
m_current_tool(initial_tool),
|
||||
wipe_volumes(wiping_matrix)
|
||||
{
|
||||
// Read absolute value of first layer speed, if given as percentage,
|
||||
// it is taken over following default. Speeds from config are not
|
||||
// easily accessible here.
|
||||
const float default_speed = 60.f;
|
||||
m_first_layer_speed = config.get_abs_value("first_layer_speed", default_speed);
|
||||
if (m_first_layer_speed == 0.f) // just to make sure autospeed doesn't break it.
|
||||
m_first_layer_speed = default_speed / 2.f;
|
||||
|
||||
// If this is a single extruder MM printer, we will use all the SE-specific config values.
|
||||
// Otherwise, the defaults will be used to turn off the SE stuff.
|
||||
if (m_semm) {
|
||||
|
@ -539,10 +546,24 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
|
|||
m_extra_loading_move = float(config.extra_loading_move);
|
||||
m_set_extruder_trimpot = config.high_current_on_filament_swap;
|
||||
}
|
||||
// Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes
|
||||
// Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
|
||||
const std::vector<Vec2d>& bed_points = config.bed_shape.values;
|
||||
BoundingBoxf bb(bed_points);
|
||||
m_bed_width = float(bb.size().x());
|
||||
m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
|
||||
m_bed_width = float(BoundingBoxf(bed_points).size().x());
|
||||
|
||||
if (m_bed_shape == CircularBed) {
|
||||
// this may still be a custom bed, check that the points are roughly on a circle
|
||||
double r2 = std::pow(m_bed_width/2., 2.);
|
||||
double lim2 = std::pow(m_bed_width/10., 2.);
|
||||
Vec2d center = bb.center();
|
||||
for (const Vec2d& pt : bed_points)
|
||||
if (std::abs(std::pow(pt.x()-center.x(), 2.) + std::pow(pt.y()-center.y(), 2.) - r2) > lim2) {
|
||||
m_bed_shape = CustomBed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_bed_bottom_left = m_bed_shape == RectangularBed
|
||||
? Vec2f(bed_points.front().x(), bed_points.front().y())
|
||||
: Vec2f::Zero();
|
||||
|
@ -556,6 +577,7 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
|||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
|
||||
m_filpar[idx].temperature = config.temperature.get_at(idx);
|
||||
m_filpar[idx].first_layer_temperature = config.first_layer_temperature.get_at(idx);
|
||||
|
||||
|
@ -617,10 +639,12 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
|
|||
|
||||
float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f);
|
||||
box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
|
||||
// In case of a circular bed, place it so it goes across the diameter and hope it will fit
|
||||
if (m_bed_shape == CircularBed)
|
||||
cleaning_box.translate(-m_bed_width/2 + m_bed_width * 0.03f, -m_bed_width * 0.12f);
|
||||
if (m_bed_shape == RectangularBed)
|
||||
if (m_bed_shape == CircularBed) {
|
||||
cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f);
|
||||
float total_width_half = tools.size() * prime_section_width / 2.f;
|
||||
cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f))));
|
||||
}
|
||||
else
|
||||
cleaning_box.translate(m_bed_bottom_left);
|
||||
|
||||
std::vector<ToolChangeResult> results;
|
||||
|
@ -680,7 +704,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
|
|||
if (m_set_extruder_trimpot)
|
||||
writer.set_extruder_trimpot(550);
|
||||
writer.speed_override_restore()
|
||||
.feedrate(6000)
|
||||
.feedrate(m_travel_speed * 60.f)
|
||||
.flush_planner_queue()
|
||||
.reset_extruder()
|
||||
.append("; CP PRIMING END\n"
|
||||
|
@ -694,21 +718,14 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
|
|||
m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear
|
||||
// in the output gcode - we should not remember emitting them (we will output them twice in the worst case)
|
||||
|
||||
// so that tool_change() will know to extrude the wipe tower brim:
|
||||
m_print_brim = true;
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
|
||||
{
|
||||
if ( m_print_brim )
|
||||
return toolchange_Brim();
|
||||
|
||||
size_t old_tool = m_current_tool;
|
||||
|
||||
float wipe_area = 0.f;
|
||||
bool last_change_in_layer = false;
|
||||
float wipe_area = 0.f;
|
||||
float wipe_volume = 0.f;
|
||||
|
||||
// Finds this toolchange info
|
||||
|
@ -716,9 +733,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
|
|||
{
|
||||
for (const auto &b : m_layer_info->tool_changes)
|
||||
if ( b.new_tool == tool ) {
|
||||
wipe_volume = b.wipe_volume;
|
||||
if (tool == m_layer_info->tool_changes.back().new_tool)
|
||||
last_change_in_layer = true;
|
||||
wipe_volume = b.wipe_volume;
|
||||
wipe_area = b.required_depth * m_layer_info->extra_spacing;
|
||||
break;
|
||||
}
|
||||
|
@ -727,17 +742,17 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
|
|||
// Otherwise we are going to Unload only. And m_layer_info would be invalid.
|
||||
}
|
||||
|
||||
box_coordinates cleaning_box(
|
||||
box_coordinates cleaning_box(
|
||||
Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f),
|
||||
m_wipe_tower_width - m_perimeter_width,
|
||||
(tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5f*m_perimeter_width
|
||||
(tool != (unsigned int)(-1) ? wipe_area+m_depth_traversed-0.5f*m_perimeter_width
|
||||
: m_wipe_tower_depth-m_perimeter_width));
|
||||
|
||||
WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
|
||||
.set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP TOOLCHANGE START\n")
|
||||
.comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based
|
||||
|
@ -759,7 +774,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
|
|||
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
||||
if (tool != (unsigned int)-1){ // This is not the last change.
|
||||
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
|
||||
m_is_first_layer ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
|
||||
is_first_layer() ? m_filpar[tool].first_layer_temperature : m_filpar[tool].temperature);
|
||||
toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
|
||||
toolchange_Load(writer, cleaning_box);
|
||||
writer.travel(writer.x(), writer.y()-m_perimeter_width); // cooling and loading were done a bit down the road
|
||||
|
@ -770,24 +785,10 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
|
|||
|
||||
m_depth_traversed += wipe_area;
|
||||
|
||||
if (last_change_in_layer) {// draw perimeter line
|
||||
writer.set_y_shift(m_y_shift);
|
||||
if (m_peters_wipe_tower)
|
||||
writer.rectangle(Vec2f::Zero(), m_layer_info->depth + 3*m_perimeter_width, m_wipe_tower_depth);
|
||||
else {
|
||||
writer.rectangle(Vec2f::Zero(), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle
|
||||
writer.add_wipe_point(writer.x(), writer.y())
|
||||
.add_wipe_point(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_set_extruder_trimpot)
|
||||
writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value.
|
||||
writer.speed_override_restore();
|
||||
writer.feedrate(6000)
|
||||
writer.feedrate(m_travel_speed * 60.f)
|
||||
.flush_planner_queue()
|
||||
.reset_extruder()
|
||||
.append("; CP TOOLCHANGE END\n"
|
||||
|
@ -801,66 +802,6 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool)
|
|||
return construct_tcr(writer, false, old_tool);
|
||||
}
|
||||
|
||||
WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_offset)
|
||||
{
|
||||
size_t old_tool = m_current_tool;
|
||||
|
||||
const box_coordinates wipeTower_box(
|
||||
Vec2f::Zero(),
|
||||
m_wipe_tower_width,
|
||||
m_wipe_tower_depth);
|
||||
|
||||
WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
|
||||
writer.set_extrusion_flow(m_extrusion_flow * 1.1f)
|
||||
.set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop.
|
||||
.set_initial_tool(m_current_tool)
|
||||
.append(";-------------------------------------\n"
|
||||
"; CP WIPE TOWER FIRST LAYER BRIM START\n");
|
||||
|
||||
Vec2f initial_position = wipeTower_box.lu - Vec2f(m_wipe_tower_brim_width + 2*m_perimeter_width, 0);
|
||||
writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
// Prime the extruder left of the wipe tower.
|
||||
writer.extrude_explicit(wipeTower_box.ld - Vec2f(m_wipe_tower_brim_width + 2*m_perimeter_width, 0),
|
||||
1.5f * m_extrusion_flow * (wipeTower_box.lu.y() - wipeTower_box.ld.y()), 2400);
|
||||
|
||||
// The tool is supposed to be active and primed at the time when the wipe tower brim is extruded.
|
||||
// Extrude brim around the future wipe tower ('normal' spacing with no extra void space).
|
||||
box_coordinates box(wipeTower_box);
|
||||
float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4);
|
||||
|
||||
// How many perimeters shall the brim have?
|
||||
size_t loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing;
|
||||
|
||||
for (size_t i = 0; i < loops_num; ++ i) {
|
||||
box.expand(spacing);
|
||||
writer.travel (box.ld, 7000)
|
||||
.extrude(box.lu, 2100).extrude(box.ru)
|
||||
.extrude(box.rd ).extrude(box.ld);
|
||||
}
|
||||
|
||||
// Save actual brim width to be later passed to the Print object, which will use it
|
||||
// for skirt calculation and pass it to GLCanvas for precise preview box
|
||||
m_wipe_tower_brim_width_real = wipeTower_box.ld.x() - box.ld.x() + spacing/2.f;
|
||||
|
||||
box.expand(-spacing);
|
||||
writer.add_wipe_point(writer.x(), writer.y())
|
||||
.add_wipe_point(box.ld)
|
||||
.add_wipe_point(box.rd);
|
||||
|
||||
writer.append("; CP WIPE TOWER FIRST LAYER BRIM END\n"
|
||||
";-----------------------------------\n");
|
||||
|
||||
m_print_brim = false; // Mark the brim as extruded
|
||||
|
||||
// Ask our writer about how much material was consumed:
|
||||
if (m_current_tool < m_used_filament_length.size())
|
||||
m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length();
|
||||
|
||||
return construct_tcr(writer, false, old_tool);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
||||
void WipeTower::toolchange_Unload(
|
||||
|
@ -957,7 +898,7 @@ void WipeTower::toolchange_Unload(
|
|||
// be already set and there is no need to change anything. Also, the temperature could be changed
|
||||
// for wrong extruder.
|
||||
if (m_semm) {
|
||||
if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait.
|
||||
if (new_temperature != 0 && (new_temperature != m_old_temperature || is_first_layer()) ) { // Set the extruder temperature, but don't wait.
|
||||
// If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset)
|
||||
// However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off).
|
||||
writer.set_extruder_temp(new_temperature, false);
|
||||
|
@ -1017,7 +958,10 @@ void WipeTower::toolchange_Change(
|
|||
// gcode could have left the extruder somewhere, we cannot just start extruding. We should also inform the
|
||||
// postprocessor that we absolutely want to have this in the gcode, even if it thought it is the same as before.
|
||||
Vec2f current_pos = writer.pos_rotated();
|
||||
writer.append(std::string("G1 X") + std::to_string(current_pos.x()) + " Y" + std::to_string(current_pos.y()) + never_skip_tag() + "\n");
|
||||
writer.feedrate(m_travel_speed * 60.f) // see https://github.com/prusa3d/PrusaSlicer/issues/5483
|
||||
.append(std::string("G1 X") + std::to_string(current_pos.x())
|
||||
+ " Y" + std::to_string(current_pos.y())
|
||||
+ never_skip_tag() + "\n");
|
||||
|
||||
// The toolchange Tn command will be inserted later, only in case that the user does
|
||||
// not provide a custom toolchange gcode.
|
||||
|
@ -1063,9 +1007,8 @@ void WipeTower::toolchange_Wipe(
|
|||
float wipe_volume)
|
||||
{
|
||||
// Increase flow on first layer, slow down print.
|
||||
writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f))
|
||||
writer.set_extrusion_flow(m_extrusion_flow * (is_first_layer() ? 1.18f : 1.f))
|
||||
.append("; CP TOOLCHANGE WIPE\n");
|
||||
float wipe_coeff = m_is_first_layer ? 0.5f : 1.f;
|
||||
const float& xl = cleaning_box.ld.x();
|
||||
const float& xr = cleaning_box.rd.x();
|
||||
|
||||
|
@ -1075,7 +1018,9 @@ void WipeTower::toolchange_Wipe(
|
|||
|
||||
float x_to_wipe = volume_to_length(wipe_volume, m_perimeter_width, m_layer_height);
|
||||
float dy = m_extra_spacing*m_perimeter_width;
|
||||
float wipe_speed = 1600.f;
|
||||
|
||||
const float target_speed = is_first_layer() ? m_first_layer_speed * 60.f : 4800.f;
|
||||
float wipe_speed = 0.33f * target_speed;
|
||||
|
||||
// if there is less than 2.5*m_perimeter_width to the edge, advance straightaway (there is likely a blob anyway)
|
||||
if ((m_left_to_right ? xr-writer.x() : writer.x()-xl) < 2.5f*m_perimeter_width) {
|
||||
|
@ -1086,17 +1031,17 @@ void WipeTower::toolchange_Wipe(
|
|||
// now the wiping itself:
|
||||
for (int i = 0; true; ++i) {
|
||||
if (i!=0) {
|
||||
if (wipe_speed < 1610.f) wipe_speed = 1800.f;
|
||||
else if (wipe_speed < 1810.f) wipe_speed = 2200.f;
|
||||
else if (wipe_speed < 2210.f) wipe_speed = 4200.f;
|
||||
else wipe_speed = std::min(4800.f, wipe_speed + 50.f);
|
||||
if (wipe_speed < 0.34f * target_speed) wipe_speed = 0.375f * target_speed;
|
||||
else if (wipe_speed < 0.377 * target_speed) wipe_speed = 0.458f * target_speed;
|
||||
else if (wipe_speed < 0.46f * target_speed) wipe_speed = 0.875f * target_speed;
|
||||
else wipe_speed = std::min(target_speed, wipe_speed + 50.f);
|
||||
}
|
||||
|
||||
float traversed_x = writer.x();
|
||||
if (m_left_to_right)
|
||||
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||
writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed);
|
||||
else
|
||||
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff);
|
||||
writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed);
|
||||
|
||||
if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*m_perimeter_width)
|
||||
break; // in case next line would not fit
|
||||
|
@ -1112,17 +1057,16 @@ void WipeTower::toolchange_Wipe(
|
|||
m_left_to_right = !m_left_to_right;
|
||||
}
|
||||
|
||||
// this is neither priming nor not the last toolchange on this layer - we are
|
||||
// going back to the model - wipe the nozzle.
|
||||
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool) {
|
||||
// We may be going back to the model - wipe the nozzle. If this is followed
|
||||
// by finish_layer, this wipe path will be overwritten.
|
||||
writer.add_wipe_point(writer.x(), writer.y())
|
||||
.add_wipe_point(writer.x(), writer.y() - dy)
|
||||
.add_wipe_point(! m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy);
|
||||
|
||||
if (m_layer_info != m_plan.end() && m_current_tool != m_layer_info->tool_changes.back().new_tool)
|
||||
m_left_to_right = !m_left_to_right;
|
||||
writer.add_wipe_point(writer.x(), writer.y())
|
||||
.add_wipe_point(writer.x(), writer.y() - dy)
|
||||
.add_wipe_point(m_left_to_right ? m_wipe_tower_width : 0.f, writer.y() - dy);
|
||||
|
||||
}
|
||||
|
||||
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
|
||||
writer.set_extrusion_flow(m_extrusion_flow); // Reset the extrusion flow.
|
||||
}
|
||||
|
||||
|
||||
|
@ -1130,9 +1074,8 @@ void WipeTower::toolchange_Wipe(
|
|||
|
||||
WipeTower::ToolChangeResult WipeTower::finish_layer()
|
||||
{
|
||||
// This should only be called if the layer is not finished yet.
|
||||
// Otherwise the caller would likely travel to the wipe tower in vain.
|
||||
assert(! this->layer_finished());
|
||||
m_current_layer_finished = true;
|
||||
|
||||
size_t old_tool = m_current_tool;
|
||||
|
||||
|
@ -1140,61 +1083,75 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower ? m_layer_info->toolchanges_depth() : 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP EMPTY GRID START\n")
|
||||
.comment_with_value(" layer #", m_num_layer_changes + 1);
|
||||
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f));
|
||||
|
||||
|
||||
// Slow down on the 1st layer.
|
||||
float speed_factor = m_is_first_layer ? 0.5f : 1.f;
|
||||
bool first_layer = is_first_layer();
|
||||
float feedrate = first_layer ? m_first_layer_speed * 60.f : 2900.f;
|
||||
float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth();
|
||||
box_coordinates fill_box(Vec2f(m_perimeter_width, m_depth_traversed + m_perimeter_width),
|
||||
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
|
||||
box_coordinates fill_box(Vec2f(m_perimeter_width, m_layer_info->depth-(current_depth-m_perimeter_width)),
|
||||
m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width);
|
||||
|
||||
|
||||
writer.set_initial_position((m_left_to_right ? fill_box.ru : fill_box.lu), // so there is never a diagonal travel
|
||||
m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation);
|
||||
|
||||
bool toolchanges_on_layer = m_layer_info->toolchanges_depth() > WT_EPSILON;
|
||||
box_coordinates box = fill_box;
|
||||
for (int i=0;i<2;++i) {
|
||||
if (! toolchanges_on_layer) {
|
||||
if (i==0) box.expand(m_perimeter_width);
|
||||
else box.expand(-m_perimeter_width);
|
||||
}
|
||||
else i=2; // only draw the inner perimeter, outer has been already drawn by tool_change(...)
|
||||
writer.rectangle(box.ld, box.rd.x()-box.ld.x(), box.ru.y()-box.rd.y(), 2900*speed_factor);
|
||||
}
|
||||
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)),
|
||||
m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
|
||||
|
||||
// inner perimeter of the sparse section, if there is space for it:
|
||||
if (fill_box.ru.y() - fill_box.rd.y() > m_perimeter_width - WT_EPSILON)
|
||||
writer.rectangle(fill_box.ld, fill_box.rd.x()-fill_box.ld.x(), fill_box.ru.y()-fill_box.rd.y(), feedrate);
|
||||
|
||||
// we are in one of the corners, travel to ld along the perimeter:
|
||||
if (writer.x() > fill_box.ld.x()+EPSILON) writer.travel(fill_box.ld.x(),writer.y());
|
||||
if (writer.y() > fill_box.ld.y()+EPSILON) writer.travel(writer.x(),fill_box.ld.y());
|
||||
|
||||
if (m_is_first_layer && m_adhesion) {
|
||||
// Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower.
|
||||
box.expand(-m_perimeter_width/2.f);
|
||||
int nsteps = int(floor((box.lu.y() - box.ld.y()) / (2*m_perimeter_width)));
|
||||
float step = (box.lu.y() - box.ld.y()) / nsteps;
|
||||
writer.travel(box.ld - Vec2f(m_perimeter_width/2.f, m_perimeter_width/2.f));
|
||||
if (nsteps >= 0)
|
||||
for (int i = 0; i < nsteps; ++i) {
|
||||
writer.extrude(box.ld.x()+m_perimeter_width/2.f, writer.y() + 0.5f * step);
|
||||
writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y());
|
||||
writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step);
|
||||
writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y());
|
||||
// Extrude infill to support the material to be printed above.
|
||||
const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
|
||||
float left = fill_box.lu.x() + 2*m_perimeter_width;
|
||||
float right = fill_box.ru.x() - 2 * m_perimeter_width;
|
||||
if (dy > m_perimeter_width)
|
||||
{
|
||||
writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f))
|
||||
.append(";--------------------\n"
|
||||
"; CP EMPTY GRID START\n")
|
||||
.comment_with_value(" layer #", m_num_layer_changes + 1);
|
||||
|
||||
// Is there a soluble filament wiped/rammed at the next layer?
|
||||
// If so, the infill should not be sparse.
|
||||
bool solid_infill = m_layer_info+1 == m_plan.end()
|
||||
? false
|
||||
: std::any_of((m_layer_info+1)->tool_changes.begin(),
|
||||
(m_layer_info+1)->tool_changes.end(),
|
||||
[this](const WipeTowerInfo::ToolChange& tch) {
|
||||
return m_filpar[tch.new_tool].is_soluble
|
||||
|| m_filpar[tch.old_tool].is_soluble;
|
||||
});
|
||||
solid_infill |= first_layer && m_adhesion;
|
||||
|
||||
if (solid_infill) {
|
||||
float sparse_factor = 1.5f; // 1=solid, 2=every other line, etc.
|
||||
if (first_layer) { // the infill should touch perimeters
|
||||
left -= m_perimeter_width;
|
||||
right += m_perimeter_width;
|
||||
sparse_factor = 1.f;
|
||||
}
|
||||
writer.add_wipe_point(writer.x(), writer.y())
|
||||
.add_wipe_point(box.rd.x()-m_perimeter_width/2.f,writer.y());
|
||||
}
|
||||
else { // Extrude a sparse infill to support the material to be printed above.
|
||||
const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
|
||||
const float left = fill_box.lu.x() + 2*m_perimeter_width;
|
||||
const float right = fill_box.ru.x() - 2 * m_perimeter_width;
|
||||
if (dy > m_perimeter_width)
|
||||
{
|
||||
// Extrude an inverse U at the left of the region.
|
||||
writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f))
|
||||
.extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor);
|
||||
float y = fill_box.ld.y() + m_perimeter_width;
|
||||
int n = dy / (m_perimeter_width * sparse_factor);
|
||||
float spacing = (dy-m_perimeter_width)/(n-1);
|
||||
int i=0;
|
||||
for (i=0; i<n; ++i) {
|
||||
writer.extrude(writer.x(), y, feedrate)
|
||||
.extrude(i%2 ? left : right, y);
|
||||
y = y + spacing;
|
||||
}
|
||||
writer.extrude(writer.x(), fill_box.lu.y());
|
||||
} else {
|
||||
// Extrude an inverse U at the left of the region and the sparse infill.
|
||||
writer.extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), feedrate);
|
||||
|
||||
const int n = 1+int((right-left)/m_bridging);
|
||||
const float dx = (right-left)/n;
|
||||
|
@ -1203,18 +1160,40 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||
writer.travel(x,writer.y());
|
||||
writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y());
|
||||
}
|
||||
writer.add_wipe_point(Vec2f(writer.x(), writer.y()))
|
||||
.add_wipe_point(Vec2f(left, writer.y()));
|
||||
}
|
||||
else {
|
||||
writer.add_wipe_point(Vec2f(writer.x(), writer.y()))
|
||||
.add_wipe_point(Vec2f(right, writer.y()));
|
||||
}
|
||||
}
|
||||
writer.append("; CP EMPTY GRID END\n"
|
||||
";------------------\n\n\n\n\n\n\n");
|
||||
|
||||
m_depth_traversed = m_wipe_tower_depth-m_perimeter_width;
|
||||
writer.append("; CP EMPTY GRID END\n"
|
||||
";------------------\n\n\n\n\n\n\n");
|
||||
}
|
||||
|
||||
// outer perimeter (always):
|
||||
writer.rectangle(wt_box, feedrate);
|
||||
|
||||
// brim (first layer only)
|
||||
if (first_layer) {
|
||||
box_coordinates box = wt_box;
|
||||
float spacing = m_perimeter_width - m_layer_height*float(1.-M_PI_4);
|
||||
// How many perimeters shall the brim have?
|
||||
size_t loops_num = (m_wipe_tower_brim_width + spacing/2.f) / spacing;
|
||||
|
||||
for (size_t i = 0; i < loops_num; ++ i) {
|
||||
box.expand(spacing);
|
||||
writer.rectangle(box);
|
||||
}
|
||||
|
||||
// Save actual brim width to be later passed to the Print object, which will use it
|
||||
// for skirt calculation and pass it to GLCanvas for precise preview box
|
||||
m_wipe_tower_brim_width_real = wt_box.ld.x() - box.ld.x() + spacing/2.f;
|
||||
wt_box = box;
|
||||
}
|
||||
|
||||
// Now prepare future wipe. box contains rectangle that was extruded last (ccw).
|
||||
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd :
|
||||
(writer.pos() == wt_box.rd ? wt_box.ru :
|
||||
(writer.pos() == wt_box.ru ? wt_box.lu :
|
||||
wt_box.ld)));
|
||||
writer.add_wipe_point(writer.pos())
|
||||
.add_wipe_point(target);
|
||||
|
||||
|
||||
// Ask our writer about how much material was consumed.
|
||||
|
@ -1227,23 +1206,22 @@ WipeTower::ToolChangeResult WipeTower::finish_layer()
|
|||
}
|
||||
|
||||
// Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box
|
||||
void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume)
|
||||
void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool,
|
||||
unsigned int new_tool, float wipe_volume)
|
||||
{
|
||||
assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON); // refuses to add a layer below the last one
|
||||
|
||||
if (m_plan.empty() || m_plan.back().z + WT_EPSILON < z_par) // if we moved to a new layer, we'll add it to m_plan first
|
||||
m_plan.push_back(WipeTowerInfo(z_par, layer_height_par));
|
||||
|
||||
if (brim) { // this toolchange prints brim - we must add it to m_plan, but not to count its depth
|
||||
m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool));
|
||||
return;
|
||||
}
|
||||
if (m_first_layer_idx == size_t(-1) && (! m_no_sparse_layers || old_tool != new_tool))
|
||||
m_first_layer_idx = m_plan.size() - 1;
|
||||
|
||||
if (old_tool==new_tool) // new layer without toolchanges - we are done
|
||||
return;
|
||||
if (old_tool == new_tool) // new layer without toolchanges - we are done
|
||||
return;
|
||||
|
||||
// this is an actual toolchange - let's calculate depth to reserve on the wipe tower
|
||||
float depth = 0.f;
|
||||
float depth = 0.f;
|
||||
float width = m_wipe_tower_width - 3*m_perimeter_width;
|
||||
float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f),
|
||||
m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator,
|
||||
|
@ -1293,28 +1271,65 @@ void WipeTower::save_on_last_wipe()
|
|||
if (m_layer_info->tool_changes.size()==0) // we have no way to save anything on an empty layer
|
||||
continue;
|
||||
|
||||
for (const auto &toolchange : m_layer_info->tool_changes)
|
||||
// Which toolchange will finish_layer extrusions be subtracted from?
|
||||
int idx = first_toolchange_to_nonsoluble(m_layer_info->tool_changes);
|
||||
|
||||
for (int i=0; i<int(m_layer_info->tool_changes.size()); ++i) {
|
||||
auto& toolchange = m_layer_info->tool_changes[i];
|
||||
tool_change(toolchange.new_tool);
|
||||
|
||||
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
|
||||
float length_to_save = 2*(m_wipe_tower_width+m_wipe_tower_depth) + (!layer_finished() ? finish_layer().total_extrusion_length_in_plane() : 0.f);
|
||||
float length_to_wipe = volume_to_length(m_layer_info->tool_changes.back().wipe_volume,
|
||||
m_perimeter_width,m_layer_info->height) - m_layer_info->tool_changes.back().first_wipe_line - length_to_save;
|
||||
if (i == idx) {
|
||||
float width = m_wipe_tower_width - 3*m_perimeter_width; // width we draw into
|
||||
float length_to_save = finish_layer().total_extrusion_length_in_plane();
|
||||
float length_to_wipe = volume_to_length(toolchange.wipe_volume,
|
||||
m_perimeter_width, m_layer_info->height) - toolchange.first_wipe_line - length_to_save;
|
||||
|
||||
length_to_wipe = std::max(length_to_wipe,0.f);
|
||||
float depth_to_wipe = m_perimeter_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
|
||||
length_to_wipe = std::max(length_to_wipe,0.f);
|
||||
float depth_to_wipe = m_perimeter_width * (std::floor(length_to_wipe/width) + ( length_to_wipe > 0.f ? 1.f : 0.f ) ) * m_extra_spacing;
|
||||
|
||||
//depth += (int(length_to_extrude / width) + 1) * m_perimeter_width;
|
||||
m_layer_info->tool_changes.back().required_depth = m_layer_info->tool_changes.back().ramming_depth + depth_to_wipe;
|
||||
toolchange.required_depth = toolchange.ramming_depth + depth_to_wipe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return index of first toolchange that switches to non-soluble extruder
|
||||
// ot -1 if there is no such toolchange.
|
||||
int WipeTower::first_toolchange_to_nonsoluble(
|
||||
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const
|
||||
{
|
||||
for (size_t idx=0; idx<tool_changes.size(); ++idx)
|
||||
if (! m_filpar[tool_changes[idx].new_tool].is_soluble)
|
||||
return idx;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
|
||||
WipeTower::ToolChangeResult& second)
|
||||
{
|
||||
assert(first.new_tool == second.initial_tool);
|
||||
WipeTower::ToolChangeResult out = first;
|
||||
if (first.end_pos != second.start_pos) {
|
||||
char buf[2048]; // Add a travel move from tc1.end_pos to tc2.start_pos.
|
||||
sprintf(buf, "G1 X%.3f Y%.3f F7200\n", second.start_pos.x(), second.start_pos.y());
|
||||
out.gcode += buf;
|
||||
}
|
||||
out.gcode += second.gcode;
|
||||
out.extrusions.insert(out.extrusions.end(), second.extrusions.begin(), second.extrusions.end());
|
||||
out.end_pos = second.end_pos;
|
||||
out.wipe_path = second.wipe_path;
|
||||
out.initial_tool = first.initial_tool;
|
||||
out.new_tool = second.new_tool;
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
// Processes vector m_plan and calls respective functions to generate G-code for the wipe tower
|
||||
// Resulting ToolChangeResults are appended into vector "result"
|
||||
void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &result)
|
||||
{
|
||||
if (m_plan.empty())
|
||||
|
||||
return;
|
||||
|
||||
m_extra_spacing = 1.f;
|
||||
|
@ -1325,9 +1340,6 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
|||
plan_tower();
|
||||
}
|
||||
|
||||
if (m_peters_wipe_tower)
|
||||
make_wipe_tower_square();
|
||||
|
||||
m_layer_info = m_plan.begin();
|
||||
|
||||
// we don't know which extruder to start with - we'll set it according to the first toolchange
|
||||
|
@ -1346,65 +1358,41 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
|
|||
std::vector<WipeTower::ToolChangeResult> layer_result;
|
||||
for (auto layer : m_plan)
|
||||
{
|
||||
set_layer(layer.z,layer.height,0,layer.z == m_plan.front().z,layer.z == m_plan.back().z);
|
||||
if (m_peters_wipe_tower)
|
||||
m_internal_rotation += 90.f;
|
||||
else
|
||||
m_internal_rotation += 180.f;
|
||||
set_layer(layer.z, layer.height, 0, false/*layer.z == m_plan.front().z*/, layer.z == m_plan.back().z);
|
||||
m_internal_rotation += 180.f;
|
||||
|
||||
if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
|
||||
if (m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width)
|
||||
m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f;
|
||||
|
||||
for (const auto &toolchange : layer.tool_changes)
|
||||
layer_result.emplace_back(tool_change(toolchange.new_tool));
|
||||
int idx = first_toolchange_to_nonsoluble(layer.tool_changes);
|
||||
ToolChangeResult finish_layer_tcr;
|
||||
|
||||
if (! layer_finished()) {
|
||||
auto finish_layer_toolchange = finish_layer();
|
||||
if ( ! layer.tool_changes.empty() ) { // we will merge it to the last toolchange
|
||||
auto& last_toolchange = layer_result.back();
|
||||
if (last_toolchange.end_pos != finish_layer_toolchange.start_pos) {
|
||||
char buf[2048]; // Add a travel move from tc1.end_pos to tc2.start_pos.
|
||||
sprintf(buf, "G1 X%.3f Y%.3f F7200\n", finish_layer_toolchange.start_pos.x(), finish_layer_toolchange.start_pos.y());
|
||||
last_toolchange.gcode += buf;
|
||||
}
|
||||
last_toolchange.gcode += finish_layer_toolchange.gcode;
|
||||
last_toolchange.extrusions.insert(last_toolchange.extrusions.end(), finish_layer_toolchange.extrusions.begin(), finish_layer_toolchange.extrusions.end());
|
||||
last_toolchange.end_pos = finish_layer_toolchange.end_pos;
|
||||
last_toolchange.wipe_path = finish_layer_toolchange.wipe_path;
|
||||
}
|
||||
if (idx == -1) {
|
||||
// if there is no toolchange switching to non-soluble, finish layer
|
||||
// will be called at the very beginning. That's the last possibility
|
||||
// where a nonsoluble tool can be.
|
||||
finish_layer_tcr = finish_layer();
|
||||
}
|
||||
|
||||
for (int i=0; i<int(layer.tool_changes.size()); ++i) {
|
||||
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool));
|
||||
if (i == idx) // finish_layer will be called after this toolchange
|
||||
finish_layer_tcr = finish_layer();
|
||||
}
|
||||
|
||||
if (layer_result.empty()) {
|
||||
// there is nothing to merge finish_layer with
|
||||
layer_result.emplace_back(std::move(finish_layer_tcr));
|
||||
}
|
||||
else {
|
||||
if (idx == -1)
|
||||
layer_result[0] = merge_tcr(finish_layer_tcr, layer_result[0]);
|
||||
else
|
||||
layer_result.emplace_back(std::move(finish_layer_toolchange));
|
||||
layer_result[idx] = merge_tcr(layer_result[idx], finish_layer_tcr);
|
||||
}
|
||||
|
||||
result.emplace_back(std::move(layer_result));
|
||||
m_is_first_layer = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WipeTower::make_wipe_tower_square()
|
||||
{
|
||||
const float width = m_wipe_tower_width - 3 * m_perimeter_width;
|
||||
const float depth = m_wipe_tower_depth - m_perimeter_width;
|
||||
// area that we actually print into is width*depth
|
||||
float side = sqrt(depth * width);
|
||||
|
||||
m_wipe_tower_width = side + 3 * m_perimeter_width;
|
||||
m_wipe_tower_depth = side + 2 * m_perimeter_width;
|
||||
// For all layers, find how depth changed and update all toolchange depths
|
||||
for (auto &lay : m_plan)
|
||||
{
|
||||
side = sqrt(lay.depth * width);
|
||||
float width_ratio = width / side;
|
||||
|
||||
//lay.extra_spacing = width_ratio;
|
||||
for (auto &tch : lay.tool_changes)
|
||||
tch.required_depth *= width_ratio;
|
||||
}
|
||||
|
||||
plan_tower(); // propagates depth downwards again (width has changed)
|
||||
for (auto& lay : m_plan) // depths set, now the spacing
|
||||
lay.extra_spacing = lay.depth / lay.toolchanges_depth();
|
||||
}
|
||||
|
||||
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -84,6 +84,37 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
struct box_coordinates
|
||||
{
|
||||
box_coordinates(float left, float bottom, float width, float height) :
|
||||
ld(left , bottom ),
|
||||
lu(left , bottom + height),
|
||||
rd(left + width, bottom ),
|
||||
ru(left + width, bottom + height) {}
|
||||
box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
|
||||
void translate(const Vec2f &shift) {
|
||||
ld += shift; lu += shift;
|
||||
rd += shift; ru += shift;
|
||||
}
|
||||
void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
|
||||
void expand(const float offset) {
|
||||
ld += Vec2f(- offset, - offset);
|
||||
lu += Vec2f(- offset, offset);
|
||||
rd += Vec2f( offset, - offset);
|
||||
ru += Vec2f( offset, offset);
|
||||
}
|
||||
void expand(const float offset_x, const float offset_y) {
|
||||
ld += Vec2f(- offset_x, - offset_y);
|
||||
lu += Vec2f(- offset_x, offset_y);
|
||||
rd += Vec2f( offset_x, - offset_y);
|
||||
ru += Vec2f( offset_x, offset_y);
|
||||
}
|
||||
Vec2f ld; // left down
|
||||
Vec2f lu; // left upper
|
||||
Vec2f rd; // right lower
|
||||
Vec2f ru; // right upper
|
||||
};
|
||||
|
||||
// Construct ToolChangeResult from current state of WipeTower and WipeTowerWriter.
|
||||
// WipeTowerWriter is moved from !
|
||||
ToolChangeResult construct_tcr(WipeTowerWriter& writer,
|
||||
|
@ -102,7 +133,7 @@ public:
|
|||
|
||||
// Appends into internal structure m_plan containing info about the future wipe tower
|
||||
// to be used before building begins. The entries must be added ordered in z.
|
||||
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f);
|
||||
void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume = 0.f);
|
||||
|
||||
// Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result"
|
||||
void generate(std::vector<std::vector<ToolChangeResult>> &result);
|
||||
|
@ -129,9 +160,8 @@ public:
|
|||
{
|
||||
m_z_pos = print_z;
|
||||
m_layer_height = layer_height;
|
||||
m_is_first_layer = is_first_layer;
|
||||
m_print_brim = is_first_layer;
|
||||
m_depth_traversed = 0.f;
|
||||
m_current_layer_finished = false;
|
||||
m_current_shape = (! is_first_layer && m_current_shape == SHAPE_NORMAL) ? SHAPE_REVERSED : SHAPE_NORMAL;
|
||||
if (is_first_layer) {
|
||||
this->m_num_layer_changes = 0;
|
||||
|
@ -175,7 +205,7 @@ public:
|
|||
|
||||
// Is the current layer finished?
|
||||
bool layer_finished() const {
|
||||
return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed);
|
||||
return m_current_layer_finished;
|
||||
}
|
||||
|
||||
std::vector<float> get_used_filament() const { return m_used_filament_length; }
|
||||
|
@ -183,6 +213,7 @@ public:
|
|||
|
||||
struct FilamentParameters {
|
||||
std::string material = "PLA";
|
||||
bool is_soluble = false;
|
||||
int temperature = 0;
|
||||
int first_layer_temperature = 0;
|
||||
float loading_speed = 0.f;
|
||||
|
@ -208,7 +239,6 @@ private:
|
|||
SHAPE_REVERSED = -1
|
||||
};
|
||||
|
||||
const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet
|
||||
const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust
|
||||
const float WT_EPSILON = 1e-3f;
|
||||
float filament_area() const {
|
||||
|
@ -228,8 +258,10 @@ private:
|
|||
float m_z_pos = 0.f; // Current Z position.
|
||||
float m_layer_height = 0.f; // Current layer height.
|
||||
size_t m_max_color_changes = 0; // Maximum number of color changes per layer.
|
||||
bool m_is_first_layer = false;// Is this the 1st layer of the print? If so, print the brim around the waste tower.
|
||||
int m_old_temperature = -1; // To keep track of what was the last temp that we set (so we don't issue the command when not neccessary)
|
||||
float m_travel_speed = 0.f;
|
||||
float m_first_layer_speed = 0.f;
|
||||
size_t m_first_layer_idx = size_t(-1);
|
||||
|
||||
// G-code generator parameters.
|
||||
float m_cooling_tube_retraction = 0.f;
|
||||
|
@ -245,7 +277,8 @@ private:
|
|||
// Bed properties
|
||||
enum {
|
||||
RectangularBed,
|
||||
CircularBed
|
||||
CircularBed,
|
||||
CustomBed
|
||||
} m_bed_shape;
|
||||
float m_bed_width; // width of the bed bounding box
|
||||
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)
|
||||
|
@ -268,9 +301,12 @@ private:
|
|||
const std::vector<std::vector<float>> wipe_volumes;
|
||||
|
||||
float m_depth_traversed = 0.f; // Current y position at the wipe tower.
|
||||
bool m_current_layer_finished = false;
|
||||
bool m_left_to_right = true;
|
||||
float m_extra_spacing = 1.f;
|
||||
|
||||
bool is_first_layer() const { return size_t(m_layer_info - m_plan.begin()) == m_first_layer_idx; }
|
||||
|
||||
// Calculates extrusion flow needed to produce required line width for given layer height
|
||||
float extrusion_flow(float layer_height = -1.f) const // negative layer_height - return current m_extrusion_flow
|
||||
{
|
||||
|
@ -294,39 +330,7 @@ private:
|
|||
void save_on_last_wipe();
|
||||
|
||||
|
||||
struct box_coordinates
|
||||
{
|
||||
box_coordinates(float left, float bottom, float width, float height) :
|
||||
ld(left , bottom ),
|
||||
lu(left , bottom + height),
|
||||
rd(left + width, bottom ),
|
||||
ru(left + width, bottom + height) {}
|
||||
box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {}
|
||||
void translate(const Vec2f &shift) {
|
||||
ld += shift; lu += shift;
|
||||
rd += shift; ru += shift;
|
||||
}
|
||||
void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); }
|
||||
void expand(const float offset) {
|
||||
ld += Vec2f(- offset, - offset);
|
||||
lu += Vec2f(- offset, offset);
|
||||
rd += Vec2f( offset, - offset);
|
||||
ru += Vec2f( offset, offset);
|
||||
}
|
||||
void expand(const float offset_x, const float offset_y) {
|
||||
ld += Vec2f(- offset_x, - offset_y);
|
||||
lu += Vec2f(- offset_x, offset_y);
|
||||
rd += Vec2f( offset_x, - offset_y);
|
||||
ru += Vec2f( offset_x, offset_y);
|
||||
}
|
||||
Vec2f ld; // left down
|
||||
Vec2f lu; // left upper
|
||||
Vec2f rd; // right lower
|
||||
Vec2f ru; // right upper
|
||||
};
|
||||
|
||||
|
||||
// to store information about tool changes for a given layer
|
||||
// to store information about tool changes for a given layer
|
||||
struct WipeTowerInfo{
|
||||
struct ToolChange {
|
||||
size_t old_tool;
|
||||
|
@ -356,11 +360,10 @@ private:
|
|||
// Stores information about used filament length per extruder:
|
||||
std::vector<float> m_used_filament_length;
|
||||
|
||||
|
||||
// Returns gcode for wipe tower brim
|
||||
// sideOnly -- set to false -- experimental, draw brim on sides of wipe tower
|
||||
// offset -- set to 0 -- experimental, offset to replace brim in front / rear of wipe tower
|
||||
ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f);
|
||||
// Return index of first toolchange that switches to non-soluble extruder
|
||||
// ot -1 if there is no such toolchange.
|
||||
int first_toolchange_to_nonsoluble(
|
||||
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const;
|
||||
|
||||
void toolchange_Unload(
|
||||
WipeTowerWriter &writer,
|
||||
|
@ -386,6 +389,6 @@ private:
|
|||
|
||||
|
||||
|
||||
}; // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // WipeTowerPrusaMM_hpp_
|
||||
|
|
|
@ -13,13 +13,13 @@ namespace Slic3r {
|
|||
void GCodeReader::apply_config(const GCodeConfig &config)
|
||||
{
|
||||
m_config = config;
|
||||
m_extrusion_axis = m_config.get_extrusion_axis()[0];
|
||||
m_extrusion_axis = get_extrusion_axis(m_config)[0];
|
||||
}
|
||||
|
||||
void GCodeReader::apply_config(const DynamicPrintConfig &config)
|
||||
{
|
||||
m_config.apply(config, true);
|
||||
m_extrusion_axis = m_config.get_extrusion_axis()[0];
|
||||
m_extrusion_axis = get_extrusion_axis(m_config)[0];
|
||||
}
|
||||
|
||||
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)
|
||||
|
|
|
@ -18,9 +18,10 @@ namespace Slic3r {
|
|||
void GCodeWriter::apply_print_config(const PrintConfig &print_config)
|
||||
{
|
||||
this->config.apply(print_config, true);
|
||||
m_extrusion_axis = this->config.get_extrusion_axis();
|
||||
m_extrusion_axis = get_extrusion_axis(this->config);
|
||||
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
|
||||
m_max_acceleration = std::lrint((print_config.gcode_flavor.value == gcfMarlin && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ?
|
||||
bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware;
|
||||
m_max_acceleration = std::lrint((is_marlin && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ?
|
||||
print_config.machine_max_acceleration_extruding.values.front() : 0);
|
||||
}
|
||||
|
||||
|
@ -48,7 +49,8 @@ std::string GCodeWriter::preamble()
|
|||
}
|
||||
if (FLAVOR_IS(gcfRepRapSprinter) ||
|
||||
FLAVOR_IS(gcfRepRapFirmware) ||
|
||||
FLAVOR_IS(gcfMarlin) ||
|
||||
FLAVOR_IS(gcfMarlinLegacy) ||
|
||||
FLAVOR_IS(gcfMarlinFirmware) ||
|
||||
FLAVOR_IS(gcfTeacup) ||
|
||||
FLAVOR_IS(gcfRepetier) ||
|
||||
FLAVOR_IS(gcfSmoothie))
|
||||
|
@ -205,8 +207,12 @@ std::string GCodeWriter::set_acceleration(unsigned int acceleration)
|
|||
// M202: Set max travel acceleration
|
||||
gcode << "M202 X" << acceleration << " Y" << acceleration;
|
||||
} else if (FLAVOR_IS(gcfRepRapFirmware)) {
|
||||
// M204: Set default acceleration
|
||||
gcode << "M204 P" << acceleration;
|
||||
// M204: Set default acceleration
|
||||
gcode << "M204 P" << acceleration;
|
||||
} else if (FLAVOR_IS(gcfMarlinFirmware)) {
|
||||
// This is new MarlinFirmware with separated print/retraction/travel acceleration.
|
||||
// Use M204 P, we don't want to override travel acc by M204 S (which is deprecated anyway).
|
||||
gcode << "M204 P" << acceleration;
|
||||
} else {
|
||||
// M204: Set default acceleration
|
||||
gcode << "M204 S" << acceleration;
|
||||
|
|
|
@ -1087,11 +1087,13 @@ bool
|
|||
MedialAxis::validate_edge(const VD::edge_type* edge)
|
||||
{
|
||||
// prevent overflows and detect almost-infinite edges
|
||||
#ifndef CLIPPERLIB_INT32
|
||||
if (std::abs(edge->vertex0()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
|
||||
std::abs(edge->vertex0()->y()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
|
||||
std::abs(edge->vertex1()->x()) > double(CLIPPER_MAX_COORD_UNSCALED) ||
|
||||
std::abs(edge->vertex1()->y()) > double(CLIPPER_MAX_COORD_UNSCALED))
|
||||
return false;
|
||||
#endif // CLIPPERLIB_INT32
|
||||
|
||||
// construct the line representing this edge of the Voronoi diagram
|
||||
const Line line(
|
||||
|
|
|
@ -22,12 +22,14 @@
|
|||
#pragma warning(pop)
|
||||
#endif // _MSC_VER
|
||||
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*>;
|
||||
}
|
||||
namespace Slic3r {
|
||||
|
||||
namespace Slic3r { namespace Geometry {
|
||||
namespace ClipperLib {
|
||||
class PolyNode;
|
||||
using PolyNodes = std::vector<PolyNode*>;
|
||||
}
|
||||
|
||||
namespace Geometry {
|
||||
|
||||
// Generic result of an orientation predicate.
|
||||
enum Orientation
|
||||
|
@ -299,20 +301,23 @@ bool liang_barsky_line_clipping(
|
|||
|
||||
// Ugly named variant, that accepts the squared line
|
||||
// Don't call me with a nearly zero length vector!
|
||||
// sympy:
|
||||
// factor(solve([a * x + b * y + c, x**2 + y**2 - r**2], [x, y])[0])
|
||||
// factor(solve([a * x + b * y + c, x**2 + y**2 - r**2], [x, y])[1])
|
||||
template<typename T>
|
||||
int ray_circle_intersections_r2_lv2_c(T r2, T a, T b, T lv2, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
|
||||
{
|
||||
T x0 = - a * c / lv2;
|
||||
T y0 = - b * c / lv2;
|
||||
T d = r2 - c * c / lv2;
|
||||
if (d < T(0))
|
||||
T x0 = - a * c;
|
||||
T y0 = - b * c;
|
||||
T d2 = r2 * lv2 - c * c;
|
||||
if (d2 < T(0))
|
||||
return 0;
|
||||
T mult = sqrt(d / lv2);
|
||||
out.first.x() = x0 + b * mult;
|
||||
out.first.y() = y0 - a * mult;
|
||||
out.second.x() = x0 - b * mult;
|
||||
out.second.y() = y0 + a * mult;
|
||||
return mult == T(0) ? 1 : 2;
|
||||
T d = sqrt(d2);
|
||||
out.first.x() = (x0 + b * d) / lv2;
|
||||
out.first.y() = (y0 - a * d) / lv2;
|
||||
out.second.x() = (x0 - b * d) / lv2;
|
||||
out.second.y() = (y0 + a * d) / lv2;
|
||||
return d == T(0) ? 1 : 2;
|
||||
}
|
||||
template<typename T>
|
||||
int ray_circle_intersections(T r, T a, T b, T c, std::pair<Eigen::Matrix<T, 2, 1, Eigen::DontAlign>, Eigen::Matrix<T, 2, 1, Eigen::DontAlign>> &out)
|
||||
|
@ -527,6 +532,6 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
|
|||
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
|
||||
}
|
||||
|
||||
} }
|
||||
} } // namespace Slicer::Geometry
|
||||
|
||||
#endif
|
||||
|
|
|
@ -39,11 +39,11 @@ void Layer::make_slices()
|
|||
ExPolygons slices;
|
||||
if (m_regions.size() == 1) {
|
||||
// optimization: if we only have one region, take its slices
|
||||
slices = m_regions.front()->slices;
|
||||
slices = to_expolygons(m_regions.front()->slices.surfaces);
|
||||
} else {
|
||||
Polygons slices_p;
|
||||
for (LayerRegion *layerm : m_regions)
|
||||
polygons_append(slices_p, to_polygons(layerm->slices));
|
||||
polygons_append(slices_p, to_polygons(layerm->slices.surfaces));
|
||||
slices = union_ex(slices_p);
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ ExPolygons Layer::merged(float offset_scaled) const
|
|||
const PrintRegionConfig &config = layerm->region()->config();
|
||||
// Our users learned to bend Slic3r to produce empty volumes to act as subtracters. Only add the region if it is non-empty.
|
||||
if (config.bottom_solid_layers > 0 || config.top_solid_layers > 0 || config.fill_density > 0. || config.perimeters > 0)
|
||||
append(polygons, offset(to_expolygons(layerm->slices.surfaces), offset_scaled));
|
||||
append(polygons, offset(layerm->slices.surfaces, offset_scaled));
|
||||
}
|
||||
ExPolygons out = union_ex(polygons);
|
||||
if (offset_scaled2 != 0.f)
|
||||
|
@ -185,7 +185,7 @@ void Layer::make_perimeters()
|
|||
}
|
||||
// merge the surfaces assigned to each group
|
||||
for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices)
|
||||
new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front());
|
||||
new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front());
|
||||
}
|
||||
|
||||
// make perimeters
|
||||
|
@ -196,7 +196,7 @@ void Layer::make_perimeters()
|
|||
if (!fill_surfaces.surfaces.empty()) {
|
||||
for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) {
|
||||
// Separate the fill surfaces.
|
||||
ExPolygons expp = intersection_ex(to_polygons(fill_surfaces), (*l)->slices);
|
||||
ExPolygons expp = intersection_ex(fill_surfaces.surfaces, (*l)->slices.surfaces);
|
||||
(*l)->fill_expolygons = expp;
|
||||
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
|
||||
}
|
||||
|
|
|
@ -196,7 +196,7 @@ protected:
|
|||
// between the raft and the object first layer.
|
||||
SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, coordf_t slice_z) :
|
||||
Layer(id, object, height, print_z, slice_z) {}
|
||||
virtual ~SupportLayer() {}
|
||||
virtual ~SupportLayer() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -49,19 +49,17 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
|
|||
// in place. However we're now only using its boundaries (which are invariant)
|
||||
// so we're safe. This guarantees idempotence of prepare_infill() also in case
|
||||
// that combine_infill() turns some fill_surface into VOID surfaces.
|
||||
// Polygons fill_boundaries = to_polygons(std::move(this->fill_surfaces));
|
||||
Polygons fill_boundaries = to_polygons(this->fill_expolygons);
|
||||
// Collect polygons per surface type.
|
||||
std::vector<Polygons> polygons_by_surface;
|
||||
polygons_by_surface.assign(size_t(stCount), Polygons());
|
||||
std::vector<SurfacesPtr> by_surface;
|
||||
by_surface.assign(size_t(stCount), SurfacesPtr());
|
||||
for (Surface &surface : this->slices.surfaces)
|
||||
polygons_append(polygons_by_surface[(size_t)surface.surface_type], surface.expolygon);
|
||||
by_surface[size_t(surface.surface_type)].emplace_back(&surface);
|
||||
// Trim surfaces by the fill_boundaries.
|
||||
this->fill_surfaces.surfaces.clear();
|
||||
for (size_t surface_type = 0; surface_type < size_t(stCount); ++ surface_type) {
|
||||
const Polygons &polygons = polygons_by_surface[surface_type];
|
||||
if (! polygons.empty())
|
||||
this->fill_surfaces.append(intersection_ex(polygons, fill_boundaries), SurfaceType(surface_type));
|
||||
const SurfacesPtr &this_surfaces = by_surface[surface_type];
|
||||
if (! this_surfaces.empty())
|
||||
this->fill_surfaces.append(intersection_ex(this_surfaces, this->fill_expolygons), SurfaceType(surface_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,12 +214,12 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
break;
|
||||
}
|
||||
// Grown by 3mm.
|
||||
Polygons polys = offset(to_polygons(bridges[i].expolygon), margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
|
||||
Polygons polys = offset(bridges[i].expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS);
|
||||
if (idx_island == -1) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!";
|
||||
} else {
|
||||
// Found an island, to which this bridge region belongs. Trim it,
|
||||
polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island]));
|
||||
polys = intersection(polys, fill_boundaries_ex[idx_island]);
|
||||
}
|
||||
bridge_bboxes.push_back(get_extents(polys));
|
||||
bridges_grown.push_back(std::move(polys));
|
||||
|
@ -325,11 +323,11 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
if (s1.empty())
|
||||
continue;
|
||||
Polygons polys;
|
||||
polygons_append(polys, std::move(s1));
|
||||
polygons_append(polys, to_polygons(std::move(s1)));
|
||||
for (size_t j = i + 1; j < top.size(); ++ j) {
|
||||
Surface &s2 = top[j];
|
||||
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
|
||||
polygons_append(polys, std::move(s2));
|
||||
polygons_append(polys, to_polygons(std::move(s2)));
|
||||
s2.clear();
|
||||
}
|
||||
}
|
||||
|
@ -351,11 +349,11 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
|||
if (s1.empty())
|
||||
continue;
|
||||
Polygons polys;
|
||||
polygons_append(polys, std::move(s1));
|
||||
polygons_append(polys, to_polygons(std::move(s1)));
|
||||
for (size_t j = i + 1; j < internal.size(); ++ j) {
|
||||
Surface &s2 = internal[j];
|
||||
if (! s2.empty() && surfaces_could_merge(s1, s2)) {
|
||||
polygons_append(polys, std::move(s2));
|
||||
polygons_append(polys, to_polygons(std::move(s2)));
|
||||
s2.clear();
|
||||
}
|
||||
}
|
||||
|
@ -423,7 +421,7 @@ void LayerRegion::trim_surfaces(const Polygons &trimming_polygons)
|
|||
for (const Surface &surface : this->slices.surfaces)
|
||||
assert(surface.surface_type == stInternal);
|
||||
#endif /* NDEBUG */
|
||||
this->slices.set(intersection_ex(to_polygons(std::move(this->slices.surfaces)), trimming_polygons, false), stInternal);
|
||||
this->slices.set(intersection_ex(this->slices.surfaces, trimming_polygons), stInternal);
|
||||
}
|
||||
|
||||
void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_compensation_perimeter_step, const Polygons &trimming_polygons)
|
||||
|
@ -432,10 +430,9 @@ void LayerRegion::elephant_foot_compensation_step(const float elephant_foot_comp
|
|||
for (const Surface &surface : this->slices.surfaces)
|
||||
assert(surface.surface_type == stInternal);
|
||||
#endif /* NDEBUG */
|
||||
ExPolygons slices_expolygons = to_expolygons(std::move(this->slices.surfaces));
|
||||
Polygons slices_polygons = to_polygons(slices_expolygons);
|
||||
Polygons tmp = intersection(slices_polygons, trimming_polygons, false);
|
||||
append(tmp, diff(slices_polygons, offset(offset_ex(slices_expolygons, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
|
||||
ExPolygons surfaces = to_expolygons(std::move(this->slices.surfaces));
|
||||
Polygons tmp = intersection(surfaces, trimming_polygons);
|
||||
append(tmp, diff(surfaces, offset(offset_ex(surfaces, -elephant_foot_compensation_perimeter_step), elephant_foot_compensation_perimeter_step)));
|
||||
this->slices.set(union_ex(tmp), stInternal);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class BoundingBox;
|
||||
|
@ -20,12 +22,28 @@ Linef3 transform(const Linef3& line, const Transform3d& t);
|
|||
|
||||
namespace line_alg {
|
||||
|
||||
template<class L, class En = void> struct Traits {
|
||||
static constexpr int Dim = L::Dim;
|
||||
using Scalar = typename L::Scalar;
|
||||
|
||||
static Vec<Dim, Scalar>& get_a(L &l) { return l.a; }
|
||||
static Vec<Dim, Scalar>& get_b(L &l) { return l.b; }
|
||||
static const Vec<Dim, Scalar>& get_a(const L &l) { return l.a; }
|
||||
static const Vec<Dim, Scalar>& get_b(const L &l) { return l.b; }
|
||||
};
|
||||
|
||||
template<class L> const constexpr int Dim = Traits<remove_cvref_t<L>>::Dim;
|
||||
template<class L> using Scalar = typename Traits<remove_cvref_t<L>>::Scalar;
|
||||
|
||||
template<class L> auto get_a(L &&l) { return Traits<remove_cvref_t<L>>::get_a(l); }
|
||||
template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l); }
|
||||
|
||||
// Distance to the closest point of line.
|
||||
template<class L, class T, int N>
|
||||
double distance_to_squared(const L &line, const Vec<N, T> &point)
|
||||
template<class L>
|
||||
double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
|
||||
{
|
||||
const Vec<N, double> v = (line.b - line.a).template cast<double>();
|
||||
const Vec<N, double> va = (point - line.a).template cast<double>();
|
||||
const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
|
||||
const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
|
||||
const double l2 = v.squaredNorm(); // avoid a sqrt
|
||||
if (l2 == 0.0)
|
||||
// a == b case
|
||||
|
@ -35,12 +53,12 @@ double distance_to_squared(const L &line, const Vec<N, T> &point)
|
|||
// It falls where t = [(this-a) . (b-a)] / |b-a|^2
|
||||
const double t = va.dot(v) / l2;
|
||||
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment
|
||||
else if (t > 1.0) return (point - line.b).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment
|
||||
else if (t > 1.0) return (point - get_b(line)).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment
|
||||
return (t * v - va).squaredNorm();
|
||||
}
|
||||
|
||||
template<class L, class T, int N>
|
||||
double distance_to(const L &line, const Vec<N, T> &point)
|
||||
template<class L>
|
||||
double distance_to(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
|
||||
{
|
||||
return std::sqrt(distance_to_squared(line, point));
|
||||
}
|
||||
|
@ -84,6 +102,9 @@ public:
|
|||
|
||||
Point a;
|
||||
Point b;
|
||||
|
||||
static const constexpr int Dim = 2;
|
||||
using Scalar = Point::Scalar;
|
||||
};
|
||||
|
||||
class ThickLine : public Line
|
||||
|
@ -107,6 +128,9 @@ public:
|
|||
|
||||
Vec3crd a;
|
||||
Vec3crd b;
|
||||
|
||||
static const constexpr int Dim = 3;
|
||||
using Scalar = Vec3crd::Scalar;
|
||||
};
|
||||
|
||||
class Linef
|
||||
|
@ -117,6 +141,9 @@ public:
|
|||
|
||||
Vec2d a;
|
||||
Vec2d b;
|
||||
|
||||
static const constexpr int Dim = 2;
|
||||
using Scalar = Vec2d::Scalar;
|
||||
};
|
||||
|
||||
class Linef3
|
||||
|
@ -133,6 +160,9 @@ public:
|
|||
|
||||
Vec3d a;
|
||||
Vec3d b;
|
||||
|
||||
static const constexpr int Dim = 3;
|
||||
using Scalar = Vec3d::Scalar;
|
||||
};
|
||||
|
||||
BoundingBox get_extents(const Lines &lines);
|
||||
|
|
|
@ -106,13 +106,8 @@ template<class C> bool all_of(const C &container)
|
|||
});
|
||||
}
|
||||
|
||||
template<class T> struct remove_cvref
|
||||
{
|
||||
using type =
|
||||
typename std::remove_cv<typename std::remove_reference<T>::type>::type;
|
||||
};
|
||||
|
||||
template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
|
||||
//template<class T>
|
||||
//using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>;
|
||||
|
||||
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
|
||||
template<class T, class I, class = IntegerOnly<I>>
|
||||
|
|
|
@ -14,55 +14,9 @@
|
|||
#include <boost/multiprecision/integer.hpp>
|
||||
#endif
|
||||
|
||||
#include <libnest2d/geometry_traits.hpp>
|
||||
#include <libnest2d/backends/libslic3r/geometries.hpp>
|
||||
#include <libnest2d/utils/rotcalipers.hpp>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
template<> struct PointType<Slic3r::Points> { using Type = Slic3r::Point; };
|
||||
template<> struct CoordType<Slic3r::Point> { using Type = coord_t; };
|
||||
template<> struct ShapeTag<Slic3r::ExPolygon> { using Type = PolygonTag; };
|
||||
template<> struct ShapeTag<Slic3r::Polygon> { using Type = PolygonTag; };
|
||||
template<> struct ShapeTag<Slic3r::Points> { using Type = PathTag; };
|
||||
template<> struct ShapeTag<Slic3r::Point> { using Type = PointTag; };
|
||||
template<> struct ContourType<Slic3r::ExPolygon> { using Type = Slic3r::Points; };
|
||||
template<> struct ContourType<Slic3r::Polygon> { using Type = Slic3r::Points; };
|
||||
|
||||
namespace pointlike {
|
||||
|
||||
template<> inline coord_t x(const Slic3r::Point& p) { return p.x(); }
|
||||
template<> inline coord_t y(const Slic3r::Point& p) { return p.y(); }
|
||||
template<> inline coord_t& x(Slic3r::Point& p) { return p.x(); }
|
||||
template<> inline coord_t& y(Slic3r::Point& p) { return p.y(); }
|
||||
|
||||
} // pointlike
|
||||
|
||||
namespace shapelike {
|
||||
template<> inline Slic3r::Points& contour(Slic3r::ExPolygon& sh) { return sh.contour.points; }
|
||||
template<> inline const Slic3r::Points& contour(const Slic3r::ExPolygon& sh) { return sh.contour.points; }
|
||||
template<> inline Slic3r::Points& contour(Slic3r::Polygon& sh) { return sh.points; }
|
||||
template<> inline const Slic3r::Points& contour(const Slic3r::Polygon& sh) { return sh.points; }
|
||||
|
||||
template<> Slic3r::Points::iterator begin(Slic3r::Points& pts, const PathTag&) { return pts.begin();}
|
||||
template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.cbegin(); }
|
||||
template<> Slic3r::Points::iterator end(Slic3r::Points& pts, const PathTag&) { return pts.end();}
|
||||
template<> Slic3r::Points::const_iterator cend(const Slic3r::Points& pts, const PathTag&) { return pts.cend(); }
|
||||
|
||||
template<> inline Slic3r::ExPolygon create<Slic3r::ExPolygon>(Slic3r::Points&& contour)
|
||||
{
|
||||
Slic3r::ExPolygon expoly; expoly.contour.points.swap(contour);
|
||||
return expoly;
|
||||
}
|
||||
|
||||
template<> inline Slic3r::Polygon create<Slic3r::Polygon>(Slic3r::Points&& contour)
|
||||
{
|
||||
Slic3r::Polygon poly; poly.points.swap(contour);
|
||||
return poly;
|
||||
}
|
||||
|
||||
} // shapelike
|
||||
} // libnest2d
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Used as compute type.
|
||||
|
@ -74,13 +28,22 @@ using Rational = boost::rational<boost::multiprecision::int128_t>;
|
|||
using Rational = boost::rational<__int128>;
|
||||
#endif
|
||||
|
||||
template<class P>
|
||||
libnest2d::RotatedBox<Point, Unit> minAreaBoundigBox_(
|
||||
const P &p, MinAreaBoundigBox::PolygonLevel lvl)
|
||||
{
|
||||
P chull = lvl == MinAreaBoundigBox::pcConvex ?
|
||||
p :
|
||||
libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::removeCollinearPoints(chull);
|
||||
|
||||
return libnest2d::minAreaBoundingBox<P, Unit, Rational>(chull);
|
||||
}
|
||||
|
||||
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
|
||||
{
|
||||
const Polygon &chull = pc == pcConvex ? p :
|
||||
libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<Polygon, Unit, Rational>(chull);
|
||||
libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
|
||||
|
||||
m_right = libnest2d::cast<long double>(box.right_extent());
|
||||
m_bottom = libnest2d::cast<long double>(box.bottom_extent());
|
||||
|
@ -89,11 +52,7 @@ MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
|
|||
|
||||
MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
|
||||
{
|
||||
const ExPolygon &chull = pc == pcConvex ? p :
|
||||
libnest2d::sl::convexHull(p);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<ExPolygon, Unit, Rational>(chull);
|
||||
libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
|
||||
|
||||
m_right = libnest2d::cast<long double>(box.right_extent());
|
||||
m_bottom = libnest2d::cast<long double>(box.bottom_extent());
|
||||
|
@ -102,11 +61,7 @@ MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
|
|||
|
||||
MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc)
|
||||
{
|
||||
const Points &chull = pc == pcConvex ? pts :
|
||||
libnest2d::sl::convexHull(pts);
|
||||
|
||||
libnest2d::RotatedBox<Point, Unit> box =
|
||||
libnest2d::minAreaBoundingBox<Points, Unit, Rational>(chull);
|
||||
libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(pts, pc);
|
||||
|
||||
m_right = libnest2d::cast<long double>(box.right_extent());
|
||||
m_bottom = libnest2d::cast<long double>(box.bottom_extent());
|
||||
|
|
|
@ -26,12 +26,8 @@ public:
|
|||
};
|
||||
|
||||
// Constructors with various types of geometry data used in Slic3r.
|
||||
// If the convexity is known apriory, pcConvex can be used to skip
|
||||
// convex hull calculation. It is very important that the input polygons
|
||||
// do NOT have any collinear points (except for the first and the last
|
||||
// vertex being the same -- meaning a closed polygon for boost)
|
||||
// To make sure this constraint is satisfied, you can call
|
||||
// remove_collinear_points on the input polygon before handing over here)
|
||||
// If the convexity is known apriory, pcConvex can be used to skip
|
||||
// convex hull calculation.
|
||||
explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple);
|
||||
explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple);
|
||||
explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple);
|
||||
|
|
|
@ -833,18 +833,6 @@ indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
|
|||
return out;
|
||||
}
|
||||
|
||||
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
|
||||
TriangleMesh ModelObject::full_raw_mesh() const
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
for (const ModelVolume *v : this->volumes)
|
||||
{
|
||||
TriangleMesh vol_mesh(v->mesh());
|
||||
vol_mesh.transform(v->get_matrix());
|
||||
mesh.merge(vol_mesh);
|
||||
}
|
||||
return mesh;
|
||||
}
|
||||
|
||||
const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
|
||||
{
|
||||
|
|
|
@ -289,8 +289,6 @@ public:
|
|||
TriangleMesh raw_mesh() const;
|
||||
// The same as above, but producing a lightweight indexed_triangle_set.
|
||||
indexed_triangle_set raw_indexed_triangle_set() const;
|
||||
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
|
||||
TriangleMesh full_raw_mesh() const;
|
||||
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
|
||||
// This bounding box is only used for the actual slicing.
|
||||
const BoundingBoxf3& raw_bounding_box() const;
|
||||
|
|
|
@ -64,6 +64,7 @@ public:
|
|||
bool has_duplicate_points() const;
|
||||
// Remove exact duplicates, return true if any duplicate has been removed.
|
||||
bool remove_duplicate_points();
|
||||
void clear() { this->points.clear(); }
|
||||
void append(const Point &point) { this->points.push_back(point); }
|
||||
void append(const Points &src) { this->append(src.begin(), src.end()); }
|
||||
void append(const Points::const_iterator &begin, const Points::const_iterator &end) { this->points.insert(this->points.end(), begin, end); }
|
||||
|
@ -83,6 +84,13 @@ public:
|
|||
|
||||
static Points _douglas_peucker(const Points &points, const double tolerance);
|
||||
static Points visivalingam(const Points& pts, const double& tolerance);
|
||||
|
||||
inline auto begin() { return points.begin(); }
|
||||
inline auto begin() const { return points.begin(); }
|
||||
inline auto end() { return points.end(); }
|
||||
inline auto end() const { return points.end(); }
|
||||
inline auto cbegin() const { return points.begin(); }
|
||||
inline auto cend() const { return points.end(); }
|
||||
};
|
||||
|
||||
class MultiPoint3
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <functional>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r { namespace opt {
|
||||
|
||||
|
|
|
@ -349,9 +349,7 @@ void PerimeterGenerator::process()
|
|||
coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter() / 3));
|
||||
ExPolygons expp = offset2_ex(
|
||||
// medial axis requires non-overlapping geometry
|
||||
diff_ex(to_polygons(last),
|
||||
offset(offsets, float(ext_perimeter_width / 2.)),
|
||||
true),
|
||||
diff_ex(last, offset(offsets, float(ext_perimeter_width / 2.)), true),
|
||||
- float(min_width / 2.), float(min_width / 2.));
|
||||
// the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop
|
||||
for (ExPolygon &ex : expp)
|
||||
|
@ -514,7 +512,7 @@ void PerimeterGenerator::process()
|
|||
and use zigzag). */
|
||||
//FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing,
|
||||
// therefore it may cover the area, but no the volume.
|
||||
last = diff_ex(to_polygons(last), gap_fill.polygons_covered_by_width(10.f));
|
||||
last = diff_ex(last, gap_fill.polygons_covered_by_width(10.f));
|
||||
this->gap_fill->append(std::move(gap_fill.entities));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,42 +17,42 @@ class BoundingBox;
|
|||
class Line;
|
||||
class MultiPoint;
|
||||
class Point;
|
||||
typedef Point Vector;
|
||||
using Vector = Point;
|
||||
|
||||
// Eigen types, to replace the Slic3r's own types in the future.
|
||||
// Vector types with a fixed point coordinate base type.
|
||||
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd;
|
||||
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd;
|
||||
typedef Eigen::Matrix<int, 2, 1, Eigen::DontAlign> Vec2i;
|
||||
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i;
|
||||
typedef Eigen::Matrix<int32_t, 2, 1, Eigen::DontAlign> Vec2i32;
|
||||
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64;
|
||||
typedef Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign> Vec3i32;
|
||||
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64;
|
||||
using Vec2crd = Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign>;
|
||||
using Vec3crd = Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign>;
|
||||
using Vec2i = Eigen::Matrix<int, 2, 1, Eigen::DontAlign>;
|
||||
using Vec3i = Eigen::Matrix<int, 3, 1, Eigen::DontAlign>;
|
||||
using Vec2i32 = Eigen::Matrix<int32_t, 2, 1, Eigen::DontAlign>;
|
||||
using Vec2i64 = Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign>;
|
||||
using Vec3i32 = Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign>;
|
||||
using Vec3i64 = Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign>;
|
||||
|
||||
// Vector types with a double coordinate base type.
|
||||
typedef Eigen::Matrix<float, 2, 1, Eigen::DontAlign> Vec2f;
|
||||
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f;
|
||||
typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vec2d;
|
||||
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d;
|
||||
using Vec2f = Eigen::Matrix<float, 2, 1, Eigen::DontAlign>;
|
||||
using Vec3f = Eigen::Matrix<float, 3, 1, Eigen::DontAlign>;
|
||||
using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
|
||||
using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
|
||||
|
||||
typedef std::vector<Point> Points;
|
||||
typedef std::vector<Point*> PointPtrs;
|
||||
typedef std::vector<const Point*> PointConstPtrs;
|
||||
typedef std::vector<Vec3crd> Points3;
|
||||
typedef std::vector<Vec2d> Pointfs;
|
||||
typedef std::vector<Vec2d> Vec2ds;
|
||||
typedef std::vector<Vec3d> Pointf3s;
|
||||
using Points = std::vector<Point>;
|
||||
using PointPtrs = std::vector<Point*>;
|
||||
using PointConstPtrs = std::vector<const Point*>;
|
||||
using Points3 = std::vector<Vec3crd>;
|
||||
using Pointfs = std::vector<Vec2d>;
|
||||
using Vec2ds = std::vector<Vec2d>;
|
||||
using Pointf3s = std::vector<Vec3d>;
|
||||
|
||||
typedef Eigen::Matrix<float, 2, 2, Eigen::DontAlign> Matrix2f;
|
||||
typedef Eigen::Matrix<double, 2, 2, Eigen::DontAlign> Matrix2d;
|
||||
typedef Eigen::Matrix<float, 3, 3, Eigen::DontAlign> Matrix3f;
|
||||
typedef Eigen::Matrix<double, 3, 3, Eigen::DontAlign> Matrix3d;
|
||||
using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
|
||||
using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>;
|
||||
using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>;
|
||||
using Matrix3d = Eigen::Matrix<double, 3, 3, Eigen::DontAlign>;
|
||||
|
||||
typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f;
|
||||
typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d;
|
||||
typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f;
|
||||
typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d;
|
||||
using Transform2f = Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
|
||||
using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
|
||||
|
||||
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
|
||||
|
||||
|
@ -101,7 +101,7 @@ template<int N, class T> using Vec = Eigen::Matrix<T, N, 1, Eigen::DontAlign, N
|
|||
class Point : public Vec2crd
|
||||
{
|
||||
public:
|
||||
typedef coord_t coord_type;
|
||||
using coord_type = coord_t;
|
||||
|
||||
Point() : Vec2crd(0, 0) {}
|
||||
Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
|
||||
|
@ -337,7 +337,7 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
typedef typename std::unordered_multimap<Vec2crd, ValueType, PointHash> map_type;
|
||||
using map_type = typename std::unordered_multimap<Vec2crd, ValueType, PointHash>;
|
||||
PointAccessor m_point_accessor;
|
||||
map_type m_map;
|
||||
coord_t m_search_radius;
|
||||
|
@ -413,6 +413,25 @@ unscaled(const Eigen::Matrix<Tin, N, EigenArgs...> &v) noexcept
|
|||
return v.template cast<Tout>() * SCALING_FACTOR;
|
||||
}
|
||||
|
||||
// Align a coordinate to a grid. The coordinate may be negative,
|
||||
// the aligned value will never be bigger than the original one.
|
||||
inline coord_t align_to_grid(const coord_t coord, const coord_t spacing) {
|
||||
// Current C++ standard defines the result of integer division to be rounded to zero,
|
||||
// for both positive and negative numbers. Here we want to round down for negative
|
||||
// numbers as well.
|
||||
coord_t aligned = (coord < 0) ?
|
||||
((coord - spacing + 1) / spacing) * spacing :
|
||||
(coord / spacing) * spacing;
|
||||
assert(aligned <= coord);
|
||||
return aligned;
|
||||
}
|
||||
inline Point align_to_grid(Point coord, Point spacing)
|
||||
{ return Point(align_to_grid(coord.x(), spacing.x()), align_to_grid(coord.y(), spacing.y())); }
|
||||
inline coord_t align_to_grid(coord_t coord, coord_t spacing, coord_t base)
|
||||
{ return base + align_to_grid(coord - base, spacing); }
|
||||
inline Point align_to_grid(Point coord, Point spacing, Point base)
|
||||
{ return Point(align_to_grid(coord.x(), spacing.x(), base.x()), align_to_grid(coord.y(), spacing.y(), base.y())); }
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
// start Boost
|
||||
|
@ -420,11 +439,11 @@ unscaled(const Eigen::Matrix<Tin, N, EigenArgs...> &v) noexcept
|
|||
#include <boost/polygon/polygon.hpp>
|
||||
namespace boost { namespace polygon {
|
||||
template <>
|
||||
struct geometry_concept<Slic3r::Point> { typedef point_concept type; };
|
||||
struct geometry_concept<Slic3r::Point> { using type = point_concept; };
|
||||
|
||||
template <>
|
||||
struct point_traits<Slic3r::Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
using coordinate_type = coord_t;
|
||||
|
||||
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
|
||||
return static_cast<coordinate_type>(point((orient == HORIZONTAL) ? 0 : 1));
|
||||
|
@ -433,7 +452,7 @@ namespace boost { namespace polygon {
|
|||
|
||||
template <>
|
||||
struct point_mutable_traits<Slic3r::Point> {
|
||||
typedef coord_t coordinate_type;
|
||||
using coordinate_type = coord_t;
|
||||
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
|
||||
point((orient == HORIZONTAL) ? 0 : 1) = value;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ double Polygon::area() const
|
|||
|
||||
bool Polygon::is_counter_clockwise() const
|
||||
{
|
||||
return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this));
|
||||
return ClipperLib::Orientation(this->points);
|
||||
}
|
||||
|
||||
bool Polygon::is_clockwise() const
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
namespace Slic3r {
|
||||
|
||||
class Polygon;
|
||||
typedef std::vector<Polygon> Polygons;
|
||||
using Polygons = std::vector<Polygon>;
|
||||
using PolygonPtrs = std::vector<Polygon*>;
|
||||
using ConstPolygonPtrs = std::vector<const Polygon*>;
|
||||
|
||||
class Polygon : public MultiPoint
|
||||
{
|
||||
|
@ -70,6 +72,9 @@ public:
|
|||
// Projection of a point onto the polygon.
|
||||
Point point_projection(const Point &point) const;
|
||||
std::vector<float> parameter_by_length() const;
|
||||
|
||||
using iterator = Points::iterator;
|
||||
using const_iterator = Points::const_iterator;
|
||||
};
|
||||
|
||||
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
|
||||
|
@ -88,6 +93,8 @@ inline double total_length(const Polygons &polylines) {
|
|||
return total;
|
||||
}
|
||||
|
||||
inline double area(const Polygon &poly) { return poly.area(); }
|
||||
|
||||
inline double area(const Polygons &polys)
|
||||
{
|
||||
double s = 0.;
|
||||
|
@ -215,6 +222,24 @@ inline Polylines to_polylines(Polygons &&polys)
|
|||
return polylines;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(const std::vector<Points> &paths)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(paths.size());
|
||||
for (const Points &path : paths)
|
||||
out.emplace_back(path);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(std::vector<Points> &&paths)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(paths.size());
|
||||
for (const Points &path : paths)
|
||||
out.emplace_back(std::move(path));
|
||||
return out;
|
||||
}
|
||||
|
||||
} // Slic3r
|
||||
|
||||
// start Boost
|
||||
|
|
|
@ -124,6 +124,24 @@ inline Lines to_lines(const Polylines &polys)
|
|||
return lines;
|
||||
}
|
||||
|
||||
inline Polylines to_polylines(const std::vector<Points> &paths)
|
||||
{
|
||||
Polylines out;
|
||||
out.reserve(paths.size());
|
||||
for (const Points &path : paths)
|
||||
out.emplace_back(path);
|
||||
return out;
|
||||
}
|
||||
|
||||
inline Polylines to_polylines(std::vector<Points> &&paths)
|
||||
{
|
||||
Polylines out;
|
||||
out.reserve(paths.size());
|
||||
for (const Points &path : paths)
|
||||
out.emplace_back(std::move(path));
|
||||
return out;
|
||||
}
|
||||
|
||||
inline void polylines_append(Polylines &dst, const Polylines &src)
|
||||
{
|
||||
dst.insert(dst.end(), src.begin(), src.end());
|
||||
|
|
|
@ -296,6 +296,13 @@ void Preset::normalize(DynamicPrintConfig &config)
|
|||
if (auto *gap_fill_enabled = config.option<ConfigOptionBool>("gap_fill_enabled", false); gap_fill_enabled)
|
||||
gap_fill_enabled->value = false;
|
||||
}
|
||||
if (auto *first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height", false); first_layer_height && first_layer_height->percent)
|
||||
if (const auto *layer_height = config.option<ConfigOptionFloat>("layer_height", false); layer_height) {
|
||||
// Legacy conversion - first_layer_height moved from PrintObject setting to a Print setting, thus we are getting rid of the dependency
|
||||
// of first_layer_height on PrintObject specific layer_height. Covert the first layer heigth to an absolute value.
|
||||
first_layer_height->value = first_layer_height->get_abs_value(layer_height->value);
|
||||
first_layer_height->percent = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)
|
||||
|
@ -427,7 +434,7 @@ const std::vector<std::string>& Preset::print_options()
|
|||
"bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield",
|
||||
"min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers",
|
||||
"raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion",
|
||||
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_style",
|
||||
"support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_closing_radius", "support_material_style",
|
||||
"support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers",
|
||||
"support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops",
|
||||
"support_material_contact_distance", "support_material_bottom_contact_distance",
|
||||
|
@ -468,7 +475,7 @@ const std::vector<std::string>& Preset::machine_limits_options()
|
|||
static std::vector<std::string> s_opts;
|
||||
if (s_opts.empty()) {
|
||||
s_opts = {
|
||||
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting",
|
||||
"machine_max_acceleration_extruding", "machine_max_acceleration_retracting", "machine_max_acceleration_travel",
|
||||
"machine_max_acceleration_x", "machine_max_acceleration_y", "machine_max_acceleration_z", "machine_max_acceleration_e",
|
||||
"machine_max_feedrate_x", "machine_max_feedrate_y", "machine_max_feedrate_z", "machine_max_feedrate_e",
|
||||
"machine_min_extruding_rate", "machine_min_travel_rate",
|
||||
|
|
|
@ -100,7 +100,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
"filament_spool_weight",
|
||||
"first_layer_acceleration",
|
||||
"first_layer_bed_temperature",
|
||||
"first_layer_speed",
|
||||
"gcode_comments",
|
||||
"gcode_label_objects",
|
||||
"infill_acceleration",
|
||||
|
@ -139,7 +138,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
"start_filament_gcode",
|
||||
"toolchange_gcode",
|
||||
"threads",
|
||||
"travel_speed",
|
||||
"use_firmware_retraction",
|
||||
"use_relative_e_distances",
|
||||
"use_volumetric_e",
|
||||
|
@ -182,7 +180,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
} else if (
|
||||
opt_key == "complete_objects"
|
||||
|| opt_key == "filament_type"
|
||||
|| opt_key == "filament_soluble"
|
||||
|| opt_key == "first_layer_temperature"
|
||||
|| opt_key == "filament_loading_speed"
|
||||
|| opt_key == "filament_loading_speed_start"
|
||||
|
@ -210,9 +207,17 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||
|| opt_key == "cooling_tube_retraction"
|
||||
|| opt_key == "cooling_tube_length"
|
||||
|| opt_key == "extra_loading_move"
|
||||
|| opt_key == "travel_speed"
|
||||
|| opt_key == "first_layer_speed"
|
||||
|| opt_key == "z_offset") {
|
||||
steps.emplace_back(psWipeTower);
|
||||
steps.emplace_back(psSkirt);
|
||||
} else if (opt_key == "filament_soluble") {
|
||||
steps.emplace_back(psWipeTower);
|
||||
// Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers.
|
||||
// Thus switching between soluble / non-soluble interface layer material may require recalculation of supports.
|
||||
//FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary.
|
||||
osteps.emplace_back(posSupportMaterial);
|
||||
} else if (
|
||||
opt_key == "first_layer_extrusion_width"
|
||||
|| opt_key == "min_layer_height"
|
||||
|
@ -1293,7 +1298,7 @@ std::string Print::validate(std::string* warning) const
|
|||
}
|
||||
|
||||
if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware &&
|
||||
m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin)
|
||||
m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlinLegacy && m_config.gcode_flavor != gcfMarlinFirmware)
|
||||
return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors.");
|
||||
if (! m_config.use_relative_e_distances)
|
||||
return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).");
|
||||
|
@ -1459,7 +1464,8 @@ std::string Print::validate(std::string* warning) const
|
|||
}
|
||||
|
||||
// validate first_layer_height
|
||||
double first_layer_height = object->config().get_abs_value("first_layer_height");
|
||||
assert(! m_config.first_layer_height.percent);
|
||||
double first_layer_height = m_config.first_layer_height.value;
|
||||
double first_layer_min_nozzle_diameter;
|
||||
if (object->has_raft()) {
|
||||
// if we have raft layers, only support material extruder is used on first layer
|
||||
|
@ -1556,9 +1562,8 @@ BoundingBox Print::total_bounding_box() const
|
|||
|
||||
double Print::skirt_first_layer_height() const
|
||||
{
|
||||
if (m_objects.empty())
|
||||
throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects");
|
||||
return m_objects.front()->config().get_abs_value("first_layer_height");
|
||||
assert(! m_config.first_layer_height.percent);
|
||||
return m_config.first_layer_height.value;
|
||||
}
|
||||
|
||||
Flow Print::brim_flow() const
|
||||
|
@ -1987,9 +1992,7 @@ void Print::_make_wipe_tower()
|
|||
|
||||
// Set the extruder & material properties at the wipe tower object.
|
||||
for (size_t i = 0; i < number_of_extruders; ++ i)
|
||||
|
||||
wipe_tower.set_extruder(
|
||||
i, m_config);
|
||||
wipe_tower.set_extruder(i, m_config);
|
||||
|
||||
m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
|
||||
wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
|
||||
|
@ -2015,8 +2018,8 @@ void Print::_make_wipe_tower()
|
|||
volume_to_wipe += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||||
|
||||
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
||||
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, extruder_id,
|
||||
first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back(), volume_to_wipe);
|
||||
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height,
|
||||
current_extruder_id, extruder_id, volume_to_wipe);
|
||||
current_extruder_id = extruder_id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -995,10 +995,8 @@ void PrintConfigDef::init_fff_params()
|
|||
def->label = L("First layer height");
|
||||
def->category = L("Layers and Perimeters");
|
||||
def->tooltip = L("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.");
|
||||
def->sidetext = L("mm or %");
|
||||
"bottom layer to improve adhesion and tolerance for non perfect build plates.");
|
||||
def->sidetext = L("mm");
|
||||
def->ratio_over = "layer_height";
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false));
|
||||
|
||||
|
@ -1103,6 +1101,7 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_values.push_back("teacup");
|
||||
def->enum_values.push_back("makerware");
|
||||
def->enum_values.push_back("marlin");
|
||||
def->enum_values.push_back("marlinfirmware");
|
||||
def->enum_values.push_back("sailfish");
|
||||
def->enum_values.push_back("mach3");
|
||||
def->enum_values.push_back("machinekit");
|
||||
|
@ -1113,7 +1112,8 @@ void PrintConfigDef::init_fff_params()
|
|||
def->enum_labels.push_back("Repetier");
|
||||
def->enum_labels.push_back("Teacup");
|
||||
def->enum_labels.push_back("MakerWare (MakerBot)");
|
||||
def->enum_labels.push_back("Marlin");
|
||||
def->enum_labels.push_back("Marlin (legacy)");
|
||||
def->enum_labels.push_back("Marlin Firmware");
|
||||
def->enum_labels.push_back("Sailfish (MakerBot)");
|
||||
def->enum_labels.push_back("Mach3/LinuxCNC");
|
||||
def->enum_labels.push_back("Machinekit");
|
||||
|
@ -1467,21 +1467,34 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats{ 0., 0. });
|
||||
|
||||
// M204 S... [mm/sec^2]
|
||||
// M204 P... [mm/sec^2]
|
||||
def = this->add("machine_max_acceleration_extruding", coFloats);
|
||||
def->full_label = L("Maximum acceleration when extruding");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Maximum acceleration when extruding (M204 S)");
|
||||
def->tooltip = L("Maximum acceleration when extruding (M204 P)\n\n"
|
||||
"Marlin (legacy) firmware flavor will use this also "
|
||||
"as travel acceleration (M204 T).");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats{ 1500., 1250. });
|
||||
|
||||
|
||||
// M204 R... [mm/sec^2]
|
||||
def = this->add("machine_max_acceleration_retracting", coFloats);
|
||||
def->full_label = L("Maximum acceleration when retracting");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Maximum acceleration when retracting (M204 R)");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloats{ 1500., 1250. });
|
||||
|
||||
// M204 T... [mm/sec^2]
|
||||
def = this->add("machine_max_acceleration_retracting", coFloats);
|
||||
def->full_label = L("Maximum acceleration when retracting");
|
||||
def = this->add("machine_max_acceleration_travel", coFloats);
|
||||
def->full_label = L("Maximum acceleration for travel moves");
|
||||
def->category = L("Machine limits");
|
||||
def->tooltip = L("Maximum acceleration when retracting (M204 T)");
|
||||
def->tooltip = L("Maximum acceleration for travel moves (M204 T)");
|
||||
def->sidetext = L("mm/s²");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
|
@ -1799,8 +1812,8 @@ void PrintConfigDef::init_fff_params()
|
|||
def->category = L("Support material");
|
||||
def->tooltip = L("Density of the first raft or support layer.");
|
||||
def->sidetext = L("%");
|
||||
def->min = 0;
|
||||
def->max = 150;
|
||||
def->min = 10;
|
||||
def->max = 100;
|
||||
def->mode = comExpert;
|
||||
def->set_default_value(new ConfigOptionPercent(90));
|
||||
|
||||
|
@ -2351,6 +2364,16 @@ void PrintConfigDef::init_fff_params()
|
|||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(-1));
|
||||
|
||||
def = this->add("support_material_closing_radius", coFloat);
|
||||
def->label = L("Closing radius");
|
||||
def->category = L("Support material");
|
||||
def->tooltip = L("For snug supports, the support regions will be merged using morphological closing operation."
|
||||
" Gaps smaller than the closing radius will be filled in.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(2));
|
||||
|
||||
def = this->add("support_material_interface_spacing", coFloat);
|
||||
def->label = L("Interface pattern spacing");
|
||||
def->category = L("Support material");
|
||||
|
@ -3585,7 +3608,7 @@ std::string DynamicPrintConfig::validate()
|
|||
FullPrintConfig fpc;
|
||||
fpc.apply(*this, true);
|
||||
// Verify this print options through the FullPrintConfig.
|
||||
return fpc.validate();
|
||||
return Slic3r::validate(fpc);
|
||||
}
|
||||
default:
|
||||
//FIXME no validation on SLA data?
|
||||
|
@ -3594,134 +3617,135 @@ std::string DynamicPrintConfig::validate()
|
|||
}
|
||||
|
||||
//FIXME localize this function.
|
||||
std::string FullPrintConfig::validate()
|
||||
std::string validate(const FullPrintConfig &cfg)
|
||||
{
|
||||
// --layer-height
|
||||
if (this->get_abs_value("layer_height") <= 0)
|
||||
if (cfg.get_abs_value("layer_height") <= 0)
|
||||
return "Invalid value for --layer-height";
|
||||
if (fabs(fmod(this->get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4)
|
||||
if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4)
|
||||
return "--layer-height must be a multiple of print resolution";
|
||||
|
||||
// --first-layer-height
|
||||
if (this->get_abs_value("first_layer_height") <= 0)
|
||||
if (cfg.first_layer_height.value <= 0)
|
||||
return "Invalid value for --first-layer-height";
|
||||
|
||||
// --filament-diameter
|
||||
for (double fd : this->filament_diameter.values)
|
||||
for (double fd : cfg.filament_diameter.values)
|
||||
if (fd < 1)
|
||||
return "Invalid value for --filament-diameter";
|
||||
|
||||
// --nozzle-diameter
|
||||
for (double nd : this->nozzle_diameter.values)
|
||||
for (double nd : cfg.nozzle_diameter.values)
|
||||
if (nd < 0.005)
|
||||
return "Invalid value for --nozzle-diameter";
|
||||
|
||||
// --perimeters
|
||||
if (this->perimeters.value < 0)
|
||||
if (cfg.perimeters.value < 0)
|
||||
return "Invalid value for --perimeters";
|
||||
|
||||
// --solid-layers
|
||||
if (this->top_solid_layers < 0)
|
||||
if (cfg.top_solid_layers < 0)
|
||||
return "Invalid value for --top-solid-layers";
|
||||
if (this->bottom_solid_layers < 0)
|
||||
if (cfg.bottom_solid_layers < 0)
|
||||
return "Invalid value for --bottom-solid-layers";
|
||||
|
||||
if (this->use_firmware_retraction.value &&
|
||||
this->gcode_flavor.value != gcfSmoothie &&
|
||||
this->gcode_flavor.value != gcfRepRapSprinter &&
|
||||
this->gcode_flavor.value != gcfRepRapFirmware &&
|
||||
this->gcode_flavor.value != gcfMarlin &&
|
||||
this->gcode_flavor.value != gcfMachinekit &&
|
||||
this->gcode_flavor.value != gcfRepetier)
|
||||
if (cfg.use_firmware_retraction.value &&
|
||||
cfg.gcode_flavor.value != gcfSmoothie &&
|
||||
cfg.gcode_flavor.value != gcfRepRapSprinter &&
|
||||
cfg.gcode_flavor.value != gcfRepRapFirmware &&
|
||||
cfg.gcode_flavor.value != gcfMarlinLegacy &&
|
||||
cfg.gcode_flavor.value != gcfMarlinFirmware &&
|
||||
cfg.gcode_flavor.value != gcfMachinekit &&
|
||||
cfg.gcode_flavor.value != gcfRepetier)
|
||||
return "--use-firmware-retraction is only supported by Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware";
|
||||
|
||||
if (this->use_firmware_retraction.value)
|
||||
for (unsigned char wipe : this->wipe.values)
|
||||
if (cfg.use_firmware_retraction.value)
|
||||
for (unsigned char wipe : cfg.wipe.values)
|
||||
if (wipe)
|
||||
return "--use-firmware-retraction is not compatible with --wipe";
|
||||
|
||||
// --gcode-flavor
|
||||
if (! print_config_def.get("gcode_flavor")->has_enum_value(this->gcode_flavor.serialize()))
|
||||
if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize()))
|
||||
return "Invalid value for --gcode-flavor";
|
||||
|
||||
// --fill-pattern
|
||||
if (! print_config_def.get("fill_pattern")->has_enum_value(this->fill_pattern.serialize()))
|
||||
if (! print_config_def.get("fill_pattern")->has_enum_value(cfg.fill_pattern.serialize()))
|
||||
return "Invalid value for --fill-pattern";
|
||||
|
||||
// --top-fill-pattern
|
||||
if (! print_config_def.get("top_fill_pattern")->has_enum_value(this->top_fill_pattern.serialize()))
|
||||
if (! print_config_def.get("top_fill_pattern")->has_enum_value(cfg.top_fill_pattern.serialize()))
|
||||
return "Invalid value for --top-fill-pattern";
|
||||
|
||||
// --bottom-fill-pattern
|
||||
if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(this->bottom_fill_pattern.serialize()))
|
||||
if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(cfg.bottom_fill_pattern.serialize()))
|
||||
return "Invalid value for --bottom-fill-pattern";
|
||||
|
||||
// --fill-density
|
||||
if (fabs(this->fill_density.value - 100.) < EPSILON &&
|
||||
! print_config_def.get("top_fill_pattern")->has_enum_value(this->fill_pattern.serialize()))
|
||||
if (fabs(cfg.fill_density.value - 100.) < EPSILON &&
|
||||
! print_config_def.get("top_fill_pattern")->has_enum_value(cfg.fill_pattern.serialize()))
|
||||
return "The selected fill pattern is not supposed to work at 100% density";
|
||||
|
||||
// --infill-every-layers
|
||||
if (this->infill_every_layers < 1)
|
||||
if (cfg.infill_every_layers < 1)
|
||||
return "Invalid value for --infill-every-layers";
|
||||
|
||||
// --skirt-height
|
||||
if (this->skirt_height < 0)
|
||||
if (cfg.skirt_height < 0)
|
||||
return "Invalid value for --skirt-height";
|
||||
|
||||
// --bridge-flow-ratio
|
||||
if (this->bridge_flow_ratio <= 0)
|
||||
if (cfg.bridge_flow_ratio <= 0)
|
||||
return "Invalid value for --bridge-flow-ratio";
|
||||
|
||||
// extruder clearance
|
||||
if (this->extruder_clearance_radius <= 0)
|
||||
if (cfg.extruder_clearance_radius <= 0)
|
||||
return "Invalid value for --extruder-clearance-radius";
|
||||
if (this->extruder_clearance_height <= 0)
|
||||
if (cfg.extruder_clearance_height <= 0)
|
||||
return "Invalid value for --extruder-clearance-height";
|
||||
|
||||
// --extrusion-multiplier
|
||||
for (double em : this->extrusion_multiplier.values)
|
||||
for (double em : cfg.extrusion_multiplier.values)
|
||||
if (em <= 0)
|
||||
return "Invalid value for --extrusion-multiplier";
|
||||
|
||||
// --default-acceleration
|
||||
if ((this->perimeter_acceleration != 0. || this->infill_acceleration != 0. || this->bridge_acceleration != 0. || this->first_layer_acceleration != 0.) &&
|
||||
this->default_acceleration == 0.)
|
||||
if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
|
||||
cfg.default_acceleration == 0.)
|
||||
return "Invalid zero value for --default-acceleration when using other acceleration settings";
|
||||
|
||||
// --spiral-vase
|
||||
if (this->spiral_vase) {
|
||||
if (cfg.spiral_vase) {
|
||||
// Note that we might want to have more than one perimeter on the bottom
|
||||
// solid layers.
|
||||
if (this->perimeters > 1)
|
||||
if (cfg.perimeters > 1)
|
||||
return "Can't make more than one perimeter when spiral vase mode is enabled";
|
||||
else if (this->perimeters < 1)
|
||||
else if (cfg.perimeters < 1)
|
||||
return "Can't make less than one perimeter when spiral vase mode is enabled";
|
||||
if (this->fill_density > 0)
|
||||
if (cfg.fill_density > 0)
|
||||
return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
|
||||
if (this->top_solid_layers > 0)
|
||||
if (cfg.top_solid_layers > 0)
|
||||
return "Spiral vase mode is not compatible with top solid layers";
|
||||
if (this->support_material || this->support_material_enforce_layers > 0)
|
||||
if (cfg.support_material || cfg.support_material_enforce_layers > 0)
|
||||
return "Spiral vase mode is not compatible with support material";
|
||||
}
|
||||
|
||||
// extrusion widths
|
||||
{
|
||||
double max_nozzle_diameter = 0.;
|
||||
for (double dmr : this->nozzle_diameter.values)
|
||||
for (double dmr : cfg.nozzle_diameter.values)
|
||||
max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
|
||||
const char *widths[] = { "external_perimeter", "perimeter", "infill", "solid_infill", "top_infill", "support_material", "first_layer" };
|
||||
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
|
||||
std::string key(widths[i]);
|
||||
key += "_extrusion_width";
|
||||
if (this->get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter)
|
||||
if (cfg.get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter)
|
||||
return std::string("Invalid extrusion width (too large): ") + key;
|
||||
}
|
||||
}
|
||||
|
||||
// Out of range validation of numeric values.
|
||||
for (const std::string &opt_key : this->keys()) {
|
||||
const ConfigOption *opt = this->optptr(opt_key);
|
||||
for (const std::string &opt_key : cfg.keys()) {
|
||||
const ConfigOption *opt = cfg.optptr(opt_key);
|
||||
assert(opt != nullptr);
|
||||
const ConfigOptionDef *optdef = print_config_def.get(opt_key);
|
||||
assert(optdef != nullptr);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -186,7 +186,7 @@ void PrintObject::make_perimeters()
|
|||
m_print->throw_if_canceled();
|
||||
LayerRegion &layerm = *m_layers[layer_idx]->m_regions[region_id];
|
||||
const LayerRegion &upper_layerm = *m_layers[layer_idx+1]->m_regions[region_id];
|
||||
const Polygons upper_layerm_polygons = upper_layerm.slices;
|
||||
const Polygons upper_layerm_polygons = to_polygons(upper_layerm.slices.surfaces);
|
||||
// Filter upper layer polygons in intersection_ppl by their bounding boxes?
|
||||
// my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ];
|
||||
const double total_loop_length = total_length(upper_layerm_polygons);
|
||||
|
@ -591,6 +591,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||
|| opt_key == "support_material_style"
|
||||
|| opt_key == "support_material_xy_spacing"
|
||||
|| opt_key == "support_material_spacing"
|
||||
|| opt_key == "support_material_closing_radius"
|
||||
|| opt_key == "support_material_synchronize_layers"
|
||||
|| opt_key == "support_material_threshold"
|
||||
|| opt_key == "support_material_with_sheath"
|
||||
|
@ -808,19 +809,14 @@ void PrintObject::detect_surfaces_type()
|
|||
// collapse very narrow parts (using the safety offset in the diff is not enough)
|
||||
float offset = layerm->flow(frExternalPerimeter).scaled_width() / 10.f;
|
||||
|
||||
Polygons layerm_slices_surfaces = to_polygons(layerm->slices.surfaces);
|
||||
|
||||
// find top surfaces (difference between current surfaces
|
||||
// of current layer and upper one)
|
||||
Surfaces top;
|
||||
if (upper_layer) {
|
||||
Polygons upper_slices = interface_shells ?
|
||||
to_polygons(upper_layer->m_regions[idx_region]->slices.surfaces) :
|
||||
to_polygons(upper_layer->lslices);
|
||||
surfaces_append(top,
|
||||
//FIXME implement offset2_ex working over ExPolygons, that should be a bit more efficient than calling offset_ex twice.
|
||||
offset_ex(offset_ex(diff_ex(layerm_slices_surfaces, upper_slices, true), -offset), offset),
|
||||
stTop);
|
||||
ExPolygons upper_slices = interface_shells ?
|
||||
diff_ex(layerm->slices.surfaces, upper_layer->m_regions[idx_region]->slices.surfaces, true) :
|
||||
diff_ex(layerm->slices.surfaces, upper_layer->lslices, true);
|
||||
surfaces_append(top, offset2_ex(upper_slices, -offset, offset), stTop);
|
||||
} else {
|
||||
// if no upper layer, all surfaces of this one are solid
|
||||
// we clone surfaces because we're going to clear the slices collection
|
||||
|
@ -838,14 +834,14 @@ void PrintObject::detect_surfaces_type()
|
|||
to_polygons(lower_layer->get_region(idx_region)->slices.surfaces) :
|
||||
to_polygons(lower_layer->slices);
|
||||
surfaces_append(bottom,
|
||||
offset2_ex(diff(layerm_slices_surfaces, lower_slices, true), -offset, offset),
|
||||
offset2_ex(diff(layerm->slices.surfaces, lower_slices, true), -offset, offset),
|
||||
surface_type_bottom_other);
|
||||
#else
|
||||
// Any surface lying on the void is a true bottom bridge (an overhang)
|
||||
surfaces_append(
|
||||
bottom,
|
||||
offset2_ex(
|
||||
diff(layerm_slices_surfaces, to_polygons(lower_layer->lslices), true),
|
||||
diff_ex(layerm->slices.surfaces, lower_layer->lslices, true),
|
||||
-offset, offset),
|
||||
surface_type_bottom_other);
|
||||
// if user requested internal shells, we need to identify surfaces
|
||||
|
@ -856,10 +852,10 @@ void PrintObject::detect_surfaces_type()
|
|||
surfaces_append(
|
||||
bottom,
|
||||
offset2_ex(
|
||||
diff(
|
||||
intersection(layerm_slices_surfaces, to_polygons(lower_layer->lslices)), // supported
|
||||
to_polygons(lower_layer->m_regions[idx_region]->slices.surfaces),
|
||||
true),
|
||||
diff_ex(
|
||||
intersection(layerm->slices.surfaces, lower_layer->lslices), // supported
|
||||
lower_layer->m_regions[idx_region]->slices.surfaces,
|
||||
true),
|
||||
-offset, offset),
|
||||
stBottom);
|
||||
}
|
||||
|
@ -882,7 +878,7 @@ void PrintObject::detect_surfaces_type()
|
|||
Polygons top_polygons = to_polygons(std::move(top));
|
||||
top.clear();
|
||||
surfaces_append(top,
|
||||
diff_ex(top_polygons, to_polygons(bottom), false),
|
||||
diff_ex(top_polygons, bottom, false),
|
||||
stTop);
|
||||
}
|
||||
|
||||
|
@ -899,15 +895,18 @@ void PrintObject::detect_surfaces_type()
|
|||
|
||||
// save surfaces to layer
|
||||
Surfaces &surfaces_out = interface_shells ? surfaces_new[idx_layer] : layerm->slices.surfaces;
|
||||
surfaces_out.clear();
|
||||
Surfaces surfaces_backup;
|
||||
if (! interface_shells) {
|
||||
surfaces_backup = std::move(surfaces_out);
|
||||
surfaces_out.clear();
|
||||
}
|
||||
const Surfaces &surfaces_prev = interface_shells ? layerm->slices.surfaces : surfaces_backup;
|
||||
|
||||
// find internal surfaces (difference between top/bottom surfaces and others)
|
||||
{
|
||||
Polygons topbottom = to_polygons(top);
|
||||
polygons_append(topbottom, to_polygons(bottom));
|
||||
surfaces_append(surfaces_out,
|
||||
diff_ex(layerm_slices_surfaces, topbottom, false),
|
||||
stInternal);
|
||||
surfaces_append(surfaces_out, diff_ex(surfaces_prev, topbottom, false), stInternal);
|
||||
}
|
||||
|
||||
surfaces_append(surfaces_out, std::move(top));
|
||||
|
@ -1011,7 +1010,7 @@ void PrintObject::process_external_surfaces()
|
|||
// Shrink the holes, let the layer above expand slightly inside the unsupported areas.
|
||||
polygons_append(voids, offset(surface.expolygon, unsupported_width));
|
||||
}
|
||||
surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->lslices), voids);
|
||||
surfaces_covered[layer_idx] = diff(this->m_layers[layer_idx]->lslices, voids);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -1106,11 +1105,11 @@ void PrintObject::discover_vertical_shells()
|
|||
LayerRegion &layerm = *layer.m_regions[idx_region];
|
||||
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
|
||||
// Top surfaces.
|
||||
append(cache.top_surfaces, offset(to_expolygons(layerm.slices.filter_by_type(stTop)), min_perimeter_infill_spacing));
|
||||
append(cache.top_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing));
|
||||
append(cache.top_surfaces, offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing));
|
||||
append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), min_perimeter_infill_spacing));
|
||||
// Bottom surfaces.
|
||||
append(cache.bottom_surfaces, offset(to_expolygons(layerm.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing));
|
||||
append(cache.bottom_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing));
|
||||
append(cache.bottom_surfaces, offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
|
||||
append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
|
||||
// Calculate the maximum perimeter offset as if the slice was extruded with a single extruder only.
|
||||
// First find the maxium number of perimeters per region slice.
|
||||
unsigned int perimeters = 0;
|
||||
|
@ -1180,11 +1179,11 @@ void PrintObject::discover_vertical_shells()
|
|||
float min_perimeter_infill_spacing = float(layerm.flow(frSolidInfill).scaled_spacing()) * 1.05f;
|
||||
// Top surfaces.
|
||||
auto &cache = cache_top_botom_regions[idx_layer];
|
||||
cache.top_surfaces = offset(to_expolygons(layerm.slices.filter_by_type(stTop)), min_perimeter_infill_spacing);
|
||||
append(cache.top_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_type(stTop)), min_perimeter_infill_spacing));
|
||||
cache.top_surfaces = offset(layerm.slices.filter_by_type(stTop), min_perimeter_infill_spacing);
|
||||
append(cache.top_surfaces, offset(layerm.fill_surfaces.filter_by_type(stTop), min_perimeter_infill_spacing));
|
||||
// Bottom surfaces.
|
||||
cache.bottom_surfaces = offset(to_expolygons(layerm.slices.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing);
|
||||
append(cache.bottom_surfaces, offset(to_expolygons(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2)), min_perimeter_infill_spacing));
|
||||
cache.bottom_surfaces = offset(layerm.slices.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing);
|
||||
append(cache.bottom_surfaces, offset(layerm.fill_surfaces.filter_by_types(surfaces_bottom, 2), min_perimeter_infill_spacing));
|
||||
// Holes over all regions. Only collect them once, they are valid for all idx_region iterations.
|
||||
if (cache.holes.empty()) {
|
||||
for (size_t idx_region = 0; idx_region < layer.regions().size(); ++ idx_region)
|
||||
|
@ -1406,16 +1405,8 @@ void PrintObject::discover_vertical_shells()
|
|||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// Trim the internal & internalvoid by the shell.
|
||||
Slic3r::ExPolygons new_internal = diff_ex(
|
||||
to_polygons(layerm->fill_surfaces.filter_by_type(stInternal)),
|
||||
shell,
|
||||
false
|
||||
);
|
||||
Slic3r::ExPolygons new_internal_void = diff_ex(
|
||||
to_polygons(layerm->fill_surfaces.filter_by_type(stInternalVoid)),
|
||||
shell,
|
||||
false
|
||||
);
|
||||
Slic3r::ExPolygons new_internal = diff_ex(layerm->fill_surfaces.filter_by_type(stInternal), shell);
|
||||
Slic3r::ExPolygons new_internal_void = diff_ex(layerm->fill_surfaces.filter_by_type(stInternalVoid), shell);
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
|
@ -1520,8 +1511,8 @@ void PrintObject::bridge_over_infill()
|
|||
#endif
|
||||
|
||||
// compute the remaning internal solid surfaces as difference
|
||||
ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true);
|
||||
to_bridge = intersection_ex(to_polygons(to_bridge), internal_solid, true);
|
||||
ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true);
|
||||
to_bridge = intersection_ex(to_bridge, internal_solid, true);
|
||||
// build the new collection of fill_surfaces
|
||||
layerm->fill_surfaces.remove_type(stInternalSolid);
|
||||
for (ExPolygon &ex : to_bridge)
|
||||
|
@ -1874,7 +1865,7 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||
slices = offset_ex(std::move(slices), delta);
|
||||
if (! processed.empty())
|
||||
// Trim by the slices of already processed regions.
|
||||
slices = diff_ex(to_polygons(std::move(slices)), processed);
|
||||
slices = diff_ex(slices, processed);
|
||||
if (size_t(&sliced_volume - &sliced_volumes.front()) + 1 < sliced_volumes.size())
|
||||
// Collect the already processed regions to trim the to be processed regions.
|
||||
polygons_append(processed, slices);
|
||||
|
@ -1925,12 +1916,11 @@ void PrintObject::_slice(const std::vector<coordf_t> &layer_height_profile)
|
|||
LayerRegion *other_layerm = layer->m_regions[other_region_id];
|
||||
if (layerm == nullptr || other_layerm == nullptr || other_layerm->slices.empty() || expolygons_by_layer[layer_id].empty())
|
||||
continue;
|
||||
Polygons other_slices = to_polygons(other_layerm->slices);
|
||||
ExPolygons my_parts = intersection_ex(other_slices, to_polygons(expolygons_by_layer[layer_id]));
|
||||
ExPolygons my_parts = intersection_ex(other_layerm->slices.surfaces, expolygons_by_layer[layer_id]);
|
||||
if (my_parts.empty())
|
||||
continue;
|
||||
// Remove such parts from original region.
|
||||
other_layerm->slices.set(diff_ex(other_slices, to_polygons(my_parts)), stInternal);
|
||||
other_layerm->slices.set(diff_ex(other_layerm->slices.surfaces, my_parts), stInternal);
|
||||
// Append new parts to our region.
|
||||
layerm->slices.append(std::move(my_parts), stInternal);
|
||||
}
|
||||
|
@ -2017,7 +2007,7 @@ end:
|
|||
slices = offset_ex(std::move(slices), xy_compensation_scaled);
|
||||
if (region_id > 0 && clip)
|
||||
// Trim by the slices of already processed regions.
|
||||
slices = diff_ex(to_polygons(std::move(slices)), processed);
|
||||
slices = diff_ex(slices, processed);
|
||||
if (clip && (region_id + 1 < layer->m_regions.size()))
|
||||
// Collect the already processed regions to trim the to be processed regions.
|
||||
polygons_append(processed, slices);
|
||||
|
@ -2206,6 +2196,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(
|
|||
TriangleMesh vol_mesh(model_volume.mesh());
|
||||
vol_mesh.transform(model_volume.get_matrix(), true);
|
||||
mesh.merge(vol_mesh);
|
||||
mesh.repair(false);
|
||||
}
|
||||
if (mesh.stl.stats.number_of_facets > 0) {
|
||||
mesh.transform(m_trafo, true);
|
||||
|
@ -2648,10 +2639,7 @@ void PrintObject::discover_horizontal_shells()
|
|||
neighbor_layerm->fill_surfaces.set(internal_solid, stInternalSolid);
|
||||
// subtract intersections from layer surfaces to get resulting internal surfaces
|
||||
Polygons polygons_internal = to_polygons(std::move(internal_solid));
|
||||
ExPolygons internal = diff_ex(
|
||||
to_polygons(backup.filter_by_type(stInternal)),
|
||||
polygons_internal,
|
||||
true);
|
||||
ExPolygons internal = diff_ex(backup.filter_by_type(stInternal), polygons_internal, true);
|
||||
// assign resulting internal surfaces to layer
|
||||
neighbor_layerm->fill_surfaces.append(internal, stInternal);
|
||||
polygons_append(polygons_internal, to_polygons(std::move(internal)));
|
||||
|
@ -2662,7 +2650,7 @@ void PrintObject::discover_horizontal_shells()
|
|||
backup.group(&top_bottom_groups);
|
||||
for (SurfacesPtr &group : top_bottom_groups)
|
||||
neighbor_layerm->fill_surfaces.append(
|
||||
diff_ex(to_polygons(group), polygons_internal),
|
||||
diff_ex(group, polygons_internal),
|
||||
// Use an existing surface as a template, it carries the bridge angle etc.
|
||||
*group.front());
|
||||
}
|
||||
|
@ -2741,10 +2729,7 @@ void PrintObject::combine_infill()
|
|||
ExPolygons intersection = to_expolygons(layerms.front()->fill_surfaces.filter_by_type(stInternal));
|
||||
// Start looping from the second layer and intersect the current intersection with it.
|
||||
for (size_t i = 1; i < layerms.size(); ++ i)
|
||||
intersection = intersection_ex(
|
||||
to_polygons(intersection),
|
||||
to_polygons(layerms[i]->fill_surfaces.filter_by_type(stInternal)),
|
||||
false);
|
||||
intersection = intersection_ex(layerms[i]->fill_surfaces.filter_by_type(stInternal), intersection);
|
||||
double area_threshold = layerms.front()->infill_area_threshold();
|
||||
if (! intersection.empty() && area_threshold > 0.)
|
||||
intersection.erase(std::remove_if(intersection.begin(), intersection.end(),
|
||||
|
@ -2773,7 +2758,7 @@ void PrintObject::combine_infill()
|
|||
for (ExPolygon &expoly : intersection)
|
||||
polygons_append(intersection_with_clearance, offset(expoly, clearance_offset));
|
||||
for (LayerRegion *layerm : layerms) {
|
||||
Polygons internal = to_polygons(layerm->fill_surfaces.filter_by_type(stInternal));
|
||||
Polygons internal = to_polygons(std::move(layerm->fill_surfaces.filter_by_type(stInternal)));
|
||||
layerm->fill_surfaces.remove_type(stInternal);
|
||||
layerm->fill_surfaces.append(diff_ex(internal, intersection_with_clearance, false), stInternal);
|
||||
if (layerm == layerms.back()) {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <libslic3r/SLA/RasterBase.hpp>
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||
|
||||
// For rasterizing
|
||||
#include <agg/agg_basics.h>
|
||||
|
@ -21,10 +20,7 @@
|
|||
namespace Slic3r {
|
||||
|
||||
inline const Polygon& contour(const ExPolygon& p) { return p.contour; }
|
||||
inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
|
||||
|
||||
inline const Polygons& holes(const ExPolygon& p) { return p.holes; }
|
||||
inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
|
||||
|
||||
namespace sla {
|
||||
|
||||
|
@ -77,8 +73,6 @@ protected:
|
|||
double getPx(const Point &p) { return p(0) * m_pxdim_scaled.w_mm; }
|
||||
double getPy(const Point &p) { return p(1) * m_pxdim_scaled.h_mm; }
|
||||
agg::path_storage to_path(const Polygon &poly) { return to_path(poly.points); }
|
||||
double getPx(const ClipperLib::IntPoint &p) { return p.X * m_pxdim_scaled.w_mm; }
|
||||
double getPy(const ClipperLib::IntPoint& p) { return p.Y * m_pxdim_scaled.h_mm; }
|
||||
|
||||
template<class PointVec> agg::path_storage _to_path(const PointVec& v)
|
||||
{
|
||||
|
@ -168,7 +162,6 @@ public:
|
|||
}
|
||||
|
||||
void draw(const ExPolygon &poly) override { _draw(poly); }
|
||||
void draw(const ClipperLib::Polygon &poly) override { _draw(poly); }
|
||||
|
||||
EncodedRaster encode(RasterEncoder encoder) const override
|
||||
{
|
||||
|
|
|
@ -42,9 +42,10 @@ Point ConcaveHull::centroid(const Points &pp)
|
|||
|
||||
// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
|
||||
// mode
|
||||
static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
|
||||
coord_t delta,
|
||||
ClipperLib::JoinType jointype)
|
||||
template<typename PolygonsProvider>
|
||||
static ClipperLib::Paths fast_offset(PolygonsProvider &&paths,
|
||||
coord_t delta,
|
||||
ClipperLib::JoinType jointype)
|
||||
{
|
||||
using ClipperLib::ClipperOffset;
|
||||
using ClipperLib::etClosedPolygon;
|
||||
|
@ -61,7 +62,7 @@ static ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
|
|||
return {};
|
||||
}
|
||||
|
||||
offs.AddPaths(paths, jointype, etClosedPolygon);
|
||||
offs.AddPaths(std::forward<PolygonsProvider>(paths), jointype, etClosedPolygon);
|
||||
|
||||
Paths result;
|
||||
offs.Execute(result, static_cast<double>(delta));
|
||||
|
@ -157,11 +158,9 @@ ExPolygons ConcaveHull::to_expolygons() const
|
|||
|
||||
ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
|
||||
{
|
||||
ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(hull.polygons());
|
||||
paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound);
|
||||
paths = fast_offset(paths, -delta, ClipperLib::jtRound);
|
||||
ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(paths);
|
||||
for (ExPolygon &p : ret) p.holes = {};
|
||||
ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(
|
||||
fast_offset(fast_offset(ClipperUtils::PolygonsProvider(hull.polygons()), 2 * delta, ClipperLib::jtRound), -delta, ClipperLib::jtRound));
|
||||
for (ExPolygon &p : ret) p.holes.clear();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
#ifndef SLA_CONCURRENCY_H
|
||||
#define SLA_CONCURRENCY_H
|
||||
|
||||
#include <tbb/spin_mutex.h>
|
||||
#include <tbb/mutex.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_reduce.h>
|
||||
#include <tbb/task_arena.h>
|
||||
// FIXME: Deprecated
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include <libslic3r/libslic3r.h>
|
||||
#include <libslic3r/Execution/ExecutionSeq.hpp>
|
||||
#include <libslic3r/Execution/ExecutionTBB.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace sla {
|
||||
|
@ -23,124 +17,48 @@ template<bool> struct _ccr {};
|
|||
|
||||
template<> struct _ccr<true>
|
||||
{
|
||||
using SpinningMutex = tbb::spin_mutex;
|
||||
using BlockingMutex = tbb::mutex;
|
||||
|
||||
template<class Fn, class It>
|
||||
static IteratorOnly<It, void> loop_(const tbb::blocked_range<It> &range, Fn &&fn)
|
||||
{
|
||||
for (auto &el : range) fn(el);
|
||||
}
|
||||
|
||||
template<class Fn, class I>
|
||||
static IntegerOnly<I, void> loop_(const tbb::blocked_range<I> &range, Fn &&fn)
|
||||
{
|
||||
for (I i = range.begin(); i < range.end(); ++i) fn(i);
|
||||
}
|
||||
using SpinningMutex = execution::SpinningMutex<ExecutionTBB>;
|
||||
using BlockingMutex = execution::BlockingMutex<ExecutionTBB>;
|
||||
|
||||
template<class It, class Fn>
|
||||
static void for_each(It from, It to, Fn &&fn, size_t granularity = 1)
|
||||
{
|
||||
tbb::parallel_for(tbb::blocked_range{from, to, granularity},
|
||||
[&fn](const auto &range) {
|
||||
loop_(range, std::forward<Fn>(fn));
|
||||
});
|
||||
execution::for_each(ex_tbb, from, to, std::forward<Fn>(fn), granularity);
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T, class AccessFn>
|
||||
static T reduce(I from,
|
||||
I to,
|
||||
const T &init,
|
||||
MergeFn &&mergefn,
|
||||
AccessFn &&access,
|
||||
size_t granularity = 1
|
||||
)
|
||||
template<class...Args>
|
||||
static auto reduce(Args&&...args)
|
||||
{
|
||||
return tbb::parallel_reduce(
|
||||
tbb::blocked_range{from, to, granularity}, init,
|
||||
[&](const auto &range, T subinit) {
|
||||
T acc = subinit;
|
||||
loop_(range, [&](auto &i) { acc = mergefn(acc, access(i)); });
|
||||
return acc;
|
||||
},
|
||||
std::forward<MergeFn>(mergefn));
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T>
|
||||
static IteratorOnly<I, T> reduce(I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn &&mergefn,
|
||||
size_t granularity = 1)
|
||||
{
|
||||
return reduce(
|
||||
from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](typename I::value_type &i) { return i; }, granularity);
|
||||
return execution::reduce(ex_tbb, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
static size_t max_concurreny()
|
||||
{
|
||||
return tbb::this_task_arena::max_concurrency();
|
||||
return execution::max_concurrency(ex_tbb);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct _ccr<false>
|
||||
{
|
||||
private:
|
||||
struct _Mtx { inline void lock() {} inline void unlock() {} };
|
||||
|
||||
public:
|
||||
using SpinningMutex = _Mtx;
|
||||
using BlockingMutex = _Mtx;
|
||||
|
||||
template<class Fn, class It>
|
||||
static IteratorOnly<It, void> loop_(It from, It to, Fn &&fn)
|
||||
{
|
||||
for (auto it = from; it != to; ++it) fn(*it);
|
||||
}
|
||||
|
||||
template<class Fn, class I>
|
||||
static IntegerOnly<I, void> loop_(I from, I to, Fn &&fn)
|
||||
{
|
||||
for (I i = from; i < to; ++i) fn(i);
|
||||
}
|
||||
using SpinningMutex = execution::SpinningMutex<ExecutionSeq>;
|
||||
using BlockingMutex = execution::BlockingMutex<ExecutionSeq>;
|
||||
|
||||
template<class It, class Fn>
|
||||
static void for_each(It from,
|
||||
It to,
|
||||
Fn &&fn,
|
||||
size_t /* ignore granularity */ = 1)
|
||||
static void for_each(It from, It to, Fn &&fn, size_t granularity = 1)
|
||||
{
|
||||
loop_(from, to, std::forward<Fn>(fn));
|
||||
execution::for_each(ex_seq, from, to, std::forward<Fn>(fn), granularity);
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T, class AccessFn>
|
||||
static T reduce(I from,
|
||||
I to,
|
||||
const T & init,
|
||||
MergeFn &&mergefn,
|
||||
AccessFn &&access,
|
||||
size_t /*granularity*/ = 1
|
||||
)
|
||||
template<class...Args>
|
||||
static auto reduce(Args&&...args)
|
||||
{
|
||||
T acc = init;
|
||||
loop_(from, to, [&](auto &i) { acc = mergefn(acc, access(i)); });
|
||||
return acc;
|
||||
return execution::reduce(ex_seq, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<class I, class MergeFn, class T>
|
||||
static IteratorOnly<I, T> reduce(I from,
|
||||
I to,
|
||||
const T &init,
|
||||
MergeFn &&mergefn,
|
||||
size_t /*granularity*/ = 1
|
||||
)
|
||||
static size_t max_concurreny()
|
||||
{
|
||||
return reduce(from, to, init, std::forward<MergeFn>(mergefn),
|
||||
[](typename I::value_type &i) { return i; });
|
||||
return execution::max_concurrency(ex_seq);
|
||||
}
|
||||
|
||||
static size_t max_concurreny() { return 1; }
|
||||
};
|
||||
|
||||
using ccr = _ccr<USE_FULL_CONCURRENCY>;
|
||||
|
|
|
@ -179,10 +179,10 @@ PadSkeleton divide_blueprint(const ExPolygons &bp)
|
|||
ret.outer.reserve(size_t(ptree.Total()));
|
||||
|
||||
for (ClipperLib::PolyTree::PolyNode *node : ptree.Childs) {
|
||||
ExPolygon poly(ClipperPath_to_Slic3rPolygon(node->Contour));
|
||||
ExPolygon poly;
|
||||
poly.contour.points = std::move(node->Contour);
|
||||
for (ClipperLib::PolyTree::PolyNode *child : node->Childs) {
|
||||
poly.holes.emplace_back(
|
||||
ClipperPath_to_Slic3rPolygon(child->Contour));
|
||||
poly.holes.emplace_back(std::move(child->Contour));
|
||||
|
||||
traverse_pt(child->Childs, &ret.inner);
|
||||
}
|
||||
|
@ -342,18 +342,18 @@ public:
|
|||
template<class...Args>
|
||||
ExPolygon offset_contour_only(const ExPolygon &poly, coord_t delta, Args...args)
|
||||
{
|
||||
ExPolygons tmp = offset_ex(poly.contour, float(delta), args...);
|
||||
Polygons tmp = offset(poly.contour, float(delta), args...);
|
||||
|
||||
if (tmp.empty()) return {};
|
||||
|
||||
Polygons holes = poly.holes;
|
||||
for (auto &h : holes) h.reverse();
|
||||
|
||||
tmp = diff_ex(to_polygons(tmp), holes);
|
||||
ExPolygons tmp2 = diff_ex(tmp, holes);
|
||||
|
||||
if (tmp.empty()) return {};
|
||||
if (tmp2.empty()) return {};
|
||||
|
||||
return tmp.front();
|
||||
return std::move(tmp2.front());
|
||||
}
|
||||
|
||||
bool add_cavity(Contour3D &pad, ExPolygon &top_poly, const PadConfig3D &cfg,
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include <libslic3r/ExPolygon.hpp>
|
||||
#include <libslic3r/SLA/Concurrency.hpp>
|
||||
|
||||
namespace ClipperLib { struct Polygon; }
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
template<class T> using uqptr = std::unique_ptr<T>;
|
||||
|
@ -92,7 +90,6 @@ public:
|
|||
|
||||
/// Draw a polygon with holes.
|
||||
virtual void draw(const ExPolygon& poly) = 0;
|
||||
virtual void draw(const ClipperLib::Polygon& poly) = 0;
|
||||
|
||||
/// Get the resolution of the raster.
|
||||
virtual Resolution resolution() const = 0;
|
||||
|
|
|
@ -1,91 +1,110 @@
|
|||
#include <limits>
|
||||
|
||||
#include <libslic3r/SLA/Rotfinder.hpp>
|
||||
#include <libslic3r/SLA/Concurrency.hpp>
|
||||
|
||||
#include <libslic3r/Execution/ExecutionTBB.hpp>
|
||||
#include <libslic3r/Execution/ExecutionSeq.hpp>
|
||||
|
||||
#include <libslic3r/Optimize/BruteforceOptimizer.hpp>
|
||||
#include <libslic3r/Optimize/NLoptOptimizer.hpp>
|
||||
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <libslic3r/Geometry.hpp>
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
namespace Slic3r { namespace sla {
|
||||
|
||||
inline bool is_on_floor(const SLAPrintObject &mo)
|
||||
{
|
||||
auto opt_elevation = mo.config().support_object_elevation.getFloat();
|
||||
auto opt_padaround = mo.config().pad_around_object.getBool();
|
||||
namespace {
|
||||
|
||||
return opt_elevation < EPSILON || opt_padaround;
|
||||
}
|
||||
|
||||
// Find transformed mesh ground level without copy and with parallel reduce.
|
||||
double find_ground_level(const TriangleMesh &mesh,
|
||||
const Transform3d & tr,
|
||||
size_t threads)
|
||||
{
|
||||
size_t vsize = mesh.its.vertices.size();
|
||||
|
||||
auto minfn = [](double a, double b) { return std::min(a, b); };
|
||||
|
||||
auto accessfn = [&mesh, &tr] (size_t vi) {
|
||||
return (tr * mesh.its.vertices[vi].template cast<double>()).z();
|
||||
};
|
||||
|
||||
double zmin = std::numeric_limits<double>::max();
|
||||
size_t granularity = vsize / threads;
|
||||
return ccr_par::reduce(size_t(0), vsize, zmin, minfn, accessfn, granularity);
|
||||
}
|
||||
inline const Vec3f DOWN = {0.f, 0.f, -1.f};
|
||||
constexpr double POINTS_PER_UNIT_AREA = 1.f;
|
||||
|
||||
// Get the vertices of a triangle directly in an array of 3 points
|
||||
std::array<Vec3d, 3> get_triangle_vertices(const TriangleMesh &mesh,
|
||||
std::array<Vec3f, 3> get_triangle_vertices(const TriangleMesh &mesh,
|
||||
size_t faceidx)
|
||||
{
|
||||
const auto &face = mesh.its.indices[faceidx];
|
||||
return {Vec3d{mesh.its.vertices[face(0)].cast<double>()},
|
||||
Vec3d{mesh.its.vertices[face(1)].cast<double>()},
|
||||
Vec3d{mesh.its.vertices[face(2)].cast<double>()}};
|
||||
return {mesh.its.vertices[face(0)],
|
||||
mesh.its.vertices[face(1)],
|
||||
mesh.its.vertices[face(2)]};
|
||||
}
|
||||
|
||||
std::array<Vec3d, 3> get_transformed_triangle(const TriangleMesh &mesh,
|
||||
const Transform3d & tr,
|
||||
std::array<Vec3f, 3> get_transformed_triangle(const TriangleMesh &mesh,
|
||||
const Transform3f & tr,
|
||||
size_t faceidx)
|
||||
{
|
||||
const auto &tri = get_triangle_vertices(mesh, faceidx);
|
||||
return {tr * tri[0], tr * tri[1], tr * tri[2]};
|
||||
}
|
||||
|
||||
template<class T> Vec<3, T> normal(const std::array<Vec<3, T>, 3> &tri)
|
||||
{
|
||||
Vec<3, T> U = tri[1] - tri[0];
|
||||
Vec<3, T> V = tri[2] - tri[0];
|
||||
return U.cross(V).normalized();
|
||||
}
|
||||
|
||||
template<class T, class AccessFn>
|
||||
T sum_score(AccessFn &&accessfn, size_t facecount, size_t Nthreads)
|
||||
{
|
||||
T initv = 0.;
|
||||
auto mergefn = [](T a, T b) { return a + b; };
|
||||
size_t grainsize = facecount / Nthreads;
|
||||
size_t from = 0, to = facecount;
|
||||
|
||||
return execution::reduce(ex_tbb, from, to, initv, mergefn, accessfn, grainsize);
|
||||
}
|
||||
|
||||
// Try to guess the number of support points needed to support a mesh
|
||||
double get_misalginment_score(const TriangleMesh &mesh, const Transform3f &tr)
|
||||
{
|
||||
if (mesh.its.vertices.empty()) return std::nan("");
|
||||
|
||||
auto accessfn = [&mesh, &tr](size_t fi) {
|
||||
auto triangle = get_transformed_triangle(mesh, tr, fi);
|
||||
Vec3f U = triangle[1] - triangle[0];
|
||||
Vec3f V = triangle[2] - triangle[0];
|
||||
Vec3f C = U.cross(V);
|
||||
|
||||
// We should score against the alignment with the reference planes
|
||||
return scaled<int_fast64_t>(std::abs(C.dot(Vec3f::UnitX())) +
|
||||
std::abs(C.dot(Vec3f::UnitY())));
|
||||
};
|
||||
|
||||
size_t facecount = mesh.its.indices.size();
|
||||
size_t Nthreads = std::thread::hardware_concurrency();
|
||||
double S = unscaled(sum_score<int_fast64_t>(accessfn, facecount, Nthreads));
|
||||
|
||||
return S / facecount;
|
||||
}
|
||||
|
||||
// Get area and normal of a triangle
|
||||
struct Facestats {
|
||||
Vec3d normal;
|
||||
Vec3f normal;
|
||||
double area;
|
||||
|
||||
explicit Facestats(const std::array<Vec3d, 3> &triangle)
|
||||
explicit Facestats(const std::array<Vec3f, 3> &triangle)
|
||||
{
|
||||
Vec3d U = triangle[1] - triangle[0];
|
||||
Vec3d V = triangle[2] - triangle[0];
|
||||
Vec3d C = U.cross(V);
|
||||
Vec3f U = triangle[1] - triangle[0];
|
||||
Vec3f V = triangle[2] - triangle[0];
|
||||
Vec3f C = U.cross(V);
|
||||
normal = C.normalized();
|
||||
area = 0.5 * C.norm();
|
||||
}
|
||||
};
|
||||
|
||||
inline const Vec3d DOWN = {0., 0., -1.};
|
||||
constexpr double POINTS_PER_UNIT_AREA = 1.;
|
||||
|
||||
// The score function for a particular face
|
||||
inline double get_score(const Facestats &fc)
|
||||
inline double get_supportedness_score(const Facestats &fc)
|
||||
{
|
||||
// Simply get the angle (acos of dot product) between the face normal and
|
||||
// the DOWN vector.
|
||||
double phi = 1. - std::acos(fc.normal.dot(DOWN)) / PI;
|
||||
float phi = 1. - std::acos(fc.normal.dot(DOWN)) / float(PI);
|
||||
|
||||
// Only consider faces that have have slopes below 90 deg:
|
||||
phi = phi * (phi > 0.5);
|
||||
// Only consider faces that have slopes below 90 deg:
|
||||
phi = phi * (phi >= 0.5f);
|
||||
|
||||
// Make the huge slopes more significant than the smaller slopes
|
||||
phi = phi * phi * phi;
|
||||
|
@ -94,96 +113,92 @@ inline double get_score(const Facestats &fc)
|
|||
return fc.area * POINTS_PER_UNIT_AREA * phi;
|
||||
}
|
||||
|
||||
template<class AccessFn>
|
||||
double sum_score(AccessFn &&accessfn, size_t facecount, size_t Nthreads)
|
||||
{
|
||||
double initv = 0.;
|
||||
auto mergefn = std::plus<double>{};
|
||||
size_t grainsize = facecount / Nthreads;
|
||||
size_t from = 0, to = facecount;
|
||||
|
||||
return ccr_par::reduce(from, to, initv, mergefn, accessfn, grainsize);
|
||||
}
|
||||
|
||||
// Try to guess the number of support points needed to support a mesh
|
||||
double get_model_supportedness(const TriangleMesh &mesh, const Transform3d &tr)
|
||||
double get_supportedness_score(const TriangleMesh &mesh, const Transform3f &tr)
|
||||
{
|
||||
if (mesh.its.vertices.empty()) return std::nan("");
|
||||
|
||||
auto accessfn = [&mesh, &tr](size_t fi) {
|
||||
Facestats fc{get_transformed_triangle(mesh, tr, fi)};
|
||||
return get_score(fc);
|
||||
|
||||
return get_supportedness_score(fc);
|
||||
};
|
||||
|
||||
size_t facecount = mesh.its.indices.size();
|
||||
size_t Nthreads = std::thread::hardware_concurrency();
|
||||
return sum_score(accessfn, facecount, Nthreads) / facecount;
|
||||
double S = unscaled(sum_score<int_fast64_t>(accessfn, facecount, Nthreads));
|
||||
|
||||
return S / facecount;
|
||||
}
|
||||
|
||||
double get_model_supportedness_onfloor(const TriangleMesh &mesh,
|
||||
const Transform3d & tr)
|
||||
// Find transformed mesh ground level without copy and with parallel reduce.
|
||||
float find_ground_level(const TriangleMesh &mesh,
|
||||
const Transform3f & tr,
|
||||
size_t threads)
|
||||
{
|
||||
size_t vsize = mesh.its.vertices.size();
|
||||
|
||||
auto minfn = [](float a, float b) { return std::min(a, b); };
|
||||
|
||||
auto accessfn = [&mesh, &tr] (size_t vi) {
|
||||
return (tr * mesh.its.vertices[vi]).z();
|
||||
};
|
||||
|
||||
auto zmin = std::numeric_limits<float>::max();
|
||||
size_t granularity = vsize / threads;
|
||||
return execution::reduce(ex_tbb, size_t(0), vsize, zmin, minfn, accessfn, granularity);
|
||||
}
|
||||
|
||||
float get_supportedness_onfloor_score(const TriangleMesh &mesh,
|
||||
const Transform3f & tr)
|
||||
{
|
||||
if (mesh.its.vertices.empty()) return std::nan("");
|
||||
|
||||
size_t Nthreads = std::thread::hardware_concurrency();
|
||||
|
||||
double zmin = find_ground_level(mesh, tr, Nthreads);
|
||||
double zlvl = zmin + 0.1; // Set up a slight tolerance from z level
|
||||
float zmin = find_ground_level(mesh, tr, Nthreads);
|
||||
float zlvl = zmin + 0.1f; // Set up a slight tolerance from z level
|
||||
|
||||
auto accessfn = [&mesh, &tr, zlvl](size_t fi) {
|
||||
std::array<Vec3d, 3> tri = get_transformed_triangle(mesh, tr, fi);
|
||||
std::array<Vec3f, 3> tri = get_transformed_triangle(mesh, tr, fi);
|
||||
Facestats fc{tri};
|
||||
|
||||
if (tri[0].z() <= zlvl && tri[1].z() <= zlvl && tri[2].z() <= zlvl)
|
||||
return -fc.area * POINTS_PER_UNIT_AREA;
|
||||
|
||||
return get_score(fc);
|
||||
return get_supportedness_score(fc);
|
||||
};
|
||||
|
||||
size_t facecount = mesh.its.indices.size();
|
||||
return sum_score(accessfn, facecount, Nthreads) / facecount;
|
||||
double S = unscaled(sum_score<int_fast64_t>(accessfn, facecount, Nthreads));
|
||||
|
||||
return S / facecount;
|
||||
}
|
||||
|
||||
using XYRotation = std::array<double, 2>;
|
||||
|
||||
// prepare the rotation transformation
|
||||
Transform3d to_transform3d(const XYRotation &rot)
|
||||
Transform3f to_transform3f(const XYRotation &rot)
|
||||
{
|
||||
Transform3d rt = Transform3d::Identity();
|
||||
rt.rotate(Eigen::AngleAxisd(rot[1], Vec3d::UnitY()));
|
||||
rt.rotate(Eigen::AngleAxisd(rot[0], Vec3d::UnitX()));
|
||||
Transform3f rt = Transform3f::Identity();
|
||||
rt.rotate(Eigen::AngleAxisf(float(rot[1]), Vec3f::UnitY()));
|
||||
rt.rotate(Eigen::AngleAxisf(float(rot[0]), Vec3f::UnitX()));
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
XYRotation from_transform3d(const Transform3d &tr)
|
||||
XYRotation from_transform3f(const Transform3f &tr)
|
||||
{
|
||||
Vec3d rot3d = Geometry::Transformation {tr}.get_rotation();
|
||||
return {rot3d.x(), rot3d.y()};
|
||||
Vec3d rot3 = Geometry::Transformation{tr.cast<double>()}.get_rotation();
|
||||
return {rot3.x(), rot3.y()};
|
||||
}
|
||||
|
||||
// Find the best score from a set of function inputs. Evaluate for every point.
|
||||
template<size_t N, class Fn, class It, class StopCond>
|
||||
std::array<double, N> find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn)
|
||||
inline bool is_on_floor(const SLAPrintObjectConfig &cfg)
|
||||
{
|
||||
std::array<double, N> ret = {};
|
||||
auto opt_elevation = cfg.support_object_elevation.getFloat();
|
||||
auto opt_padaround = cfg.pad_around_object.getBool();
|
||||
|
||||
double score = std::numeric_limits<double>::max();
|
||||
|
||||
size_t Nthreads = std::thread::hardware_concurrency();
|
||||
size_t dist = std::distance(from, to);
|
||||
std::vector<double> scores(dist, score);
|
||||
|
||||
ccr_par::for_each(size_t(0), dist, [&stopfn, &scores, &fn, &from](size_t i) {
|
||||
if (stopfn()) return;
|
||||
|
||||
scores[i] = fn(*(from + i));
|
||||
}, dist / Nthreads);
|
||||
|
||||
auto it = std::min_element(scores.begin(), scores.end());
|
||||
|
||||
if (it != scores.end()) ret = *(from + std::distance(scores.begin(), it));
|
||||
|
||||
return ret;
|
||||
return opt_elevation < EPSILON || opt_padaround;
|
||||
}
|
||||
|
||||
// collect the rotations for each face of the convex hull
|
||||
|
@ -214,8 +229,8 @@ std::vector<XYRotation> get_chull_rotations(const TriangleMesh &mesh, size_t max
|
|||
Facestats fc{get_triangle_vertices(chull, fi)};
|
||||
|
||||
if (fc.area > area_threshold) {
|
||||
auto q = Eigen::Quaterniond{}.FromTwoVectors(fc.normal, DOWN);
|
||||
XYRotation rot = from_transform3d(Transform3d::Identity() * q);
|
||||
auto q = Eigen::Quaternionf{}.FromTwoVectors(fc.normal, DOWN);
|
||||
XYRotation rot = from_transform3f(Transform3f::Identity() * q);
|
||||
RotArea ra = {rot, fc.area};
|
||||
|
||||
auto it = std::lower_bound(inputs.begin(), inputs.end(), ra, rotcmp);
|
||||
|
@ -238,10 +253,95 @@ std::vector<XYRotation> get_chull_rotations(const TriangleMesh &mesh, size_t max
|
|||
return ret;
|
||||
}
|
||||
|
||||
Vec2d find_best_rotation(const SLAPrintObject & po,
|
||||
float accuracy,
|
||||
std::function<void(unsigned)> statuscb,
|
||||
std::function<bool()> stopcond)
|
||||
// Find the best score from a set of function inputs. Evaluate for every point.
|
||||
template<size_t N, class Fn, class It, class StopCond>
|
||||
std::array<double, N> find_min_score(Fn &&fn, It from, It to, StopCond &&stopfn)
|
||||
{
|
||||
std::array<double, N> ret = {};
|
||||
|
||||
double score = std::numeric_limits<double>::max();
|
||||
|
||||
size_t Nthreads = std::thread::hardware_concurrency();
|
||||
size_t dist = std::distance(from, to);
|
||||
std::vector<double> scores(dist, score);
|
||||
|
||||
execution::for_each(
|
||||
ex_tbb, size_t(0), dist, [&stopfn, &scores, &fn, &from](size_t i) {
|
||||
if (stopfn()) return;
|
||||
|
||||
scores[i] = fn(*(from + i));
|
||||
},
|
||||
dist / Nthreads);
|
||||
|
||||
auto it = std::min_element(scores.begin(), scores.end());
|
||||
|
||||
if (it != scores.end())
|
||||
ret = *(from + std::distance(scores.begin(), it));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Vec2d find_best_misalignment_rotation(const ModelObject & mo,
|
||||
const RotOptimizeParams ¶ms)
|
||||
{
|
||||
static constexpr unsigned MAX_TRIES = 1000;
|
||||
|
||||
// return value
|
||||
XYRotation rot;
|
||||
|
||||
// We will use only one instance of this converted mesh to examine different
|
||||
// rotations
|
||||
TriangleMesh mesh = mo.raw_mesh();
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
// To keep track of the number of iterations
|
||||
int status = 0;
|
||||
|
||||
// The maximum number of iterations
|
||||
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
|
||||
|
||||
auto &statuscb = params.statuscb();
|
||||
|
||||
// call status callback with zero, because we are at the start
|
||||
statuscb(status);
|
||||
|
||||
auto statusfn = [&statuscb, &status, &max_tries] {
|
||||
// report status
|
||||
statuscb(++status * 100.0/max_tries);
|
||||
};
|
||||
|
||||
auto stopcond = [&statuscb] {
|
||||
return ! statuscb(-1);
|
||||
};
|
||||
|
||||
// Preparing the optimizer.
|
||||
size_t gridsize = std::sqrt(max_tries);
|
||||
opt::Optimizer<opt::AlgBruteForce> solver(opt::StopCriteria{}
|
||||
.max_iterations(max_tries)
|
||||
.stop_condition(stopcond),
|
||||
gridsize);
|
||||
|
||||
// We are searching rotations around only two axes x, y. Thus the
|
||||
// problem becomes a 2 dimensional optimization task.
|
||||
// We can specify the bounds for a dimension in the following way:
|
||||
auto bounds = opt::bounds({ {-PI/2, PI/2}, {-PI/2, PI/2} });
|
||||
|
||||
auto result = solver.to_max().optimize(
|
||||
[&mesh, &statusfn] (const XYRotation &rot)
|
||||
{
|
||||
statusfn();
|
||||
return get_misalginment_score(mesh, to_transform3f(rot));
|
||||
}, opt::initvals({0., 0.}), bounds);
|
||||
|
||||
rot = result.optimum;
|
||||
|
||||
return {rot[0], rot[1]};
|
||||
}
|
||||
|
||||
Vec2d find_least_supports_rotation(const ModelObject & mo,
|
||||
const RotOptimizeParams ¶ms)
|
||||
{
|
||||
static const unsigned MAX_TRIES = 1000;
|
||||
|
||||
|
@ -250,14 +350,16 @@ Vec2d find_best_rotation(const SLAPrintObject & po,
|
|||
|
||||
// We will use only one instance of this converted mesh to examine different
|
||||
// rotations
|
||||
TriangleMesh mesh = po.model_object()->raw_mesh();
|
||||
TriangleMesh mesh = mo.raw_mesh();
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
// To keep track of the number of iterations
|
||||
unsigned status = 0;
|
||||
|
||||
// The maximum number of iterations
|
||||
auto max_tries = unsigned(accuracy * MAX_TRIES);
|
||||
auto max_tries = unsigned(params.accuracy() * MAX_TRIES);
|
||||
|
||||
auto &statuscb = params.statuscb();
|
||||
|
||||
// call status callback with zero, because we are at the start
|
||||
statuscb(status);
|
||||
|
@ -267,8 +369,18 @@ Vec2d find_best_rotation(const SLAPrintObject & po,
|
|||
statuscb(unsigned(++status * 100.0/max_tries) );
|
||||
};
|
||||
|
||||
auto stopcond = [&statuscb] {
|
||||
return ! statuscb(-1);
|
||||
};
|
||||
|
||||
SLAPrintObjectConfig pocfg;
|
||||
if (params.print_config())
|
||||
pocfg.apply(*params.print_config(), true);
|
||||
|
||||
pocfg.apply(mo.config.get());
|
||||
|
||||
// Different search methods have to be used depending on the model elevation
|
||||
if (is_on_floor(po)) {
|
||||
if (is_on_floor(pocfg)) {
|
||||
|
||||
std::vector<XYRotation> inputs = get_chull_rotations(mesh, max_tries);
|
||||
max_tries = inputs.size();
|
||||
|
@ -278,17 +390,19 @@ Vec2d find_best_rotation(const SLAPrintObject & po,
|
|||
|
||||
auto objfn = [&mesh, &statusfn](const XYRotation &rot) {
|
||||
statusfn();
|
||||
Transform3d tr = to_transform3d(rot);
|
||||
return get_model_supportedness_onfloor(mesh, tr);
|
||||
Transform3f tr = to_transform3f(rot);
|
||||
return get_supportedness_onfloor_score(mesh, tr);
|
||||
};
|
||||
|
||||
rot = find_min_score<2>(objfn, inputs.begin(), inputs.end(), stopcond);
|
||||
|
||||
} else {
|
||||
|
||||
// Preparing the optimizer.
|
||||
size_t gridsize = std::sqrt(max_tries); // 2D grid has gridsize^2 calls
|
||||
opt::Optimizer<opt::AlgBruteForce> solver(opt::StopCriteria{}
|
||||
.max_iterations(max_tries)
|
||||
.stop_condition(stopcond),
|
||||
.max_iterations(max_tries)
|
||||
.stop_condition(stopcond),
|
||||
gridsize);
|
||||
|
||||
// We are searching rotations around only two axes x, y. Thus the
|
||||
|
@ -300,23 +414,14 @@ Vec2d find_best_rotation(const SLAPrintObject & po,
|
|||
[&mesh, &statusfn] (const XYRotation &rot)
|
||||
{
|
||||
statusfn();
|
||||
return get_model_supportedness(mesh, to_transform3d(rot));
|
||||
return get_supportedness_score(mesh, to_transform3f(rot));
|
||||
}, opt::initvals({0., 0.}), bounds);
|
||||
|
||||
// Save the result and fck off
|
||||
// Save the result
|
||||
rot = result.optimum;
|
||||
}
|
||||
|
||||
return {rot[0], rot[1]};
|
||||
}
|
||||
|
||||
double get_model_supportedness(const SLAPrintObject &po, const Transform3d &tr)
|
||||
{
|
||||
TriangleMesh mesh = po.model_object()->raw_mesh();
|
||||
mesh.require_shared_vertices();
|
||||
|
||||
return is_on_floor(po) ? get_model_supportedness_onfloor(mesh, tr) :
|
||||
get_model_supportedness(mesh, tr);
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::sla
|
||||
|
|
|
@ -8,10 +8,39 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
class ModelObject;
|
||||
class SLAPrintObject;
|
||||
class TriangleMesh;
|
||||
class DynamicPrintConfig;
|
||||
|
||||
namespace sla {
|
||||
|
||||
using RotOptimizeStatusCB = std::function<bool(int)>;
|
||||
|
||||
class RotOptimizeParams {
|
||||
float m_accuracy = 1.;
|
||||
const DynamicPrintConfig *m_print_config = nullptr;
|
||||
RotOptimizeStatusCB m_statuscb = [](int) { return true; };
|
||||
|
||||
public:
|
||||
|
||||
RotOptimizeParams &accuracy(float a) { m_accuracy = a; return *this; }
|
||||
RotOptimizeParams &print_config(const DynamicPrintConfig *c)
|
||||
{
|
||||
m_print_config = c;
|
||||
return *this;
|
||||
}
|
||||
RotOptimizeParams &statucb(RotOptimizeStatusCB cb)
|
||||
{
|
||||
m_statuscb = std::move(cb);
|
||||
return *this;
|
||||
}
|
||||
|
||||
float accuracy() const { return m_accuracy; }
|
||||
const DynamicPrintConfig * print_config() const { return m_print_config; }
|
||||
const RotOptimizeStatusCB &statuscb() const { return m_statuscb; }
|
||||
};
|
||||
|
||||
/**
|
||||
* The function should find the best rotation for SLA upside down printing.
|
||||
*
|
||||
|
@ -19,23 +48,22 @@ namespace sla {
|
|||
* @param accuracy The optimization accuracy from 0.0f to 1.0f. Currently,
|
||||
* the nlopt genetic optimizer is used and the number of iterations is
|
||||
* accuracy * 100000. This can change in the future.
|
||||
* @param statuscb A status indicator callback called with the unsigned
|
||||
* @param statuscb A status indicator callback called with the int
|
||||
* argument spanning from 0 to 100. May not reach 100 if the optimization finds
|
||||
* an optimum before max iterations are reached.
|
||||
* @param stopcond A function that if returns true, the search process will be
|
||||
* terminated and the best solution found will be returned.
|
||||
* an optimum before max iterations are reached. It should return a boolean
|
||||
* signaling if the operation may continue (true) or not (false). A status
|
||||
* value lower than 0 shall not update the status but still return a valid
|
||||
* continuation indicator.
|
||||
*
|
||||
* @return Returns the rotations around each axis (x, y, z)
|
||||
*/
|
||||
Vec2d find_best_rotation(
|
||||
const SLAPrintObject& modelobj,
|
||||
float accuracy = 1.0f,
|
||||
std::function<void(unsigned)> statuscb = [] (unsigned) {},
|
||||
std::function<bool()> stopcond = [] () { return false; }
|
||||
);
|
||||
Vec2d find_best_misalignment_rotation(const ModelObject &modelobj,
|
||||
const RotOptimizeParams & = {});
|
||||
|
||||
double get_model_supportedness(const SLAPrintObject &mesh,
|
||||
const Transform3d & tr);
|
||||
Vec2d find_least_supports_rotation(const ModelObject &modelobj,
|
||||
const RotOptimizeParams & = {});
|
||||
|
||||
double find_Z_fit_to_bed_rotation(const ModelObject &mo, const BoundingBox &bed);
|
||||
|
||||
} // namespace sla
|
||||
} // namespace Slic3r
|
||||
|
|
|
@ -12,11 +12,9 @@
|
|||
#include "ClipperUtils.hpp"
|
||||
#include "Tesselate.hpp"
|
||||
#include "ExPolygonCollection.hpp"
|
||||
#include "MinAreaBoundingBox.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include "libnest2d/backends/clipper/geometries.hpp"
|
||||
#include "libnest2d/utils/rotcalipers.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
|
||||
|
@ -181,9 +179,8 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
|
|||
}
|
||||
}
|
||||
if (! top.islands_below.empty()) {
|
||||
Polygons top_polygons = to_polygons(*top.polygon);
|
||||
Polygons bottom_polygons = top.polygons_below();
|
||||
top.overhangs = diff_ex(top_polygons, bottom_polygons);
|
||||
top.overhangs = diff_ex(*top.polygon, bottom_polygons);
|
||||
if (! top.overhangs.empty()) {
|
||||
|
||||
// Produce 2 bands around the island, a safe band for dangling overhangs
|
||||
|
@ -193,7 +190,7 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
|
|||
auto overh_mask = offset(bottom_polygons, slope_offset, ClipperLib::jtSquare);
|
||||
|
||||
// Absolutely hopeless overhangs are those outside the unsafe band
|
||||
top.overhangs = diff_ex(top_polygons, overh_mask);
|
||||
top.overhangs = diff_ex(*top.polygon, overh_mask);
|
||||
|
||||
// Now cut out the supported core from the safe band
|
||||
// and cut the safe band from the unsafe band to get distinct
|
||||
|
@ -201,8 +198,8 @@ static std::vector<SupportPointGenerator::MyLayer> make_layers(
|
|||
overh_mask = diff(overh_mask, dangl_mask);
|
||||
dangl_mask = diff(dangl_mask, bottom_polygons);
|
||||
|
||||
top.dangling_areas = intersection_ex(top_polygons, dangl_mask);
|
||||
top.overhangs_slopes = intersection_ex(top_polygons, overh_mask);
|
||||
top.dangling_areas = intersection_ex(*top.polygon, dangl_mask);
|
||||
top.overhangs_slopes = intersection_ex(*top.polygon, overh_mask);
|
||||
|
||||
top.overhangs_area = 0.f;
|
||||
std::vector<std::pair<ExPolygon*, float>> expolys_with_areas;
|
||||
|
@ -400,7 +397,7 @@ std::vector<Vec2f> sample_expolygon(const ExPolygons &expolys, float samples_per
|
|||
void sample_expolygon_boundary(const ExPolygon & expoly,
|
||||
float samples_per_mm,
|
||||
std::vector<Vec2f> &out,
|
||||
std::mt19937 & rng)
|
||||
std::mt19937 & /*rng*/)
|
||||
{
|
||||
double point_stepping_scaled = scale_(1.f) / samples_per_mm;
|
||||
for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) {
|
||||
|
@ -553,9 +550,8 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure
|
|||
// auto bb = get_extents(islands);
|
||||
|
||||
if (flags & icfIsNew) {
|
||||
auto chull_ex = ExPolygonCollection{islands}.convex_hull();
|
||||
auto chull = Slic3rMultiPoint_to_ClipperPath(chull_ex);
|
||||
auto rotbox = libnest2d::minAreaBoundingBox(chull);
|
||||
auto chull = ExPolygonCollection{islands}.convex_hull();
|
||||
auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex};
|
||||
Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())};
|
||||
|
||||
if (bbdim.x() > bbdim.y()) std::swap(bbdim.x(), bbdim.y());
|
||||
|
|
|
@ -90,7 +90,7 @@ public:
|
|||
float overlap_area(const Structure &rhs) const {
|
||||
double out = 0.;
|
||||
if (this->bbox.overlap(rhs.bbox)) {
|
||||
Polygons polys = intersection(to_polygons(*this->polygon), to_polygons(*rhs.polygon), false);
|
||||
Polygons polys = intersection(*this->polygon, *rhs.polygon, false);
|
||||
for (const Polygon &poly : polys)
|
||||
out += poly.area();
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "Point.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include "Zipper.hpp"
|
||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
@ -390,16 +389,25 @@ public:
|
|||
virtual void apply(const SLAPrinterConfig &cfg) = 0;
|
||||
|
||||
// Fn have to be thread safe: void(sla::RasterBase& raster, size_t lyrid);
|
||||
template<class Fn> void draw_layers(size_t layer_num, Fn &&drawfn)
|
||||
template<class Fn, class CancelFn, class EP = ExecutionTBB>
|
||||
void draw_layers(
|
||||
size_t layer_num,
|
||||
Fn && drawfn,
|
||||
CancelFn cancelfn = []() { return false; },
|
||||
const EP & ep = {})
|
||||
{
|
||||
m_layers.resize(layer_num);
|
||||
sla::ccr::for_each(size_t(0), m_layers.size(),
|
||||
[this, &drawfn] (size_t idx) {
|
||||
sla::EncodedRaster& enc = m_layers[idx];
|
||||
auto rst = create_raster();
|
||||
drawfn(*rst, idx);
|
||||
enc = rst->encode(get_encoder());
|
||||
});
|
||||
execution::for_each(
|
||||
ep, size_t(0), m_layers.size(),
|
||||
[this, &drawfn, &cancelfn](size_t idx) {
|
||||
if (cancelfn()) return;
|
||||
|
||||
sla::EncodedRaster &enc = m_layers[idx];
|
||||
auto rst = create_raster();
|
||||
drawfn(*rst, idx);
|
||||
enc = rst->encode(get_encoder());
|
||||
},
|
||||
execution::max_concurrency(ep));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -474,7 +482,7 @@ public:
|
|||
// The collection of slice records for the current level.
|
||||
std::vector<std::reference_wrapper<const SliceRecord>> m_slices;
|
||||
|
||||
std::vector<ClipperLib::Polygon> m_transformed_slices;
|
||||
ExPolygons m_transformed_slices;
|
||||
|
||||
template<class Container> void transformed_slices(Container&& c)
|
||||
{
|
||||
|
@ -498,7 +506,7 @@ public:
|
|||
|
||||
auto slices() const -> const decltype (m_slices)& { return m_slices; }
|
||||
|
||||
const std::vector<ClipperLib::Polygon> & transformed_slices() const {
|
||||
const ExPolygons & transformed_slices() const {
|
||||
return m_transformed_slices;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,9 +16,6 @@
|
|||
|
||||
#include <libslic3r/ClipperUtils.hpp>
|
||||
|
||||
// For geometry algorithms with native Clipper types (no copies and conversions)
|
||||
#include <libnest2d/backends/clipper/geometries.hpp>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#include "I18N.hpp"
|
||||
|
@ -717,55 +714,49 @@ void SLAPrint::Steps::slice_supports(SLAPrintObject &po) {
|
|||
report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
|
||||
}
|
||||
|
||||
using ClipperPoint = ClipperLib::IntPoint;
|
||||
using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d
|
||||
using ClipperPolygons = std::vector<ClipperPolygon>;
|
||||
//static ClipperPolygons polyunion(const ClipperPolygons &subjects)
|
||||
//{
|
||||
// ClipperLib::Clipper clipper;
|
||||
|
||||
static ClipperPolygons polyunion(const ClipperPolygons &subjects)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
// bool closed = true;
|
||||
|
||||
bool closed = true;
|
||||
// for(auto& path : subjects) {
|
||||
// clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
// clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
// }
|
||||
|
||||
for(auto& path : subjects) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
// auto mode = ClipperLib::pftPositive;
|
||||
|
||||
auto mode = ClipperLib::pftPositive;
|
||||
// return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
|
||||
//}
|
||||
|
||||
return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
|
||||
}
|
||||
//static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips)
|
||||
//{
|
||||
// ClipperLib::Clipper clipper;
|
||||
|
||||
static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
// bool closed = true;
|
||||
|
||||
bool closed = true;
|
||||
// for(auto& path : subjects) {
|
||||
// clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
// clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
// }
|
||||
|
||||
for(auto& path : subjects) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
// for(auto& path : clips) {
|
||||
// clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
|
||||
// clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
|
||||
// }
|
||||
|
||||
for(auto& path : clips) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
|
||||
}
|
||||
// auto mode = ClipperLib::pftPositive;
|
||||
|
||||
auto mode = ClipperLib::pftPositive;
|
||||
|
||||
return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
|
||||
}
|
||||
// return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
|
||||
//}
|
||||
|
||||
// get polygons for all instances in the object
|
||||
static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
|
||||
static ExPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
|
||||
{
|
||||
namespace sl = libnest2d::sl;
|
||||
|
||||
if (!record.print_obj()) return {};
|
||||
|
||||
ClipperPolygons polygons;
|
||||
ExPolygons polygons;
|
||||
auto &input_polygons = record.get_slice(o);
|
||||
auto &instances = record.print_obj()->instances();
|
||||
bool is_lefthanded = record.print_obj()->is_left_handed();
|
||||
|
@ -776,43 +767,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
|
|||
|
||||
for (size_t i = 0; i < instances.size(); ++i)
|
||||
{
|
||||
ClipperPolygon poly;
|
||||
ExPolygon poly;
|
||||
|
||||
// We need to reverse if is_lefthanded is true but
|
||||
bool needreverse = is_lefthanded;
|
||||
|
||||
// should be a move
|
||||
poly.Contour.reserve(polygon.contour.size() + 1);
|
||||
poly.contour.points.reserve(polygon.contour.size() + 1);
|
||||
|
||||
auto& cntr = polygon.contour.points;
|
||||
if(needreverse)
|
||||
for(auto it = cntr.rbegin(); it != cntr.rend(); ++it)
|
||||
poly.Contour.emplace_back(it->x(), it->y());
|
||||
poly.contour.points.emplace_back(it->x(), it->y());
|
||||
else
|
||||
for(auto& p : cntr)
|
||||
poly.Contour.emplace_back(p.x(), p.y());
|
||||
poly.contour.points.emplace_back(p.x(), p.y());
|
||||
|
||||
for(auto& h : polygon.holes) {
|
||||
poly.Holes.emplace_back();
|
||||
auto& hole = poly.Holes.back();
|
||||
hole.reserve(h.points.size() + 1);
|
||||
poly.holes.emplace_back();
|
||||
auto& hole = poly.holes.back();
|
||||
hole.points.reserve(h.points.size() + 1);
|
||||
|
||||
if(needreverse)
|
||||
for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
|
||||
hole.emplace_back(it->x(), it->y());
|
||||
hole.points.emplace_back(it->x(), it->y());
|
||||
else
|
||||
for(auto& p : h.points)
|
||||
hole.emplace_back(p.x(), p.y());
|
||||
hole.points.emplace_back(p.x(), p.y());
|
||||
}
|
||||
|
||||
if(is_lefthanded) {
|
||||
for(auto& p : poly.Contour) p.X = -p.X;
|
||||
for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X;
|
||||
for(auto& p : poly.contour) p.x() = -p.x();
|
||||
for(auto& h : poly.holes) for(auto& p : h) p.x() = -p.x();
|
||||
}
|
||||
|
||||
sl::rotate(poly, double(instances[i].rotation));
|
||||
sl::translate(poly, ClipperPoint{instances[i].shift.x(),
|
||||
instances[i].shift.y()});
|
||||
poly.rotate(double(instances[i].rotation));
|
||||
poly.translate(Point{instances[i].shift.x(), instances[i].shift.y()});
|
||||
|
||||
polygons.emplace_back(std::move(poly));
|
||||
}
|
||||
|
@ -878,9 +868,6 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
|
||||
print_statistics.clear();
|
||||
|
||||
// libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
|
||||
auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); };
|
||||
|
||||
const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
|
||||
const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0;
|
||||
const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0;
|
||||
|
@ -913,7 +900,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
// Going to parallel:
|
||||
auto printlayerfn = [this,
|
||||
// functions and read only vars
|
||||
areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
|
||||
area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
|
||||
|
||||
// write vars
|
||||
&mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
|
||||
|
@ -931,8 +918,8 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
|
||||
// Calculation of the consumed material
|
||||
|
||||
ClipperPolygons model_polygons;
|
||||
ClipperPolygons supports_polygons;
|
||||
ExPolygons model_polygons;
|
||||
ExPolygons supports_polygons;
|
||||
|
||||
size_t c = std::accumulate(layer.slices().begin(),
|
||||
layer.slices().end(),
|
||||
|
@ -954,44 +941,44 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
|
|||
|
||||
for(const SliceRecord& record : layer.slices()) {
|
||||
|
||||
ClipperPolygons modelslices = get_all_polygons(record, soModel);
|
||||
for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
|
||||
ExPolygons modelslices = get_all_polygons(record, soModel);
|
||||
for(ExPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
|
||||
|
||||
ClipperPolygons supportslices = get_all_polygons(record, soSupport);
|
||||
for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
|
||||
ExPolygons supportslices = get_all_polygons(record, soSupport);
|
||||
for(ExPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
|
||||
|
||||
}
|
||||
|
||||
model_polygons = polyunion(model_polygons);
|
||||
model_polygons = union_ex(model_polygons);
|
||||
double layer_model_area = 0;
|
||||
for (const ClipperPolygon& polygon : model_polygons)
|
||||
layer_model_area += areafn(polygon);
|
||||
for (const ExPolygon& polygon : model_polygons)
|
||||
layer_model_area += area(polygon);
|
||||
|
||||
if (layer_model_area < 0 || layer_model_area > 0) {
|
||||
Lock lck(mutex); models_volume += layer_model_area * l_height;
|
||||
}
|
||||
|
||||
if(!supports_polygons.empty()) {
|
||||
if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons);
|
||||
else supports_polygons = polydiff(supports_polygons, model_polygons);
|
||||
if(model_polygons.empty()) supports_polygons = union_ex(supports_polygons);
|
||||
else supports_polygons = diff_ex(supports_polygons, model_polygons);
|
||||
// allegedly, union of subject is done withing the diff according to the pftPositive polyFillType
|
||||
}
|
||||
|
||||
double layer_support_area = 0;
|
||||
for (const ClipperPolygon& polygon : supports_polygons)
|
||||
layer_support_area += areafn(polygon);
|
||||
for (const ExPolygon& polygon : supports_polygons)
|
||||
layer_support_area += area(polygon);
|
||||
|
||||
if (layer_support_area < 0 || layer_support_area > 0) {
|
||||
Lock lck(mutex); supports_volume += layer_support_area * l_height;
|
||||
}
|
||||
|
||||
// Here we can save the expensively calculated polygons for printing
|
||||
ClipperPolygons trslices;
|
||||
ExPolygons trslices;
|
||||
trslices.reserve(model_polygons.size() + supports_polygons.size());
|
||||
for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
|
||||
for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
|
||||
for(ExPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
|
||||
for(ExPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
|
||||
|
||||
layer.transformed_slices(polyunion(trslices));
|
||||
layer.transformed_slices(union_ex(trslices));
|
||||
|
||||
// Calculation of the slow and fast layers to the future controlling those values on FW
|
||||
|
||||
|
@ -1074,7 +1061,7 @@ void SLAPrint::Steps::rasterize()
|
|||
PrintLayer& printlayer = m_print->m_printer_input[idx];
|
||||
if(canceled()) return;
|
||||
|
||||
for (const ClipperLib::Polygon& poly : printlayer.transformed_slices())
|
||||
for (const ExPolygon& poly : printlayer.transformed_slices())
|
||||
raster.draw(poly);
|
||||
|
||||
// Status indication guarded with the spinlock
|
||||
|
@ -1093,7 +1080,8 @@ void SLAPrint::Steps::rasterize()
|
|||
if(canceled()) return;
|
||||
|
||||
// Print all the layers in parallel
|
||||
m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn);
|
||||
m_print->m_printer->draw_layers(m_print->m_printer_input.size(), lvlfn,
|
||||
[this]() { return canceled(); }, ex_tbb);
|
||||
}
|
||||
|
||||
std::string SLAPrint::Steps::label(SLAPrintObjectStep step)
|
||||
|
|
|
@ -273,8 +273,8 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo
|
|||
std::ostringstream d;
|
||||
d << "M ";
|
||||
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
|
||||
d << to_svg_x(scale * p->X - origin(0)) << " ";
|
||||
d << to_svg_y(scale * p->Y - origin(1)) << " ";
|
||||
d << to_svg_x(scale * p->x() - origin(0)) << " ";
|
||||
d << to_svg_y(scale * p->y() - origin(1)) << " ";
|
||||
}
|
||||
if (closed) d << "z";
|
||||
return d.str();
|
||||
|
|
|
@ -64,9 +64,9 @@ SlicingParameters SlicingParameters::create_from_config(
|
|||
coordf_t object_height,
|
||||
const std::vector<unsigned int> &object_extruders)
|
||||
{
|
||||
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ?
|
||||
object_config.layer_height.value :
|
||||
object_config.first_layer_height.get_abs_value(object_config.layer_height.value);
|
||||
assert(! print_config.first_layer_height.percent);
|
||||
coordf_t first_layer_height = (print_config.first_layer_height.value <= 0) ?
|
||||
object_config.layer_height.value : print_config.first_layer_height.value;
|
||||
// If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0,
|
||||
// print_config.nozzle_diameter.get_at(size_t(-1)) returns the 0th nozzle diameter,
|
||||
// which is consistent with the requirement that if support_material_extruder == 0 resp. support_material_interface_extruder == 0,
|
||||
|
|
|
@ -672,6 +672,7 @@ struct SupportGridParams {
|
|||
grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()),
|
||||
support_angle(Geometry::deg2rad(object_config.support_material_angle.value)),
|
||||
extrusion_width(support_material_flow.spacing()),
|
||||
support_material_closing_radius(object_config.support_material_closing_radius.value),
|
||||
expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)),
|
||||
expansion_to_propagate(-3) {}
|
||||
|
||||
|
@ -679,6 +680,7 @@ struct SupportGridParams {
|
|||
double grid_resolution;
|
||||
double support_angle;
|
||||
double extrusion_width;
|
||||
double support_material_closing_radius;
|
||||
coord_t expansion_to_slice;
|
||||
coord_t expansion_to_propagate;
|
||||
};
|
||||
|
@ -694,7 +696,9 @@ public:
|
|||
const SupportGridParams ¶ms) :
|
||||
m_style(params.style),
|
||||
m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons),
|
||||
m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle)
|
||||
m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle),
|
||||
m_extrusion_width(params.extrusion_width),
|
||||
m_support_material_closing_radius(params.support_material_closing_radius)
|
||||
{
|
||||
switch (m_style) {
|
||||
case smsGrid:
|
||||
|
@ -811,7 +815,7 @@ public:
|
|||
// Expanding, thus m_support_polygons are all inside islands.
|
||||
union_ex(*m_support_polygons) :
|
||||
// Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands.
|
||||
intersection_ex(*m_support_polygons, to_polygons(islands)));
|
||||
intersection_ex(*m_support_polygons, islands));
|
||||
|
||||
std::vector<std::pair<Point,bool>> samples_inside;
|
||||
for (ExPolygon &island : islands) {
|
||||
|
@ -878,9 +882,10 @@ public:
|
|||
return out;
|
||||
}
|
||||
case smsSnug:
|
||||
// Just close the gaps.
|
||||
float thr = scaled<float>(0.5);
|
||||
return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr);
|
||||
// Merge the support polygons by applying morphological closing and inwards smoothing.
|
||||
auto closing_distance = scaled<float>(m_support_material_closing_radius);
|
||||
auto smoothing_distance = scaled<float>(m_extrusion_width);
|
||||
return smooth_outward(offset(offset_ex(*m_support_polygons, closing_distance), - closing_distance), smoothing_distance);
|
||||
}
|
||||
assert(false);
|
||||
return Polygons();
|
||||
|
@ -927,7 +932,7 @@ public:
|
|||
}
|
||||
|
||||
// Deserialization constructor
|
||||
bool deserialize_(const std::string &path, int which = -1)
|
||||
bool deserialize_(const std::string &path, int which = -1)
|
||||
{
|
||||
FILE *file = ::fopen(path.c_str(), "rb");
|
||||
if (file == nullptr)
|
||||
|
@ -956,7 +961,7 @@ public:
|
|||
poly.points.emplace_back(Point(x * scale, y * scale));
|
||||
}
|
||||
if (which == -1 || which == i)
|
||||
m_support_polygons_deserialized.emplace_back(std::move(poly));
|
||||
m_support_polygons_deserialized.emplace_back(std::move(poly));
|
||||
printf("Polygon %d, area: %lf\n", i, area(poly.points));
|
||||
}
|
||||
::fread(&n_polygons, 4, 1, file);
|
||||
|
@ -979,14 +984,14 @@ public:
|
|||
m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false);
|
||||
//m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false));
|
||||
|
||||
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
|
||||
coord_t grid_resolution = coord_t(scale_(m_support_spacing));
|
||||
BoundingBox bbox = get_extents(*m_support_polygons);
|
||||
// Create an EdgeGrid, initialize it with projection, initialize signed distance field.
|
||||
coord_t grid_resolution = coord_t(scale_(m_support_spacing));
|
||||
BoundingBox bbox = get_extents(*m_support_polygons);
|
||||
bbox.offset(20);
|
||||
bbox.align_to_grid(grid_resolution);
|
||||
m_grid.set_bbox(bbox);
|
||||
m_grid.create(*m_support_polygons, grid_resolution);
|
||||
m_grid.calculate_sdf();
|
||||
bbox.align_to_grid(grid_resolution);
|
||||
m_grid.set_bbox(bbox);
|
||||
m_grid.create(*m_support_polygons, grid_resolution);
|
||||
m_grid.calculate_sdf();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1128,6 +1133,9 @@ private:
|
|||
coordf_t m_support_angle;
|
||||
// X spacing of the support lines parallel with the Y axis.
|
||||
coordf_t m_support_spacing;
|
||||
coordf_t m_extrusion_width;
|
||||
// For snug supports: Morphological closing of support areas.
|
||||
coordf_t m_support_material_closing_radius;
|
||||
|
||||
#ifdef SUPPORT_USE_AGG_RASTERIZER
|
||||
Vec2i m_grid_size;
|
||||
|
@ -1277,7 +1285,7 @@ namespace SupportMaterialInternal {
|
|||
// Is the straight perimeter segment supported at both sides?
|
||||
Point pts[2] = { polyline.first_point(), polyline.last_point() };
|
||||
bool supported[2] = { false, false };
|
||||
for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i)
|
||||
for (size_t i = 0; i < lower_layer.lslices.size() && ! (supported[0] && supported[1]); ++ i)
|
||||
for (int j = 0; j < 2; ++ j)
|
||||
if (! supported[j] && lower_layer.lslices_bboxes[i].contains(pts[j]) && lower_layer.lslices[i].contains(pts[j]))
|
||||
supported[j] = true;
|
||||
|
@ -1429,7 +1437,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
0.5f * fw);
|
||||
// Overhang polygons for this layer and region.
|
||||
Polygons diff_polygons;
|
||||
Polygons layerm_polygons = to_polygons(layerm->slices);
|
||||
Polygons layerm_polygons = to_polygons(layerm->slices.surfaces);
|
||||
if (lower_layer_offset == 0.f) {
|
||||
// Support everything.
|
||||
diff_polygons = diff(layerm_polygons, lower_layer_polygons);
|
||||
|
@ -1461,13 +1469,13 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
diff_polygons = diff(diff_polygons, annotations.buildplate_covered[layer_id]);
|
||||
}
|
||||
if (! diff_polygons.empty()) {
|
||||
// Offset the support regions back to a full overhang, restrict them to the full overhang.
|
||||
// This is done to increase size of the supporting columns below, as they are calculated by
|
||||
// propagating these contact surfaces downwards.
|
||||
diff_polygons = diff(
|
||||
intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
|
||||
lower_layer_polygons);
|
||||
}
|
||||
// Offset the support regions back to a full overhang, restrict them to the full overhang.
|
||||
// This is done to increase size of the supporting columns below, as they are calculated by
|
||||
// propagating these contact surfaces downwards.
|
||||
diff_polygons = diff(
|
||||
intersection(offset(diff_polygons, lower_layer_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS), layerm_polygons),
|
||||
lower_layer_polygons);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1481,7 +1489,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
// Subtracting them as they are may leave unwanted narrow
|
||||
// residues of diff_polygons that would then be supported.
|
||||
diff_polygons = diff(diff_polygons,
|
||||
offset(union_(to_polygons(std::move(annotations.blockers_layers[layer_id]))), float(1000.*SCALED_EPSILON)));
|
||||
offset(union_(annotations.blockers_layers[layer_id]), float(1000.*SCALED_EPSILON)));
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG
|
||||
|
@ -1530,7 +1538,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
slices_margin.offset = slices_margin_offset;
|
||||
slices_margin.polygons = (slices_margin_offset == 0.f) ?
|
||||
lower_layer_polygons :
|
||||
offset2(to_polygons(lower_layer.lslices), - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
offset2(lower_layer.lslices, - no_interface_offset * 0.5f, slices_margin_offset + no_interface_offset * 0.5f, SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
if (buildplate_only && ! annotations.buildplate_covered[layer_id].empty()) {
|
||||
if (has_enforcer)
|
||||
// Make a backup of trimming polygons before enforcing "on build plate only".
|
||||
|
@ -1561,9 +1569,9 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs(
|
|||
if (has_enforcer) {
|
||||
// Enforce supports (as if with 90 degrees of slope) for the regions covered by the enforcer meshes.
|
||||
#ifdef SLIC3R_DEBUG
|
||||
ExPolygons enforcers_united = union_ex(to_polygons(annotations.enforcers_layers[layer_id]), false);
|
||||
ExPolygons enforcers_united = union_ex(annotations.enforcers_layers[layer_id]);
|
||||
#endif // SLIC3R_DEBUG
|
||||
enforcer_polygons = diff(intersection(to_polygons(layer.lslices), to_polygons(std::move(annotations.enforcers_layers[layer_id]))),
|
||||
enforcer_polygons = diff(intersection(layer.lslices, annotations.enforcers_layers[layer_id]),
|
||||
// Inflate just a tiny bit to avoid intersection of the overhang areas with the object.
|
||||
offset(lower_layer_polygons, 0.05f * fw, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
#ifdef SLIC3R_DEBUG
|
||||
|
@ -1607,7 +1615,7 @@ static inline std::pair<PrintObjectSupportMaterial::MyLayer*, PrintObjectSupport
|
|||
height = layer.lower_layer->height;
|
||||
bottom_z = (layer_id == 1) ? slicing_params.object_print_z_min : layer.lower_layer->lower_layer->print_z;
|
||||
} else {
|
||||
print_z = layer.bottom_z() - slicing_params.gap_object_support;
|
||||
print_z = layer.bottom_z() - slicing_params.gap_support_object;
|
||||
bottom_z = print_z;
|
||||
height = 0.;
|
||||
// Ignore this contact area if it's too low.
|
||||
|
@ -2764,8 +2772,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object(
|
|||
break;
|
||||
some_region_overlaps = true;
|
||||
polygons_append(polygons_trimming,
|
||||
offset(to_expolygons(region->fill_surfaces.filter_by_type(stBottomBridge)),
|
||||
gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
offset(region->fill_surfaces.filter_by_type(stBottomBridge), gap_xy_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS));
|
||||
if (region->region()->config().overhangs.value)
|
||||
// Add bridging perimeters.
|
||||
SupportMaterialInternal::collect_bridging_perimeter_areas(region->perimeters, gap_xy_scaled, polygons_trimming);
|
||||
|
@ -2898,9 +2905,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
|
|||
//FIXME misusing contact_polygons for support columns.
|
||||
new_layer.contact_polygons = std::make_unique<Polygons>(columns);
|
||||
}
|
||||
} else if (columns_base != nullptr) {
|
||||
} else {
|
||||
if (columns_base != nullptr) {
|
||||
// Expand the bases of the support columns in the 1st layer.
|
||||
{
|
||||
Polygons &raft = columns_base->polygons;
|
||||
Polygons trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_support_params.gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
if (inflate_factor_1st_layer > SCALED_EPSILON) {
|
||||
|
@ -2911,11 +2918,12 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
|
|||
raft = diff(offset(raft, step), trimming);
|
||||
} else
|
||||
raft = diff(raft, trimming);
|
||||
if (contacts != nullptr)
|
||||
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
|
||||
}
|
||||
if (contacts != nullptr)
|
||||
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
|
||||
if (! brim.empty()) {
|
||||
columns_base->polygons = diff(columns_base->polygons, brim);
|
||||
if (columns_base)
|
||||
columns_base->polygons = diff(columns_base->polygons, brim);
|
||||
if (contacts)
|
||||
contacts->polygons = diff(contacts->polygons, brim);
|
||||
if (interfaces)
|
||||
|
@ -3084,8 +3092,8 @@ static inline void fill_expolygon_generate_paths(
|
|||
Polylines polylines;
|
||||
try {
|
||||
polylines = filler->fill_surface(&surface, fill_params);
|
||||
} catch (InfillFailedException &) {
|
||||
}
|
||||
} catch (InfillFailedException &) {
|
||||
}
|
||||
extrusion_entities_append_paths(
|
||||
dst,
|
||||
std::move(polylines),
|
||||
|
@ -3166,7 +3174,7 @@ static inline void fill_expolygons_with_sheath_generate_paths(
|
|||
extrusion_entities_append_paths(out, polylines, erSupportMaterial, flow.mm3_per_mm(), flow.width(), flow.height());
|
||||
// Fill in the rest.
|
||||
fill_expolygons_generate_paths(out, offset_ex(expoly, float(-0.4 * spacing)), filler, fill_params, density, role, flow);
|
||||
if (no_sort)
|
||||
if (no_sort && ! eec->empty())
|
||||
dst.emplace_back(eec.release());
|
||||
}
|
||||
}
|
||||
|
@ -3174,8 +3182,13 @@ static inline void fill_expolygons_with_sheath_generate_paths(
|
|||
// Support layers, partially processed.
|
||||
struct MyLayerExtruded
|
||||
{
|
||||
MyLayerExtruded() : layer(nullptr), m_polygons_to_extrude(nullptr) {}
|
||||
~MyLayerExtruded() { delete m_polygons_to_extrude; m_polygons_to_extrude = nullptr; }
|
||||
MyLayerExtruded& operator=(MyLayerExtruded &&rhs) {
|
||||
this->layer = rhs.layer;
|
||||
this->extrusions = std::move(rhs.extrusions);
|
||||
this->m_polygons_to_extrude = std::move(rhs.m_polygons_to_extrude);
|
||||
rhs.layer = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return layer == nullptr || layer->polygons.empty();
|
||||
|
@ -3183,7 +3196,7 @@ struct MyLayerExtruded
|
|||
|
||||
void set_polygons_to_extrude(Polygons &&polygons) {
|
||||
if (m_polygons_to_extrude == nullptr)
|
||||
m_polygons_to_extrude = new Polygons(std::move(polygons));
|
||||
m_polygons_to_extrude = std::make_unique<Polygons>(std::move(polygons));
|
||||
else
|
||||
*m_polygons_to_extrude = std::move(polygons);
|
||||
}
|
||||
|
@ -3204,12 +3217,11 @@ struct MyLayerExtruded
|
|||
if (m_polygons_to_extrude == nullptr) {
|
||||
// This layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
|
||||
assert(this->extrusions.empty());
|
||||
m_polygons_to_extrude = new Polygons(this->layer->polygons);
|
||||
m_polygons_to_extrude = std::make_unique<Polygons>(this->layer->polygons);
|
||||
}
|
||||
Slic3r::polygons_append(*m_polygons_to_extrude, std::move(*other.m_polygons_to_extrude));
|
||||
*m_polygons_to_extrude = union_(*m_polygons_to_extrude, true);
|
||||
delete other.m_polygons_to_extrude;
|
||||
other.m_polygons_to_extrude = nullptr;
|
||||
other.m_polygons_to_extrude.reset();
|
||||
} else if (m_polygons_to_extrude != nullptr) {
|
||||
assert(other.m_polygons_to_extrude == nullptr);
|
||||
// The other layer has no extrusions generated yet, if it has no m_polygons_to_extrude (its area to extrude was not reduced yet).
|
||||
|
@ -3232,12 +3244,14 @@ struct MyLayerExtruded
|
|||
}
|
||||
|
||||
// The source layer. It carries the height and extrusion type (bridging / non bridging, extrusion height).
|
||||
PrintObjectSupportMaterial::MyLayer *layer;
|
||||
PrintObjectSupportMaterial::MyLayer *layer { nullptr };
|
||||
// Collect extrusions. They will be exported sorted by the bottom height.
|
||||
ExtrusionEntitiesPtr extrusions;
|
||||
|
||||
private:
|
||||
// In case the extrusions are non-empty, m_polygons_to_extrude may contain the rest areas yet to be filled by additional support.
|
||||
// This is useful mainly for the loop interfaces, which are generated before the zig-zag infills.
|
||||
Polygons *m_polygons_to_extrude;
|
||||
std::unique_ptr<Polygons> m_polygons_to_extrude;
|
||||
};
|
||||
|
||||
typedef std::vector<MyLayerExtruded*> MyLayerExtrudedPtrs;
|
||||
|
@ -3763,7 +3777,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
// Prepare fillers.
|
||||
SupportMaterialPattern support_pattern = m_object_config->support_material_pattern;
|
||||
bool with_sheath = m_object_config->support_material_with_sheath;
|
||||
InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipRectilinear);
|
||||
InfillPattern infill_pattern = (support_pattern == smpHoneycomb ? ipHoneycomb : ipSupportBase);
|
||||
std::vector<float> angles;
|
||||
angles.push_back(base_angle);
|
||||
|
||||
|
@ -3900,7 +3914,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
std::stable_sort(this->nonempty.begin(), this->nonempty.end(), [](const LayerCacheItem &lc1, const LayerCacheItem &lc2) { return lc1.layer_extruded->layer->height > lc2.layer_extruded->layer->height; });
|
||||
}
|
||||
};
|
||||
std::vector<LayerCache> layer_caches(support_layers.size(), LayerCache());
|
||||
std::vector<LayerCache> layer_caches(support_layers.size());
|
||||
|
||||
|
||||
const auto fill_type_interface =
|
||||
|
@ -4152,6 +4166,27 @@ void PrintObjectSupportMaterial::generate_toolpaths(
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
#ifndef NDEBUG
|
||||
struct Test {
|
||||
static bool verify_nonempty(const ExtrusionEntityCollection *collection) {
|
||||
for (const ExtrusionEntity *ee : collection->entities) {
|
||||
if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath*>(ee))
|
||||
assert(! path->empty());
|
||||
else if (const ExtrusionMultiPath *multipath = dynamic_cast<const ExtrusionMultiPath*>(ee))
|
||||
assert(! multipath->empty());
|
||||
else if (const ExtrusionEntityCollection *eecol = dynamic_cast<const ExtrusionEntityCollection*>(ee)) {
|
||||
assert(! eecol->empty());
|
||||
return verify_nonempty(eecol);
|
||||
} else
|
||||
assert(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
for (const SupportLayer *support_layer : support_layers)
|
||||
assert(Test::verify_nonempty(&support_layer->support_fills));
|
||||
#endif // NDEBUG
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -90,7 +90,6 @@ public:
|
|||
return *this;
|
||||
}
|
||||
|
||||
operator Polygons() const { return this->expolygon; }
|
||||
double area() const { return this->expolygon.area(); }
|
||||
bool empty() const { return expolygon.empty(); }
|
||||
void clear() { expolygon.clear(); }
|
||||
|
@ -107,6 +106,16 @@ public:
|
|||
typedef std::vector<Surface> Surfaces;
|
||||
typedef std::vector<Surface*> SurfacesPtr;
|
||||
|
||||
inline Polygons to_polygons(const Surface &surface)
|
||||
{
|
||||
return to_polygons(surface.expolygon);
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(Surface &&surface)
|
||||
{
|
||||
return to_polygons(std::move(surface.expolygon));
|
||||
}
|
||||
|
||||
inline Polygons to_polygons(const Surfaces &src)
|
||||
{
|
||||
size_t num = 0;
|
||||
|
|
|
@ -6,18 +6,7 @@
|
|||
|
||||
namespace Slic3r {
|
||||
|
||||
SurfaceCollection::operator Polygons() const
|
||||
{
|
||||
return to_polygons(surfaces);
|
||||
}
|
||||
|
||||
SurfaceCollection::operator ExPolygons() const
|
||||
{
|
||||
return to_expolygons(surfaces);
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::simplify(double tolerance)
|
||||
void SurfaceCollection::simplify(double tolerance)
|
||||
{
|
||||
Surfaces ss;
|
||||
for (Surfaces::const_iterator it_s = this->surfaces.begin(); it_s != this->surfaces.end(); ++it_s) {
|
||||
|
@ -33,8 +22,7 @@ SurfaceCollection::simplify(double tolerance)
|
|||
}
|
||||
|
||||
/* group surfaces by common properties */
|
||||
void
|
||||
SurfaceCollection::group(std::vector<SurfacesPtr> *retval)
|
||||
void SurfaceCollection::group(std::vector<SurfacesPtr> *retval)
|
||||
{
|
||||
for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) {
|
||||
// find a group with the same properties
|
||||
|
@ -54,8 +42,7 @@ SurfaceCollection::group(std::vector<SurfacesPtr> *retval)
|
|||
}
|
||||
}
|
||||
|
||||
SurfacesPtr
|
||||
SurfaceCollection::filter_by_type(const SurfaceType type)
|
||||
SurfacesPtr SurfaceCollection::filter_by_type(const SurfaceType type)
|
||||
{
|
||||
SurfacesPtr ss;
|
||||
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
|
||||
|
@ -64,8 +51,7 @@ SurfaceCollection::filter_by_type(const SurfaceType type)
|
|||
return ss;
|
||||
}
|
||||
|
||||
SurfacesPtr
|
||||
SurfaceCollection::filter_by_types(const SurfaceType *types, int ntypes)
|
||||
SurfacesPtr SurfaceCollection::filter_by_types(const SurfaceType *types, int ntypes)
|
||||
{
|
||||
SurfacesPtr ss;
|
||||
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
|
||||
|
@ -79,8 +65,7 @@ SurfaceCollection::filter_by_types(const SurfaceType *types, int ntypes)
|
|||
return ss;
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
|
||||
void SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
|
||||
{
|
||||
for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) {
|
||||
if (surface->surface_type == type) {
|
||||
|
@ -90,8 +75,7 @@ SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::keep_type(const SurfaceType type)
|
||||
void SurfaceCollection::keep_type(const SurfaceType type)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < surfaces.size(); ++ i) {
|
||||
|
@ -105,8 +89,7 @@ SurfaceCollection::keep_type(const SurfaceType type)
|
|||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::keep_types(const SurfaceType *types, int ntypes)
|
||||
void SurfaceCollection::keep_types(const SurfaceType *types, int ntypes)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < surfaces.size(); ++ i) {
|
||||
|
@ -127,8 +110,7 @@ SurfaceCollection::keep_types(const SurfaceType *types, int ntypes)
|
|||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::remove_type(const SurfaceType type)
|
||||
void SurfaceCollection::remove_type(const SurfaceType type)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < surfaces.size(); ++ i) {
|
||||
|
@ -142,8 +124,7 @@ SurfaceCollection::remove_type(const SurfaceType type)
|
|||
surfaces.erase(surfaces.begin() + j, surfaces.end());
|
||||
}
|
||||
|
||||
void
|
||||
SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
|
||||
void SurfaceCollection::remove_types(const SurfaceType *types, int ntypes)
|
||||
{
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < surfaces.size(); ++ i) {
|
||||
|
|
|
@ -12,11 +12,10 @@ class SurfaceCollection
|
|||
public:
|
||||
Surfaces surfaces;
|
||||
|
||||
SurfaceCollection() {};
|
||||
SurfaceCollection(const Surfaces &surfaces) : surfaces(surfaces) {};
|
||||
SurfaceCollection() = default;
|
||||
SurfaceCollection(const Surfaces& surfaces) : surfaces(surfaces) {};
|
||||
SurfaceCollection(Surfaces &&surfaces) : surfaces(std::move(surfaces)) {};
|
||||
operator Polygons() const;
|
||||
operator ExPolygons() const;
|
||||
|
||||
void simplify(double tolerance);
|
||||
void group(std::vector<SurfacesPtr> *retval);
|
||||
template <class T> bool any_internal_contains(const T &item) const {
|
||||
|
|
|
@ -57,6 +57,12 @@
|
|||
#define ENABLE_GCODE_WINDOW (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable exporting lines M73 for remaining time to next printer stop to gcode
|
||||
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
|
||||
// Enable a modified version of automatic downscale on load of objects too big
|
||||
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable visualization of start gcode as regular toolpaths
|
||||
#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||
// Enable visualization of seams in preview
|
||||
#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
|
||||
|
||||
// Enable project dirty state manager
|
||||
#define ENABLE_PROJECT_DIRTY_STATE (1 && ENABLE_2_4_0_ALPHA0)
|
||||
|
|
|
@ -357,10 +357,14 @@ void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
|
|||
its_transform(its, t);
|
||||
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
|
||||
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
|
||||
this->repair(false);
|
||||
stl_reverse_all_facets(&stl);
|
||||
this->its.clear();
|
||||
this->require_shared_vertices();
|
||||
// As for the assert: the repair function would fix the normals, reversing would
|
||||
// break them again. The caller should provide a mesh that does not need repair.
|
||||
// The repair call is left here so things don't break more than they were.
|
||||
assert(this->repaired);
|
||||
this->repair(false);
|
||||
stl_reverse_all_facets(&stl);
|
||||
this->its.clear();
|
||||
this->require_shared_vertices();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,11 +373,12 @@ void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
|
|||
stl_transform(&stl, m);
|
||||
its_transform(its, m);
|
||||
if (fix_left_handed && m.determinant() < 0.) {
|
||||
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
|
||||
// See comments in function above.
|
||||
assert(this->repaired);
|
||||
this->repair(false);
|
||||
stl_reverse_all_facets(&stl);
|
||||
this->its.clear();
|
||||
this->require_shared_vertices();
|
||||
this->its.clear();
|
||||
this->require_shared_vertices();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -511,20 +516,22 @@ void TriangleMesh::merge(const TriangleMesh &mesh)
|
|||
//FIXME This could be extremely slow! Use it for tiny meshes only!
|
||||
ExPolygons TriangleMesh::horizontal_projection() const
|
||||
{
|
||||
Polygons pp;
|
||||
pp.reserve(this->stl.stats.number_of_facets);
|
||||
ClipperLib::Paths paths;
|
||||
Polygon p;
|
||||
p.points.assign(3, Point());
|
||||
auto delta = scaled<float>(0.01);
|
||||
std::vector<float> deltas { delta, delta, delta };
|
||||
paths.reserve(this->stl.stats.number_of_facets);
|
||||
for (const stl_facet &facet : this->stl.facet_start) {
|
||||
Polygon p;
|
||||
p.points.resize(3);
|
||||
p.points[0] = Point::new_scale(facet.vertex[0](0), facet.vertex[0](1));
|
||||
p.points[1] = Point::new_scale(facet.vertex[1](0), facet.vertex[1](1));
|
||||
p.points[2] = Point::new_scale(facet.vertex[2](0), facet.vertex[2](1));
|
||||
p.make_counter_clockwise(); // do this after scaling, as winding order might change while doing that
|
||||
pp.emplace_back(p);
|
||||
p.make_counter_clockwise();
|
||||
paths.emplace_back(mittered_offset_path_scaled(p.points, deltas, 3.));
|
||||
}
|
||||
|
||||
// the offset factor was tuned using groovemount.stl
|
||||
return union_ex(offset(pp, scale_(0.01)), true);
|
||||
return ClipperPaths_to_Slic3rExPolygons(paths);
|
||||
}
|
||||
|
||||
// 2D convex hull of a 3D mesh projected into the Z=0 plane.
|
||||
|
@ -1797,9 +1804,9 @@ void TriangleMeshSlicer::make_expolygons(const Polygons &loops, const float clos
|
|||
|
||||
// append to the supplied collection
|
||||
if (safety_offset > 0)
|
||||
expolygons_append(*slices, offset2_ex(union_(loops, false), +safety_offset, -safety_offset));
|
||||
expolygons_append(*slices, offset2_ex(union_ex(loops), +safety_offset, -safety_offset));
|
||||
else
|
||||
expolygons_append(*slices, union_ex(loops, false));
|
||||
expolygons_append(*slices, union_ex(loops));
|
||||
}
|
||||
|
||||
void TriangleMeshSlicer::make_expolygons(std::vector<IntersectionLine> &lines, const float closing_radius, ExPolygons* slices) const
|
||||
|
|
13
src/libslic3r/clipper.cpp
Normal file
13
src/libslic3r/clipper.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Hackish wrapper around the ClipperLib library to compile the Clipper library using Slic3r::Point.
|
||||
|
||||
#include "clipper.hpp"
|
||||
|
||||
// Don't include <clipper/clipper.hpp> for the second time.
|
||||
#define clipper_hpp
|
||||
|
||||
// Override ClipperLib namespace to Slic3r::ClipperLib
|
||||
#define CLIPPERLIB_NAMESPACE_PREFIX Slic3r
|
||||
// Override Slic3r::ClipperLib::IntPoint to Slic3r::Point
|
||||
#define CLIPPERLIB_INTPOINT_TYPE Slic3r::Point
|
||||
|
||||
#include <clipper/clipper.cpp>
|
26
src/libslic3r/clipper.hpp
Normal file
26
src/libslic3r/clipper.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Hackish wrapper around the ClipperLib library to compile the Clipper library using Slic3r's own Point type.
|
||||
|
||||
#ifndef slic3r_clipper_hpp
|
||||
|
||||
#ifdef clipper_hpp
|
||||
#error "You should include the libslic3r/clipper.hpp before clipper/clipper.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef CLIPPERLIB_USE_XYZ
|
||||
#error "Something went wrong. Using clipper.hpp with Slic3r Point type, but CLIPPERLIB_USE_XYZ is defined."
|
||||
#endif
|
||||
|
||||
#define slic3r_clipper_hpp
|
||||
|
||||
#include "Point.hpp"
|
||||
|
||||
#define CLIPPERLIB_NAMESPACE_PREFIX Slic3r
|
||||
#define CLIPPERLIB_INTPOINT_TYPE Slic3r::Point
|
||||
|
||||
#include <clipper/clipper.hpp>
|
||||
|
||||
#undef clipper_hpp
|
||||
#undef CLIPPERLIB_NAMESPACE_PREFIX
|
||||
#undef CLIPPERLIB_INTPOINT_TYPE
|
||||
|
||||
#endif // slic3r_clipper_hpp
|
|
@ -308,6 +308,10 @@ IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity)
|
|||
return ret;
|
||||
}
|
||||
|
||||
// Borrowed from C++20
|
||||
template<class T>
|
||||
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
|
|
|
@ -114,7 +114,7 @@
|
|||
#include <cereal/types/base_class.hpp>
|
||||
|
||||
#include <clipper/clipper_z.hpp>
|
||||
#include <clipper/clipper.hpp>
|
||||
#include "clipper.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Config.hpp"
|
||||
|
@ -129,8 +129,6 @@
|
|||
#include "libslic3r.h"
|
||||
#include "libslic3r_version.h"
|
||||
|
||||
#include "clipper.hpp"
|
||||
|
||||
#include <Shiny/Shiny.h>
|
||||
|
||||
#include <admesh/stl.h>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue