mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-01 09:49:45 +00:00
commit
d6f64d0786
4 changed files with 375 additions and 33 deletions
|
@ -236,7 +236,7 @@ function attach_geom(
|
|||
assert(is_vector(size,3))
|
||||
assert(is_vector(size2,2))
|
||||
assert(is_vector(shift,2))
|
||||
["cuboid", size, size2, shift, cp, offset, anchors]
|
||||
["cuboid", size, size2, shift, axis, cp, offset, anchors]
|
||||
)
|
||||
) : !is_undef(vnf)? (
|
||||
assert(is_vnf(vnf))
|
||||
|
@ -462,20 +462,24 @@ function find_anchor(anchor, geom) =
|
|||
)
|
||||
type == "cuboid"? ( //size, size2, shift
|
||||
let(
|
||||
size=geom[1], size2=geom[2], shift=point2d(geom[3]),
|
||||
size=geom[1], size2=geom[2],
|
||||
shift=point2d(geom[3]), axis=point3d(geom[4]),
|
||||
anch = rot(from=axis, to=UP, p=anchor),
|
||||
h = size.z,
|
||||
u = (anchor.z+1)/2,
|
||||
axy = point2d(anchor),
|
||||
u = (anch.z+1)/2,
|
||||
axy = point2d(anch),
|
||||
bot = point3d(vmul(point2d(size)/2,axy),-h/2),
|
||||
top = point3d(vmul(point2d(size2)/2,axy)+shift,h/2),
|
||||
pos = point3d(cp) + lerp(bot,top,u) + offset,
|
||||
sidevec = unit(rot(from=UP, to=top-bot, p=point3d(axy)),UP),
|
||||
vvec = anchor==CENTER? UP : unit([0,0,anchor.z],UP),
|
||||
vec = anchor==CENTER? UP :
|
||||
approx(axy,[0,0])? unit(anchor,UP) :
|
||||
approx(anchor.z,0)? sidevec :
|
||||
unit((sidevec+vvec)/2,UP)
|
||||
) [anchor, pos, vec, oang]
|
||||
vvec = anch==CENTER? UP : unit([0,0,anch.z],UP),
|
||||
vec = anch==CENTER? UP :
|
||||
approx(axy,[0,0])? unit(anch,UP) :
|
||||
approx(anch.z,0)? sidevec :
|
||||
unit((sidevec+vvec)/2,UP),
|
||||
pos2 = rot(from=UP, to=axis, p=pos),
|
||||
vec2 = rot(from=UP, to=axis, p=vec)
|
||||
) [anchor, pos2, vec2, oang]
|
||||
) : type == "cyl"? ( //r1, r2, l, shift
|
||||
let(
|
||||
rr1=geom[1], rr2=geom[2], l=geom[3],
|
||||
|
|
22
common.scad
22
common.scad
|
@ -468,16 +468,18 @@ function get_anchor(anchor,center,uncentered=BOT,dflt=CENTER) =
|
|||
// r = get_radius(r1=8, d=6, dflt=1); // Returns: 8
|
||||
function get_radius(r1, r2, r, d1, d2, d, dflt) =
|
||||
assert(num_defined([r1,d1,r2,d2])<2, "Conflicting or redundant radius/diameter arguments given.")
|
||||
!is_undef(r1) ? assert(is_finite(r1), "Invalid radius r1." ) r1
|
||||
: !is_undef(r2) ? assert(is_finite(r2), "Invalid radius r2." ) r2
|
||||
: !is_undef(d1) ? assert(is_finite(d1), "Invalid diameter d1." ) d1/2
|
||||
: !is_undef(d2) ? assert(is_finite(d2), "Invalid diameter d2." ) d2/2
|
||||
: !is_undef(r)
|
||||
? assert(is_undef(d), "Conflicting or redundant radius/diameter arguments given.")
|
||||
assert(is_finite(r) || is_vector(r,1) || is_vector(r,2), "Invalid radius r." )
|
||||
r
|
||||
: !is_undef(d) ? assert(is_finite(d) || is_vector(d,1) || is_vector(d,2), "Invalid diameter d." ) d/2
|
||||
: dflt;
|
||||
assert(num_defined([r,d])<2, "Conflicting or redundant radius/diameter arguments given.")
|
||||
let(
|
||||
rad = !is_undef(r1) ? r1
|
||||
: !is_undef(d1) ? d1/2
|
||||
: !is_undef(r2) ? r2
|
||||
: !is_undef(d2) ? d2/2
|
||||
: !is_undef(r) ? r
|
||||
: !is_undef(d) ? d/2
|
||||
: dflt
|
||||
)
|
||||
assert(is_undef(dflt) || is_finite(rad) || is_vector(rad), "Invalid radius." )
|
||||
rad;
|
||||
|
||||
|
||||
// Function: scalar_vec3()
|
||||
|
|
|
@ -219,23 +219,359 @@ cube(50, center=true)
|
|||
|
||||
|
||||
## Tagged Operations
|
||||
BOSL2 introduces the concept of tags. Tags are names that can be given to attachables, so that
|
||||
you can refer to them when performing `diff()`, `intersect()`, and `hulling()` operations.
|
||||
|
||||
### `diff(neg, <pos>, <keep>)`
|
||||
The `diff()` operator is used to difference away all shapes marked with the tag(s) given to
|
||||
`neg=`, from shapes marked with the tag(s) given to `pos=`. Anything marked with a tag given
|
||||
to `keep=` will be unioned onto the result. If no `pos=` argument is given, then everything
|
||||
marked with a tag given to `neg=` will be differenced from all shapes *not* marked with that
|
||||
tag.
|
||||
|
||||
For example, to difference away a child cylinder from the middle of a parent cube, you can
|
||||
do this:
|
||||
|
||||
```openscad
|
||||
diff("hole")
|
||||
cube(100, center=true)
|
||||
cylinder(h=101, d=50, center=true, $tags="hole");
|
||||
```
|
||||
|
||||
If you give both the `neg=` and `pos=` arguments to `diff()`, then the shapes marked by tags
|
||||
given to `neg=` will be differenced away from the shapes marked with tags given to `pos=`.
|
||||
Everything else will be unioned to the result.
|
||||
|
||||
```openscad
|
||||
diff("hole", "post")
|
||||
cube(100, center=true)
|
||||
attach([RIGHT,TOP]) {
|
||||
cylinder(d=95, h=5, $tags="post");
|
||||
cylinder(d=50, h=11, anchor=CTR, $tags="hole");
|
||||
}
|
||||
```
|
||||
|
||||
The `keep=` argument takes tags for shapes that you want to keep in the output.
|
||||
|
||||
```openscad
|
||||
diff("dish", keep="antenna")
|
||||
cube(100, center=true)
|
||||
attach([FRONT,TOP], overlap=33) {
|
||||
cylinder(h=33.1, d1=0, d2=95, $tags="dish");
|
||||
cylinder(h=33.1, d=10, $tags="antenna");
|
||||
}
|
||||
```
|
||||
|
||||
If you need to mark multiple children with a tag, you can use the `tags()` module.
|
||||
|
||||
```openscad
|
||||
diff("hole")
|
||||
cube(100, center=true)
|
||||
attach([FRONT,TOP], overlap=20)
|
||||
tags("hole") {
|
||||
cylinder(h=20.1, d1=0, d2=95);
|
||||
down(10) cylinder(h=30, d=30);
|
||||
}
|
||||
```
|
||||
|
||||
The parent object can be differenced away from other shapes. Tags are inherited by children,
|
||||
though, so you will need to set the tags of the children as well as the parent.
|
||||
|
||||
```openscad
|
||||
diff("hole")
|
||||
cube([20,11,45], center=true, $tags="hole")
|
||||
cube([40,10,90], center=true, $tags="body");
|
||||
```
|
||||
|
||||
### `intersect(a, <b>, <keep>)`
|
||||
|
||||
To perform an intersection of attachables, you can use the `intersect()` module. If given one
|
||||
argument to `a=`, the parent and all children *not* tagged with that will be intersected by
|
||||
everything that *is* tagged with it.
|
||||
|
||||
```openscad
|
||||
intersect("bounds")
|
||||
cube(100, center=true)
|
||||
cylinder(h=100, d1=120, d2=95, center=true, $fn=72, $tags="bounds");
|
||||
```
|
||||
|
||||
If given both the `a=` and `b=` arguments, then shapes marked with tags given to `a=` will be
|
||||
intersected with shapes marked with tags given to `b=`, then unioned with all other shapes.
|
||||
|
||||
```openscad
|
||||
intersect("pole", "cap")
|
||||
cube(100, center=true)
|
||||
attach([TOP,RIGHT]) {
|
||||
cube([40,40,80],center=true, $tags="pole");
|
||||
sphere(d=40*sqrt(2), $tags="cap");
|
||||
}
|
||||
```
|
||||
|
||||
If the `keep=` argument is given, anything marked with tags passed to it will be unioned with
|
||||
the result of the union:
|
||||
|
||||
```openscad
|
||||
intersect("bounds", keep="pole")
|
||||
cube(100, center=true) {
|
||||
cylinder(h=100, d1=120, d2=95, center=true, $fn=72, $tags="bounds");
|
||||
zrot(45) xcyl(h=140, d=20, $fn=36, $tags="pole");
|
||||
}
|
||||
```
|
||||
|
||||
### `hulling(a)`
|
||||
You can use the `hulling()` module to hull shapes marked with a given tag together, before
|
||||
unioning the result with every other shape.
|
||||
|
||||
```openscad
|
||||
hulling("hull")
|
||||
cube(50, center=true, $tags="hull") {
|
||||
cyl(h=100, d=20);
|
||||
xcyl(h=100, d=20, $tags="pole");
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Masking Children
|
||||
edge_mask()
|
||||
corner_mask()
|
||||
|
||||
face_profile()
|
||||
edge_profile()
|
||||
corner_profile()
|
||||
TBW
|
||||
|
||||
|
||||
## Coloring Attachables
|
||||
TBW
|
||||
|
||||
|
||||
## Making Attachables
|
||||
To make a shape attachable, you just need to wrap it with an `attachable()` module with a
|
||||
basic description of the shape's geometry. By default, the shape is expected to be centered
|
||||
at the origin. The `attachable()` module expects exactly two children. The first will be
|
||||
the shape to make attachable, and the second will be `children()`, literally.
|
||||
|
||||
### Prismoidal/Cuboidal Attachables
|
||||
To make a cuboidal or prismoidal shape attachable, you use the `size`, `size2`, and `offset`
|
||||
arguments of `attachable()`.
|
||||
|
||||
In the most basic form, where the shape in fully cuboid, with top and bottom of the same size,
|
||||
and directly over one another, you can just use `size=`.
|
||||
|
||||
```openscad
|
||||
module cubic_barbell(s=100, anchor=CENTER, spin=0, orient=UP) {
|
||||
attachable(anchor,spin,orient, size=[s*3,s,s]) {
|
||||
union() {
|
||||
xcopies(2*s) cube(s, center=true);
|
||||
xcyl(h=2*s, d=s/4);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
cubic_barbell(100);
|
||||
```
|
||||
|
||||
When the shape is prismoidal, where the top is a different size from the bottom, you can use
|
||||
the `size2=` argument as well.
|
||||
|
||||
```openscad
|
||||
module prismoidal(size=[100,100,100], scale=0.5, anchor=CENTER, spin=0, orient=UP) {
|
||||
attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale) {
|
||||
hull() {
|
||||
up(size.z/2-0.005)
|
||||
linear_extrude(height=0.01, center=true)
|
||||
square([size.x,size.y]*scale, center=true);
|
||||
down(size.z/2-0.005)
|
||||
linear_extrude(height=0.01, center=true)
|
||||
square([size.x,size.y], center=true);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
prismoidal([100,60,30], scale=0.5);
|
||||
```
|
||||
|
||||
When the top of the prismoid can be shifted away from directly above the bottom, you can use
|
||||
the `shift=` argument.
|
||||
|
||||
```openscad
|
||||
module prismoidal(size=[100,100,100], scale=0.5, shift=[0,0], anchor=CENTER, spin=0, orient=UP) {
|
||||
attachable(anchor,spin,orient, size=size, size2=[size.x, size.y]*scale, shift=shift) {
|
||||
hull() {
|
||||
translate([shift.x, shift.y, size.z/2-0.005])
|
||||
linear_extrude(height=0.01, center=true)
|
||||
square([size.x,size.y]*scale, center=true);
|
||||
down(size.z/2-0.005)
|
||||
linear_extrude(height=0.01, center=true)
|
||||
square([size.x,size.y], center=true);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
prismoidal([100,60,30], scale=0.5, shift=[-30,20]);
|
||||
```
|
||||
|
||||
In the case that the prismoid is not oriented vertically, you can use the `axis=` argument.
|
||||
|
||||
```openscad
|
||||
module yprismoidal(
|
||||
size=[100,100,100], scale=0.5, shift=[0,0],
|
||||
anchor=CENTER, spin=0, orient=UP
|
||||
) {
|
||||
attachable(
|
||||
anchor, spin, orient,
|
||||
size=size, size2=point2d(size)*scale,
|
||||
shift=shift, axis=BACK
|
||||
) {
|
||||
xrot(-90) hull() {
|
||||
translate([shift.x, shift.y, size.z/2-0.005])
|
||||
linear_extrude(height=0.01, center=true)
|
||||
square([size.x,size.y]*scale, center=true);
|
||||
down(size.z/2-0.005)
|
||||
linear_extrude(height=0.01, center=true)
|
||||
square([size.x,size.y], center=true);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
yprismoidal([100,60,30], scale=1.5, shift=[20,20]);
|
||||
```
|
||||
|
||||
|
||||
### Cylindrical Attachables
|
||||
To make a cylindrical shape attachable, you use the `l`, and `r`/`d`, args of `attachable()`.
|
||||
|
||||
```openscad
|
||||
module twistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
|
||||
r = get_radius(r=r,d=d,dflt=1);
|
||||
attachable(anchor,spin,orient, r=r, l=l) {
|
||||
linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
|
||||
star(n=20, r=r, ir=r*0.9);
|
||||
children();
|
||||
}
|
||||
}
|
||||
twistar(l=100, r=40);
|
||||
```
|
||||
|
||||
If the cylinder is elipsoidal in shape, you can pass the inequal X/Y sizes as a 2-item vector
|
||||
to the `r=` or `d=` argument.
|
||||
|
||||
```openscad
|
||||
module ovalstar(l,rx,ry, anchor=CENTER, spin=0, orient=UP) {
|
||||
attachable(anchor,spin,orient, r=[rx,ry], l=l) {
|
||||
linear_extrude(height=l, center=true, convexity=4)
|
||||
scale([1,ry/rx,1])
|
||||
star(n=20, r=rx, ir=rx*0.9);
|
||||
children();
|
||||
}
|
||||
}
|
||||
ovalstar(l=100, rx=50, ry=30);
|
||||
```
|
||||
|
||||
For cylindrical shapes that arent oriented vertically, use the `axis=` argument.
|
||||
|
||||
```openscad
|
||||
module ytwistar(l,r,d, anchor=CENTER, spin=0, orient=UP) {
|
||||
r = get_radius(r=r,d=d,dflt=1);
|
||||
attachable(anchor,spin,orient, r=r, l=l) {
|
||||
xrot(-90)
|
||||
linear_extrude(height=l, twist=90, slices=20, center=true, convexity=4)
|
||||
star(n=20, r=r, ir=r*0.9);
|
||||
children();
|
||||
}
|
||||
}
|
||||
ytwistar(l=100, r=40);
|
||||
```
|
||||
|
||||
### Conical Attachables
|
||||
To make a conical shape attachable, you use the `l`, `r1`/`d1`, and `r2`/`d2`, args of
|
||||
`attachable()`.
|
||||
|
||||
```openscad
|
||||
module twistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
|
||||
r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
|
||||
r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
|
||||
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
|
||||
linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
|
||||
star(n=20, r=r1, ir=r1*0.9);
|
||||
children();
|
||||
}
|
||||
}
|
||||
twistar(l=100, r1=40, r2=20);
|
||||
```
|
||||
|
||||
If the cone is elipsoidal in shape, you can pass the inequal X/Y sizes as a 2-item vectors
|
||||
to the `r1=`/`r2=` or `d1=`/`d2=` arguments.
|
||||
|
||||
```openscad
|
||||
module ovalish(l,rx1,ry1,rx2,ry2, anchor=CENTER, spin=0, orient=UP) {
|
||||
attachable(anchor,spin,orient, r1=[rx1,ry1], r2=[rx2,ry2], l=l) {
|
||||
hull() {
|
||||
up(l/2-0.005)
|
||||
linear_extrude(height=0.01, center=true)
|
||||
scale([1,ry2/rx2,1])
|
||||
oval([rx2,ry2]);
|
||||
down(l/2-0.005)
|
||||
linear_extrude(height=0.01, center=true)
|
||||
scale([1,ry1/rx1,1])
|
||||
oval([rx1,ry1]);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
ovalish(l=100, rx1=40, ry1=30, rx2=30, ry2=40);
|
||||
```
|
||||
|
||||
For conical shapes that are not oriented vertically, use the `axis=` argument.
|
||||
|
||||
```openscad
|
||||
module ytwistar(l, r,r1,r2, d,d1,d2, anchor=CENTER, spin=0, orient=UP) {
|
||||
r1 = get_radius(r1=r1,r=r,d1=d1,d=d,dflt=1);
|
||||
r2 = get_radius(r1=r2,r=r,d1=d2,d=d,dflt=1);
|
||||
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l, axis=BACK) {
|
||||
xrot(-90)
|
||||
linear_extrude(height=l, twist=90, scale=r2/r1, slices=20, center=true, convexity=4)
|
||||
star(n=20, r=r1, ir=r1*0.9);
|
||||
children();
|
||||
}
|
||||
}
|
||||
ytwistar(l=100, r1=40, r2=20);
|
||||
```
|
||||
|
||||
### Spherical Attachables
|
||||
To make a spherical shape attachable, you use the `r`/`d` args of `attachable()`.
|
||||
|
||||
```openscad
|
||||
module spikeball(r, d, anchor=CENTER, spin=0, orient=UP) {
|
||||
r = get_radius(r=r,d=d,dflt=1);
|
||||
attachable(anchor,spin,orient, r=r*1.1) {
|
||||
union() {
|
||||
ovoid_spread(r=r, n=512, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
|
||||
sphere(r=r);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
spikeball(r=50);
|
||||
```
|
||||
|
||||
If the shape is more of an ovoid, you can pass a 3-item vector of sizes to `r=` or `d=`.
|
||||
|
||||
```openscad
|
||||
module spikeball(r, d, scale, anchor=CENTER, spin=0, orient=UP) {
|
||||
r = get_radius(r=r,d=d,dflt=1);
|
||||
attachable(anchor,spin,orient, r=r*1.1*scale) {
|
||||
union() {
|
||||
ovoid_spread(r=r, n=512, scale=scale, cone_ang=180) cylinder(r1=r/10, r2=0, h=r/10);
|
||||
scale(scale) sphere(r=r);
|
||||
}
|
||||
children();
|
||||
}
|
||||
}
|
||||
spikeball(r=50, scale=[0.75,1,1.5]);
|
||||
```
|
||||
|
||||
### VNF Attachables
|
||||
If the shape just doesn't fit into any of the above categories, and you constructed it as a
|
||||
[VNF](vnf.scad), you can use the VNF itself to describe the geometry.
|
||||
TBW
|
||||
|
||||
|
||||
## Making Named Anchors
|
||||
TBW
|
||||
|
||||
|
||||
|
|
14
vnf.scad
14
vnf.scad
|
@ -837,7 +837,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("BAD_INDEX", [idx])
|
||||
],
|
||||
issues = concat(issues, bad_indices)
|
||||
) issues? issues :
|
||||
) bad_indices? issues :
|
||||
let(
|
||||
repeated_faces = [
|
||||
for (i=idx(dfaces), j=idx(dfaces))
|
||||
|
@ -854,7 +854,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("DUP_FACE", [for (i=sface1) varr[i]])
|
||||
],
|
||||
issues = concat(issues, repeated_faces)
|
||||
) issues? issues :
|
||||
) repeated_faces? issues :
|
||||
let(
|
||||
multconn_edges = unique([
|
||||
for (i = idx(uniq_edges))
|
||||
|
@ -862,7 +862,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("MULTCONN", [for (i=uniq_edges[i]) varr[i]])
|
||||
]),
|
||||
issues = concat(issues, multconn_edges)
|
||||
) issues? issues :
|
||||
) multconn_edges? issues :
|
||||
let(
|
||||
reversals = unique([
|
||||
for(i = idx(dfaces), j = idx(dfaces)) if(i != j)
|
||||
|
@ -873,7 +873,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("REVERSAL", [for (i=edge1) varr[i]])
|
||||
]),
|
||||
issues = concat(issues, reversals)
|
||||
) issues? issues :
|
||||
) reversals? issues :
|
||||
let(
|
||||
t_juncts = unique([
|
||||
for (v=idx(varr), edge=uniq_edges) let(
|
||||
|
@ -893,7 +893,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("T_JUNCTION", [b])
|
||||
]),
|
||||
issues = concat(issues, t_juncts)
|
||||
) issues? issues :
|
||||
) t_juncts? issues :
|
||||
let(
|
||||
isect_faces = !check_isects? [] : unique([
|
||||
for (i = [0:1:len(faces)-2]) let(
|
||||
|
@ -935,7 +935,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("FACE_ISECT", seg)
|
||||
]),
|
||||
issues = concat(issues, isect_faces)
|
||||
) issues? issues :
|
||||
) isect_faces? issues :
|
||||
let(
|
||||
hole_edges = unique([
|
||||
for (i=idx(uniq_edges))
|
||||
|
@ -945,7 +945,7 @@ function vnf_validate(vnf, show_warns=true, check_isects=false) =
|
|||
_vnf_validate_err("HOLE_EDGE", [for (i=uniq_edges[i]) varr[i]])
|
||||
]),
|
||||
issues = concat(issues, hole_edges)
|
||||
) issues? issues :
|
||||
) hole_edges? issues :
|
||||
let(
|
||||
nonplanars = unique([
|
||||
for (i = idx(faces)) let(
|
||||
|
|
Loading…
Reference in a new issue