diff --git a/paths.scad b/paths.scad index 844c0dd..06a5f7e 100644 --- a/paths.scad +++ b/paths.scad @@ -569,6 +569,41 @@ function _corner_roundover_path(p1, p2, p3, r, d) = +// Function: path_add_jitter() +// Topics: Paths +// See Also: jittered_poly(), subdivide_long_segments() +// Usage: +// jpath = path_add_jitter(path, , ); +// Description: +// Adds tiny jitter offsets to collinear points in the given path so that they +// are no longer collinear. This is useful for preserving subdivision on long +// straight segments, when making geometry with `polygon()`, for use with +// `linear_exrtrude()` with a `twist()`. +// Arguments: +// path = The path to add jitter to. +// dist = The amount to jitter points by. Default: 1/512 (0.00195) +// --- +// closed = If true, treat path like a closed polygon. Default: true +// Example: +// d = 100; h = 75; quadsize = 5; +// path = pentagon(d=d); +// spath = subdivide_long_segments(path, quadsize, closed=true); +// jpath = path_add_jitter(spath, closed=true); +// linear_extrude(height=h, twist=72, slices=h/quadsize) +// polygon(jpath); +function path_add_jitter(path, dist=1/512, closed=true) = + assert(is_path(path)) + assert(is_finite(dist)) + assert(is_bool(closed)) + [ + path[0], + for (i=idx(path,s=1,e=closed?-1:-2)) let( + n = line_normal([path[i-1],path[i]]) + ) path[i] + n * (collinear(select(path,i-1,i+1))? (dist * ((i%2)*2-1)) : 0), + if (!closed) last(path) + ]; + + // Function: path3d_spiral() // Description: // Returns a 3D spiral path. @@ -928,6 +963,31 @@ module modulated_circle(r, sines=[[1,1]], d) } +// Module: jittered_poly() +// Topics: Extrusions +// See Also: path_add_jitter(), subdivide_long_segments() +// Usage: +// jittered_poly(path, ); +// Description: +// Creates a 2D polygon shape from the given path in such a way that any extra +// collinear points are not stripped out in the way that `polygon()` normally does. +// This is useful for refining the mesh of a `linear_extrude()` with twist. +// Arguments: +// path = The path to add jitter to. +// dist = The amount to jitter points by. Default: 1/512 (0.00195) +// Example: +// d = 100; h = 75; quadsize = 5; +// path = pentagon(d=d); +// spath = subdivide_long_segments(path, quadsize, closed=true); +// linear_extrude(height=h, twist=72, slices=h/quadsize) +// jittered_poly(spath); +module jittered_poly(path, dist=1/512) { + polygon(path_add_jitter(path, dist, closed=true)); +} + + + + // Section: 3D Modules diff --git a/skin.scad b/skin.scad index 40237fb..e53def9 100644 --- a/skin.scad +++ b/skin.scad @@ -542,6 +542,7 @@ function _skin_core(profiles, caps) = // Function: subdivide_and_slice() +// Topics: Paths, Path Subdivision // Usage: // newprof = subdivide_and_slice(profiles, slices, , , ); // Description: @@ -569,7 +570,39 @@ function subdivide_and_slice(profiles, slices, numpoints, method="length", close slice_profiles(fixpoly, slices, closed); +// Function: subdivide_long_segments() +// Topics: Paths, Path Subdivision +// See Also: subdivide_path(), subdivide_and_slice(), path_add_jitter(), jittered_poly() +// Usage: +// spath = subdivide_long_segments(path, maxlen, ); +// Description: +// Evenly subdivides long `path` segments until they are all shorter than `maxlen`. +// Arguments: +// path = The path to subdivide. +// maxlen = The maximum allowed path segment length. +// --- +// closed = If true, treat path like a closed polygon. Default: true +// Example: +// path = pentagon(d=100); +// spath = subdivide_long_segments(path, 10, closed=true); +// stroke(path); +// color("lightgreen") move_copies(path) circle(d=5,$fn=12); +// color("blue") move_copies(spath) circle(d=3,$fn=12); +function subdivide_long_segments(path, maxlen, closed=false) = + assert(is_path(path)) + assert(is_finite(maxlen)) + assert(is_bool(closed)) + [ + for (p=pair(path,closed)) let( + steps = ceil(norm(p[1]-p[0])/maxlen) + ) each lerp(p[0],p[1],[0:1/steps:1-EPSILON]), + if (!closed) last(path) + ]; + + + // Function: slice_profiles() +// Topics: Paths, Path Subdivision // Usage: // profs = slice_profiles(profiles, slices, ); // Description: