omdl  v0.9.8
OpenSCAD Mechanical Design Library
catch_latch.scad
Go to the documentation of this file.
1 //! A catch latch generator.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2025
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 (Catch latch)
31  \amu_define group_brief (Catch latch generator.)
32 
33  \amu_include (include/amu/pgid_path_pstem_pg.amu)
34 *******************************************************************************/
35 
36 //----------------------------------------------------------------------------//
37 // group and macros.
38 //----------------------------------------------------------------------------//
39 
40 /***************************************************************************//**
41  \amu_include (include/amu/group_in_parent_start.amu)
42  \amu_define includes_required_add
43  (
44  models/3d/fastener/screws.scad
45  )
46  \amu_include (include/amu/includes_required.amu)
47 *******************************************************************************/
48 
49 //----------------------------------------------------------------------------//
50 
51 //! A catch latch generator with configurable profile.
52 /***************************************************************************//**
53  \param size <decimal-list-6 | decimal> latch size;
54  a list [px, py, pz, bx, by, bz], the latch profile
55  and base-mount dimensions, or a single decimal px and
56  the remainder using predefined scaling of px.
57 
58  \param screw <decimal-list-5 | decimal> mount screw hole
59  specifications; a list [d, t, h, r, b], the
60  hole-diameter, tolerance, head-diameter,
61  recess-height, bevel-height, or a single decimal for
62  the hole diameter.
63 
64  \param latch <integer-list-1 | integer> latch; the latch type;
65  {1=no mount, 2=vertical mount, 3=horizontal mount}.
66 
67  \param catch <integer, decimal, decimal> | integer> catch; a list
68  [t, h, g], the catch type, thickness, and gap, or a
69  single decimal t for the catch type; {1=no mount,
70  2=vertical mount, 3=horizontal mount}.
71 
72  \param profile <datastruct> latch profile (see below).
73 
74  \param align <integer-list-3> object alignment; a list [x, y, z],
75  the latch or catch alignment. Available alignment
76  options depends on the object and its mount-type.
77 
78  \param verb <integer> console output verbosity {0=quiet, 1=info}.
79 
80  \details
81 
82  Construct a catch and/or latch. Both can be produced with or
83  without a mount, that can be oriented horizontally or vertically to
84  match the application. Constructs without mounts are useful for
85  direct incorporation with other design parts. The bore for the
86  mount is specified by the parameter \p screw and generated by
87  screw_bore().
88 
89  ## Multi-value and structured parameters
90 
91  ### profile
92 
93  #### Data structure fields: profile
94 
95  e | data type | default value | parameter description
96  ---:|:-----------------:|:-----------------:|:------------------------------------
97  0 | <decimal-list-3> | [0, 0, 0] | \p bevel; [f, v, h]
98  1 | <decimal-list-3> | [60, 100, 10] | \p width; [t, c, h]
99  2 | <decimal-list-3> | [20, 40, 60] | \p length; [t, s, e]
100 
101  \p bevel: a list [f, v, h], the flat-height, bevel-height,
102  bevel-width, specified as a percentage of the latch height.
103 
104  \p width: a list [t, c, h], the width of the throat, catch, head,
105  specified as a percentage of the latch width.
106 
107  \p length: a list [t, s, e], the length of the throat-end,
108  catch-start, catch-end, specified as a percentage of the latch
109  length.
110 
111  \amu_define scope_id (example)
112  \amu_define title (Catch latch example)
113  \amu_define image_views (top bottom diag)
114  \amu_define image_size (sxga)
115 
116  \amu_include (include/amu/scope_diagrams_3d.amu)
117 *******************************************************************************/
118 
119 module catch_latch
120 (
121  size = 5,
122 
123  screw,
124 
125  latch,
126  catch,
127 
128  profile,
129 
130  align,
131  verb = 0
132 )
133 {
134  // construct latch
135  module construct_latch()
136  {
137  extrude_linear_mss( h=bevel_mss_h )
138  polygon(latch_p);
139  }
140 
141  // construct catch
142  module construct_catch()
143  {
144  extrude_linear_mss( h=bevel_mss_h )
145  difference()
146  {
147  offset(r=catch_g+catch_h) polygon(latch_p);
148  offset(r=catch_g) polygon(catch_p);
149 
150  // open mouth
151  throat_w = hprofile_p[1].x*2;
152 
153  translate([0, -catch_h/2])
154  square([ throat_w + catch_g*2 + catch_h*2, catch_h + catch_g*2], center = true);
155  }
156  }
157 
158  // construct mount
159  module construct_mount(n, t)
160  {
161  difference()
162  {
163  union()
164  {
165  // mount
166  rotate([0, 90, 0])
167  extrude_linear_mss( h=mnt_dx, center=true )
168  pg_rectangle(size=[size_my, size_mz], vr=pad_vr, center=true);
169 
170  // pad
171  for( p=[-1, 1] )
172  translate([p*mnt_dx/2, pad_dy/2 - size_mz/2, 0])
173  extrude_linear_mss( h=size_my, center=true )
174  pg_rectangle(size=[pad_dx, pad_dy], vr=mnt_vr, center=true);
175  }
176 
177  // screw holes on both sizes of mount
178  for( s=[-1, 1] )
179  translate([s*mnt_dx/2, pad_dy/2 - size_mz/2, 0])
180  screw_bore(d=screw_d, l=size_my+eps*8, h=[screw_h, screw_r, screw_b], t=[screw_t]);
181  }
182 
183  if (verb > 0)
184  {
185  echo(strl
186  ([ n, " mount, type ", t, ": screw hole diameter = ",
187  screw_d, ", offset = ", mnt_dx
188  ]));
189  }
190  }
191 
192  // assemble
193  module assemble()
194  {
195  //
196  // latch
197  //
198 
199  if ( is_defined( latch ) )
200  {
201  if ( latch_t == 1 )
202  {
203  construct_latch();
204  }
205 
206  if ( latch_t == 2 )
207  {
208  construct_latch();
209 
210  translate([0, -size_my/2, size_mz/2])
211  mirror([0, 1, 0])
212  rotate([90, 0, 0])
213  construct_mount("latch", latch_t);
214 
215  // connect latch to mount
216  translate([0, -size_my/4+eps*2, size_mz/2])
217  extrude_linear_mss( h=size_mz, center=true )
218  pg_rectangle(size=[width_t*2, size_my/2], vr=2, vrm=[0, 0, 4, 3], center=true);
219  }
220 
221  if ( latch_t == 3 )
222  {
223  construct_latch();
224 
225  translate([0, -size_mz/2, size_my/2])
226  mirror([0, 0, 1])
227  rotate([180, 0, 0])
228  construct_mount("latch", latch_t);
229 
230  // connect latch to mount
231  rotate([270, 270, 270])
233  rotate([0, 0, 90])
234  projection(cut=true)
235  rotate([90, 0, 0])
236  construct_latch();
237  }
238  }
239 
240  //
241  // catch
242  //
243 
244  if ( is_defined( catch ) )
245  {
246  if ( catch_t == 1 )
247  {
248  construct_catch();
249  }
250 
251  if ( catch_t == 2 )
252  {
253  construct_catch();
254 
255  translate([0, size_my/2 + size_py + catch_g + catch_h*0, size_mz/2])
256  rotate([90, 0, 0])
257  construct_mount("catch", catch_t);
258  }
259 
260  if ( catch_t == 3 )
261  {
262  construct_catch();
263 
264  difference()
265  {
266  translate([0, -size_mz/8 + size_py + catch_g + catch_h, size_my/2])
267  construct_mount("catch", catch_t);
268 
269  resize([size_px+catch_g*4, size_py+catch_g*4, size_pz])
270  construct_latch();
271  }
272  }
273  }
274  }
275 
276  //
277  // global parameters
278  //
279 
280  // latch/catch profile size: x, y, z
281  size_px = defined_e_or(size, 0, size);
282  size_py = defined_e_or(size, 1, size_px*5/2);
283  size_pz = defined_e_or(size, 2, size_px*5/4);
284 
285  // latch/catch mount size: x, y, z
286  size_mx = defined_e_or(size, 3, size_py*5/3);
287  size_my = defined_e_or(size, 4, size_px/2);
288  size_mz = defined_e_or(size, 5, size_pz);
289 
290  // mount screw: hole-diameter, tolerance, head-diameter, recess-height, bevel-height
291  screw_d = defined_e_or(screw, 0, defined_or(screw, size_mz/3));
292  screw_t = defined_e_or(screw, 1, 0);
293  screw_h = defined_e_or(screw, 2, 0);
294  screw_r = defined_e_or(screw, 3, 0);
295  screw_b = defined_e_or(screw, 4, 0);
296 
297  // latch: mount-type
298  latch_t = defined_e_or(latch, 0, latch);
299 
300  // catch: mount-type, thickness, gap
301  catch_t = defined_e_or(catch, 0, catch);
302  catch_h = defined_e_or(catch, 1, size_px/5);
303  catch_g = defined_e_or(catch, 2, size_px/20);
304 
305  // latch profile
306  prof_b = defined_e_or(profile, 0, undef);
307  prof_w = defined_e_or(profile, 1, undef);
308  prof_l = defined_e_or(profile, 2, undef);
309 
310  // latch/catch edge bevel (percentages): v-flat, v-bevel, h-bevel
311  bevel_f = defined_e_or(prof_b, 0, 0) / 100 * size_pz;
312  bevel_v = defined_e_or(prof_b, 1, 0) / 100 * size_pz;
313  bevel_h = defined_e_or(prof_b, 2, 0) / 100;
314 
315  // latch/catch profile width percentages): throat, catch, head
316  width_t = defined_e_or(prof_w, 0, 60) / 100 * size_px/2;
317  width_c = defined_e_or(prof_w, 1, 100) / 100 * size_px/2;
318  width_h = defined_e_or(prof_w, 2, 10) / 100 * size_px/2;
319 
320  // latch/catch profile length (percentages): throat-end, catch-start, catch-end
321  length_t = defined_e_or(prof_l, 0, 20) / 100 * size_py;
322  length_s = defined_e_or(prof_l, 1, 40) / 100 * size_py;
323  length_e = defined_e_or(prof_l, 2, 60) / 100 * size_py;
324 
325  //
326  // global variables
327  //
328 
329  // mount pad: separation, size-y, size-x, rounding(s)
330  mnt_dx = size_mx - size_mz;
331 
332  pad_dy = max(screw_d*3, size_mz);
333  pad_dx = pad_dy + screw_t;
334 
335  mnt_vr = pad_dy*2/5;
336  pad_vr = size_my/2;
337 
338  // half-profile polygon points, rounding, and rounding-mode
339  hprofile_p =
340  [
341  origin2d, // base
342  [width_t, 0], [width_t, length_t], // throat; start, end
343  [width_c, length_s], [width_c, length_e], // catch; start, end
344  [width_h, size_py] // head
345  ];
346 
347  hprofile_vr = [1/3, 1/2, 1, 1, 1/10] * size_px/2;
348  hprofile_vrm = [0, 1, 1, 1, 1];
349 
350  // assemble full profile polygon points, rounding, and rounding-modes
351  profile_p = concat( [for (i=hprofile_p) i], [for (i=reverse(hprofile_p)) [-i.x, i.y]] );
352  profile_vr = concat( [for (i=hprofile_vr) i], [for (i=reverse(hprofile_vr)) i] );
353  latch_vrm = concat( [for (i=hprofile_vrm) i], [for (i=reverse(hprofile_vrm)) i] );
354 
355  // flare-out catch mouth; replace first and last rounding elements of latch
356  catch_vrm = concat( headn(concat( [3], tailn(latch_vrm))), [4] );
357 
358  // rounded polygon points for latch and catch
359  latch_p = polygon_round_eve_all_p(c=profile_p, vr=profile_vr, vrm=latch_vrm);
360  catch_p = polygon_round_eve_all_p(c=profile_p, vr=profile_vr, vrm=catch_vrm);
361 
362  // mss extrusion for bevels
363  bevel_mss_h =
364  let( h1 = bevel_f, h2 = bevel_v, s = 1 - bevel_h )
365  [ [h1, [s, s]], [h2, [s, 1]], size_pz - (h1 + h2)*2, [h2, [1, s]], [h1, [s, s]] ];
366 
367  //
368  // orientation and alignment configuration
369  //
370  oaa = let
371  (
372  idl = is_defined(latch), idc = is_defined(catch),
373  tl = latch_t, tc = catch_t,
374 
375  // repeating values and configurations
376  v01 = catch_g+catch_h,
377  v02 = +size_mz/8-size_py-v01,
378 
379  c01 = [ [0,0,0], [0,0,0], [ [0],[0],[0] ] ],
380  c02 = [0, +mnt_dx/2+pad_dx/2, +mnt_dx/2, -mnt_dx/2, -mnt_dx/2-pad_dx/2],
381  c03 = [0, -size_pz/2, -size_pz, -pad_dy/2, -pad_dy],
382  c04 = [0, -size_my/2, -size_my, -size_pz/2, -size_pz]
383  )
384  !idc && idl ? // latch only
385  (
386  (tl == 1) ? [ [0, 0, 0], [0, 0, 0],
387  [ [0, -width_t, +width_t],
388  [0, -size_py/2, -size_py],
389  [0, -size_pz/2, -size_pz] ] ]
390  : (tl == 2) ? [ [0, 0, 0], [0, +size_my, 0],
391  [ c02,
392  [0, -size_my, -size_py/2-size_my, -size_py-size_my],
393  c03 ] ]
394  : (tl == 3) ? [ [0, 0, 0], [0, +pad_dy, 0],
395  [ c02,
396  [0, -pad_dy/2, -pad_dy, -size_py/2-pad_dy, -size_py-pad_dy],
397  c04 ] ]
398  : c01
399  )
400  : idc && !idl ? // catch only
401  (
402  (tc == 1) ? [ [0, 0, 180], [0, +size_py+v01, 0],
403  [ [0, +size_px/2+v01, -size_px/2-v01],
404  [0, -catch_h, -size_py/2-catch_h, -size_py-catch_h],
405  [0, -size_pz/2, -size_pz] ] ]
406  : (tc == 2) ? [ [0, 0, 180], [0, +size_py+catch_g+size_my, 0],
407  [ c02,
408  [0, -size_my, -size_py/2-size_my-catch_g, -size_py-size_my],
409  c03 ] ]
410  : (tc == 3) ? [ [0, 0, 180], [0, pad_dy/2-v02, 0],
411  [ c02,
412  [0, -pad_dy/2, -pad_dy, v02-pad_dy/2+size_py/2, v02-pad_dy/2],
413  c04 ] ]
414  : c01
415  )
416  : idc && idl ? // both latch and catch
417  (
418  [ [0, 0, 0], [0, 0, 0],
419  [ c02,
420  [ 0,
421  -size_py/2, +v02/2+pad_dy/4,
422  -size_py-catch_g, +size_my, -size_py-catch_g-size_my,
423  +pad_dy, +pad_dy/2, +v02+pad_dy/2, +v02, +v02-pad_dy/2
424  ],
425  concat(c03, [-size_my]) ]
426  ]
427  )
428  : c01; // neither
429 
430  align_x = select_ci ( third(oaa).x, defined_e_or(align, 0, 0), false );
431  align_y = select_ci ( third(oaa).y, defined_e_or(align, 1, 0), false );
432  align_z = select_ci ( third(oaa).z, defined_e_or(align, 2, 0), false );
433 
434  //
435  // object assembly
436  //
437 
438  translate(second(oaa) + [align_x, align_y, align_z])
439  rotate(first(oaa))
440  assemble();
441 }
442 
443 //! @}
444 //! @}
445 
446 
447 //----------------------------------------------------------------------------//
448 // openscad-amu auxiliary scripts
449 //----------------------------------------------------------------------------//
450 
451 /*
452 BEGIN_SCOPE example;
453  BEGIN_OPENSCAD;
454  include <omdl-base.scad>;
455  include <models/3d/fastener/screws.scad>;
456  include <parts/3d/fastener/catch_latch.scad>;
457 
458  s = 10;
459  p = [10, 25, 10];
460  c = [5, 4, 10, 2, 1] * s/10;
461 
462  catch_latch(latch=2, size=s, profile=[p], screw=c);
463 
464  translate([0, s*7, 0]) rotate([0, 0, 180])
465  catch_latch(catch=3, size=s, profile=[p], screw=c);
466 
467  // end_include
468  END_OPENSCAD;
469 
470  BEGIN_MFSCRIPT;
471  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
472  table_unset_all sizes;
473 
474  images name "sizes" types "sxga";
475  views name "views" views "top bottom diag";
476 
477  variables set_opts_combine "sizes views";
478  variables add_opts "--viewall --autocenter --view=axes";
479 
480  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
481  END_MFSCRIPT;
482 END_SCOPE;
483 */
484 
485 //----------------------------------------------------------------------------//
486 // end of file
487 //----------------------------------------------------------------------------//
488 
origin2d
<point-2d> The origin point coordinate in 2d Euclidean space.
Definition: constants.scad:407
eps
<decimal> Epsilon, small distance to deal with overlapping shapes.
Definition: constants.scad:195
grid_coarse
OpenSCAD coarse grid limit.
Definition: constants.scad:307
function headn(v, n=1)
Return a list containing all but the last n elements of an iterable value.
function defined_e_or(v, i, d)
Return an element of an iterable when it exists or a default value otherwise.
function third(v)
Return the third element of an iterable value.
function second(v)
Return the second element of an iterable value.
function first(v)
Return the first element of an iterable value.
function tailn(v, n=1)
Return a list containing all but the first n elements of an iterable value.
function reverse(v)
Reverse the elements of an iterable value.
function select_ci(v, i, l=true)
Select specified element from list or return a default.
function strl(v)
Convert a list of values to a concatenated string.
function defined_or(v, d)
Return given value, if defined, or a secondary value, if primary is not defined.
function is_defined(v)
Test if a value is defined.
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.
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 catch_latch(size=5, screw, latch, catch, profile, align, verb=0)
A catch latch generator with configurable profile.
module pg_rectangle(size=1, o, vr, vrm=1, vfn, center=false)
A polygon rectangle with vertex rounding.
Definition: polygon.scad:765
module extrude_rotate_tr(r, pa=0, ra=360)
Translate, rotate, and revolve a 2d shape about the z-axis.
Definition: extrude.scad:457
module extrude_linear_mss(h, center=false, c=true)
Linearly extrude a 2d shape with multi-segment uniformly-spaced profile scaling.
Definition: extrude.scad:748