diff --git a/arrays.scad b/arrays.scad index 93a5fc5..728cc47 100644 --- a/arrays.scad +++ b/arrays.scad @@ -53,6 +53,150 @@ function _same_type(a,b, depth) = && []==[for(i=idx(a)) if( ! _same_type(a[i],b[i],depth-1) ) 0] ); + + +// Function: list_shortest() +// Usage: +// llen = list_shortest(array); +// Topics: List Handling +// See Also: list_longest() +// Description: +// Returns the length of the shortest sublist in a list of lists. +// Arguments: +// array = A list of lists. +// Example: +// slen = list_shortest([[3,4,5],[6,7,8,9]]); // Returns: 3 +function list_shortest(array) = + assert(is_list(array), "Invalid input." ) + min([for (v = array) len(v)]); + + +// Function: list_longest() +// Usage: +// llen = list_longest(array); +// Topics: List Handling +// See Also: list_shortest() +// Description: +// Returns the length of the longest sublist in a list of lists. +// Arguments: +// array = A list of lists. +// Example: +// llen = list_longest([[3,4,5],[6,7,8,9]]); // Returns: 4 +function list_longest(array) = + assert(is_list(array), "Invalid input." ) + max([for (v = array) len(v)]); + + + +// Function: in_list() +// Usage: +// bool = in_list(val, list, [idx]); +// Topics: List Handling +// Description: +// Returns true if value `val` is in list `list`. When `val==NAN` the answer will be false for any list. +// Arguments: +// val = The simple value to search for. +// list = The list to search. +// idx = If given, searches the given columns for matches for `val`. +// Example: +// a = in_list("bar", ["foo", "bar", "baz"]); // Returns true. +// b = in_list("bee", ["foo", "bar", "baz"]); // Returns false. +// c = in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true. +function in_list(val,list,idx) = + assert( is_list(list) && (is_undef(idx) || is_finite(idx)), + "Invalid input." ) + let( s = search([val], list, num_returns_per_match=1, index_col_num=idx)[0] ) + s==[] || s==[[]] ? false + : is_undef(idx) ? val==list[s] + : val==list[s][idx]; + + +// Function: find_first_match() +// Topics: List Handling +// See Also: in_list() +// Usage: +// idx = find_first_match(val, list, [start=], [eps=]); +// indices = find_first_match(val, list, all=true, [start=], [eps=]); +// Description: +// Finds the first item in `list` that matches `val`, returning the index. +// Arguments: +// val = The value to search for. If given a function literal of signature `function (x)`, uses that function to check list items. Returns true for a match. +// list = The list to search through. +// --- +// start = The index to start searching from. +// all = If true, returns a list of all matching item indices. +// eps = The maximum allowed floating point rounding error for numeric comparisons. +function find_first_match(val, list, start=0, all=false, eps=EPSILON) = + all? [ + for (i=[start:1:len(list)-1]) + if ( + (!is_func(val) && approx(val, list[i], eps=eps)) || + (is_func(val) && val(list[i])) + ) i + ] : + __find_first_match(val, list, eps=eps, i=start); + +function __find_first_match(val, list, eps, i=0) = + i >= len(list)? undef : + ( + (!is_func(val) && approx(val, list[i], eps=eps)) || + (is_func(val) && val(list[i])) + )? i : __find_first_match(val, list, eps=eps, i=i+1); + + +// Function: list_increasing() +// Usage: +// bool = list_increasing(list); +// Topics: List Handling +// See Also: max_index(), min_index(), list_decreasing() +// Description: +// Returns true if the list is (non-strictly) increasing +// Example: +// a = list_increasing([1,2,3,4]); // Returns: true +// b = list_increasing([1,3,2,4]); // Returns: false +// c = list_increasing([4,3,2,1]); // Returns: false +function list_increasing(list) = + assert(is_list(list)||is_string(list)) + len([for (p=pair(list)) if(p.x>p.y) true])==0; + + +// Function: list_decreasing() +// Usage: +// bool = list_decreasing(list); +// Topics: List Handling +// See Also: max_index(), min_index(), list_increasing() +// Description: +// Returns true if the list is (non-strictly) decreasing +// Example: +// a = list_decreasing([1,2,3,4]); // Returns: false +// b = list_decreasing([4,2,3,1]); // Returns: false +// c = list_decreasing([4,3,2,1]); // Returns: true +function list_decreasing(list) = + assert(is_list(list)||is_string(list)) + len([for (p=pair(list)) if(p.x=len(array) , "Improper index list." ) + is_string(array)? str_join(bselect( [for (x=array) x], index)) : + [for(i=[0:len(array)-1]) if (index[i]) array[i]]; + + + + + +// Section: List Construction + + // Function: list() // Topics: List Handling, Type Conversion // Usage: @@ -246,155 +416,6 @@ function force_list(value, n=1, fill) = is_undef(fill)? [for (i=[1:1:n]) value] : [value, for (i=[2:1:n]) fill]; -// Function: add_scalar() -// Usage: -// v = add_scalar(v, s); -// Topics: List Handling -// Description: -// Given a list and a scalar, returns the list with the scalar added to each item in it. -// If given a list of arrays, recursively adds the scalar to the each array. -// Arguments: -// v = The initial array. -// s = A scalar value to add to every item in the array. -// Example: -// a = add_scalar([1,2,3],3); // Returns: [4,5,6] -// b = add_scalar([[1,2,3],[3,4,5]],3); // Returns: [[4,5,6],[6,7,8]] -function add_scalar(v,s) = - is_finite(s) ? [for (x=v) is_list(x)? add_scalar(x,s) : is_finite(x) ? x+s: x] : v; - - -// Function: in_list() -// Usage: -// bool = in_list(val, list, [idx]); -// Topics: List Handling -// Description: -// Returns true if value `val` is in list `list`. When `val==NAN` the answer will be false for any list. -// Arguments: -// val = The simple value to search for. -// list = The list to search. -// idx = If given, searches the given columns for matches for `val`. -// Example: -// a = in_list("bar", ["foo", "bar", "baz"]); // Returns true. -// b = in_list("bee", ["foo", "bar", "baz"]); // Returns false. -// c = in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true. -function in_list(val,list,idx) = - assert( is_list(list) && (is_undef(idx) || is_finite(idx)), - "Invalid input." ) - let( s = search([val], list, num_returns_per_match=1, index_col_num=idx)[0] ) - s==[] || s==[[]] ? false - : is_undef(idx) ? val==list[s] - : val==list[s][idx]; - - -// Function: find_first_match() -// Topics: List Handling -// See Also: in_list() -// Usage: -// idx = find_first_match(val, list, [start=], [eps=]); -// indices = find_first_match(val, list, all=true, [start=], [eps=]); -// Description: -// Finds the first item in `list` that matches `val`, returning the index. -// Arguments: -// val = The value to search for. If given a function literal of signature `function (x)`, uses that function to check list items. Returns true for a match. -// list = The list to search through. -// --- -// start = The index to start searching from. -// all = If true, returns a list of all matching item indices. -// eps = The maximum allowed floating point rounding error for numeric comparisons. -function find_first_match(val, list, start=0, all=false, eps=EPSILON) = - all? [ - for (i=[start:1:len(list)-1]) - if ( - (!is_func(val) && approx(val, list[i], eps=eps)) || - (is_func(val) && val(list[i])) - ) i - ] : - __find_first_match(val, list, eps=eps, i=start); - -function __find_first_match(val, list, eps, i=0) = - i >= len(list)? undef : - ( - (!is_func(val) && approx(val, list[i], eps=eps)) || - (is_func(val) && val(list[i])) - )? i : __find_first_match(val, list, eps=eps, i=i+1); - - -// Function: min_index() -// Usage: -// idx = min_index(vals); -// idxlist = min_index(vals, all=true); -// Topics: List Handling -// See Also: max_index(), list_increasing(), list_decreasing() -// Description: -// Returns the index of the first occurrence of the minimum value in the given list. -// If `all` is true then returns a list of all indices where the minimum value occurs. -// Arguments: -// vals = vector of values -// all = set to true to return indices of all occurences of the minimum. Default: false -// Example: -// a = min_index([5,3,9,6,2,7,8,2,1]); // Returns: 8 -// b = min_index([5,3,9,6,2,7,8,2,7],all=true); // Returns: [4,7] -function min_index(vals, all=false) = - assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") - all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; - - -// Function: max_index() -// Usage: -// idx = max_index(vals); -// idxlist = max_index(vals, all=true); -// Topics: List Handling -// See Also: min_index(), list_increasing(), list_decreasing() -// Description: -// Returns the index of the first occurrence of the maximum value in the given list. -// If `all` is true then returns a list of all indices where the maximum value occurs. -// Arguments: -// vals = vector of values -// all = set to true to return indices of all occurences of the maximum. Default: false -// Example: -// max_index([5,3,9,6,2,7,8,9,1]); // Returns: 2 -// max_index([5,3,9,6,2,7,8,9,1],all=true); // Returns: [2,7] -function max_index(vals, all=false) = - assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") - all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; - - -// Function: list_increasing() -// Usage: -// bool = list_increasing(list); -// Topics: List Handling -// See Also: max_index(), min_index(), list_decreasing() -// Description: -// Returns true if the list is (non-strictly) increasing -// Example: -// a = list_increasing([1,2,3,4]); // Returns: true -// b = list_increasing([1,3,2,4]); // Returns: false -// c = list_increasing([4,3,2,1]); // Returns: false -function list_increasing(list) = - assert(is_list(list)||is_string(list)) - len([for (p=pair(list)) if(p.x>p.y) true])==0; - - -// Function: list_decreasing() -// Usage: -// bool = list_decreasing(list); -// Topics: List Handling -// See Also: max_index(), min_index(), list_increasing() -// Description: -// Returns true if the list is (non-strictly) decreasing -// Example: -// a = list_decreasing([1,2,3,4]); // Returns: false -// b = list_decreasing([4,2,3,1]); // Returns: false -// c = list_decreasing([4,3,2,1]); // Returns: true -function list_decreasing(list) = - assert(is_list(list)||is_string(list)) - len([for (p=pair(list)) if(p.xlen(valuelist)), str("List `valuelist` too short; its length should be ",len(trueind)) ) + assert( !(len(trueind)=len(array) , "Improper index list." ) - is_string(array)? str_join(bselect( [for (x=array) x], index)) : - [for(i=[0:len(array)-1]) if (index[i]) array[i]]; - - -// Function: list_bset() -// Usage: -// arr = list_bset(indexset, valuelist, [dflt]); -// Topics: List Handling -// See Also: bselect() -// Description: -// Opposite of `bselect()`. Returns a list the same length as `indexlist`, where each item will -// either be 0 if the corresponding item in `indexset` is false, or the next sequential value -// from `valuelist` if the item is true. The number of `true` values in `indexset` must be equal -// to the length of `valuelist`. -// Arguments: -// indexset = A list of boolean values. -// valuelist = The list of values to set into the returned list. -// dflt = Default value to store when the indexset item is false. -// Example: -// a = list_bset([false,true,false,true,false], [3,4]); // Returns: [0,3,0,4,0] -// b = list_bset([false,true,false,true,false], [3,4], dflt=1); // Returns: [1,3,1,4,1] -function list_bset(indexset, valuelist, dflt=0) = - assert(is_list(indexset), "The index set is not a list." ) - assert(is_list(valuelist), "The `valuelist` is not a list." ) - let( trueind = search([true], indexset,0)[0] ) - assert( !(len(trueind)>len(valuelist)), str("List `valuelist` too short; its length should be ",len(trueind)) ) - assert( !(len(trueind)0 , "Invalid or empty list of numbers.") + all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; + + +// Function: max_index() +// Usage: +// idx = max_index(vals); +// idxlist = max_index(vals, all=true); +// Topics: List Handling +// See Also: min_index(), list_increasing(), list_decreasing() +// Description: +// Returns the index of the first occurrence of the maximum value in the given list. +// If `all` is true then returns a list of all indices where the maximum value occurs. +// Arguments: +// vals = vector of values +// all = set to true to return indices of all occurences of the maximum. Default: false +// Example: +// max_index([5,3,9,6,2,7,8,9,1]); // Returns: 2 +// max_index([5,3,9,6,2,7,8,9,1],all=true); // Returns: [2,7] +function max_index(vals, all=false) = + assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") + all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; + + // Section: Vector Searching - // Function: closest_point() // Usage: // index = closest_point(pt, points);