mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-08-11 17:04:24 +02:00
Added rounding and chamfering to trapezoid()
This commit is contained in:
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 3*[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()
|
// Function: path3d_spiral()
|
||||||
// Description:
|
// Description:
|
||||||
// Returns a 3D spiral path.
|
// Returns a 3D spiral path.
|
||||||
|
@@ -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.
|
// 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.
|
// 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
|
// 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`
|
// 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`
|
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||||
// Examples(2D):
|
// Examples(2D):
|
||||||
@@ -1217,43 +1219,69 @@ 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=10, angle=30);
|
||||||
// trapezoid(h=20, w2=30, angle=-30);
|
// trapezoid(h=20, w2=30, angle=-30);
|
||||||
// trapezoid(w1=30, w2=10, 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
|
// Example(2D): Called as Function
|
||||||
// stroke(closed=true, trapezoid(h=30, w1=40, w2=20));
|
// 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(h) || is_finite(h))
|
||||||
assert(is_undef(w1) || is_finite(w1))
|
assert(is_undef(w1) || is_finite(w1))
|
||||||
assert(is_undef(w2) || is_finite(w2))
|
assert(is_undef(w2) || is_finite(w2))
|
||||||
assert(is_undef(angle) || is_finite(angle))
|
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(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(shift))
|
||||||
|
assert(is_finite(chamfer) || is_vector(chamfer,4))
|
||||||
|
assert(is_finite(rounding) || is_vector(rounding,4))
|
||||||
let(
|
let(
|
||||||
|
simple = chamfer==0 && rounding==0,
|
||||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle)),
|
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),
|
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),
|
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]]
|
|
||||||
)
|
)
|
||||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.")
|
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) {
|
module trapezoid(h, w1, w2, angle, shift=0, chamfer=0, rounding=0, anchor=CENTER, spin=0) {
|
||||||
assert(is_undef(h) || is_finite(h));
|
path = trapezoid(h=h, w1=w1, w2=w2, angle=angle, shift=shift, chamfer=chamfer, rounding=rounding);
|
||||||
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));
|
|
||||||
union() {
|
union() {
|
||||||
|
simple = chamfer==0 && rounding==0;
|
||||||
h = !is_undef(h)? h : opp_ang_to_adj(abs(w2-w1)/2, abs(angle));
|
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);
|
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);
|
w2 = !is_undef(w2)? w2 : w1 - 2*(adj_ang_to_opp(h, angle) + shift);
|
||||||
assert(w1>=0 && w2>=0 && h>0, "Degenerate trapezoid geometry.");
|
if (simple) {
|
||||||
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) {
|
attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2, shift=shift) {
|
||||||
polygon(path);
|
polygon(path);
|
||||||
children();
|
children();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
attachable(anchor,spin, two_d=true, path=path) {
|
||||||
|
polygon(path);
|
||||||
|
children();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
BOSL_VERSION = [2,0,496];
|
BOSL_VERSION = [2,0,497];
|
||||||
|
|
||||||
|
|
||||||
// Section: BOSL Library Version Functions
|
// Section: BOSL Library Version Functions
|
||||||
|
Reference in New Issue
Block a user