Added textured_linear_sweep(), textured_revolution(), and textured_cylinder().

This commit is contained in:
Garth Minette
2022-04-17 15:53:28 -07:00
parent 5c4032f054
commit e5454a048e

414
skin.scad
View File

@@ -2039,5 +2039,419 @@ function associate_vertices(polygons, split, curpoly=0) =
// Section: Texturing
function _get_texture(tex,n,m) =
tex=="ribs"? [[1,0]] :
tex=="trunc_ribs"? [[0, each repeat(1,default(n,1)), 1]] :
tex=="wave_ribs"? [[for(a=[0:360/default(n,8):359]) (cos(a)+1)/2]] :
tex=="diamonds"? [[1,0],[0,1]] :
tex=="pyramids"? [[0,0],[0,1]] :
tex=="trunc_pyramids"? let(n=default(n,2)) [repeat(0,n+1), each repeat([0, each repeat(1,n+1)], n+1)] :
tex=="dimpled_pyramids"? [[0,0,0,0],[0,1,1,1],[0,1,0,1],[0,1,1,1]] :
tex=="hills"? let(n=default(n,12)) [for (a=[0:360/n:359.999]) [for (b=[0:360/n:359.999]) (cos(a)*cos(b)+1)/2]] :
tex=="waves"? let(n=default(n,12)) [for (v=[0:360/n:359.999]) [for (h=[0:360/n:359.999]) max(0,cos(h+90*cos(v)))]] :
tex=="dots"? let(n=default(n,12), m=default(m,0)) [for (y=[0:1:n-1]) [for (x=[0:1:n-1]) max(0,cos(90*norm([n,n]/2-[x,y])*2/(n-m))) ]] :
tex=="cones"? let(n=default(n,12), m=default(m,0)) [for (y=[0:1:n-1]) [for (x=[0:1:n-1]) max(0,1-(norm([n,n]/2-[x,y])*2/(n-m))) ]] :
assert(false, str("Unrecognized texture name: ", tex));
// Function&Module: textured_linear_sweep()
// Usage: As Function
// vnf = textured_linear_sweep(path, texture, tex_size, h, ...);
// vnf = textured_linear_sweep(path, texture, counts=, h=, ...);
// Usage: As Module
// textured_linear_sweep(path, texture, tex_size, h, ...) [ATTACHMENTS];
// textured_linear_sweep(path, texture, counts=, h=, ...) [ATTACHMENTS];
// Topics: Sweep, Extrusion, Textures
// Description:
// Given a single polygon path, creates a linear extrusion of that polygon vertically, with a given texture tiled evenly over the side surfaces.
// Arguments:
// path = The path to sweep/extrude.
// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0) that define the texture to apply to vertical surfaces.
// tex_size = An optional 2D target size for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]`
// h / l = The height to extrude/sweep the path.
// ---
// counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height.
// inset = If numeric, lowers the texture into the surface by that amount, before the tscale multiplier is applied. If `true`, insets by exactly `1`. Default: `false`
// rot = If true, rotates the texture 90º.
// tscale = Scaling multiplier for the texture depth.
// twist = Degrees of twist for the top of the extrustion/sweep, compared to the bottom. Default: 0
// scale = Scaling multiplier for the top of the extrustion/sweep, compared to the bottom. Default: 1
// shift = [X,Y] amount to translate the top, relative to the bottom. Default: [0,0]
// caps = (function only) If true, create endcaps for the extruded shape.
// col_wrap = (function only) If true, the path is considered a closed polygon.
// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Default: `"min_edge"`
// reverse = If the default faces are facing the wrong way, you can reverse them by setting this to `true`. Default: `false`
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// See Also: textured_revolution(), textured_cylinder()
// Example: "ribs" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "ribs", tex_size=[3,5]);
// Example: Rotated "ribs" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "ribs", tex_size=[3,5], rot=true);
// Example: Truncated "trunc_ribs" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "trunc_ribs", tex_size=[3,5]);
// Example: "wave_ribs" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "wave_ribs", tex_size=[3,5]);
// Example: "diamonds" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, "diamonds", tex_size=[5,10], h=40, style="concave");
// Example: "pyramids" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "pyramids", tex_size=[5,5], style="convex");
// Example: Inverted "pyramids" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "pyramids", tex_size=[5,5], tscale=-1, style="concave");
// Example: "trunc_pyramids" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "trunc_pyramids", tex_size=[5,5], style="convex");
// Example: "trunc_pyramids" with style="concave".
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "trunc_pyramids", tex_size=[5,5], style="concave");
// Example: Inverted "trunc_pyramids" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "trunc_pyramids", tex_size=[5,5], tscale=-1, style="concave");
// Example: "dimpled_pyramids" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, h=40, "dimpled_pyramids", tex_size=[5,5], style="convex");
// Example: "hills" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, "hills", tex_size=[5,5], h=40, style="quincunx");
// Example: "waves" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, "waves", tex_size=[5,10], h=40, style="min_edge");
// Example: "dots" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, "dots", tex_size=[5,5], h=40, style="concave");
// Example: Inverted "dots" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, "dots", tex_size=[5,5], tscale=-1, h=40, style="concave");
// Example: "cones" texture.
// path = glued_circles(r=15, spread=40, tangent=45);
// textured_linear_sweep(path, "cones", tex_size=[5,5], h=40, style="concave");
// Example: As Function
// path = glued_circles(r=15, spread=40, tangent=45);
// vnf = textured_linear_sweep(path, h=40, "trunc_pyramids", tex_size=[5,5], tscale=1, style="convex");
// vnf_polyhedron(vnf, convexity=10);
function textured_linear_sweep(
path, texture,
tex_size=[5,5], h, counts,
inset=false, rot=false, tscale=1,
caps=true, col_wrap=true,
twist, scale, shift,
style="min_edge", reverse=false, l,
anchor=CENTER, spin=0, orient=UP
) =
assert(is_path(path,[2]))
assert(is_bool(caps))
assert(is_bool(reverse))
assert(counts==undef || is_vector(counts,2))
assert(tex_size==undef || is_vector(tex_size,2))
let(
tex = is_string(texture)? _get_texture(texture) : texture,
texture = rot? transpose(tex) : tex,
twist = default(twist, 0),
shift = default(shift, [0,0]),
scale = scale==undef? [1,1,1] : is_num(scale)? [scale,scale,1] : scale,
h = first_defined([h, l, 1]),
plen = path_length(path, closed=col_wrap),
counts = is_vector(counts,2)? counts :
is_vector(tex_size,2)
? [round(plen/tex_size.x), max(1,round(h/tex_size.y)), ]
: [30, 5],
texcnt = [len(texture[0]), len(texture)],
inset = is_num(inset)? inset : inset? 1 : 0,
xcnt = counts.x * texcnt.x,
ycnt = counts.y * texcnt.y,
bases = resample_path(path, n=xcnt+(col_wrap?0:1), closed=col_wrap),
norms = path_normals(bases, closed=col_wrap),
tiles = [
for (i = [0:1:ycnt])
let(
u = i / ycnt,
row = texture[i % texcnt.y],
levpts = [
for (j = [0:1:xcnt-(col_wrap?1:0)])
let(
texh = (row[j % texcnt.x] - inset) * tscale,
xy = bases[j] - norms[j] * texh,
xyz = point3d(xy, (i/ycnt-0.5)*h)
) xyz
]
) apply(move(shift*u) * scale(lerp([1,1,1],scale,u)) * zrot(twist*u), levpts)
],
vnf = vnf_vertex_array(
tiles, caps=caps, style=style, reverse=reverse,
col_wrap=col_wrap, row_wrap=false
)
) reorient(anchor,spin,orient, vnf=vnf, extent=true, p=vnf);
module textured_linear_sweep(
path, texture, tex_size=[5,5], h,
inset=false, rot=false, tscale=1,
twist, scale, shift,
style="min_edge", reverse=false, l, counts,
anchor=CENTER, spin=0, orient=UP,
convexity=10
) {
h = first_defined([h, l]);
vnf = textured_linear_sweep(
path, texture, h=h,
tex_size=tex_size, counts=counts,
inset=inset, rot=rot, tscale=tscale,
twist=twist, scale=scale, shift=shift,
caps=true, col_wrap=true,
style=style, reverse=reverse,
anchor=CENTER, spin=0, orient=UP
);
attachable(anchor,spin,orient, vnf=vnf, extent=true) {
vnf_polyhedron(vnf, convexity=convexity);
children();
}
}
// Function&Module: textured_revolution()
// Usage: As Function
// vnf = textured_revolution(path, texture, tex_size, [tscale=], ...);
// vnf = textured_revolution(path, texture, counts=, [tscale=], ...);
// Usage: As Module
// textured_revolution(path, texture, tex_size, [tscale=], ...) [ATTACHMENTS];
// textured_revolution(path, texture, counts=, [tscale=], ...) [ATTACHMENTS];
// Topics: Sweep, Extrusion, Textures
// Description:
// Given a single 2D path, fully in the X+ half-plane, revolves that path around the Z axis (after rotating its Y+ to Z+).
// This creates a solid from that surface of revolution, capped top and bottom, with the sides covered in a given tiled texture.
// Arguments:
// path = The path to sweep/extrude.
// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0) that define the texture to apply to vertical surfaces. See {{textured_linear_sweep()}} for what textures are supported.
// tex_size = An optional 2D target size for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]`
// ---
// shift = [X,Y] amount to translate the top, relative to the bottom. Default: [0,0]
// tscale = Scaling multiplier for the texture depth.
// inset = If numeric, lowers the texture into the surface by that amount, before the tscale multiplier is applied. If `true`, insets by exactly `1`. Default: `false`
// rot = If true, rotates the texture 90º.
// caps = (function only) If true, create endcaps for the extruded shape. Default: `true`
// col_wrap = (function only) If true, the path is considered a closed polygon. Useful mainly for things like making a textured torus. Default: `false`
// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Default: `"min_edge"`
// reverse = If the default faces are facing the wrong way, you can reverse them by setting this to `true`. Default: `false`
// counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// See Also: textured_linear_sweep(), textured_cylinder()
// Example:
// include <BOSL2/beziers.scad>
// bezpath = [
// [15, 30], [10,15],
// [10, 0], [20, 10], [30,12],
// [30,-12], [20,-10], [10, 0],
// [10,-15], [15,-30]
// ];
// path = bezpath_curve(bezpath, splinesteps=32);
// textured_revolution(path, "diamonds", tex_size=[5,5], tscale=1, style="concave");
// Example:
// path = [
// [20, 30], [20, 20],
// each arc(r=20, corner=[[20,20],[10,0],[20,-20]]),
// [20,-20], [20,-30],
// ];
// vnf = textured_revolution(path, "trunc_pyramids", tex_size=[5,5], tscale=1, style="convex");
// vnf_polyhedron(vnf, convexity=10);
function textured_revolution(
path, texture, tex_size,
tscale=1, inset=false, rot=false,
caps=true, wrap=false, shift=[0,0],
style="min_edge", reverse=false,
counts
) =
assert(is_path(path,[2]))
assert(is_bool(caps))
assert(is_bool(wrap))
assert(is_bool(reverse))
assert(counts==undef || is_vector(counts,2))
assert(tex_size==undef || is_vector(tex_size,2))
let(
tex = is_string(texture)? _get_texture(texture) : texture,
texture = rot? transpose(tex) : tex,
plen = path_length(path),
maxx = max(column(path,0)),
circumf = 2 * PI * maxx,
counts = is_vector(counts,2)? counts :
is_vector(tex_size,2)
? [round(circumf/tex_size.x), round(plen/tex_size.y)]
: [30, 5],
texcnt = [len(texture[0]), len(texture)],
inset = is_num(inset)? inset : inset? 1 : 0,
xcnt = counts.x * texcnt.x,
ycnt = counts.y * texcnt.y,
bases = resample_path(path, n=ycnt+1, closed=false),
norms = path_normals(bases),
tiles = [
for (i = [0:1:ycnt])
let(row = texture[i % texcnt.y]) [
for (j = [0:1:xcnt-1])
let(
tscale = !wrap && (i==0 || i==ycnt)? 0 : tscale,
texh = tscale * (row[j % texcnt.x] - inset) * (bases[i].x/maxx),
xy = bases[i] - texh * norms[i],
xyz = lerp([0,0,0],point3d(shift),i/ycnt) + rot([90, 0, 360*j/xcnt], p=point3d(xy))
)
xyz
]
],
vnf = vnf_vertex_array(
tiles, caps=caps, style=style, reverse=reverse,
col_wrap=true, row_wrap=wrap
)
) vnf;
module textured_revolution(
path, texture, tex_size,
tscale=1, inset=false, rot=false,
caps=true, wrap=false, shift=[0,0],
style="min_edge", reverse=false,
atype="surface",
convexity=10, counts,
anchor=CENTER, spin=0, orient=UP
) {
assert(in_list(atype, ["surface","extent"]));
vnf = textured_revolution(
path, texture, tex_size=tex_size,
tscale=tscale, inset=inset, rot=rot,
caps=caps, wrap=wrap, style=style,
reverse=reverse, shift=shift,
counts=counts
);
geom = atype=="surface"
? attach_geom(vnf=vnf, extent=false)
: attach_geom(vnf=vnf, extent=true);
attachable(anchor,spin,orient, geom=geom) {
vnf_polyhedron(vnf, convexity=convexity);
children();
}
}
// Function&Module: textured_cylinder()
// Usage: As Function
// vnf = textured_cylinder(h|l=, r|d=, texture, tex_size|counts=, [tscale=], [inset=], [rot=], ...);
// vnf = textured_cylinder(h|l=, r1=|d1=, r2=|d2=, texture=, tex_size=|counts=, [tscale=], [inset=], [rot=], ...);
// Usage: As Module
// textured_cylinder(h, r|d=, texture, tex_size|counts=, [tscale=], [inset=], [rot=], ...) [ATTACHMENTS];
// textured_cylinder(h, r1=|d1=, r2=|d2=, texture=, tex_size=|counts=, [tscale=], [inset=], [rot=], ...) [ATTACHMENTS];
// Topics: Sweep, Extrusion, Textures
// Description:
// Creates a cylinder or cone with optional chamfers or roundings, covered in a textured surface.
// Arguments:
// h | l = The height of the cylinder.
// r = The radius of the cylinder.
// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0) that define the texture to apply to vertical surfaces. See {{textured_linear_sweep()}} for supported textures.
// tex_size = An optional 2D target size for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]`
// ---
// r1 = The radius of the bottom of the cylinder.
// r2 = The radius of the top of the cylinder.
// d = The diameter of the cylinder.
// d1 = The diameter of the bottom of the cylinder.
// d2 = The diameter of the top of the cylinder.
// tscale = Scaling multiplier for the texture depth.
// inset = If numeric, lowers the texture into the surface by that amount, before the tscale multiplier is applied. If `true`, insets by exactly `1`. Default: `false`
// rot = If true, rotates the texture 90º.
// caps = (function only) If true, create endcaps for the extruded shape. Default: `true`
// shift = [X,Y] amount to translate the top, relative to the bottom. Default: [0,0]
// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Default: `"min_edge"`
// reverse = If the default faces are facing the wrong way, you can reverse them by setting this to `true`. Default: `false`
// counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height.
// chamfer = If given, chamfers the top and bottom of the cylinder by the given size.
// chamfer1 = If given, chamfers the bottom of the cylinder by the given size.
// chamfer2 = If given, chamfers the top of the cylinder by the given size.
// rounding = If given, rounds the top and bottom of the cylinder to the given radius.
// rounding1 = If given, rounds the bottom of the cylinder to the given radius.
// rounding2 = If given, rounds the top of the cylinder to the given radius.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// See Also: textured_linear_sweep(), textured_revolution()
// Examples:
// textured_cylinder(h=40, r=20, texture="diamonds", tex_size=[5,5]);
// textured_cylinder(h=40, r1=20, r2=15, texture="pyramids", tex_size=[5,5], style="concave");
// textured_cylinder(h=40, r1=20, r2=15, texture="trunc_pyramids", tex_size=[5,5], chamfer=5, style="convex");
// textured_cylinder(h=40, r1=20, r2=15, texture="dots", tex_size=[5,5], rounding=8, style="convex");
function textured_cylinder(
h=20, r=100, texture, tex_size=[5,5], counts,
tscale=1, inset=false, rot=false,
caps=true, style="min_edge",
reverse=false, shift=[0,0],
l, r1, r2, d, d1, d2,
chamfer, chamfer1, chamfer2,
rounding, rounding1, rounding2
) =
let(
h = first_defined([h, l]),
r1 = get_radius(r1=r1, r=r, d1=d1, d=d),
r2 = get_radius(r1=r2, r=r, d1=d2, d=d),
chamf1 = first_defined([chamfer1, chamfer]),
chamf2 = first_defined([chamfer2, chamfer]),
round1 = first_defined([rounding1, rounding]),
round2 = first_defined([rounding2, rounding]),
path = [
if (is_finite(chamf1)) each arc(n=2, r=chamf1, corner=[[0,-h/2],[r1,-h/2],[r2,h/2]])
else if (is_finite(round1)) each arc(r=round1, corner=[[0,-h/2],[r1,-h/2],[r2,h/2]])
else [r1,-h/2],
if (is_finite(chamf2)) each arc(n=2, r=chamf2, corner=[[r1,-h/2],[r2,h/2],[0,h/2]])
else if (is_finite(round2)) each arc(r=round2, corner=[[r1,-h/2],[r2,h/2],[0,h/2]])
else [r2,h/2],
],
vnf = textured_revolution(
reverse(path), texture,
tex_size=tex_size, counts=counts,
tscale=tscale, inset=inset, rot=rot,
caps=caps, style=style, reverse=reverse,
shift=shift
)
) vnf;
module textured_cylinder(
texture, tex_size=[5,5], h=20, r=50,
counts, tscale=1, inset=false, rot=false,
style="min_edge", reverse=false, shift=[0,0],
l, r1, r2, d, d1, d2,
chamfer, chamfer1, chamfer2,
rounding, rounding1, rounding2,
convexity=10,
anchor=CENTER, spin=0, orient=UP
) {
h = first_defined([h, l]);
r1 = get_radius(r1=r1, r=r, d1=d1, d=d);
r2 = get_radius(r1=r2, r=r, d1=d2, d=d);
chamf1 = first_defined([chamfer1, chamfer]);
chamf2 = first_defined([chamfer2, chamfer]);
round1 = first_defined([rounding1, rounding]);
round2 = first_defined([rounding2, rounding]);
vnf = textured_cylinder(
texture=texture, h=h, r1=r1, r2=r2,
tscale=tscale, inset=inset, rot=rot,
counts=counts, tex_size=tex_size,
caps=true, style=style,
reverse=reverse, shift=shift,
chamfer1=chamf1, chamfer2=chamf2,
rounding1=round1, rounding2=round2
);
attachable(anchor,spin,orient, r1=r1, r2=r2, h=h, shift=shift) {
vnf_polyhedron(vnf, convexity=convexity);
children();
}
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap