From 2f1760a342c810660e474d1370cbbd0b3abac072 Mon Sep 17 00:00:00 2001 From: Revar Desmera Date: Thu, 9 Jan 2020 16:10:18 -0800 Subject: [PATCH] Reorganize arrays.scad docs --- arrays.scad | 447 ++++++++++++++++++++++++++------------------------- version.scad | 2 +- 2 files changed, 232 insertions(+), 217 deletions(-) diff --git a/arrays.scad b/arrays.scad index 78a010c..595a115 100644 --- a/arrays.scad +++ b/arrays.scad @@ -5,71 +5,16 @@ // ``` // use // ``` -////////////////////////////////////////////////////////////////////// - - -// Section: Terminology +// +// # Terminology // - **List**: An ordered collection of zero or more items. ie: ["a", "b", "c"] // - **Vector**: A list of numbers. ie: [4, 5, 6] // - **Array**: A nested list of lists, or list of lists of lists, or deeper. ie: [[2,3], [4,5], [6,7]] // - **Dimension**: The depth of nesting of lists in an array. A List is 1D. A list of lists is 2D. etc. +////////////////////////////////////////////////////////////////////// -// Section: List Operations - - -// Function: replist() -// Usage: -// replist(val, n) -// Description: -// Generates a list or array of `n` copies of the given `list`. -// If the count `n` is given as a list of counts, then this creates a -// multi-dimensional array, filled with `val`. -// Arguments: -// val = The value to repeat to make the list or array. -// n = The number of copies to make of `val`. -// Example: -// replist(1, 4); // Returns [1,1,1,1] -// replist(8, [2,3]); // Returns [[8,8,8], [8,8,8]] -// replist(0, [2,2,3]); // Returns [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]] -// replist([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]] -function replist(val, n, i=0) = - is_num(n)? [for(j=[1:1:n]) val] : - (i>=len(n))? val : - [for (j=[1:1:n[i]]) replist(val, n, i+1)]; - - -// Function: in_list() -// Description: Returns true if value `x` is in list `l`. -// Arguments: -// x = The value to search for. -// l = The list to search. -// idx = If given, searches the given subindexes for matches for `x`. -// Example: -// in_list("bar", ["foo", "bar", "baz"]); // Returns true. -// in_list("bee", ["foo", "bar", "baz"]); // Returns false. -// in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true. -function in_list(x,l,idx=undef) = search([x], l, num_returns_per_match=1, index_col_num=idx) != [[]]; - - -// Function: slice() -// Description: -// Returns a slice of a list. The first item is index 0. -// Negative indexes are counted back from the end. The last item is -1. -// Arguments: -// arr = The array/list to get the slice of. -// st = The index of the first item to return. -// end = The index after the last item to return, unless negative, in which case the last item to return. -// Example: -// slice([3,4,5,6,7,8,9], 3, 5); // Returns [6,7] -// slice([3,4,5,6,7,8,9], 2, -1); // Returns [5,6,7,8,9] -// slice([3,4,5,6,7,8,9], 1, 1); // Returns [] -// slice([3,4,5,6,7,8,9], 6, -1); // Returns [9] -// slice([3,4,5,6,7,8,9], 2, -2); // Returns [5,6,7,8] -function slice(arr,st,end) = let( - s=st<0?(len(arr)+st):st, - e=end<0?(len(arr)+end+1):end - ) [for (i=[s:1:e-1]) if (e>s) arr[i]]; +// Section: List Query Operations // Function: select() @@ -110,6 +55,124 @@ function select(list, start, end=undef) = ); +// Function: slice() +// Description: +// Returns a slice of a list. The first item is index 0. +// Negative indexes are counted back from the end. The last item is -1. +// Arguments: +// arr = The array/list to get the slice of. +// st = The index of the first item to return. +// end = The index after the last item to return, unless negative, in which case the last item to return. +// Example: +// slice([3,4,5,6,7,8,9], 3, 5); // Returns [6,7] +// slice([3,4,5,6,7,8,9], 2, -1); // Returns [5,6,7,8,9] +// slice([3,4,5,6,7,8,9], 1, 1); // Returns [] +// slice([3,4,5,6,7,8,9], 6, -1); // Returns [9] +// slice([3,4,5,6,7,8,9], 2, -2); // Returns [5,6,7,8] +function slice(arr,st,end) = let( + s=st<0?(len(arr)+st):st, + e=end<0?(len(arr)+end+1):end + ) [for (i=[s:1:e-1]) if (e>s) arr[i]]; + + +// Function: in_list() +// Description: Returns true if value `x` is in list `l`. +// Arguments: +// x = The value to search for. +// l = The list to search. +// idx = If given, searches the given subindexes for matches for `x`. +// Example: +// in_list("bar", ["foo", "bar", "baz"]); // Returns true. +// in_list("bee", ["foo", "bar", "baz"]); // Returns false. +// in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1); // Returns true. +function in_list(x,l,idx=undef) = search([x], l, num_returns_per_match=1, index_col_num=idx) != [[]]; + + +// Function: min_index() +// Usage: +// min_index(vals,[all]); +// 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: +// min_index([5,3,9,6,2,7,8,2,1]); // Returns: 4 +// min_index([5,3,9,6,2,7,8,2,1],all=true); // Returns: [4,7] +function min_index(vals, all=false) = + all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; + + +// Function: max_index() +// Usage: +// max_index(vals,[all]); +// 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) = + all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; + + +// Function: list_increasing() +// Usage: +// list_increasing(list) +// Description: +// Returns true if the list is (non-strictly) increasing +// Example: +// list_increasing([1,2,3,4]); // Returns: true +// list_increasing([1,3,2,4]); // Returns: false +// 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: +// list_decreasing(list) +// Description: +// Returns true if the list is (non-strictly) decreasing +// Example: +// list_decreasing([1,2,3,4]); // Returns: false +// list_decreasing([4,2,3,1]); // Returns: false +// 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(n))? val : + [for (j=[1:1:n[i]]) replist(val, n, i+1)]; + + // Function: list_range() // Usage: // list_range(n, [s], [e]) @@ -147,6 +210,10 @@ function list_range(n=undef, s=0, e=undef, step=undef) = assert(e!=undef||n!=undef, "Must supply one of `n` or `e`."); + +// Section: List Manipulation + + // Function: reverse() // Description: Reverses a list/array. // Arguments: @@ -220,7 +287,7 @@ function deduplicate(list, closed=false, eps=EPSILON) = // that the final length is exactly as requested. If you set it to `false` then the // algorithm will favor uniformity and the output list may have a different number of // entries due to rounding. -// +// // When applied to a path the output path is the same geometrical shape but has some vertices // repeated. This can be useful when you need to align paths with a different number of points. // (See also subdivide_path for a different way to do that.) @@ -234,18 +301,17 @@ function deduplicate(list, closed=false, eps=EPSILON) = // echo(repeat_entries(list, 6, exact=false)); // Ouputs [0,0,1,1,2,2,3,3] // echo(repeat_entries(list, [1,1,2,1], exact=false)); // Ouputs [0,1,2,2,3] function repeat_entries(list, N, exact = true) = - assert(is_list(list)) - assert((is_num(N) && N>0) || is_vector(N),"Parameter N to repeat_entries must be postive number or vector") - let( - length = len(list), - reps_guess = is_list(N) ? - assert(len(N)==len(list), "Vector parameter N to repeat_entries has the wrong length") - N - : replist(N/length,length), - reps = exact ? _sum_preserving_round(reps_guess) - : [for (val=reps_guess) round(val)] - ) - [for(i=[0:length-1]) each replist(list[i],reps[i])]; + assert(is_list(list)) + assert((is_num(N) && N>0) || is_vector(N),"Parameter N to repeat_entries must be postive number or vector") + let( + length = len(list), + reps_guess = is_list(N)? + assert(len(N)==len(list), "Vector parameter N to repeat_entries has the wrong length") + N : replist(N/length,length), + reps = exact? _sum_preserving_round(reps_guess) : + [for (val=reps_guess) round(val)] + ) + [for(i=[0:length-1]) each replist(list[i],reps[i])]; // Function: list_set() @@ -294,6 +360,42 @@ function list_set(list=[],indices,values,dflt=0,minlen=0) = ); +// Function: list_insert() +// Usage: +// list_insert(list, pos, elements); +// Description: +// Insert `elements` into `list` before position `pos`. +// Example: +// list_insert([3,6,9,12],1,5); // Returns [3,5,6,9,12] +// list_insert([3,6,9,12],[1,3],[5,11]); // Returns [3,5,6,9,11,12] +function list_insert(list, pos, elements, _i=0) = + assert(is_list(list)||is_string(list)) + is_list(pos)? ( + assert(len(pos)==len(elements)) + let( + idxs = sortidx(pos), + lastidx = pos[idxs[len(idxs)-1]] + ) + concat( + [ + for(i=idx(idxs)) each concat( + assert(pos[idxs[i]]<=len(list), "Indices in pos must be <= len(list)") + [for (j=[(i==0?0:pos[idxs[i-1]]):1:pos[idxs[i]]-1]) list[j]], + [elements[idxs[i]]] + ) + ], + [for (j=[lastidx:1:len(list)-1]) list[j]] + ) + ) : ( + assert(pos<=len(list), "Indices in pos must be <= len(list)") + concat( + slice(list,0,pos), + elements, + (posp.y) true])==0; - - -// Function: list_decreasing() -// Usage: -// list_decreasing(list) -// Description: -// Returns true if the list is (non-strictly) decreasing -// Example: -// list_decreasing([1,2,3,4]); // Returns: false -// list_decreasing([4,2,3,1]); // Returns: false -// 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.xlength)? list_trim(v,length) : list_pad(v,length,fill); -// Function: idx() -// Usage: -// i = idx(list); -// for(i=idx(list)) ... -// Description: -// Returns the range of indexes for the given list. -// Arguments: -// list = The list to returns the index range of. -// step = The step size to stride through the list. Default: 1 -// end = The delta from the end of the list. Default: -1 -// start = The starting index. Default: 0 -// Example(2D): -// colors = ["red", "green", "blue"]; -// for (i=idx(colors)) right(20*i) color(colors[i]) circle(d=10); -function idx(list, step=1, end=-1,start=0) = - assert(is_list(list)||is_string(list)) - [start : step : len(list)+end]; - - -// Function: enumerate() -// Description: -// Returns a list, with each item of the given list `l` numbered in a sublist. -// Something like: `[[0,l[0]], [1,l[1]], [2,l[2]], ...]` -// Arguments: -// l = List to enumerate. -// idx = If given, enumerates just the given subindex items of `l`. -// Example: -// enumerate(["a","b","c"]); // Returns: [[0,"a"], [1,"b"], [2,"c"]] -// enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1); // Returns: [[0,"a"], [1,"b"], [2,"c"]] -// enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]); // Returns: [[0,"a",12], [1,"b",10], [2,"c",14]] -// Example(2D): -// colors = ["red", "green", "blue"]; -// for (p=enumerate(colors)) right(20*p[0]) color(p[1]) circle(d=10); -function enumerate(l,idx=undef) = - assert(is_list(l)||is_string(list)) - (idx==undef)? - [for (i=[0:1:len(l)-1]) [i,l[i]]] : - [for (i=[0:1:len(l)-1]) concat([i], [for (j=idx) l[i][j]])]; +// Section: List Shuffling and Sorting // Function: shuffle() // Description: @@ -784,58 +788,46 @@ function unique(arr) = -// Section: Array Manipulation +// Section: List Iteration Helpers -// Function: subindex() -// Description: -// For each array item, return the indexed subitem. -// Returns a list of the values of each vector at the specfied -// index list or range. If the index list or range has -// only one entry the output list is flattened. -// Arguments: -// v = The given list of lists. -// idx = The index, list of indices, or range of indices to fetch. -// Example: -// v = [[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]; -// subindex(v,2); // Returns [3, 7, 11, 15] -// subindex(v,[2,1]); // Returns [[3, 2], [7, 6], [11, 10], [15, 14]] -// subindex(v,[1:3]); // Returns [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]] -function subindex(v, idx) = [ - for(val=v) let(value=[for(i=idx) val[i]]) - len(value)==1 ? value[0] : value -]; - - -// Function: min_index() +// Function: idx() // Usage: -// min_index(vals,[all]); +// i = idx(list); +// for(i=idx(list)) ... // 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. +// Returns the range of indexes for the given list. // Arguments: -// vals = vector of values -// all = set to true to return indices of all occurences of the minimum. Default: false -// Example: -// min_index([5,3,9,6,2,7,8,2,1]); // Returns: 4 -// min_index([5,3,9,6,2,7,8,2,1],all=true); // Returns: [4,7] -function min_index(vals, all=false) = - all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; +// list = The list to returns the index range of. +// step = The step size to stride through the list. Default: 1 +// end = The delta from the end of the list. Default: -1 +// start = The starting index. Default: 0 +// Example(2D): +// colors = ["red", "green", "blue"]; +// for (i=idx(colors)) right(20*i) color(colors[i]) circle(d=10); +function idx(list, step=1, end=-1,start=0) = + assert(is_list(list)||is_string(list)) + [start : step : len(list)+end]; -// Function: max_index() -// Usage: -// max_index(vals,[all]); +// Function: enumerate() // 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. +// Returns a list, with each item of the given list `l` numbered in a sublist. +// Something like: `[[0,l[0]], [1,l[1]], [2,l[2]], ...]` // Arguments: -// vals = vector of values -// all = set to true to return indices of all occurences of the maximum. Default: false +// l = List to enumerate. +// idx = If given, enumerates just the given subindex items of `l`. // 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) = - all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; +// enumerate(["a","b","c"]); // Returns: [[0,"a"], [1,"b"], [2,"c"]] +// enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1); // Returns: [[0,"a"], [1,"b"], [2,"c"]] +// enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]); // Returns: [[0,"a",12], [1,"b",10], [2,"c",14]] +// Example(2D): +// colors = ["red", "green", "blue"]; +// for (p=enumerate(colors)) right(20*p[0]) color(p[1]) circle(d=10); +function enumerate(l,idx=undef) = + assert(is_list(l)||is_string(list)) + (idx==undef)? + [for (i=[0:1:len(l)-1]) [i,l[i]]] : + [for (i=[0:1:len(l)-1]) concat([i], [for (j=idx) l[i][j]])]; // Function: pair() @@ -912,6 +904,28 @@ function permute(l,n=2,_s=0) = [for (i=[_s:1:len(l)-n], p=permute(l,n=n-1,_s=i+1)) concat([l[i]], p)]; +// Section: Array Manipulation + +// Function: subindex() +// Description: +// For each array item, return the indexed subitem. +// Returns a list of the values of each vector at the specfied +// index list or range. If the index list or range has +// only one entry the output list is flattened. +// Arguments: +// v = The given list of lists. +// idx = The index, list of indices, or range of indices to fetch. +// Example: +// v = [[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]; +// subindex(v,2); // Returns [3, 7, 11, 15] +// subindex(v,[2,1]); // Returns [[3, 2], [7, 6], [11, 10], [15, 14]] +// subindex(v,[1:3]); // Returns [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]] +function subindex(v, idx) = [ + for(val=v) let(value=[for(i=idx) val[i]]) + len(value)==1 ? value[0] : value +]; + + // Function: zip() // Usage: // zip(v1, v2, v3, [fit], [fill]); @@ -942,7 +956,7 @@ function zip(vecs, v2, v3, fit=false, fill=undef) = (v3!=undef)? zip([vecs,v2,v3], fit=fit, fill=fill) : (v2!=undef)? zip([vecs,v2], fit=fit, fill=fill) : assert(in_list(fit, [false, "short", "long"])) - assert(all( [for(v=vecs) is_list(v)]), "One of the inputs to zip is not a vector") + assert(all([for(v=vecs) is_list(v)]), "One of the inputs to zip is not a list") let( minlen = list_shortest(vecs), maxlen = list_longest(vecs), @@ -951,6 +965,7 @@ function zip(vecs, v2, v3, fit=false, fill=undef) = [for(i=[0:1:maxlen-1]) [for(v=vecs) for(x=(i