From 01040119180423ee441eec5f7c859c7f171c7cf4 Mon Sep 17 00:00:00 2001 From: Garth Minette Date: Mon, 18 Jul 2022 19:49:57 -0700 Subject: [PATCH] texture() cleanup. --- skin.scad | 549 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 330 insertions(+), 219 deletions(-) diff --git a/skin.scad b/skin.scad index 0b6ee7e..833123a 100644 --- a/skin.scad +++ b/skin.scad @@ -2098,39 +2098,48 @@ function associate_vertices(polygons, split, curpoly=0) = // Section: Texturing -// DefineHeader(Table;Headers=Texture Name|Type|Description): Texture Values +// DefineHeader(Table;Headers=Texture Name|Args|Description): Heightfield Textures +// DefineHeader(Table;Headers=Texture Name|Args|Description): VNF Textures // Function: texture() // Usage: -// tx = texture(tex, [n], [m]); +// tx = texture(tex, [n=], [inset=], [gap=], [roughness=]); // Topics: Textures, Knurling // Description: -// Given a texture name, and two optional variables, returns a heightfield texture as a 2D array of scalars. +// Given a texture name, returns a texture. Textures can come in two varieties: +// - Heightfield textures which are 2D arrays of scalars. These are faster to render, but are less precise and prone to triangulation errors. +// - VNF Tile textures, which are VNFs that completely tile the rectangle `[0,0]` to `[1,1]`. These tend to be slower to render, but are more precise. +// Sometimes the geometry of a shape to be textured will cause a heightfield texture to be badly triangulated. +// Switching to a similar VNF tile texture can solve this problem. Usually just by adding the prefix "vnf_". +// Heightfield Textures: +// "bricks" = `n`, `roughness` = A brick-wall pattern. +// "diamonds" = `n` = Diamond shapes with tips aligned with the axes. Useful for knurling. +// "hills" = `n` = Wavy sine-wave hills and valleys, +// "pyramids" = `n` = Pyramids shapes with flat sides aligned with the axes. Also useful for knurling. +// "ribs" = `n` = Vertically aligned triangular ribs. +// "rough" = `n`, `roughness` = A pseudo-randomized rough surace texture. +// "trunc_pyramids" = `n` = Like "pyramids" but with flattened tips. +// "trunc_ribs" = `n` = Like "ribs" but with flat rib tips. +// "wave_ribs" = `n` = Vertically aligned wavy ribs. +// VNF Textures: +// "vnf_bricks" = `inset`, `gap` = Like "bricks", but slower and more consistent in triangulation. +// "vnf_checkers" = `inset` = A pattern of alternating checkerboard squares. +// "vnf_cones" = `n`, `inset` = Raised conical spikes. +// "vnf_cubes" = none = Cornercubes texture. +// "vnf_diagonal_grid" = `inset` = A grid of thin lines at 45º angles. +// "vnf_diamonds" = none = Like "diamonds", but slower and more consistent in triangulation. +// "vnf_dimples" = `n`, `inset` = Small round divots. +// "vnf_dots" = `n`, `inset` = Raised small round bumps. +// "vnf_hex_grid" = `inset` = A hexagonal grid of thin lines. +// "vnf_pyramids" = none = Like "pyramids", but slower and more consistent in triangulation. +// "vnf_trunc_pyramids" = `inset` = Like "trunc_pyramids", but slower and more consistent in triangulation. // Arguments: // tex = The name of the texture to get. -// n = Generally the number of vertices in one axis to make the texture from. Depends on the texture. -// m = Generally the texture height. Depends on the texture. -// Texture Values: -// "bricks" = Heightfield = A brick-wall pattern. -// "diamonds" = Heightfield = Diamond shapes with tips aligned with the axes. Useful for knurling. -// "hills" = Heightfield = Wavy sine-wave hills and valleys, -// "pyramids" = Heightfield = Pyramids shapes with flat sides aligned with the axes. Also useful for knurling. -// "ribs" = Heightfield = Vertically aligned triangular ribs. -// "rough" = Heightfield = A pseudo-randomized rough surace texture. -// "trunc_pyramids" = Heightfield = Like "pyramids" but with flattened tips. -// "trunc_ribs" = Heightfield = Like "ribs" but with flat rib tips. -// "wave_ribs" = Heightfield = Vertically aligned wavy ribs. -// "vnf_bricks" = VNF Tile = Like "bricks", but slower and more consistent in triangulation. -// "vnf_checkers" = VNF Tile = A pattern of alternating checkerboard squares. -// "vnf_cones" = VNF Tile = Raised conical spikes. -// "vnf_cubes" = VNF Tile = Cornercubes texture. -// "vnf_diagonal_grid" = VNF Tile = A grid of thin lines at 45º angles. -// "vnf_diamonds" = VNF Tile = Like "diamonds", but slower and more consistent in triangulation. -// "vnf_dimples" = VNF Tile = Small round divots. -// "vnf_dots" = VNF Tile = Raised small round bumps. -// "vnf_hex_grid" = VNF Tile = A hexagonal grid of thin lines. -// "vnf_pyramids" = VNF Tile = Like "pyramids", but slower and more consistent in triangulation. -// "vnf_trunc_pyramids" = VNF Tile = Like "trunc_pyramids", but slower and more consistent in triangulation. +// --- +// n = The general number of vertices to use to refine the resolution of the texture. +// inset = The amount to inset part of a VNF tile texture. Generally between 0 and 0.5. +// gap = The gap between some parts of a VNF tile. (ie: gap between bricks, 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() // Example(3D): "ribs" texture. // tex = texture("ribs"); @@ -2195,13 +2204,13 @@ function associate_vertices(polygons, split, curpoly=0) = // Example(3D): "vnf_dots" texture. // tex = texture("vnf_dots"); // textured_linear_sweep( -// rect(50), tex, h=40, tscale=3, +// rect(50), tex, h=40, tscale=1, // tex_size=[10,10] // ); // Example(3D): "vnf_dimples" texture. // tex = texture("vnf_dimples"); // textured_linear_sweep( -// rect(50), tex, h=40, tscale=3, +// rect(50), tex, h=40, tscale=1, // tex_size=[10,10] // ); // Example(3D): "vnf_cones" texture. @@ -2246,195 +2255,290 @@ function associate_vertices(polygons, split, curpoly=0) = // rect(50), tex, h=40, // tex_size=[10,10], style="min_edge" // ); -function texture(tex,n,m,o) = - tex=="ribs"? [[1,0]] : - tex=="trunc_ribs"? [[each repeat(0,default(n,1)+1), each repeat(1,default(n,1)+1)]] : - tex=="wave_ribs"? [[for(a=[0:360/default(n,8):359]) (cos(a)+1)/2]] : - tex=="diamonds"? let(m=default(m,1)) [[m,0],[0,m]] : - tex=="vnf_diamonds"? let(m=default(m,1)) [ - [ - [0, 1,m], [1/2, 1,0], [1, 1,m], - [0,1/2,0], [1/2,1/2,m], [1,1/2,0], - [0, 0,m], [1/2, 0,0], [1, 0,m], - ], [ - [0,1,3], [2,5,1], [8,7,5], [6,3,7], - [1,5,4], [5,7,4], [7,3,4], [4,3,1], - ] - ] : - tex=="pyramids"? let(m=default(m,1)) [[0,0],[0,m]] : - tex=="vnf_pyramids"? let(m=default(m,1)) [ - [ [0,1,0], [1,1,0], [1/2,1/2,m], [0,0,0], [1,0,0] ], - [ [2,0,1], [2,1,4], [2,4,3], [2,3,0] ] - ] : - tex=="trunc_pyramids"? let(n=default(n,3), m=default(m,1)) [repeat(0,n+2), each repeat([0, each repeat(m,n+1)], n+1)] : - tex=="vnf_trunc_pyramids"? let(n=default(n,0.25), m=default(m,1)) [ - [ - each path3d(square(1)), - each move([1/2,1/2,m], p=path3d(rect(1-2*n))), - ], [ - for (i=[0:3]) each [ - [i, (i+1)%4, i+4], - [(i+1)%4, (i+1)%4+4, i+4], + +function texture(tex, n, inset, gap, roughness) = + tex=="ribs"? + let( + n = quantup(default(n,2),2) + ) [[ + each lerpn(1,0,n/2,endpoint=false), + each lerpn(0,1,n/2,endpoint=false), + ]] : + tex=="trunc_ribs"? + let( + n = quantup(default(n,4),4) + ) [[ + each repeat(0,n/4), + each lerpn(0,1,n/4,endpoint=false), + each repeat(1,n/4), + each lerpn(1,0,n/4,endpoint=false), + ]] : + tex=="wave_ribs"? + let( + n = max(6,default(n,8)) + ) [[ + for(a=[0:360/n:360-EPSILON]) + (cos(a)+1)/2 + ]] : + tex=="diamonds"? + let( + n = quantup(default(n,2),2) + ) [ + let( + path = [ + each lerpn(0,1,n/2,endpoint=false), + each lerpn(1,0,n/2,endpoint=false), + ] + ) + for (i=[0:1:n-1]) [ + for (j=[0:1:n-1]) min( + select(path,i+j), + select(path,i-j) + ) ], - [4,5,6], [4,6,7], - ] - ] : - 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=="bricks"? let(n=default(n,16), m=default(m,0.05)) [ - for (y = [0:1:n*2-1]) - rands(-m/2, m/2, 2*n, seed=12345+y*678) + [ - for (x = [0:1:2*n-1]) - (y%n <= max(1,n/16))? 0 : - let( even = floor(y/n)%2? n : 0 ) - (x+even) % (2*n) <= max(1,n/16)? 0 : 0.5 - ] - ] : - tex=="vnf_bricks"? let( - h=default(n,1), - gap=default(m,0.05), - inset=default(o,0.1) - ) [ + ] : + tex=="vnf_diamonds"? [ - each path3d(square(1)), - each move([gap/2, gap/2, 0], p=path3d(square([1-gap, 0.5-gap]))), - each move([gap/2+inset/2, gap/2+inset/2, h], p=path3d(square([1-gap-inset, 0.5-gap-inset]))), - each move([0, 0.5+gap/2, 0], p=path3d(square([0.5-gap/2, 0.5-gap]))), - each move([0, 0.5+gap/2+inset/2, h], p=path3d(square([0.5-gap/2-inset/2, 0.5-gap-inset]))), - each move([0.5+gap/2, 0.5+gap/2, 0], p=path3d(square([0.5-gap/2, 0.5-gap]))), - each move([0.5+gap/2+inset/2, 0.5+gap/2+inset/2, h], p=path3d(square([0.5-gap/2-inset/2, 0.5-gap-inset]))), - ], [ - [ 8, 9,10], [ 8,10,11], [16,17,18], [16,18,19], [24,25,26], - [24,26,27], [ 0, 1, 5], [ 0, 5, 4], [ 1,13, 6], [ 1, 6, 5], - [ 6,13,12], [ 6,12,21], [ 7,21,20], [ 6,21, 7], [ 0, 4, 7], - [ 0, 7,20], [21,12,15], [21,15,22], [ 3,23,22], [ 3,22,15], - [ 2,15,14], [ 2, 3,15], [23,27,26], [23,26,22], [21,22,26], - [21,26,25], [21,25,24], [21,24,20], [12,16,19], [12,19,15], - [14,15,19], [14,19,18], [13,17,16], [13,16,12], [ 6,10, 9], - [ 6, 9, 5], [ 5, 9, 8], [ 5, 8, 4], [ 4, 8,11], [ 4,11, 7], - [ 7,11,10], [ 7,10, 6], - ] - ] : - tex=="vnf_checkers"? let(n=default(n,0.05), m=default(m,1)) [ + [ + [0, 1, 1], [1/2, 1, 0], [1, 1, 1], + [0, 1/2, 0], [1/2, 1/2, 1], [1, 1/2, 0], + [0, 0, 1], [1/2, 0, 0], [1, 0, 1], + ], [ + [0,1,3], [2,5,1], [8,7,5], [6,3,7], + [1,5,4], [5,7,4], [7,3,4], [4,3,1], + ] + ] : + tex=="pyramids"? + let( + n = quantup(default(n,2),2) + ) [ + for (i = [0:1:n-1]) [ + for (j = [0:1:n-1]) + 1 - (max(abs(i-n/2), abs(j-n/2)) / (n/2)) + ] + ] : + tex=="vnf_pyramids"? [ - each move([0,0], p=path3d(square(0.5-n),m)), - each move([0,0.5], p=path3d(square(0.5-n))), - each move([0.5,0], p=path3d(square(0.5-n))), - each move([0.5,0.5], p=path3d(square(0.5-n),m)), - [1/2-n/2,1/2-n/2,m/2], [0,1,m], [1/2-n,1,m], - [1/2,1,0], [1-n,1,0], [1,0,m], [1,1/2-n,m], - [1,1/2,0], [1,1-n,0], [1,1,m], [1/2-n/2,1-n/2,m/2], - [1-n/2,1-n/2,m/2], [1-n/2,1/2-n/2,m/2], - ], [ - for (i=[0:4:12]) each [[i,i+1,i+2], [i, i+2, i+3]], - [10,13,11], [13,12,11], [2,5,4], [4,3,2], - [0,3,10], [10,9,0], [4,7,14], [4,14,13], - [4,13,16], [10,16,13], [10,3,16], [3,4,16], - [7,6,17], [7,17,18], [14,19,20], [14,20,15], - [8,11,22], [8,22,21], [12,15,24], [12,24,23], - [7,18,26], [7,26,14], [14,26,19], [18,19,26], - [15,20,27], [20,25,27], [24,27,25], [15,27,24], - [11,12,28], [12,23,28], [11,28,22], [23,22,28], - ] - ] : - tex=="vnf_cones"? let(n=quant(default(n,12),4), m=default(m,1)) [ + [ [0,1,0], [1,1,0], [1/2,1/2,1], [0,0,0], [1,0,0] ], + [ [2,0,1], [2,1,4], [2,4,3], [2,3,0] ] + ] : + tex=="trunc_pyramids"? + let( + n = quantup(default(n,6),3) + ) [ + for (i = [0:1:n-1]) [ + for (j = [0:1:n-1]) + (1 - (max(n/6, abs(i-n/2), abs(j-n/2)) / (n/2))) * 1.5 + ] + ] : + tex=="vnf_trunc_pyramids"? + let( + inset = default(inset,0.25) + ) [ + [ + each path3d(square(1)), + each move([1/2,1/2,1], p=path3d(rect(1-2*inset))), + ], [ + for (i=[0:3]) each [ + [i, (i+1)%4, i+4], + [(i+1)%4, (i+1)%4+4, i+4], + ], + [4,5,6], [4,6,7], + ] + ] : + 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=="bricks"? + let( + n = quantup(default(n,24),2), + rough = default(roughness,0.05) + ) [ + for (y = [0:1:n-1]) + rands(-rough/2, rough/2, n, seed=12345+y*678) + [ + for (x = [0:1:n-1]) + (y%(n/2) <= max(1,n/16))? 0 : + let( even = floor(y/(n/2))%2? n/2 : 0 ) + (x+even) % n <= max(1,n/16)? 0 : 0.5 + ] + ] : + tex=="vnf_bricks"? + let( + inset = default(inset,0.05), + gap = default(gap,0.05) + ) [ + [ + each path3d(square(1)), + each move([gap/2, gap/2, 0], p=path3d(square([1-gap, 0.5-gap]))), + each move([gap/2+inset/2, gap/2+inset/2, 1], p=path3d(square([1-gap-inset, 0.5-gap-inset]))), + each move([0, 0.5+gap/2, 0], p=path3d(square([0.5-gap/2, 0.5-gap]))), + each move([0, 0.5+gap/2+inset/2, 1], p=path3d(square([0.5-gap/2-inset/2, 0.5-gap-inset]))), + each move([0.5+gap/2, 0.5+gap/2, 0], p=path3d(square([0.5-gap/2, 0.5-gap]))), + each move([0.5+gap/2+inset/2, 0.5+gap/2+inset/2, 1], p=path3d(square([0.5-gap/2-inset/2, 0.5-gap-inset]))), + ], [ + [ 8, 9,10], [ 8,10,11], [16,17,18], [16,18,19], [24,25,26], + [24,26,27], [ 0, 1, 5], [ 0, 5, 4], [ 1,13, 6], [ 1, 6, 5], + [ 6,13,12], [ 6,12,21], [ 7,21,20], [ 6,21, 7], [ 0, 4, 7], + [ 0, 7,20], [21,12,15], [21,15,22], [ 3,23,22], [ 3,22,15], + [ 2,15,14], [ 2, 3,15], [23,27,26], [23,26,22], [21,22,26], + [21,26,25], [21,25,24], [21,24,20], [12,16,19], [12,19,15], + [14,15,19], [14,19,18], [13,17,16], [13,16,12], [ 6,10, 9], + [ 6, 9, 5], [ 5, 9, 8], [ 5, 8, 4], [ 4, 8,11], [ 4,11, 7], + [ 7,11,10], [ 7,10, 6], + ] + ] : + tex=="vnf_checkers"? + let( + inset = default(inset,0.05) + ) [ + [ + each move([0,0], p=path3d(square(0.5-inset),1)), + each move([0,0.5], p=path3d(square(0.5-inset))), + each move([0.5,0], p=path3d(square(0.5-inset))), + each move([0.5,0.5], p=path3d(square(0.5-inset),1)), + [1/2-inset/2,1/2-inset/2,1/2], [0,1,1], [1/2-inset,1,1], + [1/2,1,0], [1-inset,1,0], [1,0,1], [1,1/2-inset,1], + [1,1/2,0], [1,1-inset,0], [1,1,1], [1/2-inset/2,1-inset/2,1/2], + [1-inset/2,1-inset/2,1/2], [1-inset/2,1/2-inset/2,1/2], + ], [ + for (i=[0:4:12]) each [[i,i+1,i+2], [i, i+2, i+3]], + [10,13,11], [13,12,11], [2,5,4], [4,3,2], + [0,3,10], [10,9,0], [4,7,14], [4,14,13], + [4,13,16], [10,16,13], [10,3,16], [3,4,16], + [7,6,17], [7,17,18], [14,19,20], [14,20,15], + [8,11,22], [8,22,21], [12,15,24], [12,24,23], + [7,18,26], [7,26,14], [14,26,19], [18,19,26], + [15,20,27], [20,25,27], [24,27,25], [15,27,24], + [11,12,28], [12,23,28], [11,28,22], [23,22,28], + ] + ] : + tex=="vnf_cones"? + let( + n = quant(default(n,12),4), + inset = default(inset,0) + ) + assert(inset>=0 && inset<0.5) [ - each move([1/2,1/2], p=path3d(circle(d=1,$fn=n))), - [1/2,1/2,m], - each path3d(square(1)), - ], [ - for (i=[0:1:n-1]) [i, (i+1)%n, n], - for (i=[0:1:3], j=[0:1:n/4-1]) [n+1+i, (i*n/4+j+1)%n, i*n/4+j], - ] - ] : - tex=="vnf_cubes"? let(m=default(m,1)) [ + [ + each move([1/2,1/2], p=path3d(circle(d=1-inset,$fn=n))), + [1/2,1/2,1], + each path3d(square(1)), + ], [ + for (i=[0:1:n-1]) [i, (i+1)%n, n], + for (i=[0:1:3], j=[0:1:n/4-1]) [n+1+i, (i*n/4+j+1)%n, i*n/4+j], + if (inset > 0) for (i = [0:1:3]) [i+n+1, (i+1)%4+n+1, ((i+1)*n/4)%n], + ] + ] : + tex=="vnf_cubes"? [ - [0,1,m/2], [1,1,m/2], [1/2,5/6,m], [0,4/6,0], [1,4/6,0], - [1/2,3/6,m/2], [0,2/6,m], [1,2/6,m], [1/2,1/6,0], [0,0,m/2], - [1,0,m/2], - ], [ - [0,1,2], [0,2,3], [1,4,2], [2,5,3], [2,4,5], - [6,3,5], [4,7,5], [7,8,5], [6,5,8], [10,8,7], - [9,6,8], [10,9,8], - ] - ] : - tex=="vnf_diagonal_grid"? let(m=default(m,1)) [ - [ - each move([1/2,1/2,0], p=path3d(circle(d=1,$fn=4))), - each move([1/2,1/2,m], p=path3d(circle(d=0.8,$fn=4))), - for (a=[0:90:359]) each move([1/2,1/2], p=zrot(-a, p=[[1/2,0.1,m], [0.1,1/2,m], [1/2,1/2,m]])) - ], [ - for (i=[0:3]) each let(j=i*3+8) [ - [i,(i+1)%4,(i+1)%4+4], [i,(i+1)%4+4,i+4], - [j,j+1,j+2], [i, (i+3)%4, j], [(i+3)%4, j+1, j], - ], - [4,5,6], [4,6,7], - ] - ] : - tex=="vnf_dimples" || tex=="vnf_dots" ? let( - n = quant(default(n,12),4), - m = default(m,0.05), - rows=ceil(n/4), - r=adj_ang_to_hyp(1/2-m,45), - dots = tex=="vnf_dots", - cp=[1/2, 1/2, r*sin(45)*(dots?-1:1)] - ) [ + [ + [0,1,1/2], [1,1,1/2], [1/2,5/6,1], [0,4/6,0], [1,4/6,0], + [1/2,3/6,1/2], [0,2/6,1], [1,2/6,1], [1/2,1/6,0], [0,0,1/2], + [1,0,1/2], + ], [ + [0,1,2], [0,2,3], [1,4,2], [2,5,3], [2,4,5], + [6,3,5], [4,7,5], [7,8,5], [6,5,8], [10,8,7], + [9,6,8], [10,9,8], + ] + ] : + tex=="vnf_diagonal_grid"? + let( + inset = default(inset,0.1) + ) + assert(inset>0 && inset<0.5) [ - each path3d(square(1)), - for (p=[0:1:rows-1], t=[0:360/n:359.999]) - cp + ( - dots? spherical_to_xyz(r, -t, 45-45*p/rows) : - spherical_to_xyz(r, -t, 135+45*p/rows) - ), - cp + r * (dots?UP:DOWN), - ], [ - for (i=[0:1:3], j=[0:1:n/4-1]) [i, 4+(i*n/4+j+1)%n, 4+i*n/4+j], - for (i=[0:1:rows-2], j=[0:1:n-1]) each [ - [4+i*n+j, 4+(i+1)*n+(j+1)%n, 4+(i+1)*n+j], - [4+i*n+j, 4+i*n+(j+1)%n, 4+(i+1)*n+(j+1)%n], + [ + each move([1/2,1/2,0], p=path3d(circle(d=1,$fn=4))), + each move([1/2,1/2,1], p=path3d(circle(d=1-inset*2,$fn=4))), + for (a=[0:90:359]) each move([1/2,1/2], p=zrot(-a, p=[[1/2,inset,1], [inset,1/2,1], [1/2,1/2,1]])) + ], [ + for (i=[0:3]) each let(j=i*3+8) [ + [i,(i+1)%4,(i+1)%4+4], [i,(i+1)%4+4,i+4], + [j,j+1,j+2], [i, (i+3)%4, j], [(i+3)%4, j+1, j], + ], + [4,5,6], [4,6,7], + ] + ] : + tex=="vnf_dimples" || tex=="vnf_dots" ? + let( + n = quant(default(n,16),4), + inset = default(inset,0.05) + ) + assert(inset>=0 && inset < 0.5) + let( + rows=ceil(n/4), + r=adj_ang_to_hyp(1/2-inset,45), + dots = tex=="vnf_dots", + cp = [1/2, 1/2, r*sin(45)*(dots?-1:1)], + sc = 1 / (r - abs(cp.z)), + uverts = [ + each path3d(square(1)), + for (p=[0:1:rows-1], t=[0:360/n:359.999]) + cp + ( + dots? spherical_to_xyz(r, -t, 45-45*p/rows) : + spherical_to_xyz(r, -t, 135+45*p/rows) + ), + cp + r * (dots?UP:DOWN), ], - for (i=[0:1:n-1]) [4+(rows-1)*n+i, 4+(rows-1)*n+(i+1)%n, 4+rows*n], - if (m>0) for (i=[0:3]) [i, (i+1)%4, 4+(i+1)%4*n/4] - ] - ] : - tex=="vnf_hex_grid"? let( - h=default(n,1), inset=default(m,0.1), - diag=opp_ang_to_hyp(inset,60), - side=adj_ang_to_opp(1,30), - hyp=adj_ang_to_hyp(0.5,30), - check=assert(inset<0.5), - sc = 1/3/hyp, - hex=[ [1,2/6,0], [1/2,1/6,0], [0,2/6,0], [0,4/6,0], [1/2,5/6,0], [1,4/6,0] ] - ) [ - [ - each hex, - each move([0.5,0.5], p=yscale(sc, p=path3d(ellipse(d=1-2*inset, circum=true, spin=-30,$fn=6),h))), - hex[0]-[0,diag*sc,-h], - for (ang=[270+60,270-60]) hex[1]+yscale(sc, p=cylindrical_to_xyz(diag,ang,h)), - hex[2]-[0,diag*sc,-h], - [0,0,h], [0.5-inset,0,h], [0.5,0,0], [0.5+inset,0,h], [1,0,h], - hex[3]+[0,diag*sc,h], - for (ang=[90+60,90-60]) hex[4]+yscale(sc, p=cylindrical_to_xyz(diag,ang,h)), - hex[5]+[0,diag*sc,h], - [0,1,h], [0.5-inset,1,h], [0.5,1,0], [0.5+inset,1,h], [1,1,h], - ], [ - for (i=[0:2:5]) let(b=6) [b+i, b+(i+1)%6, b+(i+2)%6], [6,8,10], - for (i=[0:1:5]) each [ [i, (i+1)%6, (i+1)%6+6], [i, (i+1)%6+6, i+6] ], - [19,13,12], [19,12,20], [17,16,15], [17,15,14], - [21,25,26], [21,26,22], [23,28,29], [23,29,24], - [0,12,13], [0,13,1], [1,14,15], [1,15,2], - [3,21,22], [3,22,4], [4,23,24], [4,24,5], - [1,13,19], [1,19,18], [1,18,17], [1,17,14], - [4,22,26], [4,26,27], [4,27,28], [4,28,23], - ] - ] : - tex=="rough"? let(n=default(n,32), m=default(m,0.1)) [ - for (y = [0:1:n-1]) rands(0, m, n, seed=123456+29*y) - ] : + verts = zscale(sc, p=uverts), + faces = [ + for (i=[0:1:3], j=[0:1:n/4-1]) [i, 4+(i*n/4+j+1)%n, 4+i*n/4+j], + for (i=[0:1:rows-2], j=[0:1:n-1]) each [ + [4+i*n+j, 4+(i+1)*n+(j+1)%n, 4+(i+1)*n+j], + [4+i*n+j, 4+i*n+(j+1)%n, 4+(i+1)*n+(j+1)%n], + ], + for (i=[0:1:n-1]) [4+(rows-1)*n+i, 4+(rows-1)*n+(i+1)%n, 4+rows*n], + if (inset>0) for (i=[0:3]) [i, (i+1)%4, 4+(i+1)%4*n/4] + ] + ) [verts, faces] : + tex=="vnf_hex_grid"? + let( + inset=default(inset,0.1) + ) + assert(inset>=0 && inset<0.5) + let( + diag=opp_ang_to_hyp(inset,60), + side=adj_ang_to_opp(1,30), + hyp=adj_ang_to_hyp(0.5,30), + sc = 1/3/hyp, + hex=[ [1,2/6,0], [1/2,1/6,0], [0,2/6,0], [0,4/6,0], [1/2,5/6,0], [1,4/6,0] ] + ) [ + [ + each hex, + each move([0.5,0.5], p=yscale(sc, p=path3d(ellipse(d=1-2*inset, circum=true, spin=-30,$fn=6),1))), + hex[0]-[0,diag*sc,-1], + for (ang=[270+60,270-60]) hex[1]+yscale(sc, p=cylindrical_to_xyz(diag,ang,1)), + hex[2]-[0,diag*sc,-1], + [0,0,1], [0.5-inset,0,1], [0.5,0,0], [0.5+inset,0,1], [1,0,1], + hex[3]+[0,diag*sc,1], + for (ang=[90+60,90-60]) hex[4]+yscale(sc, p=cylindrical_to_xyz(diag,ang,1)), + hex[5]+[0,diag*sc,1], + [0,1,1], [0.5-inset,1,1], [0.5,1,0], [0.5+inset,1,1], [1,1,1], + ], [ + for (i=[0:2:5]) let(b=6) [b+i, b+(i+1)%6, b+(i+2)%6], [6,8,10], + for (i=[0:1:5]) each [ [i, (i+1)%6, (i+1)%6+6], [i, (i+1)%6+6, i+6] ], + [19,13,12], [19,12,20], [17,16,15], [17,15,14], + [21,25,26], [21,26,22], [23,28,29], [23,29,24], + [0,12,13], [0,13,1], [1,14,15], [1,15,2], + [3,21,22], [3,22,4], [4,23,24], [4,24,5], + [1,13,19], [1,19,18], [1,18,17], [1,17,14], + [4,22,26], [4,26,27], [4,27,28], [4,28,23], + ] + ] : + tex=="rough"? + let( + n = default(n,32), + rough = default(roughness, 0.2) + ) [ + for (y = [0:1:n-1]) + rands(0, rough, n, seed=123456+29*y) + ] : assert(false, str("Unrecognized texture name: ", tex)); @@ -2487,7 +2591,7 @@ function texture(tex,n,m,o) = // Example: "pyramids" texture. // textured_linear_sweep( // rect(50), "pyramids", tex_size=[10,10], -// h=40, style="concave"); +// h=40, style="convex"); // Example: "vnf_bricks" texture. // path = glued_circles(r=15, spread=40, tangent=45); // textured_linear_sweep( @@ -2524,6 +2628,7 @@ function texture(tex,n,m,o) = // path, h=40, "trunc_pyramids", tex_size=[5,5], // tscale=1, style="convex"); // vnf_polyhedron(vnf, convexity=10); + function textured_linear_sweep( region, texture, tex_size=[5,5], h, counts, @@ -2574,10 +2679,10 @@ function textured_linear_sweep( assert(all_defined(tex_dim), "Heightfield texture must be a 2D square array of scalar heights."), sorted_tile = !is_vnf(texture)? texture : - samples<=1? texture : let( - s = 1/samples, - vnf = vnf_slice(texture, "X", list([s:s:1-s/2])) + s = 1 / max(1, samples), + vnf = samples<=1? texture : + vnf_slice(texture, "X", list([s:s:1-s/2])) ) _vnf_sort_vertices(vnf, idx=[1,0]), vertzs = !is_vnf(sorted_tile)? undef : group_sort(sorted_tile[0], idx=1), @@ -2796,6 +2901,7 @@ function _find_vnf_tile_edge_path(vnf, val) = // 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` @@ -2844,6 +2950,7 @@ function _find_vnf_tile_edge_path(vnf, val) = // right(40, p=circle(d=40,$fn=6)), // ]; // textured_revolution(rgn, texture="diamonds", tex_size=[10,10], tscale=1, angle=240, style="concave"); + function textured_revolution( shape, texture, tex_size, tscale=1, inset=false, rot=false, @@ -3169,6 +3276,7 @@ module textured_revolution( // 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` @@ -3177,15 +3285,17 @@ module textured_revolution( // 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=8); +// 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"); + function textured_cylinder( h, r, texture, tex_size=[1,1], counts, tscale=1, inset=false, rot=false, caps=true, style="min_edge", shift=[0,0], l, r1, r2, d, d1, d2, chamfer, chamfer1, chamfer2, - rounding, rounding1, rounding2 + rounding, rounding1, rounding2, + samples ) = let( h = first_defined([h, l, 1]), @@ -3214,7 +3324,8 @@ function textured_cylinder( reverse(path), texture, closed=false, tex_size=tex_size, counts=counts, tscale=tscale, inset=inset, rot=rot, - style=style, shift=shift + style=style, shift=shift, + samples=samples ) ) vnf; @@ -3226,7 +3337,7 @@ module textured_cylinder( l, r1, r2, d, d1, d2, chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2, - convexity=10, + convexity=10, samples, anchor=CENTER, spin=0, orient=UP ) { h = first_defined([h, l, 1]); @@ -3241,7 +3352,7 @@ module textured_cylinder( tscale=tscale, inset=inset, rot=rot, counts=counts, tex_size=tex_size, caps=true, style=style, - shift=shift, + shift=shift, samples=samples, chamfer1=chamf1, chamfer2=chamf2, rounding1=round1, rounding2=round2 );