diff --git a/edges.scad b/edges.scad index 6b5ff5f..981ba09 100644 --- a/edges.scad +++ b/edges.scad @@ -10,13 +10,14 @@ // corners of cubes. You can simply specify these direction vectors numerically, but another // option is to use named constants for direction vectors. These constants define unit vectors // for the six axis directions as shown below. -// Figure(3D,Big,VPD=7): Named constants for direction vectors. Some directions have more than one name. +// Figure(3D,Big,VPD=6): Named constants for direction vectors. Some directions have more than one name. // $fn=12; // stroke([[0,0,0],RIGHT], endcap2="arrow2", width=.05); -// right(.05)up(.05)move(RIGHT)atext("RIGHT",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// color("black")right(.05)up(.05)move(RIGHT)atext("RIGHT",size=.1,h=.01,anchor=LEFT,orient=FRONT); // stroke([[0,0,0],LEFT], endcap2="arrow2", width=.05); -// left(.05)up(.05)move(LEFT)atext("LEFT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); +// color("black")left(.05)up(.05)move(LEFT)atext("LEFT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); // stroke([[0,0,0],FRONT], endcap2="arrow2", width=.05); +// color("black") // left(.1){ // up(.1)move(FRONT)atext("FRONT",size=.1,h=.01,anchor=RIGHT,orient=FRONT); // move(FRONT)atext("FWD",size=.1,h=.01,anchor=RIGHT,orient=FRONT); @@ -24,8 +25,9 @@ // } // stroke([[0,0,0],BACK], endcap2="arrow2", width=.05); // right(.05) -// move(BACK)atext("BACK",size=.1,h=.01,anchor=LEFT,orient=FRONT); +// color("black")move(BACK)atext("BACK",size=.1,h=.01,anchor=LEFT,orient=FRONT); // stroke([[0,0,0],DOWN], endcap2="arrow2", width=.05); +// color("black") // right(.1){ // up(.1)move(BOT)atext("DOWN",size=.1,h=.01,anchor=LEFT,orient=FRONT); // move(BOT)atext("BOTTOM",size=.1,h=.01,anchor=LEFT,orient=FRONT); @@ -33,7 +35,7 @@ // down(.2)move(BOT)atext("BTM",size=.1,h=.01,anchor=LEFT,orient=FRONT); // } // stroke([[0,0,0],TOP], endcap2="arrow2", width=.05); -// left(.05){ +// color("black")left(.05){ // up(.1)move(TOP)atext("TOP",size=.1,h=.01,anchor=RIGHT,orient=FRONT); // move(TOP)atext("UP",size=.1,h=.01,anchor=RIGHT,orient=FRONT); // } @@ -41,7 +43,7 @@ // Modules operating on faces accept a list of faces to describe the faces to operate on. Each // face is given by a vector that points to that face. Attachments of cuboid objects also // work by choosing an attachment face with a single vector in the same manner. -// Figure(3D,Big,NoScales,VPD=250): The six faces of the cube. Some have faces have more than one name. +// Figure(3D,Big,NoScales,VPD=275): The six faces of the cube. Some have faces have more than one name. // ydistribute(50) { // xdistribute(35){ // _show_cube_faces([BACK], botlabel=["BACK"]); @@ -194,7 +196,7 @@ // _show_corners(corners=BOT+RIGHT+BACK); // } // } -// Figure(3D,Big,NoScales,VPD=300): Vectors pointing toward an edge select the corners and the ends of the edge. +// Figure(3D,Big,NoScales,VPD=340): Vectors pointing toward an edge select the corners and the ends of the edge. // ydistribute(55) { // xdistribute(35) { // _show_corners(corners=BOT+RIGHT); @@ -245,7 +247,7 @@ // _show_corners(_corners([FRONT+TOP,BOT+BACK]), toplabel=["corners=[FRONT+TOP,"," BOT+BACK]"]); // _show_corners(_corners("ALL",FRONT+TOP), toplabel=["(corners=\"ALL\")","except=FRONT+TOP"]); // } -// Figure(3D,Big,NoScales,VPD=300): The first example shows a single corner removed from the top corners using a numerical vector. The second one shows removing a set of two corner descriptors from the implied set of all corners. +// Figure(3D,Med,NoScales,VPD=240): The first example shows a single corner removed from the top corners using a numerical vector. The second one shows removing a set of two corner descriptors from the implied set of all corners. // xdistribute(58){ // _show_corners(_corners(TOP,[1,1,1]), toplabel=["corners=TOP","except=[1,1,1]"]); // _show_corners(_corners("ALL",[FRONT+RIGHT+TOP,FRONT+LEFT+BOT]), @@ -463,9 +465,10 @@ module _show_edges(edges="ALL", size=20, text, txtsize=3,toplabel) { } fwd(size/2) _edges_text3d(text, size=txtsize); color("yellow",0.7) cuboid(size=size); + vpr = [55,0,25]; color("black") if (is_def(toplabel)) - for(h=idx(toplabel)) up(21+6*h)rot($vpr)atext(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); + for(h=idx(toplabel)) up(21+6*h)rot(vpr)atext(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); } @@ -634,9 +637,10 @@ module _show_corners(corners="ALL", size=20, text, txtsize=3,toplabel) { color("red") sphere(d=2, $fn=16); fwd(size/2) _edges_text3d(text, size=txtsize); color("yellow",0.7) cuboid(size=size); + vpr = [55,0,25]; color("black") if (is_def(toplabel)) - for(h=idx(toplabel)) up(21+6*h)rot($vpr)atext(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); + for(h=idx(toplabel)) up(21+6*h)rot(vpr)atext(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); } module _show_cube_faces(faces, size=20, toplabel,botlabel) { @@ -645,11 +649,13 @@ module _show_cube_faces(faces, size=20, toplabel,botlabel) { move(f*size/2) rot(from=UP,to=f) cuboid([size,size,.1]); } + + vpr = [55,0,25]; color("black"){ if (is_def(toplabel)) - for(h=idx(toplabel)) up(21+6*h)rot($vpr)atext(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); + for(h=idx(toplabel)) up(21+6*h)rot(vpr)atext(select(toplabel,-h-1),size=3.3,h=.1,orient=UP,anchor=FRONT); if (is_def(botlabel)) - for(h=idx(botlabel)) down(26+6*h)rot($vpr)atext(botlabel[h],size=3.3,h=.1,orient=UP,anchor=FRONT); + for(h=idx(botlabel)) down(26+6*h)rot(vpr)atext(botlabel[h],size=3.3,h=.1,orient=UP,anchor=FRONT); } color("yellow",0.7) cuboid(size=size); } diff --git a/shapes3d.scad b/shapes3d.scad index 7ceca1a..2008346 100644 --- a/shapes3d.scad +++ b/shapes3d.scad @@ -185,7 +185,8 @@ module cuboid( module corner_shape(corner) { e = _corner_edges(edges, corner); cnt = sum(e); - r = first_defined([chamfer, rounding, 0]); + r = first_defined([chamfer, rounding]); + dummy=assert(is_finite(r) && !approx(r,0)); c = [min(r,size.x/2), min(r,size.y/2), min(r,size.z/2)]; c2 = v_mul(corner,c/2); $fn = is_finite(chamfer)? 4 : segs(r); @@ -229,10 +230,13 @@ module cuboid( size = scalar_vec3(size); edges = _edges(edges, except=first_defined([except_edges,except])); + chamfer = approx(chamfer,0) ? undef : chamfer; + rounding = approx(rounding,0) ? undef : rounding; assert(is_vector(size,3)); assert(all_positive(size)); assert(is_undef(chamfer) || is_finite(chamfer),"chamfer must be a finite value"); assert(is_undef(rounding) || is_finite(rounding),"rounding must be a finite value"); + assert(is_undef(rounding) || is_undef(chamfer), "Cannot specify nonzero value for both chamfer and rounding"); assert(is_undef(p1) || is_vector(p1)); assert(is_undef(p2) || is_vector(p2)); assert(is_bool(trimcorners)); diff --git a/strings.scad b/strings.scad index 732595f..962ea30 100644 --- a/strings.scad +++ b/strings.scad @@ -6,7 +6,7 @@ ////////////////////////////////////////////////////////////////////// -// Section: String Operations +// Section: Extracting Substrings // Function: substr() // Usage: @@ -47,21 +47,70 @@ function suffix(str,len) = len>=len(str)? str : substr(str, len(str)-len,len); -// Function: str_join() + + +// Section: Checking character class + +// Function: is_lower() // Usage: -// str_join(list, [sep]) +// x = is_lower(s); // Description: -// Returns the concatenation of a list of strings, optionally with a -// separator string inserted between each string on the list. -// Arguments: -// list = list of strings to concatenate -// sep = separator string to insert. Default: "" -// Example: -// str_join(["abc","def","ghi"]); // Returns "abcdefghi" -// str_join(["abc","def","ghi"], " + "); // Returns "abc + def + ghi" -function str_join(list,sep="",_i=0, _result="") = - _i >= len(list)-1 ? (_i==len(list) ? _result : str(_result,list[_i])) : - str_join(list,sep,_i+1,str(_result,list[_i],sep)); +// Returns true if all the characters in the given string are lowercase letters. (a-z) +function is_lower(s) = + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_lower(v)]) : + let(v = ord(s[0])) (v>=ord("a") && v<=ord("z")); + + +// Function: is_upper() +// Usage: +// x = is_upper(s); +// Description: +// Returns true if all the characters in the given string are uppercase letters. (A-Z) +function is_upper(s) = + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_upper(v)]) : + let(v = ord(s[0])) (v>=ord("A") && v<=ord("Z")); + + +// Function: is_digit() +// Usage: +// x = is_digit(s); +// Description: +// Returns true if all the characters in the given string are digits. (0-9) +function is_digit(s) = + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_digit(v)]) : + let(v = ord(s[0])) (v>=ord("0") && v<=ord("9")); + + +// Function: is_hexdigit() +// Usage: +// x = is_hexdigit(s); +// Description: +// Returns true if all the characters in the given string are valid hexadecimal digits. (0-9 or a-f or A-F)) +function is_hexdigit(s) = + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_hexdigit(v)]) : + let(v = ord(s[0])) + (v>=ord("0") && v<=ord("9")) || + (v>=ord("A") && v<=ord("F")) || + (v>=ord("a") && v<=ord("f")); + + +// Function: is_letter() +// Usage: +// x = is_letter(s); +// Description: +// Returns true if all the characters in the given string are standard ASCII letters. (A-Z or a-z) +function is_letter(s) = + assert(is_string(s)) + s==""? false : + all([for (v=s) is_lower(v) || is_upper(v)]); // Function: downcase() @@ -92,6 +141,10 @@ function upcase(str) = str_join([for(char=str) let(code=ord(char)) code>=97 && code<=122 ? chr(code-32) : char]); + + +// Section: Converting strings to numbers + // Function: str_int() // Usage: // str_int(str, [base]) @@ -225,6 +278,282 @@ function str_num(str) = str_float(str); + + +// Section: Formatting data + +// Function: fmt_int() +// Usage: +// fmt_int(i, [mindigits]); +// Description: +// Formats an integer number into a string. This can handle larger numbers than `str()`. +// Arguments: +// i = The integer to make a string of. +// mindigits = If the number has fewer than this many digits, pad the front with zeros until it does. Default: 1. +// Example: +// str(123456789012345); // Returns "1.23457e+14" +// fmt_int(123456789012345); // Returns "123456789012345" +// fmt_int(-123456789012345); // Returns "-123456789012345" +function fmt_int(i,mindigits=1) = + i<0? str("-", fmt_int(-i,mindigits)) : + let(i=floor(i), e=floor(log(i))) + i==0? str_join([for (j=[0:1:mindigits-1]) "0"]) : + str_join( + concat( + [for (j=[0:1:mindigits-e-2]) "0"], + [for (j=[e:-1:0]) str(floor(i/pow(10,j)%10))] + ) + ); + + +// Function: fmt_fixed() +// Usage: +// s = fmt_fixed(f, [digits]); +// Description: +// Given a floating point number, formats it into a string with the given number of digits after the decimal point. +// Arguments: +// f = The floating point number to format. +// digits = The number of digits after the decimal to show. Default: 6 +function fmt_fixed(f,digits=6) = + assert(is_int(digits)) + assert(digits>0) + is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_fixed(g,digits=digits)]),"]") : + str(f)=="nan"? "nan" : + str(f)=="inf"? "inf" : + f<0? str("-",fmt_fixed(-f,digits=digits)) : + assert(is_num(f)) + let( + sc = pow(10,digits), + scaled = floor(f * sc + 0.5), + whole = floor(scaled/sc), + part = floor(scaled-(whole*sc)) + ) str(fmt_int(whole),".",fmt_int(part,digits)); + + +// Function: fmt_float() +// Usage: +// fmt_float(f,[sig]); +// Description: +// Formats the given floating point number `f` into a string with `sig` significant digits. +// Strips trailing `0`s after the decimal point. Strips trailing decimal point. +// If the number can be represented in `sig` significant digits without a mantissa, it will be. +// If given a list of numbers, recursively prints each item in the list, returning a string like `[3,4,5]` +// Arguments: +// f = The floating point number to format. +// sig = The number of significant digits to display. Default: 12 +// Example: +// fmt_float(PI,12); // Returns: "3.14159265359" +// fmt_float([PI,-16.75],12); // Returns: "[3.14159265359, -16.75]" +function fmt_float(f,sig=12) = + assert(is_int(sig)) + assert(sig>0) + is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_float(g,sig=sig)]),"]") : + f==0? "0" : + str(f)=="nan"? "nan" : + str(f)=="inf"? "inf" : + f<0? str("-",fmt_float(-f,sig=sig)) : + assert(is_num(f)) + let( + e = floor(log(f)), + mv = sig - e - 1 + ) mv == 0? fmt_int(floor(f + 0.5)) : + (e<-sig/2||mv<0)? str(fmt_float(f*pow(10,-e),sig=sig),"e",e) : + let( + ff = f + pow(10,-mv)*0.5, + whole = floor(ff), + part = floor((ff-whole) * pow(10,mv)) + ) + str_join([ + str(whole), + str_strip_trailing( + str_join([ + ".", + fmt_int(part, mindigits=mv) + ]), + "0." + ) + ]); + + +// Function: matrix_strings() +// Usage: +// matrix_strings(M, [sig], [eps]) +// Description: +// Convert a numerical matrix into a matrix of strings where every column +// is the same width so it will display in neat columns when printed. +// Values below eps will display as zero. The matrix can include nans, infs +// or undefs and the rows can be different lengths. +// Arguments: +// M = numerical matrix to convert +// sig = significant digits to display. Default: 4 +// eps = values smaller than this are shown as zero. Default: 1e-9 +function matrix_strings(M, sig=4, eps=1e-9) = + let( + columngap = 1, + figure_dash = chr(8210), + space_punc = chr(8200), + space_figure = chr(8199), + strarr= + [for(row=M) + [for(entry=row) + let( + text = is_undef(entry) ? "und" + : abs(entry) < eps ? "0" // Replace hyphens with figure dashes + : str_replace_char(fmt_float(entry, sig),"-",figure_dash), + have_dot = is_def(str_find(text, ".")) + ) + // If the text lacks a dot we add a space the same width as a dot to + // maintain alignment + str(have_dot ? "" : space_punc, text) + ] + ], + maxwidth = max([for(row=M) len(row)]), + // Find maximum length for each column. Some entries in a column may be missing. + maxlen = [for(i=[0:1:maxwidth-1]) + max( + [for(j=idx(M)) i>=len(M[j]) ? 0 : len(strarr[j][i])]) + ], + padded = + [for(row=strarr) + str_join([for(i=idx(row)) + let( + extra = ends_with(row[i],"inf") ? 1 : 0 + ) + str_pad(row[i],maxlen[i]+extra+(i==0?0:columngap),space_figure,left=true)])] + ) + padded; + + + +// Function: str_format() +// Usage: +// s = str_format(fmt, vals); +// Description: +// Given a format string and a list of values, inserts the values into the placeholders in the format string and returns it. +// Formatting placeholders have the following syntax: +// - A leading `{` character to show the start of the placeholder. +// - An integer index into the `vals` list to specify which value should be formatted at that place. If not given, the first placeholder will use index `0`, the second will use index `1`, etc. +// - An optional `:` separator to indicate that what follows if a formatting specifier. If not given, no formatting info follows. +// - An optional `-` character to indicate that the value should be left justified if the value needs field width padding. If not given, right justification is used. +// - An optional `0` character to indicate that the field should be padded with `0`s. If not given, spaces will be used for padding. +// - An optional integer field width, which the value should be padded to. If not given, no padding will be performed. +// - An optional `.` followed by an integer precision length, for specifying how many digits to display in numeric formats. If not give, 6 digits is assumed. +// - An optional letter to indicate the formatting style to use. If not given, `s` is assumed, which will do it's generic best to format any data type. +// - A trailing `}` character to show the end of the placeholder. +// . +// Formatting styles, and their effects are as follows: +// - `s`: Converts the value to a string with `str()` to display. This is very generic. +// - `i` or `d`: Formats numeric values as integers. +// - `f`: Formats numeric values with the precision number of digits after the decimal point. NaN and Inf are shown as `nan` and `inf`. +// - `F`: Formats numeric values with the precision number of digits after the decimal point. NaN and Inf are shown as `NAN` and `INF`. +// - `g`: Formats numeric values with the precision number of total significant digits. NaN and Inf are shown as `nan` and `inf`. Mantissas are demarked by `e`. +// - `G`: Formats numeric values with the precision number of total significant digits. NaN and Inf are shown as `NAN` and `INF`. Mantissas are demarked by `E`. +// - `b`: If the value logically evaluates as true, it shows as `true`, otherwise `false`. +// - `B`: If the value logically evaluates as true, it shows as `TRUE`, otherwise `FALSE`. +// Arguments: +// fmt = The formatting string, with placeholders to format the values into. +// vals = The list of values to format. +// Example(NORENDER): +// str_format("The value of {} is {:.14f}.", ["pi", PI]); // Returns: "The value of pi is 3.14159265358979." +// str_format("The value {1:f} is known as {0}.", ["pi", PI]); // Returns: "The value 3.141593 is known as pi." +// str_format("We use a very small value {1:.6g} as {0}.", ["EPSILON", EPSILON]); // Returns: "We use a very small value 1e-9 as EPSILON." +// str_format("{:-5s}{:i}{:b}", ["foo", 12e3, 5]); // Returns: "foo 12000true" +// str_format("{:-10s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostamus27.440" +// str_format("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostam 27.440" +function str_format(fmt, vals) = + let( + parts = str_split(fmt,"{") + ) str_join([ + for(i = idx(parts)) + let( + found_brace = i==0 || [for (c=parts[i]) if(c=="}") c] != [], + err = assert(found_brace, "Unbalanced { in format string."), + p = i==0? [undef,parts[i]] : str_split(parts[i],"}"), + fmta = p[0], + raw = p[1] + ) each [ + assert(i<99) + is_undef(fmta)? "" : let( + fmtb = str_split(fmta,":"), + num = is_digit(fmtb[0])? str_int(fmtb[0]) : (i-1), + left = fmtb[1][0] == "-", + fmtb1 = default(fmtb[1],""), + fmtc = left? substr(fmtb1,1) : fmtb1, + zero = fmtc[0] == "0", + lch = fmtc==""? "" : fmtc[len(fmtc)-1], + hastyp = is_letter(lch), + typ = hastyp? lch : "s", + fmtd = hastyp? substr(fmtc,0,len(fmtc)-1) : fmtc, + fmte = str_split((zero? substr(fmtd,1) : fmtd), "."), + wid = str_int(fmte[0]), + prec = str_int(fmte[1]), + val = assert(num>=0&&num= len(list)-1 ? (_i==len(list) ? _result : str(_result,list[_i])) : + str_join(list,sep,_i+1,str(_result,list[_i],sep)); + + + + // Function: str_split() // Usage: // str_split(str, sep, [keep_nulls]) @@ -425,283 +754,6 @@ function str_strip_trailing(s,c) = substr(s,len=len(s)-_str_count_trailing(s,c)) function str_strip(s,c) = str_strip_trailing(str_strip_leading(s,c),c); -// Function: fmt_int() -// Usage: -// fmt_int(i, [mindigits]); -// Description: -// Formats an integer number into a string. This can handle larger numbers than `str()`. -// Arguments: -// i = The integer to make a string of. -// mindigits = If the number has fewer than this many digits, pad the front with zeros until it does. Default: 1. -// Example: -// str(123456789012345); // Returns "1.23457e+14" -// fmt_int(123456789012345); // Returns "123456789012345" -// fmt_int(-123456789012345); // Returns "-123456789012345" -function fmt_int(i,mindigits=1) = - i<0? str("-", fmt_int(-i,mindigits)) : - let(i=floor(i), e=floor(log(i))) - i==0? str_join([for (j=[0:1:mindigits-1]) "0"]) : - str_join( - concat( - [for (j=[0:1:mindigits-e-2]) "0"], - [for (j=[e:-1:0]) str(floor(i/pow(10,j)%10))] - ) - ); - - -// Function: fmt_fixed() -// Usage: -// s = fmt_fixed(f, [digits]); -// Description: -// Given a floating point number, formats it into a string with the given number of digits after the decimal point. -// Arguments: -// f = The floating point number to format. -// digits = The number of digits after the decimal to show. Default: 6 -function fmt_fixed(f,digits=6) = - assert(is_int(digits)) - assert(digits>0) - is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_fixed(g,digits=digits)]),"]") : - str(f)=="nan"? "nan" : - str(f)=="inf"? "inf" : - f<0? str("-",fmt_fixed(-f,digits=digits)) : - assert(is_num(f)) - let( - sc = pow(10,digits), - scaled = floor(f * sc + 0.5), - whole = floor(scaled/sc), - part = floor(scaled-(whole*sc)) - ) str(fmt_int(whole),".",fmt_int(part,digits)); - - -// Function: fmt_float() -// Usage: -// fmt_float(f,[sig]); -// Description: -// Formats the given floating point number `f` into a string with `sig` significant digits. -// Strips trailing `0`s after the decimal point. Strips trailing decimal point. -// If the number can be represented in `sig` significant digits without a mantissa, it will be. -// If given a list of numbers, recursively prints each item in the list, returning a string like `[3,4,5]` -// Arguments: -// f = The floating point number to format. -// sig = The number of significant digits to display. Default: 12 -// Example: -// fmt_float(PI,12); // Returns: "3.14159265359" -// fmt_float([PI,-16.75],12); // Returns: "[3.14159265359, -16.75]" -function fmt_float(f,sig=12) = - assert(is_int(sig)) - assert(sig>0) - is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_float(g,sig=sig)]),"]") : - f==0? "0" : - str(f)=="nan"? "nan" : - str(f)=="inf"? "inf" : - f<0? str("-",fmt_float(-f,sig=sig)) : - assert(is_num(f)) - let( - e = floor(log(f)), - mv = sig - e - 1 - ) mv == 0? fmt_int(floor(f + 0.5)) : - (e<-sig/2||mv<0)? str(fmt_float(f*pow(10,-e),sig=sig),"e",e) : - let( - ff = f + pow(10,-mv)*0.5, - whole = floor(ff), - part = floor((ff-whole) * pow(10,mv)) - ) - str_join([ - str(whole), - str_strip_trailing( - str_join([ - ".", - fmt_int(part, mindigits=mv) - ]), - "0." - ) - ]); - - -// Function: escape_html() -// Usage: -// echo(escape_html(s)); -// Description: -// Converts "<", ">", "&", and double-quote chars to their entity encoding so that echoing the strong will show it verbatim. -function escape_html(s) = - str_join([ - for (c=s) - c=="<"? "<" : - c==">"? ">" : - c=="&"? "&" : - c=="\""? """ : - c - ]); - - -// Function: is_lower() -// Usage: -// x = is_lower(s); -// Description: -// Returns true if all the characters in the given string are lowercase letters. (a-z) -function is_lower(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_lower(v)]) : - let(v = ord(s[0])) (v>=ord("a") && v<=ord("z")); - - -// Function: is_upper() -// Usage: -// x = is_upper(s); -// Description: -// Returns true if all the characters in the given string are uppercase letters. (A-Z) -function is_upper(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_upper(v)]) : - let(v = ord(s[0])) (v>=ord("A") && v<=ord("Z")); - - -// Function: is_digit() -// Usage: -// x = is_digit(s); -// Description: -// Returns true if all the characters in the given string are digits. (0-9) -function is_digit(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_digit(v)]) : - let(v = ord(s[0])) (v>=ord("0") && v<=ord("9")); - - -// Function: is_hexdigit() -// Usage: -// x = is_hexdigit(s); -// Description: -// Returns true if all the characters in the given string are valid hexadecimal digits. (0-9 or a-f or A-F)) -function is_hexdigit(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_hexdigit(v)]) : - let(v = ord(s[0])) - (v>=ord("0") && v<=ord("9")) || - (v>=ord("A") && v<=ord("F")) || - (v>=ord("a") && v<=ord("f")); - - -// Function: is_letter() -// Usage: -// x = is_letter(s); -// Description: -// Returns true if all the characters in the given string are standard ASCII letters. (A-Z or a-z) -function is_letter(s) = - assert(is_string(s)) - s==""? false : - all([for (v=s) is_lower(v) || is_upper(v)]); - - -// Function: str_format() -// Usage: -// s = str_format(fmt, vals); -// Description: -// Given a format string and a list of values, inserts the values into the placeholders in the format string and returns it. -// Formatting placeholders have the following syntax: -// - A leading `{` character to show the start of the placeholder. -// - An integer index into the `vals` list to specify which value should be formatted at that place. If not given, the first placeholder will use index `0`, the second will use index `1`, etc. -// - An optional `:` separator to indicate that what follows if a formatting specifier. If not given, no formatting info follows. -// - An optional `-` character to indicate that the value should be left justified if the value needs field width padding. If not given, right justification is used. -// - An optional `0` character to indicate that the field should be padded with `0`s. If not given, spaces will be used for padding. -// - An optional integer field width, which the value should be padded to. If not given, no padding will be performed. -// - An optional `.` followed by an integer precision length, for specifying how many digits to display in numeric formats. If not give, 6 digits is assumed. -// - An optional letter to indicate the formatting style to use. If not given, `s` is assumed, which will do it's generic best to format any data type. -// - A trailing `}` character to show the end of the placeholder. -// . -// Formatting styles, and their effects are as follows: -// - `s`: Converts the value to a string with `str()` to display. This is very generic. -// - `i` or `d`: Formats numeric values as integers. -// - `f`: Formats numeric values with the precision number of digits after the decimal point. NaN and Inf are shown as `nan` and `inf`. -// - `F`: Formats numeric values with the precision number of digits after the decimal point. NaN and Inf are shown as `NAN` and `INF`. -// - `g`: Formats numeric values with the precision number of total significant digits. NaN and Inf are shown as `nan` and `inf`. Mantissas are demarked by `e`. -// - `G`: Formats numeric values with the precision number of total significant digits. NaN and Inf are shown as `NAN` and `INF`. Mantissas are demarked by `E`. -// - `b`: If the value logically evaluates as true, it shows as `true`, otherwise `false`. -// - `B`: If the value logically evaluates as true, it shows as `TRUE`, otherwise `FALSE`. -// Arguments: -// fmt = The formatting string, with placeholders to format the values into. -// vals = The list of values to format. -// Example(NORENDER): -// str_format("The value of {} is {:.14f}.", ["pi", PI]); // Returns: "The value of pi is 3.14159265358979." -// str_format("The value {1:f} is known as {0}.", ["pi", PI]); // Returns: "The value 3.141593 is known as pi." -// str_format("We use a very small value {1:.6g} as {0}.", ["EPSILON", EPSILON]); // Returns: "We use a very small value 1e-9 as EPSILON." -// str_format("{:-5s}{:i}{:b}", ["foo", 12e3, 5]); // Returns: "foo 12000true" -// str_format("{:-10s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostamus27.440" -// str_format("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostam 27.440" -function str_format(fmt, vals) = - let( - parts = str_split(fmt,"{") - ) str_join([ - for(i = idx(parts)) - let( - found_brace = i==0 || [for (c=parts[i]) if(c=="}") c] != [], - err = assert(found_brace, "Unbalanced { in format string."), - p = i==0? [undef,parts[i]] : str_split(parts[i],"}"), - fmta = p[0], - raw = p[1] - ) each [ - assert(i<99) - is_undef(fmta)? "" : let( - fmtb = str_split(fmta,":"), - num = is_digit(fmtb[0])? str_int(fmtb[0]) : (i-1), - left = fmtb[1][0] == "-", - fmtb1 = default(fmtb[1],""), - fmtc = left? substr(fmtb1,1) : fmtb1, - zero = fmtc[0] == "0", - lch = fmtc==""? "" : fmtc[len(fmtc)-1], - hastyp = is_letter(lch), - typ = hastyp? lch : "s", - fmtd = hastyp? substr(fmtc,0,len(fmtc)-1) : fmtc, - fmte = str_split((zero? substr(fmtd,1) : fmtd), "."), - wid = str_int(fmte[0]), - prec = str_int(fmte[1]), - val = assert(num>=0&&num=len(M[j]) ? 0 : len(strarr[j][i])]) - ], - padded = - [for(row=strarr) - str_join([for(i=idx(row)) - let( - extra = ends_with(row[i],"inf") ? 1 : 0 - ) - str_pad(row[i],maxlen[i]+extra+(i==0?0:columngap),space_figure,left=true)])] - ) - padded; // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap