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

View File

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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