From 0afe2aec1e34d66974f54bbeceedfb38b65f59ec Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 7 Dec 2018 14:10:16 +0100 Subject: [PATCH 001/132] First version of SLA support points generation --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/PrintConfig.cpp | 27 +++++ src/libslic3r/PrintConfig.hpp | 8 ++ src/libslic3r/SLA/SLAAutoSupports.cpp | 155 ++++++++++++++++++++++++++ src/libslic3r/SLA/SLAAutoSupports.hpp | 41 +++++++ src/libslic3r/TriangleMesh.hpp | 2 +- src/slic3r/GUI/GLGizmo.cpp | 51 +++++++-- src/slic3r/GUI/GLGizmo.hpp | 3 + src/slic3r/GUI/Preset.cpp | 3 + src/slic3r/GUI/Tab.cpp | 5 + 10 files changed, 288 insertions(+), 9 deletions(-) create mode 100644 src/libslic3r/SLA/SLAAutoSupports.cpp create mode 100644 src/libslic3r/SLA/SLAAutoSupports.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 669efd620..df3ddb6de 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -139,6 +139,8 @@ add_library(libslic3r STATIC Rasterizer/Rasterizer.cpp SLAPrint.cpp SLAPrint.hpp + SLA/SLAAutoSupports.hpp + SLA/SLAAutoSupports.cpp Slicing.cpp Slicing.hpp SlicingAdaptive.cpp diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index d86ddc25b..4b3b1e80a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2561,6 +2561,33 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->default_value = new ConfigOptionFloat(5.0); + def = this->add("support_density_at_horizontal", coInt); + def->label = L("Density on horizontal surfaces"); + def->category = L("Supports"); + def->tooltip = L("How many support points (approximately) should be placed on horizontal surface."); + def->sidetext = L("points per square dm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionInt(500); + + def = this->add("support_density_at_45", coInt); + def->label = L("Density on surfaces at 45 degrees"); + def->category = L("Supports"); + def->tooltip = L("How many support points (approximately) should be placed on surface sloping at 45 degrees."); + def->sidetext = L("points per square dm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionInt(250); + + def = this->add("support_minimal_z", coFloat); + def->label = L("Minimal support point height"); + def->category = L("Supports"); + def->tooltip = L("No support points will be placed lower than this value from the bottom."); + def->sidetext = L("mm"); + def->cli = ""; + def->min = 0; + def->default_value = new ConfigOptionFloat(0.f); + def = this->add("pad_enable", coBool); def->label = L("Use pad"); def->category = L("Pad"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index dac2de7cc..9804d72ee 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -955,6 +955,11 @@ public: // and the model object's bounding box bottom. Units in mm. ConfigOptionFloat support_object_elevation /*= 5.0*/; + /////// Following options influence automatic support points placement: + ConfigOptionInt support_density_at_horizontal; + ConfigOptionInt support_density_at_45; + ConfigOptionFloat support_minimal_z; + // Now for the base pool (pad) ///////////////////////////////////////////// // Enabling or disabling support creation @@ -987,6 +992,9 @@ protected: OPT_PTR(support_base_height); OPT_PTR(support_critical_angle); OPT_PTR(support_max_bridge_length); + OPT_PTR(support_density_at_horizontal); + OPT_PTR(support_density_at_45); + OPT_PTR(support_minimal_z); OPT_PTR(support_object_elevation); OPT_PTR(pad_enable); OPT_PTR(pad_wall_thickness); diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp new file mode 100644 index 000000000..73c418911 --- /dev/null +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -0,0 +1,155 @@ +#include "igl/random_points_on_mesh.h" +#include "igl/AABB.h" + +#include "SLAAutoSupports.hpp" +#include "Model.hpp" + +#include + + +namespace Slic3r { + +SLAAutoSupports::SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c) +: m_model_object(mo), mesh(), m_config(c) +{} + + +float SLAAutoSupports::approximate_geodesic_distance(const Vec3f& p1, const Vec3f& p2, Vec3f& n1, Vec3f& n2) +{ + n1.normalize(); + n2.normalize(); + + Vec3f v = (p2-p1); + v.normalize(); + + float c1 = n1.dot(v); + float c2 = n2.dot(v); + float result = pow(p1(0)-p2(0), 2) + pow(p1(1)-p2(1), 2) + pow(p1(2)-p2(2), 2); + // Check for division by zero: + if(fabs(c1 - c2) > 0.0001) + result *= (asin(c1) - asin(c2)) / (c1 - c2); + return result; +} + + +void SLAAutoSupports::generate() +{ + // Loads the ModelObject raw_mesh and transforms it by first instance's transformation matrix (disregarding translation). + // Instances only differ in z-rotation, so it does not matter which of them will be used for the calculation. + // The supports point will be calculated on this mesh (so scaling ang vertical direction is correctly accounted for). + // Results will be inverse-transformed to raw_mesh coordinates. + TriangleMesh mesh = m_model_object.raw_mesh(); + Transform3d transformation_matrix = m_model_object.instances[0]->get_matrix(true/*dont_translate*/); + mesh.transform(transformation_matrix); + + // Check that the object is thick enough to produce any support points + BoundingBoxf3 bb = mesh.bounding_box(); + if (bb.size()(2) < m_config.minimal_z) + return; + + // All points that we curretly have must be transformed too, so distance to them is correcly calculated. + for (Vec3f& point : m_model_object.sla_support_points) + point = transformation_matrix.cast() * point; + + const stl_file& stl = mesh.stl; + Eigen::MatrixXf V; + Eigen::MatrixXi F; + V.resize(3 * stl.stats.number_of_facets, 3); + F.resize(stl.stats.number_of_facets, 3); + for (unsigned int i=0; ivertex[0](0); V(3*i+0, 1) = facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2); + V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2); + V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2); + F(i, 0) = 3*i+0; + F(i, 1) = 3*i+1; + F(i, 2) = 3*i+2; + } + + // In order to calculate distance to already placed points, we must keep know which facet the point lies on. + std::vector facets_normals; + + // The AABB hierarchy will be used to find normals of already placed points. + // The points added automatically will just push_back the new normal on the fly. + igl::AABB aabb; + aabb.init(V, F); + for (unsigned int i=0; i dump; + Eigen::MatrixXf query_point = m_model_object.sla_support_points[i]; + aabb.squared_distance(V, F, query_point, facet_idx, dump); + Vec3f a1 = V.row(F(facet_idx,1)) - V.row(F(facet_idx,0)); + Vec3f a2 = V.row(F(facet_idx,2)) - V.row(F(facet_idx,0)); + Vec3f normal = a1.cross(a2); + normal.normalize(); + facets_normals.push_back(normal); + } + + // New potential support point is randomly generated on the mesh and distance to all already placed points is calculated. + // In case it is never smaller than certain limit (depends on the new point's facet normal), the point is accepted. + // The process stops after certain number of points is refused in a row. + Vec3f point; + Vec3f normal; + int added_points = 0; + int refused_points = 0; + const int refused_limit = std::min(100, (int)(1. / m_config.density_at_horizontal)); + // Angle at which the density reaches zero: + const float threshold_angle = std::min(M_PI_2, M_PI_4 * acos(0.f/m_config.density_at_horizontal) / acos(m_config.density_at_45/m_config.density_at_horizontal)); + + srand(time(NULL)); // rand() is used by igl::random_point_on_mesh + while (refused_points < refused_limit) { + // Place a random point on the mesh and calculate corresponding facet's normal: + Eigen::VectorXi FI; + Eigen::MatrixXf B; + igl::random_points_on_mesh(1, V, F, B, FI); + point = B(0,0)*V.row(F(FI(0),0)) + + B(0,1)*V.row(F(FI(0),1)) + + B(0,2)*V.row(F(FI(0),2)); + if (point(2) - bb.min(2) < m_config.minimal_z) + continue; + + Vec3f a1 = V.row(F(FI(0),1)) - V.row(F(FI(0),0)); + Vec3f a2 = V.row(F(FI(0),2)) - V.row(F(FI(0),0)); + normal = a1.cross(a2); + normal.normalize(); + + // calculate angle between the normal and vertical: + float angle = angle_from_normal(normal); + if (angle > threshold_angle) + continue; + + const float distance_limit = 1./(2.4*get_required_density(angle)); + bool add_it = true; + for (unsigned int i=0; i() * point; +} + + + +float SLAAutoSupports::get_required_density(float angle) const +{ + // calculation would be density_0 * cos(angle). To provide one more degree of freedom, we will scale the angle + // to get the user-set density for 45 deg. So it ends up as density_0 * cos(K * angle). + float K = 4*acos(m_config.density_at_45/m_config.density_at_horizontal) / M_PI; + return std::max(0., m_config.density_at_horizontal * cos(K*angle)); +} + + + +} // namespace Slic3r \ No newline at end of file diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp new file mode 100644 index 000000000..637f0909c --- /dev/null +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -0,0 +1,41 @@ +#ifndef SLAAUTOSUPPORTS_HPP_ +#define SLAAUTOSUPPORTS_HPP_ + +#include + + +namespace Slic3r { + +class ModelObject; + + + + +class SLAAutoSupports { +public: + struct Config { + float density_at_horizontal; + float density_at_45; + float minimal_z; + }; + + SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c); + void generate(); + +private: + TriangleMesh mesh; + static float angle_from_normal(const stl_normal& normal) { return acos((-normal.normalized())(2)); } + float get_required_density(float angle) const; + static float approximate_geodesic_distance(const Vec3f& p1, const Vec3f& p2, Vec3f& n1, Vec3f& n2); + + ModelObject& m_model_object; + SLAAutoSupports::Config m_config; +}; + + + + +} // namespace Slic3r + + +#endif // SLAAUTOSUPPORTS_HPP_ \ No newline at end of file diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index e2bf47014..81f64ac85 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -35,7 +35,7 @@ public: void repair(); float volume(); void check_topology(); - bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == this->stl.stats.number_of_facets; } + bool is_manifold() const { return this->stl.stats.connected_facets_3_edge == (int)this->stl.stats.number_of_facets; } void WriteOBJFile(char* output_file); void scale(float factor); void scale(const Vec3d &versor); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 239b45702..34de24983 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -20,6 +20,7 @@ #include #include +#include "Tab.hpp" #include "GUI.hpp" #include "GUI_Utils.hpp" #include "GUI_App.hpp" @@ -1079,7 +1080,7 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, const GLCanvas3D:: m_imgui->set_next_window_pos(x, y, ImGuiCond_Always); m_imgui->set_next_window_bg_alpha(0.5f); m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - m_imgui->input_vec3("", m_scale, 100.0f, "%.2f"); + m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f"); m_imgui->end(); } #endif // ENABLE_IMGUI @@ -1782,8 +1783,7 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const G #else void GLGizmoSlaSupports::set_model_object_ptr(ModelObject* model_object) { - if (model_object != nullptr) - { + if (model_object != nullptr) { m_starting_center = Vec3d::Zero(); m_model_object = model_object; @@ -1812,6 +1812,7 @@ void GLGizmoSlaSupports::on_render(const GLCanvas3D::Selection& selection) const Vec3d dragged_offset = selection.get_bounding_box().center() - m_starting_center; #endif // !ENABLE_SLA_SUPPORT_GIZMO_MOD + for (auto& g : m_grabbers) { g.color[0] = 1.f; g.color[1] = 0.f; @@ -1871,6 +1872,16 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection, float render_color[3]; for (int i = 0; i < (int)m_grabbers.size(); ++i) { + // first precalculate the grabber position in world coordinates, so that the grabber + // is not scaled with the object (as it would be if rendered with current gl matrix). + Eigen::Matrix glmatrix; + glGetFloatv (GL_MODELVIEW_MATRIX, glmatrix.data()); + Eigen::Matrix grabber_pos; + for (int j=0; j<3; ++j) + grabber_pos(j) = m_grabbers[i].center(j); + grabber_pos[3] = 1.f; + Eigen::Matrix grabber_world_position = glmatrix * grabber_pos; + if (!picking && (m_hover_id == i)) { render_color[0] = 1.0f - m_grabbers[i].color[0]; @@ -1882,9 +1893,10 @@ void GLGizmoSlaSupports::render_grabbers(const GLCanvas3D::Selection& selection, ::glColor3fv(render_color); ::glPushMatrix(); - ::glTranslated(m_grabbers[i].center(0), m_grabbers[i].center(1), m_grabbers[i].center(2)); + ::glLoadIdentity(); + ::glTranslated(grabber_world_position(0), grabber_world_position(1), grabber_world_position(2) + z_shift); ::gluQuadricDrawStyle(m_quadric, GLU_SILHOUETTE); - ::gluSphere(m_quadric, 0.75, 64, 32); + ::gluSphere(m_quadric, 0.75, 64, 36); ::glPopMatrix(); } @@ -1933,7 +1945,7 @@ void GLGizmoSlaSupports::render_grabbers(bool picking) const GLUquadricObj *quadric; quadric = ::gluNewQuadric(); ::gluQuadricDrawStyle(quadric, GLU_FILL ); - ::gluSphere( quadric , 0.75f, 36 , 18 ); + ::gluSphere( quadric , 0.75f, 64 , 32 ); ::gluDeleteQuadric(quadric); ::glPopMatrix(); if (!picking) @@ -1992,6 +2004,7 @@ void GLGizmoSlaSupports::update_mesh() // we'll now reload Grabbers (selection might have changed): m_grabbers.clear(); + for (const Vec3f& point : m_model_object->sla_support_points) { m_grabbers.push_back(Grabber()); m_grabbers.back().center = point.cast(); @@ -2083,7 +2096,6 @@ void GLGizmoSlaSupports::delete_current_grabber(bool delete_all) if (delete_all) { m_grabbers.clear(); m_model_object->sla_support_points.clear(); - m_parent.reload_scene(true); // in case this was called from ImGui overlay dialog, the refresh would be delayed // This should trigger the support generation // wxGetApp().plater()->reslice(); @@ -2159,12 +2171,34 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas m_imgui->text(_(L("Right mouse click - remove point"))); m_imgui->text(" "); - bool remove_all_clicked = m_imgui->button(_(L("Remove all points"))); + bool generate = m_imgui->button(_(L("Generate points automatically"))); + bool remove_all_clicked = m_imgui->button(_(L("Remove all points")) + (m_model_object == nullptr ? "" : " (" + std::to_string(m_model_object->sla_support_points.size())+")")); m_imgui->end(); if (remove_all_clicked) delete_current_grabber(true); + + if (generate) { + const DynamicPrintConfig& cfg = *wxGetApp().get_tab(Preset::TYPE_SLA_PRINT)->get_config(); + SLAAutoSupports::Config config; + config.density_at_horizontal = cfg.opt_int("support_density_at_horizontal") / 10000.f; + config.density_at_45 = cfg.opt_int("support_density_at_45") / 10000.f; + config.minimal_z = cfg.opt_float("support_minimal_z"); + + SLAAutoSupports sas(*m_model_object, config); + sas.generate(); + m_grabbers.clear(); + for (const Vec3f& point : m_model_object->sla_support_points) { + m_grabbers.push_back(Grabber()); + m_grabbers.back().center = point.cast(); + } + } + + if (remove_all_clicked || generate) { + m_parent.reload_scene(true); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } } #endif // ENABLE_IMGUI @@ -2180,6 +2214,7 @@ bool GLGizmoSlaSupports::on_is_selectable() const } std::string GLGizmoSlaSupports::on_get_name() const + { return L("SLA Support Points"); } diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 4229d0362..bfec0965e 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -5,8 +5,10 @@ #include "../../slic3r/GUI/GLTexture.hpp" #include "../../slic3r/GUI/GLCanvas3D.hpp" + #include "libslic3r/Point.hpp" #include "libslic3r/BoundingBox.hpp" +#include "libslic3r/SLA/SLAAutoSupports.hpp" #include #include @@ -434,6 +436,7 @@ protected: class GLGizmoSlaSupports : public GLGizmoBase { private: + SLAAutoSupports* m_sas = nullptr; ModelObject* m_model_object = nullptr; #if ENABLE_SLA_SUPPORT_GIZMO_MOD ModelObject* m_old_model_object = nullptr; diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index bab3293a4..3100f25a1 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -403,6 +403,9 @@ const std::vector& Preset::sla_print_options() "support_critical_angle", "support_max_bridge_length", "support_object_elevation", + "support_density_at_horizontal", + "support_density_at_45", + "support_minimal_z", "pad_enable", "pad_wall_thickness", "pad_wall_height", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6d6b3b614..9a5c960bb 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3112,6 +3112,11 @@ void TabSLAPrint::build() optgroup->append_single_option_line("support_critical_angle"); optgroup->append_single_option_line("support_max_bridge_length"); + optgroup = page->new_optgroup(_(L("Automatic generation"))); + optgroup->append_single_option_line("support_density_at_horizontal"); + optgroup->append_single_option_line("support_density_at_45"); + optgroup->append_single_option_line("support_minimal_z"); + page = add_options_page(_(L("Pad")), "brick.png"); optgroup = page->new_optgroup(_(L("Pad"))); optgroup->append_single_option_line("pad_enable"); From 27ad859d4d49fe4042ef2e366bb93d4a43f9432d Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 14 Dec 2018 16:15:59 +0100 Subject: [PATCH 002/132] SLA supports - first attempt at supporting islands --- src/libslic3r/SLA/SLAAutoSupports.cpp | 91 ++++++++++++++++++++++++++- src/libslic3r/SLA/SLAAutoSupports.hpp | 4 +- src/libslic3r/SLAPrint.cpp | 83 +++++++++++++++--------- src/libslic3r/SLAPrint.hpp | 3 +- src/slic3r/GUI/GLGizmo.cpp | 4 +- src/slic3r/GUI/GLGizmo.hpp | 1 - 6 files changed, 152 insertions(+), 34 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index ca04b1bee..dd737c988 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -3,12 +3,15 @@ #include "SLAAutoSupports.hpp" #include "Model.hpp" +#include "ExPolygon.hpp" +#include "SVG.hpp" +#include "Point.hpp" #include namespace Slic3r { - +namespace SLAAutoSupports { SLAAutoSupports::SLAAutoSupports(ModelObject& mo, const SLAAutoSupports::Config& c) : m_model_object(mo), mesh(), m_config(c) {} @@ -152,4 +155,90 @@ float SLAAutoSupports::get_required_density(float angle) const + + + + +void output_expolygons(const ExPolygons& expolys, std::string filename) +{ + BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000)); + Slic3r::SVG svg_cummulative(filename, bb); + for (size_t i = 0; i < expolys.size(); ++ i) { + /*Slic3r::SVG svg("single"+std::to_string(i)+".svg", bb); + svg.draw(expolys[i]); + svg.draw_outline(expolys[i].contour, "black", scale_(0.05)); + svg.draw_outline(expolys[i].holes, "blue", scale_(0.05)); + svg.Close();*/ + + svg_cummulative.draw(expolys[i]); + svg_cummulative.draw_outline(expolys[i].contour, "black", scale_(0.05)); + svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); + } +} + +std::vector find_islands(const std::vector& slices, const std::vector& heights) +{ + std::vector support_points_out; + + struct PointAccessor { + const Point* operator()(const Point &pt) const { return &pt; } + }; + typedef ClosestPointInRadiusLookup ClosestPointLookupType; + + for (unsigned int i = 0; i find_islands(const std::vector& slices, const std::vector& heights); +} // namespace SLAAutoSupports } // namespace Slic3r diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 0c61b591b..062c759de 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -1,6 +1,7 @@ #include "SLAPrint.hpp" #include "SLA/SLASupportTree.hpp" #include "SLA/SLABasePool.hpp" +#include "SLA/SLAAutoSupports.hpp" #include "MTUtils.hpp" #include @@ -36,8 +37,7 @@ namespace { const std::array OBJ_STEP_LEVELS = { 10, // slaposObjectSlice, - 10, // slaposSupportIslands, - 20, // slaposSupportPoints, + 30, // slaposSupportPoints, 25, // slaposSupportTree, 25, // slaposBasePool, 5, // slaposSliceSupports, @@ -47,8 +47,7 @@ const std::array OBJ_STEP_LEVELS = const std::array OBJ_STEP_LABELS = { L("Slicing model"), // slaposObjectSlice, - L("Generating islands"), // slaposSupportIslands, - L("Scanning model structure"), // slaposSupportPoints, + L("Generating support points"), // slaposSupportPoints, L("Generating support tree"), // slaposSupportTree, L("Generating base pool"), // slaposBasePool, L("Slicing supports"), // slaposSliceSupports, @@ -420,6 +419,24 @@ void swapXY(ExPolygon& expoly) { } +std::vector SLAPrint::calculate_heights(const BoundingBoxf3& bb3d, float elevation, float initial_layer_height, float layer_height) const +{ + std::vector heights; + float minZ = float(bb3d.min(Z)) - float(elevation); + float maxZ = float(bb3d.max(Z)) ; + auto flh = float(layer_height); + auto gnd = float(bb3d.min(Z)); + + // The first layer (the one before the initial height) is added only + // if there is no pad and no elevation value + if(minZ >= gnd) heights.emplace_back(minZ); + + for(float h = minZ + initial_layer_height; h < maxZ; h += flh) + if(h >= gnd) heights.emplace_back(h); + return heights; +} + + void SLAPrint::process() { using namespace sla; @@ -455,24 +472,9 @@ void SLAPrint::process() TriangleMesh mesh = po.transformed_mesh(); TriangleMeshSlicer slicer(&mesh); - auto bb3d = mesh.bounding_box(); - - double elevation = po.get_elevation(); - - float minZ = float(bb3d.min(Z)) - float(elevation); - float maxZ = float(bb3d.max(Z)) ; - auto flh = float(lh); - auto gnd = float(bb3d.min(Z)); // The 1D grid heights - std::vector heights; - - // The first layer (the one before the initial height) is added only - // if there is no pad and no elevation value - if(minZ >= gnd) heights.emplace_back(minZ); - - for(float h = minZ + ilh; h < maxZ; h += flh) - if(h >= gnd) heights.emplace_back(h); + std::vector heights = calculate_heights(mesh.bounding_box(), po.get_elevation(), ilh, lh); auto& layers = po.m_model_slices; layers.clear(); slicer.slice(heights, &layers, [this](){ throw_if_canceled(); }); @@ -480,8 +482,8 @@ void SLAPrint::process() // this procedure simply converts the points and copies them into // the support data cache - auto support_points = [](SLAPrintObject& po) { - ModelObject& mo = *po.m_model_object; + /*auto support_points = [](SLAPrintObject& po) { + const ModelObject& mo = *po.m_model_object; po.m_supportdata.reset(new SLAPrintObject::SupportData()); if(!mo.sla_support_points.empty()) { @@ -497,6 +499,36 @@ void SLAPrint::process() " Hint: create some support points or disable support " "creation.")); } + };*/ + + // In this step we check the slices, identify island and cover them with + // support points. Then we sprinkle the rest of the mesh. + auto support_points = [this, ilh](SLAPrintObject& po) { + // find islands to support + double lh = po.m_config.layer_height.getFloat(); + std::vector heights = calculate_heights(po.transformed_mesh().bounding_box(), po.get_elevation(), ilh, lh); + //SLAAutoSupports auto_supports(po.get_model_slices(), heights, *po.m_model_object); + std::vector points = SLAAutoSupports::find_islands(po.get_model_slices(), heights); + + // TODO: + + // create mesh in igl format + + // cover the islands with points, use igl to get precise z coordinate + + // sprinkle the mesh with points (SLAAutoSupports::generate()) + + + /*for (const auto& p: points) + std::cout << p(0) << " " << p(1) << " " << p(2) << std::endl; + std::cout << std::endl; + */ + //for (auto& p: points) + // p = po.trafo().inverse() * p; + + po.m_supportdata.reset(new SLAPrintObject::SupportData()); + po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); + po.m_supportdata->support_points = sla::to_point_set(points); }; // In this step we create the supports @@ -803,8 +835,7 @@ void SLAPrint::process() // This is the actual order of steps done on each PrintObject std::array objectsteps = { - slaposObjectSlice, // Support Islands will need this step - slaposSupportIslands, + slaposObjectSlice, // SupportPoints will need this step slaposSupportPoints, slaposSupportTree, slaposBasePool, @@ -815,7 +846,6 @@ void SLAPrint::process() std::array pobj_program = { slice_model, - [](SLAPrintObject&){}, // slaposSupportIslands now empty support_points, support_tree, base_pool, @@ -1014,9 +1044,6 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step) // propagate to dependent steps if (step == slaposObjectSlice) { invalidated |= this->invalidate_all_steps(); - } else if (step == slaposSupportIslands) { - invalidated |= this->invalidate_steps({ slaposSupportPoints, slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); - invalidated |= m_print->invalidate_step(slapsRasterize); } else if (step == slaposSupportPoints) { invalidated |= this->invalidate_steps({ slaposSupportTree, slaposBasePool, slaposSliceSupports, slaposIndexSlices }); invalidated |= m_print->invalidate_step(slapsRasterize); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 4a0876767..3c05b4932 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -18,7 +18,6 @@ enum SLAPrintStep : unsigned int { enum SLAPrintObjectStep : unsigned int { slaposObjectSlice, - slaposSupportIslands, slaposSupportPoints, slaposSupportTree, slaposBasePool, @@ -226,6 +225,8 @@ private: lref(std::cref(lyr)), copies(std::cref(cp)) {} }; + std::vector calculate_heights(const BoundingBoxf3& bb, float elevation, float initial_layer_height, float layer_height) const; + // One level may contain multiple slices from multiple objects and their // supports using LayerRefs = std::vector; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 7f62d0901..2ed7fa181 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -2189,12 +2189,12 @@ void GLGizmoSlaSupports::on_render_input_window(float x, float y, const GLCanvas if (generate) { const DynamicPrintConfig& cfg = *wxGetApp().get_tab(Preset::TYPE_SLA_PRINT)->get_config(); - SLAAutoSupports::Config config; + SLAAutoSupports::SLAAutoSupports::Config config; config.density_at_horizontal = cfg.opt_int("support_density_at_horizontal") / 10000.f; config.density_at_45 = cfg.opt_int("support_density_at_45") / 10000.f; config.minimal_z = cfg.opt_float("support_minimal_z"); - SLAAutoSupports sas(*m_model_object, config); + SLAAutoSupports::SLAAutoSupports sas(*m_model_object, config); sas.generate(); m_grabbers.clear(); for (const Vec3f& point : m_model_object->sla_support_points) { diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index bfec0965e..2588080b2 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -436,7 +436,6 @@ protected: class GLGizmoSlaSupports : public GLGizmoBase { private: - SLAAutoSupports* m_sas = nullptr; ModelObject* m_model_object = nullptr; #if ENABLE_SLA_SUPPORT_GIZMO_MOD ModelObject* m_old_model_object = nullptr; From 985cd17265be09ca40144d275ec070633c3e8f33 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 14:09:35 +0100 Subject: [PATCH 003/132] Axes rendering --- src/slic3r/GUI/GLCanvas3D.cpp | 96 ++++++++++++++++++++++------------- src/slic3r/GUI/GLCanvas3D.hpp | 19 +++++-- 2 files changed, 74 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6a8043cf6..eecefd381 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -617,42 +617,71 @@ bool GLCanvas3D::Bed::_are_equal(const Pointfs& bed_1, const Pointfs& bed_2) return true; } +const double GLCanvas3D::Axes::Radius = 0.5; +const double GLCanvas3D::Axes::ArrowBaseRadius = 2.5 * GLCanvas3D::Axes::Radius; +const double GLCanvas3D::Axes::ArrowLength = 5.0; + GLCanvas3D::Axes::Axes() : origin(Vec3d::Zero()) - , length(0.0f) + , length(Vec3d::Zero()) { + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); } -void GLCanvas3D::Axes::render(bool depth_test) const +GLCanvas3D::Axes::~Axes() { - if (depth_test) - ::glEnable(GL_DEPTH_TEST); - else - ::glDisable(GL_DEPTH_TEST); + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} - ::glLineWidth(2.0f); - ::glBegin(GL_LINES); - // draw line for x axis +void GLCanvas3D::Axes::render() const +{ + if (m_quadric == nullptr) + return; + + ::glEnable(GL_DEPTH_TEST); + ::glEnable(GL_LIGHTING); + + // x axis ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0) + length, (GLfloat)origin(1), (GLfloat)origin(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1) + length, (GLfloat)origin(2)); - ::glEnd(); - // draw line for Z axis - // (re-enable depth test so that axis is correctly shown when objects are behind it) - if (!depth_test) - ::glEnable(GL_DEPTH_TEST); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + ::glRotated(90.0, 0.0, 1.0, 0.0); + render_axis(length(0)); + ::glPopMatrix(); - ::glBegin(GL_LINES); + // y axis + ::glColor3f(0.0f, 1.0f, 0.0f); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + ::glRotated(-90.0, 1.0, 0.0, 0.0); + render_axis(length(1)); + ::glPopMatrix(); + + // z axis ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3dv(origin.data()); - ::glVertex3f((GLfloat)origin(0), (GLfloat)origin(1), (GLfloat)origin(2) + length); - ::glEnd(); + ::glPushMatrix(); + ::glTranslated(origin(0), origin(1), origin(2)); + render_axis(length(2)); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); } +void GLCanvas3D::Axes::render_axis(double length) const +{ + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, Radius, Radius, length, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, Radius, 32, 1); + ::glTranslated(0.0, 0.0, length); + ::gluQuadricOrientation(m_quadric, GLU_OUTSIDE); + ::gluCylinder(m_quadric, ArrowBaseRadius, 0.0, ArrowLength, 32, 1); + ::gluQuadricOrientation(m_quadric, GLU_INSIDE); + ::gluDisk(m_quadric, 0.0, ArrowBaseRadius, 32, 1); +} GLCanvas3D::Shader::Shader() : m_shader(nullptr) @@ -3781,7 +3810,7 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) // Set the origin and size for painting of the coordinate system axes. m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z); - set_axes_length(0.3f * (float)m_bed.get_bounding_box().max_size()); + set_bed_axes_length(0.1 * m_bed.get_bounding_box().max_size()); if (new_shape) zoom_to_bed(); @@ -3789,9 +3818,9 @@ void GLCanvas3D::set_bed_shape(const Pointfs& shape) m_dirty = true; } -void GLCanvas3D::set_axes_length(float length) +void GLCanvas3D::set_bed_axes_length(double length) { - m_axes.length = length; + m_axes.length = length * Vec3d::Ones(); } void GLCanvas3D::set_color_by(const std::string& value) @@ -4051,21 +4080,16 @@ void GLCanvas3D::render() _render_background(); if (is_custom_bed) // untextured bed needs to be rendered before objects - { _render_bed(theta); - // disable depth testing so that axes are not covered by ground - _render_axes(false); - } _render_objects(); _render_sla_slices(); _render_selection(); + _render_axes(); + if (!is_custom_bed) // textured bed needs to be rendered after objects - { - _render_axes(true); _render_bed(theta); - } // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods @@ -6019,9 +6043,9 @@ void GLCanvas3D::_render_bed(float theta) const m_bed.render(theta); } -void GLCanvas3D::_render_axes(bool depth_test) const +void GLCanvas3D::_render_axes() const { - m_axes.render(depth_test); + m_axes.render(); } void GLCanvas3D::_render_objects() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index fd732ddf1..e07c60ed2 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -20,6 +20,8 @@ class wxTimerEvent; class wxPaintEvent; class wxGLCanvas; +class GLUquadric; +typedef class GLUquadric GLUquadricObj; namespace Slic3r { @@ -231,12 +233,20 @@ class GLCanvas3D struct Axes { + static const double Radius; + static const double ArrowBaseRadius; + static const double ArrowLength; Vec3d origin; - float length; + Vec3d length; + GLUquadricObj* m_quadric; Axes(); + ~Axes(); - void render(bool depth_test) const; + void render() const; + + private: + void render_axis(double length) const; }; class Shader @@ -870,8 +880,7 @@ public: // fills the m_bed.m_grid_lines and sets m_bed.m_origin. // Sets m_bed.m_polygon to limit the object placement. void set_bed_shape(const Pointfs& shape); - - void set_axes_length(float length); + void set_bed_axes_length(double length); void set_clipping_plane(unsigned int id, const ClippingPlane& plane) { @@ -1011,7 +1020,7 @@ private: void _picking_pass() const; void _render_background() const; void _render_bed(float theta) const; - void _render_axes(bool depth_test) const; + void _render_axes() const; void _render_objects() const; void _render_selection() const; void _render_warning_texture() const; From cdc654540b245ed8938cb0b7f03e7eed486925a0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 14:40:54 +0100 Subject: [PATCH 004/132] Added accelerator table on Windows to let numpad work with CTRL key as required by window menu shortcuts --- src/slic3r/GUI/MainFrame.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index a74d5f72a..aa5634fec 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -326,7 +326,7 @@ void MainFrame::init_menubar() size_t tab_offset = 0; if (m_plater) { #if ENABLE_REMOVE_TABS_FROM_PLATER - append_menu_item(windowMenu, wxID_ANY, L("Plater Tab\tCtrl+1"), L("Show the plater"), + append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"), [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); #else append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"), @@ -338,22 +338,35 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); } #if ENABLE_REMOVE_TABS_FROM_PLATER - append_menu_item(windowMenu, wxID_ANY, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); - append_menu_item(windowMenu, wxID_ANY, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); - append_menu_item(windowMenu, wxID_ANY, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), + append_menu_item(windowMenu, wxID_HIGHEST + 4, L("Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); if (m_plater) { windowMenu->AppendSeparator(); - wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_ANY, L("3D\tCtrl+5"), L("Show the 3D editing view"), - [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, ""); - wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_ANY, L("Preview\tCtrl+6"), L("Show the 3D slices preview"), - [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, ""); + wxMenuItem* item_3d = append_menu_item(windowMenu, wxID_HIGHEST + 5, L("3D\tCtrl+5"), L("Show the 3D editing view"), + [this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, ""); + wxMenuItem* item_preview = append_menu_item(windowMenu, wxID_HIGHEST + 6, L("Preview\tCtrl+6"), L("Show the 3D slices preview"), + [this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, ""); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_3d->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_preview->GetId()); } + +#if _WIN32 + // This is needed on Windows to fake the CTRL+# of the window menu when using the numpad + wxAcceleratorEntry entries[6]; + entries[0].Set(wxACCEL_CTRL, WXK_NUMPAD1, wxID_HIGHEST + 1); + entries[1].Set(wxACCEL_CTRL, WXK_NUMPAD2, wxID_HIGHEST + 2); + entries[2].Set(wxACCEL_CTRL, WXK_NUMPAD3, wxID_HIGHEST + 3); + entries[3].Set(wxACCEL_CTRL, WXK_NUMPAD4, wxID_HIGHEST + 4); + entries[4].Set(wxACCEL_CTRL, WXK_NUMPAD5, wxID_HIGHEST + 5); + entries[5].Set(wxACCEL_CTRL, WXK_NUMPAD6, wxID_HIGHEST + 6); + wxAcceleratorTable accel(6, entries); + SetAcceleratorTable(accel); +#endif // _WIN32 #else append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); From 11cf10774b69e223e350b734481eb76dddd9377c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 15:28:17 +0100 Subject: [PATCH 005/132] Fixed import from PrusaControl files --- src/libslic3r/Format/PRUS.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 6c0b055d6..80aae75cf 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -96,7 +96,6 @@ static void extract_model_from_archive( const char *model_xml = strstr(scene_xml_data.data(), model_name_tag); const char *zero_tag = ""; const char *zero_xml = strstr(scene_xml_data.data(), zero_tag); - float trafo[3][4] = { 0 }; Vec3d instance_rotation = Vec3d::Zero(); Vec3d instance_scaling_factor = Vec3d::Ones(); Vec3d instance_offset = Vec3d::Zero(); @@ -124,19 +123,7 @@ static void extract_model_from_archive( "[%f, %f, %f]", zero, zero+1, zero+2) == 3) { instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]); instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]); - Eigen::Matrix3f mat_rot, mat_scale, mat_trafo; - mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) * - Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) * - Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX()); - mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]); - mat_trafo = mat_rot * mat_scale; - for (size_t r = 0; r < 3; ++ r) { - for (size_t c = 0; c < 3; ++ c) - trafo[r][c] += mat_trafo(r, c); - } instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2])); - // CHECK_ME -> Is the following correct ? - trafo[2][3] = position[2] / (float)instance_scaling_factor(2); trafo_set = true; } const char *group_tag = ""; @@ -189,8 +176,6 @@ static void extract_model_from_archive( // All the faces have been read. stl_get_size(&stl); mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); if (std::abs(stl.stats.min(2)) < EPSILON) stl.stats.min(2) = 0.; // Add a mesh to a model. @@ -274,8 +259,6 @@ static void extract_model_from_archive( memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50); stl_get_size(&stl); mesh.repair(); - // Transform the model. - stl_transform(&stl, &trafo[0][0]); // Add a mesh to a model. if (mesh.facets_count() > 0) mesh_valid = true; From f7a6ee9e29766773d2c85416c4f1f2925df84761 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 17 Dec 2018 15:45:20 +0100 Subject: [PATCH 006/132] Fixed volume shown in info panel for scaled objects --- src/slic3r/GUI/Plater.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8db804c12..aeda2e8c2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -732,8 +732,7 @@ void Sidebar::show_info_sizer() p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast(model_object->materials_count()))); auto& stats = model_object->volumes.front()->mesh.stl.stats; - auto sf = model_instance->get_scaling_factor(); - p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2) * sf(0) * sf(1) * sf(2))); + p->object_info->info_volume->SetLabel(wxString::Format("%.2f", size(0) * size(1) * size(2))); p->object_info->info_facets->SetLabel(wxString::Format(_(L("%d (%d shells)")), static_cast(model_object->facets_count()), stats.number_of_parts)); int errors = stats.degenerate_facets + stats.edges_fixed + stats.facets_removed + From b613334b818011f332e6bc7a5b2f9f472c34f893 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 17 Dec 2018 15:58:15 +0100 Subject: [PATCH 007/132] Considering multiple neighboring triangles for support point normals --- src/eigen/unsupported/Eigen/SparseExtra | 53 + .../SparseExtra/BlockOfDynamicSparseMatrix.h | 122 ++ .../Eigen/src/SparseExtra/BlockSparseMatrix.h | 1079 +++++++++++++++++ .../src/SparseExtra/DynamicSparseMatrix.h | 392 ++++++ .../Eigen/src/SparseExtra/MarketIO.h | 275 +++++ .../src/SparseExtra/MatrixMarketIterator.h | 247 ++++ .../Eigen/src/SparseExtra/RandomSetter.h | 327 +++++ src/libslic3r/SLA/SLASupportTree.cpp | 6 +- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 127 +- 9 files changed, 2605 insertions(+), 23 deletions(-) create mode 100644 src/eigen/unsupported/Eigen/SparseExtra create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h create mode 100644 src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h diff --git a/src/eigen/unsupported/Eigen/SparseExtra b/src/eigen/unsupported/Eigen/SparseExtra new file mode 100644 index 000000000..819cffa27 --- /dev/null +++ b/src/eigen/unsupported/Eigen/SparseExtra @@ -0,0 +1,53 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_EXTRA_MODULE_H +#define EIGEN_SPARSE_EXTRA_MODULE_H + +#include "../../Eigen/Sparse" + +#include "../../Eigen/src/Core/util/DisableStupidWarnings.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifdef EIGEN_GOOGLEHASH_SUPPORT + #include +#endif + +/** + * \defgroup SparseExtra_Module SparseExtra module + * + * This module contains some experimental features extending the sparse module. + * + * \code + * #include + * \endcode + */ + + +#include "src/SparseExtra/DynamicSparseMatrix.h" +#include "src/SparseExtra/BlockOfDynamicSparseMatrix.h" +#include "src/SparseExtra/RandomSetter.h" + +#include "src/SparseExtra/MarketIO.h" + +#if !defined(_WIN32) +#include +#include "src/SparseExtra/MatrixMarketIterator.h" +#endif + +#include "../../Eigen/src/Core/util/ReenableStupidWarnings.h" + +#endif // EIGEN_SPARSE_EXTRA_MODULE_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h new file mode 100644 index 000000000..e9ec746e3 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockOfDynamicSparseMatrix.h @@ -0,0 +1,122 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H +#define EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H + +namespace Eigen { + +#if 0 + +// NOTE Have to be reimplemented as a specialization of BlockImpl< DynamicSparseMatrix<_Scalar, _Options, _Index>, ... > +// See SparseBlock.h for an example + + +/*************************************************************************** +* specialisation for DynamicSparseMatrix +***************************************************************************/ + +template +class SparseInnerVectorSet, Size> + : public SparseMatrixBase, Size> > +{ + typedef DynamicSparseMatrix<_Scalar, _Options, _Index> MatrixType; + public: + + enum { IsRowMajor = internal::traits::IsRowMajor }; + + EIGEN_SPARSE_PUBLIC_INTERFACE(SparseInnerVectorSet) + class InnerIterator: public MatrixType::InnerIterator + { + public: + inline InnerIterator(const SparseInnerVectorSet& xpr, Index outer) + : MatrixType::InnerIterator(xpr.m_matrix, xpr.m_outerStart + outer), m_outer(outer) + {} + inline Index row() const { return IsRowMajor ? m_outer : this->index(); } + inline Index col() const { return IsRowMajor ? this->index() : m_outer; } + protected: + Index m_outer; + }; + + inline SparseInnerVectorSet(const MatrixType& matrix, Index outerStart, Index outerSize) + : m_matrix(matrix), m_outerStart(outerStart), m_outerSize(outerSize) + { + eigen_assert( (outerStart>=0) && ((outerStart+outerSize)<=matrix.outerSize()) ); + } + + inline SparseInnerVectorSet(const MatrixType& matrix, Index outer) + : m_matrix(matrix), m_outerStart(outer), m_outerSize(Size) + { + eigen_assert(Size!=Dynamic); + eigen_assert( (outer>=0) && (outer + inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) + { + if (IsRowMajor != ((OtherDerived::Flags&RowMajorBit)==RowMajorBit)) + { + // need to transpose => perform a block evaluation followed by a big swap + DynamicSparseMatrix aux(other); + *this = aux.markAsRValue(); + } + else + { + // evaluate/copy vector per vector + for (Index j=0; j aux(other.innerVector(j)); + m_matrix.const_cast_derived()._data()[m_outerStart+j].swap(aux._data()); + } + } + return *this; + } + + inline SparseInnerVectorSet& operator=(const SparseInnerVectorSet& other) + { + return operator=(other); + } + + Index nonZeros() const + { + Index count = 0; + for (Index j=0; j0); + return m_matrix.data()[m_outerStart].vale(m_matrix.data()[m_outerStart].size()-1); + } + +// template +// inline SparseInnerVectorSet& operator=(const SparseMatrixBase& other) +// { +// return *this; +// } + + EIGEN_STRONG_INLINE Index rows() const { return IsRowMajor ? m_outerSize.value() : m_matrix.rows(); } + EIGEN_STRONG_INLINE Index cols() const { return IsRowMajor ? m_matrix.cols() : m_outerSize.value(); } + + protected: + + const typename MatrixType::Nested m_matrix; + Index m_outerStart; + const internal::variable_if_dynamic m_outerSize; + +}; + +#endif + +} // end namespace Eigen + +#endif // EIGEN_SPARSE_BLOCKFORDYNAMICMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h new file mode 100644 index 000000000..0e8350a7d --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/BlockSparseMatrix.h @@ -0,0 +1,1079 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2013 Desire Nuentsa +// Copyright (C) 2013 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSEBLOCKMATRIX_H +#define EIGEN_SPARSEBLOCKMATRIX_H + +namespace Eigen { +/** \ingroup SparseCore_Module + * + * \class BlockSparseMatrix + * + * \brief A versatile sparse matrix representation where each element is a block + * + * This class provides routines to manipulate block sparse matrices stored in a + * BSR-like representation. There are two main types : + * + * 1. All blocks have the same number of rows and columns, called block size + * in the following. In this case, if this block size is known at compile time, + * it can be given as a template parameter like + * \code + * BlockSparseMatrix bmat(b_rows, b_cols); + * \endcode + * Here, bmat is a b_rows x b_cols block sparse matrix + * where each coefficient is a 3x3 dense matrix. + * If the block size is fixed but will be given at runtime, + * \code + * BlockSparseMatrix bmat(b_rows, b_cols); + * bmat.setBlockSize(block_size); + * \endcode + * + * 2. The second case is for variable-block sparse matrices. + * Here each block has its own dimensions. The only restriction is that all the blocks + * in a row (resp. a column) should have the same number of rows (resp. of columns). + * It is thus required in this case to describe the layout of the matrix by calling + * setBlockLayout(rowBlocks, colBlocks). + * + * In any of the previous case, the matrix can be filled by calling setFromTriplets(). + * A regular sparse matrix can be converted to a block sparse matrix and vice versa. + * It is obviously required to describe the block layout beforehand by calling either + * setBlockSize() for fixed-size blocks or setBlockLayout for variable-size blocks. + * + * \tparam _Scalar The Scalar type + * \tparam _BlockAtCompileTime The block layout option. It takes the following values + * Dynamic : block size known at runtime + * a numeric number : fixed-size block known at compile time + */ +template class BlockSparseMatrix; + +template class BlockSparseMatrixView; + +namespace internal { +template +struct traits > +{ + typedef _Scalar Scalar; + typedef _Index Index; + typedef Sparse StorageKind; // FIXME Where is it used ?? + typedef MatrixXpr XprKind; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + BlockSize = _BlockAtCompileTime, + Flags = _Options | NestByRefBit | LvalueBit, + CoeffReadCost = NumTraits::ReadCost, + SupportedAccessPatterns = InnerRandomAccessPattern + }; +}; +template +struct traits > +{ + typedef Ref > Scalar; + typedef Ref > RealScalar; + +}; + +// Function object to sort a triplet list +template +struct TripletComp +{ + typedef typename Iterator::value_type Triplet; + bool operator()(const Triplet& a, const Triplet& b) + { if(IsColMajor) + return ((a.col() == b.col() && a.row() < b.row()) || (a.col() < b.col())); + else + return ((a.row() == b.row() && a.col() < b.col()) || (a.row() < b.row())); + } +}; +} // end namespace internal + + +/* Proxy to view the block sparse matrix as a regular sparse matrix */ +template +class BlockSparseMatrixView : public SparseMatrixBase +{ + public: + typedef Ref Scalar; + typedef Ref RealScalar; + typedef typename BlockSparseMatrixT::Index Index; + typedef BlockSparseMatrixT Nested; + enum { + Flags = BlockSparseMatrixT::Options, + Options = BlockSparseMatrixT::Options, + RowsAtCompileTime = BlockSparseMatrixT::RowsAtCompileTime, + ColsAtCompileTime = BlockSparseMatrixT::ColsAtCompileTime, + MaxColsAtCompileTime = BlockSparseMatrixT::MaxColsAtCompileTime, + MaxRowsAtCompileTime = BlockSparseMatrixT::MaxRowsAtCompileTime + }; + public: + BlockSparseMatrixView(const BlockSparseMatrixT& spblockmat) + : m_spblockmat(spblockmat) + {} + + Index outerSize() const + { + return (Flags&RowMajorBit) == 1 ? this->rows() : this->cols(); + } + Index cols() const + { + return m_spblockmat.blockCols(); + } + Index rows() const + { + return m_spblockmat.blockRows(); + } + Scalar coeff(Index row, Index col) + { + return m_spblockmat.coeff(row, col); + } + Scalar coeffRef(Index row, Index col) + { + return m_spblockmat.coeffRef(row, col); + } + // Wrapper to iterate over all blocks + class InnerIterator : public BlockSparseMatrixT::BlockInnerIterator + { + public: + InnerIterator(const BlockSparseMatrixView& mat, Index outer) + : BlockSparseMatrixT::BlockInnerIterator(mat.m_spblockmat, outer) + {} + + }; + + protected: + const BlockSparseMatrixT& m_spblockmat; +}; + +// Proxy to view a regular vector as a block vector +template +class BlockVectorView +{ + public: + enum { + BlockSize = BlockSparseMatrixT::BlockSize, + ColsAtCompileTime = VectorType::ColsAtCompileTime, + RowsAtCompileTime = VectorType::RowsAtCompileTime, + Flags = VectorType::Flags + }; + typedef Ref >Scalar; + typedef typename BlockSparseMatrixT::Index Index; + public: + BlockVectorView(const BlockSparseMatrixT& spblockmat, const VectorType& vec) + : m_spblockmat(spblockmat),m_vec(vec) + { } + inline Index cols() const + { + return m_vec.cols(); + } + inline Index size() const + { + return m_spblockmat.blockRows(); + } + inline Scalar coeff(Index bi) const + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.middleRows(startRow, rowSize); + } + inline Scalar coeff(Index bi, Index j) const + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.block(startRow, j, rowSize, 1); + } + protected: + const BlockSparseMatrixT& m_spblockmat; + const VectorType& m_vec; +}; + +template class BlockVectorReturn; + + +// Proxy to view a regular vector as a block vector +template +class BlockVectorReturn +{ + public: + enum { + ColsAtCompileTime = VectorType::ColsAtCompileTime, + RowsAtCompileTime = VectorType::RowsAtCompileTime, + Flags = VectorType::Flags + }; + typedef Ref > Scalar; + typedef typename BlockSparseMatrixT::Index Index; + public: + BlockVectorReturn(const BlockSparseMatrixT& spblockmat, VectorType& vec) + : m_spblockmat(spblockmat),m_vec(vec) + { } + inline Index size() const + { + return m_spblockmat.blockRows(); + } + inline Scalar coeffRef(Index bi) + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.middleRows(startRow, rowSize); + } + inline Scalar coeffRef(Index bi, Index j) + { + Index startRow = m_spblockmat.blockRowsIndex(bi); + Index rowSize = m_spblockmat.blockRowsIndex(bi+1) - startRow; + return m_vec.block(startRow, j, rowSize, 1); + } + + protected: + const BlockSparseMatrixT& m_spblockmat; + VectorType& m_vec; +}; + +// Block version of the sparse dense product +template +class BlockSparseTimeDenseProduct; + +namespace internal { + +template +struct traits > +{ + typedef Dense StorageKind; + typedef MatrixXpr XprKind; + typedef typename BlockSparseMatrixT::Scalar Scalar; + typedef typename BlockSparseMatrixT::Index Index; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + Flags = 0, + CoeffReadCost = internal::traits::CoeffReadCost + }; +}; +} // end namespace internal + +template +class BlockSparseTimeDenseProduct + : public ProductBase, Lhs, Rhs> +{ + public: + EIGEN_PRODUCT_PUBLIC_INTERFACE(BlockSparseTimeDenseProduct) + + BlockSparseTimeDenseProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) + {} + + template void scaleAndAddTo(Dest& dest, const typename Rhs::Scalar& alpha) const + { + BlockVectorReturn tmpDest(m_lhs, dest); + internal::sparse_time_dense_product( BlockSparseMatrixView(m_lhs), BlockVectorView(m_lhs, m_rhs), tmpDest, alpha); + } + + private: + BlockSparseTimeDenseProduct& operator=(const BlockSparseTimeDenseProduct&); +}; + +template +class BlockSparseMatrix : public SparseMatrixBase > +{ + public: + typedef _Scalar Scalar; + typedef typename NumTraits::Real RealScalar; + typedef _StorageIndex StorageIndex; + typedef typename internal::ref_selector >::type Nested; + + enum { + Options = _Options, + Flags = Options, + BlockSize=_BlockAtCompileTime, + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + IsVectorAtCompileTime = 0, + IsColMajor = Flags&RowMajorBit ? 0 : 1 + }; + typedef Matrix BlockScalar; + typedef Matrix BlockRealScalar; + typedef typename internal::conditional<_BlockAtCompileTime==Dynamic, Scalar, BlockScalar>::type BlockScalarReturnType; + typedef BlockSparseMatrix PlainObject; + public: + // Default constructor + BlockSparseMatrix() + : m_innerBSize(0),m_outerBSize(0),m_innerOffset(0),m_outerOffset(0), + m_nonzerosblocks(0),m_values(0),m_blockPtr(0),m_indices(0), + m_outerIndex(0),m_blockSize(BlockSize) + { } + + + /** + * \brief Construct and resize + * + */ + BlockSparseMatrix(Index brow, Index bcol) + : m_innerBSize(IsColMajor ? brow : bcol), + m_outerBSize(IsColMajor ? bcol : brow), + m_innerOffset(0),m_outerOffset(0),m_nonzerosblocks(0), + m_values(0),m_blockPtr(0),m_indices(0), + m_outerIndex(0),m_blockSize(BlockSize) + { } + + /** + * \brief Copy-constructor + */ + BlockSparseMatrix(const BlockSparseMatrix& other) + : m_innerBSize(other.m_innerBSize),m_outerBSize(other.m_outerBSize), + m_nonzerosblocks(other.m_nonzerosblocks),m_nonzeros(other.m_nonzeros), + m_blockPtr(0),m_blockSize(other.m_blockSize) + { + // should we allow copying between variable-size blocks and fixed-size blocks ?? + eigen_assert(m_blockSize == BlockSize && " CAN NOT COPY BETWEEN FIXED-SIZE AND VARIABLE-SIZE BLOCKS"); + + std::copy(other.m_innerOffset, other.m_innerOffset+m_innerBSize+1, m_innerOffset); + std::copy(other.m_outerOffset, other.m_outerOffset+m_outerBSize+1, m_outerOffset); + std::copy(other.m_values, other.m_values+m_nonzeros, m_values); + + if(m_blockSize != Dynamic) + std::copy(other.m_blockPtr, other.m_blockPtr+m_nonzerosblocks, m_blockPtr); + + std::copy(other.m_indices, other.m_indices+m_nonzerosblocks, m_indices); + std::copy(other.m_outerIndex, other.m_outerIndex+m_outerBSize, m_outerIndex); + } + + friend void swap(BlockSparseMatrix& first, BlockSparseMatrix& second) + { + std::swap(first.m_innerBSize, second.m_innerBSize); + std::swap(first.m_outerBSize, second.m_outerBSize); + std::swap(first.m_innerOffset, second.m_innerOffset); + std::swap(first.m_outerOffset, second.m_outerOffset); + std::swap(first.m_nonzerosblocks, second.m_nonzerosblocks); + std::swap(first.m_nonzeros, second.m_nonzeros); + std::swap(first.m_values, second.m_values); + std::swap(first.m_blockPtr, second.m_blockPtr); + std::swap(first.m_indices, second.m_indices); + std::swap(first.m_outerIndex, second.m_outerIndex); + std::swap(first.m_BlockSize, second.m_blockSize); + } + + BlockSparseMatrix& operator=(BlockSparseMatrix other) + { + //Copy-and-swap paradigm ... avoid leaked data if thrown + swap(*this, other); + return *this; + } + + // Destructor + ~BlockSparseMatrix() + { + delete[] m_outerIndex; + delete[] m_innerOffset; + delete[] m_outerOffset; + delete[] m_indices; + delete[] m_blockPtr; + delete[] m_values; + } + + + /** + * \brief Constructor from a sparse matrix + * + */ + template + inline BlockSparseMatrix(const MatrixType& spmat) : m_blockSize(BlockSize) + { + EIGEN_STATIC_ASSERT((m_blockSize != Dynamic), THIS_METHOD_IS_ONLY_FOR_FIXED_SIZE); + + *this = spmat; + } + + /** + * \brief Assignment from a sparse matrix with the same storage order + * + * Convert from a sparse matrix to block sparse matrix. + * \warning Before calling this function, tt is necessary to call + * either setBlockLayout() (matrices with variable-size blocks) + * or setBlockSize() (for fixed-size blocks). + */ + template + inline BlockSparseMatrix& operator=(const MatrixType& spmat) + { + eigen_assert((m_innerBSize != 0 && m_outerBSize != 0) + && "Trying to assign to a zero-size matrix, call resize() first"); + eigen_assert(((MatrixType::Options&RowMajorBit) != IsColMajor) && "Wrong storage order"); + typedef SparseMatrix MatrixPatternType; + MatrixPatternType blockPattern(blockRows(), blockCols()); + m_nonzeros = 0; + + // First, compute the number of nonzero blocks and their locations + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + // Browse each outer block and compute the structure + std::vector nzblocksFlag(m_innerBSize,false); // Record the existing blocks + blockPattern.startVec(bj); + for(StorageIndex j = blockOuterIndex(bj); j < blockOuterIndex(bj+1); ++j) + { + typename MatrixType::InnerIterator it_spmat(spmat, j); + for(; it_spmat; ++it_spmat) + { + StorageIndex bi = innerToBlock(it_spmat.index()); // Index of the current nonzero block + if(!nzblocksFlag[bi]) + { + // Save the index of this nonzero block + nzblocksFlag[bi] = true; + blockPattern.insertBackByOuterInnerUnordered(bj, bi) = true; + // Compute the total number of nonzeros (including explicit zeros in blocks) + m_nonzeros += blockOuterSize(bj) * blockInnerSize(bi); + } + } + } // end current outer block + } + blockPattern.finalize(); + + // Allocate the internal arrays + setBlockStructure(blockPattern); + + for(StorageIndex nz = 0; nz < m_nonzeros; ++nz) m_values[nz] = Scalar(0); + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + // Now copy the values + for(StorageIndex j = blockOuterIndex(bj); j < blockOuterIndex(bj+1); ++j) + { + // Browse the outer block column by column (for column-major matrices) + typename MatrixType::InnerIterator it_spmat(spmat, j); + for(; it_spmat; ++it_spmat) + { + StorageIndex idx = 0; // Position of this block in the column block + StorageIndex bi = innerToBlock(it_spmat.index()); // Index of the current nonzero block + // Go to the inner block where this element belongs to + while(bi > m_indices[m_outerIndex[bj]+idx]) ++idx; // Not expensive for ordered blocks + StorageIndex idxVal;// Get the right position in the array of values for this element + if(m_blockSize == Dynamic) + { + // Offset from all blocks before ... + idxVal = m_blockPtr[m_outerIndex[bj]+idx]; + // ... and offset inside the block + idxVal += (j - blockOuterIndex(bj)) * blockOuterSize(bj) + it_spmat.index() - m_innerOffset[bi]; + } + else + { + // All blocks before + idxVal = (m_outerIndex[bj] + idx) * m_blockSize * m_blockSize; + // inside the block + idxVal += (j - blockOuterIndex(bj)) * m_blockSize + (it_spmat.index()%m_blockSize); + } + // Insert the value + m_values[idxVal] = it_spmat.value(); + } // end of this column + } // end of this block + } // end of this outer block + + return *this; + } + + /** + * \brief Set the nonzero block pattern of the matrix + * + * Given a sparse matrix describing the nonzero block pattern, + * this function prepares the internal pointers for values. + * After calling this function, any *nonzero* block (bi, bj) can be set + * with a simple call to coeffRef(bi,bj). + * + * + * \warning Before calling this function, tt is necessary to call + * either setBlockLayout() (matrices with variable-size blocks) + * or setBlockSize() (for fixed-size blocks). + * + * \param blockPattern Sparse matrix of boolean elements describing the block structure + * + * \sa setBlockLayout() \sa setBlockSize() + */ + template + void setBlockStructure(const MatrixType& blockPattern) + { + resize(blockPattern.rows(), blockPattern.cols()); + reserve(blockPattern.nonZeros()); + + // Browse the block pattern and set up the various pointers + m_outerIndex[0] = 0; + if(m_blockSize == Dynamic) m_blockPtr[0] = 0; + for(StorageIndex nz = 0; nz < m_nonzeros; ++nz) m_values[nz] = Scalar(0); + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + //Browse each outer block + + //First, copy and save the indices of nonzero blocks + //FIXME : find a way to avoid this ... + std::vector nzBlockIdx; + typename MatrixType::InnerIterator it(blockPattern, bj); + for(; it; ++it) + { + nzBlockIdx.push_back(it.index()); + } + std::sort(nzBlockIdx.begin(), nzBlockIdx.end()); + + // Now, fill block indices and (eventually) pointers to blocks + for(StorageIndex idx = 0; idx < nzBlockIdx.size(); ++idx) + { + StorageIndex offset = m_outerIndex[bj]+idx; // offset in m_indices + m_indices[offset] = nzBlockIdx[idx]; + if(m_blockSize == Dynamic) + m_blockPtr[offset] = m_blockPtr[offset-1] + blockInnerSize(nzBlockIdx[idx]) * blockOuterSize(bj); + // There is no blockPtr for fixed-size blocks... not needed !??? + } + // Save the pointer to the next outer block + m_outerIndex[bj+1] = m_outerIndex[bj] + nzBlockIdx.size(); + } + } + + /** + * \brief Set the number of rows and columns blocks + */ + inline void resize(Index brow, Index bcol) + { + m_innerBSize = IsColMajor ? brow : bcol; + m_outerBSize = IsColMajor ? bcol : brow; + } + + /** + * \brief set the block size at runtime for fixed-size block layout + * + * Call this only for fixed-size blocks + */ + inline void setBlockSize(Index blockSize) + { + m_blockSize = blockSize; + } + + /** + * \brief Set the row and column block layouts, + * + * This function set the size of each row and column block. + * So this function should be used only for blocks with variable size. + * \param rowBlocks : Number of rows per row block + * \param colBlocks : Number of columns per column block + * \sa resize(), setBlockSize() + */ + inline void setBlockLayout(const VectorXi& rowBlocks, const VectorXi& colBlocks) + { + const VectorXi& innerBlocks = IsColMajor ? rowBlocks : colBlocks; + const VectorXi& outerBlocks = IsColMajor ? colBlocks : rowBlocks; + eigen_assert(m_innerBSize == innerBlocks.size() && "CHECK THE NUMBER OF ROW OR COLUMN BLOCKS"); + eigen_assert(m_outerBSize == outerBlocks.size() && "CHECK THE NUMBER OF ROW OR COLUMN BLOCKS"); + m_outerBSize = outerBlocks.size(); + // starting index of blocks... cumulative sums + m_innerOffset = new StorageIndex[m_innerBSize+1]; + m_outerOffset = new StorageIndex[m_outerBSize+1]; + m_innerOffset[0] = 0; + m_outerOffset[0] = 0; + std::partial_sum(&innerBlocks[0], &innerBlocks[m_innerBSize-1]+1, &m_innerOffset[1]); + std::partial_sum(&outerBlocks[0], &outerBlocks[m_outerBSize-1]+1, &m_outerOffset[1]); + + // Compute the total number of nonzeros + m_nonzeros = 0; + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + for(StorageIndex bi = 0; bi < m_innerBSize; ++bi) + m_nonzeros += outerBlocks[bj] * innerBlocks[bi]; + + } + + /** + * \brief Allocate the internal array of pointers to blocks and their inner indices + * + * \note For fixed-size blocks, call setBlockSize() to set the block. + * And For variable-size blocks, call setBlockLayout() before using this function + * + * \param nonzerosblocks Number of nonzero blocks. The total number of nonzeros is + * is computed in setBlockLayout() for variable-size blocks + * \sa setBlockSize() + */ + inline void reserve(const Index nonzerosblocks) + { + eigen_assert((m_innerBSize != 0 && m_outerBSize != 0) && + "TRYING TO RESERVE ZERO-SIZE MATRICES, CALL resize() first"); + + //FIXME Should free if already allocated + m_outerIndex = new StorageIndex[m_outerBSize+1]; + + m_nonzerosblocks = nonzerosblocks; + if(m_blockSize != Dynamic) + { + m_nonzeros = nonzerosblocks * (m_blockSize * m_blockSize); + m_blockPtr = 0; + } + else + { + // m_nonzeros is already computed in setBlockLayout() + m_blockPtr = new StorageIndex[m_nonzerosblocks+1]; + } + m_indices = new StorageIndex[m_nonzerosblocks+1]; + m_values = new Scalar[m_nonzeros]; + } + + + /** + * \brief Fill values in a matrix from a triplet list. + * + * Each triplet item has a block stored in an Eigen dense matrix. + * The InputIterator class should provide the functions row(), col() and value() + * + * \note For fixed-size blocks, call setBlockSize() before this function. + * + * FIXME Do not accept duplicates + */ + template + void setFromTriplets(const InputIterator& begin, const InputIterator& end) + { + eigen_assert((m_innerBSize!=0 && m_outerBSize !=0) && "ZERO BLOCKS, PLEASE CALL resize() before"); + + /* First, sort the triplet list + * FIXME This can be unnecessarily expensive since only the inner indices have to be sorted + * The best approach is like in SparseMatrix::setFromTriplets() + */ + internal::TripletComp tripletcomp; + std::sort(begin, end, tripletcomp); + + /* Count the number of rows and column blocks, + * and the number of nonzero blocks per outer dimension + */ + VectorXi rowBlocks(m_innerBSize); // Size of each block row + VectorXi colBlocks(m_outerBSize); // Size of each block column + rowBlocks.setZero(); colBlocks.setZero(); + VectorXi nzblock_outer(m_outerBSize); // Number of nz blocks per outer vector + VectorXi nz_outer(m_outerBSize); // Number of nz per outer vector...for variable-size blocks + nzblock_outer.setZero(); + nz_outer.setZero(); + for(InputIterator it(begin); it !=end; ++it) + { + eigen_assert(it->row() >= 0 && it->row() < this->blockRows() && it->col() >= 0 && it->col() < this->blockCols()); + eigen_assert((it->value().rows() == it->value().cols() && (it->value().rows() == m_blockSize)) + || (m_blockSize == Dynamic)); + + if(m_blockSize == Dynamic) + { + eigen_assert((rowBlocks[it->row()] == 0 || rowBlocks[it->row()] == it->value().rows()) && + "NON CORRESPONDING SIZES FOR ROW BLOCKS"); + eigen_assert((colBlocks[it->col()] == 0 || colBlocks[it->col()] == it->value().cols()) && + "NON CORRESPONDING SIZES FOR COLUMN BLOCKS"); + rowBlocks[it->row()] =it->value().rows(); + colBlocks[it->col()] = it->value().cols(); + } + nz_outer(IsColMajor ? it->col() : it->row()) += it->value().rows() * it->value().cols(); + nzblock_outer(IsColMajor ? it->col() : it->row())++; + } + // Allocate member arrays + if(m_blockSize == Dynamic) setBlockLayout(rowBlocks, colBlocks); + StorageIndex nzblocks = nzblock_outer.sum(); + reserve(nzblocks); + + // Temporary markers + VectorXi block_id(m_outerBSize); // To be used as a block marker during insertion + + // Setup outer index pointers and markers + m_outerIndex[0] = 0; + if (m_blockSize == Dynamic) m_blockPtr[0] = 0; + for(StorageIndex bj = 0; bj < m_outerBSize; ++bj) + { + m_outerIndex[bj+1] = m_outerIndex[bj] + nzblock_outer(bj); + block_id(bj) = m_outerIndex[bj]; + if(m_blockSize==Dynamic) + { + m_blockPtr[m_outerIndex[bj+1]] = m_blockPtr[m_outerIndex[bj]] + nz_outer(bj); + } + } + + // Fill the matrix + for(InputIterator it(begin); it!=end; ++it) + { + StorageIndex outer = IsColMajor ? it->col() : it->row(); + StorageIndex inner = IsColMajor ? it->row() : it->col(); + m_indices[block_id(outer)] = inner; + StorageIndex block_size = it->value().rows()*it->value().cols(); + StorageIndex nz_marker = blockPtr(block_id[outer]); + memcpy(&(m_values[nz_marker]), it->value().data(), block_size * sizeof(Scalar)); + if(m_blockSize == Dynamic) + { + m_blockPtr[block_id(outer)+1] = m_blockPtr[block_id(outer)] + block_size; + } + block_id(outer)++; + } + + // An alternative when the outer indices are sorted...no need to use an array of markers +// for(Index bcol = 0; bcol < m_outerBSize; ++bcol) +// { +// Index id = 0, id_nz = 0, id_nzblock = 0; +// for(InputIterator it(begin); it!=end; ++it) +// { +// while (idvalue().rows()*it->value().cols(); +// m_blockPtr[id_nzblock+1] = m_blockPtr[id_nzblock] + block_size; +// id_nzblock++; +// memcpy(&(m_values[id_nz]),it->value().data(), block_size*sizeof(Scalar)); +// id_nz += block_size; +// } +// while(id < m_outerBSize-1) // Empty columns at the end +// { +// id++; +// m_outerIndex[id+1]=m_outerIndex[id]; +// } +// } + } + + + /** + * \returns the number of rows + */ + inline Index rows() const + { +// return blockRows(); + return (IsColMajor ? innerSize() : outerSize()); + } + + /** + * \returns the number of cols + */ + inline Index cols() const + { +// return blockCols(); + return (IsColMajor ? outerSize() : innerSize()); + } + + inline Index innerSize() const + { + if(m_blockSize == Dynamic) return m_innerOffset[m_innerBSize]; + else return (m_innerBSize * m_blockSize) ; + } + + inline Index outerSize() const + { + if(m_blockSize == Dynamic) return m_outerOffset[m_outerBSize]; + else return (m_outerBSize * m_blockSize) ; + } + /** \returns the number of rows grouped by blocks */ + inline Index blockRows() const + { + return (IsColMajor ? m_innerBSize : m_outerBSize); + } + /** \returns the number of columns grouped by blocks */ + inline Index blockCols() const + { + return (IsColMajor ? m_outerBSize : m_innerBSize); + } + + inline Index outerBlocks() const { return m_outerBSize; } + inline Index innerBlocks() const { return m_innerBSize; } + + /** \returns the block index where outer belongs to */ + inline Index outerToBlock(Index outer) const + { + eigen_assert(outer < outerSize() && "OUTER INDEX OUT OF BOUNDS"); + + if(m_blockSize != Dynamic) + return (outer / m_blockSize); // Integer division + + StorageIndex b_outer = 0; + while(m_outerOffset[b_outer] <= outer) ++b_outer; + return b_outer - 1; + } + /** \returns the block index where inner belongs to */ + inline Index innerToBlock(Index inner) const + { + eigen_assert(inner < innerSize() && "OUTER INDEX OUT OF BOUNDS"); + + if(m_blockSize != Dynamic) + return (inner / m_blockSize); // Integer division + + StorageIndex b_inner = 0; + while(m_innerOffset[b_inner] <= inner) ++b_inner; + return b_inner - 1; + } + + /** + *\returns a reference to the (i,j) block as an Eigen Dense Matrix + */ + Ref coeffRef(Index brow, Index bcol) + { + eigen_assert(brow < blockRows() && "BLOCK ROW INDEX OUT OF BOUNDS"); + eigen_assert(bcol < blockCols() && "BLOCK nzblocksFlagCOLUMN OUT OF BOUNDS"); + + StorageIndex rsize = IsColMajor ? blockInnerSize(brow): blockOuterSize(bcol); + StorageIndex csize = IsColMajor ? blockOuterSize(bcol) : blockInnerSize(brow); + StorageIndex inner = IsColMajor ? brow : bcol; + StorageIndex outer = IsColMajor ? bcol : brow; + StorageIndex offset = m_outerIndex[outer]; + while(offset < m_outerIndex[outer+1] && m_indices[offset] != inner) + offset++; + if(m_indices[offset] == inner) + { + return Map(&(m_values[blockPtr(offset)]), rsize, csize); + } + else + { + //FIXME the block does not exist, Insert it !!!!!!!!! + eigen_assert("DYNAMIC INSERTION IS NOT YET SUPPORTED"); + } + } + + /** + * \returns the value of the (i,j) block as an Eigen Dense Matrix + */ + Map coeff(Index brow, Index bcol) const + { + eigen_assert(brow < blockRows() && "BLOCK ROW INDEX OUT OF BOUNDS"); + eigen_assert(bcol < blockCols() && "BLOCK COLUMN OUT OF BOUNDS"); + + StorageIndex rsize = IsColMajor ? blockInnerSize(brow): blockOuterSize(bcol); + StorageIndex csize = IsColMajor ? blockOuterSize(bcol) : blockInnerSize(brow); + StorageIndex inner = IsColMajor ? brow : bcol; + StorageIndex outer = IsColMajor ? bcol : brow; + StorageIndex offset = m_outerIndex[outer]; + while(offset < m_outerIndex[outer+1] && m_indices[offset] != inner) offset++; + if(m_indices[offset] == inner) + { + return Map (&(m_values[blockPtr(offset)]), rsize, csize); + } + else +// return BlockScalar::Zero(rsize, csize); + eigen_assert("NOT YET SUPPORTED"); + } + + // Block Matrix times vector product + template + BlockSparseTimeDenseProduct operator*(const VecType& lhs) const + { + return BlockSparseTimeDenseProduct(*this, lhs); + } + + /** \returns the number of nonzero blocks */ + inline Index nonZerosBlocks() const { return m_nonzerosblocks; } + /** \returns the total number of nonzero elements, including eventual explicit zeros in blocks */ + inline Index nonZeros() const { return m_nonzeros; } + + inline BlockScalarReturnType *valuePtr() {return static_cast(m_values);} +// inline Scalar *valuePtr(){ return m_values; } + inline StorageIndex *innerIndexPtr() {return m_indices; } + inline const StorageIndex *innerIndexPtr() const {return m_indices; } + inline StorageIndex *outerIndexPtr() {return m_outerIndex; } + inline const StorageIndex* outerIndexPtr() const {return m_outerIndex; } + + /** \brief for compatibility purposes with the SparseMatrix class */ + inline bool isCompressed() const {return true;} + /** + * \returns the starting index of the bi row block + */ + inline Index blockRowsIndex(Index bi) const + { + return IsColMajor ? blockInnerIndex(bi) : blockOuterIndex(bi); + } + + /** + * \returns the starting index of the bj col block + */ + inline Index blockColsIndex(Index bj) const + { + return IsColMajor ? blockOuterIndex(bj) : blockInnerIndex(bj); + } + + inline Index blockOuterIndex(Index bj) const + { + return (m_blockSize == Dynamic) ? m_outerOffset[bj] : (bj * m_blockSize); + } + inline Index blockInnerIndex(Index bi) const + { + return (m_blockSize == Dynamic) ? m_innerOffset[bi] : (bi * m_blockSize); + } + + // Not needed ??? + inline Index blockInnerSize(Index bi) const + { + return (m_blockSize == Dynamic) ? (m_innerOffset[bi+1] - m_innerOffset[bi]) : m_blockSize; + } + inline Index blockOuterSize(Index bj) const + { + return (m_blockSize == Dynamic) ? (m_outerOffset[bj+1]- m_outerOffset[bj]) : m_blockSize; + } + + /** + * \brief Browse the matrix by outer index + */ + class InnerIterator; // Browse column by column + + /** + * \brief Browse the matrix by block outer index + */ + class BlockInnerIterator; // Browse block by block + + friend std::ostream & operator << (std::ostream & s, const BlockSparseMatrix& m) + { + for (StorageIndex j = 0; j < m.outerBlocks(); ++j) + { + BlockInnerIterator itb(m, j); + for(; itb; ++itb) + { + s << "("< in the array of values + */ + Index blockPtr(Index id) const + { + if(m_blockSize == Dynamic) return m_blockPtr[id]; + else return id * m_blockSize * m_blockSize; + //return blockDynIdx(id, typename internal::conditional<(BlockSize==Dynamic), internal::true_type, internal::false_type>::type()); + } + + + protected: +// inline Index blockDynIdx(Index id, internal::true_type) const +// { +// return m_blockPtr[id]; +// } +// inline Index blockDynIdx(Index id, internal::false_type) const +// { +// return id * BlockSize * BlockSize; +// } + + // To be implemented + // Insert a block at a particular location... need to make a room for that + Map insert(Index brow, Index bcol); + + Index m_innerBSize; // Number of block rows + Index m_outerBSize; // Number of block columns + StorageIndex *m_innerOffset; // Starting index of each inner block (size m_innerBSize+1) + StorageIndex *m_outerOffset; // Starting index of each outer block (size m_outerBSize+1) + Index m_nonzerosblocks; // Total nonzeros blocks (lower than m_innerBSize x m_outerBSize) + Index m_nonzeros; // Total nonzeros elements + Scalar *m_values; //Values stored block column after block column (size m_nonzeros) + StorageIndex *m_blockPtr; // Pointer to the beginning of each block in m_values, size m_nonzeroblocks ... null for fixed-size blocks + StorageIndex *m_indices; //Inner block indices, size m_nonzerosblocks ... OK + StorageIndex *m_outerIndex; // Starting pointer of each block column in m_indices (size m_outerBSize)... OK + Index m_blockSize; // Size of a block for fixed-size blocks, otherwise -1 +}; + +template +class BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, _StorageIndex>::BlockInnerIterator +{ + public: + + enum{ + Flags = _Options + }; + + BlockInnerIterator(const BlockSparseMatrix& mat, const Index outer) + : m_mat(mat),m_outer(outer), + m_id(mat.m_outerIndex[outer]), + m_end(mat.m_outerIndex[outer+1]) + { + } + + inline BlockInnerIterator& operator++() {m_id++; return *this; } + + inline const Map value() const + { + return Map(&(m_mat.m_values[m_mat.blockPtr(m_id)]), + rows(),cols()); + } + inline Map valueRef() + { + return Map(&(m_mat.m_values[m_mat.blockPtr(m_id)]), + rows(),cols()); + } + // Block inner index + inline Index index() const {return m_mat.m_indices[m_id]; } + inline Index outer() const { return m_outer; } + // block row index + inline Index row() const {return index(); } + // block column index + inline Index col() const {return outer(); } + // FIXME Number of rows in the current block + inline Index rows() const { return (m_mat.m_blockSize==Dynamic) ? (m_mat.m_innerOffset[index()+1] - m_mat.m_innerOffset[index()]) : m_mat.m_blockSize; } + // Number of columns in the current block ... + inline Index cols() const { return (m_mat.m_blockSize==Dynamic) ? (m_mat.m_outerOffset[m_outer+1]-m_mat.m_outerOffset[m_outer]) : m_mat.m_blockSize;} + inline operator bool() const { return (m_id < m_end); } + + protected: + const BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, StorageIndex>& m_mat; + const Index m_outer; + Index m_id; + Index m_end; +}; + +template +class BlockSparseMatrix<_Scalar, _BlockAtCompileTime, _Options, _StorageIndex>::InnerIterator +{ + public: + InnerIterator(const BlockSparseMatrix& mat, Index outer) + : m_mat(mat),m_outerB(mat.outerToBlock(outer)),m_outer(outer), + itb(mat, mat.outerToBlock(outer)), + m_offset(outer - mat.blockOuterIndex(m_outerB)) + { + if (itb) + { + m_id = m_mat.blockInnerIndex(itb.index()); + m_start = m_id; + m_end = m_mat.blockInnerIndex(itb.index()+1); + } + } + inline InnerIterator& operator++() + { + m_id++; + if (m_id >= m_end) + { + ++itb; + if (itb) + { + m_id = m_mat.blockInnerIndex(itb.index()); + m_start = m_id; + m_end = m_mat.blockInnerIndex(itb.index()+1); + } + } + return *this; + } + inline const Scalar& value() const + { + return itb.value().coeff(m_id - m_start, m_offset); + } + inline Scalar& valueRef() + { + return itb.valueRef().coeff(m_id - m_start, m_offset); + } + inline Index index() const { return m_id; } + inline Index outer() const {return m_outer; } + inline Index col() const {return outer(); } + inline Index row() const { return index();} + inline operator bool() const + { + return itb; + } + protected: + const BlockSparseMatrix& m_mat; + const Index m_outer; + const Index m_outerB; + BlockInnerIterator itb; // Iterator through the blocks + const Index m_offset; // Position of this column in the block + Index m_start; // starting inner index of this block + Index m_id; // current inner index in the block + Index m_end; // starting inner index of the next block + +}; +} // end namespace Eigen + +#endif // EIGEN_SPARSEBLOCKMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h b/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h new file mode 100644 index 000000000..037a13f86 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/DynamicSparseMatrix.h @@ -0,0 +1,392 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008-2009 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_DYNAMIC_SPARSEMATRIX_H +#define EIGEN_DYNAMIC_SPARSEMATRIX_H + +namespace Eigen { + +/** \deprecated use a SparseMatrix in an uncompressed mode + * + * \class DynamicSparseMatrix + * + * \brief A sparse matrix class designed for matrix assembly purpose + * + * \param _Scalar the scalar type, i.e. the type of the coefficients + * + * Unlike SparseMatrix, this class provides a much higher degree of flexibility. In particular, it allows + * random read/write accesses in log(rho*outer_size) where \c rho is the probability that a coefficient is + * nonzero and outer_size is the number of columns if the matrix is column-major and the number of rows + * otherwise. + * + * Internally, the data are stored as a std::vector of compressed vector. The performances of random writes might + * decrease as the number of nonzeros per inner-vector increase. In practice, we observed very good performance + * till about 100 nonzeros/vector, and the performance remains relatively good till 500 nonzeros/vectors. + * + * \see SparseMatrix + */ + +namespace internal { +template +struct traits > +{ + typedef _Scalar Scalar; + typedef _StorageIndex StorageIndex; + typedef Sparse StorageKind; + typedef MatrixXpr XprKind; + enum { + RowsAtCompileTime = Dynamic, + ColsAtCompileTime = Dynamic, + MaxRowsAtCompileTime = Dynamic, + MaxColsAtCompileTime = Dynamic, + Flags = _Options | NestByRefBit | LvalueBit, + CoeffReadCost = NumTraits::ReadCost, + SupportedAccessPatterns = OuterRandomAccessPattern + }; +}; +} + +template + class DynamicSparseMatrix + : public SparseMatrixBase > +{ + typedef SparseMatrixBase Base; + using Base::convert_index; + public: + EIGEN_SPARSE_PUBLIC_INTERFACE(DynamicSparseMatrix) + // FIXME: why are these operator already alvailable ??? + // EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, +=) + // EIGEN_SPARSE_INHERIT_ASSIGNMENT_OPERATOR(DynamicSparseMatrix, -=) + typedef MappedSparseMatrix Map; + using Base::IsRowMajor; + using Base::operator=; + enum { + Options = _Options + }; + + protected: + + typedef DynamicSparseMatrix TransposedSparseMatrix; + + Index m_innerSize; + std::vector > m_data; + + public: + + inline Index rows() const { return IsRowMajor ? outerSize() : m_innerSize; } + inline Index cols() const { return IsRowMajor ? m_innerSize : outerSize(); } + inline Index innerSize() const { return m_innerSize; } + inline Index outerSize() const { return convert_index(m_data.size()); } + inline Index innerNonZeros(Index j) const { return m_data[j].size(); } + + std::vector >& _data() { return m_data; } + const std::vector >& _data() const { return m_data; } + + /** \returns the coefficient value at given position \a row, \a col + * This operation involes a log(rho*outer_size) binary search. + */ + inline Scalar coeff(Index row, Index col) const + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return m_data[outer].at(inner); + } + + /** \returns a reference to the coefficient value at given position \a row, \a col + * This operation involes a log(rho*outer_size) binary search. If the coefficient does not + * exist yet, then a sorted insertion into a sequential buffer is performed. + */ + inline Scalar& coeffRef(Index row, Index col) + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return m_data[outer].atWithInsertion(inner); + } + + class InnerIterator; + class ReverseInnerIterator; + + void setZero() + { + for (Index j=0; j0) + { + Index reserveSizePerVector = (std::max)(reserveSize/outerSize(),Index(4)); + for (Index j=0; j(m_data[outer].size()) - 1; + m_data[outer].resize(id+2,1); + + while ( (id >= startId) && (m_data[outer].index(id) > inner) ) + { + m_data[outer].index(id+1) = m_data[outer].index(id); + m_data[outer].value(id+1) = m_data[outer].value(id); + --id; + } + m_data[outer].index(id+1) = inner; + m_data[outer].value(id+1) = 0; + return m_data[outer].value(id+1); + } + + /** Does nothing: provided for compatibility with SparseMatrix */ + inline void finalize() {} + + /** Suppress all nonzeros which are smaller than \a reference under the tolerence \a epsilon */ + void prune(Scalar reference, RealScalar epsilon = NumTraits::dummy_precision()) + { + for (Index j=0; jinnerSize) + { + // remove all coefficients with innerCoord>=innerSize + // TODO + //std::cerr << "not implemented yet\n"; + exit(2); + } + if (m_data.size() != outerSize) + { + m_data.resize(outerSize); + } + } + + /** The class DynamicSparseMatrix is deprectaed */ + EIGEN_DEPRECATED inline DynamicSparseMatrix() + : m_innerSize(0), m_data(0) + { + eigen_assert(innerSize()==0 && outerSize()==0); + } + + /** The class DynamicSparseMatrix is deprectaed */ + EIGEN_DEPRECATED inline DynamicSparseMatrix(Index rows, Index cols) + : m_innerSize(0) + { + resize(rows, cols); + } + + /** The class DynamicSparseMatrix is deprectaed */ + template + EIGEN_DEPRECATED explicit inline DynamicSparseMatrix(const SparseMatrixBase& other) + : m_innerSize(0) + { + Base::operator=(other.derived()); + } + + inline DynamicSparseMatrix(const DynamicSparseMatrix& other) + : Base(), m_innerSize(0) + { + *this = other.derived(); + } + + inline void swap(DynamicSparseMatrix& other) + { + //EIGEN_DBG_SPARSE(std::cout << "SparseMatrix:: swap\n"); + std::swap(m_innerSize, other.m_innerSize); + //std::swap(m_outerSize, other.m_outerSize); + m_data.swap(other.m_data); + } + + inline DynamicSparseMatrix& operator=(const DynamicSparseMatrix& other) + { + if (other.isRValue()) + { + swap(other.const_cast_derived()); + } + else + { + resize(other.rows(), other.cols()); + m_data = other.m_data; + } + return *this; + } + + /** Destructor */ + inline ~DynamicSparseMatrix() {} + + public: + + /** \deprecated + * Set the matrix to zero and reserve the memory for \a reserveSize nonzero coefficients. */ + EIGEN_DEPRECATED void startFill(Index reserveSize = 1000) + { + setZero(); + reserve(reserveSize); + } + + /** \deprecated use insert() + * inserts a nonzero coefficient at given coordinates \a row, \a col and returns its reference assuming that: + * 1 - the coefficient does not exist yet + * 2 - this the coefficient with greater inner coordinate for the given outer coordinate. + * In other words, assuming \c *this is column-major, then there must not exists any nonzero coefficient of coordinates + * \c i \c x \a col such that \c i >= \a row. Otherwise the matrix is invalid. + * + * \see fillrand(), coeffRef() + */ + EIGEN_DEPRECATED Scalar& fill(Index row, Index col) + { + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + return insertBack(outer,inner); + } + + /** \deprecated use insert() + * Like fill() but with random inner coordinates. + * Compared to the generic coeffRef(), the unique limitation is that we assume + * the coefficient does not exist yet. + */ + EIGEN_DEPRECATED Scalar& fillrand(Index row, Index col) + { + return insert(row,col); + } + + /** \deprecated use finalize() + * Does nothing. Provided for compatibility with SparseMatrix. */ + EIGEN_DEPRECATED void endFill() {} + +# ifdef EIGEN_DYNAMICSPARSEMATRIX_PLUGIN +# include EIGEN_DYNAMICSPARSEMATRIX_PLUGIN +# endif + }; + +template +class DynamicSparseMatrix::InnerIterator : public SparseVector::InnerIterator +{ + typedef typename SparseVector::InnerIterator Base; + public: + InnerIterator(const DynamicSparseMatrix& mat, Index outer) + : Base(mat.m_data[outer]), m_outer(outer) + {} + + inline Index row() const { return IsRowMajor ? m_outer : Base::index(); } + inline Index col() const { return IsRowMajor ? Base::index() : m_outer; } + inline Index outer() const { return m_outer; } + + protected: + const Index m_outer; +}; + +template +class DynamicSparseMatrix::ReverseInnerIterator : public SparseVector::ReverseInnerIterator +{ + typedef typename SparseVector::ReverseInnerIterator Base; + public: + ReverseInnerIterator(const DynamicSparseMatrix& mat, Index outer) + : Base(mat.m_data[outer]), m_outer(outer) + {} + + inline Index row() const { return IsRowMajor ? m_outer : Base::index(); } + inline Index col() const { return IsRowMajor ? Base::index() : m_outer; } + inline Index outer() const { return m_outer; } + + protected: + const Index m_outer; +}; + +namespace internal { + +template +struct evaluator > + : evaluator_base > +{ + typedef _Scalar Scalar; + typedef DynamicSparseMatrix<_Scalar,_Options,_StorageIndex> SparseMatrixType; + typedef typename SparseMatrixType::InnerIterator InnerIterator; + typedef typename SparseMatrixType::ReverseInnerIterator ReverseInnerIterator; + + enum { + CoeffReadCost = NumTraits<_Scalar>::ReadCost, + Flags = SparseMatrixType::Flags + }; + + evaluator() : m_matrix(0) {} + evaluator(const SparseMatrixType &mat) : m_matrix(&mat) {} + + operator SparseMatrixType&() { return m_matrix->const_cast_derived(); } + operator const SparseMatrixType&() const { return *m_matrix; } + + Scalar coeff(Index row, Index col) const { return m_matrix->coeff(row,col); } + + Index nonZerosEstimate() const { return m_matrix->nonZeros(); } + + const SparseMatrixType *m_matrix; +}; + +} + +} // end namespace Eigen + +#endif // EIGEN_DYNAMIC_SPARSEMATRIX_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h b/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h new file mode 100644 index 000000000..41e4af4a4 --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/MarketIO.h @@ -0,0 +1,275 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2011 Gael Guennebaud +// Copyright (C) 2012 Desire NUENTSA WAKAM +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_SPARSE_MARKET_IO_H +#define EIGEN_SPARSE_MARKET_IO_H + +#include + +namespace Eigen { + +namespace internal +{ + template + inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, Scalar& value) + { + line >> i >> j >> value; + i--; + j--; + if(i>=0 && j>=0 && i + inline bool GetMarketLine (std::stringstream& line, Index& M, Index& N, Index& i, Index& j, std::complex& value) + { + Scalar valR, valI; + line >> i >> j >> valR >> valI; + i--; + j--; + if(i>=0 && j>=0 && i(valR, valI); + return true; + } + else + return false; + } + + template + inline void GetVectorElt (const std::string& line, RealScalar& val) + { + std::istringstream newline(line); + newline >> val; + } + + template + inline void GetVectorElt (const std::string& line, std::complex& val) + { + RealScalar valR, valI; + std::istringstream newline(line); + newline >> valR >> valI; + val = std::complex(valR, valI); + } + + template + inline void putMarketHeader(std::string& header,int sym) + { + header= "%%MatrixMarket matrix coordinate "; + if(internal::is_same >::value || internal::is_same >::value) + { + header += " complex"; + if(sym == Symmetric) header += " symmetric"; + else if (sym == SelfAdjoint) header += " Hermitian"; + else header += " general"; + } + else + { + header += " real"; + if(sym == Symmetric) header += " symmetric"; + else header += " general"; + } + } + + template + inline void PutMatrixElt(Scalar value, int row, int col, std::ofstream& out) + { + out << row << " "<< col << " " << value << "\n"; + } + template + inline void PutMatrixElt(std::complex value, int row, int col, std::ofstream& out) + { + out << row << " " << col << " " << value.real() << " " << value.imag() << "\n"; + } + + + template + inline void putVectorElt(Scalar value, std::ofstream& out) + { + out << value << "\n"; + } + template + inline void putVectorElt(std::complex value, std::ofstream& out) + { + out << value.real << " " << value.imag()<< "\n"; + } + +} // end namepsace internal + +inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isvector) +{ + sym = 0; + iscomplex = false; + isvector = false; + std::ifstream in(filename.c_str(),std::ios::in); + if(!in) + return false; + + std::string line; + // The matrix header is always the first line in the file + std::getline(in, line); eigen_assert(in.good()); + + std::stringstream fmtline(line); + std::string substr[5]; + fmtline>> substr[0] >> substr[1] >> substr[2] >> substr[3] >> substr[4]; + if(substr[2].compare("array") == 0) isvector = true; + if(substr[3].compare("complex") == 0) iscomplex = true; + if(substr[4].compare("symmetric") == 0) sym = Symmetric; + else if (substr[4].compare("Hermitian") == 0) sym = SelfAdjoint; + + return true; +} + +template +bool loadMarket(SparseMatrixType& mat, const std::string& filename) +{ + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::Index Index; + std::ifstream input(filename.c_str(),std::ios::in); + if(!input) + return false; + + const int maxBuffersize = 2048; + char buffer[maxBuffersize]; + + bool readsizes = false; + + typedef Triplet T; + std::vector elements; + + Index M(-1), N(-1), NNZ(-1); + Index count = 0; + while(input.getline(buffer, maxBuffersize)) + { + // skip comments + //NOTE An appropriate test should be done on the header to get the symmetry + if(buffer[0]=='%') + continue; + + std::stringstream line(buffer); + + if(!readsizes) + { + line >> M >> N >> NNZ; + if(M > 0 && N > 0 && NNZ > 0) + { + readsizes = true; + //std::cout << "sizes: " << M << "," << N << "," << NNZ << "\n"; + mat.resize(M,N); + mat.reserve(NNZ); + } + } + else + { + Index i(-1), j(-1); + Scalar value; + if( internal::GetMarketLine(line, M, N, i, j, value) ) + { + ++ count; + elements.push_back(T(i,j,value)); + } + else + std::cerr << "Invalid read: " << i << "," << j << "\n"; + } + } + mat.setFromTriplets(elements.begin(), elements.end()); + if(count!=NNZ) + std::cerr << count << "!=" << NNZ << "\n"; + + input.close(); + return true; +} + +template +bool loadMarketVector(VectorType& vec, const std::string& filename) +{ + typedef typename VectorType::Scalar Scalar; + std::ifstream in(filename.c_str(), std::ios::in); + if(!in) + return false; + + std::string line; + int n(0), col(0); + do + { // Skip comments + std::getline(in, line); eigen_assert(in.good()); + } while (line[0] == '%'); + std::istringstream newline(line); + newline >> n >> col; + eigen_assert(n>0 && col>0); + vec.resize(n); + int i = 0; + Scalar value; + while ( std::getline(in, line) && (i < n) ){ + internal::GetVectorElt(line, value); + vec(i++) = value; + } + in.close(); + if (i!=n){ + std::cerr<< "Unable to read all elements from file " << filename << "\n"; + return false; + } + return true; +} + +template +bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0) +{ + typedef typename SparseMatrixType::Scalar Scalar; + std::ofstream out(filename.c_str(),std::ios::out); + if(!out) + return false; + + out.flags(std::ios_base::scientific); + out.precision(64); + std::string header; + internal::putMarketHeader(header, sym); + out << header << std::endl; + out << mat.rows() << " " << mat.cols() << " " << mat.nonZeros() << "\n"; + int count = 0; + for(int j=0; j +bool saveMarketVector (const VectorType& vec, const std::string& filename) +{ + typedef typename VectorType::Scalar Scalar; + std::ofstream out(filename.c_str(),std::ios::out); + if(!out) + return false; + + out.flags(std::ios_base::scientific); + out.precision(64); + if(internal::is_same >::value || internal::is_same >::value) + out << "%%MatrixMarket matrix array complex general\n"; + else + out << "%%MatrixMarket matrix array real general\n"; + out << vec.size() << " "<< 1 << "\n"; + for (int i=0; i < vec.size(); i++){ + internal::putVectorElt(vec(i), out); + } + out.close(); + return true; +} + +} // end namespace Eigen + +#endif // EIGEN_SPARSE_MARKET_IO_H diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h b/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h new file mode 100644 index 000000000..02916ea6f --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/MatrixMarketIterator.h @@ -0,0 +1,247 @@ + +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2012 Desire NUENTSA WAKAM +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_BROWSE_MATRICES_H +#define EIGEN_BROWSE_MATRICES_H + +namespace Eigen { + +enum { + SPD = 0x100, + NonSymmetric = 0x0 +}; + +/** + * @brief Iterator to browse matrices from a specified folder + * + * This is used to load all the matrices from a folder. + * The matrices should be in Matrix Market format + * It is assumed that the matrices are named as matname.mtx + * and matname_SPD.mtx if the matrix is Symmetric and positive definite (or Hermitian) + * The right hand side vectors are loaded as well, if they exist. + * They should be named as matname_b.mtx. + * Note that the right hand side for a SPD matrix is named as matname_SPD_b.mtx + * + * Sometimes a reference solution is available. In this case, it should be named as matname_x.mtx + * + * Sample code + * \code + * + * \endcode + * + * \tparam Scalar The scalar type + */ +template +class MatrixMarketIterator +{ + typedef typename NumTraits::Real RealScalar; + public: + typedef Matrix VectorType; + typedef SparseMatrix MatrixType; + + public: + MatrixMarketIterator(const std::string &folder) + : m_sym(0), m_isvalid(false), m_matIsLoaded(false), m_hasRhs(false), m_hasrefX(false), m_folder(folder) + { + m_folder_id = opendir(folder.c_str()); + if(m_folder_id) + Getnextvalidmatrix(); + } + + ~MatrixMarketIterator() + { + if (m_folder_id) closedir(m_folder_id); + } + + inline MatrixMarketIterator& operator++() + { + m_matIsLoaded = false; + m_hasrefX = false; + m_hasRhs = false; + Getnextvalidmatrix(); + return *this; + } + inline operator bool() const { return m_isvalid;} + + /** Return the sparse matrix corresponding to the current file */ + inline MatrixType& matrix() + { + // Read the matrix + if (m_matIsLoaded) return m_mat; + + std::string matrix_file = m_folder + "/" + m_matname + ".mtx"; + if ( !loadMarket(m_mat, matrix_file)) + { + std::cerr << "Warning loadMarket failed when loading \"" << matrix_file << "\"" << std::endl; + m_matIsLoaded = false; + return m_mat; + } + m_matIsLoaded = true; + + if (m_sym != NonSymmetric) + { + // Check whether we need to restore a full matrix: + RealScalar diag_norm = m_mat.diagonal().norm(); + RealScalar lower_norm = m_mat.template triangularView().norm(); + RealScalar upper_norm = m_mat.template triangularView().norm(); + if(lower_norm>diag_norm && upper_norm==diag_norm) + { + // only the lower part is stored + MatrixType tmp(m_mat); + m_mat = tmp.template selfadjointView(); + } + else if(upper_norm>diag_norm && lower_norm==diag_norm) + { + // only the upper part is stored + MatrixType tmp(m_mat); + m_mat = tmp.template selfadjointView(); + } + } + return m_mat; + } + + /** Return the right hand side corresponding to the current matrix. + * If the rhs file is not provided, a random rhs is generated + */ + inline VectorType& rhs() + { + // Get the right hand side + if (m_hasRhs) return m_rhs; + + std::string rhs_file; + rhs_file = m_folder + "/" + m_matname + "_b.mtx"; // The pattern is matname_b.mtx + m_hasRhs = Fileexists(rhs_file); + if (m_hasRhs) + { + m_rhs.resize(m_mat.cols()); + m_hasRhs = loadMarketVector(m_rhs, rhs_file); + } + if (!m_hasRhs) + { + // Generate a random right hand side + if (!m_matIsLoaded) this->matrix(); + m_refX.resize(m_mat.cols()); + m_refX.setRandom(); + m_rhs = m_mat * m_refX; + m_hasrefX = true; + m_hasRhs = true; + } + return m_rhs; + } + + /** Return a reference solution + * If it is not provided and if the right hand side is not available + * then refX is randomly generated such that A*refX = b + * where A and b are the matrix and the rhs. + * Note that when a rhs is provided, refX is not available + */ + inline VectorType& refX() + { + // Check if a reference solution is provided + if (m_hasrefX) return m_refX; + + std::string lhs_file; + lhs_file = m_folder + "/" + m_matname + "_x.mtx"; + m_hasrefX = Fileexists(lhs_file); + if (m_hasrefX) + { + m_refX.resize(m_mat.cols()); + m_hasrefX = loadMarketVector(m_refX, lhs_file); + } + else + m_refX.resize(0); + return m_refX; + } + + inline std::string& matname() { return m_matname; } + + inline int sym() { return m_sym; } + + bool hasRhs() {return m_hasRhs; } + bool hasrefX() {return m_hasrefX; } + bool isFolderValid() { return bool(m_folder_id); } + + protected: + + inline bool Fileexists(std::string file) + { + std::ifstream file_id(file.c_str()); + if (!file_id.good() ) + { + return false; + } + else + { + file_id.close(); + return true; + } + } + + void Getnextvalidmatrix( ) + { + m_isvalid = false; + // Here, we return with the next valid matrix in the folder + while ( (m_curs_id = readdir(m_folder_id)) != NULL) { + m_isvalid = false; + std::string curfile; + curfile = m_folder + "/" + m_curs_id->d_name; + // Discard if it is a folder + if (m_curs_id->d_type == DT_DIR) continue; //FIXME This may not be available on non BSD systems +// struct stat st_buf; +// stat (curfile.c_str(), &st_buf); +// if (S_ISDIR(st_buf.st_mode)) continue; + + // Determine from the header if it is a matrix or a right hand side + bool isvector,iscomplex=false; + if(!getMarketHeader(curfile,m_sym,iscomplex,isvector)) continue; + if(isvector) continue; + if (!iscomplex) + { + if(internal::is_same >::value || internal::is_same >::value) + continue; + } + if (iscomplex) + { + if(internal::is_same::value || internal::is_same::value) + continue; + } + + + // Get the matrix name + std::string filename = m_curs_id->d_name; + m_matname = filename.substr(0, filename.length()-4); + + // Find if the matrix is SPD + size_t found = m_matname.find("SPD"); + if( (found!=std::string::npos) && (m_sym != NonSymmetric) ) + m_sym = SPD; + + m_isvalid = true; + break; + } + } + int m_sym; // Symmetry of the matrix + MatrixType m_mat; // Current matrix + VectorType m_rhs; // Current vector + VectorType m_refX; // The reference solution, if exists + std::string m_matname; // Matrix Name + bool m_isvalid; + bool m_matIsLoaded; // Determine if the matrix has already been loaded from the file + bool m_hasRhs; // The right hand side exists + bool m_hasrefX; // A reference solution is provided + std::string m_folder; + DIR * m_folder_id; + struct dirent *m_curs_id; + +}; + +} // end namespace Eigen + +#endif diff --git a/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h b/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h new file mode 100644 index 000000000..ee97299af --- /dev/null +++ b/src/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h @@ -0,0 +1,327 @@ +// This file is part of Eigen, a lightweight C++ template library +// for linear algebra. +// +// Copyright (C) 2008 Gael Guennebaud +// +// This Source Code Form is subject to the terms of the Mozilla +// Public License v. 2.0. If a copy of the MPL was not distributed +// with this file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef EIGEN_RANDOMSETTER_H +#define EIGEN_RANDOMSETTER_H + +namespace Eigen { + +/** Represents a std::map + * + * \see RandomSetter + */ +template struct StdMapTraits +{ + typedef int KeyType; + typedef std::map Type; + enum { + IsSorted = 1 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; + +#ifdef EIGEN_UNORDERED_MAP_SUPPORT +/** Represents a std::unordered_map + * + * To use it you need to both define EIGEN_UNORDERED_MAP_SUPPORT and include the unordered_map header file + * yourself making sure that unordered_map is defined in the std namespace. + * + * For instance, with current version of gcc you can either enable C++0x standard (-std=c++0x) or do: + * \code + * #include + * #define EIGEN_UNORDERED_MAP_SUPPORT + * namespace std { + * using std::tr1::unordered_map; + * } + * \endcode + * + * \see RandomSetter + */ +template struct StdUnorderedMapTraits +{ + typedef int KeyType; + typedef std::unordered_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; +#endif // EIGEN_UNORDERED_MAP_SUPPORT + +#ifdef _DENSE_HASH_MAP_H_ +/** Represents a google::dense_hash_map + * + * \see RandomSetter + */ +template struct GoogleDenseHashMapTraits +{ + typedef int KeyType; + typedef google::dense_hash_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type& map, const KeyType& k) + { map.set_empty_key(k); } +}; +#endif + +#ifdef _SPARSE_HASH_MAP_H_ +/** Represents a google::sparse_hash_map + * + * \see RandomSetter + */ +template struct GoogleSparseHashMapTraits +{ + typedef int KeyType; + typedef google::sparse_hash_map Type; + enum { + IsSorted = 0 + }; + + static void setInvalidKey(Type&, const KeyType&) {} +}; +#endif + +/** \class RandomSetter + * + * \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access + * + * \tparam SparseMatrixType the type of the sparse matrix we are updating + * \tparam MapTraits a traits class representing the map implementation used for the temporary sparse storage. + * Its default value depends on the system. + * \tparam OuterPacketBits defines the number of rows (or columns) manage by a single map object + * as a power of two exponent. + * + * This class temporarily represents a sparse matrix object using a generic map implementation allowing for + * efficient random access. The conversion from the compressed representation to a hash_map object is performed + * in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy + * suggest the use of nested blocks as in this example: + * + * \code + * SparseMatrix m(rows,cols); + * { + * RandomSetter > w(m); + * // don't use m but w instead with read/write random access to the coefficients: + * for(;;) + * w(rand(),rand()) = rand; + * } + * // when w is deleted, the data are copied back to m + * // and m is ready to use. + * \endcode + * + * Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would + * involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter + * use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order. + * To reach optimal performance, this value should be adjusted according to the average number of nonzeros + * per rows/columns. + * + * The possible values for the template parameter MapTraits are: + * - \b StdMapTraits: corresponds to std::map. (does not perform very well) + * - \b GnuHashMapTraits: corresponds to __gnu_cxx::hash_map (available only with GCC) + * - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption) + * - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance) + * + * The default map implementation depends on the availability, and the preferred order is: + * GoogleSparseHashMapTraits, GnuHashMapTraits, and finally StdMapTraits. + * + * For performance and memory consumption reasons it is highly recommended to use one of + * the Google's hash_map implementation. To enable the support for them, you have two options: + * - \#include yourself \b before Eigen/Sparse header + * - define EIGEN_GOOGLEHASH_SUPPORT + * In the later case the inclusion of is made for you. + * + * \see http://code.google.com/p/google-sparsehash/ + */ +template class MapTraits = +#if defined _DENSE_HASH_MAP_H_ + GoogleDenseHashMapTraits +#elif defined _HASH_MAP + GnuHashMapTraits +#else + StdMapTraits +#endif + ,int OuterPacketBits = 6> +class RandomSetter +{ + typedef typename SparseMatrixType::Scalar Scalar; + typedef typename SparseMatrixType::StorageIndex StorageIndex; + + struct ScalarWrapper + { + ScalarWrapper() : value(0) {} + Scalar value; + }; + typedef typename MapTraits::KeyType KeyType; + typedef typename MapTraits::Type HashMapType; + static const int OuterPacketMask = (1 << OuterPacketBits) - 1; + enum { + SwapStorage = 1 - MapTraits::IsSorted, + TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0, + SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor + }; + + public: + + /** Constructs a random setter object from the sparse matrix \a target + * + * Note that the initial value of \a target are imported. If you want to re-set + * a sparse matrix from scratch, then you must set it to zero first using the + * setZero() function. + */ + inline RandomSetter(SparseMatrixType& target) + : mp_target(&target) + { + const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize(); + const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize(); + m_outerPackets = outerSize >> OuterPacketBits; + if (outerSize&OuterPacketMask) + m_outerPackets += 1; + m_hashmaps = new HashMapType[m_outerPackets]; + // compute number of bits needed to store inner indices + Index aux = innerSize - 1; + m_keyBitsOffset = 0; + while (aux) + { + ++m_keyBitsOffset; + aux = aux >> 1; + } + KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset)); + for (Index k=0; k::setInvalidKey(m_hashmaps[k],ik); + + // insert current coeffs + for (Index j=0; jouterSize(); ++j) + for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it) + (*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value(); + } + + /** Destructor updating back the sparse matrix target */ + ~RandomSetter() + { + KeyType keyBitsMask = (1<setZero(); + mp_target->makeCompressed(); + mp_target->reserve(nonZeros()); + Index prevOuter = -1; + for (Index k=0; kfirst >> m_keyBitsOffset) + outerOffset; + const Index inner = it->first & keyBitsMask; + if (prevOuter!=outer) + { + for (Index j=prevOuter+1;j<=outer;++j) + mp_target->startVec(j); + prevOuter = outer; + } + mp_target->insertBackByOuterInner(outer, inner) = it->second.value; + } + } + mp_target->finalize(); + } + else + { + VectorXi positions(mp_target->outerSize()); + positions.setZero(); + // pass 1 + for (Index k=0; kfirst & keyBitsMask; + ++positions[outer]; + } + } + // prefix sum + Index count = 0; + for (Index j=0; jouterSize(); ++j) + { + Index tmp = positions[j]; + mp_target->outerIndexPtr()[j] = count; + positions[j] = count; + count += tmp; + } + mp_target->makeCompressed(); + mp_target->outerIndexPtr()[mp_target->outerSize()] = count; + mp_target->resizeNonZeros(count); + // pass 2 + for (Index k=0; kfirst >> m_keyBitsOffset) + outerOffset; + const Index outer = it->first & keyBitsMask; + // sorted insertion + // Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients, + // moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a + // small fraction of them have to be sorted, whence the following simple procedure: + Index posStart = mp_target->outerIndexPtr()[outer]; + Index i = (positions[outer]++) - 1; + while ( (i >= posStart) && (mp_target->innerIndexPtr()[i] > inner) ) + { + mp_target->valuePtr()[i+1] = mp_target->valuePtr()[i]; + mp_target->innerIndexPtr()[i+1] = mp_target->innerIndexPtr()[i]; + --i; + } + mp_target->innerIndexPtr()[i+1] = inner; + mp_target->valuePtr()[i+1] = it->second.value; + } + } + } + delete[] m_hashmaps; + } + + /** \returns a reference to the coefficient at given coordinates \a row, \a col */ + Scalar& operator() (Index row, Index col) + { + const Index outer = SetterRowMajor ? row : col; + const Index inner = SetterRowMajor ? col : row; + const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map + const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet + const KeyType key = internal::convert_index((outerMinor<(m_hashmaps[k].size()); + return nz; + } + + + protected: + + HashMapType* m_hashmaps; + SparseMatrixType* mp_target; + Index m_outerPackets; + unsigned char m_keyBitsOffset; +}; + +} // end namespace Eigen + +#endif // EIGEN_RANDOMSETTER_H diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 8615ab91c..37b0c0ffc 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -612,7 +612,9 @@ double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); -PointSet normals(const PointSet& points, const EigenMesh3D& mesh); +PointSet normals(const PointSet& points, const EigenMesh3D& mesh, + double eps = 0.05, // min distance from edges + std::function throw_on_cancel = [](){}); inline Vec2d to_vec2(const Vec3d& v3) { return {v3(X), v3(Y)}; @@ -1049,7 +1051,7 @@ bool SLASupportTree::generate(const PointSet &points, tifcl(); // calculate the normals to the triangles belonging to filtered points - auto nmls = sla::normals(filt_pts, mesh); + auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl); head_norm.resize(count, 3); head_pos.resize(count, 3); diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 50d7775a2..5d40bb514 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -1,3 +1,4 @@ +#include #include "SLA/SLASupportTree.hpp" #include "SLA/SLABoilerPlate.hpp" #include "SLA/SLASpatIndex.hpp" @@ -9,15 +10,8 @@ #include "boost/geometry/index/rtree.hpp" #include - -//#if !defined(_MSC_VER) || defined(_WIN64) -#if 1 -#define IGL_COMPATIBLE -#endif - -#ifdef IGL_COMPATIBLE #include -#endif +#include #include "SLASpatIndex.hpp" #include "ClipperUtils.hpp" @@ -84,33 +78,124 @@ size_t SpatIndex::size() const return m_impl->m_store.size(); } -PointSet normals(const PointSet& points, const EigenMesh3D& mesh) { - if(points.rows() == 0 || mesh.V.rows() == 0 || mesh.F.rows() == 0) return {}; -#ifdef IGL_COMPATIBLE +bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, + double eps = 0.05) +{ + using Line3D = Eigen::ParametrizedLine; + + auto line = Line3D::Through(e1, e2); + double d = line.distance(p); + return std::abs(d) < eps; +} + +template double distance(const Vec& pp1, const Vec& pp2) { + auto p = pp2 - pp1; + return std::sqrt(p.transpose() * p); +} + +PointSet normals(const PointSet& points, const EigenMesh3D& emesh, + double eps, + std::function throw_on_cancel) { + if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0) + return {}; + Eigen::VectorXd dists; Eigen::VectorXi I; PointSet C; + // We need to remove duplicate vertices and have a true index triangle + // structure + EigenMesh3D mesh; + Eigen::VectorXi SVI, SVJ; + igl::remove_duplicate_vertices(emesh.V, emesh.F, 1e-6, + mesh.V, SVI, SVJ, mesh.F); + igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); PointSet ret(I.rows(), 3); for(int i = 0; i < I.rows(); i++) { + throw_on_cancel(); auto idx = I(i); auto trindex = mesh.F.row(idx); - auto& p1 = mesh.V.row(trindex(0)); - auto& p2 = mesh.V.row(trindex(1)); - auto& p3 = mesh.V.row(trindex(2)); + const Vec3d& p1 = mesh.V.row(trindex(0)); + const Vec3d& p2 = mesh.V.row(trindex(1)); + const Vec3d& p3 = mesh.V.row(trindex(2)); - Eigen::Vector3d U = p2 - p1; - Eigen::Vector3d V = p3 - p1; - ret.row(i) = U.cross(V).normalized(); + // We should check if the point lies on an edge of the hosting triangle. + // If it does than all the other triangles using the same two points + // have to be searched and the final normal should be some kind of + // aggregation of the participating triangle normals. We should also + // consider the cases where the support point lies right on a vertex + // of its triangle. The procedure is the same, get the neighbor + // triangles and calculate an average normal. + + const Vec3d& p = C.row(i); + + // mark the vertex indices of the edge. ia and ib marks and edge ic + // will mark a single vertex. + int ia = -1, ib = -1, ic = -1; + + if(std::abs(distance(p, p1)) < eps) { + ic = trindex(0); + } + else if(std::abs(distance(p, p2)) < eps) { + ic = trindex(1); + } + else if(std::abs(distance(p, p3)) < eps) { + ic = trindex(2); + } + else if(point_on_edge(p, p1, p2, eps)) { + ia = trindex(0); ib = trindex(1); + } + else if(point_on_edge(p, p2, p3, eps)) { + ia = trindex(1); ib = trindex(2); + } + else if(point_on_edge(p, p1, p3, eps)) { + ia = trindex(0); ib = trindex(2); + } + + std::vector neigh; + if(ic >= 0) { // The point is right on a vertex of the triangle + for(int n = 0; n < mesh.F.rows(); ++n) { + throw_on_cancel(); + Vec3i ni = mesh.F.row(n); + if((ni(X) == ic || ni(Y) == ic || ni(Z) == ic)) + neigh.emplace_back(ni); + } + } + else if(ia >= 0 && ib >= 0) { // the point is on and edge + // now get all the neigboring triangles + for(int n = 0; n < mesh.F.rows(); ++n) { + throw_on_cancel(); + Vec3i ni = mesh.F.row(n); + if((ni(X) == ia || ni(Y) == ia || ni(Z) == ia) && + (ni(X) == ib || ni(Y) == ib || ni(Z) == ib)) + neigh.emplace_back(ni); + } + } + + if(!neigh.empty()) { // there were neighbors to count with + Vec3d sumnorm(0, 0, 0); + for(const Vec3i& tri : neigh) { + const Vec3d& pt1 = mesh.V.row(tri(0)); + const Vec3d& pt2 = mesh.V.row(tri(1)); + const Vec3d& pt3 = mesh.V.row(tri(2)); + Eigen::Vector3d U = pt2 - pt1; + Eigen::Vector3d V = pt3 - pt1; + sumnorm += U.cross(V).normalized(); + } + sumnorm /= neigh.size(); + ret.row(i) = sumnorm; + } + else { // point lies safely within its triangle + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + ret.row(i) = U.cross(V).normalized(); + } } return ret; -#else // TODO: do something on 32 bit windows - return {}; -#endif } double ray_mesh_intersect(const Vec3d& s, @@ -223,7 +308,7 @@ Segments model_boundary(const EigenMesh3D& emesh, double offs) pp.emplace_back(p); } - ExPolygons merged = union_ex(offset(pp, float(scale_(offs))), true); + ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true); for(auto& expoly : merged) { auto lines = expoly.lines(); From 862217a6b3739fd66df277bc08f19cf4d4dbb45f Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Fri, 14 Dec 2018 15:27:34 +0100 Subject: [PATCH 008/132] OctoPrint basics working, niceties to-do --- src/libslic3r/Channel.hpp | 52 +++--- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 31 ++-- src/slic3r/GUI/GUI_App.cpp | 3 +- src/slic3r/GUI/GUI_App.hpp | 4 +- src/slic3r/GUI/MainFrame.cpp | 8 +- src/slic3r/GUI/MainFrame.hpp | 6 + src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/PrintHostDialogs.cpp | 108 ++++++++++++- src/slic3r/GUI/PrintHostDialogs.hpp | 51 ++++-- src/slic3r/Utils/Duet.cpp | 166 ++++++++++---------- src/slic3r/Utils/Duet.hpp | 5 +- src/slic3r/Utils/Http.hpp | 2 +- src/slic3r/Utils/OctoPrint.cpp | 60 +++---- src/slic3r/Utils/OctoPrint.hpp | 5 +- src/slic3r/Utils/PrintHost.cpp | 142 ++++++++++++++--- src/slic3r/Utils/PrintHost.hpp | 15 +- 16 files changed, 450 insertions(+), 210 deletions(-) diff --git a/src/libslic3r/Channel.hpp b/src/libslic3r/Channel.hpp index 8d1a07d35..9cf025f2c 100644 --- a/src/libslic3r/Channel.hpp +++ b/src/libslic3r/Channel.hpp @@ -1,6 +1,7 @@ #ifndef slic3r_Channel_hpp_ #define slic3r_Channel_hpp_ +#include #include #include #include @@ -13,32 +14,26 @@ namespace Slic3r { template class Channel { -private: - using UniqueLock = std::unique_lock; - using Queue = std::deque; public: - class Guard + using UniqueLock = std::unique_lock; + + template class Unlocker { public: - Guard(UniqueLock lock, const Queue &queue) : m_lock(std::move(lock)), m_queue(queue) {} - Guard(const Guard &other) = delete; - Guard(Guard &&other) = delete; - ~Guard() {} + Unlocker(UniqueLock lock) : m_lock(std::move(lock)) {} + Unlocker(const Unlocker &other) noexcept : m_lock(std::move(other.m_lock)) {} // XXX: done beacuse of MSVC 2013 not supporting init of deleter by move + Unlocker(Unlocker &&other) noexcept : m_lock(std::move(other.m_lock)) {} + Unlocker& operator=(const Unlocker &other) = delete; + Unlocker& operator=(Unlocker &&other) { m_lock = std::move(other.m_lock); } - // Access trampolines - size_t size() const noexcept { return m_queue.size(); } - bool empty() const noexcept { return m_queue.empty(); } - typename Queue::const_iterator begin() const noexcept { return m_queue.begin(); } - typename Queue::const_iterator end() const noexcept { return m_queue.end(); } - typename Queue::const_reference operator[](size_t i) const { return m_queue[i]; } - - Guard& operator=(const Guard &other) = delete; - Guard& operator=(Guard &&other) = delete; + void operator()(Ptr*) { m_lock.unlock(); } private: - UniqueLock m_lock; - const Queue &m_queue; + mutable UniqueLock m_lock; // XXX: mutable: see above }; + using Queue = std::deque; + using LockedConstPtr = std::unique_ptr>; + using LockedPtr = std::unique_ptr>; Channel() {} ~Channel() {} @@ -56,7 +51,7 @@ public: { { UniqueLock lock(m_mutex); - m_queue.push_back(std::forward(item)); + m_queue.push_back(std::forward(item)); } if (! silent) { m_condition.notify_one(); } } @@ -82,19 +77,22 @@ public: } } - // Unlocked observers - // Thread unsafe! Keep in mind you need to re-verify the result after acquiring lock! - size_t size() const noexcept { return m_queue.size(); } - bool empty() const noexcept { return m_queue.empty(); } + // Unlocked observers/hints + // Thread unsafe! Keep in mind you need to re-verify the result after locking! + size_t size_hint() const noexcept { return m_queue.size(); } - Guard read() const + LockedConstPtr lock_read() const { - return Guard(UniqueLock(m_mutex), m_queue); + return LockedConstPtr(&m_queue, Unlocker(UniqueLock(m_mutex))); } + LockedPtr lock_rw() + { + return LockedPtr(&m_queue, Unlocker(UniqueLock(m_mutex))); + } private: Queue m_queue; - std::mutex m_mutex; + mutable std::mutex m_mutex; std::condition_variable m_condition; }; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 891e5f0dc..d748919c9 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -65,11 +65,6 @@ PrinterTechnology BackgroundSlicingProcess::current_printer_technology() const return m_print->technology(); } -static bool isspace(int ch) -{ - return std::isspace(ch) != 0; -} - // This function may one day be merged into the Print, but historically the print was separated // from the G-code generator. void BackgroundSlicingProcess::process_fff() @@ -88,6 +83,27 @@ void BackgroundSlicingProcess::process_fff() m_print->set_status(95, "Running post-processing scripts"); run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, "G-code file exported to " + export_path); + } else if (! m_upload_job.empty()) { + // A print host upload job has been scheduled + + // XXX: is fs::path::string() right? + + // Generate a unique temp path to which the gcode is copied + boost::filesystem::path source_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode"); + + if (copy_file(m_temp_output_path, source_path.string()) != 0) { + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + } + + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(source_path.string(), m_fff_print->config()); + m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str()); + + m_upload_job.upload_data.source_path = std::move(source_path); + m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); + + GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job)); } else { m_print->set_status(100, "Slicing complete"); } @@ -373,13 +389,10 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) if (! m_export_path.empty()) return; - const boost::filesystem::path path = boost::filesystem::temp_directory_path() - / boost::filesystem::unique_path(".upload.%%%%-%%%%-%%%%-%%%%.gcode"); - // Guard against entering the export step before changing the export path. tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); - m_export_path = path.string(); + m_export_path = std::string(); m_upload_job = std::move(upload_job); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f4047ae3e..e4db9b6e1 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -73,7 +73,6 @@ GUI_App::GUI_App() : wxApp() #if ENABLE_IMGUI , m_imgui(new ImGuiWrapper()) - , m_printhost_queue(new PrintHostJobQueue()) #endif // ENABLE_IMGUI {} @@ -142,6 +141,8 @@ bool GUI_App::OnInit() update_mode(); SetTopWindow(mainframe); + m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); + CallAfter([this]() { // temporary workaround for the correct behavior of the Scrolled sidebar panel auto& panel = sidebar(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 875a92456..3c2b4a21f 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -92,7 +92,7 @@ class GUI_App : public wxApp std::unique_ptr m_imgui; #endif // ENABLE_IMGUI - std::unique_ptr m_printhost_queue; + std::unique_ptr m_printhost_job_queue; public: bool OnInit() override; @@ -164,7 +164,7 @@ public: ImGuiWrapper* imgui() { return m_imgui.get(); } #endif // ENABLE_IMGUI - PrintHostJobQueue& printhost_queue() { return *m_printhost_queue.get(); } + PrintHostJobQueue& printhost_job_queue() { return *m_printhost_job_queue.get(); } }; DECLARE_APP(GUI_App) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index aa5634fec..4d4ee17ae 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -18,6 +18,7 @@ #include "ProgressStatusBar.hpp" #include "3DScene.hpp" #include "AppConfig.hpp" +#include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "I18N.hpp" @@ -30,7 +31,8 @@ namespace GUI { MainFrame::MainFrame(const bool no_plater, const bool loaded) : wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE), m_no_plater(no_plater), - m_loaded(loaded) + m_loaded(loaded), + m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. #if _WIN32 @@ -375,6 +377,10 @@ void MainFrame::init_menubar() append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); #endif // ENABLE_REMOVE_TABS_FROM_PLATER + + windowMenu->AppendSeparator(); + append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"), + [this](wxCommandEvent&) { m_printhost_queue_dlg->ShowModal(); }, "arrow_up.png"); } // View menu diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 2559b5ed1..fab6aea90 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -21,7 +21,9 @@ class ProgressStatusBar; namespace GUI { + class Tab; +class PrintHostQueueDialog; enum QuickSlice { @@ -52,6 +54,8 @@ class MainFrame : public wxFrame wxMenuItem* m_menu_item_repeat { nullptr }; wxMenuItem* m_menu_item_reslice_now { nullptr }; + PrintHostQueueDialog *m_printhost_queue_dlg; + std::string get_base_name(const wxString full_name) const ; std::string get_dir_name(const wxString full_name) const ; @@ -93,6 +97,8 @@ public: void select_tab(size_t tab) const; void select_view(const std::string& direction); + PrintHostQueueDialog* printhost_queue_dlg() { return m_printhost_queue_dlg; } + Plater* m_plater { nullptr }; wxNotebook* m_tabpanel { nullptr }; wxProgressDialog* m_progress_dialog { nullptr }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index aeda2e8c2..a8a75fc3f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3144,7 +3144,7 @@ void Plater::send_gcode() } default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); - Slic3r::PrintHostSendDialog dlg(default_output_file); + PrintHostSendDialog dlg(default_output_file); if (dlg.ShowModal() == wxID_OK) { upload_job.upload_data.upload_path = dlg.filename(); upload_job.upload_data.start_print = dlg.start_print(); diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index a5de7c3c6..8ac8615a8 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -1,20 +1,28 @@ #include "PrintHostDialogs.hpp" +#include + #include -#include #include #include #include #include #include +#include +#include +#include +#include -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" -#include "slic3r/GUI/I18N.hpp" +#include "GUI.hpp" +#include "MsgDialog.hpp" +#include "I18N.hpp" +#include "../Utils/PrintHost.hpp" namespace fs = boost::filesystem; namespace Slic3r { +namespace GUI { + PrintHostSendDialog::PrintHostSendDialog(const fs::path &path) : MsgDialog(nullptr, _(L("Send G-Code to printer host")), _(L("Upload to Printer Host with the following filename:")), wxID_NONE) @@ -45,5 +53,95 @@ fs::path PrintHostSendDialog::filename() const bool PrintHostSendDialog::start_print() const { - return box_print->GetValue(); } + return box_print->GetValue(); } + + + +wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id) + : wxEvent(winid, eventType) + , job_id(job_id) +{} + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, int progress) + : wxEvent(winid, eventType) + , job_id(job_id) + , progress(progress) +{} + +PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id, wxString error) + : wxEvent(winid, eventType) + , job_id(job_id) + , error(std::move(error)) +{} + +wxEvent *PrintHostQueueDialog::Event::Clone() const +{ + return new Event(*this); +} + +PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) + : wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) + , on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this) + , on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this) +{ + enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 }; + + SetMinSize(wxSize(HEIGHT, WIDTH)); + + auto *topsizer = new wxBoxSizer(wxVERTICAL); + + job_list = new wxDataViewListCtrl(this, wxID_ANY); + job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT); + job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT); + + auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); + auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); + auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); + btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING); + btnsizer->AddStretchSpacer(); + btnsizer->Add(btn_close); + + topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING); + topsizer->Add(btnsizer, 0, wxEXPAND); + SetSizer(topsizer); +} + +void PrintHostQueueDialog::append_job(const PrintHostJob &job) +{ + wxCHECK_RET(!job.empty(), "PrintHostQueueDialog: Attempt to append an empty job"); + + wxVector fields; + fields.push_back(wxVariant(wxString::Format("%d", job_list->GetItemCount() + 1))); + fields.push_back(wxVariant(0)); + fields.push_back(wxVariant(_(L("Enqueued")))); + fields.push_back(wxVariant(job.printhost->get_host())); + fields.push_back(wxVariant(job.upload_data.upload_path.string())); + job_list->AppendItem(fields); +} + +void PrintHostQueueDialog::on_progress(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete"))); + + job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1); + job_list->SetValue(status, evt.job_id, 2); +} + +void PrintHostQueueDialog::on_error(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + // TODO +} + + +}} diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index d27fbe576..e38acee32 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -2,24 +2,27 @@ #define slic3r_PrintHostSendDialog_hpp_ #include - #include #include -#include #include -#include -#include -#include -#include -#include +#include -#include "slic3r/GUI/GUI.hpp" -#include "slic3r/GUI/MsgDialog.hpp" +#include "GUI.hpp" +#include "GUI_Utils.hpp" +#include "MsgDialog.hpp" +#include "../Utils/PrintHost.hpp" +class wxTextCtrl; +class wxCheckBox; +class wxDataViewListCtrl; namespace Slic3r { +struct PrintHostJob; + +namespace GUI { + class PrintHostSendDialog : public GUI::MsgDialog { @@ -38,12 +41,38 @@ private: class PrintHostQueueDialog : public wxDialog { public: - PrintHostQueueDialog(); + class Event : public wxEvent + { + public: + size_t job_id; + int progress = 0; // in percent + wxString error; + Event(wxEventType eventType, int winid, size_t job_id); + Event(wxEventType eventType, int winid, size_t job_id, int progress); + Event(wxEventType eventType, int winid, size_t job_id, wxString error); + + virtual wxEvent *Clone() const; + }; + + + PrintHostQueueDialog(wxWindow *parent); + + void append_job(const PrintHostJob &job); private: + wxDataViewListCtrl *job_list; + // Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog + EventGuard on_progress_evt; + EventGuard on_error_evt; + + void on_progress(Event&); + void on_error(Event&); }; +wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); +wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); -} + +}} #endif diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 4eda7bd46..1772ae8ef 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -20,7 +20,6 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/MsgDialog.hpp" -#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX #include "Http.hpp" namespace fs = boost::filesystem; @@ -55,89 +54,90 @@ wxString Duet::get_test_failed_msg (wxString &msg) const return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg); } -bool Duet::send_gcode(const std::string &filename) const -{ - enum { PROGRESS_RANGE = 1000 }; - - const auto errortitle = _(L("Error while uploading to the Duet")); - fs::path filepath(filename); - - PrintHostSendDialog send_dialog(filepath.filename()); - if (send_dialog.ShowModal() != wxID_OK) { return false; } - - const bool print = send_dialog.start_print(); - const auto upload_filepath = send_dialog.filename(); - const auto upload_filename = upload_filepath.filename(); - const auto upload_parent_path = upload_filepath.parent_path(); - - wxProgressDialog progress_dialog( - _(L("Duet upload")), - _(L("Sending G-code file to Duet...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); - - wxString connect_msg; - if (!connect(connect_msg)) { - auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg); - GUI::show_error(&progress_dialog, std::move(errormsg)); - return false; - } - - bool res = true; - - auto upload_cmd = get_upload_url(upload_filepath.string()); - BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") - % filepath.string() - % upload_filename.string() - % upload_parent_path.string() - % print - % upload_cmd; - - auto http = Http::post(std::move(upload_cmd)); - http.set_post_body(filename) - .on_complete([&](std::string body, unsigned status) { - BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; - progress_dialog.Update(PROGRESS_RANGE); - - int err_code = get_err_code_from_body(body); - if (err_code != 0) { - auto msg = format_error(body, L("Unknown error occured"), 0); - GUI::show_error(&progress_dialog, std::move(msg)); - res = false; - } else if (print) { - wxString errormsg; - res = start_print(errormsg, upload_filepath.string()); - if (!res) { - GUI::show_error(&progress_dialog, std::move(errormsg)); - } - } - }) - .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); - GUI::show_error(&progress_dialog, std::move(errormsg)); - res = false; - }) - .on_progress([&](Http::Progress progress, bool &cancel) { - if (cancel) { - // Upload was canceled - res = false; - } else if (progress.ultotal > 0) { - int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; - cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing - } else { - cancel = !progress_dialog.Pulse(); - } - }) - .perform_sync(); - - disconnect(); - - return res; -} - -bool Duet::upload(PrintHostUpload upload_data) const +// bool Duet::send_gcode(const std::string &filename) const +// { +// enum { PROGRESS_RANGE = 1000 }; + +// const auto errortitle = _(L("Error while uploading to the Duet")); +// fs::path filepath(filename); + +// GUI::PrintHostSendDialog send_dialog(filepath.filename()); +// if (send_dialog.ShowModal() != wxID_OK) { return false; } + +// const bool print = send_dialog.start_print(); +// const auto upload_filepath = send_dialog.filename(); +// const auto upload_filename = upload_filepath.filename(); +// const auto upload_parent_path = upload_filepath.parent_path(); + +// wxProgressDialog progress_dialog( +// _(L("Duet upload")), +// _(L("Sending G-code file to Duet...")), +// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); +// progress_dialog.Pulse(); + +// wxString connect_msg; +// if (!connect(connect_msg)) { +// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg); +// GUI::show_error(&progress_dialog, std::move(errormsg)); +// return false; +// } + +// bool res = true; + +// auto upload_cmd = get_upload_url(upload_filepath.string()); +// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") +// % filepath.string() +// % upload_filename.string() +// % upload_parent_path.string() +// % print +// % upload_cmd; + +// auto http = Http::post(std::move(upload_cmd)); +// http.set_post_body(filename) +// .on_complete([&](std::string body, unsigned status) { +// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; +// progress_dialog.Update(PROGRESS_RANGE); + +// int err_code = get_err_code_from_body(body); +// if (err_code != 0) { +// auto msg = format_error(body, L("Unknown error occured"), 0); +// GUI::show_error(&progress_dialog, std::move(msg)); +// res = false; +// } else if (print) { +// wxString errormsg; +// res = start_print(errormsg, upload_filepath.string()); +// if (!res) { +// GUI::show_error(&progress_dialog, std::move(errormsg)); +// } +// } +// }) +// .on_error([&](std::string body, std::string error, unsigned status) { +// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; +// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); +// GUI::show_error(&progress_dialog, std::move(errormsg)); +// res = false; +// }) +// .on_progress([&](Http::Progress progress, bool &cancel) { +// if (cancel) { +// // Upload was canceled +// res = false; +// } else if (progress.ultotal > 0) { +// int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; +// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing +// } else { +// cancel = !progress_dialog.Pulse(); +// } +// }) +// .perform_sync(); + +// disconnect(); + +// return res; +// } + +bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const { + // XXX: TODO throw "unimplemented"; } diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index db21fd0a1..0608f85a5 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -22,11 +22,10 @@ public: bool test(wxString &curl_msg) const; wxString get_test_ok_msg () const; wxString get_test_failed_msg (wxString &msg) const; - // Send gcode file to duet, filename is expected to be in UTF-8 - bool send_gcode(const std::string &filename) const; - bool upload(PrintHostUpload upload_data) const; + bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; bool has_auto_discovery() const; bool can_test() const; + virtual std::string get_host() const { return host; } private: std::string host; std::string password; diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index 44580b7ea..fd3f8830d 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -29,7 +29,7 @@ public: typedef std::shared_ptr Ptr; typedef std::function CompleteFn; - + // A HTTP request may fail at various stages of completeness (URL parsing, DNS lookup, TCP connection, ...). // If the HTTP request could not be made or failed before completion, the `error` arg contains a description // of the error and `http_status` is zero. diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index b2e2d4d45..67c58a972 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -4,9 +4,10 @@ #include #include +#include + #include "libslic3r/PrintConfig.hpp" #include "slic3r/GUI/I18N.hpp" -#include "slic3r/GUI/PrintHostDialogs.hpp" // XXX #include "Http.hpp" @@ -59,32 +60,19 @@ wxString OctoPrint::get_test_failed_msg (wxString &msg) const _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); } -bool OctoPrint::send_gcode(const std::string &filename) const +bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const { - enum { PROGRESS_RANGE = 1000 }; - - const auto errortitle = _(L("Error while uploading to the OctoPrint server")); - fs::path filepath(filename); - - PrintHostSendDialog send_dialog(filepath.filename()); - if (send_dialog.ShowModal() != wxID_OK) { return false; } - - const bool print = send_dialog.start_print(); - const auto upload_filepath = send_dialog.filename(); - const auto upload_filename = upload_filepath.filename(); - const auto upload_parent_path = upload_filepath.parent_path(); - - wxProgressDialog progress_dialog( - _(L("OctoPrint upload")), - _(L("Sending G-code file to the OctoPrint server...")), - PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); - progress_dialog.Pulse(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); wxString test_msg; - if (!test(test_msg)) { - auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); - GUI::show_error(&progress_dialog, std::move(errormsg)); - return false; + if (! test(test_msg)) { + + // TODO: + + // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); + // GUI::show_error(&progress_dialog, std::move(errormsg)); + // return false; } bool res = true; @@ -92,36 +80,31 @@ bool OctoPrint::send_gcode(const std::string &filename) const auto url = make_url("api/files/local"); BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") - % filepath.string() + % upload_data.source_path.string() % url % upload_filename.string() % upload_parent_path.string() - % print; + % upload_data.start_print; auto http = Http::post(std::move(url)); set_auth(http); - http.form_add("print", print ? "true" : "false") + http.form_add("print", upload_data.start_print ? "true" : "false") .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? - .form_add_file("file", filename, upload_filename.string()) + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) .on_complete([&](std::string body, unsigned status) { BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; - progress_dialog.Update(PROGRESS_RANGE); }) .on_error([&](std::string body, std::string error, unsigned status) { BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); - GUI::show_error(&progress_dialog, std::move(errormsg)); + error_fn(std::move(body), std::move(error), status); res = false; }) .on_progress([&](Http::Progress progress, bool &cancel) { + prorgess_fn(std::move(progress), cancel); if (cancel) { // Upload was canceled + BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; res = false; - } else if (progress.ultotal > 0) { - int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; - cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing - } else { - cancel = !progress_dialog.Pulse(); } }) .perform_sync(); @@ -129,11 +112,6 @@ bool OctoPrint::send_gcode(const std::string &filename) const return res; } -bool OctoPrint::upload(PrintHostUpload upload_data) const -{ - throw "unimplemented"; -} - bool OctoPrint::has_auto_discovery() const { return true; diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 314e4cfae..9267b4c83 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -22,11 +22,10 @@ public: bool test(wxString &curl_msg) const; wxString get_test_ok_msg () const; wxString get_test_failed_msg (wxString &msg) const; - // Send gcode file to octoprint, filename is expected to be in UTF-8 - bool send_gcode(const std::string &filename) const; - bool upload(PrintHostUpload upload_data) const; + bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; bool has_auto_discovery() const; bool can_test() const; + virtual std::string get_host() const { return host; } private: std::string host; std::string apikey; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 570d72f68..48f504884 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -1,15 +1,21 @@ -#include "OctoPrint.hpp" -#include "Duet.hpp" +#include "PrintHost.hpp" #include #include #include +#include + +#include #include "libslic3r/PrintConfig.hpp" #include "libslic3r/Channel.hpp" +#include "OctoPrint.hpp" +#include "Duet.hpp" +#include "../GUI/PrintHostDialogs.hpp" +namespace fs = boost::filesystem; using boost::optional; - +using Slic3r::GUI::PrintHostQueueDialog; namespace Slic3r { @@ -30,30 +36,130 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) struct PrintHostJobQueue::priv { - std::vector jobs; - Channel channel; + // XXX: comment on how bg thread works + + PrintHostJobQueue *q; + + Channel channel_jobs; + Channel channel_cancels; + size_t job_id = 0; + int prev_progress = -1; std::thread bg_thread; - optional bg_job; + bool bg_exit = false; + + PrintHostQueueDialog *queue_dialog; + + priv(PrintHostJobQueue *q) : q(q) {} + + void start_bg_thread(); + void bg_thread_main(); + void progress_fn(Http::Progress progress, bool &cancel); + void error_fn(std::string body, std::string error, unsigned http_status); + void perform_job(PrintHostJob the_job); }; -PrintHostJobQueue::PrintHostJobQueue() - : p(new priv()) +PrintHostJobQueue::PrintHostJobQueue(PrintHostQueueDialog *queue_dialog) + : p(new priv(this)) { - std::shared_ptr p2 = p; - p->bg_thread = std::thread([p2]() { - // Wait for commands on the channel: - auto cmd = p2->channel.pop(); - // TODO - }); + p->queue_dialog = queue_dialog; } PrintHostJobQueue::~PrintHostJobQueue() { - // TODO: stop the thread - // if (p && p->bg_thread.joinable()) { - // p->bg_thread.detach(); - // } + if (p && p->bg_thread.joinable()) { + p->bg_exit = true; + p->channel_jobs.push(PrintHostJob()); // Push an empty job to wake up bg_thread in case it's sleeping + p->bg_thread.detach(); // Let the background thread go, it should exit on its own + } +} + +void PrintHostJobQueue::priv::start_bg_thread() +{ + if (bg_thread.joinable()) { return; } + + std::shared_ptr p2 = q->p; + bg_thread = std::thread([p2]() { + p2->bg_thread_main(); + }); +} + +void PrintHostJobQueue::priv::bg_thread_main() +{ + // bg thread entry point + + try { + // Pick up jobs from the job channel: + while (! bg_exit) { + auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs + if (! job.cancelled) { + perform_job(std::move(job)); + } + job_id++; + } + } catch (...) { + wxTheApp->OnUnhandledException(); + } +} + +void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) +{ + if (bg_exit) { + cancel = true; + return; + } + + if (channel_cancels.size_hint() > 0) { + // Lock both queues + auto cancels = channel_cancels.lock_rw(); + auto jobs = channel_jobs.lock_rw(); + + for (size_t cancel_id : *cancels) { + if (cancel_id == job_id) { + cancel = true; + } else if (cancel_id > job_id) { + jobs->at(cancel_id - job_id).cancelled = true; + } + } + + cancels->clear(); + } + + int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0; + if (gui_progress != prev_progress) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress); + wxQueueEvent(queue_dialog, evt); + prev_progress = gui_progress; + } +} + +void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status) +{ + // TODO +} + +void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) +{ + if (bg_exit || the_job.empty()) { return; } + + const fs::path gcode_path = the_job.upload_data.source_path; + + the_job.printhost->upload(std::move(the_job.upload_data), + [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, + [this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); } + ); + + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); + wxQueueEvent(queue_dialog, evt); + + fs::remove(gcode_path); // XXX: error handling +} + +void PrintHostJobQueue::enqueue(PrintHostJob job) +{ + p->start_bg_thread(); + p->queue_dialog->append_job(job); + p->channel_jobs.push(std::move(job)); } diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 53f7c43d3..52ef38058 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -7,6 +7,8 @@ #include +#include "Http.hpp" + namespace Slic3r { @@ -29,11 +31,10 @@ public: virtual bool test(wxString &curl_msg) const = 0; virtual wxString get_test_ok_msg () const = 0; virtual wxString get_test_failed_msg (wxString &msg) const = 0; - // Send gcode file to print host, filename is expected to be in UTF-8 - virtual bool send_gcode(const std::string &filename) const = 0; // XXX: remove in favor of upload() - virtual bool upload(PrintHostUpload upload_data) const = 0; + virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0; virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; + virtual std::string get_host() const = 0; static PrintHost* get_print_host(DynamicPrintConfig *config); }; @@ -43,6 +44,7 @@ struct PrintHostJob { PrintHostUpload upload_data; std::unique_ptr printhost; + bool cancelled = false; PrintHostJob() {} PrintHostJob(const PrintHostJob&) = delete; @@ -68,10 +70,12 @@ struct PrintHostJob }; +namespace GUI { class PrintHostQueueDialog; } + class PrintHostJobQueue { public: - PrintHostJobQueue(); + PrintHostJobQueue(GUI::PrintHostQueueDialog *queue_dialog); PrintHostJobQueue(const PrintHostJobQueue &) = delete; PrintHostJobQueue(PrintHostJobQueue &&other) = delete; ~PrintHostJobQueue(); @@ -79,6 +83,9 @@ public: PrintHostJobQueue& operator=(const PrintHostJobQueue &) = delete; PrintHostJobQueue& operator=(PrintHostJobQueue &&other) = delete; + void enqueue(PrintHostJob job); + void cancel(size_t id); + private: struct priv; std::shared_ptr p; From 6411ab5b63f3ba88a5d59f8d792b4565cfe5b602 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 18:04:23 +0100 Subject: [PATCH 009/132] Fix of SPE-607 Part changed to Support enforcer is not sliced with one another part --- src/libslic3r/Print.cpp | 17 ++++++++++------- src/libslic3r/Print.hpp | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index e3382573c..df50012b9 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1052,10 +1052,12 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co goto print_object_end; } else { this_region_config = region_config_from_model_volume(m_default_region_config, volume, num_extruders); - for (size_t i = 0; i < region_id; ++ i) - if (m_regions[i]->config().equals(this_region_config)) - // Regions were merged. Reset this print_object. - goto print_object_end; + for (size_t i = 0; i < region_id; ++i) { + const PrintRegion ®ion_other = *m_regions[i]; + if (region_other.m_refcnt != 0 && region_other.config().equals(this_region_config)) + // Regions were merged. Reset this print_object. + goto print_object_end; + } this_region_config_set = true; } } @@ -1102,9 +1104,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Find an existing print region with the same config. int idx_empty_slot = -1; for (int i = 0; i < (int)m_regions.size(); ++ i) { - if (m_regions[i]->m_refcnt == 0) - idx_empty_slot = i; - else if (config.equals(m_regions[i]->config())) { + if (m_regions[i]->m_refcnt == 0) { + if (idx_empty_slot == -1) + idx_empty_slot = i; + } else if (config.equals(m_regions[i]->config())) { region_id = i; break; } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 1b79ef295..ad32f42ba 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -107,8 +107,8 @@ public: // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id) { if (region_id >= region_volumes.size()) - region_volumes.resize(region_id + 1); - region_volumes[region_id].push_back(volume_id); + region_volumes.assign(region_id + 1, std::vector()); + region_volumes[region_id].emplace_back(volume_id); } // This is the *total* layer count (including support layers) // this value is not supposed to be compared with Layer::id From 01edb23ffc6a526d59f011ebf1e29a4ddd85c185 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Dec 2018 15:44:30 +0100 Subject: [PATCH 010/132] Fixed the enabling of the "split to objects/parts" buttons --- src/slic3r/GUI/Plater.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a8a75fc3f..6544da789 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1033,6 +1033,7 @@ private: bool can_decrease_instances() const; bool can_split_to_objects() const; bool can_split_to_volumes() const; + bool can_split() const; bool layers_height_allowed() const; bool can_delete_all() const; bool can_arrange() const; @@ -1652,8 +1653,8 @@ void Plater::priv::selection_changed() view3D->enable_toolbar_item("delete", can_delete_object()); view3D->enable_toolbar_item("more", can_increase_instances()); view3D->enable_toolbar_item("fewer", can_decrease_instances()); - view3D->enable_toolbar_item("splitobjects", can_split_to_objects()); - view3D->enable_toolbar_item("splitvolumes", can_split_to_volumes()); + view3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); + view3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); view3D->enable_toolbar_item("layersediting", layers_height_allowed()); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); @@ -1661,8 +1662,8 @@ void Plater::priv::selection_changed() this->canvas3D->enable_toolbar_item("delete", can_delete_object()); this->canvas3D->enable_toolbar_item("more", can_increase_instances()); this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances()); - this->canvas3D->enable_toolbar_item("splitobjects", can_split_to_objects()); - this->canvas3D->enable_toolbar_item("splitvolumes", can_split_to_volumes()); + this->canvas3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); + this->canvas3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed()); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) this->canvas3D->render(); @@ -2454,8 +2455,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&) void Plater::priv::on_object_select(SimpleEvent& evt) { - selection_changed(); wxGetApp().obj_list()->update_selections(); + selection_changed(); } void Plater::priv::on_viewport_changed(SimpleEvent& evt) @@ -2605,9 +2606,9 @@ bool Plater::priv::complit_init_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects() || can_split_to_volumes()); }, item_split->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split_objects->GetId()); - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split_volumes->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects() || can_split_to_volumes*/()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split_objects->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split_volumes->GetId()); } return true; } @@ -2626,7 +2627,7 @@ bool Plater::priv::complit_init_sla_object_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_objects()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_objects*/()); }, item_split->GetId()); } return true; @@ -2645,7 +2646,7 @@ bool Plater::priv::complit_init_part_menu() // ui updates needs to be binded to the parent panel if (q != nullptr) { - q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split_to_volumes()); }, item_split->GetId()); + q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_split/*_to_volumes*/()); }, item_split->GetId()); } return true; @@ -2763,6 +2764,13 @@ bool Plater::priv::can_split_to_volumes() const return sidebar->obj_list()->is_splittable(); } +bool Plater::priv::can_split() const +{ + if (printer_technology == ptSLA) + return false; + return sidebar->obj_list()->is_splittable(); +} + bool Plater::priv::layers_height_allowed() const { int obj_idx = get_selected_object_idx(); From 17164ee33312cd01f374df004b3bb5f23e184c55 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 17 Dec 2018 18:07:31 +0100 Subject: [PATCH 011/132] Fixed #1225 (Added "Parameter validation" for "mm or %" values) + fixed correct updating of the "Contact Z distance" parameter --- src/slic3r/GUI/Field.cpp | 43 +++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index d4126d933..fc428ac0d 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -163,9 +163,31 @@ void Field::get_value_by_opt_type(wxString& str) break; } case coString: case coStrings: - case coFloatOrPercent: - m_value = str.ToStdString(); - break; + case coFloatOrPercent: { + if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') + { + double val; + if (!str.ToCDouble(&val)) + { + show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits"))); + set_value(double_to_string(val), true); + } + else if (val > 1) + { + const int nVal = int(val); + wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %dmm?\n" + "Select YES if you want to change this value to %d%%, \n" + "or NO if you are sure that %dmm is a correct value.")), nVal, nVal, nVal, nVal); + auto dialog = new wxMessageDialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO); + if (dialog->ShowModal() == wxID_YES) { + set_value(wxString::Format("%s%%", str), true); + str += "%%"; + } + } + } + + m_value = str.ToStdString(); + break; } default: break; } @@ -611,9 +633,7 @@ boost::any& Choice::get_value() if (m_opt_id == rp_option) return m_value = boost::any(ret_str); - if (m_opt.type != coEnum) - /*m_value = */get_value_by_opt_type(ret_str); - else + if (m_opt.type == coEnum) { int ret_enum = static_cast(window)->GetSelection(); if (m_opt_id.compare("external_fill_pattern") == 0) @@ -640,7 +660,16 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id.compare("display_orientation") == 0) m_value = static_cast(ret_enum); - } + } + else if (m_opt.gui_type == "f_enum_open") { + const int ret_enum = static_cast(window)->GetSelection(); + if (ret_enum < 0 || m_opt.enum_values.empty()) + get_value_by_opt_type(ret_str); + else + m_value = m_opt.enum_values[ret_enum]; + } + else + get_value_by_opt_type(ret_str); return m_value; } From 2c63af5dd9e21dd6465bffa0475929697809f6e1 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 17 Dec 2018 19:46:36 +0100 Subject: [PATCH 012/132] Fix of SPE-607 --- src/libslic3r/Print.cpp | 6 ++++-- src/libslic3r/Print.hpp | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index df50012b9..0a1bac4e4 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1095,8 +1095,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co bool fresh = print_object.region_volumes.empty(); unsigned int volume_id = 0; for (const ModelVolume *volume : model_object.volumes) { - if (! volume->is_model_part() && ! volume->is_modifier()) - continue; + if (! volume->is_model_part() && ! volume->is_modifier()) { + ++ volume_id; + continue; + } int region_id = -1; if (&print_object == &print_object0) { // Get the config applied to this volume. diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index ad32f42ba..e5060cb76 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -107,7 +107,7 @@ public: // adds region_id, too, if necessary void add_region_volume(unsigned int region_id, int volume_id) { if (region_id >= region_volumes.size()) - region_volumes.assign(region_id + 1, std::vector()); + region_volumes.resize(region_id + 1); region_volumes[region_id].emplace_back(volume_id); } // This is the *total* layer count (including support layers) From 0eca8f14cc68e596e597539d78a3ee8035a3edcd Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 09:08:32 +0100 Subject: [PATCH 013/132] Fixed update of gcode preview view type when changing printer --- src/slic3r/GUI/GUI_Preview.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 4e14aefe8..71c92f2d1 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -385,17 +385,13 @@ void Preview::set_number_extruders(unsigned int number_extruders) if (m_number_extruders != number_extruders) { m_number_extruders = number_extruders; - int type = 0; // color by a feature type - if (number_extruders > 1) - { - int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); - int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type - m_choice_view_type->SetSelection(type); - if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) - m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; + int tool_idx = m_choice_view_type->FindString(_(L("Tool"))); + int type = (number_extruders > 1) ? tool_idx /* color by a tool number */ : 0; // color by a feature type + m_choice_view_type->SetSelection(type); + if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) + m_gcode_preview_data->extrusion.view_type = (GCodePreviewData::Extrusion::EViewType)type; - m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; - } + m_preferred_color_mode = (type == tool_idx) ? "tool_or_feature" : "feature"; } } From a326ce06b1cdbe3abe122bdf022492fe10bc1dbe Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 09:34:41 +0100 Subject: [PATCH 014/132] Removed unneeded methods from ObjectManipulation --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 89 ----------------------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 6 -- 2 files changed, 95 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index d193a11a9..30e701907 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -25,18 +25,6 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : 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") { - const wxString& selection = boost::any_cast(value); - for (auto axis : axes) { - std::string key = "scale" + axis; - m_og->set_side_text(key, selection); - } - - m_is_percent_scale = selection == _("%"); - update_scale_values(); - return; - } - std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); @@ -318,59 +306,6 @@ void ObjectManipulation::reset_scale_value() m_og->set_value("scale_z", def_100); } -void ObjectManipulation::update_values() -{ - int selection = ol_selection(); - if (selection < 0 || wxGetApp().mainframe->m_plater->model().objects.size() <= selection) { - m_og->set_value("position_x", def_0); - m_og->set_value("position_y", def_0); - m_og->set_value("position_z", def_0); - m_og->set_value("scale_x" , def_0); - m_og->set_value("scale_y" , def_0); - m_og->set_value("scale_z" , def_0); - m_og->set_value("rotation_x", def_0); - m_og->set_value("rotation_y", def_0); - m_og->set_value("rotation_z", def_0); - m_og->disable(); - return; - } - m_is_percent_scale = boost::any_cast(m_og->get_value("scale_unit")) == _("%"); - - update_position_values(); - update_scale_values(); - update_rotation_values(); - m_og->enable(); -} - -void ObjectManipulation::update_scale_values() -{ - int selection = ol_selection(); - ModelObjectPtrs& objects = wxGetApp().mainframe->m_plater->model().objects; - - auto instance = objects[selection]->instances.front(); - auto size = objects[selection]->instance_bounding_box(0).size(); - - if (m_is_percent_scale) { - m_og->set_value("scale_x", double_to_string(instance->get_scaling_factor(X) * 100, 2)); - m_og->set_value("scale_y", double_to_string(instance->get_scaling_factor(Y) * 100, 2)); - m_og->set_value("scale_z", double_to_string(instance->get_scaling_factor(Z) * 100, 2)); - } - else { - m_og->set_value("scale_x", double_to_string(size(0), 2)); - m_og->set_value("scale_y", double_to_string(size(1), 2)); - m_og->set_value("scale_z", double_to_string(size(2), 2)); - } -} - -void ObjectManipulation::update_position_values() -{ - auto instance = wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front(); - - m_og->set_value("position_x", double_to_string(instance->get_offset(X), 2)); - m_og->set_value("position_y", double_to_string(instance->get_offset(Y), 2)); - m_og->set_value("position_z", double_to_string(instance->get_offset(Z), 2)); -} - void ObjectManipulation::update_position_value(const Vec3d& position) { m_og->set_value("position_x", double_to_string(position(0), 2)); @@ -397,29 +332,6 @@ void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) m_og->set_value("scale_z", double_to_string(scale(2), 2)); } -void ObjectManipulation::update_rotation_values() -{ - update_rotation_value(wxGetApp().mainframe->m_plater->model().objects[ol_selection()]->instances.front()->get_rotation()); -} - -void ObjectManipulation::update_rotation_value(double angle, Axis axis) -{ - std::string axis_str; - switch (axis) { - case X: { - axis_str = "rotation_x"; - break; } - case Y: { - axis_str = "rotation_y"; - break; } - case Z: { - axis_str = "rotation_z"; - break; } - } - - m_og->set_value(axis_str, round_nearest(int(Geometry::rad2deg(angle)), 0)); -} - void ObjectManipulation::update_rotation_value(const Vec3d& rotation) { m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); @@ -427,7 +339,6 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); } - void ObjectManipulation::change_position_value(const Vec3d& position) { Vec3d displacement(position - cache_position); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 3a8df4111..cbc22a2dd 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -37,17 +37,11 @@ public: void reset_rotation_value(); void reset_scale_value(); - void update_values(); // update position values displacements or "gizmos" - void update_position_values(); void update_position_value(const Vec3d& position); // update scale values after scale unit changing or "gizmos" - void update_scale_values(); void update_scale_value(const Vec3d& scaling_factor); - // update rotation values object selection changing - void update_rotation_values(); // update rotation value after "gizmos" - void update_rotation_value(double angle, Axis axis); void update_rotation_value(const Vec3d& rotation); void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; } From 7cd612fc175655ea714c78065de07cfa4e74f889 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 09:57:19 +0100 Subject: [PATCH 015/132] Fix of SPE-694 Slicer do not generate infill after Contact Z distance settings change --- src/libslic3r/PrintObject.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index fdf950ee5..efde5392d 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -557,15 +557,15 @@ bool PrintObject::invalidate_step(PrintObjectStep step) // propagate to dependent steps if (step == posPerimeters) { - invalidated |= this->invalidate_step(posPrepareInfill); + invalidated |= this->invalidate_steps({ posPrepareInfill, posInfill }); invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posPrepareInfill) { invalidated |= this->invalidate_step(posInfill); } else if (step == posInfill) { invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSlice) { - invalidated |= this->invalidate_steps({ posPerimeters, posSupportMaterial }); - invalidated |= m_print->invalidate_step(psWipeTower); + invalidated |= this->invalidate_steps({ posPerimeters, posPrepareInfill, posInfill, posSupportMaterial }); + invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); } else if (step == posSupportMaterial) invalidated |= m_print->invalidate_steps({ psSkirt, psBrim }); From 8854276965eb8757d659bc4a26c2fedb1a752de1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 10:10:14 +0100 Subject: [PATCH 016/132] Added size fields to sidebar matrix manipulators --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 142 +++++++++++++--------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 19 ++- 2 files changed, 93 insertions(+), 68 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 30e701907..9bc316deb 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -39,24 +39,43 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_rotation_value(new_value); else if (param == "scale") change_scale_value(new_value); + else if (param == "size") + change_size_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)); + + double value = 0.0; + 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; + value = cache_position(axis); + } + else if (param == "rotation") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = cache_rotation(axis); + } + else if (param == "scale") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = cache_scale(axis); + } + else if (param == "size") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = cache_size(axis); } - m_og->set_value(opt_key, double_to_string(0.0)); + m_og->set_value(opt_key, double_to_string(value)); }; m_og->m_set_focus = [this](const std::string& opt_key) @@ -94,51 +113,28 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : auto add_og_to_object_settings = [](const std::string& option_name, const std::string& sidetext) { Line line = { _(option_name), "" }; - if (option_name == "Scale") { - line.near_label_widget = [](wxWindow* parent) { - auto btn = new PrusaLockButton(parent, wxID_ANY); - btn->Bind(wxEVT_BUTTON, [btn](wxCommandEvent &event) { - event.Skip(); - wxTheApp->CallAfter([btn]() { - wxGetApp().obj_manipul()->set_uniform_scaling(btn->IsLocked()); - }); - }); - return btn; - }; - } - ConfigOptionDef def; def.type = coFloat; def.default_value = new ConfigOptionFloat(0.0); def.width = 50; if (option_name == "Rotation") + { def.min = -360; + def.max = 360; + } const std::string lower_name = boost::algorithm::to_lower_copy(option_name); std::vector axes{ "x", "y", "z" }; for (auto axis : axes) { - if (axis == "z" && option_name != "Scale") + if (axis == "z") def.sidetext = sidetext; Option option = Option(def, lower_name + "_" + axis); option.opt.full_width = true; line.append_option(option); } - if (option_name == "Scale") - { - def.width = 45; - def.type = coStrings; - def.gui_type = "select_open"; - def.enum_labels.push_back(L("%")); - def.enum_labels.push_back(L("mm")); - def.default_value = new ConfigOptionStrings{ "mm" }; - - const Option option = Option(def, lower_name + "_unit"); - line.append_option(option); - } - return line; }; @@ -146,7 +142,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Settings table m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); m_og->append_line(add_og_to_object_settings(L("Rotation"), "°")); - m_og->append_line(add_og_to_object_settings(L("Scale"), "mm")); + m_og->append_line(add_og_to_object_settings(L("Scale"), "%")); + m_og->append_line(add_og_to_object_settings(L("Size"), "mm")); /* Unused parameter at this time def.label = L("Place on bed"); @@ -220,6 +217,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele update_position_value(volume->get_instance_offset()); update_rotation_value(volume->get_instance_rotation()); update_scale_value(volume->get_instance_scaling_factor()); + update_size_value(volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size()); #else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); @@ -250,6 +248,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele update_position_value(volume->get_volume_offset()); update_rotation_value(volume->get_volume_rotation()); update_scale_value(volume->get_volume_scaling_factor()); + update_size_value(volume->bounding_box.size()); #else update_position_value(volume->get_offset()); update_rotation_value(volume->get_rotation()); @@ -261,13 +260,13 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele { reset_settings_value(); move_label = _(L("Displacement")); + update_size_value(selection.get_bounding_box().size()); m_og->enable(); } else reset_settings_value(); m_move_Label->SetLabel(move_label); - m_og->get_field("scale_unit")->disable();// temporary decision } void ObjectManipulation::reset_settings_value() @@ -287,7 +286,7 @@ void ObjectManipulation::reset_position_value() m_og->set_value("position_y", def_0); m_og->set_value("position_z", def_0); - cache_position = { 0., 0., 0. }; + cache_position = Vec3d::Zero(); } void ObjectManipulation::reset_rotation_value() @@ -295,15 +294,27 @@ void ObjectManipulation::reset_rotation_value() m_og->set_value("rotation_x", def_0); m_og->set_value("rotation_y", def_0); m_og->set_value("rotation_z", def_0); + + cache_rotation = Vec3d::Zero(); } void ObjectManipulation::reset_scale_value() { - m_is_percent_scale = true; m_og->set_value("scale_unit", _("%")); m_og->set_value("scale_x", def_100); m_og->set_value("scale_y", def_100); m_og->set_value("scale_z", def_100); + + cache_scale = Vec3d(100.0, 100.0, 100.0); +} + +void ObjectManipulation::reset_size_value() +{ + m_og->set_value("size_x", def_0); + m_og->set_value("size_y", def_0); + m_og->set_value("size_z", def_0); + + cache_size = Vec3d::Zero(); } void ObjectManipulation::update_position_value(const Vec3d& position) @@ -317,19 +328,21 @@ void ObjectManipulation::update_position_value(const Vec3d& position) void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) { - // this is temporary - // to be able to update the values as size - // we need to store somewhere the original size - // or have it passed as parameter - if (!m_is_percent_scale) { - m_is_percent_scale = true; - m_og->set_value("scale_unit", _("%")); - } - auto scale = scaling_factor * 100.0; m_og->set_value("scale_x", double_to_string(scale(0), 2)); m_og->set_value("scale_y", double_to_string(scale(1), 2)); m_og->set_value("scale_z", double_to_string(scale(2), 2)); + + cache_scale = scale; +} + +void ObjectManipulation::update_size_value(const Vec3d& size) +{ + m_og->set_value("size_x", double_to_string(size(0), 2)); + m_og->set_value("size_y", double_to_string(size(1), 2)); + m_og->set_value("size_z", double_to_string(size(2), 2)); + + cache_size = size; } void ObjectManipulation::update_rotation_value(const Vec3d& rotation) @@ -337,6 +350,8 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(rotation(1)), 0), 2)); m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); + + cache_rotation = rotation; } void ObjectManipulation::change_position_value(const Vec3d& position) @@ -364,27 +379,40 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_scale_value(const Vec3d& scale) { - Vec3d scaling_factor; - if (m_is_percent_scale) - scaling_factor = scale*0.01; - else { - int selection = ol_selection(); - ModelObjectPtrs& objects = *wxGetApp().model_objects(); + Vec3d scaling_factor = scale; + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + bool needs_uniform_scale = selection.is_single_full_object() && !selection.is_single_full_instance(); - auto size = objects[selection]->instance_bounding_box(0).size(); - for (size_t i = 0; i < 3; ++i) - scaling_factor(i) = scale(i) / size(i); + if (needs_uniform_scale) + { + double min = scaling_factor.minCoeff(); + double max = scaling_factor.maxCoeff(); + if (min != 100.0) + scaling_factor = Vec3d(min, min, min); + else if (max != 100.0) + scaling_factor = Vec3d(max, max, max); } + scaling_factor *= 0.01; + auto canvas = wxGetApp().plater()->canvas3D(); canvas->get_selection().start_dragging(); canvas->get_selection().scale(scaling_factor, false); canvas->do_scale(); } -void ObjectManipulation::print_cashe_value(const std::string& label, const Vec3d& v) +void ObjectManipulation::change_size_value(const Vec3d& size) { - std::cout << label << " => " << " X:" << v(0) << " Y:" << v(1) << " Z:" << v(2) << std::endl; + const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); + + Vec3d ref_size = cache_size; + if (selection.is_single_full_instance()) + { + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + ref_size = volume->bounding_box.size(); + } + + change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); } } //namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index cbc22a2dd..cacceff9d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -14,11 +14,11 @@ namespace GUI { class ObjectManipulation : public OG_Settings { - bool m_is_percent_scale = false; // true -> percentage scale unit - // false -> uniform scale unit - bool m_is_uniform_scale = false; // It indicates if scale is uniform - Vec3d cache_position { 0., 0., 0. }; + Vec3d cache_rotation { 0., 0., 0. }; + Vec3d cache_scale { 100., 100., 100. }; + Vec3d cache_size { 0., 0., 0. }; + wxStaticText* m_move_Label = nullptr; public: @@ -36,25 +36,22 @@ public: void reset_position_value(); void reset_rotation_value(); void reset_scale_value(); + void reset_size_value(); // update position values displacements or "gizmos" void update_position_value(const Vec3d& position); // update scale values after scale unit changing or "gizmos" void update_scale_value(const Vec3d& scaling_factor); + // update size values after scale unit changing or "gizmos" + void update_size_value(const Vec3d& size); // update rotation value after "gizmos" void update_rotation_value(const Vec3d& rotation); - void set_uniform_scaling(const bool uniform_scale) { m_is_uniform_scale = uniform_scale; } - - // change values void change_position_value(const Vec3d& position); void change_rotation_value(const Vec3d& rotation); void change_scale_value(const Vec3d& scale); - - -private: - void print_cashe_value(const std::string& label, const Vec3d& value); + void change_size_value(const Vec3d& size); }; }} From 334f747fa99274e835d0d5b509ab0032b5854049 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 10:40:53 +0100 Subject: [PATCH 017/132] Sidebar matrix fields focus handling --- src/slic3r/GUI/GLCanvas3D.cpp | 12 ++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 3 ++- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 6 ++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index eecefd381..22c399223 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3581,6 +3581,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_view_toolbar(nullptr) #endif // ENABLE_REMOVE_TABS_FROM_PLATER , m_use_clipping_planes(false) + , m_sidebar_field("") , m_config(nullptr) , m_process(nullptr) , m_model(nullptr) @@ -5566,6 +5567,17 @@ void GLCanvas3D::update_gizmos_on_off_state() m_gizmos.update_on_off_state(get_selection()); } +void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on) +{ + m_sidebar_field = focus_on ? opt_key : ""; + + if (!m_sidebar_field.empty()) + { + m_gizmos.reset_all_states(); + m_dirty = true; + } +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index e07c60ed2..906412e6a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -794,6 +794,7 @@ private: ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; mutable SlaCap m_sla_caps[2]; + std::string m_sidebar_field; mutable GLVolumeCollection m_volumes; Selection m_selection; @@ -995,7 +996,7 @@ public: void viewport_changed(); #endif // ENABLE_CONSTRAINED_CAMERA_TARGET - void handle_sidebar_focus_event(const std::string& opt_key) {} + void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on); private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 9bc316deb..0c07c1fe5 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -41,6 +41,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_scale_value(new_value); else if (param == "size") change_size_value(new_value); + + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_fill_empty_value = [this](const std::string& opt_key) @@ -76,11 +78,12 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : } m_og->set_value(opt_key, double_to_string(value)); + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_set_focus = [this](const std::string& opt_key) { - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key); + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; ConfigOptionDef def; @@ -300,7 +303,6 @@ void ObjectManipulation::reset_rotation_value() void ObjectManipulation::reset_scale_value() { - m_og->set_value("scale_unit", _("%")); m_og->set_value("scale_x", def_100); m_og->set_value("scale_y", def_100); m_og->set_value("scale_z", def_100); From a394e55e0781986b22c09fafae50dd1f8ed1dcc1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 10:49:22 +0100 Subject: [PATCH 018/132] Added method ModelObject::full_raw_mesh() --- src/libslic3r/Model.cpp | 19 +++++++++++++++++++ src/libslic3r/Model.hpp | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 07f1b98c1..7ebcbd815 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -809,6 +809,25 @@ TriangleMesh ModelObject::raw_mesh() const return mesh; } +// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. +TriangleMesh ModelObject::full_raw_mesh() const +{ + TriangleMesh mesh; + for (const ModelVolume *v : this->volumes) +#if ENABLE_MODELVOLUME_TRANSFORM + { + TriangleMesh vol_mesh(v->mesh); + vol_mesh.transform(v->get_matrix()); + mesh.merge(vol_mesh); + } +#else + { + mesh.merge(v->mesh); + } +#endif // ENABLE_MODELVOLUME_TRANSFORM + return mesh; +} + // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // This bounding box is only used for the actual slicing. BoundingBoxf3 ModelObject::raw_bounding_box() const diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b02862203..b396bd1db 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -218,6 +218,8 @@ public: // Non-transformed (non-rotated, non-scaled, non-translated) sum of non-modifier object volumes. // Currently used by ModelObject::mesh() and to calculate the 2D envelope for 2D platter. TriangleMesh raw_mesh() const; + // Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes. + TriangleMesh full_raw_mesh() const; // A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // This bounding box is only used for the actual slicing. BoundingBoxf3 raw_bounding_box() const; From d453b6fb3f777320bab3fb949a3499cccd12a9dc Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 11:11:06 +0100 Subject: [PATCH 019/132] Sidebar matrix field behavior for single full instance selection --- src/slic3r/GUI/GLCanvas3D.cpp | 11 ++++-- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 47 ++++++++++++----------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 2 + 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 22c399223..f34341a0e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1549,9 +1549,14 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) if (is_single_full_instance()) #if ENABLE_WORLD_ROTATIONS { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); - (*m_volumes)[i]->set_instance_rotation(new_rotation); + if (local) + (*m_volumes)[i]->set_instance_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_instance_rotation_matrix()); + (*m_volumes)[i]->set_instance_rotation(new_rotation); + } } #else #if ENABLE_MODELVOLUME_TRANSFORM diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 0c07c1fe5..5c049144e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -144,8 +144,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // Settings table m_og->append_line(add_og_to_object_settings(L("Position"), L("mm")), &m_move_Label); - m_og->append_line(add_og_to_object_settings(L("Rotation"), "°")); - m_og->append_line(add_og_to_object_settings(L("Scale"), "%")); + m_og->append_line(add_og_to_object_settings(L("Rotation"), "°"), &m_rotate_Label); + m_og->append_line(add_og_to_object_settings(L("Scale"), "%"), &m_scale_Label); m_og->append_line(add_og_to_object_settings(L("Size"), "mm")); /* Unused parameter at this time @@ -192,9 +192,11 @@ int ObjectManipulation::ol_selection() void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) { - wxString move_label = _(L("Position")); + wxString move_label = _(L("Position:")); + wxString rotate_label = _(L("Rotation:")); + wxString scale_label = _(L("Scale factors:")); #if ENABLE_MODELVOLUME_TRANSFORM - if (selection.is_single_full_instance() || selection.is_single_full_object()) + if (selection.is_single_full_instance()) #else if (selection.is_single_full_object()) { @@ -228,19 +230,15 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele #endif // ENABLE_MODELVOLUME_TRANSFORM m_og->enable(); } - else if (selection.is_wipe_tower()) + else if (selection.is_single_full_object()) { - // the selection contains a single volume - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_volume_offset()); - update_rotation_value(volume->get_volume_rotation()); - update_scale_value(volume->get_volume_scaling_factor()); -#else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); -#endif // ENABLE_MODELVOLUME_TRANSFORM + const BoundingBoxf3& box = selection.get_bounding_box(); + update_position_value(box.center()); + reset_rotation_value(); + reset_scale_value(); + update_size_value(box.size()); + rotate_label = _(L("Rotate:")); + scale_label = _(L("Scale:")); m_og->enable(); } else if (selection.is_single_modifier() || selection.is_single_volume()) @@ -262,7 +260,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele else if (wxGetApp().obj_list()->multiple_selection()) { reset_settings_value(); - move_label = _(L("Displacement")); + move_label = _(L("Translate:")); update_size_value(selection.get_bounding_box().size()); m_og->enable(); } @@ -270,6 +268,8 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele reset_settings_value(); m_move_Label->SetLabel(move_label); + m_rotate_Label->SetLabel(rotate_label); + m_scale_Label->SetLabel(scale_label); } void ObjectManipulation::reset_settings_value() @@ -358,11 +358,9 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_position_value(const Vec3d& position) { - Vec3d displacement(position - cache_position); - auto canvas = wxGetApp().plater()->canvas3D(); canvas->get_selection().start_dragging(); - canvas->get_selection().translate(displacement); + canvas->get_selection().translate(position - cache_position); canvas->do_move(); cache_position = position; @@ -370,12 +368,17 @@ void ObjectManipulation::change_position_value(const Vec3d& position) void ObjectManipulation::change_rotation_value(const Vec3d& rotation) { + GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); + const GLCanvas3D::Selection& selection = canvas->get_selection(); + Vec3d rad_rotation; for (size_t i = 0; i < 3; ++i) + { rad_rotation(i) = Geometry::deg2rad(rotation(i)); - auto canvas = wxGetApp().plater()->canvas3D(); + } + canvas->get_selection().start_dragging(); - canvas->get_selection().rotate(rad_rotation, false); + canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance()); canvas->do_rotate(); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index cacceff9d..0ada37b96 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -20,6 +20,8 @@ class ObjectManipulation : public OG_Settings Vec3d cache_size { 0., 0., 0. }; wxStaticText* m_move_Label = nullptr; + wxStaticText* m_scale_Label = nullptr; + wxStaticText* m_rotate_Label = nullptr; public: ObjectManipulation(wxWindow* parent); From 771928d916064fc8ee6fd37a449b0186abcad02a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 11:31:41 +0100 Subject: [PATCH 020/132] Logging of memory allocations on Windows during the slicing process when the SLIC3R_LOGLEVEL >= info. --- src/libslic3r/CMakeLists.txt | 4 +++ src/libslic3r/GCode.cpp | 4 +-- src/libslic3r/Print.cpp | 11 +++--- src/libslic3r/PrintObject.cpp | 17 +++++----- src/libslic3r/Utils.hpp | 3 ++ src/libslic3r/utils.cpp | 63 +++++++++++++++++++++++++++++++++++ 6 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index df3ddb6de..7870bd1fb 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -194,6 +194,10 @@ target_link_libraries(libslic3r tbb ) +if(WIN32) + target_link_libraries(libslic3r Psapi.lib) +endif() + if(SLIC3R_PROFILE) target_link_libraries(slic3r Shiny) endif() diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 7f56b5f99..894e623aa 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -423,7 +423,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ print->set_started(psGCodeExport); - BOOST_LOG_TRIVIAL(info) << "Exporting G-code..."; + BOOST_LOG_TRIVIAL(info) << "Exporting G-code..." << log_memory_info(); // Remove the old g-code if it exists. boost::nowide::remove(path); @@ -480,7 +480,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "Is " + path_tmp + " locked?" + '\n'); - BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished"; + BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info(); print->set_done(psGCodeExport); // Write the profiler measurements to file diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0a1bac4e4..97eb03662 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -8,13 +8,14 @@ #include "SupportMaterial.hpp" #include "GCode.hpp" #include "GCode/WipeTowerPrusaMM.hpp" -#include -#include -#include +#include "Utils.hpp" #include "PrintExport.hpp" +#include +#include #include +#include //! macro used to mark string used at localization, //! return same string @@ -1475,7 +1476,7 @@ void Print::auto_assign_extruders(ModelObject* model_object) const // Slicing process, running at a background thread. void Print::process() { - BOOST_LOG_TRIVIAL(info) << "Staring the slicing process."; + BOOST_LOG_TRIVIAL(info) << "Staring the slicing process." << log_memory_info(); for (PrintObject *obj : m_objects) obj->make_perimeters(); this->set_status(70, "Infilling layers"); @@ -1507,7 +1508,7 @@ void Print::process() } this->set_done(psWipeTower); } - BOOST_LOG_TRIVIAL(info) << "Slicing process finished."; + BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info(); } // G-code export process, running at a background thread. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index efde5392d..877c77b0c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -5,6 +5,7 @@ #include "SupportMaterial.hpp" #include "Surface.hpp" #include "Slicing.hpp" +#include "Utils.hpp" #include #include @@ -132,7 +133,7 @@ void PrintObject::make_perimeters() return; m_print->set_status(20, "Generating perimeters"); - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // merge slices if they were split into types if (this->typed_slices) { @@ -253,7 +254,7 @@ void PrintObject::prepare_infill() // Decide what surfaces are to be filled. // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. - BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) for (auto *region : layer->m_regions) { region->prepare_fill_surfaces(); @@ -601,7 +602,7 @@ bool PrintObject::has_support_material() const // If a part of a region is of stBottom and stTop, the stBottom wins. void PrintObject::detect_surfaces_type() { - BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Detecting solid surfaces..." << log_memory_info(); // Interface shells: the intersecting parts are treated as self standing objects supporting each other. // Each of the objects will have a full number of top / bottom layers, even if these top / bottom layers @@ -793,7 +794,7 @@ void PrintObject::detect_surfaces_type() void PrintObject::process_external_surfaces() { - BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..."; + BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; @@ -818,7 +819,7 @@ void PrintObject::discover_vertical_shells() { PROFILE_FUNC(); - BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..."; + BOOST_LOG_TRIVIAL(info) << "Discovering vertical shells..." << log_memory_info(); struct DiscoverVerticalShellsCacheEntry { @@ -1202,7 +1203,7 @@ void PrintObject::discover_vertical_shells() sparse infill */ void PrintObject::bridge_over_infill() { - BOOST_LOG_TRIVIAL(info) << "Bridge over infill..."; + BOOST_LOG_TRIVIAL(info) << "Bridge over infill..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { const PrintRegion ®ion = *m_print->regions()[region_id]; @@ -1387,7 +1388,7 @@ bool PrintObject::update_layer_height_profile() // this should be idempotent void PrintObject::_slice() { - BOOST_LOG_TRIVIAL(info) << "Slicing objects..."; + BOOST_LOG_TRIVIAL(info) << "Slicing objects..." << log_memory_info(); this->typed_slices = false; @@ -1709,7 +1710,7 @@ void PrintObject::_make_perimeters() if (! this->set_started(posPerimeters)) return; - BOOST_LOG_TRIVIAL(info) << "Generating perimeters..."; + BOOST_LOG_TRIVIAL(info) << "Generating perimeters..." << log_memory_info(); // merge slices if they were split into types if (this->typed_slices) { diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 7f2c94f03..e34476d4c 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -12,6 +12,9 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); extern void trace(unsigned int level, const char *message); +// Return string to be added to the boost::log output to inform about the current process memory allocation. +// The string is non-empty only if the loglevel >= info (3). +extern std::string log_memory_info(); extern void disable_multi_threading(); // Set a path with GUI resource files. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 7a51a6104..af6373361 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -8,6 +8,7 @@ #ifdef WIN32 #include +#include #else #include #endif @@ -365,4 +366,66 @@ std::string xml_escape(std::string text) return text; } +#ifdef WIN32 + +#ifndef PROCESS_MEMORY_COUNTERS_EX + // MingW32 doesn't have this struct in psapi.h + typedef struct _PROCESS_MEMORY_COUNTERS_EX { + DWORD cb; + DWORD PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivateUsage; + } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX; +#endif /* PROCESS_MEMORY_COUNTERS_EX */ + +std::string format_memsize_MB(size_t n) +{ + std::string out; + size_t n2 = 0; + size_t scale = 1; + // Round to MB + n += 500000; + n /= 1000000; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; + } + char buf[8]; + sprintf(buf, "%d", n); + out = buf; + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + sprintf(buf, ",%03d", n); + out += buf; + } + return out + "MB"; +} + +std::string log_memory_info() +{ + std::string out; + if (logSeverity <= boost::log::trivial::info) { + HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ::GetCurrentProcessId()); + if (hProcess != nullptr) { + PROCESS_MEMORY_COUNTERS_EX pmc; + if (GetProcessMemoryInfo(hProcess, (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc))) + out = " WorkingSet: " + format_memsize_MB(pmc.WorkingSetSize) + " PrivateBytes: " + format_memsize_MB(pmc.PrivateUsage) + " Pagefile(peak): " + format_memsize_MB(pmc.PagefileUsage) + "(" + format_memsize_MB(pmc.PeakPagefileUsage) + ")"; + CloseHandle(hProcess); + } + } + return out; +} + +#endif + }; // namespace Slic3r From f54f96666330c39c21dca14c7cf80fc32d87b495 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 11:50:22 +0100 Subject: [PATCH 021/132] Sidebar matrix field behavior for single full object selection --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 5c049144e..4f63fb61f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -390,12 +390,20 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) if (needs_uniform_scale) { - double min = scaling_factor.minCoeff(); - double max = scaling_factor.maxCoeff(); - if (min != 100.0) - scaling_factor = Vec3d(min, min, min); - else if (max != 100.0) - scaling_factor = Vec3d(max, max, max); + Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs(); + double max_diff = abs_scale_diff(X); + Axis max_diff_axis = X; + if (max_diff < abs_scale_diff(Y)) + { + max_diff = abs_scale_diff(Y); + max_diff_axis = Y; + } + if (max_diff < abs_scale_diff(Z)) + { + max_diff = abs_scale_diff(Z); + max_diff_axis = Z; + } + scaling_factor = Vec3d(scale(max_diff_axis), scale(max_diff_axis), scale(max_diff_axis)); } scaling_factor *= 0.01; From b8c8dfbb2f5575e73a694aa0c7d85dfc4238b395 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 12:05:13 +0100 Subject: [PATCH 022/132] Fix of Linux/OSX build --- src/libslic3r/utils.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index af6373361..8081828c7 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -426,6 +426,11 @@ std::string log_memory_info() return out; } +#else +std::string log_memory_info() +{ + return std::string(); +} #endif }; // namespace Slic3r From 3f96f6df84b7db79ea4364a61f7a9d5e8255209f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 12:35:49 +0100 Subject: [PATCH 023/132] Rendering of selection center (disabled) --- src/libslic3r/Technologies.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 49 +++++++++++++++++++++++++++++++++- src/slic3r/GUI/GLCanvas3D.hpp | 13 +++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index d604896ad..0f0ae974b 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -40,6 +40,8 @@ #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) // Adds background texture to toolbars #define ENABLE_TOOLBAR_BACKGROUND_TEXTURE (1 && ENABLE_1_42_0) +// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active +#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f34341a0e..776a31c47 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1162,8 +1162,21 @@ GLCanvas3D::Selection::Selection() , m_valid(false) , m_bounding_box_dirty(true) { +#if ENABLE_RENDER_SELECTION_CENTER + m_quadric = ::gluNewQuadric(); + if (m_quadric != nullptr) + ::gluQuadricDrawStyle(m_quadric, GLU_FILL); +#endif // ENABLE_RENDER_SELECTION_CENTER } +#if ENABLE_RENDER_SELECTION_CENTER +GLCanvas3D::Selection::~Selection() +{ + if (m_quadric != nullptr) + ::gluDeleteQuadric(m_quadric); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) { m_volumes = volumes; @@ -1994,7 +2007,7 @@ void GLCanvas3D::Selection::erase() void GLCanvas3D::Selection::render() const { - if (is_empty()) + if (!m_valid || is_empty()) return; // render cumulative bounding box of selected volumes @@ -2002,6 +2015,28 @@ void GLCanvas3D::Selection::render() const _render_synchronized_volumes(); } +#if ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::Selection::render_center() const +{ + if (!m_valid || is_empty() || (m_quadric == nullptr)) + return; + + const Vec3d& center = get_bounding_box().center(); + + ::glDisable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glColor3f(1.0f, 1.0f, 1.0f); + ::glPushMatrix(); + ::glTranslated(center(0), center(1), center(2)); + ::gluSphere(m_quadric, 0.75, 32, 32); + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::Selection::_update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); @@ -4097,6 +4132,10 @@ void GLCanvas3D::render() if (!is_custom_bed) // textured bed needs to be rendered after objects _render_bed(theta); +#if ENABLE_RENDER_SELECTION_CENTER + _render_selection_center(); +#endif // ENABLE_RENDER_SELECTION_CENTER + // we need to set the mouse's scene position here because the depth buffer // could be invalidated by the following gizmo render methods // this position is used later into on_mouse() to drag the objects @@ -6138,6 +6177,14 @@ void GLCanvas3D::_render_selection() const m_selection.render(); } +#if ENABLE_RENDER_SELECTION_CENTER +void GLCanvas3D::_render_selection_center() const +{ + if (!m_gizmos.is_running()) + m_selection.render_center(); +} +#endif // ENABLE_RENDER_SELECTION_CENTER + void GLCanvas3D::_render_warning_texture() const { if (!m_warning_texture_enabled) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 906412e6a..9fb514c86 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -491,8 +491,15 @@ public: mutable BoundingBoxf3 m_bounding_box; mutable bool m_bounding_box_dirty; +#if ENABLE_RENDER_SELECTION_CENTER + GLUquadricObj* m_quadric; +#endif // ENABLE_RENDER_SELECTION_CENTER + public: Selection(); +#if ENABLE_RENDER_SELECTION_CENTER + ~Selection(); +#endif // ENABLE_RENDER_SELECTION_CENTER void set_volumes(GLVolumePtrs* volumes); @@ -567,6 +574,9 @@ public: void erase(); void render() const; +#if ENABLE_RENDER_SELECTION_CENTER + void render_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER private: void _update_valid(); @@ -1024,6 +1034,9 @@ private: void _render_axes() const; void _render_objects() const; void _render_selection() const; +#if ENABLE_RENDER_SELECTION_CENTER + void _render_selection_center() const; +#endif // ENABLE_RENDER_SELECTION_CENTER void _render_warning_texture() const; void _render_legend_texture() const; void _render_layer_editing_overlay() const; From d6b8ed3e3e1f024fe27d7be5f0488bd38d8b20c3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 13:07:50 +0100 Subject: [PATCH 024/132] Sidebar matrix field behavior for single volume selection --- src/slic3r/GUI/GLCanvas3D.cpp | 33 +++++++++++++++++++++++---------- src/slic3r/GUI/GLCanvas3D.hpp | 1 + 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 776a31c47..cdadadb60 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1532,8 +1532,13 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement) #if ENABLE_MODELVOLUME_TRANSFORM if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) { - Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; - (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + if (_requires_local_axes()) + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); + else + { + Vec3d local_displacement = (m_cache.volumes_data[i].get_instance_rotation_matrix() * m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_instance_mirror_matrix()).inverse() * displacement; + (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + local_displacement); + } } else if (m_mode == Instance) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); @@ -1582,11 +1587,16 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) else if (is_single_volume() || is_single_modifier()) #if ENABLE_WORLD_ROTATIONS { - Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); - const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); - Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); - (*m_volumes)[i]->set_volume_rotation(new_rotation); - } + if (_requires_local_axes()) + (*m_volumes)[i]->set_volume_rotation(rotation); + else + { + Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); + const Transform3d& inst_m = m_cache.volumes_data[i].get_instance_rotation_matrix(); + Vec3d new_rotation = Geometry::extract_euler_angles(inst_m.inverse() * m * inst_m * m_cache.volumes_data[i].get_volume_rotation_matrix()); + (*m_volumes)[i]->set_volume_rotation(new_rotation); + } + } #else (*m_volumes)[i]->set_volume_rotation(rotation); #endif // ENABLE_WORLD_ROTATIONS @@ -2570,6 +2580,11 @@ void GLCanvas3D::Selection::_ensure_on_bed() } #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING +bool GLCanvas3D::Selection::_requires_local_axes() const +{ + return (m_mode == Volume) && is_from_single_instance(); +} + const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale; @@ -6332,9 +6347,7 @@ void GLCanvas3D::_render_camera_target() const ::glColor3f(0.0f, 1.0f, 0.0f); ::glVertex3d(target(0), target(1) - half_length, target(2)); ::glVertex3d(target(0), target(1) + half_length, target(2)); - ::glEnd(); - - ::glBegin(GL_LINES); + // draw line for z axis ::glColor3f(0.0f, 0.0f, 1.0f); ::glVertex3d(target(0), target(1), target(2) - half_length); ::glVertex3d(target(0), target(1), target(2) + half_length); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 9fb514c86..a5c97ee98 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -597,6 +597,7 @@ public: #if ENABLE_ENSURE_ON_BED_WHILE_SCALING void _ensure_on_bed(); #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING + bool _requires_local_axes() const; }; class ClippingPlane From 7077c1e4a13fb628f78b0b9b38e7c452c5ea4be8 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 18 Dec 2018 14:08:46 +0100 Subject: [PATCH 025/132] Sidebar matrix field behavior for multiple volume selection --- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 6 +++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cdadadb60..32bbb32d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1478,6 +1478,14 @@ bool GLCanvas3D::Selection::is_from_single_object() const return (0 <= idx) && (idx < 1000); } +bool GLCanvas3D::Selection::requires_uniform_scale() const +{ + if (is_single_full_instance() || is_single_modifier() || is_single_volume()) + return false; + + return true; +} + int GLCanvas3D::Selection::get_object_idx() const { return (m_cache.content.size() == 1) ? m_cache.content.begin()->first : -1; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a5c97ee98..08368a131 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -532,6 +532,7 @@ public: bool is_wipe_tower() const { return m_type == WipeTower; } bool is_modifier() const { return (m_type == SingleModifier) || (m_type == MultipleModifier); } bool is_single_modifier() const { return m_type == SingleModifier; } + bool is_multiple_modifier() const { return m_type == MultipleModifier; } bool is_single_full_instance() const; bool is_multiple_full_instance() const { return m_type == MultipleFullInstance; } bool is_single_full_object() const { return m_type == SingleFullObject; } @@ -543,6 +544,7 @@ public: bool is_from_single_object() const; bool contains_volume(unsigned int volume_idx) const { return std::find(m_list.begin(), m_list.end(), volume_idx) != m_list.end(); } + bool requires_uniform_scale() const; // Returns the the object id if the selection is from a single object, otherwise is -1 int get_object_idx() const; diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 4f63fb61f..ddf699c2c 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -261,6 +261,8 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele { reset_settings_value(); move_label = _(L("Translate:")); + rotate_label = _(L("Rotate:")); + scale_label = _(L("Scale:")); update_size_value(selection.get_bounding_box().size()); m_og->enable(); } @@ -386,9 +388,7 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) { Vec3d scaling_factor = scale; const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - bool needs_uniform_scale = selection.is_single_full_object() && !selection.is_single_full_instance(); - - if (needs_uniform_scale) + if (selection.requires_uniform_scale()) { Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs(); double max_diff = abs_scale_diff(X); From bffcaeff4130079d31f9f8997962b1379b5b9bde Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 14:10:31 +0100 Subject: [PATCH 026/132] Time estimator: Added consumed memory tracing, replaced std::map with std::vector for lower memory consumption. --- src/libslic3r/GCode.cpp | 4 +++- src/libslic3r/GCodeTimeEstimator.cpp | 26 +++++++++++++++++++++----- src/libslic3r/GCodeTimeEstimator.hpp | 6 +++++- src/libslic3r/Utils.hpp | 2 ++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 894e623aa..ef143a3e8 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1231,7 +1231,7 @@ void GCode::process_layer( const Print &print, // Set of object & print layers of the same PrintObject and with the same print_z. const std::vector &layers, - const LayerTools &layer_tools, + const LayerTools &layer_tools, // If set to size_t(-1), then print all copies of all objects. // Otherwise print a single copy of a single object. const size_t single_object_idx) @@ -1644,6 +1644,8 @@ void GCode::process_layer( // printf("G-code after filter:\n%s\n", out.c_str()); _write(file, gcode); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " + + format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0); } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 5c8cc2659..0ab49a345 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -309,8 +309,9 @@ namespace Slic3r { gcode_line += "\n"; // add remaining time lines where needed + G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); _parser.parse_line(gcode_line, - [this, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) + [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) { if (line.cmd_is("G1")) { @@ -319,10 +320,10 @@ namespace Slic3r { if (!line.has_e()) return; - G1LineIdToBlockIdMap::const_iterator it = _g1_line_ids.find(g1_lines_count); - if ((it != _g1_line_ids.end()) && (it->second < (unsigned int)_blocks.size())) + if ((it_line_id != _g1_line_ids.end()) && (it_line_id->first == g1_lines_count) && (it_line_id->second < (unsigned int)_blocks.size())) { - const Block& block = _blocks[it->second]; + const Block& block = _blocks[it_line_id->second]; + ++ it_line_id; if (block.elapsed_time != -1.0f) { float block_remaining_time = _time - block.elapsed_time; @@ -667,6 +668,21 @@ namespace Slic3r { return _get_time_minutes(get_time()); } + // Return an estimate of the memory consumed by the time estimator. + size_t GCodeTimeEstimator::memory_used() const + { + size_t out = sizeof(*this); +#if WIN32 + #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) +#else + #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) +#endif + out += STDVEC_MEMSIZE(this->_blocks, Block); + out += STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); +#undef STDVEC_MEMSIZE + return out; + } + void GCodeTimeEstimator::_reset() { _curr.reset(); @@ -1072,7 +1088,7 @@ namespace Slic3r { // adds block to blocks list _blocks.emplace_back(block); - _g1_line_ids.insert(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); + _g1_line_ids.emplace_back(G1LineIdToBlockIdMap::value_type(get_g1_line_id(), (unsigned int)_blocks.size() - 1)); } void GCodeTimeEstimator::_processG4(const GCodeReader::GCodeLine& line) diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index e9da584c3..ef91d5ff1 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -209,7 +209,8 @@ namespace Slic3r { typedef std::map MovesStatsMap; #endif // ENABLE_MOVE_STATS - typedef std::map G1LineIdToBlockIdMap; + typedef std::pair G1LineIdToBlockId; + typedef std::vector G1LineIdToBlockIdMap; private: EMode _mode; @@ -338,6 +339,9 @@ namespace Slic3r { // Returns the estimated time, in minutes (integer) std::string get_time_minutes() const; + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + private: void _reset(); void _reset_time(); diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index e34476d4c..f4f05ae66 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -12,6 +12,8 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); extern void trace(unsigned int level, const char *message); +// Format memory allocated, separate thousands by comma. +extern std::string format_memsize_MB(size_t n); // Return string to be added to the boost::log output to inform about the current process memory allocation. // The string is non-empty only if the loglevel >= info (3). extern std::string log_memory_info(); From 66b5deccf5b90e24ecf5935f263b7f893dd5440e Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 18 Dec 2018 14:34:16 +0100 Subject: [PATCH 027/132] PrintHost: Basic SL1 support --- src/libslic3r/PrintConfig.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 + src/slic3r/GUI/Preset.cpp | 1 + src/slic3r/GUI/Tab.cpp | 197 ++++++++++++++++++--------------- src/slic3r/GUI/Tab.hpp | 2 + src/slic3r/Utils/OctoPrint.cpp | 188 +++++++++++++++++-------------- src/slic3r/Utils/OctoPrint.hpp | 45 +++++--- src/slic3r/Utils/PrintHost.cpp | 14 ++- 8 files changed, 255 insertions(+), 196 deletions(-) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 487e35bf2..08f42f39b 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -36,7 +36,7 @@ enum GCodeFlavor { }; enum PrintHostType { - htOctoPrint, htDuet, + htOctoPrint, htDuet, htSL1, }; enum InfillPattern { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6544da789..51735fe67 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3244,6 +3244,8 @@ void Plater::on_config_change(const DynamicPrintConfig &config) #endif // ENABLE_REMOVE_TABS_FROM_PLATER if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); update_scheduled = true; + } else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { + p->config->option>(opt_key)->value = htSL1; } } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 90372ecd4..bfda0a2f3 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -456,6 +456,7 @@ const std::vector& Preset::sla_printer_options() "display_width", "display_height", "display_pixels_x", "display_pixels_y", "display_orientation", "printer_correction", + "print_host", "printhost_apikey", "printhost_cafile", "printer_notes", "inherits" }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 30d83d845..f46ad2142 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1538,6 +1538,108 @@ bool Tab::current_preset_is_dirty() return m_presets->current_is_dirty(); } +void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) +{ + const bool sla = m_presets->get_selected_preset().printer_technology() == ptSLA; + + // Only offer the host type selection for FFF, for SLA it's always the SL1 printer (at the moment) + if (! sla) { + optgroup->append_single_option_line("host_type"); + } else { + m_config->option>("host_type", true)->value = htSL1; + } + + auto printhost_browse = [this, optgroup] (wxWindow* parent) { + + // TODO: SLA + + auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { + BonjourDialog dialog(parent); + if (dialog.show_and_lookup()) { + optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + } + }); + + return sizer; + }; + + auto print_host_test = [this](wxWindow* parent) { + auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), + wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { + std::unique_ptr host(PrintHost::get_print_host(m_config)); + if (! host) { + const auto text = wxString::Format("%s", + _(L("Could not get a valid Printer Host reference"))); + show_error(this, text); + return; + } + wxString msg; + if (host->test(msg)) { + show_info(this, host->get_test_ok_msg(), _(L("Success!"))); + } else { + show_error(this, host->get_test_failed_msg(msg)); + } + }); + + return sizer; + }; + + Line host_line = optgroup->create_single_option_line("print_host"); + host_line.append_widget(printhost_browse); + host_line.append_widget(print_host_test); + optgroup->append_line(host_line); + optgroup->append_single_option_line("printhost_apikey"); + + if (Http::ca_file_supported()) { + + Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); + + auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { + auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(btn); + + btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { + static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); + wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); + if (openFileDialog.ShowModal() != wxID_CANCEL) { + optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); + } + }); + + return sizer; + }; + + cafile_line.append_widget(printhost_cafile_browse); + optgroup->append_line(cafile_line); + + auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, + _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + + Line cafile_hint { "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = std::move(printhost_cafile_hint); + optgroup->append_line(cafile_hint); + + } +} + void TabPrinter::build() { m_presets = &m_preset_bundle->printers; @@ -1665,96 +1767,8 @@ void TabPrinter::build_fff() } #endif - optgroup = page->new_optgroup(_(L("Printer Host upload"))); - - optgroup->append_single_option_line("host_type"); - - auto printhost_browse = [this, optgroup] (wxWindow* parent) { - auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, parent, optgroup](wxCommandEvent e) { - BonjourDialog dialog(parent); - if (dialog.show_and_lookup()) { - optgroup->set_value("print_host", std::move(dialog.get_selected()), true); - } - }); - - return sizer; - }; - - auto print_host_test = [this](wxWindow* parent) { - auto btn = m_print_host_test_btn = new wxButton(parent, wxID_ANY, _(L("Test")), - wxDefaultPosition, wxDefaultSize, wxBU_LEFT | wxBU_EXACTFIT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("wrench.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent e) { - std::unique_ptr host(PrintHost::get_print_host(m_config)); - if (! host) { - const auto text = wxString::Format("%s", - _(L("Could not get a valid Printer Host reference"))); - show_error(this, text); - return; - } - wxString msg; - if (host->test(msg)) { - show_info(this, host->get_test_ok_msg(), _(L("Success!"))); - } else { - show_error(this, host->get_test_failed_msg(msg)); - } - }); - - return sizer; - }; - - Line host_line = optgroup->create_single_option_line("print_host"); - host_line.append_widget(printhost_browse); - host_line.append_widget(print_host_test); - optgroup->append_line(host_line); - optgroup->append_single_option_line("printhost_apikey"); - - if (Http::ca_file_supported()) { - - Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); - - auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { - auto btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(btn); - - btn->Bind(wxEVT_BUTTON, [this, optgroup] (wxCommandEvent e) { - static const auto filemasks = _(L("Certificate files (*.crt, *.pem)|*.crt;*.pem|All files|*.*")); - wxFileDialog openFileDialog(this, _(L("Open CA certificate file")), "", "", filemasks, wxFD_OPEN | wxFD_FILE_MUST_EXIST); - if (openFileDialog.ShowModal() != wxID_CANCEL) { - optgroup->set_value("printhost_cafile", std::move(openFileDialog.GetPath()), true); - } - }); - - return sizer; - }; - - cafile_line.append_widget(printhost_cafile_browse); - optgroup->append_line(cafile_line); - - auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, - _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); - auto sizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(txt); - return sizer; - }; - - Line cafile_hint { "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = std::move(printhost_cafile_hint); - optgroup->append_line(cafile_hint); - - } + optgroup = page->new_optgroup(_(L("Print Host upload"))); + build_printhost(optgroup.get()); optgroup = page->new_optgroup(_(L("Firmware"))); optgroup->append_single_option_line("gcode_flavor"); @@ -1897,6 +1911,9 @@ void TabPrinter::build_sla() } optgroup->append_line(line); + optgroup = page->new_optgroup(_(L("Print Host upload"))); + build_printhost(optgroup.get()); + page = add_options_page(_(L("Notes")), "note.png"); optgroup = page->new_optgroup(_(L("Notes")), 0); option = optgroup->get_option("printer_notes"); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 337fbf006..93db1383a 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -325,6 +325,8 @@ class TabPrinter : public Tab std::vector m_pages_fff; std::vector m_pages_sla; + + void build_printhost(ConfigOptionsGroup *optgroup); public: wxButton* m_serial_test_btn = nullptr; wxButton* m_print_host_test_btn = nullptr; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index 67c58a972..cbb81c54f 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -17,141 +17,161 @@ namespace fs = boost::filesystem; namespace Slic3r { OctoPrint::OctoPrint(DynamicPrintConfig *config) : - host(config->opt_string("print_host")), - apikey(config->opt_string("printhost_apikey")), - cafile(config->opt_string("printhost_cafile")) + host(config->opt_string("print_host")), + apikey(config->opt_string("printhost_apikey")), + cafile(config->opt_string("printhost_cafile")) {} OctoPrint::~OctoPrint() {} bool OctoPrint::test(wxString &msg) const { - // Since the request is performed synchronously here, - // it is ok to refer to `msg` from within the closure + // Since the request is performed synchronously here, + // it is ok to refer to `msg` from within the closure - bool res = true; - auto url = make_url("api/version"); + bool res = true; + auto url = make_url("api/version"); - BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url; + BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Get version at: %1%") % url; - auto http = Http::get(std::move(url)); - set_auth(http); - http.on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1%, HTTP %2%, body: `%3%`") % error % status % body; - res = false; - msg = format_error(body, error, status); - }) - .on_complete([&](std::string body, unsigned) { - BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; - }) - .perform_sync(); + auto http = Http::get(std::move(url)); + set_auth(http); + http.on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error getting version: %1%, HTTP %2%, body: `%3%`") % error % status % body; + res = false; + msg = format_error(body, error, status); + }) + .on_complete([&](std::string body, unsigned) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; - return res; + // TODO: parse body, call validate_version_text + + }) + .perform_sync(); + + return res; } wxString OctoPrint::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); + return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); } wxString OctoPrint::get_test_failed_msg (wxString &msg) const { - return wxString::Format("%s: %s\n\n%s", - _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); + return wxString::Format("%s: %s\n\n%s", + _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); } bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const { - const auto upload_filename = upload_data.upload_path.filename(); - const auto upload_parent_path = upload_data.upload_path.parent_path(); + const auto upload_filename = upload_data.upload_path.filename(); + const auto upload_parent_path = upload_data.upload_path.parent_path(); - wxString test_msg; - if (! test(test_msg)) { + wxString test_msg; + if (! test(test_msg)) { - // TODO: + // TODO: - // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); - // GUI::show_error(&progress_dialog, std::move(errormsg)); - // return false; - } + // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); + // GUI::show_error(&progress_dialog, std::move(errormsg)); + // return false; + } - bool res = true; + bool res = true; - auto url = make_url("api/files/local"); + auto url = make_url("api/files/local"); - BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") - % upload_data.source_path.string() - % url - % upload_filename.string() - % upload_parent_path.string() - % upload_data.start_print; + BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") + % upload_data.source_path.string() + % url + % upload_filename.string() + % upload_parent_path.string() + % upload_data.start_print; - auto http = Http::post(std::move(url)); - set_auth(http); - http.form_add("print", upload_data.start_print ? "true" : "false") - .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? - .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) - .on_complete([&](std::string body, unsigned status) { - BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; - }) - .on_error([&](std::string body, std::string error, unsigned status) { - BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - error_fn(std::move(body), std::move(error), status); - res = false; - }) - .on_progress([&](Http::Progress progress, bool &cancel) { - prorgess_fn(std::move(progress), cancel); - if (cancel) { - // Upload was canceled - BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; - res = false; - } - }) - .perform_sync(); + auto http = Http::post(std::move(url)); + set_auth(http); + http.form_add("print", upload_data.start_print ? "true" : "false") + .form_add("path", upload_parent_path.string()) // XXX: slashes on windows ??? + .form_add_file("file", upload_data.source_path.string(), upload_filename.string()) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: File uploaded: HTTP %1%: %2%") % status % body; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; + error_fn(std::move(body), std::move(error), status); + res = false; + }) + .on_progress([&](Http::Progress progress, bool &cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; + res = false; + } + }) + .perform_sync(); - return res; + return res; } bool OctoPrint::has_auto_discovery() const { - return true; + return true; } bool OctoPrint::can_test() const { - return true; + return true; +} + +bool OctoPrint::validate_version_text(const std::string &version_text) +{ + // FIXME + return true; } void OctoPrint::set_auth(Http &http) const { - http.header("X-Api-Key", apikey); + http.header("X-Api-Key", apikey); - if (! cafile.empty()) { - http.ca_file(cafile); - } + if (! cafile.empty()) { + http.ca_file(cafile); + } } std::string OctoPrint::make_url(const std::string &path) const { - if (host.find("http://") == 0 || host.find("https://") == 0) { - if (host.back() == '/') { - return (boost::format("%1%%2%") % host % path).str(); - } else { - return (boost::format("%1%/%2%") % host % path).str(); - } - } else { - return (boost::format("http://%1%/%2%") % host % path).str(); - } + if (host.find("http://") == 0 || host.find("https://") == 0) { + if (host.back() == '/') { + return (boost::format("%1%%2%") % host % path).str(); + } else { + return (boost::format("%1%/%2%") % host % path).str(); + } + } else { + return (boost::format("http://%1%/%2%") % host % path).str(); + } } wxString OctoPrint::format_error(const std::string &body, const std::string &error, unsigned status) { - if (status != 0) { - auto wxbody = wxString::FromUTF8(body.data()); - return wxString::Format("HTTP %u: %s", status, wxbody); - } else { - return wxString::FromUTF8(error.data()); - } + if (status != 0) { + auto wxbody = wxString::FromUTF8(body.data()); + return wxString::Format("HTTP %u: %s", status, wxbody); + } else { + return wxString::FromUTF8(error.data()); + } +} + + +// SL1 + +SL1Host::~SL1Host() {} + +bool SL1Host::validate_version_text(const std::string &version_text) +{ + // FIXME + return true; } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 9267b4c83..4d6555e13 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -16,24 +16,39 @@ class Http; class OctoPrint : public PrintHost { public: - OctoPrint(DynamicPrintConfig *config); - virtual ~OctoPrint(); + OctoPrint(DynamicPrintConfig *config); + virtual ~OctoPrint(); + + bool test(wxString &curl_msg) const; + wxString get_test_ok_msg () const; + wxString get_test_failed_msg (wxString &msg) const; + bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + bool has_auto_discovery() const; + bool can_test() const; + virtual std::string get_host() const { return host; } + +protected: + virtual bool validate_version_text(const std::string &version_text); - bool test(wxString &curl_msg) const; - wxString get_test_ok_msg () const; - wxString get_test_failed_msg (wxString &msg) const; - bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; - bool has_auto_discovery() const; - bool can_test() const; - virtual std::string get_host() const { return host; } private: - std::string host; - std::string apikey; - std::string cafile; + std::string host; + std::string apikey; + std::string cafile; - void set_auth(Http &http) const; - std::string make_url(const std::string &path) const; - static wxString format_error(const std::string &body, const std::string &error, unsigned status); + void set_auth(Http &http) const; + std::string make_url(const std::string &path) const; + static wxString format_error(const std::string &body, const std::string &error, unsigned status); +}; + + +class SL1Host: public OctoPrint +{ +public: + SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {} + virtual ~SL1Host(); + +protected: + virtual bool validate_version_text(const std::string &version_text); }; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 48f504884..863da5d43 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -24,13 +24,15 @@ PrintHost::~PrintHost() {} PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) { - PrintHostType kind = config->option>("host_type")->value; - if (kind == htOctoPrint) { - return new OctoPrint(config); - } else if (kind == htDuet) { - return new Duet(config); + const auto opt = config->option>("host_type"); + if (opt == nullptr) { return nullptr; } + + switch (opt->value) { + case htOctoPrint: return new OctoPrint(config); + case htSL1: return new SL1Host(config); + case htDuet: return new Duet(config); + default: return nullptr; } - return nullptr; } From 7d1fb201e725a2c83ceb5a226ed76e652154fb4a Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Dec 2018 13:22:22 +0100 Subject: [PATCH 028/132] Implemented updating of the settings values for PointCtrl and Choice. * wx_EVT_KILL_FOCES doesn't handled on OSX, so values are updating on wx_EVT_TEXT like a temporary workaround. --- src/slic3r/GUI/Field.cpp | 59 ++++++++++++++++++++++++++++++++------- src/slic3r/GUI/Field.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 4 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index fc428ac0d..0283b7c29 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -193,9 +193,10 @@ void Field::get_value_by_opt_type(wxString& str) } } -bool TextCtrl::is_defined_input_value() const +template +bool is_defined_input_value(wxWindow* win, const ConfigOptionType& type) { - if (static_cast(window)->GetValue().empty() && m_opt.type != coString && m_opt.type != coStrings) + if (static_cast(win)->GetValue().empty() && type != coString && type != coStrings) return false; return true; } @@ -274,7 +275,7 @@ void TextCtrl::BUILD() { temp->GetToolTip()->Enable(true); #endif // __WXGTK__ // if (!is_defined_input_value()) - if (is_defined_input_value()) + if (is_defined_input_value(window, m_opt.type)) on_change_field(); else on_kill_focus(e); @@ -399,6 +400,9 @@ 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()); + + // #ys_FIXME_KILL_FOCUS + // wxEVT_KILL_FOCUS doesn't handled on OSX now temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { if (tmp_value < 0) @@ -408,6 +412,7 @@ void SpinCtrl::BUILD() { on_change_field(); } }), temp->GetId()); + temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { // # On OSX / Cocoa, wxSpinCtrl::GetValue() doesn't return the new value @@ -420,9 +425,15 @@ void SpinCtrl::BUILD() { tmp_value = std::stoi(value); 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. +#ifdef __WXOSX__ + // #ys_FIXME_KILL_FOCUS so call on_change_field() inside wxEVT_TEXT + if (tmp_value < 0) { + if (m_on_kill_focus != nullptr) + m_on_kill_focus(m_opt_id); + } + else + on_change_field(); +#endif }), temp->GetId()); temp->SetToolTip(get_tooltip_text(text_value)); @@ -454,9 +465,24 @@ void Choice::BUILD() { } set_selection(); } - temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); +// temp->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); temp->Bind(wxEVT_COMBOBOX, ([this](wxCommandEvent e) { on_change_field(); }), temp->GetId()); + if (temp->GetWindowStyle() != wxCB_READONLY) { + temp->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { + e.Skip(); + double old_val = !m_value.empty() ? boost::any_cast(m_value) : -99999; + if (is_defined_input_value(window, m_opt.type)) { + if (fabs(old_val - boost::any_cast(get_value())) <= 0.0001) + return; + else + on_change_field(); + } + else + on_kill_focus(e); + }), temp->GetId()); + } + temp->SetToolTip(get_tooltip_text(temp->GetValue())); } @@ -666,7 +692,7 @@ boost::any& Choice::get_value() if (ret_enum < 0 || m_opt.enum_values.empty()) get_value_by_opt_type(ret_str); else - m_value = m_opt.enum_values[ret_enum]; + m_value = atof(m_opt.enum_values[ret_enum].c_str()); } else get_value_by_opt_type(ret_str); @@ -733,8 +759,11 @@ void PointCtrl::BUILD() temp->Add(new wxStaticText(m_parent, wxID_ANY, " y : "), 0, wxALIGN_CENTER_VERTICAL, 0); temp->Add(y_textctrl); - x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); - y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); +// x_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), x_textctrl->GetId()); +// y_textctrl->Bind(wxEVT_TEXT, ([this](wxCommandEvent e) { on_change_field(); }), y_textctrl->GetId()); + + x_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), x_textctrl->GetId()); + y_textctrl->Bind(wxEVT_KILL_FOCUS, ([this](wxEvent& e) { OnKillFocus(e, x_textctrl); }), y_textctrl->GetId()); // // recast as a wxWindow to fit the calling convention sizer = dynamic_cast(temp); @@ -743,6 +772,16 @@ void PointCtrl::BUILD() y_textctrl->SetToolTip(get_tooltip_text(X+", "+Y)); } +void PointCtrl::OnKillFocus(wxEvent& e, wxTextCtrl* win) +{ + e.Skip(); + if (!win->GetValue().empty()) { + on_change_field(); + } + else + on_kill_focus(e); +} + void PointCtrl::set_value(const Vec2d& value, bool change_event) { m_disable_change_event = !change_event; diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 0097d3ec0..4a19b6103 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -266,7 +266,6 @@ public: } boost::any& get_value() override; - bool is_defined_input_value() const ; virtual void enable(); virtual void disable(); @@ -395,6 +394,7 @@ public: void BUILD() override; + void OnKillFocus(wxEvent& e, wxTextCtrl* win); void set_value(const Vec2d& value, bool change_event = false); void set_value(const boost::any& value, bool change_event = false); boost::any& get_value() override; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 51735fe67..ab7141e91 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -484,7 +484,7 @@ Sidebar::Sidebar(Plater *parent) : wxPanel(parent), p(new priv(parent)) { p->scrolled = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(400, -1)); - p->scrolled->SetScrollbars(0, 1, 1, 1); + p->scrolled->SetScrollbars(0, 20, 1, 2); // Sizer in the scrolled area auto *scrolled_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index f46ad2142..0b59a21ab 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, 2); + page->SetScrollbars(1, 20, 1, 2); page->Hide(); m_hsizer->Add(page.get(), 1, wxEXPAND | wxLEFT, 5); From 1dc3145e69ea522228b7ce18795f28578ab44260 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 18 Dec 2018 15:29:09 +0100 Subject: [PATCH 029/132] Suppressed selection's update if SettingsItem for the current object/part is selected --- src/slic3r/GUI/GUI_ObjectList.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cba42470a..e7b84e289 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1399,6 +1399,20 @@ void ObjectList::update_selections() auto& selection = wxGetApp().plater()->canvas3D()->get_selection(); wxDataViewItemArray sels; + // We doesn't update selection if SettingsItem for the current object/part is selected + if (GetSelectedItemsCount() == 1 && m_objects_model->GetItemType(GetSelection()) == itSettings ) + { + const auto item = GetSelection(); + if (selection.is_single_full_object() && + m_objects_model->GetIdByItem(m_objects_model->GetParent(item)) == selection.get_object_idx()) + return; + if (selection.is_single_volume() || selection.is_modifier()) { + const auto gl_vol = selection.get_volume(*selection.get_volume_idxs().begin()); + if (m_objects_model->GetVolumeIdByItem(m_objects_model->GetParent(item)) == gl_vol->volume_idx()) + return; + } + } + if (selection.is_single_full_object()) { sels.Add(m_objects_model->GetItemById(selection.get_object_idx())); From 1e6900afa22f7a650a1a6801017b597cb1656407 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 15:55:45 +0100 Subject: [PATCH 030/132] Logging of memory usage for the GCodeAnalyzer and GCodePreviewData. --- src/libslic3r/GCode.cpp | 7 +++-- src/libslic3r/GCode/Analyzer.cpp | 11 ++++++++ src/libslic3r/GCode/Analyzer.hpp | 3 +++ src/libslic3r/GCode/PreviewData.cpp | 38 ++++++++++++++++++++++++++++ src/libslic3r/GCode/PreviewData.hpp | 12 +++++++++ src/libslic3r/GCodeTimeEstimator.cpp | 10 ++------ src/libslic3r/Utils.hpp | 7 ++++- 7 files changed, 77 insertions(+), 11 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ef143a3e8..e738662cb 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1644,8 +1644,11 @@ void GCode::process_layer( // printf("G-code after filter:\n%s\n", out.c_str()); _write(file, gcode); - BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " + - format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0); + BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << + ", time estimator memory: " << + format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) << + ", analyzer memory: " << + format_memsize_MB(m_analyzer.memory_used()); } void GCode::apply_print_config(const PrintConfig &print_config) diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index c56f02753..8212b1703 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -4,6 +4,7 @@ #include "../libslic3r.h" #include "../PrintConfig.hpp" +#include "../Utils.hpp" #include "Print.hpp" #include "Analyzer.hpp" @@ -852,6 +853,16 @@ void GCodeAnalyzer::_calc_gcode_preview_unretractions(GCodePreviewData& preview_ } } +// Return an estimate of the memory consumed by the time estimator. +size_t GCodeAnalyzer::memory_used() const +{ + size_t out = sizeof(*this); + for (const std::pair &kvp : m_moves_map) + out += sizeof(kvp) + SLIC3R_STDVEC_MEMSIZE(kvp.second, GCodeMove); + out += m_process_output.size(); + return out; +} + GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2) { return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), diff --git a/src/libslic3r/GCode/Analyzer.hpp b/src/libslic3r/GCode/Analyzer.hpp index f50138b56..389c11cec 100644 --- a/src/libslic3r/GCode/Analyzer.hpp +++ b/src/libslic3r/GCode/Analyzer.hpp @@ -120,6 +120,9 @@ public: // Calculates all data needed for gcode visualization void calc_gcode_preview_data(GCodePreviewData& preview_data); + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + static bool is_valid_extrusion_role(ExtrusionRole role); private: diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 3f2df9532..d4aa9bc02 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -2,6 +2,7 @@ #include "PreviewData.hpp" #include #include +#include "Utils.hpp" #include @@ -205,6 +206,18 @@ bool GCodePreviewData::Extrusion::is_role_flag_set(unsigned int flags, Extrusion return GCodeAnalyzer::is_valid_extrusion_role(role) && (flags & (1 << (role - erPerimeter))) != 0; } +size_t GCodePreviewData::Extrusion::memory_used() const +{ + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->layers, Layer); + for (const Layer &layer : this->layers) { + out += SLIC3R_STDVEC_MEMSIZE(layer.paths, ExtrusionPath); + for (const ExtrusionPath &path : layer.paths) + out += SLIC3R_STDVEC_MEMSIZE(path.polyline.points, Point); + } + return out; +} + const float GCodePreviewData::Travel::Default_Width = 0.075f; const float GCodePreviewData::Travel::Default_Height = 0.075f; const GCodePreviewData::Color GCodePreviewData::Travel::Default_Type_Colors[Num_Types] = @@ -224,6 +237,15 @@ void GCodePreviewData::Travel::set_default() is_visible = false; } +size_t GCodePreviewData::Travel::memory_used() const +{ + size_t out = sizeof(*this); + out += SLIC3R_STDVEC_MEMSIZE(this->polylines, Polyline); + for (const Polyline &polyline : this->polylines) + out += SLIC3R_STDVEC_MEMSIZE(polyline.polyline.points, Vec3crd); + return out; +} + const GCodePreviewData::Color GCodePreviewData::Retraction::Default_Color = GCodePreviewData::Color(1.0f, 1.0f, 1.0f, 1.0f); GCodePreviewData::Retraction::Position::Position(const Vec3crd& position, float width, float height) @@ -239,6 +261,11 @@ void GCodePreviewData::Retraction::set_default() is_visible = false; } +size_t GCodePreviewData::Retraction::memory_used() const +{ + return sizeof(*this) + SLIC3R_STDVEC_MEMSIZE(this->positions, Position); +} + void GCodePreviewData::Shell::set_default() { is_visible = false; @@ -483,4 +510,15 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: return items; } +// Return an estimate of the memory consumed by the time estimator. +size_t GCodePreviewData::memory_used() const +{ + return + this->extrusion.memory_used() + + this->travel.memory_used() + + this->retraction.memory_used() + + this->unretraction.memory_used() + + sizeof(shell) + sizeof(ranges); +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 9f882788d..8ed5e91c7 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -99,6 +99,9 @@ public: void set_default(); bool is_role_flag_set(ExtrusionRole role) const; + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; + static bool is_role_flag_set(unsigned int flags, ExtrusionRole role); }; @@ -144,6 +147,9 @@ public: size_t color_print_idx; void set_default(); + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; struct Retraction @@ -166,6 +172,9 @@ public: bool is_visible; void set_default(); + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; struct Shell @@ -199,6 +208,9 @@ public: std::string get_legend_title() const; LegendItemsList get_legend_items(const std::vector& tool_colors, const std::vector>& cp_values) const; + + // Return an estimate of the memory consumed by the time estimator. + size_t memory_used() const; }; GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2); diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 0ab49a345..81119c513 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -672,14 +672,8 @@ namespace Slic3r { size_t GCodeTimeEstimator::memory_used() const { size_t out = sizeof(*this); -#if WIN32 - #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) -#else - #define STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) -#endif - out += STDVEC_MEMSIZE(this->_blocks, Block); - out += STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); -#undef STDVEC_MEMSIZE + out += SLIC3R_STDVEC_MEMSIZE(this->_blocks, Block); + out += SLIC3R_STDVEC_MEMSIZE(this->_g1_line_ids, G1LineIdToBlockId); return out; } diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index f4f05ae66..047e03bf2 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -187,7 +187,12 @@ public: void reset() { closure = Closure(); } }; - } // namespace Slic3r +#if WIN32 + #define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + __alignof(TYPE) - 1) / __alignof(TYPE)) * __alignof(TYPE) +#else + #define SLIC3R_STDVEC_MEMSIZE(NAME, TYPE) NAME.capacity() * ((sizeof(TYPE) + alignof(TYPE) - 1) / alignof(TYPE)) * alignof(TYPE) +#endif + #endif // slic3r_Utils_hpp_ From e2d7fd941f6d53ba9dc40c8643f54f7529edc6eb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 16:32:11 +0100 Subject: [PATCH 031/132] Fixed OSX/Linux builds --- src/libslic3r/utils.cpp | 52 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 8081828c7..18e27f34b 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -366,6 +366,32 @@ std::string xml_escape(std::string text) return text; } +std::string format_memsize_MB(size_t n) +{ + std::string out; + size_t n2 = 0; + size_t scale = 1; + // Round to MB + n += 500000; + n /= 1000000; + while (n >= 1000) { + n2 = n2 + scale * (n % 1000); + n /= 1000; + scale *= 1000; + } + char buf[8]; + sprintf(buf, "%d", n); + out = buf; + while (scale != 1) { + scale /= 1000; + n = n2 / scale; + n2 = n2 % scale; + sprintf(buf, ",%03d", n); + out += buf; + } + return out + "MB"; +} + #ifdef WIN32 #ifndef PROCESS_MEMORY_COUNTERS_EX @@ -385,32 +411,6 @@ std::string xml_escape(std::string text) } PROCESS_MEMORY_COUNTERS_EX, *PPROCESS_MEMORY_COUNTERS_EX; #endif /* PROCESS_MEMORY_COUNTERS_EX */ -std::string format_memsize_MB(size_t n) -{ - std::string out; - size_t n2 = 0; - size_t scale = 1; - // Round to MB - n += 500000; - n /= 1000000; - while (n >= 1000) { - n2 = n2 + scale * (n % 1000); - n /= 1000; - scale *= 1000; - } - char buf[8]; - sprintf(buf, "%d", n); - out = buf; - while (scale != 1) { - scale /= 1000; - n = n2 / scale; - n2 = n2 % scale; - sprintf(buf, ",%03d", n); - out += buf; - } - return out + "MB"; -} - std::string log_memory_info() { std::string out; From 8bc04e640aae1b45de9f987b826e2e04aa0c601f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 17:34:21 +0100 Subject: [PATCH 032/132] The G-code export was reshuffled a bit to reduce peak memory consumption. Namely, the time estimate memory is released before the G-code preview data is created from the G-code analyser data. --- src/libslic3r/GCode.cpp | 40 ++++++++++++++++++++++------------------ src/libslic3r/GCode.hpp | 2 +- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index e738662cb..b13c38742 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -435,9 +435,11 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ if (file == nullptr) throw std::runtime_error(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n"); + m_enable_analyzer = preview_data != nullptr; + try { m_placeholder_parser_failed_templates.clear(); - this->_do_export(*print, file, preview_data); + this->_do_export(*print, file); fflush(file); if (ferror(file)) { fclose(file); @@ -453,15 +455,6 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ } fclose(file); - if (print->config().remaining_times.value) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; - m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - if (m_silent_time_estimator_enabled) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; - m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); - } - } - if (! m_placeholder_parser_failed_templates.empty()) { // G-code export proceeded, but some of the PlaceholderParser substitutions failed. std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n"; @@ -475,6 +468,24 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } + if (print->config().remaining_times.value) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode"; + m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + m_normal_time_estimator.reset(); + if (m_silent_time_estimator_enabled) { + BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode"; + m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + m_silent_time_estimator.reset(); + } + } + + // starts analyzer calculations + if (m_enable_analyzer) { + BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; + m_analyzer.calc_gcode_preview_data(*preview_data); + m_analyzer.reset(); + } + if (rename_file(path_tmp, path) != 0) throw std::runtime_error( std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + @@ -488,7 +499,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ PROFILE_OUTPUT(debug_out_path("gcode-export-profile.txt").c_str()); } -void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) +void GCode::_do_export(Print &print, FILE *file) { PROFILE_FUNC(); @@ -558,7 +569,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) // resets analyzer m_analyzer.reset(); - m_enable_analyzer = preview_data != nullptr; // resets analyzer's tracking data m_last_mm3_per_mm = GCodeAnalyzer::Default_mm3_per_mm; @@ -1034,12 +1044,6 @@ void GCode::_do_export(Print &print, FILE *file, GCodePreviewData *preview_data) _write(file, full_config); } print.throw_if_canceled(); - - // starts analyzer calculations - if (preview_data != nullptr) { - BOOST_LOG_TRIVIAL(debug) << "Preparing G-code preview data"; - m_analyzer.calc_gcode_preview_data(*preview_data); - } } std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override) diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index bf65311db..32a705751 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -180,7 +180,7 @@ public: static void append_full_config(const Print& print, std::string& str); protected: - void _do_export(Print &print, FILE *file, GCodePreviewData *preview_data); + void _do_export(Print &print, FILE *file); // Object and support extrusions of the same PrintObject at the same print_z. struct LayerToPrint From bb5caf2e08ca3be318501ff0580242e84d1e4355 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 18:41:20 +0100 Subject: [PATCH 033/132] Fixed scaling of the object, if it was loaded too big. Here the large object was not scaled uniformly, and the Z height of the bed was set incorrectly to one. --- src/slic3r/GUI/Plater.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ab7141e91..a261e7e05 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1526,9 +1526,8 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode if (max_ratio > 10000) { // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, // so scale down the mesh - // const Vec3d inverse = ratio.cwiseInverse(); - // object->scale(inverse); - object->scale(ratio.cwiseInverse()); + double inv = 1. / max_ratio; + object->scale(Vec3d(inv, inv, inv)); scaled_down = true; } else if (max_ratio > 5) { const Vec3d inverse = ratio.cwiseInverse(); From ec9caae6227d8a151bd673d1e1214f197c803d1a Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Tue, 18 Dec 2018 18:40:35 +0100 Subject: [PATCH 034/132] Http & ErrorDialog: Improve error reporting --- src/libslic3r/Utils.hpp | 1 + src/libslic3r/utils.cpp | 12 ++++++++++++ src/slic3r/GUI/MsgDialog.cpp | 22 ++++++++++++++++++++-- src/slic3r/GUI/MsgDialog.hpp | 6 +++++- src/slic3r/Utils/Http.cpp | 25 ++++++++++++++----------- src/slic3r/Utils/PrintHost.cpp | 11 ++++++++++- 6 files changed, 62 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 047e03bf2..cfae9edd1 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -11,6 +11,7 @@ namespace Slic3r { extern void set_logging_level(unsigned int level); +extern unsigned get_logging_level(); extern void trace(unsigned int level, const char *message); // Format memory allocated, separate thousands by comma. extern std::string format_memsize_MB(size_t n); diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 18e27f34b..f48abfd89 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -59,6 +59,18 @@ void set_logging_level(unsigned int level) ); } +unsigned get_logging_level() +{ + switch (logSeverity) { + case boost::log::trivial::fatal : return 0; + case boost::log::trivial::error : return 1; + case boost::log::trivial::warning : return 2; + case boost::log::trivial::info : return 3; + case boost::log::trivial::debug : return 4; + default: return 1; + } +} + // Force set_logging_level(<=error) after loading of the DLL. // Switch boost::filesystem to utf8. static struct RunOnInit { diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp index ae7b40484..d6b8b438e 100644 --- a/src/slic3r/GUI/MsgDialog.cpp +++ b/src/slic3r/GUI/MsgDialog.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "libslic3r/libslic3r.h" #include "libslic3r/Utils.hpp" @@ -61,8 +62,11 @@ MsgDialog::~MsgDialog() {} // ErrorDialog -ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : - MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG)) +ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) + : MsgDialog(parent, _(L("Slic3r error")), _(L("Slic3r has encountered an error")), + wxBitmap(from_u8(Slic3r::var("Slic3r_192px_grayscale.png")), wxBITMAP_TYPE_PNG), + wxID_NONE) + , msg(msg) { auto *panel = new wxScrolledWindow(this); auto *p_sizer = new wxBoxSizer(wxVERTICAL); @@ -77,6 +81,20 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg) : content_sizer->Add(panel, 1, wxEXPAND); + auto *btn_copy = new wxButton(this, wxID_ANY, _(L("Copy to clipboard"))); + btn_copy->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) { + if (wxTheClipboard->Open()) { + wxTheClipboard->SetData(new wxTextDataObject(this->msg)); // Note: the clipboard takes ownership of the pointer + wxTheClipboard->Close(); + } + }); + + auto *btn_ok = new wxButton(this, wxID_OK); + btn_ok->SetFocus(); + + btn_sizer->Add(btn_copy, 0, wxRIGHT, HORIZ_SPACING); + btn_sizer->Add(btn_ok); + SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT)); Fit(); } diff --git a/src/slic3r/GUI/MsgDialog.hpp b/src/slic3r/GUI/MsgDialog.hpp index ca349eb5c..6064d2a9f 100644 --- a/src/slic3r/GUI/MsgDialog.hpp +++ b/src/slic3r/GUI/MsgDialog.hpp @@ -50,14 +50,18 @@ protected: // Generic error dialog, used for displaying exceptions -struct ErrorDialog : MsgDialog +class ErrorDialog : public MsgDialog { +public: ErrorDialog(wxWindow *parent, const wxString &msg); ErrorDialog(ErrorDialog &&) = delete; ErrorDialog(const ErrorDialog &) = delete; ErrorDialog &operator=(ErrorDialog &&) = delete; ErrorDialog &operator=(const ErrorDialog &) = delete; virtual ~ErrorDialog(); + +private: + wxString msg; }; diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 67c24f3f4..6e6c9ed44 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -11,6 +11,7 @@ #include #include "libslic3r/libslic3r.h" +#include "libslic3r/Utils.hpp" namespace fs = boost::filesystem; @@ -44,6 +45,7 @@ struct Http::priv // Using a deque here because unlike vector it doesn't ivalidate pointers on insertion std::deque form_files; std::string postfields; + std::string error_buffer; // Used for CURLOPT_ERRORBUFFER size_t limit; bool cancel; @@ -69,13 +71,14 @@ struct Http::priv void http_perform(); }; -Http::priv::priv(const std::string &url) : - curl(::curl_easy_init()), - form(nullptr), - form_end(nullptr), - headerlist(nullptr), - limit(0), - cancel(false) +Http::priv::priv(const std::string &url) + : curl(::curl_easy_init()) + , form(nullptr) + , form_end(nullptr) + , headerlist(nullptr) + , error_buffer(CURL_ERROR_SIZE + 1, '\0') + , limit(0) + , cancel(false) { if (curl == nullptr) { throw std::runtime_error(std::string("Could not construct Curl object")); @@ -83,6 +86,7 @@ Http::priv::priv(const std::string &url) : ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); + ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); } Http::priv::~priv() @@ -199,9 +203,10 @@ void Http::priv::set_post_body(const fs::path &path) std::string Http::priv::curl_error(CURLcode curlcode) { - return (boost::format("%1% (%2%)") + return (boost::format("%1% (%2%): %3%") % ::curl_easy_strerror(curlcode) % curlcode + % error_buffer ).str(); } @@ -227,9 +232,7 @@ void Http::priv::http_perform() ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast(this)); #endif -#ifndef NDEBUG - ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); -#endif + ::curl_easy_setopt(curl, CURLOPT_VERBOSE, get_logging_level() >= 4); if (headerlist != nullptr) { ::curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 863da5d43..cdd0c107e 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -144,6 +145,10 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) { if (bg_exit || the_job.empty()) { return; } + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`") + % the_job.upload_data.upload_path + % the_job.printhost->get_host(); + const fs::path gcode_path = the_job.upload_data.source_path; the_job.printhost->upload(std::move(the_job.upload_data), @@ -154,7 +159,11 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); wxQueueEvent(queue_dialog, evt); - fs::remove(gcode_path); // XXX: error handling + boost::system::error_code ec; + fs::remove(gcode_path, ec); + if (ec) { + BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % gcode_path % ec; + } } void PrintHostJobQueue::enqueue(PrintHostJob job) From 76c922bf9ab80a4d40ca10d1cd54443a4dcecf69 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 18 Dec 2018 19:12:59 +0100 Subject: [PATCH 035/132] Fixed a crash when trying to delete a wipe tower with the delete key. --- src/slic3r/GUI/GUI_ObjectList.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index e7b84e289..5e20ceaab 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -982,6 +982,10 @@ void ObjectList::del_instances_from_object(const int obj_idx) bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, const int type) { + if (obj_idx == 1000) + // Cannot delete a wipe tower. + return false; + if (type == itVolume) { const auto volume = (*m_objects)[obj_idx]->volumes[idx]; From 9d8e78636b452af89182976a0ad969f76b90fe0a Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 09:54:15 +0100 Subject: [PATCH 036/132] Fixed loading of huge models --- src/libslic3r/Model.cpp | 16 ++++++++++++++++ src/libslic3r/Model.hpp | 5 +++++ src/slic3r/GUI/Plater.cpp | 6 ++++-- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 7ebcbd815..e56a4b6e1 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -983,6 +983,16 @@ void ModelObject::mirror(Axis axis) this->invalidate_bounding_box(); } +void ModelObject::scale_mesh(const Vec3d &versor) +{ + for (ModelVolume *v : this->volumes) + { + v->scale_geometry(versor); + v->set_offset(versor.cwiseProduct(v->get_offset())); + } + this->invalidate_bounding_box(); +} + size_t ModelObject::materials_count() const { std::set material_ids; @@ -1514,6 +1524,12 @@ void ModelVolume::mirror(Axis axis) #endif // ENABLE_MODELVOLUME_TRANSFORM } +void ModelVolume::scale_geometry(const Vec3d& versor) +{ + mesh.scale(versor); + m_convex_hull.scale(versor); +} + #if !ENABLE_MODELVOLUME_TRANSFORM void ModelInstance::set_rotation(const Vec3d& rotation) { diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index b396bd1db..c26eb0f7a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -237,6 +237,9 @@ public: void rotate(double angle, Axis axis); void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + + void scale_mesh(const Vec3d& versor); + size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; @@ -331,6 +334,8 @@ public: void rotate(double angle, const Vec3d& axis); void mirror(Axis axis); + void scale_geometry(const Vec3d& versor); + #if ENABLE_MODELVOLUME_TRANSFORM // translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box void center_geometry(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a261e7e05..118fb41c8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1494,7 +1494,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode #if !ENABLE_MODELVOLUME_TRANSFORM const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); #endif // !ENABLE_MODELVOLUME_TRANSFORM - const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0); + const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); bool need_arrange = false; bool scaled_down = false; @@ -1527,7 +1527,9 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, // so scale down the mesh double inv = 1. / max_ratio; - object->scale(Vec3d(inv, inv, inv)); + object->scale_mesh(Vec3d(inv, inv, inv)); + object->origin_translation = Vec3d::Zero(); + object->center_around_origin(); scaled_down = true; } else if (max_ratio > 5) { const Vec3d inverse = ratio.cwiseInverse(); From d922260b73b90432cdbf5e9df8d49104068694ac Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 11:59:59 +0100 Subject: [PATCH 037/132] Do not clear selection if left-clicking out of objects while shift is down --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 32bbb32d6..e01e1dbbe 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5200,7 +5200,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftUp() && !m_mouse.dragging && (m_hover_volume_id == -1) && !gizmos_overlay_contains_mouse && !m_gizmos.is_dragging() && !is_layers_editing_enabled()) { // deselect and propagate event through callback - if (m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) + if (!evt.ShiftDown() && m_picking_enabled && !m_toolbar_action_running && !m_mouse.ignore_up_event) { m_selection.clear(); m_selection.set_mode(Selection::Instance); From d414ef33950565230d756e7c2a099d5f3132d60c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 19 Dec 2018 12:02:17 +0100 Subject: [PATCH 038/132] Fix of G-code remaining times export, that I broke with my optimizations. --- src/libslic3r/GCodeTimeEstimator.cpp | 32 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 81119c513..461b4cd35 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -290,7 +290,8 @@ namespace Slic3r { // buffer line to export only when greater than 64K to reduce writing calls std::string export_line; char time_line[64]; - while (std::getline(in, gcode_line)) + G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); + while (std::getline(in, gcode_line)) { if (!in.good()) { @@ -309,7 +310,6 @@ namespace Slic3r { gcode_line += "\n"; // add remaining time lines where needed - G1LineIdToBlockIdMap::const_iterator it_line_id = _g1_line_ids.begin(); _parser.parse_line(gcode_line, [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) { @@ -317,23 +317,23 @@ namespace Slic3r { { ++g1_lines_count; - if (!line.has_e()) - return; + assert(it_line_id == _g1_line_ids.end() || it_line_id->first >= g1_lines_count); - if ((it_line_id != _g1_line_ids.end()) && (it_line_id->first == g1_lines_count) && (it_line_id->second < (unsigned int)_blocks.size())) - { - const Block& block = _blocks[it_line_id->second]; - ++ it_line_id; - if (block.elapsed_time != -1.0f) + const Block *block = nullptr; + if (it_line_id != _g1_line_ids.end() && it_line_id->first == g1_lines_count) { + if (line.has_e() && it_line_id->second < (unsigned int)_blocks.size()) + block = &_blocks[it_line_id->second]; + ++it_line_id; + } + + if (block != nullptr && block->elapsed_time != -1.0f) { + float block_remaining_time = _time - block->elapsed_time; + if (std::abs(last_recorded_time - block_remaining_time) > interval) { - float block_remaining_time = _time - block.elapsed_time; - if (std::abs(last_recorded_time - block_remaining_time) > interval) - { - sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block.elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); - gcode_line += time_line; + sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / _time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); + gcode_line += time_line; - last_recorded_time = block_remaining_time; - } + last_recorded_time = block_remaining_time; } } } From a6a1a866d811b405f201d770334f0fbc44e02296 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 19 Dec 2018 12:07:45 +0100 Subject: [PATCH 039/132] Fix of SPE-695 Minus key should not delete object --- src/slic3r/GUI/Plater.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a261e7e05..90a2744ee 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1186,7 +1186,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); - view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) + { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); @@ -1212,7 +1213,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) canvas3Dwidget->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [q](Event &evt) { evt.data == 1 ? q->increase_instances() : q->decrease_instances(); }); + canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) + { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); canvas3Dwidget->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); canvas3Dwidget->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); From 661086554af7d255f51438e3d4e26d0a70241655 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 13:06:24 +0100 Subject: [PATCH 040/132] Added "Keyboard Shortcuts" dialog --- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_App.cpp | 8 ++ src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/KBShortcutsDialog.cpp | 152 +++++++++++++++++++++++++++ src/slic3r/GUI/KBShortcutsDialog.hpp | 32 ++++++ src/slic3r/GUI/MainFrame.cpp | 3 + 6 files changed, 198 insertions(+) create mode 100644 src/slic3r/GUI/KBShortcutsDialog.cpp create mode 100644 src/slic3r/GUI/KBShortcutsDialog.hpp diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 3b6bad16d..d79421439 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -10,6 +10,8 @@ add_library(libslic3r_gui STATIC GUI/AboutDialog.hpp GUI/SysInfoDialog.cpp GUI/SysInfoDialog.hpp + GUI/KBShortcutsDialog.cpp + GUI/KBShortcutsDialog.hpp GUI/AppConfig.cpp GUI/AppConfig.hpp GUI/BackgroundSlicingProcess.cpp diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index e4db9b6e1..88c77c193 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -35,6 +35,7 @@ #include "Preferences.hpp" #include "Tab.hpp" #include "SysInfoDialog.hpp" +#include "KBShortcutsDialog.hpp" namespace Slic3r { namespace GUI { @@ -301,6 +302,13 @@ void GUI_App::system_info() dlg.Destroy(); } +void GUI_App::keyboard_shortcuts() +{ + KBShortcutsDialog dlg; + dlg.ShowModal(); + dlg.Destroy(); +} + // static method accepting a wxWindow object as first parameter bool GUI_App::catch_error(std::function cb, // wxMessageDialog* message_dialog, diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 3c2b4a21f..81175b7ca 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -115,6 +115,7 @@ public: void recreate_GUI(); void system_info(); + void keyboard_shortcuts(); void load_project(wxWindow *parent, wxString& input_file); void import_model(wxWindow *parent, wxArrayString& input_files); static bool catch_error(std::function cb, diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp new file mode 100644 index 000000000..25419b438 --- /dev/null +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -0,0 +1,152 @@ +#include "KBShortcutsDialog.hpp" +#include "I18N.hpp" +#include "..\libslic3r\Utils.hpp" +#include "GUI.hpp" +#include + +namespace Slic3r { +namespace GUI { + +KBShortcutsDialog::KBShortcutsDialog() + : wxDialog(NULL, wxID_ANY, _(L("Slic3r Prusa Edition - Keyboard Shortcuts")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER) +{ + SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); + + auto main_sizer = new wxBoxSizer(wxVERTICAL); + + // logo + wxBitmap logo_bmp = wxBitmap(from_u8(Slic3r::var("Slic3r_32px.png")), wxBITMAP_TYPE_PNG); + + // fonts + wxFont head_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); + head_font.SetPointSize(19); + + wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); + font.SetPointSize(10); + const wxFont bold_font = font.Bold(); +#ifdef __WXOSX__ + font.SetPointSize(12); + bold_font.SetPointSize(14); +#endif /*__WXOSX__*/ + + fill_shortcuts(); + + auto panel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(500, 600)); + panel->SetScrollbars(0, 20, 1, 2); + auto sizer = new wxBoxSizer(wxVERTICAL); + panel->SetSizer(sizer); + main_sizer->Add(panel, 1, wxEXPAND | wxALL, 0); + + for (auto& sc : m_full_shortcuts) + { + wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(hsizer, 0, wxEXPAND | wxTOP, 25); + + // logo + auto *logo = new wxStaticBitmap(panel, wxID_ANY, logo_bmp); + hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 15); + + // head + wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(400,-1)); + head->SetFont(head_font); + hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL); + + // Shortcuts list + auto grid_sizer = new wxFlexGridSizer(2, 10, 25); + sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT | wxTOP, 10); + + for (auto pair : sc.second) + { + auto shortcut = new wxStaticText(panel, wxID_ANY, _(pair.first)); + shortcut->SetFont(bold_font); + grid_sizer->Add(shortcut, -1, wxALIGN_CENTRE_VERTICAL); + + auto description = new wxStaticText(panel, wxID_ANY, _(pair.second)); + description->SetFont(font); + grid_sizer->Add(description, -1, wxALIGN_CENTRE_VERTICAL); + } + } + + wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); + + this->SetEscapeId(wxID_CLOSE); + this->Bind(wxEVT_BUTTON, &KBShortcutsDialog::onCloseDialog, this, wxID_OK); + main_sizer->Add(buttons, 0, wxEXPAND | wxALL, 15); + + this->Bind(wxEVT_LEFT_DOWN, &KBShortcutsDialog::onCloseDialog, this); + + SetSizer(main_sizer); + main_sizer->SetSizeHints(this); +} + +void KBShortcutsDialog::fill_shortcuts() +{ + Shortcuts main_shortcuts; + main_shortcuts.reserve(25); + + main_shortcuts.push_back(Shortcut("Ctrl+O", L("Open project STL/OBJ/AMF/3MF with config, delete bed"))); + main_shortcuts.push_back(Shortcut("Ctrl+I", L("Import STL//OBJ/AMF/3MF without config, keep bed"))); + main_shortcuts.push_back(Shortcut("Ctrl+L", L("Load Config from .ini/amf/3mf/gcode"))); + main_shortcuts.push_back(Shortcut("Ctrl+Alt+L", L("Load Config from .ini/amf/3mf/gcode and merge"))); + main_shortcuts.push_back(Shortcut("Ctrl+G", L("Export Gcode"))); + main_shortcuts.push_back(Shortcut("Ctrl+S", L("Save project (3MF)"))); + main_shortcuts.push_back(Shortcut("Ctrl+R", L("(Re)slice"))); + main_shortcuts.push_back(Shortcut("Ctrl+U", L("Quick slice"))); + main_shortcuts.push_back(Shortcut("Ctrl+Alt+U", L("Quick slice and Save as"))); + main_shortcuts.push_back(Shortcut("Ctrl+Shift+U", L("Repeat last quick slice"))); + main_shortcuts.push_back(Shortcut("Ctrl+1", L("Select Plater Tab"))); + main_shortcuts.push_back(Shortcut("Ctrl+2", L("Select Print Settings Tab"))); + main_shortcuts.push_back(Shortcut("Ctrl+3", L("Select Filament Setting Tab"))); + main_shortcuts.push_back(Shortcut("Ctrl+4", L("Select Printer Setting Tab"))); + main_shortcuts.push_back(Shortcut("Ctrl+5", L("Switch to 3D"))); + main_shortcuts.push_back(Shortcut("Ctrl+6", L("Switch to Preview"))); + main_shortcuts.push_back(Shortcut("Ctrl+P", L("Preferences"))); + main_shortcuts.push_back(Shortcut("0-6", L("Camera view "))); + main_shortcuts.push_back(Shortcut("+", L("Add Instance to selected object "))); + main_shortcuts.push_back(Shortcut("-", L("Remove Instance from selected object"))); + main_shortcuts.push_back(Shortcut("PgUp/PgDn", L("Switch between 3D and Preview"))); + main_shortcuts.push_back(Shortcut("Shift+LeftMouse",L("Select multiple object/Move multiple object"))); + + m_full_shortcuts.emplace(_(L("Main Shortcuts")), main_shortcuts); + + + Shortcuts plater_shortcuts; + plater_shortcuts.reserve(20); + + plater_shortcuts.push_back(Shortcut("A", L("Arrange"))); + plater_shortcuts.push_back(Shortcut("Ctrl+A", L("Select All objects"))); + plater_shortcuts.push_back(Shortcut("Del", L("Delete selected"))); + plater_shortcuts.push_back(Shortcut("Ctrl+Del", L("Delete all"))); + plater_shortcuts.push_back(Shortcut("M", L("Gizmo move"))); + plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale"))); + plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate"))); + plater_shortcuts.push_back(Shortcut("C", L("Gizmo cut"))); + plater_shortcuts.push_back(Shortcut("F", L("Gizmo Place face on bed"))); + plater_shortcuts.push_back(Shortcut("L", L("Gizmo SLA support points"))); + plater_shortcuts.push_back(Shortcut("B", L("Zoom to Bed"))); + plater_shortcuts.push_back(Shortcut("Z", L("Zoom to all objects in scene, if none selected"))); + plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object"))); + plater_shortcuts.push_back(Shortcut("I", L("Zoom in"))); + plater_shortcuts.push_back(Shortcut("O", L("Zoom out"))); + plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo, keep object selection"))); + + m_full_shortcuts.emplace(_(L("Plater Shortcuts")), plater_shortcuts); + + + Shortcuts preview_shortcuts; + preview_shortcuts.reserve(2); + + preview_shortcuts.push_back(Shortcut(L("Arrow Up"), L("Upper Layer"))); + preview_shortcuts.push_back(Shortcut(L("Arrow Down"), L("Lower Layer"))); + + m_full_shortcuts.emplace(_(L("Preview Shortcuts")), preview_shortcuts); +} + +void KBShortcutsDialog::onCloseDialog(wxEvent &) +{ + this->EndModal(wxID_CLOSE); + this->Close(); +} + +} // namespace GUI +} // namespace Slic3r diff --git a/src/slic3r/GUI/KBShortcutsDialog.hpp b/src/slic3r/GUI/KBShortcutsDialog.hpp new file mode 100644 index 000000000..8517544b5 --- /dev/null +++ b/src/slic3r/GUI/KBShortcutsDialog.hpp @@ -0,0 +1,32 @@ +#ifndef slic3r_GUI_KBShortcutsDialog_hpp_ +#define slic3r_GUI_KBShortcutsDialog_hpp_ + +#include +#include + +namespace Slic3r { +namespace GUI { + +class KBShortcutsDialog : public wxDialog +{ + typedef std::pair Shortcut; + typedef std::vector< Shortcut > Shortcuts; + typedef std::map ShortcutsMap; + + wxString text_info {wxEmptyString}; + + ShortcutsMap m_full_shortcuts; + +public: + KBShortcutsDialog(); + + void fill_shortcuts(); + +private: + void onCloseDialog(wxEvent &); +}; + +} // namespace GUI +} // namespace Slic3r + +#endif diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 4d4ee17ae..2211023f0 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -432,6 +432,9 @@ void MainFrame::init_menubar() [this](wxCommandEvent&) { wxLaunchDefaultBrowser("http://github.com/prusa3d/slic3r/issues/new"); }); append_menu_item(helpMenu, wxID_ANY, _(L("About Slic3r")), _(L("Show about dialog")), [this](wxCommandEvent&) { Slic3r::GUI::about(); }); + helpMenu->AppendSeparator(); + append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + "\t?", _(L("Show the list of the keyboard shortcuts")), + [this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); } // menubar From d46fd6d9576938297315f44328500344314df7aa Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 13:54:27 +0100 Subject: [PATCH 041/132] New icons --- resources/icons/overlay/cut_hover.png | Bin 1382 -> 1777 bytes resources/icons/overlay/cut_off.png | Bin 1395 -> 1773 bytes resources/icons/overlay/cut_on.png | Bin 1393 -> 2673 bytes resources/icons/overlay/layflat_hover.png | Bin 2249 -> 2071 bytes resources/icons/overlay/layflat_off.png | Bin 2098 -> 2018 bytes resources/icons/overlay/layflat_on.png | Bin 2090 -> 2599 bytes resources/icons/overlay/move_hover.png | Bin 2081 -> 2022 bytes resources/icons/overlay/move_off.png | Bin 2073 -> 1909 bytes resources/icons/overlay/move_on.png | Bin 1917 -> 2410 bytes resources/icons/overlay/rotate_hover.png | Bin 2314 -> 2361 bytes resources/icons/overlay/rotate_off.png | Bin 2091 -> 2132 bytes resources/icons/overlay/rotate_on.png | Bin 2097 -> 3307 bytes resources/icons/overlay/scale_hover.png | Bin 2459 -> 1837 bytes resources/icons/overlay/scale_off.png | Bin 2452 -> 1776 bytes resources/icons/overlay/scale_on.png | Bin 2449 -> 2243 bytes resources/icons/toolbar.png | Bin 15742 -> 17622 bytes resources/icons/toolbar141.png | Bin 0 -> 23453 bytes resources/icons/view_toolbar.png | Bin 21977 -> 11913 bytes 18 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 resources/icons/toolbar141.png diff --git a/resources/icons/overlay/cut_hover.png b/resources/icons/overlay/cut_hover.png index eccce02430a0914f7f195992cc920048c5bf37ba..5efdefc5e88343099779b989b1ff242e041ec20c 100644 GIT binary patch delta 1031 zcmV+i1o->r3h@oF9s*!wWj8Z0Wj19kI5TE4Ei^DRW-T~3F)=M;V`XMyGGR6|GB{x( zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FM^Gch$VH#kN{LP0|@F-15*ML9+}G(ki| zLqSG0F*7ieegYgIMl&%rFgG|wMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144flD>1tO00RL@L_t(|+U%P>XcR#d$Fp|{ z7e5FjxeJ6yND~N>B0`EZL9sCqgfsyqAmP9&g?NfKf~2ufQ7mjMECf-ou@Rz$8UkV= zXc5FRg@O=24?XWlZqMJqD^AhlHv4h2w{PLcPTszmdHc`qeC?c5tJOM^e55)OeyD!{ zDu4>00wnt|-QfQSu}%k;;HzNLPD}1FY5^#B7@k21&Il#34Y#cT@?Sp*MOcH;x@)6i zD~gZPRsvAg5Ili06d?5vpOjoHi|6tv+bLyjN!bHd3V{DS zY{DCu2%#&Rkg`o3^VSG}Ulty~HY`I|hy$7~sbgE}%(hejeCFUItixo4qt{8P)6_i| zxd2@2gS$|LOOR>75I!SyS0$FdNCdz>3m@PeOj$6OnUYvcOtYa0Abs;KaN&OnbXzo` z>z0^Yi7_390D1Th(-zNcrzOUGvjTWo4=@9oRmXAGY!yIaTa{P?Ul1|AZVZ+w z!Zt(tnE2kq_88k<(pTXZoYr1IUjhH!U~C(~K*j-a>IRARk8UuifzW?Zcma9K1-LD- z1}=bUxr->fVfp)Blvs5xpaQ4>!3yBUL@-Y)S6*&t9S+*QC~9JqSeqXu>9V+f81ejR ztk=R%W4$|i%Jt)@|Ig$7E&M#*@4KDT4HCg_k;z4{MJBg{GIW!aZj;lElDbt^H%sew zc^d?%dm60x!GxnfoYW%~Ar(LcPytkc$_i>(q+L@y;X#mdyl)ftE!U7Zb_4Gmo_ER5Y;T%8S#oJ^dIOx!Gu zCf6~^C_oii;#9PYDNQ9QG1=HOEipwm+1S`b*CZ{;Lf6vF%v9GT*)Y+>ILXw)$jD@} z3$vDrg<+Dhp_!$zuBA~*qOOIpNt&)@YO=9zl3`M!S)!@2SyH0qraR~7?W^-yMlq6{{d?YV>YX8Tj&X1 zlh)iVoE|#Y%-@M5PX4R){p|mP$Wk$gB%DyRUM7%AX6tCyO0_=zfnb&wtoOWO;-SN}S6CBP2qc^cWk3TBN^GK^AAY?U6S=-ScXD)mXJ+5F^+zn&85kTE zg*VQBy?ELFZvXFccJ|j^9sXA6`*-P;{l{)GOYW1h`ycRXfAZh@RvvxRt;H*CBY*29 z{@(lWvu-s5!yWbwKm!;UuKG9M`yDMgXH(qX;&Eg2SPY b>IXy_qA!I#Z!%NL0h#IP>gTe~DWM4fXJhO; diff --git a/resources/icons/overlay/cut_off.png b/resources/icons/overlay/cut_off.png index 4d0f8c93f0e9c5d8cdc887e1be907f4d6a29d3f7..3e442101c0971058d03cda7ffbbc309855c5cfa0 100644 GIT binary patch delta 1027 zcmV+e1pNE+3hfQB9s*!wWj8Z0Wj19kI5TE4Ei^DRW-T~3F)=M;V`XMyGGR6|GB{x( zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FM^Gch$VH#kB@LP0|@F-15*ML9+}G(ki| zLqSG0F*7ieegYgIMl&%rFgG|sMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144gQbyV>H00R9v!Q4gJ|5zJoe7c8Tgo+Gw0rW=FiN1&0VKnueYW7$h4*WPyv5b z02M$5NcUm7!G959oerFbuYyTCO}WRo1)$tPcm`WAFKmfzuw@01|N1#7!#WIkt__K; zEIv+42|!r`@B}JQg3MohGIFgVz5^{G0OicVH+Tud4fqd>ugNo)SOLi2509VVME`-N7CuMC**?m?Dfd33^ z!W$Tkpeq}dvP~T`)(C)KFFb%9ScLWn2Q=+c$Bxw5+f)JYnTAiW0b?Su_itrsKES}j;NQ}is1@N;TU7a0ig(x)MK}M%>92S<1J46U;#0H@b^j5XXNsIY^&JzlfDUNX#53RN%EfM zbV7WWvA@Q4i1Z?uq3T;~*Twe|rz7(2Ud8?a+fmXNB>z)vcf{A!WhOoqC}B5C>XW2T zi|;*bkFgyjy#&ADqV@v%3Iy*46Wa)eG7d;mH)z@fZ#Nj$K;R@g;L@-|~SAK421CH6gC~9JqSQ{TD>9V+f81emStjoer zV_kcC%Jt)D@XzBtP5eCG6S$qz4HDsQktxKmMW(QaGIW!aZj;lElDbt^H%sewc^d@q xJPlU-V8YQKPU?z~3ZNeXr~oQJdXL`%3;=asyLiOpHxB>+002ovPDHLkV1mEVs2KnN delta 646 zcmaFM`$_i>(q+L@y;X#mdyl)ftG)T%8S^4Gmo_ER5Y;T%8S#oJ^dIOx!Gu zCf6~^C_ojN;#9PYDNQ9QG1=HOEipwm+1S`b*CZ{;Lf6vF%v9GT*)Y+>ILXw)$jD@} z3$vDrg<+Dhp_!$zuBA~*qOOIpNt&)@YO=9zl3`M!S)!@2SyH0q}3|Ln>~)xxL@(u!GEjk8>=d-bC!$VX>3rk;aXkB6%0gS0u_7|+(aJ|WZmyyGkwQXYU z=Js334(3J;>&zH7Lk)sZD&K4t^u(MpT2%LRVW;DH<|N*;&l!6d8J1Xa+?%^KE~~88 z?#$=eUiTJ<95>y`d@^=wrSVF>ZBN#yqz63v@z=PutXgXGfz^PXgHS#89-?^@3PjEMEec5V8jw)Cn&>DsBcQl(M^SQr}q-{+3Vp1;(pb4C37 zb36AvJ3Hb3(x)0q4f%eIDDHD$P7sXTFLsMz@(BjDRSf^O2CsCxd4Kng-7^1vxIO*I n*7`6$nWsq!rpIB?N96+ycT{68zEixE3KI2n^>bP0l+XkK^7!;z diff --git a/resources/icons/overlay/cut_on.png b/resources/icons/overlay/cut_on.png index 967d1545858b99150f1b22d473f18ea138ba922a..c1f7bf62dd3f462dd00113a6e0a371c179c776e3 100644 GIT binary patch delta 1934 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 diff --git a/resources/icons/overlay/layflat_hover.png b/resources/icons/overlay/layflat_hover.png index 31ee91f46d283bcfc136535723fef614656eb32d..4db9d63d92224cc4476724303a2b63d423667c7d 100644 GIT binary patch delta 1327 zcmV+~19rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144h;Z4*-f00bvVL_t(|+U%NLNK`=-$9K0S z=%o;pw$@@1tEH9lK?f+WB#PzNHO$N?Px3AEcR*iL`ckUxmUU^pd*BwUpP&|UycrN+OcwxghN4iC8j zN?Z{FXs_H%{7V+358TUSV^HCigIskGt~ud!N9bb!)c_3Kih9Z|JHa@|r>YGeC9tZj zeX0lnL;UYiC_ov9TsV`Bsytu~C{XDE>~Zf94r6Wzfl7}5akqt>N&(y#|0;iS01XCA zfLetDtmgPH&Ss+u4~T(6(5=w@D>?p0-4FtCvxAY`=TkFZl)4f6*c7Qjbqp*lyqt*T zV*NMtc~Y4GFnm6$+h7N{06J5W(NTr*ALYYQml6SBJQ>*mHiGq-`PYVzS8@ESUjK)U zr$MJZ-~`wPHl!Gz%lJ1fctC$}_E(}hOD+!M`_l}jTXFnvQYHXO3wQzJuZd=3N%2ot zWlOCH5EPjG5>&f*%@s~Y65W5yZ2XIQzyZ*Q*?%r08RhE8Eh0cQFMa7sl`l8zzw-jX z{TJ~O_pz*GBq{#GR{IQEFgk>a45#z;azN|uUfR_k|KMsFtP{r|Y>5Y=9 zYjGG~z=kj2kEX#4=mj4Dt@EA82R()vArEO_x1??H7hV#(oDhWG5s>I;bKhJiUQrMD z0e*qgzJKrZ$Qz1z$yoww+0b5GkgG!@D+N%LVFQzBWl$~({F}&*Ea|% z;Of2nEWjI`-(U-#CvCcK$oK!79y&vO0?!N~lh#A%^%$yi_&k(O0O3^t#tpIiHyIuu z0&78?UE^8kanKGLL6pOaJc{lR8!2Cq-Wfc^_L1AtyFtwOPE>z)c)6kM-Ukk6*b2)^ zaM?VB4rFIA-m6wUz^VY0G-+qo%G=;?V7tid+Y}_s2{q39jY+zTz<4Je6@W#cmEaWk z3Cdvnh|lFk#xiIu7kL+W-3!J`%>iC5wmksuz86&uZ+ksyJGc+-+6{r5pbfnDr~u2@ z2HOJAZSN(-`YTH>S`QB{rvsVjX)uO{yc+&7(c^Fjhk%z$hwW7$ABz6BxE4SQpaswZ lXaTeUS^zCTzLviN3;^L7`a^P$h|B;0002ovPDHLkV1hKFIWzzO delta 1506 zcmV<81s(dA5Xlj+9s*!vGBP+cIc72~G%;o{Ei^S`GA&|aWn?WhWM*PxG+}0CVly=& zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FM@MKU=?MK?o5Loh=zF-15yGe$&3LoqT! zG(s{oMmaK*egYgIMMW|>MnyM5MME${F)>9rI5S2>MME(%LNr1$G)6fxlez+CBrrB& zH#0Y6H!WpjVPP#aI5=W0I5;siEo5e9Vq-8hV>L1~W|Kq%DkL~EVP-jDGd3+{Ibt*| zF*h_YEjT%4H!UzTIXN|9VPa-9W;m0Q144hv%4bSj%7i7zXomn8-Lv(-S*G#SFkLJe9- zjH8wW(MHPTpMMcGxFbF4)d69cA1YiJ&3+eAbiR1Df zumD5s3DW?XN`X~L2ORr>0fH{B&~VmDX;##lr?rUz^P+hM%lPbF&iHc1`cKV_;OupB&Q z5&3g^kK(@|g7=?7EwIe{OYr_RBL+YgsYDsd7LgCG8{FLJ)&Ngi=KX*9&KiIT1GHL1 zejViZy1m)00X_nkEg*jtEKBDBR|deM$5G?Jjx0C88s7C>T6<&p!sj)u*@^WJ2bH9z; zHv;gwYg%jpnspi7_JU4=}J<$N*skVwWXfo<+zNibpQrtGZ~XIbHHPEiQu^9bMmeBrnHf7MseWE(bv( z;ak+yq-QP&0Stfem*38bh*G_*9+}2tA7jL7uYIOF2)?ZeJ7um3@;V|Uhezvb(?u7L z`~%)KIg+}W24|tx>pz0wjA(cmRSPOr=ZCB+FAuJ1@W+3Cjyr3p@ zh@A)0zLed6xX&$e?Mu`Bb~XzPQol+$2G<={d+-{;!@6Um3sqTcmquBJ+XsLIWMrpnXYyw?L% zj#?#IZsaiE8*T?d8T9gVjXZ;3d&)tuCk1*8&|`pZCI1UB0E}$qS6MGN5C8xG07*qo IM6N<$f_y}r=>Px# diff --git a/resources/icons/overlay/layflat_off.png b/resources/icons/overlay/layflat_off.png index 4feadbaf4db2d3e339a4259d748dfa9ec4d981b2..393dfb37f05adab834eb529b819e74312402629a 100644 GIT binary patch delta 1274 zcmV9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144gA8V&&f00Z(#L_t(|+U%Q4XcJKshLc)+ ztZJaeM{5ZZYcMK7#f4}aQ4uXJ6bmX{NYyIh!iBC~*>#uVLIr~~YSD#DACV~1g-Crs z6onKM4U`tF8c;CtKlBuaA(_mb$IT4610O>(ckbh#^WV9TX|PhM1e*D12{iek1W8VT0IrSkeg3FJW%oCc9bwJ~Va&|om=Mr9u1^b zzy|hzSG_jMh=3Sq2N{Xs-^>2bd%*$;vz2kU%;(Zqgb-2#;1&4LsQ167%#7Je+wKF9 zb}>ltO3ziWMd$xPiT;nW|HD$I!`#RR+|W%mD$^xV0Gb2j%syl%<^q?&KClBmf9DbZ zr$rRA905qv1O9?DHLlb2^rnAI^P{{1Es{uC5ki{#;HAO!qP{XNH$I~GziR^ULPHts zG}{?T5P;ST^CD4h=QVf=Ov2wU47@m{U7GUZl-rH~{ouC2^}ax3-v4RQuo1QZuC;Oa z#|_ft8|43pNE=%P3RsC#TVQ?%r<+!uz$b%SDDNTM*UnGn4eapGMGb!_;tHZWN~V^@ zktW0qU&fb8pbTDvZ-6?UaipKn2u6z_!yJ}0A)4diY1$1zXpewIN1lV#BEYTyOL6)R zMgsGDdK3*bx?2rafrE4%>9^OYTn9g)-Cz$`&w`K;&-nZn_m^!5K%MOf^0+P{fayK} ze~*HRD)+AgzrlGCJJEmoAh-sy>|I(jd)Dpn4fe?Pb!ZSd} zL*GTgoSO{c_vK(590>U2@YFX#jS|3UxN3Cc37iIa*z3!-gL~kPrI8?xKOhOd`c!~5 zfQH6PEct_9H*p__{=!EYwq}DdAOBlTtX_h80;Jdiem+u`r^C%~sISG907?KQfD%9n kpaf6?C;^)F_%9Z~06ydcYj|+CMnyL_MME${F)>9rI5S2>MME(%LNr1$G)6fxlez+CBrrB& zH#0Y6H!WpjVPP#aI5=W0I5;siEo5e9Vq-8hV>L1~W|Kq%DkL~EVP-jDGd3+{Ibt*| zF*h_YEjT%4H!UzTIXN|9VPa-9W;m0Q144hJ6xzQ400crwL_t(|+U#3fNK`=>K4;A$ zD;1Kc2*QWLBDOlv3bBNDtW!V#x}rM9DAf|kmtz%Cd?kVSz- zJ{Twqg?8CWOWi$vKRXYb*zUQ^nVogd4|(|wm^uw zXbK=Q1ZqOalT8HfK$yEgMzlgehzEdRHk#*QF6jkM0K4H4-v(3&fIL0m8IY~x@^+vc zI13^C10@8Cbdo1a15N{t5H2_7LjZpyhOuNf*5o5@0HyGTmRR+GG7IF%Rsu~BvYLq* zJ05@zr=xiX4B^yQFR%l+1dq^S!2=F(B(E?}x=0C?%_{PG083yGZ4NIIV<)`9E7ddz z@(Gm1fvEzL_%EQE7FiJDw=5yBRuFkIN{o9c*Fg9cQ4axhA{9+Do6nPklk4WnoYfg$RHzT8d_ggasef zb^(~mH4>6p(-+CB|p^hdF2FIh73^%(udBBsGA3CU9-r9|(=E(@_g z9nCrkUH=S5|E=u;z>ez4vVwo@D^OwN2A9dvFGMq6!qKn9=y%vE06W;&FCqCR;FQsu z%y_^8V7!de9!9@cga9(WQ*#UC@0q#Li~vu7_Y#q(GsbOZZ#FCdTVCb@*CY|(2>R4) zdb410C76TenDXzLpGf-*On?Bs|HiAGE5XdxdTmPEqYDZCZa3?UXe)mUN8Yw|K>5rO zDF7XHH>g@5X|lKBB#i`ckj113dj*`gyk9mN0w7Kw+y$5XfWX$!hyalFh(!Rv>AziTqVPg9ugB2VdA$-WT3RuBiK0^Xs*T6V5QvXff(MhsfnxTj z?vN1J1u?$s5F(T2ZD5f}@~uDy1n3kk0ENn9Ag7;8>Oelv;D)LI&0!$jpa82t{)(Y> zJC7s?f&nyZSp0?B7c)f>khqCvk`jD>;HS>po>KDTolTAA04jeqg;s!zAkJkg9e_8C zL9-879j3rSotzEf8V&EkDs_12);lwR|&pUZLF2y?V~_s01eT(9@;)mDJ8pJRU`Lf0LB=YU0v?d+W-In M07*qoM6N<$f-1#9WdHyG diff --git a/resources/icons/overlay/layflat_on.png b/resources/icons/overlay/layflat_on.png index 93a1869577049acb59daa0e364b1d73a6f95db22..adb04e6e04c40291d8c95ab8bc43d977a06e5471 100644 GIT binary patch delta 1859 zcmV-J2fX;I5T_Kd9s*!wWj8Z0Wj19kI5TE4Ei^DRW-T~3F)=M;V`XMyGGR6|GB{x( zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FM^GchtaGd4v=LP0|@F-15*ML9+}G(ki| zLqSG0F*7ieegYgIMl&%oI5RdxMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144g@6Y{YD00uWnL_t(|+U%KaOjB1F$NNSp z&=K7CtO-iDqGg#6?ih z;lS4qXnAK41&W0^E<~V!rO*~G)Y9%8BMBcY_O`dTR_{M)fVP)=&hI||=RD^*bQ*t+ z_C4(#7K8SVivmCapa4(+C;$`y3IGLwG@UqcVzt?9K0P@(`Bh_MV{UPA@%ps1v|vvG zpp#t+cXoDuf??QPeSLkuOeRC(;^Gj2K){=tnlkqE^q@^mP3X02*U-AUx{0?A0GrLG z8w`eT&YnH{*O@bC)C=#!2*}CFK|Fr}gj4|e`T60OFJC?w8yj1s(P(DueUD5aqyi`@ zDN$_Owk;tnEbKg<>mZQa+}xFNxqKynhwym30KHy6rBb^>) z(t8|$QmGV!CsX)3{}Tx$FE1}~cz9Tsn3z~$_rZ`A{yTQ;K*q+#bnc!!)SWwbp23$w zp@;%ehrJPnP*PG70zwict-@n4Xdv1oApB8}1AzXIj*foqV2ITA?b}2BfvT#i20RX6 z=gyrXKR-WCd3iZ$0J5^O5S@Qcr|;)w=K(AvBTxejk_8|$GgAWn-`?JC@Hl{lWaP+Th!U&_ zVOdOiL*!&~;F^xT%lG$uw&D=pWJIK3Io#abJV+XV03QbP?nhy}zubQyP2ykxM_8e&#HsB$4eJaeqk_gvj1HM@cwenvfa_#4}R}9-n`jAGy1B*JbwIm zfK30##Kc6xo&YHTeo%h|r$04XhQ56;(QYppfx(al0CtY}d_K=n|GNZ07RCvR4`Ijs z+%|cah-5^BU>_0o4)L_doCa_xfiE4Mp4VRLF%1%tjEE3yqQb^0t|ad4&DM*2qwfu3 zkCsSA52*D0A>>9frKscA>D+*wWZlg2(_Pzu6|9FxS5&nON?Q_^N$JMKFm~Jqe z8ydj$RqM>gcp+N=n~9CXv$`cVK@6o*DHRHZ{iK-d@v6S*2cX#LU%_K>m-ASB zkw23s^kZ_tYOvZcXEi01KD}sXZT2rHC_qwEQ<3=i_^8m(&}9&aMx#MnT3XPWni{ma zx_Z=Nu{aH6Vq)T0Sy|bGqeqYaL%0PDW~-%sX!c=B1ZRKE;d{>-7Jmlb3$Dl(y?3P- zQ`x&7c7k7?1-v*908zos&(B9vQc@5QDTxOT9N3Cs*lcfa??7v7D{dg*sRsrH1qp84 zxPiOoaQIvRR((~w#E19a8ozM;-|3MT^cZw{^PY%(7OTxFzcloR)ye0#7|7bSYY`YB zcz_fY6(xTZ78ZU8ilCp|1^}vQPsCo}_o=K(?REHf@#bZ+f*9`R%u4O~jC8R&WSV6+suEP{v0Z^x@~cLwWLxh4**%IP$=N> zd8El?!a(VL>B*BPm|For{kAnEEo)Uk>b%uvAv*LuA>0j-?;7;YQ34-PMX#yd+b8c- z1iWopG6GJ^g5onIersNW@Qh5N5;M+0FdRok8Kd-dUKM4bD&lM xceKPP02BZU00n>oKmnitPyi?ZUatQI7y!*LS0FHpVWR*5002ovPDHLkV1hBCPf`E? delta 1346 zcmV-I1-<&G6si!g9s*!vGBP+cIc72~G%;o{Ei^S`GA&|aWn?WhWM*PxG+}0CVly=& zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FM@MKU=?MK?4>Loh=zF-15yGe$&3LoqT! zG(s{oMmaK*egYgIL^wh>GBQInMME${F)>9rI5S2>MME(%LNr1$G)6fxlez+CBrrB& zH#0Y6H!WpjVPP#aI5=W0I5;siEo5e9Vq-8hV>L1~W|Kq%DkL~EVP-jDGd3+{Ibt*| zF*h_YEjT%4H!UzTIXN|9VPa-9W;m0Q144fsDLC!`00cToL_t(|+U#3dNEJ~So*8pN zBcnuGgz%xThc*l)w6c1Mq>uz3LV9p32+0a*p~wg;+H5zh45FxrsHltzjKU})EDAL8 z!65S?(iU2-%e~Y0bI-#~?A}?+RaLHE*x1+@he0nXDH#w!Fw{Cn4S4`30YH2_hKIl$_R)J_1B7S_ zrvM^Dpe}$s*)-rTgt-S~gewGucmN1yV|X6qkU`)CunQjXZA66t$kPR$0oeu){{n0W z&O!+9NC|;rgXGClfYU%Lgv*Qg5CDIPB$n(ZS-iw`pc>xL8L1wy%?5e0LZA&o)-o|8 z#{+P~=@{C9cux1)4Qv4}!XxxZ@PGpx$t%p0E>J>cRYhJ8U!%8zOZ^`U?Ur3zHHnL;!rzG7L*4Z1|{- z3&2#alaPETl3~9H52$3#4cTm?<7E)DpUTSK7|F;tWA>MbD1*BtBu|Z$Dx(K$He!D^ zhH?pAzX7xV)^Pz~M~!4z!S;U@*kR@b&Eo7AVOSvH?AKuSdmI&jJ=oYUA^A4ol-ZN4 zc)&tnii~9svp*<802$w@xefC7t(<5@fG5B^iOAD4#!XgFHZ1^KUgiN;B@y5V?y1A_ zX2Ir4Fc-ry<=RDXX{1q)CH^5=pk zEGL%2Af61+qwW?>_a+v;+;@^@0yxNGQiQz%&fDHEoAm$?rw{G`)C0JdaOP9dkgy;j z4gt#@7GOSRza?bJa=-)f8F{k$VUlDAu!1#6+8iQ(#Rz$)%HW!SpI-;57F3sK0qX&t z&;0)Yn|WQ#R0zQRso{S}88?xBhx;4a46)Z^XzN^|_?wn?j9#LsF}!NyQ3XJJg5rNL zIT|Qsf9ekift?WJy8$6)P~8R=TO|Jx$bbO9MGHWo@)*b+=8z_k&o{ZDD!_0UNH;0K zDv-ZyYTeEw34)-H!kWcjp!Hg5ih#sT4AT|=`vX4>p7xZIAMbx`YBmQ@t0}MpTnuq8 z*|`CD!$b^wfz?48EOe7|AzZ8Jog2@UAX@`8v%SDAkT2jh6A&Vc<%fC0{Tbv-tz4Ju zIsk-q778B}|Klfa27)gzoc%GAefk7=!>l&Al zqUkn+a07*qoM6N<$ Eg8RlTEdT%j diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png index 8cd66cab912066852721cce6e3a7fb9bbf3c47c1..322736553060145b8f9d9caafbbcb2bc0daddf4e 100644 GIT binary patch delta 1278 zcmV9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144iNhv=>V00Z_(L_t(|+U#3ROB+EL-ks)D zX{g|_w2_`YN}-h^YICt5f>-+oq~f8_tJmHN!9U=+Ac9_kN@=~696c9f@hG&U=%LW2 z?@ON0W!UWQe6P(8yo}k&&dl@7%=9%W90{*x7%; zR~wBdRDHn5wW*!%7gu9tnff3*0N~pspCN5L`~QzX0{j8VI^-Y}2_hi)SG4Vav+&=c zNDv{x58FbLAVPx=O@b%`_>d%svVadof+!RC5F`jk;AeHgXOJ-K-4iq@?@8(;4@r(` z^26tUNcIR^YXotxsalm5<_`MeKA(Tto@kuqChEGv1Z0k?MeaggSdtBr2_oS}dgm@x z*CadqQTAq;NP6egwkzy}fDB@IE1Z)|5||%S+kL9OlT1@v*HIjUS3+m?txF@yM zrSF7~1{D+@pU<}iw&*3v3CaG5_7i-k$Ps%-vPENdf)S7~b-a2@B&P*5`=Wn^h2uV+ zk{r<-nkJ#NumJub54eL8V@zW|p=JR*+v?*C1@xGzZIXM6!~Ep;ND+S>(LVGOc0q#H zP7tQx3phCpeukv0C^LB1smEAz3_iljf25V%QZ!wxSQ|w=Mw*xQPN-P|&mP_s>^z&X zOC9$&XMFYuAK?5?VN7EZgr$F#--CyDj_uoap!nM8`y6BK**Ffu#OZ{Tz4_|HEjZ5N}41u^GM)$9wBYrA+|b?f!skJc%FZAt~nG1&QmT4 z$mCojp5I4QXS)oeNFL)FV;X@ktOE*=u!>C1G`zQ_0|J`pdm3Y{nu1sI1i3@@uwWLM zgrsF)BCl*`a#A$=1;Ab<7f%BZRaoDM3>mcD_NNF05{s65R_78T~ZkVkG$;FU1J~ z$lEWaof_1?Qrd>k-oVbw%Zb+(xn&+~sX0=^lgUyd@NFCqt* zqpk?pf>QjZl1o6Arcn9x|fIJdHf%onT1FU?r9JvNxn*R|>A>j~@mR+uV=?F-W5c~%darMK= oF)tMX6#*3i6#*J!UH%F%00yCiCqegjj{pDw07*qoM6N<$g0f~V3;+NC delta 1337 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 RN5B98002ovPDHLkV1lg{*?a&1 delta 1572 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 delta 1347 zcmZ{eYgCd40ERz65O0Y`EZ01Vm((y6K0ramxT%nrOg+v^DRTL|q-l96v-%KC&FZ9R zF)7Kj(&K$JU9(6dTcu0Othv&)Y~`HV)X0^^wE1H@XMgs-f8O(+=g0FH*M`$>LW(3H z$e=MHkVKb; zGS{RTbg)K=;=hA#2-b*&o+rGewrmIfVuCX;;20Ebv9+6=^B$?A z8Yj}W2Otg5QW1fzY6qOWO9-&}FzLi#O$XxJf(xEa`RfhRy&d;NI79ivm{yEzk~D&{ z)ILwI+l7`QJY5%FhQr2pdozn+&|LsuX+dfQaP$jh!6atnLvtJ!|)lFScbRuO&1 z=>8{@e)p%h6w0G4^{O!T+cFltx)+Ej$qT^_Mw=x@*xC)&oI-@+Ox=bn5UR}sMtN>6 zqE+dBi~f>rcXX(sJ+e9=-a(tBGo2i)+SB{hwtZ!%QRQs7VP{D9t<*a`fAy(r9a)9P zZ(yi+FBa0eS@i}uV;|{6Sa>AVScX0m)7t8Mo4+h?96i(oX!vewmLg?3Um2pby*oOg zz;54?nPUZ@q*8v<_Vy>`tqBk zx>8}0{3LMw)kWL+Z5d;U*5BK6-&Q%{T$B)2IlMiqsgSL1$dk2j@U7@>vi+l39rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144h>JKshylqY1MydTQ-eIbG0U0FJY6pi`W#tJBK2mQwUa~w`$R& zZu#@SHIBEueydh|^Z?M^&hqDYf4-C%XiX(pw+ty;i(gJWFKm@|jVQFLS&vJ&sz?FD zE@2sSd60{xHxpn~i4+alwWHPl8dgeY+TX#7L{q4B8i1@e8GxPMtj{vnuw#D!0^`9e z*^k*zM}HDwaMzHB>{-q7_~Mt=K`?ymN;+guGKIr6;60k@w*SpiE9W!eyyQ0%nxQoX zqCDII@EIg(eze_2KL^^%$FJpedxhc(IQb+V?)nEmj@R#2v0~uvD!#59{{CL4?9Kb) z&xZ3-V7T;s#-S`)me+G^SgwCD=y@b~h0gba!hb>M75&;il}jhFGs=gqJvgn-#^Pk)*;N`8x>UA&b8W;U-n|$Q!oZ8NBm*A9i5Jn5v^i ztQ^M$!1iTK*Ygql9o-UjZ%k4D)uutiC&J1Q;O~BxwsI^<(yzQ(o4kLx6r!yqiI5Xy zn+P8GQ&Q<*8AKIriWk#UrL~a5a1>!429fx`hH-aP_bM8a?hXE+)aZ1kj^Br>N17l^ zj)Cx`O-rnT9(Ku|CWCnW?_Cmg-!fZ@Bj$-vq=0UBGL)Ny#K)BkJTQlNz3ETfI--0z zRXnnPij}qcwo*94#v30UxK;Gx7sHvf(!euz5n~u zK1Dx|PU1Q-$u!IW6vWtVQEPSTgu8fI^TLkLtdsTzqq;(1NG!SpEa?aT4>ceJ$m(6*3z_j=e;oQ#eF ze-5zu)jQWK7eB#L{O;Ym7oR$HDnTxnPj1<=#c#ug4f&0YjS@rq9H<&8+2+CYa<`#4 z2D@7N{BbSE3MGF=BB{C$;(kBG{WsMjMIYo0BQO#6L&OR;3IJ4Ys0AcYXt9j~kg=NWNg)!*P>9syAs&D3{JZeAe*O9zH8nNyw{PE0 zgYWVUL6~Ykk0_<^YKOP&`tRI06h?%A?zGB|=I~-vDK@GWc$(;(0(JW6*5WsJF&6(L z2rZxb8M*yUVH$du6C~b z>mf8ZH@71Q5F#B+3V=Z&FnXn$xAPSbt{;2h!iCa|jEs4QPy`_;D5$uuuI}G3gwhfd z6P-;4fJP$FQ1{kc;`OH0YAqrnBJS1J*0$v3<)vT=Va8ejwXWxKz>_CWuHL_Y|JE~S z&fI^&LOk9OW(2^Z5-GDi*tv7(eE06%yZOY46UCXCnN3)aS3LxwP}qbZq^G9~a0P&# z*6g<=3E$zjYqpuAh&ZEr<# zuxF5{)6Dj(wr}4a2I6mk>$h2$(Gn68s!)LiH(^_Re7qB80Pc3m`p-3u7mrT<|AT)` zUteEWkx0}B;!9$H#{S zQ6Y6?WJKcc?|*f4bX1Lr2N*IKYh#g-k-5&!&iwlN`XnxwOEH)DhYug7A@OiizMG49 zqW~a#_Uzd>YGm?wJPrU!G&Kah_~C!y;c+A${?55wZ}b`=)Wt;*Ks?)&5d8f7*hsus zEFPP$cuW9HgIDems>3}cRm`hi77MPHy!@w#>EZep2p;I>i}L4b?RNAIhus!}~X zJQ(v8kNSbAIg0v&s6}S7mYA^ysq!SaYXMxes-mJ|&Jgexk2?TkAV7iRgv3KcX~Rpr zNdQcR0Ap7U5YkWvXJ~uW@5_JB&))kNSP6 z`HA{}s0D_nnRK~4%GmywwvJB+!PMeS4!{ru)Y1a+J0QhvFqL?t`T@ooDw-ZYe*C$s ztE>Ix%atN2sRaNF j02Tl&0Qi*cKLQK@3#jxNUYw$K00000NkvXXu0mjfhX%n{ delta 1353 zcmZ{keK^wz0LOpZm_0ZqAxD}SWo_}>_FEg1y~>nrE}Y!sZEs#qZG|Yb5v9@*A||(- zLZUZUoL)4N)60>ku2jfdM?GGq!b#2__w?L9_x=3$eLjDD|N6eOy@I~%0FRWANHhvv zP9jo3xjT^pNl8SpSSBS>q;j!@5+#?3$q*KkvPKI*dmW#_AS6)gTMgm^(eaHP#b|LEPs#F*CA zfQq2E;xSest%v8;EUn!*wjKOErA7gy55h2QZEyY!YB*(H;=7U#d{d;>2ff*^KMD2w14=Qu1R%O(ob>81B zP$Sn)o57_TJkY6N_o4`(qx`kuPwcF9xr}=S!PXI-9cqJ|K{5}>_s%_UokIqd$*`B^ zj5@9B460M}WGWL-@C;G=Z4DMIV;VHuE8@>_W>fErheE3zg~j!Wwx;Yam@@net!Wyt z1(yCkIn<>DQQ9iLt6H2l_E4C6u1}-0{=Q~vrRE*TYj+MG{I)x{*|REPAd#~8TyIOL zm^b!Rr2E^Y+zEX)1t z!jkpDX;Yo~O&`z>z1n?dB z0{Aw?ads19$H#ZmpkUc#P)BfFHw{2w>+nqb)g(_|bO@~C%j{0}7ol$T)Z}>Z8(6cw zJ}>B`Dmu7cee<$S4(f7tZ$usK=`OjOS33D-p2%Kle1Pe0XEa$S%^N)qkG0g8xe~4= zfKn5eD4hcas}9%CASai5Yi>B?^yG~>axgwPp#|>vsXo(L^||sVr)rS_HP+oa?NX49 z5(ME6!-3AsN6Htjm)@6t-mQXIBDY1mUhFHb!E{~2dTtqT$RoWr7(J*F z41{h(b=Tf_HpGh!Z#0?qtIq4U+~?B*gbP%)Rt#-KwaV`N-HsY7pmx*f;%0i=rX36T zP%m|E$&OJ(H8ub!t^5n&e}~`$-Mxi>uyPSkWixvP!M<~GS)$ZH3vH}nQ%0C7Y7Y36 z9fL30Gs&+|TGRd*+FCPW6I*yH?1EX(ZUs2`=fyq*HZIyX*no5W{)>LhD6+Ce=QoFx zaoyji0Wa6ycC;&OTuwd`J1i-6Y4#-GGtU~~o-e5JM23%B%s4u#{b5jKTxKFq!>}A7 zmom6z4kKB}Fa)d~M*@N%0H`-@p=9}$o zjt9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144hmI5p+~00TWqL_t(|+U#4;YZFlr-YqHg zY;6U>pEqmOi+ZZI^lCsX@zkEIsHLTX2zqEQT0w!X4)eyi{u8)u&UN7T!#o0PKJ^TIhry>{ZX1u9 z0-rMY<1mv3KM8)o;74K38T=UdQG>q@L;t@Dq?^;9PlghJoqq@1a-8S342%O!e7xwo zF2j5yAHR0-3&4aCfsbAMbKr_0;C+7=KMOP)$8P=(@Eah}2LVq3EGmS&03}y|GqoDt z`0N{i1AaaR3D8&%{oKz-+piS1ZQH6B0dy1ylLXNPw!?fy0{l^i{tPKdA~br__1!5l;sO zz`7C=%^2{Z+C;!}hk>PXB7kOTr%TB8G!^Xv-g}5J8`BrO71&gc0JbruEmq|G8|paW zb$f{LEhYgnoRI%Z4(M|absm2rJOmEK;XOXj4WO+Y0bcN%pJF%9d7O7dpfdUaki@y4 z)tUswPlppRe3&&nNr^&Tiqcn{ui_Vgou{PK8sTG~P*ocCeLxS)0{ZZm3QkX3xkjIl zzwpnpuh<-bcAioJ@KU>g z3Lvu!T##M|jA$270c6(!X`owj5~Q8hv`c(WssOabtOHT75eaeGh>RmJJNb~--!6gA z8vL+T*qo4|C2He-&Wc}he<_O=6=a@$`41_0BiTPy@OVom@6002ov JPDHLkV1ihR$29rK`=x{Mnge1GBG(sGBPG%+nSH8?gcVmL5kEjVLjG-EPmHf1w4VUt7yDkL{JFg7@2H8(9XIbvlk zW@a=tEjeT|H!Ws0G&MLfGGbz4H8qox144gQI^D|v00pZ_L_t(|+U#3-OdM4h-9&G_>Jp{}3w`>4N2uqukqN&zT+HM_oX&hGPe^F!Lpk{rH%f_j|v0{N9`Wz{Y=G z31Wi&P{o zTp0I7aUZekIFyBPxYku1o~?Ef2P}XIu&rnxupIj(OP%#th&To9o|xR)v2SYoEF%!F z+Ufh~GZ9i9_%Ak0lz)HQ)DQmgH1B`p{ju_F@DDamRey2Qs0aU}?aD20Z&f@6{-?j* zJN5o`!%gsC-{Q6TNUEv;{O|slKL6^~$yV_5qhui!I-i%o|Eb&DPCFlHT?W&FdwG>yvCsY{fMaZ3T&A)N5a0M~zhsX()(+^V!Rr7!1N>~1Z zYgDeDIlnHzfX%>}!?vm;;J~%Xi=`4eo`M}=~GD=Z-}yemKG zjrP5PB_Ag%<2}9cfDy0)X29yG*m9)Q5b6{{^@S#Hqidau?JYWQ@fSENF@FsC3feDD+T1lLz695=@~vCxe5q~6@-=>z~qzzUcFyW1*Yb1;mydg$x*GXIbNZq%bv%BuiR080dr zIELLR2r8a3;6EEJOZ@iMboWXtBS{h~D=SM)PfveP zDwT@aZj9M%wl+34b`%vAHQQ`9h7|%P(UEe}<}fHkxcpp?rQIEXb#-+)85tRgtJ$NG zk&&CUTCHDZX69K|2tbnJ2&o!8hE7+w!N$-l1r`$%BSSr(xE6oITBp-(LquHa>gqDP zy#_cBMD8>mEG{liiDB59%xkD8K0aQ>s{k7Wz@egjZ{YI-`!uf=M9D%qM}S&Fh$59j zt*;pODSk+v%sB!KHV0*<7&Cki&@f=V&AEMmiDDd4KMWm3Co7SDiZXGI0I*SXsv2Fw z$%;P2J`*WSaq zMbKzx=%kK^~6QvykEPO8AJO_Ci+{^Y|)QdGK?Rz$;6x`swkl!FxlEHneiXTbMO z(-SKN)YQ~8jzCds(Re$u=H})JRtSLC;dvLw%1WB1s-L@jKRq=_Mv!N-)mi6usk1dQ zV)h#z$mM@Mji$?m-RhjpDxqxtb4Er+>=hLib!$ODd3kv~)W@0%oawO)qmN+}w_2@K zO-)T_Wo2bAt1`IIdX4ndtbdQ1=hFtKKVX`>X1-DZxa9y`ZCg^RtSfV-voFlTfV5fy z6JYD%9U`R4u6P*!U;4>n`MLZ?h$nz2fG2<_fG5Cn4CDI0&;KL90L{ZXoO=H70RR91 M07*qoM6N<$g1sdOssI20 diff --git a/resources/icons/overlay/scale_off.png b/resources/icons/overlay/scale_off.png index fac0350995400c1b59f121c36d677ebf94ed1756..81bacddfe03be000dd56769a466028dc8a0ba608 100644 GIT binary patch delta 1030 zcmV+h1o``v6YveN9s*!wWj8Z0Wj19kI5TE4Ei^DRW-T~3F)=M;V`XMyGGR6|GB{x( zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FM^GcZCpGBYtoLP0|@F-15*ML9+}G(ki| zLqSG0F*7ieegYgIMl&!%I5IOZMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu= zH8nUhW-T#hHZUzTVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KDkNfJVKg={H8?FZH#IXY zVKp=|Enze^GA%J=GG;b5V=-Z4Hf58N144ho&7Cj+00RI?L_t(|+U%N5YZO5chG%3K z#e)Ym@uUZDV)P_}5KYL{PeME?2u2i9;xQiLDGC}y6+)JNFN1KK1}#~t1Hx$56xahsvm}2)8g)Qh z9z?L!0f)hkH6)swU^;6e;J$reKJxqktKhC72P}gV+-?CsLUO`XN&-~){*iNluRPK= zDsuiWbvQ2J&;@=r`0VeLMmRl|&%_x4WR;M9<&g9GNT4aOIjgIHO@MK5F2W5-gm2(& z+QHidF#aFFe=p_UOfi;H_%eT6`5`3FYo5wW9nkD@xzNu7g%IEc_)ug4j7bp5uiNzz z;E4b(3xH+fBE1`Mc?3KG7hInLzT^Oh1$YeJxLgNZmSs=`aJvo|00)1a%>l4MupLau z6c7R2oC3F8uLA~T3Wxx1*8vYfm&-|zc3RUe@ljU=pe<(Y7zMkK5Eqn@aUO^L+@$w! zci{h{`1^3!4Q6qEM7dsl&misK>`WlO4$rbp&h}<#7_5NfTAvB!{eKM{(){!F-D2AL zjbJQD&T1S(AB7g`W0*C+wwtoT)01)g#kKt~zdjJ3c07*qoM6N<$f}9$* A1^@s6 delta 1711 zcmV;g22lC%4U`kG9s*!vGBP+cIc72~G%;o{Ei^S`GA&|aWn?WhWM*PxG+}0CVly=& zAb4$XO-(vPZ)0_BWo~pyL_H#SZE!ARX=FMvH$_G?Lqjq~G(t5oF-15*FhoX1LqRq& zF*!ssGC4AnegYgII7Ki)HZnp(Ml?b+F)>9rK`=x{Mnge1GBG(sGBPG%+nSH8?gcVmL5kEjVLjG-EPmHf1w4VUt7yDkL{JFg7@2H8(9XIbvlk zW@a=tEjeT|H!Ws0G&MLfGGbz4H8qox144f+6m~OZj#s{Z+sBs_Wvh0iQ!?tY6M#%KTvQ6FM z$R2?WbvRIpvgmY*4`9ku3KUwP?S1e5J5bVek(NR#_x_Wc{`2oW_x#T9o^yWZocn)A z5d?V;5wT&2$W0Ema;_z&Pk2qifOn}W_llfcV$09>fp;3{`BM4toH~e{Bw|fxcUu}qd;>AQ&KKR=k zM!x*!ma_-^PuJ;lcBE;Z0)L;y{`G$+SH|1HfB#YKs=cejp9lZYrLhz5UT|Cif7hz8 z~I1JZJ#p5R9V9Ubsd%jVF>!$pr;WFK{xYn2Nb&t;1GpB#o1sG5d zocq}PG3dtm{uKDzM)#)b<*Ks-)U^Yxj?4UH{L(PpZ;p^FFAh=ddo7MuhDSVq{W(P6 zT_MEVMwr{X&)F>`%X1KKvC;#pqU4Hy#@OCnXYEbnG|xiZGC&QkjFijzCRnR*{~tDz zqal95N)M++$gp8IZ+o-Z-Y|dQ;;6}bN0;m5oKW8JcC-Cd&jjoCr#t`dp^j~riXgt0V7}q%z%B?1n7mgua==| z7-#wM3R9>P02l!)VAlIk9={0~aYu2w{pQ=)+6v1wc%jF^) z8yl}zR8&|fisFMpz^JgL9Hlr%xE#~`a>spjHUR7E>x=X9@-pZ0jV2`}ElEg7h$<*3 z_&I3j0kEnPc2}5#`tQ&p5KBo(QMa|V?Lkp=&bu|w^N7i0I@EvB(czl)900~}2tS*~ zgPF}{?R+%e^Hzm=($mv*k_rTa0O0>FlZUDePGc2?3}V4SI+230~ zj?>`EGkv5@lqzsjkiVM?Py!_tmGISXJP}lTjO+ zHbuXX8$;+tsRBO{_eatIH3xA{M67c{GM$xS$Z=W_dvF0pC5rjsyy*xw+XU zfMVvO@w8wTi)BVwADAdfvIu{9n7)3&O5b?ry!|3D5v#_DqlHl$%aYZm-+RgKsU%0p zW=wQaw{ijvhI6Iys}!NM>ba&3UF-b#?V`$B!THp2@udeI7rYAF;O7 zq%H*iScmKM%cpIP;D7j-E_3I~uxG)q?Q)+h`E|J7yKH%R`I3^7l4OD)WWjDYNs>%W zP0h_CM~-v_RR+DSYn-*;^T(KJKI@qB`<(ahnQlt}&L4ntZ2>1pOr^7XXJJ5EEsi4S z!*Yfga5wzF^plz7=SmA9i2#WJi2#WJi2#X!kg$IR7y$b+J+7GN$-Dpn002ovPDHLk FV1l;LA!+~s diff --git a/resources/icons/overlay/scale_on.png b/resources/icons/overlay/scale_on.png index 4d4c075f385ccd68cf5ec27253e2762851fba7a3..271116858dec48051fb3230078b3c53ff3286c5b 100644 GIT binary patch delta 1500 zcmZ{kX;71A5XYYc6f9>r1Qj`giZBF{HwU*#I6`Sr8NdcX4N1t02MSi?CT|olTJGYB z94Tr+MWBdyLO{`==qRFSQ>gdrRWPl9o-E(8LO%jLlwh)p3vu57Gz($Tn`JKcR_xhIeEj>QM~dSTUy zQ`Dg-cLovf2l+947~XUr0>RIf?&rpEBhh_*eCP}^fru~Sqb;oeg}nb4dW43zP~6yL zvbqZ#0jA(_BsN6Gxw)w$2#$hyY?#e~$!Bn47N-17#xVD>7@D$#2xp(OVTrdrimNsTRBDCN)0y(tq)^9e)Ogq;ATt$)qNFaX0=x5|$EhJl`#I~RSIKz{H# z!c!ykizFuXp4R@F7v4c;VVe*C(WO#IRn+EjR`sT;>g(Iu9h}}g!}=uv!B}$G9-%(1 z?9+O7h~&oN6mkZ+W7l81abiS!=PPhoW>>06I$`al6)#~0anDYeOv@$G#IH!M1*auX zA=E1u_yM~d4LbH9c6)e&tap9Vh41!-DKL*%28{LfmR7sYL%v(&79mz zJDi;|*i?9qDXJTk zOMhu;GH1QoXCSsps!4C2=*vgM!hIX2r@PASKJ{1V!-iw_$kX7b+3Iq=X^V=fhRKh{zl9(Ytg0B#Hs%D zs)~wwLA6Ox3|5pppuVVR)~fPbJ&Ay0IlR2{DrK|V@u9Ym@YEd;^gf8mWadsd1|4!T zgdJM>2$s{)Q5tF=Cwi%7<#PF^27Qp4nz|+u1a+I7(c5pp#Jg`hyL)zaoaM61OSK%= z_CrSA?Xo)-o%WG!(Wfw0;Ao@w3(><4@J5Y#TQArcCt=ZeA4U z_9oDja;D;+oBe+2qjz-IYi(MZ=3cpWpTYp-#6&7uko#9L7$ST#a zU^5WCSu0wQQy`0>cGN)SYaV)DWa3!RErrE-7J%>2dr>`%==r*K6ezB$zIiiQySN5R y;`-=;S>9rK`=x{Mnge1GBG(sGBPG%+nSH8?gcVmL5kEjVLjG-EPmHf1w4VUt7yDkL{JFg7@2H8(9XIbvlk zW@a=tEjeT|H!Ws0G&MLfGGbz4H8qox144fnYfzg200p5*L_t(|+U#0;Y*SSjzpvgt z*RI>Tb??DkfdPwvf;jdtV-XD#g2Y6S2q7daF$AK^G$v?_f2jPSAts=6EWrl|x@1Cx zY%0SotPBWa&RH1au@}s3?Rsr{dwXB@8HKhQ=p)zqa)rMyaYKdu!088D|Z7kW?qb>!! zz1&gz&3t+Q@cuJ~of`HUQrDgsXn<8N8F0-a_o-daINJelvZ*s_n~d85f0su$Z&~9w3wX^fm8t$jLM4hI=ucV9`HFR8 zr)PBrm}pE7-_T*E3)cqxW)H3p;2es$k274jVbVW|(_NG2PsLNu`FGpNYfgXd3pk(? zbSAllVmMlfmWI-xE0G(V?;zCY(tO?TtfV?c^q&8UiM^9=SExFY+3VFs)I|HIIQmgo*ycJ3H3&DZ6u5|Q0Tqv8y@yivbRABvdjFihAl3&D9Dyrv2JR6H5bwnZ z4ToyrJ}Vti=Eb@Iz!A6tXW$;O0HbQ2^-ujihe~jKw%FYs>jD5r;0k}7fqPWe067to zx&VZLpwnf3v1-5-I7dYQSV;%IFz+7vJoT+NFT^c~^}z#2;0m0h76l9prpHADUwc!P zAL{}DN8k#aqY?!mq3~y2BVJr&j#@ldVqE~>2wZ`4Ad8fvfE)#60m2u6AI9J({#E~{KviInqd+tSfa%|LIC<+(jd|B*nZvqiTVLLw**JY7}TYYI>=s zE@j8MUX@vrQQ&{^5ygTG7oPUUC0(K91aOf8@59A=@8?q3+mc%ZT?&zZ^5v0FQr)=f zYzAYOWE8k&Gts9+YGhIB)iipErLa<8l;`Y83eXL_&G%G1H4H z?vnA6i~`T!Qa!zFSh47XR()x>9q|Y+KFon}2q#2E052f|^9k|;I^Gd3p*ByOofA3k zVGN!XS=Q>Y&WP&+3ndvG5j#z+t=;Uq`gS38844rKZk#-FDxrL{O_O&f)7Kv;a)esm zBR?9;wsn7N@~x@tz|_~%9>F7|E7F-|>SeQDX;$N?X*}*c-anIb!+Jel+hi=lFk diff --git a/resources/icons/toolbar.png b/resources/icons/toolbar.png index 75faea658e47d9c645d244696849410014bea213..e5eabd536dcfbcd19417064357f5802de0aafbc5 100644 GIT binary patch literal 17622 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 literal 15742 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 E4{q)*q5uE@ literal 21977 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 From b7c506170dfdecb1c5b27bcf4b348e5de5e62230 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 14:01:13 +0100 Subject: [PATCH 042/132] Fixed OSX build + Added shortcut (?) for "Keyboard Shortcuts" dialog --- src/slic3r/GUI/GLCanvas3D.cpp | 3 +++ src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/KBShortcutsDialog.cpp | 3 ++- src/slic3r/GUI/Plater.cpp | 1 + 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e01e1dbbe..c266c41d0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3624,6 +3624,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); @@ -4777,6 +4778,8 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case 43: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; } // key - case 45: { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; } + // key ? + case 63: { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; } // key A/a case 65: case 97: { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 08368a131..d4449aa7a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -105,6 +105,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_MODEL_UPDATE, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 25419b438..87010f081 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -1,6 +1,6 @@ #include "KBShortcutsDialog.hpp" #include "I18N.hpp" -#include "..\libslic3r\Utils.hpp" +#include "libslic3r/Utils.hpp" #include "GUI.hpp" #include @@ -104,6 +104,7 @@ void KBShortcutsDialog::fill_shortcuts() main_shortcuts.push_back(Shortcut("0-6", L("Camera view "))); main_shortcuts.push_back(Shortcut("+", L("Add Instance to selected object "))); main_shortcuts.push_back(Shortcut("-", L("Remove Instance from selected object"))); + main_shortcuts.push_back(Shortcut("?", L("Show keyboard shortcuts list"))); main_shortcuts.push_back(Shortcut("PgUp/PgDn", L("Switch between 3D and Preview"))); main_shortcuts.push_back(Shortcut("Shift+LeftMouse",L("Select multiple object/Move multiple object"))); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ea8a83871..ca2ae2420 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1186,6 +1186,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); view3D_canvas->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); view3D_canvas->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); + view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); From 0dcdcf74fc136ec90c2ea5912c25f43b4230d7ce Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 14:44:37 +0100 Subject: [PATCH 043/132] Visual hints in the 3D scene when sidebar matrix fields have focus (wip [position+scale+size] and disabled) --- src/libslic3r/Technologies.hpp | 2 + src/slic3r/GUI/3DScene.cpp | 124 ++++++++++++++++++++++ src/slic3r/GUI/3DScene.hpp | 35 +++++++ src/slic3r/GUI/GLCanvas3D.cpp | 185 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 28 +++++ 5 files changed, 374 insertions(+) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 0f0ae974b..797272223 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -42,6 +42,8 @@ #define ENABLE_TOOLBAR_BACKGROUND_TEXTURE (1 && ENABLE_1_42_0) // Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active #define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0) +// Show visual hints in the 3D scene when sidebar matrix fields have focus +#define ENABLE_SIDEBAR_VISUAL_HINTS (0 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 09c3443c3..012203450 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1868,6 +1868,130 @@ void _3DScene::point3_to_verts(const Vec3crd& point, double width, double height GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; +#if ENABLE_SIDEBAR_VISUAL_HINTS +GLModel::GLModel() + : m_useVBOs(false) +{ +} + +GLModel::~GLModel() +{ + m_volume.release_geometry(); +} + +void GLModel::set_color(float* color, unsigned int size) +{ + m_volume.set_render_color(color, size); +} + +void GLModel::set_scale(const Vec3d& scale) +{ + m_volume.set_volume_scaling_factor(scale); +} + +void GLModel::render() const +{ + if (m_useVBOs) + render_VBOs(); + else + { + } +} + +void GLModel::render_VBOs() const +{ + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glCullFace(GL_BACK); + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + GLint current_program_id; + ::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); + GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; + + m_volume.render_VBOs(color_id, -1, -1); + + ::glBindBuffer(GL_ARRAY_BUFFER, 0); + ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + ::glDisable(GL_BLEND); +} + +GLArrow::GLArrow() + : GLModel() +{ +} + +bool GLArrow::on_init(bool useVBOs) +{ + Pointf3s vertices; + std::vector triangles; + + // top face + vertices.emplace_back(0.5, 0.0, -0.1); + vertices.emplace_back(0.5, 2.0, -0.1); + vertices.emplace_back(1.0, 2.0, -0.1); + vertices.emplace_back(0.0, 3.0, -0.1); + vertices.emplace_back(-1.0, 2.0, -0.1); + vertices.emplace_back(-0.5, 2.0, -0.1); + vertices.emplace_back(-0.5, 0.0, -0.1); + + // bottom face + vertices.emplace_back(0.5, 0.0, 0.1); + vertices.emplace_back(0.5, 2.0, 0.1); + vertices.emplace_back(1.0, 2.0, 0.1); + vertices.emplace_back(0.0, 3.0, 0.1); + vertices.emplace_back(-1.0, 2.0, 0.1); + vertices.emplace_back(-0.5, 2.0, 0.1); + vertices.emplace_back(-0.5, 0.0, 0.1); + + // bottom face + triangles.emplace_back(0, 6, 1); + triangles.emplace_back(6, 5, 1); + triangles.emplace_back(5, 4, 3); + triangles.emplace_back(5, 3, 1); + triangles.emplace_back(1, 3, 2); + + // top face + triangles.emplace_back(7, 8, 13); + triangles.emplace_back(13, 8, 12); + triangles.emplace_back(12, 10, 11); + triangles.emplace_back(8, 10, 12); + triangles.emplace_back(8, 9, 10); + + // side face + triangles.emplace_back(0, 1, 8); + triangles.emplace_back(8, 7, 0); + triangles.emplace_back(1, 2, 9); + triangles.emplace_back(9, 8, 1); + triangles.emplace_back(2, 3, 10); + triangles.emplace_back(10, 9, 2); + triangles.emplace_back(3, 4, 11); + triangles.emplace_back(11, 10, 3); + triangles.emplace_back(4, 5, 12); + triangles.emplace_back(12, 11, 4); + triangles.emplace_back(5, 6, 13); + triangles.emplace_back(13, 12, 5); + triangles.emplace_back(6, 0, 7); + triangles.emplace_back(7, 13, 6); + + m_useVBOs = useVBOs; + + if (m_useVBOs) + m_volume.indexed_vertex_array.load_mesh_full_shading(TriangleMesh(vertices, triangles)); + else + m_volume.indexed_vertex_array.load_mesh_flat_shading(TriangleMesh(vertices, triangles)); + + m_volume.finalize_geometry(m_useVBOs); + return true; +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) { return s_canvas_mgr.get_gl_info(format_as_html, extensions); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 97b0310e5..169b0c740 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -583,6 +583,41 @@ private: GLVolumeCollection& operator=(const GLVolumeCollection &); }; +#if ENABLE_SIDEBAR_VISUAL_HINTS +class GLModel +{ +protected: + GLVolume m_volume; + bool m_useVBOs; + +public: + GLModel(); + virtual ~GLModel(); + + bool init(bool useVBOs) { return on_init(useVBOs); } + + void set_color(float* color, unsigned int size); + void set_scale(const Vec3d& scale); + + void render() const; + +protected: + virtual bool on_init(bool useVBOs) = 0; + +private: + void render_VBOs() const; +}; + +class GLArrow : public GLModel +{ +public: + GLArrow(); + +protected: + virtual bool on_init(bool useVBOs); +}; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + class _3DScene { static GUI::GLCanvas3DManager s_canvas_mgr; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c266c41d0..7facc3623 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1154,6 +1154,12 @@ GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec } #endif // ENABLE_MODELVOLUME_TRANSFORM +#if ENABLE_SIDEBAR_VISUAL_HINTS +const float GLCanvas3D::Selection::RED[3] = { 1.0f, 0.0f, 0.0f }; +const float GLCanvas3D::Selection::GREEN[3] = { 0.0f, 1.0f, 0.0f }; +const float GLCanvas3D::Selection::BLUE[3] = { 0.0f, 0.0f, 1.0f }; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + GLCanvas3D::Selection::Selection() : m_volumes(nullptr) , m_model(nullptr) @@ -1183,6 +1189,19 @@ void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) _update_valid(); } +#if ENABLE_SIDEBAR_VISUAL_HINTS +bool GLCanvas3D::Selection::init(bool useVBOs) +{ + if (m_arrow.init(useVBOs)) + { + m_arrow.set_scale(5.0 * Vec3d::Ones()); + return true; + } + + return false; +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::Selection::set_model(Model* model) { m_model = model; @@ -2055,6 +2074,50 @@ void GLCanvas3D::Selection::render_center() const } #endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_SIDEBAR_VISUAL_HINTS +void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_field) const +{ + if (sidebar_field.empty()) + return; + + ::glClear(GL_DEPTH_BUFFER_BIT); + ::glEnable(GL_DEPTH_TEST); + + ::glEnable(GL_LIGHTING); + + ::glPushMatrix(); + + const Vec3d& center = get_bounding_box().center(); + + if (is_single_full_instance()) + ::glTranslated(center(0), center(1), center(2)); + else if (is_single_volume() || is_single_modifier()) + { + const GLVolume* volume = (*m_volumes)[*m_list.begin()]; + Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true) * volume->get_volume_transformation().get_matrix(true, false, true, true); + const Vec3d& offset = get_bounding_box().center(); + + ::glTranslated(offset(0), offset(1), offset(2)); + ::glMultMatrixd(orient_matrix.data()); + } + else + ::glTranslated(center(0), center(1), center(2)); + + if (boost::starts_with(sidebar_field, "position")) + _render_sidebar_position_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "rotation")) + _render_sidebar_rotation_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "scale")) + _render_sidebar_scale_hints(sidebar_field); + else if (boost::starts_with(sidebar_field, "size")) + _render_sidebar_size_hints(sidebar_field); + + ::glPopMatrix(); + + ::glDisable(GL_LIGHTING); +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::Selection::_update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); @@ -2457,6 +2520,101 @@ void GLCanvas3D::Selection::_render_bounding_box(const BoundingBoxf3& box, float ::glEnd(); } +#if ENABLE_SIDEBAR_VISUAL_HINTS +void GLCanvas3D::Selection::_render_sidebar_position_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x")) + { + ::glRotated(-90.0, 0.0, 0.0, 1.0); + _render_sidebar_position_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + _render_sidebar_position_hint(Y); + else if (boost::ends_with(sidebar_field, "z")) + { + ::glRotated(90.0, 1.0, 0.0, 0.0); + _render_sidebar_position_hint(Z); + } +} + +void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const +{ +} + +void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const +{ + if (boost::ends_with(sidebar_field, "x") || requires_uniform_scale()) + { + ::glPushMatrix(); + ::glRotated(-90.0, 0.0, 0.0, 1.0); + _render_sidebar_scale_hint(X); + ::glPopMatrix(); + } + + if (boost::ends_with(sidebar_field, "y") || requires_uniform_scale()) + { + ::glPushMatrix(); + _render_sidebar_scale_hint(Y); + ::glPopMatrix(); + } + + if (boost::ends_with(sidebar_field, "z") || requires_uniform_scale()) + { + ::glPushMatrix(); + ::glRotated(90.0, 1.0, 0.0, 0.0); + _render_sidebar_scale_hint(Z); + ::glPopMatrix(); + } +} + +void GLCanvas3D::Selection::_render_sidebar_size_hints(const std::string& sidebar_field) const +{ + _render_sidebar_scale_hints(sidebar_field); +} + +void GLCanvas3D::Selection::_render_sidebar_position_hint(Axis axis) const +{ + float color[3]; + switch (axis) + { + case X: { ::memcpy((void*)color, (const void*)RED, 3 * sizeof(float)); break; } + case Y: { ::memcpy((void*)color, (const void*)GREEN, 3 * sizeof(float)); break; } + case Z: { ::memcpy((void*)color, (const void*)BLUE, 3 * sizeof(float)); break; } + } + + m_arrow.set_color(color, 3); + m_arrow.render(); +} + +void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis, double length) const +{ +} + +void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const +{ + float color[3]; + switch (axis) + { + case X: { ::memcpy((void*)color, (const void*)RED, 3 * sizeof(float)); break; } + case Y: { ::memcpy((void*)color, (const void*)GREEN, 3 * sizeof(float)); break; } + case Z: { ::memcpy((void*)color, (const void*)BLUE, 3 * sizeof(float)); break; } + } + + m_arrow.set_color(color, 3); + + ::glTranslated(0.0, 5.0, 0.0); + m_arrow.render(); + + ::glTranslated(0.0, -10.0, 0.0); + ::glRotated(180.0, 0.0, 0.0, 1.0); + m_arrow.render(); +} + +void GLCanvas3D::Selection::_render_sidebar_size_hint(Axis axis, double length) const +{ +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::Selection::_synchronize_unselected_instances() { std::set done; // prevent processing volumes twice @@ -3788,6 +3946,11 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) if (!_init_toolbar()) return false; +#if ENABLE_SIDEBAR_VISUAL_HINTS + if (!m_selection.init(m_use_VBOs)) + return false; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + #if ENABLE_REMOVE_TABS_FROM_PLATER post_event(SimpleEvent(EVT_GLCANVAS_INIT)); #endif // ENABLE_REMOVE_TABS_FROM_PLATER @@ -4165,6 +4328,10 @@ void GLCanvas3D::render() // this position is used later into on_mouse() to drag the objects m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast()); +#if ENABLE_SIDEBAR_VISUAL_HINTS + _render_selection_sidebar_hints(); +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + _render_current_gizmo(); #if ENABLE_SHOW_CAMERA_TARGET _render_camera_target(); @@ -5671,6 +5838,11 @@ bool GLCanvas3D::_init_toolbar() icons_data.icon_border_size = 1; icons_data.icon_gap_size = 1; +// icons_data.filename = "toolbar141.png"; +// icons_data.icon_size = 52; +// icons_data.icon_border_size = 0; +// icons_data.icon_gap_size = 0; + BackgroundTexture::Metadata background_data; background_data.filename = "toolbar_background.png"; background_data.left = 16; @@ -6577,6 +6749,19 @@ void GLCanvas3D::_render_sla_slices() const } } +#if ENABLE_SIDEBAR_VISUAL_HINTS +void GLCanvas3D::_render_selection_sidebar_hints() const +{ + if (m_use_VBOs) + m_shader.start_using(); + + m_selection.render_sidebar_hints(m_sidebar_field); + + if (m_use_VBOs) + m_shader.stop_using(); +} +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + void GLCanvas3D::_update_volumes_hover_state() const { for (GLVolume* v : m_volumes.volumes) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d4449aa7a..e73e1d835 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -368,6 +368,12 @@ class GLCanvas3D public: class Selection { +#if ENABLE_SIDEBAR_VISUAL_HINTS + static const float RED[3]; + static const float GREEN[3]; + static const float BLUE[3]; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + public: typedef std::set IndicesList; @@ -495,6 +501,9 @@ public: #if ENABLE_RENDER_SELECTION_CENTER GLUquadricObj* m_quadric; #endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_SIDEBAR_VISUAL_HINTS + mutable GLArrow m_arrow; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS public: Selection(); @@ -503,6 +512,9 @@ public: #endif // ENABLE_RENDER_SELECTION_CENTER void set_volumes(GLVolumePtrs* volumes); +#if ENABLE_SIDEBAR_VISUAL_HINTS + bool init(bool useVBOs); +#endif // ENABLE_SIDEBAR_VISUAL_HINTS Model* get_model() const { return m_model; } void set_model(Model* model); @@ -580,6 +592,9 @@ public: #if ENABLE_RENDER_SELECTION_CENTER void render_center() const; #endif // ENABLE_RENDER_SELECTION_CENTER +#if ENABLE_SIDEBAR_VISUAL_HINTS + void render_sidebar_hints(const std::string& sidebar_field) const; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS private: void _update_valid(); @@ -595,6 +610,16 @@ public: void _render_selected_volumes() const; void _render_synchronized_volumes() const; void _render_bounding_box(const BoundingBoxf3& box, float* color) const; +#if ENABLE_SIDEBAR_VISUAL_HINTS + void _render_sidebar_position_hints(const std::string& sidebar_field) const; + void _render_sidebar_rotation_hints(const std::string& sidebar_field) const; + void _render_sidebar_scale_hints(const std::string& sidebar_field) const; + void _render_sidebar_size_hints(const std::string& sidebar_field) const; + void _render_sidebar_position_hint(Axis axis) const; + void _render_sidebar_rotation_hint(Axis axis, double length) const; + void _render_sidebar_scale_hint(Axis axis) const; + void _render_sidebar_size_hint(Axis axis, double length) const; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS void _synchronize_unselected_instances(); void _synchronize_unselected_volumes(); #if ENABLE_ENSURE_ON_BED_WHILE_SCALING @@ -1055,6 +1080,9 @@ private: void _render_camera_target() const; #endif // ENABLE_SHOW_CAMERA_TARGET void _render_sla_slices() const; +#if ENABLE_SIDEBAR_VISUAL_HINTS + void _render_selection_sidebar_hints() const; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS void _update_volumes_hover_state() const; void _update_gizmos_data(); From e9990ed79ec708043f3eaa76b066f6a6c36ccdee Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 19 Dec 2018 14:47:16 +0100 Subject: [PATCH 044/132] Fix of SPE-691 Slicer crash after extruder change Added synchronization of GCodePreviewData between the front end / back end (GCodePreview data is only used if PrintStep psGCodeExport is finished). Added reset of GCodePreviewData on Print::apply() to conserve RAM. --- src/libslic3r/Config.cpp | 5 +++-- src/libslic3r/GCode/Analyzer.cpp | 17 +------------- src/libslic3r/GCode/PreviewData.cpp | 16 +++++++++++++ src/libslic3r/GCode/PreviewData.hpp | 5 +++++ src/slic3r/GUI/3DScene.hpp | 1 - src/slic3r/GUI/BackgroundSlicingProcess.cpp | 11 +++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GLCanvas3DManager.hpp | 1 - src/slic3r/GUI/GUI.hpp | 1 - src/slic3r/GUI/GUI_Preview.cpp | 25 +++++++++------------ src/slic3r/GUI/GUI_Preview.hpp | 1 - src/slic3r/GUI/Plater.cpp | 2 -- 12 files changed, 46 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 88e0c7664..2f61cc008 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -483,8 +483,9 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const t_options_map::const_iterator it2 = rhs.options.begin(); t_options_map::const_iterator it2_end = rhs.options.end(); for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2) - if (*it1->second != *it2->second) - return false; + if (it1->first != it2->first || *it1->second != *it2->second) + // key or value differ + return false; return it1 == it1_end && it2 == it2_end; } diff --git a/src/libslic3r/GCode/Analyzer.cpp b/src/libslic3r/GCode/Analyzer.cpp index 8212b1703..c32acd4e9 100644 --- a/src/libslic3r/GCode/Analyzer.cpp +++ b/src/libslic3r/GCode/Analyzer.cpp @@ -668,6 +668,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ { static GCodePreviewData::Extrusion::Layer& get_layer_at_z(GCodePreviewData::Extrusion::LayersList& layers, float z) { + //FIXME this has a terrible time complexity for (GCodePreviewData::Extrusion::Layer& layer : layers) { // if layer found, return it @@ -863,20 +864,4 @@ size_t GCodeAnalyzer::memory_used() const return out; } -GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2) -{ - return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), - clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]), - clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]), - clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3])); -} - -GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color) -{ - return GCodePreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]), - clamp(0.0f, 1.0f, f * color.rgba[1]), - clamp(0.0f, 1.0f, f * color.rgba[2]), - clamp(0.0f, 1.0f, f * color.rgba[3])); -} - } // namespace Slic3r diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index d4aa9bc02..e99eeac02 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -521,4 +521,20 @@ size_t GCodePreviewData::memory_used() const sizeof(shell) + sizeof(ranges); } +GCodePreviewData::Color operator + (const GCodePreviewData::Color& c1, const GCodePreviewData::Color& c2) +{ + return GCodePreviewData::Color(clamp(0.0f, 1.0f, c1.rgba[0] + c2.rgba[0]), + clamp(0.0f, 1.0f, c1.rgba[1] + c2.rgba[1]), + clamp(0.0f, 1.0f, c1.rgba[2] + c2.rgba[2]), + clamp(0.0f, 1.0f, c1.rgba[3] + c2.rgba[3])); +} + +GCodePreviewData::Color operator * (float f, const GCodePreviewData::Color& color) +{ + return GCodePreviewData::Color(clamp(0.0f, 1.0f, f * color.rgba[0]), + clamp(0.0f, 1.0f, f * color.rgba[1]), + clamp(0.0f, 1.0f, f * color.rgba[2]), + clamp(0.0f, 1.0f, f * color.rgba[3])); +} + } // namespace Slic3r diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 8ed5e91c7..4ca579d9a 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -22,6 +22,7 @@ public: static const Color Dummy; }; + // Color mapping from a range into a smooth rainbow of 10 colors. struct Range { static const unsigned int Colors_Count = 10; @@ -45,9 +46,13 @@ public: struct Ranges { + // Color mapping by layer height. Range height; + // Color mapping by extrusion width. Range width; + // Color mapping by feedrate. Range feedrate; + // Color mapping by volumetric extrusion rate. Range volumetric_rate; }; diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 97b0310e5..62fd8730e 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -18,7 +18,6 @@ class SLAPrintObject; enum SLAPrintObjectStep : unsigned int; class Model; class ModelObject; -class GCodePreviewData; class DynamicPrintConfig; class ExtrusionPath; class ExtrusionMultiPath; diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index d748919c9..66a0884a4 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -15,8 +15,8 @@ #include "libslic3r/SLAPrint.hpp" #include "libslic3r/Utils.hpp" #include "libslic3r/GCode/PostProcessor.hpp" +#include "libslic3r/GCode/PreviewData.hpp" -//#undef NDEBUG #include #include #include @@ -367,6 +367,13 @@ Print::ApplyStatus BackgroundSlicingProcess::apply(const Model &model, const Dyn assert(m_print != nullptr); assert(config.opt_enum("printer_technology") == m_print->technology()); Print::ApplyStatus invalidated = m_print->apply(model, config); + if ((invalidated & PrintBase::APPLY_STATUS_INVALIDATED) != 0 && m_print->technology() == ptFFF && + m_gcode_preview_data != nullptr && ! this->m_fff_print->is_step_done(psGCodeExport)) { + // Some FFF status was invalidated, and the G-code was not exported yet. + // Let the G-code preview UI know that the final G-code preview is not valid. + // In addition, this early memory deallocation reduces memory footprint. + m_gcode_preview_data->reset(); + } return invalidated; } @@ -392,7 +399,7 @@ void BackgroundSlicingProcess::schedule_upload(Slic3r::PrintHostJob upload_job) // Guard against entering the export step before changing the export path. tbb::mutex::scoped_lock lock(m_print->state_mutex()); this->invalidate_step(bspsGCodeFinalize); - m_export_path = std::string(); + m_export_path.clear(); m_upload_job = std::move(upload_job); } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 08368a131..7dd8c54a0 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -28,6 +28,7 @@ namespace Slic3r { class GLShader; class ExPolygon; class BackgroundSlicingProcess; +class GCodePreviewData; namespace GUI { diff --git a/src/slic3r/GUI/GLCanvas3DManager.hpp b/src/slic3r/GUI/GLCanvas3DManager.hpp index 1ed4a8251..64301c73d 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.hpp +++ b/src/slic3r/GUI/GLCanvas3DManager.hpp @@ -19,7 +19,6 @@ class ExPolygon; typedef std::vector ExPolygons; class ModelObject; class PrintObject; -class GCodePreviewData; namespace GUI { diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index e7ab0443d..3ca4292a9 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -16,7 +16,6 @@ namespace Slic3r { class AppConfig; class DynamicPrintConfig; class Print; -class GCodePreviewData; namespace GUI { diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 71c92f2d1..735b55125 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -395,12 +395,6 @@ void Preview::set_number_extruders(unsigned int number_extruders) } } -void Preview::reset_gcode_preview_data() -{ - m_gcode_preview_data->reset(); - m_canvas->reset_legend_texture(); -} - void Preview::set_canvas_as_dirty() { m_canvas->set_as_dirty(); @@ -451,6 +445,7 @@ void Preview::load_print() void Preview::reload_print(bool force) { m_canvas->reset_volumes(); + m_canvas->reset_legend_texture(); m_loaded = false; if (!IsShown() && !force) @@ -759,7 +754,8 @@ void Preview::load_print_as_fff() // Collect colors per extruder. std::vector colors; - if (!m_gcode_preview_data->empty() || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) + bool gcode_preview_data_valid = print->is_step_done(psGCodeExport) && ! m_gcode_preview_data->empty(); + if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool)) { const ConfigOptionStrings* extruders_opt = dynamic_cast(m_config->option("extruder_colour")); const ConfigOptionStrings* filamemts_opt = dynamic_cast(m_config->option("filament_colour")); @@ -785,13 +781,7 @@ void Preview::load_print_as_fff() // used to set the sliders to the extremes of the current zs range m_force_sliders_full_range = false; - if (m_gcode_preview_data->empty()) - { - // load skirt and brim - m_canvas->load_preview(colors); - show_hide_ui_elements("simple"); - } - else + if (gcode_preview_data_valid) { m_force_sliders_full_range = (m_canvas->get_volumes_count() == 0); m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); @@ -805,8 +795,15 @@ void Preview::load_print_as_fff() reset_sliders(); m_canvas_widget->Refresh(); } + } + else + { + // load skirt and brim + m_canvas->load_preview(colors); + show_hide_ui_elements("simple"); } + if (n_layers > 0) update_sliders(m_canvas->get_current_print_zs(true)); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 23e6a682f..534191633 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -140,7 +140,6 @@ public: #endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_number_extruders(unsigned int number_extruders); - void reset_gcode_preview_data(); void set_canvas_as_dirty(); void set_enabled(bool enabled); void set_bed_shape(const Pointfs& shape); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ea8a83871..c3f0565c8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1273,7 +1273,6 @@ void Plater::priv::update(bool force_full_scene_refresh) #else this->canvas3D->reload_scene(false, force_full_scene_refresh); #endif // ENABLE_REMOVE_TABS_FROM_PLATER - preview->reset_gcode_preview_data(); preview->reload_print(); this->schedule_background_process(); @@ -2010,7 +2009,6 @@ unsigned int Plater::priv::update_background_process() this->sidebar->show_sliced_info_sizer(false); // Reset preview canvases. If the print has been invalidated, the preview canvases will be cleared. // Otherwise they will be just refreshed. - this->gcode_preview_data.reset(); switch (this->printer_technology) { case ptFFF: if (this->preview != nullptr) From 918cb48d452d5a9eb555a8cdc668ec980104f87e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 14:50:56 +0100 Subject: [PATCH 045/132] Fixed OSX build & language selection (SPE-696) --- src/slic3r/GUI/GUI_App.cpp | 7 ++++--- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 88c77c193..2998ea7f3 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -434,7 +434,7 @@ bool GUI_App::select_language( wxArrayString & names, m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[index]); m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); - m_wxLocale->AddCatalog(GetAppName()); + m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); Preset::update_suffix_modified(); return true; @@ -461,7 +461,7 @@ bool GUI_App::load_language() m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[i]); m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); - m_wxLocale->AddCatalog(GetAppName()); + m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); Preset::update_suffix_modified(); return true; @@ -504,7 +504,8 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident { auto full_file_name = dir.GetName() + wxFileName::GetPathSeparator() + filename + wxFileName::GetPathSeparator() + - GetAppName() + wxT(".mo"); + /*GetAppName()*/"Slic3rPE" + + wxT(".mo"); if (wxFileExists(full_file_name)) { names.Add(langinfo->Description); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 87010f081..b3edbc9a8 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -23,7 +23,7 @@ KBShortcutsDialog::KBShortcutsDialog() wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); font.SetPointSize(10); - const wxFont bold_font = font.Bold(); + wxFont bold_font = font.Bold(); #ifdef __WXOSX__ font.SetPointSize(12); bold_font.SetPointSize(14); From 2548253d59054330c11479d56421e55e90cf6430 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 19 Dec 2018 15:03:49 +0100 Subject: [PATCH 046/132] Another fix on volumes translation --- src/slic3r/GUI/GLCanvas3D.cpp | 16 ++++++++-------- src/slic3r/GUI/GLCanvas3D.hpp | 5 +++-- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 5 +++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7facc3623..3c4debba0 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1549,7 +1549,7 @@ void GLCanvas3D::Selection::start_dragging() _set_caches(); } -void GLCanvas3D::Selection::translate(const Vec3d& displacement) +void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local) { if (!m_valid) return; @@ -1559,7 +1559,7 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement) #if ENABLE_MODELVOLUME_TRANSFORM if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) { - if (_requires_local_axes()) + if (local) (*m_volumes)[i]->set_volume_offset(m_cache.volumes_data[i].get_volume_position() + displacement); else { @@ -1614,7 +1614,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) else if (is_single_volume() || is_single_modifier()) #if ENABLE_WORLD_ROTATIONS { - if (_requires_local_axes()) + if (requires_local_axes()) (*m_volumes)[i]->set_volume_rotation(rotation); else { @@ -2118,6 +2118,11 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel } #endif // ENABLE_SIDEBAR_VISUAL_HINTS +bool GLCanvas3D::Selection::requires_local_axes() const +{ + return (m_mode == Volume) && is_from_single_instance(); +} + void GLCanvas3D::Selection::_update_valid() { m_valid = (m_volumes != nullptr) && (m_model != nullptr); @@ -2746,11 +2751,6 @@ void GLCanvas3D::Selection::_ensure_on_bed() } #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING -bool GLCanvas3D::Selection::_requires_local_axes() const -{ - return (m_mode == Volume) && is_from_single_instance(); -} - const float GLCanvas3D::Gizmos::OverlayIconsScale = 1.0f; const float GLCanvas3D::Gizmos::OverlayBorder = 5.0f; const float GLCanvas3D::Gizmos::OverlayGapY = 5.0f * OverlayIconsScale; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 54e546c78..413d625f1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -578,7 +578,7 @@ public: void start_dragging(); - void translate(const Vec3d& displacement); + void translate(const Vec3d& displacement, bool local = false); void rotate(const Vec3d& rotation, bool local); void flattening_rotate(const Vec3d& normal); void scale(const Vec3d& scale, bool local); @@ -597,6 +597,8 @@ public: void render_sidebar_hints(const std::string& sidebar_field) const; #endif // ENABLE_SIDEBAR_VISUAL_HINTS + bool requires_local_axes() const; + private: void _update_valid(); void _update_type(); @@ -626,7 +628,6 @@ public: #if ENABLE_ENSURE_ON_BED_WHILE_SCALING void _ensure_on_bed(); #endif // ENABLE_ENSURE_ON_BED_WHILE_SCALING - bool _requires_local_axes() const; }; class ClippingPlane diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index ddf699c2c..8cc2362e8 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -361,8 +361,9 @@ void ObjectManipulation::update_rotation_value(const Vec3d& rotation) void ObjectManipulation::change_position_value(const Vec3d& position) { auto canvas = wxGetApp().plater()->canvas3D(); - canvas->get_selection().start_dragging(); - canvas->get_selection().translate(position - cache_position); + GLCanvas3D::Selection& selection = canvas->get_selection(); + selection.start_dragging(); + selection.translate(position - cache_position, selection.requires_local_axes()); canvas->do_move(); cache_position = position; From 0ac4d13015dc93918be8ef563c435a8aaec8ca3b Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Dec 2018 15:00:30 +0100 Subject: [PATCH 047/132] Print host: Check OctoPrint vs SLA --- src/slic3r/GUI/Tab.cpp | 3 +- src/slic3r/Utils/Duet.hpp | 12 ++++---- src/slic3r/Utils/OctoPrint.cpp | 53 +++++++++++++++++++++++++++------- src/slic3r/Utils/OctoPrint.hpp | 25 +++++++++------- src/slic3r/Utils/PrintHost.cpp | 2 +- 5 files changed, 65 insertions(+), 30 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 0b59a21ab..5a212d4ee 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1551,7 +1551,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) auto printhost_browse = [this, optgroup] (wxWindow* parent) { - // TODO: SLA + // TODO: SLA Bonjour auto btn = m_printhost_browse_btn = new wxButton(parent, wxID_ANY, _(L(" Browse "))+dots, wxDefaultPosition, wxDefaultSize, wxBU_LEFT); btn->SetBitmap(wxBitmap(from_u8(Slic3r::var("zoom.png")), wxBITMAP_TYPE_PNG)); @@ -1562,6 +1562,7 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) BonjourDialog dialog(parent); if (dialog.show_and_lookup()) { optgroup->set_value("print_host", std::move(dialog.get_selected()), true); + // FIXME: emit killfocus on the edit widget } }); diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index 0608f85a5..d0f5b3009 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -19,12 +19,12 @@ public: Duet(DynamicPrintConfig *config); virtual ~Duet(); - bool test(wxString &curl_msg) const; - wxString get_test_ok_msg () const; - wxString get_test_failed_msg (wxString &msg) const; - bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; - bool has_auto_discovery() const; - bool can_test() const; + virtual bool test(wxString &curl_msg) const; + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool has_auto_discovery() const; + virtual bool can_test() const; virtual std::string get_host() const { return host; } private: std::string host; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index cbb81c54f..cf5fc2f54 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -1,8 +1,12 @@ #include "OctoPrint.hpp" #include +#include #include #include +#include +#include +#include #include @@ -12,6 +16,7 @@ namespace fs = boost::filesystem; +namespace pt = boost::property_tree; namespace Slic3r { @@ -41,11 +46,29 @@ bool OctoPrint::test(wxString &msg) const res = false; msg = format_error(body, error, status); }) - .on_complete([&](std::string body, unsigned) { + .on_complete([&, this](std::string body, unsigned) { BOOST_LOG_TRIVIAL(debug) << boost::format("Octoprint: Got version: %1%") % body; - // TODO: parse body, call validate_version_text + try { + std::stringstream ss(body); + pt::ptree ptree; + pt::read_json(ss, ptree); + if (! ptree.get_optional("api")) { + res = false; + return; + } + + const auto text = ptree.get_optional("text"); + res = validate_version_text(text); + if (! res) { + msg = wxString::Format("Mismatched type of print host: %s", text ? *text : "OctoPrint"); + } + } + catch (...) { + res = false; + msg = "Could not parse server response"; + } }) .perform_sync(); @@ -71,7 +94,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn wxString test_msg; if (! test(test_msg)) { - // TODO: + // FIXME: // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); // GUI::show_error(&progress_dialog, std::move(errormsg)); @@ -125,10 +148,9 @@ bool OctoPrint::can_test() const return true; } -bool OctoPrint::validate_version_text(const std::string &version_text) +bool OctoPrint::validate_version_text(const boost::optional &version_text) const { - // FIXME - return true; + return version_text ? boost::starts_with(*version_text, "OctoPrint") : true; } void OctoPrint::set_auth(Http &http) const @@ -164,14 +186,23 @@ wxString OctoPrint::format_error(const std::string &body, const std::string &err } -// SL1 +// SLAHost -SL1Host::~SL1Host() {} +SLAHost::~SLAHost() {} -bool SL1Host::validate_version_text(const std::string &version_text) +wxString SLAHost::get_test_ok_msg () const { - // FIXME - return true; + return wxString::Format("%s", _(L("Connection to Prusa SLA works correctly."))); +} + +wxString SLAHost::get_test_failed_msg (wxString &msg) const +{ + return wxString::Format("%s: %s", _(L("Could not connect to Prusa SLA")), msg); +} + +bool SLAHost::validate_version_text(const boost::optional &version_text) const +{ + return version_text ? boost::starts_with(*version_text, "Prusa SLA") : false; } diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 4d6555e13..57aae672a 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "PrintHost.hpp" @@ -19,16 +20,16 @@ public: OctoPrint(DynamicPrintConfig *config); virtual ~OctoPrint(); - bool test(wxString &curl_msg) const; - wxString get_test_ok_msg () const; - wxString get_test_failed_msg (wxString &msg) const; - bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; - bool has_auto_discovery() const; - bool can_test() const; + virtual bool test(wxString &curl_msg) const; + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; + virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool has_auto_discovery() const; + virtual bool can_test() const; virtual std::string get_host() const { return host; } protected: - virtual bool validate_version_text(const std::string &version_text); + virtual bool validate_version_text(const boost::optional &version_text) const; private: std::string host; @@ -41,14 +42,16 @@ private: }; -class SL1Host: public OctoPrint +class SLAHost: public OctoPrint { public: - SL1Host(DynamicPrintConfig *config) : OctoPrint(config) {} - virtual ~SL1Host(); + SLAHost(DynamicPrintConfig *config) : OctoPrint(config) {} + virtual ~SLAHost(); + virtual wxString get_test_ok_msg () const; + virtual wxString get_test_failed_msg (wxString &msg) const; protected: - virtual bool validate_version_text(const std::string &version_text); + virtual bool validate_version_text(const boost::optional &version_text) const; }; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index cdd0c107e..5c4507816 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -30,8 +30,8 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) switch (opt->value) { case htOctoPrint: return new OctoPrint(config); - case htSL1: return new SL1Host(config); case htDuet: return new Duet(config); + case htSL1: return new SLAHost(config); default: return nullptr; } } From 17c2f3d81306d0d1f280821ae49ee128e9edf683 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Dec 2018 15:40:07 +0100 Subject: [PATCH 048/132] Http: Set a sane default connection timeout --- src/slic3r/Utils/Http.cpp | 9 +++++++++ src/slic3r/Utils/Http.hpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 6e6c9ed44..27f713127 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -32,6 +32,7 @@ class CurlGlobalInit struct Http::priv { enum { + DEFAULT_TIMEOUT = 10, DEFAULT_SIZE_LIMIT = 5 * 1024 * 1024, }; @@ -84,6 +85,7 @@ Http::priv::priv(const std::string &url) throw std::runtime_error(std::string("Could not construct Curl object")); } + ::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_TIMEOUT); ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); @@ -293,6 +295,13 @@ Http::~Http() } +Http& Http::timeout(long timeout) +{ + if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT; } + if (p) { ::curl_easy_setopt(p->curl, CURLOPT_CONNECTTIMEOUT, timeout); } + return *this; +} + Http& Http::size_limit(size_t sizeLimit) { if (p) { p->limit = sizeLimit; } diff --git a/src/slic3r/Utils/Http.hpp b/src/slic3r/Utils/Http.hpp index fd3f8830d..9406a82f2 100644 --- a/src/slic3r/Utils/Http.hpp +++ b/src/slic3r/Utils/Http.hpp @@ -55,6 +55,8 @@ public: Http& operator=(const Http &) = delete; Http& operator=(Http &&) = delete; + // Sets a maximum connection timeout in seconds + Http& timeout(long timeout); // Sets a maximum size of the data that can be received. // A value of zero sets the default limit, which is is 5MB. Http& size_limit(size_t sizeLimit); From 4c55f1ce9e57537abf9502ef9f4af9b7bf8da579 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 15:58:01 +0100 Subject: [PATCH 049/132] DoubleSlider issues (1 & 3 from SPE-686) + added icon for "Keyboard shortcuts" dialog --- resources/icons/Slic3r_32px.png | Bin 0 -> 1771 bytes src/slic3r/GUI/GUI_Preview.cpp | 3 ++- src/slic3r/GUI/wxExtensions.cpp | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 resources/icons/Slic3r_32px.png diff --git a/resources/icons/Slic3r_32px.png b/resources/icons/Slic3r_32px.png new file mode 100644 index 0000000000000000000000000000000000000000..6bf5a9cd144b3059b70071bc978ca369b7b54778 GIT binary patch literal 1771 zcmV;P)~26#zC zK~z}7?U!9_99I>`f9Kwro!zy)j@ORkq)wbP2~r3&n5L+OBtj*53QdAai^@k4>O&w3Mrvomw={!Q8F$XGZ5|iW&1q;7`DWjJaCJAa_6?hRCr5Bx~*IwQ(u~Y)$p>p|j@-eZQM39O>=t!t=Zp zo-3u6L6#kH4uB*{$Y;IeE1&H9%M;%cIdLHV+n4rS{ubwxe*!TF(49s^0|USZ<|5tu!iBC8ixe`M%nAst zHJ<0Kf@y_#WdG7XzOeVLZ}9Go3o_;sH!fZ~U9JTZq#(QhFd1a8mHmCqj1Uo$Bq0a_ zeBWOM(*)7jzPgI!^@F~qKJGxkLP*# zzK`d5c%HW`R9~z;bRf3TI2gZxt!-7ecspHF;-lDH4u!0<-joO3HGsMfg0 z?!P*Dq<21+w}-MArAX?m1aSqDV067`v;#?+DK?Zd^UcX9il|g7IOo#J>Gj?yNWC1S+S?$AFo$sIK!05R_Wg^O%jGiFYLzeydFJ7F7LNDM8}+82r5bCJ zaCH=*F;_1`Q~?uJQxMxOGHZ*~Fb7=lwf*&HKN!AxbXWP-?CdOOJ~37P+=#E#RI!Ix z>9+1!3t<_OumNMu^(I8el~u-^bEl<>$1OC~Y>vs7pB4|)DUzXW*nuvbYM%i~y`dEo zLAcoXRs4OuXteJ~4q$*PbwXkmfC@5l7+bivErf|74jUlOKz$M7DnKO#X&a688Y2NL z0gjPR*O-`mjGBz%oVV%~m^vg8*yMeyFF{gkq^|G_G!u8rn!9)LKfLc*BK0h>&Eagr zTWiuyO+%E;`MB1w_cLe##W6N>PM&eLW02+tsTo4|UHK;U%vmJpLV2LmzDA_sZ4=1E zVB^OBmC^qi@M6a&JvT10y!m)~q`b=W)c7ivEdSkj9ylXouG-1X%8iR|P2&_uk=fXh zv{kmicYxh+u^S1WuUUT6>0;%G*M5Ipp8ELD)vvtot1hi| zQ7P4}l=6MwSDxp!bxhp26o9Hc>5>$rM=3R$B*}K5d(S3{4{vwFr}l8)!6EkbXX$TU zV4=>!>(g9$_$mMZ N002ovPDHLkV1i@hUjYCB literal 0 HcmV?d00001 diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 735b55125..2885a3d16 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -587,7 +587,8 @@ void Preview::create_double_slider() auto& config = wxGetApp().preset_bundle->project_config; ((config.option("colorprint_heights"))->values) = (m_slider->GetTicksValues()); m_schedule_background_process(); - int type = m_choice_view_type->FindString(_(L("Color Print"))); + bool color_print = !config.option("colorprint_heights")->values.empty(); + int type = m_choice_view_type->FindString(color_print ? _(L("Color Print")) : _(L("Feature type")) ); if (m_choice_view_type->GetSelection() != type) { m_choice_view_type->SetSelection(type); if ((0 <= type) && (type < (int)GCodePreviewData::Extrusion::Num_View_Types)) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 2daba5df4..b8b76f049 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -2043,7 +2043,7 @@ void PrusaDoubleSlider::enter_window(wxMouseEvent& event, const bool enter) // - value decrease (if wxSL_HORIZONTAL) void PrusaDoubleSlider::move_current_thumb(const bool condition) { - m_is_one_layer = wxGetKeyState(WXK_CONTROL); +// m_is_one_layer = wxGetKeyState(WXK_CONTROL); int delta = condition ? -1 : 1; if (is_horizontal()) delta *= -1; From c40b8aba2410e09984d9025c383e10287955e7a6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 19 Dec 2018 17:38:41 +0100 Subject: [PATCH 050/132] Fixed recreate_GUI() after language change. --- src/slic3r/GUI/GUI_App.cpp | 20 +++++++++++++++++--- src/slic3r/GUI/GUI_App.hpp | 1 - src/slic3r/GUI/MainFrame.cpp | 12 ++++-------- src/slic3r/GUI/MainFrame.hpp | 8 ++------ 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 2998ea7f3..bbba1d93b 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -137,7 +137,7 @@ bool GUI_App::OnInit() std::cerr << "Creating main frame..." << std::endl; if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) wxImage::AddHandler(new wxPNGHandler()); - mainframe = new MainFrame(no_plater, false); + mainframe = new MainFrame(); sidebar().obj_list()->init_objects(); // propagate model objects to object list update_mode(); SetTopWindow(mainframe); @@ -277,8 +277,8 @@ void GUI_App::recreate_GUI() { std::cerr << "recreate_GUI" << std::endl; - auto topwindow = GetTopWindow(); - mainframe = new MainFrame(no_plater,false); + MainFrame* topwindow = dynamic_cast(GetTopWindow()); + mainframe = new MainFrame(); sidebar().obj_list()->init_objects(); // propagate model objects to object list update_mode(); @@ -287,6 +287,20 @@ void GUI_App::recreate_GUI() topwindow->Destroy(); } + m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg())); + + CallAfter([this]() { + // temporary workaround for the correct behavior of the Scrolled sidebar panel + auto& panel = sidebar(); + if (panel.obj_list()->GetMinHeight() > 200) { + wxWindowUpdateLocker noUpdates_sidebar(&panel); + panel.obj_list()->SetMinSize(wxSize(-1, 200)); + panel.Layout(); + } + }); + + mainframe->Show(true); + // On OSX the UI was not initialized correctly if the wizard was called // before the UI was up and running. CallAfter([]() { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 81175b7ca..bd64a3ac5 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -71,7 +71,6 @@ static wxString dots("…", wxConvUTF8); class GUI_App : public wxApp { - bool no_plater{ false }; bool app_conf_exists{ false }; // Lock to guard the callback stack diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 2211023f0..871d50a3d 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -28,10 +28,8 @@ namespace Slic3r { namespace GUI { -MainFrame::MainFrame(const bool no_plater, const bool loaded) : +MainFrame::MainFrame() : wxFrame(NULL, wxID_ANY, SLIC3R_BUILD, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE), - m_no_plater(no_plater), - m_loaded(loaded), m_printhost_queue_dlg(new PrintHostQueueDialog(this)) { // Load the icon either from the exe, or from the ico file. @@ -125,11 +123,9 @@ void MainFrame::init_tabpanel() } }); - if (!m_no_plater) { - m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); - wxGetApp().plater_ = m_plater; - m_tabpanel->AddPage(m_plater, _(L("Plater"))); - } + m_plater = new Slic3r::GUI::Plater(m_tabpanel, this); + wxGetApp().plater_ = m_plater; + m_tabpanel->AddPage(m_plater, _(L("Plater"))); // The following event is emited by Tab implementation on config value change. Bind(EVT_TAB_VALUE_CHANGED, &MainFrame::on_value_changed, this); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index fab6aea90..e0411b6da 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -42,10 +42,7 @@ struct PresetTab { class MainFrame : public wxFrame { - bool m_no_plater; - bool m_loaded; - int m_lang_ch_event; - int m_preferences_event; + bool m_loaded {false}; wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; @@ -71,8 +68,7 @@ class MainFrame : public wxFrame bool can_delete_all() const; public: - MainFrame() {} - MainFrame(const bool no_plater, const bool loaded); + MainFrame(); ~MainFrame() {} Plater* plater() { return m_plater; } From 3b2c28fa89994230b86a0eb6e681d3ab670e3e6e Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 19 Dec 2018 18:43:03 +0100 Subject: [PATCH 051/132] Printhost: Polish error handling, bugfixes --- src/slic3r/GUI/PrintHostDialogs.cpp | 10 ++++++++-- src/slic3r/Utils/Duet.cpp | 4 ++-- src/slic3r/Utils/Duet.hpp | 2 +- src/slic3r/Utils/Http.cpp | 15 +++++++++++---- src/slic3r/Utils/OctoPrint.cpp | 21 +++++++++------------ src/slic3r/Utils/OctoPrint.hpp | 2 +- src/slic3r/Utils/PrintHost.cpp | 26 +++++++++++++++----------- src/slic3r/Utils/PrintHost.hpp | 6 +++++- 8 files changed, 52 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 8ac8615a8..586fe3d83 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -102,7 +102,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT); auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); - auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); + auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); // TODO: enable based on status ("show error" for failed jobs) auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING); btnsizer->AddStretchSpacer(); @@ -140,7 +140,13 @@ void PrintHostQueueDialog::on_error(Event &evt) { wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); - // TODO + job_list->SetValue(wxVariant(0), evt.job_id, 1); + job_list->SetValue(wxVariant(_(L("Error"))), evt.job_id, 2); + + // TODO: keep the error for repeated display + + auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error); + GUI::show_error(nullptr, std::move(errormsg)); } diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index 1772ae8ef..fd77fc130 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -46,7 +46,7 @@ bool Duet::test(wxString &msg) const wxString Duet::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to Duet works correctly."))); + return _(L("Connection to Duet works correctly.")); } wxString Duet::get_test_failed_msg (wxString &msg) const @@ -135,7 +135,7 @@ wxString Duet::get_test_failed_msg (wxString &msg) const // return res; // } -bool Duet::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const +bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { // XXX: TODO throw "unimplemented"; diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index d0f5b3009..e053f91ef 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -22,7 +22,7 @@ public: virtual bool test(wxString &curl_msg) const; virtual wxString get_test_ok_msg () const; virtual wxString get_test_failed_msg (wxString &msg) const; - virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; virtual bool has_auto_discovery() const; virtual bool can_test() const; virtual std::string get_host() const { return host; } diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 27f713127..30478cb01 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -64,6 +64,7 @@ struct Http::priv static int xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow); static size_t form_file_read_cb(char *buffer, size_t size, size_t nitems, void *userp); + void set_timeout(long timeout); void form_add_file(const char *name, const fs::path &path, const char* filename); void set_post_body(const fs::path &path); @@ -85,7 +86,7 @@ Http::priv::priv(const std::string &url) throw std::runtime_error(std::string("Could not construct Curl object")); } - ::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, DEFAULT_TIMEOUT); + set_timeout(DEFAULT_TIMEOUT); ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); // curl makes a copy internally ::curl_easy_setopt(curl, CURLOPT_USERAGENT, SLIC3R_FORK_NAME "/" SLIC3R_VERSION); ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &error_buffer.front()); @@ -169,6 +170,12 @@ size_t Http::priv::form_file_read_cb(char *buffer, size_t size, size_t nitems, v return stream->gcount(); } +void Http::priv::set_timeout(long timeout) +{ + ::curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, timeout); + ::curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout); +} + void Http::priv::form_add_file(const char *name, const fs::path &path, const char* filename) { // We can't use CURLFORM_FILECONTENT, because curl doesn't support Unicode filenames on Windows @@ -205,10 +212,10 @@ void Http::priv::set_post_body(const fs::path &path) std::string Http::priv::curl_error(CURLcode curlcode) { - return (boost::format("%1% (%2%): %3%") + return (boost::format("%1%:\n%2%\n[Error %3%]") % ::curl_easy_strerror(curlcode) + % error_buffer.c_str() % curlcode - % error_buffer ).str(); } @@ -298,7 +305,7 @@ Http::~Http() Http& Http::timeout(long timeout) { if (timeout < 1) { timeout = priv::DEFAULT_TIMEOUT; } - if (p) { ::curl_easy_setopt(p->curl, CURLOPT_CONNECTTIMEOUT, timeout); } + if (p) { p->set_timeout(timeout); } return *this; } diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index cf5fc2f54..af9d6e4f0 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -62,7 +62,7 @@ bool OctoPrint::test(wxString &msg) const const auto text = ptree.get_optional("text"); res = validate_version_text(text); if (! res) { - msg = wxString::Format("Mismatched type of print host: %s", text ? *text : "OctoPrint"); + msg = wxString::Format(_(L("Mismatched type of print host: %s")), text ? *text : "OctoPrint"); } } catch (...) { @@ -77,28 +77,24 @@ bool OctoPrint::test(wxString &msg) const wxString OctoPrint::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to OctoPrint works correctly."))); + return _(L("Connection to OctoPrint works correctly.")); } wxString OctoPrint::get_test_failed_msg (wxString &msg) const { return wxString::Format("%s: %s\n\n%s", - _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); + _(L("Could not connect to OctoPrint")), msg, _(L("Note: OctoPrint version at least 1.1.0 is required."))); } -bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const +bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { const auto upload_filename = upload_data.upload_path.filename(); const auto upload_parent_path = upload_data.upload_path.parent_path(); wxString test_msg; if (! test(test_msg)) { - - // FIXME: - - // auto errormsg = wxString::Format("%s: %s", errortitle, test_msg); - // GUI::show_error(&progress_dialog, std::move(errormsg)); - // return false; + error_fn(std::move(test_msg)); + return false; } bool res = true; @@ -122,7 +118,8 @@ bool OctoPrint::upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn }) .on_error([&](std::string body, std::string error, unsigned status) { BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - error_fn(std::move(body), std::move(error), status); + // error_fn(std::move(body), std::move(error), status); + error_fn(format_error(body, error, status)); res = false; }) .on_progress([&](Http::Progress progress, bool &cancel) { @@ -192,7 +189,7 @@ SLAHost::~SLAHost() {} wxString SLAHost::get_test_ok_msg () const { - return wxString::Format("%s", _(L("Connection to Prusa SLA works correctly."))); + return _(L("Connection to Prusa SLA works correctly.")); } wxString SLAHost::get_test_failed_msg (wxString &msg) const diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 57aae672a..1e739c99d 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -23,7 +23,7 @@ public: virtual bool test(wxString &curl_msg) const; virtual wxString get_test_ok_msg () const; virtual wxString get_test_failed_msg (wxString &msg) const; - virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const; virtual bool has_auto_discovery() const; virtual bool can_test() const; virtual std::string get_host() const { return host; } diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 5c4507816..934436a19 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -2,10 +2,12 @@ #include #include +#include #include #include #include +#include #include #include "libslic3r/PrintConfig.hpp" @@ -58,7 +60,6 @@ struct PrintHostJobQueue::priv void start_bg_thread(); void bg_thread_main(); void progress_fn(Http::Progress progress, bool &cancel); - void error_fn(std::string body, std::string error, unsigned http_status); void perform_job(PrintHostJob the_job); }; @@ -100,6 +101,9 @@ void PrintHostJobQueue::priv::bg_thread_main() } job_id++; } + } catch (const std::exception &e) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, e.what()); + wxQueueEvent(queue_dialog, evt); } catch (...) { wxTheApp->OnUnhandledException(); } @@ -136,28 +140,28 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) } } -void PrintHostJobQueue::priv::error_fn(std::string body, std::string error, unsigned http_status) -{ - // TODO -} - void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) { if (bg_exit || the_job.empty()) { return; } - BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%1%`") + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%2%`") % the_job.upload_data.upload_path % the_job.printhost->get_host(); const fs::path gcode_path = the_job.upload_data.source_path; - the_job.printhost->upload(std::move(the_job.upload_data), + bool success = the_job.printhost->upload(std::move(the_job.upload_data), [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, - [this](std::string body, std::string error, unsigned http_status) { this->error_fn(std::move(body), std::move(error), http_status); } + [this](wxString error) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error)); + wxQueueEvent(queue_dialog, evt); + } ); - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); - wxQueueEvent(queue_dialog, evt); + if (success) { + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); + wxQueueEvent(queue_dialog, evt); + } boost::system::error_code ec; fs::remove(gcode_path, ec); diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 52ef38058..a6c7a4723 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -28,10 +29,13 @@ class PrintHost public: virtual ~PrintHost(); + typedef Http::ProgressFn ProgressFn; + typedef std::function ErrorFn; + virtual bool test(wxString &curl_msg) const = 0; virtual wxString get_test_ok_msg () const = 0; virtual wxString get_test_failed_msg (wxString &msg) const = 0; - virtual bool upload(PrintHostUpload upload_data, Http::ProgressFn prorgess_fn, Http::ErrorFn error_fn) const = 0; + virtual bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const = 0; virtual bool has_auto_discovery() const = 0; virtual bool can_test() const = 0; virtual std::string get_host() const = 0; From 64f46999d210f2e63a061c70f9d9d9c831e9727f Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 08:38:46 +0100 Subject: [PATCH 052/132] New icons for sla support points gizmo overlay --- .../overlay/sla_support_points_hover.png | Bin 3282 -> 2057 bytes .../icons/overlay/sla_support_points_off.png | Bin 1512 -> 1947 bytes .../icons/overlay/sla_support_points_on.png | Bin 1435 -> 2869 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/icons/overlay/sla_support_points_hover.png b/resources/icons/overlay/sla_support_points_hover.png index 6303232b24de8cbdc9f930fa33582591d9a8e454..55955f3d6f1d8e1821a1b5f1212f78efb907b1d0 100644 GIT binary patch literal 2057 zcmaJ?d0Z2B79S3|BTx@6!Ex9w!ZMl6LUB;P6S;4@YDHxW`6VDyzhJ8 zcYUTok-m=R6Y2v108N%E&ZN#{=k4V|J^!RH9;c2FQj$YvnY3gXZXp1n#*|NhG6P;f zWD>ZhqWB__003?_y)uW)k#FFuO$Ivdw4s+9%oG{`5|YZzxVn%a!F-}XZxle&XZj#e zuMt4mY&k=277;pqYNdt9s!Ug^D+|>;4V07!CY19j2L^(~!E!^9(aJ9uK+pa1sduLu zfxzb`WT60>CzT^tfFhHH0NHd7tVUUEFpft@IZPIh7Ykwxl#4L92+D*}4xh!~V;Hz_ zK~yx0M$69>OBZ5Mk^s_?q?wN(Wo2dbGA7+*DL_yjkB2ZY1jAs;0=8BdNxU33S|b-d zhzYCOqBoOzlM!@!#PdxhqyVCr&Zl57%jGW|8?6h4qDqF8<7Nb=t~KW)M7G#&nwN>zk-EYo6pnmm^h3xxG2mbI6N3< zYFRLg$6&CyY8D=cVbB6z^HK%pCzaArnV1&|@$>U>7Kg#+!pt}}6UNyr45kzqjA}3~ zhoeSuHHUx@iX!41o|lGcQAJIt^Yz7QP{NDlB8=2VS*X?dtHr7S0QV4?Sg0%?nQU2~ zs@xcS{;sxR1d@BNX?&wQB)VvS)28m!%&Ju4@Qf;_R<+SLH+bg83>Y)mZr`1^cF9UX zQ^PrlP#KUGlCtqTXh2!{f&BOBKb`1pxO98Gr%e0#iDv7~Z$hRIRz>tpzZFrYZK`v$ zNA3H2Y(`y_OEM4uxYvA<-c8$j@%vKW_Uw;c|9MWw28LT)TkAh;FTS>X%ld{wVAbQ= zn|;G>x4SBzj?{!0{}Nu4He3kGe(e3?`iA2AKLd)4!`K8q5HqH@rvg4pqMi1VACm~a z+GdH{U=7zFcde;Dn zV5DdG)}0vJBV+aZY|;Kf_K|Q8_n1?A4%An77QF7?yL06GPk;3paxKG!RUO_OblPP~ z6IZk;y3%{Zf6P{@&dc)C26bn6_D9;1|D8B+LNfj}cy`qD*-9aKb6NN9v1HD)`;Jc= z!v^sKmr4{t-7DN?;WMpyxx(h(eLHfYsPf6GkZl=HTcIRXOUt{QW1WU|ldi0!DT#X1|nW$bqRY3Ddu1wr4I(Bl7#Q!gCFI=tBo_?{(#Y@q3$7f6P8k z3k`T6WRq__yz4HEJ=(L!fqZ!%9X|UNEy}UvVB2}SF0Ik`mTnm`w#bxZ?2Dc z^sg$dPt6}5#RIB$OKj8K=&JBcyBZt#;QF$;_=DB&FFiEd z_KkXQ)seu$s@%CYVmv3vv9$B@tp~$_m!RM$vT76pya3l400e$vo{h8v+Y}kfUIVIg P{+wl!bn)?&yt@AZI_VI& delta 3278 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 M07*qoM6N<$f_b@Sr2qf` diff --git a/resources/icons/overlay/sla_support_points_off.png b/resources/icons/overlay/sla_support_points_off.png index b654366d578df6a7407d9124264fc818deef9423..7bf84a3e647ea4232ed940b3e9609d4707310a52 100644 GIT binary patch literal 1947 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 diff --git a/resources/icons/overlay/sla_support_points_on.png b/resources/icons/overlay/sla_support_points_on.png index 8c1e69565076723113a310ffad661ba0a4376977..879b9b24128b825efd77c29f9d84aa7b0449f1c1 100644 GIT binary patch literal 2869 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 Date: Thu, 20 Dec 2018 10:55:50 +0100 Subject: [PATCH 053/132] Improvements of assigning the layer span to the layer slider on slice update. --- src/slic3r/GUI/GUI_Preview.cpp | 105 ++++++++++++++++++-------------- src/slic3r/GUI/GUI_Preview.hpp | 6 +- src/slic3r/GUI/wxExtensions.cpp | 15 +++++ src/slic3r/GUI/wxExtensions.hpp | 21 ++++--- 4 files changed, 88 insertions(+), 59 deletions(-) diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 735b55125..579cd7e5e 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -214,7 +214,6 @@ Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSli , m_preferred_color_mode("feature") , m_loaded(false) , m_enabled(false) - , m_force_sliders_full_range(false) , m_schedule_background_process(schedule_background_process_func) { #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -514,14 +513,14 @@ void Preview::show_hide_ui_elements(const std::string& what) void Preview::reset_sliders() { m_enabled = false; - reset_double_slider(); +// reset_double_slider(); m_double_slider_sizer->Hide((size_t)0); } void Preview::update_sliders(const std::vector& layers_z) { m_enabled = true; - update_double_slider(layers_z, m_force_sliders_full_range); + update_double_slider(layers_z); m_double_slider_sizer->Show((size_t)0); Layout(); } @@ -597,26 +596,70 @@ void Preview::create_double_slider() }); } +// Find an index of a value in a sorted vector, which is in . +// Returns -1 if there is no such member. +static int find_close_layer_idx(const std::vector& zs, double &z, double eps) +{ + if (zs.empty()) + return -1; + auto it_h = std::lower_bound(zs.begin(), zs.end(), z); + if (it_h == zs.end()) { + auto it_l = it_h; + -- it_l; + if (z - *it_l < eps) + return int(zs.size() - 1); + } else if (it_h == zs.begin()) { + if (*it_h - z < eps) + return 0; + } else { + auto it_l = it_h; + -- it_l; + double dist_l = z - *it_l; + double dist_h = *it_h - z; + if (std::min(dist_l, dist_h) < eps) { + return (dist_l < dist_h) ? int(it_l - zs.begin()) : int(it_h - zs.begin()); + } + } + return -1; +} + void Preview::update_double_slider(const std::vector& layers_z, bool force_sliders_full_range) { + // Save the initial slider span. + double z_low = m_slider->GetLowerValueD(); + double z_high = m_slider->GetHigherValueD(); + bool was_empty = m_slider->GetMaxValue() == 0; + bool span_changed = layers_z.empty() || std::abs(layers_z.back() - m_slider->GetMaxValueD()) > 1e-6; + force_sliders_full_range |= was_empty | span_changed; + bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); + bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); + std::vector> values; fill_slider_values(values, layers_z); - - m_slider->SetMaxValue(layers_z.size() - 1); - if (force_sliders_full_range) - m_slider->SetHigherValue(layers_z.size() - 1); - m_slider->SetSliderValues(values); - const double z_low = m_slider->GetLowerValueD(); - const double z_high = m_slider->GetHigherValueD(); + assert(m_slider->GetMinValue() == 0); + m_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); + + int idx_low = 0; + int idx_high = m_slider->GetMaxValue(); + if (! layers_z.empty()) { + if (! snap_to_min) { + int idx_new = find_close_layer_idx(layers_z, z_low, 1e-6); + if (idx_new != -1) + idx_low = idx_new; + } + if (! snap_to_max) { + int idx_new = find_close_layer_idx(layers_z, z_high, 1e-6); + if (idx_new != -1) + idx_high = idx_new; + } + } + m_slider->SetSelectionSpan(idx_low, idx_high); const auto& config = wxGetApp().preset_bundle->project_config; const std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; - m_slider->SetTicksValues(ticks_from_config); - set_double_slider_thumbs(layers_z, z_low, z_high); - bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); if (color_print_enable) { const auto& config = wxGetApp().preset_bundle->full_config(); @@ -638,8 +681,7 @@ void Preview::fill_slider_values(std::vector> &values, // All ticks that would end up outside the slider range should be erased. // TODO: this should be placed into more appropriate part of code, // this function is e.g. not called when the last object is deleted - auto& config = wxGetApp().preset_bundle->project_config; - std::vector &ticks_from_config = (config.option("colorprint_heights"))->values; + std::vector &ticks_from_config = (wxGetApp().preset_bundle->project_config.option("colorprint_heights"))->values; unsigned int old_size = ticks_from_config.size(); ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), [values](double val) { return values.back().second < val; }), @@ -648,32 +690,6 @@ void Preview::fill_slider_values(std::vector> &values, m_schedule_background_process(); } -void Preview::set_double_slider_thumbs(const std::vector &layers_z, - const double z_low, - const double z_high) -{ - // Force slider full range only when slider is created. - // Support selected diapason on the all next steps - if (z_high == 0.0) { - m_slider->SetLowerValue(0); - m_slider->SetHigherValue(layers_z.size() - 1); - return; - } - - for (int i = layers_z.size() - 1; i >= 0; i--) -// if (z_low >= layers_z[i]) { - if (fabs(z_low - layers_z[i]) <= 1e-6) { - m_slider->SetLowerValue(i); - break; - } - for (int i = layers_z.size() - 1; i >= 0; i--) -// if (z_high >= layers_z[i]) { - if (fabs(z_high-layers_z[i]) <= 1e-6) { - m_slider->SetHigherValue(i); - break; - } -} - void Preview::reset_double_slider() { m_slider->SetHigherValue(0); @@ -692,7 +708,7 @@ void Preview::update_double_slider_from_canvas(wxKeyEvent& event) if (key == 'U' || key == 'D') { const int new_pos = key == 'U' ? m_slider->GetHigherValue() + 1 : m_slider->GetHigherValue() - 1; m_slider->SetHigherValue(new_pos); - if (event.ShiftDown()) m_slider->SetLowerValue(m_slider->GetHigherValue()); + if (event.ShiftDown() || m_slider->is_one_layer()) m_slider->SetLowerValue(m_slider->GetHigherValue()); } else if (key == 'S') m_slider->ChangeOneLayerLock(); @@ -778,12 +794,8 @@ void Preview::load_print_as_fff() if (IsShown()) { - // used to set the sliders to the extremes of the current zs range - m_force_sliders_full_range = false; - if (gcode_preview_data_valid) { - m_force_sliders_full_range = (m_canvas->get_volumes_count() == 0); m_canvas->load_gcode_preview(*m_gcode_preview_data, colors); show_hide_ui_elements("full"); @@ -849,7 +861,6 @@ void Preview::load_print_as_sla() { std::vector layer_zs; std::copy(zs.begin(), zs.end(), std::back_inserter(layer_zs)); - m_force_sliders_full_range = true; update_sliders(layer_zs); } diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 534191633..bd71adcb5 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -117,7 +117,6 @@ class Preview : public wxPanel bool m_loaded; bool m_enabled; - bool m_force_sliders_full_range; PrusaDoubleSlider* m_slider {nullptr}; @@ -177,12 +176,9 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); - void update_double_slider(const std::vector& layers_z, bool force_sliders_full_range); + void update_double_slider(const std::vector& layers_z, bool force_sliders_full_range = false); void fill_slider_values(std::vector> &values, const std::vector &layers_z); - void set_double_slider_thumbs( const std::vector &layers_z, - const double z_low, - const double z_high); void reset_double_slider(); // update DoubleSlider after keyDown in canvas void update_double_slider_from_canvas(wxKeyEvent& event); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 2daba5df4..787c2a2a2 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -1506,6 +1506,21 @@ void PrusaDoubleSlider::SetHigherValue(const int higher_val) ProcessWindowEvent(e); } +void PrusaDoubleSlider::SetSelectionSpan(const int lower_val, const int higher_val) +{ + m_lower_value = std::max(lower_val, m_min_value); + m_higher_value = std::max(std::min(higher_val, m_max_value), m_lower_value); + if (m_lower_value < m_higher_value) + m_is_one_layer = false; + + Refresh(); + Update(); + + wxCommandEvent e(wxEVT_SCROLL_CHANGED); + e.SetEventObject(this); + ProcessWindowEvent(e); +} + void PrusaDoubleSlider::SetMaxValue(const int max_value) { m_max_value = max_value; diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index e8fba1ea2..b951c5635 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -697,18 +697,20 @@ public: const wxString& name = wxEmptyString); ~PrusaDoubleSlider() {} - int GetLowerValue() const { - return m_lower_value; - } - int GetHigherValue() const { - return m_higher_value; - } + int GetMinValue() const { return m_min_value; } + int GetMaxValue() const { return m_max_value; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value].second; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value].second; } + int GetLowerValue() const { return m_lower_value; } + int GetHigherValue() const { return m_higher_value; } int GetActiveValue() const; double GetLowerValueD() { return get_double_value(ssLower); } double GetHigherValueD() { return get_double_value(ssHigher); } wxSize DoGetBestSize() const override; void SetLowerValue(const int lower_val); void SetHigherValue(const int higher_val); + // Set low and high slider position. If the span is non-empty, disable the "one layer" mode. + void SetSelectionSpan(const int lower_val, const int higher_val); void SetMaxValue(const int max_value); void SetKoefForLabels(const double koef) { m_label_koef = koef; @@ -726,6 +728,12 @@ public: EnableTickManipulation(false); } + bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } + bool is_one_layer() const { return m_is_one_layer; } + bool is_lower_at_min() const { return m_lower_value == m_min_value; } + bool is_higher_at_max() const { return m_higher_value == m_max_value; } + bool is_full_span() const { return this->is_lower_at_min() && this->is_higher_at_max(); } + void OnPaint(wxPaintEvent& ) { render();} void OnLeftDown(wxMouseEvent& event); void OnMotion(wxMouseEvent& event); @@ -762,7 +770,6 @@ protected: bool is_point_in_rect(const wxPoint& pt, const wxRect& rect); int is_point_near_tick(const wxPoint& pt); - bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; } double get_scroll_step(); wxString get_label(const SelectedSlider& selection) const; From 54fae970329609c1231e475095aa80d3d28e54ef Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 11:14:53 +0100 Subject: [PATCH 054/132] Visual hints in the 3D scene when sidebar matrix fields have focus -> Completed VBOs case --- src/libslic3r/Technologies.hpp | 2 +- src/slic3r/GUI/3DScene.cpp | 160 +++++++++++++++++++++++++++++++-- src/slic3r/GUI/3DScene.hpp | 18 +++- src/slic3r/GUI/GLCanvas3D.cpp | 76 +++++++++------- src/slic3r/GUI/GLCanvas3D.hpp | 9 +- 5 files changed, 214 insertions(+), 51 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 797272223..5d49c3675 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -43,7 +43,7 @@ // Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active #define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0) // Show visual hints in the 3D scene when sidebar matrix fields have focus -#define ENABLE_SIDEBAR_VISUAL_HINTS (0 && ENABLE_1_42_0) +#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 012203450..b431cf8bc 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1872,6 +1872,7 @@ GUI::GLCanvas3DManager _3DScene::s_canvas_mgr; GLModel::GLModel() : m_useVBOs(false) { + m_volume.shader_outside_printer_detection_enabled = false; } GLModel::~GLModel() @@ -1879,11 +1880,36 @@ GLModel::~GLModel() m_volume.release_geometry(); } -void GLModel::set_color(float* color, unsigned int size) +void GLModel::set_color(const float* color, unsigned int size) { m_volume.set_render_color(color, size); } +const Vec3d& GLModel::get_offset() const +{ + return m_volume.get_volume_offset(); +} + +void GLModel::set_offset(const Vec3d& offset) +{ + m_volume.set_volume_offset(offset); +} + +const Vec3d& GLModel::get_rotation() const +{ + return m_volume.get_volume_rotation(); +} + +void GLModel::set_rotation(const Vec3d& rotation) +{ + m_volume.set_volume_rotation(rotation); +} + +const Vec3d& GLModel::get_scale() const +{ + return m_volume.get_volume_scaling_factor(); +} + void GLModel::set_scale(const Vec3d& scale) { m_volume.set_volume_scaling_factor(scale); @@ -1910,8 +1936,9 @@ void GLModel::render_VBOs() const GLint current_program_id; ::glGetIntegerv(GL_CURRENT_PROGRAM, ¤t_program_id); GLint color_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "uniform_color") : -1; + GLint print_box_detection_id = (current_program_id > 0) ? glGetUniformLocation(current_program_id, "print_box.volume_detection") : -1; - m_volume.render_VBOs(color_id, -1, -1); + m_volume.render_VBOs(color_id, print_box_detection_id, -1); ::glBindBuffer(GL_ARRAY_BUFFER, 0); ::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -1922,17 +1949,12 @@ void GLModel::render_VBOs() const ::glDisable(GL_BLEND); } -GLArrow::GLArrow() - : GLModel() -{ -} - bool GLArrow::on_init(bool useVBOs) { Pointf3s vertices; std::vector triangles; - // top face + // bottom face vertices.emplace_back(0.5, 0.0, -0.1); vertices.emplace_back(0.5, 2.0, -0.1); vertices.emplace_back(1.0, 2.0, -0.1); @@ -1941,7 +1963,7 @@ bool GLArrow::on_init(bool useVBOs) vertices.emplace_back(-0.5, 2.0, -0.1); vertices.emplace_back(-0.5, 0.0, -0.1); - // bottom face + // top face vertices.emplace_back(0.5, 0.0, 0.1); vertices.emplace_back(0.5, 2.0, 0.1); vertices.emplace_back(1.0, 2.0, 0.1); @@ -1990,6 +2012,126 @@ bool GLArrow::on_init(bool useVBOs) m_volume.finalize_geometry(m_useVBOs); return true; } + +GLCurvedArrow::GLCurvedArrow(unsigned int resolution) + : GLModel() + , m_resolution(resolution) +{ + if (m_resolution == 0) + m_resolution = 1; +} + +bool GLCurvedArrow::on_init(bool useVBOs) +{ + Pointf3s vertices; + std::vector triangles; + + double ext_radius = 2.5; + double int_radius = 1.5; + double step = 0.5 * (double)PI / (double)m_resolution; + + unsigned int vertices_per_level = 4 + 2 * m_resolution; + + // bottom face + vertices.emplace_back(0.0, 1.5, -0.1); + vertices.emplace_back(0.0, 1.0, -0.1); + vertices.emplace_back(-1.0, 2.0, -0.1); + vertices.emplace_back(0.0, 3.0, -0.1); + vertices.emplace_back(0.0, 2.5, -0.1); + + for (unsigned int i = 1; i <= m_resolution; ++i) + { + double angle = (double)i * step; + double x = ext_radius * ::sin(angle); + double y = ext_radius * ::cos(angle); + + vertices.emplace_back(x, y, -0.1); + } + + for (unsigned int i = 0; i < m_resolution; ++i) + { + double angle = (double)i * step; + double x = int_radius * ::cos(angle); + double y = int_radius * ::sin(angle); + + vertices.emplace_back(x, y, -0.1); + } + + // top face + vertices.emplace_back(0.0, 1.5, 0.1); + vertices.emplace_back(0.0, 1.0, 0.1); + vertices.emplace_back(-1.0, 2.0, 0.1); + vertices.emplace_back(0.0, 3.0, 0.1); + vertices.emplace_back(0.0, 2.5, 0.1); + + for (unsigned int i = 1; i <= m_resolution; ++i) + { + double angle = (double)i * step; + double x = ext_radius * ::sin(angle); + double y = ext_radius * ::cos(angle); + + vertices.emplace_back(x, y, 0.1); + } + + for (unsigned int i = 0; i < m_resolution; ++i) + { + double angle = (double)i * step; + double x = int_radius * ::cos(angle); + double y = int_radius * ::sin(angle); + + vertices.emplace_back(x, y, 0.1); + } + + // bottom face + triangles.emplace_back(0, 1, 2); + triangles.emplace_back(0, 2, 4); + triangles.emplace_back(4, 2, 3); + + int first_id = 4; + int last_id = (int)vertices_per_level; + triangles.emplace_back(last_id, 0, first_id); + triangles.emplace_back(last_id, first_id, first_id + 1); + for (unsigned int i = 1; i < m_resolution; ++i) + { + triangles.emplace_back(last_id - i, last_id - i + 1, first_id + i); + triangles.emplace_back(last_id - i, first_id + i, first_id + i + 1); + } + + // top face + last_id += 1; + triangles.emplace_back(last_id + 0, last_id + 2, last_id + 1); + triangles.emplace_back(last_id + 0, last_id + 4, last_id + 2); + triangles.emplace_back(last_id + 4, last_id + 3, last_id + 2); + + first_id = last_id + 4; + last_id = last_id + 4 + 2 * (int)m_resolution; + triangles.emplace_back(last_id, first_id, (int)vertices_per_level + 1); + triangles.emplace_back(last_id, first_id + 1, first_id); + for (unsigned int i = 1; i < m_resolution; ++i) + { + triangles.emplace_back(last_id - i, first_id + i, last_id - i + 1); + triangles.emplace_back(last_id - i, first_id + i + 1, first_id + i); + } + + // side face + for (unsigned int i = 0; i < 4 + 2 * (int)m_resolution; ++i) + { + triangles.emplace_back(i, vertices_per_level + 2 + i, i + 1); + triangles.emplace_back(i, vertices_per_level + 1 + i, vertices_per_level + 2 + i); + } + triangles.emplace_back(vertices_per_level, vertices_per_level + 1, 0); + triangles.emplace_back(vertices_per_level, 2 * vertices_per_level + 1, vertices_per_level + 1); + + m_useVBOs = useVBOs; + + if (m_useVBOs) + m_volume.indexed_vertex_array.load_mesh_full_shading(TriangleMesh(vertices, triangles)); + else + m_volume.indexed_vertex_array.load_mesh_flat_shading(TriangleMesh(vertices, triangles)); + + m_volume.finalize_geometry(m_useVBOs); + return true; +} #endif // ENABLE_SIDEBAR_VISUAL_HINTS std::string _3DScene::get_gl_info(bool format_as_html, bool extensions) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 22b80d627..09bb54e2f 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -595,7 +595,13 @@ public: bool init(bool useVBOs) { return on_init(useVBOs); } - void set_color(float* color, unsigned int size); + void set_color(const float* color, unsigned int size); + + const Vec3d& get_offset() const; + void set_offset(const Vec3d& offset); + const Vec3d& get_rotation() const; + void set_rotation(const Vec3d& rotation); + const Vec3d& get_scale() const; void set_scale(const Vec3d& scale); void render() const; @@ -609,8 +615,16 @@ private: class GLArrow : public GLModel { +protected: + virtual bool on_init(bool useVBOs); +}; + +class GLCurvedArrow : public GLModel +{ + unsigned int m_resolution; + public: - GLArrow(); + explicit GLCurvedArrow(unsigned int resolution); protected: virtual bool on_init(bool useVBOs); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3c4debba0..77d0f0776 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -73,6 +73,11 @@ static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f }; static const float ERROR_BG_DARK_COLOR[3] = { 0.478f, 0.192f, 0.039f }; static const float ERROR_BG_LIGHT_COLOR[3] = { 0.753f, 0.192f, 0.039f }; +#if ENABLE_SIDEBAR_VISUAL_HINTS +static const float UNIFORM_SCALE_COLOR[3] = { 1.0f, 0.38f, 0.0f }; +static const float AXES_COLOR[3][3] = { { 1.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }; +#endif // ENABLE_SIDEBAR_VISUAL_HINTS + namespace Slic3r { namespace GUI { @@ -1154,12 +1159,6 @@ GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec } #endif // ENABLE_MODELVOLUME_TRANSFORM -#if ENABLE_SIDEBAR_VISUAL_HINTS -const float GLCanvas3D::Selection::RED[3] = { 1.0f, 0.0f, 0.0f }; -const float GLCanvas3D::Selection::GREEN[3] = { 0.0f, 1.0f, 0.0f }; -const float GLCanvas3D::Selection::BLUE[3] = { 0.0f, 0.0f, 1.0f }; -#endif // ENABLE_SIDEBAR_VISUAL_HINTS - GLCanvas3D::Selection::Selection() : m_volumes(nullptr) , m_model(nullptr) @@ -1167,6 +1166,7 @@ GLCanvas3D::Selection::Selection() , m_type(Empty) , m_valid(false) , m_bounding_box_dirty(true) + , m_curved_arrow(16) { #if ENABLE_RENDER_SELECTION_CENTER m_quadric = ::gluNewQuadric(); @@ -1192,13 +1192,16 @@ void GLCanvas3D::Selection::set_volumes(GLVolumePtrs* volumes) #if ENABLE_SIDEBAR_VISUAL_HINTS bool GLCanvas3D::Selection::init(bool useVBOs) { - if (m_arrow.init(useVBOs)) - { - m_arrow.set_scale(5.0 * Vec3d::Ones()); - return true; - } + if (!m_arrow.init(useVBOs)) + return false; - return false; + m_arrow.set_scale(5.0 * Vec3d::Ones()); + + if (!m_curved_arrow.init(useVBOs)) + return false; + + m_curved_arrow.set_scale(5.0 * Vec3d::Ones()); + return true; } #endif // ENABLE_SIDEBAR_VISUAL_HINTS @@ -2094,14 +2097,22 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel else if (is_single_volume() || is_single_modifier()) { const GLVolume* volume = (*m_volumes)[*m_list.begin()]; - Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true) * volume->get_volume_transformation().get_matrix(true, false, true, true); + Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true); const Vec3d& offset = get_bounding_box().center(); ::glTranslated(offset(0), offset(1), offset(2)); ::glMultMatrixd(orient_matrix.data()); } else + { ::glTranslated(center(0), center(1), center(2)); + if (requires_local_axes()) + { + const GLVolume* volume = (*m_volumes)[*m_list.begin()]; + Transform3d orient_matrix = volume->get_instance_transformation().get_matrix(true, false, true, true); + ::glMultMatrixd(orient_matrix.data()); + } + } if (boost::starts_with(sidebar_field, "position")) _render_sidebar_position_hints(sidebar_field); @@ -2544,6 +2555,18 @@ void GLCanvas3D::Selection::_render_sidebar_position_hints(const std::string& si void GLCanvas3D::Selection::_render_sidebar_rotation_hints(const std::string& sidebar_field) const { + if (boost::ends_with(sidebar_field, "x")) + { + ::glRotated(90.0, 0.0, 1.0, 0.0); + _render_sidebar_rotation_hint(X); + } + else if (boost::ends_with(sidebar_field, "y")) + { + ::glRotated(-90.0, 1.0, 0.0, 0.0); + _render_sidebar_rotation_hint(Y); + } + else if (boost::ends_with(sidebar_field, "z")) + _render_sidebar_rotation_hint(Z); } void GLCanvas3D::Selection::_render_sidebar_scale_hints(const std::string& sidebar_field) const @@ -2579,33 +2602,22 @@ void GLCanvas3D::Selection::_render_sidebar_size_hints(const std::string& sideba void GLCanvas3D::Selection::_render_sidebar_position_hint(Axis axis) const { - float color[3]; - switch (axis) - { - case X: { ::memcpy((void*)color, (const void*)RED, 3 * sizeof(float)); break; } - case Y: { ::memcpy((void*)color, (const void*)GREEN, 3 * sizeof(float)); break; } - case Z: { ::memcpy((void*)color, (const void*)BLUE, 3 * sizeof(float)); break; } - } - - m_arrow.set_color(color, 3); + m_arrow.set_color(AXES_COLOR[axis], 3); m_arrow.render(); } -void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis, double length) const +void GLCanvas3D::Selection::_render_sidebar_rotation_hint(Axis axis) const { + m_curved_arrow.set_color(AXES_COLOR[axis], 3); + m_curved_arrow.render(); + + ::glRotated(180.0, 0.0, 0.0, 1.0); + m_curved_arrow.render(); } void GLCanvas3D::Selection::_render_sidebar_scale_hint(Axis axis) const { - float color[3]; - switch (axis) - { - case X: { ::memcpy((void*)color, (const void*)RED, 3 * sizeof(float)); break; } - case Y: { ::memcpy((void*)color, (const void*)GREEN, 3 * sizeof(float)); break; } - case Z: { ::memcpy((void*)color, (const void*)BLUE, 3 * sizeof(float)); break; } - } - - m_arrow.set_color(color, 3); + m_arrow.set_color((requires_uniform_scale() ? UNIFORM_SCALE_COLOR : AXES_COLOR[axis]), 3); ::glTranslated(0.0, 5.0, 0.0); m_arrow.render(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 413d625f1..8e9d44a4b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -369,12 +369,6 @@ class GLCanvas3D public: class Selection { -#if ENABLE_SIDEBAR_VISUAL_HINTS - static const float RED[3]; - static const float GREEN[3]; - static const float BLUE[3]; -#endif // ENABLE_SIDEBAR_VISUAL_HINTS - public: typedef std::set IndicesList; @@ -504,6 +498,7 @@ public: #endif // ENABLE_RENDER_SELECTION_CENTER #if ENABLE_SIDEBAR_VISUAL_HINTS mutable GLArrow m_arrow; + mutable GLCurvedArrow m_curved_arrow; #endif // ENABLE_SIDEBAR_VISUAL_HINTS public: @@ -619,7 +614,7 @@ public: void _render_sidebar_scale_hints(const std::string& sidebar_field) const; void _render_sidebar_size_hints(const std::string& sidebar_field) const; void _render_sidebar_position_hint(Axis axis) const; - void _render_sidebar_rotation_hint(Axis axis, double length) const; + void _render_sidebar_rotation_hint(Axis axis) const; void _render_sidebar_scale_hint(Axis axis) const; void _render_sidebar_size_hint(Axis axis, double length) const; #endif // ENABLE_SIDEBAR_VISUAL_HINTS From 11da45e32fb623a8a2717964f091f998c13b692e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 11:42:26 +0100 Subject: [PATCH 055/132] Visual hints in the 3D scene when sidebar matrix fields have focus -> legacy render case --- src/slic3r/GUI/3DScene.cpp | 22 ++++++++++++++++++++-- src/slic3r/GUI/3DScene.hpp | 1 + 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index b431cf8bc..961d4822a 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1920,8 +1920,7 @@ void GLModel::render() const if (m_useVBOs) render_VBOs(); else - { - } + render_legacy(); } void GLModel::render_VBOs() const @@ -1949,6 +1948,25 @@ void GLModel::render_VBOs() const ::glDisable(GL_BLEND); } +void GLModel::render_legacy() const +{ + ::glEnable(GL_LIGHTING); + ::glEnable(GL_BLEND); + ::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + ::glCullFace(GL_BACK); + ::glEnableClientState(GL_VERTEX_ARRAY); + ::glEnableClientState(GL_NORMAL_ARRAY); + + m_volume.render_legacy(); + + ::glDisableClientState(GL_VERTEX_ARRAY); + ::glDisableClientState(GL_NORMAL_ARRAY); + + ::glDisable(GL_BLEND); + ::glDisable(GL_LIGHTING); +} + bool GLArrow::on_init(bool useVBOs) { Pointf3s vertices; diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 09bb54e2f..76cacabbd 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -611,6 +611,7 @@ protected: private: void render_VBOs() const; + void render_legacy() const; }; class GLArrow : public GLModel From a5b846f7fc3569854fd977b55dc4aeca5929f116 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 20 Dec 2018 12:08:06 +0100 Subject: [PATCH 056/132] Improved edge normal detection. Also removed some warnings. --- .../include/libnest2d/placers/nfpplacer.hpp | 4 +- src/libslic3r/ModelArrange.cpp | 14 ++----- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 39 +++++++++++++------ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 14ed3d22c..28659c512 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -545,8 +545,8 @@ public: _NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default; #ifndef BP2D_COMPILER_MSVC12 // MSVC2013 does not support default move ctors - _NofitPolyPlacer(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default; - _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default; + _NofitPolyPlacer(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default; + _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) /*BP2D_NOEXCEPT*/ = default; #endif static inline double overfit(const Box& bb, const RawShape& bin) { diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index d182f1501..562527290 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -135,11 +135,6 @@ objfunc(const PointImpl& bincenter, const ItemGroup& remaining ) { - using Coord = TCoord; - - static const double ROUNDNESS_RATIO = 0.5; - static const double DENSITY_RATIO = 1.0 - ROUNDNESS_RATIO; - // We will treat big items (compared to the print bed) differently auto isBig = [bin_area](double a) { return a/bin_area > BIG_ITEM_TRESHOLD ; @@ -629,11 +624,12 @@ BedShapeHint bedShape(const Polyline &bed) { avg_dist /= vertex_distances.size(); Circle ret(center, avg_dist); - for (auto el: vertex_distances) + for(auto el : vertex_distances) { - if (abs(el - avg_dist) > 10 * SCALED_EPSILON) + if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) { ret = Circle(); - break; + break; + } } return ret; @@ -665,8 +661,6 @@ bool arrange(Model &model, std::function progressind, std::function stopcondition) { - using ArrangeResult = _IndexedPackGroup; - bool ret = true; // Get the 2D projected shapes with their 3D model instance pointers diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 5d40bb514..49290b3b8 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -107,7 +107,8 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // structure EigenMesh3D mesh; Eigen::VectorXi SVI, SVJ; - igl::remove_duplicate_vertices(emesh.V, emesh.F, 1e-6, + static const double dEPS = 1e-6; + igl::remove_duplicate_vertices(emesh.V, emesh.F, dEPS, mesh.V, SVI, SVJ, mesh.F); igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); @@ -155,6 +156,7 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, ia = trindex(0); ib = trindex(2); } + // vector for the neigboring triangles including the detected one. std::vector neigh; if(ic >= 0) { // The point is right on a vertex of the triangle for(int n = 0; n < mesh.F.rows(); ++n) { @@ -175,17 +177,32 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, } } - if(!neigh.empty()) { // there were neighbors to count with + // Calculate the normals for the neighboring triangles + std::vector neighnorms; neighnorms.reserve(neigh.size()); + for(const Vec3i& tri : neigh) { + const Vec3d& pt1 = mesh.V.row(tri(0)); + const Vec3d& pt2 = mesh.V.row(tri(1)); + const Vec3d& pt3 = mesh.V.row(tri(2)); + Eigen::Vector3d U = pt2 - pt1; + Eigen::Vector3d V = pt3 - pt1; + neighnorms.emplace_back(U.cross(V).normalized()); + } + + // Throw out duplicates. They would case trouble with summing. + auto lend = std::unique(neighnorms.begin(), neighnorms.end(), + [](const Vec3d& n1, const Vec3d& n2) { + // Compare normals for equivalence. This is controvers stuff. + // We will go for the third significant digit precision. + auto deq = [](double a, double b) { return std::abs(a-b) < 1e-3; }; + return deq(n1(X), n2(X)) && deq(n1(Y), n2(Y)) && deq(n1(Z), n2(Z)); + }); + + if(!neighnorms.empty()) { // there were neighbors to count with + // sum up the normals and than normalize the result again. + // This unification seems to be enough. Vec3d sumnorm(0, 0, 0); - for(const Vec3i& tri : neigh) { - const Vec3d& pt1 = mesh.V.row(tri(0)); - const Vec3d& pt2 = mesh.V.row(tri(1)); - const Vec3d& pt3 = mesh.V.row(tri(2)); - Eigen::Vector3d U = pt2 - pt1; - Eigen::Vector3d V = pt3 - pt1; - sumnorm += U.cross(V).normalized(); - } - sumnorm /= neigh.size(); + sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); + sumnorm.normalize(); ret.row(i) = sumnorm; } else { // point lies safely within its triangle From 780e3c700e9f5e1d9beb7b16b0a62067bf4e7d31 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 12:51:42 +0100 Subject: [PATCH 057/132] Attempt to reduce ugly artifacts when switching to preview for the 1st time --- src/slic3r/GUI/Plater.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index cba53cf17..6a8bcc79b 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1162,9 +1162,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0); q->SetSizer(hsizer); -#if ENABLE_REMOVE_TABS_FROM_PLATER - set_current_panel(view3D); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER +//#if ENABLE_REMOVE_TABS_FROM_PLATER +// set_current_panel(view3D); +//#endif // ENABLE_REMOVE_TABS_FROM_PLATER init_object_menu(); @@ -1251,6 +1251,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) update_ui_from_settings(); q->Layout(); + +#if ENABLE_REMOVE_TABS_FROM_PLATER + set_current_panel(view3D); +#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::update(bool force_full_scene_refresh) From 3fbc4afc9593108bc1486dfb76ce7887ce141e65 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 20 Dec 2018 12:52:16 +0100 Subject: [PATCH 058/132] Upgraded "Keyboard shortcuts" dialog --- src/slic3r/GUI/KBShortcutsDialog.cpp | 100 +++++++++++++++------------ src/slic3r/GUI/SysInfoDialog.cpp | 2 +- 2 files changed, 58 insertions(+), 44 deletions(-) diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index b3edbc9a8..12929b2cc 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -3,6 +3,7 @@ #include "libslic3r/Utils.hpp" #include "GUI.hpp" #include +#include "GUI_App.hpp" namespace Slic3r { namespace GUI { @@ -19,41 +20,46 @@ KBShortcutsDialog::KBShortcutsDialog() // fonts wxFont head_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Bold(); - head_font.SetPointSize(19); - - wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); - font.SetPointSize(10); - wxFont bold_font = font.Bold(); #ifdef __WXOSX__ - font.SetPointSize(12); - bold_font.SetPointSize(14); -#endif /*__WXOSX__*/ + head_font.SetPointSize(14); +#else + head_font.SetPointSize(12); +#endif // __WXOSX__ + + const wxFont& font = wxGetApp().small_font(); + const wxFont& bold_font = wxGetApp().bold_font(); fill_shortcuts(); - auto panel = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition, wxSize(500, 600)); - panel->SetScrollbars(0, 20, 1, 2); - auto sizer = new wxBoxSizer(wxVERTICAL); - panel->SetSizer(sizer); + auto panel = new wxPanel(this); + auto main_grid_sizer = new wxFlexGridSizer(2, 10, 10); + panel->SetSizer(main_grid_sizer); main_sizer->Add(panel, 1, wxEXPAND | wxALL, 0); + wxBoxSizer* l_sizer = new wxBoxSizer(wxVERTICAL); + main_grid_sizer->Add(l_sizer, 0); + + wxBoxSizer* r_sizer = new wxBoxSizer(wxVERTICAL); + main_grid_sizer->Add(r_sizer, 0); + for (auto& sc : m_full_shortcuts) { + auto sizer = sc.first == _(L("Main Shortcuts")) ? l_sizer : r_sizer; wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL); - sizer->Add(hsizer, 0, wxEXPAND | wxTOP, 25); + sizer->Add(hsizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); // logo auto *logo = new wxStaticBitmap(panel, wxID_ANY, logo_bmp); hsizer->Add(logo, 0, wxEXPAND | wxLEFT | wxRIGHT, 15); // head - wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(400,-1)); + wxStaticText* head = new wxStaticText(panel, wxID_ANY, sc.first, wxDefaultPosition, wxSize(200,-1)); head->SetFont(head_font); hsizer->Add(head, 0, wxALIGN_CENTER_VERTICAL); // Shortcuts list - auto grid_sizer = new wxFlexGridSizer(2, 10, 25); - sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT | wxTOP, 10); + auto grid_sizer = new wxFlexGridSizer(2, 5, 15); + sizer->Add(grid_sizer, 0, wxEXPAND | wxLEFT| wxRIGHT, 15); for (auto pair : sc.second) { @@ -69,9 +75,9 @@ KBShortcutsDialog::KBShortcutsDialog() wxStdDialogButtonSizer* buttons = this->CreateStdDialogButtonSizer(wxOK); - this->SetEscapeId(wxID_CLOSE); + this->SetEscapeId(wxID_OK); this->Bind(wxEVT_BUTTON, &KBShortcutsDialog::onCloseDialog, this, wxID_OK); - main_sizer->Add(buttons, 0, wxEXPAND | wxALL, 15); + main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 15); this->Bind(wxEVT_LEFT_DOWN, &KBShortcutsDialog::onCloseDialog, this); @@ -81,32 +87,40 @@ KBShortcutsDialog::KBShortcutsDialog() void KBShortcutsDialog::fill_shortcuts() { +#ifdef __WXOSX__ + const std::string ctrl = "Cmd+"; // #ys_FIXME_cmd_smb // Change it for the accorded symbol + const std::string alt = "Alt+"; // #ys_FIXME_cmd_smb // Change it for the accorded symbol +#else + const std::string ctrl = "Ctrl+"; + const std::string alt = "Alt+"; +#endif // __WXOSX__ + Shortcuts main_shortcuts; main_shortcuts.reserve(25); - main_shortcuts.push_back(Shortcut("Ctrl+O", L("Open project STL/OBJ/AMF/3MF with config, delete bed"))); - main_shortcuts.push_back(Shortcut("Ctrl+I", L("Import STL//OBJ/AMF/3MF without config, keep bed"))); - main_shortcuts.push_back(Shortcut("Ctrl+L", L("Load Config from .ini/amf/3mf/gcode"))); - main_shortcuts.push_back(Shortcut("Ctrl+Alt+L", L("Load Config from .ini/amf/3mf/gcode and merge"))); - main_shortcuts.push_back(Shortcut("Ctrl+G", L("Export Gcode"))); - main_shortcuts.push_back(Shortcut("Ctrl+S", L("Save project (3MF)"))); - main_shortcuts.push_back(Shortcut("Ctrl+R", L("(Re)slice"))); - main_shortcuts.push_back(Shortcut("Ctrl+U", L("Quick slice"))); - main_shortcuts.push_back(Shortcut("Ctrl+Alt+U", L("Quick slice and Save as"))); - main_shortcuts.push_back(Shortcut("Ctrl+Shift+U", L("Repeat last quick slice"))); - main_shortcuts.push_back(Shortcut("Ctrl+1", L("Select Plater Tab"))); - main_shortcuts.push_back(Shortcut("Ctrl+2", L("Select Print Settings Tab"))); - main_shortcuts.push_back(Shortcut("Ctrl+3", L("Select Filament Setting Tab"))); - main_shortcuts.push_back(Shortcut("Ctrl+4", L("Select Printer Setting Tab"))); - main_shortcuts.push_back(Shortcut("Ctrl+5", L("Switch to 3D"))); - main_shortcuts.push_back(Shortcut("Ctrl+6", L("Switch to Preview"))); - main_shortcuts.push_back(Shortcut("Ctrl+P", L("Preferences"))); - main_shortcuts.push_back(Shortcut("0-6", L("Camera view "))); - main_shortcuts.push_back(Shortcut("+", L("Add Instance to selected object "))); - main_shortcuts.push_back(Shortcut("-", L("Remove Instance from selected object"))); - main_shortcuts.push_back(Shortcut("?", L("Show keyboard shortcuts list"))); - main_shortcuts.push_back(Shortcut("PgUp/PgDn", L("Switch between 3D and Preview"))); - main_shortcuts.push_back(Shortcut("Shift+LeftMouse",L("Select multiple object/Move multiple object"))); + main_shortcuts.push_back(Shortcut(ctrl+"O" ,L("Open project STL/OBJ/AMF/3MF with config, delete bed"))); + main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL//OBJ/AMF/3MF without config, keep bed"))); + main_shortcuts.push_back(Shortcut(ctrl+"L" ,L("Load Config from .ini/amf/3mf/gcode"))); + main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export Gcode"))); + main_shortcuts.push_back(Shortcut(ctrl+"S" ,L("Save project (3MF)"))); + main_shortcuts.push_back(Shortcut(ctrl+alt+"L" ,L("Load Config from .ini/amf/3mf/gcode and merge"))); + main_shortcuts.push_back(Shortcut(ctrl+"R" ,L("(Re)slice"))); + main_shortcuts.push_back(Shortcut(ctrl+"U" ,L("Quick slice"))); + main_shortcuts.push_back(Shortcut(ctrl+"Shift+U" ,L("Repeat last quick slice"))); + main_shortcuts.push_back(Shortcut(ctrl+"1" ,L("Select Plater Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+alt+"U" ,L("Quick slice and Save as"))); + main_shortcuts.push_back(Shortcut(ctrl+"2" ,L("Select Print Settings Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+"3" ,L("Select Filament Setting Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+"4" ,L("Select Printer Setting Tab"))); + main_shortcuts.push_back(Shortcut(ctrl+"5" ,L("Switch to 3D"))); + main_shortcuts.push_back(Shortcut(ctrl+"6" ,L("Switch to Preview"))); + main_shortcuts.push_back(Shortcut(ctrl+"P" ,L("Preferences"))); + main_shortcuts.push_back(Shortcut("0-6" ,L("Camera view "))); + main_shortcuts.push_back(Shortcut("+" ,L("Add Instance to selected object "))); + main_shortcuts.push_back(Shortcut("-" ,L("Remove Instance from selected object"))); + main_shortcuts.push_back(Shortcut("?" ,L("Show keyboard shortcuts list"))); + main_shortcuts.push_back(Shortcut("PgUp/PgDn" ,L("Switch between 3D and Preview"))); + main_shortcuts.push_back(Shortcut("Shift+LeftMouse" ,L("Select multiple object/Move multiple object"))); m_full_shortcuts.emplace(_(L("Main Shortcuts")), main_shortcuts); @@ -115,9 +129,9 @@ void KBShortcutsDialog::fill_shortcuts() plater_shortcuts.reserve(20); plater_shortcuts.push_back(Shortcut("A", L("Arrange"))); - plater_shortcuts.push_back(Shortcut("Ctrl+A", L("Select All objects"))); + plater_shortcuts.push_back(Shortcut(ctrl+"A", L("Select All objects"))); plater_shortcuts.push_back(Shortcut("Del", L("Delete selected"))); - plater_shortcuts.push_back(Shortcut("Ctrl+Del", L("Delete all"))); + plater_shortcuts.push_back(Shortcut(ctrl+"Del", L("Delete all"))); plater_shortcuts.push_back(Shortcut("M", L("Gizmo move"))); plater_shortcuts.push_back(Shortcut("S", L("Gizmo scale"))); plater_shortcuts.push_back(Shortcut("R", L("Gizmo rotate"))); diff --git a/src/slic3r/GUI/SysInfoDialog.cpp b/src/slic3r/GUI/SysInfoDialog.cpp index f7f1cfedb..110bfaf44 100644 --- a/src/slic3r/GUI/SysInfoDialog.cpp +++ b/src/slic3r/GUI/SysInfoDialog.cpp @@ -116,7 +116,7 @@ SysInfoDialog::SysInfoDialog() buttons->Insert(0, btn_copy_to_clipboard, 0, wxLEFT, 5); btn_copy_to_clipboard->Bind(wxEVT_BUTTON, &SysInfoDialog::onCopyToClipboard, this); - this->SetEscapeId(wxID_CLOSE); + this->SetEscapeId(wxID_OK); this->Bind(wxEVT_BUTTON, &SysInfoDialog::onCloseDialog, this, wxID_OK); main_sizer->Add(buttons, 0, wxEXPAND | wxRIGHT | wxBOTTOM, 3); From 68684dd0037a76d1f74e7368159bfc2a43ae6d9d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 13:20:21 +0100 Subject: [PATCH 059/132] Removed obsolete GLCanvas3D::m_shader_enabled --- src/slic3r/GUI/GLCanvas3D.cpp | 10 +--------- src/slic3r/GUI/GLCanvas3D.hpp | 2 -- src/slic3r/GUI/GUI_Preview.cpp | 2 -- src/slic3r/GUI/Plater.cpp | 1 - 4 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 77d0f0776..66d4622fe 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3830,7 +3830,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_legend_texture_enabled(false) , m_picking_enabled(false) , m_moving_enabled(false) - , m_shader_enabled(false) , m_dynamic_background_enabled(false) , m_multisample_allowed(false) , m_regenerate_volumes(true) @@ -4144,11 +4143,6 @@ void GLCanvas3D::enable_toolbar(bool enable) m_toolbar.set_enabled(enable); } -void GLCanvas3D::enable_shader(bool enable) -{ - m_shader_enabled = enable; -} - void GLCanvas3D::enable_force_zoom_to_bed(bool enable) { m_force_zoom_to_bed_enabled = enable; @@ -6322,9 +6316,7 @@ void GLCanvas3D::_render_objects() const ::glEnable(GL_LIGHTING); ::glEnable(GL_DEPTH_TEST); - if (!m_shader_enabled) - _render_volumes(false); - else if (m_use_VBOs) + if (m_use_VBOs) { if (m_picking_enabled) { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 8e9d44a4b..0cd6cb9b9 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -850,7 +850,6 @@ private: bool m_legend_texture_enabled; bool m_picking_enabled; bool m_moving_enabled; - bool m_shader_enabled; bool m_dynamic_background_enabled; bool m_multisample_allowed; bool m_regenerate_volumes; @@ -950,7 +949,6 @@ public: void enable_moving(bool enable); void enable_gizmos(bool enable); void enable_toolbar(bool enable); - void enable_shader(bool enable); void enable_force_zoom_to_bed(bool enable); void enable_dynamic_background(bool enable); void allow_multisample(bool allow); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 988a7b6a1..459bd9e5b 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -70,7 +70,6 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba m_canvas->set_config(config); m_canvas->enable_gizmos(true); m_canvas->enable_toolbar(true); - m_canvas->enable_shader(true); m_canvas->enable_force_zoom_to_bed(true); #if !ENABLE_IMGUI @@ -257,7 +256,6 @@ bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundS _3DScene::add_canvas(m_canvas_widget); m_canvas = _3DScene::get_canvas(this->m_canvas_widget); m_canvas->allow_multisample(GLCanvas3DManager::can_multisample()); - m_canvas->enable_shader(true); m_canvas->set_config(m_config); m_canvas->set_process(process); m_canvas->enable_legend_texture(true); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6a8bcc79b..db19180b4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1132,7 +1132,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) this->canvas3D->set_config(config); this->canvas3D->enable_gizmos(true); this->canvas3D->enable_toolbar(true); - this->canvas3D->enable_shader(true); this->canvas3D->enable_force_zoom_to_bed(true); #endif // ENABLE_REMOVE_TABS_FROM_PLATER From afc5ed0c624b585cb41c365b591e3a917d80a096 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 11:50:08 +0100 Subject: [PATCH 060/132] Printhost: Error message stashing, improvements --- src/slic3r/GUI/PrintHostDialogs.cpp | 78 +++++++++++++++++++++++++---- src/slic3r/GUI/PrintHostDialogs.hpp | 25 +++++++++ src/slic3r/Utils/PrintHost.cpp | 1 + 3 files changed, 95 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 586fe3d83..3ab4821bd 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -95,22 +95,46 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) auto *topsizer = new wxBoxSizer(wxVERTICAL); job_list = new wxDataViewListCtrl(this, wxID_ANY); + // Note: Keep these in sync with Column job_list->AppendTextColumn("ID", wxDATAVIEW_CELL_INERT); job_list->AppendProgressColumn("Progress", wxDATAVIEW_CELL_INERT); job_list->AppendTextColumn("Status", wxDATAVIEW_CELL_INERT); job_list->AppendTextColumn("Host", wxDATAVIEW_CELL_INERT); job_list->AppendTextColumn("Filename", wxDATAVIEW_CELL_INERT); + job_list->AppendTextColumn("error_message", wxDATAVIEW_CELL_INERT, -1, wxALIGN_CENTER, wxDATAVIEW_COL_HIDDEN); auto *btnsizer = new wxBoxSizer(wxHORIZONTAL); - auto *btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); // TODO: enable based on status ("show error" for failed jobs) + btn_cancel = new wxButton(this, wxID_DELETE, _(L("Cancel selected"))); + btn_cancel->Disable(); + btn_error = new wxButton(this, wxID_ANY, _(L("Show error message"))); + btn_error->Disable(); auto *btn_close = new wxButton(this, wxID_CANCEL, _(L("Close"))); btnsizer->Add(btn_cancel, 0, wxRIGHT, SPACING); + btnsizer->Add(btn_error, 0); btnsizer->AddStretchSpacer(); btnsizer->Add(btn_close); topsizer->Add(job_list, 1, wxEXPAND | wxBOTTOM, SPACING); topsizer->Add(btnsizer, 0, wxEXPAND); SetSizer(topsizer); + + job_list->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxDataViewEvent&) { on_list_select(); }); + + btn_cancel->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + int selected = job_list->GetSelectedRow(); + if (selected == wxNOT_FOUND) { return; } + + const JobState state = get_state(selected); + if (state < ST_ERROR) { + // TODO: cancel + } + }); + + btn_error->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + int selected = job_list->GetSelectedRow(); + if (selected == wxNOT_FOUND) { return; } + GUI::show_error(nullptr, job_list->GetTextValue(selected, COL_ERRORMSG)); + }); } void PrintHostQueueDialog::append_job(const PrintHostJob &job) @@ -123,29 +147,65 @@ void PrintHostQueueDialog::append_job(const PrintHostJob &job) fields.push_back(wxVariant(_(L("Enqueued")))); fields.push_back(wxVariant(job.printhost->get_host())); fields.push_back(wxVariant(job.upload_data.upload_path.string())); - job_list->AppendItem(fields); + fields.push_back(wxVariant("")); + job_list->AppendItem(fields, static_cast(ST_NEW)); +} + +PrintHostQueueDialog::JobState PrintHostQueueDialog::get_state(int idx) +{ + wxCHECK_MSG(idx >= 0 && idx < job_list->GetItemCount(), ST_ERROR, "Out of bounds access to job list"); + return static_cast(job_list->GetItemData(job_list->RowToItem(idx))); +} + +void PrintHostQueueDialog::set_state(int idx, JobState state) +{ + wxCHECK_RET(idx >= 0 && idx < job_list->GetItemCount(), "Out of bounds access to job list"); + job_list->SetItemData(job_list->RowToItem(idx), static_cast(state)); +} + +void PrintHostQueueDialog::on_list_select() +{ + int selected = job_list->GetSelectedRow(); + if (selected != wxNOT_FOUND) { + const JobState state = get_state(selected); + btn_cancel->Enable(state < ST_ERROR); + btn_error->Enable(state == ST_ERROR); + Layout(); + } else { + btn_cancel->Disable(); + } } void PrintHostQueueDialog::on_progress(Event &evt) { wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); - const wxVariant status(evt.progress < 100 ? _(L("Uploading")) : _(L("Complete"))); + if (evt.progress < 100) { + set_state(evt.job_id, ST_PROGRESS); + job_list->SetValue(wxVariant(evt.progress), evt.job_id, COL_PROGRESS); + job_list->SetValue(_(L("Uploading")), evt.job_id, COL_STATUS); + } else { + set_state(evt.job_id, ST_COMPLETED); + job_list->SetValue(wxVariant(100), evt.job_id, COL_PROGRESS); + job_list->SetValue(_(L("Complete")), evt.job_id, COL_STATUS); + } - job_list->SetValue(wxVariant(evt.progress), evt.job_id, 1); - job_list->SetValue(status, evt.job_id, 2); + on_list_select(); } void PrintHostQueueDialog::on_error(Event &evt) { wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); - job_list->SetValue(wxVariant(0), evt.job_id, 1); - job_list->SetValue(wxVariant(_(L("Error"))), evt.job_id, 2); - - // TODO: keep the error for repeated display + set_state(evt.job_id, ST_ERROR); auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error); + job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); + job_list->SetValue(wxVariant(_(L("Error"))), evt.job_id, COL_STATUS); + job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later + + on_list_select(); + GUI::show_error(nullptr, std::move(errormsg)); } diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index e38acee32..b8b5d62bb 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -13,10 +13,12 @@ #include "MsgDialog.hpp" #include "../Utils/PrintHost.hpp" +class wxButton; class wxTextCtrl; class wxCheckBox; class wxDataViewListCtrl; + namespace Slic3r { struct PrintHostJob; @@ -60,11 +62,34 @@ public: void append_job(const PrintHostJob &job); private: + enum Column { + COL_ID, + COL_PROGRESS, + COL_STATUS, + COL_HOST, + COL_FILENAME, + COL_ERRORMSG, + }; + + enum JobState { + ST_NEW, + ST_PROGRESS, + ST_ERROR, + ST_CANCELLING, + ST_CANCELLED, + ST_COMPLETED, + }; + + wxButton *btn_cancel; + wxButton *btn_error; wxDataViewListCtrl *job_list; // Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog EventGuard on_progress_evt; EventGuard on_error_evt; + JobState get_state(int idx); + void set_state(int idx, JobState); + void on_list_select(); void on_progress(Event&); void on_error(Event&); }; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 934436a19..5d0275d2b 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -127,6 +127,7 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) } else if (cancel_id > job_id) { jobs->at(cancel_id - job_id).cancelled = true; } + // TODO: emit cancelled } cancels->clear(); From 2d0dc6b050ebf9288f187fa7f321d6394bdcc4ad Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 13:36:49 +0100 Subject: [PATCH 061/132] Printhost: Cancelation, bugfixes --- src/libslic3r/Channel.hpp | 3 +- src/slic3r/GUI/PrintHostDialogs.cpp | 28 ++++++- src/slic3r/GUI/PrintHostDialogs.hpp | 3 + src/slic3r/Utils/Http.cpp | 6 +- src/slic3r/Utils/PrintHost.cpp | 109 +++++++++++++++++++++------- src/slic3r/Utils/PrintHost.hpp | 2 + 6 files changed, 118 insertions(+), 33 deletions(-) diff --git a/src/libslic3r/Channel.hpp b/src/libslic3r/Channel.hpp index 9cf025f2c..68369af63 100644 --- a/src/libslic3r/Channel.hpp +++ b/src/libslic3r/Channel.hpp @@ -77,8 +77,7 @@ public: } } - // Unlocked observers/hints - // Thread unsafe! Keep in mind you need to re-verify the result after locking! + // Unlocked observer/hint. Thread unsafe! Keep in mind you need to re-verify the result after locking. size_t size_hint() const noexcept { return m_queue.size(); } LockedConstPtr lock_read() const diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 3ab4821bd..8c0c0fc85 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -14,6 +14,7 @@ #include #include "GUI.hpp" +#include "GUI_App.hpp" #include "MsgDialog.hpp" #include "I18N.hpp" #include "../Utils/PrintHost.hpp" @@ -59,7 +60,8 @@ bool PrintHostSendDialog::start_print() const wxDEFINE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); -wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); +wxDEFINE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); PrintHostQueueDialog::Event::Event(wxEventType eventType, int winid, size_t job_id) : wxEvent(winid, eventType) @@ -87,6 +89,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) : wxDialog(parent, wxID_ANY, _(L("Print host upload queue")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) , on_progress_evt(this, EVT_PRINTHOST_PROGRESS, &PrintHostQueueDialog::on_progress, this) , on_error_evt(this, EVT_PRINTHOST_ERROR, &PrintHostQueueDialog::on_error, this) + , on_cancel_evt(this, EVT_PRINTHOST_CANCEL, &PrintHostQueueDialog::on_cancel, this) { enum { HEIGHT = 800, WIDTH = 400, SPACING = 5 }; @@ -127,6 +130,7 @@ PrintHostQueueDialog::PrintHostQueueDialog(wxWindow *parent) const JobState state = get_state(selected); if (state < ST_ERROR) { // TODO: cancel + GUI::wxGetApp().printhost_job_queue().cancel(selected); } }); @@ -161,6 +165,15 @@ void PrintHostQueueDialog::set_state(int idx, JobState state) { wxCHECK_RET(idx >= 0 && idx < job_list->GetItemCount(), "Out of bounds access to job list"); job_list->SetItemData(job_list->RowToItem(idx), static_cast(state)); + + switch (state) { + case ST_NEW: job_list->SetValue(_(L("Enqueued")), idx, COL_STATUS); break; + case ST_PROGRESS: job_list->SetValue(_(L("Uploading")), idx, COL_STATUS); break; + case ST_ERROR: job_list->SetValue(_(L("Error")), idx, COL_STATUS); break; + case ST_CANCELLING: job_list->SetValue(_(L("Cancelling")), idx, COL_STATUS); break; + case ST_CANCELLED: job_list->SetValue(_(L("Cancelled")), idx, COL_STATUS); break; + case ST_COMPLETED: job_list->SetValue(_(L("Completed")), idx, COL_STATUS); break; + } } void PrintHostQueueDialog::on_list_select() @@ -183,11 +196,9 @@ void PrintHostQueueDialog::on_progress(Event &evt) if (evt.progress < 100) { set_state(evt.job_id, ST_PROGRESS); job_list->SetValue(wxVariant(evt.progress), evt.job_id, COL_PROGRESS); - job_list->SetValue(_(L("Uploading")), evt.job_id, COL_STATUS); } else { set_state(evt.job_id, ST_COMPLETED); job_list->SetValue(wxVariant(100), evt.job_id, COL_PROGRESS); - job_list->SetValue(_(L("Complete")), evt.job_id, COL_STATUS); } on_list_select(); @@ -201,7 +212,6 @@ void PrintHostQueueDialog::on_error(Event &evt) auto errormsg = wxString::Format("%s\n%s", _(L("Error uploading to print host:")), evt.error); job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); - job_list->SetValue(wxVariant(_(L("Error"))), evt.job_id, COL_STATUS); job_list->SetValue(wxVariant(errormsg), evt.job_id, COL_ERRORMSG); // Stashes the error message into a hidden column for later on_list_select(); @@ -209,5 +219,15 @@ void PrintHostQueueDialog::on_error(Event &evt) GUI::show_error(nullptr, std::move(errormsg)); } +void PrintHostQueueDialog::on_cancel(Event &evt) +{ + wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + + set_state(evt.job_id, ST_CANCELLED); + job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); + + on_list_select(); +} + }} diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index b8b5d62bb..ee3fe26d8 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -86,16 +86,19 @@ private: // Note: EventGuard prevents delivery of progress evts to a freed PrintHostQueueDialog EventGuard on_progress_evt; EventGuard on_error_evt; + EventGuard on_cancel_evt; JobState get_state(int idx); void set_state(int idx, JobState); void on_list_select(); void on_progress(Event&); void on_error(Event&); + void on_cancel(Event&); }; wxDECLARE_EVENT(EVT_PRINTHOST_PROGRESS, PrintHostQueueDialog::Event); wxDECLARE_EVENT(EVT_PRINTHOST_ERROR, PrintHostQueueDialog::Event); +wxDECLARE_EVENT(EVT_PRINTHOST_CANCEL, PrintHostQueueDialog::Event); }} diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 30478cb01..2561348bb 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -149,7 +149,9 @@ int Http::priv::xfercb(void *userp, curl_off_t dltotal, curl_off_t dlnow, curl_o self->progressfn(progress, cb_cancel); } - return self->cancel || cb_cancel; + if (cb_cancel) { self->cancel = true; } + + return self->cancel; } int Http::priv::xfercb_legacy(void *userp, double dltotal, double dlnow, double ultotal, double ulnow) @@ -278,7 +280,7 @@ void Http::priv::http_perform() } else { long http_status = 0; ::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status); - + if (http_status >= 400) { if (errorfn) { errorfn(std::move(buffer), std::string(), http_status); } } else { diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 5d0275d2b..d50d6c6bd 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -49,6 +49,7 @@ struct PrintHostJobQueue::priv Channel channel_cancels; size_t job_id = 0; int prev_progress = -1; + fs::path source_to_remove; std::thread bg_thread; bool bg_exit = false; @@ -57,9 +58,14 @@ struct PrintHostJobQueue::priv priv(PrintHostJobQueue *q) : q(q) {} + void emit_progress(int progress); + void emit_error(wxString error); + void emit_cancel(size_t id); void start_bg_thread(); void bg_thread_main(); void progress_fn(Http::Progress progress, bool &cancel); + void remove_source(const fs::path &path); + void remove_source(); void perform_job(PrintHostJob the_job); }; @@ -78,6 +84,24 @@ PrintHostJobQueue::~PrintHostJobQueue() } } +void PrintHostJobQueue::priv::emit_progress(int progress) +{ + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, progress); + wxQueueEvent(queue_dialog, evt); +} + +void PrintHostJobQueue::priv::emit_error(wxString error) +{ + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error)); + wxQueueEvent(queue_dialog, evt); +} + +void PrintHostJobQueue::priv::emit_cancel(size_t id) +{ + auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_CANCEL, queue_dialog->GetId(), id); + wxQueueEvent(queue_dialog, evt); +} + void PrintHostJobQueue::priv::start_bg_thread() { if (bg_thread.joinable()) { return; } @@ -96,21 +120,43 @@ void PrintHostJobQueue::priv::bg_thread_main() // Pick up jobs from the job channel: while (! bg_exit) { auto job = channel_jobs.pop(); // Sleeps in a cond var if there are no jobs + source_to_remove = job.upload_data.source_path; + + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Received job: [%1%]: `%2%` -> `%3%`, cancelled: %4%") + % job_id + % job.upload_data.upload_path + % job.printhost->get_host() + % job.cancelled; + if (! job.cancelled) { perform_job(std::move(job)); } + + remove_source(); job_id++; } } catch (const std::exception &e) { - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, e.what()); - wxQueueEvent(queue_dialog, evt); + emit_error(e.what()); } catch (...) { wxTheApp->OnUnhandledException(); } + + // Cleanup leftover files, if any + remove_source(); + auto jobs = channel_jobs.lock_rw(); + for (const PrintHostJob &job : *jobs) { + remove_source(job.upload_data.source_path); + } } void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) { + if (cancel) { + // When cancel is true from the start, Http indicates request has been cancelled + emit_cancel(job_id); + return; + } + if (bg_exit) { cancel = true; return; @@ -125,49 +171,57 @@ void PrintHostJobQueue::priv::progress_fn(Http::Progress progress, bool &cancel) if (cancel_id == job_id) { cancel = true; } else if (cancel_id > job_id) { - jobs->at(cancel_id - job_id).cancelled = true; + const size_t idx = cancel_id - job_id - 1; + if (idx < jobs->size()) { + jobs->at(idx).cancelled = true; + BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue: Job id %1% cancelled") % cancel_id; + emit_cancel(cancel_id); + } } - // TODO: emit cancelled } cancels->clear(); } - int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0; - if (gui_progress != prev_progress) { - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, gui_progress); - wxQueueEvent(queue_dialog, evt); - prev_progress = gui_progress; + if (! cancel) { + int gui_progress = progress.ultotal > 0 ? 100*progress.ulnow / progress.ultotal : 0; + if (gui_progress != prev_progress) { + emit_progress(gui_progress); + prev_progress = gui_progress; + } } } +void PrintHostJobQueue::priv::remove_source(const fs::path &path) +{ + if (! path.empty()) { + boost::system::error_code ec; + fs::remove(path, ec); + if (ec) { + BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % path % ec; + } + } +} + +void PrintHostJobQueue::priv::remove_source() +{ + remove_source(source_to_remove); + source_to_remove.clear(); +} + void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) { if (bg_exit || the_job.empty()) { return; } - BOOST_LOG_TRIVIAL(debug) << boost::format("PrintHostJobQueue/bg_thread: Got job: `%1%` -> `%2%`") - % the_job.upload_data.upload_path - % the_job.printhost->get_host(); - - const fs::path gcode_path = the_job.upload_data.source_path; - bool success = the_job.printhost->upload(std::move(the_job.upload_data), [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, [this](wxString error) { - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_ERROR, queue_dialog->GetId(), job_id, std::move(error)); - wxQueueEvent(queue_dialog, evt); + emit_error(std::move(error)); } ); if (success) { - auto evt = new PrintHostQueueDialog::Event(GUI::EVT_PRINTHOST_PROGRESS, queue_dialog->GetId(), job_id, 100); - wxQueueEvent(queue_dialog, evt); - } - - boost::system::error_code ec; - fs::remove(gcode_path, ec); - if (ec) { - BOOST_LOG_TRIVIAL(error) << boost::format("PrintHostJobQueue: Error removing file `%1%`: %2%") % gcode_path % ec; + emit_progress(100); } } @@ -178,5 +232,10 @@ void PrintHostJobQueue::enqueue(PrintHostJob job) p->channel_jobs.push(std::move(job)); } +void PrintHostJobQueue::cancel(size_t id) +{ + p->channel_cancels.push(id); +} + } diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index a6c7a4723..39b93f5fb 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -55,6 +55,7 @@ struct PrintHostJob PrintHostJob(PrintHostJob &&other) : upload_data(std::move(other.upload_data)) , printhost(std::move(other.printhost)) + , cancelled(other.cancelled) {} PrintHostJob(DynamicPrintConfig *config) @@ -66,6 +67,7 @@ struct PrintHostJob { upload_data = std::move(other.upload_data); printhost = std::move(other.printhost); + cancelled = other.cancelled; return *this; } From ece3c74380ce9dae3abcbf5293bf1f7eb7a82aaa Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 13:44:44 +0100 Subject: [PATCH 062/132] Printhost: Minor bugfix --- src/slic3r/Utils/PrintHost.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index d50d6c6bd..b6f5b8b9c 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -138,7 +138,7 @@ void PrintHostJobQueue::priv::bg_thread_main() } catch (const std::exception &e) { emit_error(e.what()); } catch (...) { - wxTheApp->OnUnhandledException(); + emit_error("Unknown exception"); } // Cleanup leftover files, if any From 70bfa4202f15e40331107c884cc8ee4fa8b09d3c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 20 Dec 2018 15:22:58 +0100 Subject: [PATCH 063/132] Fix for SPE-700 (Corrupted slice data) --- src/libslic3r/SLA/SLASupportTree.cpp | 9 +++++++++ src/libslic3r/SLA/SLASupportTree.hpp | 2 ++ src/libslic3r/SLA/SLASupportTreeIGL.cpp | 2 +- src/libslic3r/SLAPrint.cpp | 12 +++++++++--- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 37b0c0ffc..c599cd83e 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -722,6 +722,10 @@ public: return m_pad; } + void remove_pad() { + m_pad = Pad(); + } + const Pad& pad() const { return m_pad; } // WITHOUT THE PAD!!! @@ -1729,6 +1733,11 @@ const TriangleMesh &SLASupportTree::get_pad() const return m_impl->pad().tmesh; } +void SLASupportTree::remove_pad() +{ + m_impl->remove_pad(); +} + SLASupportTree::SLASupportTree(const PointSet &points, const EigenMesh3D& emesh, const SupportConfig &cfg, diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 62e790611..e19f263b6 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -164,6 +164,8 @@ public: /// Get the pad geometry const TriangleMesh& get_pad() const; + void remove_pad(); + }; } diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 49290b3b8..6d4a770aa 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -198,7 +198,7 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, }); if(!neighnorms.empty()) { // there were neighbors to count with - // sum up the normals and than normalize the result again. + // sum up the normals and then normalize the result again. // This unification seems to be enough. Vec3d sumnorm(0, 0, 0); sumnorm = std::accumulate(neighnorms.begin(), lend, sumnorm); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 90abe290f..15f0e410e 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -560,9 +560,13 @@ void SLAPrint::process() // and before the supports had been sliced. (or the slicing has to be // repeated) - if(po.m_config.pad_enable.getBool() && - po.m_supportdata && - po.m_supportdata->support_tree_ptr) + if(!po.m_supportdata || !po.m_supportdata->support_tree_ptr) { + BOOST_LOG_TRIVIAL(warning) << "Uninitialized support data at " + << "pad creation."; + return; + } + + if(po.m_config.pad_enable.getBool()) { double wt = po.m_config.pad_wall_thickness.getFloat(); double h = po.m_config.pad_wall_height.getFloat(); @@ -586,6 +590,8 @@ void SLAPrint::process() pcfg.throw_on_cancel = thrfn; po.m_supportdata->support_tree_ptr->add_pad(bp, pcfg); + } else { + po.m_supportdata->support_tree_ptr->remove_pad(); } po.throw_if_canceled(); From 5cd59377953d7bfaf5fbf79c351821c2c862b9df Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 20 Dec 2018 16:09:25 +0100 Subject: [PATCH 064/132] More transparent background texture for toolbars --- resources/icons/toolbar_background.png | Bin 1544 -> 1540 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/icons/toolbar_background.png b/resources/icons/toolbar_background.png index 2b5ea013be7a6b804fb8390bec7d386e39e42d16..e7ce3c14615b551ea8fa873b1fe52c4215160a8f 100644 GIT binary patch delta 1256 zcmVOt{9C}mBHD29)sZ>waKge_JOYoK|c zR7aT@(H<12QGcO9eYKS;mKeCRp=GWtOP0hqb6rrvEHv08OLJhvQbA21=4&mq{O0Aa zQiA73&}4$KN%T{3uY`Y6E<|nmCkhxh0FUKu>*e{yZ9FZ5u#{;9Id65`PfzkwhtyH)bP39258ytehqA z0Rp7Tjobhmkq9`6r*F(LTDq)!igC37gyNtW&}1oq6@nsvEF|PmQPJR3HL7XStR+ee zQDcl2=N(l{ESOp}vt-#yk`$7rm@K7~Q^}kKSD5CQ&E=GHE(J~tOc%^9&?x2LLymCh z5f3}kk$;a;kv=t4t+857HP_O(NyG7Pv6*YB^3jyP|xZK{vd=!x9d)M!(i#KO|E8#Rc{K1|TOPINK@F%AUcHVGi1c{1~fLf}bmGV_^n zD3n2sbi!#giGg6~#5(9=cO&;DH>csZxbd&Zxk-ucUyySX-6r>$+Y4%~tZx)qfaWQj zG4(QVu|c7y!l}&fq(5%m-x_!udK-EhdK-EhdK-Eh`hN@!{CL1$P4^RitdWu+h246S z2m>;I6b}a*L#rkB00082NklJG9Kw`%~MxcI@jU$u3NU;s>i{x^Uc&;{Dg^(GC0emp{)9l%GR2i!Tw zo3;Qx1K$t*@V#^QBk<(8y%cibA$~tOC4fiZlXEMbCa~F$AU|-}e{xo(bq~A(FU1F8 z?}D%Aj7qN;*ZQvjx?b=l;V!QAn*bW#>LsNe*ZfZa9cPl9P7y%ycvop@0o8zycT*K7 zfL4h2hf4I>Kw$e;Pid-$-FreMrxw`0&sUl%0blr}Ecfc)TLBr9sstaCx&#Xx*JACG zrX7GsGzTUFkO9a5lvn^8mox1EGMCd+ld1%B0r8U_1u%b$=Kurek(@#C8~|Vf44p|b zhH=f`JPGUr3ujY$i@4SR6W|s2aAX_Z&N=^i|1ZCg{o|KwKo@8`$D6&{?{nmRPj=#t8bAkV z;r|d^yEQ!9hdhJgR_~bX%$-+b1WlkGj}R446Bn~$`#OKd_NCr9F8=(~9sCCDH&b3) Sn1ts50000yS&GhaJQZPIe*%=>DfHr2SYHLLQmLn z7ZQ&;&RGO2kR$~)uR8Fwc=UPMRqS=_+|_*w-qm}GoiE!ikD+irZio03qirXB9lI5w z`xVh`=7$F+*A$;(H%3ci^mBJ5L_{jOy{V6_tR5T>gi_Y9h+_CC`nF0&N!Vfqu?Ct4 zN_CWp5zRq?8h;fU)K^=nVu^t}8(QYdvTR9=GuH(r%tC`rvNQ)4EEUuQX1>-!Yu>!( ztCTQvBX}}FY!dxc+$-asluKxgnYu(iU9rNtc;+;gG0DvuUI0R4$5dB*S8;cFVBszx}#;yj~@i3L-OW|k~lNs>a+6qBWtaw?g#;0V(kv$>pd&ZS_|0_lR$1s?F zJ>p?UI)Cy}D$=Khsx?-tspeW5H)(kNTWsc9YPpq89jVkq*B-m|)N?Nbxi(~kp(73( zY2;BZYMbgKHF_fVH8tAQCb6*m>_!b{vkwWH$B9m6AjW|}+$I4eG*4zeQ3yQAO=dnb zUJ7MUBb{&>O=2JzI^1-0ap z2m>;I4hbAP>e>bE00086Nklv2ex?mCSNzY@``V1tRDl-I1nNKyr~qYj z4l7^@%z!B{21daA*bQVyey#y@;^GJAe$m!{F@gaw`QHF)Ko@8`*Q+!H`f(3&asZ!! z9&qCvui66m3j93uX6cm=Zp%k7uWi)0J@&=CE+Hn^_u`1Ug{;K9oPI%03BzNoK7A;nsD zQ+SKG)&P`t-gpIGfLmu&Xb-@zLjeFh#h*>*R7l@|AG?f3cMc~5rah8jQzXyN}5T)H*eJDWU% z{8H~D*@+vk#t51~J? Date: Thu, 20 Dec 2018 16:25:32 +0100 Subject: [PATCH 065/132] Don't set done on a canceled step. --- src/libslic3r/SLAPrint.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 15f0e410e..31ba0be3d 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -872,6 +872,7 @@ void SLAPrint::process() if(po->m_stepmask[currentstep] && po->set_started(currentstep)) { report_status(*this, int(st), OBJ_STEP_LABELS[currentstep]); pobj_program[currentstep](*po); + throw_if_canceled(); po->set_done(currentstep); } @@ -897,6 +898,7 @@ void SLAPrint::process() { report_status(*this, int(st), PRINT_STEP_LABELS[currentstep]); print_program[currentstep](); + throw_if_canceled(); set_done(currentstep); } From 4d70546a0596eb3e256a9d78bfee7c0b924383e2 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 16:35:54 +0100 Subject: [PATCH 066/132] Fix: Schedule SLA print uploads too (the same way as FFF) --- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 56 +++++++++++++-------- src/slic3r/GUI/BackgroundSlicingProcess.hpp | 1 + 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 66a0884a4..44ac1bc71 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -84,26 +84,7 @@ void BackgroundSlicingProcess::process_fff() run_post_process_scripts(export_path, m_fff_print->config()); m_print->set_status(100, "G-code file exported to " + export_path); } else if (! m_upload_job.empty()) { - // A print host upload job has been scheduled - - // XXX: is fs::path::string() right? - - // Generate a unique temp path to which the gcode is copied - boost::filesystem::path source_path = boost::filesystem::temp_directory_path() - / boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode"); - - if (copy_file(m_temp_output_path, source_path.string()) != 0) { - throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); - } - - m_print->set_status(95, "Running post-processing scripts"); - run_post_process_scripts(source_path.string(), m_fff_print->config()); - m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str()); - - m_upload_job.upload_data.source_path = std::move(source_path); - m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); - - GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job)); + prepare_upload(); } else { m_print->set_status(100, "Slicing complete"); } @@ -170,6 +151,10 @@ void BackgroundSlicingProcess::process_sla() if (! m_export_path.empty()) { m_sla_print->export_raster(m_export_path); m_print->set_status(100, "Zip file exported to " + m_export_path); + } else if (! m_upload_job.empty()) { + prepare_upload(); + } else { + m_print->set_status(100, "Slicing complete"); } this->set_step_done(bspsGCodeFinalize); } @@ -440,4 +425,35 @@ bool BackgroundSlicingProcess::invalidate_all_steps() return m_step_state.invalidate_all([this](){ this->stop_internal(); }); } +void BackgroundSlicingProcess::prepare_upload() +{ + // A print host upload job has been scheduled, enqueue it to the printhost job queue + + // XXX: is fs::path::string() right? + + // Generate a unique temp path to which the gcode/zip file is copied/exported + boost::filesystem::path source_path = boost::filesystem::temp_directory_path() + / boost::filesystem::unique_path(".printhost.%%%%-%%%%-%%%%-%%%%.gcode"); + + if (m_print == m_fff_print) { + m_print->set_status(95, "Running post-processing scripts"); + run_post_process_scripts(source_path.string(), m_fff_print->config()); + + if (copy_file(m_temp_output_path, source_path.string()) != 0) { + throw std::runtime_error("Copying of the temporary G-code to the output G-code failed"); + } + + m_upload_job.upload_data.upload_path = m_fff_print->print_statistics().finalize_output_path(m_upload_job.upload_data.upload_path.string()); + } else { + m_sla_print->export_raster(source_path.string()); + // TODO: Also finalize upload path like with FFF when there are statistics for SLA print + } + + m_print->set_status(100, (boost::format("Scheduling upload to `%1%`. See Window -> Print Host Upload Queue") % m_upload_job.printhost->get_host()).str()); + + m_upload_job.upload_data.source_path = std::move(source_path); + + GUI::wxGetApp().printhost_job_queue().enqueue(std::move(m_upload_job)); +} + }; // namespace Slic3r diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp index 222ed147e..5911c8a02 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp @@ -167,6 +167,7 @@ private: bool invalidate_all_steps(); // If the background processing stop was requested, throw CanceledException. void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); } + void prepare_upload(); // wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue. int m_event_slicing_completed_id = 0; From f3185365566d3389c017ed39903e083a8942d901 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 16:42:46 +0100 Subject: [PATCH 067/132] Printhost: Make queue window non-modal, fix upload start progress --- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/Utils/PrintHost.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 871d50a3d..5b38129de 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -376,7 +376,7 @@ void MainFrame::init_menubar() windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"), - [this](wxCommandEvent&) { m_printhost_queue_dlg->ShowModal(); }, "arrow_up.png"); + [this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "arrow_up.png"); } // View menu diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index b6f5b8b9c..84d823d89 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -213,6 +213,8 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job) { if (bg_exit || the_job.empty()) { return; } + emit_progress(0); // Indicate the upload is starting + bool success = the_job.printhost->upload(std::move(the_job.upload_data), [this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); }, [this](wxString error) { From af086263988d9ac50361bff47370e64610fecf33 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 16:00:51 +0100 Subject: [PATCH 068/132] Duet: Implement upload() (refactoring from old code) --- src/slic3r/Utils/Duet.cpp | 133 ++++++++++----------------------- src/slic3r/Utils/Duet.hpp | 2 +- src/slic3r/Utils/OctoPrint.cpp | 15 +--- src/slic3r/Utils/OctoPrint.hpp | 1 - src/slic3r/Utils/PrintHost.cpp | 10 +++ src/slic3r/Utils/PrintHost.hpp | 3 + 6 files changed, 55 insertions(+), 109 deletions(-) diff --git a/src/slic3r/Utils/Duet.cpp b/src/slic3r/Utils/Duet.cpp index fd77fc130..3449e610e 100644 --- a/src/slic3r/Utils/Duet.cpp +++ b/src/slic3r/Utils/Duet.cpp @@ -54,91 +54,46 @@ wxString Duet::get_test_failed_msg (wxString &msg) const return wxString::Format("%s: %s", _(L("Could not connect to Duet")), msg); } -// bool Duet::send_gcode(const std::string &filename) const -// { -// enum { PROGRESS_RANGE = 1000 }; - -// const auto errortitle = _(L("Error while uploading to the Duet")); -// fs::path filepath(filename); - -// GUI::PrintHostSendDialog send_dialog(filepath.filename()); -// if (send_dialog.ShowModal() != wxID_OK) { return false; } - -// const bool print = send_dialog.start_print(); -// const auto upload_filepath = send_dialog.filename(); -// const auto upload_filename = upload_filepath.filename(); -// const auto upload_parent_path = upload_filepath.parent_path(); - -// wxProgressDialog progress_dialog( -// _(L("Duet upload")), -// _(L("Sending G-code file to Duet...")), -// PROGRESS_RANGE, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); -// progress_dialog.Pulse(); - -// wxString connect_msg; -// if (!connect(connect_msg)) { -// auto errormsg = wxString::Format("%s: %s", errortitle, connect_msg); -// GUI::show_error(&progress_dialog, std::move(errormsg)); -// return false; -// } - -// bool res = true; - -// auto upload_cmd = get_upload_url(upload_filepath.string()); -// BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filename: %2%, path: %3%, print: %4%, command: %5%") -// % filepath.string() -// % upload_filename.string() -// % upload_parent_path.string() -// % print -// % upload_cmd; - -// auto http = Http::post(std::move(upload_cmd)); -// http.set_post_body(filename) -// .on_complete([&](std::string body, unsigned status) { -// BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; -// progress_dialog.Update(PROGRESS_RANGE); - -// int err_code = get_err_code_from_body(body); -// if (err_code != 0) { -// auto msg = format_error(body, L("Unknown error occured"), 0); -// GUI::show_error(&progress_dialog, std::move(msg)); -// res = false; -// } else if (print) { -// wxString errormsg; -// res = start_print(errormsg, upload_filepath.string()); -// if (!res) { -// GUI::show_error(&progress_dialog, std::move(errormsg)); -// } -// } -// }) -// .on_error([&](std::string body, std::string error, unsigned status) { -// BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; -// auto errormsg = wxString::Format("%s: %s", errortitle, format_error(body, error, status)); -// GUI::show_error(&progress_dialog, std::move(errormsg)); -// res = false; -// }) -// .on_progress([&](Http::Progress progress, bool &cancel) { -// if (cancel) { -// // Upload was canceled -// res = false; -// } else if (progress.ultotal > 0) { -// int value = PROGRESS_RANGE * progress.ulnow / progress.ultotal; -// cancel = !progress_dialog.Update(std::min(value, PROGRESS_RANGE - 1)); // Cap the value to prevent premature dialog closing -// } else { -// cancel = !progress_dialog.Pulse(); -// } -// }) -// .perform_sync(); - -// disconnect(); - -// return res; -// } - bool Duet::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn) const { - // XXX: TODO - throw "unimplemented"; + wxString connect_msg; + if (!connect(connect_msg)) { + error_fn(std::move(connect_msg)); + return false; + } + + bool res = true; + + auto upload_cmd = get_upload_url(upload_data.upload_path.string()); + BOOST_LOG_TRIVIAL(info) << boost::format("Duet: Uploading file %1%, filepath: %2%, print: %3%, command: %4%") + % upload_data.source_path + % upload_data.upload_path + % upload_data.start_print + % upload_cmd; + + auto http = Http::post(std::move(upload_cmd)); + http.set_post_body(upload_data.source_path) + .on_complete([&](std::string body, unsigned status) { + BOOST_LOG_TRIVIAL(debug) << boost::format("Duet: File uploaded: HTTP %1%: %2%") % status % body; + }) + .on_error([&](std::string body, std::string error, unsigned status) { + BOOST_LOG_TRIVIAL(error) << boost::format("Duet: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; + error_fn(format_error(body, error, status)); + res = false; + }) + .on_progress([&](Http::Progress progress, bool &cancel) { + prorgess_fn(std::move(progress), cancel); + if (cancel) { + // Upload was canceled + BOOST_LOG_TRIVIAL(info) << "Duet: Upload canceled"; + res = false; + } + }) + .perform_sync(); + + disconnect(); + + return res; } bool Duet::has_auto_discovery() const @@ -241,20 +196,10 @@ std::string Duet::timestamp_str() const return std::string(buffer); } -wxString Duet::format_error(const std::string &body, const std::string &error, unsigned status) -{ - if (status != 0) { - auto wxbody = wxString::FromUTF8(body.data()); - return wxString::Format("HTTP %u: %s", status, wxbody); - } else { - return wxString::FromUTF8(error.data()); - } -} - bool Duet::start_print(wxString &msg, const std::string &filename) const { bool res = false; - + auto url = (boost::format("%1%rr_gcode?gcode=M32%%20\"%2%\"") % get_base_url() % Http::url_encode(filename)).str(); diff --git a/src/slic3r/Utils/Duet.hpp b/src/slic3r/Utils/Duet.hpp index e053f91ef..e1c28d149 100644 --- a/src/slic3r/Utils/Duet.hpp +++ b/src/slic3r/Utils/Duet.hpp @@ -26,6 +26,7 @@ public: virtual bool has_auto_discovery() const; virtual bool can_test() const; virtual std::string get_host() const { return host; } + private: std::string host; std::string password; @@ -38,7 +39,6 @@ private: void disconnect() const; bool start_print(wxString &msg, const std::string &filename) const; int get_err_code_from_body(const std::string &body) const; - static wxString format_error(const std::string &body, const std::string &error, unsigned status); }; diff --git a/src/slic3r/Utils/OctoPrint.cpp b/src/slic3r/Utils/OctoPrint.cpp index af9d6e4f0..2e2e169b8 100644 --- a/src/slic3r/Utils/OctoPrint.cpp +++ b/src/slic3r/Utils/OctoPrint.cpp @@ -102,7 +102,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro auto url = make_url("api/files/local"); BOOST_LOG_TRIVIAL(info) << boost::format("Octoprint: Uploading file %1% at %2%, filename: %3%, path: %4%, print: %5%") - % upload_data.source_path.string() + % upload_data.source_path % url % upload_filename.string() % upload_parent_path.string() @@ -118,7 +118,6 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro }) .on_error([&](std::string body, std::string error, unsigned status) { BOOST_LOG_TRIVIAL(error) << boost::format("Octoprint: Error uploading file: %1%, HTTP %2%, body: `%3%`") % error % status % body; - // error_fn(std::move(body), std::move(error), status); error_fn(format_error(body, error, status)); res = false; }) @@ -126,7 +125,7 @@ bool OctoPrint::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, Erro prorgess_fn(std::move(progress), cancel); if (cancel) { // Upload was canceled - BOOST_LOG_TRIVIAL(error) << "Octoprint: Upload canceled"; + BOOST_LOG_TRIVIAL(info) << "Octoprint: Upload canceled"; res = false; } }) @@ -172,16 +171,6 @@ std::string OctoPrint::make_url(const std::string &path) const } } -wxString OctoPrint::format_error(const std::string &body, const std::string &error, unsigned status) -{ - if (status != 0) { - auto wxbody = wxString::FromUTF8(body.data()); - return wxString::Format("HTTP %u: %s", status, wxbody); - } else { - return wxString::FromUTF8(error.data()); - } -} - // SLAHost diff --git a/src/slic3r/Utils/OctoPrint.hpp b/src/slic3r/Utils/OctoPrint.hpp index 1e739c99d..8da149f53 100644 --- a/src/slic3r/Utils/OctoPrint.hpp +++ b/src/slic3r/Utils/OctoPrint.hpp @@ -38,7 +38,6 @@ private: void set_auth(Http &http) const; std::string make_url(const std::string &path) const; - static wxString format_error(const std::string &body, const std::string &error, unsigned status); }; diff --git a/src/slic3r/Utils/PrintHost.cpp b/src/slic3r/Utils/PrintHost.cpp index 84d823d89..31fe909c4 100644 --- a/src/slic3r/Utils/PrintHost.cpp +++ b/src/slic3r/Utils/PrintHost.cpp @@ -38,6 +38,16 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config) } } +wxString PrintHost::format_error(const std::string &body, const std::string &error, unsigned status) const +{ + if (status != 0) { + auto wxbody = wxString::FromUTF8(body.data()); + return wxString::Format("HTTP %u: %s", status, wxbody); + } else { + return wxString::FromUTF8(error.data()); + } +} + struct PrintHostJobQueue::priv { diff --git a/src/slic3r/Utils/PrintHost.hpp b/src/slic3r/Utils/PrintHost.hpp index 39b93f5fb..d740ea99e 100644 --- a/src/slic3r/Utils/PrintHost.hpp +++ b/src/slic3r/Utils/PrintHost.hpp @@ -41,6 +41,9 @@ public: virtual std::string get_host() const = 0; static PrintHost* get_print_host(DynamicPrintConfig *config); + +protected: + virtual wxString format_error(const std::string &body, const std::string &error, unsigned status) const; }; From 42fada4576abc1c7d4a61106e499771df5b4168f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 20 Dec 2018 17:44:24 +0100 Subject: [PATCH 069/132] Updated SLA profiles. Fixed the update downloader to ignore downloaded preset index if it is older than the currently active one. --- resources/profiles/PrusaResearch.idx | 15 +++ resources/profiles/PrusaResearch.ini | 158 +++++++++++++++++++++++---- src/slic3r/Utils/Http.cpp | 2 + src/slic3r/Utils/PresetUpdater.cpp | 37 +++++-- 4 files changed, 179 insertions(+), 33 deletions(-) diff --git a/resources/profiles/PrusaResearch.idx b/resources/profiles/PrusaResearch.idx index e440b35df..49d8ac1a5 100644 --- a/resources/profiles/PrusaResearch.idx +++ b/resources/profiles/PrusaResearch.idx @@ -1,6 +1,21 @@ min_slic3r_version = 1.42.0-alpha +0.4.0-alpha3 Update of SLA profiles 0.4.0-alpha2 First SLA profiles +min_slic3r_version = 1.41.1 +0.3.3 Prusament PETG released +0.3.2 New MK2.5 and MK3 FW versions +0.3.1 New MK2.5 and MK3 FW versions +0.3.0 New MK2.5 and MK3 FW version min_slic3r_version = 1.41.0-alpha +0.2.9 New MK2.5 and MK3 FW versions +0.2.8 New MK2.5 and MK3 FW version +min_slic3r_version = 1.41.1 +0.2.7 New MK2.5 and MK3 FW version +0.2.6 Added MMU2 MK2.5 settings +min_slic3r_version = 1.41.0-alpha +0.2.5 Prusament is out - added prusament settings +0.2.4 Added soluble support profiles for MMU2 +0.2.3 Added materials for MMU2 single mode, edited MK3 xy stealth feedrate limit 0.2.2 Edited MMU2 Single mode purge line 0.2.1 Added PET and BVOH settings for MMU2 0.2.0-beta5 Fixed MMU1 ramming parameters diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index d5425dc12..7bdcb6906 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -5,7 +5,7 @@ name = Prusa Research # Configuration version of this file. Config file will only be installed, if the config_version differs. # This means, the server may force the Slic3r configuration to be downgraded. -config_version = 0.4.0-alpha2 +config_version = 0.4.0-alpha3 # Where to get the updates from? config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/master/live/PrusaResearch/ @@ -41,7 +41,7 @@ variants = 0.4 [printer_model:SL1] name = Original Prusa SL1 -variants = default; dummy +variants = default # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. @@ -1137,19 +1137,132 @@ min_fan_speed = 100 start_filament_gcode = "M900 K{if printer_notes=~/.*PRINTER_HAS_BOWDEN.*/}200{else}10{endif}; Filament gcode" temperature = 220 -[sla_material:*common*] +[sla_print:*common*] +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/ layer_height = 0.05 -initial_layer_height = 0.3 +output_filename_format = [input_filename_base].dwz +pad_edge_radius = 0.5 +pad_enable = 1 +pad_max_merge_distance = 50 +pad_wall_height = 3 +pad_wall_thickness = 1 +support_base_diameter = 3 +support_base_height = 0.5 +support_critical_angle = 45 +support_density_at_45 = 250 +support_density_at_horizontal = 500 +support_head_front_diameter = 0.4 +support_head_penetration = 0.4 +support_head_width = 3 +support_max_bridge_length = 10 +support_minimal_z = 0 +support_object_elevation = 5 +support_pillar_diameter = 1 +support_pillar_widening_factor = 0 +supports_enable = 1 + +[sla_print:0.025 UltraDetail] +inherits = *common* +layer_height = 0.025 +support_head_width = 2 + +[sla_print:0.035 Detail] +inherits = *common* +layer_height = 0.035 + +[sla_print:0.05 Normal] +inherits = *common* +layer_height = 0.05 + +[sla_print:0.1 Fast] +inherits = *common* +layer_height = 0.1 + +########### Materials 0.025 + +[sla_material:*common 0.05*] +compatible_printers_condition = printer_notes=~/.*PRINTER_VENDOR_PRUSA3D.*/ and printer_notes=~/.*PRINTER_MODEL_SL1.*/ +compatible_prints_condition = layer_height == 0.05 +exposure_time = 12 +initial_exposure_time = 45 +initial_layer_height = 0.5 +material_correction_curing = 1,1,1 +material_correction_printing = 1,1,1 +material_notes = + +[sla_material:*common 0.025*] +inherits = *common 0.05* +compatible_prints_condition = layer_height == 0.025 exposure_time = 10 -initial_exposure_time = 15 -material_correction_printing = 1, 1, 1 -material_correction_curing = 1, 1, 1 +initial_exposure_time = 35 -[sla_material:Material 1] -inherits = *common* +[sla_material:*common 0.035*] +inherits = *common 0.05* +compatible_prints_condition = layer_height == 0.035 +exposure_time = 13 +initial_exposure_time = 40 -[sla_material:Material 2] -inherits = *common* +[sla_material:*common 0.1*] +inherits = *common 0.05* +compatible_prints_condition = layer_height == 0.1 +exposure_time = 20 +initial_exposure_time = 90 + +########### Materials 0.025 + +[sla_material:Jamg He Transparent Clear 0.025] +inherits = *common 0.025* + +[sla_material:Jamg He Transparent Green 0.025] +inherits = *common 0.025* + +[sla_material:Jamg He Transparent Orange 0.025] +inherits = *common 0.025* + +[sla_material:Jamg He Transparent Red 0.025] +inherits = *common 0.025* + +########### Materials 0.05 + +[sla_material:Jamg He Transparent Clear 0.05] +inherits = *common 0.05* + +[sla_material:Jamg He Transparent Green 0.05] +inherits = *common 0.05* + +[sla_material:Jamg He Transparent Orange 0.05] +inherits = *common 0.05* + +[sla_material:Jamg He Transparent Red 0.05] +inherits = *common 0.05* + +########### Materials 0.035 + +[sla_material:Jamg He Transparent Clear 0.035] +inherits = *common 0.035* + +[sla_material:Jamg He Transparent Green 0.035] +inherits = *common 0.035* + +[sla_material:Jamg He Transparent Orange 0.035] +inherits = *common 0.035* + +[sla_material:Jamg He Transparent Red 0.035] +inherits = *common 0.035* + +########### Materials 0.1 + +[sla_material:Jamg He Transparent Clear 0.1] +inherits = *common 0.1* + +[sla_material:Jamg He Transparent Green 0.1] +inherits = *common 0.1* + +[sla_material:Jamg He Transparent Orange 0.1] +inherits = *common 0.1* + +[sla_material:Jamg He Transparent Red 0.1] +inherits = *common 0.1* [printer:*common*] printer_technology = FFF @@ -1467,21 +1580,20 @@ end_gcode = {if has_wipe_tower}\nG1 E-15.0000 F3000\n{else}\nG1 X0 Y210 F7200\nG printer_technology = SLA printer_model = SL1 printer_variant = default -default_sla_material_profile = Material 1 -bed_shape = 0x0,150x0,150x100,0x100 -max_print_height = 100 -display_width = 150 -display_height = 100 -display_pixels_x = 2000 -display_pixels_y = 1000 +default_sla_material_profile = Jamg He Transparent Green 0.05 +default_sla_print_profile = 0.05 Normal +bed_shape = 0.98x1.02,119.98x1.02,119.98x68.02,0.98x68.02 +display_height = 68.04 +display_orientation = portrait +display_pixels_x = 2560 +display_pixels_y = 1440 +display_width = 120.96 +max_print_height = 150 printer_correction = 1,1,1 - -[printer:Original Prusa SL1 dummy] -inherits = Original Prusa SL1 -printer_variant = dummy -default_sla_material_profile = Material 2 +printer_notes = Don't remove the following keywords! These keywords are used in the "compatible printer" condition of the print and filament profiles to link the particular print and filament profiles to this printer profile.\nPRINTER_VENDOR_PRUSA3D\nPRINTER_MODEL_SL1\n # The obsolete presets will be removed when upgrading from the legacy configuration structure (up to Slic3r 1.39.2) to 1.40.0 and newer. [obsolete_presets] print="0.05mm DETAIL 0.25 nozzle";"0.05mm DETAIL MK3";"0.05mm DETAIL";"0.20mm NORMAL MK3";"0.35mm FAST MK3";"print:0.15mm OPTIMAL MK3 MMU2";"print:0.20mm FAST MK3 MMU2" filament="ColorFabb Brass Bronze 1.75mm";"ColorFabb HT 1.75mm";"ColorFabb nGen 1.75mm";"ColorFabb Woodfil 1.75mm";"ColorFabb XT 1.75mm";"ColorFabb XT-CF20 1.75mm";"E3D PC-ABS 1.75mm";"Fillamentum ABS 1.75mm";"Fillamentum ASA 1.75mm";"Generic ABS 1.75mm";"Generic PET 1.75mm";"Generic PLA 1.75mm";"Prusa ABS 1.75mm";"Prusa HIPS 1.75mm";"Prusa PET 1.75mm";"Prusa PLA 1.75mm";"Taulman Bridge 1.75mm";"Taulman T-Glase 1.75mm" +printer="Original Prusa SL1 dummy" diff --git a/src/slic3r/Utils/Http.cpp b/src/slic3r/Utils/Http.cpp index 30478cb01..471cd8bc4 100644 --- a/src/slic3r/Utils/Http.cpp +++ b/src/slic3r/Utils/Http.cpp @@ -235,7 +235,9 @@ void Http::priv::http_perform() #if LIBCURL_VERSION_MAJOR >= 7 && LIBCURL_VERSION_MINOR >= 32 ::curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, xfercb); ::curl_easy_setopt(curl, CURLOPT_XFERINFODATA, static_cast(this)); +#ifndef _WIN32 (void)xfercb_legacy; // prevent unused function warning +#endif #else ::curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, xfercb); ::curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, static_cast(this)); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index 924cf382d..be2a17d66 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -112,7 +113,7 @@ struct PresetUpdater::priv bool get_file(const std::string &url, const fs::path &target_path) const; void prune_tmps() const; void sync_version() const; - void sync_config(const std::set vendors) const; + void sync_config(const std::set vendors); void check_install_indices() const; Updates get_config_updates() const; @@ -130,7 +131,7 @@ PresetUpdater::priv::priv() : { set_download_prefs(GUI::wxGetApp().app_config); check_install_indices(); - index_db = std::move(Index::load_db()); + index_db = Index::load_db(); } // Pull relevant preferences from AppConfig @@ -220,14 +221,14 @@ void PresetUpdater::priv::sync_version() const // Download vendor indices. Also download new bundles if an index indicates there's a new one available. // Both are saved in cache. -void PresetUpdater::priv::sync_config(const std::set vendors) const +void PresetUpdater::priv::sync_config(const std::set vendors) { BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache"; if (!enabled_config_update) { return; } // Donwload vendor preset bundles - for (const auto &index : index_db) { + for (auto &index : index_db) { if (cancel) { return; } const auto vendor_it = vendors.find(VendorProfile(index.vendor())); @@ -245,17 +246,33 @@ void PresetUpdater::priv::sync_config(const std::set vendors) con // Download a fresh index BOOST_LOG_TRIVIAL(info) << "Downloading index for vendor: " << vendor.name; const auto idx_url = vendor.config_update_url + "/" + INDEX_FILENAME; - const auto idx_path = cache_path / (vendor.id + ".idx"); - if (! get_file(idx_url, idx_path)) { continue; } + const std::string idx_path = (cache_path / (vendor.id + ".idx")).string(); + const std::string idx_path_temp = idx_path + "-update"; + if (!get_file(idx_url, idx_path_temp)) { continue; } if (cancel) { return; } // Load the fresh index up - Index new_index; - new_index.load(idx_path); + { + Index new_index; + try { + new_index.load(idx_path_temp); + } catch (const std::exception &err) { + BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading a downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name; + continue; + } + if (new_index.version() < index.version()) { + BOOST_LOG_TRIVIAL(error) << boost::format("The downloaded index %1% for vendor %2% is older than the active one. Ignoring the downloaded index.") % idx_path_temp % vendor.name; + continue; + } + Slic3r::rename_file(idx_path_temp, idx_path); + index = std::move(new_index); + if (cancel) + return; + } // See if a there's a new version to download - const auto recommended_it = new_index.recommended(); - if (recommended_it == new_index.end()) { + const auto recommended_it = index.recommended(); + if (recommended_it == index.end()) { BOOST_LOG_TRIVIAL(error) << boost::format("No recommended version for vendor: %1%, invalid index?") % vendor.name; continue; } From f56bb7d085e422ac6d1e5a6ae36370c3d6f97dc0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 20 Dec 2018 18:23:11 +0100 Subject: [PATCH 070/132] Fixed file wild cards when exporting a G-code or SLA output file. --- src/slic3r/GUI/GUI_App.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index bbba1d93b..97e78798f 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -43,7 +43,7 @@ namespace GUI { wxString file_wildcards(FileType file_type, const std::string &custom_extension) { - static const wxString defaults[FT_SIZE] = { + static const std::string defaults[FT_SIZE] = { /* FT_STL */ "STL files (*.stl)|*.stl;*.STL", /* FT_OBJ */ "OBJ files (*.obj)|*.obj;*.OBJ", /* FT_AMF */ "AMF files (*.amf)|*.zip.amf;*.amf;*.AMF;*.xml;*.XML", @@ -57,13 +57,17 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_PNGZIP */"Zipped PNG files (*.zip)|*.zip;*.ZIP", // This is lame, but that's what we use for SLA }; - wxString out = defaults[file_type]; + std::string out = defaults[file_type]; if (! custom_extension.empty()) { - // Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it. - out += ";*"; - out += from_u8(custom_extension); + // Find the custom extension in the template. + if (out.find(std::string("*") + custom_extension + ",") == std::string::npos && out.find(std::string("*") + custom_extension + ")") == std::string::npos) { + // The custom extension was not found in the template. + // Append the custom extension to the wildcards, so that the file dialog would not add the default extension to it. + boost::replace_first(out, ")|", std::string(", *") + custom_extension + ")|"); + out += std::string(";*") + custom_extension; + } } - return out; + return wxString::FromUTF8(out.c_str()); } static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); } From 56f473c2dca318f73a7913f04b74e3f55c30ba88 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Thu, 20 Dec 2018 18:48:49 +0100 Subject: [PATCH 071/132] Prinhost: Add a note explaining OS certificate store --- src/slic3r/GUI/Tab.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 5a212d4ee..a4796778a 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1601,8 +1601,9 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) optgroup->append_line(host_line); optgroup->append_single_option_line("printhost_apikey"); - if (Http::ca_file_supported()) { + const auto ca_file_hint = _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate.")); + if (Http::ca_file_supported()) { Line cafile_line = optgroup->create_single_option_line("printhost_cafile"); auto printhost_cafile_browse = [this, optgroup] (wxWindow* parent) { @@ -1625,19 +1626,31 @@ void TabPrinter::build_printhost(ConfigOptionsGroup *optgroup) cafile_line.append_widget(printhost_cafile_browse); optgroup->append_line(cafile_line); - auto printhost_cafile_hint = [this, optgroup] (wxWindow* parent) { - auto txt = new wxStaticText(parent, wxID_ANY, - _(L("HTTPS CA file is optional. It is only needed if you use HTTPS with a self-signed certificate."))); + Line cafile_hint { "", "" }; + cafile_hint.full_width = 1; + cafile_hint.widget = [this, ca_file_hint](wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, ca_file_hint); + auto sizer = new wxBoxSizer(wxHORIZONTAL); + sizer->Add(txt); + return sizer; + }; + optgroup->append_line(cafile_hint); + } else { + Line line { "", "" }; + line.full_width = 1; + + line.widget = [this, ca_file_hint] (wxWindow* parent) { + auto txt = new wxStaticText(parent, wxID_ANY, wxString::Format("%s\n\n\t%s", + _(L("HTTPS CA File:\n\ +\tOn this system, Slic3r uses HTTPS certificates from the system Certificate Store or Keychain.\n\ +\tTo use a custom CA file, please import your CA file into Certificate Store / Keychain.")), + ca_file_hint)); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(txt); return sizer; }; - Line cafile_hint { "", "" }; - cafile_hint.full_width = 1; - cafile_hint.widget = std::move(printhost_cafile_hint); - optgroup->append_line(cafile_hint); - + optgroup->append_line(line); } } From f799f2eb48fb43186c0d59e331225b13db309d30 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 20 Dec 2018 20:12:26 +0100 Subject: [PATCH 072/132] Delayed update of ObjectManipulation panel from the Canvas3D: Store the changes into temps and apply it to the wxWidgets on idle. --- src/slic3r/GUI/GUI_App.cpp | 3 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 209 +++++++++------------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 28 +-- 3 files changed, 106 insertions(+), 134 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 97e78798f..74ea52c64 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -181,6 +181,9 @@ bool GUI_App::OnInit() if (app_config->dirty()) app_config->save(); + + if (this->plater() != nullptr) + this->obj_manipul()->update_if_dirty(); }); // On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 8cc2362e8..7955326f5 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -47,6 +47,8 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->m_fill_empty_value = [this](const std::string& opt_key) { + this->update_if_dirty(); + std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); @@ -83,6 +85,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->m_set_focus = [this](const std::string& opt_key) { + this->update_if_dirty(); wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; @@ -179,22 +182,19 @@ bool ObjectManipulation::IsShown() void ObjectManipulation::UpdateAndShow(const bool show) { - if (show) + if (show) { update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); + update_if_dirty(); + } OG_Settings::UpdateAndShow(show); } -int ObjectManipulation::ol_selection() -{ - return wxGetApp().obj_list()->get_selected_obj_idx(); -} - void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& selection) { - wxString move_label = _(L("Position:")); - wxString rotate_label = _(L("Rotation:")); - wxString scale_label = _(L("Scale factors:")); + m_new_move_label_string = L("Position:"); + m_new_rotate_label_string = L("Rotation:"); + m_new_scale_label_string = L("Scale factors:"); #if ENABLE_MODELVOLUME_TRANSFORM if (selection.is_single_full_instance()) #else @@ -205,10 +205,10 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele { // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); - m_og->enable(); + m_new_position = volume->get_offset(); + m_new_rotation = volume->get_rotation(); + m_new_scale = volume->get_scaling_factor(); + m_new_enabled = true; } else reset_settings_value(); @@ -219,143 +219,104 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_instance_offset()); - update_rotation_value(volume->get_instance_rotation()); - update_scale_value(volume->get_instance_scaling_factor()); - update_size_value(volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size()); + m_new_position = volume->get_instance_offset(); + m_new_rotation = volume->get_instance_rotation(); + m_new_scale = volume->get_instance_scaling_factor(); + m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size(); #else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); + m_new_position = volume->get_offset(); + m_new_rotation = volume->get_rotation(); + m_new_scale = volume->get_scaling_factor(); #endif // ENABLE_MODELVOLUME_TRANSFORM - m_og->enable(); + m_new_enabled = true; } else if (selection.is_single_full_object()) { const BoundingBoxf3& box = selection.get_bounding_box(); - update_position_value(box.center()); - reset_rotation_value(); - reset_scale_value(); - update_size_value(box.size()); - rotate_label = _(L("Rotate:")); - scale_label = _(L("Scale:")); - m_og->enable(); + m_new_position = box.center(); + m_new_rotation = Vec3d::Zero(); + m_new_scale = Vec3d(1.0, 1.0, 1.0); + m_new_size = box.size(); + m_new_rotate_label_string = L("Rotate:"); + m_new_scale_label_string = L("Scale:"); + m_new_enabled = true; } else if (selection.is_single_modifier() || selection.is_single_volume()) { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_MODELVOLUME_TRANSFORM - update_position_value(volume->get_volume_offset()); - update_rotation_value(volume->get_volume_rotation()); - update_scale_value(volume->get_volume_scaling_factor()); - update_size_value(volume->bounding_box.size()); + m_new_position = volume->get_volume_offset(); + m_new_rotation = volume->get_volume_rotation(); + m_new_scale = volume->get_volume_scaling_factor(); + m_new_size = volume->bounding_box.size(); #else - update_position_value(volume->get_offset()); - update_rotation_value(volume->get_rotation()); - update_scale_value(volume->get_scaling_factor()); + m_new_position = volume->get_offset(); + m_new_rotation = volume->get_rotation(); + m_new_scale = volume->get_scaling_factor(); #endif // ENABLE_MODELVOLUME_TRANSFORM - m_og->enable(); + m_new_enabled = true; } else if (wxGetApp().obj_list()->multiple_selection()) { reset_settings_value(); - move_label = _(L("Translate:")); - rotate_label = _(L("Rotate:")); - scale_label = _(L("Scale:")); - update_size_value(selection.get_bounding_box().size()); - m_og->enable(); + m_new_move_label_string = L("Translate:"); + m_new_rotate_label_string = L("Rotate:"); + m_new_scale_label_string = L("Scale:"); + m_new_size = selection.get_bounding_box().size(); + m_new_enabled = true; } else reset_settings_value(); - m_move_Label->SetLabel(move_label); - m_rotate_Label->SetLabel(rotate_label); - m_scale_Label->SetLabel(scale_label); + m_dirty = true; +} + +void ObjectManipulation::update_if_dirty() +{ + if (! m_dirty) + return; + + m_move_Label->SetLabel(_(m_new_move_label_string)); + m_rotate_Label->SetLabel(_(m_new_rotate_label_string)); + m_scale_Label->SetLabel(_(m_new_scale_label_string)); + + m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); + m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); + m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); + cache_position = m_new_position; + + auto scale = m_new_scale * 100.0; + m_og->set_value("scale_x", double_to_string(scale(0), 2)); + m_og->set_value("scale_y", double_to_string(scale(1), 2)); + m_og->set_value("scale_z", double_to_string(scale(2), 2)); + cache_scale = scale; + + m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); + m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); + m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); + cache_size = m_new_size; + + m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); + m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); + m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); + cache_rotation = m_new_rotation; + + if (m_new_enabled) + m_og->enable(); + else + m_og->disable(); + + m_dirty = false; } void ObjectManipulation::reset_settings_value() { - reset_position_value(); - reset_rotation_value(); - reset_scale_value(); - m_og->disable(); -} - -wxString def_0 {"0"}; -wxString def_100 {"100"}; - -void ObjectManipulation::reset_position_value() -{ - m_og->set_value("position_x", def_0); - m_og->set_value("position_y", def_0); - m_og->set_value("position_z", def_0); - - cache_position = Vec3d::Zero(); -} - -void ObjectManipulation::reset_rotation_value() -{ - m_og->set_value("rotation_x", def_0); - m_og->set_value("rotation_y", def_0); - m_og->set_value("rotation_z", def_0); - - cache_rotation = Vec3d::Zero(); -} - -void ObjectManipulation::reset_scale_value() -{ - m_og->set_value("scale_x", def_100); - m_og->set_value("scale_y", def_100); - m_og->set_value("scale_z", def_100); - - cache_scale = Vec3d(100.0, 100.0, 100.0); -} - -void ObjectManipulation::reset_size_value() -{ - m_og->set_value("size_x", def_0); - m_og->set_value("size_y", def_0); - m_og->set_value("size_z", def_0); - - cache_size = Vec3d::Zero(); -} - -void ObjectManipulation::update_position_value(const Vec3d& position) -{ - m_og->set_value("position_x", double_to_string(position(0), 2)); - m_og->set_value("position_y", double_to_string(position(1), 2)); - m_og->set_value("position_z", double_to_string(position(2), 2)); - - cache_position = position; -} - -void ObjectManipulation::update_scale_value(const Vec3d& scaling_factor) -{ - auto scale = scaling_factor * 100.0; - m_og->set_value("scale_x", double_to_string(scale(0), 2)); - m_og->set_value("scale_y", double_to_string(scale(1), 2)); - m_og->set_value("scale_z", double_to_string(scale(2), 2)); - - cache_scale = scale; -} - -void ObjectManipulation::update_size_value(const Vec3d& size) -{ - m_og->set_value("size_x", double_to_string(size(0), 2)); - m_og->set_value("size_y", double_to_string(size(1), 2)); - m_og->set_value("size_z", double_to_string(size(2), 2)); - - cache_size = size; -} - -void ObjectManipulation::update_rotation_value(const Vec3d& rotation) -{ - m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(rotation(0)), 0), 2)); - m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(rotation(1)), 0), 2)); - m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(rotation(2)), 0), 2)); - - cache_rotation = rotation; + m_new_position = Vec3d::Zero(); + m_new_rotation = Vec3d::Zero(); + m_new_scale = Vec3d(1.0, 1.0, 1.0); + m_new_size = Vec3d::Zero(); + m_new_enabled = false; } void ObjectManipulation::change_position_value(const Vec3d& position) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 0ada37b96..cbac94058 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -23,6 +23,19 @@ class ObjectManipulation : public OG_Settings wxStaticText* m_scale_Label = nullptr; wxStaticText* m_rotate_Label = nullptr; + + // Needs to be updated from OnIdle? + bool m_dirty = false; + // Cached labels for the delayed update, not localized! + std::string m_new_move_label_string; + std::string m_new_rotate_label_string; + std::string m_new_scale_label_string; + Vec3d m_new_position; + Vec3d m_new_rotation; + Vec3d m_new_scale; + Vec3d m_new_size; + bool m_new_enabled; + public: ObjectManipulation(wxWindow* parent); ~ObjectManipulation() {} @@ -31,19 +44,14 @@ public: bool IsShown() override; void UpdateAndShow(const bool show) override; - int ol_selection(); + void update_settings_value(const GLCanvas3D::Selection& selection); - void update_settings_value(const GLCanvas3D::Selection& selection); + // Called from the App to update the UI if dirty. + void update_if_dirty(); + +private: void reset_settings_value(); - void reset_position_value(); - void reset_rotation_value(); - void reset_scale_value(); - void reset_size_value(); - // update position values displacements or "gizmos" - void update_position_value(const Vec3d& position); - // update scale values after scale unit changing or "gizmos" - void update_scale_value(const Vec3d& scaling_factor); // update size values after scale unit changing or "gizmos" void update_size_value(const Vec3d& size); // update rotation value after "gizmos" From cb0488df122938803917567df424cc844dcc1e33 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 20 Dec 2018 22:30:42 +0100 Subject: [PATCH 073/132] Tab key to switch between the 3D view / path preview. --- src/slic3r/GUI/Plater.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index db19180b4..11894adf8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1117,6 +1117,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #if ENABLE_REMOVE_TABS_FROM_PLATER view3D = new View3D(q, &model, config, &background_process); preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); + // Let the Tab key switch between the 3D view and the layer preview. + view3D->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->q->select_view_3D("Preview"); }); + preview->Bind(wxEVT_NAVIGATION_KEY, [this](wxNavigationKeyEvent &evt) { if (evt.IsFromTab()) this->q->select_view_3D("3D"); }); panels.push_back(view3D); panels.push_back(preview); From 71650a7ecf74f4f001b727efd53fe22cc969fd27 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 20 Dec 2018 22:50:30 +0100 Subject: [PATCH 074/132] Fixed the "Ctrl-R" hotkey for Reslice. --- src/slic3r/GUI/MainFrame.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 5b38129de..41339262b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -811,7 +811,7 @@ void MainFrame::on_value_changed(wxCommandEvent& event) void MainFrame::update_ui_from_settings() { bool bp_on = wxGetApp().app_config->get("background_processing") == "1"; - m_menu_item_reslice_now->Enable(bp_on); + m_menu_item_reslice_now->Enable(!bp_on); m_plater->sidebar().show_reslice(!bp_on); m_plater->sidebar().Layout(); if (m_plater) From b9ac016d89d5cc114cf0316709fdcbfac456494b Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 20 Dec 2018 16:29:24 +0100 Subject: [PATCH 075/132] Improve parameter validation for "something or %" settings --- src/slic3r/GUI/Field.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 0283b7c29..1bc4c7305 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -172,12 +172,14 @@ void Field::get_value_by_opt_type(wxString& str) show_error(m_parent, _(L("Input value contains incorrect symbol(s).\nUse, please, only digits"))); set_value(double_to_string(val), true); } - else if (val > 1) + else if (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max || + m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1) { + std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; const int nVal = int(val); - wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %dmm?\n" + wxString msg_text = wxString::Format(_(L("Do you mean %d%% instead of %d %s?\n" "Select YES if you want to change this value to %d%%, \n" - "or NO if you are sure that %dmm is a correct value.")), nVal, nVal, nVal, nVal); + "or NO if you are sure that %d %s is a correct value.")), nVal, nVal, sidetext, nVal, nVal, sidetext); auto dialog = new wxMessageDialog(m_parent, msg_text, _(L("Parameter validation")), wxICON_WARNING | wxYES | wxNO); if (dialog->ShowModal() == wxID_YES) { set_value(wxString::Format("%s%%", str), true); @@ -274,7 +276,6 @@ void TextCtrl::BUILD() { e.Skip(); temp->GetToolTip()->Enable(true); #endif // __WXGTK__ -// if (!is_defined_input_value()) if (is_defined_input_value(window, m_opt.type)) on_change_field(); else From 3182611ac2d00d847e6f2074f37e1fb2db1d02e9 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 21 Dec 2018 09:19:00 +0100 Subject: [PATCH 076/132] Fixed page updating according to view_mode (after preset changing) --- src/slic3r/GUI/GUI_App.cpp | 11 ++++++++--- src/slic3r/GUI/GUI_App.hpp | 1 + src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 20 +++++++++++++------- src/slic3r/GUI/Tab.hpp | 2 +- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 74ea52c64..6c24acd66 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -554,6 +554,13 @@ ConfigMenuIDs GUI_App::get_view_mode() mode == "simple" ? ConfigMenuModeSimple : ConfigMenuModeAdvanced; } +ConfigOptionMode GUI_App::get_opt_mode() { + const ConfigMenuIDs mode = wxGetApp().get_view_mode(); + + return mode == ConfigMenuModeSimple ? comSimple : + mode == ConfigMenuModeExpert ? comExpert : comAdvanced; +} + // Update view mode according to selected menu void GUI_App::update_mode() { @@ -568,10 +575,8 @@ void GUI_App::update_mode() sidebar().Layout(); - ConfigOptionMode opt_mode = mode == ConfigMenuModeSimple ? comSimple : - mode == ConfigMenuModeExpert ? comExpert : comAdvanced; for (auto tab : tabs_list) - tab->update_visibility(opt_mode); + tab->update_visibility(); plater()->update_object_menu(); } diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index bd64a3ac5..e388910d7 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -135,6 +135,7 @@ public: Tab* get_tab(Preset::Type type); ConfigMenuIDs get_view_mode(); + ConfigOptionMode get_opt_mode(); void update_mode(); void add_config_menu(wxMenuBar *menu); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 12929b2cc..6ebb1ef66 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -99,7 +99,7 @@ void KBShortcutsDialog::fill_shortcuts() main_shortcuts.reserve(25); main_shortcuts.push_back(Shortcut(ctrl+"O" ,L("Open project STL/OBJ/AMF/3MF with config, delete bed"))); - main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL//OBJ/AMF/3MF without config, keep bed"))); + main_shortcuts.push_back(Shortcut(ctrl+"I" ,L("Import STL/OBJ/AMF/3MF without config, keep bed"))); main_shortcuts.push_back(Shortcut(ctrl+"L" ,L("Load Config from .ini/amf/3mf/gcode"))); main_shortcuts.push_back(Shortcut(ctrl+"G" ,L("Export Gcode"))); main_shortcuts.push_back(Shortcut(ctrl+"S" ,L("Save project (3MF)"))); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a4796778a..ae17eed38 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -667,8 +667,9 @@ void Tab::reload_config() Thaw(); } -void Tab::update_visibility(ConfigOptionMode mode) +void Tab::update_visibility() { + const ConfigOptionMode mode = wxGetApp().get_opt_mode(); Freeze(); for (auto page : m_pages) @@ -1988,6 +1989,7 @@ PageShp TabPrinter::build_kinematics_page() def.type = coString; def.width = 150; def.gui_type = "legend"; + def.mode = comAdvanced; def.tooltip = L("Values in this column are for Full Power mode"); def.default_value = new ConfigOptionString{ L("Full Power") }; @@ -2345,12 +2347,15 @@ void Tab::load_current_preset() init_options_list(); update_changed_ui(); }); + update_page_tree_visibility(); } //Regerenerate content of the page tree. void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) { Freeze(); + update_visibility(); + // get label of the currently selected item const auto sel_item = m_treectrl->GetSelection(); const auto selected = sel_item ? m_treectrl->GetItemText(sel_item) : ""; @@ -2363,8 +2368,8 @@ void Tab::rebuild_page_tree(bool tree_sel_change_event /*= false*/) auto itemId = m_treectrl->AppendItem(rootItem, p->title(), p->iconID()); m_treectrl->SetItemTextColour(itemId, p->get_item_colour()); if (p->title() == selected) { - if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange - m_disable_tree_sel_changed_event = !tree_sel_change_event; +// if (!(p->title() == _(L("Machine limits")) || p->title() == _(L("Single extruder MM setup")))) // These Pages have to be updated inside OnTreeSelChange +// m_disable_tree_sel_changed_event = !tree_sel_change_event; m_treectrl->SelectItem(itemId); m_disable_tree_sel_changed_event = false; have_selection = 1; @@ -2924,14 +2929,15 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la auto extra_column = [](wxWindow* parent, const Line& line) { std::string bmp_name; - if (line.get_options().size() == 0) - bmp_name = "error.png"; + const std::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 diff --git a/resources/icons/view_toolbar.png b/resources/icons/view_toolbar.png index 6d842c5c30b8e804ad92b09762f0cf53659675a9..6c6179dd5ba7bec4485b42544098839b526cb362 100644 GIT binary patch literal 8615 zcmeHMdpwi<-@l}g5+ye!Bc1LNoAYs&RLW@zQDF@uVZ(+wZSE9xOG+UTosbkt&aoPC zi%5u^7IQvs<}h5g=UR9D4$pJHe$Vs!>-p!mmzV3>weR`!dA~pJ&*!>kV{IlOzFiyu z0ErXFkDLO4HPG!EKuiR>%#qucq02VXQAd(3!Gjcx4Zr~=?gUqy(g|OzC+-vu>mK4? zk23-Q;b<>AN0OtJrGXp4R}Cv@qek}igL(si5sK`Gb@RrNlw5J1UU;PPOa)C@$;%z7 z?4WIhu<|p-UGO>{8i2D6wYGB$^>)*DS4J5t8IcX30eo>JtPbNDzBeIxBbEPLl%ti6k|`kor=+c>qw1!vrLCl^ucoe}sim)fP)P%!t_Mfx!PPZY z)pZQC5C$3=NgVlmH)d&HeaCLoseKq-bDA(f$({<#HTKP#)h4U8xL zI4J1I;AE^HTwM(T_w^O*>w9Y==@jno%lOOIM7t0_9Q+iHNC*sYgY4n4>t|=^?Edvc zf`-s)4A22ykWsKcM+k0#zBoMT#1W)2^oyFim%D+RIu@&=>8h^k?xwD*s->f?tLlo? za8pI-t82OG=xcf4ToFIV`K$g~2<^j&W5-O6nd&3d)sO1wA&zL79MRD+(bqq!epo~E zXWtWeA_<9#(pI%^dfj}YK7tGI3c|S`3n2I^eLrafufJG@t2RPYSI^B|Ro`73p{gbLr>EyQ{mls-`YhOBJi3rK76n zu7^{_BD8T@I<6YJ>iRfkIJ88#zL{!)BP0rKcZ|O%F-!c=^n$+-vJr60j~c0+w?R zAND}4w_U<{gmc0=R4zBSsk%RngUq(R|ICoe3qO!EK0ZDWZ=d6SE^&}544lxe!3Y6j z0B{JR1&C;*03Z^&TMGb6&_xLNpFbV2!%PcvF!#GXN18JfWhe(XwkCjWJF=XyT{kmy zVmb^?W_FYh@aXdL@-cZ%Wg3-a_JAdkNc5mzUN7P6>#I-^H1z@BQ>tDOIzR5@?LE=q z&=tNfVo9DhuXxeQxlH8*K^Krc(@%&;clc&9wqvmTArXk20 z@n>gtcXt~G&Gg-uXuWl&&?9bxtp~_uOZXR?0z0<7RdTd%qWyN(i6{J0Sc}&shZ2h% zf6nsn+L}JYJxU3-IiCObBr|iAtRW;%GW`l4~A(Pzm3r;+LT#CcGJl_}ZVWameITHXzs7K-+G3h>5P{ zngDYgamC#9BUYPBO~m*wI^-{FoaXY+I`(roZL7g6Q}E}7g_`r9E#L}q zwp`|oou<)G%0=JZMjMwQCL{UIn5YMBoA@c7>1QyV{dFPR3tTO^v${O@c!8Z)i%dov z#HsrBDq?wU3ROb{uom(ll#WBKAuDZZYHK(Z^0#Bby$Y#<(CuW(RBE z6;zjiaSX_p?A)SQes^ITTD^

9QLxUD+Vp_qnIXNPChcx#`Q_#vG71+$r})jzZaz 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 literal 11913 zcmc(FcUV)|)_xENP^35xpdv*bVH9c7YXnq81so9&5JH4dgd}tV0ph5LiinDch#{b( zC|!CF9qGM8BE2S|7y@bMJLt^Z`#j(LzUTh#{p0cn=j5ET_g;JLwbp*uyY_?27N$bG z5AKFQAVOy6&s>2(c7V4#AOd{g zs@lpb+RCbGimIBr>MFXYPRai9cLW^G&(%%$%9(S2j0OHOIN}iy;G?Uo92^|16s)F% z_H$QO)zQ&WRyn17>Xah*g`$55DgcgAMEM{6vxhSXe-}Sbp8!uZN_M+P_ziSmfWZ+^ z((hA1`k0&l)iKKd4@H40Q^vr3lvR~fl#$5od42oZKi~@DA7cFN*Zx;Sd=Sc45dP>u zKNoO4+>ZX)7}VYWJ<#@tpftLcexBf>z`f6)T>_B^RDjtTgCpP@C09>ZT^Ch2TvP3a zs-mlls+OXgt**XU?88Hae%OdRE)$ ztj;+db)z#TCPwEpRMq}!YliXT@yb9JOJ%?6^-`((+Vzo zpaanU9%vs~6O-*#*OC3j9PZ+Y+J1O^d!fG1`wYU*GYH{&&JT^0{ibPM&%cqv1+J!c zN>yE5QAJx_Q&C;j4X$`Y1H5xny`ioJhij>6y67DFqrK}tY4E$Hf=|Gi`1~o5?hS2K z7uOrE8j5OKaCJraDX=4LS8aqMTtx$+u6g5>mZ}coh%zXm^0x8(lQI4I2(-}c+rL}` z{PCA_AyA-4`GKy3G7;*AKu#o@oiVzK8Cf3Ze|dGL61tL@_4S1H?+VT}KgRqZdH>@N4d8Sp>w^Wa~SXE7BPqSC(&N(_{-s&U9XtMTcl072Ami^C%>fud2&@poChKR zf!qRPz~5f_@D+#R1g>W3OCu=U~1BKGHkzpx_{W{-W?&wqVgUM(Sa`b(X zj925M1Nmjavse6EdM1Bpj!jDkOxo@eql!v9Bvs-$f}; z)ldcsGJfryA94D8hhGF0JckdK9?gLfNf#5895)z5fKzu#h=40jNs&c&r5N$nsS~e< zL$fTDK6Y4nKH?B7`A*Zd*v!M-SV)myL#)C~e5#|kJ<3C?fk)o^(u@9;sYCnZirsCw ztEq~@l$T{rC0P!|9V`kOhy8qi?@_+USN_Qn%!O6=TMJ$Ad<*(2j$IuUuPP-cl4(DNWp#I{{xa@_?GfQy!1XXKsRRTV$kN z(z>_@xnU^zS=I01F!==rsSfG7nqK=T!T!SNr zGm!KSJ=*KAsTeJ;SKOBu4yce!G`Wh1*YTgd9h$pp831HJhv_Cq7?Aq-QCSb8`R|iG!*?sU zr=c@hc@Kqb;`PGuxU!(aOzlBZ^`S#@!I4D{9&yHc^7@UNWlm4pUum=+P;Awj@aCypD_f!7OuX@K2$-Ktc+{2)p?)WgkS|K`4NV9_#& z^JEAjYSUs;JKeE-VGHFkH_&+DfOH!2Wteh-@Nh7sII79cYEV$Mf<@)#6FA=EDf(Um znK}uuh>)y3EnuhKej}b~Lf*r9h$AqTHi}FmXm#?oh)F}!fZuaAXSTczwdjkHKrs3~iJI0g~4vqxZ=^M6IIoyZ!m+F_Ve&}!^ijf1N zHX`tf1r-%v+qKB-!u$^1rTdtbaR+oI-Ds1QXci}@n6p^bL`PONMc@VbEgbXQRxhgA zbUYAT8!@5?8@|Ez@}@^j+k8xH9cW{&whD3?Tb(u?S#?5yCIdG#kG2M)Se0UYw{+ybjGlsQ9h-V1uofOKyMQ|pc^nB;dKYfU5!=7`*-N%C_0t}Ad!Yt z_ltvJ+>L|`If<;}j^p~!G})cEg2G3qgNYL>8}gOvZ#O%{GA<6HVwp=WK$MZQ^ZuO| z`f?&tf*6oTf=K5L8s>H^a33jH<(L1n_sJH*4y}my(5x}Q;0mIA-(WQ1C|7oDF7<-u z`8)ZYa}-b{QrLRCT3NtCkLS3tpoPxN^O{fA3~ob1L&hsjBr}>0BxJT{+F!d~hT#Cn zr0D{sHghdI0Ou)Z9m=|U48T%0&&FwL5PO5s!|R`=&Gig)LmDzEU+q7}aYZCI%{OpE zo9IE%k0+O+&hXzV$qV1#RGSr+`DV8Qj+|@s(iU|(mj5U7=J)LRH!Xa?FV`788`m#` zfltl7gWVh_kNc#-5aDqS+^PY(pzURWYm@zwFPb}$>q$g5K3n3a>Gy(@fJj8{@-WzX zp36uuCwJt;AEW`fz64v;9d0}~$9qa9a>?lmk@*{CALPwkZ8dg6)6h*nq=`Pm4=mp@ z)Vh1Cgl9;t0HZQ4MMuKTUu*leIg3x4 z3);<-I^E+dNXm@LNjSI!tVsgPeBr45I~N1NK)FgcdBa<+DJc1*Gs8E(Foy1BxNkPw z{z^a6cp|aq9P?OOZfcGV5H*U}Ub?+M+(JHWcAX54ocis2>5Nhp^loIg*?gt~16|HK zP+olKnFT;bYgg#I%+#w9@LHO?=uyGLMYmaQeSt-WlS4#h~hg7Pek`!mWgg>F@ z>=7>6Eh-*hL@feRm`@E}QAdi*EJigWb+QJFIpyjL#$Y9{M z*yr{`Y9VBUOG?A_){IDTBGn)6;`9LwYBhgK|n?H^B{#i+UQY2 zVQ*M4PII=*vnu{C)m%i#lY{HV24%LrC=;yusC=3+-T&c0_WfQSieQ=6 z+F*Yf=(eRi-T7P-?7@}^-NW}_0n}Jkgoo(OC9hjKQR1Dy0+{`qQ@fBP0VJ(U7mb=O3fh&P|ex-FH5%*MaS#HJq|nCZmII*!{g&`(}3)@)Q(WGX8OnI=mQdDUp>t zoDmq2Aiky3K1HXsDY(+? z5$AH9+J&iuN<_;FRC9XIvBAf%ujjT#gKxfYSZO3uF-ENSrAt+ZMJ@UDH(+H>m`w}w z;15Bjv(vw$sdkKYD5V1Q)i^E+cNGFu2MHPBTM zz}|tXZ$NTC#)l!V=w@J$?|+{}?ApAL%C#jjQ^nQq$3jjewyDB@#3`^xy4>EiSp3{` zxS+|HMQ9|MNx;H<$q)9)D>1hur(lL47HDM_x6-^F2siiW6ta1xxOXd0h`#I6~@_^$~zW%lma}eVzm*6*AH29 z=jGdj`7(3BKN+BOO9|6P<%sLS(u8&I9hzK8CXqww9iRj3UkwK7fN=07;(5*@f02dB zrhqK>!+CaS=YwIi_|F&MaCk?F?-WSZQywnFAzOUDiynpDK* z1XlTaNv^%Tybit33P-SKHp=B-*W0x$7e|ZH#hVPp;<2?cJz_Z=&Kpwc{VSlXO|&g! zLdQ^f%zlwRGDOre&v#yYtjbkwu)#mfvXfg`arop1%rm?JV=Uh^5KZ)(X}J(^V9oQd6OTZT&j_r}(%;W&w$WScxqt30dL3RGRF#!^Ay`gp!c&Ep?R z7j6ffh$m&Z80Cb1ch7s}-Hh*>jxJ>RCBBhXf>Xm=hzRv+rdG<>Cp1<$J4npd$6QUv zn7hq8H@ft!*W89j{NNFuoU9loE=upcd^%`7n_$Am^M$kgwM=ER z^D*wOCmY2cpUL(ra8rb-X<5i{0~$(1#Ghxfvi+M!F3l!-o~2b7W?)b|oE#*XP;d4i z9rsf1{a+oGbxorCXt0Sj)x!c|zfR<7#J9bzNoH{eforEwNIB~=FBUsSIJAr%ABnEi z%5eolld;M#NN+ki8>lG!xl8#;(@+>0yE0~LyB1coR6C9XI1aG~)Mh_af;r*oGC6S% zvDprMd5Ue0lgq=04=)r4I&CLO4#440JkFA~Y&p1}r-$uboBbnoso}p0iu9`&(C!VK zWZ{TiL3@y)j!Jz4Sq^iYSuNs$fXN5)(yx<}lC$I@(Qb-tRveB8+PmED z2HkK}?vmlmo9e-Q8^oFlyaux=kH9n&7O^#B$(<0veOQfwVID3f$nb zw=Ob<-3dH_j<}xhu;s@C;080SS8g1kR&X{IFF7&}pHXgiB3ixNvkU3$;h3jD?ddWU zL>haz)FCBorhGxpV$@hqw;&`HFEtXL>S&c9dFzgdVF^Vlcudv%OZ9K%nb%A6HaAAm zxV1OYN}=vx{=2lHNA3TV+kd~RJ(P~N?fU2#_xeR-=>q$WMxr%{icWKy-& zj+TDDZ+q8A;{ylmlr0E`l_ihKtJWg`{4GH^YXpq0<=>ponfVP+{s?aU7Z_P8TY(-1 z`Q;On)T-jvV{~9Q^0LROV|9M2!9n{msm&0Nv#Eu9ve03?QX^&`^X^1i2rxJ%FOy-Y zna6|U`|S^%nQEM<@EN}gf}jM)AUk5$y-3k6IqE$TT0Mc;3ge#g8khb}Dye`)ersA z_nGUG;(J|a+REsN2qIY#lpXR>KE&BqUO|=fZ=xUH?0s*()M?1_o^6nTv8Rv5>aQUj z2Z^O%F1DUU1$@YX92vgbp?`24pCM1A#-<+PE_nzVOCA_Ow^FFF1YWLA!R)YSn5hb4 zU;e}Lin)6mp&rCN3_X?dH-v*)yC;B&k9oYIlX{OZ&fN>C2K#5N(|n=Sdqhgn=;L8z z@i2jIM5!UsL-X!kdP zyN0H{Yg%N2I~!`2Te`S=N2=hFhpgc%WQ9wU)bg2BsZYo5;7ZV~UD?efn)}DR97cg4 zrMc_EQs@kkxtPI!DOo?H4`dmIck!++$64quP*Y8!46P$pphdy?9SPLI*?N`D1ole9 zmWX%r7xKG{3Adq~1N}@B!FPRxFU-SFv7j>zL#;OolaE>Tq`1zI0YU0h8kVGECmOXer0*&~hEZ{T_=2f}={!g?zLI)tJMp@)^_@xkq0f&hC(ULl>{PkjCQb>DpV+hCv= z$0~knUwE^BZ92}fBY;LSFl?;H+s_v2_$JJnY~z@fw_IJ;MA-*o8Mq9f`f)?|(f&YG z$Uw*+WG9G*cTLR&gAA)wjU?lz`WzD5Rn3h3x+_SC^4KRSEnnt`LpCvUxiAlY>mh~J zd>;VQ)un9AI_Hsg7eJ5q74zP_7$qzAow8#mW2vQDo%-u-5(-#(ZvLH7 z%4n(`Dk{M}Gt8^4{$1QQ@5_xkCpMq=C@85 zT7S5n1U1hxt#+I^p3@+i#0)yEh}tYJ1J{FijtQ+;AMFC*DkXtdrct1uoNlM_V2+pj zo%1r$Q!FwM8<(rn+mAoNy6$ts8k-@A)6A^=ePD>~!pz~odcCA^SyqOS3G}tJqY8N?Smt+^d zI`o4w9;CJPz#S!!JlxZ?C)SDy26TY4oZlG{vavjTY&8!E^Iy$(xkvSx-NF_g{Aouu zp?J#cEf|?K`w9mDcpXe0%$n-IrG~yUv4pKky9WKqlmL(PWd+^f#?GB}d|#kNlckT$ z++V#6~m5OjPYPHD^1v=`)wfHV#!x6 zn*3OPy>#-S!aeVWsYoAH&TT;@%wnQ@a-uDF4deVJo>iQvkrG~`H105~VF&bAQ=BuH z(B!sb{@CsW?4WP!+1>m|Lb^ zSXMMkyb&lTeK-E*iv*?2w4AqR{(}1T@kGz<7UjDV`kNw4z-SxjWO0I6G)q=juB5u$ zj`vc_SX@~8rTZ@{Sv|j{lz8dAnW8~w)$oElD5cWz`BBuC1)1ZcYZKS&|&Q~V&e@TS-pWqwg`3LcZb&z zxhbv|;QWr2{RRFbmkWw4`u5IALiur@43+8=+<&r+?nQ5SYNlw;;w}%$@m^5-*am~z zsb(ctJ^pcf=LK`IO^AwKL9bfkr5NcC`(}$R)0edx2xe)$>q}F{6~G;YRYLr8C7A-3 zxJq5E&a7@;y77nkp10N{3mN4TiTZ)7i*G`ix+C9keP%b9hk@P>g0t|d?acKpnCH#D z>_G2Uj4-Dx9z4XJ5{Vpp!eQKNAInC(T#n-uyJD5=pA{yy?X!w*l$d=lh}Ny(q-qT4 zq%t!^Jg`^UtYouRr<~=W)r6fa`jSNF-X|wcoUkvi1SySK`voxiqeZ_n8fIi((FWM^ zs~<;>!6WrE@6B%2b@4@QcnBPDQLx(rj$JZ*h<;&$Ju-%}aXk4s!^v)QcAG#o-S7_j z!U#eRzm=N}se@A(-&iL8^^1m~D|ZmRT7gMkIh8?-SVQJK`!`;G>KX}43tt0kNdx^) zd$u#M60ZuNPSz^tAH2;&iOyeTaCLfBlK0bgj=M;bIWiJ*T2bOe7>*>DP1Y#bO#C8~ zA6VbSwQ*GZ{O;I0uk6skiKDCeOoB`gRnL^+J>csw8$2;+&tQ>Iim^^fX7S04;w?*? ziin|HxRfDxbBYE`%R_ZkxwpU22VuZxCoF~Dw9RLwXe|ON0^U|&eM0NQ_mb7`G@kPa zTs9-KYjUf)DN^j~hnpmgn=9S6ZF%-Ql7zN4*v#?3~VUmRIF8gLrkim zgn=d?4%xJ!<4VXN$9dg`_D7~71<5=7rA9?Z!#e236ZhJdP+Jej6Qv<+)hC<%sU5z9 zj+*^V>+@u%iPDBGgO@@)s|rTGgI^RigA{0u5Q4?u-TZQ@tn=8SZkXUH)SGuG_V;xxWHYLJY@L>kOcd z5+sz>!|fE_LErXNkS#iFhMr_pYE|qNp$ztKY2=!dbmE2;E=k{4@JwH>ehuuRba#;i zDC?Ln&L!shb%Pxr^WKJ8?xuWR9w!J;nub}p$}w-?rxAw9=cuZTc5|%6SA(Aa;jKc5(6+_J(A`*2X>D;-zBPx;-SE`t0j$Tba~@>N?7WpV4so?D z!MIgy4v#gYTC*?tnBSa&9eorl0qtcWFl}>rj961Ks4e{H3T8v;dvU&pEU>&b!qI8)9;z*xS zFew>11nDP#S>oq$2J7@TA#&M^C#BZ; z@-|T4vSs3Gwkp#>iHTW@z=$*`aSAAL%6m>UO+NqPBT&ZBG#!d)7LTit21G?%# zOm!%&N$6sr@o7iCTkQ*dAFXP@a}WcSaZoUn=#hQugzCUjWMyw#q8f25WDZaBOemHk z-VnHkMhWncM~Xyq9d}e0eZ}qH2@bosXGfq8B%N5(FB0Qq;b@52xKG;5W|XNQB}b$t zXu-y#%3zsR?`QA*19!!7JLg_saLdPfDBoiRd3XpyrMqF*uZK18is-BS?%tVf&fO>` z3gJM$+H?bi%1dv-K)YzjTK0#PR0RyIX=G$a1WxQrdS%+=Rw^Zt=4+-Ya@Dh7=iU3p?m4@VT}1 zqJ+4(_;S%G%76qCKr+(4^?O<2;OJIhcKTVhNe0IjT%T=ssN@z!QTqxU5bK`Rhf~h6 zh&;kOwoCGt)6$s&vdLIhw0#(RY`I0Up_8e!TY+;lz5=X39}86|Xy;6l+@QikJ+Y97 zw;Q4*21gReB*vA?3`j{38GnEe>HyDpfDnqhQOialKNDrsWrXdjPqb;$# znvA`tD;I!e+h!0_#Z!by4;{ZOTB0~l<{>@~9#P&=bE4!KL-7$I_zbLcSn-P7KAc3@ z^1!`Yin|~ndoiZEiVs0b5H&Fz`XJPhG3F106?k;NY9HT7$u=M1P=_9;4f+UtI0@-1 z-_Dc@SvadZ)#AdCZzm4Mz%2aG5+1pW$KglFVurl{PtqO;y$~oyhz>pDIsL8PC7YIN zFp&#?bx1BMO_S)AS0lauTKu59m@=Ch=TC##U$I{>-6ynFU7db6CbW=6RE?0!{G+bo zuz!cs?!ze#w@s=Sc=diNxC!!(HL@#L_U|nF2xbOHcR|FAtJ~VgP9}%BdLI?xxu~^o zhc27$5M#LW>M#%!CVCaO<6G_4@31Ua`}1svASq2X>ek-ah^cYHepk{U^g@sYPwHlZQ(3_+lKXo=h=yZypx`?KG9Qj7)e^E&D0 z57|xR>BaS=E}2wk&Um@yD79ynE{(s7b!4vHE{?l`D>ZTkx5QTJRnWe7Pa&RlKJFLo z+<8s#oSwVxqsokHNCp`|8c76Z;v|tuZOZA4+|c({C6(5 zz^4DJzkl)Jzd7W#sQ>Pef3*AWjQsED_m6e?7d85~YWWW)3rhT78u@Q$@~=JoFR2+6 z?!Pqhf3U#+nnwTD2L6M|{)Zm?AB_A@Gf|fVV7GW6Q~ZVV$M`PAL%=_?vleHvjh%1* E4{q)*q5uE@ From c9748bd69a748951dc98e8bb94c34366c3ea4bcc Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 21 Dec 2018 15:29:59 +0100 Subject: [PATCH 100/132] New icons for gizmo overlay --- resources/icons/overlay/cut_hover.png | Bin 1777 -> 1489 bytes resources/icons/overlay/cut_off.png | Bin 1773 -> 1402 bytes resources/icons/overlay/cut_on.png | Bin 2673 -> 1626 bytes resources/icons/overlay/layflat_hover.png | Bin 2071 -> 2071 bytes resources/icons/overlay/layflat_off.png | Bin 2018 -> 2018 bytes resources/icons/overlay/layflat_on.png | Bin 2599 -> 2599 bytes resources/icons/overlay/move_hover.png | Bin 2022 -> 1903 bytes resources/icons/overlay/move_off.png | Bin 1909 -> 1750 bytes resources/icons/overlay/move_on.png | Bin 2410 -> 2193 bytes resources/icons/overlay/rotate_hover.png | Bin 2361 -> 2361 bytes resources/icons/overlay/rotate_off.png | Bin 2132 -> 2132 bytes resources/icons/overlay/rotate_on.png | Bin 3307 -> 3307 bytes resources/icons/overlay/scale_hover.png | Bin 1837 -> 1837 bytes resources/icons/overlay/scale_off.png | Bin 1776 -> 1776 bytes resources/icons/overlay/scale_on.png | Bin 2243 -> 2243 bytes .../overlay/sla_support_points_hover.png | Bin 2057 -> 2057 bytes .../icons/overlay/sla_support_points_off.png | Bin 1947 -> 1947 bytes .../icons/overlay/sla_support_points_on.png | Bin 2869 -> 2869 bytes 18 files changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/icons/overlay/cut_hover.png b/resources/icons/overlay/cut_hover.png index 5efdefc5e88343099779b989b1ff242e041ec20c..b026667c2877b5d750b42131770c63ff15ac7705 100644 GIT binary patch delta 636 zcmey!dy#uX9Fu~vfr+uJld-v*i>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IA~T$d zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<eFwg@4h)#)ls5tX4tlE zKHbs`Fz_SKQJ`j`j(Tjp_rm8!hR>27^~a~Lot*GRu~++4ugB|o|5vE(TlGlaVYD^skBAD;72JwJJNz6eyh`(on8>n-0cwD`=dlYM^Ecc*LmF7B`XR)3R=+=G_qR+)BsC!abuW73@!#>eOV3ZMH= zbtmu2yJmO2A4`2s`OW^st1Ntlm+&4&6~}uF41a#9tO!3*Bm8;dktd7idb9s(Io$S) zq1QOF+}rB&AB~){8TFz^_usxSdBL%_r}wU?z4p|p z+lJo_^WQ|CTD@x4w;JD{XP&EOZ2D#CYMJv-t6GP*lJ9}rtiV6hOYgEzU`E#VVgFm_ b21W*fBfD=f%}%ocNqM^ZxvXab!7g# delta 926 zcmV;P17ZBp3-JxGUIHLSGch$VH#kN{LP0|@F-15*ML9+}G(ki|LqSG0F*7ieegYgI zMl&%rFgG|wMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu=H8nUhW-T#hHZUzT zVKOx>IXN~kEiz_gGG$?AVPa-AIg>;Kkbe~`F}nZ&0|7}yK~#9!?3+Dk6hRcnvv&y> zKL{kb3xr5W69|$bLW(p&u`v*YGyx?b;lL_|c#1ZHq_I#@ENm<+1W~ZD5u$|}0%9R( z5yUcuf)GCsJ?}_v&)>i+PSN8w`*E|kZ{f#I-oBZ6`_Jxt?VMAq)jE=Vq&gCQsDA(| zfC``jB>OPk;Qt7*P6w9Yt64PL@n6aHi3Yw|3{Rsix3!Xv1_c}RQ6 zo0dEkDPu5J0+4qBKEo>*_fgKclz(B$TCiRK@(jR3*n$ht8NlP)DP?U**#lMzfd4#f z!W)NO1i(HEAK)EKSumHGl2}Ykv!Mwfee*4F;eQHrTQs5T zmY7_LF&%~gdH4>~7SC*_CB}TS0(e;uFaw%Z$8pwd6+mKJl~@B`5HY@P43;=tFZZQO zVk$tw4=(S^Q8-KO|pO!|hbJ_)Gvo|+>@b?kY$K~>UY%ADylfDLKX#53R zLGqs9bXa_EV1JEmAL(TriwgaS3N&ctU?uf6c%S?RAP{3Zo zHbeTD_};_z7~5XbSK$|&)?Pqg0sq}#Y#YHq#sP8a28s2LZZN2U(0@^Q0eQ;>xGk{; zE`VvdizvHc`TJg!SamL-0;mAN3gE^>Fi$I2UT$a|4%)sbYGRaFn;#|VvbcU2@%(A5 z*TPR@y*ql!_2a1j&*S|q{5;<8yPeYw62Wef$wjb5Cbxq!bd!{Blhciox>Z&;OY3%d z8w99(8m#!igrh&4)FTxk6+i`00aSqG9=`<`0CCH?@;@KpivR!s07*qoM6N<$g3T+A A8UO$Q diff --git a/resources/icons/overlay/cut_off.png b/resources/icons/overlay/cut_off.png index 3e442101c0971058d03cda7ffbbc309855c5cfa0..c01f205546182d64c9f4d75d2879014192781c21 100644 GIT binary patch delta 548 zcmaFM`-^Ks9Fu~vfr+7`fupONi>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IA{U&B zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<F!Z)WZHI^rPEF1#^_d*?5$gKmy%j$9HK_{yvEH~1%;-GRobNiU@}elRV% z5z$$&=tSggj_VnU9)Eb(2wdLL&3R^K{)6VnX4B8qO`rXSZ@Rea#I2UcHcew^5@=vx zQD9)?=;*RE6lg7GQ@p<7gm73>ds1Be8|(i;y8HLt=(Vh{d3a>AZ+`G&Xy!NE)#O&l2$ z8?p^W&M%6tx_`@;Kk-b3=mdKI;Vst09CKr A3;+NC delta 922 zcmV;L17-aB3hfQBUIHLSGch$VH#kB@LP0|@F-15*ML9+}G(ki|LqSG0F*7ieegYgI zMl&%rFgG|sMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu=H8nUhW-T#hHZUzT zVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KkbhNmRPg`+0{uxuK~#9!?3+7i6j2n0GrJo$ zJ_zKofe;BcfgmX&q(~DK8v{W|6Ho#Y7OX79P0&V=G!`0^v?(kEQLwQQqJ_Rh{3_?VkB=iYnf&&++zU8i2Jx25^Ww59w|0e@5g z6+i_@_hGuhe-UAw4xERtf=N3~xyQH#pxi-t23s&MY>92KWd)G``Z*}WIt+QP4T-HR zK2A#sKv@Iu1S(L1%wK#ma;+l111%u{<;=l1cnQM|_z#P($upN&0m$DEkDv;dA?qV= zR`OJ(jQ&IkK;BvS46k4$Ksh5)hJPt*)_MWR(+3Y>8?HbugvU20Wo=8@eO3y9{|s!x z8yJnCD;t%vO&v4V2!LNNJb)cog!TvrH0@Hyj?~%PQ~~grhEK2oV=<0i$D~eE_jK$6 zaIG8eLJh7#z6nG4ywqKjSh`~o0Q(etgm*A*!CYosVlgpIMJ9ms%`4!-4S(pcXhPQ^ zF}V_BHVOfX@Es;Bp4m=FjKxL;@UtFZ1~hAqYv4*}NVtm~MEOEGg?n{@% zRDhHpekLn5=%nR5Z23KPO011FoV$(qXjQ#k$#G7rkhffbDohglb8j~oSO6*L0H=cG z0&EiZ&p*cjp$L%FW4C+E{eLIpEl&Gj0Wp5?_es)cRW8r#rG1YBl7NE#r^@?QPLMA|5I#t#MjhiCO#D?VK+%gfu^lA61i#>-_5%6}1n&kD+X#j-4oFfrXxapCHyGAH=oGwwqJQNA+?H5F7r?aK zWt4qy`TJg$SamL-0;mAt3g9M0FkdTIer{+3j@iB_YGRaF8y_XZ&;OY3%d8wBt? w4OaYM!qFd2>WYvGpdSLL04hLwkKY0e0Ciuxc*Nv44*&oF07*qoM6N<$g8bHsng9R* diff --git a/resources/icons/overlay/cut_on.png b/resources/icons/overlay/cut_on.png index c1f7bf62dd3f462dd00113a6e0a371c179c776e3..fac6ecb45d35f5a15b466379398ba9f3ca973873 100644 GIT binary patch delta 774 zcmew;a*Jm}9Fu~vfr+uJld+SVi>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB1fEx zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<F!Z*Dnu2n9;8f0%hQc4ltqiHPG%8JRn8bSJ!ZA0=FaQ07 z_x;}a=kxE`v@g?mC1-l@V1Te30|P@n5Hm3N6!^@}mEdcCe)?(AnWsgvs+CS%i)y-$ zKYo2RtMs-{^{qF40U}3F$F08}t059MnP-CE^7AJ*-4SMaxO1s?LuzF7>Z`A&crEo; zVRl)3vF~nPdLT2{Tm%upWN_)w%aU`G>=bKzensfo^REceivC$s|8Lo$l zdFHH5nG;W@nEAvz%RZRDFhJu87h7}Qq;QA5aqq8YnLgdmuz-Wy)2_4Gc! z$y9#-=g!-Cl~>}7KfO4)^YM4*SYBJZIwm z%+>FcroQu5KCi!fT9}yZ^R2tK|5yFSzW873S$+On_HC=?{eGT%PW`&gYWK7kk!%0Y zyY}CC4TFf|8U}_xpZz8zN@&eKtM}_~UEdQ<)7fY9^rrt#C^OHIS`eY5r#kth_a>cs zwfahJnNF8;MKa%&rh1v`uyU|4?NRZ3&+1s=_K8ct^<$S^*k8Y_N?u^(>|XMJ8NZd= z9acoBf>?9d|Cok^%KoUa^H-W^;rOZLi{UGMuUU01%Y5c>`u$nGX$2#)28)=D>IWF! YEjn?ZU-PU112<64)78&qol`;+0IY^d!~g&Q delta 1829 zcmV+=2io}B4Dl4OUIHLSGch$VH#sv#LP0|@F-15*ML9+}G(ki|LqSG0F*7ieegYgI zMl&%rFgH0eMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu=H8nUhW-T#hHZUzT zVKOx>IXN~kEiz_gGG$?AVPa-AIg>;Kkbh$E59$B_27yULK~#9!>{?q)Q&|-43xp!Y zQR{;kEl?sD%PWBhD1r}|DF|XjY9T1#h}w_&5hu>izz-%GJ~XKXtOT5qw0<}s(BUz@ zKnsC5An5QarCKL|9R;dTD5cEa&JUSMCPmtEi+3l@y}jq0eb(7$owN5o_fjboN`D^Z z3lSwb2{jlD-{TMO@!4_+)!uPHXlSU%?c28xb2ywW zaD9N!($doQX*8NQaII`_Z=YC^48R$_di82Zb93`KI-O37i;I(FW@fhF5AX3AmSG)x zvcWD42o4T*uc)Xv%;j>o-MV$_et%9*POHgenqOFl!C+8}i;H(*tb$xUg1{4$&@LF42&wsI4tkp?L zNhLt*rPTj7wRND17Nn@WHQ+y9*^fMm&+e#Wo12?o}M-@MW>643$3)Y zba!fM>fY||?g4>7Pz|h@Bpd_s^73{(d-m)co6TNx^ytwWK-;FJ4ju({pf1#jy0H)J zi!coE_4Q@Ktj7lh1^M5-dw;j-#EBD4Q&UrBThNusWYWsZ%cG7QITF>`+1UeZs2vy> zm|8XlT)cSE4`;;F)02Jt_;G1UO3EW!4ITw`V;|TT_KAHX2D>xB+uPd}cJ^<=!oqe| zRaHHOS%0M0>*wql!NKN82R1~*Mj6@G*7gFH$**d)+F)}AoIiiw7k>hjvtC|ap0HDw zCMPG~xA)*tuutq8F(4MigxGAx0QAib4GjrVQBffZg+d8&`@@Ne2@?@AIu8#I2ILhn z5JW{ZH8uSn8yj0=soSN)FWQodmoHxqhRr7&!q}g3b8|apW@gM3f(T-PC8^eGwL^e! zKP-<)n5+@2E&+dk|9=gAeSNRMC%-0YZjaayBVx7s9sn5{bsmK12ONkIv63f3qyb0+ zSR@QfE1E$tPY(|dUqj>pAx6Y%bpupZR`!d<;xMIB`5loBU^1EJ3JVJ#+awf*Y|M$v z<+>5s04kMgQm50+F8hhlir53>hc?EoqEnr|ZO}F7rsj+}fqyKYL?5?LCo|Q9lP`*% z>s4!AXilYHZ-{fEQ5heMW`m$+v>wZU4F4)HY_p3O-b*?sT5Hr(BLQq@R>AJI(Nrpx zrqR#pM8Ey@6wAd2)<<$!PHb@D<{zHCf7GQj>Jz+~o6ZJ#?85T4;pw6Lma*q}FH6`M z=k82%oi)#!vVZE{*1w-N&z=cnaS!>p?ZonXgOlCG&-H_BCf)f)EIW2)Zhkhk`X5ZB z7a*6w61>1l-2^Q*!0FScIp7A<84L!U=x$IX68&6KQlhcy5>O}37FMk)wWQa5XX#k6}!Fh<_s6-zS zVnnP~H$YEM&)e>Jhr@A^$z*&Yy&4k;1PYBtGrd?n7QYx-DC6Vf zCaF}~1Amqt&B(~uo0F3hsMTu!eEIU_2YU?u*s)`twY9bBu->=k=jZML^>tY*hYioQqW57Zg85uE3 zB$BRm>(+e)HXHy~$%PH=H3Tl7Tv$v@%vzXrL1bj)PS`{05)%{ahlhua6g$Wl|GE6K z9E;ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#QB4amK z3p|Q;F{P;(7$h1S8zmX)ni-p=>Y5}Qrs-OmS(@q^m|7Z`q?#lro2ME~c43~tB4J#` KwfP3KJqrMBf*~6K delta 132 zcmbO(FkN6n9Fu~Zv7wQLv6-WrlcTetp{s?XtEHQziKC0Nv!k1-p|Qc_Iwl#IB1@c# zb}^-?m{}&8npzmA=^Cb)8R(iM8ky=^S^`-{X(>jjiD`*RX{MHwU6?1Z+{hIU-h6}E Go&^A@!6Nnm diff --git a/resources/icons/overlay/layflat_off.png b/resources/icons/overlay/layflat_off.png index 393dfb37f05adab834eb529b819e74312402629a..6bfadd316576a0c6c5cc1e39cac7da55489635e4 100644 GIT binary patch delta 132 zcmaFF|A>D>9Fu~vfw7ybg`=^Xi>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IA|srN zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<D>9Fu~Zv7wQLv6-2hlcTetp{s?XtEHQziKC0Nv!k1-p|Qc_Iwl#AA_GS! zGYg!Gb}^-?m{}&8npzmA=^Cb)8R(iM8ky=^S^`-{X(>jjiD`*RX{MHwU6?1ZxJvOc KZoa|H#R35A{URa& diff --git a/resources/icons/overlay/layflat_on.png b/resources/icons/overlay/layflat_on.png index adb04e6e04c40291d8c95ab8bc43d977a06e5471..4892b57fc7c93c267c64c3f3dd0b47c9c15c9ce7 100644 GIT binary patch delta 132 zcmZ23vRq_C9Fu~vfr+7`fup0Fi>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB1@c# zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<jjiD`*RX{MHwU6?1ZbO?XixcLUN GKN|qXL?hAw diff --git a/resources/icons/overlay/move_hover.png b/resources/icons/overlay/move_hover.png index 322736553060145b8f9d9caafbbcb2bc0daddf4e..933c34c2454a41859afb398532dff4f9c077f46f 100644 GIT binary patch delta 1053 zcmV+&1mgSV5AP1JUIHLQLN+-xIX6K@L^MS)F-15yFhoH`GcYhPL`FeEIYc;G&wgpMnp74F)>9rI50#(MKdrkFhoW{LODb@lez+CBrq^xGcz(`F)cPTHf1d| zV=-neIW{>pEig4XFf?T}V`DdEFq1?BkbhqC64?L%1Aj?GK~#9!>|4KU6hRc8J)>Yk z@CO!NXTahxa9oiD!QvpGHXR1>Jf>GuEEcwCq3Y!{n2 zUbDU9IJ%ra3AGNqgR?Up?Os4#1b-&c?x(I*APFO&0h|UZJ=#^+u93D8@B`{Qu;-(B z9vH<47>;8E48;`aX`s+Y`<+yc0MnVStp#iVYyje)1o7^jf*RA%)h_0uuLBhTIxjQ% zc@5p|qrXB_L5OMujM4cMF#J!5Kpuzs&HgqAZVm|JypH=L8v)GiC0)HONq-Kqwzmd& zh09Q{$tTo<)(*J<4<2V>bCc<9L75SG?l;=89<++g_36CS>l;$l0Y#s=eU!3CJH(!i zAbi4*PsfF>YLU#4;kL;0#@BJ!@(F4Nwk$wDg1!n}#re-#MgR#*nSn0>Q(fniCbVmc zc5eD5T5~;|7rt&4cuA6?oqy!4j`mH`-c>)8d#_jE0kDjYtAmo-l4GpG`D3)d872Vt zmCM`zg|2mp%p?nJ9ih{=fP0}{0XQrjm4RzqYKjSWzQ`q7>@Cy&aFZZ9c>`4fwt+Ry zwn=ne>{_FpCEBfL8RU{O%XPNNrKXY)$In770oQ@Mu=R0di4Z%VP=79UEi-Q3u>X15 zA@)r4>wKV87!TRvQnLb#su&5(L$br?uW*Up3R-e%-yX-krH-ck^^Pm05ciwg9%nso z?0%Dk$S&|f$P5hwVW0g)!&yYNFOU>Yu-6Y@H%3&(#Reej0f=RHm;`2DyTfr2t#NIS zzfygE9{>!eDF9EE%YWWZlf&~WY}#y}syRSDfcnC!%}bWpuAZs@aMNN=O4g(J1fG|Y zc23s^Qi)mxSgi?3**MHB@mO}6YJsb|q@}_h@A-4(QrA+#9+1Nm8fzQJvsuUlg<&V#00000NkvXXu0mjfpETPC delta 1173 zcmV;G1Zw;54(1QAUIHLSGcZCpGBP9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu=H8nUhW-T#hHZUzT zVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KkbnG#=&k?&1NKQoK~#9!>|0Gs8$lS}o#s?& zsNk`*k)AzDp_L+PbFmS~%=665`!y!+_xsT}7ZHtlsR*bDs0gSC80%8YYK-I9*?+)S z8;vJaeZa@Hsh#c@S7T+F`XD<1;M*jhA#FVS|BpZd`~k^2V)BnU^~XLZ46kTC1r6ErCAN$MmINsejq z!{>iU_6S^S1aYsaT9p>&4*KFgpMTk&Xq@FH>bk-NWR9vu?m}Kzk`0mxBH>1Q=Pp&( zBs=_3_GXz#dgs)(E9``T3}SdIoRdrvm>*KxeX72bOjBFeS1jP_2#lNy>gNQwC$-h3 z?}U#A6%-zy&$k7(=q1Ss$^MA;6MU!05qn3nMPqh?5s)x-yn0I{rv)_oqJM>j<365} z9MK$_CZV*j0RA8kxPua7Ok+QxW&u3g>f;Or^q8t`l6#87{N(pY5q}-gKJ*iIL4wv! z5T@V@I5`Y{hNP<~GkDjj$5?X=KEld>q?O!KG+nG%8$~=unwR!Ys96Hf9^Mq}Je#pg z9rrh9eD(+*;QUWvOk)y+rGJ&*gNJvH?b~*s_}b|E9AoX-I1a+bwuf&&!5$vT3dw>4 z0v6EsBwXp14V@q?CNpl}-u4`tzaEbv#}OtZ-hcxkL7_U>2E# zq-9_tuWV;>QZ)Moz+NR6PZzv$ERaJ&R#Z0+-(IE6lbk6iJZ11&;cyxW1){<2B7u!m z&hnx`Q}EjP0GWhnkbk;P&s+@R`E>`U!Wg`fBP1!tInSoPk1>tF8|MVE6F3*h=liJO zd2MF|OD6!7r&o?azHw62_r1U)gtRLuK~o)ezCoZatZtVQ-3tsE{WRiYB>5dL#R&n( z+b^Y^8q~j1+J?{Gz|PC%NcU>YI|0$)(t`8$JYhL?wwV^s^M5k}z8R%ojxwPyA_teF zt_awIQv9ZpOF)*Vm15_6=+k>RtZN-QMWaXsC#Q332v7~BU3hrG3D70MCwMO@F~y{c zSMXj4P)MqH2k(u5JQ6~I_wEVG&wglMnp74F)>9rI50#(MKdrkFhoW{LODb@lez+CBrq^xGcz(`F)cPTHf1d| zV=-neIW{>pEig4XFf?T}V`DdEFq1?BkbkGpC653A0_I6XK~#9!?3%qx96=by$0Q1g z1V1V*4^oH+ivbnk3L%B1SXia75RNOZ@QN$^6D$-ig+dS#u?Pe#f~8$RK{SwXaB_uU zX-IL2hRcZ`oX@aNAP7d!-JRKwci_iyceA_CJnYQ8^Ukz2n@yGE@<=7Thyh}N7=IuJ zhyj*L0i5yEUeR%Y1v20gyyIuak`DF6g6BQp19%Uz;ELaez)#a++AKtZ8(| z```df$Gr>Wz!`9E{O<7goaruL@?mMv0Qp<~&-4GZNdruEfv0@U55d%b>U{z)*gYC? zng*9ZH<-Pn-Yn}a#cdG$1Ac?!dwxC8SF=jxT3!6!Xw*!_8(9b=kj(zG1+<+YT!d0Df&)7c5a!ehE)PI!=%77kb za^8R&ngE)*}+#)~R=$`aKyi4`#rsa!KuaxdE*`G zoS^QKAA{hIayKn>-hL0{Lx0-%JoOz=?~b3-px&|3n-+6mQMuXM-jFoBmwFbdZ(6mm z!_?|(@GaN|A35T-TW)+mb^HRupkg8vu5hZ^RBraRYN_#6K7W&XDi66yP)SkU2fuPj z>$PZsFB#lKZaLg;`Fd*^A)Uth0nUBO2pt7wK#=4#=|6fAe)E ze$U1^4*0j|bk%F+=ItLVH+ws-EF6?JJqX^UzD%PDHLkV1kd}lLi0) delta 1059 zcmV+;1l;@94fPJNUIHLDFgHO$Ge$*5LP0k%F-15*ML9+}G(ki|LqSG0F*7ieegYgI zH!wFrLNi80MnXY1F)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu=H8nUhW-T#hHZUzT zVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KkbgR@8(;tc1BFRMK~#9!>|4KU6hRc;y<8PB zTyeFRU~PFEm>>c5nuu~>8~*{hU}0}syMXu)2(c1WNSfe5&_u9K9f+~L!klO!oa^_< ziwwi=&dkov?3tYhk4biR=I#4t=l$BbI7yPI&eVu%P62=bKmZ^BP;VkPn8l9e_J6?N zn0r;}*r*6}Qg{#iiFwa82s85r@XDWox50DF+kCC(T}Ph)$jS@Xoq{kE0Awle1BA)} zP$};kgh~O>Ro*2Cl>uO?yekkY0idYzEjHo{PtiDziwZnMa}#_3 zo(f%mz;7|{i;QtYGy^F_CjeK#pMNoKxhMdcIywS?9{4s*A$7IEkHJ?k_b+M8Jer^2 zm*BBHC3u7}2W11`I}ln-&Z(8s*W$iKpMg-*cjV3OEeM}TeGAe(|3@ys2JetxdmW=T zO0sFB87r)?%&xVLHUJ2&j%d^4qCI1oS}xMb77$X}9q={JTPbD_bIPf7AAbh&OOc71 zB`E)hEOgGj*U`}1an(@F4;lx>jx+kFEc=xW2u)k%A#|$PE~NYo@V=p_RUW&Z`T7tb@G~ysV@G;30U8T#O6L z?l4rIW4E}*Iurwd{b?V3PX)q-5frjI7qp;b1X+1{W_3(ZcQ@G3G=D0pH(|h)oF$Il z`VLmV&vP%atbx_an;Jm{2sFzd$Y zD2dg+DC*?HNX~e~^+iQ2_OxYz0~SlI50b11yV~L?1bG6d$rB-Q$Itj`k>R zhdU`*W2PhM9_~B&DDF6@v2mk~C dYE6Fy7y#XSNfgu6&1nDt002ovPDHLkV1m?))hz%3 diff --git a/resources/icons/overlay/move_on.png b/resources/icons/overlay/move_on.png index 11f46fff3a2c420d02be044bcac6c86f77e7439c..80204b52eeec697f7d3d063228daca6708e15590 100644 GIT binary patch delta 1345 zcmV-H1-|;~5|I(GUIHL9Ff&0oIYl!@L^UumF-15yFhoH`GcYhPL`FeEIYc;9rI50#(MKdrkFhoW{LODb@lez+CBrq^xGcz(`F)cPTHf1d| zV=-neIW{>pEig4XFf?T}V`DdEFq1?Bkbe>2_%i?i1ffYpK~#9!>{)wATUQ*;!7m--;lm)jaP_?ma&UC+D1B&iT%FZ_fGszJDVW z2n0t2JT{Sl=SB!11P}rU0fYcH?8FAuo0f*+FHrK1fOl3aiuS*(+%){y^BYRkc|*Vo zWs29;@6#J>2Gmiaj&{xy@bz2SuWMA2mzr)+ zm#Ddb4{az>M?3mCOTfnBv|N3eQh#^e=;$^4?Rvbp4|TMoAAX!8AX6$zIa!sn+cf1H z`*e^RdX^8`(GNfPbBchoI`w8*icof-$J(`&ANt`3f2Igvgz1U`X}K>T@O0PZA6yoY zP$+^w{k5Aj1T^+jt*@vgIidRhPWdL8dIN++5Fxn$A%NEww@t9Huu!ViYJan$9cO50 z=-=`2aZgeafW;e)#*dXsr6Ss~>~_0DqtSdDFB0NH0G>P+pPrt6)a7yoBOOOBmy4WE z=V6RT6JSZYJs_IJ$Kv;KBLJ~_XKh~1L__iCyGs>?Y;0bb*Fw(^9MR+sVH1FC?e@MG z>&w-tr3i1siSS}+nAc0s4}ToM6KNJ176HiD&u>xJ+gyRE4HwO)X1zfNn@1St2M*u? zu1NHRkX{|gRZwgoS|D$KUP{Y`J(%^+RbDh-I9{pJ>dTbXZ{*2J^gY%q5AA+;Ox^$p zmIHeWKFUuQ=jsi%Gl{hLr}YHj15V%#RcKw307%x~ROP&9q`YJ6E`OMtpH^}b&43R$ zffu+#k=#YgJpGM7TikcIUAA0Z(C`x7fD?Fu8~CFl;QRZIKKeiP+SMI-+f->{Niq-s zoWKj*z#kO>O#{@ApY%_j)2LEQZoXYmuUjR5A(0mke8363zzzISZ-O+7|IkOx=?Q1R z)}qV(5W8(6E*`jm4}Um;7r0|~4B|+sYB#szoP~6)&6AcaCvY-l#ThAS-&Lz?OS2^< zEq~a14*f9oOWcwn4Bq!(?jfXW`lxAufXU|KGg3tg;Bfor-S1wrHq+Q_aSw$t$=bF@ z%_CN?Ng@_VnG`S=8|KCQzyUmwEI#6Nc-j;6*L7JSTX(bd^nW`{j&b_)RYIr3$miMo zJ!^spWGv?sI~)r|2;;?zfVkZYB#S0UE=B}AH$nj431H-Q7y@Rvx5GH+IYYq7D%C*< zsG(?x2yYSL#UjW_7y|B?JtI(WYdR-dq2Vo+*dVqaHhV`nwFkU!u=XNhLDsgEANt`3 ze@+pA+@Z05vVQ|9>+|#kw4)z>@MnquG5uN3#6*A1_+2@2vFkvYQb%ucrqOAibFq^1 zWkuE=I-fT*-?NW$_85eoV5EG1o>;HiwV*MgL>=wniWx~62jJ2oRV;ZZWkz;S@q&Q=1D#3lY=J`T*}PI- z(RSZ){rK>Vk*!FOWP>CbA_Ncu2myouLI8j5KLG{+b}B+T3q{Z&00000NkvXXu0mjf DTxM+3 delta 1564 zcmV+%2IKjW5$Y1KUIHLSGcZCpGBQL)LP0|@F-15*ML9+}G(ki|LqSG0F*7ieegYgI zMl&!%I5ILrMnXYDF)>9rK}9)6IW$2;LqkDEH8C?Vlez+CBsMu=H8nUhW-T#hHZUzT zVKOx>IXN~kEiz_gGG$?AVPa-AIg>;KkbjH*x*q@l1$s$DK~#9!>{?%JQ&k+kx4m1} zjcpwiC<8hs8Ij-;69gT`F_{VF!H0=4(`0BgJd%jU%m-r#@xjGtOw>nx*dh|5?7;_1 zB4*5>WJn~LW|PGh3PZq+bsMAWy7qcL*u|U6ZEx?Na}Hzom)x|y=k}c6_w}6L-+zjN zARH2wuy}+eE-nEs0WJY90WJYNWbdYmwXyx-gShaXmmXenweSG{*{)#6@Xbv8=4@{A zwJnv;UYweb4~=K0A9?~5@PlJ>7oP9E)_Y|5)=+Dme-$BN7Y`Dc5}*Y?mzCwLEC|Q` zni=Lr0y6^iz$;@sNnk=iA@ICOpnpz4G2nTUK$QS9;CYcijQ}g)d5}PbfVPH;+Rs~q z`>cS^M?!3JK1SjO$4L-r6hzK-tb4t#N?LzuYCe&Z{|BFw1^M*o?9f|%;{$r%`=n*X z_NUjDZT5(F^tv0WilRv73X{1mZ|%AAmD9RPG`7{pza@Wc4g2dC2)-u)pMR%(-CW39R z_p7Ug7{#f-lbQJYO*QR$<9`^#Ge&E^vUyqf?I!}yk)sst9htexjR3{fm3l=VIrEPg z5dd%SI*wVx0Az157fSP>p(ER~&v4&<|C5+u9e z;pb1kT>EOiXPcXwYr49+TKs;$&&(RBR4U!s+1c0M-#?*mPh=Tue}CT=YO_OvWEZ?q zkOT1HaCr6L;NV3w>ohhtZoqe&nwokG9VxpcNHoDi@XG^yBoc|5St}ZiMna)b!00)< zBzT+v57&ht2Y!C*YF{-milWnp9&=)Dtbw)E?ouHEx_{hwq+nHQe z8%bIv!Q<4KheB7T=V!GMo|A&Pv4$RatH+@r0UzyL@zNh-b0bz$T3(`%0NsJX5hM7p zNOlv0!Nf&J+#HPD`LGbcXK0V3`0IkOM!B^)v=cH-8zCfP8*krBtS5bubjv-@PDRrfntt>URH<12C==_WGInHTrbw(Spjb;8`LAA(DO)Xk<}HG*r)~I z@m-qW?-UjUi)C?bLFD|6bo`AI|MszPb6@UW+4A@*Up)#UcEH=J2&s1hN@5^@p{tkC zD_B$EP1^x)yC#T*iPi5$QS@XY6)WbvYO^}6tAn*SvVSY+5s)3Q(TSocmCjH;K|X)t z#WimttJ5os!_V*j_McB-?OEx>v@f8zE~K=`>eOS1@DSvv-P@ui0Cjtm$#p{6a=ap+ z*N!Ro5aeiEV+-3pW>j{6ULVs+3Uw8{^k2z`RO6C6n=;Py04e0BmzmGnD)?{$34RVg#wDwF1fZ!h;R!MLo z!sAYWRT5Z&r%r%b5?F(0LO`)3@Bq(@03#B3foDoUJ`%hhe&Cts0@_p>b$fh9^_B;C z<~u;U6WSkLwqv>9TYa!^{2LzNx$S@lN!-qmOCf+ufJ=Z&z*3g~1Q-DQA=k!6FljUZ O0000ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#YA_F&^ zigq!jsTdd}8XFrW8S0uDo2BZSBpasbTAEp!>Kd3@8knS-Bqy7v8ccR!p1@*lo?^QB I26G}S0E2}gl>h($ delta 132 zcmdlfv{Pt99Fu~Zv7v#ZlbNBLlcTetp{s?XtEHQziKC0Nv!k1-p|Qc_Iwl#IA_JU? zb}^-?m{}&8npzmA=^Cb)8R(iM8ky=^S^`-{X(>jjiD`*RX{MHwU6?1Z%ua0)-F$;N Gkre>dNF!nZ diff --git a/resources/icons/overlay/rotate_off.png b/resources/icons/overlay/rotate_off.png index 65ca5c8435d9b93b4cf2cfdb7c49fdd4e92486a5..f5b65297953bbe8619714e2a15b01b919f07aaf7 100644 GIT binary patch delta 132 zcmca2a7AE49Fu~vfw7B$iGj15i>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IA}5@R zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<RMU?Sw?9oMyZKuiAiaumXlqWC$MnGOf=to IgSmzU0Q!t0bN~PV diff --git a/resources/icons/overlay/rotate_on.png b/resources/icons/overlay/rotate_on.png index e32c13f3f34e62d81d8cd719e86e51b47eea3f8a..6d4c5bf31e4218b13e569c8adf01068fa06a5c93 100644 GIT binary patch delta 132 zcmaDY`C4*A9Fu~vfw7ybg{7ODi>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB3GP> zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<9Zp9 delta 132 zcmaDY`C4*A9Fu~Zv7v#ZlbNZTlcTetp{s?XtEHQziKC0Nv!k1-p|Qc_Iwl#IA`_g7 zb}^-?m{}&8npzmA=^Cb)8R(iM8ky=^S^`-{X(>jjiD`*RX{MHwU6?1Z+_rugzxf8U GFed;PQY23R diff --git a/resources/icons/overlay/scale_hover.png b/resources/icons/overlay/scale_hover.png index 46a4b5d89cc6deea78ccacd563a78855f7cb5609..40a05fffdd263d51d188e794d6c47bb55136f917 100644 GIT binary patch delta 132 zcmZ3>x0Y{19Fu~vfw7~7rK^*hi>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB1fEx zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<x0Y{19Fu~Zv4N|RtDA+JlcTetp{s?XtEHQziKC0Nv!k1-p|Qc_Iwl#IB6FOI zb}^-?m{}&8npzmA=^Cb)8R(iM8ky=^S^`-{X(>jjiD`*RX{MHwU6?1Z9I!Bbw)qBg GC^G=x!z1hf diff --git a/resources/icons/overlay/scale_off.png b/resources/icons/overlay/scale_off.png index 81bacddfe03be000dd56769a466028dc8a0ba608..351d5a00466a43e42b37970df1e9fc57a49e6466 100644 GIT binary patch delta 132 zcmeys`+;{u9Fu~vfw7~7rK`D{i>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IA~T$d zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<jjiD`*RX{MHwU6?1Z96CGKVDk-T G31$EVd?Y9U diff --git a/resources/icons/overlay/scale_on.png b/resources/icons/overlay/scale_on.png index 271116858dec48051fb3230078b3c53ff3286c5b..f8450e10587826bd9bc8f7e2183545a565c287e4 100644 GIT binary patch delta 132 zcmX>scvx^l9Fu~vfw7B$iGhWii>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB6FOI zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<scvx^l9Fu~Zv4N|RtDCc%lcTetp{s?XtEHQziKC0Nv!k1-p|Qc_Iwl#IA}5@R zb}^-?m{}&8npzmA=^Cb)8R(iM8ky=^S^`-{X(>jjiD`*RX{MHwU6?1Zta`xYviSz{ GYZd?kPb7!{ diff --git a/resources/icons/overlay/sla_support_points_hover.png b/resources/icons/overlay/sla_support_points_hover.png index 55955f3d6f1d8e1821a1b5f1212f78efb907b1d0..2b385c0e74dbe999bf598d19934b4a1cfbb2aa5e 100644 GIT binary patch delta 132 zcmeAa=oHux$E09vU}9!u;^OM&VrpP$=xSkM;Ns|NY+zvE;^ydN>0&Xtj!6cl$OWgO zT}){z1_p`7#zskox@N{^sk$b~hH1K%W|pS92BwwymBlUZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB3GP> zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<Lwbendq8W8W@;ZB%36f8yQV@VV=P9PWxQo J<{Qk1nE}I0Bi8@` diff --git a/resources/icons/overlay/sla_support_points_on.png b/resources/icons/overlay/sla_support_points_on.png index 879b9b24128b825efd77c29f9d84aa7b0449f1c1..e7f5d0a87546cfb644f98695133ae9baa1fbed59 100644 GIT binary patch delta 132 zcmdlgwpDCH9Fu~vfr*)siK~&Di>ZO3p{s?3fs3Q7v4Mesi<_g9rHjSnIwl#IB14>t zb}^-?7#JiP8yh7V>Y5pwrRtg_8>Z=6npv9a8kkxdn53E{C!41lOm<Lwak80wm&npx^58mF1)nphebm{=s6B$^u;O?F|Pz{271u4wZO H=2&(B>Ek0u From 2c83bae9ed4b6bcc32c9b1f0ee3850e2086214bb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 21 Dec 2018 19:48:33 +0100 Subject: [PATCH 101/132] Changed the default file extension to the Prusa SL1 files to .dwz --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 4b66214d1..69db64168 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -54,7 +54,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", /* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG", - /* FT_PNGZIP */"Zipped PNG files (*.zip)|*.zip;*.ZIP", // This is lame, but that's what we use for SLA + /* FT_PNGZIP */"Zipped PNG files (*.dwz)|*.dwz;*.DWZ", // This is lame, but that's what we use for SLA }; std::string out = defaults[file_type]; From de65bd989d757df424198be0f13b60791a634a61 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 21 Dec 2018 20:09:25 +0100 Subject: [PATCH 102/132] Annotated the system printer profiles with a technology filed. Code (as of now disabled) to suppress the SLA profiles for the initial alpha. --- src/slic3r/GUI/Preset.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index bfda0a2f3..fd2b26458 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -133,7 +133,12 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Invalid printer technology field: `%2%`") % id % technology_field; model.technology = ptFFF; } - section.second.get("variants", ""); +#if 0 + // Remove SLA printers from the initial alpha. + if (model.technology == ptSLA) + continue; +#endif + section.second.get("variants", ""); const auto variants_field = section.second.get("variants", ""); std::vector variants; if (Slic3r::unescape_strings_cstyle(variants_field, variants)) { From 2cdf60972f302c5d236c0ad9c13b52d67d7bb484 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 21 Dec 2018 20:10:42 +0100 Subject: [PATCH 103/132] Annotated the system printer profiles with a technology filed. --- resources/profiles/PrusaResearch.ini | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/resources/profiles/PrusaResearch.ini b/resources/profiles/PrusaResearch.ini index 7bdcb6906..57c401419 100644 --- a/resources/profiles/PrusaResearch.ini +++ b/resources/profiles/PrusaResearch.ini @@ -18,30 +18,37 @@ config_update_url = https://raw.githubusercontent.com/prusa3d/Slic3r-settings/ma [printer_model:MK3] name = Original Prusa i3 MK3 variants = 0.4; 0.25; 0.6 +technology = FFF [printer_model:MK2.5] name = Original Prusa i3 MK2.5 variants = 0.4; 0.25; 0.6 +technology = FFF [printer_model:MK2S] name = Original Prusa i3 MK2/S variants = 0.4; 0.25; 0.6 +technology = FFF [printer_model:MK3MMU2] name = Original Prusa i3 MK3 MMU 2.0 variants = 0.4 +technology = FFF [printer_model:MK2SMM] name = Original Prusa i3 MK2/S MMU 1.0 variants = 0.4; 0.6 +technology = FFF [printer_model:MK2.5MMU2] name = Original Prusa i3 MK2.5 MMU 2.0 variants = 0.4 +technology = FFF [printer_model:SL1] name = Original Prusa SL1 variants = default +technology = SLA # All presets starting with asterisk, for example *common*, are intermediate and they will # not make it into the user interface. From 126035f6f8f2a95784dfeac4afd339ea07e9f4f9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 22 Dec 2018 10:02:42 +0100 Subject: [PATCH 104/132] Fixed many errors in background processing synchronization and update. Fixed couple of compiler warnings. --- src/igl/EPS.h | 4 +- src/libslic3r/Print.cpp | 21 +++-- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintObject.cpp | 13 +-- src/libslic3r/SLAPrint.cpp | 6 +- src/libslic3r/SLAPrint.hpp | 2 +- src/slic3r/GUI/3DScene.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 141 +++++++++++++++-------------- src/slic3r/Utils/PresetUpdater.cpp | 2 +- 9 files changed, 101 insertions(+), 92 deletions(-) diff --git a/src/igl/EPS.h b/src/igl/EPS.h index 17f3b8c25..d65007a64 100644 --- a/src/igl/EPS.h +++ b/src/igl/EPS.h @@ -13,8 +13,8 @@ namespace igl // Define a standard value for double epsilon const double DOUBLE_EPS = 1.0e-14; const double DOUBLE_EPS_SQ = 1.0e-28; - const float FLOAT_EPS = 1.0e-7; - const float FLOAT_EPS_SQ = 1.0e-14; + const float FLOAT_EPS = 1.0e-7f; + const float FLOAT_EPS_SQ = 1.0e-14f; // Function returning EPS for corresponding type template IGL_INLINE S_type EPS(); template IGL_INLINE S_type EPS_SQ(); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 97eb03662..0ac5c9771 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -760,7 +760,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co update_apply_status(this->invalidate_all_steps()); for (PrintObject *object : m_objects) { model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); - delete object; + update_apply_status(object->invalidate_all_steps()); + delete object; } m_objects.clear(); for (PrintRegion *region : m_regions) @@ -990,12 +991,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co const_cast(*it_old)->status = PrintObjectStatus::Deleted; } else { // The PrintObject already exists and the copies differ. - if ((*it_old)->print_object->copies().size() != new_instances.copies.size()) - update_apply_status(this->invalidate_step(psWipeTower)); - if ((*it_old)->print_object->set_copies(new_instances.copies)) { - // Invalidated - update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psGCodeExport })); - } + PrintBase::ApplyStatus status = (*it_old)->print_object->set_copies(new_instances.copies); + if (status != PrintBase::APPLY_STATUS_UNCHANGED) + update_apply_status(status == PrintBase::APPLY_STATUS_INVALIDATED); print_objects_new.emplace_back((*it_old)->print_object); const_cast(*it_old)->status = PrintObjectStatus::Reused; } @@ -1009,13 +1007,14 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co bool deleted_objects = false; for (auto &pos : print_object_status) if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { - // update_apply_status(pos.print_object->invalidate_all_steps()); + update_apply_status(pos.print_object->invalidate_all_steps()); delete pos.print_object; deleted_objects = true; } if (new_objects || deleted_objects) update_apply_status(this->invalidate_steps({ psSkirt, psBrim, psWipeTower, psGCodeExport })); - update_apply_status(new_objects); + if (new_objects) + update_apply_status(false); } print_object_status.clear(); } @@ -1629,7 +1628,9 @@ void Print::_make_skirt() { Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, scale_(0.1)); Geometry::simplify_polygons(loops, scale_(0.05), &loops); - loop = loops.front(); + if (loops.empty()) + break; + loop = loops.front(); } // Extrude the skirt loop. ExtrusionLoop eloop(elrSkirt); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index e5060cb76..eaad767ea 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -164,7 +164,7 @@ protected: void config_apply(const ConfigBase &other, bool ignore_nonexistent = false) { this->m_config.apply(other, ignore_nonexistent); } void config_apply_only(const ConfigBase &other, const t_config_option_keys &keys, bool ignore_nonexistent = false) { this->m_config.apply_only(other, keys, ignore_nonexistent); } void set_trafo(const Transform3d& trafo) { m_trafo = trafo; } - bool set_copies(const Points &points); + PrintBase::ApplyStatus set_copies(const Points &points); // Invalidates the step, and its depending steps in PrintObject and Print. bool invalidate_step(PrintObjectStep step); // Invalidates all PrintObject and Print steps. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 877c77b0c..3019233d1 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -69,7 +69,7 @@ PrintObject::PrintObject(Print* print, ModelObject* model_object, bool add_insta this->layer_height_profile = model_object->layer_height_profile; } -bool PrintObject::set_copies(const Points &points) +PrintBase::ApplyStatus PrintObject::set_copies(const Points &points) { // Order copies with a nearest-neighbor search. std::vector copies; @@ -81,14 +81,15 @@ bool PrintObject::set_copies(const Points &points) copies.emplace_back(points[point_idx] + m_copies_shift); } // Invalidate and set copies. - bool invalidated = false; + PrintBase::ApplyStatus status = PrintBase::APPLY_STATUS_UNCHANGED; if (copies != m_copies) { - invalidated = m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }); - if (copies.size() != m_copies.size()) - invalidated |= m_print->invalidate_step(psWipeTower); + status = PrintBase::APPLY_STATUS_CHANGED; + if (m_print->invalidate_steps({ psSkirt, psBrim, psGCodeExport }) || + (copies.size() != m_copies.size() && m_print->invalidate_step(psWipeTower))) + status = PrintBase::APPLY_STATUS_INVALIDATED; m_copies = copies; } - return invalidated; + return status; } // 1) Decides Z positions of the layers, diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 9d177a5e9..afb9c6f87 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -184,6 +184,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf update_apply_status(this->invalidate_all_steps()); for (SLAPrintObject *object : m_objects) { model_object_status.emplace(object->model_object()->id(), ModelObjectStatus::Deleted); + update_apply_status(object->invalidate_all_steps()); delete object; } m_objects.clear(); @@ -376,11 +377,12 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf bool deleted_objects = false; for (auto &pos : print_object_status) if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { - // update_apply_status(pos.print_object->invalidate_all_steps()); + update_apply_status(pos.print_object->invalidate_all_steps()); delete pos.print_object; deleted_objects = true; } - update_apply_status(new_objects); + if (new_objects) + update_apply_status(false); } this->update_object_placeholders(); diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index 3a4dd846e..9fab4d550 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -190,7 +190,7 @@ public: // Returns true if an object step is done on all objects and there's at least one object. bool is_step_done(SLAPrintObjectStep step) const; // Returns true if the last step was finished with success. - bool finished() const override { return this->is_step_done(slaposIndexSlices); } + bool finished() const override { return this->is_step_done(slaposIndexSlices) && this->Inherited::is_step_done(slapsRasterize); } template void export_raster(const std::string& fname) { if(m_printer) m_printer->save(fname); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index b801b6679..d0ae03e26 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -2218,7 +2218,7 @@ bool GLCurvedArrow::on_init(bool useVBOs) } // side face - for (unsigned int i = 0; i < 4 + 2 * (int)m_resolution; ++i) + for (unsigned int i = 0; i < 4 + 2 * (unsigned int)m_resolution; ++i) { triangles.emplace_back(i, vertices_per_level + 2 + i, i + 1); triangles.emplace_back(i, vertices_per_level + 1 + i, vertices_per_level + 2 + i); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bcf66b603..6d5053bfa 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -987,11 +987,17 @@ struct Plater::priv // update_background_process() reports, that the Print / SLAPrint is invalid, and the error message // was sent to the status line. UPDATE_BACKGROUND_PROCESS_INVALID = 4, + // Restart even if the background processing is disabled. + UPDATE_BACKGROUND_PROCESS_FORCE_RESTART = 8, + // Restart for G-code (or SLA zip) export or upload. + UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT = 16, }; // returns bit mask of UpdateBackgroundProcessReturnState unsigned int update_background_process(); + // Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. + bool restart_background_process(unsigned int state); + void update_restart_background_process(bool force_scene_update, bool force_preview_update); void export_gcode(fs::path output_path, PrintHostJob upload_job); - void async_apply_config(); void reload_from_disk(); void fix_through_netfabb(const int obj_idx); @@ -1141,7 +1147,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif // ENABLE_REMOVE_TABS_FROM_PLATER this->background_process_timer.SetOwner(this->q, 0); - this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->async_apply_config(); }); + this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->update_restart_background_process(false, false); }); auto *bed_shape = config->opt("bed_shape"); #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -1239,6 +1245,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); + preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); #if ENABLE_REMOVE_TABS_FROM_PLATER view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); #endif // ENABLE_REMOVE_TABS_FROM_PLATER @@ -1272,19 +1279,21 @@ void Plater::priv::update(bool force_full_scene_refresh) model.center_instances_around_point(bed_center); } - if (this->printer_technology == ptSLA) { + unsigned int update_status = 0; + if (this->printer_technology == ptSLA) // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - this->update_background_process(); - } + update_status = this->update_background_process(); #if ENABLE_REMOVE_TABS_FROM_PLATER - view3D->reload_scene(false, force_full_scene_refresh); + this->view3D->reload_scene(false, force_full_scene_refresh); #else this->canvas3D->reload_scene(false, force_full_scene_refresh); #endif // ENABLE_REMOVE_TABS_FROM_PLATER - preview->reload_print(); - - this->schedule_background_process(); + this->preview->reload_print(); + if (this->printer_technology == ptSLA) + this->restart_background_process(update_status); + else + this->schedule_background_process(); } #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -1999,12 +2008,13 @@ unsigned int Plater::priv::update_background_process() // bitmap of enum UpdateBackgroundProcessReturnState unsigned int return_state = 0; - // If the update_background_process() was not called by the timer, kill the timer, so the async_apply_config() - // will not be called again in vain. + // If the update_background_process() was not called by the timer, kill the timer, + // so the update_restart_background_process() will not be called again in vain. this->background_process_timer.Stop(); // Update the "out of print bed" state of ModelInstances. this->update_print_volume_state(); // Apply new config to the possibly running background task. + bool was_running = this->background_process.running(); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. @@ -2038,18 +2048,10 @@ unsigned int Plater::priv::update_background_process() } } - if (this->background_process.empty()) { - if (invalidated != Print::APPLY_STATUS_UNCHANGED) { - // The background processing will not be restarted, because the Print / SLAPrint is empty. - // Simulate a "canceled" callback message. - wxCommandEvent evt; - evt.SetInt(-1); // canceled - this->on_process_completed(evt); - } - } else { + if (! this->background_process.empty()) { std::string err = this->background_process.validate(); if (err.empty()) { - if (invalidated != Print::APPLY_STATUS_UNCHANGED) + if (invalidated != Print::APPLY_STATUS_UNCHANGED && this->background_processing_enabled()) return_state |= UPDATE_BACKGROUND_PROCESS_RESTART; } else { // The print is not valid. @@ -2057,9 +2059,39 @@ unsigned int Plater::priv::update_background_process() return_state |= UPDATE_BACKGROUND_PROCESS_INVALID; } } + + if (invalidated != Print::APPLY_STATUS_UNCHANGED && was_running && ! this->background_process.running() && + (return_state & UPDATE_BACKGROUND_PROCESS_RESTART) == 0) { + // The background processing was killed and it will not be restarted. + wxCommandEvent evt(EVT_PROCESS_COMPLETED); + evt.SetInt(-1); + // Post the "canceled" callback message, so that it will be processed after any possible pending status bar update messages. + wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, evt.Clone()); + } + return return_state; } +// Restart background processing thread based on a bitmask of UpdateBackgroundProcessReturnState. +bool Plater::priv::restart_background_process(unsigned int state) +{ + if ( ! this->background_process.empty() && + (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && + ( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) || + (state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 || + (state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) { + // The print is valid and it can be started. + if (this->background_process.start()) { + this->statusbar()->set_cancel_callback([this]() { + this->statusbar()->set_status_text(L("Cancelling")); + this->background_process.stop(); + }); + return true; + } + } + return false; +} + void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) { wxCHECK_RET(!(output_path.empty() && upload_job.empty()), "export_gcode: output_path and upload_job empty"); @@ -2089,34 +2121,22 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) background_process.schedule_upload(std::move(upload_job)); } - if (! background_process.running()) { - // The print is valid and it should be started. - if (background_process.start()) - statusbar()->set_cancel_callback([this]() { - statusbar()->set_status_text(L("Cancelling")); - background_process.stop(); - }); - } + this->restart_background_process(priv::UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT); } -void Plater::priv::async_apply_config() +void Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview) { // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(); - if (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) + if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) #if ENABLE_REMOVE_TABS_FROM_PLATER view3D->reload_scene(false); #else this->canvas3D->reload_scene(false); #endif // ENABLE_REMOVE_TABS_FROM_PLATER - if ((state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 && this->background_processing_enabled()) { - // The print is valid and it can be started. - if (this->background_process.start()) - this->statusbar()->set_cancel_callback([this]() { - this->statusbar()->set_status_text(L("Cancelling")); - this->background_process.stop(); - }); - } + if (force_update_preview) + this->preview->reload_print(); + this->restart_background_process(state); } void Plater::priv::update_fff_scene() @@ -2135,15 +2155,8 @@ void Plater::priv::update_sla_scene() { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) - this->schedule_background_process(); -#if ENABLE_REMOVE_TABS_FROM_PLATER - view3D->reload_scene(true); -#else - this->canvas3D->reload_scene(true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = false; - this->preview->reload_print(); + this->update_restart_background_process(true, true); } void Plater::priv::reload_from_disk() @@ -2242,10 +2255,9 @@ void Plater::priv::set_current_panel(wxPanel* panel) { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) - this->schedule_background_process(); - } - view3D->reload_scene(true); + this->update_restart_background_process(true, false); + } else + view3D->reload_scene(true); } // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) view3D->set_as_dirty(); @@ -2277,10 +2289,9 @@ void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) if (this->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. - if (this->update_background_process() & UPDATE_BACKGROUND_PROCESS_RESTART) - this->schedule_background_process(); - } - this->canvas3D->reload_scene(true); + this->update_restart_background_process(true, false); + } else + this->canvas3D->reload_scene(true); } // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) this->canvas3D->set_as_dirty(); @@ -3143,14 +3154,8 @@ void Plater::reslice() #else this->p->canvas3D->reload_scene(false); #endif // ENABLE_REMOVE_TABS_FROM_PLATER - if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 && !this->p->background_process.running() && !this->p->background_process.finished()) { - // The print is valid and it can be started. - if (this->p->background_process.start()) - this->p->statusbar()->set_cancel_callback([this]() { - this->p->statusbar()->set_status_text(L("Cancelling")); - this->p->background_process.stop(); - }); - } + // Only restarts if the state is valid. + this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); } void Plater::send_gcode() @@ -3341,13 +3346,13 @@ void Plater::changed_object(int obj_idx) model_object->ensure_on_bed(); if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() - // pulls the correct data. - this->p->update_background_process(); - } + // pulls the correct data, update the 3D scene. + this->p->update_restart_background_process(true, false); + } else #if ENABLE_REMOVE_TABS_FROM_PLATER - p->view3D->reload_scene(false); + p->view3D->reload_scene(false); #else - p->canvas3D->reload_scene(false); + p->canvas3D->reload_scene(false); #endif // ENABLE_REMOVE_TABS_FROM_PLATER } diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index be2a17d66..bfa3af3e6 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -256,7 +256,7 @@ void PresetUpdater::priv::sync_config(const std::set vendors) Index new_index; try { new_index.load(idx_path_temp); - } catch (const std::exception &err) { + } catch (const std::exception & /* err */) { BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading a downloaded index %1% for vendor %2%: invalid index?") % idx_path_temp % vendor.name; continue; } From 18beb10ae0b7040b4c97b003674f465dadffda4d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 22 Dec 2018 10:55:15 +0100 Subject: [PATCH 105/132] Automatic SLA support points: Disable debug outputs by default. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 9 ++++++++- src/libslic3r/SLA/SLAAutoSupports.hpp | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 5011c485b..8f8d5673a 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -171,7 +171,7 @@ float SLAAutoSupports::distance_limit(float angle) const return 1./(2.4*get_required_density(angle)); } - +#ifdef SLA_AUTOSUPPORTS_DEBUG void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string filename) const { BoundingBox bb(Point(-30000000, -30000000), Point(30000000, 30000000)); @@ -188,6 +188,7 @@ void SLAAutoSupports::output_expolygons(const ExPolygons& expolys, std::string f svg_cummulative.draw_outline(expolys[i].holes, "blue", scale_(0.05)); } } +#endif /* SLA_AUTOSUPPORTS_DEBUG */ std::vector> SLAAutoSupports::find_islands(const std::vector& slices, const std::vector& heights) const { @@ -203,10 +204,14 @@ std::vector> SLAAutoSupports::find_islands(const s const ExPolygons& expolys_bottom = (i == 0 ? ExPolygons() : slices[i-1]); std::string layer_num_str = std::string((i<10 ? "0" : "")) + std::string((i<100 ? "0" : "")) + std::to_string(i); +#ifdef SLA_AUTOSUPPORTS_DEBUG output_expolygons(expolys_top, "top" + layer_num_str + ".svg"); +#endif /* SLA_AUTOSUPPORTS_DEBUG */ ExPolygons diff = diff_ex(expolys_top, expolys_bottom); +#ifdef SLA_AUTOSUPPORTS_DEBUG output_expolygons(diff, "diff" + layer_num_str + ".svg"); +#endif /* SLA_AUTOSUPPORTS_DEBUG */ ClosestPointLookupType cpl(SCALED_EPSILON); for (const ExPolygon& expol : expolys_top) { @@ -240,8 +245,10 @@ std::vector> SLAAutoSupports::find_islands(const s NO_ISLAND: ;// continue with next ExPolygon } +#ifdef SLA_AUTOSUPPORTS_DEBUG //if (!islands.empty()) // output_expolygons(islands, "islands" + layer_num_str + ".svg"); +#endif /* SLA_AUTOSUPPORTS_DEBUG */ } return islands; diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 568c200d0..08dc45236 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -5,7 +5,7 @@ #include #include - +// #define SLA_AUTOSUPPORTS_DEBUG namespace Slic3r { @@ -33,7 +33,9 @@ private: std::vector uniformly_cover(const std::pair& island); void project_upward_onto_mesh(std::vector& points) const; +#ifdef SLA_AUTOSUPPORTS_DEBUG void output_expolygons(const ExPolygons& expolys, std::string filename) const; +#endif /* SLA_AUTOSUPPORTS_DEBUG */ SLAAutoSupports::Config m_config; const Eigen::MatrixXd& m_V; From 0a3758d78529e26edd68e6e2c29b2ea93c2caf47 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 22 Dec 2018 11:21:21 +0100 Subject: [PATCH 106/132] SLA automatic support points: Added cancellation points. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 15 +++++++++++++-- src/libslic3r/SLA/SLAAutoSupports.hpp | 4 +++- src/libslic3r/SLAPrint.cpp | 6 +++++- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 8f8d5673a..ee87c6b66 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -13,8 +13,9 @@ namespace Slic3r { -SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, const Config& config) -: m_config(config), m_V(emesh.V), m_F(emesh.F) +SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, + const Config& config, std::function throw_on_cancel) +: m_config(config), m_V(emesh.V), m_F(emesh.F), m_throw_on_cancel(throw_on_cancel) { // FIXME: It might be safer to get rid of the rand() calls altogether, because it is probably // not always thread-safe and can be slow if it is. @@ -28,8 +29,10 @@ SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3 // Uniformly cover each of the islands with support points. for (const auto& island : islands) { std::vector points = uniformly_cover(island); + m_throw_on_cancel(); project_upward_onto_mesh(points); m_output.insert(m_output.end(), points.begin(), points.end()); + m_throw_on_cancel(); } // We are done with the islands. Let's sprinkle the rest of the mesh. @@ -111,7 +114,14 @@ void SLAAutoSupports::sprinkle_mesh(const TriangleMesh& mesh) // Angle at which the density reaches zero: const float threshold_angle = std::min(M_PI_2, M_PI_4 * acos(0.f/m_config.density_at_horizontal) / acos(m_config.density_at_45/m_config.density_at_horizontal)); + size_t cancel_test_cntr = 0; while (refused_points < refused_limit) { + if (++ cancel_test_cntr == 500) { + // Don't call the cancellation routine too often as the multi-core cache synchronization + // may be pretty expensive. + m_throw_on_cancel(); + cancel_test_cntr = 0; + } // Place a random point on the mesh and calculate corresponding facet's normal: Eigen::VectorXi FI; Eigen::MatrixXd B; @@ -249,6 +259,7 @@ std::vector> SLAAutoSupports::find_islands(const s //if (!islands.empty()) // output_expolygons(islands, "islands" + layer_num_str + ".svg"); #endif /* SLA_AUTOSUPPORTS_DEBUG */ + m_throw_on_cancel(); } return islands; diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 08dc45236..311d7b0c7 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -17,7 +17,8 @@ public: float minimal_z; }; - SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, const std::vector& heights, const Config& config); + SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3D& emesh, const std::vector& slices, + const std::vector& heights, const Config& config, std::function throw_on_cancel); const std::vector& output() { return m_output; } private: @@ -38,6 +39,7 @@ private: #endif /* SLA_AUTOSUPPORTS_DEBUG */ SLAAutoSupports::Config m_config; + std::function m_throw_on_cancel; const Eigen::MatrixXd& m_V; const Eigen::MatrixXi& m_F; }; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index afb9c6f87..85b63a24c 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -514,6 +514,7 @@ void SLAPrint::process() float(po.get_elevation()), ilh, float(lh)); + this->throw_if_canceled(); SLAAutoSupports::Config config; const SLAPrintObjectConfig& cfg = po.config(); config.minimal_z = float(cfg.support_minimal_z); @@ -521,14 +522,17 @@ void SLAPrint::process() config.density_at_horizontal = cfg.support_density_at_horizontal / 10000.f; // Construction of this object does the calculation. + this->throw_if_canceled(); SLAAutoSupports auto_supports(po.transformed_mesh(), po.m_supportdata->emesh, po.get_model_slices(), heights, - config); + config, + [this]() { throw_if_canceled(); }); // Now let's extract the result. const std::vector& points = auto_supports.output(); + this->throw_if_canceled(); po.m_supportdata->support_points = sla::to_point_set(points); } else { From 6e53d594e549ab5af982da16d9b4d8e97f233c86 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 23 Dec 2018 18:34:11 +0100 Subject: [PATCH 107/132] Bumped up version number. --- version.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.inc b/version.inc index 95e2741dd..d648bc48b 100644 --- a/version.inc +++ b/version.inc @@ -2,7 +2,7 @@ # (the version numbers are generated by the build script from the git current label) set(SLIC3R_FORK_NAME "Slic3r Prusa Edition") -set(SLIC3R_VERSION "1.42.0-alpha") +set(SLIC3R_VERSION "1.42.0-alpha1") set(SLIC3R_BUILD "${SLIC3R_VERSION}+UNKNOWN") set(SLIC3R_BUILD_ID "${SLIC3R_BUILD_ID}") set(SLIC3R_RC_VERSION "1,42,0,0") From 9bd57285081e613c05988173a4f369bfac80b05c Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 25 Dec 2018 00:34:50 +0100 Subject: [PATCH 108/132] AboutDialog crash fix --- src/slic3r/GUI/GUI.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index fb6676aed..04e9a315c 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -377,7 +377,6 @@ void about() { AboutDialog dlg; dlg.ShowModal(); - dlg.Destroy(); } void desktop_open_datadir_folder() From 447a51a3c8355e8a7e3e7069dbdd3f3bc3f0606c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Jan 2019 08:55:56 +0100 Subject: [PATCH 109/132] Constrained camera target set as default --- src/libslic3r/Technologies.hpp | 2 -- src/slic3r/GUI/GLCanvas3D.cpp | 46 ---------------------------------- src/slic3r/GUI/GLCanvas3D.hpp | 15 ----------- 3 files changed, 63 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index aa1e49e83..16c610517 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -34,8 +34,6 @@ #define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0) // Removes the wxNotebook from plater #define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0) -// Constrains the camera target into the scene bounding box -#define ENABLE_CONSTRAINED_CAMERA_TARGET (1 && ENABLE_1_42_0) // Use wxDataViewRender instead of wxDataViewCustomRenderer #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) // Adds background texture to toolbars diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4069ec6a0..0529eeb72 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -280,13 +280,8 @@ GLCanvas3D::Camera::Camera() , zoom(1.0f) , phi(45.0f) // , distance(0.0f) -#if !ENABLE_CONSTRAINED_CAMERA_TARGET - , target(0.0, 0.0, 0.0) -#endif // !ENABLE_CONSTRAINED_CAMERA_TARGET , m_theta(45.0f) -#if ENABLE_CONSTRAINED_CAMERA_TARGET , m_target(Vec3d::Zero()) -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET { } @@ -309,7 +304,6 @@ void GLCanvas3D::Camera::set_theta(float theta) m_theta = clamp(0.0f, GIMBALL_LOCK_THETA_MAX, theta); } -#if ENABLE_CONSTRAINED_CAMERA_TARGET void GLCanvas3D::Camera::set_target(const Vec3d& target, GLCanvas3D& canvas) { m_target = target; @@ -328,7 +322,6 @@ void GLCanvas3D::Camera::set_scene_box(const BoundingBoxf3& box, GLCanvas3D& can canvas.viewport_changed(); } } -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET GLCanvas3D::Bed::Bed() : m_type(Custom) @@ -4083,14 +4076,12 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const return bb; } -#if ENABLE_CONSTRAINED_CAMERA_TARGET BoundingBoxf3 GLCanvas3D::scene_bounding_box() const { BoundingBoxf3 bb = volumes_bounding_box(); bb.merge(m_bed.get_bounding_box()); return bb; } -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET bool GLCanvas3D::is_layers_editing_enabled() const { @@ -4226,12 +4217,8 @@ void GLCanvas3D::set_viewport_from_scene(const GLCanvas3D& other) { m_camera.phi = other.m_camera.phi; m_camera.set_theta(other.m_camera.get_theta()); -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_scene_box(other.m_camera.get_scene_box(), *this); m_camera.set_target(other.m_camera.get_target(), *this); -#else - m_camera.target = other.m_camera.target; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.zoom = other.m_camera.zoom; m_dirty = true; } @@ -4783,10 +4770,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // restore to default value m_regenerate_volumes = true; -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_scene_box(scene_bounding_box(), *this); m_camera.set_target(m_camera.get_target(), *this); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET // and force this canvas to be redrawn. m_dirty = true; @@ -5343,11 +5328,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) float z = 0.0f; const Vec3d& cur_pos = _mouse_to_3d(pos, &z); Vec3d orig = _mouse_to_3d(m_mouse.drag.start_position_2D, &z); -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_target(m_camera.get_target() + orig - cur_pos, *this); -#else - m_camera.target += orig - cur_pos; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET viewport_changed(); @@ -5433,10 +5414,8 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // Let the platter 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)); -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_scene_box(scene_bounding_box(), *this); set_camera_zoom(0.0f); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET } m_moving = false; @@ -6075,11 +6054,7 @@ void GLCanvas3D::_zoom_to_bounding_box(const BoundingBoxf3& bbox) { m_camera.zoom = zoom; // center view around bounding box center -#if ENABLE_CONSTRAINED_CAMERA_TARGET m_camera.set_target(bbox.center(), *this); -#else - m_camera.target = bbox.center(); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET viewport_changed(); @@ -6201,12 +6176,8 @@ void GLCanvas3D::_camera_tranform() const ::glRotatef(-m_camera.get_theta(), 1.0f, 0.0f, 0.0f); // pitch ::glRotatef(m_camera.phi, 0.0f, 0.0f, 1.0f); // yaw -#if ENABLE_CONSTRAINED_CAMERA_TARGET Vec3d target = -m_camera.get_target(); ::glTranslated(target(0), target(1), target(2)); -#else - ::glTranslated(-m_camera.target(0), -m_camera.target(1), -m_camera.target(2)); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET } void GLCanvas3D::_picking_pass() const @@ -6555,7 +6526,6 @@ void GLCanvas3D::_render_camera_target() const ::glLineWidth(2.0f); ::glBegin(GL_LINES); -#if ENABLE_CONSTRAINED_CAMERA_TARGET const Vec3d& target = m_camera.get_target(); // draw line for x axis ::glColor3f(1.0f, 0.0f, 0.0f); @@ -6569,22 +6539,6 @@ void GLCanvas3D::_render_camera_target() const ::glColor3f(0.0f, 0.0f, 1.0f); ::glVertex3d(target(0), target(1), target(2) - half_length); ::glVertex3d(target(0), target(1), target(2) + half_length); -#else - // draw line for x axis - ::glColor3f(1.0f, 0.0f, 0.0f); - ::glVertex3d(m_camera.target(0) - half_length, m_camera.target(1), m_camera.target(2)); - ::glVertex3d(m_camera.target(0) + half_length, m_camera.target(1), m_camera.target(2)); - // draw line for y axis - ::glColor3f(0.0f, 1.0f, 0.0f); - ::glVertex3d(m_camera.target(0), m_camera.target(1) - half_length, m_camera.target(2)); - ::glVertex3d(m_camera.target(0), m_camera.target(1) + half_length, m_camera.target(2)); - ::glEnd(); - - ::glBegin(GL_LINES); - ::glColor3f(0.0f, 0.0f, 1.0f); - ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) - half_length); - ::glVertex3d(m_camera.target(0), m_camera.target(1), m_camera.target(2) + half_length); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET ::glEnd(); } #endif // ENABLE_SHOW_CAMERA_TARGET diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4666fcd05..63296dac3 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -157,15 +157,10 @@ class GLCanvas3D float zoom; float phi; // float distance; -#if !ENABLE_CONSTRAINED_CAMERA_TARGET - Vec3d target; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET private: -#if ENABLE_CONSTRAINED_CAMERA_TARGET Vec3d m_target; BoundingBoxf3 m_scene_box; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET float m_theta; public: @@ -176,13 +171,11 @@ class GLCanvas3D float get_theta() const { return m_theta; } void set_theta(float theta); -#if ENABLE_CONSTRAINED_CAMERA_TARGET const Vec3d& get_target() const { return m_target; } void set_target(const Vec3d& target, GLCanvas3D& canvas); const BoundingBoxf3& get_scene_box() const { return m_scene_box; } void set_scene_box(const BoundingBoxf3& box, GLCanvas3D& canvas); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET }; class Bed @@ -865,10 +858,6 @@ private: wxWindow *m_external_gizmo_widgets_parent; #endif // not ENABLE_IMGUI -#if !ENABLE_CONSTRAINED_CAMERA_TARGET - void viewport_changed(); -#endif // !ENABLE_CONSTRAINED_CAMERA_TARGET - public: GLCanvas3D(wxGLCanvas* canvas); ~GLCanvas3D(); @@ -933,9 +922,7 @@ public: float get_camera_zoom() const; BoundingBoxf3 volumes_bounding_box() const; -#if ENABLE_CONSTRAINED_CAMERA_TARGET BoundingBoxf3 scene_bounding_box() const; -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET bool is_layers_editing_enabled() const; bool is_layers_editing_allowed() const; @@ -1026,9 +1013,7 @@ public: void update_gizmos_on_off_state(); -#if ENABLE_CONSTRAINED_CAMERA_TARGET void viewport_changed(); -#endif // ENABLE_CONSTRAINED_CAMERA_TARGET void handle_sidebar_focus_event(const std::string& opt_key, bool focus_on); From 4b04e4e5529c56d1cf85fcdc549e53bc945b76ce Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Jan 2019 10:18:02 +0100 Subject: [PATCH 110/132] Fix for #1549 --- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 7955326f5..b2ffb1801 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -216,13 +216,18 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele else if (selection.is_single_full_instance()) #endif // ENABLE_MODELVOLUME_TRANSFORM { - // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first + // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); #if ENABLE_MODELVOLUME_TRANSFORM m_new_position = volume->get_instance_offset(); m_new_rotation = volume->get_instance_rotation(); m_new_scale = volume->get_instance_scaling_factor(); - m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->bounding_box.size(); + int obj_idx = volume->object_idx(); + if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size())) + m_new_size = volume->get_instance_transformation().get_matrix(true, true) * (*wxGetApp().model_objects())[obj_idx]->raw_mesh().bounding_box().size(); + else + // this should never happen + m_new_size = Vec3d::Zero(); #else m_new_position = volume->get_offset(); m_new_rotation = volume->get_rotation(); @@ -249,7 +254,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation(); m_new_scale = volume->get_volume_scaling_factor(); - m_new_size = volume->bounding_box.size(); + m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size(); #else m_new_position = volume->get_offset(); m_new_rotation = volume->get_rotation(); From 611d9aa0d814d945e1e60f3b7da66d22908c0c17 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Jan 2019 10:49:13 +0100 Subject: [PATCH 111/132] Model's volume transform set as default --- src/libslic3r/Format/3mf.cpp | 45 ------ src/libslic3r/Geometry.cpp | 2 - src/libslic3r/Geometry.hpp | 2 - src/libslic3r/Model.cpp | 184 +--------------------- src/libslic3r/Model.hpp | 71 --------- src/libslic3r/PrintObject.cpp | 4 - src/libslic3r/Technologies.hpp | 4 +- src/slic3r/GUI/3DScene.cpp | 167 -------------------- src/slic3r/GUI/3DScene.hpp | 39 +---- src/slic3r/GUI/GLCanvas3D.cpp | 176 +-------------------- src/slic3r/GUI/GLCanvas3D.hpp | 27 ---- src/slic3r/GUI/GLGizmo.cpp | 23 --- src/slic3r/GUI/GUI_ObjectList.cpp | 12 -- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 32 +--- src/slic3r/GUI/Plater.cpp | 10 -- 15 files changed, 6 insertions(+), 792 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index f5750162f..3b99ccd82 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1336,49 +1336,12 @@ namespace Slic3r { void _3MF_Importer::_apply_transform(ModelInstance& instance, const Transform3d& transform) { -#if ENABLE_MODELVOLUME_TRANSFORM Slic3r::Geometry::Transformation t(transform); // invalid scale value, return if (!t.get_scaling_factor().all()) return; instance.set_transformation(t); -#else - // translation - Vec3d offset = transform.matrix().block(0, 3, 3, 1); - - Eigen::Matrix m3x3 = transform.matrix().block(0, 0, 3, 3); - // mirror - // it is impossible to reconstruct the original mirroring factors from a matrix, - // we can only detect if the matrix contains a left handed reference system - // in which case we reorient it back to right handed by mirroring the x axis - Vec3d mirror = Vec3d::Ones(); - if (m3x3.col(0).dot(m3x3.col(1).cross(m3x3.col(2))) < 0.0) - { - mirror(0) = -1.0; - // remove mirror - m3x3.col(0) *= -1.0; - } - - // scale - Vec3d scale(m3x3.col(0).norm(), m3x3.col(1).norm(), m3x3.col(2).norm()); - - // invalid scale value, return - if ((scale(0) == 0.0) || (scale(1) == 0.0) || (scale(2) == 0.0)) - return; - - // remove scale - m3x3.col(0).normalize(); - m3x3.col(1).normalize(); - m3x3.col(2).normalize(); - - Vec3d rotation = Slic3r::Geometry::extract_euler_angles(m3x3); - - instance.set_offset(offset); - instance.set_scaling_factor(scale); - instance.set_rotation(rotation); - instance.set_mirror(mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM } bool _3MF_Importer::_handle_start_config(const char** attributes, unsigned int num_attributes) @@ -1875,23 +1838,15 @@ namespace Slic3r { vertices_count += stl.stats.shared_vertices; -#if ENABLE_MODELVOLUME_TRANSFORM const Transform3d& matrix = volume->get_matrix(); -#endif // ENABLE_MODELVOLUME_TRANSFORM for (int i = 0; i < stl.stats.shared_vertices; ++i) { stream << " <" << VERTEX_TAG << " "; -#if ENABLE_MODELVOLUME_TRANSFORM Vec3d v = matrix * stl.v_shared[i].cast(); stream << "x=\"" << v(0) << "\" "; stream << "y=\"" << v(1) << "\" "; stream << "z=\"" << v(2) << "\" />\n"; -#else - stream << "x=\"" << stl.v_shared[i](0) << "\" "; - stream << "y=\"" << stl.v_shared[i](1) << "\" "; - stream << "z=\"" << stl.v_shared[i](2) << "\" />\n"; -#endif // ENABLE_MODELVOLUME_TRANSFORM } } diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 256f48e85..317ca4f65 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1213,7 +1213,6 @@ Vec3d extract_euler_angles(const Transform3d& transform) return extract_euler_angles(m); } -#if ENABLE_MODELVOLUME_TRANSFORM Transformation::Flags::Flags() : dont_translate(true) , dont_rotate(true) @@ -1383,6 +1382,5 @@ Transformation Transformation::operator * (const Transformation& other) const { return Transformation(get_matrix() * other.get_matrix()); } -#endif // ENABLE_MODELVOLUME_TRANSFORM } } diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index c21c3946d..111fc3ca4 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -197,7 +197,6 @@ Vec3d extract_euler_angles(const Eigen::Matrix& // Warning -> The transform should not contain any shear !!! Vec3d extract_euler_angles(const Transform3d& transform); -#if ENABLE_MODELVOLUME_TRANSFORM class Transformation { struct Flags @@ -256,7 +255,6 @@ public: Transformation operator * (const Transformation& other) const; }; -#endif // ENABLE_MODELVOLUME_TRANSFORM } } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index e56a4b6e1..20c4a44b2 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -757,16 +757,11 @@ const BoundingBoxf3& ModelObject::bounding_box() const BoundingBoxf3 raw_bbox; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh m = v->mesh; m.transform(v->get_matrix()); raw_bbox.merge(m.bounding_box()); } -#else - // mesh.bounding_box() returns a cached value. - raw_bbox.merge(v->mesh.bounding_box()); -#endif // ENABLE_MODELVOLUME_TRANSFORM BoundingBoxf3 bb; for (const ModelInstance *i : this->instances) bb.merge(i->transform_bounding_box(raw_bbox)); @@ -797,15 +792,11 @@ TriangleMesh ModelObject::raw_mesh() const TriangleMesh mesh; for (const ModelVolume *v : this->volumes) if (v->is_model_part()) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } -#else - mesh.merge(v->mesh); -#endif // ENABLE_MODELVOLUME_TRANSFORM return mesh; } @@ -814,17 +805,11 @@ TriangleMesh ModelObject::full_raw_mesh() const { TriangleMesh mesh; for (const ModelVolume *v : this->volumes) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } -#else - { - mesh.merge(v->mesh); - } -#endif // ENABLE_MODELVOLUME_TRANSFORM return mesh; } @@ -838,13 +823,9 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const if (this->instances.empty()) throw std::invalid_argument("Can't call raw_bounding_box() with no instances"); -#if ENABLE_MODELVOLUME_TRANSFORM TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); bb.merge(this->instances.front()->transform_mesh_bounding_box(vol_mesh, true)); -#else - bb.merge(this->instances.front()->transform_mesh_bounding_box(v->mesh, true)); -#endif // ENABLE_MODELVOLUME_TRANSFORM } return bb; } @@ -853,7 +834,6 @@ BoundingBoxf3 ModelObject::raw_bounding_box() const BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_translate) const { BoundingBoxf3 bb; -#if ENABLE_MODELVOLUME_TRANSFORM for (ModelVolume *v : this->volumes) { if (v->is_model_part()) @@ -863,11 +843,6 @@ BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx, bool dont_ bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(mesh, dont_translate)); } } -#else - for (ModelVolume *v : this->volumes) - if (v->is_model_part()) - bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&v->mesh, dont_translate)); -#endif // ENABLE_MODELVOLUME_TRANSFORM return bb; } @@ -885,15 +860,6 @@ void ModelObject::center_around_origin() this->translate(shift); this->origin_translation += shift; - -#if !ENABLE_MODELVOLUME_TRANSFORM - if (!this->instances.empty()) { - for (ModelInstance *i : this->instances) { - i->set_offset(i->get_offset() - shift); - } - this->invalidate_bounding_box(); - } -#endif // !ENABLE_MODELVOLUME_TRANSFORM } void ModelObject::ensure_on_bed() @@ -933,10 +899,6 @@ void ModelObject::scale(const Vec3d &versor) { v->scale(versor); } -#if !ENABLE_MODELVOLUME_TRANSFORM - // reset origin translation since it doesn't make sense anymore - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -948,10 +910,6 @@ void ModelObject::rotate(double angle, Axis axis) } center_around_origin(); - -#if !ENABLE_MODELVOLUME_TRANSFORM - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -963,10 +921,6 @@ void ModelObject::rotate(double angle, const Vec3d& axis) } center_around_origin(); - -#if !ENABLE_MODELVOLUME_TRANSFORM - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -977,9 +931,6 @@ void ModelObject::mirror(Axis axis) v->mirror(axis); } -#if !ENABLE_MODELVOLUME_TRANSFORM - this->origin_translation = Vec3d::Zero(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM this->invalidate_bounding_box(); } @@ -1179,7 +1130,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects) new_object->instances.reserve(this->instances.size()); for (const ModelInstance *model_instance : this->instances) new_object->add_instance(*model_instance); -#if ENABLE_MODELVOLUME_TRANSFORM ModelVolume* new_vol = new_object->add_volume(*volume, std::move(*mesh)); new_vol->center_geometry(); @@ -1190,9 +1140,6 @@ void ModelObject::split(ModelObjectPtrs* new_objects) } new_vol->set_offset(Vec3d::Zero()); -#else - new_object->add_volume(*volume, std::move(*mesh)); -#endif // ENABLE_MODELVOLUME_TRANSFORM new_objects->emplace_back(new_object); delete mesh; } @@ -1233,7 +1180,6 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const if (!v->is_model_part()) continue; -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d mv = mi * v->get_matrix(); const TriangleMesh& hull = v->get_convex_hull(); for (uint32_t f = 0; f < hull.stl.stats.number_of_facets; ++f) @@ -1243,15 +1189,6 @@ double ModelObject::get_instance_min_z(size_t instance_idx) const min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[1].cast())); min_z = std::min(min_z, Vec3d::UnitZ().dot(mv * facet->vertex[2].cast())); } -#else - for (uint32_t f = 0; f < v->mesh.stl.stats.number_of_facets; ++f) - { - const stl_facet* facet = v->mesh.stl.facet_start + f; - min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[0].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[1].cast())); - min_z = std::min(min_z, Vec3d::UnitZ().dot(mi * facet->vertex[2].cast())); - } -#endif // ENABLE_MODELVOLUME_TRANSFORM } return min_z + inst->get_offset(Z); @@ -1268,11 +1205,7 @@ unsigned int ModelObject::check_instances_print_volume_state(const BoundingBoxf3 unsigned int inside_outside = 0; for (const ModelVolume *vol : this->volumes) if (vol->is_model_part()) { -#if ENABLE_MODELVOLUME_TRANSFORM BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix() * vol->get_matrix()); -#else - BoundingBoxf3 bb = vol->get_convex_hull().transformed_bounding_box(model_instance->get_matrix()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (print_volume.contains(bb)) inside_outside |= INSIDE; else if (print_volume.intersects(bb)) @@ -1364,7 +1297,6 @@ int ModelVolume::extruder_id() const return extruder_id; } -#if ENABLE_MODELVOLUME_TRANSFORM void ModelVolume::center_geometry() { Vec3d shift = -mesh.bounding_box().center(); @@ -1372,7 +1304,6 @@ void ModelVolume::center_geometry() m_convex_hull.translate((float)shift(0), (float)shift(1), (float)shift(2)); translate(-shift); } -#endif // ENABLE_MODELVOLUME_TRANSFORM void ModelVolume::calculate_convex_hull() { @@ -1432,9 +1363,7 @@ size_t ModelVolume::split(unsigned int max_extruders) std::string name = this->name; Model::reset_auto_extruder_id(); -#if ENABLE_MODELVOLUME_TRANSFORM Vec3d offset = this->get_offset(); -#endif // ENABLE_MODELVOLUME_TRANSFORM for (TriangleMesh *mesh : meshptrs) { mesh->repair(); @@ -1448,11 +1377,9 @@ size_t ModelVolume::split(unsigned int max_extruders) else this->object->volumes.insert(this->object->volumes.begin() + (++ivolume), new ModelVolume(object, *this, std::move(*mesh))); -#if ENABLE_MODELVOLUME_TRANSFORM this->object->volumes[ivolume]->set_offset(Vec3d::Zero()); this->object->volumes[ivolume]->center_geometry(); this->object->volumes[ivolume]->translate(offset); -#endif // ENABLE_MODELVOLUME_TRANSFORM this->object->volumes[ivolume]->name = name + "_" + std::to_string(idx + 1); this->object->volumes[ivolume]->config.set_deserialize("extruder", Model::get_auto_extruder_id_as_string(max_extruders)); delete mesh; @@ -1464,52 +1391,31 @@ size_t ModelVolume::split(unsigned int max_extruders) void ModelVolume::translate(const Vec3d& displacement) { -#if ENABLE_MODELVOLUME_TRANSFORM set_offset(get_offset() + displacement); -#else - mesh.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); - m_convex_hull.translate((float)displacement(0), (float)displacement(1), (float)displacement(2)); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::scale(const Vec3d& scaling_factors) { -#if ENABLE_MODELVOLUME_TRANSFORM set_scaling_factor(get_scaling_factor().cwiseProduct(scaling_factors)); -#else - mesh.scale(scaling_factors); - m_convex_hull.scale(scaling_factors); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::rotate(double angle, Axis axis) { -#if ENABLE_MODELVOLUME_TRANSFORM switch (axis) { case X: { rotate(angle, Vec3d::UnitX()); break; } case Y: { rotate(angle, Vec3d::UnitY()); break; } case Z: { rotate(angle, Vec3d::UnitZ()); break; } } -#else - mesh.rotate(angle, axis); - m_convex_hull.rotate(angle, axis); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::rotate(double angle, const Vec3d& axis) { -#if ENABLE_MODELVOLUME_TRANSFORM set_rotation(get_rotation() + Geometry::extract_euler_angles(Eigen::Quaterniond(Eigen::AngleAxisd(angle, axis)).toRotationMatrix())); -#else - mesh.rotate(angle, axis); - m_convex_hull.rotate(angle, axis); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::mirror(Axis axis) { -#if ENABLE_MODELVOLUME_TRANSFORM Vec3d mirror = get_mirror(); switch (axis) { @@ -1518,10 +1424,6 @@ void ModelVolume::mirror(Axis axis) case Z: { mirror(2) *= -1.0; break; } } set_mirror(mirror); -#else - mesh.mirror(axis); - m_convex_hull.mirror(axis); -#endif // ENABLE_MODELVOLUME_TRANSFORM } void ModelVolume::scale_geometry(const Vec3d& versor) @@ -1530,59 +1432,6 @@ void ModelVolume::scale_geometry(const Vec3d& versor) m_convex_hull.scale(versor); } -#if !ENABLE_MODELVOLUME_TRANSFORM -void ModelInstance::set_rotation(const Vec3d& rotation) -{ - set_rotation(X, rotation(0)); - set_rotation(Y, rotation(1)); - set_rotation(Z, rotation(2)); -} - -void ModelInstance::set_rotation(Axis axis, double rotation) -{ - static const double TWO_PI = 2.0 * (double)PI; - while (rotation < 0.0) - { - rotation += TWO_PI; - } - while (TWO_PI < rotation) - { - rotation -= TWO_PI; - } - m_rotation(axis) = rotation; -} - -void ModelInstance::set_scaling_factor(const Vec3d& scaling_factor) -{ - set_scaling_factor(X, scaling_factor(0)); - set_scaling_factor(Y, scaling_factor(1)); - set_scaling_factor(Z, scaling_factor(2)); -} - -void ModelInstance::set_scaling_factor(Axis axis, double scaling_factor) -{ - m_scaling_factor(axis) = std::abs(scaling_factor); -} - -void ModelInstance::set_mirror(const Vec3d& mirror) -{ - set_mirror(X, mirror(0)); - set_mirror(Y, mirror(1)); - set_mirror(Z, mirror(2)); -} - -void ModelInstance::set_mirror(Axis axis, double mirror) -{ - double abs_mirror = std::abs(mirror); - if (abs_mirror == 0.0) - mirror = 1.0; - else if (abs_mirror != 1.0) - mirror /= abs_mirror; - - m_mirror(axis) = mirror; -} -#endif // !ENABLE_MODELVOLUME_TRANSFORM - void ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const { mesh->transform(get_matrix(dont_translate)); @@ -1599,29 +1448,17 @@ BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh& mes // Scale the bounding box along the three axes. for (unsigned int i = 0; i < 3; ++i) { -#if ENABLE_MODELVOLUME_TRANSFORM if (std::abs(get_scaling_factor((Axis)i)-1.0) > EPSILON) { bbox.min(i) *= get_scaling_factor((Axis)i); bbox.max(i) *= get_scaling_factor((Axis)i); -#else - if (std::abs(this->m_scaling_factor(i) - 1.0) > EPSILON) - { - bbox.min(i) *= this->m_scaling_factor(i); - bbox.max(i) *= this->m_scaling_factor(i); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } // Translate the bounding box. if (! dont_translate) { -#if ENABLE_MODELVOLUME_TRANSFORM bbox.min += get_offset(); bbox.max += get_offset(); -#else - bbox.min += this->m_offset; - bbox.max += this->m_offset; -#endif // ENABLE_MODELVOLUME_TRANSFORM } } return bbox; @@ -1639,30 +1476,12 @@ Vec3d ModelInstance::transform_vector(const Vec3d& v, bool dont_translate) const void ModelInstance::transform_polygon(Polygon* polygon) const { -#if ENABLE_MODELVOLUME_TRANSFORM // CHECK_ME -> Is the following correct or it should take in account all three rotations ? polygon->rotate(get_rotation(Z)); // rotate around polygon origin // CHECK_ME -> Is the following correct ? polygon->scale(get_scaling_factor(X), get_scaling_factor(Y)); // scale around polygon origin -#else - // CHECK_ME -> Is the following correct or it should take in account all three rotations ? - polygon->rotate(this->m_rotation(2)); // rotate around polygon origin - // CHECK_ME -> Is the following correct ? - polygon->scale(this->m_scaling_factor(0), this->m_scaling_factor(1)); // scale around polygon origin -#endif // ENABLE_MODELVOLUME_TRANSFORM } -#if !ENABLE_MODELVOLUME_TRANSFORM -Transform3d ModelInstance::get_matrix(bool dont_translate, bool dont_rotate, bool dont_scale, bool dont_mirror) const -{ - Vec3d translation = dont_translate ? Vec3d::Zero() : m_offset; - Vec3d rotation = dont_rotate ? Vec3d::Zero() : m_rotation; - Vec3d scale = dont_scale ? Vec3d::Ones() : m_scaling_factor; - Vec3d mirror = dont_mirror ? Vec3d::Ones() : m_mirror; - return Geometry::assemble_transform(translation, rotation, scale, mirror); -} -#endif // !ENABLE_MODELVOLUME_TRANSFORM - // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. bool model_object_list_equal(const Model &model_old, const Model &model_new) @@ -1706,10 +1525,9 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO return true; //FIXME test for the content of the mesh! -#if ENABLE_MODELVOLUME_TRANSFORM if (!mv_old.get_matrix().isApprox(mv_new.get_matrix())) return true; -#endif // ENABLE_MODELVOLUME_TRANSFORM + ++i_old; ++ i_new; } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index c26eb0f7a..e65228d80 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -11,9 +11,7 @@ #include #include #include -#if ENABLE_MODELVOLUME_TRANSFORM #include "Geometry.hpp" -#endif // ENABLE_MODELVOLUME_TRANSFORM namespace Slic3r { @@ -336,10 +334,8 @@ public: void scale_geometry(const Vec3d& versor); -#if ENABLE_MODELVOLUME_TRANSFORM // translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box void center_geometry(); -#endif // ENABLE_MODELVOLUME_TRANSFORM void calculate_convex_hull(); const TriangleMesh& get_convex_hull() const; @@ -348,7 +344,6 @@ public: static Type type_from_string(const std::string &s); static std::string type_to_string(const Type t); -#if ENABLE_MODELVOLUME_TRANSFORM const Geometry::Transformation& get_transformation() const { return m_transformation; } void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } @@ -377,7 +372,6 @@ public: void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } -#endif // ENABLE_MODELVOLUME_TRANSFORM protected: friend class Print; @@ -395,9 +389,7 @@ private: t_model_material_id m_material_id; // The convex hull of this model's mesh. TriangleMesh m_convex_hull; -#if ENABLE_MODELVOLUME_TRANSFORM Geometry::Transformation m_transformation; -#endif // ENABLE_MODELVOLUME_TRANSFORM ModelVolume(ModelObject *object, const TriangleMesh &mesh) : mesh(mesh), m_type(MODEL_PART), object(object) { @@ -407,7 +399,6 @@ private: ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : mesh(std::move(mesh)), m_convex_hull(std::move(convex_hull)), m_type(MODEL_PART), object(object) {} -#if ENABLE_MODELVOLUME_TRANSFORM // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : ModelBase(other), // copy the ID @@ -423,25 +414,6 @@ private: if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } -#else - // Copying an existing volume, therefore this volume will get a copy of the ID assigned. - ModelVolume(ModelObject *object, const ModelVolume &other) : - ModelBase(other), // copy the ID - name(other.name), mesh(other.mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object) - { - if (! other.material_id().empty()) - this->set_material_id(other.material_id()); - } - // Providing a new mesh, therefore this volume will get a new unique ID assigned. - ModelVolume(ModelObject *object, const ModelVolume &other, TriangleMesh &&mesh) : - name(other.name), mesh(std::move(mesh)), config(other.config), m_type(other.m_type), object(object) - { - if (! other.material_id().empty()) - this->set_material_id(other.material_id()); - if (mesh.stl.stats.number_of_facets > 1) - calculate_convex_hull(); - } -#endif // ENABLE_MODELVOLUME_TRANSFORM ModelVolume& operator=(ModelVolume &rhs) = delete; }; @@ -460,14 +432,7 @@ public: }; private: -#if ENABLE_MODELVOLUME_TRANSFORM Geometry::Transformation m_transformation; -#else - Vec3d m_offset; // in unscaled coordinates - Vec3d m_rotation; // Rotation around the three axes, in radians around mesh center point - Vec3d m_scaling_factor; // Scaling factors along the three axes - Vec3d m_mirror; // Mirroring along the three axes -#endif // ENABLE_MODELVOLUME_TRANSFORM public: // flag showing the position of this instance with respect to the print volume (set by Print::validate() using ModelObject::check_instances_print_volume_state()) @@ -475,7 +440,6 @@ public: ModelObject* get_object() const { return this->object; } -#if ENABLE_MODELVOLUME_TRANSFORM const Geometry::Transformation& get_transformation() const { return m_transformation; } void set_transformation(const Geometry::Transformation& transformation) { m_transformation = transformation; } @@ -502,31 +466,6 @@ public: void set_mirror(const Vec3d& mirror) { m_transformation.set_mirror(mirror); } void set_mirror(Axis axis, double mirror) { m_transformation.set_mirror(axis, mirror); } -#else - const Vec3d& get_offset() const { return m_offset; } - double get_offset(Axis axis) const { return m_offset(axis); } - - void set_offset(const Vec3d& offset) { m_offset = offset; } - void set_offset(Axis axis, double offset) { m_offset(axis) = offset; } - - const Vec3d& get_rotation() const { return m_rotation; } - double get_rotation(Axis axis) const { return m_rotation(axis); } - - void set_rotation(const Vec3d& rotation); - void set_rotation(Axis axis, double rotation); - - Vec3d get_scaling_factor() const { return m_scaling_factor; } - double get_scaling_factor(Axis axis) const { return m_scaling_factor(axis); } - - void set_scaling_factor(const Vec3d& scaling_factor); - void set_scaling_factor(Axis axis, double scaling_factor); - - const Vec3d& get_mirror() const { return m_mirror; } - double get_mirror(Axis axis) const { return m_mirror(axis); } - - void set_mirror(const Vec3d& mirror); - void set_mirror(Axis axis, double mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; @@ -539,11 +478,7 @@ public: // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; -#if ENABLE_MODELVOLUME_TRANSFORM const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } -#else - Transform3d get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const; -#endif // ENABLE_MODELVOLUME_TRANSFORM bool is_printable() const { return print_volume_state == PVS_Inside; } @@ -559,17 +494,11 @@ private: // Parent object, owning this instance. ModelObject* object; -#if ENABLE_MODELVOLUME_TRANSFORM // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} -#else - explicit ModelInstance(ModelObject *object) : m_offset(Vec3d::Zero()), m_rotation(Vec3d::Zero()), m_scaling_factor(Vec3d::Ones()), m_mirror(Vec3d::Ones()), object(object), print_volume_state(PVS_Inside) {} - explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_offset(other.m_offset), m_rotation(other.m_rotation), m_scaling_factor(other.m_scaling_factor), m_mirror(other.m_mirror), object(object), print_volume_state(PVS_Inside) {} -#endif // ENABLE_MODELVOLUME_TRANSFORM ModelInstance() = delete; explicit ModelInstance(ModelInstance &&rhs) = delete; diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 3019233d1..a7214afa3 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1578,15 +1578,11 @@ std::vector PrintObject::_slice_volumes(const std::vector &z, //FIXME better to perform slicing over each volume separately and then to use a Boolean operation to merge them. TriangleMesh mesh; for (const ModelVolume *v : volumes) -#if ENABLE_MODELVOLUME_TRANSFORM { TriangleMesh vol_mesh(v->mesh); vol_mesh.transform(v->get_matrix()); mesh.merge(vol_mesh); } -#else - mesh.merge(v->mesh); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (mesh.stl.stats.number_of_facets > 0) { mesh.transform(m_trafo); // apply XY shift diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 16c610517..a2952a7d3 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -21,10 +21,8 @@ #define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0) // Modified camera target behavior #define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) -// Add Geometry::Transformation class and use it into ModelInstance, ModelVolume and GLVolume -#define ENABLE_MODELVOLUME_TRANSFORM (1 && ENABLE_1_42_0) // Keeps objects on bed while scaling them using the scale gizmo -#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_MODELVOLUME_TRANSFORM) +#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0) // All rotations made using the rotate gizmo are done with respect to the world reference system #define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0) // Scene's GUI made using imgui library diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d0ae03e26..42b025093 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -205,17 +205,7 @@ const float GLVolume::SLA_SUPPORT_COLOR[4] = { 0.75f, 0.75f, 0.75f, 1.0f }; const float GLVolume::SLA_PAD_COLOR[4] = { 0.0f, 0.2f, 0.0f, 1.0f }; GLVolume::GLVolume(float r, float g, float b, float a) -#if ENABLE_MODELVOLUME_TRANSFORM : m_transformed_bounding_box_dirty(true) -#else - : m_offset(Vec3d::Zero()) - , m_rotation(Vec3d::Zero()) - , m_scaling_factor(Vec3d::Ones()) - , m_mirror(Vec3d::Ones()) - , m_world_matrix(Transform3f::Identity()) - , m_world_matrix_dirty(true) - , m_transformed_bounding_box_dirty(true) -#endif // ENABLE_MODELVOLUME_TRANSFORM , m_sla_shift_z(0.0) , m_transformed_convex_hull_bounding_box_dirty(true) , m_convex_hull(nullptr) @@ -300,125 +290,18 @@ void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) color[3] = model_volume->is_model_part() ? 1.f : 0.5f; } -#if !ENABLE_MODELVOLUME_TRANSFORM -const Vec3d& GLVolume::get_rotation() const -{ - return m_rotation; -} - -void GLVolume::set_rotation(const Vec3d& rotation) -{ - static const double TWO_PI = 2.0 * (double)PI; - - if (m_rotation != rotation) - { - m_rotation = rotation; - for (int i = 0; i < 3; ++i) - { - while (m_rotation(i) < 0.0) - { - m_rotation(i) += TWO_PI; - } - while (TWO_PI < m_rotation(i)) - { - m_rotation(i) -= TWO_PI; - } - } - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_offset() const -{ - return m_offset; -} - -void GLVolume::set_offset(const Vec3d& offset) -{ - if (m_offset != offset) - { - m_offset = offset; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_scaling_factor() const -{ - return m_scaling_factor; -} - -void GLVolume::set_scaling_factor(const Vec3d& scaling_factor) -{ - if (m_scaling_factor != scaling_factor) - { - m_scaling_factor = scaling_factor; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -const Vec3d& GLVolume::get_mirror() const -{ - return m_mirror; -} - -double GLVolume::get_mirror(Axis axis) const -{ - return m_mirror(axis); -} - -void GLVolume::set_mirror(const Vec3d& mirror) -{ - if (m_mirror != mirror) - { - m_mirror = mirror; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} - -void GLVolume::set_mirror(Axis axis, double mirror) -{ - if (m_mirror(axis) != mirror) - { - m_mirror(axis) = mirror; - m_world_matrix_dirty = true; - m_transformed_bounding_box_dirty = true; - m_transformed_convex_hull_bounding_box_dirty = true; - } -} -#endif // !ENABLE_MODELVOLUME_TRANSFORM - void GLVolume::set_convex_hull(const TriangleMesh *convex_hull, bool owned) { m_convex_hull = convex_hull; m_convex_hull_owned = owned; } -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d GLVolume::world_matrix() const { Transform3d m = m_instance_transformation.get_matrix() * m_volume_transformation.get_matrix(); m.translation()(2) += m_sla_shift_z; return m; } -#else -const Transform3f& GLVolume::world_matrix() const -{ - if (m_world_matrix_dirty) - { - m_world_matrix = Geometry::assemble_transform(m_offset, m_rotation, m_scaling_factor, m_mirror).cast(); - m_world_matrix_dirty = false; - } - return m_world_matrix; -} -#endif // ENABLE_MODELVOLUME_TRANSFORM const BoundingBoxf3& GLVolume::transformed_bounding_box() const { @@ -426,11 +309,7 @@ const BoundingBoxf3& GLVolume::transformed_bounding_box() const if (m_transformed_bounding_box_dirty) { -#if ENABLE_MODELVOLUME_TRANSFORM m_transformed_bounding_box = bounding_box.transformed(world_matrix()); -#else - m_transformed_bounding_box = bounding_box.transformed(world_matrix().cast()); -#endif // ENABLE_MODELVOLUME_TRANSFORM m_transformed_bounding_box_dirty = false; } @@ -441,17 +320,10 @@ const BoundingBoxf3& GLVolume::transformed_convex_hull_bounding_box() const { if (m_transformed_convex_hull_bounding_box_dirty) { -#if ENABLE_MODELVOLUME_TRANSFORM if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix()); else m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix()); -#else - if ((m_convex_hull != nullptr) && (m_convex_hull->stl.stats.number_of_facets > 0)) - m_transformed_convex_hull_bounding_box = m_convex_hull->transformed_bounding_box(world_matrix().cast()); - else - m_transformed_convex_hull_bounding_box = bounding_box.transformed(world_matrix().cast()); -#endif // ENABLE_MODELVOLUME_TRANSFORM m_transformed_convex_hull_bounding_box_dirty = false; } @@ -502,11 +374,7 @@ void GLVolume::render() const ::glCullFace(GL_BACK); ::glPushMatrix(); -#if ENABLE_MODELVOLUME_TRANSFORM ::glMultMatrixd(world_matrix().data()); -#else - ::glMultMatrixf(world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (this->indexed_vertex_array.indexed()) this->indexed_vertex_array.render(this->tverts_range, this->qverts_range); else @@ -544,11 +412,7 @@ void GLVolume::render_using_layer_height() const glUniform1f(z_cursor_band_width_id, (GLfloat)layer_height_texture_data.edit_band_width); if (world_matrix_id >= 0) -#if ENABLE_MODELVOLUME_TRANSFORM ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); -#else - ::glUniformMatrix4fv(world_matrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM GLsizei w = (GLsizei)layer_height_texture_width(); GLsizei h = (GLsizei)layer_height_texture_height(); @@ -608,11 +472,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); if (worldmatrix_id != -1) -#if ENABLE_MODELVOLUME_TRANSFORM ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); -#else - ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM render(); @@ -631,11 +491,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glUniform1i(detection_id, shader_outside_printer_detection_enabled ? 1 : 0); if (worldmatrix_id != -1) -#if ENABLE_MODELVOLUME_TRANSFORM ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().cast().data()); -#else - ::glUniformMatrix4fv(worldmatrix_id, 1, GL_FALSE, (const GLfloat*)world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM ::glBindBuffer(GL_ARRAY_BUFFER, indexed_vertex_array.vertices_and_normals_interleaved_VBO_id); ::glVertexPointer(3, GL_FLOAT, 6 * sizeof(float), (const void*)(3 * sizeof(float))); @@ -643,11 +499,7 @@ void GLVolume::render_VBOs(int color_id, int detection_id, int worldmatrix_id) c ::glPushMatrix(); -#if ENABLE_MODELVOLUME_TRANSFORM ::glMultMatrixd(world_matrix().data()); -#else - ::glMultMatrixf(world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (n_triangles > 0) { @@ -691,11 +543,7 @@ void GLVolume::render_legacy() const ::glPushMatrix(); -#if ENABLE_MODELVOLUME_TRANSFORM ::glMultMatrixd(world_matrix().data()); -#else - ::glMultMatrixf(world_matrix().data()); -#endif // ENABLE_MODELVOLUME_TRANSFORM if (n_triangles > 0) ::glDrawElements(GL_TRIANGLES, n_triangles, GL_UNSIGNED_INT, indexed_vertex_array.triangle_indices.data() + tverts_range.first); @@ -780,11 +628,7 @@ int GLVolumeCollection::load_object_volume( const ModelVolume *model_volume = model_object->volumes[volume_idx]; const int extruder_id = model_volume->extruder_id(); const ModelInstance *instance = model_object->instances[instance_idx]; -#if ENABLE_MODELVOLUME_TRANSFORM const TriangleMesh& mesh = model_volume->mesh; -#else - TriangleMesh mesh = model_volume->mesh; -#endif // ENABLE_MODELVOLUME_TRANSFORM float color[4]; memcpy(color, colors[((color_by == "volume") ? volume_idx : obj_idx) % 4], sizeof(float) * 3); /* if (model_volume->is_support_blocker()) { @@ -822,15 +666,8 @@ int GLVolumeCollection::load_object_volume( } v.is_modifier = ! model_volume->is_model_part(); v.shader_outside_printer_detection_enabled = model_volume->is_model_part(); -#if ENABLE_MODELVOLUME_TRANSFORM v.set_instance_transformation(instance->get_transformation()); v.set_volume_transformation(model_volume->get_transformation()); -#else - v.set_offset(instance->get_offset()); - v.set_rotation(instance->get_rotation()); - v.set_scaling_factor(instance->get_scaling_factor()); - v.set_mirror(instance->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM return int(this->volumes.size() - 1); } @@ -941,11 +778,7 @@ int GLVolumeCollection::load_wipe_tower_preview( else v.indexed_vertex_array.load_mesh_flat_shading(mesh); -#if ENABLE_MODELVOLUME_TRANSFORM v.set_volume_offset(Vec3d(pos_x, pos_y, 0.0)); -#else - v.set_offset(Vec3d(pos_x, pos_y, 0.0)); -#endif // ENABLE_MODELVOLUME_TRANSFORM // finalize_geometry() clears the vertex arrays, therefore the bounding box has to be computed before finalize_geometry(). v.bounding_box = v.indexed_vertex_array.bounding_box(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index b5ae5b965..38996dc04 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -257,23 +257,9 @@ public: ~GLVolume(); private: -#if ENABLE_MODELVOLUME_TRANSFORM Geometry::Transformation m_instance_transformation; Geometry::Transformation m_volume_transformation; -#else - // Offset of the volume to be rendered. - Vec3d m_offset; - // Rotation around three axes of the volume to be rendered. - Vec3d m_rotation; - // Scale factor along the three axes of the volume to be rendered. - Vec3d m_scaling_factor; - // Mirroring along the three axes of the volume to be rendered. - Vec3d m_mirror; - // World matrix of the volume to be rendered. - mutable Transform3f m_world_matrix; - // Whether or not is needed to recalculate the world matrix. - mutable bool m_world_matrix_dirty; -#endif // ENABLE_MODELVOLUME_TRANSFORM + // Shift in z required by sla supports+pad double m_sla_shift_z; // Bounding box of this volume, in unscaled coordinates. @@ -357,7 +343,6 @@ public: // set color according to model volume void set_color_from_model_volume(const ModelVolume *model_volume); -#if ENABLE_MODELVOLUME_TRANSFORM const Geometry::Transformation& get_instance_transformation() const { return m_instance_transformation; } void set_instance_transformation(const Geometry::Transformation& transformation) { m_instance_transformation = transformation; set_bounding_boxes_as_dirty(); } @@ -411,21 +396,6 @@ public: void set_volume_mirror(const Vec3d& mirror) { m_volume_transformation.set_mirror(mirror); set_bounding_boxes_as_dirty(); } void set_volume_mirror(Axis axis, double mirror) { m_volume_transformation.set_mirror(axis, mirror); set_bounding_boxes_as_dirty(); } -#else - const Vec3d& get_rotation() const; - void set_rotation(const Vec3d& rotation); - - const Vec3d& get_scaling_factor() const; - void set_scaling_factor(const Vec3d& scaling_factor); - - const Vec3d& get_mirror() const; - double get_mirror(Axis axis) const; - void set_mirror(const Vec3d& mirror); - void set_mirror(Axis axis, double mirror); - - const Vec3d& get_offset() const; - void set_offset(const Vec3d& offset); -#endif // ENABLE_MODELVOLUME_TRANSFORM double get_sla_shift_z() const { return m_sla_shift_z; } void set_sla_shift_z(double z) { m_sla_shift_z = z; } @@ -436,11 +406,8 @@ public: int volume_idx() const { return this->composite_id.volume_id; } int instance_idx() const { return this->composite_id.instance_id; } -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d world_matrix() const; -#else - const Transform3f& world_matrix() const; -#endif // ENABLE_MODELVOLUME_TRANSFORM + const BoundingBoxf3& transformed_bounding_box() const; const BoundingBoxf3& transformed_convex_hull_bounding_box() const; @@ -491,9 +458,7 @@ public: void reset_layer_height_texture_data() { layer_height_texture_data.reset(); } -#if ENABLE_MODELVOLUME_TRANSFORM void set_bounding_boxes_as_dirty() { m_transformed_bounding_box_dirty = true; m_transformed_convex_hull_bounding_box_dirty = true; } -#endif // ENABLE_MODELVOLUME_TRANSFORM }; typedef std::vector GLVolumePtrs; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0529eeb72..f5615f0d6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1104,7 +1104,6 @@ bool GLCanvas3D::Mouse::is_start_position_3D_defined() const return (drag.start_position_3D != Drag::Invalid_3D_Point); } -#if ENABLE_MODELVOLUME_TRANSFORM GLCanvas3D::Selection::VolumeCache::TransformCache::TransformCache() : position(Vec3d::Zero()) , rotation(Vec3d::Zero()) @@ -1132,25 +1131,6 @@ GLCanvas3D::Selection::VolumeCache::VolumeCache(const Geometry::Transformation& , m_instance(instance_transform) { } -#else -GLCanvas3D::Selection::VolumeCache::VolumeCache() - : m_position(Vec3d::Zero()) - , m_rotation(Vec3d::Zero()) - , m_scaling_factor(Vec3d::Ones()) -{ - m_rotation_matrix = Transform3d::Identity(); - m_scale_matrix = Transform3d::Identity(); -} - -GLCanvas3D::Selection::VolumeCache::VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor) - : m_position(position) - , m_rotation(rotation) - , m_scaling_factor(scaling_factor) -{ - m_rotation_matrix = Geometry::assemble_transform(Vec3d::Zero(), m_rotation); - m_scale_matrix = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), m_scaling_factor); -} -#endif // ENABLE_MODELVOLUME_TRANSFORM GLCanvas3D::Selection::Selection() : m_volumes(nullptr) @@ -1241,13 +1221,6 @@ void GLCanvas3D::Selection::add(unsigned int volume_idx, bool as_single_selectio _add_instance(volume->object_idx(), volume->instance_idx()); break; } -#if !ENABLE_MODELVOLUME_TRANSFORM - case Object: - { - _add_object(volume->object_idx()); - break; - } -#endif // !ENABLE_MODELVOLUME_TRANSFORM } _update_type(); @@ -1273,13 +1246,6 @@ void GLCanvas3D::Selection::remove(unsigned int volume_idx) _remove_instance(volume->object_idx(), volume->instance_idx()); break; } -#if !ENABLE_MODELVOLUME_TRANSFORM - case Object: - { - _remove_object(volume->object_idx()); - break; - } -#endif // !ENABLE_MODELVOLUME_TRANSFORM } _update_type(); @@ -1552,7 +1518,6 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local) for (unsigned int i : m_list) { -#if ENABLE_MODELVOLUME_TRANSFORM if ((m_mode == Volume) || (*m_volumes)[i]->is_wipe_tower) { if (local) @@ -1565,9 +1530,6 @@ void GLCanvas3D::Selection::translate(const Vec3d& displacement, bool local) } else if (m_mode == Instance) (*m_volumes)[i]->set_instance_offset(m_cache.volumes_data[i].get_instance_position() + displacement); -#else - (*m_volumes)[i]->set_offset(m_cache.volumes_data[i].get_position() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH @@ -1600,13 +1562,8 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) } } #else -#if ENABLE_MODELVOLUME_TRANSFORM (*m_volumes)[i]->set_instance_rotation(rotation); -#else - (*m_volumes)[i]->set_rotation(rotation); -#endif // ENABLE_MODELVOLUME_TRANSFORM #endif // ENABLE_WORLD_ROTATIONS -#if ENABLE_MODELVOLUME_TRANSFORM else if (is_single_volume() || is_single_modifier()) #if ENABLE_WORLD_ROTATIONS { @@ -1623,11 +1580,9 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) #else (*m_volumes)[i]->set_volume_rotation(rotation); #endif // ENABLE_WORLD_ROTATIONS -#endif // ENABLE_MODELVOLUME_TRANSFORM else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); -#if ENABLE_MODELVOLUME_TRANSFORM if (m_mode == Instance) { // extracts rotations from the composed transformation @@ -1648,12 +1603,6 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) } (*m_volumes)[i]->set_volume_rotation(new_rotation); } -#else - // extracts rotations from the composed transformation - Vec3d new_rotation = Geometry::extract_euler_angles(m * m_cache.volumes_data[i].get_rotation_matrix()); - (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); - (*m_volumes)[i]->set_rotation(new_rotation); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } @@ -1678,7 +1627,6 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) for (unsigned int i : m_list) { -#if ENABLE_MODELVOLUME_TRANSFORM Transform3d wst = m_cache.volumes_data[i].get_instance_scale_matrix() * m_cache.volumes_data[i].get_volume_scale_matrix(); Vec3d scaling_factor = Vec3d(1./wst(0,0), 1./wst(1,1), 1./wst(2,2)); @@ -1694,23 +1642,6 @@ void GLCanvas3D::Selection::flattening_rotate(const Vec3d& normal) Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_instance_rotation_matrix() ); (*m_volumes)[i]->set_instance_rotation(new_rotation); -#else - Transform3d wst = m_cache.volumes_data[i].get_scale_matrix() * m_cache.volumes_data[i].get_scale_matrix(); - Vec3d scaling_factor = Vec3d(1. / wst(0, 0), 1. / wst(1, 1), 1. / wst(2, 2)); - - Vec3d rotation = Geometry::extract_euler_angles(m_cache.volumes_data[i].get_rotation_matrix() * m_cache.volumes_data[i].get_rotation_matrix()); - Vec3d transformed_normal = Geometry::assemble_transform(Vec3d::Zero(), rotation, scaling_factor) * normal; - transformed_normal.normalize(); - - Vec3d axis = transformed_normal(2) > 0.999f ? Vec3d(1., 0., 0.) : Vec3d(transformed_normal.cross(Vec3d(0., 0., -1.))); - axis.normalize(); - - Transform3d extra_rotation = Transform3d::Identity(); - extra_rotation.rotate(Eigen::AngleAxisd(acos(-transformed_normal(2)), axis)); - - Vec3d new_rotation = Geometry::extract_euler_angles(extra_rotation * m_cache.volumes_data[i].get_rotation_matrix()); - (*m_volumes)[i]->set_rotation(new_rotation); -#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH @@ -1729,19 +1660,12 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local) for (unsigned int i : m_list) { if (is_single_full_instance()) -#if ENABLE_MODELVOLUME_TRANSFORM (*m_volumes)[i]->set_instance_scaling_factor(scale); -#else - (*m_volumes)[i]->set_scaling_factor(scale); -#endif // ENABLE_MODELVOLUME_TRANSFORM -#if ENABLE_MODELVOLUME_TRANSFORM else if (is_single_volume() || is_single_modifier()) (*m_volumes)[i]->set_volume_scaling_factor(scale); -#endif // ENABLE_MODELVOLUME_TRANSFORM else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), Vec3d::Zero(), scale); -#if ENABLE_MODELVOLUME_TRANSFORM if (m_mode == Instance) { Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_instance_scale_matrix()).matrix().block(0, 0, 3, 3); @@ -1764,13 +1688,6 @@ void GLCanvas3D::Selection::scale(const Vec3d& scale, bool local) } (*m_volumes)[i]->set_volume_scaling_factor(new_scale); } -#else - Eigen::Matrix new_matrix = (m * m_cache.volumes_data[i].get_scale_matrix()).matrix().block(0, 0, 3, 3); - // extracts scaling factors from the composed transformation - Vec3d new_scale(new_matrix.col(0).norm(), new_matrix.col(1).norm(), new_matrix.col(2).norm()); - (*m_volumes)[i]->set_offset(m_cache.dragging_center + m * (m_cache.volumes_data[i].get_position() - m_cache.dragging_center)); - (*m_volumes)[i]->set_scaling_factor(new_scale); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } @@ -1798,13 +1715,9 @@ void GLCanvas3D::Selection::mirror(Axis axis) for (unsigned int i : m_list) { if (single_full_instance) -#if ENABLE_MODELVOLUME_TRANSFORM (*m_volumes)[i]->set_instance_mirror(axis, -(*m_volumes)[i]->get_instance_mirror(axis)); else if (m_mode == Volume) (*m_volumes)[i]->set_volume_mirror(axis, -(*m_volumes)[i]->get_volume_mirror(axis)); -#else - (*m_volumes)[i]->set_mirror(axis, -(*m_volumes)[i]->get_mirror(axis)); -#endif // ENABLE_MODELVOLUME_TRANSFORM } #if !DISABLE_INSTANCES_SYNCH @@ -1826,11 +1739,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& disp { GLVolume* v = (*m_volumes)[i]; if (v->object_idx() == object_idx) -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM } std::set done; // prevent processing volumes twice @@ -1858,11 +1767,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, const Vec3d& disp if (v->object_idx() != object_idx) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } @@ -1879,11 +1784,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst { GLVolume* v = (*m_volumes)[i]; if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM } std::set done; // prevent processing volumes twice @@ -1911,11 +1812,7 @@ void GLCanvas3D::Selection::translate(unsigned int object_idx, unsigned int inst if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_offset(v->get_instance_offset() + displacement); -#else - v->set_offset(v->get_offset() + displacement); -#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } } @@ -2366,11 +2263,7 @@ void GLCanvas3D::Selection::_set_caches() for (unsigned int i : m_list) { const GLVolume* v = (*m_volumes)[i]; -#if ENABLE_MODELVOLUME_TRANSFORM m_cache.volumes_data.emplace(i, VolumeCache(v->get_volume_transformation(), v->get_instance_transformation())); -#else - m_cache.volumes_data.emplace(i, VolumeCache(v->get_offset(), v->get_rotation(), v->get_scaling_factor())); -#endif // ENABLE_MODELVOLUME_TRANSFORM } m_cache.dragging_center = get_bounding_box().center(); } @@ -2641,15 +2534,9 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() continue; int instance_idx = volume->instance_idx(); -#if ENABLE_MODELVOLUME_TRANSFORM const Vec3d& rotation = volume->get_instance_rotation(); const Vec3d& scaling_factor = volume->get_instance_scaling_factor(); const Vec3d& mirror = volume->get_instance_mirror(); -#else - const Vec3d& rotation = volume->get_rotation(); - const Vec3d& scaling_factor = volume->get_scaling_factor(); - const Vec3d& mirror = volume->get_mirror(); -#endif // ENABLE_MODELVOLUME_TRANSFORM // Process unselected instances. for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) @@ -2664,15 +2551,9 @@ void GLCanvas3D::Selection::_synchronize_unselected_instances() if ((v->object_idx() != object_idx) || (v->instance_idx() == instance_idx)) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_instance_rotation(Vec3d(rotation(0), rotation(1), v->get_instance_rotation()(2))); v->set_instance_scaling_factor(scaling_factor); v->set_instance_mirror(mirror); -#else - v->set_rotation(Vec3d(rotation(0), rotation(1), v->get_rotation()(2))); - v->set_scaling_factor(scaling_factor); - v->set_mirror(mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM done.insert(j); } @@ -2689,17 +2570,10 @@ void GLCanvas3D::Selection::_synchronize_unselected_volumes() continue; int volume_idx = volume->volume_idx(); -#if ENABLE_MODELVOLUME_TRANSFORM const Vec3d& offset = volume->get_volume_offset(); const Vec3d& rotation = volume->get_volume_rotation(); const Vec3d& scaling_factor = volume->get_volume_scaling_factor(); const Vec3d& mirror = volume->get_volume_mirror(); -#else - const Vec3d& offset = volume->get_offset(); - const Vec3d& rotation = volume->get_rotation(); - const Vec3d& scaling_factor = volume->get_scaling_factor(); - const Vec3d& mirror = volume->get_mirror(); -#endif // ENABLE_MODELVOLUME_TRANSFORM // Process unselected volumes. for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) @@ -2711,17 +2585,10 @@ void GLCanvas3D::Selection::_synchronize_unselected_volumes() if ((v->object_idx() != object_idx) || (v->volume_idx() != volume_idx)) continue; -#if ENABLE_MODELVOLUME_TRANSFORM v->set_volume_offset(offset); v->set_volume_rotation(rotation); v->set_volume_scaling_factor(scaling_factor); v->set_volume_mirror(mirror); -#else - v->set_offset(offset); - v->set_rotation(Vec3d(rotation)); - v->set_scaling_factor(scaling_factor); - v->set_mirror(mirror); -#endif // ENABLE_MODELVOLUME_TRANSFORM } } } @@ -5571,7 +5438,6 @@ void GLCanvas3D::do_move() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); @@ -5583,20 +5449,12 @@ void GLCanvas3D::do_move() object_moved = true; } if (object_moved) -#else - model_object->instances[instance_idx]->set_offset(v->get_offset()); - object_moved = true; -#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } else if (object_idx == 1000) // Move a wipe tower proxy. -#if ENABLE_MODELVOLUME_TRANSFORM wipe_tower_origin = v->get_volume_offset(); -#else - wipe_tower_origin = v->get_offset(); -#endif // ENABLE_MODELVOLUME_TRANSFORM } // Fixes sinking/flying instances @@ -5639,7 +5497,6 @@ void GLCanvas3D::do_rotate() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_rotation(v->get_instance_rotation()); @@ -5650,10 +5507,6 @@ void GLCanvas3D::do_rotate() model_object->volumes[volume_idx]->set_rotation(v->get_volume_rotation()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); } -#else - model_object->instances[instance_idx]->set_rotation(v->get_rotation()); - model_object->instances[instance_idx]->set_offset(v->get_offset()); -#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } @@ -5694,7 +5547,6 @@ void GLCanvas3D::do_scale() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_scaling_factor(v->get_instance_scaling_factor()); @@ -5706,10 +5558,6 @@ void GLCanvas3D::do_scale() model_object->volumes[volume_idx]->set_scaling_factor(v->get_volume_scaling_factor()); model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); } -#else - model_object->instances[instance_idx]->set_scaling_factor(v->get_scaling_factor()); - model_object->instances[instance_idx]->set_offset(v->get_offset()); -#endif // ENABLE_MODELVOLUME_TRANSFORM model_object->invalidate_bounding_box(); } } @@ -5755,14 +5603,11 @@ void GLCanvas3D::do_mirror() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { -#if ENABLE_MODELVOLUME_TRANSFORM if (selection_mode == Selection::Instance) model_object->instances[instance_idx]->set_mirror(v->get_instance_mirror()); else if (selection_mode == Selection::Volume) model_object->volumes[volume_idx]->set_mirror(v->get_volume_mirror()); -#else - model_object->instances[instance_idx]->set_mirror(v->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM + model_object->invalidate_bounding_box(); } } @@ -6801,7 +6646,6 @@ void GLCanvas3D::_update_gizmos_data() if (m_selection.is_single_full_instance()) { -#if ENABLE_MODELVOLUME_TRANSFORM // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; m_gizmos.set_scale(volume->get_instance_scaling_factor()); @@ -6817,24 +6661,7 @@ void GLCanvas3D::_update_gizmos_data() #else m_gizmos.set_model_object_ptr(model_object); #endif // ENABLE_SLA_SUPPORT_GIZMO_MOD -#else - ModelObject* model_object = m_model->objects[m_selection.get_object_idx()]; - ModelInstance* model_instance = model_object->instances[m_selection.get_instance_idx()]; - m_gizmos.set_scale(model_instance->get_scaling_factor()); -#if ENABLE_WORLD_ROTATIONS - m_gizmos.set_rotation(Vec3d::Zero()); -#else - m_gizmos.set_rotation(model_instance->get_rotation()); -#endif // ENABLE_WORLD_ROTATIONS - m_gizmos.set_flattening_data(model_object); -#if ENABLE_SLA_SUPPORT_GIZMO_MOD - m_gizmos.set_sla_support_data(model_object, m_selection); -#else - m_gizmos.set_model_object_ptr(model_object); -#endif // ENABLE_SLA_SUPPORT_GIZMO_MOD -#endif // ENABLE_MODELVOLUME_TRANSFORM } -#if ENABLE_MODELVOLUME_TRANSFORM else if (m_selection.is_single_volume() || m_selection.is_single_modifier()) { const GLVolume* volume = m_volumes.volumes[*m_selection.get_volume_idxs().begin()]; @@ -6851,7 +6678,6 @@ void GLCanvas3D::_update_gizmos_data() m_gizmos.set_model_object_ptr(nullptr); #endif // ENABLE_SLA_SUPPORT_GIZMO_MOD } -#endif // ENABLE_MODELVOLUME_TRANSFORM else { m_gizmos.set_scale(Vec3d::Ones()); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 63296dac3..d7c60f990 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -367,14 +367,8 @@ public: enum EMode : unsigned char { -#if ENABLE_MODELVOLUME_TRANSFORM Volume, Instance -#else - Volume, - Instance, - Object -#endif // ENABLE_MODELVOLUME_TRANSFORM }; enum EType : unsigned char @@ -397,7 +391,6 @@ public: struct VolumeCache { private: -#if ENABLE_MODELVOLUME_TRANSFORM struct TransformCache { Vec3d position; @@ -414,24 +407,11 @@ public: TransformCache m_volume; TransformCache m_instance; -#else - Vec3d m_position; - Vec3d m_rotation; - Vec3d m_scaling_factor; - Transform3d m_rotation_matrix; - Transform3d m_scale_matrix; -#endif // ENABLE_MODELVOLUME_TRANSFORM public: -#if ENABLE_MODELVOLUME_TRANSFORM VolumeCache() {} VolumeCache(const Geometry::Transformation& volume_transform, const Geometry::Transformation& instance_transform); -#else - VolumeCache(); - VolumeCache(const Vec3d& position, const Vec3d& rotation, const Vec3d& scaling_factor); -#endif // ENABLE_MODELVOLUME_TRANSFORM -#if ENABLE_MODELVOLUME_TRANSFORM const Vec3d& get_volume_position() const { return m_volume.position; } const Vec3d& get_volume_rotation() const { return m_volume.rotation; } const Vec3d& get_volume_scaling_factor() const { return m_volume.scaling_factor; } @@ -447,13 +427,6 @@ public: const Transform3d& get_instance_rotation_matrix() const { return m_instance.rotation_matrix; } const Transform3d& get_instance_scale_matrix() const { return m_instance.scale_matrix; } const Transform3d& get_instance_mirror_matrix() const { return m_instance.mirror_matrix; } -#else - const Vec3d& get_position() const { return m_position; } - const Vec3d& get_rotation() const { return m_rotation; } - const Vec3d& get_scaling_factor() const { return m_scaling_factor; } - const Transform3d& get_rotation_matrix() const { return m_rotation_matrix; } - const Transform3d& get_scale_matrix() const { return m_scale_matrix; } -#endif // ENABLE_MODELVOLUME_TRANSFORM }; typedef std::map VolumesCache; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 0a61e7900..7de065065 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -860,14 +860,10 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const bool single_selection = single_instance || single_volume; Vec3f scale = 100.0f * Vec3f::Ones(); -#if ENABLE_MODELVOLUME_TRANSFORM if (single_instance) scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_instance_scaling_factor().cast(); else if (single_volume) scale = 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_volume_scaling_factor().cast(); -#else - Vec3f scale = single_instance ? 100.0f * selection.get_volume(*selection.get_volume_idxs().begin())->get_scaling_factor().cast() : 100.0f * m_scale.cast(); -#endif // ENABLE_MODELVOLUME_TRANSFORM if ((single_selection && ((m_hover_id == 0) || (m_hover_id == 1))) || m_grabbers[0].dragging || m_grabbers[1].dragging) set_tooltip("X: " + format(scale(0), 4) + "%"); @@ -915,37 +911,22 @@ void GLGizmoScale3D::on_render(const GLCanvas3D::Selection& selection) const // gets transform from first selected volume const GLVolume* v = selection.get_volume(*idxs.begin()); -#if ENABLE_MODELVOLUME_TRANSFORM transform = v->get_instance_transformation().get_matrix(); // gets angles from first selected volume angles = v->get_instance_rotation(); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); grabber_size = v->get_instance_transformation().get_matrix(true, true, false, true) * box.size(); -#else - transform = v->world_matrix().cast(); - // gets angles from first selected volume - angles = v->get_rotation(); - // consider rotation+mirror only components of the transform for offsets - offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM } else if (single_volume) { const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); box = v->bounding_box; -#if ENABLE_MODELVOLUME_TRANSFORM transform = v->world_matrix(); angles = Geometry::extract_euler_angles(transform); // consider rotation+mirror only components of the transform for offsets offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_instance_mirror()); grabber_size = v->get_volume_transformation().get_matrix(true, true, false, true) * box.size(); -#else - transform = v->world_matrix().cast(); - angles = Geometry::extract_euler_angles(transform); - // consider rotation+mirror only components of the transform for offsets - offsets_transform = Geometry::assemble_transform(Vec3d::Zero(), angles, Vec3d::Ones(), v->get_mirror()); -#endif // ENABLE_MODELVOLUME_TRANSFORM } else { @@ -1529,7 +1510,6 @@ void GLGizmoFlatten::update_planes() { TriangleMesh ch; for (const ModelVolume* vol : m_model_object->volumes) -#if ENABLE_MODELVOLUME_TRANSFORM { if (vol->type() != ModelVolume::Type::MODEL_PART) continue; @@ -1537,9 +1517,6 @@ void GLGizmoFlatten::update_planes() vol_ch.transform(vol->get_matrix()); ch.merge(vol_ch); } -#else - ch.merge(vol->get_convex_hull()); -#endif // ENABLE_MODELVOLUME_TRANSFORM ch = ch.convex_hull_3d(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 5e20ceaab..afeaca327 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -840,27 +840,17 @@ void ObjectList::load_part( ModelObject* model_object, if (model_object->origin_translation != Vec3d::Zero()) { object->center_around_origin(); -#if !ENABLE_MODELVOLUME_TRANSFORM - object->ensure_on_bed(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM delta = model_object->origin_translation - object->origin_translation; } for (auto volume : object->volumes) { -#if ENABLE_MODELVOLUME_TRANSFORM volume->center_geometry(); volume->translate(delta); -#endif // ENABLE_MODELVOLUME_TRANSFORM auto new_volume = model_object->add_volume(*volume); new_volume->set_type(static_cast(type)); new_volume->name = boost::filesystem::path(input_file).filename().string(); part_names.Add(new_volume->name); -#if !ENABLE_MODELVOLUME_TRANSFORM - if (delta != Vec3d::Zero()) - new_volume->translate(delta); -#endif // !ENABLE_MODELVOLUME_TRANSFORM - // set a default extruder value, since user can't add it manually new_volume->config.set_key_value("extruder", new ConfigOptionInt(0)); @@ -903,10 +893,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const int auto new_volume = (*m_objects)[obj_idx]->add_volume(mesh); new_volume->set_type(static_cast(type)); -#if ENABLE_MODELVOLUME_TRANSFORM new_volume->set_offset(Vec3d(0.0, 0.0, (*m_objects)[obj_idx]->origin_translation(2) - mesh.stl.stats.min(2))); new_volume->center_geometry(); -#endif // ENABLE_MODELVOLUME_TRANSFORM new_volume->name = name; // set a default extruder value, since user can't add it manually diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index b2ffb1801..e14373441 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -195,30 +195,10 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele m_new_move_label_string = L("Position:"); m_new_rotate_label_string = L("Rotation:"); m_new_scale_label_string = L("Scale factors:"); -#if ENABLE_MODELVOLUME_TRANSFORM if (selection.is_single_full_instance()) -#else - if (selection.is_single_full_object()) - { - auto obj_idx = selection.get_object_idx(); - if (obj_idx >=0 && !wxGetApp().model_objects()->empty() && (*wxGetApp().model_objects())[obj_idx]->instances.size() == 1) - { - // all volumes in the selection belongs to the same instance, any of them contains the needed data, so we take the first - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - m_new_position = volume->get_offset(); - m_new_rotation = volume->get_rotation(); - m_new_scale = volume->get_scaling_factor(); - m_new_enabled = true; - } - else - reset_settings_value(); - } - else if (selection.is_single_full_instance()) -#endif // ENABLE_MODELVOLUME_TRANSFORM { // all volumes in the selection belongs to the same instance, any of them contains the needed instance data, so we take the first one const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM m_new_position = volume->get_instance_offset(); m_new_rotation = volume->get_instance_rotation(); m_new_scale = volume->get_instance_scaling_factor(); @@ -228,11 +208,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele else // this should never happen m_new_size = Vec3d::Zero(); -#else - m_new_position = volume->get_offset(); - m_new_rotation = volume->get_rotation(); - m_new_scale = volume->get_scaling_factor(); -#endif // ENABLE_MODELVOLUME_TRANSFORM + m_new_enabled = true; } else if (selection.is_single_full_object()) @@ -250,16 +226,10 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele { // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); -#if ENABLE_MODELVOLUME_TRANSFORM m_new_position = volume->get_volume_offset(); m_new_rotation = volume->get_volume_rotation(); m_new_scale = volume->get_volume_scaling_factor(); m_new_size = volume->get_instance_transformation().get_matrix(true, true) * volume->get_volume_transformation().get_matrix(true, true) * volume->bounding_box.size(); -#else - m_new_position = volume->get_offset(); - m_new_rotation = volume->get_rotation(); - m_new_scale = volume->get_scaling_factor(); -#endif // ENABLE_MODELVOLUME_TRANSFORM m_new_enabled = true; } else if (wxGetApp().obj_list()->multiple_selection()) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6d5053bfa..db052c6f1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1514,9 +1514,6 @@ std::vector Plater::priv::load_files(const std::vector& input_ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &model_objects) { const BoundingBoxf bed_shape = bed_shape_bb(); -#if !ENABLE_MODELVOLUME_TRANSFORM - const Vec3d bed_center = Slic3r::to_3d(bed_shape.center().cast(), 0.0); -#endif // !ENABLE_MODELVOLUME_TRANSFORM const Vec3d bed_size = Slic3r::to_3d(bed_shape.size().cast(), 1.0) - 2.0 * Vec3d::Ones(); bool need_arrange = false; @@ -1536,11 +1533,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode // add a default instance and center object around origin object->center_around_origin(); // also aligns object to Z = 0 ModelInstance* instance = object->add_instance(); -#if ENABLE_MODELVOLUME_TRANSFORM instance->set_offset(Slic3r::to_3d(bed_shape.center().cast(), -object->origin_translation(2))); -#else - instance->set_offset(bed_center); -#endif // ENABLE_MODELVOLUME_TRANSFORM } const Vec3d size = object->bounding_box().size(); @@ -3340,9 +3333,6 @@ void Plater::changed_object(int obj_idx) if (list->is_parts_changed()) { // recenter and re - align to Z = 0 auto model_object = p->model.objects[obj_idx]; -#if !ENABLE_MODELVOLUME_TRANSFORM - model_object->center_around_origin(); -#endif // !ENABLE_MODELVOLUME_TRANSFORM model_object->ensure_on_bed(); if (this->p->printer_technology == ptSLA) { // Update the SLAPrint from the current Model, so that the reload_scene() From abf34c3b6dbc4c70ea5867ac13216bd533b6bd5f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Jan 2019 11:52:15 +0100 Subject: [PATCH 112/132] Debug logs during SLA processing --- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 1 - src/libslic3r/SLAPrint.cpp | 13 ++++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 6d4a770aa..8971b6ede 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -192,7 +192,6 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, auto lend = std::unique(neighnorms.begin(), neighnorms.end(), [](const Vec3d& n1, const Vec3d& n2) { // Compare normals for equivalence. This is controvers stuff. - // We will go for the third significant digit precision. auto deq = [](double a, double b) { return std::abs(a-b) < 1e-3; }; return deq(n1(X), n2(X)) && deq(n1(Y), n2(Y)) && deq(n1(Z), n2(Z)); }); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 85b63a24c..e2a256b72 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -502,6 +502,9 @@ void SLAPrint::process() po.m_supportdata.reset(new SLAPrintObject::SupportData()); po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); + BOOST_LOG_TRIVIAL(debug) + << "Support point count " << mo.sla_support_points.size(); + // If there are no points on the front-end, we will do the // autoplacement. Otherwise we will just blindly copy the frontend data // into the backend cache. @@ -581,8 +584,16 @@ void SLAPrint::process() // Create the unified mesh auto rc = SlicingStatus::RELOAD_SCENE; - report_status(*this, -1, L("Visualizing supports")); po.m_supportdata->support_tree_ptr->merged_mesh(); + + // Check the mesh for later troubleshooting. + + BOOST_LOG_TRIVIAL(debug) << "Processed support point count " + << po.m_supportdata->support_points.rows(); + + if(po.support_mesh().empty()) + BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty"; + report_status(*this, -1, L("Visualizing supports"), rc); } catch(sla::SLASupportsStoppedException&) { // no need to rethrow From f8b83d20d3039f621bbc414765faeadfcb61698d Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 2 Jan 2019 13:38:02 +0100 Subject: [PATCH 113/132] Build: Warn about non-existent CMAKE_PREFIX_PATH --- CMakeLists.txt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 625ebb334..781dbd01a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,7 +42,24 @@ if (MSVC) add_compile_options(-bigobj -Zm316) endif () -message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") + +# Display and check CMAKE_PREFIX_PATH +message(STATUS "SLIC3R_STATIC: ${SLIC3R_STATIC}") +if (NOT "${CMAKE_PREFIX_PATH}" STREQUAL "") + message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH} (from cache or command line)") + set(PREFIX_PATH_CHECK ${CMAKE_PREFIX_PATH}) +elseif (NOT "$ENV{CMAKE_PREFIX_PATH}" STREQUAL "") + message(STATUS "CMAKE_PREFIX_PATH: $ENV{CMAKE_PREFIX_PATH} (from environment)") + set(PREFIX_PATH_CHECK $ENV{CMAKE_PREFIX_PATH}) +else () + message(STATUS "CMAKE_PREFIX_PATH: (default)") +endif () + +foreach (DIR ${PREFIX_PATH_CHECK}) + if (NOT EXISTS "${DIR}") + message(WARNING "CMAKE_PREFIX_PATH element doesn't exist: ${DIR}") + endif () +endforeach () # Add our own cmake module path. list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules/) From 0ccd19f1eee9c732a05c0cac9215095de389691e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Jan 2019 13:42:29 +0100 Subject: [PATCH 114/132] Improved objects manipulation using sidebar --- src/libslic3r/Technologies.hpp | 7 ++ src/slic3r/GUI/GUI_App.cpp | 2 + src/slic3r/GUI/GUI_ObjectManipulation.cpp | 122 +++++++++++++++++++--- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 18 +++- 4 files changed, 130 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index a2952a7d3..5c35199e7 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -43,6 +43,13 @@ // Separate rendering for opaque and transparent volumes #define ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_1_42_0) +//==================== +// 1.42.0.alpha1 techs +//==================== +#define ENABLE_1_42_0_ALPHA1 1 + +#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA1) + #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 69db64168..69b661494 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -184,8 +184,10 @@ bool GUI_App::OnInit() if (app_config->dirty()) app_config->save(); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION if (this->plater() != nullptr) this->obj_manipul()->update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION }); // On OS X the UI tends to freeze in weird ways if modal dialogs(config wizard, update notifications, ...) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index e14373441..9b730b3f2 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -17,6 +17,12 @@ namespace GUI ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + , m_cache_position(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) + , m_cache_rotation(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) + , m_cache_scale(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) + , m_cache_size(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION { m_og->set_name(_(L("Object Manipulation"))); m_og->label_width = 100; @@ -38,16 +44,32 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : else if (param == "rotation") change_rotation_value(new_value); else if (param == "scale") +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + { change_scale_value(new_value); + update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); + } +#else + change_scale_value(new_value); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION else if (param == "size") +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + { change_size_value(new_value); + update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); + } +#else + change_size_value(new_value); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); }; m_og->m_fill_empty_value = [this](const std::string& opt_key) { +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION this->update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); @@ -58,25 +80,25 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; - value = cache_position(axis); + value = m_cache_position(axis); } else if (param == "rotation") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; - value = cache_rotation(axis); + value = m_cache_rotation(axis); } else if (param == "scale") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; - value = cache_scale(axis); + value = m_cache_scale(axis); } else if (param == "size") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; - value = cache_size(axis); + value = m_cache_size(axis); } m_og->set_value(opt_key, double_to_string(value)); @@ -85,7 +107,9 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->m_set_focus = [this](const std::string& opt_key) { +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION this->update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; @@ -184,7 +208,9 @@ void ObjectManipulation::UpdateAndShow(const bool show) { if (show) { update_settings_value(wxGetApp().plater()->canvas3D()->get_selection()); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION update_if_dirty(); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } OG_Settings::UpdateAndShow(show); @@ -244,11 +270,75 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele else reset_settings_value(); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + update_if_dirty(); +#else m_dirty = true; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::update_if_dirty() { +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (_(m_new_move_label_string) != m_move_Label->GetLabel()) + m_move_Label->SetLabel(_(m_new_move_label_string)); + + if (_(m_new_rotate_label_string) != m_rotate_Label->GetLabel()) + m_rotate_Label->SetLabel(_(m_new_rotate_label_string)); + + if (_(m_new_scale_label_string) != m_scale_Label->GetLabel()) + m_scale_Label->SetLabel(_(m_new_scale_label_string)); + + if (m_cache_position(0) != m_new_position(0)) + m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); + + if (m_cache_position(1) != m_new_position(1)) + m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); + + if (m_cache_position(2) != m_new_position(2)) + m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); + + m_cache_position = m_new_position; + + auto scale = m_new_scale * 100.0; + if (m_cache_scale(0) != scale(0)) + m_og->set_value("scale_x", double_to_string(scale(0), 2)); + + if (m_cache_scale(1) != scale(1)) + m_og->set_value("scale_y", double_to_string(scale(1), 2)); + + if (m_cache_scale(2) != scale(2)) + m_og->set_value("scale_z", double_to_string(scale(2), 2)); + + m_cache_scale = scale; + + if (m_cache_size(0) != m_new_size(0)) + m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); + + if (m_cache_size(1) != m_new_size(1)) + m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); + + if (m_cache_size(2) != m_new_size(2)) + m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); + + m_cache_size = m_new_size; + + if (m_cache_rotation(0) != m_new_rotation(0)) + m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); + + if (m_cache_rotation(1) != m_new_rotation(1)) + m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); + + if (m_cache_rotation(2) != m_new_rotation(2)) + m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); + + m_cache_rotation = m_new_rotation; + + if (m_new_enabled) + m_og->enable(); + else + m_og->disable(); +#else if (! m_dirty) return; @@ -259,23 +349,23 @@ void ObjectManipulation::update_if_dirty() m_og->set_value("position_x", double_to_string(m_new_position(0), 2)); m_og->set_value("position_y", double_to_string(m_new_position(1), 2)); m_og->set_value("position_z", double_to_string(m_new_position(2), 2)); - cache_position = m_new_position; + m_cache_position = m_new_position; auto scale = m_new_scale * 100.0; m_og->set_value("scale_x", double_to_string(scale(0), 2)); m_og->set_value("scale_y", double_to_string(scale(1), 2)); m_og->set_value("scale_z", double_to_string(scale(2), 2)); - cache_scale = scale; + m_cache_scale = scale; m_og->set_value("size_x", double_to_string(m_new_size(0), 2)); m_og->set_value("size_y", double_to_string(m_new_size(1), 2)); m_og->set_value("size_z", double_to_string(m_new_size(2), 2)); - cache_size = m_new_size; + m_cache_size = m_new_size; m_og->set_value("rotation_x", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(0)), 0), 2)); m_og->set_value("rotation_y", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(1)), 0), 2)); m_og->set_value("rotation_z", double_to_string(round_nearest(Geometry::rad2deg(m_new_rotation(2)), 0), 2)); - cache_rotation = m_new_rotation; + m_cache_rotation = m_new_rotation; if (m_new_enabled) m_og->enable(); @@ -283,15 +373,19 @@ void ObjectManipulation::update_if_dirty() m_og->disable(); m_dirty = false; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::reset_settings_value() { m_new_position = Vec3d::Zero(); m_new_rotation = Vec3d::Zero(); - m_new_scale = Vec3d(1.0, 1.0, 1.0); - m_new_size = Vec3d::Zero(); + m_new_scale = Vec3d::Ones(); + m_new_size = Vec3d::Zero(); m_new_enabled = false; +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_dirty = true; +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::change_position_value(const Vec3d& position) @@ -299,10 +393,10 @@ void ObjectManipulation::change_position_value(const Vec3d& position) auto canvas = wxGetApp().plater()->canvas3D(); GLCanvas3D::Selection& selection = canvas->get_selection(); selection.start_dragging(); - selection.translate(position - cache_position, selection.requires_local_axes()); + selection.translate(position - m_cache_position, selection.requires_local_axes()); canvas->do_move(); - cache_position = position; + m_cache_position = position; } void ObjectManipulation::change_rotation_value(const Vec3d& rotation) @@ -327,7 +421,7 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); if (selection.requires_uniform_scale()) { - Vec3d abs_scale_diff = (scale - cache_scale).cwiseAbs(); + Vec3d abs_scale_diff = (scale - m_cache_scale).cwiseAbs(); double max_diff = abs_scale_diff(X); Axis max_diff_axis = X; if (max_diff < abs_scale_diff(Y)) @@ -355,7 +449,7 @@ void ObjectManipulation::change_size_value(const Vec3d& size) { const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); - Vec3d ref_size = cache_size; + Vec3d ref_size = m_cache_size; if (selection.is_single_full_instance()) { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index cbac94058..ac80f56b3 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -14,18 +14,26 @@ namespace GUI { class ObjectManipulation : public OG_Settings { - Vec3d cache_position { 0., 0., 0. }; - Vec3d cache_rotation { 0., 0., 0. }; - Vec3d cache_scale { 100., 100., 100. }; - Vec3d cache_size { 0., 0., 0. }; +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + Vec3d m_cache_position; + Vec3d m_cache_rotation; + Vec3d m_cache_scale; + Vec3d m_cache_size; +#else + Vec3d m_cache_position{ 0., 0., 0. }; + Vec3d m_cache_rotation{ 0., 0., 0. }; + Vec3d m_cache_scale{ 100., 100., 100. }; + Vec3d m_cache_size{ 0., 0., 0. }; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxStaticText* m_move_Label = nullptr; wxStaticText* m_scale_Label = nullptr; wxStaticText* m_rotate_Label = nullptr; - +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION // Needs to be updated from OnIdle? bool m_dirty = false; +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION // Cached labels for the delayed update, not localized! std::string m_new_move_label_string; std::string m_new_rotate_label_string; From 9b8a68565f2a7835e3630e006fe43df8dba7f0c0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Jan 2019 14:04:26 +0100 Subject: [PATCH 115/132] Modified camera target behavior set as default --- src/libslic3r/Technologies.hpp | 2 -- src/slic3r/GUI/GLCanvas3D.cpp | 8 -------- src/slic3r/GUI/GLCanvas3D.hpp | 2 -- src/slic3r/GUI/Plater.cpp | 8 -------- 4 files changed, 20 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5c35199e7..84bd7fc99 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -19,8 +19,6 @@ #define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) // Disable synchronization of unselected instances #define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0) -// Modified camera target behavior -#define ENABLE_MODIFIED_CAMERA_TARGET (1 && ENABLE_1_42_0) // Keeps objects on bed while scaling them using the scale gizmo #define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0) // All rotations made using the rotate gizmo are done with respect to the world reference system diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f5615f0d6..feaf14f98 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4041,13 +4041,11 @@ void GLCanvas3D::zoom_to_volumes() m_apply_zoom_to_volumes_filter = false; } -#if ENABLE_MODIFIED_CAMERA_TARGET void GLCanvas3D::zoom_to_selection() { if (!m_selection.is_empty()) _zoom_to_bounding_box(m_selection.get_bounding_box()); } -#endif // ENABLE_MODIFIED_CAMERA_TARGET void GLCanvas3D::select_view(const std::string& direction) { @@ -4827,7 +4825,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) // key O/o case 79: case 111: { set_camera_zoom(-1.0f); break; } -#if ENABLE_MODIFIED_CAMERA_TARGET // key Z/z case 90: case 122: @@ -4839,11 +4836,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) break; } -#else - // key Z/z - case 90: - case 122: { zoom_to_volumes(); break; } -#endif // ENABLE_MODIFIED_CAMERA_TARGET default: { if (m_gizmos.handle_shortcut(keyCode, m_selection)) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d7c60f990..6a1ccc45e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -918,9 +918,7 @@ public: void zoom_to_bed(); void zoom_to_volumes(); -#if ENABLE_MODIFIED_CAMERA_TARGET void zoom_to_selection(); -#endif // ENABLE_MODIFIED_CAMERA_TARGET void select_view(const std::string& direction); void set_viewport_from_scene(const GLCanvas3D& other); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index db052c6f1..82fa99bac 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1571,9 +1571,6 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode } update(); -#if !ENABLE_MODIFIED_CAMERA_TARGET - this->canvas3D->zoom_to_volumes(); -#endif // !ENABLE_MODIFIED_CAMERA_TARGET object_list_changed(); this->schedule_background_process(); @@ -3348,11 +3345,6 @@ void Plater::changed_object(int obj_idx) // update print this->p->schedule_background_process(); - if (list->is_parts_changed() || list->is_part_settings_changed()) { -#if !ENABLE_MODIFIED_CAMERA_TARGET - p->canvas3D->zoom_to_volumes(); -#endif // !ENABLE_MODIFIED_CAMERA_TARGET - } } void Plater::fix_through_netfabb(const int obj_idx) { p->fix_through_netfabb(obj_idx); } From d88f3adce906b11511bce758e2c93590836eb3b8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Jan 2019 14:26:25 +0100 Subject: [PATCH 116/132] Fix for invalid support geometry when connecting to model surface --- src/libslic3r/SLA/SLASupportTree.cpp | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 7fd5d16ef..60915c2ad 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -8,11 +8,12 @@ #include "SLABoilerPlate.hpp" #include "SLASpatIndex.hpp" #include "SLABasePool.hpp" -#include -#include "ClipperUtils.hpp" +#include "ClipperUtils.hpp" #include "Model.hpp" +#include + /** * Terminology: * @@ -1515,22 +1516,39 @@ bool SLASupportTree::generate(const PointSet &points, { // TODO: connect these to the ground pillars if possible for(auto idx : nogndidx) { tifcl(); + double gh = gndheight[idx]; + double base_width = cfg.head_width_mm; + auto& head = result.head(idx); + + // In this case there is no room for the base pinhead. + if(gh < head.fullwidth()) { + base_width = gh - 2 * cfg.head_front_radius_mm - + 2*cfg.head_back_radius_mm + cfg.head_penetration_mm; + } + head.transform(); - double gh = gndheight[idx]; Vec3d headend = head.junction_point(); Head base_head(cfg.head_back_radius_mm, cfg.head_front_radius_mm, - cfg.head_width_mm, + base_width, cfg.head_penetration_mm, {0.0, 0.0, 1.0}, {headend(X), headend(Y), headend(Z) - gh}); base_head.transform(); - double hl = head.fullwidth() - head.r_back_mm; + // Robustness check: + if(headend(Z) < base_head.junction_point()(Z)) { + // This should not happen it is against all assumptions + BOOST_LOG_TRIVIAL(warning) + << "Ignoring invalid supports connecting to model body"; + continue; + } + + double hl = base_head.fullwidth() - head.r_back_mm; result.add_pillar(idx, Vec3d{headend(X), headend(Y), headend(Z) - gh + hl}, From 6e2ea18b9cb35270163b1111dc1c11ed86b7bb83 Mon Sep 17 00:00:00 2001 From: "Dylan \"smellyfis\" Thies" Date: Tue, 1 Jan 2019 16:08:46 -0500 Subject: [PATCH 117/132] fixing issue with disappearing tabs src/slic3r/GUI/Tab.cpp: When switch to previously created tabs they were not being restored The fix is better only used on Linux (lukasmatena) --- src/slic3r/GUI/Tab.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index e46ae6344..41318a854 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2321,7 +2321,13 @@ void Tab::load_current_preset() if (tab->type() == Preset::TYPE_PRINTER) // Printer tab is shown every time continue; if (tab->supports_printer_technology(printer_technology)) + { wxGetApp().tab_panel()->InsertPage(wxGetApp().tab_panel()->FindPage(this), tab, tab->title()); + #ifdef __linux__ // the tabs apparently need to be explicitly shown on Linux (pull request #1563) + int page_id = wxGetApp().tab_panel()->FindPage(tab); + wxGetApp().tab_panel()->GetPage(page_id)->Show(true); + #endif // __linux__ + } else { int page_id = wxGetApp().tab_panel()->FindPage(tab); wxGetApp().tab_panel()->GetPage(page_id)->Show(false); From a7d8146198328d1d9c5fcad39c55b6781e789050 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 2 Jan 2019 15:35:40 +0100 Subject: [PATCH 118/132] =?UTF-8?q?Fix=20of=20=20#1501=20"Po=20p=C5=99epnu?= =?UTF-8?q?t=C3=AD=20do=20=C4=8De=C5=A1tiny=20program=20zhavaruje=20a=20ne?= =?UTF-8?q?jde=20znovu=20spustit"=20Fixed=20wxWidgets=20assert,=20where=20?= =?UTF-8?q?the=20Win32=20decimal=20point=20was=20different=20from=20wxWidg?= =?UTF-8?q?ets=20locales=20decimal=20point.=20Fixed=20by=20calling=20the?= =?UTF-8?q?=20Win32=20"set=20locale"=20function=20directly.=20Not=20sure?= =?UTF-8?q?=20I=20know=20what=20I=20am=20doing,=20this=20fix=20needs=20to?= =?UTF-8?q?=20be=20tested=20throughly.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/slic3r/GUI/GUI_App.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 69b661494..5db2a95cd 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -462,6 +462,9 @@ bool GUI_App::select_language( wxArrayString & names, m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); +#ifdef WIN32 + ::SetLocaleInfoA(LOCALE_CUSTOM_DEFAULT, LOCALE_SDECIMAL, "."); +#endif /* WIN32 */ Preset::update_suffix_modified(); return true; } @@ -489,7 +492,10 @@ bool GUI_App::load_language() m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); - Preset::update_suffix_modified(); +#ifdef WIN32 + ::SetLocaleInfoA(LOCALE_CUSTOM_DEFAULT, LOCALE_SDECIMAL, "."); +#endif /* WIN32 */ + Preset::update_suffix_modified(); return true; } } From 760b1cd9bc9485c4995f25aa20f80f77ec6205e6 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 2 Jan 2019 15:48:38 +0100 Subject: [PATCH 119/132] Fix for normal averaging and hidden parameter for max normal Z angle. --- src/libslic3r/SLA/SLASupportTree.cpp | 3 ++- src/libslic3r/SLA/SLASupportTree.hpp | 4 ++++ src/libslic3r/SLA/SLASupportTreeIGL.cpp | 10 +++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 60915c2ad..11ffc33b0 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -1085,7 +1085,8 @@ bool SLASupportTree::generate(const PointSet &points, double polar = std::acos(z / r); double azimuth = std::atan2(n(1), n(0)); - if(polar >= PI / 2) { // skip if the tilt is not sane + // skip if the tilt is not sane + if(polar >= PI - cfg.normal_cutoff_angle) { // We saturate the polar angle to 3pi/4 polar = std::max(polar, 3*PI / 4); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index e19f263b6..c187cf5b3 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -67,6 +67,10 @@ struct SupportConfig { // The elevation in Z direction upwards. This is the space between the pad // and the model object's bounding box bottom. double object_elevation_mm = 10; + + // The max Z angle for a normal at which it will get completely ignored. + double normal_cutoff_angle = 110.0 * M_PI / 180.0; + }; struct PoolConfig; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 8971b6ede..0cc9f14e0 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -188,7 +188,15 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, neighnorms.emplace_back(U.cross(V).normalized()); } - // Throw out duplicates. They would case trouble with summing. + // Throw out duplicates. They would cause trouble with summing. We will + // use std::unique which works on sorted ranges. We will sort by the + // coefficient-wise sum of the normals. It should force the same + // elements to be consecutive. + std::sort(neighnorms.begin(), neighnorms.end(), + [](const Vec3d& v1, const Vec3d& v2){ + return v1.sum() < v2.sum(); + }); + auto lend = std::unique(neighnorms.begin(), neighnorms.end(), [](const Vec3d& n1, const Vec3d& n2) { // Compare normals for equivalence. This is controvers stuff. From 2db0906071495ea2ddedef0a30a076954fb49007 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Wed, 2 Jan 2019 15:11:05 +0100 Subject: [PATCH 120/132] Fix fs::path <-> wxString conversions --- src/slic3r/GUI/GUI.cpp | 15 +++++++++++ src/slic3r/GUI/GUI.hpp | 15 ++++++++--- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 41 +++++++++++++++-------------- src/slic3r/GUI/PrintHostDialogs.cpp | 2 +- 6 files changed, 51 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 04e9a315c..0761845b6 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -4,6 +4,7 @@ #include "WipeTowerDialog.hpp" #include +#include #include #include @@ -314,6 +315,20 @@ std::string into_u8(const wxString &str) return std::string(buffer_utf8.data()); } +wxString from_path(const boost::filesystem::path &path) +{ +#ifdef _WIN32 + return wxString(path.string()); +#else + return wxString::FromUTF8(path.string()); +#endif +} + +boost::filesystem::path into_path(const wxString &str) +{ + return boost::filesystem::path(str.wx_str()); +} + bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height) { const auto idx = wxDisplay::GetFromWindow(window); diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 3ca4292a9..79b21b503 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -1,6 +1,10 @@ #ifndef slic3r_GUI_hpp_ #define slic3r_GUI_hpp_ +#include + +#include + #include "libslic3r/Config.hpp" class wxWindow; @@ -8,7 +12,6 @@ class wxMenuBar; class wxNotebook; class wxComboCtrl; class wxFileDialog; -class wxString; class wxTopLevelWindow; namespace Slic3r { @@ -53,10 +56,16 @@ void create_combochecklist(wxComboCtrl* comboCtrl, std::string text, std::string // encoded inside an int. int combochecklist_get_flags(wxComboCtrl* comboCtrl); -// Return wxString from std::string in UTF8 +// wxString conversions: + +// wxString from std::string in UTF8 wxString from_u8(const std::string &str); -// Return std::string in UTF8 from wxString +// std::string in UTF8 from wxString std::string into_u8(const wxString &str); +// wxString from boost path +wxString from_path(const boost::filesystem::path &path); +// boost path from wxString +boost::filesystem::path into_path(const wxString &str); // Returns the dimensions of the screen on which the main frame is displayed bool get_current_screen_size(wxWindow *window, unsigned &width, unsigned &height); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 5db2a95cd..0138c1229 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -382,7 +382,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) input_files.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), _(L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):")), - app_config->get_last_dir(), "", + from_u8(app_config->get_last_dir()), "", file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST); if (dialog.ShowModal() == wxID_OK) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 41339262b..33c4cf32b 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -224,7 +224,7 @@ void MainFrame::init_menubar() wxMenuItem* item_open = append_menu_item(fileMenu, wxID_ANY, _(L("Open…\tCtrl+O")), _(L("Open a project file")), [this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "brick_add.png"); wxMenuItem* item_save = append_menu_item(fileMenu, wxID_ANY, _(L("Save\tCtrl+S")), _(L("Save current project file")), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(m_plater->get_project_filename().wx_str()); }, "disk.png"); + [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename())); }, "disk.png"); wxMenuItem* item_save_as = append_menu_item(fileMenu, wxID_ANY, _(L("Save as…\tCtrl+Alt+S")), _(L("Save current project file as")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "disk.png"); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 82fa99bac..92c408670 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -863,7 +863,7 @@ bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &fi std::vector paths; for (const auto &filename : filenames) { - fs::path path(filename); + fs::path path(into_path(filename)); if (std::regex_match(path.string(), pattern_drop)) { paths.push_back(std::move(path)); @@ -1396,7 +1396,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ for (size_t i = 0; i < input_files.size(); i++) { const auto &path = input_files[i]; const auto filename = path.filename(); - const auto dlg_info = wxString::Format(_(L("Processing input file %s\n")), filename.string()); + const auto dlg_info = wxString::Format(_(L("Processing input file %s\n")), from_path(filename)); dlg.Update(100 * i / input_files.size(), dlg_info); const bool type_3mf = std::regex_match(path.string(), pattern_3mf); @@ -1470,7 +1470,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ if ( obj->volumes.size()>1 ) { Slic3r::GUI::show_error(nullptr, wxString::Format(_(L("You can't to add the object(s) from %s because of one or some of them is(are) multi-part")), - filename.string())); + from_path(filename))); return std::vector(); } } @@ -1609,8 +1609,8 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType ((file_type == FT_AMF) || (file_type == FT_3MF)) ? _(L("Export print config")) : "", true, _(L("Save file as:")), - output_file.parent_path().string(), - output_file.filename().string(), + from_path(output_file.parent_path()), + from_path(output_file.filename()), wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); @@ -1619,7 +1619,7 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType return nullptr; } - fs::path path(dlg->GetPath()); + fs::path path(into_path(dlg->GetPath())); wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); return dlg; @@ -2855,7 +2855,7 @@ void Plater::load_project() p->project_filename = input_file; std::vector input_paths; - input_paths.push_back(input_file.wx_str()); + input_paths.push_back(into_path(input_file)); load_files(input_paths); } @@ -2868,7 +2868,7 @@ void Plater::add_model() std::vector input_paths; for (const auto &file : input_files) { - input_paths.push_back(file.wx_str()); + input_paths.push_back(into_path(file)); } load_files(input_paths, true, false); } @@ -2882,7 +2882,7 @@ void Plater::extract_config_from_project() return; std::vector input_paths; - input_paths.push_back(input_file.wx_str()); + input_paths.push_back(into_path(input_file)); load_files(input_paths, false, true); } @@ -3033,15 +3033,15 @@ void Plater::export_gcode(fs::path output_path) wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), start_dir, - default_output_file.filename().string(), + from_path(default_output_file.filename()), GUI::file_wildcards((printer_technology() == ptFFF) ? FT_GCODE : FT_PNGZIP, default_output_file.extension().string()), wxFD_SAVE | wxFD_OVERWRITE_PROMPT ); if (dlg.ShowModal() == wxID_OK) { - fs::path path(dlg.GetPath()); + fs::path path = into_path(dlg.GetPath()); wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); - output_path = path; + output_path = std::move(path); } } else { try { @@ -3065,8 +3065,8 @@ void Plater::export_stl(bool selection_only) if (! dialog) { return; } // Store a binary STL - wxString path = dialog->GetPath(); - auto path_cstr = path.c_str(); + const wxString path = dialog->GetPath(); + const std::string path_u8 = into_u8(path); TriangleMesh mesh; if (selection_only) { @@ -3080,7 +3080,7 @@ void Plater::export_stl(bool selection_only) auto mesh = p->model.mesh(); } - Slic3r::store_stl(path_cstr, &mesh, true); + Slic3r::store_stl(path_u8.c_str(), &mesh, true); p->statusbar()->set_status_text(wxString::Format(_(L("STL file exported to %s")), path)); } @@ -3091,11 +3091,11 @@ void Plater::export_amf() auto dialog = p->get_export_file(FT_AMF); if (! dialog) { return; } - wxString path = dialog->GetPath(); - auto path_cstr = path.c_str(); + const wxString path = dialog->GetPath(); + const std::string path_u8 = into_u8(path); DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); - if (Slic3r::store_amf(path_cstr, &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { + if (Slic3r::store_amf(path_u8.c_str(), &p->model, dialog->get_checkbox_value() ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); } else { @@ -3118,13 +3118,14 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) export_config = dialog->get_checkbox_value(); } else - path = output_path.string(); + path = from_path(output_path); if (!path.Lower().EndsWith(".3mf")) return; DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); - if (Slic3r::store_3mf(path.c_str(), &p->model, export_config ? &cfg : nullptr)) { + const std::string path_u8 = into_u8(path); + if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { // Success p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); } else { diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 8c0c0fc85..c2a94181e 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -49,7 +49,7 @@ PrintHostSendDialog::PrintHostSendDialog(const fs::path &path) fs::path PrintHostSendDialog::filename() const { - return fs::path(txt_filename->GetValue().wx_str()); + return into_path(txt_filename->GetValue()); } bool PrintHostSendDialog::start_print() const From 2adce6264378275abf2764a970a39e1576fd6bc0 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 2 Jan 2019 16:26:22 +0100 Subject: [PATCH 121/132] Background texture for toolbars set as default --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 51 --- src/slic3r/GUI/GLCanvas3D.hpp | 13 +- src/slic3r/GUI/GLToolbar.cpp | 651 --------------------------------- src/slic3r/GUI/GLToolbar.hpp | 172 +-------- src/slic3r/GUI/GUI_Preview.cpp | 8 - src/slic3r/GUI/GUI_Preview.hpp | 14 - src/slic3r/GUI/Plater.cpp | 33 -- 8 files changed, 3 insertions(+), 941 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 84bd7fc99..681259996 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,8 +32,6 @@ #define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0) // Use wxDataViewRender instead of wxDataViewCustomRenderer #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) -// Adds background texture to toolbars -#define ENABLE_TOOLBAR_BACKGROUND_TEXTURE (1 && ENABLE_1_42_0) // Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active #define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0) // Show visual hints in the 3D scene when sidebar matrix fields have focus diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index feaf14f98..1d6c3b117 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2706,7 +2706,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_background_texture.metadata.filename = "toolbar_background.png"; m_background_texture.metadata.left = 16; m_background_texture.metadata.top = 16; @@ -2721,7 +2720,6 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) return false; } } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } @@ -3154,7 +3152,6 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = _get_total_overlay_height(); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = OverlayBorder * inv_zoom; float top_x = (-0.5f * cnv_w) * inv_zoom; @@ -3233,10 +3230,6 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva top_x += OverlayBorder * inv_zoom; top_y -= OverlayBorder * inv_zoom; -#else - float top_x = (OverlayBorder - 0.5f * cnv_w) * inv_zoom; - float top_y = (0.5f * height - OverlayBorder) * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_gap_y = OverlayGapY * inv_zoom; for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { @@ -3275,7 +3268,6 @@ float GLCanvas3D::Gizmos::_get_total_overlay_height() const return height - OverlayGapY; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float GLCanvas3D::Gizmos::_get_total_overlay_width() const { float max_icon_width = 0.0f; @@ -3289,7 +3281,6 @@ float GLCanvas3D::Gizmos::_get_total_overlay_width() const return max_icon_width + 2.0f * OverlayBorder; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const { @@ -3666,11 +3657,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) : m_canvas(canvas) , m_context(nullptr) , m_in_render(false) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , m_toolbar(GLToolbar::Normal) -#else - , m_toolbar(*this) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER , m_view_toolbar(nullptr) #endif // ENABLE_REMOVE_TABS_FROM_PLATER @@ -4910,11 +4897,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) int layer_editing_object_idx = is_layers_editing_enabled() ? selected_object_idx : -1; m_layers_editing.last_object_id = layer_editing_object_idx; bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this); -#else - int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1; #endif // ENABLE_REMOVE_TABS_FROM_PLATER @@ -4938,11 +4921,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.LeftDClick() && (toolbar_contains_mouse != -1)) { m_toolbar_action_running = true; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); -#else - m_toolbar.do_action((unsigned int)toolbar_contains_mouse); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } else if (evt.LeftDClick() && (m_gizmos.get_current_type() != Gizmos::Undefined)) { @@ -5021,11 +5000,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (toolbar_contains_mouse != -1) { m_toolbar_action_running = true; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.do_action((unsigned int)toolbar_contains_mouse, *this); -#else - m_toolbar.do_action((unsigned int)toolbar_contains_mouse); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_mouse.left_down = false; } else @@ -5303,11 +5278,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // updates toolbar overlay if (tooltip.empty()) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE tooltip = m_toolbar.update_hover_state(m_mouse.position, *this); -#else - tooltip = m_toolbar.update_hover_state(m_mouse.position); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // updates view toolbar overlay if (tooltip.empty() && (m_view_toolbar != nullptr)) @@ -5664,7 +5635,6 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.is_enabled()) return true; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::Metadata icons_data; icons_data.filename = "toolbar.png"; icons_data.icon_size = 36; @@ -5684,9 +5654,6 @@ bool GLCanvas3D::_init_toolbar() background_data.bottom = 16; if (!m_toolbar.init(icons_data, background_data)) -#else - if (!m_toolbar.init("toolbar.png", 36, 1, 1)) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { // unable to init the toolbar texture, disable it m_toolbar.set_enabled(false); @@ -5695,10 +5662,8 @@ bool GLCanvas3D::_init_toolbar() // m_toolbar.set_layout_type(GLToolbar::Layout::Vertical); m_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_layout_orientation(GLToolbar::Layout::Top); m_toolbar.set_border(5.0f); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_separator_size(5); m_toolbar.set_gap_size(2); @@ -6339,11 +6304,7 @@ void GLCanvas3D::_render_toolbar() const #if !ENABLE_REMOVE_TABS_FROM_PLATER _resize_toolbar(); #endif // !ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.render(*this); -#else - m_toolbar.render(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -7976,9 +7937,7 @@ void GLCanvas3D::_resize_toolbar() const float zoom = get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE switch (m_toolbar.get_layout_type()) { @@ -7986,7 +7945,6 @@ void GLCanvas3D::_resize_toolbar() const case GLToolbar::Layout::Horizontal: { // centers the toolbar on the top edge of the 3d scene -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top, left; if (orientation == GLToolbar::Layout::Top) { @@ -7998,17 +7956,12 @@ void GLCanvas3D::_resize_toolbar() const top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; left = -0.5f * m_toolbar.get_width() * inv_zoom; } -#else - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float left = -0.5f * m_toolbar.get_width() * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_position(top, left); break; } case GLToolbar::Layout::Vertical: { // centers the toolbar on the right edge of the 3d scene -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top, left; if (orientation == GLToolbar::Layout::Left) { @@ -8020,10 +7973,6 @@ void GLCanvas3D::_resize_toolbar() const top = 0.5f * m_toolbar.get_height() * inv_zoom; left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; } -#else - float top = 0.5f * m_toolbar.get_height() * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_toolbar.set_position(top, left); break; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 6a1ccc45e..3c963f8be 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -640,9 +640,7 @@ private: bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE BackgroundTexture m_background_texture; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE EType m_current; public: @@ -711,9 +709,8 @@ private: void _render_current_gizmo(const Selection& selection) const; float _get_total_overlay_height() const; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float _get_total_overlay_width() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLGizmoBase* _get_current() const; }; @@ -787,11 +784,7 @@ private: mutable Gizmos m_gizmos; mutable GLToolbar m_toolbar; #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar* m_view_toolbar; -#else - GLRadioToolbar* m_view_toolbar; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; @@ -842,11 +835,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } -#else - void set_view_toolbar(GLRadioToolbar* toolbar) { m_view_toolbar = toolbar; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER bool init(bool useVBOs, bool use_legacy_opengl); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 43da81809..9bd2358bd 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -75,12 +75,10 @@ bool GLToolbarItem::is_enabled() const return m_state != Disabled; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbarItem::is_disabled() const { return m_state == Disabled; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbarItem::is_hovered() const { @@ -131,7 +129,6 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned i return uvs; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::Metadata::Metadata() : filename("") , icon_size(0) @@ -139,18 +136,7 @@ ItemsIconsTexture::Metadata::Metadata() , icon_gap_size(0) { } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -ItemsIconsTexture::ItemsIconsTexture() - : items_icon_size(0) - , items_icon_border_size(0) - , items_icon_gap_size(0) -{ -} -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE BackgroundTexture::Metadata::Metadata() : filename("") , left(0) @@ -159,36 +145,24 @@ BackgroundTexture::Metadata::Metadata() , bottom(0) { } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::Layout() : type(Horizontal) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , orientation(Center) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , top(0.0f) , left(0.0f) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , border(0.0f) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , separator_size(0.0f) , gap_size(0.0f) , icons_scale(1.0f) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , width(0.0f) , height(0.0f) , dirty(true) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::GLToolbar(GLToolbar::EType type) : m_type(type) -#else -GLToolbar::GLToolbar(GLCanvas3D& parent) - : m_parent(parent) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE , m_enabled(false) { } @@ -201,7 +175,6 @@ GLToolbar::~GLToolbar() } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture) { if (m_icons_texture.texture.get_id() != 0) @@ -220,21 +193,6 @@ bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const Bac return res; } -#else -bool GLToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) -{ - std::string path = resources_dir() + "/icons/"; - bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false); - if (res) - { - m_icons_texture.items_icon_size = items_icon_size; - m_icons_texture.items_icon_border_size = items_icon_border_size; - m_icons_texture.items_icon_gap_size = items_icon_gap_size; - } - - return res; -} -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::EType GLToolbar::get_layout_type() const { @@ -244,12 +202,9 @@ GLToolbar::Layout::EType GLToolbar::get_layout_type() const void GLToolbar::set_layout_type(GLToolbar::Layout::EType type) { m_layout.type = type; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar::Layout::EOrientation GLToolbar::get_layout_orientation() const { return m_layout.orientation; @@ -259,7 +214,6 @@ void GLToolbar::set_layout_orientation(GLToolbar::Layout::EOrientation orientati { m_layout.orientation = orientation; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::set_position(float top, float left) { @@ -267,36 +221,28 @@ void GLToolbar::set_position(float top, float left) m_layout.left = left; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::set_border(float border) { m_layout.border = border; m_layout.dirty = true; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::set_separator_size(float size) { m_layout.separator_size = size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::set_gap_size(float size) { m_layout.gap_size = size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::set_icons_scale(float scale) { m_layout.icons_scale = scale; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } bool GLToolbar::is_enabled() const @@ -316,9 +262,7 @@ bool GLToolbar::add_item(const GLToolbarItem::Data& data) return false; m_items.push_back(item); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } @@ -330,56 +274,24 @@ bool GLToolbar::add_separator() return false; m_items.push_back(item); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE m_layout.dirty = true; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return true; } float GLToolbar::get_width() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE if (m_layout.dirty) calc_layout(); return m_layout.width; -#else - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return get_width_horizontal(); - } - case Layout::Vertical: - { - return get_width_vertical(); - } - } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE if (m_layout.dirty) calc_layout(); return m_layout.height; -#else - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - return get_height_horizontal(); - } - case Layout::Vertical: - { - return get_height_vertical(); - } - } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } void GLToolbar::enable_item(const std::string& name) @@ -406,7 +318,6 @@ void GLToolbar::disable_item(const std::string& name) } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::select_item(const std::string& name) { if (is_item_disabled(name)) @@ -421,7 +332,6 @@ void GLToolbar::select_item(const std::string& name) } } } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbar::is_item_pressed(const std::string& name) const { @@ -434,7 +344,6 @@ bool GLToolbar::is_item_pressed(const std::string& name) const return false; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool GLToolbar::is_item_disabled(const std::string& name) const { for (GLToolbarItem* item : m_items) @@ -445,20 +354,11 @@ bool GLToolbar::is_item_disabled(const std::string& name) const return false; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) #else -std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER { #if ENABLE_REMOVE_TABS_FROM_PLATER @@ -473,30 +373,16 @@ void GLToolbar::update_hover_state(const Vec2d& mouse_pos) { default: #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos, parent); } case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); } #else - case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos); } - case Layout::Vertical: { return update_hover_state_vertical(mouse_pos); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; } case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } -#else - case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos); break; } - case Layout::Vertical: { update_hover_state_vertical(mouse_pos); break; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -#else -int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (!m_enabled) return -1; @@ -504,21 +390,12 @@ int GLToolbar::contains_mouse(const Vec2d& mouse_pos) const switch (m_layout.type) { default: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } -#else - case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos); } - case Layout::Vertical: { return contains_mouse_vertical(mouse_pos); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) -#else -void GLToolbar::do_action(unsigned int item_id) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (item_id < (unsigned int)m_items.size()) { @@ -533,51 +410,30 @@ void GLToolbar::do_action(unsigned int item_id) else if (state == GLToolbarItem::HoverPressed) item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.render(); item->do_action(parent.get_wxglcanvas()); -#else - m_parent.render(); - item->do_action(m_parent.get_wxglcanvas()); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } else { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE if (m_type == Radio) select_item(item->get_name()); else -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.render(); item->do_action(parent.get_wxglcanvas()); if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) -#else - m_parent.render(); - item->do_action(m_parent.get_wxglcanvas()); - if (item->get_state() != GLToolbarItem::Disabled) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { // the item may get disabled during the action, if not, set it back to hover state item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.render(); -#else - m_parent.render(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } } } } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::render(const GLCanvas3D& parent) const -#else -void GLToolbar::render() const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (!m_enabled || m_items.empty()) return; @@ -590,19 +446,13 @@ void GLToolbar::render() const switch (m_layout.type) { default: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE case Layout::Horizontal: { render_horizontal(parent); break; } case Layout::Vertical: { render_vertical(parent); break; } -#else - case Layout::Horizontal: { render_horizontal(); break; } - case Layout::Vertical: { render_vertical(); break; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } ::glPopMatrix(); } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::calc_layout() const { switch (m_layout.type) @@ -624,7 +474,6 @@ void GLToolbar::calc_layout() const m_layout.dirty = false; } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float GLToolbar::get_width_horizontal() const { @@ -633,20 +482,12 @@ float GLToolbar::get_width_horizontal() const float GLToolbar::get_width_vertical() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale; -#else - return m_icons_texture.items_icon_size * m_layout.icons_scale; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_horizontal() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE return 2.0f * m_layout.border + m_icons_texture.metadata.icon_size * m_layout.icons_scale; -#else - return m_icons_texture.items_icon_size * m_layout.icons_scale; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } float GLToolbar::get_height_vertical() const @@ -656,21 +497,13 @@ float GLToolbar::get_height_vertical() const float GLToolbar::get_main_size() const { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float size = 2.0f * m_layout.border; -#else - float size = 0.0f; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) { if (m_items[i]->is_separator()) size += m_layout.separator_size; else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE size += (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale; -#else - size += (float)m_icons_texture.items_icon_size * m_layout.icons_scale; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } if (m_items.size() > 1) @@ -680,54 +513,27 @@ float GLToolbar::get_main_size() const } #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) #else -std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string tooltip = ""; @@ -748,14 +554,10 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) case GLToolbarItem::Normal: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -764,28 +566,20 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Normal); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } case GLToolbarItem::Pressed: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -794,14 +588,10 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Pressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -825,54 +615,27 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos) } #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) #else -std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string tooltip = ""; @@ -893,14 +656,10 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) case GLToolbarItem::Normal: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Hover); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -909,28 +668,20 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Normal); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } case GLToolbarItem::Pressed: { if (inside) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::HoverPressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -939,14 +690,10 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) if (inside) tooltip = item->get_tooltip(); else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->set_state(GLToolbarItem::Pressed); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE parent.set_as_dirty(); } -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE break; } @@ -968,47 +715,24 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos) #endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -#else -int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE int id = -1; @@ -1033,47 +757,24 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos) const return -1; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -#else -int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Size cnv_size = parent.get_canvas_size(); -#else - Size cnv_size = m_parent.get_canvas_size(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE int id = -1; @@ -1098,11 +799,7 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos) const return -1; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::render_horizontal(const GLCanvas3D& parent) const -#else -void GLToolbar::render_horizontal() const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); @@ -1110,30 +807,19 @@ void GLToolbar::render_horizontal() const if ((tex_id == 0) || (tex_size <= 0)) return; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; float scaled_width = get_width() * inv_zoom; float scaled_height = get_height() * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left; float top = m_layout.top; float right = left + scaled_width; @@ -1224,10 +910,6 @@ void GLToolbar::render_horizontal() const left += scaled_border; top -= scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // renders icons for (const GLToolbarItem* item : m_items) @@ -1236,21 +918,13 @@ void GLToolbar::render_horizontal() const left += separator_stride; else { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size); -#else - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE left += icon_stride; } } } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void GLToolbar::render_vertical(const GLCanvas3D& parent) const -#else -void GLToolbar::render_vertical() const -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); @@ -1258,30 +932,19 @@ void GLToolbar::render_vertical() const if ((tex_id == 0) || (tex_size <= 0)) return; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float zoom = parent.get_camera_zoom(); -#else - float zoom = m_parent.get_camera_zoom(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * inv_zoom; -#else - float scaled_icons_size = (float)m_icons_texture.items_icon_size * m_layout.icons_scale * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_separator_size = m_layout.separator_size * inv_zoom; float scaled_gap_size = m_layout.gap_size * inv_zoom; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float scaled_border = m_layout.border * inv_zoom; float scaled_width = get_width() * inv_zoom; float scaled_height = get_height() * inv_zoom; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_stride = scaled_separator_size + scaled_gap_size; float icon_stride = scaled_icons_size + scaled_gap_size; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float left = m_layout.left; float top = m_layout.top; float right = left + scaled_width; @@ -1372,10 +1035,6 @@ void GLToolbar::render_vertical() const left += scaled_border; top -= scaled_border; -#else - float left = m_layout.left; - float top = m_layout.top; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE // renders icons for (const GLToolbarItem* item : m_items) @@ -1384,321 +1043,11 @@ void GLToolbar::render_vertical() const top -= separator_stride; else { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_border_size, m_icons_texture.metadata.icon_size, m_icons_texture.metadata.icon_gap_size); -#else - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE top -= icon_stride; } } } -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -GLRadioToolbarItem::Data::Data() - : name("") - , tooltip("") - , sprite_id(-1) -{ -} - -GLRadioToolbarItem::GLRadioToolbarItem(const GLRadioToolbarItem::Data& data) - : m_state(Normal) - , m_data(data) -{ -} - -GLRadioToolbarItem::EState GLRadioToolbarItem::get_state() const -{ - return m_state; -} - -void GLRadioToolbarItem::set_state(GLRadioToolbarItem::EState state) -{ - m_state = state; -} - -const std::string& GLRadioToolbarItem::get_name() const -{ - return m_data.name; -} - -const std::string& GLRadioToolbarItem::get_tooltip() const -{ - return m_data.tooltip; -} - -bool GLRadioToolbarItem::is_hovered() const -{ - return (m_state == Hover) || (m_state == HoverPressed); -} - -bool GLRadioToolbarItem::is_pressed() const -{ - return (m_state == Pressed) || (m_state == HoverPressed); -} - -void GLRadioToolbarItem::do_action(wxEvtHandler *target) -{ - wxPostEvent(target, SimpleEvent(m_data.action_event)); -} - -void GLRadioToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const -{ - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(texture_size, border_size, icon_size, gap_size)); -} - -GLTexture::Quad_UVs GLRadioToolbarItem::get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const -{ - GLTexture::Quad_UVs uvs; - - float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; - - float scaled_icon_size = (float)icon_size * inv_texture_size; - float scaled_border_size = (float)border_size * inv_texture_size; - float scaled_gap_size = (float)gap_size * inv_texture_size; - float stride = scaled_icon_size + scaled_gap_size; - - float left = scaled_border_size + (float)m_state * stride; - float right = left + scaled_icon_size; - float top = scaled_border_size + (float)m_data.sprite_id * stride; - float bottom = top + scaled_icon_size; - - uvs.left_top = { left, top }; - uvs.left_bottom = { left, bottom }; - uvs.right_bottom = { right, bottom }; - uvs.right_top = { right, top }; - - return uvs; -} - -GLRadioToolbar::GLRadioToolbar() - : m_top(0.0f) - , m_left(0.0f) -{ -} - -GLRadioToolbar::~GLRadioToolbar() -{ - for (GLRadioToolbarItem* item : m_items) - { - delete item; - } -} - -bool GLRadioToolbar::init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size) -{ - if (m_icons_texture.texture.get_id() != 0) - return true; - - std::string path = resources_dir() + "/icons/"; - bool res = !icons_texture_filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture_filename, false); - if (res) - { - m_icons_texture.items_icon_size = items_icon_size; - m_icons_texture.items_icon_border_size = items_icon_border_size; - m_icons_texture.items_icon_gap_size = items_icon_gap_size; - } - - return res; -} - -bool GLRadioToolbar::add_item(const GLRadioToolbarItem::Data& data) -{ - GLRadioToolbarItem* item = new GLRadioToolbarItem(data); - if (item == nullptr) - return false; - - m_items.push_back(item); - return true; -} - -float GLRadioToolbar::get_height() const -{ - return m_icons_texture.items_icon_size; -} - -void GLRadioToolbar::set_position(float top, float left) -{ - m_top = top; - m_left = left; -} - -void GLRadioToolbar::set_selection(const std::string& name) -{ - for (GLRadioToolbarItem* item : m_items) - { - item->set_state((item->get_name() == name) ? GLRadioToolbarItem::Pressed : GLRadioToolbarItem::Normal); - } -} - -int GLRadioToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -{ - float zoom = parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - - float left = m_left; - float top = m_top; - - int id = -1; - - for (GLRadioToolbarItem* item : m_items) - { - ++id; - - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - left += scaled_icons_size; - } - - return -1; -} - -std::string GLRadioToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -{ - float zoom = parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - - float left = m_left; - float top = m_top; - - std::string tooltip = ""; - - for (GLRadioToolbarItem* item : m_items) - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - GLRadioToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); - - switch (state) - { - case GLRadioToolbarItem::Normal: - { - if (inside) - { - item->set_state(GLRadioToolbarItem::Hover); - parent.set_as_dirty(); - } - - break; - } - case GLRadioToolbarItem::Hover: - { - if (inside) - tooltip = item->get_tooltip(); - else - { - item->set_state(GLRadioToolbarItem::Normal); - parent.set_as_dirty(); - } - - break; - } - case GLRadioToolbarItem::Pressed: - { - if (inside) - { - item->set_state(GLRadioToolbarItem::HoverPressed); - parent.set_as_dirty(); - } - - break; - } - case GLRadioToolbarItem::HoverPressed: - { - if (inside) - tooltip = item->get_tooltip(); - else - { - item->set_state(GLRadioToolbarItem::Pressed); - parent.set_as_dirty(); - } - - break; - } - default: - { - break; - } - } - - left += scaled_icons_size; - } - - return tooltip; -} - -void GLRadioToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) -{ - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - { - if (i != item_id) - m_items[i]->set_state(GLRadioToolbarItem::Normal); - } - - if (item_id < (unsigned int)m_items.size()) - { - GLRadioToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && item->is_hovered() && !item->is_pressed()) - { - item->set_state(GLRadioToolbarItem::HoverPressed); - item->do_action(parent.get_wxglcanvas()); - } - } - - parent.set_as_dirty(); -} - -void GLRadioToolbar::render(const GLCanvas3D& parent) const -{ - if (m_items.empty()) - return; - - ::glDisable(GL_DEPTH_TEST); - - ::glPushMatrix(); - ::glLoadIdentity(); - - unsigned int tex_id = m_icons_texture.texture.get_id(); - int tex_size = m_icons_texture.texture.get_width(); - - if ((tex_id == 0) || (tex_size <= 0)) - return; - - float zoom = parent.get_camera_zoom(); - float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - - float scaled_icons_size = (float)m_icons_texture.items_icon_size * inv_zoom; - - float left = m_left; - float top = m_top; - - // renders icons - for (const GLRadioToolbarItem* item : m_items) - { - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.items_icon_border_size, m_icons_texture.items_icon_size, m_icons_texture.items_icon_gap_size); - left += scaled_icons_size; - } - - ::glPopMatrix(); -} -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 430d4a844..49296796d 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -77,9 +77,7 @@ public: void do_action(wxEvtHandler *target); bool is_enabled() const; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_disabled() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_hovered() const; bool is_pressed() const; @@ -97,7 +95,6 @@ private: // from left to right struct ItemsIconsTexture { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE struct Metadata { // path of the file containing the icons' texture @@ -111,23 +108,11 @@ struct ItemsIconsTexture Metadata(); }; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE - GLTexture texture; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE - Metadata metadata; -#else - // size of the square icons, in pixels - unsigned int items_icon_size; - // distance from the border, in pixels - unsigned int items_icon_border_size; - // distance between two adjacent icons (to avoid filtering artifacts), in pixels - unsigned int items_icon_gap_size; - ItemsIconsTexture(); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE + GLTexture texture; + Metadata metadata; }; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE struct BackgroundTexture { struct Metadata @@ -149,19 +134,16 @@ struct BackgroundTexture GLTexture texture; Metadata metadata; }; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE class GLToolbar { public: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE enum EType : unsigned char { Normal, Radio, Num_Types }; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE struct Layout { @@ -183,23 +165,17 @@ public: }; EType type; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE EOrientation orientation; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float top; float left; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float border; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float separator_size; float gap_size; float icons_scale; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE float width; float height; bool dirty; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Layout(); }; @@ -207,47 +183,27 @@ public: private: typedef std::vector ItemsList; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE EType m_type; -#else - GLCanvas3D& m_parent; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool m_enabled; ItemsIconsTexture m_icons_texture; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE BackgroundTexture m_background_texture; mutable Layout m_layout; -#else - Layout m_layout; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsList m_items; public: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE explicit GLToolbar(EType type); -#else - explicit GLToolbar(GLCanvas3D& parent); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE ~GLToolbar(); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture); -#else - bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE Layout::EType get_layout_type() const; void set_layout_type(Layout::EType type); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE Layout::EOrientation get_layout_orientation() const; void set_layout_orientation(Layout::EOrientation orientation); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_position(float top, float left); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_border(float border); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_separator_size(float size); void set_gap_size(float size); void set_icons_scale(float scale); @@ -263,169 +219,45 @@ public: void enable_item(const std::string& name); void disable_item(const std::string& name); -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void select_item(const std::string& name); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_item_pressed(const std::string& name) const; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE bool is_item_disabled(const std::string& name) const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); #else - std::string update_hover_state(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - void update_hover_state(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE // returns the id of the item under the given mouse position or -1 if none int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; void do_action(unsigned int item_id, GLCanvas3D& parent); -#else - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos) const; - void do_action(unsigned int item_id); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE - -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void render(const GLCanvas3D& parent) const; -#else - void render() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE private: -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void calc_layout() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE float get_width_horizontal() const; float get_width_vertical() const; float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); #else - std::string update_hover_state_horizontal(const Vec2d& mouse_pos); - std::string update_hover_state_vertical(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE -#else -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - void update_hover_state_horizontal(const Vec2d& mouse_pos); - void update_hover_state_vertical(const Vec2d& mouse_pos); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; void render_horizontal(const GLCanvas3D& parent) const; void render_vertical(const GLCanvas3D& parent) const; -#else - int contains_mouse_horizontal(const Vec2d& mouse_pos) const; - int contains_mouse_vertical(const Vec2d& mouse_pos) const; - - void render_horizontal() const; - void render_vertical() const; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE }; -#if !ENABLE_TOOLBAR_BACKGROUND_TEXTURE -class GLRadioToolbarItem -{ -public: - struct Data - { - std::string name; - std::string tooltip; - unsigned int sprite_id; - wxEventType action_event; - - Data(); - }; - - enum EState : unsigned char - { - Normal, - Pressed, - Hover, - HoverPressed, - Num_States - }; - -private: - EState m_state; - Data m_data; - -public: - GLRadioToolbarItem(const Data& data); - - EState get_state() const; - void set_state(EState state); - - const std::string& get_name() const; - const std::string& get_tooltip() const; - - bool is_hovered() const; - bool is_pressed() const; - - void do_action(wxEvtHandler *target); - - void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; - -private: - GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int border_size, unsigned int icon_size, unsigned int gap_size) const; -}; - -class GLRadioToolbar -{ - typedef std::vector ItemsList; - - ItemsIconsTexture m_icons_texture; - - ItemsList m_items; - float m_top; - float m_left; - -public: - GLRadioToolbar(); - ~GLRadioToolbar(); - - bool init(const std::string& icons_texture_filename, unsigned int items_icon_size, unsigned int items_icon_border_size, unsigned int items_icon_gap_size); - - bool add_item(const GLRadioToolbarItem::Data& data); - - float get_height() const; - - void set_position(float top, float left); - void set_selection(const std::string& name); - - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; - - std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); - - void do_action(unsigned int item_id, GLCanvas3D& parent); - - void render(const GLCanvas3D& parent) const; -}; -#endif // !ENABLE_TOOLBAR_BACKGROUND_TEXTURE - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 459bd9e5b..59b4a727d 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -91,11 +91,7 @@ bool View3D::init(wxWindow* parent, Model* model, DynamicPrintConfig* config, Ba return true; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void View3D::set_view_toolbar(GLToolbar* toolbar) -#else -void View3D::set_view_toolbar(GLRadioToolbar* toolbar) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); @@ -366,11 +362,7 @@ Preview::~Preview() } #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void Preview::set_view_toolbar(GLToolbar* toolbar) -#else -void Preview::set_view_toolbar(GLRadioToolbar* toolbar) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index bd71adcb5..e04946255 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -28,15 +28,9 @@ class Model; namespace GUI { class GLCanvas3D; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER class GLToolbar; #endif // ENABLE_REMOVE_TABS_FROM_PLATER -#else -#if ENABLE_REMOVE_TABS_FROM_PLATER -class GLRadioToolbar; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #if ENABLE_REMOVE_TABS_FROM_PLATER class View3D : public wxPanel @@ -59,11 +53,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } GLCanvas3D* get_canvas3d() { return m_canvas; } -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_view_toolbar(GLToolbar* toolbar); -#else - void set_view_toolbar(GLRadioToolbar* toolbar); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_as_dirty(); void set_bed_shape(const Pointfs& shape); @@ -131,11 +121,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } #if ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE void set_view_toolbar(GLToolbar* toolbar); -#else - void set_view_toolbar(GLRadioToolbar* toolbar); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_number_extruders(unsigned int number_extruders); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 92c408670..5dd3d0ee0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -913,11 +913,7 @@ struct Plater::priv Sidebar *sidebar; #if ENABLE_REMOVE_TABS_FROM_PLATER View3D* view3D; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE GLToolbar view_toolbar; -#else - GLRadioToolbar view_toolbar; -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE #else #if !ENABLE_IMGUI wxPanel *panel3d; @@ -1080,9 +1076,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) #endif // !ENABLE_REMOVE_TABS_FROM_PLATER , delayed_scene_refresh(false) , project_filename(wxEmptyString) -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE , view_toolbar(GLToolbar::Radio) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE { arranging.store(false); rotoptimizing.store(false); @@ -2674,7 +2668,6 @@ bool Plater::priv::complit_init_part_menu() #if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::init_view_toolbar() { -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE ItemsIconsTexture::Metadata icons_data; icons_data.filename = "view_toolbar.png"; icons_data.icon_size = 64; @@ -2689,12 +2682,8 @@ void Plater::priv::init_view_toolbar() background_data.bottom = 16; if (!view_toolbar.init(icons_data, background_data)) -#else - if (!view_toolbar.init("view_toolbar.png", 64, 0, 0)) -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE return; -#if ENABLE_TOOLBAR_BACKGROUND_TEXTURE view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); view_toolbar.set_border(5.0f); view_toolbar.set_gap_size(1.0f); @@ -2725,28 +2714,6 @@ void Plater::priv::init_view_toolbar() view3D->set_view_toolbar(&view_toolbar); preview->set_view_toolbar(&view_toolbar); -#else - GLRadioToolbarItem::Data item; - - item.name = "3D"; - item.tooltip = GUI::L_str("3D editor view"); - item.sprite_id = 0; - item.action_event = EVT_GLVIEWTOOLBAR_3D; - if (!view_toolbar.add_item(item)) - return; - - item.name = "Preview"; - item.tooltip = GUI::L_str("Preview"); - item.sprite_id = 1; - item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; - if (!view_toolbar.add_item(item)) - return; - - view3D->set_view_toolbar(&view_toolbar); - preview->set_view_toolbar(&view_toolbar); - - view_toolbar.set_selection("3D"); -#endif // ENABLE_TOOLBAR_BACKGROUND_TEXTURE } #endif // ENABLE_REMOVE_TABS_FROM_PLATER From d0fc4037413cb490c426fcfb6fdc9cd97aa2a0f7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 2 Jan 2019 16:27:11 +0100 Subject: [PATCH 122/132] Fix of a regression bug in Dauglass Peucker contour simplification after an introduction of a non-recursive variant of the algorithm. Fixes "Strange behavior while printing curved perimeters #1503" Fixed some compilation warnings. --- src/libslic3r/MultiPoint.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/MultiPoint.cpp b/src/libslic3r/MultiPoint.cpp index 795ee38c2..7b483cba0 100644 --- a/src/libslic3r/MultiPoint.cpp +++ b/src/libslic3r/MultiPoint.cpp @@ -18,8 +18,8 @@ void MultiPoint::scale(double factor_x, double factor_y) { for (Point &pt : points) { - pt(0) *= factor_x; - pt(1) *= factor_y; + pt(0) = coord_t(pt(0) * factor_x); + pt(1) = coord_t(pt(1) * factor_y); } } @@ -83,7 +83,7 @@ MultiPoint::find_point(const Point &point) const { for (const Point &pt : this->points) if (pt == point) - return &pt - &this->points.front(); + return int(&pt - &this->points.front()); return -1; // not found } @@ -165,6 +165,7 @@ bool MultiPoint::first_intersection(const Line& line, Point* intersection) const std::vector MultiPoint::_douglas_peucker(const std::vector& pts, const double tolerance) { std::vector result_pts; + double tolerance_sq = tolerance * tolerance; if (! pts.empty()) { const Point *anchor = &pts.front(); size_t anchor_idx = 0; @@ -178,18 +179,18 @@ std::vector MultiPoint::_douglas_peucker(const std::vector& pts, c dpStack.reserve(pts.size()); dpStack.emplace_back(floater_idx); for (;;) { - double max_distSq = 0.0; + double max_dist_sq = 0.0; size_t furthest_idx = anchor_idx; // find point furthest from line seg created by (anchor, floater) and note it for (size_t i = anchor_idx + 1; i < floater_idx; ++ i) { - double dist = Line::distance_to_squared(pts[i], *anchor, *floater); - if (dist > max_distSq) { - max_distSq = dist; + double dist_sq = Line::distance_to_squared(pts[i], *anchor, *floater); + if (dist_sq > max_dist_sq) { + max_dist_sq = dist_sq; furthest_idx = i; } } // remove point if less than tolerance - if (max_distSq <= tolerance) { + if (max_dist_sq <= tolerance_sq) { result_pts.emplace_back(*floater); anchor_idx = floater_idx; anchor = floater; @@ -332,8 +333,8 @@ Points MultiPoint::visivalingam(const Points& pts, const double& tolerance) void MultiPoint3::translate(double x, double y) { for (Vec3crd &p : points) { - p(0) += x; - p(1) += y; + p(0) += coord_t(x); + p(1) += coord_t(y); } } From b7a92b5525c63be6d99879b8d3a941c1242a7633 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 2 Jan 2019 17:49:23 +0100 Subject: [PATCH 123/132] Fix of #1512 "Printer Host Upload Queue vanishes in the background [1.42.0-alpha1]" the canvas3d used to grab focus when mouse entered a window even if the top level window was not active. This was now changed and the top level must be active for the canvas3d to grab the focus. --- src/slic3r/GUI/GLCanvas3D.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1d6c3b117..df70a787e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4906,8 +4906,15 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { #if defined(__WXMSW__) || defined(__linux__) // On Windows and Linux needs focus in order to catch key events - if (m_canvas != nullptr) - m_canvas->SetFocus(); + if (m_canvas != nullptr) { + // Only set focus, if the top level window of this canvas is active. + auto p = dynamic_cast(evt.GetEventObject()); + while (p->GetParent()) + p = p->GetParent(); + auto *top_level_wnd = dynamic_cast(p); + if (top_level_wnd && top_level_wnd->IsActive()) + m_canvas->SetFocus(); + } m_mouse.set_start_position_2D_as_invalid(); #endif From 42274995344feec4baa0003162d14dc8e7567b18 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 2 Jan 2019 17:54:54 +0100 Subject: [PATCH 124/132] Fix of "Color Picker Placement #1496" Now the color picker from the plater is centered around the associated combo box. Keep your fingers crossed, it seems to be working, but on a two monitor setup the color picker may jump to the other screen. --- src/slic3r/GUI/Plater.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5dd3d0ee0..f3722593c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -246,7 +246,8 @@ PresetComboBox::PresetComboBox(wxWindow *parent, Preset::Type preset_type) : // Swallow the mouse click and open the color picker. auto data = new wxColourData(); data->SetChooseFull(1); - auto dialog = new wxColourDialog(wxGetApp().mainframe, data); + auto dialog = new wxColourDialog(/* wxGetApp().mainframe */this, data); + dialog->CenterOnParent(); if (dialog->ShowModal() == wxID_OK) { DynamicPrintConfig cfg = *wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_config(); From c4d5c7cefd92590a731be55800405f3590a20bbf Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 2 Jan 2019 18:36:48 +0100 Subject: [PATCH 125/132] Fix of "When exporting plate as STL Files are saved as 1KB and corrupted #1536" This is a regression issue, and it only happened when exporting the plater. --- src/slic3r/GUI/Plater.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f3722593c..668c5559a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3045,7 +3045,7 @@ void Plater::export_stl(bool selection_only) if (obj_idx == -1) { return; } mesh = p->model.objects[obj_idx]->mesh(); } else { - auto mesh = p->model.mesh(); + mesh = p->model.mesh(); } Slic3r::store_stl(path_u8.c_str(), &mesh, true); From d426895580f137ee8fa5629d1290f9e8477a0fba Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Jan 2019 09:12:50 +0100 Subject: [PATCH 126/132] Automatic selection of newly loaded objects --- src/slic3r/GUI/Plater.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 668c5559a..65ec044fd 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1503,6 +1503,17 @@ std::vector Plater::priv::load_files(const std::vector& input_ statusbar()->set_status_text(_(L("Loaded"))); } + // automatic selection of added objects + if (!obj_idxs.empty() && (view3D != nullptr)) + { + GLCanvas3D::Selection& selection = view3D->get_canvas3d()->get_selection(); + selection.clear(); + for (size_t idx : obj_idxs) + { + selection.add_object((unsigned int)idx, false); + } + } + return obj_idxs; } From ca6a7ac8d52cd65d96304b0daa46334a5d200b5c Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Jan 2019 11:24:03 +0100 Subject: [PATCH 127/132] Further improvement in objects manipulation using sidebar (multiselection) --- src/libslic3r/Technologies.hpp | 60 +++++++++++------------ src/slic3r/GUI/GLCanvas3D.cpp | 50 ++++++++++++++++--- src/slic3r/GUI/GLCanvas3D.hpp | 4 ++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 12 +++++ src/slic3r/GUI/Plater.cpp | 4 ++ 5 files changed, 94 insertions(+), 36 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 681259996..38d0f5b6e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -10,41 +10,41 @@ // Log debug messages to console when changing selection #define ENABLE_SELECTION_DEBUG_OUTPUT 0 -//============= -// 1.42.0 techs -//============= -#define ENABLE_1_42_0 1 - -// Uses a unique opengl context -#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0) -// Disable synchronization of unselected instances -#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0) -// Keeps objects on bed while scaling them using the scale gizmo -#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0) -// All rotations made using the rotate gizmo are done with respect to the world reference system -#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0) -// Scene's GUI made using imgui library -#define ENABLE_IMGUI (1 && ENABLE_1_42_0) -#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI) -// Modified Sla support gizmo -#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0) -// Removes the wxNotebook from plater -#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0) -// Use wxDataViewRender instead of wxDataViewCustomRenderer -#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0) -// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active -#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0) -// Show visual hints in the 3D scene when sidebar matrix fields have focus -#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0) -// Separate rendering for opaque and transparent volumes -#define ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_1_42_0) - //==================== // 1.42.0.alpha1 techs //==================== #define ENABLE_1_42_0_ALPHA1 1 -#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA1) +// Uses a unique opengl context +#define ENABLE_USE_UNIQUE_GLCONTEXT (1 && ENABLE_1_42_0_ALPHA1) +// Disable synchronization of unselected instances +#define DISABLE_INSTANCES_SYNCH (0 && ENABLE_1_42_0_ALPHA1) +// Keeps objects on bed while scaling them using the scale gizmo +#define ENABLE_ENSURE_ON_BED_WHILE_SCALING (1 && ENABLE_1_42_0_ALPHA1) +// All rotations made using the rotate gizmo are done with respect to the world reference system +#define ENABLE_WORLD_ROTATIONS (1 && ENABLE_1_42_0_ALPHA1) +// Scene's GUI made using imgui library +#define ENABLE_IMGUI (1 && ENABLE_1_42_0_ALPHA1) +#define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI) +// Modified Sla support gizmo +#define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1) +// Removes the wxNotebook from plater +#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0_ALPHA1) +// Use wxDataViewRender instead of wxDataViewCustomRenderer +#define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) +// Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active +#define ENABLE_RENDER_SELECTION_CENTER (0 && ENABLE_1_42_0_ALPHA1) +// Show visual hints in the 3D scene when sidebar matrix fields have focus +#define ENABLE_SIDEBAR_VISUAL_HINTS (1 && ENABLE_1_42_0_ALPHA1) +// Separate rendering for opaque and transparent volumes +#define ENABLE_IMPROVED_TRANSPARENT_VOLUMES_RENDERING (1 && ENABLE_1_42_0_ALPHA1) + +//==================== +// 1.42.0.alpha2 techs +//==================== +#define ENABLE_1_42_0_ALPHA2 1 + +#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index df70a787e..a49a130ee 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3648,6 +3648,10 @@ wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxDEFINE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDEFINE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); @@ -5023,6 +5027,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) bool already_selected = m_selection.contains_volume(m_hover_volume_id); bool shift_down = evt.ShiftDown(); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + Selection::IndicesList curr_idxs = m_selection.get_volume_idxs(); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (already_selected && shift_down) m_selection.remove(m_hover_volume_id); else @@ -5031,11 +5039,21 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_selection.add(m_hover_volume_id, add_as_single); } - m_gizmos.update_on_off_state(m_selection); - _update_gizmos_data(); - wxGetApp().obj_manipul()->update_settings_value(m_selection); - post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - m_dirty = true; +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (curr_idxs != m_selection.get_volume_idxs()) + { +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + + m_gizmos.update_on_off_state(m_selection); + _update_gizmos_data(); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + wxGetApp().obj_manipul()->update_settings_value(m_selection); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); + m_dirty = true; +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + } +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } @@ -5408,6 +5426,15 @@ void GLCanvas3D::do_move() ModelObject* model_object = m_model->objects[object_idx]; if (model_object != nullptr) { +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (selection_mode == Selection::Instance) + model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); + else if (selection_mode == Selection::Volume) + model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); + + object_moved = true; + model_object->invalidate_bounding_box(); +#else if (selection_mode == Selection::Instance) { model_object->instances[instance_idx]->set_offset(v->get_instance_offset()); @@ -5419,7 +5446,8 @@ void GLCanvas3D::do_move() object_moved = true; } if (object_moved) - model_object->invalidate_bounding_box(); + model_object->invalidate_bounding_box(); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } else if (object_idx == 1000) @@ -5490,7 +5518,12 @@ void GLCanvas3D::do_rotate() m->translate_instance(i.second, shift); } +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (!done.empty()) + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); +#else post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void GLCanvas3D::do_scale() @@ -5541,7 +5574,12 @@ void GLCanvas3D::do_scale() m->translate_instance(i.second, shift); } +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + if (!done.empty()) + post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); +#else post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void GLCanvas3D::do_flatten() diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 3c963f8be..a46168094 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -110,6 +110,10 @@ wxDECLARE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); // data: +1 => increase, -1 => decrease wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_WIPETOWER_MOVED, Vec3dEvent); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_ROTATED, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_INSTANCE_SCALED, SimpleEvent); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxDECLARE_EVENT(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, Event); wxDECLARE_EVENT(EVT_GLCANVAS_UPDATE_GEOMETRY, Vec3dsEvent<2>); wxDECLARE_EVENT(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, SimpleEvent); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 9b730b3f2..32a38a77c 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -413,6 +413,10 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) canvas->get_selection().start_dragging(); canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance()); canvas->do_rotate(); + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_cache_rotation = rotation; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::change_scale_value(const Vec3d& scale) @@ -443,6 +447,10 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) canvas->get_selection().start_dragging(); canvas->get_selection().scale(scaling_factor, false); canvas->do_scale(); + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_cache_scale = scale; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::change_size_value(const Vec3d& size) @@ -457,6 +465,10 @@ void ObjectManipulation::change_size_value(const Vec3d& size) } change_scale_value(100.0 * Vec3d(size(0) / ref_size(0), size(1) / ref_size(1), size(2) / ref_size(2))); + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + m_cache_size = size; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } //namespace GUI diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 65ec044fd..bb5eec204 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1196,6 +1196,10 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_SCALED, [this](SimpleEvent&) { update(); }); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION view3D_canvas->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); From 0997825e0e3a779fcfa9a59d3bb7d530d3f78e30 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Jan 2019 12:04:14 +0100 Subject: [PATCH 128/132] Fixed import from amf files --- src/slic3r/GUI/Plater.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bb5eec204..179aa9476 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -936,6 +936,7 @@ struct Plater::priv static const std::regex pattern_bundle; static const std::regex pattern_3mf; static const std::regex pattern_zip_amf; + static const std::regex pattern_any_amf; priv(Plater *q, MainFrame *main_frame); @@ -1051,6 +1052,8 @@ private: const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); const std::regex Plater::priv::pattern_3mf(".*3mf", std::regex::icase); const std::regex Plater::priv::pattern_zip_amf(".*[.]zip[.]amf", std::regex::icase); +const std::regex Plater::priv::pattern_any_amf(".*[.](amf|amf[.]xml|zip[.]amf)", std::regex::icase); + Plater::priv::priv(Plater *q, MainFrame *main_frame) : q(q) , main_frame(main_frame) @@ -1400,6 +1403,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ const bool type_3mf = std::regex_match(path.string(), pattern_3mf); const bool type_zip_amf = !type_3mf && std::regex_match(path.string(), pattern_zip_amf); + const bool type_any_amf = !type_3mf && std::regex_match(path.string(), pattern_any_amf); Slic3r::Model model; try { @@ -1455,7 +1459,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ } } - if (type_3mf) { + if (type_3mf || type_any_amf) { for (ModelObject* model_object : model.objects) { model_object->center_around_origin(); model_object->ensure_on_bed(); From 0d7e53badb4c62ff1a8638ca916109b524f52b6c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 3 Jan 2019 12:32:13 +0100 Subject: [PATCH 129/132] Fix of 1.42.0-Alpha1 won't open from self contained folder #1500 Added some missing UTF8 to wxString path explicit conversions. --- src/slic3r/GUI/GLTexture.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 6 +++--- src/slic3r/GUI/wxExtensions.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 235e3d93b..0ac7e983e 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -36,7 +36,7 @@ bool GLTexture::load_from_file(const std::string& filename, bool generate_mipmap // Load a PNG with an alpha channel. wxImage image; - if (!image.LoadFile(filename, wxBITMAP_TYPE_PNG)) + if (!image.LoadFile(wxString::FromUTF8(filename), wxBITMAP_TYPE_PNG)) { reset(); return false; diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0138c1229..b1ecef7df 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -459,7 +459,7 @@ bool GUI_App::select_language( wxArrayString & names, { m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[index]); - m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); + m_wxLocale->AddCatalogLookupPathPrefix(wxString::FromUTF8(localization_dir())); m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); #ifdef WIN32 @@ -489,7 +489,7 @@ bool GUI_App::load_language() { m_wxLocale = new wxLocale; m_wxLocale->Init(identifiers[i]); - m_wxLocale->AddCatalogLookupPathPrefix(localization_dir()); + m_wxLocale->AddCatalogLookupPathPrefix(wxString::FromUTF8(localization_dir())); m_wxLocale->AddCatalog(/*GetAppName()*/"Slic3rPE"); wxSetlocale(LC_NUMERIC, "C"); #ifdef WIN32 @@ -519,7 +519,7 @@ void GUI_App::get_installed_languages(wxArrayString & names, wxArrayLong & ident names.Clear(); identifiers.Clear(); - wxDir dir(localization_dir()); + wxDir dir(wxString::FromUTF8(localization_dir())); wxString filename; const wxLanguageInfo * langinfo; wxString name = wxLocale::GetLanguageName(wxLANGUAGE_DEFAULT); diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index dc917f875..6ea47b45b 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -37,7 +37,7 @@ wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxMenuItem* append_menu_item(wxMenu* menu, int id, const wxString& string, const wxString& description, std::function cb, const std::string& icon, wxEvtHandler* event_handler) { - const wxBitmap& bmp = !icon.empty() ? wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG) : wxNullBitmap; + const wxBitmap& bmp = !icon.empty() ? wxBitmap(wxString::FromUTF8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG) : wxNullBitmap; return append_menu_item(menu, id, string, description, cb, bmp, event_handler); } @@ -48,7 +48,7 @@ wxMenuItem* append_submenu(wxMenu* menu, wxMenu* sub_menu, int id, const wxStrin wxMenuItem* item = new wxMenuItem(menu, id, string, description); if (!icon.empty()) - item->SetBitmap(wxBitmap(Slic3r::var(icon), wxBITMAP_TYPE_PNG)); + item->SetBitmap(wxBitmap(wxString::FromUTF8(Slic3r::var(icon)), wxBITMAP_TYPE_PNG)); item->SetSubMenu(sub_menu); menu->Append(item); From dd8f79381f58c7ba729903a128ec7e7e45fc22c9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Jan 2019 12:35:01 +0100 Subject: [PATCH 130/132] Fixed hover icons in view toolbar --- resources/icons/view_toolbar.png | Bin 8615 -> 16695 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/resources/icons/view_toolbar.png b/resources/icons/view_toolbar.png index 6c6179dd5ba7bec4485b42544098839b526cb362..8cfb8d0918777df31e600e2e24336f6d2cdeda32 100644 GIT binary patch literal 16695 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

9QLxUD+Vp_qnIXNPChcx#`Q_#vG71+$r})jzZaz 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 From b099d5c05ed6c57961a95b3223668b41231c58ce Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 3 Jan 2019 12:59:06 +0100 Subject: [PATCH 131/132] Removal of wxNotebook from plater set as default --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 54 ------ src/slic3r/GUI/GLCanvas3D.hpp | 16 -- src/slic3r/GUI/GLToolbar.cpp | 31 ---- src/slic3r/GUI/GLToolbar.hpp | 9 - src/slic3r/GUI/GUI_Preview.cpp | 30 --- src/slic3r/GUI/GUI_Preview.hpp | 16 -- src/slic3r/GUI/MainFrame.cpp | 14 -- src/slic3r/GUI/Plater.cpp | 327 +-------------------------------- src/slic3r/GUI/Plater.hpp | 3 +- 10 files changed, 5 insertions(+), 497 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 38d0f5b6e..c32fe3e8d 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -28,8 +28,6 @@ #define DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI (1 && ENABLE_IMGUI) // Modified Sla support gizmo #define ENABLE_SLA_SUPPORT_GIZMO_MOD (1 && ENABLE_1_42_0_ALPHA1) -// Removes the wxNotebook from plater -#define ENABLE_REMOVE_TABS_FROM_PLATER (1 && ENABLE_1_42_0_ALPHA1) // Use wxDataViewRender instead of wxDataViewCustomRenderer #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) // Renders a small sphere in the center of the bounding box of the current selection when no gizmo is active diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a49a130ee..1695a532b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3635,9 +3635,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const } } -#if ENABLE_REMOVE_TABS_FROM_PLATER wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); @@ -3662,9 +3660,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas) , m_context(nullptr) , m_in_render(false) , m_toolbar(GLToolbar::Normal) -#if ENABLE_REMOVE_TABS_FROM_PLATER , m_view_toolbar(nullptr) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER , m_use_clipping_planes(false) , m_sidebar_field("") , m_config(nullptr) @@ -3813,9 +3809,7 @@ bool GLCanvas3D::init(bool useVBOs, bool use_legacy_opengl) return false; #endif // ENABLE_SIDEBAR_VISUAL_HINTS -#if ENABLE_REMOVE_TABS_FROM_PLATER post_event(SimpleEvent(EVT_GLCANVAS_INIT)); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER m_initialized = true; @@ -3862,21 +3856,12 @@ void GLCanvas3D::reset_volumes() _reset_warning_texture(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER int GLCanvas3D::check_volumes_outside_state() const { ModelInstance::EPrintVolumeState state; m_volumes.check_outside_state(m_config, &state); return (int)state; } -#else -int GLCanvas3D::check_volumes_outside_state(const DynamicPrintConfig* config) const -{ - ModelInstance::EPrintVolumeState state; - m_volumes.check_outside_state(config, &state); - return (int)state; -} -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void GLCanvas3D::set_config(DynamicPrintConfig* config) { @@ -4141,10 +4126,6 @@ void GLCanvas3D::render() float theta = m_camera.get_theta(); bool is_custom_bed = m_bed.is_custom(); -#if !ENABLE_REMOVE_TABS_FROM_PLATER - set_tooltip(""); -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER - #if ENABLE_IMGUI wxGetApp().imgui()->new_frame(); #endif // ENABLE_IMGUI @@ -4200,13 +4181,9 @@ void GLCanvas3D::render() _render_gizmos_overlay(); _render_warning_texture(); _render_legend_texture(); -#if ENABLE_REMOVE_TABS_FROM_PLATER _resize_toolbars(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER _render_toolbar(); -#if ENABLE_REMOVE_TABS_FROM_PLATER _render_view_toolbar(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER _render_layer_editing_overlay(); #if ENABLE_IMGUI @@ -4902,9 +4879,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_layers_editing.last_object_id = layer_editing_object_idx; bool gizmos_overlay_contains_mouse = m_gizmos.overlay_contains_mouse(*this, m_mouse.position); int toolbar_contains_mouse = m_toolbar.contains_mouse(m_mouse.position, *this); -#if ENABLE_REMOVE_TABS_FROM_PLATER int view_toolbar_contains_mouse = (m_view_toolbar != nullptr) ? m_view_toolbar->contains_mouse(m_mouse.position, *this) : -1; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (evt.Entering()) { @@ -5001,13 +4976,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_gizmos.get_current_type() == Gizmos::SlaSupports) m_gizmos.delete_current_grabber(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER else if (view_toolbar_contains_mouse != -1) { if (m_view_toolbar != nullptr) m_view_toolbar->do_action((unsigned int)view_toolbar_contains_mouse, *this); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER else if (toolbar_contains_mouse != -1) { m_toolbar_action_running = true; @@ -5292,7 +5265,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if (evt.Moving()) { m_mouse.position = pos.cast(); -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string tooltip = ""; // updates gizmos overlay @@ -5314,7 +5286,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } set_tooltip(tooltip); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor hovers over. if (m_picking_enabled) @@ -6074,20 +6045,6 @@ void GLCanvas3D::_picking_pass() const } _update_volumes_hover_state(); - -#if !ENABLE_REMOVE_TABS_FROM_PLATER - // updates gizmos overlay - if (!m_selection.is_empty()) - { - std::string name = m_gizmos.update_hover_state(*this, pos, m_selection); - if (!name.empty()) - set_tooltip(name); - } - else - m_gizmos.reset_all_states(); - - m_toolbar.update_hover_state(pos); -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER } } @@ -6346,19 +6303,14 @@ void GLCanvas3D::_render_gizmos_overlay() const void GLCanvas3D::_render_toolbar() const { -#if !ENABLE_REMOVE_TABS_FROM_PLATER - _resize_toolbar(); -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER m_toolbar.render(*this); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void GLCanvas3D::_render_view_toolbar() const { if (m_view_toolbar != nullptr) m_view_toolbar->render(*this); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER #if ENABLE_SHOW_CAMERA_TARGET void GLCanvas3D::_render_camera_target() const @@ -7972,11 +7924,7 @@ bool GLCanvas3D::_is_any_volume_outside() const return false; } -#if ENABLE_REMOVE_TABS_FROM_PLATER void GLCanvas3D::_resize_toolbars() const -#else -void GLCanvas3D::_resize_toolbar() const -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { Size cnv_size = get_canvas_size(); float zoom = get_camera_zoom(); @@ -8023,7 +7971,6 @@ void GLCanvas3D::_resize_toolbar() const } } -#if ENABLE_REMOVE_TABS_FROM_PLATER if (m_view_toolbar != nullptr) { // places the toolbar on the bottom-left corner of the 3d scene @@ -8031,7 +7978,6 @@ void GLCanvas3D::_resize_toolbar() const float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; m_view_toolbar->set_position(top, left); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } const Print* GLCanvas3D::fff_print() const diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a46168094..73aa4bd02 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -97,9 +97,7 @@ template using Vec2dsEvent = ArrayEvent; using Vec3dEvent = Event; template using Vec3dsEvent = ArrayEvent; -#if ENABLE_REMOVE_TABS_FROM_PLATER wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); @@ -787,9 +785,7 @@ private: Mouse m_mouse; mutable Gizmos m_gizmos; mutable GLToolbar m_toolbar; -#if ENABLE_REMOVE_TABS_FROM_PLATER GLToolbar* m_view_toolbar; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER ClippingPlane m_clipping_planes[2]; bool m_use_clipping_planes; mutable SlaCap m_sla_caps[2]; @@ -838,9 +834,7 @@ public: wxGLCanvas* get_wxglcanvas() { return m_canvas; } -#if ENABLE_REMOVE_TABS_FROM_PLATER void set_view_toolbar(GLToolbar* toolbar) { m_view_toolbar = toolbar; } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER bool init(bool useVBOs, bool use_legacy_opengl); void post_event(wxEvent &&event); @@ -853,11 +847,7 @@ public: unsigned int get_volumes_count() const; void reset_volumes(); -#if ENABLE_REMOVE_TABS_FROM_PLATER int check_volumes_outside_state() const; -#else - int check_volumes_outside_state(const DynamicPrintConfig* config) const; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_config(DynamicPrintConfig* config); void set_process(BackgroundSlicingProcess* process); @@ -1017,9 +1007,7 @@ private: void _render_current_gizmo() const; void _render_gizmos_overlay() const; void _render_toolbar() const; -#if ENABLE_REMOVE_TABS_FROM_PLATER void _render_view_toolbar() const; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER #if ENABLE_SHOW_CAMERA_TARGET void _render_camera_target() const; #endif // ENABLE_SHOW_CAMERA_TARGET @@ -1086,11 +1074,7 @@ private: bool _is_any_volume_outside() const; -#if ENABLE_REMOVE_TABS_FROM_PLATER void _resize_toolbars() const; -#else - void _resize_toolbar() const; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER static std::vector _parse_colors(const std::vector& colors); diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 9bd2358bd..0efc19dee 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -355,30 +355,16 @@ bool GLToolbar::is_item_disabled(const std::string& name) const return false; } -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_REMOVE_TABS_FROM_PLATER if (!m_enabled) return ""; -#else - if (!m_enabled) - return; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER switch (m_layout.type) { default: -#if ENABLE_REMOVE_TABS_FROM_PLATER case Layout::Horizontal: { return update_hover_state_horizontal(mouse_pos, parent); } case Layout::Vertical: { return update_hover_state_vertical(mouse_pos, parent); } -#else - case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; } - case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } } @@ -512,11 +498,7 @@ float GLToolbar::get_main_size() const return size; } -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; @@ -606,19 +588,10 @@ void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D } } -#if ENABLE_REMOVE_TABS_FROM_PLATER return tooltip; -#else - if (!tooltip.empty()) - m_parent.set_tooltip(tooltip); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) -#else -void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; @@ -708,11 +681,7 @@ void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& } } -#if ENABLE_REMOVE_TABS_FROM_PLATER return tooltip; -#else - m_parent.set_tooltip(tooltip); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 49296796d..37eef5708 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -224,11 +224,7 @@ public: bool is_item_pressed(const std::string& name) const; bool is_item_disabled(const std::string& name) const; -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // returns the id of the item under the given mouse position or -1 if none int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; @@ -244,13 +240,8 @@ private: float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; -#if ENABLE_REMOVE_TABS_FROM_PLATER std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); -#else - void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); - void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 59b4a727d..1092c761f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -27,7 +27,6 @@ namespace Slic3r { namespace GUI { -#if ENABLE_REMOVE_TABS_FROM_PLATER View3D::View3D(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process) : m_canvas_widget(nullptr) , m_canvas(nullptr) @@ -184,13 +183,8 @@ void View3D::render() if (m_canvas != nullptr) m_canvas->render(); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_REMOVE_TABS_FROM_PLATER Preview::Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) -#else -Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process_func) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER : m_canvas_widget(nullptr) , m_canvas(nullptr) , m_double_slider_sizer(nullptr) @@ -211,42 +205,20 @@ Preview::Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSli , m_enabled(false) , m_schedule_background_process(schedule_background_process_func) { -#if ENABLE_REMOVE_TABS_FROM_PLATER if (init(parent, config, process, gcode_preview_data)) { show_hide_ui_elements("none"); load_print(); } -#else - if (init(notebook, config, process, gcode_preview_data)) - { - notebook->AddPage(this, _(L("Preview"))); - show_hide_ui_elements("none"); - load_print(); - } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_REMOVE_TABS_FROM_PLATER bool Preview::init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data) -#else -bool Preview::init(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER { -#if ENABLE_REMOVE_TABS_FROM_PLATER if ((config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr)) return false; if (!Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize)) return false; -#else - if ((notebook == nullptr) || (config == nullptr) || (process == nullptr) || (gcode_preview_data == nullptr)) - return false; - - // creates this panel add append it to the given notebook as a new page - if (!Create(notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize)) - return false; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER m_canvas_widget = GLCanvas3DManager::create_wxglcanvas(this); _3DScene::add_canvas(m_canvas_widget); @@ -361,13 +333,11 @@ Preview::~Preview() } } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Preview::set_view_toolbar(GLToolbar* toolbar) { if (m_canvas != nullptr) m_canvas->set_view_toolbar(toolbar); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void Preview::set_number_extruders(unsigned int number_extruders) { diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index e04946255..ccff885f2 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -21,18 +21,13 @@ class DynamicPrintConfig; class Print; class BackgroundSlicingProcess; class GCodePreviewData; -#if ENABLE_REMOVE_TABS_FROM_PLATER class Model; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER namespace GUI { class GLCanvas3D; -#if ENABLE_REMOVE_TABS_FROM_PLATER class GLToolbar; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_REMOVE_TABS_FROM_PLATER class View3D : public wxPanel { wxGLCanvas* m_canvas_widget; @@ -79,7 +74,6 @@ public: private: bool init(wxWindow* parent, Model* model, DynamicPrintConfig* config, BackgroundSlicingProcess* process); }; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER class Preview : public wxPanel { @@ -111,18 +105,12 @@ class Preview : public wxPanel PrusaDoubleSlider* m_slider {nullptr}; public: -#if ENABLE_REMOVE_TABS_FROM_PLATER Preview(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); -#else - Preview(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data, std::function schedule_background_process = [](){}); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER virtual ~Preview(); wxGLCanvas* get_wxglcanvas() { return m_canvas_widget; } -#if ENABLE_REMOVE_TABS_FROM_PLATER void set_view_toolbar(GLToolbar* toolbar); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void set_number_extruders(unsigned int number_extruders); void set_canvas_as_dirty(); @@ -138,11 +126,7 @@ public: void refresh_print(); private: -#if ENABLE_REMOVE_TABS_FROM_PLATER bool init(wxWindow* parent, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data); -#else - bool init(wxNotebook* notebook, DynamicPrintConfig* config, BackgroundSlicingProcess* process, GCodePreviewData* gcode_preview_data); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void bind_event_handlers(); void unbind_event_handlers(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 33c4cf32b..734285db7 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -323,19 +323,13 @@ void MainFrame::init_menubar() { size_t tab_offset = 0; if (m_plater) { -#if ENABLE_REMOVE_TABS_FROM_PLATER append_menu_item(windowMenu, wxID_HIGHEST + 1, L("Plater Tab\tCtrl+1"), L("Show the plater"), [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); -#else - append_menu_item(windowMenu, wxID_ANY, L("Select Plater Tab\tCtrl+1"), L("Show the plater"), - [this](wxCommandEvent&) { select_tab(0); }, "application_view_tile.png"); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER tab_offset += 1; } if (tab_offset > 0) { windowMenu->AppendSeparator(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER append_menu_item(windowMenu, wxID_HIGHEST + 2, L("Print Settings Tab\tCtrl+2"), L("Show the print settings"), [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); append_menu_item(windowMenu, wxID_HIGHEST + 3, L("Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), @@ -365,14 +359,6 @@ void MainFrame::init_menubar() wxAcceleratorTable accel(6, entries); SetAcceleratorTable(accel); #endif // _WIN32 -#else - append_menu_item(windowMenu, wxID_ANY, L("Select Print Settings Tab\tCtrl+2"), L("Show the print settings"), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog.png"); - append_menu_item(windowMenu, wxID_ANY, L("Select Filament Settings Tab\tCtrl+3"), L("Show the filament settings"), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool.png"); - append_menu_item(windowMenu, wxID_ANY, L("Select Printer Settings Tab\tCtrl+4"), L("Show the printer settings"), - [this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer_empty.png"); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER windowMenu->AppendSeparator(); append_menu_item(windowMenu, wxID_ANY, L("Print Host Upload Queue"), L("Display the Print Host Upload Queue window"), diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 179aa9476..c9440a9d0 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -900,28 +900,12 @@ struct Plater::priv Slic3r::GCodePreviewData gcode_preview_data; // GUI elements -#if ENABLE_REMOVE_TABS_FROM_PLATER wxSizer* panel_sizer; wxPanel* current_panel; std::vector panels; -#else - wxNotebook *notebook; - EventGuard guard_on_notebook_changed; - // Note: ^ The on_notebook_changed is guarded here because the wxNotebook d-tor tends to generate - // wxEVT_NOTEBOOK_PAGE_CHANGED events on some platforms, which causes them to be received by a freed Plater. - // EventGuard unbinds the handler in its d-tor. -#endif // ENABLE_REMOVE_TABS_FROM_PLATER Sidebar *sidebar; -#if ENABLE_REMOVE_TABS_FROM_PLATER View3D* view3D; GLToolbar view_toolbar; -#else -#if !ENABLE_IMGUI - wxPanel *panel3d; -#endif // not ENABLE_IMGUI - wxGLCanvas *canvas3Dwidget; // TODO: Use GLCanvas3D when we can - GLCanvas3D *canvas3D; -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER Preview *preview; wxString project_filename; @@ -942,10 +926,8 @@ struct Plater::priv void update(bool force_full_scene_refresh = false); void select_view(const std::string& direction); -#if ENABLE_REMOVE_TABS_FROM_PLATER void select_view_3D(const std::string& name); void select_next_view_3D(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void update_ui_from_settings(); ProgressStatusBar* statusbar(); std::string get_config(const std::string &key) const; @@ -999,11 +981,8 @@ struct Plater::priv void reload_from_disk(); void fix_through_netfabb(const int obj_idx); -#if ENABLE_REMOVE_TABS_FROM_PLATER void set_current_panel(wxPanel* panel); -#else - void on_notebook_changed(wxBookCtrlEvent&); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + void on_select_preset(wxCommandEvent&); void on_slicing_update(SlicingStatusEvent&); void on_slicing_completed(wxCommandEvent&); @@ -1030,9 +1009,7 @@ private: bool complit_init_object_menu(); bool complit_init_sla_object_menu(); bool complit_init_part_menu(); -#if ENABLE_REMOVE_TABS_FROM_PLATER void init_view_toolbar(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER bool can_delete_object() const; bool can_increase_instances() const; @@ -1064,20 +1041,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology" })) -#if !ENABLE_REMOVE_TABS_FROM_PLATER - , notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)) - , guard_on_notebook_changed(notebook, wxEVT_NOTEBOOK_PAGE_CHANGED, &priv::on_notebook_changed, this) -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER , sidebar(new Sidebar(q)) -#if !ENABLE_REMOVE_TABS_FROM_PLATER -#if ENABLE_IMGUI - , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(notebook)) -#else - , panel3d(new wxPanel(notebook, wxID_ANY)) - , canvas3Dwidget(GLCanvas3DManager::create_wxglcanvas(panel3d)) -#endif // ENABLE_IMGUI - , canvas3D(nullptr) -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER , delayed_scene_refresh(false) , project_filename(wxEmptyString) , view_toolbar(GLToolbar::Radio) @@ -1100,27 +1064,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) sla_print.set_status_callback(statuscb); this->q->Bind(EVT_SLICING_UPDATE, &priv::on_slicing_update, this); -#if !ENABLE_REMOVE_TABS_FROM_PLATER - _3DScene::add_canvas(canvas3Dwidget); - this->canvas3D = _3DScene::get_canvas(this->canvas3Dwidget); - this->canvas3D->allow_multisample(GLCanvas3DManager::can_multisample()); -#if ENABLE_IMGUI - notebook->AddPage(canvas3Dwidget, _(L("3D"))); -#else - auto *panel3dsizer = new wxBoxSizer(wxVERTICAL); - panel3dsizer->Add(canvas3Dwidget, 1, wxEXPAND); - auto *panel_gizmo_widgets = new wxPanel(panel3d, wxID_ANY); - panel_gizmo_widgets->SetSizer(new wxBoxSizer(wxVERTICAL)); - panel3dsizer->Add(panel_gizmo_widgets, 0, wxEXPAND); - - panel3d->SetSizer(panel3dsizer); - notebook->AddPage(panel3d, _(L("3D"))); - - canvas3D->set_external_gizmo_widgets_parent(panel_gizmo_widgets); -#endif // ENABLE_IMGUI -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER - -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D = new View3D(q, &model, config, &background_process); preview = new Preview(q, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); // Let the Tab key switch between the 3D view and the layer preview. @@ -1129,51 +1072,24 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) panels.push_back(view3D); panels.push_back(preview); -#else - preview = new GUI::Preview(notebook, config, &background_process, &gcode_preview_data, [this](){ schedule_background_process(); }); - - // XXX: If have OpenGL - this->canvas3D->enable_picking(true); - this->canvas3D->enable_moving(true); - // XXX: more config from 3D.pm - this->canvas3D->set_model(&model); - this->canvas3D->set_process(&background_process); - this->canvas3D->set_config(config); - this->canvas3D->enable_gizmos(true); - this->canvas3D->enable_toolbar(true); - this->canvas3D->enable_force_zoom_to_bed(true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->background_process_timer.SetOwner(this->q, 0); this->q->Bind(wxEVT_TIMER, [this](wxTimerEvent &evt) { this->update_restart_background_process(false, false); }); auto *bed_shape = config->opt("bed_shape"); -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->set_bed_shape(bed_shape->values); -#else - this->canvas3D->set_bed_shape(bed_shape->values); - this->canvas3D->zoom_to_bed(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER preview->set_bed_shape(bed_shape->values); update(); auto *hsizer = new wxBoxSizer(wxHORIZONTAL); -#if ENABLE_REMOVE_TABS_FROM_PLATER panel_sizer = new wxBoxSizer(wxHORIZONTAL); panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0); panel_sizer->Add(preview, 1, wxEXPAND | wxALL, 0); hsizer->Add(panel_sizer, 1, wxEXPAND | wxALL, 0); -#else - hsizer->Add(notebook, 1, wxEXPAND | wxTOP, 1); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER hsizer->Add(sidebar, 0, wxEXPAND | wxLEFT | wxRIGHT, 0); q->SetSizer(hsizer); -//#if ENABLE_REMOVE_TABS_FROM_PLATER -// set_current_panel(view3D); -//#endif // ENABLE_REMOVE_TABS_FROM_PLATER - init_object_menu(); // Events: @@ -1184,7 +1100,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) sidebar->Bind(EVT_OBJ_LIST_OBJECT_SELECT, [this](wxEvent&) { priv::selection_changed(); }); sidebar->Bind(EVT_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); -#if ENABLE_REMOVE_TABS_FROM_PLATER wxGLCanvas* view3D_canvas = view3D->get_wxglcanvas(); // 3DScene events: view3D_canvas->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); @@ -1217,47 +1132,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); -#else - // 3DScene events: - canvas3Dwidget->Bind(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, [this](SimpleEvent&) { this->schedule_background_process(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_OBJECT_SELECT, &priv::on_object_select, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_MODEL_UPDATE, [this](SimpleEvent&) { this->schedule_background_process(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_REMOVE_OBJECT, [q](SimpleEvent&) { q->remove_selected(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) - { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, [this](Event &evt) { this->sidebar->enable_buttons(evt.data); }); - canvas3Dwidget->Bind(EVT_GLCANVAS_UPDATE_GEOMETRY, &priv::on_update_geometry, this); - canvas3Dwidget->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); - // 3DScene/Toolbar: - canvas3Dwidget->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_DELETE, [q](SimpleEvent&) { q->remove_selected(); } ); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_DELETE_ALL, [this](SimpleEvent&) { reset(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_ARRANGE, [this](SimpleEvent&) { arrange(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_MORE, [q](SimpleEvent&) { q->increase_instances(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_FEWER, [q](SimpleEvent&) { q->decrease_instances(); }); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this); - canvas3Dwidget->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Preview events: preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_VIEWPORT_CHANGED, &priv::on_viewport_changed, this); preview->get_wxglcanvas()->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); -#if ENABLE_REMOVE_TABS_FROM_PLATER + view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); }); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER q->Bind(EVT_SLICING_COMPLETED, &priv::on_slicing_completed, this); q->Bind(EVT_PROCESS_COMPLETED, &priv::on_process_completed, this); -#if ENABLE_REMOVE_TABS_FROM_PLATER q->Bind(EVT_GLVIEWTOOLBAR_3D, [q](SimpleEvent&) { q->select_view_3D("3D"); }); q->Bind(EVT_GLVIEWTOOLBAR_PREVIEW, [q](SimpleEvent&) { q->select_view_3D("Preview"); }); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Drop target: q->SetDropTarget(new PlaterDropTarget(q)); // if my understanding is right, wxWindow takes the owenership @@ -1265,9 +1150,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) update_ui_from_settings(); q->Layout(); -#if ENABLE_REMOVE_TABS_FROM_PLATER set_current_panel(view3D); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::update(bool force_full_scene_refresh) @@ -1286,11 +1169,7 @@ void Plater::priv::update(bool force_full_scene_refresh) // Update the SLAPrint from the current Model, so that the reload_scene() // pulls the correct data. update_status = this->update_background_process(); -#if ENABLE_REMOVE_TABS_FROM_PLATER this->view3D->reload_scene(false, force_full_scene_refresh); -#else - this->canvas3D->reload_scene(false, force_full_scene_refresh); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->preview->reload_print(); if (this->printer_technology == ptSLA) this->restart_background_process(update_status); @@ -1298,7 +1177,6 @@ void Plater::priv::update(bool force_full_scene_refresh) this->schedule_background_process(); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::select_view(const std::string& direction) { if (current_panel == view3D) @@ -1322,20 +1200,6 @@ void Plater::priv::select_next_view_3D() else if (current_panel == preview) set_current_panel(view3D); } -#else -void Plater::priv::select_view(const std::string& direction) -{ - int page_id = notebook->GetSelection(); - if (page_id != wxNOT_FOUND) - { - const wxString& page_text = notebook->GetPageText(page_id); - if (page_text == _(L("3D"))) - this->canvas3D->select_view(direction); - else if (page_text == _(L("Preview"))) - preview->select_view(direction); - } -} -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. @@ -1641,20 +1505,12 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType const GLCanvas3D::Selection& Plater::priv::get_selection() const { -#if ENABLE_REMOVE_TABS_FROM_PLATER return view3D->get_canvas3d()->get_selection(); -#else - return canvas3D->get_selection(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } GLCanvas3D::Selection& Plater::priv::get_selection() { -#if ENABLE_REMOVE_TABS_FROM_PLATER return view3D->get_canvas3d()->get_selection(); -#else - return canvas3D->get_selection(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } int Plater::priv::get_selected_object_idx() const @@ -1677,7 +1533,6 @@ int Plater::priv::get_selected_volume_idx() const void Plater::priv::selection_changed() { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_toolbar_item("delete", can_delete_object()); view3D->enable_toolbar_item("more", can_increase_instances()); view3D->enable_toolbar_item("fewer", can_decrease_instances()); @@ -1686,48 +1541,24 @@ void Plater::priv::selection_changed() view3D->enable_toolbar_item("layersediting", layers_height_allowed()); // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) view3D->render(); -#else - this->canvas3D->enable_toolbar_item("delete", can_delete_object()); - this->canvas3D->enable_toolbar_item("more", can_increase_instances()); - this->canvas3D->enable_toolbar_item("fewer", can_decrease_instances()); - this->canvas3D->enable_toolbar_item("splitobjects", can_split/*_to_objects*/()); - this->canvas3D->enable_toolbar_item("splitvolumes", can_split/*_to_volumes*/()); - this->canvas3D->enable_toolbar_item("layersediting", layers_height_allowed()); - // forces a frame render to update the view (to avoid a missed update if, for example, the context menu appears) - this->canvas3D->render(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::object_list_changed() { -#if ENABLE_REMOVE_TABS_FROM_PLATER // Enable/disable buttons depending on whether there are any objects on the platter. view3D->enable_toolbar_item("deleteall", can_delete_all()); view3D->enable_toolbar_item("arrange", can_arrange()); -#else - // Enable/disable buttons depending on whether there are any objects on the platter. - this->canvas3D->enable_toolbar_item("deleteall", can_delete_all()); - this->canvas3D->enable_toolbar_item("arrange", can_arrange()); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER const bool export_in_progress = this->background_process.is_export_scheduled(); // || ! send_gcode_file.empty()); // XXX: is this right? -#if ENABLE_REMOVE_TABS_FROM_PLATER const bool model_fits = view3D->check_volumes_outside_state() == ModelInstance::PVS_Inside; -#else - const bool model_fits = this->canvas3D->check_volumes_outside_state(config) == ModelInstance::PVS_Inside; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER sidebar->enable_buttons(!model.objects.empty() && !export_in_progress && model_fits); } void Plater::priv::select_all() { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->select_all(); -#else - this->canvas3D->select_all(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->sidebar->obj_list()->update_selections(); } @@ -1736,13 +1567,8 @@ void Plater::priv::remove(size_t obj_idx) // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); -#else - if (this->canvas3D->is_layers_editing_enabled()) - this->canvas3D->enable_layers_editing(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER model.delete_object(obj_idx); // Delete object from Sidebar list @@ -1767,13 +1593,8 @@ void Plater::priv::reset() // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_layers_editing_enabled()) view3D->enable_layers_editing(false); -#else - if (this->canvas3D->is_layers_editing_enabled()) - this->canvas3D->enable_layers_editing(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Stop and reset the Print content. this->background_process.reset(); @@ -1791,11 +1612,7 @@ void Plater::priv::reset() void Plater::priv::mirror(Axis axis) { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->mirror_selection(axis); -#else - this->canvas3D->mirror_selection(axis); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::arrange() @@ -1807,11 +1624,7 @@ void Plater::priv::arrange() arranging.store(true); // Disable the arrange button (to prevent reentrancies, we will call wxYied) -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_toolbar_item("arrange", can_arrange()); -#else - this->canvas3D->enable_toolbar_item("arrange", can_arrange()); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER this->background_process.stop(); unsigned count = 0; @@ -1881,11 +1694,7 @@ void Plater::priv::arrange() arranging.store(false); // We enable back the arrange button -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_toolbar_item("arrange", can_arrange()); -#else - this->canvas3D->enable_toolbar_item("arrange", can_arrange()); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Do a full refresh of scene tree, including regenerating all the GLVolumes. //FIXME The update function shall just reload the modified matrices. @@ -2022,13 +1831,8 @@ unsigned int Plater::priv::update_background_process() Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_layers_editing_enabled()) view3D->get_wxglcanvas()->Refresh(); -#else - if (this->canvas3D->is_layers_editing_enabled()) - this->canvas3Dwidget->Refresh(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (invalidated == Print::APPLY_STATUS_INVALIDATED) { // Some previously calculated data on the Print was invalidated. @@ -2111,11 +1915,8 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) // bitmask of UpdateBackgroundProcessReturnState unsigned int state = update_background_process(); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->reload_scene(false); -#else - canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + if ((state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) != 0) return; @@ -2133,11 +1934,8 @@ void Plater::priv::update_restart_background_process(bool force_update_scene, bo // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(); if (force_update_scene || (state & UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) != 0) -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->reload_scene(false); -#else - this->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + if (force_update_preview) this->preview->reload_print(); this->restart_background_process(state); @@ -2148,11 +1946,7 @@ void Plater::priv::update_fff_scene() if (this->preview != nullptr) this->preview->reload_print(); // In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth: -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->reload_scene(true); -#else - this->canvas3D->reload_scene(true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::update_sla_scene() @@ -2225,7 +2019,6 @@ void Plater::priv::fix_through_netfabb(const int obj_idx) remove(obj_idx); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::set_current_panel(wxPanel* panel) { if (std::find(panels.begin(), panels.end(), panel) == panels.end()) @@ -2277,34 +2070,6 @@ void Plater::priv::set_current_panel(wxPanel* panel) current_panel->SetFocusFromKbd(); } -#else -void Plater::priv::on_notebook_changed(wxBookCtrlEvent&) -{ - wxCHECK_RET(canvas3D != nullptr, "on_notebook_changed on freed Plater"); - - const auto current_id = notebook->GetCurrentPage()->GetId(); -#if ENABLE_IMGUI - if (current_id == canvas3Dwidget->GetId()) { -#else - if (current_id == panel3d->GetId()) { -#endif // ENABLE_IMGUI - if (this->canvas3D->is_reload_delayed()) { - // Delayed loading of the 3D scene. - if (this->printer_technology == ptSLA) { - // Update the SLAPrint from the current Model, so that the reload_scene() - // pulls the correct data. - this->update_restart_background_process(true, false); - } else - this->canvas3D->reload_scene(true); - } - // sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably) - this->canvas3D->set_as_dirty(); - } else if (current_id == preview->GetId()) { - preview->reload_print(); - preview->set_canvas_as_dirty(); - } -} -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::on_select_preset(wxCommandEvent &evt) { @@ -2356,11 +2121,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) this->update_fff_scene(); break; case ptSLA: -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_dragging()) -#else - if (this->canvas3D->is_dragging()) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = true; else this->update_sla_scene(); @@ -2376,11 +2137,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &) this->update_fff_scene(); break; case ptSLA: -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_dragging()) -#else - if (this->canvas3D->is_dragging()) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = true; else this->update_sla_scene(); @@ -2423,11 +2180,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) this->update_fff_scene(); break; case ptSLA: -#if ENABLE_REMOVE_TABS_FROM_PLATER if (view3D->is_dragging()) -#else - if (this->canvas3D->is_dragging()) -#endif // ENABLE_REMOVE_TABS_FROM_PLATER delayed_scene_refresh = true; else this->update_sla_scene(); @@ -2437,22 +2190,12 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) void Plater::priv::on_layer_editing_toggled(bool enable) { -#if ENABLE_REMOVE_TABS_FROM_PLATER view3D->enable_layers_editing(enable); if (enable && !view3D->is_layers_editing_enabled()) { // Initialization of the OpenGL shaders failed. Disable the tool. view3D->enable_toolbar_item("layersediting", false); } view3D->set_as_dirty(); -#else - this->canvas3D->enable_layers_editing(enable); - if (enable && !this->canvas3D->is_layers_editing_enabled()) { - // Initialization of the OpenGL shaders failed. Disable the tool. - this->canvas3D->enable_toolbar_item("layersediting", false); - } - canvas3Dwidget->Refresh(); - canvas3Dwidget->Update(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::on_action_add(SimpleEvent&) @@ -2473,17 +2216,10 @@ void Plater::priv::on_action_split_volumes(SimpleEvent&) void Plater::priv::on_action_layersediting(SimpleEvent&) { -#if ENABLE_REMOVE_TABS_FROM_PLATER bool enable = !view3D->is_layers_editing_enabled(); view3D->enable_layers_editing(enable); if (enable && !view3D->is_layers_editing_enabled()) view3D->enable_toolbar_item("layersediting", false); -#else - bool enable = !this->canvas3D->is_layers_editing_enabled(); - this->canvas3D->enable_layers_editing(enable); - if (enable && !this->canvas3D->is_layers_editing_enabled()) - this->canvas3D->enable_toolbar_item("layersediting", false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::on_object_select(SimpleEvent& evt) @@ -2495,17 +2231,10 @@ void Plater::priv::on_object_select(SimpleEvent& evt) void Plater::priv::on_viewport_changed(SimpleEvent& evt) { wxObject* o = evt.GetEventObject(); -#if ENABLE_REMOVE_TABS_FROM_PLATER if (o == preview->get_wxglcanvas()) preview->set_viewport_into_scene(view3D->get_canvas3d()); else if (o == view3D->get_wxglcanvas()) preview->set_viewport_from_scene(view3D->get_canvas3d()); -#else - if (o == preview->get_wxglcanvas()) - preview->set_viewport_into_scene(canvas3D); - else if (o == canvas3Dwidget) - preview->set_viewport_from_scene(canvas3D); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::priv::on_right_click(Vec2dEvent& evt) @@ -2685,7 +2414,6 @@ bool Plater::priv::complit_init_part_menu() return true; } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::priv::init_view_toolbar() { ItemsIconsTexture::Metadata icons_data; @@ -2735,7 +2463,6 @@ void Plater::priv::init_view_toolbar() view3D->set_view_toolbar(&view_toolbar); preview->set_view_toolbar(&view_toolbar); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER bool Plater::priv::can_delete_object() const { @@ -2780,11 +2507,7 @@ bool Plater::priv::can_split() const bool Plater::priv::layers_height_allowed() const { int obj_idx = get_selected_object_idx(); -#if ENABLE_REMOVE_TABS_FROM_PLATER return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && view3D->is_layers_editing_allowed(); -#else - return (0 <= obj_idx) && (obj_idx < (int)model.objects.size()) && config->opt_bool("variable_layer_height") && this->canvas3D->is_layers_editing_allowed(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } bool Plater::priv::can_delete_all() const @@ -2817,10 +2540,6 @@ Plater::Plater(wxWindow *parent, MainFrame *main_frame) Plater::~Plater() { -#if !ENABLE_REMOVE_TABS_FROM_PLATER - _3DScene::remove_canvas(p->canvas3Dwidget); - p->canvas3D = nullptr; -#endif // !ENABLE_REMOVE_TABS_FROM_PLATER } Sidebar& Plater::sidebar() { return *p->sidebar; } @@ -2881,9 +2600,7 @@ void Plater::update_ui_from_settings() { p->update_ui_from_settings(); } void Plater::select_view(const std::string& direction) { p->select_view(direction); } -#if ENABLE_REMOVE_TABS_FROM_PLATER void Plater::select_view_3D(const std::string& name) { p->select_view_3D(name); } -#endif // ENABLE_REMOVE_TABS_FROM_PLATER void Plater::select_all() { p->select_all(); } @@ -2894,11 +2611,7 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo void Plater::remove_selected() { -#if ENABLE_REMOVE_TABS_FROM_PLATER this->p->view3D->delete_selected(); -#else - this->p->canvas3D->delete_selected(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } void Plater::increase_instances(size_t num) @@ -3127,11 +2840,7 @@ void Plater::reslice() // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->p->update_background_process(); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) -#if ENABLE_REMOVE_TABS_FROM_PLATER this->p->view3D->reload_scene(false); -#else - this->p->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER // Only restarts if the state is valid. this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); } @@ -3196,11 +2905,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) if (opt_key == "printer_technology") this->set_printer_technology(config.opt_enum(opt_key)); else if (opt_key == "bed_shape") { -#if ENABLE_REMOVE_TABS_FROM_PLATER if (p->view3D) p->view3D->set_bed_shape(p->config->option(opt_key)->values); -#else - this->p->canvas3D->set_bed_shape(p->config->option(opt_key)->values); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (p->preview) p->preview->set_bed_shape(p->config->option(opt_key)->values); update_scheduled = true; } @@ -3211,24 +2916,12 @@ void Plater::on_config_change(const DynamicPrintConfig &config) } else if(opt_key == "variable_layer_height") { if (p->config->opt_bool("variable_layer_height") != true) { -#if ENABLE_REMOVE_TABS_FROM_PLATER p->view3D->enable_toolbar_item("layersediting", false); p->view3D->enable_layers_editing(false); p->view3D->set_as_dirty(); -#else - p->canvas3D->enable_toolbar_item("layersediting", false); - p->canvas3D->enable_layers_editing(0); - p->canvas3Dwidget->Refresh(); - p->canvas3Dwidget->Update(); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } -#if ENABLE_REMOVE_TABS_FROM_PLATER else if (p->view3D->is_layers_editing_allowed()) { p->view3D->enable_toolbar_item("layersediting", true); -#else - else if (p->canvas3D->is_layers_editing_allowed()) { - p->canvas3D->enable_toolbar_item("layersediting", true); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } } else if(opt_key == "extruder_colour") { @@ -3238,11 +2931,7 @@ void Plater::on_config_change(const DynamicPrintConfig &config) update_scheduled = true; } else if(opt_key == "printer_model") { // update to force bed selection(for texturing) -#if ENABLE_REMOVE_TABS_FROM_PLATER if (p->view3D) p->view3D->set_bed_shape(p->config->option("bed_shape")->values); -#else - p->canvas3D->set_bed_shape(p->config->option("bed_shape")->values); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER if (p->preview) p->preview->set_bed_shape(p->config->option("bed_shape")->values); update_scheduled = true; } else if (opt_key == "host_type" && this->p->printer_technology == ptSLA) { @@ -3284,11 +2973,7 @@ bool Plater::is_single_full_object_selection() const GLCanvas3D* Plater::canvas3D() { -#if ENABLE_REMOVE_TABS_FROM_PLATER return p->view3D->get_canvas3d(); -#else - return p->canvas3D; -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } PrinterTechnology Plater::printer_technology() const @@ -3324,11 +3009,7 @@ void Plater::changed_object(int obj_idx) // pulls the correct data, update the 3D scene. this->p->update_restart_background_process(true, false); } else -#if ENABLE_REMOVE_TABS_FROM_PLATER p->view3D->reload_scene(false); -#else - p->canvas3D->reload_scene(false); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER } // update print diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 334ab740a..f433d655d 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -119,9 +119,8 @@ public: void update(); void select_view(const std::string& direction); -#if ENABLE_REMOVE_TABS_FROM_PLATER void select_view_3D(const std::string& name); -#endif // ENABLE_REMOVE_TABS_FROM_PLATER + // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. void update_ui_from_settings(); From 8a3d2f385f457f9962e5a14653c2e91b73dcb6ac Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 3 Jan 2019 14:20:01 +0100 Subject: [PATCH 132/132] Fix for bad arrange with mirrored objects --- src/libslic3r/ModelArrange.cpp | 5 +++-- src/libslic3r/SLA/SLASupportTree.cpp | 4 +++- src/libslic3r/SLAPrint.cpp | 13 +++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 562527290..8ed595802 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -511,8 +511,9 @@ ShapeData2D projectModelFromTop(const Slic3r::Model &model) { ModelInstance * finst = objptr->instances.front(); // Object instances should carry the same scaling and - // x, y rotation that is why we use the first instance - rmesh.scale(finst->get_scaling_factor()); + // x, y rotation that is why we use the first instance. + // The next line will apply only the full mirroring and scaling + rmesh.transform(finst->get_matrix(true, true, false, false)); rmesh.rotate_x(float(finst->get_rotation()(X))); rmesh.rotate_y(float(finst->get_rotation()(Y))); diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 11ffc33b0..746acc547 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -1117,7 +1117,9 @@ bool SLASupportTree::generate(const PointSet &points, head_norm.row(pcount) = nn; ++pcount; - } else { + } else if( polar >= 3*PI/4 ) { + // Headless supports do not tilt like the headed ones so + // the normal should point almost to the ground. headless_norm.row(hlcount) = nn; headless_pos.row(hlcount++) = hp; } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index e2a256b72..f45d805b6 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -502,8 +502,8 @@ void SLAPrint::process() po.m_supportdata.reset(new SLAPrintObject::SupportData()); po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); - BOOST_LOG_TRIVIAL(debug) - << "Support point count " << mo.sla_support_points.size(); + BOOST_LOG_TRIVIAL(debug) << "Support point count " + << mo.sla_support_points.size(); // If there are no points on the front-end, we will do the // autoplacement. Otherwise we will just blindly copy the frontend data @@ -537,6 +537,9 @@ void SLAPrint::process() const std::vector& points = auto_supports.output(); this->throw_if_canceled(); po.m_supportdata->support_points = sla::to_point_set(points); + + BOOST_LOG_TRIVIAL(debug) << "Automatic support points: " + << po.m_supportdata->support_points.rows(); } else { // There are some points on the front-end, no calculation will be done. @@ -584,13 +587,15 @@ void SLAPrint::process() // Create the unified mesh auto rc = SlicingStatus::RELOAD_SCENE; - po.m_supportdata->support_tree_ptr->merged_mesh(); - // Check the mesh for later troubleshooting. + // This is to prevent "Done." being displayed during merged_mesh() + report_status(*this, -1, L("Visualizing supports")); + po.m_supportdata->support_tree_ptr->merged_mesh(); BOOST_LOG_TRIVIAL(debug) << "Processed support point count " << po.m_supportdata->support_points.rows(); + // Check the mesh for later troubleshooting. if(po.support_mesh().empty()) BOOST_LOG_TRIVIAL(warning) << "Support mesh is empty";