diff --git a/attachments.scad b/attachments.scad index 244d6651..247804a5 100644 --- a/attachments.scad +++ b/attachments.scad @@ -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 { diff --git a/tutorials/Attachment-Align.md b/tutorials/Attachment-Align.md index c49aa2f5..2430258d 100644 --- a/tutorials/Attachment-Align.md +++ b/tutorials/Attachment-Align.md @@ -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) diff --git a/tutorials/Attachment-Attach.md b/tutorials/Attachment-Attach.md index 40a3c682..a968cc06 100644 --- a/tutorials/Attachment-Attach.md +++ b/tutorials/Attachment-Attach.md @@ -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) diff --git a/tutorials/Attachment-Basic-Positioning.md b/tutorials/Attachment-Basic-Positioning.md index e800b581..d98f7829 100644 --- a/tutorials/Attachment-Basic-Positioning.md +++ b/tutorials/Attachment-Basic-Positioning.md @@ -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 square([40,30], anchor=BACK+LEFT, spin=30); ``` + +[Next: Relative Positioning of Children](Tutorial-Attachment-Relative-Positioning) diff --git a/tutorials/Attachment-Color.md b/tutorials/Attachment-Color.md index ce5c5e82..71c0bce6 100644 --- a/tutorials/Attachment-Color.md +++ b/tutorials/Attachment-Color.md @@ -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) diff --git a/tutorials/Attachment-Edge-Profiling.md b/tutorials/Attachment-Edge-Profiling.md index d3af758f..a8f03388 100644 --- a/tutorials/Attachment-Edge-Profiling.md +++ b/tutorials/Attachment-Edge-Profiling.md @@ -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) diff --git a/tutorials/Attachment-Making.md b/tutorials/Attachment-Making.md index a1576872..d7944e97 100644 --- a/tutorials/Attachment-Making.md +++ b/tutorials/Attachment-Making.md @@ -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 +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 +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 +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 +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. diff --git a/tutorials/Attachment-Overview.md b/tutorials/Attachment-Overview.md index d82ff46b..285fd4a0 100644 --- a/tutorials/Attachment-Overview.md +++ b/tutorials/Attachment-Overview.md @@ -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) + diff --git a/tutorials/Attachment-Position.md b/tutorials/Attachment-Position.md index 740d9a98..58c23cae 100644 --- a/tutorials/Attachment-Position.md +++ b/tutorials/Attachment-Position.md @@ -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) diff --git a/tutorials/Attachment-Relative-Positioning.md b/tutorials/Attachment-Relative-Positioning.md index 6e08f625..04e0c6bb 100644 --- a/tutorials/Attachment-Relative-Positioning.md +++ b/tutorials/Attachment-Relative-Positioning.md @@ -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) diff --git a/tutorials/Attachment-Tags.md b/tutorials/Attachment-Tags.md index 5fa0a745..b326f512 100644 --- a/tutorials/Attachment-Tags.md +++ b/tutorials/Attachment-Tags.md @@ -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)