Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Adrian Mariano 2022-05-15 10:35:52 -04:00
commit ee620449b3
4 changed files with 181 additions and 141 deletions

View file

@ -1219,7 +1219,7 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
// offset = If given, offsets the perimeter of the volume around the centerpoint. // offset = If given, offsets the perimeter of the volume around the centerpoint.
// anchors = If given as a list of anchor points, allows named anchor points. // anchors = If given as a list of anchor points, allows named anchor points.
// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) // two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D)
// axis = The vector pointing along the axis of a cylinder geometry. Default: UP // axis = The vector pointing along the axis of a geometry. Default: UP
// geom = If given, uses the pre-defined (via {{attach_geom()}} geometry. // geom = If given, uses the pre-defined (via {{attach_geom()}} geometry.
// //
// Side Effects: // Side Effects:
@ -1385,14 +1385,15 @@ module attachable(
region = !is_undef(region)? region : region = !is_undef(region)? region :
!is_undef(path)? [path] : !is_undef(path)? [path] :
undef; undef;
geom = is_def(geom)? geom : attach_geom( geom = is_def(geom)? geom :
size=size, size2=size2, shift=shift, attach_geom(
r=r, r1=r1, r2=r2, h=h, size=size, size2=size2, shift=shift,
d=d, d1=d1, d2=d2, l=l, r=r, r1=r1, r2=r2, h=h,
vnf=vnf, region=region, extent=extent, d=d, d1=d1, d2=d2, l=l,
cp=cp, offset=offset, anchors=anchors, vnf=vnf, region=region, extent=extent,
two_d=two_d, axis=axis cp=cp, offset=offset, anchors=anchors,
); two_d=two_d, axis=axis
);
m = _attach_transform(anchor,spin,orient,geom); m = _attach_transform(anchor,spin,orient,geom);
multmatrix(m) { multmatrix(m) {
$parent_anchor = anchor; $parent_anchor = anchor;
@ -1499,7 +1500,7 @@ module attachable(
// offset = If given, offsets the perimeter of the volume around the centerpoint. // offset = If given, offsets the perimeter of the volume around the centerpoint.
// anchors = If given as a list of anchor points, allows named anchor points. // anchors = If given as a list of anchor points, allows named anchor points.
// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) // two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D)
// axis = The vector pointing along the axis of a cylinder geometry. Default: UP // axis = The vector pointing along the axis of a geometry. Default: UP
// p = The VNF, path, or point to transform. // p = The VNF, path, or point to transform.
function reorient( function reorient(
anchor, spin, orient, anchor, spin, orient,
@ -1528,14 +1529,15 @@ function reorient(
) )
(anchor==CENTER && spin==0 && orient==UP && p!=undef)? p : (anchor==CENTER && spin==0 && orient==UP && p!=undef)? p :
let( let(
geom = is_def(geom)? geom : attach_geom( geom = is_def(geom)? geom :
size=size, size2=size2, shift=shift, attach_geom(
r=r, r1=r1, r2=r2, h=h, size=size, size2=size2, shift=shift,
d=d, d1=d1, d2=d2, l=l, r=r, r1=r1, r2=r2, h=h,
vnf=vnf, region=region, extent=extent, d=d, d1=d1, d2=d2, l=l,
cp=cp, offset=offset, anchors=anchors, vnf=vnf, region=region, extent=extent,
two_d=two_d, axis=axis cp=cp, offset=offset, anchors=anchors,
), two_d=two_d, axis=axis
),
$attach_to = undef $attach_to = undef
) _attach_transform(anchor,spin,orient,geom,p); ) _attach_transform(anchor,spin,orient,geom,p);
@ -1605,7 +1607,7 @@ function named_anchor(name, pos, orient=UP, spin=0) = [name, pos, orient, spin];
// offset = If given, offsets the perimeter of the volume around the centerpoint. // offset = If given, offsets the perimeter of the volume around the centerpoint.
// anchors = If given as a list of anchor points, allows named anchor points. // anchors = If given as a list of anchor points, allows named anchor points.
// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) // two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D)
// axis = The vector pointing along the axis of a cylinder geometry. Default: UP // axis = The vector pointing along the axis of a geometry. Default: UP
// //
// Example(NORENDER): Cubical Shape // Example(NORENDER): Cubical Shape
// geom = attach_geom(size=size); // geom = attach_geom(size=size);
@ -1697,7 +1699,7 @@ function attach_geom(
assert(is_vector(size,2)) assert(is_vector(size,2))
assert(is_num(size2)) assert(is_num(size2))
assert(is_num(shift)) assert(is_num(shift))
["rect", point2d(size), size2, shift, cp, offset, anchors] ["trapezoid", point2d(size), size2, shift, cp, offset, anchors]
) : ( ) : (
let( let(
size2 = default(size2, point2d(size)), size2 = default(size2, point2d(size)),
@ -1706,7 +1708,7 @@ function attach_geom(
assert(is_vector(size,3)) assert(is_vector(size,3))
assert(is_vector(size2,2)) assert(is_vector(size2,2))
assert(is_vector(shift,2)) assert(is_vector(shift,2))
["cuboid", size, size2, shift, axis, cp, offset, anchors] ["prismoid", size, size2, shift, axis, cp, offset, anchors]
) )
) : !is_undef(vnf)? ( ) : !is_undef(vnf)? (
assert(is_vnf(vnf)) assert(is_vnf(vnf))
@ -1748,11 +1750,11 @@ function attach_geom(
assert(is_num(r2) || is_vector(r2,2)) assert(is_num(r2) || is_vector(r2,2))
assert(is_num(l)) assert(is_num(l))
assert(is_vector(shift,2)) assert(is_vector(shift,2))
["cyl", r1, r2, l, shift, axis, cp, offset, anchors] ["conoid", r1, r2, l, shift, axis, cp, offset, anchors]
) : ( ) : (
two_d? ( two_d? (
assert(is_num(r1) || is_vector(r1,2)) assert(is_num(r1) || is_vector(r1,2))
["circle", r1, cp, offset, anchors] ["ellipse", r1, cp, offset, anchors]
) : ( ) : (
assert(is_num(r1) || is_vector(r1,3)) assert(is_num(r1) || is_vector(r1,3))
["spheroid", r1, cp, offset, anchors] ["spheroid", r1, cp, offset, anchors]
@ -1780,7 +1782,7 @@ function attach_geom(
// Returns true if the given attachment geometry description is for a 2D shape. // Returns true if the given attachment geometry description is for a 2D shape.
function _attach_geom_2d(geom) = function _attach_geom_2d(geom) =
let( type = geom[0] ) let( type = geom[0] )
type == "rect" || type == "circle" || type == "trapezoid" || type == "ellipse" ||
type == "rgn_isect" || type == "rgn_extent"; type == "rgn_isect" || type == "rgn_extent";
@ -1793,14 +1795,14 @@ function _attach_geom_2d(geom) =
// Returns the `[X,Y,Z]` bounding size for the given attachment geometry description. // Returns the `[X,Y,Z]` bounding size for the given attachment geometry description.
function _attach_geom_size(geom) = function _attach_geom_size(geom) =
let( type = geom[0] ) let( type = geom[0] )
type == "cuboid"? ( //size, size2, shift type == "prismoid"? ( //size, size2, shift, axis
let( let(
size=geom[1], size2=geom[2], shift=point2d(geom[3]), size=geom[1], size2=geom[2], shift=point2d(geom[3]),
maxx = max(size.x,size2.x), maxx = max(size.x,size2.x),
maxy = max(size.y,size2.y), maxy = max(size.y,size2.y),
z = size.z z = size.z
) [maxx, maxy, z] ) [maxx, maxy, z]
) : type == "cyl"? ( //r1, r2, l, shift ) : type == "conoid"? ( //r1, r2, l, shift
let( let(
r1=geom[1], r2=geom[2], l=geom[3], r1=geom[1], r2=geom[2], l=geom[3],
shift=point2d(geom[4]), axis=point3d(geom[5]), shift=point2d(geom[4]), axis=point3d(geom[5]),
@ -1831,12 +1833,12 @@ function _attach_geom_size(geom) =
mm = pointlist_bounds(flatten(geom[1])), mm = pointlist_bounds(flatten(geom[1])),
delt = mm[1]-mm[0] delt = mm[1]-mm[0]
) [delt.x, delt.y, geom[2]] ) [delt.x, delt.y, geom[2]]
) : type == "rect"? ( //size, size2 ) : type == "trapezoid"? ( //size, size2
let( let(
size=geom[1], size2=geom[2], shift=geom[3], size=geom[1], size2=geom[2], shift=geom[3],
maxx = max(size.x,size2+abs(shift)) maxx = max(size.x,size2+abs(shift))
) [maxx, size.y] ) [maxx, size.y]
) : type == "circle"? ( //r ) : type == "ellipse"? ( //r
let( r=geom[1] ) let( r=geom[1] )
is_num(r)? [2,2]*r : v_mul([2,2],point2d(r)) is_num(r)? [2,2]*r : v_mul([2,2],point2d(r))
) : type == "rgn_isect" || type == "rgn_extent"? ( //path ) : type == "rgn_isect" || type == "rgn_extent"? ( //path
@ -1982,37 +1984,36 @@ function _find_anchor(anchor, geom) =
type = geom[0] type = geom[0]
) )
assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor)) assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor))
let(anchor = point3d(anchor))
anchor==CENTER? [anchor, cp, UP, 0] :
let( let(
anchor = point3d(anchor),
oang = ( oang = (
approx(point2d(anchor), [0,0])? 0 : approx(point2d(anchor), [0,0])? 0 :
atan2(anchor.y, anchor.x)+90 atan2(anchor.y, anchor.x)+90
) )
) )
type == "cuboid"? ( //size, size2, shift type == "prismoid"? ( //size, size2, shift, axis
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1") assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1")
let( let(
size=geom[1], size2=geom[2], size=geom[1], size2=geom[2],
shift=point2d(geom[3]), axis=point3d(geom[4]), shift=point2d(geom[3]), axis=point3d(geom[4]),
anch = rot(from=axis, to=UP, p=anchor), anch = rot(from=axis, to=UP, p=anchor),
offset = rot(from=axis, to=UP, p=offset),
h = size.z, h = size.z,
u = (anch.z+1)/2, // u is one of 0, 0.5, or 1 u = (anch.z + 1) / 2, // u is one of 0, 0.5, or 1
axy = point2d(anch), axy = point2d(anch),
bot = point3d(v_mul(point2d(size)/2,axy),-h/2), bot = point3d(v_mul(point2d(size )/2, axy), -h/2),
top = point3d(v_mul(point2d(size2)/2,axy)+shift,h/2), top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2),
pos = point3d(cp) + lerp(bot,top,u) + offset, pos = point3d(cp) + lerp(bot,top,u) + offset,
vecs = [ vecs = [
if (anchor.x!=0) unit(rot(from=UP, to=unit([(top-bot).x,0,h]), p=[axy.x,0,0]), UP), if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,h], p=[axy.x,0,0]), UP),
if (anchor.y!=0) unit(rot(from=UP, to=unit([0,(top-bot).y,h]), p=[0,axy.y,0]), UP), if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,h], p=[0,axy.y,0]), UP),
if (anchor.z!=0) anch==CENTER? UP : unit([0,0,anch.z],UP) if (anch.z!=0) anch==CENTER? UP : unit([0,0,anch.z],UP)
], ],
vec = unit(sum(vecs) / len(vecs)), vec = anchor==CENTER? UP : rot(from=UP, to=axis, p=unit(sum(vecs) / len(vecs))),
pos2 = rot(from=UP, to=axis, p=pos), pos2 = rot(from=UP, to=axis, p=pos)
vec2 = rot(from=UP, to=axis, p=vec) ) [anchor, pos2, vec, oang]
) [anchor, pos2, vec2, oang] ) : type == "conoid"? ( //r1, r2, l, shift
) : type == "cyl"? ( //r1, r2, l, shift
assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1") assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1")
let( let(
rr1=geom[1], rr2=geom[2], l=geom[3], rr1=geom[1], rr2=geom[2], l=geom[3],
@ -2020,6 +2021,7 @@ function _find_anchor(anchor, geom) =
r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1), r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1),
r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2), r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2),
anch = rot(from=axis, to=UP, p=anchor), anch = rot(from=axis, to=UP, p=anchor),
offset = rot(from=axis, to=UP, p=offset),
u = (anch.z+1)/2, u = (anch.z+1)/2,
axy = unit(point2d(anch),[0,0]), axy = unit(point2d(anch),[0,0]),
bot = point3d(v_mul(r1,axy), -l/2), bot = point3d(v_mul(r1,axy), -l/2),
@ -2027,12 +2029,12 @@ function _find_anchor(anchor, geom) =
pos = point3d(cp) + lerp(bot,top,u) + offset, pos = point3d(cp) + lerp(bot,top,u) + offset,
sidevec = rot(from=UP, to=top-bot, p=point3d(axy)), sidevec = rot(from=UP, to=top-bot, p=point3d(axy)),
vvec = anch==CENTER? UP : unit([0,0,anch.z],UP), vvec = anch==CENTER? UP : unit([0,0,anch.z],UP),
vec = anch==CENTER? UP : vec = anch==CENTER? CENTER :
approx(axy,[0,0])? unit(anch,UP) : approx(axy,[0,0])? unit(anch,UP) :
approx(anch.z,0)? sidevec : approx(anch.z,0)? sidevec :
unit((sidevec+vvec)/2,UP), unit((sidevec+vvec)/2,UP),
pos2 = rot(from=UP, to=axis, p=pos), pos2 = rot(from=UP, to=axis, p=pos),
vec2 = rot(from=UP, to=axis, p=vec) vec2 = anch==CENTER? UP : rot(from=UP, to=axis, p=vec)
) [anchor, pos2, vec2, oang] ) [anchor, pos2, vec2, oang]
) : type == "spheroid"? ( //r ) : type == "spheroid"? ( //r
let( let(
@ -2043,9 +2045,9 @@ function _find_anchor(anchor, geom) =
vec = unit(v_mul(r,anchor),UP) vec = unit(v_mul(r,anchor),UP)
) [anchor, pos, vec, oang] ) [anchor, pos, vec, oang]
) : type == "vnf_isect"? ( //vnf ) : type == "vnf_isect"? ( //vnf
let( let( vnf=geom[1] )
vnf=geom[1] approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] :
) vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] : vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] :
let( let(
eps = 1/2048, eps = 1/2048,
points = vnf[0], points = vnf[0],
@ -2093,9 +2095,9 @@ function _find_anchor(anchor, geom) =
) )
[anchor, pos, n, oang] [anchor, pos, n, oang]
) : type == "vnf_extent"? ( //vnf ) : type == "vnf_extent"? ( //vnf
let( let( vnf=geom[1] )
vnf=geom[1] approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] :
) vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] : vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
let( let(
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]), rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]),
maxx = max(column(rpts,0)), maxx = max(column(rpts,0)),
@ -2104,7 +2106,7 @@ function _find_anchor(anchor, geom) =
mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep, mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep,
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt) pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
) [anchor, pos, anchor, oang] ) [anchor, pos, anchor, oang]
) : type == "rect"? ( //size, size2, shift ) : type == "trapezoid"? ( //size, size2, shift
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1") assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1")
let( let(
@ -2127,7 +2129,7 @@ function _find_anchor(anchor, geom) =
unit((point3d(svec) + BACK) / 2, BACK) unit((point3d(svec) + BACK) / 2, BACK)
) )
) [anchor, pos, vec, 0] ) [anchor, pos, vec, 0]
) : type == "circle"? ( //r ) : type == "ellipse"? ( //r
let( let(
anchor = unit(_force_anchor_2d(anchor),[0,0]), anchor = unit(_force_anchor_2d(anchor),[0,0]),
r = force_list(geom[1],2), r = force_list(geom[1],2),
@ -2137,17 +2139,20 @@ function _find_anchor(anchor, geom) =
px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y))) px = sign(anchor.x) * sqrt(1/(1/sqr(r.x) + m*m/sqr(r.y)))
) )
[px,m*px], [px,m*px],
vec = unit([r.y/r.x*pos.x, r.x/r.y*pos.y]) vec = unit([r.y/r.x*pos.x, r.x/r.y*pos.y],BACK)
) [anchor, point2d(cp+offset)+pos, vec, 0] ) [anchor, point2d(cp+offset)+pos, vec, 0]
) : type == "rgn_isect"? ( //region ) : type == "rgn_isect"? ( //region
let( let(
anchor = _force_anchor_2d(anchor), anchor = _force_anchor_2d(anchor),
rgn = force_region(move(-point2d(cp), p=geom[1])), rgn = force_region(move(-point2d(cp), p=geom[1]))
)
approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] :
let(
isects = [ isects = [
for (path=rgn, t=triplet(path,true)) let( for (path=rgn, t=triplet(path,true)) let(
seg1 = [t[0],t[1]], seg1 = [t[0],t[1]],
seg2 = [t[1],t[2]], seg2 = [t[1],t[2]],
isect = line_intersection([[0,0],anchor], seg1,RAY,SEGMENT), isect = line_intersection([[0,0],anchor], seg1, RAY, SEGMENT),
n = is_undef(isect)? [0,1] : n = is_undef(isect)? [0,1] :
!approx(isect, t[1])? line_normal(seg1) : !approx(isect, t[1])? line_normal(seg1) :
unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]), unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]),
@ -2164,15 +2169,16 @@ function _find_anchor(anchor, geom) =
vec = unit(isect[2],[0,1]) vec = unit(isect[2],[0,1])
) [anchor, pos, vec, 0] ) [anchor, pos, vec, 0]
) : type == "rgn_extent"? ( //region ) : type == "rgn_extent"? ( //region
let( anchor = _force_anchor_2d(anchor) )
approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] :
let( let(
anchor = _force_anchor_2d(anchor),
rgn = force_region(geom[1]), rgn = force_region(geom[1]),
rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)), rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)),
maxx = max(column(rpts,0)), maxx = max(column(rpts,0)),
ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y], ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y],
midy = (min(ys)+max(ys))/2, midy = (min(ys)+max(ys))/2,
pos = rot(from=RIGHT, to=anchor, p=[maxx,midy]) pos = rot(from=RIGHT, to=anchor, p=[maxx,midy])
) [anchor, pos, unit(anchor), 0] ) [anchor, pos, unit(anchor,BACK), 0]
) : type=="xrgn_extent" || type=="xrgn_isect" ? ( // extruded region ) : type=="xrgn_extent" || type=="xrgn_isect" ? ( // extruded region
assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.")
let( let(
@ -2188,7 +2194,7 @@ function _find_anchor(anchor, geom) =
twmat = zrot(lerp(0, -twist, u)), twmat = zrot(lerp(0, -twist, u)),
mat = shmat * scmat * twmat mat = shmat * scmat * twmat
) )
approx(anchor_xy,[0,0]) ? [anchor, apply(mat, up(anchor.z*L/2,cp)), anchor, oang] : approx(anchor_xy,[0,0]) ? [anchor, apply(mat, up(anchor.z*L/2,cp)), unit(anchor, UP), oang] :
let( let(
newrgn = apply(mat, rgn), newrgn = apply(mat, rgn),
newgeom = attach_geom(two_d=true, region=newrgn, extent=type=="xrgn_extent", cp=cp), newgeom = attach_geom(two_d=true, region=newrgn, extent=type=="xrgn_extent", cp=cp),

View file

@ -392,7 +392,7 @@ module ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER,
ry = r.y * sc; ry = r.y * sc;
attachable(anchor,spin, two_d=true, r=[rx,ry]) { attachable(anchor,spin, two_d=true, r=[rx,ry]) {
if (uniform) { if (uniform) {
assert(!circum, "Circum option not allowed when \"uniform\" is true"); check = assert(!circum, "Circum option not allowed when \"uniform\" is true");
polygon(ellipse(r,realign=realign, circum=circum, uniform=true)); polygon(ellipse(r,realign=realign, circum=circum, uniform=true));
} }
else if (rx < ry) { else if (rx < ry) {
@ -591,7 +591,7 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false,
id = is_finite(id)? id*sc : undef; id = is_finite(id)? id*sc : undef;
side = is_finite(side)? side/2/sin(180/n) : undef; side = is_finite(side)? side/2/sin(180/n) : undef;
r = get_radius(r1=ir, r2=or, r=r, d1=id, d2=od, d=d, dflt=side); r = get_radius(r1=ir, r2=or, r=r, d1=id, d2=od, d=d, dflt=side);
assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side."); check = assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side.");
mat = ( realign? zrot(-180/n) : ident(4) ) * ( mat = ( realign? zrot(-180/n) : ident(4) ) * (
!is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip)) : !is_undef(align_tip)? rot(from=RIGHT, to=point2d(align_tip)) :
!is_undef(align_side)? rot(from=RIGHT, to=point2d(align_side)) * zrot(180/n) : !is_undef(align_side)? rot(from=RIGHT, to=point2d(align_side)) * zrot(180/n) :
@ -828,7 +828,7 @@ function right_triangle(size=[1,1], center, anchor, spin=0) =
module right_triangle(size=[1,1], center, anchor, spin=0) { module right_triangle(size=[1,1], center, anchor, spin=0) {
size = is_num(size)? [size,size] : size; size = is_num(size)? [size,size] : size;
anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]); anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]);
assert(is_vector(size,2)); check = assert(is_vector(size,2));
path = right_triangle(size, center=true); path = right_triangle(size, center=true);
attachable(anchor,spin, two_d=true, size=[size.x,size.y], size2=0, shift=-size.x/2) { attachable(anchor,spin, two_d=true, size=[size.x,size.y], size2=0, shift=-size.x/2) {
polygon(path); polygon(path);
@ -1083,10 +1083,11 @@ function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit
module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, atype="hull") { module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, atype="hull") {
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); checks =
assert(is_undef(align_tip) || is_vector(align_tip)); assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
assert(is_undef(align_pit) || is_vector(align_pit)); assert(is_undef(align_tip) || is_vector(align_tip))
assert(is_undef(align_tip) || is_undef(align_pit), "Can only specify one of align_tip and align_pit"); assert(is_undef(align_pit) || is_vector(align_pit))
assert(is_undef(align_tip) || is_undef(align_pit), "Can only specify one of align_tip and align_pit");
r = get_radius(r1=or, d1=od, r=r, d=d, dflt=undef); r = get_radius(r1=or, d1=od, r=r, d=d, dflt=undef);
stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n); stepr = is_undef(step)? r : r*cos(180*step/n)/cos(180*(step-1)/n);
ir = get_radius(r=ir, d=id, dflt=stepr); ir = get_radius(r=ir, d=id, dflt=stepr);
@ -1463,7 +1464,7 @@ function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER
) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull"); ) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull");
module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0, atype="hull") { module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0, atype="hull") {
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); check = assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
path = supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d); path = supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d);
attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) { attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) {
polygon(path); polygon(path);
@ -1498,7 +1499,7 @@ module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=und
// Examples(2D): Named anchors exist for the tips // Examples(2D): Named anchors exist for the tips
// reuleaux_polygon(n=3, d=50) show_anchors(std=false); // reuleaux_polygon(n=3, d=50) show_anchors(std=false);
module reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) { module reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) {
assert(n>=3 && (n%2)==1); check = assert(n>=3 && (n%2)==1);
r = get_radius(r=r, d=d, dflt=1); r = get_radius(r=r, d=d, dflt=1);
path = reuleaux_polygon(n=n, r=r); path = reuleaux_polygon(n=n, r=r);
anchors = [ anchors = [

View file

@ -214,7 +214,7 @@ module cuboid(
e = _corner_edges(edges, corner); e = _corner_edges(edges, corner);
cnt = sum(e); cnt = sum(e);
r = first_defined([chamfer, rounding]); r = first_defined([chamfer, rounding]);
dummy=assert(is_finite(r) && !approx(r,0)); dummy = assert(is_finite(r) && !approx(r,0));
c = [r,r,r]; c = [r,r,r];
m = 0.01; m = 0.01;
c2 = v_mul(corner,c/2); c2 = v_mul(corner,c/2);
@ -266,15 +266,16 @@ module cuboid(
teardrop = is_bool(teardrop)&&teardrop? 45 : teardrop; teardrop = is_bool(teardrop)&&teardrop? 45 : teardrop;
chamfer = approx(chamfer,0) ? undef : chamfer; chamfer = approx(chamfer,0) ? undef : chamfer;
rounding = approx(rounding,0) ? undef : rounding; rounding = approx(rounding,0) ? undef : rounding;
assert(is_vector(size,3)); checks =
assert(all_positive(size)); assert(is_vector(size,3))
assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value"); assert(all_positive(size))
assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value"); assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value")
assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding"); assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value")
assert(teardrop==false || (is_finite(teardrop) && teardrop>0 && teardrop<90), "teardrop must be either false or an angle number between 0 and 90") assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding")
assert(is_undef(p1) || is_vector(p1)); assert(teardrop==false || (is_finite(teardrop) && teardrop>0 && teardrop<90), "teardrop must be either false or an angle number between 0 and 90")
assert(is_undef(p2) || is_vector(p2)); assert(is_undef(p1) || is_vector(p1))
assert(is_bool(trimcorners)); assert(is_undef(p2) || is_vector(p2))
assert(is_bool(trimcorners));
if (!is_undef(p1)) { if (!is_undef(p1)) {
if (!is_undef(p2)) { if (!is_undef(p2)) {
translate(pointlist_bounds([p1,p2])[0]) { translate(pointlist_bounds([p1,p2])[0]) {
@ -321,7 +322,7 @@ module cuboid(
} }
} }
} else if (chamfer<0) { } else if (chamfer<0) {
assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "Cannot use negative chamfer with Z aligned edges."); checks = assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "Cannot use negative chamfer with Z aligned edges.");
ach = abs(chamfer); ach = abs(chamfer);
cube(size, center=true); cube(size, center=true);
@ -408,7 +409,7 @@ module cuboid(
} }
} }
} else if (rounding<0) { } else if (rounding<0) {
assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "Cannot use negative rounding with Z aligned edges."); checks = assert(edges == EDGES_ALL || edges[2] == [0,0,0,0], "Cannot use negative rounding with Z aligned edges.");
ard = abs(rounding); ard = abs(rounding);
cube(size, center=true); cube(size, center=true);
@ -573,23 +574,25 @@ module prismoid(
l, center, l, center,
anchor, spin=0, orient=UP anchor, spin=0, orient=UP
) { ) {
assert(is_num(size1) || is_vector(size1,2)); checks =
assert(is_num(size2) || is_vector(size2,2)); assert(is_num(size1) || is_vector(size1,2))
assert(is_num(h) || is_num(l)); assert(is_num(size2) || is_vector(size2,2))
assert(is_vector(shift,2)); assert(is_num(h) || is_num(l))
assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument."); assert(is_vector(shift,2))
assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument."); assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument.")
assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument."); assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument.")
assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument."); assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument.")
assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument."); assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument.")
assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument."); assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument.")
assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument.");
eps = pow(2,-14); eps = pow(2,-14);
size1 = is_num(size1)? [size1,size1] : size1; size1 = is_num(size1)? [size1,size1] : size1;
size2 = is_num(size2)? [size2,size2] : size2; size2 = is_num(size2)? [size2,size2] : size2;
assert(all_nonnegative(size1)); checks2 =
assert(all_nonnegative(size2)); assert(all_nonnegative(size1))
assert(size1.x + size2.x > 0); assert(all_nonnegative(size2))
assert(size1.y + size2.y > 0); assert(size1.x + size2.x > 0)
assert(size1.y + size2.y > 0);
s1 = [max(size1.x, eps), max(size1.y, eps)]; s1 = [max(size1.x, eps), max(size1.y, eps)];
s2 = [max(size2.x, eps), max(size2.y, eps)]; s2 = [max(size2.x, eps), max(size2.y, eps)];
rounding1 = default(rounding1, rounding); rounding1 = default(rounding1, rounding);
@ -841,8 +844,9 @@ module rect_tube(
l l
) { ) {
h = one_defined([h,l],"h,l"); h = one_defined([h,l],"h,l");
assert(is_num(h), "l or h argument required."); checks =
assert(is_vector(shift,2)); assert(is_num(h), "l or h argument required.")
assert(is_vector(shift,2));
s1 = is_num(size1)? [size1, size1] : s1 = is_num(size1)? [size1, size1] :
is_vector(size1,2)? size1 : is_vector(size1,2)? size1 :
is_num(size)? [size, size] : is_num(size)? [size, size] :
@ -875,15 +879,16 @@ module rect_tube(
isize2 = is_def(is2)? is2 : isize2 = is_def(is2)? is2 :
(is_def(wall) && is_def(s2))? (s2-2*[wall,wall]) : (is_def(wall) && is_def(s2))? (s2-2*[wall,wall]) :
undef; undef;
assert(wall==undef || is_num(wall)); checks2 =
assert(size1!=undef, "Bad size/size1 argument."); assert(wall==undef || is_num(wall))
assert(size2!=undef, "Bad size/size2 argument."); assert(size1!=undef, "Bad size/size1 argument.")
assert(isize1!=undef, "Bad isize/isize1 argument."); assert(size2!=undef, "Bad size/size2 argument.")
assert(isize2!=undef, "Bad isize/isize2 argument."); assert(isize1!=undef, "Bad isize/isize1 argument.")
assert(isize1.x < size1.x, "Inner size is larger than outer size."); assert(isize2!=undef, "Bad isize/isize2 argument.")
assert(isize1.y < size1.y, "Inner size is larger than outer size."); assert(isize1.x < size1.x, "Inner size is larger than outer size.")
assert(isize2.x < size2.x, "Inner size is larger than outer size."); assert(isize1.y < size1.y, "Inner size is larger than outer size.")
assert(isize2.y < size2.y, "Inner size is larger than outer size."); assert(isize2.x < size2.x, "Inner size is larger than outer size.")
assert(isize2.y < size2.y, "Inner size is larger than outer size.");
anchor = get_anchor(anchor, center, BOT, BOT); anchor = get_anchor(anchor, center, BOT, BOT);
attachable(anchor,spin,orient, size=[each size1, h], size2=size2, shift=shift) { attachable(anchor,spin,orient, size=[each size1, h], size2=size2, shift=shift) {
diff("_H_o_L_e_") diff("_H_o_L_e_")
@ -1190,28 +1195,30 @@ module cyl(
fil1 = first_defined([rounding1, rounding]); fil1 = first_defined([rounding1, rounding]);
fil2 = first_defined([rounding2, rounding]); fil2 = first_defined([rounding2, rounding]);
if (chamfer != undef) { if (chamfer != undef) {
assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder."); checks =
assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder."); assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder.")
assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder.");
} }
if (cham1 != undef) { if (cham1 != undef) {
assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder."); check = assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder.");
} }
if (cham2 != undef) { if (cham2 != undef) {
assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder."); check = assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder.");
} }
if (rounding != undef) { if (rounding != undef) {
assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder."); checks =
assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder."); assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder.")
assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder.");
} }
if (fil1 != undef) { if (fil1 != undef) {
assert(fil1 <= r1, "rounding1 is larger than the r1 radius of the cylinder."); check = assert(fil1 <= r1, "rounding1 is larger than the r1 radius of the cylinder.");
} }
if (fil2 != undef) { if (fil2 != undef) {
assert(fil2 <= r2, "rounding2 is larger than the r1 radius of the cylinder."); check = assert(fil2 <= r2, "rounding2 is larger than the r1 radius of the cylinder.");
} }
dy1 = abs(first_defined([cham1, fil1, 0])); dy1 = abs(first_defined([cham1, fil1, 0]));
dy2 = abs(first_defined([cham2, fil2, 0])); dy2 = abs(first_defined([cham2, fil2, 0]));
assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder."); check = assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder.");
path = concat( path = concat(
[[0,l/2]], [[0,l/2]],
@ -1540,9 +1547,10 @@ module tube(
r2 = default(orr2, u_add(irr2,wall)); r2 = default(orr2, u_add(irr2,wall));
ir1 = default(irr1, u_sub(orr1,wall)); ir1 = default(irr1, u_sub(orr1,wall));
ir2 = default(irr2, u_sub(orr2,wall)); ir2 = default(irr2, u_sub(orr2,wall));
assert(all_defined([r1, r2, ir1, ir2]), "Must specify two of inner radius/diam, outer radius/diam, and wall width."); checks =
assert(ir1 <= r1, "Inner radius is larger than outer radius."); assert(all_defined([r1, r2, ir1, ir2]), "Must specify two of inner radius/diam, outer radius/diam, and wall width.")
assert(ir2 <= r2, "Inner radius is larger than outer radius."); assert(ir1 <= r1, "Inner radius is larger than outer radius.")
assert(ir2 <= r2, "Inner radius is larger than outer radius.");
sides = segs(max(r1,r2)); sides = segs(max(r1,r2));
anchor = get_anchor(anchor, center, BOT, CENTER); anchor = get_anchor(anchor, center, BOT, CENTER);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) {
@ -2608,7 +2616,6 @@ function _cut_interp(pathcut, path, data) =
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
// kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1,1]; // kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1,1];
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP); // path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP);
module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0) module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0)
{ {
no_children($children); no_children($children);
@ -2653,29 +2660,37 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
normpts = is_undef(normal) ? (reverse?1:-1)*column(pts,3) : _cut_interp(pts,path, normal); normpts = is_undef(normal) ? (reverse?1:-1)*column(pts,3) : _cut_interp(pts,path, normal);
toppts = is_undef(top) ? undef : _cut_interp(pts,path,top); toppts = is_undef(top) ? undef : _cut_interp(pts,path,top);
for(i=idx(text)) for (i = idx(text)) {
let( tangent = pts[i][2] ) tangent = pts[i][2];
assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)), checks =
str("Specified top direction parallel to path at character ",i)) assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)),
assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)), str("Specified top direction parallel to path at character ",i))
str("Specified normal direction parallel to path at character ",i)) assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)),
let( str("Specified normal direction parallel to path at character ",i));
adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i]) adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i])
: usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i]) : usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i])
: [0,0,0] : [0,0,0];
) move(pts[i][0]) {
move(pts[i][0]) if (dim==3) {
if(dim==3){ frame_map(
frame_map(x=tangent-adjustment, x=tangent-adjustment,
z=usetop ? undef : normpts[i], z=usetop ? undef : normpts[i],
y=usetop ? toppts[i] : undef) y=usetop ? toppts[i] : undef
up(offset-thickness/2) ) up(offset-thickness/2) {
linear_extrude(height=thickness) linear_extrude(height=thickness)
left(lsize[0]/2)text(text[i], font=font, size=size); left(lsize[0]/2)
} else { text(text[i], font=font, size=size);
frame_map(x=point3d(tangent-adjustment), y=point3d(usetop ? toppts[i] : -normpts[i])) }
left(lsize[0]/2)text(text[i], font=font, size=size); } else {
} frame_map(
x=point3d(tangent-adjustment),
y=point3d(usetop ? toppts[i] : -normpts[i])
) left(lsize[0]/2) {
text(text[i], font=font, size=size);
}
}
}
}
} }
@ -2912,8 +2927,9 @@ module ruler(length=100, width, thickness=1, depth=3, labels=false, pipscale=1/3
colors=["black","white"], alpha=1.0, unit=1, inch=false, anchor=LEFT+BACK+TOP, spin=0, orient=UP) colors=["black","white"], alpha=1.0, unit=1, inch=false, anchor=LEFT+BACK+TOP, spin=0, orient=UP)
{ {
inchfactor = 25.4; inchfactor = 25.4;
assert(depth<=5, "Cannot render scales smaller than depth=5"); checks =
assert(len(colors)==2, "colors must contain a list of exactly two colors."); assert(depth<=5, "Cannot render scales smaller than depth=5")
assert(len(colors)==2, "colors must contain a list of exactly two colors.");
length = inch ? inchfactor * length : length; length = inch ? inchfactor * length : length;
unit = inch ? inchfactor*unit : unit; unit = inch ? inchfactor*unit : unit;
maxscale = is_def(maxscale)? maxscale : floor(log(length/unit-EPSILON)); maxscale = is_def(maxscale)? maxscale : floor(log(length/unit-EPSILON));

View file

@ -104,6 +104,23 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
// vnf2 = vnf_vertex_array(points=cap1, col_wrap=true); // vnf2 = vnf_vertex_array(points=cap1, col_wrap=true);
// vnf3 = vnf_vertex_array(points=cap2, col_wrap=true, reverse=true); // vnf3 = vnf_vertex_array(points=cap2, col_wrap=true, reverse=true);
// vnf_polyhedron([vnf1, vnf2, vnf3]); // vnf_polyhedron([vnf1, vnf2, vnf3]);
// Example(3D): Building a Multi-Stage Cylindrical Ramp
// include <BOSL2/rounding.scad>
// major_r = 50;
// groove_profile = [
// [-10,0], each arc(points=[[-7,0],[0,-3],[7,0]]), [10,0]
// ];
// ramp_profile = [ [-10,25], [90,25], [180,5], [190,5] ];
// rgroove = apply(right(major_r) * xrot(90), path3d(groove_profile));
// rprofile = round_corners(ramp_profile, radius=20, closed=false, $fn=72);
// vnf = vnf_vertex_array([
// for (a = [ramp_profile[0].x : 1 : last(ramp_profile).x]) let(
// z = lookup(a,rprofile),
// m = zrot(a) * up(z)
// )
// apply(m, [ [rgroove[0].x,0,-z], each rgroove, [last(rgroove).x,0,-z] ])
// ], caps=true, col_wrap=true, reverse=true);
// vnf_polyhedron(vnf, convexity=8);
function vnf_vertex_array( function vnf_vertex_array(
points, points,
caps, cap1, cap2, caps, cap1, cap2,