From 82aa4850452f957561ec92d16b740edd89bb3dcb Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 8 Apr 2022 19:37:46 -0400 Subject: [PATCH 1/5] usage message fixes and doc tweaks remove triangle area, optimize polygon_area for triangles --- bottlecaps.scad | 25 +- comparisons.scad | 96 +-- geometry.scad | 3 + hingesnaps.scad | 212 ++++--- masks2d.scad | 63 +- masks3d.scad | 87 +-- mutators.scad | 50 +- screw_drive.scad | 3 +- screws.scad | 1177 ++++++++++++++++++----------------- tests/test_comparisons.scad | 4 + tests/test_geometry.scad | 6 +- tests/test_vectors.scad | 2 + trigonometry.scad | 21 - vectors.scad | 2 +- 14 files changed, 904 insertions(+), 847 deletions(-) diff --git a/bottlecaps.scad b/bottlecaps.scad index 68ed811..9076dc0 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -19,7 +19,7 @@ include // Module: pco1810_neck() // Usage: -// pco1810_neck([wall]) +// pco1810_neck([wall]) [ATTACHMENTS]; // Description: // Creates an approximation of a standard PCO-1810 threaded beverage bottle neck. // Arguments: @@ -140,7 +140,7 @@ function pco1810_neck(wall=2, anchor="support-ring", spin=0, orient=UP) = // Module: pco1810_cap() // Usage: -// pco1810_cap([wall], [texture]); +// pco1810_cap([wall], [texture]) [ATTACHMENTS]; // Description: // Creates a basic cap for a PCO1810 threaded beverage bottle. // Arguments: @@ -211,7 +211,7 @@ function pco1810_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) = // Module: pco1881_neck() // Usage: -// pco1881_neck([wall]) +// pco1881_neck([wall]) [ATTACHMENTS]; // Description: // Creates an approximation of a standard PCO-1881 threaded beverage bottle neck. // Arguments: @@ -332,12 +332,13 @@ function pco1881_neck(wall=2, anchor="support-ring", spin=0, orient=UP) = // Module: pco1881_cap() // Usage: -// pco1881_cap(wall, [texture]); +// pco1881_cap(wall, [texture]) [ATTACHMENTS]; // Description: // Creates a basic cap for a PCO1881 threaded beverage bottle. // Arguments: // wall = Wall thickness in mm. // texture = The surface texture of the cap. Valid values are "none", "knurled", or "ribbed". Default: "none" +// --- // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` @@ -394,11 +395,12 @@ function pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) = // Module: generic_bottle_neck() // Usage: -// generic_bottle_neck([wall], ...) +// generic_bottle_neck([wall], ...) [ATTACHMENTS]; // Description: // Creates a bottle neck given specifications. // Arguments: // wall = distance between ID and any wall that may be below the support +// --- // neck_d = Outer diameter of neck without threads // id = Inner diameter of neck // thread_od = Outer diameter of thread @@ -519,7 +521,7 @@ function generic_bottle_neck( // Module: generic_bottle_cap() // Usage: -// generic_bottle_cap(wall, [texture], ...); +// generic_bottle_cap(wall, [texture], ...) [ATTACHMENTS]; // Description: // Creates a basic threaded cap given specifications. // Arguments: @@ -608,7 +610,7 @@ function generic_bottle_cap( // Module: bottle_adapter_neck_to_cap() // Usage: -// bottle_adapter_neck_to_cap(wall, [texture]); +// bottle_adapter_neck_to_cap(wall, [texture], ...) [ATTACHMENTS]; // Description: // Creates a threaded neck to cap adapter // Arguments: @@ -834,10 +836,11 @@ function bottle_adapter_cap_to_cap( // Module: bottle_adapter_neck_to_neck() // Usage: -// bottle_adapter_neck_to_neck(); +// bottle_adapter_neck_to_neck(...); // Description: // Creates a threaded neck to neck adapter. // Arguments: +// --- // d = Distance between bottoms of necks // neck_od1 = Outer diameter of top neck w/o threads // neck_id1 = Inner diameter of top neck @@ -868,6 +871,7 @@ module bottle_adapter_neck_to_neck( support_od2, pitch2, taper_lead_in = 0, wall ) { + no_children($children); neck_od2 = (neck_od2 == undef) ? neck_od1 : neck_od2; neck_id2 = (neck_id2 == undef) ? neck_id1 : neck_id2; thread_od2 = (thread_od2 == undef) ? thread_od1 : thread_od2; @@ -956,7 +960,7 @@ function bottle_adapter_neck_to_neck( // Module: sp_neck() // Usage: -// sp_neck(diam, type, wall|id, [style], [bead], [anchor], [spin], [orient]) +// sp_neck(diam, type, wall|id=, [style=], [bead=]) [ATTACHMENTS]; // Description: // Make a SPI (Society of Plastics Industry) threaded bottle neck. You must // supply the nominal outer diameter of the threads and the thread type, one of @@ -1150,6 +1154,9 @@ module sp_neck(diam,type,wall,id,style="L",bead=false, anchor, spin, orient) // true_diam = sp_diameter(diam,type) // Description: // Returns the actual base diameter (root of the threads) for a SPI plastic bottle neck given the nominal diameter and type number (400, 410, 415). +// Arguments: +// diam = nominal diameter +// type = closure type number (400, 410 or 415) function sp_diameter(diam,type) = let( table = struct_val(_sp_specs,type) diff --git a/comparisons.scad b/comparisons.scad index 1f98468..c52c150 100644 --- a/comparisons.scad +++ b/comparisons.scad @@ -47,8 +47,8 @@ function approx(a,b,eps=EPSILON) = // Usage: // x = all_zero(x, [eps]); // Description: -// Returns true if the finite number passed to it is approximately zero, to within `eps`. -// If passed a list returns true if all its entries are approximately zero. +// Returns true if its argument is approximately zero, to within `eps`. +// If passed a list returns true if all its entries are approximately equal to zero. // Otherwise, returns false. // Arguments: // x = The value to check. @@ -67,8 +67,8 @@ function all_zero(x, eps=EPSILON) = // Usage: // test = all_nonzero(x, [eps]); // Description: -// Returns true if the finite number passed to it is different from zero by `eps`. -// If passed a list returns true if all the entries of the list are different from zero by `eps`. +// Returns true if its argument is finite and different from zero by `eps`. +// If passed a list returns true if all the entries of the list are finite numbers that are different from zero by `eps`. // Otherwise, returns false. // Arguments: // x = The value to check. @@ -88,8 +88,8 @@ function all_nonzero(x, eps=EPSILON) = // Usage: // test = all_positive(x,[eps]); // Description: -// Returns true if the finite number passed to it is greater than zero. -// If passed a list returns true if all the entries are positive. +// Returns true if the argument is finite and greater than zero, within epsilon tolerance if desired. +// If passed a list returns true if all the entries are finite positive numbers. // Otherwise, returns false. // Arguments: // x = The value to check. @@ -103,7 +103,7 @@ function all_nonzero(x, eps=EPSILON) = // f = all_positive([3,1,2]); // Returns: true. // g = all_positive([3,-1,2]); // Returns: false. function all_positive(x,eps=0) = - is_num(x)? x>eps : + is_finite(x)? x>eps : is_vector(x) && [for (xx=x) if(xx<=0) 1] == []; @@ -111,8 +111,8 @@ function all_positive(x,eps=0) = // Usage: // test = all_negative(x, [eps]); // Description: -// Returns true if the finite number passed to it is less than zero. -// If passed a list, recursively checks if all items in the list are negative. +// Returns true if the argument is finite and less than zero, within epsilon tolerance if desired. +// If passed a list, returns true if all the elements are finite negative numbers. // Otherwise, returns false. // Arguments: // x = The value to check. @@ -127,7 +127,7 @@ function all_positive(x,eps=0) = // g = all_negative([3,-1,2]); // Returns: false. // h = all_negative([-3,-1,-2]); // Returns: true. function all_negative(x, eps=0) = - is_num(x)? x<-eps : + is_finite(x)? x<-eps : is_vector(x) && [for (xx=x) if(xx>=-eps) 1] == []; @@ -135,8 +135,8 @@ function all_negative(x, eps=0) = // Usage: // all_nonpositive(x, [eps]); // Description: -// Returns true if the finite number passed to it is less than or equal to zero. -// If passed a list, recursively checks if all items in the list are nonpositive. +// Returns true if its argument is finite and less than or equal to zero. +// If passed a list, returns true if all the elements are finite non-positive numbers. // Otherwise, returns false. // Arguments: // x = The value to check. @@ -160,7 +160,7 @@ function all_nonpositive(x,eps=0) = // all_nonnegative(x, [eps]); // Description: // Returns true if the finite number passed to it is greater than or equal to zero. -// If passed a list, recursively checks if all items in the list are nonnegative. +// If passed a list, returns true if all the elements are finite non-negative numbers. // Otherwise, returns false. // Arguments: // x = The value to check. @@ -196,7 +196,7 @@ function all_equal(vec,eps=0) = // Function: is_increasing() // Usage: -// bool = is_increasing(list); +// bool = is_increasing(list, [strict]); // Topics: List Handling // See Also: max_index(), min_index(), is_decreasing() // Description: @@ -205,7 +205,7 @@ function all_equal(vec,eps=0) = // evaluated character by character. // Arguments: // list = list (or string) to check -// strict = set to true to test that list is strictly increasing +// strict = set to true to test that list is strictly increasing. Default: false // Example: // a = is_increasing([1,2,3,4]); // Returns: true // b = is_increasing([1,3,2,4]); // Returns: false @@ -220,7 +220,7 @@ function is_increasing(list,strict=false) = // Function: is_decreasing() // Usage: -// bool = is_decreasing(list); +// bool = is_decreasing(list, [strict]); // Topics: List Handling // See Also: max_index(), min_index(), is_increasing() // Description: @@ -229,7 +229,7 @@ function is_increasing(list,strict=false) = // evaluated character by character. // Arguments: // list = list (or string) to check -// strict = set to true to test that list is strictly decreasing +// strict = set to true to test that list is strictly decreasing. Default: false // Example: // a = is_decreasing([1,2,3,4]); // Returns: false // b = is_decreasing([4,2,3,1]); // Returns: false @@ -256,7 +256,7 @@ function _type_num(x) = // test = compare_vals(a, b); // Description: // Compares two values. Lists are compared recursively. -// Returns <0 if a0 if a>b. Returns 0 if a==b. +// Returns a negative value if ab. Returns 0 if a==b. // If types are not the same, then undef < bool < nan < num < str < list < range. // Arguments: // a = First value to compare. @@ -274,9 +274,9 @@ function compare_vals(a, b) = // test = compare_lists(a, b) // Description: // Compare contents of two lists using `compare_vals()`. -// Returns <0 if `a`<`b`. +// Returns a negative number if `a`<`b`. // Returns 0 if `a`==`b`. -// Returns >0 if `a`>`b`. +// Returns a positive number if `a`>`b`. // Arguments: // a = First list to compare. // b = Second list to compare. @@ -312,7 +312,7 @@ function compare_lists(a, b) = // a = min_index([5,3,9,6,2,7,8,2,1]); // Returns: 8 // b = min_index([5,3,9,6,2,7,8,2,7],all=true); // Returns: [4,7] function min_index(vals, all=false) = - assert( is_vector(vals) && len(vals)>0 , "Invalid or empty list of numbers.") + assert( is_vector(vals), "Invalid or list of numbers.") all ? search(min(vals),vals,0) : search(min(vals), vals)[0]; @@ -336,11 +336,6 @@ function max_index(vals, all=false) = all ? search(max(vals),vals,0) : search(max(vals), vals)[0]; - - - - - // Section: Dealing with duplicate list entries @@ -351,14 +346,20 @@ function max_index(vals, all=false) = // idx = find_approx(val, list, [start=], [eps=]); // indices = find_approx(val, list, all=true, [start=], [eps=]); // Description: -// Finds the first item in `list` that matches `val`, returning the index. Returns `undef` if there is no match. +// Finds the first item in `list` that matches `val` to within `eps` tolerance, returning the index. Returns `undef` if there is no match. +// If `all=true` then returns all the items that agree within `eps` and returns the empty list if no such items exist. // Arguments: // val = The value to search for. -// list = The list to search through. +// list = The list to search. // --- // start = The index to start searching from. Default: 0 -// all = If true, returns a list of all matching item indices. -// eps = The maximum allowed floating point rounding error for numeric comparisons. +// all = If true, returns a list of all matching item indices. Default: false +// eps = The maximum allowed floating point rounding error for numeric comparisons. Default: EPSILON (1e-9) +// Example: +// find_approx(3,[4,5,3.01,2,2.99], eps=0.1); // Returns 2 +// find_approx(9,[4,5,3.01,2,2.99], eps=0.1); // Returns undef +// find_approx(3,[4,5,3.01,2,2.99], all=true, eps=0.1); // Returns [2,4] +// find_approx(9,[4,5,3.01,2,2.99], all=true, eps=0.1); // Returns [] function find_approx(val, list, start=0, all=false, eps=EPSILON) = all ? [for (i=[start:1:len(list)-1]) if (approx(val, list[i], eps=eps)) i] : __find_approx(val, list, eps=eps, i=start); @@ -373,7 +374,7 @@ function __find_approx(val, list, eps, i=0) = // Function: deduplicate() // Usage: -// list = deduplicate(list, [close], [eps]); +// list = deduplicate(list, [closed], [eps]); // Topics: List Handling // See Also: deduplicate_indexed() // Description: @@ -459,7 +460,7 @@ function deduplicate_indexed(list, indices, closed=false, eps=EPSILON) = // Given a string or a list returns the sorted string or the sorted list with all repeated items removed. // The sorting order of non homogeneous lists is the function `sort` order. // Arguments: -// list = The list to uniquify. +// list = The list to process. // Example: // sorted = unique([5,2,8,3,1,3,8,7,5]); // Returns: [1,2,3,5,7,8] // sorted = unique("axdbxxc"); // Returns: "abcdx" @@ -494,7 +495,7 @@ function _unique_sort(l) = // Function: unique_count() // Usage: -// counts = unique_count(list); +// sorted_counts = unique_count(list); // Topics: List Handling // See Also: shuffle(), sort(), sortidx(), unique() // Description: @@ -724,7 +725,7 @@ function sort(list, idx=undef) = // 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) = - assert(is_list(list)||is_string(list), "Invalid input." ) + assert(is_list(list)||is_string(list), "Invalid list." ) !is_list(list) || len(list)<=1 ? list : is_homogeneous(list,1) ? let( @@ -751,26 +752,31 @@ function sortidx(list, idx=undef) = // Function: group_sort() // Usage: -// ulist = group_sort(list); +// ulist = group_sort(list,[idx]); // Topics: List Handling // See Also: shuffle(), sort(), sortidx(), unique(), unique_count() // Description: -// Given a list of values, returns the sorted list with all repeated items grouped in a list. -// When the list entries are themselves lists, the sorting may be done based on the `idx` entry -// of those entries, that should be numbers. -// The result is always a list of lists. +// Given a list of numbers, sorts the list into a sequence of lists, where each list contains any repeated values. +// If there are no repeated values the output will be a list of singleton lists. +// If you apply {{flatten()}} to the output, the result will be a simple sorted list. +// . +// When the input is a list of lists, the sorting is done based on index `idx` of the entries in `list`. +// In this case, `list[i][idx]` must be a number for every `i`, and the entries in `list` are grouped +// together in the output if they match at index `idx`. This function can be used to group together +// items that are tagged with the same index. // Arguments: // list = The list to sort. -// idx = If given, do the comparison based just on the specified index. Default: zero. +// idx = If input is a list of lists, index to sort on. Default: 0. // Example: // sorted = group_sort([5,2,8,3,1,3,8,7,5]); // Returns: [[1],[2],[3,3],[5,5],[7],[8,8]] -// sorted2 = group_sort([[5,"a"],[2,"b"], [5,"c"], [3,"d"], [2,"e"] ], idx=0); // Returns: [[[2,"b"],[2,"e"]], [[5,"a"],[5,"c"]], [[3,"d"]] ] +// // Next example returns: [ [[2,"b"],[2,"e"]], [[3,"d"]], [[5,"a"],[5,"c"]] ] +// sorted2 = group_sort([[5,"a"],[2,"b"], [5,"c"], [3,"d"], [2,"e"] ], idx=0); function group_sort(list, idx) = assert(is_list(list), "Input should be a list." ) - assert(is_undef(idx) || (is_finite(idx) && idx>=0) , "Invalid index." ) + assert(is_undef(idx) || (is_int(idx) && idx>=0) , "Invalid index." ) len(list)<=1 ? [list] : - is_vector(list)? _group_sort(list) : - let( idx = is_undef(idx) ? 0 : idx ) + is_vector(list)? assert(is_undef(idx),"Cannot give idx with a vector input") _group_sort(list) : + let( idx = default(idx,0) ) assert( [for(entry=list) if(!is_list(entry) || len(entry)=0, "k must be nonnegative") + assert(is_int(k) && k>=0, "k must be nonnegative") let( v = list[rand_int(0,len(list)-1,1)[0]], smaller = [for(li=list) if(li0 && angle<90); diff --git a/mutators.scad b/mutators.scad index 3522c44..005755a 100644 --- a/mutators.scad +++ b/mutators.scad @@ -14,11 +14,11 @@ // Module: bounding_box() // Usage: -// bounding_box() ... +// bounding_box([excess],[planar]) CHILDREN; // Description: // Returns the smallest axis-aligned square (or cube) shape that contains all the 2D (or 3D) -// children given. The module children() is supposed to be a 3d shape when planar=false and -// a 2d shape when planar=true otherwise the system will issue a warning of mixing dimension +// children given. The module children() must 3d when planar=false and +// 2d when planar=true, or you will get a warning of mixing dimension // or scaling by 0. // Arguments: // excess = The amount that the bounding box should be larger than needed to bound the children, in each axis. @@ -103,7 +103,7 @@ module bounding_box(excess=0, planar=false) { // Module: chain_hull() // // Usage: -// chain_hull() ... +// chain_hull() CHILDREN; // // Description: // Performs hull operations between consecutive pairs of children, @@ -150,7 +150,7 @@ module chain_hull() // Module: path_extrude2d() // Usage: -// path_extrude2d(path, [caps], [closed]) {...} +// path_extrude2d(path, [caps=], [closed=], [s=], [convexity=]) 2D-CHILDREN; // Description: // Extrudes 2D children along the given 2D path, with optional rounded endcaps. // It works by constructing straight sections corresponding to each segment of the path and inserting rounded joints at each corner. @@ -158,6 +158,7 @@ module chain_hull() // If you set caps to true for asymmetric children then incorrect caps will be generated. // Arguments: // path = The 2D path to extrude the geometry along. +// --- // caps = If true, caps each end of the path with a rounded copy of the children. Children must by symmetric across the Y axis, or results are wrong. Default: false // closed = If true, connect the starting point of the path to the ending point. Default: false // convexity = The max number of times a line could pass though a wall. Default: 10 @@ -261,13 +262,15 @@ module path_extrude2d(path, caps=false, closed=false, s, convexity=10) { // Module: cylindrical_extrude() // Usage: -// cylindrical_extrude(size, ir|id, or|od, [convexity]) ... +// cylindrical_extrude(ir|id=, or|od=, [size=], [convexity=], [spin=], [orient=]) 2D-CHILDREN; // Description: -// Extrudes all 2D children outwards, curved around a cylindrical shape. +// Extrudes its 2D children outwards, curved around a cylindrical shape. Uses $fn/$fa/$fs to +// control the faceting of the extrusion. // Arguments: -// or = The outer radius to extrude to. -// od = The outer diameter to extrude to. // ir = The inner radius to extrude from. +// or = The outer radius to extrude to. +// --- +// od = The outer diameter to extrude to. // id = The inner diameter to extrude from. // size = The [X,Y] size of the 2D children to extrude. Default: [1000,1000] // convexity = The max number of times a line could pass though a wall. Default: 10 @@ -282,11 +285,12 @@ module path_extrude2d(path, caps=false, closed=false, s, convexity=10) { // Example: Orient to the Y Axis. // 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) { +module cylindrical_extrude(ir, or, 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); + assert(all_positive([ir,or]), "Must supply positive inner and outer radius or diameter"); index_r = or; circumf = 2 * PI * index_r; width = min(size.x, circumf); @@ -316,11 +320,15 @@ module cylindrical_extrude(or, ir, od, id, size=1000, convexity=10, spin=0, orie // Module: extrude_from_to() +// Usage: +// extrude_from_to(pt1, pt2, [convexity=], [twist=], [scale=], [slices=]) 2D-CHILDREN; // Description: -// Extrudes a 2D shape between the 3d points pt1 and pt2. Takes as children a set of 2D shapes to extrude. +// Extrudes the 2D children linearly between the 3d points pt1 and pt2. The origin of the 2D children are placed on +// pt1 and pt2, and oriented perpendicular to the line between the points. // Arguments: // pt1 = starting point of extrusion. // pt2 = ending point of extrusion. +// --- // convexity = max number of times a line could intersect a wall of the 2D shape being extruded. // twist = number of degrees to twist the 2D shape over the entire extrusion length. // scale = scale multiplier for end of extrusion compared the start. @@ -349,8 +357,10 @@ module extrude_from_to(pt1, pt2, convexity, twist, scale, slices) { // Module: path_extrude() +// Usage: path_extrude(path, [convexity], [clipsize]) 2D-CHILDREN; // Description: -// Extrudes 2D children along a 3D path. This may be slow. +// Extrudes 2D children along a 3D path. This may be slow and can have problems with twisting. +// See Also: path_sweep() // Arguments: // path = Array of points for the bezier path to extrude along. // convexity = Maximum number of walls a ray can pass through. @@ -407,7 +417,7 @@ module path_extrude(path, convexity=10, clipsize=100) { // Module: minkowski_difference() // Usage: -// minkowski_difference() { base_shape(); diff_shape(); ... } +// minkowski_difference() { BASE; DIFF1; DIFF2; ... } // Description: // Takes a 3D base shape and one or more 3D diff shapes, carves out the diff shapes from the // surface of the base shape, in a way complementary to how `minkowski()` unions shapes to the @@ -443,16 +453,16 @@ module minkowski_difference(planar=false) { // Module: offset3d() // Usage: -// offset3d(r, [size], [convexity]); +// offset3d(r, [size], [convexity]) CHILDREN; // Description: // Expands or contracts the surface of a 3D object by a given amount. This is very, very slow. // No really, this is unbearably slow. It uses `minkowski()`. Use this as a last resort. // This is so slow that no example images will be rendered. // Arguments: -// r = Radius to expand object by. Negative numbers contract the object. +// r = Radius to expand object by. Negative numbers contract the object. // 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) { +module offset3d(r, size=100, convexity=10) { n = quant(max(8,segs(abs(r))),4); if (r==0) { children(); @@ -482,10 +492,10 @@ module offset3d(r=1, size=100, convexity=10) { // Module: round3d() // Usage: -// round3d(r) ... -// round3d(or) ... -// round3d(ir) ... -// round3d(or, ir) ... +// round3d(r) CHILDREN; +// round3d(or) CHILDREN; +// round3d(ir) CHILDREN; +// round3d(or, ir) CHILDREN; // Description: // Rounds arbitrary 3D objects. Giving `r` rounds all concave and convex corners. Giving just `ir` // rounds just concave corners. Giving just `or` rounds convex corners. Giving both `ir` and `or` diff --git a/screw_drive.scad b/screw_drive.scad index 96feec6..435029d 100644 --- a/screw_drive.scad +++ b/screw_drive.scad @@ -12,6 +12,7 @@ // Section: Phillips Drive // Module: phillips_mask() +// Usage: phillips_mask(size) [ATTACHMENTS]; // Description: // Creates a mask for creating a Phillips drive recess given the Phillips size. Each mask can // be lowered to different depths to create different sizes of recess. @@ -129,7 +130,7 @@ function phillips_diam(size, depth) = // Module: torx_mask() // Usage: -// torx_mask(size, l, [center]); +// torx_mask(size, l, [center]) [ATTACHMENTS]; // Description: Creates a torx bit tip. // Arguments: // size = Torx size. diff --git a/screws.scad b/screws.scad index 9da2bf6..f3e7f03 100644 --- a/screws.scad +++ b/screws.scad @@ -28,6 +28,522 @@ Torx values: https://www.stanleyengineeredfastening.com/-/media/web/sef/resourc */ + + +// Module: screw() +// Usage: +// screw([name], [head], [drive], [thread=], [drive_size=], [length=|l=], [shank=], [oversize=], [tolerance=], [$slop=], [spec=], [details=], [anchor=], [anchor_head=], [orient=], [spin=]) [ATTACHMENTS]; +// Description: +// Create a screw. +// . +// Most of these parameters are described in the entry for `screw_info()`. +// . +// The tolerance determines the actual thread sizing based on the +// nominal size. For UTS threads it is either "1A", "2A" or "3A", in +// order of increasing tightness. The default tolerance is "2A", which +// is the general standard for manufactured bolts. For ISO the tolerance +// has the form of a number and letter. The letter specifies the "fundamental deviation", also called the "tolerance position", the gap +// from the nominal size, and must be "e", "f", "g", or "h", where "e" is +// the loosest and "h" means no gap. The number specifies the allowed +// range (variability) of the thread heights. It must be a value from +// 3-9 for crest diameter and one of 4, 6, or 8 for pitch diameter. A +// tolerance "6g" specifies both pitch and crest diameter to be the same, +// but they can be different, with a tolerance like "5g6g" specifies a pitch diameter tolerance of "5g" and a crest diameter tolerance of "6g". +// Smaller numbers give a tighter tolerance. The default ISO tolerance is "6g". +// . +// The $slop argument gives an extra gap to account for printing overextrusion. It defaults to 0. +// Arguments: +// name = screw specification, e.g. "M5x1" or "#8-32" +// head = head type (see list above). Default: none +// drive = drive type. Default: none +// --- +// thread = thread type or specification. Default: "coarse" +// drive_size = size of drive recess to override computed value +// oversize = amount to increase screw diameter for clearance holes. Default: 0 +// spec = screw specification from `screw_info()`. If you specify this you can omit all the preceeding parameters. +// length = length of screw (in mm) +// shank = length of unthreaded portion of screw (in mm). Default: 0 +// details = toggle some details in rendering. Default: false +// tolerance = screw tolerance. Determines actual screw thread geometry based on nominal sizing. Default is "2A" for UTS and "6g" for ISO. +// $slop = add extra gap to account for printer overextrusion. Default: 0 +// anchor = Translate so anchor point on the shaft is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `BOTTOM` +// anchor_head = Translate so anchor point on the head is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// Example(Med): Selected UTS (English) screws +// $fn=32; +// xdistribute(spacing=8){ +// screw("#6", length=12); +// screw("#6-32", head="button", drive="torx",length=12); +// screw("#6-32,3/4", head="hex"); +// screw("#6", thread="fine", head="fillister",length=12, drive="phillips"); +// screw("#6", head="flat small",length=12,drive="slot"); +// screw("#6-32", head="flat large", length=12, drive="torx"); +// screw("#6-32", head="flat undercut",length=12); +// screw("#6-24", head="socket",length=12); // Non-standard threading +// screw("#6-32", drive="hex", drive_size=1.5, length=12); +// } +// Example(Med): A few examples of ISO (metric) screws +// $fn=32; +// xdistribute(spacing=8){ +// screw("M3", head="flat small",length=12); +// screw("M3", head="button",drive="torx",length=12); +// screw("M3", head="pan", drive="phillips",length=12); +// screw("M3x1", head="pan", drive="slot",length=12); // Non-standard threading! +// screw("M3", head="flat large",length=12); +// screw("M3", thread="none", head="flat", drive="hex",length=12); // No threads +// screw("M3", head="socket",length=12); +// screw("M5", head="hex", length=12); +// } +// Example(Med): Demonstration of all head types for UTS screws (using pitch zero for fast preview) +// xdistribute(spacing=15){ +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="none", drive="hex"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="none", drive="torx"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="none"); +// } +// screw("1/4", thread=0, length=8, anchor=TOP, head="hex"); +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="socket", drive="hex"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="socket", drive="torx"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="socket"); +// } +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="button", drive="hex"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="button", drive="torx"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="button"); +// } +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="round", drive="slot"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="round", drive="phillips"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="round"); +// } +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="fillister", drive="slot"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="fillister", drive="phillips"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="fillister"); +// } +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="slot"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="phillips"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="hex"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="torx"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat large"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat small"); +// } +// ydistribute(spacing=15){ +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat undercut", drive="slot"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat undercut", drive="phillips"); +// screw("1/4", thread=0,length=8, anchor=TOP, head="flat undercut"); +// } +// } +// Example(Med): Demonstration of all head types for metric screws without threading. +// xdistribute(spacing=15){ +// ydistribute(spacing=15){ +// screw("M6x0", length=8, anchor=TOP, head="none", drive="hex"); +// screw("M6x0", length=8, anchor=TOP, head="none", drive="torx"); +// screw("M6x0", length=8, anchor=TOP); +// } +// screw("M6x0", length=8, anchor=TOP, head="hex"); +// ydistribute(spacing=15){ +// screw("M6x0", length=8, anchor=TOP, head="socket", drive="hex"); +// screw("M6x0", length=8, anchor=TOP, head="socket", drive="torx"); +// screw("M6x0", length=8, anchor=TOP, head="socket"); +// } +// ydistribute(spacing=15){ +// screw("M6x0", length=8, anchor=TOP, head="pan", drive="slot"); +// screw("M6x0", length=8, anchor=TOP, head="pan", drive="phillips"); +// screw("M6x0", length=8, anchor=TOP, head="pan"); +// screw("M6x0", length=8, anchor=TOP, head="pan flat"); +// } +// ydistribute(spacing=15){ +// screw("M6x0", length=8, anchor=TOP, head="button", drive="hex"); +// screw("M6x0", length=8, anchor=TOP, head="button", drive="torx"); +// screw("M6x0", length=8, anchor=TOP, head="button"); +// } +// ydistribute(spacing=15){ +// screw("M6x0", length=8, anchor=TOP, head="cheese", drive="slot"); +// screw("M6x0", length=8, anchor=TOP, head="cheese", drive="phillips"); +// screw("M6x0", length=8, anchor=TOP, head="cheese"); +// } +// ydistribute(spacing=15){ +// screw("M6x0", length=8, anchor=TOP, head="flat", drive="phillips"); +// screw("M6x0", length=8, anchor=TOP, head="flat", drive="slot"); +// screw("M6x0", length=8, anchor=TOP, head="flat", drive="hex"); +// screw("M6x0", length=8, anchor=TOP, head="flat", drive="torx"); +// screw("M6x0", length=8, anchor=TOP, head="flat small"); +// screw("M6x0", length=8, anchor=TOP, head="flat large"); +// } +// } +// Example: The three different English (UTS) screw tolerances +// module label(val) +// { +// difference(){ +// children(); +// yflip()linear_extrude(height=.35) text(val,valign="center",halign="center",size=8); +// } +// } +// $fn=64; +// xdistribute(spacing=15){ +// label("1") screw("1/4-20,5/8", head="hex",orient=DOWN,anchor_head=TOP,tolerance="1A"); // Loose +// label("2") screw("1/4-20,5/8", head="hex",orient=DOWN,anchor_head=TOP,tolerance="2A"); // Standard +// label("3") screw("1/4-20,5/8", head="hex",orient=DOWN,anchor_head=TOP,tolerance="3A"); // Tight +// } +// Example(2D): This example shows the gap between nut and bolt at the loosest tolerance for UTS. This gap is what enables the parts to mesh without binding and is part of the definition for standard metal hardware. Note that this gap is part of the standard definition for the metal hardware, not the 3D printing adjustment provided by the $slop parameter. +// $slop=0; +// $fn=32; +// projection(cut=true)xrot(-90){ +// screw("1/4-20,1/4", head="hex",orient=UP,anchor=BOTTOM,tolerance="1A"); +// down(INCH*1/20*2.145) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="1B"); +// } + +function screw(name, head, drive, thread="coarse", drive_size, oversize=0, spec, length, l, shank=0, tolerance=undef, details=true, anchor=undef,anchor_head=undef,spin=0, orient=UP) = no_function("screw"); + +module screw(name, head, drive, thread="coarse", drive_size, oversize=0, spec, length, l, shank=0, tolerance=undef, details=true, anchor=undef,anchor_head=undef,spin=0, orient=UP) +{ + spec = _validate_screw_spec( + is_def(spec) ? spec : screw_info(name, head, thread, drive, drive_size, oversize) ); + echo_struct(spec,"spec"); + head = struct_val(spec,"head"); + pitch = struct_val(spec, "pitch"); + diameter = struct_val(spec, "diameter"); + headless = head=="none" || head==undef; + eps = headless || starts_with(head,"flat") ? 0 : 0.01; + screwlen = one_defined([l,length],"l,length",dflt=undef); + length = first_defined([screwlen,struct_val(spec,"length")]) + eps; + assert(length>0, "Must specify positive length"); + sides = max(12, segs(diameter/2)); + unthreaded = is_undef(pitch) || pitch==0 ? length : shank; + threaded = length - unthreaded; + echo(t=threaded,length,unthreaded); + head_height = headless || starts_with(head, "flat") ? 0 : struct_val(spec, "head_height"); + head_diam = struct_val(spec, "head_size"); + head_size = headless ? [diameter, diameter, head_height] : + head == "hex" ? [head_diam, head_diam*2/sqrt(3), head_height] : + [head_diam, head_diam, head_height]; + assert(num_defined([anchor,anchor_head])<=1, "Cannot define both `anchor` and `anchor_head`"); + head_anchor = is_def(anchor_head); + attachable( + d = head_anchor ? head_size[0] : diameter, // This code should be tweaked to pass diameter and length more cleanly + l = head_anchor ? head_size[2] : length, + orient = orient, + anchor = first_defined([anchor, anchor_head, BOTTOM]), + //offset = head_anchor ? [0,0,head_height/2] : [0,0,-length/2], + spin = spin + ) + { + up(head_anchor ? -head_height/2 : length/2) + difference(){ + union(){ + screw_head(spec,details); + up(eps){ + if (unthreaded>0){ + cyl(d=diameter, h=unthreaded+eps+(threaded>0?0.01:0), anchor=TOP, $fn=sides); + } + if (threaded>0) + intersection(){ + down(unthreaded) + _rod(spec, length=threaded+eps, tolerance=tolerance, $fn=sides, anchor=TOP ); + if (details) + up(.01)cyl(d=diameter, l=length+.02+eps, chamfer1 = pitch/2, chamfer2 = headless ? pitch/2 : -pitch/2, anchor=TOP, $fn=sides); + } + } + } + _driver(spec); + } + children(); + } +} + + +module _driver(spec) +{ + drive = struct_val(spec,"drive"); + echo(drive=drive); + if (is_def(drive) && drive!="none") { + echo(inside_drive=drive); + head = struct_val(spec,"head"); + diameter = struct_val(spec,"diameter"); + drive_size = struct_val(spec,"drive_size"); + drive_width = struct_val(spec,"drive_width"); + drive_diameter = struct_val(spec, "drive_diameter"); + drive_depth = first_defined([struct_val(spec, "drive_depth"), .7*diameter]); // Note hack for unspecified depth + head_top = starts_with(head,"flat") || head=="none" ? 0 : + struct_val(spec,"head_height"); + echo(drive_size=drive_size); + up(head_top-drive_depth){ + // recess should be positioned with its bottom center at (0,0) and the correct recess depth given above + if (drive=="phillips") phillips_mask(drive_size,anchor=BOTTOM); + if (drive=="torx") torx_mask(size=drive_size, l=drive_depth+1, center=false); + if (drive=="hex") linear_extrude(height=drive_depth+1) hexagon(id=drive_size); + if (drive=="slot") cuboid([2*struct_val(spec,"head_size"), drive_width, drive_depth+1],anchor=BOTTOM); + } + } +} + + +function _ISO_thread_tolerance(diameter, pitch, internal=false, tolerance=undef) = + let( + P = pitch, + H = P*sqrt(3)/2, + tolerance = first_defined([tolerance, internal?"6H":"6g"]), + + pdiam = diameter - 2*3/8*H, // nominal pitch diameter + mindiam = diameter - 2*5/8*H, // nominal minimum diameter + + EI = [ // Fundamental deviations for nut thread + ["G", 15+11*P], + ["H", 0], // Standard practice + ], + + es = [ // Fundamental deviations for bolt thread + ["e", -(50+11*P)], // Exceptions if P<=0.45mm + ["f", -(30+11*P)], + ["g", -(15+11*P)], // Standard practice + ["h", 0] // Standard practice for tight fit + ], + + T_d6 = 180*pow(P,2/3)-3.15/sqrt(P), + T_d = [ // Crest diameter tolerance for major diameter of bolt thread + [4, 0.63*T_d6], + [6, T_d6], + [8, 1.6*T_d6] + ], + + T_D1_6 = 0.2 <= P && P <= 0.8 ? 433*P - 190*pow(P,1.22) : + P > .8 ? 230 * pow(P,0.7) : undef, + T_D1 = [ // Crest diameter tolerance for minor diameter of nut thread + [4, 0.63*T_D1_6], + [5, 0.8*T_D1_6], + [6, T_D1_6], + [7, 1.25*T_D1_6], + [8, 1.6*T_D1_6] + ], + + rangepts = [0.99, 1.4, 2.8, 5.6, 11.2, 22.4, 45, 90, 180, 300], + d_ind = floor(lookup(diameter,hstack(rangepts,count(len(rangepts))))), + avgd = sqrt(rangepts[d_ind]* rangepts[d_ind+1]), + + T_d2_6 = 90*pow(P, 0.4)*pow(avgd,0.1), + T_d2 = [ // Pitch diameter tolerance for bolt thread + [3, 0.5*T_d2_6], + [4, 0.63*T_d2_6], + [5, 0.8*T_d2_6], + [6, T_d2_6], + [7, 1.25*T_d2_6], + [8, 1.6*T_d2_6], + [9, 2*T_d2_6], + ], + + T_D2 = [ // Tolerance for pitch diameter of nut thread + [4, 0.85*T_d2_6], + [5, 1.06*T_d2_6], + [6, 1.32*T_d2_6], + [7, 1.7*T_d2_6], + [8, 2.12*T_d2_6] + ], + + internal = is_def(internal) ? internal : tolerance[1] != downcase(tolerance[1]), + internalok = !internal || ( + len(tolerance)==2 && str_find("GH",tolerance[1])!=undef && str_find("45678",tolerance[0])!=undef), + tol_str = str(tolerance,tolerance), + externalok = internal || ( + (len(tolerance)==2 || len(tolerance)==4) + && str_find("efgh", tol_str[1])!=undef + && str_find("efgh", tol_str[3])!=undef + && str_find("3456789", tol_str[0]) != undef + && str_find("468", tol_str[2]) !=undef) + ) + assert(internalok,str("Invalid internal thread tolerance, ",tolerance,". Must have form ")) + assert(externalok,str("invalid external thread tolerance, ",tolerance,". Must have form or ")) + let( + tol_num_pitch = parse_num(tol_str[0]), + tol_num_crest = parse_num(tol_str[2]), + tol_letter = tol_str[1] + ) + assert(tol_letter==tol_str[3],str("Invalid tolerance, ",tolerance,". Cannot mix different letters")) + internal ? + let( // Nut case + //a=echo("nut", tol_letter, tol_num_pitch, tol_num_crest), + fdev = struct_val(EI,tol_letter)/1000, + Tdval = struct_val(T_D1, tol_num_crest)/1000, + df= echo(T_D1=T_D1), + Td2val = struct_val(T_D2, tol_num_pitch)/1000, + //fe= echo("nut",P,fdev=fdev, Tdval=Tdval, Td2val=Td2val), + bot=[diameter+fdev, diameter+fdev+Td2val+H/6], + xdiam = [mindiam+fdev,mindiam+fdev+Tdval], + pitchdiam = [pdiam + fdev, pdiam+fdev+Td2val] + ) + [["pitch",P],["d_minor",xdiam], ["d_pitch",pitchdiam], ["d_major",bot],["basic",[mindiam,pdiam,diameter]]] + : + let( // Bolt case + //a=echo("bolt"), + fdev = struct_val(es,tol_letter)/1000, + Tdval = struct_val(T_d, tol_num_crest)/1000, + Td2val = struct_val(T_d2, tol_num_pitch)/1000, + mintrunc = P/8, + d1 = diameter-5*H/4, + maxtrunc = H/4 - mintrunc * (1-cos(60-acos(1-Td2val/4/mintrunc)))+Td2val/2, + //cc=echo("bolt",P,fdev=fdev, Tdval=Tdval, Td2val=Td2val), + bot = [diameter-2*H+2*mintrunc+fdev, diameter-2*H+2*maxtrunc+fdev], + xdiam = [diameter+fdev,diameter+fdev-Tdval], + pitchdiam = [pdiam + fdev, pdiam+fdev-Td2val] + ) + [["pitch",P],["d_major",xdiam], ["d_pitch",pitchdiam], ["d_minor",bot],["basic",[mindiam,pdiam,diameter]]]; + +function _UTS_thread_tolerance(diam, pitch, internal=false, tolerance=undef) = + let( + d = diam/INCH, // diameter in inches + P = pitch/INCH, // pitch in inches + H = P*sqrt(3)/2, + tolerance = first_defined([tolerance, internal?"2B":"2A"]), + tolOK = in_list(tolerance, ["1A","1B","2A","2B","3A","3B"]), + internal = tolerance[1]=="B" + ) + assert(tolOK,str("Tolerance was ",tolerance,". Must be one of 1A, 2A, 3A, 1B, 2B, 3B")) + let( + LE = 9*P, // length of engagement. Is this right? + pitchtol_2A = 0.0015*pow(d,1/3) + 0.0015*sqrt(LE) + 0.015*pow(P,2/3), + pitchtol_table = [ + ["1A", 1.500*pitchtol_2A], + ["2A", pitchtol_2A], + ["3A", 0.750*pitchtol_2A], + ["1B", 1.950*pitchtol_2A], + ["2B", 1.300*pitchtol_2A], + ["3B", 0.975*pitchtol_2A] + ], + pitchtol = struct_val(pitchtol_table, tolerance), + allowance = tolerance=="1A" || tolerance=="2A" ? 0.3 * pitchtol_2A : 0, + majortol = tolerance == "1A" ? 0.090*pow(P,2/3) : + tolerance == "2A" || tolerance == "3A" ? 0.060*pow(P,2/3) : + pitchtol+pitch/4/sqrt(3), // Internal case + minortol = tolerance=="1B" || tolerance=="2B" ? + ( + d < 0.25 ? constrain(0.05*pow(P,2/3)+0.03*P/d - 0.002, 0.25*P-0.4*P*P, 0.394*P) + : (P > 0.25 ? 0.15*P : 0.25*P-0.4*P*P) + ) : + tolerance=="3B" ? constrain(0.05*pow(P,2/3)+0.03*P/d - 0.002, P<1/13 ? 0.12*P : 0.23*P-1.5*P*P, 0.394*P) + :0, // not used for external threads + //f=echo(allowance=allowance), + //g=echo(pta2 = pitchtol_2A), + // ff=echo(minortol=minortol, pitchtol=pitchtol, majortol=majortol), + basic_minordiam = d - 5/4*H, + basic_pitchdiam = d - 3/4*H, + majordiam = internal ? [d,d] : // A little confused here, paragraph 8.3.2 + [d-allowance-majortol, d-allowance], + //ffda=echo(allowance=allowance, majortol=majortol, "*****************************"), + pitchdiam = internal ? [basic_pitchdiam, basic_pitchdiam + pitchtol] + : [majordiam[1] - 3/4*H-pitchtol, majordiam[1]-3/4*H], + minordiam = internal ? [basic_minordiam, basic_minordiam + minortol] + : [pitchdiam[0] - 3/4*H, basic_minordiam - allowance - H/8] // the -H/8 is for the UNR case, 0 for UN case + ) + [["pitch",P*INCH],["d_major",majordiam*INCH], ["d_pitch", pitchdiam*INCH], ["d_minor",minordiam*INCH], + ["basic", INCH*[basic_minordiam, basic_pitchdiam, d]]]; + +function _exact_thread_tolerance(d,P) = + let( + H = P*sqrt(3)/2, + basic_minordiam = d - 5/4*H, + basic_pitchdiam = d - 3/4*H + ) + [["pitch", P], ["d_major", d], ["d_pitch", basic_pitchdiam], ["d_minor", basic_minordiam], + ["basic", [basic_minordiam, basic_pitchdiam, d]]]; + + + +module _rod(spec, length, tolerance, orient=UP, spin=0, anchor=CENTER) +{ + threadspec = thread_specification(spec, internal=false, tolerance=tolerance); + echo(d_major_mean = mean(struct_val(threadspec, "d_major"))); + + threaded_rod([mean(struct_val(threadspec, "d_minor")), + mean(struct_val(threadspec, "d_pitch")), + mean(struct_val(threadspec, "d_major"))], + pitch = struct_val(threadspec, "pitch"), + l=length, left_handed=false, + bevel=false, orient=orient, anchor=anchor, spin=spin); +} + + +// Module: nut() +// Usage: +// nut([name], diameter, thickness, [thread=], [oversize=], [spec=], [tolerance=], [$slop=]) [ATTACHMENTS]; +// Description: +// Generates a hexagonal nut. +// The name, thread and oversize parameters are described under `screw_info()`. As for screws, +// you can give the specification in `spec` and then omit the name. The diameter is the flat-to-flat +// size of the nut produced. +// . +// The tolerance determines the actual thread sizing based on the +// nominal size. +// For UTS threads the tolerance is either "1B", "2B" or "3B", in +// order of increasing tightness. The default tolerance is "2B", which +// is the general standard for manufactured nuts. For ISO the tolerance +// has the form of a number and letter. The letter specifies the "fundamental deviation", also called the "tolerance position", the gap +// from the nominal size, and must be "G", or "H", where "G" is looser +// he loosest and "H" means no gap. The number specifies the allowed +// range (variability) of the thread heights. Smaller numbers give tigher tolerances. It must be a value from +// 4-8, so an allowed (loose) tolerance is "7G". The default ISO tolerance is "6H". +// . +// The $slop parameter determines extra gaps left to account for printing overextrusion. It defaults to 0. +// Arguments: +// name = screw specification, e.g. "M5x1" or "#8-32" +// diameter = outside diameter of nut (flat to flat dimension) +// thickness = thickness of nut (in mm) +// --- +// thread = thread type or specification. Default: "coarse" +// oversize = amount to increase screw diameter for clearance holes. Default: 0 +// spec = screw specification from `screw_info()`. If you specify this you can omit all the preceeding parameters. +// bevel = bevel the nut. Default: false +// tolerance = nut tolerance. Determines actual nut thread geometry based on nominal sizing. Default is "2B" for UTS and "6H" for ISO. +// $slop = extra space left to account for printing over-extrusion. Default: 0 +// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` +// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` +// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` +// Example: A metric and UTS nut +// nut("3/8", 5/8*INCH, 1/4*INCH); +// right(25) +// nut("M8", 16, 6); +// Example: The three different UTS nut tolerances +// module mark(number) +// { +// difference(){ +// children(); +// ycopies(n=number, spacing=1.5)right(.25*INCH-2)up(8-.35)cyl(d=1, h=1); +// } +// } +// $fn=64; +// xdistribute(spacing=17){ +// mark(1) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="1B"); +// mark(2) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="2B"); +// mark(3) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="3B"); +// } + +function nut(name, diameter, thickness, thread="coarse", oversize=0, spec, tolerance=undef, + bevel=false, anchor=BOTTOM,spin=0, orient=UP) = no_function("nut"); + +module nut(name, diameter, thickness, thread="coarse", oversize=0, spec, tolerance=undef, + bevel=false, anchor=BOTTOM,spin=0, orient=UP) +{ + assert(is_num(diameter) && diameter>0); + assert(is_num(thickness) && thickness>0); + spec = is_def(spec) ? spec : screw_info(name, thread=thread, oversize=oversize); + threadspec = thread_specification(spec, internal=true, tolerance=tolerance); + echo(threadspec=threadspec,"for nut threads"); + echo(nut_minor_diam = mean(struct_val(threadspec,"d_minor"))); + threaded_nut( + od=diameter, + id=[mean(struct_val(threadspec, "d_minor")), + mean(struct_val(threadspec, "d_pitch")), + mean(struct_val(threadspec, "d_major"))], + pitch = struct_val(threadspec, "pitch"), + h=thickness, + bevel=bevel, + anchor=anchor,spin=spin,orient=orient) children(); +} + + + function _parse_screw_name(name) = let( commasplit = str_split(name,","), length = parse_num(commasplit[1]), @@ -56,9 +572,64 @@ function _parse_drive(drive=undef, drive_size=undef) = assert(str("Unknown screw drive type ",drive)); +// Module: screw_head() +// Usage: +// screw_head(screw_info, [details]) +// Description: +// Draws the screw head described by the data structure `screw_info`, which +// should have the fields produced by {{screw_info()}}. See that function for +// details on the fields. Standard orientation is with the head centered at (0,0) +// and oriented in the +z direction. Flat heads appear below the xy plane. +// Other heads appear sitting on the xy plane. +// Arguments: +// screw_info = structure produced by {{screw_info()}} +// details = true for more detailed model. Default: false +function screw_head(screw_info,details=false = no_function("screw_head"); +module screw_head(screw_info,details=false) { + no_children($children); + head = struct_val(screw_info, "head"); + head_size = struct_val(screw_info, "head_size"); + head_height = struct_val(screw_info, "head_height"); + if (head=="flat") { + angle = struct_val(screw_info, "head_angle")/2; + full_height = head_size/2/tan(angle); + height = is_def(head_height) ? head_height : full_height; + d2 = head_size*(1-height/full_height); + //down(height) + zflip() + cyl(d1=head_size, d2=d2, l=height, anchor=BOTTOM); + } + if (in_list(head,["round","pan round","button","fillister","cheese"])) { + base = head=="fillister" ? 0.75*head_height : + head=="pan round" ? .6 * head_height : + head=="cheese" ? .7 * head_height : + 0.1 * head_height; // round and button + head_size2 = head=="cheese" ? head_size-2*tan(5)*head_height : head_size; // 5 deg slope on cheese head + cyl(l=base, d1=head_size, d2=head_size2,anchor=BOTTOM, $fn=32) + attach(TOP) + rotate_extrude($fn=32) + intersection(){ + arc(points=[[-head_size2/2,0], [0,-base+head_height * (head=="button"?4/3:1)], [head_size2/2,0]]); + square([head_size2, head_height-base]); + } + } + if (head=="pan flat") + cyl(l=head_height, d=head_size, rounding2=0.2*head_size, anchor=BOTTOM); + if (head=="socket") + cyl(l=head_height, d=head_size, anchor=BOTTOM); + if (head=="hex") + intersection(){ + linear_extrude(height=head_height) hexagon(id=head_size); + if (details) + down(.01)cyl(l=head_height+.02,d=2*head_size/sqrt(3), chamfer=head_size*(1/sqrt(3)-1/2), anchor=BOTTOM); + } +} + + + // Function: screw_info() // Usage: -// info = screw_info(name, [head], [thread], [drive], [drive_size], [oversize]) +// info = screw_info(name, [head], [drive], [thread=], [drive_size=], [oversize=]) // // Description: // Look up screw characteristics for the specified screw type. @@ -154,11 +725,12 @@ function _parse_drive(drive=undef, drive_size=undef) = // Arguments: // name = screw specification, e.g. "M5x1" or "#8-32" // head = head type (see list above). Default: none -// thread = thread type or specification. Default: "coarse" // drive = drive type. Default: none +// --- +// thread = thread type or specification. Default: "coarse" // drive_size = size of drive recess to override computed value // oversize = amount to increase screw diameter for clearance holes. Default: 0 -function screw_info(name, head, thread="coarse", drive, drive_size=undef, oversize=0) = +function screw_info(name, head, drive, thread="coarse", drive_size=undef, oversize=0) = let(type=_parse_screw_name(name), drive_info = _parse_drive(drive, drive_size), drive=drive_info[0], @@ -751,574 +1323,6 @@ function _screw_info_metric(diam, pitch, head, thread, drive) = ); -// Module: screw_head() -// Usage: -// screw_head(screw_info, [details]) -// Description: -// Draws the screw head described by the data structure `screw_info`, which -// should have the fields produced by `screw_info()`. See that function for -// details on the fields. Standard orientation is with the head centered at (0,0) -// and oriented in the +z direction. Flat heads appear below the xy plane. -// Other heads appear sitting on the xy plane. -module screw_head(screw_info,details=false) { - head = struct_val(screw_info, "head"); - head_size = struct_val(screw_info, "head_size"); - head_height = struct_val(screw_info, "head_height"); - if (head=="flat") { - angle = struct_val(screw_info, "head_angle")/2; - full_height = head_size/2/tan(angle); - height = is_def(head_height) ? head_height : full_height; - d2 = head_size*(1-height/full_height); - //down(height) - zflip() - cyl(d1=head_size, d2=d2, l=height, anchor=BOTTOM); - } - if (in_list(head,["round","pan round","button","fillister","cheese"])) { - base = head=="fillister" ? 0.75*head_height : - head=="pan round" ? .6 * head_height : - head=="cheese" ? .7 * head_height : - 0.1 * head_height; // round and button - head_size2 = head=="cheese" ? head_size-2*tan(5)*head_height : head_size; // 5 deg slope on cheese head - cyl(l=base, d1=head_size, d2=head_size2,anchor=BOTTOM, $fn=32) - attach(TOP) - rotate_extrude($fn=32) - intersection(){ - arc(points=[[-head_size2/2,0], [0,-base+head_height * (head=="button"?4/3:1)], [head_size2/2,0]]); - square([head_size2, head_height-base]); - } - } - if (head=="pan flat") - cyl(l=head_height, d=head_size, rounding2=0.2*head_size, anchor=BOTTOM); - if (head=="socket") - cyl(l=head_height, d=head_size, anchor=BOTTOM); - if (head=="hex") - intersection(){ - linear_extrude(height=head_height) hexagon(id=head_size); - if (details) - down(.01)cyl(l=head_height+.02,d=2*head_size/sqrt(3), chamfer=head_size*(1/sqrt(3)-1/2), anchor=BOTTOM); - } -} - - -// Module: screw() -// Usage: -// screw([name],[head],[thread],[drive],[drive_size], [length], [shank], [oversize], [tolerance], [$slop], [spec], [details], [anchor], [anchor_head], [orient], [spin]) -// Description: -// Create a screw. -// . -// Most of these parameters are described in the entry for `screw_info()`. -// . -// The tolerance determines the actual thread sizing based on the -// nominal size. For UTS threads it is either "1A", "2A" or "3A", in -// order of increasing tightness. The default tolerance is "2A", which -// is the general standard for manufactured bolts. For ISO the tolerance -// has the form of a number and letter. The letter specifies the "fundamental deviation", also called the "tolerance position", the gap -// from the nominal size, and must be "e", "f", "g", or "h", where "e" is -// the loosest and "h" means no gap. The number specifies the allowed -// range (variability) of the thread heights. It must be a value from -// 3-9 for crest diameter and one of 4, 6, or 8 for pitch diameter. A -// tolerance "6g" specifies both pitch and crest diameter to be the same, -// but they can be different, with a tolerance like "5g6g" specifies a pitch diameter tolerance of "5g" and a crest diameter tolerance of "6g". -// Smaller numbers give a tighter tolerance. The default ISO tolerance is "6g". -// . -// The $slop argument gives an extra gap to account for printing overextrusion. It defaults to 0.2. -// Arguments: -// name = screw specification, e.g. "M5x1" or "#8-32" -// head = head type (see list above). Default: none -// thread = thread type or specification. Default: "coarse" -// drive = drive type. Default: none -// drive_size = size of drive recess to override computed value -// oversize = amount to increase screw diameter for clearance holes. Default: 0 -// spec = screw specification from `screw_info()`. If you specify this you can omit all the preceeding parameters. -// length = length of screw (in mm) -// shank = length of unthreaded portion of screw (in mm). Default: 0 -// details = toggle some details in rendering. Default: false -// tolerance = screw tolerance. Determines actual screw thread geometry based on nominal sizing. Default is "2A" for UTS and "6g" for ISO. -// $slop = add extra gap to account for printer overextrusion. Default: 0.2 -// anchor = anchor relative to the shaft of the screw -// anchor_head = anchor relative to the screw head -// Example(Med): Selected UTS (English) screws -// $fn=32; -// xdistribute(spacing=8){ -// screw("#6", length=12); -// screw("#6-32", head="button", drive="torx",length=12); -// screw("#6-32,3/4", head="hex"); -// screw("#6", thread="fine", head="fillister",length=12, drive="phillips"); -// screw("#6", head="flat small",length=12,drive="slot"); -// screw("#6-32", head="flat large", length=12, drive="torx"); -// screw("#6-32", head="flat undercut",length=12); -// screw("#6-24", head="socket",length=12); // Non-standard threading -// screw("#6-32", drive="hex", drive_size=1.5, length=12); -// } -// Example(Med): A few examples of ISO (metric) screws -// $fn=32; -// xdistribute(spacing=8){ -// screw("M3", head="flat small",length=12); -// screw("M3", head="button",drive="torx",length=12); -// screw("M3", head="pan", drive="phillips",length=12); -// screw("M3x1", head="pan", drive="slot",length=12); // Non-standard threading! -// screw("M3", head="flat large",length=12); -// screw("M3", thread="none", head="flat", drive="hex",length=12); // No threads -// screw("M3", head="socket",length=12); -// screw("M5", head="hex", length=12); -// } -// Example(Med): Demonstration of all head types for UTS screws (using pitch zero for fast preview) -// xdistribute(spacing=15){ -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="none", drive="hex"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="none", drive="torx"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="none"); -// } -// screw("1/4", thread=0, length=8, anchor=TOP, head="hex"); -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="socket", drive="hex"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="socket", drive="torx"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="socket"); -// } -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="button", drive="hex"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="button", drive="torx"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="button"); -// } -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="round", drive="slot"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="round", drive="phillips"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="round"); -// } -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="fillister", drive="slot"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="fillister", drive="phillips"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="fillister"); -// } -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="slot"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="phillips"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="hex"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat", drive="torx"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat large"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat small"); -// } -// ydistribute(spacing=15){ -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat undercut", drive="slot"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat undercut", drive="phillips"); -// screw("1/4", thread=0,length=8, anchor=TOP, head="flat undercut"); -// } -// } -// Example(Med): Demonstration of all head types for metric screws without threading. -// xdistribute(spacing=15){ -// ydistribute(spacing=15){ -// screw("M6x0", length=8, anchor=TOP, head="none", drive="hex"); -// screw("M6x0", length=8, anchor=TOP, head="none", drive="torx"); -// screw("M6x0", length=8, anchor=TOP); -// } -// screw("M6x0", length=8, anchor=TOP, head="hex"); -// ydistribute(spacing=15){ -// screw("M6x0", length=8, anchor=TOP, head="socket", drive="hex"); -// screw("M6x0", length=8, anchor=TOP, head="socket", drive="torx"); -// screw("M6x0", length=8, anchor=TOP, head="socket"); -// } -// ydistribute(spacing=15){ -// screw("M6x0", length=8, anchor=TOP, head="pan", drive="slot"); -// screw("M6x0", length=8, anchor=TOP, head="pan", drive="phillips"); -// screw("M6x0", length=8, anchor=TOP, head="pan"); -// screw("M6x0", length=8, anchor=TOP, head="pan flat"); -// } -// ydistribute(spacing=15){ -// screw("M6x0", length=8, anchor=TOP, head="button", drive="hex"); -// screw("M6x0", length=8, anchor=TOP, head="button", drive="torx"); -// screw("M6x0", length=8, anchor=TOP, head="button"); -// } -// ydistribute(spacing=15){ -// screw("M6x0", length=8, anchor=TOP, head="cheese", drive="slot"); -// screw("M6x0", length=8, anchor=TOP, head="cheese", drive="phillips"); -// screw("M6x0", length=8, anchor=TOP, head="cheese"); -// } -// ydistribute(spacing=15){ -// screw("M6x0", length=8, anchor=TOP, head="flat", drive="phillips"); -// screw("M6x0", length=8, anchor=TOP, head="flat", drive="slot"); -// screw("M6x0", length=8, anchor=TOP, head="flat", drive="hex"); -// screw("M6x0", length=8, anchor=TOP, head="flat", drive="torx"); -// screw("M6x0", length=8, anchor=TOP, head="flat small"); -// screw("M6x0", length=8, anchor=TOP, head="flat large"); -// } -// } -// Example: The three different English (UTS) screw tolerances -// module label(val) -// { -// difference(){ -// children(); -// yflip()linear_extrude(height=.35) text(val,valign="center",halign="center",size=8); -// } -// } -// $fn=64; -// xdistribute(spacing=15){ -// label("1") screw("1/4-20,5/8", head="hex",orient=DOWN,anchor_head=TOP,tolerance="1A"); // Loose -// label("2") screw("1/4-20,5/8", head="hex",orient=DOWN,anchor_head=TOP,tolerance="2A"); // Standard -// label("3") screw("1/4-20,5/8", head="hex",orient=DOWN,anchor_head=TOP,tolerance="3A"); // Tight -// } -// Example(2D): This example shows the gap between nut and bolt at the loosest tolerance for UTS. This gap is what enables the parts to mesh without binding and is part of the definition for standard metal hardware. -// $slop=0; -// $fn=32; -// projection(cut=true)xrot(-90){ -// screw("1/4-20,1/4", head="hex",orient=UP,anchor=BOTTOM,tolerance="1A"); -// down(INCH*1/20*2.58) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="1B"); -// } - -module screw(name, head, thread="coarse", drive, drive_size, oversize=0, spec, length, shank=0, tolerance=undef, details=true, anchor=undef,anchor_head=undef,spin=0, orient=UP) -{ - spec = _validate_screw_spec( - is_def(spec) ? spec : screw_info(name, head, thread, drive, drive_size, oversize) ); - echo_struct(spec,"spec"); - head = struct_val(spec,"head"); - pitch = struct_val(spec, "pitch"); - diameter = struct_val(spec, "diameter"); - headless = head=="none" || head==undef; - eps = headless || starts_with(head,"flat") ? 0 : 0.01; - length = first_defined([length,struct_val(spec,"length")]) + eps; - assert(length>0, "Must specify positive length"); - sides = max(12, segs(diameter/2)); - unthreaded = is_undef(pitch) || pitch==0 ? length : shank; - threaded = length - unthreaded; - echo(t=threaded,length,unthreaded); - head_height = headless || starts_with(head, "flat") ? 0 : struct_val(spec, "head_height"); - head_diam = struct_val(spec, "head_size"); - head_size = headless ? [diameter, diameter, head_height] : - head == "hex" ? [head_diam, head_diam*2/sqrt(3), head_height] : - [head_diam, head_diam, head_height]; - assert(num_defined([anchor,anchor_head])<=1, "Cannot define both `anchor` and `anchor_head`"); - head_anchor = is_def(anchor_head); - attachable( - d = head_anchor ? head_size[0] : diameter, // This code should be tweaked to pass diameter and length more cleanly - l = head_anchor ? head_size[2] : length, - orient = orient, - anchor = first_defined([anchor, anchor_head, BOTTOM]), - //offset = head_anchor ? [0,0,head_height/2] : [0,0,-length/2], - spin = spin - ) - { - up(head_anchor ? -head_height/2 : length/2) - difference(){ - union(){ - screw_head(spec,details); - up(eps){ - if (unthreaded>0){ - cyl(d=diameter, h=unthreaded+eps+(threaded>0?0.01:0), anchor=TOP, $fn=sides); - } - if (threaded>0) - intersection(){ - down(unthreaded) - _rod(spec, length=threaded+eps, tolerance=tolerance, $fn=sides, anchor=TOP ); - if (details) - up(.01)cyl(d=diameter, l=length+.02+eps, chamfer1 = pitch/2, chamfer2 = headless ? pitch/2 : -pitch/2, anchor=TOP, $fn=sides); - } - } - } - _driver(spec); - } - children(); - } -} - - -module _driver(spec) -{ - drive = struct_val(spec,"drive"); - echo(drive=drive); - if (is_def(drive) && drive!="none") { - echo(inside_drive=drive); - head = struct_val(spec,"head"); - diameter = struct_val(spec,"diameter"); - drive_size = struct_val(spec,"drive_size"); - drive_width = struct_val(spec,"drive_width"); - drive_diameter = struct_val(spec, "drive_diameter"); - drive_depth = first_defined([struct_val(spec, "drive_depth"), .7*diameter]); // Note hack for unspecified depth - head_top = starts_with(head,"flat") || head=="none" ? 0 : - struct_val(spec,"head_height"); - echo(drive_size=drive_size); - up(head_top-drive_depth){ - // recess should be positioned with its bottom center at (0,0) and the correct recess depth given above - if (drive=="phillips") phillips_mask(drive_size,anchor=BOTTOM); - if (drive=="torx") torx_mask(size=drive_size, l=drive_depth+1, center=false); - if (drive=="hex") linear_extrude(height=drive_depth+1) hexagon(id=drive_size); - if (drive=="slot") cuboid([2*struct_val(spec,"head_size"), drive_width, drive_depth+1],anchor=BOTTOM); - } - } -} - - -function _ISO_thread_tolerance(diameter, pitch, internal=false, tolerance=undef) = - let( - P = pitch, - H = P*sqrt(3)/2, - tolerance = first_defined([tolerance, internal?"6H":"6g"]), - - pdiam = diameter - 2*3/8*H, // nominal pitch diameter - mindiam = diameter - 2*5/8*H, // nominal minimum diameter - - EI = [ // Fundamental deviations for nut thread - ["G", 15+11*P], - ["H", 0], // Standard practice - ], - - es = [ // Fundamental deviations for bolt thread - ["e", -(50+11*P)], // Exceptions if P<=0.45mm - ["f", -(30+11*P)], - ["g", -(15+11*P)], // Standard practice - ["h", 0] // Standard practice for tight fit - ], - - T_d6 = 180*pow(P,2/3)-3.15/sqrt(P), - T_d = [ // Crest diameter tolerance for major diameter of bolt thread - [4, 0.63*T_d6], - [6, T_d6], - [8, 1.6*T_d6] - ], - - T_D1_6 = 0.2 <= P && P <= 0.8 ? 433*P - 190*pow(P,1.22) : - P > .8 ? 230 * pow(P,0.7) : undef, - T_D1 = [ // Crest diameter tolerance for minor diameter of nut thread - [4, 0.63*T_D1_6], - [5, 0.8*T_D1_6], - [6, T_D1_6], - [7, 1.25*T_D1_6], - [8, 1.6*T_D1_6] - ], - - rangepts = [0.99, 1.4, 2.8, 5.6, 11.2, 22.4, 45, 90, 180, 300], - d_ind = floor(lookup(diameter,hstack(rangepts,count(len(rangepts))))), - avgd = sqrt(rangepts[d_ind]* rangepts[d_ind+1]), - - T_d2_6 = 90*pow(P, 0.4)*pow(avgd,0.1), - T_d2 = [ // Pitch diameter tolerance for bolt thread - [3, 0.5*T_d2_6], - [4, 0.63*T_d2_6], - [5, 0.8*T_d2_6], - [6, T_d2_6], - [7, 1.25*T_d2_6], - [8, 1.6*T_d2_6], - [9, 2*T_d2_6], - ], - - T_D2 = [ // Tolerance for pitch diameter of nut thread - [4, 0.85*T_d2_6], - [5, 1.06*T_d2_6], - [6, 1.32*T_d2_6], - [7, 1.7*T_d2_6], - [8, 2.12*T_d2_6] - ], - - internal = is_def(internal) ? internal : tolerance[1] != downcase(tolerance[1]), - internalok = !internal || ( - len(tolerance)==2 && str_find("GH",tolerance[1])!=undef && str_find("45678",tolerance[0])!=undef), - tol_str = str(tolerance,tolerance), - externalok = internal || ( - (len(tolerance)==2 || len(tolerance)==4) - && str_find("efgh", tol_str[1])!=undef - && str_find("efgh", tol_str[3])!=undef - && str_find("3456789", tol_str[0]) != undef - && str_find("468", tol_str[2]) !=undef) - ) - assert(internalok,str("Invalid internal thread tolerance, ",tolerance,". Must have form ")) - assert(externalok,str("invalid external thread tolerance, ",tolerance,". Must have form or ")) - let( - tol_num_pitch = parse_num(tol_str[0]), - tol_num_crest = parse_num(tol_str[2]), - tol_letter = tol_str[1] - ) - assert(tol_letter==tol_str[3],str("Invalid tolerance, ",tolerance,". Cannot mix different letters")) - internal ? - let( // Nut case - //a=echo("nut", tol_letter, tol_num_pitch, tol_num_crest), - fdev = struct_val(EI,tol_letter)/1000, - Tdval = struct_val(T_D1, tol_num_crest)/1000, - df= echo(T_D1=T_D1), - Td2val = struct_val(T_D2, tol_num_pitch)/1000, - //fe= echo("nut",P,fdev=fdev, Tdval=Tdval, Td2val=Td2val), - bot=[diameter+fdev, diameter+fdev+Td2val+H/6], - xdiam = [mindiam+fdev,mindiam+fdev+Tdval], - pitchdiam = [pdiam + fdev, pdiam+fdev+Td2val] - ) - [["pitch",P],["d_minor",xdiam], ["d_pitch",pitchdiam], ["d_major",bot],["basic",[mindiam,pdiam,diameter]]] - : - let( // Bolt case - //a=echo("bolt"), - fdev = struct_val(es,tol_letter)/1000, - Tdval = struct_val(T_d, tol_num_crest)/1000, - Td2val = struct_val(T_d2, tol_num_pitch)/1000, - mintrunc = P/8, - d1 = diameter-5*H/4, - maxtrunc = H/4 - mintrunc * (1-cos(60-acos(1-Td2val/4/mintrunc)))+Td2val/2, - //cc=echo("bolt",P,fdev=fdev, Tdval=Tdval, Td2val=Td2val), - bot = [diameter-2*H+2*mintrunc+fdev, diameter-2*H+2*maxtrunc+fdev], - xdiam = [diameter+fdev,diameter+fdev-Tdval], - pitchdiam = [pdiam + fdev, pdiam+fdev-Td2val] - ) - [["pitch",P],["d_major",xdiam], ["d_pitch",pitchdiam], ["d_minor",bot],["basic",[mindiam,pdiam,diameter]]]; - -function _UTS_thread_tolerance(diam, pitch, internal=false, tolerance=undef) = - let( - d = diam/INCH, // diameter in inches - P = pitch/INCH, // pitch in inches - H = P*sqrt(3)/2, - tolerance = first_defined([tolerance, internal?"2B":"2A"]), - tolOK = in_list(tolerance, ["1A","1B","2A","2B","3A","3B"]), - internal = tolerance[1]=="B" - ) - assert(tolOK,str("Tolerance was ",tolerance,". Must be one of 1A, 2A, 3A, 1B, 2B, 3B")) - let( - LE = 9*P, // length of engagement. Is this right? - pitchtol_2A = 0.0015*pow(d,1/3) + 0.0015*sqrt(LE) + 0.015*pow(P,2/3), - pitchtol_table = [ - ["1A", 1.500*pitchtol_2A], - ["2A", pitchtol_2A], - ["3A", 0.750*pitchtol_2A], - ["1B", 1.950*pitchtol_2A], - ["2B", 1.300*pitchtol_2A], - ["3B", 0.975*pitchtol_2A] - ], - pitchtol = struct_val(pitchtol_table, tolerance), - allowance = tolerance=="1A" || tolerance=="2A" ? 0.3 * pitchtol_2A : 0, - majortol = tolerance == "1A" ? 0.090*pow(P,2/3) : - tolerance == "2A" || tolerance == "3A" ? 0.060*pow(P,2/3) : - pitchtol+pitch/4/sqrt(3), // Internal case - minortol = tolerance=="1B" || tolerance=="2B" ? - ( - d < 0.25 ? constrain(0.05*pow(P,2/3)+0.03*P/d - 0.002, 0.25*P-0.4*P*P, 0.394*P) - : (P > 0.25 ? 0.15*P : 0.25*P-0.4*P*P) - ) : - tolerance=="3B" ? constrain(0.05*pow(P,2/3)+0.03*P/d - 0.002, P<1/13 ? 0.12*P : 0.23*P-1.5*P*P, 0.394*P) - :0, // not used for external threads - //f=echo(allowance=allowance), - //g=echo(pta2 = pitchtol_2A), - // ff=echo(minortol=minortol, pitchtol=pitchtol, majortol=majortol), - basic_minordiam = d - 5/4*H, - basic_pitchdiam = d - 3/4*H, - majordiam = internal ? [d,d] : // A little confused here, paragraph 8.3.2 - [d-allowance-majortol, d-allowance], - //ffda=echo(allowance=allowance, majortol=majortol, "*****************************"), - pitchdiam = internal ? [basic_pitchdiam, basic_pitchdiam + pitchtol] - : [majordiam[1] - 3/4*H-pitchtol, majordiam[1]-3/4*H], - minordiam = internal ? [basic_minordiam, basic_minordiam + minortol] - : [pitchdiam[0] - 3/4*H, basic_minordiam - allowance - H/8] // the -H/8 is for the UNR case, 0 for UN case - ) - [["pitch",P*INCH],["d_major",majordiam*INCH], ["d_pitch", pitchdiam*INCH], ["d_minor",minordiam*INCH], - ["basic", INCH*[basic_minordiam, basic_pitchdiam, d]]]; - -function _exact_thread_tolerance(d,P) = - let( - H = P*sqrt(3)/2, - basic_minordiam = d - 5/4*H, - basic_pitchdiam = d - 3/4*H - ) - [["pitch", P], ["d_major", d], ["d_pitch", basic_pitchdiam], ["d_minor", basic_minordiam], - ["basic", [basic_minordiam, basic_pitchdiam, d]]]; - - -// Function: thread_specification() -// Usage: -// thread_specification(screw_spec, [tolerance], [internal]) -// Description: -// Determines actual thread geometry for a given screw with specified tolerance. If tolerance is omitted the default is used. If tolerance -// is "none" or 0 then return the nominal thread geometry. -// . -// The return value is a structure with the following fields: -// - pitch: the thread pitch -// - d_major: major diameter range -// - d_pitch: pitch diameter range -// - d_minor: minor diameter range -// - basic: vector `[minor, pitch, major]` of the nominal or "basic" diameters for the threads -function thread_specification(screw_spec, internal=false, tolerance=undef) = - let( diam = struct_val(screw_spec, "diameter"), - pitch = struct_val(screw_spec, "pitch")) - tolerance == 0 || tolerance=="none" ? _exact_thread_tolerance(diam, pitch) - : struct_val(screw_spec,"system") == "ISO" ? _ISO_thread_tolerance(diam, pitch, internal, tolerance) - : struct_val(screw_spec,"system") == "UTS" ? _UTS_thread_tolerance(diam, pitch, internal, tolerance) - : assert(false,"Unknown screw system ",struct_val(screw_spec,"system")); - - -module _rod(spec, length, tolerance, orient=UP, spin=0, anchor=CENTER) -{ - threadspec = thread_specification(spec, internal=false, tolerance=tolerance); - echo(d_major_mean = mean(struct_val(threadspec, "d_major"))); - - threaded_rod([mean(struct_val(threadspec, "d_minor")), - mean(struct_val(threadspec, "d_pitch")), - mean(struct_val(threadspec, "d_major"))], - pitch = struct_val(threadspec, "pitch"), - l=length, left_handed=false, - bevel=false, orient=orient, anchor=anchor, spin=spin); -} - - -// Module: nut() -// Usage: -// nut([name],diameter, thickness,[thread],[oversize],[spec],[tolerance],[details],[$slop]) -// Description: -// Generates a hexagonal nut. -// The name, thread and oversize parameters are described under `screw_info()`. As for screws, -// you can give the specification in `spec` and then omit the name. The diameter is the flat-to-flat -// size of the nut produced. -// . -// The tolerance determines the actual thread sizing based on the -// nominal size. -// For UTS threads the tolerance is either "1B", "2B" or "3B", in -// order of increasing tightness. The default tolerance is "2B", which -// is the general standard for manufactured nuts. For ISO the tolerance -// has the form of a number and letter. The letter specifies the "fundamental deviation", also called the "tolerance position", the gap -// from the nominal size, and must be "G", or "H", where "G" is looser -// he loosest and "H" means no gap. The number specifies the allowed -// range (variability) of the thread heights. Smaller numbers give tigher tolerances. It must be a value from -// 4-8, so an allowed (loose) tolerance is "7G". The default ISO tolerance is "6H". -// . -// The $slop parameter determines extra gaps left to account for printing overextrusion. It defaults to 0. -// Arguments: -// name = screw specification, e.g. "M5x1" or "#8-32" -// diameter = outside diameter of nut (flat to flat dimension) -// thickness = thickness of nut (in mm) -// --- -// thread = thread type or specification. Default: "coarse" -// oversize = amount to increase screw diameter for clearance holes. Default: 0 -// spec = screw specification from `screw_info()`. If you specify this you can omit all the preceeding parameters. -// bevel = bevel the nut. Default: false -// tolerance = nut tolerance. Determines actual nut thread geometry based on nominal sizing. Default is "2B" for UTS and "6H" for ISO. -// $slop = extra space left to account for printing over-extrusion. Default: 0 -// Example: A metric and UTS nut -// nut("3/8", 5/8*INCH, 1/4*INCH); -// right(25) -// nut("M8", 16, 6); -// Example: The three different UTS nut tolerances -// module mark(number) -// { -// difference(){ -// children(); -// ycopies(n=number, spacing=1.5)right(.25*INCH-2)up(8-.35)cyl(d=1, h=1); -// } -// } -// $fn=64; -// xdistribute(spacing=17){ -// mark(1) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="1B"); -// mark(2) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="2B"); -// mark(3) nut("1/4-20", thickness=8, diameter=0.5*INCH,tolerance="3B"); -// } -module nut(name, diameter, thickness, thread="coarse", oversize=0, spec, tolerance=undef, - bevel=false, anchor=BOTTOM,spin=0, orient=UP) -{ - assert(is_num(diameter) && diameter>0); - assert(is_num(thickness) && thickness>0); - spec = is_def(spec) ? spec : screw_info(name, thread=thread, oversize=oversize); - threadspec = thread_specification(spec, internal=true, tolerance=tolerance); - echo(threadspec=threadspec,"for nut threads"); - echo(nut_minor_diam = mean(struct_val(threadspec,"d_minor"))); - threaded_nut( - od=diameter, - id=[mean(struct_val(threadspec, "d_minor")), - mean(struct_val(threadspec, "d_pitch")), - mean(struct_val(threadspec, "d_major"))], - pitch = struct_val(threadspec, "pitch"), - h=thickness, - bevel=bevel, - anchor=anchor,spin=spin,orient=orient); -} function _is_positive(x) = is_num(x) && x>0; @@ -1355,6 +1359,37 @@ function _validate_screw_spec(spec) = let( + +// Function: thread_specification() +// Usage: +// thread_specification(screw_spec, [tolerance], [internal]) +// Description: +// Determines actual thread geometry for a given screw with specified tolerance. If tolerance is omitted the default is used. If tolerance +// is "none" or 0 then return the nominal thread geometry. See {{screw()}} or {{nut()}} for details on tolerance values for screws (internal=false) and +// nuts (internal=true). +// . +// The return value is a structure with the following fields: +// - pitch: the thread pitch +// - d_major: major diameter range +// - d_pitch: pitch diameter range +// - d_minor: minor diameter range +// - basic: vector `[minor, pitch, major]` of the nominal or "basic" diameters for the threads +// Arguments: +// screw_spec = screw specification structure +// --- +// tolerance = thread geometry tolerance +// internal = true for internal threads. Default: false +function thread_specification(screw_spec, tolerance=undef, internal=false) = + let( diam = struct_val(screw_spec, "diameter"), + pitch = struct_val(screw_spec, "pitch")) + tolerance == 0 || tolerance=="none" ? _exact_thread_tolerance(diam, pitch) + : struct_val(screw_spec,"system") == "ISO" ? _ISO_thread_tolerance(diam, pitch, internal, tolerance) + : struct_val(screw_spec,"system") == "UTS" ? _UTS_thread_tolerance(diam, pitch, internal, tolerance) + : assert(false,"Unknown screw system ",struct_val(screw_spec,"system")); + + + + // recess sizing: // http://www.fasnetdirect.com/refguide/Machinepancombo.pdf // diff --git a/tests/test_comparisons.scad b/tests/test_comparisons.scad index f96bb93..56b2f96 100644 --- a/tests/test_comparisons.scad +++ b/tests/test_comparisons.scad @@ -120,6 +120,10 @@ test_is_decreasing(); module test_find_approx() { assert(find_approx(1, [2,3,1.05,4,1,2,.99], eps=.1)==2); assert(find_approx(1, [2,3,1.05,4,1,2,.99], all=true, eps=.1)==[2,4,6]); + assert(find_approx(1, [2,3,4])==undef); + assert(find_approx(1, [2,3,4],all=true)==[]); + assert(find_approx(1, [])==undef); + assert(find_approx(1, [], all=true)==[]); } test_find_approx(); diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index d68dee6..d4358e7 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -768,9 +768,9 @@ module test_polygon_area() { assert(approx(polygon_area(rot([13,27,75], p=path3d(circle(r=50,$fn=1000),fill=23)), signed=true), PI*50*50, eps=0.1)); - 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(polygon_area([[0,0], [0,10], [10,0]],signed=true) + 50) < EPSILON); + assert(abs(polygon_area([[0,0], [0,10], [0,15]],signed=true)) < EPSILON); + assert(abs(polygon_area([[0,0], [10,0], [0,10]],signed=true) - 50) < EPSILON); } *test_polygon_area(); diff --git a/tests/test_vectors.scad b/tests/test_vectors.scad index e9dea30..47e49da 100644 --- a/tests/test_vectors.scad +++ b/tests/test_vectors.scad @@ -29,6 +29,8 @@ module test_is_vector() { assert(is_vector([1,1,1],all_nonzero=true) == true); assert(is_vector([-1,1,1],all_nonzero=true) == true); assert(is_vector([-1,-1,-1],all_nonzero=true) == true); + assert(!is_vector([3,INF,4])); + assert(!is_vector([3,NAN,4])); } test_is_vector(); diff --git a/trigonometry.scad b/trigonometry.scad index a48e432..2c10a40 100644 --- a/trigonometry.scad +++ b/trigonometry.scad @@ -88,27 +88,6 @@ function law_of_sines(a, A, b, B) = asin(constrain(b/r, -1, 1)); -// Function: triangle_area() -// Usage: -// area = triangle_area(p1,p2,p3); -// Topics: Geometry, Trigonometry, Triangles, Area -// Description: -// Returns the area of a triangle formed between three 2D or 3D vertices. -// Result will be negative if the points are 2D and in clockwise order. -// Arguments: -// p1 = The first vertex of the triangle. -// p2 = The second vertex of the triangle. -// p3 = The third vertex of the triangle. -// Example: -// triangle_area([0,0], [5,10], [10,0]); // Returns -50 -// triangle_area([10,0], [5,10], [0,0]); // Returns 50 -function triangle_area(p1,p2,p3) = - assert( is_path([p1,p2,p3]), "Invalid points or incompatible dimensions." ) - len(p1)==3 - ? 0.5*norm(cross(p3-p1,p3-p2)) - : 0.5*cross(p3-p1,p3-p2); - - // Section: 2D Right Triangle Functions // This is a set of functions to make it easier to perform trig calculations on right triangles. diff --git a/vectors.scad b/vectors.scad index 20f1f3d..ad05740 100644 --- a/vectors.scad +++ b/vectors.scad @@ -43,7 +43,7 @@ // is_vector([1,1,1],all_nonzero=false); // Returns true // is_vector([],zero=false); // Returns false function is_vector(v, length, zero, all_nonzero=false, eps=EPSILON) = - is_list(v) && len(v)>0 && []==[for(vi=v) if(!is_num(vi)) 0] + is_list(v) && len(v)>0 && []==[for(vi=v) if(!is_finite(vi)) 0] && (is_undef(length) || len(v)==length) && (is_undef(zero) || ((norm(v) >= eps) == !zero)) && (!all_nonzero || all_nonzero(v)) ; From 2cc5c39d641c9bcd29465f978c8852ccd431a135 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 8 Apr 2022 20:17:41 -0400 Subject: [PATCH 2/5] fix doc bug in bottlecaps. knurling mask didn't pass parameters (so they were ignored) --- bottlecaps.scad | 1 - knurling.scad | 52 +++++++++++++++++++++++----------------- tests/test_geometry.scad | 1 - 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/bottlecaps.scad b/bottlecaps.scad index 9076dc0..57bf810 100644 --- a/bottlecaps.scad +++ b/bottlecaps.scad @@ -408,7 +408,6 @@ function pco1881_cap(wall=2, texture="none", anchor=BOTTOM, spin=0, orient=UP) = // support_d = Outer diameter of support ring. Set to 0 for no support. // pitch = Thread pitch // round_supp = True to round the lower edge of the support ring -// --- // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#subsection-orient). Default: `UP` diff --git a/knurling.scad b/knurling.scad index 03f1191..3b67394 100644 --- a/knurling.scad +++ b/knurling.scad @@ -14,22 +14,22 @@ // Module: knurled_cylinder() // Usage: -// knurled_cylinder(l, r|d, [overage], [count], [profile], [helix]); -// knurled_cylinder(l, r1|d1, r2|d2, [overage], [count], [profile], [helix]); +// knurled_cylinder(l|h|height, r|d=, [count=], [profile=], [helix=]); +// knurled_cylinder(l|h|height, r1=|d1=, r2=|d2=, [count=], [profile=], [helix=]); // Description: -// Creates a mask to difference from a cylinder to give it a knurled surface. +// Creates a knurled cylinder. The knurling is made from small bumps (pyramids) arranged on the surface. +// The // Arguments: -// l = The length of the axis of the mask. Default: 10 -// overage = Extra backing to the mask. Default: 5 -// r = The radius of the cylinder to knurl. Default: 10 +// l / h / height = The length/height of the cylinder +// r = The radius of the cylinder to knurl. // r1 = The radius of the bottom of the conical cylinder to knurl. // r2 = The radius of the top of the conical cylinder to knurl. // d = The diameter of the cylinder to knurl. // d1 = The diameter of the bottom of the conical cylinder to knurl. // d2 = The diameter of the top of the conical cylinder to knurl. -// count = The number of grooves to have around the surface of the cylinder. Default: 30 -// profile = The angle of the bottom of the groove, in degrees. Default 120 -// helix = The helical angle of the grooves, in degrees. Default: 30 +// count = The number of bumps filling one revolution of the cylinder. Default: 30 +// profile = The lower angle between the pyramid-shaped bumps. Smaller angles make the bumps sharper and can lead to bad models if count is small. Default 120 +// helix = The helical angle of the bumps, in degrees. Close to zero produces vertical ribbing. Close to 90 degrees produces very thin bumps and is not recommended. Default: 30 // chamfer = The size of the chamfers on the ends of the cylinder. Default: none. // chamfer1 = The size of the chamfer on the bottom end of the cylinder. Default: none. // chamfer2 = The size of the chamfer on the top end of the cylinder. Default: none. @@ -48,8 +48,10 @@ // knurled_cylinder(l=30, r=20, count=30, profile=120, helix=30); // knurled_cylinder(l=30, r=20, count=30, profile=90, helix=30); // knurled_cylinder(l=30, r=20, count=20, profile=120, helix=30); +// knurled_cylinder(l=30, r=20, count=20, profile=120, helix=0.01); +// knurled_cylinder(l=30, r=20, count=20, profile=140, helix=60); module knurled_cylinder( - l=20, + l, r=undef, r1=undef, r2=undef, d=undef, d1=undef, d2=undef, count=30, profile=120, helix=30, @@ -57,8 +59,12 @@ module knurled_cylinder( chamfang=undef, chamfang1=undef, chamfang2=undef, from_end=false, rounding=undef, rounding1=undef, rounding2=undef, - anchor=CENTER, spin=0, orient=UP + anchor=CENTER, spin=0, orient=UP, + height, h ) { + assert(is_finite(helix) && helix>0 && helix<90, "Must give helix angle between 0 and 90"); + assert(is_finite(profile) && profile>0 && profile<180, "Must give profile between 0 and 180"); + l = one_defined([l,h,height],"l,h,height"); 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); @@ -127,22 +133,23 @@ module knurled_cylinder( // Module: knurled_cylinder_mask() // Usage: -// knurled_cylinder_mask(l, r|d, [overage], [count], [profile], [helix]); -// knurled_cylinder_mask(l, r1|d1, r2|d2, [overage], [count], [profile], [helix]); +// knurled_cylinder_mask(l|h|height, r|d=, [overage], [count], [profile], [helix]) [ATTACHMENTS]; +// knurled_cylinder_mask(l|h|height, r=1|d1=, r2=|d2=, [overage=], [count=], [profile=], [helix=],...) [ATTACHMENTS]; // Description: // Creates a mask to difference from a cylinder to give it a knurled surface. // Arguments: -// l = The length of the axis of the mask. Default: 10 +// l = The length of the axis of the mask. +// r = The radius of the cylinder to knurl. // overage = Extra backing to the mask. Default: 5 -// r = The radius of the cylinder to knurl. Default: 10 +// --- // r1 = The radius of the bottom of the conical cylinder to knurl. // r2 = The radius of the top of the conical cylinder to knurl. // d = The diameter of the cylinder to knurl. // d1 = The diameter of the bottom of the conical cylinder to knurl. // d2 = The diameter of the top of the conical cylinder to knurl. -// count = The number of grooves to have around the surface of the cylinder. Default: 30 -// profile = The angle of the bottom of the groove, in degrees. Default 120 -// helix = The helical angle of the grooves, in degrees. Default: 30 +// count = The number of bumps filling one revolution of the cylinder. Default: 30 +// profile = The lower angle between the pyramid-shaped bumps. Smaller angles make the bumps sharper and can lead to bad models if count is small. Default 120 +// helix = The helical angle of the bumps, in degrees. Close to zero produces vertical ribbing. Close to 90 degrees produces very thin bumps and is not recommended. Default: 30 // anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#subsection-anchor). Default: `CENTER` // spin = Rotate this many degrees around the Z axis. See [spin](attachments.scad#subsection-spin). Default: `0` // orient = Vector to rotate top towards. See [orient](attachments.scad#subsection-orient). Default: `UP` @@ -150,18 +157,19 @@ 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, + l, r, overage=5, + r1=undef, r2=undef, d=undef, d1=undef, d2=undef, count=30, profile=120, helix=30, - anchor=CENTER, spin=0, orient=UP + anchor=CENTER, spin=0, orient=UP, height,h ) { + l = one_defined([l,h,height],"l,h,height"); 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); + knurled_cylinder(r1=r1, r2=r2, l=l+0.01, profile=profile, helix=helix,count=count); } children(); } diff --git a/tests/test_geometry.scad b/tests/test_geometry.scad index d4358e7..9b15c4d 100644 --- a/tests/test_geometry.scad +++ b/tests/test_geometry.scad @@ -771,7 +771,6 @@ module test_polygon_area() { assert(abs(polygon_area([[0,0], [0,10], [10,0]],signed=true) + 50) < EPSILON); assert(abs(polygon_area([[0,0], [0,10], [0,15]],signed=true)) < EPSILON); assert(abs(polygon_area([[0,0], [10,0], [0,10]],signed=true) - 50) < EPSILON); - } *test_polygon_area(); From 6b196445602d342d1cacd7728d36e21f4a7e4268 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 8 Apr 2022 21:06:14 -0400 Subject: [PATCH 3/5] screws typo fix --- gears.scad | 23 ++++++++++++++--------- screws.scad | 2 +- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/gears.scad b/gears.scad index 8246560..a24b8c6 100644 --- a/gears.scad +++ b/gears.scad @@ -28,8 +28,8 @@ // Function&Module: spur_gear() // Usage: As a Module -// spur_gear(pitch, teeth, thickness, [shaft_diam=], [hide], [pressure_angle], [clearance], [backlash], [helical], [slices], [interior]); -// spur_gear(mod=, teeth=, thickness=, [shaft_diam=], ...); +// spur_gear(pitch, teeth, thickness, [shaft_diam], [hide=], [pressure_angle=], [clearance=], [backlash=], [helical=], [slices=], [interior=]) [ATTACHMENTS]; +// spur_gear(mod=, teeth=, thickness=, [shaft_diam=], ...) [ATTACHMENTS]; // Usage: As a Function // vnf = spur_gear(pitch, teeth, thickness, [shaft_diam], ...); // vnf = spur_gear(mod=, teeth=, thickness=, [shaft_diam], ...); @@ -193,9 +193,11 @@ module spur_gear( // Function&Module: spur_gear2d() // Usage: As Module -// spur_gear2d(pitch|mod, teeth, [hide], [pressure_angle], [clearance], [backlash], [interior]); +// spur_gear2d(pitch, teeth, [hide=], [pressure_angle=], [clearance=], [backlash=], [interior=]) [ATTACHMENTS]; +// spur_gear2d(mod=, teeth=, [hide=], [pressure_angle=], [clearance=], [backlash=], [interior=]) [ATTACHMENTS]; // Usage: As Function -// poly = spur_gear2d(pitch|mod, teeth, [hide], [pressure_angle], [clearance], [backlash], [interior]); +// poly = spur_gear2d(pitch, teeth, [hide=], [pressure_angle=], [clearance=], [backlash=], [interior=]); +// poly = spur_gear2d(mod=, teeth=, [hide=], [pressure_angle=], [clearance=], [backlash=], [interior=]); // Topics: Gears // See Also: spur_gear() // Description: @@ -291,8 +293,8 @@ module spur_gear2d( // Function&Module: rack() // Usage: As a Module -// rack(pitch, teeth, thickness, height, [pressure_angle=], [backlash=]); -// rack(mod=, teeth=, thickness=, height=, [pressure_angle=], [backlash]=); +// rack(pitch, teeth, thickness, height, [pressure_angle=], [backlash=]) [ATTACHMENTS]; +// rack(mod=, teeth=, thickness=, height=, [pressure_angle=], [backlash]=) [ATTACHMENTS]; // Usage: As a Function // vnf = rack(pitch, teeth, thickness, height, [pressure_angle=], [backlash=]); // vnf = rack(mod=, teeth=, thickness=, height=, [pressure_angle=], [backlash=]); @@ -438,10 +440,12 @@ function rack( // Function&Module: rack2d() -// Usage: As a Function -// path = rack2d(pitch|mod, teeth, height, [pressure_angle], [backlash]); // Usage: As a Module -// rack2d(pitch|mod, teeth, height, [pressure_angle], [backlash]); +// path = rack2d(pitch, teeth, height, [pressure_angle=], [backlash=]) [ATTACHMENTS]; +// path = rack2d(mod=, teeth=, height=, [pressure_angle=], [backlash=]) [ATTACHMENTS]; +// Usage: As a Function +// path = rack2d(pitch, teeth, height, [pressure_angle=], [backlash=]); +// path = rack2d(mod=, teeth=, height=, [pressure_angle=], [backlash=]); // Topics: Gears // See Also: spur_gear2d() // Description: @@ -453,6 +457,7 @@ function rack( // pitch = The circular pitch, or distance between teeth around the pitch circle, in mm. // teeth = Total number of teeth along the rack // height = Height of rack in mm, from tooth top to back of rack. +// --- // pressure_angle = Controls how straight or bulged the tooth sides are. In degrees. // backlash = Gap between two meshing teeth, in the direction along the circumference of the pitch circle // mod = The metric module/modulus of the gear. diff --git a/screws.scad b/screws.scad index f3e7f03..be75c2b 100644 --- a/screws.scad +++ b/screws.scad @@ -584,7 +584,7 @@ function _parse_drive(drive=undef, drive_size=undef) = // Arguments: // screw_info = structure produced by {{screw_info()}} // details = true for more detailed model. Default: false -function screw_head(screw_info,details=false = no_function("screw_head"); +function screw_head(screw_info,details=false) = no_function("screw_head"); module screw_head(screw_info,details=false) { no_children($children); head = struct_val(screw_info, "head"); From a84dddb43eb85e44d8f27047478a730f4750e806 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 8 Apr 2022 23:03:00 -0400 Subject: [PATCH 4/5] big fix --- screws.scad | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/screws.scad b/screws.scad index be75c2b..cec2b38 100644 --- a/screws.scad +++ b/screws.scad @@ -201,8 +201,10 @@ function screw(name, head, drive, thread="coarse", drive_size, oversize=0, spec, module screw(name, head, drive, thread="coarse", drive_size, oversize=0, spec, length, l, shank=0, tolerance=undef, details=true, anchor=undef,anchor_head=undef,spin=0, orient=UP) { + a=echo(ssthread=thread); spec = _validate_screw_spec( - is_def(spec) ? spec : screw_info(name, head, thread, drive, drive_size, oversize) ); + is_def(spec) ? spec : screw_info(name, head, drive, thread=thread, drive_size=drive_size, oversize=oversize) ); + echo(sthread=thread); echo_struct(spec,"spec"); head = struct_val(spec,"head"); pitch = struct_val(spec, "pitch"); From 5c1c457e1db5601df5409fa237adf20961440e58 Mon Sep 17 00:00:00 2001 From: Adrian Mariano Date: Fri, 8 Apr 2022 23:04:10 -0400 Subject: [PATCH 5/5] fix knurling to work with cones (at least mostly) --- knurling.scad | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/knurling.scad b/knurling.scad index 3b67394..e2eb8d4 100644 --- a/knurling.scad +++ b/knurling.scad @@ -50,6 +50,7 @@ // knurled_cylinder(l=30, r=20, count=20, profile=120, helix=30); // knurled_cylinder(l=30, r=20, count=20, profile=120, helix=0.01); // knurled_cylinder(l=30, r=20, count=20, profile=140, helix=60); +// knurled_cylinder(l=30, r1=20, r2=12, count=40, profile=90, helix=55); module knurled_cylinder( l, r=undef, r1=undef, r2=undef, @@ -79,8 +80,11 @@ module knurled_cylinder( 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]) + let(scale_factor = lerp(1,r2/r1,layer/layers)) + scale([scale_factor,scale_factor,1], + (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]