textured_tile bugfix & texture support for sweeps

This commit is contained in:
Adrian Mariano
2025-04-12 22:05:16 -04:00
parent 5a13c7126f
commit 1faf2612aa
5 changed files with 167 additions and 78 deletions

View File

@@ -899,8 +899,6 @@ function _rounded_arc(radius, rounding=0, angle, n) =
let(
rounding = force_list(rounding,3),
dir = sign(angle),
// inner_corner_radius = abs(angle)==180?0 : abs(angle)>180 ? -dir*rounding[0] : dir*rounding[0],
inner_corner_radius = abs(angle)>180 ? -dir*rounding[0] : dir*rounding[0],
arc1_opt_radius = radius - rounding[1],
arc2_opt_radius = radius - rounding[2],

View File

@@ -1250,36 +1250,6 @@ function regular_prism(n,
// textured_tile("trunc_ribs", 10, tex_reps=[5,2], tex_skip=1);
// This is like _tile_edge_path_list in that it finds the paths
// on the x=0 or y=0 face of a VNF tile. But instead of returning
// paths it turns them into a VNF. The open path is closed to the specified
// z coordinate.
function _tile_edge_vnf(vnf, axis, z, maxopen=1) =
let(
verts = vnf[0],
faces = vnf[1],
segs = [for(face=faces, edge=pair(select(verts,face),wrap=true)) if (approx(edge[0][axis],0) && approx(edge[1][axis],0)) [edge[1],edge[0]]],
paths = _assemble_partial_paths(segs),
facelist = [
for(path=paths)
if (!(len(path)<=3 && path[0]==last(path)))
path[0]==len(path) ? [list_unwrap(path),[count(len(path)-1)]]
: [
[
point3d(point2d(path[0]),z),
each path,
point3d(point2d(last(path)),z)
],
[count(len(path)+2)]
]
],
openlist = [for(entry=facelist) if (!are_ends_equal(entry[0])) 1]
)
assert(len(openlist)<=maxopen, str("VNF has ",len(openlist)," open paths on an edge and at most ",maxopen," is supported."))
vnf_join(facelist);
module textured_tile(
texture,
size,
@@ -1382,6 +1352,7 @@ function textured_tile(
tex_reps = is_def(tex_reps) ? tex_reps
: [round(size.x/tex_size.x), round(size.y/tex_size.y)],
scale = [size.x/tex_reps.x, size.y/tex_reps.y],
setz=function (v,z) [v.x,v.y,z],
vnf = !is_vnf(texture) ?
let(
texsteps = [len(texture[0]), len(texture)],
@@ -1412,18 +1383,57 @@ function textured_tile(
],
scaled_vnf = scale(scale, zadj_vnf),
tiled_vnf = [for(i=[0:1:tex_reps.x-1], j=[0:1:tex_reps.y-1]) move([scale.x*i,scale.y*j], scaled_vnf)],
unscaled_hedge=_tile_edge_vnf(zadj_vnf,1,-height/2),
hedge = unscaled_hedge==EMPTY_VNF ? [] : xscale(scale.x, unscaled_hedge),
unscaled_vedge=_tile_edge_vnf(zadj_vnf,0,-height/2),
vedge = unscaled_vedge==EMPTY_VNF ? [] : yscale(scale.y, unscaled_vedge),
hedge_flip = hedge==[] ? hedge : back(size.y,vnf_reverse_faces(hedge)),
vedge_flip = vedge==[] ? vedge : right(size.x,vnf_reverse_faces(vedge)),
front_edge = hedge==[] ? [] : [for(i=[0:1:tex_reps.x-1]) xmove(scale.x*i, hedge)],
left_edge = vedge==[] ? [] : [for(j=[0:1:tex_reps.y-1]) ymove(scale.y*j, vedge)],
back_edge = hedge==[] ? [] : [for(i=[0:1:tex_reps.x-1]) xmove(scale.x*i, hedge_flip)],
right_edge = vedge==[] ? [] : [for(j=[0:1:tex_reps.y-1]) ymove(scale.y*j, vedge_flip)],
yedge_list = _tile_edge_path_list(zadj_vnf, 0),
xedge_list = _tile_edge_path_list(zadj_vnf, 1),
front_back_closed = [for(i=[0:1:tex_reps.x-1], cpath=xedge_list[1])
each [[xscale(scale.x,xmove(i,cpath)), [count(cpath)]],
[xscale(scale.x,move([i,size.y],cpath)),[count(cpath,reverse=true)]]]],
sides_closed = [for(j=[0:1:tex_reps.y-1], cpath=yedge_list[1])
each [[yscale(scale.y,ymove(j,cpath)), [count(cpath)]],
[yscale(scale.y,move([size.x, j], cpath)),[count(cpath,reverse=true)]]]],
leftpath = yedge_list[0]==[] ? []
: deduplicate([for(j=[0:1:tex_reps.y-1]) each reverse(yscale(scale.y,ymove(j,yedge_list[0][0])))]),
frontpath = xedge_list[0]==[] ? []
: deduplicate([for(i=[0:1:tex_reps.x-1]) each xscale(scale.x,xmove(i,xedge_list[0][0]))]),
base = frontpath==[] || leftpath==[] ? []
: [
[
[setz(frontpath[0],-height/2),
each frontpath,
setz(last(frontpath), -height/2)
],
[count(len(frontpath)+2)]
],
[
[setz(last(leftpath),-height/2),
each reverse(leftpath),
setz(leftpath[0], -height/2)
],
[count(len(leftpath)+2)]
],
[
back(size.y,
[setz(last(frontpath),-height/2),
each reverse(frontpath),
setz(frontpath[0],-height/2)
]),
[count(len(frontpath)+2)]
],
[right(size.x,
[setz(leftpath[0],-height/2),
each leftpath,
setz(last(leftpath),-height/2)
]),
[count(len(leftpath)+2)]
]
],
bottom = [path3d(rect(point2d(size),anchor=FWD+LEFT),-height/2), [[3,2,1,0]]],
result = vnf_join(concat(tiled_vnf, front_edge, left_edge, right_edge, back_edge, [bottom]))
result = vnf_join(concat(tiled_vnf,front_back_closed, sides_closed,base,[bottom]))
)
move([-size.x/2,-size.y/2],result),
trans_vnf = is_undef(h_w1_w2_shift) ? vnf

106
skin.scad
View File

@@ -14,6 +14,7 @@
// FileFootnotes: STD=Included in std.scad
//////////////////////////////////////////////////////////////////////
__vnf_no_n_mesg=" texture is a VNF so it does not accept n. Set sample rate for VNF textures using the tex_samples parameter to cyl(), linear_sweep() or rotate_sweep().";
// Section: Skin and sweep
@@ -1570,6 +1571,11 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// the object's VNF data. The center of the object is determined based on the `cp` argument and can be "centroid" (the default), "mean" to use the mean of the object,
// or "box" to use the center of the bounding box. For complicated objects you may find it difficult to get useful results from the anchoring
// system, which is designed for an object whose center is inside the object. When using an anchors, confirm that it is in the location you desire.
// .
// You can apply a texture to the path sweep object using the usual texture parameters.
// See [Texturing](skin.scad#section-texturing) for more details on how textures work.
// This works by passing through to {{vnf_vertex_array()}}, which also has more details on
// texturing. Note that textures only work when the shape is a path; you cannot apply a texture to a region.
// Arguments:
// shape = A 2D polygon path or region describing the shape to be swept.
// path = 2D or 3D path giving the path to sweep over
@@ -1592,6 +1598,15 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// width = the width of lines used for profile display. (module only) Default: 1
// transforms = set to true to return transforms instead of a VNF. These transforms can be manipulated and passed to sweep(). (function only) Default: false.
// convexity = convexity parameter for polyhedron(). (module only) Default: 10
// 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
// 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
@@ -1908,9 +1923,14 @@ module spiral_sweep(poly, h, r, turns=1, taper, r1, r2, d, d1, d2, internal=fals
// attach("end") stroke([path3d(yscale(1.5,shape))],width=.5);
// }
module path_sweep(shape, path, method="incremental", normal, closed, twist=0, twist_by_length=true, scale=1, scale_by_length=true,
symmetry=1, last_normal, tangent, uniform=true, relaxed=false, caps, style="min_edge", convexity=10,
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",profiles=false,width=1)
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",profiles=false,width=1,
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
tex_depth=1, tex_extra, tex_skip)
{
dummy = assert(is_region(shape) || is_path(shape,2), "shape must be a 2D path or region")
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
@@ -1923,7 +1943,9 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
scales = trans_scale[1];
firstscale = is_num(scales[0]) ? 1/scales[0] : [1/scales[0].x, 1/scales[0].y];
lastscale = is_num(last(scales)) ? 1/last(scales) : [1/last(scales).x, 1/last(scales).y];
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, caps=fullcaps,style=style);
vnf = sweep(is_path(shape)?clockwise_polygon(shape):shape, transforms, closed=false, _closed_for_normals=closed, caps=fullcaps,style=style,
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);
shapecent = point3d(centroid(shape));
$sweep_transforms = transforms;
$sweep_scales = scales;
@@ -1957,10 +1979,14 @@ module path_sweep(shape, path, method="incremental", normal, closed, twist=0, tw
function path_sweep(shape, path, method="incremental", normal, closed, twist=0, twist_by_length=true, scale=1, scale_by_length=true,
symmetry=1, last_normal, tangent, uniform=true, relaxed=false, caps, style="min_edge", transforms=false,
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
tex_depth=1, tex_extra, tex_skip,
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",_return_scales=false) =
is_1region(path) ? path_sweep(shape=shape,path=path[0], method=method, normal=normal, closed=default(closed,true),
twist=twist, scale=scale, scale_by_length=scale_by_length, twist_by_length=twist_by_length, symmetry=symmetry, last_normal=last_normal,
tangent=tangent, uniform=uniform, relaxed=relaxed, caps=caps, style=style, transforms=transforms,
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
tex_depth=1, tex_extra, tex_skip,
anchor=anchor, cp=cp, spin=spin, orient=orient, atype=atype, _return_scales=_return_scales) :
let(closed=default(closed,false))
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
@@ -2117,7 +2143,11 @@ function path_sweep(shape, path, method="incremental", normal, closed, twist=0,
? [transform_list,scale]
: transforms ? transform_list
: sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style,
anchor=anchor,cp=cp,spin=spin,orient=orient,atype=atype);
anchor=anchor,cp=cp,spin=spin,orient=orient,atype=atype,
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,
_closed_for_normals=closed
);
// Function&Module: path_sweep2d()
@@ -2276,6 +2306,12 @@ function _ofs_face_edge(face,firstlen,second=false) =
// overlooked self-intersection. Note also that the errors will not occur when your shape is alone
// in your model, but will arise if you add a second object to the model. This may mislead you into
// thinking the second object caused a problem. Even adding a simple cube to the model will reveal the problem.
// .
// You can apply a texture to the sweep object using the usual texture parameters.
// See [Texturing](skin.scad#section-texturing) for more details on how textures work.
// This works by passing through to {{vnf_vertex_array()}}, which also has more details on
// texturing. Note that textures only work when the shape is a path; you cannot apply a texture to a region.
// The texture tiles are oriented on the path sweep so that the Y axis of the tile is aligned with the sweep direction.
// Arguments:
// shape = 2d path or region, describing the shape to be swept.
// transforms = list of 4x4 matrices to apply
@@ -2284,6 +2320,15 @@ function _ofs_face_edge(face,firstlen,second=false) =
// style = vnf_vertex_array style. Default: "min_edge"
// ---
// convexity = convexity setting for use with polyhedron. (module only) Default: 10
// 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
// 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"
@@ -2306,7 +2351,7 @@ function _ofs_face_edge(face,firstlen,second=false) =
// function rotate(t) = 180 * pow((1 - t), 3);
// step = 0.01;
// path_transforms = [for (t=[0:step:1-step]) translate(path(t)) * zrot(rotate(t)) * scale([drop(t), drop(t), 1])];
// sweep(circle(1, $fn=12), path_transforms);
// sweep(reverse(circle(1, $fn=12)), path_transforms);
// Example: Another example from list-comprehension-demos
// function f(x) = 3 - 2.5 * x;
// function r(x) = 2 * 180 * x * x * x;
@@ -2320,9 +2365,19 @@ function _ofs_face_edge(face,firstlen,second=false) =
// outside = [for(i=[0:24]) up(i)*rot(i)*scale(1.25*i/24+1)];
// inside = [for(i=[24:-1:2]) up(i)*rot(i)*scale(1.2*i/24+1)];
// sweep(shape, concat(outside,inside));
// Example: "sweet-drop" with a dots texture
// function drop(t) = 100 * 0.5 * (1 - cos(180 * t)) * sin(180 * t) + 1;
// function path(t) = [0, 0, 80 + 80 * cos(180 * t)];
// function rotate(t) = 180 * pow((1 - t), 3);
// step = 0.01;
// path_transforms = [for (t=[0:step:1-step]) translate(path(t)) * zrot(rotate(t)) * scale([drop(t), drop(t), 1])];
// sweep(reverse(circle(1, $fn=12)), path_transforms, texture="dots", tex_reps=[12,12],tex_depth=.1);
function sweep(shape, transforms, closed=false, caps, style="min_edge",
anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull") =
anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull",
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
tex_depth=1, tex_extra, tex_skip, _closed_for_normals=false) =
assert(is_consistent(transforms, ident(4)), "Input transforms must be a list of numeric 4x4 matrices in sweep")
assert(is_path(shape,2) || is_region(shape), "Input shape must be a 2d path or a region.")
let(
@@ -2334,7 +2389,9 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
assert(len(transforms)>=2, "transformation must be length 2 or more")
assert(capsOK, "caps must be boolean or a list of two booleans")
assert(!closed || !caps, "Cannot make closed shape with caps")
is_region(shape)? let(
is_region(shape)?
assert(is_undef(texture), "textures are not supported for regions, only paths")
let(
regions = region_parts(shape),
rtrans = reverse(transforms),
vnfs = [
@@ -2346,19 +2403,35 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge",
],
],
vnf = vnf_join(vnfs)
) vnf :
)
vnf
:
assert(len(shape)>=3, "shape must be a path of at least 3 non-colinear points")
vnf_vertex_array([for(i=[0:len(transforms)-(closed?0:1)]) apply(transforms[i%len(transforms)],path3d(shape))],
cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style);
let(
points = [for(i=[0:len(transforms)-(closed?0:1)]) apply(transforms[i%len(transforms)],path3d(shape))],
normals = (!(is_def(texture) && (closed || _closed_for_normals))) ? undef
: let(
n = surfnormals(select(points,0,-2), col_wrap=true, row_wrap=true)
)
[each n, n[0]]
)
vnf_vertex_array(points, normals=normals,
cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style,
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);
module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity=10,
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull")
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull",
texture, tex_reps, tex_size, tex_samples, tex_inset=false, tex_rot=0,
tex_depth=1, tex_extra, tex_skip)
{
$sweep_transforms=transforms;
$sweep_shape=shape;
$sweep_closed=closed;
vnf = sweep(shape, transforms, closed, caps, style);
vnf = sweep(shape, transforms, closed, caps, style,
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);
vnf_polyhedron(vnf, convexity=convexity, anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp)
children();
}
@@ -3563,7 +3636,7 @@ function associate_vertices(polygons, split, curpoly=0) =
function _tex_fn_default() = 16;
__vnf_no_n_mesg=" texture is a VNF so it does not accept n. Set sample rate for VNF textures using the tex_samples parameter to cyl(), linear_sweep() or rotate_sweep().";
function texture(tex, n, border, gap, roughness, inset) =
assert(num_defined([border,inset])<2, "In texture() the 'inset' parameter has been replaced by 'border'. You cannot give both parameters.")
@@ -4630,7 +4703,7 @@ 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) =
col_wrap=false, tex_depth=1, row_wrap=false, caps, cap1, cap2, reverse=false, style="min_edge", tex_extra, tex_skip, sidecaps,sidecap1,sidecap2,normals) =
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")
@@ -4654,7 +4727,7 @@ function _texture_point_array(points, texture, tex_reps, tex_size, tex_samples,
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),
normals = default(normals,surfnormals(points, col_wrap=col_wrap, row_wrap=row_wrap)),
getscale = function(x,y) (x+y)/2
)
!is_vnf(texture) ? // heightmap case
@@ -4691,7 +4764,7 @@ function _texture_point_array(points, texture, tex_reps, tex_size, tex_samples,
]
]
)
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style, caps=caps, triangulate=triangulate)
vnf_vertex_array(tex_surf, row_wrap=row_wrap, col_wrap=col_wrap, reverse=reverse,style=style, caps=caps, cap1=cap1, cap2=cap2, triangulate=triangulate)
: // VNF case
let(
local_scale = [for(y=[-1:1:ptsize.y-1])
@@ -4712,6 +4785,7 @@ function _texture_point_array(points, texture, tex_reps, tex_size, tex_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]) [
@@ -4730,6 +4804,8 @@ function _texture_point_array(points, texture, tex_reps, tex_size, tex_samples,
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),

View File

@@ -134,7 +134,7 @@ module test_make_region(){
22.451398829]], [[69.0983005625, 22.451398829], [50, 36.3271264003],
[80.9016994375, 58.7785252292]], [[61.803398875, 3.5527136788e-15],
[69.0983005625, 22.451398829], [100, 0]], [[38.196601125, 0],
[61.803398875, 3.94430452611e-31], [50, -36.3271264003]]]));
[61.803398875, 3.94430452611e-31], [50, -36.3271264003]]], either_winding=true));
/*assert_approx(region1,
[[[0, 0], [38.196601125, 0], [30.9016994375, 22.451398829]], [[50,
36.3271264003], [19.0983005625, 58.7785252292], [30.9016994375,

View File

@@ -65,6 +65,10 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
// 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.
// .
// For creating the texture, `vnf_vertex_array()` uses normals to the surface that it estimates from the surface data itself.
// If you have more accurate normals or need the normals to take particular values, you can pass an array of normals
// using the `normals` parameter.
// Arguments:
// points = A list of vertices to divide into columns and rows.
// ---
@@ -89,6 +93,7 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
// 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
// normals = array of normal vectors to each point in the point array for more accurate texture height calculation
// 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"`
// spin = (module) Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
@@ -219,7 +224,7 @@ module vnf_vertex_array(
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=convexity, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") children();
}
@@ -232,7 +237,7 @@ function vnf_vertex_array(
style="default",
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
tex_depth=1, tex_extra, tex_skip, sidecaps,sidecap1,sidecap2, normals
) =
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")
@@ -242,7 +247,7 @@ function vnf_vertex_array(
_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)
style=style, tex_extra=tex_extra, tex_skip=tex_skip, sidecaps=sidecaps, sidecap1=sidecap1, sidecap2=sidecap2,normals=normals,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)")