Added support for rounding factor k to path_to_bezier and smooth_path.

This commit is contained in:
Adrian Mariano 2020-03-05 17:05:23 -05:00
parent 863398eb24
commit f67b0dbfc5
2 changed files with 46 additions and 16 deletions

View file

@ -318,26 +318,40 @@ function bezier_polyline(bezier, splinesteps=16, N=3) = let(
); );
// Function: path_to_bezier() // Function: path_to_bezier()
// Usage: // Usage:
// path_to_bezier(path,[tangent],[closed]); // path_to_bezier(path,[tangent],[k],[closed]);
// Description: // Description:
// Given an input path and optional path of tangent vectors, computes a cubic (degree 3) bezier path that passes // Given an input path and optional path of tangent vectors, computes a cubic (degree 3) bezier path that passes
// through every point on the input path and matches the tangent vectors. If you do not supply // through every point on the input path and matches the tangent vectors. If you do not supply
// the tangent it will be computed using path_tangents. If the path is closed specify this // the tangent it will be computed using path_tangents. If the path is closed specify this
// by setting closed=true. // by setting closed=true. If you specify the curvature parameter k it scales the tangent vectors,
// which will increase or decrease the curvature of the interpolated bezier. Negative values of k create loops at the corners,
// so they are not allowed. Sufficiently large k values will also produce loops.
// Arguments: // Arguments:
// path = path of points to define the bezier // path = path of points to define the bezier
// tangents = optional list of tangent vectors at every point // tangents = optional list of tangent vectors at every point
// k = curvature parameter, a scalar or vector to adjust curvature at each point
// closed = set to true for a closed path. Default: false // closed = set to true for a closed path. Default: false
function path_to_bezier(path, tangents, closed=false) = function path_to_bezier(path, tangents, k, closed=false) =
assert(is_path(path,dim=undef),"Input path is not a valid path") assert(is_path(path,dim=undef),"Input path is not a valid path")
assert(is_undef(tangents) || is_path(tangents,dim=len(path[0])),"Tangents must be a path of the same dimension as the input path") assert(is_undef(tangents) || is_path(tangents,dim=len(path[0])),"Tangents must be a path of the same dimension as the input path")
assert(is_undef(tangents) || len(path)==len(tangents), "Input tangents must be the same length as the input path")
let(
k = is_undef(k) ? repeat(1, len(path)) :
is_list(k) ? k : repeat(k, len(path)),
k_bad = [for(entry=k) if (entry<0) entry]
)
assert(len(k)==len(path), "Curvature parameter k must have the same length as the path")
assert(k_bad==[], "Curvature parameter k must be a nonnegative number or list of nonnegative numbers")
let( let(
tangents = is_def(tangents)? tangents : deriv(path, closed=closed), tangents = is_def(tangents)? tangents : deriv(path, closed=closed),
lastpt = len(path) - (closed?0:1) lastpt = len(path) - (closed?0:1)
) )
[for(i=[0:lastpt-1]) each [path[i], path[i]+tangents[i]/3, select(path,i+1)-select(tangents,i+1)/3], [for(i=[0:lastpt-1]) each [path[i],
path[i]+k[i]*tangents[i]/3,
select(path,i+1)-select(k,i+1)*select(tangents,i+1)/3],
select(path,lastpt)]; select(path,lastpt)];

View file

@ -9,10 +9,10 @@
// ``` // ```
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
include <BOSL2/beziers.scad> include <beziers.scad>
include <BOSL2/strings.scad> include <strings.scad>
include <BOSL2/structs.scad> include <structs.scad>
include <BOSL2/skin.scad> include <skin.scad>
// CommonCode: // CommonCode:
@ -409,16 +409,23 @@ function _rounding_offsets(edgespec,z_dir=1) =
// Function: smooth_path() // Function: smooth_path()
// Usage: // Usage:
// smooth_path(path, [tangents], [splinesteps], [closed] // smooth_path(path, [tangents], [k], [splinesteps], [closed]
// Description: // Description:
// Smooths the input path using a cubic spline. Every segment of the path will be replaced by a cubic curve // Smooths the input path using a cubic spline. Every segment of the path will be replaced by a cubic curve
// with `splinesteps` points. The cubic interpolation will pass through every input point on the path // with `splinesteps` points. The cubic interpolation will pass through every input point on the path
// and will match the tangents at every point. If you do not specify tangents they will be computed using // and will match the tangents at every point. If you do not specify tangents they will be computed using
// deriv(). Note that the magnitude of the tangents affects the result. See also path_to_bezier(). // deriv(). See also path_to_bezier().
//
// Note that the magnitude of the tangents affects the result. If you increase it you will get a blunter
// corner with a larger radius of curvature. Decreasing it will produce a sharp corner. You can specify
// the curvature factor `k` to adjust the curvature. It can be a scalar or a vector the same length as
// the path and is used to scale the tangent vectors. Negative values of k create loops at the corners,
// so they are not allowed. Sufficiently large k values will also produce loops.
// Arguments: // Arguments:
// path = path to smooth // path = path to smooth
// tangents = tangent vectors of the path // tangents = tangent vectors of the path
// splinesteps = number of points to insert between the path points. Default: 10 // splinesteps = number of points to insert between the path points. Default: 10
// k = curvature parameter, a scalar or vector to adjust curvature at each point
// closed = set to true for a closed path. Default: false // closed = set to true for a closed path. Default: false
// Example(2D): Original path in green, smoothed path in yellow: // Example(2D): Original path in green, smoothed path in yellow:
// color("green")stroke(square(4), width=0.1); // color("green")stroke(square(4), width=0.1);
@ -428,21 +435,30 @@ function _rounding_offsets(edgespec,z_dir=1) =
// Example(2D): A more interesting shape: // Example(2D): A more interesting shape:
// path = [[0,0], [4,0], [7,14], [-3,12]]; // path = [[0,0], [4,0], [7,14], [-3,12]];
// polygon(smooth_path(path,closed=true)); // polygon(smooth_path(path,closed=true));
// Example(2D): Scaling the tangent data can decrease or increase the amount of smoothing: // Example(2D): Scaling the tangent data using the curvature parameter k can decrease or increase the amount of smoothing. Note this is the same
// shape = square(4); // as just multiplying the deriv(square(4)) by k.
// polygon(smooth_path(shape, tangents=0.5*deriv(shape, closed=true),closed=true)); // polygon(smooth_path(square(4), k=0.5,closed=true));
// Example(2D): Or you can specify your own tangent values to alter the shape of the curve // Example(2D): Or you can specify your own tangent values to alter the shape of the curve
// polygon(smooth_path(square(4),tangents=1.25*[[-2,-1], [-2,1], [1,2], [2,-1]],closed=true)); // polygon(smooth_path(square(4),tangents=1.25*[[-2,-1], [-2,1], [1,2], [2,-1]],closed=true));
// Example(FlatSpin): Works on 3d paths as well // Example(FlatSpin): Works on 3d paths as well
// path = [[0,0,0],[3,3,2],[6,0,1],[9,9,0]]; // path = [[0,0,0],[3,3,2],[6,0,1],[9,9,0]];
// trace_polyline(smooth_path(path),size=.3); // trace_polyline(smooth_path(path),size=.3);
function smooth_path(path, tangents, splinesteps=10, closed=false) = // Example(2D): The curve passes through all the points, but features some unexpected wiggles. These occur because the curvature is too low to change fast enough.
let( // path = [[0,0], [0,3], [.5,2.8], [1,2.2], [1,0]];
bez = path_to_bezier(path, tangents=tangents, closed=closed) // polygon(smooth_path(path));
// color("red") place_copies(path)circle(r=.1,$fn=16);
// Example(2D): Using the k parameter is one way to fix this problem. By allowing sharper curvature (k<1) at the two points next to the problematic point we can achieve a smoother result. The other fix is to move your points.
// path = [[0,0], [0,3], [.5,2.8], [1,2.2], [1,0]];
// polygon(smooth_path(path,k=[1,.5,1,.5,1]));
// color("red") place_copies(path)circle(r=.1,$fn=16);
function smooth_path(path, tangents, k, splinesteps=10, closed=false) =
let (
bez = path_to_bezier(path, tangents, k=k, closed=closed)
) )
bezier_polyline(bez,splinesteps=splinesteps); bezier_polyline(bez,splinesteps=splinesteps);
// Module: offset_sweep() // Module: offset_sweep()
// //
// Description: // Description: