omdl  v1.0
OpenSCAD Mechanical Design Library
box_screw.scad
Go to the documentation of this file.
1 //! Models for generating 2d box joints fastened with screw and nut.
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 (Box Screw)
31  \amu_define group_brief (Models for generating 2d box joints fastened with screw and nut.)
32 
33  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
34 *******************************************************************************/
35 
36 //----------------------------------------------------------------------------//
37 // group and macros.
38 //----------------------------------------------------------------------------//
39 
40 /***************************************************************************//**
41  \amu_include (include/amu/doxyg_define_in_parent_open.amu)
42  \amu_define includes_required_add
43  (
44  transforms/base_cs.scad
45  )
46  \amu_include (include/amu/includes_required.amu)
47 *******************************************************************************/
48 
49 //----------------------------------------------------------------------------//
50 
51 //! Create 2d edge profiles for screw and nut box joint construction.
52 /***************************************************************************//**
53 
54  \param conf <datastruct> box joint length, depth, pin configuration
55  and screw configuration (see below).
56 
57  \param insts <datastruct> box joint instance list (see below).
58 
59  \param mode <integer> global construction mode.
60 
61  \param type <integer> construction type {0=male additions,
62  1=male removals, 2=female removals}.
63 
64  \param trim <boolean> limit construction to within the total
65  joint width.
66 
67  \param align <integer-list-2> joint alignment; edge-1, center, and
68  edge-2 for both [x, y].
69 
70  \details
71 
72  Use this module to generate a 2d profile for constructing box-joint
73  pairs with a central screw and locking nut, as illustrated in the
74  example below.
75 
76  Set \p type = 0 to create the male finger profile, and set \p type
77  = 2 to generate the corresponding female slot profile. To ensure
78  proper alignment and fit, both components must be created using
79  identical configuration parameters.
80 
81  When producing interlocking joints with 3D-printed plastics,
82  carefully control the joint clearance. Most printed plastics are
83  relatively rigid, so insufficient gap allowance can lead to poor
84  assembly or excessive friction.
85 
86  For CNC routers and laser cutters, the interior and exterior corner
87  radii can be adjusted to accommodate tooling limitations. This
88  allows flat internal mating surfaces to be maintained while
89  compensating for the minimum practical radius imposed by the cutter
90  diameter (commonly called dogbone corner relief).
91 
92  ## Multi-value and structured parameters
93 
94  ### conf
95 
96  e | data type | default value | parameter description
97  ---:|:-----------------:|:-----------------:|:------------------------------------
98  0 | decimal | required | \p l : joint length
99  1 | decimal | l/10 | \p d : joint depth
100  2 | decimal-list-5 \| decimal | d | pin default configuration
101  3 | decimal-list-2 \| decimal | d/6 | screw default configuration
102  4 | decimal-list-4 \| decimal | d/3 | nut default configuration
103 
104  #### conf[2]: pin
105 
106  e | data type | default value | parameter description
107  ---:|:-----------------:|:-----------------:|:------------------------------------
108  0 | decimal | d | \p m : male pin width
109  1 | decimal | m * 5/2 | screw section width
110  2 | decimal | m / 25 | pin gap width
111  3 | decimal | m / 20 | pin exterior edge rounding
112  4 | decimal | m / 20 | pin interior edge rounding
113 
114  #### conf[3]: screw
115 
116  e | data type | default value | parameter description
117  ---:|:-----------------:|:-----------------:|:------------------------------------
118  0 | decimal | d/6 | \p sd : screw diameter
119  1 | decimal | d | \p sl : screw length
120 
121  #### conf[4]: nut
122 
123  e | data type | default value | parameter description
124  ---:|:-----------------:|:-----------------:|:------------------------------------
125  0 | decimal | d/3 | nut size; flat-to-flat
126  1 | decimal | sd | nut height
127  2 | decimal | sl / 10 | nut end offset
128  3 | decimal | m / 20 | nut interior edge rounding
129 
130  ### insts
131 
132  e | data type | default value | parameter description
133  ---:|:-----------------:|:-----------------:|:------------------------------------
134  0 | decimal | 0 | zero reference; [-1, 0, +1]
135  1 | decimal | 0 | length offset from reference
136  2 | integer | 7 | form (see below).
137  3 | integer | | mode override (see below).
138  4 | decimal-list-5 \| decimal | | pin override (see above).
139  5 | decimal-list-2 \| decimal | | screw override (see above).
140  6 | decimal-list-4 \| decimal | | nut override (see above).
141 
142  #### insts[2]: form
143 
144  Integer value is binary encoded.
145 
146  b | description
147  ---:|:---------------------------------------
148  0 | construct left pin at negative side of instance
149  1 | construct right pin at positive side of instance
150  2 | construct screw and nut section of instance
151 
152  ### mode
153 
154  Integer value is binary encoded.
155 
156  b | description
157  ---:|:---------------------------------------
158  0 | female removal; interior vs exterior rounding
159  1 | female removal; interior corner over cut placement
160  2 | male removal; screw bore vs skip screw bore
161  3 | male removal; screw nut vs skip screw nut
162  4 | male removal; add screw bore drill punch mark
163 
164  \amu_define scope_id (example)
165  \amu_define title (Box screw joint profile example)
166  \amu_define image_views (top)
167  \amu_define image_size (sxga)
168 
169  \amu_include (include/amu/scope_diagrams_3d.amu)
170 *******************************************************************************/
171 
172 module joint2d_box_screw
173 (
174  conf,
175  insts,
176 
177  mode = 0,
178  type = 0,
179 
180  trim = false,
181 
182  align
183 )
184 {
185  // construct 2d box joint at origin
186  module box_screw( iform=7, imode=mode, ipin=pin, iscrew=screw, inut=nut )
187  {
188  /*
189  iform
190 
191  B0: left pin (negative side)
192  B1: right pin (positive side)
193  B2: center screw
194  */
195 
196  // decode imode
197  m0 = binary_bit_is(imode, 0, 0); // female removal; interior vs exterior rounding
198  m1 = binary_bit_is(imode, 1, 0); // female removal; interior corner over cut placement
199  m2 = binary_bit_is(imode, 2, 0); // male removal; screw bore vs skip screw bore
200  m3 = binary_bit_is(imode, 3, 0); // male removal; screw nut vs skip screw nut
201  m4 = binary_bit_is(imode, 4, 1); // male removal; add screw bore drill punch mark
202 
203  // decode ipin
204  t1 = defined_fle_or([ipin, pin], 0, ipin); // male pin width
205  t2 = defined_fle_or([ipin, pin], 1, t1*5/2); // screw section width
206  tg = defined_fle_or([ipin, pin], 2, t1/25); // pin gap width
207  er = defined_fle_or([ipin, pin], 3, t1/20); // pin exterior edge rounding
208  ir = defined_fle_or([ipin, pin], 4, t1/20); // pin interior edge rounding
209 
210  // decode iscrew
211  sd = defined_fle_or([iscrew, screw], 0, iscrew); // screw diameter
212  sl = defined_fle_or([iscrew, screw], 1, depth); // screw length
213 
214  // decode inut
215  ns = defined_fle_or([inut, nut], 0, inut); // nut size; flat-to-flat
216  nh = defined_fle_or([inut, nut], 1, sd); // nut height
217  no = defined_fle_or([inut, nut], 2, sl/10); // nut end offset
218  nr = defined_fle_or([inut, nut], 3, t1/20); // nut interior edge rounding
219 
220  fvrm = m0 ? [0,0,4,3] : [0,0,0,0]; // rounding mode selections
221 
222  sg = (type == 2) ? 1 : -1; // gap adjustment
223  s1 = t1 + sg * tg/2; // pin sized for gap
224 
225  // add enabled pins only
226  pins =
227  [
228  if( binary_bit_is(iform, 0, 1) ) -1,
229  if( binary_bit_is(iform, 1, 1) ) +1
230  ];
231 
232  // male and female joint construction; male additions or female removals
233  if (type == 0 || type == 2)
234  intersection_cs(trim, trim ? undef : 1)
235  {
236  // child-0: joint trim area = length x depth
237  translate ([0, depth/2])
238  square([length, depth], center=true);
239 
240  // child-1: pin; male additions or female removals
241  for ( i = pins )
242  translate ([(t1 + t2)/2*i, depth/2])
243  pg_rectangle([s1, depth], vr=er, vrm=(type == 2) ? fvrm : [1,1,0,0], center=true);
244  }
245 
246  // interior corner minimum cut radius; removal modes only
247  if ( ir > 0 && (type == 1 || type == 2) )
248  for
249  (
250  i = pins,
251 
252  mcr_o =
253  (type == 2) ?
254  m0 ?
255  [ // type=0, m0=1, m1=[0,1]; female with rounding at edge
256  for (j = [-1,1])
257  m1 ? [ j*(s1-ir)/2, depth ]
258  : [ j*s1/2, depth-ir/2 ]
259  ]
260  : [ // type=0, m0=0, m1=[0,1]; female with rounding in field
261  for (j = [-1,1], k = [-1,1])
262  m1 ? [ j*(s1-ir)/2, depth/2 + k*depth/2 ]
263  : [ j*s1/2, depth/2 + k*(depth-ir)/2 ]
264  ]
265  : [ // type=1,2; male
266  for (j = [-1,1])
267  [ j*(s1+ir)/2, 0 ]
268  ]
269  )
270  translate ([(t1 + t2)/2*i, 0] + mcr_o)
271  circle(d=ir);
272 
273  // screw
274  if ( binary_bit_is(iform, 2, 1) )
275  {
276  // screw hole; female removal
277  if (type == 2)
278  translate ([0, depth/2])
279  circle(d=sd);
280 
281  // screw and nut slot; male removal
282  if (type == 1)
283  {
284  // screw
285  if ( m2 )
286  translate([0, -sl/2])
287  square([sd, sl], center=true);
288 
289  // nut
290  if ( m3 )
291  translate([0, -sl + nh/2 + no])
292  {
293  square([ns, nh], center=true);
294 
295  // over-cut compensation for cutter radius
296  if (nr > 0)
297  for ( x=[-1,1], y=[-1,1] )
298  translate([ns/2*x, (nh -nr)/2*y])
299  circle(d=nr);
300  }
301 
302  // drill punch
303  if ( m4 )
304  rotate(45)
305  square([sd, sd]/2, center=true);
306  }
307  }
308  }
309 
310  // decode configuration
311  length = defined_e_or(conf, 0, conf);
312  depth = defined_e_or(conf, 1, length/10);
313  pin = defined_e_or(conf, 2, depth);
314  screw = defined_e_or(conf, 3, depth/6);
315  nut = defined_e_or(conf, 4, depth/3);
316 
317  // align construction
318  translate
319  (
320  [
321  select_ci( [ 0, -length/2, +length/2 ], defined_e_or(align, 0, 0), false ),
322  select_ci( [ 0, -depth/2, -depth ], defined_e_or(align, 1, 0), false ),
323  ]
324  )
325  for ( i = insts )
326  {
327  zero = defined_e_or(i, 0, 0);
328  offset = defined_e_or(i, 1, 0);
329  iform = defined_e_or(i, 2, 7);
330 
331  imode = defined_e_or(i, 3, mode);
332  ipin = defined_e_or(i, 4, pin);
333  iscrew = defined_e_or(i, 5, screw);
334  inut = defined_e_or(i, 6, nut);
335 
336  translate([ length/2 * zero + offset, 0 ])
337  box_screw( iform=iform, imode=imode, ipin=ipin, iscrew=iscrew, inut=inut );
338  }
339 }
340 
341 
342 //! @}
343 //! @}
344 
345 
346 //----------------------------------------------------------------------------//
347 // openscad-amu auxiliary scripts
348 //----------------------------------------------------------------------------//
349 
350 /*
351 BEGIN_SCOPE example;
352  BEGIN_OPENSCAD;
353  include <omdl-base.scad>;
354  include <transforms/base_cs.scad>;
355  include <models/2d/joint/box_screw.scad>;
356 
357  w = [50, 3];
358 
359  conf =
360  [
361  w.x,
362  w.y,
363  [3, 6, 1/8, 1/3, 1/3],
364  [1/2, w.y, 1, 1/2]
365  ];
366 
367  insts =
368  [
369  [-1, +3, 2+4],
370  [+0, +0, 1+2+4, 1+4+16],
371  [+1, -3, 1 +4],
372  ];
373 
374  mode = 1 + 2;
375 
376  translate([0,-w.y])
377  {
378  joint2d_box_screw(conf=conf, insts=insts, mode=mode, type=0);
379  difference()
380  {
381  translate([0, -w.y*3/4]) square([w.x, w.y * 3/2], center=true);
382  joint2d_box_screw(conf=conf, insts=insts, mode=mode, type=1);
383  }
384  }
385 
386  translate([0,w.y/2])
387  difference()
388  {
389  translate([0, w.y/2]) square([w.x, w.y*3/2], center=true);
390  joint2d_box_screw(conf=conf, insts=insts, mode=mode, type=2);
391  }
392 
393  // end_include
394  END_OPENSCAD;
395 
396  BEGIN_MFSCRIPT;
397  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
398  table_unset_all sizes;
399 
400  images name "sizes" types "sxga";
401  views name "views" views "top";
402 
403  variables set_opts_combine "sizes views";
404  variables add_opts "--viewall --autocenter --view=axes";
405 
406  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
407  END_MFSCRIPT;
408 END_SCOPE;
409 */
410 
411 //----------------------------------------------------------------------------//
412 // end of file
413 //----------------------------------------------------------------------------//
414 
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.
module joint2d_box_screw(conf, insts, mode=0, type=0, trim=false, align)
Create 2d edge profiles for screw and nut box joint construction.
Definition: box_screw.scad:433
module pg_rectangle(size=1, o, vr, vrm=1, vfn, center=false)
A polygon rectangle with vertex rounding.
Definition: polygon.scad:773
module intersection_cs(c=true, s)
Conditionally apply the difference intersection operation.
Definition: base_cs.scad:294
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.