mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-09-30 13:49:08 +02:00
534 lines
18 KiB
Markdown
534 lines
18 KiB
Markdown
# Making Attachables
|
|
|
|
To make a shape attachable, you just need to wrap it with an `attachable()` module with a
|
|
basic description of the shape's geometry. By default, the shape is expected to be centered
|
|
at the origin. This fact is important, as this is how the
|
|
`attachable()` module knows the dimensions of your part: it assumes it
|
|
is symetric around the origin. The `attachable()` module expects
|
|
exactly two children. The first will bethe shape to make attachable,
|
|
and the second will be `children()`, literally.
|
|
|
|
### Pass-through Attachables
|
|
The simplest way to make your own attachable module is to simply pass
|
|
through to a pre-existing attachable submodule. This could be
|
|
appropriate if you want to rename a module, or if the anchors of an
|
|
existing module are suited to (or good enough for) your object. In
|
|
order for your attachable module to work properly you need to accept
|
|
the `anchor`, `spin` and `orient` parameters, give them suitable
|
|
defaults, and pass them to the attachable submodule. Don't forget to
|
|
pass the children to the attachable submodule as well, or your new
|
|
module will ignore its children.
|
|
|
|
```openscad-3D
|
|
include <BOSL2/std.scad>
|
|
$fn=32;
|
|
module cutcube(anchor=CENTER,spin=0,orient=UP)
|
|
{
|
|
tag_scope(){
|
|
diff()
|
|
cuboid(15, rounding=2, anchor=anchor,spin=spin,orient=orient){
|
|
tag("remove")attach(TOP)cuboid(5);
|
|
children();
|
|
}
|
|
}
|
|
}
|
|
diff()
|
|
cutcube()
|
|
tag("remove")attach(RIGHT) cyl(d=2,h=8);
|
|
```
|
|
|
|
### Prismoidal/Cuboidal Attachables
|
|
To make a cuboidal or prismoidal shape attachable, you use the `size`, `size2`, and `offset`
|
|
arguments of `attachable()`.
|
|
|
|
In the most basic form, where the shape is fully cuboid, with top and bottom of the same size,
|
|
and directly over one another, you can just use `size=`.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
attachable(anchor,spin,orient, size=[s*3,s,s]) {
|
|
union() {
|
|
xcopies(2*s) cube(s, center=true);
|
|
xcyl(h=2*s, d=s/4);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
cubic_barbell(100) show_anchors(60);
|
|
```
|
|
|
|
When the shape is prismoidal, where the top is a different size from the bottom, you can use
|
|
the `size2=` argument as well. While `size=` takes all three axes sizes, the `size2=` argument
|
|
only takes the [X,Y] sizes of the top of the shape.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module prismoidal(size=[100,100,100], scale=0.5, anchor=CENTER, spin=0, orient=UP) {
|
|
attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale) {
|
|
hull() {
|
|
up(size.z/2-0.005)
|
|
linear_extrude(height=0.01, center=true)
|
|
square([size.x,size.y]*scale, center=true);
|
|
down(size.z/2-0.005)
|
|
linear_extrude(height=0.01, center=true)
|
|
square([size.x,size.y], center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
prismoidal([100,60,30], scale=0.5) show_anchors(20);
|
|
```
|
|
|
|
When the top of the prismoid can be shifted away from directly above the bottom, you can use
|
|
the `shift=` argument. The `shift=` argument takes an [X,Y] vector of the offset of the center
|
|
of the top from the XY center of the bottom of the shape.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module prismoidal(size=[100,100,100], scale=0.5, shift=[0,0], anchor=CENTER, spin=0, orient=UP) {
|
|
attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale, shift=shift) {
|
|
hull() {
|
|
translate([shift.x, shift.y, size.z/2-0.005])
|
|
linear_extrude(height=0.01, center=true)
|
|
square([size.x,size.y]*scale, center=true);
|
|
down(size.z/2-0.005)
|
|
linear_extrude(height=0.01, center=true)
|
|
square([size.x,size.y], center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
prismoidal([100,60,30], scale=0.5, shift=[-30,20]) show_anchors(20);
|
|
```
|
|
|
|
In the case that the prismoid is not oriented vertically, (ie, where the `shift=` or `size2=`
|
|
arguments should refer to a plane other than XY) you can use the `axis=` argument. This lets
|
|
you make prismoids naturally oriented forwards/backwards or sideways.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module yprismoidal(
|
|
size=[100,100,100], scale=0.5, shift=[0,0],
|
|
anchor=CENTER, spin=0, orient=UP
|
|
) {
|
|
attachable(
|
|
anchor, spin, orient,
|
|
size=size, size2=point2d(size)*scale,
|
|
shift=shift, axis=BACK
|
|
) {
|
|
xrot(-90) hull() {
|
|
translate([shift.x, shift.y, size.z/2-0.005])
|
|
linear_extrude(height=0.01, center=true)
|
|
square([size.x,size.y]*scale, center=true);
|
|
down(size.z/2-0.005)
|
|
linear_extrude(height=0.01, center=true)
|
|
square([size.x,size.y], center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
yprismoidal([100,60,30], scale=1.5, shift=[20,20]) show_anchors(20);
|
|
```
|
|
|
|
|
|
### Cylindrical Attachables
|
|
To make a cylindrical shape attachable, you use the `l`, and `r`/`d`, args of `attachable()`.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module twistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
|
|
r = get_radius(r=r,d=d,dflt=1);
|
|
attachable(anchor,spin,orient, r=r, l=l) {
|
|
linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
|
|
star(n=20, r=r, ir=r*0.9);
|
|
children();
|
|
}
|
|
}
|
|
twistar(l=100, r=40) show_anchors(20);
|
|
```
|
|
|
|
If the cylinder is elipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vector
|
|
to the `r=` or `d=` argument.
|
|
|
|
```openscad-3D
|
|
include <BOSL2/std.scad>
|
|
module ovalstar(l,rx,ry, anchor=CENTER, spin=0, orient=UP) {
|
|
attachable(anchor,spin,orient, r=[rx,ry], l=l) {
|
|
linear_extrude(height=l, center=true, convexity=4)
|
|
scale([1,ry/rx,1])
|
|
star(n=20, r=rx, ir=rx*0.9);
|
|
children();
|
|
}
|
|
}
|
|
ovalstar(l=100, rx=50, ry=30) show_anchors(20);
|
|
```
|
|
|
|
For cylindrical shapes that aren't oriented vertically, use the `axis=` argument.
|
|
|
|
```openscad-3D
|
|
include <BOSL2/std.scad>
|
|
module ytwistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
|
|
r = get_radius(r=r,d=d,dflt=1);
|
|
attachable(anchor,spin,orient, r=r, l=l, axis=BACK) {
|
|
xrot(-90)
|
|
linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
|
|
star(n=20, r=r, ir=r*0.9);
|
|
children();
|
|
}
|
|
}
|
|
ytwistar(l=100, r=40) show_anchors(20);
|
|
```
|
|
|
|
### Conical Attachables
|
|
To make a conical shape attachable, you use the `l`, `r1`/`d1`, and `r2`/`d2`, args of
|
|
`attachable()`.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module twistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
|
|
r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
|
|
r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
|
|
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
|
|
linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
|
|
star(n=20, r=r1, ir=r1*0.9);
|
|
children();
|
|
}
|
|
}
|
|
twistar(l=100, r1=40, r2=20) show_anchors(20);
|
|
```
|
|
|
|
If the cone is ellipsoidal in shape, you can pass the unequal X/Y sizes as a 2-item vectors
|
|
to the `r1=`/`r2=` or `d1=`/`d2=` arguments.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module ovalish(l,rx1,ry1,rx2,ry2, anchor=CENTER, spin=0, orient=UP) {
|
|
attachable(anchor,spin,orient, r1=[rx1,ry1], r2=[rx2,ry2], l=l) {
|
|
hull() {
|
|
up(l/2-0.005)
|
|
linear_extrude(height=0.01, center=true)
|
|
ellipse([rx2,ry2]);
|
|
down(l/2-0.005)
|
|
linear_extrude(height=0.01, center=true)
|
|
ellipse([rx1,ry1]);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
ovalish(l=100, rx1=50, ry1=30, rx2=30, ry2=50) show_anchors(20);
|
|
```
|
|
|
|
For conical shapes that are not oriented vertically, use the `axis=` argument to indicate the
|
|
direction of the primary shape axis:
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module ytwistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
|
|
r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
|
|
r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
|
|
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK) {
|
|
xrot(-90)
|
|
linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
|
|
star(n=20, r=r1, ir=r1*0.9);
|
|
children();
|
|
}
|
|
}
|
|
ytwistar(l=100, r1=40, r2=20) show_anchors(20);
|
|
```
|
|
|
|
### Spherical Attachables
|
|
To make a spherical shape attachable, you use the `r`/`d` args of `attachable()`.
|
|
|
|
```openscad-3D;Big
|
|
include <BOSL2/std.scad>
|
|
module spikeball(r, d, anchor=CENTER, spin=0, orient=UP) {
|
|
r = get_radius(r=r,d=d,dflt=1);
|
|
attachable(anchor,spin,orient, r=r*1.1) {
|
|
union() {
|
|
sphere_copies(r=r, n=512, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
|
|
sphere(r=r);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
spikeball(r=50) show_anchors(20);
|
|
```
|
|
|
|
If the shape is an ellipsoid, you can pass a 3-item vector of sizes to `r=` or `d=`.
|
|
|
|
```openscad-3D
|
|
include <BOSL2/std.scad>
|
|
module spikeball(r, d, scale, anchor=CENTER, spin=0, orient=UP) {
|
|
r = get_radius(r=r,d=d,dflt=1);
|
|
attachable(anchor,spin,orient, r=r*1.1*scale) {
|
|
union() {
|
|
sphere_copies(r=r, n=512, scale=scale, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
|
|
scale(scale) sphere(r=r);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
spikeball(r=50, scale=[0.75,1,1.5]) show_anchors(20);
|
|
```
|
|
|
|
### VNF Attachables
|
|
If the shape just doesn't fit into any of the above categories, and you constructed it as a
|
|
[VNF](vnf.scad), you can use the VNF itself to describe the geometry with the `vnf=` argument.
|
|
|
|
There are two variations to how anchoring can work for VNFs. When `extent=true`, (the default)
|
|
then a plane is projected out from the origin, perpendicularly in the direction of the anchor,
|
|
to the furthest distance that intersects with the VNF shape. The anchor point is then the
|
|
center of the points that still intersect that plane.
|
|
|
|
```openscad-FlatSpin,VPD=500
|
|
include <BOSL2/std.scad>
|
|
module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
s2 = 3 * s;
|
|
verts = [
|
|
[0,0,-s2*sqrt(2)/2],
|
|
each down(s/2, p=path3d(square(s,center=true))),
|
|
each zrot(45, p=path3d(square(s2,center=true))),
|
|
each up(s/2, p=path3d(square(s,center=true))),
|
|
[0,0,s2*sqrt(2)/2]
|
|
];
|
|
faces = [
|
|
[0,2,1], [0,3,2], [0,4,3], [0,1,4],
|
|
[1,2,6], [1,6,9], [6,10,9], [2,10,6],
|
|
[1,5,4], [1,9,5], [9,12,5], [5,12,4],
|
|
[4,8,3], [4,12,8], [12,11,8], [11,3,8],
|
|
[2,3,7], [3,11,7], [7,11,10], [2,7,10],
|
|
[9,10,13], [10,11,13], [11,12,13], [12,9,13]
|
|
];
|
|
vnf = [verts, faces];
|
|
attachable(anchor,spin,orient, vnf=vnf) {
|
|
vnf_polyhedron(vnf);
|
|
children();
|
|
}
|
|
}
|
|
stellate_cube(25) {
|
|
attach(UP+RIGHT) {
|
|
anchor_arrow(20);
|
|
%cube([100,100,0.1],center=true);
|
|
}
|
|
}
|
|
```
|
|
|
|
When `extent=false`, then the anchor point will be the furthest intersection of the VNF with
|
|
the anchor ray from the origin. The orientation of the anchor point will be the normal of the
|
|
face at the intersection. If the intersection is at an edge or corner, then the orientation
|
|
will bisect the angles between the faces.
|
|
|
|
```openscad-VPD=1250
|
|
include <BOSL2/std.scad>
|
|
module stellate_cube(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
s2 = 3 * s;
|
|
verts = [
|
|
[0,0,-s2*sqrt(2)/2],
|
|
each down(s/2, p=path3d(square(s,center=true))),
|
|
each zrot(45, p=path3d(square(s2,center=true))),
|
|
each up(s/2, p=path3d(square(s,center=true))),
|
|
[0,0,s2*sqrt(2)/2]
|
|
];
|
|
faces = [
|
|
[0,2,1], [0,3,2], [0,4,3], [0,1,4],
|
|
[1,2,6], [1,6,9], [6,10,9], [2,10,6],
|
|
[1,5,4], [1,9,5], [9,12,5], [5,12,4],
|
|
[4,8,3], [4,12,8], [12,11,8], [11,3,8],
|
|
[2,3,7], [3,11,7], [7,11,10], [2,7,10],
|
|
[9,10,13], [10,11,13], [11,12,13], [12,9,13]
|
|
];
|
|
vnf = [verts, faces];
|
|
attachable(anchor,spin,orient, vnf=vnf, extent=false) {
|
|
vnf_polyhedron(vnf);
|
|
children();
|
|
}
|
|
}
|
|
stellate_cube() show_anchors(50);
|
|
```
|
|
|
|
```openscad-3D
|
|
include <BOSL2/std.scad>
|
|
$fn=32;
|
|
R = difference(circle(10), right(2, circle(9)));
|
|
linear_sweep(R,height=10,atype="hull")
|
|
attach(RIGHT) anchor_arrow();
|
|
```
|
|
|
|
|
|
## Making Named Anchors
|
|
While vector anchors are often useful, sometimes there are logically extra attachment points that
|
|
aren't on the perimeter of the shape. This is what named string anchors are for. For example,
|
|
the `teardrop()` shape uses a cylindrical geometry for it's vector anchors, but it also provides
|
|
a named anchor "cap" that is at the tip of the hat of the teardrop shape.
|
|
|
|
Named anchors are passed as an array of `named_anchor()`s to the `anchors=` argument of `attachable()`.
|
|
The `named_anchor()` call takes a name string, a positional point, an orientation vector, and a spin.
|
|
The name is the name of the anchor. The positional point is where the anchor point is at. The
|
|
orientation vector is the direction that a child attached at that anchor point should be oriented.
|
|
The spin is the number of degrees that an attached child should be rotated counter-clockwise around
|
|
the orientation vector. Spin is optional, and defaults to 0.
|
|
|
|
To make a simple attachable shape similar to a `teardrop()` that provides a "cap" anchor, you may
|
|
define it like this:
|
|
|
|
```openscad-3D
|
|
include <BOSL2/std.scad>
|
|
module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
|
|
anchors = [
|
|
named_anchor("cap", [0,r/sin(45),0], BACK, 0)
|
|
];
|
|
attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
|
|
linear_extrude(height=thick, center=true) {
|
|
circle(r=r);
|
|
back(r*sin(45)) zrot(45) square(r, center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
raindrop(r=25, thick=20, anchor="cap");
|
|
```
|
|
|
|
If you want multiple named anchors, just add them to the list of anchors:
|
|
|
|
```openscad-FlatSpin,VPD=150
|
|
include <BOSL2/std.scad>
|
|
module raindrop(r, thick, anchor=CENTER, spin=0, orient=UP) {
|
|
anchors = [
|
|
named_anchor("captop", [0,r/sin(45), thick/2], BACK+UP, 0),
|
|
named_anchor("cap", [0,r/sin(45), 0 ], BACK, 0),
|
|
named_anchor("capbot", [0,r/sin(45),-thick/2], BACK+DOWN, 0)
|
|
];
|
|
attachable(anchor,spin,orient, r=r, l=thick, anchors=anchors) {
|
|
linear_extrude(height=thick, center=true) {
|
|
circle(r=r);
|
|
back(r*sin(45)) zrot(45) square(r, center=true);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
raindrop(r=15, thick=10) show_anchors();
|
|
```
|
|
|
|
Sometimes the named anchor you want to add may be at a point that is reached through a complicated
|
|
set of translations and rotations. One quick way to calculate that point is to reproduce those
|
|
transformations in a transformation matrix chain. This is simplified by how you can use the
|
|
function forms of almost all the transformation modules to get the transformation matrices, and
|
|
chain them together with matrix multiplication. For example, if you have:
|
|
|
|
```
|
|
scale([1.1, 1.2, 1.3]) xrot(15) zrot(25) right(20) sphere(d=1);
|
|
```
|
|
|
|
and you want to calculate the center point of the sphere, you can do it like:
|
|
|
|
```
|
|
sphere_pt = apply(
|
|
scale([1.1, 1.2, 1.3]) * xrot(15) * zrot(25) * right(20),
|
|
[0,0,0]
|
|
);
|
|
```
|
|
|
|
|
|
## Overriding Standard Anchors
|
|
|
|
Sometimes you may want to use the standard anchors but override some
|
|
of them. Returning to the square barebell example above, the anchors
|
|
at the right and left sides are on the cubes at each end, but the
|
|
anchors at x=0 are in floating in space. For prismoidal/cubic anchors
|
|
in 3D and trapezoidal/rectangular anchors in 2D we can override a single anchor by
|
|
specifying the override option and giving the anchor that is being
|
|
overridden, and then the replacement in the form
|
|
`[position, direction, spin]`. Most often you will only want to
|
|
override the position. If you omit the other list items then the
|
|
value drived from the standard anchor will be used. Below we override
|
|
position of the FWD anchor:
|
|
|
|
```openscad-3D;Big
|
|
include<BOSL2/std.scad>
|
|
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
override = [
|
|
[FWD, [[0,-s/8,0]]]
|
|
];
|
|
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
|
|
union() {
|
|
xcopies(2*s) cube(s, center=true);
|
|
xcyl(h=2*s, d=s/4);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
cubic_barbell(100) show_anchors(60);
|
|
```
|
|
|
|
Note how the FWD anchor is now rooted on the cylindrical portion. If
|
|
you wanted to also change its direction and spin you could do it like
|
|
this:
|
|
|
|
```openscad-3D;Big
|
|
include<BOSL2/std.scad>
|
|
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
override = [
|
|
[FWD, [[0,-s/8,0], FWD+LEFT, 225]]
|
|
];
|
|
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
|
|
union() {
|
|
xcopies(2*s) cube(s, center=true);
|
|
xcyl(h=2*s, d=s/4);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
cubic_barbell(100) show_anchors(60);
|
|
```
|
|
|
|
In the above example we give three values for the override. As
|
|
before, the first one places the anchor on the cylinder. We have
|
|
added the second entry which points the anchor off to the left.
|
|
The third entry gives a spin override, whose effect is shown by the
|
|
position of the red flag on the arrow. If you want to override all of
|
|
the x=0 anchors to be on the cylinder, with their standard directions,
|
|
you can do that by supplying a list:
|
|
|
|
```openscad-3D;Big
|
|
include<BOSL2/std.scad>
|
|
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
override = [
|
|
for(j=[-1:1:1], k=[-1:1:1])
|
|
if ([j,k]!=[0,0]) [[0,j,k], [s/8*unit([0,j,k])]]
|
|
];
|
|
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
|
|
union() {
|
|
xcopies(2*s) cube(s, center=true);
|
|
xcyl(h=2*s, d=s/4);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
cubic_barbell(100) show_anchors(30);
|
|
```
|
|
|
|
Now all of the anchors in the middle are all rooted to the cylinder. Another
|
|
way to do the same thing is to use a function literal for override.
|
|
It will be called with the anchor as its argument and needs to return undef to just use
|
|
the default, or a `[position, direction, spin]` triple to override the
|
|
default. As before, you can omit values to keep their default.
|
|
Here is the same example using a function literal for the override:
|
|
|
|
```openscad-3D;Big
|
|
include<BOSL2/std.scad>
|
|
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
|
override = function (anchor)
|
|
anchor.x!=0 || anchor==CTR ? undef // Keep these
|
|
: [s/8*unit(anchor)];
|
|
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
|
|
union() {
|
|
xcopies(2*s) cube(s, center=true);
|
|
xcyl(h=2*s, d=s/4);
|
|
}
|
|
children();
|
|
}
|
|
}
|
|
cubic_barbell(100) show_anchors(30);
|
|
```
|