diff --git a/attachments.scad b/attachments.scad index b0401c5f..df481f81 100644 --- a/attachments.scad +++ b/attachments.scad @@ -1073,6 +1073,45 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, } + +// Module: attach_part() +// Synopsis: Select a named attachable part for subsequent attachment operations +// Topics: Attachment +// See Also: attach(), align(), attachable(), define_part(), parent_part() +// Usage: +// PARENT() attach_part(name) CHILDREN; +// Description: +// Most attachable objects have a single geometry defined that is used by the attachment commands, +// but some objects also define attachable parts. This module selects +// an attachable part using a name defined by the parent object. Any operations +// that use the parent geometry such as {{attach()}}, {{align()}}, {{position()}} or {{parent()}} +// will reference the geometry for the specified part. This allows you to access the inner wall +// of tubes, for example. Note that you cannot call `attach_part()` as a child of another `attach_part()`. +// Arguments: +// name = name of part to use for subsequent attachments. +// Example: This example shows attaching the light blue cube normally, on the outside of the tube, and the pink cube using the "inside" attachment part. +// tube(ir1=10,ir2=20,h=20, wall=3){ +// color("lightblue")attach(RIGHT,BOT) cuboid(4); +// color("pink") +// attach_part("inside") +// attach(BACK,BOT) cuboid(4); +// } + +module attach_part(name) +{ + req_children($children); + dummy=assert(!is_undef($parent_parts), "Parent does not exist or does not have any parts"); + ind = search([name], $parent_parts, 1,0)[0]; + dummy2 = assert(ind!=[], str("Parent does not have a part named ",name)); + $parent_geom = $parent_parts[ind][1]; + $anchor_inside = $parent_parts[ind][2]; + T = $parent_parts[ind][3]; + $parent_parts = []; + multmatrix(T) + children(); +} + + // Section: Tagging // Module: tag() @@ -1350,7 +1389,7 @@ module tag_scope(scope){ } -// Section: Attachment Modifiers +// Section: Tagged Operations with Attachable Objects // Module: diff() // Synopsis: Performs a differencing operation using tags rather than hierarchy to control what happens. @@ -3157,40 +3196,6 @@ module _show_ghost() } -// Module: attach_part() -// Synopsis: Select a named attachable part for subsequent attachment operations -// Topics: Attachment -// See Also: attach(), align(), attachable(), part_geometry() -// Usage: -// PARENT() attach_part(name) CHILDREN; -// Description: -// Selects an attachable part using a name defined by the parent object. Any operations -// that use the parent geometry such as {{attach()}}, {{align()}}, {{position()}} or {{parent()}} -// will reference the geometry for the specified part. This allows you to access the inner wall -// of tubes, for example. -// Arguments: -// name = name of part to use for subsequent attachments. -// Example: This example shows attaching the light blue cube normally, on the outside of the tube, and the pink cube using the "inside" attachment part. -// tube(ir1=10,ir2=20,h=20, wall=3){ -// color("lightblue")attach(RIGHT,BOT) cuboid(4); -// color("pink") -// attach_part("inside") -// attach(BACK,BOT) cuboid(4); -// } - -module attach_part(name) -{ - req_children($children); - dummy=assert(!is_undef($parent_parts), "Parent does not exist or does not have any parts"); - ind = search([name], $parent_parts, 1,0)[0]; - dummy2 = assert(ind!=[], str("Parent does not have a part named ",name)); - $parent_geom = $parent_parts[ind][1]; - $anchor_inside = $parent_parts[ind][2]; - multmatrix($parent_parts[ind][3]) - children(); -} - - function _is_geometry(entry) = is_list(entry) && is_string(entry[0]); @@ -3583,12 +3588,12 @@ function attach_geom( : ["point", cp, offset, anchors]; -// Function: part_geometry() +// Function: define_part() // Synopsis: Creates an attachable part data structure. // Topics: Attachments // See Also: attachable() // Usage: -// part = part_geometry(name, geom, [inside=], [T=]); +// part = define_part(name, geom, [inside=], [T=]); // Description: // Create a named attachable part that can be passed in the `parts` parameter of {{attachable()}} // and then selected using {{attach_part()}}. @@ -3602,8 +3607,8 @@ function attach_geom( // module twocyl(d, sep, h, ang=20) // { // parts = [ -// part_geometry("left", attach_geom(r=d/2,h=h), T=left(sep/2)*yrot(-ang)), -// part_geometry("right", attach_geom(r=d/2,h=h), T=right(sep/2)*yrot(ang)), +// define_part("left", attach_geom(r=d/2,h=h), T=left(sep/2)*yrot(-ang)), +// define_part("right", attach_geom(r=d/2,h=h), T=right(sep/2)*yrot(ang)), // ]; // attachable(size=[sep+d,d,h], parts=parts){ // union(){ @@ -3619,7 +3624,7 @@ function attach_geom( // color("green")attach_part("right")attach(TOP,BOT) cuboid(3); // } -function part_geometry(name, geom, inside=false, T=IDENT) = +function define_part(name, geom, inside=false, T=IDENT) = assert(is_string(name), "name must be a string") assert(_is_geometry(geom), "geometry appears invalid") assert(is_bool(inside), "inside must be boolean") @@ -5143,7 +5148,7 @@ function _canonical_edge(edge) = // Function: parent() // Topics: Transforms, Attachments, Descriptions -// See Also: restore() +// See Also: restore(), parent_part() // Synopsis: Returns a description (transformation state and attachment geometry) of the parent // Usage: // PARENT() let( desc = parent() ) CHILDREN; @@ -5164,6 +5169,56 @@ function parent() = [$transform, geom]; + +// Function: parent_part() +// Topics: Transforms, Attachments, Descriptions +// See Also: restore(), parent() +// Synopsis: Returns a description (transformation state and attachment geometry) of a part defined by the parent +// Usage: +// PARENT() let( desc = parent_part(name) ) CHILDREN; +// Usage: in development releases only +// PARENT() { desc=parent_part(name); CHILDREN; } +// Description: +// Returns a description of the parent part with the specified name. You can use this +// description to create new objects based on the described object or perform computations based on the described object. You can also use it to +// restore the context of the parent object and transformation state using {{restore()}}. Note that with OpenSCAD 2021.01 you need to use `let` for +// this function to work, and the definition of the variable is scoped to the children of the let module. +// (In development versions the use of let is no longer necessary.) Note that if OpenSCAD displays any warnings +// related to transformation operations then the transformation that parent_part() returns is likely to be incorrect, even if OpenSCAD +// continues to run and produces a valid result. +// Example(3D): This example defines an object with two parts and then uses `parent_part()` to create a {{prism_connector()}} between the two parts of the object. +// $fn=48; +// module twocyl(d, sep, h, ang=20) +// { +// parts = [ +// define_part("left", attach_geom(r=d/2,h=h), +// T=left(sep/2)*yrot(-ang)), +// define_part("right", attach_geom(r=d/2,h=h), +// T=right(sep/2)*yrot(ang)), +// ]; +// attachable(size=[sep+d,d,h], parts=parts){ +// union(){ +// left(sep/2) yrot(-ang) cyl(d=d,h=h); +// right(sep/2) yrot(ang) cyl(d=d,h=h); +// } +// children(); +// } +// } +// twocyl(d=10,sep=20,h=10) +// prism_connector(circle(r=2,$fn=32), +// parent_part("left"), RIGHT, +// parent_part("right"), LEFT, +// fillet=1); + +function parent_part(name) = + assert(!is_undef($parent_parts), "Parent does not exist or does not have any parts") + let( + ind = search([name], $parent_parts, 1,0)[0] + ) + assert(ind!=[], str("Parent does not have a part named ",name)) + [$transform * $parent_parts[ind][3], $parent_parts[ind][1]]; + + // Module: restore() // Synopsis: Restores transformation state and attachment geometry from a description // Topics: Transforms, Attachments, Descriptions diff --git a/rounding.scad b/rounding.scad index 37e10ac4..27d054e4 100644 --- a/rounding.scad +++ b/rounding.scad @@ -4360,6 +4360,17 @@ function _prism_fillet_prism(name, basepoly, bot, top, d, k, N, overlap, uniform // tag("remove") cyl(d=75,h=40,$fn=128) // tag("keep") zrot_copies(n=4) // prism_connector(circ,parent(),[-1,.2],parent(),[1,.4],shift1=12,shift2=-12,fillet=2); +// Example(3D): You can also make a connection between the "inside" part of a tube and the outside of a tube +// diff() +// tube(or=10,wall=2,h=10,rounding=1,$fn=40) +// let(outside=parent()) +// attach_part("inside") +// tag("remove") +// for(where = [LEFT,RIGHT,FRONT,BACK]) +// prism_connector(circle(3,$fn=100), +// outside, where, +// parent(), where, +// fillet=1); // Example(3D,Med,NoAxes,VPT=[1.42957,2.47871,-3.63111],VPR=[40.3,0,29.2],VPD=263.435): Here we use the {{zrot_copies()}} distributor to create copies of objects and create a connector to a non-symmetrically placed object. All all the connectors are different because we change the anchor point that goes with the second description. // circ = circle(r=3, $fn=64); // right(4)up(25)xrot(15) cyl(r=20,h=30,circum=true,$fn=64) let(cyl=parent()) diff --git a/shapes3d.scad b/shapes3d.scad index 15a05fce..f0d76bc5 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -1689,7 +1689,7 @@ module rect_tube( ichamfer2 = _rect_tube_rounding(1/sqrt(2),ichamfer2_temp, chamfer2, irounding2_temp, size2, isize2); anchor = get_anchor(anchor, center, BOT, BOT); parts = [ - part_geometry("inside", attach_geom(size=[each isize1, h], size2=isize2, shift=shift), inside=true) + define_part("inside", attach_geom(size=[each isize1, h], size2=isize2, shift=shift), inside=true) ]; attachable(anchor,spin,orient, size=[each size1, h], size2=size2, shift=shift, parts=parts) { down(h/2) { @@ -2971,7 +2971,7 @@ module tube( [0,h/2+1] ]; parts = [ - part_geometry("inside", attach_geom(r1=ir1, r2=ir2, l=h), inside=true) + define_part("inside", attach_geom(r1=ir1, r2=ir2, l=h), inside=true) ]; attachable(anchor,spin,orient, r1=r1, r2=r2, l=h, parts=parts) { down(h/2) skew(sxz=shift.x/h, syz=shift.y/h) up(h/2)