diff --git a/resources/icons/add.svg b/resources/icons/add.svg new file mode 100644 index 000000000..8a9b253de --- /dev/null +++ b/resources/icons/add.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/resources/icons/arrange.svg b/resources/icons/arrange.svg new file mode 100644 index 000000000..4f30e979e --- /dev/null +++ b/resources/icons/arrange.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/delete_all.svg b/resources/icons/delete_all.svg new file mode 100644 index 000000000..80e2e503c --- /dev/null +++ b/resources/icons/delete_all.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/instance_add.svg b/resources/icons/instance_add.svg new file mode 100644 index 000000000..5ef492cfa --- /dev/null +++ b/resources/icons/instance_add.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + diff --git a/resources/icons/instance_remove.svg b/resources/icons/instance_remove.svg new file mode 100644 index 000000000..466752ea8 --- /dev/null +++ b/resources/icons/instance_remove.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + diff --git a/resources/icons/layers.svg b/resources/icons/layers.svg new file mode 100644 index 000000000..7718a8cbd --- /dev/null +++ b/resources/icons/layers.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/icons/remove.svg b/resources/icons/remove.svg new file mode 100644 index 000000000..acd21256c --- /dev/null +++ b/resources/icons/remove.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + diff --git a/resources/icons/split_objects.svg b/resources/icons/split_objects.svg new file mode 100644 index 000000000..a7ccc5df8 --- /dev/null +++ b/resources/icons/split_objects.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/resources/icons/split_parts.svg b/resources/icons/split_parts.svg new file mode 100644 index 000000000..82a292770 --- /dev/null +++ b/resources/icons/split_parts.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + 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/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 7286c5cd8..7d1af094d 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_slope", coFloat); + def->label = L("Pad wall slope"); + def->category = L("Pad"); + 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 = 45; + 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..9b6f72391 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 slope of the pad wall... + ConfigOptionFloat pad_wall_slope; + 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_slope); } }; diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 6f85c43dc..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 = PI/4; - 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); @@ -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..3c88e58c8 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_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): + 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) {} + edge_radius_mm(er), + wall_slope(slope) {} }; /// Calculate the pool for the mesh for SLA printing diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 3e36dfd85..913e9beda 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -594,7 +595,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: @@ -620,9 +621,20 @@ 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 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)}; + b = {v(Y), v(Z), v(X)}; + } + 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 @@ -647,18 +659,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); @@ -693,8 +701,18 @@ 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); + // 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))) { + 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); + } // circle portions std::vector phis(samples); @@ -1156,16 +1174,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, @@ -1179,9 +1197,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); @@ -1192,10 +1210,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); @@ -1207,9 +1225,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 @@ -1230,32 +1254,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 @@ -1263,6 +1322,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; @@ -1279,7 +1339,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, @@ -1292,7 +1352,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, @@ -1311,7 +1371,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, @@ -1320,7 +1380,8 @@ bool SLASupportTree::generate(const PointSet &points, std::vector& gndheight, ClusteredPoints& ground_clusters, Result& result - ) { + ) + { /* ******************************************************** */ /* Classification */ @@ -1331,11 +1392,11 @@ 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++) { - tifcl(); + thr(); auto& head = result.head(i); Vec3d dir(0, 0, -1); @@ -1344,9 +1405,38 @@ 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. + // the head width so that the pillar can be deployed. while(!accept && head.width_mm > 0) { Vec3d startpoint = head.junction_point(); @@ -1358,11 +1448,18 @@ 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; } 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; @@ -1417,9 +1514,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 @@ -1492,7 +1589,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, @@ -1508,7 +1605,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 @@ -1519,9 +1616,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))); }); @@ -1542,7 +1639,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]; @@ -1566,12 +1663,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) @@ -1610,7 +1707,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(); @@ -1676,7 +1773,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, @@ -1688,7 +1785,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)); @@ -1697,7 +1794,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; @@ -1723,7 +1820,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); @@ -1738,19 +1835,19 @@ 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 // 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; @@ -1807,7 +1904,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, @@ -1822,7 +1919,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 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 diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d81498916..827846b71 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. @@ -484,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 { @@ -727,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_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; - 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); @@ -742,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; @@ -1344,6 +1369,7 @@ bool SLAPrintObject::invalidate_state_by_config_options(const std::vectorset_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/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; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c8707e75f..9fc1d1e3a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2405,20 +2405,37 @@ void GLCanvas3D::Selection::_ensure_on_bed() } } +#if ENABLE_SVG_ICONS +const float GLCanvas3D::Gizmos::Default_Icons_Size = 64; +#endif // ENABLE_SVG_ICONS + GLCanvas3D::Gizmos::Gizmos() : m_enabled(false) +#if ENABLE_SVG_ICONS + , m_icons_texture_dirty(true) +#endif // ENABLE_SVG_ICONS , m_current(Undefined) +#if ENABLE_SVG_ICONS + , m_overlay_icons_size(Default_Icons_Size) + , m_overlay_scale(1.0f) + , m_overlay_border(5.0f) + , m_overlay_gap_y(5.0f) +{ +} +#else { set_overlay_scale(1.0); } +#endif // ENABLE_SVG_ICONS GLCanvas3D::Gizmos::~Gizmos() { - _reset(); + reset(); } bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) { +#if !ENABLE_SVG_ICONS m_icons_texture.metadata.filename = "gizmos.png"; m_icons_texture.metadata.icon_size = 64; @@ -2426,10 +2443,11 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) { if (!m_icons_texture.texture.load_from_file(resources_dir() + "/icons/" + m_icons_texture.metadata.filename, false)) { - _reset(); + reset(); return false; } } +#endif // !ENABLE_SVG_ICONS m_background_texture.metadata.filename = "toolbar_background.png"; m_background_texture.metadata.left = 16; @@ -2441,12 +2459,16 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) { if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false)) { - _reset(); + reset(); return false; } } +#if ENABLE_SVG_ICONS + GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "add.svg", 0); +#else GLGizmoBase* gizmo = new GLGizmoMove3D(parent, 0); +#endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; @@ -2455,7 +2477,11 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); +#if ENABLE_SVG_ICONS + gizmo = new GLGizmoScale3D(parent, "remove.svg", 1); +#else gizmo = new GLGizmoScale3D(parent, 1); +#endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; @@ -2464,49 +2490,65 @@ bool GLCanvas3D::Gizmos::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); +#if ENABLE_SVG_ICONS + gizmo = new GLGizmoRotate3D(parent, "delete_all.svg", 2); +#else gizmo = new GLGizmoRotate3D(parent, 2); +#endif // ENABLE_SVG_ICONS if (gizmo == nullptr) { - _reset(); + reset(); return false; } if (!gizmo->init()) { - _reset(); + reset(); return false; } m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); +#if ENABLE_SVG_ICONS + gizmo = new GLGizmoFlatten(parent, "arrange.svg", 3); +#else gizmo = new GLGizmoFlatten(parent, 3); +#endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) { - _reset(); + reset(); return false; } m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); +#if ENABLE_SVG_ICONS + gizmo = new GLGizmoCut(parent, "instance_add.svg", 4); +#else gizmo = new GLGizmoCut(parent, 4); +#endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) { - _reset(); + reset(); return false; } m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); +#if ENABLE_SVG_ICONS + gizmo = new GLGizmoSlaSupports(parent, "instance_remove.svg", 5); +#else gizmo = new GLGizmoSlaSupports(parent, 5); +#endif // ENABLE_SVG_ICONS if (gizmo == nullptr) return false; if (!gizmo->init()) { - _reset(); + reset(); return false; } @@ -2525,11 +2567,30 @@ void GLCanvas3D::Gizmos::set_enabled(bool enable) m_enabled = enable; } +#if ENABLE_SVG_ICONS +void GLCanvas3D::Gizmos::set_overlay_icon_size(float size) +{ + if (m_overlay_icons_size != size) + { + m_overlay_icons_size = size; + m_icons_texture_dirty = true; + } +} +#endif // ENABLE_SVG_ICONS + void GLCanvas3D::Gizmos::set_overlay_scale(float scale) { +#if ENABLE_SVG_ICONS + if (m_overlay_scale != scale) + { + m_overlay_scale = scale; + m_icons_texture_dirty = true; + } +#else m_overlay_icons_scale = scale; m_overlay_border = 5.0f * scale; m_overlay_gap_y = 5.0f * scale; +#endif // ENABLE_SVG_ICONS } std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const GLCanvas3D::Selection& selection) @@ -2540,24 +2601,39 @@ std::string GLCanvas3D::Gizmos::update_hover_state(const GLCanvas3D& canvas, con return name; float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = _get_total_overlay_height(); + float height = get_total_overlay_height(); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float top_y = 0.5f * (cnv_h - height) + scaled_border; +#else float top_y = 0.5f * (cnv_h - height) + m_overlay_border; - float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; +#if ENABLE_SVG_ICONS + bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +#else bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +#endif // ENABLE_SVG_ICONS if (inside) name = it->second->get_name(); if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); +#if ENABLE_SVG_ICONS + top_y += scaled_stride_y; +#else top_y += (scaled_icons_size + m_overlay_gap_y); +#endif // ENABLE_SVG_ICONS } return name; @@ -2569,17 +2645,29 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec return; float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height) + m_overlay_border; + float height = get_total_overlay_height(); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float top_y = 0.5f * (cnv_h - height) + scaled_border; +#else + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; +#if ENABLE_SVG_ICONS + bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +#else bool inside = (m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); +#endif // ENABLE_SVG_ICONS if (it->second->is_activable(selection) && inside) { if ((it->second->get_state() == GLGizmoBase::On)) @@ -2596,7 +2684,11 @@ void GLCanvas3D::Gizmos::update_on_off_state(const GLCanvas3D& canvas, const Vec else it->second->set_state(GLGizmoBase::Off); +#if ENABLE_SVG_ICONS + top_y += scaled_stride_y; +#else top_y += (scaled_icons_size + m_overlay_gap_y); +#endif // ENABLE_SVG_ICONS } GizmosMap::iterator it = m_gizmos.find(m_current); @@ -2667,20 +2759,36 @@ bool GLCanvas3D::Gizmos::overlay_contains_mouse(const GLCanvas3D& canvas, const return false; float cnv_h = (float)canvas.get_canvas_size().get_height(); - float height = _get_total_overlay_height(); - float top_y = 0.5f * (cnv_h - height) + m_overlay_border; + float height = get_total_overlay_height(); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float top_y = 0.5f * (cnv_h - height) + scaled_border; +#else + float top_y = 0.5f * (cnv_h - height) + m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; +#if ENABLE_SVG_ICONS + if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) +#else if ((m_overlay_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= m_overlay_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) +#endif // ENABLE_SVG_ICONS return true; +#if ENABLE_SVG_ICONS + top_y += scaled_stride_y; +#else top_y += (scaled_icons_size + m_overlay_gap_y); +#endif // ENABLE_SVG_ICONS } return false; @@ -2691,7 +2799,7 @@ bool GLCanvas3D::Gizmos::grabber_contains_mouse() const if (!m_enabled) return false; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; } @@ -2700,7 +2808,7 @@ void GLCanvas3D::Gizmos::update(const Linef3& mouse_ray, const Selection& select if (!m_enabled) return; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos, shift_down), selection); } @@ -2715,7 +2823,7 @@ bool GLCanvas3D::Gizmos::is_running() const if (!m_enabled) return false; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; } @@ -2765,7 +2873,7 @@ bool GLCanvas3D::Gizmos::is_dragging() const if (!m_enabled) return false; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); return (curr != nullptr) ? curr->is_dragging() : false; } @@ -2774,7 +2882,7 @@ void GLCanvas3D::Gizmos::start_dragging(const GLCanvas3D::Selection& selection) if (!m_enabled) return; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->start_dragging(selection); } @@ -2784,7 +2892,7 @@ void GLCanvas3D::Gizmos::stop_dragging() if (!m_enabled) return; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->stop_dragging(); } @@ -2884,7 +2992,7 @@ void GLCanvas3D::Gizmos::render_current_gizmo(const GLCanvas3D::Selection& selec if (!m_enabled) return; - _render_current_gizmo(selection); + do_render_current_gizmo(selection); } void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const GLCanvas3D::Selection& selection) const @@ -2892,7 +3000,7 @@ void GLCanvas3D::Gizmos::render_current_gizmo_for_picking_pass(const GLCanvas3D: if (!m_enabled) return; - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->render_for_picking(selection); } @@ -2902,12 +3010,17 @@ void GLCanvas3D::Gizmos::render_overlay(const GLCanvas3D& canvas, const GLCanvas if (!m_enabled) return; +#if ENABLE_SVG_ICONS + if (m_icons_texture_dirty) + generate_icons_texture(); +#endif // ENABLE_SVG_ICONS + ::glDisable(GL_DEPTH_TEST); ::glPushMatrix(); ::glLoadIdentity(); - _render_overlay(canvas, selection); + do_render_overlay(canvas, selection); ::glPopMatrix(); } @@ -2921,7 +3034,7 @@ void GLCanvas3D::Gizmos::create_external_gizmo_widgets(wxWindow *parent) } #endif // not ENABLE_IMGUI -void GLCanvas3D::Gizmos::_reset() +void GLCanvas3D::Gizmos::reset() { for (GizmosMap::value_type& gizmo : m_gizmos) { @@ -2932,7 +3045,7 @@ void GLCanvas3D::Gizmos::_reset() m_gizmos.clear(); } -void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::do_render_overlay(const GLCanvas3D& canvas, const GLCanvas3D::Selection& selection) const { if (m_gizmos.empty()) return; @@ -2944,15 +3057,19 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float zoom = canvas.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; - float height = _get_total_overlay_height(); + float height = get_total_overlay_height(); +#if ENABLE_SVG_ICONS + float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom; +#else float scaled_border = m_overlay_border * inv_zoom; +#endif // ENABLE_SVG_ICONS float top_x = (-0.5f * cnv_w) * inv_zoom; float top_y = (0.5f * height) * inv_zoom; float left = top_x; float top = top_y; - float right = left + _get_total_overlay_width() * inv_zoom; + float right = left + get_total_overlay_width() * inv_zoom; float bottom = top - height * inv_zoom; // renders background @@ -3021,6 +3138,16 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva } } +#if ENABLE_SVG_ICONS + top_x += scaled_border; + top_y -= scaled_border; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom; + + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + unsigned int icons_texture_id = m_icons_texture.get_id(); + unsigned int texture_size = m_icons_texture.get_width(); +#else top_x += m_overlay_border * inv_zoom; top_y -= m_overlay_border * inv_zoom; float scaled_gap_y = m_overlay_gap_y * inv_zoom; @@ -3028,8 +3155,14 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale * inv_zoom; unsigned int icons_texture_id = m_icons_texture.texture.get_id(); unsigned int texture_size = m_icons_texture.texture.get_width(); +#endif // ENABLE_SVG_ICONS float inv_texture_size = (texture_size != 0) ? 1.0f / (float)texture_size : 0.0f; +#if ENABLE_SVG_ICONS + if ((icons_texture_id == 0) || (texture_size <= 0)) + return; +#endif // ENABLE_SVG_ICONS + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) @@ -3038,7 +3171,11 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva unsigned int sprite_id = it->second->get_sprite_id(); GLGizmoBase::EState state = it->second->get_state(); +#if ENABLE_SVG_ICONS + float uv_icon_size = m_overlay_icons_size * inv_texture_size; +#else float uv_icon_size = (float)m_icons_texture.metadata.icon_size * inv_texture_size; +#endif // ENABLE_SVG_ICONS float top = sprite_id * uv_icon_size; float left = state * uv_icon_size; float bottom = top + uv_icon_size; @@ -3047,48 +3184,98 @@ void GLCanvas3D::Gizmos::_render_overlay(const GLCanvas3D& canvas, const GLCanva GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { left, bottom }, { right, bottom }, { right, top }, { left, top } }); #if ENABLE_IMGUI if (it->second->get_state() == GLGizmoBase::On) +#if ENABLE_SVG_ICONS + it->second->render_input_window(2.0f * scaled_border + scaled_icons_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); +#else it->second->render_input_window(2.0f * m_overlay_border + scaled_icons_size * zoom, 0.5f * cnv_h - top_y * zoom, selection); +#endif // ENABLE_SVG_ICONS #endif // ENABLE_IMGUI +#if ENABLE_SVG_ICONS + top_y -= scaled_stride_y; +#else top_y -= (scaled_icons_size + scaled_gap_y); +#endif // ENABLE_SVG_ICONS } } -void GLCanvas3D::Gizmos::_render_current_gizmo(const GLCanvas3D::Selection& selection) const +void GLCanvas3D::Gizmos::do_render_current_gizmo(const GLCanvas3D::Selection& selection) const { - GLGizmoBase* curr = _get_current(); + GLGizmoBase* curr = get_current(); if (curr != nullptr) curr->render(selection); } -float GLCanvas3D::Gizmos::_get_total_overlay_height() const +float GLCanvas3D::Gizmos::get_total_overlay_height() const { +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float height = 2.0f * scaled_border; +#else float height = 2.0f * m_overlay_border; float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if ((it->second == nullptr) || !it->second->is_selectable()) continue; +#if ENABLE_SVG_ICONS + height += scaled_stride_y; +#else height += (scaled_icons_size + m_overlay_gap_y); +#endif // ENABLE_SVG_ICONS } +#if ENABLE_SVG_ICONS + return height - scaled_gap_y; +#else return height - m_overlay_gap_y; +#endif // ENABLE_SVG_ICONS } -float GLCanvas3D::Gizmos::_get_total_overlay_width() const +float GLCanvas3D::Gizmos::get_total_overlay_width() const { - float max_icon_width = std::max(max_icon_width, (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale); - return max_icon_width + 2.0f * m_overlay_border; +#if ENABLE_SVG_ICONS + return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale; +#else + return (float)m_icons_texture.metadata.icon_size * m_overlay_icons_scale + 2.0f * m_overlay_border; +#endif // ENABLE_SVG_ICONS } -GLGizmoBase* GLCanvas3D::Gizmos::_get_current() const +GLGizmoBase* GLCanvas3D::Gizmos::get_current() const { GizmosMap::const_iterator it = m_gizmos.find(m_current); return (it != m_gizmos.end()) ? it->second : nullptr; } +#if ENABLE_SVG_ICONS +bool GLCanvas3D::Gizmos::generate_icons_texture() const +{ + std::string path = resources_dir() + "/icons/"; + std::vector filenames; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + { + if (it->second != nullptr) + { + const std::string& svg_file = it->second->get_svg_file(); + if (!svg_file.empty()) + filenames.push_back(path + svg_file); + } + } + + bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, GLGizmoBase::Num_States, (unsigned int)(m_overlay_icons_size * m_overlay_scale)); + if (res) + m_icons_texture_dirty = false; + + return res; +} +#endif // ENABLE_SVG_ICONS + const unsigned char GLCanvas3D::WarningTexture::Background_Color[3] = { 9, 91, 134 }; const unsigned char GLCanvas3D::WarningTexture::Opacity = 255; @@ -3197,7 +3384,7 @@ bool GLCanvas3D::WarningTexture::_generate(const std::string& msg, const GLCanva ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); ::glBindTexture(GL_TEXTURE_2D, 0); return true; @@ -3450,7 +3637,7 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); ::glBindTexture(GL_TEXTURE_2D, 0); return true; @@ -4054,7 +4241,9 @@ void GLCanvas3D::render() _render_gizmos_overlay(); _render_warning_texture(); _render_legend_texture(); +#if !ENABLE_SVG_ICONS _resize_toolbars(); +#endif // !ENABLE_SVG_ICONS _render_toolbar(); _render_view_toolbar(); if (m_layers_editing.last_object_id >= 0) @@ -4647,7 +4836,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__ */ @@ -4670,11 +4871,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': @@ -4769,6 +4984,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 @@ -4784,15 +5047,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(); @@ -5240,6 +5515,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) @@ -5588,14 +5866,11 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.is_enabled()) return true; +#if !ENABLE_SVG_ICONS ItemsIconsTexture::Metadata icons_data; icons_data.filename = "toolbar.png"; icons_data.icon_size = 37; - -// icons_data.filename = "toolbar141.png"; -// icons_data.icon_size = 52; -// icons_data.icon_border_size = 0; -// icons_data.icon_gap_size = 0; +#endif // !ENABLE_SVG_ICONS BackgroundTexture::Metadata background_data; background_data.filename = "toolbar_background.png"; @@ -5604,7 +5879,11 @@ bool GLCanvas3D::_init_toolbar() background_data.right = 16; background_data.bottom = 16; +#if ENABLE_SVG_ICONS + if (!m_toolbar.init(background_data)) +#else if (!m_toolbar.init(icons_data, background_data)) +#endif // ENABLE_SVG_ICONS { // unable to init the toolbar texture, disable it m_toolbar.set_enabled(false); @@ -5621,6 +5900,9 @@ bool GLCanvas3D::_init_toolbar() GLToolbarItem::Data item; item.name = "add"; +#if ENABLE_SVG_ICONS + item.svg_file = "add.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Add...") + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; item.action_event = EVT_GLTOOLBAR_ADD; @@ -5628,6 +5910,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "delete"; +#if ENABLE_SVG_ICONS + item.svg_file = "remove.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Delete") + " [Del]"; item.sprite_id = 1; item.action_event = EVT_GLTOOLBAR_DELETE; @@ -5635,6 +5920,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "deleteall"; +#if ENABLE_SVG_ICONS + item.svg_file = "delete_all.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Delete all") + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; item.sprite_id = 2; item.action_event = EVT_GLTOOLBAR_DELETE_ALL; @@ -5642,6 +5930,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "arrange"; +#if ENABLE_SVG_ICONS + item.svg_file = "arrange.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Arrange [A]"); item.sprite_id = 3; item.action_event = EVT_GLTOOLBAR_ARRANGE; @@ -5652,6 +5943,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "more"; +#if ENABLE_SVG_ICONS + item.svg_file = "instance_add.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Add instance [+]"); item.sprite_id = 4; item.action_event = EVT_GLTOOLBAR_MORE; @@ -5659,6 +5953,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "fewer"; +#if ENABLE_SVG_ICONS + item.svg_file = "instance_remove.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Remove instance [-]"); item.sprite_id = 5; item.action_event = EVT_GLTOOLBAR_FEWER; @@ -5669,6 +5966,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "splitobjects"; +#if ENABLE_SVG_ICONS + item.svg_file = "split_objects.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Split to objects"); item.sprite_id = 6; item.action_event = EVT_GLTOOLBAR_SPLIT_OBJECTS; @@ -5676,6 +5976,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "splitvolumes"; +#if ENABLE_SVG_ICONS + item.svg_file = "split_parts.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Split to parts"); item.sprite_id = 8; item.action_event = EVT_GLTOOLBAR_SPLIT_VOLUMES; @@ -5686,6 +5989,9 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "layersediting"; +#if ENABLE_SVG_ICONS + item.svg_file = "layers.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Layers editing"); item.sprite_id = 7; item.is_toggable = true; @@ -6177,11 +6483,63 @@ void GLCanvas3D::_render_gizmos_overlay() const void GLCanvas3D::_render_toolbar() const { +#if ENABLE_SVG_ICONS +#if ENABLE_RETINA_GL + m_toolbar.set_scale(m_retina_helper->get_scale_factor()); +#else + m_toolbar.set_scale(m_canvas->GetContentScaleFactor()); +#endif // ENABLE_RETINA_GL + + Size cnv_size = get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + GLToolbar::Layout::EOrientation orientation = m_toolbar.get_layout_orientation(); + + float top = 0.0f; + float left = 0.0f; + switch (m_toolbar.get_layout_type()) + { + default: + case GLToolbar::Layout::Horizontal: + { + // centers the toolbar on the top edge of the 3d scene + if (orientation == GLToolbar::Layout::Top) + { + top = 0.5f * (float)cnv_size.get_height() * inv_zoom; + left = -0.5f * m_toolbar.get_width() * inv_zoom; + } + else + { + top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; + left = -0.5f * m_toolbar.get_width() * inv_zoom; + } + break; + } + case GLToolbar::Layout::Vertical: + { + // centers the toolbar on the right edge of the 3d scene + if (orientation == GLToolbar::Layout::Left) + { + top = 0.5f * m_toolbar.get_height() * inv_zoom; + left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom; + } + else + { + top = 0.5f * m_toolbar.get_height() * inv_zoom; + left = (0.5f * (float)cnv_size.get_width() - m_toolbar.get_width()) * inv_zoom; + } + break; + } + } + m_toolbar.set_position(top, left); +#else #if ENABLE_RETINA_GL m_toolbar.set_icons_scale(m_retina_helper->get_scale_factor()); #else m_toolbar.set_icons_scale(m_canvas->GetContentScaleFactor()); #endif /* __WXMSW__ */ +#endif // ENABLE_SVG_ICONS m_toolbar.render(*this); } @@ -6189,11 +6547,28 @@ void GLCanvas3D::_render_toolbar() const void GLCanvas3D::_render_view_toolbar() const { if (m_view_toolbar != nullptr) { +#if ENABLE_SVG_ICONS +#if ENABLE_RETINA_GL + m_view_toolbar->set_scale(m_retina_helper->get_scale_factor()); +#else + m_view_toolbar->set_scale(m_canvas->GetContentScaleFactor()); +#endif // ENABLE_RETINA_GL + + Size cnv_size = get_canvas_size(); + float zoom = get_camera_zoom(); + float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; + + // places the toolbar on the bottom-left corner of the 3d scene + float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar->get_height()) * inv_zoom; + float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; + m_view_toolbar->set_position(top, left); +#else #if ENABLE_RETINA_GL m_view_toolbar->set_icons_scale(m_retina_helper->get_scale_factor()); #else m_view_toolbar->set_icons_scale(m_canvas->GetContentScaleFactor()); #endif /* __WXMSW__ */ +#endif // ENABLE_SVG_ICONS m_view_toolbar->render(*this); } } @@ -7682,6 +8057,7 @@ bool GLCanvas3D::_is_any_volume_outside() const return false; } +#if !ENABLE_SVG_ICONS void GLCanvas3D::_resize_toolbars() const { Size cnv_size = get_canvas_size(); @@ -7749,6 +8125,7 @@ void GLCanvas3D::_resize_toolbars() const m_view_toolbar->set_position(top, left); } } +#endif // !ENABLE_SVG_ICONS const Print* GLCanvas3D::fff_print() const { diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d322f5e30..096789d4a 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 }; @@ -631,6 +635,10 @@ private: class Gizmos { public: +#if ENABLE_SVG_ICONS + static const float Default_Icons_Size; +#endif // ENABLE_SVG_ICONS + enum EType : unsigned char { Undefined, @@ -647,11 +655,21 @@ private: bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; +#if ENABLE_SVG_ICONS + mutable GLTexture m_icons_texture; + mutable bool m_icons_texture_dirty; +#else ItemsIconsTexture m_icons_texture; +#endif // ENABLE_SVG_ICONS BackgroundTexture m_background_texture; EType m_current; +#if ENABLE_SVG_ICONS + float m_overlay_icons_size; + float m_overlay_scale; +#else float m_overlay_icons_scale; +#endif // ENABLE_SVG_ICONS float m_overlay_border; float m_overlay_gap_y; @@ -664,6 +682,9 @@ private: bool is_enabled() const; void set_enabled(bool enable); +#if ENABLE_SVG_ICONS + void set_overlay_icon_size(float size); +#endif // ENABLE_SVG_ICONS void set_overlay_scale(float scale); std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); @@ -713,15 +734,19 @@ private: #endif // not ENABLE_IMGUI private: - void _reset(); + void reset(); - void _render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; - void _render_current_gizmo(const Selection& selection) const; + void do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; + void do_render_current_gizmo(const Selection& selection) const; - float _get_total_overlay_height() const; - float _get_total_overlay_width() const; + float get_total_overlay_height() const; + float get_total_overlay_width() const; - GLGizmoBase* _get_current() const; + GLGizmoBase* get_current() const; + +#if ENABLE_SVG_ICONS + bool generate_icons_texture() const; +#endif // ENABLE_SVG_ICONS }; struct SlaCap @@ -1092,7 +1117,9 @@ private: bool _is_any_volume_outside() const; +#if !ENABLE_SVG_ICONS void _resize_toolbars() const; +#endif // !ENABLE_SVG_ICONS static std::vector _parse_colors(const std::vector& colors); diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index fa5ff37f8..b7573410b 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -157,11 +157,18 @@ void GLGizmoBase::Grabber::render_face(float half_size) const ::glEnd(); } +#if ENABLE_SVG_ICONS +GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) +#else GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id) +#endif // ENABLE_SVG_ICONS : m_parent(parent) , m_group_id(-1) , m_state(Off) , m_shortcut_key(0) +#if ENABLE_SVG_ICONS + , m_svg_file(svg_file) +#endif // ENABLE_SVG_ICONS , m_sprite_id(sprite_id) , m_hover_id(-1) , m_dragging(false) @@ -305,7 +312,11 @@ const unsigned int GLGizmoRotate::SnapRegionsCount = 8; const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) +#if ENABLE_SVG_ICONS + : GLGizmoBase(parent, "", -1) +#else : GLGizmoBase(parent, -1) +#endif // ENABLE_SVG_ICONS , m_axis(axis) , m_angle(0.0) , m_quadric(nullptr) @@ -322,7 +333,11 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) } GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) +#if ENABLE_SVG_ICONS + : GLGizmoBase(other.m_parent, other.m_svg_file, other.m_sprite_id) +#else : GLGizmoBase(other.m_parent, other.m_sprite_id) +#endif // ENABLE_SVG_ICONS , m_axis(other.m_axis) , m_angle(other.m_angle) , m_quadric(nullptr) @@ -694,8 +709,13 @@ Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, cons return transform(mouse_ray, m).intersect_plane(0.0); } +#if ENABLE_SVG_ICONS +GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) + : GLGizmoBase(parent, svg_file, sprite_id) +#else GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) +#endif // ENABLE_SVG_ICONS { m_gizmos.emplace_back(parent, GLGizmoRotate::X); m_gizmos.emplace_back(parent, GLGizmoRotate::Y); @@ -774,8 +794,13 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, const GLCanvas3D: const float GLGizmoScale3D::Offset = 5.0f; +#if ENABLE_SVG_ICONS +GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) + : GLGizmoBase(parent, svg_file, sprite_id) +#else GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) +#endif // ENABLE_SVG_ICONS , m_scale(Vec3d::Ones()) , m_snap_step(0.05) , m_starting_scale(Vec3d::Ones()) @@ -1120,8 +1145,13 @@ double GLGizmoScale3D::calc_ratio(const UpdateData& data) const const double GLGizmoMove3D::Offset = 10.0; +#if ENABLE_SVG_ICONS +GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) + : GLGizmoBase(parent, svg_file, sprite_id) +#else GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) +#endif // ENABLE_SVG_ICONS , m_displacement(Vec3d::Zero()) , m_snap_step(1.0) , m_starting_drag_position(Vec3d::Zero()) @@ -1357,8 +1387,13 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box ::glDisable(GL_LIGHTING); } +#if ENABLE_SVG_ICONS +GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) + : GLGizmoBase(parent, svg_file, sprite_id) +#else GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) +#endif // ENABLE_SVG_ICONS , m_normal(Vec3d::Zero()) , m_starting_center(Vec3d::Zero()) { @@ -1696,8 +1731,13 @@ Vec3d GLGizmoFlatten::get_flattening_normal() const return out; } +#if ENABLE_SVG_ICONS +GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) + : GLGizmoBase(parent, svg_file, sprite_id) +#else GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) +#endif // ENABLE_SVG_ICONS , m_starting_center(Vec3d::Zero()), m_quadric(nullptr) { m_quadric = ::gluNewQuadric(); @@ -1886,12 +1926,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() @@ -1967,139 +2001,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; @@ -2186,7 +2242,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"))); @@ -2230,9 +2285,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"))); @@ -2250,70 +2302,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; @@ -2379,18 +2385,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) { @@ -2399,6 +2400,8 @@ void GLGizmoSlaSupports::on_start_dragging(const GLCanvas3D::Selection& selectio } } + + void GLGizmoSlaSupports::select_point(int i) { if (i == AllPoints || i == NoPoints) { @@ -2412,6 +2415,8 @@ void GLGizmoSlaSupports::select_point(int i) } } + + void GLGizmoSlaSupports::editing_mode_discard_changes() { m_editing_mode_cache.clear(); @@ -2421,6 +2426,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 @@ -2432,8 +2439,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(); @@ -2442,6 +2455,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()) { @@ -2459,6 +2474,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 @@ -2506,8 +2549,13 @@ const double GLGizmoCut::Offset = 10.0; const double GLGizmoCut::Margin = 20.0; const std::array GLGizmoCut::GrabberColor = { 1.0, 0.5, 0.0 }; +#if ENABLE_SVG_ICONS +GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id) + : GLGizmoBase(parent, svg_file, sprite_id) +#else GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) +#endif // ENABLE_SVG_ICONS , m_cut_z(0.0) , m_max_z(0.0) #if !ENABLE_IMGUI diff --git a/src/slic3r/GUI/GLGizmo.hpp b/src/slic3r/GUI/GLGizmo.hpp index 6cf3afdab..dc632fe80 100644 --- a/src/slic3r/GUI/GLGizmo.hpp +++ b/src/slic3r/GUI/GLGizmo.hpp @@ -87,6 +87,9 @@ protected: int m_group_id; EState m_state; int m_shortcut_key; +#if ENABLE_SVG_ICONS + std::string m_svg_file; +#endif // ENABLE_SVG_ICONS unsigned int m_sprite_id; int m_hover_id; bool m_dragging; @@ -99,7 +102,11 @@ protected: #endif // ENABLE_IMGUI public: +#if ENABLE_SVG_ICONS + GLGizmoBase(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else GLGizmoBase(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS virtual ~GLGizmoBase() {} bool init() { return on_init(); } @@ -115,6 +122,10 @@ public: int get_shortcut_key() const { return m_shortcut_key; } void set_shortcut_key(int key) { m_shortcut_key = key; } +#if ENABLE_SVG_ICONS + const std::string& get_svg_file() const { return m_svg_file; } +#endif // ENABLE_SVG_ICONS + bool is_activable(const GLCanvas3D::Selection& selection) const { return on_is_activable(selection); } bool is_selectable() const { return on_is_selectable(); } @@ -242,7 +253,11 @@ class GLGizmoRotate3D : public GLGizmoBase std::vector m_gizmos; public: +#if ENABLE_SVG_ICONS + GLGizmoRotate3D(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } @@ -313,7 +328,11 @@ class GLGizmoScale3D : public GLGizmoBase BoundingBoxf3 m_starting_box; public: - GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id); +#if ENABLE_SVG_ICONS + GLGizmoScale3D(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else + GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS double get_snap_step(double step) const { return m_snap_step; } void set_snap_step(double step) { m_snap_step = step; } @@ -360,7 +379,11 @@ class GLGizmoMove3D : public GLGizmoBase GLUquadricObj* m_quadric; public: +#if ENABLE_SVG_ICONS + GLGizmoMove3D(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS virtual ~GLGizmoMove3D(); double get_snap_step(double step) const { return m_snap_step; } @@ -415,7 +438,11 @@ private: bool is_plane_update_necessary() const; public: +#if ENABLE_SVG_ICONS + GLGizmoFlatten(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS void set_flattening_data(const ModelObject* model_object); Vec3d get_flattening_normal() const; @@ -436,6 +463,7 @@ protected: }; #define SLAGIZMO_IMGUI_MODAL 0 + class GLGizmoSlaSupports : public GLGizmoBase { private: @@ -453,7 +481,7 @@ private: igl::AABB m_AABB; struct SourceDataSummary { - Vec3d mesh_first_point; + Geometry::Transformation transformation; }; // This holds information to decide whether recalculation is necessary: @@ -462,7 +490,11 @@ private: mutable Vec3d m_starting_center; public: +#if ENABLE_SVG_ICONS + GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS virtual ~GLGizmoSlaSupports(); void set_sla_support_data(ModelObject* model_object, const GLCanvas3D::Selection& selection); bool mouse_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down); @@ -487,6 +519,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.; @@ -500,9 +533,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; @@ -517,6 +547,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; @@ -555,7 +587,11 @@ class GLGizmoCut : public GLGizmoBase #endif // not ENABLE_IMGUI public: +#if ENABLE_SVG_ICONS + GLGizmoCut(GLCanvas3D& parent, const std::string& svg_file, unsigned int sprite_id); +#else GLGizmoCut(GLCanvas3D& parent, unsigned int sprite_id); +#endif // ENABLE_SVG_ICONS #if !ENABLE_IMGUI virtual void create_external_gizmo_widgets(wxWindow *parent); diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index e7e20b27e..9679680d0 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -120,13 +120,13 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards unsigned int levels_count = generate_mipmaps(image); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -137,6 +137,106 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps) } #endif // ENABLE_TEXTURES_FROM_SVG +#if ENABLE_SVG_ICONS +bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector& filenames, unsigned int num_states, unsigned int sprite_size_px) +{ + static int pass = 0; + ++pass; + + reset(); + + if (filenames.empty() || (num_states == 0) || (sprite_size_px == 0)) + return false; + + m_width = (int)sprite_size_px * std::max((int)(num_states), (int)(filenames.size())); + m_height = m_width; + int n_pixels = m_width * m_height; + + if (n_pixels <= 0) + { + reset(); + return false; + } + + std::vector data(n_pixels * 4, 0); + std::vector sprite_data(sprite_size_px * sprite_size_px * 4, 0); + + NSVGrasterizer* rast = nsvgCreateRasterizer(); + if (rast == nullptr) + { + reset(); + return false; + } + + int sprite_id = -1; + for (const std::string& filename : filenames) + { + ++sprite_id; + + if (!boost::filesystem::exists(filename)) + continue; + + if (!boost::algorithm::iends_with(filename, ".svg")) + continue; + + NSVGimage* image = nsvgParseFromFile(filename.c_str(), "px", 96.0f); + if (image == nullptr) + continue; + + float scale = (float)sprite_size_px / std::max(image->width, image->height); + + nsvgRasterize(rast, image, 0, 0, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_size_px * 4); + + int sprite_offset_px = sprite_id * sprite_size_px * m_width; + for (unsigned int i = 0; i < num_states; ++i) + { + int state_offset_px = sprite_offset_px + i * sprite_size_px; + for (int j = 0; j < sprite_size_px; ++j) + { + int data_offset = (state_offset_px + j * m_width) * 4; + ::memcpy((void*)&data.data()[data_offset], (const void*)&sprite_data.data()[j * sprite_size_px * 4], sprite_size_px * 4); + } + } + + nsvgDelete(image); + } + + nsvgDeleteRasterizer(rast); + + // sends data to gpu + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + ::glGenTextures(1, &m_id); + ::glBindTexture(GL_TEXTURE_2D, m_id); + ::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)m_width, (GLsizei)m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + ::glBindTexture(GL_TEXTURE_2D, 0); + + + m_source = filenames.front(); + + wxImage output(m_width, m_height); + output.InitAlpha(); + + for (int h = 0; h < m_height; ++h) + { + for (int w = 0; w < m_width; ++w) + { + int px = h * m_width + w; + int px_byte = px * 4; + output.SetRGB(w, h, data.data()[px_byte + 0], data.data()[px_byte + 1], data.data()[px_byte + 2]); + output.SetAlpha(w, h, data.data()[px_byte + 3]); + } + } + + output.SaveFile("C:/prusa/slic3r/svg_icons/temp/test_" + std::to_string(pass) + ".png", wxBITMAP_TYPE_PNG); + + return true; +} +#endif // ENABLE_SVG_ICONS + void GLTexture::reset() { if (m_id != 0) @@ -265,13 +365,13 @@ bool GLTexture::load_from_png(const std::string& filename, bool use_mipmaps) { // we manually generate mipmaps because glGenerateMipmap() function is not reliable on all graphics cards unsigned int levels_count = generate_mipmaps(image); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + levels_count); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, levels_count); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); @@ -340,13 +440,13 @@ bool GLTexture::load_from_svg(const std::string& filename, bool use_mipmaps, uns ::glTexImage2D(GL_TEXTURE_2D, level, GL_RGBA, (GLsizei)lod_w, (GLsizei)lod_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, (const void*)data.data()); } - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1 + level); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, level); ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); } else { ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); } ::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index af41ac342..55e5c4c07 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -41,6 +41,9 @@ namespace GUI { #if ENABLE_TEXTURES_FROM_SVG bool load_from_svg_file(const std::string& filename, bool use_mipmaps, unsigned int max_size_px); #endif // ENABLE_TEXTURES_FROM_SVG +#if ENABLE_SVG_ICONS + bool load_from_svg_files_as_sprites_array(const std::vector& filenames, unsigned int num_states, unsigned int sprite_size_px); +#endif // ENABLE_SVG_ICONS void reset(); unsigned int get_id() const { return m_id; } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 4cfa9a879..deb287c6a 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -32,6 +32,9 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); GLToolbarItem::Data::Data() : name("") +#if ENABLE_SVG_ICONS + , svg_file("") +#endif // ENABLE_SVG_ICONS , tooltip("") , sprite_id(-1) , is_toggable(false) @@ -78,11 +81,13 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int texture_size, unsigned i return uvs; } +#if !ENABLE_SVG_ICONS ItemsIconsTexture::Metadata::Metadata() : filename("") , icon_size(0) { } +#endif // !ENABLE_SVG_ICONS BackgroundTexture::Metadata::Metadata() : filename("") @@ -93,6 +98,10 @@ BackgroundTexture::Metadata::Metadata() { } +#if ENABLE_SVG_ICONS +const float GLToolbar::Default_Icons_Size = 64.0f; +#endif // ENABLE_SVG_ICONS + GLToolbar::Layout::Layout() : type(Horizontal) , orientation(Center) @@ -101,7 +110,12 @@ GLToolbar::Layout::Layout() , border(0.0f) , separator_size(0.0f) , gap_size(0.0f) +#if ENABLE_SVG_ICONS + , icons_size(Default_Icons_Size) + , scale(1.0f) +#else , icons_scale(1.0f) +#endif // ENABLE_SVG_ICONS , width(0.0f) , height(0.0f) , dirty(true) @@ -111,6 +125,9 @@ GLToolbar::Layout::Layout() GLToolbar::GLToolbar(GLToolbar::EType type) : m_type(type) , m_enabled(false) +#if ENABLE_SVG_ICONS + , m_icons_texture_dirty(true) +#endif // ENABLE_SVG_ICONS { } @@ -122,8 +139,19 @@ GLToolbar::~GLToolbar() } } +#if ENABLE_SVG_ICONS +bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture) +#else bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture) +#endif // ENABLE_SVG_ICONS { +#if ENABLE_SVG_ICONS + if (m_background_texture.texture.get_id() != 0) + return true; + + std::string path = resources_dir() + "/icons/"; + bool res = false; +#else if (m_icons_texture.texture.get_id() != 0) return true; @@ -131,6 +159,7 @@ bool GLToolbar::init(const ItemsIconsTexture::Metadata& icons_texture, const Bac bool res = !icons_texture.filename.empty() && m_icons_texture.texture.load_from_file(path + icons_texture.filename, false); if (res) m_icons_texture.metadata = icons_texture; +#endif // ENABLE_SVG_ICONS if (!background_texture.filename.empty()) res = m_background_texture.texture.load_from_file(path + background_texture.filename, false); @@ -186,11 +215,33 @@ void GLToolbar::set_gap_size(float size) m_layout.dirty = true; } +#if ENABLE_SVG_ICONS +void GLToolbar::set_icons_size(float size) +{ + if (m_layout.icons_size != size) + { + m_layout.icons_size = size; + m_layout.dirty = true; + m_icons_texture_dirty = true; + } +} + +void GLToolbar::set_scale(float scale) +{ + if (m_layout.scale != scale) + { + m_layout.scale = scale; + m_layout.dirty = true; + m_icons_texture_dirty = true; + } +} +#else void GLToolbar::set_icons_scale(float scale) { m_layout.icons_scale = scale; m_layout.dirty = true; } +#endif // ENABLE_SVG_ICONS bool GLToolbar::is_enabled() const { @@ -411,6 +462,11 @@ void GLToolbar::render(const GLCanvas3D& parent) const if (!m_enabled || m_items.empty()) return; +#if ENABLE_SVG_ICONS + if (m_icons_texture_dirty) + generate_icons_texture(); +#endif // ENABLE_SVG_ICONS + ::glDisable(GL_DEPTH_TEST); ::glPushMatrix(); @@ -455,12 +511,20 @@ float GLToolbar::get_width_horizontal() const float GLToolbar::get_width_vertical() const { +#if ENABLE_SVG_ICONS + return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; +#else return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale; +#endif // ENABLE_SVG_ICONS } float GLToolbar::get_height_horizontal() const { +#if ENABLE_SVG_ICONS + return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; +#else return 2.0f * m_layout.border * m_layout.icons_scale + m_icons_texture.metadata.icon_size * m_layout.icons_scale; +#endif // ENABLE_SVG_ICONS } float GLToolbar::get_height_vertical() const @@ -470,6 +534,26 @@ float GLToolbar::get_height_vertical() const float GLToolbar::get_main_size() const { +#if ENABLE_SVG_ICONS + float size = 2.0f * m_layout.border; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { +#if ENABLE_MODE_AWARE_TOOLBAR_ITEMS + if (!m_items[i]->is_visible()) + continue; +#endif // ENABLE_MODE_AWARE_TOOLBAR_ITEMS + + if (m_items[i]->is_separator()) + size += m_layout.separator_size; + else + size += (float)m_layout.icons_size; + } + + if (m_items.size() > 1) + size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; + + size *= m_layout.scale; +#else float size = 2.0f * m_layout.border * m_layout.icons_scale; for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) { @@ -486,6 +570,7 @@ float GLToolbar::get_main_size() const if (m_items.size() > 1) size += ((float)m_items.size() - 1.0f) * m_layout.gap_size * m_layout.icons_scale; +#endif // ENABLE_SVG_ICONS return size; } @@ -496,12 +581,20 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_SVG_ICONS + float factor = m_layout.scale * inv_zoom; +#else float factor = m_layout.icons_scale * inv_zoom; +#endif // ENABLE_SVG_ICONS Size cnv_size = parent.get_canvas_size(); Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_layout.icons_size * factor; +#else float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; +#endif // ENABLE_SVG_ICONS float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; @@ -595,12 +688,20 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_SVG_ICONS + float factor = m_layout.scale * inv_zoom; +#else float factor = m_layout.icons_scale * inv_zoom; +#endif // ENABLE_SVG_ICONS Size cnv_size = parent.get_canvas_size(); Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_layout.icons_size * factor; +#else float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; +#endif // ENABLE_SVG_ICONS float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; @@ -693,12 +794,20 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_SVG_ICONS + float factor = m_layout.scale * inv_zoom; +#else float factor = m_layout.icons_scale * inv_zoom; +#endif // ENABLE_SVG_ICONS Size cnv_size = parent.get_canvas_size(); Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_layout.icons_size * factor; +#else float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; +#endif // ENABLE_SVG_ICONS float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; @@ -742,12 +851,20 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_SVG_ICONS + float factor = m_layout.scale * inv_zoom; +#else float factor = m_layout.icons_scale * inv_zoom; +#endif // ENABLE_SVG_ICONS Size cnv_size = parent.get_canvas_size(); Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_layout.icons_size * factor; +#else float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; +#endif // ENABLE_SVG_ICONS float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; @@ -788,17 +905,32 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& void GLToolbar::render_horizontal(const GLCanvas3D& parent) const { +#if ENABLE_SVG_ICONS + unsigned int tex_id = m_icons_texture.get_id(); + int tex_size = m_icons_texture.get_width(); +#else unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); +#endif // ENABLE_SVG_ICONS +#if !ENABLE_SVG_ICONS if ((tex_id == 0) || (tex_size <= 0)) return; +#endif // !ENABLE_SVG_ICONS float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_SVG_ICONS + float factor = inv_zoom * m_layout.scale; +#else float factor = inv_zoom * m_layout.icons_scale; +#endif // ENABLE_SVG_ICONS +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_layout.icons_size * factor; +#else float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * factor; +#endif // ENABLE_SVG_ICONS float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; @@ -899,6 +1031,11 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const left += scaled_border; top -= scaled_border; +#if ENABLE_SVG_ICONS + if ((tex_id == 0) || (tex_size <= 0)) + return; +#endif // ENABLE_SVG_ICONS + // renders icons for (const GLToolbarItem* item : m_items) { @@ -911,7 +1048,11 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const left += separator_stride; else { +#if ENABLE_SVG_ICONS + item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, (unsigned int)m_layout.icons_size); +#else item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_size); +#endif // ENABLE_SVG_ICONS left += icon_stride; } } @@ -919,17 +1060,30 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const void GLToolbar::render_vertical(const GLCanvas3D& parent) const { +#if ENABLE_SVG_ICONS + unsigned int tex_id = m_icons_texture.get_id(); + int tex_size = m_icons_texture.get_width(); +#else unsigned int tex_id = m_icons_texture.texture.get_id(); int tex_size = m_icons_texture.texture.get_width(); +#endif // ENABLE_SVG_ICONS if ((tex_id == 0) || (tex_size <= 0)) return; float zoom = parent.get_camera_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; +#if ENABLE_SVG_ICONS + float factor = inv_zoom * m_layout.scale; +#else float factor = inv_zoom * m_layout.icons_scale; +#endif // ENABLE_SVG_ICONS +#if ENABLE_SVG_ICONS + float scaled_icons_size = m_layout.icons_size * factor; +#else float scaled_icons_size = (float)m_icons_texture.metadata.icon_size * m_layout.icons_scale * factor; +#endif // ENABLE_SVG_ICONS float scaled_separator_size = m_layout.separator_size * factor; float scaled_gap_size = m_layout.gap_size * factor; float scaled_border = m_layout.border * factor; @@ -1042,11 +1196,35 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const top -= separator_stride; else { +#if ENABLE_SVG_ICONS + item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, (unsigned int)(m_layout.icons_size * m_layout.scale)); +#else item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_size, m_icons_texture.metadata.icon_size); +#endif // ENABLE_SVG_ICONS top -= icon_stride; } } } +#if ENABLE_SVG_ICONS +bool GLToolbar::generate_icons_texture() const +{ + std::string path = resources_dir() + "/icons/"; + std::vector filenames; + for (GLToolbarItem* item : m_items) + { + const std::string& svg_file = item->get_svg_file(); + if (!svg_file.empty()) + filenames.push_back(path + svg_file); + } + + bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, GLToolbarItem::Num_States, (unsigned int)(m_layout.icons_size * m_layout.scale)); + if (res) + m_icons_texture_dirty = false; + + return res; +} +#endif // ENABLE_SVG_ICONS + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 8a9ff783c..b12b2693e 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -51,6 +51,9 @@ public: struct Data { std::string name; +#if ENABLE_SVG_ICONS + std::string svg_file; +#endif // ENABLE_SVG_ICONS std::string tooltip; unsigned int sprite_id; bool is_toggable; @@ -74,6 +77,9 @@ public: void set_state(EState state) { m_state = state; } const std::string& get_name() const { return m_data.name; } +#if ENABLE_SVG_ICONS + const std::string& get_svg_file() const { return m_data.svg_file; } +#endif // ENABLE_SVG_ICONS const std::string& get_tooltip() const { return m_data.tooltip; } void do_action(wxEvtHandler *target); @@ -96,6 +102,7 @@ private: GLTexture::Quad_UVs get_uvs(unsigned int texture_size, unsigned int icon_size) const; }; +#if !ENABLE_SVG_ICONS // items icon textures are assumed to be square and all with the same size in pixels, no internal check is done // icons are layed-out into the texture starting from the top-left corner in the same order as enum GLToolbarItem::EState // from left to right @@ -114,6 +121,7 @@ struct ItemsIconsTexture GLTexture texture; Metadata metadata; }; +#endif // !ENABLE_SVG_ICONS struct BackgroundTexture { @@ -140,6 +148,10 @@ struct BackgroundTexture class GLToolbar { public: +#if ENABLE_SVG_ICONS + static const float Default_Icons_Size; +#endif // ENABLE_SVG_ICONS + enum EType : unsigned char { Normal, @@ -173,7 +185,12 @@ public: float border; float separator_size; float gap_size; +#if ENABLE_SVG_ICONS + float icons_size; + float scale; +#else float icons_scale; +#endif // ENABLE_SVG_ICONS float width; float height; @@ -187,7 +204,12 @@ private: EType m_type; bool m_enabled; +#if ENABLE_SVG_ICONS + mutable GLTexture m_icons_texture; + mutable bool m_icons_texture_dirty; +#else ItemsIconsTexture m_icons_texture; +#endif // ENABLE_SVG_ICONS BackgroundTexture m_background_texture; mutable Layout m_layout; @@ -197,7 +219,11 @@ public: explicit GLToolbar(EType type); ~GLToolbar(); +#if ENABLE_SVG_ICONS + bool init(const BackgroundTexture::Metadata& background_texture); +#else bool init(const ItemsIconsTexture::Metadata& icons_texture, const BackgroundTexture::Metadata& background_texture); +#endif // ENABLE_SVG_ICONS Layout::EType get_layout_type() const; void set_layout_type(Layout::EType type); @@ -208,7 +234,12 @@ public: void set_border(float border); void set_separator_size(float size); void set_gap_size(float size); +#if ENABLE_SVG_ICONS + void set_icons_size(float size); + void set_scale(float scale); +#else void set_icons_scale(float scale); +#endif // ENABLE_SVG_ICONS bool is_enabled() const; void set_enabled(bool enable); @@ -253,6 +284,10 @@ private: void render_horizontal(const GLCanvas3D& parent) const; void render_vertical(const GLCanvas3D& parent) const; + +#if ENABLE_SVG_ICONS + bool generate_icons_texture() const; +#endif // ENABLE_SVG_ICONS }; } // namespace GUI 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/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); 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; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 256fc3a79..d17cce97d 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); } @@ -2449,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) @@ -2614,9 +2623,11 @@ bool Plater::priv::complit_init_part_menu() void Plater::priv::init_view_toolbar() { +#if !ENABLE_SVG_ICONS ItemsIconsTexture::Metadata icons_data; icons_data.filename = "view_toolbar.png"; icons_data.icon_size = 64; +#endif // !ENABLE_SVG_ICONS BackgroundTexture::Metadata background_data; background_data.filename = "toolbar_background.png"; @@ -2625,7 +2636,11 @@ void Plater::priv::init_view_toolbar() background_data.right = 16; background_data.bottom = 16; +#if ENABLE_SVG_ICONS + if (!view_toolbar.init(background_data)) +#else if (!view_toolbar.init(icons_data, background_data)) +#endif // ENABLE_SVG_ICONS return; view_toolbar.set_layout_orientation(GLToolbar::Layout::Bottom); @@ -2635,6 +2650,9 @@ void Plater::priv::init_view_toolbar() GLToolbarItem::Data item; item.name = "3D"; +#if ENABLE_SVG_ICONS + item.svg_file = "add.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("3D editor view") + " [" + GUI::shortkey_ctrl_prefix() + "5]"; item.sprite_id = 0; item.action_event = EVT_GLVIEWTOOLBAR_3D; @@ -2643,6 +2661,9 @@ void Plater::priv::init_view_toolbar() return; item.name = "Preview"; +#if ENABLE_SVG_ICONS + item.svg_file = "remove.svg"; +#endif // ENABLE_SVG_ICONS item.tooltip = GUI::L_str("Preview") + " [" + GUI::shortkey_ctrl_prefix() + "6]"; item.sprite_id = 1; item.action_event = EVT_GLVIEWTOOLBAR_PREVIEW; @@ -3066,6 +3087,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); } diff --git a/src/slic3r/GUI/Preset.cpp b/src/slic3r/GUI/Preset.cpp index 86d5db2a7..ccccf7626 100644 --- a/src/slic3r/GUI/Preset.cpp +++ b/src/slic3r/GUI/Preset.cpp @@ -465,6 +465,7 @@ const std::vector& Preset::sla_print_options() "pad_wall_height", "pad_max_merge_distance", "pad_edge_radius", + "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 7e9a0c3a2..b52ddbf48 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_slope"); page = add_options_page(_(L("Output options")), "page_white_go.png"); optgroup = page->new_optgroup(_(L("Output file")));