From 60a565381a19265e8a0dfef003decd5d13c5f65b Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sat, 20 Aug 2022 23:31:50 -0700 Subject: [PATCH 1/3] Hid textured_*() functions/modules. Integrated textures into cyl(), linear_sweep(), and rotate_sweep() --- shapes3d.scad | 339 ++++++++++++++++----- skin.scad | 813 ++++++++++++++++++++++++++++---------------------- vnf.scad | 5 + 3 files changed, 728 insertions(+), 429 deletions(-) diff --git a/shapes3d.scad b/shapes3d.scad index aaa968d..1c1b495 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -52,6 +52,7 @@ use // Example: Called as Function // vnf = cube([20,40,50]); // vnf_polyhedron(vnf); + module cube(size=1, center, anchor, spin=0, orient=UP) { anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]); @@ -177,6 +178,7 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = // cuboid([4,2,1], rounding=2, edges=[FWD+RIGHT,BACK+LEFT]); // Example: Standard Connectors // cuboid(40) show_anchors(); + module cuboid( size=[1,1,1], p1, p2, @@ -633,6 +635,7 @@ function cuboid( // Example(Spin,VPD=160,VPT=[0,0,10]): Standard Connectors // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]) // show_anchors(); + module prismoid( size1, size2, h, shift=[0,0], rounding=0, rounding1, rounding2, @@ -775,6 +778,7 @@ function prismoid( // octahedron(size=40); // Example: Anchors // octahedron(size=40) show_anchors(); + module octahedron(size=1, anchor=CENTER, spin=0, orient=UP) { vnf = octahedron(size=size); attachable(anchor,spin,orient, vnf=vnf, extent=true) { @@ -900,6 +904,7 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) = // rounding1=[5,0,10,0], irounding1=[3,0,8,0], // rounding2=[0,5,0,10], irounding2=[0,3,0,8] // ); + module rect_tube( h, size, isize, center, shift=[0,0], wall, size1, size2, isize1, isize2, @@ -1014,6 +1019,7 @@ function rect_tube( // wedge([20, 40, 15]); // Example: Standard Connectors // wedge([20, 40, 15]) show_anchors(); + module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP) { size = scalar_vec3(size); @@ -1095,6 +1101,7 @@ function wedge(size=[1,1,1], center, anchor, spin=0, orient=UP) = // cylinder(h=30, d=25) show_anchors(); // cylinder(h=30, d1=25, d2=10) show_anchors(); // } + module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) { anchor = get_anchor(anchor, center, BOTTOM, BOTTOM); @@ -1128,13 +1135,7 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) -// Module: cyl() -// -// Description: -// Creates cylinders in various anchorings and orientations, with optional rounding and chamfers. -// You can use `h` and `l` interchangably, and all variants allow specifying size by either `r`|`d`, -// or `r1`|`d1` and `r2`|`d2`. Note: the chamfers and rounding cannot be cumulatively longer than -// the cylinder's length. +// Function&Module: cyl() // // Usage: Normal Cylinders // cyl(l|h, r, [center], [circum=], [realign=]) [ATTACHMENTS]; @@ -1154,6 +1155,14 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // cyl(l|h, r|d, rounding2=, ...); // cyl(l|h, r|d, rounding1=, rounding2=, ...); // +// Topics: Cylinders, Textures, Rounding, Chamfers +// +// Description: +// Creates cylinders in various anchorings and orientations, with optional rounding and chamfers. +// You can use `h` and `l` interchangably, and all variants allow specifying size by either `r`|`d`, +// or `r1`|`d1` and `r2`|`d2`. Note: the chamfers and rounding cannot be cumulatively longer than +// the cylinder's length. +// // Arguments: // l / h = Length of cylinder along oriented axis. Default: 1 // r = Radius of cylinder. Default: 1 @@ -1165,6 +1174,7 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // d1 = Diameter of the negative (X-, Y-, Z-) end of cylinder. // d2 = Diameter of the positive (X+, Y+, Z+) end of cylinder. // circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false` +// shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end. // chamfer = The size of the chamfers on the ends of the cylinder. Default: none. // chamfer1 = The size of the chamfer on the bottom end of the cylinder. Default: none. // chamfer2 = The size of the chamfer on the top end of the cylinder. Default: none. @@ -1176,10 +1186,20 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // rounding1 = The radius of the rounding on the bottom end of the cylinder. // rounding2 = The radius of the rounding on the top end of the cylinder. // realign = If true, rotate the cylinder by half the angle of one face. +// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named 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]` +// tex_counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height. +// tex_inset = If numeric, lowers the texture into the surface by that amount, before the tex_scale multiplier is applied. If `true`, insets by exactly `1`. Default: `false` +// tex_rot = If true, rotates the texture 90º. +// tex_scale = Scaling multiplier for the texture depth. +// tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 +// tex_style = {{vnf_vertex_array()}} style used to triangulate heightfield textures. Default: "min_edge" // 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: texture(), rotate_sweep() +// // Example: By Radius // xdistribute(30) { // cyl(l=40, r=10); @@ -1230,6 +1250,156 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // cyl(l=30, d1=25, d2=10) show_anchors(); // } // +// Example: Texturing with heightfield diamonds +// cyl(h=40, r=20, texture="diamonds", tex_size=[5,5]); +// +// Example: Texturing with heightfield pyramids +// cyl(h=40, r1=20, r2=15, +// texture="pyramids", tex_size=[5,5], +// tex_style="convex"); +// +// Example: Texturing with heightfield truncated pyramids +// cyl(h=40, r1=20, r2=15, chamfer=5, +// texture="trunc_pyramids", +// tex_size=[5,5], tex_style="convex"); +// +// Example: Texturing with VNF tile "vnf_dots" +// cyl(h=40, r1=20, r2=15, rounding=9, +// texture="vnf_dots", tex_size=[5,5], +// samples=6); +// +// Example: Texturing with VNF tile "vnf_bricks" +// cyl(h=50, r1=25, r2=20, shift=[0,10], rounding1=-10, +// texture="vnf_bricks", tex_size=[10,10], +// tex_scale=0.5, tex_style="concave"); +// +// Example: No Texture Taper +// cyl(d1=25, d2=20, h=30, rounding=5, +// texture="trunc_ribs", tex_size=[5,1]); +// +// Example: Taper Texure at Extreme Ends +// cyl(d1=25, d2=20, h=30, rounding=5, +// texture="trunc_ribs", taper=0, +// tex_size=[5,1]); +// +// Example: Taper Texture over First and Last 10% +// cyl(d1=25, d2=20, h=30, rounding=5, +// texture="trunc_ribs", taper=10, +// tex_size=[5,1]); + +function cyl( + h, r, center, + l, r1, r2, + d, d1, d2, + length, height, + chamfer, chamfer1, chamfer2, + chamfang, chamfang1, chamfang2, + rounding, rounding1, rounding2, + circum=false, realign=false, + from_end=false, shift=[0,0], + texture, tex_size=[5,5], tex_counts, + tex_inset=false, tex_rot=false, + tex_scale=1, tex_samples, + tex_taper, tex_style="min_edge", + anchor, spin=0, orient=UP +) = + let( + l = first_defined([l, h, length, height, 1]), + _r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1), + _r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1), + sides = segs(max(_r1,_r2)), + sc = circum? 1/cos(180/sides) : 1, + r1 = _r1 * sc, + r2 = _r2 * sc, + phi = atan2(l, r2-r1), + anchor = get_anchor(anchor,center,BOT,CENTER) + ) + assert(is_finite(l), "l/h/length/height must be a finite number.") + assert(is_finite(r1), "r/r1/d/d1 must be a finite number.") + assert(is_finite(r2), "r2 or d2 must be a finite number.") + assert(is_vector(shift,2), "shift must be a 2D vector.") + let( + vnf = texture != undef? _textured_cylinder( + l=l, r1=r1, r2=r2, + texture=texture, tex_size=tex_size, + counts=tex_counts, tex_scale=tex_scale, + inset=tex_inset, rot=tex_rot, + style=tex_style, taper=tex_taper, + chamfer=chamfer, + chamfer1=chamfer1, + chamfer2=chamfer2, + rounding=rounding, + rounding1=rounding1, + rounding2=rounding2, + samples=tex_samples + ) : + !any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])? + cylinder(h=l, r1=r1, r2=r2, center=true, $fn=sides) : + let( + vang = atan2(l, r1-r2)/2, + chang = default(chamfang, 45), + chang1 = 90-first_defined([chamfang1, chamfang, vang]), + chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]), + checks1 = + assert(is_finite(chang) && chang>0 && chang<90, "chamfang must be a number between 0 and 90 (exclusive) if given.") + assert(is_finite(chang1) && chang1>0 && chang1<90, "chamfang1 must be a number between 0 and 90 (exclusive) if given.") + assert(is_finite(chang2) && chang2>0 && chang2<90, "chamfang2 must be a number between 0 and 90 (exclusive) if given.") + undef, + chamf = default(chamfer, 0) * (from_end? 1 : tan(chang1)), + chamf1 = first_defined([chamfer1, chamfer, 0]) * (from_end? 1 : tan(chang1)), + chamf2 = first_defined([chamfer2, chamfer, 0]) * (from_end? 1 : tan(chang2)), + round = default(rounding, 0), + round1 = first_defined([rounding1, rounding, 0]), + round2 = first_defined([rounding2, rounding, 0]), + dy1 = abs(first_defined([chamf1, round1, 0])), + dy2 = abs(first_defined([chamf2, round2, 0])), + checks2 = + assert(is_finite(chamf), "chamfer must be a finite number if given.") + assert(is_finite(chamf1), "chamfer1 must be a finite number if given.") + assert(is_finite(chamf2), "chamfer2 must be a finite number if given.") + assert(is_finite(round), "rounding must be a finite number if given.") + assert(is_finite(round1), "rounding1 must be a finite number if given.") + assert(is_finite(round2), "rounding2 must be a finite number if given.") + assert(chamf <= r1, "chamfer is larger than the r1 radius of the cylinder.") + assert(chamf <= r2, "chamfer is larger than the r2 radius of the cylinder.") + assert(chamf1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.") + assert(chamf2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.") + assert(round <= r1, "rounding is larger than the r1 radius of the cylinder.") + assert(round <= r2, "rounding is larger than the r2 radius of the cylinder.") + assert(round1 <= r1, "rounding1 is larger than the r1 radius of the cylinder.") + assert(round2 <= r2, "rounding2 is larger than the r1 radius of the cylinder.") + assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder.") + undef, + path = [ + [0,-l/2], + if (is_finite(chamf1) && !approx(chamf1,0)) + let( + p1 = [r1-chamf1/tan(chang1),-l/2], + p2 = lerp([r1,-l/2],[r2,l/2],abs(chamf1)/l) + ) each [p1,p2] + else if (is_finite(round1) && !approx(round1,0)) + each arc(r=abs(round1), corner=[[(round1>0?0:1e6),-l/2],[r1,-l/2],[r2,l/2]]) + else [r1,-l/2], + if (is_finite(chamf2) && !approx(chamf2,0)) + let( + p1 = lerp([r2,l/2],[r1,-l/2],abs(chamf2)/l), + p2 = [r2-chamf2/tan(chang2),l/2] + ) each [p1,p2] + else if (is_finite(round2) && !approx(round2,0)) + each arc(r=abs(round2), corner=[[r1,-l/2],[r2,l/2],[(round2>0?0:1e6),l/2]]) + else [r2,l/2], + [0,l/2] + ] + ) rotate_sweep(path), + skmat = down(l/2) * + skew(sxz=shift.x/l, syz=shift.y/l) * + up(l/2) * + zrot(realign? 180/sides : 0), + ovnf = apply(skmat, vnf) + ) + reorient(anchor,spin,orient, r1=r1, r2=r2, l=l, shift=shift, p=ovnf); + + module cyl( h, r, center, l, r1, r2, @@ -1237,7 +1407,12 @@ module cyl( chamfer, chamfer1, chamfer2, chamfang, chamfang1, chamfang2, rounding, rounding1, rounding2, - circum=false, realign=false, from_end=false, + circum=false, realign=false, + from_end=false, shift=[0,0], + texture, tex_size=[5,5], tex_counts, + tex_inset=false, tex_rot=false, + tex_scale=1, tex_samples, + tex_taper, tex_style="min_edge", anchor, spin=0, orient=UP ) { l = first_defined([l, h, 1]); @@ -1245,86 +1420,76 @@ module cyl( _r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); sides = segs(max(_r1,_r2)); sc = circum? 1/cos(180/sides) : 1; - r1=_r1*sc; - r2=_r2*sc; + r1 = _r1 * sc; + r2 = _r2 * sc; phi = atan2(l, r2-r1); anchor = get_anchor(anchor,center,BOT,CENTER); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + skmat = down(l/2) * skew(sxz=shift.x/l, syz=shift.y/l) * up(l/2); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, shift=shift) { + multmatrix(skmat) zrot(realign? 180/sides : 0) { - if (!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])) { + if (texture != undef) { + _textured_cylinder( + l=l, r1=r1, r2=r2, + texture=texture, tex_size=tex_size, + counts=tex_counts, tex_scale=tex_scale, + inset=tex_inset, rot=tex_rot, + style=tex_style, taper=tex_taper, + chamfer=chamfer, + chamfer1=chamfer1, + chamfer2=chamfer2, + rounding=rounding, + rounding1=rounding1, + rounding2=rounding2, + convexity=10, samples=tex_samples + ); + } else if (!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])) { cylinder(h=l, r1=r1, r2=r2, center=true, $fn=sides); } else { vang = atan2(l, r1-r2)/2; - chang1 = 90-first_defined([chamfang1, chamfang, vang]); + chang1 = 90-first_defined([chamfang1, chamfang, vang,]); chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]); - cham1 = u_mul(first_defined([chamfer1, chamfer]) , (from_end? 1 : tan(chang1))); - cham2 = u_mul(first_defined([chamfer2, chamfer]) , (from_end? 1 : tan(chang2))); - fil1 = first_defined([rounding1, rounding]); - fil2 = first_defined([rounding2, rounding]); - if (chamfer != undef) { - checks = - assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.") - assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder."); - } - if (cham1 != undef) { - check = assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder."); - } - if (cham2 != undef) { - check = assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder."); - } - if (rounding != undef) { - checks = - assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder.") - assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder."); - } - if (fil1 != undef) { - check = assert(fil1 <= r1, "rounding1 is larger than the r1 radius of the cylinder."); - } - if (fil2 != undef) { - check = assert(fil2 <= r2, "rounding2 is larger than the r1 radius of the cylinder."); - } - dy1 = abs(first_defined([cham1, fil1, 0])); - dy2 = abs(first_defined([cham2, fil2, 0])); - check = assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder."); - - path = concat( - [[0,l/2]], - - !is_undef(cham2)? ( + chamf = default(chamfer, 0) * (from_end? 1 : tan(chang1)); + chamf1 = first_defined([chamfer1, chamfer, 0]) * (from_end? 1 : tan(chang1)); + chamf2 = first_defined([chamfer2, chamfer, 0]) * (from_end? 1 : tan(chang2)); + round = default(rounding, 0); + round1 = first_defined([rounding1, rounding, 0]); + round2 = first_defined([rounding2, rounding, 0]); + dy1 = abs(first_defined([chamf1, round1, 0])); + dy2 = abs(first_defined([chamf2, round2, 0])); + checks = + assert(chamf <= r1, "chamfer is larger than the r1 radius of the cylinder.") + assert(chamf <= r2, "chamfer is larger than the r2 radius of the cylinder.") + assert(chamf1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.") + assert(chamf2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.") + assert(round <= r1, "rounding is larger than the r1 radius of the cylinder.") + assert(round <= r2, "rounding is larger than the r2 radius of the cylinder.") + assert(round1 <= r1, "rounding1 is larger than the r1 radius of the cylinder.") + assert(round2 <= r2, "rounding2 is larger than the r1 radius of the cylinder.") + assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder.") + undef; + path = [ + [0,-l/2], + if (is_finite(chamf1) && !approx(chamf1,0)) let( - p1 = [r2-cham2/tan(chang2),l/2], - p2 = lerp([r2,l/2],[r1,-l/2],abs(cham2)/l) - ) [p1,p2] - ) : !is_undef(fil2)? ( + p1 = [r1-chamf1/tan(chang1),-l/2], + p2 = lerp([r1,-l/2],[r2,l/2],abs(chamf1)/l) + ) each [p1,p2] + else if (is_finite(round1) && !approx(round1,0)) + each arc(r=abs(round1), corner=[[(round1>0?0:1e6),-l/2],[r1,-l/2],[r2,l/2]]) + else [r1,-l/2], + if (is_finite(chamf2) && !approx(chamf2,0)) let( - cn = circle_2tangents(abs(fil2), [r2-fil2,l/2], [r2,l/2], [r1,-l/2]), - ang = fil2<0? phi : phi-180, - steps = ceil(abs(ang)/360*segs(abs(fil2))), - step = ang/steps, - pts = [for (i=[0:1:steps]) let(a=90+i*step) cn[0]+abs(fil2)*[cos(a),sin(a)]] - ) pts - ) : [[r2,l/2]], + p1 = lerp([r2,l/2],[r1,-l/2],abs(chamf2)/l), + p2 = [r2-chamf2/tan(chang2),l/2] + ) each [p1,p2] + else if (is_finite(round2) && !approx(round2,0)) + each arc(r=abs(round2), corner=[[r1,-l/2],[r2,l/2],[(round2>0?0:1e6),l/2]]) + else [r2,l/2], + [0,l/2] + ]; - !is_undef(cham1)? ( - let( - p1 = lerp([r1,-l/2],[r2,l/2],abs(cham1)/l), - p2 = [r1-cham1/tan(chang1),-l/2] - ) [p1,p2] - ) : !is_undef(fil1)? ( - let( - cn = circle_2tangents(abs(fil1), [r1-fil1,-l/2], [r1,-l/2], [r2,l/2]), - ang = fil1<0? 180-phi : -phi, - steps = ceil(abs(ang)/360*segs(abs(fil1))), - step = ang/steps, - pts = [for (i=[0:1:steps]) let(a=(fil1<0?180:0)+(phi-90)+i*step) cn[0]+abs(fil1)*[cos(a),sin(a)]] - ) pts - ) : [[r1,-l/2]], - - [[0,-l/2]] - ); - rotate_extrude(convexity=2) { - polygon(path); - } + rotate_extrude(convexity=2) polygon(path); } } children(); @@ -1378,6 +1543,7 @@ module cyl( // xcyl(l=35, d=20); // xcyl(l=35, d1=30, d2=10); // } + module xcyl( h, r, d, r1, r2, d1, d2, l, chamfer, chamfer1, chamfer2, @@ -1448,6 +1614,7 @@ module xcyl( // ycyl(l=35, d=20); // ycyl(l=35, d1=30, d2=10); // } + module ycyl( h, r, d, r1, r2, d1, d2, l, chamfer, chamfer1, chamfer2, @@ -1519,6 +1686,7 @@ module ycyl( // zcyl(l=35, d=20); // zcyl(l=35, d1=30, d2=10); // } + module zcyl( h, r, d, r1, r2, d1, d2, l, chamfer, chamfer1, chamfer2, @@ -1596,6 +1764,7 @@ module zcyl( // tube(h=30, or1=40, or2=30, ir1=20, ir2=30); // Example: Standard Connectors // tube(h=30, or=40, wall=5) show_anchors(); + module tube( h, or, ir, center, od, id, wall, @@ -1673,6 +1842,7 @@ module tube( // Example: Generating a VNF // vnf = pie_slice(ang=150, l=20, r1=30, r2=50); // vnf_polyhedron(vnf); + module pie_slice( h, r, ang=30, center, r1, r2, d, d1, d2, l, @@ -1696,7 +1866,6 @@ module pie_slice( } } - function pie_slice( h, r, ang=30, center, r1, r2, d, d1, d2, l, @@ -1774,6 +1943,7 @@ function pie_slice( // Example: Called as Function // vnf = sphere(d=100, style="icosa"); // vnf_polyhedron(vnf); + module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) { r = get_radius(r=r, d=d, dflt=1); if (!circum && style=="orig" && is_num(r)) { @@ -1789,7 +1959,6 @@ module sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP } } - function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient=UP) = spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient); @@ -1884,6 +2053,7 @@ function sphere(r, d, circum=false, style="orig", anchor=CENTER, spin=0, orient= // Example: The dual of "icosa" features hexagons and always 12 pentagons: // color("green")spheroid(r=10.01, $fn=256); // spheroid(r=10, style="icosa", circum=true, $fn=16); + module spheroid(r, style="aligned", d, circum=false, dual=false, anchor=CENTER, spin=0, orient=UP) { r = get_radius(r=r, d=d, dflt=1); @@ -2194,6 +2364,7 @@ function spheroid(r, style="aligned", d, circum=false, anchor=CENTER, spin=0, or // vnf_polyhedron(torus(d_min=15, od=60), convexity=4); // Example: Standard Connectors // torus(od=60, id=30) show_anchors(); + module torus( r_maj, r_min, center, d_maj, d_min, @@ -2314,6 +2485,7 @@ function torus( // Example(Spin,VPD=150,Med): Named Conical Connectors // teardrop(d1=20, d2=30, h=20, cap_h1=11, cap_h2=16) // show_anchors(std=false); + module teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anchor=CENTER, spin=0, orient=UP) { r1 = get_radius(r=r, r1=r1, d=d, d1=d1, dflt=1); @@ -2432,6 +2604,7 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, l, anc // } // Example: Standard Connectors // onion(d=30, ang=30, cap_h=20) show_anchors(); + module onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) { r = get_radius(r=r, d=d, dflt=1); @@ -2527,6 +2700,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) = // text3d("Foobar", h=2, anchor=CENTER); // text3d("Foobar", h=2, anchor=str("baseline",CENTER)); // text3d("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); + module text3d(text, h=1, size=10, font="Helvetica", halign, valign, spacing=1.0, direction="ltr", language="em", script="latin", anchor="baseline[-1,0,-1]", spin=0, orient=UP) { no_children($children); dummy1 = @@ -2721,6 +2895,7 @@ function _cut_interp(pathcut, path, data) = // color("red")stroke(path, width=.3); // kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1,1]; // path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP); + module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0) { no_children($children); @@ -2849,6 +3024,7 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers // position(BOT+FRONT) // interior_fillet(l=50, r=10, spin=180, orient=RIGHT); // } + module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0, orient=UP) { r = get_radius(r=r, d=d, dflt=1); steps = ceil(segs(r)*(180-ang)/360); @@ -2923,6 +3099,7 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0, // size=[100,100], bottom=-20, data=fn, // xrange=[-180:2:180], yrange=[-180:2:180] // ); + module heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04:1], yrange=[-1:0.04:1], style="default", convexity=10, anchor=CENTER, spin=0, orient=UP) { size = is_num(size)? [size,size] : point2d(size); @@ -3057,6 +3234,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04 // l=100, r=30, base=5, data=fn, // xrange=[-180:2:180], yrange=[-180:2:180] // ); + function cylindrical_heightfield( data, l, r, base=1, transpose=false, aspect=1, @@ -3176,6 +3354,7 @@ module cylindrical_heightfield( // Example(2D,Big): Metric vs Imperial // ruler(12,width=50,inch=true,labels=true,maxscale=0); // fwd(50)ruler(300,width=50,labels=true); + module ruler(length=100, width, thickness=1, depth=3, labels=false, pipscale=1/3, maxscale, colors=["black","white"], alpha=1.0, unit=1, inch=false, anchor=LEFT+BACK+TOP, spin=0, orient=UP) { diff --git a/skin.scad b/skin.scad index 43d57b1..1325121 100644 --- a/skin.scad +++ b/skin.scad @@ -156,9 +156,12 @@ // anchor = Translate so anchor point is at the origin. Default: "origin" // spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin -// atype = Select "hull" or "intersect anchor types. Default: "hull" +// atype = Select "hull" or "intersect" anchor types. Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" // style = vnf_vertex_array style. Default: "min_edge" +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. // Example: // skin([octagon(4), circle($fn=70,r=2)], z=[0,3], slices=10); // Example: Rotating the pentagon place the zero index at different locations, giving a twist @@ -524,7 +527,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named 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]` // tex_counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height. -// tex_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` +// tex_inset = If numeric, lowers the texture into the surface by that amount, before the tex_scale multiplier is applied. If `true`, insets by exactly `1`. Default: `false` // tex_rot = If true, rotates the texture 90º. // tex_scale = Scaling multiplier for the texture depth. // tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 @@ -535,6 +538,9 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"` // 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` +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. // Extra Anchors: // "origin" = Centers the extruded shape vertically only, but keeps the original path positions in the X and Y. Oriented UP. // "original_base" = Keeps the original path positions in the X and Y, but at the bottom of the extrusion. Oriented UP. @@ -576,6 +582,51 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close // orgn = difference(mrgn,rgn3); // linear_sweep(orgn,height=20,convexity=16) // show_anchors(); +// Example: "diamonds" texture. +// path = glued_circles(r=15, spread=40, tangent=45); +// linear_sweep( +// path, texture="diamonds", tex_size=[5,10], +// h=40, style="concave"); +// Example: "pyramids" texture. +// linear_sweep( +// rect(50), texture="pyramids", tex_size=[10,10], +// h=40, style="convex"); +// Example: "vnf_bricks" texture. +// path = glued_circles(r=15, spread=40, tangent=45); +// linear_sweep( +// path, texture="vnf_bricks", tex_size=[10,10], +// tex_scale=0.25, h=40); +// Example: User defined heightfield texture. +// path = ellipse(r=[20,10]); +// texture = [for (i=[0:9]) +// [for (j=[0:9]) +// 1/max(0.5,norm([i,j]-[5,5])) ]]; +// linear_sweep( +// path, texture=texture, tex_size=[5,5], +// h=40, style="min_edge", anchor=BOT); +// Example: User defined VNF tile texture. +// path = ellipse(r=[20,10]); +// tex = let(n=16,m=0.25) [ +// [ +// each resample_path(path3d(square(1)),n), +// each move([0.5,0.5], +// p=path3d(circle(d=0.5,$fn=n),m)), +// [1/2,1/2,0], +// ], [ +// for (i=[0:1:n-1]) each [ +// [i,(i+1)%n,(i+3)%n+n], +// [i,(i+3)%n+n,(i+2)%n+n], +// [2*n,n+i,n+(i+1)%n], +// ] +// ] +// ]; +// linear_sweep(path, texture=tex, tex_size=[5,5], h=40); +// Example: As Function +// path = glued_circles(r=15, spread=40, tangent=45); +// vnf = linear_sweep( +// path, h=40, texture="trunc_pyramids", tex_size=[5,5], +// tex_scale=1, style="convex"); +// vnf_polyhedron(vnf, convexity=10); module linear_sweep( region, height, center, @@ -613,7 +664,7 @@ module linear_sweep( cp = default(cp, "centroid"); geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, shift=shift, scale=scale, twist=twist, anchors=anchors) : atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, shift=shift, scale=scale, twist=twist, anchors=anchors) : - assert(in_list(atype, ["hull", "intersect"])); + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); attachable(anchor,spin,orient, geom=geom) { vnf_polyhedron(vnf, convexity=convexity); children(); @@ -638,11 +689,11 @@ function linear_sweep( let( h = first_defined([h, height, 1]) ) - !is_undef(texture)? textured_linear_sweep( + !is_undef(texture)? _textured_linear_sweep( region, h=h, texture=texture, tex_size=tex_size, counts=tex_counts, inset=tex_inset, - rot=tex_rot, tscale=tex_scale, + rot=tex_rot, tex_scale=tex_scale, twist=twist, scale=scale, shift=shift, style=style, samples=tex_samples, anchor=anchor, spin=spin, orient=orient @@ -695,7 +746,7 @@ function linear_sweep( cp = default(cp, "centroid"), geom = atype=="hull"? attach_geom(cp=cp, region=region, h=h, extent=true, shift=shift, scale=scale, twist=twist, anchors=anchors) : atype=="intersect"? attach_geom(cp=cp, region=region, h=h, extent=false, shift=shift, scale=scale, twist=twist, anchors=anchors) : - assert(in_list(atype, ["hull", "intersect"])) + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") ) reorient(anchor,spin,orient, geom=geom, p=vnf); @@ -713,13 +764,24 @@ function linear_sweep( // shape = The polygon or [region](regions.scad) to sweep around the Z axis. // angle = If given, specifies the number of degrees to sweep the shape around the Z axis, counterclockwise from the X+ axis. Default: 360 (full rotation) // --- +// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named 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]` +// tex_counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height. +// tex_inset = If numeric, lowers the texture into the surface by that amount, before the tex_scale multiplier is applied. If `true`, insets by exactly `1`. Default: `false` +// tex_rot = If true, rotates the texture 90º. +// tex_scale = Scaling multiplier for the texture depth. +// tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 // style = {{vnf_vertex_array()}} style. Default: "min_edge" +// closed = If false, and shape is given as a path, then the revolved path will be sealed to the axis of rotation with untextured caps. Default: `true` // convexity = (Module only) Convexity setting for use with polyhedron. Default: 10 // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" // atype = Select "hull" or "intersect" anchor types. Default: "hull" // anchor = Translate so anchor point is at the origin. Default: "origin" // spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin (module only) +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. // See Also: linear_sweep(), sweep() // Example: // rgn = [ @@ -734,8 +796,70 @@ function linear_sweep( // Example: // rgn = right(30, p=union([for (a = [0, 90]) rot(a, p=rect([15,5]))])); // rotate_sweep(rgn); +// Example: +// path = right(50, p=circle(d=40)); +// rotate_sweep(path, texture="vnf_bricks", tex_size=[10,10], tex_scale=0.5, style="concave"); +// Example: +// tex = [ +// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], +// [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], +// [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], +// [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], +// [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1], +// [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1], +// [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1], +// [0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1], +// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], +// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], +// [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], +// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], +// ]; +// path = arc(cp=[0,0], r=40, start=60, angle=-120); +// rotate_sweep( +// path, closed=false, +// texture=tex, tex_size=[20,20], +// tex_scale=1, style="concave"); +// Example: +// include +// 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); +// rotate_sweep( +// path, closed=false, +// texture="diamonds", tex_size=[10,10], +// tex_scale=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 = rotate_sweep( +// path, closed=false, +// texture="trunc_pyramids", +// tex_size=[5,5], tex_scale=1, +// style="convex"); +// vnf_polyhedron(vnf, convexity=10); +// Example: +// rgn = [ +// right(40, p=circle(d=50)), +// right(40, p=circle(d=40,$fn=6)), +// ]; +// rotate_sweep( +// rgn, texture="diamonds", +// tex_size=[10,10], tex_scale=1, +// angle=240, style="concave"); + function rotate_sweep( shape, angle=360, + texture, tex_size=[5,5], tex_counts, + tex_inset=false, tex_rot=false, + tex_scale=1, tex_samples, + tex_taper, shift=[0,0], closed=true, style="min_edge", cp="centroid", atype="hull", anchor="origin", spin=0, orient=UP @@ -745,14 +869,33 @@ function rotate_sweep( let( bounds = pointlist_bounds(flatten(region)), min_x = bounds[0].x, - max_x = bounds[1].x + max_x = bounds[1].x, + min_y = bounds[0].y, + max_y = bounds[1].y, + h = max_y - min_y ) assert(min_x>=0, "Input region must exist entirely in the X+ half-plane.") + !is_undef(texture)? _textured_revolution( + region, + texture=texture, + tex_size=tex_size, + counts=tex_counts, + tex_scale=tex_scale, + inset=tex_inset, + rot=tex_rot, + samples=tex_samples, + taper=tex_taper, + shift=shift, + closed=closed, + angle=angle, + style=style + ) : let( steps = segs(max_x), + skmat = down(min_y) * skew(sxz=shift.x/h, syz=shift.y/h) * up(min_y), transforms = [ - if (angle==360) for (i=[0:1:steps-1]) rot([90,0,360-i*360/steps]), - if (angle<360) for (i=[0:1:steps-1]) rot([90,0,angle-i*angle/(steps-1)]), + if (angle==360) for (i=[0:1:steps-1]) skmat * rot([90,0,360-i*360/steps]), + if (angle<360) for (i=[0:1:steps-1]) skmat * rot([90,0,angle-i*angle/(steps-1)]), ], vnf = sweep( region, transforms, @@ -767,7 +910,12 @@ function rotate_sweep( module rotate_sweep( shape, angle=360, + texture, tex_size=[5,5], tex_counts, + tex_inset=false, tex_rot=false, + tex_scale=1, tex_samples, + tex_taper, shift=[0,0], style="min_edge", + closed=true, cp="centroid", convexity=10, atype="hull", @@ -780,21 +928,45 @@ module rotate_sweep( bounds = pointlist_bounds(flatten(region)); min_x = bounds[0].x; max_x = bounds[1].x; + min_y = bounds[0].y; + max_y = bounds[1].y; + h = max_y - min_y; check2 = assert(min_x>=0, "Input region must exist entirely in the X+ half-plane."); steps = segs(max_x); - transforms = [ - if (angle==360) for (i=[0:1:steps-1]) rot([90,0,360-i*360/steps]), - if (angle<360) for (i=[0:1:steps-1]) rot([90,0,angle-i*angle/(steps-1)]), - ]; - sweep( - region, transforms, - closed=angle==360, - caps=angle!=360, - style=style, cp=cp, - convexity=convexity, - atype=atype, anchor=anchor, - spin=spin, orient=orient - ) children(); + if (!is_undef(texture)) { + _textured_revolution( + region, + texture=texture, + tex_size=tex_size, + counts=tex_counts, + tex_scale=tex_scale, + inset=tex_inset, + rot=tex_rot, + samples=tex_samples, + taper=tex_taper, + shift=shift, + closed=closed, + angle=angle, + style=style, + atype=atype, anchor=anchor, + spin=spin, orient=orient + ) children(); + } else { + skmat = down(min_y) * skew(sxz=shift.x/h, syz=shift.y/h) * up(min_y); + transforms = [ + if (angle==360) for (i=[0:1:steps-1]) skmat * rot([90,0,360-i*360/steps]), + if (angle<360) for (i=[0:1:steps-1]) skmat * rot([90,0,angle-i*angle/(steps-1)]), + ]; + sweep( + region, transforms, + closed=angle==360, + caps=angle!=360, + style=style, cp=cp, + convexity=convexity, + atype=atype, anchor=anchor, + spin=spin, orient=orient + ) children(); + } } @@ -1059,6 +1231,9 @@ module spiral_sweep(poly, h, r, turns=1, higbee, center, r1, r2, d, d1, d2, higb // orient = Vector to rotate top towards after spin // atype = Select "hull" or "intersect" anchor types. Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. // See Also: sweep(), linear_sweep(), rotate_sweep(), spiral_sweep() // Example(NoScales): A simple sweep of a square along a sine wave: // path = [for(theta=[-180:5:180]) [theta/10, 10*sin(theta)]]; @@ -1488,6 +1663,9 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0, // orient = Vector to rotate top towards after spin // atype = Select "hull" or "intersect" anchor types. Default: "hull" // cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid" +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. // Example: Sine wave example with self-intersections at each peak. This would fail with path_sweep(). // sinewave = [for(i=[-30:10:360*2+30]) [i/40,3*sin(i)]]; // path_sweep2d(circle(r=3,$fn=15), sinewave); @@ -1612,6 +1790,9 @@ function _ofs_face_edge(face,firstlen,second=false) = // anchor = Translate so anchor point is at the origin. Default: "origin" // spin = Rotate this many degrees around Z axis after anchor. Default: 0 // orient = Vector to rotate top towards after spin (module only) +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. // Example(VPR=[45,0,74],VPD=175,VPT=[-3.8,12.4,19]): A bent object that also changes shape along its length. // radius = 75; // angle = 40; @@ -1674,8 +1855,8 @@ module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity= anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") { vnf = sweep(shape, transforms, closed, caps, style); - vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp) - children(); + vnf_polyhedron(vnf, convexity=convexity, anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp) + children(); } @@ -2277,125 +2458,126 @@ function associate_vertices(polygons, split, curpoly=0) = // inset = The amount to inset part of a VNF tile texture. Generally between 0 and 0.5. // gap = The gap between logically distinct parts of a VNF tile. (ie: gap between bricks, gap between truncated ribs, etc.) // roughness = The amount of roughness used on the surface of some heightfield textures. Generally between 0 and 0.5. -// See Also: textured_revolution(), textured_cylinder(), textured_linear_sweep(), heightfield(), cylindrical_heightfield(), texture() +// See Also: heightfield(), cylindrical_heightfield(), texture() // Example(3D): "ribs" texture. // tex = texture("ribs"); -// textured_linear_sweep( -// rect(50), tex, h=40, tscale=3, +// linear_sweep( +// rect(50), texture=tex, h=40, tex_scale=3, // tex_size=[10,10], style="concave" // ); // Example(3D): Truncated "trunc_ribs" texture. // tex = texture("trunc_ribs"); -// textured_linear_sweep( -// rect(50), tex, h=40, tscale=3, -// tex_size=[10,10], style="concave" +// linear_sweep( +// rect(50), h=40, texture=tex, +// tex_scale=3, tex_size=[10,10], +// style="concave" // ); // Example(3D): "vnf_trunc_ribs" texture. Slower, but more controllable. // tex = texture("vnf_trunc_ribs", gap=0.25, inset=0.333); -// textured_linear_sweep( -// rect(50), tex, h=40, tscale=3, -// tex_size=[10,10] +// linear_sweep( +// rect(50), h=40, texture=tex, +// tex_scale=3, tex_size=[10,10] // ); // Example(3D): "wave_ribs" texture. // tex = texture("wave_ribs"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), h=40, texture=tex, // tex_size=[10,10], style="concave" // ); // Example(3D): "diamonds" texture. // tex = texture("diamonds"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10], style="concave" // ); // Example(3D): "vnf_diamonds" texture. Slower, but more consistent around complex curves. // tex = texture("vnf_diamonds"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "pyramids" texture. // tex = texture("pyramids"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10], style="convex" // ); // Example(3D): "vnf_pyramids" texture. Slower, but more consistent around complex curves. // tex = texture("vnf_pyramids"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "trunc_pyramids" texture. // tex = texture("trunc_pyramids"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10], style="convex" // ); // Example(3D): "vnf_trunc_pyramids" texture. Slower, but more consistent around complex curves. // tex = texture("vnf_trunc_pyramids"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "hills" texture. // tex = texture("hills"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10], style="quincunx" // ); // Example(3D): "vnf_dots" texture. // tex = texture("vnf_dots"); -// textured_linear_sweep( -// rect(50), tex, h=40, tscale=1, +// linear_sweep( +// rect(50), texture=tex, h=40, tex_scale=1, // tex_size=[10,10] // ); // Example(3D): "vnf_dimples" texture. // tex = texture("vnf_dimples"); -// textured_linear_sweep( -// rect(50), tex, h=40, tscale=1, +// linear_sweep( +// rect(50), texture=tex, h=40, tex_scale=1, // tex_size=[10,10] // ); // Example(3D): "vnf_cones" texture. // tex = texture("vnf_cones"); -// textured_linear_sweep( -// rect(50), tex, h=40, tscale=3, +// linear_sweep( +// rect(50), texture=tex, h=40, tex_scale=3, // tex_size=[10,10] // ); // Example(3D): "bricks" texture. // tex = texture("bricks"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "vnf_bricks" texture. // tex = texture("vnf_bricks"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "vnf_diagonal_grid" texture. // tex = texture("vnf_diagonal_grid"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "vnf_hex_grid" texture. // tex = texture("vnf_hex_grid"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[12.5,20] // ); // Example(3D): "vnf_checkers" texture. // tex = texture("vnf_checkers"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10] // ); // Example(3D): "rough" texture. // tex = texture("rough"); -// textured_linear_sweep( -// rect(50), tex, h=40, +// linear_sweep( +// rect(50), texture=tex, h=40, // tex_size=[10,10], style="min_edge" // ); @@ -2705,97 +2887,52 @@ function texture(tex, n, inset, gap, roughness) = assert(false, str("Unrecognized texture name: ", tex)); -// Function&Module: textured_linear_sweep() -// Usage: As Function -// vnf = textured_linear_sweep(region, texture, tex_size, h, ...); -// vnf = textured_linear_sweep(region, texture, counts=, h=, ...); -// Usage: As Module -// textured_linear_sweep(region, texture, tex_size, h, ...) [ATTACHMENTS]; -// textured_linear_sweep(region, texture, counts=, h=, ...) [ATTACHMENTS]; -// Topics: Sweep, Extrusion, Textures, Knurling -// Description: -// Given a [[Region|regions.scad]], creates a linear extrusion of it vertically, optionally twisted, scaled, and/or shifted, -// with a given texture tiled evenly over the side surfaces. The texture can be given in one of three ways: -// - As a texture name string. (See {{texture()}} for supported named textures.) -// - As a 2D array of evenly spread height values. (AKA a heightfield.) -// - As a VNF texture tile. A VNF tile exactly defines a surface from `[0,0]` to `[1,1]`, with the Z coordinates -// being the height of the texture point from the surface. VNF tiles MUST be able to tile in both X and Y -// directions with no gaps, with the front and back edges aligned exactly, and the left and right edges as well. -// One script to convert a grayscale image to a texture heightfield array in a .scad file can be found at: -// https://raw.githubusercontent.com/revarbat/BOSL2/master/scripts/img2scad.py -// Arguments: -// region = The [[Region|regions.scad]] to sweep/extrude. -// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named 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]` -// 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] -// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Used only with heightfield type textures. Default: `"min_edge"` -// samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 -// 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` -// Extra Anchors: -// centroid_top = The centroid of the top of the shape, oriented UP. -// centroid = The centroid of the center of the shape, oriented UP. -// centroid_bot = The centroid of the bottom of the shape, oriented DOWN. -// See Also: textured_revolution(), textured_cylinder(), textured_linear_sweep(), heightfield(), cylindrical_heightfield(), texture() -// 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. -// textured_linear_sweep( -// rect(50), "pyramids", tex_size=[10,10], -// h=40, style="convex"); -// Example: "vnf_bricks" texture. -// path = glued_circles(r=15, spread=40, tangent=45); -// textured_linear_sweep( -// path, "vnf_bricks", tex_size=[10,10], -// tscale=0.25, h=40); -// Example: User defined heightfield texture. -// path = ellipse(r=[20,10]); -// texture = [for (i=[0:9]) -// [for (j=[0:9]) -// 1/max(0.5,norm([i,j]-[5,5])) ]]; -// textured_linear_sweep( -// path, texture, tex_size=[5,5], -// h=40, style="min_edge", anchor=BOT); -// Example: User defined VNF tile texture. -// path = ellipse(r=[20,10]); -// tex = let(n=16,m=0.25) [ -// [ -// each resample_path(path3d(square(1)),n), -// each move([0.5,0.5], -// p=path3d(circle(d=0.5,$fn=n),m)), -// [1/2,1/2,0], -// ], [ -// for (i=[0:1:n-1]) each [ -// [i,(i+1)%n,(i+3)%n+n], -// [i,(i+3)%n+n,(i+2)%n+n], -// [2*n,n+i,n+(i+1)%n], -// ] -// ] -// ]; -// textured_linear_sweep(path, tex, tex_size=[5,5], h=40); -// 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&Module: _textured_linear_sweep() +/// Usage: As Function +/// vnf = _textured_linear_sweep(region, texture, tex_size, h, ...); +/// vnf = _textured_linear_sweep(region, texture, counts=, h=, ...); +/// Usage: As Module +/// _textured_linear_sweep(region, texture, tex_size, h, ...) [ATTACHMENTS]; +/// _textured_linear_sweep(region, texture, counts=, h=, ...) [ATTACHMENTS]; +/// Topics: Sweep, Extrusion, Textures, Knurling +/// Description: +/// Given a [[Region|regions.scad]], creates a linear extrusion of it vertically, optionally twisted, scaled, and/or shifted, +/// with a given texture tiled evenly over the side surfaces. The texture can be given in one of three ways: +/// - As a texture name string. (See {{texture()}} for supported named textures.) +/// - As a 2D array of evenly spread height values. (AKA a heightfield.) +/// - As a VNF texture tile. A VNF tile exactly defines a surface from `[0,0]` to `[1,1]`, with the Z coordinates +/// being the height of the texture point from the surface. VNF tiles MUST be able to tile in both X and Y +/// directions with no gaps, with the front and back edges aligned exactly, and the left and right edges as well. +/// One script to convert a grayscale image to a texture heightfield array in a .scad file can be found at: +/// https://raw.githubusercontent.com/revarbat/BOSL2/master/scripts/img2scad.py +/// Arguments: +/// region = The [[Region|regions.scad]] to sweep/extrude. +/// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named 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]` +/// 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 tex_scale multiplier is applied. If `true`, insets by exactly `1`. Default: `false` +/// rot = If true, rotates the texture 90º. +/// tex_scale = 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] +/// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Used only with heightfield type textures. Default: `"min_edge"` +/// samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 +/// 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` +/// Extra Anchors: +/// centroid_top = The centroid of the top of the shape, oriented UP. +/// centroid = The centroid of the center of the shape, oriented UP. +/// centroid_bot = The centroid of the bottom of the shape, oriented DOWN. +/// See Also: heightfield(), cylindrical_heightfield(), texture() -function textured_linear_sweep( +function _textured_linear_sweep( region, texture, tex_size=[5,5], h, counts, inset=false, rot=false, - tscale=1, twist, scale, shift, + tex_scale=1, twist, scale, shift, style="min_edge", l, height, length, samples, anchor=CENTER, spin=0, orient=UP @@ -2845,7 +2982,18 @@ function textured_linear_sweep( let( s = 1 / max(1, samples), vnf = samples<=1? texture : - vnf_slice(texture, "X", list([s:s:1-s/2])) + let( + vnft = vnf_slice(texture, "X", list([s:s:1-s/2])), + zvnf = [ + [for (p=vnft[0]) + [ + approx(p.x,0)? 0 : approx(p.x,1)? 1 : p.x, + approx(p.y,0)? 0 : approx(p.y,1)? 1 : p.y, + p.z + ] + ], vnft[1] + ] + ) zvnf ) _vnf_sort_vertices(vnf, idx=[1,0]), vertzs = !is_vnf(sorted_tile)? undef : group_sort(sorted_tile[0], idx=1), @@ -2857,8 +3005,7 @@ function textured_linear_sweep( ) [for (i = [0:1:rlen]) [i/rlen, row[i%rlen]]], tmat = scale(scale) * zrot(twist) * up(h/2), pre_skew_vnf = vnf_join([ - /*for (rgn = regions)*/ let( - rgn = last(regions), + for (rgn = regions) let( walls_vnf = vnf_join([ for (path = rgn) let( path = reverse(path), @@ -2881,7 +3028,7 @@ function textured_linear_sweep( for (vert = group) let( u = floor((j + vert.x) * samples), uu = ((j + vert.x) * samples) - u, - texh = (vert.z - inset) * tscale, + texh = (vert.z - inset) * tex_scale, base = lerp(bases[u], select(bases,u+1), uu), norm = unit(lerp(norms[u], select(norms,u+1), uu)), xy = base + norm * texh @@ -2921,7 +3068,7 @@ function textured_linear_sweep( part = (j + (tj/texcnt.x)) * samples, u = floor(part), uu = part - u, - texh = (texture[ti][tj] - inset) * tscale, + texh = (texture[ti][tj] - inset) * tex_scale, base = lerp(bases[u], select(bases,u+1), uu), norm = unit(lerp(norms[u], select(norms,u+1), uu)), xy = base + norm * texh @@ -2962,7 +3109,7 @@ function textured_linear_sweep( part = (j + vert.x) * samples, u = floor(part), uu = part - u, - texh = (vert.y - inset) * tscale, + texh = (vert.y - inset) * tex_scale, base = lerp(bases[u], select(bases,u+1), uu), norm = unit(lerp(norms[u], select(norms,u+1), uu)), xy = base + norm * texh @@ -2985,9 +3132,9 @@ function textured_linear_sweep( ) reorient(anchor,spin,orient, vnf=final_vnf, extent=true, anchors=anchors, p=final_vnf); -module textured_linear_sweep( +module _textured_linear_sweep( path, texture, tex_size=[5,5], h, - inset=false, rot=false, tscale=1, + inset=false, rot=false, tex_scale=1, twist, scale, shift, samples, style="min_edge", l, height, length, counts, @@ -2995,10 +3142,10 @@ module textured_linear_sweep( convexity=10 ) { h = first_defined([h, l, height, length, 1]); - vnf = textured_linear_sweep( + vnf = _textured_linear_sweep( path, texture, h=h, tex_size=tex_size, counts=counts, - inset=inset, rot=rot, tscale=tscale, + inset=inset, rot=rot, tex_scale=tex_scale, twist=twist, scale=scale, shift=shift, samples=samples, style=style, anchor=CENTER, spin=0, orient=UP @@ -3035,94 +3182,55 @@ function _find_vnf_tile_edge_path(vnf, val) = ) opath; -// Function&Module: textured_revolution() -// Usage: As Function -// vnf = textured_revolution(shape, texture, tex_size, [tscale=], ...); -// vnf = textured_revolution(shape, texture, counts=, [tscale=], ...); -// Usage: As Module -// textured_revolution(shape, texture, tex_size, [tscale=], ...) [ATTACHMENTS]; -// textured_revolution(shape, texture, counts=, [tscale=], ...) [ATTACHMENTS]; -// Topics: Sweep, Extrusion, Textures, Knurling -// Description: -// Given a 2D region or path, fully in the X+ half-plane, revolves that shape around the Z axis (after rotating its Y+ to Z+). -// This creates a solid from that surface of revolution, possibly capped top and bottom, with the sides covered in a given tiled texture. -// The texture can be given in one of three ways: -// - As a texture name string. (See {{texture()}} for supported named textures.) -// - As a 2D array of evenly spread height values. (AKA a heightfield.) -// - As a VNF texture tile. A VNF tile exactly defines a surface from `[0,0]` to `[1,1]`, with the Z coordinates -// being the height of the texture point from the surface. VNF tiles MUST be able to tile in both X and Y -// directions with no gaps, with the front and back edges aligned exactly, and the left and right edges as well. -// One script to convert a grayscale image to a texture heightfield array in a .scad file can be found at: -// https://raw.githubusercontent.com/revarbat/BOSL2/master/scripts/img2scad.py -// Arguments: -// shape = The path or region to sweep/extrude. -// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to the revolution surface. See {{texture()}} for what named 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]` -// 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º. -// shift = [X,Y] amount to translate the top, relative to the bottom. Default: [0,0] -// closed = If false, and shape is given as a path, then the revolved path will be sealed to the axis of rotation with untextured caps. Default: `true` -// taper = If given, and `closed=false`, tapers the texture height to zero over the first and last given percentage of the path. Default: `undef` (no taper) -// angle = The number of degrees counter-clockwise from X+ to revolve around the Z axis. Default: `360` -// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Used only with heightfield type textures. Default: `"min_edge"` -// counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height. -// samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 -// 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(), textured_linear_sweep(), heightfield(), cylindrical_heightfield(), texture() -// Example: -// path = right(50, p=circle(d=40)); -// textured_revolution(path, "vnf_bricks", tex_size=[10,10], tscale=0.5, style="concave"); -// Example: -// tex = [ -// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], -// [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], -// [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], -// [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1], -// [0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1], -// [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1], -// [0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1], -// [0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1], -// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], -// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], -// [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1], -// [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], -// ]; -// path = arc(cp=[0,0], r=40, start=60, angle=-120); -// textured_revolution(path, closed=false, texture=tex, tex_size=[20,20], tscale=1, style="concave"); -// Example: -// include -// 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, closed=false, texture="diamonds", tex_size=[10,10], 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, closed=false, texture="trunc_pyramids", tex_size=[5,5], tscale=1, style="convex"); -// vnf_polyhedron(vnf, convexity=10); -// Example: -// rgn = [ -// right(40, p=circle(d=50)), -// right(40, p=circle(d=40,$fn=6)), -// ]; -// textured_revolution(rgn, texture="diamonds", tex_size=[10,10], tscale=1, angle=240, style="concave"); +/// Function&Module: _textured_revolution() +/// Usage: As Function +/// vnf = _textured_revolution(shape, texture, tex_size, [tex_scale=], ...); +/// vnf = _textured_revolution(shape, texture, counts=, [tex_scale=], ...); +/// Usage: As Module +/// _textured_revolution(shape, texture, tex_size, [tex_scale=], ...) [ATTACHMENTS]; +/// _textured_revolution(shape, texture, counts=, [tex_scale=], ...) [ATTACHMENTS]; +/// Topics: Sweep, Extrusion, Textures, Knurling +/// Description: +/// Given a 2D region or path, fully in the X+ half-plane, revolves that shape around the Z axis (after rotating its Y+ to Z+). +/// This creates a solid from that surface of revolution, possibly capped top and bottom, with the sides covered in a given tiled texture. +/// The texture can be given in one of three ways: +/// - As a texture name string. (See {{texture()}} for supported named textures.) +/// - As a 2D array of evenly spread height values. (AKA a heightfield.) +/// - As a VNF texture tile. A VNF tile exactly defines a surface from `[0,0]` to `[1,1]`, with the Z coordinates +/// being the height of the texture point from the surface. VNF tiles MUST be able to tile in both X and Y +/// directions with no gaps, with the front and back edges aligned exactly, and the left and right edges as well. +/// One script to convert a grayscale image to a texture heightfield array in a .scad file can be found at: +/// https://raw.githubusercontent.com/revarbat/BOSL2/master/scripts/img2scad.py +/// Arguments: +/// shape = The path or region to sweep/extrude. +/// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to the revolution surface. See {{texture()}} for what named 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]` +/// tex_scale = Scaling multiplier for the texture depth. +/// --- +/// inset = If numeric, lowers the texture into the surface by that amount, before the tex_scale multiplier is applied. If `true`, insets by exactly `1`. Default: `false` +/// rot = If true, rotates the texture 90º. +/// shift = [X,Y] amount to translate the top, relative to the bottom. Default: [0,0] +/// closed = If false, and shape is given as a path, then the revolved path will be sealed to the axis of rotation with untextured caps. Default: `true` +/// taper = If given, and `closed=false`, tapers the texture height to zero over the first and last given percentage of the path. Default: `undef` (no taper) +/// angle = The number of degrees counter-clockwise from X+ to revolve around the Z axis. Default: `360` +/// style = The triangulation style used. See {{vnf_vertex_array()}} for valid styles. Used only with heightfield type textures. Default: `"min_edge"` +/// counts = If given instead of tex_size, gives the tile repetition counts for textures over the surface length and height. +/// samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 +/// 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: heightfield(), cylindrical_heightfield(), texture() +/// Anchor Types: +/// "hull" = Anchors to the virtual convex hull of the shape. +/// "intersect" = Anchors to the surface of the shape. -function textured_revolution( - shape, texture, tex_size, tscale=1, +function _textured_revolution( + shape, texture, tex_size, tex_scale=1, inset=false, rot=false, shift=[0,0], taper, closed=true, angle=360, - style="min_edge", counts, samples + counts, samples, + style="min_edge", atype="intersect", + anchor=CENTER, spin=0, orient=UP ) = assert(angle>0 && angle<=360) assert(is_path(shape,[2]) || is_region(shape)) @@ -3132,6 +3240,7 @@ function textured_revolution( assert(tex_size==undef || is_vector(tex_size,2)) assert(is_bool(rot) || in_list(rot,[0,90,180,270])) assert(is_undef(taper) || (is_finite(taper) && taper>=0 && taper<50)) + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") let( regions = !is_path(shape,2)? region_parts(shape) : shape[0].y <= last(shape).y? [[reverse(shape)]] : @@ -3185,8 +3294,17 @@ function textured_revolution( slices = list([s : s : 1-s/2]), vnfx = vnf_slice(texture, "X", slices), vnfy = vnf_slice(vnfx, "Y", slices), - vnft = vnf_triangulate(vnfy) - ) vnft + vnft = vnf_triangulate(vnfy), + zvnf = [ + [for (p=vnft[0]) + [ + approx(p.x,0)? 0 : approx(p.x,1)? 1 : p.x, + approx(p.y,0)? 0 : approx(p.y,1)? 1 : p.y, + p.z + ] + ], vnft[1] + ] + ) zvnf ) _vnf_sort_vertices(utex, idx=[0,1]), vertzs = is_vnf(texture)? group_sort(tile[0], idx=0) : undef, bpath = is_vnf(tile) @@ -3232,8 +3350,8 @@ function textured_revolution( uu = part - u, base = lerp(select(bases,u), select(bases,u+1), uu), norm = unit(lerp(select(norms,u), select(norms,u+1), uu)), - tscale = tscale * lookup(part/samples/counts_y, taper_lup), - texh = (vert.z - inset) * tscale * (base.x / maxx), + tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), + texh = (vert.z - inset) * tex_scale * (base.x / maxx), xyz = base - norm * texh ) zrot(vert.x*angle/counts_x, p=xyz) ] @@ -3258,8 +3376,8 @@ function textured_revolution( uu = part - u, base = lerp(bases[u], select(bases,u+1), uu), norm = unit(lerp(norms[u], select(norms,u+1), uu)), - tscale = tscale * lookup(part/samples/counts_y, taper_lup), - texh = (texture[ti][tj] - inset) * tscale * (base.x / maxx), + tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), + texh = (texture[ti][tj] - inset) * tex_scale * (base.x / maxx), xyz = base - norm * texh ) xyz ]) @@ -3288,15 +3406,15 @@ function textured_revolution( ppath = is_vnf(texture) ? [ // VNF tile texture for (j = [0:1:counts_y-1]) - for (group = vertzs, vert = group) - if (vert.x == 0) let( - part = (j + vert.y) * samples, + for (group = vertzs, vert = reverse(group)) + if (approx(vert.x, 0)) let( + part = (j + (1 - vert.y)) * samples, u = floor(part), uu = part - u, base = lerp(select(bases,u), select(bases,u+1), uu), norm = unit(lerp(select(norms,u), select(norms,u+1), uu)), - tscale = tscale * lookup(part/samples/counts_y, taper_lup), - texh = (vert.z - inset) * tscale * (base.x / maxx), + tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), + texh = (vert.z - inset) * tex_scale * (base.x / maxx), xyz = base - norm * texh ) xyz ] @@ -3311,8 +3429,8 @@ function textured_revolution( uu = part - u, base = lerp(bases[u], select(bases,u+1), uu), norm = unit(lerp(norms[u], select(norms,u+1), uu)), - tscale = tscale * lookup(part/samples/counts_y, taper_lup), - texh = (texture[ti][0] - inset) * tscale * (base.x / maxx), + tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), + texh = (texture[ti][0] - inset) * tex_scale * (base.x / maxx), xyz = base - norm * texh ) xyz ], @@ -3344,8 +3462,8 @@ function textured_revolution( ppath = [ for (vert = tpath) let( uang = vert.x / counts_x, - tscale = tscale * lookup([0,1][j+1], taper_lup), - texh = (vert.y - inset) * tscale * (base.x / maxx), + tex_scale = tex_scale * lookup([0,1][j+1], taper_lup), + texh = (vert.y - inset) * tex_scale * (base.x / maxx), xyz = base - norm * texh ) zrot(angle*uang, p=xyz) ], @@ -3371,27 +3489,31 @@ function textured_revolution( ) caps_vnf ) vnf_join([walls_vnf, endcap_vnf, allcaps_vnf]) ]), - skmat = down(-miny) * skew(sxz=shift.x/h, syz=shift.y/h) * up(-miny) - ) apply(skmat, full_vnf); + skmat = down(-miny) * skew(sxz=shift.x/h, syz=shift.y/h) * up(-miny), + skvnf = apply(skmat, full_vnf), + geom = atype=="intersect" + ? attach_geom(vnf=skvnf, extent=false) + : attach_geom(vnf=skvnf, extent=true) + ) reorient(anchor,spin,orient, geom=geom, p=skvnf); -module textured_revolution( - shape, texture, tex_size, tscale=1, +module _textured_revolution( + shape, texture, tex_size, tex_scale=1, inset=false, rot=false, shift=[0,0], taper, closed=true, angle=360, - style="min_edge", atype="surface", + style="min_edge", atype="intersect", convexity=10, counts, samples, anchor=CENTER, spin=0, orient=UP ) { - assert(in_list(atype, ["surface","extent"])); - vnf = textured_revolution( + assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); + vnf = _textured_revolution( shape, texture, tex_size=tex_size, - tscale=tscale, inset=inset, rot=rot, + tex_scale=tex_scale, inset=inset, rot=rot, taper=taper, closed=closed, style=style, shift=shift, angle=angle, samples=samples, counts=counts ); - geom = atype=="surface" + geom = atype=="intersect" ? attach_geom(vnf=vnf, extent=false) : attach_geom(vnf=vnf, extent=true); attachable(anchor,spin,orient, geom=geom) { @@ -3401,70 +3523,58 @@ module textured_revolution( } -// 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, Knurling -// Description: -// Creates a cylinder or cone with optional chamfers or roundings, covered in a textured surface. -// The texture can be given in one of three ways: -// - As a texture name string. (See {{texture()}} for supported named textures.) -// - As a 2D array of evenly spread height values. (AKA a heightfield.) -// - As a VNF texture tile. A VNF tile exactly defines a surface from `[0,0]` to `[1,1]`, with the Z coordinates -// being the height of the texture point from the surface. VNF tiles MUST be able to tile in both X and Y -// directions with no gaps, with the front and back edges aligned exactly, and the left and right edges as well. -// One script to convert a grayscale image to a texture heightfield array in a .scad file can be found at: -// https://raw.githubusercontent.com/revarbat/BOSL2/master/scripts/img2scad.py -// 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), or a VNF tile that defines the texture to apply to the cylinder wall surfaces. See {{texture()}} for what named 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]` -// --- -// 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"` -// taper = If given, tapers the texture height to zero over the given percentage of the top and bottom of the cylinder face. Default: `undef` (no taper) -// 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. If given a negative size, creates a chamfer that juts *outward* from the cylinder. -// chamfer1 = If given, chamfers the bottom of the cylinder by the given size. If given a negative size, creates a chamfer that juts *outward* from the cylinder. -// chamfer2 = If given, chamfers the top of the cylinder by the given size. If given a negative size, creates a chamfer that juts *outward* from the cylinder. -// rounding = If given, rounds the top and bottom of the cylinder to the given radius. If given a negative size, creates a roundover that juts *outward* from the cylinder. -// rounding1 = If given, rounds the bottom of the cylinder to the given radius. If given a negative size, creates a roundover that juts *outward* from the cylinder. -// rounding2 = If given, rounds the top of the cylinder to the given radius. If given a negative size, creates a roundover that juts *outward* from the cylinder. -// samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 -// 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(), textured_linear_sweep(), heightfield(), cylindrical_heightfield(), texture() -// 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="convex"); -// 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="vnf_dots", tex_size=[5,5], rounding=9, samples=6); -// textured_cylinder(h=50, r1=25, r2=20, shift=[0,10], texture="bricks", rounding1=-10, tex_size=[10,10], tscale=0.5, style="concave"); -// Example: No Texture Taper -// textured_cylinder(d1=25, d2=20, h=30, rounding=5, texture="trunc_ribs", tex_size=[5,1]); -// Example: Taper Texure at Extreme Ends -// textured_cylinder(d1=25, d2=20, h=30, rounding=5, texture="trunc_ribs", taper=0, tex_size=[5,1]); -// Example: Taper Texture over First and Last 10% -// textured_cylinder(d1=25, d2=20, h=30, rounding=5, texture="trunc_ribs", taper=10, tex_size=[5,1]); +/// Function&Module: _textured_cylinder() +/// Usage: As Function +/// vnf = _textured_cylinder(h|l=, r|d=, texture, tex_size|counts=, [tex_scale=], [inset=], [rot=], ...); +/// vnf = _textured_cylinder(h|l=, r1=|d1=, r2=|d2=, texture=, tex_size=|counts=, [tex_scale=], [inset=], [rot=], ...); +/// Usage: As Module +/// _textured_cylinder(h, r|d=, texture, tex_size|counts=, [tex_scale=], [inset=], [rot=], ...) [ATTACHMENTS]; +/// _textured_cylinder(h, r1=|d1=, r2=|d2=, texture=, tex_size=|counts=, [tex_scale=], [inset=], [rot=], ...) [ATTACHMENTS]; +/// Topics: Sweep, Extrusion, Textures, Knurling +/// Description: +/// Creates a cylinder or cone with optional chamfers or roundings, covered in a textured surface. +/// The texture can be given in one of three ways: +/// - As a texture name string. (See {{texture()}} for supported named textures.) +/// - As a 2D array of evenly spread height values. (AKA a heightfield.) +/// - As a VNF texture tile. A VNF tile exactly defines a surface from `[0,0]` to `[1,1]`, with the Z coordinates +/// being the height of the texture point from the surface. VNF tiles MUST be able to tile in both X and Y +/// directions with no gaps, with the front and back edges aligned exactly, and the left and right edges as well. +/// One script to convert a grayscale image to a texture heightfield array in a .scad file can be found at: +/// https://raw.githubusercontent.com/revarbat/BOSL2/master/scripts/img2scad.py +/// 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), or a VNF tile that defines the texture to apply to the cylinder wall surfaces. See {{texture()}} for what named 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]` +/// --- +/// 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. +/// tex_scale = Scaling multiplier for the texture depth. +/// inset = If numeric, lowers the texture into the surface by that amount, before the tex_scale 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"` +/// taper = If given, tapers the texture height to zero over the given percentage of the top and bottom of the cylinder face. Default: `undef` (no taper) +/// 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. If given a negative size, creates a chamfer that juts *outward* from the cylinder. +/// chamfer1 = If given, chamfers the bottom of the cylinder by the given size. If given a negative size, creates a chamfer that juts *outward* from the cylinder. +/// chamfer2 = If given, chamfers the top of the cylinder by the given size. If given a negative size, creates a chamfer that juts *outward* from the cylinder. +/// rounding = If given, rounds the top and bottom of the cylinder to the given radius. If given a negative size, creates a roundover that juts *outward* from the cylinder. +/// rounding1 = If given, rounds the bottom of the cylinder to the given radius. If given a negative size, creates a roundover that juts *outward* from the cylinder. +/// rounding2 = If given, rounds the top of the cylinder to the given radius. If given a negative size, creates a roundover that juts *outward* from the cylinder. +/// samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 +/// 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: heightfield(), cylindrical_heightfield(), texture() -function textured_cylinder( +function _textured_cylinder( h, r, texture, tex_size=[1,1], counts, - tscale=1, inset=false, rot=false, + tex_scale=1, inset=false, rot=false, caps=true, style="min_edge", taper, shift=[0,0], l, r1, r2, d, d1, d2, chamfer, chamfer1, chamfer2, @@ -3475,13 +3585,18 @@ function textured_cylinder( h = first_defined([h, l, 1]), r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1), r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1), - chamf1 = first_defined([chamfer1, chamfer]), - chamf2 = first_defined([chamfer2, chamfer]), - round1 = first_defined([rounding1, rounding]), - round2 = first_defined([rounding2, rounding]), - needed_h = default(chamf1,0) + default(chamf2,0) - + default(round1,0) + default(round2,0), - check = assert(needed_h<=h), + chamf1 = first_defined([chamfer1, chamfer, 0]), + chamf2 = first_defined([chamfer2, chamfer, 0]), + round1 = first_defined([rounding1, rounding, 0]), + round2 = first_defined([rounding2, rounding, 0]), + needed_h = chamf1 + chamf2 + round1 + round2, + needed_r1 = chamf1 + round1, + needed_r2 = chamf2 + round2, + checks = + assert(needed_h <= h, "Cylinder not tall enough for specified roundings and chamfers.") + assert(needed_r1 <= r1, "Cylinder bottom radius too small for given rounding or chamfer.") + assert(needed_r2 <= r2, "Cylinder top radius too small for given rounding or chamfer.") + , path = [ if (is_finite(chamf1) && !approx(chamf1,0)) each arc(n=2, r=abs(chamf1), corner=[[(chamf1>0?0:1e6),-h/2],[r1,-h/2],[r2,h/2]]) @@ -3494,19 +3609,19 @@ function textured_cylinder( each arc(r=abs(round2), corner=[[r1,-h/2],[r2,h/2],[(round2>0?0:1e6),h/2]]) else [r2,h/2], ], - vnf = textured_revolution( + vnf = _textured_revolution( reverse(path), texture, closed=false, tex_size=tex_size, counts=counts, - tscale=tscale, inset=inset, rot=rot, + tex_scale=tex_scale, inset=inset, rot=rot, style=style, shift=shift, taper=taper, samples=samples ) ) vnf; -module textured_cylinder( +module _textured_cylinder( h, r, texture, tex_size=[1,1], - counts, tscale=1, inset=false, rot=false, + counts, tex_scale=1, inset=false, rot=false, style="min_edge", shift=[0,0], taper, l, r1, r2, d, d1, d2, chamfer, chamfer1, chamfer2, @@ -3521,9 +3636,9 @@ module textured_cylinder( chamf2 = first_defined([chamfer2, chamfer]); round1 = first_defined([rounding1, rounding]); round2 = first_defined([rounding2, rounding]); - vnf = textured_cylinder( + vnf = _textured_cylinder( texture=texture, h=h, r1=r1, r2=r2, - tscale=tscale, inset=inset, rot=rot, + tex_scale=tex_scale, inset=inset, rot=rot, counts=counts, tex_size=tex_size, caps=true, style=style, taper=taper, shift=shift, samples=samples, diff --git a/vnf.scad b/vnf.scad index 74e52cf..95c7c61 100644 --- a/vnf.scad +++ b/vnf.scad @@ -886,6 +886,11 @@ function _slice_3dpolygons(polys, dir, cuts) = // 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` // atype = Select "hull" or "intersect" anchor type. Default: "hull" +// Anchor Types: +// "hull" = Anchors to the virtual convex hull of the shape. +// "intersect" = Anchors to the surface of the shape. +// Extra Anchors: +// "origin" = Anchor at the origin, oriented UP. module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") { vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf; assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); From 3ca4643e71fa5b34832c3fd1659781251c4917cb Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sun, 21 Aug 2022 00:21:58 -0700 Subject: [PATCH 2/3] Textures integration bugfixes. --- bottlecaps.scad | 12 ++++++------ shapes3d.scad | 10 +++++----- skin.scad | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/bottlecaps.scad b/bottlecaps.scad index 65d40a4..7e4bb17 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -181,9 +181,9 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) difference() { union() { if (texture == "knurled") { - textured_cylinder(d=w, h=h, texture="diamonds", tex_size=[3,3], style="concave", anchor=BOT); + cyl(d=w, h=h, texture="diamonds", tex_size=[3,3], tex_style="concave", anchor=BOT); } else if (texture == "ribbed") { - textured_cylinder(d=w, h=h, texture="ribs", tex_size=[3,3], style="min_edge", anchor=BOT); + cyl(d=w, h=h, texture="ribs", tex_size=[3,3], tex_style="min_edge", anchor=BOT); } else { cyl(d=w, l=tamper_ring_h+wall, anchor=BOTTOM); } @@ -362,9 +362,9 @@ module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) difference() { union() { if (texture == "knurled") { - textured_cylinder(d=w, h=11.2+wall, texture="diamonds", tex_size=[3,3], style="concave", anchor=BOT); + cyl(d=w, h=11.2+wall, texture="diamonds", tex_size=[3,3], tex_style="concave", anchor=BOT); } else if (texture == "ribbed") { - textured_cylinder(d=w, h=11.2+wall, texture="ribs", tex_size=[3,3], style="min_edge", anchor=BOT); + cyl(d=w, h=11.2+wall, texture="ribs", tex_size=[3,3], tex_style="min_edge", anchor=BOT); } else { cyl(d=w, l=11.2+wall, anchor=BOTTOM); } @@ -567,9 +567,9 @@ module generic_bottle_cap( // thickness so the wall+texture are the specified wall thickness. That // seems wrong so this does specified thickness+texture if (texture == "knurled") { - textured_cylinder(d=w + 1.5*diamMagMult, l=h, texture="diamonds", tex_size=[3,3], style="concave", anchor=BOT); + cyl(d=w + 1.5*diamMagMult, l=h, texture="diamonds", tex_size=[3,3], tex_style="concave", anchor=BOT); } else if (texture == "ribbed") { - textured_cylinder(d=w + 1.5*diamMagMult, l=h, texture="ribs", tex_size=[3,3], style="min_edge", anchor=BOT); + cyl(d=w + 1.5*diamMagMult, l=h, texture="ribs", tex_size=[3,3], tex_style="min_edge", anchor=BOT); } else { cyl(d = w, l = h, anchor = BOTTOM); } diff --git a/shapes3d.scad b/shapes3d.scad index 1c1b495..122d1e7 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -1266,7 +1266,7 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // Example: Texturing with VNF tile "vnf_dots" // cyl(h=40, r1=20, r2=15, rounding=9, // texture="vnf_dots", tex_size=[5,5], -// samples=6); +// tex_samples=6); // // Example: Texturing with VNF tile "vnf_bricks" // cyl(h=50, r1=25, r2=20, shift=[0,10], rounding1=-10, @@ -1279,12 +1279,12 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // // Example: Taper Texure at Extreme Ends // cyl(d1=25, d2=20, h=30, rounding=5, -// texture="trunc_ribs", taper=0, +// texture="trunc_ribs", tex_taper=0, // tex_size=[5,1]); // // Example: Taper Texture over First and Last 10% // cyl(d1=25, d2=20, h=30, rounding=5, -// texture="trunc_ribs", taper=10, +// texture="trunc_ribs", tex_taper=10, // tex_size=[5,1]); function cyl( @@ -3072,7 +3072,7 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, anchor=CENTER, spin=0, // 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. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// See Also: heightfield(), cylindrical_heightfield(), textured_revolution(), textured_cylinder(), textured_linear_sweep() +// See Also: heightfield(), cylindrical_heightfield() // Example: // heightfield(size=[100,100], bottom=-20, data=[ // for (y=[-180:4:180]) [ @@ -3210,7 +3210,7 @@ function heightfield(data, size=[100,100], bottom=-20, maxz=100, xrange=[-1:0.04 // 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. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` -// See Also: heightfield(), cylindrical_heightfield(), textured_revolution(), textured_cylinder(), textured_linear_sweep() +// See Also: heightfield(), cylindrical_heightfield() // Example(VPD=400;VPR=[55,0,150]): // cylindrical_heightfield(l=100, r=30, base=5, data=[ // for (y=[-180:4:180]) [ diff --git a/skin.scad b/skin.scad index 1325121..b5d345b 100644 --- a/skin.scad +++ b/skin.scad @@ -876,7 +876,7 @@ function rotate_sweep( ) assert(min_x>=0, "Input region must exist entirely in the X+ half-plane.") !is_undef(texture)? _textured_revolution( - region, + shape, texture=texture, tex_size=tex_size, counts=tex_counts, @@ -935,7 +935,7 @@ module rotate_sweep( steps = segs(max_x); if (!is_undef(texture)) { _textured_revolution( - region, + shape, texture=texture, tex_size=tex_size, counts=tex_counts, From c17fe04aeaafc7d7113575ec54057bba95793d2e Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Sun, 21 Aug 2022 00:51:39 -0700 Subject: [PATCH 3/3] Corrected masking diff() bugs in docs. --- masks3d.scad | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/masks3d.scad b/masks3d.scad index b09fe0c..243fcc7 100644 --- a/masks3d.scad +++ b/masks3d.scad @@ -36,7 +36,7 @@ // #chamfer_edge_mask(l=50, chamfer=10, orient=RIGHT); // } // Example: Masking by Attachment -// diff("mask") +// diff() // cube(50, center=true) { // edge_mask(TOP+RIGHT) // #chamfer_edge_mask(l=50, chamfer=10); @@ -71,7 +71,7 @@ module chamfer_edge_mask(l=1, chamfer=1, excess=0.1, anchor=CENTER, spin=0, orie // move(25*[1,-1,1]) #chamfer_corner_mask(chamfer=10); // } // Example: Masking by Attachment -// diff("mask") +// diff() // cuboid(100, chamfer=20, trimcorners=false) { // corner_mask(TOP+FWD+RIGHT) // chamfer_corner_mask(chamfer=20); @@ -166,12 +166,12 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE // #rounding_edge_mask(l=50, r1=25, r2=10, orient=UP, anchor=BOTTOM); // } // Example: Masking by Attachment -// diff("mask") +// diff() // cube(100, center=true) // edge_mask(FRONT+RIGHT) // #rounding_edge_mask(l=$parent_size.z+0.01, r=25); // Example: Multiple Masking by Attachment -// diff("mask") +// diff() // cube([80,90,100], center=true) { // let(p = $parent_size*1.01) { // edge_mask(TOP) @@ -237,7 +237,7 @@ module rounding_edge_mask(l, r, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, sp // #rounding_corner_mask(r=20, spin=90); // } // Example: Masking by Attachment -// diff("mask") +// diff() // cube(size=[50, 60, 70]) { // corner_mask(TOP) // #rounding_corner_mask(r=20); @@ -396,10 +396,11 @@ module rounding_angled_corner_mask(r, ang=90, d, anchor=CENTER, spin=0, orient=U // up(50) rounding_cylinder_mask(r=50, rounding=10); // } // Example: Masking by Attachment -// diff("mask") +// diff() // cyl(h=30, d=30) { // attach(TOP) -// #tag("mask")rounding_cylinder_mask(d=30, rounding=5); +// #tag("remove") +// rounding_cylinder_mask(d=30, rounding=5); // } function rounding_cylinder_mask(r, rounding, d) = no_function("rounding_cylinder_mask"); module rounding_cylinder_mask(r, rounding, d) @@ -475,7 +476,7 @@ module rounding_hole_mask(r, rounding, excess=0.1, d, anchor=CENTER, spin=0, ori // Example(VPD=50,VPR=[55,0,120]): // teardrop_edge_mask(l=20, r=10, angle=40); // Example(VPD=300,VPR=[75,0,25]): -// diff("mask") +// diff() // cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { // edge_mask(BOT) // teardrop_edge_mask(l=max($parent_size)+1, r=10, angle=40); @@ -512,7 +513,7 @@ module teardrop_edge_mask(l, r, angle, excess=0.1, d) // Example: // teardrop_corner_mask(r=20, angle=40); // Example: -// diff("mask") +// diff() // cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { // edge_profile(BOT) // mask2d_teardrop(r=10, angle=40);