This commit is contained in:
bubnikv 2019-03-26 18:00:23 +01:00
commit f65eb9afdb
13 changed files with 304 additions and 303 deletions

View File

@ -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.
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.
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.
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` (?).

View File

@ -28,8 +28,8 @@ namespace Slic3r {
//-----------------------------------------------------------
// legacy code from Clipper documentation
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons& expolygons);
void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons);
void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons *expolygons);
Slic3r::ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree& polytree);
//-----------------------------------------------------------
ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input);
@ -228,4 +228,4 @@ Polygons top_level_islands(const Slic3r::Polygons &polygons);
}
#endif
#endif

View File

@ -56,8 +56,18 @@ public:
}
};
template<class Vector,
class Value = typename Vector::value_type>
/// An std compatible random access iterator which uses indices to the source
/// 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 {
static const size_t NONE = size_t(-1);
@ -110,6 +120,8 @@ public:
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 Value & operator*() const {
@ -122,6 +134,7 @@ public:
return &m_index_ref.get().operator[](m_idx);
}
/// If both iterators point past the container, they are equal...
inline bool operator ==(const IndexBasedIterator& other) {
size_t e = m_index_ref.get().size();
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 {
It from, to;
public:
// The class is ready for range based for loops.
It begin() const { return from; }
It end() const { return to; }
// The iterator type can be obtained this way.
using Type = It;
Range() = default;
Range(It &&b, 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 bool empty() const { return size() == 0; }
};

View File

@ -587,6 +587,15 @@ void swapXY(ExPolygon& expoly) {
std::string SLAPrint::validate() const
{
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());
double pinhead_width =
@ -596,7 +605,7 @@ std::string SLAPrint::validate() const
cfg.head_penetration_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 "";
@ -623,11 +632,8 @@ void SLAPrint::process()
// shortcut to initial layer height
double ilhd = m_material_config.initial_layer_height.getFloat();
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 lhs = LevelID(lhd / SCALING_FACTOR);
auto ilhs = coord_t(ilhd / SCALING_FACTOR);
const size_t objcount = m_objects.size();
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
// 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();
// 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();
double minZ = bb3d.min(Z) - po.get_elevation();
double maxZ = bb3d.max(Z);
auto minZs = LevelID(minZ / SCALING_FACTOR);
auto maxZs = LevelID(maxZ / SCALING_FACTOR);
auto minZs = coord_t(minZ / SCALING_FACTOR);
auto maxZs = coord_t(maxZ / SCALING_FACTOR);
po.m_slice_index.clear();
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);
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);
}
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())
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++)
{
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.
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)
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)
{
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
auto rasterize = [this, max_objstatus]() {
auto rasterize = [this, max_objstatus, ilhs]() {
if(canceled()) return;
// clear the rasterizer input
m_printer_input.clear();
size_t mx = 0;
for(SLAPrintObject * o : m_objects) {
LevelID gndlvl = o->get_slice_index().front().key();
for(auto& slicerecord : o->get_slice_index()) {
auto& lyrs = m_printer_input[slicerecord.key() - gndlvl];
if(auto m = o->get_slice_index().size() > mx) mx = m;
}
const ExPolygons& objslices = o->get_slices_from_record(slicerecord, soModel);
const ExPolygons& supslices = o->get_slices_from_record(slicerecord, soSupport);
m_printer_input.reserve(mx);
if(!objslices.empty())
lyrs.emplace_back(objslices, o->instances());
auto eps = coord_t(SCALED_EPSILON);
if(!supslices.empty())
lyrs.emplace_back(supslices, o->instances());
for(SLAPrintObject * o : m_objects) {
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
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
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
auto lvlfn =
[this, &slck, &keys, &printer, slot, sd, ist, &pst, flpXY]
[this, &slck, &printer, slot, sd, ist, &pst, flpXY]
(unsigned level_id)
{
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
printer.begin_layer(level_id);
for(auto& lyrref : lrange) { // for all layers in the current level
if(canceled()) break;
const Layer& sl = lyrref.lref; // get the layer reference
const LayerCopies& copies = lyrref.copies;
using Instance = SLAPrintObject::Instance;
// Draw all the polygons in the slice to the actual layer.
for(auto& cp : copies) {
for(ExPolygon slice : sl) {
// The order is important here:
// apply rotation before translation...
slice.rotate(double(cp.rotation));
slice.translate(cp.shift(X), cp.shift(Y));
if(flpXY) swapXY(slice);
printer.draw_polygon(slice, level_id);
}
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);
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);
}
}
@ -996,11 +1031,13 @@ void SLAPrint::process()
// Status indication guarded with the spinlock
auto st = ist + unsigned(sd*level_id*slot/m_printer_input.size());
{ std::lock_guard<SpinMutex> lck(slck);
if( st > pst) {
report_status(*this, int(st), PRINT_STEP_LABELS[slapsRasterize]);
pst = st;
}
{
std::lock_guard<SpinMutex> lck(slck);
if( st > pst) {
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)
{
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());
polygons_append(polygons, to_polygons(std::move(tmp)));
}
@ -1217,33 +1254,33 @@ void SLAPrint::fill_statistics()
// 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.;
// float max_z = 0.;
size_t max_layers_cnt = 0;
size_t highest_obj_idx = 0;
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()) {
float z = (-- slice_index.end())->slice_level();
// 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;
// max_z = z;
highest_obj_idx = &po - &m_objects.front();
}
}
}
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);
double fade_layer_time = init_exp_time;
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
@ -1252,20 +1289,18 @@ void SLAPrint::fill_statistics()
for (SLAPrintObject * po : m_objects)
{
const SLAPrintObject::_SliceRecord *record = nullptr;
const SliceRecord *record = nullptr;
{
const SLAPrintObject::SliceIndex& index = po->get_slice_index();
auto it = po->search_slice_index(layer.slice_level() - float(EPSILON));
if (it == index.end() || it->slice_level() > layer.slice_level() + float(EPSILON))
continue;
record = &(*it);
const SliceRecord& slr = po->closest_slice_to_slice_level(layer.slice_level(), float(EPSILON));
if (!slr.is_valid()) continue;
record = &slr;
}
const ExPolygons &modelslices = po->get_slices_from_record(*record, soModel);
const ExPolygons &modelslices = record->get_slice(soModel);
if (!modelslices.empty())
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())
append(supports_polygons, get_all_polygons(supportslices, po->instances()));
}
@ -1472,77 +1507,13 @@ const TriangleMesh EMPTY_MESH;
const ExPolygons EMPTY_SLICE;
}
const SliceRecord SliceRecord::EMPTY(0, std::nanf(""), 0.f);
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
{
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
{
// assert(is_step_done(slaposSliceSupports));
@ -1550,30 +1521,22 @@ const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
return m_supportdata->support_slices;
}
const ExPolygons &SLAPrintObject::get_slices_from_record(
const _SliceRecord &rec,
SliceOrigin o) const
const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const
{
size_t idx = o == soModel ? rec.get_model_slice_idx() :
rec.get_support_slice_idx();
size_t idx = o == soModel ? m_model_slices_idx :
m_support_slices_idx;
const std::vector<ExPolygons>& v = o == soModel? get_model_slices() :
get_support_slices();
if(m_po == nullptr) return EMPTY_SLICE;
const std::vector<ExPolygons>& v = o == soModel? m_po->get_model_slices() :
m_po->get_support_slices();
if(idx >= v.size()) return EMPTY_SLICE;
return idx >= v.size() ? EMPTY_SLICE : v[idx];
}
const ExPolygons &SLAPrintObject::get_slices_from_record(
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
const std::vector<SliceRecord> & SLAPrintObject::get_slice_index() const
{
// assert(is_step_done(slaposIndexSlices));
return m_slice_index;

View File

@ -34,7 +34,7 @@ using _SLAPrintObjectBase =
// Layers according to quantized height levels. This will be consumed by
// the printer (rasterizer) in the SLAPrint class.
using LevelID = long long;
// using coord_t = long long;
enum SliceOrigin { soSupport, soModel };
@ -94,142 +94,140 @@ public:
const std::vector<sla::SupportPoint>& get_support_points() const;
// The public Slice record structure. It corresponds to one printable layer.
// To get the sliced polygons, use SLAPrintObject::get_slices_from_record
class SliceRecord {
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:
Key m_print_z = 0; // Top of the layer
float m_slice_z = 0.f; // Exact level of the slice
float m_height = 0.f; // Height of the sliced layer
coord_t m_print_z = 0; // Top of the layer
float m_slice_z = 0.f; // Exact level of the slice
float m_height = 0.f; // Height of the sliced layer
protected:
SliceRecord(Key key, float slicez, float height):
m_print_z(key), m_slice_z(slicez), m_height(height) {}
size_t m_model_slices_idx = NONE;
size_t m_support_slices_idx = NONE;
const SLAPrintObject *m_po = nullptr;
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.
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
inline float slice_level() const { return m_slice_z; }
float slice_level() const { return m_slice_z; }
// 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:
// An index record referencing the slices
// (get_model_slices(), get_support_slices()) where the keys are the height
// levels of the model in scaled-clipper coordinates. The levels correspond
// 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;
template <class T> inline static T level(const SliceRecord& sr) {
static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
return std::is_integral<T>::value ? T(sr.print_level()) : T(sr.slice_level());
}
public:
_SliceRecord(Key key, float slicez, float height):
SliceRecord(key, slicez, height) {}
template <class T> inline static SliceRecord create_slice_record(T val) {
static_assert(std::is_arithmetic<T>::value, "Arithmetic only!");
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.
void set_model_slice_idx(size_t id) { m_model_slices_idx = id; }
void set_support_slice_idx(size_t id) { m_support_slices_idx = id; }
// This is a template method for searching the slice index either by
// an integer key: print_level or a floating point key: slice_level.
// 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; }
inline size_t get_support_slice_idx() const { return m_support_slices_idx; }
};
SliceRecord query = create_slice_record(lvl);
// Slice index will be a plain vector sorted by the integer height levels
using SliceIndex = std::vector<_SliceRecord>;
auto it = std::lower_bound(cont.begin(), cont.end(), query,
[](const SliceRecord& r1,
const SliceRecord& r2)
{
return level<T>(r1) < level<T>(r2);
});
// Retrieve the slice index which is readable only after slaposIndexSlices
// is done.
const SliceIndex& get_slice_index() const;
T diff = std::abs(level<T>(*it) - lvl);
// Search slice index for the closest slice to the given level
SliceIndex::iterator search_slice_index(float slice_level);
SliceIndex::const_iterator search_slice_index(float slice_level) const;
if(it != cont.begin()) {
auto it_prev = std::prev(it);
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 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
// 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;
if(diff > eps) it = cont.end();
return it;
}
const std::vector<ExPolygons>& get_model_slices() const;
const std::vector<ExPolygons>& get_support_slices() const;
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
// are ready. All the print objects are processed before slapsRasterize so
// it is safe to call them during and/or after slapsRasterize.
//
// /////////////////////////////////////////////////////////////////////////
// Get the slice records from a range of slice levels (inclusive). Floating
// point keys are the levels where the model was sliced with the mesh
// slicer. Integral keys are the keys of the slice records, which
// correspond to the top of each layer.. The end() method of the returned
// range points *after* the last valid element. This is for being
// consistent with std and makeing range based for loops work. use
// std::prev(range.end()) or --range.end() to get the last element.
template<class Key> Range<SliceRecordConstIterator>
get_slice_records(Key from, Key to = std::numeric_limits<Key>::max()) const
// Retrieve the slice index.
const std::vector<SliceRecord>& get_slice_index() const;
// Search slice index for the closest slice to given print_level.
// max_epsilon gives the allowable deviation of the returned slice record's
// level.
const SliceRecord& closest_slice_to_print_level(
coord_t print_level,
coord_t max_epsilon = std::numeric_limits<coord_t>::max()) const
{
SliceIndex::const_iterator it_from, it_to;
if(std::is_integral<Key>::value) {
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),
};
auto it = closest_slice_record(m_slice_index, print_level, max_epsilon);
return it == m_slice_index.end() ? SliceRecord::EMPTY : *it;
}
// Get all the slice records as a range.
inline Range<SliceRecordConstIterator> get_slice_records() const {
return {
SliceRecordConstIterator(m_slice_index, 0),
SliceRecordConstIterator(m_slice_index, m_slice_index.size())
};
// Search slice index for the closest slice to given slice_level.
// max_epsilon gives the allowable deviation of the returned slice record's
// level. Use SliceRecord::is_valid() to check the result.
const SliceRecord& closest_slice_to_slice_level(
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:
// to be called from SLAPrint only.
friend class SLAPrint;
@ -270,7 +268,7 @@ private:
// Exact (float) height levels mapped to the slices. Each record contains
// 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;
@ -283,6 +281,8 @@ private:
using PrintObjects = std::vector<SLAPrintObject*>;
using SliceRecord = SLAPrintObject::SliceRecord;
class TriangleMesh;
struct SLAPrintStatistics
@ -328,6 +328,32 @@ private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState<SLAPrintStep, slapsCount> Inherited;
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(); }
@ -361,6 +387,10 @@ public:
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:
using SLAPrinter = FilePrinter<FilePrinterFormat::SLA_PNGZIP>;
using SLAPrinterPtr = std::unique_ptr<SLAPrinter>;
@ -378,23 +408,8 @@ private:
PrintObjects m_objects;
std::vector<bool> m_stepmask;
// Definition of the print input map. It consists of the slices indexed
// with scaled (clipper) Z coordinates. Also contains the instance
// 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;
// Ready-made data for rasterization.
std::vector<PrintLayer> m_printer_input;
// The printer itself
SLAPrinterPtr m_printer;

View File

@ -5009,24 +5009,27 @@ void GLCanvas3D::_render_sla_slices() const
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 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)) {
const ExPolygons& obj_bottom = obj->get_slices_from_record(it_low, soModel);
const ExPolygons& sup_bottom = obj->get_slices_from_record(it_low, soSupport);
coord_t key_zero = obj->get_slice_index().front().print_level();
// Slice at the center of the slab starting at clip_min_z will be rendered for the lower plane.
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
if (bottom_obj_triangles.empty() && !obj_bottom.empty())
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);
}
if (! it_high.is_end() && it_high->key() < key_high + LevelID(SCALED_EPSILON)) {
const ExPolygons& obj_top = obj->get_slices_from_record(it_high, soModel);
const ExPolygons& sup_top = obj->get_slices_from_record(it_high, soSupport);
if (slice_high.is_valid()) {
const ExPolygons& obj_top = slice_high.get_slice(soModel);
const ExPolygons& sup_top = slice_high.get_slice(soSupport);
// calculate model top cap
if (top_obj_triangles.empty() && !obj_top.empty())
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z + plane_shift_z, false);

View File

@ -772,12 +772,11 @@ 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))
if (obj->is_step_done(slaposIndexSlices) && !obj->get_slice_index().empty())
{
auto slicerecords = obj->get_slice_records();
auto low_coord = slicerecords.begin()->key();
for (auto& rec : slicerecords)
zs.emplace_back(initial_layer_height + (rec.key() - low_coord) * SCALING_FACTOR);
auto low_coord = obj->get_slice_index().front().print_level();
for (auto& rec : obj->get_slice_index())
zs.emplace_back(initial_layer_height + (rec.print_level() - low_coord) * SCALING_FACTOR);
}
sort_remove_duplicates(zs);

View File

@ -231,13 +231,7 @@ std::array<float, 3> GLGizmoBase::picking_color_component(unsigned int id) const
void GLGizmoBase::render_grabbers(const BoundingBoxf3& box) const
{
float size = (float)box.max_size();
for (int i = 0; i < (int)m_grabbers.size(); ++i)
{
if (m_grabbers[i].enabled)
m_grabbers[i].render((m_hover_id == i), size);
}
render_grabbers((float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0));
}
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
{
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)
{
@ -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[1] = color[1];
m_grabbers[i].color[2] = color[2];
m_grabbers[i].render_for_picking(size);
m_grabbers[i].render_for_picking(mean_size);
}
}
}

View File

@ -173,7 +173,7 @@ void GLGizmoCut::on_render(const Selection& selection) const
::glEnd();
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

View File

@ -216,7 +216,8 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
if (m_quadric == nullptr)
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];
::memcpy((void*)color, (const void*)m_grabbers[axis].color, 3 * sizeof(float));

View File

@ -307,7 +307,8 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
if (m_quadric == nullptr)
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];
::memcpy((void*)color, (const void*)m_grabbers[0].color, 3 * sizeof(float));

View File

@ -116,8 +116,6 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
Vec3d angles = Vec3d::Zero();
Transform3d offsets_transform = Transform3d::Identity();
Vec3d grabber_size = Vec3d::Zero();
if (single_instance)
{
// 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();
// consider rotation+mirror only components of the transform for offsets
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)
{
@ -145,13 +142,9 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
angles = Geometry::extract_euler_angles(transform);
// consider rotation+mirror only components of the transform for offsets
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
{
box = selection.get_bounding_box();
grabber_size = box.size();
}
m_box = box;
@ -196,7 +189,9 @@ void GLGizmoScale3D::on_render(const Selection& selection) const
::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)
{

View File

@ -56,6 +56,14 @@ bool ImGuiWrapper::init()
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;
size_t idx = language.find('_');
std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx);