From 7fa430c56defa04a24b75eb7fab72b9c9c10b828 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 14 Jan 2019 17:28:02 +0100 Subject: [PATCH 01/37] Adding an AABB tree to EigenMesh3D. Yet to be used. --- src/libslic3r/SLA/SLAAutoSupports.cpp | 4 +- src/libslic3r/SLA/SLARotfinder.cpp | 12 +- src/libslic3r/SLA/SLASupportTree.cpp | 74 +++-------- src/libslic3r/SLA/SLASupportTree.hpp | 34 ++++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 166 +++++++++++++++--------- src/libslic3r/SLAPrint.cpp | 2 +- 6 files changed, 155 insertions(+), 137 deletions(-) diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index ee87c6b663..98313be3fd 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -15,7 +15,7 @@ namespace Slic3r { 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) +: 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. @@ -332,4 +332,4 @@ void SLAAutoSupports::project_upward_onto_mesh(std::vector& points) const } -} // namespace Slic3r \ No newline at end of file +} // namespace Slic3r diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp index e66e26706d..1a91041b78 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -28,7 +28,7 @@ std::array find_best_rotation(const ModelObject& modelobj, // We will use only one instance of this converted mesh to examine different // rotations - EigenMesh3D emesh = to_eigenmesh(modelobj); + EigenMesh3D emesh(modelobj.raw_mesh()); // For current iteration number unsigned status = 0; @@ -68,12 +68,12 @@ std::array find_best_rotation(const ModelObject& modelobj, // area. The current function is only an example of how to optimize. // Later we can add more criteria like the number of overhangs, etc... - for(int i = 0; i < m.F.rows(); i++) { - auto idx = m.F.row(i); + for(int i = 0; i < m.F().rows(); i++) { + auto idx = m.F().row(i); - Vec3d p1 = m.V.row(idx(0)); - Vec3d p2 = m.V.row(idx(1)); - Vec3d p3 = m.V.row(idx(2)); + Vec3d p1 = m.V().row(idx(0)); + Vec3d p2 = m.V().row(idx(1)); + Vec3d p3 = m.V().row(idx(2)); Eigen::Vector3d U = p2 - p1; Eigen::Vector3d V = p3 - p1; diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index c2fcb3c3a4..a105f3e4bb 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -539,23 +539,6 @@ struct Pad { bool empty() const { return tmesh.facets_count() == 0; } }; -EigenMesh3D to_eigenmesh(const Contour3D& cntr) { - EigenMesh3D emesh; - - auto& V = emesh.V; - auto& F = emesh.F; - - V.resize(Eigen::Index(cntr.points.size()), 3); - F.resize(Eigen::Index(cntr.indices.size()), 3); - - for (int i = 0; i < V.rows(); ++i) { - V.row(i) = cntr.points[size_t(i)]; - F.row(i) = cntr.indices[size_t(i)]; - } - - return emesh; -} - // The minimum distance for two support points to remain valid. static const double /*constexpr*/ D_SP = 0.1; @@ -563,46 +546,6 @@ enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers X, Y, Z }; -EigenMesh3D to_eigenmesh(const TriangleMesh& tmesh) { - - const stl_file& stl = tmesh.stl; - - EigenMesh3D outmesh; - - auto&& bb = tmesh.bounding_box(); - outmesh.ground_level += bb.min(Z); - - auto& V = outmesh.V; - auto& F = outmesh.F; - - V.resize(3*stl.stats.number_of_facets, 3); - F.resize(stl.stats.number_of_facets, 3); - for (unsigned int i = 0; i < stl.stats.number_of_facets; ++i) { - const stl_facet* facet = stl.facet_start+i; - V(3*i+0, 0) = double(facet->vertex[0](0)); - V(3*i+0, 1) = double(facet->vertex[0](1)); - V(3*i+0, 2) = double(facet->vertex[0](2)); - - V(3*i+1, 0) = double(facet->vertex[1](0)); - V(3*i+1, 1) = double(facet->vertex[1](1)); - V(3*i+1, 2) = double(facet->vertex[1](2)); - - V(3*i+2, 0) = double(facet->vertex[2](0)); - V(3*i+2, 1) = double(facet->vertex[2](1)); - V(3*i+2, 2) = double(facet->vertex[2](2)); - - F(i, 0) = int(3*i+0); - F(i, 1) = int(3*i+1); - F(i, 2) = int(3*i+2); - } - - return outmesh; -} - -EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { - return to_eigenmesh(modelobj.raw_mesh()); -} - PointSet to_point_set(const std::vector &v) { PointSet ret(v.size(), 3); @@ -618,6 +561,21 @@ double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); +double pinhead_mesh_intersect(const Vec3d& jp, + const Vec3d& dir, + double r1, + double r2, + const EigenMesh3D& m); + +// Wrapper only +inline double pinhead_mesh_intersect(const Head& head, const EigenMesh3D& m) { + return pinhead_mesh_intersect(head.junction_point(), + head.dir, + head.r_pin_mm, + head.r_back_mm, + m); +} + PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}); @@ -1789,7 +1747,7 @@ SLASupportTree::SLASupportTree(const PointSet &points, const Controller &ctl): m_impl(new Impl(ctl)) { - m_impl->ground_level = emesh.ground_level - cfg.object_elevation_mm; + m_impl->ground_level = emesh.ground_level() - cfg.object_elevation_mm; generate(points, emesh, cfg, ctl); } diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 5a86d4623c..80f0bd68be 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -104,18 +104,40 @@ struct Controller { /// An index-triangle structure for libIGL functions. Also serves as an /// alternative (raw) input format for the SLASupportTree -struct EigenMesh3D { - Eigen::MatrixXd V; - Eigen::MatrixXi F; - double ground_level = 0; +class EigenMesh3D { + class AABBImpl; + + Eigen::MatrixXd m_V; + Eigen::MatrixXi m_F; + double m_ground_level = 0; + + std::unique_ptr m_aabb; +public: + + EigenMesh3D(); + EigenMesh3D(const TriangleMesh&); + + ~EigenMesh3D(); + + EigenMesh3D(const EigenMesh3D& other); +// EigenMesh3D(EigenMesh3D&&) = default; + EigenMesh3D& operator=(const EigenMesh3D&); +// EigenMesh3D& operator=(EigenMesh3D&&) = default; + + inline double ground_level() const { return m_ground_level; } + + inline const Eigen::MatrixXd& V() const { return m_V; } + inline const Eigen::MatrixXi& F() const { return m_F; } + + double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; }; using PointSet = Eigen::MatrixXd; -EigenMesh3D to_eigenmesh(const TriangleMesh& m); +//EigenMesh3D to_eigenmesh(const TriangleMesh& m); // needed for find best rotation -EigenMesh3D to_eigenmesh(const ModelObject& model); +//EigenMesh3D to_eigenmesh(const ModelObject& model); // Simple conversion of 'vector of points' to an Eigen matrix PointSet to_point_set(const std::vector&); diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 0cc9f14e0f..cacd1ceaa6 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -19,6 +19,10 @@ namespace Slic3r { namespace sla { +/* ************************************************************************** + * SpatIndex implementation + * ************************************************************************** */ + class SpatIndex::Impl { public: using BoostIndex = boost::geometry::index::rtree< SpatElement, @@ -78,6 +82,73 @@ size_t SpatIndex::size() const return m_impl->m_store.size(); } +/* **************************************************************************** + * EigenMesh3D implementation + * ****************************************************************************/ + +class EigenMesh3D::AABBImpl: public igl::AABB {}; + +EigenMesh3D::EigenMesh3D(): m_aabb(new AABBImpl()) {} + +EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { + static const double dEPS = 1e-6; + + const stl_file& stl = tmesh.stl; + + auto&& bb = tmesh.bounding_box(); + m_ground_level += bb.min(Z); + + Eigen::MatrixXd 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; i < stl.stats.number_of_facets; ++i) { + const stl_facet* facet = stl.facet_start+i; + V(3*i+0, 0) = double(facet->vertex[0](0)); + V(3*i+0, 1) = double(facet->vertex[0](1)); + V(3*i+0, 2) = double(facet->vertex[0](2)); + + V(3*i+1, 0) = double(facet->vertex[1](0)); + V(3*i+1, 1) = double(facet->vertex[1](1)); + V(3*i+1, 2) = double(facet->vertex[1](2)); + + V(3*i+2, 0) = double(facet->vertex[2](0)); + V(3*i+2, 1) = double(facet->vertex[2](1)); + V(3*i+2, 2) = double(facet->vertex[2](2)); + + F(i, 0) = int(3*i+0); + F(i, 1) = int(3*i+1); + F(i, 2) = int(3*i+2); + } + + // We will convert this to a proper 3d mesh with no duplicate points. + Eigen::VectorXi SVI, SVJ; + igl::remove_duplicate_vertices(V, F, dEPS, m_V, SVI, SVJ, m_F); + + // Build the AABB accelaration tree + m_aabb->init(m_V, m_F); +} + +EigenMesh3D::~EigenMesh3D() {} + +EigenMesh3D::EigenMesh3D(const EigenMesh3D &other): + m_V(other.m_V), m_F(other.m_F), m_ground_level(other.m_ground_level), + m_aabb( new AABBImpl(*other.m_aabb) ) {} + +EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) +{ + m_V = other.m_V; + m_F = other.m_F; + m_ground_level = other.m_ground_level; + m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; +} + +double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +{ + return 0; +} + bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, double eps = 0.05) { @@ -93,10 +164,10 @@ template double distance(const Vec& pp1, const Vec& pp2) { return std::sqrt(p.transpose() * p); } -PointSet normals(const PointSet& points, const EigenMesh3D& emesh, +PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps, std::function throw_on_cancel) { - if(points.rows() == 0 || emesh.V.rows() == 0 || emesh.F.rows() == 0) + if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) return {}; Eigen::VectorXd dists; @@ -105,23 +176,24 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // We need to remove duplicate vertices and have a true index triangle // structure + /* EigenMesh3D mesh; Eigen::VectorXi SVI, SVJ; static const double dEPS = 1e-6; igl::remove_duplicate_vertices(emesh.V, emesh.F, dEPS, - mesh.V, SVI, SVJ, mesh.F); + mesh.V, SVI, SVJ, mesh.F);*/ - igl::point_mesh_squared_distance( points, mesh.V, mesh.F, dists, I, C); + 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 trindex = mesh.F().row(idx); - const Vec3d& p1 = mesh.V.row(trindex(0)); - const Vec3d& p2 = mesh.V.row(trindex(1)); - const Vec3d& 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)); // 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 @@ -159,18 +231,18 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // 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) { + for(int n = 0; n < mesh.F().rows(); ++n) { throw_on_cancel(); - Vec3i ni = mesh.F.row(n); + 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) { + for(int n = 0; n < mesh.F().rows(); ++n) { throw_on_cancel(); - Vec3i ni = mesh.F.row(n); + 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); @@ -180,9 +252,9 @@ PointSet normals(const PointSet& points, const EigenMesh3D& emesh, // 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)); + 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()); @@ -228,10 +300,24 @@ double ray_mesh_intersect(const Vec3d& s, { igl::Hit hit; hit.t = std::numeric_limits::infinity(); - igl::ray_mesh_intersect(s, dir, m.V, m.F, hit); + + // Fck: this does not use any kind of spatial index acceleration... + igl::ray_mesh_intersect(s, dir, m.V(), m.F(), hit); return double(hit.t); } +// An enhanced version of ray_mesh_intersect for the pinheads. This will shoot +// multiple rays to detect collisions more accurately. +double pinhead_mesh_intersect(const Vec3d& jp, + const Vec3d& dir, + double r1, + double r2, + const EigenMesh3D& m) +{ + + return 0; +} + // Clustering a set of points by the given criteria ClusteredPoints cluster( const sla::PointSet& points, @@ -309,53 +395,5 @@ ClusteredPoints cluster( return result; } -using Segments = std::vector>; - -Segments model_boundary(const EigenMesh3D& emesh, double offs) -{ - Segments ret; - Polygons pp; - pp.reserve(size_t(emesh.F.rows())); - - for (int i = 0; i < emesh.F.rows(); i++) { - auto trindex = emesh.F.row(i); - auto& p1 = emesh.V.row(trindex(0)); - auto& p2 = emesh.V.row(trindex(1)); - auto& p3 = emesh.V.row(trindex(2)); - - Polygon p; - p.points.resize(3); - p.points[0] = Point::new_scale(p1(X), p1(Y)); - p.points[1] = Point::new_scale(p2(X), p2(Y)); - p.points[2] = Point::new_scale(p3(X), p3(Y)); - p.make_counter_clockwise(); - pp.emplace_back(p); - } - - ExPolygons merged = union_ex(Slic3r::offset(pp, float(scale_(offs))), true); - - for(auto& expoly : merged) { - auto lines = expoly.lines(); - for(Line& l : lines) { - Vec2d a(l.a(X) * SCALING_FACTOR, l.a(Y) * SCALING_FACTOR); - Vec2d b(l.b(X) * SCALING_FACTOR, l.b(Y) * SCALING_FACTOR); - ret.emplace_back(std::make_pair(a, b)); - } - } - - return ret; -} - -//struct SegmentIndex { - -//}; - -//using SegmentIndexEl = std::pair; - -//SegmentIndexEl - - - - } } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 5cddadb5b1..445c785763 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -508,7 +508,7 @@ void SLAPrint::process() auto support_points = [this, ilh](SLAPrintObject& po) { const ModelObject& mo = *po.m_model_object; po.m_supportdata.reset(new SLAPrintObject::SupportData()); - po.m_supportdata->emesh = sla::to_eigenmesh(po.transformed_mesh()); + po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh()); BOOST_LOG_TRIVIAL(debug) << "Support point count " << mo.sla_support_points.size(); From 8391e734164c3a256576049369a8ba2506c4038c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 15 Jan 2019 11:09:00 +0100 Subject: [PATCH 02/37] WIP --- src/libslic3r/SLA/SLASupportTree.cpp | 26 +++++++++---------- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 34 +++++++------------------ 2 files changed, 21 insertions(+), 39 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index a105f3e4bb..3178160849 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -557,23 +557,21 @@ Vec3d model_coord(const ModelInstance& object, const Vec3f& mesh_coord) { return object.transform_vector(mesh_coord.cast()); } -double ray_mesh_intersect(const Vec3d& s, - const Vec3d& dir, - const EigenMesh3D& m); - -double pinhead_mesh_intersect(const Vec3d& jp, - const Vec3d& dir, - double r1, - double r2, - const EigenMesh3D& m); +inline double ray_mesh_intersect(const Vec3d& s, + const Vec3d& dir, + const EigenMesh3D& m) +{ + return m.query_ray_hit(s, dir); +} // Wrapper only inline double pinhead_mesh_intersect(const Head& head, const EigenMesh3D& m) { - return pinhead_mesh_intersect(head.junction_point(), - head.dir, - head.r_pin_mm, - head.r_back_mm, - m); +// return pinhead_mesh_intersect(head.junction_point(), +// head.dir, +// head.r_pin_mm, +// head.r_back_mm, +// m); + return 0; } PointSet normals(const PointSet& points, const EigenMesh3D& mesh, diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index cacd1ceaa6..22dfeb3930 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -146,9 +146,17 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { - return 0; + igl::Hit hit; + hit.t = std::numeric_limits::infinity(); + m_aabb->intersect_ray(m_V, m_F, s, dir, hit); + + return double(hit.t); } +/* **************************************************************************** + * Misc functions + * ****************************************************************************/ + bool point_on_edge(const Vec3d& p, const Vec3d& e1, const Vec3d& e2, double eps = 0.05) { @@ -294,30 +302,6 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh, return ret; } -double ray_mesh_intersect(const Vec3d& s, - const Vec3d& dir, - const EigenMesh3D& m) -{ - igl::Hit hit; - hit.t = std::numeric_limits::infinity(); - - // Fck: this does not use any kind of spatial index acceleration... - igl::ray_mesh_intersect(s, dir, m.V(), m.F(), hit); - return double(hit.t); -} - -// An enhanced version of ray_mesh_intersect for the pinheads. This will shoot -// multiple rays to detect collisions more accurately. -double pinhead_mesh_intersect(const Vec3d& jp, - const Vec3d& dir, - double r1, - double r2, - const EigenMesh3D& m) -{ - - return 0; -} - // Clustering a set of points by the given criteria ClusteredPoints cluster( const sla::PointSet& points, From 1e1d405d70f888771203078cd970a9ed3089aaf5 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Jan 2019 15:35:01 +0100 Subject: [PATCH 03/37] Multiple rays for the pinhead collision detection. Seems to help a lot. --- src/libslic3r/SLA/SLASupportTree.cpp | 96 +++++++++++++++++++++++++--- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 7bba1c80e7..af128b1083 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -564,14 +564,86 @@ inline double ray_mesh_intersect(const Vec3d& s, return m.query_ray_hit(s, dir); } -// Wrapper only -inline double pinhead_mesh_intersect(const Head& head, const EigenMesh3D& m) { -// return pinhead_mesh_intersect(head.junction_point(), -// head.dir, -// head.r_pin_mm, -// head.r_back_mm, -// m); - return 0; +// This function will test if a future pinhead would not collide with the model +// geometry. It does not take a 'Head' object because those are created after +// this test. +// Parameters: +// s: The touching point on the model surface. +// dir: This is the direction of the head from the pin to the back +// r_pin, r_back: the radiuses of the pin and the back sphere +// width: This is the full width from the pin center to the back center +// m: The object mesh +// +// Optional: +// samples: how many rays will be shot +// safety distance: This will be added to the radiuses to have a safety distance +// from the mesh. +inline double pinhead_mesh_intersect(const Vec3d& s_original, + const Vec3d& dir, + double r_pin, + double r_back, + double width, + const EigenMesh3D& m, + unsigned samples = 8, + double safety_distance = 0.05) { + + // We will shoot multiple rays from the head pinpoint in the direction of + // the pinhead robe (side) surface. The result will be the smallest hit + // distance. + + // Move away slightly from the touching point to avoid raycasting on the + // inner surface of the mesh. + Vec3d s = s_original + 1.1 * r_pin * dir; + + Vec3d v = dir; // Our direction (axis) + Vec3d c = s + width * dir; + + // Two vectors that will be perpendicular to each other and to the axis. + // Values for a(X) and a(Y) are now arbitrary, a(Z) is just a placeholder. + Vec3d a(0, 1, 0), b; + + // The portions of the circle (the head-back circle) for which we will shoot + // rays. + std::vector phis(samples); + for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); + + a(Z) = -(v(X)*a(X) + v(Y)*a(Y)) / v(Z); + + b = a.cross(v); + + // Now a and b vectors are perpendicular to v and to each other. Together + // they define the plane where we have to iterate with the given angles + // in the 'phis' vector + + for(double& phi : phis) { + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + // Let's have a safety coefficient for the radiuses. + double rpscos = (safety_distance + r_pin) * cosphi; + double rpssin = (safety_distance + r_pin) * sinphi; + double rpbcos = (safety_distance + r_back) * cosphi; + double rpbsin = (safety_distance + r_back) * sinphi; + + // Point on the circle on the pin sphere + Vec3d ps(s(X) + rpscos * a(X) + rpssin * b(X), + s(Y) + rpscos * a(Y) + rpssin * b(Y), + s(Z) + rpscos * a(Z) + rpssin * b(Z)); + + // This is the point on the circle on the back sphere + Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), + c(Y) + rpbcos * a(Y) + rpbsin * b(Y), + c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); + + Vec3d n = (p - ps).normalized(); + + phi = m.query_ray_hit(ps, n); + std::cout << "t = " << phi << std::endl; + } + + auto mit = std::min_element(phis.begin(), phis.end()); + + return *mit; } PointSet normals(const PointSet& points, const EigenMesh3D& mesh, @@ -1069,7 +1141,13 @@ bool SLASupportTree::generate(const PointSet &points, // We should shoot a ray in the direction of the pinhead and // see if there is enough space for it - double t = ray_mesh_intersect(hp + 0.1*nn, nn, mesh); + double t = pinhead_mesh_intersect( + hp, // touching point + nn, + cfg.head_front_radius_mm, // approx the radius + cfg.head_back_radius_mm, + w, + mesh); if(t > 2*w || std::isinf(t)) { // 2*w because of lower and upper pinhead From 7a677a673fdcb596b9f488f40f93e41a3d756dab Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 16 Jan 2019 16:50:43 +0100 Subject: [PATCH 04/37] WIP --- src/libslic3r/SLA/SLASupportTree.cpp | 82 +++++++++++++++++++++++----- 1 file changed, 68 insertions(+), 14 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index af128b1083..8ff5645c95 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -578,14 +578,18 @@ inline double ray_mesh_intersect(const Vec3d& s, // samples: how many rays will be shot // safety distance: This will be added to the radiuses to have a safety distance // from the mesh. -inline double pinhead_mesh_intersect(const Vec3d& s_original, - const Vec3d& dir, - double r_pin, - double r_back, - double width, - const EigenMesh3D& m, - unsigned samples = 8, - double safety_distance = 0.05) { +double pinhead_mesh_intersect(const Vec3d& s_original, + const Vec3d& dir, + double r_pin, + double r_back, + double width, + const EigenMesh3D& m, + unsigned samples = 8, + double safety_distance = 0.05) +{ + // method based on: + // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space + // We will shoot multiple rays from the head pinpoint in the direction of // the pinhead robe (side) surface. The result will be the smallest hit @@ -593,7 +597,7 @@ inline double pinhead_mesh_intersect(const Vec3d& s_original, // Move away slightly from the touching point to avoid raycasting on the // inner surface of the mesh. - Vec3d s = s_original + 1.1 * r_pin * dir; + Vec3d s = s_original + r_pin * dir; Vec3d v = dir; // Our direction (axis) Vec3d c = s + width * dir; @@ -638,6 +642,10 @@ inline double pinhead_mesh_intersect(const Vec3d& s_original, Vec3d n = (p - ps).normalized(); phi = m.query_ray_hit(ps, n); + + // TODO: this should be an inside check + if(phi < r_pin) phi = std::numeric_limits::infinity(); + std::cout << "t = " << phi << std::endl; } @@ -646,6 +654,44 @@ inline double pinhead_mesh_intersect(const Vec3d& s_original, return *mit; } +double bridge_mesh_intersect(const Vec3d& s_original, + const Vec3d& dir, + double r, + const EigenMesh3D& m, + unsigned samples = 8, + double safety_distance = 0.05) +{ + // helper vector calculations + Vec3d s = s_original + r*dir; Vec3d a(0, 1, 0), b; + + a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z); + b = a.cross(dir); + + // circle portions + std::vector phis(samples); + for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); + + for(double& phi : phis) { + double sinphi = std::sin(phi); + double cosphi = std::cos(phi); + + // Let's have a safety coefficient for the radiuses. + double rcos = (safety_distance + r) * cosphi; + double rsin = (safety_distance + r) * sinphi; + + // Point on the circle on the pin sphere + Vec3d p (s(X) + rcos * a(X) + rsin * b(X), + s(Y) + rcos * a(Y) + rsin * b(Y), + s(Z) + rcos * a(Z) + rsin * b(Z)); + + phi = m.query_ray_hit(p, dir); + } + + auto mit = std::min_element(phis.begin(), phis.end()); + + return *mit; +} + PointSet normals(const PointSet& points, const EigenMesh3D& mesh, double eps = 0.05, // min distance from edges std::function throw_on_cancel = [](){}); @@ -1275,7 +1321,8 @@ bool SLASupportTree::generate(const PointSet &points, double zstep = pillar_dist * std::tan(-cfg.tilt); ej(Z) = sj(Z) + zstep; - double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); +// double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); + double chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); double bridge_distance = pillar_dist / std::cos(-cfg.tilt); // If the pillars are so close that they touch each other, @@ -1301,9 +1348,15 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d bej(sj(X), sj(Y), ej(Z)); // need to check collision for the cross stick - double backchkd = ray_mesh_intersect(bsj, - dirv(bsj, bej), - emesh); +// double backchkd = ray_mesh_intersect(bsj, +// dirv(bsj, bej), +// emesh); + + double backchkd = bridge_mesh_intersect(bsj, + dirv(bsj, bej), + pillar.r, + emesh); + if(backchkd >= bridge_distance) { result.add_bridge(bsj, bej, pillar.r); @@ -1312,7 +1365,8 @@ bool SLASupportTree::generate(const PointSet &points, } sj.swap(ej); ej(Z) = sj(Z) + zstep; - chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); +// chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); + chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); } }; From e160cf3ffb7aae31e531a67031ce437f89fdde34 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 17 Jan 2019 16:44:26 +0100 Subject: [PATCH 05/37] EigenMesh upgraded with inside check capability. --- src/libslic3r/SLA/SLASupportTree.cpp | 88 ++++++++++++++++++------- src/libslic3r/SLA/SLASupportTree.hpp | 9 ++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 25 ++++++- 3 files changed, 95 insertions(+), 27 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 8ff5645c95..6fa5ecd17b 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -639,12 +639,10 @@ double pinhead_mesh_intersect(const Vec3d& s_original, c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - Vec3d n = (p - ps).normalized(); - - phi = m.query_ray_hit(ps, n); - - // TODO: this should be an inside check - if(phi < r_pin) phi = std::numeric_limits::infinity(); + if(m.inside(ps) && m.inside(p)) { + Vec3d n = (p - ps).normalized(); + phi = m.query_ray_hit(ps, n); + } else phi = std::numeric_limits::infinity(); std::cout << "t = " << phi << std::endl; } @@ -684,7 +682,8 @@ double bridge_mesh_intersect(const Vec3d& s_original, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - phi = m.query_ray_hit(p, dir); + if(m.inside(p)) phi = m.query_ray_hit(p, dir); + else phi = std::numeric_limits::infinity(); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1195,9 +1194,7 @@ bool SLASupportTree::generate(const PointSet &points, w, mesh); - if(t > 2*w || std::isinf(t)) { - // 2*w because of lower and upper pinhead - + if(t > w || std::isinf(t)) { head_pos.row(pcount) = hp; // save the verified and corrected normal @@ -1269,17 +1266,59 @@ bool SLASupportTree::generate(const PointSet &points, for(unsigned i = 0; i < head_pos.rows(); i++) { tifcl(); - auto& head = result.heads()[i]; + auto& head = result.head(i); Vec3d dir(0, 0, -1); - Vec3d startpoint = head.junction_point(); + bool accept = false; + int ri = 1; + double t = std::numeric_limits::infinity(); + double hw = head.width_mm; - double t = ray_mesh_intersect(startpoint, dir, mesh); + while(!accept && head.width_mm > 0) { + + Vec3d startpoint = head.junction_point(); + + // Collision detection + t = bridge_mesh_intersect(startpoint, dir, head.r_back_mm, mesh); + + // Precise distance measurement + double tprec = ray_mesh_intersect(startpoint, dir, mesh); + + if(std::isinf(tprec) && !std::isinf(t)) { + // This is a damned case where the pillar melds into the model + // but its center ray can reach the ground. We can not route + // this to the ground nor to the model surface. We have to + // modify the head or discard this support point. + head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm; + } else { + accept = true; t = tprec; + + auto id = head.id; + // We need to regenerate the head geometry + head = Head(head.r_back_mm, + head.r_pin_mm, + head.width_mm, + head.penetration_mm, + head.dir, + head.tr); + head.id = id; + } + + ri++; + } gndheight.emplace_back(t); - if(std::isinf(t)) gndidx.emplace_back(i); - else nogndidx.emplace_back(i); + if(accept) { + if(std::isinf(t)) gndidx.emplace_back(i); + else nogndidx.emplace_back(i); + } else { + BOOST_LOG_TRIVIAL(warning) << "A support point at " + << head.tr.transpose() + << " had to be discarded as there is" + << " nowhere to route it."; + head.invalidate(); + } } PointSet gnd(gndidx.size(), 3); @@ -1321,7 +1360,6 @@ bool SLASupportTree::generate(const PointSet &points, double zstep = pillar_dist * std::tan(-cfg.tilt); ej(Z) = sj(Z) + zstep; -// double chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); double chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); double bridge_distance = pillar_dist / std::cos(-cfg.tilt); @@ -1348,10 +1386,6 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d bej(sj(X), sj(Y), ej(Z)); // need to check collision for the cross stick -// double backchkd = ray_mesh_intersect(bsj, -// dirv(bsj, bej), -// emesh); - double backchkd = bridge_mesh_intersect(bsj, dirv(bsj, bej), pillar.r, @@ -1365,7 +1399,6 @@ bool SLASupportTree::generate(const PointSet &points, } sj.swap(ej); ej(Z) = sj(Z) + zstep; -// chkd = ray_mesh_intersect(sj, dirv(sj, ej), emesh); chkd = bridge_mesh_intersect(sj, dirv(sj, ej), pillar.r, emesh); } }; @@ -1444,7 +1477,7 @@ bool SLASupportTree::generate(const PointSet &points, // is distributed more effectively on the pillar. auto search_nearest = - [&cfg, &result, &emesh, maxbridgelen, gndlvl] + [&cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] (SpatIndex& spindex, const Vec3d& jsh) { long nearest_id = -1; @@ -1476,7 +1509,9 @@ bool SLASupportTree::generate(const PointSet &points, double d = distance(jp, jn); if(jn(Z) <= gndlvl || d > max_len) break; - double chkd = ray_mesh_intersect(jp, dirv(jp, jn), emesh); + double chkd = bridge_mesh_intersect(jp, dirv(jp, jn), + pradius, + emesh); if(chkd >= d) nearest_id = ne.second; spindex.remove(ne); @@ -1695,10 +1730,15 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d dir = {0, 0, -1}; Vec3d sj = sp + R * n; - double dist = ray_mesh_intersect(sj, dir, emesh); + // This is only for checking + double dist = bridge_mesh_intersect(sj, dir, R, emesh); if(std::isinf(dist) || std::isnan(dist) || dist < 2*R) continue; + // This on the other hand will return the exact distance available + // measured through the center of the stick. + dist = ray_mesh_intersect(sj, dir, emesh); + Vec3d ej = sj + (dist + HWIDTH_MM)* dir; result.add_compact_bridge(sp, ej, n, R); } diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index 80f0bd68be..ac7d7cab87 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -120,16 +120,21 @@ public: ~EigenMesh3D(); EigenMesh3D(const EigenMesh3D& other); -// EigenMesh3D(EigenMesh3D&&) = default; EigenMesh3D& operator=(const EigenMesh3D&); -// EigenMesh3D& operator=(EigenMesh3D&&) = default; inline double ground_level() const { return m_ground_level; } inline const Eigen::MatrixXd& V() const { return m_V; } inline const Eigen::MatrixXi& F() const { return m_F; } + // Casting a ray on the mesh, returns the distance where the hit occures. double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + + // The signed distance from a point to the mesh. Outputs the distance, + // the index of the triangle and the closest point in mesh coordinate space. + std::tuple signed_distance(const Vec3d& p) const; + + bool inside(const Vec3d& p) const; }; using PointSet = Eigen::MatrixXd; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 22dfeb3930..358d9dc9c3 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -3,6 +3,9 @@ #include "SLA/SLABoilerPlate.hpp" #include "SLA/SLASpatIndex.hpp" +// Workaround: IGL signed_distance.h will define PI in the igl namespace. +#undef PI + // HEAVY headers... takes eternity to compile // for concave hull merging decisions @@ -12,6 +15,7 @@ #include #include #include +#include #include "SLASpatIndex.hpp" #include "ClipperUtils.hpp" @@ -19,6 +23,9 @@ namespace Slic3r { namespace sla { +// Bring back PI from the igl namespace +using igl::PI; + /* ************************************************************************** * SpatIndex implementation * ************************************************************************** */ @@ -86,7 +93,10 @@ size_t SpatIndex::size() const * EigenMesh3D implementation * ****************************************************************************/ -class EigenMesh3D::AABBImpl: public igl::AABB {}; +class EigenMesh3D::AABBImpl: public igl::AABB { +public: + igl::WindingNumberAABB windtree; +}; EigenMesh3D::EigenMesh3D(): m_aabb(new AABBImpl()) {} @@ -128,6 +138,9 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { // Build the AABB accelaration tree m_aabb->init(m_V, m_F); + + m_aabb->windtree.set_mesh(m_V, m_F); + m_aabb->windtree.init(); } EigenMesh3D::~EigenMesh3D() {} @@ -153,6 +166,16 @@ double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return double(hit.t); } +std::tuple +EigenMesh3D::signed_distance(const Vec3d &/*p*/) const { + // TODO: implement + return std::make_tuple(0.0, 0, Vec3d()); +} + +bool EigenMesh3D::inside(const Vec3d &p) const { + return m_aabb->windtree.inside(p); +} + /* **************************************************************************** * Misc functions * ****************************************************************************/ From 83f75f25bd43987e6ecba1357e21f16857496080 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 17 Jan 2019 17:46:29 +0100 Subject: [PATCH 06/37] Further improvements for headless sticks. --- src/libslic3r/SLA/SLASupportTree.cpp | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 6fa5ecd17b..29282b328e 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -643,8 +643,6 @@ double pinhead_mesh_intersect(const Vec3d& s_original, Vec3d n = (p - ps).normalized(); phi = m.query_ray_hit(ps, n); } else phi = std::numeric_limits::infinity(); - - std::cout << "t = " << phi << std::endl; } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1368,7 +1366,7 @@ bool SLASupportTree::generate(const PointSet &points, if(pillar_dist > 2*cfg.head_back_radius_mm && bridge_distance < cfg.max_bridge_length_mm) while(sj(Z) > pillar.endpoint(Z) + cfg.base_radius_mm && - ej(Z) > nextpillar.endpoint(Z) + + cfg.base_radius_mm) + ej(Z) > nextpillar.endpoint(Z) + cfg.base_radius_mm) { if(chkd >= bridge_distance) { result.add_bridge(sj, ej, pillar.r); @@ -1732,12 +1730,16 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d sj = sp + R * n; // This is only for checking - double dist = bridge_mesh_intersect(sj, dir, R, emesh); - if(std::isinf(dist) || std::isnan(dist) || dist < 2*R) continue; + double idist = bridge_mesh_intersect(sj, dir, R, emesh); + double dist = ray_mesh_intersect(sj, dir, emesh); - // This on the other hand will return the exact distance available - // measured through the center of the stick. - dist = ray_mesh_intersect(sj, dir, emesh); + if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || + std::isinf(dist) || std::isnan(dist) || dist < 2*R) { + BOOST_LOG_TRIVIAL(warning) << "Can not find route for headless" + << " support stick at: " + << sj.transpose(); + continue; + } Vec3d ej = sj + (dist + HWIDTH_MM)* dir; result.add_compact_bridge(sp, ej, n, R); From 4f837032324aa013f5d7ca82302a5956ed46d556 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 18 Jan 2019 12:09:53 +0100 Subject: [PATCH 07/37] A lot of comments added. --- src/libslic3r/SLA/SLASupportTree.cpp | 111 +++++++++++++++++++++------ 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 29282b328e..f756cfcf4c 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -707,6 +707,19 @@ ClusteredPoints cluster( std::function pred, unsigned max_points = 0); +// This class will hold the support tree meshes with some additional bookkeeping +// as well. Various parts of the support geometry are stored separately and are +// merged when the caller queries the merged mesh. The merged result is cached +// for fast subsequent delivery of the merged mesh which can be quite complex. +// An object of this class will be used as the result type during the support +// generation algorithm. Parts will be added with the appropriate methods such +// as add_head or add_pillar which forwards the constructor arguments and fills +// the IDs of these substructures. The IDs are basically indices into the arrays +// of the appropriate type (heads, pillars, etc...). One can later query e.g. a +// pillar for a specific head... +// +// The support pad is considered an auxiliary geometry and is not part of the +// merged mesh. It can be retrieved using a dedicated method (pad()) class SLASupportTree::Impl { std::vector m_heads; std::vector m_pillars; @@ -1067,16 +1080,22 @@ bool SLASupportTree::generate(const PointSet &points, // Indices of those who don't touch the ground IndexSet noground_heads; + // Groups of the 'ground_head' indices that belong into one cluster. These + // are candidates to be connected to one pillar. ClusteredPoints ground_connectors; + // A help function to translate ground head index to the actual coordinates. auto gnd_head_pt = [&ground_heads, &head_positions] (size_t idx) { return Vec3d(head_positions.row(ground_heads[idx])); }; + // This algorithm uses the Impl class as its output stream. It will be + // filled gradually with support elements (heads, pillars, bridges, ...) using Result = SLASupportTree::Impl; - Result& result = *m_impl; + // Let's define the individual steps of the processing. We can experiment + // later with the ordering and the dependencies between them. enum Steps { BEGIN, FILTER, @@ -1092,14 +1111,15 @@ bool SLASupportTree::generate(const PointSet &points, //... }; - // Debug: - // for(int pn = 0; pn < points.rows(); ++pn) { - // std::cout << "p " << pn << " " << points.row(pn) << std::endl; - // } - - + // t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will + // come in handy. auto& tifcl = ctl.cancelfn; + // Filtering step: here we will discard inappropriate support points and + // decide the future of the appropriate ones. We will check if a pinhead + // is applicable and adjust its angle at each support point. + // We will also merge the support points that are just too close and can be + // considered as one. auto filterfn = [tifcl] ( const SupportConfig& cfg, const PointSet& points, @@ -1110,10 +1130,6 @@ bool SLASupportTree::generate(const PointSet &points, PointSet& headless_pos, PointSet& headless_norm) { - /* ******************************************************** */ - /* Filtering step */ - /* ******************************************************** */ - // Get the points that are too close to each other and keep only the // first one auto aliases = @@ -1214,7 +1230,8 @@ bool SLASupportTree::generate(const PointSet &points, headless_norm.conservativeResize(hlcount, Eigen::NoChange); }; - // Function to write the pinheads into the result + // Pinhead creation: based on the filtering results, the Head objects will + // be constructed (together with their triangle meshes). auto pinheadfn = [tifcl] ( const SupportConfig& cfg, PointSet& head_pos, @@ -1240,8 +1257,13 @@ bool SLASupportTree::generate(const PointSet &points, } }; - // &filtered_points, &head_positions, &result, &mesh, - // &gndidx, &gndheight, &nogndidx, cfg + // Further classification of the support points with pinheads. If the + // ground is directly reachable through a vertical line parallel to the Z + // axis we consider a support point as pillar candidate. If touches the + // model geometry, it will be marked as non-ground facing and further steps + // will process it. Also, the pillars will be grouped into clusters that can + // be interconnected with bridges. Elements of these groups may or may not + // be interconnected. Here we only run the clustering algorithm. auto classifyfn = [tifcl] ( const SupportConfig& cfg, const EigenMesh3D& mesh, @@ -1262,6 +1284,9 @@ bool SLASupportTree::generate(const PointSet &points, gndidx.reserve(size_t(head_pos.rows())); nogndidx.reserve(size_t(head_pos.rows())); + // First we search decide which heads reach the ground and can be full + // pillars and which shall be connected to the model surface (or search + // a suitable path around the surface that leads to the ground -- TODO) for(unsigned i = 0; i < head_pos.rows(); i++) { tifcl(); auto& head = result.head(i); @@ -1272,6 +1297,9 @@ bool SLASupportTree::generate(const PointSet &points, double t = std::numeric_limits::infinity(); double hw = head.width_mm; + // We will try to assign a pillar to all the pinheads. If a pillar + // would pierce the model surface, we will try to adjust slightly + // the head with so that the pillar can be deployed. while(!accept && head.width_mm > 0) { Vec3d startpoint = head.junction_point(); @@ -1283,10 +1311,9 @@ bool SLASupportTree::generate(const PointSet &points, double tprec = ray_mesh_intersect(startpoint, dir, mesh); if(std::isinf(tprec) && !std::isinf(t)) { - // This is a damned case where the pillar melds into the model - // but its center ray can reach the ground. We can not route - // this to the ground nor to the model surface. We have to - // modify the head or discard this support point. + // This is a damned case where the pillar melds into the + // model but its center ray can reach the ground. We can + // not route this to the ground nor to the model surface. head.width_mm = hw + (ri % 2? -1 : 1) * ri * head.r_back_mm; } else { accept = true; t = tprec; @@ -1305,12 +1332,23 @@ bool SLASupportTree::generate(const PointSet &points, ri++; } + // Save the distance from a surface in the Z axis downwards. It may + // be infinity but that is telling us that it touches the ground. gndheight.emplace_back(t); if(accept) { if(std::isinf(t)) gndidx.emplace_back(i); else nogndidx.emplace_back(i); } else { + // This is a serious issue. There was no way to deploy a pillar + // for the given pinhead. The whole thing has to be discarded + // leaving the model potentially unprintable. + // + // TODO: In the future this has to be solved by searching for + // a path in 3D space from this support point to a suitable + // pillar position or an existing pillar. + // As a workaround we could mark this head as "sidehead only" + // let it go trough the nearby pillar search in the next step. BOOST_LOG_TRIVIAL(warning) << "A support point at " << head.tr.transpose() << " had to be discarded as there is" @@ -1319,8 +1357,8 @@ bool SLASupportTree::generate(const PointSet &points, } } + // Transform the ground facing point indices top actual coordinates. PointSet gnd(gndidx.size(), 3); - for(size_t i = 0; i < gndidx.size(); i++) gnd.row(long(i)) = head_pos.row(gndidx[i]); @@ -1340,7 +1378,8 @@ bool SLASupportTree::generate(const PointSet &points, }, 3); // max 3 heads to connect to one centroid }; - // Helper function for interconnecting two pillars with zig-zag bridges + // Helper function for interconnecting two pillars with zig-zag bridges. + // This is not an individual step. auto interconnect = [&cfg]( const Pillar& pillar, const Pillar& nextpillar, @@ -1401,6 +1440,11 @@ bool SLASupportTree::generate(const PointSet &points, } }; + // Step: Routing the ground connected pinheads, and interconnecting them + // with additional (angled) bridges. Not all of these pinheads will be + // a full pillar (ground connected). Some will connect to a nearby pillar + // using a bridge. The max number of such side-heads for a central pillar + // is limited to avoid bad weight distribution. auto routing_ground_fn = [gnd_head_pt, interconnect, tifcl]( const SupportConfig& cfg, const ClusteredPoints& gnd_clusters, @@ -1505,7 +1549,7 @@ bool SLASupportTree::generate(const PointSet &points, } double d = distance(jp, jn); - if(jn(Z) <= gndlvl || d > max_len) break; + if(jn(Z) <= gndlvl + nearhead.r_back_mm || d > max_len) break; double chkd = bridge_mesh_intersect(jp, dirv(jp, jn), pradius, @@ -1644,6 +1688,11 @@ bool SLASupportTree::generate(const PointSet &points, } }; + // Step: routing the pinheads that are would connect to the model surface + // along the Z axis downwards. For now these will actually be connected with + // the model surface with a flipped pinhead. In the future here we could use + // some smart algorithms to search for a safe path to the ground or to a + // nearby pillar that can hold the supported weight. auto routing_nongnd_fn = [tifcl]( const SupportConfig& cfg, const std::vector& gndheight, @@ -1705,6 +1754,9 @@ bool SLASupportTree::generate(const PointSet &points, } }; + // Step: process the support points where there is not enough space for a + // full pinhead. In this case we will use a rounded sphere as a touching + // point and use a thinner bridge (let's call it a stick). auto process_headless = [tifcl]( const SupportConfig& cfg, const PointSet& headless_pts, @@ -1746,16 +1798,24 @@ bool SLASupportTree::generate(const PointSet &points, } }; - using std::ref; - using std::cref; + // Now that the individual blocks are defined, lets connect the wires. We + // will create an array of functions which represents a program. Place the + // step methods in the array and bind the right arguments to the methods + // This way the data dependencies will be easily traceable between + // individual steps. + // There will be empty steps as well like the begin step or the done or + // abort steps. These are slots for future initialization or cleanup. + + using std::cref; // Bind inputs with cref (read-only) + using std::ref; // Bind outputs with ref (writable) using std::bind; // Here we can easily track what goes in and what comes out of each step: // (see the cref-s as inputs and ref-s as outputs) std::array, NUM_STEPS> program = { [] () { - // Begin - // clear up the shared data + // Begin... + // Potentially clear up the shared data (not needed for now) }, // Filtering unnecessary support points @@ -1798,6 +1858,7 @@ bool SLASupportTree::generate(const PointSet &points, Steps pc = BEGIN, pc_prev = BEGIN; + // Let's define a simple automaton that will run our program. auto progress = [&ctl, &pc, &pc_prev] () { static const std::array stepstr { "Starting", From 6c0b65208fca79b2a3e158e8a1b30ef45ec5ba70 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 18 Jan 2019 16:21:44 +0100 Subject: [PATCH 08/37] Introducing signed_distance into the collision detection. Everything is broken O.o --- src/libslic3r/SLA/SLASupportTree.cpp | 28 +++++++++++++++---------- src/libslic3r/SLA/SLASupportTree.hpp | 19 ++++++++++++++++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 10 +++++---- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index f756cfcf4c..1d9f981a6a 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -578,7 +578,7 @@ inline double ray_mesh_intersect(const Vec3d& s, // samples: how many rays will be shot // safety distance: This will be added to the radiuses to have a safety distance // from the mesh. -double pinhead_mesh_intersect(const Vec3d& s_original, +double pinhead_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r_pin, double r_back, @@ -597,8 +597,6 @@ double pinhead_mesh_intersect(const Vec3d& s_original, // Move away slightly from the touching point to avoid raycasting on the // inner surface of the mesh. - Vec3d s = s_original + r_pin * dir; - Vec3d v = dir; // Our direction (axis) Vec3d c = s + width * dir; @@ -634,15 +632,20 @@ double pinhead_mesh_intersect(const Vec3d& s_original, s(Y) + rpscos * a(Y) + rpssin * b(Y), s(Z) + rpscos * a(Z) + rpssin * b(Z)); + // Point ps is not on mesh but can be inside or outside as well. This + // would cause many problems with ray-casting. So we query the closest + // point on the mesh to this. + auto result = m.signed_distance(ps); + // This is the point on the circle on the back sphere Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - if(m.inside(ps) && m.inside(p)) { - Vec3d n = (p - ps).normalized(); - phi = m.query_ray_hit(ps, n); - } else phi = std::numeric_limits::infinity(); + if(!m.inside(p)) { + Vec3d n = (p - result.point_on_mesh() + 0.01 * dir).normalized(); + phi = m.query_ray_hit(result.point_on_mesh(), n); + } else phi = 0; } auto mit = std::min_element(phis.begin(), phis.end()); @@ -650,7 +653,7 @@ double pinhead_mesh_intersect(const Vec3d& s_original, return *mit; } -double bridge_mesh_intersect(const Vec3d& s_original, +double bridge_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r, const EigenMesh3D& m, @@ -658,7 +661,7 @@ double bridge_mesh_intersect(const Vec3d& s_original, double safety_distance = 0.05) { // helper vector calculations - Vec3d s = s_original + r*dir; Vec3d a(0, 1, 0), b; + Vec3d a(0, 1, 0), b; a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z); b = a.cross(dir); @@ -680,8 +683,9 @@ double bridge_mesh_intersect(const Vec3d& s_original, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - if(m.inside(p)) phi = m.query_ray_hit(p, dir); - else phi = std::numeric_limits::infinity(); + auto result = m.signed_distance(p); + + phi = m.query_ray_hit(result.point_on_mesh() + 0.05*dir, dir); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1190,6 +1194,8 @@ bool SLASupportTree::generate(const PointSet &points, std::sin(azimuth) * std::sin(polar), std::cos(polar)); + nn.normalize(); + // save the head (pinpoint) position Vec3d hp = filt_pts.row(i); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index ac7d7cab87..e13dd8c764 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -130,9 +130,26 @@ public: // Casting a ray on the mesh, returns the distance where the hit occures. double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + class si_result { + double m_value; + int m_fidx; + Vec3d m_p; + si_result(double val, int i, const Vec3d& c): + m_value(val), m_fidx(i), m_p(c) {} + friend class EigenMesh3D; + public: + + si_result() = delete; + + double value() const { return m_value; } + operator double() const { return m_value; } + const Vec3d& point_on_mesh() const { return m_p; } + int F_idx() const { return m_fidx; } + }; + // The signed distance from a point to the mesh. Outputs the distance, // the index of the triangle and the closest point in mesh coordinate space. - std::tuple signed_distance(const Vec3d& p) const; + si_result signed_distance(const Vec3d& p) const; bool inside(const Vec3d& p) const; }; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 358d9dc9c3..b84e66e41c 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -166,10 +166,12 @@ double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const return double(hit.t); } -std::tuple -EigenMesh3D::signed_distance(const Vec3d &/*p*/) const { - // TODO: implement - return std::make_tuple(0.0, 0, Vec3d()); +EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { + double sign = 0; double sqdst = 0; int i = 0; Vec3d c; + igl::signed_distance_winding_number(*m_aabb, m_V, m_F, m_aabb->windtree, + p, sign, sqdst, i, c); + + return si_result(sign * std::sqrt(sqdst), i, c); } bool EigenMesh3D::inside(const Vec3d &p) const { From 1c0bc8a5f3e2825337cbb94c33609c749bde0146 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 Jan 2019 15:50:02 +0100 Subject: [PATCH 09/37] Fixed synchronization between Objects List and Object manipulator table --- src/slic3r/GUI/GLCanvas3D.cpp | 5 ++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 59 ++++++++--------------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 18 +++++-- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 7366516d12..28c3baf247 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1617,6 +1617,11 @@ void GLCanvas3D::Selection::clear() _update_type(); m_bounding_box_dirty = true; + +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + // resets the cache in the sidebar + wxGetApp().obj_manipul()->reset_cache(); +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } // Update the selection based on the map from old indices to new indices after m_volumes changed. diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 93e527fe59..b8aa07613e 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -23,6 +23,14 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->set_grid_vgap(5); m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + // needed to hide the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); + + if (!m_cache.is_valid()) + return; +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + std::vector axes{ "_x", "_y", "_z" }; std::string param; @@ -38,26 +46,26 @@ 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); -#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); -#else - change_size_value(new_value); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION }; m_og->m_fill_empty_value = [this](const std::string& opt_key) { -#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + // needed to hide the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); + + if (!m_cache.is_valid()) + return; +#else this->update_if_dirty(); -#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION std::string param; std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); @@ -68,45 +76,31 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION value = m_cache.position(axis); -#else - value = m_cache_position(axis); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } else if (param == "rotation") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION value = m_cache.rotation(axis); -#else - value = m_cache_rotation(axis); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } else if (param == "scale") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION value = m_cache.scale(axis); -#else - value = m_cache_scale(axis); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } else if (param == "size") { int axis = opt_key.back() == 'x' ? 0 : opt_key.back() == 'y' ? 1 : 2; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION value = m_cache.size(axis); -#else - value = m_cache_size(axis); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } m_og->set_value(opt_key, double_to_string(value)); +#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); +#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION }; m_og->m_set_focus = [this](const std::string& opt_key) @@ -114,6 +108,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : #if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION this->update_if_dirty(); #endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + // needed to show the visual hints in 3D scene wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; @@ -459,18 +454,10 @@ void ObjectManipulation::change_position_value(const Vec3d& position) auto canvas = wxGetApp().plater()->canvas3D(); GLCanvas3D::Selection& selection = canvas->get_selection(); selection.start_dragging(); -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION selection.translate(position - m_cache.position, selection.requires_local_axes()); -#else - selection.translate(position - m_cache_position, selection.requires_local_axes()); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION canvas->do_move(); -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_cache.position = position; -#else - m_cache_position = position; -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::change_rotation_value(const Vec3d& rotation) @@ -503,11 +490,7 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); if (m_uniform_scale || selection.requires_uniform_scale()) { -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION Vec3d abs_scale_diff = (scale - m_cache.scale).cwiseAbs(); -#else - Vec3d abs_scale_diff = (scale - m_cache_scale).cwiseAbs(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION double max_diff = abs_scale_diff(X); Axis max_diff_axis = X; if (max_diff < abs_scale_diff(Y)) diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 1684a1360f..4aa3aa5f7d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -43,11 +43,19 @@ class ObjectManipulation : public OG_Settings Instance instance; - Cache() : position(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) , rotation(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) - , scale(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) , size(Vec3d(DBL_MAX, DBL_MAX, DBL_MAX)) - , move_label_string("") , rotate_label_string("") , scale_label_string("") + Cache() { reset(); } + void reset() { + position = Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); + rotation = Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); + scale = Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); + size = Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); + move_label_string = ""; + rotate_label_string = ""; + scale_label_string = ""; + instance.reset(); } + bool is_valid() const { return position != Vec3d(DBL_MAX, DBL_MAX, DBL_MAX); } }; Cache m_cache; @@ -92,6 +100,10 @@ public: void set_uniform_scaling(const bool uniform_scale) { m_uniform_scale = uniform_scale;} bool get_uniform_scaling() const { return m_uniform_scale; } +#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + void reset_cache() { m_cache.reset(); } +#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION + private: void reset_settings_value(); From 6137cc48eb368268c106f95e58b7b2254e7b6412 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 28 Jan 2019 16:06:44 +0100 Subject: [PATCH 10/37] ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION set as default --- src/libslic3r/Technologies.hpp | 2 - src/slic3r/GUI/GLCanvas3D.cpp | 36 ---------- src/slic3r/GUI/GLCanvas3D.hpp | 2 - src/slic3r/GUI/GUI_ObjectManipulation.cpp | 83 ----------------------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 9 --- src/slic3r/GUI/Plater.cpp | 2 - 6 files changed, 134 deletions(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index be37e16946..8bf5409741 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -32,8 +32,6 @@ //==================== #define ENABLE_1_42_0_ALPHA2 1 -// Improves navigation between sidebar fields -#define ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION (1 && ENABLE_1_42_0_ALPHA2) // Adds print bed models to 3D scene #define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2) #endif // _technologies_h_ diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 28c3baf247..107e763c66 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1618,10 +1618,8 @@ void GLCanvas3D::Selection::clear() _update_type(); m_bounding_box_dirty = true; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION // resets the cache in the sidebar wxGetApp().obj_manipul()->reset_cache(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } // Update the selection based on the map from old indices to new indices after m_volumes changed. @@ -3987,10 +3985,8 @@ 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>); @@ -5317,9 +5313,7 @@ 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); @@ -5336,21 +5330,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #endif // ENABLE_MOVE_MIN_THRESHOLD } -#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 } } @@ -5740,7 +5727,6 @@ 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) @@ -5748,20 +5734,6 @@ void GLCanvas3D::do_move() 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()); - object_moved = true; - } - else if (selection_mode == Selection::Volume) - { - model_object->volumes[volume_idx]->set_offset(v->get_volume_offset()); - object_moved = true; - } - if (object_moved) - model_object->invalidate_bounding_box(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } else if (object_idx == 1000) @@ -5832,12 +5804,8 @@ 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() @@ -5888,12 +5856,8 @@ 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 8bdbf89b06..22834e0d52 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -125,10 +125,8 @@ 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 b8aa07613e..b4480f7e6b 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -23,13 +23,11 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : m_og->set_grid_vgap(5); m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION // needed to hide the visual hints in 3D scene wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); if (!m_cache.is_valid()) return; -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION std::vector axes{ "_x", "_y", "_z" }; @@ -49,23 +47,15 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : change_scale_value(new_value); else if (param == "size") change_size_value(new_value); - -#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); -#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION }; m_og->m_fill_empty_value = [this](const std::string& opt_key) { -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION // needed to hide the visual hints in 3D scene wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); if (!m_cache.is_valid()) return; -#else - 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)); @@ -98,16 +88,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : } m_og->set_value(opt_key, double_to_string(value)); -#if !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); -#endif // !ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION }; 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 // needed to show the visual hints in 3D scene wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; @@ -228,9 +212,6 @@ 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); @@ -249,7 +230,6 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele m_new_rotation = volume->get_instance_rotation(); m_new_scale = volume->get_instance_scaling_factor(); int obj_idx = volume->object_idx(); -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION int instance_idx = volume->instance_idx(); if ((0 <= obj_idx) && (obj_idx < (int)wxGetApp().model_objects()->size())) { @@ -265,21 +245,12 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele else // this should never happen m_new_size = Vec3d::Zero(); -#else - 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(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_new_enabled = true; } else if (selection.is_single_full_object()) { -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_cache.instance.reset(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION const BoundingBoxf3& box = selection.get_bounding_box(); m_new_position = box.center(); @@ -292,9 +263,7 @@ void ObjectManipulation::update_settings_value(const GLCanvas3D::Selection& sele } else if (selection.is_single_modifier() || selection.is_single_volume()) { -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_cache.instance.reset(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION // the selection contains a single volume const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); @@ -324,7 +293,6 @@ void ObjectManipulation::update_if_dirty() if (!m_dirty) return; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION if (m_cache.move_label_string != _(m_new_move_label_string)+ ":") { m_cache.move_label_string = _(m_new_move_label_string)+ ":"; @@ -401,37 +369,6 @@ void ObjectManipulation::update_if_dirty() m_og->enable(); else m_og->disable(); -#else - 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)); - 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)); - 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)); - 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)); - m_cache_rotation = m_new_rotation; - - if (m_new_enabled) - m_og->enable(); - else - m_og->disable(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_dirty = false; } @@ -443,9 +380,7 @@ void ObjectManipulation::reset_settings_value() m_new_scale = Vec3d::Ones(); m_new_size = Vec3d::Zero(); m_new_enabled = false; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_cache.instance.reset(); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION m_dirty = true; } @@ -472,16 +407,10 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) } canvas->get_selection().start_dragging(); -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance() || selection.requires_local_axes()); -#else - canvas->get_selection().rotate(rad_rotation, selection.is_single_full_instance()); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION 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) @@ -513,12 +442,10 @@ void ObjectManipulation::change_scale_value(const Vec3d& scale) canvas->get_selection().scale(scaling_factor, false); canvas->do_scale(); -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION if (!m_cache.scale.isApprox(scale)) m_cache.instance.instance_idx = -1; m_cache.scale = scale; -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } void ObjectManipulation::change_size_value(const Vec3d& size) @@ -526,7 +453,6 @@ void ObjectManipulation::change_size_value(const Vec3d& size) const GLCanvas3D::Selection& selection = wxGetApp().plater()->canvas3D()->get_selection(); Vec3d ref_size = m_cache.size; -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION if (selection.is_single_volume() || selection.is_single_modifier()) { const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); @@ -564,15 +490,6 @@ void ObjectManipulation::change_size_value(const Vec3d& size) canvas->do_scale(); m_cache.size = size; -#else - 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))); -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION } } //namespace GUI diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index 4aa3aa5f7d..b761beda87 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -15,7 +15,6 @@ namespace GUI { class ObjectManipulation : public OG_Settings { -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION struct Cache { Vec3d position; @@ -59,12 +58,6 @@ class ObjectManipulation : public OG_Settings }; Cache m_cache; -#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; @@ -100,9 +93,7 @@ public: void set_uniform_scaling(const bool uniform_scale) { m_uniform_scale = uniform_scale;} bool get_uniform_scaling() const { return m_uniform_scale; } -#if ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION void reset_cache() { m_cache.reset(); } -#endif // ENABLE_IMPROVED_SIDEBAR_OBJECTS_MANIPULATION private: void reset_settings_value(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 42e404106a..3500c48b5a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1175,10 +1175,8 @@ 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 14fe55d4b83a3488c775371576407e45bf7848f4 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 Jan 2019 11:26:35 +0100 Subject: [PATCH 11/37] Fix of rotations using sidebar fields --- src/libslic3r/Geometry.cpp | 2 -- src/libslic3r/libslic3r.h | 6 ++++++ src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 18 +++++++++++++----- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index 58324893d0..a9d3be5396 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -1182,8 +1182,6 @@ Vec3d extract_euler_angles(const Eigen::Matrix& { #if ENABLE_NEW_EULER_ANGLES // reference: http://www.gregslabaugh.net/publications/euler.pdf - auto is_approx = [](double value, double test_value) -> bool { return std::abs(value - test_value) < EPSILON; }; - Vec3d angles1 = Vec3d::Zero(); Vec3d angles2 = Vec3d::Zero(); if (is_approx(std::abs(rotation_matrix(2, 0)), 1.0)) diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 19c6d3065c..5fd2fafc1b 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -164,6 +164,12 @@ static inline T lerp(const T& a, const T& b, Number t) return (Number(1) - t) * a + t * b; } +template +static inline bool is_approx(Number value, Number test_value) +{ + return std::abs(double(value) - double(test_value)) < double(EPSILON); +}; + } // namespace Slic3r #endif diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 107e763c66..3de7d67fa1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1820,7 +1820,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) if (rot_axis_max != 2 && first_volume_idx != -1) { // Generic rotation, but no rotation around the Z axis. // Always do a local rotation (do not consider the selection to be a rigid body). - assert(rotation.z() == 0); + assert(is_approx(rotation.z(), 0.0)); const GLVolume &first_volume = *(*m_volumes)[first_volume_idx]; const Vec3d &rotation = first_volume.get_instance_rotation(); double z_diff = rotation_diff_z(m_cache.volumes_data[first_volume_idx].get_instance_rotation(), m_cache.volumes_data[i].get_instance_rotation()); @@ -1845,7 +1845,7 @@ void GLCanvas3D::Selection::rotate(const Vec3d& rotation, bool local) else if (is_single_volume() || is_single_modifier()) { if (local) - volume.set_volume_rotation(rotation); + volume.set_volume_rotation(volume.get_volume_rotation() + rotation); else { Transform3d m = Geometry::assemble_transform(Vec3d::Zero(), rotation); @@ -2262,7 +2262,7 @@ void GLCanvas3D::Selection::render_sidebar_hints(const std::string& sidebar_fiel } else if (is_single_volume() || is_single_modifier()) { - Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); + Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true) * (*m_volumes)[*m_list.begin()]->get_volume_transformation().get_matrix(true, false, true, true); ::glTranslated(center(0), center(1), center(2)); ::glMultMatrixd(orient_matrix.data()); } diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index b4480f7e6b..08829be93b 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -345,16 +345,22 @@ void ObjectManipulation::update_if_dirty() m_cache.size = m_new_size; + Vec3d deg_rotation; + for (size_t i = 0; i < 3; ++i) + { + deg_rotation(i) = Geometry::rad2deg(m_new_rotation(i)); + } + if (m_cache.rotation(0) != m_new_rotation(0)) - m_og->set_value("rotation_x", double_to_string(Geometry::rad2deg(m_new_rotation(0)), 2)); + m_og->set_value("rotation_x", double_to_string(deg_rotation(0), 2)); if (m_cache.rotation(1) != m_new_rotation(1)) - m_og->set_value("rotation_y", double_to_string(Geometry::rad2deg(m_new_rotation(1)), 2)); + m_og->set_value("rotation_y", double_to_string(deg_rotation(1), 2)); if (m_cache.rotation(2) != m_new_rotation(2)) - m_og->set_value("rotation_z", double_to_string(Geometry::rad2deg(m_new_rotation(2)), 2)); + m_og->set_value("rotation_z", double_to_string(deg_rotation(2), 2)); - m_cache.rotation = m_new_rotation; + m_cache.rotation = deg_rotation; if (wxGetApp().plater()->canvas3D()->get_selection().requires_uniform_scale()) { m_lock_bnt->SetLock(true); @@ -400,10 +406,12 @@ void ObjectManipulation::change_rotation_value(const Vec3d& rotation) GLCanvas3D* canvas = wxGetApp().plater()->canvas3D(); const GLCanvas3D::Selection& selection = canvas->get_selection(); + Vec3d delta_rotation = rotation - m_cache.rotation; + Vec3d rad_rotation; for (size_t i = 0; i < 3; ++i) { - rad_rotation(i) = Geometry::deg2rad(rotation(i)); + rad_rotation(i) = Geometry::deg2rad(delta_rotation(i)); } canvas->get_selection().start_dragging(); From a09a9845b5c69daef8bff96ea7ebe9710181153d Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 Jan 2019 11:38:51 +0100 Subject: [PATCH 12/37] Fixed compile on OSX --- src/libslic3r/libslic3r.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 5fd2fafc1b..54344c6181 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -167,7 +167,7 @@ static inline T lerp(const T& a, const T& b, Number t) template static inline bool is_approx(Number value, Number test_value) { - return std::abs(double(value) - double(test_value)) < double(EPSILON); + return std::fabs(double(value) - double(test_value)) < double(EPSILON); }; } // namespace Slic3r From 39005cc8a059ceca1e69c0e47498cc8b4d87cab5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 29 Jan 2019 12:09:40 +0100 Subject: [PATCH 13/37] Added missing include (build on OSX) --- src/libslic3r/libslic3r.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 54344c6181..c7d9f78544 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -16,6 +16,7 @@ #include #include #include +#include #include "Technologies.hpp" From 7c839b8469d99e54d6663210a63de76603bd7136 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 29 Jan 2019 15:10:07 +0100 Subject: [PATCH 14/37] Many major issues solved. Performance may be degraded. --- src/libslic3r/SLA/SLASupportTree.cpp | 44 +++++++++++++------------ src/libslic3r/SLA/SLASupportTree.hpp | 6 ++-- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 13 -------- src/libslic3r/SLAPrint.cpp | 6 ++-- 4 files changed, 29 insertions(+), 40 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index cd26c96225..74c93b824b 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -510,7 +510,6 @@ struct CompactBridge { // A wrapper struct around the base pool (pad) struct Pad { -// Contour3D mesh; TriangleMesh tmesh; PoolConfig cfg; double zlevel = 0; @@ -588,7 +587,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double r_back, double width, const EigenMesh3D& m, - unsigned samples = 8, + unsigned samples = 4, double safety_distance = 0.05) { // method based on: @@ -603,6 +602,7 @@ double pinhead_mesh_intersect(const Vec3d& s, // inner surface of the mesh. Vec3d v = dir; // Our direction (axis) Vec3d c = s + width * dir; + const double& sd = safety_distance; // Two vectors that will be perpendicular to each other and to the axis. // Values for a(X) and a(Y) are now arbitrary, a(Z) is just a placeholder. @@ -626,10 +626,10 @@ double pinhead_mesh_intersect(const Vec3d& s, double cosphi = std::cos(phi); // Let's have a safety coefficient for the radiuses. - double rpscos = (safety_distance + r_pin) * cosphi; - double rpssin = (safety_distance + r_pin) * sinphi; - double rpbcos = (safety_distance + r_back) * cosphi; - double rpbsin = (safety_distance + r_back) * sinphi; + double rpscos = (sd + r_pin) * cosphi; + double rpssin = (sd + r_pin) * sinphi; + double rpbcos = (sd + r_back) * cosphi; + double rpbsin = (sd + r_back) * sinphi; // Point on the circle on the pin sphere Vec3d ps(s(X) + rpscos * a(X) + rpssin * b(X), @@ -639,17 +639,15 @@ double pinhead_mesh_intersect(const Vec3d& s, // Point ps is not on mesh but can be inside or outside as well. This // would cause many problems with ray-casting. So we query the closest // point on the mesh to this. - auto result = m.signed_distance(ps); + auto psq = m.signed_distance(ps); // This is the point on the circle on the back sphere Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - if(!m.inside(p)) { - Vec3d n = (p - result.point_on_mesh() + 0.01 * dir).normalized(); - phi = m.query_ray_hit(result.point_on_mesh(), n); - } else phi = 0; + Vec3d n = (p - psq.point_on_mesh()).normalized(); + phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -661,11 +659,12 @@ double bridge_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r, const EigenMesh3D& m, - unsigned samples = 8, + unsigned samples = 4, double safety_distance = 0.05) { // helper vector calculations Vec3d a(0, 1, 0), b; + const double& sd = safety_distance; a(Z) = -(dir(X)*a(X) + dir(Y)*a(Y)) / dir(Z); b = a.cross(dir); @@ -679,8 +678,8 @@ double bridge_mesh_intersect(const Vec3d& s, double cosphi = std::cos(phi); // Let's have a safety coefficient for the radiuses. - double rcos = (safety_distance + r) * cosphi; - double rsin = (safety_distance + r) * sinphi; + double rcos = (sd + r) * cosphi; + double rsin = (sd + r) * sinphi; // Point on the circle on the pin sphere Vec3d p (s(X) + rcos * a(X) + rsin * b(X), @@ -689,7 +688,9 @@ double bridge_mesh_intersect(const Vec3d& s, auto result = m.signed_distance(p); - phi = m.query_ray_hit(result.point_on_mesh() + 0.05*dir, dir); + Vec3d sp = result.value() < 0 ? result.point_on_mesh() : p; + + phi = m.query_ray_hit(sp + sd*dir, dir); } auto mit = std::min_element(phis.begin(), phis.end()); @@ -1560,6 +1561,8 @@ bool SLASupportTree::generate(const PointSet &points, } double d = distance(jp, jn); + + if(jn(Z) <= gndlvl + 2*cfg.head_width_mm || d > max_len) break; double chkd = bridge_mesh_intersect(jp, dirv(jp, jn), @@ -1784,16 +1787,15 @@ bool SLASupportTree::generate(const PointSet &points, // We will sink the pins into the model surface for a distance of 1/3 of // the pin radius for(int i = 0; i < headless_pts.rows(); i++) { tifcl(); - Vec3d sp = headless_pts.row(i); - - Vec3d n = headless_norm.row(i); - sp = sp - n * HWIDTH_MM; + Vec3d sph = headless_pts.row(i); // Exact support position + Vec3d n = headless_norm.row(i); // mesh outward normal + Vec3d sp = sph - n * HWIDTH_MM; // stick head start point Vec3d dir = {0, 0, -1}; - Vec3d sj = sp + R * n; + Vec3d sj = sp + R * n; // stick start point // This is only for checking - double idist = bridge_mesh_intersect(sj, dir, R, emesh); + double idist = bridge_mesh_intersect(sph, dir, R, emesh); double dist = ray_mesh_intersect(sj, dir, emesh); if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index e13dd8c764..d6e128da01 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -114,14 +114,12 @@ class EigenMesh3D { std::unique_ptr m_aabb; public: - EigenMesh3D(); EigenMesh3D(const TriangleMesh&); - - ~EigenMesh3D(); - EigenMesh3D(const EigenMesh3D& other); EigenMesh3D& operator=(const EigenMesh3D&); + ~EigenMesh3D(); + inline double ground_level() const { return m_ground_level; } inline const Eigen::MatrixXd& V() const { return m_V; } diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index b84e66e41c..b41c56acc8 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -98,8 +98,6 @@ public: igl::WindingNumberAABB windtree; }; -EigenMesh3D::EigenMesh3D(): m_aabb(new AABBImpl()) {} - EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { static const double dEPS = 1e-6; @@ -138,9 +136,7 @@ EigenMesh3D::EigenMesh3D(const TriangleMesh& tmesh): m_aabb(new AABBImpl()) { // Build the AABB accelaration tree m_aabb->init(m_V, m_F); - m_aabb->windtree.set_mesh(m_V, m_F); - m_aabb->windtree.init(); } EigenMesh3D::~EigenMesh3D() {} @@ -207,15 +203,6 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh, Eigen::VectorXi I; PointSet C; - // We need to remove duplicate vertices and have a true index triangle - // structure - /* - EigenMesh3D mesh; - Eigen::VectorXi SVI, SVJ; - 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); PointSet ret(I.rows(), 3); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 47914a8956..dc55b196bb 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -29,6 +29,8 @@ public: SupportTreePtr support_tree_ptr; // the supports SlicedSupports support_slices; // sliced supports std::vector level_ids; + + inline SupportData(const TriangleMesh& trmesh): emesh(trmesh) {} }; namespace { @@ -503,8 +505,8 @@ void SLAPrint::process() // support points. Then we sprinkle the rest of the mesh. auto support_points = [this, ilh](SLAPrintObject& po) { const ModelObject& mo = *po.m_model_object; - po.m_supportdata.reset(new SLAPrintObject::SupportData()); - po.m_supportdata->emesh = EigenMesh3D(po.transformed_mesh()); + po.m_supportdata.reset( + new SLAPrintObject::SupportData(po.transformed_mesh()) ); // If supports are disabled, we can skip the model scan. if(!po.m_config.supports_enable.getBool()) return; From fd65489c11bc83fab1f6cdadad0e830e7c350b51 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 30 Jan 2019 12:18:01 +0100 Subject: [PATCH 15/37] Added debug output to test event handling sequence between Objects List and Object Manipulator fields --- src/slic3r/GUI/GUI_ObjectList.cpp | 3 +++ src/slic3r/GUI/GUI_ObjectManipulation.cpp | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index cb031a8d0f..41a28d61b3 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -66,6 +66,9 @@ ObjectList::ObjectList(wxWindow* parent) : // describe control behavior Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::cout << "SELECTION_CHANGED" << std::endl; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ selection_changed(); #ifndef __WXMSW__ set_tooltip_for_item(get_mouse_position_in_control()); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 08829be93b..86c9fda096 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -26,6 +26,10 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : // needed to hide the visual hints in 3D scene wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + std::cout << "KILL_FOCUS" << std::endl; +//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ + if (!m_cache.is_valid()) return; From 4e82e32a2752532ad7bb64d5565ec7498e8317f7 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Jan 2019 13:51:34 +0100 Subject: [PATCH 16/37] Trying to speed up collision detection with tbb --- src/libslic3r/SLA/SLASupportTree.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 74c93b824b..e753a947a4 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -13,6 +13,7 @@ #include #include +#include /** * Terminology: @@ -588,7 +589,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double width, const EigenMesh3D& m, unsigned samples = 4, - double safety_distance = 0.05) + double safety_distance = 0.001) { // method based on: // https://math.stackexchange.com/questions/73237/parametric-equation-of-a-circle-in-3d-space @@ -621,7 +622,10 @@ double pinhead_mesh_intersect(const Vec3d& s, // they define the plane where we have to iterate with the given angles // in the 'phis' vector - for(double& phi : phis) { + tbb::parallel_for(size_t(0), phis.size(), + [&phis, &m, sd, r_pin, r_back, s, a, b, c](size_t i) + { + double& phi = phis[i]; double sinphi = std::sin(phi); double cosphi = std::cos(phi); @@ -648,7 +652,7 @@ double pinhead_mesh_intersect(const Vec3d& s, Vec3d n = (p - psq.point_on_mesh()).normalized(); phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); - } + }); auto mit = std::min_element(phis.begin(), phis.end()); @@ -660,7 +664,7 @@ double bridge_mesh_intersect(const Vec3d& s, double r, const EigenMesh3D& m, unsigned samples = 4, - double safety_distance = 0.05) + double safety_distance = 0.001) { // helper vector calculations Vec3d a(0, 1, 0), b; @@ -673,7 +677,10 @@ double bridge_mesh_intersect(const Vec3d& s, std::vector phis(samples); for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); - for(double& phi : phis) { + tbb::parallel_for(size_t(0), phis.size(), + [&phis, &m, a, b, sd, dir, r, s](size_t i) + { + double& phi = phis[i]; double sinphi = std::sin(phi); double cosphi = std::cos(phi); @@ -691,7 +698,7 @@ double bridge_mesh_intersect(const Vec3d& s, Vec3d sp = result.value() < 0 ? result.point_on_mesh() : p; phi = m.query_ray_hit(sp + sd*dir, dir); - } + }); auto mit = std::min_element(phis.begin(), phis.end()); From 3f10b2f7f8e87d90198cd91543b54772ded0a73f Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 30 Jan 2019 17:35:39 +0100 Subject: [PATCH 17/37] Getting around signed_distance in pinhead_mesh_intersect --- src/libslic3r/SLA/SLASupportTree.cpp | 48 +++++++++++++++++-------- src/libslic3r/SLA/SLASupportTree.hpp | 32 ++++++++++++++++- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 10 ++++-- 3 files changed, 72 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index e753a947a4..4640037bb4 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -565,7 +565,7 @@ inline double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m) { - return m.query_ray_hit(s, dir); + return m.query_ray_hit(s, dir).distance(); } // This function will test if a future pinhead would not collide with the model @@ -588,7 +588,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double r_back, double width, const EigenMesh3D& m, - unsigned samples = 4, + unsigned samples = 8, double safety_distance = 0.001) { // method based on: @@ -643,15 +643,26 @@ double pinhead_mesh_intersect(const Vec3d& s, // Point ps is not on mesh but can be inside or outside as well. This // would cause many problems with ray-casting. So we query the closest // point on the mesh to this. - auto psq = m.signed_distance(ps); +// auto psq = m.signed_distance(ps); // This is the point on the circle on the back sphere Vec3d p(c(X) + rpbcos * a(X) + rpbsin * b(X), c(Y) + rpbcos * a(Y) + rpbsin * b(Y), c(Z) + rpbcos * a(Z) + rpbsin * b(Z)); - Vec3d n = (p - psq.point_on_mesh()).normalized(); - phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); +// Vec3d n = (p - psq.point_on_mesh()).normalized(); +// phi = m.query_ray_hit(psq.point_on_mesh() + sd*n, n); + + Vec3d n = (p - ps).normalized(); + auto hr = m.query_ray_hit(ps + sd*n, n); + + if(hr.is_inside()) { // the hit is inside the model + if(hr.distance() > 2*r_pin) phi = 0; + else { + auto hr2 = m.query_ray_hit(ps + (hr.distance() + 2*sd)*n, n); + phi = hr2.distance(); + } + } else phi = hr.distance(); }); auto mit = std::min_element(phis.begin(), phis.end()); @@ -659,10 +670,14 @@ double pinhead_mesh_intersect(const Vec3d& s, return *mit; } +// Checking bridge (pillar and stick as well) intersection with the model. If +// the function is used for headless sticks, the ins_check parameter have to be +// true as the beginning of the stick might be inside the model geometry. double bridge_mesh_intersect(const Vec3d& s, const Vec3d& dir, double r, const EigenMesh3D& m, + bool ins_check = false, unsigned samples = 4, double safety_distance = 0.001) { @@ -678,7 +693,7 @@ double bridge_mesh_intersect(const Vec3d& s, for(size_t i = 0; i < phis.size(); ++i) phis[i] = i*2*PI/phis.size(); tbb::parallel_for(size_t(0), phis.size(), - [&phis, &m, a, b, sd, dir, r, s](size_t i) + [&phis, &m, a, b, sd, dir, r, s, ins_check](size_t i) { double& phi = phis[i]; double sinphi = std::sin(phi); @@ -693,11 +708,13 @@ double bridge_mesh_intersect(const Vec3d& s, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - auto result = m.signed_distance(p); + Vec3d sp; + if(ins_check) { + auto result = m.signed_distance(p); + sp = result.value() < 0 ? result.point_on_mesh() : p; + } else sp = p; - Vec3d sp = result.value() < 0 ? result.point_on_mesh() : p; - - phi = m.query_ray_hit(sp + sd*dir, dir); + phi = m.query_ray_hit(sp + sd*dir, dir).distance(); }); auto mit = std::min_element(phis.begin(), phis.end()); @@ -1538,12 +1555,12 @@ bool SLASupportTree::generate(const PointSet &points, // is distributed more effectively on the pillar. auto search_nearest = - [&cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] + [&tifcl, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] (SpatIndex& spindex, const Vec3d& jsh) { long nearest_id = -1; const double max_len = maxbridgelen / 2; - while(nearest_id < 0 && !spindex.empty()) { + while(nearest_id < 0 && !spindex.empty()) { tifcl(); // loop until a suitable head is not found // if there is a pillar closer than the cluster center // (this may happen as the clustering is not perfect) @@ -1660,7 +1677,7 @@ bool SLASupportTree::generate(const PointSet &points, if(!ring.empty()) { // inner ring is now in 'newring' and outer ring is in 'ring' SpatIndex innerring; - for(unsigned i : newring) { + for(unsigned i : newring) { tifcl(); const Pillar& pill = result.head_pillar(gndidx[i]); assert(pill.id >= 0); innerring.insert(pill.endpoint, unsigned(pill.id)); @@ -1669,7 +1686,7 @@ bool SLASupportTree::generate(const PointSet &points, // For all pillars in the outer ring find the closest in the // inner ring and connect them. This will create the spider web // fashioned connections between pillars - for(unsigned i : ring) { + for(unsigned i : ring) { tifcl(); const Pillar& outerpill = result.head_pillar(gndidx[i]); auto res = innerring.nearest(outerpill.endpoint, 1); if(res.empty()) continue; @@ -1695,6 +1712,7 @@ bool SLASupportTree::generate(const PointSet &points, next != ring.end(); ++it, ++next) { + tifcl(); const Pillar& pillar = result.head_pillar(gndidx[*it]); const Pillar& nextpillar = result.head_pillar(gndidx[*next]); interconnect(pillar, nextpillar, emesh, result); @@ -1802,7 +1820,7 @@ bool SLASupportTree::generate(const PointSet &points, Vec3d sj = sp + R * n; // stick start point // This is only for checking - double idist = bridge_mesh_intersect(sph, dir, R, emesh); + double idist = bridge_mesh_intersect(sph, dir, R, emesh, true); double dist = ray_mesh_intersect(sj, dir, emesh); if(std::isinf(idist) || std::isnan(idist) || idist < 2*R || diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index d6e128da01..b1e77b0562 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -125,8 +125,38 @@ public: inline const Eigen::MatrixXd& V() const { return m_V; } inline const Eigen::MatrixXi& F() const { return m_F; } + // Result of a raycast + class hit_result { + double m_t = std::numeric_limits::infinity(); + int m_face_id = -1; + const EigenMesh3D& m_mesh; + Vec3d m_dir; + inline hit_result(const EigenMesh3D& em): m_mesh(em) {} + friend class EigenMesh3D; + public: + + inline double distance() const { return m_t; } + + inline int face() const { return m_face_id; } + + inline Vec3d normal() const { + if(m_face_id < 0) return {}; + auto trindex = m_mesh.m_F.row(m_face_id); + const Vec3d& p1 = m_mesh.V().row(trindex(0)); + const Vec3d& p2 = m_mesh.V().row(trindex(1)); + const Vec3d& p3 = m_mesh.V().row(trindex(2)); + Eigen::Vector3d U = p2 - p1; + Eigen::Vector3d V = p3 - p1; + return U.cross(V).normalized(); + } + + inline bool is_inside() { + return m_face_id >= 0 && normal().dot(m_dir) > 0; + } + }; + // Casting a ray on the mesh, returns the distance where the hit occures. - double query_ray_hit(const Vec3d &s, const Vec3d &dir) const; + hit_result query_ray_hit(const Vec3d &s, const Vec3d &dir) const; class si_result { double m_value; diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index b41c56acc8..d3af1eac89 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -153,13 +153,19 @@ EigenMesh3D &EigenMesh3D::operator=(const EigenMesh3D &other) m_aabb.reset(new AABBImpl(*other.m_aabb)); return *this; } -double EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const +EigenMesh3D::hit_result +EigenMesh3D::query_ray_hit(const Vec3d &s, const Vec3d &dir) const { igl::Hit hit; hit.t = std::numeric_limits::infinity(); m_aabb->intersect_ray(m_V, m_F, s, dir, hit); - return double(hit.t); + hit_result ret(*this); + ret.m_t = double(hit.t); + ret.m_dir = dir; + if(!std::isinf(hit.t) && !std::isnan(hit.t)) ret.m_face_id = hit.id; + + return ret; } EigenMesh3D::si_result EigenMesh3D::signed_distance(const Vec3d &p) const { From 095dfcad9e23a88398c99cf49badb94d9d46066c Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Thu, 31 Jan 2019 10:11:37 +0100 Subject: [PATCH 18/37] Getting rid of signed distance from collision detection. --- src/libslic3r/SLA/SLASupportTree.cpp | 16 ++++++++++------ src/libslic3r/SLA/SLASupportTree.hpp | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 4640037bb4..54e3a0189b 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -659,6 +659,7 @@ double pinhead_mesh_intersect(const Vec3d& s, if(hr.is_inside()) { // the hit is inside the model if(hr.distance() > 2*r_pin) phi = 0; else { + // re-cast the ray from the outside of the object auto hr2 = m.query_ray_hit(ps + (hr.distance() + 2*sd)*n, n); phi = hr2.distance(); } @@ -708,13 +709,16 @@ double bridge_mesh_intersect(const Vec3d& s, s(Y) + rcos * a(Y) + rsin * b(Y), s(Z) + rcos * a(Z) + rsin * b(Z)); - Vec3d sp; - if(ins_check) { - auto result = m.signed_distance(p); - sp = result.value() < 0 ? result.point_on_mesh() : p; - } else sp = p; + auto hr = m.query_ray_hit(p + sd*dir, dir); - phi = m.query_ray_hit(sp + sd*dir, dir).distance(); + if(ins_check && hr.is_inside()) { + if(hr.distance() > 2*r) phi = 0; + else { + // re-cast the ray from the outside of the object + auto hr2 = m.query_ray_hit(p + (hr.distance() + 2*sd)*dir, dir); + phi = hr2.distance(); + } + } else phi = hr.distance(); }); auto mit = std::min_element(phis.begin(), phis.end()); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index b1e77b0562..8de8d2b337 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -78,7 +78,7 @@ struct SupportConfig { 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; + double normal_cutoff_angle = 150.0 * M_PI / 180.0; }; From 8076b39c4bc8af13c8c18e68695f0eb374169ef9 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 31 Jan 2019 14:12:07 +0100 Subject: [PATCH 19/37] Emulation of kill focus event on object manipulator fields when changing selection into objects list [WIN+LINUX] --- src/slic3r/GUI/GUI_ObjectList.cpp | 9 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 172 +++++++++++++--------- src/slic3r/GUI/GUI_ObjectManipulation.hpp | 14 ++ 3 files changed, 121 insertions(+), 74 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index eb34e12514..9a789633fb 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -66,9 +66,12 @@ ObjectList::ObjectList(wxWindow* parent) : // describe control behavior Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, [this](wxEvent& event) { -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - std::cout << "SELECTION_CHANGED" << std::endl; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ +#ifndef __APPLE__ + // On Windows and Linux, forces a kill focus emulation on the object manipulator fields because this event handler is called + // before the kill focus event handler on the object manipulator when changing selection in the list, invalidating the object + // manipulator cache with the following call to selection_changed() + wxGetApp().obj_manipul()->emulate_kill_focus(); +#endif // __APPLE__ selection_changed(); #ifndef __WXMSW__ set_tooltip_for_item(get_mouse_position_in_control()); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 86c9fda096..dc725bf88d 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -17,85 +17,23 @@ namespace GUI ObjectManipulation::ObjectManipulation(wxWindow* parent) : OG_Settings(parent, true) +#ifndef __APPLE__ + , m_focused_option("") +#endif // __APPLE__ { m_og->set_name(_(L("Object Manipulation"))); m_og->label_width = 125; m_og->set_grid_vgap(5); - m_og->m_on_change = [this](const std::string& opt_key, const boost::any& value) { - // needed to hide the visual hints in 3D scene - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); - -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - std::cout << "KILL_FOCUS" << std::endl; -//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ - - if (!m_cache.is_valid()) - return; - - std::vector axes{ "_x", "_y", "_z" }; - - std::string param; - std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); - - size_t i = 0; - Vec3d new_value; - for (auto axis : axes) - new_value(i++) = boost::any_cast(m_og->get_value(param+axis)); - - if (param == "position") - change_position_value(new_value); - else if (param == "rotation") - 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) - { - // needed to hide the visual hints in 3D scene - wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); - - if (!m_cache.is_valid()) - 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; - - value = m_cache.position(axis); - } - else if (param == "rotation") { - int axis = opt_key.back() == 'x' ? 0 : - opt_key.back() == 'y' ? 1 : 2; - - value = m_cache.rotation(axis); - } - else if (param == "scale") { - int axis = opt_key.back() == 'x' ? 0 : - opt_key.back() == 'y' ? 1 : 2; - - value = m_cache.scale(axis); - } - else if (param == "size") { - int axis = opt_key.back() == 'x' ? 0 : - opt_key.back() == 'y' ? 1 : 2; - - value = m_cache.size(axis); - } - - m_og->set_value(opt_key, double_to_string(value)); - }; + m_og->m_on_change = std::bind(&ObjectManipulation::on_change, this, std::placeholders::_1, std::placeholders::_2); + m_og->m_fill_empty_value = std::bind(&ObjectManipulation::on_fill_empty_value, this, std::placeholders::_1); m_og->m_set_focus = [this](const std::string& opt_key) { +#ifndef __APPLE__ + m_focused_option = opt_key; +#endif // __APPLE__ + // needed to show the visual hints in 3D scene wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, true); }; @@ -383,6 +321,23 @@ void ObjectManipulation::update_if_dirty() m_dirty = false; } +#ifndef __APPLE__ +void ObjectManipulation::emulate_kill_focus() +{ + if (m_focused_option.empty()) + return; + + // we need to use a copy because the value of m_focused_option is modified inside on_change() and on_fill_empty_value() + std::string option = m_focused_option; + + // see TextCtrl::propagate_value() + if (static_cast(m_og->get_fieldc(option, 0)->getWindow())->GetValue().empty()) + on_fill_empty_value(option); + else + on_change(option, 0); +} +#endif // __APPLE__ + void ObjectManipulation::reset_settings_value() { m_new_position = Vec3d::Zero(); @@ -504,5 +459,80 @@ void ObjectManipulation::change_size_value(const Vec3d& size) m_cache.size = size; } +void ObjectManipulation::on_change(const t_config_option_key& opt_key, const boost::any& value) +{ + // needed to hide the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); +#ifndef __APPLE__ + m_focused_option = ""; +#endif // __APPLE__ + + if (!m_cache.is_valid()) + return; + + std::vector axes{ "_x", "_y", "_z" }; + + std::string param; + std::copy(opt_key.begin(), opt_key.end() - 2, std::back_inserter(param)); + + size_t i = 0; + Vec3d new_value; + for (auto axis : axes) + new_value(i++) = boost::any_cast(m_og->get_value(param + axis)); + + if (param == "position") + change_position_value(new_value); + else if (param == "rotation") + change_rotation_value(new_value); + else if (param == "scale") + change_scale_value(new_value); + else if (param == "size") + change_size_value(new_value); +} + +void ObjectManipulation::on_fill_empty_value(const std::string& opt_key) +{ + // needed to hide the visual hints in 3D scene + wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event(opt_key, false); +#ifndef __APPLE__ + m_focused_option = ""; +#endif // __APPLE__ + + if (!m_cache.is_valid()) + 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; + + value = m_cache.position(axis); + } + else if (param == "rotation") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = m_cache.rotation(axis); + } + else if (param == "scale") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = m_cache.scale(axis); + } + else if (param == "size") { + int axis = opt_key.back() == 'x' ? 0 : + opt_key.back() == 'y' ? 1 : 2; + + value = m_cache.size(axis); + } + + m_og->set_value(opt_key, double_to_string(value)); +} + } //namespace GUI } //namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.hpp b/src/slic3r/GUI/GUI_ObjectManipulation.hpp index b761beda87..16160c84d1 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.hpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.hpp @@ -77,6 +77,11 @@ class ObjectManipulation : public OG_Settings bool m_uniform_scale {true}; PrusaLockButton* m_lock_bnt{ nullptr }; +#ifndef __APPLE__ + // Currently focused option name (empty if none) + std::string m_focused_option; +#endif // __APPLE__ + public: ObjectManipulation(wxWindow* parent); ~ObjectManipulation() {} @@ -94,6 +99,12 @@ public: bool get_uniform_scaling() const { return m_uniform_scale; } void reset_cache() { m_cache.reset(); } +#ifndef __APPLE__ + // On Windows and Linux, emulates a kill focus event on the currently focused option (if any) + // Used only in ObjectList wxEVT_DATAVIEW_SELECTION_CHANGED handler which is called before the regular kill focus event + // bound to this class when changing selection in the objects list + void emulate_kill_focus(); +#endif // __APPLE__ private: void reset_settings_value(); @@ -108,6 +119,9 @@ private: void change_rotation_value(const Vec3d& rotation); void change_scale_value(const Vec3d& scale); void change_size_value(const Vec3d& size); + + void on_change(const t_config_option_key& opt_key, const boost::any& value); + void on_fill_empty_value(const std::string& opt_key); }; }} From fb6ef1d20fdfaecef3f3b4792386acd6a31f58b5 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 31 Jan 2019 14:25:11 +0100 Subject: [PATCH 20/37] Keeps modifier transparent while layer editing tool is enabled --- src/slic3r/GUI/3DScene.cpp | 4 ++++ src/slic3r/GUI/3DScene.hpp | 2 ++ src/slic3r/GUI/GLCanvas3D.cpp | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index fd1645c9e2..6b1df6ab36 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -252,6 +252,7 @@ GLVolume::GLVolume(float r, float g, float b, float a) , is_modifier(false) , is_wipe_tower(false) , is_extrusion_path(false) + , force_transparent(false) , tverts_range(0, size_t(-1)) , qverts_range(0, size_t(-1)) { @@ -293,6 +294,9 @@ void GLVolume::set_render_color() set_render_color(OUTSIDE_COLOR, 4); else set_render_color(color, 4); + + if (force_transparent) + render_color[3] = color[3]; } void GLVolume::set_color_from_model_volume(const ModelVolume *model_volume) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 7430ff4aa0..2732b5a137 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -295,6 +295,8 @@ public: bool is_wipe_tower; // Wheter or not this volume has been generated from an extrusion path bool is_extrusion_path; + // Wheter or not to always render this volume using its own alpha + bool force_transparent; // Interleaved triangles & normals with indexed triangles & quads. GLIndexedVertexArray indexed_vertex_array; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 72dfe52bdd..33f62f2059 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4286,6 +4286,13 @@ bool GLCanvas3D::is_reload_delayed() const void GLCanvas3D::enable_layers_editing(bool enable) { m_layers_editing.set_enabled(enable); + const Selection::IndicesList& idxs = m_selection.get_volume_idxs(); + for (unsigned int idx : idxs) + { + GLVolume* v = m_volumes.volumes[idx]; + if (v->is_modifier) + v->force_transparent = enable; + } } void GLCanvas3D::enable_warning_texture(bool enable) From c82d346c1a5b119aeb8c5176f832ccf19e7dbd9f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 31 Jan 2019 15:09:16 +0100 Subject: [PATCH 21/37] Fixed a bug in initialization of some StaticPrintConfig derived classes. Merged implementation of support for "Octoprint-Cancelobject" #972 thanks @supermerill --- src/libslic3r/GCode.cpp | 6 +++++- src/libslic3r/Print.cpp | 1 + src/libslic3r/PrintConfig.cpp | 9 +++++++++ src/libslic3r/PrintConfig.hpp | 6 ++++-- src/slic3r/GUI/Preset.cpp | 4 ++-- src/slic3r/GUI/Tab.cpp | 1 + 6 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 66698d4ada..ec9392ec45 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1622,6 +1622,8 @@ void GCode::process_layer( unsigned int copy_id = 0; for (const Point © : copies) { + if (this->config().gcode_label_objects) + gcode += std::string("; printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n"; // When starting a new object, use the external motion planner for the first travel move. std::pair this_object_copy(print_object, copy); if (m_last_obj_copy != this_object_copy) @@ -1646,7 +1648,9 @@ void GCode::process_layer( gcode += this->extrude_infill(print,by_region_specific); } } - ++copy_id; + if (this->config().gcode_label_objects) + gcode += std::string("; stop printing object ") + print_object->model_object()->name + " id:" + std::to_string(layer_id) + " copy " + std::to_string(copy_id) + "\n"; + ++ copy_id; } } } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 3efb185997..42fda92fe5 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -127,6 +127,7 @@ bool Print::invalidate_state_by_config_options(const std::vectormode = comExpert; def->default_value = new ConfigOptionEnum(gcfRepRap); + def = this->add("gcode_label_objects", coBool); + def->label = "Label objects"; + def->tooltip = "Enable this to add comments into the G-Code labeling print moves with what object they belong to," + " which is useful for the Octoprint CancelObject plugin. This settings is NOT compatible with " + "Single Extruder Multi Material setup and Wipe into Object / Wipe into Infill."; + def->cli = "gcode-label-objects!"; + def->mode = comAdvanced; + def->default_value = new ConfigOptionBool(0); + def = this->add("high_current_on_filament_swap", coBool); def->label = L("High extruder current on filament swap"); def->tooltip = L("It may be beneficial to increase the extruder motor current during the filament exchange" diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1b218a192a..6b9e68eeff 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -619,6 +619,7 @@ public: ConfigOptionStrings filament_ramming_parameters; ConfigOptionBool gcode_comments; ConfigOptionEnum gcode_flavor; + ConfigOptionBool gcode_label_objects; ConfigOptionString layer_gcode; ConfigOptionFloat max_print_speed; ConfigOptionFloat max_volumetric_speed; @@ -690,6 +691,7 @@ protected: OPT_PTR(filament_ramming_parameters); OPT_PTR(gcode_comments); OPT_PTR(gcode_flavor); + OPT_PTR(gcode_label_objects); OPT_PTR(layer_gcode); OPT_PTR(max_print_speed); OPT_PTR(max_volumetric_speed); @@ -730,7 +732,7 @@ protected: class PrintConfig : public MachineEnvelopeConfig, public GCodeConfig { STATIC_PRINT_CONFIG_CACHE_DERIVED(PrintConfig) - PrintConfig() : GCodeConfig(0) { initialize_cache(); *this = s_cache_PrintConfig.defaults(); } + PrintConfig() : MachineEnvelopeConfig(0), GCodeConfig(0) { initialize_cache(); *this = s_cache_PrintConfig.defaults(); } public: double min_object_distance() const; static double min_object_distance(const ConfigBase *config); @@ -808,7 +810,7 @@ public: ConfigOptionFloat exp_time_first; protected: - PrintConfig(int) : GCodeConfig(1) {} + PrintConfig(int) : MachineEnvelopeConfig(1), GCodeConfig(1) {} void initialize(StaticCacheBase &cache, const char *base_ptr) { this->MachineEnvelopeConfig::initialize(cache, base_ptr); diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index ef1caf0358..53650481c3 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -336,7 +336,7 @@ const std::vector& Preset::print_options() "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_buildplate_only", "dont_support_bridges", "notes", "complete_objects", "extruder_clearance_radius", - "extruder_clearance_height", "gcode_comments", "output_filename_format", "post_process", "perimeter_extruder", + "extruder_clearance_height", "gcode_comments", "gcode_label_objects", "output_filename_format", "post_process", "perimeter_extruder", "infill_extruder", "solid_infill_extruder", "support_material_extruder", "support_material_interface_extruder", "ooze_prevention", "standby_temperature_delta", "interface_shells", "extrusion_width", "first_layer_extrusion_width", "perimeter_extrusion_width", "external_perimeter_extrusion_width", "infill_extrusion_width", "solid_infill_extrusion_width", @@ -1163,7 +1163,7 @@ std::string PresetCollection::name() const case Preset::TYPE_PRINT: return L("print"); case Preset::TYPE_FILAMENT: return L("filament"); case Preset::TYPE_SLA_PRINT: return L("SLA print"); - case Preset::TYPE_SLA_MATERIAL: return L("SLA material"); + case Preset::TYPE_SLA_MATERIAL: return L("SLA material"); case Preset::TYPE_PRINTER: return L("printer"); default: return "invalid"; } diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 3f76b4f782..06282cd24b 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1095,6 +1095,7 @@ void TabPrint::build() optgroup = page->new_optgroup(_(L("Output file"))); optgroup->append_single_option_line("gcode_comments"); + optgroup->append_single_option_line("gcode_label_objects"); option = optgroup->get_option("output_filename_format"); option.opt.full_width = true; optgroup->append_single_option_line(option); From 405d18a7fab4a7a0b0eefda531f232fecb9ec7ea Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 1 Feb 2019 08:53:29 +0100 Subject: [PATCH 22/37] Changed default the Drag & Drop target from "Move" to "Copy". Fixes #1742 --- 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 38adde8b3e..428ea07c6f 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -896,7 +896,7 @@ std::vector& Sidebar::combos_filament() class PlaterDropTarget : public wxFileDropTarget { public: - PlaterDropTarget(Plater *plater) : plater(plater) {} + PlaterDropTarget(Plater *plater) : plater(plater) { this->SetDefaultAction(wxDragCopy); } virtual bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames); From 3b973e01dd3c893116d80ba79cb8f06ff023cce5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 1 Feb 2019 11:44:08 +0100 Subject: [PATCH 23/37] Get name of both the G-code and project file from the 1st printable object's name or file path. Fixed some compilation warnings. --- src/libslic3r/GCode/SpiralVase.hpp | 2 +- src/libslic3r/GCodeReader.hpp | 2 +- src/libslic3r/Model.cpp | 13 ++++++++++--- src/libslic3r/PrintBase.cpp | 15 ++------------- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/GCode/SpiralVase.hpp b/src/libslic3r/GCode/SpiralVase.hpp index 60aa668d89..7872b1d3c5 100644 --- a/src/libslic3r/GCode/SpiralVase.hpp +++ b/src/libslic3r/GCode/SpiralVase.hpp @@ -13,7 +13,7 @@ class SpiralVase { SpiralVase(const PrintConfig &config) : enable(false), _config(&config) { - this->_reader.z() = this->_config->z_offset; + this->_reader.z() = (float)this->_config->z_offset; this->_reader.apply_config(*this->_config); }; std::string process_layer(const std::string &gcode); diff --git a/src/libslic3r/GCodeReader.hpp b/src/libslic3r/GCodeReader.hpp index 13f9e7dd74..f64605a9c5 100644 --- a/src/libslic3r/GCodeReader.hpp +++ b/src/libslic3r/GCodeReader.hpp @@ -43,7 +43,7 @@ public: } bool cmd_is(const char *cmd_test) const { const char *cmd = GCodeReader::skip_whitespaces(m_raw.c_str()); - int len = strlen(cmd_test); + size_t len = strlen(cmd_test); return strncmp(cmd, cmd_test, len) == 0 && GCodeReader::is_end_of_word(cmd[len]); } bool extruding(const GCodeReader &reader) const { return this->cmd_is("G1") && this->dist_E(reader) > 0; } diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index a88bdd9910..968a3e2348 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -549,11 +549,18 @@ void Model::reset_auto_extruder_id() std::string Model::propose_export_file_name() const { + std::string input_file; for (const ModelObject *model_object : this->objects) for (ModelInstance *model_instance : model_object->instances) - if (model_instance->is_printable()) - return model_object->name.empty() ? model_object->input_file : model_object->name; - return std::string(); + if (model_instance->is_printable()) { + input_file = model_object->name.empty() ? model_object->input_file : model_object->name; + if (! input_file.empty()) + goto end; + // Other instances will produce the same name, skip them. + break; + } +end: + return input_file; } ModelObject::~ModelObject() diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 1d078da305..48e991b8da 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -67,20 +67,9 @@ std::string PrintBase::output_filename(const std::string &format, const std::str std::string PrintBase::output_filepath(const std::string &path) const { // if we were supplied no path, generate an automatic one based on our first object's input file - if (path.empty()) { + if (path.empty()) // get the first input file name - std::string input_file; - for (const ModelObject *model_object : m_model.objects) { - for (ModelInstance *model_instance : model_object->instances) - if (model_instance->is_printable()) { - input_file = model_object->input_file; - break; - } - if (! input_file.empty()) - break; - } - return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).make_preferred().string(); - } + return (boost::filesystem::path(m_model.propose_export_file_name()).parent_path() / this->output_filename()).make_preferred().string(); // if we were supplied a directory, use it and append our automatically generated filename boost::filesystem::path p(path); From e59a10e0c28f3daeb7793a75a23c416d7112d05a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 1 Feb 2019 17:15:41 +0100 Subject: [PATCH 24/37] Fix of a cooling slow down logic. fixes "Min print speed" to "Estimated Print Time" Inconsistencies #1488 --- src/libslic3r/GCode/CoolingBuffer.cpp | 95 +++++++++++++++++++++------ src/libslic3r/GCode/CoolingBuffer.hpp | 2 +- 2 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 40ccc7b09f..09d211994a 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -83,7 +83,7 @@ struct CoolingLine struct PerExtruderAdjustments { // Calculate the total elapsed time per this extruder, adjusted for the slowdown. - float elapsed_time_total() { + float elapsed_time_total() const { float time_total = 0.f; for (const CoolingLine &line : lines) time_total += line.time; @@ -91,7 +91,7 @@ struct PerExtruderAdjustments } // Calculate the total elapsed time when slowing down // to the minimum extrusion feed rate defined for the current material. - float maximum_time_after_slowdown(bool slowdown_external_perimeters) { + float maximum_time_after_slowdown(bool slowdown_external_perimeters) const { float time_total = 0.f; for (const CoolingLine &line : lines) if (line.adjustable(slowdown_external_perimeters)) { @@ -104,7 +104,7 @@ struct PerExtruderAdjustments return time_total; } // Calculate the adjustable part of the total time. - float adjustable_time(bool slowdown_external_perimeters) { + float adjustable_time(bool slowdown_external_perimeters) const { float time_total = 0.f; for (const CoolingLine &line : lines) if (line.adjustable(slowdown_external_perimeters)) @@ -112,7 +112,7 @@ struct PerExtruderAdjustments return time_total; } // Calculate the non-adjustable part of the total time. - float non_adjustable_time(bool slowdown_external_perimeters) { + float non_adjustable_time(bool slowdown_external_perimeters) const { float time_total = 0.f; for (const CoolingLine &line : lines) if (! line.adjustable(slowdown_external_perimeters)) @@ -169,7 +169,7 @@ struct PerExtruderAdjustments // Calculate the maximum time stretch when slowing down to min_feedrate. // Slowdown to min_feedrate shall be allowed for this extruder's material. // Used by non-proportional slow down. - float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) { + float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) const { float time_stretch = 0.f; assert(this->min_print_speed < min_feedrate + EPSILON); for (size_t i = 0; i < n_lines_adjustable; ++ i) { @@ -221,6 +221,61 @@ struct PerExtruderAdjustments size_t idx_line_end = 0; }; +// Calculate a new feedrate when slowing down by time_stretch for segments faster than min_feedrate. +// Used by non-proportional slow down. +float new_feedrate_to_reach_time_stretch( + std::vector::const_iterator it_begin, std::vector::const_iterator it_end, + float min_feedrate, float time_stretch, size_t max_iter = 20) +{ + float new_feedrate = min_feedrate; + for (size_t iter = 0; iter < max_iter; ++ iter) { + float nomin = 0; + float denom = time_stretch; + for (auto it = it_begin; it != it_end; ++ it) { + assert((*it)->min_print_speed < min_feedrate + EPSILON); + for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) { + const CoolingLine &line = (*it)->lines[i]; + if (line.feedrate > min_feedrate) { + nomin += line.time * line.feedrate; + denom += line.time; + } + } + } + assert(denom > 0); + if (denom < 0) + return min_feedrate; + new_feedrate = nomin / denom; + assert(new_feedrate > min_feedrate - EPSILON); + if (new_feedrate < min_feedrate + EPSILON) + goto finished; + for (auto it = it_begin; it != it_end; ++ it) + for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) { + const CoolingLine &line = (*it)->lines[i]; + if (line.feedrate > min_feedrate && line.feedrate < new_feedrate) + // Some of the line segments taken into account in the calculation of nomin / denom are now slower than new_feedrate. + // Re-run the calculation with a new min_feedrate limit. + goto not_finished_yet; + } + goto finished; +not_finished_yet: + min_feedrate = new_feedrate; + } + // Failed to find the new feedrate for the time_stretch. + +finished: + // Test whether the time_stretch was achieved. +#ifndef NDEBUG + { + float time_stretch_final = 0.f; + for (auto it = it_begin; it != it_end; ++ it) + time_stretch_final += (*it)->time_stretch_when_slowing_down_to_feedrate(new_feedrate); + assert(std::abs(time_stretch - time_stretch_final) < EPSILON); + } +#endif /* NDEBUG */ + + return new_feedrate; +} + std::string CoolingBuffer::process_layer(const std::string &gcode, size_t layer_id) { std::vector per_extruder_adjustments = this->parse_layer_gcode(gcode, m_current_pos); @@ -241,12 +296,12 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: std::vector per_extruder_adjustments(extruders.size()); std::vector map_extruder_to_per_extruder_adjustment(num_extruders, 0); for (size_t i = 0; i < extruders.size(); ++ i) { - PerExtruderAdjustments &adj = per_extruder_adjustments[i]; - unsigned int extruder_id = extruders[i].id(); - adj.extruder_id = extruder_id; - adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); - adj.slowdown_below_layer_time = config.slowdown_below_layer_time.get_at(extruder_id); - adj.min_print_speed = config.min_print_speed.get_at(extruder_id); + PerExtruderAdjustments &adj = per_extruder_adjustments[i]; + unsigned int extruder_id = extruders[i].id(); + adj.extruder_id = extruder_id; + adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); + adj.slowdown_below_layer_time = config.slowdown_below_layer_time.get_at(extruder_id); + adj.min_print_speed = config.min_print_speed.get_at(extruder_id); map_extruder_to_per_extruder_adjustment[extruder_id] = i; } @@ -452,14 +507,14 @@ static inline void extruder_range_slow_down_non_proportional( std::vector by_min_print_speed(it_begin, it_end); // Find the next highest adjustable feedrate among the extruders. float feedrate = 0; - for (PerExtruderAdjustments *adj : by_min_print_speed) { - adj->idx_line_begin = 0; - adj->idx_line_end = 0; - assert(adj->idx_line_begin < adj->n_lines_adjustable); - if (adj->lines[adj->idx_line_begin].feedrate > feedrate) - feedrate = adj->lines[adj->idx_line_begin].feedrate; - } - assert(feedrate > 0.f); + for (PerExtruderAdjustments *adj : by_min_print_speed) { + adj->idx_line_begin = 0; + adj->idx_line_end = 0; + assert(adj->idx_line_begin < adj->n_lines_adjustable); + if (adj->lines[adj->idx_line_begin].feedrate > feedrate) + feedrate = adj->lines[adj->idx_line_begin].feedrate; + } + assert(feedrate > 0.f); // Sort by min_print_speed, maximum speed first. std::sort(by_min_print_speed.begin(), by_min_print_speed.end(), [](const PerExtruderAdjustments *p1, const PerExtruderAdjustments *p2){ return p1->min_print_speed > p2->min_print_speed; }); @@ -496,7 +551,7 @@ static inline void extruder_range_slow_down_non_proportional( for (auto it = adj; it != by_min_print_speed.end(); ++ it) time_stretch_max += (*it)->time_stretch_when_slowing_down_to_feedrate(feedrate_limit); if (time_stretch_max >= time_stretch) { - feedrate_limit = feedrate - (feedrate - feedrate_limit) * time_stretch / time_stretch_max; + feedrate_limit = new_feedrate_to_reach_time_stretch(adj, by_min_print_speed.end(), feedrate_limit, time_stretch, 20); done = true; } else time_stretch -= time_stretch_max; diff --git a/src/libslic3r/GCode/CoolingBuffer.hpp b/src/libslic3r/GCode/CoolingBuffer.hpp index bf4b082e25..511089ad0c 100644 --- a/src/libslic3r/GCode/CoolingBuffer.hpp +++ b/src/libslic3r/GCode/CoolingBuffer.hpp @@ -9,7 +9,7 @@ namespace Slic3r { class GCode; class Layer; -class PerExtruderAdjustments; +struct PerExtruderAdjustments; // A standalone G-code filter, to control cooling of the print. // The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited From a56f7d60e58f05624ccb45573e4d6344c71a9692 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 10:41:14 +0100 Subject: [PATCH 25/37] Fixed an issue, where the output G-code file name was not always updated from the current Model/ModelObjects. Fixed a possible race condition in updating Print::m_placeholder_parser with the proposed filename / filename base. Improved documentation (source code comments). --- src/libslic3r/GCode.cpp | 1 + src/libslic3r/GCode/CoolingBuffer.cpp | 6 +- src/libslic3r/Model.cpp | 17 +++++- src/libslic3r/Model.hpp | 4 +- src/libslic3r/PlaceholderParser.hpp | 3 +- src/libslic3r/Print.cpp | 8 +-- src/libslic3r/PrintBase.cpp | 16 +++--- src/libslic3r/PrintBase.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 2 - src/slic3r.cpp | 5 +- src/slic3r/GUI/Plater.cpp | 83 +++++++++++++-------------- src/slic3r/GUI/Plater.hpp | 3 +- 12 files changed, 80 insertions(+), 70 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ec9392ec45..4d3ad00dd0 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -717,6 +717,7 @@ void GCode::_do_export(Print &print, FILE *file) // Prepare the helper object for replacing placeholders in custom G-code and output filename. m_placeholder_parser = print.placeholder_parser(); m_placeholder_parser.update_timestamp(); + print.update_object_placeholders(m_placeholder_parser.config_writable()); // Get optimal tool ordering to minimize tool switches of a multi-exruder print. // For a print by objects, find the 1st printing object. diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 09d211994a..552fbf88c8 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -252,8 +252,10 @@ float new_feedrate_to_reach_time_stretch( for (size_t i = 0; i < (*it)->n_lines_adjustable; ++i) { const CoolingLine &line = (*it)->lines[i]; if (line.feedrate > min_feedrate && line.feedrate < new_feedrate) - // Some of the line segments taken into account in the calculation of nomin / denom are now slower than new_feedrate. - // Re-run the calculation with a new min_feedrate limit. + // Some of the line segments taken into account in the calculation of nomin / denom are now slower than new_feedrate, + // which makes the new_feedrate lower than it should be. + // Re-run the calculation with a new min_feedrate limit, so that the segments with current feedrate lower than new_feedrate + // are not taken into account. goto not_finished_yet; } goto finished; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 968a3e2348..08eb8df818 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -547,13 +547,26 @@ void Model::reset_auto_extruder_id() s_auto_extruder_id = 1; } -std::string Model::propose_export_file_name() const +// Propose a filename including path derived from the ModelObject's input path. +// If object's name is filled in, use the object name, otherwise use the input name. +std::string Model::propose_export_file_name_and_path() const { std::string input_file; for (const ModelObject *model_object : this->objects) for (ModelInstance *model_instance : model_object->instances) if (model_instance->is_printable()) { - input_file = model_object->name.empty() ? model_object->input_file : model_object->name; + input_file = model_object->input_file; + if (! model_object->name.empty()) { + if (input_file.empty()) + // model_object->input_file was empty, just use model_object->name + input_file = model_object->name; + else { + // Replace file name in input_file with model_object->name, but keep the path and file extension. + input_file = (boost::filesystem::path(model_object->name).parent_path().empty()) ? + (boost::filesystem::path(input_file).parent_path() / model_object->name).make_preferred().string() : + model_object->name; + } + } if (! input_file.empty()) goto end; // Other instances will produce the same name, skip them. diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index ba109246a5..732cacaf08 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -607,8 +607,8 @@ public: static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); static void reset_auto_extruder_id(); - // Propose an output file name based on the first printable object's name. - std::string propose_export_file_name() const; + // Propose an output file name & path based on the first printable object's name and source input file's path. + std::string propose_export_file_name_and_path() const; private: MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model) diff --git a/src/libslic3r/PlaceholderParser.hpp b/src/libslic3r/PlaceholderParser.hpp index b5ed56fa1e..22c790e6b5 100644 --- a/src/libslic3r/PlaceholderParser.hpp +++ b/src/libslic3r/PlaceholderParser.hpp @@ -32,7 +32,8 @@ public: void set(const std::string &key, double value) { this->set(key, new ConfigOptionFloat(value)); } void set(const std::string &key, const std::vector &values) { this->set(key, new ConfigOptionStrings(values)); } void set(const std::string &key, ConfigOption *opt) { m_config.set_key_value(key, opt); } - const DynamicConfig& config() const { return m_config; } + DynamicConfig& config_writable() { return m_config; } + const DynamicConfig& config() const { return m_config; } const ConfigOption* option(const std::string &key) const { return m_config.option(key); } // Fill in the template using a macro processing language. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 42fda92fe5..bc692ca90d 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -421,8 +421,6 @@ void Print::add_model_object(ModelObject* model_object, int idx) src_normalized.normalize(); object->config_apply(src_normalized, true); } - - this->update_object_placeholders(); } bool Print::apply_config(DynamicPrintConfig config) @@ -1096,9 +1094,6 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co } } - //FIXME there may be a race condition with the G-code export running at the background thread. - this->update_object_placeholders(); - #ifdef _DEBUG check_model_ids_equal(m_model, model); #endif /* _DEBUG */ @@ -1855,6 +1850,9 @@ int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion std::max(region.config().perimeter_extruder.value - 1, 0); } +// Generate a recommended G-code output file name based on the format template, default extension, and template parameters +// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. +// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). std::string Print::output_filename() const { // Set the placeholders for the data know first after the G-code export is finished. diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 48e991b8da..3fe9a2b4d3 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -15,7 +15,7 @@ namespace Slic3r size_t PrintStateBase::g_last_timestamp = 0; // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. -void PrintBase::update_object_placeholders() +void PrintBase::update_object_placeholders(DynamicConfig &config) const { // get the first input file name std::string input_file; @@ -33,27 +33,29 @@ void PrintBase::update_object_placeholders() "% y:" + boost::lexical_cast(printable->get_scaling_factor(Y) * 100) + "% z:" + boost::lexical_cast(printable->get_scaling_factor(Z) * 100) + "%"); if (input_file.empty()) - input_file = model_object->input_file; + input_file = model_object->name.empty() ? model_object->input_file : model_object->name; } } - PlaceholderParser &pp = m_placeholder_parser; - pp.set("scale", v_scale); + config.set_key_value("year", new ConfigOptionStrings(v_scale)); if (! input_file.empty()) { // get basename with and without suffix const std::string input_basename = boost::filesystem::path(input_file).filename().string(); - pp.set("input_filename", input_basename); + config.set_key_value("input_filename", new ConfigOptionString(input_basename)); const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); - pp.set("input_filename_base", input_basename_base); + config.set_key_value("input_filename_base", new ConfigOptionString(input_basename_base)); } } +// Generate an output file name based on the format template, default extension, and template parameters +// (timestamps, object placeholders derived from the model, current placeholder prameters, print statistics - config_override) std::string PrintBase::output_filename(const std::string &format, const std::string &default_ext, const DynamicConfig *config_override) const { DynamicConfig cfg; if (config_override != nullptr) cfg = *config_override; PlaceholderParser::update_timestamp(cfg); + this->update_object_placeholders(cfg); try { boost::filesystem::path filename = this->placeholder_parser().process(format, 0, &cfg); if (filename.extension().empty()) @@ -69,7 +71,7 @@ std::string PrintBase::output_filepath(const std::string &path) const // if we were supplied no path, generate an automatic one based on our first object's input file if (path.empty()) // get the first input file name - return (boost::filesystem::path(m_model.propose_export_file_name()).parent_path() / this->output_filename()).make_preferred().string(); + return (boost::filesystem::path(m_model.propose_export_file_name_and_path()).parent_path() / this->output_filename()).make_preferred().string(); // if we were supplied a directory, use it and append our automatically generated filename boost::filesystem::path p(path); diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index 1a61921d60..84d04d26fe 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -309,7 +309,7 @@ protected: // To be called by this->output_filename() with the format string pulled from the configuration layer. std::string output_filename(const std::string &format, const std::string &default_ext, const DynamicConfig *config_override = nullptr) const; // Update "scale", "input_filename", "input_filename_base" placeholders from the current printable ModelObjects. - void update_object_placeholders(); + void update_object_placeholders(DynamicConfig &config) const; Model m_model; diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index dc55b196bb..3cc6895589 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -387,8 +387,6 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf update_apply_status(false); } - this->update_object_placeholders(); - #ifdef _DEBUG check_model_ids_equal(m_model, model); #endif /* _DEBUG */ diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 0b7dada70f..62b56f7ffa 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -252,10 +252,6 @@ int main(int argc, char **argv) model.arrange_objects(fff_print.config().min_object_distance()); model.center_instances_around_point(cli_config.print_center); } - if (outfile.empty()) { - outfile = model.propose_export_file_name(); - outfile += (printer_technology == ptFFF) ? ".gcode" : ".zip"; - } if (printer_technology == ptFFF) { for (auto* mo : model.objects) fff_print.auto_assign_extruders(mo); @@ -265,6 +261,7 @@ int main(int argc, char **argv) std::string err = print->validate(); if (err.empty()) { if (printer_technology == ptFFF) { + // The outfile is processed by a PlaceholderParser. fff_print.export_gcode(outfile, nullptr); } else { assert(printer_technology == ptSLA); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index edc0fecd67..2fe032f9eb 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1028,7 +1028,8 @@ struct Plater::priv unsigned int update_background_process(bool force_validation = false); // 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); + // returns bit mask of UpdateBackgroundProcessReturnState + unsigned int update_restart_background_process(bool force_scene_update, bool force_preview_update); void export_gcode(fs::path output_path, PrintHostJob upload_job); void reload_from_disk(); void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); @@ -1575,7 +1576,7 @@ std::unique_ptr Plater::priv::get_export_file(GUI::FileType // Update printbility state of each of the ModelInstances. this->update_print_volume_state(); // Find the file name of the first printable object. - fs::path output_file = this->model.propose_export_file_name(); + fs::path output_file = this->model.propose_export_file_name_and_path(); switch (file_type) { case FT_STL: output_file.replace_extension("stl"); break; @@ -2045,7 +2046,7 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) this->restart_background_process(priv::UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT); } -void Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview) +unsigned int Plater::priv::update_restart_background_process(bool force_update_scene, bool force_update_preview) { // bitmask of UpdateBackgroundProcessReturnState unsigned int state = this->update_background_process(false); @@ -2055,6 +2056,7 @@ void Plater::priv::update_restart_background_process(bool force_update_scene, bo if (force_update_preview) this->preview->reload_print(); this->restart_background_process(state); + return state; } void Plater::priv::update_fff_scene() @@ -2846,51 +2848,43 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe p->load_model_objects(new_objects); } -void Plater::export_gcode(fs::path output_path) +void Plater::export_gcode() { if (p->model.objects.empty()) return; - // select output file - if (output_path.empty()) { - // XXX: take output path from CLI opts? Ancient Slic3r versions used to do that... - - // If possible, remove accents from accented latin characters. - // This function is useful for generating file names to be processed by legacy firmwares. - fs::path default_output_file; - try { - default_output_file = this->p->background_process.current_print()->output_filepath(output_path.string()); - } catch (const std::exception &ex) { - show_error(this, ex.what()); - return; - } - default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); - auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - - wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), - start_dir, - 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 = into_path(dlg.GetPath()); - wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); - output_path = std::move(path); - } - } else { - try { - output_path = this->p->background_process.current_print()->output_filepath(output_path.string()); - } catch (const std::exception &ex) { - show_error(this, ex.what()); - return; - } + // If possible, remove accents from accented latin characters. + // This function is useful for generating file names to be processed by legacy firmwares. + fs::path default_output_file; + try { + // Update the background processing, so that the placeholder parser will get the correct values for the ouput file template. + // Also if there is something wrong with the current configuration, a pop-up dialog will be shown and the export will not be performed. + unsigned int state = this->p->update_restart_background_process(false, false); + if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) + return; + default_output_file = this->p->background_process.current_print()->output_filepath(""); + } catch (const std::exception &ex) { + show_error(this, ex.what()); + return; } + default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string())); + auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string()); - if (! output_path.empty()) { + wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save Zip file as:")), + start_dir, + 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 + ); + + fs::path output_path; + if (dlg.ShowModal() == wxID_OK) { + fs::path path = into_path(dlg.GetPath()); + wxGetApp().app_config->update_last_output_dir(path.parent_path().string()); + output_path = std::move(path); + } + if (! output_path.empty()) p->export_gcode(std::move(output_path), PrintHostJob()); - } } void Plater::export_stl(bool selection_only) @@ -2991,7 +2985,12 @@ void Plater::send_gcode() // Obtain default output path fs::path default_output_file; try { - default_output_file = this->p->background_process.current_print()->output_filepath(""); + // Update the background processing, so that the placeholder parser will get the correct values for the ouput file template. + // Also if there is something wrong with the current configuration, a pop-up dialog will be shown and the export will not be performed. + unsigned int state = this->p->update_restart_background_process(false, false); + if (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) + return; + default_output_file = this->p->background_process.current_print()->output_filepath(""); } catch (const std::exception &ex) { show_error(this, ex.what()); return; diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 09b7348d5d..e3601b65c3 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -140,8 +140,7 @@ public: void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false); - // Note: empty path means "use the default" - void export_gcode(boost::filesystem::path output_path = boost::filesystem::path()); + void export_gcode(); void export_stl(bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); From f050d912395523162d1234a61158d58558635f2f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 11:10:25 +0100 Subject: [PATCH 26/37] OSX specific: The Command short keys over the 3D scene toolbars are now shown with the OSX "Command" symbols, not as "Ctrl+" --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++--- src/slic3r/GUI/GUI.cpp | 24 ++++++++++++++++++++++++ src/slic3r/GUI/GUI.hpp | 6 +++++- src/slic3r/GUI/KBShortcutsDialog.cpp | 9 ++------- src/slic3r/GUI/Plater.cpp | 4 ++-- 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e09ecd2bf4..bfcf796c7e 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -6073,7 +6073,7 @@ bool GLCanvas3D::_init_toolbar() GLToolbarItem::Data item; item.name = "add"; - item.tooltip = GUI::L_str("Add... [Ctrl+I]"); + item.tooltip = GUI::L_str("Add...") + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_ADD; @@ -6081,7 +6081,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "delete"; - item.tooltip = GUI::L_str("Delete [Del]"); + item.tooltip = GUI::L_str("Delete") + " [Del]"; item.sprite_id = 1; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_DELETE; @@ -6089,7 +6089,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "deleteall"; - item.tooltip = GUI::L_str("Delete all [Ctrl+Del]"); + item.tooltip = GUI::L_str("Delete all") + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; item.sprite_id = 2; item.is_toggable = false; item.action_event = EVT_GLTOOLBAR_DELETE_ALL; diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 148285e86e..8e80d64c30 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -75,6 +75,30 @@ void break_to_debugger() #endif /* _WIN32 */ } +const std::string& shortkey_ctrl_prefix() +{ + static const std::string str = +#ifdef __APPLE__ + "⌘" +#else + "Ctrl+" +#endif + ; + return str; +} + +const std::string& shortkey_alt_prefix() +{ + static const std::string str = +#ifdef __APPLE__ + "⌥" +#else + "Alt+" +#endif + ; + return str; +} + bool config_wizard_startup(bool app_config_exists) { if (!app_config_exists || wxGetApp().preset_bundle->printers.size() <= 1) { diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index e33be8f58c..f066c82a80 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -27,7 +27,11 @@ void enable_screensaver(); bool debugged(); void break_to_debugger(); -AppConfig* get_app_config(); +// Platform specific Ctrl+/Alt+ (Windows, Linux) vs. ⌘/⌥ (OSX) prefixes +extern const std::string& shortkey_ctrl_prefix(); +extern const std::string& shortkey_alt_prefix(); + +extern AppConfig* get_app_config(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index 3aeda03482..d893d6f765 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -88,13 +88,8 @@ KBShortcutsDialog::KBShortcutsDialog() void KBShortcutsDialog::fill_shortcuts() { -#ifdef __WXOSX__ - const std::string ctrl = "⌘"; - const std::string alt = "⌥"; -#else - const std::string ctrl = "Ctrl+"; - const std::string alt = "Alt+"; -#endif // __WXOSX__ + const std::string &ctrl = GUI::shortkey_ctrl_prefix(); + const std::string &alt = GUI::shortkey_alt_prefix(); m_full_shortcuts.reserve(4); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 2fe032f9eb..ef8e84ca8d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2555,7 +2555,7 @@ void Plater::priv::init_view_toolbar() GLToolbarItem::Data item; item.name = "3D"; - item.tooltip = GUI::L_str("3D editor view [Ctrl+5]"); + item.tooltip = GUI::L_str("3D editor view") + " [" + GUI::shortkey_ctrl_prefix() + "5]"; item.sprite_id = 0; item.action_event = EVT_GLVIEWTOOLBAR_3D; item.is_toggable = false; @@ -2563,7 +2563,7 @@ void Plater::priv::init_view_toolbar() return; item.name = "Preview"; - item.tooltip = GUI::L_str("Preview [Ctrl+6]"); + item.tooltip = GUI::L_str("Preview") + " [" + GUI::shortkey_ctrl_prefix() + "6]"; item.sprite_id = 1; item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; item.is_toggable = false; From 5deb8fcc6582f3b19e7eb0b3cf17116aeee289db Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 11:15:26 +0100 Subject: [PATCH 27/37] Suppressed the "Split to volumes" button in simple mode. --- src/slic3r/GUI/GLCanvas3D.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index bfcf796c7e..0e474670c8 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4428,6 +4428,7 @@ void GLCanvas3D::update_toolbar_items_visibility() ConfigOptionMode mode = wxGetApp().get_mode(); m_toolbar.set_item_visible("more", mode != comSimple); m_toolbar.set_item_visible("fewer", mode != comSimple); + m_toolbar.set_item_visible("splitvolumes", mode != comSimple); m_dirty = true; } #endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS From ecdf550e65a6d40ec6499fb1002b9a4b90972dd0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 12:12:26 +0100 Subject: [PATCH 28/37] OSX specific: Changed the "Preferences dialog" short cut to the platform default "Control-," fixes #1748 --- 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 9991d98ea4..056062cf4e 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -561,7 +561,13 @@ void GUI_App::add_config_menu(wxMenuBar *menu) local_menu->Append(config_id_base + ConfigMenuTakeSnapshot, _(L("Take Configuration &Snapshot")), _(L("Capture a configuration snapshot"))); // local_menu->Append(config_id_base + ConfigMenuUpdate, _(L("Check for updates")), _(L("Check for configuration updates"))); local_menu->AppendSeparator(); - local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("&Preferences")) + dots + "\tCtrl+P", _(L("Application preferences"))); + local_menu->Append(config_id_base + ConfigMenuPreferences, _(L("&Preferences")) + dots + +#ifdef __APPLE__ + "\tCtrl+,", +#else + "\tCtrl+P", +#endif + _(L("Application preferences"))); local_menu->AppendSeparator(); auto mode_menu = new wxMenu(); mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _(L("Simple")), _(L("Simple View Mode"))); From 0c1f750cba3686ab0db19779acb8782ed047de31 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 14:06:13 +0100 Subject: [PATCH 29/37] The accelerators Ctrl+A, Ctrl+Del and Del were incorrectly captured globally by being defined in the Edit menu. These accelerators are now suppressed in the menu (shown on Windows but inactive, not shown on OSX / Linux), and they are now captured by the 3D scene widget instead. Fix of ctrl-A doesn't work well #1753 --- src/slic3r/GUI/GLCanvas3D.cpp | 110 ++++++++++++++++------------------ src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/MainFrame.cpp | 10 ++-- src/slic3r/GUI/Plater.cpp | 3 + 4 files changed, 60 insertions(+), 64 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 0e474670c8..f524033218 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3995,6 +3995,7 @@ wxDEFINE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDEFINE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_SELECT_ALL, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_QUESTION_MARK, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_INCREASE_INSTANCES, Event); wxDEFINE_EVENT(EVT_GLCANVAS_INSTANCE_MOVED, SimpleEvent); @@ -5100,71 +5101,60 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) void GLCanvas3D::on_char(wxKeyEvent& evt) { - if (evt.HasModifiers()) + // see include/wx/defs.h enum wxKeyCode + int keyCode = evt.GetKeyCode(); + if (evt.GetModifiers() == wxMOD_CONTROL) { + switch (keyCode) { + case WXK_CONTROL_A: post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); break; +#ifdef __APPLE__ + case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. +#endif /* __APPLE__ */ + case WXK_DELETE: post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); break; + default: evt.Skip(); + } + } else if (evt.HasModifiers()) { evt.Skip(); - else - { - int keyCode = evt.GetKeyCode(); - switch (keyCode - 48) + } else { + switch (keyCode) { - // numerical input - case 0: { select_view("iso"); break; } - case 1: { select_view("top"); break; } - case 2: { select_view("bottom"); break; } - case 3: { select_view("front"); break; } - case 4: { select_view("rear"); break; } - case 5: { select_view("left"); break; } - case 6: { select_view("right"); break; } + // key ESC + case WXK_ESCAPE: { m_gizmos.reset_all_states(); m_dirty = true; break; } +#ifdef __APPLE__ + case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. +#endif /* __APPLE__ */ + case WXK_DELETE: post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break; + case '0': { select_view("iso"); break; } + case '1': { select_view("top"); break; } + case '2': { select_view("bottom"); break; } + case '3': { select_view("front"); break; } + case '4': { select_view("rear"); break; } + case '5': { select_view("left"); break; } + case '6': { select_view("right"); break; } + case '+': { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, +1)); break; } + case '-': { post_event(Event(EVT_GLCANVAS_INCREASE_INSTANCES, -1)); break; } + case '?': { post_event(SimpleEvent(EVT_GLCANVAS_QUESTION_MARK)); break; } + case 'A': + case 'a': { post_event(SimpleEvent(EVT_GLCANVAS_ARRANGE)); break; } + case 'B': + case 'b': { zoom_to_bed(); break; } + case 'I': + case 'i': { set_camera_zoom(1.0f); break; } + case 'O': + case 'o': { set_camera_zoom(-1.0f); break; } + case 'Z': + case 'z': { m_selection.is_empty() ? zoom_to_volumes() : zoom_to_selection(); break; } default: + { + if (m_gizmos.handle_shortcut(keyCode, m_selection)) { - // text input - switch (keyCode) - { - // key ESC - case 27: { m_gizmos.reset_all_states(); m_dirty = true; break; } - // key + - 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; } - // key B/b - case 66: - case 98: { zoom_to_bed(); break; } - // key I/i - case 73: - case 105: { set_camera_zoom(1.0f); break; } - // key O/o - case 79: - case 111: { set_camera_zoom(-1.0f); break; } - // key Z/z - case 90: - case 122: - { - if (m_selection.is_empty()) - zoom_to_volumes(); - else - zoom_to_selection(); - - break; - } - default: - { - if (m_gizmos.handle_shortcut(keyCode, m_selection)) - { - _update_gizmos_data(); - m_dirty = true; - } - else - evt.Skip(); - - break; - } - } + _update_gizmos_data(); + m_dirty = true; } + else + evt.Skip(); + + break; + } } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index ec980dd926..21ae80606a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -121,6 +121,7 @@ wxDECLARE_EVENT(EVT_GLCANVAS_VIEWPORT_CHANGED, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, Vec2dEvent); wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_ARRANGE, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_SELECT_ALL, 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); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 947dd59bdf..4ff01c0d34 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -324,12 +324,14 @@ void MainFrame::init_menubar() if (m_plater != nullptr) { editMenu = new wxMenu(); - wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + "\tCtrl+A", _(L("Selects all objects")), + // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, + // as the simple numeric accelerators spoil all numeric data entry. + wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + "\t\xA0" + "Ctrl+\xA0" + "A", _(L("Selects all objects")), [this](wxCommandEvent&) { m_plater->select_all(); }, ""); editMenu->AppendSeparator(); - wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + "\tDel", _(L("Deletes the current selection")), + wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + "\t\xA0" + "Del", _(L("Deletes the current selection")), [this](wxCommandEvent&) { m_plater->remove_selected(); }, ""); - wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + "\tCtrl+Del", _(L("Deletes all objects")), + wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + "\t\xA0" + "Ctrl+\xA0" + "Del", _(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset(); }, ""); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId()); @@ -388,7 +390,7 @@ void MainFrame::init_menubar() wxMenu* viewMenu = nullptr; if (m_plater) { viewMenu = new wxMenu(); - // \xA0 is a non-breaing space. It is entered here to spoil the automatic accelerators, + // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, // as the simple numeric accelerators spoil all numeric data entry. // The camera control accelerators are captured by GLCanvas3D::on_char(). wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("&Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); }); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ef8e84ca8d..59148c0a36 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1171,6 +1171,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_RIGHT_CLICK, &priv::on_right_click, this); 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_SELECT_ALL, [this](SimpleEvent&) { this->q->select_all(); }); 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(); }); @@ -1736,6 +1737,8 @@ void Plater::priv::arrange() // Guard the arrange process arranging.store(true); + wxBusyCursor wait; + // Disable the arrange button (to prevent reentrancies, we will call wxYied) view3D->enable_toolbar_item("arrange", can_arrange()); From 14a623f50ee7051045a999d912bd6290d18fc640 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 14:11:09 +0100 Subject: [PATCH 30/37] Removed GLCanvas3D::on_key_down() handler, as it is replaced by the on_char() handler. --- src/slic3r/GUI/GLCanvas3D.cpp | 22 +--------------------- src/slic3r/GUI/GLCanvas3D.hpp | 1 - 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index f524033218..610a5008c7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5056,7 +5056,6 @@ void GLCanvas3D::bind_event_handlers() m_canvas->Bind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Bind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); - m_canvas->Bind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this); } } @@ -5070,7 +5069,7 @@ void GLCanvas3D::unbind_event_handlers() m_canvas->Unbind(wxEVT_MOUSEWHEEL, &GLCanvas3D::on_mouse_wheel, this); m_canvas->Unbind(wxEVT_TIMER, &GLCanvas3D::on_timer, this); m_canvas->Unbind(wxEVT_LEFT_DOWN, &GLCanvas3D::on_mouse, this); - m_canvas->Unbind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this); + m_canvas->Unbind(wxEVT_LEFT_UP, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_MIDDLE_DOWN, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_MIDDLE_UP, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_RIGHT_DOWN, &GLCanvas3D::on_mouse, this); @@ -5082,7 +5081,6 @@ void GLCanvas3D::unbind_event_handlers() m_canvas->Unbind(wxEVT_MIDDLE_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_RIGHT_DCLICK, &GLCanvas3D::on_mouse, this); m_canvas->Unbind(wxEVT_PAINT, &GLCanvas3D::on_paint, this); - m_canvas->Unbind(wxEVT_KEY_DOWN, &GLCanvas3D::on_key_down, this); } } @@ -5669,24 +5667,6 @@ void GLCanvas3D::on_paint(wxPaintEvent& evt) this->render(); } -void GLCanvas3D::on_key_down(wxKeyEvent& evt) -{ - if (evt.HasModifiers()) - evt.Skip(); - else - { - int key = evt.GetKeyCode(); -#ifdef __WXOSX__ - if (key == WXK_BACK) -#else - if (key == WXK_DELETE) -#endif // __WXOSX__ - post_event(SimpleEvent(EVT_GLCANVAS_REMOVE_OBJECT)); - else - evt.Skip(); - } -} - Size GLCanvas3D::get_canvas_size() const { int w = 0; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 21ae80606a..351a3d9497 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -1043,7 +1043,6 @@ public: void on_timer(wxTimerEvent& evt); void on_mouse(wxMouseEvent& evt); void on_paint(wxPaintEvent& evt); - void on_key_down(wxKeyEvent& evt); Size get_canvas_size() const; Point get_local_mouse_position() const; From f9743d17e9b700e5d6413de2d0eb497fc283c4f4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 15:30:37 +0100 Subject: [PATCH 31/37] On Windows, system and hidden files are now ignored in all file enumeration loops. Should fix "desktop.ini still displaying error" #1761 --- src/libslic3r/Utils.hpp | 8 +++++++ src/libslic3r/utils.cpp | 32 ++++++++++++++++++++++++- src/slic3r/Config/Snapshot.cpp | 6 ++--- src/slic3r/Config/Version.cpp | 2 +- src/slic3r/GUI/ConfigWizard.cpp | 38 ++++++++++++++---------------- src/slic3r/GUI/Preset.cpp | 15 +----------- src/slic3r/GUI/PresetBundle.cpp | 2 +- src/slic3r/Utils/PresetUpdater.cpp | 16 ++++++------- 8 files changed, 70 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index ed12d05594..046745e6f5 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -8,6 +8,8 @@ #include "libslic3r.h" +namespace boost { namespace filesystem { class directory_entry; }} + namespace Slic3r { extern void set_logging_level(unsigned int level); @@ -61,6 +63,12 @@ extern int rename_file(const std::string &from, const std::string &to); // Copy a file, adjust the access attributes, so that the target is writable. extern int copy_file(const std::string &from, const std::string &to); +// Ignore system and hidden files, which may be created by the DropBox synchronisation process. +// https://github.com/prusa3d/Slic3r/issues/1298 +extern bool is_plain_file(const boost::filesystem::directory_entry &path); +extern bool is_ini_file(const boost::filesystem::directory_entry &path); +extern bool is_idx_file(const boost::filesystem::directory_entry &path); + // File path / name / extension splitting utilities, working with UTF-8, // to be published to Perl. namespace PerlUtils { diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index f48abfd89e..6727bb799a 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -30,7 +30,13 @@ #include -#include +#if defined(__linux) || defined(__GNUC__ ) +#include +#endif /* __linux */ + +#ifdef _MSC_VER + #define strcasecmp _stricmp +#endif namespace Slic3r { @@ -248,6 +254,30 @@ int copy_file(const std::string &from, const std::string &to) return 0; } +// Ignore system and hidden files, which may be created by the DropBox synchronisation process. +// https://github.com/prusa3d/Slic3r/issues/1298 +bool is_plain_file(const boost::filesystem::directory_entry &dir_entry) +{ + if (! boost::filesystem::is_regular_file(dir_entry.status())) + return false; +#ifdef _MSC_VER + DWORD attributes = GetFileAttributesW(boost::nowide::widen(dir_entry.path().string()).c_str()); + return (attributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0; +#else + return true; +#endif +} + +bool is_ini_file(const boost::filesystem::directory_entry &dir_entry) +{ + return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".ini") == 0; +} + +bool is_idx_file(const boost::filesystem::directory_entry &dir_entry) +{ + return is_plain_file(dir_entry) && strcasecmp(dir_entry.path().extension().string().c_str(), ".idx") == 0; +} + } // namespace Slic3r #ifdef WIN32 diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 35bfde0b6b..b208554b50 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -249,10 +249,10 @@ bool Snapshot::equal_to_active(const AppConfig &app_config) const boost::filesystem::path path2 = snapshot_dir / subdir; std::vector files1, files2; for (auto &dir_entry : boost::filesystem::directory_iterator(path1)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) + if (Slic3r::is_ini_file(dir_entry)) files1.emplace_back(dir_entry.path().filename().string()); for (auto &dir_entry : boost::filesystem::directory_iterator(path2)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) + if (Slic3r::is_ini_file(dir_entry)) files2.emplace_back(dir_entry.path().filename().string()); std::sort(files1.begin(), files1.end()); std::sort(files2.begin(), files2.end()); @@ -343,7 +343,7 @@ static void copy_config_dir_single_level(const boost::filesystem::path &path_src throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string()); for (auto &dir_entry : boost::filesystem::directory_iterator(path_src)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) + if (Slic3r::is_ini_file(dir_entry)) boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists); } diff --git a/src/slic3r/Config/Version.cpp b/src/slic3r/Config/Version.cpp index 48ace7b609..70b12f23b6 100644 --- a/src/slic3r/Config/Version.cpp +++ b/src/slic3r/Config/Version.cpp @@ -297,7 +297,7 @@ std::vector Index::load_db() std::vector index_db; std::string errors_cummulative; for (auto &dir_entry : boost::filesystem::directory_iterator(cache_dir)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".idx")) { + if (Slic3r::is_idx_file(dir_entry)) { Index idx; try { idx.load(dir_entry.path()); diff --git a/src/slic3r/GUI/ConfigWizard.cpp b/src/slic3r/GUI/ConfigWizard.cpp index 6cf45166db..3471395c29 100644 --- a/src/slic3r/GUI/ConfigWizard.cpp +++ b/src/slic3r/GUI/ConfigWizard.cpp @@ -635,35 +635,32 @@ void ConfigWizard::priv::load_vendors() const auto rsrc_vendor_dir = fs::path(resources_dir()) / "profiles"; // Load vendors from the "vendors" directory in datadir - for (fs::directory_iterator it(vendor_dir); it != fs::directory_iterator(); ++it) { - if (it->path().extension() == ".ini") { + for (auto &dir_entry : boost::filesystem::directory_iterator(vendor_dir)) + if (Slic3r::is_ini_file(dir_entry)) { try { - auto vp = VendorProfile::from_ini(it->path()); + auto vp = VendorProfile::from_ini(dir_entry.path()); vendors[vp.id] = std::move(vp); } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % it->path() % e.what(); + BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); } - } - } // Additionally load up vendors from the application resources directory, but only those not seen in the datadir - for (fs::directory_iterator it(rsrc_vendor_dir); it != fs::directory_iterator(); ++it) { - if (it->path().extension() == ".ini") { - const auto id = it->path().stem().string(); + for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_vendor_dir)) + if (Slic3r::is_ini_file(dir_entry)) { + const auto id = dir_entry.path().stem().string(); if (vendors.find(id) == vendors.end()) { try { - auto vp = VendorProfile::from_ini(it->path()); - vendors_rsrc[vp.id] = it->path().filename().string(); + auto vp = VendorProfile::from_ini(dir_entry.path()); + vendors_rsrc[vp.id] = dir_entry.path().filename().string(); vendors[vp.id] = std::move(vp); } catch (const std::exception& e) { - BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % it->path() % e.what(); + BOOST_LOG_TRIVIAL(error) << boost::format("Error loading vendor bundle %1%: %2%") % dir_entry.path() % e.what(); } } } - } // Load up the set of vendors / models / variants the user has had enabled up till now const AppConfig *app_config = GUI::get_app_config(); @@ -672,14 +669,15 @@ void ConfigWizard::priv::load_vendors() } else { // In case of legacy datadir, try to guess the preference based on the printer preset files that are present const auto printer_dir = fs::path(Slic3r::data_dir()) / "printer"; - for (fs::directory_iterator it(printer_dir); it != fs::directory_iterator(); ++it) { - auto needle = legacy_preset_map.find(it->path().filename().string()); - if (needle == legacy_preset_map.end()) { continue; } + for (auto &dir_entry : boost::filesystem::directory_iterator(printer_dir)) + if (Slic3r::is_ini_file(dir_entry)) { + auto needle = legacy_preset_map.find(dir_entry.path().filename().string()); + if (needle == legacy_preset_map.end()) { continue; } - const auto &model = needle->second.first; - const auto &variant = needle->second.second; - appconfig_vendors.set_variant("PrusaResearch", model, variant, true); - } + const auto &model = needle->second.first; + const auto &variant = needle->second.second; + appconfig_vendors.set_variant("PrusaResearch", model, variant, true); + } } } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 53650481c3..66cc5ca4c5 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -517,16 +517,6 @@ void PresetCollection::add_default_preset(const std::vector &keys, ++ m_num_default_presets; } -bool is_file_plain(const std::string &path) -{ -#ifdef _MSC_VER - DWORD attributes = GetFileAttributesW(boost::nowide::widen(path).c_str()); - return (attributes & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) == 0; -#else - return true; -#endif -} - // Load all presets found in dir_path. // Throws an exception on error. void PresetCollection::load_presets(const std::string &dir_path, const std::string &subdir) @@ -538,10 +528,7 @@ void PresetCollection::load_presets(const std::string &dir_path, const std::stri // (see the "Preset already present, not loading" message). std::deque presets_loaded; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini") && - // Ignore system and hidden files, which may be created by the DropBox synchronisation process. - // https://github.com/prusa3d/Slic3r/issues/1298 - is_file_plain(dir_entry.path().string())) { + if (Slic3r::is_ini_file(dir_entry)) { std::string name = dir_entry.path().filename().string(); // Remove the .ini suffix. name.erase(name.size() - 4); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 4c6c52763c..183c912348 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -243,7 +243,7 @@ std::string PresetBundle::load_system_presets() std::string errors_cummulative; bool first = true; for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) - if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini")) { + if (Slic3r::is_ini_file(dir_entry)) { std::string name = dir_entry.path().filename().string(); // Remove the .ini suffix. name.erase(name.size() - 4); diff --git a/src/slic3r/Utils/PresetUpdater.cpp b/src/slic3r/Utils/PresetUpdater.cpp index bfa3af3e69..a22d463fb7 100644 --- a/src/slic3r/Utils/PresetUpdater.cpp +++ b/src/slic3r/Utils/PresetUpdater.cpp @@ -180,12 +180,11 @@ bool PresetUpdater::priv::get_file(const std::string &url, const fs::path &targe // Remove leftover paritally downloaded files, if any. void PresetUpdater::priv::prune_tmps() const { - for (fs::directory_iterator it(cache_path); it != fs::directory_iterator(); ++it) { - if (it->path().extension() == TMP_EXTENSION) { - BOOST_LOG_TRIVIAL(debug) << "Cache prune: " << it->path().string(); - fs::remove(it->path()); + for (auto &dir_entry : boost::filesystem::directory_iterator(cache_path)) + if (is_plain_file(dir_entry) && dir_entry.path().extension() == TMP_EXTENSION) { + BOOST_LOG_TRIVIAL(debug) << "Cache prune: " << dir_entry.path().string(); + fs::remove(dir_entry.path()); } - } } // Get Slic3rPE version available online, save in AppConfig. @@ -299,9 +298,9 @@ void PresetUpdater::priv::check_install_indices() const { BOOST_LOG_TRIVIAL(info) << "Checking if indices need to be installed from resources..."; - for (fs::directory_iterator it(rsrc_path); it != fs::directory_iterator(); ++it) { - const auto &path = it->path(); - if (path.extension() == ".idx") { + for (auto &dir_entry : boost::filesystem::directory_iterator(rsrc_path)) + if (is_idx_file(dir_entry)) { + const auto &path = dir_entry.path(); const auto path_in_cache = cache_path / path.filename(); if (! fs::exists(path_in_cache)) { @@ -318,7 +317,6 @@ void PresetUpdater::priv::check_install_indices() const } } } - } } // Generates a list of bundle updates that are to be performed From d0b1b3b3dec04b529c11ac60a239b8aacb6ce8bb Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 17:57:55 +0100 Subject: [PATCH 32/37] Trying to convince OSX that we want the Control key to behave as Command key when accessing OSX machine over VNC from a PC. --- src/slic3r/GUI/GLCanvas3D.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 610a5008c7..cca4b893f1 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5101,7 +5101,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) { // see include/wx/defs.h enum wxKeyCode int keyCode = evt.GetKeyCode(); - if (evt.GetModifiers() == wxMOD_CONTROL) { + int ctrlMask = wxMOD_CONTROL; +#ifdef __APPLE__ + ctrlMask |= wxMOD_RAW_CONTROL; +#endif /* __APPLE__ */ + if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { case WXK_CONTROL_A: post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); break; #ifdef __APPLE__ From 1905d49ade517277b7b75200b1d7afaa9b686e6a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 19:10:20 +0100 Subject: [PATCH 33/37] Trying to find a reasonable workaround for the single key menu accelerators. --- src/slic3r/GUI/MainFrame.cpp | 40 ++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 4ff01c0d34..78dffa1cba 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -326,12 +326,22 @@ void MainFrame::init_menubar() editMenu = new wxMenu(); // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, // as the simple numeric accelerators spoil all numeric data entry. - wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + "\t\xA0" + "Ctrl+\xA0" + "A", _(L("Selects all objects")), + wxMenuItem* item_select_all = append_menu_item(editMenu, wxID_ANY, _(L("&Select all")) + +#ifdef _MSC_VER + "\t\xA0" + "Ctrl+\xA0" + "A" +#else +#ifdef __APPLE__ + "\tCtrl+A" +#else + " - Ctrl+A" +#endif +#endif + , _(L("Selects all objects")), [this](wxCommandEvent&) { m_plater->select_all(); }, ""); editMenu->AppendSeparator(); - wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + "\t\xA0" + "Del", _(L("Deletes the current selection")), + wxMenuItem* item_delete_sel = append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + "\tDel", _(L("Deletes the current selection")), [this](wxCommandEvent&) { m_plater->remove_selected(); }, ""); - wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + "\t\xA0" + "Ctrl+\xA0" + "Del", _(L("Deletes all objects")), + wxMenuItem* item_delete_all = append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + "\tCtrl+Del", _(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset(); }, ""); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_select()); }, item_select_all->GetId()); @@ -388,19 +398,23 @@ void MainFrame::init_menubar() // View menu wxMenu* viewMenu = nullptr; + wxString sep = +#ifdef _MSC_VER + "\t"; +#else + " - "; +#endif if (m_plater) { viewMenu = new wxMenu(); - // \xA0 is a non-breaking space. It is entered here to spoil the automatic accelerators, - // as the simple numeric accelerators spoil all numeric data entry. // The camera control accelerators are captured by GLCanvas3D::on_char(). - wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("&Iso")) + "\t\xA0" + "0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); }); + wxMenuItem* item_iso = append_menu_item(viewMenu, wxID_ANY, _(L("Iso")) + sep + "&0", _(L("Iso View")), [this](wxCommandEvent&) { select_view("iso"); }); viewMenu->AppendSeparator(); - wxMenuItem* item_top = append_menu_item(viewMenu, wxID_ANY, _(L("&Top")) + "\t\xA0" + "1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); }); - wxMenuItem* item_bottom = append_menu_item(viewMenu, wxID_ANY, _(L("&Bottom")) + "\t\xA0" + "2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); }); - wxMenuItem* item_front = append_menu_item(viewMenu, wxID_ANY, _(L("&Front")) + "\t\xA0" + "3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); }); - wxMenuItem* item_rear = append_menu_item(viewMenu, wxID_ANY, _(L("R&ear")) + "\t\xA0" + "4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); }); - wxMenuItem* item_left = append_menu_item(viewMenu, wxID_ANY, _(L("&Left")) + "\t\xA0" + "5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); }); - wxMenuItem* item_right = append_menu_item(viewMenu, wxID_ANY, _(L("&Right")) + "\t\xA0" + "6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); }); + wxMenuItem* item_top = append_menu_item(viewMenu, wxID_ANY, _(L("Top")) + sep + "&1", _(L("Top View")), [this](wxCommandEvent&) { select_view("top"); }); + wxMenuItem* item_bottom = append_menu_item(viewMenu, wxID_ANY, _(L("Bottom")) + sep + "&2", _(L("Bottom View")), [this](wxCommandEvent&) { select_view("bottom"); }); + wxMenuItem* item_front = append_menu_item(viewMenu, wxID_ANY, _(L("Front")) + sep + "&3", _(L("Front View")), [this](wxCommandEvent&) { select_view("front"); }); + wxMenuItem* item_rear = append_menu_item(viewMenu, wxID_ANY, _(L("Rear")) + sep + "&4", _(L("Rear View")), [this](wxCommandEvent&) { select_view("rear"); }); + wxMenuItem* item_left = append_menu_item(viewMenu, wxID_ANY, _(L("Left")) + sep + "&5", _(L("Left View")), [this](wxCommandEvent&) { select_view("left"); }); + wxMenuItem* item_right = append_menu_item(viewMenu, wxID_ANY, _(L("Right")) + sep + "&6", _(L("Right View")), [this](wxCommandEvent&) { select_view("right"); }); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_iso->GetId()); Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(can_change_view()); }, item_top->GetId()); @@ -436,7 +450,7 @@ void MainFrame::init_menubar() 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\xA0?", _(L("Show the list of the keyboard shortcuts")), + append_menu_item(helpMenu, wxID_ANY, _(L("Keyboard Shortcuts")) + sep + "&?", _(L("Show the list of the keyboard shortcuts")), [this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); }); } From bcab373a544b956071ba8185ce5aaea6beb97930 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 20:17:55 +0100 Subject: [PATCH 34/37] Fixed an error when importing / exporting Config Bundles with at least one SLA print or SLA material defined. --- src/slic3r/GUI/Preset.cpp | 12 ++++++++++++ src/slic3r/GUI/Preset.hpp | 3 +++ src/slic3r/GUI/PresetBundle.cpp | 4 ++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 66cc5ca4c5..871cd0b736 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -1156,6 +1156,18 @@ std::string PresetCollection::name() const } } +std::string PresetCollection::section_name() const +{ + switch (this->type()) { + case Preset::TYPE_PRINT: return "print"; + case Preset::TYPE_FILAMENT: return "filament"; + case Preset::TYPE_SLA_PRINT: return "sla_print"; + case Preset::TYPE_SLA_MATERIAL: return "sla_material"; + case Preset::TYPE_PRINTER: return "printer"; + default: return "invalid"; + } +} + std::vector PresetCollection::system_preset_names() const { size_t num = 0; diff --git a/src/slic3r/GUI/Preset.hpp b/src/slic3r/GUI/Preset.hpp index eae406bef2..73a921cf74 100644 --- a/src/slic3r/GUI/Preset.hpp +++ b/src/slic3r/GUI/Preset.hpp @@ -230,7 +230,10 @@ public: void reset(bool delete_files); Preset::Type type() const { return m_type; } + // Name, to be used on the screen and in error messages. Not localized. std::string name() const; + // Name, to be used as a section name in config bundle, and as a folder name for presets. + std::string section_name() const; const std::deque& operator()() const { return m_presets; } // Add default preset at the start of the collection, increment the m_default_preset counter. diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 183c912348..5f33fd00ab 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -1208,7 +1208,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla #else // Store the print/filament/printer presets at the same location as the upstream Slic3r. #endif - / presets->name() / file_name).make_preferred(); + / presets->section_name() / file_name).make_preferred(); // Load the preset into the list of presets, save it to disk. Preset &loaded = presets->load_preset(file_path.string(), preset_name, std::move(config), false); if (flags & LOAD_CFGBNDLE_SAVE) @@ -1365,7 +1365,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst if (preset.is_default || preset.is_external || (preset.is_system && ! export_system_settings)) // Only export the common presets, not external files or the default preset. continue; - c << std::endl << "[" << presets->name() << ":" << preset.name << "]" << std::endl; + c << std::endl << "[" << presets->section_name() << ":" << preset.name << "]" << std::endl; for (const std::string &opt_key : preset.config.keys()) c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; } From cd838561eee28a2733a9b6b61f9ba41ef36ccd23 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 3 Feb 2019 22:14:34 +0100 Subject: [PATCH 35/37] Model fixing through Netfabb service (Windows only): Model volumes are now fixed one by one, instances & parameters are maintained, it is now possible to fix just a single volume of a multi-part object. --- src/libslic3r/Model.hpp | 2 + src/slic3r/GUI/Plater.cpp | 29 ++------- src/slic3r/Utils/FixModelByWin10.cpp | 89 ++++++++++++++++++---------- src/slic3r/Utils/FixModelByWin10.hpp | 4 +- 4 files changed, 66 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 732cacaf08..b998fbb7db 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -385,6 +385,8 @@ public: 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); } + using ModelBase::set_new_unique_id; + protected: friend class Print; friend class SLAPrint; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 59148c0a36..f9ad7a39a4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2113,31 +2113,10 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = { if (obj_idx < 0) return; - - const auto model_object = model.objects[obj_idx]; - Model model_fixed;// = new Model(); - fix_model_by_win10_sdk_gui(*model_object, this->fff_print, model_fixed); - - auto new_obj_idxs = load_model_objects(model_fixed.objects); - if (new_obj_idxs.empty()) - return; - - for(auto new_obj_idx : new_obj_idxs) { - auto o = model.objects[new_obj_idx]; - o->clear_instances(); - for (auto instance: model_object->instances) - o->add_instance(*instance); - o->invalidate_bounding_box(); - - if (o->volumes.size() == model_object->volumes.size()) { - for (int i = 0; i < o->volumes.size(); i++) { - o->volumes[i]->config.apply(model_object->volumes[i]->config); - } - } - // FIXME restore volumes and their configs, layer_height_ranges, layer_height_profile - } - - remove(obj_idx); + fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx); + this->object_list_changed(); + this->update(); + this->schedule_background_process(); } void Plater::priv::set_current_panel(wxPanel* panel) diff --git a/src/slic3r/Utils/FixModelByWin10.cpp b/src/slic3r/Utils/FixModelByWin10.cpp index 4b487588a0..1daeaff269 100644 --- a/src/slic3r/Utils/FixModelByWin10.cpp +++ b/src/slic3r/Utils/FixModelByWin10.cpp @@ -216,7 +216,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path HRESULT hr = (*s_RoInitialize)(RO_INIT_MULTITHREADED); { - on_progress(L("Exporting the source model"), 20); + on_progress(L("Exporting source model"), 20); Microsoft::WRL::ComPtr fileStream; hr = winrt_open_file_stream(boost::nowide::widen(path_src), ABI::Windows::Storage::FileAccessMode::FileAccessMode_Read, fileStream.GetAddressOf(), throw_on_cancel); @@ -239,7 +239,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path unsigned num_meshes = 0; hr = meshes->get_Size(&num_meshes); - on_progress(L("Repairing the model by the Netfabb service"), 40); + on_progress(L("Repairing model by the Netfabb service"), 40); Microsoft::WRL::ComPtr repairAsync; hr = model->RepairAsync(repairAsync.GetAddressOf()); @@ -248,7 +248,7 @@ void fix_model_by_win10_sdk(const std::string &path_src, const std::string &path throw std::runtime_error(L("Mesh repair failed.")); repairAsync->GetResults(); - on_progress(L("Loading the repaired model"), 60); + on_progress(L("Loading repaired model"), 60); // Verify the number of meshes returned after the repair action. meshes.Reset(); @@ -316,7 +316,7 @@ public: const char* what() const throw() { return "Model repair has been canceled"; } }; -void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result) +void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx) { std::mutex mutex; std::condition_variable condition; @@ -329,6 +329,12 @@ void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &pr std::atomic canceled = false; std::atomic finished = false; + std::vector volumes; + if (volume_idx == -1) + volumes = model_object.volumes; + else + volumes.emplace_back(model_object.volumes[volume_idx]); + // Open a progress dialog. wxProgressDialog progress_dialog( _(L("Model fixing")), @@ -336,43 +342,64 @@ void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &pr 100, nullptr, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT); // Executing the calculation in a background thread, so that the COM context could be created with its own threading model. // (It seems like wxWidgets initialize the COM contex as single threaded and we need a multi-threaded context). - bool success = false; - auto on_progress = [&mutex, &condition, &progress](const char *msg, unsigned prcnt) { + bool success = false; + size_t ivolume = 0; + auto on_progress = [&mutex, &condition, &ivolume, &volumes, &progress](const char *msg, unsigned prcnt) { std::lock_guard lk(mutex); progress.message = msg; - progress.percent = prcnt; + progress.percent = (int)floor((float(prcnt) + float(ivolume) * 100.f) / float(volumes.size())); progress.updated = true; condition.notify_all(); }; - auto worker_thread = boost::thread([&model_object, &print, &result, on_progress, &success, &canceled, &finished]() { + auto worker_thread = boost::thread([&model_object, &volumes, &ivolume, on_progress, &success, &canceled, &finished]() { try { - on_progress(L("Exporting the source model"), 0); - boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_src += ".3mf"; - Model model; - DynamicPrintConfig config; - model.add_object(model_object); - if (! Slic3r::store_3mf(path_src.string().c_str(), &model, &config)) { + std::vector meshes_repaired; + meshes_repaired.reserve(volumes.size()); + for (; ivolume < volumes.size(); ++ ivolume) { + on_progress(L("Exporting source model"), 0); + boost::filesystem::path path_src = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_src += ".3mf"; + Model model; + ModelObject *model_object = model.add_object(); + model_object->add_volume(*volumes[ivolume]); + model_object->add_instance(); + if (! Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr)) { + boost::filesystem::remove(path_src); + throw std::runtime_error(L("Export of a temporary 3mf file failed")); + } + model.clear_objects(); + model.clear_materials(); + boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); + path_dst += ".3mf"; + fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, + [&canceled]() { if (canceled) throw RepairCanceledException(); }); boost::filesystem::remove(path_src); - throw std::runtime_error(L("Export of a temporary 3mf file failed")); + // PresetBundle bundle; + on_progress(L("Loading repaired model"), 80); + DynamicPrintConfig config; + bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &config, &model); + boost::filesystem::remove(path_dst); + if (! loaded) + throw std::runtime_error(L("Import of the repaired 3mf file failed")); + if (model.objects.size() == 0) + throw std::runtime_error(L("Repaired 3MF file does not contain any object")); + if (model.objects.size() > 1) + throw std::runtime_error(L("Repaired 3MF file contains more than one object")); + if (model.objects.front()->volumes.size() == 0) + throw std::runtime_error(L("Repaired 3MF file does not contain any volume")); + if (model.objects.front()->volumes.size() > 1) + throw std::runtime_error(L("Repaired 3MF file contains more than one volume")); + meshes_repaired.emplace_back(std::move(model.objects.front()->volumes.front()->mesh)); } - model.clear_objects(); - model.clear_materials(); - boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); - path_dst += ".3mf"; - fix_model_by_win10_sdk(path_src.string().c_str(), path_dst.string(), on_progress, - [&canceled]() { if (canceled) throw RepairCanceledException(); }); - boost::filesystem::remove(path_src); - // PresetBundle bundle; - on_progress(L("Loading the repaired model"), 80); - bool loaded = Slic3r::load_3mf(path_dst.string().c_str(), &config, &result); - result.objects[0]->name = boost::filesystem::path(model_object.name).filename().stem().string() + "_fixed"; - boost::filesystem::remove(path_dst); - if (! loaded) - throw std::runtime_error(L("Import of the repaired 3mf file failed")); + for (size_t i = 0; i < volumes.size(); ++ i) { + volumes[i]->mesh = std::move(meshes_repaired[i]); + volumes[i]->set_new_unique_id(); + } + model_object.invalidate_bounding_box(); + -- ivolume; + on_progress(L("Model repair finished"), 100); success = true; finished = true; - on_progress(L("Model repair finished"), 100); } catch (RepairCanceledException & /* ex */) { canceled = true; finished = true; diff --git a/src/slic3r/Utils/FixModelByWin10.hpp b/src/slic3r/Utils/FixModelByWin10.hpp index c148a6970d..8e47664678 100644 --- a/src/slic3r/Utils/FixModelByWin10.hpp +++ b/src/slic3r/Utils/FixModelByWin10.hpp @@ -12,12 +12,12 @@ class Print; #ifdef HAS_WIN10SDK extern bool is_windows10(); -extern void fix_model_by_win10_sdk_gui(const ModelObject &model_object, const Print &print, Model &result); +extern void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx); #else /* HAS_WIN10SDK */ inline bool is_windows10() { return false; } -inline void fix_model_by_win10_sdk_gui(const ModelObject &, const Print &, Model &) {} +inline void fix_model_by_win10_sdk_gui(ModelObject &, int) {} #endif /* HAS_WIN10SDK */ From 1f77eb1e541f5eff03d92516a79019efc365256b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Mon, 4 Feb 2019 09:13:28 +0100 Subject: [PATCH 36/37] Fixed Technologies.hpp file --- src/libslic3r/Technologies.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/Technologies.hpp b/src/libslic3r/Technologies.hpp index 5f02653c65..8d788ed51e 100644 --- a/src/libslic3r/Technologies.hpp +++ b/src/libslic3r/Technologies.hpp @@ -12,6 +12,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 + //==================== // 1.42.0.alpha1 techs //==================== @@ -27,6 +28,7 @@ // Use wxDataViewRender instead of wxDataViewCustomRenderer #define ENABLE_NONCUSTOM_DATA_VIEW_RENDERING (0 && ENABLE_1_42_0_ALPHA1) + //==================== // 1.42.0.alpha2 techs //==================== @@ -34,7 +36,6 @@ // Adds print bed models to 3D scene #define ENABLE_PRINT_BED_MODELS (1 && ENABLE_1_42_0_ALPHA2) -#endif // _technologies_h_ //==================== @@ -63,3 +64,5 @@ // Toolbar items hidden/shown in dependence of the user mode #define ENABLE_MODE_AWARE_TOOLBAR_ITEMS (1 && ENABLE_1_42_0_ALPHA5) + +#endif // _technologies_h_ From 7c5d94e0b1369b3c52f39d386a6367db6325603f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 4 Feb 2019 09:37:49 +0100 Subject: [PATCH 37/37] Don't capture Cmd+A on OSX in Canvas, it works throug the menu accelerators. --- src/slic3r/GUI/GLCanvas3D.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cca4b893f1..12ff00dce3 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5102,12 +5102,15 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) // see include/wx/defs.h enum wxKeyCode int keyCode = evt.GetKeyCode(); int ctrlMask = wxMOD_CONTROL; -#ifdef __APPLE__ - ctrlMask |= wxMOD_RAW_CONTROL; -#endif /* __APPLE__ */ +//#ifdef __APPLE__ +// ctrlMask |= wxMOD_RAW_CONTROL; +//#endif /* __APPLE__ */ if ((evt.GetModifiers() & ctrlMask) != 0) { switch (keyCode) { +#ifndef __APPLE__ + // Even though Control+A is captured by the accelerator on OSX/wxWidgets in Slic3r, it works in text edit lines. case WXK_CONTROL_A: post_event(SimpleEvent(EVT_GLCANVAS_SELECT_ALL)); break; +#endif /* __APPLE__ */ #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #endif /* __APPLE__ */