add threads-scad library
This commit is contained in:
122
threads-scad/LICENSE.txt
Normal file
122
threads-scad/LICENSE.txt
Normal file
@@ -0,0 +1,122 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
||||
|
||||
127
threads-scad/README.md
Normal file
127
threads-scad/README.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# OpenSCAD threads.scad module
|
||||
|
||||
<p align="center"><img alt="Thread demo image" src="./images/threads_demo.png"></p>
|
||||
|
||||
This is an efficient OpenSCAD threading library intended primarily to be used
|
||||
as a component in other designs. It supports metric compatible internal and
|
||||
external threads, auger threads, thread tapering, clearance holes, countersunk
|
||||
holes, recessed holes, hex bolts, nuts, washers, Phillips tips, and long rods
|
||||
which are extended with threaded joints. Whenever possible this complies with
|
||||
metric standards for the defaults of parameters, while providing overrides for
|
||||
many values.
|
||||
|
||||
The core threading routine makes use of list comprehensions to generate the
|
||||
entire threaded screw as a single polyhedron. This substantially improves the
|
||||
rendering speed and robustness over other approaches, decreases the number of
|
||||
facets necessary, and increases the tactile smoothness of the resulting parts.
|
||||
This also allows partially tapered screw ends, plus auger and
|
||||
conventional threads which come to a point.
|
||||
|
||||
# threads.scad API
|
||||
|
||||
Where parameters are shown with a default of 0 or -1, in many cases this indicates a standard default value will be calculated internally if this is not overridden.
|
||||
|
||||
```
|
||||
// This creates a vertical rod at the origin with external threads. It uses
|
||||
// metric standards by default.
|
||||
module ScrewThread(outer_diam, height, pitch=0, tooth_angle=30, tolerance=0.4, tip_height=0, tooth_height=0, tip_min_fract=0)
|
||||
|
||||
// This creates a vertical rod at the origin with external auger-style
|
||||
// threads.
|
||||
module AugerThread(outer_diam, inner_diam, height, pitch, tooth_angle=30, tolerance=0.4, tip_height=0, tip_min_fract=0)
|
||||
|
||||
// This creates a threaded hole in its children using metric standards by
|
||||
// default.
|
||||
module ScrewHole(outer_diam, height, position=[0,0,0], rotation=[0,0,0], pitch=0, tooth_angle=30, tolerance=0.4, tooth_height=0)
|
||||
|
||||
// This creates an auger-style threaded hole in its children.
|
||||
module AugerHole(outer_diam, inner_diam, height, pitch, position=[0,0,0], rotation=[0,0,0], tooth_angle=30, tolerance=0.4)
|
||||
|
||||
// This inserts a ClearanceHole in its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0.
|
||||
module ClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], tolerance=0.4)
|
||||
|
||||
// This inserts a ClearanceHole with a recessed bolt hole in its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0. The default
|
||||
// recessed parameters fit a standard metric bolt.
|
||||
module RecessedClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], recessed_diam=-1, recessed_height=-1, tolerance=0.4)
|
||||
|
||||
// This inserts a countersunk ClearanceHole in its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0.
|
||||
// The countersunk side is on the bottom by default.
|
||||
module CountersunkClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], sinkdiam=0, sinkangle=45, tolerance=0.4)
|
||||
|
||||
// This inserts a Phillips tip shaped hole into its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0.
|
||||
module PhillipsTip(width=7, thickness=0, straightdepth=0, position=[0,0,0], rotation=[0,0,0])
|
||||
|
||||
// Create a standard sized metric bolt with hex head and hex key.
|
||||
module MetricBolt(diameter, length, tolerance=0.4)
|
||||
|
||||
// Create a standard sized metric countersunk (flat) bolt with hex key drive.
|
||||
// In compliance with convention, the length for this includes the head.
|
||||
module MetricCountersunkBolt(diameter, length, tolerance=0.4)
|
||||
|
||||
// Create a standard sized metric countersunk (flat) bolt with hex key drive.
|
||||
// In compliance with convention, the length for this includes the head.
|
||||
module MetricWoodScrew(diameter, length, tolerance=0.4)
|
||||
|
||||
// Create a standard sized metric hex nut.
|
||||
module MetricNut(diameter, thickness=0, tolerance=0.4)
|
||||
|
||||
// Create a convenient washer size for a metric nominal thread diameter.
|
||||
module MetricWasher(diameter)
|
||||
|
||||
// Solid rod on the bottom, external threads on the top.
|
||||
module RodStart(diameter, height, thread_len=0, thread_diam=0, thread_pitch=0)
|
||||
|
||||
// Solid rod on the bottom, internal threads on the top.
|
||||
// Flips around x-axis after printing to pair with RodStart.
|
||||
module RodEnd(diameter, height, thread_len=0, thread_diam=0, thread_pitch=0)
|
||||
|
||||
// Internal threads on the bottom, external threads on the top.
|
||||
module RodExtender(diameter, height, thread_len=0, thread_diam=0, thread_pitch=0)
|
||||
|
||||
// Produces a matching set of metric bolts, nuts, and washers.
|
||||
module MetricBoltSet(diameter, length, quantity=1)
|
||||
|
||||
// This generates a closed polyhedron from an array of arrays of points,
|
||||
// with each inner array tracing out one loop outlining the polyhedron.
|
||||
// pointarrays should contain an array of N arrays each of size P outlining a
|
||||
// closed manifold. The points must obey the right-hand rule. For example,
|
||||
// looking down, the P points in the inner arrays are counter-clockwise in a
|
||||
// loop, while the N point arrays increase in height. Points in each inner
|
||||
// array do not need to be equal height, but they usually should not meet or
|
||||
// cross the line segments from the adjacent points in the other arrays.
|
||||
// (N>=2, P>=3)
|
||||
// Core triangles:
|
||||
// [j][i], [j+1][i], [j+1][(i+1)%P]
|
||||
// [j][i], [j+1][(i+1)%P], [j][(i+1)%P]
|
||||
// Then triangles are formed in a loop with the middle point of the first
|
||||
// and last array.
|
||||
module ClosePoints(pointarrays)
|
||||
```
|
||||
|
||||
The following functions also provide standard metric values for a given
|
||||
diameter: ThreadPitch, HexAcrossFlats, HexAcrossCorners, HexDriveAcrossFlats,
|
||||
HexDriveAcrossCorners, CountersunkDriveAcrossFlats,
|
||||
CountersunkDriveAcrossCorners, NutThickness.
|
||||
|
||||
# Usage Notes for 3D printing
|
||||
|
||||
Good use of this library requires a printer which is properly calibrated for
|
||||
extrusion width, and which is producing consistent stable parts. In early
|
||||
tests, threaded holes (internal threads) of size M2 and higher worked fine with
|
||||
standard metal bolts. M3 bolts (external threads) worked under good
|
||||
conditions, but would be disrupted by small changes in print quality. M4 and
|
||||
higher were quite reliable.
|
||||
|
||||
The extendable rod functions were added to support printing vertical rods
|
||||
taller than the available printer height. Early tests show this works very
|
||||
well and can be quite sturdy with appropriate infill. With appropriate plastic
|
||||
and settings this could probably be used to create fairly long rods.
|
||||
|
||||
BIN
threads-scad/images/threads_demo.png
Normal file
BIN
threads-scad/images/threads_demo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 73 KiB |
632
threads-scad/threads.scad
Normal file
632
threads-scad/threads.scad
Normal file
@@ -0,0 +1,632 @@
|
||||
// Created 2016-2017 by Ryan A. Colyer.
|
||||
// This work is released with CC0 into the public domain.
|
||||
// https://creativecommons.org/publicdomain/zero/1.0/
|
||||
//
|
||||
// https://www.thingiverse.com/thing:1686322
|
||||
//
|
||||
// v2.1
|
||||
|
||||
|
||||
screw_resolution = 0.2; // in mm
|
||||
|
||||
|
||||
// Provides standard metric thread pitches.
|
||||
function ThreadPitch(diameter) =
|
||||
(diameter <= 64) ?
|
||||
lookup(diameter, [
|
||||
[2, 0.4],
|
||||
[2.5, 0.45],
|
||||
[3, 0.5],
|
||||
[4, 0.7],
|
||||
[5, 0.8],
|
||||
[6, 1.0],
|
||||
[7, 1.0],
|
||||
[8, 1.25],
|
||||
[10, 1.5],
|
||||
[12, 1.75],
|
||||
[14, 2.0],
|
||||
[16, 2.0],
|
||||
[18, 2.5],
|
||||
[20, 2.5],
|
||||
[22, 2.5],
|
||||
[24, 3.0],
|
||||
[27, 3.0],
|
||||
[30, 3.5],
|
||||
[33, 3.5],
|
||||
[36, 4.0],
|
||||
[39, 4.0],
|
||||
[42, 4.5],
|
||||
[48, 5.0],
|
||||
[52, 5.0],
|
||||
[56, 5.5],
|
||||
[60, 5.5],
|
||||
[64, 6.0]
|
||||
]) :
|
||||
diameter * 6.0 / 64;
|
||||
|
||||
|
||||
// Provides standard metric hex head widths across the flats.
|
||||
function HexAcrossFlats(diameter) =
|
||||
(diameter <= 64) ?
|
||||
lookup(diameter, [
|
||||
[2, 4],
|
||||
[2.5, 5],
|
||||
[3, 5.5],
|
||||
[3.5, 6],
|
||||
[4, 7],
|
||||
[5, 8],
|
||||
[6, 10],
|
||||
[7, 11],
|
||||
[8, 13],
|
||||
[10, 16],
|
||||
[12, 18],
|
||||
[14, 21],
|
||||
[16, 24],
|
||||
[18, 27],
|
||||
[20, 30],
|
||||
[22, 34],
|
||||
[24, 36],
|
||||
[27, 41],
|
||||
[30, 46],
|
||||
[33, 50],
|
||||
[36, 55],
|
||||
[39, 60],
|
||||
[42, 65],
|
||||
[48, 75],
|
||||
[52, 80],
|
||||
[56, 85],
|
||||
[60, 90],
|
||||
[64, 95]
|
||||
]) :
|
||||
diameter * 95 / 64;
|
||||
|
||||
// Provides standard metric hex head widths across the corners.
|
||||
function HexAcrossCorners(diameter) =
|
||||
HexAcrossFlats(diameter) / cos(30);
|
||||
|
||||
|
||||
// Provides standard metric hex (Allen) drive widths across the flats.
|
||||
function HexDriveAcrossFlats(diameter) =
|
||||
(diameter <= 64) ?
|
||||
lookup(diameter, [
|
||||
[2, 1.5],
|
||||
[2.5, 2],
|
||||
[3, 2.5],
|
||||
[3.5, 3],
|
||||
[4, 3],
|
||||
[5, 4],
|
||||
[6, 5],
|
||||
[7, 5],
|
||||
[8, 6],
|
||||
[10, 8],
|
||||
[12, 10],
|
||||
[14, 12],
|
||||
[16, 14],
|
||||
[18, 15],
|
||||
[20, 17],
|
||||
[22, 18],
|
||||
[24, 19],
|
||||
[27, 20],
|
||||
[30, 22],
|
||||
[33, 24],
|
||||
[36, 27],
|
||||
[39, 30],
|
||||
[42, 32],
|
||||
[48, 36],
|
||||
[52, 36],
|
||||
[56, 41],
|
||||
[60, 42],
|
||||
[64, 46]
|
||||
]) :
|
||||
diameter * 46 / 64;
|
||||
|
||||
// Provides standard metric hex (Allen) drive widths across the corners.
|
||||
function HexDriveAcrossCorners(diameter) =
|
||||
HexDriveAcrossFlats(diameter) / cos(30);
|
||||
|
||||
// Provides metric countersunk hex (Allen) drive widths across the flats.
|
||||
function CountersunkDriveAcrossFlats(diameter) =
|
||||
(diameter <= 14) ?
|
||||
HexDriveAcrossFlats(HexDriveAcrossFlats(diameter)) :
|
||||
round(0.6*diameter);
|
||||
|
||||
// Provides metric countersunk hex (Allen) drive widths across the corners.
|
||||
function CountersunkDriveAcrossCorners(diameter) =
|
||||
CountersunkDriveAcrossFlats(diameter) / cos(30);
|
||||
|
||||
// Provides standard metric nut thickness.
|
||||
function NutThickness(diameter) =
|
||||
(diameter <= 64) ?
|
||||
lookup(diameter, [
|
||||
[2, 1.6],
|
||||
[2.5, 2],
|
||||
[3, 2.4],
|
||||
[3.5, 2.8],
|
||||
[4, 3.2],
|
||||
[5, 4.7],
|
||||
[6, 5.2],
|
||||
[7, 6.0],
|
||||
[8, 6.8],
|
||||
[10, 8.4],
|
||||
[12, 10.8],
|
||||
[14, 12.8],
|
||||
[16, 14.8],
|
||||
[18, 15.8],
|
||||
[20, 18.0],
|
||||
[22, 21.1],
|
||||
[24, 21.5],
|
||||
[27, 23.8],
|
||||
[30, 25.6],
|
||||
[33, 28.7],
|
||||
[36, 31.0],
|
||||
[42, 34],
|
||||
[48, 38],
|
||||
[56, 45],
|
||||
[64, 51]
|
||||
]) :
|
||||
diameter * 51 / 64;
|
||||
|
||||
|
||||
// This generates a closed polyhedron from an array of arrays of points,
|
||||
// with each inner array tracing out one loop outlining the polyhedron.
|
||||
// pointarrays should contain an array of N arrays each of size P outlining a
|
||||
// closed manifold. The points must obey the right-hand rule. For example,
|
||||
// looking down, the P points in the inner arrays are counter-clockwise in a
|
||||
// loop, while the N point arrays increase in height. Points in each inner
|
||||
// array do not need to be equal height, but they usually should not meet or
|
||||
// cross the line segments from the adjacent points in the other arrays.
|
||||
// (N>=2, P>=3)
|
||||
// Core triangles:
|
||||
// [j][i], [j+1][i], [j+1][(i+1)%P]
|
||||
// [j][i], [j+1][(i+1)%P], [j][(i+1)%P]
|
||||
// Then triangles are formed in a loop with the middle point of the first
|
||||
// and last array.
|
||||
module ClosePoints(pointarrays) {
|
||||
function recurse_avg(arr, n=0, p=[0,0,0]) = (n>=len(arr)) ? p :
|
||||
recurse_avg(arr, n+1, p+(arr[n]-p)/(n+1));
|
||||
|
||||
N = len(pointarrays);
|
||||
P = len(pointarrays[0]);
|
||||
NP = N*P;
|
||||
lastarr = pointarrays[N-1];
|
||||
midbot = recurse_avg(pointarrays[0]);
|
||||
midtop = recurse_avg(pointarrays[N-1]);
|
||||
|
||||
faces_bot = [
|
||||
for (i=[0:P-1])
|
||||
[0,i+1,1+(i+1)%len(pointarrays[0])]
|
||||
];
|
||||
|
||||
loop_offset = 1;
|
||||
bot_len = loop_offset + P;
|
||||
|
||||
faces_loop = [
|
||||
for (j=[0:N-2], i=[0:P-1], t=[0:1])
|
||||
[loop_offset, loop_offset, loop_offset] + (t==0 ?
|
||||
[j*P+i, (j+1)*P+i, (j+1)*P+(i+1)%P] :
|
||||
[j*P+i, (j+1)*P+(i+1)%P, j*P+(i+1)%P])
|
||||
];
|
||||
|
||||
top_offset = loop_offset + NP - P;
|
||||
midtop_offset = top_offset + P;
|
||||
|
||||
faces_top = [
|
||||
for (i=[0:P-1])
|
||||
[midtop_offset,top_offset+(i+1)%P,top_offset+i]
|
||||
];
|
||||
|
||||
points = [
|
||||
for (i=[-1:NP])
|
||||
(i<0) ? midbot :
|
||||
((i==NP) ? midtop :
|
||||
pointarrays[floor(i/P)][i%P])
|
||||
];
|
||||
faces = concat(faces_bot, faces_loop, faces_top);
|
||||
|
||||
polyhedron(points=points, faces=faces);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This creates a vertical rod at the origin with external threads. It uses
|
||||
// metric standards by default.
|
||||
module ScrewThread(outer_diam, height, pitch=0, tooth_angle=30, tolerance=0.4, tip_height=0, tooth_height=0, tip_min_fract=0) {
|
||||
|
||||
pitch = (pitch==0) ? ThreadPitch(outer_diam) : pitch;
|
||||
tooth_height = (tooth_height==0) ? pitch : tooth_height;
|
||||
tip_min_fract = (tip_min_fract<0) ? 0 :
|
||||
((tip_min_fract>0.9999) ? 0.9999 : tip_min_fract);
|
||||
|
||||
outer_diam_cor = outer_diam + 0.25*tolerance; // Plastic shrinkage correction
|
||||
inner_diam = outer_diam - tooth_height/tan(tooth_angle);
|
||||
or = (outer_diam_cor < screw_resolution) ?
|
||||
screw_resolution/2 : outer_diam_cor / 2;
|
||||
ir = (inner_diam < screw_resolution) ? screw_resolution/2 : inner_diam / 2;
|
||||
height = (height < screw_resolution) ? screw_resolution : height;
|
||||
|
||||
steps_per_loop_try = ceil(2*3.14159265359*or / screw_resolution);
|
||||
steps_per_loop = (steps_per_loop_try < 4) ? 4 : steps_per_loop_try;
|
||||
hs_ext = 3;
|
||||
hsteps = ceil(3 * height / pitch) + 2*hs_ext;
|
||||
|
||||
extent = or - ir;
|
||||
|
||||
tip_start = height-tip_height;
|
||||
tip_height_sc = tip_height / (1-tip_min_fract);
|
||||
|
||||
tip_height_ir = (tip_height_sc > tooth_height/2) ?
|
||||
tip_height_sc - tooth_height/2 : tip_height_sc;
|
||||
|
||||
tip_height_w = (tip_height_sc > tooth_height) ? tooth_height : tip_height_sc;
|
||||
tip_wstart = height + tip_height_sc - tip_height - tip_height_w;
|
||||
|
||||
|
||||
function tooth_width(a, h, pitch, tooth_height, extent) =
|
||||
let(
|
||||
ang_full = h*360.0/pitch-a,
|
||||
ang_pn = atan2(sin(ang_full), cos(ang_full)),
|
||||
ang = ang_pn < 0 ? ang_pn+360 : ang_pn,
|
||||
frac = ang/360,
|
||||
tfrac_half = tooth_height / (2*pitch),
|
||||
tfrac_cut = 2*tfrac_half
|
||||
)
|
||||
(frac > tfrac_cut) ? 0 : (
|
||||
(frac <= tfrac_half) ?
|
||||
((frac / tfrac_half) * extent) :
|
||||
((1 - (frac - tfrac_half)/tfrac_half) * extent)
|
||||
);
|
||||
|
||||
|
||||
pointarrays = [
|
||||
for (hs=[0:hsteps])
|
||||
[
|
||||
for (s=[0:steps_per_loop-1])
|
||||
let(
|
||||
ang_full = s*360.0/steps_per_loop,
|
||||
ang_pn = atan2(sin(ang_full), cos(ang_full)),
|
||||
ang = ang_pn < 0 ? ang_pn+360 : ang_pn,
|
||||
|
||||
h_fudge = pitch*0.001,
|
||||
|
||||
h_mod =
|
||||
(hs%3 == 2) ?
|
||||
((s == steps_per_loop-1) ? tooth_height - h_fudge : (
|
||||
(s == steps_per_loop-2) ? tooth_height/2 : 0)) : (
|
||||
(hs%3 == 0) ?
|
||||
((s == steps_per_loop-1) ? pitch-tooth_height/2 : (
|
||||
(s == steps_per_loop-2) ? pitch-tooth_height + h_fudge : 0)) :
|
||||
((s == steps_per_loop-1) ? pitch-tooth_height/2 + h_fudge : (
|
||||
(s == steps_per_loop-2) ? tooth_height/2 : 0))
|
||||
),
|
||||
|
||||
h_level =
|
||||
(hs%3 == 2) ? tooth_height - h_fudge : (
|
||||
(hs%3 == 0) ? 0 : tooth_height/2),
|
||||
|
||||
h_ub = floor((hs-hs_ext)/3) * pitch
|
||||
+ h_level + ang*pitch/360.0 - h_mod,
|
||||
h_max = height - (hsteps-hs) * h_fudge,
|
||||
h_min = hs * h_fudge,
|
||||
h = (h_ub < h_min) ? h_min : ((h_ub > h_max) ? h_max : h_ub),
|
||||
|
||||
ht = h - tip_start,
|
||||
hf_ir = ht/tip_height_ir,
|
||||
ht_w = h - tip_wstart,
|
||||
hf_w_t = ht_w/tip_height_w,
|
||||
hf_w = (hf_w_t < 0) ? 0 : ((hf_w_t > 1) ? 1 : hf_w_t),
|
||||
|
||||
ext_tip = (h <= tip_wstart) ? extent : (1-hf_w) * extent,
|
||||
wnormal = tooth_width(ang, h, pitch, tooth_height, ext_tip),
|
||||
w = (h <= tip_wstart) ? wnormal :
|
||||
(1-hf_w) * wnormal +
|
||||
hf_w * (0.1*screw_resolution + (wnormal * wnormal * wnormal /
|
||||
(ext_tip*ext_tip+0.1*screw_resolution))),
|
||||
r = (ht <= 0) ? ir + w :
|
||||
( (ht < tip_height_ir ? ((2/(1+(hf_ir*hf_ir))-1) * ir) : 0) + w)
|
||||
)
|
||||
[r*cos(ang), r*sin(ang), h]
|
||||
]
|
||||
];
|
||||
|
||||
|
||||
ClosePoints(pointarrays);
|
||||
}
|
||||
|
||||
|
||||
// This creates a vertical rod at the origin with external auger-style
|
||||
// threads.
|
||||
module AugerThread(outer_diam, inner_diam, height, pitch, tooth_angle=30, tolerance=0.4, tip_height=0, tip_min_fract=0) {
|
||||
tooth_height = tan(tooth_angle)*(outer_diam-inner_diam);
|
||||
ScrewThread(outer_diam, height, pitch, tooth_angle, tolerance, tip_height,
|
||||
tooth_height, tip_min_fract);
|
||||
}
|
||||
|
||||
|
||||
// This creates a threaded hole in its children using metric standards by
|
||||
// default.
|
||||
module ScrewHole(outer_diam, height, position=[0,0,0], rotation=[0,0,0], pitch=0, tooth_angle=30, tolerance=0.4, tooth_height=0) {
|
||||
extra_height = 0.001 * height;
|
||||
|
||||
difference() {
|
||||
children();
|
||||
translate(position)
|
||||
rotate(rotation)
|
||||
translate([0, 0, -extra_height/2])
|
||||
ScrewThread(1.01*outer_diam + 1.25*tolerance, height + extra_height,
|
||||
pitch, tooth_angle, tolerance, tooth_height=tooth_height);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This creates an auger-style threaded hole in its children.
|
||||
module AugerHole(outer_diam, inner_diam, height, pitch, position=[0,0,0], rotation=[0,0,0], tooth_angle=30, tolerance=0.4) {
|
||||
tooth_height = tan(tooth_angle)*(outer_diam-inner_diam);
|
||||
ScrewHole(outer_diam, height, position, rotation, pitch, tooth_angle,
|
||||
tolerance, tooth_height=tooth_height) children();
|
||||
}
|
||||
|
||||
|
||||
// This inserts a ClearanceHole in its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0.
|
||||
module ClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], tolerance=0.4) {
|
||||
extra_height = 0.001 * height;
|
||||
|
||||
difference() {
|
||||
children();
|
||||
translate(position)
|
||||
rotate(rotation)
|
||||
translate([0, 0, -extra_height/2])
|
||||
cylinder(h=height + extra_height, r=(diameter/2+tolerance));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This inserts a ClearanceHole with a recessed bolt hole in its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0. The default
|
||||
// recessed parameters fit a standard metric bolt.
|
||||
module RecessedClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], recessed_diam=-1, recessed_height=-1, tolerance=0.4) {
|
||||
recessed_diam = (recessed_diam < 0) ?
|
||||
HexAcrossCorners(diameter) : recessed_diam;
|
||||
recessed_height = (recessed_height < 0) ? diameter : recessed_height;
|
||||
extra_height = 0.001 * height;
|
||||
|
||||
difference() {
|
||||
children();
|
||||
translate(position)
|
||||
rotate(rotation)
|
||||
translate([0, 0, -extra_height/2])
|
||||
cylinder(h=height + extra_height, r=(diameter/2+tolerance));
|
||||
translate(position)
|
||||
rotate(rotation)
|
||||
translate([0, 0, -extra_height/2])
|
||||
cylinder(h=recessed_height + extra_height/2,
|
||||
r=(recessed_diam/2+tolerance));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This inserts a countersunk ClearanceHole in its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0.
|
||||
// The countersunk side is on the bottom by default.
|
||||
module CountersunkClearanceHole(diameter, height, position=[0,0,0], rotation=[0,0,0], sinkdiam=0, sinkangle=45, tolerance=0.4) {
|
||||
extra_height = 0.001 * height;
|
||||
sinkdiam = (sinkdiam==0) ? 2*diameter : sinkdiam;
|
||||
sinkheight = ((sinkdiam-diameter)/2)/tan(sinkangle);
|
||||
|
||||
difference() {
|
||||
children();
|
||||
translate(position)
|
||||
rotate(rotation)
|
||||
translate([0, 0, -extra_height/2])
|
||||
union() {
|
||||
cylinder(h=height + extra_height, r=(diameter/2+tolerance));
|
||||
cylinder(h=sinkheight + extra_height, r1=(sinkdiam/2+tolerance), r2=(diameter/2+tolerance), $fn=24*diameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// This inserts a Phillips tip shaped hole into its children.
|
||||
// The rotation vector is applied first, then the position translation,
|
||||
// starting from a position upward from the z-axis at z=0.
|
||||
module PhillipsTip(width=7, thickness=0, straightdepth=0, position=[0,0,0], rotation=[0,0,0]) {
|
||||
thickness = (thickness <= 0) ? width*2.5/7 : thickness;
|
||||
straightdepth = (straightdepth <= 0) ? width*3.5/7 : straightdepth;
|
||||
angledepth = (width-thickness)/2;
|
||||
height = straightdepth + angledepth;
|
||||
extra_height = 0.001 * height;
|
||||
|
||||
difference() {
|
||||
children();
|
||||
translate(position)
|
||||
rotate(rotation)
|
||||
union() {
|
||||
hull() {
|
||||
translate([-width/2, -thickness/2, -extra_height/2])
|
||||
cube([width, thickness, straightdepth+extra_height]);
|
||||
translate([-thickness/2, -thickness/2, height-extra_height])
|
||||
cube([thickness, thickness, extra_height]);
|
||||
}
|
||||
hull() {
|
||||
translate([-thickness/2, -width/2, -extra_height/2])
|
||||
cube([thickness, width, straightdepth+extra_height]);
|
||||
translate([-thickness/2, -thickness/2, height-extra_height])
|
||||
cube([thickness, thickness, extra_height]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Create a standard sized metric bolt with hex head and hex key.
|
||||
module MetricBolt(diameter, length, tolerance=0.4) {
|
||||
drive_tolerance = pow(3*tolerance/HexDriveAcrossCorners(diameter),2)
|
||||
+ 0.75*tolerance;
|
||||
|
||||
difference() {
|
||||
cylinder(h=diameter, r=(HexAcrossCorners(diameter)/2-0.5*tolerance), $fn=6);
|
||||
cylinder(h=diameter,
|
||||
r=(HexDriveAcrossCorners(diameter)+drive_tolerance)/2, $fn=6,
|
||||
center=true);
|
||||
}
|
||||
translate([0,0,diameter-0.01])
|
||||
ScrewThread(diameter, length+0.01, tolerance=tolerance,
|
||||
tip_height=ThreadPitch(diameter), tip_min_fract=0.75);
|
||||
}
|
||||
|
||||
|
||||
// Create a standard sized metric countersunk (flat) bolt with hex key drive.
|
||||
// In compliance with convention, the length for this includes the head.
|
||||
module MetricCountersunkBolt(diameter, length, tolerance=0.4) {
|
||||
drive_tolerance = pow(3*tolerance/CountersunkDriveAcrossCorners(diameter),2)
|
||||
+ 0.75*tolerance;
|
||||
|
||||
difference() {
|
||||
cylinder(h=diameter/2, r1=diameter, r2=diameter/2, $fn=24*diameter);
|
||||
cylinder(h=0.8*diameter,
|
||||
r=(CountersunkDriveAcrossCorners(diameter)+drive_tolerance)/2, $fn=6,
|
||||
center=true);
|
||||
}
|
||||
translate([0,0,diameter/2-0.01])
|
||||
ScrewThread(diameter, length-diameter/2+0.01, tolerance=tolerance,
|
||||
tip_height=ThreadPitch(diameter), tip_min_fract=0.75);
|
||||
}
|
||||
|
||||
|
||||
// Create a standard sized metric countersunk (flat) bolt with hex key drive.
|
||||
// In compliance with convention, the length for this includes the head.
|
||||
module MetricWoodScrew(diameter, length, tolerance=0.4) {
|
||||
drive_tolerance = pow(3*tolerance/CountersunkDriveAcrossCorners(diameter),2)
|
||||
+ 0.75*tolerance;
|
||||
|
||||
PhillipsTip(diameter-2)
|
||||
union() {
|
||||
cylinder(h=diameter/2, r1=diameter, r2=diameter/2, $fn=24*diameter);
|
||||
|
||||
translate([0,0,diameter/2-0.01])
|
||||
ScrewThread(diameter, length-diameter/2+0.01, tolerance=tolerance,
|
||||
tip_height=diameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Create a standard sized metric hex nut.
|
||||
module MetricNut(diameter, thickness=0, tolerance=0.4) {
|
||||
thickness = (thickness==0) ? NutThickness(diameter) : thickness;
|
||||
ScrewHole(diameter, thickness, tolerance=tolerance)
|
||||
cylinder(h=thickness, r=HexAcrossCorners(diameter)/2-0.5*tolerance, $fn=6);
|
||||
}
|
||||
|
||||
|
||||
// Create a convenient washer size for a metric nominal thread diameter.
|
||||
module MetricWasher(diameter) {
|
||||
difference() {
|
||||
cylinder(h=diameter/5, r=1.15*diameter, $fn=24*diameter);
|
||||
cylinder(h=2*diameter, r=0.575*diameter, $fn=12*diameter, center=true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Solid rod on the bottom, external threads on the top.
|
||||
module RodStart(diameter, height, thread_len=0, thread_diam=0, thread_pitch=0) {
|
||||
// A reasonable default.
|
||||
thread_diam = (thread_diam==0) ? 0.75*diameter : thread_diam;
|
||||
thread_len = (thread_len==0) ? 0.5*diameter : thread_len;
|
||||
thread_pitch = (thread_pitch==0) ? ThreadPitch(thread_diam) : thread_pitch;
|
||||
|
||||
cylinder(r=diameter/2, h=height, $fn=24*diameter);
|
||||
|
||||
translate([0, 0, height])
|
||||
ScrewThread(thread_diam, thread_len, thread_pitch,
|
||||
tip_height=thread_pitch, tip_min_fract=0.75);
|
||||
}
|
||||
|
||||
|
||||
// Solid rod on the bottom, internal threads on the top.
|
||||
// Flips around x-axis after printing to pair with RodStart.
|
||||
module RodEnd(diameter, height, thread_len=0, thread_diam=0, thread_pitch=0) {
|
||||
// A reasonable default.
|
||||
thread_diam = (thread_diam==0) ? 0.75*diameter : thread_diam;
|
||||
thread_len = (thread_len==0) ? 0.5*diameter : thread_len;
|
||||
thread_pitch = (thread_pitch==0) ? ThreadPitch(thread_diam) : thread_pitch;
|
||||
|
||||
ScrewHole(thread_diam, thread_len, [0, 0, height], [180,0,0], thread_pitch)
|
||||
cylinder(r=diameter/2, h=height, $fn=24*diameter);
|
||||
}
|
||||
|
||||
|
||||
// Internal threads on the bottom, external threads on the top.
|
||||
module RodExtender(diameter, height, thread_len=0, thread_diam=0, thread_pitch=0) {
|
||||
// A reasonable default.
|
||||
thread_diam = (thread_diam==0) ? 0.75*diameter : thread_diam;
|
||||
thread_len = (thread_len==0) ? 0.5*diameter : thread_len;
|
||||
thread_pitch = (thread_pitch==0) ? ThreadPitch(thread_diam) : thread_pitch;
|
||||
|
||||
max_bridge = height - thread_len;
|
||||
// Use 60 degree slope if it will fit.
|
||||
bridge_height = ((thread_diam/4) < max_bridge) ? thread_diam/4 : max_bridge;
|
||||
|
||||
difference() {
|
||||
union() {
|
||||
ScrewHole(thread_diam, thread_len, pitch=thread_pitch)
|
||||
cylinder(r=diameter/2, h=height, $fn=24*diameter);
|
||||
|
||||
translate([0,0,height])
|
||||
ScrewThread(thread_diam, thread_len, pitch=thread_pitch,
|
||||
tip_height=thread_pitch, tip_min_fract=0.75);
|
||||
}
|
||||
// Carve out a small conical area as a bridge.
|
||||
translate([0,0,thread_len])
|
||||
cylinder(h=bridge_height, r1=thread_diam/2, r2=0.1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Produces a matching set of metric bolts, nuts, and washers.
|
||||
module MetricBoltSet(diameter, length, quantity=1) {
|
||||
for (i=[0:quantity-1]) {
|
||||
translate([0, i*4*diameter, 0]) MetricBolt(diameter, length);
|
||||
translate([4*diameter, i*4*diameter, 0]) MetricNut(diameter);
|
||||
translate([8*diameter, i*4*diameter, 0]) MetricWasher(diameter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module Demo() {
|
||||
*translate([0,-0,0]) MetricBoltSet(3, 8);
|
||||
*translate([0,-20,0]) MetricBoltSet(4, 8);
|
||||
*translate([0,-40,0]) MetricBoltSet(5, 8);
|
||||
*translate([0,-60,0]) MetricBoltSet(6, 8);
|
||||
*translate([0,-80,0]) MetricBoltSet(8, 8);
|
||||
|
||||
translate([0,25,0]) MetricCountersunkBolt(5, 10);
|
||||
translate([23,18,5])
|
||||
scale([1,1,-1])
|
||||
CountersunkClearanceHole(5, 8, [7,7,0], [0,0,0])
|
||||
cube([14, 14, 5]);
|
||||
|
||||
translate([70, -10, 0])
|
||||
RodStart(20, 30);
|
||||
translate([70, 20, 0])
|
||||
RodEnd(20, 30);
|
||||
|
||||
*translate([70, -45, 0])
|
||||
MetricWoodScrew(8, 20);
|
||||
|
||||
*translate([12, 50, 0])
|
||||
union() {
|
||||
translate([0, 0, 5.99])
|
||||
AugerThread(15, 3.5, 22, 7, tooth_angle=15, tip_height=7);
|
||||
translate([-4, -9, 0]) cube([8, 18, 6]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Demo();
|
||||
|
||||
//MetricBoltSet(6, 8, 10);
|
||||
|
||||
764836
threads-scad/threads_demo.stl
Normal file
764836
threads-scad/threads_demo.stl
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user