From 571b133791b0af6789df2df49ea0cf86783d210b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 1 Mar 2023 10:48:05 +0100 Subject: [PATCH] Wipe tower: stabilization cone --- src/libslic3r/GCode/WipeTower.cpp | 113 +++++++++++++++++++++++++----- src/libslic3r/GCode/WipeTower.hpp | 4 ++ src/libslic3r/Print.cpp | 30 +++++--- src/libslic3r/Print.hpp | 1 + src/slic3r/GUI/3DScene.cpp | 13 ++++ 5 files changed, 135 insertions(+), 26 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 99cbbe87c7..516ae3d8a0 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -10,6 +10,7 @@ #include "GCodeProcessor.hpp" #include "BoundingBox.hpp" #include "LocalesUtils.hpp" +#include "Geometry.hpp" #include @@ -1170,35 +1171,94 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() ";------------------\n\n\n\n\n\n\n"); } - // outer perimeter (always): - writer.rectangle(wt_box, feedrate); + // This block creates the stabilization cone. + // First define a lambda to draw the rectangle with stabilization. + auto supported_rectangle = [this, &writer](const box_coordinates& wt_box, double feedrate) -> Polygon { + const auto [R, support_scale] = get_wipe_tower_cone_base(m_wipe_tower_width, m_wipe_tower_height, m_wipe_tower_depth); + + double r = std::tan(Geometry::deg2rad(15.)) * (m_wipe_tower_height - m_layer_info->z); + Vec2f center = (wt_box.lu + wt_box.rd) / 2.; + double w = wt_box.lu.y() - wt_box.ld.y(); + enum Type { + Arc, + Corner, + ArcStart, + ArcEnd + }; + + std::vector> pts = {{wt_box.ru, Corner}}; + if (double alpha_start = std::asin((0.5*w)/r); ! std::isnan(alpha_start) && r > 0.5*w+0.01) { + for (double alpha = alpha_start; alpha < M_PI-alpha_start+0.001; alpha+=(M_PI-2*alpha_start) / 20.) + pts.emplace_back(Vec2f(center.x() + r*std::cos(alpha)/support_scale, center.y() + r*std::sin(alpha)), alpha == alpha_start ? ArcStart : Arc); + pts.back().second = ArcEnd; + } + pts.emplace_back(wt_box.lu, Corner); + pts.emplace_back(wt_box.ld, Corner); + for (int i=int(pts.size())-3; i>0; --i) + pts.emplace_back(Vec2f(pts[i].first.x(), 2*center.y()-pts[i].first.y()), i == int(pts.size())-3 ? ArcStart : i == 1 ? ArcEnd : Arc); + pts.emplace_back(wt_box.rd, Corner); + + // Find the closest corner and travel to it. + int start_i = 0; + double min_dist = std::numeric_limits::max(); + for (int i=0; i()); + for (int i=cp+1; true; ++i ) { + if (i==int(poly.points.size())) + i = 0; + writer.extrude(unscale(poly.points[i]).cast()); + if (i == cp) + break; + } } // Save actual brim width to be later passed to the Print object, which will use it // for skirt calculation and pass it to GLCanvas for precise preview box - m_wipe_tower_brim_width_real = wt_box.ld.x() - box.ld.x() + spacing/2.f; - wt_box = box; + m_wipe_tower_brim_width_real = loops_num * spacing; } - // Now prepare future wipe. box contains rectangle that was extruded last (ccw). - Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : - (writer.pos() == wt_box.rd ? wt_box.ru : - (writer.pos() == wt_box.ru ? wt_box.lu : - wt_box.ld))); - writer.add_wipe_point(writer.pos()) - .add_wipe_point(target); - + // Now prepare future wipe. + int i = poly.closest_point_index(Point::new_scale(writer.x(), writer.y())); + writer.add_wipe_point(writer.pos()); + writer.add_wipe_point(unscale(poly.points[i==0 ? int(poly.points.size())-1 : i-1]).cast()); // Ask our writer about how much material was consumed. // Skip this in case the layer is sparse and config option to not print sparse layers is enabled. @@ -1209,6 +1269,24 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() return construct_tcr(writer, false, old_tool); } +// Static method to get the radius and x-scaling of the stabilizing cone base. +std::pair WipeTower::get_wipe_tower_cone_base(double width, double height, double depth) +{ + const double angle_deg = 15.; + double R = std::tan(Geometry::deg2rad(angle_deg)) * height; + double fake_width = 0.66 * width; + double diag = std::hypot(fake_width / 2., depth / 2.); + double support_scale = 1.; + if (R > diag) { + double w = fake_width; + double sin = 0.5 * depth / diag; + double tan = depth / w; + double t = (R - diag) * sin; + support_scale = (w / 2. + t / tan + t * tan) / (w / 2.); + } + return std::make_pair(R, support_scale); +} + // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, float wipe_volume) @@ -1251,6 +1329,7 @@ void WipeTower::plan_tower() m_wipe_tower_depth = 0.f; for (auto& layer : m_plan) layer.depth = 0.f; + m_wipe_tower_height = m_plan.empty() ? 0.f : m_plan.back().z; for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { @@ -1357,7 +1436,7 @@ void WipeTower::generate(std::vector> & m_old_temperature = -1; // reset last temperature written in the gcode std::vector layer_result; - for (auto layer : m_plan) + for (const WipeTower::WipeTowerInfo& layer : m_plan) { set_layer(layer.z, layer.height, 0, false/*layer.z == m_plan.front().z*/, layer.z == m_plan.back().z); m_internal_rotation += 180.f; diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 7f4a6bf9aa..e23c6645f4 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -23,6 +23,8 @@ class WipeTower public: static const std::string never_skip_tag() { return "_GCODE_WIPE_TOWER_NEVER_SKIP_TAG"; } + static std::pair get_wipe_tower_cone_base(double width, double height, double depth); + struct Extrusion { Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} @@ -142,6 +144,7 @@ public: float get_depth() const { return m_wipe_tower_depth; } float get_brim_width() const { return m_wipe_tower_brim_width_real; } + float get_wipe_tower_height() const { return m_wipe_tower_height; } @@ -253,6 +256,7 @@ private: Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower + float m_wipe_tower_height = 0.f; float m_wipe_tower_brim_width = 0.f; // Width of brim (mm) from config float m_wipe_tower_brim_width_real = 0.f; // Width of brim (mm) after generation float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index af897b93c3..49dbfea1fe 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1134,23 +1134,34 @@ Polygons Print::first_layer_islands() const std::vector Print::first_layer_wipe_tower_corners() const { - std::vector corners; + std::vector pts_scaled; + if (has_wipe_tower() && ! m_wipe_tower_data.tool_changes.empty()) { double width = m_config.wipe_tower_width + 2*m_wipe_tower_data.brim_width; double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width; Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width); - for (Vec2d pt : { - pt0, - Vec2d(pt0.x()+width, pt0.y() ), - Vec2d(pt0.x()+width, pt0.y()+depth), - Vec2d(pt0.x(), pt0.y()+depth) - }) { + + // First the corners. + std::vector pts = { pt0, + Vec2d(pt0.x()+width, pt0.y()), + Vec2d(pt0.x()+width, pt0.y()+depth), + Vec2d(pt0.x(),pt0.y()+depth) + }; + + // Now the stabilization cone. + Vec2d center = (pts[0] + pts[2])/2.; + const auto [cone_R, cone_x_scale] = WipeTower::get_wipe_tower_cone_base(m_config.wipe_tower_width, m_wipe_tower_data.height, m_wipe_tower_data.depth); + double r = cone_R + m_wipe_tower_data.brim_width; + for (double alpha = 0.; alpha<2*M_PI; alpha += M_PI/20.) + pts.emplace_back(center + r*Vec2d(std::cos(alpha)/cone_x_scale, std::sin(alpha))); + + for (Vec2d& pt : pts) { pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt; pt += Vec2d(m_config.wipe_tower_x.value, m_config.wipe_tower_y.value); - corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); + pts_scaled.emplace_back(Point(scale_(pt.x()), scale_(pt.y()))); } } - return corners; + return pts_scaled; } void Print::finalize_first_layer_convex_hull() @@ -1448,6 +1459,7 @@ void Print::_make_wipe_tower() wipe_tower.generate(m_wipe_tower_data.tool_changes); m_wipe_tower_data.depth = wipe_tower.get_depth(); m_wipe_tower_data.brim_width = wipe_tower.get_brim_width(); + m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height(); // Unload the current filament over the purge tower. coordf_t layer_height = m_objects.front()->config().layer_height.value; diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 9fbbe378a1..60ef1403fc 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -434,6 +434,7 @@ struct WipeTowerData // Depth of the wipe tower to pass to GLCanvas3D for exact bounding box: float depth; float brim_width; + float height; void clear() { priming.reset(nullptr); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index c25bff45de..4659bf0f07 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -544,6 +544,19 @@ int GLVolumeCollection::load_wipe_tower_preview( brim_mesh.translate(-brim_width, -brim_width, 0.f); mesh.merge(brim_mesh); + // Now the stabilization cone and its base. + const auto [R, scale_x] = WipeTower::get_wipe_tower_cone_base(width, height, depth); + TriangleMesh cone_mesh(its_make_cone(R, height)); + cone_mesh.scale(Vec3f(1.f/scale_x, 1.f, 1.f)); + + TriangleMesh disk_mesh(its_make_cylinder(R, brim_height)); + disk_mesh.scale(Vec3f(1. / scale_x, 1., 1.)); // Now it matches the base, which may be elliptic. + disk_mesh.scale(Vec3f(1.f + scale_x*brim_width/R, 1.f + brim_width/R, 1.f)); // Scale so the brim is not deformed. + cone_mesh.merge(disk_mesh); + cone_mesh.translate(width / 2., depth / 2., 0.); + mesh.merge(cone_mesh); + + volumes.emplace_back(new GLVolume(color)); GLVolume& v = *volumes.back(); #if ENABLE_OPENGL_ES