Merge pull request #659 from adrianVmariano/master

screws bugfix & paths doc changes
This commit is contained in:
Revar Desmera 2021-09-22 21:38:33 -07:00 committed by GitHub
commit 2911110bd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1117 additions and 993 deletions

View file

@ -50,7 +50,7 @@ function ident(n) = [
// Arguments:
// x = The value to test for being an affine matrix.
// dim = The number of dimensions the given affine is required to be for. Generally 2 for 2D or 3 for 3D. If given as a list of integers, allows any of the given dimensions. Default: `[2,3]`
// Examples:
// Example:
// bool = is_affine(affine2d_scale([2,3])); // Returns true
// bool = is_affine(affine3d_scale([2,3,4])); // Returns true
// bool = is_affine(affine3d_scale([2,3,4]),2); // Returns false
@ -74,7 +74,7 @@ function is_affine(x,dim=[2,3]) =
// for a simple scaling of z. Note that an input which is only a zscale returns false.
// Arguments:
// t = The transformation matrix to check.
// Examples:
// Example:
// b = is_2d_transform(zrot(45)); // Returns: true
// b = is_2d_transform(yrot(45)); // Returns: false
// b = is_2d_transform(xrot(45)); // Returns: false

View file

@ -141,12 +141,10 @@ function reduce(func, list, init=0) =
// list = The input list.
// init = The starting value for the accumulator. Default: 0
// See Also: map(), filter(), reduce(), while(), for_n()
// Examples: Reimplement cumsum()
// echo(accumulate(function (a,b) a+b, [3,4,5],0));
// // ECHO: [3,7,12]
// Examples: Reimplement cumprod()
// echo(accumulate(f_mul(),[3,4,5],1));
// // ECHO: [3,12,60,360]
// Example: Reimplement cumsum()
// echo(accumulate(function (a,b) a+b, [3,4,5],0)); // ECHO: [3,7,12]
// Example: Reimplement cumprod()
// echo(accumulate(f_mul(),[3,4,5],1)); // ECHO: [3,12,60,360]
function accumulate(func, list, init=0) =
assert(is_function(func))
assert(is_list(list))
@ -313,7 +311,7 @@ function binsearch(key, list, idx, cmp=f_cmp()) =
// Arguments:
// x = The value to get the simple hash value of.
// See Also: hashmap()
// Examples:
// Example:
// x = simple_hash("Foobar");
// x = simple_hash([[10,20],[-5,3]]);
function simple_hash(x) =

View file

@ -37,7 +37,7 @@
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// circp = circular_pitch(pitch=5);
// circp = circular_pitch(mod=2);
function circular_pitch(pitch=5, mod) =
@ -54,7 +54,7 @@ function circular_pitch(pitch=5, mod) =
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// dp = diametral_pitch(pitch=5);
// dp = diametral_pitch(mod=2);
function diametral_pitch(pitch=5, mod) =
@ -96,7 +96,7 @@ function module_value(pitch=5) = pitch / PI;
// Arguments:
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// ad = adendum(pitch=5);
// ad = adendum(mod=2);
// Example(2D):
@ -123,7 +123,7 @@ function adendum(pitch=5, mod) =
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// clearance = If given, sets the clearance between meshing teeth.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// ddn = dedendum(pitch=5);
// ddn = dedendum(mod=2);
// Example(2D):
@ -152,7 +152,7 @@ function dedendum(pitch=5, clearance, mod) =
// pitch = The circular pitch, or distance between teeth around the pitch circle, in mm.
// teeth = The number of teeth on the gear.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// pr = pitch_radius(pitch=5, teeth=11);
// pr = pitch_radius(mod=2, teeth=20);
// Example(2D):
@ -177,7 +177,7 @@ function pitch_radius(pitch=5, teeth=11, mod) =
// clearance = If given, sets the clearance between meshing teeth.
// interior = If true, calculate for an interior gear.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// or = outer_radius(pitch=5, teeth=20);
// or = outer_radius(mod=2, teeth=16);
// Example(2D):
@ -203,7 +203,7 @@ function outer_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
// clearance = If given, sets the clearance between meshing teeth.
// interior = If true, calculate for an interior gear.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// rr = root_radius(pitch=5, teeth=11);
// rr = root_radius(mod=2, teeth=16);
// Example(2D):
@ -228,7 +228,7 @@ function root_radius(pitch=5, teeth=11, clearance, interior=false, mod) =
// teeth = The number of teeth on the gear.
// pressure_angle = Pressure angle in degrees. Controls how straight or bulged the tooth sides are.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// br = base_radius(pitch=5, teeth=20, pressure_angle=20);
// br = base_radius(mod=2, teeth=18, pressure_angle=20);
// Example(2D):
@ -253,7 +253,7 @@ function base_radius(pitch=5, teeth=11, pressure_angle=28, mod) =
// teeth = Number of teeth that this gear has.
// mate_teeth = Number of teeth that the matching gear has.
// drive_angle = Angle between the drive shafts of each gear. Default: 90º.
// Examples:
// Example:
// ang = bevel_pitch_angle(teeth=18, mate_teeth=30);
// Example(2D):
// t1 = 13; t2 = 19; pitch=5;
@ -287,7 +287,7 @@ function bevel_pitch_angle(teeth, mate_teeth, drive_angle=90) =
// crowning = The amount to oversize the virtual hobbing cutter used to make the teeth, to add a slight crowning to the teeth to make them fir the work easier. Default: 1
// clearance = Clearance gap at the bottom of the inter-tooth valleys.
// mod = The metric module/modulus of the gear.
// Examples:
// Example:
// thick = worm_gear_thickness(pitch=5, teeth=36, worm_diam=30);
// thick = worm_gear_thickness(mod=2, teeth=28, worm_diam=25);
// Example(2D):

View file

@ -113,7 +113,7 @@ function point_line_distance(pt, line, bounded=false) =
// Function: segment_distance()
// Usage:
// dist = segment_distance(seg1, seg2);
// dist = segment_distance(seg1, seg2, [eps]);
// Topics: Geometry, Segments, Distance
// See Also: convex_collision(), convex_distance()
// Description:
@ -121,12 +121,13 @@ function point_line_distance(pt, line, bounded=false) =
// Arguments:
// seg1 = The list of two points representing the first line segment to check the distance of.
// seg2 = The list of two points representing the second line segment to check the distance of.
// eps = tolerance for point comparisons
// Example:
// dist = segment_distance([[-14,3], [-15,9]], [[-10,0], [10,0]]); // Returns: 5
// dist2 = segment_distance([[-5,5], [5,-5]], [[-10,3], [10,-3]]); // Returns: 0
function segment_distance(seg1, seg2) =
function segment_distance(seg1, seg2,eps=EPSILON) =
assert( is_matrix(concat(seg1,seg2),4), "Inputs should be two valid segments." )
convex_distance(seg1,seg2);
convex_distance(seg1,seg2,eps);
// Function: line_normal()
@ -1476,7 +1477,7 @@ function polygon_normal(poly) =
// color("red")back(28/(2/3))text("Even-Odd", size=5/(2/3), halign="center");
// }
// right(40){
// dp = decompose_path(path,closed=true);
// dp = polygon_parts(path,closed=true);
// region(dp);
// color("red"){stroke(path,width=1,closed=true);
// back(28/(2/3))text("Nonzero", size=5/(2/3), halign="center");

View file

@ -8,8 +8,7 @@
include <threading.scad>
include <phillips_drive.scad>
include <torx_drive.scad>
include <screw_drive.scad>
// Section: Functions
@ -607,13 +606,13 @@ module metric_bolt(
// Phillips drive hole
if (headtype != "socket" && phillips != undef) {
down(headtype != "hex"? H/6 : 0) {
phillips_drive(size=phillips, shaft=D);
phillips_mask(size=phillips); //, shaft=D);
}
}
// Torx drive hole
if (headtype != "socket" && torx != undef) {
up(1) torx_drive(size=torx, l=H+0.1, center=false);
up(1) torx_mask(size=torx, l=H+0.1, center=false);
}
}
}

View file

@ -1036,6 +1036,7 @@ module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children();
// Arguments:
// list = The list of items to iterate through.
// stride = Consecutive colors stride around the color wheel divided into this many parts.
// maxhues = max number of hues to use (to prevent lots of indistinguishable hues)
// Side Effects:
// Sets the color to progressive values along the ROYGBIV spectrum for each item.
// Sets `$idx` to the index of the current item in `list` that we want to show.
@ -1045,11 +1046,14 @@ module HSV(h,s=1,v=1,a=1) color(HSV(h,s,v),a) children();
// Example(2D):
// rgn = [circle(d=45,$fn=3), circle(d=75,$fn=4), circle(d=50)];
// rainbow(rgn) stroke($item, closed=true);
module rainbow(list, stride=1)
module rainbow(list, stride=1, maxhues)
{
ll = len(list);
huestep = 360 / ll;
maxhues = first_defined([maxhues,ll]);
huestep = 360 / maxhues;
hues = [for (i=[0:1:ll-1]) posmod(i*huestep+i*360/stride,360)];
echo(hues=hues);
s = [for (i=[0:1:ll-1]) [.5,.7,1][posmod(i,3)]];
for($idx=idx(list)) {
$item = list[$idx];
HSV(h=hues[$idx]) children();

File diff suppressed because it is too large Load diff

View file

@ -1,77 +0,0 @@
//////////////////////////////////////////////////////////////////////
// LibFile: phillips_drive.scad
// Phillips driver bits
// Includes:
// include <BOSL2/std.scad>
// include <BOSL2/phillips_drive.scad>
//////////////////////////////////////////////////////////////////////
// Section: Modules
// Module: phillips_drive()
// Description: Creates a model of a phillips driver bit of a given named size.
// Arguments:
// size = The size of the bit as a string. "#0", "#1", "#2", "#3", or "#4"
// shaft = The diameter of the drive bit's shaft.
// l = The length of the drive bit.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Example:
// xdistribute(10) {
// phillips_drive(size="#1", shaft=4, l=20);
// phillips_drive(size="#2", shaft=6, l=20);
// phillips_drive(size="#3", shaft=6, l=20);
// }
module phillips_drive(size="#2", shaft=6, l=20, $fn=36, anchor=BOTTOM, spin=0, orient=UP) {
assert(is_string(size));
assert(in_list(size,["#0","#1","#2","#3","#4"]));
num = ord(size[1]) - ord("0");
b = [0.61, 0.97, 1.47, 2.41, 3.48][num];
e = [0.31, 0.43, 0.81, 2.00, 2.41][num];
g = [0.81, 1.27, 2.29, 3.81, 5.08][num];
//f = [0.33, 0.53, 0.70, 0.82, 1.23][num];
//r = [0.30, 0.50, 0.60, 0.80, 1.00][num];
alpha = [ 136, 138, 140, 146, 153][num];
beta = [7.00, 7.00, 5.75, 5.75, 7.00][num];
gamma = 92.0;
ang1 = 28.0;
ang2 = 26.5;
h1 = adj_ang_to_opp(g/2, ang1);
h2 = adj_ang_to_opp((shaft-g)/2, 90-ang2);
h3 = adj_ang_to_opp(b/2, ang1);
p0 = [0,0];
p1 = [e/2, adj_ang_to_opp(e/2, 90-alpha/2)];
p2 = p1 + [(shaft-e)/2, adj_ang_to_hyp((shaft-e)/2, 90-gamma/2)];
attachable(anchor,spin,orient, d=shaft, l=l) {
down(l/2) {
difference() {
union() {
cyl(d1=0, d2=g, h=h1, anchor=BOT);
up(h1) {
cyl(d1=g, d2=shaft, h=h2, anchor=BOT);
up(h2) cyl(d=shaft, h=l-h1-h2, anchor=BOT);
}
}
zrot(45)
zrot_copies(n=4, r=b/2/cos(90-alpha/2), sa=90) {
up(h3) {
xrot(-beta) {
linear_extrude(height=(h1+h2)*20, convexity=4, center=true) {
path = [p0, p1, p2, [-p2.x,p2.y], [-p1.x,p1.y]];
polygon(path);
}
}
}
}
}
}
children();
}
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -29,31 +29,17 @@ function is_region(x) = is_list(x) && is_path(x.x);
function close_region(region, eps=EPSILON) = [for (path=region) close_path(path, eps=eps)];
// Module: region()
// Function: cleanup_region()
// Usage:
// region(r);
// cleanup_region(region);
// Description:
// Creates 2D polygons for the given region. The region given is a list of closed 2D paths.
// Each path will be effectively exclusive-ORed from all other paths in the region, so if a
// path is inside another path, it will be effectively subtracted from it.
// Example(2D):
// region([circle(d=50), square(25,center=true)]);
// Example(2D):
// rgn = concat(
// [for (d=[50:-10:10]) circle(d=d-5)],
// [square([60,10], center=true)]
// );
// region(rgn);
module region(r)
{
points = flatten(r);
paths = [
for (i=[0:1:len(r)-1]) let(
start = default(sum([for (j=[0:1:i-1]) len(r[j])]),0)
) [for (k=[0:1:len(r[i])-1]) start+k]
];
polygon(points=points, paths=paths);
}
// For all paths in the given region, if the last point coincides with the first point, removes the last point.
// Arguments:
// region = The region to clean up. Given as a list of polygon paths.
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
function cleanup_region(region, eps=EPSILON) =
[for (path=region) cleanup_path(path, eps=eps)];
// Function: check_and_fix_path()
@ -91,16 +77,34 @@ function check_and_fix_path(path, valid_dim=undef, closed=false, name="path") =
closed && approx(path[0], last(path))? list_head(path) : path;
// Function: cleanup_region()
// Module: region()
// Usage:
// cleanup_region(region);
// region(r);
// Description:
// For all paths in the given region, if the last point coincides with the first point, removes the last point.
// Arguments:
// region = The region to clean up. Given as a list of polygon paths.
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
function cleanup_region(region, eps=EPSILON) =
[for (path=region) cleanup_path(path, eps=eps)];
// Creates 2D polygons for the given region. The region given is a list of closed 2D paths.
// Each path will be effectively exclusive-ORed from all other paths in the region, so if a
// path is inside another path, it will be effectively subtracted from it.
// Example(2D):
// region([circle(d=50), square(25,center=true)]);
// Example(2D):
// rgn = concat(
// [for (d=[50:-10:10]) circle(d=d-5)],
// [square([60,10], center=true)]
// );
// region(rgn);
module region(r)
{
points = flatten(r);
paths = [
for (i=[0:1:len(r)-1]) let(
start = default(sum([for (j=[0:1:i-1]) len(r[j])]),0)
) [for (k=[0:1:len(r[i])-1]) start+k]
];
polygon(points=points, paths=paths);
}
// Function: point_in_region()
@ -121,11 +125,12 @@ function point_in_region(point, region, eps=EPSILON, _i=0, _cnt=0) =
) pip==0? 0 : point_in_region(point, region, eps=eps, _i=_i+1, _cnt = _cnt + (pip>0? 1 : 0));
// Function: polygons_equal()
// Usage:
// b = polygons_equal(poly1, poly2, [eps])
// Description:
// Returns true if the components of region1 and region2 are the same polygons
// Returns true if poly1 and poly2 are the same polongs
// within given epsilon tolerance.
// Arguments:
// poly1 = first polygon
@ -151,34 +156,34 @@ function __polygons_equal(poly1, poly2, eps, st) =
max([for(d=poly1-select(poly2,st,st-1)) d*d])<eps*eps;
// Function: poly_in_polygons()
// Function: is_polygon_in_list()
// Topics: Polygons, Comparators
// See Also: polygons_equal(), regions_equal()
// Usage:
// bool = poly_in_polygons(poly, polys);
// bool = is_polygon_in_list(poly, polys);
// Description:
// Returns true if one of the polygons in `polys` is equivalent to the polygon `poly`.
// Arguments:
// poly = The polygon to search for.
// polys = The list of polygons to look for the polygon in.
function poly_in_polygons(poly, polys) =
__poly_in_polygons(poly, polys, 0);
function is_polygon_in_list(poly, polys) =
__is_polygon_in_list(poly, polys, 0);
function __poly_in_polygons(poly, polys, i) =
function __is_polygon_in_list(poly, polys, i) =
i >= len(polys)? false :
polygons_equal(poly, polys[i])? true :
__poly_in_polygons(poly, polys, i+1);
__is_polygon_in_list(poly, polys, i+1);
// Function: regions_equal()
// Usage:
// b = regions_equal(region1, region2, [eps])
// Description:
// Returns true if the components of region1 and region2 are the same polygons
// Returns true if the components of region1 and region2 are the same polygons (in any order)
// within given epsilon tolerance.
// Arguments:
// poly1 = first polygon
// poly2 = second polygon
// region1 = first region
// region2 = second region
// eps = tolerance for comparison
function regions_equal(region1, region2) =
assert(is_region(region1) && is_region(region2))
@ -187,21 +192,21 @@ function regions_equal(region1, region2) =
function __regions_equal(region1, region2, i) =
i >= len(region1)? true :
!poly_in_polygons(region1[i], region2)? false :
!is_polygon_in_list(region1[i], region2)? false :
__regions_equal(region1, region2, i+1);
// Function: region_path_crossings()
// Usage:
// region_path_crossings(path, region);
// Description:
// Returns a sorted list of [SEGMENT, U] that describe where a given path is crossed by a second path.
// Arguments:
// path = The path to find crossings on.
// region = Region to test for crossings of.
// closed = If true, treat path as a closed polygon. Default: true
// eps = Acceptable variance. Default: `EPSILON` (1e-9)
function region_path_crossings(path, region, closed=true, eps=EPSILON) = sort([
/// Internal Function: _region_path_crossings()
/// Usage:
/// _region_path_crossings(path, region);
/// Description:
/// Returns a sorted list of [SEGMENT, U] that describe where a given path is crossed by a second path.
/// Arguments:
/// path = The path to find crossings on.
/// region = Region to test for crossings of.
/// closed = If true, treat path as a closed polygon. Default: true
/// eps = Acceptable variance. Default: `EPSILON` (1e-9)
function _region_path_crossings(path, region, closed=true, eps=EPSILON) = sort([
let(
segs = pair(closed? close_path(path) : cleanup_path(path))
) for (
@ -240,7 +245,7 @@ function split_path_at_region_crossings(path, region, closed=true, eps=EPSILON)
let(
path = deduplicate(path, eps=eps),
region = [for (path=region) deduplicate(path, eps=eps)],
xings = region_path_crossings(path, region, closed=closed, eps=eps),
xings = _region_path_crossings(path, region, closed=closed, eps=eps),
crossings = deduplicate(
concat([[0,0]], xings, [[len(path)-1,1]]),
eps=eps
@ -489,7 +494,7 @@ function _shift_segment(segment, d) =
// Extend to segments to their intersection point. First check if the segments already have a point in common,
// which can happen if two colinear segments are input to the path variant of `offset()`
function _segment_extension(s1,s2) =
norm(s1[1]-s2[0])<1e-6 ? s1[1] : line_intersection(s1,s2);
norm(s1[1]-s2[0])<1e-6 ? s1[1] : line_intersection(s1,s2,LINE,LINE);
function _makefaces(direction, startind, good, pointcount, closed) =
@ -745,7 +750,11 @@ function offset(
quality = max(0,round(quality)),
flip_dir = closed && !is_polygon_clockwise(path)? -1 : 1,
d = flip_dir * (is_def(r) ? r : delta),
shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
// shiftsegs = [for(i=[0:len(path)-1]) _shift_segment(select(path,i,i+1), d)],
shiftsegs = [for(i=[0:len(path)-2]) _shift_segment([path[i],path[i+1]], d),
if (closed) _shift_segment([last(path),path[0]],d)
else [path[0],path[1]] // dummy segment, not used
],
// good segments are ones where no point on the segment is less than distance d from any point on the path
good = check_valid ? _good_segments(path, abs(d), shiftsegs, closed, quality) : repeat(true,len(shiftsegs)),
goodsegs = bselect(shiftsegs, good),
@ -759,7 +768,8 @@ function offset(
// Note if !closed the last corner doesn't matter, so exclude it
parallelcheck =
(len(sharpcorners)==2 && !closed) ||
all_defined(closed? sharpcorners : list_tail(sharpcorners))
all_defined(closed? sharpcorners : select(sharpcorners, 1,-2)),
f=echo(sharpcorners=sharpcorners)
)
assert(parallelcheck, "Path contains sequential parallel segments (either 180 deg turn or 0 deg turn")
let(
@ -771,7 +781,7 @@ function offset(
:
[for(i=[0:len(goodsegs)-1])
let(prevseg=select(goodsegs,i-1))
i==0 && !closed ? false // In open case first entry is bogus
(i==0 || i==len(goodsegs)-1) && !closed ? false // In open case first entry is bogus
:
(goodsegs[i][1]-goodsegs[i][0]) * (goodsegs[i][0]-sharpcorners[i]) > 0
&& (prevseg[1]-prevseg[0]) * (sharpcorners[i]-prevseg[1]) > 0

View file

@ -1931,8 +1931,8 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
top_patch[i][4][4]
]
],
top_intersections = path_self_intersections(faces[0]),
bot_intersections = path_self_intersections(faces[1]),
top_simple = is_path_simple(faces[0],closed=true),
bot_simple = is_path_simple(faces[1],closed=true),
// verify vertical edges
verify_vert =
[for(i=[0:N-1],j=[0:4])
@ -1949,9 +1949,9 @@ function rounded_prism(bottom, top, joint_bot=0, joint_top=0, joint_sides=0, k_b
)
if (!is_collinear(hline_top) || !is_collinear(hline_bot)) [i,j]]
)
assert(debug || top_intersections==[],
assert(debug || top_simple,
"Roundovers interfere with each other on top face: either input is self intersecting or top joint length is too large")
assert(debug || bot_intersections==[],
assert(debug || bot_simple,
"Roundovers interfere with each other on bottom face: either input is self intersecting or top joint length is too large")
assert(debug || (verify_vert==[] && verify_horiz==[]), "Curvature continuity failed")
let(

312
screw_drive.scad Normal file
View file

@ -0,0 +1,312 @@
//////////////////////////////////////////////////////////////////////
// LibFile: screw_drive.scad
// Recess masks for screw heads
// Includes:
// include <BOSL2/std.scad>
// include <BOSL2/screw_drive.scad>
//////////////////////////////////////////////////////////////////////
// Section: Phillips Drive
// Module: phillips_mask()
// Description:
// Creates a mask for creating a Phillips drive recess given the Phillips size. Each mask can
// be lowered to different depths to create different sizes of recess.
// Arguments:
// size = The size of the bit as a number or string. "#0", "#1", "#2", "#3", or "#4"
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Example:
// xdistribute(10) {
// phillips_mask(size="#1");
// phillips_mask(size="#2");
// phillips_mask(size=3);
// phillips_mask(size=4);
// }
// Specs for phillips recess here:
// https://www.fasteners.eu/tech-info/ISO/4757/
_phillips_shaft = [3,4.5,6,8,10];
_ph_bot_angle = 28.0;
_ph_side_angle = 26.5;
module phillips_mask(size="#2", $fn=36, anchor=BOTTOM, spin=0, orient=UP) {
assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]));
num = is_num(size) ? size : ord(size[1]) - ord("0");
shaft = _phillips_shaft[num];
b = [0.61, 0.97, 1.47, 2.41, 3.48][num];
e = [0.31, 0.435, 0.815, 2.005, 2.415][num];
g = [0.81, 1.27, 2.29, 3.81, 5.08][num];
//f = [0.33, 0.53, 0.70, 0.82, 1.23][num];
//r = [0.30, 0.50, 0.60, 0.80, 1.00][num];
alpha = [ 136, 138, 140, 146, 153][num];
beta = [7.00, 7.00, 5.75, 5.75, 7.00][num];
gamma = 92.0;
h1 = adj_ang_to_opp(g/2, _ph_bot_angle); // height of the small conical tip
h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle); // height of larger cone
l = h1+h2;
h3 = adj_ang_to_opp(b/2, _ph_bot_angle); // height where cutout starts
p0 = [0,0];
p1 = [adj_ang_to_opp(e/2, 90-alpha/2), -e/2];
p2 = p1 + [adj_ang_to_opp((shaft-e)/2, 90-gamma/2),-(shaft-e)/2];
attachable(anchor,spin,orient, d=shaft, l=l) {
down(l/2) {
difference() {
rotate_extrude()
polygon([[0,0],[g/2,h1],[shaft/2,l],[0,l]]);
zrot(45)
zrot_copies(n=4, r=b/2) {
up(h3) {
yrot(beta) {
down(1)
linear_extrude(height=l+2, convexity=4, center=false) {
path = [p0, p1, p2, [p2.x,-p2.y], [p1.x,-p1.y]];
polygon(path);
}
}
}
}
}
}
children();
}
}
// Function: phillips_depth()
// Usage:
// depth = phillips_depth(size, d);
// Description:
// Returns the depth of the Phillips recess required to produce the specified diameter, or
// undef if not possible.
// Arguments:
// size = size as a number or text string like "#2"
// d = desired diameter
function phillips_depth(size, d) =
assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
let(
num = is_num(size) ? size : ord(size[1]) - ord("0"),
shaft = [3,4.5,6,8,10][num],
g = [0.81, 1.27, 2.29, 3.81, 5.08][num],
_ph_bot_angle = 28.0,
_ph_side_angle = 26.5,
h1 = adj_ang_to_opp(g/2, _ph_bot_angle), // height of the small conical tip
h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle) // height of larger cone
)
d>=shaft || d<g ? undef :
(d-g) / 2 / tan(_ph_side_angle) + h1;
// Function: phillips_diam()
// Usage:
// diam = phillips_diam(size, depth);
// Description:
// Returns the diameter at the top of the Phillips recess when constructed at the specified depth,
// or undef if that depth is not valid.
// Arguments:
// size = size as number or text string like "#2"
// depth = depth of recess to find the diameter of
function phillips_diam(size, depth) =
assert(in_list(size,["#0","#1","#2","#3","#4",0,1,2,3,4]))
let(
num = is_num(size) ? size : ord(size[1]) - ord("0"),
shaft = _phillips_shaft[num],
g = [0.81, 1.27, 2.29, 3.81, 5.08][num],
h1 = adj_ang_to_opp(g/2, _ph_bot_angle), // height of the small conical tip
h2 = adj_ang_to_opp((shaft-g)/2, 90-_ph_side_angle) // height of larger cone
)
depth<h1 || depth>= h1+h2 ? undef :
2 * tan(_ph_side_angle)*(depth-h1) + g;
// Section: Torx Drive
// Function: torx_outer_diam()
// Description: Get the typical outer diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_outer_diam(size) = lookup(size, [
[ 6, 1.75],
[ 8, 2.40],
[ 10, 2.80],
[ 15, 3.35],
[ 20, 3.95],
[ 25, 4.50],
[ 30, 5.60],
[ 40, 6.75],
[ 45, 7.93],
[ 50, 8.95],
[ 55, 11.35],
[ 60, 13.45],
[ 70, 15.70],
[ 80, 17.75],
[ 90, 20.20],
[100, 22.40]
]);
// Function: torx_inner_diam()
// Description: Get typical inner diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_inner_diam(size) = lookup(size, [
[ 6, 1.27],
[ 8, 1.75],
[ 10, 2.05],
[ 15, 2.40],
[ 20, 2.85],
[ 25, 3.25],
[ 30, 4.05],
[ 40, 4.85],
[ 45, 5.64],
[ 50, 6.45],
[ 55, 8.05],
[ 60, 9.60],
[ 70, 11.20],
[ 80, 12.80],
[ 90, 14.40],
[100, 16.00]
]);
// Function: torx_depth()
// Description: Gets typical drive hole depth.
// Arguments:
// size = Torx size.
function torx_depth(size) = lookup(size, [
[ 6, 1.82],
[ 8, 3.05],
[ 10, 3.56],
[ 15, 3.81],
[ 20, 4.07],
[ 25, 4.45],
[ 30, 4.95],
[ 40, 5.59],
[ 45, 6.22],
[ 50, 6.48],
[ 55, 6.73],
[ 60, 8.17],
[ 70, 8.96],
[ 80, 9.90],
[ 90, 10.56],
[100, 11.35]
]);
// Function: torx_tip_radius()
// Description: Gets minor rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_tip_radius(size) = lookup(size, [
[ 6, 0.132],
[ 8, 0.190],
[ 10, 0.229],
[ 15, 0.267],
[ 20, 0.305],
[ 25, 0.375],
[ 30, 0.451],
[ 40, 0.546],
[ 45, 0.574],
[ 50, 0.775],
[ 55, 0.867],
[ 60, 1.067],
[ 70, 1.194],
[ 80, 1.526],
[ 90, 1.530],
[100, 1.720]
]);
// Function: torx_rounding_radius()
// Description: Gets major rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_rounding_radius(size) = lookup(size, [
[ 6, 0.383],
[ 8, 0.510],
[ 10, 0.598],
[ 15, 0.716],
[ 20, 0.859],
[ 25, 0.920],
[ 30, 1.194],
[ 40, 1.428],
[ 45, 1.796],
[ 50, 1.816],
[ 55, 2.667],
[ 60, 2.883],
[ 70, 3.477],
[ 80, 3.627],
[ 90, 4.468],
[100, 4.925]
]);
// Module: torx_mask2d()
// Description: Creates a torx bit 2D profile.
// Arguments:
// size = Torx size.
// Example(2D):
// torx_mask2d(size=30, $fa=1, $fs=1);
module torx_mask2d(size) {
od = torx_outer_diam(size);
id = torx_inner_diam(size);
tip = torx_tip_radius(size);
rounding = torx_rounding_radius(size);
base = od - 2*tip;
$fn = quantup(segs(od/2),12);
difference() {
union() {
circle(d=base);
zrot_copies(n=2) {
hull() {
zrot_copies(n=3) {
translate([base/2,0,0]) {
circle(r=tip, $fn=$fn/2);
}
}
}
}
}
zrot_copies(n=6) {
zrot(180/6) {
translate([id/2+rounding,0,0]) {
circle(r=rounding);
}
}
}
}
}
// Module: torx_mask()
// Description: Creates a torx bit tip.
// Arguments:
// size = Torx size.
// l = Length of bit.
// center = If true, centers bit vertically.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Examples:
// torx_mask(size=30, l=10, $fa=1, $fs=1);
module torx_mask(size, l=5, center, anchor, spin=0, orient=UP) {
anchor = get_anchor(anchor, center, BOT, BOT);
od = torx_outer_diam(size);
attachable(anchor,spin,orient, d=od, l=l) {
linear_extrude(height=l, convexity=4, center=true) {
torx_mask2d(size);
}
children();
}
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -8,8 +8,8 @@
include <structs.scad>
include <threading.scad>
include <phillips_drive.scad>
include <torx_drive.scad>
include <screw_drive.scad>
// Section: Generic Screw Creation
@ -837,18 +837,18 @@ module screw_head(screw_info,details=false) {
// anchor = anchor relative to the shaft of the screw
// anchor_head = anchor relative to the screw head
// Example(Med): Selected UTS (English) screws
$fn=32;
xdistribute(spacing=8){
screw("#6", length=12);
screw("#6-32", head="button", drive="torx",length=12);
screw("#6-32,3/4", head="hex");
screw("#6", thread="fine", head="fillister",length=12, drive="phillips");
screw("#6", head="flat small",length=12,drive="slot");
screw("#6-32", head="flat large", length=12, drive="torx");
screw("#6-32", head="flat undercut",length=12);
screw("#6-24", head="socket",length=12); // Non-standard threading
screw("#6-32", drive="hex", drive_size=1.5, length=12);
}
// $fn=32;
// xdistribute(spacing=8){
// screw("#6", length=12);
// screw("#6-32", head="button", drive="torx",length=12);
// screw("#6-32,3/4", head="hex");
// screw("#6", thread="fine", head="fillister",length=12, drive="phillips");
// screw("#6", head="flat small",length=12,drive="slot");
// screw("#6-32", head="flat large", length=12, drive="torx");
// screw("#6-32", head="flat undercut",length=12);
// screw("#6-24", head="socket",length=12); // Non-standard threading
// screw("#6-32", drive="hex", drive_size=1.5, length=12);
// }
// Example(Med): A few examples of ISO (metric) screws
// $fn=32;
// xdistribute(spacing=8){
@ -1037,8 +1037,8 @@ module _driver(spec)
echo(drive_size=drive_size);
up(head_top-drive_depth){
// recess should be positioned with its bottom center at (0,0) and the correct recess depth given above
if (drive=="phillips") phillips_drive(size=str("#",drive_size), shaft=diameter,anchor=BOTTOM);
if (drive=="torx") torx_drive(size=drive_size, l=drive_depth+1, center=false);
if (drive=="phillips") phillips_mask(drive_size,anchor=BOTTOM);
if (drive=="torx") torx_mask(size=drive_size, l=drive_depth+1, center=false);
if (drive=="hex") linear_extrude(height=drive_depth+1) hexagon(id=drive_size);
if (drive=="slot") cuboid([2*struct_val(spec,"head_size"), drive_width, drive_depth+1],anchor=BOTTOM);
}
@ -1415,9 +1415,21 @@ http://files.engineering.com/getfile.aspx?folder=76fb0d5e-1fff-4c49-87a5-0597947
//
// Torx drive depth for UTS and ISO (at least missing for "flat small", which means you can't select torx for this head type)
// Handle generic phillips (e.g. ph2) or remove it?
// https://www.fasteners.eu/tech-info/ISO/7721-2/
//
// How do you insert a threaded hole into a model?
// Default nut thickness
//
// JIS
//https://www.garagejournal.com/forum/media/jis-b-4633-vs-iso-8764-1-din-5260-ph.84492/
//square:
//https://www.aspenfasteners.com/content/pdf/square_drive_specification.pdf
//http://www.globalfastener.com/standards/index.php?narr58=149
//https://patents.google.com/patent/US1003657
// thread standards:
// https://www.gewinde-normen.de/en/index.html
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

View file

@ -810,9 +810,45 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit,
/// Internal Function: _path_add_jitter()
/// Topics: Paths
/// See Also: jittered_poly(), subdivide_long_segments()
/// Usage:
/// jpath = _path_add_jitter(path, [dist], [closed=]);
/// Description:
/// Adds tiny jitter offsets to collinear points in the given path so that they
/// are no longer collinear. This is useful for preserving subdivision on long
/// straight segments, when making geometry with `polygon()`, for use with
/// `linear_exrtrude()` with a `twist()`.
/// Arguments:
/// path = The path to add jitter to.
/// dist = The amount to jitter points by. Default: 1/512 (0.00195)
/// ---
/// closed = If true, treat path like a closed polygon. Default: true
/// Example(3D):
/// d = 100; h = 75; quadsize = 5;
/// path = pentagon(d=d);
/// spath = subdivide_long_segments(path, quadsize, closed=true);
/// jpath = _path_add_jitter(spath, closed=true);
/// linear_extrude(height=h, twist=72, slices=h/quadsize)
/// polygon(jpath);
function _path_add_jitter(path, dist=1/512, closed=true) =
assert(is_path(path))
assert(is_finite(dist))
assert(is_bool(closed))
[
path[0],
for (i=idx(path,s=1,e=closed?-1:-2)) let(
n = line_normal([path[i-1],path[i]])
) path[i] + n * (is_collinear(select(path,i-1,i+1))? (dist * ((i%2)*2-1)) : 0),
if (!closed) last(path)
];
// Module: jittered_poly()
// Topics: Extrusions
// See Also: path_add_jitter(), subdivide_long_segments()
// See Also: _path_add_jitter(), subdivide_long_segments()
// Usage:
// jittered_poly(path, [dist]);
// Description:
@ -829,7 +865,7 @@ module star(n, r, ir, d, or, od, id, step, realign=false, align_tip, align_pit,
// linear_extrude(height=h, twist=72, slices=h/quadsize)
// jittered_poly(spath);
module jittered_poly(path, dist=1/512) {
polygon(path_add_jitter(path, dist, closed=true));
polygon(_path_add_jitter(path, dist, closed=true));
}

View file

@ -1,6 +1,10 @@
//////////////////////////////////////////////////////////////////////
// LibFile: shapes3d.scad
// Common useful shapes and structured objects.
// Some standard modules for making 3d shapes with attachment support, and function forms
// that produce a VNF. Also included are shortcuts cylinders in each orientation and extended versions of
// the standard modules that provide roundovers and chamfers. The sphereoid() module provides
// several different ways to make a sphere, and the text modules let you write text on a path
// so you can place it on a curved object.
// Includes:
// include <BOSL2/std.scad>
//////////////////////////////////////////////////////////////////////
@ -1396,6 +1400,61 @@ module tube(
// Module: pie_slice()
//
// Description:
// Creates a pie slice shape.
//
// Usage: Typical
// pie_slice(l|h, r, ang, [center]);
// pie_slice(l|h, d=, ang=, ...);
// pie_slice(l|h, r1=|d1=, r2=|d2=, ang=, ...);
// Usage: Attaching Children
// pie_slice(l|h, r, ang, ...) [attachments];
//
// Arguments:
// h / l = height of pie slice.
// r = radius of pie slice.
// ang = pie slice angle in degrees.
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
// ---
// r1 = bottom radius of pie slice.
// r2 = top radius of pie slice.
// d = diameter of pie slice.
// d1 = bottom diameter of pie slice.
// d2 = top diameter of pie slice.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
//
// Example: Cylindrical Pie Slice
// pie_slice(ang=45, l=20, r=30);
// Example: Conical Pie Slice
// pie_slice(ang=60, l=20, d1=50, d2=70);
module pie_slice(
h, r, ang=30, center,
r1, r2, d, d1, d2, l,
anchor, spin=0, orient=UP
) {
l = first_defined([l, h, 1]);
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10);
r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10);
maxd = max(r1,r2)+0.1;
anchor = get_anchor(anchor, center, BOT, BOT);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
difference() {
cyl(r1=r1, r2=r2, h=l);
if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
difference() {
fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
}
}
children();
}
}
// Section: Other Round Objects
@ -2204,59 +2263,6 @@ module nil() union(){}
module noop(spin=0, orient=UP) attachable(CENTER,spin,orient, d=0.01) {nil(); children();}
// Module: pie_slice()
//
// Description:
// Creates a pie slice shape.
//
// Usage: Typical
// pie_slice(l|h, r, ang, [center]);
// pie_slice(l|h, d=, ang=, ...);
// pie_slice(l|h, r1=|d1=, r2=|d2=, ang=, ...);
// Usage: Attaching Children
// pie_slice(l|h, r, ang, ...) [attachments];
//
// Arguments:
// h / l = height of pie slice.
// r = radius of pie slice.
// ang = pie slice angle in degrees.
// center = If given, overrides `anchor`. A true value sets `anchor=CENTER`, false sets `anchor=UP`.
// ---
// r1 = bottom radius of pie slice.
// r2 = top radius of pie slice.
// d = diameter of pie slice.
// d1 = bottom diameter of pie slice.
// d2 = top diameter of pie slice.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
//
// Example: Cylindrical Pie Slice
// pie_slice(ang=45, l=20, r=30);
// Example: Conical Pie Slice
// pie_slice(ang=60, l=20, d1=50, d2=70);
module pie_slice(
h, r, ang=30, center,
r1, r2, d, d1, d2, l,
anchor, spin=0, orient=UP
) {
l = first_defined([l, h, 1]);
r1 = get_radius(r1=r1, r=r, d1=d1, d=d, dflt=10);
r2 = get_radius(r1=r2, r=r, d1=d2, d=d, dflt=10);
maxd = max(r1,r2)+0.1;
anchor = get_anchor(anchor, center, BOT, BOT);
attachable(anchor,spin,orient, r1=r1, r2=r2, l=l) {
difference() {
cyl(r1=r1, r2=r2, h=l);
if (ang<180) rotate(ang) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
difference() {
fwd(maxd/2) cube([2*maxd, maxd, l+0.2], center=true);
if (ang>180) rotate(ang-180) back(maxd/2) cube([2*maxd, maxd, l+0.1], center=true);
}
}
children();
}
}
// Module: interior_fillet()

View file

@ -832,8 +832,13 @@ function path_sweep(shape, path, method="incremental", normal, closed=false, twi
caps = is_def(caps) ? caps :
closed ? false : true,
capsOK = is_bool(caps) || (is_list(caps) && len(caps)==2 && is_bool(caps[0]) && is_bool(caps[1])),
fullcaps = is_bool(caps) ? [caps,caps] : caps
fullcaps = is_bool(caps) ? [caps,caps] : caps,
normalOK = is_undef(normal) || (method!="natural" && is_vector(normal,3))
|| (method=="manual" && same_shape(normal,path))
)
assert(normalOK, method=="natural" ? "Cannot specify normal with the \"natural\" method"
: method=="incremental" ? "Normal with \"incremental\" method must be a 3-vector"
: str("Incompatible normal given. Must be a 3-vector or a list of ",len(path)," 3-vectors"))
assert(capsOK, "caps must be boolean or a list of two booleans")
assert(!closed || !caps, "Cannot make closed shape with caps")
assert(is_undef(normal) || (is_vector(normal) && len(normal)==3) || (is_path(normal) && len(normal)==len(path) && len(normal[0])==3), "Invalid normal specified")
@ -1158,37 +1163,6 @@ function subdivide_and_slice(profiles, slices, numpoints, method="length", close
// Function: subdivide_long_segments()
// Topics: Paths, Path Subdivision
// See Also: subdivide_path(), subdivide_and_slice(), path_add_jitter(), jittered_poly()
// Usage:
// spath = subdivide_long_segments(path, maxlen, [closed=]);
// Description:
// Evenly subdivides long `path` segments until they are all shorter than `maxlen`.
// Arguments:
// path = The path to subdivide.
// maxlen = The maximum allowed path segment length.
// ---
// closed = If true, treat path like a closed polygon. Default: true
// Example:
// path = pentagon(d=100);
// spath = subdivide_long_segments(path, 10, closed=true);
// stroke(path);
// color("lightgreen") move_copies(path) circle(d=5,$fn=12);
// color("blue") move_copies(spath) circle(d=3,$fn=12);
function subdivide_long_segments(path, maxlen, closed=false) =
assert(is_path(path))
assert(is_finite(maxlen))
assert(is_bool(closed))
[
for (p=pair(path,closed)) let(
steps = ceil(norm(p[1]-p[0])/maxlen)
) each lerpn(p[0], p[1], steps, false),
if (!closed) last(path)
];
// Function: slice_profiles()
// Topics: Paths, Path Subdivision
// Usage:

View file

@ -1,5 +1,5 @@
include <../std.scad>
include <../torx_drive.scad>
include <../screw_drive.scad>
module test_torx_outer_diam() {

View file

@ -1,199 +0,0 @@
//////////////////////////////////////////////////////////////////////
// LibFile: torx_drive.scad
// Torx driver bits
// Includes:
// include <BOSL2/std.scad>
// include <BOSL2/torx_drive.scad>
//////////////////////////////////////////////////////////////////////
// Section: Functions
// Function: torx_outer_diam()
// Description: Get the typical outer diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_outer_diam(size) = lookup(size, [
[ 6, 1.75],
[ 8, 2.40],
[ 10, 2.80],
[ 15, 3.35],
[ 20, 3.95],
[ 25, 4.50],
[ 30, 5.60],
[ 40, 6.75],
[ 45, 7.93],
[ 50, 8.95],
[ 55, 11.35],
[ 60, 13.45],
[ 70, 15.70],
[ 80, 17.75],
[ 90, 20.20],
[100, 22.40]
]);
// Function: torx_inner_diam()
// Description: Get typical inner diameter of Torx profile.
// Arguments:
// size = Torx size.
function torx_inner_diam(size) = lookup(size, [
[ 6, 1.27],
[ 8, 1.75],
[ 10, 2.05],
[ 15, 2.40],
[ 20, 2.85],
[ 25, 3.25],
[ 30, 4.05],
[ 40, 4.85],
[ 45, 5.64],
[ 50, 6.45],
[ 55, 8.05],
[ 60, 9.60],
[ 70, 11.20],
[ 80, 12.80],
[ 90, 14.40],
[100, 16.00]
]);
// Function: torx_depth()
// Description: Gets typical drive hole depth.
// Arguments:
// size = Torx size.
function torx_depth(size) = lookup(size, [
[ 6, 1.82],
[ 8, 3.05],
[ 10, 3.56],
[ 15, 3.81],
[ 20, 4.07],
[ 25, 4.45],
[ 30, 4.95],
[ 40, 5.59],
[ 45, 6.22],
[ 50, 6.48],
[ 55, 6.73],
[ 60, 8.17],
[ 70, 8.96],
[ 80, 9.90],
[ 90, 10.56],
[100, 11.35]
]);
// Function: torx_tip_radius()
// Description: Gets minor rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_tip_radius(size) = lookup(size, [
[ 6, 0.132],
[ 8, 0.190],
[ 10, 0.229],
[ 15, 0.267],
[ 20, 0.305],
[ 25, 0.375],
[ 30, 0.451],
[ 40, 0.546],
[ 45, 0.574],
[ 50, 0.775],
[ 55, 0.867],
[ 60, 1.067],
[ 70, 1.194],
[ 80, 1.526],
[ 90, 1.530],
[100, 1.720]
]);
// Function: torx_rounding_radius()
// Description: Gets major rounding radius of Torx profile.
// Arguments:
// size = Torx size.
function torx_rounding_radius(size) = lookup(size, [
[ 6, 0.383],
[ 8, 0.510],
[ 10, 0.598],
[ 15, 0.716],
[ 20, 0.859],
[ 25, 0.920],
[ 30, 1.194],
[ 40, 1.428],
[ 45, 1.796],
[ 50, 1.816],
[ 55, 2.667],
[ 60, 2.883],
[ 70, 3.477],
[ 80, 3.627],
[ 90, 4.468],
[100, 4.925]
]);
// Section: Modules
// Module: torx_drive2d()
// Description: Creates a torx bit 2D profile.
// Arguments:
// size = Torx size.
// Example(2D):
// torx_drive2d(size=30, $fa=1, $fs=1);
module torx_drive2d(size) {
od = torx_outer_diam(size);
id = torx_inner_diam(size);
tip = torx_tip_radius(size);
rounding = torx_rounding_radius(size);
base = od - 2*tip;
$fn = quantup(segs(od/2),12);
difference() {
union() {
circle(d=base);
zrot_copies(n=2) {
hull() {
zrot_copies(n=3) {
translate([base/2,0,0]) {
circle(r=tip, $fn=$fn/2);
}
}
}
}
}
zrot_copies(n=6) {
zrot(180/6) {
translate([id/2+rounding,0,0]) {
circle(r=rounding);
}
}
}
}
}
// Module: torx_drive()
// Description: Creates a torx bit tip.
// Arguments:
// size = Torx size.
// l = Length of bit.
// center = If true, centers bit vertically.
// anchor = Translate so anchor point is at origin (0,0,0). See [anchor](attachments.scad#anchor). Default: `CENTER`
// spin = Rotate this many degrees around the Z axis after anchor. See [spin](attachments.scad#spin). Default: `0`
// orient = Vector to rotate top towards, after spin. See [orient](attachments.scad#orient). Default: `UP`
// Examples:
// torx_drive(size=30, l=10, $fa=1, $fs=1);
module torx_drive(size, l=5, center, anchor, spin=0, orient=UP) {
anchor = get_anchor(anchor, center, BOT, BOT);
od = torx_outer_diam(size);
attachable(anchor,spin,orient, d=od, l=l) {
linear_extrude(height=l, convexity=4, center=true) {
torx_drive2d(size);
}
children();
}
}
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

119
vnf.scad
View file

@ -9,9 +9,8 @@
//////////////////////////////////////////////////////////////////////
// Creating Polyhedrons with VNF Structures
// Section: Creating Polyhedrons with VNF Structures
// Section: VNF Testing and Access
// VNF stands for "Vertices'N'Faces". VNF structures are 2-item lists, `[VERTICES,FACES]` where the
// first item is a list of vertex points, and the second is a list of face indices into the vertex
// list. Each VNF is self contained, with face indices referring only to its own vertex list.
@ -21,8 +20,6 @@
EMPTY_VNF = [[],[]]; // The standard empty VNF with no vertices or faces.
// Section: Constructing VNFs
// Function: vnf_vertex_array()
// Usage:
// vnf = vnf_vertex_array(points, [caps], [cap1], [cap2], [style], [reverse], [col_wrap], [row_wrap], [vnf]);
@ -207,12 +204,12 @@ function vnf_vertex_array(
// points = List of point lists for each row
// row_wrap = If true then add faces connecting the first row and last row. These rows must differ by at most 2 in length.
// reverse = Set this to reverse the direction of the faces
// Examples: Each row has one more point than the preceeding one.
// Example: Each row has one more point than the preceeding one.
// pts = [for(y=[1:1:10]) [for(x=[0:y-1]) [x,y,y]]];
// vnf = vnf_tri_array(pts);
// vnf_wireframe(vnf,d=.1);
// color("red")move_copies(flatten(pts)) sphere(r=.15,$fn=9);
// Examples: Each row has one more point than the preceeding one.
// Example: Each row has one more point than the preceeding one.
// pts = [for(y=[0:2:10]) [for(x=[-y/2:y/2]) [x,y,y]]];
// vnf = vnf_tri_array(pts);
// vnf_wireframe(vnf,d=.1);
@ -277,58 +274,6 @@ function vnf_tri_array(points, row_wrap=false, reverse=false, vnf=EMPTY_VNF) =
vnf_merge(cleanup=true, [vnf, [flatten(points), faces]]);
// Function: vnf_add_face()
// Usage:
// vnf_add_face(vnf, pts);
// Description:
// Given a VNF structure and a list of face vertex points, adds the face to the VNF structure.
// Returns the modified VNF structure `[VERTICES, FACES]`. It is up to the caller to make
// sure that the points are in the correct order to make the face normal point outwards.
// Arguments:
// vnf = The VNF structure to add a face to.
// pts = The vertex points for the face.
function vnf_add_face(vnf=EMPTY_VNF, pts) =
assert(is_vnf(vnf))
assert(is_path(pts))
let(
res = set_union(vnf[0], pts, get_indices=true),
face = deduplicate(res[0], closed=true)
) [
res[1],
concat(vnf[1], len(face)>2? [face] : [])
];
// Function: vnf_add_faces()
// Usage:
// vnf_add_faces(vnf, faces);
// Description:
// Given a VNF structure and a list of faces, where each face is given as a list of vertex points,
// adds the faces to the VNF structure. Returns the modified VNF structure `[VERTICES, FACES]`.
// It is up to the caller to make sure that the points are in the correct order to make the face
// normals point outwards.
// Arguments:
// vnf = The VNF structure to add a face to.
// faces = The list of faces, where each face is given as a list of vertex points.
function vnf_add_faces(vnf=EMPTY_VNF, faces) =
assert(is_vnf(vnf))
assert(is_list(faces))
let(
res = set_union(vnf[0], flatten(faces), get_indices=true),
idxs = res[0],
nverts = res[1],
offs = cumsum([0, for (face=faces) len(face)]),
ifaces = [
for (i=idx(faces)) [
for (j=idx(faces[i]))
idxs[offs[i]+j]
]
]
) [
nverts,
concat(vnf[1],ifaces)
];
// Function: vnf_merge()
// Usage:
@ -381,6 +326,64 @@ function vnf_merge(vnfs, cleanup=false, eps=EPSILON) =
[nverts, nfaces];
// Function: vnf_add_face()
// Usage:
// vnf_add_face(vnf, pts);
// Description:
// Given a VNF structure and a list of face vertex points, adds the face to the VNF structure.
// Returns the modified VNF structure `[VERTICES, FACES]`. It is up to the caller to make
// sure that the points are in the correct order to make the face normal point outwards.
// Arguments:
// vnf = The VNF structure to add a face to.
// pts = The vertex points for the face.
function vnf_add_face(vnf=EMPTY_VNF, pts) =
assert(is_vnf(vnf))
assert(is_path(pts))
let(
res = set_union(vnf[0], pts, get_indices=true),
face = deduplicate(res[0], closed=true)
) [
res[1],
concat(vnf[1], len(face)>2? [face] : [])
];
// Function: vnf_add_faces()
// Usage:
// vnf_add_faces(vnf, faces);
// Description:
// Given a VNF structure and a list of faces, where each face is given as a list of vertex points,
// adds the faces to the VNF structure. Returns the modified VNF structure `[VERTICES, FACES]`.
// It is up to the caller to make sure that the points are in the correct order to make the face
// normals point outwards.
// Arguments:
// vnf = The VNF structure to add a face to.
// faces = The list of faces, where each face is given as a list of vertex points.
function vnf_add_faces(vnf=EMPTY_VNF, faces) =
assert(is_vnf(vnf))
assert(is_list(faces))
let(
res = set_union(vnf[0], flatten(faces), get_indices=true),
idxs = res[0],
nverts = res[1],
offs = cumsum([0, for (face=faces) len(face)]),
ifaces = [
for (i=idx(faces)) [
for (j=idx(faces[i]))
idxs[offs[i]+j]
]
]
) [
nverts,
concat(vnf[1],ifaces)
];
// Section: VNF Testing and Access
// Function: is_vnf()
// Usage:
// bool = is_vnf(x);