From 19b1aa081d4b81d30e5fa8eacdf5a74ff3ce3fae Mon Sep 17 00:00:00 2001 From: tamasmeszaros <meszaros.q@gmail.com> Date: Wed, 7 Nov 2018 15:29:13 +0100 Subject: [PATCH] SLAPrint concept under its way. --- src/libslic3r/CMakeLists.txt | 2 - src/libslic3r/SLA/SLARotfinder.cpp | 31 -- src/libslic3r/SLA/SLASupportTree.cpp | 51 ++- src/libslic3r/SLA/SLASupportTree.hpp | 14 + src/libslic3r/SLABasePool.cpp | 531 --------------------------- src/libslic3r/SLABasePool.hpp | 32 -- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GLGizmo.cpp | 12 +- src/slic3r/GUI/Plater.cpp | 9 +- src/slic3r/GUI/SLAPrint.cpp | 21 ++ src/slic3r/GUI/SLAPrint.hpp | 138 +++++++ 11 files changed, 225 insertions(+), 618 deletions(-) delete mode 100644 src/libslic3r/SLABasePool.cpp delete mode 100644 src/libslic3r/SLABasePool.hpp create mode 100644 src/slic3r/GUI/SLAPrint.cpp create mode 100644 src/slic3r/GUI/SLAPrint.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index be31734df..0c6ae0a62 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -149,8 +149,6 @@ add_library(libslic3r STATIC Technologies.hpp TriangleMesh.cpp TriangleMesh.hpp - SLABasePool.hpp - SLABasePool.cpp utils.cpp Utils.hpp SLA/SLABoilerPlate.hpp diff --git a/src/libslic3r/SLA/SLARotfinder.cpp b/src/libslic3r/SLA/SLARotfinder.cpp index 1ecd74e0e..05cea7748 100644 --- a/src/libslic3r/SLA/SLARotfinder.cpp +++ b/src/libslic3r/SLA/SLARotfinder.cpp @@ -10,37 +10,6 @@ namespace Slic3r { namespace sla { -EigenMesh3D to_eigenmesh(const TriangleMesh& tmesh) { - - const stl_file& stl = tmesh.stl; - - EigenMesh3D outmesh; - auto& V = outmesh.V; - auto& F = outmesh.F; - - V.resize(3*stl.stats.number_of_facets, 3); - F.resize(stl.stats.number_of_facets, 3); - for (unsigned int i=0; i<stl.stats.number_of_facets; ++i) { - const stl_facet* facet = stl.facet_start+i; - V(3*i+0, 0) = facet->vertex[0](0); V(3*i+0, 1) = - facet->vertex[0](1); V(3*i+0, 2) = facet->vertex[0](2); - V(3*i+1, 0) = facet->vertex[1](0); V(3*i+1, 1) = - facet->vertex[1](1); V(3*i+1, 2) = facet->vertex[1](2); - V(3*i+2, 0) = facet->vertex[2](0); V(3*i+2, 1) = - facet->vertex[2](1); V(3*i+2, 2) = facet->vertex[2](2); - - F(i, 0) = 3*i+0; - F(i, 1) = 3*i+1; - F(i, 2) = 3*i+2; - } - - return outmesh; -} - -inline EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { - return to_eigenmesh(modelobj.raw_mesh()); -} - std::array<double, 3> find_best_rotation(const ModelObject& modelobj, float accuracy, std::function<void(unsigned)> statuscb, diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 34821f7ae..d8cff3519 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -536,19 +536,9 @@ enum { // For indexing Eigen vectors as v(X), v(Y), v(Z) instead of numbers X, Y, Z }; -EigenMesh3D to_eigenmesh(const Model& model) { - TriangleMesh combined_mesh; +EigenMesh3D to_eigenmesh(const TriangleMesh& tmesh) { - for(ModelObject *o : model.objects) { - TriangleMesh tmp = o->raw_mesh(); - for(ModelInstance * inst: o->instances) { - TriangleMesh ttmp(tmp); - inst->transform_mesh(&ttmp); - combined_mesh.merge(ttmp); - } - } - - const stl_file& stl = combined_mesh.stl; + const stl_file& stl = tmesh.stl; EigenMesh3D outmesh; auto& V = outmesh.V; @@ -573,6 +563,27 @@ EigenMesh3D to_eigenmesh(const Model& model) { return outmesh; } +EigenMesh3D to_eigenmesh(const ModelObject& modelobj) { + return to_eigenmesh(modelobj.raw_mesh()); +} + +EigenMesh3D to_eigenmesh(const Model& model) { + TriangleMesh combined_mesh; + + for(ModelObject *o : model.objects) { + TriangleMesh tmp = o->raw_mesh(); + for(ModelInstance * inst: o->instances) { + TriangleMesh ttmp(tmp); + inst->transform_mesh(&ttmp); + combined_mesh.merge(ttmp); + } + } + + return to_eigenmesh(combined_mesh); +} + + + Vec3d model_coord(const ModelInstance& object, const Vec3f& mesh_coord) { return object.transform_vector(mesh_coord.cast<double>()); } @@ -595,13 +606,24 @@ PointSet support_points(const Model& model) { return ret; } +PointSet support_points(const ModelObject& modelobject, std::size_t instance_id) +{ + PointSet ret(modelobject.sla_support_points.size(), 3); + long i = 0; + ModelInstance *inst = modelobject.instances[instance_id]; + for(const Vec3f& msource : modelobject.sla_support_points) { + ret.row(i++) = model_coord(*inst, msource); + } + return ret; +} + double ray_mesh_intersect(const Vec3d& s, const Vec3d& dir, const EigenMesh3D& m); PointSet normals(const PointSet& points, const EigenMesh3D& mesh); -Vec2d to_vec2(const Vec3d& v3) { +inline Vec2d to_vec2(const Vec3d& v3) { return {v3(X), v3(Y)}; } @@ -1612,7 +1634,6 @@ void add_sla_supports(Model &model, << " seconds" << std::endl; bench.start(); - SLASupportTree::Impl& stree = _stree.get(); ModelObject* o = model.add_object(); o->add_instance(); @@ -1648,7 +1669,7 @@ void add_sla_supports(Model &model, << " second." << std::endl; bench.start(); - poolmesh.translate(0, 0, poolcfg.min_wall_height_mm / 2); + poolmesh.translate(.0f, .0f, float(poolcfg.min_wall_height_mm / 2)); o->add_volume(poolmesh); bench.stop(); diff --git a/src/libslic3r/SLA/SLASupportTree.hpp b/src/libslic3r/SLA/SLASupportTree.hpp index fe611a2ee..1e913cf88 100644 --- a/src/libslic3r/SLA/SLASupportTree.hpp +++ b/src/libslic3r/SLA/SLASupportTree.hpp @@ -20,6 +20,7 @@ typedef std::vector<Vec3crd> Points3; class TriangleMesh; class Model; class ModelInstance; +class ModelObject; class ExPolygon; using SliceLayer = std::vector<ExPolygon>; @@ -31,6 +32,9 @@ struct SupportConfig { // Radius in mm of the pointing side of the head. double head_front_radius_mm = 0.2; + // How much the pinhead has to penetrate the model surface + double head_penetraiton = 0.2; + // Radius of the back side of the 3d arrow. double head_back_radius_mm = 0.5; @@ -39,6 +43,10 @@ struct SupportConfig { // Radius in mm of the support pillars. // Warning: this value will be at most 65% of head_back_radius_mm + // TODO: This parameter is invalid. The pillar radius will be dynamic in + // nature. Merged pillars will have an increased thickness. This parameter + // may serve as the maximum radius, or maybe an increase when two are merged + // The default radius will be derived from head_back_radius_mm double pillar_radius_mm = 0.8; // Radius in mm of the pillar base. @@ -63,6 +71,8 @@ struct Controller { std::function<bool(void)> stopcondition = [](){ return false; }; }; +/// An index-triangle structure for libIGL functions. Also serves as an +/// alternative (raw) input format for the SLASupportTree struct EigenMesh3D { Eigen::MatrixXd V; Eigen::MatrixXi F; @@ -80,8 +90,12 @@ void create_head(TriangleMesh&, double r1_mm, double r2_mm, double width_mm); void add_sla_supports(Model& model, const SupportConfig& cfg = {}, const Controller& ctl = {}); +EigenMesh3D to_eigenmesh(const TriangleMesh& m); EigenMesh3D to_eigenmesh(const Model& model); +EigenMesh3D to_eigenmesh(const ModelObject& model); + PointSet support_points(const Model& model); +PointSet support_points(const ModelObject& modelobject, size_t instance_id = 0); /* ************************************************************************** */ diff --git a/src/libslic3r/SLABasePool.cpp b/src/libslic3r/SLABasePool.cpp deleted file mode 100644 index f3683865c..000000000 --- a/src/libslic3r/SLABasePool.cpp +++ /dev/null @@ -1,531 +0,0 @@ -#include <functional> -#include <numeric> - -#include "SLABasePool.hpp" -#include "ExPolygon.hpp" -#include "TriangleMesh.hpp" -#include "ClipperUtils.hpp" -#include "boost/log/trivial.hpp" - -//#include "SVG.hpp" - -namespace Slic3r { namespace sla { - -namespace { - -using coord_t = Point::coord_type; - -/// get the scaled clipper units for a millimeter value -inline coord_t mm(double v) { return coord_t(v/SCALING_FACTOR); } - -/// Get x and y coordinates (because we are eigenizing...) -inline coord_t x(const Point& p) { return p(0); } -inline coord_t y(const Point& p) { return p(1); } -inline coord_t& x(Point& p) { return p(0); } -inline coord_t& y(Point& p) { return p(1); } - -inline coordf_t x(const Vec3d& p) { return p(0); } -inline coordf_t y(const Vec3d& p) { return p(1); } -inline coordf_t z(const Vec3d& p) { return p(2); } -inline coordf_t& x(Vec3d& p) { return p(0); } -inline coordf_t& y(Vec3d& p) { return p(1); } -inline coordf_t& z(Vec3d& p) { return p(2); } - -inline coord_t& x(Vec3crd& p) { return p(0); } -inline coord_t& y(Vec3crd& p) { return p(1); } -inline coord_t& z(Vec3crd& p) { return p(2); } -inline coord_t x(const Vec3crd& p) { return p(0); } -inline coord_t y(const Vec3crd& p) { return p(1); } -inline coord_t z(const Vec3crd& p) { return p(2); } - -inline void triangulate(const ExPolygon& expoly, Polygons& triangles) { - expoly.triangulate_p2t(&triangles); -} - -inline Polygons triangulate(const ExPolygon& expoly) { - Polygons tri; triangulate(expoly, tri); return tri; -} - -using Indices = std::vector<Vec3crd>; - -/// Intermediate struct for a 3D mesh -struct Contour3D { - Pointf3s points; - Indices indices; - - void merge(const Contour3D& ctr) { - auto s3 = coord_t(points.size()); - auto s = coord_t(indices.size()); - - points.insert(points.end(), ctr.points.begin(), ctr.points.end()); - indices.insert(indices.end(), ctr.indices.begin(), ctr.indices.end()); - - for(auto n = s; n < indices.size(); n++) { - auto& idx = indices[n]; x(idx) += s3; y(idx) += s3; z(idx) += s3; - } - } -}; - -/// Convert the triangulation output to an intermediate mesh. -inline Contour3D convert(const Polygons& triangles, coord_t z, bool dir) { - - Pointf3s points; - points.reserve(3*triangles.size()); - Indices indices; - indices.reserve(points.size()); - - for(auto& tr : triangles) { - auto c = coord_t(points.size()), b = c++, a = c++; - if(dir) indices.emplace_back(a, b, c); - else indices.emplace_back(c, b, a); - for(auto& p : tr.points) { - points.emplace_back(unscale(x(p), y(p), z)); - } - } - - return {points, indices}; -} - -/// Only a debug function to generate top and bottom plates from a 2D shape. -/// It is not used in the algorithm directly. -inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) { - Polygons triangles = triangulate(poly); - - auto lower = convert(triangles, 0, false); - auto upper = convert(triangles, z_distance, true); - lower.merge(upper); - return lower; -} - -inline Contour3D walls(const ExPolygon& floor_plate, const ExPolygon& ceiling, - double floor_z_mm, double ceiling_z_mm) { - using std::transform; using std::back_inserter; - - ExPolygon poly; - poly.contour.points = floor_plate.contour.points; - poly.holes.emplace_back(ceiling.contour); - auto& h = poly.holes.front(); - std::reverse(h.points.begin(), h.points.end()); - Polygons tri = triangulate(poly); - - Contour3D ret; - ret.points.reserve(tri.size() * 3); - - double fz = floor_z_mm; - double cz = ceiling_z_mm; - auto& rp = ret.points; - auto& rpi = ret.indices; - ret.indices.reserve(tri.size() * 3); - - coord_t idx = 0; - - auto hlines = h.lines(); - auto is_upper = [&hlines](const Point& p) { - return std::any_of(hlines.begin(), hlines.end(), - [&p](const Line& l) { - return l.distance_to(p) < mm(0.01); - }); - }; - - std::for_each(tri.begin(), tri.end(), - [&rp, &rpi, &poly, &idx, is_upper, fz, cz](const Polygon& pp) - { - for(auto& p : pp.points) - if(is_upper(p)) - rp.emplace_back(unscale(x(p), y(p), mm(cz))); - else rp.emplace_back(unscale(x(p), y(p), mm(fz))); - - coord_t a = idx++, b = idx++, c = idx++; - if(fz > cz) rpi.emplace_back(c, b, a); - else rpi.emplace_back(a, b, c); - }); - - return ret; -} - -/// Mesh from an existing contour. -inline TriangleMesh mesh(const Contour3D& ctour) { - return {ctour.points, ctour.indices}; -} - -/// Mesh from an evaporating 3D contour -inline TriangleMesh mesh(Contour3D&& ctour) { - return {std::move(ctour.points), std::move(ctour.indices)}; -} - -/// Offsetting with clipper and smoothing the edges into a curvature. -inline void offset(ExPolygon& sh, coord_t distance) { - using ClipperLib::ClipperOffset; - using ClipperLib::jtRound; - using ClipperLib::etClosedPolygon; - using ClipperLib::Paths; - using ClipperLib::Path; - - auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh.contour); - auto&& holes = Slic3rMultiPoints_to_ClipperPaths(sh.holes); - - // If the input is not at least a triangle, we can not do this algorithm - if(ctour.size() < 3 || - std::any_of(holes.begin(), holes.end(), - [](const Path& p) { return p.size() < 3; }) - ) { - BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!"; - return; - } - - ClipperOffset offs; - offs.ArcTolerance = 0.01*mm(1); - Paths result; - offs.AddPath(ctour, jtRound, etClosedPolygon); - offs.AddPaths(holes, jtRound, etClosedPolygon); - offs.Execute(result, static_cast<double>(distance)); - - // Offsetting reverts the orientation and also removes the last vertex - // so boost will not have a closed polygon. - - bool found_the_contour = false; - sh.holes.clear(); - for(auto& r : result) { - if(ClipperLib::Orientation(r)) { - // We don't like if the offsetting generates more than one contour - // but throwing would be an overkill. Instead, we should warn the - // caller about the inability to create correct geometries - if(!found_the_contour) { - auto rr = ClipperPath_to_Slic3rPolygon(r); - sh.contour.points.swap(rr.points); - found_the_contour = true; - } else { - BOOST_LOG_TRIVIAL(warning) - << "Warning: offsetting result is invalid!"; - } - } else { - // TODO If there are multiple contours we can't be sure which hole - // belongs to the first contour. (But in this case the situation is - // bad enough to let it go...) - sh.holes.emplace_back(ClipperPath_to_Slic3rPolygon(r)); - } - } -} - -template<class ExP, class D> -inline Contour3D round_edges(const ExPolygon& base_plate, - double radius_mm, - double degrees, - double ceilheight_mm, - bool dir, - ExP&& last_offset = ExP(), D&& last_height = D()) -{ - auto ob = base_plate; - auto ob_prev = ob; - double wh = ceilheight_mm, wh_prev = wh; - Contour3D curvedwalls; - - const size_t steps = 6; // steps for 180 degrees - degrees = std::fmod(degrees, 180); - const int portion = int(steps*degrees / 90); - const double ystep_mm = radius_mm/steps; - coord_t s = dir? 1 : -1; - double xxprev = 0; - for(int i = 0; i < portion; i++) { - ob = base_plate; - - // The offset is given by the equation: x = sqrt(r^2 - y^2) - // which can be derived from the circle equation. y is the current - // height for which the offset is calculated and x is the offset itself - // r is the radius of the circle that is used to smooth the edges - - double r2 = radius_mm * radius_mm; - double y2 = steps*ystep_mm - i*ystep_mm; - y2 *= y2; - - double xx = sqrt(r2 - y2); - - offset(ob, s*mm(xx)); - wh = ceilheight_mm - i*ystep_mm; - - Contour3D pwalls; - if(xxprev < xx) pwalls = walls(ob, ob_prev, wh, wh_prev); - else pwalls = walls(ob_prev, ob, wh_prev, wh); - - curvedwalls.merge(pwalls); - ob_prev = ob; - wh_prev = wh; - xxprev = xx; - } - - last_offset = std::move(ob); - last_height = wh; - - return curvedwalls; -} - -/// Generating the concave part of the 3D pool with the bottom plate and the -/// side walls. -inline Contour3D inner_bed(const ExPolygon& poly, double depth_mm, - double begin_h_mm = 0) { - - Polygons triangles = triangulate(poly); - - coord_t depth = mm(depth_mm); - coord_t begin_h = mm(begin_h_mm); - - auto bottom = convert(triangles, -depth + begin_h, false); - auto lines = poly.lines(); - - // Generate outer walls - auto fp = [](const Point& p, Point::coord_type z) { - return unscale(x(p), y(p), z); - }; - - for(auto& l : lines) { - auto s = coord_t(bottom.points.size()); - - bottom.points.emplace_back(fp(l.a, -depth + begin_h)); - bottom.points.emplace_back(fp(l.b, -depth + begin_h)); - bottom.points.emplace_back(fp(l.a, begin_h)); - bottom.points.emplace_back(fp(l.b, begin_h)); - - bottom.indices.emplace_back(s + 3, s + 1, s); - bottom.indices.emplace_back(s + 2, s + 3, s); - } - - return bottom; -} - -/// Unification of polygons (with clipper) preserving holes as well. -inline ExPolygons unify(const ExPolygons& shapes) { - using ClipperLib::ptSubject; - - ExPolygons retv; - - bool closed = true; - bool valid = true; - - ClipperLib::Clipper clipper; - - for(auto& path : shapes) { - auto clipperpath = Slic3rMultiPoint_to_ClipperPath(path.contour); - - if(!clipperpath.empty()) - valid &= clipper.AddPath(clipperpath, ptSubject, closed); - - auto clipperholes = Slic3rMultiPoints_to_ClipperPaths(path.holes); - - for(auto& hole : clipperholes) { - if(!hole.empty()) - valid &= clipper.AddPath(hole, ptSubject, closed); - } - } - - if(!valid) BOOST_LOG_TRIVIAL(warning) << "Unification of invalid shapes!"; - - ClipperLib::PolyTree result; - clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNonZero); - - retv.reserve(static_cast<size_t>(result.Total())); - - // Now we will recursively traverse the polygon tree and serialize it - // into an ExPolygon with holes. The polygon tree has the clipper-ish - // PolyTree structure which alternates its nodes as contours and holes - - // A "declaration" of function for traversing leafs which are holes - std::function<void(ClipperLib::PolyNode*, ExPolygon&)> processHole; - - // Process polygon which calls processHoles which than calls processPoly - // again until no leafs are left. - auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) { - ExPolygon poly; - poly.contour.points = ClipperPath_to_Slic3rPolygon(pptr->Contour); - for(auto h : pptr->Childs) { processHole(h, poly); } - retv.push_back(poly); - }; - - // Body of the processHole function - processHole = [&processPoly](ClipperLib::PolyNode *pptr, ExPolygon& poly) - { - poly.holes.emplace_back(); - poly.holes.back().points = ClipperPath_to_Slic3rPolygon(pptr->Contour); - for(auto c : pptr->Childs) processPoly(c); - }; - - // Wrapper for traversing. - auto traverse = [&processPoly] (ClipperLib::PolyNode *node) - { - for(auto ch : node->Childs) { - processPoly(ch); - } - }; - - // Here is the actual traverse - traverse(&result); - - return retv; -} - -inline Point centroid(Points& pp) { - Point c; - switch(pp.size()) { - case 0: break; - case 1: c = pp.front(); break; - case 2: c = (pp[0] + pp[1]) / 2; break; - default: { - Polygon p; - p.points.swap(pp); - c = p.centroid(); - pp.swap(p.points); - break; - } - } - - return c; -} - -inline Point centroid(const ExPolygon& poly) { - return poly.contour.centroid(); -} - -/// A fake concave hull that is constructed by connecting separate shapes -/// with explicit bridges. Bridges are generated from each shape's centroid -/// to the center of the "scene" which is the centroid calculated from the shape -/// centroids (a star is created...) -inline ExPolygons concave_hull(const ExPolygons& polys, double max_dist_mm = 50) -{ - if(polys.empty()) return ExPolygons(); - - ExPolygons punion = unify(polys); // could be redundant - - if(punion.size() == 1) return punion; - - // We get the centroids of all the islands in the 2D slice - Points centroids; centroids.reserve(punion.size()); - std::transform(punion.begin(), punion.end(), std::back_inserter(centroids), - [](const ExPolygon& poly) { return centroid(poly); }); - - // Centroid of the centroids of islands. This is where the additional - // connector sticks are routed. - Point cc = centroid(centroids); - - punion.reserve(punion.size() + centroids.size()); - - std::transform(centroids.begin(), centroids.end(), - std::back_inserter(punion), - [cc, max_dist_mm](const Point& c) { - - double dx = x(c) - x(cc), dy = y(c) - y(cc); - double l = std::sqrt(dx * dx + dy * dy); - double nx = dx / l, ny = dy / l; - double max_dist = mm(max_dist_mm); - - if(l > max_dist) return ExPolygon(); - - ExPolygon r; - auto& ctour = r.contour.points; - - ctour.reserve(3); - ctour.emplace_back(cc); - - Point d(coord_t(mm(1)*nx), coord_t(mm(1)*ny)); - ctour.emplace_back(c + Point( -y(d), x(d) )); - ctour.emplace_back(c + Point( y(d), -x(d) )); - offset(r, mm(1)); - - return r; - }); - - punion = unify(punion); - - return punion; -} - -} - -void ground_layer(const TriangleMesh &mesh, ExPolygons &output, float h) -{ - TriangleMesh m = mesh; - TriangleMeshSlicer slicer(&m); - - std::vector<ExPolygons> tmp; - - slicer.slice({h}, &tmp, [](){}); - - output = tmp.front(); -} - -void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, - double min_wall_thickness_mm, - double min_wall_height_mm, - double max_merge_distance_mm) -{ - auto concavehs = concave_hull(ground_layer, max_merge_distance_mm); - for(ExPolygon& concaveh : concavehs) { - if(concaveh.contour.points.empty()) return; - concaveh.holes.clear(); - - BoundingBox bb(concaveh); - coord_t w = x(bb.max) - x(bb.min); - coord_t h = y(bb.max) - y(bb.min); - - auto wall_thickness = coord_t((w+h)*0.01); - - const coord_t WALL_THICKNESS = mm(min_wall_thickness_mm) + - wall_thickness; - - const coord_t WALL_DISTANCE = coord_t(0.3*WALL_THICKNESS); - const coord_t HEIGHT = mm(min_wall_height_mm); - - auto outer_base = concaveh; - offset(outer_base, WALL_THICKNESS+WALL_DISTANCE); - auto inner_base = outer_base; - offset(inner_base, -WALL_THICKNESS); - inner_base.holes.clear(); outer_base.holes.clear(); - - ExPolygon top_poly; - top_poly.contour = outer_base.contour; - top_poly.holes.emplace_back(inner_base.contour); - auto& tph = top_poly.holes.back().points; - std::reverse(tph.begin(), tph.end()); - - Contour3D pool; - - ExPolygon ob = outer_base; double wh = 0; - auto curvedwalls = round_edges(ob, - 1, // radius 1 mm - 170, // 170 degrees - 0, // z position of the input plane - true, - ob, wh); - pool.merge(curvedwalls); - - ExPolygon ob_contr = ob; - ob_contr.holes.clear(); - - auto pwalls = walls(ob_contr, inner_base, wh, -min_wall_height_mm); - pool.merge(pwalls); - - Polygons top_triangles, bottom_triangles; - triangulate(top_poly, top_triangles); - triangulate(inner_base, bottom_triangles); - auto top_plate = convert(top_triangles, 0, false); - auto bottom_plate = convert(bottom_triangles, -HEIGHT, true); - - ob = inner_base; wh = 0; - curvedwalls = round_edges(ob, - 1, // radius 1 mm - 90, // 170 degrees - 0, // z position of the input plane - false, - ob, wh); - pool.merge(curvedwalls); - - auto innerbed = inner_bed(ob, min_wall_height_mm/2 + wh, wh); - - pool.merge(top_plate); - pool.merge(bottom_plate); - pool.merge(innerbed); - - out.merge(mesh(pool)); - } -} - -} -} diff --git a/src/libslic3r/SLABasePool.hpp b/src/libslic3r/SLABasePool.hpp deleted file mode 100644 index 55c94df07..000000000 --- a/src/libslic3r/SLABasePool.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef SLASUPPORTPOOL_HPP -#define SLASUPPORTPOOL_HPP - -#include <vector> - -namespace Slic3r { - -class ExPolygon; -class TriangleMesh; - -namespace sla { - -using ExPolygons = std::vector<ExPolygon>; - -/// Calculate the polygon representing the slice of the lowest layer of mesh -void ground_layer(const TriangleMesh& mesh, - ExPolygons& output, - float height = 0.1f); - -/// Calculate the pool for the mesh for SLA printing -void create_base_pool(const ExPolygons& ground_layer, - TriangleMesh& output_mesh, - double min_wall_thickness_mm = 2, - double min_wall_height_mm = 5, - double max_merge_distance_mm = 50 - ); - -} - -} - -#endif // SLASUPPORTPOOL_HPP diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 509e0b665..37366e483 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -121,6 +121,8 @@ add_library(libslic3r_gui STATIC Utils/Time.hpp Utils/HexFile.cpp Utils/HexFile.hpp + GUI/SLAPrint.hpp + GUI/SLAPrint.cpp AppController.hpp AppController.cpp AppControllerWx.cpp diff --git a/src/slic3r/GUI/GLGizmo.cpp b/src/slic3r/GUI/GLGizmo.cpp index b793f9626..2c8c7630a 100644 --- a/src/slic3r/GUI/GLGizmo.cpp +++ b/src/slic3r/GUI/GLGizmo.cpp @@ -1612,16 +1612,20 @@ void GLGizmoSlaSupports::on_deactivate() { std::cout << st << "% " << msg << std::endl; }; - const Model& model = *m_model_object->get_model(); - auto emesh = sla::to_eigenmesh(model); - sla::PointSet input = sla::support_points(model); + TriangleMesh&& m = m_model_object->raw_mesh(); + auto&& trafo = m_model_object_matrix.cast<float>(); + m.transform(trafo); + auto emesh = sla::to_eigenmesh(m); + sla::SupportConfig cfg; + sla::PointSet input = sla::support_points(*m_model_object, 0 /*instance*/); sla::SLASupportTree stree(input, emesh, cfg, supportctl); TriangleMesh output; stree.merged_mesh(output); - m_model_object->add_volume(output); + + _3DScene::reload_scene(m_parent.get_wxglcanvas(), false); } Vec3f GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index df1a42a19..af6c08d7c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -33,6 +33,7 @@ #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" #include "slic3r/AppController.hpp" +#include "SLAPrint.hpp" #include "GUI.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" @@ -853,6 +854,7 @@ struct Plater::priv // Data Slic3r::DynamicPrintConfig *config; Slic3r::Print print; + Slic3r::SLAPrint slaprint; Slic3r::Model model; Slic3r::GCodePreviewData gcode_preview_data; @@ -954,7 +956,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) : })), notebook(new wxNotebook(q, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_BOTTOM)), sidebar(new Sidebar(q)), - canvas3D(GLCanvas3DManager::create_wxglcanvas(notebook)) + canvas3D(GLCanvas3DManager::create_wxglcanvas(notebook)), + slaprint(&model) { background_process.set_print(&print); background_process.set_gcode_preview_data(&gcode_preview_data); @@ -1438,7 +1441,7 @@ void Plater::priv::arrange() main_frame->app_controller()->arrange_model(); // ignore arrange failures on purpose: user has visual feedback and we don't need to warn him - // when parts don't fit in print bed + // when parts don't fit in print bed update(); } @@ -2258,7 +2261,7 @@ void Plater::changed_object(int obj_idx) _3DScene::reload_scene(p->canvas3D, false); } - // update print + // update print if (list->is_parts_changed() || list->is_part_settings_changed()) { this->p->schedule_background_process(); #if !ENABLE_MODIFIED_CAMERA_TARGET diff --git a/src/slic3r/GUI/SLAPrint.cpp b/src/slic3r/GUI/SLAPrint.cpp new file mode 100644 index 000000000..52957b854 --- /dev/null +++ b/src/slic3r/GUI/SLAPrint.cpp @@ -0,0 +1,21 @@ +#include "SLAPrint.hpp" + +namespace Slic3r { + +void SLAPrint::synch() { + m_gcfg = m_config_reader(); + // TODO: check model objects and instances +} + +bool SLAPrint::start(std::shared_ptr<BackgroundProcess> scheduler) { + if(!m_process || !m_process->is_running()) set_scheduler(scheduler); + if(!m_process) return false; + + m_process->schedule([this, scheduler](){ + + }); + + return true; +} + +} diff --git a/src/slic3r/GUI/SLAPrint.hpp b/src/slic3r/GUI/SLAPrint.hpp new file mode 100644 index 000000000..b1354e8d0 --- /dev/null +++ b/src/slic3r/GUI/SLAPrint.hpp @@ -0,0 +1,138 @@ +#include <libslic3r.h> +#include "Point.hpp" +#include "SLA/SLASupportTree.hpp" + +#include <string> +#include <memory> +#include <unordered_map> +#include <atomic> + +namespace Slic3r { + +class Model; +class ModelObject; +class ModelInstance; +class GLCanvas3D; +class DynamicPrintConfig; + +// Ok, this will be the driver to create background threads. Possibly +// implemented using a BackgroundSlicingProcess or something derived from that +// The methods should be thread safe, obviously... +class BackgroundProcess { +public: + + virtual ~BackgroundProcess() {} + + virtual void schedule(std::function<void()> fn) = 0; + + virtual void status(unsigned st, const std::string& msg) = 0; + + virtual bool is_canceled() = 0; + + virtual void on_input_changed(std::function<void()> synchfn) = 0; + + virtual bool is_running() = 0; +}; + +/** + * @brief This class is the high level FSM for the SLA printing process. + * + * It should support the background processing framework and contain the + * metadata for the support geometries and their slicing. It should also + * dispatch the SLA printing configuration values to the appropriate calculation + * steps. + * + * TODO (decide): The last important feature is the support for visualization + * which (at least for now) will be implemented as a method(s) returning the + * triangle meshes or receiving the rendering canvas and drawing on that + * directly. + * + * TODO: This class has to be implement the Print interface (not defined yet) + * to be usable as the input to the BackgroundSlicingProcess. + */ +class SLAPrint /* : public Print */ { +public: + + enum CallType { + BLOCKING, NON_BLOCKING + }; + + struct GlobalConfig { + double width_mm; + double height_mm; + unsigned long width_px; + unsigned long height_px; + // ... + }; + +private: + + struct PrintObjectInstance { + Transform3f tr; + std::unique_ptr<sla::SLASupportTree> support_tree_ptr; + SlicedSupports slice_cache; + }; + + using InstanceMap = std::unordered_map<ModelInstance*, PrintObjectInstance>; + + struct PrintObject { + sla::EigenMesh3D emesh; + InstanceMap instances; + }; + + using ObjectMap = std::unordered_map<ModelObject*, PrintObject>; + + // Input data channels: *************************************************** + + const Model *m_model = nullptr; // The model itself + + // something to read out the config profiles and return the values we need. + std::function<GlobalConfig()> m_config_reader; + // ************************************************************************ + + GlobalConfig m_gcfg; + ObjectMap m_data; + std::shared_ptr<BackgroundProcess> m_process; + + // For now it will just stop the whole process and invalidate everything + void synch(); + std::atomic<bool> m_dirty; + + void set_scheduler(std::shared_ptr<BackgroundProcess> scheduler) { + if(scheduler && !scheduler->is_running()) { + m_process = scheduler; + m_process->on_input_changed([this] { + /*synch(); */ + m_dirty.store(true); + }); + } + } + +public: + + SLAPrint(const Model * model, + std::function<SLAPrint::GlobalConfig(void)> cfgreader = [](){ return SLAPrint::GlobalConfig(); }, + std::shared_ptr<BackgroundProcess> scheduler = {}): + m_model(model), m_config_reader(cfgreader) + { + synch(); + m_dirty.store(false); + set_scheduler(scheduler); + } + + // This will start the calculation using the + bool start(std::shared_ptr<BackgroundProcess> scheduler); + + // Get the full support structure (including the supports) + // This should block until the supports are not ready? + bool support_mesh(TriangleMesh& output, CallType calltype = BLOCKING); + + // Exporting to the final zip file, or possibly other format. + // filepath is reserved to be a zip filename or directory or anything + // that a particular format requires. + // (I know, we will use zipped PNG, but everything changes here so quickly) + bool export_layers(const std::string& filepath, + CallType calltype = BLOCKING); +}; + +}