mirror of
https://github.com/BelfrySCAD/BOSL2.git
synced 2025-01-04 03:09:45 +00:00
Merge pull request #44 from adrianVmariano/master
Add strings, structures, supershape fix, list_set
This commit is contained in:
commit
dc7a8a2203
3 changed files with 337 additions and 21 deletions
|
@ -477,41 +477,60 @@ module star(n, r, d, ir, id, step, realign=false, anchor=CENTER, spin=0)
|
|||
function _superformula(theta,m1,m2,n1,n2=1,n3=1,a=1,b=1) =
|
||||
pow(pow(abs(cos(m1*theta/4)/a),n2)+pow(abs(sin(m2*theta/4)/b),n3),-1/n1);
|
||||
|
||||
|
||||
// Function&Module: superformula_shape()
|
||||
// Function&Module: supershape()
|
||||
// Usage:
|
||||
// superformula_shape(step,m1,m2,n1,n2,n3,[a],[b]);
|
||||
// supershape(step,[m1],[m2],[n1],[n2],[n3],[a],[b],[r|d]);
|
||||
// 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 module, creates a 2D [Superformula](https://en.wikipedia.org/wiki/Superformula) shape.
|
||||
// Arguments:
|
||||
// step = The angle step size for sampling the superformula shape. Smaller steps are slower but more accurate.
|
||||
// scale = The scaling multiplier for the size of the shape.
|
||||
// m1 = The m1 argument for the superformula.
|
||||
// m2 = The m2 argument for the superformula.
|
||||
// n1 = The n1 argument for the superformula.
|
||||
// n2 = The n2 argument for the superformula.
|
||||
// n3 = The n3 argument for the superformula.
|
||||
// a = The a argument for the superformula.
|
||||
// b = The b argument for the superformula.
|
||||
// m1 = The m1 argument for the superformula. Default: 4.
|
||||
// m2 = The m2 argument for the superformula. Default: m1.
|
||||
// n1 = The n1 argument for the superformula. Default: 1.
|
||||
// n2 = The n2 argument for the superformula. Default: n1.
|
||||
// n3 = The n3 argument for the superformula. Default: n2.
|
||||
// a = The a argument for the superformula. Default: 1.
|
||||
// b = The b argument for the superformula. Default: a.
|
||||
// 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.
|
||||
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
|
||||
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
|
||||
// Example(2D):
|
||||
// superformula_shape(step=0.5,scale=100,m1=16,m2=16,n1=0.5,n2=0.5,n3=16);
|
||||
// supershape(step=0.5,m1=16,m2=16,n1=0.5,n2=0.5,n3=16,r=50);
|
||||
// Example(2D): Called as Function
|
||||
// stroke(close=true, superformula_shape(step=0.5,scale=100,m1=16,m2=16,n1=0.5,n2=0.5,n3=16));
|
||||
function superformula_shape(step=0.5,scale=1,m1,m2,n1,n2=1,n3=1,a=1,b=1, anchor=CENTER, spin=0) =
|
||||
// stroke(close=true, supershape(step=0.5,m1=16,m2=16,n1=0.5,n2=0.5,n3=16,d=100));
|
||||
// Examples2(2D):
|
||||
// for(n=[2:5]) right(2.5*(n-2)) supershape(m1=4,m2=4,n1=n,a=1,b=2); // Superellipses
|
||||
// m=[2,3,5,7]; for(i=[0:3]) right(2.5*i) supershape(.5,m1=m[i],n1=1);
|
||||
// m=[6,8,10,12]; for(i=[0:3]) right(2.7*i) supershape(.5,m1=m[i],n1=1,b=1.5); // m should be even
|
||||
// m=[1,2,3,5]; for(i=[0:3]) fwd(1.5*i) supershape(m1=m[i],n1=0.4);
|
||||
// supershape(m1=5, n1=4, n2=1); right(2.5) supershape(m1=5, n1=40, n2=10);
|
||||
// m=[2,3,5,7]; for(i=[0:3]) right(2.5*i) supershape(m1=m[i], n1=60, n2=55, n3=30);
|
||||
// n=[0.5,0.2,0.1,0.02]; for(i=[0:3]) right(2.5*i) supershape(m1=5,n1=n[i], n2=1.7);
|
||||
// supershape(m1=2, n1=1, n2=4, n3=8);
|
||||
// supershape(m1=7, n1=2, n2=8, n3=4);
|
||||
// supershape(m1=7, n1=3, n2=4, n3=17);
|
||||
// supershape(m1=4, n1=1/2, n2=1/2, n3=4);
|
||||
// supershape(m1=4, n1=4.0,n2=16, n3=1.5, a=0.9, b=9);
|
||||
// for(i=[1:4]) right(3*i) supershape(m1=i, m2=3*i, n1=2);
|
||||
// m=[4,6,10]; for(i=[0:2]) right(i*5) supershape(m1=m[i], n1=12, n2=8, n3=5, a=2.7);
|
||||
function supershape(step=0.5,m1=4,m2=undef,n1=1,n2=undef,n3=undef,a=1,b=undef,r=undef,d=undef,anchor=CENTER, spin=0) =
|
||||
let(
|
||||
r = get_radius(r=r,d=d,dflt=undef),
|
||||
m2 = is_def(m2) ? m2 : m1,
|
||||
n2 = is_def(n2) ? n2 : n1,
|
||||
n3 = is_def(n3) ? n3 : n2,
|
||||
b = is_def(b) ? b : a,
|
||||
steps = ceil(360/step),
|
||||
step = 360/steps,
|
||||
angs = [for (i = [0:steps-1]) step*i],
|
||||
rads = [for (a = angs) scale*_superformula(theta=a,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3)],
|
||||
path = [for (i = [0:steps-1]) let(a=angs[i]) rads[i]*[cos(a), sin(a)]]
|
||||
) rot(spin, p=move(-max(rads)*normalize(anchor), p=path));
|
||||
|
||||
|
||||
module superformula_shape(step=0.5,scale=1,m1,m2,n1,n2=1,n3=1,a=1,b=1, anchor=CENTER, spin=0)
|
||||
polygon(superformula_shape(step=step,scale=scale,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b, anchor=anchor, spin=spin));
|
||||
rads = [for (theta = angs) _superformula(theta=theta,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b)],
|
||||
scale = is_def(r) ? r/max(rads) : 1,
|
||||
path = [for (i = [0:steps-1]) let(a=angs[i]) scale*rads[i]*[cos(a), sin(a)]]
|
||||
) rot(spin, p=move(-scale*max(rads)*normalize(anchor), p=path));
|
||||
|
||||
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)
|
||||
polygon(supershape(step=step,m1=m1,m2=m2,n1=n1,n2=n2,n3=n3,a=a,b=b, r=r,d=d, anchor=anchor, spin=spin));
|
||||
|
||||
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap
|
||||
|
|
200
strings.scad
Normal file
200
strings.scad
Normal file
|
@ -0,0 +1,200 @@
|
|||
// Section: String Operations
|
||||
//-----------------------------------------------------------------------------
|
||||
// Function: substr()
|
||||
// Usage:
|
||||
// substr(str, [pos], [len])
|
||||
// Description:
|
||||
// Returns a substring from a string start at position `pos` with length `len`, or
|
||||
// if `len` isn't given, the rest of the string.
|
||||
// Arguments:
|
||||
// str = string to operate on
|
||||
// pos = starting index of substring, or vector of first and last position. Default: 0
|
||||
// len = length of substring, or omit it to get the rest of the string. If len is less than zero the emptry string is returned.
|
||||
// Example:
|
||||
// substr("abcdefg",3,3); // Returns "def"
|
||||
// substr("abcdefg",2); // Returns "cdefg"
|
||||
// substr("abcdefg",len=3); // Returns "abc"
|
||||
// substr("abcdefg",[2,4]); // Returns "cde"
|
||||
// substr("abcdefg",len=-2)); // Returns ""
|
||||
function substr(str, pos=0, len=undef, substr="") =
|
||||
is_list(pos) ? substr(str, pos[0], pos[1]-pos[0]+1) :
|
||||
len <= 0 || pos>=len(str) ? substr :
|
||||
len == undef ? substr(str, pos, len(str)-pos, substr) :
|
||||
substr(str, pos+1, len-1, str(substr, str[pos]));
|
||||
|
||||
// Function suffix()
|
||||
// Usage:
|
||||
// suffix(str,len)
|
||||
// Description:
|
||||
// Returns the last `len` characters from the input string
|
||||
function suffix(str,len) = substr(str, len(str)-len,len);
|
||||
|
||||
|
||||
// Function: str_join()
|
||||
// Usage:
|
||||
// str_join(list, [sep])
|
||||
// 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));
|
||||
|
||||
// Function: downcase()
|
||||
// Usage:
|
||||
// downcase(str)
|
||||
// Description:
|
||||
// Returns the string with the standard ASCII upper case letters A-Z replaced
|
||||
// by their lower case versions.
|
||||
// Arguments:
|
||||
// str = string to convert
|
||||
// Example:
|
||||
// downcase("ABCdef"); // Returns "abcdef"
|
||||
function downcase(str) =
|
||||
str_join([for(char=str) let(code=ord(char)) code>=65 && code<=90 ? chr(code+32) : char]);
|
||||
|
||||
// Function: str_int()
|
||||
// Usage:
|
||||
// str_int(str, [base])
|
||||
// Description:
|
||||
// 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
|
||||
// upper case or lower case.
|
||||
// Arguments:
|
||||
// str = string to convert
|
||||
// base = base for conversion, from 2-16. Default: 10
|
||||
// Example:
|
||||
// str_int("349"); // Returns 349
|
||||
// str_int("-37"); // Returns -37
|
||||
// str_int("+97"); // Returns 97
|
||||
// str_int("43.9"); // Returns nan
|
||||
// str_int("1011010",2); // Returns 90
|
||||
// str_int("13",2); // Returns nan
|
||||
// str_int("dead",16); // Returns 57005
|
||||
// str_int("CEDE", 16); // Returns 52958
|
||||
// str_int(""); // Returns 0
|
||||
function str_int(str,base=10) =
|
||||
str==undef ? undef :
|
||||
len(str)==0 ? 0 :
|
||||
let(str=downcase(str))
|
||||
str[0] == "-" ? -_str_int_recurse(substr(str,1),base,len(str)-2) :
|
||||
str[0] == "+" ? _str_int_recurse(substr(str,1),base,len(str)-2) :
|
||||
_str_int_recurse(str,base,len(str)-1);
|
||||
|
||||
function _str_int_recurse(str,base,i) =
|
||||
let( digit = search(str[i],"0123456789abcdef"),
|
||||
last_digit = digit == [] || digit[0] >= base ? (0/0) : digit[0])
|
||||
i==0 ? last_digit :
|
||||
_str_int_recurse(str,base,i-1)*base + last_digit;
|
||||
|
||||
// Function: str_float()
|
||||
// Usage:
|
||||
// str_float(str)
|
||||
// Description:
|
||||
// Converts a string to a floating point number. Returns NaN if the
|
||||
// conversion fails.
|
||||
// Arguments:
|
||||
// str = string to convert
|
||||
// Example:
|
||||
// str_float("44"); // Returns 44
|
||||
// str_float("3.4"); // Returns 3.4
|
||||
// str_float("-99.3332"); // Returns -99.3332
|
||||
// str_float("3.483e2"); // Returns 348.3
|
||||
// str_float("-44.9E2"); // Returns -4490
|
||||
// str_float("7.342e-4"); // Returns 0.0007342
|
||||
// str_float(""); // Returns 0
|
||||
function str_float(str) =
|
||||
str==undef ? undef :
|
||||
len(str) == 0 ? 0 :
|
||||
in_list(str[1], ["+","-"]) ? (0/0) : // Don't allow --3, or +-3
|
||||
str[0]=="-" ? -str_float(substr(str,1)) :
|
||||
str[0]=="+" ? str_float(substr(str,1)) :
|
||||
let(esplit = str_split(str,"eE") )
|
||||
len(esplit)==2 ? str_float(esplit[0]) * pow(10,str_int(esplit[1])) :
|
||||
let( dsplit = str_split(str,["."]))
|
||||
str_int(dsplit[0])+str_int(dsplit[1])/pow(10,len(dsplit[1]));
|
||||
|
||||
// Function: str_frac()
|
||||
// Usage:
|
||||
// str_frac(str)
|
||||
// Description:
|
||||
// Converts a string fraction, two integers separated by a "/" character, to a floating point number.
|
||||
// Arguments:
|
||||
// str = string to convert
|
||||
// Example:
|
||||
// str_frac("3/4"); // Returns 0.75
|
||||
// str_frac("-77/9"); // Returns -8.55556
|
||||
// str_frac("+1/3"); // Returns 0.33333
|
||||
// str_frac("19"); // Returns 19
|
||||
// str_frac(""); // Returns 0
|
||||
// str_frac("3/0"); // Returns inf
|
||||
// str_frac("0/0"); // Returns nan
|
||||
function str_frac(str) =
|
||||
str == undef ? undef :
|
||||
let( num = str_split(str,"/"))
|
||||
len(num)==1 ? str_int(num[0]) :
|
||||
len(num)==2 ? str_int(num[0])/str_int(num[1]) :
|
||||
(0/0);
|
||||
|
||||
// Function: str_num()
|
||||
// Usage:
|
||||
// str_num(str)
|
||||
// Description:
|
||||
// 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.
|
||||
// Example:
|
||||
// str_num("3/4"); // Returns 0.75
|
||||
// str_num("3.4e-2"); // Returns 0.034
|
||||
function str_num(str) =
|
||||
str == undef ? undef :
|
||||
let( val = str_frac(str) )
|
||||
val == val ? val :
|
||||
str_float(str);
|
||||
|
||||
|
||||
// Function: str_split()
|
||||
// Usage:
|
||||
// str_split(str, sep, [keep_nulls])
|
||||
// Description:
|
||||
// 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 no empty strings are included in the output list.
|
||||
//
|
||||
// If sep is a single string then each character in sep is treated as a delimiting character and the input string is
|
||||
// split at every delimiting character. Empty strings can occur whenever two delimiting characters are sequential.
|
||||
// If sep is a list of strings then the input string is split sequentially using each string from the list in order.
|
||||
// If keep_nulls is true then the output will have length equal to `len(sep)+1`, possibly with trailing null strings
|
||||
// if the string runs out before the separator list.
|
||||
// Arguments
|
||||
// str = string to split
|
||||
// sep = a string or list of strings to use for the separator
|
||||
// keep_nulls = boolean value indicating whether to keep null strings in the output list. Default: true
|
||||
// Example:
|
||||
// str_split("abc+def-qrs*iop","*-+"); // Returns ["abc", "def", "qrs", "iop"]
|
||||
// str_split("abc+*def---qrs**iop+","*-+");// Returns ["abc", "", "def", "", "", "qrs", "", "iop", ""]
|
||||
// str_split("abc def"," "); // Returns ["abc", "", "", "", "", "", "def"]
|
||||
// str_split("abc def"," ",keep_nulls=false); // Returns ["abc", "def"]
|
||||
// str_split("abc+def-qrs*iop",["+","-","*"]); // Returns ["abc", "def", "qrs", "iop"]
|
||||
// str_split("abc+def-qrs*iop",["-","+","*"]); // Returns ["abc+def", "qrs*iop", "", ""]
|
||||
function str_split(str,sep,keep_nulls=true) =
|
||||
!keep_nulls ? _remove_empty_strs(str_split(str,sep,keep_nulls=true)) :
|
||||
is_list(sep) ? str_split_recurse(str,sep,i=0,result=[]) :
|
||||
let( cutpts = concat([-1],sort(flatten(search(sep, str,0))),[len(str)]))
|
||||
[for(i=[0:len(cutpts)-2]) substr(str,cutpts[i]+1,cutpts[i+1]-cutpts[i]-1)];
|
||||
|
||||
function str_split_recurse(str,sep,i,result) =
|
||||
i == len(sep) ? concat(result,[str]) :
|
||||
let( pos = search(sep[i], str),
|
||||
end = pos==[] ? len(str) : pos[0]
|
||||
)
|
||||
str_split_recurse(substr(str,end+1), sep, i+1,
|
||||
concat(result, [substr(str,0,end)]));
|
||||
|
||||
function _remove_empty_strs(list) =
|
||||
list_remove(list, search([""], list,0)[0]);
|
97
structs.scad
Normal file
97
structs.scad
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Section: struct operations
|
||||
//
|
||||
// A struct is a data structure that associates arbitrary keywords (of any type) with values (of any type).
|
||||
// Structures are implemented as lists of [keyword, value] pairs.
|
||||
//
|
||||
// An empty list `[]` is an empty structure and can be used wherever a structure input is required.
|
||||
|
||||
// Function: struct_set()
|
||||
// Usage:
|
||||
// struct_set(struct, keyword, value, [grow])
|
||||
// struct_set(struct, [keyword1, value1, keyword2, value2, ...], [grow])
|
||||
// Description:
|
||||
// Sets the keyword(s) in the structure to the specified value(s), returning a new updated structure. If a keyword
|
||||
// exists its value is changed, otherwise the keyword is added to the structure. If grow is set to false then
|
||||
// it is an error to set a keyword not already defined in the structure. If you specify the same keyword twice
|
||||
// that is also an error. If speed matters, use the first form with scalars rather than the list form: this is
|
||||
// about thirty times faster.
|
||||
// Arguments:
|
||||
// struct = input structure
|
||||
// keyword = keyword to set
|
||||
// value = value to set the keyword to
|
||||
// grow = Set to true to allow structure to grow, or false for new keywords to generate an error. Default: true
|
||||
function struct_set(struct, keyword, value=undef, grow=true) =
|
||||
!is_list(keyword) ?
|
||||
let(ind=search([keyword],struct,1,0)[0])
|
||||
(ind==[] ? assert(grow,str("Unknown keyword \"",keyword))
|
||||
concat(struct, [[keyword,value]]) :
|
||||
list_set([ind], [[keyword,value]],struct)) :
|
||||
_parse_pairs(struct,keyword,grow);
|
||||
|
||||
function _parse_pairs(spec, input, grow=true, index=0, result=undef) =
|
||||
assert(len(input)%2==0,"Odd number of entries in [keyword,value] pair list")
|
||||
let( result = result==undef ? spec : result)
|
||||
index == len(input) ? result :
|
||||
_parse_pairs(spec,input,grow,index+2,struct_set(result, input[index], input[index+1],grow));
|
||||
|
||||
// Function: struct_remove()
|
||||
// Usage:
|
||||
// struct_remove(struct, keyword)
|
||||
// Description:
|
||||
// Remove keyword or keyword list from a structure
|
||||
// Arguments:
|
||||
// struct = input structure
|
||||
// keyword = a single string (keyword) or list of strings (keywords) to remove
|
||||
function struct_remove(struct, keyword) =
|
||||
is_string(keyword)? struct_remove(struct, [keyword]) :
|
||||
let(ind = search(keyword, struct))
|
||||
list_remove(struct, ind);
|
||||
|
||||
// Function: struct_val()
|
||||
// Usage:
|
||||
// struct_val(struct,keyword)
|
||||
// Description:
|
||||
// Returns the value for the specified keyword in the structure, or undef if the keyword is not present
|
||||
// Arguments:
|
||||
// struct = input structure
|
||||
// keyword = keyword whose value to return
|
||||
function struct_val(struct,keyword) =
|
||||
assert(is_def(keyword),"keyword is missing")
|
||||
let(ind = search([keyword],struct)[0])
|
||||
ind == [] ? undef : struct[ind][1];
|
||||
|
||||
// Function: struct_keys()
|
||||
// Usage:
|
||||
// struct_keys(struct)
|
||||
// Description:
|
||||
// Returns a list of the keys in a structure
|
||||
// Arguments:
|
||||
// struct = input structure
|
||||
function struct_keys(struct) =
|
||||
[for(entry=struct) entry[0]];
|
||||
|
||||
// Function&Module struct_echo()
|
||||
// Usage:
|
||||
// struct_echo(struct, [name])
|
||||
// Description:
|
||||
// Displays a list of structure keywords and values, one pair per line, for easier reading.
|
||||
// Arguments:
|
||||
// struct = input structure
|
||||
// name = optional structure name to list at the top of the output. Default: ""
|
||||
function struct_echo(struct,name="") =
|
||||
let( keylist = [for(entry=struct) str(" ",entry[0],": ",entry[1],"\n")])
|
||||
echo(str("\nStructure ",name,"\n",str_join(keylist)))
|
||||
undef;
|
||||
|
||||
module struct_echo(struct,name="") {
|
||||
dummy = struct_echo(struct,name);
|
||||
}
|
||||
|
||||
// Function is_struct()
|
||||
// Usage:
|
||||
// is_struct(struct)
|
||||
// Description: Returns true if the input has the form of a structure, false otherwise.
|
||||
function is_struct(struct, ind=0) =
|
||||
struct == [] ||
|
||||
let(dim = array_dim(struct))
|
||||
len(dim)==2 && dim[1]==2;
|
Loading…
Reference in a new issue