mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 16:29:40 +00:00
Merge pull request #1469 from adrianVmariano/master
regular_prism(), prismoid and wedge anchors
This commit is contained in:
commit
eda0cd75b5
5 changed files with 589 additions and 115 deletions
260
attachments.scad
260
attachments.scad
|
@ -909,6 +909,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
||||||
dummy2=assert(align_list==[undef] || is_def(child), "Cannot use 'align' without 'child'")
|
dummy2=assert(align_list==[undef] || is_def(child), "Cannot use 'align' without 'child'")
|
||||||
assert(!inside || is_def(child), "Cannot use 'inside' without 'child'")
|
assert(!inside || is_def(child), "Cannot use 'inside' without 'child'")
|
||||||
assert(inset==0 || is_def(child), "Cannot specify 'inset' without 'child'")
|
assert(inset==0 || is_def(child), "Cannot specify 'inset' without 'child'")
|
||||||
|
assert(inset==0 || is_def(align), "Cannot specify 'inset' without 'align'")
|
||||||
assert(shiftout==0 || is_def(child), "Cannot specify 'shiftout' without 'child'");
|
assert(shiftout==0 || is_def(child), "Cannot specify 'shiftout' without 'child'");
|
||||||
factor = inside?-1:1;
|
factor = inside?-1:1;
|
||||||
$attach_to = child;
|
$attach_to = child;
|
||||||
|
@ -920,11 +921,13 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
||||||
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]);
|
||||||
|
$anchor=anchor;
|
||||||
anchor_data = _find_anchor(anchor, $parent_geom);
|
anchor_data = _find_anchor(anchor, $parent_geom);
|
||||||
anchor_pos = anchor_data[1];
|
anchor_pos = anchor_data[1];
|
||||||
anchor_dir = factor*anchor_data[2];
|
anchor_dir = factor*anchor_data[2];
|
||||||
anchor_spin = !inside || anchor==TOP || anchor==BOT ? anchor_data[3] : 180+anchor_data[3];
|
anchor_spin = two_d || !inside || anchor==TOP || anchor==BOT ? anchor_data[3]
|
||||||
$anchor=anchor;
|
: let(spin_dir = rot(anchor_data[3],from=UP, to=-anchor_dir, p=BACK))
|
||||||
|
_compute_spin(anchor_dir,spin_dir);
|
||||||
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), "align direction must be a 2-vector or 3-vector")
|
||||||
|
@ -934,34 +937,40 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
||||||
$align=align;
|
$align=align;
|
||||||
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("Invalid alignment: align value (",align,") includes component parallel to parent anchor (",anchor,")"));
|
||||||
|
// 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); ///
|
$attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all?
|
||||||
startdir = two_d || is_undef(align)? undef
|
startdir = two_d || is_undef(align)? undef
|
||||||
: anchor==UP || anchor==DOWN ? BACK
|
: anchor==UP || anchor==DOWN ? BACK
|
||||||
: UP - (anchor*UP)*anchor/(anchor*anchor);
|
: UP - (anchor*UP)*anchor/(anchor*anchor); // Component of UP perpendicular to anchor
|
||||||
enddir = is_undef(child) || child.z==0 ? UP : BACK;
|
enddir = is_undef(child) || child.z==0 ? UP : BACK;
|
||||||
|
// Compute adjustment to the child anchor for position purposes. This adjustment
|
||||||
|
// accounts for the change in the anchor needed to to alignment.
|
||||||
child_adjustment = is_undef(align)? CTR
|
child_adjustment = is_undef(align)? CTR
|
||||||
: two_d ? rot(to=child,from=-factor*anchor,p=align)
|
: two_d ? rot(to=child,from=-factor*anchor,p=align)
|
||||||
: apply( frame_map(x=child, z=enddir)
|
: apply( frame_map(x=child, z=enddir)
|
||||||
*frame_map(x=-factor*anchor, z=startdir, reverse=true)
|
*frame_map(x=-factor*anchor, z=startdir, reverse=true)
|
||||||
*rot(v=anchor,-spin), align);
|
*rot(v=anchor,-spin), align);
|
||||||
|
// The $anchor_override anchor value forces an override of the *position* only for the anchor
|
||||||
|
// used when attachable() places the child
|
||||||
$anchor_override = all_zero(child_adjustment)? inside?child:undef
|
$anchor_override = all_zero(child_adjustment)? inside?child:undef
|
||||||
: child+child_adjustment;
|
: child+child_adjustment;
|
||||||
reference = two_d? BACK : UP;
|
reference = two_d? BACK : UP;
|
||||||
|
// inset_dir is the direction for insetting when alignment is in effect
|
||||||
inset_dir = is_undef(align) ? CTR
|
inset_dir = is_undef(align) ? CTR
|
||||||
: two_d ? rot(to=reference, from=anchor,p=align)
|
: two_d ? rot(to=reference, from=anchor,p=align)
|
||||||
: apply(zrot(-factor*spin)*frame_map(x=reference, z=BACK)*frame_map(x=factor*anchor, z=startdir, reverse=true),
|
: apply(zrot(-factor*spin)*frame_map(x=reference, z=BACK)*frame_map(x=factor*anchor, z=startdir, reverse=true),
|
||||||
align);
|
align);
|
||||||
spinaxis = two_d? UP : anchor_dir;
|
spinaxis = two_d? UP : anchor_dir;
|
||||||
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference);
|
olap = - overlap * reference - inset*inset_dir + shiftout * (inset_dir + factor*reference);
|
||||||
if (norot || (approx(anchor_dir,reference) && anchor_spin==0)) {
|
if (norot || (approx(anchor_dir,reference) && anchor_spin==0))
|
||||||
translate(pos) rot(v=spinaxis,a=factor*spin) translate(olap) default_tag("remove",inside) children();
|
translate(pos) rot(v=spinaxis,a=factor*spin) translate(olap) default_tag("remove",inside) children();
|
||||||
} else {
|
else
|
||||||
translate(pos)
|
translate(pos)
|
||||||
rot(v=spinaxis,a=factor*spin)
|
rot(v=spinaxis,a=factor*spin)
|
||||||
rot(anchor_spin,from=reference,to=anchor_dir){
|
rot(anchor_spin,from=reference,to=anchor_dir)
|
||||||
translate(olap)
|
translate(olap)
|
||||||
default_tag("remove",inside) children();}}
|
default_tag("remove",inside) children();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2044,7 +2053,7 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
||||||
// Topics: Attachments, Masking
|
// Topics: Attachments, Masking
|
||||||
// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_profile(), edge_mask(), face_mask(), corner_mask()
|
// See Also: attachable(), position(), attach(), face_profile(), edge_profile(), corner_profile(), edge_mask(), face_mask(), corner_mask()
|
||||||
// Usage:
|
// Usage:
|
||||||
// PARENT() edge_profile([edges], [except=], [convexity=], [flip=], [corner_type=]) CHILDREN;
|
// PARENT() edge_profile([edges], [except], [convexity=], [flip=], [corner_type=]) CHILDREN;
|
||||||
// Description:
|
// Description:
|
||||||
// Takes an asymmetric 2D mask shape and attaches it to the selected edges and corners, with the appropriate
|
// Takes an asymmetric 2D mask shape and attaches it to the selected edges and corners, with the appropriate
|
||||||
// orientation and extruded length to be `diff()`ed away, to give the edges and corners a matching profile.
|
// orientation and extruded length to be `diff()`ed away, to give the edges and corners a matching profile.
|
||||||
|
@ -2064,6 +2073,7 @@ module edge_profile(edges=EDGES_ALL, except=[], excess=0.01, convexity=10) {
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges.
|
// edges = Edges to mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: All edges.
|
||||||
// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges.
|
// except = Edges to explicitly NOT mask. See [Specifying Edges](attachments.scad#subsection-specifying-edges). Default: No edges.
|
||||||
|
// ---
|
||||||
// excess = Excess length to extrude the profile to make edge masks. Default: 0.01
|
// excess = Excess length to extrude the profile to make edge masks. Default: 0.01
|
||||||
// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10
|
// convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10
|
||||||
// flip = If true, reverses the orientation of any external profile parts at each edge. Default false
|
// flip = If true, reverses the orientation of any external profile parts at each edge. Default false
|
||||||
|
@ -2818,9 +2828,9 @@ module attachable(
|
||||||
) {
|
) {
|
||||||
dummy1 =
|
dummy1 =
|
||||||
assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates.")
|
assert($children==2, "attachable() 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("Got: ",anchor))
|
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor))
|
||||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin))
|
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Invalid spin: ",spin))
|
||||||
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient));
|
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient));
|
||||||
anchor = first_defined([anchor, CENTER]);
|
anchor = first_defined([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);
|
||||||
|
@ -3031,12 +3041,6 @@ function named_anchor(name, pos, orient, spin, rot, flip) =
|
||||||
[name, pos, dir, spin];
|
[name, pos, dir, spin];
|
||||||
|
|
||||||
|
|
||||||
function _force_rot(T) =
|
|
||||||
[for(i=[0:3])
|
|
||||||
[for(j=[0:3]) j<3 ? T[i][j] :
|
|
||||||
i==3 ? 1
|
|
||||||
: 0]];
|
|
||||||
|
|
||||||
// Function: attach_geom()
|
// Function: attach_geom()
|
||||||
// Synopsis: Returns the internal geometry description of an attachable object.
|
// Synopsis: Returns the internal geometry description of an attachable object.
|
||||||
// Topics: Attachments
|
// Topics: Attachments
|
||||||
|
@ -3157,11 +3161,6 @@ function _force_rot(T) =
|
||||||
// geom = attach_geom(region=region, l=length, extent=false);
|
// geom = attach_geom(region=region, l=length, extent=false);
|
||||||
//
|
//
|
||||||
|
|
||||||
function _local_struct_val(struct, key)=
|
|
||||||
assert(is_def(key),"key is missing")
|
|
||||||
let(ind = search([key],struct)[0])
|
|
||||||
ind == [] ? undef : struct[ind][1];
|
|
||||||
|
|
||||||
|
|
||||||
function attach_geom(
|
function attach_geom(
|
||||||
size, size2,
|
size, size2,
|
||||||
|
@ -3181,12 +3180,12 @@ function attach_geom(
|
||||||
assert(is_list(anchors))
|
assert(is_list(anchors))
|
||||||
assert(is_bool(two_d))
|
assert(is_bool(two_d))
|
||||||
assert(is_vector(axis))
|
assert(is_vector(axis))
|
||||||
|
let(
|
||||||
|
over_f = is_undef(override) ? function(anchor) [undef,undef,undef]
|
||||||
|
: is_func(override) ? override
|
||||||
|
: function(anchor) _local_struct_val(override,anchor)
|
||||||
|
)
|
||||||
!is_undef(size)? (
|
!is_undef(size)? (
|
||||||
let(
|
|
||||||
over_f = is_undef(override) ? function(anchor) [undef,undef,undef]
|
|
||||||
: is_func(override) ? override
|
|
||||||
: function(anchor) _local_struct_val(override,anchor)
|
|
||||||
)
|
|
||||||
two_d? (
|
two_d? (
|
||||||
let(
|
let(
|
||||||
size2 = default(size2, size.x),
|
size2 = default(size2, size.x),
|
||||||
|
@ -3209,8 +3208,8 @@ function attach_geom(
|
||||||
) : !is_undef(vnf)? (
|
) : !is_undef(vnf)? (
|
||||||
assert(is_vnf(vnf))
|
assert(is_vnf(vnf))
|
||||||
assert(two_d == false)
|
assert(two_d == false)
|
||||||
extent? ["vnf_extent", vnf, cp, offset, anchors] :
|
extent? ["vnf_extent", vnf, over_f, cp, offset, anchors]
|
||||||
["vnf_isect", vnf, cp, offset, anchors]
|
: ["vnf_isect", vnf, over_f, cp, offset, anchors]
|
||||||
) : !is_undef(region)? (
|
) : !is_undef(region)? (
|
||||||
assert(is_region(region),2)
|
assert(is_region(region),2)
|
||||||
let( l = default(l, h) )
|
let( l = default(l, h) )
|
||||||
|
@ -3460,39 +3459,40 @@ 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("Got: ",anchor))
|
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Invalid anchor: ",anchor))
|
||||||
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin))
|
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Invalid spin: ",spin))
|
||||||
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient))
|
assert(is_undef(orient) || is_vector(orient,3), str("Invalid orient: ",orient))
|
||||||
let(
|
let(
|
||||||
anchor = default(anchor, CENTER),
|
anchor = default(anchor, CENTER),
|
||||||
spin = default(spin, 0),
|
spin = default(spin, 0),
|
||||||
orient = default(orient, UP),
|
orient = default(orient, UP),
|
||||||
two_d = _attach_geom_2d(geom),
|
two_d = _attach_geom_2d(geom),
|
||||||
m = ($attach_to != undef)? (
|
m = ($attach_to != undef) ? // $attach_to is the attachment point on this object
|
||||||
let(
|
( // which will attach to the parent
|
||||||
anch = _find_anchor($attach_to, geom),
|
let(
|
||||||
pos = is_undef($anchor_override) ? anch[1]
|
anch = _find_anchor($attach_to, geom),
|
||||||
: _find_anchor(_make_anchor_legal($anchor_override,geom),geom)[1]
|
// if $anchor_override is set it defines the object position anchor (but note not direction or spin).
|
||||||
)
|
// Otherwise we use the provided anchor for the object.
|
||||||
two_d?
|
pos = is_undef($anchor_override) ? anch[1]
|
||||||
assert(is_num(spin))
|
: _find_anchor(_make_anchor_legal($anchor_override,geom),geom)[1]
|
||||||
/*affine3d_zrot(spin) * */
|
)
|
||||||
rot(to=FWD, from=point3d(anch[2]))
|
two_d?
|
||||||
* affine3d_translate(point3d(-pos))
|
assert(is_num(spin))
|
||||||
:
|
affine3d_zrot(spin)
|
||||||
assert(is_num(spin) || is_vector(spin,3))
|
* rot(to=FWD, from=point3d(anch[2]))
|
||||||
let(
|
* affine3d_translate(point3d(-pos))
|
||||||
ang = vector_angle(anch[2], DOWN),
|
:
|
||||||
axis = vector_axis(anch[2], DOWN),
|
let(
|
||||||
ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3],
|
spinT = is_num(spin) ? affine3d_zrot(-anch[3]-spin)
|
||||||
axis2 = rot(p=axis,[0,0,ang2])
|
: affine3d_zrot(-spin.z) * affine3d_yrot(-spin.y) * affine3d_xrot(-spin.x)
|
||||||
)
|
* affine3d_zrot(-anch[3])
|
||||||
affine3d_rot_by_axis(axis2,ang)
|
)
|
||||||
* (is_num(spin)? affine3d_zrot(ang2+spin)
|
affine3d_yrot(180)
|
||||||
: affine3d_zrot(spin.z) * affine3d_yrot(spin.y) * affine3d_xrot(spin.x)
|
* spinT
|
||||||
* affine3d_zrot(ang2))
|
* rot(from=anch[2],to=UP)
|
||||||
* affine3d_translate(point3d(-pos))
|
* affine3d_translate(point3d(-pos))
|
||||||
) : (
|
)
|
||||||
|
:
|
||||||
let(
|
let(
|
||||||
anchor = is_undef($attach_alignment) ? anchor
|
anchor = is_undef($attach_alignment) ? anchor
|
||||||
: two_d? _make_anchor_legal(zrot(-spin,$attach_alignment),geom)
|
: two_d? _make_anchor_legal(zrot(-spin,$attach_alignment),geom)
|
||||||
|
@ -3503,16 +3503,14 @@ function _attach_transform(anchor, spin, orient, geom, p) =
|
||||||
assert(is_num(spin))
|
assert(is_num(spin))
|
||||||
affine3d_zrot(spin) * affine3d_translate(point3d(-pos))
|
affine3d_zrot(spin) * affine3d_translate(point3d(-pos))
|
||||||
:
|
:
|
||||||
assert(is_num(spin) || is_vector(spin,3))
|
|
||||||
let(
|
let(
|
||||||
axis = vector_axis(UP,orient),
|
axis = vector_axis(UP,orient), // Returns BACK if orient is UP
|
||||||
ang = vector_angle(UP,orient)
|
ang = vector_angle(UP,orient)
|
||||||
)
|
)
|
||||||
affine3d_rot_by_axis(axis,ang)
|
affine3d_rot_by_axis(axis,ang)
|
||||||
* ( is_num(spin)? affine3d_zrot(spin)
|
* ( is_num(spin)? affine3d_zrot(spin)
|
||||||
: affine3d_zrot(spin.z) * affine3d_yrot(spin.y) * affine3d_xrot(spin.x))
|
: affine3d_zrot(spin.z) * affine3d_yrot(spin.y) * affine3d_xrot(spin.x))
|
||||||
* affine3d_translate(point3d(-pos))
|
* affine3d_translate(point3d(-pos))
|
||||||
)
|
|
||||||
)
|
)
|
||||||
is_undef(p)? m
|
is_undef(p)? m
|
||||||
: is_vnf(p) && p==EMPTY_VNF? p
|
: is_vnf(p) && p==EMPTY_VNF? p
|
||||||
|
@ -3557,12 +3555,6 @@ function _get_cp(geom) =
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function _force_anchor_2d(anchor) =
|
|
||||||
is_undef(anchor) || len(anchor)==2 ? 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.")
|
|
||||||
anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor);
|
|
||||||
|
|
||||||
|
|
||||||
/// Internal Function: _find_anchor()
|
/// Internal Function: _find_anchor()
|
||||||
/// Usage:
|
/// Usage:
|
||||||
/// anchorinfo = _find_anchor(anchor, geom);
|
/// anchorinfo = _find_anchor(anchor, geom);
|
||||||
|
@ -3618,28 +3610,37 @@ function _find_anchor(anchor, geom) =
|
||||||
axy = point2d(anch),
|
axy = point2d(anch),
|
||||||
bot = point3d(v_mul(point2d(size )/2, axy), -h/2),
|
bot = point3d(v_mul(point2d(size )/2, axy), -h/2),
|
||||||
top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2),
|
top = point3d(v_mul(point2d(size2)/2, axy) + shift, h/2),
|
||||||
|
edge = top-bot,
|
||||||
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
||||||
vecs = anchor==CENTER? [UP]
|
// Find vectors of the faces involved in the anchor
|
||||||
: [
|
facevecs =
|
||||||
if (anch.x!=0) unit(rot(from=UP, to=[(top-bot).x,0,max(0.01,h)], p=[axy.x,0,0]), UP),
|
[
|
||||||
if (anch.y!=0) unit(rot(from=UP, to=[0,(top-bot).y,max(0.01,h)], p=[0,axy.y,0]), UP),
|
if (anch.x!=0) unit(rot(from=UP, to=[edge.x,0,max(0.01,h)], p=[axy.x,0,0]), UP),
|
||||||
|
if (anch.y!=0) unit(rot(from=UP, to=[0,edge.y,max(0.01,h)], p=[0,axy.y,0]), UP),
|
||||||
if (anch.z!=0) unit([0,0,anch.z],UP)
|
if (anch.z!=0) unit([0,0,anch.z],UP)
|
||||||
],
|
],
|
||||||
vec2 = anchor==CENTER? UP
|
dir = anchor==CENTER? UP
|
||||||
: len(vecs)==1? unit(vecs[0],UP)
|
: len(facevecs)==1? unit(facevecs[0],UP)
|
||||||
: len(vecs)==2? vector_bisect(vecs[0],vecs[1])
|
: len(facevecs)==2? vector_bisect(facevecs[0],facevecs[1])
|
||||||
: let(
|
: let(
|
||||||
v1 = vector_bisect(vecs[0],vecs[2]),
|
v1 = vector_bisect(facevecs[0],facevecs[2]),
|
||||||
v2 = vector_bisect(vecs[1],vecs[2]),
|
v2 = vector_bisect(facevecs[1],facevecs[2]),
|
||||||
p1 = plane_from_normal(yrot(90,p=v1)),
|
p1 = plane_from_normal(yrot(90,p=v1)),
|
||||||
p2 = plane_from_normal(xrot(-90,p=v2)),
|
p2 = plane_from_normal(xrot(-90,p=v2)),
|
||||||
line = plane_intersection(p1,p2),
|
line = plane_intersection(p1,p2),
|
||||||
v3 = unit(line[1]-line[0],UP) * anch.z
|
v3 = unit(line[1]-line[0],UP) * anch.z
|
||||||
)
|
)
|
||||||
unit(v3,UP),
|
unit(v3,UP),
|
||||||
vec = default(override[1],rot(from=UP, to=axis, p=vec2)),
|
final_dir = default(override[1],rot(from=UP, to=axis, p=dir)),
|
||||||
pos2 = default(override[0],rot(from=UP, to=axis, p=pos))
|
final_pos = default(override[0],rot(from=UP, to=axis, p=pos)),
|
||||||
) [anchor, pos2, vec, default(override[2],oang)]
|
// If the anchor is on a face or horizontal edge we take the oang value for spin
|
||||||
|
// If the anchor is on a vertical or sloped edge or corner we want to align the spin to point upward along the edge
|
||||||
|
// The "native" spin direction is the rotation of UP to the anchor direction
|
||||||
|
// The desired spin direction is the edge vector
|
||||||
|
// The axis of rotation is the direction vector, so we need component of edge perpendicular to dir
|
||||||
|
spin = anchor.x!=0 && anchor.y!=0 ? _compute_spin(dir, edge) //sign(anchor.x)*vector_angle(edge - (edge*dir)*dir/(dir*dir), rot(from=UP,to=dir,p=BACK))
|
||||||
|
: oang
|
||||||
|
) [anchor, final_pos, final_dir, default(override[2],spin)]
|
||||||
) : type == "conoid"? ( //r1, r2, l, shift
|
) : type == "conoid"? ( //r1, r2, l, shift
|
||||||
assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1")
|
assert(anchor.z == sign(anchor.z), "The Z component of an anchor for a cylinder/cone must be -1, 0, or 1")
|
||||||
let(
|
let(
|
||||||
|
@ -3678,8 +3679,11 @@ function _find_anchor(anchor, geom) =
|
||||||
vec = unit(v_mul(r,anchor),UP)
|
vec = unit(v_mul(r,anchor),UP)
|
||||||
) [anchor, pos, vec, oang]
|
) [anchor, pos, vec, oang]
|
||||||
) : type == "vnf_isect"? ( //vnf
|
) : type == "vnf_isect"? ( //vnf
|
||||||
let( vnf=geom[1] )
|
let(
|
||||||
approx(anchor,CTR)? [anchor, cp, UP, 0] : // CENTER anchors anchor on cp, "origin" anchors on [0,0]
|
vnf=geom[1],
|
||||||
|
override = geom[2](anchor)
|
||||||
|
) // CENTER anchors anchor on cp, "origin" anchors on [0,0]
|
||||||
|
approx(anchor,CTR)? [anchor, default(override[0],cp),default(override[1],UP),default(override[2], 0)] :
|
||||||
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] :
|
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] :
|
||||||
let(
|
let(
|
||||||
eps = 1/2048,
|
eps = 1/2048,
|
||||||
|
@ -3726,19 +3730,54 @@ function _find_anchor(anchor, geom) =
|
||||||
n = unit(sum(unorms)),
|
n = unit(sum(unorms)),
|
||||||
oang = approx(point2d(n), [0,0])? 0 : atan2(n.y, n.x) + 90
|
oang = approx(point2d(n), [0,0])? 0 : atan2(n.y, n.x) + 90
|
||||||
)
|
)
|
||||||
[anchor, pos, n, oang]
|
[anchor, default(override[0],pos),default(override[1], n),default(override[2], oang)]
|
||||||
) : type == "vnf_extent"? ( //vnf
|
) : type == "vnf_extent"? ( //vnf
|
||||||
let( vnf=geom[1] )
|
let(
|
||||||
approx(anchor,CTR)? [anchor, cp, UP, 0] : // CENTER anchors anchor on cp, "origin" anchors on [0,0]
|
vnf=geom[1],
|
||||||
|
override = geom[2](anchor)
|
||||||
|
) // CENTER anchors anchor on cp, "origin" anchors on [0,0]
|
||||||
|
approx(anchor,CTR)? [anchor, default(override[0],cp),default(override[1],UP),default(override[2], 0)] :
|
||||||
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
|
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
|
||||||
let(
|
let(
|
||||||
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]),
|
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]),
|
||||||
maxx = max(column(rpts,0)),
|
maxx = max(column(rpts,0)),
|
||||||
idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i],
|
idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i],
|
||||||
|
dir = len(idxs)>2 ? [anchor,oang]
|
||||||
|
: len(idxs)==2 ?
|
||||||
|
let(
|
||||||
|
edgefaces = _vnf_find_edge_faces(vnf,idxs),
|
||||||
|
edge = select(vnf[0],idxs)
|
||||||
|
)
|
||||||
|
len(edgefaces)==0 ? [anchor,oang]
|
||||||
|
: assert(len(edgefaces)==2, "Invalid polyhedron encountered while computing VNF anchor")
|
||||||
|
edge[0]==edge[1] ? [anchor,oang] // two "edge" points are the same, so give up
|
||||||
|
: let(
|
||||||
|
direction= unit(mean([for(face=edgefaces) polygon_normal(select(vnf[0],vnf[1][face]))])),
|
||||||
|
edgedir = edge[1]-edge[0],
|
||||||
|
nz = [for(i=[0:2]) if (!approx(edgedir[i],0)) i],
|
||||||
|
flip = edgedir[last(nz)] < 0 ? -1 : 1,
|
||||||
|
spin = _compute_spin(direction, flip*edgedir)
|
||||||
|
)
|
||||||
|
[direction,spin]
|
||||||
|
: let(
|
||||||
|
vertices = vnf[0],
|
||||||
|
faces = vnf[1],
|
||||||
|
cornerfaces = _vnf_find_corner_faces(vnf,idxs[0]), // faces = [3,9,12] indicating which faces
|
||||||
|
normals = [for(faceind=cornerfaces) polygon_normal(select(vnf[0], faces[faceind]))],
|
||||||
|
angles = [for(faceind=cornerfaces)
|
||||||
|
let(
|
||||||
|
thisface = faces[faceind],
|
||||||
|
vind = search(idxs[0],thisface)[0]
|
||||||
|
)
|
||||||
|
vector_angle(select(vertices, select(thisface,vind-1,vind+1)))
|
||||||
|
],
|
||||||
|
direc = unit(angles*normals)
|
||||||
|
)
|
||||||
|
[direc, atan2(direc.y,direc.x)+90],
|
||||||
avep = sum(select(rpts,idxs))/len(idxs),
|
avep = sum(select(rpts,idxs))/len(idxs),
|
||||||
mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep,
|
mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : avep,
|
||||||
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
|
pos = point3d(cp) + rot(from=RIGHT, to=anchor, p=mpt)
|
||||||
) [anchor, pos, anchor, oang]
|
) [anchor, default(override[0],pos),default(override[1],dir[0]),default(override[2],dir[1])]
|
||||||
) : 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, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1")
|
||||||
|
@ -4568,4 +4607,39 @@ module _show_cube_faces(faces, size=20, toplabel,botlabel) {
|
||||||
color("yellow",0.7) cuboid(size=size);
|
color("yellow",0.7) cuboid(size=size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// Internal utility function
|
||||||
|
|
||||||
|
function _force_rot(T) =
|
||||||
|
[for(i=[0:3])
|
||||||
|
[for(j=[0:3]) j<3 ? T[i][j] :
|
||||||
|
i==3 ? 1
|
||||||
|
: 0]];
|
||||||
|
|
||||||
|
function _local_struct_val(struct, key)=
|
||||||
|
assert(is_def(key),"key is missing")
|
||||||
|
let(ind = search([key],struct)[0])
|
||||||
|
ind == [] ? undef : struct[ind][1];
|
||||||
|
|
||||||
|
|
||||||
|
function _force_anchor_2d(anchor) =
|
||||||
|
is_undef(anchor) || len(anchor)==2 ? 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.")
|
||||||
|
anchor.y==0 ? [anchor.x,anchor.z] : point2d(anchor);
|
||||||
|
|
||||||
|
// Compute spin angle based on a anchor direction and desired spin direction
|
||||||
|
// anchor_dir assumed to be a unit vector; no assumption on spin_dir
|
||||||
|
// Takes the component of the spin direction perpendicular to the anchor
|
||||||
|
// direction and gives the spin angle that achieves it.
|
||||||
|
function _compute_spin(anchor_dir, spin_dir) =
|
||||||
|
let(
|
||||||
|
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
|
||||||
|
angle = vector_angle(native_dir,spin_dir),
|
||||||
|
sign = cross(native_dir,spin_dir)*anchor_dir<0 ? -1 : 1
|
||||||
|
)
|
||||||
|
sign*angle;
|
||||||
|
|
||||||
|
|
||||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
|
|
@ -437,7 +437,7 @@ function _partition_cutpath(l, h, cutsize, cutpath, gap) =
|
||||||
// Creates a mask that you can use to difference or intersect with an object to remove half of it,
|
// Creates a mask that you can use to difference or intersect with an object to remove half of it,
|
||||||
// leaving behind a side designed to allow assembly of the sub-parts.
|
// leaving behind a side designed to allow assembly of the sub-parts.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l = The length of the cut axis.
|
// l = The length of the cut axis.
|
||||||
// w = The width of the part to be masked, back from the cut plane.
|
// w = The width of the part to be masked, back from the cut plane.
|
||||||
// h = The height of the part to be masked.
|
// h = The height of the part to be masked.
|
||||||
// cutsize = The width of the cut pattern to be used.
|
// cutsize = The width of the cut pattern to be used.
|
||||||
|
@ -487,16 +487,15 @@ module partition_mask(l=100, w=100, h=100, cutsize=10, cutpath="jigsaw", gap=0,
|
||||||
// Topics: Partitions, Masking, Paths
|
// Topics: Partitions, Masking, Paths
|
||||||
// See Also: partition_mask(), partition()
|
// See Also: partition_mask(), partition()
|
||||||
// Usage:
|
// Usage:
|
||||||
// partition_cut_mask(l, w, h, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
|
// partition_cut_mask(l, [cutsize], [cutpath], [gap], [inverse], [$slop=], [anchor=], [spin=], [orient=]) [ATTACHMENTS];
|
||||||
// Description:
|
// Description:
|
||||||
// Creates a mask that you can use to difference with an object to cut it into two sub-parts that can be assembled.
|
// Creates a mask that you can use to difference with an object to cut it into two sub-parts that can be assembled.
|
||||||
// The `$slop` value is important to get the proper fit and should probably be smaller than 0.2. The examples below
|
// The `$slop` value is important to get the proper fit and should probably be smaller than 0.2. The examples below
|
||||||
// use larger values to make the mask easier to see.
|
// use larger values to make the mask easier to see.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l = The length of the cut axis.
|
// l = The length of the cut axis.
|
||||||
// w = The width of the part to be masked, back from the cut plane.
|
|
||||||
// h = The height of the part to be masked.
|
// h = The height of the part to be masked.
|
||||||
// cutsize = The width of the cut pattern to be used.
|
// cutsize = The width of the cut pattern to be used. Default: 10
|
||||||
// cutpath = The cutpath to use. Standard named paths are "flat", "sawtooth", "sinewave", "comb", "finger", "dovetail", "hammerhead", and "jigsaw". Alternatively, you can give a cutpath as a 2D path, where X is between 0 and 1, and Y is between -0.5 and 0.5. Default: "jigsaw"
|
// cutpath = The cutpath to use. Standard named paths are "flat", "sawtooth", "sinewave", "comb", "finger", "dovetail", "hammerhead", and "jigsaw". Alternatively, you can give a cutpath as a 2D path, where X is between 0 and 1, and Y is between -0.5 and 0.5. Default: "jigsaw"
|
||||||
// gap = Empty gaps between cutpath iterations. Default: 0
|
// gap = Empty gaps between cutpath iterations. Default: 0
|
||||||
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
|
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
|
||||||
|
|
|
@ -1406,7 +1406,7 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||||
// star = star(5, r=22, ir=13);
|
// star = star(5, r=22, ir=13);
|
||||||
// rounded_star = round_corners(star, cut=flatten(repeat([.5,0],5)), $fn=24);
|
// rounded_star = round_corners(star, cut=flatten(repeat([.5,0],5)), $fn=24);
|
||||||
// offset_sweep(rounded_star, height=20, bottom=os_teardrop(r=4), top=os_chamfer(width=4),$fn=64);
|
// offset_sweep(rounded_star, height=20, bottom=os_teardrop(r=4), top=os_chamfer(width=4),$fn=64);
|
||||||
// Example: We round a cube using the continous curvature rounding profile. But note that the corners are not smooth because the curved square collapses into a square with corners. When a collapse like this occurs, we cannot turn `check_valid` off. For a better result use `rounded_prism()` instead.
|
// Example: We round a cube using the continous curvature rounding profile. But note that the corners are not smooth because the curved square collapses into a square with corners. When a collapse like this occurs, we cannot turn `check_valid` off. For a better result use {{rounded_prism()}} instead.
|
||||||
// square = square(1);
|
// square = square(1);
|
||||||
// rsquare = round_corners(square, method="smooth", cut=0.1, k=0.7, $fn=36);
|
// rsquare = round_corners(square, method="smooth", cut=0.1, k=0.7, $fn=36);
|
||||||
// end_spec = os_smooth(cut=0.1, k=0.7, steps=22);
|
// end_spec = os_smooth(cut=0.1, k=0.7, steps=22);
|
||||||
|
@ -2619,8 +2619,8 @@ Access to the derivative smoothing parameter?
|
||||||
// When joining between planes this function produces similar results to {{rounded_prism()}}. This function works best when the prism
|
// When joining between planes this function produces similar results to {{rounded_prism()}}. This function works best when the prism
|
||||||
// cross section is a continuous shape with a high sampling rate and without sharp corners. If you have sharp corners you should consider
|
// cross section is a continuous shape with a high sampling rate and without sharp corners. If you have sharp corners you should consider
|
||||||
// giving them a small rounding first. When the prism cross section has concavities the fillet size will be limited by the curvature of those concavities.
|
// giving them a small rounding first. When the prism cross section has concavities the fillet size will be limited by the curvature of those concavities.
|
||||||
// In contrast, {{rounded_prism()}} works best on a prism that has fewer points. A high sampling rate can lead to problems, and rounding
|
// In contrast, {{rounded_prism()}} works best on a prism that has fewer points and does well with sharp corners, but may encounter problems
|
||||||
// over sharp corners leads to poor results.
|
// with a high sampling rate.
|
||||||
// .
|
// .
|
||||||
// You specify the prism by giving its cross section as a 2D path. The cross section will always be the orthogonal cross
|
// You specify the prism by giving its cross section as a 2D path. The cross section will always be the orthogonal cross
|
||||||
// section of the prism. Depending on end conditions, the ends may not be perpendicular to the
|
// section of the prism. Depending on end conditions, the ends may not be perpendicular to the
|
||||||
|
@ -2707,7 +2707,7 @@ Access to the derivative smoothing parameter?
|
||||||
// For joins to convex objects you can choose a small value, but when joining to a concave object the overlap may need to be
|
// For joins to convex objects you can choose a small value, but when joining to a concave object the overlap may need to be
|
||||||
// very large to ensure that the base of the joiner prism is well-behaved. In such cases you may need to use an intersection
|
// very large to ensure that the base of the joiner prism is well-behaved. In such cases you may need to use an intersection
|
||||||
// remove excess base.
|
// remove excess base.
|
||||||
// Figure(2D,Med,NoAxes): Uniform fillet method. This image shows how the fillet we construct a uniform fillet. The pictures shows the cross section that is perpendicular to the prism. The blue curve represents the base object surface. The vertical line is the side of the prism. To construct a fillet we travel along the surface of the base, following the curve, until we have moved the fillet length, `a`. This defines the point `u`. We then construct a tangent line to the base and find its intersection, `v`, with the prism. Note that if the base is steeply curved, this tangent may fail to intersect, and the algorithm will fail with an error because `v` does not exist. Finally we locate `w` to be distance `a` above the point where the prism intersects the base object. The fillet is defined by the `[u,v,w]` triple and is shown in red. Note that with this method, the fillet is always height `a` above the base, so it makes a uniform curve parallel to the base object. However, when the base curvature is more extreme, point `v` may end up above point `w`, resulting in an invalid configuration. It also happens that point `v`, while below `w`, is very close to `w`, so the resulting fillet has an abrupt angle near `w` instead of a smooth transition.
|
// Figure(2D,Med,NoAxes): Uniform fillet method. This image shows how we construct a uniform fillet. The pictures shows the cross section that is perpendicular to the prism. The blue curve represents the base object surface. The vertical line is the side of the prism. To construct a fillet we travel along the surface of the base, following the curve, until we have moved the fillet length, `a`. This defines the point `u`. We then construct a tangent line to the base and find its intersection, `v`, with the prism. Note that if the base is steeply curved, this tangent may fail to intersect, and the algorithm will fail with an error because `v` does not exist. Finally we locate `w` to be distance `a` above the point where the prism intersects the base object. The fillet is defined by the `[u,v,w]` triple and is shown in red. Note that with this method, the fillet is always height `a` above the base, so it makes a uniform curve parallel to the base object. However, when the base curvature is more extreme, point `v` may end up above point `w`, resulting in an invalid configuration. It also happens that point `v`, while below `w`, is very close to `w`, so the resulting fillet has an abrupt angle near `w` instead of a smooth transition.
|
||||||
// R=60;
|
// R=60;
|
||||||
// base = R*[cos(70),sin(70)];
|
// base = R*[cos(70),sin(70)];
|
||||||
// end = R*[cos(45),sin(45)];
|
// end = R*[cos(45),sin(45)];
|
||||||
|
|
403
shapes3d.scad
403
shapes3d.scad
|
@ -590,13 +590,17 @@ function cuboid(
|
||||||
// Creates a rectangular prismoid shape with optional roundovers and chamfering.
|
// Creates a rectangular prismoid shape with optional roundovers and chamfering.
|
||||||
// You can only round or chamfer the vertical(ish) edges. For those edges, you can
|
// You can only round or chamfer the vertical(ish) edges. For those edges, you can
|
||||||
// specify rounding and/or chamferring per-edge, and for top and bottom separately.
|
// specify rounding and/or chamferring per-edge, and for top and bottom separately.
|
||||||
// If you want to round the bottom or top edges see {{rounded_prism()}}.
|
// If you want to round the bottom or top edges see {{rounded_prism()}} or {{edge_profile()}}
|
||||||
// .
|
// .
|
||||||
// Specification of the prismoid is similar to specification for {{trapezoid()}}. You can specify the dimensions of the
|
// Specification of the prismoid is similar to specification for {{trapezoid()}}. You can specify the dimensions of the
|
||||||
// bottom and top and its height to get a symmetric prismoid. You can use the shift argument to shift the top face around.
|
// bottom and top and its height to get a symmetric prismoid. You can use the shift argument to shift the top face around.
|
||||||
// You can also specify base angles either in the X direction, Y direction or both. In order to avoid overspecification,
|
// You can also specify base angles either in the X direction, Y direction or both. In order to avoid overspecification,
|
||||||
// you may need to specify a parameter such as size2 as a list of two values, one of which is undef. For example,
|
// you may need to specify a parameter such as size2 as a list of two values, one of which is undef. For example,
|
||||||
// specifying `size2=[100,undef]` sets the size in the X direction but allows the size in the Y direction to be computed based on yang.
|
// specifying `size2=[100,undef]` sets the size in the X direction but allows the size in the Y direction to be computed based on yang.
|
||||||
|
// .
|
||||||
|
// The anchors on the top and bottom faces have spin pointing back. The anchors on the side faces have spin point UP.
|
||||||
|
// The anchors on the top and bottom edges also have anchors that point up. The anchors on the side edges and the corners
|
||||||
|
// have spin with positive Z component, pointing along the edge where the anchor is located.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// size1 = [width, length] of the bottom end of the prism.
|
// size1 = [width, length] of the bottom end of the prism.
|
||||||
// size2 = [width, length] of the top end of the prism.
|
// size2 = [width, length] of the top end of the prism.
|
||||||
|
@ -827,6 +831,362 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) =
|
||||||
]
|
]
|
||||||
) reorient(anchor,spin,orient, vnf=vnf, extent=true, p=vnf);
|
) reorient(anchor,spin,orient, vnf=vnf, extent=true, p=vnf);
|
||||||
|
|
||||||
|
// Function&Module: regular_prism()
|
||||||
|
// Synopsis: Creates a regular prism with roundovers and chamfering
|
||||||
|
// SynTags: Geom, VNF
|
||||||
|
// Topics: Textures, Rounding, Chamfers
|
||||||
|
// See Also: cyl(), rounded_prism(), texture(), linear_sweep()
|
||||||
|
// Usage: Normal prisms
|
||||||
|
// regular_prism(n, h|l=|height=|length=, r, [center=], [realign=]) [ATTACHMENTS];
|
||||||
|
// regular_prism(n, h|l=|height=|length=, d=|id=|od=|ir=|or=|side=, ...) [ATTACHMENTS];
|
||||||
|
// regular_prism(n, h|l=|height=|length=, r1=|d1=|id1=|od1=|ir1=|or1=|side1=,r2=|d2=|id2=|od2=|ir2=|or2=|side2=, ...) [ATTACHMENTS];
|
||||||
|
// Usage: Chamferred end prisms
|
||||||
|
// regular_prism(n, h, r, chamfer=, [chamfang=], [from_end=], ...);
|
||||||
|
// regular_prism(n, h, r, chamfer1=, [chamfang1=], [from_end=], ...);
|
||||||
|
// regular_prism(n, h, r, chamfer2=, [chamfang2=], [from_end=], ...);
|
||||||
|
// regular_prism(n, h, r, chamfer1=, chamfer2=, [chamfang1=], [chamfang2=], [from_end=], ...);
|
||||||
|
// Usage: Rounded end prisms
|
||||||
|
// regular_prism(n, h, r, rounding=, ...);
|
||||||
|
// regular_prism(n, h, r, rounding1=, ...);
|
||||||
|
// regular_prism(n, h, r, rounding2=, ...);
|
||||||
|
// regular_prism(n, h, r, rounding1=, rounding2=, ...);
|
||||||
|
// Usage: Textured prisms
|
||||||
|
// regular_prism(n, h, r, texture=, [tex_size=]|[tex_reps=], [tex_depth=], [tex_rot=], [tex_samples=], [style=], [tex_inset=], ...);
|
||||||
|
// Usage: Called as a function to get a VNF
|
||||||
|
// vnf = rounded_prism(...);
|
||||||
|
// Description:
|
||||||
|
// Creates a prism whose ends are similar `n`-sided regular polygons, with optional rounding, chamfers or textures.
|
||||||
|
// You can specify the size of the ends using diameter or radius measured either inside or outside. Alternatively
|
||||||
|
// you can give the length of the side of the polygon. You can specify chamfers and roundings for the ends, but not
|
||||||
|
// the vertical edges. See {{rounded_prism()}} for prisms with rounded vertical edges. You can also specify texture for the side
|
||||||
|
// faces, but note that texture is not compatible with any roundings or chamfers.
|
||||||
|
// .
|
||||||
|
// Anchors are based on the VNF of the prism. Especially for tapered or shifted prisms, this may give unexpected anchor positions, such as top side anchors
|
||||||
|
// being located at the bottom of the shape, so confirm anchor positions before use.
|
||||||
|
// Additional face and edge anchors are located on the side faces and vertical edges of the prism.
|
||||||
|
// When you use `shift`, which moves the top face of the prism, the spin for the side face and edges anchors will align the child with the edge or face direction.
|
||||||
|
// Named anchors located along the top and bottom edges and corners are pointed in the direction of the associated face or edge to enable positioning
|
||||||
|
// in the direction of the side faces but positioned at the top/bottom, since {{align()}} cannot be used for this task. These edge and corners anchors do
|
||||||
|
// not split the edge/corner angle like the standard anchors.
|
||||||
|
// .
|
||||||
|
// This module is very similar to {{cyl()}}. It differs in the following ways: you can specify side length or inner radius/diameter, you can apply roundings with
|
||||||
|
// different `$fn` than the number of prism faces, you can apply texture to the flat faces without forcing a high facet count,
|
||||||
|
// anchors are located on the true object instead of the ideal cylinder and you can anchor to the edges and faces.
|
||||||
|
// Named Anchors:
|
||||||
|
// "edge0", "edge1", etc. = Center of each side edge, spin pointing up along the edge
|
||||||
|
// "face0", "face1", etc. = Center of each side face, spin pointing up
|
||||||
|
// "topedge0", "topedge1", etc = Center of each top edge, pointing in direction of associated side face, spin up
|
||||||
|
// "botedge0", "botedge1", etc = Center of each bottom edge, pointing in direction of associated side face, spin up
|
||||||
|
// "topcorner0", "topcorner1", etc = Top corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
|
// "botcorner0", "botcorner1", etc = Bottom corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
|
// Arguments:
|
||||||
|
// l / h / length / height = Length of prism
|
||||||
|
// r = Outer radius of prism.
|
||||||
|
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=DOWN`.
|
||||||
|
// ---
|
||||||
|
// r1/or1 = Outer radius of the bottom of prism
|
||||||
|
// r2/or2 = Outer radius of the top end of prism
|
||||||
|
// d = Outer Diameter of prism
|
||||||
|
// d1 / od1 = Outer diameter of bottom of prism
|
||||||
|
// d2 / od2 = Outer diameter of top end of prism
|
||||||
|
// ir = Inner radius of prism
|
||||||
|
// ir1 = Inner radius of bottom of prism
|
||||||
|
// ir2 = Inner radius of top of prism
|
||||||
|
// id = Inner diameter of prism
|
||||||
|
// id1 = Inner diameter of bottom of prism
|
||||||
|
// id2 = Inner diameter of top of prism
|
||||||
|
// side = Side length of prism faces
|
||||||
|
// side1 = Side length of prism faces at the bottom
|
||||||
|
// side2 = Side length of prism faces at the top
|
||||||
|
// shift = [X,Y] amount to shift the center of the top end with respect to the center of the bottom end.
|
||||||
|
// chamfer = The size of the chamfers on the ends of the prism. (Also see: `from_end=`) Default: none.
|
||||||
|
// chamfer1 = The size of the chamfer on the bottom end of the prism. (Also see: `from_end1=`) Default: none.
|
||||||
|
// chamfer2 = The size of the chamfer on the top end of the prism. (Also see: `from_end2=`) Default: none.
|
||||||
|
// chamfang = The angle in degrees of the chamfers away from the ends of the prismr. Default: Chamfer angle is halfway between the endcap and side face.
|
||||||
|
// chamfang1 = The angle in degrees of the bottom chamfer away from the bottom end of the prism. Default: Chamfer angle is halfway between the endcap and side face.
|
||||||
|
// chamfang2 = The angle in degrees of the top chamfer away from the top end of the prism. Default: Chamfer angle is halfway between the endcap and side face.
|
||||||
|
// from_end = If true, chamfer is measured along the side face from the ends of the prism, instead of inset from the edge. Default: `false`.
|
||||||
|
// from_end1 = If true, chamfer on the bottom end of the prism is measured along the side face from the end of the prism, instead of inset from the edge. Default: `false`.
|
||||||
|
// from_end2 = If true, chamfer on the top end of the prism is measured along the side face from the end of the prism, instead of inset from the edge. Default: `false`.
|
||||||
|
// rounding = The radius of the rounding on the ends of the prism. Default: none.
|
||||||
|
// rounding1 = The radius of the rounding on the bottom end of the prism.
|
||||||
|
// rounding2 = The radius of the rounding on the top end of the prism.
|
||||||
|
// realign = If true, rotate the prism by half the angle of one face so that a face points in the X+ direction. Default: false
|
||||||
|
// teardrop = If given as a number, rounding around the bottom edge of the prism won't exceed this many degrees from vertical. If true, the limit angle is 45 degrees. Default: `false`
|
||||||
|
// 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 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 2-vector giving the 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_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_samples = Minimum number of "bend points" to have in VNF texture tiles. Default: 8
|
||||||
|
// style = {{vnf_vertex_array()}} style used to triangulate heightfield textures. Default: "min_edge"
|
||||||
|
// 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: Simple prism
|
||||||
|
// regular_prism(5,r=10,h=25);
|
||||||
|
// Example: With end rounding
|
||||||
|
// regular_prism(5,r=10,h=25,rounding=3,$fn=32);
|
||||||
|
// Example: By side length at bottom, inner radius at top, shallow chamfer
|
||||||
|
// regular_prism(7, side1=10, ir2=7, height=20,chamfer2=2,chamfang2=20);
|
||||||
|
// Example: With shift
|
||||||
|
// regular_prism(4, d=12, h=10, shift=[12,7]);
|
||||||
|
// Example: Attaching child to face
|
||||||
|
// regular_prism(5, d1=15, d2=10, h=20)
|
||||||
|
// recolor("lightblue")
|
||||||
|
// attach("face1",BOT) regular_prism(n=4,r1=3,r2=1,h=3);
|
||||||
|
// Example: Attaching child to edge
|
||||||
|
// regular_prism(5, d1=15, d2=10, h=20)
|
||||||
|
// recolor("lightblue")
|
||||||
|
// attach("edge2",RIGHT) cuboid([4,4,20]);
|
||||||
|
// Example: Placing child on top along an edge of a regular prism is possible with the top_edge anchors, but you cannot use {{align()}} or {{attach()}}, so you must manually anchor and spin the child by half of the polygon angle (180/n) to get to face0 and then 360/n more for each subsequent face. If you set `realign=true` then you don't need the initial angle for face0.
|
||||||
|
// regular_prism(5, d1=25, d2=20, h=15, realign=false) color("lightblue"){
|
||||||
|
// position("top_edge1") prismoid([5,5],[2,2],h=3,spin=-360/5*1.5,anchor=RIGHT+BOT);
|
||||||
|
// position("top_edge3") prismoid([5,5],[2,2],h=3,spin=-360/5*3.5,anchor=RIGHT+BOT);
|
||||||
|
// }
|
||||||
|
// Example: Textured prism
|
||||||
|
// regular_prism(5, side=25, h=50, texture="diamonds", tex_size=[5,5], style="concave");
|
||||||
|
module regular_prism(n,
|
||||||
|
h, r, center,
|
||||||
|
l, length, height,
|
||||||
|
r1,r2,ir,ir1,ir2,or,or1,or2,side,side1,side2,
|
||||||
|
d, d1, d2,id,id1,id2,od,od1,od2,
|
||||||
|
chamfer, chamfer1, chamfer2,
|
||||||
|
chamfang, chamfang1, chamfang2,
|
||||||
|
rounding, rounding1, rounding2,
|
||||||
|
realign=false, shift=[0,0],
|
||||||
|
teardrop=false,
|
||||||
|
from_end, from_end1, from_end2,
|
||||||
|
texture, tex_size=[5,5], tex_reps,
|
||||||
|
tex_inset=false, tex_rot=0,
|
||||||
|
tex_depth, tex_samples,
|
||||||
|
tex_taper, style,
|
||||||
|
anchor, spin=0, orient=UP
|
||||||
|
)
|
||||||
|
{
|
||||||
|
vnf_anchors_ovr = regular_prism(n=n,h=h,r=r,center=center, l=l,length=length,height=height,
|
||||||
|
r1=r1,r2=r2,ir=ir,ir1=ir1,ir2=ir2,or=or,or1=or1,or2=or2,side=side,side1=side1,side2=side2,
|
||||||
|
d=d,d1=d1,d2=d2,id=id,id1=id1,id2=id2,od=od,od1=od1,od2=od2,
|
||||||
|
chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2,
|
||||||
|
chamfang=chamfang,chamfang1=chamfang1,chamfang2=chamfang2,
|
||||||
|
rounding=rounding,rounding1=rounding1, rounding2=rounding2,
|
||||||
|
realign=realign, shift=shift,
|
||||||
|
teardrop=teardrop,
|
||||||
|
from_end=from_end, from_end1=from_end1, from_end2=from_end2,
|
||||||
|
texture=texture, tex_size=tex_size, tex_reps=tex_reps,
|
||||||
|
tex_inset=tex_inset, tex_rot=tex_rot,
|
||||||
|
tex_depth=tex_depth, tex_samples=tex_samples,
|
||||||
|
tex_taper=tex_taper, style=style,
|
||||||
|
_return_anchors=true);
|
||||||
|
attachable(anchor=anchor, orient=orient, spin=spin, vnf=vnf_anchors_ovr[0], anchors=vnf_anchors_ovr[1],override=vnf_anchors_ovr[2]){
|
||||||
|
vnf_polyhedron(vnf_anchors_ovr[0],convexity=is_def(texture)?10:2);
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function regular_prism(n,
|
||||||
|
h, r, center,
|
||||||
|
l, length, height,
|
||||||
|
r1,r2,ir,ir1,ir2,or,or1,or2,side,side1,side2,
|
||||||
|
d, d1, d2,id,id1,id2,od,od1,od2,
|
||||||
|
chamfer, chamfer1, chamfer2,
|
||||||
|
chamfang, chamfang1, chamfang2,
|
||||||
|
rounding, rounding1, rounding2,
|
||||||
|
circum=false, realign=false, shift=[0,0],
|
||||||
|
teardrop=false,
|
||||||
|
from_end, from_end1, from_end2,
|
||||||
|
texture, tex_size=[5,5], tex_reps,
|
||||||
|
tex_inset=false, tex_rot=0,
|
||||||
|
tex_depth, tex_samples, length, height,
|
||||||
|
tex_taper, style,
|
||||||
|
anchor, spin=0, orient=UP,_return_anchors=false
|
||||||
|
) =
|
||||||
|
assert(is_integer(n) && n>2, "n must be an integer 3 or greater")
|
||||||
|
let(
|
||||||
|
style = default(style,"min_edge"),
|
||||||
|
tex_depth = default(tex_depth,1),
|
||||||
|
height = one_defined([l, h, length, height],"l,h,length,height",dflt=1),
|
||||||
|
sc = 1/cos(180/n),
|
||||||
|
ir1 = u_mul(default(ir1,ir), sc),
|
||||||
|
ir2 = u_mul(default(ir2,ir), sc),
|
||||||
|
id1 = u_mul(default(id1,id), sc),
|
||||||
|
id2 = u_mul(default(id2,id), sc),
|
||||||
|
od1 = default(od1,od),
|
||||||
|
od2 = default(od2,od),
|
||||||
|
or1 = default(or1,or),
|
||||||
|
or2 = default(or2,or),
|
||||||
|
d1 = default(d1,d),
|
||||||
|
d2 = default(d2,d),
|
||||||
|
side = is_finite(side)? side/2/sin(180/n) : undef,
|
||||||
|
side1 = is_finite(side1)? side1/2/sin(180/n) : side,
|
||||||
|
side2 = is_finite(side2)? side2/2/sin(180/n) : side,
|
||||||
|
r1 = get_radius(r1=ir1,r2=or1,r=default(r1,r),d=d1,d1=id1,d2=od1,dflt=side1),
|
||||||
|
r2 = get_radius(r1=ir2,r2=or2,r=default(r2,r),d=d2,d1=id2,d2=od2,dflt=side2),
|
||||||
|
anchor = get_anchor(anchor,center,BOT,CENTER)
|
||||||
|
)
|
||||||
|
assert(num_defined([side,od,id,or,ir])<=1, "Can only define one of side, id, od, ir, and or")
|
||||||
|
assert(is_finite(r1), "Must specify finite number for prism bottom radius / diameter / side length")
|
||||||
|
assert(is_finite(r2), "Must specify finite number for prism top radius / diameter / side length")
|
||||||
|
assert(is_finite(height), "l/h/length/height must be a finite number.")
|
||||||
|
assert(is_vector(shift,2), "shift must be a 2D vector.")
|
||||||
|
let(
|
||||||
|
vnf = any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])
|
||||||
|
? assert(is_undef(texture), "Cannot combine roundings or chamfers with texturing")
|
||||||
|
let(
|
||||||
|
vang = atan2(r1-r2,height),
|
||||||
|
_chamf1 = first_defined([chamfer1, if (is_undef(rounding1)) chamfer, 0]),
|
||||||
|
_chamf2 = first_defined([chamfer2, if (is_undef(rounding2)) chamfer, 0]),
|
||||||
|
_fromend1 = first_defined([from_end1, from_end, false]),
|
||||||
|
_fromend2 = first_defined([from_end2, from_end, false]),
|
||||||
|
chang1 = first_defined([chamfang1, chamfang, 45+sign(_chamf1)*vang/2]),
|
||||||
|
chang2 = first_defined([chamfang2, chamfang, 45-sign(_chamf2)*vang/2]),
|
||||||
|
round1 = first_defined([rounding1, if (is_undef(chamfer1)) rounding, 0]),
|
||||||
|
round2 = first_defined([rounding2, if (is_undef(chamfer2)) rounding, 0]),
|
||||||
|
checks1 =
|
||||||
|
assert(is_finite(_chamf1), "chamfer1 must be a finite number if given.")
|
||||||
|
assert(is_finite(_chamf2), "chamfer2 must be a finite number if given.")
|
||||||
|
assert(is_finite(chang1) && chang1>0, "chamfang1 must be a positive number if given.")
|
||||||
|
assert(is_finite(chang2) && chang2>0, "chamfang2 must be a positive number if given.")
|
||||||
|
assert(chang1<90+sign(_chamf1)*vang, "chamfang1 must be smaller than the cone face angle")
|
||||||
|
assert(chang2<90-sign(_chamf2)*vang, "chamfang2 must be smaller than the cone face angle")
|
||||||
|
assert(num_defined([chamfer1,rounding1])<2, "cannot define both chamfer1 and rounding1")
|
||||||
|
assert(num_defined([chamfer2,rounding2])<2, "cannot define both chamfer2 and rounding2")
|
||||||
|
assert(num_defined([chamfer,rounding])<2, "cannot define both chamfer and rounding")
|
||||||
|
undef,
|
||||||
|
chamf1r = !_chamf1? 0
|
||||||
|
: !_fromend1? _chamf1
|
||||||
|
: law_of_sines(a=_chamf1, A=chang1, B=180-chang1-(90-sign(_chamf2)*vang)),
|
||||||
|
chamf2r = !_chamf2? 0
|
||||||
|
: !_fromend2? _chamf2
|
||||||
|
: law_of_sines(a=_chamf2, A=chang2, B=180-chang2-(90+sign(_chamf2)*vang)),
|
||||||
|
chamf1l = !_chamf1? 0
|
||||||
|
: _fromend1? abs(_chamf1)
|
||||||
|
: abs(law_of_sines(a=_chamf1, A=180-chang1-(90-sign(_chamf1)*vang), B=chang1)),
|
||||||
|
chamf2l = !_chamf2? 0
|
||||||
|
: _fromend2? abs(_chamf2)
|
||||||
|
: abs(law_of_sines(a=_chamf2, A=180-chang2-(90+sign(_chamf2)*vang), B=chang2)),
|
||||||
|
facelen = adj_ang_to_hyp(height, abs(vang)),
|
||||||
|
|
||||||
|
roundlen1 = round1 >= 0 ? round1/tan(45-vang/2)
|
||||||
|
: round1/tan(45+vang/2),
|
||||||
|
roundlen2 = round2 >=0 ? round2/tan(45+vang/2)
|
||||||
|
: round2/tan(45-vang/2),
|
||||||
|
dy1 = abs(_chamf1 ? chamf1l : round1 ? roundlen1 : 0),
|
||||||
|
dy2 = abs(_chamf2 ? chamf2l : round2 ? roundlen2 : 0),
|
||||||
|
td_ang = teardrop == true? 45 :
|
||||||
|
teardrop == false? 90 :
|
||||||
|
assert(is_finite(teardrop))
|
||||||
|
assert(teardrop>=0 && teardrop<=90)
|
||||||
|
teardrop,
|
||||||
|
|
||||||
|
checks2 =
|
||||||
|
assert(is_finite(round1), "rounding1 must be a number if given.")
|
||||||
|
assert(is_finite(round2), "rounding2 must be a number if given.")
|
||||||
|
assert(chamf1r <= r1, "chamfer1 is larger than the r1 radius of the cylinder.")
|
||||||
|
assert(chamf2r <= r2, "chamfer2 is larger than the r2 radius of the cylinder.")
|
||||||
|
assert(roundlen1 <= r1, "size of rounding1 is larger than the r1 radius of the cylinder.")
|
||||||
|
assert(roundlen2 <= r2, "size of rounding2 is larger than the r2 radius of the cylinder.")
|
||||||
|
assert(dy1+dy2 <= facelen, "Chamfers/roundings don't fit on the cylinder/cone. They exceed the length of the cylinder/cone face.")
|
||||||
|
undef,
|
||||||
|
path = [
|
||||||
|
[0,-height/2],
|
||||||
|
if (!approx(chamf1r,0))
|
||||||
|
each [
|
||||||
|
[r1, -height/2] + polar_to_xy(chamf1r,180),
|
||||||
|
[r1, -height/2] + polar_to_xy(chamf1l,90+vang),
|
||||||
|
]
|
||||||
|
else if (!approx(round1,0) && td_ang < 90)
|
||||||
|
each _teardrop_corner(r=round1, corner=[[max(0,r1-2*roundlen1),-height/2],[r1,-height/2],[r2,height/2]], ang=td_ang)
|
||||||
|
else if (!approx(round1,0) && td_ang >= 90)
|
||||||
|
each arc(r=abs(round1), corner=[[max(0,r1-2*roundlen1),-height/2],[r1,-height/2],[r2,height/2]])
|
||||||
|
else [r1,-height/2],
|
||||||
|
|
||||||
|
if (is_finite(chamf2r) && !approx(chamf2r,0))
|
||||||
|
each [
|
||||||
|
[r2, height/2] + polar_to_xy(chamf2l,270+vang),
|
||||||
|
[r2, height/2] + polar_to_xy(chamf2r,180),
|
||||||
|
]
|
||||||
|
else if (is_finite(round2) && !approx(round2,0))
|
||||||
|
each arc(r=abs(round2), corner=[[r1,-height/2],[r2,height/2],[max(0,r2-2*roundlen2),height/2]])
|
||||||
|
else [r2,height/2],
|
||||||
|
[0,height/2],
|
||||||
|
]
|
||||||
|
)
|
||||||
|
rotate_sweep(path,closed=false,$fn=n)
|
||||||
|
: is_undef(texture) ? cylinder(h=height, r1=r1, r2=r2, center=true, $fn=n)
|
||||||
|
: linear_sweep(regular_ngon(n=n,r=r1),scale=r2/r1,height=height,center=true,
|
||||||
|
texture=texture, tex_reps=tex_reps, tex_size=tex_size,
|
||||||
|
tex_inset=tex_inset, tex_rot=tex_rot,
|
||||||
|
tex_depth=tex_depth, tex_samples=tex_samples,
|
||||||
|
style=style),
|
||||||
|
skmat = down(height/2) *
|
||||||
|
skew(sxz=shift.x/height, syz=shift.y/height) *
|
||||||
|
up(height/2) *
|
||||||
|
zrot(realign? 180/n : 0),
|
||||||
|
ovnf = apply(skmat, vnf),
|
||||||
|
edge_face = [ [r2-r1,0,height],[(r2-r1)/sc,0,height]], // regular edge, then face edge, in xz plane
|
||||||
|
names = ["edge","face"],
|
||||||
|
anchors = approx(shift,[0,0]) ?
|
||||||
|
[for(i=[0:n-1], j=[0:1])
|
||||||
|
let(
|
||||||
|
M = zrot(-(i+j/2-(realign?1/2:0))*360/n),
|
||||||
|
edge = apply(M,edge_face[j]),
|
||||||
|
dir = apply(M,[height,0,-edge_face[j].x]),
|
||||||
|
spin = sign(dir.x)*vector_angle(edge - (edge*dir)*dir, rot(from=UP,to=dir,p=BACK))
|
||||||
|
)
|
||||||
|
each [
|
||||||
|
named_anchor(str(names[j],i), apply(M,[(r1+r2)/2/(j==0?1:sc),0,0]), dir, spin),
|
||||||
|
named_anchor(str(j==0?"top_corner":"top_edge",i), apply(M,[r2/(j==0?1:sc),0,height/2]), dir, spin),
|
||||||
|
named_anchor(str(j==0?"bot_corner":"bot_edge",i), apply(M,[r1/(j==0?1:sc),0,-height/2]), dir, spin),
|
||||||
|
]
|
||||||
|
]
|
||||||
|
:
|
||||||
|
let(
|
||||||
|
faces = [
|
||||||
|
for(i=[0:n-1])
|
||||||
|
let(
|
||||||
|
M1 = skmat*zrot(-i*360/n),
|
||||||
|
M2 = skmat*zrot(-(i+1)*360/n),
|
||||||
|
edge1 = apply(M1,[[r2,0,height/2], [r1,0,-height/2]]),
|
||||||
|
edge2 = apply(M2,[[r2,0,height/2], [r1,0,-height/2]]),
|
||||||
|
face_edge = (edge1+edge2)/2,
|
||||||
|
facenormal = unit(cross(edge1[0]-edge1[1], edge2[1]-edge1[0]))
|
||||||
|
)
|
||||||
|
[facenormal,face_edge[0]-face_edge[1],edge1[0]-edge1[1]] // [normal to face, edge through face center, actual edge]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
[for(i=[0:n-1])
|
||||||
|
let(
|
||||||
|
Mface = skmat*zrot(-(i+1/2)*360/n),
|
||||||
|
faceedge = faces[i][1],
|
||||||
|
facenormal = faces[i][0],
|
||||||
|
//facespin = _compute_spin(facenormal, faceedge), // spin along centerline of face instea of pointing up---seems to be wrong choice
|
||||||
|
facespin = _compute_spin(facenormal, UP),
|
||||||
|
edgenormal = unit(vector_bisect(facenormal,select(faces,i-1)[0])),
|
||||||
|
Medge = skmat*zrot(-i*360/n),
|
||||||
|
edge = faces[i][2],
|
||||||
|
edgespin = _compute_spin(edgenormal, edge)
|
||||||
|
)
|
||||||
|
each [
|
||||||
|
named_anchor(str("face",i), apply(Mface,[(r1+r2)/2/sc,0,0]), facenormal, facespin),
|
||||||
|
named_anchor(str("edge",i), apply(Medge,[(r1+r2)/2,0,0]), edgenormal, edgespin),
|
||||||
|
named_anchor(str("top_edge",i), apply(Mface,[r2/sc,0,height/2]), facenormal, facespin),
|
||||||
|
named_anchor(str("top_corner",i), apply(Medge,[r2,0,height/2]), edgenormal, edgespin),
|
||||||
|
named_anchor(str("bot_edge",i), apply(Mface,[r1/sc,0,-height/2]), facenormal, facespin),
|
||||||
|
named_anchor(str("bot_corner",i), apply(Medge,[r1,0,-height/2]), edgenormal, edgespin)
|
||||||
|
]
|
||||||
|
],
|
||||||
|
override = approx(shift,[0,0]) ? undef : [[UP, [point3d(shift,height/2), UP]]],
|
||||||
|
final_vnf = reorient(anchor,spin,orient, vnf=ovnf, p=ovnf,anchors=anchors, override=override)
|
||||||
|
)
|
||||||
|
_return_anchors ? [final_vnf,anchors,override]
|
||||||
|
: final_vnf;
|
||||||
|
|
||||||
|
|
||||||
// Module: rect_tube()
|
// Module: rect_tube()
|
||||||
// Synopsis: Creates a rectangular tube.
|
// Synopsis: Creates a rectangular tube.
|
||||||
|
@ -1115,6 +1475,9 @@ function rect_tube(
|
||||||
// Description:
|
// Description:
|
||||||
// When called as a module, creates a 3D triangular wedge with the hypotenuse in the X+Z+ quadrant.
|
// When called as a module, creates a 3D triangular wedge with the hypotenuse in the X+Z+ quadrant.
|
||||||
// When called as a function, creates a VNF for a 3D triangular wedge with the hypotenuse in the X+Z+ quadrant.
|
// When called as a function, creates a VNF for a 3D triangular wedge with the hypotenuse in the X+Z+ quadrant.
|
||||||
|
// The anchors for the wedge are the anchors of the wedge's bounding box. The named enchors listed below
|
||||||
|
// give the sloped face and edges, and those edge anchors have spin oriented with positive Z value in the
|
||||||
|
// direction of the sloped edge.
|
||||||
//
|
//
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// size = [width, thickness, height]
|
// size = [width, thickness, height]
|
||||||
|
@ -1147,10 +1510,15 @@ module wedge(size=[1, 1, 1], center, anchor, spin=0, orient=UP)
|
||||||
size = scalar_vec3(size);
|
size = scalar_vec3(size);
|
||||||
anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]);
|
anchor = get_anchor(anchor, center, -[1,1,1], -[1,1,1]);
|
||||||
vnf = wedge(size, anchor="origin");
|
vnf = wedge(size, anchor="origin");
|
||||||
|
spindir = unit([0,-size.y,size.z]);
|
||||||
|
hypot_dir = unit([0,size.z,size.y],UP);
|
||||||
|
left_dir = unit(hypot_dir+LEFT);
|
||||||
|
right_dir = unit(hypot_dir+RIGHT);
|
||||||
|
hedge_spin=vector_angle(spindir,rot(from=UP,to=left_dir, p=BACK));
|
||||||
anchors = [
|
anchors = [
|
||||||
named_anchor("hypot", CTR, unit([0,size.z,size.y],UP)),
|
named_anchor("hypot", CTR, hypot_dir, 180),
|
||||||
named_anchor("hypot_left", [-size.x/2,0,0], unit(unit([0,size.z,size.y],UP)+LEFT)),
|
named_anchor("hypot_left", [-size.x/2,0,0], left_dir,-hedge_spin),
|
||||||
named_anchor("hypot_right", [size.x/2,0,0], unit(unit([0,size.z,size.y],UP)+RIGHT)),
|
named_anchor("hypot_right", [size.x/2,0,0], right_dir,hedge_spin),
|
||||||
];
|
];
|
||||||
attachable(anchor,spin,orient, size=size, anchors=anchors) {
|
attachable(anchor,spin,orient, size=size, anchors=anchors) {
|
||||||
if (size.z > 0) {
|
if (size.z > 0) {
|
||||||
|
@ -1174,10 +1542,15 @@ function wedge(size=[1,1,1], center, anchor, spin=0, orient=UP) =
|
||||||
[1,4,2], [2,4,5], [2,5,3], [0,2,3],
|
[1,4,2], [2,4,5], [2,5,3], [0,2,3],
|
||||||
],
|
],
|
||||||
vnf = [scale(size/2,p=pts), faces],
|
vnf = [scale(size/2,p=pts), faces],
|
||||||
|
spindir = unit([0,-size.y,size.z]),
|
||||||
|
hypot_dir = unit([0,size.z,size.y],UP),
|
||||||
|
left_dir = unit(hypot_dir+LEFT),
|
||||||
|
right_dir = unit(hypot_dir+RIGHT),
|
||||||
|
hedge_spin=vector_angle(spindir,rot(from=UP,to=left_dir, p=BACK)),
|
||||||
anchors = [
|
anchors = [
|
||||||
named_anchor("hypot", CTR, unit([0,size.z,size.y],UP)),
|
named_anchor("hypot", CTR, hypot_dir, 180),
|
||||||
named_anchor("hypot_left", [-size.x/2,0,0], unit(unit([0,size.z,size.y],UP)+LEFT)),
|
named_anchor("hypot_left", [-size.x/2,0,0], left_dir,-hedge_spin),
|
||||||
named_anchor("hypot_right", [size.x/2,0,0], unit(unit([0,size.z,size.y],UP)+RIGHT)),
|
named_anchor("hypot_right", [size.x/2,0,0], right_dir,hedge_spin),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
reorient(anchor,spin,orient, size=size, anchors=anchors, p=vnf);
|
reorient(anchor,spin,orient, size=size, anchors=anchors, p=vnf);
|
||||||
|
@ -1276,7 +1649,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
|
||||||
// Synopsis: Creates an attachable cylinder with roundovers and chamfering.
|
// Synopsis: Creates an attachable cylinder with roundovers and chamfering.
|
||||||
// SynTags: Geom, VNF
|
// SynTags: Geom, VNF
|
||||||
// Topics: Cylinders, Textures, Rounding, Chamfers
|
// Topics: Cylinders, Textures, Rounding, Chamfers
|
||||||
// See Also: texture(), rotate_sweep(), cylinder()
|
// See Also: regular_prism(), texture(), rotate_sweep(), cylinder()
|
||||||
// Usage: Normal Cylinders
|
// Usage: Normal Cylinders
|
||||||
// cyl(l|h|length|height, r, [center], [circum=], [realign=]) [ATTACHMENTS];
|
// cyl(l|h|length|height, r, [center], [circum=], [realign=]) [ATTACHMENTS];
|
||||||
// cyl(l|h|length|height, d=, ...) [ATTACHMENTS];
|
// cyl(l|h|length|height, d=, ...) [ATTACHMENTS];
|
||||||
|
@ -1300,7 +1673,7 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
|
||||||
// cyl(l|h|length|height, r1=, r2=, texture=, [tex_size=]|[tex_reps=], [tex_depth=], [tex_rot=], [tex_samples=], [style=], [tex_taper=], [tex_inset=], ...);
|
// cyl(l|h|length|height, r1=, r2=, texture=, [tex_size=]|[tex_reps=], [tex_depth=], [tex_rot=], [tex_samples=], [style=], [tex_taper=], [tex_inset=], ...);
|
||||||
// cyl(l|h|length|height, d1=, d2=, texture=, [tex_size=]|[tex_reps=], [tex_depth=], [tex_rot=], [tex_samples=], [style=], [tex_taper=], [tex_inset=], ...);
|
// cyl(l|h|length|height, d1=, d2=, texture=, [tex_size=]|[tex_reps=], [tex_depth=], [tex_rot=], [tex_samples=], [style=], [tex_taper=], [tex_inset=], ...);
|
||||||
//
|
//
|
||||||
// Usage: Caled as a function to get a VNF
|
// Usage: Called as a function to get a VNF
|
||||||
// vnf = cyl(...);
|
// vnf = cyl(...);
|
||||||
//
|
//
|
||||||
// Description:
|
// Description:
|
||||||
|
@ -1310,6 +1683,10 @@ function cylinder(h, r1, r2, center, r, d, d1, d2, anchor, spin=0, orient=UP) =
|
||||||
// the cylinder or cone's sloped side. The more specific parameters like chamfer1 or rounding2 override the more
|
// the cylinder or cone's sloped side. The more specific parameters like chamfer1 or rounding2 override the more
|
||||||
// general ones like chamfer or rounding, so if you specify `rounding=3, chamfer2=3` you will get a chamfer at the top and
|
// general ones like chamfer or rounding, so if you specify `rounding=3, chamfer2=3` you will get a chamfer at the top and
|
||||||
// rounding at the bottom.
|
// rounding at the bottom.
|
||||||
|
// .
|
||||||
|
// When creating a textured cylinder, the number of facets is determined by the sampling of the texture. Any `$fn`, `$fa` or `$fs` values in
|
||||||
|
// effect are ignored. To create a textured prism with a specified number of flat facets use {{regular_prism()}}. Anchors for cylinders
|
||||||
|
// appear on the ideal cylinder, not on actual discretized shape the module produces. For anchors on the shape surface, use {{regular_prism()}}.
|
||||||
// Figure(2D,Big,NoAxes,VPR = [0, 0, 0], VPT = [0,0,0], VPD = 82): Chamfers on cones can be tricky. This figure shows chamfers of the same size and same angle, A=30 degrees. Note that the angle is measured on the inside, and produces a quite different looking chamfer at the top and bottom of the cone. Straight black arrows mark the size of the chamfers, which may not even appear the same size visually. When you do not give an angle, the triangle that is cut off will be isoceles, like the triangle at the top, with two equal angles.
|
// Figure(2D,Big,NoAxes,VPR = [0, 0, 0], VPT = [0,0,0], VPD = 82): Chamfers on cones can be tricky. This figure shows chamfers of the same size and same angle, A=30 degrees. Note that the angle is measured on the inside, and produces a quite different looking chamfer at the top and bottom of the cone. Straight black arrows mark the size of the chamfers, which may not even appear the same size visually. When you do not give an angle, the triangle that is cut off will be isoceles, like the triangle at the top, with two equal angles.
|
||||||
// color("lightgray")
|
// color("lightgray")
|
||||||
// projection()
|
// projection()
|
||||||
|
@ -1520,13 +1897,13 @@ function cyl(
|
||||||
) =
|
) =
|
||||||
assert(num_defined([style,tex_style])<2, "In cyl() the 'tex_style' parameters has been replaced by 'style'. You cannot give both.")
|
assert(num_defined([style,tex_style])<2, "In cyl() the 'tex_style' parameters has been replaced by 'style'. You cannot give both.")
|
||||||
assert(num_defined([tex_reps,tex_counts])<2, "In cyl() the 'tex_counts' parameters has been replaced by 'tex_reps'. You cannot give both.")
|
assert(num_defined([tex_reps,tex_counts])<2, "In cyl() the 'tex_counts' parameters has been replaced by 'tex_reps'. You cannot give both.")
|
||||||
assert(num_defined([tex_scale,tex_depth])<2, "In linear_sweep() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both.")
|
assert(num_defined([tex_scale,tex_depth])<2, "In cyl() the 'tex_scale' parameter has been replaced by 'tex_depth'. You cannot give both.")
|
||||||
let(
|
let(
|
||||||
style = is_def(tex_style)? echo("In cyl() the 'tex_style' parameter is deprecated and has been replaced by 'style'")tex_style
|
style = is_def(tex_style)? echo("In cyl() the 'tex_style' parameter is deprecated and has been replaced by 'style'")tex_style
|
||||||
: default(style,"min_edge"),
|
: default(style,"min_edge"),
|
||||||
tex_reps = is_def(tex_counts)? echo("In cyl() the 'tex_counts' parameter is deprecated and has been replaced by 'tex_reps'")tex_counts
|
tex_reps = is_def(tex_counts)? echo("In cyl() the 'tex_counts' parameter is deprecated and has been replaced by 'tex_reps'")tex_counts
|
||||||
: tex_reps,
|
: tex_reps,
|
||||||
tex_depth = is_def(tex_scale)? echo("In rotate_sweep() the 'tex_scale' parameter is deprecated and has been replaced by 'tex_depth'")tex_scale
|
tex_depth = is_def(tex_scale)? echo("In cyl() the 'tex_scale' parameter is deprecated and has been replaced by 'tex_depth'")tex_scale
|
||||||
: default(tex_depth,1),
|
: default(tex_depth,1),
|
||||||
l = one_defined([l, h, length, height],"l,h,length,height",dflt=1),
|
l = one_defined([l, h, length, height],"l,h,length,height",dflt=1),
|
||||||
_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),
|
||||||
|
@ -1580,8 +1957,6 @@ function cyl(
|
||||||
: abs(law_of_sines(a=_chamf2, A=180-chang2-(90+sign(_chamf2)*vang), B=chang2)),
|
: abs(law_of_sines(a=_chamf2, A=180-chang2-(90+sign(_chamf2)*vang), B=chang2)),
|
||||||
facelen = adj_ang_to_hyp(l, abs(vang)),
|
facelen = adj_ang_to_hyp(l, abs(vang)),
|
||||||
|
|
||||||
cp1 = [r1,-l/2],
|
|
||||||
cp2 = [r2,+l/2],
|
|
||||||
roundlen1 = round1 >= 0 ? round1/tan(45-vang/2)
|
roundlen1 = round1 >= 0 ? round1/tan(45-vang/2)
|
||||||
: round1/tan(45+vang/2),
|
: round1/tan(45+vang/2),
|
||||||
roundlen2 = round2 >=0 ? round2/tan(45+vang/2)
|
roundlen2 = round2 >=0 ? round2/tan(45+vang/2)
|
||||||
|
@ -3738,3 +4113,5 @@ module ruler(length=100, width, thickness=1, depth=3, labels=false, pipscale=1/3
|
||||||
|
|
||||||
|
|
||||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
|
||||||
|
|
||||||
|
|
26
vnf.scad
26
vnf.scad
|
@ -1055,7 +1055,7 @@ function _slice_3dpolygons(polys, dir, cuts) =
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// vnf = A VNF structure, or list of VNF structures.
|
// vnf = A VNF structure, or list of VNF structures.
|
||||||
// convexity = Max number of times a line could intersect a wall of the shape.
|
// convexity = Max number of times a line could intersect a wall of the shape.
|
||||||
// cp = Centerpoint for determining intersection anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
// cp = Centerpoint for determining intersection anchors or centering the shape. Determines the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"`
|
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"origin"`
|
||||||
// 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`
|
||||||
|
@ -2331,5 +2331,29 @@ module vnf_validate(vnf, size=1, show_warns=true, check_isects=false, opacity=0.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function _vnf_find_edge_faces(vnf,edge) =
|
||||||
|
let(
|
||||||
|
faces = vnf[1],
|
||||||
|
goodind = [for(i=idx(faces))
|
||||||
|
let(result=search(edge,faces[i]))
|
||||||
|
if (result*0==[0,0] &&
|
||||||
|
(abs(result[0]-result[1])==1
|
||||||
|
|| (min(result)==0 && max(result)==len(faces[i])-1)))
|
||||||
|
i
|
||||||
|
]
|
||||||
|
)
|
||||||
|
goodind;
|
||||||
|
|
||||||
|
|
||||||
|
function _vnf_find_corner_faces(vnf,corner) =
|
||||||
|
let(
|
||||||
|
faces = vnf[1]
|
||||||
|
)
|
||||||
|
[for(i=idx(faces))
|
||||||
|
let(result=search([corner],faces[i])[0])
|
||||||
|
if (result!=[])
|
||||||
|
i];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||||
|
|
Loading…
Reference in a new issue