mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-08-17 23:01:17 +02:00
texture support for vnf_vertex_array
This commit is contained in:
@@ -820,6 +820,7 @@ function prismoid(
|
|||||||
// you can give the length of the side of the polygon. You can specify chamfers and roundings for the ends, but not
|
// you can give the length of the side of the polygon. You can specify chamfers and roundings for the ends, but not
|
||||||
// the vertical edges. See {{rounded_prism()}} for prisms with rounded vertical edges. You can also specify texture for the side
|
// the vertical edges. See {{rounded_prism()}} for prisms with rounded vertical edges. You can also specify texture for the side
|
||||||
// faces, but note that texture is not compatible with any roundings or chamfers.
|
// faces, but note that texture is not compatible with any roundings or chamfers.
|
||||||
|
// See [Texturing](skin.scad#section-texturing) for more details on how textures work.
|
||||||
// .
|
// .
|
||||||
// Anchors are based on the VNF of the prism. Especially for tapered or shifted prisms, this may give unexpected anchor positions, such as top side anchors
|
// Anchors are based on the VNF of the prism. Especially for tapered or shifted prisms, this may give unexpected anchor positions, such as top side anchors
|
||||||
// being located at the bottom of the shape, so confirm anchor positions before use.
|
// being located at the bottom of the shape, so confirm anchor positions before use.
|
||||||
@@ -1158,7 +1159,9 @@ function regular_prism(n,
|
|||||||
// textured_tile(texture, [size], [w1=], [w2=], [ang=], [shift=], [h=/height=/thickness=], [atype=], [diff=], [tex_extra=], [tex_skip=], ...) [ATTACHMENTS];
|
// textured_tile(texture, [size], [w1=], [w2=], [ang=], [shift=], [h=/height=/thickness=], [atype=], [diff=], [tex_extra=], [tex_skip=], ...) [ATTACHMENTS];
|
||||||
// vnf = textured_tile(texture, [size], [w1=], [w2=], [ang=], [shift=], [h=/height=/thickness=], [atype=], [tex_extra=], [tex_skip=], ...);
|
// vnf = textured_tile(texture, [size], [w1=], [w2=], [ang=], [shift=], [h=/height=/thickness=], [atype=], [tex_extra=], [tex_skip=], ...);
|
||||||
// Description:
|
// Description:
|
||||||
// Creates a cuboid or trapezoidal prism and places a texture on the top face. You can specify the size by giving a `size` scalar or vector as is
|
// Creates a cuboid or trapezoidal prism and places a texture on the top face.
|
||||||
|
// See [Texturing](skin.scad#section-texturing) for more details on how textures work.
|
||||||
|
// You can specify the size of the object by giving a `size` scalar or vector as is
|
||||||
// usual for a cube. If you give a scalar, however, it applies only to the X and Y dimensions: the default is to create a thin tile, not a cube.
|
// usual for a cube. If you give a scalar, however, it applies only to the X and Y dimensions: the default is to create a thin tile, not a cube.
|
||||||
// The Z size specifies the size of the shape **not** including the applied texture (in the same way that other textured objects work).
|
// The Z size specifies the size of the shape **not** including the applied texture (in the same way that other textured objects work).
|
||||||
// If you omit the Z value then for regular textures, the default thickness will be 0.1 which provides a thin backing layer. A zero thickness
|
// If you omit the Z value then for regular textures, the default thickness will be 0.1 which provides a thin backing layer. A zero thickness
|
||||||
@@ -1374,13 +1377,8 @@ function textured_tile(
|
|||||||
size = is_def(size) ? is_num(size) ? [size,size,1] : point3d(size,1) // We only use the x and y components of size
|
size = is_def(size) ? is_num(size) ? [size,size,1] : point3d(size,1) // We only use the x and y components of size
|
||||||
: [w1,ysize],
|
: [w1,ysize],
|
||||||
|
|
||||||
tex = is_string(texture)? texture(texture,$fn=_tex_fn_default()) : texture,
|
texture = _get_texture(texture, tex_rot),
|
||||||
texture = tex_rot==0? tex
|
|
||||||
: is_vnf(tex)? zrot(tex_rot, cp=[1/2,1/2], p=tex)
|
|
||||||
: tex_rot==180? reverse([for (row=tex) reverse(row)])
|
|
||||||
: tex_rot==270? [for (row=transpose(tex)) reverse(row)]
|
|
||||||
: reverse(transpose(tex)),
|
|
||||||
check_tex = _validate_texture(texture),
|
|
||||||
tex_reps = is_def(tex_reps) ? tex_reps
|
tex_reps = is_def(tex_reps) ? tex_reps
|
||||||
: [round(size.x/tex_size.x), round(size.y/tex_size.y)],
|
: [round(size.x/tex_size.x), round(size.y/tex_size.y)],
|
||||||
scale = [size.x/tex_reps.x, size.y/tex_reps.y],
|
scale = [size.x/tex_reps.x, size.y/tex_reps.y],
|
||||||
@@ -1991,6 +1989,8 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
|
|||||||
// rounding at the bottom. You can specify extra height at either end for use with difference(); the extra height is ignored by
|
// rounding at the bottom. You can specify extra height at either end for use with difference(); the extra height is ignored by
|
||||||
// anchoring.
|
// anchoring.
|
||||||
// .
|
// .
|
||||||
|
// You can apply a texture to the cylinder using the usual texture parameters.
|
||||||
|
// See [Texturing](skin.scad#section-texturing) for more details on how textures work.
|
||||||
// When creating a textured cylinder, the number of facets is determined by the sampling of the texture. Any `$fn`, `$fa` or `$fs` values in
|
// When creating a textured cylinder, the number of facets is determined by the sampling of the texture. Any `$fn`, `$fa` or `$fs` values in
|
||||||
// effect are ignored. To create a textured prism with a specified number of flat facets use {{regular_prism()}}. Anchors for cylinders
|
// effect are ignored. To create a textured prism with a specified number of flat facets use {{regular_prism()}}. Anchors for cylinders
|
||||||
// appear on the ideal cylinder, not on actual discretized shape the module produces. For anchors on the shape surface, use {{regular_prism()}}.
|
// appear on the ideal cylinder, not on actual discretized shape the module produces. For anchors on the shape surface, use {{regular_prism()}}.
|
||||||
|
243
skin.scad
243
skin.scad
@@ -3150,7 +3150,7 @@ function associate_vertices(polygons, split, curpoly=0) =
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// DefineHeader(Table;Headers=Texture Name|Type|Description): Texture Values
|
|
||||||
|
|
||||||
// Section: Texturing
|
// Section: Texturing
|
||||||
// Some operations are able to add texture to the objects they create. A texture can be any regularly repeated variation in the height of the surface.
|
// Some operations are able to add texture to the objects they create. A texture can be any regularly repeated variation in the height of the surface.
|
||||||
@@ -4036,6 +4036,29 @@ function _validate_texture(texture) =
|
|||||||
true;
|
true;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function _tex_height(scale, inset, z) = scale<0 ? -(1-z - inset) * scale
|
||||||
|
: (z - inset) * scale;
|
||||||
|
|
||||||
|
function _get_texture(texture, tex_rot, extra_rot=0) =
|
||||||
|
let(
|
||||||
|
tex_rot=!is_bool(tex_rot)? tex_rot
|
||||||
|
: echo("boolean value for tex_rot is deprecated. Use a numerical angle divisible by 90.") tex_rot?90:0
|
||||||
|
)
|
||||||
|
assert(is_num(tex_rot) && posmod(tex_rot,90)==0, "tex_rot must be a multiple of 90 degrees")
|
||||||
|
let(
|
||||||
|
tex = is_string(texture)? texture(texture,$fn=_tex_fn_default()) : texture,
|
||||||
|
check_tex = _validate_texture(tex),
|
||||||
|
tex_rot = posmod(tex_rot+extra_rot,360),
|
||||||
|
)
|
||||||
|
tex_rot==0 ? tex
|
||||||
|
: is_vnf(tex)? zrot(tex_rot, cp=[1/2,1/2], p=tex)
|
||||||
|
: tex_rot==180? reverse([for (row=tex) reverse(row)])
|
||||||
|
: tex_rot==270? [for (row=transpose(tex)) reverse(row)]
|
||||||
|
: reverse(transpose(tex));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _textured_linear_sweep(
|
function _textured_linear_sweep(
|
||||||
region, texture, tex_size=[5,5],
|
region, texture, tex_size=[5,5],
|
||||||
h, counts, inset=false, rot=0,
|
h, counts, inset=false, rot=0,
|
||||||
@@ -4065,14 +4088,8 @@ function _textured_linear_sweep(
|
|||||||
|
|
||||||
caps = is_bool(caps) ? [caps,caps] : caps,
|
caps = is_bool(caps) ? [caps,caps] : caps,
|
||||||
regions = is_path(region,2)? [[region]] : region_parts(region),
|
regions = is_path(region,2)? [[region]] : region_parts(region),
|
||||||
tex = is_string(texture)? texture(texture,$fn=_tex_fn_default()) : texture,
|
texture = _get_texture(texture, tex_rot),
|
||||||
dummy = assert(is_undef(samples) || is_vnf(tex), "You gave the tex_samples argument with a heightfield texture, which is not permitted. Use the n= argument to texture() instead"),
|
dummy = assert(is_undef(samples) || is_vnf(texture), "You gave the tex_samples argument with a heightfield texture, which is not permitted. Use the n= argument to texture() instead"),
|
||||||
dummy2=is_bool(rot)?echo("boolean value for tex_rot is deprecated. Use a numerical angle, one of 0, 90, 180, or 270.")0:0,
|
|
||||||
texture = !rot? tex :
|
|
||||||
is_vnf(tex)? zrot(is_num(rot)?rot:90, cp=[1/2,1/2], p=tex) :
|
|
||||||
rot==180? reverse([for (row=tex) reverse(row)]) :
|
|
||||||
rot==270? [for (row=transpose(tex)) reverse(row)] :
|
|
||||||
reverse(transpose(tex)),
|
|
||||||
h = first_defined([h, l, height, length, 1]),
|
h = first_defined([h, l, height, length, 1]),
|
||||||
inset = is_num(inset)? inset : inset? 1 : 0,
|
inset = is_num(inset)? inset : inset? 1 : 0,
|
||||||
twist = default(twist, 0),
|
twist = default(twist, 0),
|
||||||
@@ -4081,7 +4098,6 @@ function _textured_linear_sweep(
|
|||||||
is_num(scale)? [scale,scale,1] : scale,
|
is_num(scale)? [scale,scale,1] : scale,
|
||||||
samples = !is_vnf(texture)? len(texture[0]) :
|
samples = !is_vnf(texture)? len(texture[0]) :
|
||||||
is_num(samples)? samples : 8,
|
is_num(samples)? samples : 8,
|
||||||
check_tex = _validate_texture(texture),
|
|
||||||
vnf_tile =
|
vnf_tile =
|
||||||
!is_vnf(texture) || samples==1 ? texture
|
!is_vnf(texture) || samples==1 ? texture
|
||||||
:
|
:
|
||||||
@@ -4352,15 +4368,8 @@ function _textured_revolution(
|
|||||||
)
|
)
|
||||||
assert(closed || is_path(shape,2))
|
assert(closed || is_path(shape,2))
|
||||||
let(
|
let(
|
||||||
tex = is_string(texture)? texture(texture,$fn=_tex_fn_default()) : texture,
|
texture = _get_texture(texture, tex_rot),
|
||||||
dummy = assert(is_undef(samples) || is_vnf(tex), "You gave the tex_samples argument with a heightfield texture, which is not permitted. Use the n= argument to texture() instead"),
|
dummy = assert(is_undef(samples) || is_vnf(tex), "You gave the tex_samples argument with a heightfield texture, which is not permitted. Use the n= argument to texture() instead"),
|
||||||
dummy2=is_bool(rot)?echo("boolean value for tex_rot is deprecated. Use a numerical angle, one of 0, 90, 180, or 270.")0:0,
|
|
||||||
texture = !rot? tex :
|
|
||||||
is_vnf(tex)? zrot(is_num(rot)?rot:90, cp=[1/2,1/2], p=tex) :
|
|
||||||
rot==180? reverse([for (row=tex) reverse(row)]) :
|
|
||||||
rot==270? [for (row=transpose(tex)) reverse(row)] :
|
|
||||||
reverse(transpose(tex)),
|
|
||||||
check_tex = _validate_texture(texture),
|
|
||||||
inset = is_num(inset)? inset : inset? 1 : 0,
|
inset = is_num(inset)? inset : inset? 1 : 0,
|
||||||
samples = !is_vnf(texture)? len(texture) :
|
samples = !is_vnf(texture)? len(texture) :
|
||||||
is_num(samples)? samples : 8,
|
is_num(samples)? samples : 8,
|
||||||
@@ -4620,5 +4629,203 @@ module _textured_revolution(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _texture_point_array(points, texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0, triangulate=false,
|
||||||
|
col_wrap=false, tex_depth=1, row_wrap=false, caps, cap1, cap2, reverse=false, style="min_edge", tex_extra, tex_skip, sidecaps,sidecap1,sidecap2) =
|
||||||
|
assert(tex_reps==undef || is_vector(tex_reps,2))
|
||||||
|
assert(tex_size==undef || is_num(tex_size) || is_vector(tex_size,2), "tex_size must be a scalar or 2-vector")
|
||||||
|
assert(num_defined([tex_size, tex_reps])<2, "Cannot give both tex_size and tex_reps")
|
||||||
|
assert(in_list(style,["default","alt","quincunx", "convex","concave", "min_edge","min_area","flip1","flip2"]))
|
||||||
|
assert(is_matrix(points[0], n=3),"Point array has the wrong shape or points are not 3d")
|
||||||
|
assert(is_consistent(points), "Non-rectangular or invalid point array")
|
||||||
|
let(
|
||||||
|
cap1 = first_defined([cap1,caps,false]),
|
||||||
|
cap2 = first_defined([cap2,caps,false]),
|
||||||
|
sidecap1 = first_defined([sidecap1,sidecaps,false]),
|
||||||
|
sidecap2 = first_defined([sidecap2,sidecaps,false]),
|
||||||
|
tex_inset = is_num(tex_inset)? tex_inset : tex_inset? 1 : 0,
|
||||||
|
texture = _get_texture(texture, tex_rot),
|
||||||
|
dummy = assert(is_undef(tex_samples) || is_vnf(texture),
|
||||||
|
"You gave the tex_samples argument with a heightfield texture, which is not permitted. Use the n= argument to texture() instead"),
|
||||||
|
ptsize=[len(points[0]), len(points)],
|
||||||
|
tex_reps = is_def(tex_reps) ? tex_reps
|
||||||
|
: let(
|
||||||
|
tex_size = is_undef(tex_sizes) ? [5,5] : force_list(tex_size,2),
|
||||||
|
xsize = norm(points[0][0]-points[0][1])*(ptsize.x+(col_wrap?1:0)),
|
||||||
|
ysize = norm(points[0][0]-points[1][0])*(ptsize.y+(row_wrap?1:0))
|
||||||
|
)
|
||||||
|
[round(xsize/tex_size.x), round(ysize/tex_size.y)],
|
||||||
|
normals = surfnormals(points, col_wrap=col_wrap, row_wrap=row_wrap),
|
||||||
|
getscale = function(x,y) (x+y)/2
|
||||||
|
)
|
||||||
|
!is_vnf(texture) ? // heightmap case
|
||||||
|
let(
|
||||||
|
extra = is_def(tex_extra) ? force_list(tex_extra,2)
|
||||||
|
: [col_wrap?0:1, row_wrap?0:1],
|
||||||
|
skip = is_def(tex_skip) ? force_list(tex_skip,2) : [0,0],
|
||||||
|
texsize = [len(texture[0]), len(texture)],
|
||||||
|
fullsize = [texsize.x*tex_reps.x+extra.x-skip.x, texsize.y*tex_reps.y+extra.y-skip.y],
|
||||||
|
res_points = resample(points,fullsize, col_wrap=col_wrap, row_wrap=row_wrap),
|
||||||
|
res_normals=resample(normals,fullsize, col_wrap=col_wrap, row_wrap=row_wrap),
|
||||||
|
local_scale = [for(y=[0:1:fullsize.y-1])
|
||||||
|
[for(x=[0:1:fullsize.x-1])
|
||||||
|
let(
|
||||||
|
xlen = [
|
||||||
|
if(x>0 || col_wrap) norm(res_points[y][x] - select(res_points[y], x-1)),
|
||||||
|
if(x<fullsize.x-1 || col_wrap) norm(res_points[y][x] - select(res_points[y], x+1))
|
||||||
|
],
|
||||||
|
ylen = [
|
||||||
|
if(y>0 || row_wrap) norm(res_points[y][x] - select(res_points,y-1)[x]),
|
||||||
|
if(y<fullsize.y-1 || row_wrap) norm(res_points[y][x] - select(res_points,y+1)[x])
|
||||||
|
]
|
||||||
|
)
|
||||||
|
getscale(mean(xlen),mean(ylen))
|
||||||
|
]
|
||||||
|
],
|
||||||
|
tex_surf =
|
||||||
|
[for(y=[0:1:fullsize.y-1])
|
||||||
|
[for(x=[0:1:fullsize.x-1])
|
||||||
|
let(yind = (y+skip.y)%texsize.y,
|
||||||
|
xind = (x+skip.x)%texsize.x,
|
||||||
|
)
|
||||||
|
res_points[y][x] + _tex_height(tex_depth,tex_inset,texture[yind][xind]) * res_normals[y][x]*(reverse?-1:1)*local_scale[y][x]/local_scale[0][0]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style, caps=caps, triangulate=triangulate)
|
||||||
|
: // VNF case
|
||||||
|
let(
|
||||||
|
local_scale = [for(y=[-1:1:ptsize.y-1])
|
||||||
|
[for(x=[-1:1:ptsize.x-1])
|
||||||
|
((!col_wrap && (x<0 || x==ptsize.x-1))
|
||||||
|
|| (!row_wrap && (y<0 || y==ptsize.y-1))) ? undef
|
||||||
|
: let(
|
||||||
|
dx = [norm(select(select(points,y),x) - select(select(points,y),x+1)),
|
||||||
|
norm(select(select(points,y+1),x) - select(select(points,y+1),x+1))],
|
||||||
|
dy = [norm(select(select(points,y),x) - select(select(points,y+1),x)),
|
||||||
|
norm(select(select(points,y),x+1) - select(select(points,y+1),x+1))]
|
||||||
|
)
|
||||||
|
getscale(mean(dx),mean(dy))]],
|
||||||
|
samples = default(tex_samples,8),
|
||||||
|
vnf = samples==1? texture :
|
||||||
|
let(
|
||||||
|
s = 1 / samples,
|
||||||
|
slice_us = list([s:s:1-s/2]),
|
||||||
|
vnft1 = vnf_slice(texture, "X", slice_us),
|
||||||
|
vnft = vnf_slice(vnft1, "Y", slice_us),
|
||||||
|
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,
|
||||||
|
yedge_paths = !row_wrap ? _tile_edge_path_list(vnf,1) : undef,
|
||||||
|
xedge_paths = !col_wrap ? _tile_edge_path_list(vnf,0) : undef,
|
||||||
|
trans_pt = function(x,y,pt)
|
||||||
|
let(
|
||||||
|
tileindx = x+pt.x,
|
||||||
|
tileindy = y+(1-pt.y),
|
||||||
|
refx = tileindx/tex_reps.x*(ptsize.x-(col_wrap?0:1)),
|
||||||
|
refy = tileindy/tex_reps.y*(ptsize.y-(row_wrap?0:1)),
|
||||||
|
xind = floor(refx),
|
||||||
|
yind = floor(refy),
|
||||||
|
xfrac = refx-xind,
|
||||||
|
yfrac = refy-yind,
|
||||||
|
corners = [points[yind%ptsize.y][xind%ptsize.x], points[(yind+1)%ptsize.y][xind%ptsize.x],
|
||||||
|
points[yind%ptsize.y][(xind+1)%ptsize.x], points[(yind+1)%ptsize.y][(xind+1)%ptsize.x]],
|
||||||
|
base = bilerp(corners,xfrac, yfrac),
|
||||||
|
scale_list = xfrac==0 && yfrac==0 ? [local_scale[yind][xind], local_scale[yind][xind+1], local_scale[yind+1][xind], local_scale[yind+1][xind+1]]
|
||||||
|
: xfrac==0 ? [local_scale[yind+1][xind], local_scale[yind+1][xind+1]]
|
||||||
|
: yfrac==0 ? [local_scale[yind][xind+1], local_scale[yind+1][xind+1]]
|
||||||
|
: [ local_scale[yind+1][xind+1]],
|
||||||
|
scale = mean([for(s=scale_list) if (is_def(s)) s])/local_scale[1][1],
|
||||||
|
normal = bilerp([normals[yind%ptsize.y][xind%ptsize.x], normals[(yind+1)%ptsize.y][xind%ptsize.x],
|
||||||
|
normals[yind%ptsize.y][(xind+1)%ptsize.x], normals[(yind+1)%ptsize.y][(xind+1)%ptsize.x]],
|
||||||
|
xfrac, yfrac)
|
||||||
|
)
|
||||||
|
base + _tex_height(tex_depth,tex_inset,pt.z) * normal*(reverse?-1:1) * scale,
|
||||||
|
fullvnf = vnf_join([
|
||||||
|
for(y=[0:1:tex_reps.y-1], x=[0:1:tex_reps.x-1])
|
||||||
|
[
|
||||||
|
[for(pt=vnf[0]) trans_pt(x,y,pt)],
|
||||||
|
vnf[1]
|
||||||
|
],
|
||||||
|
for(y=[if (cap1) 0, if (cap2) tex_reps.y-1])
|
||||||
|
let(
|
||||||
|
cap_paths = [
|
||||||
|
if (col_wrap && len(yedge_paths[0])>0)
|
||||||
|
[for(x=[0:1:tex_reps.x-1], pt=yedge_paths[0][0])
|
||||||
|
trans_pt(x,y,[pt.x,y?0:1,pt.z])],
|
||||||
|
if (!row_wrap)
|
||||||
|
for(closed_path=yedge_paths[1], x=[0:1:tex_reps.x-1])
|
||||||
|
[for(pt = closed_path) trans_pt(x,y,[pt.x,y?0:1,pt.z])]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
for(path=cap_paths) [path, [count(path,reverse=y==0)]],
|
||||||
|
if (!col_wrap)
|
||||||
|
for(x=[if (sidecap1) 0, if (sidecap2) tex_reps.x-1])
|
||||||
|
let(
|
||||||
|
cap_paths = [for(closed_path=xedge_paths[1], y=[0:1:tex_reps.y-1])
|
||||||
|
[for(pt = closed_path) trans_pt(x,y,[x?1:0,pt.y,pt.z])]]
|
||||||
|
)
|
||||||
|
for(path=cap_paths) [path, [count(path,reverse=x!=0)]]
|
||||||
|
])
|
||||||
|
)
|
||||||
|
reverse ? vnf_reverse_faces(fullvnf) : fullvnf;
|
||||||
|
|
||||||
|
///// These need to be either hidden or documented and placed somewhere.
|
||||||
|
|
||||||
|
|
||||||
|
function bilerp(pts,x,y) =
|
||||||
|
[1,x,y,x*y]*[[1, 0, 0, 0],[-1, 0, 1, 0],[-1,1,0,0],[1,-1,-1,1]]*pts;
|
||||||
|
|
||||||
|
|
||||||
|
function resample(data, size, col_wrap=false, row_wrap=false) =
|
||||||
|
let(
|
||||||
|
xL=len(data[0]),
|
||||||
|
yL=len(data),
|
||||||
|
lastx=xL-(col_wrap?0:1),
|
||||||
|
lasty=yL-(row_wrap?0:1),
|
||||||
|
lastoutx = size.x - (col_wrap?0:1),
|
||||||
|
lastouty = size.y - (row_wrap?0:1),
|
||||||
|
xscale = lastx/lastoutx,
|
||||||
|
yscale = lasty/lastouty
|
||||||
|
)
|
||||||
|
[
|
||||||
|
for(y=[0:1:lastouty])
|
||||||
|
[
|
||||||
|
for(x=[0:1:lastoutx])
|
||||||
|
let(
|
||||||
|
sx = xscale*x,
|
||||||
|
sy = yscale*y,
|
||||||
|
xind=floor(sx),
|
||||||
|
yind=floor(sy)
|
||||||
|
)
|
||||||
|
bilerp([data[yind%yL][xind%xL], data[(yind+1)%yL][xind%xL],
|
||||||
|
data[yind%yL][(xind+1)%xL], data[(yind+1)%yL][(xind+1)%xL]],
|
||||||
|
sx-xind, sy-yind)
|
||||||
|
]
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function surfnormals(data, col_wrap=false, row_wrap=false) =
|
||||||
|
let(
|
||||||
|
rowderivs = [for(y=[0:1:len(data)-1]) path_tangents(data[y],closed=col_wrap)],
|
||||||
|
colderivs = [for(x=[0:1:len(data[0])-1]) path_tangents(column(data,x), closed=row_wrap)]
|
||||||
|
)
|
||||||
|
[for(y=[0:1:len(data)-1])
|
||||||
|
[for(x=[0:1:len(data[0])-1])
|
||||||
|
cross(colderivs[x][y],rowderivs[y][x])]];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
50
vnf.scad
50
vnf.scad
@@ -48,6 +48,23 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
|||||||
// adds a vertex in the center of each quadrilateral and creates four triangles, and the "convex" and "concave" styles
|
// adds a vertex in the center of each quadrilateral and creates four triangles, and the "convex" and "concave" styles
|
||||||
// choose the locally convex/concave subdivision. The "min_area" option creates the triangulation with the minimal area. Degenerate faces
|
// choose the locally convex/concave subdivision. The "min_area" option creates the triangulation with the minimal area. Degenerate faces
|
||||||
// are not included in the output, but if this results in unused vertices they still appear in the output.
|
// are not included in the output, but if this results in unused vertices they still appear in the output.
|
||||||
|
// .
|
||||||
|
// You can apply a texture to the vertex array VNF using the usual texture parameters.
|
||||||
|
// See [Texturing](skin.scad#section-texturing) for more details on how textures work.
|
||||||
|
// The top left corner of the texture tile will be aligned with `points[0][0]`, and the the X and Y directions correspond to `points[y][x]`.
|
||||||
|
// In practice, it is probably easiest to observe the result and apply a suitable texture tile rotation by setting `tex_rot` if the result
|
||||||
|
// is not what you wanted. The reference scale of your point data is also taken from the square at the [0][0] corner. This determines
|
||||||
|
// the meaning of `tex_size` and it also affects the vertical texture scale. The size of the texture tiles will be proportional to the point
|
||||||
|
// spacing of the location where they are placed, so if the points are closer together, you will get small texture elements. The `tex_depth` you
|
||||||
|
// specify will be correct at the `points[0][0]` but will be different at places in the point array where the scale is different. Note that this
|
||||||
|
// differs from {{rotate_sweep()}} which uses a uniform resampling of the curve you specify.
|
||||||
|
// .
|
||||||
|
// The point data for `vnf_vertex_array()` is resampled using bilinear interpolation to match the required point density of the tile count, but the
|
||||||
|
// sampling is based on the grid, not on the distance between points. If you want to
|
||||||
|
// avoid resampling, match the point data to the required point number for your tile count. For height field textures this means
|
||||||
|
// the number of data points must equal the tile count times the number of entries in the tile minus `tex_skip` plus `tex_extra`.
|
||||||
|
// Note that `tex_extra` defaults to 1 along dimensions that are not wrapped. For a VNF tile you need to have the the point
|
||||||
|
// count equal to the tile count times tex_samples, plus one if wrapping is disabled.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// points = A list of vertices to divide into columns and rows.
|
// points = A list of vertices to divide into columns and rows.
|
||||||
// ---
|
// ---
|
||||||
@@ -60,6 +77,18 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
|
|||||||
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", "flip1", "flip2", "min_edge", "min_area", "quincunx", "convex" and "concave".
|
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", "flip1", "flip2", "min_edge", "min_area", "quincunx", "convex" and "concave".
|
||||||
// triangulate = If true, triangulates endcaps to resolve possible CGAL issues. This can be an expensive operation if the endcaps are complex. Default: false
|
// triangulate = If true, triangulates endcaps to resolve possible CGAL issues. This can be an expensive operation if the endcaps are complex. Default: false
|
||||||
// convexity = (module) Max number of times a line could intersect a wall of the shape.
|
// convexity = (module) Max number of times a line could intersect a wall of the shape.
|
||||||
|
// 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 at `points[0][0]`. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]`
|
||||||
|
// tex_reps = If given instead of tex_size, a 2-vector giving the number of texture tile repetitions in the horizontal and vertical directions.
|
||||||
|
// tex_inset = If numeric, lowers the texture into the surface by the specified proportion, e.g. 0.5 would lower it half way into the surface. If `true`, insets by exactly its full depth. Default: `false`
|
||||||
|
// tex_rot = Rotate texture by specified angle, which must be a multiple of 90 degrees. Default: 0
|
||||||
|
// tex_depth = Specify texture depth; if negative, invert the texture. Default: 1.
|
||||||
|
// tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8
|
||||||
|
// tex_extra = number of extra lines of a hightfield texture to add at the end. Can be a scalar or 2-vector to give x and y values. Default: 1
|
||||||
|
// tex_skip = number of lines of a heightfield texture to skip when starting. Can be a scalar or two vector to give x and y values. Default: 0
|
||||||
|
// sidecaps = if `col_wrap==false` this controls whether to cap any floating ends of a VNF tile on the texture. Does not affect the main texture surface. Ignored it doesn't apply. Default: false
|
||||||
|
// sidecap1 = set sidecap only for the `points[][0]` edge of the output
|
||||||
|
// sidecap2 = set sidecap only for the `points[][max]` edge of the output
|
||||||
// cp = (module) Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
// cp = (module) Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
||||||
// anchor = (module) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"`
|
// anchor = (module) Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"`
|
||||||
// spin = (module) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
// spin = (module) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||||
@@ -181,10 +210,15 @@ module vnf_vertex_array(
|
|||||||
reverse=false,
|
reverse=false,
|
||||||
style="default",
|
style="default",
|
||||||
triangulate = false,
|
triangulate = false,
|
||||||
|
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
|
||||||
|
tex_depth=1, tex_extra, tex_skip, sidecaps,sidecap1,sidecap2,
|
||||||
convexity=2, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull")
|
convexity=2, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull")
|
||||||
{
|
{
|
||||||
vnf = vnf_vertex_array(points=points, caps=caps, cap1=cap2, cap2=cap2,
|
vnf = vnf_vertex_array(points=points, caps=caps, cap1=cap2, cap2=cap2,
|
||||||
col_wrap=col_wrap, row_wrap=row_wrap, reverse=reverse, style=style,triangulate=triangulate);
|
col_wrap=col_wrap, row_wrap=row_wrap, reverse=reverse, style=style,triangulate=triangulate,
|
||||||
|
texture=texture, tex_reps=tex_reps, tex_size=tex_size, tex_samples=tex_samples, tex_inset=tex_inset, tex_rot=tex_rot,
|
||||||
|
tex_depth=tex_depth, tex_extra=tex_extra, tex_skip=tex_skip, sidecaps=sidecaps,sidecap1=sidecap1,sidecap2=sidecap2
|
||||||
|
);
|
||||||
vnf_polyhedron(vnf, convexity=2, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") children();
|
vnf_polyhedron(vnf, convexity=2, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") children();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,14 +230,22 @@ function vnf_vertex_array(
|
|||||||
row_wrap=false,
|
row_wrap=false,
|
||||||
reverse=false,
|
reverse=false,
|
||||||
style="default",
|
style="default",
|
||||||
triangulate = false
|
triangulate = false,
|
||||||
|
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
|
||||||
|
tex_depth=1, tex_extra, tex_skip, sidecaps,sidecap1,sidecap2
|
||||||
) =
|
) =
|
||||||
assert(!(any([caps,cap1,cap2]) && !col_wrap), "col_wrap must be true if caps are requested")
|
|
||||||
assert(!(any([caps,cap1,cap2]) && row_wrap), "Cannot combine caps with row_wrap")
|
|
||||||
assert(in_list(style,["default","alt","quincunx", "convex","concave", "min_edge","min_area","flip1","flip2"]))
|
assert(in_list(style,["default","alt","quincunx", "convex","concave", "min_edge","min_area","flip1","flip2"]))
|
||||||
assert(is_matrix(points[0], n=3),"Point array has the wrong shape or points are not 3d")
|
assert(is_matrix(points[0], n=3),"Point array has the wrong shape or points are not 3d")
|
||||||
assert(is_consistent(points), "Non-rectangular or invalid point array")
|
assert(is_consistent(points), "Non-rectangular or invalid point array")
|
||||||
assert(is_bool(triangulate))
|
assert(is_bool(triangulate))
|
||||||
|
is_def(texture) ?
|
||||||
|
_texture_point_array(points=points, texture=texture, tex_reps=tex_reps, tex_size=tex_size,
|
||||||
|
tex_inset=tex_inset, tex_samples=tex_samples, tex_rot=tex_rot,
|
||||||
|
col_wrap=col_wrap, row_wrap=row_wrap, tex_depth=tex_depth, caps=caps, cap1=cap1, cap2=cap2, reverse=reverse,
|
||||||
|
style=style, tex_extra=tex_extra, tex_skip=tex_skip, sidecaps=sidecaps, sidecap1=sidecap1, sidecap2=sidecap2,triangulate=triangulate)
|
||||||
|
:
|
||||||
|
assert(!(any([caps,cap1,cap2]) && !col_wrap), "col_wrap must be true if caps are requested (without texture)")
|
||||||
|
assert(!(any([caps,cap1,cap2]) && row_wrap), "Cannot combine caps with row_wrap (without texture)")
|
||||||
let(
|
let(
|
||||||
pts = flatten(points),
|
pts = flatten(points),
|
||||||
pcnt = len(pts),
|
pcnt = len(pts),
|
||||||
|
Reference in New Issue
Block a user