Merge pull request #1725 from adrianVmariano/master

add attachable parts tutorial and improve tutorial links
This commit is contained in:
adrianVmariano
2025-06-21 12:07:41 -04:00
committed by GitHub
12 changed files with 179 additions and 6 deletions

View File

@@ -15,7 +15,7 @@ Requires OpenSCAD 2021.01 or later.
The BOSL2 library is an enormous library that provides many different kinds of capabilities to simplify the development of models in OpenSCAD, and to make things possible that are difficult in native OpenSCAD. Some of the things BOSL2 provides are:
* **Attachments.** Unless you make models containing just one object, the attachments features can revolutionize your modeling. They let you position components of a model relative to other components so you don't have to keep track of the positions and orientations of parts of the model. You can instead place something on the TOP of something else, perhaps aligned to the RIGHT. For a full introduction to attachments, consult the [Attachments Tutorial.](https://github.com/BelfrySCAD/BOSL2/wiki/Tutorial-Attachments)
* **Attachments.** Unless you make models containing just one object, the attachments features can revolutionize your modeling. They let you position components of a model relative to other components so you don't have to keep track of the positions and orientations of parts of the model. You can instead place something on the TOP of something else, perhaps aligned to the RIGHT. For a full introduction to attachments, consult the [Attachments Tutorial.](https://github.com/BelfrySCAD/BOSL2/wiki/Tutorial-Attachment-Overview)
* **Rounding and filleting.** Rounding and filleting is hard in OpenSCAD. The library provides modules like [cuboid()](https://github.com/BelfrySCAD/BOSL2/wiki/shapes3d.scad#module-cuboid) to make a cube with any of the edges rounded, [offset_sweep()](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#functionmodule-offset_sweep) to round the ends of a linear extrusion, and [prism_connector()](https://github.com/BelfrySCAD/BOSL2/wiki/rounding.scad#module-prism_connector) which works with the attachments feature to create filleted prisms between a variety of objects, or holes through a single object with rounded edges at the ends. You can also use [edge_profile()](https://github.com/BelfrySCAD/BOSL2/wiki/attachments.scad#module-edge_profile) to apply a variety of different mask profiles to chosen edges of a cubic shape, or you can directly subtract 3d mask shapes from an edge of objects that are not cubes.
* **Shorthands.** The shorthands make your code a little shorter, and more importantly, they can make it significantly easier to read. Compare `up(z)` to `translate([0,0,z])`. The shorthands include operations for creating [copies of objects](https://github.com/BelfrySCAD/BOSL2/wiki/distributors.scad) and for applying [transformations](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad) to objects, including [rot()](https://github.com/BelfrySCAD/BOSL2/wiki/transforms.scad#functionmodule-rot) which extends `rotate()` in some useful ways that are not easy to do directly.
* **Complex object support.** The [path_sweep()](https://github.com/BelfrySCAD/BOSL2/wiki/skin.scad#functionmodule-path_sweep) function/module takes a 2d polygon moves it through space along a path and sweeps out a 3d shape as it moves. You can link together a series of arbitrary polygons with [skin()](https://github.com/BelfrySCAD/BOSL2/wiki/skin.scad#functionmodule-skin) or [vnf_vertex_array().](https://github.com/BelfrySCAD/BOSL2/wiki/vnf.scad#functionmodule-vnf_vertex_array) Support for [beziers](https://github.com/BelfrySCAD/BOSL2/wiki/beziers.scad) and [NURBS](https://github.com/BelfrySCAD/BOSL2/wiki/nurbs.scad) can help you construct the building blocks you need. [Metaballs](https://github.com/BelfrySCAD/BOSL2/wiki/isosurface.scad#functionmodule-metaballs) can create organic surfaces that blend shapes together.

View File

@@ -1046,6 +1046,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
// used when attachable() places the child
$anchor_override = all_zero(child_adjustment)? inside?child:undef
: child+child_adjustment;
reference = two_d? BACK : UP;
// inset_dir is the direction for insetting when alignment is in effect
inset_dir = is_undef(align) ? CTR
@@ -1058,7 +1059,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
spinaxis = two_d? UP : anchor_dir;
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference);
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference*($anchor_inside?-1:1));
if (norot || (approx(anchor_dir,reference) && anchor_spin==0))
translate(pos) rot(v=spinaxis,a=factor*spin) translate(olap) default_tag("remove",removetag) children();
else
@@ -4168,6 +4169,7 @@ function _find_anchor(anchor, geom)=
let(
vnf=geom[1],
override = geom[2](anchor)
,fd=echo(cp=cp)
) // CENTER anchors anchor on cp, "origin" anchors on [0,0]
approx(anchor,CTR)? [anchor, default(override[0],cp),default(override[1],UP),default(override[2], 0)] :
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
@@ -4448,15 +4450,15 @@ module show_anchors(s=10, std=true, custom=true) {
if (std) {
for (anchor=_standard_anchors(two_d=two_d)) {
if(two_d) {
attach(anchor) anchor_arrow2d(s);
attach(anchor,BOT) anchor_arrow2d(s);
} else {
attach(anchor) anchor_arrow(s);
attach(anchor,BOT) anchor_arrow(s);
}
}
}
if (custom) {
for (anchor=last($parent_geom)) {
attach(anchor[0]) {
attach(anchor[0],BOT) {
if(two_d) {
anchor_arrow2d(s, color="cyan");
} else {

View File

@@ -1,3 +1,5 @@
[Previous: Using position()](Tutorial-Attachment-Position)
# Aligning children with align()
You may have noticed that with position() and orient(), specifying the
@@ -125,3 +127,5 @@ cyl(h=20,d=10,$fn=128)
align([1,.3],TOP)
color("lightblue")cuboid(5);
```
[Next: Using attach()](Tutorial-Attachment-Attach)

View File

@@ -1,3 +1,5 @@
[Prev: Using align()](Tutorial-Attachment-Align)
# Attachment using attach()
The `attach()` module can stick the child object to the parent object
@@ -674,3 +676,5 @@ circle(d=50){
trapezoid(w1=30,w2=0,h=30);
}
```
[Next: Attachable Parts](Tutorial-Attachment-Parts)

View File

@@ -1,3 +1,5 @@
[Previous: Attachments Overview](Tutorial-Attachment-Overview)
# Basic Object Positioning: Anchor, Spin and Orient
When you create attachable objects using BOSL2 you have some options
@@ -320,3 +322,5 @@ For 2D shapes, you can mix `anchor=` with `spin=`, but not with `orient=`.
include <BOSL2/std.scad>
square([40,30], anchor=BACK+LEFT, spin=30);
```
[Next: Relative Positioning of Children](Tutorial-Attachment-Relative-Positioning)

View File

@@ -1,3 +1,5 @@
[Prev: Attachable Parts](Tutorial-Attachment-Parts)
## Coloring Attachables
Usually, when coloring a shape with the `color()` module, the parent color overrides the colors of
all children. This is often not what you want:
@@ -57,3 +59,5 @@ affecting its descendents.
As with all of the attachable features, these color, highlight and ghost modules only work
on attachable objects, so they will have no effect on objects you
create using `linear_extrude()` or `rotate_extrude()`.
[Next: Tagged Operations with Attachments](Tutorial-Attachment-Tags)

View File

@@ -1,3 +1,5 @@
[Prev: Tagged Operations with Attachments](Tutorial-Attachment-Tags)
# Using Attachment for Edge Profiling
You can use attachment in various ways to create edge profiles on
@@ -163,4 +165,4 @@ cube([50,60,70],center=true)
mask2d_roundover(10);
```
[Next: Making Attachable Objects](Tutorial-Attachment-Making)

View File

@@ -1,3 +1,5 @@
[Prev: Edge Profiling with Attachment](Tutorial-Attachment-Edge-Profiling)
# Making Attachables
To make a shape attachable, you just need to wrap it with an `attachable()` module with a
@@ -531,3 +533,140 @@ module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
}
cubic_barbell(100) show_anchors(30);
```
## Making Gometry With attach_geom()
Sometimes it may be advantageous to create the attachable geometry as
a data structure. This can be particularly useful if you want to
implement anchor types with an object, because it allows for an easy
way to create different anchor options without having to repeat code.
Suppose we create a simple tetrahedron object:
```openscad-3D
include<BOSL2/std.scad>
module tetrahedron(base, height, spin=0, anchor=FWD+LEFT+BOT, orient=UP)
{
base_poly = path3d([[0,0],[0,base],[base,0]]);
top = [0,0,height];
vnf = vnf_vertex_array([base_poly, repeat(top,3)],col_wrap=true,cap1=true);
attachable(anchor=anchor,orient=orient,spin=spin,vnf=vnf,cp="centroid"){
vnf_polyhedron(vnf);
children();
}
}
tetrahedron(20,18) show_anchors();
```
For this module we have used VNF anchors, but this tetrahedron is half
related to a cuboid, so maybe sometimes you prefer to use anchors
based on the bounding box. You could create a module with bounding
box anchors like this, where we have had to explicitly center the VNF
to make it work with the prismoid type anchoring:
```openscad-3D
include<BOSL2/std.scad>
module tetrahedron(base, height, spin=0, anchor=FWD+LEFT+BOT, orient=UP)
{
base_poly = path3d([[0,0],[0,base],[base,0]]);
top = [0,0,height];
vnf = move([-base/2,-base/2,-height/2],
vnf_vertex_array([base_poly, repeat(top,3)],col_wrap=true,cap1=true));
attachable(anchor=anchor,orient=orient,spin=spin,size=[base,base,height]){
vnf_polyhedron(vnf);
children();
}
}
tetrahedron(20,18) show_anchors();
```
The arguments needed to attachable are different in this case. If you
want to conditionally switch between these two modes of operation,
this presents a complication. While it is possible to work around
this by conditionally setting parameters to `undef`, the resulting
code will be more complex and harder to read. A better solution is to
compute the geometry conditionally. Then the geometry can be passed
to `attachable()`.
```openscad-3D;Big
include<BOSL2/std.scad>
module tetrahedron(base, height, atype="vnf", spin=0, anchor=FWD+LEFT+BOT, orient=UP)
{
assert(atype=="vnf" || atype=="box");
base_poly = path3d([[0,0],[0,base],[base,0]]);
top = [0,0,height];
vnf = move([-base/2,-base/2,-height/2],
vnf_vertex_array([base_poly, repeat(top,3)],col_wrap=true,cap1=true));
geom = atype=="vnf" ? attach_geom(vnf=vnf,cp="centroid")
: attach_geom(size=[base,base,height]);
attachable(anchor=anchor,orient=orient,spin=spin,geom=geom){
vnf_polyhedron(vnf);
children();
}
}
tetrahedron(20,18,atype="vnf")
color("green")attach(TOP,BOT) cuboid(4);
right(25)
tetrahedron(20,18,atype="box")
color("lightblue")attach(TOP,BOT) cuboid(4);
```
Here we have created an `atype` argument that accepts two attachment
types and we compute the geometry conditionally based on the atype
setting. We can then invoke `attachable()` once with the `geom`
parameter to specify the geometry.
## Creating Attachable Parts
If your object has multiple distinct parts you may wish to create
attachble parts for your object. In the library, `tube()` create
an attachable part called "inside" that lets you attach to the inside
of the tube.
Below we create an example where an object is made from two
cylindrical parts, and we want to be able to attach to either one.
In order to create attchable parts you must pass a list of the parts
to `attachable()`. You create a part using the `define_part()`
function which requires the part's name and its geometry. You can
optionally provide a transformation using the `T=` parameter and give
a flat with the `inside=` parameter.
```openscad-3D;Big
include<BOSL2/std.scad>
module twocyl(d1, d2, sep, h, ang=20)
{
parts = [
define_part("left", attach_geom(r=d1/2,h=h),
T=left(sep/2)*yrot(-ang)),
define_part("right", attach_geom(r=d2/2,h=h),
T=right(sep/2)*yrot(ang)),
];
attachable(size=[sep+d1/2+d2/2,max(d1,d2),h], parts=parts){
union(){
left(sep/2) yrot(-ang) cyl(d=d1,h=h);
right(sep/2) yrot(ang) cyl(d=d2,h=h);
}
children();
}
}
twocyl(d1=10,d2=13,sep=20,h=10){
attach_part("left") attach(FWD,BOT)
color("lightblue") cuboid(3);
attach_part("right") attach(RIGHT,BOT)
color("green") cuboid(3);
}
```
In the above example we create a parts list containing two parts named
"left" and "right". Each part has its own geometry corresponding to
the size of the cylinder, and it has a transformation specifying where
the cylinder is located relative to the part's overall geometry.
If you create an "inside" part for a tube, the inside object will
naturally have its anchors on the inner cylinder **pointing
outward**. You can anchor on the inside by setting `inside=true` when
invoking `attach()` or `align()`, but another option set `inside=true`
when creating the part with `define_part()`. This cause `align()` and
`attach()` to invert the meaning of the `inside` parameter so that
objects will attach on the inside by default.

View File

@@ -24,4 +24,6 @@ tutorial. The non-attachables are `polyhedron()`, `linear_extrude()`,
Some of these have attachable alternatives: `vnf_polyhedron()`,
`linear_sweep()`, `rotate_sweep()`, and `region()`.
[Next: Basic Positioning](Tutorial-Attachment-Basic-Positioning)

View File

@@ -1,3 +1,5 @@
[Previous: Relative Positioning of Children](Tutorial-Attachment-Relative-Positioning)
# Placing Children using position()
If you make an object a child of another object then the child object
@@ -203,3 +205,5 @@ prismoid([50,50],[30,30],h=40)
orient(RIGHT)
anchor_arrow(40);
```
[Next: Using align()](Tutorial-Attachment-Align)

View File

@@ -1,3 +1,5 @@
[Prev: Basic Positioning](Tutorial-Attachment-Basic-Positioning)
# Relative Positioning: Placing Children using position(), align(), and attach()
Relative positioning is one of the most useful and powerful features
@@ -17,3 +19,5 @@ Relative positioning means that since objects are positioned relative
to other objects, you do not need to keep track of absolute positions
and orientations of objects in your model. This makes models simpler,
more intuitive, and easier to maintain.
[Next: Using position()](Tutorial-Attachment-Position)

View File

@@ -1,3 +1,5 @@
[Prev: Using color with attachments](Tutorial-Attachment-Color)
# Tagged Operations
BOSL2 introduces the concept of tags. Tags are names that can be given to attachables, so that
@@ -183,3 +185,5 @@ cube(50, center=true) {
tag("keep")xcyl(h=100, d=20);
}
```
[Next: Edge Profiling with Attachment](Tutorial-Attachment-Edge-Profiling)