From 243dd7723ea62ad7095cb0e9366f055dffb9772d Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Mon, 2 Mar 2020 21:39:57 -0500 Subject: [PATCH] added is_consistent, added error check to sum(), clarified docs to is_path, and added fast versions of path2d, path3d and path4d. --- common.scad | 15 +++++++++++++++ coords.scad | 52 +++++++++++++++++++++++++++++++++++++++++++--------- math.scad | 8 +++++--- paths.scad | 3 +++ 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/common.scad b/common.scad index 6f4bcfc..30c96fa 100644 --- a/common.scad +++ b/common.scad @@ -100,6 +100,21 @@ function is_list_of(list,pattern) = []==[for(entry=list) if (entry*0 != pattern) entry]; +// Function: is_consistent() +// Usage: +// is_consistent(list) +// Description: +// Tests whether input is a list of entries which all have the same list structure +// and are filled with finite numerical data. +// Example: +// is_consistent([3,4,5]); // Returns true +// is_consistent([[3,4],[4,5],[6,7]]); // Returns true +// is_consistent([[3,4,5],[3,4]]); // Returns false +// is_consistent([[3,[3,4,[5]]], [5,[2,9,[9]]]]); // Returns true +// is_consistent([[3,[3,4,[5]]], [5,[2,9,9]]]); // Returns false +function is_consistent(list) = + is_list(list) && is_list_of(list, list[0]); + // Section: Handling `undef`s. diff --git a/coords.scad b/coords.scad index 0762858..f55e267 100644 --- a/coords.scad +++ b/coords.scad @@ -22,13 +22,19 @@ function point2d(p, fill=0) = [for (i=[0:1]) (p[i]==undef)? fill : p[i]]; // Function: path2d() // Description: -// Returns a list of 2D vectors/points from a list of 2D or 3D vectors/points. -// If given a 3D point list, removes the Z coordinates from each point. +// Returns a list of 2D vectors/points from a list of 2D, 3D or higher +// dimensional vectors/points. Removes the extra coordinates from +// higher dimensional points. The input must be a path, where +// every vector has the same length. // Arguments: // points = A list of 2D or 3D points/vectors. // fill = Value to fill missing values in vectors with. -function path2d(points, fill=0) = [for (point = points) point2d(point, fill=fill)]; - +function path2d(points) = + assert(is_path(points,dim=undef,fast=true),"Input to path2d is not a path") + let (result = points * concat(ident(2), replist([0,0], len(points[0])-2))) + assert(is_def(result), "Invalid input to path2d") + result; + // Function: point3d() // Description: @@ -41,11 +47,22 @@ function point3d(p, fill=0) = [for (i=[0:2]) (p[i]==undef)? fill : p[i]]; // Function: path3d() // Description: -// Returns a list of 3D vectors/points from a list of 2D or 3D vectors/points. +// Returns a list of 3D vectors/points from a list of 2D or higher dimensional vectors/points +// by removing extra coordinates or adding the z coordinate. // Arguments: -// points = A list of 2D or 3D points/vectors. -// fill = Value to fill missing values in vectors with. -function path3d(points, fill=0) = [for (point = points) point3d(point, fill=fill)]; +// points = A list of 2D, 3D or higher dimensional points/vectors. +// fill = Value to fill missing values in vectors with (in the 2D case) +function path3d(points, fill=0) = + assert(is_num(fill)) + assert(is_path(points, dim=undef, fast=true), "Input to path3d is not a path") + let ( + change = len(points[0])-3, + M = change < 0 ? [[1,0,0],[0,1,0]] : + concat(ident(3), replist([0,0,0],change)), + result = points*M + ) + assert(is_def(result), "Input to path3d is invalid") + fill == 0 || change>=0 ? result : result + replist([0,0,fill], len(result)); // Function: point4d() @@ -63,7 +80,24 @@ function point4d(p, fill=0) = [for (i=[0:3]) (p[i]==undef)? fill : p[i]]; // Arguments: // points = A list of 2D or 3D points/vectors. // fill = Value to fill missing values in vectors with. -function path4d(points, fill=0) = [for (point = points) point4d(point, fill=fill)]; + +function path4d(points, fill=0) = + assert(is_num(fill) || is_vector(fill)) + assert(is_path(points, dim=undef, fast=true), "Input to path4d is not a path") + let ( + change = len(points[0])-4, + M = change < 0 ? select(ident(4), 0, len(points[0])-1) : + concat(ident(4), replist([0,0,0,0],change)), + result = points*M + ) + assert(is_def(result), "Input to path4d is invalid") + fill == 0 || change >= 0 ? result : + let( + addition = is_list(fill) ? concat(0*points[0],fill) : + concat(0*points[0],replist(fill,-change)) + ) + assert(len(addition) == 4, "Fill is the wrong length") + result + replist(addition, len(result)); // Function: translate_points() diff --git a/math.scad b/math.scad index 2733f06..f0637a7 100644 --- a/math.scad +++ b/math.scad @@ -432,9 +432,11 @@ function lcm(a,b=[]) = // Example: // sum([1,2,3]); // returns 6. // sum([[1,2,3], [3,4,5], [5,6,7]]); // returns [9, 12, 15] -function sum(v, dflt=0, _i=0, _acc) = - _i>=len(v)? (len(v)? _acc : dflt) : - sum(v, dflt=dflt, _i=_i+1, _acc=is_undef(_acc)? v[_i] : _acc+v[_i]); +function sum(v, dflt=0) = + assert(is_consistent(v), "Input to sum is non-numeric or inconsistent") + len(v) == 0 ? dflt : _sum(v,v[0]*0); + +function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1); // Function: cumsum() diff --git a/paths.scad b/paths.scad index 2688f03..8088e5e 100644 --- a/paths.scad +++ b/paths.scad @@ -20,6 +20,8 @@ include // Description: // Returns true if `list` is a path. A path is a list of two or more numeric vectors (AKA points). // All vectors must of the same size, and may only contain numbers that are not inf or nan. +// By default the vectors in a path must be 2d or 3d. Set the `dim` parameter to specify a list +// of allowed dimensions, or set it to `undef` to allow any dimension. // Examples: // is_path([[3,4],[5,6]]); // Returns true // is_path([[3,4]]); // Returns false @@ -35,6 +37,7 @@ include // is_path([[3,4],"hello"], fast=true); // Returns true // is_path([[3,4],[3,4,5]]); // Returns false // is_path([[1,2,3,4],[2,3,4,5]]); // Returns false +// is_path([[1,2,3,4],[2,3,4,5]],undef);// Returns true // Arguments: // list = list to check // dim = list of allowed dimensions of the vectors in the path. Default: [2,3]