diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index f5219263d6..ff1da37cc1 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -175,6 +175,11 @@ struct AMFParserContext bool mirrory_set; float mirrorz; bool mirrorz_set; + + bool anything_set() const { return deltax_set || deltay_set || deltaz_set || + rx_set || ry_set || rz_set || + scalex_set || scaley_set || scalez_set || + mirrorx_set || mirrory_set || mirrorz_set; } }; struct Object { @@ -644,11 +649,7 @@ void AMFParserContext::endDocument() continue; } for (const Instance &instance : object.second.instances) -#if ENABLE_VOLUMES_CENTERING_FIXES - { -#else - if (instance.deltax_set && instance.deltay_set) { -#endif // ENABLE_VOLUMES_CENTERING_FIXES + if (instance.anything_set()) { ModelInstance *mi = m_model.objects[object.second.idx]->add_instance(); mi->set_offset(Vec3d(instance.deltax_set ? (double)instance.deltax : 0.0, instance.deltay_set ? (double)instance.deltay : 0.0, instance.deltaz_set ? (double)instance.deltaz : 0.0)); mi->set_rotation(Vec3d(instance.rx_set ? (double)instance.rx : 0.0, instance.ry_set ? (double)instance.ry : 0.0, instance.rz_set ? (double)instance.rz : 0.0)); diff --git a/src/libslic3r/MTUtils.hpp b/src/libslic3r/MTUtils.hpp index 1e0fb426aa..1f6ca1a237 100644 --- a/src/libslic3r/MTUtils.hpp +++ b/src/libslic3r/MTUtils.hpp @@ -56,6 +56,113 @@ public: } }; +template +class IndexBasedIterator { + static const size_t NONE = size_t(-1); + + std::reference_wrapper 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 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(b)), to(std::forward(e)) {} + + inline size_t size() const { return end() - begin(); } + inline bool empty() const { return size() == 0; } +}; + } #endif // MTUTILS_HPP diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 68f71c3088..f9db9fea0b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -997,12 +997,16 @@ Polygon ModelObject::convex_hull_2d(const Transform3d &trafo_instance) return hull; } +#if ENABLE_VOLUMES_CENTERING_FIXES +void ModelObject::center_around_origin(bool include_modifiers) +#else void ModelObject::center_around_origin() +#endif // ENABLE_VOLUMES_CENTERING_FIXES { // calculate the displacements needed to // center this object around the origin #if ENABLE_VOLUMES_CENTERING_FIXES - BoundingBoxf3 bb = full_raw_mesh_bounding_box(); + BoundingBoxf3 bb = include_modifiers ? full_raw_mesh_bounding_box() : raw_mesh_bounding_box(); #else BoundingBoxf3 bb; for (ModelVolume *v : this->volumes) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 1260500eec..8a48f8ee96 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -236,7 +236,11 @@ public: // This method is used by the auto arrange function. Polygon convex_hull_2d(const Transform3d &trafo_instance); +#if ENABLE_VOLUMES_CENTERING_FIXES + void center_around_origin(bool include_modifiers = true); +#else void center_around_origin(); +#endif // ENABLE_VOLUMES_CENTERING_FIXES void ensure_on_bed(); void translate_instances(const Vec3d& vector); void translate_instance(size_t instance_idx, const Vec3d& vector); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8c67cc067e..630dbfb5fd 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -1375,6 +1375,7 @@ void PrintConfigDef::init_fff_params() def->sidetext = L("(minimum)"); def->aliases = { "perimeter_offsets" }; def->min = 0; + def->max = 10000; def->default_value = new ConfigOptionInt(3); def = this->add("post_process", coStrings); diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index df9990822d..34dd80cee0 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -2240,6 +2240,18 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const return ret; } +SlicedSupports SLASupportTree::slice(const std::vector &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 { diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 74d7da9cad..66677e4d7a 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -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 closing_radius) const; + /// Adding the "pad" (base pool) under the supports const TriangleMesh& add_pad(const SliceLayer& baseplate, const PoolConfig& pcfg) const; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 83fa61fdd0..79834908f2 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -30,7 +30,6 @@ public: std::vector support_points; // all the support points (manual/auto) SupportTreePtr support_tree_ptr; // the supports SlicedSupports support_slices; // sliced supports - std::vector 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 SLAPrint::calculate_heights(const BoundingBoxf3& bb3d, - float elevation, - float initial_layer_height, - float layer_height) const -{ - std::vector 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 -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)...); } @@ -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 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 heights = - calculate_heights(po.transformed_mesh().bounding_box(), - float(po.get_elevation()), - ilh, float(lh)); + const std::vector& 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 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& 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& 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& 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& 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 EMPTY_SLICES; const TriangleMesh EMPTY_MESH; +const ExPolygons EMPTY_SLICE; } const std::vector& SLAPrintObject::get_support_points() const @@ -1509,6 +1477,72 @@ const std::vector& 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 &SLAPrintObject::get_support_slices() const { // assert(is_step_done(slaposSliceSupports)); @@ -1516,7 +1550,30 @@ const std::vector &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& 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::get_slice_index() const { // assert(is_step_done(slaposIndexSlices)); return m_slice_index; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index c06e2fc777..fbe2821b21 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -6,6 +6,7 @@ #include "PrintExport.hpp" #include "Point.hpp" #include "MTUtils.hpp" +#include 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& get_model_slices() const; - const std::vector& get_support_slices() const; - // This method returns the support points of this SLAPrintObject. const std::vector& 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; + // 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& get_model_slices() const; + const std::vector& get_support_slices() const; + +public: + + // Should work as a polymorphic bidirectional iterator to the slice records + using SliceRecordConstIterator = + IndexBasedIterator; + + // ///////////////////////////////////////////////////////////////////////// + // + // 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 Range + get_slice_records(Key from, Key to = std::numeric_limits::max()) const + { + SliceIndex::const_iterator it_from, it_to; + if(std::is_integral::value) { + it_from = search_slice_index(SliceRecord::Key(from)); + it_to = search_slice_index(SliceRecord::Key(to)); + } else if(std::is_floating_point::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 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 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 m_level_ids; + std::vector m_model_height_levels; // Caching the transformed (m_trafo) raw mesh of the object mutable CachedObject 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 &opt_keys); - std::vector calculate_heights(const BoundingBoxf3& bb, - float elevation, - float initial_layer_height, - float layer_height) const; - void fill_statistics(); SLAPrintConfig m_print_config; diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 026b62ab59..9cd933160e 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -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(); } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index bf0e1785a0..e4f0f461e8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -34,6 +34,7 @@ #include #include #include +#include // Print now includes tbb, and tbb includes Windows. This breaks compilation of wxWidgets if included before wx. #include "libslic3r/Print.hpp" @@ -728,13 +729,69 @@ void GLCanvas3D::WarningTexture::activate(WarningTexture::Warning warning, bool _generate(text, canvas, red_colored); // GUI::GLTexture::reset() is called at the beginning of generate(...) } -bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanvas3D& canvas, const bool red_colored/* = false*/) + +#ifdef __WXMSW__ +static bool is_font_cleartype(const wxFont &font) +{ + // Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon. + wxString font_desc = font.GetNativeFontInfoDesc(); + // Find the quality field. + wxString sep(";"); + size_t startpos = 0; + for (size_t i = 0; i < 12; ++ i) + startpos = font_desc.find(sep, startpos + 1); + ++ startpos; + size_t endpos = font_desc.find(sep, startpos); + int quality = wxAtoi(font_desc(startpos, endpos - startpos)); + return quality == CLEARTYPE_QUALITY; +} + +// ClearType produces renders, which are difficult to convert into an alpha blended OpenGL texture. +// Therefore it is better to disable it, though Vojtech found out, that the font returned with ClearType +// disabled is signifcantly thicker than the default ClearType font. +// This function modifies the font provided. +static void msw_disable_cleartype(wxFont &font) +{ + // Native font description: on MSW, it is a version number plus the content of LOGFONT, separated by semicolon. + wxString font_desc = font.GetNativeFontInfoDesc(); + // Find the quality field. + wxString sep(";"); + size_t startpos_weight = 0; + for (size_t i = 0; i < 5; ++ i) + startpos_weight = font_desc.find(sep, startpos_weight + 1); + ++ startpos_weight; + size_t endpos_weight = font_desc.find(sep, startpos_weight); + // Parse the weight field. + unsigned int weight = atoi(font_desc(startpos_weight, endpos_weight - startpos_weight)); + size_t startpos = endpos_weight; + for (size_t i = 0; i < 6; ++ i) + startpos = font_desc.find(sep, startpos + 1); + ++ startpos; + size_t endpos = font_desc.find(sep, startpos); + int quality = wxAtoi(font_desc(startpos, endpos - startpos)); + if (quality == CLEARTYPE_QUALITY) { + // Replace the weight with a smaller value to compensate the weight of non ClearType font. + wxString sweight = std::to_string(weight * 2 / 4); + size_t len_weight = endpos_weight - startpos_weight; + wxString squality = std::to_string(ANTIALIASED_QUALITY); + font_desc.replace(startpos_weight, len_weight, sweight); + font_desc.replace(startpos + sweight.size() - len_weight, endpos - startpos, squality); + font.SetNativeFontInfo(font_desc); + wxString font_desc2 = font.GetNativeFontInfoDesc(); + } + wxString font_desc2 = font.GetNativeFontInfoDesc(); +} +#endif /* __WXMSW__ */ + +bool GLCanvas3D::WarningTexture::_generate(const std::string& msg_utf8, const GLCanvas3D& canvas, const bool red_colored/* = false*/) { reset(); - if (msg.empty()) + if (msg_utf8.empty()) return false; + wxString msg = GUI::from_u8(msg_utf8); + wxMemoryDC memDC; // select default font const float scale = canvas.get_canvas_size().get_scale_factor(); @@ -745,46 +802,47 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanva // calculates texture size wxCoord w, h; -// memDC.GetTextExtent(msg, &w, &h); memDC.GetMultiLineTextExtent(msg, &w, &h); - int pow_of_two_size = next_highest_power_of_2(std::max(w, h)); - m_original_width = (int)w; m_original_height = (int)h; - m_width = pow_of_two_size; - m_height = pow_of_two_size; + m_width = (int)next_highest_power_of_2((uint32_t)w); + m_height = (int)next_highest_power_of_2((uint32_t)h); // generates bitmap wxBitmap bitmap(m_width, m_height); memDC.SelectObject(bitmap); - memDC.SetBackground(wxBrush(wxColour(Background_Color[0], Background_Color[1], Background_Color[2]))); + memDC.SetBackground(wxBrush(*wxBLACK)); memDC.Clear(); // draw message - memDC.SetTextForeground(red_colored ? wxColour(255,72,65/*204,204*/) : *wxWHITE); -// memDC.DrawText(msg, 0, 0); - memDC.DrawLabel(msg, wxRect(0,0, m_original_width, m_original_height), wxALIGN_CENTER); + memDC.SetTextForeground(*wxRED); + memDC.DrawLabel(msg, wxRect(0,0, m_original_width, m_original_height), wxALIGN_CENTER); memDC.SelectObject(wxNullBitmap); // Convert the bitmap into a linear data ready to be loaded into the GPU. wxImage image = bitmap.ConvertToImage(); - image.SetMaskColour(Background_Color[0], Background_Color[1], Background_Color[2]); // prepare buffer std::vector data(4 * m_width * m_height, 0); + const unsigned char *src = image.GetData(); for (int h = 0; h < m_height; ++h) { - int hh = h * m_width; - unsigned char* px_ptr = data.data() + 4 * hh; + unsigned char* dst = data.data() + 4 * h * m_width; for (int w = 0; w < m_width; ++w) { - *px_ptr++ = image.GetRed(w, h); - *px_ptr++ = image.GetGreen(w, h); - *px_ptr++ = image.GetBlue(w, h); - *px_ptr++ = image.IsTransparent(w, h) ? 0 : Opacity; + *dst++ = 255; + if (red_colored) { + *dst++ = 72; // 204 + *dst++ = 65; // 204 + } else { + *dst++ = 255; + *dst++ = 255; + } + *dst++ = (unsigned char)std::min(255, *src); + src += 3; } } @@ -908,7 +966,15 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c const int scaled_border = Px_Border * scale; // select default font - const wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale_gl); + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale_gl); +#ifdef __WXMSW__ + // Disabling ClearType works, but the font returned is very different (much thicker) from the default. +// msw_disable_cleartype(font); + bool cleartype = is_font_cleartype(font); +#else + bool cleartype = false; +#endif /* __WXMSW__ */ + memDC.SetFont(font); mask_memDC.SetFont(font); @@ -932,10 +998,8 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c if (items_count > 1) m_original_height += (items_count - 1) * scaled_square_contour; - int pow_of_two_size = (int)next_highest_power_of_2(std::max(m_original_width, m_original_height)); - - m_width = pow_of_two_size; - m_height = pow_of_two_size; + m_width = (int)next_highest_power_of_2((uint32_t)m_original_width); + m_height = (int)next_highest_power_of_2((uint32_t)m_original_height); // generates bitmap wxBitmap bitmap(m_width, m_height); @@ -952,16 +1016,13 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c // draw title memDC.SetTextForeground(*wxWHITE); - mask_memDC.SetTextForeground(*wxWHITE); + mask_memDC.SetTextForeground(*wxRED); int title_x = scaled_border; int title_y = scaled_border; memDC.DrawText(title, title_x, title_y); mask_memDC.DrawText(title, title_x, title_y); - mask_memDC.SetPen(wxPen(*wxWHITE)); - mask_memDC.SetBrush(wxBrush(*wxWHITE)); - // draw icons contours as background int squares_contour_x = scaled_border; int squares_contour_y = scaled_border + title_height + scaled_title_offset; @@ -976,7 +1037,6 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c memDC.SetPen(pen); memDC.SetBrush(brush); memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height)); - mask_memDC.DrawRectangle(wxRect(squares_contour_x, squares_contour_y, squares_contour_width, squares_contour_height)); // draw items (colored icon + text) int icon_x = squares_contour_x + scaled_square_contour; @@ -1012,7 +1072,6 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c memDC.DrawRectangle(wxRect(icon_x_inner, icon_y + 1, px_inner_square, px_inner_square)); // draw text - memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset); mask_memDC.DrawText(GUI::from_u8(item.text), text_x, icon_y + text_y_offset); // update y @@ -1028,17 +1087,34 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c // prepare buffer std::vector data(4 * m_width * m_height, 0); - for (int h = 0; h < m_height; ++h) + const unsigned char *src_image = image.GetData(); + const unsigned char *src_mask = mask_image.GetData(); + for (int h = 0; h < m_height; ++h) { int hh = h * m_width; unsigned char* px_ptr = data.data() + 4 * hh; for (int w = 0; w < m_width; ++w) { - unsigned char alpha = (mask_image.GetRed(w, h) + mask_image.GetGreen(w, h) + mask_image.GetBlue(w, h)) / 3; - *px_ptr++ = image.GetRed(w, h); - *px_ptr++ = image.GetGreen(w, h); - *px_ptr++ = image.GetBlue(w, h); - *px_ptr++ = (alpha == 0) ? 128 : 255; + if (w >= squares_contour_x && w < squares_contour_x + squares_contour_width && + h >= squares_contour_y && h < squares_contour_y + squares_contour_height) { + // Color palette, use the color verbatim. + *px_ptr++ = *src_image++; + *px_ptr++ = *src_image++; + *px_ptr++ = *src_image++; + *px_ptr++ = 255; + } else { + // Text or background + unsigned char alpha = *src_mask; + // Compensate the white color for the 50% opacity reduction at the character edges. + //unsigned char color = (unsigned char)floor(alpha * 255.f / (128.f + 0.5f * alpha)); + unsigned char color = alpha; + *px_ptr++ = color; + *px_ptr++ = color; // *src_mask ++; + *px_ptr++ = color; // *src_mask ++; + *px_ptr++ = 128 + (alpha / 2); // (alpha > 0) ? 255 : 128; + src_image += 3; + } + src_mask += 3; } } @@ -1286,11 +1362,13 @@ int GLCanvas3D::check_volumes_outside_state() const return (int)state; } -void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible) +void GLCanvas3D::toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo, int instance_idx) { for (GLVolume* vol : m_volumes.volumes) { - if (vol->composite_id.volume_id < 0) - vol->is_active = visible; + if ((mo == nullptr || m_model->objects[vol->composite_id.object_id] == mo) + && (instance_idx == -1 || vol->composite_id.instance_id == instance_idx) + && vol->composite_id.volume_id < 0) + vol->is_active = visible; } m_render_sla_auxiliaries = visible; @@ -1306,7 +1384,7 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject } } if (visible && !mo) - toggle_sla_auxiliaries_visibility(true); + toggle_sla_auxiliaries_visibility(true, mo, instance_idx); if (!mo && !visible && !m_model->objects.empty() && (m_model->objects.size() > 1 || m_model->objects.front()->instances.size() > 1)) _set_warning_texture(WarningTexture::SomethingNotShown, true); @@ -1938,7 +2016,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_selection.volumes_changed(map_glvolume_old_to_new); } - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ // Update the toolbar post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); @@ -2253,7 +2334,10 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { if (m_gizmos.handle_shortcut(keyCode, m_selection)) { - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_dirty = true; } else @@ -2432,13 +2516,25 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) return; } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::cout << to_string(m_mouse.position) << std::endl; + + if (m_gizmos.on_mouse(evt, *this)) + { + m_mouse.set_start_position_3D_as_invalid(); + return; + } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (m_picking_enabled) _set_current(); int selected_object_idx = m_selection.get_object_idx(); int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.select_object(*m_model, layer_editing_object_idx); - bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (m_mouse.drag.move_requires_threshold && m_mouse.is_move_start_threshold_position_2D_defined() && m_mouse.is_move_threshold_met(pos)) { @@ -2508,15 +2604,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } } - else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// else if (!m_selection.is_empty() && gizmos_overlay_contains_mouse) +// { +// m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection); +// _update_gizmos_data(); +// m_dirty = true; +// } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + else if (evt.LeftDown() && m_gizmos.get_current_type() == GLGizmosManager::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::LeftDown, Vec2d(pos(0), pos(1)), evt.ShiftDown())) +// else if (evt.LeftDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::LeftDown, Vec2d(pos(0), pos(1)), evt.ShiftDown())) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { - m_gizmos.update_on_off_state(*this, m_mouse.position, m_selection); - _update_gizmos_data(); - m_dirty = true; + // the gizmo got the event and took some action, there is no need to do anything more } else if (evt.LeftDown() && !m_selection.is_empty() && m_gizmos.grabber_contains_mouse()) { - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_selection.start_dragging(); m_gizmos.start_dragging(m_selection); @@ -2529,11 +2637,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_dirty = true; } - else if (evt.LeftDown() && m_gizmos.get_current_type() == GLGizmosManager::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::LeftDown, Vec2d(pos(0), pos(1)), evt.ShiftDown())) - { - // the gizmo got the event and took some action, there is no need to do anything more - } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ else if ((selected_object_idx != -1) && evt.RightDown() && m_gizmos.get_current_type() == GLGizmosManager::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::RightDown)) +// else if ((selected_object_idx != -1) && evt.RightDown() && m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.gizmo_event(SLAGizmoEventType::RightDown)) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { // event was taken care of by the SlaSupports gizmo } @@ -2569,7 +2676,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { m_gizmos.update_on_off_state(m_selection); - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); m_dirty = true; } @@ -2596,7 +2706,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } } - else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + else if (evt.Dragging() && evt.LeftIsDown() && (m_layers_editing.state == LayersEditing::Unknown) +// else if (evt.Dragging() && evt.LeftIsDown() && !gizmos_overlay_contains_mouse && (m_layers_editing.state == LayersEditing::Unknown) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ && (m_mouse.drag.move_volume_idx != -1) && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports /* don't allow dragging objects with the Sla gizmo on */) { if (!m_mouse.drag.move_requires_threshold) @@ -2695,8 +2808,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // the gizmo got the event and took some action, no need to do anything more here m_dirty = true; } - // do not process dragging if the mouse is into any of the HUD elements - else if (evt.Dragging() && !gizmos_overlay_contains_mouse) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + else if (evt.Dragging()) +// // do not process dragging if the mouse is into any of the HUD elements +// else if (evt.Dragging() && !gizmos_overlay_contains_mouse) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { m_mouse.dragging = true; @@ -2758,8 +2874,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // of the scene with the background processing data should be performed. post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); } - else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() - && !is_layers_editing_enabled()) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !m_gizmos.is_dragging() +// else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + && !is_layers_editing_enabled()) { // deselect and propagate event through callback if (!evt.ShiftDown() && m_picking_enabled && !m_mouse.ignore_up_event) @@ -2768,7 +2887,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_selection.set_mode(Selection::Instance); wxGetApp().obj_manipul()->update_settings_value(m_selection); m_gizmos.reset_all_states(); - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); } m_mouse.ignore_up_event = false; @@ -2797,7 +2919,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) break; } m_gizmos.stop_dragging(); - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxGetApp().obj_manipul()->update_settings_value(m_selection); // Let the platter know that the dragging finished, so a delayed refresh @@ -2822,7 +2947,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_selection.add(m_hover_volume_id); m_gizmos.update_on_off_state(m_selection); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ wxGetApp().obj_manipul()->update_settings_value(m_selection); // forces a frame render to update the view before the context menu is shown render(); @@ -2853,9 +2981,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) std::string tooltip = ""; // updates gizmos overlay - tooltip = m_gizmos.update_hover_state(*this, m_mouse.position, m_selection); - if (m_selection.is_empty()) - m_gizmos.reset_all_states(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (tooltip.empty()) + tooltip = m_gizmos.get_tooltip(); + +// tooltip = m_gizmos.update_hover_state(*this, m_mouse.position, m_selection); +// if (m_selection.is_empty()) +// m_gizmos.reset_all_states(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if (tooltip.empty()) tooltip = m_toolbar.get_tooltip(); @@ -2865,6 +2998,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) set_tooltip(tooltip); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (m_selection.is_empty()) + m_gizmos.reset_all_states(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. if (m_picking_enabled) m_dirty = true; @@ -3172,7 +3310,10 @@ void GLCanvas3D::set_camera_zoom(float zoom) void GLCanvas3D::update_gizmos_on_off_state() { set_as_dirty(); - _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + update_gizmos_data(); +// _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ m_gizmos.update_on_off_state(get_selection()); } @@ -3207,6 +3348,48 @@ void GLCanvas3D::update_ui_from_settings() #endif } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +void GLCanvas3D::update_gizmos_data() +{ + if (!m_gizmos.is_enabled()) + return; + + bool enable_move_z = !m_selection.is_wipe_tower(); + m_gizmos.enable_grabber(GLGizmosManager::Move, 2, enable_move_z); + bool enable_scale_xyz = m_selection.is_single_full_instance() || m_selection.is_single_volume() || m_selection.is_single_modifier(); + for (int i = 0; i < 6; ++i) + { + m_gizmos.enable_grabber(GLGizmosManager::Scale, i, enable_scale_xyz); + } + + if (m_selection.is_single_full_instance()) + { + // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; + m_gizmos.set_scale(volume->get_instance_scaling_factor()); + m_gizmos.set_rotation(Vec3d::Zero()); + ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; + m_gizmos.set_flattening_data(model_object); + m_gizmos.set_sla_support_data(model_object, m_selection); + } + else if (m_selection.is_single_volume() || m_selection.is_single_modifier()) + { + const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; + m_gizmos.set_scale(volume->get_volume_scaling_factor()); + m_gizmos.set_rotation(Vec3d::Zero()); + m_gizmos.set_flattening_data(nullptr); + m_gizmos.set_sla_support_data(nullptr, m_selection); + } + else + { + m_gizmos.set_scale(Vec3d::Ones()); + m_gizmos.set_rotation(Vec3d::Zero()); + m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr); + m_gizmos.set_sla_support_data(nullptr, m_selection); + } +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; @@ -3964,24 +4147,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(); } @@ -4001,36 +4180,39 @@ void GLCanvas3D::_render_sla_slices() const std::vector 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& model_slices = obj->get_model_slices(); - const std::vector& 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); } } @@ -4122,45 +4304,47 @@ void GLCanvas3D::_update_volumes_hover_state() const } } -void GLCanvas3D::_update_gizmos_data() -{ - if (!m_gizmos.is_enabled()) - return; - - bool enable_move_z = !m_selection.is_wipe_tower(); - m_gizmos.enable_grabber(GLGizmosManager::Move, 2, enable_move_z); - bool enable_scale_xyz = m_selection.is_single_full_instance() || m_selection.is_single_volume() || m_selection.is_single_modifier(); - for (int i = 0; i < 6; ++i) - { - m_gizmos.enable_grabber(GLGizmosManager::Scale, i, enable_scale_xyz); - } - - if (m_selection.is_single_full_instance()) - { - // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first - const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; - m_gizmos.set_scale(volume->get_instance_scaling_factor()); - m_gizmos.set_rotation(Vec3d::Zero()); - ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; - m_gizmos.set_flattening_data(model_object); - m_gizmos.set_sla_support_data(model_object, m_selection); - } - else if (m_selection.is_single_volume() || m_selection.is_single_modifier()) - { - const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; - m_gizmos.set_scale(volume->get_volume_scaling_factor()); - m_gizmos.set_rotation(Vec3d::Zero()); - m_gizmos.set_flattening_data(nullptr); - m_gizmos.set_sla_support_data(nullptr, m_selection); - } - else - { - m_gizmos.set_scale(Vec3d::Ones()); - m_gizmos.set_rotation(Vec3d::Zero()); - m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr); - m_gizmos.set_sla_support_data(nullptr, m_selection); - } -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//void GLCanvas3D::_update_gizmos_data() +//{ +// if (!m_gizmos.is_enabled()) +// return; +// +// bool enable_move_z = !m_selection.is_wipe_tower(); +// m_gizmos.enable_grabber(GLGizmosManager::Move, 2, enable_move_z); +// bool enable_scale_xyz = m_selection.is_single_full_instance() || m_selection.is_single_volume() || m_selection.is_single_modifier(); +// for (int i = 0; i < 6; ++i) +// { +// m_gizmos.enable_grabber(GLGizmosManager::Scale, i, enable_scale_xyz); +// } +// +// if (m_selection.is_single_full_instance()) +// { +// // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first +// const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; +// m_gizmos.set_scale(volume->get_instance_scaling_factor()); +// m_gizmos.set_rotation(Vec3d::Zero()); +// ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; +// m_gizmos.set_flattening_data(model_object); +// m_gizmos.set_sla_support_data(model_object, m_selection); +// } +// else if (m_selection.is_single_volume() || m_selection.is_single_modifier()) +// { +// const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; +// m_gizmos.set_scale(volume->get_volume_scaling_factor()); +// m_gizmos.set_rotation(Vec3d::Zero()); +// m_gizmos.set_flattening_data(nullptr); +// m_gizmos.set_sla_support_data(nullptr, m_selection); +// } +// else +// { +// m_gizmos.set_scale(Vec3d::Ones()); +// m_gizmos.set_rotation(Vec3d::Zero()); +// m_gizmos.set_flattening_data(m_selection.is_from_single_object() ? m_model->objects[m_selection.get_object_idx()] : nullptr); +// m_gizmos.set_sla_support_data(nullptr, m_selection); +// } +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLCanvas3D::_perform_layer_editing_action(wxMouseEvent* evt) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index bc9932e75d..f18c4a4f10 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -430,6 +430,9 @@ private: bool m_regenerate_volumes; bool m_moving; bool m_tab_down; + + // Following variable is obsolete and it should be safe to remove it. + // I just don't want to do it now before a release (Lukas Matena 24.3.2019) bool m_render_sla_auxiliaries; std::string m_color_by; @@ -456,7 +459,7 @@ public: void reset_volumes(); int check_volumes_outside_state() const; - void toggle_sla_auxiliaries_visibility(bool visible); + void toggle_sla_auxiliaries_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void toggle_model_objects_visibility(bool visible, const ModelObject* mo = nullptr, int instance_idx = -1); void set_config(const DynamicPrintConfig* config); @@ -562,6 +565,10 @@ public: float get_view_toolbar_height() const { return m_view_toolbar.get_height(); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + private: bool _is_shown_on_screen() const; @@ -601,7 +608,9 @@ private: void _render_selection_sidebar_hints() const; void _update_volumes_hover_state() const; - void _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// void _update_gizmos_data(); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void _perform_layer_editing_action(wxMouseEvent* evt = nullptr); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index ac79784adf..de3236fc8d 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -157,7 +157,6 @@ GLToolbar::GLToolbar(GLToolbar::EType type) #if ENABLE_SVG_ICONS , m_icons_texture_dirty(true) #endif // ENABLE_SVG_ICONS - , m_mouse_capture({ false, false, false, nullptr }) , m_tooltip("") { } @@ -410,6 +409,16 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) bool processed = false; // mouse anywhere + if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr)) + { + if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) + // prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it, + // as when switching between views + processed = true; + + m_mouse_capture.reset(); + } + if (evt.Moving()) m_tooltip = update_hover_state(mouse_pos, parent); else if (evt.LeftUp()) @@ -418,17 +427,9 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.middle = false; else if (evt.RightUp()) m_mouse_capture.right = false; - else if (m_mouse_capture.any()) - { - if (evt.Dragging()) - processed = true; - else if (evt.Entering() && (m_mouse_capture.parent == &parent)) - // Resets the mouse capture state to avoid setting the dragging event as processed when, for example, - // the item action opens a modal dialog - // Keeps the mouse capture state if the entering event happens on different parent from the one - // who received the button down event, to prevent, for example, dragging when switching between scene views - m_mouse_capture.reset(); - } + else if (evt.Dragging() && m_mouse_capture.any()) + // if the button down was done on this toolbar, prevent from dragging into the scene + processed = true; int item_id = contains_mouse(mouse_pos, parent); if (item_id == -1) @@ -443,11 +444,16 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) { m_mouse_capture.left = true; m_mouse_capture.parent = &parent; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + processed = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ if ((item_id != -2) && !m_items[item_id]->is_separator()) { // mouse is inside an icon do_action((unsigned int)item_id, parent); - processed = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// processed = true; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ } } else if (evt.MiddleDown()) diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 5fe96df5fc..0f8b17e04f 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -239,6 +239,8 @@ private: bool right; GLCanvas3D* parent; + MouseCapture() { reset(); } + bool any() const { return left || middle || right; } void reset() { left = middle = right = false; parent = nullptr; } }; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index f759250b35..6c8fdcab73 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -209,8 +209,8 @@ void ObjectManipulation::update_settings_value(const Selection& selection) m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation(); m_new_scale = volume->get_volume_scaling_factor(); - m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size(); - m_new_enabled = true; + m_new_size = volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size(); + m_new_enabled = true; } else if (wxGetApp().obj_list()->multiple_selection()) { diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index 3781cbf450..dbfdb8e6c5 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -10,6 +10,8 @@ #include "I18N.hpp" +#include + 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(m_parent, cat.first, config, false, extra_column); + auto optgroup = std::make_shared(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; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index be3fad9ece..2361ff6d39 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -174,7 +174,9 @@ Preview::Preview(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_t , m_loaded(false) , m_enabled(false) , m_schedule_background_process(schedule_background_process_func) +#ifdef __linux__ , m_volumes_cleanup_required(false) +#endif // __linux__ { if (init(parent, bed, camera, view_toolbar)) { @@ -354,31 +356,28 @@ void Preview::load_print(bool keep_z_range) void Preview::reload_print(bool keep_volumes) { -#ifndef __linux__ - if (m_volumes_cleanup_required || !keep_volumes) - { - m_canvas->reset_volumes(); - m_canvas->reset_legend_texture(); - m_loaded = false; - m_volumes_cleanup_required = false; - } -#endif // __linux__ - +#ifdef __linux__ + // We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955. + // So we are applying a workaround here: a delayed release of OpenGL vertex buffers. if (!IsShown()) { m_volumes_cleanup_required = !keep_volumes; return; } - +#endif /* __linux __ */ + if ( #ifdef __linux__ - if (m_volumes_cleanup_required || !keep_volumes) + m_volumes_cleanup_required || +#endif /* __linux__ */ + !keep_volumes) { m_canvas->reset_volumes(); m_canvas->reset_legend_texture(); m_loaded = false; +#ifdef __linux__ m_volumes_cleanup_required = false; +#endif /* __linux__ */ } -#endif // __linux__ load_print(); } @@ -770,19 +769,17 @@ void Preview::load_print_as_sla() unsigned int n_layers = 0; const SLAPrint* print = m_process->sla_print(); - std::set zs; + std::vector 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) @@ -797,11 +794,7 @@ void Preview::load_print_as_sla() show_hide_ui_elements("none"); if (n_layers > 0) - { - std::vector layer_zs; - std::copy(zs.begin(), zs.end(), std::back_inserter(layer_zs)); - update_sliders(layer_zs); - } + update_sliders(zs); m_loaded = true; } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 182eaa9528..96c49e54ff 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -84,7 +84,11 @@ class Preview : public wxPanel BackgroundSlicingProcess* m_process; GCodePreviewData* m_gcode_preview_data; +#ifdef __linux__ + // We are getting mysterious crashes on Linux in gtk due to OpenGL context activation GH #1874 #1955. + // So we are applying a workaround here. bool m_volumes_cleanup_required; +#endif /* __linux__ */ // Calling this function object forces Plater::schedule_background_process. std::function m_schedule_background_process; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index e62a116a7e..0f6713e0d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -329,6 +329,10 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous // left down without selection rectangle - place point on the mesh: if (action == SLAGizmoEventType::LeftDown && !m_selection_rectangle_active && !shift_down) { + // If any point is in hover state, this should initiate its move - return control back to GLCanvas: + if (m_hover_id != -1) + return false; + // If there is some selection, don't add new point and deselect everything instead. if (m_selection_empty) { try { @@ -663,7 +667,7 @@ RENDER_AGAIN: m_imgui->end(); if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode - m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode); + m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode, m_model_object, m_active_instance); force_refresh = true; } m_old_editing_state = m_editing_mode; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 89668162eb..9ac7af5163 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -23,6 +23,9 @@ GLGizmosManager::GLGizmosManager() , m_overlay_scale(1.0f) , m_overlay_border(5.0f) , m_overlay_gap_y(5.0f) +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + , m_tooltip("") +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ { } #else @@ -186,51 +189,53 @@ void GLGizmosManager::set_overlay_scale(float scale) #endif // ENABLE_SVG_ICONS } -std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) -{ - std::string name = ""; - - if (!m_enabled) - return name; - - float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = get_total_overlay_height(); -#if ENABLE_SVG_ICONS - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float top_y = 0.5f * (cnv_h - height) + scaled_border; -#else - float top_y = 0.5f * (cnv_h - height) + m_overlay_border; - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; -#endif // ENABLE_SVG_ICONS - - for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second == nullptr) || !it->second->is_selectable()) - continue; - -#if ENABLE_SVG_ICONS - bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); -#else - bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); -#endif // ENABLE_SVG_ICONS - if (inside) - name = it->second->get_name(); - - if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) - it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); - -#if ENABLE_SVG_ICONS - top_y += scaled_stride_y; -#else - top_y += (scaled_icons_size + m_overlay_gap_y); -#endif // ENABLE_SVG_ICONS - } - - return name; -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) +//{ +// std::string name = ""; +// +// if (!m_enabled) +// return name; +// +// float cnv_h = (float)canvas.get_canvas_size().get_height(); +// float height = get_total_overlay_height(); +//#if ENABLE_SVG_ICONS +// float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; +// float scaled_border = m_overlay_border * m_overlay_scale; +// float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; +// float scaled_stride_y = scaled_icons_size + scaled_gap_y; +// float top_y = 0.5f * (cnv_h - height) + scaled_border; +//#else +// float top_y = 0.5f * (cnv_h - height) + m_overlay_border; +// float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +//#endif // ENABLE_SVG_ICONS +// +// for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) +// { +// if ((it->second == nullptr) || !it->second->is_selectable()) +// continue; +// +//#if ENABLE_SVG_ICONS +// bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +//#else +// bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +//#endif // ENABLE_SVG_ICONS +// if (inside) +// name = it->second->get_name(); +// +// if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) +// it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); +// +//#if ENABLE_SVG_ICONS +// top_y += scaled_stride_y; +//#else +// top_y += (scaled_icons_size + m_overlay_gap_y); +//#endif // ENABLE_SVG_ICONS +// } +// +// return name; +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) { @@ -346,46 +351,48 @@ void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) } } -bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const -{ - if (!m_enabled) - return false; - - float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = get_total_overlay_height(); - -#if ENABLE_SVG_ICONS - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float top_y = 0.5f * (cnv_h - height) + scaled_border; -#else - float top_y = 0.5f * (cnv_h - height) + m_overlay_border; - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; -#endif // ENABLE_SVG_ICONS - - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second == nullptr) || !it->second->is_selectable()) - continue; - -#if ENABLE_SVG_ICONS - if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) -#else - if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) -#endif // ENABLE_SVG_ICONS - return true; - -#if ENABLE_SVG_ICONS - top_y += scaled_stride_y; -#else - top_y += (scaled_icons_size + m_overlay_gap_y); -#endif // ENABLE_SVG_ICONS - } - - return false; -} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +//bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const +//{ +// if (!m_enabled) +// return false; +// +// float cnv_h = (float)canvas.get_canvas_size().get_height(); +// float height = get_total_overlay_height(); +// +//#if ENABLE_SVG_ICONS +// float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; +// float scaled_border = m_overlay_border * m_overlay_scale; +// float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; +// float scaled_stride_y = scaled_icons_size + scaled_gap_y; +// float top_y = 0.5f * (cnv_h - height) + scaled_border; +//#else +// float top_y = 0.5f * (cnv_h - height) + m_overlay_border; +// float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +//#endif // ENABLE_SVG_ICONS +// +// for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) +// { +// if ((it->second == nullptr) || !it->second->is_selectable()) +// continue; +// +//#if ENABLE_SVG_ICONS +// if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) +//#else +// if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) +//#endif // ENABLE_SVG_ICONS +// return true; +// +//#if ENABLE_SVG_ICONS +// top_y += scaled_stride_y; +//#else +// top_y += (scaled_icons_size + m_overlay_gap_y); +//#endif // ENABLE_SVG_ICONS +// } +// +// return false; +//} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool GLGizmosManager::grabber_contains_mouse() const { @@ -614,6 +621,78 @@ void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& glsafe(::glPopMatrix()); } +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) +{ + Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); + + std::cout << to_string(mouse_pos) << std::endl; + + + const Selection& selection = canvas.get_selection(); + bool processed = false; + + // mouse anywhere + if (!evt.Dragging() && !evt.Leaving() && !evt.Entering() && (m_mouse_capture.parent != nullptr)) + { + if (m_mouse_capture.any() && (evt.LeftUp() || evt.MiddleUp() || evt.RightUp())) + // prevents loosing selection into the scene if mouse down was done inside the toolbar and mouse up was down outside it + processed = true; + + m_mouse_capture.reset(); + } + + // mouse anywhere + if (evt.Moving()) + m_tooltip = update_hover_state(canvas, mouse_pos); + else if (evt.LeftUp()) + m_mouse_capture.left = false; + else if (evt.MiddleUp()) + m_mouse_capture.middle = false; + else if (evt.RightUp()) + m_mouse_capture.right = false; + else if (evt.Dragging() && m_mouse_capture.any()) + // if the button down was done on this toolbar, prevent from dragging into the scene + processed = true; + + if (!overlay_contains_mouse(canvas, mouse_pos)) + { + // mouse is outside the toolbar + m_tooltip = ""; + } + else + { + // mouse inside toolbar + if (evt.LeftDown() || evt.LeftDClick()) + { + m_mouse_capture.left = true; + m_mouse_capture.parent = &canvas; + processed = true; + if (!selection.is_empty()) + { + update_on_off_state(canvas, mouse_pos, selection); + canvas.update_gizmos_data(); + canvas.set_as_dirty(); + } + } + else if (evt.MiddleDown()) + { + m_mouse_capture.middle = true; + m_mouse_capture.parent = &canvas; + } + else if (evt.RightDown()) + { + m_mouse_capture.right = true; + m_mouse_capture.parent = &canvas; + } + else if (evt.LeftUp()) + processed = true; + } + + return processed; +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + void GLGizmosManager::reset() { for (GizmosMap::value_type& gizmo : m_gizmos) @@ -861,5 +940,96 @@ bool GLGizmosManager::generate_icons_texture() const } #endif // ENABLE_SVG_ICONS +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) +{ + std::string name = ""; + + if (!m_enabled) + return name; + + const Selection& selection = canvas.get_selection(); + + float cnv_h = (float)canvas.get_canvas_size().get_height(); + float height = get_total_overlay_height(); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float top_y = 0.5f * (cnv_h - height) + scaled_border; +#else + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS + + for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if ((it->second == nullptr) || !it->second->is_selectable()) + continue; + +#if ENABLE_SVG_ICONS + bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +#else + bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +#endif // ENABLE_SVG_ICONS + if (inside) + name = it->second->get_name(); + + if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) + it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); + +#if ENABLE_SVG_ICONS + top_y += scaled_stride_y; +#else + top_y += (scaled_icons_size + m_overlay_gap_y); +#endif // ENABLE_SVG_ICONS + } + + return name; +} + +bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const +{ + if (!m_enabled) + return false; + + float cnv_h = (float)canvas.get_canvas_size().get_height(); + float height = get_total_overlay_height(); + +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float top_y = 0.5f * (cnv_h - height) + scaled_border; +#else + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; + float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS + + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if ((it->second == nullptr) || !it->second->is_selectable()) + continue; + +#if ENABLE_SVG_ICONS + if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) +#else + if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) +#endif // ENABLE_SVG_ICONS + return true; + +#if ENABLE_SVG_ICONS + top_y += scaled_stride_y; +#else + top_y += (scaled_icons_size + m_overlay_gap_y); +#endif // ENABLE_SVG_ICONS + } + + return false; +} +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index cb121352ad..c280eda505 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -83,6 +83,24 @@ private: float m_overlay_border; float m_overlay_gap_y; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + struct MouseCapture + { + bool left; + bool middle; + bool right; + GLCanvas3D* parent; + + MouseCapture() { reset(); } + + bool any() const { return left || middle || right; } + void reset() { left = middle = right = false; parent = nullptr; } + }; + + MouseCapture m_mouse_capture; + std::string m_tooltip; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + public: GLGizmosManager(); ~GLGizmosManager(); @@ -97,7 +115,9 @@ public: #endif // ENABLE_SVG_ICONS void set_overlay_scale(float scale); - std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); void update_on_off_state(const Selection& selection); void reset_all_states(); @@ -105,7 +125,9 @@ public: void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); - bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +// bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ bool grabber_contains_mouse() const; void update(const Linef3& mouse_ray, const Selection& selection, bool shift_down, const Point* mouse_pos = nullptr); Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const; @@ -138,6 +160,12 @@ public: void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + const std::string& get_tooltip() const { return m_tooltip; } + + bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + private: void reset(); @@ -151,6 +179,11 @@ private: #if ENABLE_SVG_ICONS bool generate_icons_texture() const; #endif // ENABLE_SVG_ICONS + +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); + bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ }; } // namespace GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2acc2cec44..e158821257 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -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(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) { @@ -1617,7 +1616,11 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (type_3mf || type_any_amf) { #endif // !ENABLE_VOLUMES_CENTERING_FIXES for (ModelObject* model_object : model.objects) { +#if ENABLE_VOLUMES_CENTERING_FIXES + model_object->center_around_origin(false); +#else model_object->center_around_origin(); +#endif // ENABLE_VOLUMES_CENTERING_FIXES model_object->ensure_on_bed(); } #if !ENABLE_VOLUMES_CENTERING_FIXES @@ -1710,8 +1713,8 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode object->center_around_origin(); new_instances.emplace_back(object->add_instance()); #else /* AUTOPLACEMENT_ON_LOAD */ - // if object has no defined position(s) we need to rearrange everything after loading object->center_around_origin(); - need_arrange = true; + // if object has no defined position(s) we need to rearrange everything after loading + need_arrange = true; // add a default instance and center object around origin object->center_around_origin(); // also aligns object to Z = 0 ModelInstance* instance = object->add_instance(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index f3e195ad09..f830edce3f 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -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; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 656658b0bf..82223b15c0 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -519,6 +519,7 @@ PresetCollection::PresetCollection(Preset::Type type, const std::vector make it visible. + preset.is_visible = true; } else { // Creating a new preset. Preset &preset = *m_presets.insert(it, m_edited_preset); @@ -761,18 +766,20 @@ void PresetCollection::save_current_preset(const std::string &new_name) preset.is_default = false; preset.is_system = false; preset.is_external = false; - } + // The newly saved preset will be activated -> make it visible. + preset.is_visible = true; + } // 2) Activate the saved preset. this->select_preset_by_name(new_name, true); // 2) Store the active preset to disk. this->get_selected_preset().save(); } -void PresetCollection::delete_current_preset() +bool PresetCollection::delete_current_preset() { const Preset &selected = this->get_selected_preset(); - if (selected.is_default) - return; + if (selected.is_default) + return false; if (! selected.is_external && ! selected.is_system) { // Erase the preset file. boost::nowide::remove(selected.file.c_str()); @@ -786,6 +793,7 @@ void PresetCollection::delete_current_preset() if (new_selected_idx == m_presets.size()) for (--new_selected_idx; new_selected_idx > 0 && !m_presets[new_selected_idx].is_visible; --new_selected_idx); this->select_preset(new_selected_idx); + return true; } bool PresetCollection::load_bitmap_default(const std::string &file_name) @@ -793,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(); @@ -843,7 +856,9 @@ void PresetCollection::set_default_suppressed(bool default_suppressed) { if (m_default_suppressed != default_suppressed) { m_default_suppressed = default_suppressed; - m_presets.front().is_visible = ! default_suppressed || (m_presets.size() > m_num_default_presets && m_idx_selected > 0); + bool default_visible = ! default_suppressed || m_idx_selected < m_num_default_presets; + for (size_t i = 0; i < m_num_default_presets; ++ i) + m_presets[i].is_visible = default_visible; } } @@ -895,8 +910,8 @@ void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui) std::map 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; @@ -935,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::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 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(); } @@ -963,7 +1003,7 @@ size_t PresetCollection::update_tab_ui(wxBitmapComboBox *ui, bool show_incompati std::map 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)) @@ -994,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::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 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(); @@ -1114,7 +1165,9 @@ Preset& PresetCollection::select_preset(size_t idx) idx = first_visible_idx(); m_idx_selected = idx; m_edited_preset = m_presets[idx]; - m_presets.front().is_visible = ! m_default_suppressed || m_idx_selected == 0; + bool default_visible = ! m_default_suppressed || m_idx_selected < m_num_default_presets; + for (size_t i = 0; i < m_num_default_presets; ++i) + m_presets[i].is_visible = default_visible; return m_presets[idx]; } @@ -1232,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("printer_technology"); diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index 5113137157..ac1171e183 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -11,9 +11,10 @@ #include "slic3r/Utils/Semver.hpp" class wxBitmap; -class wxChoice; class wxBitmapComboBox; +class wxChoice; class wxItemContainer; +class wxString; namespace Slic3r { @@ -115,6 +116,8 @@ public: // Is this preset compatible with the currently active printer? bool is_compatible = true; + bool is_user() const { return ! this->is_default && ! this->is_system; } + // Name of the preset, usually derived form the file name. std::string name; // File name of the preset. This could be a Print / Filament / Printer preset, @@ -269,11 +272,15 @@ public: void save_current_preset(const std::string &new_name); // Delete the current preset, activate the first visible preset. - void delete_current_preset(); + // returns true if the preset was deleted successfully. + bool delete_current_preset(); // 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; } @@ -404,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. @@ -464,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; diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index b8c9fedacc..f78a04126f 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -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 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::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(); } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 46a6de6abe..48d9ece478 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -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; @@ -385,7 +388,7 @@ void Tab::update_changed_ui() if (m_postpone_update_ui) return; - const bool deep_compare = (m_name == "printer" || m_name == "sla_material"); + const bool deep_compare = (m_type == Slic3r::Preset::TYPE_PRINTER || m_type == Slic3r::Preset::TYPE_SLA_MATERIAL); auto dirty_options = m_presets->current_dirty_options(deep_compare); auto nonsys_options = m_presets->current_different_from_parent_options(deep_compare); if (m_type == Slic3r::Preset::TYPE_PRINTER) { @@ -2395,7 +2398,7 @@ void Tab::load_current_preset() (preset.is_default || preset.is_system) ? m_btn_delete_preset->Disable() : m_btn_delete_preset->Enable(true); update(); - if (m_name == "printer") { + if (m_type == Slic3r::Preset::TYPE_PRINTER) { // For the printer profile, generate the extruder pages. if (preset.printer_technology() == ptFFF) on_preset_loaded(); @@ -2425,7 +2428,7 @@ void Tab::load_current_preset() update_tab_ui(); // update show/hide tabs - if (m_name == "printer") { + if (m_type == Slic3r::Preset::TYPE_PRINTER) { const PrinterTechnology printer_technology = m_presets->get_edited_preset().printer_technology(); if (printer_technology != static_cast(this)->m_printer_technology) { @@ -2458,7 +2461,7 @@ void Tab::load_current_preset() } else { on_presets_changed(); - if (m_type == Preset::TYPE_SLA_PRINT || m_type == Preset::TYPE_PRINT)// if (m_name == "print") + if (m_type == Preset::TYPE_SLA_PRINT || m_type == Preset::TYPE_PRINT) update_frequently_changed_parameters(); } @@ -2526,20 +2529,35 @@ void Tab::update_page_tree_visibility() } -// Called by the UI combo box when the user switches profiles. +// Called by the UI combo box when the user switches profiles, and also to delete the current profile. // Select a preset by a name.If !defined(name), then the default preset is selected. // If the current profile is modified, user is asked to save the changes. -void Tab::select_preset(std::string preset_name) +void Tab::select_preset(std::string preset_name, bool delete_current) { - // If no name is provided, select the "-- default --" preset. - if (preset_name.empty()) - preset_name = m_presets->default_preset().name; - bool current_dirty = m_presets->current_is_dirty(); + if (preset_name.empty()) { + if (delete_current) { + // Find an alternate preset to be selected after the current preset is deleted. + const std::deque &presets = this->m_presets->get_presets(); + size_t idx_current = this->m_presets->get_idx_selected(); + // Find the next visible preset. + size_t idx_new = idx_current + 1; + if (idx_new < presets.size()) + for (; idx_new < presets.size() && ! presets[idx_new].is_visible; ++ idx_new) ; + if (idx_new == presets.size()) + for (idx_new = idx_current - 1; idx_new > 0 && ! presets[idx_new].is_visible; -- idx_new); + preset_name = presets[idx_new].name; + } else { + // If no name is provided, select the "-- default --" preset. + preset_name = m_presets->default_preset().name; + } + } + assert(! delete_current || (m_presets->get_edited_preset().name != preset_name && m_presets->get_edited_preset().is_user())); + bool current_dirty = ! delete_current && m_presets->current_is_dirty(); bool print_tab = m_presets->type() == Preset::TYPE_PRINT || m_presets->type() == Preset::TYPE_SLA_PRINT; bool printer_tab = m_presets->type() == Preset::TYPE_PRINTER; bool canceled = false; m_dependent_tabs = {}; - if (current_dirty && !may_discard_current_dirty_preset()) { + if (current_dirty && ! may_discard_current_dirty_preset()) { canceled = true; } else if (print_tab) { // Before switching the print profile to a new one, verify, whether the currently active filament or SLA material @@ -2602,6 +2620,19 @@ void Tab::select_preset(std::string preset_name) } } } + + if (! canceled && delete_current) { + // Delete the file and select some other reasonable preset. + // It does not matter which preset will be made active as the preset will be re-selected from the preset_name variable. + // The 'external' presets will only be removed from the preset list, their files will not be deleted. + try { + m_presets->delete_current_preset(); + } catch (const std::exception & /* e */) { + //FIXME add some error reporting! + canceled = true; + } + } + if (canceled) { update_tab_ui(); // Trigger the on_presets_changed event so that we also restore the previous value in the plater selector, @@ -2610,17 +2641,19 @@ void Tab::select_preset(std::string preset_name) } else { if (current_dirty) m_presets->discard_current_changes(); - const bool is_selected = m_presets->select_preset_by_name(preset_name, false); + + const bool is_selected = m_presets->select_preset_by_name(preset_name, false) || delete_current; + assert(m_presets->get_edited_preset().name == preset_name || ! is_selected); // Mark the print & filament enabled if they are compatible with the currently selected preset. // The following method should not discard changes of current print or filament presets on change of a printer profile, // if they are compatible with the current printer. - if (current_dirty || print_tab || printer_tab) + if (current_dirty || delete_current || print_tab || printer_tab) m_preset_bundle->update_compatible(true); // Initialize the UI from the current preset. if (printer_tab) static_cast(this)->update_pages(); - if (!is_selected && printer_tab) + if (! is_selected && printer_tab) { /* There is a case, when : * after Config Wizard applying we try to select previously selected preset, but @@ -2631,15 +2664,10 @@ void Tab::select_preset(std::string preset_name) * to the corresponding printer_technology */ const PrinterTechnology printer_technology = m_presets->get_edited_preset().printer_technology(); - if (printer_technology == ptFFF && m_dependent_tabs.front() != Preset::Type::TYPE_PRINT || - printer_technology == ptSLA && m_dependent_tabs.front() != Preset::Type::TYPE_SLA_PRINT ) - { - m_dependent_tabs.clear(); - if (printer_technology == ptFFF) - m_dependent_tabs = { Preset::Type::TYPE_PRINT, Preset::Type::TYPE_FILAMENT }; - else - m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; - } + if (printer_technology == ptFFF && m_dependent_tabs.front() != Preset::Type::TYPE_PRINT) + m_dependent_tabs = { Preset::Type::TYPE_PRINT, Preset::Type::TYPE_FILAMENT }; + else if (printer_technology == ptSLA && m_dependent_tabs.front() != Preset::Type::TYPE_SLA_PRINT) + m_dependent_tabs = { Preset::Type::TYPE_SLA_PRINT, Preset::Type::TYPE_SLA_MATERIAL }; } load_current_preset(); } @@ -2771,11 +2799,14 @@ void Tab::save_preset(std::string name /*= ""*/) //! m_treectrl->OnSetFocus(); if (name.empty()) { - auto preset = m_presets->get_selected_preset(); + const Preset &preset = m_presets->get_selected_preset(); auto default_name = preset.is_default ? "Untitled" : preset.name; + if (preset.is_system) { + default_name += " - "; + default_name += _(L("Copy")).ToUTF8().data(); + } bool have_extention = boost::iends_with(default_name, ".ini"); - if (have_extention) - { + if (have_extention) { size_t len = default_name.length()-4; default_name.resize(len); } @@ -2819,7 +2850,7 @@ void Tab::save_preset(std::string name /*= ""*/) // If current profile is saved, "delete preset" button have to be enabled m_btn_delete_preset->Enable(true); - if (m_name == "printer") + if (m_type == Preset::TYPE_PRINTER) static_cast(this)->m_initial_extruders_count = static_cast(this)->m_extruders_count; update_changed_ui(); } @@ -2836,15 +2867,9 @@ void Tab::delete_preset() if (current_preset.is_default || wxID_YES != wxMessageDialog(parent(), msg, title, wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION).ShowModal()) return; - // Delete the file and select some other reasonable preset. - // The 'external' presets will only be removed from the preset list, their files will not be deleted. - try{ m_presets->delete_current_preset(); } - catch (const std::exception & /* e */) - { - return; - } - // Load the newly selected preset into the UI, update selection combo boxes with their dirty flags. - load_current_preset(); + // Select will handle of the preset dependencies, of saving & closing the depending profiles, and + // finally of deleting the preset. + this->select_preset("", true); } void Tab::toggle_show_hide_incompatible() diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 15ae0443c4..58950990cf 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -239,7 +239,8 @@ public: void load_current_preset(); void rebuild_page_tree(); void update_page_tree_visibility(); - void select_preset(std::string preset_name = ""); + // Select a new preset, possibly delete the current one. + void select_preset(std::string preset_name = "", bool delete_current = false); bool may_discard_current_dirty_preset(PresetCollection* presets = nullptr, const std::string& new_printer_name = ""); bool may_switch_to_SLA_preset();