Merge pull request from revarbat/revarbat_dev

Refactored and modernized heightfield(), enabling functional literal …
This commit is contained in:
Revar Desmera 2021-01-01 01:47:35 -08:00 committed by GitHub
commit 6105480370
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 100 additions and 94 deletions

View file

@ -1689,112 +1689,118 @@ module arced_slot(
}
// Module: heightfield()
// Usage:
// heightfield(heightfield, [size], [bottom]);
// Function&Module: heightfield()
// Usage: As Module
// heightfield(data, <size>, <bottom>, <maxz>, <xrange>, <yrange>, <style>, <convexity>);
// Usage: As Function
// vnf = heightfield(data, <size>, <bottom>, <maxz>, <xrange>, <yrange>, <style>);
// Description:
// Given a regular rectangular 2D grid of scalar values, generates a 3D surface where the height at
// any given point is the scalar value for that position.
// Given a regular rectangular 2D grid of scalar values, or a function literal, generates a 3D
// surface where the height at any given point is the scalar value for that position.
// Arguments:
// heightfield = The 2D rectangular array of heights.
// size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes.
// bottom = The Z coordinate for the bottom of the heightfield object to create. Must be less than the minimum heightfield value. Default: 0
// convexity = Max number of times a line could intersect a wall of the surface being formed.
// data = This is either the 2D rectangular array of heights, or a function literal that takes X and Y arguments.
// size = The [X,Y] size of the surface to create. If given as a scalar, use it for both X and Y sizes. Default: `[100,100]`
// bottom = The Z coordinate for the bottom of the heightfield object to create. Any heights lower than this will be truncated to very slightly above this height. Default: -20
// maxz = The maximum height to model. Truncates anything taller to this height. Default: 99
// xrange = A range of values to iterate X over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1]
// yrange = A range of values to iterate Y over when calculating a surface from a function literal. Default: [-1 : 0.01 : 1]
// style = The style of subdividing the quads into faces. Valid options are "default", "alt", and "quincunx". Default: "default"
// convexity = Max number of times a line could intersect a wall of the surface being formed. Module only. Default: 10
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#orient). Default: `UP`
// Example:
// heightfield(size=[100,100], bottom=-20, heightfield=[
// for (x=[-180:4:180]) [for(y=[-180:4:180]) 10*cos(3*norm([x,y]))]
// heightfield(size=[100,100], bottom=-20, data=[
// for (y=[-180:4:180]) [for(x=[-180:4:180]) 10*cos(3*norm([x,y]))]
// ]);
// Example:
// intersection() {
// heightfield(size=[100,100], heightfield=[
// for (x=[-180:5:180]) [for(y=[-180:5:180]) 10+5*cos(3*x)*sin(3*y)]
// heightfield(size=[100,100], data=[
// for (y=[-180:5:180]) [for(x=[-180:5:180]) 10+5*cos(3*x)*sin(3*y)]
// ]);
// cylinder(h=50,d=100);
// }
module heightfield(heightfield, size=[100,100], bottom=0, convexity=10)
// Example(NORENDER): Heightfield by Function
// fn = function (x,y) 10*sin(x*360)*cos(y*360);
// heightfield(size=[100,100], data=fn);
// Example(NORENDER): Heightfield by Function, with Specific Ranges
// fn = function (x,y) 2*cos(5*norm([x,y]));
// heightfield(size=[100,100], bottom=-20, data=fn, xrange=[-180:2:180], yrange=[-180:2:180]);
module heightfield(data, size=[100,100], xrange=[-1:0.04:1], yrange=[-1:0.04:1], bottom=-20, maxz=100, style="default", convexity=10, anchor=CENTER, spin=0, orient=UP)
{
size = is_num(size)? [size,size] : point2d(size);
dim = array_dim(heightfield);
assert(dim.x!=undef);
assert(dim.y!=undef);
assert(bottom<min(flatten(heightfield)), "bottom must be less than the minimum heightfield value.");
spacing = vdiv(size,dim-[1,1]);
vertices = concat(
[
for (i=[0:1:dim.x-1], j=[0:1:dim.y-1]) let(
pos = [i*spacing.x-size.x/2, j*spacing.y-size.y/2, heightfield[i][j]]
) pos
], [
for (i=[0:1:dim.x-1]) let(
pos = [i*spacing.x-size.x/2, -size.y/2, bottom]
) pos
], [
for (i=[0:1:dim.x-1]) let(
pos = [i*spacing.x-size.x/2, size.y/2, bottom]
) pos
], [
for (j=[0:1:dim.y-1]) let(
pos = [-size.x/2, j*spacing.y-size.y/2, bottom]
) pos
], [
for (j=[0:1:dim.y-1]) let(
pos = [size.x/2, j*spacing.y-size.y/2, bottom]
) pos
]
);
faces = concat(
[
for (i=[0:1:dim.x-2], j=[0:1:dim.y-2]) let(
idx1 = (i+0)*dim.y + j+0,
idx2 = (i+0)*dim.y + j+1,
idx3 = (i+1)*dim.y + j+0,
idx4 = (i+1)*dim.y + j+1
) each [[idx1, idx2, idx4], [idx1, idx4, idx3]]
], [
for (i=[0:1:dim.x-2]) let(
idx1 = dim.x*dim.y,
idx2 = dim.x*dim.y+dim.x+i,
idx3 = idx2+1
) [idx1,idx3,idx2]
], [
for (i=[0:1:dim.y-2]) let(
idx1 = dim.x*dim.y,
idx2 = dim.x*dim.y+dim.x*2+dim.y+i,
idx3 = idx2+1
) [idx1,idx2,idx3]
], [
for (i=[0:1:dim.x-2]) let(
idx1 = (i+0)*dim.y+0,
idx2 = (i+1)*dim.y+0,
idx3 = dim.x*dim.y+i,
idx4 = idx3+1
) each [[idx1, idx2, idx4], [idx1, idx4, idx3]]
], [
for (i=[0:1:dim.x-2]) let(
idx1 = (i+0)*dim.y+dim.y-1,
idx2 = (i+1)*dim.y+dim.y-1,
idx3 = dim.x*dim.y+dim.x+i,
idx4 = idx3+1
) each [[idx1, idx4, idx2], [idx1, idx3, idx4]]
], [
for (j=[0:1:dim.y-2]) let(
idx1 = j,
idx2 = j+1,
idx3 = dim.x*dim.y+dim.x*2+j,
idx4 = idx3+1
) each [[idx1, idx4, idx2], [idx1, idx3, idx4]]
], [
for (j=[0:1:dim.y-2]) let(
idx1 = (dim.x-1)*dim.y+j,
idx2 = idx1+1,
idx3 = dim.x*dim.y+dim.x*2+dim.y+j,
idx4 = idx3+1
) each [[idx1, idx2, idx4], [idx1, idx4, idx3]]
]
);
polyhedron(points=vertices, faces=faces, convexity=convexity);
vnf = heightfield(data=data, size=size, xrange=xrange, yrange=yrange, bottom=bottom, maxz=maxz, style=style);
attachable(anchor,spin,orient, vnf=vnf) {
vnf_polyhedron(vnf, convexity=convexity);
children();
}
}
function heightfield(data, size=[100,100], xrange=[-1:0.04:1], yrange=[-1:0.04:1], bottom=-20, maxz=100, style="default", anchor=CENTER, spin=0, orient=UP) =
assert(is_list(data) || is_function(data))
let(
size = is_num(size)? [size,size] : point2d(size),
xvals = is_list(data)
? [for (i=idx(data[0])) i]
: assert(is_list(xrange)||is_range(xrange)) [for (x=xrange) x],
yvals = is_list(data)
? [for (i=idx(data)) i]
: assert(is_list(yrange)||is_range(yrange)) [for (y=yrange) y],
xcnt = len(xvals),
minx = min(xvals),
maxx = max(xvals),
ycnt = len(yvals),
miny = min(yvals),
maxy = max(yvals),
verts = is_list(data) ? [
for (y = [0:1:ycnt-1]) [
for (x = [0:1:xcnt-1]) [
size.x * (x/(xcnt-1)-0.5),
size.y * (y/(ycnt-1)-0.5),
data[y][x]
]
]
] : [
for (y = yrange) [
for (x = xrange) let(
z = data(x,y)
) [
size.x * ((x-minx)/(maxx-minx)-0.5),
size.y * ((y-miny)/(maxy-miny)-0.5),
min(maxz, max(bottom+0.1, default(z,0)))
]
]
],
vnf = vnf_merge([
vnf_vertex_array(verts, style=style, reverse=true),
vnf_vertex_array([
verts[0],
[for (v=verts[0]) [v.x, v.y, bottom]],
]),
vnf_vertex_array([
[for (v=verts[ycnt-1]) [v.x, v.y, bottom]],
verts[ycnt-1],
]),
vnf_vertex_array([
[for (r=verts) let(v=r[0]) [v.x, v.y, bottom]],
[for (r=verts) let(v=r[0]) v],
]),
vnf_vertex_array([
[for (r=verts) let(v=r[xcnt-1]) v],
[for (r=verts) let(v=r[xcnt-1]) [v.x, v.y, bottom]],
]),
vnf_vertex_array([
[
for (v=verts[0]) [v.x, v.y, bottom],
for (r=verts) let(v=r[xcnt-1]) [v.x, v.y, bottom],
], [
for (r=verts) let(v=r[0]) [v.x, v.y, bottom],
for (v=verts[ycnt-1]) [v.x, v.y, bottom],
]
])
])
) reorient(anchor,spin,orient, vnf=vnf, p=vnf);
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -8,7 +8,7 @@
//////////////////////////////////////////////////////////////////////
BOSL_VERSION = [2,0,500];
BOSL_VERSION = [2,0,504];
// Section: BOSL Library Version Functions