fix examples for display in rounding.scad

hide stuff in gears.scad
add kern option to path_text
This commit is contained in:
Adrian Mariano 2022-03-29 23:21:32 -04:00
parent 58836abd54
commit dff0cb6a0b
3 changed files with 297 additions and 220 deletions

View file

@ -129,7 +129,7 @@ function spur_gear(
pitch = is_undef(mod) ? pitch : pitch_value(mod),
p = pitch_radius(pitch, teeth),
c = outer_radius(pitch, teeth, clearance, interior),
r = root_radius(pitch, teeth, clearance, interior),
r = _root_radius(pitch, teeth, clearance, interior),
twist = atan2(thickness*tan(helical),p),
rgn = [
spur_gear2d(
@ -167,7 +167,7 @@ module spur_gear(
pitch = is_undef(mod) ? pitch : pitch_value(mod);
p = pitch_radius(pitch, teeth);
c = outer_radius(pitch, teeth, clearance, interior);
r = root_radius(pitch, teeth, clearance, interior);
r = _root_radius(pitch, teeth, clearance, interior);
twist = atan2(thickness*tan(helical),p);
attachable(anchor,spin,orient, r=p, l=thickness) {
difference() {
@ -240,7 +240,7 @@ function spur_gear2d(
) = let(
pitch = is_undef(mod) ? pitch : pitch_value(mod),
pr = pitch_radius(pitch=pitch, teeth=teeth),
tooth_profile = gear_tooth_profile(
tooth_profile = _gear_tooth_profile(
pitch = pitch,
teeth = teeth,
pressure_angle = pressure_angle,
@ -358,8 +358,8 @@ module rack(
orient = UP
) {
pitch = is_undef(mod) ? pitch : pitch_value(mod);
a = adendum(pitch);
d = dedendum(pitch, clearance);
a = _adendum(pitch);
d = _dedendum(pitch, clearance);
l = teeth * pitch;
anchors = [
named_anchor("adendum", [0,0,a], BACK),
@ -407,8 +407,8 @@ function rack(
) =
let(
pitch = is_undef(mod) ? pitch : pitch_value(mod),
a = adendum(pitch),
d = dedendum(pitch, clearance),
a = _adendum(pitch),
d = _dedendum(pitch, clearance),
l = teeth * pitch,
anchors = [
named_anchor("adendum", [0,0,a], BACK),
@ -483,8 +483,8 @@ function rack2d(
) =
let(
pitch = is_undef(mod) ? pitch : pitch_value(mod),
a = adendum(pitch),
d = dedendum(pitch, clearance)
a = _adendum(pitch),
d = _dedendum(pitch, clearance)
)
assert(a+d < height)
let(
@ -528,8 +528,8 @@ module rack2d(
spin = 0
) {
pitch = is_undef(mod) ? pitch : pitch_value(mod);
a = adendum(pitch);
d = dedendum(pitch, clearance);
a = _adendum(pitch);
d = _dedendum(pitch, clearance);
l = teeth * pitch;
anchors = [
named_anchor("adendum", [ 0, a,0], BACK),
@ -664,7 +664,7 @@ function bevel_gear(
slices = cutter_radius==0? 1 : slices,
pitch_angle = is_undef(mate_teeth)? pitch_angle : atan(teeth/mate_teeth),
pr = pitch_radius(pitch, teeth),
rr = root_radius(pitch, teeth, clearance, interior),
rr = _root_radius(pitch, teeth, clearance, interior),
pitchoff = (pr-rr) * sin(pitch_angle),
ocone_rad = opp_ang_to_hyp(pr, pitch_angle),
icone_rad = ocone_rad - face_width,
@ -676,7 +676,7 @@ function bevel_gear(
radcpang = v_theta(radcp),
sang = radcpang - (180-angC1),
eang = radcpang - (180-angC2),
profile = gear_tooth_profile(
profile = _gear_tooth_profile(
pitch = pitch,
teeth = teeth,
pressure_angle = pressure_angle,
@ -772,7 +772,7 @@ module bevel_gear(
pitch_angle = is_undef(mate_teeth)? pitch_angle : atan(teeth/mate_teeth);
pr = pitch_radius(pitch, teeth);
ipr = pr - face_width*sin(pitch_angle);
rr = root_radius(pitch, teeth, clearance, interior);
rr = _root_radius(pitch, teeth, clearance, interior);
pitchoff = (pr-rr) * sin(pitch_angle);
vnf = bevel_gear(
pitch = pitch,
@ -1011,7 +1011,7 @@ function worm_gear(
r2 = worm_diam/2 + crowning,
thickness = worm_gear_thickness(pitch=pitch, teeth=teeth, worm_diam=worm_diam, worm_arc=worm_arc, crowning=crowning, clearance=clearance),
helical = pitch * worm_starts * worm_arc / 360 * 360 / circ,
tooth_profile = reverse(gear_tooth_profile(
tooth_profile = reverse(_gear_tooth_profile(
pitch = pitch,
teeth = teeth,
pressure_angle = pressure_angle,
@ -1113,43 +1113,42 @@ module worm_gear(
}
// Section: 2D Profiles
// Function&Module: gear_tooth_profile()
// Usage: As Module
// gear_tooth_profile(pitch|mod, teeth, [pressure_angle], [clearance], [backlash], [interior], [valleys]);
// Usage: As Function
// path = gear_tooth_profile(pitch|mod, teeth, [pressure_angle], [clearance], [backlash], [interior], [valleys]);
// Topics: Gears
// See Also: spur_gear2d()
// Description:
// When called as a function, returns the 2D profile path for an individual gear tooth.
// When called as a module, creates the 2D profile shape for an individual gear tooth.
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// teeth = Total number of teeth on the spur gear that this is a tooth for.
// pressure_angle = Pressure Angle. Controls how straight or bulged the tooth sides are. In degrees.
// clearance = Gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
// interior = If true, create a mask for difference()ing from something else.
// valleys = If true, add the valley bottoms on either side of the tooth. Default: true
// center = If true, centers the pitch circle of the tooth profile at the origin. Default: false.
// mod = The metric module/modulus of the gear.
// Example(2D):
// gear_tooth_profile(pitch=5, teeth=20, pressure_angle=20);
// Example(2D): Metric Gear Tooth
// gear_tooth_profile(mod=2, teeth=20, pressure_angle=20);
// Example(2D):
// gear_tooth_profile(
// pitch=5, teeth=20, pressure_angle=20, valleys=false
// );
// Example(2D): As a function
// path = gear_tooth_profile(
// pitch=5, teeth=20, pressure_angle=20, valleys=false
// );
// stroke(path, width=0.1);
function gear_tooth_profile(
/// Function&Module: _gear_tooth_profile()
/// Usage: As Module
/// _gear_tooth_profile(pitch|mod, teeth, [pressure_angle], [clearance], [backlash], [interior], [valleys]);
/// Usage: As Function
/// path = _gear_tooth_profile(pitch|mod, teeth, [pressure_angle], [clearance], [backlash], [interior], [valleys]);
/// Topics: Gears
/// See Also: spur_gear2d()
/// Description:
/// When called as a function, returns the 2D profile path for an individual gear tooth.
/// When called as a module, creates the 2D profile shape for an individual gear tooth.
/// Arguments:
/// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
/// teeth = Total number of teeth on the spur gear that this is a tooth for.
/// pressure_angle = Pressure Angle. Controls how straight or bulged the tooth sides are. In degrees.
/// clearance = Gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
/// backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle
/// interior = If true, create a mask for difference()ing from something else.
/// valleys = If true, add the valley bottoms on either side of the tooth. Default: true
/// center = If true, centers the pitch circle of the tooth profile at the origin. Default: false.
/// mod = The metric module/modulus of the gear.
/// Example(2D):
/// _gear_tooth_profile(pitch=5, teeth=20, pressure_angle=20);
/// Example(2D): Metric Gear Tooth
/// _gear_tooth_profile(mod=2, teeth=20, pressure_angle=20);
/// Example(2D):
/// _gear_tooth_profile(
/// pitch=5, teeth=20, pressure_angle=20, valleys=false
/// );
/// Example(2D): As a function
/// path = _gear_tooth_profile(
/// pitch=5, teeth=20, pressure_angle=20, valleys=false
/// );
/// stroke(path, width=0.1);
function _gear_tooth_profile(
pitch = 3,
teeth = 11,
pressure_angle = 28,
@ -1163,8 +1162,8 @@ function gear_tooth_profile(
pitch = is_undef(mod) ? pitch : pitch_value(mod),
p = pitch_radius(pitch, teeth),
c = outer_radius(pitch, teeth, clearance, interior),
r = root_radius(pitch, teeth, clearance, interior),
b = base_radius(pitch, teeth, pressure_angle),
r = _root_radius(pitch, teeth, clearance, interior),
b = _base_radius(pitch, teeth, pressure_angle),
t = pitch/2-backlash/2, //tooth thickness at pitch circle
k = -_gear_iang(b, p) - t/2/p/PI*180, //angle to where involute meets base circle on each side of tooth
kk = r<b? k : -180/teeth,
@ -1187,7 +1186,7 @@ function gear_tooth_profile(
) pts2;
module gear_tooth_profile(
module _gear_tooth_profile(
pitch = 3,
teeth = 11,
pressure_angle = 28,
@ -1199,10 +1198,10 @@ module gear_tooth_profile(
mod
) {
pitch = is_undef(mod) ? pitch : pitch_value(mod);
r = root_radius(pitch, teeth, clearance, interior);
r = _root_radius(pitch, teeth, clearance, interior);
fwd(r)
polygon(
points=gear_tooth_profile(
points=_gear_tooth_profile(
pitch = pitch,
teeth = teeth,
pressure_angle = pressure_angle,
@ -1281,55 +1280,55 @@ function pitch_value(mod) = mod * PI;
function module_value(pitch=5) = pitch / PI;
// Function: adendum()
// Usage:
// ad = adendum(pitch|mod);
// Topics: Gears
// Description:
// The height of the top of a gear tooth above the pitch radius circle.
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// mod = The metric module/modulus of the gear.
// Example:
// ad = adendum(pitch=5);
// ad = adendum(mod=2);
// Example(2D):
// pitch = 5; teeth = 17;
// pr = pitch_radius(pitch=pitch, teeth=teeth);
// adn = adendum(pitch=5);
// #spur_gear2d(pitch=pitch, teeth=teeth);
// color("black") {
// stroke(circle(r=pr),width=0.1,closed=true);
// stroke(circle(r=pr+adn),width=0.1,closed=true);
// }
function adendum(pitch=5, mod) =
/// Function: _adendum()
/// Usage:
/// ad = _adendum(pitch|mod);
/// Topics: Gears
/// Description:
/// The height of the top of a gear tooth above the pitch radius circle.
/// Arguments:
/// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
/// mod = The metric module/modulus of the gear.
/// Example:
/// ad = _adendum(pitch=5);
/// ad = _adendum(mod=2);
/// Example(2D):
/// pitch = 5; teeth = 17;
/// pr = pitch_radius(pitch=pitch, teeth=teeth);
/// adn = _adendum(pitch=5);
/// #spur_gear2d(pitch=pitch, teeth=teeth);
/// color("black") {
/// stroke(circle(r=pr),width=0.1,closed=true);
/// stroke(circle(r=pr+adn),width=0.1,closed=true);
/// }
function _adendum(pitch=5, mod) =
let( pitch = is_undef(mod) ? pitch : pitch_value(mod) )
module_value(pitch) * 1.0;
// Function: dedendum()
// Usage:
// ddn = dedendum(pitch|mod, [clearance]);
// Topics: Gears
// Description:
// The depth of the gear tooth valley, below the pitch radius.
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// clearance = If given, sets the clearance between meshing teeth.
// mod = The metric module/modulus of the gear.
// Example:
// ddn = dedendum(pitch=5);
// ddn = dedendum(mod=2);
// Example(2D):
// pitch = 5; teeth = 17;
// pr = pitch_radius(pitch=pitch, teeth=teeth);
// ddn = dedendum(pitch=5);
// #spur_gear2d(pitch=pitch, teeth=teeth);
// color("black") {
// stroke(circle(r=pr),width=0.1,closed=true);
// stroke(circle(r=pr-ddn),width=0.1,closed=true);
// }
function dedendum(pitch=5, clearance, mod) =
/// Function: _dedendum()
/// Usage:
/// ddn = _dedendum(pitch|mod, [clearance]);
/// Topics: Gears
/// Description:
/// The depth of the gear tooth valley, below the pitch radius.
/// Arguments:
/// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
/// clearance = If given, sets the clearance between meshing teeth.
/// mod = The metric module/modulus of the gear.
/// Example:
/// ddn = _dedendum(pitch=5);
/// ddn = _dedendum(mod=2);
/// Example(2D):
/// pitch = 5; teeth = 17;
/// pr = pitch_radius(pitch=pitch, teeth=teeth);
/// ddn = _dedendum(pitch=5);
/// #spur_gear2d(pitch=pitch, teeth=teeth);
/// color("black") {
/// stroke(circle(r=pr),width=0.1,closed=true);
/// stroke(circle(r=pr-ddn),width=0.1,closed=true);
/// }
function _dedendum(pitch=5, clearance, mod) =
let( pitch = is_undef(mod) ? pitch : pitch_value(mod) )
is_undef(clearance)? (1.25 * module_value(pitch)) :
(module_value(pitch) + clearance);
@ -1382,55 +1381,55 @@ function pitch_radius(pitch=5, teeth=11, mod) =
function outer_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
let( pitch = is_undef(mod) ? pitch : pitch_value(mod) )
pitch_radius(pitch, teeth) +
(interior? dedendum(pitch, clearance) : adendum(pitch));
(interior? _dedendum(pitch, clearance) : _adendum(pitch));
// Function: root_radius()
// Usage:
// rr = root_radius(pitch|mod, teeth, [clearance], [interior]);
// Topics: Gears
// Description:
// Calculates the root radius for the gear, at the base of the dedendum.
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// teeth = The number of teeth on the gear.
// clearance = If given, sets the clearance between meshing teeth.
// interior = If true, calculate for an interior gear.
// mod = The metric module/modulus of the gear.
// Example:
// rr = root_radius(pitch=5, teeth=11);
// rr = root_radius(mod=2, teeth=16);
// Example(2D):
// pr = root_radius(pitch=5, teeth=11);
// #spur_gear2d(pitch=5, teeth=11);
// color("black")
// stroke(circle(r=pr),width=0.1,closed=true);
function root_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
/// Function: _root_radius()
/// Usage:
/// rr = _root_radius(pitch|mod, teeth, [clearance], [interior]);
/// Topics: Gears
/// Description:
/// Calculates the root radius for the gear, at the base of the dedendum.
/// Arguments:
/// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
/// teeth = The number of teeth on the gear.
/// clearance = If given, sets the clearance between meshing teeth.
/// interior = If true, calculate for an interior gear.
/// mod = The metric module/modulus of the gear.
/// Example:
/// rr = _root_radius(pitch=5, teeth=11);
/// rr = _root_radius(mod=2, teeth=16);
/// Example(2D):
/// pr = _root_radius(pitch=5, teeth=11);
/// #spur_gear2d(pitch=5, teeth=11);
/// color("black")
/// stroke(circle(r=pr),width=0.1,closed=true);
function _root_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
let( pitch = is_undef(mod) ? pitch : pitch_value(mod) )
pitch_radius(pitch, teeth) -
(interior? adendum(pitch) : dedendum(pitch, clearance));
(interior? _adendum(pitch) : _dedendum(pitch, clearance));
// Function: base_radius()
// Usage:
// br = base_radius(pitch|mod, teeth, [pressure_angle]);
// Topics: Gears
// Description:
// Get the base circle for involute teeth, at the base of the teeth.
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// teeth = The number of teeth on the gear.
// pressure_angle = Pressure angle in degrees. Controls how straight or bulged the tooth sides are.
// mod = The metric module/modulus of the gear.
// Example:
// br = base_radius(pitch=5, teeth=20, pressure_angle=20);
// br = base_radius(mod=2, teeth=18, pressure_angle=20);
// Example(2D):
// pr = base_radius(pitch=5, teeth=11);
// #spur_gear2d(pitch=5, teeth=11);
// color("black")
// stroke(circle(r=pr),width=0.1,closed=true);
function base_radius(pitch=5, teeth=11, pressure_angle=28, mod) =
/// Function: _base_radius()
/// Usage:
/// br = _base_radius(pitch|mod, teeth, [pressure_angle]);
/// Topics: Gears
/// Description:
/// Get the base circle for involute teeth, at the base of the teeth.
/// Arguments:
/// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
/// teeth = The number of teeth on the gear.
/// pressure_angle = Pressure angle in degrees. Controls how straight or bulged the tooth sides are.
/// mod = The metric module/modulus of the gear.
/// Example:
/// br = _base_radius(pitch=5, teeth=20, pressure_angle=20);
/// br = _base_radius(mod=2, teeth=18, pressure_angle=20);
/// Example(2D):
/// pr = _base_radius(pitch=5, teeth=11);
/// #spur_gear2d(pitch=5, teeth=11);
/// color("black")
/// stroke(circle(r=pr),width=0.1,closed=true);
function _base_radius(pitch=5, teeth=11, pressure_angle=28, mod) =
let( pitch = is_undef(mod) ? pitch : pitch_value(mod) )
pitch_radius(pitch, teeth) * cos(pressure_angle);
@ -1504,7 +1503,7 @@ function worm_gear_thickness(pitch=5, teeth=30, worm_diam=30, worm_arc=60, crown
r = worm_diam/2 + crowning,
pitch_thick = r * sin(worm_arc/2) * 2,
pr = pitch_radius(pitch, teeth),
rr = root_radius(pitch, teeth, clearance, false),
rr = _root_radius(pitch, teeth, clearance, false),
pitchoff = (pr-rr) * sin(worm_arc/2),
thickness = pitch_thick + 2*pitchoff
) thickness;

View file

@ -293,7 +293,7 @@ include <structs.scad>
// // Construct a square spiral path in 3D
// $fn=36;
// square = [[0,0],[1,0],[1,1],[0,1]];
// spiral = flatten(repeat(concat(square,reverse(square)),5)); // Squares repeat 10 times, forward and backward
// spiral = flatten(repeat(concat(square,reverse(square)),5)); // Squares repeat 10x, forward and backward
// squareind = [for(i=[0:9]) each [i,i,i,i]]; // Index of the square for each point
// z = count(40)*.2+squareind;
// path3d = hstack(spiral,z); // 3D spiral
@ -650,7 +650,9 @@ function _rounding_offsets(edgespec,z_dir=1) =
// stroke(smooth_path(square(4), size=4, closed=true),
// closed=true,width=.1);
// Example(2D): You can alter the shape of the curve by specifying your own arbitrary tangent values
// polygon(smooth_path(square(4),tangents=1.25*[[-2,-1], [-4,1], [1,2], [6,-1]],size=0.4,closed=true));
// polygon(smooth_path(square(4),
// tangents=1.25*[[-2,-1], [-4,1], [1,2], [6,-1]],
// size=0.4,closed=true));
// Example(2D): Or you can give a different size for each segment
// polygon(smooth_path(square(4),size = [.4, .05, 1, .3],
// closed=true));
@ -952,7 +954,8 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false)
// xdistribute(spacing=5){
// offset_stroke(sharppath,closed=true, $fn=16);
// offset_stroke(sharppath, rounded=false, closed=true);
// offset_stroke(sharppath, rounded=false, chamfer=true, closed=true);
// offset_stroke(sharppath, rounded=false, chamfer=true,
// closed=true);
// }
// Example(2D): The left stroke uses flat ends with a relative angle of zero. The right hand one uses flat ends with an absolute angle of zero, so the ends are parallel to the x-axis.
// path = [[0,0],[6,2],[9,7],[8,10]];
@ -975,18 +978,24 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false)
// $fn=36;
// arc = arc(points=[[1,1],[3,4],[6,3]],n=50);
// path = [[0,0],[6,2],[9,7],[8,10]];
// offset_stroke(path, width=2, rounded=false,start=os_round(angle=-20, cut=0.4,k=.9), end=os_round(angle=-35, cut=0.4,k=.9));
// color("red")down(.1)offset_stroke(path, width=2, rounded=false,start=os_flat(-20), end=os_flat(-35));
// offset_stroke(path, width=2, rounded=false,start=os_round(angle=-20, cut=0.4,k=.9),
// end=os_round(angle=-35, cut=0.4,k=.9));
// color("red")down(.1)offset_stroke(path, width=2, rounded=false,start=os_flat(-20),
// end=os_flat(-35));
// right(9){
// offset_stroke(arc, width=2, rounded=false, start=os_round(cut=[.3,.6],angle=-45), end=os_round(angle=20,cut=[.6,0]));
// color("red")down(.1)offset_stroke(arc, width=2, rounded=false, start=os_flat(-45), end=os_flat(20));
// offset_stroke(arc, width=2, rounded=false, start=os_round(cut=[.3,.6],angle=-45),
// end=os_round(angle=20,cut=[.6,0]));
// color("red")down(.1)offset_stroke(arc, width=2, rounded=false, start=os_flat(-45),
// end=os_flat(20));
// }
// Example(2D): Negative cut values produce a flaring end. Note how the absolute angle aligns the ends of the first example withi the axes. In the second example positive and negative cut values are combined. Note also that very different cuts are needed at the start end to produce a similar looking flare.
// arc = arc(points=[[1,1],[3,4],[6,3]],n=50);
// path = [[0,0],[6,2],[9,7],[8,10]];
// offset_stroke(path, width=2, rounded=false,start=os_round(cut=-1, abs_angle=90), end=os_round(cut=-0.5, abs_angle=0),$fn=36);
// offset_stroke(path, width=2, rounded=false,start=os_round(cut=-1, abs_angle=90),
// end=os_round(cut=-0.5, abs_angle=0),$fn=36);
// right(10)
// offset_stroke(arc, width=2, rounded=false, start=os_round(cut=[-.75,-.2], angle=-45), end=os_round(cut=[-.2,.2], angle=20),$fn=36);
// offset_stroke(arc, width=2, rounded=false, start=os_round(cut=[-.75,-.2], angle=-45),
// end=os_round(cut=[-.2,.2], angle=20),$fn=36);
// Example(2D): Setting the width to a vector allows you to offset the stroke. Here with successive increasing offsets we create a set of parallel strokes
// path = [[0,0],[4,4],[8,4],[2,9],[10,10]];
// for(i=[0:.25:2])
@ -997,14 +1006,18 @@ function _path_join(paths,joint,k=0.5,i=0,result=[],relocate=true,closed=false)
// offset_stroke(path, rounded=true,width = [i,i+.08], $fn=36);
// Example(2D): In this example a spurious triangle appears. This results from overly enthusiastic validity checking. Turning validity checking off fixes it in this case.
// path = [[0,0],[4,4],[8,4],[2,9],[10,10]];
// offset_stroke(path, check_valid=true,rounded=false,width = [1.4, 1.5]);
// offset_stroke(path, check_valid=true,rounded=false,
// width = [1.4, 1.5]);
// right(2)
// offset_stroke(path, check_valid=false,rounded=false,width = [1.4, 1.5]);
// offset_stroke(path, check_valid=false,rounded=false,
// width = [1.4, 1.5]);
// Example(2D): But in this case, disabling the validity check produces an invalid result.
// path = [[0,0],[4,4],[8,4],[2,9],[10,10]];
// offset_stroke(path, check_valid=true,rounded=false,width = [1.9, 2]);
// offset_stroke(path, check_valid=true,rounded=false,
// width = [1.9, 2]);
// translate([1,-0.25])
// offset_stroke(path, check_valid=false,rounded=false,width = [1.9, 2]);
// offset_stroke(path, check_valid=false,rounded=false,
// width = [1.9, 2]);
// Example(2D): Self-intersecting paths are handled differently than with the `stroke()` module.
// $fn=16;
// path = turtle(["move",10,"left",144], repeat=4);
@ -1350,15 +1363,17 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
// rsquare = round_corners(square, method="smooth", cut=0.1, k=0.7, $fn=36);
// end_spec = os_smooth(cut=0.1, k=0.7, steps=22);
// offset_sweep(rsquare, height=1, bottom=end_spec, top=end_spec);
// Example: A nice rounded box, with a teardrop base and circular rounded interior and top
// Example(3D,Med): A nice rounded box, with a teardrop base and circular rounded interior and top
// box = square([255,50]);
// rbox = round_corners(box, method="smooth", cut=4, $fn=12);
// thickness = 2;
// difference(){
// offset_sweep(rbox, height=50, check_valid=false, steps=22, bottom=os_teardrop(r=2), top=os_circle(r=1));
// offset_sweep(rbox, height=50, check_valid=false, steps=22,
// bottom=os_teardrop(r=2), top=os_circle(r=1));
// up(thickness)
// offset_sweep(offset(rbox, r=-thickness, closed=true,check_valid=false),
// height=48, steps=22, check_valid=false, bottom=os_circle(r=4), top=os_circle(r=-1,extra=1));
// height=48, steps=22, check_valid=false,
// bottom=os_circle(r=4), top=os_circle(r=-1,extra=1));
// }
// Example: This box is much thicker, and cut in half to show the profiles. Note also that we can turn `check_valid` off for the outside and for the top inside, but not for the bottom inside. This example shows use of the direct keyword syntax without the helper functions.
// smallbox = square([75,50]);
@ -1373,32 +1388,37 @@ module offset_stroke(path, width=1, rounded=true, start, end, check_valid=true,
// offset_sweep(offset(roundbox, r=-thickness, closed=true),
// height=height-thickness, steps=22,
// bottom=["for","offset_sweep","r",6],
// top=["for","offset_sweep","type","chamfer","angle",30,"chamfer_height",-3,"extra",1,"check_valid",false]);
// top=["for","offset_sweep","type","chamfer","angle",30,
// "chamfer_height",-3,"extra",1,"check_valid",false]);
// }
// Example: A box with multiple sections and rounded dividers
// Example(3D,Med): A box with multiple sections and rounded dividers
// thickness = 2;
// box = square([255,50]);
// cutpoints = [0, 125, 190, 255];
// rbox = round_corners(box, method="smooth", cut=4, $fn=12);
// back_half(y=25, s=700)
// difference(){
// offset_sweep(rbox, height=50, check_valid=false, steps=22, bottom=os_teardrop(r=2), top=os_circle(r=1));
// offset_sweep(rbox, height=50, check_valid=false, steps=22,
// bottom=os_teardrop(r=2), top=os_circle(r=1));
// up(thickness)
// for(i=[0:2]){
// ofs = i==1 ? 2 : 0;
// hole = round_corners([[cutpoints[i]-ofs,0], [cutpoints[i]-ofs,50], [cutpoints[i+1]+ofs, 50], [cutpoints[i+1]+ofs,0]],
// hole = round_corners([[cutpoints[i]-ofs,0], [cutpoints[i]-ofs,50],
// [cutpoints[i+1]+ofs, 50], [cutpoints[i+1]+ofs,0]],
// method="smooth", cut=4, $fn=36);
// offset_sweep(offset(hole, r=-thickness, closed=true,check_valid=false),
// height=48, steps=22, check_valid=false, bottom=os_circle(r=4), top=os_circle(r=-1,extra=1));
// height=48, steps=22, check_valid=false,
// bottom=os_circle(r=4), top=os_circle(r=-1,extra=1));
// }
// }
// Example: Star shaped box
// Example(3D,Med): Star shaped box
// star = star(5, r=22, ir=13);
// rounded_star = round_corners(star, cut=flatten(repeat([.5,0],5)), $fn=24);
// thickness = 2;
// ht=20;
// difference(){
// offset_sweep(rounded_star, height=ht, bottom=["for","offset_sweep","r",4], top=["for","offset_sweep","r",1], steps=15);
// offset_sweep(rounded_star, height=ht, bottom=["for","offset_sweep","r",4],
// top=["for","offset_sweep","r",1], steps=15);
// up(thickness)
// offset_sweep(offset(rounded_star,r=-thickness,closed=true),
// height=ht-thickness, check_valid=false,
@ -1756,14 +1776,17 @@ function os_mask(mask, out=false, extra,check_valid, quality, offset) =
// k = default curvature parameter value for "smooth" roundover
// convexity = convexity setting for use with polyhedron. Default: 10
// Example: Chamfered elliptical prism. If you stretch a chamfered cylinder the chamfer will be uneven.
// convex_offset_extrude(bottom = os_chamfer(height=-2), top=os_chamfer(height=1), height=7)
// xscale(4)circle(r=6,$fn=64);
// convex_offset_extrude(bottom = os_chamfer(height=-2),
// top=os_chamfer(height=1), height=7)
// xscale(4)circle(r=6,$fn=64);
// Example: Elliptical prism with circular roundovers.
// convex_offset_extrude(bottom=os_circle(r=-2), top=os_circle(r=1), height=7,steps=10)
// xscale(4)circle(r=6,$fn=64);
// convex_offset_extrude(bottom=os_circle(r=-2),
// top=os_circle(r=1), height=7,steps=10)
// xscale(4)circle(r=6,$fn=64);
// Example: If you give a non-convex input you get a convex hull output
// right(50) linear_extrude(height=7) star(5,r=22,ir=13);
// convex_offset_extrude(bottom = os_chamfer(height=-2), top=os_chamfer(height=1), height=7, $fn=32)
// convex_offset_extrude(bottom = os_chamfer(height=-2),
// top=os_chamfer(height=1), height=7, $fn=32)
// star(5,r=22,ir=13);
function convex_offset_extrude(
height, h, l,
@ -1967,53 +1990,78 @@ function _rp_compute_patches(top, bot, rtop, rsides, ktop, ksides, concave) =
// atype = Select "hull" or "intersect" anchor types. (module only) 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. (module only) Default: "centroid"
// Example: Uniformly rounded pentagonal prism
// rounded_prism(pentagon(3), height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// rounded_prism(pentagon(3), height=3,
// joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// Example: Maximum possible rounding.
// rounded_prism(pentagon(3), height=3, joint_top=1.5, joint_bot=1.5, joint_sides=1.5);
// rounded_prism(pentagon(3), height=3,
// joint_top=1.5, joint_bot=1.5, joint_sides=1.5);
// Example: Decreasing k from the default of 0.5 to 0.3 gives a smoother round over which takes up more space, so it appears less rounded.
// rounded_prism(pentagon(3), height=3, joint_top=1.5, joint_bot=1.5, joint_sides=1.5, k=0.3, splinesteps=32);
// rounded_prism(pentagon(3), height=3, joint_top=1.5, joint_bot=1.5,
// joint_sides=1.5, k=0.3, splinesteps=32);
// Example: Increasing k from the default of 0.5 to 0.92 approximates a circular roundover, which does not have continuous curvature. Notice the visible "edges" at the boundary of the corner and edge patches.
// rounded_prism(pentagon(3), height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5, k=0.92);
// rounded_prism(pentagon(3), height=3, joint_top=0.5,
// joint_bot=0.5, joint_sides=0.5, k=0.92);
// Example: rounding just one edge
// rounded_prism(pentagon(side=3), height=3, joint_top=0.5, joint_bot=0.5, joint_sides=[0,0,0.5,0,0], splinesteps=16);
// rounded_prism(pentagon(side=3), height=3, joint_top=0.5, joint_bot=0.5,
// joint_sides=[0,0,0.5,0,0], splinesteps=16);
// Example: rounding all the edges differently
// rounded_prism(pentagon(side=3), height=3, joint_top=0.25, joint_bot=0.5, joint_sides=[1.7,.5,.7,1.2,.4], splinesteps=32);
// rounded_prism(pentagon(side=3), height=3, joint_top=0.25, joint_bot=0.5,
// joint_sides=[1.7,.5,.7,1.2,.4], splinesteps=32);
// Example: different k values for top, bottom and sides
// rounded_prism(pentagon(side=3.0), height=3.0, joint_top=1.4, joint_bot=1.4, joint_sides=0.7, k_top=0.7, k_bot=0.3, k_sides=0.5, splinesteps=48);
// rounded_prism(pentagon(side=3.0), height=3.0, joint_top=1.4, joint_bot=1.4,
// joint_sides=0.7, k_top=0.7, k_bot=0.3, k_sides=0.5, splinesteps=48);
// Example: flared bottom
// rounded_prism(pentagon(3), height=3, joint_top=1.0, joint_bot=-0.5, joint_sides=0.5);
// rounded_prism(pentagon(3), height=3, joint_top=1.0,
// joint_bot=-0.5, joint_sides=0.5);
// Example: truncated pyramid
// rounded_prism(pentagon(3), apply(scale(.7),pentagon(3)), height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// rounded_prism(pentagon(3), apply(scale(.7),pentagon(3)),
// height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// Example: top translated
// rounded_prism(pentagon(3), apply(right(2),pentagon(3)), height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// rounded_prism(pentagon(3), apply(right(2),pentagon(3)),
// height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// Example(NORENDER): top rotated: fails due to non-coplanar side faces
// rounded_prism(pentagon(3), apply(rot(45),pentagon(3)), height=3, joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// rounded_prism(pentagon(3), apply(rot(45),pentagon(3)), height=3,
// joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// Example: skew top
// rounded_prism(path3d(pentagon(3)), apply(affine3d_skew_yz(0,-20),path3d(pentagon(3),3)), joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// rounded_prism(path3d(pentagon(3)), apply(affine3d_skew_yz(0,-20),path3d(pentagon(3),3)),
// joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// Example: this rotation gives coplanar sides
// rounded_prism(path3d(square(4)), apply(yrot(-100)*right(2),path3d(square(4),3)), joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// rounded_prism(path3d(square(4)), apply(yrot(-100)*right(2),path3d(square(4),3)),
// joint_top=0.5, joint_bot=0.5, joint_sides=0.5);
// Example: a shape with concave corners
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right", "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
// rounded_prism(M, apply(up(3),M), joint_top=0.75, joint_bot=0.2, joint_sides=[.2,2.5,2,0.5,1.5,.5,2.5], splinesteps=32);
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right",
// "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
// rounded_prism(M, apply(up(3),M), joint_top=0.75, joint_bot=0.2,
// joint_sides=[.2,2.5,2,0.5,1.5,.5,2.5], splinesteps=32);
// Example: using debug mode to see the corner patch sizes, which may help figure out problems with interfering corners or invalid polyhedra. The corner patches must not intersect each other.
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right", "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
// rounded_prism(M, apply(up(3),M), joint_top=0.75, joint_bot=0.2, joint_sides=[.2,2.5,2,0.5,1.5,.5,2.5], splinesteps=16,debug=true);
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right",
// "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
// rounded_prism(M, apply(up(3),M), joint_top=0.75, joint_bot=0.2,
// joint_sides=[.2,2.5,2,0.5,1.5,.5,2.5], splinesteps=16,debug=true);
// Example: applying transformation to the previous example
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right", "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
// rounded_prism(M, apply(right(1)*scale(.75)*up(3),M), joint_top=0.5, joint_bot=0.2, joint_sides=[.2,1,1,0.5,1.5,.5,2], splinesteps=32);
// M = path3d(turtle(["left", 180, "length",3,"move", "left", "move", 3, "right",
// "move", "right", "move", 4, "right", "move", 3, "right", "move", 2]));
// rounded_prism(M, apply(right(1)*scale(.75)*up(3),M), joint_top=0.5, joint_bot=0.2,
// joint_sides=[.2,1,1,0.5,1.5,.5,2], splinesteps=32);
// Example: this example shows most of the different types of patches that rounded_prism creates. Note that some of the patches are close to interfering with each other across the top of the polyhedron, which would create an invalid result.
// N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135, "move", sqrt(2), "left", "move", sqrt(2), "right", 135, "move", 2]));
// N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135,"move", sqrt(2),
// "left", "move", sqrt(2), "right", 135, "move", 2]));
// rounded_prism(N, height=3, joint_bot=0.5, joint_top=1.25, joint_sides=[[1,1.75],0,.5,.5,2], debug=true);
// Example: This object has different scales on its different axies. Here is the largest symmetric rounding that fits. Note that the rounding is slightly smaller than the object dimensions because of roundoff error.
// rounded_prism(square([100.1,30.1]), height=8.1, joint_top=4, joint_bot=4, joint_sides=15, k_sides=0.3, splinesteps=32);
// rounded_prism(square([100.1,30.1]), height=8.1, joint_top=4, joint_bot=4,
// joint_sides=15, k_sides=0.3, splinesteps=32);
// Example: Using asymetric rounding enables a much more rounded form:
// rounded_prism(square([100.1,30.1]), height=8.1, joint_top=[15,4], joint_bot=[15,4], joint_sides=[[15,50],[50,15],[15,50],[50,15]], k_sides=0.3, splinesteps=32);
// rounded_prism(square([100.1,30.1]), height=8.1, joint_top=[15,4], joint_bot=[15,4],
// joint_sides=[[15,50],[50,15],[15,50],[50,15]], k_sides=0.3, splinesteps=32);
// Example: Flaring the top upward instead of outward. The bottom has an asymmetric rounding with a small flare but a large rounding up the side.
// rounded_prism(pentagon(3), height=3, joint_top=[1,-1], joint_bot=[-0.5,2], joint_sides=0.5);
// rounded_prism(pentagon(3), height=3, joint_top=[1,-1],
// joint_bot=[-0.5,2], joint_sides=0.5);
// Example: Sideways polygons:
// rounded_prism(apply(yrot(95),path3d(hexagon(3))), apply(yrot(95), path3d(hexagon(3),3)), joint_top=2, joint_bot=1, joint_sides=1);
// rounded_prism(apply(yrot(95),path3d(hexagon(3))), apply(yrot(95), path3d(hexagon(3),3)),
// joint_top=2, joint_bot=1, joint_sides=1);
// Example: Chamfer a polyhedron by setting splinesteps to 1
// N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135, "move", sqrt(2), "left", "move", sqrt(2), "right", 135, "move", 2]));
// N = apply(rot(180)*yscale(.8),turtle(["length",3,"left", "move", 2, "right", 135,"move", sqrt(2),
// "left", "move", sqrt(2), "right", 135, "move", 2]));
// rounded_prism(N, height=3, joint_bot=-0.3, joint_top=.4, joint_sides=[.75,0,.2,.2,.7], splinesteps=1);
@ -2219,7 +2267,8 @@ function _circle_mask(r) =
// difference(){
// cyl(r=10.5, h=10);
// cyl(r=9.5, h=11);
// bent_cutout_mask(10, 1.05, apply(xscale(3),circle(r=3)),$fn=64);
// bent_cutout_mask(10, 1.05, apply(xscale(3),circle(r=3)),
// $fn=64);
// }
// }
// Example: An elliptical hole in a thick cylinder
@ -2237,7 +2286,9 @@ function _circle_mask(r) =
// difference(){
// cyl(r=10.5, h=10, $fn=128);
// cyl(r=9.5, h=11, $fn=128);
// bent_cutout_mask(10, 1.05, apply(scale(3),supershape(step=2,m1=5, n1=0.3,n2=1.7)),$fn=32);
// bent_cutout_mask(10, 1.05,
// apply(scale(3),
// supershape(step=2,m1=5, n1=0.3,n2=1.7)),$fn=32);
// }
// }
// Example: this shape is invalid due to self-intersections at the inner corners
@ -2246,7 +2297,9 @@ function _circle_mask(r) =
// difference(){
// cylinder(r=10.5, h=10,center=true);
// cylinder(r=9.5, h=11,center=true);
// bent_cutout_mask(10, 1.05, apply(scale(3),supershape(step=2,m1=5, n1=0.1,n2=1.7)),$fn=32);
// bent_cutout_mask(10, 1.05,
// apply(scale(3),
// supershape(step=2,m1=5, n1=0.1,n2=1.7)),$fn=32);
// }
// }
// Example: increasing the step gives a valid shape, but the shape looks terrible with so few points.
@ -2255,7 +2308,9 @@ function _circle_mask(r) =
// difference(){
// cylinder(r=10.5, h=10,center=true);
// cylinder(r=9.5, h=11,center=true);
// bent_cutout_mask(10, 1.05, apply(scale(3),supershape(step=12,m1=5, n1=0.1,n2=1.7)),$fn=32);
// bent_cutout_mask(10, 1.05,
// apply(scale(3),
// supershape(step=12,m1=5, n1=0.1,n2=1.7)),$fn=32);
// }
// }
// Example: uniform resampling produces a somewhat better result, but room remains for improvement. The lesson is that concave corners in your cutout cause trouble. To get a very good result we need to non-uniformly sample the supershape with more points at the star tips and few points at the inner corners.
@ -2264,7 +2319,11 @@ function _circle_mask(r) =
// difference(){
// cylinder(r=10.5, h=10,center=true);
// cylinder(r=9.5, h=11,center=true);
// bent_cutout_mask(10, 1.05, apply(scale(3),resample_path(supershape(step=1,m1=5, n1=0.10,n2=1.7),60,closed=true)),$fn=32);
// bent_cutout_mask(10, 1.05,
// apply(scale(3), resample_path(
// supershape(step=1,m1=5, n1=0.10,n2=1.7),
// 60,closed=true)),
// $fn=32);
// }
// }
// Example: The cutout spans 177 degrees. If you decrease the tube radius to 2.5 the cutout spans over 180 degrees and the model fails.
@ -2273,7 +2332,9 @@ function _circle_mask(r) =
// $fn=128;
// difference(){
// tube(or=r, wall=1, h=10, anchor=CENTER);
// bent_cutout_mask(r-0.5, 1.05, apply(scale(3),supershape(step=1,m1=5, n1=0.15,n2=1.7)),$fn=32);
// bent_cutout_mask(r-0.5, 1.05,
// apply(scale(3),
// supershape(step=1,m1=5, n1=0.15,n2=1.7)),$fn=32);
// }
// }
// Example: A square hole is not as simple as it seems. The model valid, but wrong, because the square didn't have enough samples to follow the curvature of the cylinder.
@ -2291,7 +2352,8 @@ function _circle_mask(r) =
// $fn=128;
// difference(){
// tube(or=r, wall=2, h=45);
// bent_cutout_mask(r-1, 2.1, subdivide_path(back(5,p=square([18,18])),64,closed=true));
// bent_cutout_mask(r-1, 2.1,
// subdivide_path(back(5,p=square([18,18])),64,closed=true));
// }
// }
// Example: Rounding just the exterior corners of this star avoids the problems we had above with concave corners of the supershape, as long as we don't oversample the star.
@ -2300,7 +2362,12 @@ function _circle_mask(r) =
// $fn=128;
// difference(){
// tube(or=r, wall=2, h=45);
// bent_cutout_mask(r-1, 2.1, apply(back(15),subdivide_path(round_corners(star(n=7,ir=5,or=10), cut=flatten(repeat([0.5,0],7)),$fn=32),14*15,closed=true)));
// bent_cutout_mask(r-1, 2.1,
// apply(back(15),
// subdivide_path(
// round_corners(star(n=7,ir=5,or=10),
// cut=flatten(repeat([0.5,0],7)),$fn=32),
// 14*15,closed=true)));
// }
// }
// Example(2D): Cutting a slot in a cylinder is tricky if you want rounded corners at the top. This slot profile has slightly angled top edges to blend into the top edge of the cylinder.

View file

@ -2568,19 +2568,20 @@ function _cut_interp(pathcut, path, data) =
// top = direction or list of directions pointing toward the top of the text
// reverse = reverse the letters if true. Not allowed for 2D path. Default: false
// textmetrics = if set to true and lettersize is not given then use the experimental textmetrics feature. You must be running a dev snapshot that includes this feature and have the feature turned on in your preferences. Default: false
// Example: The examples use Courier, a monospaced font. The width is 1/1.2 times the specified size for this font. This text could wrap around a cylinder.
// kern = scalar or array giving size adjustments for each letter. Default: 0
// Example(3D,NoScales): The examples use Courier, a monospaced font. The width is 1/1.2 times the specified size for this font. This text could wrap around a cylinder.
// path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2);
// Example: By setting the normal to UP we can get text that lies flat, for writing around the edge of a disk:
// Example(3D,NoScales): By setting the normal to UP we can get text that lies flat, for writing around the edge of a disk:
// path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, normal=UP);
// Example: If we want text that reads from the other side we can use reverse. Note we have to reverse the direction of the path and also set the reverse option.
// Example(3D,NoScales): If we want text that reads from the other side we can use reverse. Note we have to reverse the direction of the path and also set the reverse option.
// path = reverse(path3d(arc(100, r=25, angle=[65, 190])));
// color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, reverse=true);
// Example: text debossed onto a cylinder in a spiral. The text is 1 unit deep because it is half in, half out.
// Example(3D,Med,NoScales): text debossed onto a cylinder in a spiral. The text is 1 unit deep because it is half in, half out.
// text = ("A long text example to wrap around a cylinder, possibly for a few times.");
// L = 5*len(text);
// maxang = 360*L/(PI*50);
@ -2589,22 +2590,22 @@ function _cut_interp(pathcut, path, data) =
// cyl(d=50, l=50, $fn=120);
// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2);
// }
// Example: Same example but text embossed. Make sure you have enough depth for the letters to fully overlap the object.
// Example(3D,Med,NoScales): Same example but text embossed. Make sure you have enough depth for the letters to fully overlap the object.
// text = ("A long text example to wrap around a cylinder, possibly for a few times.");
// L = 5*len(text);
// maxang = 360*L/(PI*50);
// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]];
// cyl(d=50, l=50, $fn=120);
// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2);
// Example: Here the text baseline sits on the path. (Note the default orientation makes text readable from below, so we specify the normal.)
// Example(3D,NoScales): Here the text baseline sits on the path. (Note the default orientation makes text readable from below, so we specify the normal.)
// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]);
// color("red")stroke(path,width=.2);
// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", normal=FRONT);
// Example: If we use top to orient the text upward, the text baseline is no longer aligned with the path.
// Example(3D,NoScales): If we use top to orient the text upward, the text baseline is no longer aligned with the path.
// path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]);
// color("red")stroke(path,width=.2);
// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", top=UP);
// Example: This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal which is different at every path point.
// Example(3D,Med,NoScales): This sine wave wrapped around the cylinder has a twisting normal that produces wild letter layout. We fix it with a custom normal which is different at every path point.
// path = [for(theta = [0:360]) [25*cos(theta), 25*sin(theta), 4*cos(theta*4)]];
// normal = [for(theta = [0:360]) [cos(theta), sin(theta),0]];
// zrot(-120)
@ -2612,19 +2613,25 @@ function _cut_interp(pathcut, path, data) =
// cyl(r=25, h=20, $fn=120);
// path_text(path, "A sine wave wiggles", font="Courier", lettersize=5/1.2, size=5, normal=normal);
// }
// Example: The path center of curvature changes, and the text flips.
// Example(3D,Med,NoScales): The path center of curvature changes, and the text flips.
// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180])))));
// color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2);
// Example: We can fix it with top:
// Example(3D,Med,NoScales): We can fix it with top:
// path = zrot(-120,p=path3d( concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180])))));
// color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2, top=UP);
// Example(2D): With a 2D path instead of 3D there's no ambiguity about direction and it works by default:
// Example(2D,NoScales): With a 2D path instead of 3D there's no ambiguity about direction and it works by default:
// path = zrot(-120,p=concat(arc(100, r=25, angle=[0,90]), back(50,p=arc(100, r=25, angle=[268, 180]))));
// color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier");
module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false)
// Example(3D,NoScales): The kern parameter lets you adjust the letter spacing either with a uniform value for each letter, or with an array to make adjustments throughout the text. Here we show a case where adding some extra space gives a better look in a tight circle. When textmetrics are off, `lettersize` can do this job, but with textmetrics, you'll need to use `kern` to make adjustments relative to the text metric sizes.
// path = path3d(arc(100, r=12, angle=[150, 450]));
// color("red")stroke(path, width=.3);
// kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1,1];
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP);
module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false, textmetrics=false, kern=0)
{
dummy2=assert(is_path(path,[2,3]),"Must supply a 2d or 3d path")
assert(num_defined([normal,top])<=1, "Cannot define both \"normal\" and \"top\"");
@ -2647,10 +2654,14 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
: is_def(top) ? top
: undef;
lsize = is_def(lettersize) ? force_list(lettersize, len(text))
kern = force_list(kern, len(text));
dummy3 = assert(is_list(kern) && len(kern)==len(text), "kern must be a scalar or list whose length is len(text)");
lsize = kern + (
is_def(lettersize) ? force_list(lettersize, len(text))
: textmetrics ? [for(letter=text) let(t=textmetrics(letter, font=font, size=size)) t.advance[0]]
: assert(false, "textmetrics disabled: Must specify letter size");
: assert(false, "textmetrics disabled: Must specify letter size")
);
textlength = sum(lsize);
dummy1 = assert(textlength<=path_length(path),"Path is too short for the text");