Compare commits

...

3 commits

Author SHA1 Message Date
Alex Matulich
11b801ec46 added error check for 'uniform' parameter, reformatted some code 2025-01-05 08:06:52 -08:00
Alex Matulich
eb66ba3e9d fixed description indentation error in rounding.scad 2025-01-05 06:19:12 -08:00
Alex Matulich
4940551b05 fixed 2D example that should have been 3D in squircle 2025-01-05 05:52:39 -08:00
3 changed files with 76 additions and 67 deletions

View file

@ -634,7 +634,7 @@ function path_to_bezpath(path, closed, tangents, uniform=false, size, relsize) =
second + L*tangent2
],
select(path,lastpt)
];
];
@ -666,30 +666,31 @@ function path_to_bezpath(path, closed, tangents, uniform=false, size, relsize) =
function path_to_bezcornerpath(path, closed, size, relsize) =
is_1region(path) ? path_to_bezcornerpath(path[0], default(closed,true), tangents, size, relsize) :
let(closed=default(closed,false))
assert(is_bool(closed))
assert(num_defined([size,relsize])<=1, "Can't define both size and relsize")
assert(is_path(path,[2,3]),"Input path is not a valid 2d or 3d path")
let(
curvesize = first_defined([size,relsize,0.5]),
relative = is_undef(size),
pathlen = len(path)
assert(is_bool(closed))
assert(num_defined([size,relsize])<=1, "Can't define both size and relsize")
assert(is_path(path,[2,3]),"Input path is not a valid 2d or 3d path")
let(
curvesize = first_defined([size,relsize,0.5]),
relative = is_undef(size),
pathlen = len(path)
)
assert(is_num(curvesize) || len(curvesize)==pathlen, str("Size or relsize must have length ",pathlen))
let(sizevect = is_num(curvesize) ? repeat(curvesize, pathlen) : curvesize)
assert(min(sizevect)>0, "Size or relsize must be greater than zero")
let(
roundpath = closed ? [
for(i=[0:pathlen-1]) let(p3=select(path,[i-1:i+1]))
_bez_path_corner([0.5*(p3[0]+p3[1]), p3[1], 0.5*(p3[1]+p3[2])], sizevect[i], relative),
[0.5*(path[0]+path[pathlen-1])]
]
: [ for(i=[1:pathlen-2]) let(p3=select(path,[i-1:i+1]))
_bez_path_corner(
[i>1?0.5*(p3[0]+p3[1]):p3[0], p3[1], i<pathlen-2?0.5*(p3[1]+p3[2]):p3[2]],
sizevect[i], relative),
[path[pathlen-1]]
]
)
assert(is_num(curvesize) || len(curvesize)==pathlen, str("Size or relsize must have length ",pathlen))
let(sizevect = is_num(curvesize) ? repeat(curvesize, pathlen) : curvesize)
assert(min(sizevect)>0, "Size or relsize must be greater than zero")
let(
roundpath = closed ? [
for(i=[0:pathlen-1]) let(p3=select(path,[i-1:i+1]))
_bez_path_corner([0.5*(p3[0]+p3[1]), p3[1], 0.5*(p3[1]+p3[2])], sizevect[i], relative),
[0.5*(path[0]+path[pathlen-1])]
]
: [ for(i=[1:pathlen-2]) let(p3=select(path,[i-1:i+1]))
_bez_path_corner(
[i>1?0.5*(p3[0]+p3[1]):p3[0], p3[1], i<pathlen-2?0.5*(p3[1]+p3[2]):p3[2]],
sizevect[i], relative),
[path[pathlen-1]]
]
) flatten(roundpath);
flatten(roundpath);
/// Internal function: _bez_path_corner()
@ -743,9 +744,7 @@ let(
// bz6 is p3
bz3 = p2 + middir * bzdist, // center control point
bz2 = bz3 + midto12unit*(d1<d3 ? cornerlegmin : cornerlegmax),
bz1 = p1 - (d1<=d3 ? leglenmin :
leglenmax)*p21unit,
//norm(0.333*(bz2-p1)))*p21unit,
bz1 = p1 - (d1<=d3 ? leglenmin : leglenmax)*p21unit,
bz4 = bz3 - midto12unit*(d3<d1 ? cornerlegmin : cornerlegmax),
bz5 = p3 - (d3<=d1 ? leglenmin : leglenmax)*p23unit
) [p1, bz1, bz2, bz3, bz4, bz5]; // do not include last control point

View file

@ -629,7 +629,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
// relative to the segment length (e.g. 0.05 means 5% of the segment length). For the "corners" method,
// `relsize` determines where the curve intersects the corner bisector, relative to the maximum deviation
// possible (which corresponds to a circle rounding from the shortest leg of the corner). For example,
// `relsize=1` is the maximum deviation from the corner (a circle arc from the shortest leg), and `relsize=0.5`
// `relsize=1` is the maximum deviation from the corner (a circle arc from the shortest leg), and `relsize=0.5`
// causes the curve to intersect the corner bisector halfway between the maximum and the tip of the corner.
// .
// At a given segment or corner (depending on the method) there is a maximum size: a size value that is too
@ -735,18 +735,20 @@ function _rounding_offsets(edgespec,z_dir=1) =
// pts = [[-3.3, 1.7], [-3.7, -2.2], [3.8, -4.8], [-0.9, -2.4]];
// stroke(smooth_path(pts, uniform=false, relsize=0.1),width=.1);
// color("red")move_copies(pts)circle(r=.15,$fn=12);
module smooth_path(path, tangents, size, relsize, method="edges", splinesteps=10, uniform=false, closed=false) {no_module();}
function smooth_path(path, tangents, size, relsize, method="edges", splinesteps=10, uniform=false, closed) =
is_1region(path) ? smooth_path(path[0], tangents, size, relsize, method, splinesteps, uniform, default(closed,true)) :
assert(method=="edges" || method=="corners", "method must be \"edges\" or \"corners\".")
assert(method=="edges" || is_undef(tangent), "The tangents parameter is incompatible with method=\"corners\".")
let (
bez = method=="edges" ?
path_to_bezpath(path, tangents=tangents, size=size, relsize=relsize, uniform=uniform, closed=default(closed,false))
module smooth_path(path, tangents, size, relsize, method="edges", splinesteps=10, uniform, closed=false) {no_module();}
function smooth_path(path, tangents, size, relsize, method="edges", splinesteps=10, uniform, closed) =
is_1region(path)
? smooth_path(path[0], tangents, size, relsize, method, splinesteps, uniform, default(closed,true))
: assert(method=="edges" || method=="corners", "method must be \"edges\" or \"corners\".")
assert(method=="edges" || (is_undef(tangents) && is_undef(uniform)), "The tangents and uniform parameters are incompatible with method=\"corners\".")
let (
uniform = default(uniform,false),
bez = method=="edges"
? path_to_bezpath(path, tangents=tangents, size=size, relsize=relsize, uniform=uniform, closed=default(closed,false))
: path_to_bezcornerpath(path, size=size, relsize=relsize, closed=default(closed,false)),
smoothed = bezpath_curve(bez,splinesteps=splinesteps)
)
closed ? list_unwrap(smoothed) : smoothed;
smoothed = bezpath_curve(bez,splinesteps=splinesteps)
)
closed ? list_unwrap(smoothed) : smoothed;
@ -3402,11 +3404,11 @@ Access to the derivative smoothing parameter?
// aux="sphere",aux_r=-30,fillet=8, overlap=17);
// }
// Example(3D,VPT=[0.59633,-3.01826,-3.89606],VPR=[129.2,0,26.4],VPD=192.044,NoScales): Here we have rotated the auxiliary sphere which results in a hole that is off-center through the sphere. Because we rotate the auxiliary object, both ends of the prism have moved. Note that setting k to a large value better matches the bezier curve to the curvature of the sphere, resulting in a better result.
// difference(){
// spheroid(r=30,circum=true);
// join_prism(circle(r=15),base="sphere",base_r=-30, n=15,
// aux="sphere",aux_T=xrot(30), aux_r=-30,fillet=8, overlap=17, k=0.9);
// }
// difference(){
// spheroid(r=30,circum=true);
// join_prism(circle(r=15),base="sphere",base_r=-30, n=15,
// aux="sphere",aux_T=xrot(30), aux_r=-30,fillet=8, overlap=17, k=0.9);
// }
// Example(3D,VPT=[-12.5956,-5.1125,-0.322237],VPR=[82.3,0,116.7],VPD=213.382,NoScales): Here we adjust just the auxiliary end, which note is at the bottom. We rotate it by 45 deg, but this rotation would normally be relative to the other prism end, so we add a centerpoint based on the radius so that the rotation is relative to the sphere center instead.
// difference(){
// spheroid(r=30,circum=true);
@ -3414,14 +3416,14 @@ Access to the derivative smoothing parameter?
// aux="sphere",prism_end_T=xrot(45,cp=[0,0,-30]), aux_r=-30,fillet=8, overlap=17, k=0.9);
// }
// Example(3D,NoScales,VPT=[12.3373,11.6037,-1.87883],VPR=[40.3,0,323.4],VPD=292.705): A diagonal hole through a cylinder with rounded ends, created by shifting the auxiliary prism end along the prism length.
// back_half(200)
// back_half(200)
// difference(){
// right(15)xcyl(r=30,l=100,circum=true);
// join_prism(circle(r=15),base="cyl",base_r=-30, n=15,
// aux="cyl",prism_end_T=right(35),aux_r=-30,fillet=7, overlap=17);
// }
// Example(3D,NoScales,VPT=[-7.63774,-0.808304,13.8874],VPR=[46.6,0,71.2],VPD=237.091): A hole created by shifting along prism width.
// left_half()
// left_half()
// difference(){
// xcyl(r=30,l=100,circum=true);
// join_prism(circle(r=15),base="cyl",base_r=-30, n=15,

View file

@ -1793,7 +1793,7 @@ module glued_circles(r, spread=10, tangent=30, d, anchor=CENTER, spin=0) {
// Examples(2D):
// squircle(size=50, squareness=0.4);
// squircle([80,60], 0.7, $fn=64);
// Example(2D,VPD=48,VPR=[40,0,40],NoAxes): Corner differences between the three squircle styles for squareness=0.5. Style "superellipse" is pink, "fg" is gold, "bezier" is blue.
// Example(3D,VPD=48,VPR=[40,0,40],NoAxes): Corner differences between the three squircle styles for squareness=0.5. Style "superellipse" is pink, "fg" is gold, "bezier" is blue.
// color("pink") squircle(size=50, style="superellipse", squareness=0.5, $fn=256);
// color("yellow") up(1) squircle(size=50, style="fg", squareness=0.5, $fn=256);
// color("lightblue") up(2) squircle(size=50, style="bezier", squareness=0.5, $fn=256);
@ -1854,15 +1854,17 @@ function _squircle_fg(size, squareness) = [
) p*[cos(theta), aspect*sin(theta)]
];
function squircle_radius_fg(squareness, r, angle) = let(
s2a = abs(squareness*sin(2*angle))
) s2a>0 ? r*sqrt(2)/s2a * sqrt(1 - sqrt(1 - s2a*s2a)) : r;
function squircle_radius_fg(squareness, r, angle) =
let(
s2a = abs(squareness*sin(2*angle))
)
s2a>0 ? r*sqrt(2)/s2a * sqrt(1 - sqrt(1 - s2a*s2a)) : r;
function _linearize_squareness(s) =
// from Chamberlain Fong (2016). "Squircular Calculations". arXiv.
// https://arxiv.org/pdf/1604.02174v5
let(c = 2 - 2*sqrt(2), d = 1 - 0.5*c*s)
2 * sqrt((1+c)*s*s - c*s) / (d*d);
2 * sqrt((1+c)*s*s - c*s) / (d*d);
/* Superellipse squircle functions */
@ -1884,27 +1886,33 @@ function _squircle_se(size, squareness) = [
) [ra*x, rb*y] / r
];
function squircle_radius_se(n, r, angle) = let(
x = cos(angle),
y = sin(angle)
) (abs(x)^n + abs(y)^n)^(1/n) / r;
function squircle_radius_se(n, r, angle) =
let(
x = cos(angle),
y = sin(angle)
)
(abs(x)^n + abs(y)^n)^(1/n) / r;
function _squircle_se_exponent(squareness) = let(
// limit squareness; error if >0.99889, limit is smaller for r>1
s=min(0.998,squareness),
rho = 1 + s*(sqrt(2)-1),
x = rho / sqrt(2)
) log(0.5) / log(x);
function _squircle_se_exponent(squareness) =
let(
// limit squareness; error if >0.99889, limit is smaller for r>1
s=min(0.998,squareness),
rho = 1 + s*(sqrt(2)-1),
x = rho / sqrt(2)
)
log(0.5) / log(x);
/* Bezier squircle function */
function _squircle_bz(size, squareness) = let(
splinesteps = $fn>=12 ? round($fn/4) : 10,
size = is_num(size) ? [size,size] : point2d(size),
sq = square(size, center=true),
bez = path_to_bezcornerpath(sq, relsize=1-squareness, closed=true)
) bezpath_curve(bez, splinesteps=splinesteps);
function _squircle_bz(size, squareness) =
let(
splinesteps = $fn>=12 ? round($fn/4) : 10,
size = is_num(size) ? [size,size] : point2d(size),
sq = square(size, center=true),
bez = path_to_bezcornerpath(sq, relsize=1-squareness, closed=true)
)
bezpath_curve(bez, splinesteps=splinesteps);