Compare commits

...

18 commits

Author SHA1 Message Date
adrianVmariano
b93456cf67
Merge c88319ca8f into d8214cc0e1 2024-09-28 21:46:18 +00:00
Adrian Mariano
c88319ca8f add $edge_angle and $edge_length 2024-09-28 17:45:52 -04:00
Adrian Mariano
55aafee003 attachments fixes/tweaks 2024-09-28 14:46:31 -04:00
Revar Desmera
d8214cc0e1
Merge pull request #1477 from adrianVmariano/master
rounded_prism fixes & attachment updates
2024-09-26 18:01:06 -07:00
Revar Desmera
456fcd8d8a
Merge pull request #1475 from adrianVmariano/master
Fox default font
2024-09-22 20:09:39 -07:00
Revar Desmera
8a930d5495
Merge pull request #1472 from adrianVmariano/master
offset() default fix & planetary gears docfix
2024-09-19 23:29:30 -07:00
Revar Desmera
eda0cd75b5
Merge pull request #1469 from adrianVmariano/master
regular_prism(), prismoid and wedge anchors
2024-09-07 22:25:56 -07:00
Revar Desmera
53af9121e7
Merge pull request #1464 from adrianVmariano/master
doc fix & screws fix
2024-08-11 22:48:27 -07:00
Revar Desmera
736fad321b
Merge pull request #1459 from adrianVmariano/master 2024-07-27 14:01:30 -07:00
Revar Desmera
cc08eb3323
Merge pull request #1458 from adrianVmariano/master
Fix new examples
2024-07-27 02:21:31 -07:00
Revar Desmera
78ea8e4770
Merge pull request #1457 from adrianVmariano/master
vnf_sheet & bezier_sheet
2024-07-25 23:01:31 -07:00
Revar Desmera
8383d360cc
Merge pull request #1454 from adrianVmariano/master
various fixes
2024-07-19 21:35:00 -07:00
Revar Desmera
9145c0961f
Merge pull request #1450 from BelfrySCAD/revarbat_dev
Remove redundant collinear points from bezpath_curve() output.
2024-07-07 00:34:15 -07:00
Revar Desmera
ebb98b47d2 Remove redundant collinear points from bezpath_curve() output. 2024-07-07 00:11:23 -07:00
Revar Desmera
8ea8ebf341
Merge pull request #1447 from adrianVmariano/master
su/product optimization
2024-06-29 15:41:11 -07:00
Revar Desmera
17e307fdb4
Merge pull request #1446 from adrianVmariano/master
projection doc fix
2024-06-22 20:58:13 -07:00
Revar Desmera
67f0004773
Merge pull request #1445 from adrianVmariano/master
add projection()
2024-06-22 18:10:12 -07:00
Revar Desmera
76d09271c1
Merge pull request #1441 from adrianVmariano/master
spiral sweep bugfix
2024-06-12 22:57:08 -07:00
3 changed files with 144 additions and 26 deletions

View file

@ -12,6 +12,7 @@
// FileFootnotes: STD=Included in std.scad // FileFootnotes: STD=Included in std.scad
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
include<structs.scad>
// Default values for attachment code. // Default values for attachment code.
$tags=undef; // for backward compatibility $tags=undef; // for backward compatibility
@ -778,7 +779,12 @@ function _make_anchor_legal(anchor,geom) =
// Note that spin is not permitted for // Note that spin is not permitted for
// 2D objects because it would change the child orientation so that the anchors are no longer parallel. // 2D objects because it would change the child orientation so that the anchors are no longer parallel.
// When you use `align=` you can also adjust the position using `inset=`, which shifts the child // When you use `align=` you can also adjust the position using `inset=`, which shifts the child
// away from the edge or corner it is aligned to. // away from the edge or corner it is aligned to.
// .
// Note that the concept of alignment doesn't always make sense for objects without corners, such as spheres or cylinders.
// In same cases the alignments using such children will be odd because the alignment computation is trying to
// place a non-existent corner somewhere. Because attach() doesn't have in formation about the child when
// it runs it cannot handle curved shapes differently from cubes, so this behavior cannot be changed.
// . // .
// If you give `inside=true` then the anchor arrows are lined up so they are pointing the same direction and // If you give `inside=true` then the anchor arrows are lined up so they are pointing the same direction and
// the child object will be located inside the parent. In this case a default "remove" tag is applied to // the child object will be located inside the parent. In this case a default "remove" tag is applied to
@ -831,6 +837,8 @@ function _make_anchor_legal(anchor,geom) =
// `$attach_anchor` for each anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor. // `$attach_anchor` for each anchor given, this is set to the `[ANCHOR, POSITION, ORIENT, SPIN]` information for that anchor.
// if inside is true then set default tag to "remove" // if inside is true then set default tag to "remove"
// `$attach_to` is set to the value of the `child` argument, if given. Otherwise, `undef` // `$attach_to` is set to the value of the `child` argument, if given. Otherwise, `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: Cylinder placed on top of cube: // Example: Cylinder placed on top of cube:
// cuboid(50) // cuboid(50)
// attach(TOP,BOT) cylinder(d1=30,d2=15,h=25); // attach(TOP,BOT) cylinder(d1=30,d2=15,h=25);
@ -881,6 +889,29 @@ function _make_anchor_legal(anchor,geom) =
// attach(RIGHT+FRONT, TOP, inside=true) cuboid([10,3,5]); // attach(RIGHT+FRONT, TOP, inside=true) cuboid([10,3,5]);
// attach(RIGHT+FRONT, TOP, inside=true, align=TOP,shiftout=.01) cuboid([5,1,2]); // attach(RIGHT+FRONT, TOP, inside=true, align=TOP,shiftout=.01) cuboid([5,1,2]);
// } // }
// Example: Attaching a 3d edge mask. Simple 2d masks can be done using {{edge_profile()}} but this mask varies along its length.
// module wavy_edge(length,cycles, r, steps, n)
// {
// rmin = is_vector(r) ? r[0] : 0.01;
// rmax = is_vector(r) ? r[1] : r;
// layers = [for(z=[0:steps])
// let(
// r=rmin+(rmax-rmin)/2*(cos(z*360*cycles/steps)+1),ff=echo(r=r)
// )
// path3d( concat([[0,0]],
// arc(corner=path2d([BACK,CTR,RIGHT]), n=n, r=r)),
// z/steps*length-length/2)
// ];
// attachable([rmax,rmax,length]){
// skin(layers,slices=0);
// children();
// }
// }
// diff()
// cuboid(25)
// attach([TOP+RIGHT,TOP+LEFT,TOP+FWD, FWD+RIGHT], FWD+LEFT, inside=true, shiftout=.01)
// wavy_edge(length=25.1,cycles=1.4,r=4,steps=24,n=15);
module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, inside=false, from, to) module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0, inside=false, from, to)
{ {
@ -905,10 +936,11 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
assert(is_undef(align) || !is_string(child), "child is a named anchor. Named anchors are not supported with align="); assert(is_undef(align) || !is_string(child), "child is a named anchor. Named anchors are not supported with align=");
two_d = _attach_geom_2d($parent_geom); two_d = _attach_geom_2d($parent_geom);
basegeom = $parent_geom[0]=="conoid" ? attach_geom(r=2,h=2) basegeom = $parent_geom[0]=="conoid" ? attach_geom(r=2,h=2,axis=$parent_geom[5])
: $parent_geom[0]=="spheroid" ? echo("here")attach_geom(r=2) : $parent_geom[0]=="prismoid" ? attach_geom(size=[2,2,2],axis=$parent_geom[4])
: attach_geom(size=[2,2,2]); : attach_geom(size=[2,2,2]);
child_abstract_anchor = is_vector(child) && !two_d ? _find_anchor(child, basegeom) : undef; childgeom = attach_geom([2,2,2]);
child_abstract_anchor = is_vector(child) && !two_d ? _find_anchor(_make_anchor_legal(child,childgeom), childgeom) : undef;
overlap = (overlap!=undef)? overlap : $overlap; overlap = (overlap!=undef)? overlap : $overlap;
parent = first_defined([parent,from]); parent = first_defined([parent,from]);
anchors = is_vector(parent) || is_string(parent) ? [parent] : parent; anchors = is_vector(parent) || is_string(parent) ? [parent] : parent;
@ -935,21 +967,49 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
: point3d(anchors[anch_ind]); : point3d(anchors[anch_ind]);
$anchor=anchor; $anchor=anchor;
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_length = len(anchor_data)==5 ? struct_val(anchor_data[4],"edge_length") : 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]
: let(spin_dir = rot(anchor_data[3],from=UP, to=-anchor_dir, p=BACK)) : let(spin_dir = rot(anchor_data[3],from=UP, to=-anchor_dir, p=BACK))
_compute_spin(anchor_dir,spin_dir); _compute_spin(anchor_dir,spin_dir);
parent_abstract_anchor = is_vector(anchor) && !two_d ? _find_anchor(anchor,basegeom) : undef; parent_abstract_anchor = is_vector(anchor) && !two_d ? _find_anchor(_make_anchor_legal(anchor,basegeom),basegeom) : undef;
for(align_ind = idx(align_list)){ for(align_ind = idx(align_list)){
align = is_undef(align_list[align_ind]) ? undef align = is_undef(align_list[align_ind]) ? undef
: assert(is_vector(align_list[align_ind],2) || is_vector(align_list[align_ind],3), "align direction must be a 2-vector or 3-vector") : assert(is_vector(align_list[align_ind],2) || is_vector(align_list[align_ind],3), "align direction must be a 2-vector or 3-vector")
two_d ? _force_anchor_2d(align_list[align_ind]) two_d ? _force_anchor_2d(align_list[align_ind])
: point3d(align_list[align_ind]); : point3d(align_list[align_ind]);
spin = is_num(spin) ? spin
: align==CENTER ? 0
: sum(v_abs(anchor))==1 ? // parent anchor is a face
let(
spindir = in_list(anchor,[TOP,BOT]) ? BACK : UP,
proj = project_plane(point4d(anchor),[spindir,align]),
ang = v_theta(proj[1])-v_theta(proj[0])
)
ang
: // parent anchor is not a face, so must be an edge (corners not allowed)
let(
nativeback = apply(rot(to=parent_abstract_anchor[2],from=UP)
*affine3d_zrot(parent_abstract_anchor[3]), BACK)
)
nativeback*align<0 ? -180:0;
$idx = align_ind+len(align_list)*anch_ind; $idx = align_ind+len(align_list)*anch_ind;
$align=align; $align=align;
goodcyl = $parent_geom[0] != "conoid" || is_undef(align) || align==CTR ? true
: let(
align=rot(from=$parent_geom[5],to=UP,p=align),
anchor=rot(from=$parent_geom[5],to=UP,p=anchor)
)
anchor==TOP || anchor==BOT || align==TOP || align==BOT;
badcorner = !in_list($parent_geom[0],["conoid","spheroid"]) && !is_undef(align) && align!=CTR && sum(v_abs(anchor))==3;
badsphere = $parent_geom[0]=="spheroid" && !is_undef(align) && align!=CTR;
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,")"))
assert(goodcyl, str("Cannot use align with an anchor on a curved edge or surface of a cylinder at parent anchor (",anchor,")"))
assert(!badcorner, str("Cannot use align at a corner anchor (",anchor,")"))
assert(!badsphere, "Cannot use align on spheres.");
// Now compute position on the parent (including alignment but not inset) where the child will be anchored // Now compute position on the parent (including alignment but not inset) where the child will be anchored
pos = is_undef(align) ? anchor_data[1] : _find_anchor(anchor+align, $parent_geom)[1]; pos = is_undef(align) ? anchor_data[1] : _find_anchor(anchor+align, $parent_geom)[1];
$attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all? $attach_anchor = list_set(anchor_data, 1, pos); // Never used; For user informational use? Should this be set at all?
@ -963,7 +1023,7 @@ module attach(parent, child, overlap, align, spin=0, norot, inset=0, shiftout=0,
* affine3d_zrot(-parent_abstract_anchor[3]) * affine3d_zrot(-parent_abstract_anchor[3])
* rot(from=parent_abstract_anchor[2],to=UP) * rot(from=parent_abstract_anchor[2],to=UP)
* rot(v=anchor,-spin), * rot(v=anchor,-spin),
align); align);
// The $anchor_override anchor value forces an override of the *position* only for the anchor // The $anchor_override anchor value forces an override of the *position* only for the anchor
// used when attachable() places the child // used when attachable() places the child
$anchor_override = all_zero(child_adjustment)? inside?child:undef $anchor_override = all_zero(child_adjustment)? inside?child:undef
@ -3659,7 +3719,7 @@ 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 _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?
: let( : let(
@ -3687,8 +3747,10 @@ function _find_anchor(anchor, geom) =
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1") assert(all_comps_good, "All components of an anchor for a cuboid/prismoid must be -1, 0, or 1")
let( let(
size=geom[1], size2=geom[2], size=geom[1],
shift=point2d(geom[3]), axis=point3d(geom[4]), size2=geom[2],
shift=point2d(geom[3]),
axis=point3d(geom[4]),
override = geom[5](anchor) override = geom[5](anchor)
) )
let( let(
@ -3722,22 +3784,21 @@ function _find_anchor(anchor, geom) =
v3 = unit(line[1]-line[0],UP) * anch.z v3 = unit(line[1]-line[0],UP) * anch.z
) )
unit(v3,UP), unit(v3,UP),
edgeang = len(facevecs)==2 ? 180-vector_angle(facevecs[0], facevecs[1]) : undef,
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)),
// If the anchor is on a face or horizontal edge we take the oang value for spin // 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 // If the anchor is on a vertical or sloped edge or corner we want to align the spin to point upward along the edge
spin = anch.x!=0 && anch.y!=0 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=edge)) // Set "vertical" edge and corner anchors point along the edge // Set "vertical" edge and corner anchors point along the edge
: 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])) // Horizontal anchors point clockwise spin = anch.x!=0 && anch.y!=0 ? _compute_spin(final_dir, rot(from=UP, to=axis, p=edge))
// Horizontal anchors point clockwise
: 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
//spin = anchor.x!=0 && anchor.y!=0 ? _compute_spin(dir, edge) ) [anchor, final_pos, final_dir, default(override[2],spin), if (is_def(edgeang)) [["edge_angle",edgeang],["edge_length",norm(edge)]]]
// : anchor.z!=0 && (anchor.x!=0 || anchor.y!=0) ? _compute_spin(dir, _canonical_edge([anchor.y,anchor.x,0]))
// : 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")
let( let(
rr1=geom[1], rr1=geom[1],
rr2=geom[2], rr2=geom[2],
@ -3747,6 +3808,11 @@ function _find_anchor(anchor, geom) =
r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1), r1 = is_num(rr1)? [rr1,rr1] : point2d(rr1),
r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2), r2 = is_num(rr2)? [rr2,rr2] : point2d(rr2),
anch = rot(from=axis, to=UP, p=anchor), anch = rot(from=axis, to=UP, p=anchor),
axisname = axis==UP ? "Z"
: axis==RIGHT ? "X"
: axis==BACK ? "Y"
: "",
dummy = assert(anch.z == sign(anch.z), str("The ",axisname," component of an anchor for the cylinder/cone must be -1, 0, or 1")),
offset = rot(from=axis, to=UP, p=offset), offset = rot(from=axis, to=UP, p=offset),
u = (anch.z+1)/2, u = (anch.z+1)/2,
// Returns [point,tangent_dir] // Returns [point,tangent_dir]
@ -3908,8 +3974,8 @@ function _find_anchor(anchor, geom) =
edgedir = edge[1]-edge[0] edgedir = edge[1]-edge[0]
) )
_compute_spin(direction, flip*edgedir) _compute_spin(direction, flip*edgedir)
) )
[direction,spin] [direction,spin,[["edge_angle",ang],["edge_length",norm(edge[0]-edge[1])]]]
: let( // This section handles corner anchors, currently spins just point up : let( // This section handles corner anchors, currently spins just point up
vertices = vnf[0], vertices = vnf[0],
faces = vnf[1], faces = vnf[1],
@ -3928,7 +3994,7 @@ function _find_anchor(anchor, geom) =
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, default(override[0],pos),default(override[1],dir[0]),default(override[2],dir[1])] ) [anchor, default(override[0],pos),default(override[1],dir[0]),default(override[2],dir[1]),if (len(dir)==3) dir[2]]
) : type == "trapezoid"? ( //size, size2, shift, override ) : type == "trapezoid"? ( //size, size2, shift, override
let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[]) let(all_comps_good = [for (c=anchor) if (c!=sign(c)) 1]==[])
assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1") assert(all_comps_good, "All components of an anchor for a rectangle/trapezoid must be -1, 0, or 1")

View file

@ -466,12 +466,15 @@ function bezpath_curve(bezpath, splinesteps=16, N=3, endpoint=true) =
assert(len(bezpath)%N == 1, str("A degree ",N," bezier path should have a multiple of ",N," points in it, plus 1.")) assert(len(bezpath)%N == 1, str("A degree ",N," bezier path should have a multiple of ",N," points in it, plus 1."))
let( let(
segs = (len(bezpath)-1) / N, segs = (len(bezpath)-1) / N,
step = 1 / splinesteps step = 1 / splinesteps,
) [ path = [
for (seg = [0:1:segs-1]) for (seg = [0:1:segs-1])
each bezier_points(select(bezpath, seg*N, (seg+1)*N), [0:step:1-step/2]), each bezier_points(select(bezpath, seg*N, (seg+1)*N), [0:step:1-step/2]),
if (endpoint) last(bezpath) if (endpoint) last(bezpath)
]; ],
is_closed = approx(path[0], last(path)),
out = path_merge_collinear(path, closed=is_closed)
) out;
// Function: bezpath_closest_point() // Function: bezpath_closest_point()

View file

@ -681,6 +681,55 @@ prismoid(150,60,100)
show_anchors(s=45); show_anchors(s=45);
``` ```
Is is also possible to attach to edges and corners of the parent
object. The anchors for edges spin the child so its BACK direction is
aligned with the edge. If the edge belongs to a top or bottom
horizontal face, then the BACK directions will point clockwise around
the face, as seen from outside the shape. (This is the same direction
required for construction of valid faces in OpenSCAD.) Otherwise, the
BACK direction will point upwards.
Examine the red flags below, where only edge anchors appear on a
prismoid. The top face shows the red flags pointing clockwise.
The sloped side edges point along the edges, generally upward, and
the bottom ones appear to point counter-clockwise, but if we viewed
the shape from the bottom they would also appear clockwise.
```openscad-3D;Big
include <BOSL2/std.scad>
prismoid([100,175],[55,88], h=55)
for(i=[-1:1], j=[-1:1], k=[-1:1])
let(anchor=[i,j,k])
if (sum(v_abs(anchor))==2)
attach(anchor,BOT)anchor_arrow(40);
```
In this example cylinders sink half-way into the top edges of the
prismoid:
```openscad-3D;Big
include <BOSL2/std.scad>
$fn=16;
r=6;
prismoid([100,175],[55,88], h=55){
attach([TOP+RIGHT,TOP+LEFT],LEFT,overlap=r/2) cyl(r=r,l=88+2*r,rounding=r);
attach([TOP+FWD,TOP+BACK],LEFT,overlap=r/2) cyl(r=r,l=55+2*r, rounding=r);
}
```
This type of edge attachment is useful for attaching 3d edge masks to
edges:
```openscad-3D;Big
include <BOSL2/std.scad>
$fn=32;
diff()
cuboid(75)
attach([FRONT+LEFT, FRONT+RIGHT, BACK+LEFT, BACK+RIGHT],
FWD+LEFT,inside=true)
rounding_edge_mask(l=76, r1=8,r2=28);
```
## Parent-Child Anchor Attachment (Double Argument Attachment) ## Parent-Child Anchor Attachment (Double Argument Attachment)
The `attach()` module has two different modes of operation, The `attach()` module has two different modes of operation,