Output raster seem ok, stats broken.

This commit is contained in:
tamasmeszaros 2019-03-27 18:37:50 +01:00
parent 8fdff97eb7
commit 440e54181b
10 changed files with 436 additions and 347 deletions

View File

@ -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)

View File

@ -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

View File

@ -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);
}
}
@ -390,11 +312,17 @@ 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;
@ -437,15 +365,12 @@ 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);
}
valid &= clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
}
if(!valid) throw GeometryException(GeomErr::MERGE);
return _merge(clipper);
return clipper_execute(clipper, ClipperLib::ctUnion, ClipperLib::pftNegative);
}
}

View File

@ -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;

View File

@ -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.

View File

@ -42,8 +42,9 @@ template<FilePrinterFormat format>
class FilePrinter {
public:
// Draw an ExPolygon which is a polygon inside a slice on the specified layer.
void draw_polygon(const Polygon& p, unsigned lyr);
// 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);
@ -209,7 +210,12 @@ public:
inline void layers(unsigned cnt) { if(cnt > 0) m_layers_rst.resize(cnt); }
inline unsigned layers() const { return unsigned(m_layers_rst.size()); }
inline void draw_polygon(const Polygon& p, unsigned lyr) {
inline void draw_polygon(const ExPolygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
m_layers_rst[lyr].first.draw(p);
}
inline void draw_polygon(const ClipperLib::Polygon& p, unsigned lyr) {
assert(lyr < m_layers_rst.size());
m_layers_rst[lyr].first.draw(p);
}

View File

@ -1,5 +1,6 @@
#include "Rasterizer.hpp"
#include <Polygon.hpp>
#include <ExPolygon.hpp>
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
#include <cstdint>
@ -72,7 +73,7 @@ public:
clear();
}
void draw(const Polygon &poly) {
template<class Geometry> inline void draw(const Geometry &poly) {
agg::rasterizer_scanline_aa<> ras;
agg::scanline_p8 scanlines;
@ -104,14 +105,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;
}
@ -163,10 +186,16 @@ void Raster::clear()
m_impl->clear();
}
void Raster::draw(const Polygon &poly)
void Raster::draw(const ExPolygon &expoly)
{
assert(m_impl);
m_impl->draw(poly);
m_impl->draw(expoly.contour);
for(auto& h : expoly.holes) m_impl->draw(h);
}
void Raster::draw(const ClipperLib::Polygon &poly)
{
m_impl->draw(poly.Contour);
for(auto& h : poly.Holes) m_impl->draw(h);
}
void Raster::save(std::ostream& stream, Compression comp)

View File

@ -4,9 +4,11 @@
#include <ostream>
#include <memory>
namespace ClipperLib { class Polygon; }
namespace Slic3r {
class Polygon;
class ExPolygon;
/**
* @brief Raster captures an anti-aliased monochrome canvas where vectorial
@ -83,7 +85,8 @@ public:
void clear();
/// Draw a polygon with holes.
void draw(const Polygon& poly);
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);

View File

@ -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"
@ -958,8 +961,249 @@ void SLAPrint::process()
m_print_statistics.clear();
// Fill statistics
fill_statistics();
using ClipperPolygon = libnest2d::PolygonImpl;
using ClipperPath = ClipperLib::Path;
using ClipperPoint = ClipperLib::IntPoint;
using ClipperPolygons = std::vector<ClipperPolygon>;
using libnest2d::Radians;
namespace sl = libnest2d::shapelike;
// If the raster has vertical orientation, we will flip the coordinates
bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
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);
};
auto area = [](const ClipperPolygon& poly)
{
using ClipperLib::Area;
return std::accumulate( poly.Holes.begin(), poly.Holes.end(),
Area(poly.Contour),
[](double a, const ClipperPath& p) { return a + Area(p); });
};
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, Radians(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;
int sliced_layer_cnt = 0;
for (PrintLayer& layer : m_printer_input)
{
// vector of slice record references
auto& lyrslices = layer.slices();
if(lyrslices.empty()) continue;
// Layer height should match for all object slices for a given level.
const auto l_height = double(lyrslices.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 += area(polygon);
if (layer_model_area < 0 || layer_model_area > 0)
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
}
double layer_support_area = 0;
for (const ClipperPolygon& polygon : supports_polygons)
layer_support_area += area(polygon);
if (layer_support_area < 0 || layer_model_area > 0)
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;
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 (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);
};
@ -1011,7 +1255,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;
@ -1021,31 +1265,9 @@ void SLAPrint::process()
// Switch to the appropriate layer in the printer
printer.begin_layer(level_id);
for(const Polygon& poly : printlayer.transformed_slices())
for(const ClipperLib::Polygon& poly : printlayer.transformed_slices())
printer.draw_polygon(poly, level_id);
// auto draw =
// [&printer, flpXY, level_id](Polygon& poly, const Instance& tr)
// {
// poly.rotate(double(tr.rotation));
// poly.translate(tr.shift(X), tr.shift(Y));
// if(flpXY) for(auto& p : poly.points) std::swap(p(X), p(Y));
// 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);
@ -1221,149 +1443,6 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vector<t_config_opt
return invalidated;
}
void SLAPrint::fill_statistics()
{
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;
// If the raster has vertical orientation, we will flip the coordinates
bool flpXY = m_printer_config.display_orientation.getInt() ==
SLADisplayOrientation::sladoPortrait;
// get polygons for all instances in the object
auto get_all_polygons =
[flpXY](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());
if(flpXY) swapXY(tmp);
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;
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 (PrintLayer& layer : m_printer_input)
{
if(layer.slices().empty()) continue;
// Layer height should match for all object slices for a given level.
const auto l_height = double(layer.slices().front().get().layer_height());
// Calculation of the consumed material
Polygons model_polygons;
Polygons supports_polygons;
for(const SliceRecord& record : layer.slices()) {
const SLAPrintObject *po = record.print_obj();
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 || 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 || layer_model_area > 0)
supports_volume += layer_support_area * l_height;
// Here we can save the expensively calculated polygons for printing
append(model_polygons, supports_polygons);
layer.transformed_slices(union_(model_polygons));
// 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 (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;
}
// 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
{

View File

@ -6,6 +6,7 @@
#include "PrintExport.hpp"
#include "Point.hpp"
#include "MTUtils.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
#include <iterator>
namespace Slic3r {
@ -328,46 +329,8 @@ class SLAPrint : public PrintBaseWithState<SLAPrintStep, slapsCount>
private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;
void fill_statistics();
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;
Polygons m_transformed_slices;
template<class Container> void transformed_slices(Container&& c) {
m_transformed_slices = std::forward<Container>(c);
}
friend void SLAPrint::fill_statistics();
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 Polygons& transformed_slices() const {
return m_transformed_slices;
}
};
SLAPrint(): m_stepmask(slapsCount, true) {}
virtual ~SLAPrint() override { this->clear(); }
@ -401,6 +364,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; }