diff --git a/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h b/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h index 5a70fc39e7..39a4e1a7e3 100644 --- a/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h +++ b/src/libigl/igl/copyleft/cgal/SelfIntersectMesh.h @@ -635,13 +635,30 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< { using namespace std; + auto opposite_vertex = [](const Index a0, const Index a1) { + // get opposite index of A + int a2=-1; + for(int c=0;c<3;++c) + if(c!=a0 && c!=a1) { + a2 = c; + break; + } + assert(a2 != -1); + return a2; + }; + // must be co-planar - if( - A.supporting_plane() != B.supporting_plane() && - A.supporting_plane() != B.supporting_plane().opposite()) - { + Index a2 = opposite_vertex(shared[0].first, shared[1].first); + if (! B.supporting_plane().has_on(A.vertex(a2))) return false; - } + + Index b2 = opposite_vertex(shared[0].second, shared[1].second); + + if (int(CGAL::coplanar_orientation(A.vertex(shared[0].first), A.vertex(shared[1].first), A.vertex(a2))) * + int(CGAL::coplanar_orientation(B.vertex(shared[0].second), B.vertex(shared[1].second), B.vertex(b2))) < 0) + // There is certainly no self intersection as the non-shared triangle vertices lie on opposite sides of the shared edge. + return false; + // Since A and B are non-degenerate the intersection must be a polygon // (triangle). Either // - the vertex of A (B) opposite the shared edge of lies on B (A), or @@ -650,22 +667,10 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< // Determine if the vertex opposite edge (a0,a1) in triangle A lies in // (intersects) triangle B const auto & opposite_point_inside = []( - const Triangle_3 & A, const Index a0, const Index a1, const Triangle_3 & B) + const Triangle_3 & A, const Index a2, const Triangle_3 & B) -> bool { - // get opposite index - Index a2 = -1; - for(int c = 0;c<3;c++) - { - if(c != a0 && c != a1) - { - a2 = c; - break; - } - } - assert(a2 != -1); - bool ret = CGAL::do_intersect(A.vertex(a2),B); - return ret; + return CGAL::do_intersect(A.vertex(a2),B); }; // Determine if edge opposite vertex va in triangle A intersects edge @@ -681,8 +686,8 @@ inline bool igl::copyleft::cgal::SelfIntersectMesh< }; if( - !opposite_point_inside(A,shared[0].first,shared[1].first,B) && - !opposite_point_inside(B,shared[0].second,shared[1].second,A) && + !opposite_point_inside(A,a2,B) && + !opposite_point_inside(B,b2,A) && !opposite_edges_intersect(A,shared[0].first,B,shared[1].second) && !opposite_edges_intersect(A,shared[1].first,B,shared[0].second)) { @@ -936,4 +941,4 @@ inline void igl::copyleft::cgal::SelfIntersectMesh< //process_chunk(0, candidate_triangle_pairs.size()); } -#endif +#endif \ No newline at end of file diff --git a/src/libslic3r/MeshBoolean.cpp b/src/libslic3r/MeshBoolean.cpp index 025f2f97d8..1c848eb5f8 100644 --- a/src/libslic3r/MeshBoolean.cpp +++ b/src/libslic3r/MeshBoolean.cpp @@ -150,8 +150,17 @@ void minus(TriangleMesh &A, const TriangleMesh &B) triangle_mesh_to_cgal(B, meshB.m); CGALMesh meshResult; - CGALProc::corefine_and_compute_difference(meshA.m, meshB.m, meshResult.m); - + bool success = false; + try { + success = CGALProc::corefine_and_compute_difference(meshA.m, meshB.m, meshResult.m, + CGALParams::throw_on_self_intersection(true), CGALParams::throw_on_self_intersection(true)); + } + catch (const CGAL::Polygon_mesh_processing::Corefinement::Self_intersection_exception&) { + success = false; + } + if (! success) + throw std::runtime_error("CGAL corefine_and_compute_difference failed"); + A = cgal_to_triangle_mesh(meshResult.m); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index a70b977b4c..06c4f687b9 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -122,10 +122,10 @@ void SLAPrint::clear() } // Transformation without rotation around Z and without a shift by X and Y. -static Transform3d sla_trafo(const SLAPrint& p, const ModelObject &model_object) +Transform3d SLAPrint::sla_trafo(const ModelObject &model_object) const { - Vec3d corr = p.relative_correction(); + Vec3d corr = this->relative_correction(); ModelInstance &model_instance = *model_object.instances.front(); Vec3d offset = model_instance.get_offset(); @@ -376,7 +376,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con bool sla_trafo_differs = model_object.instances.empty() != model_object_new.instances.empty() || (! model_object.instances.empty() && - (! sla_trafo(*this, model_object).isApprox(sla_trafo(*this, model_object_new)) || + (! sla_trafo(model_object).isApprox(sla_trafo(model_object_new)) || model_object.instances.front()->is_left_handed() != model_object_new.instances.front()->is_left_handed())); if (model_parts_differ || sla_trafo_differs) { // The very first step (the slicing step) is invalidated. One may freely remove all associated PrintObjects. @@ -419,7 +419,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con if (model_object.sla_drain_holes != model_object_new.sla_drain_holes) { model_object.sla_drain_holes = model_object_new.sla_drain_holes; - update_apply_status(it_print_object_status->print_object->invalidate_step(slaposHollowing)); + update_apply_status(it_print_object_status->print_object->invalidate_step(slaposDrillHoles)); } // Copy the ModelObject name, input_file and instances. The instances will compared against PrintObject instances in the next step. @@ -453,7 +453,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, DynamicPrintConfig con // FIXME: this invalidates the transformed mesh in SLAPrintObject // which is expensive to calculate (especially the raw_mesh() call) - print_object->set_trafo(sla_trafo(*this, model_object), model_object.instances.front()->is_left_handed()); + print_object->set_trafo(sla_trafo(model_object), model_object.instances.front()->is_left_handed()); print_object->set_instances(std::move(new_instances)); @@ -1101,6 +1101,8 @@ const ExPolygons &SliceRecord::get_slice(SliceOrigin o) const bool SLAPrintObject::has_mesh(SLAPrintObjectStep step) const { switch (step) { + case slaposDrillHoles: + return m_hollowing_data && !m_hollowing_data->hollow_mesh_with_holes.empty(); case slaposSupportTree: return ! this->support_mesh().empty(); case slaposPad: @@ -1117,7 +1119,7 @@ TriangleMesh SLAPrintObject::get_mesh(SLAPrintObjectStep step) const return this->support_mesh(); case slaposPad: return this->pad_mesh(); - case slaposHollowing: + case slaposDrillHoles: if (m_hollowing_data) return m_hollowing_data->hollow_mesh_with_holes; [[fallthrough]]; diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 4ff7837bc6..c9f5198db8 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -78,13 +78,13 @@ public: // Support mesh is only valid if this->is_step_done(slaposPad) is true. const TriangleMesh& pad_mesh() const; - // Ready after this->is_step_done(slaposHollowing) is true + // Ready after this->is_step_done(slaposDrillHoles) is true const TriangleMesh& hollowed_interior_mesh() const; // Get the mesh that is going to be printed with all the modifications // like hollowing and drilled holes. const TriangleMesh & get_mesh_to_print() const { - return m_hollowing_data ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); + return (m_hollowing_data && is_step_done(slaposDrillHoles)) ? m_hollowing_data->hollow_mesh_with_holes : transformed_mesh(); } // This will return the transformed mesh which is cached @@ -421,6 +421,9 @@ public: // Extracted value from the configuration objects Vec3d relative_correction() const; + // Return sla tansformation for a given model_object + Transform3d sla_trafo(const ModelObject &model_object) const; + std::string output_filename(const std::string &filename_base = std::string()) const override; const SLAPrintStatistics& print_statistics() const { return m_print_statistics; } diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 2b4a1d5188..b7d1cfa1d8 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -27,7 +27,7 @@ namespace { const std::array OBJ_STEP_LEVELS = { 10, // slaposHollowing, - 10, // slaposDrillHolesIfHollowed + 10, // slaposDrillHoles 10, // slaposObjectSlice, 20, // slaposSupportPoints, 10, // slaposSupportTree, @@ -39,7 +39,7 @@ std::string OBJ_STEP_LABELS(size_t idx) { switch (idx) { case slaposHollowing: return L("Hollowing model"); - case slaposDrillHoles: return L("Drilling holes into hollowed model."); + case slaposDrillHoles: return L("Drilling holes into model."); case slaposObjectSlice: return L("Slicing model"); case slaposSupportPoints: return L("Generating support points"); case slaposSupportTree: return L("Generating support tree"); @@ -80,6 +80,7 @@ SLAPrint::Steps::Steps(SLAPrint *print) void SLAPrint::Steps::hollow_model(SLAPrintObject &po) { po.m_hollowing_data.reset(); + if (! po.m_config.hollowing_enable.getBool()) { BOOST_LOG_TRIVIAL(info) << "Skipping hollowing step!"; return; @@ -98,17 +99,37 @@ void SLAPrint::Steps::hollow_model(SLAPrintObject &po) else { po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); po.m_hollowing_data->interior = *meshptr; - auto &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; - hollowed_mesh = po.transformed_mesh(); - hollowed_mesh.merge(po.m_hollowing_data->interior); - hollowed_mesh.require_shared_vertices(); } } +// Drill holes into the hollowed/original mesh. void SLAPrint::Steps::drill_holes(SLAPrintObject &po) { - // Drill holes into the hollowed/original mesh. - if (po.m_model_object->sla_drain_holes.empty()) { + bool needs_drilling = ! po.m_model_object->sla_drain_holes.empty(); + bool is_hollowed = (po.m_hollowing_data && ! po.m_hollowing_data->interior.empty()); + + if (! is_hollowed && ! needs_drilling) { + // In this case we can dump any data that might have been + // generated on previous runs. + po.m_hollowing_data.reset(); + return; + } + + if (! po.m_hollowing_data) + po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); + + // Hollowing and/or drilling is active, m_hollowing_data is valid. + + // Regenerate hollowed mesh, even if it was there already. It may contain + // holes that are no longer on the frontend. + TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; + hollowed_mesh = po.transformed_mesh(); + if (! po.m_hollowing_data->interior.empty()) { + hollowed_mesh.merge(po.m_hollowing_data->interior); + hollowed_mesh.require_shared_vertices(); + } + + if (! needs_drilling) { BOOST_LOG_TRIVIAL(info) << "Drilling skipped (no holes)."; return; } @@ -124,17 +145,9 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po) holes_mesh.require_shared_vertices(); MeshBoolean::cgal::self_union(holes_mesh); //FIXME-fix and use the cgal version - // If there is no hollowed mesh yet, copy the original mesh. - if (! po.m_hollowing_data) { - po.m_hollowing_data.reset(new SLAPrintObject::HollowingData()); - po.m_hollowing_data->hollow_mesh_with_holes = po.transformed_mesh(); - } - - TriangleMesh &hollowed_mesh = po.m_hollowing_data->hollow_mesh_with_holes; - try { MeshBoolean::cgal::minus(hollowed_mesh, holes_mesh); - } catch (const std::runtime_error &ex) { + } catch (const std::runtime_error&) { throw std::runtime_error(L( "Drilling holes into the mesh failed. " "This is usually caused by broken model. Try to fix it first.")); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index bc6aa20fc5..06c4358099 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -93,6 +93,8 @@ namespace PerlUtils { extern std::string path_to_parent_path(const char *src); }; +std::string string_printf(const char *format, ...); + // Standard "generated by Slic3r version xxx timestamp xxx" header string, // to be placed at the top of Slic3r generated files. std::string header_slic3r_generated(); diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 7d9558f1e5..d3e4992ce8 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -230,23 +230,6 @@ static inline bool is_approx(Number value, Number test_value) return std::fabs(double(value) - double(test_value)) < double(EPSILON); } -template -std::string string_printf(const char *const _fmt, Args &&...args) -{ - static const size_t INITIAL_LEN = 1024; - std::vector buffer(INITIAL_LEN, '\0'); - - auto fmt = std::string("%s") + _fmt; - int bufflen = snprintf(buffer.data(), INITIAL_LEN - 1, fmt.c_str(), "", std::forward(args)...); - - if (bufflen >= int(INITIAL_LEN)) { - buffer.resize(size_t(bufflen) + 1); - snprintf(buffer.data(), buffer.size(), fmt.c_str(), "", std::forward(args)...); - } - - return std::string(buffer.begin(), buffer.begin() + bufflen); -} - } // namespace Slic3r #endif diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 9f0afa0614..c451507779 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -577,6 +577,29 @@ namespace PerlUtils { std::string path_to_parent_path(const char *src) { return boost::filesystem::path(src).parent_path().string(); } }; + +std::string string_printf(const char *format, ...) +{ + va_list args1; + va_start(args1, format); + va_list args2; + va_copy(args2, args1); + + static const size_t INITIAL_LEN = 200; + std::string buffer(INITIAL_LEN, '\0'); + + int bufflen = ::vsnprintf(buffer.data(), INITIAL_LEN - 1, format, args1); + + if (bufflen >= int(INITIAL_LEN)) { + buffer.resize(size_t(bufflen) + 1); + ::vsnprintf(buffer.data(), buffer.size(), format, args2); + } + + buffer.resize(bufflen); + + return buffer; +} + std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_APP_NAME " " SLIC3R_VERSION " on " ) + Utils::utc_timestamp(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 5d559c246d..aed907004f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -313,33 +313,38 @@ public: // Valid geometry_id should always be positive. std::pair geometry_id; // An ID containing the extruder ID (used to select color). - int extruder_id; - // Is this object selected? - bool selected; - // Is this object disabled from selection? - bool disabled; - // Is this object printable? - bool printable; - // Whether or not this volume is active for rendering - bool is_active; - // Whether or not to use this volume when applying zoom_to_volumes() - bool zoom_to_volumes; - // Wheter or not this volume is enabled for outside print volume detection in shader. - bool shader_outside_printer_detection_enabled; - // Wheter or not this volume is outside print volume. - bool is_outside; + int extruder_id; + + // Various boolean flags. + struct { + // Is this object selected? + bool selected : 1; + // Is this object disabled from selection? + bool disabled : 1; + // Is this object printable? + bool printable : 1; + // Whether or not this volume is active for rendering + bool is_active : 1; + // Whether or not to use this volume when applying zoom_to_volumes() + bool zoom_to_volumes : 1; + // Wheter or not this volume is enabled for outside print volume detection in shader. + bool shader_outside_printer_detection_enabled : 1; + // Wheter or not this volume is outside print volume. + bool is_outside : 1; + // Wheter or not this volume has been generated from a modifier + bool is_modifier : 1; + // Wheter or not this volume has been generated from the wipe tower + bool is_wipe_tower : 1; + // Wheter or not this volume has been generated from an extrusion path + bool is_extrusion_path : 1; + // Wheter or not to always render this volume using its own alpha + bool force_transparent : 1; + // Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE) + bool force_native_color : 1; + }; + // Is mouse or rectangle selection over this object to select/deselect it ? - EHoverState hover; - // Wheter or not this volume has been generated from a modifier - bool is_modifier; - // Wheter or not this volume has been generated from the wipe tower - bool is_wipe_tower; - // Wheter or not this volume has been generated from an extrusion path - bool is_extrusion_path; - // Wheter or not to always render this volume using its own alpha - bool force_transparent; - // Whether or not always use the volume's own color (not using SELECTED/HOVER/DISABLED/OUTSIDE) - bool force_native_color; + EHoverState hover; // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index dd3e6ffc0d..e12beb5c72 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -35,7 +35,7 @@ public: wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false, const bool dark_mode = false); /*static */wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency, bool suppress_scaling = false); - /*static */wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } + /*static */wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3], bool suppress_scaling = false) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE, suppress_scaling); } /*static */wxBitmap mkclear(size_t width, size_t height) { return mksolid(width, height, 0, 0, 0, wxALPHA_TRANSPARENT); } static bool parse_color(const std::string& scolor, unsigned char* rgb_out); diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 4a5bea9a10..5990984b02 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1,4 +1,5 @@ #include "wxExtensions.hpp" +#include "libslic3r/GCode/PreviewData.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "I18N.hpp" @@ -322,7 +323,8 @@ void Control::SetTicksValues(const CustomGCode::Info& custom_gcode_per_print_z) // Switch to the "Feature type"/"Tool" from the very beginning of a new object slicing after deleting of the old one post_ticks_changed_event(); - m_ticks.mode = custom_gcode_per_print_z.mode; + if (custom_gcode_per_print_z.mode) + m_ticks.mode = custom_gcode_per_print_z.mode; Refresh(); Update(); @@ -1169,6 +1171,8 @@ void Control::OnKeyDown(wxKeyEvent &event) m_ticks.suppress_minus(true); delete_current_tick(); } + else if (event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(false); else if (is_horizontal()) { if (key == WXK_LEFT || key == WXK_RIGHT) @@ -1194,6 +1198,9 @@ void Control::OnKeyUp(wxKeyEvent &event) { if (event.GetKeyCode() == WXK_CONTROL) m_is_one_layer = false; + else if (event.GetKeyCode() == WXK_SHIFT) + UseDefaultColors(true); + Refresh(); Update(); event.Skip(); @@ -1278,9 +1285,11 @@ std::array Control::get_active_extruders_for_tick(int tick) const // Get used extruders for tick. // Means all extruders(tools) which will be used during printing from current tick to the end -std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z) const +std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extruder, double print_z, t_mode force_mode/* = t_mode::Undef*/) const { - if (mode == t_mode::MultiExtruder) + t_mode e_mode = !force_mode ? mode : force_mode; + + if (e_mode == t_mode::MultiExtruder) { // #ys_FIXME: get tool ordering from _correct_ place const ToolOrdering& tool_ordering = GUI::wxGetApp().plater()->fff_print().get_tool_ordering(); @@ -1301,8 +1310,8 @@ std::set TickCodeInfo::get_used_extruders_for_tick(int tick, int only_extru return used_extruders; } - const int default_initial_extruder = mode == t_mode::MultiAsSingle ? std::max(only_extruder, 1) : 1; - if (ticks.empty()) + const int default_initial_extruder = e_mode == t_mode::MultiAsSingle ? std::max(only_extruder, 1) : 1; + if (ticks.empty() || e_mode == t_mode::SingleExtruder) return {default_initial_extruder}; std::set used_extruders; @@ -1346,10 +1355,13 @@ void Control::OnRightUp(wxMouseEvent& event) if (m_show_context_menu) { wxMenu menu; - if (m_mode == t_mode::SingleExtruder) + if (m_mode == t_mode::SingleExtruder) { append_menu_item(&menu, wxID_ANY, _(L("Add color change")) + " (M600)", "", [this](wxCommandEvent&) { add_code_as_tick(ColorChangeCode); }, "colorchange_add_m", &menu, [](){return true;}, this); + + UseDefaultColors(false); + } else { append_change_extruder_menu_item(&menu); @@ -1688,9 +1700,18 @@ bool Control::check_ticks_changed_event(const std::string& gcode) return true; } - std::string TickCodeInfo::get_color_for_tick(TickCode tick, const std::string& code, const int extruder) { + if (mode == t_mode::SingleExtruder && code == ColorChangeCode && m_use_default_colors) + { + const std::vector& colors = GCodePreviewData::ColorPrintColors(); + if (ticks.empty()) + return colors[0]; + m_default_color_idx++; + + return colors[m_default_color_idx % colors.size()]; + } + std::vector colors = GUI::wxGetApp().plater()->get_extruder_colors_from_plater_config(); std::string color = colors[extruder - 1]; @@ -1740,6 +1761,9 @@ bool TickCodeInfo::add_tick(const int tick, std::string& code, const int extrude return false; } + if (mode == t_mode::SingleExtruder) + m_use_default_colors = true; + ticks.emplace(TickCode{ tick, code, extruder, color }); return true; } @@ -1840,7 +1864,7 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod { // We should mark a tick as a "MeaninglessColorChange", // if it has a ColorChange for unused extruder from current print to end of the print - std::set used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z); + std::set used_extruders_for_tick = get_used_extruders_for_tick(tick.tick, only_extruder, print_z, out_mode); if (used_extruders_for_tick.find(tick.extruder) == used_extruders_for_tick.end()) return ctMeaninglessColorChange; @@ -1868,7 +1892,6 @@ ConflictType TickCodeInfo::is_conflict_tick(const TickCode& tick, t_mode out_mod { // We should mark a tick as a "MeaninglessToolChange", // if it has a ToolChange to the same extruder - auto it = ticks.find(tick); if (it == ticks.begin()) return tick.extruder == std::max(only_extruder, 1) ? ctMeaninglessToolChange : ctNone; diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index f14af621f9..01181fc2f5 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -67,12 +67,14 @@ class TickCodeInfo std::string pause_print_msg; bool m_suppress_plus = false; bool m_suppress_minus = false; + bool m_use_default_colors= false; + int m_default_color_idx = 0; std::string get_color_for_tick(TickCode tick, const std::string& code, const int extruder); public: - std::set ticks {}; - t_mode mode = t_mode::SingleExtruder; + std::set ticks {}; + t_mode mode = t_mode::Undef; bool empty() const { return ticks.empty(); } void set_pause_print_msg(const std::string& message) { pause_print_msg = message; } @@ -88,12 +90,13 @@ public: // Get used extruders for tick. // Means all extruders(tools) which will be used during printing from current tick to the end - std::set get_used_extruders_for_tick(int tick, int only_extruder, double print_z) const; + std::set get_used_extruders_for_tick(int tick, int only_extruder, double print_z, t_mode force_mode = t_mode::Undef) const; void suppress_plus (bool suppress) { m_suppress_plus = suppress; } void suppress_minus(bool suppress) { m_suppress_minus = suppress; } bool suppressed_plus () { return m_suppress_plus; } bool suppressed_minus() { return m_suppress_minus; } + void set_default_colors(bool default_colors_on) { m_use_default_colors = default_colors_on; } }; @@ -186,7 +189,11 @@ public: m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder : only_extruder < 0 ? t_mode::SingleExtruder : t_mode::MultiAsSingle; + if (!m_ticks.mode) + m_ticks.mode = m_mode; m_only_extruder = only_extruder; + + UseDefaultColors(m_mode == t_mode::SingleExtruder); } bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } @@ -201,6 +208,7 @@ public: void OnLeftUp(wxMouseEvent& event); void OnEnterWin(wxMouseEvent& event) { enter_window(event, true); } void OnLeaveWin(wxMouseEvent& event) { enter_window(event, false); } + void UseDefaultColors(bool def_colors_on) { m_ticks.set_default_colors(def_colors_on); } void OnWheel(wxMouseEvent& event); void OnKeyDown(wxKeyEvent &event); void OnKeyUp(wxKeyEvent &event); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0253e430c7..26ab4f4359 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2136,8 +2136,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re }; // SLA steps to pull the preview meshes for. - typedef std::array SLASteps; - SLASteps sla_steps = { slaposSupportTree, slaposPad }; + typedef std::array SLASteps; + SLASteps sla_steps = { slaposDrillHoles, slaposSupportTree, slaposPad }; struct SLASupportState { std::array::value> step; }; @@ -2184,7 +2184,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Consider the DONE step without a valid mesh as invalid for the purpose // of mesh visualization. state.step[istep].state = PrintStateBase::INVALID; - else + else if (sla_steps[istep] != slaposDrillHoles) for (const ModelInstance* model_instance : print_object->model_object()->instances) // Only the instances, which are currently printable, will have the SLA support structures kept. // The instances outside the print bed will have the GLVolumes of their support structures released. @@ -2197,7 +2197,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } std::sort(model_volume_state.begin(), model_volume_state.end(), model_volume_state_lower); std::sort(aux_volume_state.begin(), aux_volume_state.end(), model_volume_state_lower); - // Release all ModelVolume based GLVolumes not found in the current Model. + // Release all ModelVolume based GLVolumes not found in the current Model. Find the GLVolume of a hollowed mesh. for (size_t volume_id = 0; volume_id < m_volumes.volumes.size(); ++volume_id) { GLVolume* volume = m_volumes.volumes[volume_id]; ModelVolumeState key(volume); @@ -2279,6 +2279,9 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re if (it_old_volume != deleted_volumes.end() && it_old_volume->composite_id == it->composite_id) // If a volume changed its ObjectID, but it reuses a GLVolume's CompositeID, maintain its selection. map_glvolume_old_to_new[it_old_volume->volume_idx] = m_volumes.volumes.size(); + // Note the index of the loaded volume, so that we can reload the main model GLVolume with the hollowed mesh + // later in this function. + it->volume_idx = m_volumes.volumes.size(); m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized); m_volumes.volumes.back()->geometry_id = key.geometry_id; update_object_list = true; @@ -2307,8 +2310,6 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re const ModelObject *model_object = print_object->model_object(); // Find an index of the ModelObject int object_idx; - if (std::all_of(state.step.begin(), state.step.end(), [](const PrintStateBase::StateWithTimeStamp &state){ return state.state != PrintStateBase::DONE; })) - continue; // There may be new SLA volumes added to the scene for this print_object. // Find the object index of this print_object in the Model::objects list. auto it = std::find(sla_print->model().objects.begin(), sla_print->model().objects.end(), model_object); @@ -2327,29 +2328,53 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != model_object->instances.end()); int instance_idx = it - model_object->instances.begin(); for (size_t istep = 0; istep < sla_steps.size(); ++ istep) - if (state.step[istep].state == PrintStateBase::DONE) { - ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); - auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); - assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); - if (it->new_geometry()) { + if (sla_steps[istep] == slaposDrillHoles) { + // Hollowing is a special case, where the mesh from the backend is being loaded into the 1st volume of an instance, + // not into its own GLVolume. + // There shall always be such a GLVolume allocated. + ModelVolumeState key(model_object->volumes.front()->id(), instance.instance_id); + auto it = std::lower_bound(model_volume_state.begin(), model_volume_state.end(), key, model_volume_state_lower); + assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id); + assert(!it->new_geometry()); + GLVolume &volume = *m_volumes.volumes[it->volume_idx]; + if (! volume.offsets.empty() && state.step[istep].timestamp != volume.offsets.front()) { + // The backend either produced a new hollowed mesh, or it invalidated the one that the front end has seen. + volume.indexed_vertex_array.release_geometry(); + if (state.step[istep].state == PrintStateBase::DONE) { + TriangleMesh mesh = print_object->get_mesh(slaposDrillHoles); + assert(! mesh.empty()); + mesh.transform(sla_print->sla_trafo(*m_model->objects[volume.object_idx()]).inverse()); + volume.indexed_vertex_array.load_mesh(mesh); + } else { + // Reload the original volume. + volume.indexed_vertex_array.load_mesh(m_model->objects[volume.object_idx()]->volumes[volume.volume_idx()]->mesh()); + } + volume.finalize_geometry(true); + } + //FIXME it is an ugly hack to write the timestamp into the "offsets" field to not have to add another member variable + // to the GLVolume. We should refactor GLVolume significantly, so that the GLVolume will not contain member variables + // of various concenrs (model vs. 3D print path). + volume.offsets = { state.step[istep].timestamp }; + } else if (state.step[istep].state == PrintStateBase::DONE) { + // Check whether there is an existing auxiliary volume to be updated, or a new auxiliary volume to be created. + ModelVolumeState key(state.step[istep].timestamp, instance.instance_id.id); + auto it = std::lower_bound(aux_volume_state.begin(), aux_volume_state.end(), key, model_volume_state_lower); + assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); + if (it->new_geometry()) { // This can be an SLA support structure that should not be rendered (in case someone used undo // to revert to before it was generated). If that's the case, we should not generate anything. if (model_object->sla_points_status != sla::PointsStatus::NoPoints) instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); else shift_zs[object_idx] = 0.; + } else { + // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. + m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); } - else { - // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. - m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); - m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); - } } } -// // stores the current volumes count -// size_t volumes_count = m_volumes.volumes.size(); - for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) m_volumes.load_object_auxiliary(print_object, object_idx, instances[istep], sla_steps[istep], state.step[istep].timestamp, m_initialized); @@ -2881,6 +2906,46 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; + else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { + switch (keyCode) + { + case WXK_NUMPAD_LEFT: case WXK_LEFT: + case WXK_NUMPAD_RIGHT: case WXK_RIGHT: + case WXK_NUMPAD_UP: case WXK_UP: + case WXK_NUMPAD_DOWN: case WXK_DOWN: + { + do_move(L("Gizmo-Move")); + m_gizmos.update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + // Let the plater know that the dragging finished, so a delayed refresh + // of the scene with the background processing data should be performed. + post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + refresh_camera_scene_box(); + m_dirty = true; + + break; + } + case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: + case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: + { + do_rotate(L("Gizmo-Rotate")); + m_gizmos.update_data(); + + wxGetApp().obj_manipul()->set_dirty(); + // Let the plater know that the dragging finished, so a delayed refresh + // of the scene with the background processing data should be performed. + post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + // updates camera target constraints + refresh_camera_scene_box(); + m_dirty = true; + + break; + } + default: { break; } + } + } } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers(); @@ -2902,14 +2967,43 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else if (keyCode == WXK_CONTROL) m_dirty = true; - // DoubleSlider navigation in Preview - else if (keyCode == WXK_LEFT || - keyCode == WXK_RIGHT || - keyCode == WXK_UP || - keyCode == WXK_DOWN ) + else if (m_gizmos.is_enabled() && !m_selection.is_empty()) { - if (dynamic_cast(m_canvas->GetParent()) != nullptr) - post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); + auto do_move = [this](const Vec3d& displacement) { + m_selection.start_dragging(); + m_selection.translate(displacement); + m_dirty = true; +// wxGetApp().obj_manipul()->set_dirty(); + }; + auto do_rotate = [this](double angle_z_rad) { + m_selection.start_dragging(); + m_selection.rotate(Vec3d(0.0, 0.0, angle_z_rad), TransformationType(TransformationType::World_Relative_Joint)); + m_dirty = true; +// wxGetApp().obj_manipul()->set_dirty(); + }; + + switch (keyCode) + { + case WXK_NUMPAD_LEFT: case WXK_LEFT: { do_move(-Vec3d::UnitX()); break; } + case WXK_NUMPAD_RIGHT: case WXK_RIGHT: { do_move(Vec3d::UnitX()); break; } + case WXK_NUMPAD_UP: case WXK_UP: { do_move(Vec3d::UnitY()); break; } + case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-Vec3d::UnitY()); break; } + case WXK_NUMPAD_PAGEUP: case WXK_PAGEUP: { do_rotate(0.25 * M_PI); break; } + case WXK_NUMPAD_PAGEDOWN: case WXK_PAGEDOWN: { do_rotate(-0.25 * M_PI); break; } + default: { break; } + } + } + else if (!m_gizmos.is_enabled()) + { + // DoubleSlider navigation in Preview + if (keyCode == WXK_LEFT || + keyCode == WXK_RIGHT || + keyCode == WXK_UP || + keyCode == WXK_DOWN) + { + if (dynamic_cast(m_canvas->GetParent()) != nullptr) + post_event(wxKeyEvent(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, evt)); + } } } } @@ -6256,8 +6350,6 @@ void GLCanvas3D::_load_sla_shells() unsigned int initial_volumes_count = (unsigned int)m_volumes.volumes.size(); for (const SLAPrintObject::Instance& instance : obj->instances()) { add_volume(*obj, 0, instance, obj->get_mesh_to_print(), GLVolume::MODEL_COLOR[0], true); -// if (! obj->hollowed_interior_mesh().empty()) -// add_volume(*obj, -int(slaposHollowing), instance, obj->hollowed_interior_mesh(), GLVolume::MODEL_COLOR[0], false); // Set the extruder_id and volume_id to achieve the same color as in the 3D scene when // through the update_volumes_colors_by_extruder() call. m_volumes.volumes.back()->extruder_id = obj->model_object()->volumes.front()->extruder_id(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 33ddd475cd..d630f152f1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -978,6 +978,8 @@ void ObjectList::key_event(wxKeyEvent& event) ) { remove(); } + else if (event.GetKeyCode() == WXK_F5) + wxGetApp().plater()->reload_all_from_disk(); else if (wxGetKeyState(wxKeyCode('A')) && wxGetKeyState(WXK_CONTROL/*WXK_SHIFT*/)) select_item_all_children(); else if (wxGetKeyState(wxKeyCode('C')) && wxGetKeyState(WXK_CONTROL)) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index c256413a81..5a1cd619ca 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -594,6 +594,11 @@ void Preview::create_double_slider() // sizer, m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_double_slider_from_canvas, this); + m_canvas_widget->Bind(wxEVT_KEY_UP, [this](wxKeyEvent& event) { + if (event.GetKeyCode() == WXK_SHIFT) + m_slider->UseDefaultColors(true); + event.Skip(); + }); m_slider->Bind(wxEVT_SCROLL_CHANGED, &Preview::on_sliders_scroll_changed, this); @@ -779,6 +784,8 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) } else if (key == 'S') m_slider->ChangeOneLayerLock(); + else if (key == WXK_SHIFT) + m_slider->UseDefaultColors(false); else event.Skip(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 7dce249f28..af022352e0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -4,6 +4,9 @@ #include #include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "libslic3r/SLAPrint.hpp" +#include "slic3r/GUI/MeshUtils.hpp" @@ -302,5 +305,87 @@ unsigned char picking_checksum_alpha_channel(unsigned char red, unsigned char gr return b; } + + +bool CommonGizmosData::update_from_backend(GLCanvas3D& canvas, ModelObject* model_object) +{ + recent_update = false; + + if (m_model_object != model_object + || (model_object && m_model_object_id != model_object->id())) { + m_model_object = model_object; + m_print_object_idx = -1; + m_mesh_raycaster.reset(); + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_old_mesh = nullptr; + m_mesh = nullptr; + m_backend_mesh_transformed.clear(); + if (m_model_object) { + m_active_instance = canvas.get_selection().get_instance_idx(); + m_active_instance_bb_radius = m_model_object->instance_bounding_box(m_active_instance).radius(); + } + + recent_update = true; + } + + + if (! m_model_object || ! canvas.get_selection().is_from_single_instance()) + return false; + + int old_po_idx = m_print_object_idx; + + // First we need a pointer to the respective SLAPrintObject. The index into objects vector is + // cached so we don't have todo it on each render. We only search for the po if needed: + if (m_print_object_idx < 0 || (int)canvas.sla_print()->objects().size() != m_print_objects_count) { + m_print_objects_count = canvas.sla_print()->objects().size(); + m_print_object_idx = -1; + for (const SLAPrintObject* po : canvas.sla_print()->objects()) { + ++m_print_object_idx; + if (po->model_object()->id() == m_model_object->id()) + break; + } + } + + m_mesh = nullptr; + // Load either the model_object mesh, or one provided by the backend + // This mesh does not account for the possible Z up SLA offset. + // The backend mesh needs to be transformed and because a pointer to it is + // saved, a copy is stored as a member (FIXME) + if (m_print_object_idx >=0) { + const SLAPrintObject* po = canvas.sla_print()->objects()[m_print_object_idx]; + if (po->is_step_done(slaposDrillHoles)) { + m_backend_mesh_transformed = po->get_mesh_to_print(); + m_backend_mesh_transformed.transform(canvas.sla_print()->sla_trafo(*m_model_object).inverse()); + m_mesh = &m_backend_mesh_transformed; + } + } + + if (! m_mesh) { + m_mesh = &m_model_object->volumes.front()->mesh(); + m_backend_mesh_transformed.clear(); + } + + m_model_object_id = m_model_object->id(); + + if (m_mesh != m_old_mesh) { + wxBusyCursor wait; + m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh)); + m_object_clipper.reset(); + m_supports_clipper.reset(); + m_old_mesh = m_mesh; + m_clipping_plane_distance = 0.f; + m_clipping_plane_distance_stash = 0.f; + recent_update = true; + return true; + } + if (! recent_update) + recent_update = m_print_object_idx < 0 && old_po_idx >= 0; + + return m_print_object_idx < 0 ? old_po_idx >=0 : false; +} + + + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 9479174fbd..5cd3d9d84c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -31,6 +31,8 @@ static const float CONSTRAINED_COLOR[4] = { 0.5f, 0.5f, 0.5f, 1.0f }; class ImGuiWrapper; class CommonGizmosData; +class GLCanvas3D; +class ClippingPlane; class GLGizmoBase { @@ -189,9 +191,13 @@ class MeshClipper; class CommonGizmosData { public: const TriangleMesh* mesh() const { - return (! m_mesh ? nullptr : (m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); + return (! m_mesh ? nullptr : m_mesh); //(m_cavity_mesh ? m_cavity_mesh.get() : m_mesh)); } + bool update_from_backend(GLCanvas3D& canvas, ModelObject* model_object); + + bool recent_update = false; + ModelObject* m_model_object = nullptr; @@ -200,8 +206,8 @@ public: std::unique_ptr m_object_clipper; std::unique_ptr m_supports_clipper; - std::unique_ptr m_cavity_mesh; - std::unique_ptr m_volume_with_cavity; + //std::unique_ptr m_cavity_mesh; + //std::unique_ptr m_volume_with_cavity; int m_active_instance = -1; float m_active_instance_bb_radius = 0; @@ -209,6 +215,22 @@ public: int m_print_object_idx = -1; int m_print_objects_count = -1; int m_old_timestamp = -1; + + float m_clipping_plane_distance = 0.f; + std::unique_ptr m_clipping_plane; + + void stash_clipping_plane() { + m_clipping_plane_distance_stash = m_clipping_plane_distance; + } + + void unstash_clipping_plane() { + m_clipping_plane_distance = m_clipping_plane_distance_stash; + } + +private: + const TriangleMesh* m_old_mesh; + TriangleMesh m_backend_mesh_transformed; + float m_clipping_plane_distance_stash = 0.f; }; } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index e98446749f..52d710249b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "slic3r/GUI/GUI_App.hpp" @@ -189,7 +191,7 @@ void GLGizmoCut::update_max_z(const Selection& selection) const void GLGizmoCut::set_cut_z(double cut_z) const { // Clamp the plane to the object's bounding box - m_cut_z = std::max(0.0, std::min(m_max_z, cut_z)); + m_cut_z = std::clamp(cut_z, 0.0, m_max_z); } void GLGizmoCut::perform_cut(const Selection& selection) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 6e5738a422..b6e10861fc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -25,6 +25,9 @@ class GLGizmoCut : public GLGizmoBase public: GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + double get_cut_z() const { return m_cut_z; } + void set_cut_z(double cut_z) const; + protected: virtual bool on_init(); virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } @@ -40,7 +43,6 @@ protected: private: void update_max_z(const Selection& selection) const; - void set_cut_z(double cut_z) const; void perform_cut(const Selection& selection); double calc_projection(const Linef3& mouse_ray) const; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index 0422982745..b585a8e4f2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -22,7 +22,7 @@ GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filenam : GLGizmoBase(parent, icon_filename, sprite_id, cd) , m_quadric(nullptr) { - m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); + m_c->m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) // using GLU_FILL does not work when the instance's transformation @@ -40,7 +40,7 @@ bool GLGizmoHollow::on_init() { m_shortcut_key = WXK_CONTROL_H; m_desc["enable"] = _(L("Hollow this object")); - m_desc["preview"] = _(L("Preview")); + m_desc["preview"] = _(L("Preview hollowed and drilled model")); m_desc["offset"] = _(L("Offset")) + ": "; m_desc["quality"] = _(L("Quality")) + ": "; m_desc["closing_distance"] = _(L("Closing distance")) + ": "; @@ -55,38 +55,16 @@ bool GLGizmoHollow::on_init() return true; } -void GLGizmoHollow::set_sla_support_data(ModelObject* model_object, const Selection& selection) +void GLGizmoHollow::set_sla_support_data(ModelObject*, const Selection&) { - if (! model_object || selection.is_empty()) { - m_c->m_model_object = nullptr; - return; - } + if (m_c->recent_update) { - bool something_changed = false; - - if (m_c->m_model_object != model_object - || m_c->m_model_object_id != model_object->id() - || m_c->m_active_instance != selection.get_instance_idx()) { - m_c->m_model_object = model_object; - m_c->m_print_object_idx = -1; - m_c->m_active_instance = selection.get_instance_idx(); - something_changed = true; - } - - if (model_object && something_changed && selection.is_from_single_instance()) - { - // Cache the bb - it's needed for dealing with the clipping plane quite often - // It could be done inside update_mesh but one has to account for scaling of the instance. - m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); - - if (is_mesh_update_necessary()) { - update_mesh(); + if (m_c->m_model_object) reload_cache(); - } if (m_state == On) { m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); } else @@ -109,8 +87,8 @@ void GLGizmoHollow::on_render() const return; } - if (! m_c->m_mesh) - const_cast(this)->update_mesh(); + // !!! is it necessary? + //const_cast(this)->m_c->update_from_backend(m_parent, m_c->m_model_object); glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -132,7 +110,7 @@ void GLGizmoHollow::on_render() const void GLGizmoHollow::render_hollowed_mesh() const { - if (m_c->m_volume_with_cavity) { + /*if (m_c->m_volume_with_cavity) { m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); m_parent.get_shader().start_using(); @@ -148,14 +126,13 @@ void GLGizmoHollow::render_hollowed_mesh() const m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); - } + }*/ } - void GLGizmoHollow::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f || m_c->mesh()->empty()) + if (m_c->m_clipping_plane_distance == 0.f) return; // Get transformation of the instance @@ -177,22 +154,14 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const m_c->m_object_clipper.reset(new MeshClipper); m_c->m_object_clipper->set_mesh(*m_c->mesh()); } - m_c->m_object_clipper->set_plane(*m_clipping_plane); + m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); m_c->m_object_clipper->set_transformation(trafo); // Next, ask the backend if supports are already calculated. If so, we are gonna cut them too. - // First we need a pointer to the respective SLAPrintObject. The index into objects vector is - // cached so we don't have todo it on each render. We only search for the po if needed: - if (m_c->m_print_object_idx < 0 || (int)m_parent.sla_print()->objects().size() != m_c->m_print_objects_count) { - m_c->m_print_objects_count = m_parent.sla_print()->objects().size(); - m_c->m_print_object_idx = -1; - for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { - ++m_c->m_print_object_idx; - if (po->model_object()->id() == m_c->m_model_object->id()) - break; - } - } + //if (m_c->m_print_object_idx < 0) + // m_c->update_from_backend(m_parent, m_c->m_model_object); + if (m_c->m_print_object_idx >= 0) { const SLAPrintObject* print_object = m_parent.sla_print()->objects()[m_c->m_print_object_idx]; @@ -208,7 +177,7 @@ void GLGizmoHollow::render_clipping_plane(const Selection& selection) const m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); m_c->m_old_timestamp = timestamp; } - m_c->m_supports_clipper->set_plane(*m_clipping_plane); + m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); m_c->m_supports_clipper->set_transformation(supports_trafo); } else @@ -345,46 +314,12 @@ void GLGizmoHollow::render_points(const Selection& selection, bool picking) cons bool GLGizmoHollow::is_mesh_point_clipped(const Vec3d& point) const { - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f) return false; Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; - return m_clipping_plane->is_point_clipped(transformed_point); -} - - - -bool GLGizmoHollow::is_mesh_update_necessary() const -{ - return ((m_state == On) && (m_c->m_model_object != nullptr) && !m_c->m_model_object->instances.empty()) - && ((m_c->m_model_object->id() != m_c->m_model_object_id) || ! m_c->m_mesh); -} - - - -void GLGizmoHollow::update_mesh() -{ - if (! m_c->m_model_object) - return; - - wxBusyCursor wait; - // this way we can use that mesh directly. - // This mesh does not account for the possible Z up SLA offset. - m_c->m_mesh = &m_c->m_model_object->volumes.front()->mesh(); - - // If this is different mesh than last time - if (m_c->m_model_object_id != m_c->m_model_object->id()) { - m_c->m_cavity_mesh.reset(); // dump the cavity - m_c->m_volume_with_cavity.reset(); - m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); - m_c->m_mesh_raycaster.reset(); - } - - if (! m_c->m_mesh_raycaster) - m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); - - m_c->m_model_object_id = m_c->m_model_object->id(); + return m_c->m_clipping_plane->is_point_clipped(transformed_point); } @@ -393,9 +328,11 @@ void GLGizmoHollow::update_mesh() // Return false if no intersection was found, true otherwise. bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - // if the gizmo doesn't have the V, F structures for igl, calculate them first: if (! m_c->m_mesh_raycaster) - update_mesh(); + return false; + // if the gizmo doesn't have the V, F structures for igl, calculate them first: + // !!! is it really necessary? + //m_c->update_from_backend(m_parent, m_c->m_model_object); const Camera& camera = m_parent.get_camera(); const Selection& selection = m_parent.get_selection(); @@ -406,7 +343,7 @@ bool GLGizmoHollow::unproject_on_mesh(const Vec2d& mouse_pos, std::pairm_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); return true; @@ -492,7 +429,7 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos points_inside.push_back(points[idx].cast()); // Only select/deselect points that are actually visible - for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -546,13 +483,13 @@ bool GLGizmoHollow::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_pos } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { - m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f); + m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); update_clipping_plane(true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { - m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f); + m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); update_clipping_plane(true); return true; } @@ -605,7 +542,7 @@ void GLGizmoHollow::update_mesh_raycaster(std::unique_ptr &&rc) { m_c->m_mesh_raycaster = std::move(rc); m_c->m_object_clipper.reset(); - m_c->m_volume_with_cavity.reset(); + //m_c->m_volume_with_cavity.reset(); } void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) @@ -622,8 +559,8 @@ void GLGizmoHollow::hollow_mesh(bool postpone_error_messages) void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) { // Called from Plater when the UI job finishes - m_c->m_cavity_mesh = std::move(mesh); - + /*m_c->m_cavity_mesh = std::move(mesh); + if(m_c->m_cavity_mesh) { // First subtract the holes: if (! m_c->m_model_object->sla_drain_holes.empty()) { @@ -665,10 +602,10 @@ void GLGizmoHollow::update_hollowed_mesh(std::unique_ptr &&mesh) m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); } - if (m_clipping_plane_distance == 0.f) { - m_clipping_plane_distance = 0.5f; + if (m_c->m_clipping_plane_distance == 0.f) { + m_c->m_clipping_plane_distance = 0.5f; update_clipping_plane(); - } + }*/ } std::vector> GLGizmoHollow::get_config_options(const std::vector& keys) const @@ -701,10 +638,10 @@ std::vector> GLGizmoHollo ClippingPlane GLGizmoHollow::get_sla_clipping_plane() const { - if (!m_c->m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) + if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) return ClippingPlane::ClipsNothing(); else - return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); + return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); } @@ -724,18 +661,24 @@ RENDER_AGAIN: // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: const float settings_sliders_left = - std::max(std::max(m_imgui->calc_text_size(m_desc.at("offset")).x, - m_imgui->calc_text_size(m_desc.at("quality")).x), - m_imgui->calc_text_size(m_desc.at("closing_distance")).x) - + m_imgui->scaled(1.f); + std::max({m_imgui->calc_text_size(m_desc.at("offset")).x, + m_imgui->calc_text_size(m_desc.at("quality")).x, + m_imgui->calc_text_size(m_desc.at("closing_distance")).x, + m_imgui->calc_text_size(m_desc.at("hole_diameter")).x, + m_imgui->calc_text_size(m_desc.at("hole_depth")).x}) + + m_imgui->scaled(1.f); const float clipping_slider_left = std::max(m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x, m_imgui->calc_text_size(m_desc.at("reset_direction")).x) + m_imgui->scaled(1.5f); - const float diameter_slider_left = m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); + const float diameter_slider_left = settings_sliders_left; //m_imgui->calc_text_size(m_desc.at("hole_diameter")).x + m_imgui->scaled(1.f); const float minimal_slider_width = m_imgui->scaled(4.f); - //const float buttons_width_approx = m_imgui->calc_text_size(m_desc.at("apply_changes")).x + m_imgui->calc_text_size(m_desc.at("discard_changes")).x + m_imgui->scaled(1.5f); - float window_width = minimal_slider_width + std::max(std::max(settings_sliders_left, clipping_slider_left), diameter_slider_left); - window_width = std::max(std::max(window_width, /*buttons_width_approx*/0.f), 0.f); + float window_width = minimal_slider_width + std::max({settings_sliders_left, clipping_slider_left, diameter_slider_left}); + window_width = std::max(window_width, m_imgui->calc_text_size(m_desc.at("preview")).x); + + if (m_imgui->button(m_desc["preview"])) + hollow_mesh(); + + ImGui::Separator(); { auto opts = get_config_options({"hollowing_enable"}); @@ -745,11 +688,8 @@ RENDER_AGAIN: wxGetApp().obj_list()->update_and_show_object_settings_item(); } } - m_imgui->disabled_begin(! m_enable_hollowing); - ImGui::SameLine(); - if (m_imgui->button(m_desc["preview"])) - hollow_mesh(); + m_imgui->disabled_begin(! m_enable_hollowing); std::vector opts_keys = {"hollowing_min_thickness", "hollowing_quality", "hollowing_closing_distance"}; auto opts = get_config_options(opts_keys); @@ -834,7 +774,7 @@ RENDER_AGAIN: // m_imgui->text(" "); // vertical gap ImGui::Separator(); - float diameter_upper_cap = 5.f; + float diameter_upper_cap = 15.; if (m_new_hole_radius > diameter_upper_cap) m_new_hole_radius = diameter_upper_cap; m_imgui->text(m_desc.at("hole_diameter")); @@ -904,7 +844,7 @@ RENDER_AGAIN: // Following is rendered in both editing and non-editing mode: // m_imgui->text(""); ImGui::Separator(); - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f) m_imgui->text(m_desc.at("clipping_of_view")); else { if (m_imgui->button(m_desc.at("reset_direction"))) { @@ -916,7 +856,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); // make sure supports are shown/hidden as appropriate @@ -973,7 +913,7 @@ bool GLGizmoHollow::on_is_selectable() const std::string GLGizmoHollow::on_get_name() const { - return (_(L("Hollowing")) + " [H]").ToUTF8().data(); + return (_(L("Hollowing and drilling")) + " [H]").ToUTF8().data(); } @@ -995,17 +935,19 @@ void GLGizmoHollow::on_set_state() if (m_state == On && m_old_state != On) { // the gizmo was just turned on //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); - if (is_mesh_update_necessary()) - update_mesh(); + //m_c->update_from_backend(m_parent, m_c->m_model_object); + m_c->unstash_clipping_plane(); + update_clipping_plane(m_c->m_clipping_plane_distance != 0.f); // we'll now reload support points: if (m_c->m_model_object) reload_cache(); m_parent.toggle_model_objects_visibility(false); - if (m_c->m_model_object) - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + if (m_c->m_model_object) { + m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(m_show_supports, m_c->m_model_object, m_c->m_active_instance); + } // Set default head diameter from config. //const DynamicPrintConfig& cfg = wxGetApp().preset_bundle->sla_prints.get_edited_preset().config; @@ -1014,8 +956,9 @@ void GLGizmoHollow::on_set_state() if (m_state == Off && m_old_state != Off) { // the gizmo was just turned Off //Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_parent.toggle_model_objects_visibility(true); - m_clipping_plane_distance = 0.f; - update_clipping_plane(); + m_c->stash_clipping_plane(); + m_c->m_clipping_plane_distance = 0.f; + update_clipping_plane(true); // Release clippers and the AABB raycaster. m_c->m_object_clipper.reset(); m_c->m_supports_clipper.reset(); @@ -1060,8 +1003,8 @@ void GLGizmoHollow::on_stop_dragging() void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) { - ar(m_clipping_plane_distance, - *m_clipping_plane, + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, m_c->m_model_object_id, m_new_hole_radius, m_new_hole_height, @@ -1074,8 +1017,8 @@ void GLGizmoHollow::on_load(cereal::BinaryInputArchive& ar) void GLGizmoHollow::on_save(cereal::BinaryOutputArchive& ar) const { - ar(m_clipping_plane_distance, - *m_clipping_plane, + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, m_c->m_model_object_id, m_new_hole_radius, m_new_hole_height, @@ -1128,12 +1071,14 @@ void GLGizmoHollow::reload_cache() void GLGizmoHollow::update_clipping_plane(bool keep_normal) const { - Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); + if (! m_c->m_model_object) + return; + Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? + m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); float dist = normal.dot(center); - *m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); + *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); m_parent.set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index 79b0a69297..6264304b73 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -53,14 +53,12 @@ private: void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; void render_hollowed_mesh() const; - bool is_mesh_update_necessary() const; - void update_mesh(); void hollow_mesh(bool postpone_error_messages = false); bool unsaved_changes() const; bool m_show_supports = true; float m_new_hole_radius = 2.f; // Size of a new hole. - float m_new_hole_height = 5.f; + float m_new_hole_height = 6.f; mutable std::vector m_selected; // which holes are currently selected bool m_enable_hollowing = true; @@ -72,13 +70,9 @@ private: float m_closing_d_stash = 2.f; Vec3f m_hole_before_drag = Vec3f::Zero(); - sla::DrainHoles m_holes_stash; - - - float m_clipping_plane_distance = 0.f; - std::unique_ptr m_clipping_plane; + //std::unique_ptr m_clipping_plane; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index c6e0d9007b..879a09da2e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -27,7 +27,7 @@ GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& ic , m_quadric(nullptr) , m_its(nullptr) { - m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); + m_c->m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); m_quadric = ::gluNewQuadric(); if (m_quadric != nullptr) // using GLU_FILL does not work when the instance's transformation @@ -63,43 +63,22 @@ bool GLGizmoSlaSupports::on_init() void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const Selection& selection) { - if (! model_object || selection.is_empty()) { - m_c->m_model_object = nullptr; - return; - } - - bool something_changed = false; - - if (m_c->m_model_object != model_object - || m_c->m_model_object_id != model_object->id() - || m_c->m_active_instance != selection.get_instance_idx()) { - m_c->m_model_object = model_object; - m_c->m_print_object_idx = -1; - m_c->m_active_instance = selection.get_instance_idx(); - something_changed = true; - } - - if (model_object && selection.is_from_single_instance()) - { - // Cache the bb - it's needed for dealing with the clipping plane quite often - // It could be done inside update_mesh but one has to account for scaling of the instance. - if (something_changed) { - m_c->m_active_instance_bb_radius = m_c->m_model_object->instance_bounding_box(m_c->m_active_instance).radius(); - if (m_state == On) { - m_parent.toggle_model_objects_visibility(false); - m_parent.toggle_model_objects_visibility(! m_c->m_cavity_mesh, m_c->m_model_object, m_c->m_active_instance); - m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); - } - else - m_parent.toggle_model_objects_visibility(true, nullptr, -1); + if (m_c->recent_update) { + if (m_state == On) { + m_parent.toggle_model_objects_visibility(false); + m_parent.toggle_model_objects_visibility(/*! m_c->m_cavity_mesh*/ true, m_c->m_model_object, m_c->m_active_instance); + m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); } + else + m_parent.toggle_model_objects_visibility(true, nullptr, -1); - if (is_mesh_update_necessary()) { - update_mesh(); + disable_editing_mode(); + if (m_c->m_model_object) reload_cache(); - } + } - // If we triggered autogeneration before, check backend and fetch results if they are there + // If we triggered autogeneration before, check backend and fetch results if they are there + if (m_c->m_model_object) { if (m_c->m_model_object->sla_points_status == sla::PointsStatus::Generating) get_data_from_backend(); } @@ -120,9 +99,6 @@ void GLGizmoSlaSupports::on_render() const return; } - if (! m_its || ! m_c->m_mesh) - const_cast(this)->update_mesh(); - glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -143,7 +119,7 @@ void GLGizmoSlaSupports::on_render() const void GLGizmoSlaSupports::render_hollowed_mesh() const { - if (m_c->m_volume_with_cavity) { + /*if (m_c->m_volume_with_cavity) { m_c->m_volume_with_cavity->set_sla_shift_z(m_z_shift); m_parent.get_shader().start_using(); @@ -159,14 +135,14 @@ void GLGizmoSlaSupports::render_hollowed_mesh() const m_c->m_volume_with_cavity->set_instance_transformation(m_c->m_model_object->instances[size_t(m_c->m_active_instance)]->get_transformation()); m_c->m_volume_with_cavity->render(color_id, print_box_detection_id, print_box_worldmatrix_id); m_parent.get_shader().stop_using(); - } + }*/ } void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const { - if (m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) + if (m_c->m_clipping_plane_distance == 0.f || m_c->m_mesh->empty()) return; // Get transformation of the instance @@ -188,7 +164,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const m_c->m_object_clipper.reset(new MeshClipper); m_c->m_object_clipper->set_mesh(*m_c->mesh()); } - m_c->m_object_clipper->set_plane(*m_clipping_plane); + m_c->m_object_clipper->set_plane(*m_c->m_clipping_plane); m_c->m_object_clipper->set_transformation(trafo); @@ -219,7 +195,7 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const m_c->m_supports_clipper->set_mesh(print_object->support_mesh()); m_c->m_old_timestamp = timestamp; } - m_c->m_supports_clipper->set_plane(*m_clipping_plane); + m_c->m_supports_clipper->set_plane(*m_c->m_clipping_plane); m_c->m_supports_clipper->set_transformation(supports_trafo); } else @@ -359,7 +335,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } // Now render the drain holes: - if (! m_c->m_cavity_mesh) { + /*if (! m_c->m_cavity_mesh) { render_color[0] = 0.7f; render_color[1] = 0.7f; render_color[2] = 0.7f; @@ -394,7 +370,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glFrontFace(GL_CCW); glsafe(::glPopMatrix()); } - } + }*/ if (!picking) glsafe(::glDisable(GL_LIGHTING)); @@ -406,51 +382,22 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const { - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f) return false; Vec3d transformed_point = m_c->m_model_object->instances[m_c->m_active_instance]->get_transformation().get_matrix() * point; transformed_point(2) += m_z_shift; - return m_clipping_plane->is_point_clipped(transformed_point); + return m_c->m_clipping_plane->is_point_clipped(transformed_point); } -bool GLGizmoSlaSupports::is_mesh_update_necessary() const -{ - return ((m_state == On) && (m_c->m_model_object != nullptr) && !m_c->m_model_object->instances.empty()) - && ((m_c->m_model_object->id() != m_c->m_model_object_id) || m_its == nullptr); -} - - - -void GLGizmoSlaSupports::update_mesh() -{ - if (! m_c->m_model_object) - return; - - wxBusyCursor wait; - // this way we can use that mesh directly. - // This mesh does not account for the possible Z up SLA offset. - m_c->m_mesh = &m_c->m_model_object->volumes.front()->mesh(); - m_its = &m_c->m_mesh->its; - - // If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it. - if (m_c->m_model_object_id != m_c->m_model_object->id() || ! m_c->m_mesh_raycaster) - m_c->m_mesh_raycaster.reset(new MeshRaycaster(*m_c->mesh())); - - m_c->m_model_object_id = m_c->m_model_object->id(); - disable_editing_mode(); -} - - // Unprojects the mouse position on the mesh and saves hit point and normal of the facet into pos_and_normal // Return false if no intersection was found, true otherwise. bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair& pos_and_normal) { - // if the gizmo doesn't have the V, F structures for igl, calculate them first: if (! m_c->m_mesh_raycaster) - update_mesh(); + return false; const Camera& camera = m_parent.get_camera(); const Selection& selection = m_parent.get_selection(); @@ -461,20 +408,20 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pairm_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) { + if (m_c->m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_c->m_clipping_plane.get())) { // Check whether the hit is in a hole bool in_hole = false; // In case the hollowed and drilled mesh is available, we can allow // placing points in holes, because they should never end up // on surface that's been drilled away. - if (! m_c->m_cavity_mesh) { + /*if (! m_c->m_cavity_mesh) { for (const sla::DrainHole& hole : m_c->m_model_object->sla_drain_holes) { if (hole.is_inside(hit)) { in_hole = true; break; } } - } + }*/ if (! in_hole) { // Return both the point and the facet normal. pos_and_normal = std::make_pair(hit, normal); @@ -555,7 +502,7 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous points_inside.push_back(points[idx].cast()); // Only select/deselect points that are actually visible - for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get())) + for (size_t idx : m_c->m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_c->m_clipping_plane.get())) { if (rectangle_status == GLSelectionRectangle::Deselect) unselect_point(points_idxs[idx]); @@ -632,13 +579,13 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous } if (action == SLAGizmoEventType::MouseWheelUp && control_down) { - m_clipping_plane_distance = std::min(1.f, m_clipping_plane_distance + 0.01f); + m_c->m_clipping_plane_distance = std::min(1.f, m_c->m_clipping_plane_distance + 0.01f); update_clipping_plane(true); return true; } if (action == SLAGizmoEventType::MouseWheelDown && control_down) { - m_clipping_plane_distance = std::max(0.f, m_clipping_plane_distance - 0.01f); + m_c->m_clipping_plane_distance = std::max(0.f, m_c->m_clipping_plane_distance - 0.01f); update_clipping_plane(true); return true; } @@ -719,10 +666,10 @@ std::vector GLGizmoSlaSupports::get_config_options(const st ClippingPlane GLGizmoSlaSupports::get_sla_clipping_plane() const { - if (!m_c->m_model_object || m_state == Off || m_clipping_plane_distance == 0.f) + if (!m_c->m_model_object || m_state == Off || m_c->m_clipping_plane_distance == 0.f) return ClippingPlane::ClipsNothing(); else - return ClippingPlane(-m_clipping_plane->get_normal(), m_clipping_plane->get_data()[3]); + return ClippingPlane(-m_c->m_clipping_plane->get_normal(), m_c->m_clipping_plane->get_data()[3]); } @@ -944,7 +891,7 @@ RENDER_AGAIN: // Following is rendered in both editing and non-editing mode: ImGui::Separator(); - if (m_clipping_plane_distance == 0.f) + if (m_c->m_clipping_plane_distance == 0.f) { ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc.at("clipping_of_view")); @@ -959,7 +906,7 @@ RENDER_AGAIN: ImGui::SameLine(clipping_slider_left); ImGui::PushItemWidth(window_width - clipping_slider_left); - if (ImGui::SliderFloat(" ", &m_clipping_plane_distance, 0.f, 1.f, "%.2f")) + if (ImGui::SliderFloat(" ", &m_c->m_clipping_plane_distance, 0.f, 1.f, "%.2f")) update_clipping_plane(true); @@ -1043,15 +990,17 @@ void GLGizmoSlaSupports::on_set_state() if (m_state == On && m_old_state != On) { // the gizmo was just turned on Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); - if (is_mesh_update_necessary()) - update_mesh(); + + m_c->unstash_clipping_plane(); + update_clipping_plane(m_c->m_clipping_plane_distance != 0.f); + // we'll now reload support points: if (m_c->m_model_object) reload_cache(); m_parent.toggle_model_objects_visibility(false); - if (m_c->m_model_object && ! m_c->m_cavity_mesh) + if (m_c->m_model_object /*&& ! m_c->m_cavity_mesh*/) m_parent.toggle_model_objects_visibility(true, m_c->m_model_object, m_c->m_active_instance); m_parent.toggle_sla_auxiliaries_visibility(! m_editing_mode, m_c->m_model_object, m_c->m_active_instance); @@ -1081,13 +1030,14 @@ void GLGizmoSlaSupports::on_set_state() Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned off"))); m_parent.toggle_model_objects_visibility(true); m_normal_cache.clear(); - m_clipping_plane_distance = 0.f; - update_clipping_plane(); + m_c->stash_clipping_plane(); + m_c->m_clipping_plane_distance = 0.f; + update_clipping_plane(true); // Release clippers and the AABB raycaster. m_its = nullptr; m_c->m_object_clipper.reset(); m_c->m_supports_clipper.reset(); - m_c->m_mesh_raycaster.reset(); + //m_c->m_mesh_raycaster.reset(); } } m_old_state = m_state; @@ -1127,8 +1077,8 @@ void GLGizmoSlaSupports::on_stop_dragging() void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) { - ar(m_clipping_plane_distance, - *m_clipping_plane, + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, m_c->m_model_object_id, m_new_point_head_diameter, m_normal_cache, @@ -1141,8 +1091,8 @@ void GLGizmoSlaSupports::on_load(cereal::BinaryInputArchive& ar) void GLGizmoSlaSupports::on_save(cereal::BinaryOutputArchive& ar) const { - ar(m_clipping_plane_distance, - *m_clipping_plane, + ar(m_c->m_clipping_plane_distance, + *m_c->m_clipping_plane, m_c->m_model_object_id, m_new_point_head_diameter, m_normal_cache, @@ -1244,6 +1194,9 @@ void GLGizmoSlaSupports::reload_cache() bool GLGizmoSlaSupports::has_backend_supports() const { + if (! m_c->m_model_object) + return false; + // find SlaPrintObject with this ID for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { if (po->model_object()->id() == m_c->m_model_object->id()) @@ -1338,12 +1291,12 @@ bool GLGizmoSlaSupports::unsaved_changes() const void GLGizmoSlaSupports::update_clipping_plane(bool keep_normal) const { - Vec3d normal = (keep_normal && m_clipping_plane->get_normal() != Vec3d::Zero() ? - m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); + Vec3d normal = (keep_normal && m_c->m_clipping_plane->get_normal() != Vec3d::Zero() ? + m_c->m_clipping_plane->get_normal() : -m_parent.get_camera().get_dir_forward()); const Vec3d& center = m_c->m_model_object->instances[m_c->m_active_instance]->get_offset() + Vec3d(0., 0., m_z_shift); float dist = normal.dot(center); - *m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); + *m_c->m_clipping_plane = ClippingPlane(normal, (dist - (-m_c->m_active_instance_bb_radius) - m_c->m_clipping_plane_distance * 2*m_c->m_active_instance_bb_radius)); m_parent.set_as_dirty(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 3697e7af69..7ded6aadb4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -91,8 +91,6 @@ private: void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; void render_hollowed_mesh() const; - bool is_mesh_update_necessary() const; - void update_mesh(); bool unsaved_changes() const; bool m_lock_unique_islands = false; @@ -105,8 +103,7 @@ private: mutable std::vector m_editing_cache; // a support point and whether it is currently selected std::vector m_normal_cache; // to restore after discarding changes or undo/redo - float m_clipping_plane_distance = 0.f; - std::unique_ptr m_clipping_plane; + //std::unique_ptr m_clipping_plane; // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index ccc6369e46..be0f480071 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -348,6 +348,9 @@ void GLGizmosManager::set_sla_support_data(ModelObject* model_object) if (!m_enabled || m_gizmos.empty()) return; + // Update common data for hollowing and sla support gizmos. + m_common_gizmos_data->update_from_backend(m_parent, model_object); + dynamic_cast(m_gizmos[SlaSupports].get())->set_sla_support_data(model_object, m_parent.get_selection()); dynamic_cast(m_gizmos[Hollow].get())->set_sla_support_data(model_object, m_parent.get_selection()); } @@ -497,7 +500,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports || m_current == Hollow)) - // don't allow dragging objects with the Sla gizmo on + // don't allow dragging objects with the Sla gizmo on processed = true; else if (evt.Dragging() && (m_current == SlaSupports || m_current == Hollow) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { @@ -554,12 +557,9 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) else if (evt.LeftUp() && is_dragging()) { switch (m_current) { - case Move : m_parent.do_move(L("Gizmo-Move")); - break; - case Scale : m_parent.do_scale(L("Gizmo-Scale")); - break; - case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); - break; + case Move : m_parent.do_move(L("Gizmo-Move")); break; + case Scale : m_parent.do_scale(L("Gizmo-Scale")); break; + case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); break; default : break; } @@ -788,6 +788,21 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } + else if (m_current == Cut) + { + auto do_move = [this, &processed](double delta_z) { + GLGizmoCut* cut = dynamic_cast(get_current()); + cut->set_cut_z(delta_z + cut->get_cut_z()); + processed = true; + }; + + switch (keyCode) + { + case WXK_NUMPAD_UP: case WXK_UP: { do_move(1.0); break; } + case WXK_NUMPAD_DOWN: case WXK_DOWN: { do_move(-1.0); break; } + default: { break; } + } + } } if (processed) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8afd98d11e..bbb7db74dd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2076,7 +2076,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); }); view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); }); - view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { if (!this->model.objects.empty()) this->reload_all_from_disk(); }); + view3D_canvas->Bind(EVT_GLCANVAS_RELOAD_FROM_DISK, [this](SimpleEvent&) { this->reload_all_from_disk(); }); // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); @@ -3451,6 +3451,9 @@ void Plater::priv::reload_from_disk() void Plater::priv::reload_all_from_disk() { + if (model.objects.empty()) + return; + Plater::TakeSnapshot snapshot(q, _(L("Reload all from disk"))); Plater::SuppressSnapshots suppress(q); @@ -5130,7 +5133,7 @@ void Plater::reslice_SLA_supports(const ModelObject &object, bool postpone_error void Plater::reslice_SLA_hollowing(const ModelObject &object, bool postpone_error_messages) { - reslice_SLA_until_step(slaposHollowing, object, postpone_error_messages); + reslice_SLA_until_step(slaposDrillHoles, object, postpone_error_messages); } void Plater::reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages) diff --git a/tests/libslic3r/libslic3r_tests.cpp b/tests/libslic3r/libslic3r_tests.cpp index b9c615d90a..f4dcab42a0 100644 --- a/tests/libslic3r/libslic3r_tests.cpp +++ b/tests/libslic3r/libslic3r_tests.cpp @@ -1,6 +1,6 @@ #include -#include "libslic3r/libslic3r.h" +#include "libslic3r/Utils.hpp" namespace {