diff --git a/arrays.scad b/arrays.scad index 0a5b4ff..bbf8951 100644 --- a/arrays.scad +++ b/arrays.scad @@ -240,12 +240,18 @@ function list_insert(list, pos, elements) = ); -// True if the list is (non-strictly) increasing +// Function: list_increasing() +// Usage: +// list_increasing(list) +// Description: returns true if the list is (non-strictly) increasing function list_increasing(list,ind=0) = ind < len(list)-1 && list[ind]<=list[ind+1] ? list_increasing(list,ind+1) : (ind>=len(list)-1 ? true : false); -// True if the list is (non-strictly) decreasing +// Function: list_decreasing() +// Usage: +// list_increasing(list) +// Description: returns true if the list is (non-strictly) decreasing function list_decreasing(list,ind=0) = ind < len(list)-1 && list[ind]>=list[ind+1] ? list_increasing(list,ind+1) : (ind>=len(list)-1 ? true : false); diff --git a/roundcorners.scad b/roundcorners.scad index 938a2c6..0e58420 100644 --- a/roundcorners.scad +++ b/roundcorners.scad @@ -121,7 +121,43 @@ include // translate([0,60,0])polygon(round_corners([[0,0],[50,0],[50,50],[0,50]],all=[5,.7], // curve="smooth", type="cut")); // } - +// Example(Med2D): Rounding a path that is not closed in a three different ways. +// $fs=.25; +// $fa=1; +// zigzagx = [-10, 0, 10, 20, 29, 38, 46, 52, 59, 66, 72, 78, 83, 88, 92, 96, 99, 102, 112]; +// zigzagy = concat([0], flatten(replist([-10,10],8)), [-10,0]); +// zig = zip(zigzagx,zigzagy); +// stroke(zig,width=1); // Original shape +// fwd(20) // Smooth all corners with a cut of 4 and curvature parameter 0.6 +// stroke(round_corners(zig,all=[4,0.6],closed=false, curve="smooth", type="cut"),width=1); +// +// fwd(40) // Smooth all corners with circular arcs and a cut of 4 +// stroke(round_corners(zig,all=[4,0.6],closed=false, curve="circle", type="cut"),width=1); +// // Smooth all corners with a circular arc and radius 1.5 (close to maximum possible) +// fwd(60) // Note how the different points are cut back by different amounts +// stroke(round_corners(zig,all=1.5,closed=false, curve="circle", type="radius"),width=1); +// Example(spin): Rounding some random 3d paths +// $fn=36; +// list1= [[2.88736, 4.03497, 6.37209], [5.68221, 9.37103, 0.783548], [7.80846, 4.39414, 1.84377], +// [0.941085, 5.30548, 4.46753], [1.86054, 9.81574, 6.49753], [6.93818, 7.21163, 5.79453]]; +// list2= [[1.07907, 4.74091, 6.90039], [8.77585, 4.42248, 6.65185], [5.94714, 9.17137, 6.15642], +// [0.66266, 6.9563, 5.88423], [6.56454, 8.86334, 9.95311], [5.42015, 4.91874, 3.86696]]; +// extrude_2dpath_along_3dpath(regular_ngon(n=36,or=.1),round_corners(list1,closed=false, curve="smooth", type="cut", all=.65)); +// right(6) +// extrude_2dpath_along_3dpath(regular_ngon(n=36,or=.1),round_corners(list2,closed=false, curve="circle", type="cut", all=.75)); +// Example(spin): Rounding a spiral with increased rounding along the length +// $fn=36; +// // Construct a square spiral path in 3d +// square = [[0,0],[1,0],[1,1],[0,1]]; +// spiral = flatten(replist(concat(square,reverse(square)),5)); +// z= list_range(40)*.2+[for(i=[0:9]) each [i,i,i,i]]; +// // Make rounding parameters, which get larger up the spiral +// // and set the smoothing parameter to 1. +// rvect = zip([for(i=[0:9]) each [i,i,i,i]]/20,replist(1,40)); +// rounding = [for(i=rvect) [i]]; // Needed because zip removes a list level +// path3d = zip([spiral,z,rounding]); +// rpath = round_corners(path3d, curve="smooth", type="joint",closed=false); +// extrude_2dpath_along_3dpath( regular_ngon(n=36, or=.1), rpath); function round_corners(path, curve, type, all=undef, closed=true) = let( default_curvature = 0.5, // default curvature for "smooth" curves diff --git a/strings.scad b/strings.scad index 67bf5e2..63a2938 100644 --- a/strings.scad +++ b/strings.scad @@ -15,12 +15,15 @@ // 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) : +// substr("abcdefg",len=-2); // Returns "" +function substr(str, pos=0, len=undef) = + is_list(pos) ? _substr(str, pos[0], pos[1]-pos[0]+1) : + len == undef ? _substr(str, pos, len(str)-pos) : + _substr(str,pos,len); + +function _substr(str,pos,len,substr="") = 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])); + _substr(str, pos+1, len-1, str(substr, str[pos])); // Function suffix() // Usage: @@ -198,3 +201,111 @@ function str_split_recurse(str,sep,i,result) = function _remove_empty_strs(list) = list_remove(list, search([""], list,0)[0]); + + +// _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 +// cuts run time in half when the string is long. Two other string +// comparison methods were slower. +function _str_cmp(str,sindex,pattern) = + len(str)-sindex =0 && !_str_cmp(str,sindex, pattern) ? _str_match_last(str,pattern,sindex-1) : + (sindex >=0 ? sindex : undef); +function _str_matches(str,pattern) = + pattern == "" ? list_range(len(str)) : + [for(i=[0:1:len(str)-len(pattern)]) if (_str_cmp(str,i,pattern)) i]; + + +// Function: str_matches() +// Usage: +// str_matches(str,pattern) +// Description: +// Returns the indices of all matches where the string `pattern` appears in the input string `str`. +// If `pattern` is empty then it matches every character of `str`. +// Arguments: +// str = string to search +// pattern = string pattern to search for +// Example: + + +// Function: starts_with() +// Usage: +// starts_with(str,pattern) +// Description: +// Returns true if the input string `str` starts with the specified string pattern, `pattern`. +// Otherwise returns false. +// Arguments: +// str = string to search +// pattern = string pattern to search for +// Example: +// starts_with("abcdef","abc"); // Returns true +// starts_with("abcdef","def"); // Returns false +// starts_with("abcdef",""); // Returns true +function starts_with(str,pattern) = _str_cmp(str,0,pattern); + + +// Function: ends_with() +// Usage: +// ends_with(str,pattern) +// Description: +// Returns true if the input string `str` ends with the specified string pattern, `pattern`. +// Otherwise returns false. +// Arguments: +// str = string to search +// pattern = string pattern to search for +// Example: +// ends_with("abcdef","def"); // Returns true +// ends_with("abcdef","de"); // Returns false +// ends_with("abcdef",""); // Returns true +function ends_with(str,pattern) = _str_cmp(str,len(str)-len(pattern),pattern); +