Merge pull request #1367 from adrianVmariano/master

anchor fixes
This commit is contained in:
Revar Desmera 2024-02-06 10:11:40 -08:00 committed by GitHub
commit a9e87638b8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 168 additions and 23 deletions

View file

@ -3333,7 +3333,7 @@ function _find_anchor(anchor, geom) =
) [anchor, pos, vec, oang]
) : type == "vnf_isect"? ( //vnf
let( vnf=geom[1] )
approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] :
approx(anchor,CTR)? [anchor, cp, UP, 0] : // CENTER anchors anchor on cp, "origin" anchors on [0,0]
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor), 0] :
let(
eps = 1/2048,
@ -3383,7 +3383,7 @@ function _find_anchor(anchor, geom) =
[anchor, pos, n, oang]
) : type == "vnf_extent"? ( //vnf
let( vnf=geom[1] )
approx(anchor,CTR)? [anchor, [0,0,0], UP, 0] :
approx(anchor,CTR)? [anchor, cp, UP, 0] : // CENTER anchors anchor on cp, "origin" anchors on [0,0]
vnf==EMPTY_VNF? [anchor, [0,0,0], unit(anchor,UP), 0] :
let(
rpts = apply(rot(from=anchor, to=RIGHT) * move(point3d(-cp)), vnf[0]),
@ -3432,7 +3432,7 @@ function _find_anchor(anchor, geom) =
anchor = _force_anchor_2d(anchor),
rgn = force_region(move(-point2d(cp), p=geom[1]))
)
approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] :
approx(anchor,[0,0])? [anchor, cp, BACK, 0] : // CENTER anchors anchor on cp, "origin" anchors on [0,0]
let(
isects = [
for (path=rgn, t=triplet(path,true)) let(
@ -3456,7 +3456,7 @@ function _find_anchor(anchor, geom) =
) [anchor, pos, vec, 0]
) : type == "rgn_extent"? ( //region
let( anchor = _force_anchor_2d(anchor) )
approx(anchor,[0,0])? [anchor, [0,0,0], BACK, 0] :
approx(anchor,[0,0])? [anchor, cp, BACK, 0] : // CENTER anchors anchor on cp, "origin" anchors on [0,0]
let(
rgn = force_region(geom[1]),
rpts = rot(from=anchor, to=RIGHT, p=flatten(rgn)),

View file

@ -1365,8 +1365,13 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
// 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
// orient = Vector to rotate top towards after spin (module only)
// atype = Select "hull" or "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"
// Anchor Types:
// hull = Anchors to the convex hull 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_intersect = Anchors to the surface of the offset_sweep shape, including any end treatments.
// Example: Rounding a star shaped prism with postive radius values
// star = star(5, r=22, ir=13);
// rounded_star = round_corners(star, cut=flatten(repeat([.5,0],5)), $fn=24);
@ -1641,12 +1646,21 @@ module offset_sweep(path, height,
convexity=10,anchor="origin",cp="centroid",
spin=0, orient=UP, atype="hull")
{
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
assert(in_list(atype, ["intersect","hull","surf_hull","surf_intersect"]), "Anchor type must be \"hull\" or \"intersect\"");
vnf = offset_sweep(path=path, height=height, h=h, l=l, top=top, bottom=bottom, offset=offset, r=r, steps=steps,
quality=quality, check_valid=check_valid, extra=extra, cut=cut, chamfer_width=chamfer_width,
chamfer_height=chamfer_height, joint=joint, k=k, angle=angle);
vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype, cp=cp)
children();
if (in_list(atype,["hull","intersect"])){
h=first_defined([h,l,height]);
attachable(anchor,spin,orient,region=[path],h=h,extent=atype=="hull",cp=cp){
down(h/2)polyhedron(vnf[0],vnf[1],convexity=convexity);
children();
}
}
else
vnf_polyhedron(vnf,convexity=convexity,anchor=anchor, spin=spin, orient=orient, atype=atype=="surf_hull"?"hull":"intersect", cp=cp)
children();
}
@ -2012,6 +2026,11 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
// joint_top[1] is negative the shape will flare upward. At least one value must be non-negative. The same rules apply for joint_bot.
// The joint_sides parameter must be entirely nonnegative.
// .
// If the roundings at two adjacent side edges exceed the width of the face then the polyhedron will have self-intersecting faces, so it will be invalid.
// Similarly, if the roundings on the top or bottom edges cross the top face and intersect with each other, the resulting polyhedron is invalid:
// the top face after the roundings are applied must be a valid, non-degenerate polyhedron. There are two exceptions: it is permissible to
// 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.
// 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
// patches is a list of the bezier control points for the corner patches.
@ -2032,7 +2051,7 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
// k = continuous curvature rounding parameter for all edges. Default: 0.5
// k_top = continuous curvature rounding parameter for top
// k_bot = continuous curvature rounding parameter for bottom
// k_bot = continuous curvature rounding parameter for bottom
// k_sides = continuous curvature rounding parameter side edges, a number or vector.
// splinesteps = number of segments to use for curved patches. Default: 16
// debug = turn on debug mode which displays illegal polyhedra and shows the bezier corner patches for troubleshooting purposes. Default: False
// convexity = convexity parameter for polyhedron(), only for module version. Default: 10
@ -2196,23 +2215,44 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
bot_patch = _rp_compute_patches(bottom, top, joint_bot, joint_sides_vec, k_bot, k_sides_vec, concave),
vertbad = [for(i=[0:N-1])
if (norm(top[i]-top_patch[i][4][2]) + norm(bottom[i]-bot_patch[i][4][2]) > norm(bottom[i]-top[i])) i],
if (norm(top[i]-top_patch[i][4][2]) + norm(bottom[i]-bot_patch[i][4][2]) > EPSILON + norm(bottom[i]-top[i])) i],
// Check that the patch fits on the polygon edge
topbad = [for(i=[0:N-1])
if (norm(top_patch[i][2][4]-top_patch[i][2][2]) + norm(select(top_patch,i+1)[2][0]-select(top_patch,i+1)[2][2])
> norm(top_patch[i][2][2] - select(top_patch,i+1)[2][2])) [i,(i+1)%N]],
> EPSILON + norm(top_patch[i][2][2] - select(top_patch,i+1)[2][2])) [i,(i+1)%N]],
botbad = [for(i=[0:N-1])
if (norm(bot_patch[i][2][4]-bot_patch[i][2][2]) + norm(select(bot_patch,i+1)[2][0]-select(bot_patch,i+1)[2][2])
> norm(bot_patch[i][2][2] - select(bot_patch,i+1)[2][2])) [i,(i+1)%N]],
topinbad = [for(i=[0:N-1])
> EPSILON + norm(bot_patch[i][2][2] - select(bot_patch,i+1)[2][2])) [i,(i+1)%N]],
// If top/bot is L-shaped, check that arms of L from adjacent patches don't cross
topLbad = [for(i=[0:N-1])
if (norm(top_patch[i][0][2]-top_patch[i][0][4]) + norm(select(top_patch,i+1)[0][0]-select(top_patch,i+1)[0][2])
> norm(top_patch[i][0][2]-select(top_patch,i+1)[0][2])) [i,(i+1)%N]],
botinbad = [for(i=[0:N-1])
> EPSILON + norm(top_patch[i][0][2]-select(top_patch,i+1)[0][2])) [i,(i+1)%N]],
botLbad = [for(i=[0:N-1])
if (norm(bot_patch[i][0][2]-bot_patch[i][0][4]) + norm(select(bot_patch,i+1)[0][0]-select(bot_patch,i+1)[0][2])
> norm(bot_patch[i][0][2]-select(bot_patch,i+1)[0][2])) [i,(i+1)%N]]
> EPSILON + norm(bot_patch[i][0][2]-select(bot_patch,i+1)[0][2])) [i,(i+1)%N]],
// Check that the inner edges of the patch don't cross
topinbad = [for(i=[0:N-1])
let(
line1 = project_plane(top,[top_patch[i][2][0],top_patch[i][0][0]]),
line2 = project_plane(top,[select(top_patch,i+1)[2][4],select(top_patch,i+1)[0][4]])
)
if (!approx(line1[0],line1[1]) && !approx(line2[0],line2[1]) &&
line_intersection(line1,line2, SEGMENT,SEGMENT))
[i,(i+1)%N]],
botinbad = [for(i=[0:N-1])
let(
line1 = project_plane(bottom,[bot_patch[i][2][0],bot_patch[i][0][0]]),
line2 = project_plane(bottom,[select(bot_patch,i+1)[2][4],select(bot_patch,i+1)[0][4]])
)
if (!approx(line1[0],line1[1]) && !approx(line2[0],line2[1]) &&
line_intersection(line1,line2, SEGMENT,SEGMENT))
[i,(i+1)%N]]
)
assert(debug || vertbad==[], str("Top and bottom joint lengths are too large; they interfere with each other at vertices: ",vertbad))
assert(debug || topbad==[], str("Joint lengths too large at top edges: ",topbad))
assert(debug || botbad==[], str("Joint lengths too large at bottom edges: ",botbad))
assert(debug || topbad==[], str("Joint lengths too large at top or side edges: ",topbad))
assert(debug || botbad==[], str("Joint lengths too large at bottom or side edges: ",botbad))
assert(debug || topLbad==[], str("Joint length too large on the top face or side at edges: ", topLbad))
assert(debug || botLbad==[], str("Joint length too large on the bottom face or side at edges: ", botLbad))
assert(debug || topinbad==[], str("Joint length too large on the top face at edges: ", topinbad))
assert(debug || botinbad==[], str("Joint length too large on the bottom face at edges: ", botinbad))
let(
@ -2242,8 +2282,12 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
top_patch[i][4][4]
]
],
top_simple = is_path_simple(project_plane(faces[0],faces[0]),closed=true),
bot_simple = is_path_simple(project_plane(faces[1],faces[1]),closed=true),
top_collinear = is_collinear(faces[0]),
bot_collinear = is_collinear(faces[1]),
top_degen_ok = top_collinear && len(deduplicate(faces[0]))<=2,
bot_degen_ok = bot_collinear && len(deduplicate(faces[1]))<=2,
top_simple = top_degen_ok || (!top_collinear && is_path_simple(project_plane(faces[0],faces[0]),closed=true)),
bot_simple = bot_degen_ok || (!bot_collinear && is_path_simple(project_plane(faces[1],faces[1]),closed=true)),
// verify vertical edges
verify_vert =
[for(i=[0:N-1],j=[0:4])

View file

@ -3349,7 +3349,11 @@ module fillet(l=1.0, r, ang=90, overlap=0.01, d, length, h, height, anchor=CENTE
arc[0] + polar_to_xy(overlap, 90+ang),
each arc
];
attachable(anchor,spin,orient, size=[2*maxx,2*maxy,l]) {
override = function (anchor)
anchor.x>=0 && anchor.y>=0 ? undef
:
[[max(0,anchor.x)*maxx, max(0,anchor.y)*maxy, anchor.z*l/2]];
attachable(anchor,spin,orient, size=[2*maxx,2*maxy,l],override=override) {
if (l > 0) {
linear_extrude(height=l, convexity=4, center=true) {
polygon(path);

View file

@ -1262,7 +1262,7 @@ module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
children();
}
}
cubic_barbell(100) show_anchors(30);
cubic_barbell(100) show_anchors(60);
```
When the shape is prismoidal, where the top is a different size from the bottom, you can use
@ -1637,3 +1637,99 @@ sphere_pt = apply(
```
## Overriding Standard Anchors
Sometimes you may want to use the standard anchors but override some
of them. Returning to the square barebell example above, the anchors
at the right and left sides are on the cubes at each end, but the
anchors at x=0 are in floating in space. For prismoidal/cubic anchors
in 3D and trapezoidal/rectangular anchors in 2D we can override a single anchor by
specifying the override option and giving the anchor that is being
overridden, and then the replacement in the form
`[position, direction, spin]`. Most often you will only want to
override the position. If you omit the other list items then the
value drived from the standard anchor will be used. Below we override
position of the FWD anchor:
```
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
override = [
[FWD, [[0,-s/8,0]]]
];
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
union() {
xcopies(2*s) cube(s, center=true);
xcyl(h=2*s, d=s/4);
}
children();
}
}
cubic_barbell(100) show_anchors(60);
```
Note how the FWD anchor is now rooted on the cylindrical portion. If
you wanted to also change its direction and spin you could do it like
this:
```
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
override = [
[FWD, [[0,-s/8,0], FWD+LEFT, 225]]
];
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
union() {
xcopies(2*s) cube(s, center=true);
xcyl(h=2*s, d=s/4);
}
children();
}
}
cubic_barbell(100) show_anchors(60);
```
In the above example we give three values for the override. As
before, the first one places the anchor on the cylinder. We have
added the second entry which points the anchor off to the left.
The third entry gives a spin override, whose effect is shown by the
position of the red flag on the arrow. If you want to override all of
the x=0 anchors to be on the cylinder, with their standard directions,
you can do that by supplying a list:
```
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
override = [
for(j=[-1:1:1], k=[-1:1:1])
if ([j,k]!=[0,0]) [[0,j,k], [s/8*unit([0,j,k])]]
];
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
union() {
xcopies(2*s) cube(s, center=true);
xcyl(h=2*s, d=s/4);
}
children();
}
}
cubic_barbell(100) show_anchors(30);
```
Now all of the anchors in the middle are all rooted to the cylinder. Another
way to do the same thing is to use a function literal for override.
It will be called with the anchor as its argument and needs to return undef to just use
the default, or a `[position, direction, spin]` triple to override the
default. As before, you can omit values to keep their default.
Here is the same example using a function literal for the override:
```
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
override = function (anchor)
anchor.x!=0 || anchor==CTR ? undef // Keep these
: [s/8*unit(anchor)];
attachable(anchor,spin,orient, size=[s*3,s,s],override=override) {
union() {
xcopies(2*s) cube(s, center=true);
xcyl(h=2*s, d=s/4);
}
children();
}
}
cubic_barbell(100) show_anchors(30);
```

View file

@ -10,7 +10,8 @@
BOSL_VERSION = [2,0,710];
BOSL_VERSION = [2,0,714];
// Section: BOSL Library Version Functions