fix rotate_sweep texture endcaps

This commit is contained in:
Adrian Mariano 2025-04-03 16:56:46 -04:00
parent f1673f4cc8
commit 19fa25b6e7
3 changed files with 172 additions and 90 deletions

256
skin.scad
View file

@ -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],

View file

@ -34,8 +34,8 @@ include <coords.scad>
include <geometry.scad>
include <regions.scad>
include <strings.scad>
include <skin.scad>
include <vnf.scad>
include <skin.scad>
include <utility.scad>
include <partitions.scad>
include <structs.scad>

View file

@ -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;