Merge pull request #926 from adrianVmariano/master

string, supershape n= arg, doc fixes
This commit is contained in:
Revar Desmera 2022-08-15 16:25:29 -07:00 committed by GitHub
commit 80e646aed8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 49 deletions

View file

@ -305,9 +305,9 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) {
// Function&Module: ellipse() // Function&Module: ellipse()
// Usage: As a Module // Usage: As a Module
// ellipse(r|d=, [realign=], [circum=], ...) [ATTACHMENTS]; // ellipse(r|d=, [realign=], [circum=], [uniform=], ...) [ATTACHMENTS];
// Usage: As a Function // Usage: As a Function
// path = ellipse(r|d=, [realign=], [circum=], ...); // path = ellipse(r|d=, [realign=], [circum=], [uniform=], ...);
// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable
// See Also: circle(), circle_2tangents(), circle_3points() // See Also: circle(), circle_2tangents(), circle_3points()
// Description: // Description:
@ -315,13 +315,16 @@ module circle(r, d, points, corner, anchor=CENTER, spin=0) {
// When called as a function, returns a 2D list of points (path) for a polygon that approximates a circle or ellipse of the given size. // When called as a function, returns a 2D list of points (path) for a polygon that approximates a circle or ellipse of the given size.
// By default the point list or shape is the same as the one you would get by scaling the output of {{circle()}}, but with this module your // By default the point list or shape is the same as the one you would get by scaling the output of {{circle()}}, but with this module your
// attachments to the ellipse will retain their dimensions, whereas scaling a circle with attachments will also scale the attachments. // attachments to the ellipse will retain their dimensions, whereas scaling a circle with attachments will also scale the attachments.
// If you set unifom to true then you will get a polygon with congruent sides whose vertices lie on the ellipse. // If you set `uniform` to true then you will get a polygon with congruent sides whose vertices lie on the ellipse. The `circum` option
// requests a polygon that circumscribes the requested ellipse (so the specified ellipse will fit into the resulting polygon). Note that
// you cannot gives `circum=true` and `uniform=true`.
// Arguments: // Arguments:
// r = Radius of the circle or pair of semiaxes of ellipse // r = Radius of the circle or pair of semiaxes of ellipse
// --- // ---
// d = Diameter of the circle or a pair giving the full X and Y axis lengths. // d = Diameter of the circle or a pair giving the full X and Y axis lengths.
// realign = If false starts the approximate ellipse with a point on the X+ axis. If true the midpoint of a side is on the X+ axis and the first point of the polygon is below the X+ axis. This can result in a very different polygon when $fn is small. Default: false // realign = If false starts the approximate ellipse with a point on the X+ axis. If true the midpoint of a side is on the X+ axis and the first point of the polygon is below the X+ axis. This can result in a very different polygon when $fn is small. Default: false
// circum = If true, the polygon that approximates the circle will be upsized slightly to circumscribe the theoretical circle. If false, it inscribes the theoretical circle. Default: false // uniform = If true, the polygon that approximates the circle will have segments of equal length. Only works if `circum=false`. Default: false
// circum = If true, the polygon that approximates the circle will be upsized slightly to circumscribe the theoretical circle. If false, it inscribes the theoretical circle. If this is true then `uniform` must be false. Default: false
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
// Example(2D): By Radius // Example(2D): By Radius
@ -1396,18 +1399,22 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
// Function&Module: supershape() // Function&Module: supershape()
// Usage: As Module // Usage: As Module
// supershape(step, [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]) [ATTACHMENTS]; // supershape([step],[n=], [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]) [ATTACHMENTS];
// Usage: As Function // Usage: As Function
// path = supershape(step, [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]); // path = supershape([step], [n=], [m1=], [m2=], [n1=], [n2=], [n3=], [a=], [b=], [r=/d=]);
// Topics: Shapes (2D), Paths (2D), Path Generators, Attachable // Topics: Shapes (2D), Paths (2D), Path Generators, Attachable
// See Also: circle(), ellipse() // See Also: circle(), ellipse()
// Description: // Description:
// When called as a function, returns a 2D path for the outline of the [Superformula](https://en.wikipedia.org/wiki/Superformula) shape. // When called as a function, returns a 2D path for the outline of the [Superformula](https://en.wikipedia.org/wiki/Superformula) shape.
// When called as a module, creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape. // When called as a module, creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape.
// Note that the "hull" type anchoring (the default) is more intuitive for concave star-like shapes, but the anchor points do not // Note that the "hull" type anchoring (the default) is more intuitive for concave star-like shapes, but the anchor points do not
// necesarily lie on the line of the anchor vector, which can be confusing, especially for simpler, ellipse-like shapes. // necesarily lie on the line of the anchor vector, which can be confusing, especially for simpler, ellipse-like shapes.
// Note that the default step angle of 0.5 is very fine and can be slow, but due to the complex curves of the supershape,
// many points are often required to give a good result.
// Arguments: // Arguments:
// step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate. // step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate. Default: 0.5
// ---
// n = Produce n points as output. Alternative to step. Not to be confused with shape parameters n1 and n2.
// m1 = The m1 argument for the superformula. Default: 4. // m1 = The m1 argument for the superformula. Default: 4.
// m2 = The m2 argument for the superformula. Default: m1. // m2 = The m2 argument for the superformula. Default: m1.
// n1 = The n1 argument for the superformula. Default: 1. // n1 = The n1 argument for the superformula. Default: 1.
@ -1416,7 +1423,6 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
// a = The a argument for the superformula. Default: 1. // a = The a argument for the superformula. Default: 1.
// b = The b argument for the superformula. Default: a. // b = The b argument for the superformula. Default: a.
// r = Radius of the shape. Scale shape to fit in a circle of radius r. // r = Radius of the shape. Scale shape to fit in a circle of radius r.
// ---
// d = Diameter of the shape. Scale shape to fit in a circle of diameter d. // d = Diameter of the shape. Scale shape to fit in a circle of diameter d.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0`
@ -1446,26 +1452,25 @@ function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
// Examples: // Examples:
// linear_extrude(height=0.3, scale=0) supershape(step=1, m1=6, n1=0.4, n2=0, n3=6); // linear_extrude(height=0.3, scale=0) supershape(step=1, m1=6, n1=0.4, n2=0, n3=6);
// linear_extrude(height=5, scale=0) supershape(step=1, b=3, m1=6, n1=3.8, n2=16, n3=10); // linear_extrude(height=5, scale=0) supershape(step=1, b=3, m1=6, n1=3.8, n2=16, n3=10);
function supershape(step=0.5, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0, atype="hull") = function supershape(step=0.5, n, m1=4, m2, n1=1, n2, n3, a=1, b, r, d,anchor=CENTER, spin=0, atype="hull") =
assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"") assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"")
let( let(
n = first_defined([n, ceil(360/step)]),
angs = lerpn(360,0,n,endpoint=false),
r = get_radius(r=r, d=d, dflt=undef), r = get_radius(r=r, d=d, dflt=undef),
m2 = is_def(m2) ? m2 : m1, m2 = is_def(m2) ? m2 : m1,
n2 = is_def(n2) ? n2 : n1, n2 = is_def(n2) ? n2 : n1,
n3 = is_def(n3) ? n3 : n2, n3 = is_def(n3) ? n3 : n2,
b = is_def(b) ? b : a, b = is_def(b) ? b : a,
steps = ceil(360/step), // superformula returns r(theta), the point in polar coordinates
step = 360/steps, rvals = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)],
angs = [for (i = [0:steps]) step*i], scale = is_def(r) ? r/max(rvals) : 1,
rads = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)], path = [for (i=idx(angs)) scale*rvals[i]*[cos(angs[i]), sin(angs[i])]]
scale = is_def(r) ? r/max(rads) : 1,
path = [for (i = [steps:-1:1]) let(a=angs[i]) scale*rads[i]*[cos(a), sin(a)]]
) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull"); ) reorient(anchor,spin, two_d=true, path=path, p=path, extent=atype=="hull");
module supershape(step=0.5,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0, atype="hull") { module supershape(step=0.5,n,m1=4,m2=undef,n1,n2=undef,n3=undef,a=1,b=undef, r=undef, d=undef, anchor=CENTER, spin=0, atype="hull") {
check = assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\""); check = assert(in_list(atype, _ANCHOR_TYPES), "Anchor type must be \"hull\" or \"intersect\"");
path = supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d); path = supershape(step=step,n=n,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b,r=r,d=d);
attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) { attachable(anchor,spin,extent=atype=="hull", two_d=true, path=path) {
polygon(path); polygon(path);
children(); children();

View file

@ -13,7 +13,7 @@
// Function: substr() // Function: substr()
// Usage: // Usage:
// substr(str, [pos], [len]) // newstr = substr(str, [pos], [len]);
// Description: // Description:
// Returns a substring from a string start at position `pos` with length `len`, or // Returns a substring from a string start at position `pos` with length `len`, or
// if `len` isn't given, the rest of the string. // if `len` isn't given, the rest of the string.
@ -39,7 +39,7 @@ function _substr(str,pos,len,substr="") =
// Function: suffix() // Function: suffix()
// Usage: // Usage:
// suffix(str,len) // newstr = suffix(str,len);
// Description: // Description:
// Returns the last `len` characters from the input string `str`. // Returns the last `len` characters from the input string `str`.
// If `len` is longer than the length of `str`, then the entirety of `str` is returned. // If `len` is longer than the length of `str`, then the entirety of `str` is returned.
@ -56,7 +56,7 @@ function suffix(str,len) =
// Function: str_find() // Function: str_find()
// Usage: // Usage:
// str_find(str,pattern,[last=],[all=],[start=]) // ind = str_find(str,pattern,[last=],[all=],[start=]);
// Description: // Description:
// Searches input string `str` for the string `pattern` and returns the index or indices of the matches in `str`. // Searches input string `str` for the string `pattern` and returns the index or indices of the matches in `str`.
// By default `str_find()` returns the index of the first match in `str`. If `last` is true then it returns the index of the last match. // By default `str_find()` returns the index of the first match in `str`. If `last` is true then it returns the index of the last match.
@ -94,37 +94,56 @@ function str_find(str,pattern,start=undef,last=false,all=false) =
_str_find_first(str,pattern,len(str)-len(pattern),start); _str_find_first(str,pattern,len(str)-len(pattern),start);
function _str_find_first(str,pattern,max_sindex,sindex) = function _str_find_first(str,pattern,max_sindex,sindex) =
sindex<=max_sindex && !_str_cmp(str,sindex, pattern)? sindex<=max_sindex && !substr_match(str,sindex, pattern)?
_str_find_first(str,pattern,max_sindex,sindex+1) : _str_find_first(str,pattern,max_sindex,sindex+1) :
(sindex <= max_sindex ? sindex : undef); (sindex <= max_sindex ? sindex : undef);
function _str_find_last(str,pattern,sindex) = function _str_find_last(str,pattern,sindex) =
sindex>=0 && !_str_cmp(str,sindex, pattern)? sindex>=0 && !substr_match(str,sindex, pattern)?
_str_find_last(str,pattern,sindex-1) : _str_find_last(str,pattern,sindex-1) :
(sindex >=0 ? sindex : undef); (sindex >=0 ? sindex : undef);
function _str_find_all(str,pattern) = function _str_find_all(str,pattern) =
pattern == "" ? count(len(str)) : pattern == "" ? count(len(str)) :
[for(i=[0:1:len(str)-len(pattern)]) if (_str_cmp(str,i,pattern)) i]; [for(i=[0:1:len(str)-len(pattern)]) if (substr_match(str,i,pattern)) i];
// Function: substr_match()
// Usage
// bool = substr_match(str,start,pattern);
// Description:
// Returns true if the string `pattern` matches the string `str` starting
// at `str[start]`. If the string is too short for the pattern, or
// `start` is out of bounds---either negative or beyond the end of the
// string---then substr_match returns false.
// Arguments:
// str = String to search
// start = Starting index for search in str
// pattern = String pattern to search for
// Examples:
// substr_match("abcde",2,"cd"); // Returns true
// substr_match("abcde",2,"cx"); // Returns false
// substr_match("abcde",2,"cdef"); // Returns false
// substr_match("abcde",-2,"cd"); // Returns false
// substr_match("abcde",19,"cd"); // Returns false
// substr_match("abc",1,""); // Returns true
// _str_cmp(str,sindex,pattern)
// returns true if the string pattern matches the string
// starting at index position sindex in the string.
// //
// This is carefully optimized for speed. Precomputing the length // This is carefully optimized for speed. Precomputing the length
// cuts run time in half when the string is long. Two other string // cuts run time in half when the string is long. Two other string
// comparison methods were slower. // comparison methods were slower.
function _str_cmp(str,sindex,pattern) = function substr_match(str,start,pattern) =
len(str)-sindex <len(pattern)? false : len(str)-start <len(pattern)? false
_str_cmp_recurse(str,sindex,pattern,len(pattern)); : _substr_match_recurse(str,start,pattern,len(pattern));
function _str_cmp_recurse(str,sindex,pattern,plen,pindex=0,) = function _substr_match_recurse(str,sindex,pattern,plen,pindex=0,) =
pindex < plen && pattern[pindex]==str[sindex] ? _str_cmp_recurse(str,sindex+1,pattern,plen,pindex+1): (pindex==plen); pindex < plen && pattern[pindex]==str[sindex]
? _substr_match_recurse(str,sindex+1,pattern,plen,pindex+1)
: (pindex==plen);
// Function: starts_with() // Function: starts_with()
// Usage: // Usage:
// starts_with(str,pattern) // bool = starts_with(str,pattern);
// Description: // Description:
// Returns true if the input string `str` starts with the specified string pattern, `pattern`. // Returns true if the input string `str` starts with the specified string pattern, `pattern`.
// Otherwise returns false. // Otherwise returns false.
@ -135,12 +154,12 @@ function _str_cmp_recurse(str,sindex,pattern,plen,pindex=0,) =
// starts_with("abcdef","abc"); // Returns true // starts_with("abcdef","abc"); // Returns true
// starts_with("abcdef","def"); // Returns false // starts_with("abcdef","def"); // Returns false
// starts_with("abcdef",""); // Returns true // starts_with("abcdef",""); // Returns true
function starts_with(str,pattern) = _str_cmp(str,0,pattern); function starts_with(str,pattern) = substr_match(str,0,pattern);
// Function: ends_with() // Function: ends_with()
// Usage: // Usage:
// ends_with(str,pattern) // bool = ends_with(str,pattern);
// Description: // Description:
// Returns true if the input string `str` ends with the specified string pattern, `pattern`. // Returns true if the input string `str` ends with the specified string pattern, `pattern`.
// Otherwise returns false. // Otherwise returns false.
@ -151,13 +170,13 @@ function starts_with(str,pattern) = _str_cmp(str,0,pattern);
// ends_with("abcdef","def"); // Returns true // ends_with("abcdef","def"); // Returns true
// ends_with("abcdef","de"); // Returns false // ends_with("abcdef","de"); // Returns false
// ends_with("abcdef",""); // Returns true // ends_with("abcdef",""); // Returns true
function ends_with(str,pattern) = _str_cmp(str,len(str)-len(pattern),pattern); function ends_with(str,pattern) = substr_match(str,len(str)-len(pattern),pattern);
// Function: str_split() // Function: str_split()
// Usage: // Usage:
// str_split(str, sep, [keep_nulls]) // string_list = str_split(str, sep, [keep_nulls]);
// Description: // Description:
// Breaks an input string into substrings using a separator or list of separators. If keep_nulls is true // Breaks an input string into substrings using a separator or list of separators. If keep_nulls is true
// then two sequential separator characters produce an empty string in the output list. If keep_nulls is false // then two sequential separator characters produce an empty string in the output list. If keep_nulls is false
@ -207,7 +226,7 @@ function _remove_empty_strs(list) =
// Function: str_join() // Function: str_join()
// Usage: // Usage:
// str_join(list, [sep]) // str = str_join(list, [sep]);
// Description: // Description:
// Returns the concatenation of a list of strings, optionally with a // Returns the concatenation of a list of strings, optionally with a
// separator string inserted between each string on the list. // separator string inserted between each string on the list.
@ -226,7 +245,7 @@ function str_join(list,sep="",_i=0, _result="") =
// Function: str_strip() // Function: str_strip()
// Usage: // Usage:
// str_strip(s,c,[start],[end]); // str = str_strip(s,c,[start],[end]);
// Description: // Description:
// Takes a string `s` and strips off all leading and/or trailing characters that exist in string `c`. // Takes a string `s` and strips off all leading and/or trailing characters that exist in string `c`.
// By default strips both leading and trailing characters. If you set start or end to true then // By default strips both leading and trailing characters. If you set start or end to true then
@ -293,7 +312,7 @@ function str_pad(str,length,char=" ",left=false) =
// Function: str_replace_char() // Function: str_replace_char()
// Usage: // Usage:
// newstr = str_replace_char(str, char, replace) // newstr = str_replace_char(str, char, replace);
// Description: // Description:
// Replace every occurence of `char` in the input string with the string `replace` which // Replace every occurence of `char` in the input string with the string `replace` which
// can be any string. // can be any string.
@ -306,7 +325,7 @@ function str_replace_char(str,char,replace) =
// Function: downcase() // Function: downcase()
// Usage: // Usage:
// downcase(str) // newstr = downcase(str);
// Description: // Description:
// Returns the string with the standard ASCII upper case letters A-Z replaced // Returns the string with the standard ASCII upper case letters A-Z replaced
// by their lower case versions. // by their lower case versions.
@ -320,7 +339,7 @@ function downcase(str) =
// Function: upcase() // Function: upcase()
// Usage: // Usage:
// upcase(str) // newstr = upcase(str);
// Description: // Description:
// Returns the string with the standard ASCII lower case letters a-z replaced // Returns the string with the standard ASCII lower case letters a-z replaced
// by their upper case versions. // by their upper case versions.
@ -353,7 +372,7 @@ function rand_str(n, charset, seed) =
// Function: parse_int() // Function: parse_int()
// Usage: // Usage:
// parse_int(str, [base]) // num = parse_int(str, [base])
// Description: // Description:
// Converts a string into an integer with any base up to 16. Returns NaN if // Converts a string into an integer with any base up to 16. Returns NaN if
// conversion fails. Digits above 9 are represented using letters A-F in either // conversion fails. Digits above 9 are represented using letters A-F in either
@ -389,7 +408,7 @@ function _parse_int_recurse(str,base,i) =
// Function: parse_float() // Function: parse_float()
// Usage: // Usage:
// parse_float(str) // num = parse_float(str);
// Description: // Description:
// Converts a string to a floating point number. Returns NaN if the // Converts a string to a floating point number. Returns NaN if the
// conversion fails. // conversion fails.
@ -417,7 +436,7 @@ function parse_float(str) =
// Function: parse_frac() // Function: parse_frac()
// Usage: // Usage:
// parse_frac(str,[mixed=],[improper=],[signed=]) // num = parse_frac(str,[mixed=],[improper=],[signed=]);
// Description: // Description:
// Converts a string fraction to a floating point number. A string fraction has the form `[-][# ][#/#]` where each `#` is one or more of the // Converts a string fraction to a floating point number. A string fraction has the form `[-][# ][#/#]` where each `#` is one or more of the
// digits 0-9, and there is an optional sign character at the beginning. // digits 0-9, and there is an optional sign character at the beginning.
@ -471,7 +490,7 @@ function parse_frac(str,mixed=true,improper=true,signed=true) =
// Function: parse_num() // Function: parse_num()
// Usage: // Usage:
// parse_num(str) // num = parse_num(str);
// Description: // Description:
// Converts a string to a number. The string can be either a fraction (two integers separated by a "/") or a floating point number. // Converts a string to a number. The string can be either a fraction (two integers separated by a "/") or a floating point number.
// Returns NaN if the conversion fails. // Returns NaN if the conversion fails.
@ -491,7 +510,7 @@ function parse_num(str) =
// Function: format_int() // Function: format_int()
// Usage: // Usage:
// format_int(i, [mindigits]); // str = format_int(i, [mindigits]);
// Description: // Description:
// Formats an integer number into a string. This can handle larger numbers than `str()`. // Formats an integer number into a string. This can handle larger numbers than `str()`.
// Arguments: // Arguments:
@ -539,7 +558,7 @@ function format_fixed(f,digits=6) =
// Function: format_float() // Function: format_float()
// Usage: // Usage:
// format_float(f,[sig]); // str = format_float(f,[sig]);
// Description: // Description:
// Formats the given floating point number `f` into a string with `sig` significant digits. // 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. // Strips trailing `0`s after the decimal point. Strips trailing decimal point.

View file

@ -21,6 +21,17 @@ module test_downcase() {
} }
test_downcase(); test_downcase();
module test_substr_match(){
assert(substr_match("abcde",2,"cd"));
assert(!substr_match("abcde",2,"cx"));
assert(!substr_match("abcde",2,"cdef"));
assert(!substr_match("abcde",-2,"cd"));
assert(!substr_match("abcde",19,"cd"));
assert(substr_match("abc",1,""));
assert(!substr_match("",0,"a"));
assert(substr_match("",0,""));
}
module test_starts_with() { module test_starts_with() {
assert(!starts_with("", "abc")); assert(!starts_with("", "abc"));