Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Richard Milewski 2024-09-27 16:48:29 -07:00
commit d050abe0e0
4 changed files with 78 additions and 30 deletions

View file

@ -437,7 +437,7 @@ function _inherit_gear_thickness(thickness,dflt=10) =
// in fact, the worm can be regarded as a type of helical gear at a very extreme angle, where the teeth wrap // in fact, the worm can be regarded as a type of helical gear at a very extreme angle, where the teeth wrap
// around the gear. The worm mates with the "worm gear" which is also called the "worm wheel". The worm gear // around the gear. The worm mates with the "worm gear" which is also called the "worm wheel". The worm gear
// resembles a helical gear at a very slight angle. // resembles a helical gear at a very slight angle.
// Figure(3D,Med,NoAxes,VPT=[38.1941,-7.67869,7.95996],VPR=[56.4,0,25],VPD=361.364): Worm drive assembly, with worm on the left and worm gear (worm wheel) on the right. When the worm turns its screwing action drives the worm gear. // Figure(3D,Med,NoAxes,VPT=[38.1941,-7.67869,7.95996],VPR=[56.4,0,25],VPD=361.364): Worm drive assembly, with worm on the left and worm gear (worm wheel) on the right. When the worm turns, its screwing action drives the worm gear.
// starts=2; // starts=2;
// ps=0; // ps=0;
// dist_ba=0; // dist_ba=0;
@ -453,6 +453,10 @@ function _inherit_gear_thickness(thickness,dflt=10) =
// teeth=30, // teeth=30,
// worm_diam=44,profile_shift=ps, // worm_diam=44,profile_shift=ps,
// worm_starts=starts,backlash=gear_ba); // worm_starts=starts,backlash=gear_ba);
// color("black"){
// rot($vpr)left(45)back(25)text3d("worm",size=8);
// rot($vpr)right(55)back(27)text3d("worm gear",size=8);
// }
// Continues: // Continues:
// A close look at the worm gear reveals that it differs significantly from a helical or spur gear. // A close look at the worm gear reveals that it differs significantly from a helical or spur gear.
// This gear is an "enveloping" gear, which is designed to follow the curved profile of the worm, // This gear is an "enveloping" gear, which is designed to follow the curved profile of the worm,
@ -519,8 +523,8 @@ function _inherit_gear_thickness(thickness,dflt=10) =
// Since self-locking is associated with friction, self-locking drives have lower efficiency, // Since self-locking is associated with friction, self-locking drives have lower efficiency,
// usually less than 50%. Worm drive efficiency can exceed 90% if self-locking is not required. One consideration // usually less than 50%. Worm drive efficiency can exceed 90% if self-locking is not required. One consideration
// with self-locking systems is that if the worm gear moves a large mass and the drive is suddenly shut off, the // with self-locking systems is that if the worm gear moves a large mass and the drive is suddenly shut off, the
// worm wheel is still trying to move due to inertia, which can create large loads that fracture the worm. // worm gear is still trying to move due to inertia, which can create large loads that fracture the worm.
// In such cases, the worm cannot be stopped abruptly but must rotate a little further (called "over travel") // In such cases, the worm cannot be stopped abruptly but must be allowed to rotate a little further (called "over travel")
// after switching off the drive. // after switching off the drive.
// Subsection: Bevel Gears // Subsection: Bevel Gears
// Bevel gearing is another way of dealing with intersecting gear shafts. For bevel gears, the teeth centers lie on // Bevel gearing is another way of dealing with intersecting gear shafts. For bevel gears, the teeth centers lie on

View file

@ -2675,7 +2675,7 @@ Access to the derivative smoothing parameter?
// Note that the joint with a curved base may significantly extend the length of the joiner prism: it's total length will often be larger than // Note that the joint with a curved base may significantly extend the length of the joiner prism: it's total length will often be larger than
// the length you request. // the length you request.
// . // .
// For the cylinder and spherical objects you may with to joint a prism to the concave surface. You can do this by setting a negative // For the cylinder and spherical objects you may wish to joint a prism to the concave surface. You can do this by setting a negative
// radius for the base or auxiliary object. When `base_r` is negative, and the joiner prism axis is vertical, the prism root will be **below** the // radius for the base or auxiliary object. When `base_r` is negative, and the joiner prism axis is vertical, the prism root will be **below** the
// XY plane. In this case it is actually possible to use the same object for base and aux and you can get a joiner prism that crosses a cylindrical // XY plane. In this case it is actually possible to use the same object for base and aux and you can get a joiner prism that crosses a cylindrical
// or spherical hole. // or spherical hole.
@ -3107,7 +3107,7 @@ Access to the derivative smoothing parameter?
// flower = [for(theta=lerpn(0,360,180,endpoint=false)) // flower = [for(theta=lerpn(0,360,180,endpoint=false))
// (15+1.3*sin(6*theta))*[cos(theta),sin(theta)]]; // (15+1.3*sin(6*theta))*[cos(theta),sin(theta)]];
// aux_T = up(12)*xrot(-22); // aux_T = up(12)*xrot(-22);
// join_prism(flower,base="plane",fillet=4, n=12, // join_prism(flower,base="plane",fillet=2.75, n=12,
// aux="plane", aux_T=aux_T); // aux="plane", aux_T=aux_T);
// multmatrix(aux_T)cuboid([42,42,4],anchor=BOT); // multmatrix(aux_T)cuboid([42,42,4],anchor=BOT);
// cuboid([40,40,4],anchor=TOP); // cuboid([40,40,4],anchor=TOP);
@ -3115,7 +3115,7 @@ Access to the derivative smoothing parameter?
// flower = [for(theta=lerpn(0,360,180,endpoint=false)) // flower = [for(theta=lerpn(0,360,180,endpoint=false))
// (15+1.3*sin(6*theta))*[cos(theta),sin(theta)]]; // (15+1.3*sin(6*theta))*[cos(theta),sin(theta)]];
// aux_T = xrot(-22)*up(12); // aux_T = xrot(-22)*up(12);
// join_prism(flower,base="plane",fillet=4, n=12, // join_prism(flower,base="plane",fillet=2.75, n=12,
// aux="plane", aux_T=aux_T); // aux="plane", aux_T=aux_T);
// multmatrix(aux_T)cuboid([42,42,4],anchor=BOT); // multmatrix(aux_T)cuboid([42,42,4],anchor=BOT);
// cuboid([43,43,4],anchor=TOP); // cuboid([43,43,4],anchor=TOP);
@ -3212,6 +3212,45 @@ Access to the derivative smoothing parameter?
// multmatrix(aux_T) // multmatrix(aux_T)
// linear_sweep(flower,height=60,center=true,orient=RIGHT); // linear_sweep(flower,height=60,center=true,orient=RIGHT);
// linear_sweep(flower,height=60,center=true,orient=RIGHT); // linear_sweep(flower,height=60,center=true,orient=RIGHT);
// Example(3D,NoScales): By setting the base and auxiliary to the same thing you can create a hole cutting mask with rounded ends.
// difference(){
// spheroid(r=30,circum=true);
// join_prism(circle(r=15),base="sphere",base_r=-30, n=15,
// aux="sphere",aux_r=-30,fillet=8, overlap=17);
// }
// Example(3D,VPT=[0.59633,-3.01826,-3.89606],VPR=[129.2,0,26.4],VPD=192.044,NoScales): Here we have rotated the auxiliary sphere which results in a hole that is off-center through the sphere. Because we rotate the auxiliary object, both ends of the prism have moved. Note that setting k to a large value better matches the bezier curve to the curvature of the sphere, resulting in a better result.
// difference(){
// spheroid(r=30,circum=true);
// join_prism(circle(r=15),base="sphere",base_r=-30, n=15,
// aux="sphere",aux_T=xrot(30), aux_r=-30,fillet=8, overlap=17, k=0.9);
// }
// Example(3D,VPT=[-12.5956,-5.1125,-0.322237],VPR=[82.3,0,116.7],VPD=213.382,NoScales): Here we adjust just the auxiliary end, which note is at the bottom. We rotate it by 45 deg, but this rotation would normally be relative to the other prism end, so we add a centerpoint based on the radius so that the rotation is relative to the sphere center instead.
// difference(){
// spheroid(r=30,circum=true);
// join_prism(circle(r=15),base="sphere",base_r=-30, n=15,
// aux="sphere",prism_end_T=xrot(45,cp=[0,0,-30]), aux_r=-30,fillet=8, overlap=17, k=0.9);
// }
// Example(3D,NoScales,VPT=[12.3373,11.6037,-1.87883],VPR=[40.3,0,323.4],VPD=292.705): A diagonal hole through a cylinder with rounded ends, created by shifting the auxiliary prism end along the prism length.
// back_half(200)
// difference(){
// right(15)xcyl(r=30,l=100,circum=true);
// join_prism(circle(r=15),base="cyl",base_r=-30, n=15,
// aux="cyl",prism_end_T=right(35),aux_r=-30,fillet=7, overlap=17);
// }
// Example(3D,NoScales,VPT=[-7.63774,-0.808304,13.8874],VPR=[46.6,0,71.2],VPD=237.091): A hole created by shifting along prism width.
// left_half()
// difference(){
// xcyl(r=30,l=100,circum=true);
// join_prism(circle(r=15),base="cyl",base_r=-30, n=15,
// aux="cyl",prism_end_T=fwd(9),aux_r=-30,fillet=7, overlap=17);
// }
// Example(3D,NoScales,VPT=[1.99307,-2.05618,-0.363144],VPR=[64.8,0,15],VPD=237.091): Shifting the auxiliary cylinder changes both ends of the prism
// back_half(200)
// difference(){
// xcyl(r=30,l=100,circum=true);
// join_prism(circle(r=15),base="cyl",base_r=-30, n=15,
// aux="cyl",aux_T=right(20),aux_r=-30,fillet=7, overlap=17);
// }
// Example(3D): Positioning a joiner prism as an attachment // Example(3D): Positioning a joiner prism as an attachment
// cuboid([20,30,40]) // cuboid([20,30,40])
// attach(RIGHT,"root") // attach(RIGHT,"root")
@ -3308,8 +3347,11 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
base_r=default(base_r,0), base_r=default(base_r,0),
polygon=clockwise_polygon(polygon), polygon=clockwise_polygon(polygon),
start_center = CENTER, start_center = CENTER,
aux_T_horiz = submatrix(aux_T,[0:2],[0:2]) == ident(3) && aux_T[2][3]==0,
dir = aux=="none" ? apply(aux_T,UP) dir = aux=="none" ? apply(aux_T,UP)
: apply(aux_T,CENTER) == CENTER ? apply(aux_T,UP) : aux_T_horiz && in_list([base,aux], [["sphere","sphere"], ["cyl","cylinder"],["cylinder","cyl"], ["cyl","cyl"], ["cylinder", "cylinder"]]) ?
unit(apply(aux_T, aux_r*UP))
: apply(aux_T,CENTER)==CENTER ? apply(aux_T,UP)
: apply(aux_T,CENTER), : apply(aux_T,CENTER),
flip = short ? -1 : 1, flip = short ? -1 : 1,
start = base=="sphere" ? start = base=="sphere" ?
@ -3317,6 +3359,7 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
assert(answer,"Prism center doesn't intersect sphere (base)") assert(answer,"Prism center doesn't intersect sphere (base)")
answer answer
: base=="cyl" || base=="cylinder" ? : base=="cyl" || base=="cylinder" ?
assert(dir.y!=0 || dir.z!=0, "Prism direction parallel to the cylinder")
let( let(
mapped = apply(yrot(90),[CENTER,flip*dir]), mapped = apply(yrot(90),[CENTER,flip*dir]),
answer = _cyl_line_intersection(abs(base_r),mapped,sign(base_r)*mapped[1]) answer = _cyl_line_intersection(abs(base_r),mapped,sign(base_r)*mapped[1])
@ -3384,14 +3427,14 @@ function join_prism(polygon, base, base_r, base_d, base_T=IDENT,
base_trans = rot_inverse(base_T), base_trans = rot_inverse(base_T),
base_top = apply(base_trans, truetop), base_top = apply(base_trans, truetop),
base_bot = apply(base_trans, truebot), base_bot = apply(base_trans, truebot),
botmesh = apply(base_T,_prism_fillet("base", base, base_r, base_bot, base_top, base_fillet, base_k, n, base_overlap,base_uniform,debug)), botmesh = apply(base_T,_prism_fillet("base", base, base_r, base_bot, base_top, base_fillet, base_k, base_n, base_overlap,base_uniform,debug)),
aux_trans = rot_inverse(aux_T), aux_trans = rot_inverse(aux_T),
aux_top = apply(aux_trans, reverse_polygon(truetop)), aux_top = apply(aux_trans, reverse_polygon(truetop)),
aux_bot = apply(aux_trans, reverse_polygon(truebot)), aux_bot = apply(aux_trans, reverse_polygon(truebot)),
topmesh_reversed = _prism_fillet("aux",aux, aux_r, aux_top, aux_bot, aux_fillet, aux_k, n, aux_overlap,aux_uniform,debug), topmesh_reversed = _prism_fillet("aux",aux, aux_r, aux_top, aux_bot, aux_fillet, aux_k, aux_n, aux_overlap,aux_uniform,debug),
topmesh = apply(aux_T,[for(i=[len(topmesh_reversed)-1:-1:0]) reverse_polygon(topmesh_reversed[i])]), topmesh = apply(aux_T,[for(i=[len(topmesh_reversed)-1:-1:0]) reverse_polygon(topmesh_reversed[i])]),
round_dir = select(topmesh,-1)-botmesh[0], round_dir = select(topmesh,-1)-botmesh[0],
roundings_cross = [for(i=idx(topmesh)) if (round_dir[i]*(truetop[i]-truebot[i])<0) i], roundings_cross = [for(i=idx(truetop)) if (round_dir[i]*(truetop[i]-truebot[i])<0) i],
vnf = vnf_vertex_array(concat(topmesh,botmesh),col_wrap=true, caps=true, reverse=true) vnf = vnf_vertex_array(concat(topmesh,botmesh),col_wrap=true, caps=true, reverse=true)
) )
assert(debug || roundings_cross==[],"Roundings from the two ends cross on the prism: decrease size of roundings") assert(debug || roundings_cross==[],"Roundings from the two ends cross on the prism: decrease size of roundings")
@ -3409,6 +3452,7 @@ function _fix_angle_list(list,ind=0, result=[]) =
// intersection with cylinder of radius R oriented on Z axis, with infinite extent // intersection with cylinder of radius R oriented on Z axis, with infinite extent
// if ref is given, return point with larger inner product with ref. // if ref is given, return point with larger inner product with ref.
function _cyl_line_intersection(R, line, ref) = function _cyl_line_intersection(R, line, ref) =
assert(point2d(line[1]-line[0]) != [0,0], "Prism appears to be parallel to cylinder. Unable to find prism endpoints.")
let( let(
line2d = path2d(line), line2d = path2d(line),
cisect = circle_line_intersection(r=R, cp=[0,0], line=line2d) cisect = circle_line_intersection(r=R, cp=[0,0], line=line2d)

View file

@ -2021,7 +2021,7 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) =
// Arguments: // Arguments:
// text = Text to create. // text = Text to create.
// size = The font will be created at this size divided by 0.72. Default: 10 // size = The font will be created at this size divided by 0.72. Default: 10
// font = Font to use. Default: "Liberation Sans" // font = Font to use. Default: "Liberation Sans" (standard OpenSCAD default)
// --- // ---
// halign = If given, specifies the horizontal alignment of the text. `"left"`, `"center"`, or `"right"`. Overrides `anchor=`. // halign = If given, specifies the horizontal alignment of the text. `"left"`, `"center"`, or `"right"`. Overrides `anchor=`.
// valign = If given, specifies the vertical alignment of the text. `"top"`, `"center"`, `"baseline"` or `"bottom"`. Overrides `anchor=`. // valign = If given, specifies the vertical alignment of the text. `"top"`, `"center"`, `"baseline"` or `"bottom"`. Overrides `anchor=`.
@ -2036,7 +2036,7 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) =
// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. // str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector.
// Examples(2D): // Examples(2D):
// text("Foobar", size=10); // text("Foobar", size=10);
// text("Foobar", size=12, font="Helvetica"); // text("Foobar", size=12, font="Liberation Mono");
// text("Foobar", anchor=CENTER); // text("Foobar", anchor=CENTER);
// text("Foobar", anchor=str("baseline",CENTER)); // text("Foobar", anchor=str("baseline",CENTER));
// Example: Using line_copies() distributor // Example: Using line_copies() distributor
@ -2047,7 +2047,7 @@ function reuleaux_polygon(n=3, r, d, anchor=CENTER, spin=0) =
// txt = "This is the string"; // txt = "This is the string";
// arc_copies(r=50, n=len(txt), sa=0, ea=180) // arc_copies(r=50, n=len(txt), sa=0, ea=180)
// text(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90); // text(select(txt,-1-$idx), size=10, anchor=str("baseline",CENTER), spin=-90);
module text(text, size=10, font="Helvetica", halign, valign, spacing=1.0, direction="ltr", language="en", script="latin", anchor="baseline", spin=0) { module text(text, size=10, font, halign, valign, spacing=1.0, direction="ltr", language="en", script="latin", anchor="baseline", spin=0) {
no_children($children); no_children($children);
dummy1 = dummy1 =
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor)) assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor))

View file

@ -3355,12 +3355,12 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) =
// ycenter = Anchor center is relative to the actualy y direction center of the text // ycenter = Anchor center is relative to the actualy y direction center of the text
// Examples: // Examples:
// text3d("Fogmobar", h=3, size=10); // text3d("Fogmobar", h=3, size=10);
// text3d("Fogmobar", h=2, size=12, font="Helvetica"); // text3d("Fogmobar", h=2, size=12, font=":style=bold");
// text3d("Fogmobar", h=2, anchor=CENTER); // text3d("Fogmobar", h=2, anchor=CENTER);
// text3d("Fogmobar", h=2, anchor=CENTER, atype="ycenter"); // text3d("Fogmobar", h=2, anchor=CENTER, atype="ycenter");
// text3d("Fogmobar", h=2, anchor=RIGHT); // text3d("Fogmobar", h=2, anchor=RIGHT);
// text3d("Fogmobar", h=2, anchor=RIGHT+BOT, atype="ycenter"); // text3d("Fogmobar", h=2, anchor=RIGHT+BOT, atype="ycenter");
module text3d(text, h, size=10, font="Helvetica", spacing=1.0, direction="ltr", language="en", script="latin", module text3d(text, h, size=10, font, spacing=1.0, direction="ltr", language="en", script="latin",
height, thickness, atype, center=false, height, thickness, atype, center=false,
anchor, spin=0, orient=UP) { anchor, spin=0, orient=UP) {
no_children($children); no_children($children);
@ -3438,7 +3438,7 @@ function _cut_interp(pathcut, path, data) =
// . // .
// If you do not have the experimental textmetrics feature enabled then you must specify the space for the letters // If you do not have the experimental textmetrics feature enabled then you must specify the space for the letters
// using lettersize, which can be a scalar or array. You will have the easiest time getting good results by using // using lettersize, which can be a scalar or array. You will have the easiest time getting good results by using
// a monospace font such as Courier. Note that even with text metrics, spacing may be different because path_text() // a monospace font such as "Liberation Mono". Note that even with text metrics, spacing may be different because path_text()
// doesn't do kerning to adjust positions of individual glyphs. Also if your font has ligatures they won't be used. // doesn't do kerning to adjust positions of individual glyphs. Also if your font has ligatures they won't be used.
// . // .
// By default letters appear centered on the path. The offset can be specified to shift letters toward the reader (in // By default letters appear centered on the path. The offset can be specified to shift letters toward the reader (in
@ -3475,7 +3475,7 @@ function _cut_interp(pathcut, path, data) =
// text = text to create // text = text to create
// size = The font will be created at this size divided by 0.72. // size = The font will be created at this size divided by 0.72.
// thickness / h / height = thickness of letters (not allowed for 2D path) // thickness / h / height = thickness of letters (not allowed for 2D path)
// font = font to use. Default: "Liberation Sans" // font = Font to use. Default: "Liberation Sans" (standard OpenSCAD default)
// --- // ---
// lettersize = scalar or array giving size of letters // lettersize = scalar or array giving size of letters
// center = center text on the path instead of starting at the first point. Default: false // center = center text on the path instead of starting at the first point. Default: false
@ -3488,18 +3488,18 @@ function _cut_interp(pathcut, path, data) =
// kern = scalar or array giving spacing adjusments between each letter. If it's an array it should have one less entry than the text string. Default: 0 // kern = scalar or array giving spacing adjusments between each letter. If it's an array it should have one less entry than the text string. Default: 0
// language = text language, passed to OpenSCAD `text()`. Default: "en" // language = text language, passed to OpenSCAD `text()`. Default: "en"
// script = text script, passed to OpenSCAD `text()`. Default: "latin" // script = text script, passed to OpenSCAD `text()`. Default: "latin"
// 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. // Example(3D,NoScales): The examples use Liberation Mono, 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])); // path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2); // path_text(path, "Example text", font="Liberation Mono", size=5, lettersize = 5/1.2);
// Example(3D,NoScales): 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])); // path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, normal=UP); // path_text(path, "Example text", font="Liberation Mono", size=5, lettersize = 5/1.2, normal=UP);
// 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. // 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]))); // path = reverse(path3d(arc(100, r=25, angle=[65, 190])));
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, reverse=true); // path_text(path, "Example text", font="Liberation Mono", size=5, lettersize = 5/1.2, reverse=true);
// 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. // 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."); // text = ("A long text example to wrap around a cylinder, possibly for a few times.");
// L = 5*len(text); // L = 5*len(text);
@ -3507,7 +3507,7 @@ function _cut_interp(pathcut, path, data) =
// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]]; // spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]];
// difference(){ // difference(){
// cyl(d=50, l=50, $fn=120); // cyl(d=50, l=50, $fn=120);
// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2); // path_text(spiral, text, size=5, lettersize=5/1.2, font="Liberation Mono", thickness=2);
// } // }
// Example(3D,Med,NoScales): 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."); // text = ("A long text example to wrap around a cylinder, possibly for a few times.");
@ -3515,40 +3515,40 @@ function _cut_interp(pathcut, path, data) =
// maxang = 360*L/(PI*50); // maxang = 360*L/(PI*50);
// spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]]; // spiral = [for(a=[0:1:maxang]) [25*cos(a), 25*sin(a), 10-30/maxang*a]];
// cyl(d=50, l=50, $fn=120); // cyl(d=50, l=50, $fn=120);
// path_text(spiral, text, size=5, lettersize=5/1.2, font="Courier", thickness=2); // path_text(spiral, text, size=5, lettersize=5/1.2, font="Liberation Mono", thickness=2);
// 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.) // 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]]); // path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]);
// color("red")stroke(path,width=.2); // color("red")stroke(path,width=.2);
// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", normal=FRONT); // path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Liberation Mono", normal=FRONT);
// Example(3D,NoScales): 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]]); // path = arc(100, points = [[-20, 0, 20], [0,0,5], [20,0,20]]);
// color("red")stroke(path,width=.2); // color("red")stroke(path,width=.2);
// path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Courier", top=UP); // path_text(path, "Example Text", size=5, lettersize=5/1.2, font="Liberation Mono", top=UP);
// 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. // 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)]]; // 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]]; // normal = [for(theta = [0:360]) [cos(theta), sin(theta),0]];
// zrot(-120) // zrot(-120)
// difference(){ // difference(){
// cyl(r=25, h=20, $fn=120); // cyl(r=25, h=20, $fn=120);
// path_text(path, "A sine wave wiggles", font="Courier", lettersize=5/1.2, size=5, normal=normal); // path_text(path, "A sine wave wiggles", font="Liberation Mono", lettersize=5/1.2, size=5, normal=normal);
// } // }
// Example(3D,Med,NoScales): 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]))))); // 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); // color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2); // path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Liberation Mono", thickness=2);
// Example(3D,Med,NoScales): 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]))))); // 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); // color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier", thickness=2, top=UP); // path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Liberation Mono", thickness=2, top=UP);
// Example(2D,NoScales): 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])))); // 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); // color("red")stroke(path,width=.2);
// path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Courier"); // path_text(path, "A shorter example", size=5, lettersize=5/1.2, font="Liberation Mono");
// 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. // 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])); // path = path3d(arc(100, r=12, angle=[150, 450]));
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
// kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1]; // kern = [1,1.2,1,1,.3,-.2,1,0,.8,1,1.1];
// path_text(path, "Example text", font="Courier", size=5, lettersize = 5/1.2, kern=kern, normal=UP); // path_text(path, "Example text", font="Liberation Mono", 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, module path_text(path, text, font, size, thickness, lettersize, offset=0, reverse=false, normal, top, center=false,
textmetrics=false, kern=0, height,h, valign="baseline", language, script) textmetrics=false, kern=0, height,h, valign="baseline", language, script)