Publish all missing boilerplate requirements for publication

This commit is contained in:
Matt McWilliams 2025-02-11 19:24:21 -05:00
parent 935c90baef
commit c3c89d8d6e
15 changed files with 241346 additions and 225169 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "scad/common"]
path = scad/common
url = https://git.sixteenmillimeter.com/modules/common.git

19
LICENSE Normal file
View File

@ -0,0 +1,19 @@
Copyright (c) 2025 M McWilliams
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

25
README.md Normal file
View File

@ -0,0 +1,25 @@
# Paterson 16mm Reel
This is a 16mm gauge processing reel that fits in a [Paterson Super System](https://patersonphotographic.com/product/paterson-super-system-4-developing-tanks/) tank.
The reel can hold about 8ft or 2.4m of 16mm gauge film, roughly 320 frames.
Four of these can fit in a standard 500ml, 2 reel tank.
Notches at the top of the spiral register the top of the reel to prevent spinning while loading.
The reel will friction fit onto the center funnel of the tank.
-----
## Reel Spiral
![Paterson 16mm Reel Spiral](./img/paterson16_reel_spiral.jpg)
[Download STL](./stl/paterson16_reel_spiral.stl)
-----
## Reel Top
![Paterson 16mm Reel Spiral](./img/paterson16_reel_top.jpg)
[Download STL](./stl/paterson16_reel_top.stl)

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
img/paterson16_reel_top.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

50
meta.json Normal file
View File

@ -0,0 +1,50 @@
{
"camera" : {
"x" : 0,
"y" : 0,
"z" : 0
},
"objects" : [
{
"name" : "paterson16_reel_top",
"material" : "white_plastic",
"position" : {
"x" : 0,
"y" : 0,
"z" : -20
},
"rotation" : {
"x" : 180,
"y" : 0,
"z" : 0
}
},
{
"name" : "paterson16_reel_spiral",
"material" : "white_plastic",
"position" : {
"x" : 0,
"y" : 0,
"z" : 20
},
"rotation" : {
"x" : 0,
"y" : 0,
"z" : 0
}
}
],
"src" : [
"README.md",
"LICENSE",
"scad.sh",
"scad/paterson16.scad",
"scad/gnal_v3.scad",
"scad/common/common.scad",
"scad/common/path_extrude.scad",
"scad/common/threads.scad",
"scad/common/Triangles.scad",
"scad/common/c14n_stl.py",
"scad/common/scad.sh"
]
}

3
scad.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
bash scad/common/scad.sh scad/paterson16.scad "${@}"

View File

@ -1,215 +0,0 @@
/*
Triangles.scad
Author: Tim Koopman
https://github.com/tkoopman/Delta-Diamond/blob/master/OpenSCAD/Triangles.scad
angleCA
/|\
a / H \ c
/ | \
angleAB ------- angleBC
b
Standard Parameters
center: true/false
If true same as centerXYZ = [true, true, true]
centerXYZ: Vector of 3 true/false values [CenterX, CenterY, CenterZ]
center must be left undef
height: The 3D height of the Triangle. Ignored if heights defined
heights: Vector of 3 height values heights @ [angleAB, angleBC, angleCA]
If CenterZ is true each height will be centered individually, this means
the shape will be different depending on CenterZ. Most times you will want
CenterZ to be true to get the shape most people want.
*/
/*
Triangle
a: Length of side a
b: Length of side b
angle: angle at point angleAB
*/
module Triangle(
a, b, angle, height=1, heights=undef,
center=undef, centerXYZ=[false,false,false])
{
// Calculate Heights at each point
heightAB = ((heights==undef) ? height : heights[0])/2;
heightBC = ((heights==undef) ? height : heights[1])/2;
heightCA = ((heights==undef) ? height : heights[2])/2;
centerZ = (center || (center==undef && centerXYZ[2]))?0:max(heightAB,heightBC,heightCA);
// Calculate Offsets for centering
offsetX = (center || (center==undef && centerXYZ[0]))?((cos(angle)*a)+b)/3:0;
offsetY = (center || (center==undef && centerXYZ[1]))?(sin(angle)*a)/3:0;
pointAB1 = [-offsetX,-offsetY, centerZ-heightAB];
pointAB2 = [-offsetX,-offsetY, centerZ+heightAB];
pointBC1 = [b-offsetX,-offsetY, centerZ-heightBC];
pointBC2 = [b-offsetX,-offsetY, centerZ+heightBC];
pointCA1 = [(cos(angle)*a)-offsetX,(sin(angle)*a)-offsetY, centerZ-heightCA];
pointCA2 = [(cos(angle)*a)-offsetX,(sin(angle)*a)-offsetY, centerZ+heightCA];
polyhedron(
points=[ pointAB1, pointBC1, pointCA1,
pointAB2, pointBC2, pointCA2 ],
faces=[
[0, 1, 2],
[3, 5, 4],
[0, 3, 1],
[1, 3, 4],
[1, 4, 2],
[2, 4, 5],
[2, 5, 0],
[0, 5, 3] ] );
}
/*
Isosceles Triangle
Exactly 2 of the following paramaters must be defined.
If all 3 defined H will be ignored.
b: length of side b
angle: angle at points angleAB & angleBC.
*/
module Isosceles_Triangle(
b, angle, H=undef, height=1, heights=undef,
center=undef, centerXYZ=[true, false, false])
{
valid = (angle!=undef)?((angle < 90) && (b!=undef||H!=undef)) : (b!=undef&&H!=undef);
ANGLE = (angle!=undef) ? angle : atan(H / (b/2));
a = (b==undef)?(H/sin((180-(angle*2))/2)) :
(b / cos(ANGLE))/2;
B = (b==undef)? (cos(angle)*a)*2:b;
if (valid)
{
Triangle(a=a, b=B, angle=ANGLE, height=height, heights=heights,
center=center, centerXYZ=centerXYZ);
} else {
echo("Invalid Isosceles_Triangle. Must specify any 2 of b, angle and H, and if angle used angle must be less than 90");
}
}
/*
Right Angled Triangle
Create a Right Angled Triangle where the hypotenuse will be calculated.
|\
a| \
| \
----
b
a: length of side a
b: length of side b
*/
module Right_Angled_Triangle(
a, b, height=1, heights=undef,
center=undef, centerXYZ=[false, false, false])
{
Triangle(a=a, b=b, angle=90, height=height, heights=heights,
center=center, centerXYZ=centerXYZ);
}
/*
Wedge
Is same as Right Angled Triangle with 2 different heights, and rotated.
Good for creating support structures.
*/
module Wedge(a, b, w1, w2)
{
rotate([90,0,0])
Right_Angled_Triangle(a, b, heights=[w1, w2, w1], centerXYZ=[false, false, true]);
}
/*
Equilateral Triangle
Create a Equilateral Triangle.
l: Length of all sides (a, b & c)
H: Triangle size will be based on the this 2D height
When using H, l is ignored.
*/
module Equilateral_Triangle(
l=10, H=undef, height=1, heights=undef,
center=undef, centerXYZ=[true,false,false])
{
L = (H==undef)?l:H/sin(60);
Triangle(a=L,b=L,angle=60,height=height, heights=heights,
center=center, centerXYZ=centerXYZ);
}
/*
Trapezoid
Create a Basic Trapezoid (Based on Isosceles_Triangle)
d
/----\
/ | \
a / H \ c
/ | \
angle ------------ angle
b
b: Length of side b
angle: Angle at points angleAB & angleBC
H: The 2D height at which the triangle should be cut to create the trapezoid
heights: If vector of size 3 (Standard for triangles) both cd & da will be the same height, if vector have 4 values [ab,bc,cd,da] than each point can have different heights.
*/
module Trapezoid(
b, angle=60, H, height=1, heights=undef,
center=undef, centerXYZ=[true,false,false])
{
validAngle = (angle < 90);
adX = H / tan(angle);
// Calculate Heights at each point
heightAB = ((heights==undef) ? height : heights[0])/2;
heightBC = ((heights==undef) ? height : heights[1])/2;
heightCD = ((heights==undef) ? height : heights[2])/2;
heightDA = ((heights==undef) ? height : ((len(heights) > 3)?heights[3]:heights[2]))/2;
// Centers
centerX = (center || (center==undef && centerXYZ[0]))?0:b/2;
centerY = (center || (center==undef && centerXYZ[1]))?0:H/2;
centerZ = (center || (center==undef && centerXYZ[2]))?0:max(heightAB,heightBC,heightCD,heightDA);
// Points
y = H/2;
bx = b/2;
dx = (b-(adX*2))/2;
pointAB1 = [centerX-bx, centerY-y, centerZ-heightAB];
pointAB2 = [centerX-bx, centerY-y, centerZ+heightAB];
pointBC1 = [centerX+bx, centerY-y, centerZ-heightBC];
pointBC2 = [centerX+bx, centerY-y, centerZ+heightBC];
pointCD1 = [centerX+dx, centerY+y, centerZ-heightCD];
pointCD2 = [centerX+dx, centerY+y, centerZ+heightCD];
pointDA1 = [centerX-dx, centerY+y, centerZ-heightDA];
pointDA2 = [centerX-dx, centerY+y, centerZ+heightDA];
validH = (adX < b/2);
if (validAngle && validH)
{
polyhedron(
points=[ pointAB1, pointBC1, pointCD1, pointDA1,
pointAB2, pointBC2, pointCD2, pointDA2 ],
triangles=[
[0, 1, 2],
[0, 2, 3],
[4, 6, 5],
[4, 7, 6],
[0, 4, 1],
[1, 4, 5],
[1, 5, 2],
[2, 5, 6],
[2, 6, 3],
[3, 6, 7],
[3, 7, 0],
[0, 7, 4] ] );
} else {
if (!validAngle) echo("Trapezoid invalid, angle must be less than 90");
else echo("Trapezoid invalid, H is larger than triangle");
}
}

1
scad/common Submodule

@ -0,0 +1 @@
Subproject commit e2eeb27f173d739a174c0d147bcb62a16859e2d9

View File

@ -1,8 +1,8 @@
//GNAL v3 Shared Library
include <./path_extrude.scad>;
include <./threads.scad>;
include <./Triangles.scad>;
use <./common/path_extrude.scad>;
use <./common/threads.scad>;
use <./common/Triangles.scad>;
/**
* THREADS

View File

@ -1,4 +1,5 @@
include <./gnal_v3.scad>;
use <./common/common.scad>;
use <./gnal_v3.scad>;
$fn = 250;
@ -18,31 +19,31 @@ ROTATIONS = 14;
module notches (pos = [0, 0, 0], pad = 0) {
translate(pos) {
intersection () {
cylinder(r = (29.5 + pad) / 2, h = 6, center = true);
cylinder(r = R(29.5 + pad), h = 6, center = true);
rotate([0, 45, 0]) cube([3, 31, 3], center = true);
}
}
}
module reel_frame () {
module reel_spiral () {
difference () {
union () {
//outer wall
translate([0, 0, REEL_OUTER_WALL_H / 2]) difference () {
cylinder(r = REEL_D / 2, h = REEL_OUTER_WALL_H, center = true);
cylinder(r = (REEL_D / 2) - REEL_OUTER_WALL_W, h = REEL_OUTER_WALL_H + 1, center = true);
cylinder(r = R(REEL_D), h = REEL_OUTER_WALL_H, center = true);
cylinder(r = R(REEL_D) - REEL_OUTER_WALL_W, h = REEL_OUTER_WALL_H + 1, center = true);
}
//inner wall
translate([0, 0, REEL_INNER_H / 2]) difference () {
cylinder(r = REEL_INNER_D / 2, h = REEL_INNER_H, center = true);
cylinder(r = (REEL_INNER_D / 2) - REEL_INNER_WALL_W, h = REEL_INNER_H + 1, center = true);
cylinder(r = R(REEL_INNER_D), h = REEL_INNER_H, center = true);
cylinder(r = R(REEL_INNER_D) - REEL_INNER_WALL_W, h = REEL_INNER_H + 1, center = true);
translate([14, 0, 0]) rotate([0, 0, 45]) cube([10, 0.3, 20], center = true);
}
//top notches
translate([0, 0, 19.4]) difference () {
notches(pad = -0.4);
cylinder(r = (REEL_INNER_D / 2) - REEL_INNER_WALL_W, h = 6 + 1, center = true);
cylinder(r = R(REEL_INNER_D) - REEL_INNER_WALL_W, h = 6 + 1, center = true);
}
//spokes
@ -54,6 +55,7 @@ module reel_frame () {
}
}
//
translate([0, 0, 4]) rotate([0, 0, 8]) spiral(rotations = ROTATIONS, fn = $fn, start_d = 29.5, bottom = -4);
}
//bottom notches
@ -66,19 +68,19 @@ module reel_top () {
union () {
//outer wall
translate([0, 0, SPOKE_H / 2]) difference () {
cylinder(r = REEL_D / 2, h = SPOKE_H, center = true);
cylinder(r = (REEL_D / 2) - REEL_OUTER_WALL_W, h = REEL_OUTER_WALL_H + 1, center = true);
cylinder(r = R(REEL_D), h = SPOKE_H, center = true);
cylinder(r = R(REEL_D) - REEL_OUTER_WALL_W, h = REEL_OUTER_WALL_H + 1, center = true);
}
//inner wall
translate([0, 0, SPOKE_H / 2]) difference () {
cylinder(r = REEL_INNER_D / 2, h = SPOKE_H, center = true);
cylinder(r = (REEL_INNER_D / 2) - REEL_INNER_WALL_W, h = REEL_INNER_H + 1, center = true);
cylinder(r = R(REEL_INNER_D), h = SPOKE_H, center = true);
cylinder(r = R(REEL_INNER_D) - REEL_INNER_WALL_W, h = REEL_INNER_H + 1, center = true);
}
translate([0, 0, SPOKE_H / 2]) difference () {
cylinder(r = (REEL_INNER_D + REEL_D) / 4, h = SPOKE_H, center = true);
cylinder(r = ((REEL_INNER_D + REEL_D) / 4) - REEL_INNER_WALL_W, h = REEL_INNER_H + 1, center = true);
cylinder(r = R((REEL_INNER_D + REEL_D) / 2), h = SPOKE_H, center = true);
cylinder(r = R((REEL_INNER_D + REEL_D) / 2) - REEL_INNER_WALL_W, h = REEL_INNER_H + 1, center = true);
}
//spokes
@ -91,7 +93,7 @@ module reel_top () {
}
}
intersection () {
cylinder(r = 30.5 / 2, h = 6, center = true);
cylinder(r = R(30.5), h = 6, center = true);
rotate([0, 45, 0]) cube([3, 31, 3], center = true);
}
}
@ -100,8 +102,8 @@ module reel_top () {
PART = "reel_frame";
if (PART == "reel_frame") {
reel_frame();
if (PART == "reel_spiral") {
reel_spiral();
} else if (PART == "reel_top") {
rotate([180, 0, 0]) reel_top();
}

View File

@ -1,195 +0,0 @@
// path_extrude.scad -- Extrude a path in 3D space
// usage: add "use <path_extrude.scad>;" to the top of your OpenSCAD source code
// Copyright (C) 2014-2019 David Eccles (gringer) <bioinformatics@gringene.org>
// This program 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.
// This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
// Determine the projection of a point on a plane centered at c1 with normal n1
function project(p, c, n) =
p - (n * (p - c)) * n / (n * n);
// determine angle between two points with a given normal orientation
// see https://stackoverflow.com/questions/14066933/
// direct-way-of-computing-clockwise-angle-between-2-vectors
// dot = p1 * p2;
// det = (p1[0]*p2[1]*n1[2] + p2[0]*n1[1]*p1[2] + n1[0]*p1[1]*p2[2]) -
// (n1[0]*p2[1]*p1[2] + p1[0]*n1[1]*p2[2] + p2[0]*p1[1]*n1[2]);
// atan2(det, dot);
// determine angle between two planar points and a centre
// with a given normal orientation
function getPlanarAngle(p1, p2, c1, n1) =
let(p1 = p1-c1, n1=n1 / norm(n1), p2=p2-c1)
atan2((p1[0]*p2[1]*n1[2] + p2[0]*n1[1]*p1[2] + n1[0]*p1[1]*p2[2]) -
(n1[0]*p2[1]*p1[2] + p1[0]*n1[1]*p2[2] + p2[0]*p1[1]*n1[2]), p1 * p2);
function c3D(tPoints) =
(len(tPoints[0]) == undef) ? // single point
c3D([tPoints])[0] :
(len(tPoints[0]) < 3) ? // collection of 2D points
tPoints * [[1,0,0],[0,1,0]] :
tPoints; // 3D points
// translate a point (or points)
function myTranslate(ofs, points, acc = []) =
(len(points[0]) == undef) ?
myTranslate(ofs, [points])[0] :
[ for(i = [0:(len(points) - 1)])
[ for(d = [0:(len(points[0])-1)]) (ofs[d] + points[i][d])]];
// rotate a point (or points)
function myRotate(rotVec, points) =
let(rotX = [[1, 0, 0],
[0, cos(rotVec[0]), -sin(rotVec[0])],
[0, sin(rotVec[0]), cos(rotVec[0])]],
rotY = [[ cos(rotVec[1]), 0,-sin(rotVec[1])],
[ 0, 1, 0],
[ sin(rotVec[1]), 0, cos(rotVec[1])]],
rotZ = [[ cos(rotVec[2]), sin(rotVec[2]), 0],
[ sin(rotVec[2]), -cos(rotVec[2]), 0],
[0, 0, 1]])
(len(points[0]) == undef) ?
myRotate(rotVec, [points])[0] :
c3D(points) * rotX * rotY * rotZ;
// Determine spherical rotation for cartesian coordinates
function rToS(pt) =
[-acos((pt[2]) / norm(pt)),
0,
-atan2(pt[0],pt[1])];
function calcPreRot(p1, p2, p3) =
let(n1=p2-p1, // normal between the two points (i.e. the plane that the polygon sits on)
n2=p3-p2,
rt1=rToS(n1),
rt2=rToS(n2),
pj1=(p2 + myRotate(rt2, [[1e42,0,0]])[0]),
pj2=project(p=(p1 + myRotate(rt1, [[1e42,0,0]])[0]), c=p2, n=n2))
getPlanarAngle(p1=pj1, p2=pj2, c1=p2, n1=n2);
function cumSum(x, res=[]) =
(len(x) == len(res)) ? concat([0], res) :
(len(res) == 0) ? cumSum(x=x, res=[x[0]]) :
cumSum(x=x, res=concat(res, [x[len(res)] + res[len(res)-1]]));
// Create extrusion side panels for one polygon segment as triangles.
// Note: panels are not necessarily be planar due to path twists
function makeSides(shs, pts, ofs=0) =
concat(
[for(i=[0:(shs-1)]) [i+ofs, ((i+1) % shs + ofs + shs) % (shs * pts),
(i+1) % shs + ofs]],
[for(i=[0:(shs-1)]) [((i+1) % shs + ofs + shs) % (shs * pts),
i+ofs, (i + ofs + shs) % (shs * pts)]]);
// Concatenate the contents of the outermost list
function flatten(A, acc = [], aDone = 0) =
(aDone >= len(A)) ? acc :
flatten(A, acc=concat(acc, A[aDone]), aDone = aDone + 1);
// Linearly interpolate between two shapes
function makeTween(shape1, shape2, t) =
(t == 0) ? shape1 :
(t == 1) ? shape2 :
[for (i=[0:(len(shape1)-1)])
(shape1[i]*(1-t) + shape2[i % len(shape2)]*(t))];
// Extrude a 2D shape through a 3D path
// Note: merge has two effects:
// 1) Removes end caps
// 2) Adjusts the rotation of each path point
// so that the end and start match up
module path_extrude(exPath, exShape, exShape2=[],
exRots = [0], exScale = [1], merge=false, preRotate=true){
exShapeTween = (len(exShape2) == 0) ?
exShape : exShape2;
shs = len(exShape); // shs: shape size
pts = len(exPath); // pts: path size
exPathX = (merge) ? concat(exPath, [exPath[0], exPath[1]]) :
concat(exPath,
[exPath[pts-1] + (exPath[pts-1] - exPath[pts-2]),
exPath[pts-1] + 2*(exPath[pts-1] - exPath[pts-2])]);
exScaleX = (len(exScale) == len(exPath)) ? exScale :
[for (i = [0:(pts-1)]) exScale[i % len(exScale)]];
preRots = [for(i = [0:(pts-1)])
preRotate ?
calcPreRot(p1=exPathX[i], // "current" point on the path
p2=exPathX[(i+1)], // "next" point on the path
p3=exPathX[(i+2)]) :
0 ];
cumPreRots = cumSum(preRots);
seDiff = cumPreRots[len(cumPreRots)-1]; // rotation difference (start - end)
// rotation adjustment to get start to look like end
seAdj = -seDiff / (len(cumPreRots));
adjPreRots = (!merge) ? cumPreRots :
[for(i = [0:(pts-1)]) (cumPreRots[i] + seAdj * i)];
adjExRots = (len(exRots) == 1) ?
[for(i = [0:(len(adjPreRots)-1)]) (adjPreRots[i] + exRots[0])] :
[for(i = [0:(len(adjPreRots)-1)]) (adjPreRots[i] + exRots[i % len(exRots)])];
phPoints = flatten([
for(i = [0:(pts-1)])
let(p1=exPathX[i],
p2=exPathX[(i+1)],
n1=p2-p1, // normal between the two points
rt1=rToS(n1))
myTranslate(p1, myRotate(rt1, myRotate([0,0,-adjExRots[i]],
c3D(makeTween(exShape, exShapeTween, i / (pts-1)) *
exScaleX[i]))))
]);
if(merge){ // just the surface, no end caps
polyhedron(points=phPoints,
faces=flatten([
for(i = [0:(pts-1)])
makeSides(shs, pts, ofs=shs*i)
])
);
} else {
polyhedron(points=phPoints,
faces=concat(
flatten([
for(i = [0:(pts-2)])
makeSides(shs, pts, ofs=shs*i)
]),
concat( // add in start / end covers
[[for(i= [0:(shs-1)]) i]],
[[for(i= [(len(phPoints)-1):-1:(len(phPoints)-shs)]) i]]
)
));
}
}
myPathTrefoil = [ for(t = [0:(360 / 101):359]) [ // trefoil knot
5*(.41*cos(t) - .18*sin(t) - .83*cos(2*t) - .83*sin(2*t) -
.11*cos(3*t) + .27*sin(3*t)),
5*(.36*cos(t) + .27*sin(t) - 1.13*cos(2*t) + .30*sin(2*t) +
.11*cos(3*t) - .27*sin(3*t)),
5*(.45*sin(t) - .30*cos(2*t) +1.13*sin(2*t) -
.11*cos(3*t) + .27*sin(3*t))] ];
myPointsOctagon =
let(ofs1=15)
[ for(t = [0:(360/8):359])
((t==90)?1:2) * [cos(t+ofs1),sin(t+ofs1)]];
myPointsChunkOctagon =
let(ofs1=15)
[ for(t = [0:(360/8):359])
((t==90)?0.4:1.9) *
[cos((t * 135/360 + 45)+ofs1+45)+0.5,sin((t * 135/360 + 45)+ofs1+45)]];
//myPoints = [ for(t = [0:(360/8):359]) 2 * [cos(t+45),sin(t+45)]];
pts=[2,0,0.5];
/*translate([0,0,0]) {
path_extrude(exRots = [$t*360], exShape=myPointsOctagon,
exPath=myPathTrefoil, merge=true);
}*/

View File

@ -1,372 +0,0 @@
/*
* ISO-standard metric threads, following this specification:
* http://en.wikipedia.org/wiki/ISO_metric_screw_thread
*
* Copyright 2017 Dan Kirshner - dan_kirshner@yahoo.com
* This program 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.
*
* This program 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.
*
* See <http://www.gnu.org/licenses/>.
*
* Version 2.3. 2017-08-31 Default for leadin: 0 (best for internal threads).
* Version 2.2. 2017-01-01 Correction for angle; leadfac option. (Thanks to
* Andrew Allen <a2intl@gmail.com>.)
* Version 2.1. 2016-12-04 Chamfer bottom end (low-z); leadin option.
* Version 2.0. 2016-11-05 Backwards compatibility (earlier OpenSCAD) fixes.
* Version 1.9. 2016-07-03 Option: tapered.
* Version 1.8. 2016-01-08 Option: (non-standard) angle.
* Version 1.7. 2015-11-28 Larger x-increment - for small-diameters.
* Version 1.6. 2015-09-01 Options: square threads, rectangular threads.
* Version 1.5. 2015-06-12 Options: thread_size, groove.
* Version 1.4. 2014-10-17 Use "faces" instead of "triangles" for polyhedron
* Version 1.3. 2013-12-01 Correct loop over turns -- don't have early cut-off
* Version 1.2. 2012-09-09 Use discrete polyhedra rather than linear_extrude ()
* Version 1.1. 2012-09-07 Corrected to right-hand threads!
*/
// Examples.
//
// Standard M8 x 1.
// metric_thread (diameter=8, pitch=1, length=4);
// Square thread.
// metric_thread (diameter=8, pitch=1, length=4, square=true);
// Non-standard: long pitch, same thread size.
//metric_thread (diameter=8, pitch=4, length=4, thread_size=1, groove=true);
// Non-standard: 20 mm diameter, long pitch, square "trough" width 3 mm,
// depth 1 mm.
//metric_thread (diameter=20, pitch=8, length=16, square=true, thread_size=6,
// groove=true, rectangle=0.333);
// English: 1/4 x 20.
//english_thread (diameter=1/4, threads_per_inch=20, length=1);
// Tapered. Example -- pipe size 3/4" -- per:
// http://www.engineeringtoolbox.com/npt-national-pipe-taper-threads-d_750.html
// english_thread (diameter=1.05, threads_per_inch=14, length=3/4, taper=1/16);
// Thread for mounting on Rohloff hub.
//difference () {
// cylinder (r=20, h=10, $fn=100);
//
// metric_thread (diameter=34, pitch=1, length=10, internal=true, n_starts=6);
//}
// ----------------------------------------------------------------------------
function segments (diameter) = min (50, ceil (diameter*6));
// ----------------------------------------------------------------------------
// diameter - outside diameter of threads in mm. Default: 8.
// pitch - thread axial "travel" per turn in mm. Default: 1.
// length - overall axial length of thread in mm. Default: 1.
// internal - true = clearances for internal thread (e.g., a nut).
// false = clearances for external thread (e.g., a bolt).
// (Internal threads should be "cut out" from a solid using
// difference ()).
// n_starts - Number of thread starts (e.g., DNA, a "double helix," has
// n_starts=2). See wikipedia Screw_thread.
// thread_size - (non-standard) axial width of a single thread "V" - independent
// of pitch. Default: same as pitch.
// groove - (non-standard) subtract inverted "V" from cylinder (rather than
// add protruding "V" to cylinder).
// square - Square threads (per
// https://en.wikipedia.org/wiki/Square_thread_form).
// rectangle - (non-standard) "Rectangular" thread - ratio depth/(axial) width
// Default: 1 (square).
// angle - (non-standard) angle (deg) of thread side from perpendicular to
// axis (default = standard = 30 degrees).
// taper - diameter change per length (National Pipe Thread/ANSI B1.20.1
// is 1" diameter per 16" length). Taper decreases from 'diameter'
// as z increases.
// leadin - 0 (default): no chamfer; 1: chamfer (45 degree) at max-z end;
// 2: chamfer at both ends, 3: chamfer at z=0 end.
// leadfac - scale of leadin chamfer (default: 1.0 = 1/2 thread).
module metric_thread (diameter=8, pitch=1, length=1, internal=false, n_starts=1,
thread_size=-1, groove=false, square=false, rectangle=0,
angle=30, taper=0, leadin=0, leadfac=1.0)
{
// thread_size: size of thread "V" different than travel per turn (pitch).
// Default: same as pitch.
local_thread_size = thread_size == -1 ? pitch : thread_size;
local_rectangle = rectangle ? rectangle : 1;
n_segments = segments (diameter);
h = (square || rectangle) ? local_thread_size*local_rectangle/2 : local_thread_size / (2 * tan(angle));
h_fac1 = (square || rectangle) ? 0.90 : 0.625;
// External thread includes additional relief.
h_fac2 = (square || rectangle) ? 0.95 : 5.3/8;
tapered_diameter = diameter - length*taper;
difference () {
union () {
if (! groove) {
metric_thread_turns (diameter, pitch, length, internal, n_starts,
local_thread_size, groove, square, rectangle, angle,
taper);
}
difference () {
// Solid center, including Dmin truncation.
if (groove) {
cylinder (r1=diameter/2, r2=tapered_diameter/2,
h=length, $fn=n_segments);
} else if (internal) {
cylinder (r1=diameter/2 - h*h_fac1, r2=tapered_diameter/2 - h*h_fac1,
h=length, $fn=n_segments);
} else {
// External thread.
cylinder (r1=diameter/2 - h*h_fac2, r2=tapered_diameter/2 - h*h_fac2,
h=length, $fn=n_segments);
}
if (groove) {
metric_thread_turns (diameter, pitch, length, internal, n_starts,
local_thread_size, groove, square, rectangle,
angle, taper);
}
}
}
// chamfer z=0 end if leadin is 2 or 3
if (leadin == 2 || leadin == 3) {
difference () {
cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);
cylinder (r2=diameter/2, r1=diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
$fn=n_segments);
}
}
// chamfer z-max end if leadin is 1 or 2.
if (leadin == 1 || leadin == 2) {
translate ([0, 0, length + 0.05 - h*h_fac1*leadfac]) {
difference () {
cylinder (r=diameter/2 + 1, h=h*h_fac1*leadfac, $fn=n_segments);
cylinder (r1=tapered_diameter/2, r2=tapered_diameter/2 - h*h_fac1*leadfac, h=h*h_fac1*leadfac,
$fn=n_segments);
}
}
}
}
}
// ----------------------------------------------------------------------------
// Input units in inches.
// Note: units of measure in drawing are mm!
module english_thread (diameter=0.25, threads_per_inch=20, length=1,
internal=false, n_starts=1, thread_size=-1, groove=false,
square=false, rectangle=0, angle=30, taper=0, leadin=0,
leadfac=1.0)
{
// Convert to mm.
mm_diameter = diameter*25.4;
mm_pitch = (1.0/threads_per_inch)*25.4;
mm_length = length*25.4;
echo (str ("mm_diameter: ", mm_diameter));
echo (str ("mm_pitch: ", mm_pitch));
echo (str ("mm_length: ", mm_length));
metric_thread (mm_diameter, mm_pitch, mm_length, internal, n_starts,
thread_size, groove, square, rectangle, angle, taper, leadin,
leadfac);
}
// ----------------------------------------------------------------------------
module metric_thread_turns (diameter, pitch, length, internal, n_starts,
thread_size, groove, square, rectangle, angle,
taper)
{
// Number of turns needed.
n_turns = floor (length/pitch);
intersection () {
// Start one below z = 0. Gives an extra turn at each end.
for (i=[-1*n_starts : n_turns+1]) {
translate ([0, 0, i*pitch]) {
metric_thread_turn (diameter, pitch, internal, n_starts,
thread_size, groove, square, rectangle, angle,
taper, i*pitch);
}
}
// Cut to length.
translate ([0, 0, length/2]) {
cube ([diameter*3, diameter*3, length], center=true);
}
}
}
// ----------------------------------------------------------------------------
module metric_thread_turn (diameter, pitch, internal, n_starts, thread_size,
groove, square, rectangle, angle, taper, z)
{
n_segments = segments (diameter);
fraction_circle = 1.0/n_segments;
for (i=[0 : n_segments-1]) {
rotate ([0, 0, i*360*fraction_circle]) {
translate ([0, 0, i*n_starts*pitch*fraction_circle]) {
//current_diameter = diameter - taper*(z + i*n_starts*pitch*fraction_circle);
thread_polyhedron ((diameter - taper*(z + i*n_starts*pitch*fraction_circle))/2,
pitch, internal, n_starts, thread_size, groove,
square, rectangle, angle);
}
}
}
}
// ----------------------------------------------------------------------------
module thread_polyhedron (radius, pitch, internal, n_starts, thread_size,
groove, square, rectangle, angle)
{
n_segments = segments (radius*2);
fraction_circle = 1.0/n_segments;
local_rectangle = rectangle ? rectangle : 1;
h = (square || rectangle) ? thread_size*local_rectangle/2 : thread_size / (2 * tan(angle));
outer_r = radius + (internal ? h/20 : 0); // Adds internal relief.
//echo (str ("outer_r: ", outer_r));
// A little extra on square thread -- make sure overlaps cylinder.
h_fac1 = (square || rectangle) ? 1.1 : 0.875;
inner_r = radius - h*h_fac1; // Does NOT do Dmin_truncation - do later with
// cylinder.
translate_y = groove ? outer_r + inner_r : 0;
reflect_x = groove ? 1 : 0;
// Make these just slightly bigger (keep in proportion) so polyhedra will
// overlap.
x_incr_outer = (! groove ? outer_r : inner_r) * fraction_circle * 2 * PI * 1.02;
x_incr_inner = (! groove ? inner_r : outer_r) * fraction_circle * 2 * PI * 1.02;
z_incr = n_starts * pitch * fraction_circle * 1.005;
/*
(angles x0 and x3 inner are actually 60 deg)
/\ (x2_inner, z2_inner) [2]
/ \
(x3_inner, z3_inner) / \
[3] \ \
|\ \ (x2_outer, z2_outer) [6]
| \ /
| \ /|
z |[7]\/ / (x1_outer, z1_outer) [5]
| | | /
| x | |/
| / | / (x0_outer, z0_outer) [4]
| / | / (behind: (x1_inner, z1_inner) [1]
|/ | /
y________| |/
(r) / (x0_inner, z0_inner) [0]
*/
x1_outer = outer_r * fraction_circle * 2 * PI;
z0_outer = (outer_r - inner_r) * tan(angle);
//echo (str ("z0_outer: ", z0_outer));
//polygon ([[inner_r, 0], [outer_r, z0_outer],
// [outer_r, 0.5*pitch], [inner_r, 0.5*pitch]]);
z1_outer = z0_outer + z_incr;
// Give internal square threads some clearance in the z direction, too.
bottom = internal ? 0.235 : 0.25;
top = internal ? 0.765 : 0.75;
translate ([0, translate_y, 0]) {
mirror ([reflect_x, 0, 0]) {
if (square || rectangle) {
// Rule for face ordering: look at polyhedron from outside: points must
// be in clockwise order.
polyhedron (
points = [
[-x_incr_inner/2, -inner_r, bottom*thread_size], // [0]
[x_incr_inner/2, -inner_r, bottom*thread_size + z_incr], // [1]
[x_incr_inner/2, -inner_r, top*thread_size + z_incr], // [2]
[-x_incr_inner/2, -inner_r, top*thread_size], // [3]
[-x_incr_outer/2, -outer_r, bottom*thread_size], // [4]
[x_incr_outer/2, -outer_r, bottom*thread_size + z_incr], // [5]
[x_incr_outer/2, -outer_r, top*thread_size + z_incr], // [6]
[-x_incr_outer/2, -outer_r, top*thread_size] // [7]
],
faces = [
[0, 3, 7, 4], // This-side trapezoid
[1, 5, 6, 2], // Back-side trapezoid
[0, 1, 2, 3], // Inner rectangle
[4, 7, 6, 5], // Outer rectangle
// These are not planar, so do with separate triangles.
[7, 2, 6], // Upper rectangle, bottom
[7, 3, 2], // Upper rectangle, top
[0, 5, 1], // Lower rectangle, bottom
[0, 4, 5] // Lower rectangle, top
]
);
} else {
// Rule for face ordering: look at polyhedron from outside: points must
// be in clockwise order.
polyhedron (
points = [
[-x_incr_inner/2, -inner_r, 0], // [0]
[x_incr_inner/2, -inner_r, z_incr], // [1]
[x_incr_inner/2, -inner_r, thread_size + z_incr], // [2]
[-x_incr_inner/2, -inner_r, thread_size], // [3]
[-x_incr_outer/2, -outer_r, z0_outer], // [4]
[x_incr_outer/2, -outer_r, z0_outer + z_incr], // [5]
[x_incr_outer/2, -outer_r, thread_size - z0_outer + z_incr], // [6]
[-x_incr_outer/2, -outer_r, thread_size - z0_outer] // [7]
],
faces = [
[0, 3, 7, 4], // This-side trapezoid
[1, 5, 6, 2], // Back-side trapezoid
[0, 1, 2, 3], // Inner rectangle
[4, 7, 6, 5], // Outer rectangle
// These are not planar, so do with separate triangles.
[7, 2, 6], // Upper rectangle, bottom
[7, 3, 2], // Upper rectangle, top
[0, 5, 1], // Lower rectangle, bottom
[0, 4, 5] // Lower rectangle, top
]
);
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff