Merge branch 'master' into master

This commit is contained in:
Revar Desmera 2020-03-07 16:31:21 -08:00 committed by GitHub
commit c152d393db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 460 additions and 207 deletions

View file

@ -89,7 +89,6 @@ $tags_hidden = [];
// Section: Functions
// Function: anchorpt()
// Usage:
// anchor(name, pos, [dir], [rot])
@ -103,6 +102,142 @@ $tags_hidden = [];
function anchorpt(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, spin];
// Function: attach_geom()
//
// Usage:
// geom = attach_geom(anchor, spin, [orient], two_d, size, [size2], [shift], [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], two_d, r|d, [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], two_d, path, [extent], [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], size, [size2], [shift], [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], r|d, l, [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], r1|d1, r2|d2, l, [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], r|d, [offset], [anchors]);
// geom = attach_geom(anchor, spin, [orient], vnf, [extent], [offset], [anchors]);
//
// Description:
// Given arguments that describe the geometry of an attachable object, returns the internal geometry description.
//
// Arguments:
// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length.
// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape.
// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift.
// r = Radius of the cylindrical/conical volume.
// d = Diameter of the cylindrical/conical volume.
// r1 = Radius of the bottom of the conical volume.
// r2 = Radius of the top of the conical volume.
// d1 = Diameter of the bottom of the conical volume.
// d2 = Diameter of the top of the conical volume.
// l = Length of the cylindrical/conical volume along axis.
// vnf = The [VNF](vnf.scad) of the volume.
// path = The path to generate a polygon from.
// extent = If true, calculate anchors by extents, rather than intersection. Default: false.
// offset = If given, offsets the center of the volume.
// 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)
//
// Example(NORENDER): Cubical Shape
// geom = attach_geom(anchor, spin, orient, size=size);
//
// Example(NORENDER): Prismoidal Shape
// geom = attach_geom(
// anchor, spin, orient,
// size=point3d(botsize,h),
// size2=topsize, shift=shift
// );
//
// Example(NORENDER): Cylindrical Shape
// geom = attach_geom(anchor, spin, orient, r=r, h=h);
//
// Example(NORENDER): Conical Shape
// geom = attach_geom(anchor, spin, orient, r1=r1, r2=r2, h=h);
//
// Example(NORENDER): Spherical Shape
// geom = attach_geom(anchor, spin, orient, r=r);
//
// Example(NORENDER): Arbitrary VNF Shape
// geom = attach_geom(anchor, spin, orient, vnf=vnf);
//
// Example(NORENDER): 2D Rectangular Shape
// geom = attach_geom(anchor, spin, orient, size=size);
//
// Example(NORENDER): 2D Trapezoidal Shape
// geom = attach_geom(
// anchor, spin, orient,
// size=[x1,y], size2=x2, shift=shift
// );
//
// Example(NORENDER): 2D Circular Shape
// geom = attach_geom(anchor, spin, orient, two_d=true, r=r);
//
// Example(NORENDER): Arbitrary 2D Polygon Shape
// geom = attach_geom(anchor, spin, orient, path=path);
//
function attach_geom(
size, size2, shift,
r,r1,r2, d,d1,d2, l,h,
vnf, path,
extent=true,
offset=[0,0,0],
anchors=[],
two_d=false
) =
assert(is_vector(offset))
assert(is_list(anchors))
!is_undef(size)? (
two_d? (
let(
size2 = default(size2, size.x),
shift = default(shift, 0)
)
assert(is_vector(size) && len(size)==2)
assert(is_num(size2))
assert(is_num(shift))
["rect", point2d(size), size2, shift, offset, anchors]
) : (
let(
size2 = default(size2, point2d(size)),
shift = default(shift, [0,0])
)
assert(is_vector(size) && len(size)==3)
assert(is_vector(size2) && len(size2)==2)
assert(is_vector(shift) && len(shift)==2)
["cuboid", size, size2, shift, offset, anchors]
)
) : !is_undef(vnf)? (
assert(is_vnf(vnf))
assert(two_d == false)
extent? ["vnf_extent", vnf, offset, anchors] :
["vnf_isect", vnf, offset, anchors]
) : !is_undef(path)? (
assert(is_path(path))
assert(two_d == true)
extent? ["path_extent", path, offset, anchors] :
["path_isect", path, offset, anchors]
) :
let(
r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef)
)
!is_undef(r1)? (
assert(is_num(r1))
let( l = default(l, h) )
!is_undef(l)? (
let(
shift = default(shift, [0,0]),
r2 = get_radius(r1=r2,d1=d2,r=r,d=d,dflt=undef)
)
assert(is_num(l))
assert(is_num(r2))
assert(is_vector(shift) && len(shift)==2)
["cyl", r1, r2, l, shift, offset, anchors]
) : (
two_d? ["circle", r1, offset, anchors] :
["spheroid", r1, offset, anchors]
)
) :
assert(false, "Unrecognizable geometry description.");
// Function: attach_geom_2d()
// Usage:
// attach_geom_2d(geom);
@ -156,6 +291,65 @@ function attach_geom_size(geom) =
assert(false, "Unknown attachment geometry type.");
// Function: attach_transform()
// Usage:
// mat = attach_transform(anchor=CENTER, spin=0, orient=UP, geom);
// Description:
// Returns the affine3d transformation matrix needed to `anchor`, `spin`, and `orient`
// the given geometry `geom` shape into position.
// Arguments:
// anchor = Anchor point to translate to the 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`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// geom = The geometry description of the shape.
// p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result.
function attach_transform(anchor=CENTER, spin=0, orient=UP, geom, p) =
assert(is_string(anchor) || is_vector(anchor))
assert(is_num(spin))
assert(is_vector(orient))
let(
two_d = attach_geom_2d(geom),
m = ($attach_to != undef)? (
let(
anch = find_anchor($attach_to, geom),
pos = anch[1]
) two_d? (
let(
ang = vector_angle(anch[2], BACK)
)
affine3d_zrot(ang+spin) *
affine3d_translate(point3d(-pos))
) : (
let(
ang = vector_angle(anch[2], DOWN),
axis = vector_axis(anch[2], DOWN),
ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3],
axis2 = rotate_points3d([axis],[0,0,ang2])[0]
)
affine3d_rot_by_axis(axis2,ang) *
affine3d_zrot(ang2+spin) *
affine3d_translate(point3d(-pos))
)
) : (
let(
pos = find_anchor(anchor, geom)[1]
) two_d? (
affine3d_zrot(spin) *
affine3d_translate(point3d(-pos))
) : (
let(
axis = vector_axis(UP,orient),
ang = vector_angle(UP,orient)
)
affine3d_rot_by_axis(axis,ang) *
affine3d_zrot(spin) *
affine3d_translate(point3d(-pos))
)
)
) is_undef(p)? m :
is_vnf(p)? [apply(m, p[0]), p[1]] :
apply(m, p);
// Function: find_anchor()
// Usage:
@ -325,6 +519,18 @@ function find_anchor(anchor, geom) =
assert(false, "Unknown attachment geometry type.");
// Function: attachment_is_shown()
// Usage:
// attachment_is_shown(tags);
// Description:
// Returns true if the given space-delimited string of tag names should currently be shown.
function attachment_is_shown(tags) =
let(
tags = _str_char_split(tags, " "),
shown = !$tags_shown || any([for (tag=tags) in_list(tag, $tags_shown)]),
hidden = any([for (tag=tags) in_list(tag, $tags_hidden)])
) shown && !hidden;
function _str_char_split(s,delim,n=0,acc=[],word="") =
(n>=len(s))? concat(acc, [word]) :
@ -333,10 +539,88 @@ function _str_char_split(s,delim,n=0,acc=[],word="") =
_str_char_split(s,delim,n+1,acc,str(word,s[n]));
// Function: reorient()
//
// Usage:
// reorient(anchor, spin, [orient], two_d, size, [size2], [shift], [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], two_d, r|d, [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], two_d, path, [extent], [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], size, [size2], [shift], [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], r|d, l, [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], r1|d1, r2|d2, l, [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], r|d, [offset], [anchors], [p]);
// reorient(anchor, spin, [orient], vnf, [extent], [offset], [anchors], [p]);
//
// Description:
// Given anchor, spin, orient, and general geometry info for a managed volume, this calculates
// the transformation matrix needed to be applied to the contents of that volume. A managed 3D
// volume is assumed to be vertically (Z-axis) oriented, and centered. A managed 2D area is just
// assumed to be centered.
//
// If `p` is not given, then the transformation matrix will be returned.
// If `p` contains a VNF, a new VNF will be returned with the vertices transformed by the matrix.
// If `p` contains a path, a new path will be returned with the vertices transformed by the matrix.
// If `p` contains a point, a new point will be returned, transformed by the matrix.
//
// If `$attach_to` is not defined, then the following transformations are performed in order:
// * Translates so the `anchor` point is at the origin (0,0,0).
// * Rotates around the Z axis by `spin` degrees counter-clockwise.
// * Rotates so the top of the part points towards the vector `orient`.
//
// If `$attach_to` is defined, as a consequence of `attach(from,to)`, then
// the following transformations are performed in order:
// * Translates this part so it's anchor position matches the parent's anchor position.
// * Rotates this part so it's anchor direction vector exactly opposes the parent's anchor direction vector.
// * Rotates this part so it's anchor spin matches the parent's anchor spin.
//
// Arguments:
// 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`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// size = If given as a 3D vector, contains the XY size of the bottom of the cuboidal/prismoidal volume, and the Z height. If given as a 2D vector, contains the front X width of the rectangular/trapezoidal shape, and the Y length.
// size2 = If given as a 2D vector, contains the XY size of the top of the prismoidal volume. If given as a number, contains the back width of the trapezoidal shape.
// shift = If given as a 2D vector, shifts the top of the prismoidal or conical shape by the given amount. If given as a number, shifts the back of the trapezoidal shape right by that amount. Default: No shift.
// r = Radius of the cylindrical/conical volume.
// d = Diameter of the cylindrical/conical volume.
// r1 = Radius of the bottom of the conical volume.
// r2 = Radius of the top of the conical volume.
// d1 = Diameter of the bottom of the conical volume.
// d2 = Diameter of the top of the conical volume.
// l = Length of the cylindrical/conical volume along axis.
// vnf = The [VNF](vnf.scad) of the volume.
// path = The path to generate a polygon from.
// extent = If true, calculate anchors by extents, rather than intersection. Default: false.
// offset = If given, offsets the center of the volume.
// 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)
// p = The VNF, path, or point to transform.
function reorient(
anchor=CENTER,
spin=0,
orient=UP,
size, size2, shift,
r,r1,r2, d,d1,d2, l,h,
vnf, path,
extent=true,
offset=[0,0,0],
anchors=[],
two_d=false,
p=undef
) = let(
geom = attach_geom(
size=size, size2=size2, shift=shift,
r=r, r1=r1, r2=r2, h=h,
d=d, d1=d1, d2=d2, l=l,
vnf=vnf, path=path, extent=extent,
offset=offset, anchors=anchors,
two_d=two_d
)
) attach_transform(anchor,spin,orient,geom,p);
// Section: Attachability Modules
// Module: attachable()
//
// Usage:
@ -478,7 +762,7 @@ module attachable(
spin=0,
orient=UP,
size, size2, shift,
r,r1,r2, d,d1,d2, l,
r,r1,r2, d,d1,d2, l,h,
vnf, path,
extent=true,
offset=[0,0,0],
@ -486,110 +770,32 @@ module attachable(
two_d=false
) {
assert($children==2);
assert(is_string(anchor) || is_vector(anchor));
assert(is_num(spin));
assert(is_vector(orient));
assert(is_vector(offset));
assert(is_list(anchors));
geom = attach_geom(
size=size, size2=size2, shift=shift,
r=r, r1=r1, r2=r2, h=h,
d=d, d1=d1, d2=d2, l=l,
vnf=vnf, path=path, extent=extent,
offset=offset, anchors=anchors,
two_d=two_d
);
m = attach_transform(anchor,spin,orient,geom);
multmatrix(m) {
if (attachment_is_shown($tags)) {
$parent_anchor = anchor;
$parent_spin = spin;
$parent_orient = orient;
$parent_geom = geom;
$parent_size = attach_geom_size(geom);
geom = !is_undef(size)? (
two_d? (
let(
size2 = default(size2, size.x),
shift = default(shift, 0)
)
assert(is_vector(size) && len(size)==2)
assert(is_num(size2))
assert(is_num(shift))
["rect", point2d(size), size2, shift, offset, anchors]
) : (
let(
size2 = default(size2, point2d(size)),
shift = default(shift, [0,0])
)
assert(is_vector(size) && len(size)==3)
assert(is_vector(size2) && len(size2)==2)
assert(is_vector(shift) && len(shift)==2)
["cuboid", size, size2, shift, offset, anchors]
)
) : !is_undef(vnf)? (
assert(is_vnf(vnf))
assert(two_d == false)
extent? ["vnf_extent", vnf, offset, anchors] :
["vnf_isect", vnf, offset, anchors]
) : !is_undef(path)? (
assert(is_path(path))
assert(two_d == true)
extent? ["path_extent", path, offset, anchors] :
["path_isect", path, offset, anchors]
) :
let(
r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef)
)
!is_undef(r1)? (
assert(is_num(r1))
!is_undef(l)? (
let(
shift = default(shift, [0,0]),
r2 = get_radius(r1=r2,d1=d2,r=r,d=d,dflt=undef)
)
assert(is_num(l))
assert(is_num(r2))
assert(is_vector(shift) && len(shift)==2)
["cyl", r1, r2, l, shift, offset, anchors]
) : (
two_d? ["circle", r1, offset, anchors] :
["spheroid", r1, offset, anchors]
)
) :
assert(false, "attachable(): Unrecognizable geometry description.");
$attach_to = undef;
$tags_shown = undef;
$tags_hidden = undef;
pos = find_anchor(anchor, geom)[1];
size = attach_geom_size(geom);
$parent_anchor = anchor;
$parent_spin = spin;
$parent_orient = orient;
$parent_geom = geom;
$parent_size = size;
tags = _str_char_split($tags, " ");
s_tags = $tags_shown;
h_tags = $tags_hidden;
shown = !s_tags || any([for (tag=tags) in_list(tag, s_tags)]);
hidden = any([for (tag=tags) in_list(tag, h_tags)]);
if ($attach_to != undef) {
anch = find_anchor($attach_to, geom);
ang = vector_angle(anch[2], two_d? BACK : DOWN);
axis = two_d? UP : vector_axis(anch[2], DOWN);
ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3];
axis2 = rotate_points3d([axis],[0,0,ang2])[0];
$attach_to = undef;
rot(ang, v=axis2)
rotate(ang2+spin)
translate(-anch[1]) {
if(shown && !hidden) {
if (is_undef($color)) {
children(0);
} else color($color) {
$color = undef;
children(0);
}
}
children(1);
}
} else {
rot(from=UP,to=orient)
rotate(spin)
translate(-pos) {
if(shown && !hidden) {
if (is_undef($color)) {
children(0);
} else color($color) {
$color = undef;
children(0);
}
if (is_undef($color)) {
children(0);
} else color($color) {
$color = undef;
children(0);
}
children(1);
}
@ -600,7 +806,6 @@ module attachable(
// Section: Attachment Positioning
// Module: position()
// Usage:
// position(from, [overlap]) ...

View file

@ -161,17 +161,26 @@ function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) =
// echo(bezier_segment_length(bez));
function bezier_segment_length(curve, start_u=0, end_u=1, max_deflect=0.01) =
let(
mid_u=lerp(start_u, end_u, 0.5),
sp = bez_point(curve,start_u),
bez_mp = bez_point(curve,mid_u),
ep = bez_point(curve,end_u),
lin_mp = lerp(sp,ep,0.5),
defl = norm(bez_mp-lin_mp)
segs = len(curve) * 2,
path = [
for (i=[0:1:segs])
let(u=lerp(start_u, end_u, i/segs))
bez_point(curve,u)
],
defl = max([
for (i=idx(path,end=-3)) let(
mp = (path[i] + path[i+2]) / 2
) norm(path[i+1] - mp)
]),
mid_u = lerp(start_u, end_u, 0.5)
)
((end_u-start_u) >= 0.125 || defl > max_deflect)? (
bezier_segment_length(curve, start_u, mid_u, max_deflect) +
bezier_segment_length(curve, mid_u, end_u, max_deflect)
) : norm(ep-sp);
defl <= max_deflect? path_length(path) :
sum([
for (i=[0:1:segs-1]) let(
su = lerp(start_u, end_u, i/segs),
eu = lerp(start_u, end_u, (i+1)/segs)
) bezier_segment_length(curve, su, eu, max_deflect)
]);

View file

@ -569,7 +569,7 @@ function plane3pt(p1, p2, p3) =
// Given a list of points, and the indices of three of those points,
// generates the cartesian equation of a plane that those points all
// lie on. Requires that the three indexed points be non-collinear.
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of a plane.
// Returns [A,B,C,D] where Ax+By+Cz=D is the equation of a plane.
// Arguments:
// points = A list of points.
// i1 = The index into `points` of the first point on the plane.
@ -628,7 +628,7 @@ function plane_from_normal(normal, pt) =
// plane_from_pointslist(points);
// Description:
// Given a list of 3 or more coplanar points, returns the cartesian equation of a plane.
// Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of the plane.
// Returns [A,B,C,D] where Ax+By+Cz=D is the equation of the plane.
function plane_from_pointslist(points) =
let(
points = deduplicate(points),
@ -653,7 +653,7 @@ function plane_normal(plane) = [for (i=[0:2]) plane[i]];
// distance_from_plane(plane, point)
// Description:
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
// is Ax+By+Cz+D=0, determines how far from that plane the given point is.
// is Ax+By+Cz=D, determines how far from that plane the given point is.
// The returned distance will be positive if the point is in front of the
// plane; on the same side of the plane as the normal of that plane points
// towards. If the point is behind the plane, then the distance returned
@ -669,7 +669,7 @@ function distance_from_plane(plane, point) =
// Usage:
// pt = closest_point_on_plane(plane, point);
// Description:
// Takes a point, and a plane [A,B,C,D] where the equation of that plane is `Ax+By+Cz+D=0`.
// Takes a point, and a plane [A,B,C,D] where the equation of that plane is `Ax+By+Cz=D`.
// Returns the coordinates of the closest point on that plane to the given `point`.
// Arguments:
// plane = The [A,B,C,D] values for the equation of the plane.
@ -716,7 +716,7 @@ function plane_line_angle(plane, line) =
// Usage:
// pt = plane_line_intersection(plane, line, [eps]);
// Description:
// Takes a line, and a plane [A,B,C,D] where the equation of that plane is `Ax+By+Cz+D=0`.
// Takes a line, and a plane [A,B,C,D] where the equation of that plane is `Ax+By+Cz=D`.
// Returns the coordinates of the where the given `line` intersects the given `plane`.
// Returns `undef` if the line is parallel to the plane.
// Arguments:
@ -775,7 +775,7 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) =
// coplanar(plane, point);
// Description:
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
// is Ax+By+Cz+D=0, determines if the given point is on that plane.
// is Ax+By+Cz=D, determines if the given point is on that plane.
// Returns true if the point is on that plane.
// Arguments:
// plane = The [A,B,C,D] values for the equation of the plane.
@ -789,7 +789,7 @@ function coplanar(plane, point) =
// in_front_of_plane(plane, point);
// Description:
// Given a plane as [A,B,C,D] where the cartesian equation for that plane
// is Ax+By+Cz+D=0, determines if the given point is on the side of that
// is Ax+By+Cz=D, determines if the given point is on the side of that
// plane that the normal points towards. The normal of the plane is the
// same as [A,B,C].
// Arguments:

View file

@ -241,6 +241,8 @@ module gear_tooth_profile(
// clearance = Gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
// interior = If true, create a mask for difference()ing from something else.
// 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`
// Example(2D): Typical Gear Shape
// gear2d(pitch=5, teeth=20);
// Example(2D): Lower Pressure Angle
@ -254,8 +256,11 @@ function gear2d(
PA = 28,
clearance = undef,
backlash = 0.0,
interior = false
interior = false,
anchor = CENTER,
spin = 0
) = let(
pr = pitch_radius(pitch=pitch, teeth=teeth),
pts = concat(
[for (tooth = [0:1:teeth-hide-1])
each rot(tooth*360/teeth,
@ -273,7 +278,7 @@ function gear2d(
],
hide>0? [[0,0]] : []
)
) pts;
) attachable(anchor,spin, two_d=true, r=pr, p=pts);
module gear2d(
@ -283,19 +288,23 @@ module gear2d(
PA = 28,
clearance = undef,
backlash = 0.0,
interior = false
interior = false,
anchor = CENTER,
spin = 0
) {
polygon(
gear2d(
pitch = pitch,
teeth = teeth,
hide = hide,
PA = PA,
clearance = clearance,
backlash = backlash,
interior = interior
)
path = gear2d(
pitch = pitch,
teeth = teeth,
hide = hide,
PA = PA,
clearance = clearance,
backlash = backlash,
interior = interior
);
attachable(anchor,spin, two_d=true, r=pr) {
polygon(path);
children();
}
}

View file

@ -60,7 +60,8 @@ function square(size=1, center, rounding=0, chamfer=0, anchor, spin=0) =
assert(is_num(rounding) || len(rounding)==4)
let(
size = is_num(size)? [size,size] : point2d(size),
anchor = get_anchor(anchor, center, FRONT+LEFT, FRONT+LEFT)
anchor = get_anchor(anchor, center, FRONT+LEFT, FRONT+LEFT),
complex = rounding!=0 || chamfer!=0
)
(rounding==0 && chamfer==0)? let(
path = [
@ -97,7 +98,9 @@ function square(size=1, center, rounding=0, chamfer=0, anchor, spin=0) =
)
each [for (a = angs) cp + inset*[cos(a),sin(a)]]
]
) rot(spin, p=move(-vmul(anchor,size/2), p=path));
) complex?
reorient(anchor,spin, two_d=true, path=path, p=path) :
reorient(anchor,spin, two_d=true, size=size, p=path);
// Function&Module: circle()
@ -142,7 +145,7 @@ function circle(r, d, realign=false, circum=false, anchor=CENTER, spin=0) =
offset = realign? 180/sides : 0,
rr = r / (circum? cos(180/sides) : 1),
pts = [for (i=[0:1:sides-1]) let(a=360-offset-i*360/sides) rr*[cos(a),sin(a)]]
) rot(spin, p=move(-unit(anchor)*rr, p=pts));
) reorient(anchor,spin, two_d=true, r=rr, p=pts);
@ -235,7 +238,6 @@ module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP)
l = first_defined([h, l, 1]);
hh = l/2;
sides = segs(max(r1,r2));
size = [r1*2, r1*2, l];
path = [[0,hh],[r2,hh],[r1,-hh],[0,-hh]];
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
rotate_extrude(convexity=2, $fn=sides) {
@ -275,7 +277,6 @@ module sphere(r, d, anchor=CENTER, spin=0, orient=UP)
{
r = get_radius(r=r, d=d, dflt=1);
sides = segs(r);
size = [r*2, r*2, r*2];
attachable(anchor,spin,orient, r=r) {
rotate_extrude(convexity=2) {
difference() {

View file

@ -562,13 +562,11 @@ function _turtle_command(command, parm, parm2, state, index) =
rot(delta_angle,p=state[step],planar=true)
]
) :
assert(false,str("Unknown turtle command \"",command,"\" at index",index))
[];
// Section: 2D N-Gons
// Function&Module: regular_ngon()
@ -625,7 +623,7 @@ function regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false
(r-rounding*sc)*[cos(a),sin(a)] +
rounding*[cos(b),sin(b)]
]
) rot(spin, p=move(-r*unit(anchor), p=path));
) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) {
@ -643,8 +641,8 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false,
// Function&Module: pentagon()
// Usage:
// pentagon(or|od, [realign]);
// pentagon(ir|id, [realign];
// pentagon(side, [realign];
// pentagon(ir|id, [realign]);
// pentagon(side, [realign]);
// Description:
// When called as a function, returns a 2D path for a regular pentagon.
// When called as a module, creates a 2D regular pentagon.
@ -786,14 +784,13 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CEN
// stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
function trapezoid(h, w1, w2, anchor=CENTER, spin=0) =
let(
s = anchor.y>0? [w2,h] : anchor.y<0? [w1,h] : [(w1+w2)/2,h],
path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2]]
) rot(spin, p=move(-vmul(anchor,s/2), p=path));
) reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, p=path);
module trapezoid(h, w1, w2, anchor=CENTER, spin=0) {
path = trapezoid(h=h, w1=w1, w2=w2);
path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2]];
attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2) {
polygon(path);
children();
@ -846,12 +843,14 @@ function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) =
ea = 360 + ang,
steps = segs(r)*(ea-sa)/360,
step = (ea-sa)/steps,
path = concat(
[[ cap_w/2,cap_h]],
[for (i=[0:1:steps]) let(a=ea-i*step) r*[cos(a),sin(a)]],
[[-cap_w/2,cap_h]]
path = deduplicate(
[
[ cap_w/2,cap_h],
for (i=[0:1:steps]) let(a=ea-i*step) r*[cos(a),sin(a)],
[-cap_w/2,cap_h]
], closed=true
)
) rot(spin, p=move(-vmul(anchor,[r,cap_h]), p=deduplicate(path,closed=true)));
) reorient(anchor,spin, two_d=true, path=path, p=path);
@ -891,14 +890,13 @@ function glued_circles(r, d, spread=10, tangent=30, anchor=CENTER, spin=0) =
subarc = ea2-sa2,
arcsegs = ceil(segs(r2)*abs(subarc)/360),
arcstep = subarc / arcsegs,
s = [spread/2+r, r],
path = concat(
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep) r * [cos(a),sin(a)] - cp1],
tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep+180) r2 * [cos(a),sin(a)] - cp2],
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep+180) r * [cos(a),sin(a)] + cp1],
tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep) r2 * [cos(a),sin(a)] + cp2]
)
) rot(spin, p=move(-vmul(anchor,s), p=path));
) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path);
module glued_circles(r, d, spread=10, tangent=30, anchor=CENTER, spin=0) {
@ -951,12 +949,12 @@ function star(n, r, d, or, od, ir, id, step, realign=false, anchor=CENTER, spin=
ir = get_radius(r=ir, d=id, dflt=stepr),
offset = 90+(realign? 180/n : 0),
path = [for(i=[0:1:2*n-1]) let(theta=180*i/n+offset, radius=(i%2)?ir:r) radius*[cos(theta), sin(theta)]]
) rot(spin, p=move(-r*unit(anchor), p=path));
) reorient(anchor,spin, two_d=true, path=path, p=path);
module star(n, r, d, or, od, ir, id, step, realign=false, anchor=CENTER, spin=0) {
path = star(n=n, r=r, d=d, od=od, or=or, ir=ir, id=id, step=step, realign=realign);
attachable(anchor,spin, two_d=true, path=path, extent=true) {
attachable(anchor,spin, two_d=true, path=path) {
polygon(path);
children();
}
@ -1023,11 +1021,11 @@ function supershape(step=0.5,m1=4,m2=undef,n1=1,n2=undef,n3=undef,a=1,b=undef,r=
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 = [0:steps-1]) let(a=angs[i]) scale*rads[i]*[cos(a), sin(a)]]
) rot(spin, p=move(-scale*max(rads)*unit(anchor), p=path));
) reorient(anchor,spin, two_d=true, path=path, p=path);
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) {
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, extent=true) {
attachable(anchor,spin, two_d=true, path=path) {
polygon(path);
children();
}
@ -1057,11 +1055,15 @@ module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=und
// cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_roundover(r=10, inset=2);
module mask2d_roundover(r, d, excess, inset=0) {
polygon(mask2d_roundover(r=r,d=d,excess=excess,inset=inset));
module mask2d_roundover(r, d, excess, inset=0, anchor=CENTER,spin=0) {
path = mask2d_roundover(r=r,d=d,excess=excess,inset=inset);
attachable(anchor,spin, two_d=true, path=path, p=path) {
polygon(path);
children();
}
}
function mask2d_roundover(r, d, excess, inset=0) =
function mask2d_roundover(r, d, excess, inset=0, anchor=CENTER,spin=0) =
assert(is_num(r)||is_num(d))
assert(is_undef(excess)||is_num(excess))
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2))
@ -1070,12 +1072,13 @@ function mask2d_roundover(r, d, excess, inset=0) =
excess = default(excess,$overlap),
r = get_radius(r=r,d=d,dflt=1),
steps = quantup(segs(r),4)/4,
step = 90/steps
) [
[-excess,-excess], [-excess, r+inset.y],
for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step),
[r+inset.x,-excess]
];
step = 90/steps,
path = [
[-excess,-excess], [-excess, r+inset.y],
for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step),
[r+inset.x,-excess]
]
) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
// Function&Module: mask2d_cove()
@ -1099,11 +1102,15 @@ function mask2d_roundover(r, d, excess, inset=0) =
// cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_cove(r=10, inset=2);
module mask2d_cove(r, d, inset=0, excess) {
polygon(mask2d_cove(r=r,d=d,excess=excess,inset=inset));
module mask2d_cove(r, d, inset=0, excess, anchor=CENTER,spin=0) {
path = mask2d_cove(r=r,d=d,excess=excess,inset=inset);
attachable(anchor,spin, two_d=true, path=path) {
polygon(path);
children();
}
}
function mask2d_cove(r, d, inset=0, excess) =
function mask2d_cove(r, d, inset=0, excess, anchor=CENTER,spin=0) =
assert(is_num(r)||is_num(d))
assert(is_undef(excess)||is_num(excess))
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2))
@ -1112,12 +1119,13 @@ function mask2d_cove(r, d, inset=0, excess) =
excess = default(excess,$overlap),
r = get_radius(r=r,d=d,dflt=1),
steps = quantup(segs(r),4)/4,
step = 90/steps
) [
[-excess,-excess], [-excess, r+inset.y],
for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step),
[r+inset.x,-excess]
];
step = 90/steps,
path = [
[-excess,-excess], [-excess, r+inset.y],
for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step),
[r+inset.x,-excess]
]
) reorient(anchor,spin, two_d=true, path=path, p=path);
// Function&Module: mask2d_chamfer()
@ -1147,11 +1155,15 @@ function mask2d_cove(r, d, inset=0, excess) =
// cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_chamfer(x=10, inset=2);
module mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) {
polygon(mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset));
module mask2d_chamfer(x, y, edge, angle=45, excess, inset=0, anchor=CENTER,spin=0) {
path = mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset);
attachable(anchor,spin, two_d=true, path=path, extent=true) {
polygon(path);
children();
}
}
function mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) =
function mask2d_chamfer(x, y, edge, angle=45, excess, inset=0, anchor=CENTER,spin=0) =
assert(num_defined([x,y,edge])==1)
assert(is_num(first_defined([x,y,edge])))
assert(is_num(angle))
@ -1163,12 +1175,13 @@ function mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) =
x = !is_undef(x)? x :
!is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
hyp_ang_to_opp(hyp=edge,ang=angle),
y = opp_ang_to_adj(opp=x,ang=angle)
) [
[-excess, -excess], [-excess, y+inset.y],
[inset.x, y+inset.y], [x+inset.x, inset.y],
[x+inset.x, -excess]
];
y = opp_ang_to_adj(opp=x,ang=angle),
path = [
[-excess, -excess], [-excess, y+inset.y],
[inset.x, y+inset.y], [x+inset.x, inset.y],
[x+inset.x, -excess]
]
) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path);
// Function&Module: mask2d_rabbet()
@ -1191,19 +1204,25 @@ function mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) =
// cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_rabbet(size=10);
module mask2d_rabbet(size, excess) {
polygon(mask2d_rabbet(size=size, excess=excess));
module mask2d_rabbet(size, excess, anchor=CENTER,spin=0) {
path = mask2d_rabbet(size=size, excess=excess);
attachable(anchor,spin, two_d=true, path=path, extent=false, p=path) {
polygon(path);
children();
}
}
function mask2d_rabbet(size, excess) =
function mask2d_rabbet(size, excess, anchor=CENTER,spin=0) =
assert(is_num(size)||(is_vector(size)&&len(size)==2))
assert(is_undef(excess)||is_num(excess))
let(
excess = default(excess,$overlap),
size = is_list(size)? size : [size,size]
) [
[-excess, -excess], [-excess, size.y], size, [size.x, -excess]
];
size = is_list(size)? size : [size,size],
path = [
[-excess, -excess], [-excess, size.y],
size, [size.x, -excess]
]
) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
// Function&Module: mask2d_dovetail()
@ -1234,11 +1253,15 @@ function mask2d_rabbet(size, excess) =
// cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_dovetail(x=10, inset=2);
module mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) {
polygon(mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess));
module mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess, anchor=CENTER, spin=0) {
path = mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess);
attachable(anchor,spin, two_d=true, path=path) {
polygon(path);
children();
}
}
function mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) =
function mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess, anchor=CENTER, spin=0) =
assert(num_defined([x,y,edge])==1)
assert(is_num(first_defined([x,y,edge])))
assert(is_num(angle))
@ -1250,11 +1273,12 @@ function mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) =
x = !is_undef(x)? x :
!is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
hyp_ang_to_opp(hyp=edge,ang=angle),
y = opp_ang_to_adj(opp=x,ang=angle)
) [
[-excess, 0], [-excess, y+inset.y+shelf],
inset+[x,y+shelf], inset+[x,y], inset, [inset.x,0]
];
y = opp_ang_to_adj(opp=x,ang=angle),
path = [
[-excess, 0], [-excess, y+inset.y+shelf],
inset+[x,y+shelf], inset+[x,y], inset, [inset.x,0]
]
) reorient(anchor,spin, two_d=true, path=path, p=path);
// Function&Module: mask2d_ogee()
@ -1289,7 +1313,11 @@ function mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) =
// "ystep",1, "xstep",1 // Ending shoulder.
// ]);
module mask2d_ogee(pattern, excess) {
polygon(mask2d_ogee(pattern, excess=excess));
path = mask2d_ogee(pattern, excess=excess);
attachable(anchor,spin, two_d=true, path=path) {
polygon(path);
children();
}
}
function mask2d_ogee(pattern, excess) =
@ -1356,8 +1384,9 @@ function mask2d_ogee(pattern, excess) =
a = pat[0]=="round"? (180+i*step) : (90-i*step)
) pat[2] + abs(r)*[cos(a),sin(a)]
]
]
) deduplicate(path);
],
path2 = deduplicate(path)
) reorient(anchor,spin, two_d=true, path=path2, p=path2);

View file

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