2019-06-08 17:10:47 -04:00
//
// NopSCADlib Copyright Chris Palmer 2018
// 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/>.
//
//
//! Machine screws and wood screws with various head styles.
2020-12-12 05:58:54 -05:00
//!
2020-12-24 11:04:59 -05:00
//! For an explanation of `screw_polysink()` see <https://hydraraptor.blogspot.com/2020/12/sinkholes.html>.
2019-06-08 17:10:47 -04:00
//
2020-02-29 12:52:36 -05:00
include < ../utils/core/core.scad >
2019-06-08 17:10:47 -04:00
use < washer.scad >
2020-04-09 14:33:59 -04:00
use < nut.scad >
2019-06-08 17:10:47 -04:00
use < ../utils/rounded_cylinder.scad >
2020-01-10 05:26:12 -05:00
use < ../utils/thread.scad >
2020-02-26 08:46:11 -05:00
include < inserts.scad >
2019-06-08 17:10:47 -04:00
function screw_head_type ( type ) = type [ 2 ] ; //! Head style hs_cap, hs_pan, hs_cs, hs_hex, hs_grub, hs_cs_cap, hs_dome
function screw_radius ( type ) = type [ 3 ] / 2 ; //! Nominal radius
function screw_head_radius ( type ) = type [ 4 ] / 2 ; //! Head radius
function screw_head_height ( type ) = type [ 5 ] ; //! Head height
function screw_socket_depth ( type ) = type [ 6 ] ; //! Socket or slot depth
function screw_socket_af ( type ) = type [ 7 ] ; //! Socket across flats
function screw_max_thread ( type ) = type [ 8 ] ; //! Maximum thread length
function screw_washer ( type ) = type [ 9 ] ; //! Default washer
function screw_nut ( type ) = type [ 10 ] ; //! Default nut
function screw_pilot_hole ( type ) = type [ 11 ] ; //! Pilot hole radius for wood screws, tap radius for machine screws
function screw_clearance_radius ( type ) = type [ 12 ] ; //! Clearance hole radius
function screw_nut_radius ( type ) = screw_nut ( type ) ? nut_radius ( screw_nut ( type ) ) : 0 ; //! Radius of matching nut
function screw_boss_diameter ( type ) = max ( washer_diameter ( screw_washer ( type ) ) + 1 , 2 * ( screw_nut_radius ( type ) + 3 * extrusion_width ) ) ; //! Boss big enough for nut trap and washer
2020-12-10 12:29:48 -05:00
function screw_head_depth ( type , d = 0 ) = //! How far a counter sink head will go into a straight hole diameter d
screw_head_height ( type )
? 0
: let ( r = screw_radius ( type ) ) screw_head_radius ( type ) - max ( r , d / 2 ) + r / 5 ;
2019-06-08 17:10:47 -04:00
2021-01-15 13:12:37 -05:00
function screw_longer_than ( x ) = x < = 5 ? 5 : //! Returns the length of the shortest screw length longer or equal to x
2020-12-11 06:40:32 -05:00
x < = 6 ? 6 :
2019-06-08 17:10:47 -04:00
x < = 8 ? 8 :
x < = 10 ? 10 :
x < = 12 ? 12 :
x < = 16 ? 16 :
ceil ( x / 5 ) * 5 ;
2021-01-15 13:12:37 -05:00
function screw_shorter_than ( x ) = x >= 20 ? floor ( x / 5 ) * 5 : //! Returns the length of the longest screw shorter than or equal to x
2019-06-08 17:10:47 -04:00
x >= 16 ? 16 :
x >= 12 ? 12 :
x >= 10 ? 10 :
x >= 8 ? 8 :
x >= 6 ? 6 :
5 ;
2021-01-15 13:12:37 -05:00
function screw_length ( screw , thickness , washers , insert = false , nyloc = false , nut = false , longer = false ) = //! Returns the length of the longest or shortest screw that will got through `thickness` and `washers` and possibly an `insert`, `nut` or `nyloc`
let ( washer = washers ? washers * washer_thickness ( screw_washer ( screw ) ) : 0 ,
insert = insert ? insert_length ( screw_insert ( screw ) ) : 0 ,
nut = nut || nyloc ? nut_thickness ( screw_nut ( screw ) , nyloc ) : 0 ,
total = thickness + washer + insert + nut
)
longer || nut || nyloc ? screw_longer_than ( total ) : screw_shorter_than ( total ) ;
2019-06-08 17:10:47 -04:00
function screw_smaller_than ( d ) = d >= 2.5 && d < 3 ? 2.5 : floor ( d ) ; // Largest diameter screw less than or equal to specified diameter
2020-02-26 08:46:11 -05:00
function screw_insert ( screw , i = 0 ) = let ( d = screw_radius ( screw ) * 2 )
i >= len ( inserts ) ? undef
: insert_screw_diameter ( inserts [ i ] ) = = d ? inserts [ i ]
: screw_insert ( screw , i + 1 ) ;
2019-06-08 17:10:47 -04:00
module screw ( type , length , hob_point = 0 , nylon = false ) { //! Draw specified screw, optionally hobbed or nylon
2020-02-16 16:15:44 -05:00
description = str ( "Screw " , nylon ? "Nylon " : "" , type [ 1 ] , length < 10 ? " x " : " x " , length , "mm" , hob_point ? str ( ", hobbed at " , hob_point ) : "" ) ;
2019-06-08 17:10:47 -04:00
vitamin ( str ( "screw(" , type [ 0 ] , "_screw, " , length , arg ( hob_point , 0 , "hob_point" ) , arg ( nylon , false , "nylon" ) , "): " , description ) ) ;
head_type = screw_head_type ( type ) ;
rad = screw_radius ( type ) - eps ;
head_rad = screw_head_radius ( type ) ;
head_height = screw_head_height ( type ) ;
socket_af = screw_socket_af ( type ) ;
socket_depth = screw_socket_depth ( type ) ;
socket_rad = socket_af / cos ( 30 ) / 2 ;
max_thread = screw_max_thread ( type ) ;
2020-01-10 05:26:12 -05:00
thread = max_thread ? length >= max_thread + 5 ? max_thread
: length
: length ;
2020-02-22 14:44:01 -05:00
d = 2 * screw_radius ( type ) ;
pitch = metric_coarse_pitch ( d ) ;
2020-06-20 05:17:29 -04:00
colour = nylon || head_type = = hs_grub ? grey ( 40 ) : grey ( 80 ) ;
2019-06-08 17:10:47 -04:00
2020-02-22 14:44:01 -05:00
module shaft ( socket = 0 , headless = false ) {
2019-06-08 17:10:47 -04:00
point = screw_nut ( type ) ? 0 : 3 * rad ;
2020-02-22 14:44:01 -05:00
shank = length - thread - socket ;
2020-01-10 05:26:12 -05:00
if ( show_threads && ! point && pitch )
2020-02-22 14:44:01 -05:00
translate_z ( - length )
male_metric_thread ( d , pitch , thread - ( shank > 0 || headless ? 0 : socket ) , false , top = headless ? - 1 : 0 , solid = ! headless , colour = colour ) ;
2020-01-10 05:26:12 -05:00
else
color ( colour * 0.9 )
rotate_extrude ( ) {
translate ( [ 0 , - length + point ] )
2020-02-22 14:44:01 -05:00
square ( [ rad , length - socket - point ] ) ;
2020-01-10 05:26:12 -05:00
if ( point )
polygon ( [
2020-02-22 14:44:01 -05:00
[ 0.4 , - length ] , [ 0 , point - length ] , [ rad , point - length ]
2020-01-10 05:26:12 -05:00
] ) ;
}
2019-06-08 17:10:47 -04:00
2020-02-22 14:44:01 -05:00
if ( shank > 0 )
color ( colour )
translate_z ( - shank - socket )
cylinder ( r = rad + eps , h = shank ) ;
2019-06-08 17:10:47 -04:00
}
2020-12-10 11:33:26 -05:00
module cs_head ( socket_rad , socket_depth ) {
head_t = rad / 5 ;
head_height = head_rad + head_t ;
rotate_extrude ( )
difference ( ) {
polygon ( [ [ 0 , 0 ] , [ head_rad , 0 ] , [ head_rad , - head_t ] , [ 0 , - head_height ] ] ) ;
translate ( [ 0 , - socket_depth + eps ] )
square ( [ socket_rad , 10 ] ) ;
}
translate_z ( - socket_depth )
linear_extrude ( socket_depth )
difference ( ) {
circle ( socket_rad + 0.1 ) ;
children ( ) ;
}
}
2019-06-08 17:10:47 -04:00
explode ( length + 10 ) {
if ( head_type = = hs_cap ) {
color ( colour ) {
cylinder ( r = head_rad , h = head_height - socket_depth ) ;
translate_z ( head_height - socket_depth )
2020-03-29 15:18:57 -04:00
linear_extrude ( socket_depth )
2019-06-08 17:10:47 -04:00
difference ( ) {
circle ( head_rad ) ;
2020-01-10 05:26:12 -05:00
2019-06-08 17:10:47 -04:00
circle ( socket_rad , $fn = 6 ) ;
}
}
shaft ( ) ;
}
if ( head_type = = hs_grub ) {
color ( colour ) {
2020-02-22 14:44:01 -05:00
r = show_threads ? rad - pitch / 2 : rad ;
translate_z ( - socket_depth )
2020-03-29 15:18:57 -04:00
linear_extrude ( socket_depth )
2020-02-22 14:44:01 -05:00
difference ( ) {
circle ( r ) ;
2020-01-10 05:26:12 -05:00
2020-02-22 14:44:01 -05:00
circle ( socket_rad , $fn = 6 ) ;
}
2020-01-10 05:26:12 -05:00
2020-02-22 14:44:01 -05:00
shaft ( socket_depth , true ) ;
2020-01-10 05:26:12 -05:00
2020-02-22 14:44:01 -05:00
if ( show_threads )
translate_z ( - length )
cylinder ( r = r , h = length - socket_depth ) ;
2019-06-08 17:10:47 -04:00
}
}
if ( head_type = = hs_hex ) {
color ( colour )
cylinder ( r = head_rad , h = head_height , $fn = 6 ) ;
shaft ( ) ;
}
if ( head_type = = hs_pan ) {
socket_rad = 0.6 * head_rad ;
socket_depth = 0.5 * head_height ;
socket_width = 1 ;
color ( colour ) {
rotate_extrude ( )
difference ( ) {
rounded_corner ( r = head_rad , h = head_height , r2 = head_height / 2 ) ;
translate ( [ 0 , head_height - socket_depth ] )
square ( [ socket_rad , 10 ] ) ;
}
2020-03-29 15:18:57 -04:00
linear_extrude ( head_height )
2019-06-08 17:10:47 -04:00
difference ( ) {
circle ( socket_rad + eps ) ;
square ( [ 2 * socket_rad , socket_width ] , center = true ) ;
square ( [ socket_width , 2 * socket_rad ] , center = true ) ;
}
}
shaft ( ) ;
}
if ( head_type = = hs_dome ) {
lift = 0.38 ;
2020-09-10 13:38:24 -04:00
h = head_height - lift ;
r = min ( 2 * head_height , ( sqr ( head_rad ) + sqr ( h ) ) / 2 * h ) ; // Special case for M2
y = sqrt ( sqr ( r ) - sqr ( head_rad ) ) ;
2019-06-08 17:10:47 -04:00
color ( colour ) {
rotate_extrude ( ) {
difference ( ) {
intersection ( ) {
2020-09-10 13:38:24 -04:00
translate ( [ 0 , - y + lift ] )
circle ( r ) ;
2019-06-08 17:10:47 -04:00
square ( [ head_rad , head_height ] ) ;
}
translate ( [ 0 , head_height - socket_depth ] )
square ( [ socket_rad , 10 ] ) ;
}
}
2020-03-29 15:18:57 -04:00
linear_extrude ( head_height )
2019-06-08 17:10:47 -04:00
difference ( ) {
circle ( socket_rad + eps ) ;
circle ( socket_rad , $fn = 6 ) ;
}
}
shaft ( ) ;
}
if ( head_type = = hs_cs ) {
socket_rad = 0.6 * head_rad ;
socket_depth = 0.3 * head_rad ;
socket_width = 1 ;
2020-12-10 11:33:26 -05:00
color ( colour )
cs_head ( socket_rad , socket_depth ) {
square ( [ 2 * socket_rad , socket_width ] , center = true ) ;
square ( [ socket_width , 2 * socket_rad ] , center = true ) ;
}
2019-06-08 17:10:47 -04:00
shaft ( socket_depth ) ;
}
if ( head_type = = hs_cs_cap ) {
2020-12-10 11:33:26 -05:00
color ( colour )
cs_head ( socket_rad , socket_depth )
circle ( socket_rad , $fn = 6 ) ;
2019-06-08 17:10:47 -04:00
shaft ( socket_depth ) ;
}
}
}
2020-12-10 18:27:27 -05:00
module screw_countersink ( type , drilled = true ) { //! Countersink shape
2019-06-08 17:10:47 -04:00
head_type = screw_head_type ( type ) ;
head_rad = screw_head_radius ( type ) ;
2020-12-10 18:27:27 -05:00
rad = screw_radius ( type ) ;
head_t = rad / 5 ;
head_height = head_rad + head_t ;
2019-06-08 17:10:47 -04:00
if ( head_type = = hs_cs || head_type = = hs_cs_cap )
translate_z ( - head_height )
2020-12-10 18:27:27 -05:00
if ( drilled )
2020-12-12 04:46:46 -05:00
cylinder ( h = head_height + eps , r1 = 0 , r2 = head_rad + head_t ) ;
2020-12-10 18:27:27 -05:00
else
2021-01-09 14:11:20 -05:00
render ( ) intersection ( ) {
2020-12-10 18:27:27 -05:00
cylinder ( h = head_height + eps , r1 = 0 , r2 = head_rad + head_t ) ;
cylinder ( h = head_height + eps , r = head_rad + eps ) ;
}
2019-06-08 17:10:47 -04:00
}
2020-12-12 04:46:46 -05:00
function screw_polysink_r ( type , z ) = //! Countersink hole profile corrected for rounded staircase extrusions.
let ( rad = screw_radius ( type ) ,
head_t = rad / 5 ,
head_rad = screw_head_radius ( type )
)
limit ( head_rad + head_t - z + ( sqrt ( 2 ) - 1 ) * layer_height / 2 , screw_clearance_radius ( type ) , head_rad ) ;
2020-12-16 15:53:04 -05:00
module screw_polysink ( type , h = 100 , alt = false ) { //! A countersink hole made from stacked polyholes for printed parts
2020-12-12 04:46:46 -05:00
head_depth = screw_head_depth ( type ) ;
assert ( head_depth , "Not a countersunk screw" ) ;
layers = ceil ( head_depth / layer_height ) ;
2020-12-16 15:53:04 -05:00
rmin = screw_clearance_radius ( type ) ;
sides = sides ( rmin ) ;
lh = layer_height + eps ;
render ( convexity = 5 )
for ( side = [ 0 , 1 ] ) mirror ( [ 0 , 0 , side ] ) {
for ( i = [ 0 : layers - 1 ] )
translate_z ( i * layer_height ) {
r = screw_polysink_r ( type , i * layer_height + layer_height / 2 ) ;
if ( alt )
rotate ( i % 2 = = layers % 2 ? 180 / sides : 0 )
poly_cylinder ( r = r , h = lh , center = false , sides = sides ) ;
else
poly_cylinder ( r = r , h = lh , center = false ) ;
}
2020-12-12 04:46:46 -05:00
2021-01-02 05:04:03 -05:00
remainder = h / 2 - layers * layer_height ;
if ( remainder > 0 )
translate_z ( layers * layer_height )
poly_cylinder ( r = rmin , h = remainder , center = false ) ;
2020-12-16 15:53:04 -05:00
}
2020-12-12 04:46:46 -05:00
}
2019-06-08 17:10:47 -04:00
module screw_and_washer ( type , length , star = false , penny = false ) { //! Screw with a washer which can be standard or penny and an optional star washer on top
washer = screw_washer ( type ) ;
2020-05-18 10:29:19 -04:00
head_type = screw_head_type ( type ) ;
if ( head_type ! = hs_cs && head_type ! = hs_cs_cap ) {
translate_z ( exploded ( ) * 6 )
if ( penny )
penny_washer ( washer ) ;
else
washer ( washer ) ;
translate_z ( washer_thickness ( washer ) ) {
if ( star ) {
translate_z ( exploded ( ) * 8 )
star_washer ( washer ) ;
translate_z ( washer_thickness ( washer ) )
screw ( type , length ) ;
}
else
2019-06-08 17:10:47 -04:00
screw ( type , length ) ;
}
}
2020-05-18 10:29:19 -04:00
else
translate_z ( eps )
screw ( type , length ) ;
2019-06-08 17:10:47 -04:00
}