From ec9bfb48ff8aae8bf091dff67513b346577a1da3 Mon Sep 17 00:00:00 2001 From: Justin Lin Date: Wed, 17 May 2017 18:02:57 +0800 Subject: [PATCH] added bezier_smooth --- README.md | 1 + docs/images/lib-bezier_smooth-1.JPG | Bin 0 -> 32939 bytes docs/images/lib-bezier_smooth-2.JPG | Bin 0 -> 10712 bytes docs/lib-bezier_smooth.md | 59 ++++++++++++++++++++ src/bezier_smooth.scad | 81 ++++++++++++++++++++++++++++ 5 files changed, 141 insertions(+) create mode 100644 docs/images/lib-bezier_smooth-1.JPG create mode 100644 docs/images/lib-bezier_smooth-2.JPG create mode 100644 docs/lib-bezier_smooth.md create mode 100644 src/bezier_smooth.scad diff --git a/README.md b/README.md index 2a6d3d38..ec675810 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,7 @@ Too many dependencies? Because OpenSCAD doesn't provide namespace management, I - [circle_path](https://openhome.cc/eGossip/OpenSCAD/lib-circle_path.html) - [bezier_curve](https://openhome.cc/eGossip/OpenSCAD/lib-bezier_curve.html) - [bezier_surface](https://openhome.cc/eGossip/OpenSCAD/lib-bezier_surface.html) + - [bezier_smooth](https://openhome.cc/eGossip/OpenSCAD/lib-bezier_smooth.html) - [helix](https://openhome.cc/eGossip/OpenSCAD/lib-helix.html) - [golden_spiral](https://openhome.cc/eGossip/OpenSCAD/lib-golden_spiral.html) - [archimedean_spiral](https://openhome.cc/eGossip/OpenSCAD/lib-archimedean_spiral.html) diff --git a/docs/images/lib-bezier_smooth-1.JPG b/docs/images/lib-bezier_smooth-1.JPG new file mode 100644 index 0000000000000000000000000000000000000000..c3ec4b48498b48a7fbf6409e94429b910d793940 GIT binary patch literal 32939 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 z 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 literal 0 HcmV?d00001 diff --git a/docs/images/lib-bezier_smooth-2.JPG b/docs/images/lib-bezier_smooth-2.JPG new file mode 100644 index 0000000000000000000000000000000000000000..696bfc01edc444d0cbea0a72a8c2031a3e53119c GIT binary patch literal 10712 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 JHJ$bL{{w$cg~9*; literal 0 HcmV?d00001 diff --git a/docs/lib-bezier_smooth.md b/docs/lib-bezier_smooth.md new file mode 100644 index 00000000..7db71d13 --- /dev/null +++ b/docs/lib-bezier_smooth.md @@ -0,0 +1,59 @@ +# bezier_smooth + +Given a path, the bezier_smooth function uses bazier curves to smooth all corners. You can use it to create smoothier lines or rounded shapes. + +Dependencies: the `bezier_curve` function. + +## Parameters + +- `path_pts` : A list of points represent the path. +- `round_d` : Used to create the other two control points at the corner. +- `t_step` : The distance between two points of the Bézier path at the corner. It defaults to 0.1. +- `closed` : It defaults to `false`. If you have a closed path, set it to `true`. + +## Examples + + include ; + include ; + include ; + + width = 2; + round_d = 15; + + path_pts = [ + [0, 0, 0], + [40, 60, 10], + [-50, 90, 30], + [-10, -10, 50] + ]; + + hull_polyline3d( + path_pts, width + ); + + smoothed_path_pts = bezier_smooth(path_pts, round_d); + + color("red") translate([30, 0, 0]) hull_polyline3d( + smoothed_path_pts, width + ); + +![bezier_smooth](images/lib-bezier_smooth-1.JPG) + + include ; + include ; + + round_d = 10; + + path_pts = [ + [0, 0], + [40, 0], + [0, 60] + ]; + + polygon(path_pts); + + smoothed_path_pts = bezier_smooth(path_pts, round_d, closed = true); + + translate([50, 0, 0]) polygon(smoothed_path_pts); + +![bezier_smooth](images/lib-bezier_smooth-2.JPG) \ No newline at end of file diff --git a/src/bezier_smooth.scad b/src/bezier_smooth.scad new file mode 100644 index 00000000..95398999 --- /dev/null +++ b/src/bezier_smooth.scad @@ -0,0 +1,81 @@ +/** +* bezier_smooth.scad +* +* Given a path, the bezier_smooth function uses bazier curves to smooth all corners. +* +* @copyright Justin Lin, 2017 +* @license https://opensource.org/licenses/lgpl-3.0.html +* +* @see https://openhome.cc/eGossip/OpenSCAD/lib-bezier_curve.html +* +**/ + +include <__private__/__to3d.scad>; +include <__private__/__to2d.scad>; + +function _ya_za(p1, p2) = + let( + dx = p2[0] - p1[0], + dy = p2[1] - p1[1], + dz = p2[2] - p1[2], + za = atan2(dy, dx), + ya = atan2(dz, sqrt(pow(dx, 2) + pow(dy, 2))) + ) [ya, za]; + +function _corner_ctrl_pts(round_d, p1, p2, p3) = + let( + _ya_za_1 = _ya_za(p1, p2), + _ya_za_2 = _ya_za(p3, p2), + + dz1 = sin(_ya_za_1[0]) * round_d, + dxy1 = cos(_ya_za_1[0]) * round_d, + dy1 = sin(_ya_za_1[1]) * dxy1, + dx1 = cos(_ya_za_1[1]) * dxy1, + + dz2 = sin(_ya_za_2[0]) * round_d, + dxy2 = cos(_ya_za_2[0]) * round_d, + dy2 = sin(_ya_za_2[1]) * dxy2, + dx2 = cos(_ya_za_2[1]) * dxy2 + ) + [ + p2 - [dx1, dy1, dz1], + p2, + p2 - [dx2, dy2, dz2] + ]; + + +function _bezier_corner(t_step, p1, p2, p3) = + bezier_curve(t_step, _corner_ctrl_pts(round_d, p1, p2, p3)); + +function _recursive_bezier_smooth(pts, round_d, t_step, leng, i = 0) = + i <= leng - 3 ? + concat( + _bezier_corner(t_step, pts[i], pts[i + 1], pts[i + 2]), + _recursive_bezier_smooth(pts, round_d, t_step, leng, i + 1) + ) + : []; + +function bezier_smooth(path_pts, round_d, t_step = 0.1, closed = false) = + let( + pts = len(path_pts[0]) == 3 ? path_pts : [for(p = path_pts) __to3d(p)], + leng = len(pts), + middle_pts = _recursive_bezier_smooth(pts, round_d, t_step, leng), + pth_pts = closed ? + concat( + _recursive_bezier_smooth( + [pts[leng - 1], pts[0], pts[1]], + round_d, t_step, 3 + ), + middle_pts, + _recursive_bezier_smooth( + [pts[leng - 2], pts[leng - 1], pts[0]], + round_d, t_step, 3 + ) + ) : + concat( + [pts[0]], + middle_pts, + [pts[leng - 1]] + ) + ) + len(path_pts[0]) == 2 ? [for(p = pth_pts) __to2d(p)] : pth_pts; \ No newline at end of file