diff --git a/coords.scad b/coords.scad index 53b103c..9043a3d 100644 --- a/coords.scad +++ b/coords.scad @@ -216,13 +216,14 @@ function xy_to_polar(x,y=undef) = let( // can be useful in taking a set of nearly coplanar points, and converting // them to a pure XY set of coordinates for manipulation, before convering // them back to the original 3D plane. -function xyz_to_planar(point, a, b, c) = let( - u = normalize(b-a), - v = normalize(c-a), - n = normalize(cross(u,v)), - w = normalize(cross(n,u)), - relpoint = point-a -) [relpoint * w, relpoint * u]; +function xyz_to_planar(point, a, b, c) = + let( + u = normalize(b-a), + v = normalize(c-a), + n = normalize(cross(u,v)), + w = normalize(cross(n,u)), + relpoint = point-a + ) [relpoint * w, relpoint * u]; // Function: planar_to_xyz() diff --git a/geometry.scad b/geometry.scad index fb0090d..f85b8ad 100644 --- a/geometry.scad +++ b/geometry.scad @@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Determine if the point is on the line segment between two points. // Returns true if yes, and false if not. // Arguments: -// point = The point to check colinearity of. +// point = The point to test. // edge = Array of two points forming the line segment to test against. function point_on_segment(point, edge) = point==edge[0] || point==edge[1] || // The point is an endpoint @@ -99,9 +99,9 @@ function right_of_line2d(line, pt) = // a = First point. // b = Second point. // c = Third point. -// eps = Acceptable max angle variance. Default: EPSILON (1e-9) degrees. +// eps = Acceptable variance. Default: `EPSILON` (1e-9) function collinear(a, b, c, eps=EPSILON) = - abs(vector_angle(b-a,c-a)) < eps; + distance_from_line([a,b], c) < eps; // Function: collinear_indexed() @@ -120,7 +120,23 @@ function collinear_indexed(points, a, b, c, eps=EPSILON) = p1=points[a], p2=points[b], p3=points[c] - ) abs(vector_angle(p2-p1,p3-p1)) < eps; + ) collinear(p1, p2, p3, eps); + + +// Function: distance_from_line() +// Usage: +// distance_from_line(line, pt); +// Description: +// Finds the perpendicular distance of a point `pt` from the line `line`. +// Arguments: +// line = A list of two points, defining a line that both are on. +// pt = A point to find the distance of from the line. +// Example: +// distance_from_line([[-10,0], [10,0]], [3,8]); // Returns: 8 +function distance_from_line(line, pt) = + let(a=line[0], n=normalize(line[1]-a), d=a-pt) + norm(d - ((d * n) * n)); + // Function: triangle_area2d() @@ -146,14 +162,19 @@ function triangle_area2d(a,b,c) = // Usage: // plane3pt(p1, p2, p3); // Description: -// Generates the cartesian equation of a plane from three non-colinear points on the plane. +// Generates the cartesian equation of a plane from three non-collinear points on the plane. // Returns [A,B,C,D] where Ax+By+Cz+D=0 is the equation of a plane. // Arguments: // p1 = The first point on the plane. // p2 = The second point on the plane. // p3 = The third point on the plane. function plane3pt(p1, p2, p3) = - let(normal = normalize(cross(p3-p1, p2-p1))) concat(normal, [normal*p1]); + let( + p1=point3d(p1), + p2=point3d(p2), + p3=point3d(p3), + normal = normalize(cross(p3-p1, p2-p1)) + ) concat(normal, [normal*p1]); // Function: plane3pt_indexed() @@ -173,9 +194,8 @@ function plane3pt_indexed(points, i1, i2, i3) = let( p1 = points[i1], p2 = points[i2], - p3 = points[i3], - normal = normalize(cross(p3-p1, p2-p1)) - ) concat(normal, [normal*p1]); + p3 = points[i3] + ) plane3pt(p1,p2,p3); // Function: distance_from_plane() diff --git a/hull.scad b/hull.scad index 926f74b..7164b86 100644 --- a/hull.scad +++ b/hull.scad @@ -11,33 +11,64 @@ ////////////////////////////////////////////////////////////////////// -// Section: 2D Convex Hulls +// Section: Convex Hulls -// Module: hull2d_points() +// Function: hull() // Usage: -// hull2d_points(points); +// hull(points); // Description: -// Takes a list of 2D points and creates a 2D convex polygon that encloses all those points. +// Takes a list of 2D or 3D points (but not both in the same list) and +// returns either the list of indexes into `points` that forms the 2D +// convex hull perimeter path, or the list of faces that form the 3d +// convex hull surface. Each face is a list of indexes into `points`. +// Arguments: +// points = The set of 2D or 3D points to find the hull of. +function hull(points) = let(two_d = len(points[0]) == 2) two_d? hull2d_path(points) : hull3d_faces(points); + + +// Module: hull_points() +// Usage: +// hull_points(points, [fast]); +// Description: +// If given a list of 2D points, creates a 2D convex hull polygon +// that encloses all those points. If given a list of 3D points, +// creates a 3D polyhedron that encloses all the points. This should +// handle about 4000 points in slow mode. If `fast` is set to true, +// this should be able to handle far more. +// Arguments: +// points = The list of points to form a hull around. +// fast = If true, uses a faster cheat that may handle more points, but also may emit warnings that can stop your script if you have "Halt on first warning" enabled. Default: false // Example(2D): // pts = [[-10,-10], [0,10], [10,10], [12,-10]]; -// hull2d_points(pts); -module hull2d_points(points) { - polygon(points=points, paths=[hull2d_path(points)]); -} - - -// Module: hull2d_points_fast() -// Usage: -// hull2d_points_fast(points); -// Description: -// Takes a list of 2D points and creates a 2D convex polygon that encloses all -// those points, using a faster method that may emit warning messages. -// Example(2D): -// pts = [[-10,-10], [0,10], [10,10], [12,-10]]; -// hull2d_points_fast(pts); -module hull2d_points_fast(points) { - hull() polygon(points); +// hull_points(pts); +// Example: +// ptr = [for (phi = [30:60:150], theta = [0:60:359]) spherical_to_xyz(10, theta, phi)]; +// hull_points(pts); +module hull_points(points, fast=false) { + assert(is_list(points)); + if (points) { + assert(is_list(points[0])); + if (fast) { + if (len(points[0]) == 2) { + hull() polygon(points=points); + } else { + extra = len(points)%3; + faces = concat( + [[for(i=[0:extra+2])i]], + [for(i=[extra+3:3:len(points)-3])[i,i+1,i+2]] + ); + hull() polyhedron(points=points, faces=faces); + } + } else { + perim = hull(points); + if (len(perim[0]) == 2) { + polygon(points=points, paths=[perim]); + } else { + polyhedron(points=points, faces=perim); + } + } + } } @@ -118,42 +149,6 @@ function _remove_conflicts_and_insert_point(polygon, conflicts, point) = -// Section: 3D Convex Hulls - - -// Module: hull3d_points() -// Usage: -// hull3d_points(points); -// Description: -// Takes a list of 3D points and creates a 3D convex polyhedron that encloses all those points. -// Example(3D): -// pts = [[-20,-20,0], [20,-20,0], [0,20,5], [0,0,20]]; -// hull3d_points(pts); -module hull3d_points(points) { - indices = hull3d_faces(points); - if (is_vector(indices)) { - polyhedron(points=points, faces=[indices]); - } else { - polyhedron(points=points, faces=indices); - } -} - - -// Module: hull3d_points_fast() -// Usage: -// hull3d_points_fast(points); -// Description: -// Takes a list of 3D points and creates a 3D convex polyhedron that encloses all -// those points, using a faster method that may emit warning messages. -// Example(3D): -// pts = [[-20,-20,0], [20,-20,0], [0,20,5], [0,0,20]]; -// hull3d_points_fast(pts); -module hull3d_points_fast(points) { - faces = [for (i=[0:len(points)-3]) let(c=_find_first_noncollinear([i,i+1], points, 2)) [i, i+1, c]]; - hull() polyhedron(points=points, faces=faces); -} - - // Function: hull3d_faces() // Usage: // hull3d_faces(points) @@ -170,8 +165,11 @@ module hull3d_points_fast(points) { // %polyhedron(points=pts, faces=faces); function hull3d_faces(points) = (len(points) < 3)? list_range(len(points)) : let ( - // start with a single triangle - a=0, b=1, c=2, + // start with a single non-collinear triangle + a = 0, + b = 1, + c = _find_first_noncollinear([a,b], points, 2) + ) (c == len(points))? _hull2d_collinear(points) : let( plane = plane3pt_indexed(points, a, b, c), d = _find_first_noncoplanar(plane, points, 3) ) (d == len(points))? /* all coplanar*/ let ( diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad new file mode 100644 index 0000000..1eb3713 --- /dev/null +++ b/tests/test_arrays.scad @@ -0,0 +1,201 @@ +include + + +// List/Array Ops + +module test_replist() { + assert(replist(1, 4) == [1,1,1,1]); + assert(replist(8, [2,3]) == [[8,8,8], [8,8,8]]); + assert(replist(0, [2,2,3]) == [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]); + assert(replist([1,2,3],3) == [[1,2,3], [1,2,3], [1,2,3]]); +} +test_replist(); + + +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(); + + +module test_select() { + l = [3,4,5,6,7,8,9]; + assert(select(l, 5, 6) == [8,9]); + assert(select(l, 5, 8) == [8,9,3,4]); + assert(select(l, 5, 2) == [8,9,3,4,5]); + assert(select(l, -3, -1) == [7,8,9]); + assert(select(l, 3, 3) == [6]); + assert(select(l, 4) == 7); + assert(select(l, -2) == 8); + assert(select(l, [1:3]) == [4,5,6]); + assert(select(l, [1,3]) == [4,6]); +} +test_select(); + + +module test_list_range() { + assert(list_range(4) == [0,1,2,3]); + assert(list_range(n=4, step=2) == [0,2,4,6]); + assert(list_range(n=4, s=3, step=3) == [3,6,9,12]); + assert(list_range(n=4, s=3, e=9, step=3) == [3,6,9]); + assert(list_range(e=3) == [0,1,2,3]); + assert(list_range(e=6, step=2) == [0,2,4,6]); + assert(list_range(s=3, e=5) == [3,4,5]); + assert(list_range(s=3, e=8, step=2) == [3,5,7]); + assert(list_range(s=4, e=8, step=2) == [4,6,8]); + assert(list_range(n=4, s=[3,4], step=[2,3]) == [[3,4], [5,7], [7,10], [9,13]]); +} +test_list_range(); + + +module test_reverse() { + assert(reverse([3,4,5,6]) == [6,5,4,3]); +} +test_reverse(); + + +// TODO: list_remove() +// TODO: list_insert() + + +module test_list_shortest() { + assert(list_shortest(["foobar", "bazquxx", "abcd"]) == 4); +} +test_list_shortest(); + + +module test_list_longest() { + assert(list_longest(["foobar", "bazquxx", "abcd"]) == 7); +} +test_list_longest(); + + +module test_list_pad() { + assert(list_pad([4,5,6], 5, 8) == [4,5,6,8,8]); + assert(list_pad([4,5,6,7,8], 5, 8) == [4,5,6,7,8]); + assert(list_pad([4,5,6,7,8,9], 5, 8) == [4,5,6,7,8,9]); +} +test_list_pad(); + + +module test_list_trim() { + assert(list_trim([4,5,6], 5) == [4,5,6]); + assert(list_trim([4,5,6,7,8], 5) == [4,5,6,7,8]); + assert(list_trim([3,4,5,6,7,8,9], 5) == [3,4,5,6,7]); +} +test_list_trim(); + + +module test_list_fit() { + assert(list_fit([4,5,6], 5, 8) == [4,5,6,8,8]); + assert(list_fit([4,5,6,7,8], 5, 8) == [4,5,6,7,8]); + assert(list_fit([3,4,5,6,7,8,9], 5, 8) == [3,4,5,6,7]); +} +test_list_fit(); + + +module test_enumerate() { + assert(enumerate(["a","b","c"]) == [[0,"a"], [1,"b"], [2,"c"]]); + assert(enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1) == [[0,"a"], [1,"b"], [2,"c"]]); + assert(enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]) == [[0,"a",12], [1,"b",10], [2,"c",14]]); +} +test_enumerate(); + + +module test_sort() { + assert(sort([7,3,9,4,3,1,8]) == [1,3,3,4,7,8,9]); + assert(sort(["cat", "oat", "sat", "bat", "vat", "rat", "pat", "mat", "fat", "hat", "eat"]) == ["bat", "cat", "eat", "fat", "hat", "mat", "oat", "pat", "rat", "sat", "vat"]); + assert(sort(enumerate([[2,3,4],[1,2,3],[2,4,3]]),idx=1)==[[1,[1,2,3]], [0,[2,3,4]], [2,[2,4,3]]]); +} +test_sort(); + + +module test_sortidx() { + lst1 = ["d","b","e","c"]; + assert(sortidx(lst1) == [1,3,0,2]); + lst2 = [ + ["foo", 88, [0,0,1], false], + ["bar", 90, [0,1,0], true], + ["baz", 89, [1,0,0], false], + ["qux", 23, [1,1,1], true] + ]; + assert(sortidx(lst2, idx=1) == [3,0,2,1]); + assert(sortidx(lst2, idx=0) == [1,2,0,3]); + assert(sortidx(lst2, idx=[1,3]) == [3,0,2,1]); + lst3 = [[-4, 0, 0], [0, 0, -4], [0, -4, 0], [-4, 0, 0], [0, -4, 0], [0, 0, 4], [0, 0, -4], [0, 4, 0], [4, 0, 0], [0, 0, 4], [0, 4, 0], [4, 0, 0]]; + assert(sortidx(lst3)==[0,3,2,4,1,6,5,9,7,10,8,11]); +} +test_sortidx(); + + +module test_unique() { + assert(unique([]) == []); + assert(unique([8]) == [8]); + assert(unique([7,3,9,4,3,1,8]) == [1,3,4,7,8,9]); +} +test_unique(); + + +// Arrays + + +module test_array_subindex() { + v = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]; + assert(array_subindex(v,2) == [3, 7, 11, 15]); + assert(array_subindex(v,[2,1]) == [[3, 2], [7, 6], [11, 10], [15, 14]]); + assert(array_subindex(v,[1:3]) == [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]]); +} +test_array_subindex(); + + +module test_array_zip() { + v1 = [1,2,3,4]; + v2 = [5,6,7]; + v3 = [8,9,10,11]; + assert(array_zip(v1,v3) == [[1,8],[2,9],[3,10],[4,11]]); + assert(array_zip([v1,v3]) == [[1,8],[2,9],[3,10],[4,11]]); + assert(array_zip([v1,v2],fit="short") == [[1,5],[2,6],[3,7]]); + assert(array_zip([v1,v2],fit="long") == [[1,5],[2,6],[3,7],[4,undef]]); + assert(array_zip([v1,v2],fit="long", fill=0) == [[1,5],[2,6],[3,7],[4,0]]); + assert(array_zip([v1,v2,v3],fit="long") == [[1,5,8],[2,6,9],[3,7,10],[4,undef,11]]); +} +test_array_zip(); + + +module test_array_group() { + v = [1,2,3,4,5,6]; + assert(array_group(v,2) == [[1,2], [3,4], [5,6]]); + assert(array_group(v,3) == [[1,2,3], [4,5,6]]); + assert(array_group(v,4,0) == [[1,2,3,4], [5,6,0,0]]); +} +test_array_group(); + + +module test_flatten() { + assert(flatten([[1,2,3], [4,5,[6,7,8]]]) == [1,2,3,4,5,[6,7,8]]); +} +test_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]); +} +test_array_dim(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_coords.scad b/tests/test_coords.scad new file mode 100644 index 0000000..6f489fd --- /dev/null +++ b/tests/test_coords.scad @@ -0,0 +1,96 @@ +include + + +module test_point2d() { + assert(point2d([1,2,3])==[1,2]); + assert(point2d([2,3])==[2,3]); + assert(point2d([1])==[1,0]); +} +test_point2d(); + + +module test_path2d() { + assert(path2d([[1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5]])==[[1,0],[1,2],[1,2],[1,2],[1,2]]); +} +test_path2d(); + + +module test_point3d() { + assert(point3d([1,2,3,4,5])==[1,2,3]); + assert(point3d([1,2,3,4])==[1,2,3]); + assert(point3d([1,2,3])==[1,2,3]); + assert(point3d([2,3])==[2,3,0]); + assert(point3d([1])==[1,0,0]); +} +test_point3d(); + + +module test_path3d() { + assert(path3d([[1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5]])==[[1,0,0],[1,2,0],[1,2,3],[1,2,3],[1,2,3]]); +} +test_path3d(); + + +module test_translate_points() { + pts = [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]; + assert(translate_points(pts, v=[1,2,3]) == [[1,2,4], [1,3,3], [2,2,3], [1,2,2], [1,1,3], [0,2,3]]); + assert(translate_points(pts, v=[-1,-2,-3]) == [[-1,-2,-2], [-1,-1,-3], [0,-2,-3], [-1,-2,-4], [-1,-3,-3], [-2,-2,-3]]); +} +test_translate_points(); + + +module test_scale_points() { + pts = [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]; + assert(scale_points(pts, v=[2,3,4]) == [[0,0,4], [0,3,0], [2,0,0], [0,0,-4], [0,-3,0], [-2,0,0]]); + assert(scale_points(pts, v=[-2,-3,-4]) == [[0,0,-4], [0,-3,0], [-2,0,0], [0,0,4], [0,3,0], [2,0,0]]); + assert(scale_points(pts, v=[1,1,1]) == [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]); + assert(scale_points(pts, v=[-1,-1,-1]) == [[0,0,-1], [0,-1,0], [-1,0,0], [0,0,1], [0,1,0], [1,0,0]]); +} +test_scale_points(); + + +module test_rotate_points2d() { + pts = [[0,1], [1,0], [0,-1], [-1,0]]; + s = sin(45); + assert(rotate_points2d(pts,45) == [[-s,s],[s,s],[s,-s],[-s,-s]]); + assert(rotate_points2d(pts,90) == [[-1,0],[0,1],[1,0],[0,-1]]); + assert(rotate_points2d(pts,90,cp=[1,0]) == [[0,-1],[1,0],[2,-1],[1,-2]]); +} +test_rotate_points2d(); + + +module test_rotate_points3d() { + pts = [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]; + assert(rotate_points3d(pts, [90,0,0]) == [[0,-1,0], [0,0,1], [1,0,0], [0,1,0], [0,0,-1], [-1,0,0]]); + assert(rotate_points3d(pts, [0,90,0]) == [[1,0,0], [0,1,0], [0,0,-1], [-1,0,0], [0,-1,0], [0,0,1]]); + assert(rotate_points3d(pts, [0,0,90]) == [[0,0,1], [-1,0,0], [0,1,0], [0,0,-1], [1,0,0], [0,-1,0]]); + assert(rotate_points3d(pts, [0,0,90],cp=[2,0,0]) == [[2,-2,1], [1,-2,0], [2,-1,0], [2,-2,-1], [3,-2,0], [2,-3,0]]); + assert(rotate_points3d(pts, 90, v=UP) == [[0,0,1], [-1,0,0], [0,1,0], [0,0,-1], [1,0,0], [0,-1,0]]); + assert(rotate_points3d(pts, 90, v=DOWN) == [[0,0,1], [1,0,0], [0,-1,0], [0,0,-1], [-1,0,0], [0,1,0]]); + assert(rotate_points3d(pts, 90, v=RIGHT) == [[0,-1,0], [0,0,1], [1,0,0], [0,1,0], [0,0,-1], [-1,0,0]]); + assert(rotate_points3d(pts, from=UP, to=BACK) == [[0,1,0], [0,0,-1], [1,0,0], [0,-1,0], [0,0,1], [-1,0,0]]); + assert(rotate_points3d(pts, 90, from=UP, to=BACK), [[0,1,0], [-1,0,0], [0,0,-1], [0,-1,0], [1,0,0], [0,0,1]]); + assert(rotate_points3d(pts, from=UP, to=UP*2) == [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]); + assert(rotate_points3d(pts, from=UP, to=DOWN*2) == [[0,0,-1], [0,1,0], [-1,0,0], [0,0,1], [0,-1,0], [1,0,0]]); +} +test_rotate_points3d(); + + +module test_simplify_path() +{ + path = [[-20,10],[-10,0],[-5,0],[0,0],[5,0],[10,0], [10,10]]; + assert(simplify_path(path) == [[-20,10],[-10,0],[10,0], [10,10]]); +} +test_simplify_path(); + + +module test_simplify_path_indexed() +{ + points = [[-20,10],[-10,0],[-5,0],[0,0],[5,0],[10,0], [10,10]]; + path = list_range(len(points)); + assert(simplify_path_indexed(points, path) == [0,1,5,6]); +} +test_simplify_path_indexed(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_convex_hull.scad b/tests/test_hull.scad similarity index 95% rename from tests/test_convex_hull.scad rename to tests/test_hull.scad index 1b7a33d..72ce28b 100644 --- a/tests/test_convex_hull.scad +++ b/tests/test_hull.scad @@ -1,5 +1,5 @@ -include -include +include +include testpoints_on_sphere = [ for(p = @@ -44,7 +44,7 @@ visualize_hull(testpoints3d); module visualize_hull(points) { - hull = convex_hull(points); + hull = hull(points); %if (len(hull) > 0 && is_list(hull[0]) && len(hull[0]) > 0) polyhedron(points=points, faces = hull); diff --git a/tests/test_math.scad b/tests/test_math.scad index 97f8499..9a43473 100644 --- a/tests/test_math.scad +++ b/tests/test_math.scad @@ -1,6 +1,4 @@ -include -include - +include // Simple Calculations @@ -63,6 +61,37 @@ module test_constrain() { test_constrain(); +module test_min_index() { + vals = rands(-100,100,100); + minval = min(vals); + minidx = min_index(vals); + assert(vals[minidx] == minval); + assert(min_index([3,4,5,6]) == 0); + assert(min_index([4,3,5,6]) == 1); + assert(min_index([4,5,3,6]) == 2); + assert(min_index([4,5,6,3]) == 3); + assert(min_index([6,5,4,3]) == 3); + assert(min_index([6,3,4,5]) == 1); + assert(min_index([-56,72,-874,5]) == 2); +} +test_min_index(); + + +module test_max_index() { + vals = rands(-100,100,100); + maxval = max(vals); + maxidx = max_index(vals); + assert(vals[maxidx] == maxval); + assert(max_index([3,4,5,6]) == 3); + assert(max_index([3,4,6,5]) == 2); + assert(max_index([3,6,4,5]) == 1); + assert(max_index([6,3,4,5]) == 0); + assert(max_index([5,6,4,3]) == 1); + assert(max_index([-56,72,-874,5]) == 1); +} +test_max_index(); + + module test_posmod() { assert(posmod(-5,3) == 1); assert(posmod(-4,3) == 2); @@ -86,6 +115,9 @@ module test_modrange() { test_modrange(); +// TODO: Tests for gaussian_rand() +// TODO: Tests for log_rand() + module test_segs() { assert(segs(50,$fn=8) == 8); assert(segs(50,$fa=2,$fs=2) == 158); @@ -292,358 +324,5 @@ test_count_true(); -// List/Array Ops - -module test_cdr() { - assert(cdr([]) == []); - assert(cdr([88]) == []); - assert(cdr([1,2,3]) == [2,3]); - assert(cdr(["a","b","c"]) == ["b","c"]); -} -test_cdr(); - - -module test_replist() { - assert(replist(1, 4) == [1,1,1,1]); - assert(replist(8, [2,3]) == [[8,8,8], [8,8,8]]); - assert(replist(0, [2,2,3]) == [[[0,0,0],[0,0,0]], [[0,0,0],[0,0,0]]]); - assert(replist([1,2,3],3) == [[1,2,3], [1,2,3], [1,2,3]]); -} -test_replist(); - - -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(); - - -module test_select() { - l = [3,4,5,6,7,8,9]; - assert(select(l, 5, 6) == [8,9]); - assert(select(l, 5, 8) == [8,9,3,4]); - assert(select(l, 5, 2) == [8,9,3,4,5]); - assert(select(l, -3, -1) == [7,8,9]); - assert(select(l, 3, 3) == [6]); - assert(select(l, 4) == 7); - assert(select(l, -2) == 8); - assert(select(l, [1:3]) == [4,5,6]); - assert(select(l, [1,3]) == [4,6]); -} -test_select(); - - -module test_reverse() { - assert(reverse([3,4,5,6]) == [6,5,4,3]); -} -test_reverse(); - - -module test_array_subindex() { - v = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]]; - assert(array_subindex(v,2) == [3, 7, 11, 15]); - assert(array_subindex(v,[2,1]) == [[3, 2], [7, 6], [11, 10], [15, 14]]); - assert(array_subindex(v,[1:3]) == [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]]); -} -test_array_subindex(); - - -module test_list_range() { - assert(list_range(4) == [0,1,2,3]); - assert(list_range(n=4, step=2) == [0,2,4,6]); - assert(list_range(n=4, s=3, step=3) == [3,6,9,12]); - assert(list_range(n=4, s=3, e=9, step=3) == [3,6,9]); - assert(list_range(e=3) == [0,1,2,3]); - assert(list_range(e=6, step=2) == [0,2,4,6]); - assert(list_range(s=3, e=5) == [3,4,5]); - assert(list_range(s=3, e=8, step=2) == [3,5,7]); - assert(list_range(s=4, e=8, step=2) == [4,6,8]); - assert(list_range(n=4, s=[3,4], step=[2,3]) == [[3,4], [5,7], [7,10], [9,13]]); -} -test_list_range(); - - -module test_array_shortest() { - assert(array_shortest(["foobar", "bazquxx", "abcd"]) == 4); -} -test_array_shortest(); - - -module test_array_longest() { - assert(array_longest(["foobar", "bazquxx", "abcd"]) == 7); -} -test_array_longest(); - - -module test_array_pad() { - assert(array_pad([4,5,6], 5, 8) == [4,5,6,8,8]); - assert(array_pad([4,5,6,7,8], 5, 8) == [4,5,6,7,8]); - assert(array_pad([4,5,6,7,8,9], 5, 8) == [4,5,6,7,8,9]); -} -test_array_pad(); - - -module test_array_trim() { - assert(array_trim([4,5,6], 5) == [4,5,6]); - assert(array_trim([4,5,6,7,8], 5) == [4,5,6,7,8]); - assert(array_trim([3,4,5,6,7,8,9], 5) == [3,4,5,6,7]); -} -test_array_trim(); - - -module test_array_fit() { - assert(array_fit([4,5,6], 5, 8) == [4,5,6,8,8]); - assert(array_fit([4,5,6,7,8], 5, 8) == [4,5,6,7,8]); - assert(array_fit([3,4,5,6,7,8,9], 5, 8) == [3,4,5,6,7]); -} -test_array_fit(); - - -module test_enumerate() { - assert(enumerate(["a","b","c"]) == [[0,"a"], [1,"b"], [2,"c"]]); - assert(enumerate([[88,"a"],[76,"b"],[21,"c"]], idx=1) == [[0,"a"], [1,"b"], [2,"c"]]); - assert(enumerate([["cat","a",12],["dog","b",10],["log","c",14]], idx=[1:2]) == [[0,"a",12], [1,"b",10], [2,"c",14]]); -} -test_enumerate(); - - -module test_array_zip() { - v1 = [1,2,3,4]; - v2 = [5,6,7]; - v3 = [8,9,10,11]; - assert(array_zip(v1,v3) == [[1,8],[2,9],[3,10],[4,11]]); - assert(array_zip([v1,v3]) == [[1,8],[2,9],[3,10],[4,11]]); - assert(array_zip([v1,v2],fit="short") == [[1,5],[2,6],[3,7]]); - assert(array_zip([v1,v2],fit="long") == [[1,5],[2,6],[3,7],[4,undef]]); - assert(array_zip([v1,v2],fit="long", fill=0) == [[1,5],[2,6],[3,7],[4,0]]); - assert(array_zip([v1,v2,v3],fit="long") == [[1,5,8],[2,6,9],[3,7,10],[4,undef,11]]); -} -test_array_zip(); - - -module test_array_group() { - v = [1,2,3,4,5,6]; - assert(array_group(v,2) == [[1,2], [3,4], [5,6]]); - assert(array_group(v,3) == [[1,2,3], [4,5,6]]); - assert(array_group(v,4,0) == [[1,2,3,4], [5,6,0,0]]); -} -test_array_group(); - - -module test_flatten() { - assert(flatten([[1,2,3], [4,5,[6,7,8]]]) == [1,2,3,4,5,[6,7,8]]); -} -test_flatten(); - - -module test_sort() { - assert(sort([7,3,9,4,3,1,8]) == [1,3,3,4,7,8,9]); - assert(sort(["cat", "oat", "sat", "bat", "vat", "rat", "pat", "mat", "fat", "hat", "eat"]) == ["bat", "cat", "eat", "fat", "hat", "mat", "oat", "pat", "rat", "sat", "vat"]); - assert(sort(enumerate([[2,3,4],[1,2,3],[2,4,3]]),idx=1)==[[1,[1,2,3]], [0,[2,3,4]], [2,[2,4,3]]]); -} -test_sort(); - - -module test_sortidx() { - lst1 = ["d","b","e","c"]; - assert(sortidx(lst1) == [1,3,0,2]); - lst2 = [ - ["foo", 88, [0,0,1], false], - ["bar", 90, [0,1,0], true], - ["baz", 89, [1,0,0], false], - ["qux", 23, [1,1,1], true] - ]; - assert(sortidx(lst2, idx=1) == [3,0,2,1]); - assert(sortidx(lst2, idx=0) == [1,2,0,3]); - assert(sortidx(lst2, idx=[1,3]) == [3,0,2,1]); - lst3 = [[-4, 0, 0], [0, 0, -4], [0, -4, 0], [-4, 0, 0], [0, -4, 0], [0, 0, 4], [0, 0, -4], [0, 4, 0], [4, 0, 0], [0, 0, 4], [0, 4, 0], [4, 0, 0]]; - assert(sortidx(lst3)==[0,3,2,4,1,6,5,9,7,10,8,11]); -} -test_sortidx(); - - -module test_unique() { - assert(unique([]) == []); - assert(unique([8]) == [8]); - assert(unique([7,3,9,4,3,1,8]) == [1,3,4,7,8,9]); -} -test_unique(); - - -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]); -} -test_array_dim(); - - -module test_vmul() { - assert(vmul([3,4,5], [8,7,6]) == [24,28,30]); - assert(vmul([1,2,3], [4,5,6]) == [4,10,18]); -} -test_vmul(); - - -module test_vdiv() { - assert(vdiv([24,28,30], [8,7,6]) == [3, 4, 5]); -} -test_vdiv(); - - -module test_vabs() { - assert(vabs([2,4,8]) == [2,4,8]); - assert(vabs([-2,-4,-8]) == [2,4,8]); - assert(vabs([-2,4,8]) == [2,4,8]); - assert(vabs([2,-4,8]) == [2,4,8]); - assert(vabs([2,4,-8]) == [2,4,8]); -} -test_vabs(); - - -module test_normalize() { - assert(normalize([10,0,0]) == [1,0,0]); - assert(normalize([0,10,0]) == [0,1,0]); - assert(normalize([0,0,10]) == [0,0,1]); - assert(abs(norm(normalize([10,10,10]))-1) < EPSILON); - assert(abs(norm(normalize([-10,-10,-10]))-1) < EPSILON); - assert(abs(norm(normalize([-10,0,0]))-1) < EPSILON); - assert(abs(norm(normalize([0,-10,0]))-1) < EPSILON); - assert(abs(norm(normalize([0,0,-10]))-1) < EPSILON); -} -test_normalize(); - - -module test_vector_angle() { - vecs = [[10,0,0], [-10,0,0], [0,10,0], [0,-10,0], [0,0,10], [0,0,-10]]; - for (a=vecs, b=vecs) { - if(a==b) { - assert(vector_angle(a,b)==0); - } else if(a==-b) { - assert(vector_angle(a,b)==180); - } else { - assert(vector_angle(a,b)==90); - } - } - assert(abs(vector_angle([10,10,0],[10,0,0])-45) < EPSILON); -} -test_vector_angle(); - - -module test_vector_axis() { - assert(norm(vector_axis([10,0,0],[10,10,0]) - [0,0,1]) < EPSILON); - assert(norm(vector_axis([10,0,0],[0,10,0]) - [0,0,1]) < EPSILON); - assert(norm(vector_axis([0,10,0],[10,0,0]) - [0,0,-1]) < EPSILON); - assert(norm(vector_axis([0,0,10],[10,0,0]) - [0,1,0]) < EPSILON); - assert(norm(vector_axis([10,0,0],[0,0,10]) - [0,-1,0]) < EPSILON); - assert(norm(vector_axis([10,0,10],[0,-10,0]) - [sin(45),0,-sin(45)]) < EPSILON); -} -test_vector_axis(); - - -module test_point2d() { - assert(point2d([1,2,3])==[1,2]); - assert(point2d([2,3])==[2,3]); - assert(point2d([1])==[1,0]); -} -test_point2d(); - - -module test_path2d() { - assert(path2d([[1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5]])==[[1,0],[1,2],[1,2],[1,2],[1,2]]); -} -test_path2d(); - - -module test_point3d() { - assert(point3d([1,2,3,4,5])==[1,2,3]); - assert(point3d([1,2,3,4])==[1,2,3]); - assert(point3d([1,2,3])==[1,2,3]); - assert(point3d([2,3])==[2,3,0]); - assert(point3d([1])==[1,0,0]); -} -test_point3d(); - - -module test_path3d() { - assert(path3d([[1], [1,2], [1,2,3], [1,2,3,4], [1,2,3,4,5]])==[[1,0,0],[1,2,0],[1,2,3],[1,2,3],[1,2,3]]); -} -test_path3d(); - - -module test_translate_points() { - pts = [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]; - assert(translate_points(pts, v=[1,2,3]) == [[1,2,4], [1,3,3], [2,2,3], [1,2,2], [1,1,3], [0,2,3]]); - assert(translate_points(pts, v=[-1,-2,-3]) == [[-1,-2,-2], [-1,-1,-3], [0,-2,-3], [-1,-2,-4], [-1,-3,-3], [-2,-2,-3]]); -} -test_translate_points(); - - -module test_scale_points() { - pts = [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]; - assert(scale_points(pts, v=[2,3,4]) == [[0,0,4], [0,3,0], [2,0,0], [0,0,-4], [0,-3,0], [-2,0,0]]); - assert(scale_points(pts, v=[-2,-3,-4]) == [[0,0,-4], [0,-3,0], [-2,0,0], [0,0,4], [0,3,0], [2,0,0]]); - assert(scale_points(pts, v=[1,1,1]) == [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]); - assert(scale_points(pts, v=[-1,-1,-1]) == [[0,0,-1], [0,-1,0], [-1,0,0], [0,0,1], [0,1,0], [1,0,0]]); -} -test_scale_points(); - - -module test_rotate_points2d() { - pts = [[0,1], [1,0], [0,-1], [-1,0]]; - s = sin(45); - assert(rotate_points2d(pts,45) == [[-s,s],[s,s],[s,-s],[-s,-s]]); - assert(rotate_points2d(pts,90) == [[-1,0],[0,1],[1,0],[0,-1]]); - assert(rotate_points2d(pts,90,cp=[1,0]) == [[0,-1],[1,0],[2,-1],[1,-2]]); -} -test_rotate_points2d(); - - -module test_rotate_points3d() { - pts = [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]; - assert(rotate_points3d(pts, [90,0,0]) == [[0,-1,0], [0,0,1], [1,0,0], [0,1,0], [0,0,-1], [-1,0,0]]); - assert(rotate_points3d(pts, [0,90,0]) == [[1,0,0], [0,1,0], [0,0,-1], [-1,0,0], [0,-1,0], [0,0,1]]); - assert(rotate_points3d(pts, [0,0,90]) == [[0,0,1], [-1,0,0], [0,1,0], [0,0,-1], [1,0,0], [0,-1,0]]); - assert(rotate_points3d(pts, [0,0,90],cp=[2,0,0]) == [[2,-2,1], [1,-2,0], [2,-1,0], [2,-2,-1], [3,-2,0], [2,-3,0]]); - assert(rotate_points3d(pts, 90, axis=V_UP) == [[0,0,1], [-1,0,0], [0,1,0], [0,0,-1], [1,0,0], [0,-1,0]]); - assert(rotate_points3d(pts, 90, axis=V_DOWN) == [[0,0,1], [1,0,0], [0,-1,0], [0,0,-1], [-1,0,0], [0,1,0]]); - assert(rotate_points3d(pts, 90, axis=V_RIGHT) == [[0,-1,0], [0,0,1], [1,0,0], [0,1,0], [0,0,-1], [-1,0,0]]); - assert(rotate_points3d(pts, from=V_UP, to=V_BACK) == [[0,1,0], [0,0,-1], [1,0,0], [0,-1,0], [0,0,1], [-1,0,0]]); - assert(rotate_points3d(pts, 90, from=V_UP, to=V_BACK), [[0,1,0], [-1,0,0], [0,0,-1], [0,-1,0], [1,0,0], [0,0,1]]); - assert(rotate_points3d(pts, from=V_UP, to=V_UP*2) == [[0,0,1], [0,1,0], [1,0,0], [0,0,-1], [0,-1,0], [-1,0,0]]); - assert(rotate_points3d(pts, from=V_UP, to=V_DOWN*2) == [[0,0,-1], [0,1,0], [-1,0,0], [0,0,1], [0,-1,0], [1,0,0]]); -} -test_rotate_points3d(); - - -module test_simplify_path() -{ - path = [[-20,10],[-10,0],[-5,0],[0,0],[5,0],[10,0], [10,10]]; - assert(simplify_path(path) == [[-20,10],[-10,0],[10,0], [10,10]]); -} -test_simplify_path(); - - -module test_simplify_path_indexed() -{ - points = [[-20,10],[-10,0],[-5,0],[0,0],[5,0],[10,0], [10,10]]; - path = list_range(len(points)); - assert(simplify_path_indexed(points, path) == [0,1,5,6]); -} -test_simplify_path_indexed(); - // vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_vectors.scad b/tests/test_vectors.scad new file mode 100644 index 0000000..b5a5b26 --- /dev/null +++ b/tests/test_vectors.scad @@ -0,0 +1,67 @@ +include + + +module test_vmul() { + assert(vmul([3,4,5], [8,7,6]) == [24,28,30]); + assert(vmul([1,2,3], [4,5,6]) == [4,10,18]); +} +test_vmul(); + + +module test_vdiv() { + assert(vdiv([24,28,30], [8,7,6]) == [3, 4, 5]); +} +test_vdiv(); + + +module test_vabs() { + assert(vabs([2,4,8]) == [2,4,8]); + assert(vabs([-2,-4,-8]) == [2,4,8]); + assert(vabs([-2,4,8]) == [2,4,8]); + assert(vabs([2,-4,8]) == [2,4,8]); + assert(vabs([2,4,-8]) == [2,4,8]); +} +test_vabs(); + + +module test_normalize() { + assert(normalize([10,0,0]) == [1,0,0]); + assert(normalize([0,10,0]) == [0,1,0]); + assert(normalize([0,0,10]) == [0,0,1]); + assert(abs(norm(normalize([10,10,10]))-1) < EPSILON); + assert(abs(norm(normalize([-10,-10,-10]))-1) < EPSILON); + assert(abs(norm(normalize([-10,0,0]))-1) < EPSILON); + assert(abs(norm(normalize([0,-10,0]))-1) < EPSILON); + assert(abs(norm(normalize([0,0,-10]))-1) < EPSILON); +} +test_normalize(); + + +module test_vector_angle() { + vecs = [[10,0,0], [-10,0,0], [0,10,0], [0,-10,0], [0,0,10], [0,0,-10]]; + for (a=vecs, b=vecs) { + if(a==b) { + assert(vector_angle(a,b)==0); + } else if(a==-b) { + assert(vector_angle(a,b)==180); + } else { + assert(vector_angle(a,b)==90); + } + } + assert(abs(vector_angle([10,10,0],[10,0,0])-45) < EPSILON); +} +test_vector_angle(); + + +module test_vector_axis() { + assert(norm(vector_axis([10,0,0],[10,10,0]) - [0,0,1]) < EPSILON); + assert(norm(vector_axis([10,0,0],[0,10,0]) - [0,0,1]) < EPSILON); + assert(norm(vector_axis([0,10,0],[10,0,0]) - [0,0,-1]) < EPSILON); + assert(norm(vector_axis([0,0,10],[10,0,0]) - [0,1,0]) < EPSILON); + assert(norm(vector_axis([10,0,0],[0,0,10]) - [0,-1,0]) < EPSILON); + assert(norm(vector_axis([10,0,10],[0,-10,0]) - [sin(45),0,-sin(45)]) < EPSILON); +} +test_vector_axis(); + + +// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap