diff --git a/common.scad b/common.scad index 3467d1a..d94208f 100644 --- a/common.scad +++ b/common.scad @@ -81,6 +81,11 @@ function is_integer(n) = is_num(n) && n == round(n); function is_nan(x) = (x!=x); +// Function: is_range() +// Description: +// Returns true if its argument is a range +function is_range(x) = is_num(x[0]) && !is_list(x); + // Function: is_list_of() // Usage: // is_list_of(list, pattern) @@ -157,6 +162,25 @@ function first_defined(v,recursive=false,_i=0) = )? first_defined(v,recursive=recursive,_i=_i+1) : v[_i]; +// Function: one_defined() +// Usage: +// one_defined(vars, names, [required]) +// Description: +// Examines the input list `vars` and returns the entry which is not `undef`. If more +// than one entry is `undef` then issues an assertion specifying "Must define exactly one of" followed +// by the defined items from the `names` parameter. If `required` is set to false then it is OK if all of the +// entries of `vars` are undefined, and in this case, `undef` is returned. +// Example: +// length = one_defined([length,L,l], ["length","L","l"]); +function one_defined(vars, names, required=true) = + assert(len(vars)==len(names)) + let ( + ok = num_defined(vars)==1 || (!required && num_defined(vars)==0) + ) + assert(ok,str("Must define ",required?"exactly":"at most"," one of ",[for(i=[0:len(vars)]) if (is_def(vars[i])) names[i]])) + first_defined(vars); + + // Function: num_defined() // Description: Counts how many items in list `v` are not `undef`. function num_defined(v,_i=0,_cnt=0) = _i>=len(v)? _cnt : num_defined(v,_i+1,_cnt+(is_undef(v[_i])? 0 : 1)); @@ -180,6 +204,8 @@ function any_defined(v,recursive=false) = first_defined(v,recursive=recursive) ! function all_defined(v,recursive=false) = max([for (x=v) is_undef(x)||(recursive&&is_list(x)&&!all_defined(x))? 1 : 0])==0; + + // Section: Argument Helpers diff --git a/coords.scad b/coords.scad index f1d707f..2b7035d 100644 --- a/coords.scad +++ b/coords.scad @@ -100,6 +100,7 @@ function path4d(points, fill=0) = result + repeat(addition, len(result)); + // Section: Coordinate Systems // Function: polar_to_xy() @@ -174,7 +175,7 @@ function project_plane(point, a, b, c) = v = unit(c-a), n = unit(cross(u,v)), w = unit(cross(n,u)), - relpoint = is_vector(point)? (point-a) : move(-a,p=point) + relpoint = apply(move(-a),point) ) relpoint * transpose([w,u]); @@ -205,7 +206,7 @@ function lift_plane(point, a, b, c) = n = unit(cross(u,v)), w = unit(cross(n,u)), remapped = point*[w,u] - ) is_vector(remapped)? (a+remapped) : move(a,p=remapped); + ) apply(move(a),remapped); // Function: cylindrical_to_xyz() diff --git a/math.scad b/math.scad index 2bb5684..15f738a 100644 --- a/math.scad +++ b/math.scad @@ -26,11 +26,14 @@ NAN = acos(2); // The value `nan`, useful for comparisons. // Usage: // sqr(x); // Description: -// Returns the square of the given number. +// Returns the square of the given number or entries in list // Examples: -// sqr(3); // Returns: 9 -// sqr(-4); // Returns: 16 -function sqr(x) = x*x; +// sqr(3); // Returns: 9 +// sqr(-4); // Returns: 16 +// sqr([3,4]); // Returns: [9,16] +// sqr([[1,2],[3,4]]); // Returns [[1,4],[9,16]] +// sqr([[1,2],3]); // Returns [[1,4],9] +function sqr(x) = is_list(x) ? [for(val=x) sqr(val)] : x*x; // Function: log2() diff --git a/polyhedra.scad b/polyhedra.scad index 2afc770..1d67564 100644 --- a/polyhedra.scad +++ b/polyhedra.scad @@ -9,7 +9,7 @@ ////////////////////////////////////////////////////////////////////// -include +include // CommonCode: @@ -320,14 +320,14 @@ module regular_polyhedron( in_radius = entry[5]; if (draw){ if (rounding==0) - polyhedron(move(p=scaled_points, translation), faces = face_triangles); + 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(p=adjusted_scale*scaled_points,translation), faces = face_triangles); + polyhedron(move(translation,p=adjusted_scale*scaled_points), faces = face_triangles); } } } @@ -335,13 +335,13 @@ module regular_polyhedron( 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(p=select(scaled_points, faces[i]), translation); + facepts = move(translation, p=select(scaled_points, faces[i])); center = mean(facepts); - rotatedface = rot(p=move(p=facepts,-center), from=face_normals[i], to=[0,0,1]); + 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(p=facepts,-center), clockwise); + select(move(-center,p=facepts), clockwise); $faceindex = i; $center = -translation-center; translate(center) @@ -549,6 +549,7 @@ _stellated_polyhedra_ = [ // Calculate characteristics of regular polyhedra or the selection set for regular_polyhedron(). // Invoke with the same arguments used by regular_polyhedron() and use the `info` argument to // request the desired return value. Set `info` to: +// * `"vnf"`: vnf for the selected polyhedron // * `"vertices"`: vertex list for the selected polyhedron // * `"faces"`: list of faces for the selected polyhedron, where each entry on the list is a list of point index values to be used with the vertex list // * `"face normals"`: list of normal vectors for each face @@ -688,8 +689,16 @@ function regular_polyhedron_info( face_normals = rot(p=faces_normals_vertices[1], from=down_direction, to=[0,0,-1]), side_length = scalefactor * entry[edgelen] ) - info == "fullentry" ? [scaled_points, translation,stellate ? faces : face_triangles, faces, face_normals, side_length*entry[in_radius]] : - info == "vertices" ? move(p=scaled_points,translation) : + info == "fullentry" ? [ + scaled_points, + translation, + stellate ? faces : face_triangles, + faces, + face_normals, + side_length*entry[in_radius] + ] : + info == "vnf" ? [move(translation,p=scaled_points), stellate ? faces : face_triangles] : + info == "vertices" ? move(translation,p=scaled_points) : info == "faces" ? faces : info == "face normals" ? face_normals : info == "in_radius" ? side_length * entry[in_radius] : @@ -766,7 +775,7 @@ function _facenormal(pts, face) = unit(cross(pts[face[2]]-pts[face[0]], pts[face function _full_faces(pts,faces) = let( - normals = [for(face=faces) vquant(_facenormal(pts,face),1e-12)], + 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]]] diff --git a/vectors.scad b/vectors.scad index 9509b6c..ce82094 100644 --- a/vectors.scad +++ b/vectors.scad @@ -95,13 +95,6 @@ function vdiv(v1, v2) = [for (i = [0:1:len(v1)-1]) v1[i]/v2[i]]; function vabs(v) = [for (x=v) abs(x)]; -// Function: vsqr() -// Usage: -// x = vsqr(v); -// Description: -// Returns a vector where each value in the original given vector is squared. -function vsqr(v) = [for(i=v) i*i]; - // Function: unit() // Description: diff --git a/vnf.scad b/vnf.scad index ac4b314..d949672 100644 --- a/vnf.scad +++ b/vnf.scad @@ -349,7 +349,9 @@ module vnf_polyhedron(vnf, convexity=2) { // Usage: // vol = vnf_volume(vnf); // Description: -// Returns the volume enclosed by the given manifold VNF. May return a negative value if faces are reversed. +// Returns the volume enclosed by the given manifold VNF. The VNF must describe a valid polyhedron with consistent face direction and +// 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)) sum([ @@ -364,7 +366,10 @@ function vnf_volume(vnf) = // Usage: // vol = vnf_centroid(vnf); // Description: -// Returns the centroid of the given manifold VNF. +// Returns the centroid of the given manifold VNF. The VNF must describe a valid polyhedron with consistent face direction and +// no holes; otherwise the results are undefined. + +// Algorithm from: https://wwwf.imperial.ac.uk/~rn/centroid.pdf function vnf_centroid(vnf) = let( vnf = vnf_triangulate(vnf), @@ -376,9 +381,9 @@ function vnf_centroid(vnf) = ) [ face[0] * n, vmul(n, - vsqr(face[0] + face[1]) + - vsqr(face[0] + face[2]) + - vsqr(face[1] + face[2]) + sqr(face[0] + face[1]) + + sqr(face[0] + face[2]) + + sqr(face[1] + face[2]) ) ] ])