mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-08-11 21:04:26 +02:00
Merged changes from PR #1665
This commit is contained in:
@@ -547,6 +547,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
||||
r = struct_val(edgespec,"r"),
|
||||
cut = struct_val(edgespec,"cut"),
|
||||
k = struct_val(edgespec,"k"),
|
||||
angle = struct_val(edgespec, "angle"),
|
||||
radius = in_list(edgetype,["circle","teardrop"])
|
||||
? (is_def(cut) ? cut/(sqrt(2)-1) : r)
|
||||
:edgetype=="chamfer"
|
||||
@@ -584,7 +585,7 @@ function _rounding_offsets(edgespec,z_dir=1) =
|
||||
[[-2*radius*(1-sqrt(2)/2), z_dir*abs(radius)]]
|
||||
)
|
||||
) :
|
||||
edgetype == "circle"? radius==0? [] : [for(i=[1:N]) [radius*(cos(i*90/N)-1), z_dir*abs(radius)*sin(i*90/N)]] :
|
||||
edgetype == "circle"? radius==0? [] : [for(i=[1:N]) [radius*(cos(i*angle/N)-1), z_dir*abs(radius)*sin(i*angle/N)]] :
|
||||
/* smooth */ joint==0 ? [] :
|
||||
list_tail(
|
||||
_bezcorner([[0,0],[0,z_dir*abs(joint)],[-joint,z_dir*abs(joint)]], k, $fn=N+2)
|
||||
@@ -1370,11 +1371,11 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||
// .
|
||||
// - profile: os_profile(points)
|
||||
// Define the offset profile with a list of points. The first point must be [0,0] and the roundover should rise in the positive y direction, with positive x values for inward motion (standard roundover) and negative x values for flaring outward. If the y value ever decreases then you might create a self-intersecting polyhedron, which is invalid. Such invalid polyhedra create cryptic assertion errors when you render your model and it is your responsibility to avoid creating them. Note that the starting point of the profile is the center of the extrusion. If you use a profile as the top, it rises upward. If you use it as the bottom, it is inverted and goes downward.
|
||||
// - circle: os_circle(r|cut). Define circular rounding either by specifying the radius or cut distance.
|
||||
// - circle: os_circle(r|cut=,height=|h=,[clip_angle=],). Define circular rounding or clipped circle rounding. You specify a full circle rounding by giving the radius, cut distance or height (which is equivalent to radius in this case). For a clipped circle rounding you can use two methods. You can specify the clip angle and then give a radius, cut, or height. (The cut distance in this case is the usual cut for a full circular arc.) Alternatively you can give a height and radius (or cut) and the appropriate clip angle is chosen for you.
|
||||
// - smooth: os_smooth(cut|joint, [k]). Define continuous curvature rounding, with `cut` and `joint` as for round_corners. The k parameter controls how fast the curvature changes and should be between 0 and 1.
|
||||
// - teardrop: os_teardrop(r|cut). Rounding using a 1/8 circle that then changes to a 45 degree chamfer. The chamfer is at the end, and enables the object to be 3d printed without support. The radius gives the radius of the circular part.
|
||||
// - chamfer: os_chamfer([height], [width], [cut], [angle]). Chamfer the edge at desired angle or with desired height and width. You can specify height and width together and the angle is ignored, or specify just one of height and width and the angle is used to determine the shape. Alternatively, specify "cut" along with angle to specify the cut back distance of the chamfer.
|
||||
// - mask: os_mask(mask, [out]). Create a profile from one of the [2d masking shapes](shapes2d.scad#section-2d-masking-shapes). The `out` parameter specifies that the mask should flare outward (like crown molding or baseboard). This is set false by default.
|
||||
// - mask: os_mask(mask, [out]). Create a profile from one of the [2d masking shapes](masks2d.scad#section-2d-masking-shapes). The `out` parameter specifies that the mask should flare outward (like crown molding or baseboard). This is set false by default.
|
||||
// .
|
||||
// The general settings that you can use with all of the helper functions are mostly used to control how offset_sweep() calls the offset() function.
|
||||
// - extra: Add an extra vertical step of the specified height, to be used for intersections or differences. This extra step extends the resulting object beyond the height you specify. It is ignored by anchoring. Default: 0
|
||||
@@ -1478,6 +1479,12 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||
// 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);
|
||||
// Example(3D,NoAxes,VPR=[99.80,0.00,62.10],VPD=155.56,VPT=[-2.78,0.61,14.66]): Clipped circle rounding on the bottom (for 3d printability and regular circular rounding on the top, both with the same radius. The clipped circle rounding takes up less vertical space.
|
||||
// $fn=64;
|
||||
// offset_sweep(rect(50,rounding=6), h=30,bot=os_circle(r=6, clip_angle=50), top=os_circle(r=6));
|
||||
// Example(3D,NoAxes,VPR=[99.80,0.00,62.10],VPD=155.56,VPT=[-2.78,0.61,14.66]): The same as the previous example but with roundings specified by height. This means that they have different radii, but the height maches.
|
||||
// $fn=64;
|
||||
// offset_sweep(rect(50,rounding=6), h=30,bot=os_circle(h=6, clip_angle=50), top=os_circle(h=6));
|
||||
// Example: We round a cube using the continous curvature rounding profile. But note that the corners are not smooth because the curved square collapses into a square with corners. When a collapse like this occurs, we cannot turn `check_valid` off. For a better result use {{rounded_prism()}} instead.
|
||||
// square = square(1);
|
||||
// rsquare = round_corners(square, method="smooth", cut=0.1, k=0.7, $fn=36);
|
||||
@@ -1562,7 +1569,7 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||
// sq = [[0,0],[20,0],[20,20],[0,20]];
|
||||
// sinwave = os_profile(points=[for(theta=[0:5:720]) [4*sin(theta), theta/700*15]]);
|
||||
// offset_sweep(sq, height=20, top=sinwave, offset="delta");
|
||||
// Example: a box with a flared top. A nice roundover on the top requires a profile edge, but we can use "extra" to create a small chamfer.
|
||||
// Example(3D,NoAxes,VPR=[59.20,0.00,24.80],VPD=54.24,VPT=[-4.12,10.66,0.96]): a box with a flared top. A nice roundover on the top requires a profile edge, but we can use "extra" to create a small chamfer.
|
||||
// rhex = round_corners(hexagon(side=10), method="smooth", joint=2, $fs=0.2);
|
||||
// back_half()
|
||||
// difference(){
|
||||
@@ -1570,15 +1577,15 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
|
||||
// up(1)
|
||||
// offset_sweep(offset(rhex,r=-1), height=9.5, bottom=os_circle(r=2), top=os_teardrop(r=-4));
|
||||
// }
|
||||
// Example: Using os_mask to create ogee profiles:
|
||||
// Example(3D,NoAxes,VPR=[53.60,0.00,190.20],VPD=1036.38,VPT=[6.09,5.67,59.25]): Using os_mask to create ogee profiles:
|
||||
// ogee = mask2d_ogee([
|
||||
// "xstep",1, "ystep",1, // Starting shoulder.
|
||||
// "fillet",5, "round",5, // S-curve.
|
||||
// "ystep",1, // Ending shoulder.
|
||||
// "xstep",3, "ystep",3, // Starting shoulder.
|
||||
// "fillet",15, "round",15, // S-curve.
|
||||
// "ystep",3, // Ending shoulder.
|
||||
// ]);
|
||||
// star = star(5, r=220, ir=130);
|
||||
// rounded_star = round_corners(star, cut=flatten(repeat([5,0],5)), $fn=24);
|
||||
// offset_sweep(rounded_star, height=100, top=os_mask(ogee), bottom=os_mask(ogee,out=true));
|
||||
// offset_sweep(rounded_star, height=150, top=os_mask(ogee), bottom=os_mask(ogee,out=true));
|
||||
// Example(3D,NoAxes): Applying to a region, with different profiles for the outside in inside curves.
|
||||
// $fn = 32;
|
||||
// rgn = difference(
|
||||
@@ -1852,13 +1859,30 @@ module offset_sweep(path, height,
|
||||
}
|
||||
|
||||
|
||||
function os_circle(r,cut,extra,check_valid, quality,steps, offset) =
|
||||
assert(num_defined([r,cut])==1, "Must define exactly one of `r` and `cut`")
|
||||
function os_circle(r,cut,h,height,clip_angle,extra,check_valid, quality,steps, offset) =
|
||||
assert(is_undef(clip_angle) || is_finite(clip_angle) && clip_angle>0 && clip_angle<=90, "clip angle must a number be in the interval (0,90]")
|
||||
let(
|
||||
h = one_defined([h,height],"h,height",dflt=undef),
|
||||
r_ang = is_def(clip_angle) ?
|
||||
assert(num_defined([r,h,cut])==1, "When clip_angle is given must give exactly one of r, h/height, or cut")
|
||||
is_def(r) ? [r,clip_angle]
|
||||
: is_def(cut) ? [cut/(sqrt(2)-1),clip_angle]
|
||||
: [h / sin(clip_angle),clip_angle]
|
||||
:
|
||||
assert(num_defined([r,cut])<=1, "Cannot give both r and cut")
|
||||
let(
|
||||
r = is_def(r) ? r
|
||||
: is_def(cut) ? cut/(sqrt(2)-1)
|
||||
: undef
|
||||
)
|
||||
is_def(r) ? [r, is_def(h) ? assert(h<=r, "height cannot be larger than radius") asin(h/r) : 90]
|
||||
: [h, 90]
|
||||
)
|
||||
_remove_undefined_vals([
|
||||
"for", "offset_sweep",
|
||||
"type", "circle",
|
||||
"r",r,
|
||||
"cut",cut,
|
||||
"r",r_ang[0],
|
||||
"angle",r_ang[1],
|
||||
"extra",extra,
|
||||
"check_valid",check_valid,
|
||||
"quality", quality,
|
||||
@@ -1866,6 +1890,7 @@ function os_circle(r,cut,extra,check_valid, quality,steps, offset) =
|
||||
"offset", offset
|
||||
]);
|
||||
|
||||
|
||||
function os_teardrop(r,cut,extra,check_valid, quality,steps, offset) =
|
||||
assert(num_defined([r,cut])==1, "Must define exactly one of `r` and `cut`")
|
||||
_remove_undefined_vals([
|
||||
@@ -1968,7 +1993,7 @@ function os_mask(mask, out=false, extra,check_valid, quality, offset) =
|
||||
// .
|
||||
// - profile: os_profile(points)
|
||||
// Define the offset profile with a list of points. The first point must be [0,0] and the roundover should rise in the positive y direction, with positive x values for inward motion (standard roundover) and negative x values for flaring outward. If the y value ever decreases then you might create a self-intersecting polyhedron, which is invalid. Such invalid polyhedra create cryptic assertion errors when you render your model and it is your responsibility to avoid creating them. Note that the starting point of the profile is the center of the extrusion. If you use a profile as the top, it rises upward. If you use it as the bottom, it is inverted and goes downward.
|
||||
// - circle: os_circle(r|cut). Define circular rounding either by specifying the radius or cut distance.
|
||||
// - circle: os_circle(r|cut=,height=|h=,[clip_angle=],). Define circular rounding or clipped circle rounding. You specify a full circle rounding by giving the radius, cut distance or height (which is equivalent to radius in this case). For a clipped circle rounding you can use two methods. You can specify the clip angle and then give a radius, cut, or height. (The cut distance in this case is the usual cut for a full circular arc.) Alternatively you can give a height and radius (or cut) and the appropriate clip angle is chosen for you.
|
||||
// - smooth: os_smooth(cut|joint, [k]). Define continuous curvature rounding, with `cut` and `joint` as for round_corners. The k parameter controls how fast the curvature changes and should be between 0 and 1.
|
||||
// - teardrop: os_teardrop(r|cut). Rounding using a 1/8 circle that then changes to a 45 degree chamfer. The chamfer is at the end, and enables the object to be 3d printed without support. The radius gives the radius of the circular part.
|
||||
// - chamfer: os_chamfer([height], [width], [cut], [angle]). Chamfer the edge at desired angle or with desired height and width. You can specify height and width together and the angle is ignored, or specify just one of height and width and the angle is used to determine the shape. Alternatively, specify "cut" along with angle to specify the cut back distance of the chamfer.
|
||||
|
116
shapes3d.scad
116
shapes3d.scad
@@ -1017,102 +1017,26 @@ function regular_prism(n,
|
||||
assert(is_vector(shift,2), "shift must be a 2D vector.")
|
||||
let(
|
||||
vnf = any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])
|
||||
? assert(is_undef(texture), "Cannot combine roundings or chamfers with texturing")
|
||||
let(
|
||||
vang = atan2(r1-r2,height),
|
||||
_chamf1 = first_defined([chamfer1, if (is_undef(rounding1)) chamfer, 0]),
|
||||
_chamf2 = first_defined([chamfer2, if (is_undef(rounding2)) chamfer, 0]),
|
||||
_fromend1 = first_defined([from_end1, from_end, false]),
|
||||
_fromend2 = first_defined([from_end2, from_end, false]),
|
||||
chang1 = first_defined([chamfang1, chamfang, 45+sign(_chamf1)*vang/2]),
|
||||
chang2 = first_defined([chamfang2, chamfang, 45-sign(_chamf2)*vang/2]),
|
||||
round1 = first_defined([rounding1, if (is_undef(chamfer1)) rounding, 0]),
|
||||
round2 = first_defined([rounding2, if (is_undef(chamfer2)) rounding, 0]),
|
||||
checks1 =
|
||||
assert(is_finite(_chamf1), "chamfer1 must be a finite number if given.")
|
||||
assert(is_finite(_chamf2), "chamfer2 must be a finite number if given.")
|
||||
assert(is_finite(chang1) && chang1>0, "chamfang1 must be a positive number if given.")
|
||||
assert(is_finite(chang2) && chang2>0, "chamfang2 must be a positive number if given.")
|
||||
assert(chang1<90+sign(_chamf1)*vang, "chamfang1 must be smaller than the cone face angle")
|
||||
assert(chang2<90-sign(_chamf2)*vang, "chamfang2 must be smaller than the cone face angle")
|
||||
assert(num_defined([chamfer1,rounding1])<2, "cannot define both chamfer1 and rounding1")
|
||||
assert(num_defined([chamfer2,rounding2])<2, "cannot define both chamfer2 and rounding2")
|
||||
assert(num_defined([chamfer,rounding])<2, "cannot define both chamfer and rounding")
|
||||
undef,
|
||||
chamf1r = !_chamf1? 0
|
||||
: !_fromend1? _chamf1
|
||||
: law_of_sines(a=_chamf1, A=chang1, B=180-chang1-(90-sign(_chamf2)*vang)),
|
||||
chamf2r = !_chamf2? 0
|
||||
: !_fromend2? _chamf2
|
||||
: law_of_sines(a=_chamf2, A=chang2, B=180-chang2-(90+sign(_chamf2)*vang)),
|
||||
chamf1l = !_chamf1? 0
|
||||
: _fromend1? abs(_chamf1)
|
||||
: abs(law_of_sines(a=_chamf1, A=180-chang1-(90-sign(_chamf1)*vang), B=chang1)),
|
||||
chamf2l = !_chamf2? 0
|
||||
: _fromend2? abs(_chamf2)
|
||||
: abs(law_of_sines(a=_chamf2, A=180-chang2-(90+sign(_chamf2)*vang), B=chang2)),
|
||||
facelen = adj_ang_to_hyp(height, abs(vang)),
|
||||
|
||||
roundlen1 = round1 >= 0 ? round1/tan(45-vang/2)
|
||||
: round1/tan(45+vang/2),
|
||||
roundlen2 = round2 >=0 ? round2/tan(45+vang/2)
|
||||
: round2/tan(45-vang/2),
|
||||
dy1 = abs(_chamf1 ? chamf1l : round1 ? roundlen1 : 0),
|
||||
dy2 = abs(_chamf2 ? chamf2l : round2 ? roundlen2 : 0),
|
||||
td_ang = teardrop == true? 45 :
|
||||
teardrop == false? 90 :
|
||||
assert(is_finite(teardrop))
|
||||
assert(teardrop>=0 && teardrop<=90)
|
||||
teardrop,
|
||||
clip_ang = clip_roundings == true? 45 :
|
||||
clip_roundings == false? 90 :
|
||||
assert(is_finite(clip_roundings))
|
||||
assert(clip_roundings>=0 && clip_roundings<=90)
|
||||
clip_roundings,
|
||||
|
||||
checks2 =
|
||||
assert(is_finite(round1), "rounding1 must be a number if given.")
|
||||
assert(is_finite(round2), "rounding2 must be a number if given.")
|
||||
assert(chamf1r <= r1, "chamfer1 is larger than the r1 radius of the cylinder.")
|
||||
assert(chamf2r <= r2, "chamfer2 is larger than the r2 radius of the cylinder.")
|
||||
assert(roundlen1 <= r1, "size of rounding1 is larger than the r1 radius of the cylinder.")
|
||||
assert(roundlen2 <= r2, "size of rounding2 is larger than the r2 radius of the cylinder.")
|
||||
assert(dy1+dy2 <= facelen, "Chamfers/roundings don't fit on the cylinder/cone. They exceed the length of the cylinder/cone face.")
|
||||
assert(td_ang==90 || clip_ang==90, "teardrop= and clip_roundings= are mutually exclusive features.")
|
||||
undef,
|
||||
path = [
|
||||
[0,-height/2],
|
||||
if (!approx(chamf1r,0))
|
||||
each [
|
||||
[r1, -height/2] + polar_to_xy(chamf1r,180),
|
||||
[r1, -height/2] + polar_to_xy(chamf1l,90+vang),
|
||||
]
|
||||
else if (!approx(round1,0) && td_ang < 90)
|
||||
each _teardrop_corner(r=round1, corner=[[max(0,r1-2*roundlen1),-height/2],[r1,-height/2],[r2,height/2]], ang=td_ang)
|
||||
else if (!approx(round1,0) && clip_ang < 90)
|
||||
each _clipped_corner(r=round1, corner=[[max(0,r1-2*roundlen1),-height/2],[r1,-height/2],[r2,height/2]], ang=clip_ang)
|
||||
else if (!approx(round1,0) && td_ang >= 90)
|
||||
each arc(r=abs(round1), corner=[[max(0,r1-2*roundlen1),-height/2],[r1,-height/2],[r2,height/2]])
|
||||
else [r1,-height/2],
|
||||
|
||||
if (is_finite(chamf2r) && !approx(chamf2r,0))
|
||||
each [
|
||||
[r2, height/2] + polar_to_xy(chamf2l,270+vang),
|
||||
[r2, height/2] + polar_to_xy(chamf2r,180),
|
||||
]
|
||||
else if (is_finite(round2) && !approx(round2,0))
|
||||
each arc(r=abs(round2), corner=[[r1,-height/2],[r2,height/2],[max(0,r2-2*roundlen2),height/2]])
|
||||
else [r2,height/2],
|
||||
[0,height/2],
|
||||
]
|
||||
)
|
||||
rotate_sweep(path,caps=true,$fn=n)
|
||||
: is_undef(texture) ? cylinder(h=height, r1=r1, r2=r2, center=true, $fn=n)
|
||||
: linear_sweep(regular_ngon(n=n,r=r1),scale=r2/r1,height=height,center=true,
|
||||
texture=texture, tex_reps=tex_reps, tex_size=tex_size,
|
||||
tex_inset=tex_inset, tex_rot=tex_rot,
|
||||
tex_depth=tex_depth, tex_samples=tex_samples,
|
||||
style=style),
|
||||
? assert(is_undef(texture), "Cannot combine roundings or chamfers with texturing")
|
||||
let(
|
||||
path = [
|
||||
[0,-height/2],
|
||||
each _cyl_path(r1, r2, height,
|
||||
chamfer, chamfer1, chamfer2,
|
||||
chamfang, chamfang1, chamfang2,
|
||||
rounding, rounding1, rounding2,
|
||||
from_end, from_end1, from_end2,
|
||||
teardrop, clip_roundings),
|
||||
[0,height/2]
|
||||
]
|
||||
)
|
||||
rotate_sweep(path,caps=true,$fn=n)
|
||||
: is_undef(texture) ? cylinder(h=height, r1=r1, r2=r2, center=true, $fn=n)
|
||||
: linear_sweep(regular_ngon(n=n,r=r1),scale=r2/r1,height=height,center=true,
|
||||
texture=texture, tex_reps=tex_reps, tex_size=tex_size,
|
||||
tex_inset=tex_inset, tex_rot=tex_rot,
|
||||
tex_depth=tex_depth, tex_samples=tex_samples,
|
||||
style=style),
|
||||
skmat = down(height/2) *
|
||||
skew(sxz=shift.x/height, syz=shift.y/height) *
|
||||
up(height/2) *
|
||||
|
Reference in New Issue
Block a user