Fixed bugs in hull_points, added seeds to tests, added seed parm to shuffle()

This commit is contained in:
Adrian Mariano 2020-12-22 18:15:25 -05:00
parent e10a70fc98
commit a495528398
5 changed files with 42 additions and 35 deletions

View file

@ -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]]
)

View file

@ -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);
}
}
}

View file

@ -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();

View file

@ -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

View file

@ -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);