omdl  v1.0
OpenSCAD Mechanical Design Library
dovetail_screw.scad
Go to the documentation of this file.
1 //! Models for constructing 3d dovetail joints with optional screw and nut fastener.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2026
6 
7  \copyright
8 
9  This file is part of [omdl] (https://github.com/royasutton/omdl),
10  an OpenSCAD mechanical design library.
11 
12  The \em omdl is free software; you can redistribute it and/or modify
13  it under the terms of the [GNU Lesser General Public License]
14  (http://www.gnu.org/licenses/lgpl.html) as published by the Free
15  Software Foundation; either version 2.1 of the License, or (at
16  your option) any later version.
17 
18  The \em omdl is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with the \em omdl; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  02110-1301, USA; or see <http://www.gnu.org/licenses/>.
27 
28  \details
29 
30  \amu_define group_name (Dovetail Screw)
31  \amu_define group_brief (Models for constructing 3d dovetail joints
32  with optional screw and nut fastener.)
33 
34  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
35 *******************************************************************************/
36 
37 //----------------------------------------------------------------------------//
38 // group and macros.
39 //----------------------------------------------------------------------------//
40 
41 /***************************************************************************//**
42  \amu_include (include/amu/doxyg_define_in_parent_open.amu)
43  \amu_define includes_required_add
44  (
45  models/3d/fastener/screws.scad
46  )
47  \amu_include (include/amu/includes_required.amu)
48 *******************************************************************************/
49 
50 //----------------------------------------------------------------------------//
51 
52 //! Construct a 3d dovetail joint with optional screw and nut fastener.
53 /***************************************************************************//**
54 
55  \param h <decimal> joint height.
56 
57  \param conf <datastruct> joint length, depth, pin configuration
58  and screw configuration (see below).
59 
60  \param insts <datastruct> joint instance list (see below).
61 
62  \param mode <integer> global construction mode.
63 
64  \param type <integer> construction type {0=male additions,
65  1=male removals, 2=female removals}.
66 
67  \param align <integer-list-3> joint alignment; center, edge-1, and
68  edge-2 for [x, y, z].
69 
70  \details
71 
72  Use this module to construct 3d dovetail joints with optional screw
73  and locking nut, as illustrated in the example below.
74 
75  Set \p type = 0 to construct the male half, and set \p type = 2 to
76  for the corresponding female half. To ensure proper alignment and
77  fit, both components must be constructed using identical
78  configuration parameters.
79 
80  When producing interlocking joints with 3D-printed plastics,
81  carefully control the joint clearance. Most printed plastics are
82  relatively rigid, so insufficient gap allowance can lead to poor
83  assembly or excessive friction.
84 
85  For CNC routers and laser cutters, the interior and exterior corner
86  radii can be adjusted to accommodate tooling limitations. This
87  allows flat internal mating surfaces to be maintained while
88  compensating for the minimum practical radius imposed by the cutter
89  diameter (commonly called dogbone corner relief).
90 
91  ## Multi-value and structured parameters
92 
93  ### conf
94 
95  e | data type | default value | parameter description
96  ---:|:-----------------:|:-----------------:|:------------------------------------
97  0 | decimal | required | \p l : joint length
98  1 | decimal | l/10 | \p d : joint depth
99  2 | datastruct \| decimal | d | pin default configuration
100  3 | datastruct | undef | screw bore default configuration
101 
102  #### conf[2]: pin
103 
104  e | data type | default value | parameter description
105  ---:|:-----------------:|:-----------------:|:------------------------------------
106  0 | decimal | d | \p m : male pin width
107  1 | decimal | m * 5/2 | screw section width
108  2 | decimal | 0 | pin tail expansion (dovetail)
109  3 | decimal | m / 25 | pin gap width
110  4 | decimal | m / 20 | pin exterior edge rounding
111  5 | decimal | m / 20 | pin interior edge rounding
112 
113  #### conf[3]: screw bore
114 
115  ##### Data structure schema: screw bore
116 
117  name | schema
118  ---------------:|:----------------------------------------------
119  screw bore | [ sbo, d, l, h, n, t, s, f ]
120 
121  ##### Data structure fields: screw bore
122 
123  e | data type | default value | parameter description
124  ---:|:-----------------:|:-----------------:|:------------------------------------
125  0 | decimal | 0 | \p sbo : screw bore offset
126  1 | decimal | d / 6 | \p d : bore diameter
127  2 | decimal | d * 3/2 | \p l : bore length
128  3 | decimal-list-5 | | \p h : fastener head
129  4 | decimal-list-5 | | \p n : fastener nut
130  5 | decimal-list-2 | | \p t : bore tolerance
131  6 | decimal-list-list-3 | | \p s : nut slot cutout
132  7 | decimal-list-2 \| decimal | | \p f : bore scale factor
133 
134  The screw bore is defined using the data structure described above.
135  This structure includes seven parameters, all of which are
136  documented in screw_bore().
137 
138  ### insts
139 
140  #### Data structure schema: insts
141 
142  name | schema
143  ---------------:|:----------------------------------------------
144  insts | [ inst, inst, ..., inst ]
145 
146  #### Data structure fields: inst
147 
148  e | data type | default value | parameter description
149  ---:|:-----------------:|:-----------------:|:------------------------------------
150  0 | decimal | 0 | zero reference; [-1, 0, +1]
151  1 | decimal | 0 | length offset from reference
152  2 | integer | 7 | form (see below).
153  3 | integer | | mode override (see below).
154  4 | datastruct \| decimal | | pin override (see above).
155  5 | datastruct \| decimal | | screw bore override (see above).
156 
157  #### insts[2]: form
158 
159  Integer value is binary encoded.
160 
161  b | description
162  ---:|:---------------------------------------
163  0 | construct left pin at negative side of instance
164  1 | construct right pin at positive side of instance
165  2 | construct screw bore for instance
166 
167  ### mode
168 
169  Integer value is binary encoded.
170 
171  b | description
172  ---:|:---------------------------------------
173  0 | female removal; interior corner over cut placement
174 
175  \amu_define scope_id (example)
176  \amu_define title (Box screw joint example)
177  \amu_define image_views (top diag)
178  \amu_define image_size (sxga)
179 
180  \amu_include (include/amu/scope_diagrams_3d.amu)
181 *******************************************************************************/
182 
184 (
185  h = 1,
186  conf,
187  insts,
188  mode = 0,
189  type = 0,
190  align
191 )
192 {
193  // construct 3d joint at origin
194  module box_screw( iform=7, imode=mode, ipin=pin, ibore=bore )
195  {
196  /*
197  iform
198 
199  B0: left pin (negative side)
200  B1: right pin (positive side)
201  B2: center screw
202  */
203 
204  // decode ipin
205  t1 = defined_fle_or([ipin, pin], 0, ipin); // male pin width
206  t2 = defined_fle_or([ipin, pin], 1, t1*5/2); // screw section width
207  te = defined_fle_or([ipin, pin], 2, 0); // tail "engagement" width
208  tg = defined_fle_or([ipin, pin], 3, t1/25); // pin gap width
209  er = defined_fle_or([ipin, pin], 4, t1/20); // pin exterior edge rounding
210  ir = defined_fle_or([ipin, pin], 5, t1/20); // pin interior edge rounding
211 
212  // decode ibore; screw bore parameters
213  // see screw_bore() for details on following
214  so = defined_fle_or([ibore, bore], 0, 0);
215  sd = defined_fle_or([ibore, bore], 1, depth/6);
216  sl = defined_fle_or([ibore, bore], 2, depth*3/2);
217  sh = defined_fle_or([ibore, bore], 3, undef);
218  sn = defined_fle_or([ibore, bore], 4, undef);
219  st = defined_fle_or([ibore, bore], 5, undef);
220  ss = defined_fle_or([ibore, bore], 6, undef);
221  sf = defined_fle_or([ibore, bore], 7, undef);
222 
223  // decode imode
224  m0 = binary_bit_is(imode, 1, 0); // female removal; interior corner over cut placement
225 
226  sg = (type == 2) ? +1 : -1; // gap adjustment
227  s1 = t1 + sg * tg/2; // pin sized for gap
228 
229  // add enabled pins only
230  pins =
231  [
232  if( binary_bit_is(iform, 0, 1) ) -1,
233  if( binary_bit_is(iform, 1, 1) ) +1
234  ];
235 
236  // move to joint male / female interface
237  translate( [0, depth/2, 0] )
238  {
239  // pin; female removal or male additions
240  if (type == 0 || type == 2)
241  for ( i = pins )
242  extrude_linear_uss(h=h, center=true)
243  translate ([(t1 + t2)/2*i, 0])
244  {
245  if (te > 0)
246  { // finger tail with engagement
247  let
248  (
249  vr = (type == 2) ? er : 0,
250  vrm = [0,0,4,3]
251  )
252  pg_rectangle([s1, depth], vr=vr, vrm=vrm, center=true);
253 
254  pg_te =
255  let
256  (
257  sss = triangle_sas2sss( [depth, 90, te/2] ),
258  ppp = triangle2d_sss2ppp( sss ),
259 
260  vr = (type == 2) ? 0 : er,
261  vrm = [0,0,1]
262  )
263  polygon_round_eve_all_p(ppp, vr=vr, vrm=vrm);
264 
265  translate([ -(s1+te)/2, depth/2])
266  rotate([180,0])
267  polygon(pg_te);
268 
269  translate([ +(s1+te)/2, depth/2])
270  rotate([180,180])
271  polygon(pg_te);
272  }
273  else
274  { // straight box finger / pin
275  let
276  (
277  vr = er,
278  vrm = (type == 2) ? [0,0,4,3] : [1,1,0,0]
279  )
280  pg_rectangle([s1, depth], vr=er, vrm=vrm, center=true);
281  }
282  }
283 
284  // removal mode only
285  if (type == 1 || type == 2)
286  {
287  // interior corner minimum cut radius
288  if ( ir > 0 )
289  for
290  (
291  i = pins,
292 
293  mcr_o =
294  let ( pe = max(te, 0) )
295  (type == 2) ?
296  [ // type=2; female
297  for (j = [-1,1])
298  m0 ? [ j*(pe+s1-ir)/2, depth ]
299  : [ j*(pe+s1)/2, depth-ir/2 ]
300  ]
301  : [ // type=0,1; male
302  for (j = [-1,1])
303  [ j*(s1+ir)/2, 0 ]
304  ]
305  )
306  translate ([(t1 + t2)/2*i, -depth/2] + mcr_o)
307  cylinder(d=ir, h=h, center=true);
308 
309  // screw bore
310  if ( binary_bit_is(iform, 2, 1) )
311  translate ([0, depth/2 + so, 0])
312  rotate([270,0,0])
313  screw_bore(d=sd, l=sl, h=sh, n=sn, t=st, s=ss, f=sf, a=1);
314  }
315  }
316  }
317 
318  // decode configuration
319  length = defined_e_or(conf, 0, conf);
320  depth = defined_e_or(conf, 1, length/10);
321  pin = defined_e_or(conf, 2, depth);
322  bore = defined_e_or(conf, 3, undef);
323 
324  // align construction
325  translate
326  (
327  [
328  select_ci( [ 0, +length/2, -length/2 ], defined_e_or(align, 0, 0), false ),
329  select_ci( [ 0, -depth/2, -depth ], defined_e_or(align, 1, 0), false ),
330  select_ci( [ 0, +h/2, -h/2 ], defined_e_or(align, 2, 0), false )
331  ]
332  )
333  for ( i = insts )
334  {
335  zero = defined_e_or(i, 0, 0);
336  offset = defined_e_or(i, 1, 0);
337  iform = defined_e_or(i, 2, 7);
338  imode = defined_e_or(i, 3, mode);
339  ipin = defined_e_or(i, 4, pin);
340  ibore = defined_e_or(i, 5, bore);
341 
342  translate([ length/2 * zero + offset, 0 ])
343  box_screw( iform=iform, imode=imode, ipin=ipin, ibore=ibore );
344  }
345 }
346 
347 
348 //! @}
349 //! @}
350 
351 
352 //----------------------------------------------------------------------------//
353 // openscad-amu auxiliary scripts
354 //----------------------------------------------------------------------------//
355 
356 /*
357 BEGIN_SCOPE example;
358  BEGIN_OPENSCAD;
359  include <omdl-base.scad>;
360  include <models/3d/fastener/screws.scad>;
361  include <models/3d/joint/dovetail_screw.scad>;
362 
363  $fn = 36;
364 
365  h = 3;
366  w = [50, 3];
367 
368  pin = [5, 5, 2, 1, 1/3, 1/3];
369 
370  bore =
371  [
372  3,
373  1,
374  5 + 3,
375  [2, 1/2, 1/2],
376  [2, 3/4, 0],
377  undef,
378  [0, [-2, +2]],
379  [1 + 10/100, 1]
380 
381  ];
382 
383  conf = [ w.x, w.y, pin, bore ];
384 
385  insts =
386  [
387  [-1, +3, 2+4],
388  [+0, +0, 1+2+4, 1, [3]],
389  [+1, -3, 1 +4],
390  ];
391 
392  translate([0, -w.y, 0]) union()
393  {
394  joint3d_dovetail_screw(h=h, conf=conf, insts=insts, type=0);
395  difference()
396  {
397  translate([0, -w.y*3/4]) cube([w.x, w.y * 3/2, h], center=true);
398  joint3d_dovetail_screw(h=h, conf=conf, insts=insts, type=1);
399  }
400  }
401 
402  translate([0, +w.y, 0]) difference()
403  {
404  translate([0, w.y, -h/2 ]) cube([w.x, w.y*2 -eps*4, h*2 -eps*4], center=true);
405  joint3d_dovetail_screw(h=h, conf=conf, insts=insts, type=2);
406  }
407 
408  // end_include
409  END_OPENSCAD;
410 
411  BEGIN_MFSCRIPT;
412  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
413  table_unset_all sizes;
414 
415  images name "sizes" types "sxga";
416  views name "views" views "top diag";
417 
418  variables set_opts_combine "sizes views";
419  variables add_opts "--viewall --autocenter --view=axes";
420 
421  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
422  END_MFSCRIPT;
423 END_SCOPE;
424 */
425 
426 //----------------------------------------------------------------------------//
427 // end of file
428 //----------------------------------------------------------------------------//
429 
function binary_bit_is(v, b, t=1)
Test if a binary bit position of an integer value equals a test bit.
function defined_e_or(v, i, d)
Returns an element from an iterable if it exists, or a default value if not.
function defined_fle_or(v, i, d)
Return the first defined specified element in a list of lists, otherwise returns the given default va...
function select_ci(v, i, l=true)
Select specified element from list or return a default.
function polygon_round_eve_all_p(c, vr=0, vrm=1, vfn, w=true, cw=true)
Compute coordinates that round all of the vertices between each adjacent edges in 2D.
function triangle_sas2sss(v)
Compute the side lengths of a triangle given two sides and the included angle.
function triangle2d_sss2ppp(v, a=x_axis_ci, cw=true)
Compute a set of vertex coordinates for a triangle given its side lengths in 2D.
module screw_bore(d=1, l=1, h, n, t, s, f, a)
Flat and beveled-head fastener bore with nut, nut slot, and bore tolerance.
Definition: screws.scad:358
module joint3d_dovetail_screw(h=1, conf, insts, mode=0, type=0, align)
Construct a 3d dovetail joint with optional screw and nut fastener.
module pg_rectangle(size=1, o, vr, vrm=1, vfn, center=false)
A polygon rectangle with vertex rounding.
Definition: polygon.scad:773
module extrude_linear_uss(h, center=false, c=true, ha=0)
Linearly extrude a 2d shape with uniformly-spaced profile scaling.
Definition: extrude.scad:666
function length(v, from=length_unit_default, to=length_unit_base, d=1)
Convert a length value from one unit to another, with optional dimensional scaling.