mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2024-12-29 00:09:41 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
6ee65beb1a
6 changed files with 170 additions and 55 deletions
28
.github/workflows/pr_merge.yml
vendored
28
.github/workflows/pr_merge.yml
vendored
|
@ -1,28 +0,0 @@
|
|||
name: VersionBump
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
VersionBumpJob:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Bump Version
|
||||
run: ./scripts/increment_version.sh
|
||||
|
||||
- name: Checkin
|
||||
uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
commit_message: Version Bump
|
||||
file_pattern: version.scad
|
||||
|
|
@ -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)),
|
||||
|
|
|
@ -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);
|
||||
|
@ -1378,15 +1383,15 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
|||
// Example: If the shape has sharp corners, make sure to set `$fn/$fs/$fa`. The corners of this triangle are not round, even though `offset="round"` (the default) because the number of segments is small.
|
||||
// triangle = [[0,0],[10,0],[5,10]];
|
||||
// offset_sweep(triangle, height=6, bottom = os_circle(r=-2),steps=4);
|
||||
// Example: Can improve the result by increasing $fn
|
||||
// Example: Can improve the result by increasing `$fn`
|
||||
// $fn=12;
|
||||
// triangle = [[0,0],[10,0],[5,10]];
|
||||
// offset_sweep(triangle, height=6, bottom = os_circle(r=-2),steps=4);
|
||||
// Example: Using $fa and $fs works too; it produces a different looking triangulation of the rounded corner
|
||||
// Example: Using `$fa` and `$fs` works too; it produces a different looking triangulation of the rounded corner
|
||||
// $fa=1;$fs=0.3;
|
||||
// triangle = [[0,0],[10,0],[5,10]];
|
||||
// offset_sweep(triangle, height=6, bottom = os_circle(r=-2),steps=4);
|
||||
// Example: Here is the star chamfered at the top with a teardrop rounding at the bottom. Check out the rounded corners on the chamfer. The large $fn value ensures a smooth curve on the concave corners of the chamfer. It has no effect anywhere else on the model. Observe how the rounded star points vanish at the bottom in the teardrop: the number of vertices does not remain constant from layer to layer.
|
||||
// Example: Here is the star chamfered at the top with a teardrop rounding at the bottom. Check out the rounded corners on the chamfer. The large `$fn` value ensures a smooth curve on the concave corners of the chamfer. It has no effect anywhere else on the model. Observe how the rounded star points vanish at the bottom in the teardrop: the number of vertices does not remain constant from layer to layer.
|
||||
// star = star(5, r=22, ir=13);
|
||||
// rounded_star = round_corners(star, cut=flatten(repeat([.5,0],5)), $fn=24);
|
||||
// offset_sweep(rounded_star, height=20, bottom=os_teardrop(r=4), top=os_chamfer(width=4),$fn=64);
|
||||
|
@ -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])
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
```
|
||||
|
|
|
@ -9,8 +9,7 @@
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,714];
|
||||
BOSL_VERSION = [2,0,715];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
|
Loading…
Reference in a new issue