From 39b4b7282d7b582600dd9df5d5ee11ebf5b7be55 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Fri, 24 Jul 2020 00:04:16 +0100 Subject: [PATCH 1/5] Update typeof(), int(), range(), list_of() and segs --- common.scad | 31 ++++++++++++++++++++----------- tests/test_common.scad | 27 ++++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 12 deletions(-) diff --git a/common.scad b/common.scad index 8d0655a..6cc910e 100644 --- a/common.scad +++ b/common.scad @@ -15,7 +15,8 @@ // Usage: // typ = typeof(x); // Description: -// Returns a string representing the type of the value. One of "undef", "boolean", "number", "nan", "string", "list", or "range" +// Returns a string representing the type of the value. One of "undef", "boolean", "number", "nan", "string", "list", "range" or "invalid". +// Some malformed "ranges", like '[0:NAN:INF]' and '[0:"a":INF]', may be classified as "undef" or "invalid". function typeof(x) = is_undef(x)? "undef" : is_bool(x)? "boolean" : @@ -23,8 +24,11 @@ function typeof(x) = is_nan(x)? "nan" : is_string(x)? "string" : is_list(x)? "list" : - "range"; + is_range(x) ? "range" : + "invalid"; +//*** +// included "invalid" // Function: is_type() // Usage: @@ -70,8 +74,8 @@ function is_str(x) = is_string(x); // is_int(n) // Description: // Returns true if the given value is an integer (it is a number and it rounds to itself). -function is_int(n) = is_num(n) && n == round(n); -function is_integer(n) = is_num(n) && n == round(n); +function is_int(n) = is_finite(n) && n == round(n); +function is_integer(n) = is_finite(n) && n == round(n); // Function: is_nan() @@ -93,7 +97,7 @@ function is_finite(v) = is_num(0*v); // Function: is_range() // Description: // Returns true if its argument is a range -function is_range(x) = is_num(x[0]) && !is_list(x); +function is_range(x) = !is_list(x) && is_finite(x[0]+x[1]+x[2]) ; // Function: is_list_of() @@ -106,13 +110,15 @@ function is_range(x) = is_num(x[0]) && !is_list(x); // is_list_of([3,4,5], 0); // Returns true // is_list_of([3,4,undef], 0); // Returns false // is_list_of([[3,4],[4,5]], [1,1]); // Returns true +// is_list_of([[3,"a"],[4,true]], [1,undef]); // Returns true // is_list_of([[3,4], 6, [4,5]], [1,1]); // Returns false -// is_list_of([[1,[3,4]], [4,[5,6]]], [1,[2,3]]); // Returne true -// is_list_of([[1,[3,INF]], [4,[5,6]]], [1,[2,3]]); // Returne false +// is_list_of([[1,[3,4]], [4,[5,6]]], [1,[2,3]]); // Returns true +// is_list_of([[1,[3,INF]], [4,[5,6]]], [1,[2,3]]); // Returns false +// is_list_of([], [1,[2,3]]); // Returns true function is_list_of(list,pattern) = let(pattern = 0*pattern) is_list(list) && - []==[for(entry=list) if (entry*0 != pattern) entry]; + []==[for(entry=0*list) if (entry != pattern) entry]; // Function: is_consistent() @@ -311,10 +317,13 @@ function scalar_vec3(v, dflt=undef) = // Calculate the standard number of sides OpenSCAD would give a circle based on `$fn`, `$fa`, and `$fs`. // Arguments: // r = Radius of circle to get the number of segments for. -function segs(r) = +function segs(r) = $fn>0? ($fn>3? $fn : 3) : - ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs))); + let( r = is_finite(r)? r: 0 ) + ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs))) ; +//*** +// avoids undef // Section: Testing Helpers @@ -322,7 +331,7 @@ function segs(r) = function _valstr(x) = is_list(x)? str("[",str_join([for (xx=x) _valstr(xx)],","),"]") : - is_num(x)? fmt_float(x,12) : x; + is_finite(x)? fmt_float(x,12) : x; // Module: assert_approx() diff --git a/tests/test_common.scad b/tests/test_common.scad index ed97cae..1cad5e3 100644 --- a/tests/test_common.scad +++ b/tests/test_common.scad @@ -1,4 +1,4 @@ -include +include <../std.scad> module test_typeof() { @@ -18,6 +18,10 @@ module test_typeof() { assert(typeof([0:1:5]) == "range"); assert(typeof([-3:2:5]) == "range"); assert(typeof([10:-2:-10]) == "range"); + assert(typeof([0:NAN:INF]) == "invalid"); + assert(typeof([0:"a":INF]) == "undef"); + assert(typeof([0:[]:INF]) == "undef"); + assert(typeof([true:1:INF]) == "undef"); } test_typeof(); @@ -102,6 +106,8 @@ module test_is_int() { assert(!is_int(-99.1)); assert(!is_int(99.1)); assert(!is_int(undef)); + assert(!is_int(INF)); + assert(!is_int(NAN)); assert(!is_int(false)); assert(!is_int(true)); assert(!is_int("foo")); @@ -124,6 +130,8 @@ module test_is_integer() { assert(!is_integer(-99.1)); assert(!is_integer(99.1)); assert(!is_integer(undef)); + assert(!is_integer(INF)); + assert(!is_integer(NAN)); assert(!is_integer(false)); assert(!is_integer(true)); assert(!is_integer("foo")); @@ -166,6 +174,9 @@ module test_is_range() { assert(!is_range("foo")); assert(!is_range([])); assert(!is_range([3,4,5])); + assert(!is_range([INF:4:5])); + assert(!is_range([3:NAN:5])); + assert(!is_range([3:4:"a"])); assert(is_range([3:1:5])); } test_is_nan(); @@ -331,11 +342,25 @@ module test_scalar_vec3() { assert(scalar_vec3([3]) == [3,0,0]); assert(scalar_vec3([3,4]) == [3,4,0]); assert(scalar_vec3([3,4],dflt=1) == [3,4,1]); + assert(scalar_vec3([3,"a"],dflt=1) == [3,"a",1]); + assert(scalar_vec3([3,[2]],dflt=1) == [3,[2],1]); assert(scalar_vec3([3],dflt=1) == [3,1,1]); assert(scalar_vec3([3,4,5]) == [3,4,5]); assert(scalar_vec3([3,4,5,6]) == [3,4,5]); + assert(scalar_vec3([3,4,5,6]) == [3,4,5]); } test_scalar_vec3(); +module test_segs() { + assert_equal(segs(50,$fn=8), 8); + assert_equal(segs(50,$fa=2,$fs=2), 158); + assert(segs(1)==5); + assert(segs(11)==30); + // assert(segs(1/0)==5); + // assert(segs(0/0)==5); + // assert(segs(undef)==5); +} +test_segs(); + // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap From 88e2fc0f2953ff8bbee2ea884ebec9972cdbe068 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Fri, 24 Jul 2020 00:10:36 +0100 Subject: [PATCH 2/5] Input data check review and some refactoring Some functions have been changed as a consequence of the dat checking review. vector_axis was fully refactored. add_scalar was moved to arrays.scad --- tests/test_vectors.scad | 15 ++--- vectors.scad | 136 +++++++++++++++++++++++----------------- 2 files changed, 84 insertions(+), 67 deletions(-) diff --git a/tests/test_vectors.scad b/tests/test_vectors.scad index 399be01..be3e542 100644 --- a/tests/test_vectors.scad +++ b/tests/test_vectors.scad @@ -1,4 +1,4 @@ -include +include <../std.scad> module test_is_vector() { @@ -9,17 +9,14 @@ module test_is_vector() { assert(is_vector(1) == false); assert(is_vector("foo") == false); assert(is_vector(true) == false); + assert(is_vector([0,0],nonzero=true) == false); + assert(is_vector([0,1e-12,0],nonzero=true) == false); + assert(is_vector([0,1e-6,0],nonzero=true) == true); + assert(is_vector([0,1e-6,0],nonzero=true,eps=1e-4) == false); } test_is_vector(); -module test_add_scalar() { - assert(add_scalar([1,2,3],3) == [4,5,6]); - assert(add_scalar([[1,2,3],[3,4,5]],3) == [[4,5,6],[6,7,8]]); -} -test_add_scalar(); - - module test_vfloor() { assert_equal(vfloor([2.0, 3.14, 18.9, 7]), [2,3,18,7]); assert_equal(vfloor([-2.0, -3.14, -18.9, -7]), [-2,-4,-19,-7]); @@ -56,7 +53,7 @@ module test_vabs() { } test_vabs(); -include +include <../strings.scad> module test_vang() { assert(vang([1,0])==0); assert(vang([0,1])==90); diff --git a/vectors.scad b/vectors.scad index 0030160..92e4866 100644 --- a/vectors.scad +++ b/vectors.scad @@ -20,32 +20,29 @@ // v = The value to test to see if it is a vector. // length = If given, make sure the vector is `length` items long. // Example: -// is_vector(4); // Returns false -// is_vector([4,true,false]); // Returns false -// is_vector([3,4,INF,5]); // Returns false -// is_vector([3,4,5,6]); // Returns true -// is_vector([3,4,undef,5]); // Returns false -// is_vector([3,4,5],3); // Returns true -// is_vector([3,4,5],4); // Returns true -// is_vector([]); // Returns false -function is_vector(v,length) = - is_list(v) && is_num(0*(v*v)) && (is_undef(length)||len(v)==length); +// is_vector(4); // Returns false +// is_vector([4,true,false]); // Returns false +// is_vector([3,4,INF,5]); // Returns false +// is_vector([3,4,5,6]); // Returns true +// is_vector([3,4,undef,5]); // Returns false +// is_vector([3,4,5],3); // Returns true +// is_vector([3,4,5],4); // Returns true +// is_vector([]); // Returns false +// is_vector([0,4,0],3,nonzero=true); // Returns true +// is_vector([0,0,0],nonzero=true); // Returns false +// is_vector([0,0,1e-12],nonzero=true); // Returns false +// is_vector([],nonzero=true); // Returns false +function is_vector(v,length, nonzero=false, eps=EPSILON) = + is_list(v) && is_num(0*(v*v)) + && (is_undef(length)|| len(v)==length) + && ( ! nonzero || ([]!=[for(vi=v) if(abs(vi)>=eps) 1]) ); +//*** +// including non_zero option +// extended examples -// Function: add_scalar() -// Usage: -// add_scalar(v,s); -// Description: -// Given a vector and a scalar, returns the vector with the scalar added to each item in it. -// If given a list of vectors, recursively adds the scalar to the each vector. -// Arguments: -// v = The initial list of values. -// s = A scalar value to add to every item in the vector. -// Example: -// add_scalar([1,2,3],3); // Returns: [4,5,6] -// add_scalar([[1,2,3],[3,4,5]],3); // Returns: [[4,5,6],[6,7,8]] -function add_scalar(v,s) = [for (x=v) is_list(x)? add_scalar(x,s) : x+s]; - +//*** +// add_scalar() is an array operation: moved to array.scad // Function: vang() // Usage: @@ -55,6 +52,7 @@ function add_scalar(v,s) = [for (x=v) is_list(x)? add_scalar(x,s) : x+s]; // Given a 2D vector, returns the angle in degrees counter-clockwise from X+ on the XY plane. // Given a 3D vector, returns [THETA,PHI] where THETA is the number of degrees counter-clockwise from X+ on the XY plane, and PHI is the number of degrees up from the X+ axis along the XZ plane. function vang(v) = + assert( is_vector(v,2) || is_vector(v,3) , "Invalid vector") len(v)==2? atan2(v.y,v.x) : let(res=xyz_to_spherical(v)) [res[1], 90-res[2]]; @@ -68,7 +66,9 @@ function vang(v) = // v2 = The second vector. // Example: // vmul([3,4,5], [8,7,6]); // Returns [24, 28, 30] -function vmul(v1, v2) = [for (i = [0:1:len(v1)-1]) v1[i]*v2[i]]; +function vmul(v1, v2) = + assert( is_vector(v1) && is_vector(v2,len(v1)), "Incompatible vectors") + [for (i = [0:1:len(v1)-1]) v1[i]*v2[i]]; // Function: vdiv() @@ -80,7 +80,9 @@ function vmul(v1, v2) = [for (i = [0:1:len(v1)-1]) v1[i]*v2[i]]; // v2 = The second vector. // Example: // vdiv([24,28,30], [8,7,6]); // Returns [3, 4, 5] -function vdiv(v1, v2) = [for (i = [0:1:len(v1)-1]) v1[i]/v2[i]]; +function vdiv(v1, v2) = + assert( is_vector(v1) && is_vector(v2,len(v1)), "Incompatible vectors") + [for (i = [0:1:len(v1)-1]) v1[i]/v2[i]]; // Function: vabs() @@ -89,19 +91,25 @@ function vdiv(v1, v2) = [for (i = [0:1:len(v1)-1]) v1[i]/v2[i]]; // v = The vector to get the absolute values of. // Example: // vabs([-1,3,-9]); // Returns: [1,3,9] -function vabs(v) = [for (x=v) abs(x)]; +function vabs(v) = + assert( is_vector(v), "Invalid vector" ) + [for (x=v) abs(x)]; // Function: vfloor() // Description: // Returns the given vector after performing a `floor()` on all items. -function vfloor(v) = [for (x=v) floor(x)]; +function vfloor(v) = + assert( is_vector(v), "Invalid vector" ) + [for (x=v) floor(x)]; // Function: vceil() // Description: // Returns the given vector after performing a `ceil()` on all items. -function vceil(v) = [for (x=v) ceil(x)]; +function vceil(v) = + assert( is_vector(v), "Invalid vector" ) + [for (x=v) ceil(x)]; // Function: unit() @@ -129,6 +137,7 @@ function unit(v, error=[[["ASSERT"]]]) = // Function: vector_angle() // Usage: // vector_angle(v1,v2); +// vector_angle([v1,v2]); // vector_angle(PT1,PT2,PT3); // vector_angle([PT1,PT2,PT3]); // Description: @@ -148,34 +157,38 @@ function unit(v, error=[[["ASSERT"]]]) = // vector_angle([10,0,10], [0,0,0], [-10,10,0]); // Returns: 120 // vector_angle([[10,0,10], [0,0,0], [-10,10,0]]); // Returns: 120 function vector_angle(v1,v2,v3) = - let( - vecs = !is_undef(v3)? [v1-v2,v3-v2] : - !is_undef(v2)? [v1,v2] : - len(v1) == 3? [v1[0]-v1[1],v1[2]-v1[1]] : - len(v1) == 2? v1 : - assert(false, "Bad arguments to vector_angle()"), - is_valid = is_vector(vecs[0]) && is_vector(vecs[1]) && vecs[0]*0 == vecs[1]*0 + assert( ( is_undef(v3) && ( is_undef(v2) || same_shape(v1,v2) ) ) + || is_consistent([v1,v2,v3]) , + "Bad arguments.") + assert( is_vector(v1) || is_consistent(v1), "Bad arguments.") + let( vecs = ! is_undef(v3) ? [v1-v2,v3-v2] : + ! is_undef(v2) ? [v1,v2] : + len(v1) == 3 ? [v1[0]-v1[1], v1[2]-v1[1]] + : v1 ) - assert(is_valid, "Bad arguments to vector_angle()") + assert(is_vector(vecs[0],2) || is_vector(vecs[0],3), "Bad arguments.") let( norm0 = norm(vecs[0]), norm1 = norm(vecs[1]) ) - assert(norm0>0 && norm1>0,"Zero length vector given to vector_angle()") + assert(norm0>0 && norm1>0, "Zero length vector.") // NOTE: constrain() corrects crazy FP rounding errors that exceed acos()'s domain. acos(constrain((vecs[0]*vecs[1])/(norm0*norm1), -1, 1)); - + +//*** +// completing input data check // Function: vector_axis() // Usage: // vector_axis(v1,v2); +// vector_axis([v1,v2]); // vector_axis(PT1,PT2,PT3); // vector_axis([PT1,PT2,PT3]); // Description: // If given a single list of two vectors, like `vector_axis([V1,V2])`, returns the vector perpendicular the two vectors V1 and V2. -// If given a single list of three points, like `vector_axis([A,B,C])`, returns the vector perpendicular the line segments AB and BC. -// If given two vectors, like `vector_axis(V1,V1)`, returns the vector perpendicular the two vectors V1 and V2. -// If given three points, like `vector_axis(A,B,C)`, returns the vector perpendicular the line segments AB and BC. +// If given a single list of three points, like `vector_axis([A,B,C])`, returns the vector perpendicular to the plane through a, B and C. +// If given two vectors, like `vector_axis(V1,V2)`, returns the vector perpendicular to the two vectors V1 and V2. +// If given three points, like `vector_axis(A,B,C)`, returns the vector perpendicular to the plane through a, B and C. // Arguments: // v1 = First vector or point. // v2 = Second vector or point. @@ -188,22 +201,29 @@ function vector_angle(v1,v2,v3) = // vector_axis([10,0,10], [0,0,0], [-10,10,0]); // Returns: [-0.57735, -0.57735, 0.57735] // vector_axis([[10,0,10], [0,0,0], [-10,10,0]]); // Returns: [-0.57735, -0.57735, 0.57735] function vector_axis(v1,v2=undef,v3=undef) = - (is_list(v1) && is_list(v1[0]) && is_undef(v2) && is_undef(v3))? ( - assert(is_vector(v1.x)) - assert(is_vector(v1.y)) - len(v1)==3? assert(is_vector(v1.z)) vector_axis(v1.x, v1.y, v1.z) : - len(v1)==2? vector_axis(v1.x, v1.y) : - assert(false, "Bad arguments.") - ) : - (is_vector(v1) && is_vector(v2) && is_vector(v3))? vector_axis(v1-v2, v3-v2) : - (is_vector(v1) && is_vector(v2) && is_undef(v3))? let( - eps = 1e-6, - v1 = point3d(v1/norm(v1)), - v2 = point3d(v2/norm(v2)), - v3 = (norm(v1-v2) > eps && norm(v1+v2) > eps)? v2 : - (norm(vabs(v2)-UP) > eps)? UP : - RIGHT - ) unit(cross(v1,v3)) : assert(false, "Bad arguments."); + is_vector(v3) + ? assert(is_consistent([v3,v2,v1]), "Bad arguments.") + vector_axis(v1-v2, v3-v2) + : assert( is_undef(v3), "Bad arguments.") + is_undef(v2) + ? assert( is_list(v1), "Bad arguments.") + len(v1) == 2 + ? vector_axis(v1[0],v1[1]) + : vector_axis(v1[0],v1[1],v1[2]) + : assert( is_vector(v1,nonzero=true) && is_vector(v2,nonzero=true) && is_consistent([v1,v2]) + , "Bad arguments.") + let( + eps = 1e-6, + w1 = point3d(v1/norm(v1)), + w2 = point3d(v2/norm(v2)), + w3 = (norm(w1-w2) > eps && norm(w1+w2) > eps) ? w2 + : (norm(vabs(w2)-UP) > eps)? UP + : RIGHT + ) unit(cross(w1,w3)); +//*** +// completing input data check and refactoring +// Note: vector_angle and vector_axis have the same kind of inputs and two code strategy alternatives + // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap From 877a07b7113757cf2d70b1f61a949a86d5d4cad9 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Fri, 24 Jul 2020 00:19:49 +0100 Subject: [PATCH 3/5] Full review of input data checking, formating and improvement in some functions Refactorigng of list_set(), list_insert() and list_remove() without any sorting and speed gain. Some argument names changed for consistence between functions. test_array() updated. --- arrays.scad | 695 ++++++++++++++++++++++++----------------- tests/test_arrays.scad | 123 +++++--- 2 files changed, 492 insertions(+), 326 deletions(-) diff --git a/arrays.scad b/arrays.scad index 9d9463a..ac18535 100644 --- a/arrays.scad +++ b/arrays.scad @@ -44,20 +44,24 @@ // select(l, [1:3]); // Returns [4,5,6] // select(l, [1,3]); // Returns [4,6] function select(list, start, end=undef) = + assert( is_list(list) || is_string(list), "Invalid list.") let(l=len(list)) - end==undef? ( - is_num(start)? - let(s=(start%l+l)%l) list[s] : - assert(is_list(start) || is_range(start), "Invalid start parameter") - [for (i=start) list[(i%l+l)%l]] - ) : ( - assert(is_num(start), "Invalid start parameter.") - assert(is_num(end), "Invalid end parameter.") - let(s=(start%l+l)%l, e=(end%l+l)%l) - (s<=e)? - [for (i = [s:1:e]) list[i]] : - concat([for (i = [s:1:l-1]) list[i]], [for (i = [0:1:e]) list[i]]) - ); + l==0 ? [] + : end==undef? + is_num(start)? + list[ (start%l+l)%l ] + : assert( is_list(start) || is_range(start), "Invalid start parameter") + [for (i=start) list[ (i%l+l)%l ] ] + : assert(is_num(start), "Invalid start parameter.") + assert(is_num(end), "Invalid end parameter.") + let( s = (start%l+l)%l, e = (end%l+l)%l ) + (s <= e)? [for (i = [s:1:e]) list[i]] + : concat([for (i = [s:1:l-1]) list[i]], [for (i = [0:1:e]) list[i]]) ; + + +//*** +// 1. avoids undef when the list is void +// 2. format // Function: slice() @@ -65,8 +69,8 @@ function select(list, start, end=undef) = // 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. +// list = The array/list to get the slice of. +// start = 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] @@ -74,24 +78,47 @@ function select(list, start, end=undef) = // 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( - l=len(arr), - s=st<0?(l+st):st, - e=end<0?(l+end+1):end - ) [for (i=[s:1:e-1]) if (e>s) arr[i]]; +function slice(list,start,end) = + assert( is_list(list), "Invalid list" ) + assert( is_finite(start) && is_finite(end), "Invalid number(s)" ) + let( l = len(list) ) + l==0 ? [] + : let( + s = start<0? (l+start): start, + e = end<0? (l+end+1): end + ) [for (i=[s:1:e-1]) if (e>s) list[i]]; +//*** +// 1. for sake of consistence, the list argument identifier was changed to list and st to start +// 2. avoids undef when the list is void +// 3. checks inputs of start and end + // Function: in_list() -// Description: Returns true if value `x` is in list `l`. +// Description: Returns true if value `val` is in list `list`. // Arguments: -// x = The value to search for. -// l = The list to search. -// idx = If given, searches the given subindexes for matches for `x`. +// val = The simple value to search for. +// list = The list to search. +// idx = If given, searches the given subindexes for matches for `val`. // 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) != [[]]; +// in_list("bee", ["foo", "bar", ["bee",0]]); // Returns true. +// in_list("bar", [[2,"foo"], [4,["bar"]], [3,"baz"]]); // Returns false. +// Note: +// When `val==NAN` the answer will be false for any list. +// `val` cannot be a boolean. +// When the some element in `list` is a list containing `val` at it first element, the return is also true. +function in_list(val,list,idx=undef) = + let( s = search([val], list, num_returns_per_match=1, index_col_num=idx)[0] ) + s==[] || s[0]==[] ? false + : is_undef(idx) ? val==list[s] + : val==list[s][idx]; + +//*** +// 1. for sake of consistence, the arguments were changed to val and list +// 2. in_list(0,[ 1, [0,1],2])) was returning true // Function: min_index() @@ -104,11 +131,15 @@ function in_list(x,l,idx=undef) = search([x], l, num_returns_per_match=1, index_ // 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] +// min_index([5,3,9,6,2,7,8,2,1]); // Returns: 8 +// 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]; +//*** +// 1. corrected examples +// 2. input data check // Function: max_index() // Usage: @@ -123,8 +154,11 @@ function min_index(vals, all=false) = // 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]; +//*** +// 1. input data check // Function: list_increasing() // Usage: @@ -175,9 +209,12 @@ function list_decreasing(list) = // repeat([1,2,3],3); // Returns [[1,2,3], [1,2,3], [1,2,3]] function repeat(val, n, i=0) = is_num(n)? [for(j=[1:1:n]) val] : + assert( is_list(n), "Invalid count number.") (i>=len(n))? val : [for (j=[1:1:n[i]]) repeat(val, n, i+1)]; +//*** +// 1. input data check // Function: list_range() // Usage: @@ -188,7 +225,7 @@ function repeat(val, n, i=0) = // Description: // Returns a list, counting up from starting value `s`, by `step` increments, // until either `n` values are in the list, or it reaches the end value `e`. -// If both `n` and `e` are given, returns `n` values evenly spread fron `s` +// If both `n` and `e` are given, returns `n` values evenly spread from `s` // to `e`, and `step` is ignored. // Arguments: // n = Desired number of values in returned list, if given. @@ -201,25 +238,28 @@ function repeat(val, n, i=0) = // list_range(n=4, s=3, step=3); // Returns [3,6,9,12] // list_range(n=5, s=0, e=10); // Returns [0, 2.5, 5, 7.5, 10] // list_range(e=3); // Returns [0,1,2,3] -// list_range(e=6, step=2); // Returns [0,2,4,6] +// list_range(e=7, step=2); // Returns [0,2,4,6] // list_range(s=3, e=5); // Returns [3,4,5] // list_range(s=3, e=8, step=2); // Returns [3,5,7] -// list_range(s=4, e=8, step=2); // Returns [4,6,8] +// list_range(s=4, e=8.3, step=2); // Returns [4,6,8] // list_range(n=4, s=[3,4], step=[2,3]); // Returns [[3,4], [5,7], [7,10], [9,13]] function list_range(n=undef, s=0, e=undef, step=undef) = - (n!=undef && e!=undef)? ( - assert(is_undef(n) || is_undef(e) || is_undef(step), "At most 2 of n, e, and step can be given.") - [for (i=[0:1:n-1]) s+(e-s)*i/(n-1)] - ) : let(step = default(step,1)) - (n!=undef)? [for (i=[0:1:n-1]) let(v=s+step*i) v] : - (e!=undef)? [for (v=[s:step:e]) v] : - assert(e!=undef||n!=undef, "Must supply one of `n` or `e`."); - + assert( is_undef(n) || is_finite(n), "Parameter `n` must be a number.") + assert( is_undef(n) || is_undef(e) || is_undef(step), "At most 2 of n, e, and step can be given.") + let( step = (n!=undef && e!=undef)? (e-s)/(n-1) : default(step,1) ) + is_undef(e) ? + assert( is_consistent([s, step]), "Incompatible data.") + [for (i=[0:1:n-1]) s+step*i ] + : assert( is_vector([s,step,e]), "Start `s`, step `step` and end `e` must be numbers.") + [for (v=[s:step:e]) v] ; + +//*** +// 1. input data check +// 2. reworked accordingly // Section: List Manipulation - // Function: reverse() // Description: Reverses a list/array. // Arguments: @@ -251,34 +291,44 @@ function reverse(list) = // l8 = list_rotate([1,2,3,4,5],5); // Returns: [1,2,3,4,5] // l9 = list_rotate([1,2,3,4,5],6); // Returns: [2,3,4,5,1] function list_rotate(list,n=1) = - assert(is_list(list)||is_string(list)) - assert(is_num(n)) + assert(is_list(list)||is_string(list), "Invalid list or string.") + assert(is_finite(n), "Invalid number") select(list,n,n+len(list)-1); +//*** +// 1. review of the input data check // Function: deduplicate() // Usage: -// deduplicate(list); +// deduplicate(list,[close],[eps]); // Description: // Removes consecutive duplicate items in a list. +// When `eps` is zero, the comparison between consecutive items is exact. +// Otherwise, when all list items and subitems are numbers, the comparison is within the tolerance `eps`. // This is different from `unique()` in that the list is *not* sorted. // Arguments: // list = The list to deduplicate. // closed = If true, drops trailing items if they match the first list item. -// eps = The maximum difference to allow between numbers or vectors. +// eps = The maximum tolerance between items. // Examples: // deduplicate([8,3,4,4,4,8,2,3,3,8,8]); // Returns: [8,3,4,8,2,3,8] // deduplicate(closed=true, [8,3,4,4,4,8,2,3,3,8,8]); // Returns: [8,3,4,8,2,3] // deduplicate("Hello"); // Returns: ["H","e","l","o"] // deduplicate([[3,4],[7,2],[7,1.99],[1,4]],eps=0.1); // Returns: [[3,4],[7,2],[1,4]] +// deduplicate([[7,undef],[7,undef],[1,4],[1,4+1e-12],eps=0); // Returns: [[7,undef],[1,4],[1,4+1e-12]] function deduplicate(list, closed=false, eps=EPSILON) = assert(is_list(list)||is_string(list)) - let( - l = len(list), - end = l-(closed?0:1) - ) (is_num(list[0]) || is_vector(list[0]))? - [for (i=[0:1:l-1]) if (i==end || !approx(list[i], list[(i+1)%l], eps)) list[i]] : - [for (i=[0:1:l-1]) if (i==end || list[i] != list[(i+1)%l]) list[i]]; + let( l = len(list), + end = l-(closed?0:1) ) + is_string(list) || (eps==0) + ? [for (i=[0:1:l-1]) if (i==end || list[i] != list[(i+1)%l]) list[i]] + : [for (i=[0:1:l-1]) if (i==end || !approx(list[i], list[(i+1)%l], eps)) list[i]]; + +//*** +// 1. change Usage:, Description and Arguments +// 2. reworked accordingly +// 3. when eps==0, doesn't call approx +// 4. the list may contain non numerical itens // Function: deduplicate_indexed() @@ -296,24 +346,27 @@ function deduplicate(list, closed=false, eps=EPSILON) = // deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1]); // Returns: [1,4,3,2,0,1] // deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1], closed=true); // Returns: [1,4,3,2,0] function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) = - assert(is_list(list)||is_string(list)) - assert(indices==[] || is_vector(indices)) + assert(is_list(list)||is_string(list), "Improper list or string.") indices==[]? [] : - let( - l = len(indices), - end = l-(closed?0:1) - ) [ - for (i = [0:1:l-1]) let( - a = list[indices[i]], - b = list[indices[(i+1)%l]], - eq = (a == b)? true : - (a*0 != b*0)? false : - is_num(a)? approx(a, b, eps=eps) : - is_vector(a)? approx(a, b, eps=eps) : - false - ) if (i==end || !eq) indices[i] + assert(is_vector(indices), "Indices must be a list of numbers.") + let( l = len(indices), + end = l-(closed?0:1) ) + [ for (i = [0:1:l-1]) + let( + a = list[indices[i]], + b = list[indices[(i+1)%l]], + eq = (a == b)? true : + (a*0 != b*0) || (eps==0)? false : + is_num(a) || is_vector(a) ? approx(a, b, eps=eps) + : false + ) + if (i==end || !eq) indices[i] ]; +//** +// 1. msg of asserts +// 2. format + // Function: repeat_entries() // Usage: @@ -341,17 +394,21 @@ function deduplicate_indexed(list, indices, 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") + assert(is_list(list) && len(list)>0, "The list cannot be void.") + assert((is_finite(N) && N>0) || is_vector(N,len(list)), + "Parameter N must be a number greater than zero or vector with the same length of `list`") 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 : repeat(N/length,length), - reps = exact? _sum_preserving_round(reps_guess) : - [for (val=reps_guess) round(val)] + reps_guess = is_list(N)? N : repeat(N/length,length), + reps = exact ? + _sum_preserving_round(reps_guess) + : [for (val=reps_guess) round(val)] ) [for(i=[0:length-1]) each repeat(list[i],reps[i])]; + +//*** +// 1. Complete asserts +// 2. Format // Function: list_set() @@ -359,12 +416,11 @@ function repeat_entries(list, N, exact = true) = // list_set(list, indices, values, [dflt], [minlen]) // Description: // Takes the input list and returns a new list such that `list[indices[i]] = values[i]` for all of -// the (index,value) pairs supplied. If you supply `indices` that are beyond the length of the list -// then the list is extended and filled in with the `dflt` value. If you set `minlen` then the list is -// lengthed, if necessary, by padding with `dflt` to that length. The `indices` list can be in any -// order but run time will be (much) faster for long lists if it is already sorted. Reptitions are -// not allowed. If `indices` is given as a non-list scalar, then that index of the given `list` will -// be set to the value of `values`. +// the (index,value) pairs supplied and unchanged for other indices. If you supply `indices` that are +// beyond the length of the list then the list is extended and filled in with the `dflt` value. +// If you set `minlen` then the list is lengthed, if necessary, by padding with `dflt` to that length. +// Repetitions in `indices` are not allowed. The lists `indices` and `values` must have the same length. +// If `indices` is given as a scalar, then that index of the given `list` will be set to the scalar value of `values`. // Arguments: // list = List to set items in. Default: [] // indices = List of indices into `list` to set. @@ -374,92 +430,89 @@ function repeat_entries(list, N, exact = true) = // Examples: // list_set([2,3,4,5], 2, 21); // Returns: [2,3,21,5] // list_set([2,3,4,5], [1,3], [81,47]); // Returns: [2,81,4,47] -function list_set(list=[],indices,values,dflt=0,minlen=0) = +function list_set(list=[],indices,values,dflt=0,minlen=0) = assert(is_list(list)||is_string(list)) !is_list(indices)? ( - (is_num(indices) && indices=len(list) ? dflt : list[j]], - [values[sortind[0]]], - [for(i=[1:1:len(sortind)-1]) each - assert(indices[sortind[i]]!=indices[sortind[i-1]],"Repeated index") - concat( - [for(j=[1+indices[sortind[i-1]]:1:indices[sortind[i]]-1]) j>=len(list) ? dflt : list[j]], - [values[sortind[i]]] - ) - ], - slice(list,1+lastind, len(list)), - repeat(dflt, minlen-lastind-1) - ); + (is_finite(indices) && indices=len(array) , "Improper index list." ) [for(i=[0:len(array)-1]) if (index[i]) array[i]]; @@ -520,15 +573,17 @@ function bselect(array,index) = // list_bset([false,true,false,true,false], [3,4]); // Returns: [0,3,0,4,0] // 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)) - assert(is_list(valuelist)) - let( - trueind = search([true], indexset,0)[0] - ) concat( + assert(is_list(indexset), "The index set is not a list." ) + assert(is_list(valuelist), "The `valuelist` is not a list." ) +// trueind = search([true], indexset,0)[0] + let( trueind = [for(i=[0:len(indexset)-1]) if(indexset[i]) i ] ) + concat( list_set([],trueind, valuelist, dflt=dflt), // Fill in all of the values repeat(dflt,len(indexset)-max(trueind)-1) // Add trailing values so length matches indexset ); +//*** +// search might return false results depending if it identifies `true` with 1. // Section: List Length Manipulation @@ -537,58 +592,65 @@ function list_bset(indexset, valuelist, dflt=0) = // Description: // Returns the length of the shortest sublist in a list of lists. // Arguments: -// vecs = A list of lists. -function list_shortest(vecs) = - assert(is_list(vecs)||is_string(list)) - min([for (v = vecs) len(v)]); +// array = A list of lists. +function list_shortest(array) = + assert(is_list(array)||is_string(list), "Invalid input." ) + min([for (v = array) len(v)]); +//*** +// parameter name changed here and in the following for sake of consistence. It was `vecs` + // Function: list_longest() // Description: // Returns the length of the longest sublist in a list of lists. // Arguments: -// vecs = A list of lists. -function list_longest(vecs) = - assert(is_list(vecs)||is_string(list)) - max([for (v = vecs) len(v)]); +// array = A list of lists. +function list_longest(array) = + assert(is_list(array)||is_string(list), "Invalid input." ) + max([for (v = array) len(v)]); // Function: list_pad() // Description: -// If the list `v` is shorter than `minlen` length, pad it to length with the value given in `fill`. +// If the list `array` is shorter than `minlen` length, pad it to length with the value given in `fill`. // Arguments: -// v = A list. +// array = A list. // minlen = The minimum length to pad the list to. // fill = The value to pad the list with. -function list_pad(v, minlen, fill=undef) = - assert(is_list(v)||is_string(list)) - concat(v,repeat(fill,minlen-len(v))); +function list_pad(array, minlen, fill=undef) = + assert(is_list(array)||is_string(list), "Invalid input." ) + concat(array,repeat(fill,minlen-len(array))); // Function: list_trim() // Description: -// If the list `v` is longer than `maxlen` length, truncates it to be `maxlen` items long. +// If the list `array` is longer than `maxlen` length, truncates it to be `maxlen` items long. // Arguments: -// v = A list. +// array = A list. // minlen = The minimum length to pad the list to. -function list_trim(v, maxlen) = - assert(is_list(v)||is_string(list)) - [for (i=[0:1:min(len(v),maxlen)-1]) v[i]]; +function list_trim(array, maxlen) = + assert(is_list(array)||is_string(list), "Invalid input." ) + [for (i=[0:1:min(len(array),maxlen)-1]) array[i]]; // Function: list_fit() // Description: -// If the list `v` is longer than `length` items long, truncates it to be exactly `length` items long. -// If the list `v` is shorter than `length` items long, pad it to length with the value given in `fill`. +// If the list `array` is longer than `length` items long, truncates it to be exactly `length` items long. +// If the list `array` is shorter than `length` items long, pad it to length with the value given in `fill`. // Arguments: -// v = A list. +// array = A list. // minlen = The minimum length to pad the list to. // fill = The value to pad the list with. -function list_fit(v, length, fill) = - assert(is_list(v)||is_string(list)) - let(l=len(v)) (l==length)? v : (l>length)? list_trim(v,length) : list_pad(v,length,fill); - +function list_fit(array, length, fill) = + assert(is_list(array)||is_string(list), "Invalid input." ) + let(l=len(array)) + l==length ? array : + l> length ? list_trim(array,length) + : list_pad(array,length,fill); +//*** +// formating // Section: List Shuffling and Sorting @@ -596,51 +658,59 @@ function list_fit(v, length, fill) = // Description: // Shuffles the input list into random order. function shuffle(list) = - assert(is_list(list)||is_string(list)) + assert(is_list(list)||is_string(list), "Invalid input." ) len(list)<=1 ? list : let ( rval = rands(0,1,len(list)), left = [for (i=[0:len(list)-1]) if (rval[i]< 0.5) list[i]], right = [for (i=[0:len(list)-1]) if (rval[i]>=0.5) list[i]] - ) concat(shuffle(left), shuffle(right)); + ) + concat(shuffle(left), shuffle(right)); // Sort a vector of scalar values function _sort_scalars(arr) = - len(arr)<=1 ? arr : let( + len(arr)<=1 ? arr : + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if (y < pivot) y ], equal = [ for (y = arr) if (y == pivot) y ], greater = [ for (y = arr) if (y > pivot) y ] - ) concat( _sort_scalars(lesser), equal, _sort_scalars(greater) ); + ) + concat( _sort_scalars(lesser), equal, _sort_scalars(greater) ); // Sort a vector of vectors based on the first entry only of each vector function _sort_vectors1(arr) = len(arr)<=1 ? arr : - !(len(arr)>0) ? [] : let( + !(len(arr)>0) ? [] : + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if (y[0] < pivot[0]) y ], equal = [ for (y = arr) if (y[0] == pivot[0]) y ], greater = [ for (y = arr) if (y[0] > pivot[0]) y ] - ) concat( _sort_vectors1(lesser), equal, _sort_vectors1(greater) ); + ) + concat( _sort_vectors1(lesser), equal, _sort_vectors1(greater) ); // Sort a vector of vectors based on the first two entries of each vector // Lexicographic order, remaining entries of vector ignored function _sort_vectors2(arr) = len(arr)<=1 ? arr : - !(len(arr)>0) ? [] : let( + !(len(arr)>0) ? [] : + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if (y[0] < pivot[0] || (y[0]==pivot[0] && y[1] pivot[0] || (y[0]==pivot[0] && y[1]>pivot[1])) y ] - ) concat( _sort_vectors2(lesser), equal, _sort_vectors2(greater) ); + greater = [ for (y = arr) if (y[0] > pivot[0] || (y[0]==pivot[0] && y[1]>pivot[1])) y ] + ) + concat( _sort_vectors2(lesser), equal, _sort_vectors2(greater) ); // Sort a vector of vectors based on the first three entries of each vector // Lexicographic order, remaining entries of vector ignored function _sort_vectors3(arr) = - len(arr)<=1 ? arr : let( + len(arr)<=1 ? arr : + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if ( @@ -671,13 +741,15 @@ function _sort_vectors3(arr) = ) ) y ] - ) concat( _sort_vectors3(lesser), equal, _sort_vectors3(greater) ); + ) + concat( _sort_vectors3(lesser), equal, _sort_vectors3(greater) ); // Sort a vector of vectors based on the first four entries of each vector // Lexicographic order, remaining entries of vector ignored function _sort_vectors4(arr) = - len(arr)<=1 ? arr : let( + len(arr)<=1 ? arr : + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if ( @@ -719,26 +791,34 @@ function _sort_vectors4(arr) = ) ) y ] - ) concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) ); + ) + concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) ); + +function _sort_eval(array, eval) = + let( ival = [for(i=[0:len(array)-1]) [eval[i], i] ], + sival = _sort_vectors1(arr) ) + [for(yi=sival) array[yi[1]] ]; function _sort_general(arr, idx=undef) = (len(arr)<=1) ? arr : let( pivot = arr[floor(len(arr)/2)], pivotval = idx==undef? pivot : [for (i=idx) pivot[i]], - compare = [ - for (entry = arr) let( - val = idx==undef? entry : [for (i=idx) entry[i]], - cmp = compare_vals(val, pivotval) - ) cmp - ], + compare = + is_undef(idx) ? [for(entry=arr) compare_vals(entry, pivotval) ] : + [ for (entry = arr) + let( val = [for (i=idx) entry[i] ] ) + compare_vals(val, pivotval) ] , lesser = [ for (i = [0:1:len(arr)-1]) if (compare[i] < 0) arr[i] ], equal = [ for (i = [0:1:len(arr)-1]) if (compare[i] ==0) arr[i] ], greater = [ for (i = [0:1:len(arr)-1]) if (compare[i] > 0) arr[i] ] ) concat(_sort_general(lesser,idx), equal, _sort_general(greater,idx)); +//*** +// format simplification + // Function: sort() // Usage: @@ -757,17 +837,22 @@ function _sort_general(arr, idx=undef) = // sorted = sort(l); // Returns [2,3,8,9,12,16,23,34,37,45,89] function sort(list, idx=undef) = !is_list(list) || len(list)<=1 ? list : + assert( is_undef(idx) || is_finite(idx) || is_vector(idx), "Invalid indices.") is_def(idx) ? _sort_general(list,idx) : let(size = array_dim(list)) len(size)==1 ? _sort_scalars(list) : - len(size)==2 && size[1] <=4 ? ( + len(size)==2 && size[1] <=4 + ? ( size[1]==0 ? list : size[1]==1 ? _sort_vectors1(list) : size[1]==2 ? _sort_vectors2(list) : - size[1]==3 ? _sort_vectors3(list) : - /*size[1]==4*/ _sort_vectors4(list) - ) : _sort_general(list); + size[1]==3 ? _sort_vectors3(list) + /*size[1]==4*/ : _sort_vectors4(list) + ) + : _sort_general(list); +//*** +// Formating and input check // Function: sortidx() // Description: @@ -791,22 +876,29 @@ function sort(list, idx=undef) = // idxs2 = sortidx(lst, idx=0); // Returns: [1,2,0,3] // idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1] function sortidx(list, idx=undef) = - list==[] ? [] : let( + assert( is_list(list) || is_string(list) , "Invalid input." ) + assert( is_undef(idx) || is_finite(idx) || is_vector(idx), "Invalid indices.") + list==[] ? [] : + let( size = array_dim(list), - aug = is_undef(idx) && (len(size) == 1 || (len(size) == 2 && size[1]<=4))? - zip(list, list_range(len(list))) : - enumerate(list,idx=idx) + aug = is_undef(idx) && (len(size) == 1 || (len(size) == 2 && size[1]<=4)) + ? zip(list, list_range(len(list))) + : enumerate(list,idx=idx) ) is_undef(idx) && len(size) == 1? subindex(_sort_vectors1(aug),1) : - is_undef(idx) && len(size) == 2 && size[1] <=4? ( - size[1]==0? list_range(len(arr)) : - size[1]==1? subindex(_sort_vectors1(aug),1) : - size[1]==2? subindex(_sort_vectors2(aug),2) : - size[1]==3? subindex(_sort_vectors3(aug),3) : - /*size[1]==4*/ subindex(_sort_vectors4(aug),4) - ) : - // general case - subindex(_sort_general(aug, idx=list_range(s=1,n=len(aug)-1)), 0); + is_undef(idx) && len(size) == 2 && size[1] <=4 + ? ( + size[1]==0 ? list_range(len(arr)) : + size[1]==1 ? subindex(_sort_vectors1(aug),1) : + size[1]==2 ? subindex(_sort_vectors2(aug),2) : + size[1]==3 ? subindex(_sort_vectors3(aug),3) + /*size[1]==4*/ : subindex(_sort_vectors4(aug),4) + ) + : // general case + subindex(_sort_general(aug, idx=list_range(s=1,n=len(aug)-1)), 0); + +//*** +// Formating and input check // Function: unique() @@ -817,15 +909,16 @@ function sortidx(list, idx=undef) = // Arguments: // arr = The list to uniquify. function unique(arr) = - assert(is_list(arr)||is_string(arr)) - len(arr)<=1? arr : let( - sorted = sort(arr) - ) [ - for (i=[0:1:len(sorted)-1]) + assert(is_list(arr)||is_string(arr), "Invalid input." ) + len(arr)<=1? arr : + let( sorted = sort(arr)) + [ for (i=[0:1:len(sorted)-1]) if (i==0 || (sorted[i] != sorted[i-1])) sorted[i] ]; +//*** +// Formating and input check // Function: unique_count() // Usage: @@ -836,12 +929,15 @@ function unique(arr) = // Arguments: // arr = The list to analyze. function unique_count(arr) = - assert(is_list(arr) || is_string(arr)) + assert(is_list(arr) || is_string(arr), "Invalid input." ) arr == [] ? [[],[]] : let( arr=sort(arr) ) - let(ind = [0,for(i=[1:1:len(arr)-1]) if (arr[i]!=arr[i-1]) i]) - [select(arr,ind), - deltas(concat(ind,[len(arr)]))]; + let( ind = [0, for(i=[1:1:len(arr)-1]) if (arr[i]!=arr[i-1]) i] ) + [ select(arr,ind), deltas( concat(ind,[len(arr)]) ) ]; + +//*** +// Formating and input check + // Section: List Iteration Helpers @@ -860,7 +956,7 @@ function unique_count(arr) = // 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)) + assert(is_list(list)||is_string(list), "Invalid input." ) [start : step : len(list)+end]; @@ -879,10 +975,11 @@ function idx(list, step=1, end=-1,start=0) = // 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]])]; + assert(is_list(l)||is_string(list), "Invalid input." ) + assert(is_undef(idx)||is_finite(idx)||is_vector(idx) ||is_range(idx), "Invalid index/indices." ) + (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: force_list() @@ -905,8 +1002,7 @@ function enumerate(l,idx=undef) = // w = force_list(4, n=3, fill=1); // Returns: [4,1,1] function force_list(value, n=1, fill) = is_list(value) ? value : - is_undef(fill)? [for (i=[1:1:n]) value] : - [value, for (i=[2:1:n]) fill]; + is_undef(fill)? [for (i=[1:1:n]) value] : [value, for (i=[2:1:n]) fill]; // Function: pair() @@ -923,7 +1019,7 @@ function force_list(value, n=1, fill) = // l = ["A","B","C","D"]; // echo([for (p=pair(l)) str(p.y,p.x)]); // Outputs: ["BA", "CB", "DC"] function pair(v) = - assert(is_list(v)||is_string(v)) + assert(is_list(v)||is_string(v), "Invalid input." ) [for (i=[0:1:len(v)-2]) [v[i],v[i+1]]]; @@ -931,8 +1027,8 @@ function pair(v) = // Usage: // pair_wrap(v) // Description: -// Takes a list, and returns a list of adjacent pairss from it, wrapping around from the end to the start of the list. -// Example(2D): Note that the last point and first point DO get paired together. +// Takes a list, and returns a list of adjacent pairs from it, wrapping around from the end to the start of the list. +// Example(2D): // for (p = pair_wrap(circle(d=20, $fn=12))) // move(p[0]) // rot(from=BACK, to=p[1]-p[0]) @@ -941,7 +1037,7 @@ function pair(v) = // l = ["A","B","C","D"]; // echo([for (p=pair_wrap(l)) str(p.y,p.x)]); // Outputs: ["BA", "CB", "DC", "AD"] function pair_wrap(v) = - assert(is_list(v)||is_string(v)) + assert(is_list(v)||is_string(v), "Invalid input." ) [for (i=[0:1:len(v)-1]) [v[i],v[(i+1)%len(v)]]]; @@ -954,7 +1050,7 @@ function pair_wrap(v) = // l = ["A","B","C","D","E"]; // echo([for (p=triplet(l)) str(p.z,p.y,p.x)]); // Outputs: ["CBA", "DCB", "EDC"] function triplet(v) = - assert(is_list(v)||is_string(v)) + assert(is_list(v)||is_string(v), "Invalid input." ) [for (i=[0:1:len(v)-3]) [v[i],v[i+1],v[i+2]]]; @@ -967,7 +1063,7 @@ function triplet(v) = // l = ["A","B","C","D"]; // echo([for (p=triplet_wrap(l)) str(p.z,p.y,p.x)]); // Outputs: ["CBA", "DCB", "ADC", "BAD"] function triplet_wrap(v) = - assert(is_list(v)||is_string(v)) + assert(is_list(v)||is_string(v), "Invalid input." ) [for (i=[0:1:len(v)-1]) [v[i],v[(i+1)%len(v)],v[(i+2)%len(v)]]]; @@ -987,10 +1083,11 @@ function triplet_wrap(v) = // Example(2D): // for (p=permute(regular_ngon(n=7,d=100))) stroke(p); function permute(l,n=2,_s=0) = - assert(is_list(l)) - assert(len(l)-_s >= n) - n==1? [for (i=[_s:1:len(l)-1]) [l[i]]] : - [for (i=[_s:1:len(l)-n], p=permute(l,n=n-1,_s=i+1)) concat([l[i]], p)]; + assert(is_list(l), "Invalid list." ) + assert( is_finite(n) && n>=1 && n<=len(l), "Invalid number `n`." ) + n==1 + ? [for (i=[_s:1:len(l)-1]) [l[i]]] + : [for (i=[_s:1:len(l)-n], p=permute(l,n=n-1,_s=i+1)) concat([l[i]], p)]; @@ -1016,26 +1113,28 @@ function permute(l,n=2,_s=0) = // set_v = set_union(set_a, set_b, get_indices=true); // // set_v now equals [[5,0,1,2,6], [2,3,5,7,11,1,8]] function set_union(a, b, get_indices=false) = + assert( is_list(a) && is_list(b), "Invalid sets." ) let( found1 = search(b, a), found2 = search(b, b), - c = [ - for (i=idx(b)) - if (found1[i] == [] && found2[i] == i) - b[i] - ], + c = [ for (i=idx(b)) + if (found1[i] == [] && found2[i] == i) + b[i] + ], nset = concat(a, c) - ) !get_indices? nset : + ) + ! get_indices ? nset : let( la = len(a), found3 = search(b, c), - idxs = [ - for (i=idx(b)) - (found1[i] != [])? found1[i] : - la + found3[i] - ] + idxs = [ for (i=idx(b)) + (found1[i] != [])? found1[i] : la + found3[i] + ] ) [idxs, nset]; +//*** +// Formating and input check + // Function: set_difference() // Usage: @@ -1051,10 +1150,12 @@ function set_union(a, b, get_indices=false) = // set_d = set_difference(set_a, set_b); // // set_d now equals [7,11] function set_difference(a, b) = - let( - found = search(a, b, num_returns_per_match=1) - ) [ for (i=idx(a)) if(found[i]==[]) a[i] ]; + assert( is_list(a) && is_list(b), "Invalid sets." ) + let( found = search(a, b, num_returns_per_match=1) ) + [ for (i=idx(a)) if(found[i]==[]) a[i] ]; +//*** +// Formating and input check // Function: set_intersection() // Usage: @@ -1070,14 +1171,34 @@ function set_difference(a, b) = // set_i = set_intersection(set_a, set_b); // // set_i now equals [2,3,5] function set_intersection(a, b) = - let( - found = search(a, b, num_returns_per_match=1) - ) [ for (i=idx(a)) if(found[i]!=[]) a[i] ]; + assert( is_list(a) && is_list(b), "Invalid sets." ) + let( found = search(a, b, num_returns_per_match=1) ) + [ for (i=idx(a)) if(found[i]!=[]) a[i] ]; +//*** +// Formating and input check // Section: Array Manipulation +// Function: add_scalar() +// Usage: +// add_scalar(v,s); +// Description: +// Given an array and a scalar, returns the array 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: +// add_scalar([1,2,3],3); // Returns: [4,5,6] +// 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; + +//*** +// for sake of consistence, move it to here from vectors.scad + // Function: subindex() // Description: // For each array item, return the indexed subitem. @@ -1092,10 +1213,11 @@ function set_intersection(a, b) = // 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]]) +function subindex(v, idx) = + [ for(val=v) + let( value=[for(i=idx) val[i]] ) len(value)==1 ? value[0] : value -]; + ]; // Function: zip() @@ -1127,15 +1249,16 @@ function subindex(v, idx) = [ 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(in_list(fit, [false, "short", "long"]), "Invalid fit value." ) 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), - dummy = (fit==false)? assert(minlen==maxlen, "Input vectors to zip must have the same length") : 0 - ) (fit == "long")? - [for(i=[0:1:maxlen-1]) [for(v=vecs) for(x=(i len(dimlist))? 0 : dimlist[depth-1] - ); + 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] ; +//*** +// Formating +// This function may return undef! // Function: transpose() @@ -1238,9 +1365,15 @@ function array_dim(v, depth=undef) = // Example: // transpose([3,4,5]); // Returns: [3,4,5] function transpose(arr) = - is_list(arr[0])? [for (i=[0:1:len(arr[0])-1]) [for (j=[0:1:len(arr)-1]) arr[j][i]]] : arr; - + let( a0 = arr[0] ) + is_list(a0) + ? assert([for(a=arr) if(len(a)!=len(a0)) 1]==[], "The array is not a matrix." ) + [for (i=[0:1:len(a0)-1]) + [ for (j=[0:1:len(arr)-1]) arr[j][i] ] ] + : arr; +//*** +// Input data check and formating // vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad index 0ed2bd6..d048afa 100644 --- a/tests/test_arrays.scad +++ b/tests/test_arrays.scad @@ -1,34 +1,7 @@ -include +include <../std.scad> -// List/Array Ops - -module test_repeat() { - assert(repeat(1, 4) == [1,1,1,1]); - assert(repeat(8, [2,3]) == [[8,8,8], [8,8,8]]); - assert(repeat(0, [2,2,3]) == [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]); - assert(repeat([1,2,3],3) == [[1,2,3], [1,2,3], [1,2,3]]); -} -test_repeat(); - - -module test_in_list() { - assert(in_list("bar", ["foo", "bar", "baz"])); - assert(!in_list("bee", ["foo", "bar", "baz"])); - assert(in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1)); -} -test_in_list(); - - -module test_slice() { - assert(slice([3,4,5,6,7,8,9], 3, 5) == [6,7]); - assert(slice([3,4,5,6,7,8,9], 2, -1) == [5,6,7,8,9]); - assert(slice([3,4,5,6,7,8,9], 1, 1) == []); - assert(slice([3,4,5,6,7,8,9], 6, -1) == [9]); - assert(slice([3,4,5,6,7,8,9], 2, -2) == [5,6,7,8]); -} -test_slice(); - +// Section: List Query Operations module test_select() { l = [3,4,5,6,7,8,9]; @@ -45,6 +18,71 @@ module test_select() { test_select(); +module test_slice() { + assert(slice([3,4,5,6,7,8,9], 3, 5) == [6,7]); + assert(slice([3,4,5,6,7,8,9], 2, -1) == [5,6,7,8,9]); + assert(slice([3,4,5,6,7,8,9], 1, 1) == []); + assert(slice([3,4,5,6,7,8,9], 6, -1) == [9]); + assert(slice([3,4,5,6,7,8,9], 2, -2) == [5,6,7,8]); + assert(slice([], 2, -2) == []); +} +test_slice(); + + +module test_in_list() { + assert(in_list("bar", ["foo", "bar", "baz"])); + assert(!in_list("bee", ["foo", "bar", "baz"])); + assert(in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1)); + + assert(!in_list("bee", ["foo", "bar", ["bee"]])); + assert(in_list(NAN, [NAN])==false); +} +test_in_list(); + + +module test_min_index() { + assert(min_index([5,3,9,6,2,7,8,2,1])==8); + assert(min_index([5,3,9,6,2,7,8,2,7],all=true)==[4,7]); +// assert(min_index([],all=true)==[]); +} +test_min_index(); + + +module test_max_index() { + assert(max_index([5,3,9,6,2,7,8,9,1])==2); + assert(max_index([5,3,9,6,2,7,8,9,7],all=true)==[2,7]); +// assert(max_index([],all=true)==[]); +} +test_max_index(); + + +module test_list_increasing() { + assert(list_increasing([1,2,3,4]) == true); + assert(list_increasing([1,3,2,4]) == false); + assert(list_increasing([4,3,2,1]) == false); +} +test_list_increasing(); + + +module test_list_decreasing() { + assert(list_decreasing([1,2,3,4]) == false); + assert(list_decreasing([4,2,3,1]) == false); + assert(list_decreasing([4,3,2,1]) == true); +} +test_list_decreasing(); + +// Section: Basic List Generation + +module test_repeat() { + assert(repeat(1, 4) == [1,1,1,1]); + assert(repeat(8, [2,3]) == [[8,8,8], [8,8,8]]); + assert(repeat(0, [2,2,3]) == [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]); + assert(repeat([1,2,3],3) == [[1,2,3], [1,2,3], [1,2,3]]); + assert(repeat(4, [2,-1]) == [[], []]); +} +test_repeat(); + + module test_list_range() { assert(list_range(4) == [0,1,2,3]); assert(list_range(n=4, step=2) == [0,2,4,6]); @@ -62,6 +100,8 @@ test_list_range(); module test_reverse() { assert(reverse([3,4,5,6]) == [6,5,4,3]); + assert(reverse("abcd") == ["d","c","b","a"]); + assert(reverse([]) == []); } test_reverse(); @@ -86,6 +126,8 @@ module test_deduplicate() { assert(deduplicate(closed=true, [8,3,4,4,4,8,2,3,3,8,8]) == [8,3,4,8,2,3]); assert(deduplicate("Hello") == ["H","e","l","o"]); assert(deduplicate([[3,4],[7,1.99],[7,2],[1,4]],eps=0.1) == [[3,4],[7,2],[1,4]]); + assert(deduplicate([], closed=true) == []); + assert(deduplicate([[1,[1,[undef]]],[1,[1,[undef]]],[1,[2]],[1,[2,[0]]]])==[[1, [1,[undef]]],[1,[2]],[1,[2,[0]]]]); } test_deduplicate(); @@ -144,22 +186,6 @@ module test_list_bset() { test_list_bset(); -module test_list_increasing() { - assert(list_increasing([1,2,3,4]) == true); - assert(list_increasing([1,3,2,4]) == false); - assert(list_increasing([4,3,2,1]) == false); -} -test_list_increasing(); - - -module test_list_decreasing() { - assert(list_decreasing([1,2,3,4]) == false); - assert(list_decreasing([4,2,3,1]) == false); - assert(list_decreasing([4,3,2,1]) == true); -} -test_list_decreasing(); - - module test_list_shortest() { assert(list_shortest(["foobar", "bazquxx", "abcd"]) == 4); } @@ -311,6 +337,13 @@ test_set_intersection(); // Arrays +module test_add_scalar() { + assert(add_scalar([1,2,3],3) == [4,5,6]); + assert(add_scalar([[1,2,3],[3,4,5]],3) == [[4,5,6],[6,7,8]]); +} +test_add_scalar(); + + module test_subindex() { v = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]; assert(subindex(v,2) == [3, 7, 11, 15]); From f61e30add2f458fb0f2e9bdc2912dea333dcb2c6 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Fri, 24 Jul 2020 00:23:07 +0100 Subject: [PATCH 4/5] Update --- arrays.scad | 5 ----- 1 file changed, 5 deletions(-) diff --git a/arrays.scad b/arrays.scad index ac18535..a05d330 100644 --- a/arrays.scad +++ b/arrays.scad @@ -795,11 +795,6 @@ function _sort_vectors4(arr) = concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) ); -function _sort_eval(array, eval) = - let( ival = [for(i=[0:len(array)-1]) [eval[i], i] ], - sival = _sort_vectors1(arr) ) - [for(yi=sival) array[yi[1]] ]; - function _sort_general(arr, idx=undef) = (len(arr)<=1) ? arr : let( From 464c65ce8ccf9377663844362ac82199145c75a5 Mon Sep 17 00:00:00 2001 From: RonaldoCMP Date: Fri, 24 Jul 2020 13:30:19 +0100 Subject: [PATCH 5/5] Eliminating tabs --- arrays.scad | 174 ++++++++++++++++++++--------------------- common.scad | 2 +- tests/test_arrays.scad | 2 +- vectors.scad | 4 +- 4 files changed, 91 insertions(+), 91 deletions(-) diff --git a/arrays.scad b/arrays.scad index a05d330..e29edef 100644 --- a/arrays.scad +++ b/arrays.scad @@ -396,16 +396,16 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) = function repeat_entries(list, N, exact = true) = assert(is_list(list) && len(list)>0, "The list cannot be void.") assert((is_finite(N) && N>0) || is_vector(N,len(list)), - "Parameter N must be a number greater than zero or vector with the same length of `list`") + "Parameter N must be a number greater than zero or vector with the same length of `list`") let( length = len(list), reps_guess = is_list(N)? N : repeat(N/length,length), reps = exact ? - _sum_preserving_round(reps_guess) + _sum_preserving_round(reps_guess) : [for (val=reps_guess) round(val)] ) [for(i=[0:length-1]) each repeat(list[i],reps[i])]; - + //*** // 1. Complete asserts // 2. Format @@ -434,21 +434,21 @@ function list_set(list=[],indices,values,dflt=0,minlen=0) = assert(is_list(list)||is_string(list)) !is_list(indices)? ( (is_finite(indices) && indices length ? list_trim(array,length) - : list_pad(array,length,fill); + l==length ? array : + l> length ? list_trim(array,length) + : list_pad(array,length,fill); //*** // formating @@ -665,32 +665,32 @@ function shuffle(list) = left = [for (i=[0:len(list)-1]) if (rval[i]< 0.5) list[i]], right = [for (i=[0:len(list)-1]) if (rval[i]>=0.5) list[i]] ) - concat(shuffle(left), shuffle(right)); + concat(shuffle(left), shuffle(right)); // Sort a vector of scalar values function _sort_scalars(arr) = len(arr)<=1 ? arr : - let( + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if (y < pivot) y ], equal = [ for (y = arr) if (y == pivot) y ], greater = [ for (y = arr) if (y > pivot) y ] ) - concat( _sort_scalars(lesser), equal, _sort_scalars(greater) ); + concat( _sort_scalars(lesser), equal, _sort_scalars(greater) ); // Sort a vector of vectors based on the first entry only of each vector function _sort_vectors1(arr) = len(arr)<=1 ? arr : !(len(arr)>0) ? [] : - let( + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if (y[0] < pivot[0]) y ], equal = [ for (y = arr) if (y[0] == pivot[0]) y ], greater = [ for (y = arr) if (y[0] > pivot[0]) y ] ) - concat( _sort_vectors1(lesser), equal, _sort_vectors1(greater) ); + concat( _sort_vectors1(lesser), equal, _sort_vectors1(greater) ); // Sort a vector of vectors based on the first two entries of each vector @@ -698,19 +698,19 @@ function _sort_vectors1(arr) = function _sort_vectors2(arr) = len(arr)<=1 ? arr : !(len(arr)>0) ? [] : - let( + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if (y[0] < pivot[0] || (y[0]==pivot[0] && y[1] pivot[0] || (y[0]==pivot[0] && y[1]>pivot[1])) y ] ) - concat( _sort_vectors2(lesser), equal, _sort_vectors2(greater) ); + concat( _sort_vectors2(lesser), equal, _sort_vectors2(greater) ); // Sort a vector of vectors based on the first three entries of each vector // Lexicographic order, remaining entries of vector ignored function _sort_vectors3(arr) = len(arr)<=1 ? arr : - let( + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if ( @@ -742,14 +742,14 @@ function _sort_vectors3(arr) = ) y ] ) - concat( _sort_vectors3(lesser), equal, _sort_vectors3(greater) ); + concat( _sort_vectors3(lesser), equal, _sort_vectors3(greater) ); // Sort a vector of vectors based on the first four entries of each vector // Lexicographic order, remaining entries of vector ignored function _sort_vectors4(arr) = len(arr)<=1 ? arr : - let( + let( pivot = arr[floor(len(arr)/2)], lesser = [ for (y = arr) if ( @@ -792,18 +792,18 @@ function _sort_vectors4(arr) = ) y ] ) - concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) ); - + concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) ); + function _sort_general(arr, idx=undef) = (len(arr)<=1) ? arr : let( pivot = arr[floor(len(arr)/2)], pivotval = idx==undef? pivot : [for (i=idx) pivot[i]], - compare = - is_undef(idx) ? [for(entry=arr) compare_vals(entry, pivotval) ] : + compare = + is_undef(idx) ? [for(entry=arr) compare_vals(entry, pivotval) ] : [ for (entry = arr) - let( val = [for (i=idx) entry[i] ] ) + let( val = [for (i=idx) entry[i] ] ) compare_vals(val, pivotval) ] , lesser = [ for (i = [0:1:len(arr)-1]) if (compare[i] < 0) arr[i] ], equal = [ for (i = [0:1:len(arr)-1]) if (compare[i] ==0) arr[i] ], @@ -832,19 +832,19 @@ function _sort_general(arr, idx=undef) = // sorted = sort(l); // Returns [2,3,8,9,12,16,23,34,37,45,89] function sort(list, idx=undef) = !is_list(list) || len(list)<=1 ? list : - assert( is_undef(idx) || is_finite(idx) || is_vector(idx), "Invalid indices.") + assert( is_undef(idx) || is_finite(idx) || is_vector(idx), "Invalid indices.") is_def(idx) ? _sort_general(list,idx) : let(size = array_dim(list)) len(size)==1 ? _sort_scalars(list) : len(size)==2 && size[1] <=4 - ? ( + ? ( size[1]==0 ? list : size[1]==1 ? _sort_vectors1(list) : size[1]==2 ? _sort_vectors2(list) : size[1]==3 ? _sort_vectors3(list) /*size[1]==4*/ : _sort_vectors4(list) ) - : _sort_general(list); + : _sort_general(list); //*** // Formating and input check @@ -872,9 +872,9 @@ function sort(list, idx=undef) = // idxs3 = sortidx(lst, idx=[1,3]); // Returns: [3,0,2,1] function sortidx(list, idx=undef) = assert( is_list(list) || is_string(list) , "Invalid input." ) - assert( is_undef(idx) || is_finite(idx) || is_vector(idx), "Invalid indices.") + assert( is_undef(idx) || is_finite(idx) || is_vector(idx), "Invalid indices.") list==[] ? [] : - let( + let( size = array_dim(list), aug = is_undef(idx) && (len(size) == 1 || (len(size) == 2 && size[1]<=4)) ? zip(list, list_range(len(list))) @@ -882,14 +882,14 @@ function sortidx(list, idx=undef) = ) is_undef(idx) && len(size) == 1? subindex(_sort_vectors1(aug),1) : is_undef(idx) && len(size) == 2 && size[1] <=4 - ? ( + ? ( size[1]==0 ? list_range(len(arr)) : size[1]==1 ? subindex(_sort_vectors1(aug),1) : size[1]==2 ? subindex(_sort_vectors2(aug),2) : size[1]==3 ? subindex(_sort_vectors3(aug),3) /*size[1]==4*/ : subindex(_sort_vectors4(aug),4) ) - : // general case + : // general case subindex(_sort_general(aug, idx=list_range(s=1,n=len(aug)-1)), 0); //*** @@ -906,8 +906,8 @@ function sortidx(list, idx=undef) = function unique(arr) = assert(is_list(arr)||is_string(arr), "Invalid input." ) len(arr)<=1? arr : - let( sorted = sort(arr)) - [ for (i=[0:1:len(sorted)-1]) + let( sorted = sort(arr)) + [ for (i=[0:1:len(sorted)-1]) if (i==0 || (sorted[i] != sorted[i-1])) sorted[i] ]; @@ -1079,9 +1079,9 @@ function triplet_wrap(v) = // for (p=permute(regular_ngon(n=7,d=100))) stroke(p); function permute(l,n=2,_s=0) = assert(is_list(l), "Invalid list." ) - assert( is_finite(n) && n>=1 && n<=len(l), "Invalid number `n`." ) + assert( is_finite(n) && n>=1 && n<=len(l), "Invalid number `n`." ) n==1 - ? [for (i=[_s:1:len(l)-1]) [l[i]]] + ? [for (i=[_s:1:len(l)-1]) [l[i]]] : [for (i=[_s:1:len(l)-n], p=permute(l,n=n-1,_s=i+1)) concat([l[i]], p)]; @@ -1108,23 +1108,23 @@ function permute(l,n=2,_s=0) = // set_v = set_union(set_a, set_b, get_indices=true); // // set_v now equals [[5,0,1,2,6], [2,3,5,7,11,1,8]] function set_union(a, b, get_indices=false) = - assert( is_list(a) && is_list(b), "Invalid sets." ) + assert( is_list(a) && is_list(b), "Invalid sets." ) let( found1 = search(b, a), found2 = search(b, b), c = [ for (i=idx(b)) - if (found1[i] == [] && found2[i] == i) - b[i] - ], + if (found1[i] == [] && found2[i] == i) + b[i] + ], nset = concat(a, c) ) - ! get_indices ? nset : + ! get_indices ? nset : let( la = len(a), found3 = search(b, c), idxs = [ for (i=idx(b)) - (found1[i] != [])? found1[i] : la + found3[i] - ] + (found1[i] != [])? found1[i] : la + found3[i] + ] ) [idxs, nset]; //*** @@ -1145,7 +1145,7 @@ function set_union(a, b, get_indices=false) = // set_d = set_difference(set_a, set_b); // // set_d now equals [7,11] function set_difference(a, b) = - assert( is_list(a) && is_list(b), "Invalid sets." ) + assert( is_list(a) && is_list(b), "Invalid sets." ) let( found = search(a, b, num_returns_per_match=1) ) [ for (i=idx(a)) if(found[i]==[]) a[i] ]; @@ -1166,7 +1166,7 @@ function set_difference(a, b) = // set_i = set_intersection(set_a, set_b); // // set_i now equals [2,3,5] function set_intersection(a, b) = - assert( is_list(a) && is_list(b), "Invalid sets." ) + assert( is_list(a) && is_list(b), "Invalid sets." ) let( found = search(a, b, num_returns_per_match=1) ) [ for (i=idx(a)) if(found[i]!=[]) a[i] ]; @@ -1209,8 +1209,8 @@ function add_scalar(v,s) = // 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]] ) + [ for(val=v) + let( value=[for(i=idx) val[i]] ) len(value)==1 ? value[0] : value ]; @@ -1249,7 +1249,7 @@ function zip(vecs, v2, v3, fit=false, fill=undef) = let( minlen = list_shortest(vecs), maxlen = list_longest(vecs) - ) + ) assert(fit!=false || minlen==maxlen, "Input vectors to zip must have the same length") (fit == "long") ? [for(i=[0:1:maxlen-1]) [for(v=vecs) for(x=(i len(dimlist))? 0 : dimlist[depth-1] ; @@ -1362,10 +1362,10 @@ function array_dim(v, depth=undef) = function transpose(arr) = let( a0 = arr[0] ) is_list(a0) - ? assert([for(a=arr) if(len(a)!=len(a0)) 1]==[], "The array is not a matrix." ) - [for (i=[0:1:len(a0)-1]) - [ for (j=[0:1:len(arr)-1]) arr[j][i] ] ] - : arr; + ? assert([for(a=arr) if(len(a)!=len(a0)) 1]==[], "The array is not a matrix." ) + [for (i=[0:1:len(a0)-1]) + [ for (j=[0:1:len(arr)-1]) arr[j][i] ] ] + : arr; //*** // Input data check and formating diff --git a/common.scad b/common.scad index 6cc910e..f3fabd5 100644 --- a/common.scad +++ b/common.scad @@ -320,7 +320,7 @@ function scalar_vec3(v, dflt=undef) = function segs(r) = $fn>0? ($fn>3? $fn : 3) : let( r = is_finite(r)? r: 0 ) - ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs))) ; + ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs))) ; //*** // avoids undef diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad index d048afa..479f4bb 100644 --- a/tests/test_arrays.scad +++ b/tests/test_arrays.scad @@ -33,7 +33,7 @@ module test_in_list() { assert(in_list("bar", ["foo", "bar", "baz"])); assert(!in_list("bee", ["foo", "bar", "baz"])); assert(in_list("bar", [[2,"foo"], [4,"bar"], [3,"baz"]], idx=1)); - + assert(!in_list("bee", ["foo", "bar", ["bee"]])); assert(in_list(NAN, [NAN])==false); } diff --git a/vectors.scad b/vectors.scad index 92e4866..82535e0 100644 --- a/vectors.scad +++ b/vectors.scad @@ -217,8 +217,8 @@ function vector_axis(v1,v2=undef,v3=undef) = w1 = point3d(v1/norm(v1)), w2 = point3d(v2/norm(v2)), w3 = (norm(w1-w2) > eps && norm(w1+w2) > eps) ? w2 - : (norm(vabs(w2)-UP) > eps)? UP - : RIGHT + : (norm(vabs(w2)-UP) > eps)? UP + : RIGHT ) unit(cross(w1,w3));