Merge branch 'master' of https://github.com/prusa3d/Slic3r into et_multivolume_models

This commit is contained in:
Enrico Turri 2019-03-25 08:32:05 +01:00
commit 03aeb0d386
15 changed files with 612 additions and 249 deletions

View file

@ -56,6 +56,113 @@ public:
}
};
template<class Vector,
class Value = typename Vector::value_type>
class IndexBasedIterator {
static const size_t NONE = size_t(-1);
std::reference_wrapper<Vector> m_index_ref;
size_t m_idx = NONE;
public:
using value_type = Value;
using pointer = Value *;
using reference = Value &;
using difference_type = long;
using iterator_category = std::random_access_iterator_tag;
inline explicit
IndexBasedIterator(Vector& index, size_t idx):
m_index_ref(index), m_idx(idx) {}
// Post increment
inline IndexBasedIterator operator++(int) {
IndexBasedIterator cpy(*this); ++m_idx; return cpy;
}
inline IndexBasedIterator operator--(int) {
IndexBasedIterator cpy(*this); --m_idx; return cpy;
}
inline IndexBasedIterator& operator++() {
++m_idx; return *this;
}
inline IndexBasedIterator& operator--() {
--m_idx; return *this;
}
inline IndexBasedIterator& operator+=(difference_type l) {
m_idx += size_t(l); return *this;
}
inline IndexBasedIterator operator+(difference_type l) {
auto cpy = *this; cpy += l; return cpy;
}
inline IndexBasedIterator& operator-=(difference_type l) {
m_idx -= size_t(l); return *this;
}
inline IndexBasedIterator operator-(difference_type l) {
auto cpy = *this; cpy -= l; return cpy;
}
operator difference_type() { return difference_type(m_idx); }
inline bool is_end() const { return m_idx >= m_index_ref.get().size();}
inline Value & operator*() const {
assert(m_idx < m_index_ref.get().size());
return m_index_ref.get().operator[](m_idx);
}
inline Value * operator->() const {
assert(m_idx < m_index_ref.get().size());
return &m_index_ref.get().operator[](m_idx);
}
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);
}
inline bool operator !=(const IndexBasedIterator& other) {
return !(*this == other);
}
inline bool operator <=(const IndexBasedIterator& other) {
return (m_idx < other.m_idx) || (*this == other);
}
inline bool operator <(const IndexBasedIterator& other) {
return m_idx < other.m_idx && (*this != other);
}
inline bool operator >=(const IndexBasedIterator& other) {
return m_idx > other.m_idx || *this == other;
}
inline bool operator >(const IndexBasedIterator& other) {
return m_idx > other.m_idx && *this != other;
}
};
template<class It> class Range {
It from, to;
public:
It begin() const { return from; }
It end() const { return to; }
using Type = It;
Range() = default;
Range(It &&b, It &&e):
from(std::forward<It>(b)), to(std::forward<It>(e)) {}
inline size_t size() const { return end() - begin(); }
inline bool empty() const { return size() == 0; }
};
}
#endif // MTUTILS_HPP

View file

@ -2240,6 +2240,18 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const
return ret;
}
SlicedSupports SLASupportTree::slice(const std::vector<float> &heights,
float cr) const
{
TriangleMesh fullmesh = m_impl->merged_mesh();
fullmesh.merge(get_pad());
TriangleMeshSlicer slicer(&fullmesh);
SlicedSupports ret;
slicer.slice(heights, cr, &ret, get().ctl().cancelfn);
return ret;
}
const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate,
const PoolConfig& pcfg) const
{

View file

@ -181,6 +181,8 @@ public:
/// Get the sliced 2d layers of the support geometry.
SlicedSupports slice(float layerh, float init_layerh = -1.0) const;
SlicedSupports slice(const std::vector<float>&, float closing_radius) const;
/// Adding the "pad" (base pool) under the supports
const TriangleMesh& add_pad(const SliceLayer& baseplate,
const PoolConfig& pcfg) const;

View file

@ -30,7 +30,6 @@ public:
std::vector<sla::SupportPoint> support_points; // all the support points (manual/auto)
SupportTreePtr support_tree_ptr; // the supports
SlicedSupports support_slices; // sliced supports
std::vector<LevelID> level_ids;
inline SupportData(const TriangleMesh& trmesh): emesh(trmesh) {}
};
@ -567,6 +566,18 @@ sla::SupportConfig make_support_cfg(const SLAPrintObjectConfig& c) {
return scfg;
}
sla::PoolConfig make_pool_config(const SLAPrintObjectConfig& c) {
sla::PoolConfig pcfg;
pcfg.min_wall_thickness_mm = c.pad_wall_thickness.getFloat();
pcfg.wall_slope = c.pad_wall_slope.getFloat();
pcfg.edge_radius_mm = c.pad_edge_radius.getFloat();
pcfg.max_merge_distance_mm = c.pad_max_merge_distance.getFloat();
pcfg.min_wall_height_mm = c.pad_wall_height.getFloat();
return pcfg;
}
void swapXY(ExPolygon& expoly) {
for(auto& p : expoly.contour.points) std::swap(p(X), p(Y));
for(auto& h : expoly.holes) for(auto& p : h.points) std::swap(p(X), p(Y));
@ -591,25 +602,9 @@ std::string SLAPrint::validate() const
return "";
}
std::vector<float> SLAPrint::calculate_heights(const BoundingBoxf3& bb3d,
float elevation,
float initial_layer_height,
float layer_height) const
{
std::vector<float> heights;
float minZ = float(bb3d.min(Z)) - float(elevation);
float maxZ = float(bb3d.max(Z));
auto flh = float(layer_height);
auto gnd = float(bb3d.min(Z));
for(float h = minZ + initial_layer_height; h < maxZ; h += flh)
if(h >= gnd) heights.emplace_back(h);
return heights;
}
template<class...Args>
void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args) {
void report_status(SLAPrint& p, int st, const std::string& msg, Args&&...args)
{
BOOST_LOG_TRIVIAL(info) << st << "% " << msg;
p.set_status(st, msg, std::forward<Args>(args)...);
}
@ -620,12 +615,19 @@ void SLAPrint::process()
using namespace sla;
using ExPolygon = Slic3r::ExPolygon;
if(m_objects.empty()) return;
// Assumption: at this point the print objects should be populated only with
// the model objects we have to process and the instances are also filtered
// 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);
const size_t objcount = m_objects.size();
const unsigned min_objstatus = 0; // where the per object operations start
@ -646,24 +648,59 @@ 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, ilh](SLAPrintObject& po) {
double lh = po.m_config.layer_height.getFloat();
auto slice_model = [this, ilhs, lhs, ilh, lh](SLAPrintObject& po) {
TriangleMesh mesh = po.transformed_mesh();
// We need to prepare the slice index...
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);
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) {
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)));
if(slindex_it == po.m_slice_index.end())
throw std::runtime_error(L("Slicing had to be stopped "
"due to an internal error."));
po.m_model_height_levels.clear();
po.m_model_height_levels.reserve(po.m_slice_index.size());
for(auto it = slindex_it; it != po.m_slice_index.end(); ++it)
{
po.m_model_height_levels.emplace_back(it->slice_level());
}
TriangleMeshSlicer slicer(&mesh);
// The 1D grid heights
std::vector<float> heights = calculate_heights(mesh.bounding_box(),
float(po.get_elevation()),
ilh, float(lh));
po.m_model_slices.clear();
slicer.slice(po.m_model_height_levels,
float(po.config().slice_closing_radius.value),
&po.m_model_slices,
[this](){ throw_if_canceled(); });
auto& layers = po.m_model_slices; layers.clear();
slicer.slice(heights, float(po.config().slice_closing_radius.value), &layers, [this](){ throw_if_canceled(); });
auto mit = slindex_it;
for(size_t id = 0;
id < po.m_model_slices.size() && mit != po.m_slice_index.end();
id++)
{
mit->set_model_slice_idx(id); ++mit;
}
};
// In this step we check the slices, identify island and cover them with
// support points. Then we sprinkle the rest of the mesh.
auto support_points = [this, ilh](SLAPrintObject& po) {
auto support_points = [this](SLAPrintObject& po) {
const ModelObject& mo = *po.m_model_object;
po.m_supportdata.reset(
new SLAPrintObject::SupportData(po.transformed_mesh()) );
@ -680,12 +717,7 @@ void SLAPrint::process()
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
// calculate heights of slices (slices are calculated already)
double lh = po.m_config.layer_height.getFloat();
std::vector<float> heights =
calculate_heights(po.transformed_mesh().bounding_box(),
float(po.get_elevation()),
ilh, float(lh));
const std::vector<float>& heights = po.m_model_height_levels;
this->throw_if_canceled();
SLAAutoSupports::Config config;
@ -831,86 +863,34 @@ void SLAPrint::process()
// Slicing the support geometries similarly to the model slicing procedure.
// If the pad had been added previously (see step "base_pool" than it will
// be part of the slices)
auto slice_supports = [ilh](SLAPrintObject& po) {
auto slice_supports = [](SLAPrintObject& po) {
auto& sd = po.m_supportdata;
if(sd) sd->support_slices.clear();
if(sd && sd->support_tree_ptr) {
auto lh = float(po.m_config.layer_height.getFloat());
sd->support_slices = sd->support_tree_ptr->slice(lh, ilh);
std::vector<float> heights; heights.reserve(po.m_slice_index.size());
for(auto& rec : po.m_slice_index) {
heights.emplace_back(rec.slice_level());
}
sd->support_slices = sd->support_tree_ptr->slice(
heights, float(po.config().slice_closing_radius.value));
}
for(size_t i = 0;
i < sd->support_slices.size() && i < po.m_slice_index.size();
++i)
{
po.m_slice_index[i].set_support_slice_idx(i);
}
};
// We have the layer polygon collection but we need to unite them into
// an index where the key is the height level in discrete levels (clipper)
auto index_slices = [this, ilhd](SLAPrintObject& po) {
po.m_slice_index.clear();
auto sih = LevelID(scale_(ilhd));
// Establish the slice grid boundaries
auto bb = po.transformed_mesh().bounding_box();
double modelgnd = bb.min(Z);
double elevation = po.get_elevation();
double lh = po.m_config.layer_height.getFloat();
double minZ = modelgnd - elevation;
// scaled values:
auto sminZ = LevelID(scale_(minZ));
auto smaxZ = LevelID(scale_(bb.max(Z)));
auto smodelgnd = LevelID(scale_(modelgnd));
auto slh = LevelID(scale_(lh));
// It is important that the next levels match the levels in
// model_slice method. Only difference is that here it works with
// scaled coordinates
po.m_level_ids.clear();
for(LevelID h = sminZ + sih; h < smaxZ; h += slh)
if(h >= smodelgnd) po.m_level_ids.emplace_back(h);
std::vector<ExPolygons>& oslices = po.m_model_slices;
// If everything went well this code should not run at all, but
// let's be robust...
// assert(levelids.size() == oslices.size());
if(po.m_level_ids.size() < oslices.size()) { // extend the levels until...
BOOST_LOG_TRIVIAL(warning)
<< "Height level mismatch at rasterization!\n";
LevelID lastlvl = po.m_level_ids.back();
while(po.m_level_ids.size() < oslices.size()) {
lastlvl += slh;
po.m_level_ids.emplace_back(lastlvl);
}
}
for(size_t i = 0; i < oslices.size(); ++i) {
LevelID h = po.m_level_ids[i];
float fh = float(double(h) * SCALING_FACTOR);
// now for the public slice index:
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
// There should be only one slice layer for each print object
assert(sr.model_slices_idx == SLAPrintObject::SliceRecord::NONE);
sr.model_slices_idx = i;
}
if(po.m_supportdata) { // deal with the support slices if present
std::vector<ExPolygons>& sslices = po.m_supportdata->support_slices;
po.m_supportdata->level_ids.clear();
po.m_supportdata->level_ids.reserve(sslices.size());
for(int i = 0; i < int(sslices.size()); ++i) {
LevelID h = sminZ + sih + i * slh;
po.m_supportdata->level_ids.emplace_back(h);
float fh = float(double(h) * SCALING_FACTOR);
SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh];
assert(sr.support_slices_idx == SLAPrintObject::SliceRecord::NONE);
sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i);
}
}
auto index_slices = [this/*, ilhd*/](SLAPrintObject& /*po*/) {
// Using RELOAD_SLA_PREVIEW to tell the Plater to pass the update status to the 3D preview to load the SLA slices.
report_status(*this, -2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
};
@ -923,30 +903,18 @@ void SLAPrint::process()
m_printer_input.clear();
for(SLAPrintObject * o : m_objects) {
auto& po = *o;
std::vector<ExPolygons>& oslices = po.m_model_slices;
LevelID gndlvl = o->get_slice_index().front().key();
for(auto& slicerecord : o->get_slice_index()) {
auto& lyrs = m_printer_input[slicerecord.key() - gndlvl];
// We need to adjust the min Z level of the slices to be zero
LevelID smfirst =
po.m_supportdata && !po.m_supportdata->level_ids.empty() ?
po.m_supportdata->level_ids.front() : 0;
LevelID mfirst = po.m_level_ids.empty()? 0 : po.m_level_ids.front();
LevelID gndlvl = -(std::min(smfirst, mfirst));
const ExPolygons& objslices = o->get_slices_from_record(slicerecord, soModel);
const ExPolygons& supslices = o->get_slices_from_record(slicerecord, soSupport);
// now merge this object's support and object slices with the rest
// of the print object slices
if(!objslices.empty())
lyrs.emplace_back(objslices, o->instances());
for(size_t i = 0; i < oslices.size(); ++i) {
auto& lyrs = m_printer_input[gndlvl + po.m_level_ids[i]];
lyrs.emplace_back(oslices[i], po.m_instances);
}
if(!po.m_supportdata) continue;
std::vector<ExPolygons>& sslices = po.m_supportdata->support_slices;
for(size_t i = 0; i < sslices.size(); ++i) {
LayerRefs& lyrs =
m_printer_input[gndlvl + po.m_supportdata->level_ids[i]];
lyrs.emplace_back(sslices[i], po.m_instances);
if(!supslices.empty())
lyrs.emplace_back(supslices, o->instances());
}
}
@ -1249,13 +1217,13 @@ 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?
double 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();
if (! slice_index.empty()) {
double z = (-- slice_index.end())->first;
float z = (-- slice_index.end())->slice_level();
size_t cnt = slice_index.size();
//if (z > max_z) {
if (cnt > max_layers_cnt) {
@ -1275,7 +1243,7 @@ void SLAPrint::fill_statistics()
int sliced_layer_cnt = 0;
for (const auto& layer : highest_obj_slice_index)
{
const double l_height = (layer.first == highest_obj_slice_index.begin()->first) ? init_layer_height : layer_height;
const double l_height = (layer.key() == highest_obj_slice_index.begin()->key()) ? init_layer_height : layer_height;
// Calculation of the consumed material
@ -1284,21 +1252,22 @@ void SLAPrint::fill_statistics()
for (SLAPrintObject * po : m_objects)
{
const SLAPrintObject::SliceRecord *record = nullptr;
const SLAPrintObject::_SliceRecord *record = nullptr;
{
const SLAPrintObject::SliceIndex& index = po->get_slice_index();
auto key = layer.first;
const SLAPrintObject::SliceIndex::const_iterator it_key = index.lower_bound(key - float(EPSILON));
if (it_key == index.end() || it_key->first > key + EPSILON)
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_key->second;
record = &(*it);
}
if (record->model_slices_idx != SLAPrintObject::SliceRecord::NONE)
append(model_polygons, get_all_polygons(po->get_model_slices()[record->model_slices_idx], po->instances()));
if (record->support_slices_idx != SLAPrintObject::SliceRecord::NONE)
append(supports_polygons, get_all_polygons(po->get_support_slices()[record->support_slices_idx], po->instances()));
const ExPolygons &modelslices = po->get_slices_from_record(*record, soModel);
if (!modelslices.empty())
append(model_polygons, get_all_polygons(modelslices, po->instances()));
const ExPolygons &supportslices = po->get_slices_from_record(*record, soSupport);
if (!supportslices.empty())
append(supports_polygons, get_all_polygons(supportslices, po->instances()));
}
model_polygons = union_(model_polygons);
@ -1394,11 +1363,15 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
for (const t_config_option_key &opt_key : opt_keys) {
if ( opt_key == "layer_height"
|| opt_key == "faded_layers"
|| opt_key == "pad_enable"
|| opt_key == "pad_wall_thickness"
|| opt_key == "supports_enable"
|| opt_key == "support_object_elevation"
|| opt_key == "slice_closing_radius") {
steps.emplace_back(slaposObjectSlice);
} else if (
opt_key == "supports_enable"
|| opt_key == "support_points_density_relative"
opt_key == "support_points_density_relative"
|| opt_key == "support_points_minimal_distance") {
steps.emplace_back(slaposSupportPoints);
} else if (
@ -1413,12 +1386,10 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector<t_conf
|| opt_key == "support_critical_angle"
|| opt_key == "support_max_bridge_length"
|| opt_key == "support_max_pillar_link_distance"
|| opt_key == "support_object_elevation") {
) {
steps.emplace_back(slaposSupportTree);
} else if (
opt_key == "pad_enable"
|| opt_key == "pad_wall_thickness"
|| opt_key == "pad_wall_height"
opt_key == "pad_wall_height"
|| opt_key == "pad_max_merge_distance"
|| opt_key == "pad_wall_slope"
|| opt_key == "pad_edge_radius") {
@ -1474,11 +1445,7 @@ double SLAPrintObject::get_elevation() const {
// its walls but currently it is half of its thickness. Whatever it
// will be in the future, we provide the config to the get_pad_elevation
// method and we will have the correct value
sla::PoolConfig pcfg;
pcfg.min_wall_height_mm = m_config.pad_wall_height.getFloat();
pcfg.min_wall_thickness_mm = m_config.pad_wall_thickness.getFloat();
pcfg.edge_radius_mm = m_config.pad_edge_radius.getFloat();
pcfg.max_merge_distance_mm = m_config.pad_max_merge_distance.getFloat();
sla::PoolConfig pcfg = make_pool_config(m_config);
ret += sla::get_pad_elevation(pcfg);
}
@ -1502,6 +1469,7 @@ double SLAPrintObject::get_current_elevation() const
namespace { // dummy empty static containers for return values in some methods
const std::vector<ExPolygons> EMPTY_SLICES;
const TriangleMesh EMPTY_MESH;
const ExPolygons EMPTY_SLICE;
}
const std::vector<sla::SupportPoint>& SLAPrintObject::get_support_points() const
@ -1509,6 +1477,72 @@ 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));
@ -1516,7 +1550,30 @@ const std::vector<ExPolygons> &SLAPrintObject::get_support_slices() const
return m_supportdata->support_slices;
}
const SLAPrintObject::SliceIndex &SLAPrintObject::get_slice_index() const
const ExPolygons &SLAPrintObject::get_slices_from_record(
const _SliceRecord &rec,
SliceOrigin o) const
{
size_t idx = o == soModel ? rec.get_model_slice_idx() :
rec.get_support_slice_idx();
const std::vector<ExPolygons>& v = o == soModel? get_model_slices() :
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
{
// assert(is_step_done(slaposIndexSlices));
return m_slice_index;

View file

@ -6,6 +6,7 @@
#include "PrintExport.hpp"
#include "Point.hpp"
#include "MTUtils.hpp"
#include <iterator>
namespace Slic3r {
@ -35,12 +36,19 @@ using _SLAPrintObjectBase =
// the printer (rasterizer) in the SLAPrint class.
using LevelID = long long;
enum SliceOrigin { soSupport, soModel };
class SLAPrintObject : public _SLAPrintObjectBase
{
private: // Prevents erroneous use by other classes.
using Inherited = _SLAPrintObjectBase;
public:
// I refuse to grantee copying (Tamas)
SLAPrintObject(const SLAPrintObject&) = delete;
SLAPrintObject& operator=(const SLAPrintObject&) = delete;
const SLAPrintObjectConfig& config() const { return m_config; }
const Transform3d& trafo() const { return m_trafo; }
@ -82,40 +90,146 @@ public:
// pad is not, then without the pad, otherwise the full value is returned.
double get_current_elevation() const;
// These two 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.
const std::vector<ExPolygons>& get_model_slices() const;
const std::vector<ExPolygons>& get_support_slices() const;
// This method returns the support points of this SLAPrintObject.
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;
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
protected:
SliceRecord(Key key, float slicez, float height):
m_print_z(key), m_slice_z(slicez), m_height(height) {}
public:
// The key will be the integer height level of the top of the layer.
inline Key key() const { return m_print_z; }
// Returns the exact floating point Z coordinate of the slice
inline float slice_level() const { return m_slice_z; }
// Returns the current layer height
inline float layer_height() const { return m_height; }
};
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.
struct SliceRecord {
using Key = float;
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;
using Idx = size_t;
static const Idx NONE = Idx(-1); // this will be the max limit of size_t
public:
_SliceRecord(Key key, float slicez, float height):
SliceRecord(key, slicez, height) {}
Idx model_slices_idx = NONE;
Idx support_slices_idx = NONE;
// 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; }
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; }
};
using SliceIndex = std::map<SliceRecord::Key, SliceRecord>;
// Slice index will be a plain vector sorted by the integer height levels
using SliceIndex = std::vector<_SliceRecord>;
// Retrieve the slice index which is readable only after slaposIndexSlices
// is done.
const SliceIndex& get_slice_index() const;
// I refuse to grantee copying (Tamas)
SLAPrintObject(const SLAPrintObject&) = delete;
SLAPrintObject& operator=(const SLAPrintObject&) = delete;
// 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;
// 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;
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)
// 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
{
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),
};
}
// 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())
};
}
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;
@ -145,8 +259,10 @@ protected:
private:
// Object specific configuration, pulled from the configuration layer.
SLAPrintObjectConfig m_config;
// Translation in Z + Rotation by Y and Z + Scaling / Mirroring.
Transform3d m_trafo = Transform3d::Identity();
std::vector<Instance> m_instances;
// Individual 2d slice polygons from lower z to higher z levels
@ -154,11 +270,9 @@ private:
// Exact (float) height levels mapped to the slices. Each record contains
// the index to the model and the support slice vectors.
SliceIndex m_slice_index;
std::vector<_SliceRecord> m_slice_index;
// The height levels corrected and scaled up in integer values. This will
// be used at rasterization.
std::vector<LevelID> m_level_ids;
std::vector<float> m_model_height_levels;
// Caching the transformed (m_trafo) raw mesh of the object
mutable CachedObject<TriangleMesh> m_transformed_rmesh;
@ -236,6 +350,11 @@ public:
}
const PrintObjects& objects() const { return m_objects; }
const SLAPrintConfig& print_config() const { return m_print_config; }
const SLAPrinterConfig& printer_config() const { return m_printer_config; }
const SLAMaterialConfig& material_config() const { return m_material_config; }
std::string output_filename() const override;
const SLAPrintStatistics& print_statistics() const { return m_print_statistics; }
@ -249,11 +368,6 @@ private:
// Invalidate steps based on a set of parameters changed.
bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
std::vector<float> calculate_heights(const BoundingBoxf3& bb,
float elevation,
float initial_layer_height,
float layer_height) const;
void fill_statistics();
SLAPrintConfig m_print_config;

View file

@ -540,7 +540,10 @@ void Choice::BUILD() {
else{
for (auto el : m_opt.enum_labels.empty() ? m_opt.enum_values : m_opt.enum_labels) {
const wxString& str = _(el);//m_opt_id == "support" ? _(el) : el;
temp->Append(str, create_scaled_bitmap("empty_icon.png"));
//FIXME Vojtech: Why is the single column empty icon necessary? It is a workaround of some kind, but what for?
// Please document such workarounds by comments!
// temp->Append(str, create_scaled_bitmap("empty_icon.png"));
temp->Append(str, wxNullBitmap);
}
set_selection();
}

View file

@ -4971,24 +4971,20 @@ void GLCanvas3D::_render_sla_slices() const
{
const SLAPrintObject* obj = print_objects[i];
double shift_z = obj->get_current_elevation();
double min_z = clip_min_z - shift_z;
double max_z = clip_max_z - shift_z;
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_bottom = m_sla_caps[0].triangles.find(i);
SlaCap::ObjectIdToTrianglesMap::iterator it_caps_top = m_sla_caps[1].triangles.find(i);
{
if (it_caps_bottom == m_sla_caps[0].triangles.end())
it_caps_bottom = m_sla_caps[0].triangles.emplace(i, SlaCap::Triangles()).first;
if (! m_sla_caps[0].matches(min_z)) {
m_sla_caps[0].z = min_z;
if (! m_sla_caps[0].matches(clip_min_z)) {
m_sla_caps[0].z = clip_min_z;
it_caps_bottom->second.object.clear();
it_caps_bottom->second.supports.clear();
}
if (it_caps_top == m_sla_caps[1].triangles.end())
it_caps_top = m_sla_caps[1].triangles.emplace(i, SlaCap::Triangles()).first;
if (! m_sla_caps[1].matches(max_z)) {
m_sla_caps[1].z = max_z;
if (! m_sla_caps[1].matches(clip_max_z)) {
m_sla_caps[1].z = clip_max_z;
it_caps_top->second.object.clear();
it_caps_top->second.supports.clear();
}
@ -5008,36 +5004,39 @@ void GLCanvas3D::_render_sla_slices() const
std::vector<InstanceTransform> instance_transforms;
for (const SLAPrintObject::Instance& inst : instances)
{
instance_transforms.push_back({ to_3d(unscale(inst.shift), shift_z), 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))
{
const std::vector<ExPolygons>& model_slices = obj->get_model_slices();
const std::vector<ExPolygons>& support_slices = obj->get_support_slices();
const SLAPrintObject::SliceIndex& index = obj->get_slice_index();
SLAPrintObject::SliceIndex::const_iterator it_min_z = std::find_if(index.begin(), index.end(), [min_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(min_z - id.first) < EPSILON; });
SLAPrintObject::SliceIndex::const_iterator it_max_z = std::find_if(index.begin(), index.end(), [max_z](const SLAPrintObject::SliceIndex::value_type& id) -> bool { return std::abs(max_z - id.first) < EPSILON; });
if (it_min_z != index.end())
{
double initial_layer_height = print->material_config().initial_layer_height.value;
LevelID key_zero = obj->get_slice_records().begin()->key();
LevelID key_low = LevelID((clip_min_z - initial_layer_height) / SCALING_FACTOR) + key_zero;
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());
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);
// calculate model bottom cap
if (bottom_obj_triangles.empty() && (it_min_z->second.model_slices_idx < model_slices.size()))
bottom_obj_triangles = triangulate_expolygons_3d(model_slices[it_min_z->second.model_slices_idx], min_z, true);
if (bottom_obj_triangles.empty() && !obj_bottom.empty())
bottom_obj_triangles = triangulate_expolygons_3d(obj_bottom, clip_min_z, true);
// calculate support bottom cap
if (bottom_sup_triangles.empty() && (it_min_z->second.support_slices_idx < support_slices.size()))
bottom_sup_triangles = triangulate_expolygons_3d(support_slices[it_min_z->second.support_slices_idx], min_z, true);
if (bottom_sup_triangles.empty() && !sup_bottom.empty())
bottom_sup_triangles = triangulate_expolygons_3d(sup_bottom, clip_min_z, true);
}
if (it_max_z != index.end())
{
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);
// calculate model top cap
if (top_obj_triangles.empty() && (it_max_z->second.model_slices_idx < model_slices.size()))
top_obj_triangles = triangulate_expolygons_3d(model_slices[it_max_z->second.model_slices_idx], max_z, false);
if (top_obj_triangles.empty() && !obj_top.empty())
top_obj_triangles = triangulate_expolygons_3d(obj_top, clip_max_z, false);
// calculate support top cap
if (top_sup_triangles.empty() && (it_max_z->second.support_slices_idx < support_slices.size()))
top_sup_triangles = triangulate_expolygons_3d(support_slices[it_max_z->second.support_slices_idx], max_z, false);
if (top_sup_triangles.empty() && !sup_top.empty())
top_sup_triangles = triangulate_expolygons_3d(sup_top, clip_max_z, false);
}
}

View file

@ -10,6 +10,8 @@
#include "I18N.hpp"
#include <wx/wupdlock.h>
namespace Slic3r
{
namespace GUI
@ -40,7 +42,7 @@ void OG_Settings::Hide()
void OG_Settings::UpdateAndShow(const bool show)
{
Show(show);
// m_parent->Layout();
// m_parent->Layout();
}
wxSizer* OG_Settings::get_sizer()
@ -84,6 +86,7 @@ void ObjectSettings::update_settings_list()
btn->Bind(wxEVT_BUTTON, [opt_key, config, this](wxEvent &event) {
config->erase(opt_key);
wxTheApp->CallAfter([this]() {
wxWindowUpdateLocker noUpdates(m_parent);
update_settings_list();
m_parent->Layout();
});
@ -119,7 +122,7 @@ void ObjectSettings::update_settings_list()
if (cat.second.size() == 1 && cat.second[0] == "extruder")
continue;
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_parent, cat.first, config, false, extra_column);
auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), cat.first, config, false, extra_column);
optgroup->label_width = 15 * wxGetApp().em_unit();//150;
optgroup->sidetext_width = 7 * wxGetApp().em_unit();//70;

View file

@ -769,19 +769,17 @@ void Preview::load_print_as_sla()
unsigned int n_layers = 0;
const SLAPrint* print = m_process->sla_print();
std::set<double> zs;
std::vector<double> zs;
double initial_layer_height = print->material_config().initial_layer_height.value;
for (const SLAPrintObject* obj : print->objects())
{
double shift_z = obj->get_current_elevation();
if (obj->is_step_done(slaposIndexSlices))
{
const SLAPrintObject::SliceIndex& index = obj->get_slice_index();
for (const SLAPrintObject::SliceIndex::value_type& id : index)
{
zs.insert(shift_z + id.first);
}
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);
}
}
sort_remove_duplicates(zs);
n_layers = (unsigned int)zs.size();
if (n_layers == 0)
@ -796,11 +794,7 @@ void Preview::load_print_as_sla()
show_hide_ui_elements("none");
if (n_layers > 0)
{
std::vector<double> layer_zs;
std::copy(zs.begin(), zs.end(), std::back_inserter(layer_zs));
update_sliders(layer_zs);
}
update_sliders(zs);
m_loaded = true;
}

View file

@ -57,6 +57,7 @@
#include "BackgroundSlicingProcess.hpp"
#include "ProgressStatusBar.hpp"
#include "PrintHostDialogs.hpp"
#include "ConfigWizard.hpp"
#include "../Utils/ASCIIFolding.hpp"
#include "../Utils/PrintHost.hpp"
#include "../Utils/FixModelByWin10.hpp"
@ -233,9 +234,11 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
auto selected_item = this->GetSelection();
auto marker = reinterpret_cast<Marker>(this->GetClientData(selected_item));
if (marker == LABEL_ITEM_MARKER) {
if (marker == LABEL_ITEM_MARKER || marker == LABEL_ITEM_CONFIG_WIZARD) {
this->SetSelection(this->last_selected);
evt.StopPropagation();
if (marker == LABEL_ITEM_CONFIG_WIZARD)
wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); });
} else if ( this->last_selected != selected_item ||
wxGetApp().get_tab(this->preset_type)->get_presets()->current_is_dirty() ) {
this->last_selected = selected_item;
@ -317,15 +320,14 @@ PresetComboBox::~PresetComboBox()
}
void PresetComboBox::set_label_marker(int item)
void PresetComboBox::set_label_marker(int item, LabelItemType label_item_type)
{
this->SetClientData(item, (void*)LABEL_ITEM_MARKER);
this->SetClientData(item, (void*)label_item_type);
}
void PresetComboBox::check_selection()
{
if (this->last_selected != GetSelection())
this->last_selected = GetSelection();
this->last_selected = GetSelection();
}
// Frequently changed parameters
@ -826,10 +828,7 @@ void Sidebar::update_presets(Preset::Type preset_type)
preset_bundle.sla_materials.update_platter_ui(p->combo_sla_material);
}
// Update the printer choosers, update the dirty flags.
auto prev_selection = p->combo_printer->GetSelection();
preset_bundle.printers.update_platter_ui(p->combo_printer);
if (prev_selection != p->combo_printer->GetSelection())
p->combo_printer->check_selection();
// Update the filament choosers to only contain the compatible presets, update the color preview,
// update the dirty flags.
if (print_tech == ptFFF) {

View file

@ -48,14 +48,18 @@ public:
wxButton* edit_btn { nullptr };
void set_label_marker(int item);
enum LabelItemType {
LABEL_ITEM_MARKER = 0x4d,
LABEL_ITEM_CONFIG_WIZARD = 0x4e
};
void set_label_marker(int item, LabelItemType label_item_type = LABEL_ITEM_MARKER);
void set_extruder_idx(const int extr_idx) { extruder_idx = extr_idx; }
int get_extruder_idx() const { return extruder_idx; }
void check_selection();
private:
typedef std::size_t Marker;
enum { LABEL_ITEM_MARKER = 0x4d };
Preset::Type preset_type;
int last_selected;

View file

@ -519,6 +519,7 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector<std::str
m_edited_preset(type, "", false),
m_idx_selected(0),
m_bitmap_main_frame(new wxBitmap),
m_bitmap_add(new wxBitmap),
m_bitmap_cache(new GUI::BitmapCache)
{
// Insert just the default preset.
@ -530,6 +531,8 @@ PresetCollection::~PresetCollection()
{
delete m_bitmap_main_frame;
m_bitmap_main_frame = nullptr;
delete m_bitmap_add;
m_bitmap_add = nullptr;
delete m_bitmap_cache;
m_bitmap_cache = nullptr;
}
@ -798,6 +801,11 @@ bool PresetCollection::load_bitmap_default(const std::string &file_name)
return m_bitmap_main_frame->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG);
}
bool PresetCollection::load_bitmap_add(const std::string &file_name)
{
return m_bitmap_add->LoadFile(wxString::FromUTF8(Slic3r::var(file_name).c_str()), wxBITMAP_TYPE_PNG);
}
const Preset* PresetCollection::get_selected_preset_parent() const
{
const std::string &inherits = this->get_edited_preset().inherits();
@ -902,8 +910,8 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap));
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++ i) {
const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! preset.is_compatible && i != m_idx_selected))
continue;
@ -942,20 +950,45 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
}
if (i + 1 == m_num_default_presets)
ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap));
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
}
if (!nonsys_presets.empty())
{
ui->set_label_marker(ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap));
ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap));
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected)
selected_preset_item = ui->GetCount() - 1;
}
}
if (m_type == Preset::TYPE_PRINTER) {
std::string bitmap_key = "";
// If the filament preset is not compatible and there is a "red flag" icon loaded, show it left
// to the filament color image.
if (wide_icons)
bitmap_key += "wide,";
bitmap_key += "add_printer";
wxBitmap *bmp = m_bitmap_cache->find(bitmap_key);
if (bmp == nullptr) {
// Create the bitmap with color bars.
std::vector<wxBitmap> bmps;
if (wide_icons)
// Paint a red flag for incompatible presets.
bmps.emplace_back(m_bitmap_cache->mkclear(16, 16));
// Paint the color bars.
bmps.emplace_back(m_bitmap_cache->mkclear(4, 16));
bmps.emplace_back(*m_bitmap_main_frame);
// Paint a lock at the system presets.
bmps.emplace_back(m_bitmap_cache->mkclear(6, 16));
bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
bmp = m_bitmap_cache->insert(bitmap_key, bmps);
}
ui->set_label_marker(ui->Append(PresetCollection::separator(L("Add a new printer")), *bmp), GUI::PresetComboBox::LABEL_ITEM_CONFIG_WIZARD);
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->check_selection();
ui->Thaw();
}
@ -970,7 +1003,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected = "";
if (!this->m_presets.front().is_visible)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap);
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
const Preset &preset = this->m_presets[i];
if (! preset.is_visible || (! show_incompatible && ! preset.is_compatible && i != m_idx_selected))
@ -1001,18 +1034,29 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati
if (i == m_idx_selected)
selected = wxString::FromUTF8((preset.name + (preset.is_dirty ? g_suffix_modified : "")).c_str());
}
if (i + 1 == m_num_default_presets)
ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap);
if (i + 1 == m_num_default_presets)
ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap);
}
if (!nonsys_presets.empty())
{
ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap);
ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap);
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected)
selected_preset_item = ui->GetCount() - 1;
}
}
if (m_type == Preset::TYPE_PRINTER) {
wxBitmap *bmp = m_bitmap_cache->find("add_printer_tab");
if (bmp == nullptr) {
// Create the bitmap with color bars.
std::vector<wxBitmap> bmps;
bmps.emplace_back(*m_bitmap_main_frame);
bmps.emplace_back(m_bitmap_add ? *m_bitmap_add : wxNullBitmap);
bmp = m_bitmap_cache->insert("add_printer_tab", bmps);
}
ui->Append(PresetCollection::separator("Add a new printer"), *bmp);
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->Thaw();
@ -1241,6 +1285,11 @@ std::string PresetCollection::path_from_name(const std::string &new_name) const
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
}
wxString PresetCollection::separator(const std::string &label)
{
return wxString::FromUTF8(PresetCollection::separator_head()) + _(label) + wxString::FromUTF8(PresetCollection::separator_tail());
}
const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const
{
const ConfigOptionEnumGeneric *opt_printer_technology = config.opt<ConfigOptionEnumGeneric>("printer_technology");

View file

@ -11,9 +11,10 @@
#include "slic3r/Utils/Semver.hpp"
class wxBitmap;
class wxChoice;
class wxBitmapComboBox;
class wxChoice;
class wxItemContainer;
class wxString;
namespace Slic3r {
@ -277,6 +278,9 @@ public:
// Load default bitmap to be placed at the wxBitmapComboBox of a MainFrame.
bool load_bitmap_default(const std::string &file_name);
// Load "add new printer" bitmap to be placed at the wxBitmapComboBox of a MainFrame.
bool load_bitmap_add(const std::string &file_name);
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items.
void set_bitmap_compatible (const wxBitmap *bmp) { m_bitmap_compatible = bmp; }
void set_bitmap_incompatible(const wxBitmap *bmp) { m_bitmap_incompatible = bmp; }
@ -407,6 +411,15 @@ public:
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string path_from_name(const std::string &new_name) const;
#ifdef __linux__
static const char* separator_head() { return "------- "; }
static const char* separator_tail() { return " -------"; }
#else /* __linux__ */
static const char* separator_head() { return "————— "; }
static const char* separator_tail() { return " —————"; }
#endif /* __linux__ */
static wxString separator(const std::string &label);
protected:
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
// This is a temporary state, which shall be fixed immediately by the following step.
@ -467,6 +480,8 @@ private:
// Marks placed at the wxBitmapComboBox of a MainFrame.
// These bitmaps are owned by PresetCollection.
wxBitmap *m_bitmap_main_frame;
// "Add printer profile" icon, owned by PresetCollection.
wxBitmap *m_bitmap_add;
// Path to the directory to store the config files into.
std::string m_dir_path;

View file

@ -107,6 +107,7 @@ PresetBundle::PresetBundle() :
this->filaments .load_bitmap_default("spool.png");
this->sla_materials.load_bitmap_default("package_green.png");
this->printers .load_bitmap_default("printer_empty.png");
this->printers .load_bitmap_add("add.png");
this->load_compatible_bitmaps();
// Re-activate the default presets, so their "edited" preset copies will be updated with the additional configuration values above.
@ -1405,7 +1406,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst
// an optional "(modified)" suffix will be removed from the filament name.
void PresetBundle::set_filament_preset(size_t idx, const std::string &name)
{
if (name.find_first_of("-------") == 0)
if (name.find_first_of(PresetCollection::separator_head()) == 0)
return;
if (idx >= filament_presets.size())
@ -1461,7 +1462,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
std::map<wxString, wxBitmap*> nonsys_presets;
wxString selected_str = "";
if (!this->filaments().front().is_visible)
ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap));
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
for (int i = this->filaments().front().is_visible ? 0 : 1; i < int(this->filaments().size()); ++i) {
const Preset &preset = this->filaments.preset(i);
bool selected = this->filament_presets[idx_extruder] == preset.name;
@ -1514,12 +1515,12 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
selected_str = wxString::FromUTF8((preset.name + (preset.is_dirty ? Preset::suffix_modified() : "")).c_str());
}
if (preset.is_default)
ui->set_label_marker(ui->Append("------- " + _(L("System presets")) + " -------", wxNullBitmap));
ui->set_label_marker(ui->Append(PresetCollection::separator(L("System presets")), wxNullBitmap));
}
if (!nonsys_presets.empty())
{
ui->set_label_marker(ui->Append("------- " + _(L("User presets")) + " -------", wxNullBitmap));
ui->set_label_marker(ui->Append(PresetCollection::separator(L("User presets")), wxNullBitmap));
for (std::map<wxString, wxBitmap*>::iterator it = nonsys_presets.begin(); it != nonsys_presets.end(); ++it) {
ui->Append(it->first, *it->second);
if (it->first == selected_str)
@ -1528,6 +1529,7 @@ void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::Pr
}
ui->SetSelection(selected_preset_item);
ui->SetToolTip(ui->GetString(selected_preset_item));
ui->check_selection();
ui->Thaw();
}

View file

@ -29,6 +29,7 @@
#include "GUI_App.hpp"
#include "GUI_ObjectList.hpp"
#include "ConfigWizard.hpp"
namespace Slic3r {
namespace GUI {
@ -248,10 +249,12 @@ void Tab::create_preset_tab()
return;
if (selected_item >= 0) {
std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data();
if (selected_string.find("-------") == 0
if (selected_string.find(PresetCollection::separator_head()) == 0
/*selected_string == "------- System presets -------" ||
selected_string == "------- User presets -------"*/) {
m_presets_choice->SetSelection(m_selected_preset_item);
if (wxString::FromUTF8(selected_string.c_str()) == PresetCollection::separator(L("Add a new printer")))
wxTheApp->CallAfter([]() { Slic3r::GUI::config_wizard(Slic3r::GUI::ConfigWizard::RR_USER); });
return;
}
m_selected_preset_item = selected_item;