mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-15 08:59:40 +00:00
Compare commits
7 commits
dcd02d26f2
...
6ad978185e
Author | SHA1 | Date | |
---|---|---|---|
|
6ad978185e | ||
|
3f9fb3a5b4 | ||
|
4d95f81352 | ||
|
9b2a32e574 | ||
|
82a13f9e97 | ||
|
428649e97b | ||
|
5f6e067341 |
5 changed files with 426 additions and 118 deletions
102
attachments.scad
102
attachments.scad
|
@ -35,9 +35,14 @@ $parent_orient = UP;
|
||||||
$parent_size = undef;
|
$parent_size = undef;
|
||||||
$parent_geom = undef;
|
$parent_geom = undef;
|
||||||
|
|
||||||
|
$edge_angle = undef;
|
||||||
|
$edge_length = undef;
|
||||||
|
|
||||||
$tags_shown = "ALL";
|
$tags_shown = "ALL";
|
||||||
$tags_hidden = [];
|
$tags_hidden = [];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ANCHOR_TYPES = ["intersect","hull"];
|
_ANCHOR_TYPES = ["intersect","hull"];
|
||||||
|
|
||||||
|
|
||||||
|
@ -486,6 +491,8 @@ _ANCHOR_TYPES = ["intersect","hull"];
|
||||||
// Side Effects:
|
// Side Effects:
|
||||||
// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
|
// `$attach_anchor` for each `from=` anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
|
||||||
// `$attach_to` is set to `undef`.
|
// `$attach_to` is set to `undef`.
|
||||||
|
// `$edge_angle` is set to the angle of the edge if the anchor is on an edge and the parent is a prismoid, or vnf with "hull" anchoring
|
||||||
|
// `$edge_length` is set to the length of the edge if the anchor is on an edge and the parent is a prismoid, or vnf with "hull" anchoring
|
||||||
// Example:
|
// Example:
|
||||||
// spheroid(d=20) {
|
// spheroid(d=20) {
|
||||||
// position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
|
// position(TOP) cyl(l=10, d1=10, d2=5, anchor=BOTTOM);
|
||||||
|
@ -505,6 +512,8 @@ module position(at,from)
|
||||||
two_d = _attach_geom_2d($parent_geom);
|
two_d = _attach_geom_2d($parent_geom);
|
||||||
for (anchr = anchors) {
|
for (anchr = anchors) {
|
||||||
anch = _find_anchor(anchr, $parent_geom);
|
anch = _find_anchor(anchr, $parent_geom);
|
||||||
|
$edge_angle = len(anch)==5 ? struct_val(anch[4],"edge_angle") : undef;
|
||||||
|
$edge_length = len(anch)==5 ? struct_val(anch[4],"edge_length") : undef;
|
||||||
$attach_to = undef;
|
$attach_to = undef;
|
||||||
$attach_anchor = anch;
|
$attach_anchor = anch;
|
||||||
translate(anch[1]) children();
|
translate(anch[1]) children();
|
||||||
|
@ -972,6 +981,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
|
||||||
anchor_data = _find_anchor(anchor, $parent_geom);
|
anchor_data = _find_anchor(anchor, $parent_geom);
|
||||||
$edge_angle = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_angle") : undef;
|
$edge_angle = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_angle") : undef;
|
||||||
$edge_length = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_length") : undef;
|
$edge_length = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_length") : undef;
|
||||||
|
$edge_end1 = len(anchor_data)==5 ? struct_val(anchor_data[4],"vec") : undef;
|
||||||
anchor_pos = anchor_data[1];
|
anchor_pos = anchor_data[1];
|
||||||
anchor_dir = factor*anchor_data[2];
|
anchor_dir = factor*anchor_data[2];
|
||||||
anchor_spin = two_d || !inside || anchor==TOP || anchor==BOT ? anchor_data[3]
|
anchor_spin = two_d || !inside || anchor==TOP || anchor==BOT ? anchor_data[3]
|
||||||
|
@ -1955,7 +1965,7 @@ module face_mask(faces=[LEFT,RIGHT,FRONT,BACK,BOT,TOP]) {
|
||||||
// Usage:
|
// Usage:
|
||||||
// PARENT() edge_mask([edges], [except]) CHILDREN;
|
// PARENT() edge_mask([edges], [except]) CHILDREN;
|
||||||
// Description:
|
// Description:
|
||||||
// Takes a 3D mask shape, and attaches it to the given edges, with the appropriate orientation to be
|
// Takes a 3D mask shape, and attaches it to the given edges of a cuboid parent, with the appropriate orientation to be
|
||||||
// differenced away. The mask shape should be vertically oriented (Z-aligned) with the back-right
|
// differenced away. The mask shape should be vertically oriented (Z-aligned) with the back-right
|
||||||
// quadrant (X+Y+) shaped to be diffed away from the edge of parent attachable shape. If no tag is set
|
// quadrant (X+Y+) shaped to be diffed away from the edge of parent attachable shape. If no tag is set
|
||||||
// then `edge_mask` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag.
|
// then `edge_mask` sets the tag for children to "remove" so that it will work with the default {{diff()}} tag.
|
||||||
|
@ -1996,6 +2006,8 @@ module edge_mask(edges=EDGES_ALL, except=[]) {
|
||||||
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, "Not 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_length = len(anch)==5 ? struct_val(anch[4],"edge_length") : undef;
|
||||||
$attach_to = undef;
|
$attach_to = undef;
|
||||||
$attach_anchor = anch;
|
$attach_anchor = anch;
|
||||||
rotang =
|
rotang =
|
||||||
|
@ -3179,12 +3191,13 @@ function reorient(
|
||||||
// orient = A vector pointing in the direction parts should project from the anchor position. Default: UP
|
// orient = A vector pointing in the direction parts should project from the anchor position. Default: UP
|
||||||
// spin = If needed, the angle to rotate the part around the direction vector. Default: 0
|
// spin = If needed, the angle to rotate the part around the direction vector. Default: 0
|
||||||
// ---
|
// ---
|
||||||
|
// info = structure listing info to be propagated to the attached child, e.g. "edge_anchor"
|
||||||
// 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) =
|
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, "Cannot 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, "Must give pos or rot")
|
||||||
is_undef(rot) ? [name, pos, default(orient,UP), default(spin,0)]
|
is_undef(rot) ? [name, pos, default(orient,UP), default(spin,0), if (info) info]
|
||||||
:
|
:
|
||||||
let(
|
let(
|
||||||
flip = default(flip,false),
|
flip = default(flip,false),
|
||||||
|
@ -3198,7 +3211,7 @@ function named_anchor(name, pos, orient, spin, rot, flip) =
|
||||||
decode=rot_decode(rot(to=UP,from=dir)*_force_rot(rot)),
|
decode=rot_decode(rot(to=UP,from=dir)*_force_rot(rot)),
|
||||||
spin = decode[0]*sign(decode[1].z)
|
spin = decode[0]*sign(decode[1].z)
|
||||||
)
|
)
|
||||||
[name, pos, dir, spin];
|
[name, pos, dir, spin, if (info) info];
|
||||||
|
|
||||||
|
|
||||||
// Function: attach_geom()
|
// Function: attach_geom()
|
||||||
|
@ -3252,7 +3265,7 @@ function named_anchor(name, pos, orient, spin, rot, flip) =
|
||||||
// anchors = If given as a list of anchor points, allows named anchor points.
|
// anchors = If given as a list of anchor points, allows named anchor points.
|
||||||
// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D)
|
// two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D)
|
||||||
// axis = The vector pointing along the axis of a geometry. Default: UP
|
// axis = The vector pointing along the axis of a geometry. Default: UP
|
||||||
// override = Function that takes an anchor and returns a pair `[position,direction]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction]]` entries. If the direction/position that is returned is undef then the default will be used.
|
// override = Function that takes an anchor and returns a pair `[position,direction,spin]` to use for that anchor to override the normal one. You can also supply a lookup table that is a list of `[anchor, [position, direction,spin]]` entries. If the direction/position/spin that is returned is undef then the default will be used.
|
||||||
//
|
//
|
||||||
// Example(NORENDER): Null/Point Shape
|
// Example(NORENDER): Null/Point Shape
|
||||||
// geom = attach_geom();
|
// geom = attach_geom();
|
||||||
|
@ -3723,6 +3736,18 @@ function _get_cp(geom) =
|
||||||
/// Arguments:
|
/// Arguments:
|
||||||
/// anchor = Vector or named anchor string.
|
/// anchor = Vector or named anchor string.
|
||||||
/// geom = The geometry description of the shape.
|
/// geom = The geometry description of the shape.
|
||||||
|
|
||||||
|
function _three_edge_corner_dir(facevecs,edges) =
|
||||||
|
let(
|
||||||
|
v1 = vector_bisect(facevecs[0],facevecs[2]),
|
||||||
|
v2 = vector_bisect(facevecs[1],facevecs[2]),
|
||||||
|
p1 = plane_from_normal(rot(v=edges[0],a=90,p=v1)),
|
||||||
|
p2 = plane_from_normal(rot(v=edges[1],a=-90,p=v2)),
|
||||||
|
line = plane_intersection(p1,p2),
|
||||||
|
v3 = unit(line[1]-line[0],UP)
|
||||||
|
)
|
||||||
|
unit(v3,UP);
|
||||||
|
|
||||||
function _find_anchor(anchor, geom)=
|
function _find_anchor(anchor, geom)=
|
||||||
is_string(anchor)? (
|
is_string(anchor)? (
|
||||||
anchor=="origin"? [anchor, CENTER, UP, 0] // Ok that this returns 3d anchor in the 2d case?
|
anchor=="origin"? [anchor, CENTER, UP, 0] // Ok that this returns 3d anchor in the 2d case?
|
||||||
|
@ -3779,16 +3804,28 @@ function _find_anchor(anchor, geom)=
|
||||||
dir = anch==CENTER? UP
|
dir = anch==CENTER? UP
|
||||||
: len(facevecs)==1? unit(facevecs[0],UP)
|
: len(facevecs)==1? unit(facevecs[0],UP)
|
||||||
: len(facevecs)==2? vector_bisect(facevecs[0],facevecs[1])
|
: len(facevecs)==2? vector_bisect(facevecs[0],facevecs[1])
|
||||||
: let(
|
: _three_edge_corner_dir(facevecs,[FWD,LEFT])*anch.z,
|
||||||
v1 = vector_bisect(facevecs[0],facevecs[2]),
|
|
||||||
v2 = vector_bisect(facevecs[1],facevecs[2]),
|
|
||||||
p1 = plane_from_normal(yrot(90,p=v1)),
|
|
||||||
p2 = plane_from_normal(xrot(-90,p=v2)),
|
|
||||||
line = plane_intersection(p1,p2),
|
|
||||||
v3 = unit(line[1]-line[0],UP) * anch.z
|
|
||||||
)
|
|
||||||
unit(v3,UP),
|
|
||||||
edgeang = len(facevecs)==2 ? 180-vector_angle(facevecs[0], facevecs[1]) : undef,
|
edgeang = len(facevecs)==2 ? 180-vector_angle(facevecs[0], facevecs[1]) : undef,
|
||||||
|
edgelen = anch.z==0 ? norm(edge)
|
||||||
|
: anch.z>0 ? abs([size2.y,size2.x]*axy)
|
||||||
|
: abs([size.y,size.x]*axy),
|
||||||
|
endvecs = len(facevecs)!=2 ? undef
|
||||||
|
: anch.z==0 ? [DOWN, UP]
|
||||||
|
: let(
|
||||||
|
raxy = zrot(-90,axy),
|
||||||
|
bot1 = point3d(v_mul(point2d(size )/2, raxy), -h/2),
|
||||||
|
top1 = point3d(v_mul(point2d(size2)/2, raxy) + shift, h/2),
|
||||||
|
edge1 = top1-bot1,
|
||||||
|
vec1 = (raxy.x!=0) ? unit(rot(from=UP, to=[edge1.x,0,max(0.01,h)], p=[raxy.x,0,0]), UP)
|
||||||
|
: unit(rot(from=UP, to=[0,edge1.y,max(0.01,h)], p=[0,raxy.y,0]), UP),
|
||||||
|
raxy2 = zrot(90,axy),
|
||||||
|
bot2 = point3d(v_mul(point2d(size )/2, raxy2), -h/2),
|
||||||
|
top2 = point3d(v_mul(point2d(size2)/2, raxy2) + shift, h/2),
|
||||||
|
edge2 = top2-bot2,
|
||||||
|
vec2 = (raxy2.y!=0) ? unit(rot(from=UP, to=[edge.x,0,max(0.01,h)], p=[raxy2.x,0,0]), UP)
|
||||||
|
: unit(rot(from=UP, to=[0,edge.y,max(0.01,h)], p=[0,raxy2.y,0]), UP)
|
||||||
|
)
|
||||||
|
[vec1,vec2],
|
||||||
final_dir = default(override[1],anch==CENTER?UP:rot(from=UP, to=axis, p=dir)),
|
final_dir = default(override[1],anch==CENTER?UP:rot(from=UP, to=axis, p=dir)),
|
||||||
final_pos = default(override[0],rot(from=UP, to=axis, p=pos)),
|
final_pos = default(override[0],rot(from=UP, to=axis, p=pos)),
|
||||||
|
|
||||||
|
@ -3801,7 +3838,8 @@ function _find_anchor(anchor, geom)=
|
||||||
: anch.z!=0 && sum(v_abs(anch))==2 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=anch.z*[anch.y,-anch.x,0]))
|
: anch.z!=0 && sum(v_abs(anch))==2 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=anch.z*[anch.y,-anch.x,0]))
|
||||||
: norm(anch)==3 ? _compute_spin(final_dir, final_dir==DOWN || final_dir==UP ? BACK : UP)
|
: norm(anch)==3 ? _compute_spin(final_dir, final_dir==DOWN || final_dir==UP ? BACK : UP)
|
||||||
: oang // face anchors point UP/BACK
|
: oang // face anchors point UP/BACK
|
||||||
) [anchor, final_pos, final_dir, default(override[2],spin), if (is_def(edgeang)) [["edge_angle",edgeang],["edge_length",norm(edge)]]]
|
) [anchor, final_pos, final_dir, default(override[2],spin),
|
||||||
|
if (is_def(edgeang)) [["edge_angle",edgeang],["edge_length",edgelen], ["vec", endvecs]]]
|
||||||
) : type == "conoid"? ( //r1, r2, l, shift
|
) : type == "conoid"? ( //r1, r2, l, shift
|
||||||
let(
|
let(
|
||||||
rr1=geom[1],
|
rr1=geom[1],
|
||||||
|
@ -3941,16 +3979,41 @@ function _find_anchor(anchor, geom)=
|
||||||
len(matchind)!=1 ? []
|
len(matchind)!=1 ? []
|
||||||
: let( // After this runs we have two edges as index pairs, and their associated faces as index values
|
: let( // After this runs we have two edges as index pairs, and their associated faces as index values
|
||||||
match1 = select(idxs,[0,matchind[0]]),
|
match1 = select(idxs,[0,matchind[0]]),
|
||||||
match2 = list_remove_values(idxs,match1),
|
match2 = list_remove(idxs,[0,matchind[0]]),
|
||||||
|
facelists = [for(i=[0:1], j=[0:1])
|
||||||
|
let(
|
||||||
|
ed = [match1[i],match2[j]],
|
||||||
|
fl = _vnf_find_edge_faces(vnf,ed)
|
||||||
|
)
|
||||||
|
if (fl!=[]) [ed,fl]
|
||||||
|
],
|
||||||
|
final = [column(facelists,0), flatten(column(facelists,1))]
|
||||||
|
)
|
||||||
|
assert(len(final[1])==2, "invalid!")
|
||||||
|
final,
|
||||||
|
|
||||||
|
|
||||||
|
/*[column(facelists,0), column(facelists
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
face1 = _vnf_find_edge_faces(vnf,[match1[0],match2[0]]),
|
face1 = _vnf_find_edge_faces(vnf,[match1[0],match2[0]]),
|
||||||
face2 = _vnf_find_edge_faces(vnf,[match1[0],match2[1]]),
|
face2 = _vnf_find_edge_faces(vnf,[match1[0],match2[1]]),
|
||||||
|
face1a = _vnf_find_edge_faces(vnf,[match1[1],match2[0]]),
|
||||||
|
face2a = _vnf_find_edge_faces(vnf,[match1[1],match2[1]]),
|
||||||
|
feet= echo(facelist1 =select(vnf[1],face1a), facelist2=select(vnf[1],face2a)),
|
||||||
edge1 = [match1[0], face1==[] ? match2[1] : match2[0]],
|
edge1 = [match1[0], face1==[] ? match2[1] : match2[0]],
|
||||||
edge2 = list_remove_values(idxs,edge1),
|
edge2 = list_remove_values(idxs,edge1),
|
||||||
|
fee=echo(edxs=idxs, edge1=edge1, edge2=edge2,alt=[match1[1],match2[0]],[match1[1],match2[1]],
|
||||||
|
edgefaces = _vnf_find_edge_faces(vnf,edge1),_vnf_find_edge_faces(vnf,edge2) ),
|
||||||
face3 = _vnf_find_edge_faces(vnf,edge2),
|
face3 = _vnf_find_edge_faces(vnf,edge2),
|
||||||
allfaces = concat(face1,face2,face3)
|
allfaces = concat(face1,face2,face3),
|
||||||
|
f=echo(pts=pts,matchind=matchind,match1=match1,match2=match2,face1=face1,face2=face2,face3=face3,edge1=edge1,edge2=edge2,face1a=face1a,face2a=face2a)
|
||||||
)
|
)
|
||||||
assert(len(allfaces)==2, "Invalid polyhedron encountered while computing VNF anchor")
|
assert(len(allfaces)==2, str("Invalid polyhedron encountered while computing VNF anchor",len(allfaces)))
|
||||||
[[edge1,edge2], allfaces],
|
[[edge1,edge2], allfaces],*/
|
||||||
|
// fe=echo(edge_faces=edges_faces),
|
||||||
dir = len(idxs)>2 && edges_faces==[] ? [anchor,oang]
|
dir = len(idxs)>2 && edges_faces==[] ? [anchor,oang]
|
||||||
: edges_faces!=[] ?
|
: edges_faces!=[] ?
|
||||||
let(
|
let(
|
||||||
|
@ -3961,6 +4024,7 @@ function _find_anchor(anchor, geom)=
|
||||||
projnormals = project_plane(point4d(cross(facenormals[0],facenormals[1])), facenormals),
|
projnormals = project_plane(point4d(cross(facenormals[0],facenormals[1])), facenormals),
|
||||||
ang = 180- posmod(v_theta(projnormals[1])-v_theta(projnormals[0]),360),
|
ang = 180- posmod(v_theta(projnormals[1])-v_theta(projnormals[0]),360),
|
||||||
horiz_face = [for(i=[0:1]) if (approx(v_abs(facenormals[i]),UP)) i],
|
horiz_face = [for(i=[0:1]) if (approx(v_abs(facenormals[i]),UP)) i],
|
||||||
|
fda= echo(horiz=horiz_face),
|
||||||
spin = horiz_face==[] ?
|
spin = horiz_face==[] ?
|
||||||
let(
|
let(
|
||||||
edgedir = edge[1]-edge[0],
|
edgedir = edge[1]-edge[0],
|
||||||
|
|
|
@ -44,6 +44,8 @@ _UNDEF="LRG+HX7dy89RyHvDlAKvb9Y04OTuaikpx205CTh8BSI";
|
||||||
// The correct hole should hold the plug when the long block is turned upside-down.
|
// The correct hole should hold the plug when the long block is turned upside-down.
|
||||||
// The number in front of that hole will indicate the `$slop` value that is ideal for your printer.
|
// The number in front of that hole will indicate the `$slop` value that is ideal for your printer.
|
||||||
// Remember to set that slop value in your scripts after you include the BOSL2 library: ie: `$slop = 0.15;`
|
// Remember to set that slop value in your scripts after you include the BOSL2 library: ie: `$slop = 0.15;`
|
||||||
|
// .
|
||||||
|
// Note that the `$slop` value may be different using different materials even on the same printer.
|
||||||
// Example(3D,Med): Slop Calibration Part.
|
// Example(3D,Med): Slop Calibration Part.
|
||||||
// min_slop = 0.00;
|
// min_slop = 0.00;
|
||||||
// slop_step = 0.05;
|
// slop_step = 0.05;
|
||||||
|
@ -215,6 +217,36 @@ CENTER = [ 0, 0, 0]; // Centered zero vector.
|
||||||
CTR = CENTER;
|
CTR = CENTER;
|
||||||
CENTRE = CENTER;
|
CENTRE = CENTER;
|
||||||
|
|
||||||
|
// Function: EDGE()
|
||||||
|
// Synopsis: Named edge anchor constants
|
||||||
|
// Topics: Constants, Attachment
|
||||||
|
// Usage:
|
||||||
|
// EDGE(i)
|
||||||
|
// EDGE(direction,i)
|
||||||
|
// Description:
|
||||||
|
// A shorthand for the named anchors "edge0", "top_edge0", "bot_edge0", etc.
|
||||||
|
// Use `EDGE(i)` to get "edge<i>". Use `EDGE(TOP,i)` to get "top_edge<i>" and
|
||||||
|
// use `EDGE(BOT,i)` to get "bot_edge(i)". You can also use
|
||||||
|
// `EDGE(CTR,i)` to get "edge<i>" and you can replace TOP or BOT with simply 1 or -1.
|
||||||
|
|
||||||
|
function EDGE(a,b) =
|
||||||
|
is_undef(b) ? str("edge",a)
|
||||||
|
: assert(in_list(a,[TOP,BOT,CTR,1,0,-1]),str("Invalid direction: ",a))
|
||||||
|
let(
|
||||||
|
choices=["bot_","","top_"],
|
||||||
|
ind=is_vector(a) ? a.z : a
|
||||||
|
)
|
||||||
|
str(choices[ind+1],"edge",b);
|
||||||
|
|
||||||
|
// Function: FACE()
|
||||||
|
// Synopsis: Named face anchor constants
|
||||||
|
// Topics: Constants, Attachment
|
||||||
|
// Usage:
|
||||||
|
// FACE(i)
|
||||||
|
// Description:
|
||||||
|
// A shorthand for the named anchors "face0", "face1", etc.
|
||||||
|
|
||||||
|
function FACE(i) = str("face",i);
|
||||||
|
|
||||||
// Section: Line specifiers
|
// Section: Line specifiers
|
||||||
// Used by functions in geometry.scad for specifying whether two points
|
// Used by functions in geometry.scad for specifying whether two points
|
||||||
|
|
125
masks3d.scad
125
masks3d.scad
|
@ -25,7 +25,7 @@
|
||||||
// Difference it from the object to be chamfered. The center of
|
// Difference it from the object to be chamfered. The center of
|
||||||
// the mask object should align exactly with the edge to be chamfered.
|
// the mask object should align exactly with the edge to be chamfered.
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l/h/length/height = Length of mask.
|
// l/h/length/height = Length of mask. Default: $edge_length if defined
|
||||||
// chamfer = Size of chamfer.
|
// chamfer = Size of chamfer.
|
||||||
// excess = The extra amount to add to the length of the mask so that it differences away from other shapes cleanly. Default: `0.1`
|
// excess = The extra amount to add to the length of the mask so that it differences away from other shapes cleanly. Default: `0.1`
|
||||||
// ---
|
// ---
|
||||||
|
@ -49,7 +49,8 @@
|
||||||
// }
|
// }
|
||||||
function chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_edge_mask");
|
function chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) = no_function("chamfer_edge_mask");
|
||||||
module chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) {
|
module chamfer_edge_mask(l, chamfer=1, excess=0.1, h, length, height, anchor=CENTER, spin=0, orient=UP) {
|
||||||
l = one_defined([l, h, height, length], "l,h,height,length");
|
l = is_def($edge_length) && !any_defined([l,length,h,height]) ? $edge_length
|
||||||
|
: one_defined([l,length,h,height],"l,length,h,height");
|
||||||
default_tag("remove") {
|
default_tag("remove") {
|
||||||
attachable(anchor,spin,orient, size=[chamfer*2, chamfer*2, l]) {
|
attachable(anchor,spin,orient, size=[chamfer*2, chamfer*2, l]) {
|
||||||
cylinder(r=chamfer, h=l+excess, center=true, $fn=4);
|
cylinder(r=chamfer, h=l+excess, center=true, $fn=4);
|
||||||
|
@ -169,28 +170,35 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Section: Rounding Masks
|
// Section: Rounding Masks
|
||||||
|
|
||||||
// Module: rounding_edge_mask()
|
// Module: rounding_edge_mask()
|
||||||
// Synopsis: Creates a shape to round a 90° edge.
|
// Synopsis: Creates a shape to round a 90° edge.
|
||||||
// SynTags: Geom
|
// SynTags: Geom
|
||||||
// Topics: Masks, Rounding, Shapes (3D)
|
// Topics: Masks, Rounding, Shapes (3D)
|
||||||
// See Also: rounding_corner_mask(), default_tag(), diff()
|
// See Also: edge_profile(), rounding_corner_mask(), default_tag(), diff()
|
||||||
// Usage:
|
// Usage:
|
||||||
// rounding_edge_mask(l|h=|length=|height=, r|d=, [ang], [excess=]) [ATTACHMENTS];
|
// rounding_edge_mask(l|h=|length=|height=, r|d=, [ang], [excess=], [rounding=|chamfer=], ) [ATTACHMENTS];
|
||||||
// rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [ang=], [excess=]) [ATTACHMENTS];
|
// rounding_edge_mask(l|h=|length=|height=, r1=|d1=, r2=|d2=, [ang=], [excess=], [rounding=|chamfer=]) [ATTACHMENTS];
|
||||||
// Description:
|
// Description:
|
||||||
// Creates a shape that can be used to round a straight edge at any angle.
|
// Creates a mask shape that can be used to round a straight edge at any angle, with
|
||||||
// Difference it from the object to be rounded. The center of the mask
|
// different rounding radii at each end. The corner of the mask appears on the Z axis with one face on the XZ plane.
|
||||||
// object should align exactly with the edge to be rounded. You can use it with {{diff()}} and
|
// You must align the mask corner with the edge you want to round. If your parent object is a cuboid, the easiest way to
|
||||||
// {{edge_mask()}} to attach masks automatically to objects. The default "remove" tag is set
|
// do this is to use {{diff()}} and {{edge_mask()}}. However, this method is somewhat inflexible regarding orientation of a tapered
|
||||||
// automatically.
|
// mask, and it does not support other parent shapes. You can attach the mask to a larger range of shapes using
|
||||||
|
// {{attach()}} to anchor the `LEFT+FWD` anchor of the mask to a desired corner on the parent with `inside=true`.
|
||||||
|
// Many shapes propagate `$edge_angle` and `$edge_length` which can aid in configuring the mask, and you can adjust the
|
||||||
|
// mask as needed to align the taper as desired. The default "remove" tag is set so {{diff()}} will automatically difference
|
||||||
|
// away the mask. You can of course also position the mask manually and use `difference()`.
|
||||||
|
// .
|
||||||
|
// For mating with other roundings or chamfers on cuboids or regular prisms, you can choose end roundings and end chamfers. These affect
|
||||||
|
// only the curved edge of the mask ends and will only work if the terminating face is perpendicular to the masked edge. The `excess`
|
||||||
|
// parameter will add extra length to the mask when you use these settings.
|
||||||
//
|
//
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l/h/length/height = Length of mask.
|
// l/h/length/height = Length of mask. Default: $edge_length if defined
|
||||||
// r = Radius of the rounding.
|
// r = Radius of the rounding.
|
||||||
// ang = Angle between faces for rounding. Default: 90
|
// ang = Angle between faces for rounding. Default: $edge_angle if defined, otherwise 90
|
||||||
// ---
|
// ---
|
||||||
// r1 = Bottom radius of rounding.
|
// r1 = Bottom radius of rounding.
|
||||||
// r2 = Top radius of rounding.
|
// r2 = Top radius of rounding.
|
||||||
|
@ -198,6 +206,12 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE
|
||||||
// d1 = Bottom diameter of rounding.
|
// d1 = Bottom diameter of rounding.
|
||||||
// d2 = Top diameter of rounding.
|
// d2 = Top diameter of rounding.
|
||||||
// excess = Extra size for the mask. Defaults: 0.1
|
// excess = Extra size for the mask. Defaults: 0.1
|
||||||
|
// rounding = Radius of roundong along ends. Default: 0
|
||||||
|
// rounding1 = Radius of rounding along bottom end
|
||||||
|
// rounding2 = Radius of rounding along top end
|
||||||
|
// chamfer = Chamfer size of end chamfers. Default: 0
|
||||||
|
// chamfer1 = Chamfer size of chamfer at bottom end
|
||||||
|
// chamfer2 = Chamfer size of chamfer at top end
|
||||||
// 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`
|
||||||
|
@ -249,34 +263,85 @@ module chamfer_cylinder_mask(r, chamfer, d, ang=45, from_end=false, anchor=CENTE
|
||||||
// rounding_edge_mask(l=p.z, r=25);
|
// rounding_edge_mask(l=p.z, r=25);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
// Example(3D,VPT=[5.02872,6.37039,-0.503894],VPR=[75.3,0,107.4],VPD=74.4017): Mask shape with end rounding at the top, chamfer at the bottom, and a large excess value:
|
||||||
|
// rounding_edge_mask(r=10,h=20, chamfer1=3, rounding2=3, excess=1);
|
||||||
|
// Example(3D,VPT=[1.05892,1.10442,2.20513],VPR=[60.6,0,118.1],VPD=74.4017): Attaching masks using {{attach()}} with automatic angle and length from the parent. Note that sometimes the automatic length is too short because it is the length of the edge itself.
|
||||||
|
// diff()
|
||||||
|
// prismoid([20,30],[12,19], h=10,shift=[4,7])
|
||||||
|
// attach([TOP+RIGHT,RIGHT+FRONT],LEFT+FWD,inside=true)
|
||||||
|
// rounding_edge_mask(r1=2,r2=4);
|
||||||
|
// Example(3D): The mask does not need to be the full length of the edge
|
||||||
|
// diff()
|
||||||
|
// cuboid(20)
|
||||||
|
// attach(RIGHT+TOP,LEFT+FWD,inside=true,inset=-.1,align=FWD)
|
||||||
|
// rounding_edge_mask(r1=0,r2=10,length=10);
|
||||||
|
|
||||||
function rounding_edge_mask(l, r, ang=90, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask");
|
function rounding_edge_mask(l, r, ang=90, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("rounding_edge_mask");
|
||||||
module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length, h, height, anchor=CENTER, spin=0, orient=UP,
|
module rounding_edge_mask(l, r, ang, r1, r2, excess=0.01, d1, d2,d,r,length, h, height, anchor=CENTER, spin=0, orient=UP,
|
||||||
|
rounding,rounding1,rounding2,chamfer,chamfer1,chamfer2,
|
||||||
_remove_tag=true)
|
_remove_tag=true)
|
||||||
{
|
{
|
||||||
length = one_defined([l,length,h,height],"l,length,h,height");
|
ang = first_defined([ang,$edge_angle,90]);
|
||||||
|
length = is_def($edge_length) && !any_defined([l,length,h,height]) ? $edge_length
|
||||||
|
: one_defined([l,length,h,height],"l,length,h,height");
|
||||||
r1 = get_radius(r1=r1, d1=d1,d=d,r=r);
|
r1 = get_radius(r1=r1, d1=d1,d=d,r=r);
|
||||||
r2 = get_radius(r2=r2, d1=d2,d=d,r=r);
|
r2 = get_radius(r2=r2, d1=d2,d=d,r=r);
|
||||||
|
dummy1 = assert(num_defined([chamfer,rounding])<2, "Cannot give both rounding and chamfer")
|
||||||
|
assert(num_defined([chamfer1,rounding1])<2, "Cannot give both rounding1 and chamfer1")
|
||||||
|
assert(num_defined([chamfer2,rounding2])<2, "Cannot give both rounding2 and chamfer2");
|
||||||
|
rounding1 = first_defined([rounding1,rounding,0]);
|
||||||
|
rounding2 = first_defined([rounding2,rounding,0]);
|
||||||
|
chamfer1 = first_defined([chamfer1,chamfer,0]);
|
||||||
|
chamfer2 = first_defined([chamfer2,chamfer,0]);
|
||||||
dummy = assert(all_nonnegative([r1,r2]), "radius/diameter value(s) must be nonnegative")
|
dummy = assert(all_nonnegative([r1,r2]), "radius/diameter value(s) must be nonnegative")
|
||||||
assert(all_positive([length]), "length/l/h/height must be a positive value")
|
assert(all_positive([length]), "length/l/h/height must be a positive value")
|
||||||
assert(is_finite(ang) && ang>0 && ang<180, "ang must be a number between 0 and 180");
|
assert(is_finite(ang) && ang>0 && ang<180, "ang must be a number between 0 and 180")
|
||||||
|
assert(all_nonnegative([chamfer1,chamfer2,rounding1,rounding2]), "chamfers and roundings must be nonnegative");
|
||||||
steps = ceil(segs(max(r1,r2))*(180-ang)/360);
|
steps = ceil(segs(max(r1,r2))*(180-ang)/360);
|
||||||
function make_path(r) =
|
function make_path(r) =
|
||||||
let(
|
r==0 ? repeat([0,0],steps+1)
|
||||||
arc = r==0 ? repeat([0,0],steps+1)
|
: arc(n=steps+1, r=r, corner=[polar_to_xy(r,ang),[0,0],[r,0]]);
|
||||||
: arc(n=steps+1, r=r, corner=[polar_to_xy(r,ang),[0,0],[r,0]]),
|
|
||||||
maxx = last(arc).x,
|
|
||||||
maxy = arc[0].y,
|
|
||||||
cp = [-excess/tan(ang/2),-excess]
|
|
||||||
)
|
|
||||||
[
|
|
||||||
[maxx, -excess],
|
|
||||||
cp,
|
|
||||||
arc[0] + polar_to_xy(excess, 90+ang),
|
|
||||||
each arc
|
|
||||||
];
|
|
||||||
path1 = path3d(make_path(r1),-length/2);
|
path1 = path3d(make_path(r1),-length/2);
|
||||||
path2 = path3d(make_path(r2),length/2);
|
path2 = path3d(make_path(r2),length/2);
|
||||||
|
|
||||||
|
function getarc(bigr,r,chamfer,p1,p2,h,print=false) =
|
||||||
|
r==0 && chamfer==0? [p2]
|
||||||
|
:
|
||||||
|
let(
|
||||||
|
steps = ceil(segs(r)/4)+1,
|
||||||
|
center = [bigr/tan(ang/2), bigr,h],
|
||||||
|
refplane = plane_from_normal([-(p2-center).y, (p2-center).x, 0], p2),
|
||||||
|
refnormal = plane_normal(refplane),
|
||||||
|
mplane = plane3pt(p2,p1,center),
|
||||||
|
A = plane_normal(mplane),
|
||||||
|
basept = lerp(p2,p1,max(r,chamfer)/2/h),
|
||||||
|
corner = [basept+refnormal*(refplane[3]-basept*refnormal)/(refnormal*refnormal),
|
||||||
|
p2,
|
||||||
|
center],
|
||||||
|
bare_arc = chamfer ? [p2+chamfer*unit(corner[0]-corner[1]),p2+chamfer*unit(corner[2]-corner[1])]
|
||||||
|
: arc(r=r, corner = corner, n=steps),
|
||||||
|
arc_with_excess = [each bare_arc, up(excess, last(bare_arc))],
|
||||||
|
arc = [for(pt=arc_with_excess) pt+refnormal*(mplane[3]-pt*A)/(refnormal*A)]
|
||||||
|
)
|
||||||
|
arc;
|
||||||
|
cp = [-excess/tan(ang/2), -excess];
|
||||||
|
extra1 = rounding1 || chamfer1 ? [0,0,excess] : CTR;
|
||||||
|
extra2 = rounding2 || chamfer2 ? [0,0,excess] : CTR;
|
||||||
|
pathlist = [for(i=[0:len(path1)-1])
|
||||||
|
let(
|
||||||
|
path = [
|
||||||
|
if (i==0) move(polar_to_xy( excess, 90+ang),path1[i]-extra1)
|
||||||
|
else if (i==len(path1)-1) fwd(excess,last(path1)-extra1)
|
||||||
|
else point3d(cp,-length/2-extra1.z),
|
||||||
|
each reverse(zflip(getarc(r1,rounding1,chamfer1,zflip(path2[i]), zflip(path1[i]),length/2))),
|
||||||
|
each getarc(r2,rounding2,chamfer2,path1[i],path2[i],length/2,print=rounding2!=0&&!is_undef(rounding2)&&i==3),
|
||||||
|
if (i==0) move(polar_to_xy( excess, 90+ang),path2[i]+extra2)
|
||||||
|
else if (i==len(path2)-1) fwd(excess,last(path2)+extra2)
|
||||||
|
else point3d(cp, length/2+extra2.z),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
path];
|
||||||
|
|
||||||
left_normal = cylindrical_to_xyz(1,90+ang,0);
|
left_normal = cylindrical_to_xyz(1,90+ang,0);
|
||||||
left_dir = cylindrical_to_xyz(1,ang,0);
|
left_dir = cylindrical_to_xyz(1,ang,0);
|
||||||
zdir = unit([length, 0,-(r2-r1)/tan(ang/2)]);
|
zdir = unit([length, 0,-(r2-r1)/tan(ang/2)]);
|
||||||
|
@ -315,7 +380,7 @@ module rounding_edge_mask(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,r,length,
|
||||||
[BACK+RIGHT+TOP, [cylindrical_to_xyz(cutfact*r2,ang/2,length/2), zrot(ang/2,zdir)+UP,ang/2+90]],
|
[BACK+RIGHT+TOP, [cylindrical_to_xyz(cutfact*r2,ang/2,length/2), zrot(ang/2,zdir)+UP,ang/2+90]],
|
||||||
[BACK+RIGHT+BOT, [cylindrical_to_xyz(cutfact*r1,ang/2,-length/2), zrot(ang/2,zdir)+DOWN,ang/2+90]],
|
[BACK+RIGHT+BOT, [cylindrical_to_xyz(cutfact*r1,ang/2,-length/2), zrot(ang/2,zdir)+DOWN,ang/2+90]],
|
||||||
];
|
];
|
||||||
vnf = vnf_vertex_array([path1,path2],caps=true,col_wrap=true);
|
vnf = vnf_vertex_array(reverse(pathlist), col_wrap=true,caps=true);
|
||||||
default_tag("remove", _remove_tag)
|
default_tag("remove", _remove_tag)
|
||||||
attachable(anchor,spin,orient,size=[1,1,length],override=override){
|
attachable(anchor,spin,orient,size=[1,1,length],override=override){
|
||||||
vnf_polyhedron(vnf);
|
vnf_polyhedron(vnf);
|
||||||
|
|
146
rounding.scad
146
rounding.scad
|
@ -1348,7 +1348,10 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||||
// will get a similar or better looking model with fewer vertices using "round" instead of
|
// will get a similar or better looking model with fewer vertices using "round" instead of
|
||||||
// "chamfer". Use the "chamfer" style offset only in cases where the number of steps is very small or just one (such as when using
|
// "chamfer". Use the "chamfer" style offset only in cases where the number of steps is very small or just one (such as when using
|
||||||
// the `os_chamfer` profile type).
|
// the `os_chamfer` profile type).
|
||||||
//
|
// .
|
||||||
|
// This module offers four anchor types. The default is "hull" in which VNF anchors are placed on the VNF of the **unrounded** object. You
|
||||||
|
// can also use "intersect" to get the intersection anchors to the unrounded object. If you prefer anchors that respect the rounding
|
||||||
|
// then use "surf_hull" or "intersect_hull".
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// path = 2d path (list of points) to extrude
|
// path = 2d path (list of points) to extrude
|
||||||
// height / length / l / h = total height (including rounded portions, but not extra sections) of the output. Default: combined height of top and bottom end treatments.
|
// height / length / l / h = total height (including rounded portions, but not extra sections) of the output. Default: combined height of top and bottom end treatments.
|
||||||
|
@ -1375,7 +1378,7 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||||
// atype = Select "hull", "intersect", "surf_hull" or "surf_intersect" anchor types. Default: "hull"
|
// atype = Select "hull", "intersect", "surf_hull" or "surf_intersect" anchor types. Default: "hull"
|
||||||
// cp = Centerpoint for determining "intersect" 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 "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. Default: "centroid"
|
||||||
// Anchor Types:
|
// Anchor Types:
|
||||||
// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings.
|
// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. (default)
|
||||||
// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
|
// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
|
||||||
// surf_hull = Anchors to the convex hull of the offset_sweep shape, including end treatments.
|
// surf_hull = Anchors to the convex hull of the offset_sweep shape, including end treatments.
|
||||||
// surf_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments.
|
// surf_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments.
|
||||||
|
@ -2030,7 +2033,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
|
||||||
// vnf = rounded_prism(bottom, [top], [height=|h=|length=|l=], [joint_top=], [joint_bot=], [joint_sides=], [k=], [k_top=], [k_bot=], [k_sides=], [splinesteps=], [debug=]);
|
// vnf = rounded_prism(bottom, [top], [height=|h=|length=|l=], [joint_top=], [joint_bot=], [joint_sides=], [k=], [k_top=], [k_bot=], [k_sides=], [splinesteps=], [debug=]);
|
||||||
// Description:
|
// Description:
|
||||||
// Construct a generalized prism with continuous curvature rounding. You supply the polygons for the top and bottom of the prism. The only
|
// Construct a generalized prism with continuous curvature rounding. You supply the polygons for the top and bottom of the prism. The only
|
||||||
// limitation is that joining the edges must produce a valid polyhedron with coplanar side faces. You specify the rounding by giving
|
// limitation is that joining the edges must produce a valid polyhedron with coplanar side faces. The vertices of the top and bottom
|
||||||
|
// are joined in the order listed. The top should have the standard vertex order for a polyhedron: clockwise as seen when viewing the prism
|
||||||
|
// from the outside.
|
||||||
|
// .
|
||||||
|
// You specify the rounding by giving
|
||||||
// the joint distance away from the corner for the rounding curve. The k parameter ranges from 0 to 1 with a default of 0.5. Larger
|
// the joint distance away from the corner for the rounding curve. The k parameter ranges from 0 to 1 with a default of 0.5. Larger
|
||||||
// values give a more abrupt transition and smaller ones a more gradual transition. If you set the value much higher
|
// values give a more abrupt transition and smaller ones a more gradual transition. If you set the value much higher
|
||||||
// than 0.8 the curvature changes abruptly enough that though it is theoretically continuous, it may
|
// than 0.8 the curvature changes abruptly enough that though it is theoretically continuous, it may
|
||||||
|
@ -2059,14 +2066,30 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
|
||||||
// construct a top that is a single point or two points. This means you can completely round a cube by setting the joint to half of
|
// construct a top that is a single point or two points. This means you can completely round a cube by setting the joint to half of
|
||||||
// the cube's width.
|
// the cube's width.
|
||||||
// If you set `debug` to true the module version will display the polyhedron even when it is invalid and it will show the bezier patches at the corners.
|
// If you set `debug` to true the module version will display the polyhedron even when it is invalid and it will show the bezier patches at the corners.
|
||||||
// This can help troubleshoot problems with your parameters. With the function form setting debug to true causes it to return [patches,vnf] where
|
// This can help troubleshoot problems with your parameters. With the function form setting debug to true causes run even on invalid cases and to return [patches,vnf] where
|
||||||
// patches is a list of the bezier control points for the corner patches.
|
// patches is a list of the bezier control points for the corner patches.
|
||||||
// .
|
// .
|
||||||
|
// This module offers five anchor types. The default is "hull" in which VNF anchors are placed on the VNF of the **unrounded** object. You
|
||||||
|
// can also use "intersect" to get the intersection anchors to the unrounded object. If you prefer anchors that respect the rounding
|
||||||
|
// then use "surf_hull" or "intersect_hull". Lastly, in the special case of a prism with four sides, you can use "prismoid" anchoring
|
||||||
|
// which will attempt to assign standard prismoid anchors to the shape by assigning as RIGHT the face that is closest to the RIGHT direction,
|
||||||
|
// and defining the other anchors around the shape baesd on that choice.
|
||||||
|
// .
|
||||||
// Note that rounded_prism() is not well suited to rounding shapes that have already been rounded, or that have many points.
|
// Note that rounded_prism() is not well suited to rounding shapes that have already been rounded, or that have many points.
|
||||||
// It works best when the top and bottom are polygons with well-defined corners. When the polygons have been rounded already,
|
// It works best when the top and bottom are polygons with well-defined corners. When the polygons have been rounded already,
|
||||||
// further rounding generates tiny bezier patches patches that can more easily
|
// further rounding generates tiny bezier patches patches that can more easily
|
||||||
// interfere, giving rise to an invalid polyhedron. It's also slow because you get bezier patches for every corner in the model.
|
// interfere, giving rise to an invalid polyhedron. It's also slow because you get bezier patches for every corner in the model.
|
||||||
// .
|
// .
|
||||||
|
// Named Anchors:
|
||||||
|
// "origin" = The native position of the prism.
|
||||||
|
// "top" = Top face, with spin BACK if face is parallel to the XY plane, or with positive Z otherwise
|
||||||
|
// "bot" = Bottom face, with spin BACK if face is parallel to the XY plane, or with positive Z otherwise
|
||||||
|
// "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
|
||||||
|
// "top_edge0", "top_edge1", etc = Center of each top edge, spin pointing clockwise (from top)
|
||||||
|
// "bot_edge0", "bot_edge1", etc = Center of each bottom edge, spin pointing clockwise (from bottom)
|
||||||
|
// "top_corner0", "top_corner1", etc = Top corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
|
// "bot_corner0", "bot_2corner1", etc = Bottom corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// bottom = 2d or 3d path describing bottom polygon
|
// bottom = 2d or 3d path describing bottom polygon
|
||||||
// top = 2d or 3d path describing top polygon (must be the same dimension as bottom)
|
// top = 2d or 3d path describing top polygon (must be the same dimension as bottom)
|
||||||
|
@ -2085,11 +2108,23 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
|
||||||
// anchor = Translate so anchor point is at the origin. (module only) Default: "origin"
|
// anchor = Translate so anchor point is at the origin. (module only) Default: "origin"
|
||||||
// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0
|
// spin = Rotate this many degrees around Z axis after anchor. (module only) Default: 0
|
||||||
// orient = Vector to rotate top towards after spin (module only)
|
// orient = Vector to rotate top towards after spin (module only)
|
||||||
// atype = Select "hull" or "intersect" anchor types. (module only) Default: "hull"
|
// atype = Select "prismoid", "hull", "intersect", "surf_hull" or "surf_intersect" anchor types. (module only) Default: "hull"
|
||||||
// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. (module only) Default: "centroid"
|
// cp = Centerpoint for determining "intersect" anchors or centering the shape. Determintes the base of the anchor vector. Can be "centroid", "mean", "box" or a 3D point. (module only) Default: "centroid"
|
||||||
// Named Anchors:
|
// Named Anchors:
|
||||||
// "origin" = The native position of the prism.
|
// "top" = center of top face pointing normal to that face
|
||||||
|
// "bot" = center of bottom face pointing normal to that face
|
||||||
|
// "edge0", "edge1", etc. = Center of each side edge, spin pointing up along the edge. Can access with EDGE(i)
|
||||||
|
// "face0", "face1", etc. = Center of each side face, spin pointing up. Can access with FACE(i)
|
||||||
|
// "top_edge0", "top_edge1", etc = Center of each top edge, spin pointing clockwise (from top). Can access with EDGE(TOP,i)
|
||||||
|
// "bot_edge0", "bot_edge1", etc = Center of each bottom edge, spin pointing clockwise (from bottom). Can access with EDGE(BOT,i)
|
||||||
|
// "top_corner0", "top_corner1", etc = Top corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
|
// "bot_corner0", "bot_corner1", etc = Bottom corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
// Anchor Types:
|
// Anchor Types:
|
||||||
|
// hull = Anchors to the convex hull of the linear sweep of the path, ignoring any end roundings. (default)
|
||||||
|
// intersect = Anchors to the surface of the linear sweep of the path, ignoring any end roundings.
|
||||||
|
// surf_hull = Anchors to the convex hull of the offset_sweep shape, including end treatments.
|
||||||
|
// surf_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments.
|
||||||
|
|
||||||
// "hull" = Anchors to the virtual convex hull of the prism.
|
// "hull" = Anchors to the virtual convex hull of the prism.
|
||||||
// "intersect" = Anchors to the surface of the prism.
|
// "intersect" = Anchors to the surface of the prism.
|
||||||
// Example: Uniformly rounded pentagonal prism
|
// Example: Uniformly rounded pentagonal prism
|
||||||
|
@ -2172,11 +2207,24 @@ module rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_bot
|
||||||
k=0.5, splinesteps=16, h, length, l, height, convexity=10, debug=false,
|
k=0.5, splinesteps=16, h, length, l, height, convexity=10, debug=false,
|
||||||
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull")
|
anchor="origin",cp="centroid",spin=0, orient=UP, atype="hull")
|
||||||
{
|
{
|
||||||
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
|
dummy1 = assert(in_list(atype, ["intersect","hull","surf_intersect","surf_hull","prismoid"]),
|
||||||
|
"Anchor type must be one of: \"hull\", \"intersect\", \"surf_hull\", \"surf_intersect\" or \"prismoid\"")
|
||||||
|
assert(atype!="prismoid" || len(bottom)==4, "Anchor type \"prismoid\" requires that len(bottom)=4");
|
||||||
result = rounded_prism(bottom=bottom, top=top, joint_bot=joint_bot, joint_top=joint_top, joint_sides=joint_sides,
|
result = rounded_prism(bottom=bottom, top=top, joint_bot=joint_bot, joint_top=joint_top, joint_sides=joint_sides,
|
||||||
k_bot=k_bot, k_top=k_top, k_sides=k_sides, k=k, splinesteps=splinesteps, h=h, length=length, height=height, l=l,debug=debug);
|
k_bot=k_bot, k_top=k_top, k_sides=k_sides, k=k, splinesteps=splinesteps, h=h, length=length, height=height, l=l,
|
||||||
vnf = debug ? result[1] : result;
|
debug=debug, _full_info=true);
|
||||||
attachable(anchor=anchor, spin=spin, orient=orient, vnf=vnf, extent=atype=="hull", cp=cp)
|
|
||||||
|
top = is_undef(top) ? path3d(bottom,height/2) :
|
||||||
|
len(top[0])==2 ? path3d(top,height/2) :
|
||||||
|
top;
|
||||||
|
bottom = len(bottom[0])==2 ? path3d(bottom,-height/2) : bottom;
|
||||||
|
unrounded = vnf_vertex_array([top,bottom],caps=true, col_wrap=true,reverse=true);
|
||||||
|
|
||||||
|
vnf = result[1];
|
||||||
|
geom = atype=="prismoid" ? attach_geom(size=[1,1,1],anchors=result[2], override=result[3])
|
||||||
|
: in_list(atype,["hull","intersect"]) ? attach_geom(vnf=unrounded, extent=atype=="hull", cp=cp, anchors=result[2])
|
||||||
|
: attach_geom(vnf=vnf, extent=atype=="surf_hull", cp=cp, anchors=result[2]);
|
||||||
|
attachable(anchor=anchor, spin=spin, orient=orient, geom=geom)
|
||||||
{
|
{
|
||||||
if (debug){
|
if (debug){
|
||||||
vnf_polyhedron(vnf, convexity=convexity);
|
vnf_polyhedron(vnf, convexity=convexity);
|
||||||
|
@ -2189,7 +2237,7 @@ module rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_bot
|
||||||
|
|
||||||
|
|
||||||
function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_bot, k_top, k_sides, k=0.5, splinesteps=16,
|
function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_bot, k_top, k_sides, k=0.5, splinesteps=16,
|
||||||
h, length, l, height, debug=false) =
|
h, length, l, height, debug=false, _full_info=false) =
|
||||||
let(
|
let(
|
||||||
bottom = force_path(bottom,"bottom"),
|
bottom = force_path(bottom,"bottom"),
|
||||||
top = force_path(top,"top")
|
top = force_path(top,"top")
|
||||||
|
@ -2347,9 +2395,81 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
|
||||||
for(pts=edge_points) vnf_vertex_array(pts),
|
for(pts=edge_points) vnf_vertex_array(pts),
|
||||||
debug ? vnf_from_polygons(faces,fast=true)
|
debug ? vnf_from_polygons(faces,fast=true)
|
||||||
: vnf_triangulate(vnf_from_polygons(faces))
|
: vnf_triangulate(vnf_from_polygons(faces))
|
||||||
])
|
]),
|
||||||
|
topnormal = unit(cross(top[0]-top[1],top[2]-top[1])),
|
||||||
|
botnormal = -unit(cross(bottom[0]-bottom[1],bottom[2]-bottom[1])),
|
||||||
|
sidenormal = [for(i=idx(top))
|
||||||
|
unit(cross(select(top,i+1)-top[i], bottom[i]-top[i]))],
|
||||||
|
|
||||||
|
//pos, orient, spin, info=...
|
||||||
|
|
||||||
|
anchors = [
|
||||||
|
for(i=idx(top))
|
||||||
|
let(
|
||||||
|
face = concat(select(top,[i+1,i]), select(bottom,i,i+1)),
|
||||||
|
face_ctr = mean(concat(select(top,[i+1,i]), select(bottom,i,i+1))),
|
||||||
|
bot_edge = bottom[i]-select(bottom,i+1),
|
||||||
|
bot_edge_ctr = mean(select(bottom,i,i+1)),
|
||||||
|
bot_edge_normal = unit(mean([sidenormal[i],botnormal])),
|
||||||
|
top_edge_normal = unit(mean([sidenormal[i],topnormal])),
|
||||||
|
top_edge = select(top,i+1)-top[i],
|
||||||
|
top_edge_ctr = mean(select(top,i,i+1)),
|
||||||
|
top_edge_dir = select(top,i+1)-top[i],
|
||||||
|
edge = [top[i],bottom[i]],
|
||||||
|
edge_ctr = mean([top[i],bottom[i]]),
|
||||||
|
edge_normal = unit(mean(select(sidenormal,[i,i-1]))),
|
||||||
|
top_corner_dir = _three_edge_corner_dir([select(sidenormal,i-1),sidenormal[i],topnormal],
|
||||||
|
[top[i]-select(top,i-1), top_edge]),
|
||||||
|
bot_corner_dir = _three_edge_corner_dir([select(sidenormal,i-1),sidenormal[i],botnormal],
|
||||||
|
[bottom[i]-select(bottom,i-1), bot_edge])
|
||||||
|
)
|
||||||
|
each[
|
||||||
|
named_anchor(EDGE(i),edge_ctr,edge_normal, _compute_spin(edge_normal,top[i]-bottom[i]),
|
||||||
|
info=[["edge_angle",180-vector_angle(sidenormal[i],select(sidenormal,i-1))], ["edge_length",norm(top[i]-bottom[i])]]),
|
||||||
|
named_anchor(EDGE(UP,i),top_edge_ctr, top_edge_normal, _compute_spin(top_edge_normal, top_edge),
|
||||||
|
info=[["edge_angle",180-vector_angle(topnormal,sidenormal[i])], ["edge_length",norm(top_edge)]]),
|
||||||
|
named_anchor(EDGE(DOWN,i),bot_edge_ctr, bot_edge_normal, _compute_spin(bot_edge_normal, bot_edge),
|
||||||
|
info=[["edge_angle",180-vector_angle(botnormal,sidenormal[i])], ["edge_length",norm(bot_edge)]]),
|
||||||
|
named_anchor(FACE(i),mean(face), sidenormal[i], _compute_spin(sidenormal[i],UP)),
|
||||||
|
named_anchor(str("top_corner",i),top[i], top_corner_dir, _compute_spin(top_corner_dir,UP)),
|
||||||
|
named_anchor(str("bot_corner",i),bottom[i], bot_corner_dir, _compute_spin(bot_corner_dir,UP))
|
||||||
|
],
|
||||||
|
named_anchor("top", mean(top), topnormal, _compute_spin(topnormal, approx(v_abs(topnormal),UP)?BACK:UP)),
|
||||||
|
named_anchor("bot", mean(bottom), botnormal, _compute_spin(botnormal, approx(v_abs(botnormal),UP)?BACK:UP)),
|
||||||
|
],
|
||||||
|
override = len(top)!=4 ? undef
|
||||||
|
:
|
||||||
|
let(
|
||||||
|
stddir = [RIGHT,FWD,LEFT,BACK],
|
||||||
|
getanch = function(name) search([name], anchors, num_returns_per_match=1)[0],
|
||||||
|
dir_angle = [for(i=[0:3]) vector_angle(anchors[6*i+3][2],RIGHT)],
|
||||||
|
ofs = search([min(dir_angle)], dir_angle, num_returns_per_match=1)[0]
|
||||||
|
)
|
||||||
|
[
|
||||||
|
[UP, select(anchors[24],1,3)],
|
||||||
|
[DOWN, select(anchors[25],1,3)],
|
||||||
|
for(i=[0:3])
|
||||||
|
let(
|
||||||
|
edgeanch=anchors[((i+ofs)%4)*6],
|
||||||
|
upedge=anchors[((i+ofs)%4)*6+1],
|
||||||
|
downedge=anchors[((i+ofs)%4)*6+2],
|
||||||
|
faceanch=anchors[((i+ofs)%4)*6+3],
|
||||||
|
upcorner=anchors[((i+ofs)%4)*6+4],
|
||||||
|
downcorner=anchors[((i+ofs)%4)*6+5]
|
||||||
|
)
|
||||||
|
each [
|
||||||
|
[stddir[i],select(faceanch,1,3)],
|
||||||
|
[stddir[i]+select(stddir,i-1), select(edgeanch,1,3)],
|
||||||
|
[stddir[i]+UP, select(upedge,1,3)],
|
||||||
|
[stddir[i]+DOWN, select(downedge,1,3)],
|
||||||
|
[stddir[i]+select(stddir,i-1)+UP, select(upcorner,1,3)],
|
||||||
|
[stddir[i]+select(stddir,i-1)+DOWN, select(downcorner,1,3)],
|
||||||
|
]
|
||||||
|
]
|
||||||
)
|
)
|
||||||
debug ? [concat(top_patch, bot_patch), vnf] : vnf;
|
!debug && !_full_info ? vnf
|
||||||
|
: _full_info ? [concat(top_patch, bot_patch), vnf, anchors, override]
|
||||||
|
: [concat(top_patch, bot_patch), vnf];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
139
shapes3d.scad
139
shapes3d.scad
|
@ -599,8 +599,8 @@ function cuboid(
|
||||||
// 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 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
|
// The anchors on the top and bottom edges also have anchors that point clockwise as viewed from outside the shapep.
|
||||||
// have spin with positive Z component, pointing along the edge where the anchor is located.
|
// 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.
|
||||||
|
@ -835,7 +835,7 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) =
|
||||||
// Synopsis: Creates a regular prism with roundovers and chamfering
|
// Synopsis: Creates a regular prism with roundovers and chamfering
|
||||||
// SynTags: Geom, VNF
|
// SynTags: Geom, VNF
|
||||||
// Topics: Textures, Rounding, Chamfers
|
// Topics: Textures, Rounding, Chamfers
|
||||||
// See Also: cyl(), rounded_prism(), texture(), linear_sweep()
|
// See Also: cyl(), rounded_prism(), texture(), linear_sweep(), EDGE(), FACE()
|
||||||
// Usage: Normal prisms
|
// Usage: Normal prisms
|
||||||
// regular_prism(n, h|l=|height=|length=, r, [center=], [realign=]) [ATTACHMENTS];
|
// 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=, d=|id=|od=|ir=|or=|side=, ...) [ATTACHMENTS];
|
||||||
|
@ -863,22 +863,24 @@ function octahedron(size=1, anchor=CENTER, spin=0, orient=UP) =
|
||||||
// .
|
// .
|
||||||
// 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
|
// 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.
|
// 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.
|
// Additional named 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.
|
// You can use `EDGE(i)`, `EDGE(TOP,i)` and `EDGE(BOT,i)` as a shorthand for accessing the named edge anchors, and `FACE(i)` for the face anchors.
|
||||||
// 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
|
// When you use `shift`, which moves the top face of the prism, the spin for the side face and edges anchors will align
|
||||||
// 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
|
// the child with the edge or face direction. The "edge0" anchor identifies an edge located along the X+ axis, and then edges
|
||||||
// not split the edge/corner angle like the standard anchors.
|
// are labeled counting up in the clockwise direction. Similarly "face0" is the face immediately clockwise from "edge0", and face
|
||||||
|
// labeling proceeds clockwise. The top and bottom edge anchors label edges directly above and below the face with the same label.
|
||||||
|
// If you set `realign=true` then "face0" is oriented in the X+ direction.
|
||||||
// .
|
// .
|
||||||
// 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
|
// 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,
|
// 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.
|
// anchors are located on the true object instead of the ideal cylinder and you can anchor to the edges and faces.
|
||||||
// Named Anchors:
|
// Named Anchors:
|
||||||
// "edge0", "edge1", etc. = Center of each side edge, spin pointing up along the edge
|
// "edge0", "edge1", etc. = Center of each side edge, spin pointing up along the edge. Can access with EDGE(i)
|
||||||
// "face0", "face1", etc. = Center of each side face, spin pointing up
|
// "face0", "face1", etc. = Center of each side face, spin pointing up. Can access with FACE(i)
|
||||||
// "topedge0", "topedge1", etc = Center of each top edge, pointing in direction of associated side face, spin up
|
// "top_edge0", "top_edge1", etc = Center of each top edge, spin pointing clockwise (from top). Can access with EDGE(TOP,i)
|
||||||
// "botedge0", "botedge1", etc = Center of each bottom edge, pointing in direction of associated side face, spin up
|
// "bot_edge0", "bot_edge1", etc = Center of each bottom edge, spin pointing clockwise (from bottom). Can access with EDGE(BOT,i)
|
||||||
// "topcorner0", "topcorner1", etc = Top corner, pointing in direction of associated edge anchor, spin up along associated edge
|
// "top_corner0", "top_corner1", 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
|
// "bot_corner0", "bot_corner1", etc = Bottom corner, pointing in direction of associated edge anchor, spin up along associated edge
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l / h / length / height = Length of prism
|
// l / h / length / height = Length of prism
|
||||||
// r = Outer radius of prism.
|
// r = Outer radius of prism.
|
||||||
|
@ -1131,33 +1133,18 @@ function regular_prism(n,
|
||||||
ovnf = apply(skmat, vnf),
|
ovnf = apply(skmat, vnf),
|
||||||
edge_face = [ [r2-r1,0,height],[(r2-r1)/sc,0,height]], // regular edge, then face edge, in xz plane
|
edge_face = [ [r2-r1,0,height],[(r2-r1)/sc,0,height]], // regular edge, then face edge, in xz plane
|
||||||
names = ["edge","face"],
|
names = ["edge","face"],
|
||||||
anchors = approx(shift,[0,0]) ?
|
anchors = let(
|
||||||
[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 = [
|
faces = [
|
||||||
for(i=[0:n-1])
|
for(i=[0:n-1])
|
||||||
let(
|
let(
|
||||||
M1 = skmat*zrot(-i*360/n),
|
M1 = skmat*zrot(-i*360/n), // map to point i
|
||||||
M2 = skmat*zrot(-(i+1)*360/n),
|
M2 = skmat*zrot(-(i+1)*360/n), // map to point i+1
|
||||||
edge1 = apply(M1,[[r2,0,height/2], [r1,0,-height/2]]),
|
edge1 = apply(M1,[[r2,0,height/2], [r1,0,-height/2]]), // "vertical" edge at i
|
||||||
edge2 = apply(M2,[[r2,0,height/2], [r1,0,-height/2]]),
|
edge2 = apply(M2,[[r2,0,height/2], [r1,0,-height/2]]), // "vertical" edge at i+1
|
||||||
face_edge = (edge1+edge2)/2,
|
face_edge = (edge1+edge2)/2, // "vertical" edge across side face between i and i+1
|
||||||
facenormal = unit(cross(edge1[0]-edge1[1], edge2[1]-edge1[0]))
|
facenormal = unit(cross(edge1[0]-edge1[1], edge2[1]-edge1[0]))
|
||||||
)
|
) // [normal to face, edge through face center vector, actual edge vector, top edge vector]
|
||||||
[facenormal,face_edge[0]-face_edge[1],edge1[0]-edge1[1]] // [normal to face, edge through face center, actual edge]
|
[facenormal,face_edge[0]-face_edge[1],edge1[0]-edge1[1],edge2[0]-edge1[0]]
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
[for(i=[0:n-1])
|
[for(i=[0:n-1])
|
||||||
|
@ -1165,20 +1152,34 @@ function regular_prism(n,
|
||||||
Mface = skmat*zrot(-(i+1/2)*360/n),
|
Mface = skmat*zrot(-(i+1/2)*360/n),
|
||||||
faceedge = faces[i][1],
|
faceedge = faces[i][1],
|
||||||
facenormal = faces[i][0],
|
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, faceedge), // spin along centerline of face instead of pointing up---seems to be wrong choice
|
||||||
facespin = _compute_spin(facenormal, UP),
|
facespin = _compute_spin(facenormal, UP),
|
||||||
edgenormal = unit(vector_bisect(facenormal,select(faces,i-1)[0])),
|
edgenormal = unit(vector_bisect(facenormal,select(faces,i-1)[0])),
|
||||||
Medge = skmat*zrot(-i*360/n),
|
Medge = skmat*zrot(-i*360/n),
|
||||||
edge = faces[i][2],
|
edge = faces[i][2],
|
||||||
edgespin = _compute_spin(edgenormal, edge)
|
edgespin = _compute_spin(edgenormal, edge),
|
||||||
|
topedge = unit(faces[i][3]),
|
||||||
|
topnormal = unit(facenormal+UP),
|
||||||
|
botnormal = unit(facenormal+DOWN),
|
||||||
|
topedgespin = _compute_spin(topnormal, topedge),
|
||||||
|
botedgespin = _compute_spin(botnormal, -topedge),
|
||||||
|
topedgeangle = 180-vector_angle(UP,facenormal),
|
||||||
|
sideedgeangle = 180-vector_angle(facenormal, select(faces,i-1)[0]),
|
||||||
|
edgelen = norm(select(faces,i)[2])
|
||||||
)
|
)
|
||||||
each [
|
each [
|
||||||
named_anchor(str("face",i), apply(Mface,[(r1+r2)/2/sc,0,0]), facenormal, facespin),
|
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("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),
|
info=[["edge_angle",sideedgeangle], ["edge_length",edgelen]]),
|
||||||
named_anchor(str("top_corner",i), apply(Medge,[r2,0,height/2]), edgenormal, edgespin),
|
named_anchor(str("top_edge",i), apply(Mface,[r2/sc,0,height/2]), topnormal, topedgespin,
|
||||||
named_anchor(str("bot_edge",i), apply(Mface,[r1/sc,0,-height/2]), facenormal, facespin),
|
info=[["edge_angle",topedgeangle],["edge_length",2*sin(180/n)*r2]]),
|
||||||
named_anchor(str("bot_corner",i), apply(Medge,[r1,0,-height/2]), edgenormal, edgespin)
|
named_anchor(str("bot_edge",i), apply(Mface,[r1/sc,0,-height/2]), botnormal, botedgespin,
|
||||||
|
info=[["edge_angle",180-topedgeangle],["edge_length",2*sin(180/n)*r1]]),
|
||||||
|
named_anchor(str("top_corner",i), apply(Medge,[r2,0,height/2]), unit(edgenormal+UP),
|
||||||
|
_compute_spin(unit(edgenormal+UP),edge)),
|
||||||
|
named_anchor(str("bot_corner",i), apply(Medge,[r1,0,-height/2]), unit(edgenormal+DOWN),
|
||||||
|
_compute_spin(unit(edgenormal+DOWN),edge))
|
||||||
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
override = approx(shift,[0,0]) ? undef : [[UP, [point3d(shift,height/2), UP]]],
|
override = approx(shift,[0,0]) ? undef : [[UP, [point3d(shift,height/2), UP]]],
|
||||||
|
@ -3666,20 +3667,34 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
|
||||||
// Description:
|
// Description:
|
||||||
// Creates a shape that can be unioned into a concave joint between two faces, to fillet them.
|
// Creates a shape that can be unioned into a concave joint between two faces, to fillet them.
|
||||||
// Note that this module is the same as {{rounding_edge_mask()}}, except that it does not
|
// Note that this module is the same as {{rounding_edge_mask()}}, except that it does not
|
||||||
// apply the default "remove" tag.
|
// apply the default "remove" tag and has a different default angle.
|
||||||
//
|
// It can be convenient to {{attach()}} the fillet to the edge of a parent object.
|
||||||
|
// Many objects propagate the $edge_angle and $edge_length which are used as defaults for the fillet.
|
||||||
|
// If you attach the fillet to the edge, it will be hovering in space and you need to apply {{yrot()}}
|
||||||
|
// to place it on the parent object, generally either 90 degrees or -90 degrees dependong on which
|
||||||
|
// face you want the fillet.
|
||||||
// Usage:
|
// Usage:
|
||||||
// fillet(l|h=|length=|height=, r|d=, [ang=], [excess=]) [ATTACHMENTS];
|
// fillet(l|h=|length=|height=, r|d=, [ang=], [excess=], [rounding=|chamfer=]) [ATTACHMENTS];
|
||||||
// fillet(l|h=|length=|height=, r1=|d1=, r2=|d2=, [ang=], [excess=]) [ATTACHMENTS];
|
// fillet(l|h=|length=|height=, r1=|d1=, r2=|d2=, [ang=], [excess=], [rounding=|chamfer=]) [ATTACHMENTS];
|
||||||
//
|
//
|
||||||
// Arguments:
|
// Arguments:
|
||||||
// l / length / h / height = Length of edge to fillet.
|
// l/h/length/height = Length of mask. Default: $edge_length if defined
|
||||||
// r = Radius of fillet.
|
// r = Radius of the rounding.
|
||||||
// ang = Angle between faces to fillet.
|
// ang = Angle between faces for rounding. Default: 180-$edge_angle if defined, otherwise 90
|
||||||
// excess = Overlap size for unioning with faces.
|
|
||||||
// ---
|
// ---
|
||||||
// d = Diameter of fillet.
|
// r1 = Bottom radius of fillet.
|
||||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `FRONT+LEFT`
|
// r2 = Top radius of fillet.
|
||||||
|
// d = Diameter of the fillet.
|
||||||
|
// d1 = Bottom diameter of fillet.
|
||||||
|
// d2 = Top diameter of fillet.
|
||||||
|
// excess = Extra size for the fillet. Defaults: .1
|
||||||
|
// rounding = Radius of roundong along ends. Default: 0
|
||||||
|
// rounding1 = Radius of rounding along bottom end
|
||||||
|
// rounding2 = Radius of rounding along top end
|
||||||
|
// chamfer = Chamfer size of end chamfers. Default: 0
|
||||||
|
// chamfer1 = Chamfer size of chamfer at bottom end
|
||||||
|
// chamfer2 = Chamfer size of chamfer at top end
|
||||||
|
// 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`
|
||||||
//
|
//
|
||||||
|
@ -3712,7 +3727,15 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
|
||||||
// cuboid(50){
|
// cuboid(50){
|
||||||
// align(TOP,RIGHT,inset=10) fillet(l=50,r=10,orient=FWD);
|
// align(TOP,RIGHT,inset=10) fillet(l=50,r=10,orient=FWD);
|
||||||
// align(TOP,RIGHT,inset=20) cuboid([4,50,20],anchor=BOT);
|
// align(TOP,RIGHT,inset=20) cuboid([4,50,20],anchor=BOT);
|
||||||
// }
|
// }
|
||||||
|
// Example(3D,VPT=[3.03052,-2.34905,8.07573],VPR=[70.4,0,326.2],VPD=82.6686): Automatic positioning of the fillet at the odd angle of this shifted prismoid is simple using {{attach()}} with the inherited $edge_angle.
|
||||||
|
// $fn=64;
|
||||||
|
// prismoid([20,15],[12,17], h=10, shift=[3,5]){
|
||||||
|
// attach(TOP+RIGHT,FWD+LEFT,inside=false)
|
||||||
|
// yrot(90)fillet(r=4);
|
||||||
|
// attach(RIGHT,BOT)
|
||||||
|
// cuboid([22,22,2]);
|
||||||
|
// }
|
||||||
|
|
||||||
module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, length, h, height, anchor=CENTER, spin=0, orient=UP)
|
module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, length, h, height, anchor=CENTER, spin=0, orient=UP)
|
||||||
{
|
{
|
||||||
|
@ -3722,9 +3745,13 @@ module interior_fillet(l=1.0, r, ang=90, overlap=0.01, d, length, h, height, anc
|
||||||
|
|
||||||
|
|
||||||
function fillet(l, r, ang, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("fillet");
|
function fillet(l, r, ang, r1, r2, d, d1, d2, excess=0.1, anchor=CENTER, spin=0, orient=UP, h,height,length) = no_function("fillet");
|
||||||
module fillet(l, r, ang=90, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor=CENTER, spin=0, orient=UP)
|
module fillet(l, r, ang, r1, r2, excess=0.01, d1, d2,d,length, h, height, anchor=CENTER, spin=0, orient=UP,
|
||||||
|
rounding,rounding1,rounding2,chamfer,chamfer1,chamfer2)
|
||||||
{
|
{
|
||||||
|
ang = first_defined([ang, u_add(u_mul($edge_angle,-1),180), 90]);
|
||||||
|
//echo(ang,180-$edge_angle);
|
||||||
rounding_edge_mask(l=l, r1=r1, r2=r2, ang=ang, excess=excess, d1=d1, d2=d2,d=d,r=r,length=length, h=h, height=height,
|
rounding_edge_mask(l=l, r1=r1, r2=r2, ang=ang, excess=excess, d1=d1, d2=d2,d=d,r=r,length=length, h=h, height=height,
|
||||||
|
chamfer1=chamfer1, chamfer2=chamfer2, chamfer=chamfer, rounding1=rounding1, rounding2=rounding2, rounding=rounding,
|
||||||
anchor=anchor, spin=spin, orient=orient, _remove_tag=false)
|
anchor=anchor, spin=spin, orient=orient, _remove_tag=false)
|
||||||
children();
|
children();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue