diff --git a/sandboxes/slabasebed/slabasebed.cpp b/sandboxes/slabasebed/slabasebed.cpp index 255ce2cc34..9804ea3c94 100644 --- a/sandboxes/slabasebed/slabasebed.cpp +++ b/sandboxes/slabasebed/slabasebed.cpp @@ -1,10 +1,10 @@ #include #include -#include -#include "TriangleMesh.hpp" -#include "SLABasePool.hpp" -#include "benchmark.h" +#include +#include +#include +#include const std::string USAGE_STR = { "Usage: slabasebed stlfilename.stl" @@ -28,7 +28,7 @@ int main(const int argc, const char *argv[]) { ExPolygons ground_slice; TriangleMesh basepool; - sla::ground_layer(model, ground_slice, 0.1f); + sla::base_plate(model, ground_slice, 0.1f); bench.start(); sla::create_base_pool(ground_slice, basepool); diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 6ecc63576a..6f28caf71c 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -10,6 +10,10 @@ namespace Slic3r { namespace sla { +namespace { +ThrowOnCancel throw_on_cancel = [](){}; +} + /// Convert the triangulation output to an intermediate mesh. Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { @@ -60,9 +64,13 @@ Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, }); }; + auto& thr = throw_on_cancel; + std::for_each(tri.begin(), tri.end(), - [&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp) + [&rp, &rpi, thr, &idx, is_upper, fz, cz](const Polygon& pp) { + thr(); // may throw if cancellation was requested + for(auto& p : pp.points) if(is_upper(p)) rp.emplace_back(unscale(x(p), y(p), mm(cz))); @@ -231,6 +239,8 @@ Contour3D round_edges(const ExPolygon& base_plate, if(degrees >= 90) { for(int i = 1; i <= steps; ++i) { + throw_on_cancel(); + ob = base_plate; double r2 = radius_mm * radius_mm; @@ -254,6 +264,7 @@ Contour3D round_edges(const ExPolygon& base_plate, int tos = int(tox / stepx); for(int i = 1; i <= tos; ++i) { + throw_on_cancel(); ob = base_plate; double r2 = radius_mm * radius_mm; @@ -385,7 +396,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50) std::back_inserter(punion), [&punion, &boxindex, cc, max_dist_mm, &idx](const Point& c) { - + throw_on_cancel(); double dx = x(c) - x(cc), dy = y(c) - y(cc); double l = std::sqrt(dx * dx + dy * dy); double nx = dx / l, ny = dy / l; @@ -419,7 +430,7 @@ ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50) } void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, - float layerh) + float layerh, ThrowOnCancel thrfn) { TriangleMesh m = mesh; TriangleMeshSlicer slicer(&m); @@ -431,7 +442,7 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, heights.emplace_back(hi); std::vector out; out.reserve(size_t(std::ceil(h/layerh))); - slicer.slice(heights, &out, [](){}); + slicer.slice(heights, &out, thrfn); size_t count = 0; for(auto& o : out) count += o.size(); ExPolygons tmp; tmp.reserve(count); @@ -444,6 +455,8 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, const PoolConfig& cfg) { + throw_on_cancel = cfg.throw_on_cancel; + double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) + cfg.max_merge_distance_mm; diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp index 132a7aeac5..07a76265a8 100644 --- a/src/libslic3r/SLA/SLABasePool.hpp +++ b/src/libslic3r/SLA/SLABasePool.hpp @@ -2,6 +2,7 @@ #define SLABASEPOOL_HPP #include +#include namespace Slic3r { @@ -11,12 +12,14 @@ class TriangleMesh; namespace sla { using ExPolygons = std::vector; +using ThrowOnCancel = std::function; /// Calculate the polygon representing the silhouette from the specified height -void base_plate(const TriangleMesh& mesh, - ExPolygons& output, - float zlevel = 0.1f, - float layerheight = 0.05f); +void base_plate(const TriangleMesh& mesh, // input mesh + ExPolygons& output, // Output will be merged with + float zlevel = 0.1f, // Plate creation level + float layerheight = 0.05f, // The sampling height + ThrowOnCancel thrfn = [](){}); // Will be called frequently struct PoolConfig { double min_wall_thickness_mm = 2; @@ -24,6 +27,8 @@ struct PoolConfig { double max_merge_distance_mm = 50; double edge_radius_mm = 1; + ThrowOnCancel throw_on_cancel = [](){}; + inline PoolConfig() {} inline PoolConfig(double wt, double wh, double md, double er): min_wall_thickness_mm(wt), @@ -35,8 +40,7 @@ struct PoolConfig { /// Calculate the pool for the mesh for SLA printing void create_base_pool(const ExPolygons& base_plate, TriangleMesh& output_mesh, - const PoolConfig& = PoolConfig() - ); + const PoolConfig& = PoolConfig()); /// TODO: Currently the base plate of the pool will have half the height of the /// whole pool. So the carved out space has also half the height. This is not diff --git a/src/libslic3r/SLA/SLABoilerPlate.hpp b/src/libslic3r/SLA/SLABoilerPlate.hpp index 9a79c8e58f..1436be17f3 100644 --- a/src/libslic3r/SLA/SLABoilerPlate.hpp +++ b/src/libslic3r/SLA/SLABoilerPlate.hpp @@ -58,7 +58,7 @@ struct Contour3D { points.insert(points.end(), ctr.points.begin(), ctr.points.end()); indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); - for(auto n = s; n < indices.size(); n++) { + for(size_t n = s; n < indices.size(); n++) { auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3; } } diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index f66351bdb6..6cab763cd8 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -515,8 +515,13 @@ struct Pad { zlevel(ground_level + sla::get_pad_elevation(pcfg)) { ExPolygons basep; + cfg.throw_on_cancel(); + + // The 0.1f is the layer height with which the mesh is sampled and then + // the layers are unified into one vector of polygons. base_plate(object_support_mesh, basep, - float(cfg.min_wall_height_mm)/*,layer_height*/); + float(cfg.min_wall_height_mm), 0.1f, pcfg.throw_on_cancel); + for(auto& bp : baseplate) basep.emplace_back(bp); create_base_pool(basep, tmesh, cfg); @@ -622,12 +627,19 @@ class SLASupportTree::Impl { std::vector m_junctions; std::vector m_bridges; std::vector m_compact_bridges; + Controller m_ctl; + Pad m_pad; mutable TriangleMesh meshcache; mutable bool meshcache_valid; mutable double model_height = 0; // the full height of the model public: double ground_level = 0; + Impl() = default; + inline Impl(const Controller& ctl): m_ctl(ctl) {} + + const Controller& ctl() const { return m_ctl; } + template Head& add_head(Args&&... args) { m_heads.emplace_back(std::forward(args)...); m_heads.back().id = long(m_heads.size() - 1); @@ -710,27 +722,38 @@ public: meshcache = TriangleMesh(); for(auto& head : heads()) { + if(m_ctl.stopcondition()) break; auto&& m = mesh(head.mesh); meshcache.merge(m); } for(auto& stick : pillars()) { + if(m_ctl.stopcondition()) break; meshcache.merge(mesh(stick.mesh)); meshcache.merge(mesh(stick.base)); } for(auto& j : junctions()) { + if(m_ctl.stopcondition()) break; meshcache.merge(mesh(j.mesh)); } for(auto& cb : compact_bridges()) { + if(m_ctl.stopcondition()) break; meshcache.merge(mesh(cb.mesh)); } for(auto& bs : bridges()) { + if(m_ctl.stopcondition()) break; meshcache.merge(mesh(bs.mesh)); } + if(m_ctl.stopcondition()) { + // In case of failure we have to return an empty mesh + meshcache = TriangleMesh(); + return meshcache; + } + // TODO: Is this necessary? meshcache.repair(); @@ -1659,22 +1682,19 @@ SlicedSupports SLASupportTree::slice(float layerh, float init_layerh) const fullmesh.merge(get_pad()); TriangleMeshSlicer slicer(&fullmesh); SlicedSupports ret; - slicer.slice(heights, &ret, m_ctl.cancelfn); + slicer.slice(heights, &ret, get().ctl().cancelfn); return ret; } const TriangleMesh &SLASupportTree::add_pad(const SliceLayer& baseplate, - double min_wall_thickness_mm, - double min_wall_height_mm, - double max_merge_distance_mm, - double edge_radius_mm) const + const PoolConfig& pcfg) const { - PoolConfig pcfg; - pcfg.min_wall_thickness_mm = min_wall_thickness_mm; - pcfg.min_wall_height_mm = min_wall_height_mm; - pcfg.max_merge_distance_mm = max_merge_distance_mm; - pcfg.edge_radius_mm = edge_radius_mm; +// PoolConfig pcfg; +// pcfg.min_wall_thickness_mm = min_wall_thickness_mm; +// pcfg.min_wall_height_mm = min_wall_height_mm; +// pcfg.max_merge_distance_mm = max_merge_distance_mm; +// pcfg.edge_radius_mm = edge_radius_mm; return m_impl->create_pad(merged_mesh(), baseplate, pcfg).tmesh; } @@ -1687,14 +1707,14 @@ SLASupportTree::SLASupportTree(const PointSet &points, const EigenMesh3D& emesh, const SupportConfig &cfg, const Controller &ctl): - m_impl(new Impl()), m_ctl(ctl) + m_impl(new Impl(ctl)) { m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm; generate(points, emesh, cfg, ctl); } SLASupportTree::SLASupportTree(const SLASupportTree &c): - m_impl(new Impl(*c.m_impl)), m_ctl(c.m_ctl) {} + m_impl(new Impl(*c.m_impl)) {} SLASupportTree &SLASupportTree::operator=(const SLASupportTree &c) { diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 9213101f3b..90162d6b54 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -69,6 +69,8 @@ struct SupportConfig { double object_elevation_mm = 10; }; +struct PoolConfig; + /// A Control structure for the support calculation. Consists of the status /// indicator callback and the stop condition predicate. struct Controller { @@ -119,7 +121,6 @@ public: class SLASupportTree { class Impl; std::unique_ptr m_impl; - Controller m_ctl; Impl& get() { return *m_impl; } const Impl& get() const { return *m_impl; } @@ -158,10 +159,7 @@ public: /// Adding the "pad" (base pool) under the supports const TriangleMesh& add_pad(const SliceLayer& baseplate, - double min_wall_thickness_mm, - double min_wall_height_mm, - double max_merge_distance_mm, - double edge_radius_mm) const; + const PoolConfig& pcfg) const; /// Get the pad geometry const TriangleMesh& get_pad() const; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 3cb4920a5a..239c86eca8 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -445,7 +445,7 @@ 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, ilhd](SLAPrintObject& po) { + auto slice_model = [this, ilh](SLAPrintObject& po) { double lh = po.m_config.layer_height.getFloat(); TriangleMesh mesh = po.transformed_mesh(); @@ -530,6 +530,11 @@ void SLAPrint::process() po.m_supportdata->support_tree_ptr.reset( new SLASupportTree(pts, emesh, scfg, ctl)); + // Create the unified mesh + auto rc = SlicingStatus::RELOAD_SCENE; + set_status(-1, L("Visualizing supports")); + po.m_supportdata->support_tree_ptr->merged_mesh(); + set_status(-1, L("Visualizing supports"), rc); } catch(sla::SLASupportsStoppedException&) { // no need to rethrow // throw_if_canceled(); @@ -560,18 +565,23 @@ void SLAPrint::process() auto&& trmesh = po.transformed_mesh(); // This call can get pretty time consuming - if(elevation < pad_h) sla::base_plate(trmesh, bp, - float(pad_h), float(lh)); + auto thrfn = [this](){ throw_if_canceled(); }; - po.m_supportdata->support_tree_ptr->add_pad(bp, wt, h, md, er); + if(elevation < pad_h) + sla::base_plate(trmesh, bp, float(pad_h), float(lh), + thrfn); + + pcfg.throw_on_cancel = thrfn; + po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); } - // if the base pool (which means also the support tree) is - // done, do a refresh when indicating progress. Now the - // geometries for the supports and the optional base pad are - // ready. We can grant access for the control thread to read - // the geometries, but first we have to update the caches: - po.support_mesh(); /*po->pad_mesh();*/ +// // if the base pool (which means also the support tree) is +// // done, do a refresh when indicating progress. Now the +// // geometries for the supports and the optional base pad are +// // ready. We can grant access for the control thread to read +// // the geometries, but first we have to update the caches: +// po.support_mesh(); /*po->pad_mesh();*/ + po.throw_if_canceled(); auto rc = SlicingStatus::RELOAD_SCENE; set_status(-1, L("Visualizing supports"), rc); }; @@ -589,9 +599,9 @@ void SLAPrint::process() // 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, ilh, ilhd](SLAPrintObject& po) { + auto index_slices = [ilhd](SLAPrintObject& po) { po.m_slice_index.clear(); - auto sih = LevelID(scale_(ilh)); + auto sih = LevelID(scale_(ilhd)); // For all print objects, go through its initial layers and place them // into the layers hash @@ -635,7 +645,7 @@ void SLAPrint::process() // shortcut for empty index into the slice vectors static const auto EMPTY_SLICE = SLAPrintObject::SliceRecord::NONE; - for(int i = 0; i < oslices.size(); ++i) { + for(size_t i = 0; i < oslices.size(); ++i) { LevelID h = levelids[i]; float fh = float(double(h) * SCALING_FACTOR); @@ -652,7 +662,7 @@ void SLAPrint::process() po.m_supportdata->level_ids.clear(); po.m_supportdata->level_ids.reserve(sslices.size()); - for(int i = 0; i < sslices.size(); ++i) { + for(int i = 0; i < int(sslices.size()); ++i) { int a = i == 0 ? 0 : 1; int b = i == 0 ? 0 : i - 1; LevelID h = sminZ + a * sih + b * slh; @@ -662,7 +672,7 @@ void SLAPrint::process() SLAPrintObject::SliceRecord& sr = po.m_slice_index[fh]; assert(sr.support_slices_idx == EMPTY_SLICE); - sr.support_slices_idx = i; + sr.support_slices_idx = SLAPrintObject::SliceRecord::Idx(i); } } }; @@ -670,7 +680,7 @@ void SLAPrint::process() auto& levels = m_printer_input; // Rasterizing the model objects, and their supports - auto rasterize = [this, ilh, ilhd, max_objstatus, &levels]() { + auto rasterize = [this, max_objstatus, &levels]() { if(canceled()) return; // clear the rasterizer input @@ -688,14 +698,14 @@ void SLAPrint::process() // now merge this object's support and object slices with the rest // of the print object slices - for(int i = 0; i < oslices.size(); ++i) { + for(size_t i = 0; i < oslices.size(); ++i) { auto& lyrs = levels[gndlvl + po.m_level_ids[i]]; lyrs.emplace_back(oslices[i], po.m_instances); } if(!po.m_supportdata) continue; auto& sslices = po.m_supportdata->support_slices; - for(int i = 0; i < sslices.size(); ++i) { + for(size_t i = 0; i < sslices.size(); ++i) { auto& lyrs = levels[gndlvl + po.m_supportdata->level_ids[i]]; lyrs.emplace_back(sslices[i], po.m_instances); } @@ -713,8 +723,8 @@ void SLAPrint::process() double w = printcfg.display_width.getFloat(); double h = printcfg.display_height.getFloat(); - unsigned pw = printcfg.display_pixels_x.getInt(); - unsigned ph = printcfg.display_pixels_y.getInt(); + auto pw = unsigned(printcfg.display_pixels_x.getInt()); + auto ph = unsigned(printcfg.display_pixels_y.getInt()); double lh = ocfg.layer_height.getFloat(); double exp_t = matcfg.exposure_time.getFloat(); double iexp_t = matcfg.initial_exposure_time.getFloat(); @@ -1098,7 +1108,9 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const const TriangleMesh& SLAPrintObject::support_mesh() const { if(m_config.supports_enable.getBool() && m_supportdata && - m_supportdata->support_tree_ptr) return m_supportdata->support_tree_ptr->merged_mesh(); + m_supportdata->support_tree_ptr) { + return m_supportdata->support_tree_ptr->merged_mesh(); + } return EMPTY_MESH; } diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 14f409698e..ebc7f3665c 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -74,7 +74,16 @@ void Field::on_kill_focus(wxEvent& event) event.Skip(); // call the registered function if it is available if (m_on_kill_focus!=nullptr) - m_on_kill_focus(); + m_on_kill_focus(m_opt_id); +} + +void Field::on_set_focus(wxEvent& event) +{ + // to allow the default behavior + event.Skip(); + // call the registered function if it is available + if (m_on_set_focus!=nullptr) + m_on_set_focus(m_opt_id); } void Field::on_change_field() @@ -125,9 +134,9 @@ void Field::get_value_by_opt_type(wxString& str) case coPercents: case coFloats: case coFloat:{ - if (m_opt.type == coPercent && str.Last() == '%') + if (m_opt.type == coPercent && !str.IsEmpty() && str.Last() == '%') str.RemoveLast(); - else if (str.Last() == '%') { + else if (!str.IsEmpty() && str.Last() == '%') { wxString label = m_Label->GetLabel(); if (label.Last() == '\n') label.RemoveLast(); while (label.Last() == ' ') label.RemoveLast(); @@ -162,7 +171,7 @@ void Field::get_value_by_opt_type(wxString& str) } } -bool TextCtrl::is_defined_input_value() +bool TextCtrl::is_defined_input_value() const { if (static_cast(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings) return false; @@ -216,10 +225,12 @@ void TextCtrl::BUILD() { break; } - const long style = m_opt.multiline ? wxTE_MULTILINE : 0 | m_process_enter ? wxTE_PROCESS_ENTER : 0; + const long style = m_opt.multiline ? wxTE_MULTILINE : 0; auto temp = new wxTextCtrl(m_parent, wxID_ANY, text_value, wxDefaultPosition, size, style); temp->SetToolTip(get_tooltip_text(text_value)); + + temp->Bind(wxEVT_SET_FOCUS, ([this](wxEvent& e) { on_set_focus(e); }), temp->GetId()); temp->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent& event) { @@ -240,17 +251,13 @@ void TextCtrl::BUILD() { e.Skip(); temp->GetToolTip()->Enable(true); #endif // __WXGTK__ - if (!is_defined_input_value()) +// if (!is_defined_input_value()) + if (is_defined_input_value()) + on_change_field(); + else on_kill_focus(e); }), temp->GetId()); - - if (m_process_enter) { - temp->Bind(wxEVT_TEXT_ENTER, ([this](wxCommandEvent& evt) { - if(is_defined_input_value()) - on_change_field(); - }), temp->GetId()); - } - else { + /* temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent& evt) { #ifdef __WXGTK__ @@ -267,8 +274,7 @@ void TextCtrl::BUILD() { temp->Bind(wxEVT_KEY_DOWN, &TextCtrl::change_field_value, this); temp->Bind(wxEVT_KEY_UP, &TextCtrl::change_field_value, this); #endif //__WXGTK__ - } - +*/ // select all text using Ctrl+A temp->Bind(wxEVT_CHAR, ([temp](wxKeyEvent& event) { @@ -371,7 +377,15 @@ void SpinCtrl::BUILD() { 0, min_val, max_val, default_value); // temp->Bind(wxEVT_SPINCTRL, ([this](wxCommandEvent e) { tmp_value = undef_spin_val; on_change_field(); }), temp->GetId()); - temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { on_kill_focus(e); }), temp->GetId()); + temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) + { + if (tmp_value < 0) + on_kill_focus(e); + else { + e.Skip(); + on_change_field(); + } + }), temp->GetId()); temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { // # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value @@ -382,7 +396,8 @@ void SpinCtrl::BUILD() { std::string value = e.GetString().utf8_str().data(); if (is_matched(value, "^\\d+$")) tmp_value = std::stoi(value); - on_change_field(); + else tmp_value = -9999; +// on_change_field(); // # We don't reset tmp_value here because _on_change might put callbacks // # in the CallAfter queue, and we want the tmp value to be available from // # them as well. diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index aef2094a77..0097d3ec00 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -29,8 +29,8 @@ namespace Slic3r { namespace GUI { class Field; using t_field = std::unique_ptr; -using t_kill_focus = std::function; -using t_change = std::function; +using t_kill_focus = std::function; +using t_change = std::function; using t_back_to_init = std::function; wxString double_to_string(double const value, const int max_precision = 4); @@ -76,6 +76,8 @@ protected: //! in another case we can't unfocused control at all void on_kill_focus(wxEvent& event); /// Call the attached on_change method. + void on_set_focus(wxEvent& event); + /// Call the attached on_change method. void on_change_field(); /// Call the attached m_back_to_initial_value method. void on_back_to_initial_value(); @@ -89,6 +91,9 @@ public: /// Function object to store callback passed in from owning object. t_kill_focus m_on_kill_focus {nullptr}; + /// Function object to store callback passed in from owning object. + t_kill_focus m_on_set_focus {nullptr}; + /// Function object to store callback passed in from owning object. t_change m_on_change {nullptr}; @@ -139,10 +144,9 @@ public: /// Factory method for generating new derived classes. template - static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id, const bool process_enter = false)// interface for creating shared objects + static t_field Create(wxWindow* parent, const ConfigOptionDef& opt, const t_config_option_key& id)// interface for creating shared objects { auto p = Slic3r::make_unique(parent, opt, id); - p->m_process_enter = process_enter; p->PostInitialize(); return std::move(p); //!p; } @@ -223,9 +227,6 @@ protected: // current value boost::any m_value; - //this variable shows a mode of a call of the on_change function - bool m_process_enter { false }; - friend class OptionsGroup; }; @@ -265,7 +266,7 @@ public: } boost::any& get_value() override; - bool is_defined_input_value(); + bool is_defined_input_value() const ; virtual void enable(); virtual void disable(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3076e3c5d5..fc284c5951 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -971,6 +971,8 @@ public: void viewport_changed(); #endif // ENABLE_CONSTRAINED_CAMERA_TARGET + void handle_sidebar_focus_event(const std::string& opt_key) {} + private: bool _is_shown_on_screen() const; void _force_zoom_to_bed(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 85236b2a1d..ba98573bf7 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -453,7 +453,6 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) obj->SetText(wxString::Format("%d", m_objects_model->GetVolumeIdByItem(item))); event.SetDataObject(obj); event.SetDragFlags(/*wxDrag_AllowMove*/wxDrag_DefaultMove); // allows both copy and move; - printf("BeginDrag\n"); } void ObjectList::OnDropPossible(wxDataViewEvent &event) @@ -464,7 +463,6 @@ void ObjectList::OnDropPossible(wxDataViewEvent &event) if (event.GetDataFormat() != wxDF_UNICODETEXT || item.IsOk() && (m_objects_model->GetParent(item) == wxDataViewItem(0) || m_objects_model->GetItemType(item) != itVolume)) event.Veto(); - printf("DropPossible\n"); } void ObjectList::OnDrop(wxDataViewEvent &event) @@ -477,13 +475,14 @@ void ObjectList::OnDrop(wxDataViewEvent &event) event.Veto(); return; } - printf("Drop\n"); wxTextDataObject obj; obj.SetData(wxDF_UNICODETEXT, event.GetDataSize(), event.GetDataBuffer()); + printf("Drop\n"); int from_volume_id = std::stoi(obj.GetText().ToStdString()); int to_volume_id = m_objects_model->GetVolumeIdByItem(item); + printf("from %d to %d\n", from_volume_id, to_volume_id); #ifdef __WXGTK__ /* Under GTK, DnD moves an item between another two items. @@ -498,10 +497,12 @@ void ObjectList::OnDrop(wxDataViewEvent &event) int cnt = 0; for (int id = from_volume_id; cnt < abs(from_volume_id - to_volume_id); id += delta, cnt++) std::swap(volumes[id], volumes[id + delta]); + printf("Volumes are swapped\n"); select_item(m_objects_model->ReorganizeChildren(from_volume_id, to_volume_id, m_objects_model->GetParent(item))); + printf("ItemChildren are Reorganized\n"); m_parts_changed = true; parts_changed(m_selected_object_id); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 28d709ef71..d193a11a92 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -21,9 +21,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->set_name(_(L("Object Manipulation"))); m_og->label_width = 100; m_og->set_grid_vgap(5); - m_og->set_process_enter(); // We need to update new values only after press ENTER - m_og->m_on_change = [this](t_config_option_key opt_key, boost::any value) { + m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { std::vector axes{ "_x", "_y", "_z" }; if (opt_key == "scale_unit") { @@ -54,6 +53,29 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_scale_value(new_value); }; + m_og->m_fill_empty_value = [this](const std::string& opt_key) + { + if (opt_key == "scale_unit") + return; + + std::string param; + std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); + if (param == "position") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + m_og->set_value(opt_key, double_to_string(cache_position(axis))); + return; + } + + m_og->set_value(opt_key, double_to_string(0.0)); + }; + + m_og->m_set_focus = [this](const std::string& opt_key) + { + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key); + }; + ConfigOptionDef def; // Objects(sub-objects) name diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index 7b58d4c4eb..19140efe3b 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -17,7 +17,7 @@ protected: wxWindow* m_parent; public: OG_Settings(wxWindow* parent, const bool staticbox); - ~OG_Settings() {} + virtual ~OG_Settings() {} virtual bool IsShown(); virtual void Show(const bool show); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index be9a4e9cd7..4701ae20bb 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -44,7 +44,7 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co case coPercents: case coString: case coStrings: - m_fields.emplace(id, std::move(TextCtrl::Create(parent(), opt, id, process_enter))); + m_fields.emplace(id, std::move(TextCtrl::Create(parent(), opt, id))); break; case coBool: case coBools: @@ -67,16 +67,21 @@ const t_field& OptionsGroup::build_field(const t_config_option_key& id, const Co } // Grab a reference to fields for convenience const t_field& field = m_fields[id]; - field->m_on_change = [this](std::string opt_id, boost::any value) { + field->m_on_change = [this](const std::string& opt_id, const boost::any& value) { //! This function will be called from Field. //! Call OptionGroup._on_change(...) if (!m_disabled) this->on_change_OG(opt_id, value); }; - field->m_on_kill_focus = [this]() { + field->m_on_kill_focus = [this](const std::string& opt_id) { //! This function will be called from Field. if (!m_disabled) - this->on_kill_focus(); + this->on_kill_focus(opt_id); + }; + field->m_on_set_focus = [this](const std::string& opt_id) { + //! This function will be called from Field. + if (!m_disabled) + this->on_set_focus(opt_id); }; field->m_parent = parent(); @@ -277,6 +282,12 @@ Line OptionsGroup::create_single_option_line(const Option& option) const { return retval; } +void OptionsGroup::on_set_focus(const std::string& opt_key) +{ + if (m_set_focus != nullptr) + m_set_focus(opt_key); +} + void OptionsGroup::on_change_OG(const t_config_option_key& opt_id, const boost::any& value) { if (m_on_change != nullptr) m_on_change(opt_id, value); @@ -378,6 +389,15 @@ void ConfigOptionsGroup::back_to_config_value(const DynamicPrintConfig& config, on_change_OG(opt_key, get_value(opt_key)); } +void ConfigOptionsGroup::on_kill_focus(const std::string& opt_key) +{ + if (m_fill_empty_value) { + m_fill_empty_value(opt_key); + return; + } + reload_config(); +} + void ConfigOptionsGroup::reload_config() { for (t_opt_map::iterator it = m_opt_map.begin(); it != m_opt_map.end(); ++it) { auto opt_id = it->first; diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index dd135636d4..9097dcab60 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -85,7 +85,9 @@ public: size_t label_width {200}; wxSizer* sizer {nullptr}; column_t extra_column {nullptr}; - t_change m_on_change {nullptr}; + t_change m_on_change { nullptr }; + t_kill_focus m_fill_empty_value { nullptr }; + t_kill_focus m_set_focus { nullptr }; std::function m_get_initial_config{ nullptr }; std::function m_get_sys_config{ nullptr }; std::function have_sys_config{ nullptr }; @@ -94,8 +96,6 @@ public: wxFont label_font {wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT) }; int sidetext_width{ -1 }; - bool process_enter { false }; - /// Returns a copy of the pointer of the parent wxWindow. /// Accessor function is because users are not allowed to change the parent /// but defining it as const means a lot of const_casts to deal with wx functions. @@ -154,11 +154,6 @@ public: m_show_modified_btns = show; } - // The controls inside this option group will generate the event wxEVT_TEXT_ENTER - void set_process_enter() { - process_enter = true; - } - OptionsGroup( wxWindow* _parent, const wxString& title, bool is_tab_opt = false, column_t extra_clmn = nullptr) : m_parent(_parent), title(title), @@ -215,7 +210,8 @@ protected: const t_field& build_field(const Option& opt, wxStaticText* label = nullptr); void add_undo_buttuns_to_sizer(wxSizer* sizer, const t_field& field); - virtual void on_kill_focus () {}; + virtual void on_kill_focus(const std::string& opt_key) {}; + virtual void on_set_focus(const std::string& opt_key); virtual void on_change_OG(const t_config_option_key& opt_id, const boost::any& value); virtual void back_to_initial_value(const std::string& opt_key) {} virtual void back_to_sys_value(const std::string& opt_key) {} @@ -251,7 +247,7 @@ public: void back_to_initial_value(const std::string& opt_key) override; void back_to_sys_value(const std::string& opt_key) override; void back_to_config_value(const DynamicPrintConfig& config, const std::string& opt_key); - void on_kill_focus() override{ reload_config();} + void on_kill_focus(const std::string& opt_key) override;// { reload_config(); } void reload_config(); // return value shows visibility : false => all options are hidden void Hide(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 9a5c960bb5..fa39ac2407 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -270,7 +270,7 @@ Slic3r::GUI::PageShp Tab::add_options_page(const wxString& title, const std::str auto panel = this; #endif PageShp page(new Page(panel, title, icon_idx)); - page->SetScrollbars(1, 1, 1, 1); + page->SetScrollbars(1, 1, 1, 2); page->Hide(); m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5);