diff --git a/skin.scad b/skin.scad index 7430e8d1..a184e7b8 100644 --- a/skin.scad +++ b/skin.scad @@ -1008,6 +1008,61 @@ function linear_sweep( // tex_taper=[[0,0], [10,0], [10.1,1], [100,1]], // style="convex", // convexity=10); +// Example(3D,NoAxes,Med,VPT=[-2.92656,1.26781,0.102897],VPR=[62.7,0,222.4],VPD=216.381): This VNF tile makes a closed shape and the actual main extrusion is not created. +// shape = skin([rect(2/5), +// rect(2/3), +// rect(2/5)], +// z=[0,1/2,1], +// slices=0, +// caps=false); +// tile = move([0,1/2,2/3],yrot(90,shape)); +// path = [for(y=[-30:30]) [ 20-3*(1-cos((y+30)/60*360)),y]]; +// rotate_sweep(path, closed=false, texture=tile, +// tex_size=[10,10], tex_depth=5); +// Example(3D,Med,VPT=[1.04269,4.35278,-0.716624],VPR=[98.4,0,43.9],VPD=175.268): Adding the angle parameter cuts off the extrusion. Note how each extruded component is capped. +// shape = skin([rect(2/5), +// rect(2/3), +// rect(2/5)], +// z=[0,1/2,1], +// slices=0, +// caps=false); +// tile = move([0,1/2,2/3],yrot(90,shape)); +// path = [for(y=[-30:30]) [ 20-3*(1-cos((y+30)/60*360)),y]]; +// rotate_sweep(path, closed=false, texture=tile, +// tex_size=[10,15], tex_depth=5, angle=215); +// Example(3D,NoAxes,Med,VPT=[1.00759,3.89216,-1.27032],VPR=[57.1,0,34.8],VPD=240.423): Turning the texture 90 degrees with `tex_rot` produces a texture that ends at the top and bottom. +// shape = skin([rect(2/5), +// rect(2/3), +// rect(2/5)], +// z=[0,1/2,1], +// slices=0, +// caps=false); +// tile = move([0,1/2,2/3],yrot(90,shape)); +// path = [for(y=[-30:30]) [ 20-3*(1-cos((y+30)/60*360)),y]]; +// rotate_sweep(path, closed=false, texture=tile, tex_rot=90, +// tex_size=[12,8], tex_depth=9, angle=360); +// Example(3D,Med,NoAxes: A basket weave texture, here only half way around the circle to avoid clutter. +// diag_weave_vnf = [ +// [[0.2, 0, 0], [0.8, 0, 0], [1, 0.2, 0.5], [1, 0.8, 0.5], [0.7, 0.5, 0.5], +// [0.5, 0.3, 0], [0.2, 0, 0.5], [0.8, 0, 0.5], [1, 0.2, 1], [1, 0.8, 1], +// [0.7, 0.5, 1], [0.5, 0.3, 0.5], [1, 0.2, 0], [1, 0.8, 0], [0.8, 1, 0.5], +// [0.2, 1, 0.5], [0.5, 0.7, 0.5], [0.7, 0.5, 0], [0.8, 1, 1], [0.2, 1, 1], +// [0.5, 0.7, 1], [0.8, 1, 0], [0.2, 1, 0], [0, 0.8, 0.5], [0, 0.2, 0.5], +// [0.3, 0.5, 0.5], [0.5, 0.7, 0], [0, 0.8, 1], [0, 0.2, 1], [0.3, 0.5, 1], +// [0, 0.8, 0], [0, 0.2, 0], [0.3, 0.5, 0], [0.2, 0, 1], [0.8, 0, 1], [0.5, 0.3, 1]], +// [[0, 1, 5], [1, 2, 4, 5], [7, 11, 10, 8], [8, 10, 9], [7, 8, 2, 1], [9, 10, 4, 3], +// [10, 11, 5, 4], [0, 5, 11, 6], [12, 13, 17], [13, 14, 16, 17], [3, 4, 20, 18], +// [18, 20, 19], [3, 18, 14, 13], [19, 20, 16, 15], [20, 4, 17, 16], [12, 17, 4, 2], +// [21, 22, 26], [22, 23, 25, 26], [15, 16, 29, 27], [27, 29, 28], [15, 27, 23, 22], +// [28, 29, 25, 24], [29, 16, 26, 25], [21, 26, 16, 14], [30, 31, 32], [31, 6, 11, 32], +// [24, 25, 35, 33], [33, 35, 34], [24, 33, 6, 31], [34, 35, 11, 7], +// [35, 25, 32, 11], [30, 32, 25, 23]] +// ]; +// path = [for(y=[-30:30]) [ 20-3*(1-cos((y+30)/60*360)),y]]; +// down(31)linear_extrude(height=1)arc(r=23,angle=[0,180], wedge=true); +// rotate_sweep(path, closed=false, texture=diag_weave_vnf, angle=180, +// tex_size=[10,10], convexity=12, tex_depth=2); + function rotate_sweep( shape, angle=360, @@ -1144,6 +1199,7 @@ module rotate_sweep( } + // Function&Module: spiral_sweep() // Synopsis: Sweep a path along a helix. // SynTags: VNF, Geom @@ -4195,6 +4251,12 @@ function _textured_linear_sweep( +// Given a VNF texture tile finds the paths on either the x=0 (axis=0) or the y=0 (axis=1) cases. +// Would also find the z=0 paths if you gave axis=2. +// +// It returns two lists, a list of open paths and a list of closed paths. By default a max of +// one open path is permitted; either list can be empty. The paths go in the direction of the segments +// in the VNF. function _tile_edge_path_list(vnf, axis, maxopen=1) = let( @@ -4283,6 +4345,7 @@ function _find_vnf_tile_edge_path(vnf, val) = /// "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, tex_scale=1, inset=false, rot=false, shift=[0,0], @@ -4358,12 +4421,18 @@ function _textured_revolution( ) zvnf ) _vnf_sort_vertices(utex, idx=[0,1]), vertzs = is_vnf(texture)? group_sort(tile[0], idx=0) : undef, - bpath = is_vnf(tile) - ? _find_vnf_tile_edge_path(tile,1) + edge_paths = is_vnf(tile) ? _tile_edge_path_list(tile,1) : undef, + bpath = is_def(edge_paths) + ? len(edge_paths[0])==0 ? [] : hstack([column(edge_paths[0][0],0), column(edge_paths[0][0],2)]) : let( row = tile[0], rlen = len(row) ) [for (i = [0:1:rlen]) [i/rlen, row[i%rlen]]], + edge_closed_paths = is_def(edge_paths) ? edge_paths[1] : [], + side_paths = angle==360 || !is_vnf(tile) ? undef + : _tile_edge_path_list(tile,0), + side_open_path = is_undef(side_paths) ? undef : len(side_paths[0])==0 ? [] : side_paths[0][1], + side_closed_paths = is_undef(side_paths) ? [] : side_paths[1], counts_x = is_vector(counts,2)? counts.x : is_vector(tex_size,2) ? max(1,round(angle/360*circumf/tex_size.x)) @@ -4379,6 +4448,18 @@ function _textured_revolution( taperout = [[-1,retaper[0][1]], each retaper, [2,last(retaper)[1]]] ) taperout : assert(false, "Bad taper= argument value."), + transform_point = function(tileind, tilez, counts_y, bases, norms) + let( + part = tileind * samples, + ind = floor(part), + frac = part - ind, + base = lerp(bases[ind], select(bases,ind+1), frac), + norm = unit(lerp(norms[ind], select(norms,ind+1), frac)), + scale = tex_scale * lookup(tileind/counts_y, taper_lup) * base.x/maxx, + texh = scale<0 ? -(1-tilez - inset) * scale + : (tilez - inset) * scale + ) + base - norm * texh, full_vnf = vnf_join([ for (rgn = regions) let( rgn_wall_vnf = vnf_join([ @@ -4398,17 +4479,9 @@ function _textured_revolution( [ [ for (group = vertzs) each [ - for (vert = group) 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)), - tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), - texh = tex_scale<0 ? -(1-vert.z - inset) * tex_scale * (base.x / maxx) - : (vert.z - inset) * tex_scale * (base.x / maxx), - xyz = base - norm * texh - ) zrot(vert.x*angle/counts_x, p=xyz) + for (vert = group) + let(xyz = transform_point(j + (1-vert.y),vert.z,counts_y,bases, norms)) + zrot(vert.x*angle/counts_x, p=xyz) ] ], tile[1] @@ -4422,21 +4495,12 @@ function _textured_revolution( let( v = (j + (tj/texcnt.x)) / counts_x, mat = zrot(v*angle) - ) apply(mat, [ - for (i = [0:1:counts_y-(closed?1:0)], ti = [0:1:texcnt.y-1]) - if (i != counts_y || ti == 0) - let( - part = (i + (ti/texcnt.y)) * samples, - u = floor(part), - uu = part - u, - base = lerp(bases[u], select(bases,u+1), uu), - norm = unit(lerp(norms[u], select(norms,u+1), uu)), - tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), - texh = tex_scale<0 ? -(1-texture[ti][tj] - inset) * tex_scale * (base.x / maxx) - : (texture[ti][tj] - inset) * tex_scale * (base.x / maxx), - xyz = base - norm * texh - ) xyz - ]) + ) + apply(mat, [ + for (i = [0:1:counts_y-(closed?1:0)], ti = [0:1:texcnt.y-1]) + if (i != counts_y || ti == 0) + transform_point(i + (ti/texcnt.y),texture[ti][tj],counts_y, bases, norms) + ]) ]) ) vnf_vertex_array( tiles, caps=false, style=style, @@ -4448,9 +4512,9 @@ function _textured_revolution( for (i = [0:1:counts_x-1]) zrot(i*angle/counts_x, rgn_wall_vnf) ]), - endcap_vnf = angle == 360? EMPTY_VNF : + sidecap_vnf = angle == 360? EMPTY_VNF : let( - cap_rgn = [ + cap_rgn = side_open_path == [] ? [] : [ for (path = rgn) let( plen = path_length(path, closed=closed), counts_y = is_vector(counts,2)? counts.y : @@ -4462,35 +4526,16 @@ function _textured_revolution( ppath = is_vnf(texture) ? [ // VNF tile texture for (j = [0:1:counts_y-1]) - 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)), - tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), - texh = tex_scale<0 ? -(1-vert.z - inset) * tex_scale * (base.x / maxx) - : (vert.z - inset) * tex_scale * (base.x / maxx), - xyz = base - norm * texh - ) xyz + //for (group = vertzs, vert = reverse(group)) + for(vert=side_open_path) + transform_point(j + (1 - vert.y),vert.z,counts_y,bases, norms) ] : let( // Heightfield texture texcnt = [len(texture[0]), len(texture)] ) [ for (i = [0:1:counts_y-(closed?1:0)], ti = [0:1:texcnt.y-1]) if (i != counts_y || ti == 0) - let( - part = (i + (ti/texcnt.y)) * samples, - u = floor(part), - uu = part - u, - base = lerp(bases[u], select(bases,u+1), uu), - norm = unit(lerp(norms[u], select(norms,u+1), uu)), - tex_scale = tex_scale * lookup(part/samples/counts_y, taper_lup), - texh = tex_scale<0 ? -(1-texture[ti][0] - inset) * tex_scale * (base.x / maxx) - : (texture[ti][0] - inset) * tex_scale * (base.x / maxx), - xyz = base - norm * texh - ) xyz + transform_point(i + (ti/texcnt.y),texture[ti][0],counts_y,bases, norms) ], path = closed? ppath : [ [0, ppath[0].y], @@ -4499,10 +4544,30 @@ function _textured_revolution( ] ) deduplicate(path, closed=closed) ], - vnf2 = vnf_from_region(cap_rgn, xrot(90), reverse=false), - vnf3 = vnf_from_region(cap_rgn, rot([90,0,angle]), reverse=true) - ) vnf_join([vnf2, vnf3]), - allcaps_vnf = closed? EMPTY_VNF : + vnf2 = cap_rgn==[] ? EMPTY_VNF : vnf_from_region(cap_rgn, xrot(90), reverse=false), + vnf3 = cap_rgn==[] ? EMPTY_VNF : vnf_from_region(cap_rgn, rot([90,0,angle]), reverse=true), + extra_paths = side_closed_paths==[] ? [] + : [for (path = rgn) let( + plen = path_length(path, closed=closed), + counts_y = is_vector(counts,2)? counts.y : + is_vector(tex_size,2)? max(1,round(plen/tex_size.y)) : 6, + obases = resample_path(path, n=counts_y * samples + (closed?0:1), closed=closed), + onorms = path_normals(obases, closed=closed), + bases = closed? list_wrap(obases) : obases, + norms = closed? list_wrap(onorms) : onorms, + modpaths = [for (j = [0:1:counts_y-1], cpath=side_closed_paths) + [for(vert=cpath) + transform_point(j + (1 - vert.y),vert.z,counts_y,bases, norms)] + ] + ) + each modpaths + ], + extra_vnfs = [ + if (len(extra_paths)>0) for(path=extra_paths) [xrot(90,path3d(path)), [count(len(path))]], + if (len(extra_paths)>0) for(path=extra_paths) [rot([90,0,angle],p=path3d(path)), [count(len(path),reverse=true)]], + ] + ) vnf_join([vnf2, vnf3, each extra_vnfs]), + endcaps_vnf = closed? EMPTY_VNF : let( plen = path_length(rgn[0], closed=closed), counts_y = is_vector(counts,2)? counts.y : @@ -4514,39 +4579,55 @@ function _textured_revolution( bases = xrot(90, p=path3d(rbases)), norms = xrot(90, p=path3d(rnorms)), caps_vnf = vnf_join([ - for (j = [-1,0]) let( - base = select(bases,j), - norm = unit(select(norms,j)), - ppath = [ - for (vert = bpath) let( - uang = vert.x / counts_x, - tex_scale = tex_scale * lookup([0,1][j+1], taper_lup), - texh = tex_scale<0 ? -(1-vert.y - inset) * tex_scale * (base.x / maxx) - : (vert.y - inset) * tex_scale * (base.x / maxx), - xyz = base - norm * texh - ) zrot(angle*uang, p=xyz) - ], - pplen = len(ppath), - zed = j<0? max(column(ppath,2)) : - min(column(ppath,2)), - slice_vnf = [ - [ - each ppath, - [0, 0, zed], - ], [ - for (i = [0:1:pplen-2]) - j<0? [pplen, i, (i+1)%pplen] : - [pplen, (i+1)%pplen, i] - ] - ], - cap_vnf = vnf_join([ + for (epath=edge_closed_paths, j = [-1,0]) + let( + base = select(bases,j), + norm = unit(select(norms,j)), + ppath = [ + for (vert = epath) let( + uang = vert.x / counts_x, + tex_scale = tex_scale * lookup(j+1, taper_lup), + texh = tex_scale<0 ? -(1-vert.z - inset) * tex_scale * (base.x / maxx) + : (vert.z - inset) * tex_scale * (base.x / maxx), + xyz = base - norm * texh + ) zrot(angle*uang, p=xyz) + ], + faces = [count(ppath,reverse=j==0)] + ) + for(i=[0:1:counts_x-1]) + [zrot(i*angle/counts_x, ppath), faces], + if (len(bpath)>0) + for (j = [-1,0]) + let( + base = select(bases,j), + norm = unit(select(norms,j)), + ppath = [ + for (vert = bpath) let( + uang = vert.x / counts_x, + tex_scale = tex_scale * lookup(j+1, taper_lup), + texh = tex_scale<0 ? -(1-vert.y - inset) * tex_scale * (base.x / maxx) + : (vert.y - inset) * tex_scale * (base.x / maxx), + xyz = base - norm * texh + ) zrot(angle*uang, p=xyz) + ], + pplen = len(ppath), + zed = j<0? max(column(ppath,2)): min(column(ppath,2)), + slice_vnf = [ + [ + each ppath, + [0, 0, zed], + ], [ + for (i = [0:1:pplen-2]) + j<0? [pplen, i, (i+1)%pplen] + : [pplen, (i+1)%pplen, i] + ] + ] + ) for (i = [0:1:counts_x-1]) - zrot(i*angle/counts_x, p=slice_vnf) - ]) - ) cap_vnf + zrot(i*angle/counts_x, p=slice_vnf) ]) ) caps_vnf - ) vnf_join([walls_vnf, endcap_vnf, allcaps_vnf]) + ) vnf_join([walls_vnf, sidecap_vnf, endcaps_vnf]) ]), skmat = zrot(start) * down(-miny) * skew(sxz=shift.x/h, syz=shift.y/h) * up(-miny), skvnf = apply(skmat, full_vnf), @@ -4556,6 +4637,7 @@ function _textured_revolution( ) reorient(anchor,spin,orient, geom=geom, p=skvnf); + module _textured_revolution( shape, texture, tex_size, tex_scale=1, inset=false, rot=false, shift=[0,0], diff --git a/std.scad b/std.scad index 1b745f4e..eadade62 100644 --- a/std.scad +++ b/std.scad @@ -34,8 +34,8 @@ include include include include -include include +include include include include diff --git a/vnf.scad b/vnf.scad index 4f7e73ae..ddc0a03a 100644 --- a/vnf.scad +++ b/vnf.scad @@ -1092,8 +1092,8 @@ function _split_polygon_at_x(poly, x) = out1 = [for (p = poly2) if(p.x <= x) p], out2 = [for (p = poly2) if(p.x >= x) p], out3 = [ - if (len(out1)>=3) each split_path_at_self_crossings(out1), - if (len(out2)>=3) each split_path_at_self_crossings(out2), + if (len(out1)>=3 && polygon_area(out1)>EPSILON) each split_path_at_self_crossings(out1), + if (len(out2)>=3 && polygon_area(out2)>EPSILON) each split_path_at_self_crossings(out2), ], out = [for (p=out3) if (len(p) > 2) list_unwrap(p)] ) out;