2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2019-03-23 04:13:18 +00:00
// LibFile: joiners.scad
// Snap-together joiners.
// To use, add the following lines to the beginning of your file:
// ```
2019-04-19 07:25:10 +00:00
// include <BOSL2/std.scad>
// include <BOSL2/joiners.scad>
2019-03-23 04:13:18 +00:00
// ```
2017-08-30 00:00:16 +00:00
//////////////////////////////////////////////////////////////////////
2020-03-29 03:50:04 +00:00
include < rounding.scad >
2020-01-10 02:22:07 +00:00
2019-03-23 04:13:18 +00:00
// Section: Half Joiners
// Module: half_joiner_clear()
// Description:
// Creates a mask to clear an area so that a half_joiner can be placed there.
// Usage:
2019-05-26 20:45:22 +00:00
// half_joiner_clear(h, w, [a], [clearance], [overlap])
2019-03-23 04:13:18 +00:00
// Arguments:
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
2019-05-26 19:47:50 +00:00
// 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`
2019-03-23 04:13:18 +00:00
// Example:
2019-05-26 06:31:05 +00:00
// half_joiner_clear(spin=-90);
module half_joiner_clear ( h = 20 , w = 10 , a = 30 , clearance = 0 , overlap = 0.01 , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
dmnd_height = h * 1.0 ;
dmnd_width = dmnd_height * tan ( a ) ;
guide_size = w / 3 ;
guide_width = 2 * ( dmnd_height / 2 - guide_size ) * tan ( a ) ;
attachable ( anchor , spin , orient , size = [ w , guide_width , h ] ) {
union ( ) {
ycopies ( overlap , n = overlap > 0 ? 2 : 1 ) {
difference ( ) {
// Diamonds.
scale ( [ w + clearance , dmnd_width / 2 , dmnd_height / 2 ] ) {
xrot ( 45 ) cube ( size = [ 1 , sqrt ( 2 ) , sqrt ( 2 ) ] , center = true ) ;
}
// Blunt point of tab.
ycopies ( guide_width + 4 ) {
cube ( size = [ ( w + clearance ) * 1.05 , 4 , h * 0.99 ] , center = true ) ;
}
}
}
if ( overlap > 0 ) cube ( [ w + clearance , overlap + 0.001 , h ] , center = true ) ;
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: half_joiner()
// Usage:
2019-07-18 01:54:35 +00:00
// half_joiner(h, w, l, [a], [screwsize], [guides], [$slop])
2019-03-23 04:13:18 +00:00
// Description:
// Creates a half_joiner object that can be attached to half_joiner2 object.
// Arguments:
// h = Height of the half_joiner.
// w = Width of the half_joiner.
// l = Length of the backing to the half_joiner.
// a = Overhang angle of the half_joiner.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
2019-05-26 19:47:50 +00:00
// 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`
2019-07-18 01:54:35 +00:00
// $slop = Printer specific slop value to make parts fit more closely.
2019-03-23 04:13:18 +00:00
// Example:
2019-05-26 06:31:05 +00:00
// half_joiner(screwsize=3, spin=-90);
2019-07-18 01:54:35 +00:00
module half_joiner ( h = 20 , w = 10 , l = 10 , a = 30 , screwsize = undef , guides = true , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
dmnd_height = h * 1.0 ;
dmnd_width = dmnd_height * tan ( a ) ;
guide_size = w / 3 ;
guide_width = 2 * ( dmnd_height / 2 - guide_size ) * tan ( a ) ;
render ( convexity = 12 )
attachable ( anchor , spin , orient , size = [ w , 2 * l , h ] ) {
difference ( ) {
union ( ) {
// Make base.
difference ( ) {
// Solid backing base.
fwd ( l / 2 ) cube ( size = [ w , l , h ] , center = true ) ;
// Clear diamond for tab
xcopies ( 2 * w * 2 / 3 ) {
half_joiner_clear ( h = h + 0.01 , w = w , clearance = $ slop * 2 , a = a ) ;
}
}
difference ( ) {
// Make tab
scale ( [ w / 3 - $ slop * 2 , dmnd_width / 2 , dmnd_height / 2 ] ) xrot ( 45 )
cube ( size = [ 1 , sqrt ( 2 ) , sqrt ( 2 ) ] , center = true ) ;
// Blunt point of tab.
back ( guide_width / 2 + 2 )
cube ( size = [ w * 0.99 , 4 , guide_size * 2 ] , center = true ) ;
}
// Guide ridges.
if ( guides = = true ) {
xcopies ( w / 3 - $ slop * 2 ) {
// Guide ridge.
fwd ( 0.05 / 2 ) {
scale ( [ 0.75 , 1 , 2 ] ) yrot ( 45 )
cube ( size = [ guide_size / sqrt ( 2 ) , guide_width + 0.05 , guide_size / sqrt ( 2 ) ] , center = true ) ;
}
// Snap ridge.
scale ( [ 0.25 , 0.5 , 1 ] ) zrot ( 45 )
cube ( size = [ guide_size / sqrt ( 2 ) , guide_size / sqrt ( 2 ) , dmnd_width ] , center = true ) ;
}
}
}
// Make screwholes, if needed.
if ( screwsize ! = undef ) {
yrot ( 90 ) cylinder ( r = screwsize * 1.1 / 2 , h = w + 1 , center = true , $fn = 12 ) ;
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-05-26 06:31:05 +00:00
//half_joiner(screwsize=3);
2019-03-23 04:13:18 +00:00
// Module: half_joiner2()
// Usage:
2019-05-26 20:45:22 +00:00
// half_joiner2(h, w, l, [a], [screwsize], [guides])
2019-03-23 04:13:18 +00:00
// Description:
// Creates a half_joiner2 object that can be attached to half_joiner object.
// Arguments:
// h = Height of the half_joiner.
// w = Width of the half_joiner.
// l = Length of the backing to the half_joiner.
// a = Overhang angle of the half_joiner.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
2019-05-26 19:47:50 +00:00
// 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`
2019-03-23 04:13:18 +00:00
// Example:
2019-05-26 06:31:05 +00:00
// half_joiner2(screwsize=3, spin=-90);
module half_joiner2 ( h = 20 , w = 10 , l = 10 , a = 30 , screwsize = undef , guides = true , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
dmnd_height = h * 1.0 ;
dmnd_width = dmnd_height * tan ( a ) ;
guide_size = w / 3 ;
guide_width = 2 * ( dmnd_height / 2 - guide_size ) * tan ( a ) ;
render ( convexity = 12 )
attachable ( anchor , spin , orient , size = [ w , 2 * l , h ] ) {
difference ( ) {
union ( ) {
fwd ( l / 2 ) cube ( size = [ w , l , h ] , center = true ) ;
cube ( [ w , guide_width , h ] , center = true ) ;
}
// Subtract mated half_joiner.
zrot ( 180 ) half_joiner ( h = h + 0.01 , w = w + 0.01 , l = guide_width + 0.01 , a = a , screwsize = undef , guides = guides , $ slop = 0.0 ) ;
// Make screwholes, if needed.
if ( screwsize ! = undef ) {
xcyl ( r = screwsize * 1.1 / 2 , l = w + 1 , $fn = 12 ) ;
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Section: Full Joiners
// Module: joiner_clear()
// Description:
// Creates a mask to clear an area so that a joiner can be placed there.
// Usage:
2019-05-26 20:45:22 +00:00
// joiner_clear(h, w, [a], [clearance], [overlap])
2019-03-23 04:13:18 +00:00
// Arguments:
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
2019-05-26 19:47:50 +00:00
// 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`
2019-03-23 04:13:18 +00:00
// Example:
2019-05-26 06:31:05 +00:00
// joiner_clear(spin=-90);
module joiner_clear ( h = 40 , w = 10 , a = 30 , clearance = 0 , overlap = 0.01 , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
dmnd_height = h * 0.5 ;
dmnd_width = dmnd_height * tan ( a ) ;
guide_size = w / 3 ;
guide_width = 2 * ( dmnd_height / 2 - guide_size ) * tan ( a ) ;
attachable ( anchor , spin , orient , size = [ w , guide_width , h ] ) {
union ( ) {
up ( h / 4 ) half_joiner_clear ( h = h / 2.0 - 0.01 , w = w , a = a , overlap = overlap , clearance = clearance ) ;
down ( h / 4 ) half_joiner_clear ( h = h / 2.0 - 0.01 , w = w , a = a , overlap = overlap , clearance = - 0.01 ) ;
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: joiner()
// Usage:
2019-07-18 01:54:35 +00:00
// joiner(h, w, l, [a], [screwsize], [guides], [$slop])
2019-03-23 04:13:18 +00:00
// Description:
// Creates a joiner object that can be attached to another joiner object.
// Arguments:
// h = Height of the joiner.
// w = Width of the joiner.
// l = Length of the backing to the joiner.
// a = Overhang angle of the joiner.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
2019-05-26 19:47:50 +00:00
// 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`
2019-07-18 01:54:35 +00:00
// $slop = Printer specific slop value to make parts fit more closely.
2019-03-23 04:13:18 +00:00
// Examples:
2019-05-26 06:31:05 +00:00
// joiner(screwsize=3, spin=-90);
// joiner(w=10, l=10, h=40, spin=-90) cuboid([10, 10*2, 40], anchor=RIGHT);
2019-07-18 01:54:35 +00:00
module joiner ( h = 40 , w = 10 , l = 10 , a = 30 , screwsize = undef , guides = true , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
attachable ( anchor , spin , orient , size = [ w , 2 * l , h ] ) {
union ( ) {
up ( h / 4 ) half_joiner ( h = h / 2 , w = w , l = l , a = a , screwsize = screwsize , guides = guides ) ;
down ( h / 4 ) half_joiner2 ( h = h / 2 , w = w , l = l , a = a , screwsize = screwsize , guides = guides ) ;
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Section: Full Joiners Pairs/Sets
// Module: joiner_pair_clear()
// Description:
// Creates a mask to clear an area so that a pair of joiners can be placed there.
// Usage:
2019-05-26 20:45:22 +00:00
// joiner_pair_clear(spacing, [n], [h], [w], [a], [clearance], [overlap])
2019-03-23 04:13:18 +00:00
// Arguments:
// spacing = Spacing between joiner centers.
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// n = Number of joiners (2 by default) to clear for.
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
2019-05-26 19:47:50 +00:00
// 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`
2019-03-23 04:13:18 +00:00
// Examples:
// joiner_pair_clear(spacing=50, n=2);
// joiner_pair_clear(spacing=50, n=3);
2019-05-26 06:31:05 +00:00
module joiner_pair_clear ( spacing = 100 , h = 40 , w = 10 , a = 30 , n = 2 , clearance = 0 , overlap = 0.01 , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
dmnd_height = h * 0.5 ;
dmnd_width = dmnd_height * tan ( a ) ;
guide_size = w / 3 ;
guide_width = 2 * ( dmnd_height / 2 - guide_size ) * tan ( a ) ;
attachable ( anchor , spin , orient , size = [ spacing + w , guide_width , h ] ) {
xcopies ( spacing , n = n ) {
joiner_clear ( h = h , w = w , a = a , clearance = clearance , overlap = overlap ) ;
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: joiner_pair()
// Usage:
2019-07-18 01:54:35 +00:00
// joiner_pair(h, w, l, [a], [screwsize], [guides], [$slop])
2019-03-23 04:13:18 +00:00
// Description:
// Creates a joiner_pair object that can be attached to other joiner_pairs .
// Arguments:
// spacing = Spacing between joiner centers.
// h = Height of the joiners.
// w = Width of the joiners.
// l = Length of the backing to the joiners.
// a = Overhang angle of the joiners.
// n = Number of joiners in a row. Default: 2
// alternate = If true (default), each joiner alternates it's orientation. If alternate is "alt", do opposite alternating orientations.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
2019-05-26 19:47:50 +00:00
// 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`
2019-07-18 01:54:35 +00:00
// $slop = Printer specific slop value to make parts fit more closely.
2019-03-23 04:13:18 +00:00
// Examples:
2019-05-26 06:31:05 +00:00
// joiner_pair(spacing=50, l=10, spin=-90) cuboid([10, 50+10-0.1, 40], anchor=RIGHT);
// joiner_pair(spacing=50, l=10, n=2, spin=-90);
// joiner_pair(spacing=50, l=10, n=3, alternate=false, spin=-90);
// joiner_pair(spacing=50, l=10, n=3, alternate=true, spin=-90);
// joiner_pair(spacing=50, l=10, n=3, alternate="alt", spin=-90);
2019-07-18 01:54:35 +00:00
module joiner_pair ( spacing = 100 , h = 40 , w = 10 , l = 10 , a = 30 , n = 2 , alternate = true , screwsize = undef , guides = true , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
attachable ( anchor , spin , orient , size = [ spacing + w , 2 * l , h ] ) {
left ( ( n - 1 ) * spacing / 2 ) {
for ( i = [ 0 : 1 : n - 1 ] ) {
right ( i * spacing ) {
yrot ( 180 + ( alternate ? ( i * 180 + ( alternate = = "alt" ? 180 : 0 ) ) % 360 : 0 ) ) {
joiner ( h = h , w = w , l = l , a = a , screwsize = screwsize , guides = guides ) ;
}
}
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Section: Full Joiners Quads/Sets
// Module: joiner_quad_clear()
// Description:
// Creates a mask to clear an area so that a pair of joiners can be placed there.
// Usage:
2019-05-26 20:45:22 +00:00
// joiner_quad_clear(spacing, [n], [h], [w], [a], [clearance], [overlap])
2019-03-23 04:13:18 +00:00
// Arguments:
// spacing1 = Spacing between joiner centers.
// spacing2 = Spacing between back-to-back pairs/sets of joiners.
// h = Height of the joiner to clear space for.
// w = Width of the joiner to clear space for.
// a = Overhang angle of the joiner.
// n = Number of joiners in a row. Default: 2
// clearance = Extra width to clear.
// overlap = Extra depth to clear.
2019-05-26 19:47:50 +00:00
// 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`
2019-03-23 04:13:18 +00:00
// Examples:
// joiner_quad_clear(spacing1=50, spacing2=50, n=2);
// joiner_quad_clear(spacing1=50, spacing2=50, n=3);
2019-05-26 06:31:05 +00:00
module joiner_quad_clear ( xspacing = undef , yspacing = undef , spacing1 = undef , spacing2 = undef , n = 2 , h = 40 , w = 10 , a = 30 , clearance = 0 , overlap = 0.01 , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
spacing1 = first_defined ( [ spacing1 , xspacing , 100 ] ) ;
spacing2 = first_defined ( [ spacing2 , yspacing , 50 ] ) ;
attachable ( anchor , spin , orient , size = [ w + spacing1 , spacing2 , h ] ) {
zrot_copies ( n = 2 ) {
back ( spacing2 / 2 ) {
joiner_pair_clear ( spacing = spacing1 , n = n , h = h , w = w , a = a , clearance = clearance , overlap = overlap ) ;
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2019-03-23 04:13:18 +00:00
// Module: joiner_quad()
// Usage:
2019-07-18 01:54:35 +00:00
// joiner_quad(h, w, l, [a], [screwsize], [guides], [$slop])
2019-03-23 04:13:18 +00:00
// Description:
// Creates a joiner_quad object that can be attached to other joiner_pairs .
// Arguments:
// spacing = Spacing between joiner centers.
// h = Height of the joiners.
// w = Width of the joiners.
// l = Length of the backing to the joiners.
// a = Overhang angle of the joiners.
// n = Number of joiners in a row. Default: 2
// alternate = If true (default), each joiner alternates it's orientation. If alternate is "alt", do opposite alternating orientations.
// screwsize = Diameter of screwhole.
// guides = If true, create sliding alignment guides.
2019-07-18 01:54:35 +00:00
// $slop = Printer specific slop value to make parts fit more closely.
2019-05-26 19:47:50 +00:00
// 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`
2019-03-23 04:13:18 +00:00
// Examples:
2019-05-26 06:31:05 +00:00
// joiner_quad(spacing1=50, spacing2=50, l=10, spin=-90) cuboid([50, 50+10-0.1, 40]);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=2, spin=-90);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate=false, spin=-90);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate=true, spin=-90);
// joiner_quad(spacing1=50, spacing2=50, l=10, n=3, alternate="alt", spin=-90);
2019-07-18 01:54:35 +00:00
module joiner_quad ( spacing1 = undef , spacing2 = undef , xspacing = undef , yspacing = undef , h = 40 , w = 10 , l = 10 , a = 30 , n = 2 , alternate = true , screwsize = undef , guides = true , anchor = CENTER , spin = 0 , orient = UP )
2017-08-30 00:00:16 +00:00
{
2020-05-30 02:04:34 +00:00
spacing1 = first_defined ( [ spacing1 , xspacing , 100 ] ) ;
spacing2 = first_defined ( [ spacing2 , yspacing , 50 ] ) ;
attachable ( anchor , spin , orient , size = [ w + spacing1 , spacing2 , h ] ) {
zrot_copies ( n = 2 ) {
back ( spacing2 / 2 ) {
joiner_pair ( spacing = spacing1 , n = n , h = h , w = w , l = l , a = a , screwsize = screwsize , guides = guides ) ;
}
}
children ( ) ;
}
2017-08-30 00:00:16 +00:00
}
2020-01-11 03:30:20 +00:00
// Section: Dovetails
2020-01-09 23:52:08 +00:00
// Module: dovetail()
//
2020-01-11 03:30:20 +00:00
// Usage:
2020-01-22 01:29:43 +00:00
// dovetail(l|length, h|height, w|width, slope|angle, taper|back_width, [chamfer], [r|radius], [round], [$slop])
2020-01-11 03:30:20 +00:00
//
2020-01-09 23:52:08 +00:00
// Description:
// Produces a possibly tapered dovetail joint shape to attach to or subtract from two parts you wish to join together.
// The tapered dovetail is particularly advantageous for long joints because the joint assembles without binding until
// it is fully closed, and then wedges tightly. You can chamfer or round the corners of the dovetail shape for better
2020-01-22 01:29:43 +00:00
// printing and assembly, or choose a fully rounded joint that looks more like a puzzle piece. The dovetail appears
// parallel to the Y axis and projecting upwards, so in its default orientation it will slide together with a translation
// in the positive Y direction. The default anchor for dovetails is BOTTOM; the default orientation depends on the gender,
// with male dovetails oriented UP and female ones DOWN.
2020-01-09 23:52:08 +00:00
//
// Arguments:
// l / length = Length of the dovetail (amount the joint slides during assembly)
// h / height = Height of the dovetail
// w / width = Width (at the wider, top end) of the dovetail before tapering
// slope = slope of the dovetail. Standard woodworking slopes are 4, 6, or 8. Default: 6.
// angle = angle (in degrees) of the dovetail. Specify only one of slope and angle.
2020-01-22 01:29:43 +00:00
// taper = taper angle (in degrees). Dovetail gets narrower by this angle. Default: no taper
// back_width = width of right hand end of the dovetail. This alternate method of specifying the taper may be easier to manage. Specify only one of `taper` and `back_width`. Note that `back_width` should be smaller than `width` to taper in the customary direction, with the smaller end at the back.
2020-01-09 23:52:08 +00:00
// chamfer = amount to chamfer the corners of the joint (Default: no chamfer)
// r / radius = amount to round over the corners of the joint (Default: no rounding)
2020-01-22 01:29:43 +00:00
// round = true to round both corners of the dovetail and give it a puzzle piece look. Default: false.
// extra = amount of extra length and base extension added to dovetails for unions and differences. Default: 0.01
// Example: Ordinary straight dovetail, male version (sticking up) and female version (below the xy plane)
2020-01-09 23:52:08 +00:00
// dovetail("male", length=30, width=15, height=8);
2020-01-22 01:29:43 +00:00
// right(20) dovetail("female", length=30, width=15, height=8);
2020-01-09 23:52:08 +00:00
// Example: Adding a 6 degree taper (Such a big taper is usually not necessary, but easier to see for the example.)
2020-01-10 02:22:07 +00:00
// dovetail("male", length=30, width=15, height=8, taper=6);
2020-01-22 01:29:43 +00:00
// right(20) dovetail("female", length=30, width=15, height=8, taper=6);
2020-01-09 23:52:08 +00:00
// Example: A block that can link to itself
// diff("remove")
// cuboid([50,30,10]){
2020-01-22 01:29:43 +00:00
// attach(BACK) dovetail("male", length=10, width=15, height=8);
// attach(FRONT) dovetail("female", length=10, width=15, height=8,$tags="remove");
2020-01-09 23:52:08 +00:00
// }
// Example: Setting the dovetail angle. This is too extreme to be useful.
// diff("remove")
// cuboid([50,30,10]){
2020-01-22 01:29:43 +00:00
// attach(BACK) dovetail("male", length=10, width=15, height=8,angle=30);
// attach(FRONT) dovetail("female", length=10, width=15, height=8,angle=30,$tags="remove");
2020-01-09 23:52:08 +00:00
// }
// Example: Adding a chamfer helps printed parts fit together without problems at the corners
// diff("remove")
// cuboid([50,30,10]){
2020-01-22 01:29:43 +00:00
// attach(BACK) dovetail("male", length=10, width=15, height=8,chamfer=1);
// attach(FRONT) dovetail("female", length=10, width=15, height=8,chamfer=1,$tags="remove");
2020-01-09 23:52:08 +00:00
// }
// Example: Rounding the outside corners is another option
// diff("remove")
// cuboid([50,30,10]){
2020-01-22 01:29:43 +00:00
// attach(BACK) dovetail("male", length=10, width=15, height=8,radius=1,$fn=32);
// attach(FRONT) dovetail("female", length=10, width=15, height=8,radius=1,$tags="remove",$fn=32);
2020-01-09 23:52:08 +00:00
// }
// Example: Or you can make a fully rounded joint
2020-01-22 01:29:43 +00:00
// $fn=32;
2020-01-09 23:52:08 +00:00
// diff("remove")
// cuboid([50,30,10]){
2020-01-22 01:29:43 +00:00
// attach(BACK) dovetail("male", length=10, width=15, height=8,radius=1.5, round=true);
// attach(FRONT) dovetail("female", length=10, width=15, height=8,radius=1.5, round=true, $tags="remove");
2020-01-09 23:52:08 +00:00
// }
2020-01-22 01:29:43 +00:00
// Example: With a long joint like this, a taper makes the joint easy to assemble. It will go together easily and wedge tightly if you get the tolerances right. Specifying the taper with `back_width` may be easier than using a taper angle.
2020-01-09 23:52:08 +00:00
// cuboid([50,30,10])
2020-01-22 01:29:43 +00:00
// attach(TOP) dovetail("male", length=50, width=18, height=4, back_width=15, spin=90);
2020-01-09 23:52:08 +00:00
// fwd(35)
// diff("remove")
// cuboid([50,30,10])
2020-01-22 01:29:43 +00:00
// attach(TOP) dovetail("female", length=50, width=18, height=4, back_width=15, spin=90,$tags="remove");
2020-01-09 23:52:08 +00:00
// Example: A series of dovtails
// cuboid([50,30,10])
2020-03-25 02:11:05 +00:00
// attach(BACK) xcopies(10,5) dovetail("male", length=10, width=7, height=4);
2020-01-22 01:29:43 +00:00
// Example: Mating pin board for a right angle joint. Note that the anchor method and use of `spin` ensures that the joint works even with a taper.
2020-01-09 23:52:08 +00:00
// diff("remove")
// cuboid([50,30,10])
2020-03-25 02:11:05 +00:00
// position(TOP+BACK) xcopies(10,5) dovetail("female", length=10, width=7, taper=4, height=4, $tags="remove",anchor=BOTTOM+FRONT,spin=180);
2020-01-22 01:29:43 +00:00
module dovetail ( gender , length , l , width , w , height , h , angle , slope , taper , back_width , chamfer , extra = 0.01 , r , radius , round = false , anchor = BOTTOM , spin = 0 , orient )
2020-01-09 23:52:08 +00:00
{
2020-05-30 02:04:34 +00:00
radius = get_radius ( r1 = radius , r2 = r ) ;
lcount = num_defined ( [ l , length ] ) ;
hcount = num_defined ( [ h , height ] ) ;
wcount = num_defined ( [ w , width ] ) ;
assert ( lcount = = 1 , "Must define exactly one of l and length" ) ;
assert ( wcount = = 1 , "Must define exactly one of w and width" ) ;
assert ( hcount = = 1 , "Must define exactly one of h and height" ) ;
h = first_defined ( [ h , height ] ) ;
w = first_defined ( [ w , width ] ) ;
length = first_defined ( [ l , length ] ) ;
orient = is_def ( orient ) ? orient :
gender = = "female" ? DOWN : UP ;
count = num_defined ( [ angle , slope ] ) ;
assert ( count < = 1 , "Do not specify both angle and slope" ) ;
count2 = num_defined ( [ taper , back_width ] ) ;
assert ( count2 < = 1 , "Do not specify both taper and back_width" ) ;
count3 = num_defined ( [ chamfer , radius ] ) ;
assert ( count3 < = 1 || ( radius = = 0 && chamfer = = 0 ) , "Do not specify both chamfer and radius" ) ;
slope = is_def ( slope ) ? slope :
is_def ( angle ) ? 1 / tan ( angle ) : 6 ;
2020-09-02 03:42:01 +00:00
extra_slop = gender = = "female" ? 2 * $ slop : 0 ;
width = w + extra_slop ;
height = h + extra_slop ;
back_width = back_width + extra_slop ;
2020-05-30 02:04:34 +00:00
front_offset = is_def ( taper ) ? - extra * tan ( taper ) :
is_def ( back_width ) ? extra * ( back_width - width ) / length / 2 : 0 ;
size = is_def ( chamfer ) && chamfer > 0 ? chamfer :
is_def ( radius ) && radius > 0 ? radius : 0 ;
type = is_def ( chamfer ) && chamfer > 0 ? "chamfer" : "circle" ;
fullsize = round ? [ size , size ] :
gender = = "male" ? [ size , 0 ] : [ 0 , size ] ;
smallend_half = round_corners (
move (
[ 0 , - length / 2 - extra , 0 ] ,
p = [
[ 0 , 0 , height ] ,
[ width / 2 - front_offset , 0 , height ] ,
[ width / 2 - height / slope - front_offset , 0 , 0 ] ,
[ width / 2 - front_offset + height , 0 , 0 ]
]
) ,
method = type , cut = fullsize , closed = false
) ;
smallend_points = concat ( select ( smallend_half , 1 , - 2 ) , [ down ( extra , p = select ( smallend_half , - 2 ) ) ] ) ;
offset = is_def ( taper ) ? - ( length + extra ) * tan ( taper ) :
is_def ( back_width ) ? ( back_width - width ) / 2 : 0 ;
bigend_points = move ( [ offset , length + 2 * extra , 0 ] , p = smallend_points ) ;
adjustment = gender = = "male" ? - 0.01 : 0.01 ; // Adjustment for default overlap in attach()
attachable ( anchor , spin , orient , size = [ width + 2 * offset , length , height ] ) {
down ( height / 2 + adjustment ) {
skin (
[
reverse ( concat ( smallend_points , xflip ( p = reverse ( smallend_points ) ) ) ) ,
reverse ( concat ( bigend_points , xflip ( p = reverse ( bigend_points ) ) ) )
] ,
slices = 0 , convexity = 4
) ;
}
children ( ) ;
}
2020-01-09 23:52:08 +00:00
}
2020-08-18 00:44:42 +00:00
// Section: Tension Clips
2020-01-09 23:52:08 +00:00
2020-03-04 03:52:19 +00:00
// h is total height above 0 of the nub
// nub extends below xy plane by distance nub/2
module _pin_nub ( r , nub , h )
{
L = h / 4 ;
rotate_extrude ( ) {
polygon (
[ [ 0 , - nub / 2 ] ,
[ - r , - nub / 2 ] ,
[ - r - nub , nub / 2 ] ,
[ - r - nub , nub / 2 + L ] ,
[ - r , h ] ,
[ 0 , h ] ] ) ;
}
}
module _pin_slot ( l , r , t , d , nub , depth , stretch ) {
yscale ( 4 )
intersection ( ) {
translate ( [ t , 0 , d + t / 4 ] )
_pin_nub ( r = r + t , nub = nub , h = l - ( d + t / 4 ) ) ;
translate ( [ - t , 0 , d + t / 4 ] )
_pin_nub ( r = r + t , nub = nub , h = l - ( d + t / 4 ) ) ;
}
cube ( [ 2 * r , depth , 2 * l ] , center = true ) ;
up ( l )
zscale ( stretch )
ycyl ( r = r , h = depth ) ;
}
module _pin_shaft ( r , lStraight , nub , nubscale , stretch , d , pointed )
{
extra = 0.02 ;
rPoint = r / sqrt ( 2 ) ;
down ( extra ) cylinder ( r = r , h = lStraight + extra ) ;
up ( lStraight ) {
zscale ( stretch ) {
sphere ( r = r ) ;
if ( pointed ) up ( rPoint ) cylinder ( r1 = rPoint , r2 = 0 , h = rPoint ) ;
}
}
up ( d ) yscale ( nubscale ) _pin_nub ( r = r , nub = nub , h = lStraight - d ) ;
}
function _pin_size ( size ) =
is_undef ( size ) ? [ ] :
let ( sizeok = in_list ( size , [ "tiny" , "small" , "medium" , "large" , "standard" ] ) )
assert ( sizeok , "Pin size must be one of \"tiny\", \"small\", or \"standard\"" )
size = = "standard" || size = = "large" ?
struct_set ( [ ] , [ "length" , 10.8 ,
"diameter" , 7 ,
"snap" , 0.5 ,
"nub_depth" , 1.8 ,
"thickness" , 1.8 ,
"preload" , 0.2 ] ) :
size = = "medium" ?
struct_set ( [ ] , [ "length" , 8 ,
"diameter" , 4.6 ,
"snap" , 0.45 ,
"nub_depth" , 1.5 ,
"thickness" , 1.4 ,
"preload" , 0.2 ] ) :
size = = "small" ?
struct_set ( [ ] , [ "length" , 6 ,
"diameter" , 3.2 ,
"snap" , 0.4 ,
"nub_depth" , 1.2 ,
"thickness" , 1.0 ,
"preload" , 0.16 ] ) :
size = = "tiny" ?
struct_set ( [ ] , [ "length" , 4 ,
"diameter" , 2.5 ,
"snap" , 0.25 ,
"nub_depth" , 0.9 ,
"thickness" , 0.8 ,
"preload" , 0.1 ] ) :
undef ;
// Module: snap_pin()
// Usage:
// snap_pin(size, [pointed], [anchor], [spin], [orient])
// snap_pin(r|radius|d|diameter, l|length, nub_depth, snap, thickness, [clearance], [preload], [pointed], [anchor], [spin], [orient])
// Description:
// Creates a snap pin that can be inserted into an appropriate socket to connect two objects together. You can choose from some standard
// pin dimensions by giving a size, or you can specify all the pin geometry parameters yourself. If you use a standard size you can
// override the standard parameters by specifying other ones. The pins have flat sides so they can
// be printed. When oriented UP the shaft of the pin runs in the Z direction and the flat sides are the front and back. The default
// orientation (FRONT) and anchor (FRONT) places the pin in a printable configuration, flat side down on the xy plane.
// The tightness of fit is determined by `preload` and `clearance`. To make pins tighter increase `preload` and/or decrease `clearance`.
2020-07-27 22:15:34 +00:00
// .
2020-03-04 03:52:19 +00:00
// The "large" or "standard" size pin has a length of 10.8 and diameter of 7. The "medium" pin has a length of 8 and diameter of 4.6. The "small" pin
// has a length of 6 and diameter of 3.2. The "tiny" pin has a length of 4 and a diameter of 2.5.
2020-07-27 22:15:34 +00:00
// .
2020-03-04 03:52:19 +00:00
// This pin is based on https://www.thingiverse.com/thing:213310 by Emmett Lalishe
// and a modified version at https://www.thingiverse.com/thing:3218332 by acwest
// and distributed under the Creative Commons - Attribution - Share Alike License
// Arguments:
// size = text string to select from a list of predefined sizes, one of "standard", "small", or "tiny".
// pointed = set to true to get a pointed pin, false to get one with a rounded end. Default: true
// r|radius = radius of the pin
// d|diameter = diameter of the pin
// l|length = length of the pin
// nub_depth = the distance of the nub from the base of the pin
// snap = how much snap the pin provides (the nub projection)
// thickness = thickness of the pin walls
// pointed = if true the pin is pointed, otherwise it has a rounded tip. Default: true
// clearance = how far to shrink the pin away from the socket walls. Default: 0.2
// preload = amount to move the nub towards the pin base, which can create tension from the misalignment with the socket. Default: 0.2
// Example: Pin in native orientation
// snap_pin("standard", anchor=CENTER, orient=UP, thickness = 1, $fn=40);
// Example: Pins oriented for printing
2020-03-25 02:11:05 +00:00
// xcopies(spacing=10, n=4) snap_pin("standard", $fn=40);
2020-03-04 03:52:19 +00:00
module snap_pin ( size , r , radius , d , diameter , l , length , nub_depth , snap , thickness , clearance = 0.2 , preload , pointed = true , anchor = FRONT , spin = 0 , orient = FRONT , center ) {
preload_default = 0.2 ;
sizedat = _pin_size ( size ) ;
radius = get_radius ( r1 = r , r2 = radius , d1 = d , d2 = diameter , dflt = struct_val ( sizedat , "diameter" ) / 2 ) ;
length = first_defined ( [ l , length , struct_val ( sizedat , "length" ) ] ) ;
snap = first_defined ( [ snap , struct_val ( sizedat , "snap" ) ] ) ;
thickness = first_defined ( [ thickness , struct_val ( sizedat , "thickness" ) ] ) ;
nub_depth = first_defined ( [ nub_depth , struct_val ( sizedat , "nub_depth" ) ] ) ;
preload = first_defined ( [ first_defined ( [ preload , struct_val ( sizedat , "preload" ) ] ) , preload_default ] ) ;
nubscale = 0.9 ; // Mysterious arbitrary parameter
// The basic pin assumes a rounded cap of length sqrt(2)*r, which defines lStraight.
// If the point is enabled the cap length is instead 2*r
// preload shrinks the length, bringing the nubs closer together
rInner = radius - clearance ;
stretch = sqrt ( 2 ) * radius / rInner ; // extra stretch factor to make cap have proper length even though r is reduced.
lStraight = length - sqrt ( 2 ) * radius - clearance ;
lPin = lStraight + ( pointed ? 2 * radius : sqrt ( 2 ) * radius ) ;
attachable ( anchor = anchor , spin = spin , orient = orient ,
size = [ nubscale * ( 2 * rInner + 2 * snap + clearance ) , radius * sqrt ( 2 ) - 2 * clearance , 2 * lPin ] ) {
zflip_copy ( )
difference ( ) {
intersection ( ) {
cube ( [ 3 * ( radius + snap ) , radius * sqrt ( 2 ) - 2 * clearance , 2 * length + 3 * radius ] , center = true ) ;
_pin_shaft ( rInner , lStraight , snap + clearance / 2 , nubscale , stretch , nub_depth - preload , pointed ) ;
}
_pin_slot ( l = lStraight , r = rInner - thickness , t = thickness , d = nub_depth - preload , nub = snap , depth = 2 * radius + 0.02 , stretch = stretch ) ;
}
children ( ) ;
}
}
// Module: snap_pin_socket()
// Usage:
// snap_pin_socket(size, [fixed], [fins], [pointed], [anchor], [spin], [orient]);
// snap_pin_socket(r|radius|d|diameter, l|length, nub_depth, snap, [fixed], [pointed], [fins], [anchor], [spin], [orient])
// Description:
// Constructs a socket suitable for a snap_pin with the same parameters. If `fixed` is true then the socket has flat walls and the
// pin will not rotate in the socket. If `fixed` is false then the socket is round and the pin will rotate, particularly well
// if you add a lubricant. If `pointed` is true the socket is pointed to receive a pointed pin, otherwise it has a rounded and and
// will be shorter. If `fins` is set to true then two fins are included inside the socket to act as supports (which may help when printing tip up,
// especially when `pointed=false`). The default orientation is DOWN with anchor BOTTOM so that you can difference() the socket away from an object.
2020-07-27 22:15:34 +00:00
// .
2020-03-04 03:52:19 +00:00
// The "large" or "standard" size pin has a length of 10.8 and diameter of 7. The "medium" pin has a length of 8 and diameter of 4.6. The "small" pin
// has a length of 6 and diameter of 3.2. The "tiny" pin has a length of 4 and a diameter of 2.5.
// Arguments:
// size = text string to select from a list of predefined sizes, one of "standard", "small", or "tiny".
// pointed = set to true to get a pointed pin, false to get one with a rounded end. Default: true
// r|radius = radius of the pin
// d|diameter = diameter of the pin
// l|length = length of the pin
// nub_depth = the distance of the nub from the base of the pin
// snap = how much snap the pin provides (the nub projection)
// fixed = if true the pin cannot rotate, if false it can. Default: true
// pointed = if true the socket has a pointed tip. Default: true
// fins = if true supporting fins are included. Default: false
// Example: The socket shape itself in native orientation.
// snap_pin_socket("standard", anchor=CENTER, orient=UP, fins=true, $fn=40);
// Example: A spinning socket with fins:
// snap_pin_socket("standard", anchor=CENTER, orient=UP, fins=true, fixed=false, $fn=40);
// Example: A cube with a socket in the middle and one half-way off the front edge so you can see inside:
// $fn=40;
// diff("socket") cuboid([20,20,20]) {
// attach(TOP) snap_pin_socket("standard", $tags="socket");
// position(TOP+FRONT)snap_pin_socket("standard", $tags="socket");
// }
module snap_pin_socket ( size , r , radius , l , length , d , diameter , nub_depth , snap , fixed = true , pointed = true , fins = false , anchor = BOTTOM , spin = 0 , orient = DOWN ) {
sizedat = _pin_size ( size ) ;
radius = get_radius ( r1 = r , r2 = radius , d1 = d , d2 = diameter , dflt = struct_val ( sizedat , "diameter" ) / 2 ) ;
length = first_defined ( [ l , length , struct_val ( sizedat , "length" ) ] ) ;
snap = first_defined ( [ snap , struct_val ( sizedat , "snap" ) ] ) ;
nub_depth = first_defined ( [ nub_depth , struct_val ( sizedat , "nub_depth" ) ] ) ;
tip = pointed ? sqrt ( 2 ) * radius : radius ;
lPin = length + ( pointed ? ( 2 - sqrt ( 2 ) ) * radius : 0 ) ;
lStraight = lPin - ( pointed ? sqrt ( 2 ) * radius : radius ) ;
attachable ( anchor = anchor , spin = spin , orient = orient ,
size = [ 2 * ( radius + snap ) , radius * sqrt ( 2 ) , lPin ] )
{
down ( lPin / 2 )
intersection ( ) {
if ( fixed )
cube ( [ 3 * ( radius + snap ) , radius * sqrt ( 2 ) , 3 * lPin + 3 * radius ] , center = true ) ;
union ( ) {
_pin_shaft ( radius , lStraight , snap , 1 , 1 , nub_depth , pointed ) ;
if ( fins )
up ( lStraight ) {
cube ( [ 2 * radius , 0.01 , 2 * tip ] , center = true ) ;
cube ( [ 0.01 , 2 * radius , 2 * tip ] , center = true ) ;
}
}
}
children ( ) ;
}
}
2020-08-18 00:44:42 +00:00
// Module: rabbit_clip()
// Usage:
2020-09-19 01:44:05 +00:00
// rabbit_clip(type, length, width, snap, thickness, depth, [compression], [clearance], [lock], [lock_clearance], [splineteps], [anchor], [orient], [spin])
2020-08-18 00:44:42 +00:00
// Description:
// Creates a clip with two flexible ears to lock into a mating socket, or create a mask to produce the appropriate
// mating socket. The clip can be made to insert and release easily, or to hold much better, or it can be
// created with locking flanges that will make it very hard or impossible to remove. Unlike the snap pin, this clip
// is rectangular and can be made at any height, so a suitable clip could be very thin. It's also possible to get a
// solid connection with a short pin.
// .
// The type parameters specifies whether to make a clip, a socket mask, or a double clip. The length is the
// total nominal length of the clip. (The actual length will be very close, but not equal to this.) The width
// gives the nominal width of the clip, which is the actual width of the clip at its base. The snap parameter
// gives the depth of the clip sides, which controls how easy the clip is to insert and remove. The clip "ears" are
// made over-wide by the compression value. A nonzero compression helps make the clip secure in its socket.
// The socket's width and length are increased by the clearance value which creates some space and can compensate
// for printing inaccuracy. The socket will be slightly longer than the nominal width. The thickness is the thickness
// curved line that forms the clip. The clip depth is the amount the basic clip shape is extruded. Be sure that you
// make the socket with a larger depth than the clip (try 0.4 mm) to allow ease of insertion of the clip. The clearance
// value does not apply to the depth. The splinesteps parameter increases the sampling of the clip curves.
// .
// By default clips appear with orient=UP and sockets with orient=DOWN.
// .
// The first figure shows the dimensions of the rabbit clip. The second figure shows the clip in red overlayed on
// its socket in yellow. The left clip has a nonzero clearance, so its socket is bigger than the clip all around.
// The right hand locking clip has no clearance, but it has a lock clearance, which provides some space behind
// the lock to allow the clip to fit. (Note that depending on your printer, this can be set to zero.)
//
// Figure(2DMed):
// snap=1.5;
// comp=0.75;
// mid = 8.053; // computed in rabbit_clip
// tip = [-4.58,18.03];
// translate([9,3]){
// back_half()
// rabbit_clip("pin",width=12, length=18, depth=1, thickness = 1, compression=comp, snap=snap, orient=BACK);
// color("blue"){
// stroke([[6,0],[6,18]],width=0.1);
// stroke([[6+comp, 12], [6+comp, 18]], width=.1);
// }
// color("red"){
// stroke([[6-snap,mid], [6,mid]], endcaps="arrow2",width=0.15);
// translate([6+.4,mid-.15])text("snap",size=1,valign="center");
// translate([6+comp/2,19.5])text("compression", size=1, halign="center");
// stroke([[6+comp/2,19.3], [6+comp/2,17.7]], endcap2="arrow2", width=.15);
// fwd(1.1)text("width",size=1,halign="center");
// xflip_copy()stroke([[2,-.7], [6,-.7]], endcap2="arrow2", width=.15);
// move([-6.7,mid])rot(90)text("length", size=1, halign="center");
// stroke([[-7,10.3], [-7,18]], width=.15, endcap2="arrow2");
// stroke([[-7,0], [-7,5.8]], width=.15,endcap1="arrow2");
// stroke([tip, tip-[0,1]], width=.15);
// move([tip.x+2,19.5])text("thickness", halign="center",size=1);
// stroke([[tip.x+2, 19.3], tip+[.1,.1]], width=.15, endcap2="arrow2");
// }
// }
//
// Figure(2DMed):
// snap=1.5;
// comp=0;
// translate([29,3]){
// back_half()
// rabbit_clip("socket", width=12, length=18, depth=1, thickness = 1, compression=comp, snap=snap, orient=BACK,lock=true);
// color("red")back_half()
// rabbit_clip("pin",width=12, length=18, depth=1, thickness = 1, compression=comp, snap=snap,
// orient=BACK,lock=true,lock_clearance=1);
// }
// translate([9,3]){
// back_half()
// rabbit_clip("socket", clearance=.5,width=12, length=18, depth=1, thickness = 1,
// compression=comp, snap=snap, orient=BACK,lock=false);
// color("red")back_half()
// rabbit_clip("pin",width=12, length=18, depth=1, thickness = 1, compression=comp, snap=snap,
// orient=BACK,lock=false,lock_clearance=1);
// }
// Arguments:
// type = One of "pin", "socket", "male", "female" or "double" to specify what to make.
// length = nominal clip length
// width = nominal clip width
// snap = depth of hollow on the side of the clip
// thickness = thickness of the clip "line"
// depth = amount to extrude clip (give extra room for the socket, about 0.4mm)
// compression = excess width at the "ears" to lock more tightly. Default: 0.1
// clearance = extra space in the socket for easier insertion. Default: 0.1
// lock = set to true to make a locking clip that may be irreversible. Default: false
// lock_clearance = give clearance for the lock. Default: 0
// splinesteps = number of samples in the curves of the clip. Default: 8
// anchor = anchor point for clip
// orient = clip orientation. Default: UP for pins, DOWN for sockets
// spin = spin the clip. Default: 0
//
// Example: Here are several sizes that work printed in PLA on a Prusa MK3, with default clearance of 0.1 and a depth of 5
// module test_pair(length, width, snap, thickness, compression, lock=false)
// {
// depth = 5;
// extra_depth = 10;// Change this to 0.4 for closed sockets
// cuboid([max(width+5,12),12, depth], chamfer=.5, edges=[FRONT,"Y"], anchor=BOTTOM)
// attach(BACK)
// rabbit_clip(type="pin",length=length, width=width,snap=snap,thickness=thickness,depth=depth,
// compression=compression,lock=lock);
// right(width+13)
// diff("remove")
// cuboid([width+8,max(12,length+2),depth+3], chamfer=.5, edges=[FRONT,"Y"], anchor=BOTTOM)
// attach(BACK)
// rabbit_clip(type="socket",length=length, width=width,snap=snap,thickness=thickness,depth=depth+extra_depth,
// lock=lock,compression=0,$tags="remove");
// }
// left(37)ydistribute(spacing=28){
// test_pair(length=6, width=7, snap=0.25, thickness=0.8, compression=0.1);
// test_pair(length=3.5, width=7, snap=0.1, thickness=0.8, compression=0.1); // snap = 0.2 gives a firmer connection
// test_pair(length=3.5, width=5, snap=0.1, thickness=0.8, compression=0.1); // hard to take apart
// }
// right(17)ydistribute(spacing=28){
// test_pair(length=12, width=10, snap=1, thickness=1.2, compression=0.2);
// test_pair(length=8, width=7, snap=0.75, thickness=0.8, compression=0.2, lock=true); // With lock, very firm and irreversible
// test_pair(length=8, width=7, snap=0.75, thickness=0.8, compression=0.2, lock=true); // With lock, very firm and irreversible
// }
// Example: Double clip to connect two sockets
// rabbit_clip("double",length=8, width=7, snap=0.75, thickness=0.8, compression=0.2,depth=5);
// Example: A modified version of the clip that acts like a backpack strap clip, where it locks tightly but you can squeeze to release.
// cuboid([25,15,5],anchor=BOTTOM)
// attach(BACK)rabbit_clip("pin", length=25, width=25, thickness=1.5, snap=2, compression=0, lock=true, depth=5, lock_clearance=3);
// left(32)
// diff("remove")
// cuboid([30,30,11],orient=BACK,anchor=BACK){
// attach(BACK)rabbit_clip("socket", length=25, width=25, thickness=1.5, snap=2, compression=0, lock=true, depth=5.5, lock_clearance=3,$tags="remove");
// xflip_copy()
// position(FRONT+LEFT)
// xscale(0.8)
// zcyl(l=20,r=13.5, $tags="remove",$fn=64);
// }
module rabbit_clip ( type , length , width , snap , thickness , depth , compression = 0.1 , clearance = . 1 , lock = false , lock_clearance = 0 ,
splinesteps = 8 , anchor , orient , spin = 0 )
{
assert ( is_num ( width ) && width > 0 , "Width must be a positive value" ) ;
assert ( is_num ( length ) && length > 0 , "Length must be a positive value" ) ;
assert ( is_num ( thickness ) && thickness > 0 , "Thickness must be a positive value" ) ;
assert ( is_num ( snap ) && snap >= 0 , "Snap must be a non-negative value" ) ;
assert ( is_num ( depth ) && depth > 0 , "Depth must be a positive value" ) ;
assert ( is_num ( compression ) && compression >= 0 , "Compression must be a nonnegative value" ) ;
assert ( is_bool ( lock ) ) ;
assert ( is_num ( lock_clearance ) ) ;
legal_types = [ "pin" , "socket" , "male" , "female" , "double" ] ;
assert ( in_list ( type , legal_types ) , str ( "type must be one of " , legal_types ) ) ;
if ( type = = "double" ) {
attachable ( size = [ width + 2 * compression , depth , 2 * length ] , anchor = default ( anchor , BACK ) , spin = spin , orient = default ( orient , BACK ) ) {
union ( ) {
rabbit_clip ( "pin" , length = length , width = width , snap = snap , thickness = thickness , depth = depth , compression = compression ,
lock = lock , anchor = BOTTOM , orient = UP ) ;
rabbit_clip ( "pin" , length = length , width = width , snap = snap , thickness = thickness , depth = depth , compression = compression ,
lock = lock , anchor = BOTTOM , orient = DOWN ) ;
cuboid ( [ width - thickness , depth , thickness ] ) ;
}
children ( ) ;
}
} else {
anchor = default ( anchor , BOTTOM ) ;
is_pin = in_list ( type , [ "pin" , "male" ] ) ;
default_overlap = 0.01 * ( is_pin ? 1 : - 1 ) ; // Shift by this much to undo default overlap
extra = 0.02 ; // Amount of extension below nominal based position for the socket, must exceed default overlap of 0.01
clearance = is_pin ? 0 : clearance ;
compression = is_pin ? compression : 0 ;
orient = is_def ( orient ) ? orient
: is_pin ? UP
: DOWN ;
earwidth = 2 * thickness + snap ;
point_length = earwidth / 2.15 ;
// The adjustment is using cos(theta)*earwidth/2 and sin(theta)*point_length, but the computation
// is obscured because theta is atan(length/2/snap)
scaled_len = length - 0.5 * ( earwidth * snap + point_length * length ) / sqrt ( sqr ( snap ) + sqr ( length / 2 ) ) ;
bottom_pt = [ 0 , max ( scaled_len * 0.15 + thickness , 2 * thickness ) ] ;
ctr = [ width / 2 , scaled_len ] + line_normal ( [ width / 2 - snap , scaled_len / 2 ] , [ width / 2 , scaled_len ] ) * earwidth / 2 ;
inside_pt = circle_circle_tangents ( bottom_pt , 0 , ctr , earwidth / 2 ) [ 0 ] [ 1 ] ;
sidepath = [
[ width / 2 , 0 ] ,
[ width / 2 - snap , scaled_len / 2 ] ,
[ width / 2 + ( is_pin ? compression : 0 ) , scaled_len ] ,
ctr - point_length * line_normal ( [ width / 2 , scaled_len ] , inside_pt ) ,
inside_pt
] ;
fullpath = concat (
sidepath ,
[ bottom_pt ] ,
reverse ( apply ( xflip ( ) , sidepath ) )
) ;
assert ( fullpath [ 4 ] . y < fullpath [ 3 ] . y , "Pin is too wide for its length" ) ;
snapmargin = - snap + select ( sidepath , - 1 ) . x ; // - compression;
if ( is_pin ) {
if ( snapmargin < 0 ) echo ( "WARNING: The snap is too large for the clip to squeeze to fit its socket" )
echo ( snapmargin = snapmargin ) ;
}
// Force tangent to be vertical at the outer edge of the clip to avoid overshoot
fulltangent = list_set ( path_tangents ( fullpath , uniform = false ) , [ 2 , 8 ] , [ [ 0 , 1 ] , [ 0 , - 1 ] ] ) ;
subset = is_pin ? [ 0 : 10 ] : [ 0 , 1 , 2 , 3 , 7 , 8 , 9 , 10 ] ; // Remove internal points from the socket
tangent = select ( fulltangent , subset ) ;
path = select ( fullpath , subset ) ;
socket_smooth = . 04 ;
pin_smooth = [ . 075 , . 075 , . 15 , . 12 , . 06 ] ;
smoothing = is_pin
? concat ( pin_smooth , reverse ( pin_smooth ) )
: let ( side_smooth = select ( pin_smooth , 0 , 2 ) )
concat ( side_smooth , [ socket_smooth ] , reverse ( side_smooth ) ) ;
bez = path_to_bezier ( path , relsize = smoothing , tangents = tangent ) ;
2020-11-17 01:50:08 +00:00
rounded = bezier_path ( bez , splinesteps = splinesteps ) ;
2020-08-18 00:44:42 +00:00
bounds = pointlist_bounds ( rounded ) ;
2020-09-19 01:44:05 +00:00
//kk = search([bounds[1].y], subindex(rounded,1));
//echo(rounded[kk[0]]);
2020-08-18 00:44:42 +00:00
extrapt = is_pin ? [ ] : [ rounded [ 0 ] - [ 0 , extra ] ] ;
finalpath = is_pin ? rounded
: let ( withclearance = offset ( rounded , r = - clearance ) )
concat ( [ [ withclearance [ 0 ] . x , - extra ] ] ,
withclearance ,
[ [ - withclearance [ 0 ] . x , - extra ] ] ) ;
attachable ( size = [ bounds [ 1 ] . x - bounds [ 0 ] . x , depth , bounds [ 1 ] . y - bounds [ 0 ] . y ] , anchor = anchor , spin = spin , orient = orient ) {
xrot ( 90 )
translate ( [ 0 , - ( bounds [ 1 ] . y - bounds [ 0 ] . y ) / 2 + default_overlap , - depth / 2 ] )
linear_extrude ( height = depth , convexity = 10 ) {
if ( lock )
xflip_copy ( )
right ( clearance )
polygon ( [ sidepath [ 1 ] + [ - thickness / 10 , lock_clearance ] ,
sidepath [ 2 ] ,
[ sidepath [ 2 ] . x , sidepath [ 1 ] . y + lock_clearance ] ] ) ;
if ( is_pin )
offset_stroke ( finalpath , width = [ thickness , 0 ] ) ;
else
polygon ( finalpath ) ;
}
children ( ) ;
}
}
}
2020-03-04 03:52:19 +00:00
2020-05-30 02:04:34 +00:00
// vim: expandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap