From 01091152bef452fb72cbc99a5554875b3509d0b8 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 15 Feb 2019 16:55:15 +0100 Subject: [PATCH 01/13] Optimization for bad head angles. --- src/libnest2d/include/libnest2d/optimizer.hpp | 10 +- src/libslic3r/SLA/SLASupportTree.cpp | 203 +++++++++++++----- 2 files changed, 153 insertions(+), 60 deletions(-) diff --git a/src/libnest2d/include/libnest2d/optimizer.hpp b/src/libnest2d/include/libnest2d/optimizer.hpp index 78e105598..962a47392 100644 --- a/src/libnest2d/include/libnest2d/optimizer.hpp +++ b/src/libnest2d/include/libnest2d/optimizer.hpp @@ -163,7 +163,7 @@ public: { dir_ = OptDir::MIN; return static_cast(this)->template optimize( - objectfunction, initvals, Bound()... ); + forward(objectfunction), initvals, Bound()... ); } template @@ -171,7 +171,7 @@ public: { dir_ = OptDir::MIN; return static_cast(this)->template optimize( - objectfunction, + forward(objectfunction), Input(), Bound()... ); } @@ -184,7 +184,7 @@ public: { dir_ = OptDir::MAX; return static_cast(this)->template optimize( - objectfunction, initvals, bounds... ); + forward(objectfunction), initvals, bounds... ); } template @@ -193,7 +193,7 @@ public: { dir_ = OptDir::MAX; return static_cast(this)->template optimize( - objectfunction, initvals, Bound()... ); + forward(objectfunction), initvals, Bound()... ); } template @@ -201,7 +201,7 @@ public: { dir_ = OptDir::MAX; return static_cast(this)->template optimize( - objectfunction, + forward(objectfunction), Input(), Bound()... ); } diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 5a5187093..9d61ac144 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -588,7 +589,7 @@ double pinhead_mesh_intersect(const Vec3d& s, double r_back, double width, const EigenMesh3D& m, - unsigned samples = 8, + unsigned samples = 16, double safety_distance = 0.001) { // method based on: @@ -614,9 +615,17 @@ double pinhead_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(); - a(Z) = -(v(X)*a(X) + v(Y)*a(Y)) / v(Z); - - b = a.cross(v); + // We have to address the case when the direction vector v (same as dir) + // is coincident with one of the world axes. In this case two of its + // components will be completely zero and one is 1.0. Our method becomes + // dangerous here due to division with zero. Instead, vector a can be a + // rotated version of v + auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; }; + if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) a = {v(Z), v(X), v(Y)}; + else { + a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize(); + 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 @@ -686,8 +695,13 @@ double bridge_mesh_intersect(const Vec3d& s, 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); + auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; }; + if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) + a = {dir(Z), dir(X), dir(Y)}; + else { + a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize(); + b = a.cross(dir); + } // circle portions std::vector phis(samples); @@ -1149,16 +1163,16 @@ bool SLASupportTree::generate(const PointSet &points, //... }; - // t-hrow i-f c-ance-l-ed: It will be called many times so a shorthand will + // throw if canceled: It will be called many times so a shorthand will // come in handy. - auto& tifcl = ctl.cancelfn; + auto& thr = 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] ( + auto filterfn = [thr] ( const SupportConfig& cfg, const PointSet& points, const EigenMesh3D& mesh, @@ -1172,9 +1186,9 @@ bool SLASupportTree::generate(const PointSet &points, // first one auto aliases = cluster(points, - [tifcl](const SpatElement& p, const SpatElement& se) + [thr](const SpatElement& p, const SpatElement& se) { - tifcl(); + thr(); return distance(p.first, se.first) < D_SP; }, 2); @@ -1185,10 +1199,10 @@ bool SLASupportTree::generate(const PointSet &points, filt_pts.row(count++) = points.row(a.front()); } - tifcl(); + thr(); // calculate the normals to the triangles belonging to filtered points - auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, tifcl); + auto nmls = sla::normals(filt_pts, mesh, cfg.head_front_radius_mm, thr); head_norm.resize(count, 3); head_pos.resize(count, 3); @@ -1200,9 +1214,15 @@ bool SLASupportTree::generate(const PointSet &points, // not be enough space for the pinhead. Filtering is applied for // these reasons. + using libnest2d::opt::bound; + using libnest2d::opt::initvals; + using libnest2d::opt::SimplexOptimizer; + using libnest2d::opt::StopCriteria; + static const unsigned MAX_TRIES = 100; + int pcount = 0, hlcount = 0; for(int i = 0; i < count; i++) { - tifcl(); + thr(); auto n = nmls.row(i); // for all normals we generate the spherical coordinates and @@ -1223,32 +1243,67 @@ bool SLASupportTree::generate(const PointSet &points, // We saturate the polar angle to 3pi/4 polar = std::max(polar, 3*PI / 4); - // Reassemble the now corrected normal - Vec3d nn(std::cos(azimuth) * std::sin(polar), - std::sin(azimuth) * std::sin(polar), - std::cos(polar)); - - nn.normalize(); - // save the head (pinpoint) position Vec3d hp = filt_pts.row(i); - // the full width of the head double w = cfg.head_width_mm + cfg.head_back_radius_mm + 2*cfg.head_front_radius_mm; - // We should shoot a ray in the direction of the pinhead and - // see if there is enough space for it - double t = pinhead_mesh_intersect( - hp, // touching point - nn, - cfg.head_front_radius_mm, // approx the radius - cfg.head_back_radius_mm, - w, - mesh); + // Reassemble the now corrected normal + auto nn = Vec3d(std::cos(azimuth) * std::sin(polar), + std::sin(azimuth) * std::sin(polar), + std::cos(polar)).normalized(); - if(t > w || std::isinf(t)) { + // check available distance + double t = pinhead_mesh_intersect( + hp, // touching point + nn, // normal + cfg.head_front_radius_mm, + cfg.head_back_radius_mm, + w, + mesh); + + if(t <= w) { + // Let's try to optimize this angle, there might be a viable + // normal that doesn't collide with the model geometry and + // its very close to the default. + + StopCriteria stc; + stc.max_iterations = MAX_TRIES; + stc.relative_score_difference = 1e-3; + stc.stop_score = w; // space greater than w is enough + SimplexOptimizer solver(stc); + + auto oresult = solver.optimize_max( + [&mesh, &cfg, w, hp](double plr, double azm) + { + auto n = Vec3d(std::cos(azm) * std::sin(plr), + std::sin(azm) * std::sin(plr), + std::cos(plr)).normalized(); + + double score = pinhead_mesh_intersect( + hp, n, + cfg.head_front_radius_mm, + cfg.head_back_radius_mm, + w, + mesh); + return score; + }, + initvals(polar, azimuth), // let's start with what we have + bound(3*PI/4, PI), // Must not exceed the tilt limit + bound(-PI, PI) // azimuth can be a full range search + ); + + t = oresult.score; + polar = std::get<0>(oresult.optimum); + azimuth = std::get<1>(oresult.optimum); + nn = Vec3d(std::cos(azimuth) * std::sin(polar), + std::sin(azimuth) * std::sin(polar), + std::cos(polar)).normalized(); + } + + if(t > w) { head_pos.row(pcount) = hp; // save the verified and corrected normal @@ -1256,6 +1311,7 @@ bool SLASupportTree::generate(const PointSet &points, ++pcount; } else if( polar >= 3*PI/4 ) { + // Headless supports do not tilt like the headed ones so // the normal should point almost to the ground. headless_norm.row(hlcount) = nn; @@ -1272,7 +1328,7 @@ bool SLASupportTree::generate(const PointSet &points, // Pinhead creation: based on the filtering results, the Head objects will // be constructed (together with their triangle meshes). - auto pinheadfn = [tifcl] ( + auto pinheadfn = [thr] ( const SupportConfig& cfg, PointSet& head_pos, PointSet& nmls, @@ -1285,7 +1341,7 @@ bool SLASupportTree::generate(const PointSet &points, /* ******************************************************** */ for (int i = 0; i < head_pos.rows(); ++i) { - tifcl(); + thr(); result.add_head( cfg.head_back_radius_mm, cfg.head_front_radius_mm, @@ -1304,7 +1360,7 @@ bool SLASupportTree::generate(const PointSet &points, // 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] ( + auto classifyfn = [thr] ( const SupportConfig& cfg, const EigenMesh3D& mesh, PointSet& head_pos, @@ -1313,7 +1369,8 @@ bool SLASupportTree::generate(const PointSet &points, std::vector& gndheight, ClusteredPoints& ground_clusters, Result& result - ) { + ) + { /* ******************************************************** */ /* Classification */ @@ -1328,7 +1385,7 @@ bool SLASupportTree::generate(const PointSet &points, // 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(); + thr(); auto& head = result.head(i); Vec3d dir(0, 0, -1); @@ -1337,6 +1394,35 @@ bool SLASupportTree::generate(const PointSet &points, double t = std::numeric_limits::infinity(); double hw = head.width_mm; + + { +// using libnest2d::opt::Method; +// using libnest2d::opt::bound; +// using libnest2d::opt::Optimizer; +// using libnest2d::opt::TOptimizer; +// using libnest2d::opt::StopCriteria; + +// auto stopcond = [] () { return false; }; +// static const unsigned max_tries = 100; + +// auto objfunc = +// [&head](double polar, double azimuth, double width) +// { +// Vec3d nn(std::cos(azimuth) * std::sin(polar), +// std::sin(azimuth) * std::sin(polar), +// std::cos(polar)); + + +// }; + +// StopCriteria stc; +// stc.max_iterations = max_tries; +// stc.relative_score_difference = 1e-3; +// stc.stop_condition = stopcond; +// TOptimizer solver(stc); + } + + // 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. @@ -1356,6 +1442,13 @@ bool SLASupportTree::generate(const PointSet &points, // 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 { + if(!std::isinf(t) && !std::isinf(tprec) && + std::abs(tprec - t) > hw) + { + // In this case the head would scratch the model body + BOOST_LOG_TRIVIAL(warning) << "Head scratch detected."; + } + accept = true; t = tprec; auto id = head.id; @@ -1410,9 +1503,9 @@ bool SLASupportTree::generate(const PointSet &points, ground_clusters = cluster( gnd, - [d_base, tifcl](const SpatElement& p, const SpatElement& s) + [d_base, thr](const SpatElement& p, const SpatElement& s) { - tifcl(); + thr(); return distance(Vec2d(p.first(X), p.first(Y)), Vec2d(s.first(X), s.first(Y))) < d_base; }, 3); // max 3 heads to connect to one centroid @@ -1485,7 +1578,7 @@ bool SLASupportTree::generate(const PointSet &points, // 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]( + auto routing_ground_fn = [gnd_head_pt, interconnect, thr]( const SupportConfig& cfg, const ClusteredPoints& gnd_clusters, const IndexSet& gndidx, @@ -1501,7 +1594,7 @@ bool SLASupportTree::generate(const PointSet &points, cl_centroids.reserve(gnd_clusters.size()); SpatIndex pheadindex; // spatial index for the junctions - for(auto& cl : gnd_clusters) { tifcl(); + for(auto& cl : gnd_clusters) { thr(); // place all the centroid head positions into the index. We will // query for alternative pillar positions. If a sidehead cannot // connect to the cluster centroid, we have to search for another @@ -1512,9 +1605,9 @@ bool SLASupportTree::generate(const PointSet &points, // get the current cluster centroid long lcid = cluster_centroid(cl, gnd_head_pt, - [tifcl](const Vec3d& p1, const Vec3d& p2) + [thr](const Vec3d& p1, const Vec3d& p2) { - tifcl(); + thr(); return distance(Vec2d(p1(X), p1(Y)), Vec2d(p2(X), p2(Y))); }); @@ -1535,7 +1628,7 @@ bool SLASupportTree::generate(const PointSet &points, // sidepoints with the cluster centroid (which is a ground pillar) // or a nearby pillar if the centroid is unreachable. size_t ci = 0; - for(auto cl : gnd_clusters) { tifcl(); + for(auto cl : gnd_clusters) { thr(); auto cidx = cl_centroids[ci]; cl_centroids[ci++] = cl[cidx]; @@ -1559,12 +1652,12 @@ bool SLASupportTree::generate(const PointSet &points, // is distributed more effectively on the pillar. auto search_nearest = - [&tifcl, &cfg, &result, &emesh, maxbridgelen, gndlvl, pradius] + [&thr, &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()) { tifcl(); + while(nearest_id < 0 && !spindex.empty()) { thr(); // 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) @@ -1603,7 +1696,7 @@ bool SLASupportTree::generate(const PointSet &points, return nearest_id; }; - for(auto c : cl) { tifcl(); + for(auto c : cl) { thr(); auto& sidehead = result.head(gndidx[c]); sidehead.transform(); @@ -1669,7 +1762,7 @@ bool SLASupportTree::generate(const PointSet &points, ClusterEl ring; while(!rem.empty()) { // loop until all the points belong to some ring - tifcl(); + thr(); std::sort(rem.begin(), rem.end()); auto newring = pts_convex_hull(rem, @@ -1681,7 +1774,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) { tifcl(); + for(unsigned i : newring) { thr(); const Pillar& pill = result.head_pillar(gndidx[i]); assert(pill.id >= 0); innerring.insert(pill.endpoint, unsigned(pill.id)); @@ -1690,7 +1783,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) { tifcl(); + for(unsigned i : ring) { thr(); const Pillar& outerpill = result.head_pillar(gndidx[i]); auto res = innerring.nearest(outerpill.endpoint, 1); if(res.empty()) continue; @@ -1716,7 +1809,7 @@ bool SLASupportTree::generate(const PointSet &points, next != ring.end(); ++it, ++next) { - tifcl(); + thr(); const Pillar& pillar = result.head_pillar(gndidx[*it]); const Pillar& nextpillar = result.head_pillar(gndidx[*next]); interconnect(pillar, nextpillar, emesh, result); @@ -1736,14 +1829,14 @@ bool SLASupportTree::generate(const PointSet &points, // 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]( + auto routing_nongnd_fn = [thr]( const SupportConfig& cfg, const std::vector& gndheight, const IndexSet& nogndidx, Result& result) { // TODO: connect these to the ground pillars if possible - for(auto idx : nogndidx) { tifcl(); + for(auto idx : nogndidx) { thr(); double gh = gndheight[idx]; double base_width = cfg.head_width_mm; @@ -1800,7 +1893,7 @@ 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]( + auto process_headless = [thr]( const SupportConfig& cfg, const PointSet& headless_pts, const PointSet& headless_norm, @@ -1815,7 +1908,7 @@ 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(); + for(int i = 0; i < headless_pts.rows(); i++) { thr(); 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 From a50ffd079185ab3471cb5e2ce77d3840185c9f29 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Feb 2019 10:16:04 +0100 Subject: [PATCH 02/13] Fix for uninitialized value in an edge case. --- src/libslic3r/SLA/SLASupportTree.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 9d61ac144..60646fadf 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -618,10 +618,13 @@ double pinhead_mesh_intersect(const Vec3d& s, // We have to address the case when the direction vector v (same as dir) // is coincident with one of the world axes. In this case two of its // components will be completely zero and one is 1.0. Our method becomes - // dangerous here due to division with zero. Instead, vector a can be a - // rotated version of v + // dangerous here due to division with zero. Instead, vector 'a' can be an + // element-wise rotated version of 'v' auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; }; - if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) a = {v(Z), v(X), v(Y)}; + if(chk1(v(X)) || chk1(v(Y)) || chk1(v(Z))) { + a = {v(Z), v(X), v(Y)}; + b = {v(Y), v(Z), v(X)}; + } else { a(Z) = -(v(Y)*a(Y)) / v(Z); a.normalize(); b = a.cross(v); @@ -650,18 +653,14 @@ double pinhead_mesh_intersect(const Vec3d& s, 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 psq = m.signed_distance(ps); + // would cause many problems with ray-casting. To detect the position we + // will use the ray-casting result (which has an is_inside predicate). // 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 - ps).normalized(); auto hr = m.query_ray_hit(ps + sd*n, n); @@ -695,9 +694,14 @@ double bridge_mesh_intersect(const Vec3d& s, Vec3d a(0, 1, 0), b; const double& sd = safety_distance; + // INFO: for explanation of the method used here, see the previous method's + // comments. + auto chk1 = [] (double val) { return std::abs(std::abs(val) - 1) < 1e-20; }; - if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) + if(chk1(dir(X)) || chk1(dir(Y)) || chk1(dir(Z))) { a = {dir(Z), dir(X), dir(Y)}; + b = {dir(Y), dir(Z), dir(X)}; + } else { a(Z) = -(dir(Y)*a(Y)) / dir(Z); a.normalize(); b = a.cross(dir); From c8bda6c13b210ad4bde6fc0ecf5389f8c7d2ee4a Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 18 Feb 2019 10:52:16 +0100 Subject: [PATCH 03/13] Fix for a few comment typos --- src/libslic3r/SLA/SLASupportTree.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 60646fadf..7cf6015cb 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -1385,7 +1385,7 @@ 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 + // First we 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++) { @@ -1429,7 +1429,7 @@ bool SLASupportTree::generate(const PointSet &points, // 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. + // the head width so that the pillar can be deployed. while(!accept && head.width_mm > 0) { Vec3d startpoint = head.junction_point(); @@ -1441,7 +1441,7 @@ 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 + // This is a damned case where the pillar melts 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; @@ -1828,7 +1828,7 @@ bool SLASupportTree::generate(const PointSet &points, } }; - // Step: routing the pinheads that are would connect to the model surface + // Step: routing the pinheads that 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 From 2ff95054437cd2e4f3d1ace2f81af1fd601c5c5c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 25 Feb 2019 10:14:49 +0100 Subject: [PATCH 04/13] Fixed wrong updating of the editable ComboBox under OSX --- src/slic3r/GUI/Field.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index d514615f2..0d65e0ef5 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -631,8 +631,13 @@ void Choice::set_value(const boost::any& value, bool change_event) break; ++idx; } - idx == m_opt.enum_values.size() ? - dynamic_cast(window)->SetValue(text_value) : + if (idx == m_opt.enum_values.size()) { + // For editable Combobox under OSX is needed to set selection to -1 explicitly, + // otherwise selection doesn't be changed + dynamic_cast(window)->SetSelection(-1); + dynamic_cast(window)->SetValue(text_value); + } + else dynamic_cast(window)->SetSelection(idx); break; } From a0d256f9f775d23c8412f92e9fdbd8197c02df2e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 25 Feb 2019 10:21:12 +0100 Subject: [PATCH 05/13] Restart background processing for all steps on G-code export and send to octoprint for SLA if the slicing for a single object only is active. --- src/libslic3r/SLAPrint.cpp | 60 ++++++++++++++++++++++++++------------ src/slic3r/GUI/Plater.cpp | 4 +++ 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d81498916..3a08cddf1 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -415,49 +415,73 @@ void SLAPrint::set_task(const TaskParams ¶ms) n_object_steps = (int)slaposCount; if (params.single_model_object.valid()) { + // Find the print object to be processed with priority. SLAPrintObject *print_object = nullptr; size_t idx_print_object = 0; - for (; idx_print_object < m_objects.size(); ++idx_print_object) + for (; idx_print_object < m_objects.size(); ++ idx_print_object) if (m_objects[idx_print_object]->model_object()->id() == params.single_model_object) { print_object = m_objects[idx_print_object]; break; } assert(print_object != nullptr); - bool shall_cancel = false; - for (int istep = 0; istep < n_object_steps; ++istep) - if (! print_object->m_stepmask[istep]) { - shall_cancel = true; + // Find out whether the priority print object is being currently processed. + bool running = false; + for (int istep = 0; istep < n_object_steps; ++ istep) { + if (! print_object->m_stepmask[istep]) + // Step was skipped, cancel. + break; + if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { + // No step was skipped, and a wanted step is being processed. Don't cancel. + running = true; break; } - bool running = false; - if (!shall_cancel) { - for (int istep = 0; istep < n_object_steps; ++ istep) - if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { - running = true; - break; - } } - if (!running) + if (! running) this->call_cancel_callback(); // Now the background process is either stopped, or it is inside one of the print object steps to be calculated anyway. if (params.single_model_instance_only) { // Suppress all the steps of other instances. for (SLAPrintObject *po : m_objects) - for (int istep = 0; istep < (int)slaposCount; ++istep) + for (int istep = 0; istep < (int)slaposCount; ++ istep) po->m_stepmask[istep] = false; - } - else if (!running) { + } else if (! running) { // Swap the print objects, so that the selected print_object is first in the row. // At this point the background processing must be stopped, so it is safe to shuffle print objects. if (idx_print_object != 0) std::swap(m_objects.front(), m_objects[idx_print_object]); } + // and set the steps for the current object. for (int istep = 0; istep < n_object_steps; ++ istep) print_object->m_stepmask[istep] = true; - for (int istep = n_object_steps; istep < (int)slaposCount; ++istep) + for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep) print_object->m_stepmask[istep] = false; - } + } else { + // Slicing all objects. + bool running = false; + for (SLAPrintObject *print_object : m_objects) + for (int istep = 0; istep < n_object_steps; ++ istep) { + if (! print_object->m_stepmask[istep]) { + // Step may have been skipped. Restart. + goto loop_end; + } + if (print_object->is_step_started_unguarded(SLAPrintObjectStep(istep))) { + // This step is running, and the state cannot be changed due to the this->state_mutex() being locked. + // It is safe to manipulate m_stepmask of other SLAPrintObjects and SLAPrint now. + running = true; + goto loop_end; + } + } + loop_end: + if (! running) + this->call_cancel_callback(); + for (SLAPrintObject *po : m_objects) { + for (int istep = 0; istep < n_object_steps; ++ istep) + po->m_stepmask[istep] = true; + for (int istep = n_object_steps; istep < (int)slaposCount; ++ istep) + po->m_stepmask[istep] = false; + } + } if (params.to_object_step != -1 || params.to_print_step != -1) { // Limit the print steps. diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 28b3008fd..c368c4a07 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2142,6 +2142,8 @@ void Plater::priv::export_gcode(fs::path output_path, PrintHostJob upload_job) background_process.schedule_upload(std::move(upload_job)); } + // If the SLA processing of just a single object's supports is running, restart slicing for the whole object. + this->background_process.set_task(PrintBase::TaskParams()); this->restart_background_process(priv::UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT); } @@ -3068,6 +3070,8 @@ void Plater::reslice() unsigned int state = this->p->update_background_process(true); if (state & priv::UPDATE_BACKGROUND_PROCESS_REFRESH_SCENE) this->p->view3D->reload_scene(false); + // If the SLA processing of just a single object's supports is running, restart slicing for the whole object. + this->p->background_process.set_task(PrintBase::TaskParams()); // Only restarts if the state is valid. this->p->restart_background_process(state | priv::UPDATE_BACKGROUND_PROCESS_FORCE_RESTART); } From 3aee6ddc4c6930a288de5a7e3d272976728f85a7 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 25 Feb 2019 10:35:16 +0100 Subject: [PATCH 06/13] Attempt to fix 3D scene popup menu on Linux --- src/slic3r/GUI/Plater.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c368c4a07..e917269d2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2451,8 +2451,15 @@ void Plater::priv::on_right_click(Vec2dEvent& evt) sidebar->obj_list()->append_menu_item_settings(menu); - if (q != nullptr) + if (q != nullptr) { +#ifdef __linux__ + // For some reason on Linux the menu isn't displayed if position is specified + // (even though the position is sane). + q->PopupMenu(menu); +#else q->PopupMenu(menu, (int)evt.data.x(), (int)evt.data.y()); +#endif + } } void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) From 01c9b13ade6354c89b3c88ec1df8ae73aa5dcd9e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 25 Feb 2019 12:06:38 +0100 Subject: [PATCH 07/13] Disabling pad edge radius and adding the "wall tilt" parameter. --- src/libslic3r/PrintConfig.cpp | 11 +++++++++++ src/libslic3r/PrintConfig.hpp | 4 ++++ src/libslic3r/SLA/SLABasePool.cpp | 23 +++++++++++++++-------- src/libslic3r/SLA/SLABasePool.hpp | 7 +++++-- src/libslic3r/SLAPrint.cpp | 10 ++++++---- src/slic3r/GUI/Preset.cpp | 1 + src/slic3r/GUI/Tab.cpp | 4 +++- 7 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7286c5cd8..cc644fc74 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2730,6 +2730,17 @@ void PrintConfigDef::init_sla_params() def->cli = ""; def->min = 0; def->default_value = new ConfigOptionFloat(1.0); + + def = this->add("pad_wall_tilt", coFloat); + def->label = L("Pad wall tilt"); + def->category = L("Pad"); + def->tooltip = L("The tilt of the pad wall relative to the bed plane. " + "90 degrees means straight walls."); + def->sidetext = L("degrees"); + def->cli = ""; + def->min = 0.1; // What should be the minimum? + def->max = 90; + def->default_value = new ConfigOptionFloat(45.0); } void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &value) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1ea556773..21bc32ed9 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1028,6 +1028,9 @@ public: // The smoothing radius of the pad edges ConfigOptionFloat pad_edge_radius /*= 1*/; + // The tilt of the pad wall... + ConfigOptionFloat pad_wall_tilt; + protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { @@ -1053,6 +1056,7 @@ protected: OPT_PTR(pad_wall_height); OPT_PTR(pad_max_merge_distance); OPT_PTR(pad_edge_radius); + OPT_PTR(pad_wall_tilt); } }; diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 6f85c43dc..42b22acb9 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -601,7 +601,7 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, const double thickness = cfg.min_wall_thickness_mm; const double wingheight = cfg.min_wall_height_mm; const double fullheight = wingheight + thickness; - const double tilt = PI/4; + const double tilt = cfg.wall_tilt; const double wingdist = wingheight / std::tan(tilt); // scaled values @@ -627,15 +627,22 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, auto outer_base = concaveh; outer_base.holes.clear(); offset(outer_base, s_safety_dist + s_wingdist + s_thickness); - auto inner_base = outer_base; - offset(inner_base, -(s_thickness + s_wingdist)); + + + ExPolygon bottom_poly = outer_base; + bottom_poly.holes.clear(); + if(s_wingdist > 0) offset(bottom_poly, -s_wingdist); // Punching a hole in the top plate for the cavity ExPolygon top_poly; ExPolygon middle_base; + ExPolygon inner_base; top_poly.contour = outer_base.contour; if(wingheight > 0) { + inner_base = outer_base; + offset(inner_base, -(s_thickness + s_wingdist + s_eradius)); + middle_base = outer_base; offset(middle_base, -s_thickness); top_poly.holes.emplace_back(middle_base.contour); @@ -682,10 +689,10 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, thrcl, ob, wh)); - // Now that we have the rounded edge connencting the top plate with + // Now that we have the rounded edge connecting the top plate with // the outer side walls, we can generate and merge the sidewall geometry - pool.merge(walls(ob.contour, inner_base.contour, wh, -fullheight, - (s_thickness + s_wingdist) * SCALING_FACTOR, thrcl)); + pool.merge(walls(ob.contour, bottom_poly.contour, wh, -fullheight, + wingdist, thrcl)); if(wingheight > 0) { // Generate the smoothed edge geometry @@ -700,14 +707,14 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // Next is the cavity walls connecting to the top plate's // artificially created hole. pool.merge(walls(inner_base.contour, ob.contour, -wingheight, - wh, -s_safety_dist * SCALING_FACTOR, thrcl)); + wh, -wingdist, thrcl)); } // Now we need to triangulate the top and bottom plates as well as the // cavity bottom plate which is the same as the bottom plate but it is // elevated by the thickness. pool.merge(triangulate_expolygon_3d(top_poly)); - pool.merge(triangulate_expolygon_3d(inner_base, -fullheight, true)); + pool.merge(triangulate_expolygon_3d(bottom_poly, -fullheight, true)); if(wingheight > 0) pool.merge(triangulate_expolygon_3d(inner_base, -wingheight)); diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp index 3917d995b..69b4561b1 100644 --- a/src/libslic3r/SLA/SLABasePool.hpp +++ b/src/libslic3r/SLA/SLABasePool.hpp @@ -3,6 +3,7 @@ #include #include +#include namespace Slic3r { @@ -27,15 +28,17 @@ struct PoolConfig { double min_wall_height_mm = 5; double max_merge_distance_mm = 50; double edge_radius_mm = 1; + double wall_tilt = std::atan(1.0); // Universal constant for Pi/4 ThrowOnCancel throw_on_cancel = [](){}; inline PoolConfig() {} - inline PoolConfig(double wt, double wh, double md, double er): + inline PoolConfig(double wt, double wh, double md, double er, double tilt): min_wall_thickness_mm(wt), min_wall_height_mm(wh), max_merge_distance_mm(md), - edge_radius_mm(er) {} + edge_radius_mm(er), + wall_tilt(tilt) {} }; /// Calculate the pool for the mesh for SLA printing diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index 3a08cddf1..d83e756e8 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -751,11 +751,13 @@ void SLAPrint::process() double wt = po.m_config.pad_wall_thickness.getFloat(); double h = po.m_config.pad_wall_height.getFloat(); double md = po.m_config.pad_max_merge_distance.getFloat(); - double er = po.m_config.pad_edge_radius.getFloat(); + // Radius is disabled for now... + double er = 0; // po.m_config.pad_edge_radius.getFloat(); + double tilt = po.m_config.pad_wall_tilt.getFloat() * PI / 180.0; double lh = po.m_config.layer_height.getFloat(); double elevation = po.m_config.support_object_elevation.getFloat(); if(!po.m_config.supports_enable.getBool()) elevation = 0; - sla::PoolConfig pcfg(wt, h, md, er); + sla::PoolConfig pcfg(wt, h, md, er, tilt); ExPolygons bp; double pad_h = sla::get_pad_fullheight(pcfg); @@ -766,8 +768,7 @@ void SLAPrint::process() if(elevation < pad_h) { // we have to count with the model geometry for the base plate - sla::base_plate(trmesh, bp, float(pad_h), float(lh), - thrfn); + sla::base_plate(trmesh, bp, float(pad_h), float(lh), thrfn); } pcfg.throw_on_cancel = thrfn; @@ -1368,6 +1369,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector& Preset::sla_print_options() "pad_wall_height", "pad_max_merge_distance", "pad_edge_radius", + "pad_wall_tilt", "output_filename_format", "default_sla_print_profile", "compatible_printers", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 7e9a0c3a2..783527026 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3288,7 +3288,9 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_wall_thickness"); optgroup->append_single_option_line("pad_wall_height"); optgroup->append_single_option_line("pad_max_merge_distance"); - optgroup->append_single_option_line("pad_edge_radius"); + // TODO: Disabling this parameter for the beta release +// optgroup->append_single_option_line("pad_edge_radius"); + optgroup->append_single_option_line("pad_wall_tilt"); page = add_options_page(_(L("Output options")), "page_white_go.png"); optgroup = page->new_optgroup(_(L("Output file"))); From bb3819fd1833043af9cf987992c638dd2582623e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 25 Feb 2019 13:03:06 +0100 Subject: [PATCH 08/13] SLA support gizmo hotkeys added (A,M,Esc,Enter) --- src/slic3r/GUI/GLCanvas3D.cpp | 30 ++- src/slic3r/GUI/GLCanvas3D.hpp | 6 +- src/slic3r/GUI/GLGizmo.cpp | 375 +++++++++++++++++----------------- src/slic3r/GUI/GLGizmo.hpp | 9 +- 4 files changed, 226 insertions(+), 194 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c6d465ad2..24fcd4a70 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4625,7 +4625,19 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) switch (keyCode) { // key ESC - case WXK_ESCAPE: { m_gizmos.reset_all_states(); m_dirty = true; break; } + case WXK_ESCAPE: { + if (m_gizmos.get_current_type() != Gizmos::SlaSupports || !m_gizmos.mouse_event(SLAGizmoEventType::DiscardChanges)) + m_gizmos.reset_all_states(); + m_dirty = true; + break; + } + + case WXK_RETURN: { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::ApplyChanges)) + m_dirty = true; + break; + } + #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #else /* __APPLE__ */ @@ -4648,11 +4660,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) 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 'a': { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports) { + if (m_gizmos.mouse_event(SLAGizmoEventType::AutomaticGeneration)) + m_dirty = true; + } + else + 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 'M': + case 'm': { + if (m_gizmos.get_current_type() == Gizmos::SlaSupports && m_gizmos.mouse_event(SLAGizmoEventType::ManualEditing)) + m_dirty = true; + break; + } case 'O': case 'o': { set_camera_zoom(-1.0f); break; } case 'Z': diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index bcae0f9af..008e77056 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -125,7 +125,11 @@ enum class SLAGizmoEventType { Dragging, Delete, SelectAll, - ShiftUp + ShiftUp, + ApplyChanges, + DiscardChanges, + AutomaticGeneration, + ManualEditing }; diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index 723ac32a2..f1a4589fe 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1941,12 +1941,6 @@ bool GLGizmoSlaSupports::is_mesh_update_necessary() const //if (m_state != On || !m_model_object || m_model_object->instances.empty() || ! m_instance_matrix.isApprox(m_source_data.matrix)) // return false; - - // following should detect direct mesh changes (can be removed after the mesh is made completely immutable): - /*const float* first_vertex = m_model_object->volumes.front()->get_convex_hull().first_vertex(); - Vec3d first_point((double)first_vertex[0], (double)first_vertex[1], (double)first_vertex[2]); - if (first_point != m_source_data.mesh_first_point) - return true;*/ } void GLGizmoSlaSupports::update_mesh() @@ -2022,139 +2016,161 @@ Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) // concludes that the event was not intended for it, it should return false. bool GLGizmoSlaSupports::mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down) { - if (!m_editing_mode) - return false; + if (m_editing_mode) { - // left down - show the selection rectangle: - if (action == SLAGizmoEventType::LeftDown && shift_down) { - if (m_hover_id == -1) { - m_selection_rectangle_active = true; - m_selection_rectangle_start_corner = mouse_position; + // left down - show the selection rectangle: + if (action == SLAGizmoEventType::LeftDown && shift_down) { + if (m_hover_id == -1) { + m_selection_rectangle_active = true; + m_selection_rectangle_start_corner = mouse_position; + m_selection_rectangle_end_corner = mouse_position; + m_canvas_width = m_parent.get_canvas_size().get_width(); + m_canvas_height = m_parent.get_canvas_size().get_height(); + } + else + select_point(m_hover_id); + + return true; + } + + // dragging the selection rectangle: + if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) { m_selection_rectangle_end_corner = mouse_position; - m_canvas_width = m_parent.get_canvas_size().get_width(); - m_canvas_height = m_parent.get_canvas_size().get_height(); - } - else - select_point(m_hover_id); - - return true; - } - - // dragging the selection rectangle: - if (action == SLAGizmoEventType::Dragging && m_selection_rectangle_active) { - m_selection_rectangle_end_corner = mouse_position; - return true; - } - - // mouse up without selection rectangle - place point on the mesh: - if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) { - if (m_ignore_up_event) { - m_ignore_up_event = false; - return false; + return true; } - int instance_id = m_parent.get_selection().get_instance_idx(); - if (m_old_instance_id != instance_id) - { - bool something_selected = (m_old_instance_id != -1); - m_old_instance_id = instance_id; - if (something_selected) + // mouse up without selection rectangle - place point on the mesh: + if (action == SLAGizmoEventType::LeftUp && !m_selection_rectangle_active && !shift_down) { + if (m_ignore_up_event) { + m_ignore_up_event = false; return false; - } - if (instance_id == -1) - return false; - - // If there is some selection, don't add new point and deselect everything instead. - if (m_selection_empty) { - Vec3f new_pos; - try { - new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case - m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false)); - m_unsaved_changes = true; } - catch (...) { // not clicked on object - return true; // prevents deselection of the gizmo by GLCanvas3D + + int instance_id = m_parent.get_selection().get_instance_idx(); + if (m_old_instance_id != instance_id) + { + bool something_selected = (m_old_instance_id != -1); + m_old_instance_id = instance_id; + if (something_selected) + return false; } - } - else - select_point(NoPoints); + if (instance_id == -1) + return false; - return true; - } - - // left up with selection rectangle - select points inside the rectangle: - if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp) - && m_selection_rectangle_active) { - if (action == SLAGizmoEventType::ShiftUp) - m_ignore_up_event = true; - const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix(); - GLint viewport[4]; - ::glGetIntegerv(GL_VIEWPORT, viewport); - GLdouble modelview_matrix[16]; - ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); - GLdouble projection_matrix[16]; - ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - - const GLCanvas3D::Selection& selection = m_parent.get_selection(); - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - double z_offset = volume->get_sla_shift_z(); - - // bounding box created from the rectangle corners - will take care of order of the corners - BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); - - const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true); - // we'll recover current look direction from the modelview matrix (in world coords)... - Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]); - // ...and transform it to model coords. - direction_to_camera = (instance_matrix_no_translation.inverse().cast() * direction_to_camera).normalized().eval(); - - // Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh: - for (unsigned int i=0; i() * support_point.pos; - pos(2) += z_offset; - GLdouble out_x, out_y, out_z; - ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); - out_y = m_canvas_height - out_y; - - if (rectangle.contains(Point(out_x, out_y))) { - bool is_obscured = false; - // Cast a ray in the direction of the camera and look for intersection with the mesh: - std::vector hits; - // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. - if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits)) - // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. - // Also, the threshold is in mesh coordinates, not in actual dimensions. - if (hits.size() > 1 || hits.front().t > 0.001f) - is_obscured = true; - - if (!is_obscured) - select_point(i); + // If there is some selection, don't add new point and deselect everything instead. + if (m_selection_empty) { + Vec3f new_pos; + try { + new_pos = unproject_on_mesh(mouse_position); // this can throw - we don't want to create a new point in that case + m_editing_mode_cache.emplace_back(std::make_pair(sla::SupportPoint(new_pos, m_new_point_head_diameter/2.f, false), false)); + m_unsaved_changes = true; + } + catch (...) { // not clicked on object + return true; // prevents deselection of the gizmo by GLCanvas3D + } } + else + select_point(NoPoints); + + return true; } - m_selection_rectangle_active = false; - return true; - } - if (action == SLAGizmoEventType::Delete) { - // delete key pressed - delete_selected_points(); - return true; - } + // left up with selection rectangle - select points inside the rectangle: + if ((action == SLAGizmoEventType::LeftUp || action == SLAGizmoEventType::ShiftUp) + && m_selection_rectangle_active) { + if (action == SLAGizmoEventType::ShiftUp) + m_ignore_up_event = true; + const Transform3d& instance_matrix = m_model_object->instances[m_active_instance]->get_transformation().get_matrix(); + GLint viewport[4]; + ::glGetIntegerv(GL_VIEWPORT, viewport); + GLdouble modelview_matrix[16]; + ::glGetDoublev(GL_MODELVIEW_MATRIX, modelview_matrix); + GLdouble projection_matrix[16]; + ::glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix); - if (action == SLAGizmoEventType::RightDown) { - if (m_hover_id != -1) { - select_point(NoPoints); - select_point(m_hover_id); + const GLCanvas3D::Selection& selection = m_parent.get_selection(); + const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); + double z_offset = volume->get_sla_shift_z(); + + // bounding box created from the rectangle corners - will take care of order of the corners + BoundingBox rectangle(Points{Point(m_selection_rectangle_start_corner.cast()), Point(m_selection_rectangle_end_corner.cast())}); + + const Transform3d& instance_matrix_no_translation = volume->get_instance_transformation().get_matrix(true); + // we'll recover current look direction from the modelview matrix (in world coords)... + Vec3f direction_to_camera(modelview_matrix[2], modelview_matrix[6], modelview_matrix[10]); + // ...and transform it to model coords. + direction_to_camera = (instance_matrix_no_translation.inverse().cast() * direction_to_camera).normalized().eval(); + + // Iterate over all points, check if they're in the rectangle and if so, check that they are not obscured by the mesh: + for (unsigned int i=0; i() * support_point.pos; + pos(2) += z_offset; + GLdouble out_x, out_y, out_z; + ::gluProject((GLdouble)pos(0), (GLdouble)pos(1), (GLdouble)pos(2), modelview_matrix, projection_matrix, viewport, &out_x, &out_y, &out_z); + out_y = m_canvas_height - out_y; + + if (rectangle.contains(Point(out_x, out_y))) { + bool is_obscured = false; + // Cast a ray in the direction of the camera and look for intersection with the mesh: + std::vector hits; + // Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies. + if (m_AABB.intersect_ray(m_V, m_F, support_point.pos + direction_to_camera * (support_point.head_front_radius + EPSILON), direction_to_camera, hits)) + // FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction. + // Also, the threshold is in mesh coordinates, not in actual dimensions. + if (hits.size() > 1 || hits.front().t > 0.001f) + is_obscured = true; + + if (!is_obscured) + select_point(i); + } + } + m_selection_rectangle_active = false; + return true; + } + + if (action == SLAGizmoEventType::Delete) { + // delete key pressed delete_selected_points(); return true; } - return false; + + if (action == SLAGizmoEventType::ApplyChanges) { + editing_mode_apply_changes(); + return true; + } + + if (action == SLAGizmoEventType::DiscardChanges) { + editing_mode_discard_changes(); + return true; + } + + if (action == SLAGizmoEventType::RightDown) { + if (m_hover_id != -1) { + select_point(NoPoints); + select_point(m_hover_id); + delete_selected_points(); + return true; + } + return false; + } + + if (action == SLAGizmoEventType::SelectAll) { + select_point(AllPoints); + return true; + } } - if (action == SLAGizmoEventType::SelectAll) { - select_point(AllPoints); - return true; + if (!m_editing_mode) { + if (action == SLAGizmoEventType::AutomaticGeneration) { + auto_generate(); + return true; + } + + if (action == SLAGizmoEventType::ManualEditing) { + switch_to_editing_mode(); + return true; + } } return false; @@ -2241,7 +2257,6 @@ RENDER_AGAIN: bool force_refresh = false; bool remove_selected = false; - bool old_editing_state = m_editing_mode; if (m_editing_mode) { m_imgui->text(_(L("Left mouse click - add point"))); @@ -2285,9 +2300,6 @@ RENDER_AGAIN: if (apply_changes) { editing_mode_apply_changes(); force_refresh = true; - // Recalculate support structures once the editing mode is left. - // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); - wxGetApp().plater()->reslice_SLA_supports(*m_model_object); } ImGui::SameLine(); bool discard_changes = m_imgui->button(_(L("Discard changes"))); @@ -2305,70 +2317,24 @@ RENDER_AGAIN: ImGui::SameLine(); value_changed |= ImGui::InputDouble("%", &m_density, 0.0f, 0.0f, "%.f");*/ - bool generate = m_imgui->button(_(L("Auto-generate points"))); + bool generate = m_imgui->button(_(L("Auto-generate points [A]"))); - if (generate) { -#if SLAGIZMO_IMGUI_MODAL - ImGui::OpenPopup(_(L("Warning"))); - m_show_modal = true; - force_refresh = true; -#else - wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( - "Autogeneration will erase all manually edited points.\n\n" - "Are you sure you want to do it?\n" - )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); - if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) { - m_model_object->sla_support_points.clear(); - m_editing_mode_cache.clear(); - wxGetApp().plater()->reslice_SLA_supports(*m_model_object); - } -#endif - } -#if SLAGIZMO_IMGUI_MODAL - if (m_show_modal) { - if (ImGui::BeginPopupModal(_(L("Warning")), &m_show_modal/*, ImGuiWindowFlags_NoDecoration*/)) - { - m_imgui->text(_(L("Autogeneration will erase all manually edited points."))); - m_imgui->text(""); - m_imgui->text(_(L("Are you sure you want to do it?"))); + if (generate) + auto_generate(); - if (m_imgui->button(_(L("Continue")))) - { - ImGui::CloseCurrentPopup(); - m_show_modal = false; - - m_model_object->sla_support_points.clear(); - m_editing_mode_cache.clear(); - wxGetApp().plater()->reslice_SLA_supports(*m_model_object); - } - ImGui::SameLine(); - if (m_imgui->button(_(L("Cancel")))) { - ImGui::CloseCurrentPopup(); - m_show_modal = false; - } - ImGui::EndPopup(); - } - - if (!m_show_modal) - force_refresh = true; - } -#endif m_imgui->text(""); m_imgui->text(""); - bool editing_clicked = m_imgui->button(_(L("Manual editing"))); - if (editing_clicked) { - editing_mode_reload_cache(); - m_editing_mode = true; - } + if (m_imgui->button(_(L("Manual editing [M]")))) + switch_to_editing_mode(); } m_imgui->end(); - if (m_editing_mode != old_editing_state) { // user just toggled between editing/non-editing mode + if (m_editing_mode != m_old_editing_state) { // user toggled between editing/non-editing mode m_parent.toggle_sla_auxiliaries_visibility(!m_editing_mode); force_refresh = true; } - + m_old_editing_state = m_editing_mode; if (remove_selected) { force_refresh = false; @@ -2434,18 +2400,13 @@ void GLGizmoSlaSupports::on_set_state() m_parent.toggle_model_objects_visibility(true); m_editing_mode = false; // so it is not active next time the gizmo opens - -#if SLAGIZMO_IMGUI_MODAL - if (m_show_modal) { - m_show_modal = false; - on_render_input_window(0,0,m_parent.get_selection()); // this is necessary to allow ImGui to terminate the modal dialog correctly - } -#endif } } m_old_state = m_state; } + + void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selection) { if (m_hover_id != -1) { @@ -2454,6 +2415,8 @@ void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selectio } } + + void GLGizmoSlaSupports::select_point(int i) { if (i == AllPoints || i == NoPoints) { @@ -2467,6 +2430,8 @@ void GLGizmoSlaSupports::select_point(int i) } } + + void GLGizmoSlaSupports::editing_mode_discard_changes() { m_editing_mode_cache.clear(); @@ -2476,6 +2441,8 @@ void GLGizmoSlaSupports::editing_mode_discard_changes() m_unsaved_changes = false; } + + void GLGizmoSlaSupports::editing_mode_apply_changes() { // If there are no changes, don't touch the front-end. The data in the cache could have been @@ -2487,8 +2454,14 @@ void GLGizmoSlaSupports::editing_mode_apply_changes() } m_editing_mode = false; m_unsaved_changes = false; + + // Recalculate support structures once the editing mode is left. + // m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + wxGetApp().plater()->reslice_SLA_supports(*m_model_object); } + + void GLGizmoSlaSupports::editing_mode_reload_cache() { m_editing_mode_cache.clear(); @@ -2497,6 +2470,8 @@ void GLGizmoSlaSupports::editing_mode_reload_cache() m_unsaved_changes = false; } + + void GLGizmoSlaSupports::get_data_from_backend() { for (const SLAPrintObject* po : m_parent.sla_print()->objects()) { @@ -2514,6 +2489,34 @@ void GLGizmoSlaSupports::get_data_from_backend() } + +void GLGizmoSlaSupports::auto_generate() +{ + wxMessageDialog dlg(GUI::wxGetApp().plater(), _(L( + "Autogeneration will erase all manually edited points.\n\n" + "Are you sure you want to do it?\n" + )), _(L("Warning")), wxICON_WARNING | wxYES | wxNO); + + if (m_model_object->sla_support_points.empty() || dlg.ShowModal() == wxID_YES) { + m_model_object->sla_support_points.clear(); + m_editing_mode_cache.clear(); + wxGetApp().plater()->reslice_SLA_supports(*m_model_object); + } +} + + + +void GLGizmoSlaSupports::switch_to_editing_mode() +{ + editing_mode_reload_cache(); + m_editing_mode = true; +} + + + + + + // GLGizmoCut class GLGizmoCutPanel : public wxPanel diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 7118c1650..6996f5576 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -439,7 +439,6 @@ protected: -#define SLAGIZMO_IMGUI_MODAL 0 class GLGizmoSlaSupports : public GLGizmoBase { private: @@ -457,7 +456,7 @@ private: igl::AABB m_AABB; struct SourceDataSummary { - Vec3d mesh_first_point; + Geometry::Transformation transformation; }; // This holds information to decide whether recalculation is necessary: @@ -491,6 +490,7 @@ private: bool m_lock_unique_islands = false; bool m_editing_mode = false; + bool m_old_editing_state = false; float m_new_point_head_diameter = 0.4f; double m_minimal_point_distance = 20.; double m_density = 100.; @@ -504,9 +504,6 @@ private: bool m_unsaved_changes = false; bool m_selection_empty = true; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) -#if SLAGIZMO_IMGUI_MODAL - bool m_show_modal = false; -#endif int m_canvas_width; int m_canvas_height; @@ -521,6 +518,8 @@ private: void editing_mode_discard_changes(); void editing_mode_reload_cache(); void get_data_from_backend(); + void auto_generate(); + void switch_to_editing_mode(); protected: void on_set_state() override; From e38522b3c1d43426de0ef6b82ae5525ac6706015 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 25 Feb 2019 13:24:01 +0100 Subject: [PATCH 09/13] Tiny cosmetics --- src/libslic3r/SLA/SLASupportTreeIGL.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/SLA/SLASupportTreeIGL.cpp b/src/libslic3r/SLA/SLASupportTreeIGL.cpp index 6a3b71e7d..25638fe69 100644 --- a/src/libslic3r/SLA/SLASupportTreeIGL.cpp +++ b/src/libslic3r/SLA/SLASupportTreeIGL.cpp @@ -205,9 +205,11 @@ template double distance(const Vec& pp1, const Vec& pp2) { return std::sqrt(p.transpose() * p); } -PointSet normals(const PointSet& points, const EigenMesh3D& mesh, +PointSet normals(const PointSet& points, + const EigenMesh3D& mesh, double eps, - std::function throw_on_cancel) { + std::function throw_on_cancel) +{ if(points.rows() == 0 || mesh.V().rows() == 0 || mesh.F().rows() == 0) return {}; @@ -228,7 +230,7 @@ PointSet normals(const PointSet& points, const EigenMesh3D& mesh, 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 + // If it does then all the other triangles using the same two points // have to be searched and the final normal should be some kind of // aggregation of the participating triangle normals. We should also // consider the cases where the support point lies right on a vertex From f23919985f8360364340cfe51fc226b22c63d15d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 25 Feb 2019 15:01:39 +0100 Subject: [PATCH 10/13] Renamed the SLA output file to ".sl1" --- src/libslic3r/SLAPrint.cpp | 2 +- src/slic3r/GUI/BackgroundSlicingProcess.cpp | 2 +- src/slic3r/GUI/GUI_App.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 7 +------ 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d83e756e8..bad64b8fc 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -508,7 +508,7 @@ void SLAPrint::finalize() std::string SLAPrint::output_filename() const { DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders(); - return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "zip", &config); + return this->PrintBase::output_filename(m_print_config.output_filename_format.value, "sl1", &config); } namespace { diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp index 7c8164f6b..7c2f6c68c 100644 --- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp +++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp @@ -150,7 +150,7 @@ void BackgroundSlicingProcess::process_sla() if (this->set_step_started(bspsGCodeFinalize)) { if (! m_export_path.empty()) { m_sla_print->export_raster(m_export_path); - m_print->set_status(100, "Zip file exported to " + m_export_path); + m_print->set_status(100, "Masked SLA file exported to " + m_export_path); } else if (! m_upload_job.empty()) { prepare_upload(); } else { diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 76d5f849a..eb02d622c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -56,7 +56,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension) /* FT_INI */ "INI files (*.ini)|*.ini;*.INI", /* FT_SVG */ "SVG files (*.svg)|*.svg;*.SVG", - /* FT_PNGZIP */"Zipped PNG files (*.dwz)|*.dwz;*.DWZ", // This is lame, but that's what we use for SLA + /* FT_PNGZIP */"Masked SLA files (*.sl1)|*.sl1;*.SL1", }; std::string out = defaults[file_type]; diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 456811051..cddf081eb 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -557,9 +557,6 @@ void MainFrame::quick_slice(const int qs) } else if (qs & qsSaveAs) { // The following line may die if the output_filename_format template substitution fails. -// output_file = sprint->output_filepath; -// if (export_svg) -// output_file = ~s / \.[gG][cC][oO][dD][eE]$ / .svg /; auto dlg = new wxFileDialog(this, _(L("Save ")) + (qs & qsExportSVG ? _(L("SVG")) : _(L("G-code"))) + _(L(" file as:")), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), get_base_name(input_file), qs & qsExportSVG ? file_wildcards(FT_SVG) : file_wildcards(FT_GCODE), @@ -575,11 +572,9 @@ void MainFrame::quick_slice(const int qs) wxGetApp().app_config->update_last_output_dir(get_dir_name(output_file)); } else if (qs & qsExportPNG) { -// output_file = sprint->output_filepath; -// output_file = ~s / \.[gG][cC][oO][dD][eE]$ / .zip / ; auto dlg = new wxFileDialog(this, _(L("Save zip file as:")), wxGetApp().app_config->get_last_output_dir(get_dir_name(output_file)), - get_base_name(output_file), "*.zip", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); + get_base_name(output_file), "*.sl1", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (dlg->ShowModal() != wxID_OK) { dlg->Destroy(); return; From 2b03c25bc6bf0278dca19f69eb7da44693eb9fb9 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 25 Feb 2019 16:04:46 +0100 Subject: [PATCH 11/13] Pad "tilt" changed to "slope" --- src/libslic3r/PrintConfig.cpp | 8 ++++---- src/libslic3r/PrintConfig.hpp | 6 +++--- src/libslic3r/SLA/SLABasePool.cpp | 4 ++-- src/libslic3r/SLA/SLABasePool.hpp | 6 +++--- src/libslic3r/SLAPrint.cpp | 4 ++-- src/slic3r/GUI/Preset.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 +- 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index cc644fc74..7d1af094d 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2731,14 +2731,14 @@ void PrintConfigDef::init_sla_params() def->min = 0; def->default_value = new ConfigOptionFloat(1.0); - def = this->add("pad_wall_tilt", coFloat); - def->label = L("Pad wall tilt"); + def = this->add("pad_wall_slope", coFloat); + def->label = L("Pad wall slope"); def->category = L("Pad"); - def->tooltip = L("The tilt of the pad wall relative to the bed plane. " + def->tooltip = L("The slope of the pad wall relative to the bed plane. " "90 degrees means straight walls."); def->sidetext = L("degrees"); def->cli = ""; - def->min = 0.1; // What should be the minimum? + def->min = 45; def->max = 90; def->default_value = new ConfigOptionFloat(45.0); } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 21bc32ed9..9b6f72391 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1028,8 +1028,8 @@ public: // The smoothing radius of the pad edges ConfigOptionFloat pad_edge_radius /*= 1*/; - // The tilt of the pad wall... - ConfigOptionFloat pad_wall_tilt; + // The slope of the pad wall... + ConfigOptionFloat pad_wall_slope; protected: void initialize(StaticCacheBase &cache, const char *base_ptr) @@ -1056,7 +1056,7 @@ protected: OPT_PTR(pad_wall_height); OPT_PTR(pad_max_merge_distance); OPT_PTR(pad_edge_radius); - OPT_PTR(pad_wall_tilt); + OPT_PTR(pad_wall_slope); } }; diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 42b22acb9..62d8f0f30 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -601,8 +601,8 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, const double thickness = cfg.min_wall_thickness_mm; const double wingheight = cfg.min_wall_height_mm; const double fullheight = wingheight + thickness; - const double tilt = cfg.wall_tilt; - const double wingdist = wingheight / std::tan(tilt); + const double slope = cfg.wall_slope; + const double wingdist = wingheight / std::tan(slope); // scaled values const coord_t s_thickness = mm(thickness); diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp index 69b4561b1..3c88e58c8 100644 --- a/src/libslic3r/SLA/SLABasePool.hpp +++ b/src/libslic3r/SLA/SLABasePool.hpp @@ -28,17 +28,17 @@ struct PoolConfig { double min_wall_height_mm = 5; double max_merge_distance_mm = 50; double edge_radius_mm = 1; - double wall_tilt = std::atan(1.0); // Universal constant for Pi/4 + double wall_slope = std::atan(1.0); // Universal constant for Pi/4 ThrowOnCancel throw_on_cancel = [](){}; inline PoolConfig() {} - inline PoolConfig(double wt, double wh, double md, double er, double tilt): + inline PoolConfig(double wt, double wh, double md, double er, double slope): min_wall_thickness_mm(wt), min_wall_height_mm(wh), max_merge_distance_mm(md), edge_radius_mm(er), - wall_tilt(tilt) {} + wall_slope(slope) {} }; /// Calculate the pool for the mesh for SLA printing diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index bad64b8fc..827846b71 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -753,7 +753,7 @@ void SLAPrint::process() double md = po.m_config.pad_max_merge_distance.getFloat(); // Radius is disabled for now... double er = 0; // po.m_config.pad_edge_radius.getFloat(); - double tilt = po.m_config.pad_wall_tilt.getFloat() * PI / 180.0; + double tilt = po.m_config.pad_wall_slope.getFloat() * PI / 180.0; double lh = po.m_config.layer_height.getFloat(); double elevation = po.m_config.support_object_elevation.getFloat(); if(!po.m_config.supports_enable.getBool()) elevation = 0; @@ -1369,7 +1369,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vector& Preset::sla_print_options() "pad_wall_height", "pad_max_merge_distance", "pad_edge_radius", - "pad_wall_tilt", + "pad_wall_slope", "output_filename_format", "default_sla_print_profile", "compatible_printers", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 783527026..b52ddbf48 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3290,7 +3290,7 @@ void TabSLAPrint::build() optgroup->append_single_option_line("pad_max_merge_distance"); // TODO: Disabling this parameter for the beta release // optgroup->append_single_option_line("pad_edge_radius"); - optgroup->append_single_option_line("pad_wall_tilt"); + optgroup->append_single_option_line("pad_wall_slope"); page = add_options_page(_(L("Output options")), "page_white_go.png"); optgroup = page->new_optgroup(_(L("Output file"))); From feef5608b956070bf75bd5c09868650895bdf5c6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 25 Feb 2019 17:09:44 +0100 Subject: [PATCH 12/13] Workaround for mouse events sent out of order (mouse button down being sent before mouse enter) --- src/slic3r/GUI/GLCanvas3D.cpp | 67 +++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 24fcd4a70..9d4401b1f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4773,6 +4773,54 @@ void GLCanvas3D::on_timer(wxTimerEvent& evt) _perform_layer_editing_action(); } +#ifndef NDEBUG +// #define SLIC3R_DEBUG_MOUSE_EVENTS +#endif + +#ifdef SLIC3R_DEBUG_MOUSE_EVENTS +std::string format_mouse_event_debug_message(const wxMouseEvent &evt) +{ + static int idx = 0; + char buf[2048]; + std::string out; + sprintf(buf, "Mouse Event %d - ", idx ++); + out = buf; + + if (evt.Entering()) + out += "Entering "; + if (evt.Leaving()) + out += "Leaving "; + if (evt.Dragging()) + out += "Dragging "; + if (evt.Moving()) + out += "Moving "; + if (evt.Magnify()) + out += "Magnify "; + if (evt.LeftDown()) + out += "LeftDown "; + if (evt.LeftUp()) + out += "LeftUp "; + if (evt.LeftDClick()) + out += "LeftDClick "; + if (evt.MiddleDown()) + out += "MiddleDown "; + if (evt.MiddleUp()) + out += "MiddleUp "; + if (evt.MiddleDClick()) + out += "MiddleDClick "; + if (evt.RightDown()) + out += "RightDown "; + if (evt.RightUp()) + out += "RightUp "; + if (evt.RightDClick()) + out += "RightDClick "; + + sprintf(buf, "(%d, %d)", evt.GetX(), evt.GetY()); + out += buf; + return out; +} +#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ + void GLCanvas3D::on_mouse(wxMouseEvent& evt) { #if ENABLE_RETINA_GL @@ -4788,15 +4836,27 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (imgui->update_mouse_data(evt)) { m_mouse.position = evt.Leaving() ? Vec2d(-1.0, -1.0) : pos.cast(); render(); - return; +#ifdef SLIC3R_DEBUG_MOUSE_EVENTS + printf((format_mouse_event_debug_message(evt) + " - Consumed by ImGUI\n").c_str()); +#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ + return; } #endif // ENABLE_IMGUI + bool on_enter_workaround = false; if (! evt.Entering() && ! evt.Leaving() && m_mouse.position.x() == -1.0) { // Workaround for SPE-832: There seems to be a mouse event sent to the window before evt.Entering() m_mouse.position = pos.cast(); render(); - } +#ifdef SLIC3R_DEBUG_MOUSE_EVENTS + printf((format_mouse_event_debug_message(evt) + " - OnEnter workaround\n").c_str()); +#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ + on_enter_workaround = true; + } else { +#ifdef SLIC3R_DEBUG_MOUSE_EVENTS + printf((format_mouse_event_debug_message(evt) + " - other\n").c_str()); +#endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ + } if (m_picking_enabled) _set_current(); @@ -5244,6 +5304,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else evt.Skip(); + + if (on_enter_workaround) + m_mouse.position = Vec2d(-1., -1.); } void GLCanvas3D::on_paint(wxPaintEvent& evt) From 7067cef812f69516548620023d763738a209e047 Mon Sep 17 00:00:00 2001 From: Vojtech Kral Date: Mon, 25 Feb 2019 16:47:49 +0100 Subject: [PATCH 13/13] imgui: Take orange color radiance down a notch --- src/slic3r/GUI/ImGuiWrapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 61baf352b..7f95b6c28 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -405,8 +405,8 @@ void ImGuiWrapper::init_style() static const unsigned COL_GREY_DARK = 0x444444ff; static const unsigned COL_GREY_LIGHT = 0x666666ff; - static const unsigned COL_ORANGE_DARK = 0xba5418ff; - static const unsigned COL_ORANGE_LIGHT = 0xff6f22ff; + static const unsigned COL_ORANGE_DARK = 0xc16737ff; + static const unsigned COL_ORANGE_LIGHT = 0xff7d38ff; // Generics set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK);