Added screw threads to most things that are threaded.

Added a mechanism for tests.py and views.py to have command line options.
This commit is contained in:
Chris Palmer 2020-02-22 19:44:01 +00:00
parent 1614f50b73
commit e068918e21
76 changed files with 650 additions and 242 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@ tests/bom/
*.log
*.html
bounds.json
options.json
times.txt
*_diff.png
*.echo

BIN
docs/metric_threads.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 760 KiB

After

Width:  |  Height:  |  Size: 760 KiB

View File

@ -32,8 +32,8 @@ See [usage](docs/usage.md) for requirements, installation instructions and a usa
<tr><td> <a href = "#Fans">Fans</a> </td><td> <a href = "#Rod">Rod</a> </td><td> <a href = "#Handle">Handle</a> </td><td> <a href = "#Rounded_polygon">Rounded_polygon</a> </td><td></td></tr>
<tr><td> <a href = "#Fuseholder">Fuseholder</a> </td><td> <a href = "#Screws">Screws</a> </td><td> <a href = "#Pcb_mount">Pcb_mount</a> </td><td> <a href = "#Sector">Sector</a> </td><td></td></tr>
<tr><td> <a href = "#Geared_steppers">Geared_steppers</a> </td><td> <a href = "#Sealing_strip">Sealing_strip</a> </td><td> <a href = "#Psu_shroud">Psu_shroud</a> </td><td> <a href = "#Sweep">Sweep</a> </td><td></td></tr>
<tr><td> <a href = "#Green_terminals">Green_terminals</a> </td><td> <a href = "#Sheets">Sheets</a> </td><td> <a href = "#Ribbon_clamp">Ribbon_clamp</a> </td><td> <a href = "#Tube">Tube</a> </td><td></td></tr>
<tr><td> <a href = "#Hot_ends">Hot_ends</a> </td><td> <a href = "#Spades">Spades</a> </td><td> <a href = "#Screw_knob">Screw_knob</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Green_terminals">Green_terminals</a> </td><td> <a href = "#Sheets">Sheets</a> </td><td> <a href = "#Ribbon_clamp">Ribbon_clamp</a> </td><td> <a href = "#Thread">Thread</a> </td><td></td></tr>
<tr><td> <a href = "#Hot_ends">Hot_ends</a> </td><td> <a href = "#Spades">Spades</a> </td><td> <a href = "#Screw_knob">Screw_knob</a> </td><td> <a href = "#Tube">Tube</a> </td><td></td></tr>
<tr><td> <a href = "#Hygrometer">Hygrometer</a> </td><td> <a href = "#Spools">Spools</a> </td><td> <a href = "#Socket_box">Socket_box</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Iecs">Iecs</a> </td><td> <a href = "#Springs">Springs</a> </td><td> <a href = "#Ssr_shroud">Ssr_shroud</a> </td><td></td><td></td></tr>
<tr><td> <a href = "#Inserts">Inserts</a> </td><td> <a href = "#Ssrs">Ssrs</a> </td><td> <a href = "#Strap_handle">Strap_handle</a> </td><td></td><td></td></tr>
@ -1152,7 +1152,9 @@ Nuts for leadscrews.
| ```leadnut_hole_dia(type)``` | The diameter of the screw holes |
| ```leadnut_hole_pitch(type)``` | The radia pitch of the screw holes |
| ```leadnut_holes(type)``` | The number of screw holes |
| ```leadnut_lead(type)``` | Screw lead |
| ```leadnut_od(type)``` | Outer diameter of the shank |
| ```leadnut_pitch(type)``` | Screw pitch |
| ```leadnut_screw(type)``` | The type of the fixing screws |
### Functions
@ -2281,14 +2283,20 @@ Steel rods and studding with chamfered ends.
### Modules
| Module | Description |
|:--- |:--- |
| ```rod(d , l)``` | Draw a smooth rod with specified length and diameter |
| ```studding(d , l)``` | Draw a threaded rod with specified length and diameter |
| ```leadscrew(d , l, lead, starts, center = true)``` | Draw a leadscrew with specified diameter, length, lead and number of starts |
| ```rod(d , l, center = true)``` | Draw a smooth rod with specified diameter and length |
| ```studding(d , l, center = true)``` | Draw a threaded rod with specified diameter and length |
![rod](tests/png/rod.png)
### Vitamins
| Qty | Module call | BOM entry |
| ---:|:--- |:---|
| 1 | ```leadscrew(10, 80, 8, 4)``` | Leadscrew 10 x 80mm, 8mm lead, 4 starts |
| 1 | ```leadscrew(12, 80, 12, 4)``` | Leadscrew 12 x 80mm, 12mm lead, 4 starts |
| 1 | ```leadscrew(16, 80, 16, 4)``` | Leadscrew 16 x 80mm, 16mm lead, 4 starts |
| 1 | ```leadscrew(6, 80, 2, 1)``` | Leadscrew 6 x 80mm, 2mm lead, 1 starts |
| 1 | ```leadscrew(8, 80, 8, 4)``` | Leadscrew 8 x 80mm, 8mm lead, 4 starts |
| 1 | ```rod(10, 80)``` | Smooth rod 10mm x 80mm |
| 1 | ```rod(12, 80)``` | Smooth rod 12mm x 80mm |
| 1 | ```rod(16, 80)``` | Smooth rod 16mm x 80mm |
@ -2358,17 +2366,17 @@ Machine screws and wood screws with various head styles.
| Qty | Module call | BOM entry |
| ---:|:--- |:---|
| 1 | ```screw(No632_pan_screw, 30)``` | Screw 6-32 pan x 30mm |
| 1 | ```screw(M2_cap_screw, 25)``` | Screw M2 cap x 25mm |
| 1 | ```screw(M2_cs_cap_screw, 25)``` | Screw M2 cs cap x 25mm |
| 1 | ```screw(M2p5_cap_screw, 25)``` | Screw M2.5 cap x 25mm |
| 1 | ```screw(M2p5_pan_screw, 30)``` | Screw M2.5 pan x 30mm |
| 1 | ```screw(M3_cap_screw, 25)``` | Screw M3 cap x 25mm |
| 1 | ```screw(M3_cs_cap_screw, 25)``` | Screw M3 cs cap x 25mm |
| 1 | ```screw(M3_dome_screw, 25)``` | Screw M3 dome x 25mm |
| 1 | ```screw(M2_cap_screw, 10)``` | Screw M2 cap x 10mm |
| 1 | ```screw(M2_cs_cap_screw, 10)``` | Screw M2 cs cap x 10mm |
| 1 | ```screw(M2p5_cap_screw, 10)``` | Screw M2.5 cap x 10mm |
| 1 | ```screw(M2p5_pan_screw, 10)``` | Screw M2.5 pan x 10mm |
| 1 | ```screw(M3_cap_screw, 10)``` | Screw M3 cap x 10mm |
| 1 | ```screw(M3_cs_cap_screw, 10)``` | Screw M3 cs cap x 10mm |
| 1 | ```screw(M3_dome_screw, 10)``` | Screw M3 dome x 10mm |
| 1 | ```screw(M3_grub_screw, 6)``` | Screw M3 grub x 6mm |
| 1 | ```screw(M3_hex_screw, 30)``` | Screw M3 hex x 30mm |
| 1 | ```screw(M3_low_cap_screw, 25)``` | Screw M3 low cap x 25mm |
| 1 | ```screw(M3_pan_screw, 30)``` | Screw M3 pan x 30mm |
| 1 | ```screw(M3_hex_screw, 10)``` | Screw M3 hex x 10mm |
| 1 | ```screw(M3_low_cap_screw, 10)``` | Screw M3 low cap x 10mm |
| 1 | ```screw(M3_pan_screw, 10)``` | Screw M3 pan x 10mm |
| 1 | ```screw(M4_cap_screw, 25)``` | Screw M4 cap x 25mm |
| 1 | ```screw(M4_cs_cap_screw, 25)``` | Screw M4 cs cap x 25mm |
| 1 | ```screw(M4_dome_screw, 25)``` | Screw M4 dome x 25mm |
@ -2383,8 +2391,8 @@ Machine screws and wood screws with various head styles.
| 1 | ```screw(M6_pan_screw, 30)``` | Screw M6 pan x 30mm |
| 1 | ```screw(M8_cap_screw, 35)``` | Screw M8 cap x 35mm |
| 1 | ```screw(M8_hex_screw, 30)``` | Screw M8 hex x 30mm |
| 1 | ```screw(No2_screw, 30)``` | Screw No2 pan wood x 30mm |
| 1 | ```screw(No4_screw, 30)``` | Screw No4 pan wood x 30mm |
| 1 | ```screw(No2_screw, 10)``` | Screw No2 pan wood x 10mm |
| 1 | ```screw(No4_screw, 10)``` | Screw No4 pan wood x 10mm |
| 1 | ```screw(No6_cs_screw, 30)``` | Screw No6 cs wood x 30mm |
| 1 | ```screw(No6_screw, 30)``` | Screw No6 pan wood x 30mm |
@ -2672,7 +2680,7 @@ NEMA stepper motor model.
| ```NEMA_length(type)``` | Body length |
| ```NEMA_radius(type)``` | End cap radius |
| ```NEMA_shaft_dia(type)``` | Shaft diameter |
| ```NEMA_shaft_length(type)``` | Shaft length above the face |
| ```NEMA_shaft_length(type)``` | Shaft length above the face, if a list then a leadscrew: length, lead, starts |
| ```NEMA_width(type)``` | Width of the square face |
### Functions
@ -2684,7 +2692,7 @@ NEMA stepper motor model.
### Modules
| Module | Description |
|:--- |:--- |
| ```NEMA(type)``` | Draw specified NEMA stepper motor |
| ```NEMA(type, shaft_angle = 0)``` | Draw specified NEMA stepper motor |
| ```NEMA_outline(type)``` | 2D outline |
| ```NEMA_screw_positions(type, n = 4)``` | Positions children at the screw holes |
| ```NEMA_screws(type, screw, n = 4, screw_length = 8, earth = undef)``` | Place screws and optional earth tag |
@ -4431,6 +4439,8 @@ Maths utilities for manipulating vectors and matrices.
### Functions
| Function | Description |
|:--- |:--- |
| ```angle_between(v1, v2)``` | Return the angle between two vectors |
| ```euler(R)``` | Convert a rotation matrix to a Euler rotation vector. |
| ```identity(n, x = 1)``` | Construct an arbitrary size identity matrix |
| ```reverse(v)``` | Reverse a vector |
| ```rotate(a, v)``` | Generate a 4x4 rotation matrix, ```a``` can be a vector of three angles or a single angle around ```z```, or around axis ```v``` |
@ -4441,6 +4451,7 @@ Maths utilities for manipulating vectors and matrices.
| ```transpose(m)``` | Transpose an arbitrary size matrix |
| ```unit(v)``` | Convert ```v``` to a unit vector |
| ```vec3(v)``` | Return a 3 vector with the first three elements of ```v``` |
| ```vec4(v)``` | Return a 4 vector with the first three elements of ```v``` |
![maths](tests/png/maths.png)
@ -4607,9 +4618,12 @@ An additional twist around the path can be specified. If the path is closed this
| ```after(path1, path2)``` | Translate ```path2``` so its start meets the end of ```path1``` and then concatenate |
| ```arc_points(r, a = [90, 0, 180], al = 90)``` | Generate the points of a circular arc |
| ```before(path1, path2)``` | Translate ```path1``` so its end meets the start of ```path2``` and then concatenate |
| ```circle_points(r = 1, z = 0)``` | Generate the points of a circle, setting z makes a single turn spiral |
| ```cap(facets, segment = 0, end)``` | Create the mesh for an end cap |
| ```circle_points(r = 1, z = 0, dir = -1)``` | Generate the points of a circle, setting z makes a single turn spiral |
| ```helical_twist_per_segment(r, pitch, sides)``` | Calculate the twist around Z that rotate_from_to() introduces |
| ```path_length(path, i = 0, length = 0)``` | Calculated the length along a path |
| ```rectangle_points(w, h)``` | Generate the points of a rectangle |
| ```skin_faces(points, npoints, facets, loop, offset = 0)``` | Create the mesh for the swept volume without end caps |
| ```sweep(path, profile, loop = false, twist = 0)``` | Generate the point list and face list of the swept volume |
### Modules
@ -4620,6 +4634,50 @@ An additional twist around the path can be specified. If the path is closed this
![sweep](tests/png/sweep.png)
<a href="#top">Top</a>
---
<a name="Thread"></a>
## Thread
Utilities for making threads with sweep. They can be used to model screws, nuts, studding, leadscrews, etc, and also to make printed threads.
The ends can be tapered, flat or chamfered by setting the ```top``` and ```bot``` parameters to -1 for tapered, 0 for a flat cut and positive to
specify a chamfer angle.
Threads are by default solid, so the male version is wrapped around a cylinder and the female inside a tube. This can be suppressed to just get the helix, for
example to make a printed pot with a screw top lid.
Threads with a typical 60 degree angle appear too bright with OpenSCAD's primitive lighting model as they face towards the lights more than the top and sides of
a cylinder. To get around this a colour can be passed to thread that is used to colour the cylinder and then toned down to colour the helix.
Making the ends requires a CGAL intersection, which make threads relatively slow. For this reason they are generally disabled when using the GUI but can
be enabled by setting ```$show_threads``` to ```true```. When the tests are run, by default, threads are enabled only for things that feature them like screws.
This behaviour can be changed by setting a ```SHOW_THREADS``` environment variable to ```false``` to disable all threads and ```true``` to enable all threads.
The same variable also affects the generation of assembly diagrams.
Threads obey the $fn, $fa, $fs variables.
[utils/thread.scad](utils/thread.scad) Implementation.
[tests/thread.scad](tests/thread.scad) Code for this example.
### Functions
| Function | Description |
|:--- |:--- |
| ```metric_coarse_pitch(d)``` | Convert metric diameter to pitch |
| ```thread_profile(h, crest, angle, overlap = 0.1)``` | Create thread profile path |
### Modules
| Module | Description |
|:--- |:--- |
| ```female_metric_thread(d, pitch, length, center = true, top = -1, bot = -1, colour = undef)``` | Create female thread with metric profile |
| ```male_metric_thread(d, pitch, length, center = true, top = -1, bot = -1, solid = true, colour = undef)``` | Create male thread with metric profile |
| ```thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, starts = 1, solid = true, female = false, colour = undef)``` | Create male or femail thread, ends can be tapered, chamfered or square |
![thread](tests/png/thread.png)
<a href="#top">Top</a>
---

View File

@ -24,7 +24,7 @@ from __future__ import print_function
import subprocess, sys
def _run(args, silent):
def run_list(args, silent = False):
cmd = ["openscad"] + args
if not silent:
for arg in cmd:
@ -39,7 +39,7 @@ def _run(args, silent):
sys.exit(rc)
def run(*args):
_run(list(args), False)
run_list(list(args), False)
def run_silent(*args):
_run(list(args), True);
run_list(list(args), True);

49
scripts/options.py Normal file
View File

@ -0,0 +1,49 @@
#
# NopSCADlib Copyright Chris Palmer 2020
# nop.head@gmail.com
# hydraraptor.blogspot.com
#
# This file is part of NopSCADlib.
#
# NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
# GNU General Public License as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with NopSCADlib.
# If not, see <https://www.gnu.org/licenses/>.
#
# Set command line options from enviroment variables and check if they have changed
import json, os, deps
def check_options(dir = '.'):
global options, options_mtime
options = { "show_threads": str(os.getenv("SHOW_THREADS")) }
options_fname = dir + '/options.json'
try:
with open(options_fname) as json_file:
last_options = json.load(json_file)
except:
last_options = {}
if last_options != options:
with open(options_fname, 'w') as outfile:
json.dump(options, outfile, indent = 4)
options_mtime = deps.mtime(options_fname)
def have_changed(changed, target):
if not changed and deps.mtime(target) < options_mtime:
return "command line options changed"
return changed
def list():
result = []
for name in options.keys():
value = options[name]
if value != 'None':
result.append('-D$' + name + '=' + value)
return result

View File

@ -27,6 +27,7 @@ import openscad
import subprocess
import bom
import times
import options
import time
import json
import shutil
@ -96,6 +97,7 @@ def tests(tests):
index = {}
bodies = {}
times.read_times()
options.check_options(deps_dir)
#
# Make cover pic if does not exist as very slow. Delete it to force an update.
#
@ -190,11 +192,12 @@ def tests(tests):
oldest = png_name if mtime(png_name) < mtime(bom_name) else bom_name
changed = check_deps(oldest, dname)
changed = times.check_have_time(changed, scad_name)
changed = options.have_changed(changed, oldest)
if changed:
print(changed)
t = time.time()
tmp_name = 'tmp.png'
openscad.run("-D", "$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name);
openscad.run_list(options.list() + ["-D$bom=2", colour_scheme, "--projection=p", "--imgsize=%d,%d" % (w, h), "--camera=0,0,0,70,0,315,500", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, scad_name]);
times.add_time(scad_name, t)
do_cmd(["magick", tmp_name, "-trim", "-resize", "1000x600", "-bordercolor", background, "-border", "10", tmp_name])
update_image(tmp_name, png_name)

View File

@ -28,6 +28,7 @@ import openscad
from tests import do_cmd, update_image, colour_scheme, background
import time
import times
import options
from deps import *
import os
import json
@ -102,6 +103,7 @@ def views(target, do_assemblies = None):
os.makedirs(deps_dir)
times.read_times(target_dir)
options.check_options(deps_dir)
bounds_fname = top_dir + 'stls/bounds.json'
with open(bounds_fname) as json_file:
bounds_map = json.load(json_file)
@ -163,11 +165,12 @@ def views(target, do_assemblies = None):
png_name = png_name.replace('_assembly', '_assembled')
changed = check_deps(png_name, dname)
changed = times.check_have_time(changed, png_name)
changed = options.have_changed(changed, png_name)
tmp_name = 'tmp.png'
if changed:
print(changed)
t = time.time()
openscad.run("-D$show_threads=1", "-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, png_maker_name);
openscad.run_list(options.list() + ["-D$pose=1", "-D$explode=%d" % explode, colour_scheme, "--projection=p", "--imgsize=4096,4096", "--autocenter", "--viewall", "-d", dname, "-o", tmp_name, png_maker_name]);
times.add_time(png_name, t)
do_cmd(["magick", tmp_name, "-trim", "-resize", "1004x1004", "-bordercolor", background, "-border", "10", tmp_name])
update_image(tmp_name, png_name)

View File

@ -37,4 +37,5 @@ module inserts() {
}
if($preview)
let($show_threads = true)
inserts();

View File

@ -27,4 +27,5 @@ module leadnuts()
leadnut(leadnuts[$i]);
if($preview)
let($show_threads = true)
leadnuts();

View File

@ -56,7 +56,7 @@ module maths() {
//
z = [0, 0, 1];
v = cross(u, z);
a = acos(u * z);
a = angle_between(u, z);
l = 20;
@ -64,6 +64,11 @@ module maths() {
translate_z(l)
vflip()
arrow(l);
//
// Test Euler
//
assert(euler(rotate(r)) == r, "euler() failed");
}
rotate(45)

View File

@ -48,4 +48,5 @@ module nuts() {
}
if($preview)
let($show_threads = true)
nuts();

View File

@ -29,4 +29,5 @@ module opengrab_test() {
}
if($preview)
let($show_threads = true)
opengrab_test();

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 92 KiB

BIN
tests/png/thread.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -24,12 +24,21 @@ include <../vitamins/linear_bearings.scad>
use <../vitamins/rod.scad>
module rods()
layout([for(b = linear_bearings) 2 * bearing_radius(b)]) {
layout([for(b = linear_bearings) 2 * bearing_radius(b)]) let(d = bearing_rod_dia(linear_bearings[$i])){
rod(bearing_rod_dia(linear_bearings[$i]), 80);
rod(d, 80);
translate([0, 30])
studding(bearing_rod_dia(linear_bearings[$i]), 80);
studding(d, 80);
if(d >= 6)
translate([0, 60]) {
starts = d > 6 ? 4 : 1;
pitch = d > 14 ? 4
: d > 10 ? 3 : 2;
let($show_threads = true)
leadscrew(d, 80, starts * pitch, starts);
}
}
if($preview)

View File

@ -25,9 +25,10 @@ for(y = [0 : len(screw_lists) -1])
for(x = [0 : len(screw_lists[y]) -1]) {
screw = screw_lists[y][x];
if(screw) {
length = screw_max_thread(screw)
? screw_longer_than(screw_max_thread(screw) + 5)
: screw_head_type(screw) == hs_grub ? 6 : 30;
length = screw_head_type(screw) == hs_grub ? 6
: screw_radius(screw) <= 1.5 ? 10
: screw_max_thread(screw) ? screw_longer_than(screw_max_thread(screw) + 5)
: 30;
translate([x * 20, y * 20])
screw(screw, length);
}

View File

@ -44,7 +44,7 @@ knot = [ for(i=[0:.2:359])
(19*cos(3*i) + 40)*sin(2*i),
19*sin(3*i) ] ];
sweep(knot, L_points, loop = true, twist = 0);
sweep(knot, L_points, loop = true);
p = transform_points([[0,0,0], [20,0,5], [10,30,4], [0,0,0], [0,0,20]], scale(10));
n = 100;

52
tests/thread.scad Normal file
View File

@ -0,0 +1,52 @@
//
// NopSCADlib Copyright Chris Palmer 2020
// nop.head@gmail.com
// hydraraptor.blogspot.com
//
// This file is part of NopSCADlib.
//
// NopSCADlib is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// NopSCADlib is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with NopSCADlib.
// If not, see <https://www.gnu.org/licenses/>.
//
include <../core.scad>
use <../utils/thread.scad>
pitch = 2;
starts = 4;
profile = thread_profile(pitch / 2, pitch * 0.366, 30);
module threads()
for(female = [false, true]) translate([0, female ? -20 : 0]) {
length = female ? 8 : 40;
dia = female ? 8 : 8 - pitch;
colour = female ? brass : silver;
thread(dia, starts * pitch, length, profile, starts = starts, top = 45, bot = 45, female = female, colour = colour);
color(colour)
translate([20, 0])
thread(dia, starts * pitch, length, profile, starts = starts, top = 0, bot = 0, female = female);
translate([40, 0])
thread(dia, starts * pitch, length, profile, starts = starts, top = -1, bot = -1, female = female, colour = colour);
color(colour)
translate([60, 0])
thread(dia, 2 * pitch, length, profile, starts = 2, top = -1, bot = -1, female = female);
color(colour)
translate([80, 0])
thread(dia, pitch, length, profile, starts = 1, top = -1, bot = -1, female = female);
}
let($show_threads = true)
threads();

View File

@ -27,4 +27,5 @@ module toggles()
toggle(toggles[$i], 3);
if($preview)
let($show_threads = true)
toggles();

View File

@ -39,9 +39,9 @@ function rotate(a, v) = //! Generate a 4x4 rotation matrix, ```a``` can be a vec
sy = sin(av[1]),
sz = sin(av[2]))
[
[ cy * cz, cz * sx * sy - cx * sz, cx * cz * sy + sx * sz, 0],
[ cy * sz, cx * cz + sx * sy * sz,-cz * sx + cx * sy * sz, 0],
[-sy, cy * sx, cx * cy, 0],
[ cy * cz, sx * sy * cz - cx * sz, cx * sy * cz + sx * sz, 0],
[ cy * sz, sx * sy * sz + cx * cz, cx * sy * sz - sx * cz, 0],
[-sy, sx * cy, cx * cy, 0],
[ 0, 0, 0, 1]
]
: let(s = sin(a),
@ -65,6 +65,7 @@ function scale(v) = let(s = is_list(v) ? v : [v, v, v]) //! Generate a 4x4 matr
];
function vec3(v) = [v.x, v.y, v.z]; //! Return a 3 vector with the first three elements of ```v```
function vec4(v) = [v.x, v.y, v.z, 1]; //! Return a 4 vector with the first three elements of ```v```
function transform(v, m) = vec3(m * [v.x, v.y, v.z, 1]); //! Apply 4x4 transform to a 3 vector by extending it and cropping it again
function transform_points(path, m) = [for(p = path) transform(p, m)]; //! Apply transform to a path
function unit(v) = let(n = norm(v)) n ? v / n : v; //! Convert ```v``` to a unit vector
@ -74,3 +75,11 @@ function transpose(m) = [ for(j = [0 : len(m[0]) - 1]) [ for(i = [0 : len(m) - 1
function identity(n, x = 1) = [for(i = [0 : n - 1]) [for(j = [0 : n - 1]) i == j ? x : 0] ]; //! Construct an arbitrary size identity matrix
function reverse(v) = let(n = len(v) - 1) n < 0 ? [] : [for(i = [0 : n]) v[n - i]]; //! Reverse a vector
function angle_between(v1, v2) = acos(v1 * v2 / (norm(v1) * norm(v2))); //! Return the angle between two vectors
// https://www.gregslabaugh.net/publications/euler.pdf
function euler(R) = let(ay = asin(-R[2][0]), cy = cos(ay)) //! Convert a rotation matrix to a Euler rotation vector.
cy ? [ atan2(R[2][1] / cy, R[2][2] / cy), ay, atan2(R[1][0] / cy, R[0][0] / cy) ]
: R[2][0] < 0 ? [atan2( R[0][1], R[0][2]), 180, 0]
: [atan2(-R[0][1], -R[0][2]), -180, 0];

View File

@ -62,6 +62,7 @@ function rotate_from_to(a, b) =
function calculate_twist(A, B) = let(D = transpose3(B) * A) atan2(D[1][0], D[0][0]);
//
// Compute a 4x3 matrix to orientate a frame of the sweep given the position and a 3x3 rotation matrix.
// Note that the rotation matrix is transposed to allow post multiplication.
//
function orientate(p, r) =
let(x = r[0], y = r[1], z = r[2])
@ -79,12 +80,21 @@ function rot3_z(a) =
[ [ c, -s, 0],
[ s, c, 0],
[ 0, 0, 1] ];
//
// Calculate the unit tangent at a vertex given the indices before and after. One of these can be the same as i in the case
// of the start and end of a non closed path.
// of the start and end of a non closed path. Note that the edges are converted to unit vectors so that their relative lengths
// don't affect the direction of the tangent.
//
function tangent(path, before, i, after) = unit(unit(path[after] - path[i]) - unit(path[before] - path[i]));
function tangent(path, before, i, after) = unit(unit(path[i] - path[before]) + unit(path[after] - path[i]));
//
// Calculate the twist per segment caused by rotate_from_to() instead of a simple Euler rotation around Z.
//
function helical_twist_per_segment(r, pitch, sides) = //! Calculate the twist around Z that rotate_from_to() introduces
let(step_angle = 360 / sides,
lt = 2 * r * sin(step_angle), // length of tangent between two facets
slope = atan(2 * pitch / sides / lt) // slope of tangents
) step_angle * sin(slope); // angle tangent should rotate around z projected onto axis rotate_from_to() uses
//
// Generate all the surface points of the swept volume.
//
@ -111,24 +121,28 @@ function skin_points(profile, path, loop, twist = 0) =
each profile4 * orientate(path[i], rotations[i] * rot3_z(za))
];
function cap(facets, segment = 0) = [for(i = [0 : facets - 1]) segment ? facets * segment + i : facets - 1 - i];
function cap(facets, segment = 0, end) = //! Create the mesh for an end cap
let(reverse = is_undef(end) ? segment : end)
[for(i = [0 : facets - 1]) facets * segment + (reverse ? i : facets - 1 - i)];
function quad(p, a, b, c, d) = norm(p[a] - p[c]) > norm(p[b] - p[d]) ? [[b, c, d], [b, d, a]] : [[a, b, c], [a, c, d]];
function skin_faces(points, segs, facets, loop) = [for(i = [0 : facets - 1], s = [0 : segs - (loop ? 1 : 2)])
function skin_faces(points, npoints, facets, loop, offset = 0) = //! Create the mesh for the swept volume without end caps
[for(i = [0 : facets - 1], s = [0 : npoints - (loop ? 1 : 2)])
let(j = s + offset, k = loop ? (j + 1) % npoints : j + 1)
each quad(points,
s * facets + i,
s * facets + (i + 1) % facets,
((s + 1) % segs) * facets + (i + 1) % facets,
((s + 1) % segs) * facets + i)];
j * facets + i,
j * facets + (i + 1) % facets,
k * facets + (i + 1) % facets,
k * facets + i)];
function sweep(path, profile, loop = false, twist = 0) = //! Generate the point list and face list of the swept volume
let(
segments = len(path),
npoints = len(path),
facets = len(profile),
points = skin_points(profile, path, loop, twist),
skin_faces = skin_faces(points, segments, facets, loop),
faces = loop ? skin_faces : concat([cap(facets)], skin_faces, [cap(facets, segments - 1)])
skin_faces = skin_faces(points, npoints, facets, loop),
faces = loop ? skin_faces : concat([cap(facets)], skin_faces, [cap(facets, npoints - 1)])
) [points, faces];
module sweep(path, profile, loop = false, twist = 0) { //! Draw a polyhedron that is the swept volume
@ -141,9 +155,9 @@ function path_length(path, i = 0, length = 0) = //! Calculated the length along
i >= len(path) - 1 ? length
: path_length(path, i + 1, length + norm(path[i + 1] - path[i]));
function circle_points(r = 1, z = 0) = //! Generate the points of a circle, setting z makes a single turn spiral
function circle_points(r = 1, z = 0, dir = -1) = //! Generate the points of a circle, setting z makes a single turn spiral
let(sides = r2sides(r))
[for(i = [0 : sides - 1]) let(a = i * 360 / sides) [r * sin(a), r * cos(a), z * a / 360]];
[for(i = [0 : sides - 1]) let(a = dir * i * 360 / sides) [r * cos(a), r * sin(a), z * i / sides]];
function rectangle_points(w, h) = [[-w/2, -h/2, 0], [-w/2, h/2, 0], [w/2, h/2, 0], [w/2, -h/2, 0]]; //! Generate the points of a rectangle

View File

@ -18,69 +18,169 @@
//
//
//! A utilities for making threads with sweep.
//! Utilities for making threads with sweep. They can be used to model screws, nuts, studding, leadscrews, etc, and also to make printed threads.
//!
//! The ends can be tapered, flat or chamfered by setting the ```top``` and ```bot``` parameters to -1 for tapered, 0 for a flat cut and positive to
//! specify a chamfer angle.
//!
//! Threads are by default solid, so the male version is wrapped around a cylinder and the female inside a tube. This can be suppressed to just get the helix, for
//! example to make a printed pot with a screw top lid.
//!
//! Threads with a typical 60 degree angle appear too bright with OpenSCAD's primitive lighting model as they face towards the lights more than the top and sides of
//! a cylinder. To get around this a colour can be passed to thread that is used to colour the cylinder and then toned down to colour the helix.
//!
//! Making the ends requires a CGAL intersection, which make threads relatively slow. For this reason they are generally disabled when using the GUI but can
//! be enabled by setting ```$show_threads``` to ```true```. When the tests are run, by default, threads are enabled only for things that feature them like screws.
//! This behaviour can be changed by setting a ```SHOW_THREADS``` environment variable to ```false``` to disable all threads and ```true``` to enable all threads.
//! The same variable also affects the generation of assembly diagrams.
//!
//! Threads obey the $fn, $fa, $fs variables.
//
include <../core.scad>
use <sweep.scad>
use <maths.scad>
use <tube.scad>
function thread_profile(h, crest, angle) = //! Create thread profile path
let(base = crest + 2 * h * tan(angle / 2))
[[-base / 2, 0, 0], [-crest / 2, h, 0], [crest / 2, h, 0], [base / 2, 0, 0]];
thread_colour_factor = 0.8; // 60 degree threads appear too bright due to the angle facing the light sources
module male_thread(pitch, minor_d, length, profile, taper_top = true, center = true, solid = true) { //! Create male thread
turns = length / pitch + (taper_top ? 0 : 1);
r = minor_d / 2;
sides = r2sides(r);
h = max([for(p = profile) p.y]);
final = (turns - 1) * sides;
path = [for(i = [0 : sides * turns],
R = i < sides ? r - h + h * i / sides
: i > final && taper_top ? r - h * (i - final) / sides : r,
a = i * 360 / sides)
[R * sin(-a), R * cos(-a), pitch * a / 360]];
t = atan(pitch / sides / (r * cos(225 / sides)));
translate_z(center ? -length / 2 : 0) {
function thread_profile(h, crest, angle, overlap = 0.1) = //! Create thread profile path
let(base = crest + 2 * (h + overlap) * tan(angle / 2))
[[-base / 2, -overlap, 0], [-crest / 2, h, 0], [crest / 2, h, 0], [base / 2, -overlap, 0]];
module thread(dia, pitch, length, profile, center = true, top = -1, bot = -1, starts = 1, solid = true, female = false, colour = undef) { //! Create male or femail thread, ends can be tapered, chamfered or square
//
// Apply colour if defined
//
module colour(factor) if(is_undef(colour)) children(); else color(colour * factor) children();
//
// Compress the profile to compensate for it being tilted by the helix angle
//
scale = cos(atan(pitch / (PI * dia)));
sprofile = [for(p = profile) [p.x * scale, p.y, p.z]];
//
// Extract some properties from the profile, perhaps they should be stored in it.
//
h = max([for(p = sprofile) p.y]);
maxx = max([for(p = sprofile) p.x]);
minx = min([for(p = sprofile) p.x]);
crest_xmax = max([for(p = sprofile) if(p.x != maxx) p.x]);
crest_xmin = min([for(p = sprofile) if(p.x != minx) p.x]);
//
// If the ends don't taper we need an extra half turn past the ends to be cropped horizontally.
//
extra_top = top < 0 ? 0 : -minx / pitch;
extra_bot = bot < 0 ? 0 : maxx / pitch;
turns = length / pitch + extra_top + extra_bot;
//
// Generate the helix path, possibly with tapered ends
//
dir = female ? 1 : -1;
r = dia / 2;
sides = r2sides4n(r);
step_angle = 360 / sides;
segs = ceil(turns * sides);
leadin = ceil(sides / starts);
final = floor(turns * sides) - leadin;
path = [for(i = [0 : segs],
R = i < leadin && bot < 0 ? r + dir * (h - h * i / leadin)
: i > final && top < 0 ? r + dir * h * (i - final) / leadin : r,
a = i * step_angle - 360 * extra_bot)
[R * cos(a), R * sin(a), a * pitch / 360]];
//
// Generate the skin vertices
//
facets = len(profile);
twist = helical_twist_per_segment(r, pitch, sides);
//
// For female threads we need to invert the profile
//
iprofile = female ? reverse([for(p = sprofile) [p.x, -p.y, 0]]) : sprofile;
//
// If the bottom is tapered then the twist will be greater, so pre-twist the profile to get the straight bit at the correct angle
//
rprofile = bot < 0 ? transform_points(iprofile, rotate(-dir * (helical_twist_per_segment(r - h, pitch, sides) - twist) * sides / PI))
: iprofile;
points = skin_points(rprofile, path, false, twist * segs);
//
// To form the ends correctly we need to use intersection but it is very slow with the full thread so we just
// intersect the start and the end and sweep the rest outside of the intersection.
//
top_chamfer_h = (top > 0 ? h * tan(top) : 0);
bot_chamfer_h = (bot > 0 ? h * tan(bot) : 0);
top_overlap = max( maxx, top_chamfer_h - crest_xmin) / pitch;
bot_overlap = max(-minx, bot_chamfer_h + crest_xmax) / pitch;
start = ceil(sides * (bot_overlap + extra_bot));
end = segs - ceil(sides * (top_overlap + extra_top));
start_skin_faces = skin_faces(points, start + 1, facets, false);
middle_skin_faces = skin_faces(points, end - start + 1, facets, false, start);
end_skin_faces = skin_faces(points, segs - end + 1, facets, false, end);
start_faces = concat([cap(facets) ], start_skin_faces, [cap(facets, start)]);
middle_faces = concat([cap(facets, start, false)], middle_skin_faces, [cap(facets, end)]);
end_faces = concat([cap(facets, end, false)], end_skin_faces, [cap(facets, segs)]);
overlap = - profile[0].y;
translate_z((center ? -length / 2 : 0)) {
ends_faces = concat(start_faces, end_faces);
for(i = [0 : starts - 1])
colour(thread_colour_factor)
rotate(360 * i / starts + (female ? 180 / starts : 0)) {
render() intersection() {
sweep(path, profile, twist = t * sides * turns);
cylinder(d = minor_d + 5, h = length);
polyhedron(points, ends_faces);
len = length - 2 * eps;
rotate_extrude()
if(female) {
difference() {
translate([0, eps])
square([r + h + overlap, len]);
if(top_chamfer_h)
polygon([[0, length], [r, length], [r - h, length - top_chamfer_h], [0, length - top_chamfer_h]]);
if(bot_chamfer_h)
polygon([[0, 0], [r, 0], [r - h, bot_chamfer_h], [0, bot_chamfer_h]]);
}
}
else
difference() {
hull() {
translate([0, eps])
square([r, len]);
translate([0, bot_chamfer_h])
square([r + h + overlap, len - top_chamfer_h - bot_chamfer_h]);
}
if(!solid)
square([r - overlap, length]);
}
}
polyhedron(points, middle_faces);
}
if(solid)
colour(1)
rotate(90)
cylinder(d = minor_d + eps, h = length);
if(female)
tube(or = r + (top < 0 || bot < 0 ? h : 0) + 2 * overlap, ir = r, h = length, center = false);
else
cylinder(d = dia, h = length);
}
}
module female_thread(pitch, outer_d, length, profile, taper_top = true, center = true) { //! Create female thread
turns = length / pitch + (taper_top ? 0 : 1);
r = outer_d / 2;
sides = r2sides(r);
h = max([for(p = profile) p.y]);
final = (turns - 1) * sides;
path = [for(i = [0 : sides * turns],
R = i < sides ? r + h - h * i / sides
: i > final && taper_top ? r + h * (i - final) / sides : r,
a = i * 360 / sides)
[R * sin(-a), R * cos(-a), pitch * a / 360]];
t = atan(pitch / sides / (r * cos(225 / sides)));
translate_z(center ? -length / 2 : 0) {
render() intersection() {
sweep(path, reverse([for(p = profile) [p.x, -p.y, 0]]), twist = t * sides * turns);
cylinder(d = outer_d + 5, h = length);
}
}
module male_metric_thread(d, pitch, length, center = true, top = -1, bot = -1, solid = true, colour = undef) { //! Create male thread with metric profile
H = pitch * sqrt(3) / 2;
h = 5 * H / 8;
minor_d = d - 2 * h;
thread(minor_d, pitch, length, thread_profile(h, pitch / 8, 60), center, top, bot, solid = solid, colour = colour);
}
module male_metric_thread(d, pitch, length, taper_top = true, center = true) { //! Create male thread with metric profile
h = sqrt(3) / 2 * pitch;
minor_d = d - 5 * h / 4;
male_thread(pitch, minor_d, length, thread_profile((d - minor_d) / 2, pitch / 8, 60), taper_top, center);
}
module female_metric_thread(d, pitch, length, taper_top = true, center = true) { //! Create male thread with metric profile
h = sqrt(3) / 2 * pitch;
outer_d = d + 5 * h / 4;
male_thread(pitch, outer_d, length, thread_profile((outer_d - d) / 2, pitch / 8, 60), taper_top, center);
module female_metric_thread(d, pitch, length, center = true, top = -1, bot = -1, colour = undef) { //! Create female thread with metric profile
H = pitch * sqrt(3) / 2;
h = 5 * H / 8;
thread(d, pitch, length, thread_profile(h, pitch / 4, 60), center, top, bot, solid = false, female = true, colour = colour);
}
function metric_coarse_pitch(d) //! Convert metric diameter to pitch
@ -106,9 +206,12 @@ function metric_coarse_pitch(d) //! Convert metric diameter to pitch
0,
0,
1.75, // M12
0,
0,
0,
0, // M14
0,
0,
0,
2.0, // M16
][d * 2 - 4];
male_metric_thread(3, 0.5, 25);
translate([10, 0])
male_metric_thread(8, 1.25, 30);

View File

@ -52,22 +52,25 @@ module d_pillar() { //! Draw a pillar for a D-connector
height = 4.5;
screw = 2.5;
screw_length = 8;
pitch = metric_coarse_pitch(screw);
translate_z(-screw_length)
if(show_threads)
color(d_pillar_color * 0.7)
male_metric_thread(screw, metric_coarse_pitch(screw), screw_length, false, false);
male_metric_thread(screw, pitch, screw_length, false, top = 0, colour = d_pillar_color);
else
color(d_pillar_color)
cylinder(d = screw, h = screw_length + 1);
color(d_pillar_color)
color(d_pillar_color) {
linear_extrude(height = height)
difference() {
circle(r = rad, $fn = 6);
circle(d = screw);
}
}
if(show_threads)
female_metric_thread(screw, pitch, height, false, colour = d_pillar_color);
}
module d_plug(type, socket = false, pcb = false, idc = false) { //! Draw specified D plug, which can be IDC, PCB or plain solder bucket
hole_r = 3.05 / 2;

View File

@ -22,6 +22,7 @@
//
include <../core.scad>
use <../utils/quadrant.scad>
use <../utils/thread.scad>
function insert_length(type) = type[1]; //! Length
function insert_outer_d(type) = type[2]; //! Outer diameter at the top
@ -45,9 +46,9 @@ module insert(type) { //! Draw specified insert
vitamin(str("insert(", type[0], "): Heatfit insert M", insert_screw_diameter(type)));
$fn = 64;
explode(20, offset =[0, 0, -5]) color(brass) translate_z(eps) {
vflip(){
r1 = insert_screw_diameter(type) / 2;
thread_d = insert_screw_diameter(type);
explode(20, offset =[0, 0, -5]) translate_z(eps) vflip() {
r1 = thread_d / 2;
r2 = insert_barrel_d(type) / 2;
r3 = insert_ring3_d(type) / 2;
r4 = insert_ring2_d(type) / 2;
@ -56,6 +57,7 @@ module insert(type) { //! Draw specified insert
h2 = ring1_h + gap;
h3 = ring1_h + gap + ring2_h;
h4 = ring1_h + gap + ring2_h + gap;
color(brass)
rotate_extrude()
polygon([
[r1, 0],
@ -72,7 +74,9 @@ module insert(type) { //! Draw specified insert
[r5, h1],
[r5, 0],
]);
}
if(show_threads)
female_metric_thread(thread_d, metric_coarse_pitch(thread_d), length, center = false, colour = brass);
}
}

View File

@ -21,7 +21,8 @@
//! Nuts for leadscrews.
//
include <../core.scad>
include <../utils/tube.scad>
use <../utils/tube.scad>
use <../utils/thread.scad>
function leadnut_bore(type) = type[2]; //! Thread size
function leadnut_od(type) = type[3]; //! Outer diameter of the shank
@ -33,6 +34,8 @@ function leadnut_holes(type) = type[8]; //! The number of screw hole
function leadnut_hole_dia(type) = type[9]; //! The diameter of the screw holes
function leadnut_hole_pitch(type) = type[10]; //! The radia pitch of the screw holes
function leadnut_screw(type) = type[11]; //! The type of the fixing screws
function leadnut_pitch(type) = type[12]; //! Screw pitch
function leadnut_lead(type) = type[13]; //! Screw lead
function leadnut_shank(type) = leadnut_height(type) - leadnut_flange_t(type) - leadnut_flange_offset(type); //! The length of the shank below the flange
@ -47,11 +50,18 @@ module leadnut_screw_positions(type) { //! Position children at the screw holes
module leadnut(type) { //! Draw specified leadnut
vitamin(str("leadnut(", type[0], "): ", type[1]));
bore_r = (leadnut_bore(type) + 0.5) / 2;
bore_d = leadnut_bore(type);
bore_r = bore_d / 2;
h = leadnut_height(type);
pitch = leadnut_pitch(type);
lead = leadnut_lead(type);
color("dimgrey") vflip()
translate_z(-leadnut_flange_offset(type) - leadnut_flange_t(type)) {
tube(or = leadnut_od(type) / 2, ir = bore_r, h = leadnut_height(type), center = false);
tube(or = leadnut_od(type) / 2, ir = bore_r, h = h, center = false);
if(show_threads)
thread(bore_d, lead, h, thread_profile(pitch / 2, pitch * 0.366, 30), false, starts = lead / pitch, female = true, solid = false);
translate_z(leadnut_flange_offset(type))
linear_extrude(height = leadnut_flange_t(type))

View File

@ -17,8 +17,8 @@
// If not, see <https://www.gnu.org/licenses/>.
//
LSN8x2 = ["LSN8x2", "Leadscrew nut 8 x 2", 8, 10.2, 15, 22, 3.5, 1.5, 4, 3.5, 8, M3_cap_screw];
LSN8x8 = ["LSN8x8", "Leadscrew nut 8 x 8 RobotDigg",8, 12.75,19, 25.4, 4.1, 0, 3, 3.5, 19.05/2, M3_cap_screw];
LSN8x2 = ["LSN8x2", "Leadscrew nut 8 x 2", 8, 10.2, 15, 22, 3.5, 1.5, 4, 3.5, 8, M3_cap_screw, 2, 2];
LSN8x8 = ["LSN8x8", "Leadscrew nut 8 x 8 RobotDigg",8, 12.75,19, 25.4, 4.1, 0, 3, 3.5, 19.05/2, M3_cap_screw, 2, 8];
leadnuts = [LSN8x2, LSN8x8];

View File

@ -26,6 +26,8 @@ include <../core.scad>
use <washer.scad>
use <screw.scad>
use <../utils/rounded_cylinder.scad>
use <../utils/thread.scad>
use <../utils/tube.scad>
brass_colour = brass;
function nut_size(type) = type[1]; //! Diameter of the corresponding screw
@ -37,7 +39,8 @@ function nut_trap_depth(type) = type[6]; //! Depth of nut trap
function nut_flat_radius(type) = nut_radius(type) * cos(30); //! Radius across the flats
module nut(type, nyloc = false, brass = false, nylon = false) { //! Draw specified nut
hole_rad = nut_size(type) / 2;
thread_d = nut_size(type);
hole_rad = thread_d / 2;
outer_rad = nut_radius(type);
thickness = nut_thickness(type);
nyloc_thickness = nut_thickness(type, true);
@ -45,18 +48,29 @@ module nut(type, nyloc = false, brass = false, nylon = false) { //! Draw specifi
vitamin(str("nut(", type[0], arg(nyloc, false, "nyloc"), arg(brass, false, "brass"), arg(nylon, false, "nylon"),
"): Nut M", nut_size(type), " x ", thickness, "mm ", desc));
explode(nyloc ? 10 : 0)
color(brass ? brass_colour : nylon ? grey30: grey70) {
colour = brass ? brass_colour : nylon ? grey30: grey70;
explode(nyloc ? 10 : 0) {
color(colour) {
linear_extrude(height = thickness)
difference() {
circle(outer_rad, $fn = 6);
circle(hole_rad);
}
if(nyloc)
translate_z(-eps)
rounded_cylinder(r = outer_rad * cos(30) , h = nyloc_thickness, r2 = (nyloc_thickness - thickness) / 2, ir = hole_rad);
}
if(show_threads)
female_metric_thread(thread_d, metric_coarse_pitch(thread_d), thickness, center = false, colour = colour);
if(nyloc)
translate_z(thickness)
color("royalblue")
tube(or = thread_d / 2 + eps, ir = (thread_d * 0.8) / 2, h = (nyloc_thickness - thickness) * 0.8, center = false);
}
if($children)
translate_z(nut_thickness(type, nyloc))
children();
@ -73,7 +87,8 @@ module nut_and_washer(type, nyloc) { //! Draw nut with corresponding washer
}
module wingnut(type) { //! Draw a wingnut
hole_rad = nut_size(type) / 2;
thread_d = nut_size(type);
hole_rad = thread_d / 2;
bottom_rad = nut_radius(type);
top_rad = type[4] / 2;
thickness = nut_thickness(type);
@ -87,7 +102,9 @@ module wingnut(type) { //! Draw a wingnut
vitamin(str("wingnut(", type[0], "): Wingnut M", nut_size(type)));
explode(10) color(grey70) {
colour = silver;
explode(10) {
color(colour) {
rotate_extrude()
polygon([
[hole_rad, 0],
@ -107,6 +124,10 @@ module wingnut(type) { //! Draw a wingnut
]);
}
}
if(show_threads)
female_metric_thread(thread_d, metric_coarse_pitch(thread_d), thickness, center = false, colour = colour);
}
}
function nut_trap_radius(nut, horizontal = false) = nut_radius(nut) + (horizontal ? layer_height / 4 : 0); //! Radius across the corners of a nut trap
function nut_trap_flat_radius(nut, horizontal = false) = nut_trap_radius(nut, horizontal) * cos(30); //! Radius across the flats of a nut trap

View File

@ -23,6 +23,7 @@
//! A permanent magnet that can be magnatized and de-magnatized electronically.
//
include <../core.scad>
use <../utils/thread.scad>
pitch = 33.8;
width = 40;
@ -64,15 +65,20 @@ module opengrab() { //! Draw OpenGrab module
translate_z(depth - pillar - pcb / 2)
cube([width, width, pcb], center = true);
color(brass)
translate_z(1)
opengrab_hole_positions()
opengrab_hole_positions() {
color(brass)
linear_extrude(height = depth - 1)
difference() {
circle(d = 4.7 / cos(30), $fn = 6);
circle(r = 3/2);
}
if(show_threads)
female_metric_thread(3, metric_coarse_pitch(3), depth - 1, center = false, colour = brass);
}
}
module opengrab_target() { //! Draw OpenGrab target

View File

@ -44,26 +44,26 @@ module pillar(type) { //! Draw specified pillar
thread_d = pillar_thread(type);
bot_thread_l = pillar_bot_thread(type);
top_thread_l = pillar_top_thread(type);
thread_colour = pillar_i_colour(type) * (show_threads ? 0.7 : 1);
thread_colour = pillar_i_colour(type);
pitch = metric_coarse_pitch(thread_d);
vitamin(str("pillar(", type[0], "): Pillar ", pillar_name(type), " ", sex, " M", thread_d, "x", height));
color(thread_colour) {
if(bot_thread_l > 0)
translate_z(-bot_thread_l + eps)
if(show_threads)
male_metric_thread(thread_d, metric_coarse_pitch(thread_d), bot_thread_l, false, false);
male_metric_thread(thread_d, pitch, bot_thread_l, false, top = 0, colour = thread_colour);
else
color(thread_colour)
cylinder(h = bot_thread_l, d = thread_d);
if(top_thread_l > 0)
translate_z(height + top_thread_l - eps)
translate_z(height - eps)
if(show_threads)
vflip()
male_metric_thread(thread_d, metric_coarse_pitch(thread_d), top_thread_l, false, false);
male_metric_thread(thread_d, pitch, top_thread_l, false, bot = 0, colour = thread_colour);
else
color(thread_colour)
cylinder(h = top_thread_l, d = thread_d);
}
color(pillar_i_colour(type)) {
linear_extrude(height = height)
@ -79,6 +79,16 @@ module pillar(type) { //! Draw specified pillar
cylinder(h = top - bot, d = thread_d + eps);
}
if(show_threads) {
if(top_thread_l < 0)
translate_z(height)
vflip()
female_metric_thread(thread_d, pitch, -top_thread_l, false, colour = thread_colour);
if(bot_thread_l < 0)
female_metric_thread(thread_d, pitch, -bot_thread_l, false, colour = thread_colour);
}
if(pillar_od(type) > pillar_id(type))
color(pillar_o_colour(type)) linear_extrude(height = height)
difference() {

View File

@ -25,12 +25,14 @@ use <../utils/thread.scad>
rod_colour = grey80;
studding_colour = grey70;
leadscrew_colour = grey70;
module rod(d , l) { //! Draw a smooth rod with specified length and diameter
module rod(d , l, center = true) { //! Draw a smooth rod with specified diameter and length
vitamin(str("rod(", d, ", ", l, "): Smooth rod ", d, "mm x ", l, "mm"));
chamfer = d / 10;
color(rod_colour)
translate_z(center ? 0 : l / 2)
hull() {
cylinder(d = d, h = l - 2 * chamfer, center = true);
@ -38,15 +40,35 @@ module rod(d , l) { //! Draw a smooth rod with specified length and diameter
}
}
module studding(d , l) { //! Draw a threaded rod with specified length and diameter
module studding(d , l, center = true) { //! Draw a threaded rod with specified diameter and length
vitamin(str("studding(", d, ", ", l,"): Threaded rod M", d, " x ", l, "mm"));
chamfer = d / 20;
pitch = metric_coarse_pitch(d);
color(studding_colour)
translate_z(center ? 0 : l / 2)
if(show_threads && pitch)
male_metric_thread(d, pitch, l);
male_metric_thread(d, pitch, l, colour = rod_colour);
else
color(studding_colour)
hull() {
cylinder(d = d, h = l - 2 * chamfer, center = true);
cylinder(d = d - 2 * chamfer, h = l, center = true);
}
}
module leadscrew(d , l, lead, starts, center = true) { //! Draw a leadscrew with specified diameter, length, lead and number of starts
vitamin(str("leadscrew(", d, ", ", l, ", ", lead, ", ", starts, "): Leadscrew ", d, " x ", l, "mm, ", lead, "mm lead, ", starts, " starts"));
pitch = lead / starts;
chamfer = pitch / 2;
translate_z(center ? 0 : l / 2)
if(show_threads && pitch)
thread(d - pitch, lead, l, thread_profile(pitch / 2, pitch * 0.366, 30), top = 45, bot = 45, starts = starts, center = center, colour = rod_colour);
else
color(leadscrew_colour)
hull() {
cylinder(d = d, h = l - 2 * chamfer, center = true);

View File

@ -73,34 +73,33 @@ module screw(type, length, hob_point = 0, nylon = false) { //! Draw specified sc
thread = max_thread ? length >= max_thread + 5 ? max_thread
: length
: length;
shank = length - thread;
colour = nylon || head_type == hs_grub ? grey40 : grey80;
module shaft(headless = 0) {
point = screw_nut(type) ? 0 : 3 * rad;
d = 2 * screw_radius(type);
pitch = metric_coarse_pitch(d);
l = length - shank;
colour = nylon || head_type == hs_grub ? grey40 : grey80;
module shaft(socket = 0, headless = false) {
point = screw_nut(type) ? 0 : 3 * rad;
shank = length - thread - socket;
if(show_threads && !point && pitch)
translate_z(-l - shank)
color(colour * 0.7)
male_metric_thread(d, pitch, l, !!headless, false);
translate_z(-length)
male_metric_thread(d, pitch, thread - (shank > 0 || headless ? 0 : socket), false, top = headless ? -1 : 0, solid = !headless, colour = colour);
else
color(colour * 0.9)
rotate_extrude() {
translate([0, -length + point])
square([rad, length - headless - point]);
square([rad, length - socket - point]);
if(point)
polygon([
[0, -length], [0, point - length], [rad - 0.1, point - length]
[0.4, -length], [0, point - length], [rad, point - length]
]);
}
if(shank - headless > 0)
color(colour)
translate_z(-shank)
cylinder(r = rad + eps, h = shank - headless);
if(shank > 0)
color(colour)
translate_z(-shank - socket)
cylinder(r = rad + eps, h = shank);
}
explode(length + 10) {
@ -121,23 +120,20 @@ module screw(type, length, hob_point = 0, nylon = false) { //! Draw specified sc
}
if(head_type == hs_grub) {
color(colour) {
if(!show_threads) {
r = show_threads ? rad - pitch / 2 : rad;
translate_z(-socket_depth)
linear_extrude(height = socket_depth)
difference() {
circle(r = rad);
circle(r);
circle(socket_rad, $fn = 6);
}
shaft(socket_depth);
}
else
render() difference() {
shaft(socket_depth);
shaft(socket_depth, true);
cylinder(r = socket_rad, $fn = 6, h = 2 * socket_depth, center = true);
}
if(show_threads)
translate_z(-length)
cylinder(r = r, h = length - socket_depth);
}
}
if(head_type == hs_hex) {

View File

@ -26,6 +26,7 @@ include <screws.scad>
use <washer.scad>
include <ring_terminals.scad>
use <../utils/tube.scad>
use <rod.scad>
function NEMA_width(type) = type[1]; //! Width of the square face
function NEMA_length(type) = type[2]; //! Body length
@ -34,7 +35,7 @@ function NEMA_body_radius(type) = type[4]; //! Body radius
function NEMA_boss_radius(type) = type[5]; //! Boss around the spindle radius
function NEMA_boss_height(type) = type[6]; //! Boss height
function NEMA_shaft_dia(type) = type[7]; //! Shaft diameter
function NEMA_shaft_length(type)= type[8]; //! Shaft length above the face
function NEMA_shaft_length(type)= type[8]; //! Shaft length above the face, if a list then a leadscrew: length, lead, starts
function NEMA_hole_pitch(type) = type[9]; //! Screw hole pitch
function NEMA_holes(type) = [-NEMA_hole_pitch(type) / 2, NEMA_hole_pitch(type) / 2]; //! Screw positions for for loop
function NEMA_big_hole(type) = NEMA_boss_radius(type) + 0.2; //! Clearance hole for the big boss
@ -50,7 +51,7 @@ module NEMA_outline(type) //! 2D outline
circle(NEMA_radius(type));
}
module NEMA(type) { //! Draw specified NEMA stepper motor
module NEMA(type, shaft_angle = 0) { //! Draw specified NEMA stepper motor
side = NEMA_width(type);
length = NEMA_length(type);
body_rad = NEMA_body_radius(type);
@ -88,9 +89,15 @@ module NEMA(type) { //! Draw specified NEMA stepper motor
}
}
color(NEMA_shaft_length(type) > 50 ? "silver" : stepper_cap_colour)
shaft = NEMA_shaft_length(type);
translate_z(-5)
cylinder(r = shaft_rad, h = NEMA_shaft_length(type) + 5); // shaft
rotate(shaft_angle)
if(!is_list(shaft))
color(stepper_cap_colour)
cylinder(r = shaft_rad, h = shaft + 5); // shaft
else
not_on_bom()
leadscrew(shaft_rad * 2, shaft.x + 5, shaft.y, shaft.z, center = false)
translate([0, side / 2, -length + cap / 2])
rotate([90, 0, 0])

View File

@ -25,7 +25,7 @@
// side, length, radius, radius, radius, depth, shaft, length, holes
NEMA17 = ["NEMA17", 42.3, 47, 53.6/2, 25, 11, 2, 5, 24, 31 ];
NEMA17M = ["NEMA17M", 42.3, 40, 53.6/2, 25, 11, 2, 5, 20, 31 ];
NEMA17M8= ["NEMA17M8", 42.3, 40, 53.6/2, 25, 11, 2, 8, 280, 31 ];
NEMA17M8= ["NEMA17M8", 42.3, 40, 53.6/2, 25, 11, 2, 8, [280, 8, 4], 31 ];
NEMA17S = ["NEMA17S", 42.3, 34, 53.6/2, 25, 11, 2, 5, 24, 31 ];
NEMA16 = ["NEMA16", 39.5, 19.2, 50.6/2, 50.6/2, 11, 2, 5, 12, 31 ];
NEMA14 = ["NEMA14", 35.2, 36, 46.4/2, 21, 11, 2, 5, 21, 26 ];

View File

@ -23,6 +23,8 @@
include <../core.scad>
use <nut.scad>
use <washer.scad>
use <../utils/thread.scad>
use <../utils/tube.scad>
function toggle_part(type) = type[1]; //! Part description
function toggle_width(type) = type[2]; //! Body width
@ -80,16 +82,18 @@ module toggle(type, thickness) { //! Draw specified toggle switch with the nuts
translate_z(-h1 / 2 - t)
cube([toggle_width(type), toggle_height(type), h1], center = true);
color("silver") {
if(toggle_collar_t(type))
cylinder(d = toggle_collar_d(type), h = toggle_collar_t(type));
if(show_threads) {
d = toggle_od(type);
pitch = inch(1/40);
h = 5 * pitch * sqrt(3) / 16;
translate_z(-t / 2)
cube([toggle_width(type), toggle_height(type), t], center = true);
translate_z(-h2 / 2)
cube([toggle_width(type) + 2 * eps, toggle_height(type) - 2 * inset, h2], center = true);
male_metric_thread(d, pitch, thread, center = false, solid = false, colour = silver);
color(silver)
tube(or = d / 2 - h, ir = toggle_id(type) / 2, h = thread, center = false);
}
else
color(silver)
rotate_extrude()
difference() {
hull() {
@ -100,6 +104,17 @@ module toggle(type, thickness) { //! Draw specified toggle switch with the nuts
square([toggle_id(type) / 2, thread + 1]);
}
color(silver) {
if(toggle_collar_t(type))
cylinder(d = toggle_collar_d(type), h = toggle_collar_t(type));
translate_z(-t / 2)
cube([toggle_width(type), toggle_height(type), t], center = true);
translate_z(-h2 / 2)
cube([toggle_width(type) + 2 * eps, toggle_height(type) - 2 * inset, h2], center = true);
translate_z(toggle_pivot(type)) {
angle = toggle_angle(type);
l1 = toggle_paddle_l(type);

View File

@ -103,12 +103,14 @@ module spring_washer(type) { //! Draw spring version of washer
vitamin(str("spring_washer(", type[0], "_washer): Washer spring M", hole, " x ", thickness, "mm"));
ir = washer_id(type) / 2;
or = diameter / 2;
path = circle_points((ir + or) / 2, exploded() ? thickness / 2 : 0);
pitch = exploded() ? thickness / 2 : 0;
path = circle_points((ir + or) / 2, pitch);
profile = rectangle_points(thickness, or - ir);
len = len(path);
color(hard_washer_colour)
translate_z(thickness / 2)
rotate(180)
sweep(path, profile);
rotate(-90)
sweep(path, profile, twist = -helical_twist_per_segment(ir, pitch, len) * (len - 1));
if($children)
translate_z(thickness)