Reworked attachable() internals, and provided geometry only function equivalent called reorient().

This commit is contained in:
Revar Desmera 2020-03-06 15:32:53 -08:00
parent 863398eb24
commit 6ec5013835
5 changed files with 434 additions and 190 deletions

View file

@ -89,7 +89,6 @@ $tags_hidden = [];
// Section: Functions // Section: Functions
// Function: anchorpt() // Function: anchorpt()
// Usage: // Usage:
// anchor(name, pos, [dir], [rot]) // 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 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() // Function: attach_geom_2d()
// Usage: // Usage:
// attach_geom_2d(geom); // attach_geom_2d(geom);
@ -156,6 +291,65 @@ function attach_geom_size(geom) =
assert(false, "Unknown attachment geometry type."); 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() // Function: find_anchor()
// Usage: // Usage:
@ -325,6 +519,18 @@ function find_anchor(anchor, geom) =
assert(false, "Unknown attachment geometry type."); 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="") = function _str_char_split(s,delim,n=0,acc=[],word="") =
(n>=len(s))? concat(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])); _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 // Section: Attachability Modules
// Module: attachable() // Module: attachable()
// //
// Usage: // Usage:
@ -478,7 +762,7 @@ module attachable(
spin=0, spin=0,
orient=UP, orient=UP,
size, size2, shift, size, size2, shift,
r,r1,r2, d,d1,d2, l, r,r1,r2, d,d1,d2, l,h,
vnf, path, vnf, path,
extent=true, extent=true,
offset=[0,0,0], offset=[0,0,0],
@ -486,110 +770,32 @@ module attachable(
two_d=false two_d=false
) { ) {
assert($children==2); assert($children==2);
assert(is_string(anchor) || is_vector(anchor)); geom = attach_geom(
assert(is_num(spin)); size=size, size2=size2, shift=shift,
assert(is_vector(orient)); r=r, r1=r1, r2=r2, h=h,
assert(is_vector(offset)); d=d, d1=d1, d2=d2, l=l,
assert(is_list(anchors)); 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)? ( $attach_to = undef;
two_d? ( $tags_shown = undef;
let( $tags_hidden = undef;
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.");
pos = find_anchor(anchor, geom)[1]; if (is_undef($color)) {
size = attach_geom_size(geom); children(0);
} else color($color) {
$parent_anchor = anchor; $color = undef;
$parent_spin = spin; children(0);
$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);
}
} }
children(1); children(1);
} }
@ -600,7 +806,6 @@ module attachable(
// Section: Attachment Positioning // Section: Attachment Positioning
// Module: position() // Module: position()
// Usage: // Usage:
// position(from, [overlap]) ... // position(from, [overlap]) ...

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) // 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 // 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. // 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 // Example(2D): Typical Gear Shape
// gear2d(pitch=5, teeth=20); // gear2d(pitch=5, teeth=20);
// Example(2D): Lower Pressure Angle // Example(2D): Lower Pressure Angle
@ -254,8 +256,11 @@ function gear2d(
PA = 28, PA = 28,
clearance = undef, clearance = undef,
backlash = 0.0, backlash = 0.0,
interior = false interior = false,
anchor = CENTER,
spin = 0
) = let( ) = let(
pr = pitch_radius(pitch=pitch, teeth=teeth),
pts = concat( pts = concat(
[for (tooth = [0:1:teeth-hide-1]) [for (tooth = [0:1:teeth-hide-1])
each rot(tooth*360/teeth, each rot(tooth*360/teeth,
@ -273,7 +278,7 @@ function gear2d(
], ],
hide>0? [[0,0]] : [] hide>0? [[0,0]] : []
) )
) pts; ) attachable(anchor,spin, two_d=true, r=pr, p=pts);
module gear2d( module gear2d(
@ -283,19 +288,23 @@ module gear2d(
PA = 28, PA = 28,
clearance = undef, clearance = undef,
backlash = 0.0, backlash = 0.0,
interior = false interior = false,
anchor = CENTER,
spin = 0
) { ) {
polygon( path = gear2d(
gear2d( pitch = pitch,
pitch = pitch, teeth = teeth,
teeth = teeth, hide = hide,
hide = hide, PA = PA,
PA = PA, clearance = clearance,
clearance = clearance, backlash = backlash,
backlash = backlash, interior = interior
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) assert(is_num(rounding) || len(rounding)==4)
let( let(
size = is_num(size)? [size,size] : point2d(size), 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( (rounding==0 && chamfer==0)? let(
path = [ 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)]] 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() // Function&Module: circle()
@ -142,7 +145,7 @@ function circle(r, d, realign=false, circum=false, anchor=CENTER, spin=0) =
offset = realign? 180/sides : 0, offset = realign? 180/sides : 0,
rr = r / (circum? cos(180/sides) : 1), 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)]] 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]); l = first_defined([h, l, 1]);
hh = l/2; hh = l/2;
sides = segs(max(r1,r2)); sides = segs(max(r1,r2));
size = [r1*2, r1*2, l];
path = [[0,hh],[r2,hh],[r1,-hh],[0,-hh]]; path = [[0,hh],[r2,hh],[r1,-hh],[0,-hh]];
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
rotate_extrude(convexity=2, $fn=sides) { 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); r = get_radius(r=r, d=d, dflt=1);
sides = segs(r); sides = segs(r);
size = [r*2, r*2, r*2];
attachable(anchor,spin,orient, r=r) { attachable(anchor,spin,orient, r=r) {
rotate_extrude(convexity=2) { rotate_extrude(convexity=2) {
difference() { difference() {

View file

@ -562,13 +562,11 @@ function _turtle_command(command, parm, parm2, state, index) =
rot(delta_angle,p=state[step],planar=true) rot(delta_angle,p=state[step],planar=true)
] ]
) : ) :
assert(false,str("Unknown turtle command \"",command,"\" at index",index)) assert(false,str("Unknown turtle command \"",command,"\" at index",index))
[]; [];
// Section: 2D N-Gons // Section: 2D N-Gons
// Function&Module: regular_ngon() // 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)] + (r-rounding*sc)*[cos(a),sin(a)] +
rounding*[cos(b),sin(b)] 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) { 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() // Function&Module: pentagon()
// Usage: // Usage:
// pentagon(or|od, [realign]); // pentagon(or|od, [realign]);
// pentagon(ir|id, [realign]; // pentagon(ir|id, [realign]);
// pentagon(side, [realign]; // pentagon(side, [realign]);
// Description: // Description:
// When called as a function, returns a 2D path for a regular pentagon. // When called as a function, returns a 2D path for a regular pentagon.
// When called as a module, creates a 2D 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)); // stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
function trapezoid(h, w1, w2, anchor=CENTER, spin=0) = function trapezoid(h, w1, w2, anchor=CENTER, spin=0) =
let( 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]] 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) { 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) { attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2) {
polygon(path); polygon(path);
children(); children();
@ -846,12 +843,14 @@ function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) =
ea = 360 + ang, ea = 360 + ang,
steps = segs(r)*(ea-sa)/360, steps = segs(r)*(ea-sa)/360,
step = (ea-sa)/steps, step = (ea-sa)/steps,
path = concat( 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],
[[-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, subarc = ea2-sa2,
arcsegs = ceil(segs(r2)*abs(subarc)/360), arcsegs = ceil(segs(r2)*abs(subarc)/360),
arcstep = subarc / arcsegs, arcstep = subarc / arcsegs,
s = [spread/2+r, r],
path = concat( path = concat(
[for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep) r * [cos(a),sin(a)] - cp1], [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], 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], [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] 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) { 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), ir = get_radius(r=ir, d=id, dflt=stepr),
offset = 90+(realign? 180/n : 0), 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)]] 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) { 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); 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); polygon(path);
children(); 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)], 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, 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)]] 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) { 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); 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); polygon(path);
children(); 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) // cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_roundover(r=10, inset=2); // mask2d_roundover(r=10, inset=2);
module mask2d_roundover(r, d, excess, inset=0) { module mask2d_roundover(r, d, excess, inset=0, anchor=CENTER,spin=0) {
polygon(mask2d_roundover(r=r,d=d,excess=excess,inset=inset)); 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_num(r)||is_num(d))
assert(is_undef(excess)||is_num(excess)) assert(is_undef(excess)||is_num(excess))
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) 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), excess = default(excess,$overlap),
r = get_radius(r=r,d=d,dflt=1), r = get_radius(r=r,d=d,dflt=1),
steps = quantup(segs(r),4)/4, steps = quantup(segs(r),4)/4,
step = 90/steps step = 90/steps,
) [ path = [
[-excess,-excess], [-excess, r+inset.y], [-excess,-excess], [-excess, r+inset.y],
for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step), for (i=[0:1:steps]) [r,r] + inset + polar_to_xy(r,180+i*step),
[r+inset.x,-excess] [r+inset.x,-excess]
]; ]
) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path);
// Function&Module: mask2d_cove() // Function&Module: mask2d_cove()
@ -1099,11 +1102,15 @@ function mask2d_roundover(r, d, excess, inset=0) =
// cube([50,60,70],center=true) // cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_cove(r=10, inset=2); // mask2d_cove(r=10, inset=2);
module mask2d_cove(r, d, inset=0, excess) { module mask2d_cove(r, d, inset=0, excess, anchor=CENTER,spin=0) {
polygon(mask2d_cove(r=r,d=d,excess=excess,inset=inset)); 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_num(r)||is_num(d))
assert(is_undef(excess)||is_num(excess)) assert(is_undef(excess)||is_num(excess))
assert(is_num(inset)||(is_vector(inset)&&len(inset)==2)) 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), excess = default(excess,$overlap),
r = get_radius(r=r,d=d,dflt=1), r = get_radius(r=r,d=d,dflt=1),
steps = quantup(segs(r),4)/4, steps = quantup(segs(r),4)/4,
step = 90/steps step = 90/steps,
) [ path = [
[-excess,-excess], [-excess, r+inset.y], [-excess,-excess], [-excess, r+inset.y],
for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step), for (i=[0:1:steps]) inset + polar_to_xy(r,90-i*step),
[r+inset.x,-excess] [r+inset.x,-excess]
]; ]
) reorient(anchor,spin, two_d=true, path=path, p=path);
// Function&Module: mask2d_chamfer() // Function&Module: mask2d_chamfer()
@ -1147,11 +1155,15 @@ function mask2d_cove(r, d, inset=0, excess) =
// cube([50,60,70],center=true) // cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_chamfer(x=10, inset=2); // mask2d_chamfer(x=10, inset=2);
module mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) { module mask2d_chamfer(x, y, edge, angle=45, excess, inset=0, anchor=CENTER,spin=0) {
polygon(mask2d_chamfer(x=x, y=y, edge=edge, angle=angle, excess=excess, inset=inset)); 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(num_defined([x,y,edge])==1)
assert(is_num(first_defined([x,y,edge]))) assert(is_num(first_defined([x,y,edge])))
assert(is_num(angle)) assert(is_num(angle))
@ -1163,12 +1175,13 @@ function mask2d_chamfer(x, y, edge, angle=45, excess, inset=0) =
x = !is_undef(x)? x : x = !is_undef(x)? x :
!is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
hyp_ang_to_opp(hyp=edge,ang=angle), hyp_ang_to_opp(hyp=edge,ang=angle),
y = opp_ang_to_adj(opp=x,ang=angle) y = opp_ang_to_adj(opp=x,ang=angle),
) [ path = [
[-excess, -excess], [-excess, y+inset.y], [-excess, -excess], [-excess, y+inset.y],
[inset.x, y+inset.y], [x+inset.x, inset.y], [inset.x, y+inset.y], [x+inset.x, inset.y],
[x+inset.x, -excess] [x+inset.x, -excess]
]; ]
) reorient(anchor,spin, two_d=true, path=path, extent=true, p=path);
// Function&Module: mask2d_rabbet() // 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) // cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_rabbet(size=10); // mask2d_rabbet(size=10);
module mask2d_rabbet(size, excess) { module mask2d_rabbet(size, excess, anchor=CENTER,spin=0) {
polygon(mask2d_rabbet(size=size, excess=excess)); 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_num(size)||(is_vector(size)&&len(size)==2))
assert(is_undef(excess)||is_num(excess)) assert(is_undef(excess)||is_num(excess))
let( let(
excess = default(excess,$overlap), excess = default(excess,$overlap),
size = is_list(size)? size : [size,size] size = is_list(size)? size : [size,size],
) [ path = [
[-excess, -excess], [-excess, size.y], size, [size.x, -excess] [-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() // Function&Module: mask2d_dovetail()
@ -1234,11 +1253,15 @@ function mask2d_rabbet(size, excess) =
// cube([50,60,70],center=true) // cube([50,60,70],center=true)
// edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT])
// mask2d_dovetail(x=10, inset=2); // mask2d_dovetail(x=10, inset=2);
module mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess) { module mask2d_dovetail(x, y, edge, angle=30, inset=0, shelf=0, excess, anchor=CENTER, spin=0) {
polygon(mask2d_dovetail(x=x, y=y, edge=edge, angle=angle, inset=inset, shelf=shelf, excess=excess)); 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(num_defined([x,y,edge])==1)
assert(is_num(first_defined([x,y,edge]))) assert(is_num(first_defined([x,y,edge])))
assert(is_num(angle)) 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 : x = !is_undef(x)? x :
!is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) : !is_undef(y)? adj_ang_to_opp(adj=y,ang=angle) :
hyp_ang_to_opp(hyp=edge,ang=angle), hyp_ang_to_opp(hyp=edge,ang=angle),
y = opp_ang_to_adj(opp=x,ang=angle) y = opp_ang_to_adj(opp=x,ang=angle),
) [ path = [
[-excess, 0], [-excess, y+inset.y+shelf], [-excess, 0], [-excess, y+inset.y+shelf],
inset+[x,y+shelf], inset+[x,y], inset, [inset.x,0] 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() // 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. // "ystep",1, "xstep",1 // Ending shoulder.
// ]); // ]);
module mask2d_ogee(pattern, excess) { 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) = function mask2d_ogee(pattern, excess) =
@ -1356,8 +1384,9 @@ function mask2d_ogee(pattern, excess) =
a = pat[0]=="round"? (180+i*step) : (90-i*step) a = pat[0]=="round"? (180+i*step) : (90-i*step)
) pat[2] + abs(r)*[cos(a),sin(a)] ) 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,163];
// Section: BOSL Library Version Functions // Section: BOSL Library Version Functions