Merge pull request #1749 from amatulic/anachronist_dev

xcyl(), ycyl(), zcyl() pass through all parameters to cyl()
This commit is contained in:
adrianVmariano
2025-07-17 14:27:21 -04:00
committed by GitHub
2 changed files with 369 additions and 307 deletions

View File

@@ -509,10 +509,10 @@ module position(at,from)
if (is_def(from)){ if (is_def(from)){
echo("'from' argument of position() has changed to 'at' and will be removed in a future version"); echo("'from' argument of position() has changed to 'at' and will be removed in a future version");
} }
dummy0=assert(num_defined([at,from])==1, "Cannot give both `at` argument and the deprectated `from` argument to position()"); dummy0=assert(num_defined([at,from])==1, "\nCannot give both `at` argument and the deprectated `from` argument to position().");
at = first_defined([at,from]); at = first_defined([at,from]);
req_children($children); req_children($children);
dummy1=assert($parent_geom != undef, "No object to position relative to."); dummy1=assert($parent_geom != undef, "\nNo object to position relative to.");
anchors = (is_vector(at)||is_string(at))? [at] : at; anchors = (is_vector(at)||is_string(at))? [at] : at;
two_d = _attach_geom_2d($parent_geom); two_d = _attach_geom_2d($parent_geom);
for (anchr = anchors) { for (anchr = anchors) {
@@ -563,7 +563,7 @@ module position(at,from)
module orient(anchor, spin) { module orient(anchor, spin) {
req_children($children); req_children($children);
check= check=
assert($parent_geom != undef, "No parent to orient from!") assert($parent_geom != undef, "\nNo parent to orient from!")
assert(is_string(anchor) || is_vector(anchor)); assert(is_string(anchor) || is_vector(anchor));
anch = _find_anchor(anchor, $parent_geom); anch = _find_anchor(anchor, $parent_geom);
two_d = _attach_geom_2d($parent_geom); two_d = _attach_geom_2d($parent_geom);
@@ -700,8 +700,8 @@ module align(anchor,align=CENTER,inside=false,inset=0,shiftout=0,overlap)
{ {
req_children($children); req_children($children);
overlap = (overlap!=undef)? overlap : $overlap; overlap = (overlap!=undef)? overlap : $overlap;
dummy1=assert($parent_geom != undef, "No object to align to.") dummy1=assert($parent_geom != undef, "\nNo object to align to.")
assert(is_undef($attach_to), "Cannot use align() as a child of attach()"); assert(is_undef($attach_to), "\nCannot use align() as a child of attach().");
anchor = is_vector(anchor) ? [anchor] : anchor; anchor = is_vector(anchor) ? [anchor] : anchor;
align = is_vector(align) ? [align] : align; align = is_vector(align) ? [align] : align;
two_d = _attach_geom_2d($parent_geom); two_d = _attach_geom_2d($parent_geom);
@@ -712,19 +712,19 @@ module align(anchor,align=CENTER,inside=false,inset=0,shiftout=0,overlap)
$anchor=face; $anchor=face;
dummy= dummy=
assert(!is_string(face), assert(!is_string(face),
str("Named anchor \"",face,"\" given for anchor, but align() does not support named anchors")) str("\nNamed anchor \"",face,"\" given for anchor, but align() does not support named anchors."))
assert(is_vector(face) && (len(face)==2 || len(face)==3), assert(is_vector(face) && (len(face)==2 || len(face)==3),
str("Invalid face ",face, ". Must be a 2-vector or 3-vector")); str("\nInvalid face ",face, ". Must be a 2-vector or 3-vector."));
thisface = two_d? _force_anchor_2d(face) : point3d(face); thisface = two_d? _force_anchor_2d(face) : point3d(face);
for(j = idx(align)) { for(j = idx(align)) {
edge=align[j]; edge=align[j];
$idx = j+len(align)*i; $idx = j+len(align)*i;
$align=edge; $align=edge;
dummy1=assert(is_vector(edge) && (len(edge)==2 || len(edge)==3), dummy1=assert(is_vector(edge) && (len(edge)==2 || len(edge)==3),
"align direction must be a 2-vector or 3-vector"); "\nalign direction must be a 2-vector or 3-vector.");
thisedge = two_d? _force_anchor_2d(edge) : point3d(edge); thisedge = two_d? _force_anchor_2d(edge) : point3d(edge);
dummy=assert(all_zero(v_mul(thisedge,thisface)), dummy=assert(all_zero(v_mul(thisedge,thisface)),
str("align (",thisedge,") cannot include component parallel to anchor ",thisface)); str("\nalign (",thisedge,") cannot include component parallel to anchor ",thisface,"."));
thisface_anch = _find_anchor(thisface, $parent_geom); thisface_anch = _find_anchor(thisface, $parent_geom);
inset_dir = two_d ? -thisface inset_dir = two_d ? -thisface
: unit(thisface_anch[1]-_find_anchor([thisedge.x,0,0]+thisface, $parent_geom)[1],CTR) : unit(thisface_anch[1]-_find_anchor([thisedge.x,0,0]+thisface, $parent_geom)[1],CTR)
@@ -931,10 +931,10 @@ function _make_anchor_legal(anchor,geom) =
module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, inside=false, from, to) module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, inside=false, from, to)
{ {
dummy3= dummy3=
assert(num_defined([to,child])<2, "Cannot combine deprecated 'to' argument with 'child' parameter") assert(num_defined([to,child])<2, "\nCannot combine deprecated 'to' argument with 'child' parameter.")
assert(num_defined([from,parent])<2, "Cannot combine deprecated 'from' argument with 'parent' parameter") assert(num_defined([from,parent])<2, "\nCannot combine deprecated 'from' argument with 'parent' parameter.")
assert(spin!="align" || is_def(align), "Can only set spin to \"align\" when the 'align' parameter is given") assert(spin!="align" || is_def(align), "\nCan only set spin to \"align\" when the 'align' parameter is given.")
assert(is_finite(spin) || spin=="align", "Spin must be a number (unless align is given)") assert(is_finite(spin) || spin=="align", "\nSpin must be a number (unless align is given).")
assert((is_undef(overlap) || is_finite(overlap)) && (is_def(overlap) || is_undef($overlap) || is_finite($overlap)), assert((is_undef(overlap) || is_finite(overlap)) && (is_def(overlap) || is_undef($overlap) || is_finite($overlap)),
str("Provided ",is_def(overlap)?"":"$","overlap is not valid.")); str("Provided ",is_def(overlap)?"":"$","overlap is not valid."));
removetag = inside; removetag = inside;
@@ -947,10 +947,10 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
echo("The 'norot' option to attach() is deprecated and will be removed in the future. Use position() instead."); echo("The 'norot' option to attach() is deprecated and will be removed in the future. Use position() instead.");
req_children($children); req_children($children);
dummy=assert($parent_geom != undef, "No object to attach to!") dummy=assert($parent_geom != undef, "\nNo object to attach to!")
assert(is_undef(child) || is_string(child) || (is_vector(child) && (len(child)==2 || len(child)==3)), assert(is_undef(child) || is_string(child) || (is_vector(child) && (len(child)==2 || len(child)==3)),
"child must be a named anchor (a string) or a 2-vector or 3-vector") "\nChild must be a named anchor (a string) or a 2-vector or 3-vector.")
assert(is_undef(align) || !is_string(child), "child is a named anchor. Named anchors are not supported with align="); assert(is_undef(align) || !is_string(child), "\nChild is a named anchor. Named anchors are not supported with align=.");
two_d = _attach_geom_2d($parent_geom); two_d = _attach_geom_2d($parent_geom);
basegeom = $parent_geom[0]=="conoid" ? attach_geom(r=2,h=2,axis=$parent_geom[5]) basegeom = $parent_geom[0]=="conoid" ? attach_geom(r=2,h=2,axis=$parent_geom[5])
@@ -963,22 +963,22 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
anchors = is_vector(parent) || is_string(parent) ? [parent] : parent; anchors = is_vector(parent) || is_string(parent) ? [parent] : parent;
align_list = is_undef(align) ? [undef] align_list = is_undef(align) ? [undef]
: is_vector(align) || is_string(align) ? [align] : align; : is_vector(align) || is_string(align) ? [align] : align;
dummy4 = assert(is_string(parent) || is_list(parent), "Invalid parent anchor or anchor list") dummy4 = assert(is_string(parent) || is_list(parent), "\nInvalid parent anchor or anchor list.")
assert(spin==0 || (!two_d || is_undef(child)), "spin is not allowed for 2d objects when 'child' is given"); assert(spin==0 || (!two_d || is_undef(child)), "\nspin is not allowed for 2d objects when 'child' is given.");
child_temp = first_defined([child,to]); child_temp = first_defined([child,to]);
child = two_d ? _force_anchor_2d(child_temp) : child_temp; child = two_d ? _force_anchor_2d(child_temp) : child_temp;
dummy2=assert(align_list==[undef] || is_def(child), "Cannot use 'align' without 'child'") dummy2=assert(align_list==[undef] || is_def(child), "\nCannot use 'align' without 'child'.")
assert(!inside || is_def(child), "Cannot use 'inside' without 'child'") assert(!inside || is_def(child), "\nCannot use 'inside' without 'child'.")
assert(inset==0 || is_def(child), "Cannot specify 'inset' without 'child'") assert(inset==0 || is_def(child), "\nCannot specify 'inset' without 'child'.")
assert(inset==0 || is_def(align), "Cannot specify 'inset' without 'align'") assert(inset==0 || is_def(align), "\nCannot specify 'inset' without 'align'.")
assert(shiftout==0 || is_def(child), "Cannot specify 'shiftout' without 'child'"); assert(shiftout==0 || is_def(child), "\nCannot specify 'shiftout' without 'child'.");
factor = inside?-1:1; factor = inside?-1:1;
$attach_to = child; $attach_to = child;
for (anch_ind = idx(anchors)) { for (anch_ind = idx(anchors)) {
dummy=assert(is_string(anchors[anch_ind]) || (is_vector(anchors[anch_ind]) && (len(anchors[anch_ind])==2 || len(anchors[anch_ind])==3)), dummy=assert(is_string(anchors[anch_ind]) || (is_vector(anchors[anch_ind]) && (len(anchors[anch_ind])==2 || len(anchors[anch_ind])==3)),
str("parent[",anch_ind,"] is ",anchors[anch_ind]," but it must be a named anchor (string) or a 2-vector or 3-vector")) str("\nParent[",anch_ind,"] is ",anchors[anch_ind]," but it must be a named anchor (string), a 2-vector, or 3-vector."))
assert(align_list==[undef] || !is_string(anchors[anch_ind]), assert(align_list==[undef] || !is_string(anchors[anch_ind]),
str("parent[",anch_ind,"] is a named anchor (",anchors[anch_ind],"), but named anchors are not supported with align=")); str("\nParent[",anch_ind,"] is a named anchor (",anchors[anch_ind],"), but named anchors are not supported with align=."));
anchor = is_string(anchors[anch_ind])? anchors[anch_ind] anchor = is_string(anchors[anch_ind])? anchors[anch_ind]
: two_d?_force_anchor_2d(anchors[anch_ind]) : two_d?_force_anchor_2d(anchors[anch_ind])
: point3d(anchors[anch_ind]); : point3d(anchors[anch_ind]);
@@ -995,7 +995,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
parent_abstract_anchor = is_vector(anchor) && !two_d ? _find_anchor(_make_anchor_legal(anchor,basegeom),basegeom) : undef; parent_abstract_anchor = is_vector(anchor) && !two_d ? _find_anchor(_make_anchor_legal(anchor,basegeom),basegeom) : undef;
for(align_ind = idx(align_list)){ for(align_ind = idx(align_list)){
align = is_undef(align_list[align_ind]) ? undef align = is_undef(align_list[align_ind]) ? undef
: assert(is_vector(align_list[align_ind],2) || is_vector(align_list[align_ind],3), "align direction must be a 2-vector or 3-vector") : assert(is_vector(align_list[align_ind],2) || is_vector(align_list[align_ind],3), "\nAlign direction must be a 2-vector or 3-vector.")
two_d ? _force_anchor_2d(align_list[align_ind]) two_d ? _force_anchor_2d(align_list[align_ind])
: point3d(align_list[align_ind]); : point3d(align_list[align_ind]);
spin = is_num(spin) ? spin spin = is_num(spin) ? spin
@@ -1024,10 +1024,10 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
badcorner = !in_list($parent_geom[0],["conoid","spheroid"]) && !is_undef(align) && align!=CTR && sum(v_abs(anchor))==3; badcorner = !in_list($parent_geom[0],["conoid","spheroid"]) && !is_undef(align) && align!=CTR && sum(v_abs(anchor))==3;
badsphere = $parent_geom[0]=="spheroid" && !is_undef(align) && align!=CTR; badsphere = $parent_geom[0]=="spheroid" && !is_undef(align) && align!=CTR;
dummy=assert(is_undef(align) || all_zero(v_mul(anchor,align)), dummy=assert(is_undef(align) || all_zero(v_mul(anchor,align)),
str("Invalid alignment: align value (",align,") includes component parallel to parent anchor (",anchor,")")) str("\nInvalid alignment: align value (",align,") includes component parallel to parent anchor (",anchor,")."))
assert(goodcyl, str("Cannot use align with an anchor on a curved edge or surface of a cylinder at parent anchor (",anchor,")")) assert(goodcyl, str("\nCannot use align with an anchor on a curved edge or surface of a cylinder at parent anchor (",anchor,")."))
assert(!badcorner, str("Cannot use align at a corner anchor (",anchor,")")) assert(!badcorner, str("\nCannot use align at a corner anchor (",anchor,")."))
assert(!badsphere, "Cannot use align on spheres."); assert(!badsphere, "\nCannot use align on spheres.");
// Now compute position on the parent (including alignment but not inset) where the child will be anchored // Now compute position on the parent (including alignment but not inset) where the child will be anchored
pos = is_undef(align) ? anchor_data[1] : _find_anchor(anchor+align, $parent_geom)[1]; pos = is_undef(align) ? anchor_data[1] : _find_anchor(anchor+align, $parent_geom)[1];
$attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all? $attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all?
@@ -1100,9 +1100,9 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
module attach_part(name) module attach_part(name)
{ {
req_children($children); req_children($children);
dummy=assert(!is_undef($parent_parts), "Parent does not exist or does not have any parts"); dummy=assert(!is_undef($parent_parts), "\nParent does not exist or does not have any parts.");
ind = search([name], $parent_parts, 1,0)[0]; ind = search([name], $parent_parts, 1,0)[0];
dummy2 = assert(ind!=[], str("Parent does not have a part named ",name)); dummy2 = assert(ind!=[], str("\nParent does not have a part named \"",name,"\"."));
$parent_geom = $parent_parts[ind][1]; $parent_geom = $parent_parts[ind][1];
$anchor_inside = $parent_parts[ind][2]; $anchor_inside = $parent_parts[ind][2];
T = $parent_parts[ind][3]; T = $parent_parts[ind][3];
@@ -1145,8 +1145,8 @@ module tag(tag)
{ {
req_children($children); req_children($children);
check= check=
assert(is_string(tag),"tag must be a string") assert(is_string(tag),"\n'tag' must be a string.")
assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); assert(undef==str_find(tag," "),str("\nTag string \"",tag,"\" contains a space, which is not allowed."));
$tag = str($tag_prefix,tag); $tag = str($tag_prefix,tag);
children(); children();
} }
@@ -1178,8 +1178,8 @@ module tag_this(tag)
{ {
req_children($children); req_children($children);
check= check=
assert(is_string(tag),"tag must be a string") assert(is_string(tag),"\n'tag' must be a string.")
assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); assert(undef==str_find(tag," "),str("\nTag string \"",tag,"\" contains a space, which is not allowed."));
$save_tag=default($tag,""); $save_tag=default($tag,"");
$tag = str($tag_prefix,tag); $tag = str($tag_prefix,tag);
children(); children();
@@ -1239,9 +1239,9 @@ module tag_this(tag)
module force_tag(tag) module force_tag(tag)
{ {
req_children($children); req_children($children);
check1=assert(is_undef(tag) || is_string(tag),"tag must be a string"); check1=assert(is_undef(tag) || is_string(tag),"\n'tag' must be a string.");
$tag = str($tag_prefix,default(tag,$tag)); $tag = str($tag_prefix,default(tag,$tag));
assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed")); assert(undef==str_find($tag," "),str("\nTag string \"",$tag,"\" contains a space, which is not allowed."));
if(_is_shown()) if(_is_shown())
show_all() show_all()
children(); children();
@@ -1382,8 +1382,8 @@ module default_tag(tag,do_tag=true)
module tag_scope(scope){ module tag_scope(scope){
req_children($children); req_children($children);
scope = is_undef(scope) ? rand_str(20) : scope; scope = is_undef(scope) ? rand_str(20) : scope;
assert(is_string(scope), "scope must be a string"); assert(is_string(scope), "\n'scope' must be a string.");
assert(undef==str_find(scope," "),str("Scope string \"",scope,"\" contains a space, which is not allowed")); assert(undef==str_find(scope," "),str("\nScope string \"",scope,"\" contains a space, which is not allowed."));
$tag_prefix=scope; $tag_prefix=scope;
children(); children();
} }
@@ -1569,8 +1569,8 @@ module tag_scope(scope){
module diff(remove="remove", keep="keep") module diff(remove="remove", keep="keep")
{ {
req_children($children); req_children($children);
assert(is_string(remove),"remove must be a string of tags"); assert(is_string(remove),"\n'remove' must be a string of tags.");
assert(is_string(keep),"keep must be a string of tags"); assert(is_string(keep),"\n'keep' must be a string of tags.");
if (_is_shown()) if (_is_shown())
{ {
difference() { difference() {
@@ -1637,10 +1637,10 @@ module diff(remove="remove", keep="keep")
module tag_diff(tag="",remove="remove", keep="keep") module tag_diff(tag="",remove="remove", keep="keep")
{ {
req_children($children); req_children($children);
assert(is_string(remove),"remove must be a string of tags"); assert(is_string(remove),"\n'remove' must be a string of tags.");
assert(is_string(keep),"keep must be a string of tags"); assert(is_string(keep),"\n'keep' must be a string of tags.");
assert(is_string(tag),"tag must be a string"); assert(is_string(tag),"\n'tag' must be a string.");
assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); assert(undef==str_find(tag," "),str("\nTag string \"",tag,"\" contains a space, which is not allowed."));
$tag=str($tag_prefix,tag); $tag=str($tag_prefix,tag);
if (_is_shown()) if (_is_shown())
show_all(){ show_all(){
@@ -1715,8 +1715,8 @@ module tag_diff(tag="",remove="remove", keep="keep")
// } // }
module intersect(intersect="intersect",keep="keep") module intersect(intersect="intersect",keep="keep")
{ {
assert(is_string(intersect),"intersect must be a string of tags"); assert(is_string(intersect),"\n'intersect' must be a string of tags.");
assert(is_string(keep),"keep must be a string of tags"); assert(is_string(keep),"\n'keep' must be a string of tags.");
intersection(){ intersection(){
show_only(intersect) children(); show_only(intersect) children();
hide(str(intersect," ",keep)) children(); hide(str(intersect," ",keep)) children();
@@ -1765,10 +1765,10 @@ module intersect(intersect="intersect",keep="keep")
// } // }
module tag_intersect(tag="",intersect="intersect",keep="keep") module tag_intersect(tag="",intersect="intersect",keep="keep")
{ {
assert(is_string(intersect),"intersect must be a string of tags"); assert(is_string(intersect),"\n'intersect' must be a string of tags.");
assert(is_string(keep),"keep must be a string of tags"); assert(is_string(keep),"\n'keep' must be a string of tags.");
assert(is_string(tag),"tag must be a string"); assert(is_string(tag),"\n'tag' must be a string.");
assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); assert(undef==str_find(tag," "),str("\nTag string \"",tag,"\" contains a space, which is not allowed."));
$tag=str($tag_prefix,tag); $tag=str($tag_prefix,tag);
if (_is_shown()) if (_is_shown())
show_all(){ show_all(){
@@ -1813,7 +1813,7 @@ module tag_intersect(tag="",intersect="intersect",keep="keep")
module conv_hull(keep="keep") module conv_hull(keep="keep")
{ {
req_children($children); req_children($children);
assert(is_string(keep),"keep must be a string of tags"); assert(is_string(keep),"\n'keep' must be a string of tags.");
if (_is_shown()) if (_is_shown())
hull() hide(keep) children(); hull() hide(keep) children();
show_int(keep) children(); show_int(keep) children();
@@ -1860,9 +1860,9 @@ module conv_hull(keep="keep")
module tag_conv_hull(tag="",keep="keep") module tag_conv_hull(tag="",keep="keep")
{ {
req_children($children); req_children($children);
assert(is_string(keep),"keep must be a string of tags"); assert(is_string(keep),"\n'keep' must be a string of tags.");
assert(is_string(tag),"tag must be a string"); assert(is_string(tag),"\n'tag' must be a string.");
assert(undef==str_find(tag," "),str("Tag string \"",tag,"\" contains a space, which is not allowed")); assert(undef==str_find(tag," "),str("\nTag string \"",tag,"\" contains a space, which is not allowed."));
$tag=str($tag_prefix,tag); $tag=str($tag_prefix,tag);
if (_is_shown()) if (_is_shown())
show_all(){ show_all(){
@@ -1900,7 +1900,7 @@ module tag_conv_hull(tag="",keep="keep")
module hide(tags) module hide(tags)
{ {
req_children($children); req_children($children);
dummy=assert(is_string(tags), "tags must be a string"); dummy=assert(is_string(tags), "\n'tags' must be a string.");
taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)];
$tags_hidden = concat($tags_hidden,taglist); $tags_hidden = concat($tags_hidden,taglist);
children(); children();
@@ -1961,7 +1961,7 @@ module hide_this()
module show_only(tags) module show_only(tags)
{ {
req_children($children); req_children($children);
dummy=assert(is_string(tags), str("tags must be a string",tags)); dummy=assert(is_string(tags), str("\n'tags' must be a string.",tags));
taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)];
$tags_shown = taglist; $tags_shown = taglist;
children(); children();
@@ -2004,7 +2004,7 @@ module show_all()
module show_int(tags) module show_int(tags)
{ {
req_children($children); req_children($children);
dummy=assert(is_string(tags), str("tags must be a string",tags)); dummy=assert(is_string(tags), str("\n'tags' must be a string.",tags));
taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)]; taglist = [for(s=str_split(tags," ",keep_nulls=false)) str($tag_prefix,s)];
$tags_shown = $tags_shown == "ALL" ? taglist : set_intersection($tags_shown,taglist); $tags_shown = $tags_shown == "ALL" ? taglist : set_intersection($tags_shown,taglist);
children(); children();
@@ -2049,8 +2049,8 @@ module show_int(tags)
module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) { module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) {
req_children($children); req_children($children);
faces = is_vector(faces)? [faces] : faces; faces = is_vector(faces)? [faces] : faces;
assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "\nVector in faces doesn't point at a face.");
assert($parent_geom != undef, "No object to attach to!"); assert($parent_geom != undef, "\nNo object to attach to!");
attach(faces) { attach(faces) {
default_tag("remove") children(); default_tag("remove") children();
} }
@@ -2094,7 +2094,7 @@ module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) {
// rounding_edge_mask(l=71,r=10); // rounding_edge_mask(l=71,r=10);
module edge_mask(edges=EDGES_ALL, except=[]) { module edge_mask(edges=EDGES_ALL, except=[]) {
req_children($children); req_children($children);
assert($parent_geom != undef, "No object to attach to!"); assert($parent_geom != undef, "\nNo object to attach to!");
edges = _edges(edges, except=except); edges = _edges(edges, except=except);
vecs = [ vecs = [
for (i = [0:3], axis=[0:2]) for (i = [0:3], axis=[0:2])
@@ -2104,7 +2104,7 @@ module edge_mask(edges=EDGES_ALL, except=[]) {
for ($idx = idx(vecs)) { for ($idx = idx(vecs)) {
vec = vecs[$idx]; vec = vecs[$idx];
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
dummy=assert(vcount == 2, "Not an edge vector!"); dummy=assert(vcount == 2, "\nNot an edge vector!");
anch = _find_anchor(vec, $parent_geom); anch = _find_anchor(vec, $parent_geom);
$edge_angle = len(anch)==5 ? struct_val(anch[4],"edge_angle") : undef; $edge_angle = len(anch)==5 ? struct_val(anch[4],"edge_angle") : undef;
$edge_length = len(anch)==5 ? struct_val(anch[4],"edge_length") : undef; $edge_length = len(anch)==5 ? struct_val(anch[4],"edge_length") : undef;
@@ -2151,13 +2151,13 @@ module edge_mask(edges=EDGES_ALL, except=[]) {
// } // }
module corner_mask(corners=CORNERS_ALL, except=[]) { module corner_mask(corners=CORNERS_ALL, except=[]) {
req_children($children); req_children($children);
assert($parent_geom != undef, "No object to attach to!"); assert($parent_geom != undef, "\nNo object to attach to!");
corners = _corners(corners, except=except); corners = _corners(corners, except=except);
vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]];
for ($idx = idx(vecs)) { for ($idx = idx(vecs)) {
vec = vecs[$idx]; vec = vecs[$idx];
vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0);
dummy=assert(vcount == 3, "Not an edge vector!"); dummy=assert(vcount == 3, "\nNot an edge vector!");
anch = _find_anchor(vec, $parent_geom); anch = _find_anchor(vec, $parent_geom);
$attach_to = undef; $attach_to = undef;
$attach_anchor = anch; $attach_anchor = anch;
@@ -2202,7 +2202,7 @@ module corner_mask(corners=CORNERS_ALL, except=[]) {
module face_profile(faces=[], r, d, excess=0.01, convexity=10) { module face_profile(faces=[], r, d, excess=0.01, convexity=10) {
req_children($children); req_children($children);
faces = is_vector(faces)? [faces] : faces; faces = is_vector(faces)? [faces] : faces;
assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "\nVector in faces doesn't point at a face.");
r = get_radius(r=r, d=d, dflt=undef); r = get_radius(r=r, d=d, dflt=undef);
assert(is_num(r) && r>=0); assert(is_num(r) && r>=0);
edge_profile(faces, excess=excess) children(); edge_profile(faces, excess=excess) children();
@@ -2256,11 +2256,11 @@ module face_profile(faces=[], r, d, excess=0.01, convexity=10) {
module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) { module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
req_children($children); req_children($children);
check1 = assert($parent_geom != undef, "No object to attach to!"); check1 = assert($parent_geom != undef, "\nNo object to attach to!");
conoid = $parent_geom[0] == "conoid"; conoid = $parent_geom[0] == "conoid";
edges = !conoid? _edges(edges, except=except) : edges = !conoid? _edges(edges, except=except) :
edges==EDGES_ALL? [TOP,BOT] : edges==EDGES_ALL? [TOP,BOT] :
assert(all([for (e=edges) in_list(e,[TOP,BOT])]), "Invalid conoid edge spec.") assert(all([for (e=edges) in_list(e,[TOP,BOT])]), "\nInvalid conoid edge spec.")
edges; edges;
vecs = conoid vecs = conoid
? [for (e=edges) e+FWD] ? [for (e=edges) e+FWD]
@@ -2270,7 +2270,7 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
EDGE_OFFSETS[axis][i] EDGE_OFFSETS[axis][i]
]; ];
all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]); all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]);
check2 = assert(all_vecs_are_edges, "All vectors must be edges."); check2 = assert(all_vecs_are_edges, "\nAll vectors must be edges.");
default_tag("remove") default_tag("remove")
for ($idx = idx(vecs)) { for ($idx = idx(vecs)) {
vec = vecs[$idx]; vec = vecs[$idx];
@@ -2452,7 +2452,7 @@ module edge_profile_asym(
[BACK+RIGHT, [TOP+BACK, BOT+BACK]], [BACK+RIGHT, [TOP+BACK, BOT+BACK]],
], ],
i = search([from], flip_edges, num_returns_per_match=1)[0], i = search([from], flip_edges, num_returns_per_match=1)[0],
check = assert(i!=[], "Bad edge vector.") check = assert(i!=[], "\nBad edge vector.")
) in_list(to,flip_edges[i][1]); ) in_list(to,flip_edges[i][1]);
function _edge_corner_numbers(vec) = function _edge_corner_numbers(vec) =
@@ -2467,7 +2467,7 @@ module edge_profile_asym(
function _gather_contiguous_edges(edge_corners) = function _gather_contiguous_edges(edge_corners) =
let( let(
no_tri_corners = all([for(cn = [0:7]) len([for (ec=edge_corners) if(in_list(cn,ec[1])) 1])<3]), no_tri_corners = all([for(cn = [0:7]) len([for (ec=edge_corners) if(in_list(cn,ec[1])) 1])<3]),
check = assert(no_tri_corners, "Cannot have three edges that meet at the same corner.") check = assert(no_tri_corners, "\nCannot have three edges that meet at the same corner.")
) )
_gather_contiguous_edges_r( _gather_contiguous_edges_r(
[for (i=idx(edge_corners)) if(i) edge_corners[i]], [for (i=idx(edge_corners)) if(i) edge_corners[i]],
@@ -2552,7 +2552,7 @@ module edge_profile_asym(
[for (i=[0:2]) if (abs(e1[i])==1 && e1[i]==e2[i]) -e1[i] else 0]; [for (i=[0:2]) if (abs(e1[i])==1 && e1[i]==e2[i]) -e1[i] else 0];
req_children($children); req_children($children);
check1 = assert($parent_geom != undef, "No object to attach to!") check1 = assert($parent_geom != undef, "\nNo object to attach to!")
assert(in_list(corner_type, ["none", "round", "chamfer", "sharp"])) assert(in_list(corner_type, ["none", "round", "chamfer", "sharp"]))
assert(is_bool(flip)); assert(is_bool(flip));
edges = _edges(edges, except=except); edges = _edges(edges, except=except);
@@ -2562,7 +2562,7 @@ module edge_profile_asym(
EDGE_OFFSETS[axis][i] EDGE_OFFSETS[axis][i]
]; ];
all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]); all_vecs_are_edges = all([for (vec = vecs) sum(v_abs(vec))==2]);
check2 = assert(all_vecs_are_edges, "All vectors must be edges."); check2 = assert(all_vecs_are_edges, "\nAll vectors must be edges.");
edge_corners = [for (vec = vecs) [vec, _edge_corner_numbers(vec)]]; edge_corners = [for (vec = vecs) [vec, _edge_corner_numbers(vec)]];
edge_strings = _gather_contiguous_edges(edge_corners); edge_strings = _gather_contiguous_edges(edge_corners);
default_tag("remove") default_tag("remove")
@@ -2688,13 +2688,13 @@ module edge_profile_asym(
// mask2d_teardrop(r=10, angle=40); // mask2d_teardrop(r=10, angle=40);
// } // }
module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) {
check1 = assert($parent_geom != undef, "No object to attach to!"); check1 = assert($parent_geom != undef, "\nNo object to attach to!");
r = max(0.01, get_radius(r=r, d=d, dflt=undef)); r = max(0.01, get_radius(r=r, d=d, dflt=undef));
check2 = assert(is_num(r), "Bad r/d argument."); check2 = assert(is_num(r), "\nBad r/d argument.");
corners = _corners(corners, except=except); corners = _corners(corners, except=except);
vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]];
all_vecs_are_corners = all([for (vec = vecs) sum(v_abs(vec))==3]); all_vecs_are_corners = all([for (vec = vecs) sum(v_abs(vec))==3]);
check3 = assert(all_vecs_are_corners, "All vectors must be corners."); check3 = assert(all_vecs_are_corners, "\nAll vectors must be corners.");
for ($idx = idx(vecs)) { for ($idx = idx(vecs)) {
vec = vecs[$idx]; vec = vecs[$idx];
anch = _find_anchor(vec, $parent_geom); anch = _find_anchor(vec, $parent_geom);
@@ -3121,11 +3121,11 @@ module attachable(
expose_tags=false, keep_color=false expose_tags=false, keep_color=false
) { ) {
dummy1 = dummy1 =
assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates.") assert($children==2, "\nattachable() expects exactly two children: the shape to manage, and the union of all attachment candidates.")
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor)) assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor))
assert(is_undef(spin) || is_finite(spin), str("Invalid spin: ",spin)) assert(is_undef(spin) || is_finite(spin), str("\nInvalid spin: ",spin))
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient)); assert(is_undef(orient) || is_vector(orient,3), str("\nInvalid orient: ",orient));
assert(in_list(v_abs(axis),[UP,RIGHT,BACK]), "axis must be a coordinate direction"); assert(in_list(v_abs(axis),[UP,RIGHT,BACK]), "\n'axis' must be a coordinate direction.");
anchor = default(anchor,CENTER); anchor = default(anchor,CENTER);
spin = default(spin,0); spin = default(spin,0);
orient = is_def($anchor_override)? UP : default(orient, UP); orient = is_def($anchor_override)? UP : default(orient, UP);
@@ -3300,9 +3300,9 @@ function reorient(
geom, geom,
p=undef p=undef
) = ) =
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor)) assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("\nInvalid anchor: ",anchor))
assert(is_undef(spin) || is_finite(spin), str("Invalid spin: ",spin)) assert(is_undef(spin) || is_finite(spin), str("\nInvalid spin: ",spin))
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient)) assert(is_undef(orient) || is_vector(orient,3), str("\nInvalid orient: ",orient))
let( let(
anchor = default(anchor, CENTER), anchor = default(anchor, CENTER),
spin = default(spin, 0), spin = default(spin, 0),
@@ -3348,15 +3348,15 @@ function reorient(
// rot = A 4x4 rotations matrix, which may include a translation // rot = A 4x4 rotations matrix, which may include a translation
// flip = If true, flip the anchor the opposite direction. Default: false // flip = If true, flip the anchor the opposite direction. Default: false
function named_anchor(name, pos, orient, spin, rot, flip, info) = function named_anchor(name, pos, orient, spin, rot, flip, info) =
assert(num_defined([orient,spin])==0 || num_defined([rot,flip])==0, "Cannot mix orient or spin with rot or flip") assert(num_defined([orient,spin])==0 || num_defined([rot,flip])==0, "\nCannot mix orient or spin with rot or flip.")
assert(num_defined([pos,rot])>0, "Must give pos or rot") assert(num_defined([pos,rot])>0, "\nMust give pos or rot")
is_undef(rot) ? [name, pos, default(orient,UP), default(spin,0), if (info) info] is_undef(rot) ? [name, pos, default(orient,UP), default(spin,0), if (info) info]
: :
let( let(
flip = default(flip,false), flip = default(flip,false),
pos = default(pos,apply(rot,CTR)), pos = default(pos,apply(rot,CTR)),
rotpart = _force_rot(rot), rotpart = _force_rot(rot),
dummy = assert(approx(det4(rotpart),1), "Input rotation is not a rotation matrix"), dummy = assert(approx(det4(rotpart),1), "\nInput rotation is not a rotation matrix."),
dir = flip ? apply(rotpart,DOWN) dir = flip ? apply(rotpart,DOWN)
: apply(rotpart,UP), : apply(rotpart,UP),
rot = flip? affine3d_rot_by_axis(apply(rotpart,BACK),180)*rot rot = flip? affine3d_rot_by_axis(apply(rotpart,BACK),180)*rot
@@ -3540,11 +3540,11 @@ function attach_geom(
assert(is_region(region),2) assert(is_region(region),2)
let( l = default(l, h) ) let( l = default(l, h) )
two_d==true two_d==true
? assert(is_undef(l), "Cannot give l/h with region anchor types (when two_d is set)") ? assert(is_undef(l), "\nCannot give l/h with region anchor types (when two_d is set).")
extent==true extent==true
? ["rgn_extent", region, cp, offset, anchors] ? ["rgn_extent", region, cp, offset, anchors]
: ["rgn_isect", region, cp, offset, anchors] : ["rgn_isect", region, cp, offset, anchors]
: assert(is_finite(l), "Must give l/h with extrusion anchor types (did you forget to set two_d?)") : assert(is_finite(l), "\nMust give l/h with extrusion anchor types (did you forget to set two_d?).")
let( let(
shift = default(shift, [0,0]), shift = default(shift, [0,0]),
scale = is_num(scale)? [scale,scale] : default(scale, [1,1]), scale = is_num(scale)? [scale,scale] : default(scale, [1,1]),
@@ -3623,10 +3623,10 @@ function attach_geom(
// } // }
function define_part(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_string(name), "\n'name' must be a string.")
assert(_is_geometry(geom), "geometry appears invalid") assert(_is_geometry(geom), "\ngeometry appears invalid.")
assert(is_bool(inside), "inside must be boolean") assert(is_bool(inside), "\n'inside' must be boolean.")
assert(is_matrix(T,4), "T must be a 4x4 transformation matrix") assert(is_matrix(T,4), "\nT must be a 4×4 transformation matrix.")
[name, geom, inside, T]; [name, geom, inside, T];
@@ -3714,7 +3714,7 @@ function _attach_geom_size(geom) =
delt = mm[1]-mm[0] delt = mm[1]-mm[0]
) [delt.x, delt.y] ) [delt.x, delt.y]
) : ) :
assert(false, "Unknown attachment geometry type."); assert(false, "\nUnknown attachment geometry type.");
@@ -3727,7 +3727,7 @@ function _attach_geom_size(geom) =
/// Returns the path and post-transform matrix of the indicated edge. /// Returns the path and post-transform matrix of the indicated edge.
/// If the edge is invalid for the geometry, returns `undef`. /// If the edge is invalid for the geometry, returns `undef`.
function _attach_geom_edge_path(geom, edge) = function _attach_geom_edge_path(geom, edge) =
assert(is_vector(edge),str("Invalid edge: edge=",edge)) assert(is_vector(edge),str("\nInvalid edge: ",edge))
let( let(
type = geom[0], type = geom[0],
cp = _get_cp(geom), cp = _get_cp(geom),
@@ -3737,9 +3737,9 @@ function _attach_geom_edge_path(geom, edge) =
) )
type == "prismoid"? ( //size, size2, shift, axis type == "prismoid"? ( //size, size2, shift, axis
let(all_comps_good = [for (c=edge) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=edge) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an edge for a cuboid/prismoid must be -1, 0, or 1") assert(all_comps_good, "\nAll components of an edge for a cuboid/prismoid must be -1, 0, or 1.")
let(edge_good = len([for (c=edge) if(c) 1])==2) let(edge_good = len([for (c=edge) if(c) 1])==2)
assert(edge_good, "Invalid edge.") assert(edge_good, "\nInvalid edge.")
let( let(
size = geom[1], size = geom[1],
size2 = geom[2], size2 = geom[2],
@@ -3776,7 +3776,7 @@ function _attach_geom_edge_path(geom, edge) =
m = rot(from=UP,to=axis) * move(offset) m = rot(from=UP,to=axis) * move(offset)
) [path, [vecs], m] ) [path, [vecs], m]
) : type == "conoid"? ( //r1, r2, l, shift, axis ) : type == "conoid"? ( //r1, r2, l, shift, axis
assert(edge.z && edge.z == sign(edge.z), "The Z component of an edge for a cylinder/cone must be -1 or 1") assert(edge.z && edge.z == sign(edge.z), "\nThe Z component of an edge for a cylinder/cone must be -1 or 1.")
let( let(
rr1 = geom[1], rr1 = geom[1],
rr2 = geom[2], rr2 = geom[2],
@@ -3830,9 +3830,9 @@ function _attach_geom_edge_path(geom, edge) =
/// p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result. /// p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result.
function _attach_transform(anchor, spin, orient, geom, p) = function _attach_transform(anchor, spin, orient, geom, p) =
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor)) assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("\nInvalid anchor: ",anchor))
assert(is_undef(spin) || is_finite(spin), str("Invalid spin: ",spin)) assert(is_undef(spin) || is_finite(spin), str("\nInvalid spin: ",spin))
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient)) assert(is_undef(orient) || is_vector(orient,3), str("\nInvalid orient: ",orient))
let( let(
anchor=default(anchor,CENTER), anchor=default(anchor,CENTER),
spin=default(spin,0), spin=default(spin,0),
@@ -3888,7 +3888,7 @@ function _get_cp(geom) =
: in_list(geom[0],["extrusion_extent","extrusion_isect"]) ? "xpath" : in_list(geom[0],["extrusion_extent","extrusion_isect"]) ? "xpath"
: "other" : "other"
) )
assert(type!="other", "Invalid cp value") assert(type!="other", "\nInvalid cp value.")
cp=="centroid" ? ( cp=="centroid" ? (
type=="vnf" && (len(geom[1][0])==0 || len(geom[1][1])==0) ? [0,0,0] : type=="vnf" && (len(geom[1][0])==0 || len(geom[1][1])==0) ? [0,0,0] :
[each centroid(geom[1]), if (type=="xpath") 0] [each centroid(geom[1]), if (type=="xpath") 0]
@@ -3896,7 +3896,7 @@ function _get_cp(geom) =
: let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1]))) : let(points = type=="vnf"?geom[1][0]:flatten(force_region(geom[1])))
cp=="mean" ? [each mean(points), if (type=="xpath") 0] cp=="mean" ? [each mean(points), if (type=="xpath") 0]
: cp=="box" ?[each mean(pointlist_bounds(points)), if (type=="xpath") 0] : cp=="box" ?[each mean(pointlist_bounds(points)), if (type=="xpath") 0]
: assert(false,"Invalid cp specification"); : assert(false,"\nInvalid cp specification.");
function _get_cp(geom) = function _get_cp(geom) =
@@ -3913,7 +3913,7 @@ function _get_cp(geom) =
: let(points = is_vnf?geom[1][0]:flatten(force_region(geom[1]))) : let(points = is_vnf?geom[1][0]:flatten(force_region(geom[1])))
cp=="mean" ? mean(points) cp=="mean" ? mean(points)
: cp=="box" ? mean(pointlist_bounds(points)) : cp=="box" ? mean(pointlist_bounds(points))
: assert(false,"Invalid cp specification"); : assert(false,"\nInvalid cp specification.");
@@ -3949,7 +3949,7 @@ function _find_anchor(anchor, geom)=
anchors = last(geom), anchors = last(geom),
found = search([anchor], anchors, num_returns_per_match=1)[0] found = search([anchor], anchors, num_returns_per_match=1)[0]
) )
assert(found!=[], str("Unknown anchor: ",anchor)) assert(found!=[], str("\nUnknown anchor: ",anchor))
anchors[found] anchors[found]
) : ) :
let( let(
@@ -3958,7 +3958,7 @@ function _find_anchor(anchor, geom)=
offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering. offset = [for (i=[0:2]) anchor[i]==0? 0 : offset_raw[i]], // prevents bad centering.
type = geom[0] type = geom[0]
) )
assert(is_vector(anchor),str("Invalid anchor: anchor=",anchor)) assert(is_vector(anchor),str("\nInvalid anchor: ",anchor))
let( let(
anchor = point3d(anchor), anchor = point3d(anchor),
oang = ( oang = (
@@ -3968,7 +3968,7 @@ function _find_anchor(anchor, geom)=
) )
type == "prismoid"? ( //size, size2, shift, axis type == "prismoid"? ( //size, size2, shift, axis
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1") assert(all_comps_good, "\nAll components of an anchor for a cuboid/prismoid must be -1, 0, or 1.")
let( let(
size=geom[1], size=geom[1],
size2=geom[2], size2=geom[2],
@@ -4059,7 +4059,7 @@ function _find_anchor(anchor, geom)=
: axis==RIGHT ? "X" : axis==RIGHT ? "X"
: axis==BACK ? "Y" : axis==BACK ? "Y"
: "", : "",
dummy = assert(anch.z == sign(anch.z), str("The ",axisname," component of an anchor for the cylinder/cone must be -1, 0, or 1")), dummy = assert(anch.z == sign(anch.z), str("\nThe ",axisname," component of an anchor for the cylinder/cone must be -1, 0, or 1.")),
offset = rot(from=axis, to=UP, p=offset), offset = rot(from=axis, to=UP, p=offset),
u = (anch.z+1)/2, u = (anch.z+1)/2,
// Returns [point,tangent_dir] // Returns [point,tangent_dir]
@@ -4144,7 +4144,7 @@ function _find_anchor(anchor, geom)=
for(pt=ptlist) [anchor * (pt-cp), n, pt] for(pt=ptlist) [anchor * (pt-cp), n, pt]
] ]
) )
assert(len(hits)>0, "Anchor vector does not intersect with the shape. Attachment failed.") assert(len(hits)>0, "\nAnchor vector does not intersect with the shape. Attachment failed.")
let( let(
furthest = max_index(column(hits,0)), furthest = max_index(column(hits,0)),
dist = hits[furthest][0], dist = hits[furthest][0],
@@ -4169,8 +4169,8 @@ function _find_anchor(anchor, geom)=
let( let(
vnf=geom[1], vnf=geom[1],
override = geom[2](anchor) override = geom[2](anchor)
,fd=echo(cp=cp) //,fd=echo(cp=cp)
) // CENTER anchors anchor on cp, "origin" anchors on [0,0] ) // 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)] : 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] : vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
let( let(
@@ -4236,7 +4236,7 @@ function _find_anchor(anchor, geom)=
center = mean(plist) center = mean(plist)
) )
[center,anchor,basic_spin] [center,anchor,basic_spin]
: len(vlist)==0 ? assert(false,"Cannot find anchor on the VNF") : len(vlist)==0 ? assert(false,"\nCannot find anchor on the VNF.")
: let( : let(
vlist = flatten(vlist), vlist = flatten(vlist),
uind = unique_approx_indexed(select(vnf[0],vlist)), uind = unique_approx_indexed(select(vnf[0],vlist)),
@@ -4266,7 +4266,7 @@ function _find_anchor(anchor, geom)=
) [anchor, default(override[0],res[0]),default(override[1],res[1]),default(override[2],res[2]),if (len(res)==3) res[2]] ) [anchor, default(override[0],res[0]),default(override[1],res[1]),default(override[2],res[2]),if (len(res)==3) res[2]]
) : type == "trapezoid"? ( //size, size2, shift, override ) : type == "trapezoid"? ( //size, size2, shift, override
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1") assert(all_comps_good, "\nAll components of an anchor for a rectangle/trapezoid must be -1, 0, or 1.")
let( let(
anchor=_force_anchor_2d(anchor), anchor=_force_anchor_2d(anchor),
size=geom[1], size2=geom[2], shift=geom[3], size=geom[1], size2=geom[2], shift=geom[3],
@@ -4318,7 +4318,7 @@ function _find_anchor(anchor, geom)=
if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2] if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2]
] ]
) )
assert(len(isects)>0, "Anchor vector does not intersect with the shape. Attachment failed.") assert(len(isects)>0, "\nAnchor vector does not intersect with the shape. Attachment failed.")
let( let(
maxidx = max_index(column(isects,0)), maxidx = max_index(column(isects,0)),
isect = isects[maxidx], isect = isects[maxidx],
@@ -4347,7 +4347,7 @@ function _find_anchor(anchor, geom)=
[is_polygon_clockwise(path) ? -normal : normal, vector_angle(corner)] [is_polygon_clockwise(path) ? -normal : normal, vector_angle(corner)]
) [anchor, pos, dir[0], 0, if(len(dir)>1) [["corner_angle",dir[1]]]] ) [anchor, pos, dir[0], 0, if(len(dir)>1) [["corner_angle",dir[1]]]]
) : type=="extrusion_extent" || type=="extrusion_isect" ? ( // extruded region ) : type=="extrusion_extent" || type=="extrusion_isect" ? ( // extruded region
assert(in_list(anchor.z,[-1,0,1]), "The Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.") assert(in_list(anchor.z,[-1,0,1]), "\nThe Z component of an anchor for an extruded 2D shape must be -1, 0, or 1.")
let( let(
anchor_xy = point2d(anchor), anchor_xy = point2d(anchor),
rgn = geom[1], rgn = geom[1],
@@ -4379,7 +4379,7 @@ function _find_anchor(anchor, geom)=
) )
[anchor, pos, vec, oang] [anchor, pos, vec, oang]
) : ) :
assert(false, "Unknown attachment geometry type."); assert(false, "\nUnknown attachment geometry type.");
/// Internal Function: _is_shown() /// Internal Function: _is_shown()
@@ -4396,8 +4396,8 @@ function _is_shown() =
dummy=is_undef($tags) ? 0 : echo("Use tag() instead of $tags for specifying an object's tag."), dummy=is_undef($tags) ? 0 : echo("Use tag() instead of $tags for specifying an object's tag."),
$tag = default($tag,$tags) $tag = default($tag,$tags)
) )
assert(is_string($tag), str("Tag value (",$tag,") is not a string")) assert(is_string($tag), str("\nTag value (",$tag,") is not a string"))
assert(undef==str_find($tag," "),str("Tag string \"",$tag,"\" contains a space, which is not allowed")) assert(undef==str_find($tag," "),str("\nTag string \"",$tag,"\" contains a space, which is not allowed."))
let( let(
shown = $tags_shown=="ALL" || in_list($tag,$tags_shown), shown = $tags_shown=="ALL" || in_list($tag,$tags_shown),
hidden = in_list($tag, $tags_hidden) hidden = in_list($tag, $tags_hidden)
@@ -4822,7 +4822,7 @@ function _edge_set(v) =
let(valid_values = ["X", "Y", "Z", "ALL", "NONE"]) let(valid_values = ["X", "Y", "Z", "ALL", "NONE"])
assert( assert(
in_list(v, valid_values), in_list(v, valid_values),
str(v, " must be a vector, edge array, or one of ", valid_values) str("\n", v, " must be a vector, edge array, or one of ", valid_values, ".")
) v ) v
) : ) :
let(nonz = sum(v_abs(v))) let(nonz = sum(v_abs(v)))
@@ -4983,7 +4983,7 @@ function _corner_set(v) =
let(valid_values = ["ALL", "NONE"]) let(valid_values = ["ALL", "NONE"])
assert( assert(
in_list(v, valid_values), in_list(v, valid_values),
str(v, " must be a vector, corner array, or one of ", valid_values) str("\n", v, " must be a vector, corner array, or one of ", valid_values, ".")
) v ) v
) : ) :
all([for (i=[0:2]) !v[i] || (v[i]==v2[i])]) all([for (i=[0:2]) !v[i] || (v[i]==v2[i])])
@@ -5114,14 +5114,14 @@ function _force_rot(T) =
: 0]]; : 0]];
function _local_struct_val(struct, key)= function _local_struct_val(struct, key)=
assert(is_def(key),"key is missing") assert(is_def(key),"\nkey is missing.")
let(ind = search([key],struct)[0]) let(ind = search([key],struct)[0])
ind == [] ? undef : struct[ind][1]; ind == [] ? undef : struct[ind][1];
function _force_anchor_2d(anchor) = function _force_anchor_2d(anchor) =
is_undef(anchor) || len(anchor)==2 || is_string(anchor) ? anchor : is_undef(anchor) || len(anchor)==2 || is_string(anchor) ? anchor :
assert(anchor.y==0 || anchor.z==0, "Anchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.") assert(anchor.y==0 || anchor.z==0, "\nAnchor for a 2D shape cannot be fully 3D. It must have either Y or Z component equal to zero.")
anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor); anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor);
// Compute spin angle based on a anchor direction and desired spin direction // Compute spin angle based on a anchor direction and desired spin direction
@@ -5132,7 +5132,7 @@ function _compute_spin(anchor_dir, spin_dir) =
let( let(
native_dir = rot(from=UP, to=anchor_dir, p=BACK), native_dir = rot(from=UP, to=anchor_dir, p=BACK),
spin_dir = spin_dir - (spin_dir*anchor_dir)*anchor_dir, // component of spin_dir perpendicular to anchor_dir spin_dir = spin_dir - (spin_dir*anchor_dir)*anchor_dir, // component of spin_dir perpendicular to anchor_dir
dummy = assert(!approx(spin_dir,[0,0,0]),"spin direction is parallel to anchor"), dummy = assert(!approx(spin_dir,[0,0,0]),"\nSpin direction is parallel to anchor."),
angle = vector_angle(native_dir,spin_dir), angle = vector_angle(native_dir,spin_dir),
sign = cross(native_dir,spin_dir)*anchor_dir<0 ? -1 : 1 sign = cross(native_dir,spin_dir)*anchor_dir<0 ? -1 : 1
) )
@@ -5216,11 +5216,11 @@ function parent() =
// fillet=1); // fillet=1);
function parent_part(name) = function parent_part(name) =
assert(!is_undef($parent_parts), "Parent does not exist or does not have any parts") assert(!is_undef($parent_parts), "\nParent does not exist or does not have any parts.")
let( let(
ind = search([name], $parent_parts, 1,0)[0] ind = search([name], $parent_parts, 1,0)[0]
) )
assert(ind!=[], str("Parent does not have a part named ",name)) assert(ind!=[], str("\nParent does not have a part named \"",name,"\"."))
[$transform * $parent_parts[ind][3], $parent_parts[ind][1]]; [$transform * $parent_parts[ind][3], $parent_parts[ind][1]];
@@ -5251,7 +5251,7 @@ module restore(desc)
multmatrix(T) children(); multmatrix(T) children();
} }
else{ else{
check=assert(is_description(desc), "Invalid description"); check=assert(is_description(desc), "\nInvalid description.");
T = linear_solve($transform, desc[0]); T = linear_solve($transform, desc[0]);
$parent_geom = desc[1]; $parent_geom = desc[1];
multmatrix(T) children(); multmatrix(T) children();
@@ -5286,13 +5286,13 @@ module restore(desc)
// stroke([[0,0,0], desc_point(desc,anchor=TOP+FWD+RIGHT)],width=.5,color="red"); // stroke([[0,0,0], desc_point(desc,anchor=TOP+FWD+RIGHT)],width=.5,color="red");
function desc_point(desc, p, anchor) = function desc_point(desc, p, anchor) =
is_undef(desc) ? is_undef(desc) ?
assert(is_undef(anchor), "Cannot give anchor withot desc") assert(is_undef(anchor), "\nCannot give 'anchor' withot 'desc'.")
let( let(
T = matrix_inverse($transform) T = matrix_inverse($transform)
) )
apply(T, default(p,UP)) apply(T, default(p,UP))
: assert(is_description(desc), "Invalid description") : assert(is_description(desc), "\nInvalid description.")
assert(num_defined([anchor,p])<2, "Cannot give both anchor and p") assert(num_defined([anchor,p])<2, "\nCannot give both anchor and p")
let ( let (
T = linear_solve($transform, desc[0]), T = linear_solve($transform, desc[0]),
p = is_def(p) ? p p = is_def(p) ? p
@@ -5328,14 +5328,14 @@ function desc_point(desc, p, anchor) =
// position(TOP) cyl(d=2,h=15,orient=desc_dir(pris,anchor=FWD),anchor=LEFT); // position(TOP) cyl(d=2,h=15,orient=desc_dir(pris,anchor=FWD),anchor=LEFT);
function desc_dir(desc, dir, anchor) = function desc_dir(desc, dir, anchor) =
is_undef(desc) ? is_undef(desc) ?
assert(is_undef(anchor), "Cannot give anchor without desc") assert(is_undef(anchor), "\nCannot give 'anchor' without 'desc'.")
let( let(
T = matrix_inverse($transform) T = matrix_inverse($transform)
) )
move(-apply(T,CENTER), apply(T, default(dir,UP))) move(-apply(T,CENTER), apply(T, default(dir,UP)))
: :
assert(is_description(desc), "Invalid description") assert(is_description(desc), "Invalid description")
assert(num_defined([dir,anchor])<2, "Cannot give both dir and anchor") assert(num_defined([dir,anchor])<2, "\nCannot give both dir and anchor.")
let( let(
T = linear_solve($transform, desc[0]), T = linear_solve($transform, desc[0]),
dir = is_def(dir) ? dir dir = is_def(dir) ? dir
@@ -5347,7 +5347,7 @@ function desc_dir(desc, dir, anchor) =
move(-apply(T,CENTER),apply(T, dir)); move(-apply(T,CENTER),apply(T, dir));
function desc_attach(desc, anchor=UP, p, reverse=false) = function desc_attach(desc, anchor=UP, p, reverse=false) =
assert(is_description(desc), "Invalid description") assert(is_description(desc), "\nInvalid description.")
let( let(
T = linear_solve($transform, desc[0]), T = linear_solve($transform, desc[0]),
anch = _find_anchor(anchor,desc[1]), anch = _find_anchor(anchor,desc[1]),
@@ -5385,8 +5385,8 @@ function desc_attach(desc, anchor=UP, p, reverse=false) =
// } // }
function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)= function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)=
assert(is_description(desc1),"Invalid description: desc1") assert(is_description(desc1),str("\nInvalid description: desc1=",desc1))
assert(is_description(desc2),"Invalid description: desc2") assert(is_description(desc2),str("\nInvalid description: desc2=",desc2))
let( let(
anch1 = _find_anchor(anchor1, desc1[1]), anch1 = _find_anchor(anchor1, desc1[1]),
anch2 = _find_anchor(anchor2, desc2[1]), anch2 = _find_anchor(anchor2, desc2[1]),
@@ -5415,10 +5415,10 @@ function desc_dist(desc1,anchor1=CENTER, desc2, anchor2=CENTER)=
// desc = description to transform // desc = description to transform
function transform_desc(T,desc) = function transform_desc(T,desc) =
assert(is_description(desc), "Invalid description") assert(is_description(desc), "\nInvalid description.")
is_consistent(T, ident(4)) ? [for(t=T) [t*desc[0], desc[1]]] is_consistent(T, ident(4)) ? [for(t=T) [t*desc[0], desc[1]]]
: is_matrix(T,4,4) ? [T*desc[0], desc[1]] : is_matrix(T,4,4) ? [T*desc[0], desc[1]]
: assert(false,"T must be a 4x4 matrix or list of 4x4 matrices"); : assert(false,"\nT must be a 4×4 matrix or list of 4×4 matrices.");
// Module: desc_copies() // Module: desc_copies()

View File

@@ -659,7 +659,7 @@ function cuboid(
// prismoid(size1=[100,75], h=30, xang=50, yang=70); // prismoid(size1=[100,75], h=30, xang=50, yang=70);
// Example: Specifying top, height and angle, with asymmetric angles // Example: Specifying top, height and angle, with asymmetric angles
// prismoid(size2=[100,75], h=30, xang=[50,60], yang=[70,40]); // prismoid(size2=[100,75], h=30, xang=[50,60], yang=[70,40]);
// Example: Specifying top, bottom and angle for X and using that to define height. Note that giving yang here would likely give a conflicting height calculation, which is not allowed. // Example: Specifying top, bottom and angle for X and using that to define height. Giving yang here would likely give a conflicting height calculation, which is not allowed.
// prismoid(size1=[100,75], size2=[75,35], xang=50); // prismoid(size1=[100,75], size2=[75,35], xang=50);
// Example: The same as the previous example but we give a shift in Y. Note that shift.x must be undef because you cannot give combine an angle with a shift, so a shift.x value would conflict with xang being defined. // Example: The same as the previous example but we give a shift in Y. Note that shift.x must be undef because you cannot give combine an angle with a shift, so a shift.x value would conflict with xang being defined.
// prismoid(size1=[100,75], size2=[75,35], xang=50, shift=[undef,20]); // prismoid(size1=[100,75], size2=[75,35], xang=50, shift=[undef,20]);
@@ -1757,7 +1757,6 @@ function rect_tube(
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `FRONT+LEFT+BOTTOM` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `FRONT+LEFT+BOTTOM`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
//
// Named Anchors: // Named Anchors:
// "hypot" = Center of angled wedge face, perpendicular to that face. // "hypot" = Center of angled wedge face, perpendicular to that face.
// "hypot_left" = Left side of angled wedge face, bisecting the angle between the left side and angled faces. // "hypot_left" = Left side of angled wedge face, bisecting the angle between the left side and angled faces.
@@ -2092,7 +2091,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
// texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named textures are supported. // texture = A texture name string, or a rectangular array of scalar height values (0.0 to 1.0), or a VNF tile that defines the texture to apply to vertical surfaces. See {{texture()}} for what named textures are supported.
// tex_size = An optional 2D target size (2-vector or scalar) for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]` // tex_size = An optional 2D target size (2-vector or scalar) for the textures. Actual texture sizes will be scaled somewhat to evenly fit the available surface. Default: `[5,5]`
// tex_reps = If given instead of tex_size, a scalar or 2-vector giving the integer number of texture tile repetitions in the horizontal and vertical directions. // tex_reps = If given instead of tex_size, a scalar or 2-vector giving the integer number of texture tile repetitions in the horizontal and vertical directions.
// tex_inset = If numeric, lowers the texture into the surface by the specified proportion, e.g. 0.5 would lower it half way into the surface. If `true`, insets by exactly its full depth. Default: `false` // tex_inset = If numeric, lowers the texture into the surface by the specified proportion, e.g. 0.5 would lower it halfway into the surface. If `true`, insets by exactly its full depth. Default: `false`
// tex_rot = Rotate texture by specified angle, which must be a multiple of 90 degrees. Default: 0 // tex_rot = Rotate texture by specified angle, which must be a multiple of 90 degrees. Default: 0
// tex_depth = Specify texture depth; if negative, invert the texture. Default: 1. // tex_depth = Specify texture depth; if negative, invert the texture. Default: 1.
// tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8 // tex_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8
@@ -2101,20 +2100,16 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
// // Example: By radius
//
// Example: By Radius
// xdistribute(30) { // xdistribute(30) {
// cyl(l=40, r=10); // cyl(l=40, r=10);
// cyl(l=40, r1=10, r2=5); // cyl(l=40, r1=10, r2=5);
// } // }
// // Example: By diameter
// Example: By Diameter
// xdistribute(30) { // xdistribute(30) {
// cyl(l=40, d=25); // cyl(l=40, d=25);
// cyl(l=40, d1=25, d2=10); // cyl(l=40, d1=25, d2=10);
// } // }
//
// Example: Chamferring // Example: Chamferring
// xdistribute(60) { // xdistribute(60) {
// // Shown Left to right. // // Shown Left to right.
@@ -2122,16 +2117,12 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=false); // cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=false);
// cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=true); // cyl(l=40, d=40, chamfer=7, chamfang=30, from_end=true);
// } // }
//
// Example: Rounding // Example: Rounding
// cyl(l=40, d=40, rounding=10); // cyl(l=40, d=40, rounding=10);
//
// Example(VPD=175;VPR=[90,0,0]): Teardrop Bottom Rounding // Example(VPD=175;VPR=[90,0,0]): Teardrop Bottom Rounding
// cyl(l=40, d=40, rounding=10, teardrop=true); // cyl(l=40, d=40, rounding=10, teardrop=true);
//
// Example(VPD=175;VPR=[90,0,0]): Clipped Bottom Rounding // Example(VPD=175;VPR=[90,0,0]): Clipped Bottom Rounding
// cyl(l=40, d=40, rounding=10, clip_angle=40); // cyl(l=40, d=40, rounding=10, clip_angle=40);
//
// Example: Heterogenous Chamfers and Rounding // Example: Heterogenous Chamfers and Rounding
// ydistribute(80) { // ydistribute(80) {
// // Shown Front to Back. // // Shown Front to Back.
@@ -2139,63 +2130,50 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
// cyl(l=40, d=40, chamfer2=5, orient=UP); // cyl(l=40, d=40, chamfer2=5, orient=UP);
// cyl(l=40, d=40, chamfer1=12, rounding2=10, orient=UP); // cyl(l=40, d=40, chamfer1=12, rounding2=10, orient=UP);
// } // }
//
// Example: Putting it all together // Example: Putting it all together
// cyl( // cyl(
// l=20, d1=25, d2=15, // l=20, d1=25, d2=15,
// chamfer1=5, chamfang1=60, // chamfer1=5, chamfang1=60,
// from_end=true, rounding2=5 // from_end=true, rounding2=5
// ); // );
// // Example: External chamfers
// Example: External Chamfers
// cyl(l=50, r=30, chamfer=-5, chamfang=30, $fa=1, $fs=1); // cyl(l=50, r=30, chamfer=-5, chamfang=30, $fa=1, $fs=1);
//
// Example: External Roundings // Example: External Roundings
// cyl(l=50, r=30, rounding1=-5, rounding2=5, $fa=1, $fs=1); // cyl(l=50, r=30, rounding1=-5, rounding2=5, $fa=1, $fs=1);
//
// Example(Med): Standard Connectors // Example(Med): Standard Connectors
// xdistribute(40) { // xdistribute(40) {
// cyl(l=30, d=25) show_anchors(); // cyl(l=30, d=25) show_anchors();
// cyl(l=30, d1=25, d2=10) show_anchors(); // cyl(l=30, d1=25, d2=10) show_anchors();
// } // }
//
// Example: Texturing with heightfield diamonds // Example: Texturing with heightfield diamonds
// cyl(h=40, r=20, texture="diamonds", tex_size=[5,5]); // cyl(h=40, r=20, texture="diamonds", tex_size=[5,5]);
//
// Example: Texturing with heightfield pyramids // Example: Texturing with heightfield pyramids
// cyl(h=40, r1=20, r2=15, // cyl(h=40, r1=20, r2=15,
// texture="pyramids", tex_size=[5,5], // texture="pyramids", tex_size=[5,5],
// style="convex"); // style="convex");
//
// Example: Texturing with heightfield truncated pyramids // Example: Texturing with heightfield truncated pyramids
// cyl(h=40, r1=20, r2=15, chamfer=5, // cyl(h=40, r1=20, r2=15, chamfer=5,
// texture="trunc_pyramids", // texture="trunc_pyramids",
// tex_size=[5,5], style="convex"); // tex_size=[5,5], style="convex");
//
// Example: Texturing with VNF tile "dots" // Example: Texturing with VNF tile "dots"
// cyl(h=40, r1=20, r2=15, rounding=9, // cyl(h=40, r1=20, r2=15, rounding=9,
// texture="dots", tex_size=[5,5], // texture="dots", tex_size=[5,5],
// tex_samples=6); // tex_samples=6);
//
// Example: Texturing with VNF tile "bricks_vnf" // Example: Texturing with VNF tile "bricks_vnf"
// cyl(h=50, r1=25, r2=20, shift=[0,10], rounding1=-10, // cyl(h=50, r1=25, r2=20, shift=[0,10], rounding1=-10,
// texture="bricks_vnf", tex_size=[10,10], // texture="bricks_vnf", tex_size=[10,10],
// tex_depth=0.5, style="concave"); // tex_depth=0.5, style="concave");
//
// Example: No Texture Taper // Example: No Texture Taper
// cyl(d1=25, d2=20, h=30, rounding=5, // cyl(d1=25, d2=20, h=30, rounding=5,
// texture="trunc_ribs", tex_size=[5,1]); // texture="trunc_ribs", tex_size=[5,1]);
//
// Example: Taper Texure at Extreme Ends // Example: Taper Texure at Extreme Ends
// cyl(d1=25, d2=20, h=30, rounding=5, // cyl(d1=25, d2=20, h=30, rounding=5,
// texture="trunc_ribs", tex_taper=0, // texture="trunc_ribs", tex_taper=0,
// tex_size=[5,1]); // tex_size=[5,1]);
//
// Example: Taper Texture over First and Last 10% // Example: Taper Texture over First and Last 10%
// cyl(d1=25, d2=20, h=30, rounding=5, // cyl(d1=25, d2=20, h=30, rounding=5,
// texture="trunc_ribs", tex_taper=0.1, // texture="trunc_ribs", tex_taper=0.1,
// tex_size=[5,1]); // tex_size=[5,1]);
//
// Example(3D,Med,NoAxes): Making a Clay Pattern Roller // Example(3D,Med,NoAxes): Making a Clay Pattern Roller
// tex = [ // tex = [
// [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,], // [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,],
@@ -2347,7 +2325,7 @@ function cyl(
from_end, from_end1, from_end2, from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts, texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0, tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples, length, height, tex_scale, tex_depth, tex_samples,
tex_taper, style, tex_style, tex_taper, style, tex_style,
extra, extra1, extra2, extra, extra1, extra2,
anchor, spin=0, orient=UP anchor, spin=0, orient=UP
@@ -2455,6 +2433,7 @@ module cyl(
h, r, center, h, r, center,
l, r1, r2, l, r1, r2,
d, d1, d2, d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
@@ -2463,7 +2442,7 @@ module cyl(
from_end, from_end1, from_end2, from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts, texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0, tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples, length, height, tex_scale, tex_depth, tex_samples,
tex_taper, style, tex_style, tex_taper, style, tex_style,
extra, extra1, extra2, extra, extra1, extra2,
anchor, spin=0, orient=UP anchor, spin=0, orient=UP
@@ -2516,251 +2495,334 @@ module cyl(
// Module: xcyl() // Function&Module: xcyl()
// Synopsis: creates a cylinder oriented along the X axis. // Synopsis: Creates a cylinder oriented along the X axis.
// SynTags: Geom // SynTags: Geom, VNF
// Topics: Cylinders, Textures, Rounding, Chamfers // Topics: Cylinders, Textures, Rounding, Chamfers
// See Also: texture(), rotate_sweep(), cyl() // See Also: texture(), rotate_sweep(), cyl()
// Description:
// Creates an attachable cylinder with roundovers and chamfering oriented along the X axis.
//
// Usage: Typical // Usage: Typical
// xcyl(l|h|length|height, r|d=, [anchor=], ...) [ATTACHMENTS]; // xcyl(l|h|length|height, r|d=, [anchor=], ...) [ATTACHMENTS];
// xcyl(l|h|length|height, r1=|d1=, r2=|d2=, [anchor=], ...) [ATTACHMENTS]; // xcyl(l|h|length|height, r1=|d1=, r2=|d2=, [anchor=], ...) [ATTACHMENTS];
// // Description:
// Arguments: // Creates an attachable cylinder with roundovers, chamfering, and optional texture, oriented along the X axis.
// l / h / length / height = Length of cylinder along oriented axis. Default: 1 // .
// r = Radius of cylinder. Default: 1 // Used as a function, this is a shortcut for `cyl()` with `orient=RIGHT`, but otherwise using the same arguments excluding `orient`, which is not accepted.
// --- // Used as a module, the difference from cyl() is in the anchors, which don't get reoriented with the cylinder but maintain
// r1 = Optional radius of left (X-) end of cylinder. // the orientations you specify.
// r2 = Optional radius of right (X+) end of cylinder. // For example, top and right anchors on xcyl() are on the top curved surface and the right (positive x) end,
// d = Optional diameter of cylinder. (use instead of `r`) // respectively, whereas with cyl() these anchors are associated with the top end and right side.
// d1 = Optional diameter of left (X-) end of cylinder. // .
// d2 = Optional diameter of right (X+) end of cylinder. // See [cyl()] for more detailed usage and arguments.
// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false` // Example: By radius. The cone shows anchor arrows for `TOP` and `RIGHT`.
// chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
// chamfer1 = The size of the chamfer on the left end of the cylinder. Default: none.
// chamfer2 = The size of the chamfer on the right end of the cylinder. Default: none.
// chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
// chamfang1 = The angle in degrees of the chamfer on the left end of the cylinder.
// chamfang2 = The angle in degrees of the chamfer on the right end of the cylinder.
// from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge. Default: `false`.
// rounding = The radius of the rounding on the ends of the cylinder. Default: none.
// rounding1 = The radius of the rounding on the left end of the cylinder.
// rounding2 = The radius of the rounding on the right end of the cylinder.
// realign = If true, rotate the cylinder by half the angle of one face.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
//
// Example: By Radius
// ydistribute(50) { // ydistribute(50) {
// xcyl(l=35, r=10); // xcyl(l=35, r=10);
// xcyl(l=35, r1=15, r2=5); // xcyl(l=35, r1=15, r2=5) {
// attach(TOP) anchor_arrow();
// attach(RIGHT) anchor_arrow();
// }
// } // }
// // Example: By diameter
// Example: By Diameter
// ydistribute(50) { // ydistribute(50) {
// xcyl(l=35, d=20); // xcyl(l=35, d=20);
// xcyl(l=35, d1=30, d2=10); // xcyl(l=35, d1=30, d2=10);
// } // }
function xcyl( function xcyl(
h, r, d, r1, r2, d1, d2, l, h, r, center,
l, r1, r2,
d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false, length, height, circum=false, realign=false, shift=[0,0],
anchor=CENTER, spin=0, orient=UP teardrop=false, clip_angle,
) = no_function("xcyl"); from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples,
tex_taper, style, tex_style,
extra, extra1, extra2,
anchor, spin=0
) = let(
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),
l = one_defined([l,h,length,height],"l,h,length,height",1),
vnf=cyl(h=h, r=r, center=center,
l=l, r1=r1, r2=r2,
d=d, d1=d1, d2=d2,
length=length, height=height,
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2,
rounding=rounding, rounding1=rounding1, rounding2=rounding2,
circum=circum, realign=realign, shift=shift,
teardrop=teardrop, clip_angle=clip_angle,
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
texture=texture, tex_size=tex_size, tex_reps=tex_reps, tex_counts=tex_counts,
tex_inset=tex_inset, tex_rot=tex_rot,
tex_scale=tex_scale, tex_depth=tex_depth, tex_samples=tex_samples,
tex_taper=tex_taper, style=style, tex_style=tex_style,
extra=extra, extra1=extra1, extra2=extra2,
anchor=CENTER, spin=0, orient=RIGHT)
) reorient(anchor, spin, UP, p=vnf, r1=r1, r2=r2, l=l, axis=RIGHT);
module xcyl( module xcyl(
h, r, d, r1, r2, d1, d2, l, h, r, center,
l, r1, r2,
d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false, length, height, circum=false, realign=false, shift=[0,0],
anchor=CENTER, spin=0, orient=UP teardrop=false, clip_angle,
from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples,
tex_taper, style, tex_style,
extra, extra1, extra2,
anchor=CENTER, spin=0
) { ) {
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); 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); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1);
l = one_defined([l,h,length,height],"l,h,length,height",1); l = one_defined([l,h,length,height],"l,h,length,height",1);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=RIGHT) { attachable(anchor,spin,orient=UP, r1=r1, r2=r2, l=l, axis=RIGHT) {
cyl( cyl(
center=center,
l=l, r1=r1, r2=r2, l=l, r1=r1, r2=r2,
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2, chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2,
rounding=rounding, rounding1=rounding1, rounding2=rounding2, rounding=rounding, rounding1=rounding1, rounding2=rounding2,
circum=circum, realign=realign, from_end=from_end, circum=circum, realign=realign, shift=shift,
anchor=CENTER, orient=RIGHT teardrop=teardrop, clip_angle=clip_angle,
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
texture=texture, tex_size=tex_size, tex_reps=tex_reps, tex_counts=tex_counts,
tex_inset=tex_inset, tex_rot=tex_rot,
tex_scale=tex_scale, tex_depth=tex_depth, tex_samples=tex_samples,
tex_taper=tex_taper, style=style, tex_style=tex_style,
extra=extra, extra1=extra1, extra2=extra2,
anchor=CENTER, spin=0, orient=RIGHT
); );
children(); children();
} }
} }
// Module: ycyl() // Function&Module: ycyl()
// Synopsis: Creates a cylinder oriented along the y axis. // Synopsis: Creates a cylinder oriented along the y axis.
// SynTags: Geom // SynTags: Geom, VNF
// Topics: Cylinders, Textures, Rounding, Chamfers // Topics: Cylinders, Textures, Rounding, Chamfers
// See Also: texture(), rotate_sweep(), cyl() // See Also: texture(), rotate_sweep(), cyl()
// Description: // Description:
// Creates an attachable cylinder with roundovers and chamfering oriented along the y axis. // Creates an attachable cylinder with roundovers, chamfering, and optional texture, oriented along the Y axis.
// // .
// Usage: Typical // Used as a function, this is a shortcut for `cyl()` with `orient=BACK`, but otherwise using the same arguments excluding `orient`, which is not accepted.
// ycyl(l|h|length|height, r|d=, [anchor=], ...) [ATTACHMENTS]; // Used as a module, the difference from cyl() is in the anchors, which don't get reoriented with the cylinder but maintain
// ycyl(l|h|length|height, r1=|d1=, r2=|d2=, [anchor=], ...) [ATTACHMENTS]; // the orientations you specify.
// // For example, top and right anchors on ycyl() are on the top and right of the curved cylinder surface,
// Arguments: // respectively, whereas with cyl() these anchors are associated with the top end and right side.
// l / h / length / height = Length of cylinder along oriented axis. (Default: `1.0`) // .
// r = Radius of cylinder. // See [cyl()] for more detailed usage and arguments.
// --- // Example: By radius. The cone shows anchor arrows for `TOP` and `RIGHT`.
// r1 = Radius of front (Y-) end of cone.
// r2 = Radius of back (Y+) end of one.
// d = Diameter of cylinder.
// d1 = Diameter of front (Y-) end of one.
// d2 = Diameter of back (Y+) end of one.
// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false`
// chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
// chamfer1 = The size of the chamfer on the front end of the cylinder. Default: none.
// chamfer2 = The size of the chamfer on the back end of the cylinder. Default: none.
// chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
// chamfang1 = The angle in degrees of the chamfer on the front end of the cylinder.
// chamfang2 = The angle in degrees of the chamfer on the back end of the cylinder.
// from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge. Default: `false`.
// rounding = The radius of the rounding on the ends of the cylinder. Default: none.
// rounding1 = The radius of the rounding on the front end of the cylinder.
// rounding2 = The radius of the rounding on the back end of the cylinder.
// realign = If true, rotate the cylinder by half the angle of one face.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
//
// Example: By Radius
// xdistribute(50) { // xdistribute(50) {
// ycyl(l=35, r=10); // ycyl(l=35, r=10);
// ycyl(l=35, r1=15, r2=5); // ycyl(l=35, r1=15, r2=5) {
// attach(TOP) anchor_arrow();
// attach(RIGHT) anchor_arrow();
// }
// } // }
// // Example: By diameter
// Example: By Diameter
// xdistribute(50) { // xdistribute(50) {
// ycyl(l=35, d=20); // ycyl(l=35, d=20);
// ycyl(l=35, d1=30, d2=10); // ycyl(l=35, d1=30, d2=10);
// } // }
function ycyl( function ycyl(
h, r, d, r1, r2, d1, d2, l, h, r, center,
l, r1, r2,
d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false,height,length, circum=false, realign=false, shift=[0,0],
anchor=CENTER, spin=0, orient=UP teardrop=false, clip_angle,
) = no_function("ycyl"); from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples, length, height,
tex_taper, style, tex_style,
extra, extra1, extra2,
anchor, spin=0
) = let(
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),
l = one_defined([l,h,length,height],"l,h,length,height",1),
vnf=cyl(h=h, r=r, center=center,
l=l, r1=r1, r2=r2,
d=d, d1=d1, d2=d2,
length=length, height=height,
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2,
rounding=rounding, rounding1=rounding1, rounding2=rounding2,
circum=circum, realign=realign, shift=shift,
teardrop=teardrop, clip_angle=clip_angle,
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
texture=texture, tex_size=tex_size, tex_reps=tex_reps, tex_counts=tex_counts,
tex_inset=tex_inset, tex_rot=tex_rot,
tex_scale=tex_scale, tex_depth=tex_depth, tex_samples=tex_samples,
tex_taper=tex_taper, style=style, tex_style=tex_style,
extra=extra, extra1=extra1, extra2=extra2,
anchor=CENTER, spin=0, orient=BACK)
) reorient(anchor, spin, UP, p=vnf, r1=r1, r2=r2, l=l, axis=BACK);
module ycyl( module ycyl(
h, r, d, r1, r2, d1, d2, l, h, r, center,
l, r1, r2,
d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false,height,length, circum=false, realign=false, shift=[0,0],
anchor=CENTER, spin=0, orient=UP teardrop=false, clip_angle,
from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples,
tex_taper, style, tex_style,
extra, extra1, extra2,
anchor, spin=0
) { ) {
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); 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); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1);
l = one_defined([l,h,length,height],"l,h,length,height",1); l = one_defined([l,h,length,height],"l,h,length,height",1);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK) { attachable(anchor,spin,orient=UP, r1=r1, r2=r2, l=l, axis=BACK) {
cyl( cyl(
center=center,
l=l, r1=r1, r2=r2, l=l, r1=r1, r2=r2,
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2, chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2,
rounding=rounding, rounding1=rounding1, rounding2=rounding2, rounding=rounding, rounding1=rounding1, rounding2=rounding2,
circum=circum, realign=realign, from_end=from_end, circum=circum, realign=realign, shift=shift,
anchor=CENTER, orient=BACK teardrop=teardrop, clip_angle=clip_angle,
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
texture=texture, tex_size=tex_size, tex_reps=tex_reps, tex_counts=tex_counts,
tex_inset=tex_inset, tex_rot=tex_rot,
tex_scale=tex_scale, tex_depth=tex_depth, tex_samples=tex_samples,
tex_taper=tex_taper, style=style, tex_style=tex_style,
extra=extra, extra1=extra1, extra2=extra2,
anchor=CENTER, spin=0, orient=BACK
); );
children(); children();
} }
} }
// Module: zcyl() // Module: zcyl()
// Synopsis: Creates a cylinder oriented along the Z axis. // Synopsis: Creates a cylinder oriented along the y axis.
// SynTags: Geom // SynTags: Geom, VNF
// Topics: Cylinders, Textures, Rounding, Chamfers // Topics: Cylinders, Textures, Rounding, Chamfers
// See Also: texture(), rotate_sweep(), cyl() // See Also: texture(), rotate_sweep(), cyl()
// Description: // Description:
// Creates an attachable cylinder with roundovers and chamfering oriented along the Z axis. // Pass-through to [cyl()]. Creates an attachable cylinder with roundovers, chamfering, and optional texture, oriented along the Z axis.
// // .
// Usage: Typical // This is a shortcut for `cyl()` with `orient=UP` (which is also the default for [cyl()]), but otherwise using the same arguments excluding `orient`, which is not accepted. Unlike `xcyl()` and `ycyl()`, anchoring for `zcyl()` works the same as for `cyl()`.
// zcyl(l|h|length|height, r|d=, [anchor=],...) [ATTACHMENTS]; // .
// zcyl(l|h|length|height, r1=|d1=, r2=|d2=, [anchor=],...); // See [cyl()] for more detailed usage and arguments.
// // Example: By radius. The cone shows anchor arrows for `TOP` and `RIGHT`, which for `zcyl()` work the same as for `cyl()`.
// Arguments:
// l / h / length / height = Length of cylinder along oriented axis. (Default: 1.0)
// r = Radius of cylinder.
// ---
// r1 = Radius of front (Y-) end of cone.
// r2 = Radius of back (Y+) end of one.
// d = Diameter of cylinder.
// d1 = Diameter of front (Y-) end of one.
// d2 = Diameter of back (Y+) end of one.
// circum = If true, cylinder should circumscribe the circle of the given size. Otherwise inscribes. Default: `false`
// chamfer = The size of the chamfers on the ends of the cylinder. Default: none.
// chamfer1 = The size of the chamfer on the bottom end of the cylinder. Default: none.
// chamfer2 = The size of the chamfer on the top end of the cylinder. Default: none.
// chamfang = The angle in degrees of the chamfers on the ends of the cylinder.
// chamfang1 = The angle in degrees of the chamfer on the bottom end of the cylinder.
// chamfang2 = The angle in degrees of the chamfer on the top end of the cylinder.
// from_end = If true, chamfer is measured from the end of the cylinder, instead of inset from the edge. Default: `false`.
// rounding = The radius of the rounding on the ends of the cylinder. Default: none.
// rounding1 = The radius of the rounding on the bottom end of the cylinder.
// rounding2 = The radius of the rounding on the top end of the cylinder.
// realign = If true, rotate the cylinder by half the angle of one face.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP`
//
// Example: By Radius
// xdistribute(50) { // xdistribute(50) {
// zcyl(l=35, r=10); // zcyl(l=35, r=10);
// zcyl(l=35, r1=15, r2=5); // zcyl(l=35, r1=15, r2=5) {
// attach(TOP) anchor_arrow();
// attach(RIGHT) anchor_arrow();
// }
// } // }
// // Example: By diameter
// Example: By Diameter
// xdistribute(50) { // xdistribute(50) {
// zcyl(l=35, d=20); // zcyl(l=35, d=20);
// zcyl(l=35, d1=30, d2=10); // zcyl(l=35, d1=30, d2=10);
// } // }
function zcyl( function zcyl(
h, r, d, r1, r2, d1, d2, l, h, r, center,
l, r1, r2,
d, d1, d2,
length, height,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false, length, height, circum=false, realign=false, shift=[0,0],
anchor=CENTER, spin=0, orient=UP teardrop=false, clip_angle,
) = no_function("zcyl"); from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples, length, height,
tex_taper, style, tex_style,
extra, extra1, extra2,
anchor, spin=0
) = let(
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),
l = one_defined([l,h,length,height],"l,h,length,height",1),
vnf=cyl(h=h, r=r, center=center,
l=l, r1=r1, r2=r2,
d=d, d1=d1, d2=d2,
length=length, height=height,
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2,
rounding=rounding, rounding1=rounding1, rounding2=rounding2,
circum=circum, realign=realign, shift=shift,
teardrop=teardrop, clip_angle=clip_angle,
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
texture=texture, tex_size=tex_size, tex_reps=tex_reps, tex_counts=tex_counts,
tex_inset=tex_inset, tex_rot=tex_rot,
tex_scale=tex_scale, tex_depth=tex_depth, tex_samples=tex_samples,
tex_taper=tex_taper, style=style, tex_style=tex_style,
extra=extra, extra1=extra1, extra2=extra2,
anchor=CENTER, spin=0, orient=UP)
) reorient(anchor, spin, UP, p=vnf, r1=r1, r2=r2, l=l, axis=UP);
module zcyl( module zcyl(
h, r, d, r1, r2, d1, d2, l, h, r, center,
l, r1, r2,
d, d1, d2,
chamfer, chamfer1, chamfer2, chamfer, chamfer1, chamfer2,
chamfang, chamfang1, chamfang2, chamfang, chamfang1, chamfang2,
rounding, rounding1, rounding2, rounding, rounding1, rounding2,
circum=false, realign=false, from_end=false, length, height, circum=false, realign=false, shift=[0,0],
anchor=CENTER, spin=0, orient=UP teardrop=false, clip_angle,
from_end, from_end1, from_end2,
texture, tex_size=[5,5], tex_reps, tex_counts,
tex_inset=false, tex_rot=0,
tex_scale, tex_depth, tex_samples, length, height,
tex_taper, style, tex_style,
extra, extra1, extra2,
anchor, spin=0
) { ) {
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); 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); r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1);
l = one_defined([l,h,length,height],"l,h,length,height",1); l = one_defined([l,h,length,height],"l,h,length,height",1);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { attachable(anchor,spin,orient=UP, r1=r1, r2=r2, l=l) {
cyl( cyl(
center=center,
l=l, r1=r1, r2=r2, l=l, r1=r1, r2=r2,
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2, chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2,
rounding=rounding, rounding1=rounding1, rounding2=rounding2, rounding=rounding, rounding1=rounding1, rounding2=rounding2,
circum=circum, realign=realign, from_end=from_end, circum=circum, realign=realign, shift=shift,
anchor=CENTER teardrop=teardrop, clip_angle=clip_angle,
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
texture=texture, tex_size=tex_size, tex_reps=tex_reps, tex_counts=tex_counts,
tex_inset=tex_inset, tex_rot=tex_rot,
tex_scale=tex_scale, tex_depth=tex_depth, tex_samples=tex_samples,
tex_taper=tex_taper, style=style, tex_style=tex_style,
extra=extra, extra1=extra1, extra2=extra2,
anchor=CENTER, spin=0, orient=UP
); );
children(); children();
} }
@@ -3193,7 +3255,7 @@ function sphere(r, d, anchor=CENTER, spin=0, orient=UP) =
// With style="align", the circumscribed sphere has its maximum radius on the X and Y axes // With style="align", the circumscribed sphere has its maximum radius on the X and Y axes
// but is undersized on the Z axis. With style="octa" the circumscribed sphere has faces at each axis, so // but is undersized on the Z axis. With style="octa" the circumscribed sphere has faces at each axis, so
// the radius on the axes is equal to the specified radius, which is the *minimum* radius of the circumscribed sphere. // the radius on the axes is equal to the specified radius, which is the *minimum* radius of the circumscribed sphere.
// The same thing is true for style="icosa" when $fn is a multiple of 10. This would enable you to create spherical // The same thing is true for style="icosa" when $fn is a multiple of 10. This enables you to create spherical
// holes with guaranteed on-axis dimensions. // holes with guaranteed on-axis dimensions.
// Arguments: // Arguments:
// r = Radius of the spheroid. // r = Radius of the spheroid.
@@ -3825,7 +3887,7 @@ function teardrop(h, r, ang=45, cap_h, r1, r2, d, d1, d2, cap_h1, cap_h2, chamf
// //
// Named Anchors: // Named Anchors:
// "cap" = The center of the top of the cap, oriented with the cap face normal. // "cap" = The center of the top of the cap, oriented with the cap face normal.
// "tip" = The position where an un-capped onion would come to a point, oriented in the direction the point is from the center. // "tip" = The position where an un-capped onion comes to a point, oriented in the direction the point is from the center.
// //
// Example: Typical Shape // Example: Typical Shape
// onion(r=30, ang=30); // onion(r=30, ang=30);