diff --git a/arrays.scad b/arrays.scad index ff3d9d4..b0a9a74 100644 --- a/arrays.scad +++ b/arrays.scad @@ -672,15 +672,20 @@ function _valid_idx(idx,imin,imax) = // Function: shuffle() +// Usage: +// shuffled = shuffle(list,[seed]) // Description: // Shuffles the input list into random order. // If given a string, shuffles the characters within the string. -function shuffle(list) = +// If you give a numeric seed value then the permutation +// will be repeatable. +function shuffle(list,seed) = assert(is_list(list)||is_string(list), "Invalid input." ) - is_string(list)? str_join(shuffle([for (x = list) x])) : + is_string(list)? str_join(shuffle([for (x = list) x],seed=seed)) : len(list)<=1 ? list : let ( - rval = rands(0,1,len(list)), + rval = is_num(seed) ? rands(0,1,len(list),seed_value=seed) + : 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]] ) diff --git a/hull.scad b/hull.scad index e2a6b3d..5ca23e1 100644 --- a/hull.scad +++ b/hull.scad @@ -41,10 +41,10 @@ function hull(points) = // 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. +// far more. When fast mode is off, 3d hulls that lie in a plane will produce a single face of a polyhedron, which can be viewed in preview but will not render. // 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 +// fast = If true for 3d case, 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. Ignored for the 2d case. Default: false // Example(2D): // pts = [[-10,-10], [0,10], [10,10], [12,-10]]; // hull_points(pts); @@ -52,27 +52,26 @@ function hull(points) = // pts = [for (phi = [30:60:150], theta = [0:60:359]) spherical_to_xyz(10, theta, phi)]; // hull_points(pts); module hull_points(points, fast=false) { - 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:1: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 (is_num(perim[0])) { - polygon(points=points, paths=[perim]); - } else { - polyhedron(points=points, faces=perim); - } + assert(is_path(points)) + assert(len(points)>=3, "Point list must contain 3 points") + if (len(points[0])==2) + hull() polygon(points=points); + else { + if (fast) { + extra = len(points)%3; + faces = [ + [for(i=[0:1:extra+2])i], // If vertex count not divisible by 3, combine extras with first 3 + for(i=[extra+3:3:len(points)-3])[i,i+1,i+2] + ]; + hull() polyhedron(points=points, faces=faces); + } else { + faces = hull(points); + if (is_num(faces[0])){ + if (len(faces)<=2) echo("Hull contains only two points"); + else polyhedron(points=points, faces=[faces]); } + else polyhedron(points=points, faces=faces); + } } } diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad index 521e6e9..4483104 100644 --- a/tests/test_arrays.scad +++ b/tests/test_arrays.scad @@ -268,13 +268,16 @@ test_enumerate(); module test_shuffle() { nums1 = [for (i=list_range(100)) i]; - nums2 = shuffle(nums1); - nums3 = shuffle(nums2); - assert(len(nums2)==len(nums1)); - assert(len(nums3)==len(nums2)); + nums2 = shuffle(nums1,33); + nums3 = shuffle(nums2,99); + assert(sort(nums2)==nums1); + assert(sort(nums3)==nums1); assert(nums1!=nums2); assert(nums2!=nums3); assert(nums1!=nums3); + str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + shufstr = shuffle(str,12); + assert(shufstr != str && sort(shufstr)==str); } test_shuffle(); diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index 5979e86..63a1966 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -112,8 +112,8 @@ function info_str(list,i=0,string=chr(10)) = module test_closest_point_on_plane(){ - plane = rands(-5,5,4)+[10,0,0,0]; - point = rands(-1,1,3); + plane = rands(-5,5,4,seed=175)+[10,0,0,0]; + point = rands(-1,1,3,seed=477); point2 = closest_point_on_plane(plane,point); assert_approx(norm(point-point2), abs(distance_from_plane(plane,point))); } @@ -121,7 +121,7 @@ module test_closest_point_on_plane(){ module test_normalize_plane(){ - plane = rands(-5,5,4)+[10,0,0,0]; + plane = rands(-5,5,4,seed=333)+[10,0,0,0]; plane2 = normalize_plane(plane); assert_approx(norm(point3d(plane2)),1); assert_approx(plane*plane2[3],plane2*plane[3]); @@ -129,7 +129,7 @@ module test_normalize_plane(){ *test_normalize_plane(); module test_plane_line_intersection(){ - line = [rands(-1,1,3),rands(-1,1,3)+[2,0,0]]; + line = [rands(-1,1,3,seed=74),rands(-1,1,3,seed=99)+[2,0,0]]; plane1 = plane_from_normal(line[1]-line[0],2*line[0]-line[1]); // plane disjoint from segment plane2 = plane_from_normal(line[1]-line[0],(line[0]+line[1])/2); // through middle point of line plane3 = plane3pt(line[1],line[0], rands(-1,1,3)+[0,3,0]); // containing line diff --git a/tests/test_math.scad b/tests/test_math.scad index 2103eb8..b0e1782 100644 --- a/tests/test_math.scad +++ b/tests/test_math.scad @@ -238,7 +238,7 @@ test_approx(); module test_min_index() { - vals = rands(-100,100,100); + vals = rands(-100,100,100,seed=75); minval = min(vals); minidx = min_index(vals); assert_equal(vals[minidx], minval); @@ -254,7 +254,7 @@ test_min_index(); module test_max_index() { - vals = rands(-100,100,100); + vals = rands(-100,100,100,seed=97); maxval = max(vals); maxidx = max_index(vals); assert_equal(vals[maxidx], maxval);