From 3eef8cc92ad77f74fc4a1897c697453725c794e5 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sun, 13 Nov 2022 11:40:07 -0500 Subject: [PATCH 1/3] dovetail $slop fix --- joiners.scad | 44 ++++++++++++++++++++++------------ tutorials/Attachments.md | 52 ++++++++++++++++++++++++---------------- 2 files changed, 60 insertions(+), 36 deletions(-) diff --git a/joiners.scad b/joiners.scad index d828409..5dd0a06 100644 --- a/joiners.scad +++ b/joiners.scad @@ -634,16 +634,25 @@ module dovetail(gender, width, height, slide, h, w, angle, slope, thickness, tap assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius"); slope = is_def(slope) ? slope : is_def(angle) ? 1/tan(angle) : 6; - extra_slop = gender == "female" ? 2*get_slop() : 0; - width = w + extra_slop; - height = h + extra_slop; - back_width = u_add(back_width, extra_slop); + height_slop = gender == "female" ? get_slop() : 0; - front_offset = is_def(taper) ? -extra * tan(taper) : - is_def(back_width) ? extra * (back_width-width)/slide/2 : 0; + // This adjustment factor doesn't seem to be exactly right, but don't know how to get it right - size = is_def(chamfer) && chamfer>0 ? chamfer : - is_def(radius) && radius>0 ? radius : 0; + wfactor=rot(atan(tan(angle)*cos(taper)),p=zrot(taper, RIGHT), v=[-sin(taper),cos(taper),0]).x; + // adjust width for increased height adjust for normal to dovetail surface + width_slop = 2*height_slop/slope + 2* height_slop / wfactor; + + width = w + width_slop; + height = h + height_slop; + back_width = u_add(back_width, width_slop); + + front_offset = is_def(taper) ? -extra * tan(taper) + : is_def(back_width) ? extra * (back_width-width)/slide/2 + : 0; + + size = is_def(chamfer) && chamfer>0 ? chamfer + : is_def(radius) && radius>0 ? radius + : 0; type = is_def(chamfer) && chamfer>0 ? "chamfer" : "circle"; fullsize = round ? [size,size] : @@ -653,21 +662,26 @@ module dovetail(gender, width, height, slide, h, w, angle, slope, thickness, tap move( [0,-slide/2-extra,0], p=[ - [0 , 0, height], - [width/2-front_offset , 0, height], - [width/2 - height/slope - front_offset, 0, 0 ], - [width/2 - front_offset + height, 0, 0] + [0, 0, height], + [width/2 - front_offset, 0, height], + [width/2 - height/slope - front_offset, 0, 0 ], + [width/2 - front_offset + height, 0, 0 ] ] ), method=type, cut = fullsize, closed=false ); smallend_points = concat(select(smallend_half, 1, -2), [down(extra,p=select(smallend_half, -2))]); - offset = is_def(taper) ? -(slide+extra) * tan(taper) : - is_def(back_width) ? (back_width-width) / 2 : 0; + offset = is_def(taper) ? -(slide+extra) * tan(taper) + : is_def(back_width) ? (back_width-width) / 2 + : 0; bigend_points = move([offset,slide+2*extra,0], p=smallend_points); - //adjustment = $overlap * (gender == "male" ? -1 : 1); // Adjustment for default overlap in attach() adjustment = 0; // Default overlap is assumed to be zero + + // This code computes the true normal from which the exact width factor can be obtained + // as the x component. Comparing to wfactor above shows small discrepancy + // pts = [smallend_points[0], smallend_points[1], bigend_points[1],bigend_points[0]]; + // n = -polygon_normal(pts); attachable(anchor,spin,orient, size=[width+2*offset, slide, height]) { down(height/2+adjustment) { diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index df77971..ed87803 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -26,7 +26,8 @@ well. Anchoring allows you to align a specified part of an object or point on an object with the origin. The alignment point can be the center of a side, the center of an edge, a corner, or some other -distinguished point on the object. This is done by passing a vector into the `anchor=` argument. For roughly cubical +distinguished point on the object. This is done by passing a vector +or text string into the `anchor=` argument. For roughly cubical or prismoidal shapes, that vector points in the general direction of the side, edge, or corner that will be aligned to. For example, a vector of [1,0,-1] refers to the lower-right edge of the shape. Each vector component should be -1, 0, or 1: @@ -56,14 +57,14 @@ Constant | Direction | Value -------- | --------- | ----------- `LEFT` | X- | `[-1, 0, 0]` `RIGHT` | X+ | `[ 1, 0, 0]` -`FRONT`/`FORWARD`/`FWD` | Y- | `[ 0,-1, 0]` +`FRONT`/`FORWARD`/`FWD` | Y− | `[ 0, −1, 0]` `BACK` | Y+ | `[ 0, 1, 0]` -`BOTTOM`/`BOT`/`DOWN` | Z- (Y- in 2D) | `[ 0, 0,-1]` (`[0,-1]` in 2D.) -`TOP`/`UP` | Z+ (Y+ in 2D) | `[ 0, 0, 1]` (`[0,-1]` in 2D.) +`BOTTOM`/`BOT`/`DOWN` | Z− (Y− in 2D) | `[ 0, 0, −1]` (`[0, −1]` in 2D.) +`TOP`/`UP` | Z+ (Y+ in 2D) | `[ 0, 0, 1]` (`[0, 1]` in 2D.) `CENTER`/`CTR` | Centered | `[ 0, 0, 0]` -If you want a vector pointing towards the bottom-left edge, just add the `BOTTOM` and `LEFT` vector -constants together like `BOTTOM + LEFT`. This will result in a vector of `[-1,0,-1]`. You can pass +If you want a vector pointing towards the bottom−left edge, just add the `BOTTOM` and `LEFT` vector +constants together like `BOTTOM + LEFT`. This will result in a vector of `[−1,0,−1]`. You can pass that to the `anchor=` argument for a clearly understandable anchoring: ```openscad-3D @@ -78,9 +79,9 @@ cube([40,30,50], anchor=FRONT); --- -For cylindrical type attachables, the Z component of the vector will be -1, 0, or 1, referring +For cylindrical type attachables, the Z component of the vector will be −1, 0, or 1, referring to the bottom rim, the middle side, or the top rim of the cylindrical or conical shape. -The X and Y components can be any value, pointing towards the circular perimeter of the cone. +The X and Y components can be any value, pointing towards the circular perimeter of the cone. These combined let you point at any place on the bottom or top rims, or at an arbitrary side wall: @@ -122,20 +123,29 @@ sphere(r=50, anchor=spherical_to_xyz(1,-30,60)); --- Some attachable shapes may provide specific named anchors for shape-specific anchoring. These -will be given as strings and will be specific to that type of attachable. For example, the -`teardrop()` attachable has a named anchor called "cap": +will be given as strings and will be specific to that type of +attachable. When named anchors are supported, they are listed in a +"Named Anchors" section of the documentation for the module. The +`teardrop()` attachable, for example, has a named anchor called "cap" and in 2D the +`star()` attachable has anchors labeled by tip number: ```openscad-3D include teardrop(d=100, l=20, anchor="cap"); ``` +```openscad-2D +include +star(n=7, od=30, id=20, anchor="tip2"); +``` + --- Some shapes, for backwards compatibility reasons, can take a `center=` argument. This just overrides the `anchor=` argument. A `center=true` argument is the same as `anchor=CENTER`. -A `center=false` argument can mean `anchor=[-1,-1,-1]` for a cube, or `anchor=BOTTOM` for a -cylinder, to make them behave just like the builtin versions: +A `center=false` argument chooses the anchor to match the behavior of +the builtin version: for a cube it is the same as `anchor=[-1,-1,-1]` but for a +cylinder, it is the same as `anchor=BOTTOM`. ```openscad-3D include @@ -150,7 +160,7 @@ cube([50,40,30],center=false); --- Most 2D shapes provided by BOSL2 are also anchorable. The built-in `square()` and `circle()` -modules have been overridden to enable attachability and anchoring. The `anchor=` options for 2D +modules have been overridden to make them attachable.. The `anchor=` options for 2D shapes treat 2D vectors as expected. Special handling occurs with 3D vectors: if the Y coordinate is zero and the Z coordinate is nonzero, then the Z coordinate is used to replace the Y coordinate. This is @@ -202,9 +212,9 @@ include cube([20,20,40], center=true, spin=45); ``` -You can even spin around each of the three axes in one pass, by giving 3 angles (in degrees) to -`spin=` as a vector, like [Xang,Yang,Zang]. Similarly to `rotate()`, the axes will be spun in -the order given, X-axis spin, then Y-axis, then Z-axis: +You can also spin around other axes, or multiple axes at once, by giving 3 angles (in degrees) to +`spin=` as a vector, like [Xang,Yang,Zang]. Similarly to `rotate()`, +the rotations apply in the order given, X-axis spin, then Y-axis, then Z-axis: ```openscad-3D include @@ -213,14 +223,14 @@ cube([20,20,40], center=true, spin=[10,20,30]); This example shows a cylinder which has been anchored at its FRONT, with a rotated copy in gray. The rotation is performed around the -origin, but the cylinder is off the origin, so the rotation *does* +origin, but the cylinder is off the origin, so the rotation **does** have an effect on the cylinder, even though the cylinder has rotational symmetry. ```openscad-3D include cylinder(h=40,d=20,anchor=FRONT+BOT); -%cylinder(h=40.1,d=20,anchor=FRONT+BOT,spin=40); +%cylinder(h=40.2,d=20,anchor=FRONT+BOT,spin=40); ``` @@ -295,12 +305,12 @@ square([40,30], anchor=BACK+LEFT, spin=30); Positioning is a powerful method for placing an object relative to another object. You do this by making the second object a child of the first object. By default, the child's anchor point will be -aligned with the center of the parent. Note that the cylinder is this example -is centered on the cube, not on the Z axis. +aligned with the center of the parent. The default anchor for `cyl()` +is CENTER, and in this case, the cylinder is centered on the cube's center ```openscad-3D include -cube(50) +up(13) cube(50) cyl(d=25,l=75); ``` From 1cf60c41e2c6f9e54b2af64a5b7000dabacccd3d Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sun, 13 Nov 2022 13:16:16 -0500 Subject: [PATCH 2/3] fix to handle case where taper not given --- joiners.scad | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/joiners.scad b/joiners.scad index 5dd0a06..9179e5a 100644 --- a/joiners.scad +++ b/joiners.scad @@ -547,7 +547,7 @@ module joiner(l=40, w=10, base=10, ang=30, screwsize, anchor=CENTER, spin=0, ori // the default orientation depends on the gender, with male dovetails oriented UP and female ones DOWN. The dovetails by default // have extra extension of 0.01 for unions and differences. You should ensure that attachment is done with overlap=0 to ensure that // the sizing and positioning is correct. To adjust the fit, use the $slop variable, which increases the depth and width of -// the female part of the joint. +// the female part of the joint to allow a clearance gap of $slop on each of the three sides. // // Arguments: // gender = A string, "male" or "female", to specify the gender of the dovetail. @@ -562,7 +562,7 @@ module joiner(l=40, w=10, base=10, ang=30, screwsize, anchor=CENTER, spin=0, ori // chamfer = amount to chamfer the corners of the joint (Default: no chamfer) // r / radius = amount to round over the corners of the joint (Default: no rounding) // round = true to round both corners of the dovetail and give it a puzzle piece look. Default: false. -// $slop = Increase the width and depth of the female joint by this amount to allow adjustment of the fit. +// $slop = Increase the width of socket by double this amount and depth by this amount to allow adjustment of the fit. // extra = amount of extra length and base extension added to dovetails for unions and differences. Default: 0.01 // Example: Ordinary straight dovetail, male version (sticking up) and female version (below the xy plane) // dovetail("male", width=15, height=8, slide=30); @@ -638,7 +638,10 @@ module dovetail(gender, width, height, slide, h, w, angle, slope, thickness, tap // This adjustment factor doesn't seem to be exactly right, but don't know how to get it right - wfactor=rot(atan(tan(angle)*cos(taper)),p=zrot(taper, RIGHT), v=[-sin(taper),cos(taper),0]).x; + taper_ang = is_def(taper) ? taper + : is_def(back_width) ? atan((back_width-width)/slide) + : 0; + wfactor=rot(atan(cos(taper_ang)/slope),p=zrot(taper_ang, RIGHT), v=[-sin(taper_ang),cos(taper_ang),0]).x; // adjust width for increased height adjust for normal to dovetail surface width_slop = 2*height_slop/slope + 2* height_slop / wfactor; @@ -679,9 +682,14 @@ module dovetail(gender, width, height, slide, h, w, angle, slope, thickness, tap adjustment = 0; // Default overlap is assumed to be zero // This code computes the true normal from which the exact width factor can be obtained - // as the x component. Comparing to wfactor above shows small discrepancy + // as the x component. Comparing to wfactor above shows small discrepancy. + // Note, male joint case is totally wrong, but that doesn't matter because we only need + // slop for female // pts = [smallend_points[0], smallend_points[1], bigend_points[1],bigend_points[0]]; // n = -polygon_normal(pts); + // echo(n=n); + // echo(wfactor=wfactor); + // echo(err = n.x-wfactor); attachable(anchor,spin,orient, size=[width+2*offset, slide, height]) { down(height/2+adjustment) { From 45826195c8b295059c9a1ec2d55e596a589e7139 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Tue, 15 Nov 2022 23:02:39 -0500 Subject: [PATCH 3/3] joiners bugfix, attachments proofread --- joiners.scad | 58 +++++++++++++------------ tutorials/Attachments.md | 91 +++++++++++++++++++++++++--------------- 2 files changed, 90 insertions(+), 59 deletions(-) diff --git a/joiners.scad b/joiners.scad index 9179e5a..38bdfa0 100644 --- a/joiners.scad +++ b/joiners.scad @@ -629,70 +629,76 @@ module dovetail(gender, width, height, slide, h, w, angle, slope, thickness, tap count2 = num_defined([taper,back_width]); count3 = num_defined([chamfer, radius]); dummy = - assert(count<=1, "Do not specify both angle and slope") - assert(count2<=1, "Do not specify both taper and back_width") - assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius"); - slope = is_def(slope) ? slope : - is_def(angle) ? 1/tan(angle) : 6; + assert(count<=1, "Do not specify both angle and slope") + assert(count2<=1, "Do not specify both taper and back_width") + assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius"); + slope = is_def(slope) ? slope + : is_def(angle) ? 1/tan(angle) + : 6; height_slop = gender == "female" ? get_slop() : 0; - // This adjustment factor doesn't seem to be exactly right, but don't know how to get it right - + // Need taper angle for computing width adjustment, but not used elsewhere taper_ang = is_def(taper) ? taper - : is_def(back_width) ? atan((back_width-width)/slide) + : is_def(back_width) ? atan((back_width-width)/2/slide) : 0; - wfactor=rot(atan(cos(taper_ang)/slope),p=zrot(taper_ang, RIGHT), v=[-sin(taper_ang),cos(taper_ang),0]).x; + // This is the adjustment factor for width to grow in the direction normal to the dovetail face + wfactor = sqrt( 1/slope^2 + 1/cos(taper_ang)^2 ); // adjust width for increased height adjust for normal to dovetail surface - width_slop = 2*height_slop/slope + 2* height_slop / wfactor; - + width_slop = 2*height_slop/slope + 2* height_slop * wfactor; width = w + width_slop; height = h + height_slop; back_width = u_add(back_width, width_slop); - front_offset = is_def(taper) ? -extra * tan(taper) + extra_offset = is_def(taper) ? -extra * tan(taper) : is_def(back_width) ? extra * (back_width-width)/slide/2 : 0; size = is_def(chamfer) && chamfer>0 ? chamfer : is_def(radius) && radius>0 ? radius : 0; + fullsize = round ? [size,size] + : gender == "male" ? [size,0] + : [0,size]; + type = is_def(chamfer) && chamfer>0 ? "chamfer" : "circle"; - fullsize = round ? [size,size] : - gender == "male" ? [size,0] : [0,size]; - smallend_half = round_corners( move( [0,-slide/2-extra,0], p=[ [0, 0, height], - [width/2 - front_offset, 0, height], - [width/2 - height/slope - front_offset, 0, 0 ], - [width/2 - front_offset + height, 0, 0 ] + [width/2 - extra_offset, 0, height], + [width/2 - extra_offset - height/slope, 0, 0 ], + [width/2 - extra_offset + height, 0, 0 ] ] ), method=type, cut = fullsize, closed=false ); + smallend_points = concat(select(smallend_half, 1, -2), [down(extra,p=select(smallend_half, -2))]); - offset = is_def(taper) ? -(slide+extra) * tan(taper) + offset = is_def(taper) ? -slide * tan(taper) : is_def(back_width) ? (back_width-width) / 2 : 0; - bigend_points = move([offset,slide+2*extra,0], p=smallend_points); + bigend_points = move([offset+2*extra_offset,slide+2*extra,0], p=smallend_points); + + bigenough = all_nonnegative(column(smallend_half,0)) && all_nonnegative(column(bigend_points,0)); + + assert(bigenough, "Width of dovetail is not large enough for its geometry (angle and taper"); + //adjustment = $overlap * (gender == "male" ? -1 : 1); // Adjustment for default overlap in attach() adjustment = 0; // Default overlap is assumed to be zero // This code computes the true normal from which the exact width factor can be obtained - // as the x component. Comparing to wfactor above shows small discrepancy. - // Note, male joint case is totally wrong, but that doesn't matter because we only need - // slop for female + // as the x component. Comparing to wfactor above shows that they agree. // pts = [smallend_points[0], smallend_points[1], bigend_points[1],bigend_points[0]]; // n = -polygon_normal(pts); // echo(n=n); - // echo(wfactor=wfactor); - // echo(err = n.x-wfactor); - + // echo(invwfactor = 1/wfactor, error = n.x-1/wfactor); + attachable(anchor,spin,orient, size=[width+2*offset, slide, height]) { down(height/2+adjustment) { + //color("red")stroke([pts],width=.1); + skin( [ reverse(concat(smallend_points, xflip(p=reverse(smallend_points)))), diff --git a/tutorials/Attachments.md b/tutorials/Attachments.md index ed87803..4515446 100644 --- a/tutorials/Attachments.md +++ b/tutorials/Attachments.md @@ -311,7 +311,7 @@ is CENTER, and in this case, the cylinder is centered on the cube's center ```openscad-3D include up(13) cube(50) - cyl(d=25,l=75); + cyl(d=25,l=95); ``` With `cylinder()` the default anchor is BOTTOM. It's hard to tell, @@ -331,7 +331,7 @@ side of the cylinder is aligned with the center of the cube. ```openscad-3D include cube(50,anchor=FRONT) - cylinder(d=25,l=75,anchor=RIGHT); + cylinder(d=25,l=95,anchor=RIGHT); ``` The `position()` module enables you to specify where on the parent to @@ -389,14 +389,17 @@ mechanism for re-orienting the child() that eases this burden. Using its `anchor=` argument you can orient the child relative to the parent anchor directions. This is different than giving an `orient=` argument to the child, because that orients -relative to the **child** anchor directions. A series of three +relative to the parent's global coordinate system by just using the vector +directly instead of orienting to the parent's anchor, which takes +account of face orientation. A series of three examples shows the different results. In the first example, we use only `position()`. The child cube is erected pointing upwards, in the Z direction. In the second example we use `orient=RIGHT` in the child and the result is that the child object points in the X+ direction, without regard for the shape of the parent object. In the final example we apply `orient(anchor=RIGHT)` and the child is oriented -relative to the slanted right face of the parent. +relative to the slanted right face of the parent using the parent +RIGHT anchor. ```openscad-3D include @@ -422,8 +425,8 @@ prismoid([50,50],[30,30],h=40) cube([15,15,25],anchor=BACK+BOT); ``` -You may have noticed that the anchors were different in each of the -three examples above. Why is that? The first and second examples +You may have noticed that the children in the above three examples +have different anchors. Why is that? The first and second examples differ because anchoring up and anchoring to the right require anchoring on opposite sides of the child. But the third case differs because the spin has changed. The examples below show the same models @@ -436,7 +439,7 @@ flag. include prismoid([50,50],[30,30],h=40) position(RIGHT+TOP) - anchor_arrow(20); + anchor_arrow(40); ``` @@ -444,7 +447,7 @@ prismoid([50,50],[30,30],h=40) include prismoid([50,50],[30,30],h=40) position(RIGHT+TOP) - anchor_arrow(20, orient=RIGHT); + anchor_arrow(40, orient=RIGHT); ``` ```openscad-3D @@ -452,12 +455,12 @@ include prismoid([50,50],[30,30],h=40) position(RIGHT+TOP) orient(anchor=RIGHT) - anchor_arrow(20); + anchor_arrow(40); ``` Note also that `orient()` can be used to orient the child relative to -the absolute coordinate system using its first argument, `dir=`. This +the parent global coordinate system using its first argument, `dir=`. This use of `orient()` is the same as using the `orient=` argument for the child object. @@ -467,10 +470,22 @@ child object. Attachables get their name from their ability to be attached to each other. Unlike with positioning, attaching changes the orientation of the child object. When you attach an object, it appears on the parent -relative to the local coordinate system of the parent. To understand +relative to the local coordinate system of the parent at the anchor point. To understand what this means, imagine the perspective of an ant walking on a -sphere. If you attach a cylinder to the sphere then the cylinder will -be "up" from the ant's perspective. +sphere. The meaning of UP varies depending on where on the sphere the +ant is standing. If you **attach** a cylinder to the sphere then the cylinder will +be "up" from the ant's perspective. The first example shows a +cylinder placed with `position()` so it points up in the global parent +coordinate system. The second example shows how `attach()` points the +cylinder UP from the perspective of an ant standing at the anchor +point on the sphere. + +```openscad-3D +include +sphere(40) + position(RIGHT+TOP) cylinder(r=8,l=20); +``` + ```openscad-3D include @@ -491,9 +506,13 @@ direction you can use anchor arrows. ## Anchor Directions and Anchor Arrows +For the ant on the sphere it is obvious which direction is UP; that +direction corresponds to the Z+ axis. The location of the X and Y +axes is less clear and in fact it may be arbitrary. One way that is useful to show the position and orientation of an anchor point is by attaching an anchor arrow to that anchor. As noted before, the small red flag -points in the direction that is zero spin for the anchor. +points in the direction of the anchor's Y+ axis when the spin is +zero. ```openscad-3D include @@ -565,7 +584,7 @@ cube(50,center=true) In the second example, the child object point diagonally away from the cube. If you want the child at at edge of the parent it's likely that this result will not be what you want. To get a different -result, use `position()`, maybe combined with `orient(anchor=)`. +result, use `position()` with `orient(anchor=)`, if needed. If you give an anchor point to the child object it moves the child around (in the attached coordinate system). Or alternatively you can @@ -595,16 +614,11 @@ appeared above the anchor point. The CENTER anchor generally has a direction that points upward, so an attached object will keep its orientation if attached to the CENTER of a parent. -```openscad-3D -include -cube(50,center=true) - attach(RIGHT)anchor_arrow(30); - - By default, `attach()` places the child exactly flush with the surface of the parent. Sometimes it's useful to have the child overlap the parent by insetting a bit. You can do this with the `overlap=` argument to `attach()`. A positive value will inset the child into the parent, and -a negative value will outset out from the parent: +a negative value will outset out from the parent, which may be helpful +when doing differences. ```openscad-3D include @@ -621,16 +635,17 @@ cube(50,center=true) ``` As with `position()`, you can still apply your own translations and -other transformations even after anchoring an object. However, the +other transformations even after attaching an object. However, the order of operations now matters. If you apply a translation outside -of the anchor then it acts in the global coordinate system, so the +of the anchor then it acts in the parent's global coordinate system, so the child moves up in this example: ```openscad-3D include cube(50,center=true) - up(10) - attach(RIGHT)cylinder(d1=30,d2=15,l=25); + up(13) + attach(RIGHT) + cylinder(d1=30,d2=15,l=25); ``` On the other hand, if you put the translation between the attach and @@ -640,7 +655,9 @@ the parent, so in the example below it moves to the right. ```openscad-3D include cube(50,center=true) - attach(RIGHT) up(10) cylinder(d1=30,d2=15,l=25); + attach(RIGHT) + up(13) + cylinder(d1=30,d2=15,l=25); ``` @@ -658,13 +675,13 @@ attaching with those anchors. ```openscad-3D include -cube(50,center=true) attach(TOP) anchor_arrow(30); +cube(50,anchor=BOT) attach(TOP) anchor_arrow(30); right(60)cylinder(d1=30,d2=15,l=25) attach(TOP) anchor_arrow(30); ``` ```openscad-3D include -cube(50,center=true) +cube(50,anchor=BOT) attach(TOP,TOP) cylinder(d1=30,d2=15,l=25); ``` @@ -682,12 +699,20 @@ cube(50,center=true) Note that when you attach with two anchors like this, the attachment operation **overrides any anchor or orientation specified in the -child**. That means the child `anchor=` and `orient=` options are +child**. That means the child's `anchor=` and `orient=` options are ignored. Attachment with CENTER anchors can be surprising because the anchors point upwards, so in the example below, the child's CENTER anchor -points up, so it is inverted when it is attached to the parent cone. +points up, so it is inverted when it is attached to the parent cone. +Note that the anchors are CENTER anchors, so the bases of the anchors are +hidden in the middle of the objects. + +```openscad-3D +include +cylinder(d1=30,d2=15,l=25) attach(CENTER) anchor_arrow(40); +right(40)cylinder(d1=30,d2=15,l=25) attach(CENTER) anchor_arrow(40); +``` ```openscad-3D include @@ -715,13 +740,13 @@ desired anchors as a list to the `attach()` or `position()` modules: ```openscad-3D include cube(50, center=true) - attach([RIGHT,FRONT],TOP) cylinder(d1=50,d2=20,l=20); + attach([RIGHT,FRONT],TOP) cylinder(d1=35,d2=20,l=25); ``` ```openscad-3D include cube(50, center=true) - position([TOP,RIGHT,FRONT]) cylinder(d1=50,d2=20,l=20); + position([TOP,RIGHT,FRONT]) cylinder(d1=35,d2=20,l=25); ```