Merge branch 'master' of https://github.com/prusa3d/Slic3r
This commit is contained in:
commit
f65eb9afdb
@ -92,7 +92,7 @@ The `DESTDIR` option is the location where the bundle will be installed.
|
|||||||
This may be customized. If you leave it empty, the `DESTDIR` will be placed inside the same `build` directory.
|
This may be customized. If you leave it empty, the `DESTDIR` will be placed inside the same `build` directory.
|
||||||
|
|
||||||
Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build
|
Warning: If the `build` directory is nested too deep inside other folders, various file paths during the build
|
||||||
become too long and the build might fail due to file writing errors. For this reason, it is recommended to
|
become too long and the build might fail due to file writing errors (\*). For this reason, it is recommended to
|
||||||
place the `build` directory relatively close to the drive root.
|
place the `build` directory relatively close to the drive root.
|
||||||
|
|
||||||
Note that the build variant that you may choose using Visual Studio (i.e. _Release_ or _Debug_ etc.) when building the dependency package is **not relevant**.
|
Note that the build variant that you may choose using Visual Studio (i.e. _Release_ or _Debug_ etc.) when building the dependency package is **not relevant**.
|
||||||
@ -100,3 +100,6 @@ The dependency build will by default build _both_ the _Release_ and _Debug_ vari
|
|||||||
You can disable building of the debug variant by passing the `-DDEP_DEBUG=OFF` option to CMake, this will only produce a _Release_ build.
|
You can disable building of the debug variant by passing the `-DDEP_DEBUG=OFF` option to CMake, this will only produce a _Release_ build.
|
||||||
|
|
||||||
Refer to the CMake scripts inside the `deps` directory to see which dependencies are built in what versions and how this is done.
|
Refer to the CMake scripts inside the `deps` directory to see which dependencies are built in what versions and how this is done.
|
||||||
|
|
||||||
|
\*) Specifically, the problem arises when building boost. Boost build tool appends all build options into paths of
|
||||||
|
intermediate files, which are not handled correctly by either `b2.exe` or possibly `ninja` (?).
|
||||||
|
@ -28,8 +28,8 @@ namespace Slic3r {
|
|||||||
|
|
||||||
//-----------------------------------------------------------
|
//-----------------------------------------------------------
|
||||||
// legacy code from Clipper documentation
|
// legacy code from Clipper documentation
|
||||||
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
|
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons *expolygons);
|
||||||
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
|
Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree);
|
||||||
//-----------------------------------------------------------
|
//-----------------------------------------------------------
|
||||||
|
|
||||||
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
|
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
|
||||||
@ -228,4 +228,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons);
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -56,8 +56,18 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class Vector,
|
/// An std compatible random access iterator which uses indices to the source
|
||||||
class Value = typename Vector::value_type>
|
/// vector thus resistant to invalidation caused by relocations. It also "knows"
|
||||||
|
/// its container. No comparison is neccesary to the container "end()" iterator.
|
||||||
|
/// The template can be instantiated with a different value type than that of
|
||||||
|
/// the container's but the types must be compatible. E.g. a base class of the
|
||||||
|
/// contained objects is compatible.
|
||||||
|
///
|
||||||
|
/// For a constant iterator, one can instantiate this template with a value
|
||||||
|
/// type preceded with 'const'.
|
||||||
|
template<class Vector, // The container type, must be random access...
|
||||||
|
class Value = typename Vector::value_type // The value type
|
||||||
|
>
|
||||||
class IndexBasedIterator {
|
class IndexBasedIterator {
|
||||||
static const size_t NONE = size_t(-1);
|
static const size_t NONE = size_t(-1);
|
||||||
|
|
||||||
@ -110,6 +120,8 @@ public:
|
|||||||
|
|
||||||
operator difference_type() { return difference_type(m_idx); }
|
operator difference_type() { return difference_type(m_idx); }
|
||||||
|
|
||||||
|
/// Tesing the end of the container... this is not possible with std
|
||||||
|
/// iterators.
|
||||||
inline bool is_end() const { return m_idx >= m_index_ref.get().size();}
|
inline bool is_end() const { return m_idx >= m_index_ref.get().size();}
|
||||||
|
|
||||||
inline Value & operator*() const {
|
inline Value & operator*() const {
|
||||||
@ -122,6 +134,7 @@ public:
|
|||||||
return &m_index_ref.get().operator[](m_idx);
|
return &m_index_ref.get().operator[](m_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If both iterators point past the container, they are equal...
|
||||||
inline bool operator ==(const IndexBasedIterator& other) {
|
inline bool operator ==(const IndexBasedIterator& other) {
|
||||||
size_t e = m_index_ref.get().size();
|
size_t e = m_index_ref.get().size();
|
||||||
return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e);
|
return m_idx == other.m_idx || (m_idx >= e && other.m_idx >= e);
|
||||||
@ -148,17 +161,23 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A very simple range concept implementation with iterator-like objects.
|
||||||
template<class It> class Range {
|
template<class It> class Range {
|
||||||
It from, to;
|
It from, to;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
// The class is ready for range based for loops.
|
||||||
It begin() const { return from; }
|
It begin() const { return from; }
|
||||||
It end() const { return to; }
|
It end() const { return to; }
|
||||||
|
|
||||||
|
// The iterator type can be obtained this way.
|
||||||
using Type = It;
|
using Type = It;
|
||||||
|
|
||||||
Range() = default;
|
Range() = default;
|
||||||
Range(It &&b, It &&e):
|
Range(It &&b, It &&e):
|
||||||
from(std::forward<It>(b)), to(std::forward<It>(e)) {}
|
from(std::forward<It>(b)), to(std::forward<It>(e)) {}
|
||||||
|
|
||||||
|
// Some useful container-like methods...
|
||||||
inline size_t size() const { return end() - begin(); }
|
inline size_t size() const { return end() - begin(); }
|
||||||
inline bool empty() const { return size() == 0; }
|
inline bool empty() const { return size() == 0; }
|
||||||
};
|
};
|
||||||
|
@ -587,6 +587,15 @@ void swapXY(ExPolygon& expoly) {
|
|||||||
std::string SLAPrint::validate() const
|
std::string SLAPrint::validate() const
|
||||||
{
|
{
|
||||||
for(SLAPrintObject * po : m_objects) {
|
for(SLAPrintObject * po : m_objects) {
|
||||||
|
|
||||||
|
const ModelObject *mo = po->model_object();
|
||||||
|
|
||||||
|
if(po->config().supports_enable.getBool() &&
|
||||||
|
mo->sla_points_status == sla::PointsStatus::UserModified &&
|
||||||
|
mo->sla_support_points.empty())
|
||||||
|
return L("Cannot proceed without support points! "
|
||||||
|
"Add support points or disable support generation.");
|
||||||
|
|
||||||
sla::SupportConfig cfg = make_support_cfg(po->config());
|
sla::SupportConfig cfg = make_support_cfg(po->config());
|
||||||
|
|
||||||
double pinhead_width =
|
double pinhead_width =
|
||||||
@ -596,7 +605,7 @@ std::string SLAPrint::validate() const
|
|||||||
cfg.head_penetration_mm;
|
cfg.head_penetration_mm;
|
||||||
|
|
||||||
if(pinhead_width > cfg.object_elevation_mm)
|
if(pinhead_width > cfg.object_elevation_mm)
|
||||||
return L("Elevetion is too low for object.");
|
return L("Elevation is too low for object.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
@ -623,11 +632,8 @@ void SLAPrint::process()
|
|||||||
// shortcut to initial layer height
|
// shortcut to initial layer height
|
||||||
double ilhd = m_material_config.initial_layer_height.getFloat();
|
double ilhd = m_material_config.initial_layer_height.getFloat();
|
||||||
auto ilh = float(ilhd);
|
auto ilh = float(ilhd);
|
||||||
double lhd = m_objects.front()->m_config.layer_height.getFloat();
|
|
||||||
float lh = float(lhd);
|
|
||||||
|
|
||||||
auto ilhs = LevelID(ilhd / SCALING_FACTOR);
|
auto ilhs = coord_t(ilhd / SCALING_FACTOR);
|
||||||
auto lhs = LevelID(lhd / SCALING_FACTOR);
|
|
||||||
const size_t objcount = m_objects.size();
|
const size_t objcount = m_objects.size();
|
||||||
|
|
||||||
const unsigned min_objstatus = 0; // where the per object operations start
|
const unsigned min_objstatus = 0; // where the per object operations start
|
||||||
@ -648,27 +654,33 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// Slicing the model object. This method is oversimplified and needs to
|
// Slicing the model object. This method is oversimplified and needs to
|
||||||
// be compared with the fff slicing algorithm for verification
|
// be compared with the fff slicing algorithm for verification
|
||||||
auto slice_model = [this, ilhs, lhs, ilh, lh](SLAPrintObject& po) {
|
auto slice_model = [this, ilhs, ilh](SLAPrintObject& po) {
|
||||||
TriangleMesh mesh = po.transformed_mesh();
|
TriangleMesh mesh = po.transformed_mesh();
|
||||||
|
|
||||||
// We need to prepare the slice index...
|
// We need to prepare the slice index...
|
||||||
|
|
||||||
|
double lhd = m_objects.front()->m_config.layer_height.getFloat();
|
||||||
|
float lh = float(lhd);
|
||||||
|
auto lhs = coord_t(lhd / SCALING_FACTOR);
|
||||||
|
|
||||||
auto&& bb3d = mesh.bounding_box();
|
auto&& bb3d = mesh.bounding_box();
|
||||||
double minZ = bb3d.min(Z) - po.get_elevation();
|
double minZ = bb3d.min(Z) - po.get_elevation();
|
||||||
double maxZ = bb3d.max(Z);
|
double maxZ = bb3d.max(Z);
|
||||||
|
|
||||||
auto minZs = LevelID(minZ / SCALING_FACTOR);
|
auto minZs = coord_t(minZ / SCALING_FACTOR);
|
||||||
auto maxZs = LevelID(maxZ / SCALING_FACTOR);
|
auto maxZs = coord_t(maxZ / SCALING_FACTOR);
|
||||||
|
|
||||||
po.m_slice_index.clear();
|
po.m_slice_index.clear();
|
||||||
po.m_slice_index.reserve(size_t(maxZs - (minZs + ilhs) / lhs) + 1);
|
po.m_slice_index.reserve(size_t(maxZs - (minZs + ilhs) / lhs) + 1);
|
||||||
po.m_slice_index.emplace_back(minZs + ilhs, float(minZ) + ilh / 2.f, ilh);
|
po.m_slice_index.emplace_back(minZs + ilhs, float(minZ) + ilh / 2.f, ilh);
|
||||||
|
|
||||||
for(LevelID h = minZs + ilhs + lhs; h <= maxZs; h += lhs) {
|
for(coord_t h = minZs + ilhs + lhs; h <= maxZs; h += lhs) {
|
||||||
po.m_slice_index.emplace_back(h, float(h*SCALING_FACTOR) - lh / 2.f, lh);
|
po.m_slice_index.emplace_back(h, float(h*SCALING_FACTOR) - lh / 2.f, lh);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto slindex_it = po.search_slice_index(float(bb3d.min(Z)));
|
// Just get the first record that is form the model:
|
||||||
|
auto slindex_it =
|
||||||
|
po.closest_slice_record(po.m_slice_index, float(bb3d.min(Z)));
|
||||||
|
|
||||||
if(slindex_it == po.m_slice_index.end())
|
if(slindex_it == po.m_slice_index.end())
|
||||||
throw std::runtime_error(L("Slicing had to be stopped "
|
throw std::runtime_error(L("Slicing had to be stopped "
|
||||||
@ -694,7 +706,7 @@ void SLAPrint::process()
|
|||||||
id < po.m_model_slices.size() && mit != po.m_slice_index.end();
|
id < po.m_model_slices.size() && mit != po.m_slice_index.end();
|
||||||
id++)
|
id++)
|
||||||
{
|
{
|
||||||
mit->set_model_slice_idx(id); ++mit;
|
mit->set_model_slice_idx(po, id); ++mit;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -716,6 +728,12 @@ void SLAPrint::process()
|
|||||||
// into the backend cache.
|
// into the backend cache.
|
||||||
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
|
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
|
||||||
|
|
||||||
|
// Hypotetical use of the slice index:
|
||||||
|
// auto bb = po.transformed_mesh().bounding_box();
|
||||||
|
// auto range = po.get_slice_records(bb.min(Z));
|
||||||
|
// std::vector<float> heights; heights.reserve(range.size());
|
||||||
|
// for(auto& record : range) heights.emplace_back(record.slice_level());
|
||||||
|
|
||||||
// calculate heights of slices (slices are calculated already)
|
// calculate heights of slices (slices are calculated already)
|
||||||
const std::vector<float>& heights = po.m_model_height_levels;
|
const std::vector<float>& heights = po.m_model_height_levels;
|
||||||
|
|
||||||
@ -884,7 +902,7 @@ void SLAPrint::process()
|
|||||||
i < sd->support_slices.size() && i < po.m_slice_index.size();
|
i < sd->support_slices.size() && i < po.m_slice_index.size();
|
||||||
++i)
|
++i)
|
||||||
{
|
{
|
||||||
po.m_slice_index[i].set_support_slice_idx(i);
|
po.m_slice_index[i].set_support_slice_idx(po, i);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -896,31 +914,43 @@ void SLAPrint::process()
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Rasterizing the model objects, and their supports
|
// Rasterizing the model objects, and their supports
|
||||||
auto rasterize = [this, max_objstatus]() {
|
auto rasterize = [this, max_objstatus, ilhs]() {
|
||||||
if(canceled()) return;
|
if(canceled()) return;
|
||||||
|
|
||||||
// clear the rasterizer input
|
// clear the rasterizer input
|
||||||
m_printer_input.clear();
|
m_printer_input.clear();
|
||||||
|
|
||||||
|
size_t mx = 0;
|
||||||
for(SLAPrintObject * o : m_objects) {
|
for(SLAPrintObject * o : m_objects) {
|
||||||
LevelID gndlvl = o->get_slice_index().front().key();
|
if(auto m = o->get_slice_index().size() > mx) mx = m;
|
||||||
for(auto& slicerecord : o->get_slice_index()) {
|
}
|
||||||
auto& lyrs = m_printer_input[slicerecord.key() - gndlvl];
|
|
||||||
|
|
||||||
const ExPolygons& objslices = o->get_slices_from_record(slicerecord, soModel);
|
m_printer_input.reserve(mx);
|
||||||
const ExPolygons& supslices = o->get_slices_from_record(slicerecord, soSupport);
|
|
||||||
|
|
||||||
if(!objslices.empty())
|
auto eps = coord_t(SCALED_EPSILON);
|
||||||
lyrs.emplace_back(objslices, o->instances());
|
|
||||||
|
|
||||||
if(!supslices.empty())
|
for(SLAPrintObject * o : m_objects) {
|
||||||
lyrs.emplace_back(supslices, o->instances());
|
coord_t gndlvl = o->get_slice_index().front().print_level() - ilhs;
|
||||||
|
|
||||||
|
for(const SliceRecord& slicerecord : o->get_slice_index()) {
|
||||||
|
coord_t lvlid = slicerecord.print_level() - gndlvl;
|
||||||
|
|
||||||
|
// Neat trick to round the layer levels to the grid.
|
||||||
|
lvlid = eps * (lvlid / eps);
|
||||||
|
|
||||||
|
auto it = std::lower_bound(m_printer_input.begin(),
|
||||||
|
m_printer_input.end(),
|
||||||
|
PrintLayer(lvlid));
|
||||||
|
|
||||||
|
if(it == m_printer_input.end() || it->level() != lvlid)
|
||||||
|
it = m_printer_input.insert(it, PrintLayer(lvlid));
|
||||||
|
|
||||||
|
|
||||||
|
it->add(slicerecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// collect all the keys
|
// collect all the keys
|
||||||
std::vector<long long> keys; keys.reserve(m_printer_input.size());
|
|
||||||
for(auto& e : m_printer_input) keys.emplace_back(e.first);
|
|
||||||
|
|
||||||
// If the raster has vertical orientation, we will flip the coordinates
|
// If the raster has vertical orientation, we will flip the coordinates
|
||||||
bool flpXY = m_printer_config.display_orientation.getInt() ==
|
bool flpXY = m_printer_config.display_orientation.getInt() ==
|
||||||
@ -963,31 +993,36 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// procedure to process one height level. This will run in parallel
|
// procedure to process one height level. This will run in parallel
|
||||||
auto lvlfn =
|
auto lvlfn =
|
||||||
[this, &slck, &keys, &printer, slot, sd, ist, &pst, flpXY]
|
[this, &slck, &printer, slot, sd, ist, &pst, flpXY]
|
||||||
(unsigned level_id)
|
(unsigned level_id)
|
||||||
{
|
{
|
||||||
if(canceled()) return;
|
if(canceled()) return;
|
||||||
|
|
||||||
LayerRefs& lrange = m_printer_input[keys[level_id]];
|
PrintLayer& printlayer = m_printer_input[level_id];
|
||||||
|
|
||||||
// Switch to the appropriate layer in the printer
|
// Switch to the appropriate layer in the printer
|
||||||
printer.begin_layer(level_id);
|
printer.begin_layer(level_id);
|
||||||
|
|
||||||
for(auto& lyrref : lrange) { // for all layers in the current level
|
using Instance = SLAPrintObject::Instance;
|
||||||
if(canceled()) break;
|
|
||||||
const Layer& sl = lyrref.lref; // get the layer reference
|
|
||||||
const LayerCopies& copies = lyrref.copies;
|
|
||||||
|
|
||||||
// Draw all the polygons in the slice to the actual layer.
|
auto draw =
|
||||||
for(auto& cp : copies) {
|
[&printer, flpXY, level_id](ExPolygon& poly, const Instance& tr)
|
||||||
for(ExPolygon slice : sl) {
|
{
|
||||||
// The order is important here:
|
poly.rotate(double(tr.rotation));
|
||||||
// apply rotation before translation...
|
poly.translate(tr.shift(X), tr.shift(Y));
|
||||||
slice.rotate(double(cp.rotation));
|
if(flpXY) swapXY(poly);
|
||||||
slice.translate(cp.shift(X), cp.shift(Y));
|
printer.draw_polygon(poly, level_id);
|
||||||
if(flpXY) swapXY(slice);
|
};
|
||||||
printer.draw_polygon(slice, 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -996,11 +1031,13 @@ void SLAPrint::process()
|
|||||||
|
|
||||||
// Status indication guarded with the spinlock
|
// Status indication guarded with the spinlock
|
||||||
auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
|
auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
|
||||||
{ std::lock_guard<SpinMutex> lck(slck);
|
{
|
||||||
if( st > pst) {
|
std::lock_guard<SpinMutex> lck(slck);
|
||||||
report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]);
|
if( st > pst) {
|
||||||
pst = st;
|
report_status(*this, int(st),
|
||||||
}
|
PRINT_STEP_LABELS[slapsRasterize]);
|
||||||
|
pst = st;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1199,7 +1236,7 @@ void SLAPrint::fill_statistics()
|
|||||||
for (size_t i = 0; i < inst_cnt; ++i)
|
for (size_t i = 0; i < inst_cnt; ++i)
|
||||||
{
|
{
|
||||||
ExPolygon tmp = polygon;
|
ExPolygon tmp = polygon;
|
||||||
tmp.rotate(Geometry::rad2deg(instances[i].rotation));
|
tmp.rotate(double(instances[i].rotation));
|
||||||
tmp.translate(instances[i].shift.x(), instances[i].shift.y());
|
tmp.translate(instances[i].shift.x(), instances[i].shift.y());
|
||||||
polygons_append(polygons, to_polygons(std::move(tmp)));
|
polygons_append(polygons, to_polygons(std::move(tmp)));
|
||||||
}
|
}
|
||||||
@ -1217,33 +1254,33 @@ void SLAPrint::fill_statistics()
|
|||||||
|
|
||||||
// find highest object
|
// find highest object
|
||||||
// Which is a better bet? To compare by max_z or by number of layers in the index?
|
// Which is a better bet? To compare by max_z or by number of layers in the index?
|
||||||
float max_z = 0.;
|
// float max_z = 0.;
|
||||||
size_t max_layers_cnt = 0;
|
size_t max_layers_cnt = 0;
|
||||||
size_t highest_obj_idx = 0;
|
size_t highest_obj_idx = 0;
|
||||||
for (SLAPrintObject *&po : m_objects) {
|
for (SLAPrintObject *&po : m_objects) {
|
||||||
const SLAPrintObject::SliceIndex& slice_index = po->get_slice_index();
|
auto& slice_index = po->get_slice_index();
|
||||||
if (! slice_index.empty()) {
|
if (! slice_index.empty()) {
|
||||||
float z = (-- slice_index.end())->slice_level();
|
// float z = (-- slice_index.end())->slice_level();
|
||||||
size_t cnt = slice_index.size();
|
size_t cnt = slice_index.size();
|
||||||
//if (z > max_z) {
|
//if (z > max_z) {
|
||||||
if (cnt > max_layers_cnt) {
|
if (cnt > max_layers_cnt) {
|
||||||
max_layers_cnt = cnt;
|
max_layers_cnt = cnt;
|
||||||
max_z = z;
|
// max_z = z;
|
||||||
highest_obj_idx = &po - &m_objects.front();
|
highest_obj_idx = &po - &m_objects.front();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SLAPrintObject * highest_obj = m_objects[highest_obj_idx];
|
const SLAPrintObject * highest_obj = m_objects[highest_obj_idx];
|
||||||
const SLAPrintObject::SliceIndex& highest_obj_slice_index = highest_obj->get_slice_index();
|
auto& highest_obj_slice_index = highest_obj->get_slice_index();
|
||||||
|
|
||||||
const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
|
const double delta_fade_time = (init_exp_time - exp_time) / (fade_layers_cnt + 1);
|
||||||
double fade_layer_time = init_exp_time;
|
double fade_layer_time = init_exp_time;
|
||||||
|
|
||||||
int sliced_layer_cnt = 0;
|
int sliced_layer_cnt = 0;
|
||||||
for (const auto& layer : highest_obj_slice_index)
|
for (const SliceRecord& layer : highest_obj_slice_index)
|
||||||
{
|
{
|
||||||
const double l_height = (layer.key() == highest_obj_slice_index.begin()->key()) ? init_layer_height : layer_height;
|
const auto l_height = double(layer.layer_height());
|
||||||
|
|
||||||
// Calculation of the consumed material
|
// Calculation of the consumed material
|
||||||
|
|
||||||
@ -1252,20 +1289,18 @@ void SLAPrint::fill_statistics()
|
|||||||
|
|
||||||
for (SLAPrintObject * po : m_objects)
|
for (SLAPrintObject * po : m_objects)
|
||||||
{
|
{
|
||||||
const SLAPrintObject::_SliceRecord *record = nullptr;
|
const SliceRecord *record = nullptr;
|
||||||
{
|
{
|
||||||
const SLAPrintObject::SliceIndex& index = po->get_slice_index();
|
const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level(), float(EPSILON));
|
||||||
auto it = po->search_slice_index(layer.slice_level() - float(EPSILON));
|
if (!slr.is_valid()) continue;
|
||||||
if (it == index.end() || it->slice_level() > layer.slice_level() + float(EPSILON))
|
record = &slr;
|
||||||
continue;
|
|
||||||
record = &(*it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExPolygons &modelslices = po->get_slices_from_record(*record, soModel);
|
const ExPolygons &modelslices = record->get_slice(soModel);
|
||||||
if (!modelslices.empty())
|
if (!modelslices.empty())
|
||||||
append(model_polygons, get_all_polygons(modelslices, po->instances()));
|
append(model_polygons, get_all_polygons(modelslices, po->instances()));
|
||||||
|
|
||||||
const ExPolygons &supportslices = po->get_slices_from_record(*record, soSupport);
|
const ExPolygons &supportslices = record->get_slice(soSupport);
|
||||||
if (!supportslices.empty())
|
if (!supportslices.empty())
|
||||||
append(supports_polygons, get_all_polygons(supportslices, po->instances()));
|
append(supports_polygons, get_all_polygons(supportslices, po->instances()));
|
||||||
}
|
}
|
||||||
@ -1472,77 +1507,13 @@ const TriangleMesh EMPTY_MESH;
|
|||||||
const ExPolygons EMPTY_SLICE;
|
const ExPolygons EMPTY_SLICE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
|
||||||
|
|
||||||
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
|
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
|
||||||
{
|
{
|
||||||
return m_supportdata->support_points;
|
return m_supportdata->support_points;
|
||||||
}
|
}
|
||||||
|
|
||||||
SLAPrintObject::SliceIndex::iterator
|
|
||||||
SLAPrintObject::search_slice_index(float slice_level)
|
|
||||||
{
|
|
||||||
_SliceRecord query(0, slice_level, 0);
|
|
||||||
auto it = std::lower_bound(m_slice_index.begin(), m_slice_index.end(),
|
|
||||||
query,
|
|
||||||
[](const _SliceRecord& r1, const _SliceRecord& r2)
|
|
||||||
{
|
|
||||||
return r1.slice_level() < r2.slice_level();
|
|
||||||
});
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
SLAPrintObject::SliceIndex::const_iterator
|
|
||||||
SLAPrintObject::search_slice_index(float slice_level) const
|
|
||||||
{
|
|
||||||
_SliceRecord query(0, slice_level, 0);
|
|
||||||
auto it = std::lower_bound(m_slice_index.cbegin(), m_slice_index.cend(),
|
|
||||||
query,
|
|
||||||
[](const _SliceRecord& r1, const _SliceRecord& r2)
|
|
||||||
{
|
|
||||||
return r1.slice_level() < r2.slice_level();
|
|
||||||
});
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
SLAPrintObject::SliceIndex::iterator
|
|
||||||
SLAPrintObject::search_slice_index(SLAPrintObject::_SliceRecord::Key key,
|
|
||||||
bool exact)
|
|
||||||
{
|
|
||||||
_SliceRecord query(key, 0.f, 0.f);
|
|
||||||
auto it = std::lower_bound(m_slice_index.begin(), m_slice_index.end(),
|
|
||||||
query,
|
|
||||||
[](const _SliceRecord& r1, const _SliceRecord& r2)
|
|
||||||
{
|
|
||||||
return r1.key() < r2.key();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return valid iterator only if the keys really match
|
|
||||||
if(exact && it != m_slice_index.end() && it->key() != key)
|
|
||||||
it = m_slice_index.end();
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
SLAPrintObject::SliceIndex::const_iterator
|
|
||||||
SLAPrintObject::search_slice_index(SLAPrintObject::_SliceRecord::Key key,
|
|
||||||
bool exact) const
|
|
||||||
{
|
|
||||||
_SliceRecord query(key, 0.f, 0.f);
|
|
||||||
auto it = std::lower_bound(m_slice_index.cbegin(), m_slice_index.cend(),
|
|
||||||
query,
|
|
||||||
[](const _SliceRecord& r1, const _SliceRecord& r2)
|
|
||||||
{
|
|
||||||
return r1.key() < r2.key();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Return valid iterator only if the keys really match
|
|
||||||
if(exact && it != m_slice_index.end() && it->key() != key)
|
|
||||||
it = m_slice_index.end();
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
|
const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
|
||||||
{
|
{
|
||||||
// assert(is_step_done(slaposSliceSupports));
|
// assert(is_step_done(slaposSliceSupports));
|
||||||
@ -1550,30 +1521,22 @@ const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
|
|||||||
return m_supportdata->support_slices;
|
return m_supportdata->support_slices;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExPolygons &SLAPrintObject::get_slices_from_record(
|
const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
|
||||||
const _SliceRecord &rec,
|
|
||||||
SliceOrigin o) const
|
|
||||||
{
|
{
|
||||||
size_t idx = o == soModel ? rec.get_model_slice_idx() :
|
size_t idx = o == soModel ? m_model_slices_idx :
|
||||||
rec.get_support_slice_idx();
|
m_support_slices_idx;
|
||||||
|
|
||||||
const std::vector<ExPolygons>& v = o == soModel? get_model_slices() :
|
if(m_po == nullptr) return EMPTY_SLICE;
|
||||||
get_support_slices();
|
|
||||||
|
const std::vector<ExPolygons>& v = o == soModel? m_po->get_model_slices() :
|
||||||
|
m_po->get_support_slices();
|
||||||
|
|
||||||
if(idx >= v.size()) return EMPTY_SLICE;
|
if(idx >= v.size()) return EMPTY_SLICE;
|
||||||
|
|
||||||
return idx >= v.size() ? EMPTY_SLICE : v[idx];
|
return idx >= v.size() ? EMPTY_SLICE : v[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExPolygons &SLAPrintObject::get_slices_from_record(
|
const std::vector<SliceRecord> & SLAPrintObject::get_slice_index() const
|
||||||
SLAPrintObject::SliceRecordConstIterator it, SliceOrigin o) const
|
|
||||||
{
|
|
||||||
if(it.is_end()) return EMPTY_SLICE;
|
|
||||||
return get_slices_from_record(*it, o);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<SLAPrintObject::_SliceRecord>&
|
|
||||||
SLAPrintObject::get_slice_index() const
|
|
||||||
{
|
{
|
||||||
// assert(is_step_done(slaposIndexSlices));
|
// assert(is_step_done(slaposIndexSlices));
|
||||||
return m_slice_index;
|
return m_slice_index;
|
||||||
|
@ -34,7 +34,7 @@ using _SLAPrintObjectBase =
|
|||||||
|
|
||||||
// Layers according to quantized height levels. This will be consumed by
|
// Layers according to quantized height levels. This will be consumed by
|
||||||
// the printer (rasterizer) in the SLAPrint class.
|
// the printer (rasterizer) in the SLAPrint class.
|
||||||
using LevelID = long long;
|
// using coord_t = long long;
|
||||||
|
|
||||||
enum SliceOrigin { soSupport, soModel };
|
enum SliceOrigin { soSupport, soModel };
|
||||||
|
|
||||||
@ -94,142 +94,140 @@ public:
|
|||||||
const std::vector<sla::SupportPoint>& get_support_points() const;
|
const std::vector<sla::SupportPoint>& get_support_points() const;
|
||||||
|
|
||||||
// The public Slice record structure. It corresponds to one printable layer.
|
// The public Slice record structure. It corresponds to one printable layer.
|
||||||
// To get the sliced polygons, use SLAPrintObject::get_slices_from_record
|
|
||||||
class SliceRecord {
|
class SliceRecord {
|
||||||
public:
|
public:
|
||||||
using Key = LevelID;
|
// this will be the max limit of size_t
|
||||||
|
static const size_t NONE = size_t(-1);
|
||||||
|
|
||||||
|
static const SliceRecord EMPTY;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Key m_print_z = 0; // Top of the layer
|
coord_t m_print_z = 0; // Top of the layer
|
||||||
float m_slice_z = 0.f; // Exact level of the slice
|
float m_slice_z = 0.f; // Exact level of the slice
|
||||||
float m_height = 0.f; // Height of the sliced layer
|
float m_height = 0.f; // Height of the sliced layer
|
||||||
|
|
||||||
protected:
|
size_t m_model_slices_idx = NONE;
|
||||||
SliceRecord(Key key, float slicez, float height):
|
size_t m_support_slices_idx = NONE;
|
||||||
m_print_z(key), m_slice_z(slicez), m_height(height) {}
|
const SLAPrintObject *m_po = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
SliceRecord(coord_t key, float slicez, float height):
|
||||||
|
m_print_z(key), m_slice_z(slicez), m_height(height) {}
|
||||||
|
|
||||||
// The key will be the integer height level of the top of the layer.
|
// The key will be the integer height level of the top of the layer.
|
||||||
inline Key key() const { return m_print_z; }
|
coord_t print_level() const { return m_print_z; }
|
||||||
|
|
||||||
// Returns the exact floating point Z coordinate of the slice
|
// Returns the exact floating point Z coordinate of the slice
|
||||||
inline float slice_level() const { return m_slice_z; }
|
float slice_level() const { return m_slice_z; }
|
||||||
|
|
||||||
// Returns the current layer height
|
// Returns the current layer height
|
||||||
inline float layer_height() const { return m_height; }
|
float layer_height() const { return m_height; }
|
||||||
|
|
||||||
|
bool is_valid() const { return ! std::isnan(m_slice_z); }
|
||||||
|
|
||||||
|
const SLAPrintObject* print_obj() const { return m_po; }
|
||||||
|
|
||||||
|
// Methods for setting the indices into the slice vectors.
|
||||||
|
void set_model_slice_idx(const SLAPrintObject &po, size_t id) {
|
||||||
|
m_po = &po; m_model_slices_idx = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_support_slice_idx(const SLAPrintObject& po, size_t id) {
|
||||||
|
m_po = &po; m_support_slices_idx = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExPolygons& get_slice(SliceOrigin o) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// An index record referencing the slices
|
template <class T> inline static T level(const SliceRecord& sr) {
|
||||||
// (get_model_slices(), get_support_slices()) where the keys are the height
|
static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
|
||||||
// levels of the model in scaled-clipper coordinates. The levels correspond
|
return std::is_integral<T>::value ? T(sr.print_level()) : T(sr.slice_level());
|
||||||
// to the z coordinate of the object coordinate system.
|
}
|
||||||
class _SliceRecord: public SliceRecord {
|
|
||||||
public:
|
|
||||||
static const size_t NONE = size_t(-1); // this will be the max limit of size_t
|
|
||||||
private:
|
|
||||||
size_t m_model_slices_idx = NONE;
|
|
||||||
size_t m_support_slices_idx = NONE;
|
|
||||||
|
|
||||||
public:
|
template <class T> inline static SliceRecord create_slice_record(T val) {
|
||||||
_SliceRecord(Key key, float slicez, float height):
|
static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
|
||||||
SliceRecord(key, slicez, height) {}
|
return std::is_integral<T>::value ? SliceRecord{ coord_t(val), 0.f, 0.f } : SliceRecord{ 0, float(val), 0.f };
|
||||||
|
}
|
||||||
|
|
||||||
// Methods for setting the indices into the slice vectors.
|
// This is a template method for searching the slice index either by
|
||||||
void set_model_slice_idx(size_t id) { m_model_slices_idx = id; }
|
// an integer key: print_level or a floating point key: slice_level.
|
||||||
void set_support_slice_idx(size_t id) { m_support_slices_idx = id; }
|
// The eps parameter gives the max deviation in + or - direction.
|
||||||
|
//
|
||||||
|
// This method can be used in const or non-const contexts as well.
|
||||||
|
template<class Container, class T>
|
||||||
|
static auto closest_slice_record(
|
||||||
|
Container& cont,
|
||||||
|
T lvl,
|
||||||
|
T eps = std::numeric_limits<T>::max()) -> decltype (cont.begin())
|
||||||
|
{
|
||||||
|
if(cont.empty()) return cont.end();
|
||||||
|
if(cont.size() == 1 && std::abs(level<T>(cont.front()) - lvl) > eps)
|
||||||
|
return cont.end();
|
||||||
|
|
||||||
inline size_t get_model_slice_idx() const { return m_model_slices_idx; }
|
SliceRecord query = create_slice_record(lvl);
|
||||||
inline size_t get_support_slice_idx() const { return m_support_slices_idx; }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Slice index will be a plain vector sorted by the integer height levels
|
auto it = std::lower_bound(cont.begin(), cont.end(), query,
|
||||||
using SliceIndex = std::vector<_SliceRecord>;
|
[](const SliceRecord& r1,
|
||||||
|
const SliceRecord& r2)
|
||||||
|
{
|
||||||
|
return level<T>(r1) < level<T>(r2);
|
||||||
|
});
|
||||||
|
|
||||||
// Retrieve the slice index which is readable only after slaposIndexSlices
|
T diff = std::abs(level<T>(*it) - lvl);
|
||||||
// is done.
|
|
||||||
const SliceIndex& get_slice_index() const;
|
|
||||||
|
|
||||||
// Search slice index for the closest slice to the given level
|
if(it != cont.begin()) {
|
||||||
SliceIndex::iterator search_slice_index(float slice_level);
|
auto it_prev = std::prev(it);
|
||||||
SliceIndex::const_iterator search_slice_index(float slice_level) const;
|
T diff_prev = std::abs(level<T>(*it_prev) - lvl);
|
||||||
|
if(diff_prev < diff) { diff = diff_prev; it = it_prev; }
|
||||||
|
}
|
||||||
|
|
||||||
// Search the slice index for a particular level in integer coordinates.
|
if(diff > eps) it = cont.end();
|
||||||
// If no such layer is present, it will return m_slice_index.end()
|
|
||||||
// This behavior can be suppressed by the second parameter. If it is true
|
return it;
|
||||||
// the method will return the closest (non-equal) record
|
}
|
||||||
SliceIndex::iterator search_slice_index(_SliceRecord::Key key, bool exact = false);
|
|
||||||
SliceIndex::const_iterator search_slice_index(_SliceRecord::Key key, bool = false) const;
|
|
||||||
|
|
||||||
const std::vector<ExPolygons>& get_model_slices() const;
|
const std::vector<ExPolygons>& get_model_slices() const;
|
||||||
const std::vector<ExPolygons>& get_support_slices() const;
|
const std::vector<ExPolygons>& get_support_slices() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Should work as a polymorphic bidirectional iterator to the slice records
|
|
||||||
using SliceRecordConstIterator =
|
|
||||||
IndexBasedIterator<const SliceIndex, const _SliceRecord>;
|
|
||||||
|
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// These two methods should be callable on the client side (e.g. UI thread)
|
// These methods should be callable on the client side (e.g. UI thread)
|
||||||
// when the appropriate steps slaposObjectSlice and slaposSliceSupports
|
// when the appropriate steps slaposObjectSlice and slaposSliceSupports
|
||||||
// are ready. All the print objects are processed before slapsRasterize so
|
// are ready. All the print objects are processed before slapsRasterize so
|
||||||
// it is safe to call them during and/or after slapsRasterize.
|
// it is safe to call them during and/or after slapsRasterize.
|
||||||
//
|
//
|
||||||
// /////////////////////////////////////////////////////////////////////////
|
// /////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Get the slice records from a range of slice levels (inclusive). Floating
|
// Retrieve the slice index.
|
||||||
// point keys are the levels where the model was sliced with the mesh
|
const std::vector<SliceRecord>& get_slice_index() const;
|
||||||
// slicer. Integral keys are the keys of the slice records, which
|
|
||||||
// correspond to the top of each layer.. The end() method of the returned
|
// Search slice index for the closest slice to given print_level.
|
||||||
// range points *after* the last valid element. This is for being
|
// max_epsilon gives the allowable deviation of the returned slice record's
|
||||||
// consistent with std and makeing range based for loops work. use
|
// level.
|
||||||
// std::prev(range.end()) or --range.end() to get the last element.
|
const SliceRecord& closest_slice_to_print_level(
|
||||||
template<class Key> Range<SliceRecordConstIterator>
|
coord_t print_level,
|
||||||
get_slice_records(Key from, Key to = std::numeric_limits<Key>::max()) const
|
coord_t max_epsilon = std::numeric_limits<coord_t>::max()) const
|
||||||
{
|
{
|
||||||
SliceIndex::const_iterator it_from, it_to;
|
auto it = closest_slice_record(m_slice_index, print_level, max_epsilon);
|
||||||
if(std::is_integral<Key>::value) {
|
return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
|
||||||
it_from = search_slice_index(SliceRecord::Key(from));
|
|
||||||
it_to = search_slice_index(SliceRecord::Key(to));
|
|
||||||
} else if(std::is_floating_point<Key>::value) {
|
|
||||||
it_from = search_slice_index(float(from));
|
|
||||||
it_to = search_slice_index(float(to));
|
|
||||||
} else return {
|
|
||||||
SliceRecordConstIterator(m_slice_index, _SliceRecord::NONE ),
|
|
||||||
SliceRecordConstIterator(m_slice_index, _SliceRecord::NONE ),
|
|
||||||
};
|
|
||||||
|
|
||||||
auto start = m_slice_index.begin();
|
|
||||||
|
|
||||||
size_t bidx = it_from == m_slice_index.end() ? _SliceRecord::NONE :
|
|
||||||
size_t(it_from - start);
|
|
||||||
|
|
||||||
size_t eidx = it_to == m_slice_index.end() ? _SliceRecord::NONE :
|
|
||||||
size_t(it_to - start) + 1;
|
|
||||||
|
|
||||||
return {
|
|
||||||
SliceRecordConstIterator(m_slice_index, bidx),
|
|
||||||
SliceRecordConstIterator(m_slice_index, eidx),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all the slice records as a range.
|
// Search slice index for the closest slice to given slice_level.
|
||||||
inline Range<SliceRecordConstIterator> get_slice_records() const {
|
// max_epsilon gives the allowable deviation of the returned slice record's
|
||||||
return {
|
// level. Use SliceRecord::is_valid() to check the result.
|
||||||
SliceRecordConstIterator(m_slice_index, 0),
|
const SliceRecord& closest_slice_to_slice_level(
|
||||||
SliceRecordConstIterator(m_slice_index, m_slice_index.size())
|
float slice_level,
|
||||||
};
|
float max_epsilon = std::numeric_limits<float>::max()) const
|
||||||
|
{
|
||||||
|
auto it = closest_slice_record(m_slice_index, slice_level, max_epsilon);
|
||||||
|
return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExPolygons& get_slices_from_record(SliceRecordConstIterator it,
|
|
||||||
SliceOrigin o) const;
|
|
||||||
|
|
||||||
const ExPolygons& get_slices_from_record(const _SliceRecord& rec,
|
|
||||||
SliceOrigin o) const;
|
|
||||||
protected:
|
protected:
|
||||||
// to be called from SLAPrint only.
|
// to be called from SLAPrint only.
|
||||||
friend class SLAPrint;
|
friend class SLAPrint;
|
||||||
@ -270,7 +268,7 @@ private:
|
|||||||
|
|
||||||
// Exact (float) height levels mapped to the slices. Each record contains
|
// Exact (float) height levels mapped to the slices. Each record contains
|
||||||
// the index to the model and the support slice vectors.
|
// the index to the model and the support slice vectors.
|
||||||
std::vector<_SliceRecord> m_slice_index;
|
std::vector<SliceRecord> m_slice_index;
|
||||||
|
|
||||||
std::vector<float> m_model_height_levels;
|
std::vector<float> m_model_height_levels;
|
||||||
|
|
||||||
@ -283,6 +281,8 @@ private:
|
|||||||
|
|
||||||
using PrintObjects = std::vector<SLAPrintObject*>;
|
using PrintObjects = std::vector<SLAPrintObject*>;
|
||||||
|
|
||||||
|
using SliceRecord = SLAPrintObject::SliceRecord;
|
||||||
|
|
||||||
class TriangleMesh;
|
class TriangleMesh;
|
||||||
|
|
||||||
struct SLAPrintStatistics
|
struct SLAPrintStatistics
|
||||||
@ -328,6 +328,32 @@ private: // Prevents erroneous use by other classes.
|
|||||||
typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;
|
typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;
|
||||||
|
|
||||||
public:
|
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) {}
|
SLAPrint(): m_stepmask(slapsCount, true) {}
|
||||||
|
|
||||||
virtual ~SLAPrint() override { this->clear(); }
|
virtual ~SLAPrint() override { this->clear(); }
|
||||||
@ -361,6 +387,10 @@ public:
|
|||||||
|
|
||||||
std::string validate() const override;
|
std::string validate() const override;
|
||||||
|
|
||||||
|
// 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; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
|
||||||
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
|
||||||
@ -378,23 +408,8 @@ private:
|
|||||||
PrintObjects m_objects;
|
PrintObjects m_objects;
|
||||||
std::vector<bool> m_stepmask;
|
std::vector<bool> m_stepmask;
|
||||||
|
|
||||||
// Definition of the print input map. It consists of the slices indexed
|
// Ready-made data for rasterization.
|
||||||
// with scaled (clipper) Z coordinates. Also contains the instance
|
std::vector<PrintLayer> m_printer_input;
|
||||||
// transformations in scaled and filtered version. This is enough for the
|
|
||||||
// rasterizer to be able to draw every layer in the right position
|
|
||||||
using Layer = ExPolygons;
|
|
||||||
using LayerCopies = std::vector<SLAPrintObject::Instance>;
|
|
||||||
struct LayerRef {
|
|
||||||
std::reference_wrapper<const Layer> lref;
|
|
||||||
std::reference_wrapper<const LayerCopies> copies;
|
|
||||||
LayerRef(const Layer& lyr, const LayerCopies& cp) :
|
|
||||||
lref(std::cref(lyr)), copies(std::cref(cp)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// One level may contain multiple slices from multiple objects and their
|
|
||||||
// supports
|
|
||||||
using LayerRefs = std::vector<LayerRef>;
|
|
||||||
std::map<LevelID, LayerRefs> m_printer_input;
|
|
||||||
|
|
||||||
// The printer itself
|
// The printer itself
|
||||||
SLAPrinterPtr m_printer;
|
SLAPrinterPtr m_printer;
|
||||||
|
@ -5009,24 +5009,27 @@ void GLCanvas3D::_render_sla_slices() const
|
|||||||
instance_transforms.push_back({ to_3d(unscale(inst.shift), 0.), Geometry::rad2deg(inst.rotation) });
|
instance_transforms.push_back({ to_3d(unscale(inst.shift), 0.), Geometry::rad2deg(inst.rotation) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bottom_obj_triangles.empty() || bottom_sup_triangles.empty() || top_obj_triangles.empty() || top_sup_triangles.empty()) && obj->is_step_done(slaposIndexSlices))
|
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())
|
||||||
{
|
{
|
||||||
double layer_height = print->default_object_config().layer_height.value;
|
double layer_height = print->default_object_config().layer_height.value;
|
||||||
double initial_layer_height = print->material_config().initial_layer_height.value;
|
double initial_layer_height = print->material_config().initial_layer_height.value;
|
||||||
LevelID key_zero = obj->get_slice_records().begin()->key();
|
|
||||||
// Slice at the center of the slab starting at clip_min_z will be rendered for the lower plane.
|
|
||||||
LevelID key_low = LevelID((clip_min_z - initial_layer_height + layer_height) / SCALING_FACTOR) + key_zero;
|
|
||||||
// Slice at the center of the slab ending at clip_max_z will be rendered for the upper plane.
|
|
||||||
LevelID key_high = LevelID((clip_max_z - initial_layer_height) / SCALING_FACTOR) + key_zero;
|
|
||||||
auto slice_range = obj->get_slice_records(key_low - LevelID(SCALED_EPSILON), key_high - LevelID(SCALED_EPSILON));
|
|
||||||
auto it_low = slice_range.begin();
|
|
||||||
auto it_high = std::prev(slice_range.end());
|
|
||||||
// Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts.
|
|
||||||
double plane_shift_z = 0.002f;
|
|
||||||
|
|
||||||
if (! it_low.is_end() && it_low->key() < key_low + LevelID(SCALED_EPSILON)) {
|
coord_t key_zero = obj->get_slice_index().front().print_level();
|
||||||
const ExPolygons& obj_bottom = obj->get_slices_from_record(it_low, soModel);
|
// Slice at the center of the slab starting at clip_min_z will be rendered for the lower plane.
|
||||||
const ExPolygons& sup_bottom = obj->get_slices_from_record(it_low, soSupport);
|
coord_t key_low = coord_t((clip_min_z - initial_layer_height + layer_height) / SCALING_FACTOR) + key_zero;
|
||||||
|
// Slice at the center of the slab ending at clip_max_z will be rendered for the upper plane.
|
||||||
|
coord_t key_high = coord_t((clip_max_z - initial_layer_height) / SCALING_FACTOR) + key_zero;
|
||||||
|
|
||||||
|
const SliceRecord& slice_low = obj->closest_slice_to_print_level(key_low, coord_t(SCALED_EPSILON));
|
||||||
|
const SliceRecord& slice_high = obj->closest_slice_to_print_level(key_high, coord_t(SCALED_EPSILON));
|
||||||
|
|
||||||
|
// Offset to avoid OpenGL Z fighting between the object's horizontal surfaces and the triangluated surfaces of the cuts.
|
||||||
|
double plane_shift_z = 0.002;
|
||||||
|
|
||||||
|
if (slice_low.is_valid()) {
|
||||||
|
const ExPolygons& obj_bottom = slice_low.get_slice(soModel);
|
||||||
|
const ExPolygons& sup_bottom = slice_low.get_slice(soSupport);
|
||||||
// calculate model bottom cap
|
// calculate model bottom cap
|
||||||
if (bottom_obj_triangles.empty() && !obj_bottom.empty())
|
if (bottom_obj_triangles.empty() && !obj_bottom.empty())
|
||||||
bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, true);
|
bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z - plane_shift_z, true);
|
||||||
@ -5035,9 +5038,9 @@ void GLCanvas3D::_render_sla_slices() const
|
|||||||
bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, true);
|
bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z - plane_shift_z, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! it_high.is_end() && it_high->key() < key_high + LevelID(SCALED_EPSILON)) {
|
if (slice_high.is_valid()) {
|
||||||
const ExPolygons& obj_top = obj->get_slices_from_record(it_high, soModel);
|
const ExPolygons& obj_top = slice_high.get_slice(soModel);
|
||||||
const ExPolygons& sup_top = obj->get_slices_from_record(it_high, soSupport);
|
const ExPolygons& sup_top = slice_high.get_slice(soSupport);
|
||||||
// calculate model top cap
|
// calculate model top cap
|
||||||
if (top_obj_triangles.empty() && !obj_top.empty())
|
if (top_obj_triangles.empty() && !obj_top.empty())
|
||||||
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, false);
|
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, false);
|
||||||
|
@ -772,12 +772,11 @@ void Preview::load_print_as_sla()
|
|||||||
std::vector<double> zs;
|
std::vector<double> zs;
|
||||||
double initial_layer_height = print->material_config().initial_layer_height.value;
|
double initial_layer_height = print->material_config().initial_layer_height.value;
|
||||||
for (const SLAPrintObject* obj : print->objects())
|
for (const SLAPrintObject* obj : print->objects())
|
||||||
if (obj->is_step_done(slaposIndexSlices))
|
if (obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty())
|
||||||
{
|
{
|
||||||
auto slicerecords = obj->get_slice_records();
|
auto low_coord = obj->get_slice_index().front().print_level();
|
||||||
auto low_coord = slicerecords.begin()->key();
|
for (auto& rec : obj->get_slice_index())
|
||||||
for (auto& rec : slicerecords)
|
zs.emplace_back(initial_layer_height + (rec.print_level() - low_coord) * SCALING_FACTOR);
|
||||||
zs.emplace_back(initial_layer_height + (rec.key() - low_coord) * SCALING_FACTOR);
|
|
||||||
}
|
}
|
||||||
sort_remove_duplicates(zs);
|
sort_remove_duplicates(zs);
|
||||||
|
|
||||||
|
@ -231,13 +231,7 @@ std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
|
|||||||
|
|
||||||
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
|
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
float size = (float)box.max_size();
|
render_grabbers((float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
|
||||||
|
|
||||||
for (int i = 0; i < (int)m_grabbers.size(); ++i)
|
|
||||||
{
|
|
||||||
if (m_grabbers[i].enabled)
|
|
||||||
m_grabbers[i].render((m_hover_id == i), size);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoBase::render_grabbers(float size) const
|
void GLGizmoBase::render_grabbers(float size) const
|
||||||
@ -251,7 +245,7 @@ void GLGizmoBase::render_grabbers(float size) const
|
|||||||
|
|
||||||
void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
|
void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
|
||||||
{
|
{
|
||||||
float size = (float)box.max_size();
|
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
|
for (unsigned int i = 0; i < (unsigned int)m_grabbers.size(); ++i)
|
||||||
{
|
{
|
||||||
@ -261,7 +255,7 @@ void GLGizmoBase::render_grabbers_for_picking(const BoundingBoxf3& box) const
|
|||||||
m_grabbers[i].color[0] = color[0];
|
m_grabbers[i].color[0] = color[0];
|
||||||
m_grabbers[i].color[1] = color[1];
|
m_grabbers[i].color[1] = color[1];
|
||||||
m_grabbers[i].color[2] = color[2];
|
m_grabbers[i].color[2] = color[2];
|
||||||
m_grabbers[i].render_for_picking(size);
|
m_grabbers[i].render_for_picking(mean_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,7 +173,7 @@ void GLGizmoCut::on_render(const Selection& selection) const
|
|||||||
::glEnd();
|
::glEnd();
|
||||||
|
|
||||||
std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color);
|
std::copy(std::begin(GrabberColor), std::end(GrabberColor), m_grabbers[0].color);
|
||||||
m_grabbers[0].render(m_hover_id == 0, box.max_size());
|
m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLGizmoCut::on_render_for_picking(const Selection& selection) const
|
void GLGizmoCut::on_render_for_picking(const Selection& selection) const
|
||||||
|
@ -216,7 +216,8 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
|
|||||||
if (m_quadric == nullptr)
|
if (m_quadric == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size((float)box.max_size()) : (double)m_grabbers[axis].get_half_size((float)box.max_size());
|
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
|
||||||
|
double size = m_dragging ? (double)m_grabbers[axis].get_dragging_half_size(mean_size) : (double)m_grabbers[axis].get_half_size(mean_size);
|
||||||
|
|
||||||
float color[3];
|
float color[3];
|
||||||
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float));
|
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float));
|
||||||
|
@ -307,7 +307,8 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
|
|||||||
if (m_quadric == nullptr)
|
if (m_quadric == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size((float)box.max_size()) : (double)m_grabbers[0].get_half_size((float)box.max_size());
|
float mean_size = (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0);
|
||||||
|
double size = m_dragging ? (double)m_grabbers[0].get_dragging_half_size(mean_size) : (double)m_grabbers[0].get_half_size(mean_size);
|
||||||
|
|
||||||
float color[3];
|
float color[3];
|
||||||
::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float));
|
::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float));
|
||||||
|
@ -116,8 +116,6 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
|
|||||||
Vec3d angles = Vec3d::Zero();
|
Vec3d angles = Vec3d::Zero();
|
||||||
Transform3d offsets_transform = Transform3d::Identity();
|
Transform3d offsets_transform = Transform3d::Identity();
|
||||||
|
|
||||||
Vec3d grabber_size = Vec3d::Zero();
|
|
||||||
|
|
||||||
if (single_instance)
|
if (single_instance)
|
||||||
{
|
{
|
||||||
// calculate bounding box in instance local reference system
|
// calculate bounding box in instance local reference system
|
||||||
@ -135,7 +133,6 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
|
|||||||
angles = v->get_instance_rotation();
|
angles = v->get_instance_rotation();
|
||||||
// consider rotation+mirror only components of the transform for offsets
|
// consider rotation+mirror only components of the transform for offsets
|
||||||
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
|
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
|
||||||
grabber_size = v->get_instance_transformation().get_matrix(true, true, false, true) * box.size();
|
|
||||||
}
|
}
|
||||||
else if (single_volume)
|
else if (single_volume)
|
||||||
{
|
{
|
||||||
@ -145,13 +142,9 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
|
|||||||
angles = Geometry::extract_euler_angles(transform);
|
angles = Geometry::extract_euler_angles(transform);
|
||||||
// consider rotation+mirror only components of the transform for offsets
|
// consider rotation+mirror only components of the transform for offsets
|
||||||
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
|
offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror());
|
||||||
grabber_size = v->get_volume_transformation().get_matrix(true, true, false, true) * box.size();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
box = selection.get_bounding_box();
|
box = selection.get_bounding_box();
|
||||||
grabber_size = box.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_box = box;
|
m_box = box;
|
||||||
|
|
||||||
@ -196,7 +189,9 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
|
|||||||
|
|
||||||
::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
|
::glLineWidth((m_hover_id != -1) ? 2.0f : 1.5f);
|
||||||
|
|
||||||
float grabber_mean_size = (float)(grabber_size(0) + grabber_size(1) + grabber_size(2)) / 3.0f;
|
const BoundingBoxf3& selection_box = selection.get_bounding_box();
|
||||||
|
|
||||||
|
float grabber_mean_size = (float)((selection_box.size()(0) + selection_box.size()(1) + selection_box.size()(2)) / 3.0);
|
||||||
|
|
||||||
if (m_hover_id == -1)
|
if (m_hover_id == -1)
|
||||||
{
|
{
|
||||||
|
@ -56,6 +56,14 @@ bool ImGuiWrapper::init()
|
|||||||
|
|
||||||
void ImGuiWrapper::set_language(const std::string &language)
|
void ImGuiWrapper::set_language(const std::string &language)
|
||||||
{
|
{
|
||||||
|
if (m_new_frame_open) {
|
||||||
|
// ImGUI internally locks the font between NewFrame() and EndFrame()
|
||||||
|
// NewFrame() might've been called here because of input from the 3D scene;
|
||||||
|
// call EndFrame()
|
||||||
|
ImGui::EndFrame();
|
||||||
|
m_new_frame_open = false;
|
||||||
|
}
|
||||||
|
|
||||||
const ImWchar *ranges = nullptr;
|
const ImWchar *ranges = nullptr;
|
||||||
size_t idx = language.find('_');
|
size_t idx = language.find('_');
|
||||||
std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);
|
std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);
|
||||||
|
Loading…
Reference in New Issue
Block a user