diff --git a/src/libslic3r/SLA/SLABasePool.cpp b/src/libslic3r/SLA/SLABasePool.cpp index 21bd124f71..f1e79357dd 100644 --- a/src/libslic3r/SLA/SLABasePool.cpp +++ b/src/libslic3r/SLA/SLABasePool.cpp @@ -215,41 +215,67 @@ inline Contour3D roofs(const ExPolygon& poly, coord_t z_distance) { return lower; } -template Contour3D round_edges(const ExPolygon& base_plate, double radius_mm, double degrees, double ceilheight_mm, bool dir, ThrowOnCancel throw_on_cancel, - ExP&& last_offset = ExP(), D&& last_height = D()) + ExPolygon& last_offset, double& last_height) { auto ob = base_plate; auto ob_prev = ob; double wh = ceilheight_mm, wh_prev = wh; Contour3D curvedwalls; - int steps = 15; // int(std::ceil(10*std::pow(radius_mm, 1.0/3))); + int steps = 30; double stepx = radius_mm / steps; coord_t s = dir? 1 : -1; degrees = std::fmod(degrees, 180); - if(degrees >= 90) { - for(int i = 1; i <= steps; ++i) { - throw_on_cancel(); + // we use sin for x distance because we interpret the angle starting from + // PI/2 + int tos = degrees < 90? + int(radius_mm*std::cos(degrees * PI / 180 - PI/2) / stepx) : steps; + for(int i = 1; i <= tos; ++i) { + throw_on_cancel(); + + ob = base_plate; + + double r2 = radius_mm * radius_mm; + double xx = i*stepx; + double x2 = xx*xx; + double stepy = std::sqrt(r2 - x2); + + offset(ob, s*mm(xx)); + wh = ceilheight_mm - radius_mm + stepy; + + Contour3D pwalls; + pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel); + + curvedwalls.merge(pwalls); + ob_prev = ob; + wh_prev = wh; + } + + if(degrees > 90) { + double tox = radius_mm - radius_mm*std::cos(degrees * PI / 180 - PI/2); + int tos = int(tox / stepx); + + for(int i = 1; i <= tos; ++i) { + throw_on_cancel(); ob = base_plate; double r2 = radius_mm * radius_mm; - double xx = i*stepx; + double xx = radius_mm - i*stepx; double x2 = xx*xx; double stepy = std::sqrt(r2 - x2); - offset(ob, s*mm(xx)); - wh = ceilheight_mm - radius_mm + stepy; + wh = ceilheight_mm - radius_mm - stepy; Contour3D pwalls; - pwalls = walls(ob, ob_prev, wh, wh_prev, throw_on_cancel); + pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel); curvedwalls.merge(pwalls); ob_prev = ob; @@ -257,28 +283,6 @@ Contour3D round_edges(const ExPolygon& base_plate, } } - double tox = radius_mm - radius_mm*std::sin(degrees * PI / 180); - int tos = int(tox / stepx); - - for(int i = 1; i <= tos; ++i) { - throw_on_cancel(); - ob = base_plate; - - double r2 = radius_mm * radius_mm; - double xx = radius_mm - i*stepx; - double x2 = xx*xx; - double stepy = std::sqrt(r2 - x2); - offset(ob, s*mm(xx)); - wh = ceilheight_mm - radius_mm - stepy; - - Contour3D pwalls; - pwalls = walls(ob_prev, ob, wh_prev, wh, throw_on_cancel); - - curvedwalls.merge(pwalls); - ob_prev = ob; - wh_prev = wh; - } - last_offset = std::move(ob); last_height = wh; @@ -457,32 +461,60 @@ void base_plate(const TriangleMesh &mesh, ExPolygons &output, float h, void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, const PoolConfig& cfg) { - double mdist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm) + - cfg.max_merge_distance_mm; - auto concavehs = concave_hull(ground_layer, mdist, cfg.throw_on_cancel); + double mergedist = 2*(1.8*cfg.min_wall_thickness_mm + 4*cfg.edge_radius_mm)+ + cfg.max_merge_distance_mm; + + // Here we get the base polygon from which the pad has to be generated. + // We create an artificial concave hull from this polygon and that will + // serve as the bottom plate of the pad. We will offset this concave hull + // and then offset back the result with clipper with rounding edges ON. This + // trick will create a nice rounded pad shape. + auto concavehs = concave_hull(ground_layer, mergedist, cfg.throw_on_cancel); + + 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); + + // scaled values + const coord_t s_thickness = mm(thickness); + const coord_t s_eradius = mm(cfg.edge_radius_mm); + const coord_t s_safety_dist = 2*s_eradius + coord_t(0.8*s_thickness); + // const coord_t wheight = mm(cfg.min_wall_height_mm); + coord_t s_wingdist = mm(wingdist); + + auto& thrcl = cfg.throw_on_cancel; + for(ExPolygon& concaveh : concavehs) { if(concaveh.contour.points.empty()) return; + + // Get rif of any holes in the concave hull output. concaveh.holes.clear(); - const coord_t WALL_THICKNESS = mm(cfg.min_wall_thickness_mm); - - const coord_t WALL_DISTANCE = mm(2*cfg.edge_radius_mm) + - coord_t(0.8*WALL_THICKNESS); - - const coord_t HEIGHT = mm(cfg.min_wall_height_mm); - + // Here lies the trick that does the smooting only with clipper offset + // calls. The offset is configured to round edges. Inner edges will + // be rounded because we offset twice: ones to get the outer (top) plate + // and again to get the inner (bottom) plate auto outer_base = concaveh; - offset(outer_base, WALL_THICKNESS+WALL_DISTANCE); + outer_base.holes.clear(); + offset(outer_base, s_safety_dist + s_wingdist + s_thickness); auto inner_base = outer_base; - offset(inner_base, -WALL_THICKNESS); - inner_base.holes.clear(); outer_base.holes.clear(); + offset(inner_base, -(s_thickness + s_wingdist)); + // Punching a hole in the top plate for the cavity ExPolygon top_poly; + ExPolygon middle_base; 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()); + + if(wingheight > 0) { + middle_base = outer_base; + offset(middle_base, -s_thickness); + top_poly.holes.emplace_back(middle_base.contour); + auto& tph = top_poly.holes.back().points; + std::reverse(tph.begin(), tph.end()); + } Contour3D pool; @@ -497,13 +529,15 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, // y = cy + (r^2*py - r*px*sqrt(px^2 + py^2 - r^2) / (px^2 + py^2) // where px and py are the coordinates of the point outside the circle // cx and cy are the circle center, r is the radius + // We place the circle center to (0, 0) in the calculation the make + // things easier. // to get the angle we use arcsin function and subtract 90 degrees then // flip the sign to get the right input to the round_edge function. double r = cfg.edge_radius_mm; double cy = 0; double cx = 0; - double px = cfg.min_wall_thickness_mm; - double py = r - cfg.min_wall_height_mm; + double px = thickness + wingdist; + double py = r - fullheight; double pxcx = px - cx; double pycy = py - cy; @@ -513,45 +547,59 @@ void create_base_pool(const ExPolygons &ground_layer, TriangleMesh& out, double vy = (r_2*pycy - r*pxcx*D) / b_2; double phi = -(std::asin(vy/r) * 180 / PI - 90); - auto curvedwalls = round_edges(ob, - r, - phi, // 170 degrees - 0, // z position of the input plane - true, - cfg.throw_on_cancel, - ob, wh); - pool.merge(curvedwalls); + // Generate the smoothed edge geometry + auto walledges = round_edges(ob, + r, + phi, + 0, // z position of the input plane + true, + thrcl, + ob, wh); + pool.merge(walledges); - ExPolygon ob_contr = ob; - ob_contr.holes.clear(); - - auto pwalls = walls(ob_contr, inner_base, wh, -cfg.min_wall_height_mm, - cfg.throw_on_cancel); + // Now that we have the rounded edge connencting the top plate with + // the outer side walls, we can generate and merge the sidewall geometry + auto pwalls = walls(ob, inner_base, wh, -fullheight, thrcl); pool.merge(pwalls); + if(wingheight > 0) { + // Generate the smoothed edge geometry + auto cavityedges = round_edges(middle_base, + r, + phi - 90, // from tangent lines + 0, + false, + thrcl, + ob, wh); + pool.merge(cavityedges); + + // Next is the cavity walls connecting to the top plate's + // artificially created hole. + auto cavitywalls = walls(inner_base, ob, -wingheight, wh, thrcl); + pool.merge(cavitywalls); + } + + // 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 + // eleveted by the thickness. 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; - // rounded edge generation for the inner bed - curvedwalls = round_edges(ob, - cfg.edge_radius_mm, - 90, // 90 degrees - 0, // z position of the input plane - false, - cfg.throw_on_cancel, - ob, wh); - pool.merge(curvedwalls); - - auto innerbed = inner_bed(ob, cfg.min_wall_height_mm/2 + wh, wh); + auto bottom_plate = convert(bottom_triangles, -mm(fullheight), true); pool.merge(top_plate); pool.merge(bottom_plate); - pool.merge(innerbed); + + if(wingheight > 0) { + Polygons middle_triangles; + triangulate(inner_base, middle_triangles); + auto middle_plate = convert(middle_triangles, -mm(wingheight), false); + pool.merge(middle_plate); + } out.merge(mesh(pool)); } diff --git a/src/libslic3r/SLA/SLABasePool.hpp b/src/libslic3r/SLA/SLABasePool.hpp index 62c4971ebe..3917d995b7 100644 --- a/src/libslic3r/SLA/SLABasePool.hpp +++ b/src/libslic3r/SLA/SLABasePool.hpp @@ -49,7 +49,11 @@ void create_base_pool(const ExPolygons& base_plate, /// min_wall_thickness and it should be corrected in the future. This method /// will return the correct value for further processing. inline double get_pad_elevation(const PoolConfig& cfg) { - return cfg.min_wall_height_mm / 2.0; + return cfg.min_wall_thickness_mm; +} + +inline double get_pad_fullheight(const PoolConfig& cfg) { + return cfg.min_wall_height_mm + cfg.min_wall_thickness_mm; } } diff --git a/src/libslic3r/SLA/SLASupportTree.cpp b/src/libslic3r/SLA/SLASupportTree.cpp index 746acc5474..5239255b12 100644 --- a/src/libslic3r/SLA/SLASupportTree.cpp +++ b/src/libslic3r/SLA/SLASupportTree.cpp @@ -515,7 +515,8 @@ struct Pad { double ground_level, const PoolConfig& pcfg) : cfg(pcfg), - zlevel(ground_level + sla::get_pad_elevation(pcfg)) + zlevel(ground_level + + (sla::get_pad_fullheight(pcfg) - sla::get_pad_elevation(pcfg)) ) { ExPolygons basep; cfg.throw_on_cancel(); @@ -523,7 +524,8 @@ struct Pad { // The 0.1f is the layer height with which the mesh is sampled and then // the layers are unified into one vector of polygons. base_plate(object_support_mesh, basep, - float(cfg.min_wall_height_mm), 0.1f, pcfg.throw_on_cancel); + float(cfg.min_wall_height_mm + cfg.min_wall_thickness_mm), + 0.1f, pcfg.throw_on_cancel); for(auto& bp : baseplate) basep.emplace_back(bp); @@ -781,7 +783,7 @@ public: // WITH THE PAD double full_height() const { if(merged_mesh().empty() && !pad().empty()) - return pad().cfg.min_wall_height_mm; + return get_pad_fullheight(pad().cfg); double h = mesh_height(); if(!pad().empty()) h += sla::get_pad_elevation(pad().cfg); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index f45d805b6a..aa759a47a3 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -630,7 +630,7 @@ void SLAPrint::process() sla::PoolConfig pcfg(wt, h, md, er); ExPolygons bp; - double pad_h = sla::get_pad_elevation(pcfg); + double pad_h = sla::get_pad_fullheight(pcfg); auto&& trmesh = po.transformed_mesh(); // This call can get pretty time consuming