Merge branch 'tm_reuse_unified'
This commit is contained in:
commit
50942e9382
@ -64,6 +64,7 @@ endif()
|
||||
target_include_directories(ClipperBackend INTERFACE ${Boost_INCLUDE_DIRS} )
|
||||
target_sources(ClipperBackend INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/geometries.hpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clipper_polygon.hpp
|
||||
${SRC_DIR}/libnest2d/utils/boost_alg.hpp )
|
||||
|
||||
target_compile_definitions(ClipperBackend INTERFACE LIBNEST2D_BACKEND_CLIPPER)
|
||||
|
@ -0,0 +1,72 @@
|
||||
#ifndef CLIPPER_POLYGON_HPP
|
||||
#define CLIPPER_POLYGON_HPP
|
||||
|
||||
#include <clipper.hpp>
|
||||
|
||||
namespace ClipperLib {
|
||||
|
||||
struct Polygon {
|
||||
Path Contour;
|
||||
Paths Holes;
|
||||
|
||||
inline Polygon() = default;
|
||||
|
||||
inline explicit Polygon(const Path& cont): Contour(cont) {}
|
||||
inline explicit Polygon(const Paths& holes):
|
||||
Holes(holes) {}
|
||||
inline Polygon(const Path& cont, const Paths& holes):
|
||||
Contour(cont), Holes(holes) {}
|
||||
|
||||
inline explicit Polygon(Path&& cont): Contour(std::move(cont)) {}
|
||||
inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {}
|
||||
inline Polygon(Path&& cont, Paths&& holes):
|
||||
Contour(std::move(cont)), Holes(std::move(holes)) {}
|
||||
};
|
||||
|
||||
inline IntPoint& operator +=(IntPoint& p, const IntPoint& pa ) {
|
||||
// This could be done with SIMD
|
||||
p.X += pa.X;
|
||||
p.Y += pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline IntPoint operator+(const IntPoint& p1, const IntPoint& p2) {
|
||||
IntPoint ret = p1;
|
||||
ret += p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline IntPoint& operator -=(IntPoint& p, const IntPoint& pa ) {
|
||||
p.X -= pa.X;
|
||||
p.Y -= pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline IntPoint operator -(IntPoint& p ) {
|
||||
IntPoint ret = p;
|
||||
ret.X = -ret.X;
|
||||
ret.Y = -ret.Y;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline IntPoint operator-(const IntPoint& p1, const IntPoint& p2) {
|
||||
IntPoint ret = p1;
|
||||
ret -= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline IntPoint& operator *=(IntPoint& p, const IntPoint& pa ) {
|
||||
p.X *= pa.X;
|
||||
p.Y *= pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline IntPoint operator*(const IntPoint& p1, const IntPoint& p2) {
|
||||
IntPoint ret = p1;
|
||||
ret *= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // CLIPPER_POLYGON_HPP
|
@ -10,84 +10,15 @@
|
||||
#include <libnest2d/geometry_traits.hpp>
|
||||
#include <libnest2d/geometry_traits_nfp.hpp>
|
||||
|
||||
#include <clipper.hpp>
|
||||
|
||||
namespace ClipperLib {
|
||||
using PointImpl = IntPoint;
|
||||
using PathImpl = Path;
|
||||
using HoleStore = std::vector<PathImpl>;
|
||||
|
||||
struct PolygonImpl {
|
||||
PathImpl Contour;
|
||||
HoleStore Holes;
|
||||
|
||||
inline PolygonImpl() = default;
|
||||
|
||||
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
||||
inline explicit PolygonImpl(const HoleStore& holes):
|
||||
Holes(holes) {}
|
||||
inline PolygonImpl(const Path& cont, const HoleStore& holes):
|
||||
Contour(cont), Holes(holes) {}
|
||||
|
||||
inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {}
|
||||
inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {}
|
||||
inline PolygonImpl(Path&& cont, HoleStore&& holes):
|
||||
Contour(std::move(cont)), Holes(std::move(holes)) {}
|
||||
};
|
||||
|
||||
inline PointImpl& operator +=(PointImpl& p, const PointImpl& pa ) {
|
||||
// This could be done with SIMD
|
||||
p.X += pa.X;
|
||||
p.Y += pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline PointImpl operator+(const PointImpl& p1, const PointImpl& p2) {
|
||||
PointImpl ret = p1;
|
||||
ret += p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline PointImpl& operator -=(PointImpl& p, const PointImpl& pa ) {
|
||||
p.X -= pa.X;
|
||||
p.Y -= pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline PointImpl operator -(PointImpl& p ) {
|
||||
PointImpl ret = p;
|
||||
ret.X = -ret.X;
|
||||
ret.Y = -ret.Y;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline PointImpl operator-(const PointImpl& p1, const PointImpl& p2) {
|
||||
PointImpl ret = p1;
|
||||
ret -= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline PointImpl& operator *=(PointImpl& p, const PointImpl& pa ) {
|
||||
p.X *= pa.X;
|
||||
p.Y *= pa.Y;
|
||||
return p;
|
||||
}
|
||||
|
||||
inline PointImpl operator*(const PointImpl& p1, const PointImpl& p2) {
|
||||
PointImpl ret = p1;
|
||||
ret *= p2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
#include "clipper_polygon.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
// Aliases for convinience
|
||||
using ClipperLib::PointImpl;
|
||||
using ClipperLib::PathImpl;
|
||||
using ClipperLib::PolygonImpl;
|
||||
using ClipperLib::HoleStore;
|
||||
using PointImpl = ClipperLib::IntPoint;
|
||||
using PathImpl = ClipperLib::Path;
|
||||
using HoleStore = ClipperLib::Paths;
|
||||
using PolygonImpl = ClipperLib::Polygon;
|
||||
|
||||
// Type of coordinate units used by Clipper
|
||||
template<> struct CoordType<PointImpl> {
|
||||
@ -158,33 +89,24 @@ template<> inline TCoord<PointImpl>& y(PointImpl& p)
|
||||
#define DISABLE_BOOST_AREA
|
||||
|
||||
namespace _smartarea {
|
||||
|
||||
template<Orientation o>
|
||||
inline double area(const PolygonImpl& /*sh*/) {
|
||||
return std::nan("");
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double area<Orientation::CLOCKWISE>(const PolygonImpl& sh) {
|
||||
double a = 0;
|
||||
|
||||
std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h)
|
||||
{
|
||||
a -= ClipperLib::Area(h);
|
||||
inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
|
||||
return std::accumulate(sh.Holes.begin(), sh.Holes.end(),
|
||||
ClipperLib::Area(sh.Contour),
|
||||
[](double a, const ClipperLib::Path& pt){
|
||||
return a + ClipperLib::Area(pt);
|
||||
});
|
||||
|
||||
return -ClipperLib::Area(sh.Contour) + a;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
|
||||
double a = 0;
|
||||
|
||||
std::for_each(sh.Holes.begin(), sh.Holes.end(), [&a](const PathImpl& h)
|
||||
{
|
||||
a += ClipperLib::Area(h);
|
||||
});
|
||||
|
||||
return ClipperLib::Area(sh.Contour) + a;
|
||||
inline double area<Orientation::CLOCKWISE>(const PolygonImpl& sh) {
|
||||
return -area<Orientation::COUNTER_CLOCKWISE>(sh);
|
||||
}
|
||||
|
||||
}
|
||||
@ -228,9 +150,10 @@ template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
|
||||
// but throwing would be an overkill. Instead, we should warn the
|
||||
// caller about the inability to create correct geometries
|
||||
if(!found_the_contour) {
|
||||
sh.Contour = r;
|
||||
sh.Contour = std::move(r);
|
||||
ClipperLib::ReversePath(sh.Contour);
|
||||
sh.Contour.push_back(sh.Contour.front());
|
||||
auto front_p = sh.Contour.front();
|
||||
sh.Contour.emplace_back(std::move(front_p));
|
||||
found_the_contour = true;
|
||||
} else {
|
||||
dout() << "Warning: offsetting result is invalid!";
|
||||
@ -240,9 +163,10 @@ template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
|
||||
// TODO If there are multiple contours we can't be sure which hole
|
||||
// belongs to the first contour. (But in this case the situation is
|
||||
// bad enough to let it go...)
|
||||
sh.Holes.push_back(r);
|
||||
sh.Holes.emplace_back(std::move(r));
|
||||
ClipperLib::ReversePath(sh.Holes.back());
|
||||
sh.Holes.back().push_back(sh.Holes.back().front());
|
||||
auto front_p = sh.Holes.back().front();
|
||||
sh.Holes.back().emplace_back(std::move(front_p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,34 +314,53 @@ inline void rotate(PolygonImpl& sh, const Radians& rads)
|
||||
} // namespace shapelike
|
||||
|
||||
#define DISABLE_BOOST_NFP_MERGE
|
||||
inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
inline std::vector<PolygonImpl> clipper_execute(
|
||||
ClipperLib::Clipper& clipper,
|
||||
ClipperLib::ClipType clipType,
|
||||
ClipperLib::PolyFillType subjFillType = ClipperLib::pftEvenOdd,
|
||||
ClipperLib::PolyFillType clipFillType = ClipperLib::pftEvenOdd)
|
||||
{
|
||||
shapelike::Shapes<PolygonImpl> retv;
|
||||
|
||||
ClipperLib::PolyTree result;
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
||||
clipper.Execute(clipType, result, subjFillType, clipFillType);
|
||||
|
||||
retv.reserve(static_cast<size_t>(result.Total()));
|
||||
|
||||
std::function<void(ClipperLib::PolyNode*, PolygonImpl&)> processHole;
|
||||
|
||||
auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
|
||||
PolygonImpl poly(pptr->Contour);
|
||||
poly.Contour.push_back(poly.Contour.front());
|
||||
PolygonImpl poly;
|
||||
poly.Contour.swap(pptr->Contour);
|
||||
|
||||
assert(!pptr->IsHole());
|
||||
|
||||
if(pptr->IsOpen()) {
|
||||
auto front_p = poly.Contour.front();
|
||||
poly.Contour.emplace_back(front_p);
|
||||
}
|
||||
|
||||
for(auto h : pptr->Childs) { processHole(h, poly); }
|
||||
retv.push_back(poly);
|
||||
};
|
||||
|
||||
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
|
||||
{
|
||||
poly.Holes.push_back(pptr->Contour);
|
||||
poly.Holes.back().push_back(poly.Holes.back().front());
|
||||
poly.Holes.emplace_back(std::move(pptr->Contour));
|
||||
|
||||
assert(pptr->IsHole());
|
||||
|
||||
if(pptr->IsOpen()) {
|
||||
auto front_p = poly.Holes.back().front();
|
||||
poly.Holes.back().emplace_back(front_p);
|
||||
}
|
||||
|
||||
for(auto c : pptr->Childs) processPoly(c);
|
||||
};
|
||||
|
||||
auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
|
||||
{
|
||||
for(auto ch : node->Childs) {
|
||||
processPoly(ch);
|
||||
}
|
||||
for(auto ch : node->Childs) processPoly(ch);
|
||||
};
|
||||
|
||||
traverse(&result);
|
||||
@ -438,14 +381,13 @@ merge(const std::vector<PolygonImpl>& shapes)
|
||||
for(auto& path : shapes) {
|
||||
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
|
||||
for(auto& hole : path.Holes) {
|
||||
valid &= clipper.AddPath(hole, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
for(auto& h : path.Holes)
|
||||
valid &= clipper.AddPath(h, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
|
||||
if(!valid) throw GeometryException(GeomErr::MERGE);
|
||||
|
||||
return _merge(clipper);
|
||||
return clipper_execute(clipper, ClipperLib::ctUnion, ClipperLib::pftNegative);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -966,7 +966,7 @@ private:
|
||||
|
||||
for(size_t i = 0; i < pckgrp.size(); i++) {
|
||||
auto items = pckgrp[i];
|
||||
pg.push_back({});
|
||||
pg.emplace_back();
|
||||
pg[i].reserve(items.size());
|
||||
|
||||
for(Item& itemA : items) {
|
||||
|
@ -261,7 +261,7 @@ template<class RawShape> class EdgeCache {
|
||||
while(next != endit) {
|
||||
contour_.emap.emplace_back(*(first++), *(next++));
|
||||
contour_.full_distance += contour_.emap.back().length();
|
||||
contour_.distances.push_back(contour_.full_distance);
|
||||
contour_.distances.emplace_back(contour_.full_distance);
|
||||
}
|
||||
}
|
||||
|
||||
@ -276,10 +276,10 @@ template<class RawShape> class EdgeCache {
|
||||
while(next != endit) {
|
||||
hc.emap.emplace_back(*(first++), *(next++));
|
||||
hc.full_distance += hc.emap.back().length();
|
||||
hc.distances.push_back(hc.full_distance);
|
||||
hc.distances.emplace_back(hc.full_distance);
|
||||
}
|
||||
|
||||
holes_.push_back(hc);
|
||||
holes_.emplace_back(std::move(hc));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public:
|
||||
bool pack(Item& item, const Range& rem = Range()) {
|
||||
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
|
||||
if(r) {
|
||||
items_.push_back(*(r.item_ptr_));
|
||||
items_.emplace_back(*(r.item_ptr_));
|
||||
farea_valid_ = false;
|
||||
}
|
||||
return r;
|
||||
@ -78,7 +78,7 @@ public:
|
||||
if(r) {
|
||||
r.item_ptr_->translation(r.move_);
|
||||
r.item_ptr_->rotation(r.rot_);
|
||||
items_.push_back(*(r.item_ptr_));
|
||||
items_.emplace_back(*(r.item_ptr_));
|
||||
farea_valid_ = false;
|
||||
}
|
||||
}
|
||||
|
@ -667,7 +667,7 @@ public:
|
||||
addBin();
|
||||
ItemList& not_packed = not_packeds[b];
|
||||
for(unsigned idx = b; idx < store_.size(); idx+=bincount_guess) {
|
||||
not_packed.push_back(store_[idx]);
|
||||
not_packed.emplace_back(store_[idx]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -463,7 +463,7 @@ template<> inline std::string serialize<libnest2d::Formats::SVG>(
|
||||
auto& v = *it;
|
||||
hf.emplace_back(getX(v)*scale, getY(v)*scale);
|
||||
};
|
||||
holes.push_back(hf);
|
||||
holes.emplace_back(std::move(hf));
|
||||
}
|
||||
|
||||
Polygonf poly;
|
||||
|
@ -2,36 +2,10 @@
|
||||
#define PRINTER_PARTS_H
|
||||
|
||||
#include <vector>
|
||||
#include <clipper.hpp>
|
||||
|
||||
#ifndef CLIPPER_BACKEND_HPP
|
||||
namespace ClipperLib {
|
||||
using PointImpl = IntPoint;
|
||||
using PathImpl = Path;
|
||||
using HoleStore = std::vector<PathImpl>;
|
||||
|
||||
struct PolygonImpl {
|
||||
PathImpl Contour;
|
||||
HoleStore Holes;
|
||||
|
||||
inline PolygonImpl() {}
|
||||
|
||||
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
||||
inline explicit PolygonImpl(const HoleStore& holes):
|
||||
Holes(holes) {}
|
||||
inline PolygonImpl(const Path& cont, const HoleStore& holes):
|
||||
Contour(cont), Holes(holes) {}
|
||||
|
||||
inline explicit PolygonImpl(PathImpl&& cont): Contour(std::move(cont)) {}
|
||||
inline explicit PolygonImpl(HoleStore&& holes): Holes(std::move(holes)) {}
|
||||
inline PolygonImpl(Path&& cont, HoleStore&& holes):
|
||||
Contour(std::move(cont)), Holes(std::move(holes)) {}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||
|
||||
using TestData = std::vector<ClipperLib::Path>;
|
||||
using TestDataEx = std::vector<ClipperLib::PolygonImpl>;
|
||||
using TestDataEx = std::vector<ClipperLib::Polygon>;
|
||||
|
||||
extern const TestData PRINTER_PART_POLYGONS;
|
||||
extern const TestData STEGOSAUR_POLYGONS;
|
||||
|
@ -574,7 +574,7 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) {
|
||||
|
||||
for(ModelInstance* objinst : objptr->instances) {
|
||||
if(objinst) {
|
||||
ClipperLib::PolygonImpl pn;
|
||||
ClipperLib::Polygon pn;
|
||||
pn.Contour = clpath;
|
||||
|
||||
// Efficient conversion to item.
|
||||
|
@ -42,8 +42,9 @@ template<FilePrinterFormat format>
|
||||
class FilePrinter {
|
||||
public:
|
||||
|
||||
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
|
||||
// Draw a polygon which is a polygon inside a slice on the specified layer.
|
||||
void draw_polygon(const ExPolygon& p, unsigned lyr);
|
||||
void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr);
|
||||
|
||||
// Tell the printer how many layers should it consider.
|
||||
void layers(unsigned layernum);
|
||||
@ -221,6 +222,11 @@ public:
|
||||
m_layers_rst[lyr].raster.draw(p);
|
||||
}
|
||||
|
||||
inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) {
|
||||
assert(lyr < m_layers_rst.size());
|
||||
m_layers_rst[lyr].raster.draw(p);
|
||||
}
|
||||
|
||||
inline void begin_layer(unsigned lyr) {
|
||||
if(m_layers_rst.size() <= lyr) m_layers_rst.resize(lyr+1);
|
||||
m_layers_rst[lyr].raster.reset(m_res, m_pxdim, m_o);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "Rasterizer.hpp"
|
||||
#include <ExPolygon.hpp>
|
||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||
|
||||
// For rasterizing
|
||||
#include <agg/agg_basics.h>
|
||||
@ -89,6 +90,25 @@ public:
|
||||
agg::render_scanlines(ras, scanlines, m_renderer);
|
||||
}
|
||||
|
||||
void draw(const ClipperLib::Polygon &poly) {
|
||||
agg::rasterizer_scanline_aa<> ras;
|
||||
agg::scanline_p8 scanlines;
|
||||
|
||||
auto&& path = to_path(poly.Contour);
|
||||
|
||||
if(m_o == Origin::TOP_LEFT) flipy(path);
|
||||
|
||||
ras.add_path(path);
|
||||
|
||||
for(auto h : poly.Holes) {
|
||||
auto&& holepath = to_path(h);
|
||||
if(m_o == Origin::TOP_LEFT) flipy(holepath);
|
||||
ras.add_path(holepath);
|
||||
}
|
||||
|
||||
agg::render_scanlines(ras, scanlines, m_renderer);
|
||||
}
|
||||
|
||||
inline void clear() {
|
||||
m_raw_renderer.clear(ColorBlack);
|
||||
}
|
||||
@ -108,14 +128,36 @@ private:
|
||||
return p(1) * SCALING_FACTOR/m_pxdim.h_mm;
|
||||
}
|
||||
|
||||
agg::path_storage to_path(const Polygon& poly) {
|
||||
agg::path_storage to_path(const Polygon& poly)
|
||||
{
|
||||
agg::path_storage path;
|
||||
|
||||
auto it = poly.points.begin();
|
||||
path.move_to(getPx(*it), getPy(*it));
|
||||
while(++it != poly.points.end())
|
||||
while(++it != poly.points.end()) path.line_to(getPx(*it), getPy(*it));
|
||||
path.line_to(getPx(poly.points.front()), getPy(poly.points.front()));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
double getPx(const ClipperLib::IntPoint& p) {
|
||||
return p.X * SCALING_FACTOR/m_pxdim.w_mm;
|
||||
}
|
||||
|
||||
double getPy(const ClipperLib::IntPoint& p) {
|
||||
return p.Y * SCALING_FACTOR/m_pxdim.h_mm;
|
||||
}
|
||||
|
||||
agg::path_storage to_path(const ClipperLib::Path& poly)
|
||||
{
|
||||
agg::path_storage path;
|
||||
auto it = poly.begin();
|
||||
path.move_to(getPx(*it), getPy(*it));
|
||||
while(++it != poly.end())
|
||||
path.line_to(getPx(*it), getPy(*it));
|
||||
|
||||
path.line_to(getPx(poly.points.front()), getPy(poly.points.front()));
|
||||
path.line_to(getPx(poly.front()), getPy(poly.front()));
|
||||
return path;
|
||||
}
|
||||
|
||||
@ -167,9 +209,13 @@ void Raster::clear()
|
||||
m_impl->clear();
|
||||
}
|
||||
|
||||
void Raster::draw(const ExPolygon &poly)
|
||||
void Raster::draw(const ExPolygon &expoly)
|
||||
{
|
||||
m_impl->draw(expoly);
|
||||
}
|
||||
|
||||
void Raster::draw(const ClipperLib::Polygon &poly)
|
||||
{
|
||||
assert(m_impl);
|
||||
m_impl->draw(poly);
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace ClipperLib { class Polygon; }
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ExPolygon;
|
||||
@ -123,6 +125,7 @@ public:
|
||||
|
||||
/// Draw a polygon with holes.
|
||||
void draw(const ExPolygon& poly);
|
||||
void draw(const ClipperLib::Polygon& poly);
|
||||
|
||||
/// Save the raster on the specified stream.
|
||||
void save(std::ostream& stream, Compression comp = Compression::RAW);
|
||||
|
@ -12,6 +12,9 @@
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
// For geometry algorithms with native Clipper types (no copies and conversions)
|
||||
#include <libnest2d/backends/clipper/geometries.hpp>
|
||||
|
||||
//#include <tbb/spin_mutex.h>//#include "tbb/mutex.h"
|
||||
|
||||
#include "I18N.hpp"
|
||||
@ -43,8 +46,7 @@ const std::array<unsigned, slaposCount> OBJ_STEP_LEVELS =
|
||||
30, // slaposSupportPoints,
|
||||
25, // slaposSupportTree,
|
||||
25, // slaposBasePool,
|
||||
5, // slaposSliceSupports,
|
||||
5 // slaposIndexSlices
|
||||
10, // slaposSliceSupports,
|
||||
};
|
||||
|
||||
const std::array<std::string, slaposCount> OBJ_STEP_LABELS =
|
||||
@ -54,22 +56,19 @@ const std::array<std::string, slaposCount> OBJ_STEP_LABELS =
|
||||
L("Generating support tree"), // slaposSupportTree,
|
||||
L("Generating pad"), // slaposBasePool,
|
||||
L("Slicing supports"), // slaposSliceSupports,
|
||||
L("Slicing supports") // slaposIndexSlices,
|
||||
};
|
||||
|
||||
// Should also add up to 100 (%)
|
||||
const std::array<unsigned, slapsCount> PRINT_STEP_LEVELS =
|
||||
{
|
||||
5, // slapsStats
|
||||
94, // slapsRasterize
|
||||
1, // slapsValidate
|
||||
95, // slapsRasterize
|
||||
};
|
||||
|
||||
const std::array<std::string, slapsCount> PRINT_STEP_LABELS =
|
||||
{
|
||||
L("Calculating statistics"), // slapsStats
|
||||
L("Merging slices and calculating statistics"), // slapsStats
|
||||
L("Rasterizing layers"), // slapsRasterize
|
||||
L("Validating"), // slapsValidate
|
||||
};
|
||||
|
||||
}
|
||||
@ -206,7 +205,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||
} else if (model_object_list_extended(m_model, model)) {
|
||||
// Add new objects. Their volumes and configs will be synchronized later.
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
|
||||
for (const ModelObject *model_object : m_model.objects)
|
||||
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
|
||||
for (size_t i = m_model.objects.size(); i < model.objects.size(); ++ i) {
|
||||
@ -218,7 +217,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
||||
// Reorder the objects, add new objects.
|
||||
// First stop background processing before shuffling or deleting the PrintObjects in the object list.
|
||||
this->call_cancel_callback();
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
|
||||
// Second create a new list of objects.
|
||||
std::vector<ModelObject*> model_objects_old(std::move(m_model.objects));
|
||||
m_model.objects.clear();
|
||||
@ -390,7 +389,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf
|
||||
if (new_instances != it_print_object_status->print_object->instances()) {
|
||||
// Instances changed.
|
||||
it_print_object_status->print_object->set_instances(new_instances);
|
||||
update_apply_status(this->invalidate_step(slapsRasterize));
|
||||
update_apply_status(this->invalidate_step(slapsMergeSlicesAndEval));
|
||||
}
|
||||
print_objects_new.emplace_back(it_print_object_status->print_object);
|
||||
const_cast<PrintObjectStatus&>(*it_print_object_status).status = PrintObjectStatus::Reused;
|
||||
@ -579,11 +578,6 @@ sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
|
||||
|
||||
return pcfg;
|
||||
}
|
||||
|
||||
void swapXY(ExPolygon& expoly) {
|
||||
for(auto& p : expoly.contour.points) std::swap(p(X), p(Y));
|
||||
for(auto& h : expoly.holes) for(auto& p : h.points) std::swap(p(X), p(Y));
|
||||
}
|
||||
}
|
||||
|
||||
std::string SLAPrint::validate() const
|
||||
@ -613,6 +607,18 @@ std::string SLAPrint::validate() const
|
||||
return "";
|
||||
}
|
||||
|
||||
bool SLAPrint::invalidate_step(SLAPrintStep step)
|
||||
{
|
||||
bool invalidated = Inherited::invalidate_step(step);
|
||||
|
||||
// propagate to dependent steps
|
||||
if (step == slapsMergeSlicesAndEval) {
|
||||
invalidated |= this->invalidate_all_steps();
|
||||
}
|
||||
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
template<class...Args>
|
||||
void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args)
|
||||
{
|
||||
@ -639,7 +645,7 @@ void SLAPrint::process()
|
||||
const size_t objcount = m_objects.size();
|
||||
|
||||
const unsigned min_objstatus = 0; // where the per object operations start
|
||||
const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsRasterize]; // where the per object operations end
|
||||
const unsigned max_objstatus = PRINT_STEP_LEVELS[slapsMergeSlicesAndEval]; // where the per object operations end
|
||||
|
||||
// the coefficient that multiplies the per object status values which
|
||||
// are set up for <0, 100>. They need to be scaled into the whole process
|
||||
@ -883,7 +889,7 @@ void SLAPrint::process()
|
||||
// Slicing the support geometries similarly to the model slicing procedure.
|
||||
// If the pad had been added previously (see step "base_pool" than it will
|
||||
// be part of the slices)
|
||||
auto slice_supports = [](SLAPrintObject& po) {
|
||||
auto slice_supports = [this](SLAPrintObject& po) {
|
||||
auto& sd = po.m_supportdata;
|
||||
|
||||
if(sd) sd->support_slices.clear();
|
||||
@ -906,28 +912,14 @@ void SLAPrint::process()
|
||||
{
|
||||
po.m_slice_index[i].set_support_slice_idx(po, i);
|
||||
}
|
||||
};
|
||||
|
||||
// We have the layer polygon collection but we need to unite them into
|
||||
// an index where the key is the height level in discrete levels (clipper)
|
||||
auto index_slices = [this/*, ilhd*/](SLAPrintObject& /*po*/) {
|
||||
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
|
||||
report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
|
||||
};
|
||||
|
||||
auto fillstats = [this]() {
|
||||
|
||||
m_print_statistics.clear();
|
||||
|
||||
// Fill statistics
|
||||
fill_statistics();
|
||||
|
||||
report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
|
||||
};
|
||||
|
||||
// Rasterizing the model objects, and their supports
|
||||
auto rasterize = [this, max_objstatus, ilhs]() {
|
||||
if(canceled()) return;
|
||||
// Merging the slices from all the print objects into one slice grid and
|
||||
// calculating print statistics from the merge result.
|
||||
auto merge_slices_and_eval_stats = [this, ilhs]() {
|
||||
|
||||
// clear the rasterizer input
|
||||
m_printer_input.clear();
|
||||
@ -962,6 +954,272 @@ void SLAPrint::process()
|
||||
}
|
||||
}
|
||||
|
||||
m_print_statistics.clear();
|
||||
|
||||
using ClipperPoint = ClipperLib::IntPoint;
|
||||
using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d
|
||||
using ClipperPolygons = std::vector<ClipperPolygon>;
|
||||
namespace sl = libnest2d::shapelike; // For algorithms
|
||||
|
||||
// If the raster has vertical orientation, we will flip the coordinates
|
||||
bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
|
||||
|
||||
// Set up custom union and diff functions for clipper polygons
|
||||
auto polyunion = [] (const ClipperPolygons& subjects)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
|
||||
bool closed = true;
|
||||
|
||||
for(auto& path : subjects) {
|
||||
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
|
||||
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
|
||||
}
|
||||
|
||||
auto mode = ClipperLib::pftPositive;
|
||||
|
||||
return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
|
||||
};
|
||||
|
||||
auto polydiff = [](const ClipperPolygons& subjects, const ClipperPolygons& clips)
|
||||
{
|
||||
ClipperLib::Clipper clipper;
|
||||
|
||||
bool closed = true;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
auto mode = ClipperLib::pftPositive;
|
||||
|
||||
return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
|
||||
};
|
||||
|
||||
// libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
|
||||
auto areafn = [](const ClipperPolygon& poly) { return - sl::area(poly); };
|
||||
|
||||
const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
|
||||
const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0;
|
||||
const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0;
|
||||
|
||||
const double init_exp_time = m_material_config.initial_exposure_time.getFloat();
|
||||
const double exp_time = m_material_config.exposure_time.getFloat();
|
||||
|
||||
const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20]
|
||||
|
||||
const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR;
|
||||
const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR;
|
||||
const double display_area = width*height;
|
||||
|
||||
// get polygons for all instances in the object
|
||||
auto get_all_polygons =
|
||||
[flpXY](const ExPolygons& input_polygons,
|
||||
const std::vector<SLAPrintObject::Instance>& instances)
|
||||
{
|
||||
ClipperPolygons polygons;
|
||||
polygons.reserve(input_polygons.size() * instances.size());
|
||||
|
||||
for (const ExPolygon& polygon : input_polygons) {
|
||||
if(polygon.contour.empty()) continue;
|
||||
|
||||
for (size_t i = 0; i < instances.size(); ++i)
|
||||
{
|
||||
ClipperPolygon poly;
|
||||
|
||||
// should be a move
|
||||
poly.Contour.reserve(polygon.contour.size() + 1);
|
||||
|
||||
for(auto& p : polygon.contour.points)
|
||||
poly.Contour.emplace_back(p.x(), p.y());
|
||||
|
||||
auto pfirst = poly.Contour.front();
|
||||
poly.Contour.emplace_back(pfirst);
|
||||
|
||||
for(auto& h : polygon.holes) {
|
||||
poly.Holes.emplace_back();
|
||||
auto& hole = poly.Holes.back();
|
||||
hole.reserve(h.points.size() + 1);
|
||||
|
||||
for(auto& p : h.points) hole.emplace_back(p.x(), p.y());
|
||||
auto pfirst = hole.front(); hole.emplace_back(pfirst);
|
||||
}
|
||||
|
||||
sl::rotate(poly, double(instances[i].rotation));
|
||||
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
||||
instances[i].shift(Y)});
|
||||
if (flpXY) {
|
||||
for(auto& p : poly.Contour) std::swap(p.X, p.Y);
|
||||
std::reverse(poly.Contour.begin(), poly.Contour.end());
|
||||
|
||||
for(auto& h : poly.Holes) {
|
||||
for(auto& p : h) std::swap(p.X, p.Y);
|
||||
std::reverse(h.begin(), h.end());
|
||||
}
|
||||
}
|
||||
|
||||
polygons.emplace_back(std::move(poly));
|
||||
}
|
||||
}
|
||||
return polygons;
|
||||
};
|
||||
|
||||
double supports_volume(0.0);
|
||||
double models_volume(0.0);
|
||||
|
||||
double estim_time(0.0);
|
||||
|
||||
size_t slow_layers = 0;
|
||||
size_t fast_layers = 0;
|
||||
|
||||
const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
|
||||
double fade_layer_time = init_exp_time;
|
||||
|
||||
SpinMutex mutex;
|
||||
using Lock = std::lock_guard<SpinMutex>;
|
||||
|
||||
// Going to parallel:
|
||||
auto printlayerfn = [this,
|
||||
// functions and read only vars
|
||||
get_all_polygons, polyunion, polydiff, areafn,
|
||||
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,
|
||||
&fast_layers, &fade_layer_time](size_t sliced_layer_cnt)
|
||||
{
|
||||
PrintLayer& layer = m_printer_input[sliced_layer_cnt];
|
||||
|
||||
// vector of slice record references
|
||||
auto& slicerecord_references = layer.slices();
|
||||
|
||||
if(slicerecord_references.empty()) return;
|
||||
|
||||
// Layer height should match for all object slices for a given level.
|
||||
const auto l_height = double(slicerecord_references.front().get().layer_height());
|
||||
|
||||
// Calculation of the consumed material
|
||||
|
||||
ClipperPolygons model_polygons;
|
||||
ClipperPolygons supports_polygons;
|
||||
|
||||
size_t c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) {
|
||||
return a + sr.get_slice(soModel).size();
|
||||
});
|
||||
|
||||
model_polygons.reserve(c);
|
||||
|
||||
c = std::accumulate(layer.slices().begin(), layer.slices().end(), 0u, [](size_t a, const SliceRecord& sr) {
|
||||
return a + sr.get_slice(soModel).size();
|
||||
});
|
||||
|
||||
supports_polygons.reserve(c);
|
||||
|
||||
for(const SliceRecord& record : layer.slices()) {
|
||||
const SLAPrintObject *po = record.print_obj();
|
||||
|
||||
const ExPolygons &modelslices = record.get_slice(soModel);
|
||||
if (!modelslices.empty()) {
|
||||
ClipperPolygons v = get_all_polygons(modelslices, po->instances());
|
||||
for(ClipperPolygon& p_tmp : v) model_polygons.emplace_back(std::move(p_tmp));
|
||||
}
|
||||
|
||||
const ExPolygons &supportslices = record.get_slice(soSupport);
|
||||
if (!supportslices.empty()) {
|
||||
ClipperPolygons v = get_all_polygons(supportslices, po->instances());
|
||||
for(ClipperPolygon& p_tmp : v) supports_polygons.emplace_back(std::move(p_tmp));
|
||||
}
|
||||
}
|
||||
|
||||
model_polygons = polyunion(model_polygons);
|
||||
double layer_model_area = 0;
|
||||
for (const ClipperPolygon& polygon : model_polygons)
|
||||
layer_model_area += areafn(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);
|
||||
// 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);
|
||||
|
||||
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;
|
||||
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));
|
||||
|
||||
layer.transformed_slices(polyunion(trslices));
|
||||
|
||||
// Calculation of the slow and fast layers to the future controlling those values on FW
|
||||
|
||||
const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
|
||||
const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
|
||||
|
||||
{ Lock lck(mutex);
|
||||
if (is_fast_layer)
|
||||
fast_layers++;
|
||||
else
|
||||
slow_layers++;
|
||||
|
||||
|
||||
// Calculation of the printing time
|
||||
|
||||
if (sliced_layer_cnt < 3)
|
||||
estim_time += init_exp_time;
|
||||
else if (fade_layer_time > exp_time)
|
||||
{
|
||||
fade_layer_time -= delta_fade_time;
|
||||
estim_time += fade_layer_time;
|
||||
}
|
||||
else
|
||||
estim_time += exp_time;
|
||||
|
||||
estim_time += tilt_time;
|
||||
}
|
||||
};
|
||||
|
||||
// sequential version for debugging:
|
||||
// for(size_t i = 0; i < m_printer_input.size(); ++i) printlayerfn(i);
|
||||
tbb::parallel_for<size_t, decltype(printlayerfn)>(0, m_printer_input.size(), printlayerfn);
|
||||
|
||||
m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
|
||||
// Estimated printing time
|
||||
// A layers count o the highest object
|
||||
if (m_printer_input.size() == 0)
|
||||
m_print_statistics.estimated_print_time = "N/A";
|
||||
else
|
||||
m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time));
|
||||
|
||||
m_print_statistics.fast_layers_count = fast_layers;
|
||||
m_print_statistics.slow_layers_count = slow_layers;
|
||||
|
||||
report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
|
||||
};
|
||||
|
||||
// Rasterizing the model objects, and their supports
|
||||
auto rasterize = [this, max_objstatus]() {
|
||||
if(canceled()) return;
|
||||
|
||||
// collect all the keys
|
||||
|
||||
// If the raster has vertical orientation, we will flip the coordinates
|
||||
@ -1005,7 +1263,7 @@ void SLAPrint::process()
|
||||
|
||||
// procedure to process one height level. This will run in parallel
|
||||
auto lvlfn =
|
||||
[this, &slck, &printer, slot, sd, ist, &pst, flpXY]
|
||||
[this, &slck, &printer, slot, sd, ist, &pst]
|
||||
(unsigned level_id)
|
||||
{
|
||||
if(canceled()) return;
|
||||
@ -1015,28 +1273,8 @@ void SLAPrint::process()
|
||||
// Switch to the appropriate layer in the printer
|
||||
printer.begin_layer(level_id);
|
||||
|
||||
using Instance = SLAPrintObject::Instance;
|
||||
|
||||
auto draw =
|
||||
[&printer, flpXY, level_id](ExPolygon& poly, const Instance& tr)
|
||||
{
|
||||
poly.rotate(double(tr.rotation));
|
||||
poly.translate(tr.shift(X), tr.shift(Y));
|
||||
if(flpXY) swapXY(poly);
|
||||
for(const ClipperLib::Polygon& poly : printlayer.transformed_slices())
|
||||
printer.draw_polygon(poly, level_id);
|
||||
};
|
||||
|
||||
for(const SliceRecord& sr : printlayer.slices()) {
|
||||
if(! sr.print_obj()) continue;
|
||||
|
||||
for(const Instance& inst : sr.print_obj()->instances()) {
|
||||
ExPolygons objsl = sr.get_slice(soModel);
|
||||
for(ExPolygon& poly : objsl) draw(poly, inst);
|
||||
|
||||
ExPolygons supsl = sr.get_slice(soSupport);
|
||||
for(ExPolygon& poly : supsl) draw(poly, inst);
|
||||
}
|
||||
}
|
||||
|
||||
// Finish the layer for later saving it.
|
||||
printer.finish_layer(level_id);
|
||||
@ -1079,15 +1317,13 @@ void SLAPrint::process()
|
||||
support_points,
|
||||
support_tree,
|
||||
base_pool,
|
||||
slice_supports,
|
||||
index_slices
|
||||
slice_supports
|
||||
};
|
||||
|
||||
std::array<slapsFn, slapsCount> print_program =
|
||||
{
|
||||
fillstats,
|
||||
rasterize,
|
||||
[](){} // validate
|
||||
merge_slices_and_eval_stats,
|
||||
rasterize
|
||||
};
|
||||
|
||||
unsigned st = min_objstatus;
|
||||
@ -1127,7 +1363,7 @@ void SLAPrint::process()
|
||||
}
|
||||
|
||||
std::array<SLAPrintStep, slapsCount> printsteps = {
|
||||
slapsStats, slapsRasterize, slapsValidate
|
||||
slapsMergeSlicesAndEval, slapsRasterize
|
||||
};
|
||||
|
||||
// this would disable the rasterization step
|
||||
@ -1193,11 +1429,11 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
||||
if (steps_rasterize.find(opt_key) != steps_rasterize.end()) {
|
||||
// These options only affect the final rasterization, or they are just notes without influence on the output,
|
||||
// so there is nothing to invalidate.
|
||||
steps.emplace_back(slapsRasterize);
|
||||
steps.emplace_back(slapsMergeSlicesAndEval);
|
||||
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||||
// These steps have no influence on the output. Just ignore them.
|
||||
} else if (opt_key == "initial_layer_height") {
|
||||
steps.emplace_back(slapsRasterize);
|
||||
steps.emplace_back(slapsMergeSlicesAndEval);
|
||||
osteps.emplace_back(slaposObjectSlice);
|
||||
} else {
|
||||
// All values should be covered.
|
||||
@ -1215,165 +1451,6 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
|
||||
return invalidated;
|
||||
}
|
||||
|
||||
void SLAPrint::fill_statistics()
|
||||
{
|
||||
const double init_layer_height = m_material_config.initial_layer_height.getFloat();
|
||||
const double layer_height = m_default_object_config.layer_height.getFloat();
|
||||
|
||||
const double area_fill = m_printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
|
||||
const double fast_tilt = m_printer_config.fast_tilt_time.getFloat();// 5.0;
|
||||
const double slow_tilt = m_printer_config.slow_tilt_time.getFloat();// 8.0;
|
||||
|
||||
const double init_exp_time = m_material_config.initial_exposure_time.getFloat();
|
||||
const double exp_time = m_material_config.exposure_time.getFloat();
|
||||
|
||||
const int fade_layers_cnt = m_default_object_config.faded_layers.getInt();// 10 // [3;20]
|
||||
|
||||
const double width = m_printer_config.display_width.getFloat() / SCALING_FACTOR;
|
||||
const double height = m_printer_config.display_height.getFloat() / SCALING_FACTOR;
|
||||
const double display_area = width*height;
|
||||
|
||||
// get polygons for all instances in the object
|
||||
auto get_all_polygons = [](const ExPolygons& input_polygons, const std::vector<SLAPrintObject::Instance>& instances) {
|
||||
const size_t inst_cnt = instances.size();
|
||||
|
||||
size_t polygon_cnt = 0;
|
||||
for (const ExPolygon& polygon : input_polygons)
|
||||
polygon_cnt += polygon.holes.size() + 1;
|
||||
|
||||
Polygons polygons;
|
||||
polygons.reserve(polygon_cnt * inst_cnt);
|
||||
for (const ExPolygon& polygon : input_polygons) {
|
||||
for (size_t i = 0; i < inst_cnt; ++i)
|
||||
{
|
||||
ExPolygon tmp = polygon;
|
||||
tmp.rotate(double(instances[i].rotation));
|
||||
tmp.translate(instances[i].shift.x(), instances[i].shift.y());
|
||||
polygons_append(polygons, to_polygons(std::move(tmp)));
|
||||
}
|
||||
}
|
||||
return polygons;
|
||||
};
|
||||
|
||||
double supports_volume = 0.0;
|
||||
double models_volume = 0.0;
|
||||
|
||||
double estim_time = 0.0;
|
||||
|
||||
size_t slow_layers = 0;
|
||||
size_t fast_layers = 0;
|
||||
|
||||
// find highest object
|
||||
// Which is a better bet? To compare by max_z or by number of layers in the index?
|
||||
// float max_z = 0.;
|
||||
size_t max_layers_cnt = 0;
|
||||
size_t highest_obj_idx = 0;
|
||||
for (SLAPrintObject *&po : m_objects) {
|
||||
auto& slice_index = po->get_slice_index();
|
||||
if (! slice_index.empty()) {
|
||||
// float z = (-- slice_index.end())->slice_level();
|
||||
size_t cnt = slice_index.size();
|
||||
//if (z > max_z) {
|
||||
if (cnt > max_layers_cnt) {
|
||||
max_layers_cnt = cnt;
|
||||
// max_z = z;
|
||||
highest_obj_idx = &po - &m_objects.front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SLAPrintObject * highest_obj = m_objects[highest_obj_idx];
|
||||
auto& highest_obj_slice_index = highest_obj->get_slice_index();
|
||||
|
||||
const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
|
||||
double fade_layer_time = init_exp_time;
|
||||
|
||||
int sliced_layer_cnt = 0;
|
||||
for (const SliceRecord& layer : highest_obj_slice_index)
|
||||
{
|
||||
const auto l_height = double(layer.layer_height());
|
||||
|
||||
// Calculation of the consumed material
|
||||
|
||||
Polygons model_polygons;
|
||||
Polygons supports_polygons;
|
||||
|
||||
for (SLAPrintObject * po : m_objects)
|
||||
{
|
||||
const SliceRecord *record = nullptr;
|
||||
{
|
||||
const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level(), float(EPSILON));
|
||||
if (!slr.is_valid()) continue;
|
||||
record = &slr;
|
||||
}
|
||||
|
||||
const ExPolygons &modelslices = record->get_slice(soModel);
|
||||
if (!modelslices.empty())
|
||||
append(model_polygons, get_all_polygons(modelslices, po->instances()));
|
||||
|
||||
const ExPolygons &supportslices = record->get_slice(soSupport);
|
||||
if (!supportslices.empty())
|
||||
append(supports_polygons, get_all_polygons(supportslices, po->instances()));
|
||||
}
|
||||
|
||||
model_polygons = union_(model_polygons);
|
||||
double layer_model_area = 0;
|
||||
for (const Polygon& polygon : model_polygons)
|
||||
layer_model_area += polygon.area();
|
||||
|
||||
if (layer_model_area != 0)
|
||||
models_volume += layer_model_area * l_height;
|
||||
|
||||
if (!supports_polygons.empty() && !model_polygons.empty())
|
||||
supports_polygons = diff(supports_polygons, model_polygons);
|
||||
double layer_support_area = 0;
|
||||
for (const Polygon& polygon : supports_polygons)
|
||||
layer_support_area += polygon.area();
|
||||
|
||||
if (layer_support_area != 0)
|
||||
supports_volume += layer_support_area * l_height;
|
||||
|
||||
// Calculation of the slow and fast layers to the future controlling those values on FW
|
||||
|
||||
const bool is_fast_layer = (layer_model_area + layer_support_area) <= display_area*area_fill;
|
||||
const double tilt_time = is_fast_layer ? fast_tilt : slow_tilt;
|
||||
if (is_fast_layer)
|
||||
fast_layers++;
|
||||
else
|
||||
slow_layers++;
|
||||
|
||||
|
||||
// Calculation of the printing time
|
||||
|
||||
if (sliced_layer_cnt < 3)
|
||||
estim_time += init_exp_time;
|
||||
else if (fade_layer_time > exp_time)
|
||||
{
|
||||
fade_layer_time -= delta_fade_time;
|
||||
estim_time += fade_layer_time;
|
||||
}
|
||||
else
|
||||
estim_time += exp_time;
|
||||
|
||||
estim_time += tilt_time;
|
||||
|
||||
sliced_layer_cnt++;
|
||||
}
|
||||
|
||||
m_print_statistics.support_used_material = supports_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
m_print_statistics.objects_used_material = models_volume * SCALING_FACTOR * SCALING_FACTOR;
|
||||
|
||||
// Estimated printing time
|
||||
// A layers count o the highest object
|
||||
if (max_layers_cnt == 0)
|
||||
m_print_statistics.estimated_print_time = "N/A";
|
||||
else
|
||||
m_print_statistics.estimated_print_time = get_time_dhms(float(estim_time));
|
||||
|
||||
m_print_statistics.fast_layers_count = fast_layers;
|
||||
m_print_statistics.slow_layers_count = slow_layers;
|
||||
}
|
||||
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
bool SLAPrint::is_step_done(SLAPrintObjectStep step) const
|
||||
{
|
||||
@ -1459,19 +1536,16 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
||||
if (step == slaposObjectSlice) {
|
||||
invalidated |= this->invalidate_all_steps();
|
||||
} else if (step == slaposSupportPoints) {
|
||||
invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices });
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
} else if (step == slaposSupportTree) {
|
||||
invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports, slaposIndexSlices });
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
invalidated |= this->invalidate_steps({ slaposBasePool, slaposSliceSupports });
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
} else if (step == slaposBasePool) {
|
||||
invalidated |= this->invalidate_steps({slaposSliceSupports, slaposIndexSlices});
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
invalidated |= this->invalidate_steps({slaposSliceSupports});
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
} else if (step == slaposSliceSupports) {
|
||||
invalidated |= this->invalidate_step(slaposIndexSlices);
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
} else if(step == slaposIndexSlices) {
|
||||
invalidated |= m_print->invalidate_step(slapsRasterize);
|
||||
invalidated |= m_print->invalidate_step(slapsMergeSlicesAndEval);
|
||||
}
|
||||
return invalidated;
|
||||
}
|
||||
@ -1547,18 +1621,6 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
|
||||
return idx >= v.size() ? EMPTY_SLICE : v[idx];
|
||||
}
|
||||
|
||||
const std::vector<SliceRecord> & SLAPrintObject::get_slice_index() const
|
||||
{
|
||||
// assert(is_step_done(slaposIndexSlices));
|
||||
return m_slice_index;
|
||||
}
|
||||
|
||||
const std::vector<ExPolygons> &SLAPrintObject::get_model_slices() const
|
||||
{
|
||||
// assert(is_step_done(slaposObjectSlice));
|
||||
return m_model_slices;
|
||||
}
|
||||
|
||||
bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const
|
||||
{
|
||||
switch (step) {
|
||||
|
@ -6,14 +6,14 @@
|
||||
#include "PrintExport.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "MTUtils.hpp"
|
||||
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
|
||||
#include "Zipper.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum SLAPrintStep : unsigned int {
|
||||
slapsStats,
|
||||
slapsRasterize,
|
||||
slapsValidate,
|
||||
slapsMergeSlicesAndEval,
|
||||
slapsRasterize,
|
||||
slapsCount
|
||||
};
|
||||
|
||||
@ -22,8 +22,7 @@ enum SLAPrintObjectStep : unsigned int {
|
||||
slaposSupportPoints,
|
||||
slaposSupportTree,
|
||||
slaposBasePool,
|
||||
slaposSliceSupports,
|
||||
slaposIndexSlices,
|
||||
slaposSliceSupports,
|
||||
slaposCount
|
||||
};
|
||||
|
||||
@ -127,7 +126,7 @@ public:
|
||||
|
||||
bool is_valid() const { return ! std::isnan(m_slice_z); }
|
||||
|
||||
const SLAPrintObject* print_obj() const { return m_po; }
|
||||
const SLAPrintObject* print_obj() const { assert(m_po); return m_po; }
|
||||
|
||||
// Methods for setting the indices into the slice vectors.
|
||||
void set_model_slice_idx(const SLAPrintObject &po, size_t id) {
|
||||
@ -190,7 +189,7 @@ private:
|
||||
return it;
|
||||
}
|
||||
|
||||
const std::vector<ExPolygons>& get_model_slices() const;
|
||||
const std::vector<ExPolygons>& get_model_slices() const { return m_model_slices; }
|
||||
const std::vector<ExPolygons>& get_support_slices() const;
|
||||
|
||||
public:
|
||||
@ -205,7 +204,9 @@ public:
|
||||
// /////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Retrieve the slice index.
|
||||
const std::vector<SliceRecord>& get_slice_index() const;
|
||||
const std::vector<SliceRecord>& get_slice_index() const {
|
||||
return m_slice_index;
|
||||
}
|
||||
|
||||
// Search slice index for the closest slice to given print_level.
|
||||
// max_epsilon gives the allowable deviation of the returned slice record's
|
||||
@ -367,31 +368,6 @@ private: // Prevents erroneous use by other classes.
|
||||
|
||||
public:
|
||||
|
||||
// An aggregation of SliceRecord-s from all the print objects for each
|
||||
// occupied layer. Slice record levels dont have to match exactly.
|
||||
// They are unified if the level difference is within +/- SCALED_EPSILON
|
||||
class PrintLayer {
|
||||
coord_t m_level;
|
||||
|
||||
// The collection of slice records for the current level.
|
||||
std::vector<std::reference_wrapper<const SliceRecord>> m_slices;
|
||||
|
||||
public:
|
||||
|
||||
explicit PrintLayer(coord_t lvl) : m_level(lvl) {}
|
||||
|
||||
// for being sorted in their container (see m_printer_input)
|
||||
bool operator<(const PrintLayer& other) const {
|
||||
return m_level < other.m_level;
|
||||
}
|
||||
|
||||
void add(const SliceRecord& sr) { m_slices.emplace_back(sr); }
|
||||
|
||||
coord_t level() const { return m_level; }
|
||||
|
||||
auto slices() const -> const decltype (m_slices)& { return m_slices; }
|
||||
};
|
||||
|
||||
SLAPrint(): m_stepmask(slapsCount, true) {}
|
||||
|
||||
virtual ~SLAPrint() override { this->clear(); }
|
||||
@ -407,7 +383,7 @@ public:
|
||||
// Returns true if an object step is done on all objects and there's at least one object.
|
||||
bool is_step_done(SLAPrintObjectStep step) const;
|
||||
// Returns true if the last step was finished with success.
|
||||
bool finished() const override { return this->is_step_done(slaposIndexSlices) && this->Inherited::is_step_done(slapsRasterize); }
|
||||
bool finished() const override { return this->is_step_done(slaposSliceSupports) && this->Inherited::is_step_done(slapsRasterize); }
|
||||
|
||||
template<class Fmt = SLAminzZipper>
|
||||
void export_raster(const std::string& fname) {
|
||||
@ -427,6 +403,43 @@ public:
|
||||
|
||||
std::string validate() const override;
|
||||
|
||||
// An aggregation of SliceRecord-s from all the print objects for each
|
||||
// occupied layer. Slice record levels dont have to match exactly.
|
||||
// They are unified if the level difference is within +/- SCALED_EPSILON
|
||||
class PrintLayer {
|
||||
coord_t m_level;
|
||||
|
||||
// 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;
|
||||
|
||||
template<class Container> void transformed_slices(Container&& c) {
|
||||
m_transformed_slices = std::forward<Container>(c);
|
||||
}
|
||||
|
||||
friend void SLAPrint::process();
|
||||
|
||||
public:
|
||||
|
||||
explicit PrintLayer(coord_t lvl) : m_level(lvl) {}
|
||||
|
||||
// for being sorted in their container (see m_printer_input)
|
||||
bool operator<(const PrintLayer& other) const {
|
||||
return m_level < other.m_level;
|
||||
}
|
||||
|
||||
void add(const SliceRecord& sr) { m_slices.emplace_back(sr); }
|
||||
|
||||
coord_t level() const { return m_level; }
|
||||
|
||||
auto slices() const -> const decltype (m_slices)& { return m_slices; }
|
||||
|
||||
const std::vector<ClipperLib::Polygon> & transformed_slices() const {
|
||||
return m_transformed_slices;
|
||||
}
|
||||
};
|
||||
|
||||
// The aggregated and leveled print records from various objects.
|
||||
// TODO: use this structure for the preview in the future.
|
||||
const std::vector<PrintLayer>& print_layers() const { return m_printer_input; }
|
||||
@ -435,11 +448,12 @@ private:
|
||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||
|
||||
// Implement same logic as in SLAPrintObject
|
||||
bool invalidate_step(SLAPrintStep st);
|
||||
|
||||
// Invalidate steps based on a set of parameters changed.
|
||||
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
|
||||
|
||||
void fill_statistics();
|
||||
|
||||
SLAPrintConfig m_print_config;
|
||||
SLAPrinterConfig m_printer_config;
|
||||
SLAMaterialConfig m_material_config;
|
||||
|
@ -5022,7 +5022,7 @@ void GLCanvas3D::_render_sla_slices() const
|
||||
}
|
||||
|
||||
if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) &&
|
||||
obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty())
|
||||
obj->is_step_done(slaposSliceSupports) && !obj->get_slice_index().empty())
|
||||
{
|
||||
double layer_height = print->default_object_config().layer_height.value;
|
||||
double initial_layer_height = print->material_config().initial_layer_height.value;
|
||||
@ -6224,7 +6224,7 @@ void GLCanvas3D::_load_shells_sla()
|
||||
int obj_idx = 0;
|
||||
for (const SLAPrintObject* obj : print->objects())
|
||||
{
|
||||
if (!obj->is_step_done(slaposIndexSlices))
|
||||
if (!obj->is_step_done(slaposSliceSupports))
|
||||
continue;
|
||||
|
||||
unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size();
|
||||
|
@ -773,7 +773,7 @@ void Preview::load_print_as_sla()
|
||||
std::vector<double> zs;
|
||||
double initial_layer_height = print->material_config().initial_layer_height.value;
|
||||
for (const SLAPrintObject* obj : print->objects())
|
||||
if (obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty())
|
||||
if (obj->is_step_done(slaposSliceSupports) && !obj->get_slice_index().empty())
|
||||
{
|
||||
auto low_coord = obj->get_slice_index().front().print_level();
|
||||
for (auto& rec : obj->get_slice_index())
|
||||
|
Loading…
Reference in New Issue
Block a user