Fixed conflicts after merge with master

This commit is contained in:
Enrico Turri 2019-04-01 14:21:55 +02:00
commit 83aaa471cf
24 changed files with 693 additions and 569 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);
}
}
@ -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);
}
}

View file

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

View file

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

View file

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

View file

@ -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]);
}
}

View file

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

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

@ -61,7 +61,7 @@ Model& Model::assign_copy(Model &&rhs)
this->objects = std::move(rhs.objects);
for (ModelObject *model_object : this->objects)
model_object->set_model(this);
rhs.objects.clear();
rhs.objects.clear();
return *this;
}
@ -651,7 +651,7 @@ ModelObject& ModelObject::assign_copy(ModelObject &&rhs)
for (ModelInstance *model_instance : this->instances)
model_instance->set_model_object(this);
return *this;
return *this;
}
void ModelObject::assign_new_unique_ids_recursive()
@ -970,8 +970,8 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance)
}
}
}
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
std::sort(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) < b(0) || (a(0) == b(0) && a(1) < b(1)); });
pts.erase(std::unique(pts.begin(), pts.end(), [](const Point& a, const Point& b) { return a(0) == b(0) && a(1) == b(1); }), pts.end());
Polygon hull;
int n = (int)pts.size();
@ -1291,11 +1291,11 @@ void ModelObject::split(ModelObjectPtrs* new_objects)
// XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed?
ModelObject* new_object = m_model->add_object();
new_object->name = this->name;
new_object->config = this->config;
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
new_object->name = this->name;
new_object->config = this->config;
new_object->instances.reserve(this->instances.size());
for (const ModelInstance *model_instance : this->instances)
new_object->add_instance(*model_instance);
ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh));
#if !ENABLE_VOLUMES_CENTERING_FIXES
new_vol->center_geometry();
@ -1467,9 +1467,9 @@ int ModelVolume::extruder_id() const
bool ModelVolume::is_splittable() const
{
// the call mesh.has_multiple_patches() is expensive, so cache the value to calculate it only once
// the call mesh.is_splittable() is expensive, so cache the value to calculate it only once
if (m_is_splittable == -1)
m_is_splittable = (int)mesh.has_multiple_patches();
m_is_splittable = (int)mesh.is_splittable();
return m_is_splittable == 1;
}
@ -1609,6 +1609,7 @@ void ModelVolume::rotate(double angle, Axis axis)
case X: { rotate(angle, Vec3d::UnitX()); break; }
case Y: { rotate(angle, Vec3d::UnitY()); break; }
case Z: { rotate(angle, Vec3d::UnitZ()); break; }
default: break;
}
}
@ -1625,6 +1626,7 @@ void ModelVolume::mirror(Axis axis)
case X: { mirror(0) *= -1.0; break; }
case Y: { mirror(1) *= -1.0; break; }
case Z: { mirror(2) *= -1.0; break; }
default: break;
}
set_mirror(mirror);
}
@ -1711,7 +1713,6 @@ bool model_object_list_extended(const Model &model_old, const Model &model_new)
bool model_volume_list_changed(const ModelObject &model_object_old, const ModelObject &model_object_new, const ModelVolumeType type)
{
bool modifiers_differ = false;
size_t i_old, i_new;
for (i_old = 0, i_new = 0; i_old < model_object_old.volumes.size() && i_new < model_object_new.volumes.size();) {
const ModelVolume &mv_old = *model_object_old.volumes[i_old];

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.
// 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);

View file

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

View file

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

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

View file

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

View file

@ -55,7 +55,7 @@ TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector<Vec3crd>& f
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
for (int i = 0; i < stl.stats.number_of_facets; i++) {
for (uint32_t i = 0; i < stl.stats.number_of_facets; i++) {
stl_facet facet;
facet.vertex[0] = points[facets[i](0)].cast<float>();
facet.vertex[1] = points[facets[i](1)].cast<float>();
@ -125,9 +125,9 @@ void TriangleMesh::repair()
float tolerance = stl.stats.shortest_edge;
float increment = stl.stats.bounding_diameter / 10000.0;
int iterations = 2;
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
for (int i = 0; i < iterations; i++) {
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_check_faces_nearby";
@ -143,7 +143,7 @@ void TriangleMesh::repair()
}
// remove_unconnected
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
#ifdef SLIC3R_TRACE_REPAIR
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
#endif /* SLIC3R_TRACE_REPAIR */
@ -212,9 +212,9 @@ void TriangleMesh::check_topology()
float tolerance = stl.stats.shortest_edge;
float increment = stl.stats.bounding_diameter / 10000.0;
int iterations = 2;
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
for (int i = 0; i < iterations; i++) {
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
if (stl.stats.connected_facets_3_edge < (int)stl.stats.number_of_facets) {
//printf("Checking nearby. Tolerance= %f Iteration=%d of %d...", tolerance, i + 1, iterations);
stl_check_facets_nearby(&stl, tolerance);
//printf(" Fixed %d edges.\n", stl.stats.edges_fixed - last_edges_fixed);
@ -338,113 +338,78 @@ void TriangleMesh::rotate(double angle, Point* center)
this->translate(c(0), c(1), 0);
}
bool TriangleMesh::has_multiple_patches() const
/**
* Calculates whether or not the mesh is splittable.
*/
bool TriangleMesh::is_splittable() const
{
// we need neighbors
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0)
return false;
std::vector<bool> visited;
find_unvisited_neighbors(visited);
std::vector<int> facet_queue(this->stl.stats.number_of_facets, 0);
std::vector<char> facet_visited(this->stl.stats.number_of_facets, false);
int facet_queue_cnt = 1;
facet_queue[0] = 0;
facet_visited[0] = true;
while (facet_queue_cnt > 0) {
int facet_idx = facet_queue[-- facet_queue_cnt];
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
}
// If any of the face was not visited at the first time, return "multiple bodies".
for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx)
if (! facet_visited[facet_idx])
return true;
return false;
// Try finding an unvisited facet. If there are none, the mesh is not splittable.
auto it = std::find(visited.begin(), visited.end(), false);
return it != visited.end();
}
size_t TriangleMesh::number_of_patches() const
/**
* Visit all unvisited neighboring facets that are reachable from the first unvisited facet,
* and return them.
*
* @param facet_visited A reference to a vector of booleans. Contains whether or not a
* facet with the same index has been visited.
* @return A deque with all newly visited facets.
*/
std::deque<uint32_t> TriangleMesh::find_unvisited_neighbors(std::vector<bool> &facet_visited) const
{
// we need neighbors
// Make sure we're not operating on a broken mesh.
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
if (this->stl.stats.number_of_facets == 0)
return false;
std::vector<int> facet_queue(this->stl.stats.number_of_facets, 0);
std::vector<char> facet_visited(this->stl.stats.number_of_facets, false);
int facet_queue_cnt = 0;
size_t num_bodies = 0;
for (;;) {
// Find a seeding triangle for a new body.
int facet_idx = 0;
for (; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx)
if (! facet_visited[facet_idx]) {
// A seed triangle was found.
facet_queue[facet_queue_cnt ++] = facet_idx;
facet_visited[facet_idx] = true;
break;
}
if (facet_idx == this->stl.stats.number_of_facets)
// No seed found.
break;
++ num_bodies;
while (facet_queue_cnt > 0) {
int facet_idx = facet_queue[-- facet_queue_cnt];
// If the visited list is empty, populate it with false for every facet.
if (facet_visited.empty()) {
facet_visited = std::vector<bool>(this->stl.stats.number_of_facets, false);
}
// Find the first unvisited facet.
std::queue<int> facet_queue;
auto facet = std::find(facet_visited.begin(), facet_visited.end(), false);
if (facet != facet_visited.end())
facet_queue.push(facet - facet_visited.begin());
// Traverse all reachable neighbors and mark them as visited.
std::deque<uint32_t> facets;
while (!facet_queue.empty()) {
int facet_idx = facet_queue.front();
facet_queue.pop();
if (facet_idx != -1 && !facet_visited[facet_idx]) {
facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx;
}
facets.emplace_back(facet_idx);
for (int facet : this->stl.neighbors_start[facet_idx].neighbor)
facet_queue.push(facet);
}
}
return num_bodies;
return facets;
}
/**
* Splits a mesh into multiple meshes when possible.
*
* @return A TriangleMeshPtrs with the newly created meshes.
*/
TriangleMeshPtrs TriangleMesh::split() const
{
TriangleMeshPtrs meshes;
std::vector<unsigned char> facet_visited(this->stl.stats.number_of_facets, false);
// we need neighbors
if (!this->repaired)
throw std::runtime_error("split() requires repair()");
// loop while we have remaining facets
// Loop while we have remaining facets.
std::vector<bool> facet_visited;
TriangleMeshPtrs meshes;
for (;;) {
// get the first facet
std::queue<int> facet_queue;
std::deque<int> facets;
for (int facet_idx = 0; facet_idx < this->stl.stats.number_of_facets; ++ facet_idx) {
if (! facet_visited[facet_idx]) {
// if facet was not seen put it into queue and start searching
facet_queue.push(facet_idx);
break;
}
}
if (facet_queue.empty())
std::deque<uint32_t> facets = find_unvisited_neighbors(facet_visited);
if (facets.empty())
break;
while (! facet_queue.empty()) {
int facet_idx = facet_queue.front();
facet_queue.pop();
if (! facet_visited[facet_idx]) {
facets.emplace_back(facet_idx);
for (int j = 0; j < 3; ++ j)
facet_queue.push(this->stl.neighbors_start[facet_idx].neighbor[j]);
facet_visited[facet_idx] = true;
}
}
// Create a new mesh for the part that was just split off.
TriangleMesh* mesh = new TriangleMesh;
meshes.emplace_back(mesh);
mesh->stl.stats.type = inmemory;
@ -452,14 +417,15 @@ TriangleMeshPtrs TriangleMesh::split() const
mesh->stl.stats.original_num_facets = mesh->stl.stats.number_of_facets;
stl_clear_error(&mesh->stl);
stl_allocate(&mesh->stl);
// Assign the facets to the new mesh.
bool first = true;
for (std::deque<int>::const_iterator facet = facets.begin(); facet != facets.end(); ++ facet) {
for (auto facet = facets.begin(); facet != facets.end(); ++ facet) {
mesh->stl.facet_start[facet - facets.begin()] = this->stl.facet_start[*facet];
stl_facet_stats(&mesh->stl, this->stl.facet_start[*facet], first);
}
}
return meshes;
}
@ -476,7 +442,7 @@ void TriangleMesh::merge(const TriangleMesh &mesh)
stl_reallocate(&this->stl);
// copy facets
for (int i = 0; i < mesh.stl.stats.number_of_facets; ++ i)
for (uint32_t i = 0; i < mesh.stl.stats.number_of_facets; ++ i)
this->stl.facet_start[number_of_facets + i] = mesh.stl.facet_start[i];
// update size
@ -489,7 +455,7 @@ ExPolygons TriangleMesh::horizontal_projection() const
{
Polygons pp;
pp.reserve(this->stl.stats.number_of_facets);
for (int i = 0; i < this->stl.stats.number_of_facets; ++ i) {
for (uint32_t i = 0; i < this->stl.stats.number_of_facets; ++ i) {
stl_facet* facet = &this->stl.facet_start[i];
Polygon p;
p.points.resize(3);
@ -531,7 +497,7 @@ BoundingBoxf3 TriangleMesh::transformed_bounding_box(const Transform3d &trafo) c
BoundingBoxf3 bbox;
if (stl.v_shared == nullptr) {
// Using the STL faces.
for (int i = 0; i < this->facets_count(); ++ i) {
for (size_t i = 0; i < this->facets_count(); ++ i) {
const stl_facet &facet = this->stl.facet_start[i];
for (size_t j = 0; j < 3; ++ j)
bbox.merge(trafo * facet.vertex[j].cast<double>());
@ -656,7 +622,7 @@ void TriangleMeshSlicer::init(TriangleMesh *_mesh, throw_on_cancel_callback_type
};
std::vector<EdgeToFace> edges_map;
edges_map.assign(this->mesh->stl.stats.number_of_facets * 3, EdgeToFace());
for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx)
for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx)
for (int i = 0; i < 3; ++ i) {
EdgeToFace &e2f = edges_map[facet_idx*3+i];
e2f.vertex_low = this->mesh->stl.v_indices[facet_idx].vertex[i];
@ -905,7 +871,6 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = Slicing;
const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
if (min_z == max_z) {
// All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal;
@ -917,8 +882,6 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
}
} else {
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
int nbr_idx = j % 3;
int nbr_face = nbr.neighbor[nbr_idx];
// Is the third vertex below the cutting plane?
bool third_below = v0.z() < slice_z || v1.z() < slice_z || v2.z() < slice_z;
// Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice
@ -1697,7 +1660,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::cut - slicing object";
float scaled_z = scale_(z);
for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) {
for (uint32_t facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; ++ facet_idx) {
stl_facet* facet = &this->mesh->stl.facet_start[facet_idx];
// find facet extents

View file

@ -68,12 +68,8 @@ public:
size_t facets_count() const { return this->stl.stats.number_of_facets; }
bool empty() const { return this->facets_count() == 0; }
// Returns true, if there are two and more connected patches in the mesh.
// Returns false, if one or zero connected patch is in the mesh.
bool has_multiple_patches() const;
// Count disconnected triangle patches.
size_t number_of_patches() const;
bool is_splittable() const;
std::deque<uint32_t> find_unvisited_neighbors(std::vector<bool> &facet_visited) const;
stl_file stl;
bool repaired;

View file

@ -208,7 +208,7 @@ public:
// Shorten the dhms time by removing the seconds, rounding the dhm to full minutes
// and removing spaces.
static std::string short_time(const std::string &time)
inline std::string short_time(const std::string &time)
{
// Parse the dhms time format.
int days = 0;
@ -247,7 +247,7 @@ static std::string short_time(const std::string &time)
}
// Returns the given time is seconds in format DDd HHh MMm SSs
static std::string get_time_dhms(float time_in_secs)
inline std::string get_time_dhms(float time_in_secs)
{
int days = (int)(time_in_secs / 86400.0f);
time_in_secs -= (float)days * 86400.0f;

View file

@ -361,6 +361,14 @@ int CLI::run(int argc, char **argv)
std::string outfile = m_config.opt_string("output");
Print fff_print;
SLAPrint sla_print;
sla_print.set_status_callback(
[](const PrintBase::SlicingStatus& s)
{
if(s.percent >= 0) // FIXME: is this sufficient?
printf("%3d%s %s\n", s.percent, "% =>", s.text.c_str());
});
PrintBase *print = (printer_technology == ptFFF) ? static_cast<PrintBase*>(&fff_print) : static_cast<PrintBase*>(&sla_print);
if (! m_config.opt_bool("dont_arrange")) {
//FIXME make the min_object_distance configurable.

View file

@ -3902,7 +3902,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;
@ -5053,7 +5053,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();

View file

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

View file

@ -119,17 +119,17 @@ void Selection::add(unsigned int volume_idx, bool as_single_selection)
if (needs_reset)
clear();
if (volume->is_modifier)
m_mode = Volume;
else if (!contains_volume(volume_idx))
m_mode = Instance;
// else -> keep current mode
if (!contains_volume(volume_idx))
m_mode = volume->is_modifier ? Volume : Instance;
else
// keep current mode
return;
switch (m_mode)
{
case Volume:
{
if (volume->volume_idx() >= 0 && (is_empty() || (volume->instance_idx() == get_instance_idx())))
if ((volume->volume_idx() >= 0) && (is_empty() || (volume->instance_idx() == get_instance_idx())))
do_add_volume(volume_idx);
break;
@ -440,6 +440,8 @@ void Selection::translate(const Vec3d& displacement, bool local)
if (!m_valid)
return;
EMode translation_type = m_mode;
for (unsigned int i : m_list)
{
if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower)
@ -453,13 +455,22 @@ void Selection::translate(const Vec3d& displacement, bool local)
}
}
else if (m_mode == Instance)
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
{
if (is_from_fully_selected_instance(i))
(*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement);
else
{
Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement;
(*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement);
translation_type = Volume;
}
}
}
#if !DISABLE_INSTANCES_SYNCH
if (m_mode == Instance)
if (translation_type == Instance)
synchronize_unselected_instances(SYNC_ROTATION_NONE);
else if (m_mode == Volume)
else if (translation_type == Volume)
synchronize_unselected_volumes();
#endif // !DISABLE_INSTANCES_SYNCH
@ -1684,5 +1695,29 @@ void Selection::ensure_on_bed()
}
}
bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const
{
struct SameInstance
{
int obj_idx;
int inst_idx;
GLVolumePtrs& volumes;
SameInstance(int obj_idx, int inst_idx, GLVolumePtrs& volumes) : obj_idx(obj_idx), inst_idx(inst_idx), volumes(volumes) {}
bool operator () (unsigned int i) { return (volumes[i]->object_idx() == obj_idx) && (volumes[i]->instance_idx() == inst_idx); }
};
if ((unsigned int)m_volumes->size() <= volume_idx)
return false;
GLVolume* volume = (*m_volumes)[volume_idx];
int object_idx = volume->object_idx();
if ((int)m_model->objects.size() <= object_idx)
return false;
unsigned int count = (unsigned int)std::count_if(m_list.begin(), m_list.end(), SameInstance(object_idx, volume->instance_idx(), *m_volumes));
return count == (unsigned int)m_model->objects[object_idx]->volumes.size();
}
} // namespace GUI
} // namespace Slic3r

View file

@ -297,6 +297,7 @@ private:
void synchronize_unselected_instances(SyncRotationType sync_rotation_type);
void synchronize_unselected_volumes();
void ensure_on_bed();
bool is_from_fully_selected_instance(unsigned int volume_idx) const;
};
} // namespace GUI