diff --git a/comparisons.scad b/comparisons.scad index 9718ec5..e78d189 100644 --- a/comparisons.scad +++ b/comparisons.scad @@ -650,7 +650,7 @@ function sort(list, idx=undef) = is_string(list)? str_join(sort([for (x = list) x],idx)) : !is_list(list) || len(list)<=1 ? list : is_homogeneous(list,1) - ? let(size = array_dim(list[0])) + ? let(size = list_shape(list[0])) size==0 ? _sort_scalars(list) : len(size)!=1 ? _sort_general(list,idx) : is_undef(idx) ? _sort_vectors(list) @@ -692,7 +692,7 @@ function sortidx(list, idx=undef) = !is_list(list) || len(list)<=1 ? list : is_homogeneous(list,1) ? let( - size = array_dim(list[0]), + size = list_shape(list[0]), aug = ! (size==0 || len(size)==1) ? 0 // for general sorting : [for(i=[0:len(list)-1]) concat(i,list[i])], // for scalar or vector sorting lidx = size==0? [1] : // scalar sorting diff --git a/lists.scad b/lists.scad index fb2d804..f8bd372 100644 --- a/lists.scad +++ b/lists.scad @@ -84,6 +84,59 @@ function max_length(array) = + +// Internal. Not exposed. +function _list_shape_recurse(v) = + !is_list(v[0]) + ? len( [for(entry=v) if(!is_list(entry)) 0] ) == 0 ? [] : [undef] + : let( + firstlen = is_list(v[0]) ? len(v[0]): undef, + first = len( [for(entry = v) if(! is_list(entry) || (len(entry) != firstlen)) 0 ] ) == 0 ? firstlen : undef, + leveldown = flatten(v) + ) + is_list(leveldown[0]) + ? concat([first],_list_shape_recurse(leveldown)) + : [first]; + +function _list_shape_recurse(v) = + let( alen = [for(vi=v) is_list(vi) ? len(vi): -1] ) + v==[] || max(alen)==-1 ? [] : + let( add = max(alen)!=min(alen) ? undef : alen[0] ) + concat( add, _list_shape_recurse(flatten(v))); + + +// Function: list_shape() +// Usage: +// dims = list_shape(v, [depth]); +// Topics: Matrices, Array Handling +// Description: +// Returns the size of a multi-dimensional array, a list of the lengths at each depth. +// If the returned value has `dims[i] = j` then it means the ith index ranges of j items. +// The return `dims[0]` is equal to the length of v. Then `dims[1]` is equal to the +// length of the lists in v, and in general, `dims[i]` is equal to the length of the items +// nested to depth i in the list v. If the length of items at that depth is inconsistent, then +// `undef` is returned. If no items exist at that depth then `0` is returned. Note that +// for simple vectors or matrices it is faster to compute `len(v)` and `len(v[0])`. +// Arguments: +// v = list to get shape of +// depth = depth to compute the size of. If not given, returns a list of sizes at all depths. +// Example: +// a = list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]); // Returns [2,2,3] +// b = list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0); // Returns 2 +// c = list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2); // Returns 3 +// d = list_shape([[[1,2,3],[4,5,6]],[[7,8,9]]]); // Returns [2,undef,3] +function list_shape(v, depth=undef) = + assert( is_undef(depth) || ( is_finite(depth) && depth>=0 ), "Invalid depth.") + ! is_list(v) ? 0 : + (depth == undef) + ? concat([len(v)], _list_shape_recurse(v)) + : (depth == 0) + ? len(v) + : let( dimlist = _list_shape_recurse(v)) + (depth > len(dimlist))? 0 : dimlist[depth-1] ; + + + // Function: in_list() // Usage: // bool = in_list(val, list, [idx]); @@ -641,31 +694,44 @@ function list_insert(list, indices, values) = // Function: list_remove() // Usage: -// list = list_remove(list, indices); +// list = list_remove(list, ind); // Topics: List Handling // See Also: list_set(), list_insert(), list_remove_values() // Description: -// Remove all items from `list` whose indexes are in `indices`. +// If `ind` is a number remove `list[ind]` from the list. If `ind` is a list of indices +// remove from the list the item all items whose indices appear in `ind`. If you give +// indices that are not in the list they are ignored. // Arguments: // list = The list to remove items from. -// indices = The list of indexes of items to remove. +// ind = index or list of indices of items to remove. // Example: -// a = list_insert([3,6,9,12],1); // Returns: [3,9,12] -// b = list_insert([3,6,9,12],[1,3]); // Returns: [3,9] -function list_remove(list, indices) = - assert(is_list(list)) - is_finite(indices) ? - [ - for (i=[0:1:min(indices, len(list)-1)-1]) list[i], - for (i=[min(indices, len(list)-1)+1:1:len(list)-1]) list[i] - ] - : indices==[] ? list - : assert( is_vector(indices), "Invalid list `indices`." ) +// a = list_remove([3,6,9,12],1); // Returns: [3,9,12] +// b = list_remove([3,6,9,12],[1,3]); // Returns: [3,9] +// c = list_remove([3,6],3); // Returns: [3,6] +function list_remove(list, ind) = + assert(is_list(list), "Invalid list in list_remove") + is_finite(ind) ? + ( + (ind<0 || ind>=len(list)) ? list + : + [ + for (i=[0:1:ind-1]) list[i], + for (i=[ind+1:1:len(list)-1]) list[i] + ] + ) + : ind==[] ? list + : assert( is_vector(ind), "Invalid index list in list_remove") + let(sres = search(count(list),ind,1)) [ for(i=[0:len(list)-1]) - if ( []==search(i,indices,1) ) - list[i] - ]; + if (sres[i] == []) + list[i] + ]; + +// This method is faster for long lists with few values to remove +// let( rem = list_set([], indices, repeat(1,len(indices)), minlen=len(list))) +// [for(i=idx(list)) if (rem[i]==0) list[i]]; + // Function: list_remove_values() @@ -931,54 +997,6 @@ function permutations(l,n=2) = // Section: Changing list structure -// Internal. Not exposed. -function _array_dim_recurse(v) = - !is_list(v[0]) - ? len( [for(entry=v) if(!is_list(entry)) 0] ) == 0 ? [] : [undef] - : let( - firstlen = is_list(v[0]) ? len(v[0]): undef, - first = len( [for(entry = v) if(! is_list(entry) || (len(entry) != firstlen)) 0 ] ) == 0 ? firstlen : undef, - leveldown = flatten(v) - ) - is_list(leveldown[0]) - ? concat([first],_array_dim_recurse(leveldown)) - : [first]; - -function _array_dim_recurse(v) = - let( alen = [for(vi=v) is_list(vi) ? len(vi): -1] ) - v==[] || max(alen)==-1 ? [] : - let( add = max(alen)!=min(alen) ? undef : alen[0] ) - concat( add, _array_dim_recurse(flatten(v))); - - -// Function: array_dim() -// Usage: -// dims = array_dim(v, [depth]); -// Topics: Matrices, Array Handling -// Description: -// Returns the size of a multi-dimensional array. Returns a list of dimension lengths. The length -// of `v` is the dimension `0`. The length of the items in `v` is dimension `1`. The length of the -// items in the items in `v` is dimension `2`, etc. For each dimension, if the length of items at -// that depth is inconsistent, `undef` will be returned. If no items of that dimension depth exist, -// `0` is returned. Otherwise, the consistent length of items in that dimensional depth is -// returned. -// Arguments: -// v = Array to get dimensions of. -// depth = Dimension to get size of. If not given, returns a list of dimension lengths. -// Example: -// a = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]); // Returns [2,2,3] -// b = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0); // Returns 2 -// c = array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2); // Returns 3 -// d = array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]); // Returns [2,undef,3] -function array_dim(v, depth=undef) = - assert( is_undef(depth) || ( is_finite(depth) && depth>=0 ), "Invalid depth.") - ! is_list(v) ? 0 : - (depth == undef) - ? concat([len(v)], _array_dim_recurse(v)) - : (depth == 0) - ? len(v) - : let( dimlist = _array_dim_recurse(v)) - (depth > len(dimlist))? 0 : dimlist[depth-1] ; // Function: list_to_matrix() diff --git a/skin.scad b/skin.scad index 37f01cc..804d0db 100644 --- a/skin.scad +++ b/skin.scad @@ -436,7 +436,7 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close assert(capsOK, "caps must be boolean or a list of two booleans") assert(!closed || !caps, "Cannot make closed shape with caps") let( - profile_dim=array_dim(profiles,2), + profile_dim=list_shape(profiles,2), profiles_zcheck = (profile_dim != 2) || (profile_dim==2 && is_list(z) && len(z)==len(profiles)), profiles_ok = (profile_dim==2 && is_list(z) && len(z)==len(profiles)) || profile_dim==3 ) diff --git a/tests/test_lists.scad b/tests/test_lists.scad index bbfc541..be29703 100644 --- a/tests/test_lists.scad +++ b/tests/test_lists.scad @@ -156,9 +156,14 @@ test_list_set(); module test_list_remove() { assert(list_remove([3,6,9,12],1) == [3,9,12]); + assert(list_remove([3,6,9,12],[1]) == [3,9,12]); assert(list_remove([3,6,9,12],[1,3]) == [3,9]); assert(list_remove([3,6,9],[]) == [3,6,9]); assert(list_remove([],[]) == []); + assert(list_remove([1,2,3], -1)==[1,2,3]); + assert(list_remove([1,2,3], 3)==[1,2,3]); + assert(list_remove([1,2,3], [-1,3])==[1,2,3]); + assert(list_remove([1,2,3], [-1,1,3])==[1,3]); } test_list_remove(); @@ -402,19 +407,19 @@ module test_full_flatten() { test_full_flatten(); -module test_array_dim() { - assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) == [2,2,3]); - assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0) == 2); - assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2) == 3); - assert(array_dim([[[1,2,3],[4,5,6]],[[7,8,9]]]) == [2,undef,3]); - assert(array_dim([1,2,3,4,5,6,7,8,9]) == [9]); - assert(array_dim([[1],[2],[3],[4],[5],[6],[7],[8],[9]]) == [9,1]); - assert(array_dim([]) == [0]); - assert(array_dim([[]]) == [1,0]); - assert(array_dim([[],[]]) == [2,0]); - assert(array_dim([[],[1]]) == [2,undef]); +module test_list_shape() { + assert(list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]]) == [2,2,3]); + assert(list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 0) == 2); + assert(list_shape([[[1,2,3],[4,5,6]],[[7,8,9],[10,11,12]]], 2) == 3); + assert(list_shape([[[1,2,3],[4,5,6]],[[7,8,9]]]) == [2,undef,3]); + assert(list_shape([1,2,3,4,5,6,7,8,9]) == [9]); + assert(list_shape([[1],[2],[3],[4],[5],[6],[7],[8],[9]]) == [9,1]); + assert(list_shape([]) == [0]); + assert(list_shape([[]]) == [1,0]); + assert(list_shape([[],[]]) == [2,0]); + assert(list_shape([[],[1]]) == [2,undef]); } -test_array_dim(); +test_list_shape(); // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap