diff --git a/affine.scad b/affine.scad index dd465db..4ad3f1c 100644 --- a/affine.scad +++ b/affine.scad @@ -20,13 +20,13 @@ function ident(n) = [for (i = [0:1:n-1]) [for (j = [0:1:n-1]) (i==j)?1:0]]; // Function: affine2d_to_3d() // Description: Takes a 3x3 affine2d matrix and returns its 4x4 affine3d equivalent. function affine2d_to_3d(m) = concat( - [for (r = [0:2]) - concat( - [for (c = [0:2]) m[r][c]], - [0] - ) - ], - [[0, 0, 0, 1]] + [for (r = [0:2]) + concat( + [for (c = [0:2]) m[r][c]], + [0] + ) + ], + [[0, 0, 0, 1]] ); @@ -45,9 +45,9 @@ function affine2d_identity() = ident(3); // Arguments: // v = 2D Offset to translate by. [X,Y] function affine2d_translate(v) = [ - [1, 0, v.x], - [0, 1, v.y], - [0 ,0, 1] + [1, 0, v.x], + [0, 1, v.y], + [0 ,0, 1] ]; @@ -57,9 +57,9 @@ function affine2d_translate(v) = [ // Arguments: // v = 2D vector of scaling factors. [X,Y] function affine2d_scale(v) = [ - [v.x, 0, 0], - [ 0, v.y, 0], - [ 0, 0, 1] + [v.x, 0, 0], + [ 0, v.y, 0], + [ 0, 0, 1] ]; @@ -69,9 +69,9 @@ function affine2d_scale(v) = [ // Arguments: // ang = Number of degrees to rotate. function affine2d_zrot(ang) = [ - [cos(ang), -sin(ang), 0], - [sin(ang), cos(ang), 0], - [ 0, 0, 1] + [cos(ang), -sin(ang), 0], + [sin(ang), cos(ang), 0], + [ 0, 0, 1] ]; @@ -83,12 +83,12 @@ function affine2d_zrot(ang) = [ // Arguments: // v = The normal vector of the line to reflect across. function affine2d_mirror(v) = - let(v=unit(point2d(v)), a=v.x, b=v.y) - [ - [1-2*a*a, 0-2*a*b, 0], - [0-2*a*b, 1-2*b*b, 0], - [ 0, 0, 1] - ]; + let(v=unit(point2d(v)), a=v.x, b=v.y) + [ + [1-2*a*a, 0-2*a*b, 0], + [0-2*a*b, 1-2*b*b, 0], + [ 0, 0, 1] + ]; // Function: affine2d_skew() @@ -100,9 +100,9 @@ function affine2d_mirror(v) = // xa = Skew angle, in degrees, in the direction of the X axis. // ya = Skew angle, in degrees, in the direction of the Y axis. function affine2d_skew(xa, ya) = [ - [1, tan(xa), 0], - [tan(ya), 1, 0], - [0, 0, 1] + [1, tan(xa), 0], + [tan(ya), 1, 0], + [0, 0, 1] ]; @@ -114,8 +114,8 @@ function affine2d_skew(xa, ya) = [ // Arguments: // affines = A list of 3x3 affine2d matrices. function affine2d_chain(affines, _m=undef, _i=0) = - (_i>=len(affines))? (is_undef(_m)? ident(3) : _m) : - affine2d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1); + (_i>=len(affines))? (is_undef(_m)? ident(3) : _m) : + affine2d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1); @@ -133,10 +133,10 @@ function affine3d_identity() = ident(4); // Arguments: // v = 3D offset to translate by. [X,Y,Z] function affine3d_translate(v) = [ - [1, 0, 0, v.x], - [0, 1, 0, v.y], - [0, 0, 1, v.z], - [0 ,0, 0, 1] + [1, 0, 0, v.x], + [0, 1, 0, v.y], + [0, 0, 1, v.z], + [0 ,0, 0, 1] ]; @@ -146,10 +146,10 @@ function affine3d_translate(v) = [ // Arguments: // v = 3D vector of scaling factors. [X,Y,Z] function affine3d_scale(v) = [ - [v.x, 0, 0, 0], - [ 0, v.y, 0, 0], - [ 0, 0, v.z, 0], - [ 0, 0, 0, 1] + [v.x, 0, 0, 0], + [ 0, v.y, 0, 0], + [ 0, 0, v.z, 0], + [ 0, 0, 0, 1] ]; @@ -159,10 +159,10 @@ function affine3d_scale(v) = [ // Arguments: // ang = number of degrees to rotate. function affine3d_xrot(ang) = [ - [1, 0, 0, 0], - [0, cos(ang), -sin(ang), 0], - [0, sin(ang), cos(ang), 0], - [0, 0, 0, 1] + [1, 0, 0, 0], + [0, cos(ang), -sin(ang), 0], + [0, sin(ang), cos(ang), 0], + [0, 0, 0, 1] ]; @@ -172,10 +172,10 @@ function affine3d_xrot(ang) = [ // Arguments: // ang = Number of degrees to rotate. function affine3d_yrot(ang) = [ - [ cos(ang), 0, sin(ang), 0], - [ 0, 1, 0, 0], - [-sin(ang), 0, cos(ang), 0], - [ 0, 0, 0, 1] + [ cos(ang), 0, sin(ang), 0], + [ 0, 1, 0, 0], + [-sin(ang), 0, cos(ang), 0], + [ 0, 0, 0, 1] ]; @@ -187,10 +187,10 @@ function affine3d_yrot(ang) = [ // Arguments: // ang = number of degrees to rotate. function affine3d_zrot(ang) = [ - [cos(ang), -sin(ang), 0, 0], - [sin(ang), cos(ang), 0, 0], - [ 0, 0, 1, 0], - [ 0, 0, 0, 1] + [cos(ang), -sin(ang), 0, 0], + [sin(ang), cos(ang), 0, 0], + [ 0, 0, 1, 0], + [ 0, 0, 0, 1] ]; @@ -203,18 +203,18 @@ function affine3d_zrot(ang) = [ // u = 3D axis vector to rotate around. // ang = number of degrees to rotate. function affine3d_rot_by_axis(u, ang) = - approx(ang,0)? affine3d_identity() : - let( - u = unit(u), - c = cos(ang), - c2 = 1-c, - s = sin(ang) - ) [ - [u.x*u.x*c2+c , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0], - [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c , u.y*u.z*c2-u.x*s, 0], - [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c , 0], - [ 0, 0, 0, 1] - ]; + approx(ang,0)? affine3d_identity() : + let( + u = unit(u), + c = cos(ang), + c2 = 1-c, + s = sin(ang) + ) [ + [u.x*u.x*c2+c , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0], + [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c , u.y*u.z*c2-u.x*s, 0], + [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c , 0], + [ 0, 0, 0, 1] + ]; // Function: affine3d_rot_from_to() @@ -226,22 +226,22 @@ function affine3d_rot_by_axis(u, ang) = // from = 3D axis vector to rotate from. // to = 3D axis vector to rotate to. function affine3d_rot_from_to(from, to) = - let( - from = unit(point3d(from)), - to = unit(point3d(to)) - ) approx(from,to)? affine3d_identity() : - let( - u = vector_axis(from,to), - ang = vector_angle(from,to), - c = cos(ang), - c2 = 1-c, - s = sin(ang) - ) [ - [u.x*u.x*c2+c , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0], - [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c , u.y*u.z*c2-u.x*s, 0], - [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c , 0], - [ 0, 0, 0, 1] - ]; + let( + from = unit(point3d(from)), + to = unit(point3d(to)) + ) approx(from,to)? affine3d_identity() : + let( + u = vector_axis(from,to), + ang = vector_angle(from,to), + c = cos(ang), + c2 = 1-c, + s = sin(ang) + ) [ + [u.x*u.x*c2+c , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0], + [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c , u.y*u.z*c2-u.x*s, 0], + [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c , 0], + [ 0, 0, 0, 1] + ]; // Function: affine_frame_map() @@ -266,35 +266,35 @@ function affine3d_rot_from_to(from, to) = // // The next map sends [1,1,0] to [0,1,1] and [-1,1,0] to [0,-1,1] // T = affine_frame_map(x=[0,1,1], y=[0,-1,1]) * affine_frame_map(x=[1,1,0], y=[-1,1,0],reverse=true); function affine_frame_map(x,y,z, reverse=false) = - assert(num_defined([x,y,z])>=2, "Must define at least two inputs") - let( - xvalid = is_undef(x) || (is_vector(x) && len(x)==3), - yvalid = is_undef(y) || (is_vector(y) && len(y)==3), - zvalid = is_undef(z) || (is_vector(z) && len(z)==3) - ) - assert(xvalid,"Input x must be a length 3 vector") - assert(yvalid,"Input y must be a length 3 vector") - assert(zvalid,"Input z must be a length 3 vector") - let( - x = is_undef(x)? undef : unit(x), - y = is_undef(y)? undef : unit(y), - z = is_undef(z)? undef : unit(z), - map = is_undef(x)? [cross(y,z), y, z] : - is_undef(y)? [x, cross(z,x), z] : - is_undef(z)? [x, y, cross(x,y)] : - [x, y, z] - ) - reverse? ( - let( - ocheck = ( - approx(map[0]*map[1],0) && - approx(map[0]*map[2],0) && - approx(map[1]*map[2],0) - ) - ) - assert(ocheck, "Inputs must be orthogonal when reverse==true") - affine2d_to_3d(map) - ) : affine2d_to_3d(transpose(map)); + assert(num_defined([x,y,z])>=2, "Must define at least two inputs") + let( + xvalid = is_undef(x) || (is_vector(x) && len(x)==3), + yvalid = is_undef(y) || (is_vector(y) && len(y)==3), + zvalid = is_undef(z) || (is_vector(z) && len(z)==3) + ) + assert(xvalid,"Input x must be a length 3 vector") + assert(yvalid,"Input y must be a length 3 vector") + assert(zvalid,"Input z must be a length 3 vector") + let( + x = is_undef(x)? undef : unit(x), + y = is_undef(y)? undef : unit(y), + z = is_undef(z)? undef : unit(z), + map = is_undef(x)? [cross(y,z), y, z] : + is_undef(y)? [x, cross(z,x), z] : + is_undef(z)? [x, y, cross(x,y)] : + [x, y, z] + ) + reverse? ( + let( + ocheck = ( + approx(map[0]*map[1],0) && + approx(map[0]*map[2],0) && + approx(map[1]*map[2],0) + ) + ) + assert(ocheck, "Inputs must be orthogonal when reverse==true") + affine2d_to_3d(map) + ) : affine2d_to_3d(transpose(map)); @@ -306,15 +306,15 @@ function affine_frame_map(x,y,z, reverse=false) = // Arguments: // v = The normal vector of the plane to reflect across. function affine3d_mirror(v) = - let( - v=unit(point3d(v)), - a=v.x, b=v.y, c=v.z - ) [ - [1-2*a*a, -2*a*b, -2*a*c, 0], - [ -2*b*a, 1-2*b*b, -2*b*c, 0], - [ -2*c*a, -2*c*b, 1-2*c*c, 0], - [ 0, 0, 0, 1] - ]; + let( + v=unit(point3d(v)), + a=v.x, b=v.y, c=v.z + ) [ + [1-2*a*a, -2*a*b, -2*a*c, 0], + [ -2*b*a, 1-2*b*b, -2*b*c, 0], + [ -2*c*a, -2*c*b, 1-2*c*c, 0], + [ 0, 0, 0, 1] + ]; // Function: affine3d_skew() @@ -330,10 +330,10 @@ function affine3d_mirror(v) = // szx = Skew factor multiplier for skewing along the Z axis as you get farther from the X axis. Default: 0 // szy = Skew factor multiplier for skewing along the Z axis as you get farther from the Y axis. Default: 0 function affine3d_skew(sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) = [ - [ 1, sxy, sxz, 0], - [syx, 1, syz, 0], - [szx, szy, 1, 0], - [ 0, 0, 0, 1] + [ 1, sxy, sxz, 0], + [syx, 1, syz, 0], + [szx, szy, 1, 0], + [ 0, 0, 0, 1] ]; @@ -346,10 +346,10 @@ function affine3d_skew(sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) = [ // xa = Skew angle, in degrees, in the direction of the X axis. // ya = Skew angle, in degrees, in the direction of the Y axis. function affine3d_skew_xy(xa, ya) = [ - [1, 0, tan(xa), 0], - [0, 1, tan(ya), 0], - [0, 0, 1, 0], - [0, 0, 0, 1] + [1, 0, tan(xa), 0], + [0, 1, tan(ya), 0], + [0, 0, 1, 0], + [0, 0, 0, 1] ]; @@ -362,10 +362,10 @@ function affine3d_skew_xy(xa, ya) = [ // xa = Skew angle, in degrees, in the direction of the X axis. // za = Skew angle, in degrees, in the direction of the Z axis. function affine3d_skew_xz(xa, za) = [ - [1, tan(xa), 0, 0], - [0, 1, 0, 0], - [0, tan(za), 1, 0], - [0, 0, 0, 1] + [1, tan(xa), 0, 0], + [0, 1, 0, 0], + [0, tan(za), 1, 0], + [0, 0, 0, 1] ]; @@ -378,10 +378,10 @@ function affine3d_skew_xz(xa, za) = [ // ya = Skew angle, in degrees, in the direction of the Y axis. // za = Skew angle, in degrees, in the direction of the Z axis. function affine3d_skew_yz(ya, za) = [ - [ 1, 0, 0, 0], - [tan(ya), 1, 0, 0], - [tan(za), 0, 1, 0], - [ 0, 0, 0, 1] + [ 1, 0, 0, 0], + [tan(ya), 1, 0, 0], + [tan(za), 0, 1, 0], + [ 0, 0, 0, 1] ]; @@ -393,8 +393,8 @@ function affine3d_skew_yz(ya, za) = [ // Arguments: // affines = A list of 4x4 affine3d matrices. function affine3d_chain(affines, _m=undef, _i=0) = - (_i>=len(affines))? (is_undef(_m)? ident(4) : _m) : - affine3d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1); + (_i>=len(affines))? (is_undef(_m)? ident(4) : _m) : + affine3d_chain(affines, _m=(is_undef(_m)? affines[_i] : affines[_i] * _m), _i=_i+1); // Function: apply() @@ -464,4 +464,4 @@ function is_2d_transform(t) = // z-parameters are zero, except we allow t[2][ -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/arrays.scad b/arrays.scad index 3fddebb..6ed7be5 100644 --- a/arrays.scad +++ b/arrays.scad @@ -44,20 +44,20 @@ // select(l, [1:3]); // Returns [4,5,6] // select(l, [1,3]); // Returns [4,6] function select(list, start, end=undef) = - 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]]) - ); + 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]]) + ); // Function: slice() @@ -75,10 +75,10 @@ function select(list, start, end=undef) = // 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]]; + 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: in_list() @@ -107,7 +107,7 @@ function in_list(x,l,idx=undef) = search([x], l, num_returns_per_match=1, index_ // 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] function min_index(vals, all=false) = - all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; + all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; // Function: max_index() @@ -123,7 +123,7 @@ 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) = - all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; + all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; // Function: list_increasing() @@ -136,8 +136,8 @@ function max_index(vals, all=false) = // list_increasing([1,3,2,4]); // Returns: false // list_increasing([4,3,2,1]); // Returns: false function list_increasing(list) = - assert(is_list(list)||is_string(list)) - len([for (p=pair(list)) if(p.x>p.y) true])==0; + assert(is_list(list)||is_string(list)) + len([for (p=pair(list)) if(p.x>p.y) true])==0; // Function: list_decreasing() @@ -150,8 +150,8 @@ function list_increasing(list) = // list_decreasing([4,2,3,1]); // Returns: false // list_decreasing([4,3,2,1]); // Returns: true function list_decreasing(list) = - assert(is_list(list)||is_string(list)) - len([for (p=pair(list)) if(p.x=len(n))? val : - [for (j=[1:1:n[i]]) repeat(val, n, i+1)]; + is_num(n)? [for(j=[1:1:n]) val] : + (i>=len(n))? val : + [for (j=[1:1:n[i]]) repeat(val, n, i+1)]; // Function: list_range() @@ -207,13 +207,13 @@ function repeat(val, n, i=0) = // list_range(s=4, e=8, 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`."); + (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`."); @@ -227,8 +227,8 @@ function list_range(n=undef, s=0, e=undef, step=undef) = // Example: // reverse([3,4,5,6]); // Returns [6,5,4,3] function reverse(list) = - assert(is_list(list)||is_string(list)) - [ for (i = [len(list)-1 : -1 : 0]) list[i] ]; + assert(is_list(list)||is_string(list)) + [ for (i = [len(list)-1 : -1 : 0]) list[i] ]; // Function: list_rotate() @@ -251,9 +251,9 @@ 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)) - select(list,n,n+len(list)-1); + assert(is_list(list)||is_string(list)) + assert(is_num(n)) + select(list,n,n+len(list)-1); // Function: deduplicate() @@ -272,13 +272,13 @@ function list_rotate(list,n=1) = // 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]] 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]]; + 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]]; // Function: deduplicate_indexed() @@ -296,23 +296,23 @@ 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)) - 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_list(list)||is_string(list)) + assert(indices==[] || is_vector(indices)) + 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] + ]; // Function: repeat_entries() @@ -341,17 +341,17 @@ 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") - 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)] - ) - [for(i=[0:length-1]) each repeat(list[i],reps[i])]; + assert(is_list(list)) + assert((is_num(N) && N>0) || is_vector(N),"Parameter N to repeat_entries must be postive number or vector") + 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)] + ) + [for(i=[0:length-1]) each repeat(list[i],reps[i])]; // Function: list_set() @@ -375,29 +375,29 @@ function repeat_entries(list, N, exact = true) = // 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) = - 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) - ); + 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) + ); // Function: list_insert() @@ -409,31 +409,31 @@ function list_set(list=[],indices,values,dflt=0,minlen=0) = // list_insert([3,6,9,12],1,5); // Returns [3,5,6,9,12] // list_insert([3,6,9,12],[1,3],[5,11]); // Returns [3,5,6,9,11,12] function list_insert(list, pos, elements, _i=0) = - assert(is_list(list)||is_string(list)) - is_list(pos)? ( - assert(len(pos)==len(elements)) - let( - idxs = sortidx(pos), - lastidx = pos[idxs[len(idxs)-1]] - ) - concat( - [ - for(i=idx(idxs)) each concat( - assert(pos[idxs[i]]<=len(list), "Indices in pos must be <= len(list)") - [for (j=[(i==0?0:pos[idxs[i-1]]):1:pos[idxs[i]]-1]) list[j]], - [elements[idxs[i]]] - ) - ], - [for (j=[lastidx:1:len(list)-1]) list[j]] - ) - ) : ( - assert(pos<=len(list), "Indices in pos must be <= len(list)") - concat( - slice(list,0,pos), - elements, - (poslength)? list_trim(v,length) : list_pad(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); @@ -596,148 +596,148 @@ function list_fit(v, length, fill) = // Description: // Shuffles the input list into random order. function shuffle(list) = - assert(is_list(list)||is_string(list)) - 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)); + assert(is_list(list)||is_string(list)) + 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)); // Sort a vector of scalar values function _sort_scalars(arr) = - 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) ); + 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) ); // 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( - 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) ); + len(arr)<=1 ? arr : + !(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) ); // 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( - 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) ); + len(arr)<=1 ? arr : + !(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) ); // 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( - 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[1]==pivot[1] && - y[2]>pivot[2] - ) - ) - ) - ) y - ] - ) concat( _sort_vectors3(lesser), equal, _sort_vectors3(greater) ); + len(arr)<=1 ? arr : 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[1]==pivot[1] && + y[2]>pivot[2] + ) + ) + ) + ) y + ] + ) 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( - 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[1]==pivot[1] && ( - y[2]>pivot[2] || ( - y[2]==pivot[2] && - y[3]>pivot[3] - ) - ) - ) - ) - ) - ) y - ] - ) concat( _sort_vectors4(lesser), equal, _sort_vectors4(greater) ); + len(arr)<=1 ? arr : 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[1]==pivot[1] && ( + y[2]>pivot[2] || ( + y[2]==pivot[2] && + y[3]>pivot[3] + ) + ) + ) + ) + ) + ) y + ] + ) 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 = [ - for (entry = arr) let( - val = idx==undef? entry : [for (i=idx) entry[i]], - cmp = compare_vals(val, pivotval) - ) cmp - ], - 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)); + (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 + ], + 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)); // Function: sort() @@ -756,17 +756,17 @@ function _sort_general(arr, idx=undef) = // l = [45,2,16,37,8,3,9,23,89,12,34]; // 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 : - 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); + !is_list(list) || len(list)<=1 ? list : + 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); // Function: sortidx() @@ -782,31 +782,31 @@ function sort(list, idx=undef) = // ordered = select(lst, idxs); // Returns: ["b", "c", "d", "e"] // Example: // lst = [ -// ["foo", 88, [0,0,1], false], -// ["bar", 90, [0,1,0], true], -// ["baz", 89, [1,0,0], false], -// ["qux", 23, [1,1,1], true] +// ["foo", 88, [0,0,1], false], +// ["bar", 90, [0,1,0], true], +// ["baz", 89, [1,0,0], false], +// ["qux", 23, [1,1,1], true] // ]; // idxs1 = sortidx(lst, idx=1); // Returns: [3,0,2,1] // 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( - 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) - ) - 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); + 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) + ) + 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); // Function: unique() @@ -817,14 +817,14 @@ 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]) - if (i==0 || (sorted[i] != sorted[i-1])) - sorted[i] - ]; + assert(is_list(arr)||is_string(arr)) + 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] + ]; // Function: unique_count() @@ -860,8 +860,8 @@ 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)) - [start : step : len(list)+end]; + assert(is_list(list)||is_string(list)) + [start : step : len(list)+end]; // Function: enumerate() @@ -879,10 +879,10 @@ 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)) + (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() @@ -903,8 +903,8 @@ function force_list(value) = is_list(value) ? value : [value]; // 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)) - [for (i=[0:1:len(v)-2]) [v[i],v[i+1]]]; + assert(is_list(v)||is_string(v)) + [for (i=[0:1:len(v)-2]) [v[i],v[i+1]]]; // Function: pair_wrap() @@ -916,8 +916,8 @@ 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)) - [for (i=[0:1:len(v)-1]) [v[i],v[(i+1)%len(v)]]]; + assert(is_list(v)||is_string(v)) + [for (i=[0:1:len(v)-1]) [v[i],v[(i+1)%len(v)]]]; // Function: triplet() @@ -929,8 +929,8 @@ 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)) - [for (i=[0:1:len(v)-3]) [v[i],v[i+1],v[i+2]]]; + assert(is_list(v)||is_string(v)) + [for (i=[0:1:len(v)-3]) [v[i],v[i+1],v[i+2]]]; // Function: triplet_wrap() @@ -942,8 +942,8 @@ 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)) - [for (i=[0:1:len(v)-1]) [v[i],v[(i+1)%len(v)],v[(i+2)%len(v)]]]; + assert(is_list(v)||is_string(v)) + [for (i=[0:1:len(v)-1]) [v[i],v[(i+1)%len(v)],v[(i+2)%len(v)]]]; // Function: permute() @@ -962,10 +962,10 @@ 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)) + 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)]; @@ -991,25 +991,25 @@ 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) = - let( - found1 = search(b, a), - found2 = search(b, b), - c = [ - for (i=idx(b)) - if (found1[i] == [] && found2[i] == i) - b[i] - ], - nset = concat(a, c) - ) !get_indices? nset : - let( - la = len(a), - found3 = search(b, c), - idxs = [ - for (i=idx(b)) - (found1[i] != [])? found1[i] : - la + found3[i] - ] - ) [idxs, nset]; + let( + found1 = search(b, a), + found2 = search(b, b), + c = [ + for (i=idx(b)) + if (found1[i] == [] && found2[i] == i) + b[i] + ], + nset = concat(a, c) + ) !get_indices? nset : + let( + la = len(a), + found3 = search(b, c), + idxs = [ + for (i=idx(b)) + (found1[i] != [])? found1[i] : + la + found3[i] + ] + ) [idxs, nset]; // Function: set_difference() @@ -1026,9 +1026,9 @@ 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] ]; + let( + found = search(a, b, num_returns_per_match=1) + ) [ for (i=idx(a)) if(found[i]==[]) a[i] ]; // Function: set_intersection() @@ -1045,9 +1045,9 @@ 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] ]; + let( + found = search(a, b, num_returns_per_match=1) + ) [ for (i=idx(a)) if(found[i]!=[]) a[i] ]; @@ -1068,8 +1068,8 @@ function set_intersection(a, b) = // 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]]) - len(value)==1 ? value[0] : value + for(val=v) let(value=[for(i=idx) val[i]]) + len(value)==1 ? value[0] : value ]; @@ -1100,17 +1100,17 @@ function subindex(v, idx) = [ // v2 = [[20,19,18], [17,16,15], [14,13,12]]; // zip(v1,v2); // Returns [[1,2,3,20,19,18], [4,5,6,17,16,15], [7,8,9,14,13,12]] 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(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] - ); + (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] + ); @@ -1213,9 +1213,9 @@ 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; + is_list(arr[0])? [for (i=[0:1:len(arr[0])-1]) [for (j=[0:1:len(arr)-1]) arr[j][i]]] : arr; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/attachments.scad b/attachments.scad index bbd93cc..bb6b817 100644 --- a/attachments.scad +++ b/attachments.scad @@ -173,75 +173,75 @@ function anchorpt(name, pos=[0,0,0], orient=UP, spin=0) = [name, pos, orient, sp // geom = attach_geom(anchor, spin, orient, path=path); // function attach_geom( - size, size2, shift, - r,r1,r2, d,d1,d2, l,h, - vnf, path, - extent=true, - offset=[0,0,0], - anchors=[], - two_d=false + size, size2, shift, + r,r1,r2, d,d1,d2, l,h, + vnf, path, + extent=true, + offset=[0,0,0], + anchors=[], + two_d=false ) = - assert(is_bool(extent)) - assert(is_vector(offset)) - assert(is_list(anchors)) - assert(is_bool(two_d)) - !is_undef(size)? ( - two_d? ( - let( - size2 = default(size2, size.x), - shift = default(shift, 0) - ) - assert(is_vector(size,2)) - assert(is_num(size2)) - assert(is_num(shift)) - ["rect", point2d(size), size2, shift, offset, anchors] - ) : ( - let( - size2 = default(size2, point2d(size)), - shift = default(shift, [0,0]) - ) - assert(is_vector(size,3)) - assert(is_vector(size2,2)) - assert(is_vector(shift,2)) - ["cuboid", size, size2, shift, offset, anchors] - ) - ) : !is_undef(vnf)? ( - assert(is_vnf(vnf)) - assert(two_d == false) - extent? ["vnf_extent", vnf, offset, anchors] : - ["vnf_isect", vnf, offset, anchors] - ) : !is_undef(path)? ( - assert(is_path(path),2) - assert(two_d == true) - extent? ["path_extent", path, offset, anchors] : - ["path_isect", path, offset, anchors] - ) : - let( - r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef) - ) - !is_undef(r1)? ( - let( l = default(l, h) ) - !is_undef(l)? ( - let( - shift = default(shift, [0,0]), - r2 = get_radius(r1=r2,d1=d2,r=r,d=d,dflt=undef) - ) - assert(is_num(r1) || is_vector(r1,2)) - assert(is_num(r2) || is_vector(r2,2)) - assert(is_num(l)) - assert(is_vector(shift,2)) - ["cyl", r1, r2, l, shift, offset, anchors] - ) : ( - two_d? ( - assert(is_num(r1) || is_vector(r1,2)) - ["circle", r1, offset, anchors] - ) : ( - assert(is_num(r1) || is_vector(r1,3)) - ["spheroid", r1, offset, anchors] - ) - ) - ) : - assert(false, "Unrecognizable geometry description."); + assert(is_bool(extent)) + assert(is_vector(offset)) + assert(is_list(anchors)) + assert(is_bool(two_d)) + !is_undef(size)? ( + two_d? ( + let( + size2 = default(size2, size.x), + shift = default(shift, 0) + ) + assert(is_vector(size,2)) + assert(is_num(size2)) + assert(is_num(shift)) + ["rect", point2d(size), size2, shift, offset, anchors] + ) : ( + let( + size2 = default(size2, point2d(size)), + shift = default(shift, [0,0]) + ) + assert(is_vector(size,3)) + assert(is_vector(size2,2)) + assert(is_vector(shift,2)) + ["cuboid", size, size2, shift, offset, anchors] + ) + ) : !is_undef(vnf)? ( + assert(is_vnf(vnf)) + assert(two_d == false) + extent? ["vnf_extent", vnf, offset, anchors] : + ["vnf_isect", vnf, offset, anchors] + ) : !is_undef(path)? ( + assert(is_path(path),2) + assert(two_d == true) + extent? ["path_extent", path, offset, anchors] : + ["path_isect", path, offset, anchors] + ) : + let( + r1 = get_radius(r1=r1,d1=d1,r=r,d=d,dflt=undef) + ) + !is_undef(r1)? ( + let( l = default(l, h) ) + !is_undef(l)? ( + let( + shift = default(shift, [0,0]), + r2 = get_radius(r1=r2,d1=d2,r=r,d=d,dflt=undef) + ) + assert(is_num(r1) || is_vector(r1,2)) + assert(is_num(r2) || is_vector(r2,2)) + assert(is_num(l)) + assert(is_vector(shift,2)) + ["cyl", r1, r2, l, shift, offset, anchors] + ) : ( + two_d? ( + assert(is_num(r1) || is_vector(r1,2)) + ["circle", r1, offset, anchors] + ) : ( + assert(is_num(r1) || is_vector(r1,3)) + ["spheroid", r1, offset, anchors] + ) + ) + ) : + assert(false, "Unrecognizable geometry description."); @@ -251,9 +251,9 @@ function attach_geom( // Description: // Returns true if the given attachment geometry description is for a 2D shape. function attach_geom_2d(geom) = - let( type = geom[0] ) - type == "rect" || type == "circle" || - type == "path_isect" || type == "path_extent"; + let( type = geom[0] ) + type == "rect" || type == "circle" || + type == "path_isect" || type == "path_extent"; // Function: attach_geom_size() @@ -262,47 +262,47 @@ function attach_geom_2d(geom) = // Description: // Returns the `[X,Y,Z]` bounding size for the given attachment geometry description. function attach_geom_size(geom) = - let( type = geom[0] ) - type == "cuboid"? ( //size, size2, shift - let( - size=geom[1], size2=geom[2], shift=point2d(geom[3]), - maxx = max(size.x,size2.x), - maxy = max(size.y,size2.y), - z = size.z - ) [maxx, maxy, z] - ) : type == "cyl"? ( //r1, r2, l, shift - let( - r1=geom[1], r2=geom[2], l=geom[3], shift=point2d(geom[4]), - rx1 = default(r1[0],r1), - ry1 = default(r1[1],r1), - rx2 = default(r2[0],r2), - ry2 = default(r2[1],r2), - maxxr = max(rx1,rx2), - maxyr = max(ry1,ry2) - ) [2*maxxr,2*maxyr,l] - ) : type == "spheroid"? ( //r - let( r=geom[1] ) - is_num(r)? [2,2,2]*r : vmul([2,2,2],r) - ) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf - let( - mm = pointlist_bounds(geom[1][0]), - delt = mm[1]-mm[0] - ) delt - ) : type == "rect"? ( //size, size2 - let( - size=geom[1], size2=geom[2], - maxx = max(size.x,size2) - ) [maxx, size.y] - ) : type == "circle"? ( //r - let( r=geom[1] ) - is_num(r)? [2,2]*r : vmul([2,2],r) - ) : type == "path_isect" || type == "path_extent"? ( //path - let( - mm = pointlist_bounds(geom[1]), - delt = mm[1]-mm[0] - ) [delt.x, delt.y] - ) : - assert(false, "Unknown attachment geometry type."); + let( type = geom[0] ) + type == "cuboid"? ( //size, size2, shift + let( + size=geom[1], size2=geom[2], shift=point2d(geom[3]), + maxx = max(size.x,size2.x), + maxy = max(size.y,size2.y), + z = size.z + ) [maxx, maxy, z] + ) : type == "cyl"? ( //r1, r2, l, shift + let( + r1=geom[1], r2=geom[2], l=geom[3], shift=point2d(geom[4]), + rx1 = default(r1[0],r1), + ry1 = default(r1[1],r1), + rx2 = default(r2[0],r2), + ry2 = default(r2[1],r2), + maxxr = max(rx1,rx2), + maxyr = max(ry1,ry2) + ) [2*maxxr,2*maxyr,l] + ) : type == "spheroid"? ( //r + let( r=geom[1] ) + is_num(r)? [2,2,2]*r : vmul([2,2,2],r) + ) : type == "vnf_extent" || type=="vnf_isect"? ( //vnf + let( + mm = pointlist_bounds(geom[1][0]), + delt = mm[1]-mm[0] + ) delt + ) : type == "rect"? ( //size, size2 + let( + size=geom[1], size2=geom[2], + maxx = max(size.x,size2) + ) [maxx, size.y] + ) : type == "circle"? ( //r + let( r=geom[1] ) + is_num(r)? [2,2]*r : vmul([2,2],r) + ) : type == "path_isect" || type == "path_extent"? ( //path + let( + mm = pointlist_bounds(geom[1]), + delt = mm[1]-mm[0] + ) [delt.x, delt.y] + ) : + assert(false, "Unknown attachment geometry type."); // Function: attach_transform() @@ -318,61 +318,61 @@ function attach_geom_size(geom) = // geom = The geometry description of the shape. // p = If given as a VNF, path, or point, applies the affine3d transformation matrix to it and returns the result. function attach_transform(anchor=CENTER, spin=0, orient=UP, geom, p) = - assert(is_string(anchor) || is_vector(anchor)) - assert(is_vector(orient)) - let( - two_d = attach_geom_2d(geom), - m = ($attach_to != undef)? ( - let( - anch = find_anchor($attach_to, geom), - pos = anch[1] - ) two_d? ( - assert(two_d && is_num(spin)) - affine3d_zrot(spin) * - rot(to=FWD, from=point3d(anch[2])) * - affine3d_translate(point3d(-pos)) - ) : ( - assert(is_num(spin) || is_vector(spin,3)) - let( - ang = vector_angle(anch[2], DOWN), - axis = vector_axis(anch[2], DOWN), - ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3], - axis2 = rot(p=axis,[0,0,ang2]) - ) - affine3d_rot_by_axis(axis2,ang) * ( - is_num(spin)? affine3d_zrot(ang2+spin) : ( - affine3d_zrot(spin.z) * - affine3d_yrot(spin.y) * - affine3d_xrot(spin.x) * - affine3d_zrot(ang2) - ) - ) * affine3d_translate(point3d(-pos)) - ) - ) : ( - let( - pos = find_anchor(anchor, geom)[1] - ) two_d? ( - assert(two_d && is_num(spin)) - affine3d_zrot(spin) * - affine3d_translate(point3d(-pos)) - ) : ( - assert(is_num(spin) || is_vector(spin,3)) - let( - axis = vector_axis(UP,orient), - ang = vector_angle(UP,orient) - ) - affine3d_rot_by_axis(axis,ang) * ( - is_num(spin)? affine3d_zrot(spin) : ( - affine3d_zrot(spin.z) * - affine3d_yrot(spin.y) * - affine3d_xrot(spin.x) - ) - ) * affine3d_translate(point3d(-pos)) - ) - ) - ) is_undef(p)? m : - is_vnf(p)? [apply(m, p[0]), p[1]] : - apply(m, p); + assert(is_string(anchor) || is_vector(anchor)) + assert(is_vector(orient)) + let( + two_d = attach_geom_2d(geom), + m = ($attach_to != undef)? ( + let( + anch = find_anchor($attach_to, geom), + pos = anch[1] + ) two_d? ( + assert(two_d && is_num(spin)) + affine3d_zrot(spin) * + rot(to=FWD, from=point3d(anch[2])) * + affine3d_translate(point3d(-pos)) + ) : ( + assert(is_num(spin) || is_vector(spin,3)) + let( + ang = vector_angle(anch[2], DOWN), + axis = vector_axis(anch[2], DOWN), + ang2 = (anch[2]==UP || anch[2]==DOWN)? 0 : 180-anch[3], + axis2 = rot(p=axis,[0,0,ang2]) + ) + affine3d_rot_by_axis(axis2,ang) * ( + is_num(spin)? affine3d_zrot(ang2+spin) : ( + affine3d_zrot(spin.z) * + affine3d_yrot(spin.y) * + affine3d_xrot(spin.x) * + affine3d_zrot(ang2) + ) + ) * affine3d_translate(point3d(-pos)) + ) + ) : ( + let( + pos = find_anchor(anchor, geom)[1] + ) two_d? ( + assert(two_d && is_num(spin)) + affine3d_zrot(spin) * + affine3d_translate(point3d(-pos)) + ) : ( + assert(is_num(spin) || is_vector(spin,3)) + let( + axis = vector_axis(UP,orient), + ang = vector_angle(UP,orient) + ) + affine3d_rot_by_axis(axis,ang) * ( + is_num(spin)? affine3d_zrot(spin) : ( + affine3d_zrot(spin.z) * + affine3d_yrot(spin.y) * + affine3d_xrot(spin.x) + ) + ) * affine3d_translate(point3d(-pos)) + ) + ) + ) is_undef(p)? m : + is_vnf(p)? [apply(m, p[0]), p[1]] : + apply(m, p); // Function: find_anchor() @@ -387,166 +387,166 @@ function attach_transform(anchor=CENTER, spin=0, orient=UP, geom, p) = // anchor = Vector or named anchor string. // geom = The geometry description of the shape. function find_anchor(anchor, geom) = - let( - offset = anchor==CENTER? CENTER : select(geom,-2), - anchors = select(geom,-1), - type = geom[0] - ) - is_string(anchor)? ( - let(found = search([anchor], anchors, num_returns_per_match=1)[0]) - assert(found!=[], str("Unknown anchor: ",anchor)) - anchors[found] - ) : - assert(is_vector(anchor),str("anchor=",anchor)) - let(anchor = point3d(anchor)) - anchor==CENTER? [anchor, CENTER, UP, 0] : - let( - oang = ( - approx(point2d(anchor), [0,0])? 0 : - atan2(anchor.y, anchor.x)+90 - ) - ) - type == "cuboid"? ( //size, size2, shift - let( - size=geom[1], size2=geom[2], shift=point2d(geom[3]), - h = size.z, - u = (anchor.z+1)/2, - axy = point2d(anchor), - bot = point3d(vmul(point2d(size)/2,axy),-h/2), - top = point3d(vmul(point2d(size2)/2,axy)+shift,h/2), - pos = lerp(bot,top,u)+offset, - sidevec = unit(rot(from=UP, to=top-bot, p=point3d(axy))), - vvec = unit([0,0,anchor.z]), - vec = anchor==CENTER? UP : - approx(axy,[0,0])? unit(anchor) : - approx(anchor.z,0)? sidevec : - unit((sidevec+vvec)/2) - ) [anchor, pos, vec, oang] - ) : type == "cyl"? ( //r1, r2, l, shift - let( - rr1=geom[1], rr2=geom[2], l=geom[3], shift=point2d(geom[4]), - r1 = is_num(rr1)? [rr1,rr1] : rr1, - r2 = is_num(rr2)? [rr2,rr2] : rr2, - u = (anchor.z+1)/2, - axy = unit(point2d(anchor)), - bot = point3d(vmul(r1,axy), -l/2), - top = point3d(vmul(r2,axy)+shift, l/2), - pos = lerp(bot,top,u)+offset, - sidevec = rot(from=UP, to=top-bot, p=point3d(axy)), - vvec = unit([0,0,anchor.z]), - vec = anchor==CENTER? UP : - approx(axy,[0,0])? unit(anchor) : - approx(anchor.z,0)? sidevec : - unit((sidevec+vvec)/2) - ) [anchor, pos, vec, oang] - ) : type == "spheroid"? ( //r - let( - rr = geom[1], - r = is_num(rr)? [rr,rr,rr] : rr, - anchor = unit(point3d(anchor)) - ) [anchor, vmul(r,anchor)+offset, unit(vmul(r,anchor)), oang] - ) : type == "vnf_isect"? ( //vnf - let( - vnf=geom[1], - eps = 1/2048, - rpts = rot(from=anchor, to=RIGHT, p=vnf[0]), - hits = [ - for (i = idx(vnf[1])) let( - face = vnf[1][i], - verts = select(rpts, face) - ) if ( - max(subindex(verts,0)) >= -eps && - max(subindex(verts,1)) >= -eps && - max(subindex(verts,2)) >= -eps && - min(subindex(verts,1)) <= eps && - min(subindex(verts,2)) <= eps - ) let( - pt = polygon_line_intersection( - select(vnf[0], face), - [CENTER,anchor], eps=eps - ) - ) if (!is_undef(pt)) [norm(pt),i,pt] - ] - ) - assert(len(hits)>0, "Anchor vector does not intersect with the shape. Attachment failed.") - let( - furthest = max_index(subindex(hits,0)), - pos = hits[furthest][2], - dist = hits[furthest][0], - nfaces = [for (hit = hits) if(approx(hit[0],dist,eps=eps)) hit[1]], - n = unit( - sum([ - for (i = nfaces) let( - faceverts = select(vnf[0],vnf[1][i]), - faceplane = plane_from_points(faceverts), - nrm = plane_normal(faceplane) - ) nrm - ]) / len(nfaces) - ) - ) - [anchor, pos, n, oang] - ) : type == "vnf_extent"? ( //vnf - let( - vnf=geom[1], - rpts = rot(from=anchor, to=RIGHT, p=vnf[0]), - maxx = max(subindex(rpts,0)), - idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], - mm = pointlist_bounds(select(rpts,idxs)), - avgy = (mm[0].y+mm[1].y)/2, - avgz = (mm[0].z+mm[1].z)/2, - mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : [maxx, avgy, avgz], - pos = rot(from=RIGHT, to=anchor, p=mpt) - ) [anchor, pos, anchor, oang] - ) : type == "rect"? ( //size, size2 - let( - size=geom[1], size2=geom[2], - u = (anchor.y+1)/2, - frpt = [size.x/2*anchor.x, -size.y/2], - bkpt = [size2/2*anchor.x, size.y/2], - pos = lerp(frpt, bkpt, u), - vec = unit(rot(from=BACK, to=bkpt-frpt, p=anchor)) - ) [anchor, pos, vec, 0] - ) : type == "circle"? ( //r - let( - rr = geom[1], - r = is_num(rr)? [rr,rr] : rr, - anchor = unit(point2d(anchor)) - ) [anchor, vmul(r,anchor)+offset, unit(vmul([r.y,r.x],anchor)), 0] - ) : type == "path_isect"? ( //path - let( - path=geom[1], - anchor = point2d(anchor), - isects = [ - for (t=triplet_wrap(path)) let( - seg1 = [t[0],t[1]], - seg2 = [t[1],t[2]], - isect = ray_segment_intersection([[0,0],anchor], seg1), - n = is_undef(isect)? [0,1] : - !approx(isect, t[1])? line_normal(seg1) : - unit((line_normal(seg1)+line_normal(seg2))/2), - n2 = vector_angle(anchor,n)>90? -n : n - ) - if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2] - ], - maxidx = max_index(subindex(isects,0)), - isect = isects[maxidx], - pos = isect[1], - vec = unit(isect[2]) - ) [anchor, pos, vec, 0] - ) : type == "path_extent"? ( //path - let( - path=geom[1], - anchor = point2d(anchor), - rpath = rot(from=anchor, to=RIGHT, p=path), - maxx = max(subindex(rpath,0)), - idxs = [for (i = idx(rpath)) if (approx(rpath[i].x, maxx)) i], - miny = min([for (i=idxs) rpath[i].y]), - maxy = max([for (i=idxs) rpath[i].y]), - avgy = (miny+maxy)/2, - pos = rot(from=RIGHT, to=anchor, p=[maxx,avgy]) - ) [anchor, pos, anchor, 0] - ) : - assert(false, "Unknown attachment geometry type."); + let( + offset = anchor==CENTER? CENTER : select(geom,-2), + anchors = select(geom,-1), + type = geom[0] + ) + is_string(anchor)? ( + let(found = search([anchor], anchors, num_returns_per_match=1)[0]) + assert(found!=[], str("Unknown anchor: ",anchor)) + anchors[found] + ) : + assert(is_vector(anchor),str("anchor=",anchor)) + let(anchor = point3d(anchor)) + anchor==CENTER? [anchor, CENTER, UP, 0] : + let( + oang = ( + approx(point2d(anchor), [0,0])? 0 : + atan2(anchor.y, anchor.x)+90 + ) + ) + type == "cuboid"? ( //size, size2, shift + let( + size=geom[1], size2=geom[2], shift=point2d(geom[3]), + h = size.z, + u = (anchor.z+1)/2, + axy = point2d(anchor), + bot = point3d(vmul(point2d(size)/2,axy),-h/2), + top = point3d(vmul(point2d(size2)/2,axy)+shift,h/2), + pos = lerp(bot,top,u)+offset, + sidevec = unit(rot(from=UP, to=top-bot, p=point3d(axy))), + vvec = unit([0,0,anchor.z]), + vec = anchor==CENTER? UP : + approx(axy,[0,0])? unit(anchor) : + approx(anchor.z,0)? sidevec : + unit((sidevec+vvec)/2) + ) [anchor, pos, vec, oang] + ) : type == "cyl"? ( //r1, r2, l, shift + let( + rr1=geom[1], rr2=geom[2], l=geom[3], shift=point2d(geom[4]), + r1 = is_num(rr1)? [rr1,rr1] : rr1, + r2 = is_num(rr2)? [rr2,rr2] : rr2, + u = (anchor.z+1)/2, + axy = unit(point2d(anchor)), + bot = point3d(vmul(r1,axy), -l/2), + top = point3d(vmul(r2,axy)+shift, l/2), + pos = lerp(bot,top,u)+offset, + sidevec = rot(from=UP, to=top-bot, p=point3d(axy)), + vvec = unit([0,0,anchor.z]), + vec = anchor==CENTER? UP : + approx(axy,[0,0])? unit(anchor) : + approx(anchor.z,0)? sidevec : + unit((sidevec+vvec)/2) + ) [anchor, pos, vec, oang] + ) : type == "spheroid"? ( //r + let( + rr = geom[1], + r = is_num(rr)? [rr,rr,rr] : rr, + anchor = unit(point3d(anchor)) + ) [anchor, vmul(r,anchor)+offset, unit(vmul(r,anchor)), oang] + ) : type == "vnf_isect"? ( //vnf + let( + vnf=geom[1], + eps = 1/2048, + rpts = rot(from=anchor, to=RIGHT, p=vnf[0]), + hits = [ + for (i = idx(vnf[1])) let( + face = vnf[1][i], + verts = select(rpts, face) + ) if ( + max(subindex(verts,0)) >= -eps && + max(subindex(verts,1)) >= -eps && + max(subindex(verts,2)) >= -eps && + min(subindex(verts,1)) <= eps && + min(subindex(verts,2)) <= eps + ) let( + pt = polygon_line_intersection( + select(vnf[0], face), + [CENTER,anchor], eps=eps + ) + ) if (!is_undef(pt)) [norm(pt),i,pt] + ] + ) + assert(len(hits)>0, "Anchor vector does not intersect with the shape. Attachment failed.") + let( + furthest = max_index(subindex(hits,0)), + pos = hits[furthest][2], + dist = hits[furthest][0], + nfaces = [for (hit = hits) if(approx(hit[0],dist,eps=eps)) hit[1]], + n = unit( + sum([ + for (i = nfaces) let( + faceverts = select(vnf[0],vnf[1][i]), + faceplane = plane_from_points(faceverts), + nrm = plane_normal(faceplane) + ) nrm + ]) / len(nfaces) + ) + ) + [anchor, pos, n, oang] + ) : type == "vnf_extent"? ( //vnf + let( + vnf=geom[1], + rpts = rot(from=anchor, to=RIGHT, p=vnf[0]), + maxx = max(subindex(rpts,0)), + idxs = [for (i = idx(rpts)) if (approx(rpts[i].x, maxx)) i], + mm = pointlist_bounds(select(rpts,idxs)), + avgy = (mm[0].y+mm[1].y)/2, + avgz = (mm[0].z+mm[1].z)/2, + mpt = approx(point2d(anchor),[0,0])? [maxx,0,0] : [maxx, avgy, avgz], + pos = rot(from=RIGHT, to=anchor, p=mpt) + ) [anchor, pos, anchor, oang] + ) : type == "rect"? ( //size, size2 + let( + size=geom[1], size2=geom[2], + u = (anchor.y+1)/2, + frpt = [size.x/2*anchor.x, -size.y/2], + bkpt = [size2/2*anchor.x, size.y/2], + pos = lerp(frpt, bkpt, u), + vec = unit(rot(from=BACK, to=bkpt-frpt, p=anchor)) + ) [anchor, pos, vec, 0] + ) : type == "circle"? ( //r + let( + rr = geom[1], + r = is_num(rr)? [rr,rr] : rr, + anchor = unit(point2d(anchor)) + ) [anchor, vmul(r,anchor)+offset, unit(vmul([r.y,r.x],anchor)), 0] + ) : type == "path_isect"? ( //path + let( + path=geom[1], + anchor = point2d(anchor), + isects = [ + for (t=triplet_wrap(path)) let( + seg1 = [t[0],t[1]], + seg2 = [t[1],t[2]], + isect = ray_segment_intersection([[0,0],anchor], seg1), + n = is_undef(isect)? [0,1] : + !approx(isect, t[1])? line_normal(seg1) : + unit((line_normal(seg1)+line_normal(seg2))/2), + n2 = vector_angle(anchor,n)>90? -n : n + ) + if(!is_undef(isect) && !approx(isect,t[0])) [norm(isect), isect, n2] + ], + maxidx = max_index(subindex(isects,0)), + isect = isects[maxidx], + pos = isect[1], + vec = unit(isect[2]) + ) [anchor, pos, vec, 0] + ) : type == "path_extent"? ( //path + let( + path=geom[1], + anchor = point2d(anchor), + rpath = rot(from=anchor, to=RIGHT, p=path), + maxx = max(subindex(rpath,0)), + idxs = [for (i = idx(rpath)) if (approx(rpath[i].x, maxx)) i], + miny = min([for (i=idxs) rpath[i].y]), + maxy = max([for (i=idxs) rpath[i].y]), + avgy = (miny+maxy)/2, + pos = rot(from=RIGHT, to=anchor, p=[maxx,avgy]) + ) [anchor, pos, anchor, 0] + ) : + assert(false, "Unknown attachment geometry type."); // Function: attachment_is_shown() @@ -555,13 +555,13 @@ function find_anchor(anchor, geom) = // Description: // Returns true if the given space-delimited string of tag names should currently be shown. function attachment_is_shown(tags) = - assert(!is_undef($tags_shown)) - assert(!is_undef($tags_hidden)) - let( - tags = str_split(tags, " "), - shown = !$tags_shown || any([for (tag=tags) in_list(tag, $tags_shown)]), - hidden = any([for (tag=tags) in_list(tag, $tags_hidden)]) - ) shown && !hidden; + assert(!is_undef($tags_shown)) + assert(!is_undef($tags_hidden)) + let( + tags = str_split(tags, " "), + shown = !$tags_shown || any([for (tag=tags) in_list(tag, $tags_shown)]), + hidden = any([for (tag=tags) in_list(tag, $tags_hidden)]) + ) shown && !hidden; // Function: reorient() @@ -620,27 +620,27 @@ function attachment_is_shown(tags) = // two_d = If true, the attachable shape is 2D. If false, 3D. Default: false (3D) // p = The VNF, path, or point to transform. function reorient( - anchor=CENTER, - spin=0, - orient=UP, - size, size2, shift, - r,r1,r2, d,d1,d2, l,h, - vnf, path, - extent=true, - offset=[0,0,0], - anchors=[], - two_d=false, - p=undef + anchor=CENTER, + spin=0, + orient=UP, + size, size2, shift, + r,r1,r2, d,d1,d2, l,h, + vnf, path, + extent=true, + offset=[0,0,0], + anchors=[], + two_d=false, + p=undef ) = (anchor==CENTER && spin==0 && orient==UP && p!=undef)? p : let( - geom = attach_geom( - size=size, size2=size2, shift=shift, - r=r, r1=r1, r2=r2, h=h, - d=d, d1=d1, d2=d2, l=l, - vnf=vnf, path=path, extent=extent, - offset=offset, anchors=anchors, - two_d=two_d - ), - $attach_to = undef + geom = attach_geom( + size=size, size2=size2, shift=shift, + r=r, r1=r1, r2=r2, h=h, + d=d, d1=d1, d2=d2, l=l, + vnf=vnf, path=path, extent=extent, + offset=offset, anchors=anchors, + two_d=two_d + ), + $attach_to = undef ) attach_transform(anchor,spin,orient,geom,p); @@ -784,47 +784,47 @@ function reorient( // children(); // } module attachable( - anchor=CENTER, - spin=0, - orient=UP, - size, size2, shift, - r,r1,r2, d,d1,d2, l,h, - vnf, path, - extent=true, - offset=[0,0,0], - anchors=[], - two_d=false + anchor=CENTER, + spin=0, + orient=UP, + size, size2, shift, + r,r1,r2, d,d1,d2, l,h, + vnf, path, + extent=true, + offset=[0,0,0], + anchors=[], + two_d=false ) { - assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates."); - assert(!is_undef(anchor), str("anchor undefined in attachable(). Did you forget to set a default value for anchor in ", parent_module(1))); - assert(!is_undef(spin), str("spin undefined in attachable(). Did you forget to set a default value for spin in ", parent_module(1))); - assert(!is_undef(orient), str("orient undefined in attachable(). Did you forget to set a default value for orient in ", parent_module(1))); - geom = attach_geom( - size=size, size2=size2, shift=shift, - r=r, r1=r1, r2=r2, h=h, - d=d, d1=d1, d2=d2, l=l, - vnf=vnf, path=path, extent=extent, - offset=offset, anchors=anchors, - two_d=two_d - ); - m = attach_transform(anchor,spin,orient,geom); - multmatrix(m) { - $parent_anchor = anchor; - $parent_spin = spin; - $parent_orient = orient; - $parent_geom = geom; - $parent_size = attach_geom_size(geom); - $attach_to = undef; - if (attachment_is_shown($tags)) { - if (is_undef($color)) { - children(0); - } else color($color) { - $color = undef; - children(0); - } - } - children(1); - } + assert($children==2, "attachable() expects exactly two children; the shape to manage, and the union of all attachment candidates."); + assert(!is_undef(anchor), str("anchor undefined in attachable(). Did you forget to set a default value for anchor in ", parent_module(1))); + assert(!is_undef(spin), str("spin undefined in attachable(). Did you forget to set a default value for spin in ", parent_module(1))); + assert(!is_undef(orient), str("orient undefined in attachable(). Did you forget to set a default value for orient in ", parent_module(1))); + geom = attach_geom( + size=size, size2=size2, shift=shift, + r=r, r1=r1, r2=r2, h=h, + d=d, d1=d1, d2=d2, l=l, + vnf=vnf, path=path, extent=extent, + offset=offset, anchors=anchors, + two_d=two_d + ); + m = attach_transform(anchor,spin,orient,geom); + multmatrix(m) { + $parent_anchor = anchor; + $parent_spin = spin; + $parent_orient = orient; + $parent_geom = geom; + $parent_size = attach_geom_size(geom); + $attach_to = undef; + if (attachment_is_shown($tags)) { + if (is_undef($color)) { + children(0); + } else color($color) { + $color = undef; + children(0); + } + } + children(1); + } } @@ -846,15 +846,15 @@ module attachable( // } module position(from) { - assert($parent_geom != undef, "No object to attach to!"); - anchors = (is_vector(from)||is_string(from))? [from] : from; - for (anchr = anchors) { - anch = find_anchor(anchr, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - translate(anch[1]) children(); - } + assert($parent_geom != undef, "No object to attach to!"); + anchors = (is_vector(from)||is_string(from))? [from] : from; + for (anchr = anchors) { + anch = find_anchor(anchr, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + translate(anch[1]) children(); + } } @@ -882,22 +882,22 @@ module position(from) // } module attach(from, to=undef, overlap=undef, norot=false) { - assert($parent_geom != undef, "No object to attach to!"); - overlap = (overlap!=undef)? overlap : $overlap; - anchors = (is_vector(from)||is_string(from))? [from] : from; - for (anchr = anchors) { - anch = find_anchor(anchr, $parent_geom); - two_d = attach_geom_2d($parent_geom); - $attach_to = to; - $attach_anchor = anch; - $attach_norot = norot; - if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) { - translate(anch[1]) translate([0,0,-overlap]) children(); - } else { - fromvec = two_d? BACK : UP; - translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate([0,0,-overlap]) children(); - } - } + assert($parent_geom != undef, "No object to attach to!"); + overlap = (overlap!=undef)? overlap : $overlap; + anchors = (is_vector(from)||is_string(from))? [from] : from; + for (anchr = anchors) { + anch = find_anchor(anchr, $parent_geom); + two_d = attach_geom_2d($parent_geom); + $attach_to = to; + $attach_anchor = anch; + $attach_norot = norot; + if (norot || (norm(anch[2]-UP)<1e-9 && anch[3]==0)) { + translate(anch[1]) translate([0,0,-overlap]) children(); + } else { + fromvec = two_d? BACK : UP; + translate(anch[1]) rot(anch[3],from=fromvec,to=anch[2]) translate([0,0,-overlap]) children(); + } + } } @@ -912,12 +912,12 @@ module attach(from, to=undef, overlap=undef, norot=false) // d = Diameter of corner mask. // convexity = Max number of times a line could intersect the perimeter of the mask shape. Default: 10 module face_profile(faces=[], r, d, convexity=10) { - faces = is_vector(faces)? [faces] : faces; - assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); - r = get_radius(r=r, d=d, dflt=undef); - assert(is_num(r) && r>0); - edge_profile(faces) children(); - corner_profile(faces, convexity=convexity, r=r) children(); + faces = is_vector(faces)? [faces] : faces; + assert(all([for (face=faces) is_vector(face) && sum([for (x=face) x!=0? 1 : 0])==1]), "Vector in faces doesn't point at a face."); + r = get_radius(r=r, d=d, dflt=undef); + assert(is_num(r) && r>0); + edge_profile(faces) children(); + corner_profile(faces, convexity=convexity, r=r) children(); } @@ -939,35 +939,35 @@ module face_profile(faces=[], r, d, convexity=10) { // edge_profile([TOP,"Z"],except=[BACK,TOP+LEFT]) // mask2d_roundover(r=10, inset=2); module edge_profile(edges=EDGES_ALL, except=[], convexity=10) { - assert($parent_geom != undef, "No object to attach to!"); - edges = edges(edges, except=except); - vecs = [ - for (i = [0:3], axis=[0:2]) - if (edges[axis][i]>0) - EDGE_OFFSETS[axis][i] - ]; - for (vec = vecs) { - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - assert(vcount == 2, "Not an edge vector!"); - anch = find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $tags = "mask"; - length = sum(vmul($parent_size, [for (x=vec) x?0:1]))+0.1; - rotang = - vec.z<0? [90,0,180+vang(point2d(vec))] : - vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) : - vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] : - [-90,0,180+vang(point2d(vec))]; - translate(anch[1]) { - rot(rotang) { - linear_extrude(height=length, center=true, convexity=convexity) { - children(); - } - } - } - } + assert($parent_geom != undef, "No object to attach to!"); + edges = edges(edges, except=except); + vecs = [ + for (i = [0:3], axis=[0:2]) + if (edges[axis][i]>0) + EDGE_OFFSETS[axis][i] + ]; + for (vec = vecs) { + vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); + assert(vcount == 2, "Not an edge vector!"); + anch = find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $tags = "mask"; + length = sum(vmul($parent_size, [for (x=vec) x?0:1]))+0.1; + rotang = + vec.z<0? [90,0,180+vang(point2d(vec))] : + vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) : + vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] : + [-90,0,180+vang(point2d(vec))]; + translate(anch[1]) { + rot(rotang) { + linear_extrude(height=length, center=true, convexity=convexity) { + children(); + } + } + } + } } // Module: corner_profile() @@ -988,45 +988,45 @@ module edge_profile(edges=EDGES_ALL, except=[], convexity=10) { // Example: // diff("mask") // cuboid([50,60,70],rounding=10,edges="Z",anchor=CENTER) { -// corner_profile(BOT,r=10) -// mask2d_teardrop(r=10, angle=40); +// corner_profile(BOT,r=10) +// mask2d_teardrop(r=10, angle=40); // } module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { - assert($parent_geom != undef, "No object to attach to!"); - r = get_radius(r=r, d=d, dflt=undef); - assert(is_num(r)); - corners = corners(corners, except=except); - vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; - for (vec = vecs) { - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - assert(vcount == 3, "Not an edge vector!"); - anch = find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $tags = "mask"; - rotang = vec.z<0? - [ 0,0,180+vang(point2d(vec))-45] : - [180,0,-90+vang(point2d(vec))-45]; - translate(anch[1]) { - rot(rotang) { - render(convexity=convexity) - difference() { - translate(-0.1*[1,1,1]) cube(r+0.1, center=false); - right(r) back(r) zrot(180) { - rotate_extrude(angle=90, convexity=convexity) { - xflip() left(r) { - difference() { - square(r,center=false); - children(); - } - } - } - } - } - } - } - } + assert($parent_geom != undef, "No object to attach to!"); + r = get_radius(r=r, d=d, dflt=undef); + assert(is_num(r)); + corners = corners(corners, except=except); + vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; + for (vec = vecs) { + vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); + assert(vcount == 3, "Not an edge vector!"); + anch = find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $tags = "mask"; + rotang = vec.z<0? + [ 0,0,180+vang(point2d(vec))-45] : + [180,0,-90+vang(point2d(vec))-45]; + translate(anch[1]) { + rot(rotang) { + render(convexity=convexity) + difference() { + translate(-0.1*[1,1,1]) cube(r+0.1, center=false); + right(r) back(r) zrot(180) { + rotate_extrude(angle=90, convexity=convexity) { + xflip() left(r) { + difference() { + square(r,center=false); + children(); + } + } + } + } + } + } + } + } } @@ -1049,28 +1049,28 @@ module corner_profile(corners=CORNERS_ALL, except=[], r, d, convexity=10) { // edge_mask([TOP,"Z"],except=[BACK,TOP+LEFT]) // rounding_mask_z(l=71,r=10); module edge_mask(edges=EDGES_ALL, except=[]) { - assert($parent_geom != undef, "No object to attach to!"); - edges = edges(edges, except=except); - vecs = [ - for (i = [0:3], axis=[0:2]) - if (edges[axis][i]>0) - EDGE_OFFSETS[axis][i] - ]; - for (vec = vecs) { - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - assert(vcount == 2, "Not an edge vector!"); - anch = find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $tags = "mask"; - rotang = - vec.z<0? [90,0,180+vang(point2d(vec))] : - vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) : - vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] : - [-90,0,180+vang(point2d(vec))]; - translate(anch[1]) rot(rotang) children(); - } + assert($parent_geom != undef, "No object to attach to!"); + edges = edges(edges, except=except); + vecs = [ + for (i = [0:3], axis=[0:2]) + if (edges[axis][i]>0) + EDGE_OFFSETS[axis][i] + ]; + for (vec = vecs) { + vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); + assert(vcount == 2, "Not an edge vector!"); + anch = find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $tags = "mask"; + rotang = + vec.z<0? [90,0,180+vang(point2d(vec))] : + vec.z==0 && sign(vec.x)==sign(vec.y)? 135+vang(point2d(vec)) : + vec.z==0 && sign(vec.x)!=sign(vec.y)? [0,180,45+vang(point2d(vec))] : + [-90,0,180+vang(point2d(vec))]; + translate(anch[1]) rot(rotang) children(); + } } @@ -1095,22 +1095,22 @@ module edge_mask(edges=EDGES_ALL, except=[]) { // translate([20,20,20]) sphere(r=20); // } module corner_mask(corners=CORNERS_ALL, except=[]) { - assert($parent_geom != undef, "No object to attach to!"); - corners = corners(corners, except=except); - vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; - for (vec = vecs) { - vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); - assert(vcount == 3, "Not an edge vector!"); - anch = find_anchor(vec, $parent_geom); - $attach_to = undef; - $attach_anchor = anch; - $attach_norot = true; - $tags = "mask"; - rotang = vec.z<0? - [ 0,0,180+vang(point2d(vec))-45] : - [180,0,-90+vang(point2d(vec))-45]; - translate(anch[1]) rot(rotang) children(); - } + assert($parent_geom != undef, "No object to attach to!"); + corners = corners(corners, except=except); + vecs = [for (i = [0:7]) if (corners[i]>0) CORNER_OFFSETS[i]]; + for (vec = vecs) { + vcount = (vec.x?1:0) + (vec.y?1:0) + (vec.z?1:0); + assert(vcount == 3, "Not an edge vector!"); + anch = find_anchor(vec, $parent_geom); + $attach_to = undef; + $attach_anchor = anch; + $attach_norot = true; + $tags = "mask"; + rotang = vec.z<0? + [ 0,0,180+vang(point2d(vec))-45] : + [180,0,-90+vang(point2d(vec))-45]; + translate(anch[1]) rot(rotang) children(); + } } @@ -1123,8 +1123,8 @@ module corner_mask(corners=CORNERS_ALL, except=[]) { // tags = String containing space delimited set of tags to apply. module tags(tags) { - $tags = tags; - children(); + $tags = tags; + children(); } @@ -1139,8 +1139,8 @@ module tags(tags) // recolor("red") cyl(l=20, d=10); module recolor(c) { - $color = c; - children(); + $color = c; + children(); } @@ -1156,8 +1156,8 @@ module recolor(c) // } module hide(tags="") { - $tags_hidden = tags==""? [] : str_split(tags, " "); - children(); + $tags_hidden = tags==""? [] : str_split(tags, " "); + children(); } @@ -1173,8 +1173,8 @@ module hide(tags="") // } module show(tags="") { - $tags_shown = tags==""? [] : str_split(tags, " "); - children(); + $tags_shown = tags==""? [] : str_split(tags, " "); + children(); } @@ -1214,23 +1214,23 @@ module show(tags="") // } module diff(neg, pos=undef, keep=undef) { - difference() { - if (pos != undef) { - show(pos) children(); - } else { - if (keep == undef) { - hide(neg) children(); - } else { - hide(str(neg," ",keep)) children(); - } - } - show(neg) children(); - } - if (keep!=undef) { - show(keep) children(); - } else if (pos!=undef) { - hide(str(pos," ",neg)) children(); - } + difference() { + if (pos != undef) { + show(pos) children(); + } else { + if (keep == undef) { + hide(neg) children(); + } else { + hide(str(neg," ",keep)) children(); + } + } + show(neg) children(); + } + if (keep!=undef) { + show(keep) children(); + } else if (pos!=undef) { + hide(str(pos," ",neg)) children(); + } } @@ -1259,23 +1259,23 @@ module diff(neg, pos=undef, keep=undef) // } module intersect(a, b=undef, keep=undef) { - intersection() { - if (b != undef) { - show(b) children(); - } else { - if (keep == undef) { - hide(a) children(); - } else { - hide(str(a," ",keep)) children(); - } - } - show(a) children(); - } - if (keep!=undef) { - show(keep) children(); - } else if (b!=undef) { - hide(str(a," ",b)) children(); - } + intersection() { + if (b != undef) { + show(b) children(); + } else { + if (keep == undef) { + hide(a) children(); + } else { + hide(str(a," ",keep)) children(); + } + } + show(a) children(); + } + if (keep!=undef) { + show(keep) children(); + } else if (b!=undef) { + hide(str(a," ",b)) children(); + } } @@ -1299,10 +1299,10 @@ module intersect(a, b=undef, keep=undef) // } module hulling(a) { - hull() show(a) children(); - children(); + hull() show(a) children(); + children(); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/beziers.scad b/beziers.scad index 3953ef4..68c59fc 100644 --- a/beziers.scad +++ b/beziers.scad @@ -81,36 +81,36 @@ include // lookup table or hard coded powers list the code is about twice as fast as the recursive method. // Note that everything I tried to simplify or tidy this code made is slower, sometimes a lot slower. function bezier_points(curve, u) = - is_num(u) ? bezier_points(curve,[u])[0] : - let( - N = len(curve)-1, - M = _bezier_matrix(N)*curve - ) - N==0 ? [for(uval=u)[1]*M] : - N==1 ? [for(uval=u)[1, uval]*M] : - N==2 ? [for(uval=u)[1, uval, uval*uval]*M] : - N==3 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval]*M] : // It appears that pow() is as fast or faster for powers 5 or above - N==4 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval]*M] : - N==5 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5)]*M] : - N==6 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6)]*M] : - N==7 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7)]*M] : - N==8 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8)]*M] : - N==9 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9)]*M] : - N==10? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9), pow(uval,10)]*M] : - /* N>=11 */ [for(uval=u)[for (i=[0:1:N]) pow(uval,i)]*M]; + is_num(u) ? bezier_points(curve,[u])[0] : + let( + N = len(curve)-1, + M = _bezier_matrix(N)*curve + ) + N==0 ? [for(uval=u)[1]*M] : + N==1 ? [for(uval=u)[1, uval]*M] : + N==2 ? [for(uval=u)[1, uval, uval*uval]*M] : + N==3 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval]*M] : // It appears that pow() is as fast or faster for powers 5 or above + N==4 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval]*M] : + N==5 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5)]*M] : + N==6 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6)]*M] : + N==7 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7)]*M] : + N==8 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8)]*M] : + N==9 ? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9)]*M] : + N==10? [for(uval=u)[1, uval, uval*uval, uval*uval*uval, uval*uval*uval*uval, pow(uval,5),pow(uval,6), pow(uval,7), pow(uval,8), pow(uval,9), pow(uval,10)]*M] : + /* N>=11 */ [for(uval=u)[for (i=[0:1:N]) pow(uval,i)]*M]; // Not public. function _signed_pascals_triangle(N,tri=[[-1]]) = - len(tri)==N+1 ? tri : - let(last=tri[len(tri)-1]) - _signed_pascals_triangle(N,concat(tri,[[-1, for(i=[0:1:len(tri)-2]) (i%2==1?-1:1)*(abs(last[i])+abs(last[i+1])),len(last)%2==0? -1:1]])); + len(tri)==N+1 ? tri : + let(last=tri[len(tri)-1]) + _signed_pascals_triangle(N,concat(tri,[[-1, for(i=[0:1:len(tri)-2]) (i%2==1?-1:1)*(abs(last[i])+abs(last[i+1])),len(last)%2==0? -1:1]])); // Not public. function _compute_bezier_matrix(N) = - let(tri = _signed_pascals_triangle(N)) - [for(i=[0:N]) concat(tri[N][i]*tri[i], repeat(0,N-i))]; + let(tri = _signed_pascals_triangle(N)) + [for(i=[0:N]) concat(tri[N][i]*tri[i], repeat(0,N-i))]; // The bezier matrix, which is related to Pascal's triangle, enables nonrecursive computation @@ -119,66 +119,66 @@ function _compute_bezier_matrix(N) = // Not public. _bezier_matrix_table = [ - [[1]], - [[ 1, 0], - [-1, 1]], - [[1, 0, 0], - [-2, 2, 0], - [1, -2, 1]], - [[ 1, 0, 0, 0], - [-3, 3, 0, 0], - [ 3,-6, 3, 0], - [-1, 3,-3, 1]], - [[ 1, 0, 0, 0, 0], - [-4, 4, 0, 0, 0], - [ 6,-12, 6, 0, 0], - [-4, 12,-12, 4, 0], - [ 1, -4, 6,-4, 1]], - [[ 1, 0, 0, 0, 0, 0], - [ -5, 5, 0, 0, 0, 0], - [ 10,-20, 10, 0, 0, 0], - [-10, 30,-30, 10, 0, 0], - [ 5,-20, 30,-20, 5, 0], - [ -1, 5,-10, 10,-5, 1]], - [[ 1, 0, 0, 0, 0, 0, 0], - [ -6, 6, 0, 0, 0, 0, 0], - [ 15,-30, 15, 0, 0, 0, 0], - [-20, 60,-60, 20, 0, 0, 0], - [ 15,-60, 90,-60, 15, 0, 0], - [ -6, 30,-60, 60,-30, 6, 0], - [ 1, -6, 15,-20, 15,-6, 1]], - [[ 1, 0, 0, 0, 0, 0, 0, 0], - [ -7, 7, 0, 0, 0, 0, 0, 0], - [ 21, -42, 21, 0, 0, 0, 0, 0], - [-35, 105,-105, 35, 0, 0, 0, 0], - [ 35,-140, 210,-140, 35, 0, 0, 0], - [-21, 105,-210, 210,-105, 21, 0, 0], - [ 7, -42, 105,-140, 105,-42, 7, 0], - [ -1, 7, -21, 35, -35, 21,-7, 1]], - [[ 1, 0, 0, 0, 0, 0, 0, 0, 0], - [ -8, 8, 0, 0, 0, 0, 0, 0, 0], - [ 28, -56, 28, 0, 0, 0, 0, 0, 0], - [-56, 168,-168, 56, 0, 0, 0, 0, 0], - [ 70,-280, 420,-280, 70, 0, 0, 0, 0], - [-56, 280,-560, 560,-280, 56, 0, 0, 0], - [ 28,-168, 420,-560, 420,-168, 28, 0, 0], - [ -8, 56,-168, 280,-280, 168,-56, 8, 0], - [ 1, -8, 28, -56, 70, -56, 28,-8, 1]], - [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-9, 9, 0, 0, 0, 0, 0, 0, 0, 0], [36, -72, 36, 0, 0, 0, 0, 0, 0, 0], [-84, 252, -252, 84, 0, 0, 0, 0, 0, 0], - [126, -504, 756, -504, 126, 0, 0, 0, 0, 0], [-126, 630, -1260, 1260, -630, 126, 0, 0, 0, 0], [84, -504, 1260, -1680, 1260, -504, 84, 0, 0, 0], - [-36, 252, -756, 1260, -1260, 756, -252, 36, 0, 0], [9, -72, 252, -504, 630, -504, 252, -72, 9, 0], [-1, 9, -36, 84, -126, 126, -84, 36, -9, 1]], - [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], [45, -90, 45, 0, 0, 0, 0, 0, 0, 0, 0], [-120, 360, -360, 120, 0, 0, 0, 0, 0, 0, 0], - [210, -840, 1260, -840, 210, 0, 0, 0, 0, 0, 0], [-252, 1260, -2520, 2520, -1260, 252, 0, 0, 0, 0, 0], - [210, -1260, 3150, -4200, 3150, -1260, 210, 0, 0, 0, 0], [-120, 840, -2520, 4200, -4200, 2520, -840, 120, 0, 0, 0], - [45, -360, 1260, -2520, 3150, -2520, 1260, -360, 45, 0, 0], [-10, 90, -360, 840, -1260, 1260, -840, 360, -90, 10, 0], - [1, -10, 45, -120, 210, -252, 210, -120, 45, -10, 1]] + [[1]], + [[ 1, 0], + [-1, 1]], + [[1, 0, 0], + [-2, 2, 0], + [1, -2, 1]], + [[ 1, 0, 0, 0], + [-3, 3, 0, 0], + [ 3,-6, 3, 0], + [-1, 3,-3, 1]], + [[ 1, 0, 0, 0, 0], + [-4, 4, 0, 0, 0], + [ 6,-12, 6, 0, 0], + [-4, 12,-12, 4, 0], + [ 1, -4, 6,-4, 1]], + [[ 1, 0, 0, 0, 0, 0], + [ -5, 5, 0, 0, 0, 0], + [ 10,-20, 10, 0, 0, 0], + [-10, 30,-30, 10, 0, 0], + [ 5,-20, 30,-20, 5, 0], + [ -1, 5,-10, 10,-5, 1]], + [[ 1, 0, 0, 0, 0, 0, 0], + [ -6, 6, 0, 0, 0, 0, 0], + [ 15,-30, 15, 0, 0, 0, 0], + [-20, 60,-60, 20, 0, 0, 0], + [ 15,-60, 90,-60, 15, 0, 0], + [ -6, 30,-60, 60,-30, 6, 0], + [ 1, -6, 15,-20, 15,-6, 1]], + [[ 1, 0, 0, 0, 0, 0, 0, 0], + [ -7, 7, 0, 0, 0, 0, 0, 0], + [ 21, -42, 21, 0, 0, 0, 0, 0], + [-35, 105,-105, 35, 0, 0, 0, 0], + [ 35,-140, 210,-140, 35, 0, 0, 0], + [-21, 105,-210, 210,-105, 21, 0, 0], + [ 7, -42, 105,-140, 105,-42, 7, 0], + [ -1, 7, -21, 35, -35, 21,-7, 1]], + [[ 1, 0, 0, 0, 0, 0, 0, 0, 0], + [ -8, 8, 0, 0, 0, 0, 0, 0, 0], + [ 28, -56, 28, 0, 0, 0, 0, 0, 0], + [-56, 168,-168, 56, 0, 0, 0, 0, 0], + [ 70,-280, 420,-280, 70, 0, 0, 0, 0], + [-56, 280,-560, 560,-280, 56, 0, 0, 0], + [ 28,-168, 420,-560, 420,-168, 28, 0, 0], + [ -8, 56,-168, 280,-280, 168,-56, 8, 0], + [ 1, -8, 28, -56, 70, -56, 28,-8, 1]], + [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-9, 9, 0, 0, 0, 0, 0, 0, 0, 0], [36, -72, 36, 0, 0, 0, 0, 0, 0, 0], [-84, 252, -252, 84, 0, 0, 0, 0, 0, 0], + [126, -504, 756, -504, 126, 0, 0, 0, 0, 0], [-126, 630, -1260, 1260, -630, 126, 0, 0, 0, 0], [84, -504, 1260, -1680, 1260, -504, 84, 0, 0, 0], + [-36, 252, -756, 1260, -1260, 756, -252, 36, 0, 0], [9, -72, 252, -504, 630, -504, 252, -72, 9, 0], [-1, 9, -36, 84, -126, 126, -84, 36, -9, 1]], + [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [-10, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0], [45, -90, 45, 0, 0, 0, 0, 0, 0, 0, 0], [-120, 360, -360, 120, 0, 0, 0, 0, 0, 0, 0], + [210, -840, 1260, -840, 210, 0, 0, 0, 0, 0, 0], [-252, 1260, -2520, 2520, -1260, 252, 0, 0, 0, 0, 0], + [210, -1260, 3150, -4200, 3150, -1260, 210, 0, 0, 0, 0], [-120, 840, -2520, 4200, -4200, 2520, -840, 120, 0, 0, 0], + [45, -360, 1260, -2520, 3150, -2520, 1260, -360, 45, 0, 0], [-10, 90, -360, 840, -1260, 1260, -840, 360, -90, 10, 0], + [1, -10, 45, -120, 210, -252, 210, -120, 45, -10, 1]] ]; // Not public. function _bezier_matrix(N) = - N>10 ? _compute_bezier_matrix(N) : - _bezier_matrix_table[N]; + N>10 ? _compute_bezier_matrix(N) : + _bezier_matrix_table[N]; // Function: bezier_derivative() @@ -192,12 +192,12 @@ function _bezier_matrix(N) = // u = The proportion of the way along the curve to find the derivative of. 0<=`u`<=1 If given as a list or range, returns a list of derivatives, one for each u value. // order = The order of the derivative to return. Default: 1 (for the first derivative) function bezier_derivative(curve, u, order=1) = - assert(is_int(order) && order>=0) - order==0? bezier_points(curve, u) : let( - N = len(curve) - 1, - dpts = N * deltas(curve) - ) order==1? bezier_points(dpts, u) : - bezier_derivative(dpts, u, order-1); + assert(is_int(order) && order>=0) + order==0? bezier_points(curve, u) : let( + N = len(curve) - 1, + dpts = N * deltas(curve) + ) order==1? bezier_points(dpts, u) : + bezier_derivative(dpts, u, order-1); @@ -210,10 +210,10 @@ function bezier_derivative(curve, u, order=1) = // curve = The list of endpoints and control points for this bezier segment. // u = The proportion of the way along the curve to find the tangent vector of. 0<=`u`<=1 If given as a list or range, returns a list of tangent vectors, one for each u value. function bezier_tangent(curve, u) = - let( - res = bezier_derivative(curve, u) - ) is_vector(res)? unit(res) : - [for (v=res) unit(v)]; + let( + res = bezier_derivative(curve, u) + ) is_vector(res)? unit(res) : + [for (v=res) unit(v)]; @@ -229,17 +229,17 @@ function bezier_tangent(curve, u) = // curve = The list of endpoints and control points for this bezier segment. // u = The proportion of the way along the curve to find the curvature of. 0<=`u`<=1 If given as a list or range, returns a list of curvature values, one for each u value. function bezier_curvature(curve, u) = - is_num(u) ? bezier_curvature(curve,[u])[0] : - let( - d1 = bezier_derivative(curve, u, 1), - d2 = bezier_derivative(curve, u, 2) - ) [ - for(i=idx(d1)) - sqrt( - sqr(norm(d1[i])*norm(d2[i])) - - sqr(d1[i]*d2[i]) - ) / pow(norm(d1[i]),3) - ]; + is_num(u) ? bezier_curvature(curve,[u])[0] : + let( + d1 = bezier_derivative(curve, u, 1), + d2 = bezier_derivative(curve, u, 2) + ) [ + for(i=idx(d1)) + sqrt( + sqr(norm(d1[i])*norm(d2[i])) - + sqr(d1[i]*d2[i]) + ) / pow(norm(d1[i]),3) + ]; @@ -289,32 +289,32 @@ function bezier_curve(curve,n) = bezier_points(curve, [0:1/n:(n-0.5)/n]); // color("red") translate(pt) sphere(r=1); // color("blue") translate(bezier_points(bez,u)) sphere(r=1); function bezier_segment_closest_point(curve, pt, max_err=0.01, u=0, end_u=1) = - let( - steps = len(curve)*3, - uvals = [u, for (i=[0:1:steps]) (end_u-u)*(i/steps)+u, end_u], - path = bezier_points(curve,uvals), - minima_ranges = [ - for (i = [1:1:len(uvals)-2]) let( - d1 = norm(path[i-1]-pt), - d2 = norm(path[i ]-pt), - d3 = norm(path[i+1]-pt) - ) if (d2<=d1 && d2<=d3) [uvals[i-1],uvals[i+1]] - ] - ) len(minima_ranges)>1? ( - let( - min_us = [ - for (minima = minima_ranges) - bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima.x, end_u=minima.y) - ], - dists = [for (v=min_us) norm(bezier_points(curve,v)-pt)], - min_i = min_index(dists) - ) min_us[min_i] - ) : let( - minima = minima_ranges[0], - pp = bezier_points(curve, minima), - err = norm(pp[1]-pp[0]) - ) err1? ( + let( + min_us = [ + for (minima = minima_ranges) + bezier_segment_closest_point(curve, pt, max_err=max_err, u=minima.x, end_u=minima.y) + ], + dists = [for (v=min_us) norm(bezier_points(curve,v)-pt)], + min_i = min_index(dists) + ) min_us[min_i] + ) : let( + minima = minima_ranges[0], + pp = bezier_points(curve, minima), + err = norm(pp[1]-pp[0]) + ) err= len(path))? ( - let(curve = select(path, min_seg*N, (min_seg+1)*N)) - [min_seg, bezier_segment_closest_point(curve, pt, max_err=max_err)] - ) : ( - let( - curve = select(path,seg*N,(seg+1)*N), - u = bezier_segment_closest_point(curve, pt, max_err=0.05), - dist = norm(bezier_points(curve, u)-pt), - mseg = (min_dist==undef || dist= len(path))? ( + let(curve = select(path, min_seg*N, (min_seg+1)*N)) + [min_seg, bezier_segment_closest_point(curve, pt, max_err=max_err)] + ) : ( + let( + curve = select(path,seg*N,(seg+1)*N), + u = bezier_segment_closest_point(curve, pt, max_err=0.05), + dist = norm(bezier_points(curve, u)-pt), + mseg = (min_dist==undef || dist= len(patches))? vnf : - bezier_patch(patches[i], splinesteps=splinesteps, vnf=vnf, style=style) - ) (i >= len(patches))? vnf : - bezier_surface(patches=patches, splinesteps=splinesteps, vnf=vnf, style=style, i=i+1); + let( + vnf = (i >= len(patches))? vnf : + bezier_patch(patches[i], splinesteps=splinesteps, vnf=vnf, style=style) + ) (i >= len(patches))? vnf : + bezier_surface(patches=patches, splinesteps=splinesteps, vnf=vnf, style=style, i=i+1); @@ -1217,10 +1217,10 @@ function bezier_surface(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="defaul // bezier_polyhedron([patch1, patch2], splinesteps=8); module bezier_polyhedron(patches=[], splinesteps=16, vnf=EMPTY_VNF, style="default", convexity=10) { - vnf_polyhedron( - bezier_surface(patches=patches, splinesteps=splinesteps, vnf=vnf, style=style), - convexity=convexity - ); + vnf_polyhedron( + bezier_surface(patches=patches, splinesteps=splinesteps, vnf=vnf, style=style), + convexity=convexity + ); } @@ -1263,33 +1263,33 @@ module trace_bezier_patches(patches=[], size, splinesteps=16, showcps=true, show assert(is_bool(showpatch)); assert(is_int(convexity) && convexity>0); for (patch = patches) { - size = is_num(size)? size : - let( bounds = pointlist_bounds(flatten(patch)) ) - max(bounds[1]-bounds[0])*0.01; - if (showcps) { - move_copies(flatten(patch)) color("red") sphere(d=size*2); - color("cyan") { - if (is_tripatch(patch)) { - for (i=[0:1:len(patch)-2], j=[0:1:len(patch[i])-2]) { - extrude_from_to(patch[i][j], patch[i+1][j]) circle(d=size); - extrude_from_to(patch[i][j], patch[i][j+1]) circle(d=size); - extrude_from_to(patch[i+1][j], patch[i][j+1]) circle(d=size); - } - } else { - for (i=[0:1:len(patch)-1], j=[0:1:len(patch[i])-1]) { - if (i // pco1810_neck(); module pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) { - inner_d = 21.74; - neck_d = 26.19; - neck_h = 5.00; - support_d = 33.00; - support_width = 1.45; - support_rad = 0.40; - support_h = 21.00; - support_ang = 16; - tamper_ring_d = 27.97; - tamper_ring_width = 0.50; - tamper_ring_r = 1.60; - tamper_base_d = 25.71; - tamper_base_h = 14.10; - threadbase_d = 24.51; - thread_pitch = 3.18; - thread_angle = 20; - thread_od = 27.43; - lip_d = 25.07; - lip_h = 1.70; - lip_leadin_r = 0.20; - lip_recess_d = 24.94; - lip_recess_h = 1.00; - lip_roundover_r = 0.58; + inner_d = 21.74; + neck_d = 26.19; + neck_h = 5.00; + support_d = 33.00; + support_width = 1.45; + support_rad = 0.40; + support_h = 21.00; + support_ang = 16; + tamper_ring_d = 27.97; + tamper_ring_width = 0.50; + tamper_ring_r = 1.60; + tamper_base_d = 25.71; + tamper_base_h = 14.10; + threadbase_d = 24.51; + thread_pitch = 3.18; + thread_angle = 20; + thread_od = 27.43; + lip_d = 25.07; + lip_h = 1.70; + lip_leadin_r = 0.20; + lip_recess_d = 24.94; + lip_recess_h = 1.00; + lip_roundover_r = 0.58; - $fn = segs(support_d/2); - h = support_h+neck_h; - thread_h = (thread_od-threadbase_d)/2; - anchors = [ - anchorpt("support-ring", [0,0,neck_h-h/2]), - anchorpt("tamper-ring", [0,0,h/2-tamper_base_h]) - ]; - attachable(anchor,spin,orient, d=support_d, l=h, anchors=anchors) { - down(h/2) { - rotate_extrude(convexity=10) { - polygon(turtle( - state=[inner_d/2,0], [ - "untilx", neck_d/2, - "left", 90, - "move", neck_h - 1, - "arcright", 1, 90, - "untilx", support_d/2-support_rad, - "arcleft", support_rad, 90, - "move", support_width, - "arcleft", support_rad, 90-support_ang, - "untilx", tamper_base_d/2, - "right", 90-support_ang, - "untily", h-tamper_base_h, // Tamper ring holder base. - "right", 90, - "untilx", tamper_ring_d/2, - "left", 90, - "move", tamper_ring_width, - "arcleft", tamper_ring_r, 90, - "untilx", threadbase_d/2, - "right", 90, - "untily", h-lip_h-lip_leadin_r, // Lip base. - "arcright", lip_leadin_r, 90, - "untilx", lip_d/2, - "left", 90, - "untily", h-lip_recess_h, - "left", 90, - "untilx", lip_recess_d/2, - "right", 90, - "untily", h-lip_roundover_r, - "arcleft", lip_roundover_r, 90, - "untilx", inner_d/2 - ] - )); - } - up(h-lip_h) { - bottom_half() { - difference() { - thread_helix( - base_d=threadbase_d-0.1, - pitch=thread_pitch, - thread_depth=thread_h+0.1, - thread_angle=thread_angle, - twist=810, - higbee=75, - anchor=TOP - ); - zrot_copies(rots=[90,270]) { - zrot_copies(rots=[-28,28], r=threadbase_d/2) { - prismoid([20,1.82], [20,1.82+2*sin(29)*thread_h], h=thread_h+0.1, anchor=BOT, orient=RIGHT); - } - } - } - } - } - } - children(); - } + $fn = segs(support_d/2); + h = support_h+neck_h; + thread_h = (thread_od-threadbase_d)/2; + anchors = [ + anchorpt("support-ring", [0,0,neck_h-h/2]), + anchorpt("tamper-ring", [0,0,h/2-tamper_base_h]) + ]; + attachable(anchor,spin,orient, d=support_d, l=h, anchors=anchors) { + down(h/2) { + rotate_extrude(convexity=10) { + polygon(turtle( + state=[inner_d/2,0], [ + "untilx", neck_d/2, + "left", 90, + "move", neck_h - 1, + "arcright", 1, 90, + "untilx", support_d/2-support_rad, + "arcleft", support_rad, 90, + "move", support_width, + "arcleft", support_rad, 90-support_ang, + "untilx", tamper_base_d/2, + "right", 90-support_ang, + "untily", h-tamper_base_h, // Tamper ring holder base. + "right", 90, + "untilx", tamper_ring_d/2, + "left", 90, + "move", tamper_ring_width, + "arcleft", tamper_ring_r, 90, + "untilx", threadbase_d/2, + "right", 90, + "untily", h-lip_h-lip_leadin_r, // Lip base. + "arcright", lip_leadin_r, 90, + "untilx", lip_d/2, + "left", 90, + "untily", h-lip_recess_h, + "left", 90, + "untilx", lip_recess_d/2, + "right", 90, + "untily", h-lip_roundover_r, + "arcleft", lip_roundover_r, 90, + "untilx", inner_d/2 + ] + )); + } + up(h-lip_h) { + bottom_half() { + difference() { + thread_helix( + base_d=threadbase_d-0.1, + pitch=thread_pitch, + thread_depth=thread_h+0.1, + thread_angle=thread_angle, + twist=810, + higbee=75, + anchor=TOP + ); + zrot_copies(rots=[90,270]) { + zrot_copies(rots=[-28,28], r=threadbase_d/2) { + prismoid([20,1.82], [20,1.82+2*sin(29)*thread_h], h=thread_h+0.1, anchor=BOT, orient=RIGHT); + } + } + } + } + } + } + children(); + } } @@ -146,41 +146,41 @@ module pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) // pco1810_cap(texture="ribbed"); module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) { - cap_id = 28.58; - tamper_ring_h = 14.10; - thread_pitch = 3.18; - thread_angle = 20; - thread_od = cap_id; - thread_depth = 1.6; + cap_id = 28.58; + tamper_ring_h = 14.10; + thread_pitch = 3.18; + thread_angle = 20; + thread_od = cap_id; + thread_depth = 1.6; - $fn = segs(33/2); - w = cap_id + 2*wall; - h = tamper_ring_h + wall; - anchors = [ - anchorpt("inside-top", [0,0,-(h/2-wall)]) - ]; - attachable(anchor,spin,orient, d=w, l=h, anchors=anchors) { - down(h/2) zrot(45) { - difference() { - union() { - if (texture == "knurled") { - knurled_cylinder(d=w, helix=45, l=tamper_ring_h+wall, anchor=BOTTOM); - cyl(d=w-1.5, l=tamper_ring_h+wall, anchor=BOTTOM); - } else if (texture == "ribbed") { - zrot_copies(n=30, r=(w-1)/2) { - cube([1, 1, tamper_ring_h+wall], anchor=BOTTOM); - } - cyl(d=w-1, l=tamper_ring_h+wall, anchor=BOTTOM); - } else { - cyl(d=w, l=tamper_ring_h+wall, anchor=BOTTOM); - } - } - up(wall) cyl(d=cap_id, h=tamper_ring_h+wall, anchor=BOTTOM); - } - up(wall+2) thread_helix(base_d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, thread_angle=thread_angle, twist=810, higbee=45, internal=true, anchor=BOTTOM); - } - children(); - } + $fn = segs(33/2); + w = cap_id + 2*wall; + h = tamper_ring_h + wall; + anchors = [ + anchorpt("inside-top", [0,0,-(h/2-wall)]) + ]; + attachable(anchor,spin,orient, d=w, l=h, anchors=anchors) { + down(h/2) zrot(45) { + difference() { + union() { + if (texture == "knurled") { + knurled_cylinder(d=w, helix=45, l=tamper_ring_h+wall, anchor=BOTTOM); + cyl(d=w-1.5, l=tamper_ring_h+wall, anchor=BOTTOM); + } else if (texture == "ribbed") { + zrot_copies(n=30, r=(w-1)/2) { + cube([1, 1, tamper_ring_h+wall], anchor=BOTTOM); + } + cyl(d=w-1, l=tamper_ring_h+wall, anchor=BOTTOM); + } else { + cyl(d=w, l=tamper_ring_h+wall, anchor=BOTTOM); + } + } + up(wall) cyl(d=cap_id, h=tamper_ring_h+wall, anchor=BOTTOM); + } + up(wall+2) thread_helix(base_d=thread_od-thread_depth*2, pitch=thread_pitch, thread_depth=thread_depth, thread_angle=thread_angle, twist=810, higbee=45, internal=true, anchor=BOTTOM); + } + children(); + } } @@ -204,97 +204,97 @@ module pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) // pco1881_neck(); module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) { - inner_d = 21.74; - neck_d = 26.19; - neck_h = 5.00; - support_d = 33.00; - support_width = 0.58; - support_rad = 0.30; - support_h = 17.00; - support_ang = 15; - tamper_ring_d = 28.00; - tamper_ring_width = 0.30; - tamper_ring_ang = 45; - tamper_base_d = 25.71; - tamper_base_h = 11.20; - tamper_divot_r = 1.08; - threadbase_d = 24.20; - thread_pitch = 2.70; - thread_angle = 15; - thread_od = 27.4; - lip_d = 25.07; - lip_h = 1.70; - lip_leadin_r = 0.30; - lip_recess_d = 24.94; - lip_recess_h = 1.00; - lip_roundover_r = 0.58; + inner_d = 21.74; + neck_d = 26.19; + neck_h = 5.00; + support_d = 33.00; + support_width = 0.58; + support_rad = 0.30; + support_h = 17.00; + support_ang = 15; + tamper_ring_d = 28.00; + tamper_ring_width = 0.30; + tamper_ring_ang = 45; + tamper_base_d = 25.71; + tamper_base_h = 11.20; + tamper_divot_r = 1.08; + threadbase_d = 24.20; + thread_pitch = 2.70; + thread_angle = 15; + thread_od = 27.4; + lip_d = 25.07; + lip_h = 1.70; + lip_leadin_r = 0.30; + lip_recess_d = 24.94; + lip_recess_h = 1.00; + lip_roundover_r = 0.58; - $fn = segs(support_d/2); - h = support_h+neck_h; - thread_h = (thread_od-threadbase_d)/2; - anchors = [ - anchorpt("support-ring", [0,0,neck_h-h/2]), - anchorpt("tamper-ring", [0,0,h/2-tamper_base_h]) - ]; - attachable(anchor,spin,orient, d=support_d, l=h, anchors=anchors) { - down(h/2) { - rotate_extrude(convexity=10) { - polygon(turtle( - state=[inner_d/2,0], [ - "untilx", neck_d/2, - "left", 90, - "move", neck_h - 1, - "arcright", 1, 90, - "untilx", support_d/2-support_rad, - "arcleft", support_rad, 90, - "move", support_width, - "arcleft", support_rad, 90-support_ang, - "untilx", tamper_base_d/2, - "arcright", tamper_divot_r, 180-support_ang*2, - "left", 90-support_ang, - "untily", h-tamper_base_h, // Tamper ring holder base. - "right", 90, - "untilx", tamper_ring_d/2, - "left", 90, - "move", tamper_ring_width, - "left", tamper_ring_ang, - "untilx", threadbase_d/2, - "right", tamper_ring_ang, - "untily", h-lip_h-lip_leadin_r, // Lip base. - "arcright", lip_leadin_r, 90, - "untilx", lip_d/2, - "left", 90, - "untily", h-lip_recess_h, - "left", 90, - "untilx", lip_recess_d/2, - "right", 90, - "untily", h-lip_roundover_r, - "arcleft", lip_roundover_r, 90, - "untilx", inner_d/2 - ] - )); - } - up(h-lip_h) { - difference() { - thread_helix( - base_d=threadbase_d-0.1, - pitch=thread_pitch, - thread_depth=thread_h+0.1, - thread_angle=thread_angle, - twist=650, - higbee=75, - anchor=TOP - ); - zrot_copies(rots=[90,270]) { - zrot_copies(rots=[-28,28], r=threadbase_d/2) { - prismoid([20,1.82], [20,1.82+2*sin(29)*thread_h], h=thread_h+0.1, anchor=BOT, orient=RIGHT); - } - } - } - } - } - children(); - } + $fn = segs(support_d/2); + h = support_h+neck_h; + thread_h = (thread_od-threadbase_d)/2; + anchors = [ + anchorpt("support-ring", [0,0,neck_h-h/2]), + anchorpt("tamper-ring", [0,0,h/2-tamper_base_h]) + ]; + attachable(anchor,spin,orient, d=support_d, l=h, anchors=anchors) { + down(h/2) { + rotate_extrude(convexity=10) { + polygon(turtle( + state=[inner_d/2,0], [ + "untilx", neck_d/2, + "left", 90, + "move", neck_h - 1, + "arcright", 1, 90, + "untilx", support_d/2-support_rad, + "arcleft", support_rad, 90, + "move", support_width, + "arcleft", support_rad, 90-support_ang, + "untilx", tamper_base_d/2, + "arcright", tamper_divot_r, 180-support_ang*2, + "left", 90-support_ang, + "untily", h-tamper_base_h, // Tamper ring holder base. + "right", 90, + "untilx", tamper_ring_d/2, + "left", 90, + "move", tamper_ring_width, + "left", tamper_ring_ang, + "untilx", threadbase_d/2, + "right", tamper_ring_ang, + "untily", h-lip_h-lip_leadin_r, // Lip base. + "arcright", lip_leadin_r, 90, + "untilx", lip_d/2, + "left", 90, + "untily", h-lip_recess_h, + "left", 90, + "untilx", lip_recess_d/2, + "right", 90, + "untily", h-lip_roundover_r, + "arcleft", lip_roundover_r, 90, + "untilx", inner_d/2 + ] + )); + } + up(h-lip_h) { + difference() { + thread_helix( + base_d=threadbase_d-0.1, + pitch=thread_pitch, + thread_depth=thread_h+0.1, + thread_angle=thread_angle, + twist=650, + higbee=75, + anchor=TOP + ); + zrot_copies(rots=[90,270]) { + zrot_copies(rots=[-28,28], r=threadbase_d/2) { + prismoid([20,1.82], [20,1.82+2*sin(29)*thread_h], h=thread_h+0.1, anchor=BOT, orient=RIGHT); + } + } + } + } + } + children(); + } } @@ -317,36 +317,36 @@ module pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) // pco1881_cap(texture="ribbed"); module pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) { - $fn = segs(33/2); - w = 28.58 + 2*wall; - h = 11.2 + wall; - anchors = [ - anchorpt("inside-top", [0,0,-(h/2-wall)]) - ]; - attachable(anchor,spin,orient, d=w, l=h, anchors=anchors) { - down(h/2) zrot(45) { - difference() { - union() { - if (texture == "knurled") { - knurled_cylinder(d=w, helix=45, l=11.2+wall, anchor=BOTTOM); - cyl(d=w-1.5, l=11.2+wall, anchor=BOTTOM); - } else if (texture == "ribbed") { - zrot_copies(n=30, r=(w-1)/2) { - cube([1, 1, 11.2+wall], anchor=BOTTOM); - } - cyl(d=w-1, l=11.2+wall, anchor=BOTTOM); - } else { - cyl(d=w, l=11.2+wall, anchor=BOTTOM); - } - } - up(wall) cyl(d=28.58, h=11.2+wall, anchor=BOTTOM); - } - up(wall+2) thread_helix(base_d=25.5, pitch=2.7, thread_depth=1.6, thread_angle=15, twist=650, higbee=45, internal=true, anchor=BOTTOM); - } - children(); - } + $fn = segs(33/2); + w = 28.58 + 2*wall; + h = 11.2 + wall; + anchors = [ + anchorpt("inside-top", [0,0,-(h/2-wall)]) + ]; + attachable(anchor,spin,orient, d=w, l=h, anchors=anchors) { + down(h/2) zrot(45) { + difference() { + union() { + if (texture == "knurled") { + knurled_cylinder(d=w, helix=45, l=11.2+wall, anchor=BOTTOM); + cyl(d=w-1.5, l=11.2+wall, anchor=BOTTOM); + } else if (texture == "ribbed") { + zrot_copies(n=30, r=(w-1)/2) { + cube([1, 1, 11.2+wall], anchor=BOTTOM); + } + cyl(d=w-1, l=11.2+wall, anchor=BOTTOM); + } else { + cyl(d=w, l=11.2+wall, anchor=BOTTOM); + } + } + up(wall) cyl(d=28.58, h=11.2+wall, anchor=BOTTOM); + } + up(wall+2) thread_helix(base_d=25.5, pitch=2.7, thread_depth=1.6, thread_angle=15, twist=650, higbee=45, internal=true, anchor=BOTTOM); + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/common.scad b/common.scad index 31d5811..11b117c 100644 --- a/common.scad +++ b/common.scad @@ -17,12 +17,12 @@ // Description: // Returns a string representing the type of the value. One of "undef", "boolean", "number", "string", "list", or "range" function typeof(x) = - is_undef(x)? "undef" : - is_bool(x)? "boolean" : - is_num(x)? "number" : - is_string(x)? "string" : - is_list(x)? "list" : - "range"; + is_undef(x)? "undef" : + is_bool(x)? "boolean" : + is_num(x)? "number" : + is_string(x)? "string" : + is_list(x)? "list" : + "range"; // Function: is_type() @@ -43,9 +43,9 @@ function typeof(x) = // is_str3 = is_type(["foo"], "string"); // Returns: false // is_str4 = is_type(3, "string"); // Returns: false function is_type(x,types) = - is_list(types)? in_list(typeof(x),types) : - is_string(types)? typeof(x) == types : - assert(is_list(types)||is_string(types)); + is_list(types)? in_list(typeof(x),types) : + is_string(types)? typeof(x) == types : + assert(is_list(types)||is_string(types)); // Function: is_def() @@ -100,9 +100,9 @@ function is_range(x) = is_num(x[0]) && !is_list(x); // 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 function is_list_of(list,pattern) = - let(pattern = 0*pattern) - is_list(list) && - []==[for(entry=list) if (entry*0 != pattern) entry]; + let(pattern = 0*pattern) + is_list(list) && + []==[for(entry=list) if (entry*0 != pattern) entry]; // Function: is_consistent() @@ -153,13 +153,13 @@ function default(v,dflt=undef) = is_undef(v)? dflt : v; // v = The list whose items are being checked. // recursive = If true, sublists are checked recursively for defined values. The first sublist that has a defined item is returned. function first_defined(v,recursive=false,_i=0) = - _i0? ($fn>3? $fn : 3) : - ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs))); + $fn>0? ($fn>3? $fn : 3) : + ceil(max(5, min(360/$fa, abs(r)*2*PI/$fs))); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/constants.scad b/constants.scad index 4be92c3..ed5bd80 100644 --- a/constants.scad +++ b/constants.scad @@ -118,4 +118,4 @@ FORWARD = FRONT; // Vector pointing forward, alias to `FRONT`. -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/coords.scad b/coords.scad index 19f3406..97729be 100644 --- a/coords.scad +++ b/coords.scad @@ -30,10 +30,10 @@ function point2d(p, fill=0) = [for (i=[0:1]) (p[i]==undef)? fill : p[i]]; // points = A list of 2D or 3D points/vectors. // fill = Value to fill missing values in vectors with. function path2d(points) = - assert(is_path(points,dim=undef,fast=true),"Input to path2d is not a path") - let (result = points * concat(ident(2), repeat([0,0], len(points[0])-2))) - assert(is_def(result), "Invalid input to path2d") - result; + assert(is_path(points,dim=undef,fast=true),"Input to path2d is not a path") + let (result = points * concat(ident(2), repeat([0,0], len(points[0])-2))) + assert(is_def(result), "Invalid input to path2d") + result; // Function: point3d() @@ -53,16 +53,16 @@ function point3d(p, fill=0) = [for (i=[0:2]) (p[i]==undef)? fill : p[i]]; // 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), repeat([0,0,0],change)), - result = points*M - ) - assert(is_def(result), "Input to path3d is invalid") - fill == 0 || change>=0 ? result : result + repeat([0,0,fill], len(result)); + 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), repeat([0,0,0],change)), + result = points*M + ) + assert(is_def(result), "Input to path3d is invalid") + fill == 0 || change>=0 ? result : result + repeat([0,0,fill], len(result)); // Function: point4d() @@ -118,9 +118,9 @@ function path4d(points, fill=0) = // xy = polar_to_xy(40,30); // Returns: ~[34.6410162, 15] // xy = polar_to_xy([40,30]); // Returns: ~[34.6410162, 15] function polar_to_xy(r,theta=undef) = let( - rad = theta==undef? r[0] : r, - t = theta==undef? r[1] : theta - ) rad*[cos(t), sin(t)]; + rad = theta==undef? r[0] : r, + t = theta==undef? r[1] : theta + ) rad*[cos(t), sin(t)]; // Function: xy_to_polar() @@ -137,9 +137,9 @@ function polar_to_xy(r,theta=undef) = let( // plr = xy_to_polar(20,30); // plr = xy_to_polar([40,60]); function xy_to_polar(x,y=undef) = let( - xx = y==undef? x[0] : x, - yy = y==undef? x[1] : y - ) [norm([xx,yy]), atan2(yy,xx)]; + xx = y==undef? x[0] : x, + yy = y==undef? x[1] : y + ) [norm([xx,yy]), atan2(yy,xx)]; // Function: project_plane() @@ -169,26 +169,26 @@ function xy_to_polar(x,y=undef) = let( // xy = project_plane(pt, a, b, c); // xy2 = project_plane(pt, [a,b,c]); function project_plane(point, a, b, c) = - is_undef(b) && is_undef(c) && is_list(a)? let( - mat = is_vector(a,4)? plane_transform(a) : - assert(is_path(a) && len(a)>=3) - plane_transform(plane_from_points(a)), - pts = is_vector(point)? point2d(apply(mat,point)) : - is_path(point)? path2d(apply(mat,point)) : - is_region(point)? [for (x=point) path2d(apply(mat,x))] : - assert(false, "point must be a 3D point, path, or region.") - ) pts : - assert(is_vector(a)) - assert(is_vector(b)) - assert(is_vector(c)) - assert(is_vector(point)||is_path(point)) - let( - u = unit(b-a), - v = unit(c-a), - n = unit(cross(u,v)), - w = unit(cross(n,u)), - relpoint = apply(move(-a),point) - ) relpoint * transpose([w,u]); + is_undef(b) && is_undef(c) && is_list(a)? let( + mat = is_vector(a,4)? plane_transform(a) : + assert(is_path(a) && len(a)>=3) + plane_transform(plane_from_points(a)), + pts = is_vector(point)? point2d(apply(mat,point)) : + is_path(point)? path2d(apply(mat,point)) : + is_region(point)? [for (x=point) path2d(apply(mat,x))] : + assert(false, "point must be a 3D point, path, or region.") + ) pts : + assert(is_vector(a)) + assert(is_vector(b)) + assert(is_vector(c)) + assert(is_vector(point)||is_path(point)) + let( + u = unit(b-a), + v = unit(c-a), + n = unit(cross(u,v)), + w = unit(cross(n,u)), + relpoint = apply(move(-a),point) + ) relpoint * transpose([w,u]); // Function: lift_plane() @@ -210,27 +210,27 @@ function project_plane(point, a, b, c) = // b = A 3D point that the plane passes through. Used to define the plane. // c = A 3D point that the plane passes through. Used to define the plane. function lift_plane(point, a, b, c) = - is_undef(b) && is_undef(c) && is_list(a)? let( - mat = is_vector(a,4)? plane_transform(a) : - assert(is_path(a) && len(a)>=3) - plane_transform(plane_from_points(a)), - imat = matrix_inverse(mat), - pts = is_vector(point)? apply(imat,point3d(point)) : - is_path(point)? apply(imat,path3d(point)) : - is_region(point)? [for (x=point) apply(imat,path3d(x))] : - assert(false, "point must be a 2D point, path, or region.") - ) pts : - assert(is_vector(a)) - assert(is_vector(b)) - assert(is_vector(c)) - assert(is_vector(point)||is_path(point)) - let( - u = unit(b-a), - v = unit(c-a), - n = unit(cross(u,v)), - w = unit(cross(n,u)), - remapped = point*[w,u] - ) apply(move(a),remapped); + is_undef(b) && is_undef(c) && is_list(a)? let( + mat = is_vector(a,4)? plane_transform(a) : + assert(is_path(a) && len(a)>=3) + plane_transform(plane_from_points(a)), + imat = matrix_inverse(mat), + pts = is_vector(point)? apply(imat,point3d(point)) : + is_path(point)? apply(imat,path3d(point)) : + is_region(point)? [for (x=point) apply(imat,path3d(x))] : + assert(false, "point must be a 2D point, path, or region.") + ) pts : + assert(is_vector(a)) + assert(is_vector(b)) + assert(is_vector(c)) + assert(is_vector(point)||is_path(point)) + let( + u = unit(b-a), + v = unit(c-a), + n = unit(cross(u,v)), + w = unit(cross(n,u)), + remapped = point*[w,u] + ) apply(move(a),remapped); // Function: cylindrical_to_xyz() @@ -247,10 +247,10 @@ function lift_plane(point, a, b, c) = // xyz = cylindrical_to_xyz(20,30,40); // xyz = cylindrical_to_xyz([40,60,50]); function cylindrical_to_xyz(r,theta=undef,z=undef) = let( - rad = theta==undef? r[0] : r, - t = theta==undef? r[1] : theta, - zed = theta==undef? r[2] : z - ) [rad*cos(t), rad*sin(t), zed]; + rad = theta==undef? r[0] : r, + t = theta==undef? r[1] : theta, + zed = theta==undef? r[2] : z + ) [rad*cos(t), rad*sin(t), zed]; // Function: xyz_to_cylindrical() @@ -269,8 +269,8 @@ function cylindrical_to_xyz(r,theta=undef,z=undef) = let( // cyl = xyz_to_cylindrical(20,30,40); // cyl = xyz_to_cylindrical([40,50,70]); function xyz_to_cylindrical(x,y=undef,z=undef) = let( - p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x) - ) [norm([p.x,p.y]), atan2(p.y,p.x), p.z]; + p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x) + ) [norm([p.x,p.y]), atan2(p.y,p.x), p.z]; // Function: spherical_to_xyz() @@ -288,10 +288,10 @@ function xyz_to_cylindrical(x,y=undef,z=undef) = let( // xyz = spherical_to_xyz(20,30,40); // xyz = spherical_to_xyz([40,60,50]); function spherical_to_xyz(r,theta=undef,phi=undef) = let( - rad = theta==undef? r[0] : r, - t = theta==undef? r[1] : theta, - p = theta==undef? r[2] : phi - ) rad*[sin(p)*cos(t), sin(p)*sin(t), cos(p)]; + rad = theta==undef? r[0] : r, + t = theta==undef? r[1] : theta, + p = theta==undef? r[2] : phi + ) rad*[sin(p)*cos(t), sin(p)*sin(t), cos(p)]; // Function: xyz_to_spherical() @@ -310,8 +310,8 @@ function spherical_to_xyz(r,theta=undef,phi=undef) = let( // sph = xyz_to_spherical(20,30,40); // sph = xyz_to_spherical([40,50,70]); function xyz_to_spherical(x,y=undef,z=undef) = let( - p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x) - ) [norm(p), atan2(p.y,p.x), atan2(norm([p.x,p.y]),p.z)]; + p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x) + ) [norm(p), atan2(p.y,p.x), atan2(norm([p.x,p.y]),p.z)]; // Function: altaz_to_xyz() @@ -329,10 +329,10 @@ function xyz_to_spherical(x,y=undef,z=undef) = let( // xyz = altaz_to_xyz(20,30,40); // xyz = altaz_to_xyz([40,60,50]); function altaz_to_xyz(alt,az=undef,r=undef) = let( - p = az==undef? alt[0] : alt, - t = 90 - (az==undef? alt[1] : az), - rad = az==undef? alt[2] : r - ) rad*[cos(p)*cos(t), cos(p)*sin(t), sin(p)]; + p = az==undef? alt[0] : alt, + t = 90 - (az==undef? alt[1] : az), + rad = az==undef? alt[2] : r + ) rad*[cos(p)*cos(t), cos(p)*sin(t), sin(p)]; // Function: xyz_to_altaz() @@ -352,9 +352,9 @@ function altaz_to_xyz(alt,az=undef,r=undef) = let( // aa = xyz_to_altaz(20,30,40); // aa = xyz_to_altaz([40,50,70]); function xyz_to_altaz(x,y=undef,z=undef) = let( - p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x) - ) [atan2(p.z,norm([p.x,p.y])), atan2(p.x,p.y), norm(p)]; + p = is_num(x)? [x, default(y,0), default(z,0)] : point3d(x) + ) [atan2(p.z,norm([p.x,p.y])), atan2(p.x,p.y), norm(p)]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/cubetruss.scad b/cubetruss.scad index 3306eb8..b372b75 100644 --- a/cubetruss.scad +++ b/cubetruss.scad @@ -25,10 +25,10 @@ $cubetruss_clip_thickness = 1.6; // size = The length of each side of the cubetruss cubes. Default: `$cubetruss_size` (usually 30) // strut = The width of the struts on the cubetruss cubes. Default: `$cubetruss_strut_size` (usually 3) function cubetruss_dist(cubes=0, gaps=0, size=undef, strut=undef) = - let( - size = is_undef(size)? $cubetruss_size : size, - strut = is_undef(strut)? $cubetruss_strut_size : strut - ) cubes*(size-strut)+gaps*strut; + let( + size = is_undef(size)? $cubetruss_size : size, + strut = is_undef(strut)? $cubetruss_strut_size : strut + ) cubes*(size-strut)+gaps*strut; // Module: cubetruss_segment() @@ -49,52 +49,52 @@ function cubetruss_dist(cubes=0, gaps=0, size=undef, strut=undef) = // cubetruss_segment(strut=4); // cubetruss_segment(size=40); module cubetruss_segment(size=undef, strut=undef, bracing=undef, anchor=CENTER, spin=0, orient=UP) { - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - bracing = is_undef(bracing)? $cubetruss_bracing : bracing; - h = size; - crossthick = strut/sqrt(2); - voffset = 0.333; - attachable(anchor,spin,orient, size=[size,size,size]) { - render(convexity=10) - union() { - difference() { - // Start with a cube. - cube([size, size, h], center=true); + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + bracing = is_undef(bracing)? $cubetruss_bracing : bracing; + h = size; + crossthick = strut/sqrt(2); + voffset = 0.333; + attachable(anchor,spin,orient, size=[size,size,size]) { + render(convexity=10) + union() { + difference() { + // Start with a cube. + cube([size, size, h], center=true); - cube([size-strut*2, size-strut*2, h-strut*2], center=true); + cube([size-strut*2, size-strut*2, h-strut*2], center=true); - // Hollow out octogons in X and Y axes. - zrot_copies([0,90]) { - xrot(90) zrot(180/8) cylinder(h=max(h,size)+1, d=(min(h,size)-2*strut)/cos(180/8), center=true, $fn=8); - } + // Hollow out octogons in X and Y axes. + zrot_copies([0,90]) { + xrot(90) zrot(180/8) cylinder(h=max(h,size)+1, d=(min(h,size)-2*strut)/cos(180/8), center=true, $fn=8); + } - // Hollow out octogon vertically. - zrot(180/8) cylinder(h=max(h,size)+1, d=(min(h,size)-2*strut)/cos(180/8), center=true, $fn=8); - } + // Hollow out octogon vertically. + zrot(180/8) cylinder(h=max(h,size)+1, d=(min(h,size)-2*strut)/cos(180/8), center=true, $fn=8); + } - // Interior cross-supports - if (bracing) { - for (i = [-1,1]) { - zrot(i*45) { - difference() { - cube([crossthick, (size-strut)*sqrt(2), h], center=true); - up(i*voffset) { - yscale(1.3) { - yrot(90) { - zrot(180/6) { - cylinder(h=crossthick+1, d=(min(h,size)-2*strut)/cos(180/6)-2*voffset, center=true, $fn=6); - } - } - } - } - } - } - } - } - } - children(); - } + // Interior cross-supports + if (bracing) { + for (i = [-1,1]) { + zrot(i*45) { + difference() { + cube([crossthick, (size-strut)*sqrt(2), h], center=true); + up(i*voffset) { + yscale(1.3) { + yrot(90) { + zrot(180/6) { + cylinder(h=crossthick+1, d=(min(h,size)-2*strut)/cos(180/6)-2*voffset, center=true, $fn=6); + } + } + } + } + } + } + } + } + } + children(); + } } @@ -116,47 +116,47 @@ module cubetruss_segment(size=undef, strut=undef, bracing=undef, anchor=CENTER, // cubetruss_clip(extents=1); // cubetruss_clip(clipthick=2.5); module cubetruss_clip(extents=1, size=undef, strut=undef, clipthick=undef, anchor=CENTER, spin=0, orient=UP) { - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - cliplen = strut * 2.6; - clipheight = min(size+strut, size/3+2*strut*2.6); - clipsize = 0.5; - s = [extents*(size-strut)+strut+2*clipthick, strut*2, clipheight-2*strut]; - attachable(anchor,spin,orient, size=s) { - xflip_copy(offset=(extents*(size-strut)+strut)/2) { - difference() { - union() { - difference() { - right(clipthick/2-0.01) { - back(strut) { - difference() { - xrot(90) prismoid([clipthick, clipheight], [clipthick, clipheight-cliplen*2], h=cliplen); - right(clipthick/2) chamfer_mask_z(l=clipheight+0.1, chamfer=clipthick); - } - } - } - fwd(strut*3/2) { - cube([$slop, strut*3, size], center=true); - } - } - right($slop/2+0.01) { - fwd(strut*1.25+$slop) { - yrot(-90) prismoid([clipheight-cliplen*2, strut/2], [clipheight-cliplen*2-2*clipsize, strut/2], h=clipsize+0.01); - } - } - } - fwd(strut*1.6) { - left(clipsize) { - yscale(1.5) chamfer_mask_z(l=size+1, chamfer=clipsize+clipthick/3); - } - } - zcopies(clipheight-strut) cube([clipthick*3, cliplen*2, strut], center=true); - zcopies(clipheight-2*strut) right(clipthick) chamfer_mask_y(l=cliplen*2, chamfer=clipthick); - } - } - children(); - } + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; + cliplen = strut * 2.6; + clipheight = min(size+strut, size/3+2*strut*2.6); + clipsize = 0.5; + s = [extents*(size-strut)+strut+2*clipthick, strut*2, clipheight-2*strut]; + attachable(anchor,spin,orient, size=s) { + xflip_copy(offset=(extents*(size-strut)+strut)/2) { + difference() { + union() { + difference() { + right(clipthick/2-0.01) { + back(strut) { + difference() { + xrot(90) prismoid([clipthick, clipheight], [clipthick, clipheight-cliplen*2], h=cliplen); + right(clipthick/2) chamfer_mask_z(l=clipheight+0.1, chamfer=clipthick); + } + } + } + fwd(strut*3/2) { + cube([$slop, strut*3, size], center=true); + } + } + right($slop/2+0.01) { + fwd(strut*1.25+$slop) { + yrot(-90) prismoid([clipheight-cliplen*2, strut/2], [clipheight-cliplen*2-2*clipsize, strut/2], h=clipsize+0.01); + } + } + } + fwd(strut*1.6) { + left(clipsize) { + yscale(1.5) chamfer_mask_z(l=size+1, chamfer=clipsize+clipthick/3); + } + } + zcopies(clipheight-strut) cube([clipthick*3, cliplen*2, strut], center=true); + zcopies(clipheight-2*strut) right(clipthick) chamfer_mask_y(l=cliplen*2, chamfer=clipthick); + } + } + children(); + } } @@ -177,60 +177,60 @@ module cubetruss_clip(extents=1, size=undef, strut=undef, clipthick=undef, ancho // cubetruss_foot(w=1); // cubetruss_foot(w=3); module cubetruss_foot(w=1, size=undef, strut=undef, clipthick=undef, anchor=CENTER, spin=0, orient=UP) { - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - clipsize = 0.5; - wall_h = strut+clipthick*1.5; - cyld = (size-2*strut)/cos(180/8); - s = [w*(size-strut)+strut+2*clipthick, size-2*strut, strut+clipthick]; - attachable(anchor,spin,orient, size=s, offset=[0,0,(strut-clipthick)/2]) { - down(clipthick) { - // Base - up(clipthick/2) { - cuboid([w*(size-strut)+strut+2*clipthick, size-2*strut, clipthick], chamfer=strut, edges=edges("Z")); - } + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; + clipsize = 0.5; + wall_h = strut+clipthick*1.5; + cyld = (size-2*strut)/cos(180/8); + s = [w*(size-strut)+strut+2*clipthick, size-2*strut, strut+clipthick]; + attachable(anchor,spin,orient, size=s, offset=[0,0,(strut-clipthick)/2]) { + down(clipthick) { + // Base + up(clipthick/2) { + cuboid([w*(size-strut)+strut+2*clipthick, size-2*strut, clipthick], chamfer=strut, edges=edges("Z")); + } - // Walls - xcopies(w*(size-strut)+strut+clipthick) { - up(clipthick-0.01) { - prismoid([clipthick, (size-4*strut)], [clipthick, size/3.5], h=wall_h, anchor=BOT); - } - } + // Walls + xcopies(w*(size-strut)+strut+clipthick) { + up(clipthick-0.01) { + prismoid([clipthick, (size-4*strut)], [clipthick, size/3.5], h=wall_h, anchor=BOT); + } + } - // Horiz Wall Clips - up(clipthick+strut+$slop*2) { - xcopies(w*(size-strut)+strut) { - prismoid([clipsize*2, size/3.5], [0.1, size/3.5], h=clipsize*3, anchor=BOT); - } - } + // Horiz Wall Clips + up(clipthick+strut+$slop*2) { + xcopies(w*(size-strut)+strut) { + prismoid([clipsize*2, size/3.5], [0.1, size/3.5], h=clipsize*3, anchor=BOT); + } + } - // Middle plugs - for (xcol = [0:w-1]) { - right((xcol-(w-1)/2)*(size-strut)) { - difference() { - // Start with octagon to fit sides. - up(clipthick-0.01) { - zrot(180/8) cylinder(h=strut, d1=cyld-4*$slop, d2=cyld-4*$slop-1, center=false, $fn=8); - } + // Middle plugs + for (xcol = [0:w-1]) { + right((xcol-(w-1)/2)*(size-strut)) { + difference() { + // Start with octagon to fit sides. + up(clipthick-0.01) { + zrot(180/8) cylinder(h=strut, d1=cyld-4*$slop, d2=cyld-4*$slop-1, center=false, $fn=8); + } - // Bevel to fit. - up(clipthick+strut) { - ycopies(size-2*strut-4*$slop) { - chamfer_mask_x(l=size-strut, chamfer=strut*2/3); - } - } + // Bevel to fit. + up(clipthick+strut) { + ycopies(size-2*strut-4*$slop) { + chamfer_mask_x(l=size-strut, chamfer=strut*2/3); + } + } - // Cut out X for possible top mount. - zrot_copies([-45, 45]) { - cube([size*3, strut/sqrt(2)+2*$slop, size*3], center=true); - } - } - } - } - } - children(); - } + // Cut out X for possible top mount. + zrot_copies([-45, 45]) { + cube([size*3, strut/sqrt(2)+2*$slop, size*3], center=true); + } + } + } + } + } + children(); + } } @@ -253,49 +253,49 @@ module cubetruss_foot(w=1, size=undef, strut=undef, clipthick=undef, anchor=CENT // cubetruss_joiner(w=1, vert=true); // cubetruss_joiner(w=2, vert=true, anchor=BOT); module cubetruss_joiner(w=1, vert=true, size=undef, strut=undef, clipthick=undef, anchor=CENTER, spin=0, orient=UP) { - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - clipsize = 0.5; - s = [cubetruss_dist(w,1)+2*clipthick, cubetruss_dist(2,0)-0.1, strut+clipthick]; - attachable(anchor,spin,orient, size=s, offset=[0,0,-(clipthick-strut)/2]) { - down(clipthick) { - // Base - cube([w*(size-strut)+strut+2*clipthick, size, clipthick], anchor=BOT); + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; + clipsize = 0.5; + s = [cubetruss_dist(w,1)+2*clipthick, cubetruss_dist(2,0)-0.1, strut+clipthick]; + attachable(anchor,spin,orient, size=s, offset=[0,0,-(clipthick-strut)/2]) { + down(clipthick) { + // Base + cube([w*(size-strut)+strut+2*clipthick, size, clipthick], anchor=BOT); - xcopies(w*(size-strut)+strut+clipthick) { - cube([clipthick, size, clipthick+strut*3/4], anchor=BOT); - } + xcopies(w*(size-strut)+strut+clipthick) { + cube([clipthick, size, clipthick+strut*3/4], anchor=BOT); + } - // Use feet - ycopies(size) { - cubetruss_foot(w=w, size=size, strut=strut, clipthick=clipthick, anchor=BOT); - } + // Use feet + ycopies(size) { + cubetruss_foot(w=w, size=size, strut=strut, clipthick=clipthick, anchor=BOT); + } - if (vert) { - // Vert Walls - xcopies(w*(size-strut)+strut+clipthick) { - up(clipthick-0.01) { - prismoid([clipthick, size], [clipthick, 2*strut+2*clipthick], h=size*0.6, anchor=BOT); - } - } + if (vert) { + // Vert Walls + xcopies(w*(size-strut)+strut+clipthick) { + up(clipthick-0.01) { + prismoid([clipthick, size], [clipthick, 2*strut+2*clipthick], h=size*0.6, anchor=BOT); + } + } - // Vert Wall Clips - up(size/2) { - xflip_copy(offset=(w*(size-strut)+strut+0.02)/2) { - yflip_copy(offset=strut+$slop/2) { - yrot(-90) { - back_half() { - prismoid([size/3.5, clipthick*2], [size/3.5-4*2*clipsize, 0.1], h=2*clipsize, anchor=BOT); - } - } - } - } - } - } - } - children(); - } + // Vert Wall Clips + up(size/2) { + xflip_copy(offset=(w*(size-strut)+strut+0.02)/2) { + yflip_copy(offset=strut+$slop/2) { + yrot(-90) { + back_half() { + prismoid([size/3.5, clipthick*2], [size/3.5-4*2*clipsize, 0.1], h=2*clipsize, anchor=BOT); + } + } + } + } + } + } + } + children(); + } } @@ -314,29 +314,29 @@ module cubetruss_joiner(w=1, vert=true, size=undef, strut=undef, clipthick=undef // cubetruss_uclip(dual=false); // cubetruss_uclip(dual=true); module cubetruss_uclip(dual=true, size=undef, strut=undef, clipthick=undef, anchor=CENTER, spin=0, orient=UP) { - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - clipsize = 0.5; - s = [(dual?2:1)*strut+2*clipthick+$slop, strut+2*clipthick, size/3.5]; - attachable(anchor,spin,orient, size=s) { - union() { - difference() { - cube(s, center=true); - back(clipthick) cube([(dual?2:1)*strut+$slop, strut+2*clipthick, size+1], center=true); - } - back((strut+$slop)/2) { - xflip_copy(offset=(dual?1:0.5)*strut+$slop/2) { - yrot(-90) { - back_half() { - prismoid([size/3.5, clipthick*1.87], [size/3.5, 0.1], h=clipsize, anchor=BOT); - } - } - } - } - } - children(); - } + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; + clipsize = 0.5; + s = [(dual?2:1)*strut+2*clipthick+$slop, strut+2*clipthick, size/3.5]; + attachable(anchor,spin,orient, size=s) { + union() { + difference() { + cube(s, center=true); + back(clipthick) cube([(dual?2:1)*strut+$slop, strut+2*clipthick, size+1], center=true); + } + back((strut+$slop)/2) { + xflip_copy(offset=(dual?1:0.5)*strut+$slop/2) { + yrot(-90) { + back_half() { + prismoid([size/3.5, clipthick*1.87], [size/3.5, 0.1], h=clipsize, anchor=BOT); + } + } + } + } + } + children(); + } } @@ -363,48 +363,48 @@ module cubetruss_uclip(dual=true, size=undef, strut=undef, clipthick=undef, anch // cubetruss(extents=[1,4,2]); // cubetruss(extents=[1,4,2], bracing=false); module cubetruss(extents=6, clips=[], bracing=undef, size=undef, strut=undef, clipthick=undef, anchor=CENTER, spin=0, orient=UP) { - clips = is_vector(clips)? [clips] : clips; - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - bracing = is_undef(bracing)? $cubetruss_bracing : bracing; - clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - extents = is_vector(extents)? point3d(extents,fill=1) : [1,extents,1]; - w = extents[0]; - l = extents[1]; - h = extents[2]; - s = [cubetruss_dist(w,1), cubetruss_dist(l,1), cubetruss_dist(h,1)]; - attachable(anchor,spin,orient, size=s) { - union() { - for (zrow = [0:h-1]) { - up((zrow-(h-1)/2)*(size-strut)) { - for (xcol = [0:w-1]) { - right((xcol-(w-1)/2)*(size-strut)) { - for (ycol = [0:l-1]) { - back((ycol-(l-1)/2)*(size-strut)) { - cubetruss_segment(size=size, strut=strut, bracing=bracing); - } - } - } - } - } - } - if (clipthick > 0) { - for (vec = clips) { - exts = vabs(rot(from=FWD, to=vec, p=extents)); - rot(from=FWD,to=vec) { - for (zrow = [0:1:exts.z-1]) { - up((zrow-(exts.z-1)/2)*(size-strut)) { - fwd((exts.y*(size-strut)+strut)/2) { - cubetruss_clip(size=size, strut=strut, extents=exts.x, clipthick=clipthick); - } - } - } - } - } - } - } - children(); - } + clips = is_vector(clips)? [clips] : clips; + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + bracing = is_undef(bracing)? $cubetruss_bracing : bracing; + clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; + extents = is_vector(extents)? point3d(extents,fill=1) : [1,extents,1]; + w = extents[0]; + l = extents[1]; + h = extents[2]; + s = [cubetruss_dist(w,1), cubetruss_dist(l,1), cubetruss_dist(h,1)]; + attachable(anchor,spin,orient, size=s) { + union() { + for (zrow = [0:h-1]) { + up((zrow-(h-1)/2)*(size-strut)) { + for (xcol = [0:w-1]) { + right((xcol-(w-1)/2)*(size-strut)) { + for (ycol = [0:l-1]) { + back((ycol-(l-1)/2)*(size-strut)) { + cubetruss_segment(size=size, strut=strut, bracing=bracing); + } + } + } + } + } + } + if (clipthick > 0) { + for (vec = clips) { + exts = vabs(rot(from=FWD, to=vec, p=extents)); + rot(from=FWD,to=vec) { + for (zrow = [0:1:exts.z-1]) { + up((zrow-(exts.z-1)/2)*(size-strut)) { + fwd((exts.y*(size-strut)+strut)/2) { + cubetruss_clip(size=size, strut=strut, extents=exts.x, clipthick=clipthick); + } + } + } + } + } + } + } + children(); + } } @@ -430,53 +430,53 @@ module cubetruss(extents=6, clips=[], bracing=undef, size=undef, strut=undef, cl // cubetruss_corner(extents=[3,0,3,0,2]); // cubetruss_corner(extents=[3,3,3,3,2]); module cubetruss_corner(h=1, extents=[1,1,0,0,1], bracing=undef, size=undef, strut=undef, clipthick=undef, anchor=CENTER, spin=0, orient=UP) { - size = is_undef(size)? $cubetruss_size : size; - strut = is_undef(strut)? $cubetruss_strut_size : strut; - bracing = is_undef(bracing)? $cubetruss_bracing : bracing; - clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; - exts = is_vector(extents)? list_fit(extents,5,fill=0) : [extents, extents, 0, 0, extents]; - s = [cubetruss_dist(1+exts[0]+exts[2],1), cubetruss_dist(1+exts[1]+exts[3],1), cubetruss_dist(h+exts[4],1)]; - offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(exts[4],0)]/2; - attachable(anchor,spin,orient, size=s, offset=offset) { - union() { - for (zcol = [0:h-1]) { - up((size-strut+0.01)*zcol) { - cubetruss_segment(size=size, strut=strut, bracing=bracing); - } - } - for (dir = [0:3]) { - if (exts[dir] != undef && exts[dir] > 0) { - zrot(dir*90) { - for (zcol = [0:h-1]) { - up((size-strut+0.01)*zcol) { - for (i = [1:exts[dir]]) { - right((size-strut+0.01)*i) cubetruss_segment(size=size, strut=strut, bracing=bracing); - } - if (clipthick > 0) { - right(exts[dir]*(size-strut)+size/2) { - zrot(90) cubetruss_clip(size=size, strut=strut, clipthick=clipthick); - } - } - } - } - } - } - } - if (exts[4] != undef && exts[4] > 0) { - for (i = [1:exts[4]]) { - up((size-strut+0.01)*(i+h-1)) cubetruss_segment(size=size, strut=strut, bracing=bracing); - } - if (clipthick > 0) { - up((exts[4]+h-1)*(size-strut)+size/2) { - xrot(-90) cubetruss_clip(size=size, strut=strut, clipthick=clipthick); - } - } - } - } - children(); - } + size = is_undef(size)? $cubetruss_size : size; + strut = is_undef(strut)? $cubetruss_strut_size : strut; + bracing = is_undef(bracing)? $cubetruss_bracing : bracing; + clipthick = is_undef(clipthick)? $cubetruss_clip_thickness : clipthick; + exts = is_vector(extents)? list_fit(extents,5,fill=0) : [extents, extents, 0, 0, extents]; + s = [cubetruss_dist(1+exts[0]+exts[2],1), cubetruss_dist(1+exts[1]+exts[3],1), cubetruss_dist(h+exts[4],1)]; + offset = [cubetruss_dist(exts[0]-exts[2],0), cubetruss_dist(exts[1]-exts[3],0), cubetruss_dist(exts[4],0)]/2; + attachable(anchor,spin,orient, size=s, offset=offset) { + union() { + for (zcol = [0:h-1]) { + up((size-strut+0.01)*zcol) { + cubetruss_segment(size=size, strut=strut, bracing=bracing); + } + } + for (dir = [0:3]) { + if (exts[dir] != undef && exts[dir] > 0) { + zrot(dir*90) { + for (zcol = [0:h-1]) { + up((size-strut+0.01)*zcol) { + for (i = [1:exts[dir]]) { + right((size-strut+0.01)*i) cubetruss_segment(size=size, strut=strut, bracing=bracing); + } + if (clipthick > 0) { + right(exts[dir]*(size-strut)+size/2) { + zrot(90) cubetruss_clip(size=size, strut=strut, clipthick=clipthick); + } + } + } + } + } + } + } + if (exts[4] != undef && exts[4] > 0) { + for (i = [1:exts[4]]) { + up((size-strut+0.01)*(i+h-1)) cubetruss_segment(size=size, strut=strut, bracing=bracing); + } + if (clipthick > 0) { + up((exts[4]+h-1)*(size-strut)+size/2) { + xrot(-90) cubetruss_clip(size=size, strut=strut, clipthick=clipthick); + } + } + } + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/debug.scad b/debug.scad index b183e7a..3ff5704 100644 --- a/debug.scad +++ b/debug.scad @@ -28,33 +28,33 @@ include // polyline = [for (a=[0:30:210]) 10*[cos(a), sin(a), sin(a)]]; // trace_polyline(polyline, showpts=true, size=0.5, color="lightgreen"); module trace_polyline(pline, closed=false, showpts=false, N=1, size=1, color="yellow") { - assert(is_path(pline),"Input pline is not a path"); - sides = segs(size/2); - pline = closed? close_path(pline) : pline; - if (showpts) { - for (i = [0:1:len(pline)-1]) { - translate(pline[i]) { - if (i%N == 0) { - color("blue") sphere(d=size*2.5, $fn=8); - } else { - color("red") { - cylinder(d=size/2, h=size*3, center=true, $fn=8); - xrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8); - yrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8); - } - } - } - } - } - if (N!=3) { - color(color) stroke(path3d(pline), width=size, $fn=8); - } else { - for (i = [0:1:len(pline)-2]) { - if (N!=3 || (i%N) != 1) { - color(color) extrude_from_to(pline[i], pline[i+1]) circle(d=size, $fn=sides); - } - } - } + assert(is_path(pline),"Input pline is not a path"); + sides = segs(size/2); + pline = closed? close_path(pline) : pline; + if (showpts) { + for (i = [0:1:len(pline)-1]) { + translate(pline[i]) { + if (i%N == 0) { + color("blue") sphere(d=size*2.5, $fn=8); + } else { + color("red") { + cylinder(d=size/2, h=size*3, center=true, $fn=8); + xrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8); + yrot(90) cylinder(d=size/2, h=size*3, center=true, $fn=8); + } + } + } + } + } + if (N!=3) { + color(color) stroke(path3d(pline), width=size, $fn=8); + } else { + for (i = [0:1:len(pline)-2]) { + if (N!=3 || (i%N) != 1) { + color(color) extrude_from_to(pline[i], pline[i+1]) circle(d=size, $fn=sides); + } + } + } } @@ -77,44 +77,44 @@ module trace_polyline(pline, closed=false, showpts=false, N=1, size=1, color="ye // ); module debug_polygon(points, paths=undef, convexity=2, size=1) { - pths = is_undef(paths)? [for (i=[0:1:len(points)-1]) i] : is_num(paths[0])? [paths] : paths; - echo(points=points); - echo(paths=paths); - linear_extrude(height=0.01, convexity=convexity, center=true) { - polygon(points=points, paths=paths, convexity=convexity); - } - for (i = [0:1:len(points)-1]) { - color("red") { - up(0.2) { - translate(points[i]) { - linear_extrude(height=0.1, convexity=10, center=true) { - text(text=str(i), size=size, halign="center", valign="center"); - } - } - } - } - } - for (j = [0:1:len(paths)-1]) { - path = paths[j]; - translate(points[path[0]]) { - color("cyan") up(0.1) cylinder(d=size*1.5, h=0.01, center=false, $fn=12); - } - translate(points[path[len(path)-1]]) { - color("pink") up(0.11) cylinder(d=size*1.5, h=0.01, center=false, $fn=4); - } - for (i = [0:1:len(path)-1]) { - midpt = (points[path[i]] + points[path[(i+1)%len(path)]])/2; - color("blue") { - up(0.2) { - translate(midpt) { - linear_extrude(height=0.1, convexity=10, center=true) { - text(text=str(chr(65+j),i), size=size/2, halign="center", valign="center"); - } - } - } - } - } - } + pths = is_undef(paths)? [for (i=[0:1:len(points)-1]) i] : is_num(paths[0])? [paths] : paths; + echo(points=points); + echo(paths=paths); + linear_extrude(height=0.01, convexity=convexity, center=true) { + polygon(points=points, paths=paths, convexity=convexity); + } + for (i = [0:1:len(points)-1]) { + color("red") { + up(0.2) { + translate(points[i]) { + linear_extrude(height=0.1, convexity=10, center=true) { + text(text=str(i), size=size, halign="center", valign="center"); + } + } + } + } + } + for (j = [0:1:len(paths)-1]) { + path = paths[j]; + translate(points[path[0]]) { + color("cyan") up(0.1) cylinder(d=size*1.5, h=0.01, center=false, $fn=12); + } + translate(points[path[len(path)-1]]) { + color("pink") up(0.11) cylinder(d=size*1.5, h=0.01, center=false, $fn=4); + } + for (i = [0:1:len(path)-1]) { + midpt = (points[path[i]] + points[path[(i+1)%len(path)]])/2; + color("blue") { + up(0.2) { + translate(midpt) { + linear_extrude(height=0.1, convexity=10, center=true) { + text(text=str(chr(65+j),i), size=size/2, halign="center", valign="center"); + } + } + } + } + } + } } @@ -138,29 +138,29 @@ module debug_polygon(points, paths=undef, convexity=2, size=1) // polyhedron(points=verts, faces=faces); // } module debug_vertices(vertices, size=1, disabled=false) { - if (!disabled) { - echo(vertices=vertices); - color("blue") { - for (i = [0:1:len(vertices)-1]) { - v = vertices[i]; - translate(v) { - up(size/8) zrot($vpr[2]) xrot(90) { - linear_extrude(height=size/10, center=true, convexity=10) { - text(text=str(i), size=size, halign="center"); - } - } - sphere(size/10); - } - } - } - } - if ($children > 0) { - if (!disabled) { - color([0.2, 1.0, 0, 0.5]) children(); - } else { - children(); - } - } + if (!disabled) { + echo(vertices=vertices); + color("blue") { + for (i = [0:1:len(vertices)-1]) { + v = vertices[i]; + translate(v) { + up(size/8) zrot($vpr[2]) xrot(90) { + linear_extrude(height=size/10, center=true, convexity=10) { + text(text=str(i), size=size, halign="center"); + } + } + sphere(size/10); + } + } + } + } + if ($children > 0) { + if (!disabled) { + color([0.2, 1.0, 0, 0.5]) children(); + } else { + children(); + } + } } @@ -183,46 +183,46 @@ module debug_vertices(vertices, size=1, disabled=false) { // polyhedron(points=verts, faces=faces); // } module debug_faces(vertices, faces, size=1, disabled=false) { - if (!disabled) { - vlen = len(vertices); - color("red") { - for (i = [0:1:len(faces)-1]) { - face = faces[i]; - if (face[0] < 0 || face[1] < 0 || face[2] < 0 || face[0] >= vlen || face[1] >= vlen || face[2] >= vlen) { - echo("BAD FACE: ", vlen=vlen, face=face); - } else { - v0 = vertices[face[0]]; - v1 = vertices[face[1]]; - v2 = vertices[face[2]]; - c = mean(select(vertices,face)); - dv0 = unit(v1 - v0); - dv1 = unit(v2 - v0); - nrm0 = unit(cross(dv0, dv1)); - nrm1 = [0, 0, 1]; - axis = unit(cross(nrm0, nrm1)); - ang = vector_angle(nrm0, nrm1); - theta = atan2(nrm0[1], nrm0[0]); - translate(c) { - rotate(a=180-ang, v=axis) { - zrot(theta-90) - linear_extrude(height=size/10, center=true, convexity=10) { - union() { - text(text=str(i), size=size, halign="center"); - text(text=str("_"), size=size, halign="center"); - } - } - } - } - } - } - } - } - debug_vertices(vertices, size=size, disabled=disabled) { - children(); - } - if (!disabled) { - echo(faces=faces); - } + if (!disabled) { + vlen = len(vertices); + color("red") { + for (i = [0:1:len(faces)-1]) { + face = faces[i]; + if (face[0] < 0 || face[1] < 0 || face[2] < 0 || face[0] >= vlen || face[1] >= vlen || face[2] >= vlen) { + echo("BAD FACE: ", vlen=vlen, face=face); + } else { + v0 = vertices[face[0]]; + v1 = vertices[face[1]]; + v2 = vertices[face[2]]; + c = mean(select(vertices,face)); + dv0 = unit(v1 - v0); + dv1 = unit(v2 - v0); + nrm0 = unit(cross(dv0, dv1)); + nrm1 = [0, 0, 1]; + axis = unit(cross(nrm0, nrm1)); + ang = vector_angle(nrm0, nrm1); + theta = atan2(nrm0[1], nrm0[0]); + translate(c) { + rotate(a=180-ang, v=axis) { + zrot(theta-90) + linear_extrude(height=size/10, center=true, convexity=10) { + union() { + text(text=str(i), size=size, halign="center"); + text(text=str("_"), size=size, halign="center"); + } + } + } + } + } + } + } + } + debug_vertices(vertices, size=size, disabled=disabled) { + children(); + } + if (!disabled) { + echo(faces=faces); + } } @@ -245,9 +245,9 @@ module debug_faces(vertices, faces, size=1, disabled=false) { // faces = [[0,1,2], [5,4,3], [0,3,4], [0,4,1], [1,4,5], [1,5,2], [2,5,3], [2,3,0]]; // debug_polyhedron(points=verts, faces=faces, txtsize=1); module debug_polyhedron(points, faces, convexity=10, txtsize=1, disabled=false) { - debug_faces(vertices=points, faces=faces, size=txtsize, disabled=disabled) { - polyhedron(points=points, faces=faces, convexity=convexity); - } + debug_faces(vertices=points, faces=faces, size=txtsize, disabled=disabled) { + polyhedron(points=points, faces=faces, convexity=convexity); + } } @@ -256,11 +256,11 @@ module debug_polyhedron(points, faces, convexity=10, txtsize=1, disabled=false) // Description: // Return the vectors for all standard anchors. function standard_anchors() = [ - for ( - zv = [TOP, CENTER, BOTTOM], - yv = [FRONT, CENTER, BACK], - xv = [LEFT, CENTER, RIGHT] - ) xv+yv+zv + for ( + zv = [TOP, CENTER, BOTTOM], + yv = [FRONT, CENTER, BACK], + xv = [LEFT, CENTER, RIGHT] + ) xv+yv+zv ]; @@ -277,19 +277,19 @@ function standard_anchors() = [ // Example: // anchor_arrow(s=20); module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow") { - $fn=12; - recolor("gray") spheroid(d=s/6) { - attach(CENTER,BOT) recolor(color) cyl(h=s*2/3, d=s/15) { - attach(TOP,BOT) cyl(h=s/3, d1=s/5, d2=0) { - if(flag) { - position(BOT) - recolor([1,0.5,0.5]) - cuboid([s/100, s/6, s/4], anchor=FRONT+BOT); - } - children(); - } - } - } + $fn=12; + recolor("gray") spheroid(d=s/6) { + attach(CENTER,BOT) recolor(color) cyl(h=s*2/3, d=s/15) { + attach(TOP,BOT) cyl(h=s/3, d1=s/5, d2=0) { + if(flag) { + position(BOT) + recolor([1,0.5,0.5]) + cuboid([s/100, s/6, s/4], anchor=FRONT+BOT); + } + children(); + } + } + } } @@ -303,8 +303,8 @@ module anchor_arrow(s=10, color=[0.333,0.333,1], flag=true, $tags="anchor-arrow" // Example(FlatSpin): // show_internal_anchors() cube(50, center=true) show_anchors(); module show_internal_anchors(opacity=0.2) { - show("anchor-arrow") children() show_anchors(); - hide("anchor-arrow") recolor(list_pad(point3d($color),4,fill=opacity)) children(); + show("anchor-arrow") children() show_anchors(); + hide("anchor-arrow") recolor(list_pad(point3d($color),4,fill=opacity)) children(); } @@ -318,29 +318,29 @@ module show_internal_anchors(opacity=0.2) { // Example(FlatSpin): // cube(50, center=true) show_anchors(); module show_anchors(s=10, std=true, custom=true) { - if (std) { - for (anchor=standard_anchors()) { - attach(anchor) anchor_arrow(s); - } - } - if (custom) { - for (anchor=select($parent_geom,-1)) { - attach(anchor[0]) { - anchor_arrow(s, color="cyan"); - recolor("black") - noop($tags="anchor-arrow") { - xrot(90) { - up(s/10) { - linear_extrude(height=0.01, convexity=12, center=true) { - text(text=anchor[0], size=s/4, halign="center", valign="center"); - } - } - } - } - } - } - } - children(); + if (std) { + for (anchor=standard_anchors()) { + attach(anchor) anchor_arrow(s); + } + } + if (custom) { + for (anchor=select($parent_geom,-1)) { + attach(anchor[0]) { + anchor_arrow(s, color="cyan"); + recolor("black") + noop($tags="anchor-arrow") { + xrot(90) { + up(s/10) { + linear_extrude(height=0.01, convexity=12, center=true) { + text(text=anchor[0], size=s/4, halign="center", valign="center"); + } + } + } + } + } + } + } + children(); } @@ -353,12 +353,12 @@ module show_anchors(s=10, std=true, custom=true) { // Examples: // frame_ref(25); module frame_ref(s=15) { - cube(0.01, center=true) { - attach(RIGHT) anchor_arrow(s=s, flag=false, color="red"); - attach(BACK) anchor_arrow(s=s, flag=false, color="green"); - attach(TOP) anchor_arrow(s=s, flag=false, color="blue"); - children(); - } + cube(0.01, center=true) { + attach(RIGHT) anchor_arrow(s=s, flag=false, color="red"); + attach(BACK) anchor_arrow(s=s, flag=false, color="green"); + attach(TOP) anchor_arrow(s=s, flag=false, color="blue"); + children(); + } } @@ -389,63 +389,63 @@ module frame_ref(s=15) { // fwd(50)ruler(300,width=50,labels=true); module ruler(length=100, width=undef, thickness=1, depth=3, labels=false, pipscale=1/3, maxscale=undef, colors=["black","white"], alpha=1.0, unit=1, inch=false, anchor=LEFT+BACK+TOP, spin=0, orient=UP) { - inchfactor = 25.4; - assert(depth<=5, "Cannot render scales smaller than depth=5"); - assert(len(colors)==2, "colors must contain a list of exactly two colors."); - length = inch ? inchfactor * length : length; - unit = inch ? inchfactor*unit : unit; - maxscale = is_def(maxscale)? maxscale : floor(log(length/unit-EPSILON)); - scales = unit * [for(logsize = [maxscale:-1:maxscale-depth+1]) pow(10,logsize)]; - widthfactor = (1-pipscale) / (1-pow(pipscale,depth)); - width = default(width, scales[0]); - widths = width * widthfactor * [for(logsize = [0:-1:-depth+1]) pow(pipscale,-logsize)]; - offsets = concat([0],cumsum(widths)); - attachable(anchor,spin,orient, size=[length,width,thickness]) { - translate([-length/2, -width/2, 0]) - for(i=[0:1:len(scales)-1]) { - count = ceil(length/scales[i]); - fontsize = 0.5*min(widths[i], scales[i]/ceil(log(count*scales[i]/unit))); - back(offsets[i]) { - xcopies(scales[i], n=count, sp=[0,0,0]) union() { - actlen = ($idx0 ? quantup(widths[i],1/1024) : widths[i]; // What is the i>0 test supposed to do here? - cube([quantup(actlen,1/1024),quantup(w,1/1024),thickness], anchor=FRONT+LEFT); - } - mark = - i == 0 && $idx % 10 == 0 && $idx != 0 ? 0 : - i == 0 && $idx % 10 == 9 && $idx != count-1 ? 1 : - $idx % 10 == 4 ? 1 : - $idx % 10 == 5 ? 0 : -1; - flip = 1-mark*2; - if (mark >= 0) { - marklength = min(widths[i]/2, scales[i]*2); - markwidth = marklength*0.4; - translate([mark*scales[i], widths[i], 0]) { - color(colors[1-$idx%2], alpha=alpha) { - linear_extrude(height=thickness+scales[i]/100, convexity=2, center=true) { - polygon(scale([flip*markwidth, marklength],p=[[0,0], [1, -1], [0,-0.9]])); - } - } - } - } - if (labels && scales[i]/unit+EPSILON >= 1) { - color(colors[($idx+1)%2], alpha=alpha) { - linear_extrude(height=thickness+scales[i]/100, convexity=2, center=true) { - back(scales[i]*.02) { - text(text=str( $idx * scales[i] / unit), size=fontsize, halign="left", valign="baseline"); - } - } - } - } + inchfactor = 25.4; + assert(depth<=5, "Cannot render scales smaller than depth=5"); + assert(len(colors)==2, "colors must contain a list of exactly two colors."); + length = inch ? inchfactor * length : length; + unit = inch ? inchfactor*unit : unit; + maxscale = is_def(maxscale)? maxscale : floor(log(length/unit-EPSILON)); + scales = unit * [for(logsize = [maxscale:-1:maxscale-depth+1]) pow(10,logsize)]; + widthfactor = (1-pipscale) / (1-pow(pipscale,depth)); + width = default(width, scales[0]); + widths = width * widthfactor * [for(logsize = [0:-1:-depth+1]) pow(pipscale,-logsize)]; + offsets = concat([0],cumsum(widths)); + attachable(anchor,spin,orient, size=[length,width,thickness]) { + translate([-length/2, -width/2, 0]) + for(i=[0:1:len(scales)-1]) { + count = ceil(length/scales[i]); + fontsize = 0.5*min(widths[i], scales[i]/ceil(log(count*scales[i]/unit))); + back(offsets[i]) { + xcopies(scales[i], n=count, sp=[0,0,0]) union() { + actlen = ($idx0 ? quantup(widths[i],1/1024) : widths[i]; // What is the i>0 test supposed to do here? + cube([quantup(actlen,1/1024),quantup(w,1/1024),thickness], anchor=FRONT+LEFT); + } + mark = + i == 0 && $idx % 10 == 0 && $idx != 0 ? 0 : + i == 0 && $idx % 10 == 9 && $idx != count-1 ? 1 : + $idx % 10 == 4 ? 1 : + $idx % 10 == 5 ? 0 : -1; + flip = 1-mark*2; + if (mark >= 0) { + marklength = min(widths[i]/2, scales[i]*2); + markwidth = marklength*0.4; + translate([mark*scales[i], widths[i], 0]) { + color(colors[1-$idx%2], alpha=alpha) { + linear_extrude(height=thickness+scales[i]/100, convexity=2, center=true) { + polygon(scale([flip*markwidth, marklength],p=[[0,0], [1, -1], [0,-0.9]])); + } + } + } + } + if (labels && scales[i]/unit+EPSILON >= 1) { + color(colors[($idx+1)%2], alpha=alpha) { + linear_extrude(height=thickness+scales[i]/100, convexity=2, center=true) { + back(scales[i]*.02) { + text(text=str( $idx * scales[i] / unit), size=fontsize, halign="left", valign="baseline"); + } + } + } + } - } - } - } - children(); - } + } + } + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/distributors.scad b/distributors.scad index b008113..603e50d 100644 --- a/distributors.scad +++ b/distributors.scad @@ -33,12 +33,12 @@ // move_copies([[-25,-25,0], [25,-25,0], [0,0,50], [0,25,0]]) sphere(r=10); module move_copies(a=[[0,0,0]]) { - assert(is_list(a)); - for ($idx = idx(a)) { - $pos = a[$idx]; - assert(is_vector($pos)); - translate($pos) children(); - } + assert(is_list(a)); + for ($idx = idx(a)) { + $pos = a[$idx]; + assert(is_vector($pos)); + translate($pos) children(); + } } @@ -80,30 +80,30 @@ module move_copies(a=[[0,0,0]]) // } module line_of(p1, p2, spacing, l, n) { - ll = ( - !is_undef(l)? scalar_vec3(l, 0) : - (!is_undef(spacing) && !is_undef(n))? (n * scalar_vec3(spacing, 0)) : - (!is_undef(p1) && !is_undef(p2))? point3d(p2-p1) : - undef - ); - cnt = ( - !is_undef(n)? n : - (!is_undef(spacing) && !is_undef(ll))? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001) : - 2 - ); - spc = ( - is_undef(spacing)? (ll/(cnt-1)) : - is_num(spacing) && !is_undef(ll)? (ll/(cnt-1)) : - scalar_vec3(spacing, 0) - ); - assert(!is_undef(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `line_of()`."); - spos = !is_undef(p1)? point3d(p1) : -(cnt-1)/2 * spc; - for (i=[0:1:cnt-1]) { - pos = i * spc + spos; - $pos = pos; - $idx = i; - translate(pos) children(); - } + ll = ( + !is_undef(l)? scalar_vec3(l, 0) : + (!is_undef(spacing) && !is_undef(n))? (n * scalar_vec3(spacing, 0)) : + (!is_undef(p1) && !is_undef(p2))? point3d(p2-p1) : + undef + ); + cnt = ( + !is_undef(n)? n : + (!is_undef(spacing) && !is_undef(ll))? floor(norm(ll) / norm(scalar_vec3(spacing, 0)) + 1.000001) : + 2 + ); + spc = ( + is_undef(spacing)? (ll/(cnt-1)) : + is_num(spacing) && !is_undef(ll)? (ll/(cnt-1)) : + scalar_vec3(spacing, 0) + ); + assert(!is_undef(cnt), "Need two of `spacing`, 'l', 'n', or `p1`/`p2` arguments in `line_of()`."); + spos = !is_undef(p1)? point3d(p1) : -(cnt-1)/2 * spc; + for (i=[0:1:cnt-1]) { + pos = i * spc + spos; + $pos = pos; + $idx = i; + translate(pos) children(); + } } @@ -138,7 +138,7 @@ module line_of(p1, p2, spacing, l, n) // } module xcopies(spacing, n, l, sp) { - line_of(l=l*RIGHT, spacing=spacing*RIGHT, n=n, p1=sp) children(); + line_of(l=l*RIGHT, spacing=spacing*RIGHT, n=n, p1=sp) children(); } @@ -173,7 +173,7 @@ module xcopies(spacing, n, l, sp) // } module ycopies(spacing, n, l, sp) { - line_of(l=l*BACK, spacing=spacing*BACK, n=n, p1=sp) children(); + line_of(l=l*BACK, spacing=spacing*BACK, n=n, p1=sp) children(); } @@ -208,7 +208,7 @@ module ycopies(spacing, n, l, sp) // } module zcopies(spacing, n, l, sp) { - line_of(l=l*UP, spacing=spacing*UP, n=n, p1=sp) children(); + line_of(l=l*UP, spacing=spacing*UP, n=n, p1=sp) children(); } @@ -243,18 +243,18 @@ module zcopies(spacing, n, l, sp) // } module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef) { - gaps = ($children < 2)? [0] : - !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : - [for (i=[0:1:$children-2]) 0]; - spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); - gaps2 = [for (gap = gaps) gap+spc]; - spos = dir * -sum(gaps2)/2; - for (i=[0:1:$children-1]) { - totspc = sum(concat([0], slice(gaps2, 0, i))); - $pos = spos + totspc * dir; - $idx = i; - translate($pos) children(i); - } + gaps = ($children < 2)? [0] : + !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : + [for (i=[0:1:$children-2]) 0]; + spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); + gaps2 = [for (gap = gaps) gap+spc]; + spos = dir * -sum(gaps2)/2; + for (i=[0:1:$children-1]) { + totspc = sum(concat([0], slice(gaps2, 0, i))); + $pos = spos + totspc * dir; + $idx = i; + translate($pos) children(i); + } } @@ -287,19 +287,19 @@ module distribute(spacing=undef, sizes=undef, dir=RIGHT, l=undef) // } module xdistribute(spacing=10, sizes=undef, l=undef) { - dir = RIGHT; - gaps = ($children < 2)? [0] : - !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : - [for (i=[0:1:$children-2]) 0]; - spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); - gaps2 = [for (gap = gaps) gap+spc]; - spos = dir * -sum(gaps2)/2; - for (i=[0:1:$children-1]) { - totspc = sum(concat([0], slice(gaps2, 0, i))); - $pos = spos + totspc * dir; - $idx = i; - translate($pos) children(i); - } + dir = RIGHT; + gaps = ($children < 2)? [0] : + !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : + [for (i=[0:1:$children-2]) 0]; + spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); + gaps2 = [for (gap = gaps) gap+spc]; + spos = dir * -sum(gaps2)/2; + for (i=[0:1:$children-1]) { + totspc = sum(concat([0], slice(gaps2, 0, i))); + $pos = spos + totspc * dir; + $idx = i; + translate($pos) children(i); + } } @@ -332,19 +332,19 @@ module xdistribute(spacing=10, sizes=undef, l=undef) // } module ydistribute(spacing=10, sizes=undef, l=undef) { - dir = BACK; - gaps = ($children < 2)? [0] : - !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : - [for (i=[0:1:$children-2]) 0]; - spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); - gaps2 = [for (gap = gaps) gap+spc]; - spos = dir * -sum(gaps2)/2; - for (i=[0:1:$children-1]) { - totspc = sum(concat([0], slice(gaps2, 0, i))); - $pos = spos + totspc * dir; - $idx = i; - translate($pos) children(i); - } + dir = BACK; + gaps = ($children < 2)? [0] : + !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : + [for (i=[0:1:$children-2]) 0]; + spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); + gaps2 = [for (gap = gaps) gap+spc]; + spos = dir * -sum(gaps2)/2; + for (i=[0:1:$children-1]) { + totspc = sum(concat([0], slice(gaps2, 0, i))); + $pos = spos + totspc * dir; + $idx = i; + translate($pos) children(i); + } } @@ -377,19 +377,19 @@ module ydistribute(spacing=10, sizes=undef, l=undef) // } module zdistribute(spacing=10, sizes=undef, l=undef) { - dir = UP; - gaps = ($children < 2)? [0] : - !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : - [for (i=[0:1:$children-2]) 0]; - spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); - gaps2 = [for (gap = gaps) gap+spc]; - spos = dir * -sum(gaps2)/2; - for (i=[0:1:$children-1]) { - totspc = sum(concat([0], slice(gaps2, 0, i))); - $pos = spos + totspc * dir; - $idx = i; - translate($pos) children(i); - } + dir = UP; + gaps = ($children < 2)? [0] : + !is_undef(sizes)? [for (i=[0:1:$children-2]) sizes[i]/2 + sizes[i+1]/2] : + [for (i=[0:1:$children-2]) 0]; + spc = !is_undef(l)? ((l - sum(gaps)) / ($children-1)) : default(spacing, 10); + gaps2 = [for (gap = gaps) gap+spc]; + spos = dir * -sum(gaps2)/2; + for (i=[0:1:$children-1]) { + totspc = sum(concat([0], slice(gaps2, 0, i))); + $pos = spos + totspc * dir; + $idx = i; + translate($pos) children(i); + } } @@ -450,73 +450,73 @@ module zdistribute(spacing=10, sizes=undef, l=undef) // } module grid2d(spacing, n, size, stagger=false, inside=undef) { - assert(in_list(stagger, [false, true, "alt"])); - bounds = is_undef(inside)? undef : - is_path(inside)? pointlist_bounds(inside) : - assert(is_region(inside)) - pointlist_bounds(flatten(inside)); - size = is_num(size)? [size, size] : - is_vector(size)? assert(len(size)==2) size : - bounds!=undef? [ - for (i=[0:1]) 2*max(abs(bounds[0][i]),bounds[1][i]) - ] : undef; - spacing = is_num(spacing)? ( - stagger!=false? polar_to_xy(spacing,60) : - [spacing,spacing] - ) : - is_vector(spacing)? assert(len(spacing)==2) spacing : - size!=undef? ( - is_num(n)? vdiv(size,(n-1)*[1,1]) : - is_vector(n)? assert(len(n)==2) vdiv(size,n-[1,1]) : - vdiv(size,(stagger==false? [1,1] : [2,2])) - ) : - undef; - n = is_num(n)? [n,n] : - is_vector(n)? assert(len(n)==2) n : - size!=undef && spacing!=undef? vfloor(vdiv(size,spacing))+[1,1] : - [2,2]; - offset = vmul(spacing, n-[1,1])/2; - if (stagger == false) { - for (row = [0:1:n.y-1]) { - for (col = [0:1:n.x-1]) { - pos = vmul([col,row],spacing) - offset; - if ( - is_undef(inside) || - (is_path(inside) && point_in_polygon(pos, inside)>=0) || - (is_region(inside) && point_in_region(pos, inside)>=0) - ) { - $col = col; - $row = row; - $pos = pos; - translate(pos) children(); - } - } - } - } else { - // stagger == true or stagger == "alt" - staggermod = (stagger == "alt")? 1 : 0; - cols1 = ceil(n.x/2); - cols2 = n.x - cols1; - for (row = [0:1:n.y-1]) { - rowcols = ((row%2) == staggermod)? cols1 : cols2; - if (rowcols > 0) { - for (col = [0:1:rowcols-1]) { - rowdx = (row%2 != staggermod)? spacing.x : 0; - pos = vmul([2*col,row],spacing) + [rowdx,0] - offset; - if ( - is_undef(inside) || - (is_path(inside) && point_in_polygon(pos, inside)>=0) || - (is_region(inside) && point_in_region(pos, inside)>=0) - ) { - $col = col * 2 + ((row%2!=staggermod)? 1 : 0); - $row = row; - $pos = pos; - translate(pos) children(); - } - } - } - } - } + assert(in_list(stagger, [false, true, "alt"])); + bounds = is_undef(inside)? undef : + is_path(inside)? pointlist_bounds(inside) : + assert(is_region(inside)) + pointlist_bounds(flatten(inside)); + size = is_num(size)? [size, size] : + is_vector(size)? assert(len(size)==2) size : + bounds!=undef? [ + for (i=[0:1]) 2*max(abs(bounds[0][i]),bounds[1][i]) + ] : undef; + spacing = is_num(spacing)? ( + stagger!=false? polar_to_xy(spacing,60) : + [spacing,spacing] + ) : + is_vector(spacing)? assert(len(spacing)==2) spacing : + size!=undef? ( + is_num(n)? vdiv(size,(n-1)*[1,1]) : + is_vector(n)? assert(len(n)==2) vdiv(size,n-[1,1]) : + vdiv(size,(stagger==false? [1,1] : [2,2])) + ) : + undef; + n = is_num(n)? [n,n] : + is_vector(n)? assert(len(n)==2) n : + size!=undef && spacing!=undef? vfloor(vdiv(size,spacing))+[1,1] : + [2,2]; + offset = vmul(spacing, n-[1,1])/2; + if (stagger == false) { + for (row = [0:1:n.y-1]) { + for (col = [0:1:n.x-1]) { + pos = vmul([col,row],spacing) - offset; + if ( + is_undef(inside) || + (is_path(inside) && point_in_polygon(pos, inside)>=0) || + (is_region(inside) && point_in_region(pos, inside)>=0) + ) { + $col = col; + $row = row; + $pos = pos; + translate(pos) children(); + } + } + } + } else { + // stagger == true or stagger == "alt" + staggermod = (stagger == "alt")? 1 : 0; + cols1 = ceil(n.x/2); + cols2 = n.x - cols1; + for (row = [0:1:n.y-1]) { + rowcols = ((row%2) == staggermod)? cols1 : cols2; + if (rowcols > 0) { + for (col = [0:1:rowcols-1]) { + rowdx = (row%2 != staggermod)? spacing.x : 0; + pos = vmul([2*col,row],spacing) + [rowdx,0] - offset; + if ( + is_undef(inside) || + (is_path(inside) && point_in_polygon(pos, inside)>=0) || + (is_region(inside) && point_in_region(pos, inside)>=0) + ) { + $col = col * 2 + ((row%2!=staggermod)? 1 : 0); + $row = row; + $pos = pos; + translate(pos) children(); + } + } + } + } + } } @@ -554,24 +554,24 @@ module grid2d(spacing, n, size, stagger=false, inside=undef) // grid3d(n=[10, 10, 10], spacing=50) color($idx/9) cube(50, center=true); module grid3d(xa=[0], ya=[0], za=[0], n=undef, spacing=undef) { - n = scalar_vec3(n, 1); - spacing = scalar_vec3(spacing, undef); - if (!is_undef(n) && !is_undef(spacing)) { - for (xi = [0:1:n.x-1]) { - for (yi = [0:1:n.y-1]) { - for (zi = [0:1:n.z-1]) { - $idx = [xi,yi,zi]; - $pos = vmul(spacing, $idx - (n-[1,1,1])/2); - translate($pos) children(); - } - } - } - } else { - for (xoff = xa, yoff = ya, zoff = za) { - $pos = [xoff, yoff, zoff]; - translate($pos) children(); - } - } + n = scalar_vec3(n, 1); + spacing = scalar_vec3(spacing, undef); + if (!is_undef(n) && !is_undef(spacing)) { + for (xi = [0:1:n.x-1]) { + for (yi = [0:1:n.y-1]) { + for (zi = [0:1:n.z-1]) { + $idx = [xi,yi,zi]; + $pos = vmul(spacing, $idx - (n-[1,1,1])/2); + translate($pos) children(); + } + } + } + } else { + for (xoff = xa, yoff = ya, zoff = za) { + $pos = [xoff, yoff, zoff]; + translate($pos) children(); + } + } } @@ -642,28 +642,28 @@ module grid3d(xa=[0], ya=[0], za=[0], n=undef, spacing=undef) // color("red",0.333) yrot(90) cylinder(h=20, r1=5, r2=0); module rot_copies(rots=[], v=undef, cp=[0,0,0], n=undef, sa=0, offset=0, delta=[0,0,0], subrot=true) { - sang = sa + offset; - angs = !is_undef(n)? - (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) : - rots==[]? [] : - assert(!is_string(rots), "Argument rots must be an angle, a list of angles, or a range of angles.") - assert(!is_undef(rots[0]), "Argument rots must be an angle, a list of angles, or a range of angles.") - [for (a=rots) a]; - for ($idx = idx(angs)) { - $ang = angs[$idx]; - $axis = v; - translate(cp) { - rotate(a=$ang, v=v) { - translate(delta) { - rot(a=(subrot? sang : $ang), v=v, reverse=true) { - translate(-cp) { - children(); - } - } - } - } - } - } + sang = sa + offset; + angs = !is_undef(n)? + (n<=0? [] : [for (i=[0:1:n-1]) i/n*360+sang]) : + rots==[]? [] : + assert(!is_string(rots), "Argument rots must be an angle, a list of angles, or a range of angles.") + assert(!is_undef(rots[0]), "Argument rots must be an angle, a list of angles, or a range of angles.") + [for (a=rots) a]; + for ($idx = idx(angs)) { + $ang = angs[$idx]; + $axis = v; + translate(cp) { + rotate(a=$ang, v=v) { + translate(delta) { + rot(a=(subrot? sang : $ang), v=v, reverse=true) { + translate(-cp) { + children(); + } + } + } + } + } + } } @@ -720,7 +720,7 @@ module rot_copies(rots=[], v=undef, cp=[0,0,0], n=undef, sa=0, offset=0, delta=[ // color("red",0.333) xrot(-90) cylinder(h=20, r1=5, r2=0, center=true); module xrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) { - rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children(); + rot_copies(rots=rots, v=RIGHT, cp=cp, n=n, sa=sa, delta=[0, r, 0], subrot=subrot) children(); } @@ -777,7 +777,7 @@ module xrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true); module yrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) { - rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children(); + rot_copies(rots=rots, v=BACK, cp=cp, n=n, sa=sa, delta=[-r, 0, 0], subrot=subrot) children(); } @@ -834,7 +834,7 @@ module yrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // color("red",0.333) yrot(-90) cylinder(h=20, r1=5, r2=0, center=true); module zrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) { - rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot) children(); + rot_copies(rots=rots, v=UP, cp=cp, n=n, sa=sa, delta=[r, 0, 0], subrot=subrot) children(); } @@ -880,27 +880,27 @@ module zrot_copies(rots=[], cp=[0,0,0], n=undef, sa=0, r=0, subrot=true) // #cube(size=[10,3,3],center=true); // arc_of(rx=20, ry=10, n=8) cube(size=[10,3,3],center=true); module arc_of( - n=6, - r=undef, rx=undef, ry=undef, - d=undef, dx=undef, dy=undef, - sa=0, ea=360, - rot=true + n=6, + r=undef, rx=undef, ry=undef, + d=undef, dx=undef, dy=undef, + sa=0, ea=360, + rot=true ) { - rx = get_radius(r1=rx, r=r, d1=dx, d=d, dflt=1); - ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1); - sa = posmod(sa, 360); - ea = posmod(ea, 360); - n = (abs(ea-sa)<0.01)?(n+1):n; - delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1); - for ($idx = [0:1:n-1]) { - $ang = sa + ($idx * delt); - $pos =[rx*cos($ang), ry*sin($ang), 0]; - translate($pos) { - zrot(rot? atan2(ry*sin($ang), rx*cos($ang)) : 0) { - children(); - } - } - } + rx = get_radius(r1=rx, r=r, d1=dx, d=d, dflt=1); + ry = get_radius(r1=ry, r=r, d1=dy, d=d, dflt=1); + sa = posmod(sa, 360); + ea = posmod(ea, 360); + n = (abs(ea-sa)<0.01)?(n+1):n; + delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1); + for ($idx = [0:1:n-1]) { + $ang = sa + ($idx * delt); + $pos =[rx*cos($ang), ry*sin($ang), 0]; + translate($pos) { + zrot(rot? atan2(ry*sin($ang), rx*cos($ang)) : 0) { + children(); + } + } + } } @@ -938,29 +938,29 @@ module arc_of( // cylinder(d=8, h=10, center=false); module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=true) { - r = get_radius(r=r, d=d, dflt=50); - cnt = ceil(n / (cone_ang/180)); + r = get_radius(r=r, d=d, dflt=50); + cnt = ceil(n / (cone_ang/180)); - // Calculate an array of [theta,phi] angles for `n` number of - // points, almost evenly spaced across the surface of a sphere. - // This approximation is based on the golden spiral method. - theta_phis = [for (x=[0:1:n-1]) [180*(1+sqrt(5))*(x+0.5)%360, acos(1-2*(x+0.5)/cnt)]]; + // Calculate an array of [theta,phi] angles for `n` number of + // points, almost evenly spaced across the surface of a sphere. + // This approximation is based on the golden spiral method. + theta_phis = [for (x=[0:1:n-1]) [180*(1+sqrt(5))*(x+0.5)%360, acos(1-2*(x+0.5)/cnt)]]; - for ($idx = idx(theta_phis)) { - tp = theta_phis[$idx]; - xyz = spherical_to_xyz(r, tp[0], tp[1]); - $pos = vmul(xyz,scale); - $theta = tp[0]; - $phi = tp[1]; - $rad = r; - translate($pos) { - if (perp) { - rot(from=UP, to=xyz) children(); - } else { - children(); - } - } - } + for ($idx = idx(theta_phis)) { + tp = theta_phis[$idx]; + xyz = spherical_to_xyz(r, tp[0], tp[1]); + $pos = vmul(xyz,scale); + $theta = tp[0]; + $phi = tp[1]; + $rad = r; + translate($pos) { + if (perp) { + rot(from=UP, to=xyz) children(); + } else { + children(); + } + } + } } @@ -1000,27 +1000,27 @@ module ovoid_spread(r=undef, d=undef, n=100, cone_ang=90, scale=[1,1,1], perp=tr // color("blue",0.25) translate([0,-5,-5]) rot(from=UP, to=BACK+UP) cube([15,15,0.01], center=true); module mirror_copy(v=[0,0,1], offset=0, cp) { - cp = is_vector(v,4)? plane_normal(v) * v[3] : - is_vector(cp)? cp : - is_num(cp)? cp*unit(v) : - [0,0,0]; - nv = is_vector(v,4)? plane_normal(v) : unit(v); - off = nv*offset; - if (cp == [0,0,0]) { - translate(off) { - $orig = true; - $idx = 0; - children(); - } - mirror(nv) translate(off) { - $orig = false; - $idx = 1; - children(); - } - } else { - translate(off) children(); - translate(cp) mirror(nv) translate(-cp) translate(off) children(); - } + cp = is_vector(v,4)? plane_normal(v) * v[3] : + is_vector(cp)? cp : + is_num(cp)? cp*unit(v) : + [0,0,0]; + nv = is_vector(v,4)? plane_normal(v) : unit(v); + off = nv*offset; + if (cp == [0,0,0]) { + translate(off) { + $orig = true; + $idx = 0; + children(); + } + mirror(nv) translate(off) { + $orig = false; + $idx = 1; + children(); + } + } else { + translate(off) children(); + translate(cp) mirror(nv) translate(-cp) translate(off) children(); + } } @@ -1053,7 +1053,7 @@ module mirror_copy(v=[0,0,1], offset=0, cp) // color("blue",0.25) left(5) cube([0.01,15,15], center=true); module xflip_copy(offset=0, x=0) { - mirror_copy(v=[1,0,0], offset=offset, cp=[x,0,0]) children(); + mirror_copy(v=[1,0,0], offset=offset, cp=[x,0,0]) children(); } @@ -1086,7 +1086,7 @@ module xflip_copy(offset=0, x=0) // color("blue",0.25) fwd(5) cube([15,0.01,15], center=true); module yflip_copy(offset=0, y=0) { - mirror_copy(v=[0,1,0], offset=offset, cp=[0,y,0]) children(); + mirror_copy(v=[0,1,0], offset=offset, cp=[0,y,0]) children(); } @@ -1119,9 +1119,9 @@ module yflip_copy(offset=0, y=0) // color("blue",0.25) down(5) cube([15,15,0.01], center=true); module zflip_copy(offset=0, z=0) { - mirror_copy(v=[0,0,1], offset=offset, cp=[0,0,z]) children(); + mirror_copy(v=[0,0,1], offset=offset, cp=[0,0,z]) children(); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/edges.scad b/edges.scad index c434455..2027151 100644 --- a/edges.scad +++ b/edges.scad @@ -68,34 +68,34 @@ function is_edge_array(v) = is_list(v) && is_vector(v[0]) && len(v)==3 && len(v[ function _edge_set(v) = - is_edge_array(v)? v : [ - for (ax=[0:2]) [ - for (b=[-1,1], a=[-1,1]) let( - v2=[[0,a,b],[a,0,b],[a,b,0]][ax] - ) ( - is_string(v)? ( - v=="X"? (ax==0) : // Return all X axis aligned edges. - v=="Y"? (ax==1) : // Return all Y axis aligned edges. - v=="Z"? (ax==2) : // Return all Z axis aligned edges. - v=="ALL"? true : // Return all edges. - v=="NONE"? false : // Return no edges. - let(valid_values = ["X", "Y", "Z", "ALL", "NONE"]) - assert( - in_list(v, valid_values), - str(v, " must be a vector, edge array, or one of ", valid_values) - ) v - ) : - let(nonz = sum(vabs(v))) - nonz==2? (v==v2) : // Edge: return matching edge. - let( - matches = count_true([ - for (i=[0:2]) v[i] && (v[i]==v2[i]) - ]) - ) - nonz==1? (matches==1) : // Face: return surrounding edges. - (matches==2) // Corner: return touching edges. - )? 1 : 0 - ] + is_edge_array(v)? v : [ + for (ax=[0:2]) [ + for (b=[-1,1], a=[-1,1]) let( + v2=[[0,a,b],[a,0,b],[a,b,0]][ax] + ) ( + is_string(v)? ( + v=="X"? (ax==0) : // Return all X axis aligned edges. + v=="Y"? (ax==1) : // Return all Y axis aligned edges. + v=="Z"? (ax==2) : // Return all Z axis aligned edges. + v=="ALL"? true : // Return all edges. + v=="NONE"? false : // Return no edges. + let(valid_values = ["X", "Y", "Z", "ALL", "NONE"]) + assert( + in_list(v, valid_values), + str(v, " must be a vector, edge array, or one of ", valid_values) + ) v + ) : + let(nonz = sum(vabs(v))) + nonz==2? (v==v2) : // Edge: return matching edge. + let( + matches = count_true([ + for (i=[0:2]) v[i] && (v[i]==v2[i]) + ]) + ) + nonz==1? (matches==1) : // Face: return surrounding edges. + (matches==2) // Corner: return touching edges. + )? 1 : 0 + ] ]; @@ -211,32 +211,32 @@ function normalize_edges(v) = [for (ax=v) [for (edge=ax) edge>0? 1 : 0]]; // Example: All edges, except Z-aligned edges on the front. // edges("ALL", except=edges("Z", except=BACK)) function edges(v, except=[]) = - (is_string(v) || is_vector(v) || is_edge_array(v))? edges([v], except=except) : - (is_string(except) || is_vector(except) || is_edge_array(except))? edges(v, except=[except]) : - except==[]? normalize_edges(sum([for (x=v) _edge_set(x)])) : - normalize_edges( - normalize_edges(sum([for (x=v) _edge_set(x)])) - - sum([for (x=except) _edge_set(x)]) - ); + (is_string(v) || is_vector(v) || is_edge_array(v))? edges([v], except=except) : + (is_string(except) || is_vector(except) || is_edge_array(except))? edges(v, except=[except]) : + except==[]? normalize_edges(sum([for (x=v) _edge_set(x)])) : + normalize_edges( + normalize_edges(sum([for (x=v) _edge_set(x)])) - + sum([for (x=except) _edge_set(x)]) + ); EDGE_OFFSETS = [ // Array of XYZ offsets to the center of each edge. - [ - [ 0,-1,-1], - [ 0, 1,-1], - [ 0,-1, 1], - [ 0, 1, 1] - ], [ - [-1, 0,-1], - [ 1, 0,-1], - [-1, 0, 1], - [ 1, 0, 1] - ], [ - [-1,-1, 0], - [ 1,-1, 0], - [-1, 1, 0], - [ 1, 1, 0] - ] + [ + [ 0,-1,-1], + [ 0, 1,-1], + [ 0,-1, 1], + [ 0, 1, 1] + ], [ + [-1, 0,-1], + [ 1, 0,-1], + [-1, 0, 1], + [ 1, 0, 1] + ], [ + [-1,-1, 0], + [ 1,-1, 0], + [-1, 1, 0], + [ 1, 1, 0] + ] ]; @@ -267,21 +267,21 @@ function normalize_corners(v) = [for (x=v) x>0? 1 : 0]; function _corner_set(v) = - is_corner_array(v)? v : [ - for (i=[0:7]) let( - v2 = CORNER_OFFSETS[i] - ) ( - is_string(v)? ( - v=="ALL"? true : // Return all corners. - v=="NONE"? false : // Return no corners. - let(valid_values = ["ALL", "NONE"]) - assert( - in_list(v, valid_values), - str(v, " must be a vector, corner array, or one of ", valid_values) - ) v - ) : - all([for (i=[0:2]) !v[i] || (v[i]==v2[i])]) - )? 1 : 0 + is_corner_array(v)? v : [ + for (i=[0:7]) let( + v2 = CORNER_OFFSETS[i] + ) ( + is_string(v)? ( + v=="ALL"? true : // Return all corners. + v=="NONE"? false : // Return no corners. + let(valid_values = ["ALL", "NONE"]) + assert( + in_list(v, valid_values), + str(v, " must be a vector, corner array, or one of ", valid_values) + ) v + ) : + all([for (i=[0:2]) !v[i] || (v[i]==v2[i])]) + )? 1 : 0 ]; @@ -370,18 +370,18 @@ function _corner_set(v) = // Example: All corners around the bottom or front faces, except those on the bottom-front edge. // corners([BOTTOM,FRONT], except=BOTTOM+FRONT) function corners(v, except=[]) = - (is_string(v) || is_vector(v) || is_corner_array(v))? corners([v], except=except) : - (is_string(except) || is_vector(except) || is_corner_array(except))? corners(v, except=[except]) : - except==[]? normalize_corners(sum([for (x=v) _corner_set(x)])) : - let( - a = normalize_corners(sum([for (x=v) _corner_set(x)])), - b = normalize_corners(sum([for (x=except) _corner_set(x)])) - ) normalize_corners(a - b); + (is_string(v) || is_vector(v) || is_corner_array(v))? corners([v], except=except) : + (is_string(except) || is_vector(except) || is_corner_array(except))? corners(v, except=[except]) : + except==[]? normalize_corners(sum([for (x=v) _corner_set(x)])) : + let( + a = normalize_corners(sum([for (x=v) _corner_set(x)])), + b = normalize_corners(sum([for (x=except) _corner_set(x)])) + ) normalize_corners(a - b); CORNER_OFFSETS = [ // Array of XYZ offsets to each corner. - [-1,-1,-1], [ 1,-1,-1], [-1, 1,-1], [ 1, 1,-1], - [-1,-1, 1], [ 1,-1, 1], [-1, 1, 1], [ 1, 1, 1] + [-1,-1,-1], [ 1,-1,-1], [-1, 1,-1], [ 1, 1,-1], + [-1,-1, 1], [ 1,-1, 1], [-1, 1, 1], [ 1, 1, 1] ]; @@ -391,7 +391,7 @@ CORNER_OFFSETS = [ // Array of XYZ offsets to each corner. // edges = Standard edges array. // v = Vector pointing to the corner to count edge intersections at. function corner_edge_count(edges, v) = - let(u = (v+[1,1,1])/2) edges[0][u.y+u.z*2] + edges[1][u.x+u.z*2] + edges[2][u.x+u.y*2]; + let(u = (v+[1,1,1])/2) edges[0][u.y+u.z*2] + edges[1][u.x+u.z*2] + edges[2][u.x+u.y*2]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/errors.scad b/errors.scad index 4a3e009..8c174d9 100644 --- a/errors.scad +++ b/errors.scad @@ -35,11 +35,11 @@ module no_children(count) { // msg = The message to print. // pfx = The prefix to print before `msg`. Default: `ERROR` module echo_error(msg, pfx="ERROR") { - echo(str("

", pfx, ": ", msg, "

")); + echo(str("

", pfx, ": ", msg, "

")); } function echo_error(msg, pfx="ERROR") = - echo(str("

", pfx, ": ", msg, "

")); + echo(str("

", pfx, ": ", msg, "

")); // Function&Module: echo_warning() @@ -52,11 +52,11 @@ function echo_error(msg, pfx="ERROR") = // msg = The message to print. // pfx = The prefix to print before `msg`. Default: `WARNING` module echo_warning(msg, pfx="WARNING") { - echo(str("

", pfx, ": ", msg, "

")); + echo(str("

", pfx, ": ", msg, "

")); } function echo_warning(msg, pfx="WARNING") = - echo(str("

", pfx, ": ", msg, "

")); + echo(str("

", pfx, ": ", msg, "

")); // Function&Module: deprecate() @@ -69,25 +69,25 @@ function echo_warning(msg, pfx="WARNING") = // name = The name of the module that is deprecated. // suggest = If given, the module to recommend using instead. module deprecate(name, suggest=undef) { - echo_warning(pfx="DEPRECATED", - str( - "`", name, "` is deprecated and should not be used.", - is_undef(suggest)? "" : str( - " You should use `", suggest, "` instead." - ) - ) - ); + echo_warning(pfx="DEPRECATED", + str( + "`", name, "` is deprecated and should not be used.", + is_undef(suggest)? "" : str( + " You should use `", suggest, "` instead." + ) + ) + ); } function deprecate(name, suggest=undef) = - echo_warning(pfx="DEPRECATED", - str( - "`", name, "` is deprecated and should not be used.", - is_undef(suggest)? "" : str( - " You should use `", suggest, "` instead." - ) - ) - ); + echo_warning(pfx="DEPRECATED", + str( + "`", name, "` is deprecated and should not be used.", + is_undef(suggest)? "" : str( + " You should use `", suggest, "` instead." + ) + ) + ); // Function&Module: deprecate_argument() @@ -101,26 +101,26 @@ function deprecate(name, suggest=undef) = // arg = The name of the deprecated argument. // suggest = If given, the argument to recommend using instead. module deprecate_argument(name, arg, suggest=undef) { - echo_warning(pfx="DEPRECATED ARG", str( - "In `", name, "`, ", - "the argument `", arg, "` ", - "is deprecated and should not be used.", - is_undef(suggest)? "" : str( - " You should use `", suggest, "` instead." - ) - )); + echo_warning(pfx="DEPRECATED ARG", str( + "In `", name, "`, ", + "the argument `", arg, "` ", + "is deprecated and should not be used.", + is_undef(suggest)? "" : str( + " You should use `", suggest, "` instead." + ) + )); } function deprecate_argument(name, arg, suggest=undef) = - echo_warning(pfx="DEPRECATED ARG", str( - "In `", name, "`, ", - "the argument `", arg, "` ", - "is deprecated and should not be used.", - is_undef(suggest)? "" : str( - " You should use `", suggest, "` instead." - ) - )); + echo_warning(pfx="DEPRECATED ARG", str( + "In `", name, "`, ", + "the argument `", arg, "` ", + "is deprecated and should not be used.", + is_undef(suggest)? "" : str( + " You should use `", suggest, "` instead." + ) + )); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/attachments.scad b/examples/attachments.scad index 238b3c1..8c8a4e6 100644 --- a/examples/attachments.scad +++ b/examples/attachments.scad @@ -4,15 +4,15 @@ include $fn=32; cuboid([60,40,40], rounding=5, edges=edges("Z"), anchor=BOTTOM) { - attach(TOP, BOTTOM) prismoid([60,40],[20,20], h=50, rounding1=5, rounding2=10) { - attach(TOP) cylinder(d=20, h=30, center=false) { - attach(TOP) cylinder(d1=50, d2=30, h=12, center=false); - } - attach([FRONT, BACK, LEFT, RIGHT]) cylinder(d1=14, d2=5, h=20) { - attach(TOP, LEFT, overlap=5) prismoid([30,20], [20,20], h=10, shift=[-7,0]); - } - } + attach(TOP, BOTTOM) prismoid([60,40],[20,20], h=50, rounding1=5, rounding2=10) { + attach(TOP) cylinder(d=20, h=30, center=false) { + attach(TOP) cylinder(d1=50, d2=30, h=12, center=false); + } + attach([FRONT, BACK, LEFT, RIGHT]) cylinder(d1=14, d2=5, h=20) { + attach(TOP, LEFT, overlap=5) prismoid([30,20], [20,20], h=10, shift=[-7,0]); + } + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/bezier_patches.scad b/examples/bezier_patches.scad index 03941e4..de7042b 100644 --- a/examples/bezier_patches.scad +++ b/examples/bezier_patches.scad @@ -3,75 +3,75 @@ include function CR_corner(size, spin=0, orient=UP, trans=[0,0,0]) = - let ( - // This patch might not yet correct for continuous rounding, - // but it's a first approximation proof of concept. - a = 0.68, - b = 0.60, - c = 0.24, - patch = [ - [[0,1,1], [0,a,1], [0,c,1], [c,0,1], [a,0,1], [1,0,1]], - [[0,1,a], [0,b,b], [0,0,b], [b,0,b], [1,0,a]], - [[0,1,c], [0,b,0], [b,0,0], [1,0,c]], - [[c,1,0], [b,b,0], [1,c,0]], - [[a,1,0], [1,a,0]], - [[1,1,0]], - ] - ) - translate(trans, - p=rot(a=spin, from=UP, to=orient, - p=scale(size, patch) - ) - ); + let ( + // This patch might not yet correct for continuous rounding, + // but it's a first approximation proof of concept. + a = 0.68, + b = 0.60, + c = 0.24, + patch = [ + [[0,1,1], [0,a,1], [0,c,1], [c,0,1], [a,0,1], [1,0,1]], + [[0,1,a], [0,b,b], [0,0,b], [b,0,b], [1,0,a]], + [[0,1,c], [0,b,0], [b,0,0], [1,0,c]], + [[c,1,0], [b,b,0], [1,c,0]], + [[a,1,0], [1,a,0]], + [[1,1,0]], + ] + ) + translate(trans, + p=rot(a=spin, from=UP, to=orient, + p=scale(size, patch) + ) + ); function CR_edge(size, spin=0, orient=UP, trans=[0,0,0]) = - let ( - // This patch might not be correct for continuous rounding, - // but it's a first approximation proof of concept. - vvals = [1.00, 0.68, 0.24], - xyvals = [ - for (x=vvals) [x,0], - for (y=reverse(vvals)) [0,y] - ], - zvals = [-0.5:0.2:0.5], - patch = [for (xy=xyvals) [for (z=zvals) [each xy, z]]] - ) - translate(trans, - p=rot(a=spin, from=UP, to=orient, - p=scale(size, p=patch) - ) - ); + let ( + // This patch might not be correct for continuous rounding, + // but it's a first approximation proof of concept. + vvals = [1.00, 0.68, 0.24], + xyvals = [ + for (x=vvals) [x,0], + for (y=reverse(vvals)) [0,y] + ], + zvals = [-0.5:0.2:0.5], + patch = [for (xy=xyvals) [for (z=zvals) [each xy, z]]] + ) + translate(trans, + p=rot(a=spin, from=UP, to=orient, + p=scale(size, p=patch) + ) + ); module CR_cube(size=[100,100,100], r=10, splinesteps=8, debug=false) { - s = size-2*[r,r,r]; - h = size/2; - corner_pat = CR_corner([r,r,r], trans=[-size.x/2, -size.y/2, -size.z/2]); - edge_pat = CR_edge([r, r, s.z], trans=[-h.x, -h.y, 0]); - face_pat = bezier_patch_flat([s.x, s.z], N=1, orient=FRONT, trans=[0, -h.y, 0]); - corners = bezier_surface([ - for (yr=[0,180], zr=[0:90:270]) let( - m = yrot(yr) * zrot(zr) - ) [for (row=corner_pat) apply(m, row)] - ], splinesteps=splinesteps); - edges = bezier_surface([ - for (axr=[[0,0,0],[90,0,0],[0,90,0]],zr=[0:90:270]) let( - m = rot(axr) * zrot(zr) - ) [for (row=edge_pat) apply(m, row)] - ], splinesteps=[splinesteps,1]); - faces = bezier_surface([ - for (axr=[0,90,180,270,[-90,0,0],[90,0,0]]) let( - m = rot(axr) - ) [for (row=face_pat) apply(m, row)] - ], splinesteps=1); + s = size-2*[r,r,r]; + h = size/2; + corner_pat = CR_corner([r,r,r], trans=[-size.x/2, -size.y/2, -size.z/2]); + edge_pat = CR_edge([r, r, s.z], trans=[-h.x, -h.y, 0]); + face_pat = bezier_patch_flat([s.x, s.z], N=1, orient=FRONT, trans=[0, -h.y, 0]); + corners = bezier_surface([ + for (yr=[0,180], zr=[0:90:270]) let( + m = yrot(yr) * zrot(zr) + ) [for (row=corner_pat) apply(m, row)] + ], splinesteps=splinesteps); + edges = bezier_surface([ + for (axr=[[0,0,0],[90,0,0],[0,90,0]],zr=[0:90:270]) let( + m = rot(axr) * zrot(zr) + ) [for (row=edge_pat) apply(m, row)] + ], splinesteps=[splinesteps,1]); + faces = bezier_surface([ + for (axr=[0,90,180,270,[-90,0,0],[90,0,0]]) let( + m = rot(axr) + ) [for (row=face_pat) apply(m, row)] + ], splinesteps=1); - if (debug) { - vnf_validate([edges, faces, corners], convexity=4); - } else { - vnf_polyhedron([edges, faces, corners], convexity=4); - } + if (debug) { + vnf_validate([edges, faces, corners], convexity=4); + } else { + vnf_polyhedron([edges, faces, corners], convexity=4); + } } @@ -80,4 +80,4 @@ cube(1); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/boolean_geometry.scad b/examples/boolean_geometry.scad index 23615d6..1e7190f 100644 --- a/examples/boolean_geometry.scad +++ b/examples/boolean_geometry.scad @@ -4,15 +4,15 @@ include $fn = 36; rgn1 = [ - square(100), - move([50,50], p=circle(d=60)), - [[35,35],[35,65],[65,65]] + square(100), + move([50,50], p=circle(d=60)), + [[35,35],[35,65],[65,65]] ]; rgn2 = [ - [[0,0], [100,100], [100,0]], - [[27,10], [90,73], [90,10]], - move([70,30], p=circle(d=25)) + [[0,0], [100,100], [100,0]], + [[27,10], [90,73], [90,10]], + move([70,30], p=circle(d=25)) ]; @@ -21,29 +21,29 @@ outlinecolor="black"; module showit(label, rgn, poly=polycolor, outline=outlinecolor, width=0.5) { - move([-50,-50]) { - if(outline) color(outline) linear_extrude(height=max(0.1,1-width)) for(path=rgn) stroke(path, width=width, closed=true); - if(poly) color(poly) linear_extrude(height=0.1) region(rgn); - color("black") right(50) fwd(7) linear_extrude(height=0.1) text(text=label, size=8, halign="center", valign="center"); - } + move([-50,-50]) { + if(outline) color(outline) linear_extrude(height=max(0.1,1-width)) for(path=rgn) stroke(path, width=width, closed=true); + if(poly) color(poly) linear_extrude(height=0.1) region(rgn); + color("black") right(50) fwd(7) linear_extrude(height=0.1) text(text=label, size=8, halign="center", valign="center"); + } } ydistribute(-125) { - xdistribute(120) { - showit("Region A", rgn1, poly=[1,0,0,0.5]); - showit("Region B", rgn2, poly=[0,0,1,0.5]); - union() { - showit("A and B Overlaid", rgn1, poly=[1,0,0,0.5]); - showit("", rgn2, poly=[0,0,1,0.5]); - } - } - xdistribute(120) { - showit("Union A+B", union(rgn1, rgn2)); - showit("Difference A-B", difference(rgn1, rgn2)); - showit("Intersection A&B", intersection(rgn1, rgn2)); - showit("Exclusive OR A^B", exclusive_or(rgn1, rgn2)); - } + xdistribute(120) { + showit("Region A", rgn1, poly=[1,0,0,0.5]); + showit("Region B", rgn2, poly=[0,0,1,0.5]); + union() { + showit("A and B Overlaid", rgn1, poly=[1,0,0,0.5]); + showit("", rgn2, poly=[0,0,1,0.5]); + } + } + xdistribute(120) { + showit("Union A+B", union(rgn1, rgn2)); + showit("Difference A-B", difference(rgn1, rgn2)); + showit("Intersection A&B", intersection(rgn1, rgn2)); + showit("Exclusive OR A^B", exclusive_or(rgn1, rgn2)); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/conical_anchors.scad b/examples/conical_anchors.scad index 5067d4b..42e9de3 100644 --- a/examples/conical_anchors.scad +++ b/examples/conical_anchors.scad @@ -5,4 +5,4 @@ include cylinder(h=30, d1=50, d2=30) show_anchors(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/cube_anchors.scad b/examples/cube_anchors.scad index da4f242..db122de 100644 --- a/examples/cube_anchors.scad +++ b/examples/cube_anchors.scad @@ -5,4 +5,4 @@ include cube(40, center=true) show_anchors(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/cubic_sphere_packing.scad b/examples/cubic_sphere_packing.scad index a529250..3a782d0 100644 --- a/examples/cubic_sphere_packing.scad +++ b/examples/cubic_sphere_packing.scad @@ -6,8 +6,8 @@ include s = 20; s2 = s * sin(45); zcopies(s2,n=8) union() - grid2d([s2,s2],n=8,stagger=($idx%2)? true : "alt") - sphere(d=s); + grid2d([s2,s2],n=8,stagger=($idx%2)? true : "alt") + sphere(d=s); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/cylinder_anchors.scad b/examples/cylinder_anchors.scad index 6787502..1c3d605 100644 --- a/examples/cylinder_anchors.scad +++ b/examples/cylinder_anchors.scad @@ -5,4 +5,4 @@ include cylinder(h=30, d=30) show_anchors(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/fractal_tree.scad b/examples/fractal_tree.scad index 5ef423e..ea1f07d 100644 --- a/examples/fractal_tree.scad +++ b/examples/fractal_tree.scad @@ -1,17 +1,17 @@ include module tree(l=1500, sc=0.7, depth=10) - recolor("lightgray") - cylinder(l=l, d1=l/5, d2=l/5*sc) - attach(TOP) - if (depth>0) - zrot(90) - zrot_copies(n=2) - yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); - else - recolor("springgreen") - yscale(0.67) - teardrop(d=l*3, l=1, anchor=BOT, spin=90); + recolor("lightgray") + cylinder(l=l, d1=l/5, d2=l/5*sc) + attach(TOP) + if (depth>0) + zrot(90) + zrot_copies(n=2) + yrot(30) tree(depth=depth-1, l=l*sc, sc=sc); + else + recolor("springgreen") + yscale(0.67) + teardrop(d=l*3, l=1, anchor=BOT, spin=90); tree(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/hex_sphere_packing.scad b/examples/hex_sphere_packing.scad index baa3983..c36d31e 100644 --- a/examples/hex_sphere_packing.scad +++ b/examples/hex_sphere_packing.scad @@ -7,9 +7,9 @@ s = 20; xyr = adj_ang_to_hyp(s/2,30); h = hyp_adj_to_opp(s,xyr); zcopies(h,n=8) union() - back(($idx%2)*xyr*cos(60)) - grid2d(s,n=[12,7],stagger=($idx%2)? "alt" : true) - sphere(d=s); + back(($idx%2)*xyr*cos(60)) + grid2d(s,n=[12,7],stagger=($idx%2)? "alt" : true) + sphere(d=s); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/lsystems.scad b/examples/lsystems.scad index d9d9734..1d3dd1c 100644 --- a/examples/lsystems.scad +++ b/examples/lsystems.scad @@ -1,46 +1,46 @@ include function _lsystem_recurse(s, rules, lev) = - lev<=0? s : _lsystem_recurse([ - for ( - i = 0, - slen = len(s), - sout = ""; + lev<=0? s : _lsystem_recurse([ + for ( + i = 0, + slen = len(s), + sout = ""; - i <= slen; + i <= slen; - ch = s[i], - found = search([ch], rules)[0], - sout = str(sout, i==slen? "" : found==[]? ch : rules[found][1]), - i = i + 1 - ) if (i==slen) sout - ][0], rules, lev-1); + ch = s[i], + found = search([ch], rules)[0], + sout = str(sout, i==slen? "" : found==[]? ch : rules[found][1]), + i = i + 1 + ) if (i==slen) sout + ][0], rules, lev-1); function _lsystem_to_turtle(s, step=1, angle=90, startang=0) = - concat( - startang? ["left", startang] : [], - ["angle", angle, "length", step], - [ - for ( - i = 0, - slen = len(s); + concat( + startang? ["left", startang] : [], + ["angle", angle, "length", step], + [ + for ( + i = 0, + slen = len(s); - i <= slen; + i <= slen; - ch = s[i], - cmd = (ch=="A" || ch=="B" || ch=="F")? ["move"] : - (ch=="+")? ["left"] : - (ch=="-")? ["right"] : - [], - i=i+1 - ) if(i>0 && cmd!=[]) each cmd - ] - ); + ch = s[i], + cmd = (ch=="A" || ch=="B" || ch=="F")? ["move"] : + (ch=="+")? ["left"] : + (ch=="-")? ["right"] : + [], + i=i+1 + ) if(i>0 && cmd!=[]) each cmd + ] + ); function lsystem_turtle(basis, rules, levels=5, step=1, angle=90, startang=0) = - turtle(_lsystem_to_turtle(_lsystem_recurse(basis, rules, levels), step=step, angle=angle, startang=startang)); + turtle(_lsystem_to_turtle(_lsystem_recurse(basis, rules, levels), step=step, angle=angle, startang=startang)); function dragon_curve (levels=9, step=1) = lsystem_turtle(levels=levels, step=step, angle=90, "FX", [["X", "X+YF+"], ["Y", "-FX-Y"]]); @@ -67,4 +67,4 @@ points = hilbert_curve(levels=5, step=100/pow(2,5)); stroke(points, width=1); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/orientations.scad b/examples/orientations.scad index 2a55d6d..8e7b96a 100644 --- a/examples/orientations.scad +++ b/examples/orientations.scad @@ -18,10 +18,10 @@ module orient_cube(ang) { color(axiscolors.z) back((20-1)/2+0.01) right((20-1)/2+0.01) cube([1,1,18], center=true); for (axis=[0:2], neg=[0:1]) { idx = axis + 3*neg; - labels = [ - "RIGHT", "BACK", "UP", - "LEFT", "FWD", "DOWN" - ]; + labels = [ + "RIGHT", "BACK", "UP", + "LEFT", "FWD", "DOWN" + ]; rot(ang, from=UP, to=orientations[idx]) { up(10) { back(4) color("black") text3d(text=str("spin=",ang), size=2.5); @@ -42,34 +42,34 @@ module text3d(text, h=0.01, size=3) { module dottedline(l, d) for(y = [0:d*3:l]) up(y) sphere(d=d); module orient_cubes() { - // X axis - color(axiscolors[0]) { - yrot( 90) cylinder(h=axislen, d=axisdiam, center=false); - right(axislbllen) rot([90,0,0]) text3d(text="X+"); - yrot(-90) dottedline(l=axislen, d=axisdiam); - left(axislbllen) rot([90,0,180]) text3d(text="X-"); - } - // Y axis - color(axiscolors[1]) { - xrot(-90) cylinder(h=axislen, d=axisdiam, center=false); - back(axislbllen) rot([90,0,90]) text3d(text="Y+"); - xrot( 90) dottedline(l=axislen, d=axisdiam); - fwd(axislbllen) rot([90,0,-90]) text3d(text="Y-"); - } - // Z axis - color(axiscolors[2]) { - cylinder(h=axislen, d=axisdiam, center=false); - up(axislbllen) rot([0,-90,90+$vpr[2]]) text3d(text="Z+"); - xrot(180) dottedline(l=axislen, d=axisdiam); - down(axislbllen) rot([0,90,-90+$vpr[2]]) text3d(text="Z-"); - } + // X axis + color(axiscolors[0]) { + yrot( 90) cylinder(h=axislen, d=axisdiam, center=false); + right(axislbllen) rot([90,0,0]) text3d(text="X+"); + yrot(-90) dottedline(l=axislen, d=axisdiam); + left(axislbllen) rot([90,0,180]) text3d(text="X-"); + } + // Y axis + color(axiscolors[1]) { + xrot(-90) cylinder(h=axislen, d=axisdiam, center=false); + back(axislbllen) rot([90,0,90]) text3d(text="Y+"); + xrot( 90) dottedline(l=axislen, d=axisdiam); + fwd(axislbllen) rot([90,0,-90]) text3d(text="Y-"); + } + // Z axis + color(axiscolors[2]) { + cylinder(h=axislen, d=axisdiam, center=false); + up(axislbllen) rot([0,-90,90+$vpr[2]]) text3d(text="Z+"); + xrot(180) dottedline(l=axislen, d=axisdiam); + down(axislbllen) rot([0,90,-90+$vpr[2]]) text3d(text="Z-"); + } - for (ang = [0:90:270]) { - off = rot(p=40*BACK,ang); - translate(off) { - orient_cube(ang); - } - } + for (ang = [0:90:270]) { + off = rot(p=40*BACK,ang); + translate(off) { + orient_cube(ang); + } + } } @@ -77,4 +77,4 @@ orient_cubes(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/prismoid_anchors.scad b/examples/prismoid_anchors.scad index 0adfe98..7bf4fc4 100644 --- a/examples/prismoid_anchors.scad +++ b/examples/prismoid_anchors.scad @@ -5,4 +5,4 @@ include prismoid([60,40], [30,20], h=40) show_anchors(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/randomized_fractal_tree.scad b/examples/randomized_fractal_tree.scad index c3d2592..5dd6f87 100644 --- a/examples/randomized_fractal_tree.scad +++ b/examples/randomized_fractal_tree.scad @@ -3,46 +3,46 @@ include include module leaf(s) { - path = [ - [0,0], [1.5,-1], - [2,1], [0,3], [-2,1], - [-1.5,-1], [0,0] - ]; - xrot(90) - linear_sweep_bezier( - path * s/2, - height=0.02 - ); + path = [ + [0,0], [1.5,-1], + [2,1], [0,3], [-2,1], + [-1.5,-1], [0,0] + ]; + xrot(90) + linear_sweep_bezier( + path * s/2, + height=0.02 + ); } module branches(minsize, s1, s2){ if(s2>minsize) { - attach(TOP) - zrot(gaussian_rands(90,20)[0]) - zrot_copies(n=floor(log_rands(2,5,4)[0])) - zrot(gaussian_rands(0,5)[0]) - yrot(gaussian_rands(30,10)[0]) { - sc = gaussian_rands(0.7,0.05)[0]; - cylinder(d1=s2, d2=s2*sc, l=s1) - branches(minsize, s1*sc, s2*sc); - } - } else { - recolor("springgreen") - attach(TOP) zrot(90) - leaf(gaussian_rands(100,5)[0]); - } + attach(TOP) + zrot(gaussian_rands(90,20)[0]) + zrot_copies(n=floor(log_rands(2,5,4)[0])) + zrot(gaussian_rands(0,5)[0]) + yrot(gaussian_rands(30,10)[0]) { + sc = gaussian_rands(0.7,0.05)[0]; + cylinder(d1=s2, d2=s2*sc, l=s1) + branches(minsize, s1*sc, s2*sc); + } + } else { + recolor("springgreen") + attach(TOP) zrot(90) + leaf(gaussian_rands(100,5)[0]); + } } module tree(h, d, minsize) { - sc = gaussian_rands(0.7,0.05)[0]; - recolor("lightgray") { - cylinder(d1=d, d2=d*sc, l=h) { - branches(minsize, h, d*sc); - } - } + sc = gaussian_rands(0.7,0.05)[0]; + recolor("lightgray") { + cylinder(d1=d, d2=d*sc, l=h) { + branches(minsize, h, d*sc); + } + } } tree(d=300, h=1500, minsize=10); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/screw_anchors.scad b/examples/screw_anchors.scad index 1bb2164..76d4eff 100644 --- a/examples/screw_anchors.scad +++ b/examples/screw_anchors.scad @@ -10,4 +10,4 @@ metric_bolt(headtype="oval", size=10, l=15, shank=5, details=true, phillips="#2" show_anchors(5, std=false); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/sphere_anchors.scad b/examples/sphere_anchors.scad index 03dfe22..b1fc43a 100644 --- a/examples/sphere_anchors.scad +++ b/examples/sphere_anchors.scad @@ -5,4 +5,4 @@ include spheroid(d=30) show_anchors(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/spherical_patch.scad b/examples/spherical_patch.scad index de07293..8cc2307 100644 --- a/examples/spherical_patch.scad +++ b/examples/spherical_patch.scad @@ -8,13 +8,13 @@ p = s * d; q = s * 0.55 * d; u = s * 2.5 * UP; patch1 = [ - [p[2], p[2]+q[3], p[3]+q[2], p[3] ], - [p[2]+q[1], p[2]+q[2]+u, p[3]+q[3]+u, p[3]+q[0]], - [p[1]+q[2], p[1]+q[1]+u, p[0]+q[0]+u, p[0]+q[3]], - [p[1], p[1]+q[0], p[0]+q[1], p[0] ], + [p[2], p[2]+q[3], p[3]+q[2], p[3] ], + [p[2]+q[1], p[2]+q[2]+u, p[3]+q[3]+u, p[3]+q[0]], + [p[1]+q[2], p[1]+q[1]+u, p[0]+q[0]+u, p[0]+q[3]], + [p[1], p[1]+q[0], p[0]+q[1], p[0] ], ]; patch2 = patch_reverse(zflip(p=patch1)); trace_bezier_patches([patch1, patch2], splinesteps=16, style="quincunx"); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/examples/tagged_diff.scad b/examples/tagged_diff.scad index 23879d6..bbb41f0 100644 --- a/examples/tagged_diff.scad +++ b/examples/tagged_diff.scad @@ -3,13 +3,13 @@ include diff("hole", "body pole") sphere(d=100, $tags="body") { - zcyl(d=55, h=100, $tags="pole"); // attach() not needed for center-to-center. - tags("hole") { - xcyl(d=55, h=101); - ycyl(d=55, h=101); - } - zcyl(d=15, h=140, $tags="axle"); + zcyl(d=55, h=100, $tags="pole"); // attach() not needed for center-to-center. + tags("hole") { + xcyl(d=55, h=101); + ycyl(d=55, h=101); + } + zcyl(d=15, h=140, $tags="axle"); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/geometry.scad b/geometry.scad index 09882a7..b5f4c29 100644 --- a/geometry.scad +++ b/geometry.scad @@ -21,10 +21,10 @@ // edge = Array of two points forming the line segment to test against. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function point_on_segment2d(point, edge, eps=EPSILON) = - approx(point,edge[0],eps=eps) || approx(point,edge[1],eps=eps) || // The point is an endpoint - sign(edge[0].x-point.x)==sign(point.x-edge[1].x) // point is in between the - && sign(edge[0].y-point.y)==sign(point.y-edge[1].y) // edge endpoints - && approx(point_left_of_segment2d(point, edge),0,eps=eps); // and on the line defined by edge + approx(point,edge[0],eps=eps) || approx(point,edge[1],eps=eps) || // The point is an endpoint + sign(edge[0].x-point.x)==sign(point.x-edge[1].x) // point is in between the + && sign(edge[0].y-point.y)==sign(point.y-edge[1].y) // edge endpoints + && approx(point_left_of_segment2d(point, edge),0,eps=eps); // and on the line defined by edge // Function: point_left_of_segment2d() @@ -38,16 +38,16 @@ function point_on_segment2d(point, edge, eps=EPSILON) = // point = The point to check position of. // edge = Array of two points forming the line segment to test against. function point_left_of_segment2d(point, edge) = - (edge[1].x-edge[0].x) * (point.y-edge[0].y) - (point.x-edge[0].x) * (edge[1].y-edge[0].y); + (edge[1].x-edge[0].x) * (point.y-edge[0].y) - (point.x-edge[0].x) * (edge[1].y-edge[0].y); // Internal non-exposed function. function _point_above_below_segment(point, edge) = - edge[0].y <= point.y? ( - (edge[1].y > point.y && point_left_of_segment2d(point, edge) > 0)? 1 : 0 - ) : ( - (edge[1].y <= point.y && point_left_of_segment2d(point, edge) < 0)? -1 : 0 - ); + edge[0].y <= point.y? ( + (edge[1].y > point.y && point_left_of_segment2d(point, edge) > 0)? 1 : 0 + ) : ( + (edge[1].y <= point.y && point_left_of_segment2d(point, edge) < 0)? -1 : 0 + ); // Function: collinear() @@ -61,8 +61,8 @@ function _point_above_below_segment(point, edge) = // c = Third point. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function collinear(a, b, c, eps=EPSILON) = - approx(a,b,eps=eps)? true : - distance_from_line([a,b], c) < eps; + approx(a,b,eps=eps)? true : + distance_from_line([a,b], c) < eps; // Function: collinear_indexed() @@ -77,11 +77,11 @@ function collinear(a, b, c, eps=EPSILON) = // c = Index in `points` of third point. // eps = Acceptable max angle variance. Default: EPSILON (1e-9) degrees. function collinear_indexed(points, a, b, c, eps=EPSILON) = - let( - p1=points[a], - p2=points[b], - p3=points[c] - ) collinear(p1, p2, p3, eps); + let( + p1=points[a], + p2=points[b], + p3=points[c] + ) collinear(p1, p2, p3, eps); // Function: points_are_collinear() @@ -93,12 +93,12 @@ function collinear_indexed(points, a, b, c, eps=EPSILON) = // points = The list of points to test. // eps = How much variance is allowed in testing that each point is on the same line. Default: `EPSILON` (1e-9) function points_are_collinear(points, eps=EPSILON) = - let( - a = furthest_point(points[0], points), - b = furthest_point(points[a], points), - pa = points[a], - pb = points[b] - ) all([for (pt = points) collinear(pa, pb, pt, eps=eps)]); + let( + a = furthest_point(points[0], points), + b = furthest_point(points[a], points), + pa = points[a], + pb = points[b] + ) all([for (pt = points) collinear(pa, pb, pt, eps=eps)]); // Function: distance_from_line() @@ -112,8 +112,8 @@ function points_are_collinear(points, eps=EPSILON) = // Example: // distance_from_line([[-10,0], [10,0]], [3,8]); // Returns: 8 function distance_from_line(line, pt) = - let(a=line[0], n=unit(line[1]-a), d=a-pt) - norm(d - ((d * n) * n)); + let(a=line[0], n=unit(line[1]-a), d=a-pt) + norm(d - ((d * n) * n)); // Function: line_normal() @@ -133,8 +133,8 @@ function distance_from_line(line, pt) = // color("green") stroke([p1,p1+10*n], endcap2="arrow2"); // color("blue") move_copies([p1,p2]) circle(d=2, $fn=12); function line_normal(p1,p2) = - is_undef(p2)? line_normal(p1[0],p1[1]) : - unit([p1.y-p2.y,p2.x-p1.x]); + is_undef(p2)? line_normal(p1[0],p1[1]) : + unit([p1.y-p2.y,p2.x-p1.x]); // 2D Line intersection from two segments. @@ -146,12 +146,12 @@ function line_normal(p1,p2) = // the intersection lies on the segment. Otherwise it lies somewhere on // the extension of the segment. Result is undef for coincident lines. function _general_line_intersection(s1,s2,eps=EPSILON) = - let( - denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]]) - ) approx(denominator,0,eps=eps)? [undef,undef,undef] : let( - t = det2([s1[0],s2[0]]-s2) / denominator, - u = det2([s1[0],s1[0]]-[s2[0],s1[1]]) / denominator - ) [s1[0]+t*(s1[1]-s1[0]), t, u]; + let( + denominator = det2([s1[0],s2[0]]-[s1[1],s2[1]]) + ) approx(denominator,0,eps=eps)? [undef,undef,undef] : let( + t = det2([s1[0],s2[0]]-s2) / denominator, + u = det2([s1[0],s1[0]]-[s2[0],s1[1]]) / denominator + ) [s1[0]+t*(s1[1]-s1[0]), t, u]; // Function: line_intersection() @@ -165,7 +165,7 @@ function _general_line_intersection(s1,s2,eps=EPSILON) = // l2 = Second 2D line, given as a list of two 2D points on the line. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function line_intersection(l1,l2,eps=EPSILON) = - let(isect = _general_line_intersection(l1,l2,eps=eps)) isect[0]; + let(isect = _general_line_intersection(l1,l2,eps=eps)) isect[0]; // Function: line_ray_intersection() @@ -179,9 +179,9 @@ function line_intersection(l1,l2,eps=EPSILON) = // ray = The 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function line_ray_intersection(line,ray,eps=EPSILON) = - let( - isect = _general_line_intersection(line,ray,eps=eps) - ) isect[2]<0-eps? undef : isect[0]; + let( + isect = _general_line_intersection(line,ray,eps=eps) + ) isect[2]<0-eps? undef : isect[0]; // Function: line_segment_intersection() @@ -195,9 +195,9 @@ function line_ray_intersection(line,ray,eps=EPSILON) = // segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function line_segment_intersection(line,segment,eps=EPSILON) = - let( - isect = _general_line_intersection(line,segment,eps=eps) - ) isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0]; + let( + isect = _general_line_intersection(line,segment,eps=eps) + ) isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0]; // Function: ray_intersection() @@ -211,9 +211,9 @@ function line_segment_intersection(line,segment,eps=EPSILON) = // r2 = Second 2D ray, given as a list `[START,POINT]` of the 2D start-point START, and a 2D point POINT on the ray. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function ray_intersection(r1,r2,eps=EPSILON) = - let( - isect = _general_line_intersection(r1,r2,eps=eps) - ) isect[1]<0-eps || isect[2]<0-eps? undef : isect[0]; + let( + isect = _general_line_intersection(r1,r2,eps=eps) + ) isect[1]<0-eps || isect[2]<0-eps? undef : isect[0]; // Function: ray_segment_intersection() @@ -227,9 +227,9 @@ function ray_intersection(r1,r2,eps=EPSILON) = // segment = The bounded 2D line segment, given as a list of the two 2D endpoints of the segment. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function ray_segment_intersection(ray,segment,eps=EPSILON) = - let( - isect = _general_line_intersection(ray,segment,eps=eps) - ) isect[1]<0-eps || isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0]; + let( + isect = _general_line_intersection(ray,segment,eps=eps) + ) isect[1]<0-eps || isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0]; // Function: segment_intersection() @@ -243,9 +243,9 @@ function ray_segment_intersection(ray,segment,eps=EPSILON) = // s2 = Second 2D segment, given as a list of the two 2D endpoints of the line segment. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function segment_intersection(s1,s2,eps=EPSILON) = - let( - isect = _general_line_intersection(s1,s2,eps=eps) - ) isect[1]<0-eps || isect[1]>1+eps || isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0]; + let( + isect = _general_line_intersection(s1,s2,eps=eps) + ) isect[1]<0-eps || isect[1]>1+eps || isect[2]<0-eps || isect[2]>1+eps ? undef : isect[0]; // Function: line_closest_point() @@ -257,10 +257,10 @@ function segment_intersection(s1,s2,eps=EPSILON) = // line = A list of two points that are on the unbounded line. // pt = The point to find the closest point on the line to. function line_closest_point(line,pt) = - let( - n = line_normal(line), - isect = _general_line_intersection(line,[pt,pt+n]) - ) isect[0]; + let( + n = line_normal(line), + isect = _general_line_intersection(line,[pt,pt+n]) + ) isect[0]; // Function: segment_closest_point() @@ -272,14 +272,14 @@ function line_closest_point(line,pt) = // seg = A list of two points that are the endpoints of the bounded line segment. // pt = The point to find the closest point on the segment to. function segment_closest_point(seg,pt) = - let( - n = line_normal(seg), - isect = _general_line_intersection(seg,[pt,pt+n]) - ) - norm(n)==0? seg[0] : - isect[1]<=0? seg[0] : - isect[1]>=1? seg[1] : - isect[0]; + let( + n = line_normal(seg), + isect = _general_line_intersection(seg,[pt,pt+n]) + ) + norm(n)==0? seg[0] : + isect[1]<=0? seg[0] : + isect[1]>=1? seg[1] : + isect[0]; // Section: 2D Triangles @@ -327,23 +327,23 @@ function segment_closest_point(seg,pt) = // ang = tri_calc(adj=20,hyp=30)[3]; // ang2 = tri_calc(adj=20,hyp=40)[4]; function tri_calc(ang,ang2,adj,opp,hyp) = - assert(ang==undef || ang2==undef,"You cannot specify both ang and ang2.") - assert(num_defined([ang,ang2,adj,opp,hyp])==2, "You must specify exactly two arguments.") - let( - ang = ang!=undef? assert(ang>0&&ang<90) ang : - ang2!=undef? (90-ang2) : - adj==undef? asin(constrain(opp/hyp,-1,1)) : - opp==undef? acos(constrain(adj/hyp,-1,1)) : - atan2(opp,adj), - ang2 = ang2!=undef? assert(ang2>0&&ang2<90) ang2 : (90-ang), - adj = adj!=undef? assert(adj>0) adj : - (opp!=undef? (opp/tan(ang)) : (hyp*cos(ang))), - opp = opp!=undef? assert(opp>0) opp : - (adj!=undef? (adj*tan(ang)) : (hyp*sin(ang))), - hyp = hyp!=undef? assert(hyp>0) assert(adj0&&ang<90) ang : + ang2!=undef? (90-ang2) : + adj==undef? asin(constrain(opp/hyp,-1,1)) : + opp==undef? acos(constrain(adj/hyp,-1,1)) : + atan2(opp,adj), + ang2 = ang2!=undef? assert(ang2>0&&ang2<90) ang2 : (90-ang), + adj = adj!=undef? assert(adj>0) adj : + (opp!=undef? (opp/tan(ang)) : (hyp*cos(ang))), + opp = opp!=undef? assert(opp>0) opp : + (adj!=undef? (adj*tan(ang)) : (hyp*sin(ang))), + hyp = hyp!=undef? assert(hyp>0) assert(adj=0) - assert(is_num(opp)&&opp>=0) - sqrt(hyp*hyp-opp*opp); + assert(is_num(hyp)&&hyp>=0) + assert(is_num(opp)&&opp>=0) + sqrt(hyp*hyp-opp*opp); // Function: hyp_ang_to_adj() @@ -375,9 +375,9 @@ function hyp_opp_to_adj(hyp,opp) = // Example: // adj = hyp_ang_to_adj(8,60); // Returns: 4 function hyp_ang_to_adj(hyp,ang) = - assert(is_num(hyp)&&hyp>=0) - assert(is_num(ang)&&ang>0&&ang<90) - hyp*cos(ang); + assert(is_num(hyp)&&hyp>=0) + assert(is_num(ang)&&ang>0&&ang<90) + hyp*cos(ang); // Function: opp_ang_to_adj() @@ -392,9 +392,9 @@ function hyp_ang_to_adj(hyp,ang) = // Example: // adj = opp_ang_to_adj(8,30); // Returns: 4 function opp_ang_to_adj(opp,ang) = - assert(is_num(opp)&&opp>=0) - assert(is_num(ang)&&ang>0&&ang<90) - opp/tan(ang); + assert(is_num(opp)&&opp>=0) + assert(is_num(ang)&&ang>0&&ang<90) + opp/tan(ang); // Function: hyp_adj_to_opp() @@ -408,9 +408,9 @@ function opp_ang_to_adj(opp,ang) = // Example: // opp = hyp_adj_to_opp(5,4); // Returns: 3 function hyp_adj_to_opp(hyp,adj) = - assert(is_num(hyp)&&hyp>=0) - assert(is_num(adj)&&adj>=0) - sqrt(hyp*hyp-adj*adj); + assert(is_num(hyp)&&hyp>=0) + assert(is_num(adj)&&adj>=0) + sqrt(hyp*hyp-adj*adj); // Function: hyp_ang_to_opp() @@ -424,9 +424,9 @@ function hyp_adj_to_opp(hyp,adj) = // Example: // opp = hyp_ang_to_opp(8,30); // Returns: 4 function hyp_ang_to_opp(hyp,ang) = - assert(is_num(hyp)&&hyp>=0) - assert(is_num(ang)&&ang>0&&ang<90) - hyp*sin(ang); + assert(is_num(hyp)&&hyp>=0) + assert(is_num(ang)&&ang>0&&ang<90) + hyp*sin(ang); // Function: adj_ang_to_opp() @@ -440,9 +440,9 @@ function hyp_ang_to_opp(hyp,ang) = // Example: // opp = adj_ang_to_opp(8,45); // Returns: 8 function adj_ang_to_opp(adj,ang) = - assert(is_num(adj)&&adj>=0) - assert(is_num(ang)&&ang>0&&ang<90) - adj*tan(ang); + assert(is_num(adj)&&adj>=0) + assert(is_num(ang)&&ang>0&&ang<90) + adj*tan(ang); // Function: adj_opp_to_hyp() @@ -456,9 +456,9 @@ function adj_ang_to_opp(adj,ang) = // Example: // hyp = adj_opp_to_hyp(3,4); // Returns: 5 function adj_opp_to_hyp(adj,opp) = - assert(is_num(adj)&&adj>=0) - assert(is_num(opp)&&opp>=0) - norm([opp,adj]); + assert(is_num(adj)&&adj>=0) + assert(is_num(opp)&&opp>=0) + norm([opp,adj]); // Function: adj_ang_to_hyp() @@ -472,9 +472,9 @@ function adj_opp_to_hyp(adj,opp) = // Example: // hyp = adj_ang_to_hyp(4,60); // Returns: 8 function adj_ang_to_hyp(adj,ang) = - assert(is_num(adj)&&adj>=0) - assert(is_num(ang)&&ang>=0&&ang<90) - adj/cos(ang); + assert(is_num(adj)&&adj>=0) + assert(is_num(ang)&&ang>=0&&ang<90) + adj/cos(ang); // Function: opp_ang_to_hyp() @@ -488,9 +488,9 @@ function adj_ang_to_hyp(adj,ang) = // Example: // hyp = opp_ang_to_hyp(4,30); // Returns: 8 function opp_ang_to_hyp(opp,ang) = - assert(is_num(opp)&&opp>=0) - assert(is_num(ang)&&ang>0&&ang<=90) - opp/sin(ang); + assert(is_num(opp)&&opp>=0) + assert(is_num(ang)&&ang>0&&ang<=90) + opp/sin(ang); // Function: hyp_adj_to_ang() @@ -504,9 +504,9 @@ function opp_ang_to_hyp(opp,ang) = // Example: // ang = hyp_adj_to_ang(8,4); // Returns: 60 degrees function hyp_adj_to_ang(hyp,adj) = - assert(is_num(hyp)&&hyp>0) - assert(is_num(adj)&&adj>=0) - acos(adj/hyp); + assert(is_num(hyp)&&hyp>0) + assert(is_num(adj)&&adj>=0) + acos(adj/hyp); // Function: hyp_opp_to_ang() @@ -520,9 +520,9 @@ function hyp_adj_to_ang(hyp,adj) = // Example: // ang = hyp_opp_to_ang(8,4); // Returns: 30 degrees function hyp_opp_to_ang(hyp,opp) = - assert(is_num(hyp)&&hyp>0) - assert(is_num(opp)&&opp>=0) - asin(opp/hyp); + assert(is_num(hyp)&&hyp>0) + assert(is_num(opp)&&opp>=0) + asin(opp/hyp); // Function: adj_opp_to_ang() @@ -536,9 +536,9 @@ function hyp_opp_to_ang(hyp,opp) = // Example: // ang = adj_opp_to_ang(sqrt(3)/2,0.5); // Returns: 30 degrees function adj_opp_to_ang(adj,opp) = - assert(is_num(adj)&&adj>=0) - assert(is_num(opp)&&opp>=0) - atan2(opp,adj); + assert(is_num(adj)&&adj>=0) + assert(is_num(opp)&&opp>=0) + atan2(opp,adj); // Function: triangle_area() @@ -551,11 +551,11 @@ function adj_opp_to_ang(adj,opp) = // triangle_area([0,0], [5,10], [10,0]); // Returns -50 // triangle_area([10,0], [5,10], [0,0]); // Returns 50 function triangle_area(a,b,c) = - len(a)==3? 0.5*norm(cross(c-a,c-b)) : ( - a.x * (b.y - c.y) + - b.x * (c.y - a.y) + - c.x * (a.y - b.y) - ) / 2; + len(a)==3? 0.5*norm(cross(c-a,c-b)) : ( + a.x * (b.y - c.y) + + b.x * (c.y - a.y) + + c.x * (a.y - b.y) + ) / 2; @@ -572,12 +572,12 @@ function triangle_area(a,b,c) = // p2 = The second point on the plane. // p3 = The third point on the plane. function plane3pt(p1, p2, p3) = - let( - p1=point3d(p1), - p2=point3d(p2), - p3=point3d(p3), - normal = unit(cross(p3-p1, p2-p1)) - ) concat(normal, [normal*p1]); + let( + p1=point3d(p1), + p2=point3d(p2), + p3=point3d(p3), + normal = unit(cross(p3-p1, p2-p1)) + ) concat(normal, [normal*p1]); // Function: plane3pt_indexed() @@ -594,11 +594,11 @@ function plane3pt(p1, p2, p3) = // i2 = The index into `points` of the second point on the plane. // i3 = The index into `points` of the third point on the plane. function plane3pt_indexed(points, i1, i2, i3) = - let( - p1 = points[i1], - p2 = points[i2], - p3 = points[i3] - ) plane3pt(p1,p2,p3); + let( + p1 = points[i1], + p2 = points[i2], + p3 = points[i3] + ) plane3pt(p1,p2,p3); // Function: plane_from_normal() @@ -632,17 +632,17 @@ function plane_from_normal(normal, pt=[0,0,0]) = // cp = centroid(xyzpath); // move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow(); function plane_from_points(points, fast=false, eps=EPSILON) = - let( - points = deduplicate(points), - indices = sort(find_noncollinear_points(points)), - p1 = points[indices[0]], - p2 = points[indices[1]], - p3 = points[indices[2]], - plane = plane3pt(p1,p2,p3), - all_coplanar = fast || all([ - for (pt = points) coplanar(plane,pt,eps=eps) - ]) - ) all_coplanar? plane : undef; + let( + points = deduplicate(points), + indices = sort(find_noncollinear_points(points)), + p1 = points[indices[0]], + p2 = points[indices[1]], + p3 = points[indices[2]], + plane = plane3pt(p1,p2,p3), + all_coplanar = fast || all([ + for (pt = points) coplanar(plane,pt,eps=eps) + ]) + ) all_coplanar? plane : undef; // Function: plane_from_polygon() @@ -665,17 +665,17 @@ function plane_from_points(points, fast=false, eps=EPSILON) = // cp = centroid(xyzpath); // move(cp) rot(from=UP,to=plane_normal(plane)) anchor_arrow(); function plane_from_polygon(poly, fast=false, eps=EPSILON) = - let( - poly = deduplicate(poly), - n = polygon_normal(poly), - plane = [n.x, n.y, n.z, n*poly[0]] - ) fast? plane : let( - all_coplanar = [ - for (pt = poly) - if (!coplanar(plane,pt,eps=eps)) 1 - ] == [] - ) all_coplanar? plane : - undef; + let( + poly = deduplicate(poly), + n = polygon_normal(poly), + plane = [n.x, n.y, n.z, n*poly[0]] + ) fast? plane : let( + all_coplanar = [ + for (pt = poly) + if (!coplanar(plane,pt,eps=eps)) 1 + ] == [] + ) all_coplanar? plane : + undef; // Function: plane_normal() @@ -713,10 +713,10 @@ function plane_offset(plane) = plane[3]; // #stroke(xyzpath,closed=true); // stroke(xypath,closed=true); function plane_transform(plane) = - let( - n = plane_normal(plane), - cp = n * plane[3] - ) rot(from=n, to=UP) * move(-cp); + let( + n = plane_normal(plane), + cp = n * plane[3] + ) rot(from=n, to=UP) * move(-cp); // Function: plane_point_nearest_origin() @@ -725,7 +725,7 @@ function plane_transform(plane) = // Description: // Returns the point on the plane that is closest to the origin. function plane_point_nearest_origin(plane) = - plane_normal(plane) * plane[3]; + plane_normal(plane) * plane[3]; // Function: distance_from_plane() @@ -742,7 +742,7 @@ function plane_point_nearest_origin(plane) = // plane = The [A,B,C,D] values for the equation of the plane. // point = The point to test. function distance_from_plane(plane, point) = - [plane.x, plane.y, plane.z] * point3d(point) - plane[3]; + [plane.x, plane.y, plane.z] * point3d(point) - plane[3]; // Function: closest_point_on_plane() @@ -755,31 +755,31 @@ function distance_from_plane(plane, point) = // plane = The [A,B,C,D] values for the equation of the plane. // point = The 3D point to find the closest point to. function closest_point_on_plane(plane, point) = - let( - n = unit(plane_normal(plane)), - d = distance_from_plane(plane, point) - ) point - n*d; + let( + n = unit(plane_normal(plane)), + d = distance_from_plane(plane, point) + ) point - n*d; // Returns [POINT, U] if line intersects plane at one point. // Returns [LINE, undef] if the line is on the plane. // Returns undef if line is parallel to, but not on the given plane. function _general_plane_line_intersection(plane, line, eps=EPSILON) = - let( - p0 = line[0], - p1 = line[1], - n = plane_normal(plane), - u = p1 - p0, - d = n * u - ) abs(d)1? undef : - res[0]; + assert(is_vector(plane)&&len(plane)==4, "Invalid plane value.") + assert(is_path(line)&&len(line)==2, "Invalid line value.") + assert(!approx(line[0],line[1]), "The two points defining the line must not be the same point.") + let( + bounded = is_list(bounded)? bounded : [bounded, bounded], + res = _general_plane_line_intersection(plane, line, eps=eps) + ) + is_undef(res)? undef : + is_undef(res[1])? res[0] : + bounded[0]&&res[1]<0? undef : + bounded[1]&&res[1]>1? undef : + res[0]; // Function: polygon_line_intersection() @@ -839,47 +839,47 @@ function plane_line_intersection(plane, line, bounded=false, eps=EPSILON) = // bounded = If false, the line is considered unbounded. If true, it is treated as a bounded line segment. If given as `[true, false]` or `[false, true]`, the boundedness of the points are specified individually, allowing the line to be treated as a half-bounded ray. Default: false (unbounded) // eps = The epsilon error value to determine whether the line is too close to parallel to the plane. Default: `EPSILON` (1e-9) function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) = - assert(is_path(poly)) - assert(is_path(line)&&len(line)==2) - let( - bounded = is_list(bounded)? bounded : [bounded, bounded], - poly = deduplicate(poly), - indices = sort(find_noncollinear_points(poly)), - p1 = poly[indices[0]], - p2 = poly[indices[1]], - p3 = poly[indices[2]], - plane = plane3pt(p1,p2,p3), - res = _general_plane_line_intersection(plane, line, eps=eps) - ) - is_undef(res)? undef : - is_undef(res[1])? ( - let( - // Line is on polygon plane. - linevec = unit(line[1] - line[0]), - lp1 = line[0] + (bounded[0]? 0 : -1000000) * linevec, - lp2 = line[1] + (bounded[1]? 0 : 1000000) * linevec, - poly2d = clockwise_polygon(project_plane(poly, p1, p2, p3)), - line2d = project_plane([lp1,lp2], p1, p2, p3), - parts = split_path_at_region_crossings(line2d, [poly2d], closed=false), - inside = [ - for (part = parts) - if (point_in_polygon(mean(part), poly2d)>0) part - ] - ) !inside? undef : - let( - isegs = [ - for (seg = inside) - lift_plane(seg, p1, p2, p3) - ] - ) isegs - ) : - bounded[0]&&res[1]<0? undef : - bounded[1]&&res[1]>1? undef : - let( - proj = clockwise_polygon(project_plane(poly, p1, p2, p3)), - pt = project_plane(res[0], p1, p2, p3) - ) point_in_polygon(pt, proj) < 0? undef : - res[0]; + assert(is_path(poly)) + assert(is_path(line)&&len(line)==2) + let( + bounded = is_list(bounded)? bounded : [bounded, bounded], + poly = deduplicate(poly), + indices = sort(find_noncollinear_points(poly)), + p1 = poly[indices[0]], + p2 = poly[indices[1]], + p3 = poly[indices[2]], + plane = plane3pt(p1,p2,p3), + res = _general_plane_line_intersection(plane, line, eps=eps) + ) + is_undef(res)? undef : + is_undef(res[1])? ( + let( + // Line is on polygon plane. + linevec = unit(line[1] - line[0]), + lp1 = line[0] + (bounded[0]? 0 : -1000000) * linevec, + lp2 = line[1] + (bounded[1]? 0 : 1000000) * linevec, + poly2d = clockwise_polygon(project_plane(poly, p1, p2, p3)), + line2d = project_plane([lp1,lp2], p1, p2, p3), + parts = split_path_at_region_crossings(line2d, [poly2d], closed=false), + inside = [ + for (part = parts) + if (point_in_polygon(mean(part), poly2d)>0) part + ] + ) !inside? undef : + let( + isegs = [ + for (seg = inside) + lift_plane(seg, p1, p2, p3) + ] + ) isegs + ) : + bounded[0]&&res[1]<0? undef : + bounded[1]&&res[1]>1? undef : + let( + proj = clockwise_polygon(project_plane(poly, p1, p2, p3)), + pt = project_plane(res[0], p1, p2, p3) + ) point_in_polygon(pt, proj) < 0? undef : + res[0]; // Function: plane_intersection() @@ -891,19 +891,19 @@ function polygon_line_intersection(poly, line, bounded=false, eps=EPSILON) = // is returned as a list of two points on the line of intersection. If any of the input planes are parallel // then returns undef. function plane_intersection(plane1,plane2,plane3) = - is_def(plane3)? let( - matrix = [for(p=[plane1,plane2,plane3]) select(p,0,2)], - rhs = [for(p=[plane1,plane2,plane3]) p[3]] - ) linear_solve(matrix,rhs) : - let( - normal = cross(plane_normal(plane1), plane_normal(plane2)) - ) approx(norm(normal),0) ? undef : - let( - matrix = [for(p=[plane1,plane2]) select(p,0,2)], - rhs = [for(p=[plane1,plane2]) p[3]], - point = linear_solve(matrix,rhs) - ) is_undef(point)? undef : - [point, point+normal]; + is_def(plane3)? let( + matrix = [for(p=[plane1,plane2,plane3]) select(p,0,2)], + rhs = [for(p=[plane1,plane2,plane3]) p[3]] + ) linear_solve(matrix,rhs) : + let( + normal = cross(plane_normal(plane1), plane_normal(plane2)) + ) approx(norm(normal),0) ? undef : + let( + matrix = [for(p=[plane1,plane2]) select(p,0,2)], + rhs = [for(p=[plane1,plane2]) p[3]], + point = linear_solve(matrix,rhs) + ) is_undef(point)? undef : + [point, point+normal]; // Function: coplanar() @@ -918,7 +918,7 @@ function plane_intersection(plane1,plane2,plane3) = // point = The point to test. // eps = How much variance is allowed in testing that each point is on the same plane. Default: `EPSILON` (1e-9) function coplanar(plane, point, eps=EPSILON) = - abs(distance_from_plane(plane, point)) <= eps; + abs(distance_from_plane(plane, point)) <= eps; // Function: points_are_coplanar() @@ -930,10 +930,10 @@ function coplanar(plane, point, eps=EPSILON) = // points = The list of points to test. // eps = How much variance is allowed in testing that each point is on the same plane. Default: `EPSILON` (1e-9) function points_are_coplanar(points, eps=EPSILON) = - points_are_collinear(points, eps=eps)? true : - let( - plane = plane_from_points(points, fast=true, eps=eps) - ) all([for (pt = points) coplanar(plane, pt, eps=eps)]); + points_are_collinear(points, eps=eps)? true : + let( + plane = plane_from_points(points, fast=true, eps=eps) + ) all([for (pt = points) coplanar(plane, pt, eps=eps)]); @@ -949,7 +949,7 @@ function points_are_coplanar(points, eps=EPSILON) = // plane = The [A,B,C,D] values for the equation of the plane. // point = The point to test. function in_front_of_plane(plane, point) = - distance_from_plane(plane, point) > EPSILON; + distance_from_plane(plane, point) > EPSILON; @@ -995,27 +995,27 @@ function in_front_of_plane(plane, point) = // labels = [[pts[0], "pt1"], [pts[1],"pt2"], [pts[2],"pt3"], [circ[0], "CP"], [circ[0]+[cos(315),sin(315)]*rad*0.7, "r"]]; // for(l=labels) translate(l[0]+[0,2]) color("black") text(text=l[1], size=2.5, halign="center"); function find_circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) = - let(r = get_radius(r=r, d=d, dflt=undef)) - assert(r!=undef, "Must specify either r or d.") - (is_undef(pt2) && is_undef(pt3) && is_list(pt1))? find_circle_2tangents(pt1[0], pt1[1], pt1[2], r=r) : - collinear(pt1, pt2, pt3)? undef : - let( - v1 = unit(pt1 - pt2), - v2 = unit(pt3 - pt2), - vmid = unit(mean([v1, v2])), - n = vector_axis(v1, v2), - a = vector_angle(v1, v2), - hyp = r / sin(a/2), - cp = pt2 + hyp * vmid - ) !tangents? [cp, n] : - let( - x = hyp * cos(a/2), - tp1 = pt2 + x * v1, - tp2 = pt2 + x * v2, - fff=echo(tp1=tp1,cp=cp,pt2=pt2), - dang1 = vector_angle(tp1-cp,pt2-cp), - dang2 = vector_angle(tp2-cp,pt2-cp) - ) [cp, n, tp1, tp2, dang1, dang2]; + let(r = get_radius(r=r, d=d, dflt=undef)) + assert(r!=undef, "Must specify either r or d.") + (is_undef(pt2) && is_undef(pt3) && is_list(pt1))? find_circle_2tangents(pt1[0], pt1[1], pt1[2], r=r) : + collinear(pt1, pt2, pt3)? undef : + let( + v1 = unit(pt1 - pt2), + v2 = unit(pt3 - pt2), + vmid = unit(mean([v1, v2])), + n = vector_axis(v1, v2), + a = vector_angle(v1, v2), + hyp = r / sin(a/2), + cp = pt2 + hyp * vmid + ) !tangents? [cp, n] : + let( + x = hyp * cos(a/2), + tp1 = pt2 + x * v1, + tp2 = pt2 + x * v2, + fff=echo(tp1=tp1,cp=cp,pt2=pt2), + dang1 = vector_angle(tp1-cp,pt2-cp), + dang2 = vector_angle(tp2-cp,pt2-cp) + ) [cp, n, tp1, tp2, dang1, dang2]; // Function: find_circle_3points() @@ -1039,34 +1039,34 @@ function find_circle_2tangents(pt1, pt2, pt3, r, d, tangents=false) = // translate(circ[0]) color("red") circle(d=3, $fn=12); // move_copies(pts) color("blue") circle(d=3, $fn=12); function find_circle_3points(pt1, pt2, pt3) = - (is_undef(pt2) && is_undef(pt3) && is_list(pt1))? find_circle_3points(pt1[0], pt1[1], pt1[2]) : - collinear(pt1,pt2,pt3)? [undef,undef,undef] : - let( - v1 = pt1-pt2, - v2 = pt3-pt2, - n = vector_axis(v1,v2), - n2 = n.z<0? -n : n - ) len(pt1)+len(pt2)+len(pt3)>6? ( - let( - a = project_plane(pt1, pt1, pt2, pt3), - b = project_plane(pt2, pt1, pt2, pt3), - c = project_plane(pt3, pt1, pt2, pt3), - res = find_circle_3points(a, b, c) - ) res[0]==undef? [undef,undef,undef] : let( - cp = lift_plane(res[0], pt1, pt2, pt3), - r = norm(pt2-cp) - ) [cp, r, n2] - ) : let( - mp1 = pt2 + v1/2, - mp2 = pt2 + v2/2, - mpv1 = rot(90, v=n, p=v1), - mpv2 = rot(90, v=n, p=v2), - l1 = [mp1, mp1+mpv1], - l2 = [mp2, mp2+mpv2], - isect = line_intersection(l1,l2) - ) is_undef(isect)? [undef,undef,undef] : let( - r = norm(pt2-isect) - ) [isect, r, n2]; + (is_undef(pt2) && is_undef(pt3) && is_list(pt1))? find_circle_3points(pt1[0], pt1[1], pt1[2]) : + collinear(pt1,pt2,pt3)? [undef,undef,undef] : + let( + v1 = pt1-pt2, + v2 = pt3-pt2, + n = vector_axis(v1,v2), + n2 = n.z<0? -n : n + ) len(pt1)+len(pt2)+len(pt3)>6? ( + let( + a = project_plane(pt1, pt1, pt2, pt3), + b = project_plane(pt2, pt1, pt2, pt3), + c = project_plane(pt3, pt1, pt2, pt3), + res = find_circle_3points(a, b, c) + ) res[0]==undef? [undef,undef,undef] : let( + cp = lift_plane(res[0], pt1, pt2, pt3), + r = norm(pt2-cp) + ) [cp, r, n2] + ) : let( + mp1 = pt2 + v1/2, + mp2 = pt2 + v2/2, + mpv1 = rot(90, v=n, p=v1), + mpv2 = rot(90, v=n, p=v2), + l1 = [mp1, mp1+mpv1], + l2 = [mp2, mp2+mpv2], + isect = line_intersection(l1,l2) + ) is_undef(isect)? [undef,undef,undef] : let( + r = norm(pt2-isect) + ) [isect, r, n2]; @@ -1089,20 +1089,20 @@ function find_circle_3points(pt1, pt2, pt3) = // color("red") move_copies(tanpts) circle(d=3,$fn=12); // color("blue") move_copies([cp,pt]) circle(d=3,$fn=12); function circle_point_tangents(r, d, cp, pt) = - assert(is_num(r) || is_num(d)) - assert(is_vector(cp)) - assert(is_vector(pt)) - let( - r = get_radius(r=r, d=d, dflt=1), - delta = pt - cp, - dist = norm(delta), - baseang = atan2(delta.y,delta.x) - ) dist < r? [] : - approx(dist,r)? [[baseang, pt]] : - let( - relang = acos(r/dist), - angs = [baseang + relang, baseang - relang] - ) [for (ang=angs) [ang, cp + r*[cos(ang),sin(ang)]]]; + assert(is_num(r) || is_num(d)) + assert(is_vector(cp)) + assert(is_vector(pt)) + let( + r = get_radius(r=r, d=d, dflt=1), + delta = pt - cp, + dist = norm(delta), + baseang = atan2(delta.y,delta.x) + ) dist < r? [] : + approx(dist,r)? [[baseang, pt]] : + let( + relang = acos(r/dist), + angs = [baseang + relang, baseang - relang] + ) [for (ang=angs) [ang, cp + r*[cos(ang),sin(ang)]]]; @@ -1192,7 +1192,7 @@ function circle_circle_tangents(c1,r1,c2,r2,d1,d2) = // i2 = The second point. // points = The list of points to find a non-collinear point from. function first_noncollinear(i1, i2, points) = - [for (j = idx(points)) if (j!=i1 && j!=i2 && !collinear_indexed(points,i1,i2,j)) j][0]; + [for (j = idx(points)) if (j!=i1 && j!=i2 && !collinear_indexed(points,i1,i2,j)) j][0]; // Function: find_noncollinear_points() @@ -1201,20 +1201,20 @@ function first_noncollinear(i1, i2, points) = // Description: // Finds the indices of three good non-collinear points from the points list `points`. function find_noncollinear_points(points) = - let( - a = 0, - b = furthest_point(points[a], points), - pa = points[a], - pb = points[b], - c = max_index([ - for (p=points) - (approx(p,pa) || approx(p,pb))? 0 : - sin(vector_angle(points[a]-p,points[b]-p)) * - norm(p-points[a]) * norm(p-points[b]) - ]) - ) - assert(c!=a && c!=b, "Cannot find three noncollinear points in pointlist.") - [a, b, c]; + let( + a = 0, + b = furthest_point(points[a], points), + pa = points[a], + pb = points[b], + c = max_index([ + for (p=points) + (approx(p,pa) || approx(p,pb))? 0 : + sin(vector_angle(points[a]-p,points[b]-p)) * + norm(p-points[a]) * norm(p-points[b]) + ]) + ) + assert(c!=a && c!=b, "Cannot find three noncollinear points in pointlist.") + [a, b, c]; // Function: pointlist_bounds() @@ -1226,8 +1226,8 @@ function find_noncollinear_points(points) = // Arguments: // pts = List of points. function pointlist_bounds(pts) = [ - [for (a=[0:2]) min([ for (x=pts) point3d(x)[a] ]) ], - [for (a=[0:2]) max([ for (x=pts) point3d(x)[a] ]) ] + [for (a=[0:2]) min([ for (x=pts) point3d(x)[a] ]) ], + [for (a=[0:2]) max([ for (x=pts) point3d(x)[a] ]) ] ]; @@ -1240,7 +1240,7 @@ function pointlist_bounds(pts) = [ // pt = The point to find the closest point to. // points = The list of points to search. function closest_point(pt, points) = - min_index([for (p=points) norm(p-pt)]); + min_index([for (p=points) norm(p-pt)]); // Function: furthest_point() @@ -1252,7 +1252,7 @@ function closest_point(pt, points) = // pt = The point to find the farthest point from. // points = The list of points to search. function furthest_point(pt, points) = - max_index([for (p=points) norm(p-pt)]); + max_index([for (p=points) norm(p-pt)]); @@ -1264,16 +1264,16 @@ function furthest_point(pt, points) = // Description: // Given a 2D or 3D planar polygon, returns the area of that polygon. If the polygon is self-crossing, the results are undefined. function polygon_area(poly) = - len(poly)<3? 0 : - len(poly[0])==2? 0.5*sum([for(i=[0:1:len(poly)-1]) det2(select(poly,i,i+1))]) : - let( - plane = plane_from_points(poly) - ) plane==undef? undef : - let( - n = unit(plane_normal(plane)), - total = sum([for (i=[0:1:len(poly)-1]) cross(poly[i], select(poly,i+1))]), - res = abs(total * n) / 2 - ) res; + len(poly)<3? 0 : + len(poly[0])==2? 0.5*sum([for(i=[0:1:len(poly)-1]) det2(select(poly,i,i+1))]) : + let( + plane = plane_from_points(poly) + ) plane==undef? undef : + let( + n = unit(plane_normal(plane)), + total = sum([for (i=[0:1:len(poly)-1]) cross(poly[i], select(poly,i+1))]), + res = abs(total * n) / 2 + ) res; // Function: polygon_is_convex() @@ -1287,12 +1287,12 @@ function polygon_area(poly) = // spiral = [for (i=[0:36]) let(a=-i*10) (10+i)*[cos(a),sin(a)]]; // polygon_is_convex(spiral); // Returns: false function polygon_is_convex(poly) = - let( - l = len(poly), - c = [for (i=idx(poly)) cross(poly[(i+1)%l]-poly[i],poly[(i+2)%l]-poly[(i+1)%l])] - ) - len([for (x=c) if(x>0) 1])==0 || - len([for (x=c) if(x<0) 1])==0; + let( + l = len(poly), + c = [for (i=idx(poly)) cross(poly[(i+1)%l]-poly[i],poly[(i+2)%l]-poly[(i+1)%l])] + ) + len([for (x=c) if(x>0) 1])==0 || + len([for (x=c) if(x<0) 1])==0; // Function: polygon_shift() @@ -1306,7 +1306,7 @@ function polygon_is_convex(poly) = // Example: // polygon_shift([[3,4], [8,2], [0,2], [-4,0]], 2); // Returns [[0,2], [-4,0], [3,4], [8,2]] function polygon_shift(poly, i) = - list_rotate(cleanup_path(poly), i); + list_rotate(cleanup_path(poly), i); // Function: polygon_shift_to_closest_point() @@ -1315,11 +1315,11 @@ function polygon_shift(poly, i) = // Description: // Given a polygon `path`, rotates the point ordering so that the first point in the path is the one closest to the given point `pt`. function polygon_shift_to_closest_point(path, pt) = - let( - path = cleanup_path(path), - dists = [for (p=path) norm(p-pt)], - closest = min_index(dists) - ) select(path,closest,closest+len(path)-1); + let( + path = cleanup_path(path), + dists = [for (p=path) norm(p-pt)], + closest = min_index(dists) + ) select(path,closest,closest+len(path)-1); // Function: reindex_polygon() @@ -1352,30 +1352,30 @@ function polygon_shift_to_closest_point(path, pt) = // color("red") move_copies([pent[0],circ[0]]) circle(r=.1,$fn=32); // color("blue") translate(reindexed[0])circle(r=.1,$fn=32); function reindex_polygon(reference, poly, return_error=false) = - assert(is_path(reference) && is_path(poly)) - assert(len(reference)==len(poly), "Polygons must be the same length in reindex_polygon") - let( - dim = len(reference[0]), - N = len(reference), - fixpoly = dim != 2? poly : - polygon_is_clockwise(reference)? clockwise_polygon(poly) : - ccw_polygon(poly), - dist = [ - // Matrix of all pairwise distances - for (p1=reference) [ - for (p2=fixpoly) norm(p1-p2) - ] - ], - // Compute the sum of all distance pairs for a each shift - sums = [ - for(shift=[0:1:N-1]) sum([ - for(i=[0:1:N-1]) dist[i][(i+shift)%N] - ]) - ], - optimal_poly = polygon_shift(fixpoly,min_index(sums)) - ) - return_error? [optimal_poly, min(sums)] : - optimal_poly; + assert(is_path(reference) && is_path(poly)) + assert(len(reference)==len(poly), "Polygons must be the same length in reindex_polygon") + let( + dim = len(reference[0]), + N = len(reference), + fixpoly = dim != 2? poly : + polygon_is_clockwise(reference)? clockwise_polygon(poly) : + ccw_polygon(poly), + dist = [ + // Matrix of all pairwise distances + for (p1=reference) [ + for (p2=fixpoly) norm(p1-p2) + ] + ], + // Compute the sum of all distance pairs for a each shift + sums = [ + for(shift=[0:1:N-1]) sum([ + for(i=[0:1:N-1]) dist[i][(i+shift)%N] + ]) + ], + optimal_poly = polygon_shift(fixpoly,min_index(sums)) + ) + return_error? [optimal_poly, min(sums)] : + optimal_poly; // Function: align_polygon() @@ -1398,19 +1398,19 @@ function reindex_polygon(reference, poly, return_error=false) = // color("red") move_copies(scale(1.4,p=align_polygon(pentagon,hexagon,[0:10:359]))) circle(r=.1); // move_copies(concat(pentagon,hexagon))circle(r=.1); function align_polygon(reference, poly, angles, cp) = - assert(is_path(reference) && is_path(poly)) - assert(len(reference)==len(poly), "Polygons must be the same length to be aligned in align_polygon") - assert(is_num(angles[0]), "The `angle` parameter to align_polygon must be a range or vector") - let( // alignments is a vector of entries of the form: [polygon, error] - alignments = [ - for(angle=angles) reindex_polygon( - reference, - zrot(angle,p=poly,cp=cp), - return_error=true - ) - ], - best = min_index(subindex(alignments,1)) - ) alignments[best][0]; + assert(is_path(reference) && is_path(poly)) + assert(len(reference)==len(poly), "Polygons must be the same length to be aligned in align_polygon") + assert(is_num(angles[0]), "The `angle` parameter to align_polygon must be a range or vector") + let( // alignments is a vector of entries of the form: [polygon, error] + alignments = [ + for(angle=angles) reindex_polygon( + reference, + zrot(angle,p=poly,cp=cp), + return_error=true + ) + ], + best = min_index(subindex(alignments,1)) + ) alignments[best][0]; // Function: centroid() @@ -1421,22 +1421,22 @@ function align_polygon(reference, poly, angles, cp) = // Given a simple 3D planar polygon, returns the 3D coordinates of the polygon's centroid. // If the polygon is self-intersecting, the results are undefined. function centroid(poly) = - len(poly[0])==2? ( - sum([ - for(i=[0:len(poly)-1]) - let(segment=select(poly,i,i+1)) - det2(segment)*sum(segment) - ]) / 6 / polygon_area(poly) - ) : ( - let( - n = plane_normal(plane_from_points(poly)), - p1 = vector_angle(n,UP)>15? vector_axis(n,UP) : vector_axis(n,RIGHT), - p2 = vector_axis(n,p1), - cp = mean(poly), - proj = project_plane(poly,cp,cp+p1,cp+p2), - cxy = centroid(proj) - ) lift_plane(cxy,cp,cp+p1,cp+p2) - ); + len(poly[0])==2? ( + sum([ + for(i=[0:len(poly)-1]) + let(segment=select(poly,i,i+1)) + det2(segment)*sum(segment) + ]) / 6 / polygon_area(poly) + ) : ( + let( + n = plane_normal(plane_from_points(poly)), + p1 = vector_angle(n,UP)>15? vector_axis(n,UP) : vector_axis(n,RIGHT), + p2 = vector_axis(n,p1), + cp = mean(poly), + proj = project_plane(poly,cp,cp+p1,cp+p2), + cxy = centroid(proj) + ) lift_plane(cxy,cp,cp+p1,cp+p2) + ); // Function: point_in_polygon() @@ -1457,11 +1457,11 @@ function centroid(poly) = // path = The list of 2D path points forming the perimeter of the polygon. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function point_in_polygon(point, path, eps=EPSILON) = - // Original algorithm from http://geomalgorithms.com/a03-_inclusion.html - // Does the point lie on any edges? If so return 0. - sum([for(i=[0:1:len(path)-1]) let(seg=select(path,i,i+1)) if(!approx(seg[0],seg[1],eps=eps)) point_on_segment2d(point, seg, eps=eps)?1:0]) > 0? 0 : - // Otherwise compute winding number and return 1 for interior, -1 for exterior - sum([for(i=[0:1:len(path)-1]) let(seg=select(path,i,i+1)) if(!approx(seg[0],seg[1],eps=eps)) _point_above_below_segment(point, seg)]) != 0? 1 : -1; + // Original algorithm from http://geomalgorithms.com/a03-_inclusion.html + // Does the point lie on any edges? If so return 0. + sum([for(i=[0:1:len(path)-1]) let(seg=select(path,i,i+1)) if(!approx(seg[0],seg[1],eps=eps)) point_on_segment2d(point, seg, eps=eps)?1:0]) > 0? 0 : + // Otherwise compute winding number and return 1 for interior, -1 for exterior + sum([for(i=[0:1:len(path)-1]) let(seg=select(path,i,i+1)) if(!approx(seg[0],seg[1],eps=eps)) _point_above_below_segment(point, seg)]) != 0? 1 : -1; // Function: polygon_is_clockwise() @@ -1473,15 +1473,15 @@ function point_in_polygon(point, path, eps=EPSILON) = // Arguments: // path = The list of 2D path points for the perimeter of the polygon. function polygon_is_clockwise(path) = - assert(is_path(path) && len(path[0])==2, "Input must be a 2d path") - let( - minx = min(subindex(path,0)), - lowind = search(minx, path, 0, 0), - lowpts = select(path, lowind), - miny = min(subindex(lowpts, 1)), - extreme_sub = search(miny, lowpts, 1, 1)[0], - extreme = select(lowind,extreme_sub) - ) det2([select(path,extreme+1)-path[extreme], select(path, extreme-1)-path[extreme]])<0; + assert(is_path(path) && len(path[0])==2, "Input must be a 2d path") + let( + minx = min(subindex(path,0)), + lowind = search(minx, path, 0, 0), + lowpts = select(path, lowind), + miny = min(subindex(lowpts, 1)), + extreme_sub = search(miny, lowpts, 1, 1)[0], + extreme = select(lowind,extreme_sub) + ) det2([select(path,extreme+1)-path[extreme], select(path, extreme-1)-path[extreme]])<0; // Function: clockwise_polygon() @@ -1490,7 +1490,7 @@ function polygon_is_clockwise(path) = // Description: // Given a 2D polygon path, returns the clockwise winding version of that path. function clockwise_polygon(path) = - polygon_is_clockwise(path)? path : reverse_polygon(path); + polygon_is_clockwise(path)? path : reverse_polygon(path); // Function: ccw_polygon() @@ -1499,7 +1499,7 @@ function clockwise_polygon(path) = // Description: // Given a 2D polygon path, returns the counter-clockwise winding version of that path. function ccw_polygon(path) = - polygon_is_clockwise(path)? reverse_polygon(path) : path; + polygon_is_clockwise(path)? reverse_polygon(path) : path; // Function: reverse_polygon() @@ -1508,7 +1508,7 @@ function ccw_polygon(path) = // Description: // Reverses a polygon's winding direction, while still using the same start point. function reverse_polygon(poly) = - let(lp=len(poly)) [for (i=idx(poly)) poly[(lp-i)%lp]]; + let(lp=len(poly)) [for (i=idx(poly)) poly[(lp-i)%lp]]; // Function: polygon_normal() @@ -1518,101 +1518,101 @@ function reverse_polygon(poly) = // Given a 3D planar polygon, returns a unit-length normal vector for the // clockwise orientation of the polygon. function polygon_normal(poly) = - let( - poly = path3d(cleanup_path(poly)), - p0 = poly[0], - n = sum([ - for (i=[1:1:len(poly)-2]) - cross(poly[i+1]-p0, poly[i]-p0) - ]) - ) unit(n); + let( + poly = path3d(cleanup_path(poly)), + p0 = poly[0], + n = sum([ + for (i=[1:1:len(poly)-2]) + cross(poly[i+1]-p0, poly[i]-p0) + ]) + ) unit(n); function _split_polygon_at_x(poly, x) = - let( - xs = subindex(poly,0) - ) (min(xs) >= x || max(xs) <= x)? [poly] : - let( - poly2 = [ - for (p = pair_wrap(poly)) each [ - p[0], - if( - (p[0].x < x && p[1].x > x) || - (p[1].x < x && p[0].x > x) - ) let( - u = (x - p[0].x) / (p[1].x - p[0].x) - ) [ - x, // Important for later exact match tests - u*(p[1].y-p[0].y)+p[0].y, - u*(p[1].z-p[0].z)+p[0].z, - ] - ] - ], - out1 = [for (p = poly2) if(p.x <= x) p], - out2 = [for (p = poly2) if(p.x >= x) p], - out = [ - if (len(out1)>=3) out1, - if (len(out2)>=3) out2, - ] - ) out; + let( + xs = subindex(poly,0) + ) (min(xs) >= x || max(xs) <= x)? [poly] : + let( + poly2 = [ + for (p = pair_wrap(poly)) each [ + p[0], + if( + (p[0].x < x && p[1].x > x) || + (p[1].x < x && p[0].x > x) + ) let( + u = (x - p[0].x) / (p[1].x - p[0].x) + ) [ + x, // Important for later exact match tests + u*(p[1].y-p[0].y)+p[0].y, + u*(p[1].z-p[0].z)+p[0].z, + ] + ] + ], + out1 = [for (p = poly2) if(p.x <= x) p], + out2 = [for (p = poly2) if(p.x >= x) p], + out = [ + if (len(out1)>=3) out1, + if (len(out2)>=3) out2, + ] + ) out; function _split_polygon_at_y(poly, y) = - let( - ys = subindex(poly,1) - ) (min(ys) >= y || max(ys) <= y)? [poly] : - let( - poly2 = [ - for (p = pair_wrap(poly)) each [ - p[0], - if( - (p[0].y < y && p[1].y > y) || - (p[1].y < y && p[0].y > y) - ) let( - u = (y - p[0].y) / (p[1].y - p[0].y) - ) [ - u*(p[1].x-p[0].x)+p[0].x, - y, // Important for later exact match tests - u*(p[1].z-p[0].z)+p[0].z, - ] - ] - ], - out1 = [for (p = poly2) if(p.y <= y) p], - out2 = [for (p = poly2) if(p.y >= y) p], - out = [ - if (len(out1)>=3) out1, - if (len(out2)>=3) out2, - ] - ) out; + let( + ys = subindex(poly,1) + ) (min(ys) >= y || max(ys) <= y)? [poly] : + let( + poly2 = [ + for (p = pair_wrap(poly)) each [ + p[0], + if( + (p[0].y < y && p[1].y > y) || + (p[1].y < y && p[0].y > y) + ) let( + u = (y - p[0].y) / (p[1].y - p[0].y) + ) [ + u*(p[1].x-p[0].x)+p[0].x, + y, // Important for later exact match tests + u*(p[1].z-p[0].z)+p[0].z, + ] + ] + ], + out1 = [for (p = poly2) if(p.y <= y) p], + out2 = [for (p = poly2) if(p.y >= y) p], + out = [ + if (len(out1)>=3) out1, + if (len(out2)>=3) out2, + ] + ) out; function _split_polygon_at_z(poly, z) = - let( - zs = subindex(poly,2) - ) (min(zs) >= z || max(zs) <= z)? [poly] : - let( - poly2 = [ - for (p = pair_wrap(poly)) each [ - p[0], - if( - (p[0].z < z && p[1].z > z) || - (p[1].z < z && p[0].z > z) - ) let( - u = (z - p[0].z) / (p[1].z - p[0].z) - ) [ - u*(p[1].x-p[0].x)+p[0].x, - u*(p[1].y-p[0].y)+p[0].y, - z, // Important for later exact match tests - ] - ] - ], - out1 = [for (p = poly2) if(p.z <= z) p], - out2 = [for (p = poly2) if(p.z >= z) p], - out = [ - if (len(out1)>=3) out1, - if (len(out2)>=3) out2, - ] - ) out; + let( + zs = subindex(poly,2) + ) (min(zs) >= z || max(zs) <= z)? [poly] : + let( + poly2 = [ + for (p = pair_wrap(poly)) each [ + p[0], + if( + (p[0].z < z && p[1].z > z) || + (p[1].z < z && p[0].z > z) + ) let( + u = (z - p[0].z) / (p[1].z - p[0].z) + ) [ + u*(p[1].x-p[0].x)+p[0].x, + u*(p[1].y-p[0].y)+p[0].y, + z, // Important for later exact match tests + ] + ] + ], + out1 = [for (p = poly2) if(p.z <= z) p], + out2 = [for (p = poly2) if(p.z >= z) p], + out = [ + if (len(out1)>=3) out1, + if (len(out2)>=3) out2, + ] + ) out; // Function: split_polygons_at_each_x() @@ -1624,13 +1624,13 @@ function _split_polygon_at_z(poly, z) = // polys = A list of 3D polygons to split. // xs = A list of scalar X values to split at. function split_polygons_at_each_x(polys, xs, _i=0) = - _i>=len(xs)? polys : - split_polygons_at_each_x( - [ - for (poly = polys) - each _split_polygon_at_x(poly, xs[_i]) - ], xs, _i=_i+1 - ); + _i>=len(xs)? polys : + split_polygons_at_each_x( + [ + for (poly = polys) + each _split_polygon_at_x(poly, xs[_i]) + ], xs, _i=_i+1 + ); // Function: split_polygons_at_each_y() @@ -1642,13 +1642,13 @@ function split_polygons_at_each_x(polys, xs, _i=0) = // polys = A list of 3D polygons to split. // ys = A list of scalar Y values to split at. function split_polygons_at_each_y(polys, ys, _i=0) = - _i>=len(ys)? polys : - split_polygons_at_each_y( - [ - for (poly = polys) - each _split_polygon_at_y(poly, ys[_i]) - ], ys, _i=_i+1 - ); + _i>=len(ys)? polys : + split_polygons_at_each_y( + [ + for (poly = polys) + each _split_polygon_at_y(poly, ys[_i]) + ], ys, _i=_i+1 + ); // Function: split_polygons_at_each_z() @@ -1660,14 +1660,14 @@ function split_polygons_at_each_y(polys, ys, _i=0) = // polys = A list of 3D polygons to split. // zs = A list of scalar Z values to split at. function split_polygons_at_each_z(polys, zs, _i=0) = - _i>=len(zs)? polys : - split_polygons_at_each_z( - [ - for (poly = polys) - each _split_polygon_at_z(poly, zs[_i]) - ], zs, _i=_i+1 - ); + _i>=len(zs)? polys : + split_polygons_at_each_z( + [ + for (poly = polys) + each _split_polygon_at_z(poly, zs[_i]) + ], zs, _i=_i+1 + ); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/hingesnaps.scad b/hingesnaps.scad index 5c00f9f..de6f3c6 100644 --- a/hingesnaps.scad +++ b/hingesnaps.scad @@ -29,13 +29,13 @@ // folding_hinge_mask(l=100, thick=3, foldangle=60); module folding_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP) { - hingegap = default(hingegap, layerheight)+2*$slop; - size = [l, hingegap, 2*thick]; - size2 = [l, hingegap+2*thick*tan(foldangle/2)]; - attachable(anchor,spin,orient, size=size, size2=size2) { - up(layerheight*2) prismoid([l,hingegap], [l, hingegap+2*thick/tan(foldangle/2)], h=thick, anchor=BOT); - children(); - } + hingegap = default(hingegap, layerheight)+2*$slop; + size = [l, hingegap, 2*thick]; + size2 = [l, hingegap+2*thick*tan(foldangle/2)]; + attachable(anchor,spin,orient, size=size, size2=size2) { + up(layerheight*2) prismoid([l,hingegap], [l, hingegap+2*thick/tan(foldangle/2)], h=thick, anchor=BOT); + children(); + } } @@ -58,18 +58,18 @@ module folding_hinge_mask(l, thick, layerheight=0.2, foldangle=90, hingegap=unde // snap_lock(thick=3, foldangle=60); module snap_lock(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP) { - hingegap = default(hingegap, layerheight)+2*$slop; - snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2; - size = [snaplen, snapdiam, 2*thick]; - attachable(anchor,spin,orient, size=size) { - back(snap_x) { - cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) { - attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16); - attach(TOP) xcopies(snaplen-snapdiam/4/3) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12); - } - } - children(); - } + hingegap = default(hingegap, layerheight)+2*$slop; + snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2; + size = [snaplen, snapdiam, 2*thick]; + attachable(anchor,spin,orient, size=size) { + back(snap_x) { + cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) { + attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16); + attach(TOP) xcopies(snaplen-snapdiam/4/3) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12); + } + } + children(); + } } @@ -92,21 +92,21 @@ module snap_lock(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hi // snap_socket(thick=3, foldangle=60); module snap_socket(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, hingegap=undef, anchor=CENTER, spin=0, orient=UP) { - hingegap = default(hingegap, layerheight)+2*$slop; - snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2; - size = [snaplen, snapdiam, 2*thick]; - attachable(anchor,spin,orient, size=size) { - fwd(snap_x) { - zrot_copies([0,180], r=snaplen+$slop) { - diff("divot") - cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) { - attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16); - attach(TOP) left((snaplen+snapdiam/4/3)/2) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12, $tags="divot"); - } - } - } - children(); - } + hingegap = default(hingegap, layerheight)+2*$slop; + snap_x = (snapdiam/2) / tan(foldangle/2) + (thick-2*layerheight)/tan(foldangle/2) + hingegap/2; + size = [snaplen, snapdiam, 2*thick]; + attachable(anchor,spin,orient, size=size) { + fwd(snap_x) { + zrot_copies([0,180], r=snaplen+$slop) { + diff("divot") + cube([snaplen, snapdiam, snapdiam/2+thick], anchor=BOT) { + attach(TOP) xcyl(l=snaplen, d=snapdiam, $fn=16); + attach(TOP) left((snaplen+snapdiam/4/3)/2) xscale(0.333) sphere(d=snapdiam*0.8, $fn=12, $tags="divot"); + } + } + } + children(); + } } @@ -157,37 +157,37 @@ module snap_socket(thick, snaplen=5, snapdiam=5, layerheight=0.2, foldangle=90, // } module apply_folding_hinges_and_snaps(thick, foldangle=90, hinges=[], snaps=[], sockets=[], snaplen=5, snapdiam=5, hingegap=undef, layerheight=0.2) { - hingegap = default(hingegap, layerheight)+2*$slop; - difference() { - children(); - for (hinge = hinges) { - translate(hinge[1]) { - folding_hinge_mask( - l=hinge[0], thick=thick, layerheight=layerheight, - foldangle=foldangle, hingegap=hingegap, spin=hinge[2] - ); - } - } - } - for (snap = snaps) { - translate(snap[0]) { - snap_lock( - thick=thick, snaplen=snaplen, snapdiam=snapdiam, - layerheight=layerheight, foldangle=foldangle, - hingegap=hingegap, spin=snap[1] - ); - } - } - for (socket = sockets) { - translate(socket[0]) { - snap_socket( - thick=thick, snaplen=snaplen, snapdiam=snapdiam, - layerheight=layerheight, foldangle=foldangle, - hingegap=hingegap, spin=socket[1] - ); - } - } + hingegap = default(hingegap, layerheight)+2*$slop; + difference() { + children(); + for (hinge = hinges) { + translate(hinge[1]) { + folding_hinge_mask( + l=hinge[0], thick=thick, layerheight=layerheight, + foldangle=foldangle, hingegap=hingegap, spin=hinge[2] + ); + } + } + } + for (snap = snaps) { + translate(snap[0]) { + snap_lock( + thick=thick, snaplen=snaplen, snapdiam=snapdiam, + layerheight=layerheight, foldangle=foldangle, + hingegap=hingegap, spin=snap[1] + ); + } + } + for (socket = sockets) { + translate(socket[0]) { + snap_socket( + thick=thick, snaplen=snaplen, snapdiam=snapdiam, + layerheight=layerheight, foldangle=foldangle, + hingegap=hingegap, spin=socket[1] + ); + } + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/hull.scad b/hull.scad index a9295bb..1855ce0 100644 --- a/hull.scad +++ b/hull.scad @@ -48,29 +48,29 @@ function hull(points) = let(two_d = len(points[0]) == 2) two_d? hull2d_path(poin // 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) { - 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: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_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: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); + } + } + } } @@ -86,62 +86,62 @@ module hull_points(points, fast=false) { // move_copies(pts) color("red") sphere(1); // polygon(points=pts, paths=[path]); function hull2d_path(points) = - (len(points) < 3)? [] : let( - a=0, b=1, - c = first_noncollinear(a, b, points) - ) (c == len(points))? _hull2d_collinear(points) : let( - remaining = [ for (i = [2:1:len(points)-1]) if (i != c) i ], - ccw = triangle_area(points[a], points[b], points[c]) > 0, - polygon = ccw? [a,b,c] : [a,c,b] - ) _hull2d_iterative(points, polygon, remaining); + (len(points) < 3)? [] : let( + a=0, b=1, + c = first_noncollinear(a, b, points) + ) (c == len(points))? _hull2d_collinear(points) : let( + remaining = [ for (i = [2:1:len(points)-1]) if (i != c) i ], + ccw = triangle_area(points[a], points[b], points[c]) > 0, + polygon = ccw? [a,b,c] : [a,c,b] + ) _hull2d_iterative(points, polygon, remaining); // Adds the remaining points one by one to the convex hull function _hull2d_iterative(points, polygon, remaining, _i=0) = - (_i >= len(remaining))? polygon : let ( - // pick a point - i = remaining[_i], - // find the segments that are in conflict with the point (point not inside) - conflicts = _find_conflicting_segments(points, polygon, points[i]) - // no conflicts, skip point and move on - ) (len(conflicts) == 0)? _hull2d_iterative(points, polygon, remaining, _i+1) : let( - // find the first conflicting segment and the first not conflicting - // conflict will be sorted, if not wrapping around, do it the easy way - polygon = _remove_conflicts_and_insert_point(polygon, conflicts, i) - ) _hull2d_iterative(points, polygon, remaining, _i+1); + (_i >= len(remaining))? polygon : let ( + // pick a point + i = remaining[_i], + // find the segments that are in conflict with the point (point not inside) + conflicts = _find_conflicting_segments(points, polygon, points[i]) + // no conflicts, skip point and move on + ) (len(conflicts) == 0)? _hull2d_iterative(points, polygon, remaining, _i+1) : let( + // find the first conflicting segment and the first not conflicting + // conflict will be sorted, if not wrapping around, do it the easy way + polygon = _remove_conflicts_and_insert_point(polygon, conflicts, i) + ) _hull2d_iterative(points, polygon, remaining, _i+1); function _hull2d_collinear(points) = - let( - a = points[0], - n = points[1] - a, - points1d = [ for(p = points) (p-a)*n ], - min_i = min_index(points1d), - max_i = max_index(points1d) - ) [min_i, max_i]; + let( + a = points[0], + n = points[1] - a, + points1d = [ for(p = points) (p-a)*n ], + min_i = min_index(points1d), + max_i = max_index(points1d) + ) [min_i, max_i]; function _find_conflicting_segments(points, polygon, point) = [ - for (i = [0:1:len(polygon)-1]) let( - j = (i+1) % len(polygon), - p1 = points[polygon[i]], - p2 = points[polygon[j]], - area = triangle_area(p1, p2, point) - ) if (area < 0) i + for (i = [0:1:len(polygon)-1]) let( + j = (i+1) % len(polygon), + p1 = points[polygon[i]], + p2 = points[polygon[j]], + area = triangle_area(p1, p2, point) + ) if (area < 0) i ]; // remove the conflicting segments from the polygon function _remove_conflicts_and_insert_point(polygon, conflicts, point) = - (conflicts[0] == 0)? let( - nonconflicting = [ for(i = [0:1:len(polygon)-1]) if (!in_list(i, conflicts)) i ], - new_indices = concat(nonconflicting, (nonconflicting[len(nonconflicting)-1]+1) % len(polygon)), - polygon = concat([ for (i = new_indices) polygon[i] ], point) - ) polygon : let( - before_conflicts = [ for(i = [0:1:min(conflicts)]) polygon[i] ], - after_conflicts = (max(conflicts) >= (len(polygon)-1))? [] : [ for(i = [max(conflicts)+1:1:len(polygon)-1]) polygon[i] ], - polygon = concat(before_conflicts, point, after_conflicts) - ) polygon; + (conflicts[0] == 0)? let( + nonconflicting = [ for(i = [0:1:len(polygon)-1]) if (!in_list(i, conflicts)) i ], + new_indices = concat(nonconflicting, (nonconflicting[len(nonconflicting)-1]+1) % len(polygon)), + polygon = concat([ for (i = new_indices) polygon[i] ], point) + ) polygon : let( + before_conflicts = [ for(i = [0:1:min(conflicts)]) polygon[i] ], + after_conflicts = (max(conflicts) >= (len(polygon)-1))? [] : [ for(i = [max(conflicts)+1:1:len(polygon)-1]) polygon[i] ], + polygon = concat(before_conflicts, point, after_conflicts) + ) polygon; @@ -159,83 +159,83 @@ function _remove_conflicts_and_insert_point(polygon, conflicts, point) = // move_copies(pts) color("red") sphere(1); // %polyhedron(points=pts, faces=faces); function hull3d_faces(points) = - (len(points) < 3)? list_range(len(points)) : let ( - // start with a single non-collinear triangle - a = 0, - b = 1, - c = first_noncollinear(a, b, points) - ) (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 ( - pts2d = [ for (p = points) project_plane(p, points[a], points[b], points[c]) ], - hull2d = hull2d_path(pts2d) - ) hull2d : let( - remaining = [for (i = [3:1:len(points)-1]) if (i != d) i], - // Build an initial tetrahedron. - // Swap b, c if d is in front of triangle t. - ifop = in_front_of_plane(plane, points[d]), - bc = ifop? [c,b] : [b,c], - b = bc[0], - c = bc[1], - triangles = [ - [a,b,c], - [d,b,a], - [c,d,a], - [b,d,c] - ], - // calculate the plane equations - planes = [ for (t = triangles) plane3pt_indexed(points, t[0], t[1], t[2]) ] - ) _hull3d_iterative(points, triangles, planes, remaining); + (len(points) < 3)? list_range(len(points)) : let ( + // start with a single non-collinear triangle + a = 0, + b = 1, + c = first_noncollinear(a, b, points) + ) (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 ( + pts2d = [ for (p = points) project_plane(p, points[a], points[b], points[c]) ], + hull2d = hull2d_path(pts2d) + ) hull2d : let( + remaining = [for (i = [3:1:len(points)-1]) if (i != d) i], + // Build an initial tetrahedron. + // Swap b, c if d is in front of triangle t. + ifop = in_front_of_plane(plane, points[d]), + bc = ifop? [c,b] : [b,c], + b = bc[0], + c = bc[1], + triangles = [ + [a,b,c], + [d,b,a], + [c,d,a], + [b,d,c] + ], + // calculate the plane equations + planes = [ for (t = triangles) plane3pt_indexed(points, t[0], t[1], t[2]) ] + ) _hull3d_iterative(points, triangles, planes, remaining); // Adds the remaining points one by one to the convex hull function _hull3d_iterative(points, triangles, planes, remaining, _i=0) = - _i >= len(remaining) ? triangles : - let ( - // pick a point - i = remaining[_i], - // find the triangles that are in conflict with the point (point not inside) - conflicts = _find_conflicts(points[i], planes), - // for all triangles that are in conflict, collect their halfedges - halfedges = [ - for(c = conflicts, i = [0:2]) let( - j = (i+1)%3 - ) [triangles[c][i], triangles[c][j]] - ], - // find the outer perimeter of the set of conflicting triangles - horizon = _remove_internal_edges(halfedges), - // generate a new triangle for each horizon halfedge together with the picked point i - new_triangles = [ for (h = horizon) concat(h,i) ], - // calculate the corresponding plane equations - new_planes = [ for (t = new_triangles) plane3pt_indexed(points, t[0], t[1], t[2]) ] - ) _hull3d_iterative( - points, - // remove the conflicting triangles and add the new ones - concat(list_remove(triangles, conflicts), new_triangles), - concat(list_remove(planes, conflicts), new_planes), - remaining, - _i+1 - ); + _i >= len(remaining) ? triangles : + let ( + // pick a point + i = remaining[_i], + // find the triangles that are in conflict with the point (point not inside) + conflicts = _find_conflicts(points[i], planes), + // for all triangles that are in conflict, collect their halfedges + halfedges = [ + for(c = conflicts, i = [0:2]) let( + j = (i+1)%3 + ) [triangles[c][i], triangles[c][j]] + ], + // find the outer perimeter of the set of conflicting triangles + horizon = _remove_internal_edges(halfedges), + // generate a new triangle for each horizon halfedge together with the picked point i + new_triangles = [ for (h = horizon) concat(h,i) ], + // calculate the corresponding plane equations + new_planes = [ for (t = new_triangles) plane3pt_indexed(points, t[0], t[1], t[2]) ] + ) _hull3d_iterative( + points, + // remove the conflicting triangles and add the new ones + concat(list_remove(triangles, conflicts), new_triangles), + concat(list_remove(planes, conflicts), new_planes), + remaining, + _i+1 + ); function _remove_internal_edges(halfedges) = [ - for (h = halfedges) - if (!in_list(reverse(h), halfedges)) - h + for (h = halfedges) + if (!in_list(reverse(h), halfedges)) + h ]; function _find_conflicts(point, planes) = [ - for (i = [0:1:len(planes)-1]) - if (in_front_of_plane(planes[i], point)) - i + for (i = [0:1:len(planes)-1]) + if (in_front_of_plane(planes[i], point)) + i ]; function _find_first_noncoplanar(plane, points, i) = - (i >= len(points) || !coplanar(plane, points[i]))? i : - _find_first_noncoplanar(plane, points, i+1); + (i >= len(points) || !coplanar(plane, points[i]))? i : + _find_first_noncoplanar(plane, points, i+1); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/involute_gears.scad b/involute_gears.scad index 8b080d9..f614868 100644 --- a/involute_gears.scad +++ b/involute_gears.scad @@ -78,7 +78,7 @@ function adendum(pitch=5) = module_value(pitch); // pitch = The circular pitch, or distance between teeth around the pitch circle, in mm. // clearance = If given, sets the clearance between meshing teeth. function dedendum(pitch=5, clearance=undef) = - (clearance==undef)? (1.25 * module_value(pitch)) : (module_value(pitch) + clearance); + (clearance==undef)? (1.25 * module_value(pitch)) : (module_value(pitch) + clearance); // Function: pitch_radius() @@ -87,7 +87,7 @@ function dedendum(pitch=5, clearance=undef) = // pitch = The circular pitch, or distance between teeth around the pitch circle, in mm. // teeth = The number of teeth on the gear. function pitch_radius(pitch=5, teeth=11) = - pitch * teeth / PI / 2; + pitch * teeth / PI / 2; // Function: outer_radius() @@ -99,8 +99,8 @@ function pitch_radius(pitch=5, teeth=11) = // clearance = If given, sets the clearance between meshing teeth. // interior = If true, calculate for an interior gear. function outer_radius(pitch=5, teeth=11, clearance=undef, interior=false) = - pitch_radius(pitch, teeth) + - (interior? dedendum(pitch, clearance) : adendum(pitch)); + pitch_radius(pitch, teeth) + + (interior? dedendum(pitch, clearance) : adendum(pitch)); // Function: root_radius() @@ -112,8 +112,8 @@ function outer_radius(pitch=5, teeth=11, clearance=undef, interior=false) = // clearance = If given, sets the clearance between meshing teeth. // interior = If true, calculate for an interior gear. function root_radius(pitch=5, teeth=11, clearance=undef, interior=false) = - pitch_radius(pitch, teeth) - - (interior? adendum(pitch) : dedendum(pitch, clearance)); + pitch_radius(pitch, teeth) - + (interior? adendum(pitch) : dedendum(pitch, clearance)); // Function: base_radius() @@ -123,7 +123,7 @@ function root_radius(pitch=5, teeth=11, clearance=undef, interior=false) = // teeth = The number of teeth on the gear. // PA = Pressure angle in degrees. Controls how straight or bulged the tooth sides are. function base_radius(pitch=5, teeth=11, PA=28) = - pitch_radius(pitch, teeth) * cos(PA); + pitch_radius(pitch, teeth) * cos(PA); // Function bevel_pitch_angle() @@ -137,7 +137,7 @@ function base_radius(pitch=5, teeth=11, PA=28) = // mate_teeth = Number of teeth that the matching gear has. // drive_angle = Angle between the drive shafts of each gear. Usually 90º. function bevel_pitch_angle(teeth, mate_teeth, drive_angle=90) = - atan(sin(drive_angle)/((mate_teeth/teeth)+cos(drive_angle))); + atan(sin(drive_angle)/((mate_teeth/teeth)+cos(drive_angle))); function _gear_polar(r,t) = r*[sin(t),cos(t)]; @@ -166,63 +166,63 @@ function _gear_q7(f,r,b,r2,t,s) = _gear_q6(b,s,t,(1-f)*max(b,r)+f*r2); // // Example(2D): // gear_tooth_profile(pitch=5, teeth=20, PA=20, valleys=true); function gear_tooth_profile( - pitch = 3, - teeth = 11, - PA = 28, - backlash = 0.0, - clearance = undef, - interior = false, - valleys = true + pitch = 3, + teeth = 11, + PA = 28, + backlash = 0.0, + clearance = undef, + interior = false, + valleys = true ) = let( - p = pitch_radius(pitch, teeth), - c = outer_radius(pitch, teeth, clearance, interior), - r = root_radius(pitch, teeth, clearance, interior), - b = base_radius(pitch, teeth, PA), - t = pitch/2-backlash/2, //tooth thickness at pitch circle - k = -_gear_iang(b, p) - t/2/p/PI*180, //angle to where involute meets base circle on each side of tooth - kk = r0? [[0,0]] : [] - ) + pr = pitch_radius(pitch=pitch, teeth=teeth), + pts = concat( + [for (tooth = [0:1:teeth-hide-1]) + each rot(tooth*360/teeth, + planar=true, + p=gear_tooth_profile( + pitch = pitch, + teeth = teeth, + PA = PA, + clearance = clearance, + backlash = backlash, + interior = interior, + valleys = false + ) + ) + ], + hide>0? [[0,0]] : [] + ) ) reorient(anchor,spin, two_d=true, r=pr, p=pts); module gear2d( - pitch = 3, - teeth = 11, - hide = 0, - PA = 28, - clearance = undef, - backlash = 0.0, - interior = false, - anchor = CENTER, - spin = 0 + pitch = 3, + teeth = 11, + hide = 0, + PA = 28, + clearance = undef, + backlash = 0.0, + interior = false, + anchor = CENTER, + spin = 0 ) { - path = gear2d( - pitch = pitch, - teeth = teeth, - hide = hide, - PA = PA, - clearance = clearance, - backlash = backlash, - interior = interior - ); - pr = pitch_radius(pitch=pitch, teeth=teeth); - attachable(anchor,spin, two_d=true, r=pr) { - polygon(path); - children(); - } + path = gear2d( + pitch = pitch, + teeth = teeth, + hide = hide, + PA = PA, + clearance = clearance, + backlash = backlash, + interior = interior + ); + pr = pitch_radius(pitch=pitch, teeth=teeth); + attachable(anchor,spin, two_d=true, r=pr) { + polygon(path); + children(); + } } @@ -362,44 +362,44 @@ module gear2d( // Example: Beveled Gear // gear(pitch=5, teeth=20, thickness=10, shaft_diam=5, helical=-30, slices=12, $fa=1, $fs=1); module gear( - pitch = 3, - teeth = 11, - PA = 28, - thickness = 6, - hide = 0, - shaft_diam = 3, - clearance = undef, - backlash = 0.0, - helical = 0, - slices = 2, - interior = false, - anchor = CENTER, - spin = 0, - orient = UP + pitch = 3, + teeth = 11, + PA = 28, + thickness = 6, + hide = 0, + shaft_diam = 3, + clearance = undef, + backlash = 0.0, + helical = 0, + slices = 2, + interior = false, + anchor = CENTER, + spin = 0, + orient = UP ) { - p = pitch_radius(pitch, teeth); - c = outer_radius(pitch, teeth, clearance, interior); - r = root_radius(pitch, teeth, clearance, interior); - twist = atan2(thickness*tan(helical),p); - attachable(anchor,spin,orient, r=p, l=thickness) { - difference() { - linear_extrude(height=thickness, center=true, convexity=10, twist=twist) { - gear2d( - pitch = pitch, - teeth = teeth, - PA = PA, - hide = hide, - clearance = clearance, - backlash = backlash, - interior = interior - ); - } - if (shaft_diam > 0) { - cylinder(h=2*thickness+1, r=shaft_diam/2, center=true, $fn=max(12,segs(shaft_diam/2))); - } - } - children(); - } + p = pitch_radius(pitch, teeth); + c = outer_radius(pitch, teeth, clearance, interior); + r = root_radius(pitch, teeth, clearance, interior); + twist = atan2(thickness*tan(helical),p); + attachable(anchor,spin,orient, r=p, l=thickness) { + difference() { + linear_extrude(height=thickness, center=true, convexity=10, twist=twist) { + gear2d( + pitch = pitch, + teeth = teeth, + PA = PA, + hide = hide, + clearance = clearance, + backlash = backlash, + interior = interior + ); + } + if (shaft_diam > 0) { + cylinder(h=2*thickness+1, r=shaft_diam/2, center=true, $fn=max(12,segs(shaft_diam/2))); + } + } + children(); + } } @@ -457,131 +457,131 @@ module gear( // Example: Beveled Gear // bevel_gear(pitch=5, teeth=36, face_width=10, shaft_diam=5, spiral_rad=-20, spiral_ang=35, bevelang=45, slices=12, $fa=1, $fs=1); module bevel_gear( - pitch = 3, - teeth = 11, - PA = 20, - face_width = 6, - bevelang = 45, - hide = 0, - shaft_diam = 3, - clearance = undef, - backlash = 0.0, - spiral_rad = 0, - spiral_ang = 0, - slices = 2, - interior = false, - anchor = CENTER, - spin = 0, - orient = UP + pitch = 3, + teeth = 11, + PA = 20, + face_width = 6, + bevelang = 45, + hide = 0, + shaft_diam = 3, + clearance = undef, + backlash = 0.0, + spiral_rad = 0, + spiral_ang = 0, + slices = 2, + interior = false, + anchor = CENTER, + spin = 0, + orient = UP ) { - thickness = face_width * cos(bevelang); - slices = spiral_rad==0? 1 : slices; - spiral_rad = spiral_rad==0? 10000 : spiral_rad; - p1 = pitch_radius(pitch, teeth); - r1 = root_radius(pitch, teeth, clearance, interior); - c1 = outer_radius(pitch, teeth, clearance, interior); - dx = thickness * tan(bevelang); - dy = (p1-r1) * sin(bevelang); - scl = (p1-dx)/p1; - p2 = pitch_radius(pitch*scl, teeth); - r2 = root_radius(pitch*scl, teeth, clearance, interior); - c2 = outer_radius(pitch*scl, teeth, clearance, interior); - slice_u = 1/slices; - Rm = (p1+p2)/2; - H = spiral_rad * cos(spiral_ang); - V = Rm - abs(spiral_rad) * sin(spiral_ang); - spiral_cp = [H,V,0]; - S = norm(spiral_cp); - theta_r = acos((S*S+spiral_rad*spiral_rad-p1*p1)/(2*S*spiral_rad)) - acos((S*S+spiral_rad*spiral_rad-p2*p2)/(2*S*spiral_rad)); - theta_ro = acos((S*S+spiral_rad*spiral_rad-p1*p1)/(2*S*spiral_rad)) - acos((S*S+spiral_rad*spiral_rad-Rm*Rm)/(2*S*spiral_rad)); - theta_ri = theta_r - theta_ro; - extent_u = 2*(p2-r2)*tan(bevelang) / thickness; - slice_us = concat( - [for (u = [0:slice_u:1+extent_u]) u] - ); - lsus = len(slice_us); - vertices = concat( - [ - for (u=slice_us, tooth=[0:1:teeth-1]) let( - p = lerp(p1,p2,u), - r = lerp(r1,r2,u), - theta = lerp(-theta_ro, theta_ri, u), - profile = gear_tooth_profile( - pitch = pitch*(p/p1), - teeth = teeth, - PA = PA, - clearance = clearance, - backlash = backlash, - interior = interior, - valleys = false - ), - pp = rot(theta, cp=spiral_cp, p=[0,Rm,0]), - ang = atan2(pp.y,pp.x)-90, - pts = apply_list( - path3d(profile), [ - move([0,-p,0]), - rot([0,ang,0]), - rot([bevelang,0,0]), - move(pp), - rot(tooth*360/teeth), - move([0,0,thickness*u]) - ] - ) - ) each pts - ], [ - [0,0,-dy], [0,0,thickness] - ] - ); - lcnt = (len(vertices)-2)/lsus/teeth; - function _gv(layer,tooth,i) = ((layer*teeth)+(tooth%teeth))*lcnt+(i%lcnt); - function _lv(layer,i) = layer*teeth*lcnt+(i%(teeth*lcnt)); - faces = concat( - [ - for (sl=[0:1:lsus-2], i=[0:1:lcnt*teeth-1]) each [ - [_lv(sl,i), _lv(sl+1,i), _lv(sl,i+1)], - [_lv(sl+1,i), _lv(sl+1,i+1), _lv(sl,i+1)] - ] - ], [ - for (tooth=[0:1:teeth-1], i=[0:1:lcnt/2-1]) each [ - [_gv(0,tooth,i), _gv(0,tooth,i+1), _gv(0,tooth,lcnt-1-(i+1))], - [_gv(0,tooth,i), _gv(0,tooth,lcnt-1-(i+1)), _gv(0,tooth,lcnt-1-i)], - [_gv(lsus-1,tooth,i), _gv(lsus-1,tooth,lcnt-1-(i+1)), _gv(lsus-1,tooth,i+1)], - [_gv(lsus-1,tooth,i), _gv(lsus-1,tooth,lcnt-1-i), _gv(lsus-1,tooth,lcnt-1-(i+1))], - ] - ], [ - for (tooth=[0:1:teeth-1]) each [ - [len(vertices)-2, _gv(0,tooth,0), _gv(0,tooth,lcnt-1)], - [len(vertices)-2, _gv(0,tooth,lcnt-1), _gv(0,tooth+1,0)], - [len(vertices)-1, _gv(lsus-1,tooth,lcnt-1), _gv(lsus-1,tooth,0)], - [len(vertices)-1, _gv(lsus-1,tooth+1,0), _gv(lsus-1,tooth,lcnt-1)], - ] - ] - ); - attachable(anchor,spin,orient, r1=p1, r2=p2, l=thickness) { - union() { - difference() { - down(thickness/2) { - polyhedron(points=vertices, faces=faces, convexity=floor(teeth/2)); - } - if (shaft_diam > 0) { - cylinder(h=2*thickness+1, r=shaft_diam/2, center=true, $fn=max(12,segs(shaft_diam/2))); - } - if (bevelang != 0) { - h = (c1-r1)/tan(45); - down(thickness/2+dy) { - difference() { - cube([2*c1/cos(45),2*c1/cos(45),2*h], center=true); - cylinder(h=h, r1=r1-0.5, r2=c1-0.5, center=false, $fn=teeth*4); - } - } - up(thickness/2-0.01) { - cylinder(h=(c2-r2)/tan(45)*5, r1=r2-0.5, r2=lerp(r2-0.5,c2-0.5,5), center=false, $fn=teeth*4); - } - } - } - } - children(); - } + thickness = face_width * cos(bevelang); + slices = spiral_rad==0? 1 : slices; + spiral_rad = spiral_rad==0? 10000 : spiral_rad; + p1 = pitch_radius(pitch, teeth); + r1 = root_radius(pitch, teeth, clearance, interior); + c1 = outer_radius(pitch, teeth, clearance, interior); + dx = thickness * tan(bevelang); + dy = (p1-r1) * sin(bevelang); + scl = (p1-dx)/p1; + p2 = pitch_radius(pitch*scl, teeth); + r2 = root_radius(pitch*scl, teeth, clearance, interior); + c2 = outer_radius(pitch*scl, teeth, clearance, interior); + slice_u = 1/slices; + Rm = (p1+p2)/2; + H = spiral_rad * cos(spiral_ang); + V = Rm - abs(spiral_rad) * sin(spiral_ang); + spiral_cp = [H,V,0]; + S = norm(spiral_cp); + theta_r = acos((S*S+spiral_rad*spiral_rad-p1*p1)/(2*S*spiral_rad)) - acos((S*S+spiral_rad*spiral_rad-p2*p2)/(2*S*spiral_rad)); + theta_ro = acos((S*S+spiral_rad*spiral_rad-p1*p1)/(2*S*spiral_rad)) - acos((S*S+spiral_rad*spiral_rad-Rm*Rm)/(2*S*spiral_rad)); + theta_ri = theta_r - theta_ro; + extent_u = 2*(p2-r2)*tan(bevelang) / thickness; + slice_us = concat( + [for (u = [0:slice_u:1+extent_u]) u] + ); + lsus = len(slice_us); + vertices = concat( + [ + for (u=slice_us, tooth=[0:1:teeth-1]) let( + p = lerp(p1,p2,u), + r = lerp(r1,r2,u), + theta = lerp(-theta_ro, theta_ri, u), + profile = gear_tooth_profile( + pitch = pitch*(p/p1), + teeth = teeth, + PA = PA, + clearance = clearance, + backlash = backlash, + interior = interior, + valleys = false + ), + pp = rot(theta, cp=spiral_cp, p=[0,Rm,0]), + ang = atan2(pp.y,pp.x)-90, + pts = apply_list( + path3d(profile), [ + move([0,-p,0]), + rot([0,ang,0]), + rot([bevelang,0,0]), + move(pp), + rot(tooth*360/teeth), + move([0,0,thickness*u]) + ] + ) + ) each pts + ], [ + [0,0,-dy], [0,0,thickness] + ] + ); + lcnt = (len(vertices)-2)/lsus/teeth; + function _gv(layer,tooth,i) = ((layer*teeth)+(tooth%teeth))*lcnt+(i%lcnt); + function _lv(layer,i) = layer*teeth*lcnt+(i%(teeth*lcnt)); + faces = concat( + [ + for (sl=[0:1:lsus-2], i=[0:1:lcnt*teeth-1]) each [ + [_lv(sl,i), _lv(sl+1,i), _lv(sl,i+1)], + [_lv(sl+1,i), _lv(sl+1,i+1), _lv(sl,i+1)] + ] + ], [ + for (tooth=[0:1:teeth-1], i=[0:1:lcnt/2-1]) each [ + [_gv(0,tooth,i), _gv(0,tooth,i+1), _gv(0,tooth,lcnt-1-(i+1))], + [_gv(0,tooth,i), _gv(0,tooth,lcnt-1-(i+1)), _gv(0,tooth,lcnt-1-i)], + [_gv(lsus-1,tooth,i), _gv(lsus-1,tooth,lcnt-1-(i+1)), _gv(lsus-1,tooth,i+1)], + [_gv(lsus-1,tooth,i), _gv(lsus-1,tooth,lcnt-1-i), _gv(lsus-1,tooth,lcnt-1-(i+1))], + ] + ], [ + for (tooth=[0:1:teeth-1]) each [ + [len(vertices)-2, _gv(0,tooth,0), _gv(0,tooth,lcnt-1)], + [len(vertices)-2, _gv(0,tooth,lcnt-1), _gv(0,tooth+1,0)], + [len(vertices)-1, _gv(lsus-1,tooth,lcnt-1), _gv(lsus-1,tooth,0)], + [len(vertices)-1, _gv(lsus-1,tooth+1,0), _gv(lsus-1,tooth,lcnt-1)], + ] + ] + ); + attachable(anchor,spin,orient, r1=p1, r2=p2, l=thickness) { + union() { + difference() { + down(thickness/2) { + polyhedron(points=vertices, faces=faces, convexity=floor(teeth/2)); + } + if (shaft_diam > 0) { + cylinder(h=2*thickness+1, r=shaft_diam/2, center=true, $fn=max(12,segs(shaft_diam/2))); + } + if (bevelang != 0) { + h = (c1-r1)/tan(45); + down(thickness/2+dy) { + difference() { + cube([2*c1/cos(45),2*c1/cos(45),2*h], center=true); + cylinder(h=h, r1=r1-0.5, r2=c1-0.5, center=false, $fn=teeth*4); + } + } + up(thickness/2-0.01) { + cylinder(h=(c2-r2)/tan(45)*5, r1=r2-0.5, r2=lerp(r2-0.5,c2-0.5,5), center=false, $fn=teeth*4); + } + } + } + } + children(); + } } @@ -614,57 +614,57 @@ module bevel_gear( // Example: // rack(pitch=5, teeth=10, thickness=5, height=5, PA=20); module rack( - pitch = 5, - teeth = 20, - thickness = 5, - height = 10, - PA = 28, - backlash = 0.0, - clearance = undef, - anchor = CENTER, - spin = 0, - orient = UP + pitch = 5, + teeth = 20, + thickness = 5, + height = 10, + PA = 28, + backlash = 0.0, + clearance = undef, + anchor = CENTER, + spin = 0, + orient = UP ) { - a = adendum(pitch); - d = dedendum(pitch, clearance); - xa = a * sin(PA); - xd = d * sin(PA); - l = teeth * pitch; - anchors = [ - anchorpt("adendum", [0,a,0], BACK), - anchorpt("adendum-left", [-l/2,a,0], LEFT), - anchorpt("adendum-right", [l/2,a,0], RIGHT), - anchorpt("adendum-top", [0,a,thickness/2], UP), - anchorpt("adendum-bottom", [0,a,-thickness/2], DOWN), - anchorpt("dedendum", [0,-d,0], BACK), - anchorpt("dedendum-left", [-l/2,-d,0], LEFT), - anchorpt("dedendum-right", [l/2,-d,0], RIGHT), - anchorpt("dedendum-top", [0,-d,thickness/2], UP), - anchorpt("dedendum-bottom", [0,-d,-thickness/2], DOWN), - ]; - attachable(anchor,spin,orient, size=[l, 2*abs(a-height), thickness], anchors=anchors) { - left((teeth-1)*pitch/2) { - linear_extrude(height = thickness, center = true, convexity = 10) { - for (i = [0:1:teeth-1] ) { - translate([i*pitch,0,0]) { - polygon( - points=[ - [-1/2 * pitch - 0.01, a-height], - [-1/2 * pitch, -d], - [-1/4 * pitch + backlash - xd, -d], - [-1/4 * pitch + backlash + xa, a], - [ 1/4 * pitch - backlash - xa, a], - [ 1/4 * pitch - backlash + xd, -d], - [ 1/2 * pitch, -d], - [ 1/2 * pitch + 0.01, a-height], - ] - ); - } - } - } - } - children(); - } + a = adendum(pitch); + d = dedendum(pitch, clearance); + xa = a * sin(PA); + xd = d * sin(PA); + l = teeth * pitch; + anchors = [ + anchorpt("adendum", [0,a,0], BACK), + anchorpt("adendum-left", [-l/2,a,0], LEFT), + anchorpt("adendum-right", [l/2,a,0], RIGHT), + anchorpt("adendum-top", [0,a,thickness/2], UP), + anchorpt("adendum-bottom", [0,a,-thickness/2], DOWN), + anchorpt("dedendum", [0,-d,0], BACK), + anchorpt("dedendum-left", [-l/2,-d,0], LEFT), + anchorpt("dedendum-right", [l/2,-d,0], RIGHT), + anchorpt("dedendum-top", [0,-d,thickness/2], UP), + anchorpt("dedendum-bottom", [0,-d,-thickness/2], DOWN), + ]; + attachable(anchor,spin,orient, size=[l, 2*abs(a-height), thickness], anchors=anchors) { + left((teeth-1)*pitch/2) { + linear_extrude(height = thickness, center = true, convexity = 10) { + for (i = [0:1:teeth-1] ) { + translate([i*pitch,0,0]) { + polygon( + points=[ + [-1/2 * pitch - 0.01, a-height], + [-1/2 * pitch, -d], + [-1/4 * pitch + backlash - xd, -d], + [-1/4 * pitch + backlash + xa, a], + [ 1/4 * pitch - backlash - xa, a], + [ 1/4 * pitch - backlash + xd, -d], + [ 1/2 * pitch, -d], + [ 1/2 * pitch + 0.01, a-height], + ] + ); + } + } + } + } + children(); + } } @@ -698,5 +698,5 @@ translate([(-floor(n5/2)-floor(n1/2)+$t+n1/2-1/2)*9, -d1+0.0, 0]) rotate([0,0,0] */ -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/joiners.scad b/joiners.scad index 2613a77..a8afb4d 100644 --- a/joiners.scad +++ b/joiners.scad @@ -34,29 +34,29 @@ include // half_joiner_clear(spin=-90); module half_joiner_clear(h=20, w=10, a=30, clearance=0, overlap=0.01, anchor=CENTER, spin=0, orient=UP) { - dmnd_height = h*1.0; - dmnd_width = dmnd_height*tan(a); - guide_size = w/3; - guide_width = 2*(dmnd_height/2-guide_size)*tan(a); + dmnd_height = h*1.0; + dmnd_width = dmnd_height*tan(a); + guide_size = w/3; + guide_width = 2*(dmnd_height/2-guide_size)*tan(a); - attachable(anchor,spin,orient, size=[w, guide_width, h]) { - union() { - ycopies(overlap, n=overlap>0? 2 : 1) { - difference() { - // Diamonds. - scale([w+clearance, dmnd_width/2, dmnd_height/2]) { - xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true); - } - // Blunt point of tab. - ycopies(guide_width+4) { - cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true); - } - } - } - if (overlap>0) cube([w+clearance, overlap+0.001, h], center=true); - } - children(); - } + attachable(anchor,spin,orient, size=[w, guide_width, h]) { + union() { + ycopies(overlap, n=overlap>0? 2 : 1) { + difference() { + // Diamonds. + scale([w+clearance, dmnd_width/2, dmnd_height/2]) { + xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true); + } + // Blunt point of tab. + ycopies(guide_width+4) { + cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true); + } + } + } + if (overlap>0) cube([w+clearance, overlap+0.001, h], center=true); + } + children(); + } } @@ -81,60 +81,60 @@ module half_joiner_clear(h=20, w=10, a=30, clearance=0, overlap=0.01, anchor=CEN // half_joiner(screwsize=3, spin=-90); module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, anchor=CENTER, spin=0, orient=UP) { - dmnd_height = h*1.0; - dmnd_width = dmnd_height*tan(a); - guide_size = w/3; - guide_width = 2*(dmnd_height/2-guide_size)*tan(a); + dmnd_height = h*1.0; + dmnd_width = dmnd_height*tan(a); + guide_size = w/3; + guide_width = 2*(dmnd_height/2-guide_size)*tan(a); - render(convexity=12) - attachable(anchor,spin,orient, size=[w, 2*l, h]) { - difference() { - union() { - // Make base. - difference() { - // Solid backing base. - fwd(l/2) cube(size=[w, l, h], center=true); + render(convexity=12) + attachable(anchor,spin,orient, size=[w, 2*l, h]) { + difference() { + union() { + // Make base. + difference() { + // Solid backing base. + fwd(l/2) cube(size=[w, l, h], center=true); - // Clear diamond for tab - xcopies(2*w*2/3) { - half_joiner_clear(h=h+0.01, w=w, clearance=$slop*2, a=a); - } - } + // Clear diamond for tab + xcopies(2*w*2/3) { + half_joiner_clear(h=h+0.01, w=w, clearance=$slop*2, a=a); + } + } - difference() { - // Make tab - scale([w/3-$slop*2, dmnd_width/2, dmnd_height/2]) xrot(45) - cube(size=[1,sqrt(2),sqrt(2)], center=true); + difference() { + // Make tab + scale([w/3-$slop*2, dmnd_width/2, dmnd_height/2]) xrot(45) + cube(size=[1,sqrt(2),sqrt(2)], center=true); - // Blunt point of tab. - back(guide_width/2+2) - cube(size=[w*0.99,4,guide_size*2], center=true); - } + // Blunt point of tab. + back(guide_width/2+2) + cube(size=[w*0.99,4,guide_size*2], center=true); + } - // Guide ridges. - if (guides == true) { - xcopies(w/3-$slop*2) { - // Guide ridge. - fwd(0.05/2) { - scale([0.75, 1, 2]) yrot(45) - cube(size=[guide_size/sqrt(2), guide_width+0.05, guide_size/sqrt(2)], center=true); - } + // Guide ridges. + if (guides == true) { + xcopies(w/3-$slop*2) { + // Guide ridge. + fwd(0.05/2) { + scale([0.75, 1, 2]) yrot(45) + cube(size=[guide_size/sqrt(2), guide_width+0.05, guide_size/sqrt(2)], center=true); + } - // Snap ridge. - scale([0.25, 0.5, 1]) zrot(45) - cube(size=[guide_size/sqrt(2), guide_size/sqrt(2), dmnd_width], center=true); - } - } - } + // Snap ridge. + scale([0.25, 0.5, 1]) zrot(45) + cube(size=[guide_size/sqrt(2), guide_size/sqrt(2), dmnd_width], center=true); + } + } + } - // Make screwholes, if needed. - if (screwsize != undef) { - yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12); - } - } - children(); - } + // Make screwholes, if needed. + if (screwsize != undef) { + yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12); + } + } + children(); + } } //half_joiner(screwsize=3); @@ -159,29 +159,29 @@ module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, anchor= // half_joiner2(screwsize=3, spin=-90); module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, anchor=CENTER, spin=0, orient=UP) { - dmnd_height = h*1.0; - dmnd_width = dmnd_height*tan(a); - guide_size = w/3; - guide_width = 2*(dmnd_height/2-guide_size)*tan(a); + dmnd_height = h*1.0; + dmnd_width = dmnd_height*tan(a); + guide_size = w/3; + guide_width = 2*(dmnd_height/2-guide_size)*tan(a); - render(convexity=12) - attachable(anchor,spin,orient, size=[w, 2*l, h]) { - difference() { - union () { - fwd(l/2) cube(size=[w, l, h], center=true); - cube([w, guide_width, h], center=true); - } + render(convexity=12) + attachable(anchor,spin,orient, size=[w, 2*l, h]) { + difference() { + union () { + fwd(l/2) cube(size=[w, l, h], center=true); + cube([w, guide_width, h], center=true); + } - // Subtract mated half_joiner. - zrot(180) half_joiner(h=h+0.01, w=w+0.01, l=guide_width+0.01, a=a, screwsize=undef, guides=guides, $slop=0.0); + // Subtract mated half_joiner. + zrot(180) half_joiner(h=h+0.01, w=w+0.01, l=guide_width+0.01, a=a, screwsize=undef, guides=guides, $slop=0.0); - // Make screwholes, if needed. - if (screwsize != undef) { - xcyl(r=screwsize*1.1/2, l=w+1, $fn=12); - } - } - children(); - } + // Make screwholes, if needed. + if (screwsize != undef) { + xcyl(r=screwsize*1.1/2, l=w+1, $fn=12); + } + } + children(); + } } @@ -207,18 +207,18 @@ module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, anchor // joiner_clear(spin=-90); module joiner_clear(h=40, w=10, a=30, clearance=0, overlap=0.01, anchor=CENTER, spin=0, orient=UP) { - dmnd_height = h*0.5; - dmnd_width = dmnd_height*tan(a); - guide_size = w/3; - guide_width = 2*(dmnd_height/2-guide_size)*tan(a); + dmnd_height = h*0.5; + dmnd_width = dmnd_height*tan(a); + guide_size = w/3; + guide_width = 2*(dmnd_height/2-guide_size)*tan(a); - attachable(anchor,spin,orient, size=[w, guide_width, h]) { - union() { - up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=clearance); - down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=-0.01); - } - children(); - } + attachable(anchor,spin,orient, size=[w, guide_width, h]) { + union() { + up(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=clearance); + down(h/4) half_joiner_clear(h=h/2.0-0.01, w=w, a=a, overlap=overlap, clearance=-0.01); + } + children(); + } } @@ -244,13 +244,13 @@ module joiner_clear(h=40, w=10, a=30, clearance=0, overlap=0.01, anchor=CENTER, // joiner(w=10, l=10, h=40, spin=-90) cuboid([10, 10*2, 40], anchor=RIGHT); module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, anchor=CENTER, spin=0, orient=UP) { - attachable(anchor,spin,orient, size=[w, 2*l, h]) { - union() { - up(h/4) half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides); - down(h/4) half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides); - } - children(); - } + attachable(anchor,spin,orient, size=[w, 2*l, h]) { + union() { + up(h/4) half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides); + down(h/4) half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides); + } + children(); + } } @@ -279,17 +279,17 @@ module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, anchor=CENTE // joiner_pair_clear(spacing=50, n=3); module joiner_pair_clear(spacing=100, h=40, w=10, a=30, n=2, clearance=0, overlap=0.01, anchor=CENTER, spin=0, orient=UP) { - dmnd_height = h*0.5; - dmnd_width = dmnd_height*tan(a); - guide_size = w/3; - guide_width = 2*(dmnd_height/2-guide_size)*tan(a); + dmnd_height = h*0.5; + dmnd_width = dmnd_height*tan(a); + guide_size = w/3; + guide_width = 2*(dmnd_height/2-guide_size)*tan(a); - attachable(anchor,spin,orient, size=[spacing+w, guide_width, h]) { - xcopies(spacing, n=n) { - joiner_clear(h=h, w=w, a=a, clearance=clearance, overlap=overlap); - } - children(); - } + attachable(anchor,spin,orient, size=[spacing+w, guide_width, h]) { + xcopies(spacing, n=n) { + joiner_clear(h=h, w=w, a=a, clearance=clearance, overlap=overlap); + } + children(); + } } @@ -321,18 +321,18 @@ module joiner_pair_clear(spacing=100, h=40, w=10, a=30, n=2, clearance=0, overla // joiner_pair(spacing=50, l=10, n=3, alternate="alt", spin=-90); module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, n=2, alternate=true, screwsize=undef, guides=true, anchor=CENTER, spin=0, orient=UP) { - attachable(anchor,spin,orient, size=[spacing+w, 2*l, h]) { - left((n-1)*spacing/2) { - for (i=[0:1:n-1]) { - right(i*spacing) { - yrot(180 + (alternate? (i*180+(alternate=="alt"?180:0))%360 : 0)) { - joiner(h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides); - } - } - } - } - children(); - } + attachable(anchor,spin,orient, size=[spacing+w, 2*l, h]) { + left((n-1)*spacing/2) { + for (i=[0:1:n-1]) { + right(i*spacing) { + yrot(180 + (alternate? (i*180+(alternate=="alt"?180:0))%360 : 0)) { + joiner(h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides); + } + } + } + } + children(); + } } @@ -362,16 +362,16 @@ module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, n=2, alternate=true, scr // joiner_quad_clear(spacing1=50, spacing2=50, n=3); module joiner_quad_clear(xspacing=undef, yspacing=undef, spacing1=undef, spacing2=undef, n=2, h=40, w=10, a=30, clearance=0, overlap=0.01, anchor=CENTER, spin=0, orient=UP) { - spacing1 = first_defined([spacing1, xspacing, 100]); - spacing2 = first_defined([spacing2, yspacing, 50]); - attachable(anchor,spin,orient, size=[w+spacing1, spacing2, h]) { - zrot_copies(n=2) { - back(spacing2/2) { - joiner_pair_clear(spacing=spacing1, n=n, h=h, w=w, a=a, clearance=clearance, overlap=overlap); - } - } - children(); - } + spacing1 = first_defined([spacing1, xspacing, 100]); + spacing2 = first_defined([spacing2, yspacing, 50]); + attachable(anchor,spin,orient, size=[w+spacing1, spacing2, h]) { + zrot_copies(n=2) { + back(spacing2/2) { + joiner_pair_clear(spacing=spacing1, n=n, h=h, w=w, a=a, clearance=clearance, overlap=overlap); + } + } + children(); + } } @@ -403,16 +403,16 @@ module joiner_quad_clear(xspacing=undef, yspacing=undef, spacing1=undef, spacing // joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate="alt", spin=-90); module joiner_quad(spacing1=undef, spacing2=undef, xspacing=undef, yspacing=undef, h=40, w=10, l=10, a=30, n=2, alternate=true, screwsize=undef, guides=true, anchor=CENTER, spin=0, orient=UP) { - spacing1 = first_defined([spacing1, xspacing, 100]); - spacing2 = first_defined([spacing2, yspacing, 50]); - attachable(anchor,spin,orient, size=[w+spacing1, spacing2, h]) { - zrot_copies(n=2) { - back(spacing2/2) { - joiner_pair(spacing=spacing1, n=n, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides); - } - } - children(); - } + spacing1 = first_defined([spacing1, xspacing, 100]); + spacing2 = first_defined([spacing2, yspacing, 50]); + attachable(anchor,spin,orient, size=[w+spacing1, spacing2, h]) { + zrot_copies(n=2) { + back(spacing2/2) { + joiner_pair(spacing=spacing1, n=n, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides); + } + } + children(); + } } @@ -497,70 +497,70 @@ module joiner_quad(spacing1=undef, spacing2=undef, xspacing=undef, yspacing=unde // position(TOP+BACK) xcopies(10,5) dovetail("female", length=10, width=7, taper=4, height=4, $tags="remove",anchor=BOTTOM+FRONT,spin=180); module dovetail(gender, length, l, width, w, height, h, angle, slope, taper, back_width, chamfer, extra=0.01, r, radius, round=false, anchor=BOTTOM, spin=0, orient) { - radius = get_radius(r1=radius,r2=r); - lcount = num_defined([l,length]); - hcount = num_defined([h,height]); - wcount = num_defined([w,width]); - assert(lcount==1, "Must define exactly one of l and length"); - assert(wcount==1, "Must define exactly one of w and width"); - assert(hcount==1, "Must define exactly one of h and height"); - h = first_defined([h,height]); - w = first_defined([w,width]); - length = first_defined([l,length]); - orient = is_def(orient) ? orient : - gender == "female" ? DOWN : UP; - count = num_defined([angle,slope]); - assert(count<=1, "Do not specify both angle and slope"); - count2 = num_defined([taper,back_width]); - assert(count2<=1, "Do not specify both taper and back_width"); - count3 = num_defined([chamfer, radius]); - assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius"); - slope = is_def(slope) ? slope : - is_def(angle) ? 1/tan(angle) : 6; - width = gender == "male" ? w : w + 2*$slop; - height = h + (gender == "female" ? 2*$slop : 0); + radius = get_radius(r1=radius,r2=r); + lcount = num_defined([l,length]); + hcount = num_defined([h,height]); + wcount = num_defined([w,width]); + assert(lcount==1, "Must define exactly one of l and length"); + assert(wcount==1, "Must define exactly one of w and width"); + assert(hcount==1, "Must define exactly one of h and height"); + h = first_defined([h,height]); + w = first_defined([w,width]); + length = first_defined([l,length]); + orient = is_def(orient) ? orient : + gender == "female" ? DOWN : UP; + count = num_defined([angle,slope]); + assert(count<=1, "Do not specify both angle and slope"); + count2 = num_defined([taper,back_width]); + assert(count2<=1, "Do not specify both taper and back_width"); + count3 = num_defined([chamfer, radius]); + assert(count3<=1 || (radius==0 && chamfer==0), "Do not specify both chamfer and radius"); + slope = is_def(slope) ? slope : + is_def(angle) ? 1/tan(angle) : 6; + width = gender == "male" ? w : w + 2*$slop; + height = h + (gender == "female" ? 2*$slop : 0); - front_offset = is_def(taper) ? -extra * tan(taper) : - is_def(back_width) ? extra * (back_width-width)/length/2 : 0; + front_offset = is_def(taper) ? -extra * tan(taper) : + is_def(back_width) ? extra * (back_width-width)/length/2 : 0; - size = is_def(chamfer) && chamfer>0 ? chamfer : - is_def(radius) && radius>0 ? radius : 0; - type = is_def(chamfer) && chamfer>0 ? "chamfer" : "circle"; + size = is_def(chamfer) && chamfer>0 ? chamfer : + is_def(radius) && radius>0 ? radius : 0; + type = is_def(chamfer) && chamfer>0 ? "chamfer" : "circle"; - fullsize = round ? [size,size] : - gender == "male" ? [size,0] : [0,size]; + fullsize = round ? [size,size] : + gender == "male" ? [size,0] : [0,size]; - smallend_half = round_corners( - move( - [0,-length/2-extra,0], - p=[ - [0 , 0, height], - [width/2-front_offset , 0, height], - [width/2 - height/slope - front_offset, 0, 0 ], - [width/2 - front_offset + height, 0, 0] - ] - ), - method=type, cut = fullsize, closed=false - ); - smallend_points = concat(select(smallend_half, 1, -2), [down(extra,p=select(smallend_half, -2))]); - offset = is_def(taper) ? -(length+extra) * tan(taper) : - is_def(back_width) ? (back_width-width) / 2 : 0; - bigend_points = move([offset,length+2*extra,0], p=smallend_points); + smallend_half = round_corners( + move( + [0,-length/2-extra,0], + p=[ + [0 , 0, height], + [width/2-front_offset , 0, height], + [width/2 - height/slope - front_offset, 0, 0 ], + [width/2 - front_offset + height, 0, 0] + ] + ), + method=type, cut = fullsize, closed=false + ); + smallend_points = concat(select(smallend_half, 1, -2), [down(extra,p=select(smallend_half, -2))]); + offset = is_def(taper) ? -(length+extra) * tan(taper) : + is_def(back_width) ? (back_width-width) / 2 : 0; + bigend_points = move([offset,length+2*extra,0], p=smallend_points); - adjustment = gender == "male" ? -0.01 : 0.01; // Adjustment for default overlap in attach() + adjustment = gender == "male" ? -0.01 : 0.01; // Adjustment for default overlap in attach() - attachable(anchor,spin,orient, size=[width+2*offset, length, height]) { - down(height/2+adjustment) { - skin( - [ - reverse(concat(smallend_points, xflip(p=reverse(smallend_points)))), - reverse(concat(bigend_points, xflip(p=reverse(bigend_points)))) - ], - slices=0, convexity=4 - ); - } - children(); - } + attachable(anchor,spin,orient, size=[width+2*offset, length, height]) { + down(height/2+adjustment) { + skin( + [ + reverse(concat(smallend_points, xflip(p=reverse(smallend_points)))), + reverse(concat(bigend_points, xflip(p=reverse(bigend_points)))) + ], + slices=0, convexity=4 + ); + } + children(); + } } @@ -781,4 +781,4 @@ module snap_pin_socket(size, r, radius, l,length, d,diameter,nub_depth, snap, fi -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/knurling.scad b/knurling.scad index bcf44a5..c2c6f52 100644 --- a/knurling.scad +++ b/knurling.scad @@ -48,79 +48,79 @@ // knurled_cylinder(l=30, r=20, count=30, profile=90, helix=30); // knurled_cylinder(l=30, r=20, count=20, profile=120, helix=30); module knurled_cylinder( - l=20, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - count=30, profile=120, helix=30, - chamfer=undef, chamfer1=undef, chamfer2=undef, - chamfang=undef, chamfang1=undef, chamfang2=undef, - from_end=false, - rounding=undef, rounding1=undef, rounding2=undef, - anchor=CENTER, spin=0, orient=UP + l=20, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + count=30, profile=120, helix=30, + chamfer=undef, chamfer1=undef, chamfer2=undef, + chamfang=undef, chamfang1=undef, chamfang2=undef, + from_end=false, + rounding=undef, rounding1=undef, rounding2=undef, + anchor=CENTER, spin=0, orient=UP ) { - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); - inset = r1 * sin(180/count) / tan(profile/2); - twist = 360*l*tan(helix)/(r1*2*PI); - c1 = circle(r=r1,$fn=count); - c2 = rot(-180/count,p=circle(r=r1-inset,$fn=count)); - path = [for (i=idx(c1)) each [c1[i],c2[i]]]; - knob_w = 2*PI*r1/count; - knob_h = knob_w / tan(helix); - layers = ceil(l/knob_h); - plen = len(path); - vertices = concat( - [ - for (layer = [0:1:layers], pt=path) - (layer%2)? [pt.x, pt.y, layer*knob_h-layers*knob_h/2] : - rot(180/count, p=[pt.x, pt.y, layer*knob_h-layers*knob_h/2]) - ], [ - [0,0,-layers*knob_h/2], - [0,0, layers*knob_h/2] - ] - ); - faces = concat( - [ - for (layer = [0:1:layers-1], i=idx(path)) let( - loff = (layer%2)? 2 : 0, - i1 = layer*plen+((i+1)%plen), - i2 = layer*plen+((i+2)%plen), - i3 = (layer+1)*plen+posmod(i+1+loff,plen), - i4 = (layer+1)*plen+posmod(i+2+loff,plen), - i5 = (layer+1)*plen+posmod(i-0+loff,plen), - i6 = (layer+1)*plen+posmod(i-1+loff,plen) - ) each [ - [i1, i2, ((i%2)? i5 : i3)], - [i3, i5, ((i%2)? i2 : i1)] - ] - ], [ - for (i=[0:1:count-1]) let( - i1 = posmod(i*2+1,plen), - i2 = posmod(i*2+2,plen), - i3 = posmod(i*2+3,plen), - loff = layers*plen - ) each [ - [i1,i3,i2], - [i1+loff,i2+loff,i3+loff], - [i3,i1,len(vertices)-2], - [i1+loff,i3+loff,len(vertices)-1] - ] - ] - ); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - intersection() { - polyhedron(points=vertices, faces=faces, convexity=2*layers); - cyl( - r1=r1, r2=r2, l=l, - chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, - chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2, - from_end=from_end, - rounding=rounding, rounding1=rounding1, rounding2=rounding2, - $fn=count*2 - ); - } - children(); - } + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); + inset = r1 * sin(180/count) / tan(profile/2); + twist = 360*l*tan(helix)/(r1*2*PI); + c1 = circle(r=r1,$fn=count); + c2 = rot(-180/count,p=circle(r=r1-inset,$fn=count)); + path = [for (i=idx(c1)) each [c1[i],c2[i]]]; + knob_w = 2*PI*r1/count; + knob_h = knob_w / tan(helix); + layers = ceil(l/knob_h); + plen = len(path); + vertices = concat( + [ + for (layer = [0:1:layers], pt=path) + (layer%2)? [pt.x, pt.y, layer*knob_h-layers*knob_h/2] : + rot(180/count, p=[pt.x, pt.y, layer*knob_h-layers*knob_h/2]) + ], [ + [0,0,-layers*knob_h/2], + [0,0, layers*knob_h/2] + ] + ); + faces = concat( + [ + for (layer = [0:1:layers-1], i=idx(path)) let( + loff = (layer%2)? 2 : 0, + i1 = layer*plen+((i+1)%plen), + i2 = layer*plen+((i+2)%plen), + i3 = (layer+1)*plen+posmod(i+1+loff,plen), + i4 = (layer+1)*plen+posmod(i+2+loff,plen), + i5 = (layer+1)*plen+posmod(i-0+loff,plen), + i6 = (layer+1)*plen+posmod(i-1+loff,plen) + ) each [ + [i1, i2, ((i%2)? i5 : i3)], + [i3, i5, ((i%2)? i2 : i1)] + ] + ], [ + for (i=[0:1:count-1]) let( + i1 = posmod(i*2+1,plen), + i2 = posmod(i*2+2,plen), + i3 = posmod(i*2+3,plen), + loff = layers*plen + ) each [ + [i1,i3,i2], + [i1+loff,i2+loff,i3+loff], + [i3,i1,len(vertices)-2], + [i1+loff,i3+loff,len(vertices)-1] + ] + ] + ); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + intersection() { + polyhedron(points=vertices, faces=faces, convexity=2*layers); + cyl( + r1=r1, r2=r2, l=l, + chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, + chamfang=chamfang, chamfang1=chamfang1, chamfang2=chamfang2, + from_end=from_end, + rounding=rounding, rounding1=rounding1, rounding2=rounding2, + $fn=count*2 + ); + } + children(); + } } @@ -149,22 +149,22 @@ module knurled_cylinder( // knurled_cylinder_mask(l=30, r=20, overage=5, profile=120, helix=30); // knurled_cylinder_mask(l=30, r=20, overage=10, profile=120, helix=30); module knurled_cylinder_mask( - l=10, overage=5, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - count=30, profile=120, helix=30, - anchor=CENTER, spin=0, orient=UP + l=10, overage=5, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + count=30, profile=120, helix=30, + anchor=CENTER, spin=0, orient=UP ) { - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - difference() { - cylinder(r1=r1+overage, r2=r2+overage, h=l, center=true); - knurled_cylinder(r1=r1, r2=r2, l=l+0.01); - } - children(); - } + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + difference() { + cylinder(r1=r1+overage, r2=r2+overage, h=l, center=true); + knurled_cylinder(r1=r1, r2=r2, l=l+0.01); + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/linear_bearings.scad b/linear_bearings.scad index a0848e1..fe039f2 100644 --- a/linear_bearings.scad +++ b/linear_bearings.scad @@ -20,24 +20,24 @@ include // Arguments: // size = Inner size of lmXuu bearing, in mm. function get_lmXuu_bearing_diam(size) = lookup(size, [ - [ 4.0, 8.0], - [ 5.0, 10.0], - [ 6.0, 12.0], - [ 8.0, 15.0], - [ 10.0, 19.0], - [ 12.0, 21.0], - [ 13.0, 23.0], - [ 16.0, 28.0], - [ 20.0, 32.0], - [ 25.0, 40.0], - [ 30.0, 45.0], - [ 35.0, 52.0], - [ 40.0, 60.0], - [ 50.0, 80.0], - [ 60.0, 90.0], - [ 80.0, 120.0], - [100.0, 150.0] - ]); + [ 4.0, 8.0], + [ 5.0, 10.0], + [ 6.0, 12.0], + [ 8.0, 15.0], + [ 10.0, 19.0], + [ 12.0, 21.0], + [ 13.0, 23.0], + [ 16.0, 28.0], + [ 20.0, 32.0], + [ 25.0, 40.0], + [ 30.0, 45.0], + [ 35.0, 52.0], + [ 40.0, 60.0], + [ 50.0, 80.0], + [ 60.0, 90.0], + [ 80.0, 120.0], + [100.0, 150.0] + ]); // Function: get_lmXuu_bearing_length() @@ -45,24 +45,24 @@ function get_lmXuu_bearing_diam(size) = lookup(size, [ // Arguments: // size = Inner size of lmXuu bearing, in mm. function get_lmXuu_bearing_length(size) = lookup(size, [ - [ 4.0, 12.0], - [ 5.0, 15.0], - [ 6.0, 19.0], - [ 8.0, 24.0], - [ 10.0, 29.0], - [ 12.0, 30.0], - [ 13.0, 32.0], - [ 16.0, 37.0], - [ 20.0, 42.0], - [ 25.0, 59.0], - [ 30.0, 64.0], - [ 35.0, 70.0], - [ 40.0, 80.0], - [ 50.0, 100.0], - [ 60.0, 110.0], - [ 80.0, 140.0], - [100.0, 175.0] - ]); + [ 4.0, 12.0], + [ 5.0, 15.0], + [ 6.0, 19.0], + [ 8.0, 24.0], + [ 10.0, 29.0], + [ 12.0, 30.0], + [ 13.0, 32.0], + [ 16.0, 37.0], + [ 20.0, 42.0], + [ 25.0, 59.0], + [ 30.0, 64.0], + [ 35.0, 70.0], + [ 40.0, 80.0], + [ 50.0, 100.0], + [ 60.0, 110.0], + [ 80.0, 140.0], + [100.0, 175.0] + ]); // Module: linear_bearing_housing() @@ -83,45 +83,45 @@ function get_lmXuu_bearing_length(size) = lookup(size, [ // linear_bearing_housing(d=19, l=29, wall=2, tab=6, screwsize=2.5); module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, anchor=BOTTOM, spin=0, orient=UP) { - od = d+2*wall; - ogap = gap+2*tabwall; - tabh = tab/2+od/2*sqrt(2)-ogap/2; - h = od+tab/2; - anchors = [ - anchorpt("axis", [0,0,-tab/2/2]), - anchorpt("screw", [0,2-ogap/2,tabh-tab/2/2],FWD), - anchorpt("nut", [0,ogap/2-2,tabh-tab/2/2],FWD) - ]; - attachable(anchor,spin,orient, size=[l, od, h], anchors=anchors) { - down(tab/2/2) - difference() { - union() { - // Housing - zrot(90) teardrop(r=od/2,h=l); + od = d+2*wall; + ogap = gap+2*tabwall; + tabh = tab/2+od/2*sqrt(2)-ogap/2; + h = od+tab/2; + anchors = [ + anchorpt("axis", [0,0,-tab/2/2]), + anchorpt("screw", [0,2-ogap/2,tabh-tab/2/2],FWD), + anchorpt("nut", [0,ogap/2-2,tabh-tab/2/2],FWD) + ]; + attachable(anchor,spin,orient, size=[l, od, h], anchors=anchors) { + down(tab/2/2) + difference() { + union() { + // Housing + zrot(90) teardrop(r=od/2,h=l); - // Base - cube([l,od,od/2], anchor=TOP); + // Base + cube([l,od,od/2], anchor=TOP); - // Tabs - cube([l,ogap,od/2+tab/2], anchor=BOTTOM); - } + // Tabs + cube([l,ogap,od/2+tab/2], anchor=BOTTOM); + } - // Clear bearing space - zrot(90) teardrop(r=d/2,h=l+0.05); + // Clear bearing space + zrot(90) teardrop(r=d/2,h=l+0.05); - // Clear gap - cube([l+0.05,gap,od], anchor=BOTTOM); + // Clear gap + cube([l+0.05,gap,od], anchor=BOTTOM); - up(tabh) { - // Screwhole - fwd(ogap/2-2+0.01) screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10, orient=FWD); + up(tabh) { + // Screwhole + fwd(ogap/2-2+0.01) screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10, orient=FWD); - // Nut holder - back(ogap/2-2+0.01) metric_nut(size=screwsize, hole=false, anchor=BOTTOM, orient=BACK); - } - } - children(); - } + // Nut holder + back(ogap/2-2+0.01) metric_nut(size=screwsize, hole=false, anchor=BOTTOM, orient=BACK); + } + } + children(); + } } @@ -142,10 +142,10 @@ module linear_bearing_housing(d=15, l=24, tab=7, gap=5, wall=3, tabwall=5, screw // lmXuu_housing(size=10, wall=2, tab=6, screwsize=2.5); module lmXuu_housing(size=8, tab=7, gap=5, wall=3, tabwall=5, screwsize=3, anchor=BOTTOM, spin=0, orient=UP) { - d = get_lmXuu_bearing_diam(size); - l = get_lmXuu_bearing_length(size); - linear_bearing_housing(d=d, l=l, tab=tab, gap=gap, wall=wall, tabwall=tabwall, screwsize=screwsize, orient=orient, spin=spin, anchor=anchor) children(); + d = get_lmXuu_bearing_diam(size); + l = get_lmXuu_bearing_length(size); + linear_bearing_housing(d=d, l=l, tab=tab, gap=gap, wall=wall, tabwall=tabwall, screwsize=screwsize, orient=orient, spin=spin, anchor=anchor) children(); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/masks.scad b/masks.scad index 6f6c43f..4a75886 100644 --- a/masks.scad +++ b/masks.scad @@ -31,19 +31,19 @@ // Example(FR): // angle_pie_mask(ang=30, d=100, l=20); module angle_pie_mask( - ang=45, l=undef, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - h=undef, - anchor=CENTER, spin=0, orient=UP + ang=45, l=undef, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + h=undef, + anchor=CENTER, spin=0, orient=UP ) { - l = first_defined([l, h, 1]); - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - pie_slice(ang=ang, l=l+0.1, r1=r1, r2=r2, anchor=CENTER); - children(); - } + l = first_defined([l, h, 1]); + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + pie_slice(ang=ang, l=l+0.1, r1=r1, r2=r2, anchor=CENTER); + children(); + } } @@ -98,53 +98,53 @@ module angle_pie_mask( // cube([100,50,100], center=true); // } module cylinder_mask( - l, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - chamfer=undef, chamfer1=undef, chamfer2=undef, - chamfang=undef, chamfang1=undef, chamfang2=undef, - rounding=undef, rounding1=undef, rounding2=undef, - circum=false, from_end=false, - overage=10, ends_only=false, - anchor=CENTER, spin=0, orient=UP + l, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + chamfer=undef, chamfer1=undef, chamfer2=undef, + chamfang=undef, chamfang1=undef, chamfang2=undef, + rounding=undef, rounding1=undef, rounding2=undef, + circum=false, from_end=false, + overage=10, ends_only=false, + anchor=CENTER, spin=0, orient=UP ) { - r1 = get_radius(r=r, d=d, r1=r1, d1=d1, dflt=1); - r2 = get_radius(r=r, d=d, r1=r2, d1=d2, dflt=1); - sides = segs(max(r1,r2)); - sc = circum? 1/cos(180/sides) : 1; - vang = atan2(l, r1-r2)/2; - ang1 = first_defined([chamfang1, chamfang, vang]); - ang2 = first_defined([chamfang2, chamfang, 90-vang]); - cham1 = first_defined([chamfer1, chamfer, 0]); - cham2 = first_defined([chamfer2, chamfer, 0]); - fil1 = first_defined([rounding1, rounding, 0]); - fil2 = first_defined([rounding2, rounding, 0]); - maxd = max(r1,r2); - if ($children > 0) { - difference() { - children(); - cylinder_mask(l=l, r1=sc*r1, r2=sc*r2, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, rounding1=fil1, rounding2=fil2, orient=orient, from_end=from_end); - } - } else { - attachable(anchor,spin,orient, r=r1, l=l) { - difference() { - union() { - chlen1 = cham1 / (from_end? 1 : tan(ang1)); - chlen2 = cham2 / (from_end? 1 : tan(ang2)); - if (!ends_only) { - cylinder(r=maxd+overage, h=l+2*overage, center=true); - } else { - if (cham2>0) up(l/2-chlen2) cylinder(r=maxd+overage, h=chlen2+overage, center=false); - if (cham1>0) down(l/2+overage) cylinder(r=maxd+overage, h=chlen1+overage, center=false); - if (fil2>0) up(l/2-fil2) cylinder(r=maxd+overage, h=fil2+overage, center=false); - if (fil1>0) down(l/2+overage) cylinder(r=maxd+overage, h=fil1+overage, center=false); - } - } - cyl(r1=sc*r1, r2=sc*r2, l=l, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, from_end=from_end, rounding1=fil1, rounding2=fil2); - } - nil(); - } - } + r1 = get_radius(r=r, d=d, r1=r1, d1=d1, dflt=1); + r2 = get_radius(r=r, d=d, r1=r2, d1=d2, dflt=1); + sides = segs(max(r1,r2)); + sc = circum? 1/cos(180/sides) : 1; + vang = atan2(l, r1-r2)/2; + ang1 = first_defined([chamfang1, chamfang, vang]); + ang2 = first_defined([chamfang2, chamfang, 90-vang]); + cham1 = first_defined([chamfer1, chamfer, 0]); + cham2 = first_defined([chamfer2, chamfer, 0]); + fil1 = first_defined([rounding1, rounding, 0]); + fil2 = first_defined([rounding2, rounding, 0]); + maxd = max(r1,r2); + if ($children > 0) { + difference() { + children(); + cylinder_mask(l=l, r1=sc*r1, r2=sc*r2, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, rounding1=fil1, rounding2=fil2, orient=orient, from_end=from_end); + } + } else { + attachable(anchor,spin,orient, r=r1, l=l) { + difference() { + union() { + chlen1 = cham1 / (from_end? 1 : tan(ang1)); + chlen2 = cham2 / (from_end? 1 : tan(ang2)); + if (!ends_only) { + cylinder(r=maxd+overage, h=l+2*overage, center=true); + } else { + if (cham2>0) up(l/2-chlen2) cylinder(r=maxd+overage, h=chlen2+overage, center=false); + if (cham1>0) down(l/2+overage) cylinder(r=maxd+overage, h=chlen1+overage, center=false); + if (fil2>0) up(l/2-fil2) cylinder(r=maxd+overage, h=fil2+overage, center=false); + if (fil1>0) down(l/2+overage) cylinder(r=maxd+overage, h=fil1+overage, center=false); + } + } + cyl(r1=sc*r1, r2=sc*r2, l=l, chamfer1=cham1, chamfer2=cham2, chamfang1=ang1, chamfang2=ang2, from_end=from_end, rounding1=fil1, rounding2=fil2); + } + nil(); + } + } } @@ -171,10 +171,10 @@ module cylinder_mask( // #chamfer_mask(l=50, chamfer=10, orient=RIGHT); // } module chamfer_mask(l=1, chamfer=1, anchor=CENTER, spin=0, orient=UP) { - attachable(anchor,spin,orient, size=[chamfer*2, chamfer*2, l]) { - cylinder(r=chamfer, h=l+0.1, center=true, $fn=4); - children(); - } + attachable(anchor,spin,orient, size=[chamfer*2, chamfer*2, l]) { + cylinder(r=chamfer, h=l+0.1, center=true, $fn=4); + children(); + } } @@ -196,7 +196,7 @@ module chamfer_mask(l=1, chamfer=1, anchor=CENTER, spin=0, orient=UP) { // #chamfer_mask_x(l=50, chamfer=10); // } module chamfer_mask_x(l=1.0, chamfer=1.0, anchor=CENTER, spin=0) { - chamfer_mask(l=l, chamfer=chamfer, anchor=anchor, spin=spin, orient=RIGHT) children(); + chamfer_mask(l=l, chamfer=chamfer, anchor=anchor, spin=spin, orient=RIGHT) children(); } @@ -218,7 +218,7 @@ module chamfer_mask_x(l=1.0, chamfer=1.0, anchor=CENTER, spin=0) { // #chamfer_mask_y(l=50, chamfer=10); // } module chamfer_mask_y(l=1.0, chamfer=1.0, anchor=CENTER, spin=0) { - chamfer_mask(l=l, chamfer=chamfer, anchor=anchor, spin=spin, orient=BACK) children(); + chamfer_mask(l=l, chamfer=chamfer, anchor=anchor, spin=spin, orient=BACK) children(); } @@ -240,7 +240,7 @@ module chamfer_mask_y(l=1.0, chamfer=1.0, anchor=CENTER, spin=0) { // #chamfer_mask_z(l=50, chamfer=10); // } module chamfer_mask_z(l=1.0, chamfer=1.0, anchor=CENTER, spin=0) { - chamfer_mask(l=l, chamfer=chamfer, anchor=anchor, spin=spin, orient=UP) children(); + chamfer_mask(l=l, chamfer=chamfer, anchor=anchor, spin=spin, orient=UP) children(); } @@ -264,13 +264,13 @@ module chamfer_mask_z(l=1.0, chamfer=1.0, anchor=CENTER, spin=0) { // } module chamfer(chamfer=1, size=[1,1,1], edges=EDGES_ALL, except_edges=[]) { - difference() { - children(); - difference() { - cube(size, center=true); - cuboid(size+[1,1,1]*0.02, chamfer=chamfer+0.01, edges=edges, except_edges=except_edges, trimcorners=true); - } - } + difference() { + children(); + difference() { + cube(size, center=true); + cuboid(size+[1,1,1]*0.02, chamfer=chamfer+0.01, edges=edges, except_edges=except_edges, trimcorners=true); + } + } } @@ -303,11 +303,11 @@ module chamfer(chamfer=1, size=[1,1,1], edges=EDGES_ALL, except_edges=[]) // } module chamfer_cylinder_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false, anchor=CENTER, spin=0, orient=UP) { - r = get_radius(r=r, d=d, dflt=1); - attachable(anchor,spin,orient, r=r, l=chamfer*2) { - cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, anchor=TOP); - children(); - } + r = get_radius(r=r, d=d, dflt=1); + attachable(anchor,spin,orient, r=r, l=chamfer*2) { + cylinder_mask(l=chamfer*3, r=r, chamfer2=chamfer, chamfang2=ang, from_end=from_end, ends_only=true, anchor=TOP); + children(); + } } @@ -344,17 +344,17 @@ module chamfer_cylinder_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=fa // chamfer_hole_mask(d=100, chamfer=25, ang=30, overage=10); module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false, overage=0.1, anchor=CENTER, spin=0, orient=UP) { - r = get_radius(r=r, d=d, dflt=1); - h = chamfer * (from_end? 1 : tan(90-ang)); - r2 = r + chamfer * (from_end? tan(ang) : 1); - $fn = segs(r); - attachable(anchor,spin,orient, r1=r, r2=r2, l=h*2) { - union() { - cylinder(r=r2, h=overage, center=false); - down(h) cylinder(r1=r, r2=r2, h=h, center=false); - } - children(); - } + r = get_radius(r=r, d=d, dflt=1); + h = chamfer * (from_end? 1 : tan(90-ang)); + r2 = r + chamfer * (from_end? tan(ang) : 1); + $fn = segs(r); + attachable(anchor,spin,orient, r1=r, r2=r2, l=h*2) { + union() { + cylinder(r=r2, h=overage, center=false); + down(h) cylinder(r1=r, r2=r2, h=h, center=false); + } + children(); + } } @@ -406,30 +406,30 @@ module chamfer_hole_mask(r=undef, d=undef, chamfer=0.25, ang=45, from_end=false, // } module rounding_mask(l=undef, r=undef, r1=undef, r2=undef, anchor=CENTER, spin=0, orient=UP, h=undef) { - l = first_defined([l, h, 1]); - r1 = get_radius(r1=r1, r=r, dflt=1); - r2 = get_radius(r1=r2, r=r, dflt=1); - sides = quantup(segs(max(r1,r2)),4); - attachable(anchor,spin,orient, size=[2*r1,2*r1,l], size2=[2*r2,2*r2]) { - if (r10 && angle<90); - r = get_radius(r=r, d=d, dflt=1); - difference() { - translate(-[1,1,1]*excess) cube(r+excess, center=false); - translate([1,1,1]*r) onion(r=r,maxang=angle,orient=DOWN); - } + assert(is_num(angle)); + assert(is_num(excess)); + assert(angle>0 && angle<90); + r = get_radius(r=r, d=d, dflt=1); + difference() { + translate(-[1,1,1]*excess) cube(r+excess, center=false); + translate([1,1,1]*r) onion(r=r,maxang=angle,orient=DOWN); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/math.scad b/math.scad index 15f738a..f8dc707 100644 --- a/math.scad +++ b/math.scad @@ -109,10 +109,10 @@ function factorial(n,d=1) = product([for (i=[n:-1:d]) i]); // // Points colored in ROYGBIV order. // rainbow(pts) translate($item) circle(d=3,$fn=8); function lerp(a,b,u) = - assert(same_shape(a,b), "Bad or inconsistent inputs to lerp") - is_num(u)? (1-u)*a + u*b : - assert(!is_undef(u)&&!is_bool(u)&&!is_string(u), "Input u to lerp must be a number, vector, or range.") - [for (v = u) lerp(a,b,v)]; + assert(same_shape(a,b), "Bad or inconsistent inputs to lerp") + is_num(u)? (1-u)*a + u*b : + assert(!is_undef(u)&&!is_bool(u)&&!is_string(u), "Input u to lerp must be a number, vector, or range.") + [for (v = u) lerp(a,b,v)]; @@ -121,37 +121,37 @@ function lerp(a,b,u) = // Function: sinh() // Description: Takes a value `x`, and returns the hyperbolic sine of it. function sinh(x) = - (exp(x)-exp(-x))/2; + (exp(x)-exp(-x))/2; // Function: cosh() // Description: Takes a value `x`, and returns the hyperbolic cosine of it. function cosh(x) = - (exp(x)+exp(-x))/2; + (exp(x)+exp(-x))/2; // Function: tanh() // Description: Takes a value `x`, and returns the hyperbolic tangent of it. function tanh(x) = - sinh(x)/cosh(x); + sinh(x)/cosh(x); // Function: asinh() // Description: Takes a value `x`, and returns the inverse hyperbolic sine of it. function asinh(x) = - ln(x+sqrt(x*x+1)); + ln(x+sqrt(x*x+1)); // Function: acosh() // Description: Takes a value `x`, and returns the inverse hyperbolic cosine of it. function acosh(x) = - ln(x+sqrt(x*x-1)); + ln(x+sqrt(x*x-1)); // Function: atanh() // Description: Takes a value `x`, and returns the inverse hyperbolic tangent of it. function atanh(x) = - ln((1+x)/(1-x))/2; + ln((1+x)/(1-x))/2; @@ -182,8 +182,8 @@ function atanh(x) = // quant([9,10,10.4,10.5,11,12],3); // Returns: [9,9,9,12,12,12] // quant([[9,10,10.4],[10.5,11,12]],3); // Returns: [[9,9,9],[12,12,12]] function quant(x,y) = - is_list(x)? [for (v=x) quant(v,y)] : - floor(x/y+0.5)*y; + is_list(x)? [for (v=x) quant(v,y)] : + floor(x/y+0.5)*y; // Function: quantdn() @@ -211,8 +211,8 @@ function quant(x,y) = // quantdn([9,10,10.4,10.5,11,12],3); // Returns: [9,9,9,9,9,12] // quantdn([[9,10,10.4],[10.5,11,12]],3); // Returns: [[9,9,9],[9,9,12]] function quantdn(x,y) = - is_list(x)? [for (v=x) quantdn(v,y)] : - floor(x/y)*y; + is_list(x)? [for (v=x) quantdn(v,y)] : + floor(x/y)*y; // Function: quantup() @@ -240,8 +240,8 @@ function quantdn(x,y) = // quantup([9,10,10.4,10.5,11,12],3); // Returns: [9,12,12,12,12,12] // quantup([[9,10,10.4],[10.5,11,12]],3); // Returns: [[9,12,12],[12,12,12]] function quantup(x,y) = - is_list(x)? [for (v=x) quantup(v,y)] : - ceil(x/y)*y; + is_list(x)? [for (v=x) quantup(v,y)] : + ceil(x/y)*y; // Section: Constraints and Modulos @@ -296,7 +296,7 @@ function posmod(x,m) = (x%m+m)%m; // modang(270,360); // Returns: -90 // modang(700,360); // Returns: -20 function modang(x) = - let(xx = posmod(x,360)) xx<180? xx : xx-360; + let(xx = posmod(x,360)) xx<180? xx : xx-360; // Function: modrange() @@ -315,11 +315,11 @@ function modang(x) = // modrange(90,270,360, step=-45); // Returns: [90,45,0,315,270] // modrange(270,90,360, step=-45); // Returns: [270,225,180,135,90] function modrange(x, y, m, step=1) = - let( - a = posmod(x, m), - b = posmod(y, m), - c = step>0? (a>b? b+m : b) : (a0? (a>b? b+m : b) : (a= min, "Max value cannot be smaller than min") - let (rvect = is_def(seed) ? rands(min,max+1,N,seed) : rands(min,max+1,N)) - [for(entry = rvect) floor(entry)]; + assert(max >= min, "Max value cannot be smaller than min") + let (rvect = is_def(seed) ? rands(min,max+1,N,seed) : rands(min,max+1,N)) + [for(entry = rvect) floor(entry)]; // Function: gaussian_rands() @@ -355,8 +355,8 @@ function rand_int(min, max, N, seed=undef) = // N = Number of random numbers to return. Default: 1 // seed = If given, sets the random number seed. function gaussian_rands(mean, stddev, N=1, seed=undef) = - let(nums = is_undef(seed)? rands(0,1,N*2) : rands(0,1,N*2,seed)) - [for (i = list_range(N)) mean + stddev*sqrt(-2*ln(nums[i*2]))*cos(360*nums[i*2+1])]; + let(nums = is_undef(seed)? rands(0,1,N*2) : rands(0,1,N*2,seed)) + [for (i = list_range(N)) mean + stddev*sqrt(-2*ln(nums[i*2]))*cos(360*nums[i*2+1])]; // Function: log_rands() @@ -371,12 +371,12 @@ function gaussian_rands(mean, stddev, N=1, seed=undef) = // N = Number of random numbers to return. Default: 1 // seed = If given, sets the random number seed. function log_rands(minval, maxval, factor, N=1, seed=undef) = - assert(maxval >= minval, "maxval cannot be smaller than minval") - let( - minv = 1-1/pow(factor,minval), - maxv = 1-1/pow(factor,maxval), - nums = is_undef(seed)? rands(minv, maxv, N) : rands(minv, maxv, N, seed) - ) [for (num=nums) -ln(1-num)/ln(factor)]; + assert(maxval >= minval, "maxval cannot be smaller than minval") + let( + minv = 1-1/pow(factor,minval), + maxv = 1-1/pow(factor,maxval), + nums = is_undef(seed)? rands(minv, maxv, N) : rands(minv, maxv, N, seed) + ) [for (num=nums) -ln(1-num)/ln(factor)]; @@ -388,22 +388,22 @@ function log_rands(minval, maxval, factor, N=1, seed=undef) = // Description: // Computes the Greatest Common Divisor/Factor of `a` and `b`. function gcd(a,b) = - assert(is_int(a) && is_int(b),"Arguments to gcd must be integers") - b==0 ? abs(a) : gcd(b,a % b); + assert(is_int(a) && is_int(b),"Arguments to gcd must be integers") + b==0 ? abs(a) : gcd(b,a % b); // Computes lcm for two scalars function _lcm(a,b) = - assert(is_int(a), "Invalid non-integer parameters to lcm") - assert(is_int(b), "Invalid non-integer parameters to lcm") - assert(a!=0 && b!=0, "Arguments to lcm must be nonzero") - abs(a*b) / gcd(a,b); + assert(is_int(a), "Invalid non-integer parameters to lcm") + assert(is_int(b), "Invalid non-integer parameters to lcm") + assert(a!=0 && b!=0, "Arguments to lcm must be nonzero") + abs(a*b) / gcd(a,b); // Computes lcm for a list of values function _lcmlist(a) = - len(a)==1 ? a[0] : - _lcmlist(concat(slice(a,0,len(a)-2),[lcm(a[len(a)-2],a[len(a)-1])])); + len(a)==1 ? a[0] : + _lcmlist(concat(slice(a,0,len(a)-2),[lcm(a[len(a)-2],a[len(a)-1])])); // Function: lcm() @@ -415,12 +415,12 @@ function _lcmlist(a) = // be non-zero integers. The output is always a positive integer. It is an error to pass zero // as an argument. function lcm(a,b=[]) = - !is_list(a) && !is_list(b) ? _lcm(a,b) : - let( - arglist = concat(force_list(a),force_list(b)) - ) - assert(len(arglist)>0,"invalid call to lcm with empty list(s)") - _lcmlist(arglist); + !is_list(a) && !is_list(b) ? _lcm(a,b) : + let( + arglist = concat(force_list(a),force_list(b)) + ) + assert(len(arglist)>0,"invalid call to lcm with empty list(s)") + _lcmlist(arglist); @@ -438,8 +438,8 @@ function lcm(a,b=[]) = // 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) = - assert(is_consistent(v), "Input to sum is non-numeric or inconsistent") - len(v) == 0 ? dflt : _sum(v,v[0]*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); @@ -456,14 +456,14 @@ function _sum(v,_total,_i=0) = _i>=len(v) ? _total : _sum(v,_total+v[_i], _i+1); // cumsum([1,2,3]); // returns [1,3,6] // cumsum([[1,2,3], [3,4,5], [5,6,7]]); // returns [[1,2,3], [4,6,8], [9,12,15]] function cumsum(v,_i=0,_acc=[]) = - _i==len(v) ? _acc : - cumsum( - v, _i+1, - concat( - _acc, - [_i==0 ? v[_i] : select(_acc,-1)+v[_i]] - ) - ); + _i==len(v) ? _acc : + cumsum( + v, _i+1, + concat( + _acc, + [_i==0 ? v[_i] : select(_acc,-1)+v[_i]] + ) + ); // Function: sum_of_squares() @@ -489,12 +489,12 @@ function sum_of_squares(v, i=0, tot=0) = sum(vmul(v,v)); // Examples: // v = sum_of_sines(30, [[10,3,0], [5,5.5,60]]); function sum_of_sines(a, sines) = - sum([ - for (s = sines) let( - ss=point3d(s), - v=ss.x*sin(a*ss.y+ss.z) - ) v - ]); + sum([ + for (s = sines) let( + ss=point3d(s), + v=ss.x*sin(a*ss.y+ss.z) + ) v + ]); // Function: deltas() @@ -541,16 +541,16 @@ function mean(v) = sum(v)/len(v); // Given a list of numbers or vectors, finds the median value or midpoint. // If passed a list of vectors, returns the vector of the median of each part. function median(v) = - assert(is_list(v)) - assert(len(v)>0) - is_vector(v[0])? ( - assert(is_consistent(v)) - [ - for (i=idx(v[0])) - let(vals = subindex(v,i)) - (min(vals)+max(vals))/2 - ] - ) : (min(v)+max(v))/2; + assert(is_list(v)) + assert(len(v)>0) + is_vector(v[0])? ( + assert(is_consistent(v)) + [ + for (i=idx(v[0])) + let(vals = subindex(v,i)) + (min(vals)+max(vals))/2 + ] + ) : (min(v)+max(v))/2; // Section: Matrix math @@ -565,23 +565,23 @@ function median(v) = // want to solve Ax=b1 and Ax=b2 that you need to form the matrix transpose([b1,b2]) for the right hand side and then // transpose the returned value. function linear_solve(A,b) = - assert(is_matrix(A)) - let( - m = len(A), - n = len(A[0]) - ) - assert(is_vector(b,m) || is_matrix(b,m),"Incompatible matrix and right hand side") - let ( - qr = mj ? 0 : qr[1][i][j] - ] - ] - ) [qr[0],Rzero]; + assert(is_matrix(A)) + let( + m = len(A), + n = len(A[0]) + ) + let( + qr =_qr_factor(A, column=0, m = m, n=n, Q=ident(m)), + Rzero = [ + for(i=[0:m-1]) [ + for(j=[0:n-1]) + i>j ? 0 : qr[1][i][j] + ] + ] + ) [qr[0],Rzero]; function _qr_factor(A,Q, column, m, n) = - column >= min(m-1,n) ? [Q,A] : - let( - x = [for(i=[column:1:m-1]) A[i][column]], - alpha = (x[0]<=0 ? 1 : -1) * norm(x), - u = x - concat([alpha],repeat(0,m-1)), - v = u / norm(u), - Qc = ident(len(x)) - 2*transpose([v])*[v], - Qf = [for(i=[0:m-1]) [for(j=[0:m-1]) i= min(m-1,n) ? [Q,A] : + let( + x = [for(i=[column:1:m-1]) A[i][column]], + alpha = (x[0]<=0 ? 1 : -1) * norm(x), + u = x - concat([alpha],repeat(0,m-1)), + v = u / norm(u), + Qc = ident(len(x)) - 2*transpose([v])*[v], + Qf = [for(i=[0:m-1]) [for(j=[0:m-1]) i0 && - (is_undef(m) || len(A)==m) && - is_vector(A[0]) && - (is_undef(n) || len(A[0])==n) && - (!square || n==m) && - is_consistent(A); + is_list(A) && len(A)>0 && + (is_undef(m) || len(A)==m) && + is_vector(A[0]) && + (is_undef(n) || len(A[0])==n) && + (!square || n==m) && + is_consistent(A); @@ -758,18 +758,18 @@ function is_matrix(A,m,n, square=false) = // approx(0.3333,1/3,eps=1e-3); // Returns: true // approx(PI,3.1415926536); // Returns: true function approx(a,b,eps=EPSILON) = - a==b? true : - a*0!=b*0? false : - is_list(a)? ([for (i=idx(a)) if(!approx(a[i],b[i],eps=eps)) 1] == []) : - (abs(a-b) <= eps); + a==b? true : + a*0!=b*0? false : + is_list(a)? ([for (i=idx(a)) if(!approx(a[i],b[i],eps=eps)) 1] == []) : + (abs(a-b) <= eps); function _type_num(x) = - is_undef(x)? 0 : - is_bool(x)? 1 : - is_num(x)? 2 : - is_string(x)? 3 : - is_list(x)? 4 : 5; + is_undef(x)? 0 : + is_bool(x)? 1 : + is_num(x)? 2 : + is_string(x)? 3 : + is_list(x)? 4 : 5; // Function: compare_vals() @@ -782,10 +782,10 @@ function _type_num(x) = // a = First value to compare. // b = Second value to compare. function compare_vals(a, b) = - (a==b)? 0 : - let(t1=_type_num(a), t2=_type_num(b)) (t1!=t2)? (t1-t2) : - is_list(a)? compare_lists(a,b) : - (ab)? 1 : 0; + (a==b)? 0 : + let(t1=_type_num(a), t2=_type_num(b)) (t1!=t2)? (t1-t2) : + is_list(a)? compare_lists(a,b) : + (ab)? 1 : 0; // Function: compare_lists() @@ -800,13 +800,13 @@ function compare_vals(a, b) = // a = First list to compare. // b = Second list to compare. function compare_lists(a, b) = - a==b? 0 : let( - cmps = [ - for(i=[0:1:min(len(a),len(b))-1]) let( - cmp = compare_vals(a[i],b[i]) - ) if(cmp!=0) cmp - ] - ) cmps==[]? (len(a)-len(b)) : cmps[0]; + a==b? 0 : let( + cmps = [ + for(i=[0:1:min(len(a),len(b))-1]) let( + cmp = compare_vals(a[i],b[i]) + ) if(cmp!=0) cmp + ] + ) cmps==[]? (len(a)-len(b)) : cmps[0]; // Function: any() @@ -822,13 +822,13 @@ function compare_lists(a, b) = // any([[0,0], [0,0]]); // Returns false. // any([[0,0], [1,0]]); // Returns true. function any(l, i=0, succ=false) = - (i>=len(l) || succ)? succ : - any( - l, i=i+1, succ=( - is_list(l[i])? any(l[i]) : - !(!l[i]) - ) - ); + (i>=len(l) || succ)? succ : + any( + l, i=i+1, succ=( + is_list(l[i])? any(l[i]) : + !(!l[i]) + ) + ); // Function: all() @@ -845,13 +845,13 @@ function any(l, i=0, succ=false) = // all([[0,0], [1,0]]); // Returns false. // all([[1,1], [1,1]]); // Returns true. function all(l, i=0, fail=false) = - (i>=len(l) || fail)? (!fail) : - all( - l, i=i+1, fail=( - is_list(l[i])? !all(l[i]) : - !l[i] - ) - ); + (i>=len(l) || fail)? (!fail) : + all( + l, i=i+1, fail=( + is_list(l[i])? !all(l[i]) : + !l[i] + ) + ); // Function: count_true() @@ -875,13 +875,13 @@ function all(l, i=0, fail=false) = // count_true([[1,1], [1,1]]); // Returns 4. // count_true([[1,1], [1,1]], nmax=3); // Returns 3. function count_true(l, nmax=undef, i=0, cnt=0) = - (i>=len(l) || (nmax!=undef && cnt>=nmax))? cnt : - count_true( - l=l, nmax=nmax, i=i+1, cnt=cnt+( - is_list(l[i])? count_true(l[i], nmax=nmax-cnt) : - (l[i]? 1 : 0) - ) - ); + (i>=len(l) || (nmax!=undef && cnt>=nmax))? cnt : + count_true( + l=l, nmax=nmax, i=i+1, cnt=cnt+( + is_list(l[i])? count_true(l[i], nmax=nmax-cnt) : + (l[i]? 1 : 0) + ) + ); @@ -897,23 +897,23 @@ function count_true(l, nmax=undef, i=0, cnt=0) = // for internal points, f'(t) = (f(t+h)-f(t-h))/2h. For the endpoints (when closed=false) the algorithm // uses a two point method if sufficient points are available: f'(t) = (3*(f(t+h)-f(t)) - (f(t+2*h)-f(t+h)))/2h. function deriv(data, h=1, closed=false) = - let( L = len(data) ) - closed? [ - for(i=[0:1:L-1]) - (data[(i+1)%L]-data[(L+i-1)%L])/2/h - ] : - let( - first = - L<3? data[1]-data[0] : - 3*(data[1]-data[0]) - (data[2]-data[1]), - last = - L<3? data[L-1]-data[L-2]: - (data[L-3]-data[L-2])-3*(data[L-2]-data[L-1]) - ) [ - first/2/h, - for(i=[1:1:L-2]) (data[i+1]-data[i-1])/2/h, - last/2/h - ]; + let( L = len(data) ) + closed? [ + for(i=[0:1:L-1]) + (data[(i+1)%L]-data[(L+i-1)%L])/2/h + ] : + let( + first = + L<3? data[1]-data[0] : + 3*(data[1]-data[0]) - (data[2]-data[1]), + last = + L<3? data[L-1]-data[L-2]: + (data[L-3]-data[L-2])-3*(data[L-2]-data[L-1]) + ) [ + first/2/h, + for(i=[1:1:L-2]) (data[i+1]-data[i-1])/2/h, + last/2/h + ]; // Function: deriv2() @@ -928,25 +928,25 @@ function deriv(data, h=1, closed=false) = // f''(t) = (2*f(t) - 5*f(t+h) + 4*f(t+2*h) - f(t+3*h))/h^2 or if five points are available // f''(t) = (35*f(t) - 104*f(t+h) + 114*f(t+2*h) - 56*f(t+3*h) + 11*f(t+4*h)) / 12h^2 function deriv2(data, h=1, closed=false) = - let( L = len(data) ) - closed? [ - for(i=[0:1:L-1]) - (data[(i+1)%L]-2*data[i]+data[(L+i-1)%L])/h/h - ] : - let( - first = L<3? undef : - L==3? data[0] - 2*data[1] + data[2] : - L==4? 2*data[0] - 5*data[1] + 4*data[2] - data[3] : - (35*data[0] - 104*data[1] + 114*data[2] - 56*data[3] + 11*data[4])/12, - last = L<3? undef : - L==3? data[L-1] - 2*data[L-2] + data[L-3] : - L==4? -2*data[L-1] + 5*data[L-2] - 4*data[L-3] + data[L-4] : - (35*data[L-1] - 104*data[L-2] + 114*data[L-3] - 56*data[L-4] + 11*data[L-5])/12 - ) [ - first/h/h, - for(i=[1:1:L-2]) (data[i+1]-2*data[i]+data[i-1])/h/h, - last/h/h - ]; + let( L = len(data) ) + closed? [ + for(i=[0:1:L-1]) + (data[(i+1)%L]-2*data[i]+data[(L+i-1)%L])/h/h + ] : + let( + first = L<3? undef : + L==3? data[0] - 2*data[1] + data[2] : + L==4? 2*data[0] - 5*data[1] + 4*data[2] - data[3] : + (35*data[0] - 104*data[1] + 114*data[2] - 56*data[3] + 11*data[4])/12, + last = L<3? undef : + L==3? data[L-1] - 2*data[L-2] + data[L-3] : + L==4? -2*data[L-1] + 5*data[L-2] - 4*data[L-3] + data[L-4] : + (35*data[L-1] - 104*data[L-2] + 114*data[L-3] - 56*data[L-4] + 11*data[L-5])/12 + ) [ + first/h/h, + for(i=[1:1:L-2]) (data[i+1]-2*data[i]+data[i-1])/h/h, + last/h/h + ]; // Function: deriv3() @@ -960,28 +960,28 @@ function deriv2(data, h=1, closed=false) = // the estimates are f'''(t) = (-5*f(t)+18*f(t+h)-24*f(t+2*h)+14*f(t+3*h)-3*f(t+4*h)) / 2h^3 and // f'''(t) = (-3*f(t-h)+10*f(t)-12*f(t+h)+6*f(t+2*h)-f(t+3*h)) / 2h^3. function deriv3(data, h=1, closed=false) = - let( - L = len(data), - h3 = h*h*h - ) - assert(L>=5, "Need five points for 3rd derivative estimate") - closed? [ - for(i=[0:1:L-1]) - (-data[(L+i-2)%L]+2*data[(L+i-1)%L]-2*data[(i+1)%L]+data[(i+2)%L])/2/h3 - ] : - let( - first=(-5*data[0]+18*data[1]-24*data[2]+14*data[3]-3*data[4])/2, - second=(-3*data[0]+10*data[1]-12*data[2]+6*data[3]-data[4])/2, - last=(5*data[L-1]-18*data[L-2]+24*data[L-3]-14*data[L-4]+3*data[L-5])/2, - prelast=(3*data[L-1]-10*data[L-2]+12*data[L-3]-6*data[L-4]+data[L-5])/2 - ) [ - first/h3, - second/h3, - for(i=[2:1:L-3]) (-data[i-2]+2*data[i-1]-2*data[i+1]+data[i+2])/2/h3, - prelast/h3, - last/h3 - ]; + let( + L = len(data), + h3 = h*h*h + ) + assert(L>=5, "Need five points for 3rd derivative estimate") + closed? [ + for(i=[0:1:L-1]) + (-data[(L+i-2)%L]+2*data[(L+i-1)%L]-2*data[(i+1)%L]+data[(i+2)%L])/2/h3 + ] : + let( + first=(-5*data[0]+18*data[1]-24*data[2]+14*data[3]-3*data[4])/2, + second=(-3*data[0]+10*data[1]-12*data[2]+6*data[3]-data[4])/2, + last=(5*data[L-1]-18*data[L-2]+24*data[L-3]-14*data[L-4]+3*data[L-5])/2, + prelast=(3*data[L-1]-10*data[L-2]+12*data[L-3]-6*data[L-4]+data[L-5])/2 + ) [ + first/h3, + second/h3, + for(i=[2:1:L-3]) (-data[i-2]+2*data[i-1]-2*data[i+1]+data[i+2])/2/h3, + prelast/h3, + last/h3 + ]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/metric_screws.scad b/metric_screws.scad index 81b0de7..01ae5aa 100644 --- a/metric_screws.scad +++ b/metric_screws.scad @@ -20,339 +20,339 @@ include // Function: get_metric_bolt_head_size() // Description: Returns the diameter of a typical metric bolt's head, based on the bolt `size`. function get_metric_bolt_head_size(size) = lookup(size, [ - [ 3.0, 5.5], - [ 4.0, 7.0], - [ 5.0, 8.0], - [ 6.0, 10.0], - [ 7.0, 11.0], - [ 8.0, 13.0], - [10.0, 17.0], - [12.0, 19.0], - [14.0, 22.0], - [16.0, 24.0], - [18.0, 27.0], - [20.0, 30.0], - [24.0, 36.0], - [30.0, 46.0], - [36.0, 55.0], - [42.0, 65.0], - [48.0, 75.0], - [56.0, 85.0], - [64.0, 95.0] - ]); + [ 3.0, 5.5], + [ 4.0, 7.0], + [ 5.0, 8.0], + [ 6.0, 10.0], + [ 7.0, 11.0], + [ 8.0, 13.0], + [10.0, 17.0], + [12.0, 19.0], + [14.0, 22.0], + [16.0, 24.0], + [18.0, 27.0], + [20.0, 30.0], + [24.0, 36.0], + [30.0, 46.0], + [36.0, 55.0], + [42.0, 65.0], + [48.0, 75.0], + [56.0, 85.0], + [64.0, 95.0] + ]); // Function: get_metric_bolt_head_height() // Description: Returns the height of a typical metric bolt's head, based on the bolt `size`. function get_metric_bolt_head_height(size) = lookup(size, [ - [ 1.6, 1.23], - [ 2.0, 1.53], - [ 2.5, 1.83], - [ 3.0, 2.13], - [ 4.0, 2.93], - [ 5.0, 3.65], - [ 6.0, 4.15], - [ 8.0, 5.45], - [10.0, 6.58], - [12.0, 7.68], - [14.0, 8.98], - [16.0, 10.18], - [20.0, 12.72], - [24.0, 15.35], - [30.0, 19.12], - [36.0, 22.92], - [42.0, 26.42], - [48.0, 30.42], - [56.0, 35.50], - [64.0, 40.50] - ]); + [ 1.6, 1.23], + [ 2.0, 1.53], + [ 2.5, 1.83], + [ 3.0, 2.13], + [ 4.0, 2.93], + [ 5.0, 3.65], + [ 6.0, 4.15], + [ 8.0, 5.45], + [10.0, 6.58], + [12.0, 7.68], + [14.0, 8.98], + [16.0, 10.18], + [20.0, 12.72], + [24.0, 15.35], + [30.0, 19.12], + [36.0, 22.92], + [42.0, 26.42], + [48.0, 30.42], + [56.0, 35.50], + [64.0, 40.50] + ]); // Function: get_metric_socket_cap_diam() // Description: Returns the diameter of a typical metric socket cap bolt's head, based on the bolt `size`. function get_metric_socket_cap_diam(size) = lookup(size, [ - [ 1.6, 3.0], - [ 2.0, 3.8], - [ 2.5, 4.5], - [ 3.0, 5.5], - [ 4.0, 7.0], - [ 5.0, 8.5], - [ 6.0, 10.0], - [ 8.0, 13.0], - [10.0, 16.0], - [12.0, 18.0], - [14.0, 21.0], - [16.0, 24.0], - [18.0, 27.0], - [20.0, 30.0], - [22.0, 33.0], - [24.0, 36.0], - [27.0, 40.0], - [30.0, 45.0], - [33.0, 50.0], - [36.0, 54.0], - [42.0, 63.0], - [48.0, 72.0], - [56.0, 84.0], - [64.0, 96.0] - ]); + [ 1.6, 3.0], + [ 2.0, 3.8], + [ 2.5, 4.5], + [ 3.0, 5.5], + [ 4.0, 7.0], + [ 5.0, 8.5], + [ 6.0, 10.0], + [ 8.0, 13.0], + [10.0, 16.0], + [12.0, 18.0], + [14.0, 21.0], + [16.0, 24.0], + [18.0, 27.0], + [20.0, 30.0], + [22.0, 33.0], + [24.0, 36.0], + [27.0, 40.0], + [30.0, 45.0], + [33.0, 50.0], + [36.0, 54.0], + [42.0, 63.0], + [48.0, 72.0], + [56.0, 84.0], + [64.0, 96.0] + ]); // Function: get_metric_socket_cap_height() // Description: Returns the height of a typical metric socket cap bolt's head, based on the bolt `size`. function get_metric_socket_cap_height(size) = lookup(size, [ - [ 1.6, 1.7], - [ 2.0, 2.0], - [ 2.5, 2.5], - [ 3.0, 3.0], - [ 4.0, 4.0], - [ 5.0, 5.0], - [ 6.0, 6.0], - [ 8.0, 8.0], - [10.0, 10.0], - [12.0, 12.0], - [14.0, 14.0], - [16.0, 16.0], - [18.0, 18.0], - [20.0, 20.0], - [22.0, 22.0], - [24.0, 24.0], - [27.0, 27.0], - [30.0, 30.0], - [33.0, 33.0], - [36.0, 36.0], - [42.0, 42.0], - [48.0, 48.0], - [56.0, 56.0], - [64.0, 64.0] - ]); + [ 1.6, 1.7], + [ 2.0, 2.0], + [ 2.5, 2.5], + [ 3.0, 3.0], + [ 4.0, 4.0], + [ 5.0, 5.0], + [ 6.0, 6.0], + [ 8.0, 8.0], + [10.0, 10.0], + [12.0, 12.0], + [14.0, 14.0], + [16.0, 16.0], + [18.0, 18.0], + [20.0, 20.0], + [22.0, 22.0], + [24.0, 24.0], + [27.0, 27.0], + [30.0, 30.0], + [33.0, 33.0], + [36.0, 36.0], + [42.0, 42.0], + [48.0, 48.0], + [56.0, 56.0], + [64.0, 64.0] + ]); // Function: get_metric_socket_cap_socket_size() // Description: Returns the diameter of a typical metric socket cap bolt's hex drive socket, based on the bolt `size`. function get_metric_socket_cap_socket_size(size) = lookup(size, [ - [ 1.6, 1.5], - [ 2.0, 1.5], - [ 2.5, 2.0], - [ 3.0, 2.5], - [ 4.0, 3.0], - [ 5.0, 4.0], - [ 6.0, 5.0], - [ 8.0, 6.0], - [10.0, 8.0], - [12.0, 10.0], - [14.0, 12.0], - [16.0, 14.0], - [18.0, 14.0], - [20.0, 17.0], - [22.0, 17.0], - [24.0, 19.0], - [27.0, 19.0], - [30.0, 22.0], - [33.0, 24.0], - [36.0, 27.0], - [42.0, 32.0], - [48.0, 36.0], - [56.0, 41.0], - [64.0, 46.0] - ]); + [ 1.6, 1.5], + [ 2.0, 1.5], + [ 2.5, 2.0], + [ 3.0, 2.5], + [ 4.0, 3.0], + [ 5.0, 4.0], + [ 6.0, 5.0], + [ 8.0, 6.0], + [10.0, 8.0], + [12.0, 10.0], + [14.0, 12.0], + [16.0, 14.0], + [18.0, 14.0], + [20.0, 17.0], + [22.0, 17.0], + [24.0, 19.0], + [27.0, 19.0], + [30.0, 22.0], + [33.0, 24.0], + [36.0, 27.0], + [42.0, 32.0], + [48.0, 36.0], + [56.0, 41.0], + [64.0, 46.0] + ]); // Function: get_metric_socket_cap_socket_depth() // Description: Returns the depth of a typical metric socket cap bolt's hex drive socket, based on the bolt `size`. function get_metric_socket_cap_socket_depth(size) = lookup(size, [ - [ 1.6, 0.7], - [ 2.0, 1.0], - [ 2.5, 1.1], - [ 3.0, 1.3], - [ 4.0, 2.0], - [ 5.0, 2.5], - [ 6.0, 3.0], - [ 8.0, 4.0], - [10.0, 5.0], - [12.0, 6.0], - [14.0, 7.0], - [16.0, 8.0], - [18.0, 9.0], - [20.0, 10.0], - [22.0, 11.0], - [24.0, 12.0], - [27.0, 13.5], - [30.0, 15.5], - [33.0, 18.0], - [36.0, 19.0], - [42.0, 24.0], - [48.0, 28.0], - [56.0, 34.0], - [64.0, 38.0] - ]); + [ 1.6, 0.7], + [ 2.0, 1.0], + [ 2.5, 1.1], + [ 3.0, 1.3], + [ 4.0, 2.0], + [ 5.0, 2.5], + [ 6.0, 3.0], + [ 8.0, 4.0], + [10.0, 5.0], + [12.0, 6.0], + [14.0, 7.0], + [16.0, 8.0], + [18.0, 9.0], + [20.0, 10.0], + [22.0, 11.0], + [24.0, 12.0], + [27.0, 13.5], + [30.0, 15.5], + [33.0, 18.0], + [36.0, 19.0], + [42.0, 24.0], + [48.0, 28.0], + [56.0, 34.0], + [64.0, 38.0] + ]); // Function: get_metric_iso_coarse_thread_pitch() // Description: Returns the ISO metric standard coarse threading pitch for a given bolt `size`. function get_metric_iso_coarse_thread_pitch(size) = lookup(size, [ - [ 1.6, 0.35], - [ 2.0, 0.40], - [ 2.5, 0.45], - [ 3.0, 0.50], - [ 4.0, 0.70], - [ 5.0, 0.80], - [ 6.0, 1.00], - [ 7.0, 1.00], - [ 8.0, 1.25], - [10.0, 1.50], - [12.0, 1.75], - [14.0, 2.00], - [16.0, 2.00], - [18.0, 2.50], - [20.0, 2.50], - [22.0, 2.50], - [24.0, 3.00], - [27.0, 3.00], - [30.0, 3.50], - [33.0, 3.50], - [36.0, 4.00], - [39.0, 4.00], - [42.0, 4.50], - [45.0, 4.50], - [48.0, 5.00], - [56.0, 5.50], - [64.0, 6.00] - ]); + [ 1.6, 0.35], + [ 2.0, 0.40], + [ 2.5, 0.45], + [ 3.0, 0.50], + [ 4.0, 0.70], + [ 5.0, 0.80], + [ 6.0, 1.00], + [ 7.0, 1.00], + [ 8.0, 1.25], + [10.0, 1.50], + [12.0, 1.75], + [14.0, 2.00], + [16.0, 2.00], + [18.0, 2.50], + [20.0, 2.50], + [22.0, 2.50], + [24.0, 3.00], + [27.0, 3.00], + [30.0, 3.50], + [33.0, 3.50], + [36.0, 4.00], + [39.0, 4.00], + [42.0, 4.50], + [45.0, 4.50], + [48.0, 5.00], + [56.0, 5.50], + [64.0, 6.00] + ]); // Function: get_metric_iso_fine_thread_pitch() // Description: Returns the ISO metric standard fine threading pitch for a given bolt `size`. function get_metric_iso_fine_thread_pitch(size) = lookup(size, [ - [ 1.6, 0.35], - [ 2.0, 0.40], - [ 2.5, 0.45], - [ 3.0, 0.50], - [ 4.0, 0.70], - [ 5.0, 0.80], - [ 6.0, 1.00], - [ 7.0, 1.00], - [ 8.0, 1.00], - [10.0, 1.25], - [12.0, 1.50], - [14.0, 1.50], - [16.0, 2.00], - [18.0, 2.50], - [20.0, 2.50], - [22.0, 2.50], - [24.0, 3.00], - [27.0, 3.00], - [30.0, 3.50], - [33.0, 3.50], - [36.0, 4.00], - [39.0, 4.00], - [42.0, 4.50], - [45.0, 4.50], - [48.0, 5.00], - [56.0, 5.50], - [64.0, 6.00] - ]); + [ 1.6, 0.35], + [ 2.0, 0.40], + [ 2.5, 0.45], + [ 3.0, 0.50], + [ 4.0, 0.70], + [ 5.0, 0.80], + [ 6.0, 1.00], + [ 7.0, 1.00], + [ 8.0, 1.00], + [10.0, 1.25], + [12.0, 1.50], + [14.0, 1.50], + [16.0, 2.00], + [18.0, 2.50], + [20.0, 2.50], + [22.0, 2.50], + [24.0, 3.00], + [27.0, 3.00], + [30.0, 3.50], + [33.0, 3.50], + [36.0, 4.00], + [39.0, 4.00], + [42.0, 4.50], + [45.0, 4.50], + [48.0, 5.00], + [56.0, 5.50], + [64.0, 6.00] + ]); // Function: get_metric_iso_superfine_thread_pitch() // Description: Returns the ISO metric standard superfine threading pitch for a given bolt `size`. function get_metric_iso_superfine_thread_pitch(size) = lookup(size, [ - [ 1.6, 0.35], - [ 2.0, 0.40], - [ 2.5, 0.45], - [ 3.0, 0.50], - [ 4.0, 0.70], - [ 5.0, 0.80], - [ 6.0, 1.00], - [ 7.0, 1.00], - [ 8.0, 1.00], - [10.0, 1.00], - [12.0, 1.25], - [14.0, 1.50], - [16.0, 2.00], - [18.0, 2.50], - [20.0, 2.50], - [22.0, 2.50], - [24.0, 3.00], - [27.0, 3.00], - [30.0, 3.50], - [33.0, 3.50], - [36.0, 4.00], - [39.0, 4.00], - [42.0, 4.50], - [45.0, 4.50], - [48.0, 5.00], - [56.0, 5.50], - [64.0, 6.00] - ]); + [ 1.6, 0.35], + [ 2.0, 0.40], + [ 2.5, 0.45], + [ 3.0, 0.50], + [ 4.0, 0.70], + [ 5.0, 0.80], + [ 6.0, 1.00], + [ 7.0, 1.00], + [ 8.0, 1.00], + [10.0, 1.00], + [12.0, 1.25], + [14.0, 1.50], + [16.0, 2.00], + [18.0, 2.50], + [20.0, 2.50], + [22.0, 2.50], + [24.0, 3.00], + [27.0, 3.00], + [30.0, 3.50], + [33.0, 3.50], + [36.0, 4.00], + [39.0, 4.00], + [42.0, 4.50], + [45.0, 4.50], + [48.0, 5.00], + [56.0, 5.50], + [64.0, 6.00] + ]); // Function: get_metric_jis_thread_pitch() // Description: Returns the JIS metric standard threading pitch for a given bolt `size`. function get_metric_jis_thread_pitch(size) = lookup(size, [ - [ 2.0, 0.40], - [ 2.5, 0.45], - [ 3.0, 0.50], - [ 4.0, 0.70], - [ 5.0, 0.80], - [ 6.0, 1.00], - [ 7.0, 1.00], - [ 8.0, 1.25], - [10.0, 1.25], - [12.0, 1.25], - [14.0, 1.50], - [16.0, 1.50], - [18.0, 1.50], - [20.0, 1.50] - ]); + [ 2.0, 0.40], + [ 2.5, 0.45], + [ 3.0, 0.50], + [ 4.0, 0.70], + [ 5.0, 0.80], + [ 6.0, 1.00], + [ 7.0, 1.00], + [ 8.0, 1.25], + [10.0, 1.25], + [12.0, 1.25], + [14.0, 1.50], + [16.0, 1.50], + [18.0, 1.50], + [20.0, 1.50] + ]); // Function: get_metric_nut_size() // Description: Returns the typical metric nut flat-to-flat diameter for a given bolt `size`. function get_metric_nut_size(size) = lookup(size, [ - [ 2.0, 4.0], - [ 2.5, 5.0], - [ 3.0, 5.5], - [ 4.0, 7.0], - [ 5.0, 8.0], - [ 6.0, 10.0], - [ 7.0, 11.0], - [ 8.0, 13.0], - [10.0, 17.0], - [12.0, 19.0], - [14.0, 22.0], - [16.0, 24.0], - [18.0, 27.0], - [20.0, 30.0] - ]); + [ 2.0, 4.0], + [ 2.5, 5.0], + [ 3.0, 5.5], + [ 4.0, 7.0], + [ 5.0, 8.0], + [ 6.0, 10.0], + [ 7.0, 11.0], + [ 8.0, 13.0], + [10.0, 17.0], + [12.0, 19.0], + [14.0, 22.0], + [16.0, 24.0], + [18.0, 27.0], + [20.0, 30.0] + ]); // Function: get_metric_nut_thickness() // Description: Returns the typical metric nut thickness for a given bolt `size`. function get_metric_nut_thickness(size) = lookup(size, [ - [ 1.6, 1.3], - [ 2.0, 1.6], - [ 2.5, 2.0], - [ 3.0, 2.4], - [ 4.0, 3.2], - [ 5.0, 4.0], - [ 6.0, 5.0], - [ 7.0, 5.5], - [ 8.0, 6.5], - [10.0, 8.0], - [12.0, 10.0], - [14.0, 11.0], - [16.0, 13.0], - [18.0, 15.0], - [20.0, 16.0], - [24.0, 21.5], - [30.0, 25.6], - [36.0, 31.0], - [42.0, 34.0], - [48.0, 38.0], - [56.0, 45.0], - [64.0, 51.0] - ]); + [ 1.6, 1.3], + [ 2.0, 1.6], + [ 2.5, 2.0], + [ 3.0, 2.4], + [ 4.0, 3.2], + [ 5.0, 4.0], + [ 6.0, 5.0], + [ 7.0, 5.5], + [ 8.0, 6.5], + [10.0, 8.0], + [12.0, 10.0], + [14.0, 11.0], + [16.0, 13.0], + [18.0, 15.0], + [20.0, 16.0], + [24.0, 21.5], + [30.0, 25.6], + [36.0, 31.0], + [42.0, 34.0], + [48.0, 38.0], + [56.0, 45.0], + [64.0, 51.0] + ]); @@ -386,33 +386,33 @@ function get_metric_nut_thickness(size) = lookup(size, [ // screw(screwsize=3,screwlen=10,headsize=6,headlen=3) // show_anchors(5, std=false); module screw( - screwsize=3, - screwlen=10, - headsize=6, - headlen=3, - pitch=undef, - anchor="base", - spin=0, - orient=UP + screwsize=3, + screwlen=10, + headsize=6, + headlen=3, + pitch=undef, + anchor="base", + spin=0, + orient=UP ) { - sides = max(12, segs(screwsize/2)); - anchors = [ - anchorpt("countersunk", [0,0,(headlen+screwlen)/2-0.01]), - anchorpt("base", [0,0,-headlen/2+screwlen/2]) - ]; - attachable(anchor,spin,orient, d=screwsize, l=headlen+screwlen, anchors=anchors) { - down(headlen/2-screwlen/2) { - down(screwlen/2) { - if (pitch == undef) { - cylinder(r=screwsize/2, h=screwlen+0.05, center=true, $fn=sides); - } else { - threaded_rod(d=screwsize, l=screwlen+0.05, pitch=pitch, $fn=sides); - } - } - cylinder(r=headsize/2, h=headlen, center=false, $fn=sides*2); - } - children(); - } + sides = max(12, segs(screwsize/2)); + anchors = [ + anchorpt("countersunk", [0,0,(headlen+screwlen)/2-0.01]), + anchorpt("base", [0,0,-headlen/2+screwlen/2]) + ]; + attachable(anchor,spin,orient, d=screwsize, l=headlen+screwlen, anchors=anchors) { + down(headlen/2-screwlen/2) { + down(screwlen/2) { + if (pitch == undef) { + cylinder(r=screwsize/2, h=screwlen+0.05, center=true, $fn=sides); + } else { + threaded_rod(d=screwsize, l=screwlen+0.05, pitch=pitch, $fn=sides); + } + } + cylinder(r=headsize/2, h=headlen, center=false, $fn=sides*2); + } + children(); + } } @@ -477,150 +477,150 @@ module screw( // metric_bolt(headtype="oval", size=10, l=15, shank=5, details=true, phillips="#2") // show_anchors(5, std=false); module metric_bolt( - headtype="socket", - size=3, - l=12, - shank=0, - pitch=undef, - details=false, - coarse=true, - phillips=undef, - torx=undef, - flange=0, - anchor="base", - spin=0, - orient=UP + headtype="socket", + size=3, + l=12, + shank=0, + pitch=undef, + details=false, + coarse=true, + phillips=undef, + torx=undef, + flange=0, + anchor="base", + spin=0, + orient=UP ) { - D = headtype != "hex"? - get_metric_socket_cap_diam(size) : - get_metric_bolt_head_size(size); - H = headtype == "socket"? - get_metric_socket_cap_height(size) : - get_metric_bolt_head_height(size); - P = coarse? - (pitch==undef? get_metric_iso_coarse_thread_pitch(size) : pitch) : - (pitch==undef? get_metric_iso_fine_thread_pitch(size) : pitch); - tlen = l - min(l, shank); - sides = max(12, segs(size/2)); - tcirc = D/cos(30); - bevtop = (tcirc-D)/2; - bevbot = P/2; + D = headtype != "hex"? + get_metric_socket_cap_diam(size) : + get_metric_bolt_head_size(size); + H = headtype == "socket"? + get_metric_socket_cap_height(size) : + get_metric_bolt_head_height(size); + P = coarse? + (pitch==undef? get_metric_iso_coarse_thread_pitch(size) : pitch) : + (pitch==undef? get_metric_iso_fine_thread_pitch(size) : pitch); + tlen = l - min(l, shank); + sides = max(12, segs(size/2)); + tcirc = D/cos(30); + bevtop = (tcirc-D)/2; + bevbot = P/2; - headlen = ( - (headtype == "pan" || headtype == "round" || headtype == "button")? H*0.75 : - (headtype == "countersunk")? (D-size)/2 : - (headtype == "oval")? ((D-size)/2 + D/2/3) : - H - ); - base = l/2 - headlen/2; - sunklen = ( - (headtype == "oval")? (D-size)/2 : - headlen-0.001 - ); + headlen = ( + (headtype == "pan" || headtype == "round" || headtype == "button")? H*0.75 : + (headtype == "countersunk")? (D-size)/2 : + (headtype == "oval")? ((D-size)/2 + D/2/3) : + H + ); + base = l/2 - headlen/2; + sunklen = ( + (headtype == "oval")? (D-size)/2 : + headlen-0.001 + ); - anchors = [ - anchorpt("countersunk", [0,0,base+sunklen]), - anchorpt("base", [0,0,base]), - anchorpt("shank", [0,0,base-shank]) - ]; + anchors = [ + anchorpt("countersunk", [0,0,base+sunklen]), + anchorpt("base", [0,0,base]), + anchorpt("shank", [0,0,base-shank]) + ]; - //color("silver") - attachable(anchor,spin,orient, d=size, l=headlen+l, anchors=anchors) { - up(base) { - difference() { - union() { - // Head - if (headtype == "hex") { - difference() { - cylinder(d=tcirc, h=H, $fn=6); + //color("silver") + attachable(anchor,spin,orient, d=size, l=headlen+l, anchors=anchors) { + up(base) { + difference() { + union() { + // Head + if (headtype == "hex") { + difference() { + cylinder(d=tcirc, h=H, $fn=6); - // Bevel hex nut top - if (details) { - up(H-bevtop) { - difference() { - cube([tcirc+1, tcirc+1, bevtop+0.5], anchor=BOTTOM); - down(0.01) cylinder(d1=tcirc, d2=tcirc-bevtop*2, h=bevtop+0.02, center=false); - } - } - } - } - } else if (headtype == "socket") { - sockw = get_metric_socket_cap_socket_size(size); - sockd = get_metric_socket_cap_socket_depth(size); - difference() { - cylinder(d=D, h=H); - up(H-sockd) cylinder(h=sockd+0.1, d=sockw/cos(30), $fn=6); - if (details) { - kcnt = 36; - zrot_copies(n=kcnt, r=D/2) up(H/3) cube([PI*D/kcnt/2, PI*D/kcnt/2, H], anchor=BOTTOM); - } - } - } else if (headtype == "pan") { - cyl(l=H*0.75, d=D, rounding2=H*0.75/2, anchor=DOWN); - } else if (headtype == "round") { - top_half(D) zscale(H*0.75/D*2) sphere(d=D); - } else if (headtype == "button") { - up(H*0.75/3) top_half(D) zscale(H*0.75*2/3/D*2) sphere(d=D); - cylinder(d=D, h=H*0.75/3+0.01, center=false); - } else if (headtype == "countersunk") { - cylinder(h=(D-size)/2, d1=size, d2=D); - } else if (headtype == "oval") { - up((D-size)/2) top_half(D) zscale(0.333) sphere(d=D); - cylinder(h=(D-size)/2, d1=size, d2=D); - } + // Bevel hex nut top + if (details) { + up(H-bevtop) { + difference() { + cube([tcirc+1, tcirc+1, bevtop+0.5], anchor=BOTTOM); + down(0.01) cylinder(d1=tcirc, d2=tcirc-bevtop*2, h=bevtop+0.02, center=false); + } + } + } + } + } else if (headtype == "socket") { + sockw = get_metric_socket_cap_socket_size(size); + sockd = get_metric_socket_cap_socket_depth(size); + difference() { + cylinder(d=D, h=H); + up(H-sockd) cylinder(h=sockd+0.1, d=sockw/cos(30), $fn=6); + if (details) { + kcnt = 36; + zrot_copies(n=kcnt, r=D/2) up(H/3) cube([PI*D/kcnt/2, PI*D/kcnt/2, H], anchor=BOTTOM); + } + } + } else if (headtype == "pan") { + cyl(l=H*0.75, d=D, rounding2=H*0.75/2, anchor=DOWN); + } else if (headtype == "round") { + top_half(D) zscale(H*0.75/D*2) sphere(d=D); + } else if (headtype == "button") { + up(H*0.75/3) top_half(D) zscale(H*0.75*2/3/D*2) sphere(d=D); + cylinder(d=D, h=H*0.75/3+0.01, center=false); + } else if (headtype == "countersunk") { + cylinder(h=(D-size)/2, d1=size, d2=D); + } else if (headtype == "oval") { + up((D-size)/2) top_half(D) zscale(0.333) sphere(d=D); + cylinder(h=(D-size)/2, d1=size, d2=D); + } - // Flange - if (flange>0) { - up(headtype == "countersunk" || headtype == "oval"? (D-size)/2 : 0) { - cylinder(d=D+flange, h=H/8, center=false); - up(H/8) cylinder(d1=D+flange, d2=D, h=H/8, center=false); - } - } + // Flange + if (flange>0) { + up(headtype == "countersunk" || headtype == "oval"? (D-size)/2 : 0) { + cylinder(d=D+flange, h=H/8, center=false); + up(H/8) cylinder(d1=D+flange, d2=D, h=H/8, center=false); + } + } - // Unthreaded Shank - if (tlen < l) { - down(l-tlen) cylinder(d=size, h=l-tlen+0.05, center=false, $fn=sides); - } + // Unthreaded Shank + if (tlen < l) { + down(l-tlen) cylinder(d=size, h=l-tlen+0.05, center=false, $fn=sides); + } - // Threads - down(l) { - difference() { - up(tlen/2+0.05) { - if (tlen > 0) { - if (P > 0) { - threaded_rod(d=size, l=tlen+0.05, pitch=P, $fn=sides); - } else { - cylinder(d=size, h=tlen+0.05, $fn=sides, center=true); - } - } - } + // Threads + down(l) { + difference() { + up(tlen/2+0.05) { + if (tlen > 0) { + if (P > 0) { + threaded_rod(d=size, l=tlen+0.05, pitch=P, $fn=sides); + } else { + cylinder(d=size, h=tlen+0.05, $fn=sides, center=true); + } + } + } - // Bevel bottom end of threads - if (details) { - difference() { - down(0.5) cube([size+1, size+1, bevbot+0.5], anchor=BOTTOM); - cylinder(d1=size-bevbot*2, d2=size, h=bevbot+0.01, center=false); - } - } - } - } - } + // Bevel bottom end of threads + if (details) { + difference() { + down(0.5) cube([size+1, size+1, bevbot+0.5], anchor=BOTTOM); + cylinder(d1=size-bevbot*2, d2=size, h=bevbot+0.01, center=false); + } + } + } + } + } - // Phillips drive hole - if (headtype != "socket" && phillips != undef) { - down(headtype != "hex"? H/6 : 0) { - phillips_drive(size=phillips, shaft=D); - } - } + // Phillips drive hole + if (headtype != "socket" && phillips != undef) { + down(headtype != "hex"? H/6 : 0) { + phillips_drive(size=phillips, shaft=D); + } + } - // Torx drive hole - if (headtype != "socket" && torx != undef) { - up(1) torx_drive(size=torx, l=H+0.1, center=false); - } - } - } - children(); - } + // Torx drive hole + if (headtype != "socket" && torx != undef) { + up(1) torx_drive(size=torx, l=H+0.1, center=false); + } + } + } + children(); + } } @@ -650,65 +650,65 @@ module metric_bolt( // Example: Flange // metric_nut(size=10, hole=true, pitch=1.5, flange=3, details=true); module metric_nut( - size=3, - hole=true, - pitch=undef, - details=false, - flange=0, - center, - anchor, - spin=0, - orient=UP + size=3, + hole=true, + pitch=undef, + details=false, + flange=0, + center, + anchor, + spin=0, + orient=UP ) { - H = get_metric_nut_thickness(size); - D = get_metric_nut_size(size); - boltfn = max(12, segs(size/2)); - nutfn = max(12, segs(D/2)); - dcirc = D/cos(30); - bevtop = (dcirc - D)/2; + H = get_metric_nut_thickness(size); + D = get_metric_nut_size(size); + boltfn = max(12, segs(size/2)); + nutfn = max(12, segs(D/2)); + dcirc = D/cos(30); + bevtop = (dcirc - D)/2; - //color("silver") - anchor = get_anchor(anchor,center,BOT,CENTER); - attachable(anchor,spin,orient, d=dcirc+flange, l=H) { - difference() { - union() { - difference() { - cylinder(d=dcirc, h=H, center=true, $fn=6); - if (details) { - up(H/2-bevtop) { - difference() { - cube([dcirc+1, dcirc+1, bevtop+0.5], anchor=BOTTOM); - down(0.01) cylinder(d1=dcirc, d2=dcirc-bevtop*2, h=bevtop+0.02, center=false, $fn=nutfn); - } - } - if (flange == 0) { - down(H/2) { - difference() { - down(0.5) cube([dcirc+1, dcirc+1, bevtop+0.5], anchor=BOTTOM); - down(0.01) cylinder(d1=dcirc-bevtop*2, d2=dcirc, h=bevtop+0.02, center=false, $fn=nutfn); - } - } - } - } - } - if (flange>0) { - down(H/2) { - cylinder(d=D+flange, h=H/8, center=false); - up(H/8) cylinder(d1=D+flange, d2=D, h=H/8, center=false); - } - } - } - if (hole == true) { - if (pitch == undef) { - cylinder(r=size/2, h=H+0.5, center=true, $fn=boltfn); - } else { - threaded_rod(d=size, l=H+0.5, pitch=pitch, $fn=boltfn); - } - } - } - children(); - } + //color("silver") + anchor = get_anchor(anchor,center,BOT,CENTER); + attachable(anchor,spin,orient, d=dcirc+flange, l=H) { + difference() { + union() { + difference() { + cylinder(d=dcirc, h=H, center=true, $fn=6); + if (details) { + up(H/2-bevtop) { + difference() { + cube([dcirc+1, dcirc+1, bevtop+0.5], anchor=BOTTOM); + down(0.01) cylinder(d1=dcirc, d2=dcirc-bevtop*2, h=bevtop+0.02, center=false, $fn=nutfn); + } + } + if (flange == 0) { + down(H/2) { + difference() { + down(0.5) cube([dcirc+1, dcirc+1, bevtop+0.5], anchor=BOTTOM); + down(0.01) cylinder(d1=dcirc-bevtop*2, d2=dcirc, h=bevtop+0.02, center=false, $fn=nutfn); + } + } + } + } + } + if (flange>0) { + down(H/2) { + cylinder(d=D+flange, h=H/8, center=false); + up(H/8) cylinder(d1=D+flange, d2=D, h=H/8, center=false); + } + } + } + if (hole == true) { + if (pitch == undef) { + cylinder(r=size/2, h=H+0.5, center=true, $fn=boltfn); + } else { + threaded_rod(d=size, l=H+0.5, pitch=pitch, $fn=boltfn); + } + } + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/mutators.scad b/mutators.scad index a8619cc..b801e48 100644 --- a/mutators.scad +++ b/mutators.scad @@ -33,30 +33,30 @@ // half_of([1,1], planar=true) circle(d=50); module half_of(v=UP, cp, s=1000, planar=false) { - cp = is_vector(v,4)? assert(cp==undef, "Don't use cp with plane definition.") plane_normal(v) * v[3] : - is_vector(cp)? cp : - is_num(cp)? cp*unit(v) : - [0,0,0]; - v = is_vector(v,4)? plane_normal(v) : v; - if (cp != [0,0,0]) { - translate(cp) half_of(v=v, s=s, planar=planar) translate(-cp) children(); - } else if (planar) { - v = (v==UP)? BACK : (v==DOWN)? FWD : v; - ang = atan2(v.y, v.x); - difference() { - children(); - rotate(ang+90) { - back(s/2) square(s, center=true); - } - } - } else { - difference() { - children(); - rot(from=UP, to=-v) { - up(s/2) cube(s, center=true); - } - } - } + cp = is_vector(v,4)? assert(cp==undef, "Don't use cp with plane definition.") plane_normal(v) * v[3] : + is_vector(cp)? cp : + is_num(cp)? cp*unit(v) : + [0,0,0]; + v = is_vector(v,4)? plane_normal(v) : v; + if (cp != [0,0,0]) { + translate(cp) half_of(v=v, s=s, planar=planar) translate(-cp) children(); + } else if (planar) { + v = (v==UP)? BACK : (v==DOWN)? FWD : v; + ang = atan2(v.y, v.x); + difference() { + children(); + rotate(ang+90) { + back(s/2) square(s, center=true); + } + } + } else { + difference() { + children(); + rot(from=UP, to=-v) { + up(s/2) cube(s, center=true); + } + } + } } @@ -81,17 +81,17 @@ module half_of(v=UP, cp, s=1000, planar=false) // left_half(planar=true) circle(r=20); module left_half(s=1000, x=0, planar=false) { - dir = LEFT; - difference() { - children(); - translate([x,0,0]-dir*s/2) { - if (planar) { - square(s, center=true); - } else { - cube(s, center=true); - } - } - } + dir = LEFT; + difference() { + children(); + translate([x,0,0]-dir*s/2) { + if (planar) { + square(s, center=true); + } else { + cube(s, center=true); + } + } + } } @@ -117,17 +117,17 @@ module left_half(s=1000, x=0, planar=false) // right_half(planar=true) circle(r=20); module right_half(s=1000, x=0, planar=false) { - dir = RIGHT; - difference() { - children(); - translate([x,0,0]-dir*s/2) { - if (planar) { - square(s, center=true); - } else { - cube(s, center=true); - } - } - } + dir = RIGHT; + difference() { + children(); + translate([x,0,0]-dir*s/2) { + if (planar) { + square(s, center=true); + } else { + cube(s, center=true); + } + } + } } @@ -153,17 +153,17 @@ module right_half(s=1000, x=0, planar=false) // front_half(planar=true) circle(r=20); module front_half(s=1000, y=0, planar=false) { - dir = FWD; - difference() { - children(); - translate([0,y,0]-dir*s/2) { - if (planar) { - square(s, center=true); - } else { - cube(s, center=true); - } - } - } + dir = FWD; + difference() { + children(); + translate([0,y,0]-dir*s/2) { + if (planar) { + square(s, center=true); + } else { + cube(s, center=true); + } + } + } } @@ -189,17 +189,17 @@ module front_half(s=1000, y=0, planar=false) // back_half(planar=true) circle(r=20); module back_half(s=1000, y=0, planar=false) { - dir = BACK; - difference() { - children(); - translate([0,y,0]-dir*s/2) { - if (planar) { - square(s, center=true); - } else { - cube(s, center=true); - } - } - } + dir = BACK; + difference() { + children(); + translate([0,y,0]-dir*s/2) { + if (planar) { + square(s, center=true); + } else { + cube(s, center=true); + } + } + } } @@ -221,13 +221,13 @@ module back_half(s=1000, y=0, planar=false) // bottom_half(z=-10) sphere(r=20); module bottom_half(s=1000, z=0) { - dir = DOWN; - difference() { - children(); - translate([0,0,z]-dir*s/2) { - cube(s, center=true); - } - } + dir = DOWN; + difference() { + children(); + translate([0,0,z]-dir*s/2) { + cube(s, center=true); + } + } } @@ -249,13 +249,13 @@ module bottom_half(s=1000, z=0) // top_half(z=5) sphere(r=20); module top_half(s=1000, z=0) { - dir = UP; - difference() { - children(); - translate([0,0,z]-dir*s/2) { - cube(s, center=true); - } - } + dir = UP; + difference() { + children(); + translate([0,0,z]-dir*s/2) { + cube(s, center=true); + } + } } @@ -296,19 +296,19 @@ module top_half(s=1000, z=0) // } module chain_hull() { - union() { - if ($children == 1) { - children(); - } else if ($children > 1) { - for (i =[1:1:$children-1]) { - $idx = i; - hull() { - let($primary=true) children(i-1); - let($primary=false) children(i); - } - } - } - } + union() { + if ($children == 1) { + children(); + } else if ($children > 1) { + for (i =[1:1:$children-1]) { + $idx = i; + hull() { + let($primary=true) children(i-1); + let($primary=false) children(i); + } + } + } + } } @@ -341,35 +341,35 @@ module chain_hull() // cylindrical_extrude(or=40, ir=35, orient=BACK) // text(text="Hello World!", size=10, halign="center", valign="center"); module cylindrical_extrude(or, ir, od, id, size=1000, convexity=10, spin=0, orient=UP) { - assert(is_num(size) || is_vector(size,2)); - size = is_num(size)? [size,size] : size; - ir = get_radius(r=ir,d=id); - or = get_radius(r=or,d=od); - index_r = or; - circumf = 2 * PI * index_r; - width = min(size.x, circumf); - assert(width <= circumf, "Shape would more than completely wrap around."); - sides = segs(or); - step = circumf / sides; - steps = ceil(width / step); - rot(from=UP, to=orient) rot(spin) { - for (i=[0:1:steps-2]) { - x = (i+0.5-steps/2) * step; - zrot(360 * x / circumf) { - fwd(or*cos(180/sides)) { - xrot(-90) { - linear_extrude(height=or-ir, scale=[ir/or,1], center=false, convexity=convexity) { - yflip() - intersection() { - left(x) children(); - rect([quantup(step,pow(2,-15)),size.y],center=true); - } - } - } - } - } - } - } + assert(is_num(size) || is_vector(size,2)); + size = is_num(size)? [size,size] : size; + ir = get_radius(r=ir,d=id); + or = get_radius(r=or,d=od); + index_r = or; + circumf = 2 * PI * index_r; + width = min(size.x, circumf); + assert(width <= circumf, "Shape would more than completely wrap around."); + sides = segs(or); + step = circumf / sides; + steps = ceil(width / step); + rot(from=UP, to=orient) rot(spin) { + for (i=[0:1:steps-2]) { + x = (i+0.5-steps/2) * step; + zrot(360 * x / circumf) { + fwd(or*cos(180/sides)) { + xrot(-90) { + linear_extrude(height=or-ir, scale=[ir/or,1], center=false, convexity=convexity) { + yflip() + intersection() { + left(x) children(); + rect([quantup(step,pow(2,-15)),size.y],center=true); + } + } + } + } + } + } + } } @@ -397,12 +397,12 @@ module cylindrical_extrude(or, ir, od, id, size=1000, convexity=10, spin=0, orie // ir = Radius to round only inside (concave) corners to. Use instead of `r`. module round3d(r, or, ir, size=100) { - or = get_radius(r1=or, r=r, dflt=0); - ir = get_radius(r1=ir, r=r, dflt=0); - offset3d(or, size=size) - offset3d(-ir-or, size=size) - offset3d(ir, size=size) - children(); + or = get_radius(r1=or, r=r, dflt=0); + ir = get_radius(r1=ir, r=r, dflt=0); + offset3d(or, size=size) + offset3d(-ir-or, size=size) + offset3d(ir, size=size) + children(); } @@ -418,30 +418,30 @@ module round3d(r, or, ir, size=100) // size = Maximum size of object to be contracted, given as a scalar. Default: 100 // convexity = Max number of times a line could intersect the walls of the object. Default: 10 module offset3d(r=1, size=100, convexity=10) { - n = quant(max(8,segs(abs(r))),4); - if (r==0) { - children(); - } else if (r>0) { - render(convexity=convexity) - minkowski() { - children(); - sphere(r, $fn=n); - } - } else { - size2 = size * [1,1,1]; - size1 = size2 * 1.02; - render(convexity=convexity) - difference() { - cube(size2, center=true); - minkowski() { - difference() { - cube(size1, center=true); - children(); - } - sphere(-r, $fn=n); - } - } - } + n = quant(max(8,segs(abs(r))),4); + if (r==0) { + children(); + } else if (r>0) { + render(convexity=convexity) + minkowski() { + children(); + sphere(r, $fn=n); + } + } else { + size2 = size * [1,1,1]; + size1 = size2 * 1.02; + render(convexity=convexity) + difference() { + cube(size2, center=true); + minkowski() { + difference() { + cube(size1, center=true); + children(); + } + sphere(-r, $fn=n); + } + } + } } @@ -468,9 +468,9 @@ module offset3d(r=1, size=100, convexity=10) { // round2d(or=16,ir=8) {square([40,100], center=true); square([100,40], center=true);} module round2d(r, or, ir) { - or = get_radius(r1=or, r=r, dflt=0); - ir = get_radius(r1=ir, r=r, dflt=0); - offset(or) offset(-ir-or) offset(delta=ir,chamfer=true) children(); + or = get_radius(r1=or, r=r, dflt=0); + ir = get_radius(r1=ir, r=r, dflt=0); + offset(or) offset(-ir-or) offset(delta=ir,chamfer=true) children(); } @@ -496,19 +496,19 @@ module round2d(r, or, ir) // shell2d(8,or=16,ir=8,round=16,fill=8) {square([40,100], center=true); square([100,40], center=true);} module shell2d(thickness, or=0, ir=0, fill=0, round=0) { - thickness = is_num(thickness)? ( - thickness<0? [thickness,0] : [0,thickness] - ) : (thickness[0]>thickness[1])? ( - [thickness[1],thickness[0]] - ) : thickness; - difference() { - round2d(or=or,ir=ir) - offset(delta=thickness[1]) - children(); - round2d(or=fill,ir=round) - offset(delta=thickness[0]) - children(); - } + thickness = is_num(thickness)? ( + thickness<0? [thickness,0] : [0,thickness] + ) : (thickness[0]>thickness[1])? ( + [thickness[1],thickness[0]] + ) : thickness; + difference() { + round2d(or=or,ir=ir) + offset(delta=thickness[1]) + children(); + round2d(or=fill,ir=round) + offset(delta=thickness[0]) + children(); + } } @@ -534,13 +534,13 @@ module shell2d(thickness, or=0, ir=0, fill=0, round=0) // rgb = HSL(h=270,s=0.75,l=0.6); // color(rgb) cube(60, center=true); function HSL(h,s=1,l=0.5) = - let( - h=posmod(h,360) - ) [ - for (n=[0,8,4]) let( - k=(n+h/30)%12 - ) l - s*min(l,1-l)*max(min(k-3,9-k,1),-1) - ]; + let( + h=posmod(h,360) + ) [ + for (n=[0,8,4]) let( + k=(n+h/30)%12 + ) l - s*min(l,1-l)*max(min(k-3,9-k,1),-1) + ]; module HSL(h,s=1,l=0.5,a=1) color(HSL(h,s,l),a) children(); @@ -563,13 +563,13 @@ module HSL(h,s=1,l=0.5,a=1) color(HSL(h,s,l),a) children(); // rgb = HSV(h=270,s=0.75,v=0.9); // color(rgb) cube(60, center=true); function HSV(h,s=1,v=1) = - let( - h=posmod(h,360), - v2=v*(1-s), - r=lookup(h,[[0,v], [60,v], [120,v2], [240,v2], [300,v], [360,v]]), - g=lookup(h,[[0,v2], [60,v], [180,v], [240,v2], [360,v2]]), - b=lookup(h,[[0,v2], [120,v2], [180,v], [300,v], [360,v2]]) - ) [r,g,b]; + let( + h=posmod(h,360), + v2=v*(1-s), + r=lookup(h,[[0,v], [60,v], [120,v2], [240,v2], [300,v], [360,v]]), + g=lookup(h,[[0,v2], [60,v], [180,v], [240,v2], [360,v2]]), + b=lookup(h,[[0,v2], [120,v2], [180,v], [300,v], [360,v2]]) + ) [r,g,b]; module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children(); @@ -594,14 +594,14 @@ module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children(); // rainbow(rgn) stroke($item, closed=true); module rainbow(list, stride=1) { - ll = len(list); - huestep = 360 / ll; - hues = [for (i=[0:1:ll-1]) posmod(i*huestep+i*360/stride,360)]; - for($idx=idx(list)) { - $item = list[$idx]; - HSV(h=hues[$idx]) children(); - } + ll = len(list); + huestep = 360 / ll; + hues = [for (i=[0:1:ll-1]) posmod(i*huestep+i*360/stride,360)]; + for($idx=idx(list)) { + $item = list[$idx]; + HSV(h=hues[$idx]) children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/nema_steppers.scad b/nema_steppers.scad index a8b54bb..2244806 100644 --- a/nema_steppers.scad +++ b/nema_steppers.scad @@ -17,12 +17,12 @@ // Arguments: // size = The standard NEMA motor size. function nema_motor_width(size) = lookup(size, [ - [11.0, 28.2], - [14.0, 35.2], - [17.0, 42.3], - [23.0, 57.0], - [34.0, 86.0], - ]); + [11.0, 28.2], + [14.0, 35.2], + [17.0, 42.3], + [23.0, 57.0], + [34.0, 86.0], + ]); // Function: nema_motor_plinth_height() @@ -30,12 +30,12 @@ function nema_motor_width(size) = lookup(size, [ // Arguments: // size = The standard NEMA motor size. function nema_motor_plinth_height(size) = lookup(size, [ - [11.0, 1.5], - [14.0, 2.0], - [17.0, 2.0], - [23.0, 1.6], - [34.0, 2.03], - ]); + [11.0, 1.5], + [14.0, 2.0], + [17.0, 2.0], + [23.0, 1.6], + [34.0, 2.03], + ]); // Function: nema_motor_plinth_diam() @@ -43,12 +43,12 @@ function nema_motor_plinth_height(size) = lookup(size, [ // Arguments: // size = The standard NEMA motor size. function nema_motor_plinth_diam(size) = lookup(size, [ - [11.0, 22.0], - [14.0, 22.0], - [17.0, 22.0], - [23.0, 38.1], - [34.0, 73.0], - ]); + [11.0, 22.0], + [14.0, 22.0], + [17.0, 22.0], + [23.0, 38.1], + [34.0, 73.0], + ]); // Function: nema_motor_screw_spacing() @@ -56,12 +56,12 @@ function nema_motor_plinth_diam(size) = lookup(size, [ // Arguments: // size = The standard NEMA motor size. function nema_motor_screw_spacing(size) = lookup(size, [ - [11.0, 23.11], - [14.0, 26.0], - [17.0, 30.99], - [23.0, 47.14], - [34.0, 69.6], - ]); + [11.0, 23.11], + [14.0, 26.0], + [17.0, 30.99], + [23.0, 47.14], + [34.0, 69.6], + ]); // Function: nema_motor_screw_size() @@ -69,12 +69,12 @@ function nema_motor_screw_spacing(size) = lookup(size, [ // Arguments: // size = The standard NEMA motor size. function nema_motor_screw_size(size) = lookup(size, [ - [11.0, 2.6], - [14.0, 3.0], - [17.0, 3.0], - [23.0, 5.1], - [34.0, 5.5], - ]); + [11.0, 2.6], + [14.0, 3.0], + [17.0, 3.0], + [23.0, 5.1], + [34.0, 5.5], + ]); // Function: nema_motor_screw_depth() @@ -82,12 +82,12 @@ function nema_motor_screw_size(size) = lookup(size, [ // Arguments: // size = The standard NEMA motor size. function nema_motor_screw_depth(size) = lookup(size, [ - [11.0, 3.0], - [14.0, 4.5], - [17.0, 4.5], - [23.0, 4.8], - [34.0, 9.0], - ]); + [11.0, 3.0], + [14.0, 4.5], + [17.0, 4.5], + [23.0, 4.8], + [34.0, 9.0], + ]); // Section: Motor Models @@ -115,45 +115,45 @@ function nema_motor_screw_depth(size) = lookup(size, [ // nema11_stepper(); module nema11_stepper(h=24, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP) { - size = 11; - motor_width = nema_motor_width(size); - plinth_height = nema_motor_plinth_height(size); - plinth_diam = nema_motor_plinth_diam(size); - screw_spacing = nema_motor_screw_spacing(size); - screw_size = nema_motor_screw_size(size); - screw_depth = nema_motor_screw_depth(size); + size = 11; + motor_width = nema_motor_width(size); + plinth_height = nema_motor_plinth_height(size); + plinth_diam = nema_motor_plinth_diam(size); + screw_spacing = nema_motor_screw_spacing(size); + screw_size = nema_motor_screw_size(size); + screw_depth = nema_motor_screw_depth(size); - anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), - ]; - attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { - up(h/2) - union() { - difference() { - color([0.4, 0.4, 0.4]) - cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); - color("silver") - xcopies(screw_spacing) - ycopies(screw_spacing) - cyl(r=screw_size/2, h=screw_depth*2, $fn=max(12,segs(screw_size/2))); - } - color([0.6, 0.6, 0.6]) { - difference() { - cylinder(h=plinth_height, d=plinth_diam); - cyl(h=plinth_height*3, d=shaft+0.75); - } - } - color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); - } - children(); - } + anchors = [ + anchorpt("shaft-top", [0,0,h/2+shaft_len]), + anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + anchorpt("plinth-top", [0,0,h/2+plinth_height]), + anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + ]; + attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { + up(h/2) + union() { + difference() { + color([0.4, 0.4, 0.4]) + cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); + color("silver") + xcopies(screw_spacing) + ycopies(screw_spacing) + cyl(r=screw_size/2, h=screw_depth*2, $fn=max(12,segs(screw_size/2))); + } + color([0.6, 0.6, 0.6]) { + difference() { + cylinder(h=plinth_height, d=plinth_diam); + cyl(h=plinth_height*3, d=shaft+0.75); + } + } + color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); + } + children(); + } } @@ -180,45 +180,45 @@ module nema11_stepper(h=24, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP // nema14_stepper(); module nema14_stepper(h=24, shaft=5, shaft_len=24, anchor=TOP, spin=0, orient=UP) { - size = 14; - motor_width = nema_motor_width(size); - plinth_height = nema_motor_plinth_height(size); - plinth_diam = nema_motor_plinth_diam(size); - screw_spacing = nema_motor_screw_spacing(size); - screw_size = nema_motor_screw_size(size); - screw_depth = nema_motor_screw_depth(size); + size = 14; + motor_width = nema_motor_width(size); + plinth_height = nema_motor_plinth_height(size); + plinth_diam = nema_motor_plinth_diam(size); + screw_spacing = nema_motor_screw_spacing(size); + screw_size = nema_motor_screw_size(size); + screw_depth = nema_motor_screw_depth(size); - anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), - ]; - attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { - up(h/2) - union() { - difference() { - color([0.4, 0.4, 0.4]) - cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); - color("silver") - xcopies(screw_spacing) - ycopies(screw_spacing) - cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2))); - } - color([0.6, 0.6, 0.6]) { - difference() { - cylinder(h=plinth_height, d=plinth_diam); - cyl(h=plinth_height*3, d=shaft+0.75); - } - } - color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); - } - children(); - } + anchors = [ + anchorpt("shaft-top", [0,0,h/2+shaft_len]), + anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + anchorpt("plinth-top", [0,0,h/2+plinth_height]), + anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + ]; + attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { + up(h/2) + union() { + difference() { + color([0.4, 0.4, 0.4]) + cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); + color("silver") + xcopies(screw_spacing) + ycopies(screw_spacing) + cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2))); + } + color([0.6, 0.6, 0.6]) { + difference() { + cylinder(h=plinth_height, d=plinth_diam); + cyl(h=plinth_height*3, d=shaft+0.75); + } + } + color("silver") cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); + } + children(); + } } @@ -245,64 +245,64 @@ module nema14_stepper(h=24, shaft=5, shaft_len=24, anchor=TOP, spin=0, orient=UP // nema17_stepper(); module nema17_stepper(h=34, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP) { - size = 17; - motor_width = nema_motor_width(size); - plinth_height = nema_motor_plinth_height(size); - plinth_diam = nema_motor_plinth_diam(size); - screw_spacing = nema_motor_screw_spacing(size); - screw_size = nema_motor_screw_size(size); - screw_depth = nema_motor_screw_depth(size); + size = 17; + motor_width = nema_motor_width(size); + plinth_height = nema_motor_plinth_height(size); + plinth_diam = nema_motor_plinth_diam(size); + screw_spacing = nema_motor_screw_spacing(size); + screw_size = nema_motor_screw_size(size); + screw_depth = nema_motor_screw_depth(size); - anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), - ]; - attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { - up(h/2) - union() { - difference() { - color([0.4, 0.4, 0.4]) - cuboid([motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); - color("silver") - xcopies(screw_spacing) - ycopies(screw_spacing) - cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2))); - } - color([0.6, 0.6, 0.6]) { - difference() { - cylinder(h=plinth_height, d=plinth_diam); - cyl(h=plinth_height*3, d=shaft+0.75); - } - } - color([0.9, 0.9, 0.9]) { - down(h-motor_width/12) { - fwd(motor_width/2+motor_width/24/2-0.1) { - difference() { - cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true); - cyl(d=motor_width/8-2, h=motor_width/6, orient=BACK, $fn=12); - } - } - } - } - color("silver") { - difference() { - cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); - up(shaft_len/2+1) { - right(shaft-0.75) { - cube([shaft, shaft, shaft_len], center=true); - } - } - } - } - } - children(); - } + anchors = [ + anchorpt("shaft-top", [0,0,h/2+shaft_len]), + anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + anchorpt("plinth-top", [0,0,h/2+plinth_height]), + anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + ]; + attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { + up(h/2) + union() { + difference() { + color([0.4, 0.4, 0.4]) + cuboid([motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); + color("silver") + xcopies(screw_spacing) + ycopies(screw_spacing) + cyl(d=screw_size, h=screw_depth*2, $fn=max(12,segs(screw_size/2))); + } + color([0.6, 0.6, 0.6]) { + difference() { + cylinder(h=plinth_height, d=plinth_diam); + cyl(h=plinth_height*3, d=shaft+0.75); + } + } + color([0.9, 0.9, 0.9]) { + down(h-motor_width/12) { + fwd(motor_width/2+motor_width/24/2-0.1) { + difference() { + cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true); + cyl(d=motor_width/8-2, h=motor_width/6, orient=BACK, $fn=12); + } + } + } + } + color("silver") { + difference() { + cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); + up(shaft_len/2+1) { + right(shaft-0.75) { + cube([shaft, shaft, shaft_len], center=true); + } + } + } + } + } + children(); + } } @@ -329,47 +329,47 @@ module nema17_stepper(h=34, shaft=5, shaft_len=20, anchor=TOP, spin=0, orient=UP // nema23_stepper(); module nema23_stepper(h=50, shaft=6.35, shaft_len=25, anchor=TOP, spin=0, orient=UP) { - size = 23; - motor_width = nema_motor_width(size); - plinth_height = nema_motor_plinth_height(size); - plinth_diam = nema_motor_plinth_diam(size); - screw_spacing = nema_motor_screw_spacing(size); - screw_size = nema_motor_screw_size(size); - screw_depth = nema_motor_screw_depth(size); + size = 23; + motor_width = nema_motor_width(size); + plinth_height = nema_motor_plinth_height(size); + plinth_diam = nema_motor_plinth_diam(size); + screw_spacing = nema_motor_screw_spacing(size); + screw_size = nema_motor_screw_size(size); + screw_depth = nema_motor_screw_depth(size); - screw_inset = motor_width - screw_spacing + 1; - anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), - ]; - attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { - up(h/2) - difference() { - union() { - color([0.4, 0.4, 0.4]) - cuboid([motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); - color([0.4, 0.4, 0.4]) - cylinder(h=plinth_height, d=plinth_diam); - color("silver") - cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); - } - color([0.4, 0.4, 0.4]) { - xcopies(screw_spacing) { - ycopies(screw_spacing) { - cyl(d=screw_size, h=screw_depth*3, $fn=max(12,segs(screw_size/2))); - down(screw_depth) cuboid([screw_inset, screw_inset, h], anchor=TOP); - } - } - } - } - children(); - } + screw_inset = motor_width - screw_spacing + 1; + anchors = [ + anchorpt("shaft-top", [0,0,h/2+shaft_len]), + anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + anchorpt("plinth-top", [0,0,h/2+plinth_height]), + anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + ]; + attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { + up(h/2) + difference() { + union() { + color([0.4, 0.4, 0.4]) + cuboid([motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); + color([0.4, 0.4, 0.4]) + cylinder(h=plinth_height, d=plinth_diam); + color("silver") + cylinder(h=shaft_len, d=shaft, $fn=max(12,segs(shaft/2))); + } + color([0.4, 0.4, 0.4]) { + xcopies(screw_spacing) { + ycopies(screw_spacing) { + cyl(d=screw_size, h=screw_depth*3, $fn=max(12,segs(screw_size/2))); + down(screw_depth) cuboid([screw_inset, screw_inset, h], anchor=TOP); + } + } + } + } + children(); + } } @@ -396,47 +396,47 @@ module nema23_stepper(h=50, shaft=6.35, shaft_len=25, anchor=TOP, spin=0, orient // nema34_stepper(); module nema34_stepper(h=75, shaft=12.7, shaft_len=32, anchor=TOP, spin=0, orient=UP) { - size = 34; - motor_width = nema_motor_width(size); - plinth_height = nema_motor_plinth_height(size); - plinth_diam = nema_motor_plinth_diam(size); - screw_spacing = nema_motor_screw_spacing(size); - screw_size = nema_motor_screw_size(size); - screw_depth = nema_motor_screw_depth(size); + size = 34; + motor_width = nema_motor_width(size); + plinth_height = nema_motor_plinth_height(size); + plinth_diam = nema_motor_plinth_diam(size); + screw_spacing = nema_motor_screw_spacing(size); + screw_size = nema_motor_screw_size(size); + screw_depth = nema_motor_screw_depth(size); - screw_inset = motor_width - screw_spacing + 1; - anchors = [ - anchorpt("shaft-top", [0,0,h/2+shaft_len]), - anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), - anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), - anchorpt("plinth-top", [0,0,h/2+plinth_height]), - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), - ]; - attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { - up(h/2) - difference() { - union() { - color([0.4, 0.4, 0.4]) - cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); - color([0.4, 0.4, 0.4]) - cylinder(h=plinth_height, d=plinth_diam); - color("silver") - cylinder(h=shaft_len, d=shaft, $fn=max(24,segs(shaft/2))); - } - color([0.4, 0.4, 0.4]) { - xcopies(screw_spacing) { - ycopies(screw_spacing) { - cylinder(d=screw_size, h=screw_depth*3, center=true, $fn=max(12,segs(screw_size/2))); - down(screw_depth) cube([screw_inset, screw_inset, h], anchor=TOP); - } - } - } - } - children(); - } + screw_inset = motor_width - screw_spacing + 1; + anchors = [ + anchorpt("shaft-top", [0,0,h/2+shaft_len]), + anchorpt("shaft-middle", [0,0,h/2+plinth_height+(shaft_len-plinth_height)/2]), + anchorpt("shaft-bottom", [0,0,h/2+plinth_height+0.1]), + anchorpt("plinth-top", [0,0,h/2+plinth_height]), + anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, h/2]), + anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, h/2]), + anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, h/2]), + ]; + attachable(anchor,spin,orient, size=[motor_width, motor_width, h], anchors=anchors) { + up(h/2) + difference() { + union() { + color([0.4, 0.4, 0.4]) + cuboid(size=[motor_width, motor_width, h], chamfer=2, edges=edges("Z"), anchor=TOP); + color([0.4, 0.4, 0.4]) + cylinder(h=plinth_height, d=plinth_diam); + color("silver") + cylinder(h=shaft_len, d=shaft, $fn=max(24,segs(shaft/2))); + } + color([0.4, 0.4, 0.4]) { + xcopies(screw_spacing) { + ycopies(screw_spacing) { + cylinder(d=screw_size, h=screw_depth*3, center=true, $fn=max(12,segs(screw_size/2))); + down(screw_depth) cube([screw_inset, screw_inset, h], anchor=TOP); + } + } + } + } + children(); + } } @@ -468,45 +468,45 @@ module nema34_stepper(h=75, shaft=12.7, shaft_len=32, anchor=TOP, spin=0, orient // nema_mount_holes(size=17, depth=5, l=0); module nema_mount_holes(size=17, depth=5, l=5, anchor=CENTER, spin=0, orient=UP) { - motor_width = nema_motor_width(size); - plinth_diam = nema_motor_plinth_diam(size)+$slop; - screw_spacing = nema_motor_screw_spacing(size); - screw_size = nema_motor_screw_size(size)+$slop; + motor_width = nema_motor_width(size); + plinth_diam = nema_motor_plinth_diam(size)+$slop; + screw_spacing = nema_motor_screw_spacing(size); + screw_size = nema_motor_screw_size(size)+$slop; - anchors = [ - anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, depth/2]), - anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, depth/2]), - anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, depth/2]), - anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, depth/2]), - ]; - screwfn = quantup(max(8,segs(screw_size/2)),4); - plinthfn = quantup(max(8,segs(plinth_diam/2)),4); - s = [screw_spacing+screw_size, screw_spacing+screw_size+l, depth]; - attachable(anchor,spin,orient, size=s, anchors=anchors) { - union() { - xcopies(screw_spacing) { - ycopies(screw_spacing) { - if (l>0) { - union() { - ycopies(l) cyl(h=depth, d=screw_size, $fn=screwfn); - cube([screw_size, l, depth], center=true); - } - } else { - cyl(h=depth, d=screw_size, $fn=screwfn); - } - } - } - if (l>0) { - union () { - ycopies(l) cyl(h=depth, d=plinth_diam, $fn=plinthfn); - cube([plinth_diam, l, depth], center=true); - } - } else { - cyl(h=depth, d=plinth_diam, $fn=plinthfn); - } - } - children(); - } + anchors = [ + anchorpt("screw1", [+screw_spacing/2, +screw_spacing/2, depth/2]), + anchorpt("screw2", [-screw_spacing/2, +screw_spacing/2, depth/2]), + anchorpt("screw3", [-screw_spacing/2, -screw_spacing/2, depth/2]), + anchorpt("screw4", [+screw_spacing/2, -screw_spacing/2, depth/2]), + ]; + screwfn = quantup(max(8,segs(screw_size/2)),4); + plinthfn = quantup(max(8,segs(plinth_diam/2)),4); + s = [screw_spacing+screw_size, screw_spacing+screw_size+l, depth]; + attachable(anchor,spin,orient, size=s, anchors=anchors) { + union() { + xcopies(screw_spacing) { + ycopies(screw_spacing) { + if (l>0) { + union() { + ycopies(l) cyl(h=depth, d=screw_size, $fn=screwfn); + cube([screw_size, l, depth], center=true); + } + } else { + cyl(h=depth, d=screw_size, $fn=screwfn); + } + } + } + if (l>0) { + union () { + ycopies(l) cyl(h=depth, d=plinth_diam, $fn=plinthfn); + cube([plinth_diam, l, depth], center=true); + } + } else { + cyl(h=depth, d=plinth_diam, $fn=plinthfn); + } + } + children(); + } } @@ -531,7 +531,7 @@ module nema_mount_holes(size=17, depth=5, l=5, anchor=CENTER, spin=0, orient=UP) // nema11_mount_holes(depth=5, l=0); module nema11_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) { - nema_mount_holes(size=11, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); + nema_mount_holes(size=11, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); } @@ -556,7 +556,7 @@ module nema11_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) // nema14_mount_holes(depth=5, l=0); module nema14_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) { - nema_mount_holes(size=14, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); + nema_mount_holes(size=14, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); } @@ -581,7 +581,7 @@ module nema14_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) // nema17_mount_holes(depth=5, l=0); module nema17_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) { - nema_mount_holes(size=17, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); + nema_mount_holes(size=17, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); } @@ -606,7 +606,7 @@ module nema17_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) // nema23_mount_holes(depth=5, l=0); module nema23_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) { - nema_mount_holes(size=23, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); + nema_mount_holes(size=23, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); } @@ -631,9 +631,9 @@ module nema23_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) // nema34_mount_holes(depth=5, l=0); module nema34_mount_holes(depth=5, l=5, anchor=CENTER, spin=0, orient=UP) { - nema_mount_holes(size=34, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); + nema_mount_holes(size=34, depth=depth, l=l, anchor=anchor, spin=spin, orient=orient) children(); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/partitions.scad b/partitions.scad index ef3c4fd..534219b 100644 --- a/partitions.scad +++ b/partitions.scad @@ -13,39 +13,39 @@ _partition_cutpaths = [ - ["flat", [[0,0],[1,0]]], - ["sawtooth", [[0,-0.5], [0.5,0.5], [1,-0.5]]], - ["sinewave", [for (a=[0:5:360]) [a/360,sin(a)/2]]], - ["comb", let(dx=0.5*sin(2)) [[0,0],[0+dx,0.5],[0.5-dx,0.5],[0.5+dx,-0.5],[1-dx,-0.5],[1,0]]], - ["finger", let(dx=0.5*sin(20)) [[0,0],[0+dx,0.5],[0.5-dx,0.5],[0.5+dx,-0.5],[1-dx,-0.5],[1,0]]], - ["dovetail", [[0,-0.5], [0.3,-0.5], [0.2,0.5], [0.8,0.5], [0.7,-0.5], [1,-0.5]]], - ["hammerhead", [[0,-0.5], [0.35,-0.5], [0.35,0], [0.15,0], [0.15,0.5], [0.85,0.5], [0.85,0], [0.65,0], [0.65,-0.5],[1,-0.5]]], - ["jigsaw", concat( - arc(N=6, r=5/16, cp=[0,-3/16], start=270, angle=125), - arc(N=12, r=5/16, cp=[1/2,3/16], start=215, angle=-250), - arc(N=6, r=5/16, cp=[1,-3/16], start=145, angle=125) - ) - ], + ["flat", [[0,0],[1,0]]], + ["sawtooth", [[0,-0.5], [0.5,0.5], [1,-0.5]]], + ["sinewave", [for (a=[0:5:360]) [a/360,sin(a)/2]]], + ["comb", let(dx=0.5*sin(2)) [[0,0],[0+dx,0.5],[0.5-dx,0.5],[0.5+dx,-0.5],[1-dx,-0.5],[1,0]]], + ["finger", let(dx=0.5*sin(20)) [[0,0],[0+dx,0.5],[0.5-dx,0.5],[0.5+dx,-0.5],[1-dx,-0.5],[1,0]]], + ["dovetail", [[0,-0.5], [0.3,-0.5], [0.2,0.5], [0.8,0.5], [0.7,-0.5], [1,-0.5]]], + ["hammerhead", [[0,-0.5], [0.35,-0.5], [0.35,0], [0.15,0], [0.15,0.5], [0.85,0.5], [0.85,0], [0.65,0], [0.65,-0.5],[1,-0.5]]], + ["jigsaw", concat( + arc(N=6, r=5/16, cp=[0,-3/16], start=270, angle=125), + arc(N=12, r=5/16, cp=[1/2,3/16], start=215, angle=-250), + arc(N=6, r=5/16, cp=[1,-3/16], start=145, angle=125) + ) + ], ]; function _partition_cutpath(l, h, cutsize, cutpath, gap) = - let( - cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize], - cutpath = is_path(cutpath)? cutpath : ( - assert(is_string(cutpath), "cutpath must be a 2D path or a string.") - let(idx = search([cutpath], _partition_cutpaths)) - idx==[[]]? assert(in_list(cutpath,_partition_cutpaths,idx=0)) : - _partition_cutpaths[idx.x][1] - ), - reps = ceil(l/(cutsize.x+gap)), - cplen = (cutsize.x+gap) * reps, - path = deduplicate(concat( - [[-l/2, cutpath[0].y*cutsize.y]], - [for (i=[0:1:reps-1], pt=cutpath) vmul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]], - [[ l/2, cutpath[len(cutpath)-1].y*cutsize.y]] - )) - ) path; + let( + cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize], + cutpath = is_path(cutpath)? cutpath : ( + assert(is_string(cutpath), "cutpath must be a 2D path or a string.") + let(idx = search([cutpath], _partition_cutpaths)) + idx==[[]]? assert(in_list(cutpath,_partition_cutpaths,idx=0)) : + _partition_cutpaths[idx.x][1] + ), + reps = ceil(l/(cutsize.x+gap)), + cplen = (cutsize.x+gap) * reps, + path = deduplicate(concat( + [[-l/2, cutpath[0].y*cutsize.y]], + [for (i=[0:1:reps-1], pt=cutpath) vmul(pt,cutsize)+[i*(cutsize.x+gap)+gap/2-cplen/2,0]], + [[ l/2, cutpath[len(cutpath)-1].y*cutsize.y]] + )) + ) path; // Module: partition_mask() @@ -79,16 +79,16 @@ function _partition_cutpath(l, h, cutsize, cutpath, gap) = // partition_mask(w=20, cutpath="jigsaw"); module partition_mask(l=100, w=100, h=100, cutsize=10, cutpath=undef, gap=0, inverse=false, spin=0, orient=UP) { - cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; - path = _partition_cutpath(l, h, cutsize, cutpath, gap); - fullpath = concat(path, [[l/2,w*(inverse?-1:1)], [-l/2,w*(inverse?-1:1)]]); - rot(from=UP,to=orient) { - rotate(spin) { - linear_extrude(height=h, convexity=10) { - offset(delta=-$slop) polygon(fullpath); - } - } - } + cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; + path = _partition_cutpath(l, h, cutsize, cutpath, gap); + fullpath = concat(path, [[l/2,w*(inverse?-1:1)], [-l/2,w*(inverse?-1:1)]]); + rot(from=UP,to=orient) { + rotate(spin) { + linear_extrude(height=h, convexity=10) { + offset(delta=-$slop) polygon(fullpath); + } + } + } } @@ -121,15 +121,15 @@ module partition_mask(l=100, w=100, h=100, cutsize=10, cutpath=undef, gap=0, inv // partition_cut_mask(cutpath="jigsaw"); module partition_cut_mask(l=100, h=100, cutsize=10, cutpath=undef, gap=0, spin=0, orient=UP) { - cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; - path = _partition_cutpath(l, h, cutsize, cutpath, gap); - rot(from=UP,to=orient) { - rotate(spin) { - linear_extrude(height=h, convexity=10) { - stroke(path, width=$slop*2); - } - } - } + cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; + path = _partition_cutpath(l, h, cutsize, cutpath, gap); + rot(from=UP,to=orient) { + rotate(spin) { + linear_extrude(height=h, convexity=10) { + stroke(path, width=$slop*2); + } + } + } } @@ -160,24 +160,24 @@ module partition_cut_mask(l=100, h=100, cutsize=10, cutpath=undef, gap=0, spin=0 // partition(cutpath="jigsaw") cylinder(h=50, d=80, center=false); module partition(size=100, spread=10, cutsize=10, cutpath=undef, gap=0, spin=0) { - size = is_vector(size)? size : [size,size,size]; - cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; - rsize = vabs(rot(spin,p=size)); - vec = rot(spin,p=BACK)*spread/2; - move(vec) { - intersection() { - children(); - partition_mask(l=rsize.x, w=rsize.y, h=rsize.z, cutsize=cutsize, cutpath=cutpath, gap=gap, spin=spin); - } - } - move(-vec) { - intersection() { - children(); - partition_mask(l=rsize.x, w=rsize.y, h=rsize.z, cutsize=cutsize, cutpath=cutpath, gap=gap, inverse=true, spin=spin); - } - } + size = is_vector(size)? size : [size,size,size]; + cutsize = is_vector(cutsize)? cutsize : [cutsize*2, cutsize]; + rsize = vabs(rot(spin,p=size)); + vec = rot(spin,p=BACK)*spread/2; + move(vec) { + intersection() { + children(); + partition_mask(l=rsize.x, w=rsize.y, h=rsize.z, cutsize=cutsize, cutpath=cutpath, gap=gap, spin=spin); + } + } + move(-vec) { + intersection() { + children(); + partition_mask(l=rsize.x, w=rsize.y, h=rsize.z, cutsize=cutsize, cutpath=cutpath, gap=gap, inverse=true, spin=spin); + } + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/paths.scad b/paths.scad index 34a1f55..ad8b732 100644 --- a/paths.scad +++ b/paths.scad @@ -43,10 +43,10 @@ include // dim = list of allowed dimensions of the vectors in the path. Default: [2,3] // fast = set to true for fast check that only looks at first entry. Default: false function is_path(list, dim=[2,3], fast=false) = - fast? is_list(list) && is_vector(list[0],fast=true) : - is_list(list) && is_list(list[0]) && len(list)>1 && - (is_undef(dim) || in_list(len(list[0]), force_list(dim))) && - is_list_of(list, repeat(0,len(list[0]))); + fast? is_list(list) && is_vector(list[0],fast=true) : + is_list(list) && is_list(list[0]) && len(list)>1 && + (is_undef(dim) || in_list(len(list[0]), force_list(dim))) && + is_list_of(list, repeat(0,len(list[0]))); // Function: is_closed_path() @@ -88,19 +88,19 @@ function cleanup_path(path, eps=EPSILON) = is_closed_path(path,eps=eps)? select( // u2 = The proportion along the ending segment, between 0.0 and 1.0, inclusive. // closed = If true, treat path as a closed polygon. function path_subselect(path, s1, u1, s2, u2, closed=false) = - let( - lp = len(path), - l = lp-(closed?0:1), - u1 = s1<0? 0 : s1>l? 1 : u1, - u2 = s2<0? 0 : s2>l? 1 : u2, - s1 = constrain(s1,0,l), - s2 = constrain(s2,0,l), - pathout = concat( - (s10)? [lerp(path[s2],path[(s2+1)%lp],u2)] : [] - ) - ) pathout; + let( + lp = len(path), + l = lp-(closed?0:1), + u1 = s1<0? 0 : s1>l? 1 : u1, + u2 = s2<0? 0 : s2>l? 1 : u2, + s1 = constrain(s1,0,l), + s2 = constrain(s2,0,l), + pathout = concat( + (s10)? [lerp(path[s2],path[(s2+1)%lp],u2)] : [] + ) + ) pathout; // Function: simplify_path() @@ -112,9 +112,9 @@ function path_subselect(path, s1, u1, s2, u2, closed=false) = // path = A list of 2D path points. // eps = Largest positional variance allowed. Default: `EPSILON` (1-e9) function simplify_path(path, eps=EPSILON) = - len(path)<=2? path : let( - indices = concat([0], [for (i=[1:1:len(path)-2]) if (!collinear_indexed(path, i-1, i, i+1, eps=eps)) i], [len(path)-1]) - ) [for (i = indices) path[i]]; + len(path)<=2? path : let( + indices = concat([0], [for (i=[1:1:len(path)-2]) if (!collinear_indexed(path, i-1, i, i+1, eps=eps)) i], [len(path)-1]) + ) [for (i = indices) path[i]]; // Function: simplify_path_indexed() @@ -128,9 +128,9 @@ function simplify_path(path, eps=EPSILON) = // path = A list of indices into `points` that forms a path. // eps = Largest angle variance allowed. Default: EPSILON (1-e9) degrees. function simplify_path_indexed(points, path, eps=EPSILON) = - len(path)<=2? path : let( - indices = concat([0], [for (i=[1:1:len(path)-2]) if (!collinear_indexed(points, path[i-1], path[i], path[i+1], eps=eps)) i], [len(path)-1]) - ) [for (i = indices) path[i]]; + len(path)<=2? path : let( + indices = concat([0], [for (i=[1:1:len(path)-2]) if (!collinear_indexed(points, path[i-1], path[i], path[i+1], eps=eps)) i], [len(path)-1]) + ) [for (i = indices) path[i]]; // Function: path_length() @@ -145,8 +145,8 @@ function simplify_path_indexed(points, path, eps=EPSILON) = // path = [[0,0], [5,35], [60,-25], [80,0]]; // echo(path_length(path)); function path_length(path,closed=false) = - len(path)<2? 0 : - sum([for (i = [0:1:len(path)-2]) norm(path[i+1]-path[i])])+(closed?norm(path[len(path)-1]-path[0]):0); + len(path)<2? 0 : + sum([for (i = [0:1:len(path)-2]) norm(path[i+1]-path[i])])+(closed?norm(path[len(path)-1]-path[0]):0); // Function: path_pos_from_start() @@ -167,11 +167,11 @@ function path_length(path,closed=false) = // pt = lerp(path[pos[0]], path[(pos[0]+1)%len(path)], pos[1]); // color("red") translate(pt) circle(d=2,$fn=12); function path_pos_from_start(path,length,closed=false,_d=0,_i=0) = - let (lp = len(path)) - _i >= lp - (closed?0:1)? undef : - let (l = norm(path[(_i+1)%lp]-path[_i])) - _d+l <= length? path_pos_from_start(path,length,closed,_d+l,_i+1) : - [_i, (length-_d)/l]; + let (lp = len(path)) + _i >= lp - (closed?0:1)? undef : + let (l = norm(path[(_i+1)%lp]-path[_i])) + _d+l <= length? path_pos_from_start(path,length,closed,_d+l,_i+1) : + [_i, (length-_d)/l]; // Function: path_pos_from_end() @@ -192,14 +192,14 @@ function path_pos_from_start(path,length,closed=false,_d=0,_i=0) = // pt = lerp(path[pos[0]], path[(pos[0]+1)%len(path)], pos[1]); // color("red") translate(pt) circle(d=2,$fn=12); function path_pos_from_end(path,length,closed=false,_d=0,_i=undef) = - let ( - lp = len(path), - _i = _i!=undef? _i : lp - (closed?1:2) - ) - _i < 0? undef : - let (l = norm(path[(_i+1)%lp]-path[_i])) - _d+l <= length? path_pos_from_end(path,length,closed,_d+l,_i-1) : - [_i, 1-(length-_d)/l]; + let ( + lp = len(path), + _i = _i!=undef? _i : lp - (closed?1:2) + ) + _i < 0? undef : + let (l = norm(path[(_i+1)%lp]-path[_i])) + _d+l <= length? path_pos_from_end(path,length,closed,_d+l,_i-1) : + [_i, 1-(length-_d)/l]; // Function: path_trim_start() @@ -218,14 +218,14 @@ function path_pos_from_end(path,length,closed=false,_d=0,_i=undef) = // color("cyan") stroke(path2,width=3,endcaps=false); // color("red") stroke(path,width=1,endcaps=false); function path_trim_start(path,trim,_d=0,_i=0) = - _i >= len(path)-1? [] : - let (l = norm(path[_i+1]-path[_i])) - _d+l <= trim? path_trim_start(path,trim,_d+l,_i+1) : - let (v = unit(path[_i+1]-path[_i])) - concat( - [path[_i+1]-v*(l-(trim-_d))], - [for (i=[_i+1:1:len(path)-1]) path[i]] - ); + _i >= len(path)-1? [] : + let (l = norm(path[_i+1]-path[_i])) + _d+l <= trim? path_trim_start(path,trim,_d+l,_i+1) : + let (v = unit(path[_i+1]-path[_i])) + concat( + [path[_i+1]-v*(l-(trim-_d))], + [for (i=[_i+1:1:len(path)-1]) path[i]] + ); // Function: path_trim_end() @@ -244,15 +244,15 @@ function path_trim_start(path,trim,_d=0,_i=0) = // color("cyan") stroke(path2,width=3,endcaps=false); // color("red") stroke(path,width=1,endcaps=false); function path_trim_end(path,trim,_d=0,_i=undef) = - let (_i = _i!=undef? _i : len(path)-1) - _i <= 0? [] : - let (l = norm(path[_i]-path[_i-1])) - _d+l <= trim? path_trim_end(path,trim,_d+l,_i-1) : - let (v = unit(path[_i]-path[_i-1])) - concat( - [for (i=[0:1:_i-1]) path[i]], - [path[_i-1]+v*(l-(trim-_d))] - ); + let (_i = _i!=undef? _i : len(path)-1) + _i <= 0? [] : + let (l = norm(path[_i]-path[_i-1])) + _d+l <= trim? path_trim_end(path,trim,_d+l,_i-1) : + let (v = unit(path[_i]-path[_i-1])) + concat( + [for (i=[0:1:_i-1]) path[i]], + [path[_i-1]+v*(l-(trim-_d))] + ); // Function: path_closest_point() @@ -272,11 +272,11 @@ function path_trim_end(path,trim,_d=0,_i=undef) = // color("blue") translate(pt) circle(d=3, $fn=12); // color("red") translate(closest[1]) circle(d=3, $fn=12); function path_closest_point(path, pt) = - let( - pts = [for (seg=idx(path)) segment_closest_point(select(path,seg,seg+1),pt)], - dists = [for (p=pts) norm(p-pt)], - min_seg = min_index(dists) - ) [min_seg, pts[min_seg]]; + let( + pts = [for (seg=idx(path)) segment_closest_point(select(path,seg,seg+1),pt)], + dists = [for (p=pts) norm(p-pt)], + min_seg = min_index(dists) + ) [min_seg, pts[min_seg]]; // Function: path_tangents() @@ -285,8 +285,8 @@ function path_closest_point(path, pt) = // Compute the tangent vector to the input path. The derivative approximation is described in deriv(). // The returns vectors will be normalized to length 1. function path_tangents(path, closed=false) = - assert(is_path(path)) - [for(t=deriv(path,closed=closed)) unit(t)]; + assert(is_path(path)) + [for(t=deriv(path,closed=closed)) unit(t)]; // Function: path_normals() @@ -296,20 +296,20 @@ function path_tangents(path, closed=false) = // path tangent and lies in the plane of the curve. When there are collinear points, // the curve does not define a unique plane and the normal is not uniquely defined. function path_normals(path, tangents, closed=false) = - assert(is_path(path)) - assert(is_bool(closed)) - let( tangents = default(tangents, path_tangents(path,closed)) ) - assert(is_path(tangents)) - [ - for(i=idx(path)) let( - pts = i==0? (closed? select(path,-1,1) : select(path,0,2)) : - i==len(path)-1? (closed? select(path,i-1,i+1) : select(path,i-2,i)) : - select(path,i-1,i+1) - ) unit(cross( - cross(pts[1]-pts[0], pts[2]-pts[0]), - tangents[i] - )) - ]; + assert(is_path(path)) + assert(is_bool(closed)) + let( tangents = default(tangents, path_tangents(path,closed)) ) + assert(is_path(tangents)) + [ + for(i=idx(path)) let( + pts = i==0? (closed? select(path,-1,1) : select(path,0,2)) : + i==len(path)-1? (closed? select(path,i-1,i+1) : select(path,i-2,i)) : + select(path,i-1,i+1) + ) unit(cross( + cross(pts[1]-pts[0], pts[2]-pts[0]), + tangents[i] + )) + ]; // Function: path_curvature() @@ -317,16 +317,16 @@ function path_normals(path, tangents, closed=false) = // Description: // Numerically estimate the curvature of the path (in any dimension). function path_curvature(path, closed=false) = - let( - d1 = deriv(path, closed=closed), - d2 = deriv2(path, closed=closed) - ) [ - for(i=idx(path)) - sqrt( - sqr(norm(d1[i])*norm(d2[i])) - - sqr(d1[i]*d2[i]) - ) / pow(norm(d1[i]),3) - ]; + let( + d1 = deriv(path, closed=closed), + d2 = deriv2(path, closed=closed) + ) [ + for(i=idx(path)) + sqrt( + sqr(norm(d1[i])*norm(d2[i])) - + sqr(d1[i]*d2[i]) + ) / pow(norm(d1[i]),3) + ]; // Function: path_torsion() @@ -334,15 +334,15 @@ function path_curvature(path, closed=false) = // Description: // Numerically estimate the torsion of a 3d path. function path_torsion(path, closed=false) = - let( - d1 = deriv(path,closed=closed), - d2 = deriv2(path,closed=closed), - d3 = deriv3(path,closed=closed) - ) [ - for (i=idx(path)) let( - crossterm = cross(d1[i],d2[i]) - ) crossterm * d3[i] / sqr(norm(crossterm)) - ]; + let( + d1 = deriv(path,closed=closed), + d2 = deriv2(path,closed=closed), + d3 = deriv3(path,closed=closed) + ) [ + for (i=idx(path)) let( + crossterm = cross(d1[i],d2[i]) + ) crossterm * d3[i] / sqr(norm(crossterm)) + ]; // Function: path3d_spiral() @@ -361,16 +361,16 @@ function path_torsion(path, closed=false) = // Example(3D): // trace_polyline(path3d_spiral(turns=2.5, h=100, n=24, r=50), N=1, showpts=true); function path3d_spiral(turns=3, h=100, n=12, r=undef, d=undef, cp=[0,0], scale=[1,1]) = let( - rr=get_radius(r=r, d=d, dflt=100), - cnt=floor(turns*n), - dz=h/cnt - ) [ - for (i=[0:1:cnt]) [ - rr * cos(i*360/n) * scale.x + cp.x, - rr * sin(i*360/n) * scale.y + cp.y, - i*dz - ] - ]; + rr=get_radius(r=r, d=d, dflt=100), + cnt=floor(turns*n), + dz=h/cnt + ) [ + for (i=[0:1:cnt]) [ + rr * cos(i*360/n) * scale.x + cp.x, + rr * sin(i*360/n) * scale.y + cp.y, + i*dz + ] + ]; // Function: points_along_path3d() @@ -388,25 +388,25 @@ function path3d_spiral(turns=3, h=100, n=12, r=undef, d=undef, cp=[0,0], scale=[ // polyline = A closed list of 2D path points. // path = A list of 3D path points. function points_along_path3d( - polyline, // The 2D polyline to drag along the 3D path. - path, // The 3D polyline path to follow. - q=Q_Ident(), // Used in recursion - n=0 // Used in recursion + polyline, // The 2D polyline to drag along the 3D path. + path, // The 3D polyline path to follow. + q=Q_Ident(), // Used in recursion + n=0 // Used in recursion ) = let( - end = len(path)-1, - v1 = (n == 0)? [0, 0, 1] : unit(path[n]-path[n-1]), - v2 = (n == end)? unit(path[n]-path[n-1]) : unit(path[n+1]-path[n]), - crs = cross(v1, v2), - axis = norm(crs) <= 0.001? [0, 0, 1] : crs, - ang = vector_angle(v1, v2), - hang = ang * (n==0? 1.0 : 0.5), - hrot = Quat(axis, hang), - arot = Quat(axis, ang), - roth = Q_Mul(hrot, q), - rotm = Q_Mul(arot, q) + end = len(path)-1, + v1 = (n == 0)? [0, 0, 1] : unit(path[n]-path[n-1]), + v2 = (n == end)? unit(path[n]-path[n-1]) : unit(path[n+1]-path[n]), + crs = cross(v1, v2), + axis = norm(crs) <= 0.001? [0, 0, 1] : crs, + ang = vector_angle(v1, v2), + hang = ang * (n==0? 1.0 : 0.5), + hrot = Quat(axis, hang), + arot = Quat(axis, ang), + roth = Q_Mul(hrot, q), + rotm = Q_Mul(arot, q) ) concat( - [for (i = [0:1:len(polyline)-1]) Qrot(roth,p=point3d(polyline[i])) + path[n]], - (n == end)? [] : points_along_path3d(polyline, path, rotm, n+1) + [for (i = [0:1:len(polyline)-1]) Qrot(roth,p=point3d(polyline[i])) + path[n]], + (n == end)? [] : points_along_path3d(polyline, path, rotm, n+1) ); @@ -434,35 +434,35 @@ function points_along_path3d( // stroke(path, closed=true, width=1); // for (isect=isects) translate(isect[0]) color("blue") sphere(d=10); function path_self_intersections(path, closed=true, eps=EPSILON) = - let( - path = cleanup_path(path, eps=eps), - plen = len(path) - ) [ - for (i = [0:1:plen-(closed?2:3)], j=[i+1:1:plen-(closed?1:2)]) let( - a1 = path[i], - a2 = path[(i+1)%plen], - b1 = path[j], - b2 = path[(j+1)%plen], - isect = - (max(a1.x, a2.x) < min(b1.x, b2.x))? undef : - (min(a1.x, a2.x) > max(b1.x, b2.x))? undef : - (max(a1.y, a2.y) < min(b1.y, b2.y))? undef : - (min(a1.y, a2.y) > max(b1.y, b2.y))? undef : - let( - c = a1-a2, - d = b1-b2, - denom = (c.x*d.y)-(c.y*d.x) - ) abs(denom)eps && isect[1]<=1+eps && - isect[2]>eps && isect[2]<=1+eps - ) [isect[0], i, isect[1], j, isect[2]] - ]; + let( + path = cleanup_path(path, eps=eps), + plen = len(path) + ) [ + for (i = [0:1:plen-(closed?2:3)], j=[i+1:1:plen-(closed?1:2)]) let( + a1 = path[i], + a2 = path[(i+1)%plen], + b1 = path[j], + b2 = path[(j+1)%plen], + isect = + (max(a1.x, a2.x) < min(b1.x, b2.x))? undef : + (min(a1.x, a2.x) > max(b1.x, b2.x))? undef : + (max(a1.y, a2.y) < min(b1.y, b2.y))? undef : + (min(a1.y, a2.y) > max(b1.y, b2.y))? undef : + let( + c = a1-a2, + d = b1-b2, + denom = (c.x*d.y)-(c.y*d.x) + ) abs(denom)eps && isect[1]<=1+eps && + isect[2]>eps && isect[2]<=1+eps + ) [isect[0], i, isect[1], j, isect[2]] + ]; // Function: split_path_at_self_crossings() @@ -480,52 +480,52 @@ function path_self_intersections(path, closed=true, eps=EPSILON) = // polylines = split_path_at_self_crossings(path); // rainbow(polylines) stroke($item, closed=false, width=2); function split_path_at_self_crossings(path, closed=true, eps=EPSILON) = - let( - path = cleanup_path(path, eps=eps), - isects = deduplicate( - eps=eps, - concat( - [[0, 0]], - sort([ - for ( - a = path_self_intersections(path, closed=closed, eps=eps), - ss = [ [a[1],a[2]], [a[3],a[4]] ] - ) if (ss[0] != undef) ss - ]), - [[len(path)-(closed?1:2), 1]] - ) - ) - ) [ - for (p = pair(isects)) - let( - s1 = p[0][0], - u1 = p[0][1], - s2 = p[1][0], - u2 = p[1][1], - section = path_subselect(path, s1, u1, s2, u2, closed=closed), - outpath = deduplicate(eps=eps, section) - ) - outpath - ]; + let( + path = cleanup_path(path, eps=eps), + isects = deduplicate( + eps=eps, + concat( + [[0, 0]], + sort([ + for ( + a = path_self_intersections(path, closed=closed, eps=eps), + ss = [ [a[1],a[2]], [a[3],a[4]] ] + ) if (ss[0] != undef) ss + ]), + [[len(path)-(closed?1:2), 1]] + ) + ) + ) [ + for (p = pair(isects)) + let( + s1 = p[0][0], + u1 = p[0][1], + s2 = p[1][0], + u2 = p[1][1], + section = path_subselect(path, s1, u1, s2, u2, closed=closed), + outpath = deduplicate(eps=eps, section) + ) + outpath + ]; function _tag_self_crossing_subpaths(path, closed=true, eps=EPSILON) = - let( - subpaths = split_path_at_self_crossings( - path, closed=closed, eps=eps - ) - ) [ - for (subpath = subpaths) let( - seg = select(subpath,0,1), - mp = mean(seg), - n = line_normal(seg) / 2048, - p1 = mp + n, - p2 = mp - n, - p1in = point_in_polygon(p1, path) >= 0, - p2in = point_in_polygon(p2, path) >= 0, - tag = (p1in && p2in)? "I" : "O" - ) [tag, subpath] - ]; + let( + subpaths = split_path_at_self_crossings( + path, closed=closed, eps=eps + ) + ) [ + for (subpath = subpaths) let( + seg = select(subpath,0,1), + mp = mean(seg), + n = line_normal(seg) / 2048, + p1 = mp + n, + p2 = mp - n, + p1in = point_in_polygon(p1, path) >= 0, + p2in = point_in_polygon(p2, path) >= 0, + tag = (p1in && p2in)? "I" : "O" + ) [tag, subpath] + ]; // Function: decompose_path() @@ -545,74 +545,74 @@ function _tag_self_crossing_subpaths(path, closed=true, eps=EPSILON) = // splitpaths = decompose_path(path, closed=true); // rainbow(splitpaths) stroke($item, closed=true, width=3); function decompose_path(path, closed=true, eps=EPSILON) = - let( - path = cleanup_path(path, eps=eps), - tagged = _tag_self_crossing_subpaths(path, closed=closed, eps=eps), - kept = [for (sub = tagged) if(sub[0] == "O") sub[1]], - completed = [for (frag=kept) if(is_closed_path(frag)) frag], - incomplete = [for (frag=kept) if(!is_closed_path(frag)) frag], - defrag = _path_fast_defragment(incomplete, eps=eps), - completed2 = assemble_path_fragments(defrag, eps=eps) - ) concat(completed2,completed); + let( + path = cleanup_path(path, eps=eps), + tagged = _tag_self_crossing_subpaths(path, closed=closed, eps=eps), + kept = [for (sub = tagged) if(sub[0] == "O") sub[1]], + completed = [for (frag=kept) if(is_closed_path(frag)) frag], + incomplete = [for (frag=kept) if(!is_closed_path(frag)) frag], + defrag = _path_fast_defragment(incomplete, eps=eps), + completed2 = assemble_path_fragments(defrag, eps=eps) + ) concat(completed2,completed); function _path_fast_defragment(fragments, eps=EPSILON, _done=[]) = - len(fragments)==0? _done : - let( - path = fragments[0], - endpt = select(path,-1), - extenders = [ - for (i = [1:1:len(fragments)-1]) let( - test1 = approx(endpt,fragments[i][0],eps=eps), - test2 = approx(endpt,select(fragments[i],-1),eps=eps) - ) if (test1 || test2) (test1? i : -1) - ] - ) len(extenders) == 1 && extenders[0] >= 0? _path_fast_defragment( - fragments=[ - concat(select(path,0,-2),fragments[extenders[0]]), - for (i = [1:1:len(fragments)-1]) - if (i != extenders[0]) fragments[i] - ], - eps=eps, - _done=_done - ) : _path_fast_defragment( - fragments=[for (i = [1:1:len(fragments)-1]) fragments[i]], - eps=eps, - _done=concat(_done,[deduplicate(path,closed=true,eps=eps)]) - ); + len(fragments)==0? _done : + let( + path = fragments[0], + endpt = select(path,-1), + extenders = [ + for (i = [1:1:len(fragments)-1]) let( + test1 = approx(endpt,fragments[i][0],eps=eps), + test2 = approx(endpt,select(fragments[i],-1),eps=eps) + ) if (test1 || test2) (test1? i : -1) + ] + ) len(extenders) == 1 && extenders[0] >= 0? _path_fast_defragment( + fragments=[ + concat(select(path,0,-2),fragments[extenders[0]]), + for (i = [1:1:len(fragments)-1]) + if (i != extenders[0]) fragments[i] + ], + eps=eps, + _done=_done + ) : _path_fast_defragment( + fragments=[for (i = [1:1:len(fragments)-1]) fragments[i]], + eps=eps, + _done=concat(_done,[deduplicate(path,closed=true,eps=eps)]) + ); function _extreme_angle_fragment(seg, fragments, rightmost=true, eps=EPSILON) = - !fragments? [undef, []] : - let( - delta = seg[1] - seg[0], - segang = atan2(delta.y,delta.x), - frags = [ - for (i = idx(fragments)) let( - fragment = fragments[i], - fwdmatch = approx(seg[1], fragment[0], eps=eps), - bakmatch = approx(seg[1], select(fragment,-1), eps=eps) - ) [ - fwdmatch, - bakmatch, - bakmatch? reverse(fragment) : fragment - ] - ], - angs = [ - for (frag = frags) - (frag[0] || frag[1])? let( - delta2 = frag[2][1] - frag[2][0], - segang2 = atan2(delta2.y, delta2.x) - ) modang(segang2 - segang) : ( - rightmost? 999 : -999 - ) - ], - fi = rightmost? min_index(angs) : max_index(angs) - ) abs(angs[fi]) > 360? [undef, fragments] : let( - remainder = [for (i=idx(fragments)) if (i!=fi) fragments[i]], - frag = frags[fi], - foundfrag = frag[2] - ) [foundfrag, remainder]; + !fragments? [undef, []] : + let( + delta = seg[1] - seg[0], + segang = atan2(delta.y,delta.x), + frags = [ + for (i = idx(fragments)) let( + fragment = fragments[i], + fwdmatch = approx(seg[1], fragment[0], eps=eps), + bakmatch = approx(seg[1], select(fragment,-1), eps=eps) + ) [ + fwdmatch, + bakmatch, + bakmatch? reverse(fragment) : fragment + ] + ], + angs = [ + for (frag = frags) + (frag[0] || frag[1])? let( + delta2 = frag[2][1] - frag[2][0], + segang2 = atan2(delta2.y, delta2.x) + ) modang(segang2 - segang) : ( + rightmost? 999 : -999 + ) + ], + fi = rightmost? min_index(angs) : max_index(angs) + ) abs(angs[fi]) > 360? [undef, fragments] : let( + remainder = [for (i=idx(fragments)) if (i!=fi) fragments[i]], + frag = frags[fi], + foundfrag = frag[2] + ) [foundfrag, remainder]; // Function: assemble_a_path_from_fragments() @@ -628,48 +628,48 @@ function _extreme_angle_fragment(seg, fragments, rightmost=true, eps=EPSILON) = // startfrag = The fragment to start with. Default: 0 // eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9) function assemble_a_path_from_fragments(fragments, rightmost=true, startfrag=0, eps=EPSILON) = - len(fragments)==0? _finished : - let( - path = fragments[startfrag], - newfrags = [for (i=idx(fragments)) if (i!=startfrag) fragments[i]] - ) is_closed_path(path, eps=eps)? ( - // starting fragment is already closed - [path, newfrags] - ) : let( - // Find rightmost/leftmost continuation fragment - seg = select(path,-2,-1), - extrema = _extreme_angle_fragment(seg=seg, fragments=newfrags, rightmost=rightmost, eps=eps), - foundfrag = extrema[0], - remainder = extrema[1] - ) is_undef(foundfrag)? ( - // No remaining fragments connect! INCOMPLETE PATH! - // Treat it as complete. - [path, remainder] - ) : is_closed_path(foundfrag, eps=eps)? ( - // Found fragment is already closed - [foundfrag, concat([path], remainder)] - ) : let( - fragend = select(foundfrag,-1), - hits = [for (i = idx(path,end=-2)) if(approx(path[i],fragend,eps=eps)) i] - ) hits? ( - let( - // Found fragment intersects with initial path - hitidx = select(hits,-1), - newpath = slice(path,0,hitidx+1), - newfrags = concat(len(newpath)>1? [newpath] : [], remainder), - outpath = concat(slice(path,hitidx,-2), foundfrag) - ) - [outpath, newfrags] - ) : let( - // Path still incomplete. Continue building it. - newpath = concat(path, slice(foundfrag, 1, -1)), - newfrags = concat([newpath], remainder) - ) - assemble_a_path_from_fragments( - fragments=newfrags, - rightmost=rightmost, - eps=eps - ); + len(fragments)==0? _finished : + let( + path = fragments[startfrag], + newfrags = [for (i=idx(fragments)) if (i!=startfrag) fragments[i]] + ) is_closed_path(path, eps=eps)? ( + // starting fragment is already closed + [path, newfrags] + ) : let( + // Find rightmost/leftmost continuation fragment + seg = select(path,-2,-1), + extrema = _extreme_angle_fragment(seg=seg, fragments=newfrags, rightmost=rightmost, eps=eps), + foundfrag = extrema[0], + remainder = extrema[1] + ) is_undef(foundfrag)? ( + // No remaining fragments connect! INCOMPLETE PATH! + // Treat it as complete. + [path, remainder] + ) : is_closed_path(foundfrag, eps=eps)? ( + // Found fragment is already closed + [foundfrag, concat([path], remainder)] + ) : let( + fragend = select(foundfrag,-1), + hits = [for (i = idx(path,end=-2)) if(approx(path[i],fragend,eps=eps)) i] + ) hits? ( + let( + // Found fragment intersects with initial path + hitidx = select(hits,-1), + newpath = slice(path,0,hitidx+1), + newfrags = concat(len(newpath)>1? [newpath] : [], remainder), + outpath = concat(slice(path,hitidx,-2), foundfrag) + ) + [outpath, newfrags] + ) : let( + // Path still incomplete. Continue building it. + newpath = concat(path, slice(foundfrag, 1, -1)), + newfrags = concat([newpath], remainder) + ) + assemble_a_path_from_fragments( + fragments=newfrags, + rightmost=rightmost, + eps=eps + ); // Function: assemble_path_fragments() @@ -681,34 +681,34 @@ function assemble_a_path_from_fragments(fragments, rightmost=true, startfrag=0, // fragments = List of polylines to be assembled into complete polygons. // eps = The epsilon error value to determine whether two points coincide. Default: `EPSILON` (1e-9) function assemble_path_fragments(fragments, eps=EPSILON, _finished=[]) = - len(fragments)==0? _finished : - let( - minxidx = min_index([ - for (frag=fragments) min(subindex(frag,0)) - ]), - result_l = assemble_a_path_from_fragments( - fragments=fragments, - startfrag=minxidx, - rightmost=false, - eps=eps - ), - result_r = assemble_a_path_from_fragments( - fragments=fragments, - startfrag=minxidx, - rightmost=true, - eps=eps - ), - l_area = abs(polygon_area(result_l[0])), - r_area = abs(polygon_area(result_r[0])), - result = l_area < r_area? result_l : result_r, - newpath = cleanup_path(result[0]), - remainder = result[1], - finished = concat(_finished, [newpath]) - ) assemble_path_fragments( - fragments=remainder, - eps=eps, - _finished=finished - ); + len(fragments)==0? _finished : + let( + minxidx = min_index([ + for (frag=fragments) min(subindex(frag,0)) + ]), + result_l = assemble_a_path_from_fragments( + fragments=fragments, + startfrag=minxidx, + rightmost=false, + eps=eps + ), + result_r = assemble_a_path_from_fragments( + fragments=fragments, + startfrag=minxidx, + rightmost=true, + eps=eps + ), + l_area = abs(polygon_area(result_l[0])), + r_area = abs(polygon_area(result_r[0])), + result = l_area < r_area? result_l : result_r, + newpath = cleanup_path(result[0]), + remainder = result[1], + finished = concat(_finished, [newpath]) + ) assemble_path_fragments( + fragments=remainder, + eps=eps, + _finished=finished + ); @@ -725,12 +725,12 @@ function assemble_path_fragments(fragments, eps=EPSILON, _finished=[]) = // modulated_circle(r=40, sines=[[3, 11], [1, 31]], $fn=6); module modulated_circle(r=40, sines=[10]) { - freqs = len(sines)>0? [for (i=sines) i[1]] : [5]; - points = [ - for (a = [0 : (360/segs(r)/max(freqs)) : 360]) - let(nr=r+sum_of_sines(a,sines)) [nr*cos(a), nr*sin(a)] - ]; - polygon(points); + freqs = len(sines)>0? [for (i=sines) i[1]] : [5]; + points = [ + for (a = [0 : (360/segs(r)/max(freqs)) : 360]) + let(nr=r+sum_of_sines(a,sines)) [nr*cos(a), nr*sin(a)] + ]; + polygon(points); } @@ -752,14 +752,14 @@ module modulated_circle(r=40, sines=[10]) // xcopies(3) circle(3, $fn=32); // } module extrude_from_to(pt1, pt2, convexity=undef, twist=undef, scale=undef, slices=undef) { - rtp = xyz_to_spherical(pt2-pt1); - translate(pt1) { - rotate([0, rtp[2], rtp[1]]) { - linear_extrude(height=rtp[0], convexity=convexity, center=false, slices=slices, twist=twist, scale=scale) { - children(); - } - } - } + rtp = xyz_to_spherical(pt2-pt1); + translate(pt1) { + rotate([0, rtp[2], rtp[1]]) { + linear_extrude(height=rtp[0], convexity=convexity, center=false, slices=slices, twist=twist, scale=scale) { + children(); + } + } + } } @@ -781,53 +781,53 @@ module extrude_from_to(pt1, pt2, convexity=undef, twist=undef, scale=undef, slic // poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]]; // spiral_sweep(poly, h=200, r=50, twist=1080, $fn=36); module spiral_sweep(polyline, h, r, twist=360, center, anchor, spin=0, orient=UP) { - polyline = path3d(polyline); - pline_count = len(polyline); - steps = ceil(segs(r)*(twist/360)); - anchor = get_anchor(anchor,center,BOT,BOT); + polyline = path3d(polyline); + pline_count = len(polyline); + steps = ceil(segs(r)*(twist/360)); + anchor = get_anchor(anchor,center,BOT,BOT); - poly_points = [ - for ( - p = [0:1:steps] - ) let ( - a = twist * (p/steps), - dx = r*cos(a), - dy = r*sin(a), - dz = h * (p/steps), - pts = apply_list( - polyline, [ - affine3d_xrot(90), - affine3d_zrot(a), - affine3d_translate([dx, dy, dz-h/2]) - ] - ) - ) for (pt = pts) pt - ]; + poly_points = [ + for ( + p = [0:1:steps] + ) let ( + a = twist * (p/steps), + dx = r*cos(a), + dy = r*sin(a), + dz = h * (p/steps), + pts = apply_list( + polyline, [ + affine3d_xrot(90), + affine3d_zrot(a), + affine3d_translate([dx, dy, dz-h/2]) + ] + ) + ) for (pt = pts) pt + ]; - poly_faces = concat( - [[for (b = [0:1:pline_count-1]) b]], - [ - for ( - p = [0:1:steps-1], - b = [0:1:pline_count-1], - i = [0:1] - ) let ( - b2 = (b == pline_count-1)? 0 : b+1, - p0 = p * pline_count + b, - p1 = p * pline_count + b2, - p2 = (p+1) * pline_count + b2, - p3 = (p+1) * pline_count + b, - pt = (i==0)? [p0, p2, p1] : [p0, p3, p2] - ) pt - ], - [[for (b = [pline_count-1:-1:0]) b+(steps)*pline_count]] - ); + poly_faces = concat( + [[for (b = [0:1:pline_count-1]) b]], + [ + for ( + p = [0:1:steps-1], + b = [0:1:pline_count-1], + i = [0:1] + ) let ( + b2 = (b == pline_count-1)? 0 : b+1, + p0 = p * pline_count + b, + p1 = p * pline_count + b2, + p2 = (p+1) * pline_count + b2, + p3 = (p+1) * pline_count + b, + pt = (i==0)? [p0, p2, p1] : [p0, p3, p2] + ) pt + ], + [[for (b = [pline_count-1:-1:0]) b+(steps)*pline_count]] + ); - tri_faces = triangulate_faces(poly_points, poly_faces); - attachable(anchor,spin,orient, r=r, l=h) { - polyhedron(points=poly_points, faces=tri_faces, convexity=10); - children(); - } + tri_faces = triangulate_faces(poly_points, poly_faces); + attachable(anchor,spin,orient, r=r, l=h) { + polyhedron(points=poly_points, faces=tri_faces, convexity=10); + children(); + } } @@ -843,44 +843,44 @@ module spiral_sweep(polyline, h, r, twist=360, center, anchor, spin=0, orient=UP // path = [ [0, 0, 0], [33, 33, 33], [66, 33, 40], [100, 0, 0], [150,0,0] ]; // path_extrude(path) circle(r=10, $fn=6); module path_extrude(path, convexity=10, clipsize=100) { - function polyquats(path, q=Q_Ident(), v=[0,0,1], i=0) = let( - v2 = path[i+1] - path[i], - ang = vector_angle(v,v2), - axis = ang>0.001? unit(cross(v,v2)) : [0,0,1], - newq = Q_Mul(Quat(axis, ang), q), - dist = norm(v2) - ) i < (len(path)-2)? - concat([[dist, newq, ang]], polyquats(path, newq, v2, i+1)) : - [[dist, newq, ang]]; + function polyquats(path, q=Q_Ident(), v=[0,0,1], i=0) = let( + v2 = path[i+1] - path[i], + ang = vector_angle(v,v2), + axis = ang>0.001? unit(cross(v,v2)) : [0,0,1], + newq = Q_Mul(Quat(axis, ang), q), + dist = norm(v2) + ) i < (len(path)-2)? + concat([[dist, newq, ang]], polyquats(path, newq, v2, i+1)) : + [[dist, newq, ang]]; - epsilon = 0.0001; // Make segments ever so slightly too long so they overlap. - ptcount = len(path); - pquats = polyquats(path); - for (i = [0:1:ptcount-2]) { - pt1 = path[i]; - pt2 = path[i+1]; - dist = pquats[i][0]; - q = pquats[i][1]; - difference() { - translate(pt1) { - Qrot(q) { - down(clipsize/2/2) { - linear_extrude(height=dist+clipsize/2, convexity=convexity) { - children(); - } - } - } - } - translate(pt1) { - hq = (i > 0)? Q_Slerp(q, pquats[i-1][1], 0.5) : q; - Qrot(hq) down(clipsize/2+epsilon) cube(clipsize, center=true); - } - translate(pt2) { - hq = (i < ptcount-2)? Q_Slerp(q, pquats[i+1][1], 0.5) : q; - Qrot(hq) up(clipsize/2+epsilon) cube(clipsize, center=true); - } - } - } + epsilon = 0.0001; // Make segments ever so slightly too long so they overlap. + ptcount = len(path); + pquats = polyquats(path); + for (i = [0:1:ptcount-2]) { + pt1 = path[i]; + pt2 = path[i+1]; + dist = pquats[i][0]; + q = pquats[i][1]; + difference() { + translate(pt1) { + Qrot(q) { + down(clipsize/2/2) { + linear_extrude(height=dist+clipsize/2, convexity=convexity) { + children(); + } + } + } + } + translate(pt1) { + hq = (i > 0)? Q_Slerp(q, pquats[i-1][1], 0.5) : q; + Qrot(hq) down(clipsize/2+epsilon) cube(clipsize, center=true); + } + translate(pt2) { + hq = (i < ptcount-2)? Q_Slerp(q, pquats[i+1][1], 0.5) : q; + Qrot(hq) up(clipsize/2+epsilon) cube(clipsize, center=true); + } + } + } } @@ -969,46 +969,46 @@ module path_extrude(path, convexity=10, clipsize=100) { // } module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=false) { - length = path_length(path,closed); - distances = is_def(sp)? ( - is_def(n) && is_def(spacing)? list_range(s=sp, step=spacing, n=n) : - is_def(n)? list_range(s=sp, e=length, n=n) : - list_range(s=sp, step=spacing, e=length) - ) : is_def(n) && is_undef(spacing)? ( - closed? - let(range=list_range(s=0,e=length, n=n+1)) slice(range,0,-2) : - list_range(s=0, e=length, n=n) - ) : ( - let( - n = is_def(n)? n : floor(length/spacing)+(closed?0:1), - ptlist = list_range(s=0,step=spacing,n=n), - listcenter = mean(ptlist) - ) closed? - sort([for(entry=ptlist) posmod(entry-listcenter,length)]) : - [for(entry=ptlist) entry + length/2-listcenter ] - ); - distOK = min(distances)>=0 && max(distances)<=length; - assert(distOK,"Cannot fit all of the copies"); - cutlist = path_cut(path, distances, closed, direction=true); - planar = len(path[0])==2; - if (true) for(i=[0:1:len(cutlist)-1]) { - $pos = cutlist[i][0]; - $idx = i; - $dir = rotate_children ? (planar?[1,0]:[1,0,0]) : cutlist[i][2]; - $normal = rotate_children? (planar?[0,1]:[0,0,1]) : cutlist[i][3]; - translate($pos) { - if (rotate_children) { - if(planar) { - rot(from=[0,1],to=cutlist[i][3]) children(); - } else { - multmatrix(affine2d_to_3d(transpose([cutlist[i][2],cross(cutlist[i][3],cutlist[i][2]), cutlist[i][3]]))) - children(); - } - } else { - children(); - } - } - } + length = path_length(path,closed); + distances = is_def(sp)? ( + is_def(n) && is_def(spacing)? list_range(s=sp, step=spacing, n=n) : + is_def(n)? list_range(s=sp, e=length, n=n) : + list_range(s=sp, step=spacing, e=length) + ) : is_def(n) && is_undef(spacing)? ( + closed? + let(range=list_range(s=0,e=length, n=n+1)) slice(range,0,-2) : + list_range(s=0, e=length, n=n) + ) : ( + let( + n = is_def(n)? n : floor(length/spacing)+(closed?0:1), + ptlist = list_range(s=0,step=spacing,n=n), + listcenter = mean(ptlist) + ) closed? + sort([for(entry=ptlist) posmod(entry-listcenter,length)]) : + [for(entry=ptlist) entry + length/2-listcenter ] + ); + distOK = min(distances)>=0 && max(distances)<=length; + assert(distOK,"Cannot fit all of the copies"); + cutlist = path_cut(path, distances, closed, direction=true); + planar = len(path[0])==2; + if (true) for(i=[0:1:len(cutlist)-1]) { + $pos = cutlist[i][0]; + $idx = i; + $dir = rotate_children ? (planar?[1,0]:[1,0,0]) : cutlist[i][2]; + $normal = rotate_children? (planar?[0,1]:[0,0,1]) : cutlist[i][3]; + translate($pos) { + if (rotate_children) { + if(planar) { + rot(from=[0,1],to=cutlist[i][3]) children(); + } else { + multmatrix(affine2d_to_3d(transpose([cutlist[i][2],cross(cutlist[i][3],cutlist[i][2]), cutlist[i][3]]))) + children(); + } + } else { + children(); + } + } + } } @@ -1040,76 +1040,76 @@ module path_spread(path, n, spacing, sp=undef, rotate_children=true, closed=fals // path_cut(square, [0,0.8,1.6,2.4,3.2], closed=true); // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], [[0, 0.8], 4]] // path_cut(square, [0,0.8,1.6,2.4,3.2]); // Returns [[[0, 0], 1], [[0.8, 0], 1], [[1, 0.6], 2], [[0.6, 1], 3], undef] function path_cut(path, dists, closed=false, direction=false) = - let(long_enough = len(path) >= (closed ? 3 : 2)) - assert(long_enough,len(path)<2 ? "Two points needed to define a path" : "Closed path must include three points") - !is_list(dists)? path_cut(path, [dists],closed, direction)[0] : - let(cuts = _path_cut(path,dists,closed)) - !direction ? cuts : let( - dir = _path_cuts_dir(path, cuts, closed), - normals = _path_cuts_normals(path, cuts, dir, closed) - ) zip(cuts, array_group(dir,1), array_group(normals,1)); + let(long_enough = len(path) >= (closed ? 3 : 2)) + assert(long_enough,len(path)<2 ? "Two points needed to define a path" : "Closed path must include three points") + !is_list(dists)? path_cut(path, [dists],closed, direction)[0] : + let(cuts = _path_cut(path,dists,closed)) + !direction ? cuts : let( + dir = _path_cuts_dir(path, cuts, closed), + normals = _path_cuts_normals(path, cuts, dir, closed) + ) zip(cuts, array_group(dir,1), array_group(normals,1)); // Main recursive path cut function function _path_cut(path, dists, closed=false, pind=0, dtotal=0, dind=0, result=[]) = - dind == len(dists) ? result : - let( - lastpt = len(result)>0? select(result,-1)[0] : [], - dpartial = len(result)==0? 0 : norm(lastpt-path[pind]), - nextpoint = dpartial > dists[dind]-dtotal? - [lerp(lastpt,path[pind], (dists[dind]-dtotal)/dpartial),pind] : - _path_cut_single(path, dists[dind]-dtotal-dpartial, closed, pind) - ) is_undef(nextpoint)? - concat(result, repeat(undef,len(dists)-dind)) : - _path_cut(path, dists, closed, nextpoint[1], dists[dind],dind+1, concat(result, [nextpoint])); + dind == len(dists) ? result : + let( + lastpt = len(result)>0? select(result,-1)[0] : [], + dpartial = len(result)==0? 0 : norm(lastpt-path[pind]), + nextpoint = dpartial > dists[dind]-dtotal? + [lerp(lastpt,path[pind], (dists[dind]-dtotal)/dpartial),pind] : + _path_cut_single(path, dists[dind]-dtotal-dpartial, closed, pind) + ) is_undef(nextpoint)? + concat(result, repeat(undef,len(dists)-dind)) : + _path_cut(path, dists, closed, nextpoint[1], dists[dind],dind+1, concat(result, [nextpoint])); // Search for a single cut point in the path function _path_cut_single(path, dist, closed=false, ind=0, eps=1e-7) = - ind>=len(path)? undef : - ind==len(path)-1 && !closed? (dist dist ? - [lerp(path[ind],select(path,ind+1),dist/d), ind+1] : - _path_cut_single(path, dist-d,closed, ind+1, eps); + ind>=len(path)? undef : + ind==len(path)-1 && !closed? (dist dist ? + [lerp(path[ind],select(path,ind+1),dist/d), ind+1] : + _path_cut_single(path, dist-d,closed, ind+1, eps); // Find normal directions to the path, coplanar to local part of the path // Or return a vector parallel to the x-y plane if the above fails function _path_cuts_normals(path, cuts, dirs, closed=false) = - [for(i=[0:len(cuts)-1]) - len(path[0])==2? [-dirs[i].y, dirs[i].x] : ( - let( - plane = len(path)<3 ? undef : - let(start = max(min(cuts[i][1],len(path)-1),2)) _path_plane(path, start, start-2) - ) - plane==undef? - unit([-dirs[i].y, dirs[i].x,0]) : - unit(cross(dirs[i],cross(plane[0],plane[1]))) - ) - ]; + [for(i=[0:len(cuts)-1]) + len(path[0])==2? [-dirs[i].y, dirs[i].x] : ( + let( + plane = len(path)<3 ? undef : + let(start = max(min(cuts[i][1],len(path)-1),2)) _path_plane(path, start, start-2) + ) + plane==undef? + unit([-dirs[i].y, dirs[i].x,0]) : + unit(cross(dirs[i],cross(plane[0],plane[1]))) + ) + ]; // Scan from the specified point (ind) to find a noncoplanar triple to use // to define the plane of the path. function _path_plane(path, ind, i,closed) = - i<(closed?-1:0) ? undef : - !collinear(path[ind],path[ind-1], select(path,i))? - [select(path,i)-path[ind-1],path[ind]-path[ind-1]] : - _path_plane(path, ind, i-1); + i<(closed?-1:0) ? undef : + !collinear(path[ind],path[ind-1], select(path,i))? + [select(path,i)-path[ind-1],path[ind]-path[ind-1]] : + _path_plane(path, ind, i-1); // Find the direction of the path at the cut points function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) = - [for(ind=[0:len(cuts)-1]) - let( - nextind = cuts[ind][1], - nextpath = unit(select(path, nextind+1)-select(path, nextind)), - thispath = unit(select(path, nextind) - path[nextind-1]), - lastpath = unit(path[nextind-1] - select(path, nextind-2)), - nextdir = - nextind==len(path) && !closed? lastpath : - (nextind<=len(path)-2 || closed) && approx(cuts[ind][0], path[nextind],eps)? - unit(nextpath+thispath) : - (nextind>1 || closed) && approx(cuts[ind][0],path[nextind-1],eps)? - unit(thispath+lastpath) : - thispath - ) nextdir - ]; + [for(ind=[0:len(cuts)-1]) + let( + nextind = cuts[ind][1], + nextpath = unit(select(path, nextind+1)-select(path, nextind)), + thispath = unit(select(path, nextind) - path[nextind-1]), + lastpath = unit(path[nextind-1] - select(path, nextind-2)), + nextdir = + nextind==len(path) && !closed? lastpath : + (nextind<=len(path)-2 || closed) && approx(cuts[ind][0], path[nextind],eps)? + unit(nextpath+thispath) : + (nextind>1 || closed) && approx(cuts[ind][0],path[nextind-1],eps)? + unit(thispath+lastpath) : + thispath + ) nextdir + ]; // Input `data` is a list that sums to an integer. // Returns rounded version of input data so that every @@ -1118,14 +1118,14 @@ function _path_cuts_dir(path, cuts, closed=false, eps=1e-2) = // and passing the rounding error forward to the next entry. // This will generally distribute the error in a uniform manner. function _sum_preserving_round(data, index=0) = - index == len(data)-1 ? list_set(data, len(data)-1, round(data[len(data)-1])) : - let( - newval = round(data[index]), - error = newval - data[index] - ) _sum_preserving_round( - list_set(data, [index,index+1], [newval, data[index+1]-error]), - index+1 - ); + index == len(data)-1 ? list_set(data, len(data)-1, round(data[len(data)-1])) : + let( + newval = round(data[index]), + error = newval - data[index] + ) _sum_preserving_round( + list_set(data, [index,index+1], [newval, data[index+1]-error]), + index+1 + ); // Function: subdivide_path() @@ -1184,37 +1184,37 @@ function _sum_preserving_round(data, index=0) = // mypath = subdivide_path([[0,0,0],[2,0,1],[2,3,2]], 12); // move_copies(mypath)sphere(r=.1,$fn=32); function subdivide_path(path, N, closed=true, exact=true, method="length") = - assert(is_path(path)) - assert(method=="length" || method=="segment") - assert((is_num(N) && N>0) || is_vector(N),"Parameter N to subdivide_path must be postive number or vector") - let( - count = len(path) - (closed?0:1), - add_guess = method=="segment"? ( - is_list(N)? ( - assert(len(N)==count,"Vector parameter N to subdivide_path has the wrong length") - add_scalar(N,-1) - ) : repeat((N-len(path)) / count, count) - ) : // method=="length" - assert(is_num(N),"Parameter N to subdivide path must be a number when method=\"length\"") - let( - path_lens = concat( - [ for (i = [0:1:len(path)-2]) norm(path[i+1]-path[i]) ], - closed? [norm(path[len(path)-1]-path[0])] : [] - ), - add_density = (N - len(path)) / sum(path_lens) - ) - path_lens * add_density, - add = exact? _sum_preserving_round(add_guess) : - [for (val=add_guess) round(val)] - ) concat( - [ - for (i=[0:1:count]) each [ - for(j=[0:1:add[i]]) - lerp(path[i],select(path,i+1), j/(add[i]+1)) - ] - ], - closed? [] : [select(path,-1)] - ); + assert(is_path(path)) + assert(method=="length" || method=="segment") + assert((is_num(N) && N>0) || is_vector(N),"Parameter N to subdivide_path must be postive number or vector") + let( + count = len(path) - (closed?0:1), + add_guess = method=="segment"? ( + is_list(N)? ( + assert(len(N)==count,"Vector parameter N to subdivide_path has the wrong length") + add_scalar(N,-1) + ) : repeat((N-len(path)) / count, count) + ) : // method=="length" + assert(is_num(N),"Parameter N to subdivide path must be a number when method=\"length\"") + let( + path_lens = concat( + [ for (i = [0:1:len(path)-2]) norm(path[i+1]-path[i]) ], + closed? [norm(path[len(path)-1]-path[0])] : [] + ), + add_density = (N - len(path)) / sum(path_lens) + ) + path_lens * add_density, + add = exact? _sum_preserving_round(add_guess) : + [for (val=add_guess) round(val)] + ) concat( + [ + for (i=[0:1:count]) each [ + for(j=[0:1:add[i]]) + lerp(path[i],select(path,i+1), j/(add[i]+1)) + ] + ], + closed? [] : [select(path,-1)] + ); // Function: path_length_fractions() @@ -1225,17 +1225,17 @@ function subdivide_path(path, N, closed=true, exact=true, method="length") = // will have one extra point because of the final connecting segment that connects the last // point of the path to the first point. function path_length_fractions(path, closed=false) = - assert(is_path(path)) - assert(is_bool(closed)) - let( - lengths = [ - 0, - for (i=[0:1:len(path)-(closed?1:2)]) - norm(select(path,i+1)-path[i]) - ], - partial_len = cumsum(lengths), - total_len = select(partial_len,-1) - ) partial_len / total_len; + assert(is_path(path)) + assert(is_bool(closed)) + let( + lengths = [ + 0, + for (i=[0:1:len(path)-(closed?1:2)]) + norm(select(path,i+1)-path[i]) + ], + partial_len = cumsum(lengths), + total_len = select(partial_len,-1) + ) partial_len / total_len; // Function: resample_path() @@ -1258,14 +1258,14 @@ function resample_path(path, N, spacing, closed=false) = assert(num_defined([N,spacing])==1,"Must define exactly one of N and spacing") assert(is_bool(closed)) let( - length = path_length(path,closed), - N = is_def(N) ? N : round(length/spacing) + (closed?0:1), + length = path_length(path,closed), + N = is_def(N) ? N : round(length/spacing) + (closed?0:1), spacing = length/(closed?N:N-1), // Note: worried about round-off error, so don't include distlist = list_range(closed?N:N-1,step=spacing), // last point when closed=false - cuts = path_cut(path, distlist, closed=closed) + cuts = path_cut(path, distlist, closed=closed) ) concat(subindex(cuts,0),closed?[]:[select(path,-1)]); // Then add last point here -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/phillips_drive.scad b/phillips_drive.scad index 2a169b1..3288722 100644 --- a/phillips_drive.scad +++ b/phillips_drive.scad @@ -28,52 +28,52 @@ // phillips_drive(size="#3", shaft=6, l=20); // } module phillips_drive(size="#2", shaft=6, l=20, $fn=36, anchor=BOTTOM, spin=0, orient=UP) { - assert(is_string(size)); - assert(in_list(size,["#0","#1","#2","#3","#4"])); + assert(is_string(size)); + assert(in_list(size,["#0","#1","#2","#3","#4"])); - num = ord(size[1]) - ord("0"); - b = [0.61, 0.97, 1.47, 2.41, 3.48][num]; - e = [0.31, 0.43, 0.81, 2.00, 2.41][num]; - g = [0.81, 1.27, 2.29, 3.81, 5.08][num]; - //f = [0.33, 0.53, 0.70, 0.82, 1.23][num]; - //r = [0.30, 0.50, 0.60, 0.80, 1.00][num]; - alpha = [ 136, 138, 140, 146, 153][num]; - beta = [7.00, 7.00, 5.75, 5.75, 7.00][num]; - gamma = 92.0; - ang1 = 28.0; - ang2 = 26.5; - h1 = adj_ang_to_opp(g/2, ang1); - h2 = adj_ang_to_opp((shaft-g)/2, 90-ang2); - h3 = adj_ang_to_opp(b/2, ang1); - p0 = [0,0]; - p1 = [e/2, adj_ang_to_opp(e/2, 90-alpha/2)]; - p2 = p1 + [(shaft-e)/2, adj_ang_to_hyp((shaft-e)/2, 90-gamma/2)]; - attachable(anchor,spin,orient, d=shaft, l=l) { - down(l/2) { - difference() { - union() { - cyl(d1=0, d2=g, h=h1, anchor=BOT); - up(h1) { - cyl(d1=g, d2=shaft, h=h2, anchor=BOT); - up(h2) cyl(d=shaft, h=l-h1-h2, anchor=BOT); - } - } - zrot(45) - zrot_copies(n=4, r=b/2/cos(90-alpha/2), sa=90) { - up(h3) { - xrot(-beta) { - linear_extrude(height=(h1+h2)*20, convexity=4, center=true) { - path = [p0, p1, p2, [-p2.x,p2.y], [-p1.x,p1.y]]; - polygon(path); - } - } - } - } - } - } - children(); - } + num = ord(size[1]) - ord("0"); + b = [0.61, 0.97, 1.47, 2.41, 3.48][num]; + e = [0.31, 0.43, 0.81, 2.00, 2.41][num]; + g = [0.81, 1.27, 2.29, 3.81, 5.08][num]; + //f = [0.33, 0.53, 0.70, 0.82, 1.23][num]; + //r = [0.30, 0.50, 0.60, 0.80, 1.00][num]; + alpha = [ 136, 138, 140, 146, 153][num]; + beta = [7.00, 7.00, 5.75, 5.75, 7.00][num]; + gamma = 92.0; + ang1 = 28.0; + ang2 = 26.5; + h1 = adj_ang_to_opp(g/2, ang1); + h2 = adj_ang_to_opp((shaft-g)/2, 90-ang2); + h3 = adj_ang_to_opp(b/2, ang1); + p0 = [0,0]; + p1 = [e/2, adj_ang_to_opp(e/2, 90-alpha/2)]; + p2 = p1 + [(shaft-e)/2, adj_ang_to_hyp((shaft-e)/2, 90-gamma/2)]; + attachable(anchor,spin,orient, d=shaft, l=l) { + down(l/2) { + difference() { + union() { + cyl(d1=0, d2=g, h=h1, anchor=BOT); + up(h1) { + cyl(d1=g, d2=shaft, h=h2, anchor=BOT); + up(h2) cyl(d=shaft, h=l-h1-h2, anchor=BOT); + } + } + zrot(45) + zrot_copies(n=4, r=b/2/cos(90-alpha/2), sa=90) { + up(h3) { + xrot(-beta) { + linear_extrude(height=(h1+h2)*20, convexity=4, center=true) { + path = [p0, p1, p2, [-p2.x,p2.y], [-p1.x,p1.y]]; + polygon(path); + } + } + } + } + } + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/polyhedra.scad b/polyhedra.scad index 1d67564..3902ecf 100644 --- a/polyhedra.scad +++ b/polyhedra.scad @@ -22,9 +22,9 @@ include // Groups entries in "arr" into groups of equal values and returns index lists of those groups function _unique_groups(m) = [ - for (i=[0:1:len(m)-1]) let( - s = search([m[i]], m, 0)[0] - ) if (s[0]==i) s + for (i=[0:1:len(m)-1]) let( + s = search([m[i]], m, 0)[0] + ) if (s[0]==i) s ]; @@ -277,82 +277,82 @@ function _unique_groups(m) = [ // regular_polyhedron("pentagonal hexecontahedron", or=1,facedown=false); // } module regular_polyhedron( - name=undef, - index=undef, - type=undef, - faces=undef, - facetype=undef, - hasfaces=undef, - side=1, - ir=undef, - mr=undef, - or=undef, - r=undef, - d=undef, - anchor=[0,0,0], - center=undef, - rounding=0, - repeat=true, - facedown=true, - draw=true, - rotate_children=true, - stellate = false, - longside=undef, // special parameter for trapezohedron - h=undef // special parameter for trapezohedron + name=undef, + index=undef, + type=undef, + faces=undef, + facetype=undef, + hasfaces=undef, + side=1, + ir=undef, + mr=undef, + or=undef, + r=undef, + d=undef, + anchor=[0,0,0], + center=undef, + rounding=0, + repeat=true, + facedown=true, + draw=true, + rotate_children=true, + stellate = false, + longside=undef, // special parameter for trapezohedron + h=undef // special parameter for trapezohedron ) { - assert(rounding>=0, "'rounding' must be nonnegative"); - entry = regular_polyhedron_info( - "fullentry", name=name, index=index, - type=type, faces=faces, facetype=facetype, - hasfaces=hasfaces, side=side, - ir=ir, mr=mr, or=or, - r=r, d=d, - anchor=anchor, center=center, - facedown=facedown, - stellate=stellate, - longside=longside, h=h - ); - scaled_points = entry[0]; - translation = entry[1]; - face_triangles = entry[2]; - faces = entry[3]; - face_normals = entry[4]; - in_radius = entry[5]; - if (draw){ - if (rounding==0) - polyhedron(move(translation, p=scaled_points), faces = face_triangles); - else { - fn = segs(rounding); - rounding = rounding/cos(180/fn); - adjusted_scale = 1 - rounding / in_radius; - minkowski(){ - sphere(r=rounding, $fn=fn); - polyhedron(move(translation,p=adjusted_scale*scaled_points), faces = face_triangles); - } - } - } - if ($children>0) { - maxrange = repeat ? len(faces)-1 : $children-1; - for(i=[0:1:maxrange]) { - // Would like to orient so an edge (longest edge?) is parallel to x axis - facepts = move(translation, p=select(scaled_points, faces[i])); - center = mean(facepts); - rotatedface = rot(from=face_normals[i], to=[0,0,1], p=move(-center, p=facepts)); - clockwise = sortidx([for(pt=rotatedface) -atan2(pt.y,pt.x)]); - $face = rotate_children? - path2d(select(rotatedface,clockwise)) : - select(move(-center,p=facepts), clockwise); - $faceindex = i; - $center = -translation-center; - translate(center) - if (rotate_children) { - rot(from=[0,0,1], to=face_normals[i]) - children(i % $children); - } else { - children(i % $children); - } - } - } + assert(rounding>=0, "'rounding' must be nonnegative"); + entry = regular_polyhedron_info( + "fullentry", name=name, index=index, + type=type, faces=faces, facetype=facetype, + hasfaces=hasfaces, side=side, + ir=ir, mr=mr, or=or, + r=r, d=d, + anchor=anchor, center=center, + facedown=facedown, + stellate=stellate, + longside=longside, h=h + ); + scaled_points = entry[0]; + translation = entry[1]; + face_triangles = entry[2]; + faces = entry[3]; + face_normals = entry[4]; + in_radius = entry[5]; + if (draw){ + if (rounding==0) + polyhedron(move(translation, p=scaled_points), faces = face_triangles); + else { + fn = segs(rounding); + rounding = rounding/cos(180/fn); + adjusted_scale = 1 - rounding / in_radius; + minkowski(){ + sphere(r=rounding, $fn=fn); + polyhedron(move(translation,p=adjusted_scale*scaled_points), faces = face_triangles); + } + } + } + if ($children>0) { + maxrange = repeat ? len(faces)-1 : $children-1; + for(i=[0:1:maxrange]) { + // Would like to orient so an edge (longest edge?) is parallel to x axis + facepts = move(translation, p=select(scaled_points, faces[i])); + center = mean(facepts); + rotatedface = rot(from=face_normals[i], to=[0,0,1], p=move(-center, p=facepts)); + clockwise = sortidx([for(pt=rotatedface) -atan2(pt.y,pt.x)]); + $face = rotate_children? + path2d(select(rotatedface,clockwise)) : + select(move(-center,p=facepts), clockwise); + $faceindex = i; + $center = -translation-center; + translate(center) + if (rotate_children) { + rot(from=[0,0,1], to=face_normals[i]) + children(i % $children); + } else { + children(i % $children); + } + } + } } ///////////////////////////////////////////////////////////////////////////// @@ -364,17 +364,17 @@ module regular_polyhedron( function _even_perms(v) = [v, [v[2], v[0], v[1]], [v[1],v[2],v[0]]]; function _all_perms(v) = [v, [v[2], v[0], v[1]], [v[1],v[2],v[0]], [v[1],v[0],v[2]],[v[2],v[1],v[0]],[v[0],v[2],v[1]]]; // -// Point reflections across all planes. In the unconstrained case, this means one point becomes 8 points. +// Point reflections across all planes. In the unconstrained case, this means one point becomes 8 points. // // sign=="even" means an even number of minus signs (odd number of plus signs) // sign=="odd" means an odd number of minus signs (even number of plus signs) // function _point_ref(points, sign="both") = - unique([ - for(i=[-1,1],j=[-1,1],k=[-1,1]) - if (sign=="both" || sign=="even" && i*j*k>0 || sign=="odd" && i*j*k<0) - each [for(point=points) vmul(point,[i,j,k])] - ]); + unique([ + for(i=[-1,1],j=[-1,1],k=[-1,1]) + if (sign=="both" || sign=="even" && i*j*k>0 || sign=="odd" && i*j*k<0) + each [for(point=points) vmul(point,[i,j,k])] + ]); // _tribonacci=(1+4*cosh(acosh(2+3/8)/3))/3; // @@ -384,160 +384,160 @@ _tribonacci=(1+4*cosh(acosh(2+3/8)/3))/3; // The polyhedra information is from Wikipedia and http://dmccooey.com/polyhedra/ // _polyhedra_ = [ - // Platonic Solids + // Platonic Solids - ["tetrahedron", "platonic", 4,[3], 2*sqrt(2), sqrt(6)/12, sqrt(2)/4, sqrt(6)/4, 1/6/sqrt(2), - _point_ref([[1,1,1]], sign="even")], - ["cube", "platonic", 6, [4], 2, 1/2, 1/sqrt(2), sqrt(3)/2, 1, - _point_ref([[1,1,1]])], - ["octahedron", "platonic", 8, [3], sqrt(2), sqrt(6)/6, 1/2, sqrt(2)/2, sqrt(2)/3, - _point_ref(_even_perms([1,0,0]))], - ["dodecahedron", "platonic", 12, [5], 2/PHI, sqrt(5/2+11*sqrt(5)/10)/2, (3+sqrt(5))/4, sqrt(3)*PHI/2, (15+7*sqrt(5))/4, - _point_ref(concat([[1,1,1]],_even_perms([0,PHI,1/PHI])))], - ["icosahedron", "platonic", 20, [3], 2, PHI*PHI/2/sqrt(3), cos(36), sin(72), 5*(3+sqrt(5))/12, - _point_ref(_even_perms([0,1,PHI]))], + ["tetrahedron", "platonic", 4,[3], 2*sqrt(2), sqrt(6)/12, sqrt(2)/4, sqrt(6)/4, 1/6/sqrt(2), + _point_ref([[1,1,1]], sign="even")], + ["cube", "platonic", 6, [4], 2, 1/2, 1/sqrt(2), sqrt(3)/2, 1, + _point_ref([[1,1,1]])], + ["octahedron", "platonic", 8, [3], sqrt(2), sqrt(6)/6, 1/2, sqrt(2)/2, sqrt(2)/3, + _point_ref(_even_perms([1,0,0]))], + ["dodecahedron", "platonic", 12, [5], 2/PHI, sqrt(5/2+11*sqrt(5)/10)/2, (3+sqrt(5))/4, sqrt(3)*PHI/2, (15+7*sqrt(5))/4, + _point_ref(concat([[1,1,1]],_even_perms([0,PHI,1/PHI])))], + ["icosahedron", "platonic", 20, [3], 2, PHI*PHI/2/sqrt(3), cos(36), sin(72), 5*(3+sqrt(5))/12, + _point_ref(_even_perms([0,1,PHI]))], - // Archimedian Solids, listed in order by Wenniger number, W6-W18 + // Archimedian Solids, listed in order by Wenniger number, W6-W18 - ["truncated tetrahedron", "archimedean", 8,[6,3], sqrt(8), sqrt(6)/4, 3*sqrt(2)/4, sqrt(11/8), 23*sqrt(2)/12, - _point_ref(_all_perms([1,1,3]),sign="even")], - ["truncated octahedron", "archimedean", 14, [6,4], sqrt(2), sqrt(6)/2, 1.5, sqrt(10)/2, 8*sqrt(2), - _point_ref(_all_perms([0,1,2]))], - ["truncated cube", "archimedean", 14, [8,3], 2*(sqrt(2)-1), (1+sqrt(2))/2, 1+sqrt(2)/2, sqrt(7+4*sqrt(2))/2, 7+14*sqrt(2)/3, - _point_ref(_all_perms([1,1,sqrt(2)-1]))], - ["truncated icosahedron", "archimedean", 32, [6, 5], 2, (3*sqrt(3)+sqrt(15))/4, 3*PHI/2, sqrt(58+18*sqrt(5))/4, (125+43*sqrt(5))/4, - _point_ref(concat( - _even_perms([0,1,3*PHI]), - _even_perms([1,2+PHI,2*PHI]), - _even_perms([PHI,2,PHI*PHI*PHI]) - ))], - ["truncated dodecahedron", "archimedean", 32, [10, 3], 2*PHI-2, sqrt(7+11*PHI)/2, (3*PHI+1)/2,sqrt(11+PHI*15)/2, 5*(99+47*sqrt(5))/12, - _point_ref(concat( - _even_perms([0,1/PHI, 2+PHI]), - _even_perms([1/PHI,PHI,2*PHI]), - _even_perms([PHI,2,PHI+1]) - ))], - ["cuboctahedron", "archimedean", 14, [4,3], sqrt(2), sqrt(2)/2, sqrt(3)/2, 1, 5*sqrt(2)/3, - _point_ref(_all_perms([1,1,0]))], - ["icosidodecahedron", "archimedean", 32, [5,3], 1, sqrt(5*(5+2*sqrt(5)))/5,sqrt(5+2*sqrt(5))/2, PHI, (14+17*PHI)/3, - _point_ref(concat(_even_perms([0,0,PHI]),_even_perms([1/2,PHI/2,PHI*PHI/2])))], - ["rhombicuboctahedron", "archimedean", 26, [4, 3], 2, (1+sqrt(2))/2, sqrt(2*(2+sqrt(2)))/2, sqrt(5+2*sqrt(2))/2, 4+10*sqrt(2)/3, - _point_ref(_even_perms([1,1,1+sqrt(2)]))], - ["rhombicosidodecahedron", "archimedean", 62, [5,4,3], 2, 3/10*sqrt(15+20*PHI), sqrt(3/2+2*PHI), sqrt(8*PHI+7)/2, (31+58*PHI)/3, - _point_ref(concat( - _even_perms([1,1,PHI*PHI*PHI]), - _even_perms([PHI*PHI,PHI,2*PHI]), - _even_perms([2+PHI,0,PHI*PHI]) - ))], - ["truncated cuboctahedron", "archimedean", 26, [8, 6, 4], 2, (1+2*sqrt(2))/2, sqrt(6*(2+sqrt(2)))/2, sqrt(13+6*sqrt(2))/2, (22+14*sqrt(2)), - _point_ref(_all_perms([1,1+sqrt(2), 1+2*sqrt(2)]))], - ["truncated icosidodecahedron", "archimedean", 62, [10,6,4], 2*PHI - 2, sqrt(15/4+5*PHI),sqrt(9/2+6*PHI),sqrt(19/4+6*PHI), 95+50*sqrt(5), - _point_ref(concat( - _even_perms([1/PHI,1/PHI,3+PHI]), - _even_perms([2/PHI,PHI,1+2*PHI]), - _even_perms([1/PHI,PHI*PHI,3*PHI-1]), - _even_perms([2*PHI-1,2,2+PHI]), - _even_perms([PHI,3,2*PHI]) - ))], - ["snub cube", "archimedean", 38, [4,3], 1.60972,1.14261350892596209,1.24722316799364325, 1.34371337374460170, - sqrt((613*_tribonacci+203)/(9*(35*_tribonacci-62))), - concat( - _point_ref(_even_perms([1,1/_tribonacci,_tribonacci]), sign="odd"), - _point_ref(_even_perms([1,_tribonacci,1/_tribonacci]), sign="even") - )], - ["snub dodecahedron", "archimedean", 92, [5, 3], 1, 1.98091594728184,2.097053835252087,2.155837375115, 37.61664996273336, - concat( - _point_ref(_even_perms([0.374821658114562,0.330921024729844,2.097053835252088]), sign="odd"), - _point_ref(_even_perms([0.192893711352359,1.249503788463027,1.746186440985827]), sign="odd"), - _point_ref(_even_perms([1.103156835071754,0.847550046789061,1.646917940690374]), sign="odd"), - _point_ref(_even_perms([0.567715369466922,0.643029605914072,1.977838965420219]), sign="even"), - _point_ref(_even_perms([1.415265416255982,0.728335176957192,1.454024229338015]), sign="even") - )], + ["truncated tetrahedron", "archimedean", 8,[6,3], sqrt(8), sqrt(6)/4, 3*sqrt(2)/4, sqrt(11/8), 23*sqrt(2)/12, + _point_ref(_all_perms([1,1,3]),sign="even")], + ["truncated octahedron", "archimedean", 14, [6,4], sqrt(2), sqrt(6)/2, 1.5, sqrt(10)/2, 8*sqrt(2), + _point_ref(_all_perms([0,1,2]))], + ["truncated cube", "archimedean", 14, [8,3], 2*(sqrt(2)-1), (1+sqrt(2))/2, 1+sqrt(2)/2, sqrt(7+4*sqrt(2))/2, 7+14*sqrt(2)/3, + _point_ref(_all_perms([1,1,sqrt(2)-1]))], + ["truncated icosahedron", "archimedean", 32, [6, 5], 2, (3*sqrt(3)+sqrt(15))/4, 3*PHI/2, sqrt(58+18*sqrt(5))/4, (125+43*sqrt(5))/4, + _point_ref(concat( + _even_perms([0,1,3*PHI]), + _even_perms([1,2+PHI,2*PHI]), + _even_perms([PHI,2,PHI*PHI*PHI]) + ))], + ["truncated dodecahedron", "archimedean", 32, [10, 3], 2*PHI-2, sqrt(7+11*PHI)/2, (3*PHI+1)/2,sqrt(11+PHI*15)/2, 5*(99+47*sqrt(5))/12, + _point_ref(concat( + _even_perms([0,1/PHI, 2+PHI]), + _even_perms([1/PHI,PHI,2*PHI]), + _even_perms([PHI,2,PHI+1]) + ))], + ["cuboctahedron", "archimedean", 14, [4,3], sqrt(2), sqrt(2)/2, sqrt(3)/2, 1, 5*sqrt(2)/3, + _point_ref(_all_perms([1,1,0]))], + ["icosidodecahedron", "archimedean", 32, [5,3], 1, sqrt(5*(5+2*sqrt(5)))/5,sqrt(5+2*sqrt(5))/2, PHI, (14+17*PHI)/3, + _point_ref(concat(_even_perms([0,0,PHI]),_even_perms([1/2,PHI/2,PHI*PHI/2])))], + ["rhombicuboctahedron", "archimedean", 26, [4, 3], 2, (1+sqrt(2))/2, sqrt(2*(2+sqrt(2)))/2, sqrt(5+2*sqrt(2))/2, 4+10*sqrt(2)/3, + _point_ref(_even_perms([1,1,1+sqrt(2)]))], + ["rhombicosidodecahedron", "archimedean", 62, [5,4,3], 2, 3/10*sqrt(15+20*PHI), sqrt(3/2+2*PHI), sqrt(8*PHI+7)/2, (31+58*PHI)/3, + _point_ref(concat( + _even_perms([1,1,PHI*PHI*PHI]), + _even_perms([PHI*PHI,PHI,2*PHI]), + _even_perms([2+PHI,0,PHI*PHI]) + ))], + ["truncated cuboctahedron", "archimedean", 26, [8, 6, 4], 2, (1+2*sqrt(2))/2, sqrt(6*(2+sqrt(2)))/2, sqrt(13+6*sqrt(2))/2, (22+14*sqrt(2)), + _point_ref(_all_perms([1,1+sqrt(2), 1+2*sqrt(2)]))], + ["truncated icosidodecahedron", "archimedean", 62, [10,6,4], 2*PHI - 2, sqrt(15/4+5*PHI),sqrt(9/2+6*PHI),sqrt(19/4+6*PHI), 95+50*sqrt(5), + _point_ref(concat( + _even_perms([1/PHI,1/PHI,3+PHI]), + _even_perms([2/PHI,PHI,1+2*PHI]), + _even_perms([1/PHI,PHI*PHI,3*PHI-1]), + _even_perms([2*PHI-1,2,2+PHI]), + _even_perms([PHI,3,2*PHI]) + ))], + ["snub cube", "archimedean", 38, [4,3], 1.60972,1.14261350892596209,1.24722316799364325, 1.34371337374460170, + sqrt((613*_tribonacci+203)/(9*(35*_tribonacci-62))), + concat( + _point_ref(_even_perms([1,1/_tribonacci,_tribonacci]), sign="odd"), + _point_ref(_even_perms([1,_tribonacci,1/_tribonacci]), sign="even") + )], + ["snub dodecahedron", "archimedean", 92, [5, 3], 1, 1.98091594728184,2.097053835252087,2.155837375115, 37.61664996273336, + concat( + _point_ref(_even_perms([0.374821658114562,0.330921024729844,2.097053835252088]), sign="odd"), + _point_ref(_even_perms([0.192893711352359,1.249503788463027,1.746186440985827]), sign="odd"), + _point_ref(_even_perms([1.103156835071754,0.847550046789061,1.646917940690374]), sign="odd"), + _point_ref(_even_perms([0.567715369466922,0.643029605914072,1.977838965420219]), sign="even"), + _point_ref(_even_perms([1.415265416255982,0.728335176957192,1.454024229338015]), sign="even") + )], - // Catalan Solids, the duals to the Archimedean solids, listed in the corresponding order + // Catalan Solids, the duals to the Archimedean solids, listed in the corresponding order - ["triakis tetrahedron","catalan", 12, [3], 9/5, 5*sqrt(22)/44, 5*sqrt(2)/12, 5*sqrt(6)/12, 25*sqrt(2)/36, - concat( - _point_ref([9*sqrt(2)/20*[1,1,1]],sign="even"), - _point_ref([3*sqrt(2)/4*[1,1,1]],sign="odd") - )], - ["tetrakis hexahedron", "catalan", 24, [3], 1, 2/sqrt(5), 2*sqrt(2)/3, 2/sqrt(3), 32/9, - _point_ref(concat([[2/3,2/3,2/3]],_even_perms([1,0,0])))], - ["triakis octahedron", "catalan", 24, [3], 2, sqrt(17*(23+16*sqrt(2)))/34, 1/2+sqrt(2)/4,(1+sqrt(2))/2,3/2+sqrt(2), - _point_ref(concat([[1,1,1]],_even_perms([1+sqrt(2),0,0])))], - ["pentakis dodecahedron", "catalan", 60, [3], 1,sqrt(477/436+97*sqrt(5)/218), sqrt(5)/4+11/12, sqrt(7/4+sqrt(5)/3), 125*sqrt(5)/36+205/36, - _point_ref(concat( - _even_perms([0,(5-PHI)/6, PHI/2+2/3]), - _even_perms([0,(PHI+1)/2,PHI/2]),[(4*PHI-1)/6 * [1,1,1]] - ))], - ["triakis icosahedron", "catalan", 60, [3], 1, sqrt((139+199*PHI)/244), (8*PHI+1)/10, sqrt(13/8+19/8/sqrt(5)), (13*PHI+3)/2, - _point_ref(concat( - _even_perms([(PHI+7)/10, 0, (8*PHI+1)/10]), - _even_perms([0, 1/2, (PHI+1)/2]),[PHI/2*[1,1,1]] - ))], - ["rhombic dodecahedron", "catalan", 12, [4], sqrt(3), sqrt(2/3), 2*sqrt(2)/3, 2/sqrt(3), 16*sqrt(3)/9, - _point_ref(concat([[1,1,1]], _even_perms([2,0,0])))], - ["rhombic triacontahedron", "catalan", 30,[4], 1, sqrt(1+2/sqrt(5)), 1+1/sqrt(5), (1+sqrt(5))/2, 4*sqrt(5+2*sqrt(5)), - concat( - _point_ref(_even_perms([0,sqrt(1+2/sqrt(5)), sqrt((5+sqrt(5))/10)])), - _point_ref(_even_perms([0,sqrt(2/(5+sqrt(5))), sqrt(1+2/sqrt(5))])), - _point_ref([sqrt((5+sqrt(5))/10)*[1,1,1]]) - )], - ["deltoidal icositetrahedron", "catalan", 24, [4], 2*sqrt(10-sqrt(2))/7, 7*sqrt((7+4*sqrt(2))/(34 * (10-sqrt(2)))), - 7*sqrt(2*(2+sqrt(2)))/sqrt(10-sqrt(2))/4, 7*sqrt(2)/sqrt(10-sqrt(2))/2, - (14+21*sqrt(2))/sqrt(10-sqrt(2)), - _point_ref(concat( - _even_perms([0,1,1]), _even_perms([sqrt(2),0,0]), - _even_perms((4+sqrt(2))/7*[1,1,1]) - ))], - ["deltoidal hexecontahedron", "catalan", 60, [4], sqrt(5*(85-31*sqrt(5)))/11, sqrt(571/164+1269/164/sqrt(5)), 5/4+13/4/sqrt(5), - sqrt(147+65*sqrt(5))/6, sqrt(29530+13204*sqrt(5))/3, - _point_ref(concat( - _even_perms([0,0,sqrt(5)]), - _even_perms([0,(15+sqrt(5))/22, (25+9*sqrt(5))/22]), - _even_perms([0,(5+3*sqrt(5))/6, (5+sqrt(5))/6]), - _even_perms([(5-sqrt(5))/4, sqrt(5)/2, (5+sqrt(5))/4]), - [(5+4*sqrt(5))/11*[1,1,1]] - ))], - ["disdyakis dodecahedron", "catalan", 48, [3], 1,sqrt(249/194+285/194/sqrt(2)) ,(2+3*sqrt(2))/4, sqrt(183/98+213/98/sqrt(2)), - sqrt(6582+4539*sqrt(2))/7, - _point_ref(concat( - _even_perms([sqrt(183/98+213/98/sqrt(2)),0,0]), - _even_perms(sqrt(3+3/sqrt(2))/2 * [1,1,0]),[7/sqrt(6*(10-sqrt(2)))*[1,1,1]] - ))], - ["disdyakis triacontahedron","catalan", 120, [3], sqrt(15*(85-31*sqrt(5)))/11, sqrt(3477/964+7707/964/sqrt(5)), 5/4+13/4/sqrt(5), - sqrt(441+195*sqrt(5))/10,sqrt(17718/5+39612/5/sqrt(5)), - _point_ref(concat( - _even_perms([0,0,3*(5+4*sqrt(5))/11]), - _even_perms([0,(5-sqrt(5))/2,(5+sqrt(5))/2]), - _even_perms([0,(15+9*sqrt(5))/10,3*(5+sqrt(5))/10]), - _even_perms([3*(15+sqrt(5))/44,3*(5+4*sqrt(5))/22, (75+27*sqrt(5))/44]), [sqrt(5)*[1,1,1]] - ))], - ["pentagonal icositetrahedron","catalan",24, [5], 0.593465355971, 1.950681331784, 2.1015938932963, 2.29400105368695, 35.6302020120713, - concat( - _point_ref(_even_perms([0.21879664300048044,0.740183741369857,1.0236561781126901]),sign="even"), - _point_ref(_even_perms([0.21879664300048044,1.0236561781126901,0.740183741369857]),sign="odd"), - _point_ref(_even_perms([1.3614101519264425,0,0])), - _point_ref([0.7401837413698572*[1,1,1]]) - )], - ["pentagonal hexecontahedron", "catalan", 60,[5], 0.58289953474498, 3.499527848905764,3.597624822551189,3.80854772878239, 189.789852066885, - concat( - _point_ref(_even_perms([0.192893711352359,0.218483370127321,2.097053835252087]), sign="even"), - _point_ref(_even_perms([0,0.7554672605165955,1.9778389654202186])), - _point_ref(_even_perms([0,1.888445389283669154,1.1671234364753339])), - _point_ref(_even_perms([0.56771536946692131,0.824957552676275846,1.8654013108176956657]),sign="odd"), - _point_ref(_even_perms([0.37482165811456229,1.13706613386050418,1.746186440985826345]), sign="even"), - _point_ref(_even_perms([0.921228888309550,0.95998770139158,1.6469179406903744]),sign="even"), - _point_ref(_even_perms([0.7283351769571914773,1.2720962825758121,1.5277030708585051]),sign="odd"), - _point_ref([1.222371704903623092*[1,1,1]]) - )], + ["triakis tetrahedron","catalan", 12, [3], 9/5, 5*sqrt(22)/44, 5*sqrt(2)/12, 5*sqrt(6)/12, 25*sqrt(2)/36, + concat( + _point_ref([9*sqrt(2)/20*[1,1,1]],sign="even"), + _point_ref([3*sqrt(2)/4*[1,1,1]],sign="odd") + )], + ["tetrakis hexahedron", "catalan", 24, [3], 1, 2/sqrt(5), 2*sqrt(2)/3, 2/sqrt(3), 32/9, + _point_ref(concat([[2/3,2/3,2/3]],_even_perms([1,0,0])))], + ["triakis octahedron", "catalan", 24, [3], 2, sqrt(17*(23+16*sqrt(2)))/34, 1/2+sqrt(2)/4,(1+sqrt(2))/2,3/2+sqrt(2), + _point_ref(concat([[1,1,1]],_even_perms([1+sqrt(2),0,0])))], + ["pentakis dodecahedron", "catalan", 60, [3], 1,sqrt(477/436+97*sqrt(5)/218), sqrt(5)/4+11/12, sqrt(7/4+sqrt(5)/3), 125*sqrt(5)/36+205/36, + _point_ref(concat( + _even_perms([0,(5-PHI)/6, PHI/2+2/3]), + _even_perms([0,(PHI+1)/2,PHI/2]),[(4*PHI-1)/6 * [1,1,1]] + ))], + ["triakis icosahedron", "catalan", 60, [3], 1, sqrt((139+199*PHI)/244), (8*PHI+1)/10, sqrt(13/8+19/8/sqrt(5)), (13*PHI+3)/2, + _point_ref(concat( + _even_perms([(PHI+7)/10, 0, (8*PHI+1)/10]), + _even_perms([0, 1/2, (PHI+1)/2]),[PHI/2*[1,1,1]] + ))], + ["rhombic dodecahedron", "catalan", 12, [4], sqrt(3), sqrt(2/3), 2*sqrt(2)/3, 2/sqrt(3), 16*sqrt(3)/9, + _point_ref(concat([[1,1,1]], _even_perms([2,0,0])))], + ["rhombic triacontahedron", "catalan", 30,[4], 1, sqrt(1+2/sqrt(5)), 1+1/sqrt(5), (1+sqrt(5))/2, 4*sqrt(5+2*sqrt(5)), + concat( + _point_ref(_even_perms([0,sqrt(1+2/sqrt(5)), sqrt((5+sqrt(5))/10)])), + _point_ref(_even_perms([0,sqrt(2/(5+sqrt(5))), sqrt(1+2/sqrt(5))])), + _point_ref([sqrt((5+sqrt(5))/10)*[1,1,1]]) + )], + ["deltoidal icositetrahedron", "catalan", 24, [4], 2*sqrt(10-sqrt(2))/7, 7*sqrt((7+4*sqrt(2))/(34 * (10-sqrt(2)))), + 7*sqrt(2*(2+sqrt(2)))/sqrt(10-sqrt(2))/4, 7*sqrt(2)/sqrt(10-sqrt(2))/2, + (14+21*sqrt(2))/sqrt(10-sqrt(2)), + _point_ref(concat( + _even_perms([0,1,1]), _even_perms([sqrt(2),0,0]), + _even_perms((4+sqrt(2))/7*[1,1,1]) + ))], + ["deltoidal hexecontahedron", "catalan", 60, [4], sqrt(5*(85-31*sqrt(5)))/11, sqrt(571/164+1269/164/sqrt(5)), 5/4+13/4/sqrt(5), + sqrt(147+65*sqrt(5))/6, sqrt(29530+13204*sqrt(5))/3, + _point_ref(concat( + _even_perms([0,0,sqrt(5)]), + _even_perms([0,(15+sqrt(5))/22, (25+9*sqrt(5))/22]), + _even_perms([0,(5+3*sqrt(5))/6, (5+sqrt(5))/6]), + _even_perms([(5-sqrt(5))/4, sqrt(5)/2, (5+sqrt(5))/4]), + [(5+4*sqrt(5))/11*[1,1,1]] + ))], + ["disdyakis dodecahedron", "catalan", 48, [3], 1,sqrt(249/194+285/194/sqrt(2)) ,(2+3*sqrt(2))/4, sqrt(183/98+213/98/sqrt(2)), + sqrt(6582+4539*sqrt(2))/7, + _point_ref(concat( + _even_perms([sqrt(183/98+213/98/sqrt(2)),0,0]), + _even_perms(sqrt(3+3/sqrt(2))/2 * [1,1,0]),[7/sqrt(6*(10-sqrt(2)))*[1,1,1]] + ))], + ["disdyakis triacontahedron","catalan", 120, [3], sqrt(15*(85-31*sqrt(5)))/11, sqrt(3477/964+7707/964/sqrt(5)), 5/4+13/4/sqrt(5), + sqrt(441+195*sqrt(5))/10,sqrt(17718/5+39612/5/sqrt(5)), + _point_ref(concat( + _even_perms([0,0,3*(5+4*sqrt(5))/11]), + _even_perms([0,(5-sqrt(5))/2,(5+sqrt(5))/2]), + _even_perms([0,(15+9*sqrt(5))/10,3*(5+sqrt(5))/10]), + _even_perms([3*(15+sqrt(5))/44,3*(5+4*sqrt(5))/22, (75+27*sqrt(5))/44]), [sqrt(5)*[1,1,1]] + ))], + ["pentagonal icositetrahedron","catalan",24, [5], 0.593465355971, 1.950681331784, 2.1015938932963, 2.29400105368695, 35.6302020120713, + concat( + _point_ref(_even_perms([0.21879664300048044,0.740183741369857,1.0236561781126901]),sign="even"), + _point_ref(_even_perms([0.21879664300048044,1.0236561781126901,0.740183741369857]),sign="odd"), + _point_ref(_even_perms([1.3614101519264425,0,0])), + _point_ref([0.7401837413698572*[1,1,1]]) + )], + ["pentagonal hexecontahedron", "catalan", 60,[5], 0.58289953474498, 3.499527848905764,3.597624822551189,3.80854772878239, 189.789852066885, + concat( + _point_ref(_even_perms([0.192893711352359,0.218483370127321,2.097053835252087]), sign="even"), + _point_ref(_even_perms([0,0.7554672605165955,1.9778389654202186])), + _point_ref(_even_perms([0,1.888445389283669154,1.1671234364753339])), + _point_ref(_even_perms([0.56771536946692131,0.824957552676275846,1.8654013108176956657]),sign="odd"), + _point_ref(_even_perms([0.37482165811456229,1.13706613386050418,1.746186440985826345]), sign="even"), + _point_ref(_even_perms([0.921228888309550,0.95998770139158,1.6469179406903744]),sign="even"), + _point_ref(_even_perms([0.7283351769571914773,1.2720962825758121,1.5277030708585051]),sign="odd"), + _point_ref([1.222371704903623092*[1,1,1]]) + )], ]; _stellated_polyhedra_ = [ - ["great dodecahedron", "icosahedron", -sqrt(5/3-PHI)], - ["small stellated dodecahedron", "dodecahedron", sqrt((5+2*sqrt(5))/5)], - ["great stellated dodecahedron", "icosahedron", sqrt(2/3+PHI)], + ["great dodecahedron", "icosahedron", -sqrt(5/3-PHI)], + ["small stellated dodecahedron", "dodecahedron", sqrt((5+2*sqrt(5))/5)], + ["great stellated dodecahedron", "icosahedron", sqrt(2/3+PHI)], ]; @@ -587,130 +587,130 @@ _stellated_polyhedra_ = [ // longside = Specify the long side length for a trapezohedron. Ignored for other shapes. // h = Specify the height of the apex for a trapezohedron. Ignored for other shapes. function regular_polyhedron_info( - info=undef, name=undef, - index=undef, type=undef, - faces=undef, facetype=undef, - hasfaces=undef, side=1, - ir=undef, mr=undef, or=undef, - r=undef, d=undef, - anchor=[0,0,0], center=undef, - facedown=true, stellate=false, - longside=undef, h=undef // special parameters for trapezohedron + info=undef, name=undef, + index=undef, type=undef, + faces=undef, facetype=undef, + hasfaces=undef, side=1, + ir=undef, mr=undef, or=undef, + r=undef, d=undef, + anchor=[0,0,0], center=undef, + facedown=true, stellate=false, + longside=undef, h=undef // special parameters for trapezohedron ) = let( - anchor = !is_undef(center) ? [0,0,0] : anchor, - argcount = num_defined([ir,mr,or,r,d]) - ) - assert(argcount<=1, "You must specify only one of 'ir', 'mr', 'or', 'r', and 'd'") - let( - ////////////////////// - //Index values into the _polyhedra_ array - // - pname = 0, // name of polyhedron - class = 1, // class name (e.g. platonic, archimedean) - facecount = 2, // number of faces - facevertices = 3, // vertices on the faces, e.g. [3] for all triangles, [3,4] for triangles and squares - edgelen = 4, // length of the edge for the vertex list in the database - in_radius = 5, // in radius for unit polyhedron (shortest side 1) - mid_radius = 6, // mid radius for unit polyhedron - out_radius = 7, // out radius for unit polyhedron - volume = 8, // volume of unit polyhedron (data not validated, not used right now) - vertices = 9, // vertex list (in arbitrary order) - ////////////////////// - r = !is_undef(d) ? d/2 : r, - or = !is_undef(r) ? r : or, - stellate_index = search([name], _stellated_polyhedra_, 1, 0)[0], - name = stellate_index==[] ? name : _stellated_polyhedra_[stellate_index][1], - stellate = stellate_index==[] ? stellate : _stellated_polyhedra_[stellate_index][2], - indexlist = ( - name=="trapezohedron" ? [0] : [ // dumy list of one item - for(i=[0:1:len(_polyhedra_)-1]) ( - if ( - (is_undef(name) || _polyhedra_[i][pname]==name) && - (is_undef(type) || _polyhedra_[i][class]==type) && - (is_undef(faces) || _polyhedra_[i][facecount]==faces) && - ( - is_undef(facetype) || 0==compare_lists( - is_list(facetype)? reverse(sort(facetype)) : [facetype], - _polyhedra_[i][facevertices] - ) - ) && - (is_undef(hasfaces) || any([for (ft=hasfaces) in_list(ft,_polyhedra_[i][facevertices])])) - ) i - ) - ] - ) - ) - assert(len(indexlist)>0, "No polyhedra meet your specification") - let(validindex = is_undef(index) || (index>=0 && index0, "No polyhedra meet your specification") + let(validindex = is_undef(index) || (index>=0 && index0 ? 1 : -1], - maxvertex = len(vertices), - newpts = [for(i=[0:1:len(faces)-1]) mean(select(vertices,faces[i]))+stellate*scalefactor*faces_normals[1][i]], - newfaces = [for(i=[0:1:len(faces)-1], j=[0:len(faces[i])-1]) concat([i+maxvertex],select(faces[i], [j, j+direction[i]]))], - allpts = concat(vertices, newpts), - normals = [for(face=newfaces) _facenormal(allpts,face)] - ) [newfaces, normals, allpts]; + (stellate == false || stellate == 0)? concat(faces_normals,[vertices]) : + let( + faces = [for(face=faces_normals[0]) select(face,hull(select(vertices,face)))], + direction = [for(i=[0:1:len(faces)-1]) _facenormal(vertices, faces[i])*faces_normals[1][i]>0 ? 1 : -1], + maxvertex = len(vertices), + newpts = [for(i=[0:1:len(faces)-1]) mean(select(vertices,faces[i]))+stellate*scalefactor*faces_normals[1][i]], + newfaces = [for(i=[0:1:len(faces)-1], j=[0:len(faces[i])-1]) concat([i+maxvertex],select(faces[i], [j, j+direction[i]]))], + allpts = concat(vertices, newpts), + normals = [for(face=newfaces) _facenormal(allpts,face)] + ) [newfaces, normals, allpts]; function trapezohedron(faces, r, side, longside, h) = - assert(faces%2==0, "Number of faces must be even") - let( - N = faces/2, - parmcount = num_defined([r,side,longside,h]) - ) - assert(parmcount==2,"Must define exactly two of 'r', 'side', 'longside', and 'height'") - let( - separation = ( - !is_undef(h) ? 2*h*sqr(tan(90/N)) : - (!is_undef(r) && !is_undef(side))? sqrt(side*side+2*r*r*(cos(180/N)-1)) : - (!is_undef(r) && !is_undef(longside))? 2 * sqrt(sqr(longside)-sqr(r)) / (1-sqr(tan(90/N))) * sqr(tan(90/N)) : - 2*sqr(sin(90/N))*sqrt((sqr(side) + 2*sqr(longside)*(cos(180/N)-1)) / (cos(180/N)-1) / (cos(180/N)+cos(360/N))) - ) - ) - assert(separation==separation, "Impossible trapezohedron specification") - let( - h = !is_undef(h) ? h : 0.5*separation / sqr(tan(90/N)), - r = ( - !is_undef(r) ? r : - !is_undef(side) ? sqrt((sqr(separation) - sqr(side))/2/(cos(180/N)-1)) : - sqrt(sqr(longside) - sqr(h-separation/2)) - ), - top = [for(i=[0:1:N-1]) [r*cos(360/N*i), r*sin(360/N*i),separation/2]], - bot = [for(i=[0:1:N-1]) [r*cos(180/N+360/N*i), r*sin(180/N+360/N*i),-separation/2]], - vertices = concat([[0,0,h],[0,0,-h]],top,bot) - ) [ - "trapezohedron", "trapezohedron", faces, [4], - !is_undef(side)? side : sqrt(sqr(separation)-2*r*(cos(180/N)-1)), // actual side length - h*r/sqrt(r*r+sqr(h+separation/2)), // in_radius - h*r/sqrt(r*r+sqr(h-separation/2)), // mid_radius - max(h,sqrt(r*r+sqr(separation/2))), // out_radius - undef, // volume - vertices - ]; + assert(faces%2==0, "Number of faces must be even") + let( + N = faces/2, + parmcount = num_defined([r,side,longside,h]) + ) + assert(parmcount==2,"Must define exactly two of 'r', 'side', 'longside', and 'height'") + let( + separation = ( + !is_undef(h) ? 2*h*sqr(tan(90/N)) : + (!is_undef(r) && !is_undef(side))? sqrt(side*side+2*r*r*(cos(180/N)-1)) : + (!is_undef(r) && !is_undef(longside))? 2 * sqrt(sqr(longside)-sqr(r)) / (1-sqr(tan(90/N))) * sqr(tan(90/N)) : + 2*sqr(sin(90/N))*sqrt((sqr(side) + 2*sqr(longside)*(cos(180/N)-1)) / (cos(180/N)-1) / (cos(180/N)+cos(360/N))) + ) + ) + assert(separation==separation, "Impossible trapezohedron specification") + let( + h = !is_undef(h) ? h : 0.5*separation / sqr(tan(90/N)), + r = ( + !is_undef(r) ? r : + !is_undef(side) ? sqrt((sqr(separation) - sqr(side))/2/(cos(180/N)-1)) : + sqrt(sqr(longside) - sqr(h-separation/2)) + ), + top = [for(i=[0:1:N-1]) [r*cos(360/N*i), r*sin(360/N*i),separation/2]], + bot = [for(i=[0:1:N-1]) [r*cos(180/N+360/N*i), r*sin(180/N+360/N*i),-separation/2]], + vertices = concat([[0,0,h],[0,0,-h]],top,bot) + ) [ + "trapezohedron", "trapezohedron", faces, [4], + !is_undef(side)? side : sqrt(sqr(separation)-2*r*(cos(180/N)-1)), // actual side length + h*r/sqrt(r*r+sqr(h+separation/2)), // in_radius + h*r/sqrt(r*r+sqr(h-separation/2)), // mid_radius + max(h,sqrt(r*r+sqr(separation/2))), // out_radius + undef, // volume + vertices + ]; function _facenormal(pts, face) = unit(cross(pts[face[2]]-pts[face[0]], pts[face[1]]-pts[face[0]])); -// hull() function returns triangulated faces. This function identifies the vertices that belong to each face -// by grouping together the face triangles that share normal vectors. The output gives the face polygon +// hull() function returns triangulated faces. This function identifies the vertices that belong to each face +// by grouping together the face triangles that share normal vectors. The output gives the face polygon // point indices in arbitrary order (not usable as input to a polygon call) and a normal vector. function _full_faces(pts,faces) = - let( - normals = [for(face=faces) quant(_facenormal(pts,face),1e-12)], - groups = _unique_groups(normals), - faces = [for(entry=groups) unique(flatten(select(faces, entry)))], - facenormals = [for(entry=groups) normals[entry[0]]] - ) [faces, facenormals]; + let( + normals = [for(face=faces) quant(_facenormal(pts,face),1e-12)], + groups = _unique_groups(normals), + faces = [for(entry=groups) unique(flatten(select(faces, entry)))], + facenormals = [for(entry=groups) normals[entry[0]]] + ) [faces, facenormals]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/primitives.scad b/primitives.scad index e6bb936..60f6e5a 100644 --- a/primitives.scad +++ b/primitives.scad @@ -32,16 +32,16 @@ // stroke(path, closed=true); // move_copies(path) color("blue") circle(d=2,$fn=8); function square(size=1, center, anchor, spin=0) = - let( - anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]), - size = is_num(size)? [size,size] : point2d(size), - path = [ - [ size.x,-size.y], - [-size.x,-size.y], - [-size.x, size.y], - [ size.x, size.y] - ] / 2 - ) reorient(anchor,spin, two_d=true, size=size, p=path); + let( + anchor = get_anchor(anchor, center, [-1,-1], [-1,-1]), + size = is_num(size)? [size,size] : point2d(size), + path = [ + [ size.x,-size.y], + [-size.x,-size.y], + [-size.x, size.y], + [ size.x, size.y] + ] / 2 + ) reorient(anchor,spin, two_d=true, size=size, p=path); // Function&Module: circle() @@ -62,11 +62,11 @@ function square(size=1, center, anchor, spin=0) = // Example(NORENDER): Called as Function // path = circle(d=50, anchor=FRONT, spin=45); function circle(r, d, anchor=CENTER, spin=0) = - let( - r = get_radius(r=r, d=d, dflt=1), - sides = segs(r), - path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]] - ) reorient(anchor,spin, two_d=true, r=r, p=path); + let( + r = get_radius(r=r, d=d, dflt=1), + sides = segs(r), + path = [for (i=[0:1:sides-1]) let(a=360-i*360/sides) r*[cos(a),sin(a)]] + ) reorient(anchor,spin, two_d=true, r=r, p=path); @@ -105,36 +105,36 @@ function circle(r, d, anchor=CENTER, spin=0) = // vnf_polyhedron(vnf); module cube(size=1, center, anchor, spin=0, orient=UP) { - anchor = get_anchor(anchor, center, ALLNEG, ALLNEG); - size = scalar_vec3(size); - attachable(anchor,spin,orient, size=size) { - linear_extrude(height=size.z, center=true, convexity=2) { - square([size.x,size.y], center=true); - } - children(); - } + anchor = get_anchor(anchor, center, ALLNEG, ALLNEG); + size = scalar_vec3(size); + attachable(anchor,spin,orient, size=size) { + linear_extrude(height=size.z, center=true, convexity=2) { + square([size.x,size.y], center=true); + } + children(); + } } function cube(size=1, center, anchor, spin=0, orient=UP) = - let( - siz = scalar_vec3(size), - anchor = get_anchor(anchor, center, ALLNEG, ALLNEG), - unscaled = [ - [-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1], - [-1,-1, 1],[1,-1, 1],[1,1, 1],[-1,1, 1], - ]/2, - verts = is_num(size)? unscaled * size : - is_vector(size,3)? [for (p=unscaled) vmul(p,size)] : - assert(is_num(size) || is_vector(size,3)), - faces = [ - [0,1,2], [0,2,3], //BOTTOM - [0,4,5], [0,5,1], //FRONT - [1,5,6], [1,6,2], //RIGHT - [2,6,7], [2,7,3], //BACK - [3,7,4], [3,4,0], //LEFT - [6,4,7], [6,5,4] //TOP - ] - ) [reorient(anchor,spin,orient, size=siz, p=verts), faces]; + let( + siz = scalar_vec3(size), + anchor = get_anchor(anchor, center, ALLNEG, ALLNEG), + unscaled = [ + [-1,-1,-1],[1,-1,-1],[1,1,-1],[-1,1,-1], + [-1,-1, 1],[1,-1, 1],[1,1, 1],[-1,1, 1], + ]/2, + verts = is_num(size)? unscaled * size : + is_vector(size,3)? [for (p=unscaled) vmul(p,size)] : + assert(is_num(size) || is_vector(size,3)), + faces = [ + [0,1,2], [0,2,3], //BOTTOM + [0,4,5], [0,5,1], //FRONT + [1,5,6], [1,6,2], //RIGHT + [2,6,7], [2,7,3], //BACK + [3,7,4], [3,4,0], //LEFT + [6,4,7], [6,5,4] //TOP + ] + ) [reorient(anchor,spin,orient, size=siz, p=verts), faces]; // Function&Module: cylinder() @@ -183,45 +183,45 @@ function cube(size=1, center, anchor, spin=0, orient=UP) = // } module cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) { - anchor = get_anchor(anchor, center, BOTTOM, BOTTOM); - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); - l = first_defined([h, l, 1]); - sides = segs(max(r1,r2)); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - if(r1>r2) { - linear_extrude(height=l, center=true, convexity=2, scale=r2/r1) { - circle(r=r1); - } - } else { - zflip() { - linear_extrude(height=l, center=true, convexity=2, scale=r1/r2) { - circle(r=r2); - } - } - } - children(); - } + anchor = get_anchor(anchor, center, BOTTOM, BOTTOM); + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); + l = first_defined([h, l, 1]); + sides = segs(max(r1,r2)); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + if(r1>r2) { + linear_extrude(height=l, center=true, convexity=2, scale=r2/r1) { + circle(r=r1); + } + } else { + zflip() { + linear_extrude(height=l, center=true, convexity=2, scale=r1/r2) { + circle(r=r2); + } + } + } + children(); + } } function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) = - let( - anchor = get_anchor(anchor, center, BOTTOM, BOTTOM), - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1), - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1), - l = first_defined([h, l, 1]), - sides = segs(max(r1,r2)), - verts = [ - for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r1*cos(a),r1*sin(a),-l/2], - for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r2*cos(a),r2*sin(a), l/2], - ], - faces = [ - [for (i=[0:1:sides-1]) sides-1-i], - for (i=[0:1:sides-1]) [i, ((i+1)%sides)+sides, i+sides], - for (i=[0:1:sides-1]) [i, (i+1)%sides, ((i+1)%sides)+sides], - [for (i=[0:1:sides-1]) sides+i] - ] - ) [reorient(anchor,spin,orient, l=l, r1=r1, r2=r2, p=verts), faces]; + let( + anchor = get_anchor(anchor, center, BOTTOM, BOTTOM), + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1), + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1), + l = first_defined([h, l, 1]), + sides = segs(max(r1,r2)), + verts = [ + for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r1*cos(a),r1*sin(a),-l/2], + for (i=[0:1:sides-1]) let(a=360*(1-i/sides)) [r2*cos(a),r2*sin(a), l/2], + ], + faces = [ + [for (i=[0:1:sides-1]) sides-1-i], + for (i=[0:1:sides-1]) [i, ((i+1)%sides)+sides, i+sides], + for (i=[0:1:sides-1]) [i, (i+1)%sides, ((i+1)%sides)+sides], + [for (i=[0:1:sides-1]) sides+i] + ] + ) [reorient(anchor,spin,orient, l=l, r1=r1, r2=r2, p=verts), faces]; @@ -268,11 +268,11 @@ function cylinder(h, r1, r2, center, l, r, d, d1, d2, anchor, spin=0, orient=UP) // vnf = sphere(d=100, style="icosa"); // vnf_polyhedron(vnf); module sphere(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orient=UP) - spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient) children(); + spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient) children(); function sphere(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orient=UP) = - spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient); + spheroid(r=r, d=d, circum=circum, style=style, anchor=anchor, spin=spin, orient=orient); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/quaternions.scad b/quaternions.scad index 0f9db2d..40712d9 100644 --- a/quaternions.scad +++ b/quaternions.scad @@ -65,12 +65,12 @@ function QuatZ(a=0) = Quat([0,0,1],a); // Arguments: // a = The triplet of rotation angles, [X,Y,Z] function QuatXYZ(a=[0,0,0]) = - let( - qx = QuatX(a[0]), - qy = QuatY(a[1]), - qz = QuatZ(a[2]) - ) - Q_Mul(qz, Q_Mul(qy, qx)); + let( + qx = QuatX(a[0]), + qy = QuatY(a[1]), + qz = QuatZ(a[2]) + ) + Q_Mul(qz, Q_Mul(qy, qx)); // Function: Q_Ident() @@ -125,10 +125,10 @@ function Q_Sub(a, b) = a-b; // Q_Mul(a, b) // Description: Multiplies quaternion `a` by quaternion `b`. function Q_Mul(a, b) = [ - a[3]*b.x + a.x*b[3] + a.y*b.z - a.z*b.y, - a[3]*b.y - a.x*b.z + a.y*b[3] + a.z*b.x, - a[3]*b.z + a.x*b.y - a.y*b.x + a.z*b[3], - a[3]*b[3] - a.x*b.x - a.y*b.y - a.z*b.z, + a[3]*b.x + a.x*b[3] + a.y*b.z - a.z*b.y, + a[3]*b.y - a.x*b.z + a.y*b[3] + a.z*b.x, + a[3]*b.z + a.x*b.y - a.y*b.x + a.z*b[3], + a[3]*b[3] - a.x*b.x - a.y*b.y - a.z*b.z, ]; @@ -140,14 +140,14 @@ function Q_Mul(a, b) = [ // of each cumulative Quaternion product. It starts with the first quaternion // given in the list, and applies successive quaternion rotations in list order. function Q_Cumulative(v, _i=0, _acc=[]) = - _i==len(v) ? _acc : - Q_Cumulative( - v, _i+1, - concat( - _acc, - [_i==0 ? v[_i] : Q_Mul(v[_i], select(_acc,-1))] - ) - ); + _i==len(v) ? _acc : + Q_Cumulative( + v, _i+1, + concat( + _acc, + [_i==0 ? v[_i] : Q_Mul(v[_i], select(_acc,-1))] + ) + ); // Function: Q_Dot() @@ -215,23 +215,23 @@ function Q_Dist(q1, q2) = norm(q2-q1); // Qrot(q) right(80) cube([10,10,1]); // #sphere(r=80); function Q_Slerp(q1, q2, u) = - assert(is_num(u) || is_num(u[0])) - !is_num(u)? [for (uu=u) Q_Slerp(q1,q2,uu)] : + assert(is_num(u) || is_num(u[0])) + !is_num(u)? [for (uu=u) Q_Slerp(q1,q2,uu)] : let( - q1 = Q_Normalize(q1), - q2 = Q_Normalize(q2), - dot = Q_Dot(q1, q2) - ) let( - q2 = dot<0? Q_Neg(q2) : q2, - dot = dot<0? -dot : dot - ) (dot>0.9995)? Q_Normalize(q1 + (u * (q2-q1))) : - let( - dot = constrain(dot,-1,1), - theta_0 = acos(dot), - theta = theta_0 * u, - q3 = Q_Normalize(q2 - q1*dot), - out = q1*cos(theta) + q3*sin(theta) - ) out; + q1 = Q_Normalize(q1), + q2 = Q_Normalize(q2), + dot = Q_Dot(q1, q2) + ) let( + q2 = dot<0? Q_Neg(q2) : q2, + dot = dot<0? -dot : dot + ) (dot>0.9995)? Q_Normalize(q1 + (u * (q2-q1))) : + let( + dot = constrain(dot,-1,1), + theta_0 = acos(dot), + theta = theta_0 * u, + q3 = Q_Normalize(q2 - q1*dot), + out = q1*cos(theta) + q3*sin(theta) + ) out; // Function: Q_Matrix3() @@ -240,9 +240,9 @@ function Q_Slerp(q1, q2, u) = // Description: // Returns the 3x3 rotation matrix for the given normalized quaternion q. function Q_Matrix3(q) = [ - [1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3]], - [ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3]], - [ 2*q[0]*q[2]-2*q[1]*q[3], 2*q[1]*q[2]+2*q[0]*q[3], 1-2*q[0]*q[0]-2*q[1]*q[1]] + [1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3]], + [ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3]], + [ 2*q[0]*q[2]-2*q[1]*q[3], 2*q[1]*q[2]+2*q[0]*q[3], 1-2*q[0]*q[0]-2*q[1]*q[1]] ]; @@ -252,10 +252,10 @@ function Q_Matrix3(q) = [ // Description: // Returns the 4x4 rotation matrix for the given normalized quaternion q. function Q_Matrix4(q) = [ - [1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3], 0], - [ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3], 0], - [ 2*q[0]*q[2]-2*q[1]*q[3], 2*q[1]*q[2]+2*q[0]*q[3], 1-2*q[0]*q[0]-2*q[1]*q[1], 0], - [ 0, 0, 0, 1] + [1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3], 0], + [ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3], 0], + [ 2*q[0]*q[2]-2*q[1]*q[3], 2*q[1]*q[2]+2*q[0]*q[3], 1-2*q[0]*q[0]-2*q[1]*q[1], 0], + [ 0, 0, 0, 1] ]; @@ -299,15 +299,15 @@ function Q_Angle(q) = 2 * acos(q[3]); // q = QuatXYZ([45,35,10]); // pts = Qrot(q, p=[[2,3,4], [4,5,6], [9,2,3]]); module Qrot(q) { - multmatrix(Q_Matrix4(q)) { - children(); - } + multmatrix(Q_Matrix4(q)) { + children(); + } } function Qrot(q,p) = - is_undef(p)? Q_Matrix4(q) : - is_vector(p)? Qrot(q,[p])[0] : - apply(Q_Matrix4(q), p); + is_undef(p)? Q_Matrix4(q) : + is_vector(p)? Qrot(q,[p])[0] : + apply(Q_Matrix4(q), p); // Module: Qrot_copies() @@ -327,4 +327,4 @@ function Qrot(q,p) = module Qrot_copies(quats) for (q=quats) Qrot(q) children(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/queues.scad b/queues.scad index cb346c0..f2e2d00 100644 --- a/queues.scad +++ b/queues.scad @@ -39,8 +39,8 @@ function queue_init() = []; // queue2 = queue_add(queue, "foo"); // is_empty2 = queue_empty(queue2); // Returns: false function queue_empty(queue) = - assert(is_list(queue)) - len(queue)==0; + assert(is_list(queue)) + len(queue)==0; // Function: queue_size() @@ -58,8 +58,8 @@ function queue_empty(queue) = // queue3 = queue_add(queue2, ["bar","baz","qux"]); // depth3 = queue_size(queue3); // Returns: 4 function queue_size(queue) = - assert(is_list(queue)) - len(queue); + assert(is_list(queue)) + len(queue); // Function: queue_head() @@ -76,16 +76,16 @@ function queue_size(queue) = // item = queue_head(queue); // Returns: 4 // list = queue_head(queue,n=3); // Returns: [4,5,6] function queue_head(queue,n=undef) = - assert(is_list(queue)) - is_undef(n)? ( - queue[0] - ) : ( - let(queuesize = len(queue)) - assert(is_num(n)) - assert(n>=0) - assert(queuesize>=n, "queue underflow") - [for (i=[0:1:n-1]) queue[i]] - ); + assert(is_list(queue)) + is_undef(n)? ( + queue[0] + ) : ( + let(queuesize = len(queue)) + assert(is_num(n)) + assert(n>=0) + assert(queuesize>=n, "queue underflow") + [for (i=[0:1:n-1]) queue[i]] + ); // Function: queue_tail() @@ -102,16 +102,16 @@ function queue_head(queue,n=undef) = // item = queue_tail(queue); // Returns: 9 // list = queue_tail(queue,n=3); // Returns: [7,8,9] function queue_tail(queue,n=undef) = - assert(is_list(queue)) - let(queuesize = len(queue)) - is_undef(n)? ( - queue[queuesize-1] - ) : ( - assert(is_num(n)) - assert(n>=0) - assert(queuesize>=n, "queue underflow") - [for (i=[0:1:n-1]) queue[queuesize-n+i]] - ); + assert(is_list(queue)) + let(queuesize = len(queue)) + is_undef(n)? ( + queue[queuesize-1] + ) : ( + assert(is_num(n)) + assert(n>=0) + assert(queuesize>=n, "queue underflow") + [for (i=[0:1:n-1]) queue[queuesize-n+i]] + ); // Function: queue_peek() @@ -131,19 +131,19 @@ function queue_tail(queue,n=undef) = // item2 = queue_peek(queue, 3); // Returns: 5 // list = queue_peek(queue, 4, 3); // Returns: [6,7,8] function queue_peek(queue,pos=0,n=undef) = - assert(is_list(queue)) - assert(is_num(pos)) - assert(pos>=0) - let(queuesize = len(queue)) - assert(queuesize>=pos, "queue underflow") - is_undef(n)? ( - queue[pos] - ) : ( - assert(is_num(n)) - assert(n>=0) - assert(n=0) + let(queuesize = len(queue)) + assert(queuesize>=pos, "queue underflow") + is_undef(n)? ( + queue[pos] + ) : ( + assert(is_num(n)) + assert(n>=0) + assert(n=0) - let(queuesize = len(queue)) - assert(queuesize>=n, "queue underflow") - [for (i = [n:1:queuesize-1]) queue[i]]; + assert(is_list(queue)) + assert(is_num(n)) + assert(n>=0) + let(queuesize = len(queue)) + assert(queuesize>=n, "queue underflow") + [for (i = [n:1:queuesize-1]) queue[i]]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/regions.scad b/regions.scad index f2b7996..5a64ff9 100644 --- a/regions.scad +++ b/regions.scad @@ -48,13 +48,13 @@ function close_region(region, eps=EPSILON) = [for (path=region) close_path(path, // region(rgn); module region(r) { - points = flatten(r); - paths = [ - for (i=[0:1:len(r)-1]) let( - start = default(sum([for (j=[0:1:i-1]) len(r[j])]),0) - ) [for (k=[0:1:len(r[i])-1]) start+k] - ]; - polygon(points=points, paths=paths); + points = flatten(r); + paths = [ + for (i=[0:1:len(r)-1]) let( + start = default(sum([for (j=[0:1:i-1]) len(r[j])]),0) + ) [for (k=[0:1:len(r[i])-1]) start+k] + ]; + polygon(points=points, paths=paths); } @@ -70,28 +70,28 @@ module region(r) // valid_dim = list of allowed dimensions for the points in the path, e.g. [2,3] to require 2 or 3 dimensional input. If left undefined do not perform this check. Default: undef // closed = set to true if the path is closed, which enables a check for endpoint duplication function check_and_fix_path(path, valid_dim=undef, closed=false) = - let( - path = is_region(path)? ( - assert(len(path)==1,"Region supplied as path does not have exactly one component") - path[0] - ) : ( - assert(is_path(path), "Input is not a path") - path - ), - dim = array_dim(path) - ) - assert(dim[0]>1,"Path must have at least 2 points") - assert(len(dim)==2,"Invalid path: path is either a list of scalars or a list of matrices") - assert(is_def(dim[1]), "Invalid path: entries in the path have variable length") - let(valid=is_undef(valid_dim) || in_list(dim[1],valid_dim)) - assert( - valid, str( - "The points on the path have length ", - dim[1], " but length must be ", - len(valid_dim)==1? valid_dim[0] : str("one of ",valid_dim) - ) - ) - closed && approx(path[0],select(path,-1))? slice(path,0,-2) : path; + let( + path = is_region(path)? ( + assert(len(path)==1,"Region supplied as path does not have exactly one component") + path[0] + ) : ( + assert(is_path(path), "Input is not a path") + path + ), + dim = array_dim(path) + ) + assert(dim[0]>1,"Path must have at least 2 points") + assert(len(dim)==2,"Invalid path: path is either a list of scalars or a list of matrices") + assert(is_def(dim[1]), "Invalid path: entries in the path have variable length") + let(valid=is_undef(valid_dim) || in_list(dim[1],valid_dim)) + assert( + valid, str( + "The points on the path have length ", + dim[1], " but length must be ", + len(valid_dim)==1? valid_dim[0] : str("one of ",valid_dim) + ) + ) + closed && approx(path[0],select(path,-1))? slice(path,0,-2) : path; // Function: cleanup_region() @@ -103,7 +103,7 @@ function check_and_fix_path(path, valid_dim=undef, closed=false) = // region = The region to clean up. Given as a list of polygon paths. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function cleanup_region(region, eps=EPSILON) = - [for (path=region) cleanup_path(path, eps=eps)]; + [for (path=region) cleanup_path(path, eps=eps)]; // Function: point_in_region() @@ -119,9 +119,9 @@ function cleanup_region(region, eps=EPSILON) = // region = The region to test against. Given as a list of polygon paths. // eps = Acceptable variance. Default: `EPSILON` (1e-9) function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) = - (_i >= len(region))? ((_cnt%2==1)? 1 : -1) : let( - pip = point_in_polygon(point, region[_i], eps=eps) - ) pip==0? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>0? 1 : 0)); + (_i >= len(region))? ((_cnt%2==1)? 1 : -1) : let( + pip = point_in_polygon(point, region[_i], eps=eps) + ) pip==0? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>0? 1 : 0)); // Function: region_path_crossings() @@ -135,20 +135,20 @@ function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) = // closed = If true, treat path as a closed polygon. Default: true // eps = Acceptable variance. Default: `EPSILON` (1e-9) function region_path_crossings(path, region, closed=true, eps=EPSILON) = sort([ - let( - segs = pair(closed? close_path(path) : cleanup_path(path)) - ) for ( - si = idx(segs), - p = close_region(region), - s2 = pair(p) - ) let ( - isect = _general_line_intersection(segs[si], s2, eps=eps) - ) if ( - !is_undef(isect) && - isect[1] >= 0-eps && isect[1] < 1+eps && - isect[2] >= 0-eps && isect[2] < 1+eps - ) - [si, isect[1]] + let( + segs = pair(closed? close_path(path) : cleanup_path(path)) + ) for ( + si = idx(segs), + p = close_region(region), + s2 = pair(p) + ) let ( + isect = _general_line_intersection(segs[si], s2, eps=eps) + ) if ( + !is_undef(isect) && + isect[1] >= 0-eps && isect[1] < 1+eps && + isect[2] >= 0-eps && isect[2] < 1+eps + ) + [si, isect[1]] ]); @@ -170,22 +170,22 @@ function region_path_crossings(path, region, closed=true, eps=EPSILON) = sort([ // color("#aaa") region(region); // rainbow(polylines) stroke($item, closed=false, width=2); function split_path_at_region_crossings(path, region, closed=true, eps=EPSILON) = - let( - path = deduplicate(path, eps=eps), - region = [for (path=region) deduplicate(path, eps=eps)], - xings = region_path_crossings(path, region, closed=closed, eps=eps), - crossings = deduplicate( - concat([[0,0]], xings, [[len(path)-1,1]]), - eps=eps - ), - subpaths = [ - for (p = pair(crossings)) - deduplicate(eps=eps, - path_subselect(path, p[0][0], p[0][1], p[1][0], p[1][1], closed=closed) - ) - ] - ) - subpaths; + let( + path = deduplicate(path, eps=eps), + region = [for (path=region) deduplicate(path, eps=eps)], + xings = region_path_crossings(path, region, closed=closed, eps=eps), + crossings = deduplicate( + concat([[0,0]], xings, [[len(path)-1,1]]), + eps=eps + ), + subpaths = [ + for (p = pair(crossings)) + deduplicate(eps=eps, + path_subselect(path, p[0][0], p[0][1], p[1][0], p[1][1], closed=closed) + ) + ] + ) + subpaths; // Function: split_nested_region() @@ -196,75 +196,75 @@ function split_path_at_region_crossings(path, region, closed=true, eps=EPSILON) // Returns a list of regions, such that each returned region has exactly one positive outline // and zero or more void outlines. function split_nested_region(region) = - let( - paths = sort(idx=0, [ - for(i = idx(region)) let( - cnt = sum([ - for (j = idx(region)) if (i!=j) - let(pt = lerp(region[i][0],region[i][1],0.5)) - point_in_polygon(pt, region[j]) >=0 ? 1 : 0 - ]) - ) [cnt, region[i]] - ]), - outs = [ - for (candout = paths) let( - lev = candout[0], - parent = candout[1] - ) if (lev % 2 == 0) [ - clockwise_polygon(parent), - for (path = paths) if ( - path[0] == lev+1 && - point_in_polygon( - lerp(path[1][0], path[1][1], 0.5), - parent - ) >= 0 - ) ccw_polygon(path[1]) - ] - ] - ) outs; + let( + paths = sort(idx=0, [ + for(i = idx(region)) let( + cnt = sum([ + for (j = idx(region)) if (i!=j) + let(pt = lerp(region[i][0],region[i][1],0.5)) + point_in_polygon(pt, region[j]) >=0 ? 1 : 0 + ]) + ) [cnt, region[i]] + ]), + outs = [ + for (candout = paths) let( + lev = candout[0], + parent = candout[1] + ) if (lev % 2 == 0) [ + clockwise_polygon(parent), + for (path = paths) if ( + path[0] == lev+1 && + point_in_polygon( + lerp(path[1][0], path[1][1], 0.5), + parent + ) >= 0 + ) ccw_polygon(path[1]) + ] + ] + ) outs; // Section: Region Extrusion and VNFs function _path_path_closest_vertices(path1,path2) = - let( - dists = [for (i=idx(path1)) let(j=closest_point(path1[i],path2)) [j,norm(path2[j]-path1[i])]], - i1 = min_index(subindex(dists,1)), - i2 = dists[i1][0] - ) [dists[i1][1], i1, i2]; + let( + dists = [for (i=idx(path1)) let(j=closest_point(path1[i],path2)) [j,norm(path2[j]-path1[i])]], + i1 = min_index(subindex(dists,1)), + i2 = dists[i1][0] + ) [dists[i1][1], i1, i2]; function _join_paths_at_vertices(path1,path2,seg1,seg2) = - let( - path1 = close_path(clockwise_polygon(polygon_shift(path1, seg1))), - path2 = close_path(ccw_polygon(polygon_shift(path2, seg2))) - ) cleanup_path(deduplicate([each path1, each path2])); + let( + path1 = close_path(clockwise_polygon(polygon_shift(path1, seg1))), + path2 = close_path(ccw_polygon(polygon_shift(path2, seg2))) + ) cleanup_path(deduplicate([each path1, each path2])); function _cleave_simple_region(region) = - len(region)==0? [] : - len(region)<=1? clockwise_polygon(region[0]) : - let( - dists = [ - for (i=[1:1:len(region)-1]) - _path_path_closest_vertices(region[0],region[i]) - ], - idxi = min_index(subindex(dists,0)), - newoline = _join_paths_at_vertices( - region[0], region[idxi+1], - dists[idxi][1], dists[idxi][2] - ) - ) len(region)==2? clockwise_polygon(newoline) : - let( - orgn = [ - newoline, - for (i=idx(region)) - if (i>0 && i!=idxi+1) - region[i] - ] - ) - assert(len(orgn)0 && i!=idxi+1) + region[i] + ] + ) + assert(len(orgn)maxind)? true : - _segment_good(path,pathseg_unit,pathseg_len, d - 1e-7, shiftsegs[i], alpha) - ]; + let( + maxind = len(path)-(closed ? 1 : 2), + pathseg = [for(i=[0:maxind]) select(path,i+1)-path[i]], + pathseg_len = [for(seg=pathseg) norm(seg)], + pathseg_unit = [for(i=[0:maxind]) pathseg[i]/pathseg_len[i]], + // Order matters because as soon as a valid point is found, the test stops + // This order works better for circular paths because they succeed in the center + alpha = concat([for(i=[1:1:quality]) i/(quality+1)],[0,1]) + ) [ + for (i=[0:len(shiftsegs)-1]) + (i>maxind)? true : + _segment_good(path,pathseg_unit,pathseg_len, d - 1e-7, shiftsegs[i], alpha) + ]; // Determine if a segment is good (approximately) @@ -486,60 +486,60 @@ function _good_segments(path, d, shiftsegs, closed, quality) = // This test is approximate because it only samples the points listed in alpha. Listing more points // will make the test more accurate, but slower. function _segment_good(path,pathseg_unit,pathseg_len, d, seg,alpha ,index=0) = - index == len(alpha) ? false : - _point_dist(path,pathseg_unit,pathseg_len, alpha[index]*seg[0]+(1-alpha[index])*seg[1]) > d ? true : - _segment_good(path,pathseg_unit,pathseg_len,d,seg,alpha,index+1); + index == len(alpha) ? false : + _point_dist(path,pathseg_unit,pathseg_len, alpha[index]*seg[0]+(1-alpha[index])*seg[1]) > d ? true : + _segment_good(path,pathseg_unit,pathseg_len,d,seg,alpha,index+1); // Input is the path, the path segments normalized to unit length, the length of each path segment // and a test point. Computes the (minimum) distance from the path to the point, taking into // account that the minimal distance may be anywhere along a path segment, not just at the ends. function _point_dist(path,pathseg_unit,pathseg_len,pt) = - min([ - for(i=[0:len(pathseg_unit)-1]) let( - v = pt-path[i], - projection = v*pathseg_unit[i], - segdist = projection < 0? norm(pt-path[i]) : - projection > pathseg_len[i]? norm(pt-select(path,i+1)) : - norm(v-projection*pathseg_unit[i]) - ) segdist - ]); + min([ + for(i=[0:len(pathseg_unit)-1]) let( + v = pt-path[i], + projection = v*pathseg_unit[i], + segdist = projection < 0? norm(pt-path[i]) : + projection > pathseg_len[i]? norm(pt-select(path,i+1)) : + norm(v-projection*pathseg_unit[i]) + ) segdist + ]); function _offset_region( - paths, r, delta, chamfer, closed, - maxstep, check_valid, quality, - return_faces, firstface_index, - flip_faces, _acc=[], _i=0 + paths, r, delta, chamfer, closed, + maxstep, check_valid, quality, + return_faces, firstface_index, + flip_faces, _acc=[], _i=0 ) = - _i>=len(paths)? _acc : - _offset_region( - paths, _i=_i+1, - _acc = (paths[_i].x % 2 == 0)? ( - union(_acc, [ - offset( - paths[_i].y, - r=r, delta=delta, chamfer=chamfer, closed=closed, - maxstep=maxstep, check_valid=check_valid, quality=quality, - return_faces=return_faces, firstface_index=firstface_index, - flip_faces=flip_faces - ) - ]) - ) : ( - difference(_acc, [ - offset( - paths[_i].y, - r=-r, delta=-delta, chamfer=chamfer, closed=closed, - maxstep=maxstep, check_valid=check_valid, quality=quality, - return_faces=return_faces, firstface_index=firstface_index, - flip_faces=flip_faces - ) - ]) - ), - r=r, delta=delta, chamfer=chamfer, closed=closed, - maxstep=maxstep, check_valid=check_valid, quality=quality, - return_faces=return_faces, firstface_index=firstface_index, flip_faces=flip_faces - ); + _i>=len(paths)? _acc : + _offset_region( + paths, _i=_i+1, + _acc = (paths[_i].x % 2 == 0)? ( + union(_acc, [ + offset( + paths[_i].y, + r=r, delta=delta, chamfer=chamfer, closed=closed, + maxstep=maxstep, check_valid=check_valid, quality=quality, + return_faces=return_faces, firstface_index=firstface_index, + flip_faces=flip_faces + ) + ]) + ) : ( + difference(_acc, [ + offset( + paths[_i].y, + r=-r, delta=-delta, chamfer=chamfer, closed=closed, + maxstep=maxstep, check_valid=check_valid, quality=quality, + return_faces=return_faces, firstface_index=firstface_index, + flip_faces=flip_faces + ) + ]) + ), + r=r, delta=delta, chamfer=chamfer, closed=closed, + maxstep=maxstep, check_valid=check_valid, quality=quality, + return_faces=return_faces, firstface_index=firstface_index, flip_faces=flip_faces + ); // Function: offset() @@ -635,162 +635,162 @@ function _offset_region( // #linear_extrude(height=1.1) for (p=rgn) stroke(closed=true, width=0.5, p); // region(offset(rgn, r=-5)); function offset( - path, r=undef, delta=undef, chamfer=false, - maxstep=0.1, closed=false, check_valid=true, - quality=1, return_faces=false, firstface_index=0, - flip_faces=false + path, r=undef, delta=undef, chamfer=false, + maxstep=0.1, closed=false, check_valid=true, + quality=1, return_faces=false, firstface_index=0, + flip_faces=false ) = - is_region(path)? ( - assert(!return_faces, "return_faces not supported for regions.") - let( - path = [for (p=path) polygon_is_clockwise(p)? p : reverse(p)], - rgn = exclusive_or([for (p = path) [p]]), - pathlist = sort(idx=0,[ - for (i=[0:1:len(rgn)-1]) [ - sum(concat([0],[ - for (j=[0:1:len(rgn)-1]) if (i!=j) - point_in_polygon(rgn[i][0],rgn[j])>=0? 1 : 0 - ])), - rgn[i] - ] - ]) - ) _offset_region( - pathlist, r=r, delta=delta, chamfer=chamfer, closed=true, - maxstep=maxstep, check_valid=check_valid, quality=quality, - return_faces=return_faces, firstface_index=firstface_index, - flip_faces=flip_faces - ) - ) : let(rcount = num_defined([r,delta])) - assert(rcount==1,"Must define exactly one of 'delta' and 'r'") - let( - chamfer = is_def(r) ? false : chamfer, - quality = max(0,round(quality)), - flip_dir = closed && !polygon_is_clockwise(path)? -1 : 1, - d = flip_dir * (is_def(r) ? r : delta), - shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)], - // good segments are ones where no point on the segment is less than distance d from any point on the path - good = check_valid ? _good_segments(path, abs(d), shiftsegs, closed, quality) : repeat(true,len(shiftsegs)), - goodsegs = bselect(shiftsegs, good), - goodpath = bselect(path,good) - ) - assert(len(goodsegs)>0,"Offset of path is degenerate") - let( - // Extend the shifted segments to their intersection points - sharpcorners = [for(i=[0:len(goodsegs)-1]) _segment_extension(select(goodsegs,i-1), select(goodsegs,i))], - // If some segments are parallel then the extended segments are undefined. This case is not handled - // Note if !closed the last corner doesn't matter, so exclude it - parallelcheck = - (len(sharpcorners)==2 && !closed) || - all_defined(select(sharpcorners,closed?0:1,-1)) - ) - assert(parallelcheck, "Path turns back on itself (180 deg turn)") - let( - // This is a boolean array that indicates whether a corner is an outside or inside corner - // For outside corners, the newcorner is an extension (angle 0), for inside corners, it turns backward - // If either side turns back it is an inside corner---must check both. - // Outside corners can get rounded (if r is specified and there is space to round them) - outsidecorner = [ - for(i=[0:len(goodsegs)-1]) let( - prevseg=select(goodsegs,i-1) - ) ( - (goodsegs[i][1]-goodsegs[i][0]) * - (goodsegs[i][0]-sharpcorners[i]) > 0 - ) && ( - (prevseg[1]-prevseg[0]) * - (sharpcorners[i]-prevseg[1]) > 0 - ) - ], - steps = is_def(delta) ? [] : [ - for(i=[0:len(goodsegs)-1]) + is_region(path)? ( + assert(!return_faces, "return_faces not supported for regions.") + let( + path = [for (p=path) polygon_is_clockwise(p)? p : reverse(p)], + rgn = exclusive_or([for (p = path) [p]]), + pathlist = sort(idx=0,[ + for (i=[0:1:len(rgn)-1]) [ + sum(concat([0],[ + for (j=[0:1:len(rgn)-1]) if (i!=j) + point_in_polygon(rgn[i][0],rgn[j])>=0? 1 : 0 + ])), + rgn[i] + ] + ]) + ) _offset_region( + pathlist, r=r, delta=delta, chamfer=chamfer, closed=true, + maxstep=maxstep, check_valid=check_valid, quality=quality, + return_faces=return_faces, firstface_index=firstface_index, + flip_faces=flip_faces + ) + ) : let(rcount = num_defined([r,delta])) + assert(rcount==1,"Must define exactly one of 'delta' and 'r'") + let( + chamfer = is_def(r) ? false : chamfer, + quality = max(0,round(quality)), + flip_dir = closed && !polygon_is_clockwise(path)? -1 : 1, + d = flip_dir * (is_def(r) ? r : delta), + shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)], + // good segments are ones where no point on the segment is less than distance d from any point on the path + good = check_valid ? _good_segments(path, abs(d), shiftsegs, closed, quality) : repeat(true,len(shiftsegs)), + goodsegs = bselect(shiftsegs, good), + goodpath = bselect(path,good) + ) + assert(len(goodsegs)>0,"Offset of path is degenerate") + let( + // Extend the shifted segments to their intersection points + sharpcorners = [for(i=[0:len(goodsegs)-1]) _segment_extension(select(goodsegs,i-1), select(goodsegs,i))], + // If some segments are parallel then the extended segments are undefined. This case is not handled + // Note if !closed the last corner doesn't matter, so exclude it + parallelcheck = + (len(sharpcorners)==2 && !closed) || + all_defined(select(sharpcorners,closed?0:1,-1)) + ) + assert(parallelcheck, "Path turns back on itself (180 deg turn)") + let( + // This is a boolean array that indicates whether a corner is an outside or inside corner + // For outside corners, the newcorner is an extension (angle 0), for inside corners, it turns backward + // If either side turns back it is an inside corner---must check both. + // Outside corners can get rounded (if r is specified and there is space to round them) + outsidecorner = [ + for(i=[0:len(goodsegs)-1]) let( + prevseg=select(goodsegs,i-1) + ) ( + (goodsegs[i][1]-goodsegs[i][0]) * + (goodsegs[i][0]-sharpcorners[i]) > 0 + ) && ( + (prevseg[1]-prevseg[0]) * + (sharpcorners[i]-prevseg[1]) > 0 + ) + ], + steps = is_def(delta) ? [] : [ + for(i=[0:len(goodsegs)-1]) r==0 ? 0 : - ceil( - abs(r)*vector_angle( - select(goodsegs,i-1)[1]-goodpath[i], - goodsegs[i][0]-goodpath[i] - )*PI/180/maxstep - ) - ], - // If rounding is true then newcorners replaces sharpcorners with rounded arcs where needed - // Otherwise it's the same as sharpcorners - // If rounding is on then newcorners[i] will be the point list that replaces goodpath[i] and newcorners later - // gets flattened. If rounding is off then we set it to [sharpcorners] so we can later flatten it and get - // plain sharpcorners back. - newcorners = is_def(delta) && !chamfer ? [sharpcorners] : [ - for(i=[0:len(goodsegs)-1]) ( - (!chamfer && steps[i] <=2) //Chamfer all points but only round if steps is 3 or more - || !outsidecorner[i] // Don't round inside corners - || (!closed && (i==0 || i==len(goodsegs)-1)) // Don't round ends of an open path - )? [sharpcorners[i]] : ( - chamfer? - _offset_chamfer( - goodpath[i], [ - select(goodsegs,i-1)[1], - sharpcorners[i], - goodsegs[i][0] - ], d - ) : - arc( - cp=goodpath[i], - points=[ - select(goodsegs,i-1)[1], - goodsegs[i][0] - ], - N=steps[i] - ) - ) - ], - pointcount = (is_def(delta) && !chamfer)? - repeat(1,len(sharpcorners)) : - [for(i=[0:len(goodsegs)-1]) len(newcorners[i])], - start = [goodsegs[0][0]], - end = [goodsegs[len(goodsegs)-2][1]], - edges = closed? - flatten(newcorners) : - concat(start,slice(flatten(newcorners),1,-2),end), - faces = !return_faces? [] : - _makefaces( - flip_faces, firstface_index, good, - pointcount, closed - ) - ) return_faces? [edges,faces] : edges; + ceil( + abs(r)*vector_angle( + select(goodsegs,i-1)[1]-goodpath[i], + goodsegs[i][0]-goodpath[i] + )*PI/180/maxstep + ) + ], + // If rounding is true then newcorners replaces sharpcorners with rounded arcs where needed + // Otherwise it's the same as sharpcorners + // If rounding is on then newcorners[i] will be the point list that replaces goodpath[i] and newcorners later + // gets flattened. If rounding is off then we set it to [sharpcorners] so we can later flatten it and get + // plain sharpcorners back. + newcorners = is_def(delta) && !chamfer ? [sharpcorners] : [ + for(i=[0:len(goodsegs)-1]) ( + (!chamfer && steps[i] <=2) //Chamfer all points but only round if steps is 3 or more + || !outsidecorner[i] // Don't round inside corners + || (!closed && (i==0 || i==len(goodsegs)-1)) // Don't round ends of an open path + )? [sharpcorners[i]] : ( + chamfer? + _offset_chamfer( + goodpath[i], [ + select(goodsegs,i-1)[1], + sharpcorners[i], + goodsegs[i][0] + ], d + ) : + arc( + cp=goodpath[i], + points=[ + select(goodsegs,i-1)[1], + goodsegs[i][0] + ], + N=steps[i] + ) + ) + ], + pointcount = (is_def(delta) && !chamfer)? + repeat(1,len(sharpcorners)) : + [for(i=[0:len(goodsegs)-1]) len(newcorners[i])], + start = [goodsegs[0][0]], + end = [goodsegs[len(goodsegs)-2][1]], + edges = closed? + flatten(newcorners) : + concat(start,slice(flatten(newcorners),1,-2),end), + faces = !return_faces? [] : + _makefaces( + flip_faces, firstface_index, good, + pointcount, closed + ) + ) return_faces? [edges,faces] : edges; function _tag_subpaths(path, region, eps=EPSILON) = - let( - subpaths = split_path_at_region_crossings(path, region, eps=eps), - tagged = [ - for (sub = subpaths) let( - subpath = deduplicate(sub) - ) if (len(sub)>1) let( - midpt = lerp(subpath[0], subpath[1], 0.5), - rel = point_in_region(midpt,region,eps=eps) - ) rel<0? ["O", subpath] : rel>0? ["I", subpath] : let( - vec = unit(subpath[1]-subpath[0]), - perp = rot(90, planar=true, p=vec), - sidept = midpt + perp*0.01, - rel1 = point_in_polygon(sidept,path,eps=eps)>0, - rel2 = point_in_region(sidept,region,eps=eps)>0 - ) rel1==rel2? ["S", subpath] : ["U", subpath] - ] - ) tagged; + let( + subpaths = split_path_at_region_crossings(path, region, eps=eps), + tagged = [ + for (sub = subpaths) let( + subpath = deduplicate(sub) + ) if (len(sub)>1) let( + midpt = lerp(subpath[0], subpath[1], 0.5), + rel = point_in_region(midpt,region,eps=eps) + ) rel<0? ["O", subpath] : rel>0? ["I", subpath] : let( + vec = unit(subpath[1]-subpath[0]), + perp = rot(90, planar=true, p=vec), + sidept = midpt + perp*0.01, + rel1 = point_in_polygon(sidept,path,eps=eps)>0, + rel2 = point_in_region(sidept,region,eps=eps)>0 + ) rel1==rel2? ["S", subpath] : ["U", subpath] + ] + ) tagged; function _tag_region_subpaths(region1, region2, eps=EPSILON) = - [for (path=region1) each _tag_subpaths(path, region2, eps=eps)]; + [for (path=region1) each _tag_subpaths(path, region2, eps=eps)]; function _tagged_region(region1,region2,keep1,keep2,eps=EPSILON) = - let( - region1 = close_region(region1, eps=eps), - region2 = close_region(region2, eps=eps), - tagged1 = _tag_region_subpaths(region1, region2, eps=eps), - tagged2 = _tag_region_subpaths(region2, region1, eps=eps), - tagged = concat( - [for (tagpath = tagged1) if (in_list(tagpath[0], keep1)) tagpath[1]], - [for (tagpath = tagged2) if (in_list(tagpath[0], keep2)) tagpath[1]] - ), - outregion = assemble_path_fragments(tagged, eps=eps) - ) outregion; + let( + region1 = close_region(region1, eps=eps), + region2 = close_region(region2, eps=eps), + tagged1 = _tag_region_subpaths(region1, region2, eps=eps), + tagged2 = _tag_region_subpaths(region2, region1, eps=eps), + tagged = concat( + [for (tagpath = tagged1) if (in_list(tagpath[0], keep1)) tagpath[1]], + [for (tagpath = tagged2) if (in_list(tagpath[0], keep2)) tagpath[1]] + ), + outregion = assemble_path_fragments(tagged, eps=eps) + ) outregion; @@ -812,16 +812,16 @@ function _tagged_region(region1,region2,keep1,keep2,eps=EPSILON) = // for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true); // color("green") region(union(shape1,shape2)); function union(regions=[],b=undef,c=undef,eps=EPSILON) = - b!=undef? union(concat([regions],[b],c==undef?[]:[c]), eps=eps) : - len(regions)<=1? regions[0] : - union( - let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)]) - concat( - [_tagged_region(regions[0],regions[1],["O","S"],["O"], eps=eps)], - [for (i=[2:1:len(regions)-1]) regions[i]] - ), - eps=eps - ); + b!=undef? union(concat([regions],[b],c==undef?[]:[c]), eps=eps) : + len(regions)<=1? regions[0] : + union( + let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)]) + concat( + [_tagged_region(regions[0],regions[1],["O","S"],["O"], eps=eps)], + [for (i=[2:1:len(regions)-1]) regions[i]] + ), + eps=eps + ); // Function&Module: difference() @@ -843,16 +843,16 @@ function union(regions=[],b=undef,c=undef,eps=EPSILON) = // for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true); // color("green") region(difference(shape1,shape2)); function difference(regions=[],b=undef,c=undef,eps=EPSILON) = - b!=undef? difference(concat([regions],[b],c==undef?[]:[c]), eps=eps) : - len(regions)<=1? regions[0] : - difference( - let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)]) - concat( - [_tagged_region(regions[0],regions[1],["O","U"],["I"], eps=eps)], - [for (i=[2:1:len(regions)-1]) regions[i]] - ), - eps=eps - ); + b!=undef? difference(concat([regions],[b],c==undef?[]:[c]), eps=eps) : + len(regions)<=1? regions[0] : + difference( + let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)]) + concat( + [_tagged_region(regions[0],regions[1],["O","U"],["I"], eps=eps)], + [for (i=[2:1:len(regions)-1]) regions[i]] + ), + eps=eps + ); // Function&Module: intersection() @@ -873,16 +873,16 @@ function difference(regions=[],b=undef,c=undef,eps=EPSILON) = // for (shape = [shape1,shape2]) color("red") stroke(shape, width=0.5, closed=true); // color("green") region(intersection(shape1,shape2)); function intersection(regions=[],b=undef,c=undef,eps=EPSILON) = - b!=undef? intersection(concat([regions],[b],c==undef?[]:[c]),eps=eps) : - len(regions)<=1? regions[0] : - intersection( - let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)]) - concat( - [_tagged_region(regions[0],regions[1],["I","S"],["I"],eps=eps)], - [for (i=[2:1:len(regions)-1]) regions[i]] - ), - eps=eps - ); + b!=undef? intersection(concat([regions],[b],c==undef?[]:[c]),eps=eps) : + len(regions)<=1? regions[0] : + intersection( + let(regions=[for (r=regions) quant(is_path(r)? [r] : r, 1/65536)]) + concat( + [_tagged_region(regions[0],regions[1],["I","S"],["I"],eps=eps)], + [for (i=[2:1:len(regions)-1]) regions[i]] + ), + eps=eps + ); // Function&Module: exclusive_or() @@ -909,137 +909,137 @@ function intersection(regions=[],b=undef,c=undef,eps=EPSILON) = // circle(d=40); // } function exclusive_or(regions=[],b=undef,c=undef,eps=EPSILON) = - b!=undef? exclusive_or(concat([regions],[b],c==undef?[]:[c]),eps=eps) : - len(regions)<=1? regions[0] : - exclusive_or( - let(regions=[for (r=regions) is_path(r)? [r] : r]) - concat( - [union([ - difference([regions[0],regions[1]], eps=eps), - difference([regions[1],regions[0]], eps=eps) - ], eps=eps)], - [for (i=[2:1:len(regions)-1]) regions[i]] - ), - eps=eps - ); + b!=undef? exclusive_or(concat([regions],[b],c==undef?[]:[c]),eps=eps) : + len(regions)<=1? regions[0] : + exclusive_or( + let(regions=[for (r=regions) is_path(r)? [r] : r]) + concat( + [union([ + difference([regions[0],regions[1]], eps=eps), + difference([regions[1],regions[0]], eps=eps) + ], eps=eps)], + [for (i=[2:1:len(regions)-1]) regions[i]] + ), + eps=eps + ); module exclusive_or() { - if ($children==1) { - children(); - } else if ($children==2) { - difference() { - children(0); - children(1); - } - difference() { - children(1); - children(0); - } - } else if ($children==3) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - } - children(2); - } - } else if ($children==4) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - } - exclusive_or() { - children(2); - children(3); - } - } - } else if ($children==5) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - children(2); - children(3); - } - children(4); - } - } else if ($children==6) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - children(2); - children(3); - } - children(4); - children(5); - } - } else if ($children==7) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - children(2); - children(3); - } - children(4); - children(5); - children(6); - } - } else if ($children==8) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - children(2); - children(3); - } - exclusive_or() { - children(4); - children(5); - children(6); - children(7); - } - } - } else if ($children==9) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - children(2); - children(3); - } - exclusive_or() { - children(4); - children(5); - children(6); - children(7); - } - children(8); - } - } else if ($children==10) { - exclusive_or() { - exclusive_or() { - children(0); - children(1); - children(2); - children(3); - } - exclusive_or() { - children(4); - children(5); - children(6); - children(7); - } - children(8); - children(9); - } - } else { - assert($children<=10, "exclusive_or() can only handle up to 10 children."); - } + if ($children==1) { + children(); + } else if ($children==2) { + difference() { + children(0); + children(1); + } + difference() { + children(1); + children(0); + } + } else if ($children==3) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + } + children(2); + } + } else if ($children==4) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + } + exclusive_or() { + children(2); + children(3); + } + } + } else if ($children==5) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + children(2); + children(3); + } + children(4); + } + } else if ($children==6) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + children(2); + children(3); + } + children(4); + children(5); + } + } else if ($children==7) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + children(2); + children(3); + } + children(4); + children(5); + children(6); + } + } else if ($children==8) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + children(2); + children(3); + } + exclusive_or() { + children(4); + children(5); + children(6); + children(7); + } + } + } else if ($children==9) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + children(2); + children(3); + } + exclusive_or() { + children(4); + children(5); + children(6); + children(7); + } + children(8); + } + } else if ($children==10) { + exclusive_or() { + exclusive_or() { + children(0); + children(1); + children(2); + children(3); + } + exclusive_or() { + children(4); + children(5); + children(6); + children(7); + } + children(8); + children(9); + } + } else { + assert($children<=10, "exclusive_or() can only handle up to 10 children."); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/rounding.scad b/rounding.scad index 266318e..e890ddc 100644 --- a/rounding.scad +++ b/rounding.scad @@ -1548,10 +1548,10 @@ function rounded_prism(bottom, top, joint_bot, joint_top, joint_sides, k_bot, k_ if (norm(top[i]-top_patch[i][4][2]) + norm(bottom[i]-bot_patch[i][4][2]) > norm(bottom[i]-top[i])) i], topbad = [for(i=[0:N-1]) if (norm(top_patch[i][2][4]-top_patch[i][2][2]) + norm(select(top_patch,i+1)[2][0]-select(top_patch,i+1)[2][2]) - > norm(top_patch[i][2][2] - select(top_patch,i+1)[2][2])) [i,(i+1)%N]], + > norm(top_patch[i][2][2] - select(top_patch,i+1)[2][2])) [i,(i+1)%N]], botbad = [for(i=[0:N-1]) if (norm(bot_patch[i][2][4]-bot_patch[i][2][2]) + norm(select(bot_patch,i+1)[2][0]-select(bot_patch,i+1)[2][2]) - > norm(bot_patch[i][2][2] - select(bot_patch,i+1)[2][2])) [i,(i+1)%N]], + > norm(bot_patch[i][2][2] - select(bot_patch,i+1)[2][2])) [i,(i+1)%N]], topinbad = [for(i=[0:N-1]) if (norm(top_patch[i][0][2]-top_patch[i][0][4]) + norm(select(top_patch,i+1)[0][0]-select(top_patch,i+1)[0][2]) > norm(top_patch[i][0][2]-select(top_patch,i+1)[0][2])) [i,(i+1)%N]], @@ -1619,7 +1619,7 @@ function rounded_prism(bottom, top, joint_bot, joint_top, joint_sides, k_bot, k_ each subindex(bot_samples,1), for(pts=edge_points) vnf_vertex_array(pts), vnf_triangulate(vnf_add_faces(EMPTY_VNF,faces)) - ]) + ]) ) debug ? [concat(top_patch, bot_patch), vnf] : vnf; @@ -1942,4 +1942,4 @@ module bent_cutout_mask(r, thickness, path, convexity=10) } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/screws.scad b/screws.scad index 58d799d..c9e9a57 100644 --- a/screws.scad +++ b/screws.scad @@ -493,55 +493,55 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = tind=struct_val([["coarse",0], ["fine",1], ["extra fine",2],["extrafine",2], - ["super fine",3],["superfine",3]], + ["super fine",3],["superfine",3]], downcase(thread)), // coarse fine xfine superfine ISO_thread = [ - [1 , [0.25, 0.2 , undef, undef,]], - [1.2, [0.25, 0.2 , undef, undef,]], - [1.4, [0.3 , 0.2 , undef, undef,]], - [1.6, [0.35, 0.2 , undef, undef,]], - [1.7, [0.35, undef, undef, undef,]], - [1.8, [0.35, 0.2 , undef, undef,]], - [2 , [0.4 , 0.25, undef, undef,]], - [2.2, [0.45, 0.25, undef, undef,]], - [2.3, [0.4 , undef, undef, undef,]], - [2.5, [0.45, 0.35, undef, undef,]], - [2.6, [0.45, undef, undef, undef,]], - [3 , [0.5 , 0.35, undef, undef,]], - [3.5, [0.6 , 0.35, undef, undef,]], - [4 , [0.7 , 0.5 , undef, undef,]], - [5 , [0.8 , 0.5 , undef, undef,]], - [6 , [1 , 0.75, undef, undef,]], - [7 , [1 , 0.75, undef, undef,]], - [8 , [1.25, 1 , 0.75, undef,]], - [9 , [1.25, 1 , 0.75, undef,]], - [10 , [1.5 , 1.25, 1 , 0.75,]], - [11 , [1.5 , 1 , 0.75, undef,]], - [12 , [1.75, 1.5 , 1.25, 1, ]], - [14 , [2 , 1.5 , 1.25, 1, ]], - [16 , [2 , 1.5 , 1 , undef,]], - [18 , [2.5 , 2 , 1.5 , 1, ]], - [20 , [2.5 , 2 , 1.5 , 1, ]], - [22 , [2.5 , 2 , 1.5 , 1,]], - [24 , [3 , 2 , 1.5 , 1,]], - [27 , [3 , 2 , 1.5 , 1,]], - [30 , [3.5 , 3 , 2 , 1.5,]], - [33 , [3.5 , 3 , 2 , 1.5,]], - [36 , [4 , 3 , 2 , 1.5,]], - [39 , [4 , 3 , 2 , 1.5,]], - [42 , [4.5 , 4 , 3 , 2,]], - [45 , [4.5 , 4 , 3 , 2,]], - [48 , [5 , 4 , 3 , 2,]], - [52 , [5 , 4 , 3 , 2,]], - [56 , [5.5 , 4 , 3 , 2,]], - [60 , [5.5 , 4 , 3 , 2,]], - [64 , [6 , 4 , 3 , 2,]], - [68 , [6 , 4 , 3 , 2,]], - [72 , [6 , 4 , 3 , 2,]], - [80 , [6 , 4 , 3 , 2,]], - [90 , [6 , 4 , 3 , 2,]], - [100, [6 , 4 , 3 , 2,]], + [1 , [0.25, 0.2 , undef, undef,]], + [1.2, [0.25, 0.2 , undef, undef,]], + [1.4, [0.3 , 0.2 , undef, undef,]], + [1.6, [0.35, 0.2 , undef, undef,]], + [1.7, [0.35, undef, undef, undef,]], + [1.8, [0.35, 0.2 , undef, undef,]], + [2 , [0.4 , 0.25, undef, undef,]], + [2.2, [0.45, 0.25, undef, undef,]], + [2.3, [0.4 , undef, undef, undef,]], + [2.5, [0.45, 0.35, undef, undef,]], + [2.6, [0.45, undef, undef, undef,]], + [3 , [0.5 , 0.35, undef, undef,]], + [3.5, [0.6 , 0.35, undef, undef,]], + [4 , [0.7 , 0.5 , undef, undef,]], + [5 , [0.8 , 0.5 , undef, undef,]], + [6 , [1 , 0.75, undef, undef,]], + [7 , [1 , 0.75, undef, undef,]], + [8 , [1.25, 1 , 0.75, undef,]], + [9 , [1.25, 1 , 0.75, undef,]], + [10 , [1.5 , 1.25, 1 , 0.75,]], + [11 , [1.5 , 1 , 0.75, undef,]], + [12 , [1.75, 1.5 , 1.25, 1, ]], + [14 , [2 , 1.5 , 1.25, 1, ]], + [16 , [2 , 1.5 , 1 , undef,]], + [18 , [2.5 , 2 , 1.5 , 1, ]], + [20 , [2.5 , 2 , 1.5 , 1, ]], + [22 , [2.5 , 2 , 1.5 , 1,]], + [24 , [3 , 2 , 1.5 , 1,]], + [27 , [3 , 2 , 1.5 , 1,]], + [30 , [3.5 , 3 , 2 , 1.5,]], + [33 , [3.5 , 3 , 2 , 1.5,]], + [36 , [4 , 3 , 2 , 1.5,]], + [39 , [4 , 3 , 2 , 1.5,]], + [42 , [4.5 , 4 , 3 , 2,]], + [45 , [4.5 , 4 , 3 , 2,]], + [48 , [5 , 4 , 3 , 2,]], + [52 , [5 , 4 , 3 , 2,]], + [56 , [5.5 , 4 , 3 , 2,]], + [60 , [5.5 , 4 , 3 , 2,]], + [64 , [6 , 4 , 3 , 2,]], + [68 , [6 , 4 , 3 , 2,]], + [72 , [6 , 4 , 3 , 2,]], + [80 , [6 , 4 , 3 , 2,]], + [90 , [6 , 4 , 3 , 2,]], + [100, [6 , 4 , 3 , 2,]], ] ) struct_val(ISO_thread, diam)[tind], @@ -550,19 +550,19 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = metric_setscrew = [ [1.4, [0.7]], - [1.6, [0.7]], - [1.8, [0.7]], - [2, [0.9]], - [2.5, [1.3]], - [3, [1.5, 6, 0.77]], - [4, [2, 8, 1.05]], - [5, [2.5, 10, 1.24]], - [6, [3, 15, 1.74]], - [8, [4, 25, 2.24]], - [10, [5, 40, 2.97]], - [12, [6, 45, 3.48]], - [16, [8, 55, 5.15]], - [20, [10, undef, undef]], + [1.6, [0.7]], + [1.8, [0.7]], + [2, [0.9]], + [2.5, [1.3]], + [3, [1.5, 6, 0.77]], + [4, [2, 8, 1.05]], + [5, [2.5, 10, 1.24]], + [6, [3, 15, 1.74]], + [8, [4, 25, 2.24]], + [10, [5, 40, 2.97]], + [12, [6, 45, 3.48]], + [16, [8, 55, 5.15]], + [20, [10, undef, undef]], ], entry = struct_val(metric_setscrew, diam), drive_dim = drive=="hex" ? [["drive_size", entry[0]], ["drive_depth", diam/2]] : @@ -572,17 +572,17 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = head=="hex" ? let( metric_hex = [ // flat to flat width, height - [5, [8, 3.5]], - [6, [10,4]], - [8, [13, 5.3]], - [10, [17, 6.4]], - [12, [19, 7.5]], - [14, [22, 8.8]], - [16, [24, 10]], - [18, [27,11.5]], - [20, [30, 12.5]], - [24, [36, 15]], - [30, [46, 18.7]], + [5, [8, 3.5]], + [6, [10,4]], + [8, [13, 5.3]], + [10, [17, 6.4]], + [12, [19, 7.5]], + [14, [22, 8.8]], + [16, [24, 10]], + [18, [27,11.5]], + [20, [30, 12.5]], + [24, [36, 15]], + [30, [46, 18.7]], ], entry = struct_val(metric_hex, diam) ) @@ -591,31 +591,31 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = metric_socket = [ // height = screw diameter //diam, hex [1.4, [2.5, 1.3]], - [1.6, [3, 1.5]], - [2, [3.8, 1.5, 6, 0.77]], - [2.5, [4.5, 2, 8, 1.05]], - [2.6, [5, 2, 8, 1.05]], - [3, [5.5, 2.5, 10, 1.24]], - [3.5, [6.2, 2.5]], - [4, [7, 3, 25, 1.76]], - [5, [8.5, 4, 27, 2.24]], - [6, [10, 5, 30, 2.47]], - [7, [12, 6]], - [8, [13, 6, 45, 3.48]], - [10, [16, 8, 50, 3.93]], - [12, [18, 10, 55, 5.15]], - [14, [21, 12]], - [16, [24, 14, 70, 7.39]], - [18, [27, 14]], - [20, [30, 17, 90, 9.67]], - [22, [33, 17]], - [24, [36, 19, 100, 10.79]], - [27, [40, 19]], - [30, [45, 22]], - [33, [50, 24]], - [36, [54, 27]], - [42, [63, 32]], - [48, [72, 36]], + [1.6, [3, 1.5]], + [2, [3.8, 1.5, 6, 0.77]], + [2.5, [4.5, 2, 8, 1.05]], + [2.6, [5, 2, 8, 1.05]], + [3, [5.5, 2.5, 10, 1.24]], + [3.5, [6.2, 2.5]], + [4, [7, 3, 25, 1.76]], + [5, [8.5, 4, 27, 2.24]], + [6, [10, 5, 30, 2.47]], + [7, [12, 6]], + [8, [13, 6, 45, 3.48]], + [10, [16, 8, 50, 3.93]], + [12, [18, 10, 55, 5.15]], + [14, [21, 12]], + [16, [24, 14, 70, 7.39]], + [18, [27, 14]], + [20, [30, 17, 90, 9.67]], + [22, [33, 17]], + [24, [36, 19, 100, 10.79]], + [27, [40, 19]], + [30, [45, 22]], + [33, [50, 24]], + [36, [54, 27]], + [42, [63, 32]], + [48, [72, 36]], ], entry = struct_val(metric_socket, diam), drive_size = drive=="hex" ? [["drive_size",entry[1]],["drive_depth",diam/2]] : @@ -626,16 +626,16 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = starts_with(head,"pan") ? let ( metric_pan = [ // pan head for phillips or slotted // diam, slotted diam, phillips diam, phillips depth, ph width, slot width,slot depth - [1.6,[3.2, 1 , 1.3, 0, undef,undef,undef, 0.4, 0.35]], - [2, [4, 1.3, 1.6, 1, 1.82, 1.19, 0.48, 0.5, 0.5]], - [2.5,[5, 1.5, 2, 1, 2.68, 1.53, 0.70, 0.6, 0.6]], - [3, [5.6, 1.8, 2.4, 1, 2.90, 1.76, 0.74, 0.8, 0.7]], - [3.5,[7, 2.1, 3.1, 2, 3.92, 1.95, 0.87, 1.0, 0.8]], + [1.6,[3.2, 1 , 1.3, 0, undef,undef,undef, 0.4, 0.35]], + [2, [4, 1.3, 1.6, 1, 1.82, 1.19, 0.48, 0.5, 0.5]], + [2.5,[5, 1.5, 2, 1, 2.68, 1.53, 0.70, 0.6, 0.6]], + [3, [5.6, 1.8, 2.4, 1, 2.90, 1.76, 0.74, 0.8, 0.7]], + [3.5,[7, 2.1, 3.1, 2, 3.92, 1.95, 0.87, 1.0, 0.8]], [4, [8, 2.4 , 3.1, 2, 4.40, 2.45, 0.93, 1.2, 1.0]], - [5, [9.5, 3, 3.8, 2, 4.90, 2.95, 1.00, 1.2, 1.2]], - [6, [12, 3.6, 4.6, 3, 6.92, 3.81, 1.14, 1.6, 1.4]], - [8, [16, 4.8, 6, 4, 9.02, 4.88, 1.69, 2.0, 1.9]], - [10, [20, 6.0, 7.5, 4, 10.18, 5.09, 1.84,2.5, 2.4]], + [5, [9.5, 3, 3.8, 2, 4.90, 2.95, 1.00, 1.2, 1.2]], + [6, [12, 3.6, 4.6, 3, 6.92, 3.81, 1.14, 1.6, 1.4]], + [8, [16, 4.8, 6, 4, 9.02, 4.88, 1.69, 2.0, 1.9]], + [10, [20, 6.0, 7.5, 4, 10.18, 5.09, 1.84,2.5, 2.4]], ], type = head=="pan" ? (drive=="slot" ? "pan flat" : "pan round") : head, htind = drive=="slot" ? 1 : 2, @@ -648,34 +648,34 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = metric_button = [ // button, hex drive // head diam, height, hex, phillips, hex drive depth [1.6, [2.9, 0.8, 0.9, undef, 0.55]], // These four cases, - [2, [3.5, 1.3, 1.3, undef, 0.69]], // extrapolated hex depth - [2.2, [3.8, 0.9, 1.3, undef, 0.76]], // - [2.5, [4.6, 1.5, 1.5, undef, 0.87]], // - [3, [5.7, 1.65, 2, undef, 1.04, 8, 0.81]], - [3.5, [5.7, 1.65, 2, undef, 1.21]], // interpolated hex depth - [4, [7.6, 2.2, 2.5, undef, 1.30, 15, 1.3]], - [5, [9.5, 2.75, 3, undef, 1.56, 25, 1.56]], - [6, [10.5,3.3, 4, undef, 2.08, 27, 2.08]], - [8, [14, 4.4, 5, undef, 2.60, 40, 2.3]], - [10, [17.5,5.5, 6, undef, 3.21, 45, 2.69]], - [12, [21, 6.6, 8, undef, 4.16, 55, 4.02]], - [16, [28, 8.8, 10, undef, 5.55]], // interpolated hex depth + [2, [3.5, 1.3, 1.3, undef, 0.69]], // extrapolated hex depth + [2.2, [3.8, 0.9, 1.3, undef, 0.76]], // + [2.5, [4.6, 1.5, 1.5, undef, 0.87]], // + [3, [5.7, 1.65, 2, undef, 1.04, 8, 0.81]], + [3.5, [5.7, 1.65, 2, undef, 1.21]], // interpolated hex depth + [4, [7.6, 2.2, 2.5, undef, 1.30, 15, 1.3]], + [5, [9.5, 2.75, 3, undef, 1.56, 25, 1.56]], + [6, [10.5,3.3, 4, undef, 2.08, 27, 2.08]], + [8, [14, 4.4, 5, undef, 2.60, 40, 2.3]], + [10, [17.5,5.5, 6, undef, 3.21, 45, 2.69]], + [12, [21, 6.6, 8, undef, 4.16, 55, 4.02]], + [16, [28, 8.8, 10, undef, 5.55]], // interpolated hex depth ], metric_cheese = [ // slotted, phillips ISO 1207, ISO 7048 // head diam, head height, hex drive, phillips drive, slot width, slight depth, ph diam - [1, [2, 0.7, undef, undef]], - [1.2, [2.3,0.8, undef, undef]], - [1.4, [2.6,0.9, undef, undef]], - [1.6, [3, 1, undef, undef, 0.4, 0.45]], - [2, [3.8,1.3, undef, 1 , 0.5, 0.6]], - [2.5, [4.5,1.6, undef, 1 , 0.6, 0.7, 2.7,1.2]], - [3, [5.5,2, undef, 2 , 0.8, 0.85, 3.5,0.86]], - [3.5, [6, 2.4, undef, 2 , 1.0, 1.0, 3.8, 1.15]], - [4, [7, 2.6, undef, 2 , 1.2, 1.1, 4.1, 1.45]], - [5, [8.5,3.3, undef, 2 , 1.2, 1.3, 4.8, 2.14]], - [6, [10, 3.9, undef, 3 , 1.6, 1.6, 6.2, 2.25]], - [8, [13, 5, undef, 3 , 2.0, 2.0, 7.7, 3.73]], - [10, [16, 6, undef, undef, 2.5, 2.4]] + [1, [2, 0.7, undef, undef]], + [1.2, [2.3,0.8, undef, undef]], + [1.4, [2.6,0.9, undef, undef]], + [1.6, [3, 1, undef, undef, 0.4, 0.45]], + [2, [3.8,1.3, undef, 1 , 0.5, 0.6]], + [2.5, [4.5,1.6, undef, 1 , 0.6, 0.7, 2.7,1.2]], + [3, [5.5,2, undef, 2 , 0.8, 0.85, 3.5,0.86]], + [3.5, [6, 2.4, undef, 2 , 1.0, 1.0, 3.8, 1.15]], + [4, [7, 2.6, undef, 2 , 1.2, 1.1, 4.1, 1.45]], + [5, [8.5,3.3, undef, 2 , 1.2, 1.3, 4.8, 2.14]], + [6, [10, 3.9, undef, 3 , 1.6, 1.6, 6.2, 2.25]], + [8, [13, 5, undef, 3 , 2.0, 2.0, 7.7, 3.73]], + [10, [16, 6, undef, undef, 2.5, 2.4]] ], entry = struct_val(head=="button" ? metric_button : metric_cheese, diam), drive_index = drive=="phillips" ? 3 : @@ -693,18 +693,18 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = small = head == "flat small" || (head=="flat" && (drive!="hex" && drive!="torx")), metric_flat_large = [ // for hex drive [2, [4, 1.3,undef]], - [2.5,[5, 1.5, undef]], - [3, [6, 2 , 1.1, 10, 0.96]], - [4, [8, 2.5, 1.5, 20, 1.34]], - [5, [10, 3 , 1.9, 25, 1.54]], - [6, [12, 4 , 2.2, 30, 1.91]], - [8, [16, 5 , 3.0, 40, 2.3]], - [10, [20, 6 , 3.6, 50, 3.04]], - [12, [24, 8 ,undef]], - [14, [27, 10 ,undef]], - [16, [30, 10 ,undef]], - [18, [33, 12 ,undef]], - [20, [36, 12 ,undef]], + [2.5,[5, 1.5, undef]], + [3, [6, 2 , 1.1, 10, 0.96]], + [4, [8, 2.5, 1.5, 20, 1.34]], + [5, [10, 3 , 1.9, 25, 1.54]], + [6, [12, 4 , 2.2, 30, 1.91]], + [8, [16, 5 , 3.0, 40, 2.3]], + [10, [20, 6 , 3.6, 50, 3.04]], + [12, [24, 8 ,undef]], + [14, [27, 10 ,undef]], + [16, [30, 10 ,undef]], + [18, [33, 12 ,undef]], + [20, [36, 12 ,undef]], ], metric_flat_small = [ // for phillips, slotted // Phillips from ASME B18.6.7M (ISO 7046 gives different values), @@ -715,20 +715,20 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = [1.6, [2.6, 0,undef,undef,undef, 0.3, 0.28]], [1.6, [3, 0,undef,undef,undef, 0.4, 0.32]], [2, [3.8, 0, 2.14, 1.54, 0.53, 0.5, 0.4]], - [2.5, [4.7, 1, 2.80, 1.78, 0.74, 0.6, 0.5]], - [2.6, [4.7, 1, 2.80, 1.78, 0.74, 0.6, 0.5]], - [3, [5.6, 1, 3.10, 2.08, 0.79, 0.8, 0.6]], - [3.5, [6.5, 1, 4.06, 2.25, 0.91, 0.8, 0.7]], - [4, [7.5, 2, 4.46, 2.65, 0.96, 1.0, 0.8]], - [5, [9.2, 2, 5.06, 3.25, 1.04, 1.2, 1.0]], - [6, [11, 3, 6.62, 3.61, 1.12, 1.6, 1.2]], - [8, [14.5,4, 8.78, 4.88, 1.80, 2.0, 1.6]], - [10, [18,undef,undef,undef,undef,2.5,2 ]], - [12, [22,undef,undef,undef,undef,3,2.4 ]], - [14, [25,undef,undef,undef,undef,3,2.8 ]], - [16, [29,undef,undef,undef,undef,4,3.2 ]], - [18, [33,undef,undef,undef,undef,4,3.6 ]], - [20, [36,undef,undef,undef,undef,5,4 ]], + [2.5, [4.7, 1, 2.80, 1.78, 0.74, 0.6, 0.5]], + [2.6, [4.7, 1, 2.80, 1.78, 0.74, 0.6, 0.5]], + [3, [5.6, 1, 3.10, 2.08, 0.79, 0.8, 0.6]], + [3.5, [6.5, 1, 4.06, 2.25, 0.91, 0.8, 0.7]], + [4, [7.5, 2, 4.46, 2.65, 0.96, 1.0, 0.8]], + [5, [9.2, 2, 5.06, 3.25, 1.04, 1.2, 1.0]], + [6, [11, 3, 6.62, 3.61, 1.12, 1.6, 1.2]], + [8, [14.5,4, 8.78, 4.88, 1.80, 2.0, 1.6]], + [10, [18,undef,undef,undef,undef,2.5,2 ]], + [12, [22,undef,undef,undef,undef,3,2.4 ]], + [14, [25,undef,undef,undef,undef,3,2.8 ]], + [16, [29,undef,undef,undef,undef,4,3.2 ]], + [18, [33,undef,undef,undef,undef,4,3.6 ]], + [20, [36,undef,undef,undef,undef,5,4 ]], ], entry = struct_val(small ? metric_flat_small : metric_flat_large, diam), driveind = small && drive=="phillips" || !small && drive=="hex" ? 1 : !small && drive=="torx" ? 3 : undef, @@ -1330,10 +1330,10 @@ module nut(name, thread="coarse", oversize=0, spec, diameter, thickness, toleran echo(threadspec=threadspec,"for nut threads"); echo(nut_minor_diam = mean(struct_val(threadspec,"d_minor"))); trapezoidal_threaded_nut( - od=diameter, id=mean(struct_val(threadspec, "d_major")), h=thickness, - pitch=struct_val(threadspec, "pitch"), - profile=_thread_profile(threadspec), - bevel=false,anchor=anchor,spin=spin,orient=orient); + od=diameter, id=mean(struct_val(threadspec, "d_major")), h=thickness, + pitch=struct_val(threadspec, "pitch"), + profile=_thread_profile(threadspec), + bevel=false,anchor=anchor,spin=spin,orient=orient); } diff --git a/shapes.scad b/shapes.scad index d80476a..fe6e3ee 100644 --- a/shapes.scad +++ b/shapes.scad @@ -62,242 +62,242 @@ // Example: Standard Connectors // cuboid(40) show_anchors(); module cuboid( - size=[1,1,1], - p1=undef, p2=undef, - chamfer=undef, - rounding=undef, - edges=EDGES_ALL, - except_edges=[], - trimcorners=true, - anchor=CENTER, - spin=0, - orient=UP + size=[1,1,1], + p1=undef, p2=undef, + chamfer=undef, + rounding=undef, + edges=EDGES_ALL, + except_edges=[], + trimcorners=true, + anchor=CENTER, + spin=0, + orient=UP ) { - size = scalar_vec3(size); - edges = edges(edges, except=except_edges); - if (!is_undef(p1)) { - if (!is_undef(p2)) { - translate(pointlist_bounds([p1,p2])[0]) { - cuboid(size=vabs(p2-p1), chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children(); - } - } else { - translate(p1) { - cuboid(size=size, chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children(); - } - } - } else { - if (chamfer != undef) { - if (any(edges[0])) assert(chamfer <= size.y/2 && chamfer <=size.z/2, "chamfer must be smaller than half the cube length or height."); - if (any(edges[1])) assert(chamfer <= size.x/2 && chamfer <=size.z/2, "chamfer must be smaller than half the cube width or height."); - if (any(edges[2])) assert(chamfer <= size.x/2 && chamfer <=size.y/2, "chamfer must be smaller than half the cube width or length."); - } - if (rounding != undef) { - if (any(edges[0])) assert(rounding <= size.y/2 && rounding<=size.z/2, "rounding radius must be smaller than half the cube length or height."); - if (any(edges[1])) assert(rounding <= size.x/2 && rounding<=size.z/2, "rounding radius must be smaller than half the cube width or height."); - if (any(edges[2])) assert(rounding <= size.x/2 && rounding<=size.y/2, "rounding radius must be smaller than half the cube width or length."); - } - majrots = [[0,90,0], [90,0,0], [0,0,0]]; - attachable(anchor,spin,orient, size=size) { - if (chamfer != undef) { - if (edges == EDGES_ALL && trimcorners) { - if (chamfer<0) { - cube(size, center=true) { - attach(TOP) prismoid([size.x,size.y], [size.x-2*chamfer,size.y-2*chamfer], h=-chamfer, anchor=TOP); - attach(BOT) prismoid([size.x,size.y], [size.x-2*chamfer,size.y-2*chamfer], h=-chamfer, anchor=TOP); - } - } else { - isize = [for (v = size) max(0.001, v-2*chamfer)]; - hull() { - cube([size.x, isize.y, isize.z], center=true); - cube([isize.x, size.y, isize.z], center=true); - cube([isize.x, isize.y, size.z], center=true); - } - } - } else if (chamfer<0) { - ach = abs(chamfer); - cube(size, center=true); + size = scalar_vec3(size); + edges = edges(edges, except=except_edges); + if (!is_undef(p1)) { + if (!is_undef(p2)) { + translate(pointlist_bounds([p1,p2])[0]) { + cuboid(size=vabs(p2-p1), chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children(); + } + } else { + translate(p1) { + cuboid(size=size, chamfer=chamfer, rounding=rounding, edges=edges, trimcorners=trimcorners, anchor=ALLNEG) children(); + } + } + } else { + if (chamfer != undef) { + if (any(edges[0])) assert(chamfer <= size.y/2 && chamfer <=size.z/2, "chamfer must be smaller than half the cube length or height."); + if (any(edges[1])) assert(chamfer <= size.x/2 && chamfer <=size.z/2, "chamfer must be smaller than half the cube width or height."); + if (any(edges[2])) assert(chamfer <= size.x/2 && chamfer <=size.y/2, "chamfer must be smaller than half the cube width or length."); + } + if (rounding != undef) { + if (any(edges[0])) assert(rounding <= size.y/2 && rounding<=size.z/2, "rounding radius must be smaller than half the cube length or height."); + if (any(edges[1])) assert(rounding <= size.x/2 && rounding<=size.z/2, "rounding radius must be smaller than half the cube width or height."); + if (any(edges[2])) assert(rounding <= size.x/2 && rounding<=size.y/2, "rounding radius must be smaller than half the cube width or length."); + } + majrots = [[0,90,0], [90,0,0], [0,0,0]]; + attachable(anchor,spin,orient, size=size) { + if (chamfer != undef) { + if (edges == EDGES_ALL && trimcorners) { + if (chamfer<0) { + cube(size, center=true) { + attach(TOP) prismoid([size.x,size.y], [size.x-2*chamfer,size.y-2*chamfer], h=-chamfer, anchor=TOP); + attach(BOT) prismoid([size.x,size.y], [size.x-2*chamfer,size.y-2*chamfer], h=-chamfer, anchor=TOP); + } + } else { + isize = [for (v = size) max(0.001, v-2*chamfer)]; + hull() { + cube([size.x, isize.y, isize.z], center=true); + cube([isize.x, size.y, isize.z], center=true); + cube([isize.x, isize.y, size.z], center=true); + } + } + } else if (chamfer<0) { + ach = abs(chamfer); + cube(size, center=true); - // External-Chamfer mask edges - difference() { - union() { - for (i = [0:3], axis=[0:1]) { - if (edges[axis][i]>0) { - vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[ach,ach,-ach])) { - rotate(majrots[axis]) { - cube([ach, ach, size[axis]], center=true); - } - } - } - } + // External-Chamfer mask edges + difference() { + union() { + for (i = [0:3], axis=[0:1]) { + if (edges[axis][i]>0) { + vec = EDGE_OFFSETS[axis][i]; + translate(vmul(vec/2, size+[ach,ach,-ach])) { + rotate(majrots[axis]) { + cube([ach, ach, size[axis]], center=true); + } + } + } + } - // Add multi-edge corners. - if (trimcorners) { - for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { - if (corner_edge_count(edges, [xa,ya,za]) > 1) { - translate(vmul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) { - cube([ach+0.01,ach+0.01,ach], center=true); - } - } - } - } - } + // Add multi-edge corners. + if (trimcorners) { + for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { + if (corner_edge_count(edges, [xa,ya,za]) > 1) { + translate(vmul([xa,ya,za]/2, size+[ach-0.01,ach-0.01,-ach])) { + cube([ach+0.01,ach+0.01,ach], center=true); + } + } + } + } + } - // Remove bevels from overhangs. - for (i = [0:3], axis=[0:1]) { - if (edges[axis][i]>0) { - vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[2*ach,2*ach,-2*ach])) { - rotate(majrots[axis]) { - zrot(45) cube([ach*sqrt(2), ach*sqrt(2), size[axis]+2.1*ach], center=true); - } - } - } - } - } - } else { - difference() { - cube(size, center=true); + // Remove bevels from overhangs. + for (i = [0:3], axis=[0:1]) { + if (edges[axis][i]>0) { + vec = EDGE_OFFSETS[axis][i]; + translate(vmul(vec/2, size+[2*ach,2*ach,-2*ach])) { + rotate(majrots[axis]) { + zrot(45) cube([ach*sqrt(2), ach*sqrt(2), size[axis]+2.1*ach], center=true); + } + } + } + } + } + } else { + difference() { + cube(size, center=true); - // Chamfer edges - for (i = [0:3], axis=[0:2]) { - if (edges[axis][i]>0) { - translate(vmul(EDGE_OFFSETS[axis][i], size/2)) { - rotate(majrots[axis]) { - zrot(45) cube([chamfer*sqrt(2), chamfer*sqrt(2), size[axis]+0.01], center=true); - } - } - } - } + // Chamfer edges + for (i = [0:3], axis=[0:2]) { + if (edges[axis][i]>0) { + translate(vmul(EDGE_OFFSETS[axis][i], size/2)) { + rotate(majrots[axis]) { + zrot(45) cube([chamfer*sqrt(2), chamfer*sqrt(2), size[axis]+0.01], center=true); + } + } + } + } - // Chamfer triple-edge corners. - if (trimcorners) { - for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { - if (corner_edge_count(edges, [xa,ya,za]) > 2) { - translate(vmul([xa,ya,za]/2, size-[1,1,1]*chamfer*4/3)) { - rot(from=UP, to=[xa,ya,za]) { - cube(chamfer*3, anchor=BOTTOM); - } - } - } - } - } - } - } - } else if (rounding != undef) { - sides = quantup(segs(rounding),4); - if (edges == EDGES_ALL) { - if(rounding<0) { - cube(size, center=true); - zflip_copy() { - up(size.z/2) { - difference() { - down(-rounding/2) cube([size.x-2*rounding, size.y-2*rounding, -rounding], center=true); - down(-rounding) { - ycopies(size.y-2*rounding) xcyl(l=size.x-3*rounding, r=-rounding); - xcopies(size.x-2*rounding) ycyl(l=size.y-3*rounding, r=-rounding); - } - } - } - } - } else { - isize = [for (v = size) max(0.001, v-2*rounding)]; - minkowski() { - cube(isize, center=true); - if (trimcorners) { - spheroid(r=rounding, $fn=sides); - } else { - intersection() { - cyl(r=rounding, h=rounding*2, $fn=sides); - rotate([90,0,0]) cyl(r=rounding, h=rounding*2, $fn=sides); - rotate([0,90,0]) cyl(r=rounding, h=rounding*2, $fn=sides); - } - } - } - } - } else if (rounding<0) { - ard = abs(rounding); - cube(size, center=true); + // Chamfer triple-edge corners. + if (trimcorners) { + for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { + if (corner_edge_count(edges, [xa,ya,za]) > 2) { + translate(vmul([xa,ya,za]/2, size-[1,1,1]*chamfer*4/3)) { + rot(from=UP, to=[xa,ya,za]) { + cube(chamfer*3, anchor=BOTTOM); + } + } + } + } + } + } + } + } else if (rounding != undef) { + sides = quantup(segs(rounding),4); + if (edges == EDGES_ALL) { + if(rounding<0) { + cube(size, center=true); + zflip_copy() { + up(size.z/2) { + difference() { + down(-rounding/2) cube([size.x-2*rounding, size.y-2*rounding, -rounding], center=true); + down(-rounding) { + ycopies(size.y-2*rounding) xcyl(l=size.x-3*rounding, r=-rounding); + xcopies(size.x-2*rounding) ycyl(l=size.y-3*rounding, r=-rounding); + } + } + } + } + } else { + isize = [for (v = size) max(0.001, v-2*rounding)]; + minkowski() { + cube(isize, center=true); + if (trimcorners) { + spheroid(r=rounding, $fn=sides); + } else { + intersection() { + cyl(r=rounding, h=rounding*2, $fn=sides); + rotate([90,0,0]) cyl(r=rounding, h=rounding*2, $fn=sides); + rotate([0,90,0]) cyl(r=rounding, h=rounding*2, $fn=sides); + } + } + } + } + } else if (rounding<0) { + ard = abs(rounding); + cube(size, center=true); - // External-Chamfer mask edges - difference() { - union() { - for (i = [0:3], axis=[0:1]) { - if (edges[axis][i]>0) { - vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[ard,ard,-ard])) { - rotate(majrots[axis]) { - cube([ard, ard, size[axis]], center=true); - } - } - } - } + // External-Chamfer mask edges + difference() { + union() { + for (i = [0:3], axis=[0:1]) { + if (edges[axis][i]>0) { + vec = EDGE_OFFSETS[axis][i]; + translate(vmul(vec/2, size+[ard,ard,-ard])) { + rotate(majrots[axis]) { + cube([ard, ard, size[axis]], center=true); + } + } + } + } - // Add multi-edge corners. - if (trimcorners) { - for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { - if (corner_edge_count(edges, [xa,ya,za]) > 1) { - translate(vmul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) { - cube([ard+0.01,ard+0.01,ard], center=true); - } - } - } - } - } + // Add multi-edge corners. + if (trimcorners) { + for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { + if (corner_edge_count(edges, [xa,ya,za]) > 1) { + translate(vmul([xa,ya,za]/2, size+[ard-0.01,ard-0.01,-ard])) { + cube([ard+0.01,ard+0.01,ard], center=true); + } + } + } + } + } - // Remove roundings from overhangs. - for (i = [0:3], axis=[0:1]) { - if (edges[axis][i]>0) { - vec = EDGE_OFFSETS[axis][i]; - translate(vmul(vec/2, size+[2*ard,2*ard,-2*ard])) { - rotate(majrots[axis]) { - cyl(l=size[axis]+2.1*ard, r=ard); - } - } - } - } - } - } else { - difference() { - cube(size, center=true); + // Remove roundings from overhangs. + for (i = [0:3], axis=[0:1]) { + if (edges[axis][i]>0) { + vec = EDGE_OFFSETS[axis][i]; + translate(vmul(vec/2, size+[2*ard,2*ard,-2*ard])) { + rotate(majrots[axis]) { + cyl(l=size[axis]+2.1*ard, r=ard); + } + } + } + } + } + } else { + difference() { + cube(size, center=true); - // Round edges. - for (i = [0:3], axis=[0:2]) { - if (edges[axis][i]>0) { - difference() { - translate(vmul(EDGE_OFFSETS[axis][i], size/2)) { - rotate(majrots[axis]) cube([rounding*2, rounding*2, size[axis]+0.1], center=true); - } - translate(vmul(EDGE_OFFSETS[axis][i], size/2 - [1,1,1]*rounding)) { - rotate(majrots[axis]) cyl(h=size[axis]+0.2, r=rounding, $fn=sides); - } - } - } - } + // Round edges. + for (i = [0:3], axis=[0:2]) { + if (edges[axis][i]>0) { + difference() { + translate(vmul(EDGE_OFFSETS[axis][i], size/2)) { + rotate(majrots[axis]) cube([rounding*2, rounding*2, size[axis]+0.1], center=true); + } + translate(vmul(EDGE_OFFSETS[axis][i], size/2 - [1,1,1]*rounding)) { + rotate(majrots[axis]) cyl(h=size[axis]+0.2, r=rounding, $fn=sides); + } + } + } + } - // Round triple-edge corners. - if (trimcorners) { - for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { - if (corner_edge_count(edges, [xa,ya,za]) > 2) { - difference() { - translate(vmul([xa,ya,za], size/2)) { - cube(rounding*2, center=true); - } - translate(vmul([xa,ya,za], size/2-[1,1,1]*rounding)) { - spheroid(r=rounding, $fn=sides); - } - } - } - } - } - } - } - } else { - cube(size=size, center=true); - } - children(); - } - } + // Round triple-edge corners. + if (trimcorners) { + for (za=[-1,1], ya=[-1,1], xa=[-1,1]) { + if (corner_edge_count(edges, [xa,ya,za]) > 2) { + difference() { + translate(vmul([xa,ya,za], size/2)) { + cube(rounding*2, center=true); + } + translate(vmul([xa,ya,za], size/2-[1,1,1]*rounding)) { + spheroid(r=rounding, $fn=sides); + } + } + } + } + } + } + } + } else { + cube(size=size, center=true); + } + children(); + } + } } @@ -383,97 +383,97 @@ module cuboid( // prismoid(size1=[50,30], size2=[20,20], h=20, shift=[15,5]) // show_anchors(); module prismoid( - size1, size2, h, shift=[0,0], - rounding=0, rounding1, rounding2, - chamfer=0, chamfer1, chamfer2, - l, center, - anchor, spin=0, orient=UP + size1, size2, h, shift=[0,0], + rounding=0, rounding1, rounding2, + chamfer=0, chamfer1, chamfer2, + l, center, + anchor, spin=0, orient=UP ) { - assert(is_num(size1) || is_vector(size1,2)); - assert(is_num(size2) || is_vector(size2,2)); - assert(is_num(h) || is_num(l)); - assert(is_vector(shift,2)); - assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument."); - assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument."); - assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument."); - assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument."); - assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument."); - assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument."); - eps = pow(2,-14); - size1 = is_num(size1)? [size1,size1] : size1; - size2 = is_num(size2)? [size2,size2] : size2; - s1 = [max(size1.x, eps), max(size1.y, eps)]; - s2 = [max(size2.x, eps), max(size2.y, eps)]; - rounding1 = default(rounding1, rounding); - rounding2 = default(rounding2, rounding); - chamfer1 = default(chamfer1, chamfer); - chamfer2 = default(chamfer2, chamfer); - anchor = get_anchor(anchor, center, BOT, BOT); - vnf = prismoid( - size1=size1, size2=size2, h=h, shift=shift, - rounding=rounding, rounding1=rounding1, rounding2=rounding2, - chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, - l=l, center=CENTER - ); - attachable(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift) { - vnf_polyhedron(vnf, convexity=4); - children(); - } + assert(is_num(size1) || is_vector(size1,2)); + assert(is_num(size2) || is_vector(size2,2)); + assert(is_num(h) || is_num(l)); + assert(is_vector(shift,2)); + assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument."); + assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument."); + assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument."); + assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument."); + assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument."); + assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument."); + eps = pow(2,-14); + size1 = is_num(size1)? [size1,size1] : size1; + size2 = is_num(size2)? [size2,size2] : size2; + s1 = [max(size1.x, eps), max(size1.y, eps)]; + s2 = [max(size2.x, eps), max(size2.y, eps)]; + rounding1 = default(rounding1, rounding); + rounding2 = default(rounding2, rounding); + chamfer1 = default(chamfer1, chamfer); + chamfer2 = default(chamfer2, chamfer); + anchor = get_anchor(anchor, center, BOT, BOT); + vnf = prismoid( + size1=size1, size2=size2, h=h, shift=shift, + rounding=rounding, rounding1=rounding1, rounding2=rounding2, + chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, + l=l, center=CENTER + ); + attachable(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift) { + vnf_polyhedron(vnf, convexity=4); + children(); + } } function prismoid( - size1, size2, h, shift=[0,0], - rounding=0, rounding1, rounding2, - chamfer=0, chamfer1, chamfer2, - l, center, - anchor=DOWN, spin=0, orient=UP + size1, size2, h, shift=[0,0], + rounding=0, rounding1, rounding2, + chamfer=0, chamfer1, chamfer2, + l, center, + anchor=DOWN, spin=0, orient=UP ) = - assert(is_vector(size1,2)) - assert(is_vector(size2,2)) - assert(is_num(h) || is_num(l)) - assert(is_vector(shift,2)) - assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument.") - assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument.") - assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument.") - assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument.") - assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument.") - assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument.") - let( - eps = pow(2,-14), - h = first_defined([h,l,1]), - shiftby = point3d(point2d(shift)), - s1 = [max(size1.x, eps), max(size1.y, eps)], - s2 = [max(size2.x, eps), max(size2.y, eps)], - rounding1 = default(rounding1, rounding), - rounding2 = default(rounding2, rounding), - chamfer1 = default(chamfer1, chamfer), - chamfer2 = default(chamfer2, chamfer), - anchor = get_anchor(anchor, center, BOT, BOT), - vnf = (rounding1==0 && rounding2==0 && chamfer1==0 && chamfer2==0)? ( - let( - corners = [[1,1],[1,-1],[-1,-1],[-1,1]] * 0.5, - points = [ - for (p=corners) point3d(vmul(s2,p), +h/2) + shiftby, - for (p=corners) point3d(vmul(s1,p), -h/2) - ], - faces=[ - [0,1,2], [0,2,3], [0,4,5], [0,5,1], - [1,5,6], [1,6,2], [2,6,7], [2,7,3], - [3,7,4], [3,4,0], [4,7,6], [4,6,5], - ] - ) [points, faces] - ) : ( - let( - path1 = rect(size1, rounding=rounding1, chamfer=chamfer1, anchor=CTR), - path2 = rect(size2, rounding=rounding2, chamfer=chamfer2, anchor=CTR), - points = [ - each path3d(path1, -h/2), - each path3d(move(shiftby, p=path2), +h/2), - ], - faces = hull(points) - ) [points, faces] - ) - ) reorient(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift, p=vnf); + assert(is_vector(size1,2)) + assert(is_vector(size2,2)) + assert(is_num(h) || is_num(l)) + assert(is_vector(shift,2)) + assert(is_num(rounding) || is_vector(rounding,4), "Bad rounding argument.") + assert(is_undef(rounding1) || is_num(rounding1) || is_vector(rounding1,4), "Bad rounding1 argument.") + assert(is_undef(rounding2) || is_num(rounding2) || is_vector(rounding2,4), "Bad rounding2 argument.") + assert(is_num(chamfer) || is_vector(chamfer,4), "Bad chamfer argument.") + assert(is_undef(chamfer1) || is_num(chamfer1) || is_vector(chamfer1,4), "Bad chamfer1 argument.") + assert(is_undef(chamfer2) || is_num(chamfer2) || is_vector(chamfer2,4), "Bad chamfer2 argument.") + let( + eps = pow(2,-14), + h = first_defined([h,l,1]), + shiftby = point3d(point2d(shift)), + s1 = [max(size1.x, eps), max(size1.y, eps)], + s2 = [max(size2.x, eps), max(size2.y, eps)], + rounding1 = default(rounding1, rounding), + rounding2 = default(rounding2, rounding), + chamfer1 = default(chamfer1, chamfer), + chamfer2 = default(chamfer2, chamfer), + anchor = get_anchor(anchor, center, BOT, BOT), + vnf = (rounding1==0 && rounding2==0 && chamfer1==0 && chamfer2==0)? ( + let( + corners = [[1,1],[1,-1],[-1,-1],[-1,1]] * 0.5, + points = [ + for (p=corners) point3d(vmul(s2,p), +h/2) + shiftby, + for (p=corners) point3d(vmul(s1,p), -h/2) + ], + faces=[ + [0,1,2], [0,2,3], [0,4,5], [0,5,1], + [1,5,6], [1,6,2], [2,6,7], [2,7,3], + [3,7,4], [3,4,0], [4,7,6], [4,6,5], + ] + ) [points, faces] + ) : ( + let( + path1 = rect(size1, rounding=rounding1, chamfer=chamfer1, anchor=CTR), + path2 = rect(size2, rounding=rounding2, chamfer=chamfer2, anchor=CTR), + points = [ + each path3d(path1, -h/2), + each path3d(move(shiftby, p=path2), +h/2), + ], + faces = hull(points) + ) [points, faces] + ) + ) reorient(anchor,spin,orient, size=[s1.x,s1.y,h], size2=s2, shift=shift, p=vnf); // Module: right_triangle() @@ -498,14 +498,14 @@ function prismoid( // right_triangle([60, 40, 15]) show_anchors(); module right_triangle(size=[1, 1, 1], center, anchor, spin=0, orient=UP) { - size = scalar_vec3(size); - anchor = get_anchor(anchor, center, ALLNEG, ALLNEG); - attachable(anchor,spin,orient, size=size) { - linear_extrude(height=size.z, convexity=2, center=true) { - polygon([[-size.x/2,-size.y/2], [-size.x/2,size.y/2], [size.x/2,-size.y/2]]); - } - children(); - } + size = scalar_vec3(size); + anchor = get_anchor(anchor, center, ALLNEG, ALLNEG); + attachable(anchor,spin,orient, size=size) { + linear_extrude(height=size.z, convexity=2, center=true) { + polygon([[-size.x/2,-size.y/2], [-size.x/2,size.y/2], [size.x/2,-size.y/2]]); + } + children(); + } } @@ -611,100 +611,100 @@ module right_triangle(size=[1, 1, 1], center, anchor, spin=0, orient=UP) // } // module cyl( - l=undef, h=undef, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - chamfer=undef, chamfer1=undef, chamfer2=undef, - chamfang=undef, chamfang1=undef, chamfang2=undef, - rounding=undef, rounding1=undef, rounding2=undef, - circum=false, realign=false, from_end=false, - center, anchor, spin=0, orient=UP + l=undef, h=undef, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + chamfer=undef, chamfer1=undef, chamfer2=undef, + chamfang=undef, chamfang1=undef, chamfang2=undef, + rounding=undef, rounding1=undef, rounding2=undef, + circum=false, realign=false, from_end=false, + center, anchor, spin=0, orient=UP ) { - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); - l = first_defined([l, h, 1]); - sides = segs(max(r1,r2)); - sc = circum? 1/cos(180/sides) : 1; - phi = atan2(l, r2-r1); - anchor = get_anchor(anchor,center,BOT,CENTER); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - zrot(realign? 180/sides : 0) { - if (!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])) { - cylinder(h=l, r1=r1*sc, r2=r2*sc, center=true, $fn=sides); - } else { - vang = atan2(l, r1-r2)/2; - chang1 = 90-first_defined([chamfang1, chamfang, vang]); - chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]); - cham1 = first_defined([chamfer1, chamfer]) * (from_end? 1 : tan(chang1)); - cham2 = first_defined([chamfer2, chamfer]) * (from_end? 1 : tan(chang2)); - fil1 = first_defined([rounding1, rounding]); - fil2 = first_defined([rounding2, rounding]); - if (chamfer != undef) { - assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder."); - assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder."); - } - if (cham1 != undef) { - assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder."); - } - if (cham2 != undef) { - assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder."); - } - if (rounding != undef) { - assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder."); - assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder."); - } - if (fil1 != undef) { - assert(fil1 <= r1, "rounding1 is larger than the r1 radius of the cylinder."); - } - if (fil2 != undef) { - assert(fil2 <= r2, "rounding2 is larger than the r1 radius of the cylinder."); - } - dy1 = abs(first_defined([cham1, fil1, 0])); - dy2 = abs(first_defined([cham2, fil2, 0])); - assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder."); + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=1); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=1); + l = first_defined([l, h, 1]); + sides = segs(max(r1,r2)); + sc = circum? 1/cos(180/sides) : 1; + phi = atan2(l, r2-r1); + anchor = get_anchor(anchor,center,BOT,CENTER); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + zrot(realign? 180/sides : 0) { + if (!any_defined([chamfer, chamfer1, chamfer2, rounding, rounding1, rounding2])) { + cylinder(h=l, r1=r1*sc, r2=r2*sc, center=true, $fn=sides); + } else { + vang = atan2(l, r1-r2)/2; + chang1 = 90-first_defined([chamfang1, chamfang, vang]); + chang2 = 90-first_defined([chamfang2, chamfang, 90-vang]); + cham1 = first_defined([chamfer1, chamfer]) * (from_end? 1 : tan(chang1)); + cham2 = first_defined([chamfer2, chamfer]) * (from_end? 1 : tan(chang2)); + fil1 = first_defined([rounding1, rounding]); + fil2 = first_defined([rounding2, rounding]); + if (chamfer != undef) { + assert(chamfer <= r1, "chamfer is larger than the r1 radius of the cylinder."); + assert(chamfer <= r2, "chamfer is larger than the r2 radius of the cylinder."); + } + if (cham1 != undef) { + assert(cham1 <= r1, "chamfer1 is larger than the r1 radius of the cylinder."); + } + if (cham2 != undef) { + assert(cham2 <= r2, "chamfer2 is larger than the r2 radius of the cylinder."); + } + if (rounding != undef) { + assert(rounding <= r1, "rounding is larger than the r1 radius of the cylinder."); + assert(rounding <= r2, "rounding is larger than the r2 radius of the cylinder."); + } + if (fil1 != undef) { + assert(fil1 <= r1, "rounding1 is larger than the r1 radius of the cylinder."); + } + if (fil2 != undef) { + assert(fil2 <= r2, "rounding2 is larger than the r1 radius of the cylinder."); + } + dy1 = abs(first_defined([cham1, fil1, 0])); + dy2 = abs(first_defined([cham2, fil2, 0])); + assert(dy1+dy2 <= l, "Sum of fillets and chamfer sizes must be less than the length of the cylinder."); - path = concat( - [[0,l/2]], + path = concat( + [[0,l/2]], - !is_undef(cham2)? ( - let( - p1 = [r2-cham2/tan(chang2),l/2], - p2 = lerp([r2,l/2],[r1,-l/2],abs(cham2)/l) - ) [p1,p2] - ) : !is_undef(fil2)? ( - let( - cn = find_circle_2tangents([r2-fil2,l/2], [r2,l/2], [r1,-l/2], r=abs(fil2)), - ang = fil2<0? phi : phi-180, - steps = ceil(abs(ang)/360*segs(abs(fil2))), - step = ang/steps, - pts = [for (i=[0:1:steps]) let(a=90+i*step) cn[0]+abs(fil2)*[cos(a),sin(a)]] - ) pts - ) : [[r2,l/2]], + !is_undef(cham2)? ( + let( + p1 = [r2-cham2/tan(chang2),l/2], + p2 = lerp([r2,l/2],[r1,-l/2],abs(cham2)/l) + ) [p1,p2] + ) : !is_undef(fil2)? ( + let( + cn = find_circle_2tangents([r2-fil2,l/2], [r2,l/2], [r1,-l/2], r=abs(fil2)), + ang = fil2<0? phi : phi-180, + steps = ceil(abs(ang)/360*segs(abs(fil2))), + step = ang/steps, + pts = [for (i=[0:1:steps]) let(a=90+i*step) cn[0]+abs(fil2)*[cos(a),sin(a)]] + ) pts + ) : [[r2,l/2]], - !is_undef(cham1)? ( - let( - p1 = lerp([r1,-l/2],[r2,l/2],abs(cham1)/l), - p2 = [r1-cham1/tan(chang1),-l/2] - ) [p1,p2] - ) : !is_undef(fil1)? ( - let( - cn = find_circle_2tangents([r1-fil1,-l/2], [r1,-l/2], [r2,l/2], r=abs(fil1)), - ang = fil1<0? 180-phi : -phi, - steps = ceil(abs(ang)/360*segs(abs(fil1))), - step = ang/steps, - pts = [for (i=[0:1:steps]) let(a=(fil1<0?180:0)+(phi-90)+i*step) cn[0]+abs(fil1)*[cos(a),sin(a)]] - ) pts - ) : [[r1,-l/2]], + !is_undef(cham1)? ( + let( + p1 = lerp([r1,-l/2],[r2,l/2],abs(cham1)/l), + p2 = [r1-cham1/tan(chang1),-l/2] + ) [p1,p2] + ) : !is_undef(fil1)? ( + let( + cn = find_circle_2tangents([r1-fil1,-l/2], [r1,-l/2], [r2,l/2], r=abs(fil1)), + ang = fil1<0? 180-phi : -phi, + steps = ceil(abs(ang)/360*segs(abs(fil1))), + step = ang/steps, + pts = [for (i=[0:1:steps]) let(a=(fil1<0?180:0)+(phi-90)+i*step) cn[0]+abs(fil1)*[cos(a),sin(a)]] + ) pts + ) : [[r1,-l/2]], - [[0,-l/2]] - ); - rotate_extrude(convexity=2) { - polygon(path); - } - } - } - children(); - } + [[0,-l/2]] + ); + rotate_extrude(convexity=2) { + polygon(path); + } + } + } + children(); + } } @@ -741,8 +741,8 @@ module cyl( // } module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, anchor=CENTER) { - anchor = rot(from=RIGHT, to=UP, p=anchor); - cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=RIGHT, anchor=anchor) children(); + anchor = rot(from=RIGHT, to=UP, p=anchor); + cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=RIGHT, anchor=anchor) children(); } @@ -779,8 +779,8 @@ module xcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h // } module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, anchor=CENTER) { - anchor = rot(from=BACK, to=UP, p=anchor); - cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=BACK, anchor=anchor) children(); + anchor = rot(from=BACK, to=UP, p=anchor); + cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=BACK, anchor=anchor) children(); } @@ -817,7 +817,7 @@ module ycyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h // } module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h=undef, anchor=CENTER) { - cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=UP, anchor=anchor) children(); + cyl(l=l, h=h, r=r, r1=r1, r2=r2, d=d, d1=d1, d2=d2, orient=UP, anchor=anchor) children(); } @@ -869,34 +869,34 @@ module zcyl(l=undef, r=undef, d=undef, r1=undef, r2=undef, d1=undef, d2=undef, h // Example: Standard Connectors // tube(h=30, or=40, wall=5) show_anchors(); module tube( - h, wall=undef, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - or=undef, or1=undef, or2=undef, - od=undef, od1=undef, od2=undef, - ir=undef, id=undef, ir1=undef, - ir2=undef, id1=undef, id2=undef, - anchor, spin=0, orient=UP, - center, realign=false, l + h, wall=undef, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + or=undef, or1=undef, or2=undef, + od=undef, od1=undef, od2=undef, + ir=undef, id=undef, ir1=undef, + ir2=undef, id1=undef, id2=undef, + anchor, spin=0, orient=UP, + center, realign=false, l ) { - h = first_defined([h,l,1]); - r1 = first_defined([or1, od1/2, r1, d1/2, or, od/2, r, d/2, ir1+wall, id1/2+wall, ir+wall, id/2+wall]); - r2 = first_defined([or2, od2/2, r2, d2/2, or, od/2, r, d/2, ir2+wall, id2/2+wall, ir+wall, id/2+wall]); - ir1 = first_defined([ir1, id1/2, ir, id/2, r1-wall, d1/2-wall, r-wall, d/2-wall]); - ir2 = first_defined([ir2, id2/2, ir, id/2, r2-wall, d2/2-wall, r-wall, d/2-wall]); - assert(ir1 <= r1, "Inner radius is larger than outer radius."); - assert(ir2 <= r2, "Inner radius is larger than outer radius."); - sides = segs(max(r1,r2)); - anchor = get_anchor(anchor, center, BOT, BOT); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { - zrot(realign? 180/sides : 0) { - difference() { - cyl(h=h, r1=r1, r2=r2, $fn=sides) children(); - cyl(h=h+0.05, r1=ir1, r2=ir2); - } - } - children(); - } + h = first_defined([h,l,1]); + r1 = first_defined([or1, od1/2, r1, d1/2, or, od/2, r, d/2, ir1+wall, id1/2+wall, ir+wall, id/2+wall]); + r2 = first_defined([or2, od2/2, r2, d2/2, or, od/2, r, d/2, ir2+wall, id2/2+wall, ir+wall, id/2+wall]); + ir1 = first_defined([ir1, id1/2, ir, id/2, r1-wall, d1/2-wall, r-wall, d/2-wall]); + ir2 = first_defined([ir2, id2/2, ir, id/2, r2-wall, d2/2-wall, r-wall, d/2-wall]); + assert(ir1 <= r1, "Inner radius is larger than outer radius."); + assert(ir2 <= r2, "Inner radius is larger than outer radius."); + sides = segs(max(r1,r2)); + anchor = get_anchor(anchor, center, BOT, BOT); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=h) { + zrot(realign? 180/sides : 0) { + difference() { + cyl(h=h, r1=r1, r2=r2, $fn=sides) children(); + cyl(h=h+0.05, r1=ir1, r2=ir2); + } + } + children(); + } } @@ -983,80 +983,80 @@ module tube( // rounding2=[0,5,0,10], irounding2=[0,3,0,8] // ); module rect_tube( - size, isize, - h, shift=[0,0], wall, - size1, size2, - isize1, isize2, - rounding=0, rounding1, rounding2, - irounding=0, irounding1, irounding2, - chamfer=0, chamfer1, chamfer2, - ichamfer=0, ichamfer1, ichamfer2, - anchor, spin=0, orient=UP, - center, l + size, isize, + h, shift=[0,0], wall, + size1, size2, + isize1, isize2, + rounding=0, rounding1, rounding2, + irounding=0, irounding1, irounding2, + chamfer=0, chamfer1, chamfer2, + ichamfer=0, ichamfer1, ichamfer2, + anchor, spin=0, orient=UP, + center, l ) { - h = first_defined([h,l,1]); - assert(is_num(h), "l or h argument required."); - assert(is_vector(shift,2)); - s1 = is_num(size1)? [size1, size1] : - is_vector(size1,2)? size1 : - is_num(size)? [size, size] : - is_vector(size,2)? size : - undef; - s2 = is_num(size2)? [size2, size2] : - is_vector(size2,2)? size2 : - is_num(size)? [size, size] : - is_vector(size,2)? size : - undef; - is1 = is_num(isize1)? [isize1, isize1] : - is_vector(isize1,2)? isize1 : - is_num(isize)? [isize, isize] : - is_vector(isize,2)? isize : - undef; - is2 = is_num(isize2)? [isize2, isize2] : - is_vector(isize2,2)? isize2 : - is_num(isize)? [isize, isize] : - is_vector(isize,2)? isize : - undef; - size1 = is_def(s1)? s1 : - (is_def(wall) && is_def(is1))? (is1+2*[wall,wall]) : - undef; - size2 = is_def(s2)? s2 : - (is_def(wall) && is_def(is2))? (is2+2*[wall,wall]) : - undef; - isize1 = is_def(is1)? is1 : - (is_def(wall) && is_def(s1))? (s1-2*[wall,wall]) : - undef; - isize2 = is_def(is2)? is2 : - (is_def(wall) && is_def(s2))? (s2-2*[wall,wall]) : - undef; - assert(wall==undef || is_num(wall)); - assert(size1!=undef, "Bad size/size1 argument."); - assert(size2!=undef, "Bad size/size2 argument."); - assert(isize1!=undef, "Bad isize/isize1 argument."); - assert(isize2!=undef, "Bad isize/isize2 argument."); - assert(isize1.x < size1.x, "Inner size is larger than outer size."); - assert(isize1.y < size1.y, "Inner size is larger than outer size."); - assert(isize2.x < size2.x, "Inner size is larger than outer size."); - assert(isize2.y < size2.y, "Inner size is larger than outer size."); - anchor = get_anchor(anchor, center, BOT, BOT); - attachable(anchor,spin,orient, size=[each size1, h], size2=size2, shift=shift) { - diff("_H_o_L_e_") - prismoid( - size1, size2, h=h, shift=shift, - rounding=rounding, rounding1=rounding1, rounding2=rounding2, - chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, - anchor=CTR - ) { - children(); - tags("_H_o_L_e_") prismoid( - isize1, isize2, h=h+0.05, shift=shift, - rounding=irounding, rounding1=irounding1, rounding2=irounding2, - chamfer=ichamfer, chamfer1=ichamfer1, chamfer2=ichamfer2, - anchor=CTR - ); - } - children(); - } + h = first_defined([h,l,1]); + assert(is_num(h), "l or h argument required."); + assert(is_vector(shift,2)); + s1 = is_num(size1)? [size1, size1] : + is_vector(size1,2)? size1 : + is_num(size)? [size, size] : + is_vector(size,2)? size : + undef; + s2 = is_num(size2)? [size2, size2] : + is_vector(size2,2)? size2 : + is_num(size)? [size, size] : + is_vector(size,2)? size : + undef; + is1 = is_num(isize1)? [isize1, isize1] : + is_vector(isize1,2)? isize1 : + is_num(isize)? [isize, isize] : + is_vector(isize,2)? isize : + undef; + is2 = is_num(isize2)? [isize2, isize2] : + is_vector(isize2,2)? isize2 : + is_num(isize)? [isize, isize] : + is_vector(isize,2)? isize : + undef; + size1 = is_def(s1)? s1 : + (is_def(wall) && is_def(is1))? (is1+2*[wall,wall]) : + undef; + size2 = is_def(s2)? s2 : + (is_def(wall) && is_def(is2))? (is2+2*[wall,wall]) : + undef; + isize1 = is_def(is1)? is1 : + (is_def(wall) && is_def(s1))? (s1-2*[wall,wall]) : + undef; + isize2 = is_def(is2)? is2 : + (is_def(wall) && is_def(s2))? (s2-2*[wall,wall]) : + undef; + assert(wall==undef || is_num(wall)); + assert(size1!=undef, "Bad size/size1 argument."); + assert(size2!=undef, "Bad size/size2 argument."); + assert(isize1!=undef, "Bad isize/isize1 argument."); + assert(isize2!=undef, "Bad isize/isize2 argument."); + assert(isize1.x < size1.x, "Inner size is larger than outer size."); + assert(isize1.y < size1.y, "Inner size is larger than outer size."); + assert(isize2.x < size2.x, "Inner size is larger than outer size."); + assert(isize2.y < size2.y, "Inner size is larger than outer size."); + anchor = get_anchor(anchor, center, BOT, BOT); + attachable(anchor,spin,orient, size=[each size1, h], size2=size2, shift=shift) { + diff("_H_o_L_e_") + prismoid( + size1, size2, h=h, shift=shift, + rounding=rounding, rounding1=rounding1, rounding2=rounding2, + chamfer=chamfer, chamfer1=chamfer1, chamfer2=chamfer2, + anchor=CTR + ) { + children(); + tags("_H_o_L_e_") prismoid( + isize1, isize2, h=h+0.05, shift=shift, + rounding=irounding, rounding1=irounding1, rounding2=irounding2, + chamfer=ichamfer, chamfer1=ichamfer1, chamfer2=ichamfer2, + anchor=CTR + ); + } + children(); + } } @@ -1090,23 +1090,23 @@ module rect_tube( // Example: Standard Connectors // torus(od=60, id=30) show_anchors(); module torus( - r=undef, d=undef, - r2=undef, d2=undef, - or=undef, od=undef, - ir=undef, id=undef, - center, anchor, spin=0, orient=UP + r=undef, d=undef, + r2=undef, d2=undef, + or=undef, od=undef, + ir=undef, id=undef, + center, anchor, spin=0, orient=UP ) { - orr = get_radius(r=or, d=od, dflt=1.0); - irr = get_radius(r=ir, d=id, dflt=0.5); - majrad = get_radius(r=r, d=d, dflt=(orr+irr)/2); - minrad = get_radius(r=r2, d=d2, dflt=(orr-irr)/2); - anchor = get_anchor(anchor, center, BOT, CENTER); - attachable(anchor,spin,orient, r=(majrad+minrad), l=minrad*2) { - rotate_extrude(convexity=4) { - right(majrad) circle(r=minrad); - } - children(); - } + orr = get_radius(r=or, d=od, dflt=1.0); + irr = get_radius(r=ir, d=id, dflt=0.5); + majrad = get_radius(r=r, d=d, dflt=(orr+irr)/2); + minrad = get_radius(r=r2, d=d2, dflt=(orr-irr)/2); + anchor = get_anchor(anchor, center, BOT, CENTER); + attachable(anchor,spin,orient, r=(majrad+minrad), l=minrad*2) { + rotate_extrude(convexity=4) { + right(majrad) circle(r=minrad); + } + children(); + } } @@ -1158,124 +1158,124 @@ module torus( // vnf_polyhedron(vnf); module spheroid(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orient=UP) { - r = get_radius(r=r, d=d, dflt=1); - sides = segs(r); - attachable(anchor,spin,orient, r=r) { - if (style=="orig") { - rotate_extrude(convexity=2,$fn=sides) { - difference() { - oval(r=r, circum=circum, $fn=sides); - left(r) square(2*r,center=true); - } - } - } else if (style=="aligned") { - rotate_extrude(convexity=2,$fn=sides) { - difference() { - zrot(180/sides) oval(r=r, circum=circum, $fn=sides); - left(r) square(2*r,center=true); - } - } - } else { - vnf = spheroid(r=r, circum=circum, style=style); - vnf_polyhedron(vnf, convexity=2); - } - children(); - } + r = get_radius(r=r, d=d, dflt=1); + sides = segs(r); + attachable(anchor,spin,orient, r=r) { + if (style=="orig") { + rotate_extrude(convexity=2,$fn=sides) { + difference() { + oval(r=r, circum=circum, $fn=sides); + left(r) square(2*r,center=true); + } + } + } else if (style=="aligned") { + rotate_extrude(convexity=2,$fn=sides) { + difference() { + zrot(180/sides) oval(r=r, circum=circum, $fn=sides); + left(r) square(2*r,center=true); + } + } + } else { + vnf = spheroid(r=r, circum=circum, style=style); + vnf_polyhedron(vnf, convexity=2); + } + children(); + } } function spheroid(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, orient=UP) = - let( - r = get_radius(r=r, d=d, dflt=1), - hsides = segs(r), - vsides = max(2,ceil(hsides/2)), - icosa_steps = round(max(5,hsides)/5), - rr = circum? (r / cos(90/vsides) / cos(180/hsides)) : r, - stagger = style=="stagger", - verts = style=="orig"? [ - for (i=[0:1:vsides-1]) let(phi = (i+0.5)*180/(vsides)) - for (j=[0:1:hsides-1]) let(theta = j*360/hsides) - spherical_to_xyz(rr, theta, phi), - ] : style=="aligned" || style=="stagger"? [ - spherical_to_xyz(rr, 0, 0), - for (i=[1:1:vsides-1]) let(phi = i*180/vsides) - for (j=[0:1:hsides-1]) let(theta = (j+((stagger && i%2!=0)?0.5:0))*360/hsides) - spherical_to_xyz(rr, theta, phi), - spherical_to_xyz(rr, 0, 180) - ] : style=="icosa"? [ - for (tb=[0,1], j=[0,2], i = [0:1:4]) let( - theta0 = i*360/5, - theta1 = (i-0.5)*360/5, - theta2 = (i+0.5)*360/5, - phi0 = 180/3 * j, - phi1 = 180/3, - v0 = spherical_to_xyz(1,theta0,phi0), - v1 = spherical_to_xyz(1,theta1,phi1), - v2 = spherical_to_xyz(1,theta2,phi1), - ax0 = vector_axis(v0, v1), - ang0 = vector_angle(v0, v1), - ax1 = vector_axis(v0, v2), - ang1 = vector_angle(v0, v2) - ) - for (k = [0:1:icosa_steps]) let( - u = k/icosa_steps, - vv0 = rot(ang0*u, ax0, p=v0), - vv1 = rot(ang1*u, ax1, p=v0), - ax2 = vector_axis(vv0, vv1), - ang2 = vector_angle(vv0, vv1) - ) - for (l = [0:1:k]) let( - v = k? l/k : 0, - pt = rot(ang2*v, v=ax2, p=vv0) * rr * (tb? -1 : 1) - ) pt - ] : assert(in_list(style,["orig","aligned","stagger","icosa"])), - lv = len(verts), - faces = style=="orig"? [ - [for (i=[0:1:hsides-1]) hsides-i-1], - [for (i=[0:1:hsides-1]) lv-hsides+i], - for (i=[0:1:vsides-2], j=[0:1:hsides-1]) each [ - [(i+1)*hsides+j, i*hsides+j, i*hsides+(j+1)%hsides], - [(i+1)*hsides+j, i*hsides+(j+1)%hsides, (i+1)*hsides+(j+1)%hsides], - ] - ] : style=="aligned" || style=="stagger"? [ - for (i=[0:1:hsides-1]) let( - b2 = lv-2-hsides - ) each [ - [i+1, 0, ((i+1)%hsides)+1], - [lv-1, b2+i+1, b2+((i+1)%hsides)+1], - ], - for (i=[0:1:vsides-3], j=[0:1:hsides-1]) let( - base = 1 + hsides*i - ) each ( - (stagger && i%2!=0)? [ - [base+j, base+hsides+j%hsides, base+hsides+(j+hsides-1)%hsides], - [base+j, base+(j+1)%hsides, base+hsides+j], - ] : [ - [base+j, base+(j+1)%hsides, base+hsides+(j+1)%hsides], - [base+j, base+hsides+(j+1)%hsides, base+hsides+j], - ] - ) - ] : style=="icosa"? let( - pyr = [for (x=[0:1:icosa_steps+1]) x], - tri = sum(pyr), - soff = cumsum(pyr) - ) [ - for (tb=[0,1], j=[0,1], i = [0:1:4]) let( - base = ((((tb*2) + j) * 5) + i) * tri - ) - for (k = [0:1:icosa_steps-1]) - for (l = [0:1:k]) let( - v1 = base + soff[k] + l, - v2 = base + soff[k+1] + l, - v3 = base + soff[k+1] + (l + 1), - faces = [ - if(l>0) [v1-1,v1,v2], - [v1,v3,v2], - ], - faces2 = (tb+j)%2? [for (f=faces) reverse(f)] : faces - ) each faces2 - ] : [] - ) [reorient(anchor,spin,orient, r=r, p=verts), faces]; + let( + r = get_radius(r=r, d=d, dflt=1), + hsides = segs(r), + vsides = max(2,ceil(hsides/2)), + icosa_steps = round(max(5,hsides)/5), + rr = circum? (r / cos(90/vsides) / cos(180/hsides)) : r, + stagger = style=="stagger", + verts = style=="orig"? [ + for (i=[0:1:vsides-1]) let(phi = (i+0.5)*180/(vsides)) + for (j=[0:1:hsides-1]) let(theta = j*360/hsides) + spherical_to_xyz(rr, theta, phi), + ] : style=="aligned" || style=="stagger"? [ + spherical_to_xyz(rr, 0, 0), + for (i=[1:1:vsides-1]) let(phi = i*180/vsides) + for (j=[0:1:hsides-1]) let(theta = (j+((stagger && i%2!=0)?0.5:0))*360/hsides) + spherical_to_xyz(rr, theta, phi), + spherical_to_xyz(rr, 0, 180) + ] : style=="icosa"? [ + for (tb=[0,1], j=[0,2], i = [0:1:4]) let( + theta0 = i*360/5, + theta1 = (i-0.5)*360/5, + theta2 = (i+0.5)*360/5, + phi0 = 180/3 * j, + phi1 = 180/3, + v0 = spherical_to_xyz(1,theta0,phi0), + v1 = spherical_to_xyz(1,theta1,phi1), + v2 = spherical_to_xyz(1,theta2,phi1), + ax0 = vector_axis(v0, v1), + ang0 = vector_angle(v0, v1), + ax1 = vector_axis(v0, v2), + ang1 = vector_angle(v0, v2) + ) + for (k = [0:1:icosa_steps]) let( + u = k/icosa_steps, + vv0 = rot(ang0*u, ax0, p=v0), + vv1 = rot(ang1*u, ax1, p=v0), + ax2 = vector_axis(vv0, vv1), + ang2 = vector_angle(vv0, vv1) + ) + for (l = [0:1:k]) let( + v = k? l/k : 0, + pt = rot(ang2*v, v=ax2, p=vv0) * rr * (tb? -1 : 1) + ) pt + ] : assert(in_list(style,["orig","aligned","stagger","icosa"])), + lv = len(verts), + faces = style=="orig"? [ + [for (i=[0:1:hsides-1]) hsides-i-1], + [for (i=[0:1:hsides-1]) lv-hsides+i], + for (i=[0:1:vsides-2], j=[0:1:hsides-1]) each [ + [(i+1)*hsides+j, i*hsides+j, i*hsides+(j+1)%hsides], + [(i+1)*hsides+j, i*hsides+(j+1)%hsides, (i+1)*hsides+(j+1)%hsides], + ] + ] : style=="aligned" || style=="stagger"? [ + for (i=[0:1:hsides-1]) let( + b2 = lv-2-hsides + ) each [ + [i+1, 0, ((i+1)%hsides)+1], + [lv-1, b2+i+1, b2+((i+1)%hsides)+1], + ], + for (i=[0:1:vsides-3], j=[0:1:hsides-1]) let( + base = 1 + hsides*i + ) each ( + (stagger && i%2!=0)? [ + [base+j, base+hsides+j%hsides, base+hsides+(j+hsides-1)%hsides], + [base+j, base+(j+1)%hsides, base+hsides+j], + ] : [ + [base+j, base+(j+1)%hsides, base+hsides+(j+1)%hsides], + [base+j, base+hsides+(j+1)%hsides, base+hsides+j], + ] + ) + ] : style=="icosa"? let( + pyr = [for (x=[0:1:icosa_steps+1]) x], + tri = sum(pyr), + soff = cumsum(pyr) + ) [ + for (tb=[0,1], j=[0,1], i = [0:1:4]) let( + base = ((((tb*2) + j) * 5) + i) * tri + ) + for (k = [0:1:icosa_steps-1]) + for (l = [0:1:k]) let( + v1 = base + soff[k] + l, + v2 = base + soff[k+1] + l, + v3 = base + soff[k+1] + (l + 1), + faces = [ + if(l>0) [v1-1,v1,v2], + [v1,v3,v2], + ], + faces2 = (tb+j)%2? [for (f=faces) reverse(f)] : faces + ) each faces2 + ] : [] + ) [reorient(anchor,spin,orient, r=r, p=verts), faces]; @@ -1308,17 +1308,17 @@ function spheroid(r, d, circum=false, style="aligned", anchor=CENTER, spin=0, or // teardrop(r=30, h=10, ang=30, cap_h=20); module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, anchor=CENTER, spin=0, orient=UP) { - r = get_radius(r=r, d=d, dflt=1); - l = first_defined([l, h, 1]); - size = [r*2,l,r*2]; - attachable(anchor,spin,orient, size=size) { - rot(from=UP,to=FWD) { - linear_extrude(height=l, center=true, slices=2) { - teardrop2d(r=r, ang=ang, cap_h=cap_h); - } - } - children(); - } + r = get_radius(r=r, d=d, dflt=1); + l = first_defined([l, h, 1]); + size = [r*2,l,r*2]; + attachable(anchor,spin,orient, size=size) { + rot(from=UP,to=FWD) { + linear_extrude(height=l, center=true, slices=2) { + teardrop2d(r=r, ang=ang, cap_h=cap_h); + } + } + children(); + } } @@ -1349,21 +1349,21 @@ module teardrop(r=undef, d=undef, l=undef, h=undef, ang=45, cap_h=undef, anchor= // onion(r=30, maxang=30, cap_h=40) show_anchors(); module onion(cap_h=undef, r=undef, d=undef, maxang=45, h=undef, anchor=CENTER, spin=0, orient=UP) { - r = get_radius(r=r, d=d, dflt=1); - h = first_defined([cap_h, h]); - maxd = 3*r/tan(maxang); - anchors = [ - ["cap", [0,0,h], UP, 0] - ]; - attachable(anchor,spin,orient, r=r, anchors=anchors) { - rotate_extrude(convexity=2) { - difference() { - teardrop2d(r=r, ang=maxang, cap_h=h); - left(r) square(size=[2*r,maxd], center=true); - } - } - children(); - } + r = get_radius(r=r, d=d, dflt=1); + h = first_defined([cap_h, h]); + maxd = 3*r/tan(maxang); + anchors = [ + ["cap", [0,0,h], UP, 0] + ]; + attachable(anchor,spin,orient, r=r, anchors=anchors) { + rotate_extrude(convexity=2) { + difference() { + teardrop2d(r=r, ang=maxang, cap_h=h); + left(r) square(size=[2*r,maxd], center=true); + } + } + children(); + } } @@ -1418,28 +1418,28 @@ module noop(spin=0, orient=UP) attachable(CENTER,spin,orient, d=0.01) {nil(); ch // Example: Conical Pie Slice // pie_slice(ang=60, l=20, d1=50, d2=70); module pie_slice( - ang=30, l=undef, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef, - h=undef, center, - anchor, spin=0, orient=UP + ang=30, l=undef, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef, + h=undef, center, + anchor, spin=0, orient=UP ) { - l = first_defined([l, h, 1]); - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); - maxd = max(r1,r2)+0.1; - anchor = get_anchor(anchor, center, BOT, BOT); - attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { - difference() { - cyl(r1=r1, r2=r2, h=l); - if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true); - difference() { - fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true); - if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true); - } - } - children(); - } + l = first_defined([l, h, 1]); + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10); + maxd = max(r1,r2)+0.1; + anchor = get_anchor(anchor, center, BOT, BOT); + attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) { + difference() { + cyl(r1=r1, r2=r2, h=l); + if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true); + difference() { + fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true); + if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true); + } + } + children(); + } } @@ -1479,19 +1479,19 @@ module pie_slice( // interior_fillet(l=50, r=10, spin=180, orient=RIGHT); // } module interior_fillet(l=1.0, r=1.0, ang=90, overlap=0.01, anchor=FRONT+LEFT, spin=0, orient=UP) { - dy = r/tan(ang/2); - steps = ceil(segs(r)*ang/360); - step = ang/steps; - attachable(anchor,spin,orient, size=[r,r,l]) { - linear_extrude(height=l, convexity=4, center=true) { - path = concat( - [[0,0]], - [for (i=[0:1:steps]) let(a=270-i*step) r*[cos(a),sin(a)]+[dy,r]] - ); - translate(-[r,r]/2) polygon(path); - } - children(); - } + dy = r/tan(ang/2); + steps = ceil(segs(r)*ang/360); + step = ang/steps; + attachable(anchor,spin,orient, size=[r,r,l]) { + linear_extrude(height=l, convexity=4, center=true) { + path = concat( + [[0,0]], + [for (i=[0:1:steps]) let(a=270-i*step) r*[cos(a),sin(a)]+[dy,r]] + ); + translate(-[r,r]/2) polygon(path); + } + children(); + } } @@ -1524,15 +1524,15 @@ module interior_fillet(l=1.0, r=1.0, ang=90, overlap=0.01, anchor=FRONT+LEFT, sp // Example: By Length // slot(l=50, r1=5, r2=10, h=5); module slot( - p1=undef, p2=undef, h=10, l=undef, - r=undef, r1=undef, r2=undef, - d=undef, d1=undef, d2=undef + p1=undef, p2=undef, h=10, l=undef, + r=undef, r1=undef, r2=undef, + d=undef, d1=undef, d2=undef ) { - r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=5); - r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=5); - sides = quantup(segs(max(r1, r2)), 4); - // TODO: implement orient and anchors. - hull() line_of(p1=p1, p2=p2, l=l, n=2) cyl(l=h, r1=r1, r2=r2, center=true, $fn=sides); + r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=5); + r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=5); + sides = quantup(segs(max(r1, r2)), 4); + // TODO: implement orient and anchors. + hull() line_of(p1=p1, p2=p2, l=l, n=2) cyl(l=h, r1=r1, r2=r2, center=true, $fn=sides); } @@ -1568,31 +1568,31 @@ module slot( // Example(Med): Conical Arced Slot // arced_slot(r=60, h=5, sd1=10, sd2=15, sa=45, ea=180); module arced_slot( - r=undef, d=undef, h=1.0, - sr=undef, sr1=undef, sr2=undef, - sd=undef, sd1=undef, sd2=undef, - sa=0, ea=90, cp=[0,0,0], - anchor=TOP, spin=0, orient=UP, - $fn2 = undef + r=undef, d=undef, h=1.0, + sr=undef, sr1=undef, sr2=undef, + sd=undef, sd1=undef, sd2=undef, + sa=0, ea=90, cp=[0,0,0], + anchor=TOP, spin=0, orient=UP, + $fn2 = undef ) { - r = get_radius(r=r, d=d, dflt=2); - sr1 = get_radius(r1=sr1, r=sr, d1=sd1, d=sd, dflt=2); - sr2 = get_radius(r1=sr2, r=sr, d1=sd2, d=sd, dflt=2); - fn_minor = first_defined([$fn2, $fn]); - da = ea - sa; - attachable(anchor,spin,orient, r1=r+sr1, r2=r+sr2, l=h) { - translate(cp) { - zrot(sa) { - difference() { - pie_slice(ang=da, l=h, r1=r+sr1, r2=r+sr2, orient=UP, anchor=CENTER); - cyl(h=h+0.1, r1=r-sr1, r2=r-sr2); - } - right(r) cyl(h=h, r1=sr1, r2=sr2, $fn=fn_minor); - zrot(da) right(r) cyl(h=h, r1=sr1, r2=sr2, $fn=fn_minor); - } - } - children(); - } + r = get_radius(r=r, d=d, dflt=2); + sr1 = get_radius(r1=sr1, r=sr, d1=sd1, d=sd, dflt=2); + sr2 = get_radius(r1=sr2, r=sr, d1=sd2, d=sd, dflt=2); + fn_minor = first_defined([$fn2, $fn]); + da = ea - sa; + attachable(anchor,spin,orient, r1=r+sr1, r2=r+sr2, l=h) { + translate(cp) { + zrot(sa) { + difference() { + pie_slice(ang=da, l=h, r1=r+sr1, r2=r+sr2, orient=UP, anchor=CENTER); + cyl(h=h+0.1, r1=r-sr1, r2=r-sr2); + } + right(r) cyl(h=h, r1=sr1, r2=sr2, $fn=fn_minor); + zrot(da) right(r) cyl(h=h, r1=sr1, r2=sr2, $fn=fn_minor); + } + } + children(); + } } @@ -1620,88 +1620,88 @@ module arced_slot( // } module heightfield(heightfield, size=[100,100], bottom=0, convexity=10) { - size = is_num(size)? [size,size] : point2d(size); - dim = array_dim(heightfield); - assert(dim.x!=undef); - assert(dim.y!=undef); - assert(bottom0, "Width must be postive") assert(thickness>0, "Thickness must be positive") - arc(N,points=[[width/2,0], [0,thickness], [-width/2,0]],wedge=wedge) - ) : is_def(angle)? ( - let( - parmok = !any_defined([points,width,thickness]) && - ((is_vector(angle,2) && is_undef(start)) || is_num(angle)) - ) - assert(parmok,"Invalid parameters in arc") - let( - cp = first_defined([cp,[0,0]]), - start = is_def(start)? start : is_vector(angle) ? angle[0] : 0, - angle = is_vector(angle)? angle[1]-angle[0] : angle, - r = get_radius(r=r, d=d) + arc(N,points=[[width/2,0], [0,thickness], [-width/2,0]],wedge=wedge) + ) : is_def(angle)? ( + let( + parmok = !any_defined([points,width,thickness]) && + ((is_vector(angle,2) && is_undef(start)) || is_num(angle)) + ) + assert(parmok,"Invalid parameters in arc") + let( + cp = first_defined([cp,[0,0]]), + start = is_def(start)? start : is_vector(angle) ? angle[0] : 0, + angle = is_vector(angle)? angle[1]-angle[0] : angle, + r = get_radius(r=r, d=d) ) assert(is_vector(cp,2),"Centerpoint must be a 2d vector") assert(angle!=0, "Arc has zero length") assert(r>0, "Arc radius invalid") let( - N = max(3, is_undef(N)? ceil(segs(r)*abs(angle)/360) : N), - arcpoints = [for(i=[0:N-1]) let(theta = start + i*angle/(N-1)) r*[cos(theta),sin(theta)]+cp], - extra = wedge? [cp] : [] - ) - concat(extra,arcpoints) - ) : - assert(is_path(points,[2,3]),"Point list is invalid") - // Arc is 3D, so transform points to 2D and make a recursive call, then remap back to 3D - len(points[0])==3? ( + N = max(3, is_undef(N)? ceil(segs(r)*abs(angle)/360) : N), + arcpoints = [for(i=[0:N-1]) let(theta = start + i*angle/(N-1)) r*[cos(theta),sin(theta)]+cp], + extra = wedge? [cp] : [] + ) + concat(extra,arcpoints) + ) : + assert(is_path(points,[2,3]),"Point list is invalid") + // Arc is 3D, so transform points to 2D and make a recursive call, then remap back to 3D + len(points[0])==3? ( assert(!(cw || ccw), "(Counter)clockwise isn't meaningful in 3d, so `cw` and `ccw` must be false") assert(is_undef(cp) || is_vector(cp,3),"points are 3d so cp must be 3d") - let( - thirdpoint = is_def(cp) ? cp : points[2], - center2d = is_def(cp) ? project_plane(cp,thirdpoint,points[0],points[1]) : undef, - points2d = project_plane(points,thirdpoint,points[0],points[1]) - ) - lift_plane(arc(N,cp=center2d,points=points2d,wedge=wedge,long=long),thirdpoint,points[0],points[1]) - ) : is_def(cp)? ( - // Arc defined by center plus two points, will have radius defined by center and points[0] - // and extent defined by direction of point[1] from the center + let( + thirdpoint = is_def(cp) ? cp : points[2], + center2d = is_def(cp) ? project_plane(cp,thirdpoint,points[0],points[1]) : undef, + points2d = project_plane(points,thirdpoint,points[0],points[1]) + ) + lift_plane(arc(N,cp=center2d,points=points2d,wedge=wedge,long=long),thirdpoint,points[0],points[1]) + ) : is_def(cp)? ( + // Arc defined by center plus two points, will have radius defined by center and points[0] + // and extent defined by direction of point[1] from the center assert(is_vector(cp,2), "Centerpoint must be a 2d vector") assert(len(points)==2, "When pointlist has length 3 centerpoint is not allowed") assert(points[0]!=points[1], "Arc endpoints are equal") assert(cp!=points[0]&&cp!=points[1], "Centerpoint equals an arc endpoint") assert(count_true([long,cw,ccw])<=1, str("Only one of `long`, `cw` and `ccw` can be true",cw,ccw,long)) - let( - angle = vector_angle(points[0], cp, points[1]), - v1 = points[0]-cp, - v2 = points[1]-cp, - prelim_dir = sign(det2([v1,v2])), // z component of cross product + let( + angle = vector_angle(points[0], cp, points[1]), + v1 = points[0]-cp, + v2 = points[1]-cp, + prelim_dir = sign(det2([v1,v2])), // z component of cross product dir = prelim_dir != 0 ? prelim_dir : assert(cw || ccw, "Collinear inputs don't define a unique arc") 1, - r=norm(v1), + r=norm(v1), final_angle = long || (ccw && dir<0) || (cw && dir>0) ? -dir*(360-angle) : dir*angle - ) - arc(N,cp=cp,r=r,start=atan2(v1.y,v1.x),angle=final_angle,wedge=wedge) - ) : ( - // Final case is arc passing through three points, starting at point[0] and ending at point[3] - let(col = collinear(points[0],points[1],points[2])) - assert(!col, "Collinear inputs do not define an arc") - let( - cp = line_intersection(_normal_segment(points[0],points[1]),_normal_segment(points[1],points[2])), - // select order to be counterclockwise - dir = det2([points[1]-points[0],points[2]-points[1]]) > 0, - points = dir? select(points,[0,2]) : select(points,[2,0]), - r = norm(points[0]-cp), - theta_start = atan2(points[0].y-cp.y, points[0].x-cp.x), - theta_end = atan2(points[1].y-cp.y, points[1].x-cp.x), - angle = posmod(theta_end-theta_start, 360), - arcpts = arc(N,cp=cp,r=r,start=theta_start,angle=angle,wedge=wedge) - ) - dir ? arcpts : reverse(arcpts) - ); + ) + arc(N,cp=cp,r=r,start=atan2(v1.y,v1.x),angle=final_angle,wedge=wedge) + ) : ( + // Final case is arc passing through three points, starting at point[0] and ending at point[3] + let(col = collinear(points[0],points[1],points[2])) + assert(!col, "Collinear inputs do not define an arc") + let( + cp = line_intersection(_normal_segment(points[0],points[1]),_normal_segment(points[1],points[2])), + // select order to be counterclockwise + dir = det2([points[1]-points[0],points[2]-points[1]]) > 0, + points = dir? select(points,[0,2]) : select(points,[2,0]), + r = norm(points[0]-cp), + theta_start = atan2(points[0].y-cp.y, points[0].x-cp.x), + theta_end = atan2(points[1].y-cp.y, points[1].x-cp.x), + angle = posmod(theta_end-theta_start, 360), + arcpts = arc(N,cp=cp,r=r,start=theta_start,angle=angle,wedge=wedge) + ) + dir ? arcpts : reverse(arcpts) + ); module arc(N, r, angle, d, cp, points, width, thickness, start, wedge=false) { - path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge); - polygon(path); + path = arc(N=N, r=r, angle=angle, d=d, cp=cp, points=points, width=width, thickness=thickness, start=start, wedge=wedge); + polygon(path); } function _normal_segment(p1,p2) = - let(center = (p1+p2)/2) - [center, center + norm(p1-p2)/2 * line_normal(p1,p2)]; + let(center = (p1+p2)/2) + [center, center + norm(p1-p2)/2 * line_normal(p1,p2)]; // Function: turtle() @@ -574,145 +574,145 @@ function _normal_segment(p1,p2) = // koch=concat(["angle",60,"repeat",3],[concat(koch_unit(3),["left","left"])]); // polygon(turtle(koch)); function turtle(commands, state=[[[0,0]],[1,0],90,0], full_state=false, repeat=1) = - let( state = is_vector(state) ? [[state],[1,0],90,0] : state ) - repeat == 1? - _turtle(commands,state,full_state) : - _turtle_repeat(commands, state, full_state, repeat); + let( state = is_vector(state) ? [[state],[1,0],90,0] : state ) + repeat == 1? + _turtle(commands,state,full_state) : + _turtle_repeat(commands, state, full_state, repeat); function _turtle_repeat(commands, state, full_state, repeat) = - repeat==1? - _turtle(commands,state,full_state) : - _turtle_repeat(commands, _turtle(commands, state, true), full_state, repeat-1); + repeat==1? + _turtle(commands,state,full_state) : + _turtle_repeat(commands, _turtle(commands, state, true), full_state, repeat-1); function _turtle_command_len(commands, index) = - let( one_or_two_arg = ["arcleft","arcright", "arcleftto", "arcrightto"] ) - commands[index] == "repeat"? 3 : // Repeat command requires 2 args - // For these, the first arg is required, second arg is present if it is not a string - in_list(commands[index], one_or_two_arg) && len(commands)>index+2 && !is_string(commands[index+2]) ? 3 : - is_string(commands[index+1])? 1 : // If 2nd item is a string it's must be a new command - 2; // Otherwise we have command and arg + let( one_or_two_arg = ["arcleft","arcright", "arcleftto", "arcrightto"] ) + commands[index] == "repeat"? 3 : // Repeat command requires 2 args + // For these, the first arg is required, second arg is present if it is not a string + in_list(commands[index], one_or_two_arg) && len(commands)>index+2 && !is_string(commands[index+2]) ? 3 : + is_string(commands[index+1])? 1 : // If 2nd item is a string it's must be a new command + 2; // Otherwise we have command and arg function _turtle(commands, state, full_state, index=0) = - index < len(commands) ? - _turtle(commands, - _turtle_command(commands[index],commands[index+1],commands[index+2],state,index), - full_state, - index+_turtle_command_len(commands,index) - ) : - ( full_state ? state : state[0] ); + index < len(commands) ? + _turtle(commands, + _turtle_command(commands[index],commands[index+1],commands[index+2],state,index), + full_state, + index+_turtle_command_len(commands,index) + ) : + ( full_state ? state : state[0] ); // Turtle state: state = [path, step_vector, default angle] function _turtle_command(command, parm, parm2, state, index) = - command == "repeat"? - assert(is_num(parm),str("\"repeat\" command requires a numeric repeat count at index ",index)) - assert(is_list(parm2),str("\"repeat\" command requires a command list parameter at index ",index)) - _turtle_repeat(parm2, state, true, parm) : - let( - path = 0, - step=1, - angle=2, - arcsteps=3, - parm = !is_string(parm) ? parm : undef, - parm2 = !is_string(parm2) ? parm2 : undef, - needvec = ["jump", "xymove"], - neednum = ["untilx","untily","xjump","yjump","angle","length","scale","addlength"], - needeither = ["setdir"], - chvec = !in_list(command,needvec) || is_vector(parm,2), - chnum = !in_list(command,neednum) || is_num(parm), - vec_or_num = !in_list(command,needeither) || (is_num(parm) || is_vector(parm,2)), - lastpt = select(state[path],-1) - ) - assert(chvec,str("\"",command,"\" requires a vector parameter at index ",index)) - assert(chnum,str("\"",command,"\" requires a numeric parameter at index ",index)) - assert(vec_or_num,str("\"",command,"\" requires a vector or numeric parameter at index ",index)) + command == "repeat"? + assert(is_num(parm),str("\"repeat\" command requires a numeric repeat count at index ",index)) + assert(is_list(parm2),str("\"repeat\" command requires a command list parameter at index ",index)) + _turtle_repeat(parm2, state, true, parm) : + let( + path = 0, + step=1, + angle=2, + arcsteps=3, + parm = !is_string(parm) ? parm : undef, + parm2 = !is_string(parm2) ? parm2 : undef, + needvec = ["jump", "xymove"], + neednum = ["untilx","untily","xjump","yjump","angle","length","scale","addlength"], + needeither = ["setdir"], + chvec = !in_list(command,needvec) || is_vector(parm,2), + chnum = !in_list(command,neednum) || is_num(parm), + vec_or_num = !in_list(command,needeither) || (is_num(parm) || is_vector(parm,2)), + lastpt = select(state[path],-1) + ) + assert(chvec,str("\"",command,"\" requires a vector parameter at index ",index)) + assert(chnum,str("\"",command,"\" requires a numeric parameter at index ",index)) + assert(vec_or_num,str("\"",command,"\" requires a vector or numeric parameter at index ",index)) - command=="move" ? list_set(state, path, concat(state[path],[default(parm,1)*state[step]+lastpt])) : - command=="untilx" ? ( - let( - int = line_intersection([lastpt,lastpt+state[step]], [[parm,0],[parm,1]]), - xgood = sign(state[step].x) == sign(int.x-lastpt.x) - ) - assert(xgood,str("\"untilx\" never reaches desired goal at index ",index)) - list_set(state,path,concat(state[path],[int])) - ) : - command=="untily" ? ( - let( - int = line_intersection([lastpt,lastpt+state[step]], [[0,parm],[1,parm]]), - ygood = is_def(int) && sign(state[step].y) == sign(int.y-lastpt.y) - ) - assert(ygood,str("\"untily\" never reaches desired goal at index ",index)) - list_set(state,path,concat(state[path],[int])) - ) : - command=="xmove" ? list_set(state, path, concat(state[path],[default(parm,1)*norm(state[step])*[1,0]+lastpt])): - command=="ymove" ? list_set(state, path, concat(state[path],[default(parm,1)*norm(state[step])*[0,1]+lastpt])): + command=="move" ? list_set(state, path, concat(state[path],[default(parm,1)*state[step]+lastpt])) : + command=="untilx" ? ( + let( + int = line_intersection([lastpt,lastpt+state[step]], [[parm,0],[parm,1]]), + xgood = sign(state[step].x) == sign(int.x-lastpt.x) + ) + assert(xgood,str("\"untilx\" never reaches desired goal at index ",index)) + list_set(state,path,concat(state[path],[int])) + ) : + command=="untily" ? ( + let( + int = line_intersection([lastpt,lastpt+state[step]], [[0,parm],[1,parm]]), + ygood = is_def(int) && sign(state[step].y) == sign(int.y-lastpt.y) + ) + assert(ygood,str("\"untily\" never reaches desired goal at index ",index)) + list_set(state,path,concat(state[path],[int])) + ) : + command=="xmove" ? list_set(state, path, concat(state[path],[default(parm,1)*norm(state[step])*[1,0]+lastpt])): + command=="ymove" ? list_set(state, path, concat(state[path],[default(parm,1)*norm(state[step])*[0,1]+lastpt])): command=="xymove" ? list_set(state, path, concat(state[path], [lastpt+parm])): - command=="jump" ? list_set(state, path, concat(state[path],[parm])): - command=="xjump" ? list_set(state, path, concat(state[path],[[parm,lastpt.y]])): - command=="yjump" ? list_set(state, path, concat(state[path],[[lastpt.x,parm]])): - command=="turn" || command=="left" ? list_set(state, step, rot(default(parm,state[angle]),p=state[step],planar=true)) : - command=="right" ? list_set(state, step, rot(-default(parm,state[angle]),p=state[step],planar=true)) : - command=="angle" ? list_set(state, angle, parm) : - command=="setdir" ? ( - is_vector(parm) ? - list_set(state, step, norm(state[step]) * unit(parm)) : - list_set(state, step, norm(state[step]) * [cos(parm),sin(parm)]) - ) : - command=="length" ? list_set(state, step, parm*unit(state[step])) : - command=="scale" ? list_set(state, step, parm*state[step]) : - command=="addlength" ? list_set(state, step, state[step]+unit(state[step])*parm) : - command=="arcsteps" ? list_set(state, arcsteps, parm) : - command=="arcleft" || command=="arcright" ? - assert(is_num(parm),str("\"",command,"\" command requires a numeric radius value at index ",index)) - let( - myangle = default(parm2,state[angle]), - lrsign = command=="arcleft" ? 1 : -1, - radius = parm*sign(myangle), - center = lastpt + lrsign*radius*line_normal([0,0],state[step]), - steps = state[arcsteps]==0 ? segs(abs(radius)) : state[arcsteps], - arcpath = myangle == 0 || radius == 0 ? [] : arc( - steps, - points = [ - lastpt, - rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle/2), - rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle) - ] - ) - ) - list_set( - state, [path,step], [ - concat(state[path], slice(arcpath,1,-1)), - rot(lrsign * myangle,p=state[step],planar=true) - ] - ) : - command=="arcleftto" || command=="arcrightto" ? - assert(is_num(parm),str("\"",command,"\" command requires a numeric radius value at index ",index)) - assert(is_num(parm2),str("\"",command,"\" command requires a numeric angle value at index ",index)) - let( - radius = parm, - lrsign = command=="arcleftto" ? 1 : -1, - center = lastpt + lrsign*radius*line_normal([0,0],state[step]), - steps = state[arcsteps]==0 ? segs(abs(radius)) : state[arcsteps], - start_angle = posmod(atan2(state[step].y, state[step].x),360), - end_angle = posmod(parm2,360), - delta_angle = -start_angle + (lrsign * end_angle < lrsign*start_angle ? end_angle+lrsign*360 : end_angle), - arcpath = delta_angle == 0 || radius==0 ? [] : arc( - steps, - points = [ - lastpt, - rot(cp=center, p=lastpt, a=sign(radius)*delta_angle/2), - rot(cp=center, p=lastpt, a=sign(radius)*delta_angle) - ] - ) - ) - list_set( - state, [path,step], [ - concat(state[path], slice(arcpath,1,-1)), - rot(delta_angle,p=state[step],planar=true) - ] - ) : - assert(false,str("Unknown turtle command \"",command,"\" at index",index)) - []; + command=="jump" ? list_set(state, path, concat(state[path],[parm])): + command=="xjump" ? list_set(state, path, concat(state[path],[[parm,lastpt.y]])): + command=="yjump" ? list_set(state, path, concat(state[path],[[lastpt.x,parm]])): + command=="turn" || command=="left" ? list_set(state, step, rot(default(parm,state[angle]),p=state[step],planar=true)) : + command=="right" ? list_set(state, step, rot(-default(parm,state[angle]),p=state[step],planar=true)) : + command=="angle" ? list_set(state, angle, parm) : + command=="setdir" ? ( + is_vector(parm) ? + list_set(state, step, norm(state[step]) * unit(parm)) : + list_set(state, step, norm(state[step]) * [cos(parm),sin(parm)]) + ) : + command=="length" ? list_set(state, step, parm*unit(state[step])) : + command=="scale" ? list_set(state, step, parm*state[step]) : + command=="addlength" ? list_set(state, step, state[step]+unit(state[step])*parm) : + command=="arcsteps" ? list_set(state, arcsteps, parm) : + command=="arcleft" || command=="arcright" ? + assert(is_num(parm),str("\"",command,"\" command requires a numeric radius value at index ",index)) + let( + myangle = default(parm2,state[angle]), + lrsign = command=="arcleft" ? 1 : -1, + radius = parm*sign(myangle), + center = lastpt + lrsign*radius*line_normal([0,0],state[step]), + steps = state[arcsteps]==0 ? segs(abs(radius)) : state[arcsteps], + arcpath = myangle == 0 || radius == 0 ? [] : arc( + steps, + points = [ + lastpt, + rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle/2), + rot(cp=center, p=lastpt, a=sign(parm)*lrsign*myangle) + ] + ) + ) + list_set( + state, [path,step], [ + concat(state[path], slice(arcpath,1,-1)), + rot(lrsign * myangle,p=state[step],planar=true) + ] + ) : + command=="arcleftto" || command=="arcrightto" ? + assert(is_num(parm),str("\"",command,"\" command requires a numeric radius value at index ",index)) + assert(is_num(parm2),str("\"",command,"\" command requires a numeric angle value at index ",index)) + let( + radius = parm, + lrsign = command=="arcleftto" ? 1 : -1, + center = lastpt + lrsign*radius*line_normal([0,0],state[step]), + steps = state[arcsteps]==0 ? segs(abs(radius)) : state[arcsteps], + start_angle = posmod(atan2(state[step].y, state[step].x),360), + end_angle = posmod(parm2,360), + delta_angle = -start_angle + (lrsign * end_angle < lrsign*start_angle ? end_angle+lrsign*360 : end_angle), + arcpath = delta_angle == 0 || radius==0 ? [] : arc( + steps, + points = [ + lastpt, + rot(cp=center, p=lastpt, a=sign(radius)*delta_angle/2), + rot(cp=center, p=lastpt, a=sign(radius)*delta_angle) + ] + ) + ) + list_set( + state, [path,step], [ + concat(state[path], slice(arcpath,1,-1)), + rot(delta_angle,p=state[step],planar=true) + ] + ) : + assert(false,str("Unknown turtle command \"",command,"\" at index",index)) + []; @@ -750,70 +750,70 @@ function _turtle_command(command, parm, parm2, state, index) = // stroke(path, closed=true); // move_copies(path) color("blue") circle(d=2,$fn=8); module rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) { - size = is_num(size)? [size,size] : point2d(size); - anchor = get_anchor(anchor, center, FRONT+LEFT, FRONT+LEFT); - if (rounding==0 && chamfer==0) { - attachable(anchor,spin, two_d=true, size=size) { - square(size, center=true); - children(); - } - } else { - pts = rect(size=size, rounding=rounding, chamfer=chamfer, center=true); - attachable(anchor,spin, two_d=true, path=pts) { - polygon(pts); - children(); - } - } + size = is_num(size)? [size,size] : point2d(size); + anchor = get_anchor(anchor, center, FRONT+LEFT, FRONT+LEFT); + if (rounding==0 && chamfer==0) { + attachable(anchor,spin, two_d=true, size=size) { + square(size, center=true); + children(); + } + } else { + pts = rect(size=size, rounding=rounding, chamfer=chamfer, center=true); + attachable(anchor,spin, two_d=true, path=pts) { + polygon(pts); + children(); + } + } } function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) = - assert(is_num(size) || is_vector(size)) - assert(is_num(chamfer) || len(chamfer)==4) - assert(is_num(rounding) || len(rounding)==4) - let( - size = is_num(size)? [size,size] : point2d(size), - anchor = get_anchor(anchor, center, FRONT+LEFT, FRONT+LEFT), - complex = rounding!=0 || chamfer!=0 - ) - (rounding==0 && chamfer==0)? let( - path = [ - [ size.x/2, -size.y/2], - [-size.x/2, -size.y/2], - [-size.x/2, size.y/2], - [ size.x/2, size.y/2] - ] - ) rot(spin, p=move(-vmul(anchor,size/2), p=path)) : - let( - chamfer = is_list(chamfer)? chamfer : [for (i=[0:3]) chamfer], - rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding], - quadorder = [3,2,1,0], - quadpos = [[1,1],[-1,1],[-1,-1],[1,-1]], - insets = [for (i=[0:3]) chamfer[i]>0? chamfer[i] : rounding[i]>0? rounding[i] : 0], - insets_x = max(insets[0]+insets[1],insets[2]+insets[3]), - insets_y = max(insets[0]+insets[3],insets[1]+insets[2]) - ) - assert(insets_x <= size.x, "Requested roundings and/or chamfers exceed the rect width.") - assert(insets_y <= size.y, "Requested roundings and/or chamfers exceed the rect height.") - let( - path = [ - for(i = [0:3]) - let( - quad = quadorder[i], - inset = insets[quad], - cverts = quant(segs(inset),4)/4, - cp = vmul(size/2-[inset,inset], quadpos[quad]), - step = 90/cverts, - angs = - chamfer[quad] > 0? [0,-90]-90*[i,i] : - rounding[quad] > 0? [for (j=[0:1:cverts]) 360-j*step-i*90] : - [0] - ) - each [for (a = angs) cp + inset*[cos(a),sin(a)]] - ] - ) complex? - reorient(anchor,spin, two_d=true, path=path, p=path) : - reorient(anchor,spin, two_d=true, size=size, p=path); + assert(is_num(size) || is_vector(size)) + assert(is_num(chamfer) || len(chamfer)==4) + assert(is_num(rounding) || len(rounding)==4) + let( + size = is_num(size)? [size,size] : point2d(size), + anchor = get_anchor(anchor, center, FRONT+LEFT, FRONT+LEFT), + complex = rounding!=0 || chamfer!=0 + ) + (rounding==0 && chamfer==0)? let( + path = [ + [ size.x/2, -size.y/2], + [-size.x/2, -size.y/2], + [-size.x/2, size.y/2], + [ size.x/2, size.y/2] + ] + ) rot(spin, p=move(-vmul(anchor,size/2), p=path)) : + let( + chamfer = is_list(chamfer)? chamfer : [for (i=[0:3]) chamfer], + rounding = is_list(rounding)? rounding : [for (i=[0:3]) rounding], + quadorder = [3,2,1,0], + quadpos = [[1,1],[-1,1],[-1,-1],[1,-1]], + insets = [for (i=[0:3]) chamfer[i]>0? chamfer[i] : rounding[i]>0? rounding[i] : 0], + insets_x = max(insets[0]+insets[1],insets[2]+insets[3]), + insets_y = max(insets[0]+insets[3],insets[1]+insets[2]) + ) + assert(insets_x <= size.x, "Requested roundings and/or chamfers exceed the rect width.") + assert(insets_y <= size.y, "Requested roundings and/or chamfers exceed the rect height.") + let( + path = [ + for(i = [0:3]) + let( + quad = quadorder[i], + inset = insets[quad], + cverts = quant(segs(inset),4)/4, + cp = vmul(size/2-[inset,inset], quadpos[quad]), + step = 90/cverts, + angs = + chamfer[quad] > 0? [0,-90]-90*[i,i] : + rounding[quad] > 0? [for (j=[0:1:cverts]) 360-j*step-i*90] : + [0] + ) + each [for (a = angs) cp + inset*[cos(a),sin(a)]] + ] + ) complex? + reorient(anchor,spin, two_d=true, path=path, p=path) : + reorient(anchor,spin, two_d=true, size=size, p=path); // Function&Module: oval() @@ -840,40 +840,40 @@ function rect(size=1, center, rounding=0, chamfer=0, anchor, spin=0) = // Example(NORENDER): Called as Function // path = oval(d=50, anchor=FRONT, spin=45); module oval(r, d, realign=false, circum=false, anchor=CENTER, spin=0) { - r = get_radius(r=r, d=d, dflt=1); - sides = segs(max(r)); - sc = circum? (1 / cos(180/sides)) : 1; - rx = default(r[0],r) * sc; - ry = default(r[1],r) * sc; - attachable(anchor,spin, two_d=true, r=[rx,ry]) { - if (rx < ry) { - xscale(rx/ry) { - zrot(realign? 180/sides : 0) { - circle(r=ry, $fn=sides); - } - } - } else { - yscale(ry/rx) { - zrot(realign? 180/sides : 0) { - circle(r=rx, $fn=sides); - } - } - } - children(); - } + r = get_radius(r=r, d=d, dflt=1); + sides = segs(max(r)); + sc = circum? (1 / cos(180/sides)) : 1; + rx = default(r[0],r) * sc; + ry = default(r[1],r) * sc; + attachable(anchor,spin, two_d=true, r=[rx,ry]) { + if (rx < ry) { + xscale(rx/ry) { + zrot(realign? 180/sides : 0) { + circle(r=ry, $fn=sides); + } + } + } else { + yscale(ry/rx) { + zrot(realign? 180/sides : 0) { + circle(r=rx, $fn=sides); + } + } + } + children(); + } } function oval(r, d, realign=false, circum=false, anchor=CENTER, spin=0) = - let( - r = get_radius(r=r, d=d, dflt=1), - sides = segs(max(r)), - offset = realign? 180/sides : 0, - sc = circum? (1 / cos(180/sides)) : 1, - rx = default(r[0],r) * sc, - ry = default(r[1],r) * sc, - pts = [for (i=[0:1:sides-1]) let(a=360-offset-i*360/sides) [rx*cos(a), ry*sin(a)]] - ) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts); + let( + r = get_radius(r=r, d=d, dflt=1), + sides = segs(max(r)), + offset = realign? 180/sides : 0, + sc = circum? (1 / cos(180/sides)) : 1, + rx = default(r[0],r) * sc, + ry = default(r[1],r) * sc, + pts = [for (i=[0:1:sides-1]) let(a=360-offset-i*360/sides) [rx*cos(a), ry*sin(a)]] + ) reorient(anchor,spin, two_d=true, r=[rx,ry], p=pts); @@ -918,67 +918,67 @@ function oval(r, d, realign=false, circum=false, anchor=CENTER, spin=0) = // Example(2D): Called as Function // stroke(closed=true, regular_ngon(n=6, or=30)); function regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) = - let( - sc = 1/cos(180/n), - r = get_radius(r1=ir*sc, r2=or, r=r, d1=id*sc, d2=od, d=d, dflt=side/2/sin(180/n)) - ) - assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side.") - let( - inset = opp_ang_to_hyp(rounding, (180-360/n)/2), - path = rounding==0? oval(r=r, realign=realign, $fn=n) : ( - let( - steps = floor(segs(r)/n), - step = 360/n/steps, - path2 = [ - for (i = [0:1:n-1]) let( - a = 360 - i*360/n - (realign? 180/n : 0), - p = polar_to_xy(r-inset, a) - ) - each arc(N=steps, cp=p, r=rounding, start=a+180/n, angle=-360/n) - ], - maxx_idx = max_index(subindex(path2,0)), - path3 = polygon_shift(path2,maxx_idx) - ) path3 - ), - anchors = !is_string(anchor)? [] : [ - for (i = [0:1:n-1]) let( - a1 = 360 - i*360/n - (realign? 180/n : 0), - a2 = a1 - 360/n, - p1 = polar_to_xy(r,a1), - p2 = polar_to_xy(r,a2), - tipp = polar_to_xy(r-inset+rounding,a1), - pos = (p1+p2)/2 - ) each [ - anchorpt(str("tip",i), tipp, unit(tipp), 0), - anchorpt(str("side",i), pos, unit(pos), 0), - ] - ] - ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path, anchors=anchors); + let( + sc = 1/cos(180/n), + r = get_radius(r1=ir*sc, r2=or, r=r, d1=id*sc, d2=od, d=d, dflt=side/2/sin(180/n)) + ) + assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side.") + let( + inset = opp_ang_to_hyp(rounding, (180-360/n)/2), + path = rounding==0? oval(r=r, realign=realign, $fn=n) : ( + let( + steps = floor(segs(r)/n), + step = 360/n/steps, + path2 = [ + for (i = [0:1:n-1]) let( + a = 360 - i*360/n - (realign? 180/n : 0), + p = polar_to_xy(r-inset, a) + ) + each arc(N=steps, cp=p, r=rounding, start=a+180/n, angle=-360/n) + ], + maxx_idx = max_index(subindex(path2,0)), + path3 = polygon_shift(path2,maxx_idx) + ) path3 + ), + anchors = !is_string(anchor)? [] : [ + for (i = [0:1:n-1]) let( + a1 = 360 - i*360/n - (realign? 180/n : 0), + a2 = a1 - 360/n, + p1 = polar_to_xy(r,a1), + p2 = polar_to_xy(r,a2), + tipp = polar_to_xy(r-inset+rounding,a1), + pos = (p1+p2)/2 + ) each [ + anchorpt(str("tip",i), tipp, unit(tipp), 0), + anchorpt(str("side",i), pos, unit(pos), 0), + ] + ] + ) reorient(anchor,spin, two_d=true, path=path, extent=false, p=path, anchors=anchors); module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) { - sc = 1/cos(180/n); - r = get_radius(r1=ir*sc, r2=or, r=r, d1=id*sc, d2=od, d=d, dflt=side/2/sin(180/n)); - assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side."); - path = regular_ngon(n=n, r=r, rounding=rounding, realign=realign); - inset = opp_ang_to_hyp(rounding, (180-360/n)/2); - anchors = [ - for (i = [0:1:n-1]) let( - a1 = 360 - i*360/n - (realign? 180/n : 0), - a2 = a1 - 360/n, - p1 = polar_to_xy(r,a1), - p2 = polar_to_xy(r,a2), - tipp = polar_to_xy(r-inset+rounding,a1), - pos = (p1+p2)/2 - ) each [ - anchorpt(str("tip",i), tipp, unit(tipp), 0), - anchorpt(str("side",i), pos, unit(pos), 0), - ] - ]; - attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) { - polygon(path); - children(); - } + sc = 1/cos(180/n); + r = get_radius(r1=ir*sc, r2=or, r=r, d1=id*sc, d2=od, d=d, dflt=side/2/sin(180/n)); + assert(!is_undef(r), "regular_ngon(): need to specify one of r, d, or, od, ir, id, side."); + path = regular_ngon(n=n, r=r, rounding=rounding, realign=realign); + inset = opp_ang_to_hyp(rounding, (180-360/n)/2); + anchors = [ + for (i = [0:1:n-1]) let( + a1 = 360 - i*360/n - (realign? 180/n : 0), + a2 = a1 - 360/n, + p1 = polar_to_xy(r,a1), + p2 = polar_to_xy(r,a2), + tipp = polar_to_xy(r-inset+rounding,a1), + pos = (p1+p2)/2 + ) each [ + anchorpt(str("tip",i), tipp, unit(tipp), 0), + anchorpt(str("side",i), pos, unit(pos), 0), + ] + ]; + attachable(anchor,spin, two_d=true, path=path, extent=false, anchors=anchors) { + polygon(path); + children(); + } } @@ -1020,11 +1020,11 @@ module regular_ngon(n=6, r, d, or, od, ir, id, side, rounding=0, realign=false, // Example(2D): Called as Function // stroke(closed=true, pentagon(or=30)); function pentagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) = - regular_ngon(n=5, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin); + regular_ngon(n=5, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin); module pentagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) - regular_ngon(n=5, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin) children(); + regular_ngon(n=5, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin) children(); // Function&Module: hexagon() @@ -1063,11 +1063,11 @@ module pentagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CE // Example(2D): Called as Function // stroke(closed=true, hexagon(or=30)); function hexagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) = - regular_ngon(n=6, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin); + regular_ngon(n=6, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin); module hexagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) - regular_ngon(n=6, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin) children(); + regular_ngon(n=6, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin) children(); // Function&Module: octagon() @@ -1106,11 +1106,11 @@ module hexagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CEN // Example(2D): Called as Function // stroke(closed=true, octagon(or=30)); function octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) = - regular_ngon(n=8, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin); + regular_ngon(n=8, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin); module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CENTER, spin=0) - regular_ngon(n=8, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin) children(); + regular_ngon(n=8, r=r, d=d, or=or, od=od, ir=ir, id=id, side=side, rounding=rounding, realign=realign, anchor=anchor, spin=spin) children(); @@ -1136,18 +1136,18 @@ module octagon(r, d, or, od, ir, id, side, rounding=0, realign=false, anchor=CEN // Example(2D): Called as Function // stroke(closed=true, trapezoid(h=30, w1=40, w2=20)); function trapezoid(h, w1, w2, anchor=CENTER, spin=0) = - let( - path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2]] - ) reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, p=path); + let( + path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2]] + ) reorient(anchor,spin, two_d=true, size=[w1,h], size2=w2, p=path); module trapezoid(h, w1, w2, anchor=CENTER, spin=0) { - path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2]]; - attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2) { - polygon(path); - children(); - } + path = [[w1/2,-h/2], [-w1/2,-h/2], [-w2/2,h/2], [w2/2,h/2]]; + attachable(anchor,spin, two_d=true, size=[w1,h], size2=w2) { + polygon(path); + children(); + } } @@ -1175,37 +1175,37 @@ module trapezoid(h, w1, w2, anchor=CENTER, spin=0) { // teardrop2d(r=30, ang=30, cap_h=20); module teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) { - path = teardrop2d(r=r, d=d, ang=ang, cap_h=cap_h); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } + path = teardrop2d(r=r, d=d, ang=ang, cap_h=cap_h); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } } function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) = - let( - r = get_radius(r=r, d=d, dflt=1), - cord = 2 * r * cos(ang), - cord_h = r * sin(ang), - tip_y = (cord/2)/tan(ang), - cap_h = min((!is_undef(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h), - cap_w = cord * (1 - (cap_h - cord_h)/tip_y), - ang = min(ang,asin(cap_h/r)), - sa = 180 - ang, - ea = 360 + ang, - steps = segs(r)*(ea-sa)/360, - step = (ea-sa)/steps, - path = deduplicate( - [ - [ cap_w/2,cap_h], - for (i=[0:1:steps]) let(a=ea-i*step) r*[cos(a),sin(a)], - [-cap_w/2,cap_h] - ], closed=true - ), - maxx_idx = max_index(subindex(path,0)), - path2 = polygon_shift(path,maxx_idx) - ) reorient(anchor,spin, two_d=true, path=path2, p=path2); + let( + r = get_radius(r=r, d=d, dflt=1), + cord = 2 * r * cos(ang), + cord_h = r * sin(ang), + tip_y = (cord/2)/tan(ang), + cap_h = min((!is_undef(cap_h)? cap_h : tip_y+cord_h), tip_y+cord_h), + cap_w = cord * (1 - (cap_h - cord_h)/tip_y), + ang = min(ang,asin(cap_h/r)), + sa = 180 - ang, + ea = 360 + ang, + steps = segs(r)*(ea-sa)/360, + step = (ea-sa)/steps, + path = deduplicate( + [ + [ cap_w/2,cap_h], + for (i=[0:1:steps]) let(a=ea-i*step) r*[cos(a),sin(a)], + [-cap_w/2,cap_h] + ], closed=true + ), + maxx_idx = max_index(subindex(path,0)), + path2 = polygon_shift(path,maxx_idx) + ) reorient(anchor,spin, two_d=true, path=path2, p=path2); @@ -1230,38 +1230,38 @@ function teardrop2d(r, d, ang=45, cap_h, anchor=CENTER, spin=0) = // Example(2D): Called as Function // stroke(closed=true, glued_circles(r=15, spread=40, tangent=45)); function glued_circles(r, d, spread=10, tangent=30, anchor=CENTER, spin=0) = - let( - r = get_radius(r=r, d=d, dflt=10), - r2 = (spread/2 / sin(tangent)) - r, - cp1 = [spread/2, 0], - cp2 = [0, (r+r2)*cos(tangent)], - sa1 = 90-tangent, - ea1 = 270+tangent, - lobearc = ea1-sa1, - lobesegs = floor(segs(r)*lobearc/360), - lobestep = lobearc / lobesegs, - sa2 = 270-tangent, - ea2 = 270+tangent, - subarc = ea2-sa2, - arcsegs = ceil(segs(r2)*abs(subarc)/360), - arcstep = subarc / arcsegs, - path = concat( - [for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep) r * [cos(a),sin(a)] - cp1], - tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep+180) r2 * [cos(a),sin(a)] - cp2], - [for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep+180) r * [cos(a),sin(a)] + cp1], - tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep) r2 * [cos(a),sin(a)] + cp2] - ), - maxx_idx = max_index(subindex(path,0)), - path2 = reverse_polygon(polygon_shift(path,maxx_idx)) - ) reorient(anchor,spin, two_d=true, path=path2, extent=true, p=path2); + let( + r = get_radius(r=r, d=d, dflt=10), + r2 = (spread/2 / sin(tangent)) - r, + cp1 = [spread/2, 0], + cp2 = [0, (r+r2)*cos(tangent)], + sa1 = 90-tangent, + ea1 = 270+tangent, + lobearc = ea1-sa1, + lobesegs = floor(segs(r)*lobearc/360), + lobestep = lobearc / lobesegs, + sa2 = 270-tangent, + ea2 = 270+tangent, + subarc = ea2-sa2, + arcsegs = ceil(segs(r2)*abs(subarc)/360), + arcstep = subarc / arcsegs, + path = concat( + [for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep) r * [cos(a),sin(a)] - cp1], + tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep+180) r2 * [cos(a),sin(a)] - cp2], + [for (i=[0:1:lobesegs]) let(a=sa1+i*lobestep+180) r * [cos(a),sin(a)] + cp1], + tangent==0? [] : [for (i=[0:1:arcsegs]) let(a=ea2-i*arcstep) r2 * [cos(a),sin(a)] + cp2] + ), + maxx_idx = max_index(subindex(path,0)), + path2 = reverse_polygon(polygon_shift(path,maxx_idx)) + ) reorient(anchor,spin, two_d=true, path=path2, extent=true, p=path2); module glued_circles(r, d, spread=10, tangent=30, anchor=CENTER, spin=0) { - path = glued_circles(r=r, d=d, spread=spread, tangent=tangent); - attachable(anchor,spin, two_d=true, path=path, extent=true) { - polygon(path); - children(); - } + path = glued_circles(r=r, d=d, spread=spread, tangent=tangent); + attachable(anchor,spin, two_d=true, path=path, extent=true) { + polygon(path); + children(); + } } @@ -1297,66 +1297,66 @@ module glued_circles(r, d, spread=10, tangent=30, anchor=CENTER, spin=0) { // Example(2D): Called as Function // stroke(closed=true, star(n=5, r=50, ir=25)); function star(n, r, d, or, od, ir, id, step, realign=false, anchor=CENTER, spin=0) = - let( - r = get_radius(r1=or, d1=od, r=r, d=d), - count = num_defined([ir,id,step]), - stepOK = is_undef(step) || (step>1 && step1 && step0 && angle<90) - assert(is_num(excess)) - let( - r = get_radius(r=r, d=d, dflt=1), - n = ceil(segs(r) * angle/360), - cp = [r,r], - tp = cp + polar_to_xy(r,180+angle), - bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0], - step = angle/n, - path = [ - bp, bp-[0,excess], [-excess,-excess], [-excess,r], - for (i=[0:1:n]) cp+polar_to_xy(r,180+i*step) - ] - ) reorient(anchor,spin, two_d=true, path=path, p=path); + assert(is_num(angle)) + assert(angle>0 && angle<90) + assert(is_num(excess)) + let( + r = get_radius(r=r, d=d, dflt=1), + n = ceil(segs(r) * angle/360), + cp = [r,r], + tp = cp + polar_to_xy(r,180+angle), + bp = [tp.x+adj_ang_to_opp(tp.y,angle), 0], + step = angle/n, + path = [ + bp, bp-[0,excess], [-excess,-excess], [-excess,r], + for (i=[0:1:n]) cp+polar_to_xy(r,180+i*step) + ] + ) reorient(anchor,spin, two_d=true, path=path, p=path); module mask2d_teardrop(r,d,angle=45,excess=0.1,anchor=CENTER,spin=0) { - path = mask2d_teardrop(r=r, d=d, angle=angle, excess=excess); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } + path = mask2d_teardrop(r=r, d=d, angle=angle, excess=excess); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } } // Function&Module: mask2d_ogee() @@ -1773,81 +1773,81 @@ module mask2d_teardrop(r,d,angle=45,excess=0.1,anchor=CENTER,spin=0) { // "ystep",1, "xstep",1 // Ending shoulder. // ]); module mask2d_ogee(pattern, excess, anchor=CENTER,spin=0) { - path = mask2d_ogee(pattern, excess=excess); - attachable(anchor,spin, two_d=true, path=path) { - polygon(path); - children(); - } + path = mask2d_ogee(pattern, excess=excess); + attachable(anchor,spin, two_d=true, path=path) { + polygon(path); + children(); + } } function mask2d_ogee(pattern, excess, anchor=CENTER, spin=0) = - assert(is_list(pattern)) - assert(len(pattern)>0) - assert(len(pattern)%2==0,"pattern must be a list of TYPE, VAL pairs.") - assert(all([for (i = idx(pattern,step=2)) in_list(pattern[i],["step","xstep","ystep","round","fillet"])])) - let( - excess = default(excess,$overlap), - x = concat([0], cumsum([ - for (i=idx(pattern,step=2)) let( - type = pattern[i], - val = pattern[i+1] - ) ( - type=="step"? val.x : - type=="xstep"? val : - type=="round"? val : - type=="fillet"? val : - 0 - ) - ])), - y = concat([0], cumsum([ - for (i=idx(pattern,step=2)) let( - type = pattern[i], - val = pattern[i+1] - ) ( - type=="step"? val.y : - type=="ystep"? val : - type=="round"? val : - type=="fillet"? val : - 0 - ) - ])), - tot_x = select(x,-1), - tot_y = select(y,-1), - data = [ - for (i=idx(pattern,step=2)) let( - type = pattern[i], - val = pattern[i+1], - pt = [x[i/2], tot_y-y[i/2]] + ( - type=="step"? [val.x,-val.y] : - type=="xstep"? [val,0] : - type=="ystep"? [0,-val] : - type=="round"? [val,0] : - type=="fillet"? [0,-val] : - [0,0] - ) - ) [type, val, pt] - ], - path = [ - [tot_x,-excess], - [-excess,-excess], - [-excess,tot_y], - for (pat = data) each - pat[0]=="step"? [pat[2]] : - pat[0]=="xstep"? [pat[2]] : - pat[0]=="ystep"? [pat[2]] : - let( - r = pat[1], - steps = segs(abs(r)), - step = 90/steps - ) [ - for (i=[0:1:steps]) let( - a = pat[0]=="round"? (180+i*step) : (90-i*step) - ) pat[2] + abs(r)*[cos(a),sin(a)] - ] - ], - path2 = deduplicate(path) - ) reorient(anchor,spin, two_d=true, path=path2, p=path2); + assert(is_list(pattern)) + assert(len(pattern)>0) + assert(len(pattern)%2==0,"pattern must be a list of TYPE, VAL pairs.") + assert(all([for (i = idx(pattern,step=2)) in_list(pattern[i],["step","xstep","ystep","round","fillet"])])) + let( + excess = default(excess,$overlap), + x = concat([0], cumsum([ + for (i=idx(pattern,step=2)) let( + type = pattern[i], + val = pattern[i+1] + ) ( + type=="step"? val.x : + type=="xstep"? val : + type=="round"? val : + type=="fillet"? val : + 0 + ) + ])), + y = concat([0], cumsum([ + for (i=idx(pattern,step=2)) let( + type = pattern[i], + val = pattern[i+1] + ) ( + type=="step"? val.y : + type=="ystep"? val : + type=="round"? val : + type=="fillet"? val : + 0 + ) + ])), + tot_x = select(x,-1), + tot_y = select(y,-1), + data = [ + for (i=idx(pattern,step=2)) let( + type = pattern[i], + val = pattern[i+1], + pt = [x[i/2], tot_y-y[i/2]] + ( + type=="step"? [val.x,-val.y] : + type=="xstep"? [val,0] : + type=="ystep"? [0,-val] : + type=="round"? [val,0] : + type=="fillet"? [0,-val] : + [0,0] + ) + ) [type, val, pt] + ], + path = [ + [tot_x,-excess], + [-excess,-excess], + [-excess,tot_y], + for (pat = data) each + pat[0]=="step"? [pat[2]] : + pat[0]=="xstep"? [pat[2]] : + pat[0]=="ystep"? [pat[2]] : + let( + r = pat[1], + steps = segs(abs(r)), + step = 90/steps + ) [ + for (i=[0:1:steps]) let( + a = pat[0]=="round"? (180+i*step) : (90-i*step) + ) pat[2] + abs(r)*[cos(a),sin(a)] + ] + ], + path2 = deduplicate(path) + ) reorient(anchor,spin, two_d=true, path=path2, p=path2); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/skin.scad b/skin.scad index 8267eb4..648ceab 100644 --- a/skin.scad +++ b/skin.scad @@ -163,26 +163,26 @@ include // Example: Vaccum nozzle example from list-comprehension-demos, using "length" sampling (the default) // xrot(90)down(1.5) // difference() { -// skin( -// [square([2,.2],center=true), -// circle($fn=64,r=0.5)], z=[0,3], -// slices=40,sampling="length",method="reindex"); -// skin( -// [square([1.9,.1],center=true), -// circle($fn=64,r=0.45)], z=[-.01,3.01], -// slices=40,sampling="length",method="reindex"); +// skin( +// [square([2,.2],center=true), +// circle($fn=64,r=0.5)], z=[0,3], +// slices=40,sampling="length",method="reindex"); +// skin( +// [square([1.9,.1],center=true), +// circle($fn=64,r=0.45)], z=[-.01,3.01], +// slices=40,sampling="length",method="reindex"); // } // Example: Same thing with "segment" sampling // xrot(90)down(1.5) // difference() { -// skin( -// [square([2,.2],center=true), -// circle($fn=64,r=0.5)], z=[0,3], -// slices=40,sampling="segment",method="reindex"); -// skin( -// [square([1.9,.1],center=true), -// circle($fn=64,r=0.45)], z=[-.01,3.01], -// slices=40,sampling="segment",method="reindex"); +// skin( +// [square([2,.2],center=true), +// circle($fn=64,r=0.5)], z=[0,3], +// slices=40,sampling="segment",method="reindex"); +// skin( +// [square([1.9,.1],center=true), +// circle($fn=64,r=0.45)], z=[-.01,3.01], +// slices=40,sampling="segment",method="reindex"); // } // Example: Forma Candle Holder (from list-comprehension-demos) // r = 50; @@ -319,7 +319,7 @@ include module skin(profiles, slices, refine=1, method="direct", sampling, caps, closed=false, z, convexity=10) { - vnf_polyhedron(skin(profiles, slices, refine, method, sampling, caps, closed, z), convexity=convexity); + vnf_polyhedron(skin(profiles, slices, refine, method, sampling, caps, closed, z), convexity=convexity); } @@ -421,56 +421,56 @@ function skin(profiles, slices, refine=1, method="direct", sampling, caps, close function _skin_core(profiles, caps) = - let( + let( vertices = [for (prof=profiles) each prof], - plens = [for (prof=profiles) len(prof)], - sidefaces = [ - for(pidx=idx(profiles,end=-2)) - let( - prof1 = profiles[pidx%len(profiles)], - prof2 = profiles[(pidx+1)%len(profiles)], - voff = default(sum([for (i=[0:1:pidx-1]) plens[i]]),0), - faces = [ - for( - first = true, - finishing = false, - finished = false, - plen1 = len(prof1), - plen2 = len(prof2), - i=0, j=0, side=0; + plens = [for (prof=profiles) len(prof)], + sidefaces = [ + for(pidx=idx(profiles,end=-2)) + let( + prof1 = profiles[pidx%len(profiles)], + prof2 = profiles[(pidx+1)%len(profiles)], + voff = default(sum([for (i=[0:1:pidx-1]) plens[i]]),0), + faces = [ + for( + first = true, + finishing = false, + finished = false, + plen1 = len(prof1), + plen2 = len(prof2), + i=0, j=0, side=0; - !finished; + !finished; - side = - let( - p1a = prof1[(i+0)%plen1], - p1b = prof1[(i+1)%plen1], - p2a = prof2[(j+0)%plen2], - p2b = prof2[(j+1)%plen2], - dist1 = norm(p1a-p2b), - dist2 = norm(p1b-p2a) - ) (i==j) ? (dist1>dist2? 1 : 0) : (i=plen1 && j>=plen2 - ) if (!first) face - ] - ) each faces - ], + side = + let( + p1a = prof1[(i+0)%plen1], + p1b = prof1[(i+1)%plen1], + p2a = prof2[(j+0)%plen2], + p2b = prof2[(j+1)%plen2], + dist1 = norm(p1a-p2b), + dist2 = norm(p1b-p2a) + ) (i==j) ? (dist1>dist2? 1 : 0) : (i=plen1 && j>=plen2 + ) if (!first) face + ] + ) each faces + ], firstcap = !caps[0] ? [] : let( prof1 = profiles[0] ) [[for (i=idx(prof1)) plens[0]-1-i]], secondcap = !caps[1] ? [] : let( - prof2 = select(profiles,-1), - eoff = sum(select(plens,0,-2)) - ) [[for (i=idx(prof2)) eoff+i]] - ) [vertices, concat(sidefaces,firstcap,secondcap)]; + prof2 = select(profiles,-1), + eoff = sum(select(plens,0,-2)) + ) [[for (i=idx(prof2)) eoff+i]] + ) [vertices, concat(sidefaces,firstcap,secondcap)]; @@ -1223,4 +1223,4 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/sliders.scad b/sliders.scad index 2d5b0e4..466fffb 100644 --- a/sliders.scad +++ b/sliders.scad @@ -32,30 +32,30 @@ // slider(l=30, base=10, wall=4, $slop=0.2, spin=90); module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP) { - full_width = w + 2*wall; - full_height = h + base; + full_width = w + 2*wall; + full_height = h + base; - attachable(anchor,spin,orient, size=[full_width, l, h+2*base]) { - zrot(90) - down(base+h/2) { - // Base - cuboid([full_width, l, base-$slop], chamfer=2, edges=edges([FRONT,BACK], except=BOT), anchor=BOTTOM); + attachable(anchor,spin,orient, size=[full_width, l, h+2*base]) { + zrot(90) + down(base+h/2) { + // Base + cuboid([full_width, l, base-$slop], chamfer=2, edges=edges([FRONT,BACK], except=BOT), anchor=BOTTOM); - // Wall - xflip_copy(offset=w/2+$slop) { - cuboid([wall, l, full_height], chamfer=2, edges=edges(RIGHT, except=BOT), anchor=BOTTOM+LEFT); - } + // Wall + xflip_copy(offset=w/2+$slop) { + cuboid([wall, l, full_height], chamfer=2, edges=edges(RIGHT, except=BOT), anchor=BOTTOM+LEFT); + } - // Sliders - up(base+h/2) { - xflip_copy(offset=w/2+$slop+0.02) { - bev_h = h/2*tan(ang); - prismoid([h, l], [0, l-w], h=bev_h+0.01, orient=LEFT, anchor=BOT); - } - } - } - children(); - } + // Sliders + up(base+h/2) { + xflip_copy(offset=w/2+$slop+0.02) { + bev_h = h/2*tan(ang); + prismoid([h, l], [0, l-w], h=bev_h+0.01, orient=LEFT, anchor=BOT); + } + } + } + children(); + } } @@ -78,129 +78,129 @@ module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, anchor=BOTTOM, spin=0, // rail(l=100, w=10, h=10); module rail(l=30, w=10, h=10, chamfer=1.0, ang=30, anchor=BOTTOM, spin=0, orient=UP) { - attack_ang = 30; - attack_len = 2; + attack_ang = 30; + attack_len = 2; - fudge = 1.177; - chamf = sqrt(2) * chamfer; - cosa = cos(ang*fudge); - sina = sin(ang*fudge); + fudge = 1.177; + chamf = sqrt(2) * chamfer; + cosa = cos(ang*fudge); + sina = sin(ang*fudge); - z1 = h/2; - z2 = z1 - chamf * cosa; - z3 = z1 - attack_len * sin(attack_ang); - z4 = 0; + z1 = h/2; + z2 = z1 - chamf * cosa; + z3 = z1 - attack_len * sin(attack_ang); + z4 = 0; - x1 = w/2; - x2 = x1 - chamf * sina; - x3 = x1 - chamf; - x4 = x1 - attack_len * sin(attack_ang); - x5 = x2 - attack_len * sin(attack_ang); - x6 = x1 - z1 * sina; - x7 = x4 - z1 * sina; + x1 = w/2; + x2 = x1 - chamf * sina; + x3 = x1 - chamf; + x4 = x1 - attack_len * sin(attack_ang); + x5 = x2 - attack_len * sin(attack_ang); + x6 = x1 - z1 * sina; + x7 = x4 - z1 * sina; - y1 = l/2; - y2 = y1 - attack_len * cos(attack_ang); + y1 = l/2; + y2 = y1 - attack_len * cos(attack_ang); - attachable(anchor,spin,orient, size=[w, l, h]) { - polyhedron( - convexity=4, - points=[ - [-x5, -y1, z3], - [ x5, -y1, z3], - [ x7, -y1, z4], - [ x4, -y1, -z1-0.05], - [-x4, -y1, -z1-0.05], - [-x7, -y1, z4], + attachable(anchor,spin,orient, size=[w, l, h]) { + polyhedron( + convexity=4, + points=[ + [-x5, -y1, z3], + [ x5, -y1, z3], + [ x7, -y1, z4], + [ x4, -y1, -z1-0.05], + [-x4, -y1, -z1-0.05], + [-x7, -y1, z4], - [-x3, -y2, z1], - [ x3, -y2, z1], - [ x2, -y2, z2], - [ x6, -y2, z4], - [ x1, -y2, -z1-0.05], - [-x1, -y2, -z1-0.05], - [-x6, -y2, z4], - [-x2, -y2, z2], + [-x3, -y2, z1], + [ x3, -y2, z1], + [ x2, -y2, z2], + [ x6, -y2, z4], + [ x1, -y2, -z1-0.05], + [-x1, -y2, -z1-0.05], + [-x6, -y2, z4], + [-x2, -y2, z2], - [ x5, y1, z3], - [-x5, y1, z3], - [-x7, y1, z4], - [-x4, y1, -z1-0.05], - [ x4, y1, -z1-0.05], - [ x7, y1, z4], + [ x5, y1, z3], + [-x5, y1, z3], + [-x7, y1, z4], + [-x4, y1, -z1-0.05], + [ x4, y1, -z1-0.05], + [ x7, y1, z4], - [ x3, y2, z1], - [-x3, y2, z1], - [-x2, y2, z2], - [-x6, y2, z4], - [-x1, y2, -z1-0.05], - [ x1, y2, -z1-0.05], - [ x6, y2, z4], - [ x2, y2, z2], - ], - faces=[ - [0, 1, 2], - [0, 2, 5], - [2, 3, 4], - [2, 4, 5], + [ x3, y2, z1], + [-x3, y2, z1], + [-x2, y2, z2], + [-x6, y2, z4], + [-x1, y2, -z1-0.05], + [ x1, y2, -z1-0.05], + [ x6, y2, z4], + [ x2, y2, z2], + ], + faces=[ + [0, 1, 2], + [0, 2, 5], + [2, 3, 4], + [2, 4, 5], - [0, 13, 6], - [0, 6, 7], - [0, 7, 1], - [1, 7, 8], - [1, 8, 9], - [1, 9, 2], - [2, 9, 10], - [2, 10, 3], - [3, 10, 11], - [3, 11, 4], - [4, 11, 12], - [4, 12, 5], - [5, 12, 13], - [5, 13, 0], + [0, 13, 6], + [0, 6, 7], + [0, 7, 1], + [1, 7, 8], + [1, 8, 9], + [1, 9, 2], + [2, 9, 10], + [2, 10, 3], + [3, 10, 11], + [3, 11, 4], + [4, 11, 12], + [4, 12, 5], + [5, 12, 13], + [5, 13, 0], - [14, 15, 16], - [14, 16, 19], - [16, 17, 18], - [16, 18, 19], + [14, 15, 16], + [14, 16, 19], + [16, 17, 18], + [16, 18, 19], - [14, 27, 20], - [14, 20, 21], - [14, 21, 15], - [15, 21, 22], - [15, 22, 23], - [15, 23, 16], - [16, 23, 24], - [16, 24, 17], - [17, 24, 25], - [17, 25, 18], - [18, 25, 26], - [18, 26, 19], - [19, 26, 27], - [19, 27, 14], + [14, 27, 20], + [14, 20, 21], + [14, 21, 15], + [15, 21, 22], + [15, 22, 23], + [15, 23, 16], + [16, 23, 24], + [16, 24, 17], + [17, 24, 25], + [17, 25, 18], + [18, 25, 26], + [18, 26, 19], + [19, 26, 27], + [19, 27, 14], - [6, 21, 20], - [6, 20, 7], - [7, 20, 27], - [7, 27, 8], - [8, 27, 26], - [8, 26, 9], - [9, 26, 25], - [9, 25, 10], - [10, 25, 24], - [10, 24, 11], - [11, 24, 23], - [11, 23, 12], - [12, 23, 22], - [12, 22, 13], - [13, 22, 21], - [13, 21, 6], - ] - ); - children(); - } + [6, 21, 20], + [6, 20, 7], + [7, 20, 27], + [7, 27, 8], + [8, 27, 26], + [8, 26, 9], + [9, 26, 25], + [9, 25, 10], + [10, 25, 24], + [10, 24, 11], + [11, 24, 23], + [11, 23, 12], + [12, 23, 22], + [12, 22, 13], + [13, 22, 21], + [13, 21, 6], + ] + ); + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/stacks.scad b/stacks.scad index acf945e..4a452c0 100644 --- a/stacks.scad +++ b/stacks.scad @@ -41,8 +41,8 @@ function stack_init() = []; // stack2 = stack_push(stack, "foo"); // is_empty2 = stack_empty(stack2); // Returns: false function stack_empty(stack) = - assert(is_list(stack)) - len(stack)==0; + assert(is_list(stack)) + len(stack)==0; // Function: stack_depth() @@ -60,8 +60,8 @@ function stack_empty(stack) = // stack3 = stack_push(stack2, ["bar","baz","qux"]); // depth3 = stack_depth(stack3); // Returns: 4 function stack_depth(stack) = - assert(is_list(stack)) - len(stack); + assert(is_list(stack)) + len(stack); // Function: stack_top() @@ -78,16 +78,16 @@ function stack_depth(stack) = // item = stack_top(stack); // Returns: 7 // list = stack_top(stack,n=3); // Returns: [5,6,7] function stack_top(stack,n=undef) = - assert(is_list(stack)) - is_undef(n)? ( - stack[len(stack)-1] - ) : ( - let(stacksize = len(stack)) - assert(is_num(n)) - assert(n>=0) - assert(stacksize>=n, "stack underflow") - [for (i=[0:1:n-1]) stack[stacksize-n+i]] - ); + assert(is_list(stack)) + is_undef(n)? ( + stack[len(stack)-1] + ) : ( + let(stacksize = len(stack)) + assert(is_num(n)) + assert(n>=0) + assert(stacksize>=n, "stack underflow") + [for (i=[0:1:n-1]) stack[stacksize-n+i]] + ); // Function: stack_peek() @@ -107,19 +107,19 @@ function stack_top(stack,n=undef) = // item2 = stack_peek(stack, 3); // Returns: 7 // list = stack_peek(stack, 6, 4); // Returns: [4,5,6,7] function stack_peek(stack,depth=0,n=undef) = - assert(is_list(stack)) - assert(is_num(depth)) - assert(depth>=0) - let(stacksize = len(stack)) - assert(stacksize>=depth, "stack underflow") - is_undef(n)? ( - stack[stacksize-depth-1] - ) : ( - assert(is_num(n)) - assert(n>=0) - assert(n<=depth+1) - [for (i=[0:1:n-1]) stack[stacksize-1-depth+i]] - ); + assert(is_list(stack)) + assert(is_num(depth)) + assert(depth>=0) + let(stacksize = len(stack)) + assert(stacksize>=depth, "stack underflow") + is_undef(n)? ( + stack[stacksize-depth-1] + ) : ( + assert(is_num(n)) + assert(n>=0) + assert(n<=depth+1) + [for (i=[0:1:n-1]) stack[stacksize-1-depth+i]] + ); // Function: stack_push() @@ -137,8 +137,8 @@ function stack_peek(stack,depth=0,n=undef) = // stack4 = stack_push(stack,[[5,8]]); // Returns: [4,9,2,3,[5,8]] // stack5 = stack_push(stack,[[5,8],6,7]); // Returns: [4,9,2,3,[5,8],6,7] function stack_push(stack,items) = - assert(is_list(stack)) - is_list(items)? concat(stack, items) : concat(stack, [items]); + assert(is_list(stack)) + is_list(items)? concat(stack, items) : concat(stack, [items]); // Function: stack_pop() @@ -154,11 +154,11 @@ function stack_push(stack,items) = // stack2 = stack_pop(stack); // Returns: [4,5,6,7,8] // stack3 = stack_pop(stack2,n=3); // Returns: [4,5] function stack_pop(stack,n=1) = - assert(is_list(stack)) - assert(is_num(n)) - assert(n>=0) - assert(len(stack)>=n, "stack underflow") - [for (i = [0:1:len(stack)-1-n]) stack[i]]; + assert(is_list(stack)) + assert(is_num(n)) + assert(n>=0) + assert(len(stack)>=n, "stack underflow") + [for (i = [0:1:len(stack)-1-n]) stack[i]]; // Function: stack_rotate() @@ -176,18 +176,18 @@ function stack_pop(stack,n=1) = // stack2 = stack_rotate(stack,3); // Returns: [4,5,7,8,6] // stack3 = stack_rotate(stack2,-4); // Returns: [4,6,5,7,8] function stack_rotate(stack,n=3) = - assert(is_list(stack)) - let(stacksize = len(stack)) - assert(stacksize>=n, "stack underflow") - n>=0? concat( - [for (i=[0:1:stacksize-1-n]) stack[i]], - [for (i=[0:1:n-2]) stack[stacksize-n+i+1]], - [stack[stacksize-n]] - ) : concat( - [for (i=[0:1:stacksize-1+n]) stack[i]], - [stack[stacksize-1]], - [for (i=[0:1:-n-2]) stack[stacksize+n+i]] - ); + assert(is_list(stack)) + let(stacksize = len(stack)) + assert(stacksize>=n, "stack underflow") + n>=0? concat( + [for (i=[0:1:stacksize-1-n]) stack[i]], + [for (i=[0:1:n-2]) stack[stacksize-n+i+1]], + [stack[stacksize-n]] + ) : concat( + [for (i=[0:1:stacksize-1+n]) stack[i]], + [stack[stacksize-1]], + [for (i=[0:1:-n-2]) stack[stacksize+n+i]] + ); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/std.scad b/std.scad index ce46f5c..8375fcc 100644 --- a/std.scad +++ b/std.scad @@ -39,5 +39,5 @@ include include -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/strings.scad b/strings.scad index 94cafeb..7565e17 100644 --- a/strings.scad +++ b/strings.scad @@ -27,13 +27,13 @@ // substr("abcdefg",[2,4]); // Returns "cde" // substr("abcdefg",len=-2); // Returns "" function substr(str, pos=0, len=undef) = - is_list(pos) ? _substr(str, pos[0], pos[1]-pos[0]+1) : - len == undef ? _substr(str, pos, len(str)-pos) : - _substr(str,pos,len); + is_list(pos) ? _substr(str, pos[0], pos[1]-pos[0]+1) : + len == undef ? _substr(str, pos, len(str)-pos) : + _substr(str,pos,len); function _substr(str,pos,len,substr="") = - len <= 0 || pos>=len(str) ? substr : - _substr(str, pos+1, len-1, str(substr, str[pos])); + len <= 0 || pos>=len(str) ? substr : + _substr(str, pos+1, len-1, str(substr, str[pos])); // Function suffix() @@ -57,8 +57,8 @@ function suffix(str,len) = substr(str, len(str)-len,len); // str_join(["abc","def","ghi"]); // Returns "abcdefghi" // str_join(["abc","def","ghi"], " + "); // Returns "abc + def + ghi" function str_join(list,sep="",_i=0, _result="") = - _i >= len(list)-1 ? (_i==len(list) ? _result : str(_result,list[_i])) : - str_join(list,sep,_i+1,str(_result,list[_i],sep)); + _i >= len(list)-1 ? (_i==len(list) ? _result : str(_result,list[_i])) : + str_join(list,sep,_i+1,str(_result,list[_i],sep)); // Function: downcase() @@ -72,7 +72,7 @@ function str_join(list,sep="",_i=0, _result="") = // Example: // downcase("ABCdef"); // Returns "abcdef" function downcase(str) = - str_join([for(char=str) let(code=ord(char)) code>=65 && code<=90 ? chr(code+32) : char]); + str_join([for(char=str) let(code=ord(char)) code>=65 && code<=90 ? chr(code+32) : char]); // Function: upcase() @@ -86,7 +86,7 @@ function downcase(str) = // Example: // upcase("ABCdef"); // Returns "ABCDEF" function upcase(str) = - str_join([for(char=str) let(code=ord(char)) code>=97 && code<=122 ? chr(code-32) : char]); + str_join([for(char=str) let(code=ord(char)) code>=97 && code<=122 ? chr(code-32) : char]); // Function: str_int() @@ -110,19 +110,19 @@ function upcase(str) = // str_int("CEDE", 16); // Returns 52958 // str_int(""); // Returns 0 function str_int(str,base=10) = - str==undef ? undef : - len(str)==0 ? 0 : - let(str=downcase(str)) - str[0] == "-" ? -_str_int_recurse(substr(str,1),base,len(str)-2) : - str[0] == "+" ? _str_int_recurse(substr(str,1),base,len(str)-2) : - _str_int_recurse(str,base,len(str)-1); + str==undef ? undef : + len(str)==0 ? 0 : + let(str=downcase(str)) + str[0] == "-" ? -_str_int_recurse(substr(str,1),base,len(str)-2) : + str[0] == "+" ? _str_int_recurse(substr(str,1),base,len(str)-2) : + _str_int_recurse(str,base,len(str)-1); function _str_int_recurse(str,base,i) = - let( - digit = search(str[i],"0123456789abcdef"), - last_digit = digit == [] || digit[0] >= base ? (0/0) : digit[0] - ) i==0 ? last_digit : - _str_int_recurse(str,base,i-1)*base + last_digit; + let( + digit = search(str[i],"0123456789abcdef"), + last_digit = digit == [] || digit[0] >= base ? (0/0) : digit[0] + ) i==0 ? last_digit : + _str_int_recurse(str,base,i-1)*base + last_digit; // Function: str_float() @@ -142,15 +142,15 @@ function _str_int_recurse(str,base,i) = // str_float("7.342e-4"); // Returns 0.0007342 // str_float(""); // Returns 0 function str_float(str) = - str==undef ? undef : - len(str) == 0 ? 0 : - in_list(str[1], ["+","-"]) ? (0/0) : // Don't allow --3, or +-3 - str[0]=="-" ? -str_float(substr(str,1)) : - str[0]=="+" ? str_float(substr(str,1)) : - let(esplit = str_split(str,"eE") ) - len(esplit)==2 ? str_float(esplit[0]) * pow(10,str_int(esplit[1])) : - let( dsplit = str_split(str,["."])) - str_int(dsplit[0])+str_int(dsplit[1])/pow(10,len(dsplit[1])); + str==undef ? undef : + len(str) == 0 ? 0 : + in_list(str[1], ["+","-"]) ? (0/0) : // Don't allow --3, or +-3 + str[0]=="-" ? -str_float(substr(str,1)) : + str[0]=="+" ? str_float(substr(str,1)) : + let(esplit = str_split(str,"eE") ) + len(esplit)==2 ? str_float(esplit[0]) * pow(10,str_int(esplit[1])) : + let( dsplit = str_split(str,["."])) + str_int(dsplit[0])+str_int(dsplit[1])/pow(10,len(dsplit[1])); // Function: str_frac() @@ -186,24 +186,24 @@ function str_float(str) = // str_frac("-2 12/4",mixed=false); // Returns nan // str_frac("2 1/4",mixed=false); // Returns nan function str_frac(str,mixed=true,improper=true,signed=true) = - str == undef ? undef : - len(str)==0 ? 0 : - signed && str[0]=="-" ? -str_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : - signed && str[0]=="+" ? str_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : - mixed ? ( - str_find(str," ")>0 || is_undef(str_find(str,"/"))? ( - let(whole = str_split(str,[" "])) - _str_int_recurse(whole[0],10,len(whole[0])-1) + str_frac(whole[1], mixed=false, improper=improper, signed=false) - ) : str_frac(str,mixed=false, improper=improper) - ) : ( - let(split = str_split(str,"/")) - len(split)!=2 ? (0/0) : - let( - numerator = _str_int_recurse(split[0],10,len(split[0])-1), - denominator = _str_int_recurse(split[1],10,len(split[1])-1) - ) !improper && numerator>=denominator? (0/0) : - denominator<0 ? (0/0) : numerator/denominator - ); + str == undef ? undef : + len(str)==0 ? 0 : + signed && str[0]=="-" ? -str_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : + signed && str[0]=="+" ? str_frac(substr(str,1),mixed=mixed,improper=improper,signed=false) : + mixed ? ( + str_find(str," ")>0 || is_undef(str_find(str,"/"))? ( + let(whole = str_split(str,[" "])) + _str_int_recurse(whole[0],10,len(whole[0])-1) + str_frac(whole[1], mixed=false, improper=improper, signed=false) + ) : str_frac(str,mixed=false, improper=improper) + ) : ( + let(split = str_split(str,"/")) + len(split)!=2 ? (0/0) : + let( + numerator = _str_int_recurse(split[0],10,len(split[0])-1), + denominator = _str_int_recurse(split[1],10,len(split[1])-1) + ) !improper && numerator>=denominator? (0/0) : + denominator<0 ? (0/0) : numerator/denominator + ); // Function: str_num() @@ -216,10 +216,10 @@ function str_frac(str,mixed=true,improper=true,signed=true) = // str_num("3/4"); // Returns 0.75 // str_num("3.4e-2"); // Returns 0.034 function str_num(str) = - str == undef ? undef : - let( val = str_frac(str) ) - val == val ? val : - str_float(str); + str == undef ? undef : + let( val = str_frac(str) ) + val == val ? val : + str_float(str); // Function: str_split() @@ -247,25 +247,25 @@ function str_num(str) = // str_split("abc+def-qrs*iop",["+","-","*"]); // Returns ["abc", "def", "qrs", "iop"] // str_split("abc+def-qrs*iop",["-","+","*"]); // Returns ["abc+def", "qrs*iop", "", ""] function str_split(str,sep,keep_nulls=true) = - !keep_nulls ? _remove_empty_strs(str_split(str,sep,keep_nulls=true)) : - is_list(sep) ? _str_split_recurse(str,sep,i=0,result=[]) : - let( cutpts = concat([-1],sort(flatten(search(sep, str,0))),[len(str)])) - [for(i=[0:len(cutpts)-2]) substr(str,cutpts[i]+1,cutpts[i+1]-cutpts[i]-1)]; + !keep_nulls ? _remove_empty_strs(str_split(str,sep,keep_nulls=true)) : + is_list(sep) ? _str_split_recurse(str,sep,i=0,result=[]) : + let( cutpts = concat([-1],sort(flatten(search(sep, str,0))),[len(str)])) + [for(i=[0:len(cutpts)-2]) substr(str,cutpts[i]+1,cutpts[i+1]-cutpts[i]-1)]; function _str_split_recurse(str,sep,i,result) = - i == len(sep) ? concat(result,[str]) : - let( - pos = search(sep[i], str), - end = pos==[] ? len(str) : pos[0] - ) - _str_split_recurse( - substr(str,end+1), - sep, i+1, - concat(result, [substr(str,0,end)]) - ); + i == len(sep) ? concat(result,[str]) : + let( + pos = search(sep[i], str), + end = pos==[] ? len(str) : pos[0] + ) + _str_split_recurse( + substr(str,end+1), + sep, i+1, + concat(result, [substr(str,0,end)]) + ); function _remove_empty_strs(list) = - list_remove(list, search([""], list,0)[0]); + list_remove(list, search([""], list,0)[0]); // _str_cmp(str,sindex,pattern) @@ -276,11 +276,11 @@ function _remove_empty_strs(list) = // cuts run time in half when the string is long. Two other string // comparison methods were slower. function _str_cmp(str,sindex,pattern) = - len(str)-sindex =0 && !_str_cmp(str,sindex, pattern)? - _str_find_last(str,pattern,sindex-1) : - (sindex >=0 ? sindex : undef); + sindex>=0 && !_str_cmp(str,sindex, pattern)? + _str_find_last(str,pattern,sindex-1) : + (sindex >=0 ? sindex : undef); function _str_find_all(str,pattern) = - pattern == "" ? list_range(len(str)) : - [for(i=[0:1:len(str)-len(pattern)]) if (_str_cmp(str,i,pattern)) i]; + pattern == "" ? list_range(len(str)) : + [for(i=[0:1:len(str)-len(pattern)]) if (_str_cmp(str,i,pattern)) i]; // Function: starts_with() @@ -369,12 +369,12 @@ function ends_with(str,pattern) = _str_cmp(str,len(str)-len(pattern),pattern); function _str_count_leading(s,c,_i=0) = - (_i>=len(s)||!in_list(s[_i],[each c]))? _i : - _str_count_leading(s,c,_i=_i+1); + (_i>=len(s)||!in_list(s[_i],[each c]))? _i : + _str_count_leading(s,c,_i=_i+1); function _str_count_trailing(s,c,_i=0) = - (_i>=len(s)||!in_list(s[len(s)-1-_i],[each c]))? _i : - _str_count_trailing(s,c,_i=_i+1); + (_i>=len(s)||!in_list(s[len(s)-1-_i],[each c]))? _i : + _str_count_trailing(s,c,_i=_i+1); // Function: str_strip_leading() @@ -435,15 +435,15 @@ function str_strip(s,c) = str_strip_trailing(str_strip_leading(s,c),c); // fmt_int(123456789012345); // Returns "123456789012345" // fmt_int(-123456789012345); // Returns "-123456789012345" function fmt_int(i,mindigits=1) = - i<0? str("-", fmt_int(-i)) : - let(i=floor(i), e=floor(log(i))) - i==0? "0" : - str_join( - concat( - [for (j=[0:1:mindigits-e-2]) "0"], - [for (j=[e:-1:0]) str(floor(i/pow(10,j)%10))] - ) - ); + i<0? str("-", fmt_int(-i)) : + let(i=floor(i), e=floor(log(i))) + i==0? "0" : + str_join( + concat( + [for (j=[0:1:mindigits-e-2]) "0"], + [for (j=[e:-1:0]) str(floor(i/pow(10,j)%10))] + ) + ); // Function: fmt_fixed() @@ -455,19 +455,19 @@ function fmt_int(i,mindigits=1) = // f = The floating point number to format. // digits = The number of digits after the decimal to show. Default: 6 function fmt_fixed(f,digits=6) = - assert(is_int(digits)) - assert(digits>0) - is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_fixed(g,digits=digits)]),"]") : - str(f)=="nan"? "nan" : - str(f)=="inf"? "inf" : - f<0? str("-",fmt_fixed(-f,digits=digits)) : - assert(is_num(f)) - let( - sc = pow(10,digits), - scaled = floor(f * sc + 0.5), - whole = floor(scaled/sc), - part = floor(scaled-(whole*sc)) - ) str(fmt_int(whole),".",fmt_int(part,digits)); + assert(is_int(digits)) + assert(digits>0) + is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_fixed(g,digits=digits)]),"]") : + str(f)=="nan"? "nan" : + str(f)=="inf"? "inf" : + f<0? str("-",fmt_fixed(-f,digits=digits)) : + assert(is_num(f)) + let( + sc = pow(10,digits), + scaled = floor(f * sc + 0.5), + whole = floor(scaled/sc), + part = floor(scaled-(whole*sc)) + ) str(fmt_int(whole),".",fmt_int(part,digits)); // Function: fmt_float() @@ -485,34 +485,34 @@ function fmt_fixed(f,digits=6) = // fmt_float(PI,12); // Returns: "3.14159265359" // fmt_float([PI,-16.75],12); // Returns: "[3.14159265359, -16.75]" function fmt_float(f,sig=12) = - assert(is_int(sig)) - assert(sig>0) - is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_float(g,sig=sig)]),"]") : - f==0? "0" : - str(f)=="nan"? "nan" : - str(f)=="inf"? "inf" : - f<0? str("-",fmt_float(-f,sig=sig)) : - assert(is_num(f)) - let( - e = floor(log(f)), - mv = sig - e - 1 - ) mv == 0? fmt_int(floor(f + 0.5)) : - (e<-sig/2||mv<0)? str(fmt_float(f*pow(10,-e),sig=sig),"e",e) : - let( - ff = f + pow(10,-mv)*0.5, - whole = floor(ff), - part = floor((ff-whole) * pow(10,mv)) - ) - str_join([ - str(whole), - str_strip_trailing( - str_join([ - ".", - fmt_int(part, mindigits=mv) - ]), - "0." - ) - ]); + assert(is_int(sig)) + assert(sig>0) + is_list(f)? str("[",str_join(sep=", ", [for (g=f) fmt_float(g,sig=sig)]),"]") : + f==0? "0" : + str(f)=="nan"? "nan" : + str(f)=="inf"? "inf" : + f<0? str("-",fmt_float(-f,sig=sig)) : + assert(is_num(f)) + let( + e = floor(log(f)), + mv = sig - e - 1 + ) mv == 0? fmt_int(floor(f + 0.5)) : + (e<-sig/2||mv<0)? str(fmt_float(f*pow(10,-e),sig=sig),"e",e) : + let( + ff = f + pow(10,-mv)*0.5, + whole = floor(ff), + part = floor((ff-whole) * pow(10,mv)) + ) + str_join([ + str(whole), + str_strip_trailing( + str_join([ + ".", + fmt_int(part, mindigits=mv) + ]), + "0." + ) + ]); // Function: escape_html() @@ -521,14 +521,14 @@ function fmt_float(f,sig=12) = // Description: // Converts "<", ">", "&", and double-quote chars to their entity encoding so that echoing the strong will show it verbatim. function escape_html(s) = - str_join([ - for (c=s) - c=="<"? "<" : - c==">"? ">" : - c=="&"? "&" : - c=="\""? """ : - c - ]); + str_join([ + for (c=s) + c=="<"? "<" : + c==">"? ">" : + c=="&"? "&" : + c=="\""? """ : + c + ]); // Function: is_lower() @@ -537,10 +537,10 @@ function escape_html(s) = // Description: // Returns true if all the characters in the given string are lowercase letters. (a-z) function is_lower(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_lower(v)]) : - let(v = ord(s[0])) (v>=ord("a") && v<=ord("z")); + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_lower(v)]) : + let(v = ord(s[0])) (v>=ord("a") && v<=ord("z")); // Function: is_upper() @@ -549,10 +549,10 @@ function is_lower(s) = // Description: // Returns true if all the characters in the given string are uppercase letters. (A-Z) function is_upper(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_upper(v)]) : - let(v = ord(s[0])) (v>=ord("A") && v<=ord("Z")); + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_upper(v)]) : + let(v = ord(s[0])) (v>=ord("A") && v<=ord("Z")); // Function: is_digit() @@ -561,10 +561,10 @@ function is_upper(s) = // Description: // Returns true if all the characters in the given string are digits. (0-9) function is_digit(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_digit(v)]) : - let(v = ord(s[0])) (v>=ord("0") && v<=ord("9")); + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_digit(v)]) : + let(v = ord(s[0])) (v>=ord("0") && v<=ord("9")); // Function: is_hexdigit() @@ -573,13 +573,13 @@ function is_digit(s) = // Description: // Returns true if all the characters in the given string are valid hexadecimal digits. (0-9 or a-f or A-F)) function is_hexdigit(s) = - assert(is_string(s)) - s==""? false : - len(s)>1? all([for (v=s) is_hexdigit(v)]) : - let(v = ord(s[0])) - (v>=ord("0") && v<=ord("9")) || - (v>=ord("A") && v<=ord("F")) || - (v>=ord("a") && v<=ord("f")); + assert(is_string(s)) + s==""? false : + len(s)>1? all([for (v=s) is_hexdigit(v)]) : + let(v = ord(s[0])) + (v>=ord("0") && v<=ord("9")) || + (v>=ord("A") && v<=ord("F")) || + (v>=ord("a") && v<=ord("f")); // Function: is_letter() @@ -588,9 +588,9 @@ function is_hexdigit(s) = // Description: // Returns true if all the characters in the given string are standard ASCII letters. (A-Z or a-z) function is_letter(s) = - assert(is_string(s)) - s==""? false : - all([for (v=s) is_lower(v) || is_upper(v)]); + assert(is_string(s)) + s==""? false : + all([for (v=s) is_lower(v) || is_upper(v)]); // Function: str_format() @@ -630,54 +630,54 @@ function is_letter(s) = // str_format("{:-10s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostamus27.440" // str_format("{:-10.9s}{:.3f}", ["plecostamus",27.43982]); // Returns: "plecostam 27.440" function str_format(fmt, vals, use_nbsp=false) = - let( - parts = str_split(fmt,"{") - ) str_join([ - for(i = idx(parts)) - let( - found_brace = i==0 || [for (c=parts[i]) if(c=="}") c] != [], - err = assert(found_brace, "Unbalanced { in format string."), - p = i==0? [undef,parts[i]] : str_split(parts[i],"}"), - fmta = p[0], - raw = p[1] - ) each [ - assert(i<99) - is_undef(fmta)? "" : let( - fmtb = str_split(fmta,":"), - num = is_digit(fmtb[0])? str_int(fmtb[0]) : (i-1), - left = fmtb[1][0] == "-", - fmtb1 = default(fmtb[1],""), - fmtc = left? substr(fmtb1,1) : fmtb1, - zero = fmtc[0] == "0", - lch = fmtc==""? "" : fmtc[len(fmtc)-1], - hastyp = is_letter(lch), - typ = hastyp? lch : "s", - fmtd = hastyp? substr(fmtc,0,len(fmtc)-1) : fmtc, - fmte = str_split((zero? substr(fmtd,1) : fmtd), "."), - wid = str_int(fmte[0]), - prec = str_int(fmte[1]), - val = assert(num>=0&&num=0&&num testpoints_on_sphere = [ for(p = - [ - [1,PHI,0], [-1,PHI,0], [1,-PHI,0], [-1,-PHI,0], - [0,1,PHI], [0,-1,PHI], [0,1,-PHI], [0,-1,-PHI], - [PHI,0,1], [-PHI,0,1], [PHI,0,-1], [-PHI,0,-1] - ]) - unit(p) + [ + [1,PHI,0], [-1,PHI,0], [1,-PHI,0], [-1,-PHI,0], + [0,1,PHI], [0,-1,PHI], [0,1,-PHI], [0,-1,-PHI], + [PHI,0,1], [-PHI,0,1], [PHI,0,-1], [-PHI,0,-1] + ]) + unit(p) ]; testpoints_circular = [ for(a = [0:15:360-EPSILON]) [cos(a),sin(a)] ]; @@ -44,30 +44,30 @@ visualize_hull(testpoints3d); module visualize_hull(points) { - hull = hull(points); - - %if (len(hull) > 0 && is_list(hull[0]) && len(hull[0]) > 0) - polyhedron(points=points, faces = hull); - else - polyhedron(points=points, faces = [hull]); - - for (i = [0:len(points)-1]) { - p = points[i]; - $fn = 16; - translate(p) { - if (hull_contains_index(hull,i)) { - color("blue") sphere(1); - } else { - color("red") sphere(1); - } - } - } - - function hull_contains_index(hull, index) = - search(index,hull,1,0) || - search(index,hull,1,1) || - search(index,hull,1,2); + hull = hull(points); + + %if (len(hull) > 0 && is_list(hull[0]) && len(hull[0]) > 0) + polyhedron(points=points, faces = hull); + else + polyhedron(points=points, faces = [hull]); + + for (i = [0:len(points)-1]) { + p = points[i]; + $fn = 16; + translate(p) { + if (hull_contains_index(hull,i)) { + color("blue") sphere(1); + } else { + color("red") sphere(1); + } + } + } + + function hull_contains_index(hull, index) = + search(index,hull,1,0) || + search(index,hull,1,1) || + search(index,hull,1,2); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_arrays.scad b/tests/test_arrays.scad index b066049..71fa750 100644 --- a/tests/test_arrays.scad +++ b/tests/test_arrays.scad @@ -4,259 +4,259 @@ include // 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]]); + 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)); + 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]); + 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]); + 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(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(e=4, n=3) == [0,2,4]); - assert(list_range(n=4, s=[3,4], step=[2,3]) == [[3,4], [5,7], [7,10], [9,13]]); + 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(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(e=4, n=3) == [0,2,4]); + 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]); + assert(reverse([3,4,5,6]) == [6,5,4,3]); } test_reverse(); module test_list_rotate() { - assert(list_rotate([1,2,3,4,5],-2) == [4,5,1,2,3]); - assert(list_rotate([1,2,3,4,5],-1) == [5,1,2,3,4]); - assert(list_rotate([1,2,3,4,5],0) == [1,2,3,4,5]); - assert(list_rotate([1,2,3,4,5],1) == [2,3,4,5,1]); - assert(list_rotate([1,2,3,4,5],2) == [3,4,5,1,2]); - assert(list_rotate([1,2,3,4,5],3) == [4,5,1,2,3]); - assert(list_rotate([1,2,3,4,5],4) == [5,1,2,3,4]); - assert(list_rotate([1,2,3,4,5],5) == [1,2,3,4,5]); - assert(list_rotate([1,2,3,4,5],6) == [2,3,4,5,1]); - assert(list_rotate([],3) == []); + assert(list_rotate([1,2,3,4,5],-2) == [4,5,1,2,3]); + assert(list_rotate([1,2,3,4,5],-1) == [5,1,2,3,4]); + assert(list_rotate([1,2,3,4,5],0) == [1,2,3,4,5]); + assert(list_rotate([1,2,3,4,5],1) == [2,3,4,5,1]); + assert(list_rotate([1,2,3,4,5],2) == [3,4,5,1,2]); + assert(list_rotate([1,2,3,4,5],3) == [4,5,1,2,3]); + assert(list_rotate([1,2,3,4,5],4) == [5,1,2,3,4]); + assert(list_rotate([1,2,3,4,5],5) == [1,2,3,4,5]); + assert(list_rotate([1,2,3,4,5],6) == [2,3,4,5,1]); + assert(list_rotate([],3) == []); } test_list_rotate(); module test_deduplicate() { - assert(deduplicate([8,3,4,4,4,8,2,3,3,8,8]) == [8,3,4,8,2,3,8]); - 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([8,3,4,4,4,8,2,3,3,8,8]) == [8,3,4,8,2,3,8]); + 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]]); } test_deduplicate(); module test_deduplicate_indexed() { - assert(deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1]) == [1,4,1,2,0,1]); - assert(deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1], closed=true) == [1,4,1,2,0]); + assert(deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1]) == [1,4,1,2,0,1]); + assert(deduplicate_indexed([8,6,4,6,3], [1,4,3,1,2,2,0,1], closed=true) == [1,4,1,2,0]); } test_deduplicate_indexed(); module test_list_set() { - assert(list_set([2,3,4,5], 2, 21) == [2,3,21,5]); - assert(list_set([2,3,4,5], [1,3], [81,47]) == [2,81,4,47]); + assert(list_set([2,3,4,5], 2, 21) == [2,3,21,5]); + assert(list_set([2,3,4,5], [1,3], [81,47]) == [2,81,4,47]); } test_list_set(); module test_list_remove() { - assert(list_remove([3,6,9,12],1) == [3,9,12]); - assert(list_remove([3,6,9,12],[1,3]) == [3,9]); + assert(list_remove([3,6,9,12],1) == [3,9,12]); + assert(list_remove([3,6,9,12],[1,3]) == [3,9]); } test_list_remove(); module test_list_remove_values() { - animals = ["bat", "cat", "rat", "dog", "bat", "rat"]; - assert(list_remove_values(animals, "rat") == ["bat","cat","dog","bat","rat"]); - assert(list_remove_values(animals, "bat", all=true) == ["cat","rat","dog","rat"]); - assert(list_remove_values(animals, ["bat","rat"]) == ["cat","dog","bat","rat"]); - assert(list_remove_values(animals, ["bat","rat"], all=true) == ["cat","dog"]); - assert(list_remove_values(animals, ["tucan","rat"], all=true) == ["bat","cat","dog","bat"]); + animals = ["bat", "cat", "rat", "dog", "bat", "rat"]; + assert(list_remove_values(animals, "rat") == ["bat","cat","dog","bat","rat"]); + assert(list_remove_values(animals, "bat", all=true) == ["cat","rat","dog","rat"]); + assert(list_remove_values(animals, ["bat","rat"]) == ["cat","dog","bat","rat"]); + assert(list_remove_values(animals, ["bat","rat"], all=true) == ["cat","dog"]); + assert(list_remove_values(animals, ["tucan","rat"], all=true) == ["bat","cat","dog","bat"]); } test_list_remove_values(); module test_list_insert() { - assert(list_insert([3,6,9,12],1,5) == [3,5,6,9,12]); - assert(list_insert([3,6,9,12],[1,3],[5,11]) == [3,5,6,9,11,12]); + assert(list_insert([3,6,9,12],1,5) == [3,5,6,9,12]); + assert(list_insert([3,6,9,12],[1,3],[5,11]) == [3,5,6,9,11,12]); } test_list_insert(); module test_bselect() { - assert(bselect([3,4,5,6,7], [false,false,false,false,false]) == []); - assert(bselect([3,4,5,6,7], [false,true,true,false,true]) == [4,5,7]); - assert(bselect([3,4,5,6,7], [true,true,true,true,true]) == [3,4,5,6,7]); + assert(bselect([3,4,5,6,7], [false,false,false,false,false]) == []); + assert(bselect([3,4,5,6,7], [false,true,true,false,true]) == [4,5,7]); + assert(bselect([3,4,5,6,7], [true,true,true,true,true]) == [3,4,5,6,7]); } test_bselect(); module test_list_bset() { - assert(list_bset([false,true,false,true,false], [3,4]) == [0,3,0,4,0]); - assert(list_bset([false,true,false,true,false], [3,4], dflt=1) == [1,3,1,4,1]); + assert(list_bset([false,true,false,true,false], [3,4]) == [0,3,0,4,0]); + assert(list_bset([false,true,false,true,false], [3,4], dflt=1) == [1,3,1,4,1]); } 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); + 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); + 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); + assert(list_shortest(["foobar", "bazquxx", "abcd"]) == 4); } test_list_shortest(); module test_list_longest() { - assert(list_longest(["foobar", "bazquxx", "abcd"]) == 7); + 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]); + 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]); + 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]); + 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_idx() { - colors = ["red", "green", "blue", "cyan"]; - assert([for (i=idx(colors)) i] == [0,1,2,3]); - assert([for (i=idx(colors,end=-2)) i] == [0,1,2]); - assert([for (i=idx(colors,start=1)) i] == [1,2,3]); - assert([for (i=idx(colors,start=1,end=-2)) i] == [1,2]); + colors = ["red", "green", "blue", "cyan"]; + assert([for (i=idx(colors)) i] == [0,1,2,3]); + assert([for (i=idx(colors,end=-2)) i] == [0,1,2]); + assert([for (i=idx(colors,start=1)) i] == [1,2,3]); + assert([for (i=idx(colors,start=1,end=-2)) i] == [1,2]); } test_idx(); 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]]); + 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_shuffle() { - nums1 = [for (i=list_range(100)) i]; - nums2 = shuffle(nums1); - nums3 = shuffle(nums2); - assert(len(nums2)==len(nums1)); - assert(len(nums3)==len(nums2)); - assert(nums1!=nums2); - assert(nums2!=nums3); - assert(nums1!=nums3); + nums1 = [for (i=list_range(100)) i]; + nums2 = shuffle(nums1); + nums3 = shuffle(nums2); + assert(len(nums2)==len(nums1)); + assert(len(nums3)==len(nums2)); + assert(nums1!=nums2); + assert(nums2!=nums3); + assert(nums1!=nums3); } test_shuffle(); 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]]]); + 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]); + 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]); + assert(unique([]) == []); + assert(unique([8]) == [8]); + assert(unique([7,3,9,4,3,1,8]) == [1,3,4,7,8,9]); } test_unique(); @@ -265,100 +265,100 @@ test_unique(); 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]); - assert(subindex(v,[2,1]) == [[3, 2], [7, 6], [11, 10], [15, 14]]); - assert(subindex(v,[1:3]) == [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]]); + 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]); + assert(subindex(v,[2,1]) == [[3, 2], [7, 6], [11, 10], [15, 14]]); + assert(subindex(v,[1:3]) == [[2, 3, 4], [6, 7, 8], [10, 11, 12], [14, 15, 16]]); } test_subindex(); module test_pair() { - assert(pair([3,4,5,6]) == [[3,4], [4,5], [5,6]]); - assert(pair("ABCD") == [["A","B"], ["B","C"], ["C","D"]]); + assert(pair([3,4,5,6]) == [[3,4], [4,5], [5,6]]); + assert(pair("ABCD") == [["A","B"], ["B","C"], ["C","D"]]); } test_pair(); module test_pair_wrap() { - assert(pair_wrap([3,4,5,6]) == [[3,4], [4,5], [5,6], [6,3]]); - assert(pair_wrap("ABCD") == [["A","B"], ["B","C"], ["C","D"], ["D","A"]]); + assert(pair_wrap([3,4,5,6]) == [[3,4], [4,5], [5,6], [6,3]]); + assert(pair_wrap("ABCD") == [["A","B"], ["B","C"], ["C","D"], ["D","A"]]); } test_pair_wrap(); module test_triplet() { - assert(triplet([3,4,5,6,7]) == [[3,4,5], [4,5,6], [5,6,7]]); - assert(triplet("ABCDE") == [["A","B","C"], ["B","C","D"], ["C","D","E"]]); + assert(triplet([3,4,5,6,7]) == [[3,4,5], [4,5,6], [5,6,7]]); + assert(triplet("ABCDE") == [["A","B","C"], ["B","C","D"], ["C","D","E"]]); } test_triplet(); module test_triplet_wrap() { - assert(triplet_wrap([3,4,5,6]) == [[3,4,5], [4,5,6], [5,6,3], [6,3,4]]); - assert(triplet_wrap("ABCD") == [["A","B","C"], ["B","C","D"], ["C","D","A"], ["D","A","B"]]); + assert(triplet_wrap([3,4,5,6]) == [[3,4,5], [4,5,6], [5,6,3], [6,3,4]]); + assert(triplet_wrap("ABCD") == [["A","B","C"], ["B","C","D"], ["C","D","A"], ["D","A","B"]]); } test_triplet_wrap(); module test_permute() { - assert(permute([3,4,5,6]) == [[3,4],[3,5],[3,6],[4,5],[4,6],[5,6]]); - assert(permute([3,4,5,6],n=3) == [[3,4,5],[3,4,6],[3,5,6],[4,5,6]]); + assert(permute([3,4,5,6]) == [[3,4],[3,5],[3,6],[4,5],[4,6],[5,6]]); + assert(permute([3,4,5,6],n=3) == [[3,4,5],[3,4,6],[3,5,6],[4,5,6]]); } test_permute(); module test_repeat_entries() { - list = [0,1,2,3]; - assert(repeat_entries(list, 6) == [0,0,1,2,2,3]); - assert(repeat_entries(list, 6, exact=false) == [0,0,1,1,2,2,3,3]); - assert(repeat_entries(list, [1,1,2,1], exact=false) == [0,1,2,2,3]); + list = [0,1,2,3]; + assert(repeat_entries(list, 6) == [0,0,1,2,2,3]); + assert(repeat_entries(list, 6, exact=false) == [0,0,1,1,2,2,3,3]); + assert(repeat_entries(list, [1,1,2,1], exact=false) == [0,1,2,2,3]); } test_repeat_entries(); module test_zip() { - v1 = [1,2,3,4]; - v2 = [5,6,7]; - v3 = [8,9,10,11]; - assert(zip(v1,v3) == [[1,8],[2,9],[3,10],[4,11]]); - assert(zip([v1,v3]) == [[1,8],[2,9],[3,10],[4,11]]); - assert(zip([v1,v2],fit="short") == [[1,5],[2,6],[3,7]]); - assert(zip([v1,v2],fit="long") == [[1,5],[2,6],[3,7],[4,undef]]); - assert(zip([v1,v2],fit="long", fill=0) == [[1,5],[2,6],[3,7],[4,0]]); - assert(zip([v1,v2,v3],fit="long") == [[1,5,8],[2,6,9],[3,7,10],[4,undef,11]]); + v1 = [1,2,3,4]; + v2 = [5,6,7]; + v3 = [8,9,10,11]; + assert(zip(v1,v3) == [[1,8],[2,9],[3,10],[4,11]]); + assert(zip([v1,v3]) == [[1,8],[2,9],[3,10],[4,11]]); + assert(zip([v1,v2],fit="short") == [[1,5],[2,6],[3,7]]); + assert(zip([v1,v2],fit="long") == [[1,5],[2,6],[3,7],[4,undef]]); + assert(zip([v1,v2],fit="long", fill=0) == [[1,5],[2,6],[3,7],[4,0]]); + assert(zip([v1,v2,v3],fit="long") == [[1,5,8],[2,6,9],[3,7,10],[4,undef,11]]); } test_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]]); + 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]]); + 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]); + 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_transpose() { - assert(transpose([[1,2,3],[4,5,6],[7,8,9]]) == [[1,4,7],[2,5,8],[3,6,9]]); - assert(transpose([[1,2,3],[4,5,6]]) == [[1,4],[2,5],[3,6]]); - assert(transpose([3,4,5]) == [3,4,5]); + assert(transpose([[1,2,3],[4,5,6],[7,8,9]]) == [[1,4,7],[2,5,8],[3,6,9]]); + assert(transpose([[1,2,3],[4,5,6]]) == [[1,4],[2,5],[3,6]]); + assert(transpose([3,4,5]) == [3,4,5]); } test_transpose(); @@ -366,4 +366,4 @@ test_transpose(); cube(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_common.scad b/tests/test_common.scad index 4710965..d6756b0 100644 --- a/tests/test_common.scad +++ b/tests/test_common.scad @@ -2,254 +2,254 @@ include module test_typeof() { - assert(typeof(undef) == "undef"); - assert(typeof(true) == "boolean"); - assert(typeof(false) == "boolean"); - assert(typeof(-123) == "number"); - assert(typeof(0) == "number"); - assert(typeof(123) == "number"); - assert(typeof("") == "string"); - assert(typeof("foo") == "string"); - assert(typeof([]) == "list"); - assert(typeof(["foo","bar"]) == "list"); - assert(typeof([123,849,32]) == "list"); - assert(typeof([0:5]) == "range"); - assert(typeof([-3:0]) == "range"); - assert(typeof([0:1:5]) == "range"); - assert(typeof([-3:2:5]) == "range"); - assert(typeof([10:-2:-10]) == "range"); + assert(typeof(undef) == "undef"); + assert(typeof(true) == "boolean"); + assert(typeof(false) == "boolean"); + assert(typeof(-123) == "number"); + assert(typeof(0) == "number"); + assert(typeof(123) == "number"); + assert(typeof("") == "string"); + assert(typeof("foo") == "string"); + assert(typeof([]) == "list"); + assert(typeof(["foo","bar"]) == "list"); + assert(typeof([123,849,32]) == "list"); + assert(typeof([0:5]) == "range"); + assert(typeof([-3:0]) == "range"); + assert(typeof([0:1:5]) == "range"); + assert(typeof([-3:2:5]) == "range"); + assert(typeof([10:-2:-10]) == "range"); } test_typeof(); module test_is_type() { - assert(is_type(undef,"undef")); - assert(is_type(true,"boolean")); - assert(is_type(false,"boolean")); - assert(is_type(-123,"number")); - assert(is_type(0,"number")); - assert(is_type(123,"number")); - assert(is_type("","string")); - assert(is_type("foo","string")); - assert(is_type([],"list")); - assert(is_type([1,2,3],"list")); - assert(is_type(["foo","bar"],"list")); - assert(is_type([0:5],"range")); + assert(is_type(undef,"undef")); + assert(is_type(true,"boolean")); + assert(is_type(false,"boolean")); + assert(is_type(-123,"number")); + assert(is_type(0,"number")); + assert(is_type(123,"number")); + assert(is_type("","string")); + assert(is_type("foo","string")); + assert(is_type([],"list")); + assert(is_type([1,2,3],"list")); + assert(is_type(["foo","bar"],"list")); + assert(is_type([0:5],"range")); - assert(is_type(undef,["undef"])); - assert(is_type(true,["boolean"])); - assert(is_type(false,["boolean"])); - assert(is_type(-123,["number"])); - assert(is_type(0,["number"])); - assert(is_type(123,["number"])); - assert(is_type("",["string"])); - assert(is_type("foo",["string"])); - assert(is_type([],["list"])); - assert(is_type([1,2,3],["list"])); - assert(is_type(["foo","bar"],["list"])); - assert(is_type([0:5],["range"])); + assert(is_type(undef,["undef"])); + assert(is_type(true,["boolean"])); + assert(is_type(false,["boolean"])); + assert(is_type(-123,["number"])); + assert(is_type(0,["number"])); + assert(is_type(123,["number"])); + assert(is_type("",["string"])); + assert(is_type("foo",["string"])); + assert(is_type([],["list"])); + assert(is_type([1,2,3],["list"])); + assert(is_type(["foo","bar"],["list"])); + assert(is_type([0:5],["range"])); - assert(is_type(123,["number","string"])); - assert(is_type("foo",["number","string"])); + assert(is_type(123,["number","string"])); + assert(is_type("foo",["number","string"])); } test_is_type(); module test_is_def() { - assert(!is_def(undef)); - assert(is_def(true)); - assert(is_def(false)); - assert(is_def(-123)); - assert(is_def(0)); - assert(is_def(123)); - assert(is_def("")); - assert(is_def("foo")); - assert(is_def([])); - assert(is_def([3,4,5])); - assert(is_def(["foo","bar","baz"])); - assert(is_def([0:5])); + assert(!is_def(undef)); + assert(is_def(true)); + assert(is_def(false)); + assert(is_def(-123)); + assert(is_def(0)); + assert(is_def(123)); + assert(is_def("")); + assert(is_def("foo")); + assert(is_def([])); + assert(is_def([3,4,5])); + assert(is_def(["foo","bar","baz"])); + assert(is_def([0:5])); } test_is_def(); module test_is_str() { - assert(!is_str(undef)); - assert(!is_str(true)); - assert(!is_str(false)); - assert(!is_str(-123)); - assert(!is_str(0)); - assert(!is_str(123)); - assert(is_str("")); - assert(is_str("foo")); - assert(!is_str([])); - assert(!is_str([3,4,5])); - assert(!is_str(["foo","bar","baz"])); - assert(!is_str([0:5])); + assert(!is_str(undef)); + assert(!is_str(true)); + assert(!is_str(false)); + assert(!is_str(-123)); + assert(!is_str(0)); + assert(!is_str(123)); + assert(is_str("")); + assert(is_str("foo")); + assert(!is_str([])); + assert(!is_str([3,4,5])); + assert(!is_str(["foo","bar","baz"])); + assert(!is_str([0:5])); } test_is_str(); module test_is_int() { - assert(is_int(-999)); - assert(is_int(-1)); - assert(is_int(0)); - assert(is_int(1)); - assert(is_int(999)); - assert(!is_int(-1.1)); - assert(!is_int(1.1)); - assert(!is_int(-0.1)); - assert(!is_int(0.1)); - assert(!is_int(-99.1)); - assert(!is_int(99.1)); - assert(!is_int(undef)); - assert(!is_int(false)); - assert(!is_int(true)); - assert(!is_int("foo")); - assert(!is_int([0,1,2])); - assert(!is_int([0:1:2])); + assert(is_int(-999)); + assert(is_int(-1)); + assert(is_int(0)); + assert(is_int(1)); + assert(is_int(999)); + assert(!is_int(-1.1)); + assert(!is_int(1.1)); + assert(!is_int(-0.1)); + assert(!is_int(0.1)); + assert(!is_int(-99.1)); + assert(!is_int(99.1)); + assert(!is_int(undef)); + assert(!is_int(false)); + assert(!is_int(true)); + assert(!is_int("foo")); + assert(!is_int([0,1,2])); + assert(!is_int([0:1:2])); } test_is_int(); module test_is_integer() { - assert(is_integer(-999)); - assert(is_integer(-1)); - assert(is_integer(0)); - assert(is_integer(1)); - assert(is_integer(999)); - assert(!is_integer(-1.1)); - assert(!is_integer(1.1)); - assert(!is_integer(-0.1)); - assert(!is_integer(0.1)); - assert(!is_integer(-99.1)); - assert(!is_integer(99.1)); - assert(!is_integer(undef)); - assert(!is_integer(false)); - assert(!is_integer(true)); - assert(!is_integer("foo")); - assert(!is_integer([0,1,2])); - assert(!is_integer([0:1:2])); + assert(is_integer(-999)); + assert(is_integer(-1)); + assert(is_integer(0)); + assert(is_integer(1)); + assert(is_integer(999)); + assert(!is_integer(-1.1)); + assert(!is_integer(1.1)); + assert(!is_integer(-0.1)); + assert(!is_integer(0.1)); + assert(!is_integer(-99.1)); + assert(!is_integer(99.1)); + assert(!is_integer(undef)); + assert(!is_integer(false)); + assert(!is_integer(true)); + assert(!is_integer("foo")); + assert(!is_integer([0,1,2])); + assert(!is_integer([0:1:2])); } test_is_integer(); module test_default() { - assert(default(undef,23) == 23); - assert(default(true,23) == true); - assert(default(false,23) == false); - assert(default(-123,23) == -123); - assert(default(0,23) == 0); - assert(default(123,23) == 123); - assert(default("",23) == ""); - assert(default("foo",23) == "foo"); + assert(default(undef,23) == 23); + assert(default(true,23) == true); + assert(default(false,23) == false); + assert(default(-123,23) == -123); + assert(default(0,23) == 0); + assert(default(123,23) == 123); + assert(default("",23) == ""); + assert(default("foo",23) == "foo"); } test_default(); module test_first_defined() { - assert(first_defined([undef,undef,true,false,undef]) == true); - assert(first_defined([undef,undef,false,true,undef]) == false); - assert(first_defined([undef,undef,0,1,undef]) == 0); - assert(first_defined([undef,undef,43,44,undef]) == 43); - assert(first_defined([undef,undef,"foo","bar",undef]) == "foo"); - assert(first_defined([0,1,2,3,4]) == 0); - assert(first_defined([2,3,4]) == 2); - assert(first_defined([[undef,undef],[undef,true],[false,undef]],recursive=true) == [undef, true]); + assert(first_defined([undef,undef,true,false,undef]) == true); + assert(first_defined([undef,undef,false,true,undef]) == false); + assert(first_defined([undef,undef,0,1,undef]) == 0); + assert(first_defined([undef,undef,43,44,undef]) == 43); + assert(first_defined([undef,undef,"foo","bar",undef]) == "foo"); + assert(first_defined([0,1,2,3,4]) == 0); + assert(first_defined([2,3,4]) == 2); + assert(first_defined([[undef,undef],[undef,true],[false,undef]],recursive=true) == [undef, true]); } test_first_defined(); module test_num_defined() { - assert(num_defined([undef,undef,true,false,undef]) == 2); - assert(num_defined([9,undef,true,false,undef]) == 3); - assert(num_defined([undef,9,true,false,undef]) == 3); - assert(num_defined(["foo",9,true,false,undef]) == 4); + assert(num_defined([undef,undef,true,false,undef]) == 2); + assert(num_defined([9,undef,true,false,undef]) == 3); + assert(num_defined([undef,9,true,false,undef]) == 3); + assert(num_defined(["foo",9,true,false,undef]) == 4); } test_num_defined(); module test_any_defined() { - assert(!any_defined([undef,undef,undef,undef,undef])); - assert(any_defined([3,undef,undef,undef,undef])); - assert(any_defined([undef,3,undef,undef,undef])); - assert(any_defined([undef,undef,3,undef,undef])); - assert(any_defined([undef,undef,undef,3,undef])); - assert(any_defined([undef,undef,undef,undef,3])); - assert(any_defined([3,undef,undef,undef,3])); - assert(any_defined([3,3,3,3,3])); - assert(any_defined(["foo",undef,undef,undef,undef])); - assert(any_defined([undef,"foo",undef,undef,undef])); - assert(any_defined([undef,undef,"foo",undef,undef])); - assert(any_defined([undef,undef,undef,"foo",undef])); - assert(any_defined([undef,undef,undef,undef,"foo"])); - assert(any_defined(["foo",undef,undef,undef,"foo"])); - assert(any_defined(["foo","foo","foo","foo","foo"])); - assert(any_defined([undef,undef,true,false,undef])); + assert(!any_defined([undef,undef,undef,undef,undef])); + assert(any_defined([3,undef,undef,undef,undef])); + assert(any_defined([undef,3,undef,undef,undef])); + assert(any_defined([undef,undef,3,undef,undef])); + assert(any_defined([undef,undef,undef,3,undef])); + assert(any_defined([undef,undef,undef,undef,3])); + assert(any_defined([3,undef,undef,undef,3])); + assert(any_defined([3,3,3,3,3])); + assert(any_defined(["foo",undef,undef,undef,undef])); + assert(any_defined([undef,"foo",undef,undef,undef])); + assert(any_defined([undef,undef,"foo",undef,undef])); + assert(any_defined([undef,undef,undef,"foo",undef])); + assert(any_defined([undef,undef,undef,undef,"foo"])); + assert(any_defined(["foo",undef,undef,undef,"foo"])); + assert(any_defined(["foo","foo","foo","foo","foo"])); + assert(any_defined([undef,undef,true,false,undef])); } test_any_defined(); module test_all_defined() { - assert(!all_defined([undef,undef,undef,undef,undef])); - assert(!all_defined([3,undef,undef,undef,undef])); - assert(!all_defined([undef,3,undef,undef,undef])); - assert(!all_defined([undef,undef,3,undef,undef])); - assert(!all_defined([undef,undef,undef,3,undef])); - assert(!all_defined([undef,undef,undef,undef,3])); - assert(!all_defined([3,undef,undef,undef,3])); - assert(all_defined([3,3,3,3,3])); - assert(!all_defined(["foo",undef,undef,undef,undef])); - assert(!all_defined([undef,"foo",undef,undef,undef])); - assert(!all_defined([undef,undef,"foo",undef,undef])); - assert(!all_defined([undef,undef,undef,"foo",undef])); - assert(!all_defined([undef,undef,undef,undef,"foo"])); - assert(!all_defined(["foo",undef,undef,undef,"foo"])); - assert(all_defined(["foo","foo","foo","foo","foo"])); - assert(!all_defined([undef,undef,true,false,undef])); + assert(!all_defined([undef,undef,undef,undef,undef])); + assert(!all_defined([3,undef,undef,undef,undef])); + assert(!all_defined([undef,3,undef,undef,undef])); + assert(!all_defined([undef,undef,3,undef,undef])); + assert(!all_defined([undef,undef,undef,3,undef])); + assert(!all_defined([undef,undef,undef,undef,3])); + assert(!all_defined([3,undef,undef,undef,3])); + assert(all_defined([3,3,3,3,3])); + assert(!all_defined(["foo",undef,undef,undef,undef])); + assert(!all_defined([undef,"foo",undef,undef,undef])); + assert(!all_defined([undef,undef,"foo",undef,undef])); + assert(!all_defined([undef,undef,undef,"foo",undef])); + assert(!all_defined([undef,undef,undef,undef,"foo"])); + assert(!all_defined(["foo",undef,undef,undef,"foo"])); + assert(all_defined(["foo","foo","foo","foo","foo"])); + assert(!all_defined([undef,undef,true,false,undef])); } test_all_defined(); module test_get_radius() { - assert(get_radius(r1=100,d1=undef,r=undef,d=undef,dflt=23) == 100); - assert(get_radius(r1=undef,d1=200,r=undef,d=undef,dflt=23) == 100); - assert(get_radius(r1=undef,d1=undef,r=100,d=undef,dflt=23) == 100); - assert(get_radius(r1=undef,d1=undef,r=undef,d=200,dflt=23) == 100); - assert(get_radius(r1=50,d1=undef,r=undef,d=undef,dflt=23) == 50); - assert(get_radius(r1=undef,d1=100,r=undef,d=undef,dflt=23) == 50); - assert(get_radius(r1=undef,d1=undef,r=50,d=undef,dflt=23) == 50); - assert(get_radius(r1=undef,d1=undef,r=undef,d=100,dflt=23) == 50); - assert(get_radius(r1=undef,d1=undef,r=undef,d=undef,dflt=23) == 23); - assert(get_radius(r1=undef,d1=undef,r=undef,d=undef,dflt=undef) == undef); + assert(get_radius(r1=100,d1=undef,r=undef,d=undef,dflt=23) == 100); + assert(get_radius(r1=undef,d1=200,r=undef,d=undef,dflt=23) == 100); + assert(get_radius(r1=undef,d1=undef,r=100,d=undef,dflt=23) == 100); + assert(get_radius(r1=undef,d1=undef,r=undef,d=200,dflt=23) == 100); + assert(get_radius(r1=50,d1=undef,r=undef,d=undef,dflt=23) == 50); + assert(get_radius(r1=undef,d1=100,r=undef,d=undef,dflt=23) == 50); + assert(get_radius(r1=undef,d1=undef,r=50,d=undef,dflt=23) == 50); + assert(get_radius(r1=undef,d1=undef,r=undef,d=100,dflt=23) == 50); + assert(get_radius(r1=undef,d1=undef,r=undef,d=undef,dflt=23) == 23); + assert(get_radius(r1=undef,d1=undef,r=undef,d=undef,dflt=undef) == undef); } test_get_radius(); module test_get_height() { - assert(get_height(h=undef, l=undef, height=undef, dflt=undef) == undef); - assert(get_height(h=undef, l=undef, height=undef, dflt=23) == 23); - assert(get_height(h=undef, l=undef, height=50, dflt=23) == 50); - assert(get_height(h=undef, l=50, height=undef, dflt=23) == 50); - assert(get_height(h=50, l=undef, height=undef, dflt=23) == 50); - assert(get_height(h=undef, l=undef, height=75, dflt=23) == 75); - assert(get_height(h=undef, l=75, height=undef, dflt=23) == 75); - assert(get_height(h=75, l=undef, height=undef, dflt=23) == 75); + assert(get_height(h=undef, l=undef, height=undef, dflt=undef) == undef); + assert(get_height(h=undef, l=undef, height=undef, dflt=23) == 23); + assert(get_height(h=undef, l=undef, height=50, dflt=23) == 50); + assert(get_height(h=undef, l=50, height=undef, dflt=23) == 50); + assert(get_height(h=50, l=undef, height=undef, dflt=23) == 50); + assert(get_height(h=undef, l=undef, height=75, dflt=23) == 75); + assert(get_height(h=undef, l=75, height=undef, dflt=23) == 75); + assert(get_height(h=75, l=undef, height=undef, dflt=23) == 75); } test_get_height(); module test_scalar_vec3() { - assert(scalar_vec3(undef) == undef); - assert(scalar_vec3(3) == [3,3,3]); - assert(scalar_vec3(3,dflt=1) == [3,1,1]); - 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],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(undef) == undef); + assert(scalar_vec3(3) == [3,3,3]); + assert(scalar_vec3(3,dflt=1) == [3,1,1]); + 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],dflt=1) == [3,1,1]); + assert(scalar_vec3([3,4,5]) == [3,4,5]); + assert(scalar_vec3([3,4,5,6]) == [3,4,5]); } test_scalar_vec3(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_coords.scad b/tests/test_coords.scad index 0b321d5..702a351 100644 --- a/tests/test_coords.scad +++ b/tests/test_coords.scad @@ -2,208 +2,208 @@ include module test_point2d() { - assert(point2d([1,2,3])==[1,2]); - assert(point2d([2,3])==[2,3]); - assert(point2d([1])==[1,0]); + 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,2], [3,4], [5,6], [7,8]])==[[1,2],[3,4],[5,6],[7,8]]); - assert(path2d([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])==[[1,2],[2,3],[3,4],[4,5]]); - assert(path2d([[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]])==[[1,2],[2,3],[3,4],[4,5]]); + assert(path2d([[1,2], [3,4], [5,6], [7,8]])==[[1,2],[3,4],[5,6],[7,8]]); + assert(path2d([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])==[[1,2],[2,3],[3,4],[4,5]]); + assert(path2d([[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]])==[[1,2],[2,3],[3,4],[4,5]]); } 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]); + 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,2], [3,4], [5,6], [7,8]])==[[1,2,0],[3,4,0],[5,6,0],[7,8,0]]); - assert(path3d([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])==[[1,2,3],[2,3,4],[3,4,5],[4,5,6]]); - assert(path3d([[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]])==[[1,2,3],[2,3,4],[3,4,5],[4,5,6]]); + assert(path3d([[1,2], [3,4], [5,6], [7,8]])==[[1,2,0],[3,4,0],[5,6,0],[7,8,0]]); + assert(path3d([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])==[[1,2,3],[2,3,4],[3,4,5],[4,5,6]]); + assert(path3d([[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]])==[[1,2,3],[2,3,4],[3,4,5],[4,5,6]]); } test_path3d(); module test_point4d() { - assert(point4d([1,2,3,4,5])==[1,2,3,4]); - assert(point4d([1,2,3,4])==[1,2,3,4]); - assert(point4d([1,2,3])==[1,2,3,0]); - assert(point4d([2,3])==[2,3,0,0]); - assert(point4d([1])==[1,0,0,0]); + assert(point4d([1,2,3,4,5])==[1,2,3,4]); + assert(point4d([1,2,3,4])==[1,2,3,4]); + assert(point4d([1,2,3])==[1,2,3,0]); + assert(point4d([2,3])==[2,3,0,0]); + assert(point4d([1])==[1,0,0,0]); } test_point4d(); module test_path4d() { - assert(path4d([[1,2], [3,4], [5,6], [7,8]])==[[1,2,0,0],[3,4,0,0],[5,6,0,0],[7,8,0,0]]); - assert(path4d([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])==[[1,2,3,0],[2,3,4,0],[3,4,5,0],[4,5,6,0]]); - assert(path4d([[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]])==[[1,2,3,4],[2,3,4,5],[3,4,5,6],[4,5,6,7]]); - assert(path4d([[1,2,3,4,5], [2,3,4,5,6], [3,4,5,6,7], [4,5,6,7,8]])==[[1,2,3,4],[2,3,4,5],[3,4,5,6],[4,5,6,7]]); + assert(path4d([[1,2], [3,4], [5,6], [7,8]])==[[1,2,0,0],[3,4,0,0],[5,6,0,0],[7,8,0,0]]); + assert(path4d([[1,2,3], [2,3,4], [3,4,5], [4,5,6]])==[[1,2,3,0],[2,3,4,0],[3,4,5,0],[4,5,6,0]]); + assert(path4d([[1,2,3,4], [2,3,4,5], [3,4,5,6], [4,5,6,7]])==[[1,2,3,4],[2,3,4,5],[3,4,5,6],[4,5,6,7]]); + assert(path4d([[1,2,3,4,5], [2,3,4,5,6], [3,4,5,6,7], [4,5,6,7,8]])==[[1,2,3,4],[2,3,4,5],[3,4,5,6],[4,5,6,7]]); } test_path4d(); module test_polar_to_xy() { - assert(approx(polar_to_xy(20,45), [20/sqrt(2), 20/sqrt(2)])); - assert(approx(polar_to_xy(20,135), [-20/sqrt(2), 20/sqrt(2)])); - assert(approx(polar_to_xy(20,-135), [-20/sqrt(2), -20/sqrt(2)])); - assert(approx(polar_to_xy(20,-45), [20/sqrt(2), -20/sqrt(2)])); - assert(approx(polar_to_xy(40,30), [40*sqrt(3)/2, 40/2])); - assert(approx(polar_to_xy([40,30]), [40*sqrt(3)/2, 40/2])); + assert(approx(polar_to_xy(20,45), [20/sqrt(2), 20/sqrt(2)])); + assert(approx(polar_to_xy(20,135), [-20/sqrt(2), 20/sqrt(2)])); + assert(approx(polar_to_xy(20,-135), [-20/sqrt(2), -20/sqrt(2)])); + assert(approx(polar_to_xy(20,-45), [20/sqrt(2), -20/sqrt(2)])); + assert(approx(polar_to_xy(40,30), [40*sqrt(3)/2, 40/2])); + assert(approx(polar_to_xy([40,30]), [40*sqrt(3)/2, 40/2])); } test_polar_to_xy(); module test_xy_to_polar() { - assert(approx(xy_to_polar([20/sqrt(2), 20/sqrt(2)]),[20,45])); - assert(approx(xy_to_polar([-20/sqrt(2), 20/sqrt(2)]),[20,135])); - assert(approx(xy_to_polar([-20/sqrt(2), -20/sqrt(2)]),[20,-135])); - assert(approx(xy_to_polar([20/sqrt(2), -20/sqrt(2)]),[20,-45])); - assert(approx(xy_to_polar([40*sqrt(3)/2, 40/2]),[40,30])); - assert(approx(xy_to_polar([-40*sqrt(3)/2, 40/2]),[40,150])); - assert(approx(xy_to_polar([-40*sqrt(3)/2, -40/2]),[40,-150])); - assert(approx(xy_to_polar([40*sqrt(3)/2, -40/2]),[40,-30])); + assert(approx(xy_to_polar([20/sqrt(2), 20/sqrt(2)]),[20,45])); + assert(approx(xy_to_polar([-20/sqrt(2), 20/sqrt(2)]),[20,135])); + assert(approx(xy_to_polar([-20/sqrt(2), -20/sqrt(2)]),[20,-135])); + assert(approx(xy_to_polar([20/sqrt(2), -20/sqrt(2)]),[20,-45])); + assert(approx(xy_to_polar([40*sqrt(3)/2, 40/2]),[40,30])); + assert(approx(xy_to_polar([-40*sqrt(3)/2, 40/2]),[40,150])); + assert(approx(xy_to_polar([-40*sqrt(3)/2, -40/2]),[40,-150])); + assert(approx(xy_to_polar([40*sqrt(3)/2, -40/2]),[40,-30])); } test_xy_to_polar(); module test_project_plane() { - assert(approx(project_plane([-5,0,-5], [-10,0,-10], [0,0,0], [0,-10,-10]),[0,10*sqrt(2)/2])); - assert(approx(project_plane([0,-5,-5], [-10,0,-10], [0,0,0], [0,-10,-10]),[6.12372, 10.6066],eps=1e-5)); + assert(approx(project_plane([-5,0,-5], [-10,0,-10], [0,0,0], [0,-10,-10]),[0,10*sqrt(2)/2])); + assert(approx(project_plane([0,-5,-5], [-10,0,-10], [0,0,0], [0,-10,-10]),[6.12372, 10.6066],eps=1e-5)); } test_project_plane(); module test_lift_plane() { - assert(approx(lift_plane([0,10*sqrt(2)/2], [-10,0,-10], [0,0,0], [0,-10,-10]),[-5,0,-5])); - assert(approx(lift_plane([6.12372, 10.6066], [-10,0,-10], [0,0,0], [0,-10,-10]),[0,-5,-5],eps=1e-5)); + assert(approx(lift_plane([0,10*sqrt(2)/2], [-10,0,-10], [0,0,0], [0,-10,-10]),[-5,0,-5])); + assert(approx(lift_plane([6.12372, 10.6066], [-10,0,-10], [0,0,0], [0,-10,-10]),[0,-5,-5],eps=1e-5)); } test_lift_plane(); module test_cylindrical_to_xyz() { - assert(approx(cylindrical_to_xyz(100,90,10),[0,100,10])); - assert(approx(cylindrical_to_xyz(100,270,-10),[0,-100,-10])); - assert(approx(cylindrical_to_xyz(100,-90,-10),[0,-100,-10])); - assert(approx(cylindrical_to_xyz(100,180,0),[-100,0,0])); - assert(approx(cylindrical_to_xyz(100,0,0),[100,0,0])); - assert(approx(cylindrical_to_xyz(100,45,10),[100*sqrt(2)/2,100*sqrt(2)/2,10])); - assert(approx(cylindrical_to_xyz([100,90,10]),[0,100,10])); - assert(approx(cylindrical_to_xyz([100,270,-10]),[0,-100,-10])); - assert(approx(cylindrical_to_xyz([100,-90,-10]),[0,-100,-10])); - assert(approx(cylindrical_to_xyz([100,180,0]),[-100,0,0])); - assert(approx(cylindrical_to_xyz([100,0,0]),[100,0,0])); - assert(approx(cylindrical_to_xyz([100,45,10]),[100*sqrt(2)/2,100*sqrt(2)/2,10])); + assert(approx(cylindrical_to_xyz(100,90,10),[0,100,10])); + assert(approx(cylindrical_to_xyz(100,270,-10),[0,-100,-10])); + assert(approx(cylindrical_to_xyz(100,-90,-10),[0,-100,-10])); + assert(approx(cylindrical_to_xyz(100,180,0),[-100,0,0])); + assert(approx(cylindrical_to_xyz(100,0,0),[100,0,0])); + assert(approx(cylindrical_to_xyz(100,45,10),[100*sqrt(2)/2,100*sqrt(2)/2,10])); + assert(approx(cylindrical_to_xyz([100,90,10]),[0,100,10])); + assert(approx(cylindrical_to_xyz([100,270,-10]),[0,-100,-10])); + assert(approx(cylindrical_to_xyz([100,-90,-10]),[0,-100,-10])); + assert(approx(cylindrical_to_xyz([100,180,0]),[-100,0,0])); + assert(approx(cylindrical_to_xyz([100,0,0]),[100,0,0])); + assert(approx(cylindrical_to_xyz([100,45,10]),[100*sqrt(2)/2,100*sqrt(2)/2,10])); } test_cylindrical_to_xyz(); module test_xyz_to_cylindrical() { - assert(approx(xyz_to_cylindrical(0,100,10),[100,90,10])); - assert(approx(xyz_to_cylindrical(0,-100,-10),[100,-90,-10])); - assert(approx(xyz_to_cylindrical(-100,0,0),[100,180,0])); - assert(approx(xyz_to_cylindrical(100,0,0),[100,0,0])); - assert(approx(xyz_to_cylindrical(100*sqrt(2)/2,100*sqrt(2)/2,10),[100,45,10])); - assert(approx(xyz_to_cylindrical([0,100,10]),[100,90,10])); - assert(approx(xyz_to_cylindrical([0,-100,-10]),[100,-90,-10])); - assert(approx(xyz_to_cylindrical([-100,0,0]),[100,180,0])); - assert(approx(xyz_to_cylindrical([100,0,0]),[100,0,0])); - assert(approx(xyz_to_cylindrical([100*sqrt(2)/2,100*sqrt(2)/2,10]),[100,45,10])); + assert(approx(xyz_to_cylindrical(0,100,10),[100,90,10])); + assert(approx(xyz_to_cylindrical(0,-100,-10),[100,-90,-10])); + assert(approx(xyz_to_cylindrical(-100,0,0),[100,180,0])); + assert(approx(xyz_to_cylindrical(100,0,0),[100,0,0])); + assert(approx(xyz_to_cylindrical(100*sqrt(2)/2,100*sqrt(2)/2,10),[100,45,10])); + assert(approx(xyz_to_cylindrical([0,100,10]),[100,90,10])); + assert(approx(xyz_to_cylindrical([0,-100,-10]),[100,-90,-10])); + assert(approx(xyz_to_cylindrical([-100,0,0]),[100,180,0])); + assert(approx(xyz_to_cylindrical([100,0,0]),[100,0,0])); + assert(approx(xyz_to_cylindrical([100*sqrt(2)/2,100*sqrt(2)/2,10]),[100,45,10])); } test_xyz_to_cylindrical(); module test_spherical_to_xyz() { - assert(approx(spherical_to_xyz(100,90,45),100*[0,sqrt(2)/2,sqrt(2)/2])); - assert(approx(spherical_to_xyz(100,270,45),100*[0,-sqrt(2)/2,sqrt(2)/2])); - assert(approx(spherical_to_xyz(100,-90,45),100*[0,-sqrt(2)/2,sqrt(2)/2])); - assert(approx(spherical_to_xyz(100,90,90),100*[0,1,0])); - assert(approx(spherical_to_xyz(100,-90,90),100*[0,-1,0])); - assert(approx(spherical_to_xyz(100,180,90),100*[-1,0,0])); - assert(approx(spherical_to_xyz(100,0,90),100*[1,0,0])); - assert(approx(spherical_to_xyz(100,0,0),100*[0,0,1])); - assert(approx(spherical_to_xyz(100,0,180),100*[0,0,-1])); - assert(approx(spherical_to_xyz([100,90,45]),100*[0,sqrt(2)/2,sqrt(2)/2])); - assert(approx(spherical_to_xyz([100,270,45]),100*[0,-sqrt(2)/2,sqrt(2)/2])); - assert(approx(spherical_to_xyz([100,-90,45]),100*[0,-sqrt(2)/2,sqrt(2)/2])); - assert(approx(spherical_to_xyz([100,90,90]),100*[0,1,0])); - assert(approx(spherical_to_xyz([100,-90,90]),100*[0,-1,0])); - assert(approx(spherical_to_xyz([100,180,90]),100*[-1,0,0])); - assert(approx(spherical_to_xyz([100,0,90]),100*[1,0,0])); - assert(approx(spherical_to_xyz([100,0,0]),100*[0,0,1])); - assert(approx(spherical_to_xyz([100,0,180]),100*[0,0,-1])); + assert(approx(spherical_to_xyz(100,90,45),100*[0,sqrt(2)/2,sqrt(2)/2])); + assert(approx(spherical_to_xyz(100,270,45),100*[0,-sqrt(2)/2,sqrt(2)/2])); + assert(approx(spherical_to_xyz(100,-90,45),100*[0,-sqrt(2)/2,sqrt(2)/2])); + assert(approx(spherical_to_xyz(100,90,90),100*[0,1,0])); + assert(approx(spherical_to_xyz(100,-90,90),100*[0,-1,0])); + assert(approx(spherical_to_xyz(100,180,90),100*[-1,0,0])); + assert(approx(spherical_to_xyz(100,0,90),100*[1,0,0])); + assert(approx(spherical_to_xyz(100,0,0),100*[0,0,1])); + assert(approx(spherical_to_xyz(100,0,180),100*[0,0,-1])); + assert(approx(spherical_to_xyz([100,90,45]),100*[0,sqrt(2)/2,sqrt(2)/2])); + assert(approx(spherical_to_xyz([100,270,45]),100*[0,-sqrt(2)/2,sqrt(2)/2])); + assert(approx(spherical_to_xyz([100,-90,45]),100*[0,-sqrt(2)/2,sqrt(2)/2])); + assert(approx(spherical_to_xyz([100,90,90]),100*[0,1,0])); + assert(approx(spherical_to_xyz([100,-90,90]),100*[0,-1,0])); + assert(approx(spherical_to_xyz([100,180,90]),100*[-1,0,0])); + assert(approx(spherical_to_xyz([100,0,90]),100*[1,0,0])); + assert(approx(spherical_to_xyz([100,0,0]),100*[0,0,1])); + assert(approx(spherical_to_xyz([100,0,180]),100*[0,0,-1])); } test_spherical_to_xyz(); module test_xyz_to_spherical() { - assert(approx(xyz_to_spherical(0, 100*sqrt(2)/2,100*sqrt(2)/2),[100, 90,45])); - assert(approx(xyz_to_spherical(0,-100*sqrt(2)/2,100*sqrt(2)/2),[100,-90,45])); - assert(approx(xyz_to_spherical( 0, 100, 0),[100, 90, 90])); - assert(approx(xyz_to_spherical( 0,-100, 0),[100,-90, 90])); - assert(approx(xyz_to_spherical(-100, 0, 0),[100,180, 90])); - assert(approx(xyz_to_spherical( 100, 0, 0),[100, 0, 90])); - assert(approx(xyz_to_spherical( 0, 0, 100),[100, 0, 0])); - assert(approx(xyz_to_spherical( 0, 0,-100),[100, 0,180])); - assert(approx(xyz_to_spherical([0, 100*sqrt(2)/2,100*sqrt(2)/2]),[100, 90,45])); - assert(approx(xyz_to_spherical([0,-100*sqrt(2)/2,100*sqrt(2)/2]),[100,-90,45])); - assert(approx(xyz_to_spherical([ 0, 100, 0]),[100, 90, 90])); - assert(approx(xyz_to_spherical([ 0,-100, 0]),[100,-90, 90])); - assert(approx(xyz_to_spherical([-100, 0, 0]),[100,180, 90])); - assert(approx(xyz_to_spherical([ 100, 0, 0]),[100, 0, 90])); - assert(approx(xyz_to_spherical([ 0, 0, 100]),[100, 0, 0])); - assert(approx(xyz_to_spherical([ 0, 0,-100]),[100, 0,180])); + assert(approx(xyz_to_spherical(0, 100*sqrt(2)/2,100*sqrt(2)/2),[100, 90,45])); + assert(approx(xyz_to_spherical(0,-100*sqrt(2)/2,100*sqrt(2)/2),[100,-90,45])); + assert(approx(xyz_to_spherical( 0, 100, 0),[100, 90, 90])); + assert(approx(xyz_to_spherical( 0,-100, 0),[100,-90, 90])); + assert(approx(xyz_to_spherical(-100, 0, 0),[100,180, 90])); + assert(approx(xyz_to_spherical( 100, 0, 0),[100, 0, 90])); + assert(approx(xyz_to_spherical( 0, 0, 100),[100, 0, 0])); + assert(approx(xyz_to_spherical( 0, 0,-100),[100, 0,180])); + assert(approx(xyz_to_spherical([0, 100*sqrt(2)/2,100*sqrt(2)/2]),[100, 90,45])); + assert(approx(xyz_to_spherical([0,-100*sqrt(2)/2,100*sqrt(2)/2]),[100,-90,45])); + assert(approx(xyz_to_spherical([ 0, 100, 0]),[100, 90, 90])); + assert(approx(xyz_to_spherical([ 0,-100, 0]),[100,-90, 90])); + assert(approx(xyz_to_spherical([-100, 0, 0]),[100,180, 90])); + assert(approx(xyz_to_spherical([ 100, 0, 0]),[100, 0, 90])); + assert(approx(xyz_to_spherical([ 0, 0, 100]),[100, 0, 0])); + assert(approx(xyz_to_spherical([ 0, 0,-100]),[100, 0,180])); } test_xyz_to_spherical(); module test_altaz_to_xyz() { - assert(approx(altaz_to_xyz( 0, 0,100),[ 0,100, 0])); - assert(approx(altaz_to_xyz( 90, 0,100),[ 0, 0, 100])); - assert(approx(altaz_to_xyz(-90, 0,100),[ 0, 0,-100])); - assert(approx(altaz_to_xyz( 0, 90,100),[ 100, 0, 0])); - assert(approx(altaz_to_xyz( 0,-90,100),[-100, 0, 0])); - assert(approx(altaz_to_xyz( 45, 90,100),[100*sqrt(2)/2,0,100*sqrt(2)/2])); - assert(approx(altaz_to_xyz(-45, 90,100),[100*sqrt(2)/2,0,-100*sqrt(2)/2])); - assert(approx(altaz_to_xyz([ 0, 0,100]),[ 0,100, 0])); - assert(approx(altaz_to_xyz([ 90, 0,100]),[ 0, 0, 100])); - assert(approx(altaz_to_xyz([-90, 0,100]),[ 0, 0,-100])); - assert(approx(altaz_to_xyz([ 0, 90,100]),[ 100, 0, 0])); - assert(approx(altaz_to_xyz([ 0,-90,100]),[-100, 0, 0])); - assert(approx(altaz_to_xyz([ 45, 90,100]),[100*sqrt(2)/2,0,100*sqrt(2)/2])); - assert(approx(altaz_to_xyz([-45, 90,100]),[100*sqrt(2)/2,0,-100*sqrt(2)/2])); + assert(approx(altaz_to_xyz( 0, 0,100),[ 0,100, 0])); + assert(approx(altaz_to_xyz( 90, 0,100),[ 0, 0, 100])); + assert(approx(altaz_to_xyz(-90, 0,100),[ 0, 0,-100])); + assert(approx(altaz_to_xyz( 0, 90,100),[ 100, 0, 0])); + assert(approx(altaz_to_xyz( 0,-90,100),[-100, 0, 0])); + assert(approx(altaz_to_xyz( 45, 90,100),[100*sqrt(2)/2,0,100*sqrt(2)/2])); + assert(approx(altaz_to_xyz(-45, 90,100),[100*sqrt(2)/2,0,-100*sqrt(2)/2])); + assert(approx(altaz_to_xyz([ 0, 0,100]),[ 0,100, 0])); + assert(approx(altaz_to_xyz([ 90, 0,100]),[ 0, 0, 100])); + assert(approx(altaz_to_xyz([-90, 0,100]),[ 0, 0,-100])); + assert(approx(altaz_to_xyz([ 0, 90,100]),[ 100, 0, 0])); + assert(approx(altaz_to_xyz([ 0,-90,100]),[-100, 0, 0])); + assert(approx(altaz_to_xyz([ 45, 90,100]),[100*sqrt(2)/2,0,100*sqrt(2)/2])); + assert(approx(altaz_to_xyz([-45, 90,100]),[100*sqrt(2)/2,0,-100*sqrt(2)/2])); } test_altaz_to_xyz(); module test_xyz_to_altaz() { - assert(approx(xyz_to_altaz( 0,100, 0),[ 0, 0,100])); - assert(approx(xyz_to_altaz( 0, 0, 100),[ 90, 0,100])); - assert(approx(xyz_to_altaz( 0, 0,-100),[-90, 0,100])); - assert(approx(xyz_to_altaz( 100, 0, 0),[ 0, 90,100])); - assert(approx(xyz_to_altaz(-100, 0, 0),[ 0,-90,100])); - assert(approx(xyz_to_altaz(100*sqrt(2)/2,0,100*sqrt(2)/2),[ 45, 90,100])); - assert(approx(xyz_to_altaz(100*sqrt(2)/2,0,-100*sqrt(2)/2),[-45, 90,100])); - assert(approx(xyz_to_altaz([ 0,100, 0]),[ 0, 0,100])); - assert(approx(xyz_to_altaz([ 0, 0, 100]),[ 90, 0,100])); - assert(approx(xyz_to_altaz([ 0, 0,-100]),[-90, 0,100])); - assert(approx(xyz_to_altaz([ 100, 0, 0]),[ 0, 90,100])); - assert(approx(xyz_to_altaz([-100, 0, 0]),[ 0,-90,100])); - assert(approx(xyz_to_altaz([100*sqrt(2)/2,0,100*sqrt(2)/2]),[ 45, 90,100])); - assert(approx(xyz_to_altaz([100*sqrt(2)/2,0,-100*sqrt(2)/2]),[-45, 90,100])); + assert(approx(xyz_to_altaz( 0,100, 0),[ 0, 0,100])); + assert(approx(xyz_to_altaz( 0, 0, 100),[ 90, 0,100])); + assert(approx(xyz_to_altaz( 0, 0,-100),[-90, 0,100])); + assert(approx(xyz_to_altaz( 100, 0, 0),[ 0, 90,100])); + assert(approx(xyz_to_altaz(-100, 0, 0),[ 0,-90,100])); + assert(approx(xyz_to_altaz(100*sqrt(2)/2,0,100*sqrt(2)/2),[ 45, 90,100])); + assert(approx(xyz_to_altaz(100*sqrt(2)/2,0,-100*sqrt(2)/2),[-45, 90,100])); + assert(approx(xyz_to_altaz([ 0,100, 0]),[ 0, 0,100])); + assert(approx(xyz_to_altaz([ 0, 0, 100]),[ 90, 0,100])); + assert(approx(xyz_to_altaz([ 0, 0,-100]),[-90, 0,100])); + assert(approx(xyz_to_altaz([ 100, 0, 0]),[ 0, 90,100])); + assert(approx(xyz_to_altaz([-100, 0, 0]),[ 0,-90,100])); + assert(approx(xyz_to_altaz([100*sqrt(2)/2,0,100*sqrt(2)/2]),[ 45, 90,100])); + assert(approx(xyz_to_altaz([100*sqrt(2)/2,0,-100*sqrt(2)/2]),[-45, 90,100])); } test_xyz_to_altaz(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_cubetruss.scad b/tests/test_cubetruss.scad index fe5095b..9b71f0e 100644 --- a/tests/test_cubetruss.scad +++ b/tests/test_cubetruss.scad @@ -3,12 +3,12 @@ include module test_cubetruss_dist() { - assert(cubetruss_dist(5,1,size=30,strut=3) == 138); - assert(cubetruss_dist(3,2,size=30,strut=3) == 87); - assert(cubetruss_dist(5,1,size=20,strut=2) == 92); - assert(cubetruss_dist(3,2,size=20,strut=2) == 58); + assert(cubetruss_dist(5,1,size=30,strut=3) == 138); + assert(cubetruss_dist(3,2,size=30,strut=3) == 87); + assert(cubetruss_dist(5,1,size=20,strut=2) == 92); + assert(cubetruss_dist(3,2,size=20,strut=2) == 58); } test_cubetruss_dist(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_edges.scad b/tests/test_edges.scad index ba35b27..047c1f9 100644 --- a/tests/test_edges.scad +++ b/tests/test_edges.scad @@ -2,103 +2,103 @@ include module test_is_edge_array() { - assert(is_edge_array([[0,0,0,0],[0,0,0,0],[0,0,0,0]])); - assert(is_edge_array([[1,1,1,1],[1,1,1,1],[1,1,1,1]])); - assert(!is_edge_array([[1,1,1],[1,1,1],[1,1,1]])); - assert(!is_edge_array([[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]])); - assert(!is_edge_array([[1,1,1,1],[1,1,1,1]])); - assert(!is_edge_array([1,1,1,1])); - assert(!is_edge_array("foo")); - assert(!is_edge_array(42)); - assert(!is_edge_array(true)); - assert(is_edge_array(edges(["X","Y"]))); + assert(is_edge_array([[0,0,0,0],[0,0,0,0],[0,0,0,0]])); + assert(is_edge_array([[1,1,1,1],[1,1,1,1],[1,1,1,1]])); + assert(!is_edge_array([[1,1,1],[1,1,1],[1,1,1]])); + assert(!is_edge_array([[1,1,1,1,1],[1,1,1,1,1],[1,1,1,1,1]])); + assert(!is_edge_array([[1,1,1,1],[1,1,1,1]])); + assert(!is_edge_array([1,1,1,1])); + assert(!is_edge_array("foo")); + assert(!is_edge_array(42)); + assert(!is_edge_array(true)); + assert(is_edge_array(edges(["X","Y"]))); } test_is_edge_array(); module test__edge_set() { - // Edge set pass through - assert(_edge_set([[1,1,1,1],[0,1,0,1],[0,0,0,0]]) == [[1,1,1,1],[0,1,0,1],[0,0,0,0]]); + // Edge set pass through + assert(_edge_set([[1,1,1,1],[0,1,0,1],[0,0,0,0]]) == [[1,1,1,1],[0,1,0,1],[0,0,0,0]]); - // Vectors towards corners - assert(_edge_set([-1,-1,-1]) == [[1,0,0,0],[1,0,0,0],[1,0,0,0]]); - assert(_edge_set([-1,-1, 1]) == [[0,0,1,0],[0,0,1,0],[1,0,0,0]]); - assert(_edge_set([-1, 1,-1]) == [[0,1,0,0],[1,0,0,0],[0,0,1,0]]); - assert(_edge_set([-1, 1, 1]) == [[0,0,0,1],[0,0,1,0],[0,0,1,0]]); - assert(_edge_set([ 1,-1,-1]) == [[1,0,0,0],[0,1,0,0],[0,1,0,0]]); - assert(_edge_set([ 1,-1, 1]) == [[0,0,1,0],[0,0,0,1],[0,1,0,0]]); - assert(_edge_set([ 1, 1,-1]) == [[0,1,0,0],[0,1,0,0],[0,0,0,1]]); - assert(_edge_set([ 1, 1, 1]) == [[0,0,0,1],[0,0,0,1],[0,0,0,1]]); + // Vectors towards corners + assert(_edge_set([-1,-1,-1]) == [[1,0,0,0],[1,0,0,0],[1,0,0,0]]); + assert(_edge_set([-1,-1, 1]) == [[0,0,1,0],[0,0,1,0],[1,0,0,0]]); + assert(_edge_set([-1, 1,-1]) == [[0,1,0,0],[1,0,0,0],[0,0,1,0]]); + assert(_edge_set([-1, 1, 1]) == [[0,0,0,1],[0,0,1,0],[0,0,1,0]]); + assert(_edge_set([ 1,-1,-1]) == [[1,0,0,0],[0,1,0,0],[0,1,0,0]]); + assert(_edge_set([ 1,-1, 1]) == [[0,0,1,0],[0,0,0,1],[0,1,0,0]]); + assert(_edge_set([ 1, 1,-1]) == [[0,1,0,0],[0,1,0,0],[0,0,0,1]]); + assert(_edge_set([ 1, 1, 1]) == [[0,0,0,1],[0,0,0,1],[0,0,0,1]]); - // Vectors towards edges - assert(_edge_set([ 0,-1,-1]) == [[1,0,0,0],[0,0,0,0],[0,0,0,0]]); - assert(_edge_set([ 0, 1,-1]) == [[0,1,0,0],[0,0,0,0],[0,0,0,0]]); - assert(_edge_set([ 0,-1, 1]) == [[0,0,1,0],[0,0,0,0],[0,0,0,0]]); - assert(_edge_set([ 0, 1, 1]) == [[0,0,0,1],[0,0,0,0],[0,0,0,0]]); - assert(_edge_set([-1, 0,-1]) == [[0,0,0,0],[1,0,0,0],[0,0,0,0]]); - assert(_edge_set([ 1, 0,-1]) == [[0,0,0,0],[0,1,0,0],[0,0,0,0]]); - assert(_edge_set([-1, 0, 1]) == [[0,0,0,0],[0,0,1,0],[0,0,0,0]]); - assert(_edge_set([ 1, 0, 1]) == [[0,0,0,0],[0,0,0,1],[0,0,0,0]]); - assert(_edge_set([-1,-1, 0]) == [[0,0,0,0],[0,0,0,0],[1,0,0,0]]); - assert(_edge_set([ 1,-1, 0]) == [[0,0,0,0],[0,0,0,0],[0,1,0,0]]); - assert(_edge_set([-1, 1, 0]) == [[0,0,0,0],[0,0,0,0],[0,0,1,0]]); - assert(_edge_set([ 1, 1, 0]) == [[0,0,0,0],[0,0,0,0],[0,0,0,1]]); + // Vectors towards edges + assert(_edge_set([ 0,-1,-1]) == [[1,0,0,0],[0,0,0,0],[0,0,0,0]]); + assert(_edge_set([ 0, 1,-1]) == [[0,1,0,0],[0,0,0,0],[0,0,0,0]]); + assert(_edge_set([ 0,-1, 1]) == [[0,0,1,0],[0,0,0,0],[0,0,0,0]]); + assert(_edge_set([ 0, 1, 1]) == [[0,0,0,1],[0,0,0,0],[0,0,0,0]]); + assert(_edge_set([-1, 0,-1]) == [[0,0,0,0],[1,0,0,0],[0,0,0,0]]); + assert(_edge_set([ 1, 0,-1]) == [[0,0,0,0],[0,1,0,0],[0,0,0,0]]); + assert(_edge_set([-1, 0, 1]) == [[0,0,0,0],[0,0,1,0],[0,0,0,0]]); + assert(_edge_set([ 1, 0, 1]) == [[0,0,0,0],[0,0,0,1],[0,0,0,0]]); + assert(_edge_set([-1,-1, 0]) == [[0,0,0,0],[0,0,0,0],[1,0,0,0]]); + assert(_edge_set([ 1,-1, 0]) == [[0,0,0,0],[0,0,0,0],[0,1,0,0]]); + assert(_edge_set([-1, 1, 0]) == [[0,0,0,0],[0,0,0,0],[0,0,1,0]]); + assert(_edge_set([ 1, 1, 0]) == [[0,0,0,0],[0,0,0,0],[0,0,0,1]]); - // Vectors towards faces - assert(_edge_set([ 0, 0,-1]) == [[1,1,0,0],[1,1,0,0],[0,0,0,0]]); - assert(_edge_set([ 0, 0, 1]) == [[0,0,1,1],[0,0,1,1],[0,0,0,0]]); - assert(_edge_set([ 0,-1, 0]) == [[1,0,1,0],[0,0,0,0],[1,1,0,0]]); - assert(_edge_set([ 0, 1, 0]) == [[0,1,0,1],[0,0,0,0],[0,0,1,1]]); - assert(_edge_set([-1, 0, 0]) == [[0,0,0,0],[1,0,1,0],[1,0,1,0]]); - assert(_edge_set([ 1, 0, 0]) == [[0,0,0,0],[0,1,0,1],[0,1,0,1]]); + // Vectors towards faces + assert(_edge_set([ 0, 0,-1]) == [[1,1,0,0],[1,1,0,0],[0,0,0,0]]); + assert(_edge_set([ 0, 0, 1]) == [[0,0,1,1],[0,0,1,1],[0,0,0,0]]); + assert(_edge_set([ 0,-1, 0]) == [[1,0,1,0],[0,0,0,0],[1,1,0,0]]); + assert(_edge_set([ 0, 1, 0]) == [[0,1,0,1],[0,0,0,0],[0,0,1,1]]); + assert(_edge_set([-1, 0, 0]) == [[0,0,0,0],[1,0,1,0],[1,0,1,0]]); + assert(_edge_set([ 1, 0, 0]) == [[0,0,0,0],[0,1,0,1],[0,1,0,1]]); - // Named edge sets - assert(_edge_set("X") == [[1,1,1,1],[0,0,0,0],[0,0,0,0]]); - assert(_edge_set("Y") == [[0,0,0,0],[1,1,1,1],[0,0,0,0]]); - assert(_edge_set("Z") == [[0,0,0,0],[0,0,0,0],[1,1,1,1]]); - assert(_edge_set("NONE") == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); - assert(_edge_set("ALL") == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); + // Named edge sets + assert(_edge_set("X") == [[1,1,1,1],[0,0,0,0],[0,0,0,0]]); + assert(_edge_set("Y") == [[0,0,0,0],[1,1,1,1],[0,0,0,0]]); + assert(_edge_set("Z") == [[0,0,0,0],[0,0,0,0],[1,1,1,1]]); + assert(_edge_set("NONE") == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); + assert(_edge_set("ALL") == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); } test__edge_set(); module test_normalize_edges() { - assert(normalize_edges([[-2,-2,-2,-2],[-2,-2,-2,-2],[-2,-2,-2,-2]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); - assert(normalize_edges([[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); - assert(normalize_edges([[0,0,0,0],[0,0,0,0],[0,0,0,0]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); - assert(normalize_edges([[1,1,1,1],[1,1,1,1],[1,1,1,1]]) == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); - assert(normalize_edges([[2,2,2,2],[2,2,2,2],[2,2,2,2]]) == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); + assert(normalize_edges([[-2,-2,-2,-2],[-2,-2,-2,-2],[-2,-2,-2,-2]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); + assert(normalize_edges([[-1,-1,-1,-1],[-1,-1,-1,-1],[-1,-1,-1,-1]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); + assert(normalize_edges([[0,0,0,0],[0,0,0,0],[0,0,0,0]]) == [[0,0,0,0],[0,0,0,0],[0,0,0,0]]); + assert(normalize_edges([[1,1,1,1],[1,1,1,1],[1,1,1,1]]) == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); + assert(normalize_edges([[2,2,2,2],[2,2,2,2],[2,2,2,2]]) == [[1,1,1,1],[1,1,1,1],[1,1,1,1]]); } test_normalize_edges(); module test_edges() { - assert(edges("X")==[[1,1,1,1],[0,0,0,0],[0,0,0,0]]); - assert(edges("Y")==[[0,0,0,0],[1,1,1,1],[0,0,0,0]]); - assert(edges("Z")==[[0,0,0,0],[0,0,0,0],[1,1,1,1]]); - assert(edges(["X"])==[[1,1,1,1],[0,0,0,0],[0,0,0,0]]); - assert(edges(["Y"])==[[0,0,0,0],[1,1,1,1],[0,0,0,0]]); - assert(edges(["Z"])==[[0,0,0,0],[0,0,0,0],[1,1,1,1]]); - assert(edges(["X","Y"])==[[1,1,1,1],[1,1,1,1],[0,0,0,0]]); - assert(edges(["X","Z"])==[[1,1,1,1],[0,0,0,0],[1,1,1,1]]); - assert(edges(["Y","Z"])==[[0,0,0,0],[1,1,1,1],[1,1,1,1]]); - assert(edges("ALL",except="X")==[[0,0,0,0],[1,1,1,1],[1,1,1,1]]); - assert(edges("ALL",except="Y")==[[1,1,1,1],[0,0,0,0],[1,1,1,1]]); - assert(edges("ALL",except="Z")==[[1,1,1,1],[1,1,1,1],[0,0,0,0]]); - assert(edges(["Y","Z"],except=[FRONT+RIGHT,FRONT+LEFT])==[[0,0,0,0],[1,1,1,1],[0,0,1,1]]); + assert(edges("X")==[[1,1,1,1],[0,0,0,0],[0,0,0,0]]); + assert(edges("Y")==[[0,0,0,0],[1,1,1,1],[0,0,0,0]]); + assert(edges("Z")==[[0,0,0,0],[0,0,0,0],[1,1,1,1]]); + assert(edges(["X"])==[[1,1,1,1],[0,0,0,0],[0,0,0,0]]); + assert(edges(["Y"])==[[0,0,0,0],[1,1,1,1],[0,0,0,0]]); + assert(edges(["Z"])==[[0,0,0,0],[0,0,0,0],[1,1,1,1]]); + assert(edges(["X","Y"])==[[1,1,1,1],[1,1,1,1],[0,0,0,0]]); + assert(edges(["X","Z"])==[[1,1,1,1],[0,0,0,0],[1,1,1,1]]); + assert(edges(["Y","Z"])==[[0,0,0,0],[1,1,1,1],[1,1,1,1]]); + assert(edges("ALL",except="X")==[[0,0,0,0],[1,1,1,1],[1,1,1,1]]); + assert(edges("ALL",except="Y")==[[1,1,1,1],[0,0,0,0],[1,1,1,1]]); + assert(edges("ALL",except="Z")==[[1,1,1,1],[1,1,1,1],[0,0,0,0]]); + assert(edges(["Y","Z"],except=[FRONT+RIGHT,FRONT+LEFT])==[[0,0,0,0],[1,1,1,1],[0,0,1,1]]); } test_edges(); module test_corner_edge_count() { - edges = edges([TOP,FRONT+RIGHT]); - assert(corner_edge_count(edges,TOP+FRONT+RIGHT) == 3); - assert(corner_edge_count(edges,TOP+FRONT+LEFT) == 2); - assert(corner_edge_count(edges,BOTTOM+FRONT+RIGHT) == 1); - assert(corner_edge_count(edges,BOTTOM+FRONT+LEFT) == 0); + edges = edges([TOP,FRONT+RIGHT]); + assert(corner_edge_count(edges,TOP+FRONT+RIGHT) == 3); + assert(corner_edge_count(edges,TOP+FRONT+LEFT) == 2); + assert(corner_edge_count(edges,BOTTOM+FRONT+RIGHT) == 1); + assert(corner_edge_count(edges,BOTTOM+FRONT+LEFT) == 0); } test_corner_edge_count(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_errors.scad b/tests/test_errors.scad index aa52225..ba82755 100644 --- a/tests/test_errors.scad +++ b/tests/test_errors.scad @@ -8,4 +8,4 @@ module test_deprecate() {} module test_deprecate_argument() {} -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index ce69361..e5d1f5b 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -2,330 +2,330 @@ include module test_point_on_segment2d() { - assert(point_on_segment2d([-15,0], [[-10,0], [10,0]]) == false); - assert(point_on_segment2d([-10,0], [[-10,0], [10,0]]) == true); - assert(point_on_segment2d([-5,0], [[-10,0], [10,0]]) == true); - assert(point_on_segment2d([0,0], [[-10,0], [10,0]]) == true); - assert(point_on_segment2d([3,3], [[-10,0], [10,0]]) == false); - assert(point_on_segment2d([5,0], [[-10,0], [10,0]]) == true); - assert(point_on_segment2d([10,0], [[-10,0], [10,0]]) == true); - assert(point_on_segment2d([15,0], [[-10,0], [10,0]]) == false); + assert(point_on_segment2d([-15,0], [[-10,0], [10,0]]) == false); + assert(point_on_segment2d([-10,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([-5,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([0,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([3,3], [[-10,0], [10,0]]) == false); + assert(point_on_segment2d([5,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([10,0], [[-10,0], [10,0]]) == true); + assert(point_on_segment2d([15,0], [[-10,0], [10,0]]) == false); - assert(point_on_segment2d([0,-15], [[0,-10], [0,10]]) == false); - assert(point_on_segment2d([0,-10], [[0,-10], [0,10]]) == true); - assert(point_on_segment2d([0, -5], [[0,-10], [0,10]]) == true); - assert(point_on_segment2d([0, 0], [[0,-10], [0,10]]) == true); - assert(point_on_segment2d([3, 3], [[0,-10], [0,10]]) == false); - assert(point_on_segment2d([0, 5], [[0,-10], [0,10]]) == true); - assert(point_on_segment2d([0, 10], [[0,-10], [0,10]]) == true); - assert(point_on_segment2d([0, 15], [[0,-10], [0,10]]) == false); + assert(point_on_segment2d([0,-15], [[0,-10], [0,10]]) == false); + assert(point_on_segment2d([0,-10], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, -5], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, 0], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([3, 3], [[0,-10], [0,10]]) == false); + assert(point_on_segment2d([0, 5], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, 10], [[0,-10], [0,10]]) == true); + assert(point_on_segment2d([0, 15], [[0,-10], [0,10]]) == false); - assert(point_on_segment2d([-15,-15], [[-10,-10], [10,10]]) == false); - assert(point_on_segment2d([-10,-10], [[-10,-10], [10,10]]) == true); - assert(point_on_segment2d([ -5, -5], [[-10,-10], [10,10]]) == true); - assert(point_on_segment2d([ 0, 0], [[-10,-10], [10,10]]) == true); - assert(point_on_segment2d([ 0, 3], [[-10,-10], [10,10]]) == false); - assert(point_on_segment2d([ 5, 5], [[-10,-10], [10,10]]) == true); - assert(point_on_segment2d([ 10, 10], [[-10,-10], [10,10]]) == true); - assert(point_on_segment2d([ 15, 15], [[-10,-10], [10,10]]) == false); + assert(point_on_segment2d([-15,-15], [[-10,-10], [10,10]]) == false); + assert(point_on_segment2d([-10,-10], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ -5, -5], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 0, 0], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 0, 3], [[-10,-10], [10,10]]) == false); + assert(point_on_segment2d([ 5, 5], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 10, 10], [[-10,-10], [10,10]]) == true); + assert(point_on_segment2d([ 15, 15], [[-10,-10], [10,10]]) == false); } test_point_on_segment2d(); module test_point_left_of_segment() { - assert(point_left_of_segment2d([ -3, 0], [[-10,-10], [10,10]]) > 0); - assert(point_left_of_segment2d([ 0, 0], [[-10,-10], [10,10]]) == 0); - assert(point_left_of_segment2d([ 3, 0], [[-10,-10], [10,10]]) < 0); + assert(point_left_of_segment2d([ -3, 0], [[-10,-10], [10,10]]) > 0); + assert(point_left_of_segment2d([ 0, 0], [[-10,-10], [10,10]]) == 0); + assert(point_left_of_segment2d([ 3, 0], [[-10,-10], [10,10]]) < 0); } test_point_left_of_segment(); module test_collinear() { - assert(collinear([-10,-10], [-15, -16], [10,10]) == false); - assert(collinear([-10,-10], [-15, -15], [10,10]) == true); - assert(collinear([-10,-10], [ -3, 0], [10,10]) == false); - assert(collinear([-10,-10], [ 0, 0], [10,10]) == true); - assert(collinear([-10,-10], [ 3, 0], [10,10]) == false); - assert(collinear([-10,-10], [ 15, 15], [10,10]) == true); - assert(collinear([-10,-10], [ 15, 16], [10,10]) == false); + assert(collinear([-10,-10], [-15, -16], [10,10]) == false); + assert(collinear([-10,-10], [-15, -15], [10,10]) == true); + assert(collinear([-10,-10], [ -3, 0], [10,10]) == false); + assert(collinear([-10,-10], [ 0, 0], [10,10]) == true); + assert(collinear([-10,-10], [ 3, 0], [10,10]) == false); + assert(collinear([-10,-10], [ 15, 15], [10,10]) == true); + assert(collinear([-10,-10], [ 15, 16], [10,10]) == false); } test_collinear(); module test_collinear_indexed() { - pts = [ - [-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30] - ]; - assert(collinear_indexed(pts, 0,1,2) == false); - assert(collinear_indexed(pts, 1,2,3) == true); - assert(collinear_indexed(pts, 2,3,4) == true); - assert(collinear_indexed(pts, 3,4,5) == false); - assert(collinear_indexed(pts, 4,5,6) == false); - assert(collinear_indexed(pts, 4,3,2) == true); - assert(collinear_indexed(pts, 0,5,6) == false); + pts = [ + [-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30] + ]; + assert(collinear_indexed(pts, 0,1,2) == false); + assert(collinear_indexed(pts, 1,2,3) == true); + assert(collinear_indexed(pts, 2,3,4) == true); + assert(collinear_indexed(pts, 3,4,5) == false); + assert(collinear_indexed(pts, 4,5,6) == false); + assert(collinear_indexed(pts, 4,3,2) == true); + assert(collinear_indexed(pts, 0,5,6) == false); } test_collinear_indexed(); module test_distance_from_line() { - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,1,1])) < EPSILON); - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [-1,-1,-1])) < EPSILON); - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,-1,0]) - sqrt(2)) < EPSILON); - assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [8,-8,0]) - 8*sqrt(2)) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,1,1])) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [-1,-1,-1])) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [1,-1,0]) - sqrt(2)) < EPSILON); + assert(abs(distance_from_line([[-10,-10,-10], [10,10,10]], [8,-8,0]) - 8*sqrt(2)) < EPSILON); } test_distance_from_line(); module test_line_normal() { - assert(line_normal([0,0],[10,0]) == [0,1]); - assert(line_normal([0,0],[0,10]) == [-1,0]); - assert(line_normal([0,0],[-10,0]) == [0,-1]); - assert(line_normal([0,0],[0,-10]) == [1,0]); - assert(approx(line_normal([0,0],[10,10]), [-sqrt(2)/2,sqrt(2)/2])); - assert(line_normal([[0,0],[10,0]]) == [0,1]); - assert(line_normal([[0,0],[0,10]]) == [-1,0]); - assert(line_normal([[0,0],[-10,0]]) == [0,-1]); - assert(line_normal([[0,0],[0,-10]]) == [1,0]); - assert(approx(line_normal([[0,0],[10,10]]), [-sqrt(2)/2,sqrt(2)/2])); - pts = [for (i=list_range(1000)) rands(-100,100,2,seed_value=4312)]; - for (p = pair_wrap(pts)) { - p1 = p.x; - p2 = p.y; - n = unit(p2-p1); - n1 = [-n.y, n.x]; - n2 = line_normal(p1,p2); - assert(approx(n2, n1)); - } + assert(line_normal([0,0],[10,0]) == [0,1]); + assert(line_normal([0,0],[0,10]) == [-1,0]); + assert(line_normal([0,0],[-10,0]) == [0,-1]); + assert(line_normal([0,0],[0,-10]) == [1,0]); + assert(approx(line_normal([0,0],[10,10]), [-sqrt(2)/2,sqrt(2)/2])); + assert(line_normal([[0,0],[10,0]]) == [0,1]); + assert(line_normal([[0,0],[0,10]]) == [-1,0]); + assert(line_normal([[0,0],[-10,0]]) == [0,-1]); + assert(line_normal([[0,0],[0,-10]]) == [1,0]); + assert(approx(line_normal([[0,0],[10,10]]), [-sqrt(2)/2,sqrt(2)/2])); + pts = [for (i=list_range(1000)) rands(-100,100,2,seed_value=4312)]; + for (p = pair_wrap(pts)) { + p1 = p.x; + p2 = p.y; + n = unit(p2-p1); + n1 = [-n.y, n.x]; + n2 = line_normal(p1,p2); + assert(approx(n2, n1)); + } } test_line_normal(); module test_line_intersection() { - assert(line_intersection([[-10,-10], [ -1,-10]], [[ 10,-10], [ 1,-10]]) == undef); - assert(line_intersection([[-10, 0], [ -1, 0]], [[ 10, 0], [ 1, 0]]) == undef); - assert(line_intersection([[-10, 0], [ -1, 0]], [[ 1, 0], [ 10, 0]]) == undef); - assert(line_intersection([[-10, 0], [ 10, 0]], [[-10, 0], [ 10, 0]]) == undef); - assert(line_intersection([[-10, 10], [ 10, 10]], [[-10,-10], [ 10,-10]]) == undef); - assert(line_intersection([[-10,-10], [ -1, -1]], [[ 10,-10], [ 1, -1]]) == [0,0]); - assert(line_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [-10, 10]]) == [0,0]); - assert(line_intersection([[ -8, 0], [ 12, 4]], [[ 12, 0], [ -8, 4]]) == [2,2]); + assert(line_intersection([[-10,-10], [ -1,-10]], [[ 10,-10], [ 1,-10]]) == undef); + assert(line_intersection([[-10, 0], [ -1, 0]], [[ 10, 0], [ 1, 0]]) == undef); + assert(line_intersection([[-10, 0], [ -1, 0]], [[ 1, 0], [ 10, 0]]) == undef); + assert(line_intersection([[-10, 0], [ 10, 0]], [[-10, 0], [ 10, 0]]) == undef); + assert(line_intersection([[-10, 10], [ 10, 10]], [[-10,-10], [ 10,-10]]) == undef); + assert(line_intersection([[-10,-10], [ -1, -1]], [[ 10,-10], [ 1, -1]]) == [0,0]); + assert(line_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [-10, 10]]) == [0,0]); + assert(line_intersection([[ -8, 0], [ 12, 4]], [[ 12, 0], [ -8, 4]]) == [2,2]); } test_line_intersection(); module test_segment_intersection() { - assert(segment_intersection([[-10,-10], [ -1, -1]], [[ 10,-10], [ 1, -1]]) == undef); - assert(segment_intersection([[-10,-10], [ -1,-10]], [[ 10,-10], [ 1,-10]]) == undef); - assert(segment_intersection([[-10, 0], [ -1, 0]], [[ 10, 0], [ 1, 0]]) == undef); - assert(segment_intersection([[-10, 0], [ -1, 0]], [[ 1, 0], [ 10, 0]]) == undef); - assert(segment_intersection([[-10, 10], [ -1, 1]], [[ 10, 10], [ 1, 1]]) == undef); - assert(segment_intersection([[-10, 0], [ 10, 0]], [[-10, 0], [ 10, 0]]) == undef); - assert(segment_intersection([[-10, 10], [ 10, 10]], [[-10,-10], [ 10,-10]]) == undef); - assert(segment_intersection([[-10, 0], [ 0, 10]], [[ 0, 10], [ 10, 0]]) == [0,10]); - assert(segment_intersection([[-10, 0], [ 0, 10]], [[-10, 20], [ 10, 0]]) == [0,10]); - assert(segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [-10, 10]]) == [0,0]); - assert(segment_intersection([[ -8, 0], [ 12, 4]], [[ 12, 0], [ -8, 4]]) == [2,2]); + assert(segment_intersection([[-10,-10], [ -1, -1]], [[ 10,-10], [ 1, -1]]) == undef); + assert(segment_intersection([[-10,-10], [ -1,-10]], [[ 10,-10], [ 1,-10]]) == undef); + assert(segment_intersection([[-10, 0], [ -1, 0]], [[ 10, 0], [ 1, 0]]) == undef); + assert(segment_intersection([[-10, 0], [ -1, 0]], [[ 1, 0], [ 10, 0]]) == undef); + assert(segment_intersection([[-10, 10], [ -1, 1]], [[ 10, 10], [ 1, 1]]) == undef); + assert(segment_intersection([[-10, 0], [ 10, 0]], [[-10, 0], [ 10, 0]]) == undef); + assert(segment_intersection([[-10, 10], [ 10, 10]], [[-10,-10], [ 10,-10]]) == undef); + assert(segment_intersection([[-10, 0], [ 0, 10]], [[ 0, 10], [ 10, 0]]) == [0,10]); + assert(segment_intersection([[-10, 0], [ 0, 10]], [[-10, 20], [ 10, 0]]) == [0,10]); + assert(segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [-10, 10]]) == [0,0]); + assert(segment_intersection([[ -8, 0], [ 12, 4]], [[ 12, 0], [ -8, 4]]) == [2,2]); } test_segment_intersection(); module test_line_segment_intersection() { - assert(line_segment_intersection([[-10,-10], [ -1,-10]], [[ 10,-10], [ 1,-10]]) == undef); - assert(line_segment_intersection([[-10, 0], [ -1, 0]], [[ 10, 0], [ 1, 0]]) == undef); - assert(line_segment_intersection([[-10, 0], [ -1, 0]], [[ 1, 0], [ 10, 0]]) == undef); - assert(line_segment_intersection([[-10, 0], [ 10, 0]], [[-10, 0], [ 10, 0]]) == undef); - assert(line_segment_intersection([[-10, 10], [ 10, 10]], [[-10,-10], [ 10,-10]]) == undef); - assert(line_segment_intersection([[-10,-10], [ -1, -1]], [[ 10,-10], [ 1, -1]]) == undef); - assert(line_segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [-10, 10]]) == [0,0]); - assert(line_segment_intersection([[ -8, 0], [ 12, 4]], [[ 12, 0], [ -8, 4]]) == [2,2]); - assert(line_segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [ 1, -1]]) == undef); - assert(line_segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [ -1, 1]]) == [0,0]); + assert(line_segment_intersection([[-10,-10], [ -1,-10]], [[ 10,-10], [ 1,-10]]) == undef); + assert(line_segment_intersection([[-10, 0], [ -1, 0]], [[ 10, 0], [ 1, 0]]) == undef); + assert(line_segment_intersection([[-10, 0], [ -1, 0]], [[ 1, 0], [ 10, 0]]) == undef); + assert(line_segment_intersection([[-10, 0], [ 10, 0]], [[-10, 0], [ 10, 0]]) == undef); + assert(line_segment_intersection([[-10, 10], [ 10, 10]], [[-10,-10], [ 10,-10]]) == undef); + assert(line_segment_intersection([[-10,-10], [ -1, -1]], [[ 10,-10], [ 1, -1]]) == undef); + assert(line_segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [-10, 10]]) == [0,0]); + assert(line_segment_intersection([[ -8, 0], [ 12, 4]], [[ 12, 0], [ -8, 4]]) == [2,2]); + assert(line_segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [ 1, -1]]) == undef); + assert(line_segment_intersection([[-10,-10], [ 10, 10]], [[ 10,-10], [ -1, 1]]) == [0,0]); } test_line_segment_intersection(); module test_line_closest_point() { - assert(approx(line_closest_point([[-10,-10], [10,10]], [1,-1]), [0,0])); - assert(approx(line_closest_point([[-10,-10], [10,10]], [-1,1]), [0,0])); - assert(approx(line_closest_point([[-10,-20], [10,20]], [1,2]+[-2,1]), [1,2])); - assert(approx(line_closest_point([[-10,-20], [10,20]], [1,2]+[2,-1]), [1,2])); - assert(approx(line_closest_point([[-10,-20], [10,20]], [13,31]), [15,30])); + assert(approx(line_closest_point([[-10,-10], [10,10]], [1,-1]), [0,0])); + assert(approx(line_closest_point([[-10,-10], [10,10]], [-1,1]), [0,0])); + assert(approx(line_closest_point([[-10,-20], [10,20]], [1,2]+[-2,1]), [1,2])); + assert(approx(line_closest_point([[-10,-20], [10,20]], [1,2]+[2,-1]), [1,2])); + assert(approx(line_closest_point([[-10,-20], [10,20]], [13,31]), [15,30])); } test_line_closest_point(); module test_segment_closest_point() { - assert(approx(segment_closest_point([[-10,-10], [10,10]], [1,-1]), [0,0])); - assert(approx(segment_closest_point([[-10,-10], [10,10]], [-1,1]), [0,0])); - assert(approx(segment_closest_point([[-10,-20], [10,20]], [1,2]+[-2,1]), [1,2])); - assert(approx(segment_closest_point([[-10,-20], [10,20]], [1,2]+[2,-1]), [1,2])); - assert(approx(segment_closest_point([[-10,-20], [10,20]], [13,31]), [10,20])); - assert(approx(segment_closest_point([[-10,-20], [10,20]], [15,25]), [10,20])); + assert(approx(segment_closest_point([[-10,-10], [10,10]], [1,-1]), [0,0])); + assert(approx(segment_closest_point([[-10,-10], [10,10]], [-1,1]), [0,0])); + assert(approx(segment_closest_point([[-10,-20], [10,20]], [1,2]+[-2,1]), [1,2])); + assert(approx(segment_closest_point([[-10,-20], [10,20]], [1,2]+[2,-1]), [1,2])); + assert(approx(segment_closest_point([[-10,-20], [10,20]], [13,31]), [10,20])); + assert(approx(segment_closest_point([[-10,-20], [10,20]], [15,25]), [10,20])); } test_segment_closest_point(); module test_find_circle_2tangents() { - assert(approx(find_circle_2tangents([10,10],[0,0],[10,-10],r=10/sqrt(2))[0],[10,0])); - assert(approx(find_circle_2tangents([-10,10],[0,0],[-10,-10],r=10/sqrt(2))[0],[-10,0])); - assert(approx(find_circle_2tangents([-10,10],[0,0],[10,10],r=10/sqrt(2))[0],[0,10])); - assert(approx(find_circle_2tangents([-10,-10],[0,0],[10,-10],r=10/sqrt(2))[0],[0,-10])); - assert(approx(find_circle_2tangents([0,10],[0,0],[10,0],r=10)[0],[10,10])); - assert(approx(find_circle_2tangents([10,0],[0,0],[0,-10],r=10)[0],[10,-10])); - assert(approx(find_circle_2tangents([0,-10],[0,0],[-10,0],r=10)[0],[-10,-10])); - assert(approx(find_circle_2tangents([-10,0],[0,0],[0,10],r=10)[0],[-10,10])); - assert(approx(find_circle_2tangents(polar_to_xy(10,60),[0,0],[10,0],r=10)[0],polar_to_xy(20,30))); + assert(approx(find_circle_2tangents([10,10],[0,0],[10,-10],r=10/sqrt(2))[0],[10,0])); + assert(approx(find_circle_2tangents([-10,10],[0,0],[-10,-10],r=10/sqrt(2))[0],[-10,0])); + assert(approx(find_circle_2tangents([-10,10],[0,0],[10,10],r=10/sqrt(2))[0],[0,10])); + assert(approx(find_circle_2tangents([-10,-10],[0,0],[10,-10],r=10/sqrt(2))[0],[0,-10])); + assert(approx(find_circle_2tangents([0,10],[0,0],[10,0],r=10)[0],[10,10])); + assert(approx(find_circle_2tangents([10,0],[0,0],[0,-10],r=10)[0],[10,-10])); + assert(approx(find_circle_2tangents([0,-10],[0,0],[-10,0],r=10)[0],[-10,-10])); + assert(approx(find_circle_2tangents([-10,0],[0,0],[0,10],r=10)[0],[-10,10])); + assert(approx(find_circle_2tangents(polar_to_xy(10,60),[0,0],[10,0],r=10)[0],polar_to_xy(20,30))); } test_find_circle_2tangents(); module test_find_circle_3points() { - count = 200; - coords = rands(-100,100,count,seed_value=888); - radii = rands(10,100,count,seed_value=390); - angles = rands(0,360,count,seed_value=699); - // 2D tests. - for(i = list_range(count)) { - cp = select(coords,i,i+1); - r = radii[i]; - angs = sort(select(angles,i,i+2)); - pts = [for (a=angs) cp+polar_to_xy(r,a)]; - res = find_circle_3points(pts); - if (!approx(res[0], cp)) { - echo(cp=cp, r=r, angs=angs); - echo(pts=pts); - echo(got=res[0], expected=cp, delta=res[0]-cp); - assert(approx(res[0], cp)); - } - if (!approx(res[1], r)) { - echo(cp=cp, r=r, angs=angs); - echo(pts=pts); - echo(got=res[1], expected=r, delta=res[1]-r); - assert(approx(res[1], r)); - } - if (!approx(res[2], UP)) { - echo(cp=cp, r=r, angs=angs); - echo(pts=pts); - echo(got=res[2], expected=UP, delta=res[2]-UP); - assert(approx(res[2], UP)); - } - } - for(i = list_range(count)) { - cp = select(coords,i,i+1); - r = radii[i]; - angs = sort(select(angles,i,i+2)); - pts = [for (a=angs) cp+polar_to_xy(r,a)]; - res = find_circle_3points(pts[0], pts[1], pts[2]); - if (!approx(res[0], cp)) { - echo(cp=cp, r=r, angs=angs); - echo(pts=pts); - echo(got=res[0], expected=cp, delta=res[0]-cp); - assert(approx(res[0], cp)); - } - if (!approx(res[1], r)) { - echo(cp=cp, r=r, angs=angs); - echo(pts=pts); - echo(got=res[1], expected=r, delta=res[1]-r); - assert(approx(res[1], r)); - } - if (!approx(res[2], UP)) { - echo(cp=cp, r=r, angs=angs); - echo(pts=pts); - echo(got=res[2], expected=UP, delta=res[2]-UP); - assert(approx(res[2], UP)); - } - } - // 3D tests. - for(i = list_range(count)) { - cp = select(coords,i,i+2); - r = radii[i]; - nrm = unit(select(coords,i+10,i+12)); - n = nrm.z<0? -nrm : nrm; - angs = sort(select(angles,i,i+2)); - pts = translate(cp,p=rot(from=UP,to=n,p=[for (a=angs) point3d(polar_to_xy(r,a))])); - res = find_circle_3points(pts); - if (!approx(res[0], cp)) { - echo(cp=cp, r=r, angs=angs, n=n); - echo(pts=pts); - echo("CP:", got=res[0], expected=cp, delta=res[0]-cp); - assert(approx(res[0], cp)); - } - if (!approx(res[1], r)) { - echo(cp=cp, r=r, angs=angs, n=n); - echo(pts=pts); - echo("R:", got=res[1], expected=r, delta=res[1]-r); - assert(approx(res[1], r)); - } - if (!approx(res[2], n)) { - echo(cp=cp, r=r, angs=angs, n=n); - echo(pts=pts); - echo("NORMAL:", got=res[2], expected=n, delta=res[2]-n); - assert(approx(res[2], n)); - } - } - for(i = list_range(count)) { - cp = select(coords,i,i+2); - r = radii[i]; - nrm = unit(select(coords,i+10,i+12)); - n = nrm.z<0? -nrm : nrm; - angs = sort(select(angles,i,i+2)); - pts = translate(cp,p=rot(from=UP,to=n,p=[for (a=angs) point3d(polar_to_xy(r,a))])); - res = find_circle_3points(pts[0], pts[1], pts[2]); - if (!approx(res[0], cp)) { - echo(cp=cp, r=r, angs=angs, n=n); - echo(pts=pts); - echo("CENTER:", got=res[0], expected=cp, delta=res[0]-cp); - assert(approx(res[0], cp)); - } - if (!approx(res[1], r)) { - echo(cp=cp, r=r, angs=angs, n=n); - echo(pts=pts); - echo("RADIUS:", got=res[1], expected=r, delta=res[1]-r); - assert(approx(res[1], r)); - } - if (!approx(res[2], n)) { - echo(cp=cp, r=r, angs=angs, n=n); - echo(pts=pts); - echo("NORMAL:", got=res[2], expected=n, delta=res[2]-n); - assert(approx(res[2], n)); - } - } + count = 200; + coords = rands(-100,100,count,seed_value=888); + radii = rands(10,100,count,seed_value=390); + angles = rands(0,360,count,seed_value=699); + // 2D tests. + for(i = list_range(count)) { + cp = select(coords,i,i+1); + r = radii[i]; + angs = sort(select(angles,i,i+2)); + pts = [for (a=angs) cp+polar_to_xy(r,a)]; + res = find_circle_3points(pts); + if (!approx(res[0], cp)) { + echo(cp=cp, r=r, angs=angs); + echo(pts=pts); + echo(got=res[0], expected=cp, delta=res[0]-cp); + assert(approx(res[0], cp)); + } + if (!approx(res[1], r)) { + echo(cp=cp, r=r, angs=angs); + echo(pts=pts); + echo(got=res[1], expected=r, delta=res[1]-r); + assert(approx(res[1], r)); + } + if (!approx(res[2], UP)) { + echo(cp=cp, r=r, angs=angs); + echo(pts=pts); + echo(got=res[2], expected=UP, delta=res[2]-UP); + assert(approx(res[2], UP)); + } + } + for(i = list_range(count)) { + cp = select(coords,i,i+1); + r = radii[i]; + angs = sort(select(angles,i,i+2)); + pts = [for (a=angs) cp+polar_to_xy(r,a)]; + res = find_circle_3points(pts[0], pts[1], pts[2]); + if (!approx(res[0], cp)) { + echo(cp=cp, r=r, angs=angs); + echo(pts=pts); + echo(got=res[0], expected=cp, delta=res[0]-cp); + assert(approx(res[0], cp)); + } + if (!approx(res[1], r)) { + echo(cp=cp, r=r, angs=angs); + echo(pts=pts); + echo(got=res[1], expected=r, delta=res[1]-r); + assert(approx(res[1], r)); + } + if (!approx(res[2], UP)) { + echo(cp=cp, r=r, angs=angs); + echo(pts=pts); + echo(got=res[2], expected=UP, delta=res[2]-UP); + assert(approx(res[2], UP)); + } + } + // 3D tests. + for(i = list_range(count)) { + cp = select(coords,i,i+2); + r = radii[i]; + nrm = unit(select(coords,i+10,i+12)); + n = nrm.z<0? -nrm : nrm; + angs = sort(select(angles,i,i+2)); + pts = translate(cp,p=rot(from=UP,to=n,p=[for (a=angs) point3d(polar_to_xy(r,a))])); + res = find_circle_3points(pts); + if (!approx(res[0], cp)) { + echo(cp=cp, r=r, angs=angs, n=n); + echo(pts=pts); + echo("CP:", got=res[0], expected=cp, delta=res[0]-cp); + assert(approx(res[0], cp)); + } + if (!approx(res[1], r)) { + echo(cp=cp, r=r, angs=angs, n=n); + echo(pts=pts); + echo("R:", got=res[1], expected=r, delta=res[1]-r); + assert(approx(res[1], r)); + } + if (!approx(res[2], n)) { + echo(cp=cp, r=r, angs=angs, n=n); + echo(pts=pts); + echo("NORMAL:", got=res[2], expected=n, delta=res[2]-n); + assert(approx(res[2], n)); + } + } + for(i = list_range(count)) { + cp = select(coords,i,i+2); + r = radii[i]; + nrm = unit(select(coords,i+10,i+12)); + n = nrm.z<0? -nrm : nrm; + angs = sort(select(angles,i,i+2)); + pts = translate(cp,p=rot(from=UP,to=n,p=[for (a=angs) point3d(polar_to_xy(r,a))])); + res = find_circle_3points(pts[0], pts[1], pts[2]); + if (!approx(res[0], cp)) { + echo(cp=cp, r=r, angs=angs, n=n); + echo(pts=pts); + echo("CENTER:", got=res[0], expected=cp, delta=res[0]-cp); + assert(approx(res[0], cp)); + } + if (!approx(res[1], r)) { + echo(cp=cp, r=r, angs=angs, n=n); + echo(pts=pts); + echo("RADIUS:", got=res[1], expected=r, delta=res[1]-r); + assert(approx(res[1], r)); + } + if (!approx(res[2], n)) { + echo(cp=cp, r=r, angs=angs, n=n); + echo(pts=pts); + echo("NORMAL:", got=res[2], expected=n, delta=res[2]-n); + assert(approx(res[2], n)); + } + } } test_find_circle_3points(); module test_circle_point_tangents() { - tangs = circle_point_tangents(r=50,cp=[0,0],pt=[50*sqrt(2),0]); - assert(approx(subindex(tangs,0), [45,-45])); - expected = [for (ang=subindex(tangs,0)) polar_to_xy(50,ang)]; - got = subindex(tangs,1); - if (!approx(flatten(got), flatten(expected))) { - echo("TAN_PTS:", got=got, expected=expected, delta=got-expected); - assert(approx(flatten(got), flatten(expected))); - } + tangs = circle_point_tangents(r=50,cp=[0,0],pt=[50*sqrt(2),0]); + assert(approx(subindex(tangs,0), [45,-45])); + expected = [for (ang=subindex(tangs,0)) polar_to_xy(50,ang)]; + got = subindex(tangs,1); + if (!approx(flatten(got), flatten(expected))) { + echo("TAN_PTS:", got=got, expected=expected, delta=got-expected); + assert(approx(flatten(got), flatten(expected))); + } } test_circle_point_tangents(); module test_tri_calc() { - sides = rands(1,100,100,seed_value=8888); - for (p=pair_wrap(sides)) { - opp = p[0]; - adj = p[1]; - hyp = norm([opp,adj]); - ang = acos(adj/hyp); - ang2 = 90-ang; - expected = [adj, opp, hyp, ang, ang2]; - assert(approx(tri_calc(adj=adj, hyp=hyp), expected)); - assert(approx(tri_calc(opp=opp, hyp=hyp), expected)); - assert(approx(tri_calc(adj=adj, opp=opp), expected)); - assert(approx(tri_calc(adj=adj, ang=ang), expected)); - assert(approx(tri_calc(opp=opp, ang=ang), expected, eps=1e-8)); - assert(approx(tri_calc(hyp=hyp, ang=ang), expected)); - assert(approx(tri_calc(adj=adj, ang2=ang2), expected)); - assert(approx(tri_calc(opp=opp, ang2=ang2), expected, eps=1e-8)); - assert(approx(tri_calc(hyp=hyp, ang2=ang2), expected)); - } + sides = rands(1,100,100,seed_value=8888); + for (p=pair_wrap(sides)) { + opp = p[0]; + adj = p[1]; + hyp = norm([opp,adj]); + ang = acos(adj/hyp); + ang2 = 90-ang; + expected = [adj, opp, hyp, ang, ang2]; + assert(approx(tri_calc(adj=adj, hyp=hyp), expected)); + assert(approx(tri_calc(opp=opp, hyp=hyp), expected)); + assert(approx(tri_calc(adj=adj, opp=opp), expected)); + assert(approx(tri_calc(adj=adj, ang=ang), expected)); + assert(approx(tri_calc(opp=opp, ang=ang), expected, eps=1e-8)); + assert(approx(tri_calc(hyp=hyp, ang=ang), expected)); + assert(approx(tri_calc(adj=adj, ang2=ang2), expected)); + assert(approx(tri_calc(opp=opp, ang2=ang2), expected, eps=1e-8)); + assert(approx(tri_calc(hyp=hyp, ang2=ang2), expected)); + } } test_tri_calc(); @@ -345,151 +345,151 @@ module test_hyp_opp_to_ang(); module test_adj_opp_to_ang(); module test_tri_functions() { - sides = rands(1,100,100,seed_value=8181); - for (p = pair_wrap(sides)) { - adj = p.x; - opp = p.y; - hyp = norm([opp,adj]); - ang = atan2(opp,adj); - assert(approx(hyp_opp_to_adj(hyp,opp),adj)); - assert(approx(hyp_ang_to_adj(hyp,ang),adj)); - assert(approx(opp_ang_to_adj(opp,ang),adj)); - assert(approx(hyp_adj_to_opp(hyp,adj),opp)); - assert(approx(hyp_ang_to_opp(hyp,ang),opp)); - assert(approx(adj_ang_to_opp(adj,ang),opp)); - assert(approx(adj_opp_to_hyp(adj,opp),hyp)); - assert(approx(adj_ang_to_hyp(adj,ang),hyp)); - assert(approx(opp_ang_to_hyp(opp,ang),hyp)); - assert(approx(hyp_adj_to_ang(hyp,adj),ang)); - assert(approx(hyp_opp_to_ang(hyp,opp),ang)); - assert(approx(adj_opp_to_ang(adj,opp),ang)); - } + sides = rands(1,100,100,seed_value=8181); + for (p = pair_wrap(sides)) { + adj = p.x; + opp = p.y; + hyp = norm([opp,adj]); + ang = atan2(opp,adj); + assert(approx(hyp_opp_to_adj(hyp,opp),adj)); + assert(approx(hyp_ang_to_adj(hyp,ang),adj)); + assert(approx(opp_ang_to_adj(opp,ang),adj)); + assert(approx(hyp_adj_to_opp(hyp,adj),opp)); + assert(approx(hyp_ang_to_opp(hyp,ang),opp)); + assert(approx(adj_ang_to_opp(adj,ang),opp)); + assert(approx(adj_opp_to_hyp(adj,opp),hyp)); + assert(approx(adj_ang_to_hyp(adj,ang),hyp)); + assert(approx(opp_ang_to_hyp(opp,ang),hyp)); + assert(approx(hyp_adj_to_ang(hyp,adj),ang)); + assert(approx(hyp_opp_to_ang(hyp,opp),ang)); + assert(approx(adj_opp_to_ang(adj,opp),ang)); + } } test_tri_functions(); module test_triangle_area() { - assert(abs(triangle_area([0,0], [0,10], [10,0]) + 50) < EPSILON); - assert(abs(triangle_area([0,0], [0,10], [0,15])) < EPSILON); - assert(abs(triangle_area([0,0], [10,0], [0,10]) - 50) < EPSILON); + assert(abs(triangle_area([0,0], [0,10], [10,0]) + 50) < EPSILON); + assert(abs(triangle_area([0,0], [0,10], [0,15])) < EPSILON); + assert(abs(triangle_area([0,0], [10,0], [0,10]) - 50) < EPSILON); } test_triangle_area(); module test_plane3pt() { - assert(plane3pt([0,0,20], [0,10,10], [0,0,0]) == [1,0,0,0]); - assert(plane3pt([2,0,20], [2,10,10], [2,0,0]) == [1,0,0,2]); - assert(plane3pt([0,0,0], [10,0,10], [0,0,20]) == [0,1,0,0]); - assert(plane3pt([0,2,0], [10,2,10], [0,2,20]) == [0,1,0,2]); - assert(plane3pt([0,0,0], [10,10,0], [20,0,0]) == [0,0,1,0]); - assert(plane3pt([0,0,2], [10,10,2], [20,0,2]) == [0,0,1,2]); + assert(plane3pt([0,0,20], [0,10,10], [0,0,0]) == [1,0,0,0]); + assert(plane3pt([2,0,20], [2,10,10], [2,0,0]) == [1,0,0,2]); + assert(plane3pt([0,0,0], [10,0,10], [0,0,20]) == [0,1,0,0]); + assert(plane3pt([0,2,0], [10,2,10], [0,2,20]) == [0,1,0,2]); + assert(plane3pt([0,0,0], [10,10,0], [20,0,0]) == [0,0,1,0]); + assert(plane3pt([0,0,2], [10,10,2], [20,0,2]) == [0,0,1,2]); } test_plane3pt(); module test_plane3pt_indexed() { - pts = [ [0,0,0], [10,0,0], [0,10,0], [0,0,10] ]; - s13 = sqrt(1/3); - assert(plane3pt_indexed(pts, 0,3,2) == [1,0,0,0]); - assert(plane3pt_indexed(pts, 0,2,3) == [-1,0,0,0]); - assert(plane3pt_indexed(pts, 0,1,3) == [0,1,0,0]); - assert(plane3pt_indexed(pts, 0,3,1) == [0,-1,0,0]); - assert(plane3pt_indexed(pts, 0,2,1) == [0,0,1,0]); - assert(plane3pt_indexed(pts, 0,1,2) == [0,0,-1,0]); - assert(plane3pt_indexed(pts, 3,2,1) == [s13,s13,s13,10*s13]); - assert(plane3pt_indexed(pts, 1,2,3) == [-s13,-s13,-s13,-10*s13]); + pts = [ [0,0,0], [10,0,0], [0,10,0], [0,0,10] ]; + s13 = sqrt(1/3); + assert(plane3pt_indexed(pts, 0,3,2) == [1,0,0,0]); + assert(plane3pt_indexed(pts, 0,2,3) == [-1,0,0,0]); + assert(plane3pt_indexed(pts, 0,1,3) == [0,1,0,0]); + assert(plane3pt_indexed(pts, 0,3,1) == [0,-1,0,0]); + assert(plane3pt_indexed(pts, 0,2,1) == [0,0,1,0]); + assert(plane3pt_indexed(pts, 0,1,2) == [0,0,-1,0]); + assert(plane3pt_indexed(pts, 3,2,1) == [s13,s13,s13,10*s13]); + assert(plane3pt_indexed(pts, 1,2,3) == [-s13,-s13,-s13,-10*s13]); } test_plane3pt_indexed(); module test_plane_from_points() { - assert(plane_from_points([[0,0,20], [0,10,10], [0,0,0], [0,5,3]]) == [1,0,0,0]); - assert(plane_from_points([[2,0,20], [2,10,10], [2,0,0], [2,3,4]]) == [1,0,0,2]); - assert(plane_from_points([[0,0,0], [10,0,10], [0,0,20], [5,0,7]]) == [0,1,0,0]); - assert(plane_from_points([[0,2,0], [10,2,10], [0,2,20], [4,2,3]]) == [0,1,0,2]); - assert(plane_from_points([[0,0,0], [10,10,0], [20,0,0], [8,3,0]]) == [0,0,1,0]); - assert(plane_from_points([[0,0,2], [10,10,2], [20,0,2], [3,4,2]]) == [0,0,1,2]); + assert(plane_from_points([[0,0,20], [0,10,10], [0,0,0], [0,5,3]]) == [1,0,0,0]); + assert(plane_from_points([[2,0,20], [2,10,10], [2,0,0], [2,3,4]]) == [1,0,0,2]); + assert(plane_from_points([[0,0,0], [10,0,10], [0,0,20], [5,0,7]]) == [0,1,0,0]); + assert(plane_from_points([[0,2,0], [10,2,10], [0,2,20], [4,2,3]]) == [0,1,0,2]); + assert(plane_from_points([[0,0,0], [10,10,0], [20,0,0], [8,3,0]]) == [0,0,1,0]); + assert(plane_from_points([[0,0,2], [10,10,2], [20,0,2], [3,4,2]]) == [0,0,1,2]); } test_plane_from_points(); module test_plane_normal() { - assert(plane_normal(plane3pt([0,0,20], [0,10,10], [0,0,0])) == [1,0,0]); - assert(plane_normal(plane3pt([2,0,20], [2,10,10], [2,0,0])) == [1,0,0]); - assert(plane_normal(plane3pt([0,0,0], [10,0,10], [0,0,20])) == [0,1,0]); - assert(plane_normal(plane3pt([0,2,0], [10,2,10], [0,2,20])) == [0,1,0]); - assert(plane_normal(plane3pt([0,0,0], [10,10,0], [20,0,0])) == [0,0,1]); - assert(plane_normal(plane3pt([0,0,2], [10,10,2], [20,0,2])) == [0,0,1]); + assert(plane_normal(plane3pt([0,0,20], [0,10,10], [0,0,0])) == [1,0,0]); + assert(plane_normal(plane3pt([2,0,20], [2,10,10], [2,0,0])) == [1,0,0]); + assert(plane_normal(plane3pt([0,0,0], [10,0,10], [0,0,20])) == [0,1,0]); + assert(plane_normal(plane3pt([0,2,0], [10,2,10], [0,2,20])) == [0,1,0]); + assert(plane_normal(plane3pt([0,0,0], [10,10,0], [20,0,0])) == [0,0,1]); + assert(plane_normal(plane3pt([0,0,2], [10,10,2], [20,0,2])) == [0,0,1]); } test_plane_normal(); module test_distance_from_plane() { - plane1 = plane3pt([-10,0,0], [0,10,0], [10,0,0]); - assert(distance_from_plane(plane1, [0,0,5]) == 5); - assert(distance_from_plane(plane1, [5,5,8]) == 8); + plane1 = plane3pt([-10,0,0], [0,10,0], [10,0,0]); + assert(distance_from_plane(plane1, [0,0,5]) == 5); + assert(distance_from_plane(plane1, [5,5,8]) == 8); } test_distance_from_plane(); module test_coplanar() { - plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); - assert(coplanar(plane, [5,5,10]) == true); - assert(coplanar(plane, [10/3,10/3,20/3]) == true); - assert(coplanar(plane, [0,0,0]) == true); - assert(coplanar(plane, [1,1,0]) == false); - assert(coplanar(plane, [-1,1,0]) == true); - assert(coplanar(plane, [1,-1,0]) == true); - assert(coplanar(plane, [5,5,5]) == false); + plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); + assert(coplanar(plane, [5,5,10]) == true); + assert(coplanar(plane, [10/3,10/3,20/3]) == true); + assert(coplanar(plane, [0,0,0]) == true); + assert(coplanar(plane, [1,1,0]) == false); + assert(coplanar(plane, [-1,1,0]) == true); + assert(coplanar(plane, [1,-1,0]) == true); + assert(coplanar(plane, [5,5,5]) == false); } test_coplanar(); module test_in_front_of_plane() { - plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); - assert(in_front_of_plane(plane, [5,5,10]) == false); - assert(in_front_of_plane(plane, [-5,0,0]) == true); - assert(in_front_of_plane(plane, [5,0,0]) == false); - assert(in_front_of_plane(plane, [0,-5,0]) == true); - assert(in_front_of_plane(plane, [0,5,0]) == false); - assert(in_front_of_plane(plane, [0,0,5]) == true); - assert(in_front_of_plane(plane, [0,0,-5]) == false); + plane = plane3pt([0,0,0], [0,10,10], [10,0,10]); + assert(in_front_of_plane(plane, [5,5,10]) == false); + assert(in_front_of_plane(plane, [-5,0,0]) == true); + assert(in_front_of_plane(plane, [5,0,0]) == false); + assert(in_front_of_plane(plane, [0,-5,0]) == true); + assert(in_front_of_plane(plane, [0,5,0]) == false); + assert(in_front_of_plane(plane, [0,0,5]) == true); + assert(in_front_of_plane(plane, [0,0,-5]) == false); } test_in_front_of_plane(); module test_is_path() { - assert(!is_path(123)); - assert(!is_path("foo")); - assert(!is_path(true)); - assert(!is_path([])); - assert(!is_path([[]])); - assert(!is_path([["foo","bar","baz"]])); - assert(!is_path([[1,2,3]])); - assert(!is_path([["foo","bar","baz"],["qux","quux","quuux"]])); - assert(is_path([[1,2,3],[4,5,6]])); - assert(is_path([[1,2,3],[4,5,6],[7,8,9]])); + assert(!is_path(123)); + assert(!is_path("foo")); + assert(!is_path(true)); + assert(!is_path([])); + assert(!is_path([[]])); + assert(!is_path([["foo","bar","baz"]])); + assert(!is_path([[1,2,3]])); + assert(!is_path([["foo","bar","baz"],["qux","quux","quuux"]])); + assert(is_path([[1,2,3],[4,5,6]])); + assert(is_path([[1,2,3],[4,5,6],[7,8,9]])); } test_is_path(); module test_is_closed_path() { - assert(!is_closed_path([[1,2,3],[4,5,6],[1,8,9]])); - assert(is_closed_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]])); + assert(!is_closed_path([[1,2,3],[4,5,6],[1,8,9]])); + assert(is_closed_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]])); } test_is_closed_path(); module test_close_path() { - assert(close_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); - assert(close_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); + assert(close_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); + assert(close_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9],[1,2,3]]); } test_close_path(); module test_cleanup_path() { - assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]); - assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]); + assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9]]) == [[1,2,3],[4,5,6],[1,8,9]]); + assert(cleanup_path([[1,2,3],[4,5,6],[1,8,9],[1,2,3]]) == [[1,2,3],[4,5,6],[1,8,9]]); } test_cleanup_path(); @@ -500,71 +500,71 @@ test_cleanup_path(); module test_polygon_area() { - assert(approx(polygon_area([[1,1],[-1,1],[-1,-1],[1,-1]]), 4)); - assert(approx(polygon_area(circle(r=50,$fn=1000)), -PI*50*50, eps=0.1)); + assert(approx(polygon_area([[1,1],[-1,1],[-1,-1],[1,-1]]), 4)); + assert(approx(polygon_area(circle(r=50,$fn=1000)), -PI*50*50, eps=0.1)); } test_polygon_area(); module test_polygon_shift() { - path = [[1,1],[-1,1],[-1,-1],[1,-1]]; - assert(polygon_shift(path,1) == [[-1,1],[-1,-1],[1,-1],[1,1]]); - assert(polygon_shift(path,2) == [[-1,-1],[1,-1],[1,1],[-1,1]]); + path = [[1,1],[-1,1],[-1,-1],[1,-1]]; + assert(polygon_shift(path,1) == [[-1,1],[-1,-1],[1,-1],[1,1]]); + assert(polygon_shift(path,2) == [[-1,-1],[1,-1],[1,1],[-1,1]]); } test_polygon_shift(); module test_polygon_shift_to_closest_point() { - path = [[1,1],[-1,1],[-1,-1],[1,-1]]; - assert(polygon_shift_to_closest_point(path,[1.1,1.1]) == [[1,1],[-1,1],[-1,-1],[1,-1]]); - assert(polygon_shift_to_closest_point(path,[-1.1,1.1]) == [[-1,1],[-1,-1],[1,-1],[1,1]]); - assert(polygon_shift_to_closest_point(path,[-1.1,-1.1]) == [[-1,-1],[1,-1],[1,1],[-1,1]]); - assert(polygon_shift_to_closest_point(path,[1.1,-1.1]) == [[1,-1],[1,1],[-1,1],[-1,-1]]); + path = [[1,1],[-1,1],[-1,-1],[1,-1]]; + assert(polygon_shift_to_closest_point(path,[1.1,1.1]) == [[1,1],[-1,1],[-1,-1],[1,-1]]); + assert(polygon_shift_to_closest_point(path,[-1.1,1.1]) == [[-1,1],[-1,-1],[1,-1],[1,1]]); + assert(polygon_shift_to_closest_point(path,[-1.1,-1.1]) == [[-1,-1],[1,-1],[1,1],[-1,1]]); + assert(polygon_shift_to_closest_point(path,[1.1,-1.1]) == [[1,-1],[1,1],[-1,1],[-1,-1]]); } test_polygon_shift_to_closest_point(); module test_first_noncollinear(){ - pts = [ - [1,1], [2,2], [3,3], [4,4], [4,5], [5,6] - ]; - assert(first_noncollinear(0,1,pts) == 4); - assert(first_noncollinear(1,0,pts) == 4); - assert(first_noncollinear(0,2,pts) == 4); - assert(first_noncollinear(2,0,pts) == 4); - assert(first_noncollinear(1,2,pts) == 4); - assert(first_noncollinear(2,1,pts) == 4); - assert(first_noncollinear(0,3,pts) == 4); - assert(first_noncollinear(3,0,pts) == 4); - assert(first_noncollinear(1,3,pts) == 4); - assert(first_noncollinear(3,1,pts) == 4); - assert(first_noncollinear(2,3,pts) == 4); - assert(first_noncollinear(3,2,pts) == 4); - assert(first_noncollinear(0,4,pts) == 1); - assert(first_noncollinear(4,0,pts) == 1); - assert(first_noncollinear(1,4,pts) == 0); - assert(first_noncollinear(4,1,pts) == 0); - assert(first_noncollinear(2,4,pts) == 0); - assert(first_noncollinear(4,2,pts) == 0); - assert(first_noncollinear(3,4,pts) == 0); - assert(first_noncollinear(4,3,pts) == 0); - assert(first_noncollinear(0,5,pts) == 1); - assert(first_noncollinear(5,0,pts) == 1); - assert(first_noncollinear(1,5,pts) == 0); - assert(first_noncollinear(5,1,pts) == 0); - assert(first_noncollinear(2,5,pts) == 0); - assert(first_noncollinear(5,2,pts) == 0); - assert(first_noncollinear(3,5,pts) == 0); - assert(first_noncollinear(5,3,pts) == 0); - assert(first_noncollinear(4,5,pts) == 0); - assert(first_noncollinear(5,4,pts) == 0); + pts = [ + [1,1], [2,2], [3,3], [4,4], [4,5], [5,6] + ]; + assert(first_noncollinear(0,1,pts) == 4); + assert(first_noncollinear(1,0,pts) == 4); + assert(first_noncollinear(0,2,pts) == 4); + assert(first_noncollinear(2,0,pts) == 4); + assert(first_noncollinear(1,2,pts) == 4); + assert(first_noncollinear(2,1,pts) == 4); + assert(first_noncollinear(0,3,pts) == 4); + assert(first_noncollinear(3,0,pts) == 4); + assert(first_noncollinear(1,3,pts) == 4); + assert(first_noncollinear(3,1,pts) == 4); + assert(first_noncollinear(2,3,pts) == 4); + assert(first_noncollinear(3,2,pts) == 4); + assert(first_noncollinear(0,4,pts) == 1); + assert(first_noncollinear(4,0,pts) == 1); + assert(first_noncollinear(1,4,pts) == 0); + assert(first_noncollinear(4,1,pts) == 0); + assert(first_noncollinear(2,4,pts) == 0); + assert(first_noncollinear(4,2,pts) == 0); + assert(first_noncollinear(3,4,pts) == 0); + assert(first_noncollinear(4,3,pts) == 0); + assert(first_noncollinear(0,5,pts) == 1); + assert(first_noncollinear(5,0,pts) == 1); + assert(first_noncollinear(1,5,pts) == 0); + assert(first_noncollinear(5,1,pts) == 0); + assert(first_noncollinear(2,5,pts) == 0); + assert(first_noncollinear(5,2,pts) == 0); + assert(first_noncollinear(3,5,pts) == 0); + assert(first_noncollinear(5,3,pts) == 0); + assert(first_noncollinear(4,5,pts) == 0); + assert(first_noncollinear(5,4,pts) == 0); } test_first_noncollinear(); module test_find_noncollinear_points() { - assert(find_noncollinear_points([[1,1],[2,2],[3,3],[4,4],[4,5],[5,6]]) == [0,5,3]); - assert(find_noncollinear_points([[1,1],[2,2],[8,3],[4,4],[4,5],[5,6]]) == [0,2,5]); + assert(find_noncollinear_points([[1,1],[2,2],[3,3],[4,4],[4,5],[5,6]]) == [0,5,3]); + assert(find_noncollinear_points([[1,1],[2,2],[8,3],[4,4],[4,5],[5,6]]) == [0,2,5]); } test_find_noncollinear_points(); @@ -576,7 +576,7 @@ test_find_noncollinear_points(); module test_simplify_path() { path = [[-20,-20], [-10,-20], [0,-10], [10,0], [20,10], [20,20], [15,30]]; - assert(simplify_path(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]); + assert(simplify_path(path) == [[-20,-20], [-10,-20], [20,10], [20,20], [15,30]]); } test_simplify_path(); @@ -584,110 +584,110 @@ test_simplify_path(); module test_simplify_path_indexed() { pts = [[10,0], [0,-10], [20,20], [20,10], [-20,-20], [15,30], [-10,-20]]; path = [4,6,1,0,3,2,5]; - assert(simplify_path_indexed(pts, path) == [4,6,3,2,5]); + assert(simplify_path_indexed(pts, path) == [4,6,3,2,5]); } test_simplify_path_indexed(); module test_point_in_polygon() { - poly = [for (a=[0:30:359]) 10*[cos(a),sin(a)]]; - assert(point_in_polygon([0,0], poly) == 1); - assert(point_in_polygon([20,0], poly) == -1); - assert(point_in_polygon([5,5], poly) == 1); - assert(point_in_polygon([-5,5], poly) == 1); - assert(point_in_polygon([-5,-5], poly) == 1); - assert(point_in_polygon([5,-5], poly) == 1); - assert(point_in_polygon([-10,-10], poly) == -1); - assert(point_in_polygon([10,0], poly) == 0); - assert(point_in_polygon([0,10], poly) == 0); - assert(point_in_polygon([0,-10], poly) == 0); + poly = [for (a=[0:30:359]) 10*[cos(a),sin(a)]]; + assert(point_in_polygon([0,0], poly) == 1); + assert(point_in_polygon([20,0], poly) == -1); + assert(point_in_polygon([5,5], poly) == 1); + assert(point_in_polygon([-5,5], poly) == 1); + assert(point_in_polygon([-5,-5], poly) == 1); + assert(point_in_polygon([5,-5], poly) == 1); + assert(point_in_polygon([-10,-10], poly) == -1); + assert(point_in_polygon([10,0], poly) == 0); + assert(point_in_polygon([0,10], poly) == 0); + assert(point_in_polygon([0,-10], poly) == 0); } test_point_in_polygon(); module test_pointlist_bounds() { - pts = [ - [-53,27,12], - [-63,97,36], - [84,-32,-5], - [63,-24,42], - [23,57,-42] - ]; - assert(pointlist_bounds(pts) == [[-63,-32,-42], [84,97,42]]); + pts = [ + [-53,27,12], + [-63,97,36], + [84,-32,-5], + [63,-24,42], + [23,57,-42] + ]; + assert(pointlist_bounds(pts) == [[-63,-32,-42], [84,97,42]]); } test_pointlist_bounds(); module test_closest_point() { - ptlist = [for (i=list_range(100)) rands(-100,100,2,seed_value=8463)]; - testpts = [for (i=list_range(100)) rands(-100,100,2,seed_value=6834)]; - for (pt = testpts) { - pidx = closest_point(pt,ptlist); - dists = [for (p=ptlist) norm(pt-p)]; - mindist = min(dists); - assert(mindist == dists[pidx]); - } + ptlist = [for (i=list_range(100)) rands(-100,100,2,seed_value=8463)]; + testpts = [for (i=list_range(100)) rands(-100,100,2,seed_value=6834)]; + for (pt = testpts) { + pidx = closest_point(pt,ptlist); + dists = [for (p=ptlist) norm(pt-p)]; + mindist = min(dists); + assert(mindist == dists[pidx]); + } } test_closest_point(); module test_furthest_point() { - ptlist = [for (i=list_range(100)) rands(-100,100,2,seed_value=8463)]; - testpts = [for (i=list_range(100)) rands(-100,100,2,seed_value=6834)]; - for (pt = testpts) { - pidx = furthest_point(pt,ptlist); - dists = [for (p=ptlist) norm(pt-p)]; - mindist = max(dists); - assert(mindist == dists[pidx]); - } + ptlist = [for (i=list_range(100)) rands(-100,100,2,seed_value=8463)]; + testpts = [for (i=list_range(100)) rands(-100,100,2,seed_value=6834)]; + for (pt = testpts) { + pidx = furthest_point(pt,ptlist); + dists = [for (p=ptlist) norm(pt-p)]; + mindist = max(dists); + assert(mindist == dists[pidx]); + } } test_furthest_point(); module test_polygon_is_clockwise() { - assert(polygon_is_clockwise([[-1,1],[1,1],[1,-1],[-1,-1]])); - assert(!polygon_is_clockwise([[1,1],[-1,1],[-1,-1],[1,-1]])); - assert(polygon_is_clockwise(circle(d=100))); - assert(polygon_is_clockwise(square(100))); + assert(polygon_is_clockwise([[-1,1],[1,1],[1,-1],[-1,-1]])); + assert(!polygon_is_clockwise([[1,1],[-1,1],[-1,-1],[1,-1]])); + assert(polygon_is_clockwise(circle(d=100))); + assert(polygon_is_clockwise(square(100))); } test_polygon_is_clockwise(); module test_clockwise_polygon() { - path = circle(d=100); - rpath = concat([path[0]], reverse(select(path,1,-1))); - assert(clockwise_polygon(path) == path); - assert(clockwise_polygon(rpath) == path); + path = circle(d=100); + rpath = concat([path[0]], reverse(select(path,1,-1))); + assert(clockwise_polygon(path) == path); + assert(clockwise_polygon(rpath) == path); } test_clockwise_polygon(); module test_ccw_polygon() { - path = circle(d=100); - rpath = concat([path[0]], reverse(select(path,1,-1))); - assert(ccw_polygon(path) == rpath); - assert(ccw_polygon(rpath) == rpath); + path = circle(d=100); + rpath = concat([path[0]], reverse(select(path,1,-1))); + assert(ccw_polygon(path) == rpath); + assert(ccw_polygon(rpath) == rpath); } test_ccw_polygon(); module test_reverse_polygon() { - path = circle(d=100); - rpath = concat([path[0]], reverse(select(path,1,-1))); - assert(reverse_polygon(path) == rpath); - assert(reverse_polygon(rpath) == path); + path = circle(d=100); + rpath = concat([path[0]], reverse(select(path,1,-1))); + assert(reverse_polygon(path) == rpath); + assert(reverse_polygon(rpath) == path); } test_reverse_polygon(); module test_is_region() { - assert(is_region([circle(d=10),square(10)])); - assert(is_region([circle(d=10),square(10),circle(d=50)])); - assert(is_region([square(10)])); - assert(!is_region([])); - assert(!is_region(23)); - assert(!is_region(true)); - assert(!is_region("foo")); + assert(is_region([circle(d=10),square(10)])); + assert(is_region([circle(d=10),square(10),circle(d=50)])); + assert(is_region([square(10)])); + assert(!is_region([])); + assert(!is_region(23)); + assert(!is_region(true)); + assert(!is_region("foo")); } test_is_region(); @@ -708,4 +708,4 @@ test_is_region(); cube(); // Prevents warning about no top-level geometry. -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_math.scad b/tests/test_math.scad index 2103ed0..5a3dd7e 100644 --- a/tests/test_math.scad +++ b/tests/test_math.scad @@ -3,377 +3,377 @@ include // Simple Calculations module test_quant() { - assert(quant(-4,3) == -3); - assert(quant(-3,3) == -3); - assert(quant(-2,3) == -3); - assert(quant(-1,3) == 0); - assert(quant(0,3) == 0); - assert(quant(1,3) == 0); - assert(quant(2,3) == 3); - assert(quant(3,3) == 3); - assert(quant(4,3) == 3); - assert(quant(7,3) == 6); - assert(quant([12,13,13.1,14,14.1,15,16],4) == [12,12,12,16,16,16,16]); - assert(quant([9,10,10.4,10.5,11,12],3) == [9,9,9,12,12,12]); - assert(quant([[9,10,10.4],[10.5,11,12]],3) == [[9,9,9],[12,12,12]]); + assert(quant(-4,3) == -3); + assert(quant(-3,3) == -3); + assert(quant(-2,3) == -3); + assert(quant(-1,3) == 0); + assert(quant(0,3) == 0); + assert(quant(1,3) == 0); + assert(quant(2,3) == 3); + assert(quant(3,3) == 3); + assert(quant(4,3) == 3); + assert(quant(7,3) == 6); + assert(quant([12,13,13.1,14,14.1,15,16],4) == [12,12,12,16,16,16,16]); + assert(quant([9,10,10.4,10.5,11,12],3) == [9,9,9,12,12,12]); + assert(quant([[9,10,10.4],[10.5,11,12]],3) == [[9,9,9],[12,12,12]]); } test_quant(); module test_quantdn() { - assert(quantdn(-4,3) == -6); - assert(quantdn(-3,3) == -3); - assert(quantdn(-2,3) == -3); - assert(quantdn(-1,3) == -3); - assert(quantdn(0,3) == 0); - assert(quantdn(1,3) == 0); - assert(quantdn(2,3) == 0); - assert(quantdn(3,3) == 3); - assert(quantdn(4,3) == 3); - assert(quantdn(7,3) == 6); - assert(quantdn([12,13,13.1,14,14.1,15,16],4) == [12,12,12,12,12,12,16]); - assert(quantdn([9,10,10.4,10.5,11,12],3) == [9,9,9,9,9,12]); - assert(quantdn([[9,10,10.4],[10.5,11,12]],3) == [[9,9,9],[9,9,12]]); + assert(quantdn(-4,3) == -6); + assert(quantdn(-3,3) == -3); + assert(quantdn(-2,3) == -3); + assert(quantdn(-1,3) == -3); + assert(quantdn(0,3) == 0); + assert(quantdn(1,3) == 0); + assert(quantdn(2,3) == 0); + assert(quantdn(3,3) == 3); + assert(quantdn(4,3) == 3); + assert(quantdn(7,3) == 6); + assert(quantdn([12,13,13.1,14,14.1,15,16],4) == [12,12,12,12,12,12,16]); + assert(quantdn([9,10,10.4,10.5,11,12],3) == [9,9,9,9,9,12]); + assert(quantdn([[9,10,10.4],[10.5,11,12]],3) == [[9,9,9],[9,9,12]]); } test_quantdn(); module test_quantup() { - assert(quantup(-4,3) == -3); - assert(quantup(-3,3) == -3); - assert(quantup(-2,3) == 0); - assert(quantup(-1,3) == 0); - assert(quantup(0,3) == 0); - assert(quantup(1,3) == 3); - assert(quantup(2,3) == 3); - assert(quantup(3,3) == 3); - assert(quantup(4,3) == 6); - assert(quantup(7,3) == 9); - assert(quantup([12,13,13.1,14,14.1,15,16],4) == [12,16,16,16,16,16,16]); - assert(quantup([9,10,10.4,10.5,11,12],3) == [9,12,12,12,12,12]); - assert(quantup([[9,10,10.4],[10.5,11,12]],3) == [[9,12,12],[12,12,12]]); + assert(quantup(-4,3) == -3); + assert(quantup(-3,3) == -3); + assert(quantup(-2,3) == 0); + assert(quantup(-1,3) == 0); + assert(quantup(0,3) == 0); + assert(quantup(1,3) == 3); + assert(quantup(2,3) == 3); + assert(quantup(3,3) == 3); + assert(quantup(4,3) == 6); + assert(quantup(7,3) == 9); + assert(quantup([12,13,13.1,14,14.1,15,16],4) == [12,16,16,16,16,16,16]); + assert(quantup([9,10,10.4,10.5,11,12],3) == [9,12,12,12,12,12]); + assert(quantup([[9,10,10.4],[10.5,11,12]],3) == [[9,12,12],[12,12,12]]); } test_quantup(); module test_constrain() { - assert(constrain(-2,-1,1) == -1); - assert(constrain(-1.75,-1,1) == -1); - assert(constrain(-1,-1,1) == -1); - assert(constrain(-0.75,-1,1) == -0.75); - assert(constrain(0,-1,1) == 0); - assert(constrain(0.75,-1,1) == 0.75); - assert(constrain(1,-1,1) == 1); - assert(constrain(1.75,-1,1) == 1); - assert(constrain(2,-1,1) == 1); + assert(constrain(-2,-1,1) == -1); + assert(constrain(-1.75,-1,1) == -1); + assert(constrain(-1,-1,1) == -1); + assert(constrain(-0.75,-1,1) == -0.75); + assert(constrain(0,-1,1) == 0); + assert(constrain(0.75,-1,1) == 0.75); + assert(constrain(1,-1,1) == 1); + assert(constrain(1.75,-1,1) == 1); + assert(constrain(2,-1,1) == 1); } test_constrain(); module test_approx() { - assert(approx(PI, 3.141592653589793236) == true); - assert(approx(PI, 3.1415926) == false); - assert(approx(PI, 3.1415926, eps=1e-6) == true); - assert(approx(-PI, -3.141592653589793236) == true); - assert(approx(-PI, -3.1415926) == false); - assert(approx(-PI, -3.1415926, eps=1e-6) == true); - assert(approx(1/3, 0.3333333333) == true); - assert(approx(-1/3, -0.3333333333) == true); - assert(approx(10*[cos(30),sin(30)], 10*[sqrt(3)/2, 1/2]) == true); + assert(approx(PI, 3.141592653589793236) == true); + assert(approx(PI, 3.1415926) == false); + assert(approx(PI, 3.1415926, eps=1e-6) == true); + assert(approx(-PI, -3.141592653589793236) == true); + assert(approx(-PI, -3.1415926) == false); + assert(approx(-PI, -3.1415926, eps=1e-6) == true); + assert(approx(1/3, 0.3333333333) == true); + assert(approx(-1/3, -0.3333333333) == true); + assert(approx(10*[cos(30),sin(30)], 10*[sqrt(3)/2, 1/2]) == true); } test_approx(); 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); + 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); + 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); - assert(posmod(-3,3) == 0); - assert(posmod(-2,3) == 1); - assert(posmod(-1,3) == 2); - assert(posmod(0,3) == 0); - assert(posmod(1,3) == 1); - assert(posmod(2,3) == 2); - assert(posmod(3,3) == 0); + assert(posmod(-5,3) == 1); + assert(posmod(-4,3) == 2); + assert(posmod(-3,3) == 0); + assert(posmod(-2,3) == 1); + assert(posmod(-1,3) == 2); + assert(posmod(0,3) == 0); + assert(posmod(1,3) == 1); + assert(posmod(2,3) == 2); + assert(posmod(3,3) == 0); } test_posmod(); module test_modang() { - assert(modang(-700) == 20); - assert(modang(-270) == 90); - assert(modang(-120) == -120); - assert(modang(120) == 120); - assert(modang(270) == -90); - assert(modang(700) == -20); + assert(modang(-700) == 20); + assert(modang(-270) == 90); + assert(modang(-120) == -120); + assert(modang(120) == 120); + assert(modang(270) == -90); + assert(modang(700) == -20); } test_modang(); module test_modrange() { - assert(modrange(-5,5,3) == [1,2]); - assert(modrange(-1,4,3) == [2,0,1]); - assert(modrange(1,8,10,step=2) == [1,3,5,7]); - assert(modrange(5,12,10,step=2) == [5,7,9,1]); + assert(modrange(-5,5,3) == [1,2]); + assert(modrange(-1,4,3) == [2,0,1]); + assert(modrange(1,8,10,step=2) == [1,3,5,7]); + assert(modrange(5,12,10,step=2) == [5,7,9,1]); } test_modrange(); module test_sqr() { - assert(sqr(-3) == 9); - assert(sqr(0) == 0); - assert(sqr(1) == 1); - assert(sqr(2) == 4); - assert(sqr(3) == 9); - assert(sqr(16) == 256); + assert(sqr(-3) == 9); + assert(sqr(0) == 0); + assert(sqr(1) == 1); + assert(sqr(2) == 4); + assert(sqr(3) == 9); + assert(sqr(16) == 256); } test_sqr(); module test_log2() { - assert(log2(0.125) == -3); - assert(log2(16) == 4); - assert(log2(256) == 8); + assert(log2(0.125) == -3); + assert(log2(16) == 4); + assert(log2(256) == 8); } test_log2(); module test_rand_int() { - nums = rand_int(-100,100,1000,seed=2134); - assert(len(nums)==1000); - for (num = nums) { - assert(num>=-100); - assert(num<=100); - assert(num==floor(num)); - } + nums = rand_int(-100,100,1000,seed=2134); + assert(len(nums)==1000); + for (num = nums) { + assert(num>=-100); + assert(num<=100); + assert(num==floor(num)); + } } test_rand_int(); module test_gaussian_rands() { - nums1 = gaussian_rands(0,10,1000,seed=2132); - nums2 = gaussian_rands(0,10,1000,seed=2130); - nums3 = gaussian_rands(0,10,1000,seed=2132); - assert(len(nums1)==1000); - assert(len(nums2)==1000); - assert(len(nums3)==1000); - assert(nums1==nums3); - assert(nums1!=nums2); + nums1 = gaussian_rands(0,10,1000,seed=2132); + nums2 = gaussian_rands(0,10,1000,seed=2130); + nums3 = gaussian_rands(0,10,1000,seed=2132); + assert(len(nums1)==1000); + assert(len(nums2)==1000); + assert(len(nums3)==1000); + assert(nums1==nums3); + assert(nums1!=nums2); } test_gaussian_rands(); module test_log_rands() { - nums1 = log_rands(0,100,10,1000,seed=2189); - nums2 = log_rands(0,100,10,1000,seed=2310); - nums3 = log_rands(0,100,10,1000,seed=2189); - assert(len(nums1)==1000); - assert(len(nums2)==1000); - assert(len(nums3)==1000); - assert(nums1==nums3); - assert(nums1!=nums2); + nums1 = log_rands(0,100,10,1000,seed=2189); + nums2 = log_rands(0,100,10,1000,seed=2310); + nums3 = log_rands(0,100,10,1000,seed=2189); + assert(len(nums1)==1000); + assert(len(nums2)==1000); + assert(len(nums3)==1000); + assert(nums1==nums3); + assert(nums1!=nums2); } test_log_rands(); module test_segs() { - assert(segs(50,$fn=8) == 8); - assert(segs(50,$fa=2,$fs=2) == 158); + assert(segs(50,$fn=8) == 8); + assert(segs(50,$fa=2,$fs=2) == 158); } test_segs(); module test_lerp() { - assert(lerp(-20,20,0) == -20); - assert(lerp(-20,20,0.25) == -10); - assert(lerp(-20,20,0.5) == 0); - assert(lerp(-20,20,0.75) == 10); - assert(lerp(-20,20,1) == 20); - assert(lerp(-20,20,[0,0.25,0.5,0.75,1]) == [-20,-10,0,10,20]); - assert(lerp(-20,20,[0:0.25:1]) == [-20,-10,0,10,20]); - assert(lerp([10,10],[30,-10],0.5) == [20,0]); + assert(lerp(-20,20,0) == -20); + assert(lerp(-20,20,0.25) == -10); + assert(lerp(-20,20,0.5) == 0); + assert(lerp(-20,20,0.75) == 10); + assert(lerp(-20,20,1) == 20); + assert(lerp(-20,20,[0,0.25,0.5,0.75,1]) == [-20,-10,0,10,20]); + assert(lerp(-20,20,[0:0.25:1]) == [-20,-10,0,10,20]); + assert(lerp([10,10],[30,-10],0.5) == [20,0]); } test_lerp(); module test_hypot() { - assert(hypot(20,30) == norm([20,30])); + assert(hypot(20,30) == norm([20,30])); } test_hypot(); module test_sinh() { - assert(abs(sinh(-2)+3.6268604078) < EPSILON); - assert(abs(sinh(-1)+1.1752011936) < EPSILON); - assert(abs(sinh(0)) < EPSILON); - assert(abs(sinh(1)-1.1752011936) < EPSILON); - assert(abs(sinh(2)-3.6268604078) < EPSILON); + assert(abs(sinh(-2)+3.6268604078) < EPSILON); + assert(abs(sinh(-1)+1.1752011936) < EPSILON); + assert(abs(sinh(0)) < EPSILON); + assert(abs(sinh(1)-1.1752011936) < EPSILON); + assert(abs(sinh(2)-3.6268604078) < EPSILON); } test_sinh(); module test_cosh() { - assert(abs(cosh(-2)-3.7621956911) < EPSILON); - assert(abs(cosh(-1)-1.5430806348) < EPSILON); - assert(abs(cosh(0)-1) < EPSILON); - assert(abs(cosh(1)-1.5430806348) < EPSILON); - assert(abs(cosh(2)-3.7621956911) < EPSILON); + assert(abs(cosh(-2)-3.7621956911) < EPSILON); + assert(abs(cosh(-1)-1.5430806348) < EPSILON); + assert(abs(cosh(0)-1) < EPSILON); + assert(abs(cosh(1)-1.5430806348) < EPSILON); + assert(abs(cosh(2)-3.7621956911) < EPSILON); } test_cosh(); module test_tanh() { - assert(abs(tanh(-2)+0.9640275801) < EPSILON); - assert(abs(tanh(-1)+0.761594156) < EPSILON); - assert(abs(tanh(0)) < EPSILON); - assert(abs(tanh(1)-0.761594156) < EPSILON); - assert(abs(tanh(2)-0.9640275801) < EPSILON); + assert(abs(tanh(-2)+0.9640275801) < EPSILON); + assert(abs(tanh(-1)+0.761594156) < EPSILON); + assert(abs(tanh(0)) < EPSILON); + assert(abs(tanh(1)-0.761594156) < EPSILON); + assert(abs(tanh(2)-0.9640275801) < EPSILON); } test_tanh(); module test_asinh() { - assert(abs(asinh(sinh(-2))+2) < EPSILON); - assert(abs(asinh(sinh(-1))+1) < EPSILON); - assert(abs(asinh(sinh(0))) < EPSILON); - assert(abs(asinh(sinh(1))-1) < EPSILON); - assert(abs(asinh(sinh(2))-2) < EPSILON); + assert(abs(asinh(sinh(-2))+2) < EPSILON); + assert(abs(asinh(sinh(-1))+1) < EPSILON); + assert(abs(asinh(sinh(0))) < EPSILON); + assert(abs(asinh(sinh(1))-1) < EPSILON); + assert(abs(asinh(sinh(2))-2) < EPSILON); } test_asinh(); module test_acosh() { - assert(abs(acosh(cosh(-2))-2) < EPSILON); - assert(abs(acosh(cosh(-1))-1) < EPSILON); - assert(abs(acosh(cosh(0))) < EPSILON); - assert(abs(acosh(cosh(1))-1) < EPSILON); - assert(abs(acosh(cosh(2))-2) < EPSILON); + assert(abs(acosh(cosh(-2))-2) < EPSILON); + assert(abs(acosh(cosh(-1))-1) < EPSILON); + assert(abs(acosh(cosh(0))) < EPSILON); + assert(abs(acosh(cosh(1))-1) < EPSILON); + assert(abs(acosh(cosh(2))-2) < EPSILON); } test_acosh(); module test_atanh() { - assert(abs(atanh(tanh(-2))+2) < EPSILON); - assert(abs(atanh(tanh(-1))+1) < EPSILON); - assert(abs(atanh(tanh(0))) < EPSILON); - assert(abs(atanh(tanh(1))-1) < EPSILON); - assert(abs(atanh(tanh(2))-2) < EPSILON); + assert(abs(atanh(tanh(-2))+2) < EPSILON); + assert(abs(atanh(tanh(-1))+1) < EPSILON); + assert(abs(atanh(tanh(0))) < EPSILON); + assert(abs(atanh(tanh(1))-1) < EPSILON); + assert(abs(atanh(tanh(2))-2) < EPSILON); } test_atanh(); module test_sum() { - assert(sum([]) == 0); - assert(sum([],dflt=undef) == undef); - assert(sum([1,2,3]) == 6); - assert(sum([-2,-1,0,1,2]) == 0); - assert(sum([[1,2,3], [3,4,5], [5,6,7]]) == [9,12,15]); + assert(sum([]) == 0); + assert(sum([],dflt=undef) == undef); + assert(sum([1,2,3]) == 6); + assert(sum([-2,-1,0,1,2]) == 0); + assert(sum([[1,2,3], [3,4,5], [5,6,7]]) == [9,12,15]); } test_sum(); module test_cumsum() { - assert(cumsum([]) == []); - assert(cumsum([1,1,1]) == [1,2,3]); - assert(cumsum([2,2,2]) == [2,4,6]); - assert(cumsum([1,2,3]) == [1,3,6]); - assert(cumsum([-2,-1,0,1,2]) == [-2,-3,-3,-2,0]); - assert(cumsum([[1,2,3], [3,4,5], [5,6,7]]) == [[1,2,3],[4,6,8],[9,12,15]]); + assert(cumsum([]) == []); + assert(cumsum([1,1,1]) == [1,2,3]); + assert(cumsum([2,2,2]) == [2,4,6]); + assert(cumsum([1,2,3]) == [1,3,6]); + assert(cumsum([-2,-1,0,1,2]) == [-2,-3,-3,-2,0]); + assert(cumsum([[1,2,3], [3,4,5], [5,6,7]]) == [[1,2,3],[4,6,8],[9,12,15]]); } test_cumsum(); module test_sum_of_squares() { - assert(sum_of_squares([1,2,3]) == 14); - assert(sum_of_squares([1,2,4]) == 21); - assert(sum_of_squares([-3,-2,-1]) == 14); + assert(sum_of_squares([1,2,3]) == 14); + assert(sum_of_squares([1,2,4]) == 21); + assert(sum_of_squares([-3,-2,-1]) == 14); } test_sum_of_squares(); module test_sum_of_sines() { - assert(sum_of_sines(0, [[3,4,0],[2,2,0]]) == 0); - assert(sum_of_sines(45, [[3,4,0],[2,2,0]]) == 2); - assert(sum_of_sines(90, [[3,4,0],[2,2,0]]) == 0); - assert(sum_of_sines(135, [[3,4,0],[2,2,0]]) == -2); - assert(sum_of_sines(180, [[3,4,0],[2,2,0]]) == 0); + assert(sum_of_sines(0, [[3,4,0],[2,2,0]]) == 0); + assert(sum_of_sines(45, [[3,4,0],[2,2,0]]) == 2); + assert(sum_of_sines(90, [[3,4,0],[2,2,0]]) == 0); + assert(sum_of_sines(135, [[3,4,0],[2,2,0]]) == -2); + assert(sum_of_sines(180, [[3,4,0],[2,2,0]]) == 0); } test_sum_of_sines(); module test_deltas() { - assert(deltas([2,5,9,17]) == [3,4,8]); - assert(deltas([[1,2,3], [3,6,8], [4,8,11]]) == [[2,4,5], [1,2,3]]); + assert(deltas([2,5,9,17]) == [3,4,8]); + assert(deltas([[1,2,3], [3,6,8], [4,8,11]]) == [[2,4,5], [1,2,3]]); } test_deltas(); module test_product() { - assert(product([2,3,4]) == 24); - assert(product([[1,2,3], [3,4,5], [5,6,7]]) == [15, 48, 105]); - m1 = [[2,3,4],[4,5,6],[6,7,8]]; - m2 = [[4,1,2],[3,7,2],[8,7,4]]; - m3 = [[3,7,8],[9,2,4],[5,8,3]]; - assert(product([m1,m2,m3]) == m1*m2*m3); + assert(product([2,3,4]) == 24); + assert(product([[1,2,3], [3,4,5], [5,6,7]]) == [15, 48, 105]); + m1 = [[2,3,4],[4,5,6],[6,7,8]]; + m2 = [[4,1,2],[3,7,2],[8,7,4]]; + m3 = [[3,7,8],[9,2,4],[5,8,3]]; + assert(product([m1,m2,m3]) == m1*m2*m3); } test_product(); module test_mean() { - assert(mean([2,3,4]) == 3); - assert(mean([[1,2,3], [3,4,5], [5,6,7]]) == [3,4,5]); + assert(mean([2,3,4]) == 3); + assert(mean([[1,2,3], [3,4,5], [5,6,7]]) == [3,4,5]); } test_mean(); module test_det2() { - assert(det2([[6,-2], [1,8]]) == 50); - assert(det2([[4,7], [3,2]]) == -13); - assert(det2([[4,3], [3,4]]) == 7); + assert(det2([[6,-2], [1,8]]) == 50); + assert(det2([[4,7], [3,2]]) == -13); + assert(det2([[4,3], [3,4]]) == 7); } test_det2(); module test_det3() { - M = [ [6,4,-2], [1,-2,8], [1,5,7] ]; - assert(det3(M) == -334); + M = [ [6,4,-2], [1,-2,8], [1,5,7] ]; + assert(det3(M) == -334); } test_det3(); module test_determinant() { - M = [ [6,4,-2,9], [1,-2,8,3], [1,5,7,6], [4,2,5,1] ]; - assert(determinant(M) == 2267); + M = [ [6,4,-2,9], [1,-2,8,3], [1,5,7,6], [4,2,5,1] ]; + assert(determinant(M) == 2267); } test_determinant(); @@ -382,167 +382,167 @@ test_determinant(); module test_compare_vals() { - assert(compare_vals(-10,0) < 0); - assert(compare_vals(10,0) > 0); - assert(compare_vals(10,10) == 0); + assert(compare_vals(-10,0) < 0); + assert(compare_vals(10,0) > 0); + assert(compare_vals(10,10) == 0); - assert(compare_vals("abc","abcd") < 0); - assert(compare_vals("abcd","abc") > 0); - assert(compare_vals("abcd","abcd") == 0); + assert(compare_vals("abc","abcd") < 0); + assert(compare_vals("abcd","abc") > 0); + assert(compare_vals("abcd","abcd") == 0); - assert(compare_vals(false,false) == 0); - assert(compare_vals(true,false) > 0); - assert(compare_vals(false,true) < 0); - assert(compare_vals(true,true) == 0); + assert(compare_vals(false,false) == 0); + assert(compare_vals(true,false) > 0); + assert(compare_vals(false,true) < 0); + assert(compare_vals(true,true) == 0); - assert(compare_vals([2,3,4], [2,3,4,5]) < 0); - assert(compare_vals([2,3,4,5], [2,3,4,5]) == 0); - assert(compare_vals([2,3,4,5], [2,3,4]) > 0); - assert(compare_vals([2,3,4,5], [2,3,5,5]) < 0); - assert(compare_vals([[2,3,4,5]], [[2,3,5,5]]) < 0); + assert(compare_vals([2,3,4], [2,3,4,5]) < 0); + assert(compare_vals([2,3,4,5], [2,3,4,5]) == 0); + assert(compare_vals([2,3,4,5], [2,3,4]) > 0); + assert(compare_vals([2,3,4,5], [2,3,5,5]) < 0); + assert(compare_vals([[2,3,4,5]], [[2,3,5,5]]) < 0); - assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5]]) == 0); - assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4,5], [3,4,5]]) < 0); - assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5,6]]) < 0); - assert(compare_vals([[2,3,4,5],[3,4,5]], [[2,3,4], [3,4,5]]) > 0); - assert(compare_vals([[2,3,4],[3,4,5,6]], [[2,3,4], [3,4,5]]) > 0); - assert(compare_vals([[2,3,4],[3,5,5]], [[2,3,4], [3,4,5]]) > 0); - assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4], [3,5,5]]) < 0); + assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5]]) == 0); + assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4,5], [3,4,5]]) < 0); + assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5,6]]) < 0); + assert(compare_vals([[2,3,4,5],[3,4,5]], [[2,3,4], [3,4,5]]) > 0); + assert(compare_vals([[2,3,4],[3,4,5,6]], [[2,3,4], [3,4,5]]) > 0); + assert(compare_vals([[2,3,4],[3,5,5]], [[2,3,4], [3,4,5]]) > 0); + assert(compare_vals([[2,3,4],[3,4,5]], [[2,3,4], [3,5,5]]) < 0); - assert(compare_vals(undef, undef) == 0); - assert(compare_vals(undef, true) < 0); - assert(compare_vals(undef, 0) < 0); - assert(compare_vals(undef, "foo") < 0); - assert(compare_vals(undef, [2,3,4]) < 0); - assert(compare_vals(undef, [0:3]) < 0); + assert(compare_vals(undef, undef) == 0); + assert(compare_vals(undef, true) < 0); + assert(compare_vals(undef, 0) < 0); + assert(compare_vals(undef, "foo") < 0); + assert(compare_vals(undef, [2,3,4]) < 0); + assert(compare_vals(undef, [0:3]) < 0); - assert(compare_vals(true, undef) > 0); - assert(compare_vals(true, true) == 0); - assert(compare_vals(true, 0) < 0); - assert(compare_vals(true, "foo") < 0); - assert(compare_vals(true, [2,3,4]) < 0); - assert(compare_vals(true, [0:3]) < 0); + assert(compare_vals(true, undef) > 0); + assert(compare_vals(true, true) == 0); + assert(compare_vals(true, 0) < 0); + assert(compare_vals(true, "foo") < 0); + assert(compare_vals(true, [2,3,4]) < 0); + assert(compare_vals(true, [0:3]) < 0); - assert(compare_vals(0, undef) > 0); - assert(compare_vals(0, true) > 0); - assert(compare_vals(0, 0) == 0); - assert(compare_vals(0, "foo") < 0); - assert(compare_vals(0, [2,3,4]) < 0); - assert(compare_vals(0, [0:3]) < 0); + assert(compare_vals(0, undef) > 0); + assert(compare_vals(0, true) > 0); + assert(compare_vals(0, 0) == 0); + assert(compare_vals(0, "foo") < 0); + assert(compare_vals(0, [2,3,4]) < 0); + assert(compare_vals(0, [0:3]) < 0); - assert(compare_vals(1, undef) > 0); - assert(compare_vals(1, true) > 0); - assert(compare_vals(1, 1) == 0); - assert(compare_vals(1, "foo") < 0); - assert(compare_vals(1, [2,3,4]) < 0); - assert(compare_vals(1, [0:3]) < 0); + assert(compare_vals(1, undef) > 0); + assert(compare_vals(1, true) > 0); + assert(compare_vals(1, 1) == 0); + assert(compare_vals(1, "foo") < 0); + assert(compare_vals(1, [2,3,4]) < 0); + assert(compare_vals(1, [0:3]) < 0); - assert(compare_vals("foo", undef) > 0); - assert(compare_vals("foo", true) > 0); - assert(compare_vals("foo", 1) > 0); - assert(compare_vals("foo", "foo") == 0); - assert(compare_vals("foo", [2,3,4]) < 0); - assert(compare_vals("foo", [0:3]) < 0); + assert(compare_vals("foo", undef) > 0); + assert(compare_vals("foo", true) > 0); + assert(compare_vals("foo", 1) > 0); + assert(compare_vals("foo", "foo") == 0); + assert(compare_vals("foo", [2,3,4]) < 0); + assert(compare_vals("foo", [0:3]) < 0); - assert(compare_vals([2,3,4], undef) > 0); - assert(compare_vals([2,3,4], true) > 0); - assert(compare_vals([2,3,4], 1) > 0); - assert(compare_vals([2,3,4], "foo") > 0); - assert(compare_vals([2,3,4], [2,3,4]) == 0); - assert(compare_vals([2,3,4], [0:3]) < 0); + assert(compare_vals([2,3,4], undef) > 0); + assert(compare_vals([2,3,4], true) > 0); + assert(compare_vals([2,3,4], 1) > 0); + assert(compare_vals([2,3,4], "foo") > 0); + assert(compare_vals([2,3,4], [2,3,4]) == 0); + assert(compare_vals([2,3,4], [0:3]) < 0); - assert(compare_vals([0:3], undef) > 0); - assert(compare_vals([0:3], true) > 0); - assert(compare_vals([0:3], 1) > 0); - assert(compare_vals([0:3], "foo") > 0); - assert(compare_vals([0:3], [2,3,4]) > 0); - assert(compare_vals([0:3], [0:3]) == 0); + assert(compare_vals([0:3], undef) > 0); + assert(compare_vals([0:3], true) > 0); + assert(compare_vals([0:3], 1) > 0); + assert(compare_vals([0:3], "foo") > 0); + assert(compare_vals([0:3], [2,3,4]) > 0); + assert(compare_vals([0:3], [0:3]) == 0); } test_compare_vals(); module test_compare_lists() { - assert(compare_lists([2,3,4], [2,3,4,5]) < 0); - assert(compare_lists([2,3,4,5], [2,3,4,5]) == 0); - assert(compare_lists([2,3,4,5], [2,3,4]) > 0); - assert(compare_lists([2,3,4,5], [2,3,5,5]) < 0); + assert(compare_lists([2,3,4], [2,3,4,5]) < 0); + assert(compare_lists([2,3,4,5], [2,3,4,5]) == 0); + assert(compare_lists([2,3,4,5], [2,3,4]) > 0); + assert(compare_lists([2,3,4,5], [2,3,5,5]) < 0); - assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5]]) == 0); - assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4,5], [3,4,5]]) < 0); - assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5,6]]) < 0); - assert(compare_lists([[2,3,4,5],[3,4,5]], [[2,3,4], [3,4,5]]) > 0); - assert(compare_lists([[2,3,4],[3,4,5,6]], [[2,3,4], [3,4,5]]) > 0); - assert(compare_lists([[2,3,4],[3,5,5]], [[2,3,4], [3,4,5]]) > 0); - assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4], [3,5,5]]) < 0); + assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5]]) == 0); + assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4,5], [3,4,5]]) < 0); + assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4], [3,4,5,6]]) < 0); + assert(compare_lists([[2,3,4,5],[3,4,5]], [[2,3,4], [3,4,5]]) > 0); + assert(compare_lists([[2,3,4],[3,4,5,6]], [[2,3,4], [3,4,5]]) > 0); + assert(compare_lists([[2,3,4],[3,5,5]], [[2,3,4], [3,4,5]]) > 0); + assert(compare_lists([[2,3,4],[3,4,5]], [[2,3,4], [3,5,5]]) < 0); - assert(compare_lists("cat", "bat") > 0); - assert(compare_lists(["cat"], ["bat"]) > 0); + assert(compare_lists("cat", "bat") > 0); + assert(compare_lists(["cat"], ["bat"]) > 0); } test_compare_lists(); module test_any() { - assert(any([0,false,undef]) == false); - assert(any([1,false,undef]) == true); - assert(any([1,5,true]) == true); - assert(any([[0,0], [0,0]]) == false); - assert(any([[0,0], [1,0]]) == true); + assert(any([0,false,undef]) == false); + assert(any([1,false,undef]) == true); + assert(any([1,5,true]) == true); + assert(any([[0,0], [0,0]]) == false); + assert(any([[0,0], [1,0]]) == true); } test_any(); module test_all() { - assert(all([0,false,undef]) == false); - assert(all([1,false,undef]) == false); - assert(all([1,5,true]) == true); - assert(all([[0,0], [0,0]]) == false); - assert(all([[0,0], [1,0]]) == false); - assert(all([[1,1], [1,1]]) == true); + assert(all([0,false,undef]) == false); + assert(all([1,false,undef]) == false); + assert(all([1,5,true]) == true); + assert(all([[0,0], [0,0]]) == false); + assert(all([[0,0], [1,0]]) == false); + assert(all([[1,1], [1,1]]) == true); } test_all(); module test_count_true() { - assert(count_true([0,false,undef]) == 0); - assert(count_true([1,false,undef]) == 1); - assert(count_true([1,5,false]) == 2); - assert(count_true([1,5,true]) == 3); - assert(count_true([[0,0], [0,0]]) == 0); - assert(count_true([[0,0], [1,0]]) == 1); - assert(count_true([[1,1], [1,1]]) == 4); - assert(count_true([[1,1], [1,1]], nmax=3) == 3); + assert(count_true([0,false,undef]) == 0); + assert(count_true([1,false,undef]) == 1); + assert(count_true([1,5,false]) == 2); + assert(count_true([1,5,true]) == 3); + assert(count_true([[0,0], [0,0]]) == 0); + assert(count_true([[0,0], [1,0]]) == 1); + assert(count_true([[1,1], [1,1]]) == 4); + assert(count_true([[1,1], [1,1]], nmax=3) == 3); } test_count_true(); module test_factorial() { - assert(factorial(1) == 1); - assert(factorial(2) == 2); - assert(factorial(3) == 6); - assert(factorial(4) == 24); - assert(factorial(5) == 120); - assert(factorial(6) == 720); - assert(factorial(7) == 5040); - assert(factorial(8) == 40320); + assert(factorial(1) == 1); + assert(factorial(2) == 2); + assert(factorial(3) == 6); + assert(factorial(4) == 24); + assert(factorial(5) == 120); + assert(factorial(6) == 720); + assert(factorial(7) == 5040); + assert(factorial(8) == 40320); } test_factorial(); module test_gcd() { - assert(gcd(15,25) == 5); - assert(gcd(15,27) == 3); - assert(gcd(270,405) == 135); + assert(gcd(15,25) == 5); + assert(gcd(15,27) == 3); + assert(gcd(270,405) == 135); } test_gcd(); module test_lcm() { - assert(lcm(15,25) == 75); - assert(lcm(15,27) == 135); - assert(lcm(270,405) == 810); + assert(lcm(15,25) == 75); + assert(lcm(15,27) == 135); + assert(lcm(270,405) == 810); } test_lcm(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_primitives.scad b/tests/test_primitives.scad index 2a15d25..c80ccf7 100644 --- a/tests/test_primitives.scad +++ b/tests/test_primitives.scad @@ -2,27 +2,27 @@ include module test_square() { - assert(square(100, center=true) == [[50,-50],[-50,-50],[-50,50],[50,50]]); - assert(square(100, center=false) == [[100,0],[0,0],[0,100],[100,100]]); - assert(square(100, anchor=FWD+LEFT) == [[100,0],[0,0],[0,100],[100,100]]); - assert(square(100, anchor=BACK+RIGHT) == [[0,-100],[-100,-100],[-100,0],[0,0]]); + assert(square(100, center=true) == [[50,-50],[-50,-50],[-50,50],[50,50]]); + assert(square(100, center=false) == [[100,0],[0,0],[0,100],[100,100]]); + assert(square(100, anchor=FWD+LEFT) == [[100,0],[0,0],[0,100],[100,100]]); + assert(square(100, anchor=BACK+RIGHT) == [[0,-100],[-100,-100],[-100,0],[0,0]]); } test_square(); module test_circle() { - for (pt = circle(d=200)) { - assert(approx(norm(pt),100)); - } - for (pt = circle(r=100)) { - assert(approx(norm(pt),100)); - } - assert(polygon_is_clockwise(circle(d=200))); - assert(polygon_is_clockwise(circle(r=100))); - assert(len(circle(d=100,$fn=6)) == 6); - assert(len(circle(d=100,$fn=36)) == 36); + for (pt = circle(d=200)) { + assert(approx(norm(pt),100)); + } + for (pt = circle(r=100)) { + assert(approx(norm(pt),100)); + } + assert(polygon_is_clockwise(circle(d=200))); + assert(polygon_is_clockwise(circle(r=100))); + assert(len(circle(d=100,$fn=6)) == 6); + assert(len(circle(d=100,$fn=36)) == 36); } test_circle(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_quaternions.scad b/tests/test_quaternions.scad index 4aeb08c..44eb85e 100644 --- a/tests/test_quaternions.scad +++ b/tests/test_quaternions.scad @@ -3,283 +3,283 @@ include function rec_cmp(a,b,eps=1e-9) = - typeof(a)!=typeof(b)? false : - is_num(a)? approx(a,b,eps=eps) : - is_list(a)? len(a)==len(b) && all([for (i=idx(a)) rec_cmp(a[i],b[i],eps=eps)]) : - a == b; + typeof(a)!=typeof(b)? false : + is_num(a)? approx(a,b,eps=eps) : + is_list(a)? len(a)==len(b) && all([for (i=idx(a)) rec_cmp(a[i],b[i],eps=eps)]) : + a == b; module verify_f(actual,expected) { - if (!rec_cmp(actual,expected)) { - echo(str("Expected: ",fmt_float(expected,10))); - echo(str(" : ",expected)); - echo(str("Actual : ",fmt_float(actual,10))); - echo(str(" : ",actual)); - echo(str("Delta : ",fmt_float(expected-actual,10))); - echo(str(" : ",expected-actual)); - assert(approx(expected,actual)); - } + if (!rec_cmp(actual,expected)) { + echo(str("Expected: ",fmt_float(expected,10))); + echo(str(" : ",expected)); + echo(str("Actual : ",fmt_float(actual,10))); + echo(str(" : ",actual)); + echo(str("Delta : ",fmt_float(expected-actual,10))); + echo(str(" : ",expected-actual)); + assert(approx(expected,actual)); + } } module test_Quat() { - verify_f(Quat(UP,0),[0,0,0,1]); - verify_f(Quat(FWD,0),[0,0,0,1]); - verify_f(Quat(LEFT,0),[0,0,0,1]); - verify_f(Quat(UP,45),[0,0,0.3826834324,0.9238795325]); - verify_f(Quat(LEFT,45),[-0.3826834324, 0, 0, 0.9238795325]); - verify_f(Quat(BACK,45),[0,0.3826834323,0,0.9238795325]); - verify_f(Quat(FWD+RIGHT,30),[0.1830127019, -0.1830127019, 0, 0.9659258263]); + verify_f(Quat(UP,0),[0,0,0,1]); + verify_f(Quat(FWD,0),[0,0,0,1]); + verify_f(Quat(LEFT,0),[0,0,0,1]); + verify_f(Quat(UP,45),[0,0,0.3826834324,0.9238795325]); + verify_f(Quat(LEFT,45),[-0.3826834324, 0, 0, 0.9238795325]); + verify_f(Quat(BACK,45),[0,0.3826834323,0,0.9238795325]); + verify_f(Quat(FWD+RIGHT,30),[0.1830127019, -0.1830127019, 0, 0.9659258263]); } test_Quat(); module test_QuatX() { - verify_f(QuatX(0),[0,0,0,1]); - verify_f(QuatX(35),[0.3007057995,0,0,0.9537169507]); - verify_f(QuatX(45),[0.3826834324,0,0,0.9238795325]); + verify_f(QuatX(0),[0,0,0,1]); + verify_f(QuatX(35),[0.3007057995,0,0,0.9537169507]); + verify_f(QuatX(45),[0.3826834324,0,0,0.9238795325]); } test_QuatX(); module test_QuatY() { - verify_f(QuatY(0),[0,0,0,1]); - verify_f(QuatY(35),[0,0.3007057995,0,0.9537169507]); - verify_f(QuatY(45),[0,0.3826834323,0,0.9238795325]); + verify_f(QuatY(0),[0,0,0,1]); + verify_f(QuatY(35),[0,0.3007057995,0,0.9537169507]); + verify_f(QuatY(45),[0,0.3826834323,0,0.9238795325]); } test_QuatY(); module test_QuatZ() { - verify_f(QuatZ(0),[0,0,0,1]); - verify_f(QuatZ(36),[0,0,0.3090169944,0.9510565163]); - verify_f(QuatZ(45),[0,0,0.3826834324,0.9238795325]); + verify_f(QuatZ(0),[0,0,0,1]); + verify_f(QuatZ(36),[0,0,0.3090169944,0.9510565163]); + verify_f(QuatZ(45),[0,0,0.3826834324,0.9238795325]); } test_QuatZ(); module test_QuatXYZ() { - verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); - verify_f(QuatXYZ([30,0,0]), [0.2588190451, 0, 0, 0.9659258263]); - verify_f(QuatXYZ([90,0,0]), [0.7071067812, 0, 0, 0.7071067812]); - verify_f(QuatXYZ([-270,0,0]), [-0.7071067812, 0, 0, -0.7071067812]); - verify_f(QuatXYZ([180,0,0]), [1,0,0,0]); - verify_f(QuatXYZ([270,0,0]), [0.7071067812, 0, 0, -0.7071067812]); - verify_f(QuatXYZ([-90,0,0]), [-0.7071067812, 0, 0, 0.7071067812]); - verify_f(QuatXYZ([360,0,0]), [0,0,0,-1]); + verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); + verify_f(QuatXYZ([30,0,0]), [0.2588190451, 0, 0, 0.9659258263]); + verify_f(QuatXYZ([90,0,0]), [0.7071067812, 0, 0, 0.7071067812]); + verify_f(QuatXYZ([-270,0,0]), [-0.7071067812, 0, 0, -0.7071067812]); + verify_f(QuatXYZ([180,0,0]), [1,0,0,0]); + verify_f(QuatXYZ([270,0,0]), [0.7071067812, 0, 0, -0.7071067812]); + verify_f(QuatXYZ([-90,0,0]), [-0.7071067812, 0, 0, 0.7071067812]); + verify_f(QuatXYZ([360,0,0]), [0,0,0,-1]); - verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); - verify_f(QuatXYZ([0,30,0]), [0, 0.2588190451, 0, 0.9659258263]); - verify_f(QuatXYZ([0,90,0]), [0, 0.7071067812, 0, 0.7071067812]); - verify_f(QuatXYZ([0,-270,0]), [0, -0.7071067812, 0, -0.7071067812]); - verify_f(QuatXYZ([0,180,0]), [0,1,0,0]); - verify_f(QuatXYZ([0,270,0]), [0, 0.7071067812, 0, -0.7071067812]); - verify_f(QuatXYZ([0,-90,0]), [0, -0.7071067812, 0, 0.7071067812]); - verify_f(QuatXYZ([0,360,0]), [0,0,0,-1]); + verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); + verify_f(QuatXYZ([0,30,0]), [0, 0.2588190451, 0, 0.9659258263]); + verify_f(QuatXYZ([0,90,0]), [0, 0.7071067812, 0, 0.7071067812]); + verify_f(QuatXYZ([0,-270,0]), [0, -0.7071067812, 0, -0.7071067812]); + verify_f(QuatXYZ([0,180,0]), [0,1,0,0]); + verify_f(QuatXYZ([0,270,0]), [0, 0.7071067812, 0, -0.7071067812]); + verify_f(QuatXYZ([0,-90,0]), [0, -0.7071067812, 0, 0.7071067812]); + verify_f(QuatXYZ([0,360,0]), [0,0,0,-1]); - verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); - verify_f(QuatXYZ([0,0,30]), [0, 0, 0.2588190451, 0.9659258263]); - verify_f(QuatXYZ([0,0,90]), [0, 0, 0.7071067812, 0.7071067812]); - verify_f(QuatXYZ([0,0,-270]), [0, 0, -0.7071067812, -0.7071067812]); - verify_f(QuatXYZ([0,0,180]), [0,0,1,0]); - verify_f(QuatXYZ([0,0,270]), [0, 0, 0.7071067812, -0.7071067812]); - verify_f(QuatXYZ([0,0,-90]), [0, 0, -0.7071067812, 0.7071067812]); - verify_f(QuatXYZ([0,0,360]), [0,0,0,-1]); + verify_f(QuatXYZ([0,0,0]), [0,0,0,1]); + verify_f(QuatXYZ([0,0,30]), [0, 0, 0.2588190451, 0.9659258263]); + verify_f(QuatXYZ([0,0,90]), [0, 0, 0.7071067812, 0.7071067812]); + verify_f(QuatXYZ([0,0,-270]), [0, 0, -0.7071067812, -0.7071067812]); + verify_f(QuatXYZ([0,0,180]), [0,0,1,0]); + verify_f(QuatXYZ([0,0,270]), [0, 0, 0.7071067812, -0.7071067812]); + verify_f(QuatXYZ([0,0,-90]), [0, 0, -0.7071067812, 0.7071067812]); + verify_f(QuatXYZ([0,0,360]), [0,0,0,-1]); - verify_f(QuatXYZ([30,30,30]), [0.1767766953, 0.3061862178, 0.1767766953, 0.9185586535]); - verify_f(QuatXYZ([12,34,56]), [-0.04824789229, 0.3036636044, 0.4195145429, 0.8540890495]); + verify_f(QuatXYZ([30,30,30]), [0.1767766953, 0.3061862178, 0.1767766953, 0.9185586535]); + verify_f(QuatXYZ([12,34,56]), [-0.04824789229, 0.3036636044, 0.4195145429, 0.8540890495]); } test_QuatXYZ(); module test_Q_Ident() { - verify_f(Q_Ident(), [0,0,0,1]); + verify_f(Q_Ident(), [0,0,0,1]); } test_Q_Ident(); module test_Q_Add_S() { - verify_f(Q_Add_S([0,0,0,1],3),[0,0,0,4]); - verify_f(Q_Add_S([0,0,1,0],3),[0,0,1,3]); - verify_f(Q_Add_S([0,1,0,0],3),[0,1,0,3]); - verify_f(Q_Add_S([1,0,0,0],3),[1,0,0,3]); - verify_f(Q_Add_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, 1.979924705]); + verify_f(Q_Add_S([0,0,0,1],3),[0,0,0,4]); + verify_f(Q_Add_S([0,0,1,0],3),[0,0,1,3]); + verify_f(Q_Add_S([0,1,0,0],3),[0,1,0,3]); + verify_f(Q_Add_S([1,0,0,0],3),[1,0,0,3]); + verify_f(Q_Add_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, 1.979924705]); } test_Q_Add_S(); module test_Q_Sub_S() { - verify_f(Q_Sub_S([0,0,0,1],3),[0,0,0,-2]); - verify_f(Q_Sub_S([0,0,1,0],3),[0,0,1,-3]); - verify_f(Q_Sub_S([0,1,0,0],3),[0,1,0,-3]); - verify_f(Q_Sub_S([1,0,0,0],3),[1,0,0,-3]); - verify_f(Q_Sub_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, -0.02007529538]); + verify_f(Q_Sub_S([0,0,0,1],3),[0,0,0,-2]); + verify_f(Q_Sub_S([0,0,1,0],3),[0,0,1,-3]); + verify_f(Q_Sub_S([0,1,0,0],3),[0,1,0,-3]); + verify_f(Q_Sub_S([1,0,0,0],3),[1,0,0,-3]); + verify_f(Q_Sub_S(Quat(LEFT+FWD,23),1),[-0.1409744184, -0.1409744184, 0, -0.02007529538]); } test_Q_Sub_S(); module test_Q_Mul_S() { - verify_f(Q_Mul_S([0,0,0,1],3),[0,0,0,3]); - verify_f(Q_Mul_S([0,0,1,0],3),[0,0,3,0]); - verify_f(Q_Mul_S([0,1,0,0],3),[0,3,0,0]); - verify_f(Q_Mul_S([1,0,0,0],3),[3,0,0,0]); - verify_f(Q_Mul_S([1,0,0,1],3),[3,0,0,3]); - verify_f(Q_Mul_S(Quat(LEFT+FWD,23),4),[-0.5638976735, -0.5638976735, 0, 3.919698818]); + verify_f(Q_Mul_S([0,0,0,1],3),[0,0,0,3]); + verify_f(Q_Mul_S([0,0,1,0],3),[0,0,3,0]); + verify_f(Q_Mul_S([0,1,0,0],3),[0,3,0,0]); + verify_f(Q_Mul_S([1,0,0,0],3),[3,0,0,0]); + verify_f(Q_Mul_S([1,0,0,1],3),[3,0,0,3]); + verify_f(Q_Mul_S(Quat(LEFT+FWD,23),4),[-0.5638976735, -0.5638976735, 0, 3.919698818]); } test_Q_Mul_S(); module test_Q_Div_S() { - verify_f(Q_Div_S([0,0,0,1],3),[0,0,0,1/3]); - verify_f(Q_Div_S([0,0,1,0],3),[0,0,1/3,0]); - verify_f(Q_Div_S([0,1,0,0],3),[0,1/3,0,0]); - verify_f(Q_Div_S([1,0,0,0],3),[1/3,0,0,0]); - verify_f(Q_Div_S([1,0,0,1],3),[1/3,0,0,1/3]); - verify_f(Q_Div_S(Quat(LEFT+FWD,23),4),[-0.03524360459, -0.03524360459, 0, 0.2449811762]); + verify_f(Q_Div_S([0,0,0,1],3),[0,0,0,1/3]); + verify_f(Q_Div_S([0,0,1,0],3),[0,0,1/3,0]); + verify_f(Q_Div_S([0,1,0,0],3),[0,1/3,0,0]); + verify_f(Q_Div_S([1,0,0,0],3),[1/3,0,0,0]); + verify_f(Q_Div_S([1,0,0,1],3),[1/3,0,0,1/3]); + verify_f(Q_Div_S(Quat(LEFT+FWD,23),4),[-0.03524360459, -0.03524360459, 0, 0.2449811762]); } test_Q_Div_S(); module test_Q_Add() { - verify_f(Q_Add([2,3,4,5],[-1,-1,-1,-1]),[1,2,3,4]); - verify_f(Q_Add([2,3,4,5],[-3,-3,-3,-3]),[-1,0,1,2]); - verify_f(Q_Add([2,3,4,5],[0,0,0,0]),[2,3,4,5]); - verify_f(Q_Add([2,3,4,5],[1,1,1,1]),[3,4,5,6]); - verify_f(Q_Add([2,3,4,5],[1,0,0,0]),[3,3,4,5]); - verify_f(Q_Add([2,3,4,5],[0,1,0,0]),[2,4,4,5]); - verify_f(Q_Add([2,3,4,5],[0,0,1,0]),[2,3,5,5]); - verify_f(Q_Add([2,3,4,5],[0,0,0,1]),[2,3,4,6]); - verify_f(Q_Add([2,3,4,5],[2,1,2,1]),[4,4,6,6]); - verify_f(Q_Add([2,3,4,5],[1,2,1,2]),[3,5,5,7]); + verify_f(Q_Add([2,3,4,5],[-1,-1,-1,-1]),[1,2,3,4]); + verify_f(Q_Add([2,3,4,5],[-3,-3,-3,-3]),[-1,0,1,2]); + verify_f(Q_Add([2,3,4,5],[0,0,0,0]),[2,3,4,5]); + verify_f(Q_Add([2,3,4,5],[1,1,1,1]),[3,4,5,6]); + verify_f(Q_Add([2,3,4,5],[1,0,0,0]),[3,3,4,5]); + verify_f(Q_Add([2,3,4,5],[0,1,0,0]),[2,4,4,5]); + verify_f(Q_Add([2,3,4,5],[0,0,1,0]),[2,3,5,5]); + verify_f(Q_Add([2,3,4,5],[0,0,0,1]),[2,3,4,6]); + verify_f(Q_Add([2,3,4,5],[2,1,2,1]),[4,4,6,6]); + verify_f(Q_Add([2,3,4,5],[1,2,1,2]),[3,5,5,7]); } test_Q_Add(); module test_Q_Sub() { - verify_f(Q_Sub([2,3,4,5],[-1,-1,-1,-1]),[3,4,5,6]); - verify_f(Q_Sub([2,3,4,5],[-3,-3,-3,-3]),[5,6,7,8]); - verify_f(Q_Sub([2,3,4,5],[0,0,0,0]),[2,3,4,5]); - verify_f(Q_Sub([2,3,4,5],[1,1,1,1]),[1,2,3,4]); - verify_f(Q_Sub([2,3,4,5],[1,0,0,0]),[1,3,4,5]); - verify_f(Q_Sub([2,3,4,5],[0,1,0,0]),[2,2,4,5]); - verify_f(Q_Sub([2,3,4,5],[0,0,1,0]),[2,3,3,5]); - verify_f(Q_Sub([2,3,4,5],[0,0,0,1]),[2,3,4,4]); - verify_f(Q_Sub([2,3,4,5],[2,1,2,1]),[0,2,2,4]); - verify_f(Q_Sub([2,3,4,5],[1,2,1,2]),[1,1,3,3]); + verify_f(Q_Sub([2,3,4,5],[-1,-1,-1,-1]),[3,4,5,6]); + verify_f(Q_Sub([2,3,4,5],[-3,-3,-3,-3]),[5,6,7,8]); + verify_f(Q_Sub([2,3,4,5],[0,0,0,0]),[2,3,4,5]); + verify_f(Q_Sub([2,3,4,5],[1,1,1,1]),[1,2,3,4]); + verify_f(Q_Sub([2,3,4,5],[1,0,0,0]),[1,3,4,5]); + verify_f(Q_Sub([2,3,4,5],[0,1,0,0]),[2,2,4,5]); + verify_f(Q_Sub([2,3,4,5],[0,0,1,0]),[2,3,3,5]); + verify_f(Q_Sub([2,3,4,5],[0,0,0,1]),[2,3,4,4]); + verify_f(Q_Sub([2,3,4,5],[2,1,2,1]),[0,2,2,4]); + verify_f(Q_Sub([2,3,4,5],[1,2,1,2]),[1,1,3,3]); } test_Q_Sub(); module test_Q_Mul() { - verify_f(Q_Mul(QuatZ(30),QuatX(57)),[0.4608999698, 0.1234977747, 0.2274546059, 0.8488721457]); - verify_f(Q_Mul(QuatY(30),QuatZ(23)),[0.05160021841, 0.2536231763, 0.1925746368, 0.94653458]); + verify_f(Q_Mul(QuatZ(30),QuatX(57)),[0.4608999698, 0.1234977747, 0.2274546059, 0.8488721457]); + verify_f(Q_Mul(QuatY(30),QuatZ(23)),[0.05160021841, 0.2536231763, 0.1925746368, 0.94653458]); } test_Q_Mul(); module test_Q_Dot() { - verify_f(Q_Dot(QuatZ(30),QuatX(57)),0.8488721457); - verify_f(Q_Dot(QuatY(30),QuatZ(23)),0.94653458); + verify_f(Q_Dot(QuatZ(30),QuatX(57)),0.8488721457); + verify_f(Q_Dot(QuatY(30),QuatZ(23)),0.94653458); } test_Q_Dot(); module test_Q_Neg() { - verify_f(Q_Neg([1,0,0,1]),[-1,0,0,-1]); - verify_f(Q_Neg([0,1,1,0]),[0,-1,-1,0]); - verify_f(Q_Neg(QuatXYZ([23,45,67])),[0.0533818345,-0.4143703268,-0.4360652669,-0.7970537592]); + verify_f(Q_Neg([1,0,0,1]),[-1,0,0,-1]); + verify_f(Q_Neg([0,1,1,0]),[0,-1,-1,0]); + verify_f(Q_Neg(QuatXYZ([23,45,67])),[0.0533818345,-0.4143703268,-0.4360652669,-0.7970537592]); } test_Q_Neg(); module test_Q_Conj() { - verify_f(Q_Conj([1,0,0,1]),[-1,0,0,1]); - verify_f(Q_Conj([0,1,1,0]),[0,-1,-1,0]); - verify_f(Q_Conj(QuatXYZ([23,45,67])),[0.0533818345, -0.4143703268, -0.4360652669, 0.7970537592]); + verify_f(Q_Conj([1,0,0,1]),[-1,0,0,1]); + verify_f(Q_Conj([0,1,1,0]),[0,-1,-1,0]); + verify_f(Q_Conj(QuatXYZ([23,45,67])),[0.0533818345, -0.4143703268, -0.4360652669, 0.7970537592]); } test_Q_Conj(); module test_Q_Norm() { - verify_f(Q_Norm([1,0,0,1]),1.414213562); - verify_f(Q_Norm([0,1,1,0]),1.414213562); - verify_f(Q_Norm(QuatXYZ([23,45,67])),1); + verify_f(Q_Norm([1,0,0,1]),1.414213562); + verify_f(Q_Norm([0,1,1,0]),1.414213562); + verify_f(Q_Norm(QuatXYZ([23,45,67])),1); } test_Q_Norm(); module test_Q_Normalize() { - verify_f(Q_Normalize([1,0,0,1]),[0.7071067812, 0, 0, 0.7071067812]); - verify_f(Q_Normalize([0,1,1,0]),[0, 0.7071067812, 0.7071067812, 0]); - verify_f(Q_Normalize(QuatXYZ([23,45,67])),[-0.0533818345, 0.4143703268, 0.4360652669, 0.7970537592]); + verify_f(Q_Normalize([1,0,0,1]),[0.7071067812, 0, 0, 0.7071067812]); + verify_f(Q_Normalize([0,1,1,0]),[0, 0.7071067812, 0.7071067812, 0]); + verify_f(Q_Normalize(QuatXYZ([23,45,67])),[-0.0533818345, 0.4143703268, 0.4360652669, 0.7970537592]); } test_Q_Normalize(); module test_Q_Dist() { - verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([23,45,67])),0); - verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([12,34,56])),0.1257349854); + verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([23,45,67])),0); + verify_f(Q_Dist(QuatXYZ([23,45,67]),QuatXYZ([12,34,56])),0.1257349854); } test_Q_Dist(); module test_Q_Slerp() { - verify_f(Q_Slerp(QuatX(45),QuatY(30),0.0),QuatX(45)); - verify_f(Q_Slerp(QuatX(45),QuatY(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]); - verify_f(Q_Slerp(QuatX(45),QuatY(30),1.0),QuatY(30)); + verify_f(Q_Slerp(QuatX(45),QuatY(30),0.0),QuatX(45)); + verify_f(Q_Slerp(QuatX(45),QuatY(30),0.5),[0.1967063121, 0.1330377423, 0, 0.9713946602]); + verify_f(Q_Slerp(QuatX(45),QuatY(30),1.0),QuatY(30)); } test_Q_Slerp(); module test_Q_Matrix3() { - verify_f(Q_Matrix3(QuatZ(37)),rot(37,planar=true)); - verify_f(Q_Matrix3(QuatZ(-49)),rot(-49,planar=true)); + verify_f(Q_Matrix3(QuatZ(37)),rot(37,planar=true)); + verify_f(Q_Matrix3(QuatZ(-49)),rot(-49,planar=true)); } test_Q_Matrix3(); module test_Q_Matrix4() { - verify_f(Q_Matrix4(QuatZ(37)),rot(37)); - verify_f(Q_Matrix4(QuatZ(-49)),rot(-49)); - verify_f(Q_Matrix4(QuatX(37)),rot([37,0,0])); - verify_f(Q_Matrix4(QuatY(37)),rot([0,37,0])); - verify_f(Q_Matrix4(QuatXYZ([12,34,56])),rot([12,34,56])); + verify_f(Q_Matrix4(QuatZ(37)),rot(37)); + verify_f(Q_Matrix4(QuatZ(-49)),rot(-49)); + verify_f(Q_Matrix4(QuatX(37)),rot([37,0,0])); + verify_f(Q_Matrix4(QuatY(37)),rot([0,37,0])); + verify_f(Q_Matrix4(QuatXYZ([12,34,56])),rot([12,34,56])); } test_Q_Matrix4(); module test_Q_Axis() { - verify_f(Q_Axis(QuatX(37)),RIGHT); - verify_f(Q_Axis(QuatX(-37)),LEFT); - verify_f(Q_Axis(QuatY(37)),BACK); - verify_f(Q_Axis(QuatY(-37)),FWD); - verify_f(Q_Axis(QuatZ(37)),UP); - verify_f(Q_Axis(QuatZ(-37)),DOWN); + verify_f(Q_Axis(QuatX(37)),RIGHT); + verify_f(Q_Axis(QuatX(-37)),LEFT); + verify_f(Q_Axis(QuatY(37)),BACK); + verify_f(Q_Axis(QuatY(-37)),FWD); + verify_f(Q_Axis(QuatZ(37)),UP); + verify_f(Q_Axis(QuatZ(-37)),DOWN); } test_Q_Axis(); module test_Q_Angle() { - verify_f(Q_Angle(QuatX(0)),0); - verify_f(Q_Angle(QuatY(0)),0); - verify_f(Q_Angle(QuatZ(0)),0); - verify_f(Q_Angle(QuatX(37)),37); - verify_f(Q_Angle(QuatX(-37)),37); - verify_f(Q_Angle(QuatY(37)),37); - verify_f(Q_Angle(QuatY(-37)),37); - verify_f(Q_Angle(QuatZ(37)),37); - verify_f(Q_Angle(QuatZ(-37)),37); + verify_f(Q_Angle(QuatX(0)),0); + verify_f(Q_Angle(QuatY(0)),0); + verify_f(Q_Angle(QuatZ(0)),0); + verify_f(Q_Angle(QuatX(37)),37); + verify_f(Q_Angle(QuatX(-37)),37); + verify_f(Q_Angle(QuatY(37)),37); + verify_f(Q_Angle(QuatY(-37)),37); + verify_f(Q_Angle(QuatZ(37)),37); + verify_f(Q_Angle(QuatZ(-37)),37); } test_Q_Angle(); module test_Qrot() { - verify_f(Qrot(QuatXYZ([12,34,56])),rot([12,34,56])); - verify_f(Qrot(QuatXYZ([12,34,56]),p=[2,3,4]),rot([12,34,56],p=[2,3,4])); - verify_f(Qrot(QuatXYZ([12,34,56]),p=[[2,3,4],[4,9,6]]),rot([12,34,56],p=[[2,3,4],[4,9,6]])); + verify_f(Qrot(QuatXYZ([12,34,56])),rot([12,34,56])); + verify_f(Qrot(QuatXYZ([12,34,56]),p=[2,3,4]),rot([12,34,56],p=[2,3,4])); + verify_f(Qrot(QuatXYZ([12,34,56]),p=[[2,3,4],[4,9,6]]),rot([12,34,56],p=[[2,3,4],[4,9,6]])); } test_Qrot(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_queues.scad b/tests/test_queues.scad index bda43cb..cf45eef 100644 --- a/tests/test_queues.scad +++ b/tests/test_queues.scad @@ -3,75 +3,75 @@ include module test_queue_init() { - assert(queue_init()==[]); + assert(queue_init()==[]); } test_queue_init(); module test_queue_empty() { - assert(queue_empty([])); - assert(!queue_empty([3])); - assert(!queue_empty([2,4,8])); + assert(queue_empty([])); + assert(!queue_empty([3])); + assert(!queue_empty([2,4,8])); } test_queue_empty(); module test_queue_size() { - assert(queue_size([]) == 0); - assert(queue_size([3]) == 1); - assert(queue_size([2,4,8]) == 3); + assert(queue_size([]) == 0); + assert(queue_size([3]) == 1); + assert(queue_size([2,4,8]) == 3); } test_queue_size(); module test_queue_head() { - assert(queue_head([]) == undef); - assert(queue_head([3,5,7,9]) == 3); - assert(queue_head([3,5,7,9], 3) == [3,5,7]); + assert(queue_head([]) == undef); + assert(queue_head([3,5,7,9]) == 3); + assert(queue_head([3,5,7,9], 3) == [3,5,7]); } test_queue_head(); module test_queue_tail() { - assert(queue_tail([]) == undef); - assert(queue_tail([3,5,7,9]) == 9); - assert(queue_tail([3,5,7,9], 3) == [5,7,9]); + assert(queue_tail([]) == undef); + assert(queue_tail([3,5,7,9]) == 9); + assert(queue_tail([3,5,7,9], 3) == [5,7,9]); } test_queue_tail(); module test_queue_peek() { - q = [8,5,4,3,2,3,7]; - assert(queue_peek(q,0) == 8); - assert(queue_peek(q,2) == 4); - assert(queue_peek(q,2,1) == [4]); - assert(queue_peek(q,2,3) == [4,3,2]); + q = [8,5,4,3,2,3,7]; + assert(queue_peek(q,0) == 8); + assert(queue_peek(q,2) == 4); + assert(queue_peek(q,2,1) == [4]); + assert(queue_peek(q,2,3) == [4,3,2]); } test_queue_peek(); module test_queue_add() { - q1 = queue_init(); - q2 = queue_add(q1, "Foo"); - assert(q2==["Foo"]); - q3 = queue_add(q2, "Bar"); - assert(q3==["Foo","Bar"]); - q4 = queue_add(q3, "Baz"); - assert(q4==["Foo","Bar","Baz"]); + q1 = queue_init(); + q2 = queue_add(q1, "Foo"); + assert(q2==["Foo"]); + q3 = queue_add(q2, "Bar"); + assert(q3==["Foo","Bar"]); + q4 = queue_add(q3, "Baz"); + assert(q4==["Foo","Bar","Baz"]); } test_queue_add(); module test_queue_pop() { - q = ["Foo", "Bar", "Baz", "Qux"]; - q1 = queue_pop(q); - assert(q1 == ["Bar", "Baz", "Qux"]); - q2 = queue_pop(q,2); - assert(q2 == ["Baz", "Qux"]); - q3 = queue_pop(q,3); - assert(q3 == ["Qux"]); + q = ["Foo", "Bar", "Baz", "Qux"]; + q1 = queue_pop(q); + assert(q1 == ["Bar", "Baz", "Qux"]); + q2 = queue_pop(q,2); + assert(q2 == ["Baz", "Qux"]); + q3 = queue_pop(q,3); + assert(q3 == ["Qux"]); } test_queue_pop(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_skin.scad b/tests/test_skin.scad index 767ffb4..a799240 100644 --- a/tests/test_skin.scad +++ b/tests/test_skin.scad @@ -3,17 +3,17 @@ include module test_skin() { - profiles = [ - [[-100,-100,0], [0,100,0], [100,-100,0]], - [[-100,-100,100], [-100,100,100], [100,100,100], [100,-100,100]], - ]; - vnf1 = skin(profiles, caps=false, method="distance"); - assert(vnf1 == [[[-100,-100,0],[-100,100,100],[-100,-100,100],[0,100,0],[100,100,100],[100,-100,0],[100,-100,100]],[[0,1,2],[0,3,1],[3,4,1],[3,5,4],[5,6,4],[5,2,6],[5,0,2]]]); - vnf2 = skin(profiles, caps=true, method="distance"); - assert(vnf2 == [[[-100,-100,0],[-100,100,100],[-100,-100,100],[0,100,0],[100,100,100],[100,-100,0],[100,-100,100],[100,-100,0],[0,100,0],[-100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[0,1,2],[0,3,1],[3,4,1],[3,5,4],[5,6,4],[5,2,6],[5,0,2],[7,8,9],[10,11,12],[12,13,10]]]); - vnf_polyhedron(vnf2); + profiles = [ + [[-100,-100,0], [0,100,0], [100,-100,0]], + [[-100,-100,100], [-100,100,100], [100,100,100], [100,-100,100]], + ]; + vnf1 = skin(profiles, caps=false, method="distance"); + assert(vnf1 == [[[-100,-100,0],[-100,100,100],[-100,-100,100],[0,100,0],[100,100,100],[100,-100,0],[100,-100,100]],[[0,1,2],[0,3,1],[3,4,1],[3,5,4],[5,6,4],[5,2,6],[5,0,2]]]); + vnf2 = skin(profiles, caps=true, method="distance"); + assert(vnf2 == [[[-100,-100,0],[-100,100,100],[-100,-100,100],[0,100,0],[100,100,100],[100,-100,0],[100,-100,100],[100,-100,0],[0,100,0],[-100,-100,0],[-100,-100,100],[-100,100,100],[100,100,100],[100,-100,100]],[[0,1,2],[0,3,1],[3,4,1],[3,5,4],[5,6,4],[5,2,6],[5,0,2],[7,8,9],[10,11,12],[12,13,10]]]); + vnf_polyhedron(vnf2); } test_skin(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_stacks.scad b/tests/test_stacks.scad index e261093..3f3f0d2 100644 --- a/tests/test_stacks.scad +++ b/tests/test_stacks.scad @@ -3,77 +3,77 @@ include module test_stack_init() { - assert(stack_init()==[]); + assert(stack_init()==[]); } test_stack_init(); module test_stack_empty() { - assert(stack_empty([])); - assert(!stack_empty([3])); - assert(!stack_empty([2,4,8])); + assert(stack_empty([])); + assert(!stack_empty([3])); + assert(!stack_empty([2,4,8])); } test_stack_empty(); module test_stack_depth() { - assert(stack_depth([]) == 0); - assert(stack_depth([3]) == 1); - assert(stack_depth([2,4,8]) == 3); + assert(stack_depth([]) == 0); + assert(stack_depth([3]) == 1); + assert(stack_depth([2,4,8]) == 3); } test_stack_depth(); module test_stack_top() { - assert(stack_top([]) == undef); - assert(stack_top([3,5,7,9]) == 9); - assert(stack_top([3,5,7,9], 3) == [5,7,9]); + assert(stack_top([]) == undef); + assert(stack_top([3,5,7,9]) == 9); + assert(stack_top([3,5,7,9], 3) == [5,7,9]); } test_stack_top(); module test_stack_peek() { - s = [8,5,4,3,2,3,7]; - assert(stack_peek(s,0) == 7); - assert(stack_peek(s,2) == 2); - assert(stack_peek(s,2,1) == [2]); - assert(stack_peek(s,2,3) == [2,3,7]); + s = [8,5,4,3,2,3,7]; + assert(stack_peek(s,0) == 7); + assert(stack_peek(s,2) == 2); + assert(stack_peek(s,2,1) == [2]); + assert(stack_peek(s,2,3) == [2,3,7]); } test_stack_peek(); module test_stack_push() { - s1 = stack_init(); - s2 = stack_push(s1, "Foo"); - assert(s2==["Foo"]); - s3 = stack_push(s2, "Bar"); - assert(s3==["Foo","Bar"]); - s4 = stack_push(s3, "Baz"); - assert(s4==["Foo","Bar","Baz"]); + s1 = stack_init(); + s2 = stack_push(s1, "Foo"); + assert(s2==["Foo"]); + s3 = stack_push(s2, "Bar"); + assert(s3==["Foo","Bar"]); + s4 = stack_push(s3, "Baz"); + assert(s4==["Foo","Bar","Baz"]); } test_stack_push(); module test_stack_pop() { - s = ["Foo", "Bar", "Baz", "Qux"]; - s1 = stack_pop(s); - assert(s1 == ["Foo", "Bar", "Baz"]); - s2 = stack_pop(s,2); - assert(s2 == ["Foo", "Bar"]); - s3 = stack_pop(s,3); - assert(s3 == ["Foo"]); + s = ["Foo", "Bar", "Baz", "Qux"]; + s1 = stack_pop(s); + assert(s1 == ["Foo", "Bar", "Baz"]); + s2 = stack_pop(s,2); + assert(s2 == ["Foo", "Bar"]); + s3 = stack_pop(s,3); + assert(s3 == ["Foo"]); } test_stack_pop(); module test_stack_rotate() { - s = ["Foo", "Bar", "Baz", "Qux", "Quux"]; - s1 = stack_rotate(s,4); - assert(s1 == ["Foo", "Baz", "Qux", "Quux", "Bar"]); - s2 = stack_rotate(s,-4); - assert(s2 == ["Foo", "Quux", "Bar", "Baz", "Qux"]); + s = ["Foo", "Bar", "Baz", "Qux", "Quux"]; + s1 = stack_rotate(s,4); + assert(s1 == ["Foo", "Baz", "Qux", "Quux", "Bar"]); + s2 = stack_rotate(s,-4); + assert(s2 == ["Foo", "Quux", "Bar", "Baz", "Qux"]); } test_stack_rotate(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_structs.scad b/tests/test_structs.scad index 147990a..55db3b4 100644 --- a/tests/test_structs.scad +++ b/tests/test_structs.scad @@ -3,57 +3,57 @@ include module test_struct_set() { - st = struct_set([], "Foo", 42); - assert(st == [["Foo",42]]); - st2 = struct_set(st, "Bar", 28); - assert(st2 == [["Foo",42],["Bar",28]]); - st3 = struct_set(st2, "Foo", 91); - assert(st3 == [["Foo",91],["Bar",28]]); + st = struct_set([], "Foo", 42); + assert(st == [["Foo",42]]); + st2 = struct_set(st, "Bar", 28); + assert(st2 == [["Foo",42],["Bar",28]]); + st3 = struct_set(st2, "Foo", 91); + assert(st3 == [["Foo",91],["Bar",28]]); } test_struct_set(); module test_struct_remove() { - st = [["Foo",91],["Bar",28],["Baz",9]]; - assert(struct_remove(st, "Foo") == [["Bar",28],["Baz",9]]); - assert(struct_remove(st, "Bar") == [["Foo",91],["Baz",9]]); - assert(struct_remove(st, "Baz") == [["Foo",91],["Bar",28]]); + st = [["Foo",91],["Bar",28],["Baz",9]]; + assert(struct_remove(st, "Foo") == [["Bar",28],["Baz",9]]); + assert(struct_remove(st, "Bar") == [["Foo",91],["Baz",9]]); + assert(struct_remove(st, "Baz") == [["Foo",91],["Bar",28]]); } test_struct_remove(); module test_struct_val() { - st = [["Foo",91],["Bar",28],["Baz",9]]; - assert(struct_val(st,"Foo") == 91); - assert(struct_val(st,"Bar") == 28); - assert(struct_val(st,"Baz") == 9); + st = [["Foo",91],["Bar",28],["Baz",9]]; + assert(struct_val(st,"Foo") == 91); + assert(struct_val(st,"Bar") == 28); + assert(struct_val(st,"Baz") == 9); } test_struct_val(); module test_struct_keys() { - assert(struct_keys([["Foo",3],["Bar",2],["Baz",1]]) == ["Foo","Bar","Baz"]); - assert(struct_keys([["Zee",1],["Why",2],["Exx",3]]) == ["Zee","Why","Exx"]); + assert(struct_keys([["Foo",3],["Bar",2],["Baz",1]]) == ["Foo","Bar","Baz"]); + assert(struct_keys([["Zee",1],["Why",2],["Exx",3]]) == ["Zee","Why","Exx"]); } test_struct_keys(); module test_struct_echo() { - // Can't yet test echo output + // Can't yet test echo output } test_struct_echo(); module test_is_struct() { - assert(is_struct([["Foo",1],["Bar",2],["Baz",3]])); - assert(!is_struct([["Foo"],["Bar"],["Baz"]])); - assert(!is_struct(["Foo","Bar","Baz"])); - assert(!is_struct([3,4,5])); - assert(!is_struct(3)); - assert(!is_struct(true)); - assert(!is_struct("foo")); + assert(is_struct([["Foo",1],["Bar",2],["Baz",3]])); + assert(!is_struct([["Foo"],["Bar"],["Baz"]])); + assert(!is_struct(["Foo","Bar","Baz"])); + assert(!is_struct([3,4,5])); + assert(!is_struct(3)); + assert(!is_struct(true)); + assert(!is_struct("foo")); } test_is_struct(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_transforms.scad b/tests/test_transforms.scad index 1db7ff5..1e2a787 100644 --- a/tests/test_transforms.scad +++ b/tests/test_transforms.scad @@ -1,159 +1,159 @@ include module test(got,expect,extra_info) { - if ( - is_undef(expect) != is_undef(got) || - expect*0 != got*0 || - (is_vnf(expect) && !all([for (i=idx(expect[0])) approx(got[0][i],expect[0][i])]) && got[1]!=expect[1]) || - (is_matrix(expect) && !all([for (i=idx(expect)) approx(got[i],expect[i])])) || - (got!=expect && !approx(got, expect)) - ) { - fmt = is_int(expect)? "{:.14i}" : - is_num(expect)? "{:.14g}" : - is_vector(expect)? "{:.14g}" : - "{}"; - echofmt(str("Expected: ",fmt),[expect]); - echofmt(str("But Got : ",fmt),[got]); - if (expect*0 == got*0) { - echofmt(str("Delta is: ",fmt),[expect-got]); - } - if (!is_undef(extra_info)) { - echo(str("Extra Info: ",extra_info)); - } - assert(false, "TEST FAILED!"); - } + if ( + is_undef(expect) != is_undef(got) || + expect*0 != got*0 || + (is_vnf(expect) && !all([for (i=idx(expect[0])) approx(got[0][i],expect[0][i])]) && got[1]!=expect[1]) || + (is_matrix(expect) && !all([for (i=idx(expect)) approx(got[i],expect[i])])) || + (got!=expect && !approx(got, expect)) + ) { + fmt = is_int(expect)? "{:.14i}" : + is_num(expect)? "{:.14g}" : + is_vector(expect)? "{:.14g}" : + "{}"; + echofmt(str("Expected: ",fmt),[expect]); + echofmt(str("But Got : ",fmt),[got]); + if (expect*0 == got*0) { + echofmt(str("Delta is: ",fmt),[expect-got]); + } + if (!is_undef(extra_info)) { + echo(str("Extra Info: ",extra_info)); + } + assert(false, "TEST FAILED!"); + } } module test_rot() { - pts2d = 50 * [for (x=[-1,0,1],y=[-1,0,1]) [x,y]]; - pts3d = 50 * [for (x=[-1,0,1],y=[-1,0,1],z=[-1,0,1]) [x,y,z]]; - vecs2d = [ - for (x=[-1,0,1], y=[-1,0,1]) if(x!=0||y!=0) [x,y], - polar_to_xy(1, -75), - polar_to_xy(1, 75) - ]; - vecs3d = [ - LEFT, RIGHT, FRONT, BACK, DOWN, UP, - spherical_to_xyz(1, -30, 45), - spherical_to_xyz(1, 0, 45), - spherical_to_xyz(1, 30, 45), - spherical_to_xyz(2, 30, 45), - spherical_to_xyz(1, -30, 135), - spherical_to_xyz(2, -30, 135), - spherical_to_xyz(1, 0, 135), - spherical_to_xyz(1, 30, 135), - spherical_to_xyz(1, -30, 75), - spherical_to_xyz(1, 45, 45), - ]; - angs = [-180, -90, -45, 0, 30, 45, 90]; - for (a = [-360*3:360:360*3]) { - test(rot(a), affine3d_identity(), extra_info=str("rot(",a,") != identity")); - test(rot(a,p=pts2d), pts2d, extra_info=str("rot(",a,",p=...), 2D")); - test(rot(a,p=pts3d), pts3d, extra_info=str("rot(",a,",p=...), 3D")); - } - test(rot(90), [[0,-1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]) - for (a=angs) { - test(rot(a), affine3d_zrot(a), extra_info=str("Z angle (only) = ",a)); - test(rot([a,0,0]), affine3d_xrot(a), extra_info=str("X angle = ",a)); - test(rot([0,a,0]), affine3d_yrot(a), extra_info=str("Y angle = ",a)); - test(rot([0,0,a]), affine3d_zrot(a), extra_info=str("Z angle = ",a)); + pts2d = 50 * [for (x=[-1,0,1],y=[-1,0,1]) [x,y]]; + pts3d = 50 * [for (x=[-1,0,1],y=[-1,0,1],z=[-1,0,1]) [x,y,z]]; + vecs2d = [ + for (x=[-1,0,1], y=[-1,0,1]) if(x!=0||y!=0) [x,y], + polar_to_xy(1, -75), + polar_to_xy(1, 75) + ]; + vecs3d = [ + LEFT, RIGHT, FRONT, BACK, DOWN, UP, + spherical_to_xyz(1, -30, 45), + spherical_to_xyz(1, 0, 45), + spherical_to_xyz(1, 30, 45), + spherical_to_xyz(2, 30, 45), + spherical_to_xyz(1, -30, 135), + spherical_to_xyz(2, -30, 135), + spherical_to_xyz(1, 0, 135), + spherical_to_xyz(1, 30, 135), + spherical_to_xyz(1, -30, 75), + spherical_to_xyz(1, 45, 45), + ]; + angs = [-180, -90, -45, 0, 30, 45, 90]; + for (a = [-360*3:360:360*3]) { + test(rot(a), affine3d_identity(), extra_info=str("rot(",a,") != identity")); + test(rot(a,p=pts2d), pts2d, extra_info=str("rot(",a,",p=...), 2D")); + test(rot(a,p=pts3d), pts3d, extra_info=str("rot(",a,",p=...), 3D")); + } + test(rot(90), [[0,-1,0,0],[1,0,0,0],[0,0,1,0],[0,0,0,1]]) + for (a=angs) { + test(rot(a), affine3d_zrot(a), extra_info=str("Z angle (only) = ",a)); + test(rot([a,0,0]), affine3d_xrot(a), extra_info=str("X angle = ",a)); + test(rot([0,a,0]), affine3d_yrot(a), extra_info=str("Y angle = ",a)); + test(rot([0,0,a]), affine3d_zrot(a), extra_info=str("Z angle = ",a)); - test(rot(a,p=pts2d), apply(affine3d_zrot(a),pts2d), extra_info=str("Z angle (only) = ",a, ", p=..., 2D")); - test(rot([0,0,a],p=pts2d), apply(affine3d_zrot(a),pts2d), extra_info=str("Z angle = ",a, ", p=..., 2D")); + test(rot(a,p=pts2d), apply(affine3d_zrot(a),pts2d), extra_info=str("Z angle (only) = ",a, ", p=..., 2D")); + test(rot([0,0,a],p=pts2d), apply(affine3d_zrot(a),pts2d), extra_info=str("Z angle = ",a, ", p=..., 2D")); - test(rot(a,p=pts3d), apply(affine3d_zrot(a),pts3d), extra_info=str("Z angle (only) = ",a, ", p=..., 3D")); - test(rot([a,0,0],p=pts3d), apply(affine3d_xrot(a),pts3d), extra_info=str("X angle = ",a, ", p=..., 3D")); - test(rot([0,a,0],p=pts3d), apply(affine3d_yrot(a),pts3d), extra_info=str("Y angle = ",a, ", p=..., 3D")); - test(rot([0,0,a],p=pts3d), apply(affine3d_zrot(a),pts3d), extra_info=str("Z angle = ",a, ", p=..., 3D")); - } - for (xa=angs, ya=angs, za=angs) { - test( - rot([xa,ya,za]), - affine3d_chain([ - affine3d_xrot(xa), - affine3d_yrot(ya), - affine3d_zrot(za) - ]), - extra_info=str("[X,Y,Z] = ",[xa,ya,za]) - ); - test( - rot([xa,ya,za],p=pts3d), - apply( - affine3d_chain([ - affine3d_xrot(xa), - affine3d_yrot(ya), - affine3d_zrot(za) - ]), - pts3d - ), - extra_info=str("[X,Y,Z] = ",[xa,ya,za], ", p=...") - ); - } - for (vec1 = vecs3d) { - for (ang = angs) { - test( - rot(a=ang, v=vec1), - affine3d_rot_by_axis(vec1,ang), - extra_info=str("a = ",ang,", v = ", vec1) - ); - test( - rot(a=ang, v=vec1, p=pts3d), - apply(affine3d_rot_by_axis(vec1,ang), pts3d), - extra_info=str("a = ",ang,", v = ", vec1, ", p=...") - ); - } - } - for (vec1 = vecs2d) { - for (vec2 = vecs2d) { - test( - rot(from=vec1, to=vec2, p=pts2d, planar=true), - apply(affine2d_zrot(vang(vec2)-vang(vec1)), pts2d), - extra_info=str( - "from = ", vec1, ", ", - "to = ", vec2, ", ", - "planar = ", true, ", ", - "p=..., 2D" - ) - ); - } - } - for (vec1 = vecs3d) { - for (vec2 = vecs3d) { - for (a = angs) { - test( - rot(from=vec1, to=vec2, a=a), - affine3d_chain([ - affine3d_zrot(a), - affine3d_rot_from_to(vec1,vec2) - ]), - extra_info=str( - "from = ", vec1, ", ", - "to = ", vec2, ", ", - "a = ", a - ) - ); - test( - rot(from=vec1, to=vec2, a=a, p=pts3d), - apply( - affine3d_chain([ - affine3d_zrot(a), - affine3d_rot_from_to(vec1,vec2) - ]), - pts3d - ), - extra_info=str( - "from = ", vec1, ", ", - "to = ", vec2, ", ", - "a = ", a, ", ", - "p=..., 3D" - ) - ); - } - } - } + test(rot(a,p=pts3d), apply(affine3d_zrot(a),pts3d), extra_info=str("Z angle (only) = ",a, ", p=..., 3D")); + test(rot([a,0,0],p=pts3d), apply(affine3d_xrot(a),pts3d), extra_info=str("X angle = ",a, ", p=..., 3D")); + test(rot([0,a,0],p=pts3d), apply(affine3d_yrot(a),pts3d), extra_info=str("Y angle = ",a, ", p=..., 3D")); + test(rot([0,0,a],p=pts3d), apply(affine3d_zrot(a),pts3d), extra_info=str("Z angle = ",a, ", p=..., 3D")); + } + for (xa=angs, ya=angs, za=angs) { + test( + rot([xa,ya,za]), + affine3d_chain([ + affine3d_xrot(xa), + affine3d_yrot(ya), + affine3d_zrot(za) + ]), + extra_info=str("[X,Y,Z] = ",[xa,ya,za]) + ); + test( + rot([xa,ya,za],p=pts3d), + apply( + affine3d_chain([ + affine3d_xrot(xa), + affine3d_yrot(ya), + affine3d_zrot(za) + ]), + pts3d + ), + extra_info=str("[X,Y,Z] = ",[xa,ya,za], ", p=...") + ); + } + for (vec1 = vecs3d) { + for (ang = angs) { + test( + rot(a=ang, v=vec1), + affine3d_rot_by_axis(vec1,ang), + extra_info=str("a = ",ang,", v = ", vec1) + ); + test( + rot(a=ang, v=vec1, p=pts3d), + apply(affine3d_rot_by_axis(vec1,ang), pts3d), + extra_info=str("a = ",ang,", v = ", vec1, ", p=...") + ); + } + } + for (vec1 = vecs2d) { + for (vec2 = vecs2d) { + test( + rot(from=vec1, to=vec2, p=pts2d, planar=true), + apply(affine2d_zrot(vang(vec2)-vang(vec1)), pts2d), + extra_info=str( + "from = ", vec1, ", ", + "to = ", vec2, ", ", + "planar = ", true, ", ", + "p=..., 2D" + ) + ); + } + } + for (vec1 = vecs3d) { + for (vec2 = vecs3d) { + for (a = angs) { + test( + rot(from=vec1, to=vec2, a=a), + affine3d_chain([ + affine3d_zrot(a), + affine3d_rot_from_to(vec1,vec2) + ]), + extra_info=str( + "from = ", vec1, ", ", + "to = ", vec2, ", ", + "a = ", a + ) + ); + test( + rot(from=vec1, to=vec2, a=a, p=pts3d), + apply( + affine3d_chain([ + affine3d_zrot(a), + affine3d_rot_from_to(vec1,vec2) + ]), + pts3d + ), + extra_info=str( + "from = ", vec1, ", ", + "to = ", vec2, ", ", + "a = ", a, ", ", + "p=..., 3D" + ) + ); + } + } + } } test_rot(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_vectors.scad b/tests/test_vectors.scad index b13ead9..7090c4f 100644 --- a/tests/test_vectors.scad +++ b/tests/test_vectors.scad @@ -2,121 +2,121 @@ include module test_is_vector() { - assert(is_vector([1,2,3]) == true); - assert(is_vector([[1,2,3]]) == false); - assert(is_vector(["foo"]) == false); - assert(is_vector([]) == false); - assert(is_vector(1) == false); - assert(is_vector("foo") == false); - assert(is_vector(true) == false); + assert(is_vector([1,2,3]) == true); + assert(is_vector([[1,2,3]]) == false); + assert(is_vector(["foo"]) == false); + assert(is_vector([]) == false); + assert(is_vector(1) == false); + assert(is_vector("foo") == false); + assert(is_vector(true) == 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]]); + 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_vmul() { - assert(vmul([3,4,5], [8,7,6]) == [24,28,30]); - assert(vmul([1,2,3], [4,5,6]) == [4,10,18]); + 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]); + 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]); + 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(); include module test_vang() { - assert(vang([1,0])==0); - assert(vang([0,1])==90); - assert(vang([-1,0])==180); - assert(vang([0,-1])==-90); - assert(vang([1,1])==45); - assert(vang([-1,1])==135); - assert(vang([1,-1])==-45); - assert(vang([-1,-1])==-135); - assert(vang([0,0,1])==[0,90]); - assert(vang([0,1,1])==[90,45]); - assert(vang([0,1,-1])==[90,-45]); - assert(vang([1,0,0])==[0,0]); - assert(vang([0,1,0])==[90,0]); - assert(vang([0,-1,0])==[-90,0]); - assert(vang([-1,0,0])==[180,0]); - assert(vang([1,0,1])==[0,45]); - assert(vang([0,1,1])==[90,45]); - assert(vang([0,-1,1])==[-90,45]); - assert(approx(vang([1,1,1]),[45, 35.2643896828])); + assert(vang([1,0])==0); + assert(vang([0,1])==90); + assert(vang([-1,0])==180); + assert(vang([0,-1])==-90); + assert(vang([1,1])==45); + assert(vang([-1,1])==135); + assert(vang([1,-1])==-45); + assert(vang([-1,-1])==-135); + assert(vang([0,0,1])==[0,90]); + assert(vang([0,1,1])==[90,45]); + assert(vang([0,1,-1])==[90,-45]); + assert(vang([1,0,0])==[0,0]); + assert(vang([0,1,0])==[90,0]); + assert(vang([0,-1,0])==[-90,0]); + assert(vang([-1,0,0])==[180,0]); + assert(vang([1,0,1])==[0,45]); + assert(vang([0,1,1])==[90,45]); + assert(vang([0,-1,1])==[-90,45]); + assert(approx(vang([1,1,1]),[45, 35.2643896828])); } test_vang(); module test_unit() { - assert(unit([10,0,0]) == [1,0,0]); - assert(unit([0,10,0]) == [0,1,0]); - assert(unit([0,0,10]) == [0,0,1]); - assert(abs(norm(unit([10,10,10]))-1) < EPSILON); - assert(abs(norm(unit([-10,-10,-10]))-1) < EPSILON); - assert(abs(norm(unit([-10,0,0]))-1) < EPSILON); - assert(abs(norm(unit([0,-10,0]))-1) < EPSILON); - assert(abs(norm(unit([0,0,-10]))-1) < EPSILON); + assert(unit([10,0,0]) == [1,0,0]); + assert(unit([0,10,0]) == [0,1,0]); + assert(unit([0,0,10]) == [0,0,1]); + assert(abs(norm(unit([10,10,10]))-1) < EPSILON); + assert(abs(norm(unit([-10,-10,-10]))-1) < EPSILON); + assert(abs(norm(unit([-10,0,0]))-1) < EPSILON); + assert(abs(norm(unit([0,-10,0]))-1) < EPSILON); + assert(abs(norm(unit([0,0,-10]))-1) < EPSILON); } test_unit(); 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); - assert(vector_angle([a,b])==0); - } else if(a==-b) { - assert(vector_angle(a,b)==180); - assert(vector_angle([a,b])==180); - } else { - assert(vector_angle(a,b)==90); - assert(vector_angle([a,b])==90); - } - } - assert(abs(vector_angle([10,10,0],[10,0,0])-45) < EPSILON); - assert(abs(vector_angle([[10,10,0],[10,0,0]])-45) < EPSILON); - assert(abs(vector_angle([11,11,1],[1,1,1],[11,-9,1])-90) < EPSILON); - assert(abs(vector_angle([[11,11,1],[1,1,1],[11,-9,1]])-90) < EPSILON); + 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); + assert(vector_angle([a,b])==0); + } else if(a==-b) { + assert(vector_angle(a,b)==180); + assert(vector_angle([a,b])==180); + } else { + assert(vector_angle(a,b)==90); + assert(vector_angle([a,b])==90); + } + } + assert(abs(vector_angle([10,10,0],[10,0,0])-45) < EPSILON); + assert(abs(vector_angle([[10,10,0],[10,0,0]])-45) < EPSILON); + assert(abs(vector_angle([11,11,1],[1,1,1],[11,-9,1])-90) < EPSILON); + assert(abs(vector_angle([[11,11,1],[1,1,1],[11,-9,1]])-90) < 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],[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([[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,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([[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,0],[0,0,10]]) - [0,-1,0]) < EPSILON); - assert(norm(vector_axis([10,0,10],[0,-10,0]) - [sin(45),0,-sin(45)]) < EPSILON); - assert(norm(vector_axis([[10,0,10],[0,-10,0]]) - [sin(45),0,-sin(45)]) < EPSILON); - assert(norm(vector_axis([11,1,11],[1,1,1],[1,-9,1]) - [sin(45),0,-sin(45)]) < EPSILON); - assert(norm(vector_axis([[11,1,11],[1,1,1],[1,-9,1]]) - [sin(45),0,-sin(45)]) < EPSILON); + assert(norm(vector_axis([10,0,0],[10,10,0]) - [0,0,1]) < EPSILON); + 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([[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,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([[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,0],[0,0,10]]) - [0,-1,0]) < EPSILON); + assert(norm(vector_axis([10,0,10],[0,-10,0]) - [sin(45),0,-sin(45)]) < EPSILON); + assert(norm(vector_axis([[10,0,10],[0,-10,0]]) - [sin(45),0,-sin(45)]) < EPSILON); + assert(norm(vector_axis([11,1,11],[1,1,1],[1,-9,1]) - [sin(45),0,-sin(45)]) < EPSILON); + assert(norm(vector_axis([[11,1,11],[1,1,1],[1,-9,1]]) - [sin(45),0,-sin(45)]) < EPSILON); } test_vector_axis(); @@ -124,4 +124,4 @@ test_vector_axis(); cube(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_version.scad b/tests/test_version.scad index 9a65c80..f877b5c 100644 --- a/tests/test_version.scad +++ b/tests/test_version.scad @@ -2,116 +2,116 @@ include module test_bosl_version() { - assert(is_vector(bosl_version())); // Returned value is a vector. - assert(len(bosl_version())==3); // of three numbers. - assert(bosl_version()[0]==2); // The major version is 2. - for (v=bosl_version()) { - assert(floor(v)==v); // All version parts are integers. - } + assert(is_vector(bosl_version())); // Returned value is a vector. + assert(len(bosl_version())==3); // of three numbers. + assert(bosl_version()[0]==2); // The major version is 2. + for (v=bosl_version()) { + assert(floor(v)==v); // All version parts are integers. + } } test_bosl_version(); module test_bosl_version_num() { - assert(is_num(bosl_version_num())); - v = bosl_version(); - assert(bosl_version_num() == v[0]+v[1]/100+v[2]/1000000); + assert(is_num(bosl_version_num())); + v = bosl_version(); + assert(bosl_version_num() == v[0]+v[1]/100+v[2]/1000000); } test_bosl_version_num(); module test_bosl_version_str() { - assert(is_string(bosl_version_str())); - v = bosl_version(); - assert(bosl_version_str() == str(v[0],".",v[1],".",v[2])); + assert(is_string(bosl_version_str())); + v = bosl_version(); + assert(bosl_version_str() == str(v[0],".",v[1],".",v[2])); } test_bosl_version_str(); module test_bosl_required() { - bosl_required(2.000001); - bosl_required("2.0.1"); - bosl_required([2,0,1]); + bosl_required(2.000001); + bosl_required("2.0.1"); + bosl_required([2,0,1]); } test_bosl_required(); module test_version_to_list() { - assert(is_list(version_to_list(2.010001))); - assert(is_list(version_to_list("2.1.1"))); - assert(is_list(version_to_list([2,1,1]))); - assert(version_to_list(2.010001)==[2,1,1]); - assert(version_to_list("2.1.1")==[2,1,1]); - assert(version_to_list([2,1,1])==[2,1,1]); - assert(version_to_list(2.010035)==[2,1,35]); - assert(version_to_list(2.345678)==[2,34,5678]); - assert(version_to_list("2.34.5678")==[2,34,5678]); - assert(version_to_list([2,34,5678])==[2,34,5678]); - assert(version_to_list([2,34,56,78])==[2,34,56]); + assert(is_list(version_to_list(2.010001))); + assert(is_list(version_to_list("2.1.1"))); + assert(is_list(version_to_list([2,1,1]))); + assert(version_to_list(2.010001)==[2,1,1]); + assert(version_to_list("2.1.1")==[2,1,1]); + assert(version_to_list([2,1,1])==[2,1,1]); + assert(version_to_list(2.010035)==[2,1,35]); + assert(version_to_list(2.345678)==[2,34,5678]); + assert(version_to_list("2.34.5678")==[2,34,5678]); + assert(version_to_list([2,34,5678])==[2,34,5678]); + assert(version_to_list([2,34,56,78])==[2,34,56]); } test_version_to_list(); module test_version_to_str() { - assert(is_string(version_to_str(2.010001))); - assert(is_string(version_to_str("2.1.1"))); - assert(is_string(version_to_str([2,1,1]))); - assert(version_to_str(2.010001)=="2.1.1"); - assert(version_to_str("2.1.1")=="2.1.1"); - assert(version_to_str([2,1,1])=="2.1.1"); - assert(version_to_str(2.345678)=="2.34.5678"); - assert(version_to_str("2.34.5678")=="2.34.5678"); - assert(version_to_str([2,34,5678])=="2.34.5678"); - assert(version_to_str([2,34,56,78])=="2.34.56"); + assert(is_string(version_to_str(2.010001))); + assert(is_string(version_to_str("2.1.1"))); + assert(is_string(version_to_str([2,1,1]))); + assert(version_to_str(2.010001)=="2.1.1"); + assert(version_to_str("2.1.1")=="2.1.1"); + assert(version_to_str([2,1,1])=="2.1.1"); + assert(version_to_str(2.345678)=="2.34.5678"); + assert(version_to_str("2.34.5678")=="2.34.5678"); + assert(version_to_str([2,34,5678])=="2.34.5678"); + assert(version_to_str([2,34,56,78])=="2.34.56"); } test_version_to_str(); module test_version_to_num() { - assert(is_num(version_to_num(2.010001))); - assert(is_num(version_to_num("2.1.1"))); - assert(is_num(version_to_num([2,1,1]))); - assert(version_to_num(2.010001)==2.010001); - assert(version_to_num("2.1.1")==2.010001); - assert(version_to_num([2,1,1])==2.010001); - assert(version_to_num(2.345678)==2.345678); - assert(version_to_num("2.34.5678")==2.345678); - assert(version_to_num([2,34,5678])==2.345678); - assert(version_to_num([2,34,56,78])==2.340056); + assert(is_num(version_to_num(2.010001))); + assert(is_num(version_to_num("2.1.1"))); + assert(is_num(version_to_num([2,1,1]))); + assert(version_to_num(2.010001)==2.010001); + assert(version_to_num("2.1.1")==2.010001); + assert(version_to_num([2,1,1])==2.010001); + assert(version_to_num(2.345678)==2.345678); + assert(version_to_num("2.34.5678")==2.345678); + assert(version_to_num([2,34,5678])==2.345678); + assert(version_to_num([2,34,56,78])==2.340056); } test_version_to_num(); module test_version_cmp() { - function diversify(x) = [ - version_to_num(x), - version_to_str(x), - version_to_list(x) - ]; + function diversify(x) = [ + version_to_num(x), + version_to_str(x), + version_to_list(x) + ]; - module testvercmp(x,y,z) { - for (a = diversify(y)) { - for (b = diversify(x)) { - assert(version_cmp(a,b)>0); - } - for (b = diversify(y)) { - assert(version_cmp(a,b)==0); - } - for (b = diversify(z)) { - assert(version_cmp(a,b)<0); - } - } - } + module testvercmp(x,y,z) { + for (a = diversify(y)) { + for (b = diversify(x)) { + assert(version_cmp(a,b)>0); + } + for (b = diversify(y)) { + assert(version_cmp(a,b)==0); + } + for (b = diversify(z)) { + assert(version_cmp(a,b)<0); + } + } + } - testvercmp([2,1,33],[2,1,34],[2,1,35]); - testvercmp([2,2,1],[2,2,34],[2,2,67]); - testvercmp([2,2,34],[2,3,34],[2,4,34]); - testvercmp([2,3,34],[3,3,34],[4,3,34]); - testvercmp([2,3,34],[3,1,1],[4,1,1]); - testvercmp([2,1,1],[3,3,34],[4,1,1]); - testvercmp([2,1,1],[3,1,1],[4,3,34]); + testvercmp([2,1,33],[2,1,34],[2,1,35]); + testvercmp([2,2,1],[2,2,34],[2,2,67]); + testvercmp([2,2,34],[2,3,34],[2,4,34]); + testvercmp([2,3,34],[3,3,34],[4,3,34]); + testvercmp([2,3,34],[3,1,1],[4,1,1]); + testvercmp([2,1,1],[3,3,34],[4,1,1]); + testvercmp([2,1,1],[3,1,1],[4,3,34]); } test_version_cmp(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/tests/test_vnf.scad b/tests/test_vnf.scad index 52da3fb..353d117 100644 --- a/tests/test_vnf.scad +++ b/tests/test_vnf.scad @@ -3,101 +3,101 @@ include module test_is_vnf() { - assert(is_vnf([[],[]])); - assert(!is_vnf([])); - assert(is_vnf([[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]])); + assert(is_vnf([[],[]])); + assert(!is_vnf([])); + assert(is_vnf([[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]])); } test_is_vnf(); module test_is_vnf_list() { - assert(is_vnf_list([])); - assert(!is_vnf_list([[],[]])); - assert(is_vnf_list([[[],[]]])); - assert(!is_vnf_list([[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]])); - assert(is_vnf_list([[[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]])); + assert(is_vnf_list([])); + assert(!is_vnf_list([[],[]])); + assert(is_vnf_list([[[],[]]])); + assert(!is_vnf_list([[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]])); + assert(is_vnf_list([[[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]])); } test_is_vnf_list(); module test_vnf_vertices() { - vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; - assert(vnf_vertices(vnf) == vnf[0]); + vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; + assert(vnf_vertices(vnf) == vnf[0]); } test_vnf_vertices(); module test_vnf_faces() { - vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; - assert(vnf_faces(vnf) == vnf[1]); + vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; + assert(vnf_faces(vnf) == vnf[1]); } test_vnf_faces(); module test_vnf_get_vertex() { - vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; - assert(vnf_get_vertex(vnf,[0,1,-1]) == [2,vnf]); - assert(vnf_get_vertex(vnf,[0,1,2]) == [4,[concat(vnf[0],[[0,1,2]]),vnf[1]]]); + vnf = [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]],[[0,1,2],[0,3,1],[1,3,2],[2,3,0]]]; + assert(vnf_get_vertex(vnf,[0,1,-1]) == [2,vnf]); + assert(vnf_get_vertex(vnf,[0,1,2]) == [4,[concat(vnf[0],[[0,1,2]]),vnf[1]]]); } test_vnf_get_vertex(); module test_vnf_add_face() { - verts = [[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]]; - faces = [[0,1,2],[0,3,1],[1,3,2],[2,3,0]]; - vnf1 = vnf_add_face(pts=select(verts,faces[0])); - vnf2 = vnf_add_face(vnf1, pts=select(verts,faces[1])); - vnf3 = vnf_add_face(vnf2, pts=select(verts,faces[2])); - vnf4 = vnf_add_face(vnf3, pts=select(verts,faces[3])); - assert(vnf1 == [select(verts,0,2),select(faces,[0])]); - assert(vnf2 == [verts,select(faces,[0:1])]); - assert(vnf3 == [verts,select(faces,[0:2])]); - assert(vnf4 == [verts,faces]); + verts = [[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]]; + faces = [[0,1,2],[0,3,1],[1,3,2],[2,3,0]]; + vnf1 = vnf_add_face(pts=select(verts,faces[0])); + vnf2 = vnf_add_face(vnf1, pts=select(verts,faces[1])); + vnf3 = vnf_add_face(vnf2, pts=select(verts,faces[2])); + vnf4 = vnf_add_face(vnf3, pts=select(verts,faces[3])); + assert(vnf1 == [select(verts,0,2),select(faces,[0])]); + assert(vnf2 == [verts,select(faces,[0:1])]); + assert(vnf3 == [verts,select(faces,[0:2])]); + assert(vnf4 == [verts,faces]); } test_vnf_add_face(); module test_vnf_add_faces() { - verts = [[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]]; - faces = [[0,1,2],[0,3,1],[1,3,2],[2,3,0]]; - assert(vnf_add_faces(faces=[for (face=faces) select(verts,face)]) == [verts,faces]); + verts = [[-1,-1,-1],[1,-1,-1],[0,1,-1],[0,0,1]]; + faces = [[0,1,2],[0,3,1],[1,3,2],[2,3,0]]; + assert(vnf_add_faces(faces=[for (face=faces) select(verts,face)]) == [verts,faces]); } test_vnf_add_faces(); module test_vnf_merge() { - vnf1 = vnf_add_face(pts=[[-1,-1,-1],[1,-1,-1],[0,1,-1]]); - vnf2 = vnf_add_face(pts=[[1,1,1],[-1,1,1],[0,1,-1]]); - assert(vnf_merge([vnf1,vnf2]) == [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[1,1,1],[-1,1,1],[0,1,-1]],[[0,1,2],[3,4,5]]]); + vnf1 = vnf_add_face(pts=[[-1,-1,-1],[1,-1,-1],[0,1,-1]]); + vnf2 = vnf_add_face(pts=[[1,1,1],[-1,1,1],[0,1,-1]]); + assert(vnf_merge([vnf1,vnf2]) == [[[-1,-1,-1],[1,-1,-1],[0,1,-1],[1,1,1],[-1,1,1],[0,1,-1]],[[0,1,2],[3,4,5]]]); } test_vnf_merge(); module test_vnf_triangulate() { - vnf = [[[-1,-1,0],[1,-1,0],[1,1,0],[-1,1,0]],[[0,1,2,3]]]; - assert(vnf_triangulate(vnf) == [[[-1,-1,0],[1,-1,0],[1,1,0],[-1,1,0]], [[0,1,2],[2,3,0]]]); + vnf = [[[-1,-1,0],[1,-1,0],[1,1,0],[-1,1,0]],[[0,1,2,3]]]; + assert(vnf_triangulate(vnf) == [[[-1,-1,0],[1,-1,0],[1,1,0],[-1,1,0]], [[0,1,2],[2,3,0]]]); } test_vnf_triangulate(); module test_vnf_vertex_array() { - vnf1 = vnf_vertex_array( - points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], - col_wrap=true, caps=true - ); - vnf2 = vnf_vertex_array( - points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], - col_wrap=true, caps=true, style="alt" - ); - vnf3 = vnf_vertex_array( - points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], - col_wrap=true, caps=true, style="quincunx" - ); - assert(vnf1 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[0,4,3],[0,1,4],[1,5,4],[1,2,5],[2,3,5],[2,0,3],[2,1,0],[3,4,5]]]); - assert(vnf2 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[0,1,3],[3,1,4],[1,2,4],[4,2,5],[2,0,5],[5,0,3],[2,1,0],[3,4,5]]]); - assert(vnf3 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100],[0,-50,50],[-50,25,50],[50,25,50]],[[0,6,3],[3,6,4],[4,6,1],[1,6,0],[1,7,4],[4,7,5],[5,7,2],[2,7,1],[2,8,5],[5,8,3],[3,8,0],[0,8,2],[2,1,0],[3,4,5]]]); + vnf1 = vnf_vertex_array( + points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], + col_wrap=true, caps=true + ); + vnf2 = vnf_vertex_array( + points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], + col_wrap=true, caps=true, style="alt" + ); + vnf3 = vnf_vertex_array( + points=[for (h=[0:100:100]) [[100,-50,h],[-100,-50,h],[0,100,h]]], + col_wrap=true, caps=true, style="quincunx" + ); + assert(vnf1 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[0,4,3],[0,1,4],[1,5,4],[1,2,5],[2,3,5],[2,0,3],[2,1,0],[3,4,5]]]); + assert(vnf2 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100]],[[0,1,3],[3,1,4],[1,2,4],[4,2,5],[2,0,5],[5,0,3],[2,1,0],[3,4,5]]]); + assert(vnf3 == [[[100,-50,0],[-100,-50,0],[0,100,0],[100,-50,100],[-100,-50,100],[0,100,100],[0,-50,50],[-50,25,50],[50,25,50]],[[0,6,3],[3,6,4],[4,6,1],[1,6,0],[1,7,4],[4,7,5],[5,7,2],[2,7,1],[2,8,5],[5,8,3],[3,8,0],[0,8,2],[2,1,0],[3,4,5]]]); } test_vnf_vertex_array(); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/threading.scad b/threading.scad index 1ea3313..7d4ced5 100644 --- a/threading.scad +++ b/threading.scad @@ -43,34 +43,34 @@ // stroke(profile, width=0.02); module thread_helix(base_d, pitch, thread_depth=undef, thread_angle=15, twist=720, profile=undef, left_handed=false, higbee=60, internal=false, anchor=CENTER, spin=0, orient=UP) { - h = pitch*twist/360; - r = base_d/2; - dz = thread_depth/pitch * tan(thread_angle); - cap = (1 - 2*dz)/2; - profile = !is_undef(profile)? profile : ( - internal? [ - [thread_depth/pitch, -cap/2-dz], - [0, -cap/2], - [0, +cap/2], - [thread_depth/pitch, +cap/2+dz], - ] : [ - [0, +cap/2+dz], - [thread_depth/pitch, +cap/2], - [thread_depth/pitch, -cap/2], - [0, -cap/2-dz], - ] - ); - pline = profile * pitch; - dir = left_handed? -1 : 1; - idir = internal? -1 : 1; - attachable(anchor,spin,orient, r=r, l=h) { - difference() { - spiral_sweep(pline, h=h, r=base_d/2, twist=twist*dir, $fn=segs(base_d/2), anchor=CENTER); - down(h/2) right(r) right(internal? thread_depth : 0) zrot(higbee*dir*idir) fwd(dir*pitch/2) cube([3*thread_depth/cos(higbee), pitch, pitch], center=true); - up(h/2) zrot(twist*dir) right(r) right(internal? thread_depth : 0) zrot(-higbee*dir*idir) back(dir*pitch/2) cube([3*thread_depth/cos(higbee), pitch, pitch], center=true); - } - children(); - } + h = pitch*twist/360; + r = base_d/2; + dz = thread_depth/pitch * tan(thread_angle); + cap = (1 - 2*dz)/2; + profile = !is_undef(profile)? profile : ( + internal? [ + [thread_depth/pitch, -cap/2-dz], + [0, -cap/2], + [0, +cap/2], + [thread_depth/pitch, +cap/2+dz], + ] : [ + [0, +cap/2+dz], + [thread_depth/pitch, +cap/2], + [thread_depth/pitch, -cap/2], + [0, -cap/2-dz], + ] + ); + pline = profile * pitch; + dir = left_handed? -1 : 1; + idir = internal? -1 : 1; + attachable(anchor,spin,orient, r=r, l=h) { + difference() { + spiral_sweep(pline, h=h, r=base_d/2, twist=twist*dir, $fn=segs(base_d/2), anchor=CENTER); + down(h/2) right(r) right(internal? thread_depth : 0) zrot(higbee*dir*idir) fwd(dir*pitch/2) cube([3*thread_depth/cos(higbee), pitch, pitch], center=true); + up(h/2) zrot(twist*dir) right(r) right(internal? thread_depth : 0) zrot(-higbee*dir*idir) back(dir*pitch/2) cube([3*thread_depth/cos(higbee), pitch, pitch], center=true); + } + children(); + } } @@ -127,162 +127,162 @@ module thread_helix(base_d, pitch, thread_depth=undef, thread_angle=15, twist=72 // ]; // stroke(profile, width=0.02); module trapezoidal_threaded_rod( - d=10, - l=100, - pitch=2, - thread_angle=15, - thread_depth=undef, - left_handed=false, - bevel=false, - starts=1, - profile=undef, - internal=false, - center, anchor, spin=0, orient=UP + d=10, + l=100, + pitch=2, + thread_angle=15, + thread_depth=undef, + left_handed=false, + bevel=false, + starts=1, + profile=undef, + internal=false, + center, anchor, spin=0, orient=UP ) { - function _thread_pt(thread, threads, start, starts, astep, asteps, part, parts) = - astep + asteps * (thread + threads * (part + parts * start)); + function _thread_pt(thread, threads, start, starts, astep, asteps, part, parts) = + astep + asteps * (thread + threads * (part + parts * start)); - d = internal? (d/cos(180/segs(d/2)) + $slop*3) : d; - astep = 360 / quantup(segs(d/2), starts); - asteps = ceil(360/astep); - threads = ceil(l/pitch/starts)+(starts<4?4-starts:1); - depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle)); - pa_delta = min(pitch/4-0.01,depth*tan(thread_angle)/2)/pitch; - dir = left_handed? -1 : 1; - r1 = -depth/pitch; - z1 = 1/4-pa_delta; - z2 = 1/4+pa_delta; - profile = profile!=undef? profile : [ - [-z2, r1], - [-z1, 0], - [ z1, 0], - [ z2, r1], - ]; - parts = len(profile); - poly_points = concat( - [ - for ( - start = [0:1:starts-1], - part = [0:1:parts-1], - thread = [0:1:threads-1], - astep = [0:1:asteps-1] - ) let ( - ppt = profile[part] * pitch, - dz = ppt.x, - r = ppt.y + d/2, - a = astep / asteps, - c = cos(360 * (a * dir + start/starts)), - s = sin(360 * (a * dir + start/starts)), - z = (thread + a - threads/2) * starts * pitch - ) [r*c, r*s, z+dz] - ], - [[0, 0, -threads*pitch*starts/2-pitch/4], [0, 0, threads*pitch*starts/2+pitch/4]] - ); - point_count = len(poly_points); - poly_faces = concat( - // Thread surfaces - [ - for ( - start = [0:1:starts-1], - part = [0:1:parts-2], - thread = [0:1:threads-1], - astep = [0:1:asteps-1], - trinum = [0, 1] - ) let ( - p0 = _thread_pt(thread, threads, start, starts, astep, asteps, part, parts), - p1 = _thread_pt(thread, threads, start, starts, astep, asteps, part+1, parts), - p2 = _thread_pt(thread, threads, start, starts, astep+1, asteps, part, parts), - p3 = _thread_pt(thread, threads, start, starts, astep+1, asteps, part+1, parts), - tri = trinum==0? [p0, p1, p3] : [p0, p3, p2], - otri = left_handed? [tri[0], tri[2], tri[1]] : tri - ) - if (!(thread == threads-1 && astep == asteps-1)) otri - ], - // Thread trough bottom - [ - for ( - start = [0:1:starts-1], - thread = [0:1:threads-1], - astep = [0:1:asteps-1], - trinum = [0, 1] - ) let ( - p0 = _thread_pt(thread, threads, start, starts, astep, asteps, parts-1, parts), - p1 = _thread_pt(thread, threads, (start+(left_handed?1:starts-1))%starts, starts, astep+asteps/starts, asteps, 0, parts), - p2 = p0 + 1, - p3 = p1 + 1, - tri = trinum==0? [p0, p1, p3] : [p0, p3, p2], - otri = left_handed? [tri[0], tri[2], tri[1]] : tri - ) - if ( - !(thread >= threads-1 && astep > asteps-asteps/starts-2) && - !(thread >= threads-2 && starts == 1 && astep >= asteps-1) - ) otri - ], - // top and bottom thread endcap - [ - for ( - start = [0:1:starts-1], - part = [1:1:parts-2], - is_top = [0, 1] - ) let ( - astep = is_top? asteps-1 : 0, - thread = is_top? threads-1 : 0, - p0 = _thread_pt(thread, threads, start, starts, astep, asteps, 0, parts), - p1 = _thread_pt(thread, threads, start, starts, astep, asteps, part, parts), - p2 = _thread_pt(thread, threads, start, starts, astep, asteps, part+1, parts), - tri = is_top? [p0, p1, p2] : [p0, p2, p1], - otri = left_handed? [tri[0], tri[2], tri[1]] : tri - ) otri - ], - // body side triangles - [ - for ( - start = [0:1:starts-1], - is_top = [false, true], - trinum = [0, 1] - ) let ( - astep = is_top? asteps-1 : 0, - thread = is_top? threads-1 : 0, - ostart = (is_top != left_handed? (start+1) : (start+starts-1))%starts, - ostep = is_top? astep-asteps/starts : astep+asteps/starts, - oparts = is_top? parts-1 : 0, - p0 = is_top? point_count-1 : point_count-2, - p1 = _thread_pt(thread, threads, start, starts, astep, asteps, 0, parts), - p2 = _thread_pt(thread, threads, start, starts, astep, asteps, parts-1, parts), - p3 = _thread_pt(thread, threads, ostart, starts, ostep, asteps, oparts, parts), - tri = trinum==0? - (is_top? [p0, p1, p2] : [p0, p2, p1]) : - (is_top? [p0, p3, p1] : [p0, p3, p2]), - otri = left_handed? [tri[0], tri[2], tri[1]] : tri - ) otri - ], - // Caps - [ - for ( - start = [0:1:starts-1], - astep = [0:1:asteps/starts-1], - is_top = [0, 1] - ) let ( - thread = is_top? threads-1 : 0, - part = is_top? parts-1 : 0, - ostep = is_top? asteps-astep-2 : astep, - p0 = is_top? point_count-1 : point_count-2, - p1 = _thread_pt(thread, threads, start, starts, ostep, asteps, part, parts), - p2 = _thread_pt(thread, threads, start, starts, ostep+1, asteps, part, parts), - tri = is_top? [p0, p2, p1] : [p0, p1, p2], - otri = left_handed? [tri[0], tri[2], tri[1]] : tri - ) otri - ] - ); - anchor = get_anchor(anchor, center, BOT, CENTER); - attachable(anchor,spin,orient, d=d, l=l) { - difference() { - polyhedron(points=poly_points, faces=poly_faces, convexity=threads*starts*2); - zcopies(l+4*pitch*starts) cube([d+1, d+1, 4*pitch*starts], center=true); - if (bevel) cylinder_mask(d=d, l=l+0.01, chamfer=depth); - } - children(); - } + d = internal? (d/cos(180/segs(d/2)) + $slop*3) : d; + astep = 360 / quantup(segs(d/2), starts); + asteps = ceil(360/astep); + threads = ceil(l/pitch/starts)+(starts<4?4-starts:1); + depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle)); + pa_delta = min(pitch/4-0.01,depth*tan(thread_angle)/2)/pitch; + dir = left_handed? -1 : 1; + r1 = -depth/pitch; + z1 = 1/4-pa_delta; + z2 = 1/4+pa_delta; + profile = profile!=undef? profile : [ + [-z2, r1], + [-z1, 0], + [ z1, 0], + [ z2, r1], + ]; + parts = len(profile); + poly_points = concat( + [ + for ( + start = [0:1:starts-1], + part = [0:1:parts-1], + thread = [0:1:threads-1], + astep = [0:1:asteps-1] + ) let ( + ppt = profile[part] * pitch, + dz = ppt.x, + r = ppt.y + d/2, + a = astep / asteps, + c = cos(360 * (a * dir + start/starts)), + s = sin(360 * (a * dir + start/starts)), + z = (thread + a - threads/2) * starts * pitch + ) [r*c, r*s, z+dz] + ], + [[0, 0, -threads*pitch*starts/2-pitch/4], [0, 0, threads*pitch*starts/2+pitch/4]] + ); + point_count = len(poly_points); + poly_faces = concat( + // Thread surfaces + [ + for ( + start = [0:1:starts-1], + part = [0:1:parts-2], + thread = [0:1:threads-1], + astep = [0:1:asteps-1], + trinum = [0, 1] + ) let ( + p0 = _thread_pt(thread, threads, start, starts, astep, asteps, part, parts), + p1 = _thread_pt(thread, threads, start, starts, astep, asteps, part+1, parts), + p2 = _thread_pt(thread, threads, start, starts, astep+1, asteps, part, parts), + p3 = _thread_pt(thread, threads, start, starts, astep+1, asteps, part+1, parts), + tri = trinum==0? [p0, p1, p3] : [p0, p3, p2], + otri = left_handed? [tri[0], tri[2], tri[1]] : tri + ) + if (!(thread == threads-1 && astep == asteps-1)) otri + ], + // Thread trough bottom + [ + for ( + start = [0:1:starts-1], + thread = [0:1:threads-1], + astep = [0:1:asteps-1], + trinum = [0, 1] + ) let ( + p0 = _thread_pt(thread, threads, start, starts, astep, asteps, parts-1, parts), + p1 = _thread_pt(thread, threads, (start+(left_handed?1:starts-1))%starts, starts, astep+asteps/starts, asteps, 0, parts), + p2 = p0 + 1, + p3 = p1 + 1, + tri = trinum==0? [p0, p1, p3] : [p0, p3, p2], + otri = left_handed? [tri[0], tri[2], tri[1]] : tri + ) + if ( + !(thread >= threads-1 && astep > asteps-asteps/starts-2) && + !(thread >= threads-2 && starts == 1 && astep >= asteps-1) + ) otri + ], + // top and bottom thread endcap + [ + for ( + start = [0:1:starts-1], + part = [1:1:parts-2], + is_top = [0, 1] + ) let ( + astep = is_top? asteps-1 : 0, + thread = is_top? threads-1 : 0, + p0 = _thread_pt(thread, threads, start, starts, astep, asteps, 0, parts), + p1 = _thread_pt(thread, threads, start, starts, astep, asteps, part, parts), + p2 = _thread_pt(thread, threads, start, starts, astep, asteps, part+1, parts), + tri = is_top? [p0, p1, p2] : [p0, p2, p1], + otri = left_handed? [tri[0], tri[2], tri[1]] : tri + ) otri + ], + // body side triangles + [ + for ( + start = [0:1:starts-1], + is_top = [false, true], + trinum = [0, 1] + ) let ( + astep = is_top? asteps-1 : 0, + thread = is_top? threads-1 : 0, + ostart = (is_top != left_handed? (start+1) : (start+starts-1))%starts, + ostep = is_top? astep-asteps/starts : astep+asteps/starts, + oparts = is_top? parts-1 : 0, + p0 = is_top? point_count-1 : point_count-2, + p1 = _thread_pt(thread, threads, start, starts, astep, asteps, 0, parts), + p2 = _thread_pt(thread, threads, start, starts, astep, asteps, parts-1, parts), + p3 = _thread_pt(thread, threads, ostart, starts, ostep, asteps, oparts, parts), + tri = trinum==0? + (is_top? [p0, p1, p2] : [p0, p2, p1]) : + (is_top? [p0, p3, p1] : [p0, p3, p2]), + otri = left_handed? [tri[0], tri[2], tri[1]] : tri + ) otri + ], + // Caps + [ + for ( + start = [0:1:starts-1], + astep = [0:1:asteps/starts-1], + is_top = [0, 1] + ) let ( + thread = is_top? threads-1 : 0, + part = is_top? parts-1 : 0, + ostep = is_top? asteps-astep-2 : astep, + p0 = is_top? point_count-1 : point_count-2, + p1 = _thread_pt(thread, threads, start, starts, ostep, asteps, part, parts), + p2 = _thread_pt(thread, threads, start, starts, ostep+1, asteps, part, parts), + tri = is_top? [p0, p2, p1] : [p0, p1, p2], + otri = left_handed? [tri[0], tri[2], tri[1]] : tri + ) otri + ] + ); + anchor = get_anchor(anchor, center, BOT, CENTER); + attachable(anchor,spin,orient, d=d, l=l) { + difference() { + polyhedron(points=poly_points, faces=poly_faces, convexity=threads*starts*2); + zcopies(l+4*pitch*starts) cube([d+1, d+1, 4*pitch*starts], center=true); + if (bevel) cylinder_mask(d=d, l=l+0.01, chamfer=depth); + } + children(); + } } @@ -315,45 +315,45 @@ module trapezoidal_threaded_rod( // trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, $slop=0.2, left_handed=true); // trapezoidal_threaded_nut(od=17.4, id=10, h=10, pitch=2, thread_angle=15, starts=3, $fa=1, $fs=1); module trapezoidal_threaded_nut( - od=17.4, - id=10, - h=10, - pitch=2, - thread_depth=undef, - thread_angle=15, - profile=undef, - left_handed=false, - starts=1, - bevel=true, - anchor=CENTER, - spin=0, - orient=UP + od=17.4, + id=10, + h=10, + pitch=2, + thread_depth=undef, + thread_angle=15, + profile=undef, + left_handed=false, + starts=1, + bevel=true, + anchor=CENTER, + spin=0, + orient=UP ) { - depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle)); - attachable(anchor,spin,orient, size=[od/cos(30),od,h]) { - difference() { - cylinder(d=od/cos(30), h=h, center=true, $fn=6); - trapezoidal_threaded_rod( - d=id, - l=h+1, - pitch=pitch, - thread_depth=depth, - thread_angle=thread_angle, - profile=profile, - left_handed=left_handed, - starts=starts, - internal=true - ); - if (bevel) { - zflip_copy() { - down(h/2+0.01) { - cylinder(r1=id/2+$slop, r2=id/2+$slop-depth, h=depth, center=false); - } - } - } - } - children(); - } + depth = min((thread_depth==undef? pitch/2 : thread_depth), pitch/2/tan(thread_angle)); + attachable(anchor,spin,orient, size=[od/cos(30),od,h]) { + difference() { + cylinder(d=od/cos(30), h=h, center=true, $fn=6); + trapezoidal_threaded_rod( + d=id, + l=h+1, + pitch=pitch, + thread_depth=depth, + thread_angle=thread_angle, + profile=profile, + left_handed=left_handed, + starts=starts, + internal=true + ); + if (bevel) { + zflip_copy() { + down(h/2+0.01) { + cylinder(r1=id/2+$slop, r2=id/2+$slop-depth, h=depth, center=false); + } + } + } + } + children(); + } } @@ -381,42 +381,42 @@ module trapezoidal_threaded_nut( // threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1); // threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1); module threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - bevel=false, - internal=false, - anchor=CENTER, - spin=0, - orient=UP + d=10, l=100, pitch=2, + left_handed=false, + bevel=false, + internal=false, + anchor=CENTER, + spin=0, + orient=UP ) { - depth = pitch * cos(30) * 5/8; - profile = internal? [ - [-6/16, -depth/pitch], - [-1/16, 0], - [-1/32, 0.02], - [ 1/32, 0.02], - [ 1/16, 0], - [ 6/16, -depth/pitch] - ] : [ - [-7/16, -depth/pitch*1.07], - [-6/16, -depth/pitch], - [-1/16, 0], - [ 1/16, 0], - [ 6/16, -depth/pitch], - [ 7/16, -depth/pitch*1.07] - ]; - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_depth=depth, - thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + depth = pitch * cos(30) * 5/8; + profile = internal? [ + [-6/16, -depth/pitch], + [-1/16, 0], + [-1/32, 0.02], + [ 1/32, 0.02], + [ 1/16, 0], + [ 6/16, -depth/pitch] + ] : [ + [-7/16, -depth/pitch*1.07], + [-6/16, -depth/pitch], + [-1/16, 0], + [ 1/16, 0], + [ 6/16, -depth/pitch], + [ 7/16, -depth/pitch*1.07] + ]; + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_depth=depth, + thread_angle=30, + profile=profile, + left_handed=left_handed, + bevel=bevel, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -439,28 +439,28 @@ module threaded_rod( // Examples(Med): // threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.2, $fa=1, $fs=1); module threaded_nut( - od=16, id=10, h=10, - pitch=2, left_handed=false, bevel=false, - anchor=CENTER, spin=0, orient=UP + od=16, id=10, h=10, + pitch=2, left_handed=false, bevel=false, + anchor=CENTER, spin=0, orient=UP ) { - depth = pitch * cos(30) * 5/8; - profile = [ - [-6/16, -depth/pitch], - [-1/16, 0], - [-1/32, 0.02], - [ 1/32, 0.02], - [ 1/16, 0], - [ 6/16, -depth/pitch] - ]; - trapezoidal_threaded_nut( - od=od, id=id, h=h, - pitch=pitch, thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - anchor=anchor, spin=spin, - orient=orient - ) children(); + depth = pitch * cos(30) * 5/8; + profile = [ + [-6/16, -depth/pitch], + [-1/16, 0], + [-1/32, 0.02], + [ 1/32, 0.02], + [ 1/16, 0], + [ 6/16, -depth/pitch] + ]; + trapezoidal_threaded_nut( + od=od, id=id, h=h, + pitch=pitch, thread_angle=30, + profile=profile, + left_handed=left_handed, + bevel=bevel, + anchor=anchor, spin=spin, + orient=orient + ) children(); } @@ -488,34 +488,34 @@ module threaded_nut( // buttress_threaded_rod(d=10, l=20, pitch=1.25, left_handed=true, $fa=1, $fs=1); // buttress_threaded_rod(d=25, l=20, pitch=2, $fa=1, $fs=1); module buttress_threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - bevel=false, - internal=false, - anchor=CENTER, - spin=0, - orient=UP + d=10, l=100, pitch=2, + left_handed=false, + bevel=false, + internal=false, + anchor=CENTER, + spin=0, + orient=UP ) { - depth = pitch * 3/4; - profile = [ - [ -7/16, -0.75], - [ 5/16, 0], - [ 7/16, 0], - [ 7/16, -0.75], - [ 1/ 2, -0.77], - ]; - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_depth=depth, - thread_angle=30, - profile=profile, - left_handed=left_handed, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + depth = pitch * 3/4; + profile = [ + [ -7/16, -0.75], + [ 5/16, 0], + [ 7/16, 0], + [ 7/16, -0.75], + [ 1/ 2, -0.77], + ]; + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_depth=depth, + thread_angle=30, + profile=profile, + left_handed=left_handed, + bevel=bevel, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -538,31 +538,31 @@ module buttress_threaded_rod( // Examples(Med): // buttress_threaded_nut(od=16, id=8, h=8, pitch=1.25, left_handed=true, $slop=0.2, $fa=1, $fs=1); module buttress_threaded_nut( - od=16, id=10, h=10, - pitch=2, left_handed=false, - bevel=false, - anchor=CENTER, - spin=0, - orient=UP + od=16, id=10, h=10, + pitch=2, left_handed=false, + bevel=false, + anchor=CENTER, + spin=0, + orient=UP ) { - depth = pitch * 3/4; - profile = [ - [ -7/16, -0.75], - [ 5/16, 0], - [ 7/16, 0], - [ 7/16, -0.75], - [ 1/ 2, -0.77], - ]; - trapezoidal_threaded_nut( - od=od, id=id, h=h, - pitch=pitch, thread_angle=30, - profile=profile, - thread_depth=pitch*3*sqrt(3)/8, - left_handed=left_handed, - bevel=bevel, - anchor=anchor, spin=spin, - orient=orient - ) children(); + depth = pitch * 3/4; + profile = [ + [ -7/16, -0.75], + [ 5/16, 0], + [ 7/16, 0], + [ 7/16, -0.75], + [ 1/ 2, -0.77], + ]; + trapezoidal_threaded_nut( + od=od, id=id, h=h, + pitch=pitch, thread_angle=30, + profile=profile, + thread_depth=pitch*3*sqrt(3)/8, + left_handed=left_handed, + bevel=bevel, + anchor=anchor, spin=spin, + orient=orient + ) children(); } @@ -590,27 +590,27 @@ module buttress_threaded_nut( // Examples(Med): // metric_trapezoidal_threaded_rod(d=10, l=30, pitch=2, left_handed=true, $fa=1, $fs=1); module metric_trapezoidal_threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - starts=1, - bevel=false, - internal=false, - anchor=CENTER, - spin=0, - orient=UP + d=10, l=100, pitch=2, + left_handed=false, + starts=1, + bevel=false, + internal=false, + anchor=CENTER, + spin=0, + orient=UP ) { - trapezoidal_threaded_rod( - d=d, l=l, - pitch=pitch, - thread_angle=15, - left_handed=left_handed, - starts=starts, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + trapezoidal_threaded_rod( + d=d, l=l, + pitch=pitch, + thread_angle=15, + left_handed=left_handed, + starts=starts, + bevel=bevel, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -634,25 +634,25 @@ module metric_trapezoidal_threaded_rod( // Examples(Med): // metric_trapezoidal_threaded_nut(od=16, id=10, h=10, pitch=2, left_handed=true, bevel=true, $fa=1, $fs=1); module metric_trapezoidal_threaded_nut( - od=17.4, id=10.5, h=10, - pitch=3.175, - starts=1, - left_handed=false, - bevel=false, - anchor=CENTER, - spin=0, - orient=UP + od=17.4, id=10.5, h=10, + pitch=3.175, + starts=1, + left_handed=false, + bevel=false, + anchor=CENTER, + spin=0, + orient=UP ) { - trapezoidal_threaded_nut( - od=od, id=id, h=h, - pitch=pitch, thread_angle=15, - left_handed=left_handed, - starts=starts, - bevel=bevel, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + trapezoidal_threaded_nut( + od=od, id=id, h=h, + pitch=pitch, thread_angle=15, + left_handed=left_handed, + starts=starts, + bevel=bevel, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -683,29 +683,29 @@ module metric_trapezoidal_threaded_nut( // acme_threaded_rod(d=3/8*25.4, l=20, pitch=1/8*25.4, $fn=32); // acme_threaded_rod(d=10, l=30, pitch=2, starts=3, $fa=1, $fs=1); module acme_threaded_rod( - d=10, l=100, pitch=2, - thread_angle=14.5, - thread_depth=undef, - starts=1, - left_handed=false, - bevel=false, - internal=false, - anchor=CENTER, - spin=0, - orient=UP + d=10, l=100, pitch=2, + thread_angle=14.5, + thread_depth=undef, + starts=1, + left_handed=false, + bevel=false, + internal=false, + anchor=CENTER, + spin=0, + orient=UP ) { - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_angle=thread_angle, - thread_depth=thread_depth, - starts=starts, - left_handed=left_handed, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_angle=thread_angle, + thread_depth=thread_depth, + starts=starts, + left_handed=left_handed, + bevel=bevel, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -731,27 +731,27 @@ module acme_threaded_rod( // acme_threaded_nut(od=16, id=3/8*25.4, h=8, pitch=1/8*25.4, $slop=0.2); // acme_threaded_nut(od=16, id=10, h=10, pitch=2, starts=3, $slop=0.2, $fa=1, $fs=1); module acme_threaded_nut( - od, id, h, pitch, - thread_angle=14.5, - thread_depth=undef, - starts=1, - left_handed=false, - bevel=false, - anchor=CENTER, - spin=0, - orient=UP + od, id, h, pitch, + thread_angle=14.5, + thread_depth=undef, + starts=1, + left_handed=false, + bevel=false, + anchor=CENTER, + spin=0, + orient=UP ) { - trapezoidal_threaded_nut( - od=od, id=id, h=h, pitch=pitch, - thread_depth=thread_depth, - thread_angle=thread_angle, - left_handed=left_handed, - bevel=bevel, - starts=starts, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + trapezoidal_threaded_nut( + od=od, id=id, h=h, pitch=pitch, + thread_depth=thread_depth, + thread_angle=thread_angle, + left_handed=left_handed, + bevel=bevel, + starts=starts, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -779,26 +779,26 @@ module acme_threaded_nut( // Examples(Med): // square_threaded_rod(d=10, l=20, pitch=2, starts=2, $fn=32); module square_threaded_rod( - d=10, l=100, pitch=2, - left_handed=false, - bevel=false, - starts=1, - internal=false, - anchor=CENTER, - spin=0, - orient=UP + d=10, l=100, pitch=2, + left_handed=false, + bevel=false, + starts=1, + internal=false, + anchor=CENTER, + spin=0, + orient=UP ) { - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_angle=0, - left_handed=left_handed, - bevel=bevel, - starts=starts, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_angle=0, + left_handed=left_handed, + bevel=bevel, + starts=starts, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -822,25 +822,25 @@ module square_threaded_rod( // Examples(Med): // square_threaded_nut(od=16, id=10, h=10, pitch=2, starts=2, $slop=0.15, $fn=32); module square_threaded_nut( - od=17.4, id=10.5, h=10, - pitch=3.175, - left_handed=false, - bevel=false, - starts=1, - anchor=CENTER, - spin=0, - orient=UP + od=17.4, id=10.5, h=10, + pitch=3.175, + left_handed=false, + bevel=false, + starts=1, + anchor=CENTER, + spin=0, + orient=UP ) { - trapezoidal_threaded_nut( - od=od, id=id, h=h, pitch=pitch, - thread_angle=0, - left_handed=left_handed, - bevel=bevel, - starts=starts, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + trapezoidal_threaded_nut( + od=od, id=id, h=h, pitch=pitch, + thread_angle=0, + left_handed=left_handed, + bevel=bevel, + starts=starts, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } @@ -874,32 +874,32 @@ module square_threaded_nut( // ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, $fa=1, $fs=1); // ball_screw_rod(d=15, l=20, pitch=5, ball_diam=4, ball_arc=120, left_handed=true, $fa=1, $fs=1); module ball_screw_rod( - d=10, l=100, pitch=2, starts=1, - ball_diam=5, ball_arc=100, - left_handed=false, - internal=false, - bevel=false, - anchor=CENTER, - spin=0, - orient=UP + d=10, l=100, pitch=2, starts=1, + ball_diam=5, ball_arc=100, + left_handed=false, + internal=false, + bevel=false, + anchor=CENTER, + spin=0, + orient=UP ) { - depth = ball_diam * (1-cos(ball_arc/2))/2; - profile = arc(N=11, d=ball_diam/pitch, cp=[0,ball_diam/2/pitch*cos(ball_arc/2)], start=270-ball_arc/2, angle=ball_arc); - trapezoidal_threaded_rod( - d=d, l=l, pitch=pitch, - thread_depth=depth, - thread_angle=90-ball_arc/2, - profile=profile, - left_handed=left_handed, - starts=starts, - bevel=bevel, - internal=internal, - anchor=anchor, - spin=spin, - orient=orient - ) children(); + depth = ball_diam * (1-cos(ball_arc/2))/2; + profile = arc(N=11, d=ball_diam/pitch, cp=[0,ball_diam/2/pitch*cos(ball_arc/2)], start=270-ball_arc/2, angle=ball_arc); + trapezoidal_threaded_rod( + d=d, l=l, pitch=pitch, + thread_depth=depth, + thread_angle=90-ball_arc/2, + profile=profile, + left_handed=left_handed, + starts=starts, + bevel=bevel, + internal=internal, + anchor=anchor, + spin=spin, + orient=orient + ) children(); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/torx_drive.scad b/torx_drive.scad index 573d1e8..1206b33 100644 --- a/torx_drive.scad +++ b/torx_drive.scad @@ -17,22 +17,22 @@ // Arguments: // size = Torx size. function torx_outer_diam(size) = lookup(size, [ - [ 6, 1.75], - [ 8, 2.40], - [ 10, 2.80], - [ 15, 3.35], - [ 20, 3.95], - [ 25, 4.50], - [ 30, 5.60], - [ 40, 6.75], - [ 45, 7.93], - [ 50, 8.95], - [ 55, 11.35], - [ 60, 13.45], - [ 70, 15.70], - [ 80, 17.75], - [ 90, 20.20], - [100, 22.40] + [ 6, 1.75], + [ 8, 2.40], + [ 10, 2.80], + [ 15, 3.35], + [ 20, 3.95], + [ 25, 4.50], + [ 30, 5.60], + [ 40, 6.75], + [ 45, 7.93], + [ 50, 8.95], + [ 55, 11.35], + [ 60, 13.45], + [ 70, 15.70], + [ 80, 17.75], + [ 90, 20.20], + [100, 22.40] ]); @@ -41,22 +41,22 @@ function torx_outer_diam(size) = lookup(size, [ // Arguments: // size = Torx size. function torx_inner_diam(size) = lookup(size, [ - [ 6, 1.27], - [ 8, 1.75], - [ 10, 2.05], - [ 15, 2.40], - [ 20, 2.85], - [ 25, 3.25], - [ 30, 4.05], - [ 40, 4.85], - [ 45, 5.64], - [ 50, 6.45], - [ 55, 8.05], - [ 60, 9.60], - [ 70, 11.20], - [ 80, 12.80], - [ 90, 14.40], - [100, 16.00] + [ 6, 1.27], + [ 8, 1.75], + [ 10, 2.05], + [ 15, 2.40], + [ 20, 2.85], + [ 25, 3.25], + [ 30, 4.05], + [ 40, 4.85], + [ 45, 5.64], + [ 50, 6.45], + [ 55, 8.05], + [ 60, 9.60], + [ 70, 11.20], + [ 80, 12.80], + [ 90, 14.40], + [100, 16.00] ]); @@ -65,22 +65,22 @@ function torx_inner_diam(size) = lookup(size, [ // Arguments: // size = Torx size. function torx_depth(size) = lookup(size, [ - [ 6, 1.82], - [ 8, 3.05], - [ 10, 3.56], - [ 15, 3.81], - [ 20, 4.07], - [ 25, 4.45], - [ 30, 4.95], - [ 40, 5.59], - [ 45, 6.22], - [ 50, 6.48], - [ 55, 6.73], - [ 60, 8.17], - [ 70, 8.96], - [ 80, 9.90], - [ 90, 10.56], - [100, 11.35] + [ 6, 1.82], + [ 8, 3.05], + [ 10, 3.56], + [ 15, 3.81], + [ 20, 4.07], + [ 25, 4.45], + [ 30, 4.95], + [ 40, 5.59], + [ 45, 6.22], + [ 50, 6.48], + [ 55, 6.73], + [ 60, 8.17], + [ 70, 8.96], + [ 80, 9.90], + [ 90, 10.56], + [100, 11.35] ]); @@ -89,22 +89,22 @@ function torx_depth(size) = lookup(size, [ // Arguments: // size = Torx size. function torx_tip_radius(size) = lookup(size, [ - [ 6, 0.132], - [ 8, 0.190], - [ 10, 0.229], - [ 15, 0.267], - [ 20, 0.305], - [ 25, 0.375], - [ 30, 0.451], - [ 40, 0.546], - [ 45, 0.574], - [ 50, 0.775], - [ 55, 0.867], - [ 60, 1.067], - [ 70, 1.194], - [ 80, 1.526], - [ 90, 1.530], - [100, 1.720] + [ 6, 0.132], + [ 8, 0.190], + [ 10, 0.229], + [ 15, 0.267], + [ 20, 0.305], + [ 25, 0.375], + [ 30, 0.451], + [ 40, 0.546], + [ 45, 0.574], + [ 50, 0.775], + [ 55, 0.867], + [ 60, 1.067], + [ 70, 1.194], + [ 80, 1.526], + [ 90, 1.530], + [100, 1.720] ]); @@ -113,22 +113,22 @@ function torx_tip_radius(size) = lookup(size, [ // Arguments: // size = Torx size. function torx_rounding_radius(size) = lookup(size, [ - [ 6, 0.383], - [ 8, 0.510], - [ 10, 0.598], - [ 15, 0.716], - [ 20, 0.859], - [ 25, 0.920], - [ 30, 1.194], - [ 40, 1.428], - [ 45, 1.796], - [ 50, 1.816], - [ 55, 2.667], - [ 60, 2.883], - [ 70, 3.477], - [ 80, 3.627], - [ 90, 4.468], - [100, 4.925] + [ 6, 0.383], + [ 8, 0.510], + [ 10, 0.598], + [ 15, 0.716], + [ 20, 0.859], + [ 25, 0.920], + [ 30, 1.194], + [ 40, 1.428], + [ 45, 1.796], + [ 50, 1.816], + [ 55, 2.667], + [ 60, 2.883], + [ 70, 3.477], + [ 80, 3.627], + [ 90, 4.468], + [100, 4.925] ]); @@ -142,33 +142,33 @@ function torx_rounding_radius(size) = lookup(size, [ // Example(2D): // torx_drive2d(size=30, $fa=1, $fs=1); module torx_drive2d(size) { - od = torx_outer_diam(size); - id = torx_inner_diam(size); - tip = torx_tip_radius(size); - rounding = torx_rounding_radius(size); - base = od - 2*tip; - $fn = quantup(segs(od/2),12); - difference() { - union() { - circle(d=base); - zrot_copies(n=2) { - hull() { - zrot_copies(n=3) { - translate([base/2,0,0]) { - circle(r=tip, $fn=$fn/2); - } - } - } - } - } - zrot_copies(n=6) { - zrot(180/6) { - translate([id/2+rounding,0,0]) { - circle(r=rounding); - } - } - } - } + od = torx_outer_diam(size); + id = torx_inner_diam(size); + tip = torx_tip_radius(size); + rounding = torx_rounding_radius(size); + base = od - 2*tip; + $fn = quantup(segs(od/2),12); + difference() { + union() { + circle(d=base); + zrot_copies(n=2) { + hull() { + zrot_copies(n=3) { + translate([base/2,0,0]) { + circle(r=tip, $fn=$fn/2); + } + } + } + } + } + zrot_copies(n=6) { + zrot(180/6) { + translate([id/2+rounding,0,0]) { + circle(r=rounding); + } + } + } + } } @@ -185,17 +185,17 @@ module torx_drive2d(size) { // Examples: // torx_drive(size=30, l=10, $fa=1, $fs=1); module torx_drive(size, l=5, center, anchor, spin=0, orient=UP) { - anchor = get_anchor(anchor, center, BOT, BOT); - od = torx_outer_diam(size); - attachable(anchor,spin,orient, d=od, l=l) { - linear_extrude(height=l, convexity=4, center=true) { - torx_drive2d(size); - } - children(); - } + anchor = get_anchor(anchor, center, BOT, BOT); + od = torx_outer_diam(size); + attachable(anchor,spin,orient, d=od, l=l) { + linear_extrude(height=l, convexity=4, center=true) { + torx_drive2d(size); + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/transforms.scad b/transforms.scad index c0e6d74..be73fbc 100644 --- a/transforms.scad +++ b/transforms.scad @@ -68,20 +68,20 @@ // mat3d = move([2,3,4]); // Returns: [[1,0,0,2],[0,1,0,3],[0,0,1,4],[0,0,0,1]] module move(v=[0,0,0], x=0, y=0, z=0) { - translate(point3d(v)+[x,y,z]) children(); + translate(point3d(v)+[x,y,z]) children(); } function move(v=[0,0,0], p=undef, x=0, y=0, z=0) = - is_undef(p)? ( - len(v)==2? affine2d_translate(v+[x,y]) : - affine3d_translate(point3d(v)+[x,y,z]) - ) : ( - assert(is_list(p)) - let(v=v+[x,y,z]) - is_num(p.x)? p+v : - is_vnf(p)? [move(v=v,p=p.x), p.y] : - [for (l=p) is_vector(l)? l+v : move(v=v, p=l)] - ); + is_undef(p)? ( + len(v)==2? affine2d_translate(v+[x,y]) : + affine3d_translate(point3d(v)+[x,y,z]) + ) : ( + assert(is_list(p)) + let(v=v+[x,y,z]) + is_num(p.x)? p+v : + is_vnf(p)? [move(v=v,p=p.x), p.y] : + [for (l=p) is_vector(l)? l+v : move(v=v, p=l)] + ); function translate(v=[0,0,0], p=undef) = move(v=v, p=p); @@ -337,57 +337,57 @@ function up(z=0,p=undef) = move([0,0,z],p=p); // stroke(rot(30,p=path), closed=true); module rot(a=0, v=undef, cp=undef, from=undef, to=undef, reverse=false) { - m = rot(a=a, v=v, cp=cp, from=from, to=to, reverse=reverse, planar=false); - multmatrix(m) children(); + m = rot(a=a, v=v, cp=cp, from=from, to=to, reverse=reverse, planar=false); + multmatrix(m) children(); } function rot(a=0, v, cp, from, to, reverse=false, planar=false, p, _m) = - assert(is_undef(from)==is_undef(to), "from and to must be specified together.") - is_undef(p)? ( - planar? let( - cp = is_undef(cp)? cp : point2d(cp), - m1 = is_undef(from)? affine2d_zrot(a) : - assert(is_vector(from)) - assert(!approx(norm(from),0)) - assert(approx(point3d(from).z, 0)) - assert(is_vector(to)) - assert(!approx(norm(to),0)) - assert(approx(point3d(to).z, 0)) - affine2d_zrot( - vang(point2d(to)) - - vang(point2d(from)) - ), - m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)), - m3 = reverse? matrix_inverse(m2) : m2 - ) m3 : let( - from = is_undef(from)? undef : point3d(from), - to = is_undef(to)? undef : point3d(to), - cp = is_undef(cp)? undef : point3d(cp), - m1 = !is_undef(from)? ( - assert(is_vector(from)) - assert(!approx(norm(from),0)) - assert(is_vector(to)) - assert(!approx(norm(to),0)) - affine3d_rot_from_to(from,to) * affine3d_zrot(a) - ) : - !is_undef(v)? affine3d_rot_by_axis(v,a) : - is_num(a)? affine3d_zrot(a) : - affine3d_zrot(a.z) * affine3d_yrot(a.y) * affine3d_xrot(a.x), - m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)), - m3 = reverse? matrix_inverse(m2) : m2 - ) m3 - ) : ( - assert(is_list(p)) - let( - m = !is_undef(_m)? _m : - rot(a=a, v=v, cp=cp, from=from, to=to, reverse=reverse, planar=planar), - res = p==[]? [] : - is_vector(p)? apply(m, p) : - is_vnf(p)? [apply(m, p[0]), p[1]] : - is_list(p[0])? [for (pp=p) rot(p=pp, _m=m)] : - assert(false, "The p argument for rot() is not a point, path, patch, matrix, or VNF.") - ) res - ); + assert(is_undef(from)==is_undef(to), "from and to must be specified together.") + is_undef(p)? ( + planar? let( + cp = is_undef(cp)? cp : point2d(cp), + m1 = is_undef(from)? affine2d_zrot(a) : + assert(is_vector(from)) + assert(!approx(norm(from),0)) + assert(approx(point3d(from).z, 0)) + assert(is_vector(to)) + assert(!approx(norm(to),0)) + assert(approx(point3d(to).z, 0)) + affine2d_zrot( + vang(point2d(to)) - + vang(point2d(from)) + ), + m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)), + m3 = reverse? matrix_inverse(m2) : m2 + ) m3 : let( + from = is_undef(from)? undef : point3d(from), + to = is_undef(to)? undef : point3d(to), + cp = is_undef(cp)? undef : point3d(cp), + m1 = !is_undef(from)? ( + assert(is_vector(from)) + assert(!approx(norm(from),0)) + assert(is_vector(to)) + assert(!approx(norm(to),0)) + affine3d_rot_from_to(from,to) * affine3d_zrot(a) + ) : + !is_undef(v)? affine3d_rot_by_axis(v,a) : + is_num(a)? affine3d_zrot(a) : + affine3d_zrot(a.z) * affine3d_yrot(a.y) * affine3d_xrot(a.x), + m2 = is_undef(cp)? m1 : (move(cp) * m1 * move(-cp)), + m3 = reverse? matrix_inverse(m2) : m2 + ) m3 + ) : ( + assert(is_list(p)) + let( + m = !is_undef(_m)? _m : + rot(a=a, v=v, cp=cp, from=from, to=to, reverse=reverse, planar=planar), + res = p==[]? [] : + is_vector(p)? apply(m, p) : + is_vnf(p)? [apply(m, p[0]), p[1]] : + is_list(p[0])? [for (pp=p) rot(p=pp, _m=m)] : + assert(false, "The p argument for rot() is not a point, path, patch, matrix, or VNF.") + ) res + ); @@ -421,13 +421,13 @@ function rot(a=0, v, cp, from, to, reverse=false, planar=false, p, _m) = // xrot(90) cylinder(h=50, r=10, center=true); module xrot(a=0, cp=undef) { - if (a==0) { - children(); // May be slightly faster? - } else if (!is_undef(cp)) { - translate(cp) rotate([a, 0, 0]) translate(-cp) children(); - } else { - rotate([a, 0, 0]) children(); - } + if (a==0) { + children(); // May be slightly faster? + } else if (!is_undef(cp)) { + translate(cp) rotate([a, 0, 0]) translate(-cp) children(); + } else { + rotate([a, 0, 0]) children(); + } } function xrot(a=0, cp=undef, p=undef) = rot([a,0,0], cp=cp, p=p); @@ -462,13 +462,13 @@ function xrot(a=0, cp=undef, p=undef) = rot([a,0,0], cp=cp, p=p); // yrot(90) cylinder(h=50, r=10, center=true); module yrot(a=0, cp=undef) { - if (a==0) { - children(); // May be slightly faster? - } else if (!is_undef(cp)) { - translate(cp) rotate([0, a, 0]) translate(-cp) children(); - } else { - rotate([0, a, 0]) children(); - } + if (a==0) { + children(); // May be slightly faster? + } else if (!is_undef(cp)) { + translate(cp) rotate([0, a, 0]) translate(-cp) children(); + } else { + rotate([0, a, 0]) children(); + } } function yrot(a=0, cp=undef, p=undef) = rot([0,a,0], cp=cp, p=p); @@ -503,13 +503,13 @@ function yrot(a=0, cp=undef, p=undef) = rot([0,a,0], cp=cp, p=p); // zrot(90) cube(size=[60,20,40], center=true); module zrot(a=0, cp=undef) { - if (a==0) { - children(); // May be slightly faster? - } else if (!is_undef(cp)) { - translate(cp) rotate(a) translate(-cp) children(); - } else { - rotate(a) children(); - } + if (a==0) { + children(); // May be slightly faster? + } else if (!is_undef(cp)) { + translate(cp) rotate(a) translate(-cp) children(); + } else { + rotate(a) children(); + } } function zrot(a=0, cp=undef, p=undef) = rot(a, cp=cp, p=p); @@ -551,20 +551,20 @@ function zrot(a=0, cp=undef, p=undef) = rot(a, cp=cp, p=p); // #stroke(path,closed=true); // stroke(scale([1.5,3],p=path),closed=true); function scale(v=1, p=undef) = - assert(is_num(v) || is_vector(v)) - assert(is_undef(p) || is_list(p)) - let(v = is_num(v)? [v,v,v] : v) - is_undef(p)? ( - len(v)==2? affine2d_scale(v) : affine3d_scale(point3d(v)) - ) : ( - assert(is_list(p)) - is_num(p.x)? vmul(p,v) : - is_vnf(p)? let(inv=product([for (x=v) x<0? -1 : 1])) [ - scale(v=v,p=p.x), - inv>=0? p.y : [for (l=p.y) reverse(l)] - ] : - [for (l=p) is_vector(l)? vmul(l,v) : scale(v=v, p=l)] - ); + assert(is_num(v) || is_vector(v)) + assert(is_undef(p) || is_list(p)) + let(v = is_num(v)? [v,v,v] : v) + is_undef(p)? ( + len(v)==2? affine2d_scale(v) : affine3d_scale(point3d(v)) + ) : ( + assert(is_list(p)) + is_num(p.x)? vmul(p,v) : + is_vnf(p)? let(inv=product([for (x=v) x<0? -1 : 1])) [ + scale(v=v,p=p.x), + inv>=0? p.y : [for (l=p.y) reverse(l)] + ] : + [for (l=p) is_vector(l)? vmul(l,v) : scale(v=v, p=l)] + ); // Function&Module: xscale() @@ -737,13 +737,13 @@ function zscale(z=1, p=undef) = scale([1,1,z],p=p); // #stroke(path,closed=true); // stroke(mirror(n, p=path),closed=true); function mirror(v, p) = - assert(is_vector(v)) - assert(is_undef(p) || is_list(p)) - let(m = len(v)==2? affine2d_mirror(v) : affine3d_mirror(v)) - is_undef(p)? m : - is_num(p.x)? apply(m,p) : - is_vnf(p)? [mirror(v=v,p=p[0]), [for (face=p[1]) reverse(face)]] : - [for (l=p) is_vector(l)? apply(m,l) : mirror(v=v, p=l)]; + assert(is_vector(v)) + assert(is_undef(p) || is_list(p)) + let(m = len(v)==2? affine2d_mirror(v) : affine3d_mirror(v)) + is_undef(p)? m : + is_num(p.x)? apply(m,p) : + is_vnf(p)? [mirror(v=v,p=p[0]), [for (face=p[1]) reverse(face)]] : + [for (l=p) is_vector(l)? apply(m,l) : mirror(v=v, p=l)]; // Function&Module: xflip() @@ -780,8 +780,8 @@ function mirror(v, p) = module xflip(x=0) translate([x,0,0]) mirror([1,0,0]) translate([-x,0,0]) children(); function xflip(x=0,p) = - x==0? mirror([1,0,0],p=p) : - move([x,0,0],p=mirror([1,0,0],p=move([-x,0,0],p=p))); + x==0? mirror([1,0,0],p=p) : + move([x,0,0],p=mirror([1,0,0],p=move([-x,0,0],p=p))); // Function&Module: yflip() @@ -818,8 +818,8 @@ function xflip(x=0,p) = module yflip(y=0) translate([0,y,0]) mirror([0,1,0]) translate([0,-y,0]) children(); function yflip(y=0,p) = - y==0? mirror([0,1,0],p=p) : - move([0,y,0],p=mirror([0,1,0],p=move([0,-y,0],p=p))); + y==0? mirror([0,1,0],p=p) : + move([0,y,0],p=mirror([0,1,0],p=move([0,-y,0],p=p))); @@ -857,8 +857,8 @@ function yflip(y=0,p) = module zflip(z=0) translate([0,0,z]) mirror([0,0,1]) translate([0,0,-z]) children(); function zflip(z=0,p) = - z==0? mirror([0,0,1],p=p) : - move([0,0,z],p=mirror([0,0,1],p=move([0,0,-z],p=p))); + z==0? mirror([0,0,1],p=p) : + move([0,0,z],p=mirror([0,0,1],p=move([0,0,-z],p=p))); @@ -918,29 +918,29 @@ function zflip(z=0,p) = // trace_polyline(close_path(pts), showpts=true); module skew(sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0) { - multmatrix( - affine3d_skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy) - ) children(); + multmatrix( + affine3d_skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy) + ) children(); } function skew(p, sxy=0, sxz=0, syx=0, syz=0, szx=0, szy=0, planar=false) = - let( - planar = planar || (is_list(p) && is_num(p.x) && len(p)==2), - m = planar? [ - [ 1, sxy, 0], - [syx, 1, 0], - [ 0, 0, 1] - ] : affine3d_skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy) - ) - is_undef(p)? m : - assert(is_list(p)) - is_num(p.x)? ( - planar? - point2d(m*concat(point2d(p),[1])) : - point3d(m*concat(point3d(p),[1])) - ) : - is_vnf(p)? [skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy, planar=planar, p=p.x), p.y] : - [for (l=p) skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy, planar=planar, p=l)]; + let( + planar = planar || (is_list(p) && is_num(p.x) && len(p)==2), + m = planar? [ + [ 1, sxy, 0], + [syx, 1, 0], + [ 0, 0, 1] + ] : affine3d_skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy) + ) + is_undef(p)? m : + assert(is_list(p)) + is_num(p.x)? ( + planar? + point2d(m*concat(point2d(p),[1])) : + point3d(m*concat(point3d(p),[1])) + ) : + is_vnf(p)? [skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy, planar=planar, p=p.x), p.y] : + [for (l=p) skew(sxy=sxy, sxz=sxz, syx=syx, syz=syz, szx=szx, szy=szy, planar=planar, p=l)]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/triangulation.scad b/triangulation.scad index 3da350d..9dbd2ef 100644 --- a/triangulation.scad +++ b/triangulation.scad @@ -20,17 +20,17 @@ // points = Array of vertices for the polyhedron. // face = The face, given as a list of indices into the vertex array `points`. function face_normal(points, face) = - let(count=len(face)) - unit( - sum( - [ - for(i=[0:1:count-1]) cross( - points[face[(i+1)%count]]-points[face[0]], - points[face[(i+2)%count]]-points[face[(i+1)%count]] - ) - ] - ) - ) + let(count=len(face)) + unit( + sum( + [ + for(i=[0:1:count-1]) cross( + points[face[(i+1)%count]]-points[face[0]], + points[face[(i+2)%count]]-points[face[(i+1)%count]] + ) + ] + ) + ) ; @@ -42,16 +42,16 @@ function face_normal(points, face) = // face = The face, given as a list of indices into the vertex array `points`. // facenorm = The normal vector of the face. function find_convex_vertex(points, face, facenorm, i=0) = - let(count=len(face), - p0=points[face[i]], - p1=points[face[(i+1)%count]], - p2=points[face[(i+2)%count]] - ) - (len(face)>i)? ( - (cross(p1-p0, p2-p1)*facenorm>0)? (i+1)%count : - find_convex_vertex(points, face, facenorm, i+1) - ) : //This should never happen since there is at least 1 convex vertex. - undef + let(count=len(face), + p0=points[face[i]], + p1=points[face[(i+1)%count]], + p2=points[face[(i+2)%count]] + ) + (len(face)>i)? ( + (cross(p1-p0, p2-p1)*facenorm>0)? (i+1)%count : + find_convex_vertex(points, face, facenorm, i+1) + ) : //This should never happen since there is at least 1 convex vertex. + undef ; @@ -61,27 +61,27 @@ function find_convex_vertex(points, face, facenorm, i=0) = // points = Array of vertices for the polyhedron. // face = The face, given as a list of indices into the vertex array `points`. function point_in_ear(points, face, tests, i=0) = - (iprev[0])? [test, i] : prev - : - [_check_point_in_ear(points[face[i]], tests), i] + (iprev[0])? [test, i] : prev + : + [_check_point_in_ear(points[face[i]], tests), i] ; // Internal non-exposed function. function _check_point_in_ear(point, tests) = - let( - result=[ - (point*tests[0][0])-tests[0][1], - (point*tests[1][0])-tests[1][1], - (point*tests[2][0])-tests[2][1] - ] - ) - (result[0]>0 && result[1]>0 && result[2]>0)? result[0] : -1 + let( + result=[ + (point*tests[0][0])-tests[0][1], + (point*tests[1][0])-tests[1][1], + (point*tests[2][0])-tests[2][1] + ] + ) + (result[0]>0 && result[1]>0 && result[2]>0)? result[0] : -1 ; @@ -90,10 +90,10 @@ function _check_point_in_ear(point, tests) = // Arguments: // v = The array to normalize. function normalize_vertex_perimeter(v) = - let(lv = len(v)) - (lv < 2)? v : - (v[lv-1] != v[0])? v : - [for (i=[0:1:lv-2]) v[i]] + let(lv = len(v)) + (lv < 2)? v : + (v[lv-1] != v[0])? v : + [for (i=[0:1:lv-2]) v[i]] ; @@ -106,20 +106,20 @@ function normalize_vertex_perimeter(v) = // facelist = The face, given as a list of indices into the vertex array `points`. // vertex = The index into `facelist`, of the vertex to test. function is_only_noncolinear_vertex(points, facelist, vertex) = - let( - face=select(facelist, vertex+1, vertex-1), - count=len(face) - ) - 0==sum( - [ - for(i=[0:1:count-1]) norm( - cross( - points[face[(i+1)%count]]-points[face[0]], - points[face[(i+2)%count]]-points[face[(i+1)%count]] - ) - ) - ] - ) + let( + face=select(facelist, vertex+1, vertex-1), + count=len(face) + ) + 0==sum( + [ + for(i=[0:1:count-1]) norm( + cross( + points[face[(i+1)%count]]-points[face[0]], + points[face[(i+2)%count]]-points[face[(i+1)%count]] + ) + ) + ] + ) ; @@ -131,50 +131,50 @@ function is_only_noncolinear_vertex(points, facelist, vertex) = // points = Array of vertices for the polyhedron. // face = The face, given as a list of indices into the vertex array `points`. function triangulate_face(points, face) = - let( - face = deduplicate_indexed(points,face), - count = len(face) - ) - (count < 3)? [] : - (count == 3)? [face] : - let( - facenorm=face_normal(points, face), - cv=find_convex_vertex(points, face, facenorm) - ) - assert(!is_undef(cv), "Cannot triangulate self-crossing face perimeters.") - let( - pv=(count+cv-1)%count, - nv=(cv+1)%count, - p0=points[face[pv]], - p1=points[face[cv]], - p2=points[face[nv]], - tests=[ - [cross(facenorm, p0-p2), cross(facenorm, p0-p2)*p0], - [cross(facenorm, p1-p0), cross(facenorm, p1-p0)*p1], - [cross(facenorm, p2-p1), cross(facenorm, p2-p1)*p2] - ], - ear_test=point_in_ear(points, face, tests), - clipable_ear=(ear_test[0]<0), - diagonal_point=ear_test[1] - ) - (clipable_ear)? // There is no point inside the ear. - is_only_noncolinear_vertex(points, face, cv)? - // In the point&line degeneracy clip to somewhere in the middle of the line. - flatten([ - triangulate_face(points, select(face, cv, (cv+2)%count)), - triangulate_face(points, select(face, (cv+2)%count, cv)) - ]) - : - // Otherwise the ear is safe to clip. - flatten([ - [select(face, pv, nv)], - triangulate_face(points, select(face, nv, pv)) - ]) - : // If there is a point inside the ear, make a diagonal and clip along that. - flatten([ - triangulate_face(points, select(face, cv, diagonal_point)), - triangulate_face(points, select(face, diagonal_point, cv)) - ]); + let( + face = deduplicate_indexed(points,face), + count = len(face) + ) + (count < 3)? [] : + (count == 3)? [face] : + let( + facenorm=face_normal(points, face), + cv=find_convex_vertex(points, face, facenorm) + ) + assert(!is_undef(cv), "Cannot triangulate self-crossing face perimeters.") + let( + pv=(count+cv-1)%count, + nv=(cv+1)%count, + p0=points[face[pv]], + p1=points[face[cv]], + p2=points[face[nv]], + tests=[ + [cross(facenorm, p0-p2), cross(facenorm, p0-p2)*p0], + [cross(facenorm, p1-p0), cross(facenorm, p1-p0)*p1], + [cross(facenorm, p2-p1), cross(facenorm, p2-p1)*p2] + ], + ear_test=point_in_ear(points, face, tests), + clipable_ear=(ear_test[0]<0), + diagonal_point=ear_test[1] + ) + (clipable_ear)? // There is no point inside the ear. + is_only_noncolinear_vertex(points, face, cv)? + // In the point&line degeneracy clip to somewhere in the middle of the line. + flatten([ + triangulate_face(points, select(face, cv, (cv+2)%count)), + triangulate_face(points, select(face, (cv+2)%count, cv)) + ]) + : + // Otherwise the ear is safe to clip. + flatten([ + [select(face, pv, nv)], + triangulate_face(points, select(face, nv, pv)) + ]) + : // If there is a point inside the ear, make a diagonal and clip along that. + flatten([ + triangulate_face(points, select(face, cv, diagonal_point)), + triangulate_face(points, select(face, diagonal_point, cv)) + ]); // Function: triangulate_faces() @@ -185,11 +185,11 @@ function triangulate_face(points, face) = // points = Array of vertices for the polyhedron. // faces = Array of faces for the polyhedron. Each face is a list of 3 or more indices into the `points` array. function triangulate_faces(points, faces) = - [ - for (face=faces) each - len(face)==3? [face] : - triangulate_face(points, normalize_vertex_perimeter(face)) - ]; + [ + for (face=faces) each + len(face)==3? [face] : + triangulate_face(points, normalize_vertex_perimeter(face)) + ]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/vectors.scad b/vectors.scad index d0bf251..8a96590 100644 --- a/vectors.scad +++ b/vectors.scad @@ -31,8 +31,8 @@ // is_vector([]); // Returns false // is_vector([3,undef,undef,true], fast=true); // Returns true function is_vector(v,length,fast=false) = - (fast? (is_list(v) && is_num(v[0])) : is_list_of(v,0)) && - len(v) && (is_undef(length) || length==len(v)); + (fast? (is_list(v) && is_num(v[0])) : is_list_of(v,0)) && + len(v) && (is_undef(length) || length==len(v)); // Function: add_scalar() @@ -58,8 +58,8 @@ 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) = - len(v)==2? atan2(v.y,v.x) : - let(res=xyz_to_spherical(v)) [res[1], 90-res[2]]; + len(v)==2? atan2(v.y,v.x) : + let(res=xyz_to_spherical(v)) [res[1], 90-res[2]]; // Function: vmul() @@ -144,22 +144,22 @@ function unit(v) = assert(is_vector(v),str(v)) norm(v)<=EPSILON? v : v/norm(v); // 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_valid, "Bad arguments to vector_angle()") - let( - norm0 = norm(vecs[0]), - norm1 = norm(vecs[1]) - ) - assert(norm0>0 && norm1>0,"Zero length vector given to vector_angle()") - // NOTE: constrain() corrects crazy FP rounding errors that exceed acos()'s domain. - acos(constrain((vecs[0]*vecs[1])/(norm0*norm1), -1, 1)); + 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_valid, "Bad arguments to vector_angle()") + let( + norm0 = norm(vecs[0]), + norm1 = norm(vecs[1]) + ) + assert(norm0>0 && norm1>0,"Zero length vector given to vector_angle()") + // NOTE: constrain() corrects crazy FP rounding errors that exceed acos()'s domain. + acos(constrain((vecs[0]*vecs[1])/(norm0*norm1), -1, 1)); // Function: vector_axis() @@ -184,22 +184,22 @@ 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_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."); -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/version.scad b/version.scad index 7847591..0f3004c 100644 --- a/version.scad +++ b/version.scad @@ -8,7 +8,7 @@ ////////////////////////////////////////////////////////////////////// -BOSL_VERSION = [2,0,331]; +BOSL_VERSION = [2,0,332]; // Section: BOSL Library Version Functions @@ -49,27 +49,27 @@ function bosl_version_str() = version_to_str(BOSL_VERSION); // Description: // Given a version as a list, number, or string, asserts that the currently installed BOSL library is at least the given version. module bosl_required(target) { - assert( - version_cmp(bosl_version(), target) >= 0, - str( - "BOSL ", bosl_version_str(), " is installed, but BOSL ", - version_to_str(target), " or better is required." - ) - ); + assert( + version_cmp(bosl_version(), target) >= 0, + str( + "BOSL ", bosl_version_str(), " is installed, but BOSL ", + version_to_str(target), " or better is required." + ) + ); } // Section: Generic Version Functions function _version_split_str(x, _i=0, _out=[], _num=0) = - _i>=len(x)? concat(_out,[_num]) : - let( - cval = ord(x[_i]) - ord("0"), - numend = cval<0 || cval>9, - _out = numend? concat(_out, [_num]) : _out, - _num = numend? 0 : (10*_num + cval) - ) - _version_split_str(x, _i=_i+1, _out=_out, _num=_num); + _i>=len(x)? concat(_out,[_num]) : + let( + cval = ord(x[_i]) - ord("0"), + numend = cval<0 || cval>9, + _out = numend? concat(_out, [_num]) : _out, + _num = numend? 0 : (10*_num + cval) + ) + _version_split_str(x, _i=_i+1, _out=_out, _num=_num); // Function: version_to_list() @@ -83,10 +83,10 @@ function _version_split_str(x, _i=0, _out=[], _num=0) = // v3 = version_to_list([2,3,4]); // Returns: [2,3,4] // v4 = version_to_list([2,3,4,5]); // Returns: [2,3,4] function version_to_list(x) = - is_list(x)? [default(x[0],0), default(x[1],0), default(x[2],0)] : - is_string(x)? _version_split_str(x) : - is_num(x)? [floor(x), floor(x*100%100), floor(x*1000000%10000+0.5)] : - assert(is_num(x) || is_vector(x) || is_string(x)) 0; + is_list(x)? [default(x[0],0), default(x[1],0), default(x[2],0)] : + is_string(x)? _version_split_str(x) : + is_num(x)? [floor(x), floor(x*100%100), floor(x*1000000%10000+0.5)] : + assert(is_num(x) || is_vector(x) || is_string(x)) 0; // Function: version_to_str() @@ -100,8 +100,8 @@ function version_to_list(x) = // v3 = version_to_str(2.340789); // Returns: "2.34.789" // v4 = version_to_str("2.3.89"); // Returns: "2.3.89" function version_to_str(x) = - let(x = version_to_list(x)) - str(x[0],".",x[1],".",x[2]); + let(x = version_to_list(x)) + str(x[0],".",x[1],".",x[2]); // Function: version_to_num() @@ -115,8 +115,8 @@ function version_to_str(x) = // v3 = version_to_num(2.120567); // Returns: 2.120567 // v4 = version_to_num("2.6.79"); // Returns: 2.060079 function version_to_num(x) = - let(x = version_to_list(x)) - (x[0]*1000000 + x[1]*10000 + x[2])/1000000; + let(x = version_to_list(x)) + (x[0]*1000000 + x[1]*10000 + x[2])/1000000; // Function: version_cmp() @@ -130,11 +130,11 @@ function version_to_num(x) = // cmp2 = version_cmp(2.010034, "2.1.34"); // Returns: 0 // cmp3 = version_cmp(2.010034, "2.1.35"); // Returns: <0 function version_cmp(a,b) = - let( - a = version_to_list(a), - b = version_to_list(b), - cmps = [for (i=[0:1:2]) if(a[i]!=b[i]) a[i]-b[i]] - ) cmps==[]? 0 : cmps[0]; + let( + a = version_to_list(a), + b = version_to_list(b), + cmps = [for (i=[0:1:2]) if(a[i]!=b[i]) a[i]-b[i]] + ) cmps==[]? 0 : cmps[0]; -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/vnf.scad b/vnf.scad index a6f716c..85858c4 100644 --- a/vnf.scad +++ b/vnf.scad @@ -29,12 +29,12 @@ EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces. // Description: // Returns true if the given value looks like a VNF structure. function is_vnf(x) = - is_list(x) && - len(x)==2 && - is_list(x[0]) && - is_list(x[1]) && - (x[0]==[] || (len(x[0])>=3 && is_vector(x[0][0]))) && - (x[1]==[] || is_vector(x[1][0])); + is_list(x) && + len(x)==2 && + is_list(x[0]) && + is_list(x[1]) && + (x[0]==[] || (len(x[0])>=3 && is_vector(x[0][0]))) && + (x[1]==[] || is_vector(x[1][0])); // Function: is_vnf_list() @@ -61,7 +61,7 @@ function vnf_faces(vnf) = vnf[1]; // vnf = The VNF to quantize. // q = The quanta to quantize the VNF coordinates to. function vnf_quantize(vnf,q=pow(2,-12)) = - [[for (pt = vnf[0]) quant(pt,q)], vnf[1]]; + [[for (pt = vnf[0]) quant(pt,q)], vnf[1]]; // Function: vnf_get_vertex() @@ -81,11 +81,11 @@ function vnf_quantize(vnf,q=pow(2,-12)) = // vnf3 = vnf_get_vertex(vnf2, p=[3,5,8]); // Returns: [0, [[[3,5,8],[3,2,1]],[]]] // vnf4 = vnf_get_vertex(vnf3, p=[[1,3,2],[3,2,1]]); // Returns: [[1,2], [[[3,5,8],[3,2,1],[1,3,2]],[]]] function vnf_get_vertex(vnf=EMPTY_VNF, p) = - let( - p = is_vector(p)? [p] : p, - res = set_union(vnf[0], p, get_indices=true) - ) - [res[0], [res[1],vnf[1]]]; + let( + p = is_vector(p)? [p] : p, + res = set_union(vnf[0], p, get_indices=true) + ) + [res[0], [res[1],vnf[1]]]; // Function: vnf_add_face() @@ -99,15 +99,15 @@ function vnf_get_vertex(vnf=EMPTY_VNF, p) = // vnf = The VNF structure to add a face to. // pts = The vertex points for the face. function vnf_add_face(vnf=EMPTY_VNF, pts) = - assert(is_vnf(vnf)) - assert(is_path(pts)) - let( - res = set_union(vnf[0], pts, get_indices=true), - face = deduplicate(res[0], closed=true) - ) [ - res[1], - concat(vnf[1], len(face)>2? [face] : []) - ]; + assert(is_vnf(vnf)) + assert(is_path(pts)) + let( + res = set_union(vnf[0], pts, get_indices=true), + face = deduplicate(res[0], closed=true) + ) [ + res[1], + concat(vnf[1], len(face)>2? [face] : []) + ]; // Function: vnf_add_faces() @@ -122,23 +122,23 @@ function vnf_add_face(vnf=EMPTY_VNF, pts) = // vnf = The VNF structure to add a face to. // faces = The list of faces, where each face is given as a list of vertex points. function vnf_add_faces(vnf=EMPTY_VNF, faces) = - assert(is_vnf(vnf)) - assert(is_list(faces)) - let( - res = set_union(vnf[0], flatten(faces), get_indices=true), - idxs = res[0], - nverts = res[1], - offs = cumsum([0, for (face=faces) len(face)]), - ifaces = [ - for (i=idx(faces)) [ - for (j=idx(faces[i])) - idxs[offs[i]+j] - ] - ] - ) [ - nverts, - concat(vnf[1],ifaces) - ]; + assert(is_vnf(vnf)) + assert(is_list(faces)) + let( + res = set_union(vnf[0], flatten(faces), get_indices=true), + idxs = res[0], + nverts = res[1], + offs = cumsum([0, for (face=faces) len(face)]), + ifaces = [ + for (i=idx(faces)) [ + for (j=idx(faces[i])) + idxs[offs[i]+j] + ] + ] + ) [ + nverts, + concat(vnf[1],ifaces) + ]; // Function: vnf_merge() @@ -147,14 +147,14 @@ function vnf_add_faces(vnf=EMPTY_VNF, faces) = // Description: // Given a list of VNF structures, merges them all into a single VNF structure. function vnf_merge(vnfs=[],_i=0,_acc=EMPTY_VNF) = - (assert(is_vnf_list(vnfs)) _i>=len(vnfs))? _acc : - vnf_merge( - vnfs, _i=_i+1, - _acc = let(base=len(_acc[0])) [ - concat(_acc[0], vnfs[_i][0]), - concat(_acc[1], [for (f=vnfs[_i][1]) [for (i=f) i+base]]), - ] - ); + (assert(is_vnf_list(vnfs)) _i>=len(vnfs))? _acc : + vnf_merge( + vnfs, _i=_i+1, + _acc = let(base=len(_acc[0])) [ + concat(_acc[0], vnfs[_i][0]), + concat(_acc[1], [for (f=vnfs[_i][1]) [for (i=f) i+base]]), + ] + ); // Function: vnf_compact() // Usage: @@ -162,15 +162,15 @@ function vnf_merge(vnfs=[],_i=0,_acc=EMPTY_VNF) = // Description: // Takes a VNF and consolidates all duplicate vertices, and drops unreferenced vertices. function vnf_compact(vnf) = - let( - vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf, - verts = vnf[0], - faces = [ - for (face=vnf[1]) [ - for (i=face) verts[i] - ] - ] - ) vnf_add_faces(faces=faces); + let( + vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf, + verts = vnf[0], + faces = [ + for (face=vnf[1]) [ + for (i=face) verts[i] + ] + ] + ) vnf_add_faces(faces=faces); // Function: vnf_triangulate() @@ -179,10 +179,10 @@ function vnf_compact(vnf) = // Description: // Forces triangulation of faces in the VNF that have more than 3 vertices. function vnf_triangulate(vnf) = - let( - vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf, - verts = vnf[0] - ) [verts, triangulate_faces(verts, vnf[1])]; + let( + vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf, + verts = vnf[0] + ) [verts, triangulate_faces(verts, vnf[1])]; // Function: vnf_vertex_array() @@ -257,79 +257,79 @@ function vnf_triangulate(vnf) = // vnf3 = vnf_vertex_array(points=cap2, col_wrap=true, reverse=true); // vnf_polyhedron([vnf1, vnf2, vnf3]); function vnf_vertex_array( - points, - caps, cap1, cap2, - col_wrap=false, - row_wrap=false, - reverse=false, - style="default", - vnf=EMPTY_VNF + points, + caps, cap1, cap2, + col_wrap=false, + row_wrap=false, + reverse=false, + style="default", + vnf=EMPTY_VNF ) = - assert((!caps)||(caps&&col_wrap)) - assert(in_list(style,["default","alt","quincunx"])) + assert((!caps)||(caps&&col_wrap)) + assert(in_list(style,["default","alt","quincunx"])) assert(is_consistent(points), "Non-rectangular or invalid point array") - let( - pts = flatten(points), - pcnt = len(pts), - rows = len(points), - cols = len(points[0]), - cap1 = first_defined([cap1,caps,false]), - cap2 = first_defined([cap2,caps,false]), - colcnt = cols - (col_wrap?0:1), - rowcnt = rows - (row_wrap?0:1) - ) - rows<=1 || cols<=1 ? vnf : - vnf_merge([ - vnf, [ - concat( - pts, - style!="quincunx"? [] : [ - for (r = [0:1:rowcnt-1]) ( - for (c = [0:1:colcnt-1]) ( - let( - i1 = ((r+0)%rows)*cols + ((c+0)%cols), - i2 = ((r+1)%rows)*cols + ((c+0)%cols), - i3 = ((r+1)%rows)*cols + ((c+1)%cols), - i4 = ((r+0)%rows)*cols + ((c+1)%cols) - ) mean([pts[i1], pts[i2], pts[i3], pts[i4]]) - ) - ) - ] - ), - concat( - [ - for (r = [0:1:rowcnt-1]) ( - for (c = [0:1:colcnt-1]) each ( - let( - i1 = ((r+0)%rows)*cols + ((c+0)%cols), - i2 = ((r+1)%rows)*cols + ((c+0)%cols), - i3 = ((r+1)%rows)*cols + ((c+1)%cols), - i4 = ((r+0)%rows)*cols + ((c+1)%cols) - ) - style=="quincunx"? ( - let(i5 = pcnt + r*colcnt + c) - reverse? [[i1,i2,i5],[i2,i3,i5],[i3,i4,i5],[i4,i1,i5]] : [[i1,i5,i2],[i2,i5,i3],[i3,i5,i4],[i4,i5,i1]] - ) : style=="alt"? ( - reverse? [[i1,i2,i4],[i2,i3,i4]] : [[i1,i4,i2],[i2,i4,i3]] - ) : ( - reverse? [[i1,i2,i3],[i1,i3,i4]] : [[i1,i3,i2],[i1,i4,i3]] - ) - ) - ) - ], - !cap1? [] : [ - reverse? - [for (c = [0:1:cols-1]) c] : - [for (c = [cols-1:-1:0]) c] - ], - !cap2? [] : [ - reverse? - [for (c = [cols-1:-1:0]) (rows-1)*cols + c] : - [for (c = [0:1:cols-1]) (rows-1)*cols + c] - ] - ) - ] - ]); + let( + pts = flatten(points), + pcnt = len(pts), + rows = len(points), + cols = len(points[0]), + cap1 = first_defined([cap1,caps,false]), + cap2 = first_defined([cap2,caps,false]), + colcnt = cols - (col_wrap?0:1), + rowcnt = rows - (row_wrap?0:1) + ) + rows<=1 || cols<=1 ? vnf : + vnf_merge([ + vnf, [ + concat( + pts, + style!="quincunx"? [] : [ + for (r = [0:1:rowcnt-1]) ( + for (c = [0:1:colcnt-1]) ( + let( + i1 = ((r+0)%rows)*cols + ((c+0)%cols), + i2 = ((r+1)%rows)*cols + ((c+0)%cols), + i3 = ((r+1)%rows)*cols + ((c+1)%cols), + i4 = ((r+0)%rows)*cols + ((c+1)%cols) + ) mean([pts[i1], pts[i2], pts[i3], pts[i4]]) + ) + ) + ] + ), + concat( + [ + for (r = [0:1:rowcnt-1]) ( + for (c = [0:1:colcnt-1]) each ( + let( + i1 = ((r+0)%rows)*cols + ((c+0)%cols), + i2 = ((r+1)%rows)*cols + ((c+0)%cols), + i3 = ((r+1)%rows)*cols + ((c+1)%cols), + i4 = ((r+0)%rows)*cols + ((c+1)%cols) + ) + style=="quincunx"? ( + let(i5 = pcnt + r*colcnt + c) + reverse? [[i1,i2,i5],[i2,i3,i5],[i3,i4,i5],[i4,i1,i5]] : [[i1,i5,i2],[i2,i5,i3],[i3,i5,i4],[i4,i5,i1]] + ) : style=="alt"? ( + reverse? [[i1,i2,i4],[i2,i3,i4]] : [[i1,i4,i2],[i2,i4,i3]] + ) : ( + reverse? [[i1,i2,i3],[i1,i3,i4]] : [[i1,i3,i2],[i1,i4,i3]] + ) + ) + ) + ], + !cap1? [] : [ + reverse? + [for (c = [0:1:cols-1]) c] : + [for (c = [cols-1:-1:0]) c] + ], + !cap2? [] : [ + reverse? + [for (c = [cols-1:-1:0]) (rows-1)*cols + c] : + [for (c = [0:1:cols-1]) (rows-1)*cols + c] + ] + ) + ] + ]); // Module: vnf_polyhedron() @@ -342,8 +342,8 @@ function vnf_vertex_array( // vnf = A VNF structure, or list of VNF structures. // convexity = Max number of times a line could intersect a wall of the shape. module vnf_polyhedron(vnf, convexity=2) { - vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf; - polyhedron(vnf[0], vnf[1], convexity=convexity); + vnf = is_vnf_list(vnf)? vnf_merge(vnf) : vnf; + polyhedron(vnf[0], vnf[1], convexity=convexity); } @@ -358,15 +358,15 @@ module vnf_polyhedron(vnf, convexity=2) { // no holes; otherwise the results are undefined. Returns a positive volume if face direction is clockwise and a negative volume // if face direction is counter-clockwise. function vnf_volume(vnf) = - let( - vnf = vnf_triangulate(vnf), - verts = vnf[0] - ) sum([ - for(face_index=vnf[1]) let( - face = select(verts, face_index), - n = cross(face[2]-face[0],face[1]-face[0]) - ) face[0] * n - ])/6; + let( + vnf = vnf_triangulate(vnf), + verts = vnf[0] + ) sum([ + for(face_index=vnf[1]) let( + face = select(verts, face_index), + n = cross(face[2]-face[0],face[1]-face[0]) + ) face[0] * n + ])/6; // Function: vnf_centroid() @@ -378,36 +378,36 @@ function vnf_volume(vnf) = // Algorithm from: https://wwwf.imperial.ac.uk/~rn/centroid.pdf function vnf_centroid(vnf) = - let( - vnf = vnf_triangulate(vnf), - verts = vnf[0], - val=sum([ - for(face_index=vnf[1]) - let( - face = select(verts, face_index), - n = cross(face[2]-face[0],face[1]-face[0]) - ) [ - face[0] * n, - vmul(n, - sqr(face[0] + face[1]) + - sqr(face[0] + face[2]) + - sqr(face[1] + face[2]) - ) - ] - ]) - ) val[1]/val[0]/8; + let( + vnf = vnf_triangulate(vnf), + verts = vnf[0], + val=sum([ + for(face_index=vnf[1]) + let( + face = select(verts, face_index), + n = cross(face[2]-face[0],face[1]-face[0]) + ) [ + face[0] * n, + vmul(n, + sqr(face[0] + face[1]) + + sqr(face[0] + face[2]) + + sqr(face[1] + face[2]) + ) + ] + ]) + ) val[1]/val[0]/8; function _triangulate_planar_convex_polygons(polys) = - polys==[]? [] : - let( - tris = [for (poly=polys) if (len(poly)==3) poly], - bigs = [for (poly=polys) if (len(poly)>3) poly], - newtris = [for (poly=bigs) select(poly,-2,0)], - newbigs = [for (poly=bigs) select(poly,0,-2)], - newtris2 = _triangulate_planar_convex_polygons(newbigs), - outtris = concat(tris, newtris, newtris2) - ) outtris; + polys==[]? [] : + let( + tris = [for (poly=polys) if (len(poly)==3) poly], + bigs = [for (poly=polys) if (len(poly)>3) poly], + newtris = [for (poly=bigs) select(poly,-2,0)], + newbigs = [for (poly=bigs) select(poly,0,-2)], + newtris2 = _triangulate_planar_convex_polygons(newbigs), + outtris = concat(tris, newtris, newtris2) + ) outtris; // Function: vnf_bend() @@ -482,49 +482,49 @@ function _triangulate_planar_convex_polygons(polys) = // bent1 = vnf_bend(vnf1, axis="Z"); // vnf_polyhedron([bent1]); function vnf_bend(vnf,r,d,axis="Z") = - let( - chk_axis = assert(in_list(axis,["X","Y","Z"])), - vnf = vnf_triangulate(vnf), - verts = vnf[0], - bounds = pointlist_bounds(verts), - bmin = bounds[0], - bmax = bounds[1], - dflt = axis=="Z"? - max(abs(bmax.y), abs(bmin.y)) : - max(abs(bmax.z), abs(bmin.z)), - r = get_radius(r=r,d=d,dflt=dflt), - width = axis=="X"? (bmax.y-bmin.y) : (bmax.x - bmin.x) - ) - assert(width <= 2*PI*r, "Shape would wrap more than completely around the cylinder.") - let( - span_chk = axis=="Z"? - assert(bmin.y > 0 || bmax.y < 0, "Entire shape MUST be completely in front of or behind y=0.") : - assert(bmin.z > 0 || bmax.z < 0, "Entire shape MUST be completely above or below z=0."), - min_ang = 180 * bmin.x / (PI * r), - max_ang = 180 * bmax.x / (PI * r), - ang_span = max_ang-min_ang, - steps = ceil(segs(r) * ang_span/360), - step = width / steps, - bend_at = axis=="X"? [for(i = [1:1:steps-1]) i*step+bmin.y] : - [for(i = [1:1:steps-1]) i*step+bmin.x], - facepolys = [for (face=vnf[1]) select(verts,face)], - splits = axis=="X"? - split_polygons_at_each_y(facepolys, bend_at) : - split_polygons_at_each_x(facepolys, bend_at), - newtris = _triangulate_planar_convex_polygons(splits), - bent_faces = [ - for (tri = newtris) [ - for (p = tri) let( - a = axis=="X"? 180*p.y/(r*PI) * sign(bmax.z) : - axis=="Y"? 180*p.x/(r*PI) * sign(bmax.z) : - 180*p.x/(r*PI) * sign(bmax.y) - ) - axis=="X"? [p.x, p.z*sin(a), p.z*cos(a)] : - axis=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] : - [p.y*sin(a), p.y*cos(a), p.z] - ] - ] - ) vnf_add_faces(faces=bent_faces); + let( + chk_axis = assert(in_list(axis,["X","Y","Z"])), + vnf = vnf_triangulate(vnf), + verts = vnf[0], + bounds = pointlist_bounds(verts), + bmin = bounds[0], + bmax = bounds[1], + dflt = axis=="Z"? + max(abs(bmax.y), abs(bmin.y)) : + max(abs(bmax.z), abs(bmin.z)), + r = get_radius(r=r,d=d,dflt=dflt), + width = axis=="X"? (bmax.y-bmin.y) : (bmax.x - bmin.x) + ) + assert(width <= 2*PI*r, "Shape would wrap more than completely around the cylinder.") + let( + span_chk = axis=="Z"? + assert(bmin.y > 0 || bmax.y < 0, "Entire shape MUST be completely in front of or behind y=0.") : + assert(bmin.z > 0 || bmax.z < 0, "Entire shape MUST be completely above or below z=0."), + min_ang = 180 * bmin.x / (PI * r), + max_ang = 180 * bmax.x / (PI * r), + ang_span = max_ang-min_ang, + steps = ceil(segs(r) * ang_span/360), + step = width / steps, + bend_at = axis=="X"? [for(i = [1:1:steps-1]) i*step+bmin.y] : + [for(i = [1:1:steps-1]) i*step+bmin.x], + facepolys = [for (face=vnf[1]) select(verts,face)], + splits = axis=="X"? + split_polygons_at_each_y(facepolys, bend_at) : + split_polygons_at_each_x(facepolys, bend_at), + newtris = _triangulate_planar_convex_polygons(splits), + bent_faces = [ + for (tri = newtris) [ + for (p = tri) let( + a = axis=="X"? 180*p.y/(r*PI) * sign(bmax.z) : + axis=="Y"? 180*p.x/(r*PI) * sign(bmax.z) : + 180*p.x/(r*PI) * sign(bmax.y) + ) + axis=="X"? [p.x, p.z*sin(a), p.z*cos(a)] : + axis=="Y"? [p.z*sin(a), p.y, p.z*cos(a)] : + [p.y*sin(a), p.y*cos(a), p.z] + ] + ] + ) vnf_add_faces(faces=bent_faces); // Function&Module: vnf_validate() @@ -614,198 +614,198 @@ function vnf_bend(vnf,r,d,axis="Z") = // ], slices=0, caps=false); // vnf_validate(vnf,size=2); function vnf_validate(vnf, show_warns=true, check_isects=false) = - assert(is_path(vnf[0])) - let( - vnf = vnf_compact(vnf), - varr = vnf[0], - faces = vnf[1], - edges = sort([ - for (face=faces, edge=pair_wrap(face)) - edge[0] 3) [ - "WARNING", - "BIG_FACE", - "Face has more than 3 vertices, and may confuse CGAL", - [for (i=face) varr[i]], - "yellow" - ] - ], - null_faces = !show_warns? [] : [ - for (face = faces) let( - face = deduplicate(face,closed=true) - ) - if (len(face)>=3) let( - faceverts = [for (k=face) varr[k]], - area = polygon_area(faceverts) - ) if (is_num(area) && abs(area) < EPSILON) [ - "WARNING", - "NULL_FACE", - str("Face has zero area: ",fmt_float(abs(area),15)), - faceverts, - "brown" - ] - ], - nonplanars = unique([ - for (face = faces) let( - faceverts = [for (k=face) varr[k]] - ) if (!points_are_coplanar(faceverts)) [ - "ERROR", - "NONPLANAR", - "Face vertices are not coplanar", - faceverts, - "cyan" - ] - ]), - overpop_edges = unique([ - for (i=idx(uniq_edges)) - if (edgecnts[1][i]>2) [ - "ERROR", - "OVRPOP_EDGE", - "Too many faces attached at Edge", - [for (i=uniq_edges[i]) varr[i]], - "#f70" - ] - ]), - reversals = unique([ - for(i = idx(faces), j = idx(faces)) if(i != j) - if(len(deduplicate(faces[i],closed=true))>=3) - if(len(deduplicate(faces[j],closed=true))>=3) - for(edge1 = pair_wrap(faces[i])) - for(edge2 = pair_wrap(faces[j])) - if(edge1 == edge2) // Valid adjacent faces will never have the same vertex ordering. - if(_edge_not_reported(edge1, varr, overpop_edges)) - [ - "ERROR", - "REVERSAL", - "Faces Reverse Across Edge", - [for (i=edge1) varr[i]], - "violet" - ] - ]), - t_juncts = unique([ - for (v=idx(varr), edge=uniq_edges) - if (v!=edge[0] && v!=edge[1]) let( - a = varr[edge[0]], - b = varr[v], - c = varr[edge[1]], - pt = segment_closest_point([a,c],b) - ) if (pt == b) [ - "ERROR", - "T_JUNCTION", - "Vertex is mid-edge on another Face", - [b], - "red" - ] - ]), - isect_faces = !check_isects? [] : unique([ - for (i = [0:1:len(faces)-2]) - for (j = [i+1:1:len(faces)-1]) let( - f1 = faces[i], - f2 = faces[j], - shared_edges = [ - for (edge1 = pair_wrap(f1), edge2 = pair_wrap(f2)) let( - e1 = edge1[0]1) let( - poly2 = select(varr,f2), - isects2 = polygon_line_intersection(poly2,isect,bounded=true) - ) - if (!is_undef(isects2)) - for (seg=isects2) - if (seg[0] != seg[1]) [ - "ERROR", - "FACE_ISECT", - "Faces intersect", - seg, - "blue" - ] - ]), - hole_edges = unique([ - for (i=idx(uniq_edges)) - if (edgecnts[1][i]<2) - if (_pts_not_reported(uniq_edges[i], varr, t_juncts)) - if (_pts_not_reported(uniq_edges[i], varr, isect_faces)) - [ - "ERROR", - "HOLE_EDGE", - "Edge bounds Hole", - [for (i=uniq_edges[i]) varr[i]], - "magenta" - ] - ]) - ) concat( - big_faces, - null_faces, - nonplanars, - overpop_edges, - reversals, - t_juncts, - isect_faces, - hole_edges - ); + assert(is_path(vnf[0])) + let( + vnf = vnf_compact(vnf), + varr = vnf[0], + faces = vnf[1], + edges = sort([ + for (face=faces, edge=pair_wrap(face)) + edge[0] 3) [ + "WARNING", + "BIG_FACE", + "Face has more than 3 vertices, and may confuse CGAL", + [for (i=face) varr[i]], + "yellow" + ] + ], + null_faces = !show_warns? [] : [ + for (face = faces) let( + face = deduplicate(face,closed=true) + ) + if (len(face)>=3) let( + faceverts = [for (k=face) varr[k]], + area = polygon_area(faceverts) + ) if (is_num(area) && abs(area) < EPSILON) [ + "WARNING", + "NULL_FACE", + str("Face has zero area: ",fmt_float(abs(area),15)), + faceverts, + "brown" + ] + ], + nonplanars = unique([ + for (face = faces) let( + faceverts = [for (k=face) varr[k]] + ) if (!points_are_coplanar(faceverts)) [ + "ERROR", + "NONPLANAR", + "Face vertices are not coplanar", + faceverts, + "cyan" + ] + ]), + overpop_edges = unique([ + for (i=idx(uniq_edges)) + if (edgecnts[1][i]>2) [ + "ERROR", + "OVRPOP_EDGE", + "Too many faces attached at Edge", + [for (i=uniq_edges[i]) varr[i]], + "#f70" + ] + ]), + reversals = unique([ + for(i = idx(faces), j = idx(faces)) if(i != j) + if(len(deduplicate(faces[i],closed=true))>=3) + if(len(deduplicate(faces[j],closed=true))>=3) + for(edge1 = pair_wrap(faces[i])) + for(edge2 = pair_wrap(faces[j])) + if(edge1 == edge2) // Valid adjacent faces will never have the same vertex ordering. + if(_edge_not_reported(edge1, varr, overpop_edges)) + [ + "ERROR", + "REVERSAL", + "Faces Reverse Across Edge", + [for (i=edge1) varr[i]], + "violet" + ] + ]), + t_juncts = unique([ + for (v=idx(varr), edge=uniq_edges) + if (v!=edge[0] && v!=edge[1]) let( + a = varr[edge[0]], + b = varr[v], + c = varr[edge[1]], + pt = segment_closest_point([a,c],b) + ) if (pt == b) [ + "ERROR", + "T_JUNCTION", + "Vertex is mid-edge on another Face", + [b], + "red" + ] + ]), + isect_faces = !check_isects? [] : unique([ + for (i = [0:1:len(faces)-2]) + for (j = [i+1:1:len(faces)-1]) let( + f1 = faces[i], + f2 = faces[j], + shared_edges = [ + for (edge1 = pair_wrap(f1), edge2 = pair_wrap(f2)) let( + e1 = edge1[0]1) let( + poly2 = select(varr,f2), + isects2 = polygon_line_intersection(poly2,isect,bounded=true) + ) + if (!is_undef(isects2)) + for (seg=isects2) + if (seg[0] != seg[1]) [ + "ERROR", + "FACE_ISECT", + "Faces intersect", + seg, + "blue" + ] + ]), + hole_edges = unique([ + for (i=idx(uniq_edges)) + if (edgecnts[1][i]<2) + if (_pts_not_reported(uniq_edges[i], varr, t_juncts)) + if (_pts_not_reported(uniq_edges[i], varr, isect_faces)) + [ + "ERROR", + "HOLE_EDGE", + "Edge bounds Hole", + [for (i=uniq_edges[i]) varr[i]], + "magenta" + ] + ]) + ) concat( + big_faces, + null_faces, + nonplanars, + overpop_edges, + reversals, + t_juncts, + isect_faces, + hole_edges + ); function _pts_not_reported(pts, varr, reports) = - [ - for (i = pts, report = reports, pt = report[3]) - if (varr[i] == pt) 1 - ] == []; + [ + for (i = pts, report = reports, pt = report[3]) + if (varr[i] == pt) 1 + ] == []; function _edge_not_reported(edge, varr, reports) = - let( - edge = sort([for (i=edge) varr[i]]) - ) [ - for (report = reports) let( - pts = sort(report[3]) - ) if (len(pts)==2 && edge == pts) 1 - ] == []; + let( + edge = sort([for (i=edge) varr[i]]) + ) [ + for (report = reports) let( + pts = sort(report[3]) + ) if (len(pts)==2 && edge == pts) 1 + ] == []; module vnf_validate(vnf, size=1, show_warns=true, check_isects=false) { - faults = vnf_validate( - vnf, show_warns=show_warns, - check_isects=check_isects - ); - for (fault = faults) { - typ = fault[0]; - err = fault[1]; - msg = fault[2]; - pts = fault[3]; - clr = fault[4]; - echo(str(typ, " ", err, ": ", msg, " at ", pts)); - color(clr) { - if (len(pts)==2) { - stroke(pts, width=size); - } else if (len(pts)>2) { - stroke(pts, width=size, closed=true); - polyhedron(pts,[[for (i=idx(pts)) i]]); - } else { - move_copies(pts) sphere(d=size*3, $fn=18); - } - } - } - color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf); + faults = vnf_validate( + vnf, show_warns=show_warns, + check_isects=check_isects + ); + for (fault = faults) { + typ = fault[0]; + err = fault[1]; + msg = fault[2]; + pts = fault[3]; + clr = fault[4]; + echo(str(typ, " ", err, ": ", msg, " at ", pts)); + color(clr) { + if (len(pts)==2) { + stroke(pts, width=size); + } else if (len(pts)>2) { + stroke(pts, width=size, closed=true); + polyhedron(pts,[[for (i=idx(pts)) i]]); + } else { + move_copies(pts) sphere(d=size*3, $fn=18); + } + } + } + color([0.5,0.5,0.5,0.5]) vnf_polyhedron(vnf); } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/walls.scad b/walls.scad index 10838a5..e87b6f6 100644 --- a/walls.scad +++ b/walls.scad @@ -36,25 +36,25 @@ // narrowing_strut(w=10, l=100, wall=5, ang=30); module narrowing_strut(w=10, l=100, wall=5, ang=30, anchor=BOTTOM, spin=0, orient=UP) { - h = wall + w/2/tan(ang); - size = [w, l, h]; - attachable(anchor,spin,orient, size=size) { - xrot(90) - fwd(h/2) { - linear_extrude(height=l, center=true, slices=2) { - back(wall/2) square([w, wall], center=true); - back(wall-0.001) { - yscale(1/tan(ang)) { - difference() { - zrot(45) square(w/sqrt(2), center=true); - fwd(w/2) square(w, center=true); - } - } - } - } - } - children(); - } + h = wall + w/2/tan(ang); + size = [w, l, h]; + attachable(anchor,spin,orient, size=size) { + xrot(90) + fwd(h/2) { + linear_extrude(height=l, center=true, slices=2) { + back(wall/2) square([w, wall], center=true); + back(wall-0.001) { + yscale(1/tan(ang)) { + difference() { + zrot(45) square(w/sqrt(2), center=true); + fwd(w/2) square(w, center=true); + } + } + } + } + } + children(); + } } @@ -87,152 +87,152 @@ module narrowing_strut(w=10, l=100, wall=5, ang=30, anchor=BOTTOM, spin=0, orien // thinning_wall(h=50, l=[80,50], thick=4, strut=4, wall=2, braces=true); module thinning_wall(h=50, l=100, thick=5, ang=30, braces=false, strut, wall, anchor=CENTER, spin=0, orient=UP) { - l1 = (l[0] == undef)? l : l[0]; - l2 = (l[1] == undef)? l : l[1]; - strut = is_num(strut)? strut : min(h,l1,l2,thick)/2; - wall = is_num(wall)? wall : thick/2; + l1 = (l[0] == undef)? l : l[0]; + l2 = (l[1] == undef)? l : l[1]; + strut = is_num(strut)? strut : min(h,l1,l2,thick)/2; + wall = is_num(wall)? wall : thick/2; - bevel_h = strut + (thick-wall)/2/tan(ang); - cp1 = find_circle_2tangents([0,0,h/2], [l2/2,0,h/2], [l1/2,0,-h/2], r=strut)[0]; - cp2 = find_circle_2tangents([0,0,h/2], [l2/2,0,h/2], [l1/2,0,-h/2], r=bevel_h)[0]; - cp3 = find_circle_2tangents([0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,h/2], r=bevel_h)[0]; - cp4 = find_circle_2tangents([0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,h/2], r=strut)[0]; + bevel_h = strut + (thick-wall)/2/tan(ang); + cp1 = find_circle_2tangents([0,0,h/2], [l2/2,0,h/2], [l1/2,0,-h/2], r=strut)[0]; + cp2 = find_circle_2tangents([0,0,h/2], [l2/2,0,h/2], [l1/2,0,-h/2], r=bevel_h)[0]; + cp3 = find_circle_2tangents([0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,h/2], r=bevel_h)[0]; + cp4 = find_circle_2tangents([0,0,-h/2], [l1/2,0,-h/2], [l2/2,0,h/2], r=strut)[0]; - z1 = h/2; - z2 = cp1.z; - z3 = cp2.z; + z1 = h/2; + z2 = cp1.z; + z3 = cp2.z; - x1 = l2/2; - x2 = cp1.x; - x3 = cp2.x; - x4 = l1/2; - x5 = cp4.x; - x6 = cp3.x; + x1 = l2/2; + x2 = cp1.x; + x3 = cp2.x; + x4 = l1/2; + x5 = cp4.x; + x6 = cp3.x; - y1 = thick/2; - y2 = wall/2; + y1 = thick/2; + y2 = wall/2; - corner1 = [ x2, 0, z2]; - corner2 = [-x5, 0, -z2]; - brace_len = norm(corner1-corner2); + corner1 = [ x2, 0, z2]; + corner2 = [-x5, 0, -z2]; + brace_len = norm(corner1-corner2); - size = [l1, thick, h]; - attachable(anchor,spin,orient, size=size, size2=[l2,thick]) { - union() { - polyhedron( - points=[ - [-x4, -y1, -z1], - [ x4, -y1, -z1], - [ x1, -y1, z1], - [-x1, -y1, z1], + size = [l1, thick, h]; + attachable(anchor,spin,orient, size=size, size2=[l2,thick]) { + union() { + polyhedron( + points=[ + [-x4, -y1, -z1], + [ x4, -y1, -z1], + [ x1, -y1, z1], + [-x1, -y1, z1], - [-x5, -y1, -z2], - [ x5, -y1, -z2], - [ x2, -y1, z2], - [-x2, -y1, z2], + [-x5, -y1, -z2], + [ x5, -y1, -z2], + [ x2, -y1, z2], + [-x2, -y1, z2], - [-x6, -y2, -z3], - [ x6, -y2, -z3], - [ x3, -y2, z3], - [-x3, -y2, z3], + [-x6, -y2, -z3], + [ x6, -y2, -z3], + [ x3, -y2, z3], + [-x3, -y2, z3], - [-x4, y1, -z1], - [ x4, y1, -z1], - [ x1, y1, z1], - [-x1, y1, z1], + [-x4, y1, -z1], + [ x4, y1, -z1], + [ x1, y1, z1], + [-x1, y1, z1], - [-x5, y1, -z2], - [ x5, y1, -z2], - [ x2, y1, z2], - [-x2, y1, z2], + [-x5, y1, -z2], + [ x5, y1, -z2], + [ x2, y1, z2], + [-x2, y1, z2], - [-x6, y2, -z3], - [ x6, y2, -z3], - [ x3, y2, z3], - [-x3, y2, z3], - ], - faces=[ - [ 4, 5, 1], - [ 5, 6, 2], - [ 6, 7, 3], - [ 7, 4, 0], + [-x6, y2, -z3], + [ x6, y2, -z3], + [ x3, y2, z3], + [-x3, y2, z3], + ], + faces=[ + [ 4, 5, 1], + [ 5, 6, 2], + [ 6, 7, 3], + [ 7, 4, 0], - [ 4, 1, 0], - [ 5, 2, 1], - [ 6, 3, 2], - [ 7, 0, 3], + [ 4, 1, 0], + [ 5, 2, 1], + [ 6, 3, 2], + [ 7, 0, 3], - [ 8, 9, 5], - [ 9, 10, 6], - [10, 11, 7], - [11, 8, 4], + [ 8, 9, 5], + [ 9, 10, 6], + [10, 11, 7], + [11, 8, 4], - [ 8, 5, 4], - [ 9, 6, 5], - [10, 7, 6], - [11, 4, 7], + [ 8, 5, 4], + [ 9, 6, 5], + [10, 7, 6], + [11, 4, 7], - [11, 10, 9], - [20, 21, 22], + [11, 10, 9], + [20, 21, 22], - [11, 9, 8], - [20, 22, 23], + [11, 9, 8], + [20, 22, 23], - [16, 17, 21], - [17, 18, 22], - [18, 19, 23], - [19, 16, 20], + [16, 17, 21], + [17, 18, 22], + [18, 19, 23], + [19, 16, 20], - [16, 21, 20], - [17, 22, 21], - [18, 23, 22], - [19, 20, 23], + [16, 21, 20], + [17, 22, 21], + [18, 23, 22], + [19, 20, 23], - [12, 13, 17], - [13, 14, 18], - [14, 15, 19], - [15, 12, 16], + [12, 13, 17], + [13, 14, 18], + [14, 15, 19], + [15, 12, 16], - [12, 17, 16], - [13, 18, 17], - [14, 19, 18], - [15, 16, 19], + [12, 17, 16], + [13, 18, 17], + [14, 19, 18], + [15, 16, 19], - [ 0, 1, 13], - [ 1, 2, 14], - [ 2, 3, 15], - [ 3, 0, 12], + [ 0, 1, 13], + [ 1, 2, 14], + [ 2, 3, 15], + [ 3, 0, 12], - [ 0, 13, 12], - [ 1, 14, 13], - [ 2, 15, 14], - [ 3, 12, 15], - ], - convexity=6 - ); - if(braces) { - bracepath = [ - [-strut*0.33,thick/2], - [ strut*0.33,thick/2], - [ strut*0.33+(thick-wall)/2/tan(ang), wall/2], - [ strut*0.33+(thick-wall)/2/tan(ang),-wall/2], - [ strut*0.33,-thick/2], - [-strut*0.33,-thick/2], - [-strut*0.33-(thick-wall)/2/tan(ang),-wall/2], - [-strut*0.33-(thick-wall)/2/tan(ang), wall/2] - ]; - xflip_copy() { - intersection() { - extrude_from_to(corner1,corner2) { - polygon(bracepath); - } - prismoid([l1,thick],[l2,thick],h=h,anchor=CENTER); - } - } - } - } - children(); - } + [ 0, 13, 12], + [ 1, 14, 13], + [ 2, 15, 14], + [ 3, 12, 15], + ], + convexity=6 + ); + if(braces) { + bracepath = [ + [-strut*0.33,thick/2], + [ strut*0.33,thick/2], + [ strut*0.33+(thick-wall)/2/tan(ang), wall/2], + [ strut*0.33+(thick-wall)/2/tan(ang),-wall/2], + [ strut*0.33,-thick/2], + [-strut*0.33,-thick/2], + [-strut*0.33-(thick-wall)/2/tan(ang),-wall/2], + [-strut*0.33-(thick-wall)/2/tan(ang), wall/2] + ]; + xflip_copy() { + intersection() { + extrude_from_to(corner1,corner2) { + polygon(bracepath); + } + prismoid([l1,thick],[l2,thick],h=h,anchor=CENTER); + } + } + } + } + children(); + } } @@ -266,35 +266,35 @@ module thinning_wall(h=50, l=100, thick=5, ang=30, braces=false, strut, wall, an // thinning_triangle(h=50, l=80, thick=4, ang=30, strut=5, wall=2, diagonly=true, center=false); module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly=false, center, anchor, spin=0, orient=UP) { - dang = atan(h/l); - dlen = h/sin(dang); - size = [thick, l, h]; - anchor = get_anchor(anchor, center, BOT+FRONT, CENTER); - attachable(anchor,spin,orient, size=size) { - difference() { - union() { - if (!diagonly) { - translate([0, 0, -h/2]) - narrowing_strut(w=thick, l=l, wall=strut, ang=ang); - translate([0, -l/2, 0]) - xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang); - } - intersection() { - cube(size=[thick, l, h], center=true); - xrot(-dang) yrot(180) { - narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang); - } - } - cube(size=[wall, l-0.1, h-0.1], center=true); - } - xrot(-dang) { - translate([0, 0, h/2]) { - cube(size=[thick+0.1, l*2, h], center=true); - } - } - } - children(); - } + dang = atan(h/l); + dlen = h/sin(dang); + size = [thick, l, h]; + anchor = get_anchor(anchor, center, BOT+FRONT, CENTER); + attachable(anchor,spin,orient, size=size) { + difference() { + union() { + if (!diagonly) { + translate([0, 0, -h/2]) + narrowing_strut(w=thick, l=l, wall=strut, ang=ang); + translate([0, -l/2, 0]) + xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang); + } + intersection() { + cube(size=[thick, l, h], center=true); + xrot(-dang) yrot(180) { + narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang); + } + } + cube(size=[wall, l-0.1, h-0.1], center=true); + } + xrot(-dang) { + translate([0, 0, h/2]) { + cube(size=[thick+0.1, l*2, h], center=true); + } + } + } + children(); + } } @@ -328,41 +328,41 @@ module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly // sparse_strut(h=40, l=100, thick=3, strut=2, maxang=45, max_bridge=30); module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, anchor=CENTER, spin=0, orient=UP) { - zoff = h/2 - strut/2; - yoff = l/2 - strut/2; + zoff = h/2 - strut/2; + yoff = l/2 - strut/2; - maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang); - maxz = 2 * maxhyp * cos(maxang); + maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang); + maxz = 2 * maxhyp * cos(maxang); - zreps = ceil(2*zoff/maxz); - zstep = 2*zoff / zreps; + zreps = ceil(2*zoff/maxz); + zstep = 2*zoff / zreps; - hyp = zstep/2 / cos(maxang); - maxy = min(2 * hyp * sin(maxang), max_bridge+strut); + hyp = zstep/2 / cos(maxang); + maxy = min(2 * hyp * sin(maxang), max_bridge+strut); - yreps = ceil(2*yoff/maxy); - ystep = 2*yoff / yreps; + yreps = ceil(2*yoff/maxy); + ystep = 2*yoff / yreps; - ang = atan(ystep/zstep); - len = zstep / cos(ang); + ang = atan(ystep/zstep); + len = zstep / cos(ang); - size = [thick, l, h]; - attachable(anchor,spin,orient, size=size) { - yrot(90) - linear_extrude(height=thick, convexity=4*yreps, center=true) { - difference() { - square([h, l], center=true); - square([h-2*strut, l-2*strut], center=true); - } - ycopies(ystep, n=yreps) { - xcopies(zstep, n=zreps) { - skew(syx=tan(-ang)) square([(h-strut)/zreps, strut], center=true); - skew(syx=tan( ang)) square([(h-strut)/zreps, strut], center=true); - } - } - } - children(); - } + size = [thick, l, h]; + attachable(anchor,spin,orient, size=size) { + yrot(90) + linear_extrude(height=thick, convexity=4*yreps, center=true) { + difference() { + square([h, l], center=true); + square([h-2*strut, l-2*strut], center=true); + } + ycopies(ystep, n=yreps) { + xcopies(zstep, n=zreps) { + skew(syx=tan(-ang)) square([(h-strut)/zreps, strut], center=true); + skew(syx=tan( ang)) square([(h-strut)/zreps, strut], center=true); + } + } + } + children(); + } } @@ -398,70 +398,70 @@ module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge=20, anc module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge=30, anchor=CENTER, spin=0, orient=UP) { - xoff = w - thick; - yoff = l - thick; - zoff = h - thick; + xoff = w - thick; + yoff = l - thick; + zoff = h - thick; - xreps = ceil(xoff/yoff); - yreps = ceil(yoff/xoff); - zreps = ceil(zoff/min(xoff, yoff)); + xreps = ceil(xoff/yoff); + yreps = ceil(yoff/xoff); + zreps = ceil(zoff/min(xoff, yoff)); - xstep = xoff / xreps; - ystep = yoff / yreps; - zstep = zoff / zreps; + xstep = xoff / xreps; + ystep = yoff / yreps; + zstep = zoff / zreps; - cross_ang = atan2(xstep, ystep); - cross_len = hypot(xstep, ystep); + cross_ang = atan2(xstep, ystep); + cross_len = hypot(xstep, ystep); - supp_ang = min(maxang, min(atan2(max_bridge, zstep), atan2(cross_len/2, zstep))); - supp_reps = floor(cross_len/2/(zstep*sin(supp_ang))); - supp_step = cross_len/2/supp_reps; + supp_ang = min(maxang, min(atan2(max_bridge, zstep), atan2(cross_len/2, zstep))); + supp_reps = floor(cross_len/2/(zstep*sin(supp_ang))); + supp_step = cross_len/2/supp_reps; - size = [w, l, h]; - attachable(anchor,spin,orient, size=size) { - intersection() { - union() { - ybridge = (l - (yreps+1) * strut) / yreps; - xcopies(xoff) sparse_strut(h=h, l=l, thick=thick, maxang=maxang, strut=strut, max_bridge=ybridge/ceil(ybridge/max_bridge)); - ycopies(yoff) zrot(90) sparse_strut(h=h, l=w, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge); - for(zs = [0:1:zreps-1]) { - for(xs = [0:1:xreps-1]) { - for(ys = [0:1:yreps-1]) { - translate([(xs+0.5)*xstep-xoff/2, (ys+0.5)*ystep-yoff/2, (zs+0.5)*zstep-zoff/2]) { - zflip_copy(offset=-(zstep-strut)/2) { - xflip_copy() { - zrot(cross_ang) { - down(strut/2) { - cube([strut, cross_len, strut], center=true); - } - if (zreps>1) { - back(cross_len/2) { - zrot(-cross_ang) { - down(strut) cube([strut, strut, zstep+strut], anchor=BOTTOM); - } - } - } - for (soff = [0:1:supp_reps-1] ) { - yflip_copy() { - back(soff*supp_step) { - skew(syz=tan(supp_ang)) { - cube([strut, strut, zstep], anchor=BOTTOM); - } - } - } - } - } - } - } - } - } - } - } - } - cube([w,l,h], center=true); - } - children(); - } + size = [w, l, h]; + attachable(anchor,spin,orient, size=size) { + intersection() { + union() { + ybridge = (l - (yreps+1) * strut) / yreps; + xcopies(xoff) sparse_strut(h=h, l=l, thick=thick, maxang=maxang, strut=strut, max_bridge=ybridge/ceil(ybridge/max_bridge)); + ycopies(yoff) zrot(90) sparse_strut(h=h, l=w, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge); + for(zs = [0:1:zreps-1]) { + for(xs = [0:1:xreps-1]) { + for(ys = [0:1:yreps-1]) { + translate([(xs+0.5)*xstep-xoff/2, (ys+0.5)*ystep-yoff/2, (zs+0.5)*zstep-zoff/2]) { + zflip_copy(offset=-(zstep-strut)/2) { + xflip_copy() { + zrot(cross_ang) { + down(strut/2) { + cube([strut, cross_len, strut], center=true); + } + if (zreps>1) { + back(cross_len/2) { + zrot(-cross_ang) { + down(strut) cube([strut, strut, zstep+strut], anchor=BOTTOM); + } + } + } + for (soff = [0:1:supp_reps-1] ) { + yflip_copy() { + back(soff*supp_step) { + skew(syz=tan(supp_ang)) { + cube([strut, strut, zstep], anchor=BOTTOM); + } + } + } + } + } + } + } + } + } + } + } + } + cube([w,l,h], center=true); + } + children(); + } } @@ -492,31 +492,31 @@ module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge // corrugated_wall(h=50, l=100, strut=8, wall=3); module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2, anchor=CENTER, spin=0, orient=UP) { - amplitude = (thick - wall) / 2; - period = min(15, thick * 2); - steps = quantup(segs(thick/2),4); - step = period/steps; - il = l - 2*strut + 2*step; - size = [thick, l, h]; - attachable(anchor,spin,orient, size=size) { - union() { - linear_extrude(height=h-2*strut+0.1, slices=2, convexity=ceil(2*il/period), center=true) { - polygon( - points=concat( - [for (y=[-il/2:step:il/2]) [amplitude*sin(y/period*360)-wall/2, y] ], - [for (y=[il/2:-step:-il/2]) [amplitude*sin(y/period*360)+wall/2, y] ] - ) - ); - } - difference() { - cube([thick, l, h], center=true); - cube([thick+0.5, l-2*strut, h-2*strut], center=true); - } - } - children(); - } + amplitude = (thick - wall) / 2; + period = min(15, thick * 2); + steps = quantup(segs(thick/2),4); + step = period/steps; + il = l - 2*strut + 2*step; + size = [thick, l, h]; + attachable(anchor,spin,orient, size=size) { + union() { + linear_extrude(height=h-2*strut+0.1, slices=2, convexity=ceil(2*il/period), center=true) { + polygon( + points=concat( + [for (y=[-il/2:step:il/2]) [amplitude*sin(y/period*360)-wall/2, y] ], + [for (y=[il/2:-step:-il/2]) [amplitude*sin(y/period*360)+wall/2, y] ] + ) + ); + } + difference() { + cube([thick, l, h], center=true); + cube([thick+0.5, l-2*strut, h-2*strut], center=true); + } + } + children(); + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap diff --git a/wiring.scad b/wiring.scad index 8f7d1ee..bc970d2 100644 --- a/wiring.scad +++ b/wiring.scad @@ -28,15 +28,15 @@ include // Example: // hex_offset_ring(d=1, lev=3); // Returns a hex ring of 18 points. function hex_offset_ring(d, lev=0) = - (lev == 0)? [[0,0]] : [ - for ( - sideang = [0:60:359.999], - sidenum = [1:1:lev] - ) [ - lev*d*cos(sideang)+sidenum*d*cos(sideang+120), - lev*d*sin(sideang)+sidenum*d*sin(sideang+120) - ] - ]; + (lev == 0)? [[0,0]] : [ + for ( + sideang = [0:60:359.999], + sidenum = [1:1:lev] + ) [ + lev*d*cos(sideang)+sidenum*d*cos(sideang+120), + lev*d*sin(sideang)+sidenum*d*sin(sideang+120) + ] + ]; // Function: hex_offsets() @@ -51,13 +51,13 @@ function hex_offset_ring(d, lev=0) = // n = Number of items to bundle. // d = How far to space each point away from others. function hex_offsets(n, d, lev=0, arr=[]) = - (len(arr) >= n)? arr : - hex_offsets( - n=n, - d=d, - lev=lev+1, - arr=concat(arr, hex_offset_ring(d, lev=lev)) - ); + (len(arr) >= n)? arr : + hex_offsets( + n=n, + d=d, + lev=lev+1, + arr=concat(arr, hex_offset_ring(d, lev=lev)) + ); @@ -81,26 +81,26 @@ function hex_offsets(n, d, lev=0, arr=[]) = // Example: // wiring([[50,0,-50], [50,50,-50], [0,50,-50], [0,0,-50], [0,0,0]], rounding=10, wires=13); module wiring(path, wires, wirediam=2, rounding=10, wirenum=0, bezsteps=12) { - colors = [ - [0.2, 0.2, 0.2], [1.0, 0.2, 0.2], [0.0, 0.8, 0.0], [1.0, 1.0, 0.2], - [0.3, 0.3, 1.0], [1.0, 1.0, 1.0], [0.7, 0.5, 0.0], [0.5, 0.5, 0.5], - [0.2, 0.9, 0.9], [0.8, 0.0, 0.8], [0.0, 0.6, 0.6], [1.0, 0.7, 0.7], - [1.0, 0.5, 1.0], [0.5, 0.6, 0.0], [1.0, 0.7, 0.0], [0.7, 1.0, 0.5], - [0.6, 0.6, 1.0], - ]; - offsets = hex_offsets(wires, wirediam); - bezpath = fillet_path(path, rounding); - poly = simplify_path(path3d(bezier_polyline(bezpath, bezsteps))); - n = max(segs(wirediam), 8); - r = wirediam/2; - for (i = [0:1:wires-1]) { - extpath = [for (j = [0:1:n-1]) let(a=j*360/n) [r*cos(a)+offsets[i][0], r*sin(a)+offsets[i][1]]]; - color(colors[(i+wirenum)%len(colors)]) { - path_sweep(extpath, poly); - } - } + colors = [ + [0.2, 0.2, 0.2], [1.0, 0.2, 0.2], [0.0, 0.8, 0.0], [1.0, 1.0, 0.2], + [0.3, 0.3, 1.0], [1.0, 1.0, 1.0], [0.7, 0.5, 0.0], [0.5, 0.5, 0.5], + [0.2, 0.9, 0.9], [0.8, 0.0, 0.8], [0.0, 0.6, 0.6], [1.0, 0.7, 0.7], + [1.0, 0.5, 1.0], [0.5, 0.6, 0.0], [1.0, 0.7, 0.0], [0.7, 1.0, 0.5], + [0.6, 0.6, 1.0], + ]; + offsets = hex_offsets(wires, wirediam); + bezpath = fillet_path(path, rounding); + poly = simplify_path(path3d(bezier_polyline(bezpath, bezsteps))); + n = max(segs(wirediam), 8); + r = wirediam/2; + for (i = [0:1:wires-1]) { + extpath = [for (j = [0:1:n-1]) let(a=j*360/n) [r*cos(a)+offsets[i][0], r*sin(a)+offsets[i][1]]]; + color(colors[(i+wirenum)%len(colors)]) { + path_sweep(extpath, poly); + } + } } -// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap +// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap