mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-01-16 13:50:23 +01:00
commit
69e8cdc845
238
mutators.scad
238
mutators.scad
@ -19,6 +19,7 @@
|
||||
// Returns an axis-aligned cube shape that exactly contains all the 3D children given.
|
||||
// Arguments:
|
||||
// excess = The amount that the bounding box should be larger than needed to bound the children, in each axis.
|
||||
// planar = If true, creates a 2D bounding rectangle. Is false, creates a 3D bounding cube. Default: false
|
||||
// Example:
|
||||
// #bounding_box() {
|
||||
// translate([10,8,4]) cube(5);
|
||||
@ -26,32 +27,46 @@
|
||||
// }
|
||||
// translate([10,8,4]) cube(5);
|
||||
// translate([3,0,12]) cube(2);
|
||||
module bounding_box(excess=0) {
|
||||
module bounding_box(excess=0, planar=true) {
|
||||
xs = excess>.1? excess : 1;
|
||||
// a 3D approx. of the children projection on X axis
|
||||
module _xProjection()
|
||||
linear_extrude(xs, center=true)
|
||||
if (planar) {
|
||||
projection()
|
||||
rotate([90,0,0])
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
hull()
|
||||
children();
|
||||
hull()
|
||||
children();
|
||||
} else {
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
rotate([90,0,0])
|
||||
linear_extrude(xs, center=true)
|
||||
projection()
|
||||
hull()
|
||||
children();
|
||||
}
|
||||
|
||||
// a bounding box with an offset of 1 in all axis
|
||||
module _oversize_bbox() {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
rotate([0,-90,0]) _xProjection() rotate([0,90,0]) children(); // z axis
|
||||
if (planar) {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
}
|
||||
} else {
|
||||
minkowski() {
|
||||
_xProjection() children(); // x axis
|
||||
rotate(-90) _xProjection() rotate(90) children(); // y axis
|
||||
rotate([0,-90,0]) _xProjection() rotate([0,90,0]) children(); // z axis
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// offset children() (a cube) by -1 in all axis
|
||||
module _shrink_cube() {
|
||||
intersection() {
|
||||
translate((1-excess)*[ 1, 1, 1]) children();
|
||||
translate((1-excess)*[-1,-1,-1]) children();
|
||||
translate((1-excess)*[ 1, 1, planar?0: 1]) children();
|
||||
translate((1-excess)*[-1,-1, planar?0:-1]) children();
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,7 +224,6 @@ function left_half(_arg1=_undef, _arg2=_undef, _arg3=_undef,
|
||||
// right_half([s], [x]) ...
|
||||
// right_half(planar=true, [s], [x]) ...
|
||||
//
|
||||
//
|
||||
// Description:
|
||||
// Slices an object at a vertical Y-Z cut plane, and masks away everything that is left of it.
|
||||
//
|
||||
@ -516,31 +530,103 @@ module cylindrical_extrude(or, ir, od, id, size=1000, convexity=10, spin=0, orie
|
||||
// Section: Offset Mutators
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Module: round3d()
|
||||
// Module: minkowski_difference()
|
||||
// Usage:
|
||||
// round3d(r) ...
|
||||
// round3d(or) ...
|
||||
// round3d(ir) ...
|
||||
// round3d(or, ir) ...
|
||||
// minkowski_difference() { base_shape(); diff_shape(); ... }
|
||||
// Description:
|
||||
// Rounds arbitrary 3D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the
|
||||
// surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the
|
||||
// surface of its base shape.
|
||||
// Arguments:
|
||||
// planar = If true, performs minkowski difference in 2D. Default: false (3D)
|
||||
// Example:
|
||||
// minkowski_difference() {
|
||||
// union() {
|
||||
// cube([120,70,70], center=true);
|
||||
// cube([70,120,70], center=true);
|
||||
// cube([70,70,120], center=true);
|
||||
// }
|
||||
// sphere(r=10);
|
||||
// }
|
||||
module minkowski_difference(planar=false) {
|
||||
difference() {
|
||||
bounding_box(excess=0, planar=planar) children(0);
|
||||
render(convexity=20) {
|
||||
minkowski() {
|
||||
difference() {
|
||||
bounding_box(excess=1, planar=planar) children(0);
|
||||
children(0);
|
||||
}
|
||||
for (i=[1:1:$children-1]) children(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: round2d()
|
||||
// Usage:
|
||||
// round2d(r) ...
|
||||
// round2d(or) ...
|
||||
// round2d(ir) ...
|
||||
// round2d(or, ir) ...
|
||||
// Description:
|
||||
// Rounds arbitrary 2D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// rounds just concave corners. Giving just `or` rounds convex corners. Giving both `ir` and `or`
|
||||
// can let you round to different radii for concave and convex corners. The 3D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear. This is an *extremely*
|
||||
// slow operation. I cannot emphasize enough just how slow it is. It uses `minkowski()` multiple times.
|
||||
// Use this as a last resort. This is so slow that no example images will be rendered.
|
||||
// can let you round to different radii for concave and convex corners. The 2D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear.
|
||||
// Arguments:
|
||||
// r = Radius to round all concave and convex corners to.
|
||||
// or = Radius to round only outside (convex) corners to. Use instead of `r`.
|
||||
// ir = Radius to round only inside (concave) corners to. Use instead of `r`.
|
||||
module round3d(r, or, ir, size=100)
|
||||
// Examples(2D):
|
||||
// round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module round2d(r, or, ir)
|
||||
{
|
||||
or = get_radius(r1=or, r=r, dflt=0);
|
||||
ir = get_radius(r1=ir, r=r, dflt=0);
|
||||
offset3d(or, size=size)
|
||||
offset3d(-ir-or, size=size)
|
||||
offset3d(ir, size=size)
|
||||
offset(or) offset(-ir-or) offset(delta=ir,chamfer=true) children();
|
||||
}
|
||||
|
||||
|
||||
// Module: shell2d()
|
||||
// Usage:
|
||||
// shell2d(thickness, [or], [ir], [fill], [round])
|
||||
// Description:
|
||||
// Creates a hollow shell from 2D children, with optional rounding.
|
||||
// Arguments:
|
||||
// thickness = Thickness of the shell. Positive to expand outward, negative to shrink inward, or a two-element list to do both.
|
||||
// or = Radius to round corners on the outside of the shell. If given a list of 2 radii, [CONVEX,CONCAVE], specifies the radii for convex and concave corners separately. Default: 0 (no outside rounding)
|
||||
// ir = Radius to round corners on the inside of the shell. If given a list of 2 radii, [CONVEX,CONCAVE], specifies the radii for convex and concave corners separately. Default: 0 (no inside rounding)
|
||||
// Examples(2D):
|
||||
// shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,round=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,fill=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module shell2d(thickness, or=0, ir=0)
|
||||
{
|
||||
thickness = is_num(thickness)? (
|
||||
thickness<0? [thickness,0] : [0,thickness]
|
||||
) : (thickness[0]>thickness[1])? (
|
||||
[thickness[1],thickness[0]]
|
||||
) : thickness;
|
||||
orad = is_finite(or)? [or,or] : or;
|
||||
irad = is_finite(ir)? [ir,ir] : ir;
|
||||
difference() {
|
||||
round2d(or=orad[0],ir=orad[1])
|
||||
offset(delta=thickness[1])
|
||||
children();
|
||||
round2d(or=irad[1],ir=irad[0])
|
||||
offset(delta=thickness[0])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -583,101 +669,31 @@ module offset3d(r=1, size=100, convexity=10) {
|
||||
}
|
||||
|
||||
|
||||
// Module: round2d()
|
||||
// Module: round3d()
|
||||
// Usage:
|
||||
// round2d(r) ...
|
||||
// round2d(or) ...
|
||||
// round2d(ir) ...
|
||||
// round2d(or, ir) ...
|
||||
// round3d(r) ...
|
||||
// round3d(or) ...
|
||||
// round3d(ir) ...
|
||||
// round3d(or, ir) ...
|
||||
// Description:
|
||||
// Rounds arbitrary 2D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// Rounds arbitrary 3D objects. Giving `r` rounds all concave and convex corners. Giving just `ir`
|
||||
// rounds just concave corners. Giving just `or` rounds convex corners. Giving both `ir` and `or`
|
||||
// can let you round to different radii for concave and convex corners. The 2D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear.
|
||||
// can let you round to different radii for concave and convex corners. The 3D object must not have
|
||||
// any parts narrower than twice the `or` radius. Such parts will disappear. This is an *extremely*
|
||||
// slow operation. I cannot emphasize enough just how slow it is. It uses `minkowski()` multiple times.
|
||||
// Use this as a last resort. This is so slow that no example images will be rendered.
|
||||
// Arguments:
|
||||
// r = Radius to round all concave and convex corners to.
|
||||
// or = Radius to round only outside (convex) corners to. Use instead of `r`.
|
||||
// ir = Radius to round only inside (concave) corners to. Use instead of `r`.
|
||||
// Examples(2D):
|
||||
// round2d(r=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module round2d(r, or, ir)
|
||||
module round3d(r, or, ir, size=100)
|
||||
{
|
||||
or = get_radius(r1=or, r=r, dflt=0);
|
||||
ir = get_radius(r1=ir, r=r, dflt=0);
|
||||
offset(or) offset(-ir-or) offset(delta=ir,chamfer=true) children();
|
||||
}
|
||||
|
||||
|
||||
// Module: shell2d()
|
||||
// Usage:
|
||||
// shell2d(thickness, [or], [ir], [fill], [round])
|
||||
// Description:
|
||||
// Creates a hollow shell from 2D children, with optional rounding.
|
||||
// Arguments:
|
||||
// thickness = Thickness of the shell. Positive to expand outward, negative to shrink inward, or a two-element list to do both.
|
||||
// or = Radius to round convex corners/pointy bits on the outside of the shell.
|
||||
// ir = Radius to round concave corners on the outside of the shell.
|
||||
// round = Radius to round convex corners/pointy bits on the inside of the shell.
|
||||
// fill = Radius to round concave corners on the inside of the shell.
|
||||
// Examples(2D):
|
||||
// shell2d(10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(-10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d([-10,10]) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,or=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,ir=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,round=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(10,fill=10) {square([40,100], center=true); square([100,40], center=true);}
|
||||
// shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);}
|
||||
module shell2d(thickness, or=0, ir=0, fill=0, round=0)
|
||||
{
|
||||
thickness = is_num(thickness)? (
|
||||
thickness<0? [thickness,0] : [0,thickness]
|
||||
) : (thickness[0]>thickness[1])? (
|
||||
[thickness[1],thickness[0]]
|
||||
) : thickness;
|
||||
difference() {
|
||||
round2d(or=or,ir=ir)
|
||||
offset(delta=thickness[1])
|
||||
offset3d(or, size=size)
|
||||
offset3d(-ir-or, size=size)
|
||||
offset3d(ir, size=size)
|
||||
children();
|
||||
round2d(or=fill,ir=round)
|
||||
offset(delta=thickness[0])
|
||||
children();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Module: minkowski_difference()
|
||||
// Usage:
|
||||
// minkowski_difference() { base_shape(); diff_shape(); ... }
|
||||
// Description:
|
||||
// Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the
|
||||
// surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the
|
||||
// surface of its base shape.
|
||||
// Example:
|
||||
// minkowski_difference() {
|
||||
// union() {
|
||||
// cube([120,70,70], center=true);
|
||||
// cube([70,120,70], center=true);
|
||||
// cube([70,70,120], center=true);
|
||||
// }
|
||||
// sphere(r=10);
|
||||
// }
|
||||
module minkowski_difference() {
|
||||
difference() {
|
||||
bounding_box(excess=0) children(0);
|
||||
render(convexity=10) {
|
||||
minkowski() {
|
||||
difference() {
|
||||
bounding_box(excess=1) children(0);
|
||||
children(0);
|
||||
}
|
||||
for (i=[1:1:$children-1]) children(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
152
paths.scad
152
paths.scad
@ -407,6 +407,158 @@ function path_torsion(path, closed=false) =
|
||||
];
|
||||
|
||||
|
||||
// Function: path_chamfer_and_rounding()
|
||||
// Usage:
|
||||
// path2 = path_chamfer_and_rounding(path, [closed], [chamfer], [rounding]);
|
||||
// Description:
|
||||
// Rounds or chamfers corners in the given path.
|
||||
// Arguments:
|
||||
// path = The path to chamfer and/or round.
|
||||
// closed = If true, treat path like a closed polygon. Default: true
|
||||
// chamfer = The length of the chamfer faces at the corners. If given as a list of numbers, gives individual chamfers for each corner, from first to last. Default: 0 (no chamfer)
|
||||
// rounding = The rounding radius for the corners. If given as a list of numbers, gives individual radii for each corner, from first to last. Default: 0 (no rounding)
|
||||
// Example(2D): Chamfering a Path
|
||||
// path = star(5, step=2, d=100);
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=5);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Per-Corner Chamfering
|
||||
// path = star(5, step=2, d=100);
|
||||
// chamfs = [for (i=[0:1:4]) each 3*[i,i]];
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=chamfs);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Rounding a Path
|
||||
// path = star(5, step=2, d=100);
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, rounding=5);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Per-Corner Chamfering
|
||||
// path = star(5, step=2, d=100);
|
||||
// rs = [for (i=[0:1:4]) each 2*[i,i]];
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, rounding=rs);
|
||||
// stroke(path2, closed=true);
|
||||
// Example(2D): Mixing Chamfers and Roundings
|
||||
// path = star(5, step=2, d=100);
|
||||
// chamfs = [for (i=[0:4]) each [5,0]];
|
||||
// rs = [for (i=[0:4]) each [0,10]];
|
||||
// path2 = path_chamfer_and_rounding(path, closed=true, chamfer=chamfs, rounding=rs);
|
||||
// stroke(path2, closed=true);
|
||||
function path_chamfer_and_rounding(path, closed, chamfer, rounding) =
|
||||
let (
|
||||
path = deduplicate(path,closed=true),
|
||||
lp = len(path),
|
||||
chamfer = is_undef(chamfer)? repeat(0,lp) :
|
||||
is_vector(chamfer)? list_pad(chamfer,lp,0) :
|
||||
is_num(chamfer)? repeat(chamfer,lp) :
|
||||
assert(false, "Bad chamfer value."),
|
||||
rounding = is_undef(rounding)? repeat(0,lp) :
|
||||
is_vector(rounding)? list_pad(rounding,lp,0) :
|
||||
is_num(rounding)? repeat(rounding,lp) :
|
||||
assert(false, "Bad rounding value."),
|
||||
corner_paths = [
|
||||
for (i=(closed? [0:1:lp-1] : [1:1:lp-2])) let(
|
||||
p1 = select(path,i-1),
|
||||
p2 = select(path,i),
|
||||
p3 = select(path,i+1)
|
||||
)
|
||||
chamfer[i] > 0? _corner_chamfer_path(p1, p2, p3, side=chamfer[i]) :
|
||||
rounding[i] > 0? _corner_roundover_path(p1, p2, p3, r=rounding[i]) :
|
||||
[p2]
|
||||
],
|
||||
out = [
|
||||
if (!closed) path[0],
|
||||
for (i=(closed? [0:1:lp-1] : [1:1:lp-2])) let(
|
||||
p1 = select(path,i-1),
|
||||
p2 = select(path,i),
|
||||
crn1 = select(corner_paths,i-1),
|
||||
crn2 = corner_paths[i],
|
||||
l1 = norm(select(crn1,-1)-p1),
|
||||
l2 = norm(crn2[0]-p2),
|
||||
needed = l1 + l2,
|
||||
seglen = norm(p2-p1),
|
||||
check = assert(seglen >= needed, str("Path segment ",i," is too short to fulfill rounding/chamfering for the adjacent corners."))
|
||||
) each crn2,
|
||||
if (!closed) select(path,-1)
|
||||
]
|
||||
) deduplicate(out);
|
||||
|
||||
|
||||
function _corner_chamfer_path(p1, p2, p3, dist1, dist2, side, angle) =
|
||||
let(
|
||||
v1 = unit(p1 - p2),
|
||||
v2 = unit(p3 - p2),
|
||||
n = vector_axis(v1,v2),
|
||||
ang = vector_angle(v1,v2),
|
||||
path = (is_num(dist1) && is_undef(dist2) && is_undef(side))? (
|
||||
// dist1 & optional angle
|
||||
assert(dist1 > 0)
|
||||
let(angle = default(angle,(180-ang)/2))
|
||||
assert(is_num(angle))
|
||||
assert(angle > 0 && angle < 180)
|
||||
let(
|
||||
pta = p2 + dist1*v1,
|
||||
a3 = 180 - angle - ang
|
||||
) assert(a3>0, "Angle too extreme.")
|
||||
let(
|
||||
side = sin(angle) * dist1/sin(a3),
|
||||
ptb = p2 + side*v2
|
||||
) [pta, ptb]
|
||||
) : (is_undef(dist1) && is_num(dist2) && is_undef(side))? (
|
||||
// dist2 & optional angle
|
||||
assert(dist2 > 0)
|
||||
let(angle = default(angle,(180-ang)/2))
|
||||
assert(is_num(angle))
|
||||
assert(angle > 0 && angle < 180)
|
||||
let(
|
||||
ptb = p2 + dist2*v2,
|
||||
a3 = 180 - angle - ang
|
||||
) assert(a3>0, "Angle too extreme.")
|
||||
let(
|
||||
side = sin(angle) * dist2/sin(a3),
|
||||
pta = p2 + side*v1
|
||||
) [pta, ptb]
|
||||
) : (is_undef(dist1) && is_undef(dist2) && is_num(side))? (
|
||||
// side & optional angle
|
||||
assert(side > 0)
|
||||
let(angle = default(angle,(180-ang)/2))
|
||||
assert(is_num(angle))
|
||||
assert(angle > 0 && angle < 180)
|
||||
let(
|
||||
a3 = 180 - angle - ang
|
||||
) assert(a3>0, "Angle too extreme.")
|
||||
let(
|
||||
dist1 = sin(a3) * side/sin(ang),
|
||||
dist2 = sin(angle) * side/sin(ang),
|
||||
pta = p2 + dist1*v1,
|
||||
ptb = p2 + dist2*v2
|
||||
) [pta, ptb]
|
||||
) : (is_num(dist1) && is_num(dist2) && is_undef(side) && is_undef(side))? (
|
||||
// dist1 & dist2
|
||||
assert(dist1 > 0)
|
||||
assert(dist2 > 0)
|
||||
let(
|
||||
pta = p2 + dist1*v1,
|
||||
ptb = p2 + dist2*v2
|
||||
) [pta, ptb]
|
||||
) : (
|
||||
assert(false,"Bad arguments.")
|
||||
)
|
||||
) path;
|
||||
|
||||
|
||||
function _corner_roundover_path(p1, p2, p3, r, d) =
|
||||
let(
|
||||
r = get_radius(r=r,d=d,dflt=undef),
|
||||
res = circle_2tangents(p1, p2, p3, r=r, tangents=true),
|
||||
cp = res[0],
|
||||
n = res[1],
|
||||
tp1 = res[2],
|
||||
ang = res[4]+res[5],
|
||||
steps = floor(segs(r)*ang/360+0.5),
|
||||
step = ang / steps,
|
||||
path = [for (i=[0:1:steps]) move(cp, p=rot(a=-i*step, v=n, p=tp1-cp))]
|
||||
) path;
|
||||
|
||||
|
||||
|
||||
// Function: path3d_spiral()
|
||||
// Description:
|
||||
// Returns a 3D spiral path.
|
||||
|
@ -15,7 +15,7 @@ done
|
||||
if [[ "$FILES" != "" ]]; then
|
||||
PREVIEW_LIBS="$FILES"
|
||||
else
|
||||
PREVIEW_LIBS="Transforms Distributors Shapes2d Shapes3d Paths FractalTree"
|
||||
PREVIEW_LIBS="Shapes2d Shapes3d Transforms Distributors Mutators Paths FractalTree"
|
||||
fi
|
||||
|
||||
dir="$(basename $PWD)"
|
||||
|
@ -1206,6 +1206,8 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip,
|
||||
// w2 = The X axis width of the back end of the trapezoid.
|
||||
// angle = If given in place of `h`, `w1`, or `w2`, then the missing value is calculated such that the right side has that angle away from the Y axis.
|
||||
// shift = Scalar value to shift the back of the trapezoid along the X axis by. Default: 0
|
||||
// rounding = The rounding radius for the corners. If given as a list of four numbers, gives individual radii for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no rounding)
|
||||
// chamfer = The Length of the chamfer faces at the corners. If given as a list of four numbers, gives individual chamfers for each corner, in the order [X+Y+,X-Y+,X-Y-,X+Y-]. Default: 0 (no chamfer)
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// Examples(2D):
|
||||
@ -1217,42 +1219,68 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, align_tip,
|
||||
// trapezoid(h=20, w2=10, angle=30);
|
||||
// trapezoid(h=20, w2=30, angle=-30);
|
||||
// trapezoid(w1=30, w2=10, angle=30);
|
||||
// Example(2D): Chamferred Trapezoid
|
||||
// trapezoid(h=30, w1=60, w2=40, chamfer=5);
|
||||
// Example(2D): Rounded Trapezoid
|
||||
// trapezoid(h=30, w1=60, w2=40, rounding=5);
|
||||
// Example(2D): Mixed Chamfering and Rounding
|
||||
// trapezoid(h=30, w1=60, w2=40, rounding=[5,0,10,0],chamfer=[0,8,0,15],$fa=1,$fs=1);
|
||||
// Example(2D): Called as Function
|
||||
// stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
|
||||
function trapezoid(h, w1, w2, angle, shift=0, anchor=CENTER, spin=0) =
|
||||
function trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) =
|
||||
assert(is_undef(h) || is_finite(h))
|
||||
assert(is_undef(w1) || is_finite(w1))
|
||||
assert(is_undef(w2) || is_finite(w2))
|
||||
assert(is_undef(angle) || is_finite(angle))
|
||||
assert(num_defined([h, w1, w2, angle]) == 3, "Must give exactly 3 of the arguments h, w1, w2, and angle.")
|
||||
assert(is_finite(shift))
|
||||
assert(is_finite(chamfer) || is_vector(chamfer,4))
|
||||
assert(is_finite(rounding) || is_vector(rounding,4))
|
||||
let(
|
||||
simple = chamfer==0 && rounding==0,
|
||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle)),
|
||||
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift),
|
||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift),
|
||||
path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2+shift,h/2], [w2/2+shift,h/2]]
|
||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift)
|
||||
)
|
||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.")
|
||||
reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, p=path);
|
||||
assert(w1+w2>0, "Degenerate trapezoid geometry.")
|
||||
let(
|
||||
base_path = [
|
||||
[w2/2+shift,h/2],
|
||||
[-w2/2+shift,h/2],
|
||||
[-w1/2,-h/2],
|
||||
[w1/2,-h/2],
|
||||
],
|
||||
cpath = simple? base_path :
|
||||
path_chamfer_and_rounding(
|
||||
base_path, closed=true,
|
||||
chamfer=chamfer,
|
||||
rounding=rounding
|
||||
),
|
||||
path = reverse(cpath)
|
||||
) simple?
|
||||
reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift, p=path) :
|
||||
reorient(anchor,spin, two_d=true, path=path, p=path);
|
||||
|
||||
|
||||
|
||||
module trapezoid(h, w1, w2, angle, shift=0, anchor=CENTER, spin=0) {
|
||||
assert(is_undef(h) || is_finite(h));
|
||||
assert(is_undef(w1) || is_finite(w1));
|
||||
assert(is_undef(w2) || is_finite(w2));
|
||||
assert(is_undef(angle) || is_finite(angle));
|
||||
assert(num_defined([h, w1, w2, angle]) == 3, "Must give exactly 3 of the arguments h, w1, w2, and angle.");
|
||||
assert(is_finite(shift));
|
||||
module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) {
|
||||
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding);
|
||||
union() {
|
||||
simple = chamfer==0 && rounding==0;
|
||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle));
|
||||
w1 = !is_undef(w1)? w1 : w2 + 2*(adj_ang_to_opp(h, angle) + shift);
|
||||
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift);
|
||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.");
|
||||
path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2+shift,h/2], [w2/2+shift,h/2]];
|
||||
attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift) {
|
||||
polygon(path);
|
||||
children();
|
||||
if (simple) {
|
||||
attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
} else {
|
||||
attachable(anchor,spin, two_d=true, path=path) {
|
||||
polygon(path);
|
||||
children();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
273
tutorials/Mutators.md
Normal file
273
tutorials/Mutators.md
Normal file
@ -0,0 +1,273 @@
|
||||
# Mutators Tutorial
|
||||
|
||||
<!-- TOC -->
|
||||
|
||||
## 3D Space Halving
|
||||
Sometimes you want to take a 3D shape like a sphere, and cut it in half.
|
||||
The BOSL2 library provides a number of ways to do this:
|
||||
|
||||
```openscad
|
||||
left_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
right_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
front_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
back_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
bottom_half() sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
top_half() sphere(d=100);
|
||||
```
|
||||
|
||||
You can use the `half_of()` module if you want to split space in a way not aligned with an axis:
|
||||
|
||||
```openscad
|
||||
half_of([-1,0,-1]) sphere(d=100);
|
||||
```
|
||||
|
||||
The plane of dissection can be shifted along the axis of any of these operators:
|
||||
|
||||
```openscad
|
||||
left_half(x=20) sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
back_half(y=-20) sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
bottom_half(z=20) sphere(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
half_of([-1,0,-1], cp=[20,0,20]) sphere(d=100);
|
||||
```
|
||||
|
||||
By default, these operators can be applied to objects that fit in a cube 1000 on a side. If you need
|
||||
to apply these halving operators to objects larger than this, you can give the size in the `s=`
|
||||
argument:
|
||||
|
||||
```openscad
|
||||
bottom_half(s=2000) sphere(d=1500);
|
||||
```
|
||||
|
||||
## 2D Plane Halving
|
||||
To cut 2D shapes in half, you will need to add the `planar=true` argument:
|
||||
|
||||
```openscad
|
||||
left_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
right_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
front_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
```openscad
|
||||
back_half(planar=true) circle(d=100);
|
||||
```
|
||||
|
||||
## Chained Mutators
|
||||
If you have a set of shapes that you want to do pair-wise hulling of, you can use `chain_hull()`:
|
||||
|
||||
```openscad
|
||||
chain_hull() {
|
||||
cube(5, center=true);
|
||||
translate([30, 0, 0]) sphere(d=15);
|
||||
translate([60, 30, 0]) cylinder(d=10, h=20);
|
||||
translate([60, 60, 0]) cube([10,1,20], center=false);
|
||||
}
|
||||
```
|
||||
|
||||
## Extrusion Mutators
|
||||
The OpenSCAD `linear_extrude()` module can take a 2D shape and extrude it vertically in a line:
|
||||
|
||||
```openscad
|
||||
linear_extrude(height=30) zrot(45) square(40,center=true);
|
||||
```
|
||||
|
||||
The `rotate_extrude()` module can take a 2D shape and rotate it around the Z axis.
|
||||
|
||||
```openscad
|
||||
linear_extrude(height=30) left(30) zrot(45) square(40,center=true);
|
||||
```
|
||||
|
||||
In a similar manner, the BOSL2 `cylindrical_extrude()` module can take a 2d shape and extrude it
|
||||
out radially from the center of a cylinder:
|
||||
|
||||
```openscad
|
||||
cylindrical_extrude(or=40, ir=35)
|
||||
text(text="Hello World!", size=10, halign="center", valign="center");
|
||||
```
|
||||
|
||||
|
||||
## Offset Mutators
|
||||
|
||||
### Minkowski Difference
|
||||
Openscad provides the `minkowski()` module to trace a shape over the entire surface of another shape:
|
||||
|
||||
```openscad
|
||||
minkowski() {
|
||||
union() {
|
||||
cube([100,33,33], center=true);
|
||||
cube([33,100,33], center=true);
|
||||
cube([33,33,100], center=true);
|
||||
}
|
||||
sphere(r=8);
|
||||
}
|
||||
```
|
||||
|
||||
However, it doesn't provide the inverse of this operation; to remove a shape from the entire surface
|
||||
of another object. For this, the BOSL2 library provides the `minkowski_difference()` module:
|
||||
|
||||
```openscad
|
||||
minkowski_difference() {
|
||||
union() {
|
||||
cube([100,33,33], center=true);
|
||||
cube([33,100,33], center=true);
|
||||
cube([33,33,100], center=true);
|
||||
}
|
||||
sphere(r=8);
|
||||
}
|
||||
```
|
||||
|
||||
To perform a `minkowski_difference()` on 2D shapes, you need to supply the `planar=true` argument:
|
||||
|
||||
```openscad-2D
|
||||
minkowski_difference(planar=true) {
|
||||
union() {
|
||||
square([100,33], center=true);
|
||||
square([33,100], center=true);
|
||||
}
|
||||
circle(r=8);
|
||||
}
|
||||
```
|
||||
|
||||
### Round2d
|
||||
The `round2d()` module lets you take a 2D shape and round inside and outside corners. The inner concave corners are rounded to the radius `ir=`, while the outer convex corners are rounded to the radius `or=`:
|
||||
|
||||
```openscad-2D
|
||||
round2d(or=8) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
round2d(ir=12) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
round2d(or=8,ir=12) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
You can use `r=` to effectively set both `ir=` and `or=` to the same value:
|
||||
|
||||
```openscad-2D
|
||||
round2d(r=8) star(6, step=2, d=100);
|
||||
```
|
||||
|
||||
### Shell2d
|
||||
With the `shell2d()` module, you can take an arbitrary shape, and get the shell outline of it.
|
||||
With a positive thickness, the shell is offset outwards from the original shape:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=5) star(5,step=2,d=100);
|
||||
color("blue") stroke(star(5,step=2,d=100),closed=true);
|
||||
```
|
||||
|
||||
With a negative thickness, the shell if inset from the original shape:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5) star(5,step=2,d=100);
|
||||
color("blue") stroke(star(5,step=2,d=100),closed=true);
|
||||
```
|
||||
|
||||
You can give a pair of thickness values if you want it both inset and outset from the original shape:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=[-5,5]) star(5,step=2,d=100);
|
||||
color("blue") stroke(star(5,step=2,d=100),closed=true);
|
||||
```
|
||||
|
||||
You can add rounding to the outside by passing a radius to the `or=` argument.
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=5) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
If you need to pass different radii for the convex and concave corners of the outside, you can pass them as `or=[CONVEX,CONCAVE]`:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[5,10]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
A radius of 0 can be used to specify no rounding:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[5,0]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
You can add rounding to the inside by passing a radius to the `ir=` argument.
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,ir=5) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
If you need to pass different radii for the convex and concave corners of the inside, you can pass them as `ir=[CONVEX,CONCAVE]`:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,ir=[8,3]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
You can use `or=` and `ir=` together to get nice combined rounding effects:
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[7,2],ir=[7,2]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
```openscad-2D
|
||||
shell2d(thickness=-5,or=[5,0],ir=[5,0]) star(5,step=2,d=100);
|
||||
```
|
||||
|
||||
|
||||
### Round3d
|
||||
### Offset3d
|
||||
(To be Written)
|
||||
|
||||
|
||||
## Color Manipulators
|
||||
The built-in OpenSCAD `color()` module can let you set the RGB color of an object, but it's often
|
||||
easier to select colors using other color schemes. You can use the HSL or Hue-Saturation-Lightness
|
||||
color scheme with the `HSL()` module:
|
||||
|
||||
```openscad
|
||||
for (h=[0:0.1:1], s=[0:0.1:1], l=[0:0.1:1]) {
|
||||
translate(100*[h,s,l]) {
|
||||
HSL(h*360,1-s,l) cube(10,center=true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can use the HSV or Hue-Saturation-Value color scheme with the `HSV()` module:
|
||||
|
||||
```openscad
|
||||
for (h=[0:0.1:1], s=[0:0.1:1], v=[0:0.1:1]) {
|
||||
translate(100*[h,s,v]) {
|
||||
HSV(h*360,1-s,v) cube(10,center=true);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
BOSL_VERSION = [2,0,491];
|
||||
BOSL_VERSION = [2,0,498];
|
||||
|
||||
|
||||
// Section: BOSL Library Version Functions
|
||||
|
Loading…
x
Reference in New Issue
Block a user