From 60be226e8509c7724cd3a967aec9976ae5a51175 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 22 Jun 2019 13:33:49 -0400 Subject: [PATCH 1/2] Added 3 examples to roundcorners. Added doc text for list_increasing and list_decreasing. Added str_match, str_matches, starts_width, ends_width. Fixed substr to use tail recursion. --- arrays.scad | 10 ++++- roundcorners.scad | 38 +++++++++++++++- strings.scad | 110 +++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 150 insertions(+), 8 deletions(-) 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..b398e54 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,100 @@ 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() +// 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: +// str_matches("abc123def123abc","123"); // Returns [3,9] +// str_matches("abc123def123abc","b"); // Returns [1,13] +// str_matches("abc123def123abc","1234"); // Returns [] +// str_matches("abc",""); // Returns [0,1,2] +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: 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); + From e3be9a3b5b20755ebcbe74d88d6f88fcad89ae2b Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Sat, 22 Jun 2019 14:50:01 -0400 Subject: [PATCH 2/2] Combined str_matches and str_match and added start parameter. --- strings.scad | 51 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/strings.scad b/strings.scad index b398e54..63a2938 100644 --- a/strings.scad +++ b/strings.scad @@ -219,35 +219,53 @@ function _str_cmp_recurse(str,sindex,pattern,plen,pindex=0,) = // Function: str_match() // Usage: -// str_match(str,pattern) +// str_match(str,pattern,[last],[all],[start]) // Description: -// Searches input string `str` for the string `pattern` and returns the index of the first or last match in `str`. -// If `pattern` is empty then it returns 0. If `pattern` doesn't match it returns undef. +// Searches input string `str` for the string `pattern` and returns the index or indices of the matches in `str`. +// By default str_match() returns the index of the first match in `str`. If `last` is true then it returns the index of the last match. +// If the pattern is the empty string the first match is at zero and the last match is the last character of the `str`. +// If `start` is set then the search begins at index start, working either forward and backward from that position. If you set `start` +// and `last` is true then the search will find the pattern if it begins at index `start`. If no match exists, returns undef. +// If you set `all` to true then all str_match() returns all of the matches in a list, or an empty list if there are no matches. // Arguments: // str = string to search // pattern = string pattern to search for // last = set to true to return the last match. Default: false +// all = set to true to return all matches as a list. Overrides last. Default: false +// start = index where the search starts // Example: // str_match("abc123def123abc","123"); // Returns 3 // str_match("abc123def123abc","b"); // Returns 1 // str_match("abc123def123abc","1234"); // Returns undef // str_match("abc",""); // Returns 0 -// str_match("abc123def123abc","123",last=true); // Returns 9 -// str_match("abc123def123abc","b",last=true); // Returns 13 -// str_match("abc123def123abc","1234",last=true); // Returns undef -// str_match("abc","",last=true); // Returns 2 -function str_match(str,pattern,last=false) = - pattern=="" ? (last?len(str)-1:0) : - last ? _str_match_last(str,pattern,len(str)-len(pattern)) : - _str_match(str,pattern,len(str)-len(pattern)); +// str_match("abc123def123", "123", start=4); // Returns 9 +// str_match("abc123def123abc","123",last=true); // Returns 9 +// str_match("abc123def123abc","b",last=true); // Returns 13 +// str_match("abc123def123abc","1234",last=true); // Returns undef +// str_match("abc","",last=true); // Returns 2 +// str_match("abc123def123", "123", start=8, last=true)); // Returns 3 +// str_match("abc123def123abc","123",all=true); // Returns [3,9] +// str_match("abc123def123abc","b",all=true); // Returns [1,13] +// str_match("abc123def123abc","1234",all=true); // Returns [] +// str_match("abc","",all=true); // Returns [0,1,2] +function str_match(str,pattern,start=undef,last=false,all=false) = + all ? _str_matches(str,pattern) : + let( start = first_defined([start,last?len(str)-len(pattern):0])) + pattern=="" ? start : + last ? _str_match_last(str,pattern,start) : + _str_match(str,pattern,len(str)-len(pattern),start); -function _str_match(str,pattern,max_sindex,sindex=0) = +function _str_match(str,pattern,max_sindex,sindex) = sindex<=max_sindex && !_str_cmp(str,sindex, pattern) ? _str_match(str,pattern,max_sindex,sindex+1) : (sindex <= max_sindex ? sindex : undef); - function _str_match_last(str,pattern,sindex) = 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) @@ -258,13 +276,6 @@ function _str_match_last(str,pattern,sindex) = // str = string to search // pattern = string pattern to search for // Example: -// str_matches("abc123def123abc","123"); // Returns [3,9] -// str_matches("abc123def123abc","b"); // Returns [1,13] -// str_matches("abc123def123abc","1234"); // Returns [] -// str_matches("abc",""); // Returns [0,1,2] -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: starts_with()