mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
Merged attachments tweaks.
This commit is contained in:
commit
f1ff6b4480
10 changed files with 227 additions and 235 deletions
129
attachments.scad
129
attachments.scad
|
@ -29,6 +29,8 @@ $parent_geom = undef;
|
|||
$tags_shown = [];
|
||||
$tags_hidden = [];
|
||||
|
||||
_ANCHOR_TYPES = ["intersect","hull"];
|
||||
|
||||
|
||||
// Section: Anchors, Spin, and Orientation
|
||||
// This library adds the concept of anchoring, spin and orientation to the `cube()`, `cylinder()`
|
||||
|
@ -1513,18 +1515,23 @@ function _attach_transform(anchor, spin, orient, geom, p) =
|
|||
|
||||
|
||||
function _get_cp(geom) =
|
||||
|
||||
let(cp=select(geom,-3))
|
||||
is_vector(cp) ? cp
|
||||
: let(
|
||||
type = in_list(geom[0],["vnf_extent","vnf_isect"]) ? "vnf"
|
||||
: in_list(geom[0],["rgn_extent","rgn_isect"]) ? "path"
|
||||
: in_list(geom[0],["xrgn_extent","xrgn_isect"]) ? "xpath"
|
||||
: "other"
|
||||
)
|
||||
assert(type!="other", "Invalid cp value")
|
||||
cp=="centroid" ? centroid(geom[1])
|
||||
cp=="centroid" ? (
|
||||
type=="vnf" && (len(geom[1][0])==0 || len(geom[1][1])==0) ? [0,0,0] :
|
||||
[each centroid(geom[1]), if (type=="xpath") geom[2]/2]
|
||||
)
|
||||
: let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1])))
|
||||
cp=="mean" ? mean(points)
|
||||
: cp=="box" ? mean(pointlist_bounds(points))
|
||||
cp=="mean" ? [each mean(points), if (type=="xpath") geom[2]/2]
|
||||
: cp=="box" ?[each mean(pointlist_bounds(points)), if (type=="xpath") geom[2]/2]
|
||||
: assert(false,"Invalid cp specification");
|
||||
|
||||
|
||||
|
@ -1542,20 +1549,22 @@ function _get_cp(geom) =
|
|||
// anchor = Vector or named anchor string.
|
||||
// geom = The geometry description of the shape.
|
||||
function _find_anchor(anchor, geom) =
|
||||
is_string(anchor)? (
|
||||
anchor=="origin"? [anchor, CENTER, UP, 0]
|
||||
: let(
|
||||
anchors = last(geom),
|
||||
found = search([anchor], anchors, num_returns_per_match=1)[0]
|
||||
)
|
||||
assert(found!=[], str("Unknown anchor: ",anchor))
|
||||
anchors[found]
|
||||
) :
|
||||
let(
|
||||
cp = _get_cp(geom),
|
||||
offset_raw = select(geom,-2),
|
||||
offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering.
|
||||
anchors = last(geom),
|
||||
type = geom[0]
|
||||
)
|
||||
is_string(anchor)? (
|
||||
anchor=="origin"? [anchor, CENTER, UP, 0]
|
||||
: let(found = search([anchor], anchors, num_returns_per_match=1)[0])
|
||||
assert(found!=[], str("Unknown anchor: ",anchor))
|
||||
anchors[found]
|
||||
) :
|
||||
assert(is_vector(anchor),str("anchor=",anchor))
|
||||
assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor))
|
||||
let(anchor = point3d(anchor))
|
||||
anchor==CENTER? [anchor, cp, UP, 0] :
|
||||
let(
|
||||
|
@ -1718,8 +1727,7 @@ function _find_anchor(anchor, geom) =
|
|||
) : type == "rgn_isect"? ( //region
|
||||
assert(anchor.z==0, "The Z component of an anchor for a 2D shape must be 0.")
|
||||
let(
|
||||
rgn_raw = move(-point2d(cp), p=geom[1]),
|
||||
rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw],
|
||||
rgn = force_region(move(-point2d(cp), p=geom[1])),
|
||||
anchor = point2d(anchor),
|
||||
isects = [
|
||||
for (path=rgn, t=triplet(path,true)) let(
|
||||
|
@ -1746,68 +1754,27 @@ function _find_anchor(anchor, geom) =
|
|||
let(
|
||||
rgn = force_region(geom[1]),
|
||||
anchor = point2d(anchor),
|
||||
m = rot(from=anchor, to=RIGHT) * move(-[cp.x, cp.y, 0]),
|
||||
rpts = apply(m, flatten(rgn)),
|
||||
rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)),
|
||||
maxx = max(column(rpts,0)),
|
||||
idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i],
|
||||
miny = min([for (i=idxs) rpts[i].y]),
|
||||
maxy = max([for (i=idxs) rpts[i].y]),
|
||||
midy = (miny+maxy)/2,
|
||||
pos = point2d(cp) + rot(from=RIGHT, to=anchor, p=[maxx,midy])
|
||||
) [anchor, pos, anchor, 0]
|
||||
) : type == "xrgn_isect"? ( //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.")
|
||||
let(
|
||||
rgn_raw = move(-point2d(cp), p=geom[1]),
|
||||
l = geom[2],
|
||||
rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw],
|
||||
anchor = point3d(anchor),
|
||||
xyanch = point2d(anchor)
|
||||
) approx(xyanch,[0,0])? [anchor, [0,0,anchor.z*l/2], unit(anchor,UP), 0] :
|
||||
let(
|
||||
isects = [
|
||||
for (path=rgn, t=triplet(path,true)) let(
|
||||
seg1 = [t[0],t[1]],
|
||||
seg2 = [t[1],t[2]],
|
||||
isect = line_intersection([[0,0],xyanch], seg1, RAY, SEGMENT),
|
||||
n = is_undef(isect)? [0,1] :
|
||||
!approx(isect, t[1])? line_normal(seg1) :
|
||||
unit((line_normal(seg1)+line_normal(seg2))/2,[0,1]),
|
||||
n2 = vector_angle(xyanch,n)>90? -n : n
|
||||
)
|
||||
if(!is_undef(isect) && !approx(isect,t[0]))
|
||||
[norm(isect), isect, n2]
|
||||
],
|
||||
maxidx = max_index(column(isects,0)),
|
||||
isect = isects[maxidx],
|
||||
pos = point3d(cp) + point3d(isect[1]) + unit([0,0,anchor.z],CENTER)*l/2,
|
||||
xyvec = unit(isect[2],[0,1]),
|
||||
vec = unit((point3d(xyvec)+UP*anchor.z)/2,UP),
|
||||
oang = approx(xyvec, [0,0])? 0 : atan2(xyvec.y, xyvec.x) + 90
|
||||
) [anchor, pos, vec, oang]
|
||||
) : type == "xrgn_extent"? ( //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.")
|
||||
let(
|
||||
rgn_raw = geom[1], l = geom[2],
|
||||
rgn = is_region(rgn_raw)? rgn_raw : [rgn_raw],
|
||||
anchor = point3d(anchor),
|
||||
xyanch = point2d(anchor),
|
||||
m = (
|
||||
approx(xyanch,[0,0])? [[1,0,0],[0,1,0],[0,0,1]] :
|
||||
rot(from=xyanch, to=RIGHT, planar=true)
|
||||
) * move(-[cp.x, cp.y]),
|
||||
rpts = apply(m, flatten(rgn)),
|
||||
maxx = max(column(rpts,0)),
|
||||
idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i],
|
||||
ys = [for (i=idxs) rpts[i].y],
|
||||
ys = [for (pt=rpts) if (approx(pt.x, maxx)) pt.y],
|
||||
midy = (min(ys)+max(ys))/2,
|
||||
xypos = point2d(cp) + (
|
||||
approx(xyanch,[0,0])? [0,0] :
|
||||
rot(from=RIGHT, to=xyanch, p=[maxx,midy])
|
||||
),
|
||||
pos = point3d(xypos) + unit([0,0,anchor.z],CENTER)*l/2,
|
||||
vec = unit((point3d(xyanch)+UP*anchor.z)/2,UP)
|
||||
) [anchor, pos, vec, oang]
|
||||
pos = rot(from=RIGHT, to=anchor, p=[maxx,midy])
|
||||
) [anchor, pos, unit(anchor), 0]
|
||||
) : 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.")
|
||||
let(
|
||||
anchor_xy = point2d(anchor),
|
||||
L = geom[2]
|
||||
)
|
||||
approx(anchor_xy,[0,0]) ? [anchor, up(anchor.z*L/2,cp), anchor, oang] :
|
||||
let(
|
||||
newgeom = list_set(geom, [0,len(geom)-3], [substr(geom[0],1), point2d(cp)]),
|
||||
result2d = _find_anchor(anchor_xy, newgeom),
|
||||
pos = point3d(result2d[1], cp.z+anchor.z*L/2),
|
||||
vec = unit(point3d(result2d[2], anchor.z),UP),
|
||||
oang = atan2(vec.y,vec.x) + 90
|
||||
)
|
||||
[anchor, pos, vec, oang]
|
||||
) :
|
||||
assert(false, "Unknown attachment geometry type.");
|
||||
|
||||
|
@ -1885,7 +1852,8 @@ module show_anchors(s=10, std=true, custom=true) {
|
|||
anchor_arrow(s, color="cyan");
|
||||
}
|
||||
color("black")
|
||||
noop($tags="anchor-arrow") {
|
||||
tags("anchor-arrow")
|
||||
{
|
||||
xrot(two_d? 0 : 90) {
|
||||
back(s/3) {
|
||||
yrot_copies(n=2)
|
||||
|
@ -1897,13 +1865,14 @@ module show_anchors(s=10, std=true, custom=true) {
|
|||
}
|
||||
}
|
||||
}
|
||||
color([1, 1, 1, 0.4])
|
||||
noop($tags="anchor-arrow") {
|
||||
color([1, 1, 1, 1])
|
||||
tags("anchor-arrow")
|
||||
{
|
||||
xrot(two_d? 0 : 90) {
|
||||
back(s/3) {
|
||||
zcopies(s/21) cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true);
|
||||
cube([s/4.5*len(anchor[0]), s/3, 0.01], center=true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1953,7 +1922,7 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow"
|
|||
// Example:
|
||||
// anchor_arrow2d(s=20);
|
||||
module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tags="anchor-arrow") {
|
||||
noop() color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2");
|
||||
color(color) stroke([[0,0],[0,s]], width=s/10, endcap1="butt", endcap2="arrow2");
|
||||
}
|
||||
|
||||
|
||||
|
@ -1971,7 +1940,7 @@ module anchor_arrow2d(s=15, color=[0.333,0.333,1], $tags="anchor-arrow") {
|
|||
// expose_anchors() cube(50, center=true) show_anchors();
|
||||
module expose_anchors(opacity=0.2) {
|
||||
show("anchor-arrow")
|
||||
children();
|
||||
children();
|
||||
hide("anchor-arrow")
|
||||
color(is_undef($color)? [0,0,0] :
|
||||
is_string($color)? $color :
|
||||
|
|
|
@ -598,6 +598,8 @@ module dashed_stroke(path, dashpat=[3,3], width=1, closed=false) {
|
|||
// start = Start angle of arc.
|
||||
// wedge = If true, include centerpoint `cp` in output to form pie slice shape.
|
||||
// endpoint = If false exclude the last point (function only). Default: true
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). (Module only) Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). (Module only) Default: `0`
|
||||
// Examples(2D):
|
||||
// arc(N=4, r=30, angle=30, wedge=true);
|
||||
// arc(r=30, angle=30, wedge=true);
|
||||
|
@ -703,7 +705,7 @@ function arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, l
|
|||
module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false, anchor=CENTER, spin=0)
|
||||
{
|
||||
path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge);
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=true) {
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=false) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
|
|
17
regions.scad
17
regions.scad
|
@ -288,6 +288,7 @@ function force_region(poly) = is_path(poly) ? [poly] : poly;
|
|||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"`
|
||||
// spin = Rotate this many degrees after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 2D point. Default: "centroid"
|
||||
// atype = Set to "hull" or "intersect to select anchor type. Default: "hull"
|
||||
// Example(2D): Displaying a region
|
||||
// region([circle(d=50), square(25,center=true)]);
|
||||
// Example(2D): Displaying a list of polygons that intersect each other, which is not a region
|
||||
|
@ -296,15 +297,16 @@ function force_region(poly) = is_path(poly) ? [poly] : poly;
|
|||
// [square([60,10], center=true)]
|
||||
// );
|
||||
// region(rgn);
|
||||
module region(r, anchor="origin", spin=0, cp="centroid")
|
||||
module region(r, anchor="origin", spin=0, cp="centroid", atype="hull")
|
||||
{
|
||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
|
||||
r = force_region(r);
|
||||
dummy=assert(is_region(r), "Input is not a region");
|
||||
points = flatten(r);
|
||||
lengths = [for(path=r) len(path)];
|
||||
starts = [0,each cumsum(lengths)];
|
||||
paths = [for(i=idx(r)) count(s=starts[i], n=lengths[i])];
|
||||
attachable(anchor, spin, two_d=true, region=r, extent=false, cp=cp){
|
||||
attachable(anchor, spin, two_d=true, region=r, extent=atype=="hull", cp=cp){
|
||||
polygon(points=points, paths=paths);
|
||||
children();
|
||||
}
|
||||
|
@ -610,7 +612,7 @@ function region_parts(region) =
|
|||
// style = The style to use when triangulating the surface of the object. Valid values are `"default"`, `"alt"`, or `"quincunx"`.
|
||||
// convexity = Max number of surfaces any single ray could pass through. Module use only.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"`
|
||||
// anchor_isect = If true, anchoring it performed by finding where the anchor vector intersects the swept shape. Default: false
|
||||
// atype = Set to "hull" or "intersect" to select anchor type. Default: "hull"
|
||||
// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
|
@ -635,7 +637,8 @@ function region_parts(region) =
|
|||
// mrgn = union(rgn1,rgn2);
|
||||
// orgn = difference(mrgn,rgn3);
|
||||
// linear_sweep(orgn,height=20,convexity=16) show_anchors();
|
||||
module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity, anchor_isect=false, spin=0, orient=UP, cp="centroid", anchor="origin") {
|
||||
module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg, style="default", convexity,
|
||||
spin=0, orient=UP, cp="centroid", anchor="origin", atype="hull") {
|
||||
region = force_region(region);
|
||||
dummy=assert(is_region(region),"Input is not a region");
|
||||
anchor = center ? "zcenter" : anchor;
|
||||
|
@ -646,7 +649,7 @@ module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg,
|
|||
slices=slices, maxseg=maxseg,
|
||||
style=style
|
||||
);
|
||||
attachable(anchor,spin,orient, cp=cp, vnf=vnf, extent=!anchor_isect, anchors=anchors) {
|
||||
attachable(anchor,spin,orient, cp=cp, region=region, h=height, extent=atype=="hull", anchors=anchors) {
|
||||
vnf_polyhedron(vnf, convexity=convexity);
|
||||
children();
|
||||
}
|
||||
|
@ -654,7 +657,7 @@ module linear_sweep(region, height=1, center, twist=0, scale=1, slices, maxseg,
|
|||
|
||||
|
||||
function linear_sweep(region, height=1, center, twist=0, scale=1, slices,
|
||||
maxseg, style="default", cp="centroid", anchor_isect=false, anchor, spin=0, orient=UP) =
|
||||
maxseg, style="default", cp="centroid", atype="hull", anchor, spin=0, orient=UP) =
|
||||
let(
|
||||
region = force_region(region)
|
||||
)
|
||||
|
@ -699,7 +702,7 @@ function linear_sweep(region, height=1, center, twist=0, scale=1, slices,
|
|||
for (rgn = regions) vnf_from_region(rgn, ident(4), reverse=true),
|
||||
for (rgn = trgns) vnf_from_region(rgn, up(height), reverse=false)
|
||||
])
|
||||
) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=!anchor_isect, p=vnf, anchors=anchors);
|
||||
) reorient(anchor,spin,orient, cp=cp, vnf=vnf, extent=atype=="hull", p=vnf, anchors=anchors);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -244,15 +244,20 @@ module circle(r, d, anchor=CENTER, spin=0) {
|
|||
// ellipse(d=50, anchor=FRONT, spin=45);
|
||||
// Example(NORENDER): Called as Function
|
||||
// path = ellipse(d=50, anchor=FRONT, spin=45);
|
||||
module ellipse(r, d, realign=false, circum=false, anchor=CENTER, spin=0) {
|
||||
r = get_radius(r=r, d=d, dflt=1);
|
||||
dummy = assert((is_finite(r) || is_vector(r,2)) && all_positive(r), "Invalid radius or diameter for ellipse");
|
||||
module ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER, spin=0)
|
||||
{
|
||||
r = force_list(get_radius(r=r, d=d, dflt=1),2);
|
||||
dummy = assert(is_vector(r,2) && all_positive(r), "Invalid radius or diameter for ellipse");
|
||||
sides = segs(max(r));
|
||||
sc = circum? (1 / cos(180/sides)) : 1;
|
||||
rx = default(r[0],r) * sc;
|
||||
ry = default(r[1],r) * sc;
|
||||
rx = r.x * sc;
|
||||
ry = r.y * sc;
|
||||
attachable(anchor,spin, two_d=true, r=[rx,ry]) {
|
||||
if (rx < ry) {
|
||||
if (uniform) {
|
||||
assert(!circum, "Circum option not allowed when \"uniform\" is true");
|
||||
polygon(ellipse(r,realign=realign, circum=circum, uniform=true));
|
||||
}
|
||||
else if (rx < ry) {
|
||||
xscale(rx/ry) {
|
||||
zrot(realign? 180/sides : 0) {
|
||||
circle(r=ry, $fn=sides);
|
||||
|
@ -270,14 +275,42 @@ module ellipse(r, d, realign=false, circum=false, anchor=CENTER, spin=0) {
|
|||
}
|
||||
|
||||
|
||||
function ellipse(r, d, realign=false, circum=false, anchor=CENTER, spin=0) =
|
||||
// Iterative refinement to produce an inscribed polygon
|
||||
// in an ellipse whose side lengths are all equal
|
||||
function _ellipse_refine(a,b,N, _theta=[]) =
|
||||
len(_theta)==0? _ellipse_refine(a,b,N,lerpn(0,360,N,endpoint=false))
|
||||
:
|
||||
let(
|
||||
pts = [for(t=_theta) [a*cos(t),b*sin(t)]],
|
||||
lenlist= path_segment_lengths(pts,closed=true),
|
||||
meanlen = mean(lenlist),
|
||||
error = lenlist/meanlen
|
||||
)
|
||||
all_equal(error,EPSILON) ? pts
|
||||
:
|
||||
let(
|
||||
dtheta = [each deltas(_theta),
|
||||
360-last(_theta)],
|
||||
newdtheta = [for(i=idx(dtheta)) dtheta[i]/error[i]],
|
||||
adjusted = [0,each cumsum(list_head(newdtheta / sum(newdtheta) * 360))]
|
||||
)
|
||||
_ellipse_refine(a,b,N,adjusted);
|
||||
|
||||
|
||||
|
||||
function ellipse(r, d, realign=false, circum=false, uniform=false, anchor=CENTER, spin=0) =
|
||||
let(
|
||||
r = force_list(get_radius(r=r, d=d, dflt=1),2),
|
||||
sides = segs(max(r))
|
||||
)
|
||||
uniform ? assert(!circum, "Circum option not allowed when \"uniform\" is true")
|
||||
reorient(anchor,spin,two_d=true,r=[r.x,r.y],p=_ellipse_refine(r.x,r.y,sides))
|
||||
:
|
||||
let(
|
||||
r = get_radius(r=r, d=d, dflt=1),
|
||||
sides = segs(max(r)),
|
||||
offset = realign? 180/sides : 0,
|
||||
sc = circum? (1 / cos(180/sides)) : 1,
|
||||
rx = default(r[0],r) * sc,
|
||||
ry = default(r[1],r) * sc,
|
||||
rx = r.x * sc,
|
||||
ry = r.y * sc,
|
||||
pts = [for (i=[0:1:sides-1]) let(a=360-offset-i*360/sides) [rx*cos(a), ry*sin(a)]]
|
||||
) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts);
|
||||
|
||||
|
@ -780,6 +813,7 @@ module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER
|
|||
// align_pit = If given as a 2D vector, rotates the whole shape so that the first inner corner is pointed towards that direction. This occurs before spin.
|
||||
// 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 after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// atype = Choose "hull" or "intersect" anchor methods. Default: "hull"
|
||||
// Extra Anchors:
|
||||
// "tip0" ... "tip4" = Each tip has an anchor, pointing outwards.
|
||||
// "pit0" ... "pit4" = The inside corner between each tip has an anchor, pointing outwards.
|
||||
|
@ -801,7 +835,8 @@ module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER
|
|||
// stroke([[0,0],[0,7]], endcap2="arrow2");
|
||||
// Example(2D): Called as Function
|
||||
// stroke(closed=true, star(n=5, r=50, ir=25));
|
||||
function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, _mat, _anchs) =
|
||||
function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0, atype="hull", _mat, _anchs) =
|
||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
|
||||
assert(is_undef(align_tip) || is_vector(align_tip))
|
||||
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")
|
||||
|
@ -843,10 +878,11 @@ function star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit
|
|||
named_anchor(str("midpt",i), pos, unit(pos,BACK), 0),
|
||||
]
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, p=path, anchors=anchors);
|
||||
) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull", anchors=anchors);
|
||||
|
||||
|
||||
module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit, anchor=CENTER, spin=0) {
|
||||
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\"");
|
||||
assert(is_undef(align_tip) || is_vector(align_tip));
|
||||
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");
|
||||
|
@ -874,7 +910,7 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit,
|
|||
]
|
||||
];
|
||||
path = star(n=n, r=r, ir=ir, realign=realign, _mat=mat, _anchs=anchors);
|
||||
attachable(anchor,spin, two_d=true, path=path, anchors=anchors) {
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=atype=="hull", anchors=anchors) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
|
@ -948,7 +984,7 @@ module jittered_poly(path, dist=1/512) {
|
|||
// Function&Module: teardrop2d()
|
||||
//
|
||||
// Description:
|
||||
// Makes a 2D teardrop shape. Useful for extruding into 3D printable holes.
|
||||
// Makes a 2D teardrop shape. Useful for extruding into 3D printable holes. Uses "intersect" style anchoring.
|
||||
//
|
||||
// Usage: As Module
|
||||
// teardrop2d(r/d=, [ang], [cap_h]);
|
||||
|
@ -979,7 +1015,7 @@ module jittered_poly(path, dist=1/512) {
|
|||
module teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0)
|
||||
{
|
||||
path = teardrop2d(r=r, d=d, ang=ang, cap_h=cap_h);
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=false) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
|
@ -1008,7 +1044,7 @@ function teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) =
|
|||
),
|
||||
maxx_idx = max_index(column(path,0)),
|
||||
path2 = list_rotate(path,maxx_idx)
|
||||
) reorient(anchor,spin, two_d=true, path=path2, p=path2);
|
||||
) reorient(anchor,spin, two_d=true, path=path2, p=path2, extent=false);
|
||||
|
||||
|
||||
|
||||
|
@ -1023,7 +1059,7 @@ function teardrop2d(r, ang=45, cap_h, d, anchor=CENTER, spin=0) =
|
|||
// See Also: circle(), ellipse()
|
||||
// Description:
|
||||
// When called as a function, returns a 2D path forming a shape of two circles joined by curved waist.
|
||||
// When called as a module, creates a 2D shape of two circles joined by curved waist.
|
||||
// When called as a module, creates a 2D shape of two circles joined by curved waist. Uses "hull" style anchoring.
|
||||
// Arguments:
|
||||
// r = The radius of the end circles.
|
||||
// spread = The distance between the centers of the end circles. Default: 10
|
||||
|
@ -1094,6 +1130,8 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
|
|||
// Description:
|
||||
// When called as a function, returns a 2D path for the outline of the [Superformula](https://en.wikipedia.org/wiki/Superformula) shape.
|
||||
// When called as a module, creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape.
|
||||
// Note that the "hull" type anchoring (the default) is more intuitive for concave star-like shapes, but the anchor points do not
|
||||
// necesarily lie on the line of the anchor vector, which can be confusing, especially for simpler, ellipse-like shapes.
|
||||
// Arguments:
|
||||
// step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate.
|
||||
// m1 = The m1 argument for the superformula. Default: 4.
|
||||
|
@ -1108,6 +1146,7 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
|
|||
// d = Diameter of the shape. Scale shape to fit in a circle of diameter d.
|
||||
// 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 after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// atype = Select "hull" or "intersect" style anchoring. Default: "hull".
|
||||
// Example(2D):
|
||||
// supershape(step=0.5,m1=16,m2=16,n1=0.5,n2=0.5,n3=16,r=50);
|
||||
// Example(2D): Called as Function
|
||||
|
@ -1133,8 +1172,10 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
|
|||
// Examples:
|
||||
// linear_extrude(height=0.3, scale=0) supershape(step=1, m1=6, n1=0.4, n2=0, n3=6);
|
||||
// linear_extrude(height=5, scale=0) supershape(step=1, b=3, m1=6, n1=3.8, n2=16, n3=10);
|
||||
function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0) =
|
||||
function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0, atype="hull") =
|
||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
|
||||
let(
|
||||
|
||||
r = get_radius(r=r, d=d, dflt=undef),
|
||||
m2 = is_def(m2) ? m2 : m1,
|
||||
n2 = is_def(n2) ? n2 : n1,
|
||||
|
@ -1146,11 +1187,12 @@ function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER
|
|||
rads = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)],
|
||||
scale = is_def(r) ? r/max(rads) : 1,
|
||||
path = [for (i = [steps:-1:1]) let(a=angs[i]) scale*rads[i]*[cos(a), sin(a)]]
|
||||
) reorient(anchor,spin, two_d=true, path=path, p=path);
|
||||
) 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) {
|
||||
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\"");
|
||||
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, two_d=true, path=path) {
|
||||
attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
|
@ -1165,7 +1207,7 @@ module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=und
|
|||
// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable
|
||||
// See Also: regular_ngon(), pentagon(), hexagon(), octagon()
|
||||
// Description:
|
||||
// Creates a 2D Reuleaux Polygon; a constant width shape that is not circular.
|
||||
// Creates a 2D Reuleaux Polygon; a constant width shape that is not circular. Uses "intersect" type anchoring.
|
||||
// Arguments:
|
||||
// N = Number of "sides" to the Reuleaux Polygon. Must be an odd positive number. Default: 3
|
||||
// r = Radius of the shape. Scale shape to fit in a circle of radius r.
|
||||
|
@ -1192,7 +1234,7 @@ module reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) {
|
|||
cp = polar_to_xy(r, ca)
|
||||
) named_anchor(str("tip",i), cp, unit(cp,BACK), 0),
|
||||
];
|
||||
attachable(anchor,spin, two_d=true, path=path, anchors=anchors) {
|
||||
attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
|
@ -1219,7 +1261,7 @@ function reuleaux_polygon(N=3, r, d, anchor=CENTER, spin=0) =
|
|||
cp = polar_to_xy(r, ca)
|
||||
) named_anchor(str("tip",i), cp, unit(cp,BACK), 0),
|
||||
]
|
||||
) reorient(anchor,spin, two_d=true, path=path, anchors=anchors, p=path);
|
||||
) reorient(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors, p=path);
|
||||
|
||||
|
||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
|
@ -2304,27 +2304,6 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
|
|||
// Section: Miscellaneous
|
||||
|
||||
|
||||
// Module: nil()
|
||||
//
|
||||
// Description:
|
||||
// Useful when you MUST pass a child to a module, but you want it to be nothing.
|
||||
module nil() union(){}
|
||||
|
||||
|
||||
// Module: noop()
|
||||
//
|
||||
// Description:
|
||||
// Passes through the children passed to it, with no action at all. Useful while debugging when
|
||||
// you want to replace a command. This is an attachable non-object.
|
||||
//
|
||||
// Arguments:
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
module noop(spin=0, orient=UP) attachable(CENTER,spin,orient, d=0.01) {nil(); children();}
|
||||
|
||||
|
||||
|
||||
|
||||
// Module: interior_fillet()
|
||||
//
|
||||
// Description:
|
||||
|
|
133
skin.scad
133
skin.scad
|
@ -16,9 +16,9 @@
|
|||
|
||||
// Function&Module: skin()
|
||||
// Usage: As module:
|
||||
// skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [convexity=], [anchor=],[cp=],[spin=],[orient=],[extent=]) [attachments];
|
||||
// skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [convexity=], [anchor=],[cp=],[spin=],[orient=],[atype=]) {attachments};
|
||||
// Usage: As function:
|
||||
// vnf = skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=]);
|
||||
// vnf = skin(profiles, slices, [z=], [refine=], [method=], [sampling=], [caps=], [closed=], [style=], [anchor=],[cp=],[spin=],[orient=],[atype=]);
|
||||
// Description:
|
||||
// Given a list of two or more path `profiles` in 3d space, produces faces to skin a surface between
|
||||
// the profiles. Optionally the first and last profiles can have endcaps, or the first and last profiles
|
||||
|
@ -150,11 +150,11 @@
|
|||
// method = method for connecting profiles, one of "distance", "fast_distance", "tangent", "direct" or "reindex". Default: "direct".
|
||||
// z = array of height values for each profile if the profiles are 2d
|
||||
// convexity = convexity setting for use with polyhedron. (module only) Default: 10
|
||||
// anchor = Translate so anchor point is at the origin. (module only) Default: "origin"
|
||||
// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0
|
||||
// orient = Vector to rotate top towards after spin (module only)
|
||||
// extent = use extent method for computing anchors. (module only) Default: false
|
||||
// cp = set centerpoint for anchor computation. (module only) Default: object centroid
|
||||
// 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
|
||||
// atype = Select "hull" or "intersect anchor types. Default: "hull"
|
||||
// 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"
|
||||
// style = vnf_vertex_array style. Default: "min_edge"
|
||||
// Example:
|
||||
// skin([octagon(4), circle($fn=70,r=2)], z=[0,3], slices=10);
|
||||
|
@ -383,18 +383,17 @@
|
|||
// }
|
||||
// }
|
||||
module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge", convexity=10,
|
||||
anchor="origin",cp,spin=0, orient=UP, extent=false)
|
||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull")
|
||||
{
|
||||
vnf = skin(profiles, slices, refine, method, sampling, caps, closed, z, style=style);
|
||||
attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf))
|
||||
{
|
||||
vnf_polyhedron(vnf,convexity=convexity);
|
||||
vnf_polyhedron(vnf,convexity=convexity,spin=spin,anchor=anchor,orient=orient,atype=atype,cp=cp)
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge") =
|
||||
function skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, style="min_edge",
|
||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") =
|
||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
|
||||
assert(is_def(slices),"The slices argument must be specified.")
|
||||
assert(is_list(profiles) && len(profiles)>1, "Must provide at least two profiles")
|
||||
let(
|
||||
|
@ -490,20 +489,20 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
|||
". Method ",method[i]," requires equal values"))
|
||||
refine[i] * len(pair[0])
|
||||
)
|
||||
subdivide_and_slice(pair,slices[i], nsamples, method=sampling)]
|
||||
subdivide_and_slice(pair,slices[i], nsamples, method=sampling)],
|
||||
vnf=vnf_join(
|
||||
[for(i=idx(full_list))
|
||||
vnf_vertex_array(full_list[i], cap1=i==0 && fullcaps[0], cap2=i==len(full_list)-1 && fullcaps[1],
|
||||
col_wrap=true, style=style)])
|
||||
)
|
||||
vnf_join(
|
||||
[for(i=idx(full_list))
|
||||
vnf_vertex_array(full_list[i], cap1=i==0 && fullcaps[0], cap2=i==len(full_list)-1 && fullcaps[1],
|
||||
col_wrap=true, style=style)]);
|
||||
|
||||
reorient(anchor,spin,orient,vnf=vnf,p=vnf,extent=atype=="hull",cp=cp);
|
||||
|
||||
|
||||
// Function&Module: path_sweep()
|
||||
// Usage: As module
|
||||
// path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [transforms=], [anchor=], [cp=], [spin=], [orient=], [extent=]) [attachments];
|
||||
// Usage: As function
|
||||
// vnf = path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [transforms=]);
|
||||
// path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [convexity=], [anchor=], [cp=], [spin=], [orient=], [atype=]) {attachments};
|
||||
// Usage: As function
|
||||
// vnf = path_sweep(shape, path, [method], [normal=], [closed=], [twist=], [twist_by_length=], [symmetry=], [last_normal=], [tangent=], [relaxed=], [caps=], [style=], [transforms=], [anchor=], [cp=], [spin=], [orient=], [atype=]) {attachments};
|
||||
// Description:
|
||||
// Takes as input a 2D polygon path, and a 2d or 3d path and constructs a polyhedron by sweeping the shape along the path.
|
||||
// When run as a module returns the polyhedron geometry. When run as a function returns a VNF by default or if you set `transforms=true`
|
||||
|
@ -566,13 +565,12 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
|||
// caps = Can be a boolean or vector of two booleans. Set to false to disable caps at the two ends. Default: true
|
||||
// style = vnf_vertex_array style. Default: "min_edge"
|
||||
// transforms = set to true to return transforms instead of a VNF. These transforms can be manipulated and passed to sweep(). Default: false.
|
||||
// convexity = convexity parameter for polyhedron(). Only accepted by the module version. Default: 10
|
||||
// anchor = Translate so anchor point is at the origin. (module only) Default: "origin"
|
||||
// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0
|
||||
// orient = Vector to rotate top towards after spin (module only)
|
||||
// extent = use extent method for computing anchors. (module only) Default: false
|
||||
// cp = set centerpoint for anchor computation. (module only) Default: object centroid
|
||||
//
|
||||
// convexity = convexity parameter for polyhedron(). (module only) Default: 10
|
||||
// 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
|
||||
// atype = Select "hull" or "intersect" anchor types. Default: "hull"
|
||||
// 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"
|
||||
// Example(2D): We'll use this shape in several examples
|
||||
// ushape = [[-10, 0],[-10, 10],[ -7, 10],[ -7, 2],[ 7, 2],[ 7, 7],[ 10, 7],[ 10, 0]];
|
||||
// polygon(ushape);
|
||||
|
@ -812,20 +810,19 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close
|
|||
// }
|
||||
module path_sweep(shape, path, method="incremental", normal, closed=false, twist=0, twist_by_length=true,
|
||||
symmetry=1, last_normal, tangent, relaxed=false, caps, style="min_edge", convexity=10,
|
||||
anchor="origin",cp,spin=0, orient=UP, extent=false)
|
||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull")
|
||||
{
|
||||
vnf = path_sweep(shape, path, method, normal, closed, twist, twist_by_length,
|
||||
symmetry, last_normal, tangent, relaxed, caps, style);
|
||||
attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf))
|
||||
{
|
||||
vnf_polyhedron(vnf,convexity=convexity);
|
||||
vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp)
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function path_sweep(shape, path, method="incremental", normal, closed=false, twist=0, twist_by_length=true,
|
||||
symmetry=1, last_normal, tangent, relaxed=false, caps, style="min_edge", transforms=false) =
|
||||
symmetry=1, last_normal, tangent, relaxed=false, caps, style="min_edge", transforms=false,
|
||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") =
|
||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
|
||||
assert(!closed || twist % (360/symmetry)==0, str("For a closed sweep, twist must be a multiple of 360/symmetry = ",360/symmetry))
|
||||
assert(closed || symmetry==1, "symmetry must be 1 when closed is false")
|
||||
assert(is_integer(symmetry) && symmetry>0, "symmetry must be a positive integer")
|
||||
|
@ -919,14 +916,16 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
apply(transform_list[L], rshape)),
|
||||
dummy = ends_match ? 0 : echo("WARNING: ***** The points do not match when closing the model *****")
|
||||
)
|
||||
transforms ? transform_list : sweep(is_path(shape)?clockwise_polygon(shape):shape, transform_list, closed=false, caps=fullcaps,style=style);
|
||||
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);
|
||||
|
||||
|
||||
// Function&Module: path_sweep2d()
|
||||
// Usage: as module
|
||||
// path_sweep2d(shape, path, [closed], [caps], [quality], [style], [convexity=], [anchor=], [spin=], [orient=], [extent=], [cp=]) [attachments];
|
||||
// path_sweep2d(shape, path, [closed], [caps], [quality], [style], [convexity=], [anchor=], [spin=], [orient=], [atype=], [cp=]) {attachments};
|
||||
// Usage: as function
|
||||
// vnf = path_sweep2d(shape, path, [closed], [caps], [quality], [style]);
|
||||
// vnf = path_sweep2d(shape, path, [closed], [caps], [quality], [style], [anchor=], [spin=], [orient=], [atype=], [cp=]);
|
||||
// Description:
|
||||
// Takes an input 2D polygon (the shape) and a 2d path and constructs a polyhedron by sweeping the shape along the path.
|
||||
// When run as a module returns the polyhedron geometry. When run as a function returns a VNF.
|
||||
|
@ -946,11 +945,11 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
// style = vnf_vertex_array style. Default: "min_edge"
|
||||
// ---
|
||||
// convexity = convexity parameter for polyhedron (module only) Default: 10
|
||||
// anchor = Translate so anchor point is at the origin. (module only) Default: "origin"
|
||||
// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0
|
||||
// orient = Vector to rotate top towards after spin (module only)
|
||||
// extent = use extent method for computing anchors. (module only) Default: false
|
||||
// cp = set centerpoint for anchor computation. (module only) Default: object centroid
|
||||
// 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
|
||||
// atype = Select "hull" or "intersect" anchor types. Default: "hull"
|
||||
// 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"
|
||||
// Example: Sine wave example with self-intersections at each peak. This would fail with path_sweep().
|
||||
// sinewave = [for(i=[-30:10:360*2+30]) [i/40,3*sin(i)]];
|
||||
// path_sweep2d(circle(r=3,$fn=15), sinewave);
|
||||
|
@ -967,7 +966,8 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
|
|||
// path_sweep2d(circle(r=3.25, $fn=32), select(ellipse,floor(L*.2),ceil(L*.8)),closed=false);
|
||||
// path_sweep2d(circle(r=3.25, $fn=32), select(ellipse,floor(L*.7),ceil(L*.3)),closed=false);
|
||||
|
||||
function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edge") =
|
||||
function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edge",
|
||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull") =
|
||||
let(
|
||||
caps = is_def(caps) ? caps
|
||||
: closed ? false : true,
|
||||
|
@ -992,23 +992,21 @@ function path_sweep2d(shape, path, closed=false, caps, quality=1, style="min_edg
|
|||
)
|
||||
select(path3d(ofs[0],pt.y),map)
|
||||
]
|
||||
)
|
||||
),
|
||||
vnf = vnf_vertex_array([
|
||||
each proflist,
|
||||
if (closed) proflist[0]
|
||||
],cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style)
|
||||
)
|
||||
vnf_vertex_array([
|
||||
each proflist,
|
||||
if (closed) proflist[0]
|
||||
],cap1=fullcaps[0],cap2=fullcaps[1],col_wrap=true,style=style);
|
||||
reorient(anchor,spin,orient,vnf=vnf,p=vnf,extent=atype=="hull",cp=cp);
|
||||
|
||||
|
||||
module path_sweep2d(profile, path, closed=false, caps, quality=1, style="min_edge", convexity=10,
|
||||
anchor="origin", cp, spin=0, orient=UP, extent=false)
|
||||
anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull")
|
||||
{
|
||||
vnf = path_sweep2d(profile, path, closed, caps, quality, style);
|
||||
attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf))
|
||||
{
|
||||
vnf_polyhedron(vnf,convexity=convexity);
|
||||
vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp)
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
// Extract vertex mapping from offset face list. The output of this function
|
||||
|
@ -1046,9 +1044,9 @@ function _ofs_face_edge(face,firstlen,second=false) =
|
|||
|
||||
// Function&Module: sweep()
|
||||
// Usage: As Module
|
||||
// sweep(shape, transforms, [closed], [caps], [style], [convexity=], [anchor=], [spin=], [orient=], [extent=]) [attachments];
|
||||
// sweep(shape, transforms, [closed], [caps], [style], [convexity=], [anchor=], [spin=], [orient=], [atype=]) [attachments];
|
||||
// Usage: As Function
|
||||
// vnf = sweep(shape, transforms, [closed], [caps], [style]);
|
||||
// vnf = sweep(shape, transforms, [closed], [caps], [style], [anchor=], [spin=], [orient=], [atype=]);
|
||||
// Description:
|
||||
// The input `shape` must be a non-self-intersecting 2D polygon or region, and `transforms`
|
||||
// is a list of 4x4 transformation matrices. The sweep algorithm applies each transformation in sequence
|
||||
|
@ -1071,11 +1069,11 @@ 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
|
||||
// anchor = Translate so anchor point is at the origin. (module only) Default: "origin"
|
||||
// spin = Rotate this many degrees around Z axis after anchor. (module only) 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 (module only)
|
||||
// extent = use extent method for computing anchors. (module only) Default: false
|
||||
// cp = set centerpoint for anchor computation. (module only) Default: object centroid
|
||||
// atype = Select "hull" or "intersect" anchor types. Default: "hull"
|
||||
// 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"
|
||||
// Example: This is the "sweep-drop" example from list-comprehension-demos.
|
||||
// function drop(t) = 100 * 0.5 * (1 - cos(180 * t)) * sin(180 * t) + 1;
|
||||
// function path(t) = [0, 0, 80 + 80 * cos(180 * t)];
|
||||
|
@ -1097,7 +1095,8 @@ function _ofs_face_edge(face,firstlen,second=false) =
|
|||
// inside = [for(i=[24:-1:2]) up(i)*rot(i)*scale(1.2*i/24+1)];
|
||||
// sweep(shape, concat(outside,inside));
|
||||
|
||||
function sweep(shape, transforms, closed=false, caps, style="min_edge") =
|
||||
function sweep(shape, transforms, closed=false, caps, style="min_edge",
|
||||
anchor="origin", cp="centroid", spin=0, orient=UP, atype="hull") =
|
||||
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(
|
||||
|
@ -1128,15 +1127,13 @@ function sweep(shape, transforms, closed=false, caps, style="min_edge") =
|
|||
|
||||
|
||||
module sweep(shape, transforms, closed=false, caps, style="min_edge", convexity=10,
|
||||
anchor="origin",cp,spin=0, orient=UP, extent=false)
|
||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull")
|
||||
{
|
||||
vnf = sweep(shape, transforms, closed, caps, style);
|
||||
attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=extent, cp=is_def(cp) ? cp : centroid(vnf))
|
||||
{
|
||||
vnf_polyhedron(vnf,convexity=convexity);
|
||||
children();
|
||||
}
|
||||
}
|
||||
vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp)
|
||||
children();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Section: Functions for resampling and slicing profile lists
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
// Arguments:
|
||||
// str = string to operate on
|
||||
// pos = starting index of substring, or vector of first and last position. Default: 0
|
||||
// len = length of substring, or omit it to get the rest of the string. If len is less than zero the emptry string is returned.
|
||||
// len = length of substring, or omit it to get the rest of the string. If len is zero or less then the emptry string is returned.
|
||||
// Example:
|
||||
// substr("abcdefg",3,3); // Returns "def"
|
||||
// substr("abcdefg",2); // Returns "cdefg"
|
||||
|
|
|
@ -8,7 +8,7 @@ module test_translate() {
|
|||
assert_equal(translate(val, p=[1,2,3]), [1,2,3]+val);
|
||||
}
|
||||
// Verify that module at least doesn't crash.
|
||||
translate([-5,-5,-5]) translate([0,0,0]) translate([5,5,5]) nil();
|
||||
translate([-5,-5,-5]) translate([0,0,0]) translate([5,5,5]) union(){};
|
||||
}
|
||||
test_translate();
|
||||
|
||||
|
@ -21,8 +21,8 @@ module test_move() {
|
|||
assert_equal(move(x=val.x, y=val.y, z=val.z, p=[1,2,3]), [1,2,3]+val);
|
||||
}
|
||||
// Verify that module at least doesn't crash.
|
||||
move(x=-5) move(y=-5) move(z=-5) move([-5,-5,-5]) nil();
|
||||
move(x=5) move(y=5) move(z=5) move([5,5,5]) nil();
|
||||
move(x=-5) move(y=-5) move(z=-5) move([-5,-5,-5]) union(){};
|
||||
move(x=5) move(y=5) move(z=5) move([5,5,5]) union(){};
|
||||
}
|
||||
test_move();
|
||||
|
||||
|
@ -35,7 +35,7 @@ module test_left() {
|
|||
assert_equal(left(0,p=[1,2,3]),[1,2,3]);
|
||||
assert_equal(left(-5,p=[1,2,3]),[6,2,3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
left(-5) left(0) left(5) nil();
|
||||
left(-5) left(0) left(5) union(){};
|
||||
}
|
||||
test_left();
|
||||
|
||||
|
@ -48,7 +48,7 @@ module test_right() {
|
|||
assert_equal(right(0,p=[1,2,3]),[1,2,3]);
|
||||
assert_equal(right(5,p=[1,2,3]),[6,2,3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
right(-5) right(0) right(5) nil();
|
||||
right(-5) right(0) right(5) union(){};
|
||||
}
|
||||
test_right();
|
||||
|
||||
|
@ -61,7 +61,7 @@ module test_back() {
|
|||
assert_equal(back(0,p=[1,2,3]),[1,2,3]);
|
||||
assert_equal(back(5,p=[1,2,3]),[1,7,3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
back(-5) back(0) back(5) nil();
|
||||
back(-5) back(0) back(5) union(){};
|
||||
}
|
||||
test_back();
|
||||
|
||||
|
@ -74,7 +74,7 @@ module test_fwd() {
|
|||
assert_equal(fwd(0,p=[1,2,3]),[1,2,3]);
|
||||
assert_equal(fwd(-5,p=[1,2,3]),[1,7,3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
fwd(-5) fwd(0) fwd(5) nil();
|
||||
fwd(-5) fwd(0) fwd(5) union(){};
|
||||
}
|
||||
test_fwd();
|
||||
|
||||
|
@ -87,7 +87,7 @@ module test_down() {
|
|||
assert_equal(down(0,p=[1,2,3]),[1,2,3]);
|
||||
assert_equal(down(-5,p=[1,2,3]),[1,2,8]);
|
||||
// Verify that module at least doesn't crash.
|
||||
down(-5) down(0) down(5) nil();
|
||||
down(-5) down(0) down(5) union(){};
|
||||
}
|
||||
test_down();
|
||||
|
||||
|
@ -100,7 +100,7 @@ module test_up() {
|
|||
assert_equal(up(0,p=[1,2,3]),[1,2,3]);
|
||||
assert_equal(up(5,p=[1,2,3]),[1,2,8]);
|
||||
// Verify that module at least doesn't crash.
|
||||
up(-5) up(0) up(5) nil();
|
||||
up(-5) up(0) up(5) union(){};
|
||||
}
|
||||
test_up();
|
||||
|
||||
|
@ -112,7 +112,7 @@ module test_scale() {
|
|||
assert_equal(scale(point2d(val)), [[val.x,0,0],[0,val.y,0],[0,0,1]]);
|
||||
assert_equal(scale(val), [[val.x,0,0,0],[0,val.y,0,0],[0,0,val.z,0],[0,0,0,1]]);
|
||||
assert_equal(scale(val, p=[1,2,3]), v_mul([1,2,3], val));
|
||||
scale(val) nil();
|
||||
scale(val) union(){};
|
||||
}
|
||||
assert_equal(scale(3), [[3,0,0,0],[0,3,0,0],[0,0,3,0],[0,0,0,1]]);
|
||||
assert_equal(scale(3, p=[1,2,3]), 3*[1,2,3]);
|
||||
|
@ -124,7 +124,7 @@ module test_scale() {
|
|||
assert_equal(scale([2,3,4], p=cb), cube([2,3,4]));
|
||||
assert_equal(scale([-2,-3,-4], p=cb), [[for (p=cb[0]) v_mul(p,[-2,-3,-4])], [for (f=cb[1]) reverse(f)]]);
|
||||
// Verify that module at least doesn't crash.
|
||||
scale(-5) scale(5) nil();
|
||||
scale(-5) scale(5) union(){};
|
||||
}
|
||||
test_scale();
|
||||
|
||||
|
@ -134,10 +134,10 @@ module test_xscale() {
|
|||
for (val=vals) {
|
||||
assert_equal(xscale(val), [[val,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);
|
||||
assert_equal(xscale(val, p=[1,2,3]), [val*1,2,3]);
|
||||
xscale(val) nil();
|
||||
xscale(val) union(){};
|
||||
}
|
||||
// Verify that module at least doesn't crash.
|
||||
xscale(-5) xscale(5) nil();
|
||||
xscale(-5) xscale(5) union(){};
|
||||
}
|
||||
test_xscale();
|
||||
|
||||
|
@ -147,10 +147,10 @@ module test_yscale() {
|
|||
for (val=vals) {
|
||||
assert_equal(yscale(val), [[1,0,0,0],[0,val,0,0],[0,0,1,0],[0,0,0,1]]);
|
||||
assert_equal(yscale(val, p=[1,2,3]), [1,val*2,3]);
|
||||
yscale(val) nil();
|
||||
yscale(val) union(){};
|
||||
}
|
||||
// Verify that module at least doesn't crash.
|
||||
yscale(-5) yscale(5) nil();
|
||||
yscale(-5) yscale(5) union(){};
|
||||
}
|
||||
test_yscale();
|
||||
|
||||
|
@ -160,10 +160,10 @@ module test_zscale() {
|
|||
for (val=vals) {
|
||||
assert_equal(zscale(val), [[1,0,0,0],[0,1,0,0],[0,0,val,0],[0,0,0,1]]);
|
||||
assert_equal(zscale(val, p=[1,2,3]), [1,2,val*3]);
|
||||
zscale(val) nil();
|
||||
zscale(val) union(){};
|
||||
}
|
||||
// Verify that module at least doesn't crash.
|
||||
zscale(-5) zscale(5) nil();
|
||||
zscale(-5) zscale(5) union(){};
|
||||
}
|
||||
test_zscale();
|
||||
|
||||
|
@ -184,7 +184,7 @@ module test_mirror() {
|
|||
assert_approx(mirror(val), m, str("mirror(",val,")"));
|
||||
assert_approx(mirror(val, p=[1,2,3]), apply(m, [1,2,3]), str("mirror(",val,",p=...)"));
|
||||
// Verify that module at least doesn't crash.
|
||||
mirror(val) nil();
|
||||
mirror(val) union(){};
|
||||
}
|
||||
}
|
||||
test_mirror();
|
||||
|
@ -194,7 +194,7 @@ module test_xflip() {
|
|||
assert_approx(xflip(), [[-1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]);
|
||||
assert_approx(xflip(p=[1,2,3]), [-1,2,3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
xflip() nil();
|
||||
xflip() union(){};
|
||||
}
|
||||
test_xflip();
|
||||
|
||||
|
@ -203,7 +203,7 @@ module test_yflip() {
|
|||
assert_approx(yflip(), [[1,0,0,0],[0,-1,0,0],[0,0,1,0],[0,0,0,1]]);
|
||||
assert_approx(yflip(p=[1,2,3]), [1,-2,3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
yflip() nil();
|
||||
yflip() union(){};
|
||||
}
|
||||
test_yflip();
|
||||
|
||||
|
@ -212,7 +212,7 @@ module test_zflip() {
|
|||
assert_approx(zflip(), [[1,0,0,0],[0,1,0,0],[0,0,-1,0],[0,0,0,1]]);
|
||||
assert_approx(zflip(p=[1,2,3]), [1,2,-3]);
|
||||
// Verify that module at least doesn't crash.
|
||||
zflip() nil();
|
||||
zflip() union(){};
|
||||
}
|
||||
test_zflip();
|
||||
|
||||
|
@ -341,7 +341,7 @@ module test_xrot() {
|
|||
assert_approx(xrot(a, p=path[0]), apply(m, path[0]));
|
||||
assert_approx(xrot(a, p=path), apply(m, path));
|
||||
// Verify that module at least doesn't crash.
|
||||
xrot(a) nil();
|
||||
xrot(a) union(){};
|
||||
}
|
||||
}
|
||||
test_xrot();
|
||||
|
@ -356,7 +356,7 @@ module test_yrot() {
|
|||
assert_approx(yrot(a, p=path[0]), apply(m, path[0]));
|
||||
assert_approx(yrot(a, p=path), apply(m, path));
|
||||
// Verify that module at least doesn't crash.
|
||||
yrot(a) nil();
|
||||
yrot(a) union(){};
|
||||
}
|
||||
}
|
||||
test_yrot();
|
||||
|
@ -371,7 +371,7 @@ module test_zrot() {
|
|||
assert_approx(zrot(a, p=path[0]), apply(m, path[0]));
|
||||
assert_approx(zrot(a, p=path), apply(m, path));
|
||||
// Verify that module at least doesn't crash.
|
||||
zrot(a) nil();
|
||||
zrot(a) union(){};
|
||||
}
|
||||
}
|
||||
test_zrot();
|
||||
|
@ -390,7 +390,7 @@ module test_skew() {
|
|||
assert_approx(skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7), m);
|
||||
assert_approx(skew(sxy=2, sxz=3, syx=4, syz=5, szx=6, szy=7, p=[1,2,3]), apply(m,[1,2,3]));
|
||||
// Verify that module at least doesn't crash.
|
||||
skew(undef,2,3,4,5,6,7) nil();
|
||||
skew(undef,2,3,4,5,6,7) union(){};
|
||||
}
|
||||
test_skew();
|
||||
|
||||
|
|
|
@ -535,7 +535,6 @@ module npt_threaded_rod(
|
|||
higbee=r1*PI/2
|
||||
);
|
||||
if (hollow) cylinder(l=l+1, d=size*INCH, center=true);
|
||||
else nil();
|
||||
}
|
||||
children();
|
||||
}
|
||||
|
|
11
vnf.scad
11
vnf.scad
|
@ -783,14 +783,15 @@ function _slice_3dpolygons(polys, dir, cuts) =
|
|||
// vnf = A VNF structure, or list of VNF structures.
|
||||
// convexity = Max number of times a line could intersect a wall of the shape.
|
||||
// extent = If true, calculate anchors by extents, rather than intersection. Default: true.
|
||||
// cp = Centerpoint of VNF to use for anchoring when `extent` is false. Default: `[0, 0, 0]`
|
||||
// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `"origin"`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
|
||||
module vnf_polyhedron(vnf, convexity=2, extent=true, cp=[0,0,0], anchor="origin", spin=0, orient=UP) {
|
||||
// atype = Select "hull" or "intersect" anchor type. Default: "hull"
|
||||
module vnf_polyhedron(vnf, convexity=2, extent=true, cp="centroid", anchor="origin", spin=0, orient=UP, atype="hull") {
|
||||
vnf = is_vnf_list(vnf)? vnf_join(vnf) : vnf;
|
||||
cp = is_def(cp) ? cp : centroid(vnf);
|
||||
attachable(anchor,spin,orient, vnf=vnf, extent=extent, cp=cp) {
|
||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
|
||||
attachable(anchor,spin,orient, vnf=vnf, extent=atype=="hull", cp=cp) {
|
||||
polyhedron(vnf[0], vnf[1], convexity=convexity);
|
||||
children();
|
||||
}
|
||||
|
@ -876,7 +877,7 @@ function vnf_area(vnf) =
|
|||
/// The centroid of a tetrahedron is the average of its vertices.
|
||||
/// The centroid of the total is the volume weighted average.
|
||||
function _vnf_centroid(vnf,eps=EPSILON) =
|
||||
assert(is_vnf(vnf) && len(vnf[0])!=0 )
|
||||
assert(is_vnf(vnf) && len(vnf[0])!=0 && len(vnf[1])!=0,"Invalid or empty VNF given to centroid")
|
||||
let(
|
||||
verts = vnf[0],
|
||||
pos = sum([
|
||||
|
|
Loading…
Reference in a new issue