fix text3d, various doc fixes, remove spurious arrows from path_text

and make path_text a null attachable so coloring works.
This commit is contained in:
Adrian Mariano 2023-03-12 21:40:01 -04:00
parent 13f23c2a61
commit d138c0d07e
4 changed files with 71 additions and 84 deletions

View File

@ -787,7 +787,7 @@ function _scalar_to_vector(value,length,varname) =
// color("red")stroke( // color("red")stroke(
// path_join([p1,p2,p3], joint=0, relocate=false, // path_join([p1,p2,p3], joint=0, relocate=false,
// closed=true), // closed=true),
// width=.3,$fn=12); // width=.3,$fn=48);
// for(x=[p1,p2,p3]) stroke(x,width=.3); // for(x=[p1,p2,p3]) stroke(x,width=.3);
// Example(2D): If you specify closed=true when the last path doesn't meet the first one then it is similar to using relocate=false: the function tries to close the path using a curve. In the example below, this results in a long curve to the left, when given the unclosed three segments as input. Note that if the segments are parallel the function fails with an error. The extension of the curves must intersect in a corner for the rounding to be well-defined. To get a normal rounding of the closed shape, you must include a fourth path, the last segment that closes the shape. // Example(2D): If you specify closed=true when the last path doesn't meet the first one then it is similar to using relocate=false: the function tries to close the path using a curve. In the example below, this results in a long curve to the left, when given the unclosed three segments as input. Note that if the segments are parallel the function fails with an error. The extension of the curves must intersect in a corner for the rounding to be well-defined. To get a normal rounding of the closed shape, you must include a fourth path, the last segment that closes the shape.
// horiz = [[0,0],[10,0]]; // horiz = [[0,0],[10,0]];

View File

@ -2789,7 +2789,7 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) =
// Module: text3d() // Module: text3d()
// Topics: Attachments, Text // Topics: Attachments, Text
// Usage: // Usage:
// text3d(text, [h], [size], [font], ...); // text3d(text, [h], [size], [font], [language=], [script=], [direction=], [atype=], [anchor=], [spin=], [orient=]);
// Description: // Description:
// Creates a 3D text block that supports anchoring and attachment to attachable objects. You cannot attach children to text. // Creates a 3D text block that supports anchoring and attachment to attachable objects. You cannot attach children to text.
// . // .
@ -2816,62 +2816,46 @@ function onion(r, ang=45, cap_h, d, anchor=CENTER, spin=0, orient=UP) =
// text = Text to create. // text = Text to create.
// h / height / thickness = Extrusion height for the text. Default: 1 // h / height / thickness = Extrusion height for the text. Default: 1
// 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=`.
// valign = If given, specifies the vertical alignment of the text. `"top"`, `"center"`, `"baseline"` or `"bottom"`. Overrides `anchor=`.
// spacing = The relative spacing multiplier between characters. Default: `1.0` // spacing = The relative spacing multiplier between characters. Default: `1.0`
// direction = The text direction. `"ltr"` for left to right. `"rtl"` for right to left. `"ttb"` for top to bottom. `"btt"` for bottom to top. Default: `"ltr"` // direction = The text direction. `"ltr"` for left to right. `"rtl"` for right to left. `"ttb"` for top to bottom. `"btt"` for bottom to top. Default: `"ltr"`
// language = The language the text is in. Default: `"en"` // language = The language the text is in. Default: `"en"`
// script = The script the text is in. Default: `"latin"` // script = The script the text is in. Default: `"latin"`
// atype = Change vertical center between "baseline" and "center". Default: "baseline"
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"baseline"` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `"baseline"`
// center = Center the text. Equivalent to `atype="center", anchor=CENTER`. Default: false
// spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0`
// orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP`
// See Also: path_text() // See Also: path_text()
// Extra Anchors: // Anchor Types:
// "baseline" = Anchors at the baseline of the text, at the start of the string. // baseline = Anchor center is relative to text baseline
// str("baseline",VECTOR) = Anchors at the baseline of the text, modified by the X and Z components of the appended vector. // ycenter = Anchor center is relative to the actualy y direction center of the text
// Examples: // Examples:
// text3d("Foobar", h=3, size=10); // text3d("Fogmobar", h=3, size=10);
// text3d("Foobar", h=2, size=12, font="Helvetica"); // text3d("Fogmobar", h=2, size=12, font="Helvetica");
// text3d("Foobar", h=2, anchor=CENTER); // text3d("Fogmobar", h=2, anchor=CENTER);
// text3d("Foobar", h=2, anchor=str("baseline",CENTER)); // text3d("Fogmobar", h=2, anchor=CENTER, atype="ycenter");
// text3d("Foobar", h=2, anchor=str("baseline",BOTTOM+RIGHT)); // text3d("Fogmobar", h=2, anchor=RIGHT);
// text3d("Fogmobar", h=2, anchor=RIGHT+BOT, atype="ycenter");
module text3d(text, h, size=10, font="Helvetica", halign, valign, spacing=1.0, direction="ltr", language="em", script="latin", module text3d(text, h, size=10, font="Helvetica", spacing=1.0, direction="ltr", language="em", script="latin",
height, thickness, height, thickness, atype, center=false,
anchor="baseline[-1,0,-1]", spin=0, orient=UP) { anchor, spin=0, orient=UP) {
no_children($children); no_children($children);
h = one_defined([h,height,thickness],"h,height,thickness",dflt=1); h = one_defined([h,height,thickness],"h,height,thickness",dflt=1);
dummy1 = assert(is_undef(atype) || in_list(atype,["ycenter","baseline"]), "atype must be \"center\" or \"baseline\"");
assert(is_undef(anchor) || is_vector(anchor) || is_string(anchor), str("Got: ",anchor)) assert(is_bool(center));
assert(is_undef(spin) || is_vector(spin,3) || is_num(spin), str("Got: ",spin)) atype = default(atype, center?"ycenter":"baseline");
assert(is_undef(orient) || is_vector(orient,3), str("Got: ",orient)); anchor = default(anchor, center?CENTER:LEFT);
anchor = default(anchor, CENTER);
spin = default(spin, 0);
orient = default(orient, UP);
geom = attach_geom(size=[size,size,h]); geom = attach_geom(size=[size,size,h]);
anch = !any([for (c=anchor) c=="["])? anchor : ha = anchor.x<0? "left"
let( : anchor.x>0? "right"
parts = str_split(str_split(str_split(anchor,"]")[0],"[")[1],","), : "center";
vec = [for (p=parts) parse_float(str_strip(p," ",start=true))] va = anchor.y<0? "bottom"
) vec; : anchor.y>0? "top"
ha = anchor=="baseline"? "left" : : atype=="baseline"? "baseline"
anchor==anch && is_string(anchor)? "center" : : "center";
anch.x<0? "left" : m = _attach_transform([0,0,anchor.z],spin,orient,geom);
anch.x>0? "right" :
"center";
va = starts_with(anchor,"baseline")? "baseline" :
anchor==anch && is_string(anchor)? "center" :
anch.y<0? "bottom" :
anch.y>0? "top" :
"center";
base = anchor=="baseline"? CENTER :
anchor==anch && is_string(anchor)? CENTER :
anch.z<0? BOTTOM :
anch.z>0? TOP :
CENTER;
m = _attach_transform(base,spin,orient,geom);
multmatrix(m) { multmatrix(m) {
$parent_anchor = anchor; $parent_anchor = anchor;
$parent_spin = spin; $parent_spin = spin;
@ -2975,6 +2959,8 @@ function _cut_interp(pathcut, path, data) =
// 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 // 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
// valign = align text to the path using "top", "bottom", "center" or "baseline". You can also adjust position with a numerical offset as in "top-5" or "bottom+2". This only works with textmetrics enabled. You can give a simple numerical offset, which will be relative to the baseline and works even without textmetrics. Default: "baseline" // valign = align text to the path using "top", "bottom", "center" or "baseline". You can also adjust position with a numerical offset as in "top-5" or "bottom+2". This only works with textmetrics enabled. You can give a simple numerical offset, which will be relative to the baseline and works even without textmetrics. Default: "baseline"
// 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"
// 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 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])); // path = path3d(arc(100, r=25, angle=[245, 370]));
// color("red")stroke(path, width=.3); // color("red")stroke(path, width=.3);
@ -3037,7 +3023,8 @@ function _cut_interp(pathcut, path, data) =
// 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="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, height,h, valign="baseline") 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)
{ {
no_children($children); no_children($children);
dummy2=assert(is_path(path,[2,3]),"Must supply a 2d or 3d path") dummy2=assert(is_path(path,[2,3]),"Must supply a 2d or 3d path")
@ -3100,42 +3087,42 @@ module path_text(path, text, font, size, thickness, lettersize, offset=0, revers
usetop = is_def(top); usetop = is_def(top);
normpts = is_undef(normal) ? (reverse?1:-1)*column(pts,3) : _cut_interp(pts,path, normal); normpts = is_undef(normal) ? (reverse?1:-1)*column(pts,3) : _cut_interp(pts,path, normal);
toppts = is_undef(top) ? undef : _cut_interp(pts,path,top); toppts = is_undef(top) ? undef : _cut_interp(pts,path,top);
attachable(){
for (i = idx(text)) { for (i = idx(text)) {
tangent = pts[i][2]; tangent = pts[i][2];
checks = checks =
assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)), assert(!usetop || !approx(tangent*toppts[i],norm(top[i])*norm(tangent)),
str("Specified top direction parallel to path at character ",i)) str("Specified top direction parallel to path at character ",i))
assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)), assert(usetop || !approx(tangent*normpts[i],norm(normpts[i])*norm(tangent)),
str("Specified normal direction parallel to path at character ",i)); str("Specified normal direction parallel to path at character ",i));
adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i]) adjustment = usetop ? (tangent*toppts[i])*toppts[i]/(toppts[i]*toppts[i])
: usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i]) : usernorm ? (tangent*normpts[i])*normpts[i]/(normpts[i]*normpts[i])
: [0,0,0]; : [0,0,0];
move(pts[i][0]) { move(pts[i][0]) {
if (dim==3) { if (dim==3) {
frame_map( frame_map(
x=tangent-adjustment, x=tangent-adjustment,
z=usetop ? undef : normpts[i], z=usetop ? undef : normpts[i],
y=usetop ? toppts[i] : undef y=usetop ? toppts[i] : undef
) up(offset-thickness/2) { ) up(offset-thickness/2) {
linear_extrude(height=thickness) linear_extrude(height=thickness)
back(vadjustment) back(vadjustment)
{ {
stroke([[0,0],[0,14]], width=.3, endcap2="arrow"); left(lsize[i]/2)
text(text[i], font=font, size=size, language=language, script=script);
left(lsize[i]/2) }
text(text[i], font=font, size=size); }
} else {
frame_map(
x=point3d(tangent-adjustment),
y=point3d(usetop ? toppts[i] : -normpts[i])
) left(lsize[0]/2) {
text(text[i], font=font, size=size, language=language, script=script);
} }
} }
} else {
frame_map(
x=point3d(tangent-adjustment),
y=point3d(usetop ? toppts[i] : -normpts[i])
) left(lsize[0]/2) {
text(text[i], font=font, size=size);
}
} }
} }
union();
} }
} }

View File

@ -2678,11 +2678,9 @@ function associate_vertices(polygons, split, curpoly=0) =
// } // }
// } // }
// Continues: // Continues:
// Note that of the six available styles, five produce a different result. You might think that simply identifying the // Note that of the six available styles, five produce a different result. There may exist some concave shape where none of the styles
// correct style will enable you to get the result you want. This is true if you apply the texture to a convex object, but for concave // produce the right result everywhere on the shape. If this happens it would be another limitation of height field textures. (If you have an
// objects, the triangulation rules for the styles may result in different triangulations in different parts of the surface. // example of such a texture and shape please let us know!)
// For some cases, none of the six styles produces the correct result in every case.
// [Can this actually happen? We need an example of this!]
// Subsection: VNF Textures // Subsection: VNF Textures
// VNF textures overcome all of the limitations of height field textures, but with two costs. They can be more difficult to construct than // VNF textures overcome all of the limitations of height field textures, but with two costs. They can be more difficult to construct than
// a simple array of height values, and they are significantly slower to compute for a tile with the same number of points. Note, however, for // a simple array of height values, and they are significantly slower to compute for a tile with the same number of points. Note, however, for

View File

@ -89,7 +89,9 @@ include <BOSL2/std.scad>
circle(d=50, anchor=polar_to_xy(1,150)); circle(d=50, anchor=polar_to_xy(1,150));
``` ```
You can see the typical anchor points by making `show_anchors()` the child of the shape: Note that the radius does not matter for the anchor because only the
anchor's direction affects the result. You can see the typical anchor
points by giving `show_anchors()` as a child of the shape:
```openscad-2D ```openscad-2D
include <BOSL2/std.scad> include <BOSL2/std.scad>