omdl  v1.0
OpenSCAD Mechanical Design Library
project_box_rectangle.scad
Go to the documentation of this file.
1 //! A rectangular box maker for project boxes, enclosures and housings.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2024,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 (Rectangular Project Box)
31  \amu_define group_brief (Rectangular prism project box generator.)
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  shapes/select_common_2d.scad
45  shapes/select_common_3d.scad
46  transforms/base_cs.scad
47  transforms/layout.scad
48  )
49  \amu_include (include/amu/includes_required.amu)
50 *******************************************************************************/
51 
52 //----------------------------------------------------------------------------//
53 
54 //! A rectangular box maker for project boxes, enclosures and housings.
55 /***************************************************************************//**
56  \param wth <decimal> wall thickness.
57 
58  \param h <datastruct | decimal> box extrusion height;
59  structured data or a single decimal to set the box
60  height.
61 
62  \param size <decimal-list-2 | decimal> box base size; a list [x, y]
63  or a single decimal for (x=y).
64 
65  \param vr <decimal-list-4 | decimal> wall corner rounding radius;
66  a list [v1r, v2r, v3r, v4r] or a single decimal for
67  (v1r=v2r=v3r=v4r).
68 
69  \param vrm <integer-list-4 | integer> wall corner rounding mode =
70  {0:none, 1:bevel, 2:round}; a list [v1rm, v2rm, v3rm,
71  v4rm] or a single decimal for (v1rm=v2rm=v3rm=v4rm).
72 
73  \param inset <decimal-list-2 | decimal> wall-to-lid negative offset;
74  a list [x, y] or a single decimal for (x=y).
75 
76  \param lid <datastruct | decimal> lid extrusion height;
77  structured data or a single decimal to set the lid
78  height.
79 
80  \param lip <datastruct | integer> wall lip; structured data or a
81  single integer to set the lip mode.
82 
83  \param rib <datastruct | integer> wall ribs; structured data or a
84  single integer to set the rib mode.
85 
86  \param wall <datastruct> interior walls; structured data.
87 
88  \param post <datastruct> posts; structured data.
89 
90  \param hole <datastruct> holes; structured data.
91 
92  \param shape <datastruct> shapes; structured data.
93 
94  \param align <integer-list-3> box alignment; a list [x, y, z].
95 
96  \param mode <integer> module mode of operation.
97 
98  \param verb <integer> console output verbosity
99  {0=quiet, 1=info, 2=details}.
100 
101  \details
102 
103  Construct a rectangular enclosure for electronic and or other
104  project boxes of the like. This module is designed to construct no
105  more than one attached exterior cover and four exterior walls for
106  each invocation. Therefore, a minimum of two invocations are
107  required to construct a complete enclosure with six sides (top,
108  walls, and bottom). Only the parameters \p wth and \p size are
109  required; all others are optional.
110 
111  In a simple case, a box would consist of a (1) cover with attached
112  walls and (2) a separate opposite cover, without walls, to close
113  the box. This would require exactly two invocations. More than two
114  invocations can be used to construct boxes with multiple
115  mid-sections and/or removable covers on one or both sides as shown
116  in the following example.
117 
118  \amu_define scope_id (example_multiple)
119  \amu_define title (Muilti-section project box example)
120  \amu_define image_views (front diag)
121  \amu_define image_size (sxga)
122 
123  \amu_include (include/amu/scope_diagrams_3d.amu)
124 
125  Notice that the middle sections omit the \p lid parameter to skip
126  cover construction in this example.
127 
128  ## Parameter defaults
129 
130  Default values are calculated for all parameters. In some cases,
131  the defaults are calculated based on prior user supplied values.
132  For example, post diameters are calculated based on the supplied
133  screw size for a post instance. Whenever a configuration consists
134  of a list of values, there is no requirement to supply values for
135  all elements of the list. Generally speaking, the list may be
136  terminated at the parameter of interest. The caveat is for
137  parameters of interest that comes after those which are not of
138  interest. In this case, the prior values may be assigned a suitable
139  value or may be assigned the value \b undef to use the calculated
140  default as demonstrated below.
141 
142  \code
143  full_config = [ 12.42, [1,2], [2,1], 0, [1,2], 31, [1,2,3], 0 ];
144  partial_config = [ undef, undef, undef, 2, undef, 31 ];
145  \endcode
146 
147  ## Rounding and extrusions
148 
149  The edge rounding during shape construction is performed using the
150  function polygon_round_eve_all_p(). Refer to its documentation for
151  more information on the rounding modes and their descriptions.
152 
153  All height extrusions for walls, ribs, posts, etc, are performed by
154  the function extrude_linear_mss(). This facilitates flexible
155  scaling along the extrusion height as described in that functions
156  documentation. The <em>box bottom section example</em> at the end
157  of this page shows how this scaling can be used to create features,
158  such as corner rounding and face protrusion, along the lid and box
159  height.
160 
161  ## Multi-value and structured parameters
162 
163  ### h, lid
164 
165  The height of the exterior walls and the box lid are the result of
166  extrusions produced using the function extrude_linear_mss(). The
167  height can be specified as a single decimal value or may be
168  specified as list of scaled sections along the height. For a
169  description on specifying scaled extrusions see the documentation
170  for this extrusion function.
171 
172  ### lip
173 
174  By way of the parameter \p lip, the box walls can have an overhang
175  that interfaces with adjacent wall or lid sections. The adjacent
176  section should be constructed with an opposite lip orientation
177  using the mode configuration. The \p lip is considered to be a
178  feature of the exterior enclosure walls and therefore a specified
179  \p lip height should always be less than or equal to the total wall
180  height \p h.
181 
182  #### Data structure schema:
183 
184  name | schema
185  ---------------:|:----------------------------------------------
186  lip | [ mode, height, width, taper, snap, removals ]
187 
188  #### Data structure fields: lip
189 
190  e | data type | default value | parameter description
191  ---:|:-----------------:|:-----------------:|:------------------------------------
192  0 | integer | required | mode
193  1 | decimal | wth | height
194  2 | decimal | 35 | base width (percentage of wall thickness)
195  3 | decimal | 10 | top taper width (percentage of wall thickness)
196  4 | datastruct \| integer | 0 | snap configuration
197  5 | datastruct | undef | removals instances
198 
199  ##### lip[0]: mode
200 
201  Integer value is binary encoded.
202 
203  b | description
204  ---:|:---------------------------------------
205  0 | inside lip at upper edge of wall
206  1 | outer lip at upper edge of wall
207  2 | inside lip at lower edge of wall
208  3 | outer lip at lower edge of wall
209  4 | center pin at upper wall
210  5 | inside lip at upper edge of wall
211  6 | center pin at lower wall
212  7 | inside lip at lower edge of wall
213 
214  ##### lip[4]: snap
215 
216  e | data type | default value | parameter description
217  ---:|:-----------------:|:-----------------:|:------------------------------------
218  0 | integer | required | snap segments of total steps
219  1 | integer | 5 | total segment steps
220  2 | decimal | 1.0 | max scale (1 = 1x taper width)
221  3 | decimal-list | [0, 3.5/5, 4.5/5, 1, 4.5/5, 3.5/5, 0] | scale multiplier steps
222 
223  ##### lip[5]: removals
224 
225  ##### Data structure schema:
226 
227  name | schema
228  ---------------:|:----------------------------------------------
229  removals | [ removal, removal, ..., removal ]
230  removal | [ mode, axis, offset ]
231 
232  ##### Data structure fields: removal instance
233 
234  e | data type | default value | parameter description
235  ---:|:-----------------:|:-----------------:|:------------------------------------
236  0 | integer | required | mode: lip section application
237  1 | integer-list-2 | [0, 0] | axis [x, y]; {0=unchanged, 1=remove}
238  2 | integer-list-2 | [0, 0] | offset [x, y]; {-1, 0, +1}
239 
240  Any lip sections specified by the \p mode variable can have their
241  walls selectively removed, enabling features like sliding lid
242  assembly, for example. This removal specification allows anywhere
243  from zero to all four sections to be removed, with each lip mode
244  being independent. Removals are defined by one or more removal
245  instances. If a mode is repeated in the instance list, the last
246  occurrence is used.
247 
248  \amu_define scope_id (example_lip)
249  \amu_define title (slide-in snap-lip example)
250  \amu_define image_views (front back diag)
251  \amu_define image_size (sxga)
252 
253  \amu_include (include/amu/scope_diagrams_3d.amu)
254 
255  The above shows an example of a lip with a basic snap-in edge
256  configuration, with sections removed from the upper and lower
257  halves of the box. This allows the box to be assembled by sliding
258  the halves together. If the sections were not removed, the two
259  halves could instead be snapped together and held in place by the
260  configured snap edges on the lip.
261 
262  Depending on the flexibility of the construction material, the
263  engagement and gap sizes of the mating halves may need to be
264  adjusted. Specifically, this includes considering the base width,
265  taper width, and maximum snap scaling.
266 
267  ### rib
268 
269  The exterior box walls and lid rigidity can be reinforced using a
270  this options to configure and construct a grid of rib-like
271  structures which are embed on the interior surface of the walls and
272  lid.
273 
274  #### Data structure schema:
275 
276  name | schema
277  ---------------:|:----------------------------------------------
278  rib | [ mode, extrusions, coverage, counts ]
279  extrusions | [ base, x-extrusion, y-extrusion ]
280 
281  #### Data structure fields: rib
282 
283  e | data type | default value | parameter description
284  ---:|:-----------------:|:-----------------:|:------------------------------------
285  0 | integer | required | mode
286  1 | datastruct | (see below) | base and height extrusions
287  2 | decimal-list-3:1 \| decimal | 10 | [x, y, h] coverage percentage
288  3 | integer-list-3:1 \| integer | (calculated) | [x, y, h] count override
289 
290  ##### rib[0]: mode
291 
292  Integer value is binary encoded.
293 
294  b | description
295  ---:|:---------------------------------------
296  0 | no ribs on the lid
297  1 | no ribs on the x-positive wall
298  2 | no ribs on the y-positive wall
299  3 | no ribs on the x-negative wall
300  4 | no ribs on the y-negative wall
301  5-6 | lip coverage count (2-bit encoded integer)
302  7 | offset all ribs to bottom of lower lip
303 
304  ##### rib[1]: base and height extrusion
305 
306  e | data type | default value | parameter description
307  ---:|:-----------------:|:-----------------:|:------------------------------------
308  0 | decimal | wth | rib base width
309  1 | decimal-list-2 |[[wth, rib_edx]] | x-axis oriented rib height extrusion
310  2 | decimal-list-2 |[[wth, rib_edy]] | y-axis oriented rib height extrusion
311 
312  The constants \c rib_edx and \c rib_edy are defaults that
313  approximates a half-ellipse rib-like shape. To specify alternative
314  custom extrusions, see the documentation for the extrusion function
315  extrude_linear_mss().
316 
317  ### wall
318 
319  The parameter \p wall may be used to define one or more interior
320  walls to be constructed. There are two types which correspond to
321  walls parallel to the x-axis (type 0) or y-axis (type 1). Each wall
322  instance may be moved, scaled, or rotated.
323 
324  #### Data structure schema:
325 
326  name | schema
327  ---------------:|:----------------------------------------------
328  wall | [ configuration, instances ]
329  configuration | [ mode, defaults ]
330  defaults | [ w, h, vr, vrm ]
331  instances | [ instance, instance, ..., instance ]
332  instance | [ type, move, scale, rotate, size, h, vr, vrm ]
333 
334  #### Data structure fields: wall
335 
336  e | data type | default value | parameter description
337  ---:|:-----------------:|:-----------------:|:------------------------------------
338  0 | datastruct | required | configuration
339  1 | datastruct-list | required | instance list
340 
341  ##### wall[0]: configuration
342 
343  e | data type | default value | parameter description
344  ---:|:-----------------:|:-----------------:|:------------------------------------
345  0 | integer | required | mode
346  1 | datastruct | (see below) | defaults
347 
348  ###### wall[0]: configuration[0]: mode
349 
350  Integer value is binary encoded.
351 
352  b | description
353  ---:|:---------------------------------------
354  0-1 | wall end rounding {0:none, 1:bevel, 2:fillet, 3:round-out}
355  2-4 | wall top rounding (1)
356  5-7 | wall base rounding (1)
357  8-9 | wall on lips {0:none, 1:one, 2:both}
358  10 | offset all walls to bottom of lower lip
359 
360  (1) The bits 2-4 and bits 5-7, configure the rounding mode for the
361  wall top and wall base, respectively. The 3-bits integer values are
362  indexed to the rounding options as summarized in the following
363  table:
364 
365  Integer value is binary encoded.
366 
367  v | wall top rounding | wall base rounding
368  ---:|:-----------------:|:-----------------:
369  0 | none | none
370  1 | bevel-in | bevel-in
371  2 | round-in | round-in
372  3 | fillet-in | fillet-in
373  4 | bevel-out | bevel-out
374  5 | fillet-out | fillet-out
375  6 | round-out | round-out
376 
377  ###### wall[0]: configuration[1]: defaults
378 
379  e | data type | default value | parameter description
380  ---:|:-----------------:|:-----------------:|:------------------------------------
381  0 | decimal | wth | width
382  1 | datastruct | cfg_he | extrusion
383  2 | decimal-list-4 \| decimal | wth | rounding radius
384  3 | integer-list-4 \| integer | cfg_vrm | rounding mode
385 
386  The constants \c cfg_he and \c cfg_vrm define defaults that may
387  be used to round the top, base, and edges of a wall.
388 
389  ##### wall[1]: instance
390 
391  e | data type | default value | parameter description
392  ---:|:-----------------:|:-----------------:|:------------------------------------
393  0 | integer | required | type {0: x-axis, 1:y-axis}
394  1 | decimal-list-3:2 \| decimal | [0, 0, 0] | move
395  2 | decimal-list-3:2 \| decimal | [1, 1, 1] | scale
396  3 | decimal-list-3:1 \| decimal | [0, 0, 0] | rotate
397  4 | decimal-list-2 | tdef_s | size
398  5 | decimal-list-2 | tdef_he | extrusion
399  6 | decimal-list-4 \| decimal | tdef_vr | rounding radius
400  7 | integer-list-4 \| integer | tdef_vrm | rounding mode
401 
402  The constants \c tdef_s, \c tdef_he, \c tdef_vr, and \c tdef_vrm
403  are default values that depend on the wall type being constructed.
404  For example, `tdef_s = [max_x, wth]` for wall type \c 0 and `tdef_s
405  = [wth, max_y]` for wall type \c 1. These default may be overridden
406  to provide custom wall sizes, wall rounding and/or height
407  extrusions for each wall instance.
408 
409  ### post
410 
411  This parameter may be used to define one or more screw posts for
412  securing covers, circuit boards, mounts, and the like. There are
413  two types of predefined posts; normal posts and recessed access
414  posts. Recessed posts have a widened access tunnel where screws can
415  be inserted through and below the surface where the post is
416  attached. All posts (and holes) may be rounded at the bottom, to
417  widen the contact with the the adjacent surface, or at the top, to
418  enhance contact or smooth its edges. Each post instance consists of
419  two holes, a post with optional fins, and can be aligned, moved,
420  and rotated. The optional post fins can be either triangular or
421  rectangular in shape.
422 
423  #### Data structure schema:
424 
425  name | schema
426  ---------------:|:----------------------------------------------
427  post | [ configuration, instances ]
428  configuration | [ mode, defaults ]
429  defaults | [ hole0, hole1, post1, hole2, post2, fins0, fins1, calculation ]
430  hole, post (1) | [ d, h, ho, da, ha, vr, vrm ]
431  fins (2) | [ c, sweep-angle, w, d-scale, h-scale, vr, vrm ]
432  calculation | [ hole1-mul, hole1-add, post1-mul, post1-add, hole2-mul, hole2-add, post2-mul, post2-add ]
433  instances | [ instance, instance, ..., instance ]
434  instance | [ type, align. move, rotate, hole0, hole1, post, fins ]
435 
436  (1) All numbered and unnumbered holes and posts utilize this form.
437  (2) All fins defaults and instances are of this form.
438 
439  #### Data structure fields: post
440 
441  e | data type | default value | parameter description
442  ---:|:-----------------:|:-----------------:|:------------------------------------
443  0 | datastruct | required | configuration
444  1 | datastruct-list | required | instance list
445 
446  ##### post[0]: configuration
447 
448  e | data type | default value | parameter description
449  ---:|:-----------------:|:-----------------:|:------------------------------------
450  0 | integer | required | mode
451  1 | datastruct | (see below) | defaults
452 
453  ###### post[0]: configuration[0]: mode
454 
455  Integer value is binary encoded.
456 
457  b | description
458  ---:|:---------------------------------------
459  0-1 | post rounding {0:none, 1:bevel, 2:filet}
460  2-3 | fin rounding {0:none, 1:bevel, 2:filet}
461  4 | post base rounded same as top {0:opposite, 1:same}
462  5 | set auxiliary screw hole on opposite side of lid
463  6 | re-calculate defaults with each instance (1)
464  7 | post type that extends into lip height {0:recessed, 1:normal}
465  8 | lip height extension count {0:one, 1:both}
466  9 | offset all posts to bottom of lower lip
467 
468 
469  (1) The post and secondary hole diameter defaults are calculated as
470  shown under calculation described below. This mode bit controls
471  when the calculation is performed; either when defaults are
472  configured (b=0), or when a post instance is created (b=1).
473 
474  ###### post[0]: configuration[1]: defaults
475 
476  e | data type | default value | parameter description
477  ---:|:-----------------:|:-----------------:|:------------------------------------
478  0 | datastruct | (see below) | hole0; the screw hole
479  1 | datastruct | (see below) | hole1; post 1 aux hole
480  2 | datastruct | (see below) | post1; normal-post
481  3 | datastruct | (see below) | hole2; post 2 access hole
482  4 | datastruct | (see below) | post2; recessed-post
483  5 | datastruct | (see below) | fins0: triangular-fins
484  6 | datastruct | (see below) | fins1: rectangular-fins
485  7 | datastruct | (see below) | calculation
486 
487  ###### post[0]: configuration[1]: defaults[0]: hole0
488 
489  The default values and instance configuration for post, hole0,
490  hole1, post1, hole2, and post2 follow the schema described in the
491  table below, with the only difference being the default values.
492  These defaults are calculated according to the method outlined in
493  the "Defaults Calculation" section. Specifically, the default
494  heights for the posts and screw holes are determined based on the
495  post height and other configured requirements.
496 
497  e | data type | default value | parameter description
498  ---:|:-----------------:|:-----------------:|:------------------------------------
499  0 | decimal | 3.25 | diameter
500  1 | decimal | (maximum) | height
501  2 | decimal | 0 | height offset
502  3 | decimal | 0 | (1) \em late diameter adjust
503  4 | decimal | 0 | (1) \em late height adjust
504  5 | decimal-list-4 \| decimal| 0 | rounding radius
505  6 | integer-list-4 \| integer| 0 | rounding mode
506  7 | decimal | 0 | (2) removal height
507  8 | decimal | 0 | (2) removal height offset
508  9 | decimal-list-2 \| decimal | [0, 0] | (2) removal top cut angles [t, r]
509  10| decimal-list-2 \| decimal | [0, 0] | (2) removal bottom cut angles [t, r]
510 
511  (1) Elements 3–4 are used for \em late adjustments to post and hole
512  diameters and heights. By \em late, it is meant that these elements
513  allow dimensional changes to be made without affecting dependent
514  value calculations. This is useful for creating screw-hole
515  clearances or increasing diameters to accommodate brass threaded
516  inserts, for example. Another application is post height adjustment
517  to provide clearance for circuit board mounting.
518 
519  (2) Elements 7–10 may be used to remove sections of the post
520  structure. This feature allows support for posts that do not extend
521  the full height of the enclosure. The ends of the removed sections
522  may be cut at an angle to provide a gradual phase-in during 3D
523  printing, eliminating the need for additional support structures.
524 
525  ###### post[0]: configuration[1]: defaults[5]: fins0: triangular-fins
526 
527  e | data type | default value | parameter description
528  ---:|:-----------------:|:-----------------:|:------------------------------------
529  0 | integer | 4 | count
530  1 | decimal | 360 | distribution angle
531  2 | decimal | wth | width
532  3 | decimal | 1/5 | post diameter fraction
533  4 | decimal | 5/8 | post height fraction
534  5 | decimal-list-3 \| decimal | def_f0_vr | rounding radius
535  6 | integer-list-3 \| integer | def_f0_vrm | rounding mode
536 
537  The constants \em def_f0_vr and \em def_f0_vrm define defaults for
538  fin rounding and may be overridden if needed. See the source code
539  for more details.
540 
541  ###### post[0]: configuration[1]: defaults[6]: fins1: rectangular-fins
542 
543  e | data type | default value | parameter description
544  ---:|:-----------------:|:-----------------:|:------------------------------------
545  0 | integer | 4 | count
546  1 | decimal | 360 | distribution angle
547  2 | decimal | wth | width
548  3 | decimal | 1/2 | post diameter fraction
549  4 | decimal | 1 | post height fraction
550  5 | decimal-list-4 \| decimal | def_f1_vr | rounding radius
551  6 | integer-list-4 \| integer | def_f1_vrm | rounding mode
552 
553  The constants \em def_f1_vr and \em def_f1_vrm define defaults for
554  fin rounding and may be overridden if needed. See the source code
555  for more details.
556 
557  ###### post[0]: configuration[1]: defaults[7]: calculation
558 
559  e | data type | default value | parameter description
560  ---:|:-----------------:|:-----------------:|:------------------------------------
561  0 | decimal | 0 | hole1 multiplier
562  1 | decimal | 0 | hole1 addition
563  2 | decimal | 3.0 | post1 multiplier
564  3 | decimal | wth/2 | post1 addition
565  4 | decimal | 2.0 | hole2 multiplier
566  5 | decimal | wth/2 | hole2 addition
567  6 | decimal | 4.0 | post2 multiplier
568  7 | decimal | wth/2 | post2 addition
569 
570  For hole1, hole2, post1, and post2, the diameters are calculated
571  based on the following model:
572 
573  \code
574  diameter = hole0 + wth * multiplier + addition
575  \endcode
576 
577  where \c hole0 is the screw-hole diameter and \c wth is the
578  configured wall thickness parameter value. This allow for a simple
579  way to generate posts and holes that are dependent on the screw
580  hole diameter. The multiplier and fixed additions for each hole and
581  post may be configured to replace the values shown in the above
582  table.
583 
584  ##### post[1]: instance
585 
586  e | data type | default value | parameter description
587  ---:|:-----------------:|:-----------------:|:------------------------------------
588  0 | integer | required | type
589  1 | decimal-list-3:1 | [0, 0, 0] | zero
590  2 | decimal-list-3:2 | [0, 0, 0] | move
591  3 | decimal-list-3:1 \| decimal | [0, 0, 0] | rotate
592  4 | datastruct | (see note) | (1) hole0; screw hole
593  5 | datastruct | (see note) | (1) hole1; aux hole
594  6 | datastruct | (see note) | (1) post
595  7 | datastruct | (see note) | (1) fins
596 
597  (1) The elements of the datastruct for hole0, hole1, post, and fins
598  instances are described in the post configuration defaults section,
599  which covers posts, holes, and fins.
600 
601  \note The default values for the holes, post, and fins are
602  determined by their type. When not explicitly specified for an
603  instance, these values are automatically derived from the
604  configured default settings, as outlined above.
605 
606  ###### post[1]: instance[0]: type
607 
608  Integer value is binary encoded.
609 
610  b | description
611  ---:|:---------------------------------------
612  0 | post type {0:normal, 1:recessed}
613  1 | fin type {0:triangular, 1:rectangular}
614 
615  ###### post[1]: instance[1]: zero
616 
617  The x and y zero can be assigned decimal values in the interval
618  (-1, +1), to set the post zero alignment position along the
619  enclosure x and y dimensions. The z zero can be assigned a decimal
620  value in the interval (-1, 0), to set the post zero alignment
621  position along the enclosure lid height.
622 
623  ### hole
624 
625  #### Data structure schema:
626 
627  This parameter can be used to create holes in the enclosure using
628  common or user-defined 2D shapes. Typical applications include
629  openings for vent holes, LEDs, switches, and fans. For more
630  information on defining and using custom shapes, see
631  select_common_2d_shape(). Hole placement is performed using
632  layout_grid_rp(); refer to its documentation for details on usage
633  and configuration options. The layout bounding box is the center of
634  the enclosure walls.
635 
636  name | schema
637  ---------------:|:----------------------------------------------
638  hole | [instances]
639  instances | [instance, instance, ..., instance ]
640 
641  #### Data structure fields: instance
642 
643  e | data type | default value | parameter description
644  ---:|:-----------------:|:-----------------:|:------------------------------------
645  0 | datastruct \| integer | 1 | 2d shape selections (see: select_common_2d_shape())
646  1 | decimal | 0 | shape extrusion height (see note below)
647  2 | datastruct | [0] | shape layout (see: layout_grid_rp()
648 
649  When the shape extrusion height is \p 0, the height is
650  automatically derived from the maximum enclosure dimension. When
651  set to \p −1, \p −2, or \p −3, the height is derived from the
652  width, length, or height dimension, respectively. Any other
653  positive value explicitly sets the extrusion height.
654 
655  ### shape
656 
657  This parameter can be used to add features to the enclosure design
658  using common or user-defined 3D shapes. Typical applications
659  include standoffs, feet, handles, and cosmetic detailing. For more
660  information on defining and using custom shapes, see
661  select_common_3d_shape(). Shape placement is performed using
662  layout_grid_rp(); refer to its documentation for details on usage
663  and configuration options. The layout bounding box is the center of
664  the enclosure walls.
665 
666  #### Data structure schema:
667 
668  name | schema
669  ---------------:|:----------------------------------------------
670  shape | [instances]
671  instances | [instance, instance, ..., instance ]
672 
673  #### Data structure fields: instance
674 
675  e | data type | default value | parameter description
676  ---:|:-----------------:|:-----------------:|:------------------------------------
677  0 | datastruct \| integer | 1 | 3d shape selections (see: select_common_3d_shape())
678  1 | datastruct | [0] | shape layout (see: layout_grid_rp()
679 
680  ### align
681 
682  The x, y, and z axis of the box can be aligned independently using
683  this parameter.
684 
685  e | data type | default value | parameter description
686  ---:|:-----------------:|:-----------------:|:------------------------------------
687  0 | integer | 0 | x-axis alignment
688  1 | integer | 0 | y-axis alignment
689  2 | integer | 0 | z-axis alignment
690 
691  #### align[0-1]: x or y axis alignment
692 
693  v | description
694  ---:|:---------------------------------------
695  0 | center of enclosure
696  1 | positive exterior edge of lid
697  2 | positive exterior edge of wall
698  3 | positive interior edge of wall
699  4 | negative interior edge of wall
700  5 | negative exterior edge of wall
701  6 | negative exterior edge of lid
702 
703  #### align[2]: z-axis alignment
704 
705  v | description
706  ---:|:---------------------------------------
707  0 | bottom edge of lid
708  1 | bottom edge of wall
709  2 | middle of enclosure
710  3 | middle of wall
711  4 | top edge of wall
712  5 | top edge of lip
713 
714  ### mode
715 
716  Integer value is binary encoded.
717 
718  b | description
719  ---:|:---------------------------------------
720  0 | size is specified for enclosure interior
721  1 | remove features outside of enclosure envelope
722  2 | scale interior with exterior wall during extrusion
723  3 | do not limit wall rounding modes to bevel and rounded (1)
724  4 | do not construct exterior walls
725  5 | do not construct exterior wall lips
726  6 | do not construct lid
727  7 | do not construct ribs
728  8 | do not construct interior walls
729  9 | do not construct posts
730  10 | do not construct holes
731  11 | do not construct shapes
732 
733  (1) When rounding mode limiting is disabled, the rounding mode
734  value, \p vrm, is no longer mapped to \em bevel or \em rounded and
735  any mode of the function polygon_round_eve_all_p() may be used to
736  round the box exterior walls and lid.
737 
738  Bits 4-9 can be used to disable the construction of select parts.
739  This may be used during design iteration to help understand
740  internal alignment and clearance. For example, turning off the
741  construction of exterior walls allows to see how a PCB fits inside
742  an assembled enclosure.
743 
744  \amu_define scope_id (example_bottom)
745  \amu_define title (Project box bottom section example)
746  \amu_define image_views (top front right diag)
747  \amu_define image_size (sxga)
748  \amu_define image_columns (4)
749 
750  \amu_include (include/amu/scope_diagrams_3d.amu)
751 *******************************************************************************/
753 (
754  wth,
755  h,
756  size,
757 
758  vr,
759  vrm,
760 
761  inset,
762 
763  lid,
764  lip,
765  rib,
766  wall,
767  post,
768  hole,
769  shape,
770 
771  align,
772  mode = 0,
773  verb = 0
774 )
775 {
776  //
777  //
778  // helper modules and functions
779  //
780  //
781 
782  // hole construction
783  module construct_holes()
784  {
785  insts = defined_or(hole, empty_lst);
786 
787  // use center of enclosure walls
788  bsize = [szint_x, szint_y, szint_z] + [wth, wth, wth];
789 
790  for (inst = insts)
791  {
792  shape = defined_e_or (inst, 0, 1);
793  h_p = defined_e_or (inst, 1, 0);
794  layout = defined_e_or (inst, 2, [0]);
795 
796  s_type = is_list(shape) ? first(shape) : shape;
797  s_argv = is_list(shape) ? tailn(shape, 1) : undef;
798 
799  /*
800 
801  auto-extrusion calculation: add wall thickness
802 
803  0 = max(w,l,h)
804  -1 = w, -2 = l, -3 = h
805  >0 = supplied value
806 
807  */
808 
809  h = (h_p < 0) ? bsize[abs(h_p+1)] + wth + eps*8
810  : (h_p == 0) ? max(bsize) + wth + eps*8
811  : h_p;
812 
813  // move layout to enclosure center
814  translate([0, 0, bsize.z/2])
815  layout_grid_rp(t=layout, b=bsize, center=true, verb=verb-1)
816  extrude_linear_uss(h, center=true)
817  select_common_2d_shape(type=s_type, argv=s_argv, center=true, verb=verb-1);
818 
819  if (verb > 1)
820  {
821  echo(shape=shape, type=s_type, argv=s_argv);
822  echo(height=h);
823  echo(layout=layout);
824  }
825  }
826 
827  if (verb > 0)
828  {
829  echo(strl(["hole: instances = ", insts]));
830  }
831  }
832 
833  // shape construction
834  module construct_shapes()
835  {
836  insts = defined_or(shape, empty_lst);
837 
838  // use center of enclosure walls
839  bsize = [szint_x, szint_y, szint_z] + [wth, wth, wth];
840 
841  for (inst = insts)
842  {
843  shape = defined_e_or (inst, 0, 1);
844  layout = defined_e_or (inst, 1, [0]);
845 
846  s_type = is_list(shape) ? first(shape) : shape;
847  s_argv = is_list(shape) ? tailn(shape, 1) : undef;
848 
849  // move layout to enclosure center
850  translate([0, 0, bsize.z/2])
851  layout_grid_rp(t=layout, b=bsize, center=true, verb=verb-1)
852  select_common_3d_shape(type=s_type, argv=s_argv, center=true, verb=verb-1);
853 
854  if (verb > 1)
855  {
856  echo(shape=shape, type=s_type, argv=s_argv);
857  echo(layout=layout);
858  }
859  }
860 
861  if (verb > 0)
862  {
863  echo(strl(["shape: instances = ", insts]));
864  }
865  }
866 
867  // exterior walls
868  module construct_exterior_walls( envelop=false )
869  {
870  // re-scale total extrusion height of 'h' equally to 'wall_h'
871  // and extrude scaled version 'hs' to maintain proper wall height
872  hs = !is_list(h) ?
873  wall_h
874  : let( sf=wall_h/h_h )
875  [ for (e=h) !is_list(e) ? e * sf : [ first(e) * sf, second(e) ] ];
876 
877  if ( mode_scale_io == true )
878  { // scale inner and outer wall together
880  difference_cs( envelop == false )
881  {
882  pg_rectangle(wall_xy + 0*[wth, wth], vr=vr, vrm=vrm_ci, center=true);
883  pg_rectangle(wall_xy - 2*[wth, wth], vr=vr, vrm=vrm_ci, center=true);
884  }
885  }
886  else
887  { // scale outer wall only
888  difference_cs( envelop == false )
889  {
891  pg_rectangle(wall_xy + 0*[wth, wth], vr=vr, vrm=vrm_ci, center=true);
892 
893  translate([0, 0, -10*eps/2])
894  extrude_linear_mss(wall_h + 10*eps)
895  pg_rectangle(wall_xy - 2*[wth, wth], vr=vr, vrm=vrm_ci, center=true);
896  }
897  }
898 
899  if (verb > 0)
900  {
901  // generate list of all scale factors in extrusion [x, y]
902  sf_x = !is_list(h) ?
903  1
904  : let( sf = [ for (e=h) !is_list(e) ? 1 : [ for (f=second(e)) !is_list(f) ? f : first(f) ] ] )
905  merge_s( sf );
906 
907  sf_y = !is_list(h) ?
908  1
909  : let( sf = [ for (e=h) !is_list(e) ? 1 : [ for (f=second(e)) !is_list(f) ? f : second(f) ] ] )
910  merge_s( sf );
911 
912  echo(strl(["wall: extrusion scale factors [x-min, x-max] = ", [min(sf_x), max(sf_x)]]));
913  echo(strl(["wall: extrusion scale factors [y-min,y- max] = ", [min(sf_y), max(sf_y)]]));
914  echo(strl(["wall: height (ignoring ribs) = ", wall_h]));
915  echo(strl(["wall: total interior height (ignoring ribs) = ", wall_h + lip_h]));
916  }
917  }
918 
919  // wall lips
920  module construct_lips( envelop=false )
921  {
922  // 'lip_h', bit '1', is set globally (ensure coherency with bits of 'lip')
923  // wall lip: mode, height, base pct, taper pct, alignment
924  lip_m = defined_e_or(lip, 0, lip);
925  lip_bw = defined_e_or(lip, 2, 35);
926  lip_tw = defined_e_or(lip, 3, 10);
927  lip_sc = defined_e_or(lip, 4, 0);
928  lip_sr = defined_e_or(lip, 5, 0);
929 
930  // lip snap configuration
931  lip_sc_s = defined_eon_or(lip_sc, 0, 0);
932  lip_sc_d = defined_e_or (lip_sc, 1, 5);
933  lip_sc_m = defined_e_or (lip_sc, 2, 1);
934  lip_sc_f = defined_e_or (lip_sc, 3, [0, 3.5/5, 4.5/5, 1, 4.5/5, 3.5/5, 0]);
935 
936  // generate 'mss' linerar scaled extrusion profile
937  function el_mss_profile
938  (
939  h, // total height
940  t, // location {0=bottom | 1=top}
941  i // inversion {+1 | -1}
942  ) =
943  let
944  (
945  m = lip_sc_m, // max amplitude
946  d = lip_sc_d, // total segment divisions
947  s = lip_sc_s, // lip snap divisions
948  f = lip_sc_f, // snap scale multiplier steps
949 
950  // wall taper scale factors (referenced to taper width)
951  fx = (2 * wth * lip_tw / 100) / wall_xy.x,
952  fy = (2 * wth * lip_tw / 100) / wall_xy.y,
953 
954  h1 = h * (d-s)/d,
955  h2 = h * s /d,
956 
957  s1 = [1, [1 - i*fx, 1 - i*fy]],
958  s2 = [for (j=f) [1 - i*fx*(1-j*m), 1 - i*fy*(1-j*m)]]
959  )
960  t ?
961  [ [h1, s1], if (s ) [h2, s2] ]
962  : [ if (s) [h2, reverse(s2)], [h1, reverse(s1)] ];
963 
964  translate( [0, 0, wall_h/2] )
965  for
966  (
967  dt =
968  let
969  (
970  // straight extrusion height for additions and removals
971  lip_ha = lip_h,
972  lip_hr = lip_h + eps*10,
973 
974  // extrusion profile; inner and outer lip at top and bottom.
975  ep_it = el_mss_profile( lip_ha, 1, +1 ),
976  ep_ot = el_mss_profile( lip_hr, 0, +1 ),
977  ep_ib = el_mss_profile( lip_ha, 0, +1 ),
978  ep_ob = el_mss_profile( lip_hr, 1, +1 ),
979 
980  // extrusion profile; 'pin' and 'clip' at top and bottom.
981  ep_pt = el_mss_profile( lip_hr, 1, -1 ),
982  ep_ct = el_mss_profile( lip_ha, 0, -1 ),
983  ep_pb = el_mss_profile( lip_hr, 0, -1 ),
984  ep_cb = el_mss_profile( lip_ha, 1, -1 ),
985 
986  // lip wall sizes; for outer and inner combinations
987  ws_oo = wall_xy,
988  ws_oi = wall_xy - 2*[wth, wth] * lip_bw/100,
989  ws_io = wall_xy - 2*[wth, wth] * (1-lip_bw/100),
990  ws_ii = wall_xy - 2*[wth, wth]
991  )
992  [ // m, io, tb, ae, re, as, rs
993  [0, 1, +1, ep_it, lip_hr, ws_io, ws_ii], // inner lip at top
994  [1, 0, +1, lip_ha, ep_ot, ws_oo, ws_oi], // outer lip at top
995  [2, 1, -1, ep_ib, lip_hr, ws_io, ws_ii], // inner lip at bottom
996  [3, 0, -1, lip_ha, ep_ob, ws_oo, ws_oi], // outer lip at bottom
997 
998  [4, 1, +1, ep_it, ep_pt, ws_oi, ws_io], // center pin at top
999  [5, 0, +1, ep_ct, lip_hr, ws_io, ws_ii], // inner lip at top (clip section)
1000  [6, 1, -1, ep_ib, ep_pb, ws_oi, ws_io], // center pin at bottom
1001  [7, 0, -1, ep_cb, lip_hr, ws_io, ws_ii] // inner lip at bottom (clip section)
1002  ]
1003  )
1004  {
1005  m = dt[0]; // m : configuration mode
1006 
1007  if ( binary_bit_is(lip_m, m, 1) )
1008  {
1009  io = dt[1]; // io : inside / outside
1010  tb = dt[2]; // tb : top / bottom
1011  ap = dt[3]; // ap : add extrusion profile
1012  rp = dt[4]; // rp : remove extrusion profile
1013  as = dt[5]; // as : add size
1014  rs = dt[6]; // rs : remove size
1015 
1016  //
1017  // section removal: format = [ [m, ee, ep], ... ]
1018  //
1019 
1020  sr = lip_sr[ last(search(m, lip_sr, 0, 0)) ];
1021 
1022  ee = defined_ei_or(sr, 1, [0, 0], 2); // enable {0, 1}
1023  eo = defined_ei_or(sr, 2, [0, 0], 2); // offset {-1, 0, +1}
1024 
1025  es = [wall_xy.x * ee.x, wall_xy.y * ee.y] / 2; // size
1026  et = [es.x * eo.x, es.y * eo.y] / 2; // translation
1027 
1028  translate([0, 0, (wall_h + lip_h - eps)/2 * tb])
1029  difference_cs( envelop == false )
1030  {
1031  extrude_linear_mss(ap, center=true)
1032  pg_rectangle(as, vr=vr, vrm=vrm_ci, center=true);
1033 
1034  extrude_linear_mss(rp, center=true)
1035  translate(et)
1036  pg_rectangle(rs + es, vr=vr, vrm=vrm_ci, center=true);
1037  }
1038  }
1039  }
1040 
1041  if (verb > 0)
1042  {
1043  echo(strl(["lip: mode = ", lip_m]));
1044  echo(strl(["lip: height = ", lip_h]));
1045  echo(strl(["lip: base width percentage = ", lip_bw]));
1046  echo(strl(["lip: top reduction percentage = ", lip_tw]));
1047  echo(strl(["lip: snap = ", lip_sc]));
1048  echo(strl(["lip: section removal = ", lip_sr]));
1049  }
1050  }
1051 
1052  // lid extrusion
1053  module construct_lid()
1054  {
1055  mirror([0, 0, 1])
1056  translate([0, 0, -eps])
1057  extrude_linear_mss(lid)
1058  pg_rectangle([encl_x, encl_y] + 0*[wth, wth], vr=vr, vrm=vrm_ci, center=true);
1059 
1060  if (verb > 0)
1061  {
1062  echo(strl(["lid: extrusion = ", lid]));
1063  echo(strl(["lid: height = ", lid_h]));
1064  }
1065  }
1066 
1067  // ribs
1068  module construct_ribs()
1069  {
1070  // mode
1071  rib_m = defined_e_or(rib, 0, rib);
1072 
1073  // B5-6: wall limits (mode dependent)
1074  max_x = first( wall_xy) - 2*(wth - eps);
1075  max_y = second(wall_xy) - 2*(wth - eps);
1076 
1077  // 'max_h' may include 0 to 2 'lip_h' (ie: one at top and bottom)
1078  max_h = wall_h + min(2, binary_iw2i(rib_m, 5, 2)) * lip_h;
1079 
1080  // B7: configurable global offset (to align with lower lip)
1081  rib_lo = binary_bit_is(rib_m, 7, 1) ? [0, 0, -lip_h] : zero3d;
1082 
1083  // rib width and extrusion configuration (semicircle)
1084  rib_edx = [for (x=[0:1/get_fn(1)/2:1]) [2*sqrt(1-pow(x,2)), 1]];
1085 
1086  rib_edy = [for (e=rib_edx) reverse(e)];
1087 
1088  rib_sd = defined_e_or(rib, 1, [ wth, [[wth, rib_edx]], [[wth, rib_edy]] ] );
1089 
1090  rib_w = defined_e_or(rib_sd, 0, rib_sd);
1091  rib_hx = defined_e_or(rib_sd, 1, [[rib_w, rib_edx]]);
1092  rib_hy = defined_e_or(rib_sd, 2, [[rib_w, rib_edy]]);
1093 
1094  // rib coverage percentages
1095  rcp_d = defined_e_or(rib, 2, 10);
1096 
1097  rcp_x = defined_e_or(rcp_d, 0, rcp_d);
1098  rcp_y = defined_e_or(rcp_d, 1, rcp_x);
1099  rcp_z = defined_e_or(rcp_d, 2, rcp_y);
1100 
1101  // calculated rib counts based on coverage percentage
1102  crc_x = max([0, floor(max_x/rib_w * rcp_x / 100)]);
1103  crc_y = max([0, floor(max_y/rib_w * rcp_y / 100)]);
1104  crc_z = max([0, floor(max_h/rib_w * rcp_z / 100)]);
1105 
1106  // rib count assignment
1107  cnt_d = defined_e_or(rib, 3, [crc_x, crc_y, crc_z]);
1108 
1109  cnt_x = defined_e_or(cnt_d, 0, cnt_d);
1110  cnt_y = defined_e_or(cnt_d, 1, cnt_x);
1111  cnt_z = defined_e_or(cnt_d, 2, cnt_y);
1112 
1113  //
1114  //
1115  // construct ribs
1116  //
1117  //
1118 
1119  translate(rib_lo)
1120  for
1121  (
1122  i =
1123  [ // [ B0-4: mode-bit, size:[x, y], count:[x, y], xform:[r, t] ]
1124  [ 0, [max_x, max_y], [cnt_x, cnt_y], [[0, 0, 0], [0, 0, 0]] ],
1125  [ 1, [max_y, max_h], [cnt_y, cnt_z], [[90, 0, -90], [max_x/2, 0, max_h/2]] ],
1126  [ 2, [max_x, max_h], [cnt_x, cnt_z], [[90, 0, 0], [0, max_y/2, max_h/2]] ],
1127  [ 3, [max_y, max_h], [cnt_y, cnt_z], [[90, 0, 90], [-max_x/2, 0, max_h/2]] ],
1128  [ 4, [max_x, max_h], [cnt_x, cnt_z], [[-90, 0, 0], [0, -max_y/2, max_h/2]] ],
1129  ]
1130  )
1131  {
1132  if ( binary_bit_is(rib_m, first(i), 0) == true )
1133  {
1134  si = second(i); // size [x, y]
1135  ni = third(i); // number [x, y]
1136  xi = i[3]; // transform [r, t]
1137 
1138  sx = first(si);
1139  sy = second(si);
1140 
1141  nx = first(ni);
1142  ny = second(ni);
1143 
1144  translate(second(xi))
1145  rotate(first(xi))
1146  union()
1147  {
1148  // ribs-x
1149  if (nx > 0)
1150  for (i = [0: nx-1])
1151  translate([(sx/nx*(1-nx)/2) + (sx/nx * i), 0, 0])
1152  extrude_linear_mss(rib_hx)
1153  square([rib_w, sy], center=true);
1154 
1155  // ribs-y
1156  if (ny > 0)
1157  for (i = [0: ny-1])
1158  translate([0, (sy/ny*(1-ny)/2) + (sy/ny * i), 0])
1159  extrude_linear_mss(rib_hy)
1160  square([sx, rib_w], center=true);
1161  }
1162  }
1163  }
1164 
1165  if (verb > 0)
1166  {
1167  // calculate rib heights
1168  rib_hxd = sum( [for (e=rib_hx) is_list(e) ? first(e) : e] );
1169  rib_hyd = sum( [for (e=rib_hy) is_list(e) ? first(e) : e] );
1170 
1171  // only when there are ribs on the lid (mode-bit '0' is for the lid)
1172  // can not be greater than max xy height less negative lower offset
1173  rib_rwh = wall_h + lip_h
1174  - max( 0, max([rib_hxd, rib_hyd]) + third(rib_lo) )
1175  * ( (binary_bit_is(rib_m, 0, 0) == true) ? 1 : 0 );
1176 
1177  echo(strl(["rib: mode = ", rib_m]));
1178  echo(strl(["rib: width = ", rib_w]));
1179  echo(strl(["rib: extrusion x = ", rib_hx]));
1180  echo(strl(["rib: height x = ", rib_hxd]));
1181  echo(strl(["rib: extrusion y = ", rib_hy]));
1182  echo(strl(["rib: height y = ", rib_hyd]));
1183  echo(strl(["rib: coverage [x, y, z] = ", [rcp_x, rcp_y, rcp_z]]));
1184  echo(strl(["rib: count [x, y, z] = ", [cnt_x, cnt_y, cnt_z]]));
1185 
1186  echo(strl(["rib: global offset = ", rib_lo]));
1187  echo(strl(["rib: remaining wall height = ", rib_rwh]));
1188  }
1189  }
1190 
1191  // interior walls
1192  module construct_interior_walls()
1193  {
1194  // wall
1195  config = defined_e_or(wall, 0, wall);
1196  inst_l = defined_e_or(wall, 1, empty_lst);
1197 
1198  // configuration
1199  wall_m = defined_e_or(config, 0, config);
1200  defs_l = defined_e_or(config, 1, empty_lst);
1201 
1202  // set a few values early for dependent defaults
1203  def_dw = defined_e_or(defs_l, 0, wth);
1204 
1205  // wall limits (mx, my, mz)
1206  max_x = first( wall_xy) - 2*(wth - eps);
1207  max_y = second(wall_xy) - 2*(wth - eps);
1208 
1209  // B8-9: 'max_h' may include 0 to 2 'lip_h' (ie: one at top and bottom)
1210  max_h = wall_h + min(2, binary_iw2i(wall_m, 8, 2)) * lip_h;
1211 
1212  // B10: global lower-lip offset
1213  wall_lo = binary_bit_is(wall_m, 10, 1) ? [0, 0, -lip_h] : zero3d;
1214 
1215  // B0-1: default wall end rounding
1216  cfg_vrm = let( i = binary_iw2i(wall_m, 0, 2) )
1217  (i == 1) ? [ 9, 10, 9, 10] // bevel
1218  : (i == 2) ? [ 3, 4, 3, 4] // fillet
1219  : (i == 3) ? [ 7, 8, 7, 8] // round-out
1220  : 0; // none
1221 
1222  // configurations for top wall extrusion rounding factors for horizontal
1223  // wall using approximated semicircles with $fn segments for rounding
1224  // (must reverse for bottom and reorder x/y for vertical wall).
1225  cfg_wt_rm =
1226  [
1227  0, // none
1228  [[1,1],[1,1/2]], // bevel-in
1229  [for (x=[0:1/get_fn(1)/2:1]) [1,sqrt(1+eps-pow(x,2))]], // round-in
1230  [for (x=[1:-1/get_fn(1)/2:1/2]) [1,1-sqrt(1-pow(x,2))]], // fillet-in
1231  [[1,1],[1,1+1/2]], // bevel-out
1232  [for (x=[0:1/get_fn(1)/2:1]) [1,sqrt(1+eps+pow(x,2))]], // fillet-out
1233  [for (x=[1:-1/get_fn(1)/2:1/2]) [1,1+sqrt(1-pow(x,2))]], // round-out
1234  ];
1235 
1236  // B2-4: wall top rounding
1237  wt_rm_i = binary_iw2i(wall_m, 2, 3);
1238  s_wt_rm = select_ci( cfg_wt_rm, wt_rm_i, true );
1239  cfg_rt = [def_dw/2, s_wt_rm];
1240 
1241  // B5-7: wall base rounding
1242  wb_rm_i = binary_iw2i(wall_m, 5, 3);
1243  s_wb_rm = select_ci( cfg_wt_rm, wb_rm_i, true );
1244  cfg_rb = [def_dw/2, reverse(s_wb_rm)];
1245 
1246  // default height extrusion configuration
1247  cfg_he =
1248  (wt_rm_i > 0 && wb_rm_i == 0) ? [max_h - def_dw/2, cfg_rt] // top only
1249  : (wt_rm_i == 0 && wb_rm_i > 0) ? [cfg_rb, max_h - def_dw/2] // base only
1250  : (wt_rm_i > 0 && wb_rm_i > 0) ? [cfg_rb, max_h - def_dw, cfg_rt] // both
1251  : max_h; // neither
1252 
1253  // configured configuration defaults
1254  def_he = defined_e_or(defs_l, 1, cfg_he);
1255  def_vr = defined_e_or(defs_l, 2, def_dw);
1256  def_vrm = defined_e_or(defs_l, 3, cfg_vrm);
1257 
1258  //
1259  //
1260  // construct walls
1261  //
1262  //
1263 
1264  // process wall instance list
1265  translate(wall_lo)
1266  for (inst=inst_l)
1267  {
1268  inst_t = defined_e_or(inst, 0, inst); // type
1269 
1270  inst_m = defined_e_or(inst, 1, zero3d); // *move
1271  inst_f = defined_e_or(inst, 2, [1, 1, 1]); // scale
1272  inst_r = defined_e_or(inst, 3, zero3d); // rotate
1273 
1274  inst_s = defined_e_or(inst, 4, undef); // *size
1275  inst_he = defined_e_or(inst, 5, undef); // *he
1276  inst_vr = defined_e_or(inst, 6, undef); // *vr
1277  inst_vrm = defined_e_or(inst, 7, undef); // *vrm
1278 
1279  //
1280  // default value updates based on wall type: (0=horizontal, 1=vertical)
1281  //
1282 
1283  // move vector
1284  type_m = is_list(inst_m) ?
1285  inst_m
1286  : (inst_t == 0) ?
1287  [0, inst_m]
1288  : [inst_m, 0];
1289 
1290  // max length horizontal or vertical base shape
1291  tdef_s = (inst_t == 0) ?
1292  [max_x, def_dw]
1293  : [def_dw, max_y];
1294 
1295  // wall base extrusion (adjusted for vertical walls)
1296  tdef_he = !is_list(def_he) ?
1297  def_he // use specified scalar value
1298  : (inst_t == 0) ?
1299  def_he // horizontal: use list as defined
1300  : [ for (he=def_he) // vertical: reverse [x, y] scale factors
1301  !is_list(he) ?
1302  he
1303  : [ first(he), [ for (s=second(he)) reverse(s) ] ]
1304  ];
1305 
1306  // wall-end rounding radii (adjusted for vertical walls)
1307  tdef_vr = !is_list(def_vr) ?
1308  def_vr
1309  : (inst_t == 0) ?
1310  def_vr
1311  : shift_cd(def_vr, n=-1, r=false, d=false);
1312 
1313  // wall-end rounding modes (adjusted for vertical walls)
1314  tdef_vrm = !is_list(def_vrm) ?
1315  def_vrm
1316  : (inst_t == 0) ?
1317  def_vrm
1318  : shift_cd(def_vrm, n=+1, r=false, d=false);
1319 
1320  //
1321  // assign defaults when not specified with wall instance
1322  //
1324  s = defined_or(inst_s, tdef_s);
1325  he = defined_or(inst_he, tdef_he);
1326  vr = defined_or(inst_vr, tdef_vr);
1327  vrm = defined_or(inst_vrm, tdef_vrm);
1328 
1329  //
1330  // construct wall instance
1331  //
1332 
1333  translate(type_m)
1334  scale(inst_f)
1335  rotate(inst_r)
1336  extrude_linear_mss(he)
1337  pg_rectangle(s, vr=vr, vrm=vrm, center=true);
1338 
1339  if (verb > 1)
1340  echo(strl(["wall-inst: [type, move, scale, rotation, size, he, vr, vrm] = ",
1341  [inst_t, inst_m, inst_f, inst_r, s, he, vr, vrm]]));
1342  }
1343 
1344  if (verb > 0)
1345  {
1346  // handle special case: a single horizontal or vertical wall
1347  wall_cnt = is_list(inst_l) ? len(inst_l) : 1;
1348 
1349  echo(strl(["wall: configuration = ", config]));
1350  echo(strl(["wall: mode = ", wall_m]));
1351 
1352  echo(strl(["wall: max [x, y, h] = ", [max_x, max_y, max_h]]));
1353 
1354  echo(strl(["wall: default width = ", def_dw]));
1355  echo(strl(["wall: default extrusion = ", def_he]));
1356  echo(strl(["wall: default edge rounding = ", def_vr]));
1357  echo(strl(["wall: default edge rounding mode = ", def_vrm]));
1358 
1359  echo(strl(["wall: count = ", wall_cnt]));
1360 
1361  echo(strl(["wall: global offset = ", wall_lo]));
1362  }
1363  }
1364 
1365  // posts and post holes
1366  module construct_posts( add=false, remove=false )
1367  {
1368  // post
1369  config = defined_e_or(post, 0, post);
1370  inst_l = defined_e_or(post, 1, empty_lst);
1371 
1372  // configuration
1373  post_m = defined_e_or(config, 0, config);
1374  defs_l = defined_e_or(config, 1, empty_lst);
1375 
1376  // B4: post base rounded same as post top
1377  cfg_rbst = binary_bit_is(post_m, 4, 1);
1378 
1379  // rounding constant configurations
1380  cfg_p_vr_sf = (cfg_rbst == true) ? [0, 1/2, 1/2, 0] : [0, 1/2, 3/2, 0];
1381  cfg_p_vrm_filet = (cfg_rbst == true) ? [0, 1, 1, 0] : [0, 1, 4, 0];
1382  cfg_p_vrm_bevel = (cfg_rbst == true) ? [0, 5, 5, 0] : [0, 5, 10, 0];
1383 
1384  cfg_f0_vr_sf = [2, 0, 1];
1385  cfg_f0_vrm_filet = [4, 0, 3];
1386  cfg_f0_vrm_bevel = [10, 0, 9];
1387 
1388  cfg_f1_vr_sf = [0, 1, 1, 0];
1389  cfg_f1_vrm_filet = [0, 4, 3, 0];
1390  cfg_f1_vrm_bevel = [0, 10, 9, 0];
1391 
1392  // mode dependent configuration
1393  // B0-1: post rounding mode
1394  cfg_p_vrm = let( i = binary_iw2i(post_m, 0, 2) )
1395  (i == 1) ? cfg_p_vrm_bevel
1396  : (i == 2) ? cfg_p_vrm_filet
1397  : 0;
1398 
1399  // B2-3: fin rounding mode
1400  cfg_f0_vrm = let( i = binary_iw2i(post_m, 2, 2) )
1401  (i == 1) ? cfg_f0_vrm_bevel
1402  : (i == 2) ? cfg_f0_vrm_filet
1403  : 0;
1404 
1405  cfg_f1_vrm = let( i = binary_iw2i(post_m, 2, 2) )
1406  (i == 1) ? cfg_f1_vrm_bevel
1407  : (i == 2) ? cfg_f1_vrm_filet
1408  : 0;
1409 
1410  // B5: auxiliary screw hole height (through lid)
1411  cfg_h1_h = binary_bit_is(post_m, 5, 1) ? lid_h : 0;
1412 
1413  // B6: re-calculate defaults with each instance.
1414  cfg_hp_idr = binary_bit_is(post_m, 6, 1);
1415 
1416  // B8: total lip_h extension height (0: one, 1: both)
1417  lip_h_t = ((binary_bit_is(post_m, 8, 1) ? 1 : 0) + 1) * lip_h;
1418 
1419  // B7: post1 and post2 heights (only one extends by lip height)
1420  cfg_p1_h = (binary_bit_is(post_m, 7, 1) ? lip_h_t : 0) + wall_h;
1421  cfg_p2_h = (binary_bit_is(post_m, 7, 0) ? lip_h_t : 0) + wall_h;
1422 
1423  // B9: global lower-lip offset
1424  post_lo = binary_bit_is(post_m, 9, 1) ? [0, 0, -lip_h] : zero3d;
1425 
1426  max_x = first( wall_xy) - 2*(wth - eps);
1427  max_y = second(wall_xy) - 2*(wth - eps);
1428  max_h = wall_h + lip_h_t;
1429 
1430  //
1431  // configured configuration defaults
1432  //
1433 
1434  def_h0 = defined_e_or(defs_l, 0, empty_lst);
1435  def_h1 = defined_e_or(defs_l, 1, empty_lst);
1436  def_p1 = defined_e_or(defs_l, 2, empty_lst);
1437  def_h2 = defined_e_or(defs_l, 3, empty_lst);
1438  def_p2 = defined_e_or(defs_l, 4, empty_lst);
1439  def_f0 = defined_e_or(defs_l, 5, empty_lst);
1440  def_f1 = defined_e_or(defs_l, 6, empty_lst);
1441  def_dc = defined_e_or(defs_l, 7, empty_lst);
1442 
1443  // hole0: normal & recessed screw common hole
1444  def_h0_d = defined_e_or(def_h0, 0, 3.25);
1445  def_h0_h = defined_e_or(def_h0, 1, max_h);
1446  def_h0_ho = defined_e_or(def_h0, 2, 0);
1447  def_h0_da = defined_e_or(def_h0, 3, 0);
1448  def_h0_ha = defined_e_or(def_h0, 4, 0);
1449  def_h0_vr = defined_e_or(def_h0, 5, 0);
1450  def_h0_vrm = defined_e_or(def_h0, 6, 0);
1451 
1452  def_h0_rh = defined_e_or(def_h0, 7, 0);
1453  def_h0_ro = defined_e_or(def_h0, 8, 0);
1454  def_h0_rta = defined_e_or(def_h0, 9, [0, 0]);
1455  def_h0_rba = defined_e_or(def_h0,10, [0, 0]);
1456 
1457  //
1458  // default diameter calculations based on hole0
1459  //
1460  def_h1_d_c = def_h0_d
1461  + defined_e_or(def_dc, 0, 0.0) * wth
1462  + defined_e_or(def_dc, 1, 0);
1463 
1464  def_p1_d_c = def_h0_d
1465  + defined_e_or(def_dc, 2, 3.0) * wth
1466  + defined_e_or(def_dc, 3, wth/2);
1467 
1468  def_h2_d_c = def_h0_d
1469  + defined_e_or(def_dc, 4, 2.0) * wth
1470  + defined_e_or(def_dc, 5, wth/2);
1471 
1472  def_p2_d_c = def_h0_d
1473  + defined_e_or(def_dc, 6, 4.0) * wth
1474  + defined_e_or(def_dc, 7, wth/2);
1475 
1476  // hole1: normal thru lid hole
1477  def_h1_d = defined_e_or(def_h1, 0, def_h1_d_c);
1478  def_h1_h = defined_e_or(def_h1, 1, cfg_h1_h);
1479  def_h1_ho = defined_e_or(def_h1, 2, -cfg_h1_h);
1480  def_h1_da = defined_e_or(def_h1, 3, 0);
1481  def_h1_ha = defined_e_or(def_h1, 4, 0);
1482  def_h1_vr = defined_e_or(def_h1, 5, 0);
1483  def_h1_vrm = defined_e_or(def_h1, 6, 0);
1484 
1485  def_h1_rh = defined_e_or(def_h1, 7, 0);
1486  def_h1_ro = defined_e_or(def_h1, 8, 0);
1487  def_h1_rta = defined_e_or(def_h1, 9, [0, 0]);
1488  def_h1_rba = defined_e_or(def_h1,10, [0, 0]);
1489 
1490  // post1: normal mount post
1491  def_p1_d = defined_e_or(def_p1, 0, def_p1_d_c);
1492  def_p1_h = defined_e_or(def_p1, 1, cfg_p1_h);
1493  def_p1_ho = defined_e_or(def_p1, 2, 0);
1494  def_p1_da = defined_e_or(def_p1, 3, 0);
1495  def_p1_ha = defined_e_or(def_p1, 4, 0);
1496  def_p1_vr = defined_e_or(def_p1, 5, cfg_p_vr_sf * wth);
1497  def_p1_vrm = defined_e_or(def_p1, 6, cfg_p_vrm);
1498 
1499  def_p1_rh = defined_e_or(def_p1, 7, 0);
1500  def_p1_ro = defined_e_or(def_p1, 8, 0);
1501  def_p1_rta = defined_e_or(def_p1, 9, [0, 0]);
1502  def_p1_rba = defined_e_or(def_p1,10, [0, 0]);
1503 
1504  // hole2: recessed access hole thru lid
1505  def_h2_d = defined_e_or(def_h2, 0, def_h2_d_c);
1506  def_h2_h = defined_e_or(def_h2, 1, cfg_p2_h);
1507  def_h2_ho = defined_e_or(def_h2, 2, -lid_h);
1508  def_h2_da = defined_e_or(def_h2, 3, 0);
1509  def_h2_ha = defined_e_or(def_h2, 4, 0);
1510  def_h2_vr = defined_e_or(def_h2, 5, cfg_p_vr_sf * wth/2);
1511  def_h2_vrm = defined_e_or(def_h2, 6, cfg_p_vrm);
1512 
1513  def_h2_rh = defined_e_or(def_h2, 7, 0);
1514  def_h2_ro = defined_e_or(def_h2, 8, 0);
1515  def_h2_rta = defined_e_or(def_h2, 9, [0, 0]);
1516  def_h2_rba = defined_e_or(def_h2,10, [0, 0]);
1517 
1518  // post2: recessed access post
1519  def_p2_d = defined_e_or(def_p2, 0, def_p2_d_c);
1520  def_p2_h = defined_e_or(def_p2, 1, cfg_p2_h);
1521  def_p2_ho = defined_e_or(def_p2, 2, 0);
1522  def_p2_da = defined_e_or(def_p2, 3, 0);
1523  def_p2_ha = defined_e_or(def_p2, 4, 0);
1524  def_p2_vr = defined_e_or(def_p2, 5, cfg_p_vr_sf * wth);
1525  def_p2_vrm = defined_e_or(def_p2, 6, cfg_p_vrm);
1526 
1527  def_p2_rh = defined_e_or(def_p2, 7, 0);
1528  def_p2_ro = defined_e_or(def_p2, 8, 0);
1529  def_p2_rta = defined_e_or(def_p2, 9, [0, 0]);
1530  def_p2_rba = defined_e_or(def_p2,10, [0, 0]);
1531 
1532  // fins0: triangular fins
1533  def_f0_c = defined_e_or(def_f0, 0, 4);
1534  def_f0_da = defined_e_or(def_f0, 1, 360);
1535  def_f0_w = defined_e_or(def_f0, 2, wth);
1536  def_f0_d_sf = defined_e_or(def_f0, 3, 1/5);
1537  def_f0_h_sf = defined_e_or(def_f0, 4, 5/8);
1538  def_f0_vr = defined_e_or(def_f0, 5, cfg_f0_vr_sf * wth);
1539  def_f0_vrm = defined_e_or(def_f0, 6, cfg_f0_vrm);
1540 
1541  // fins1: rectangular fins
1542  def_f1_c = defined_e_or(def_f1, 0, 4);
1543  def_f1_da = defined_e_or(def_f1, 1, 360);
1544  def_f1_w = defined_e_or(def_f1, 2, wth);
1545  def_f1_d_sf = defined_e_or(def_f1, 3, 1/2);
1546  def_f1_h_sf = defined_e_or(def_f1, 4, 1);
1547  def_f1_vr = defined_e_or(def_f1, 5, cfg_f1_vr_sf * wth);
1548  def_f1_vrm = defined_e_or(def_f1, 6, cfg_f1_vrm);
1549 
1550  //
1551  //
1552  // construct posts
1553  //
1554  //
1555 
1556  // remove cylinder section
1557  module remove_cylinder ( d, r, eps_h=0, eps_d=0 )
1558  {
1559  h = r[0];
1560  ho = r[1];
1561  ta = r[2];
1562  ba = r[3];
1563 
1564  // top cut tilt and rotation
1565  tt = defined_eon_or(ta, 0, 0);
1566  tr = defined_e_or (ta, 1, 0);
1567 
1568  // bottom cut tilt and rotation
1569  bt = defined_eon_or(ba, 0, 0);
1570  br = defined_e_or (ba, 1, 0);
1571 
1572  if (verb > 2)
1573  echo(strl(["post-inst-cylinder-remove: [d, r, eps_h, eps_d] = ", [d, r, eps_h, eps_d]]));
1574 
1575  translate([0, 0, ho - eps_h/2])
1576  difference()
1577  {
1578  // cut blade [x, y, z]
1579  x = h; y = h; z = d;
1580 
1581  // cylinder removal section
1582  cylinder(d=d + eps_d, h=h + eps_h);
1583 
1584  // cut at top of cylinder
1585  rotate(tr)
1586  translate([0, -d/2, 0])
1587  translate([0, 0, h])
1588  rotate([tt, 0, 0])
1589  translate([-x/2, 0, 0])
1590  cube([x, y, z], center=false);
1591 
1592  // cut at bottom of cylinder
1593  rotate(br)
1594  translate([0, -d/2, 0])
1595  rotate([bt, 0, 0])
1596  translate([-x/2, 0, -z])
1597  cube([x, y, z], center=false);
1598  }
1599  }
1600 
1601  // construct fins around a cylinder
1602  module construct_fins(d, h, t, f)
1603  {
1604  c = defined_e_or(f, 0, 0);
1605 
1606  // move distance for fin to always contact polygon cylinder
1607  function fin_embed(r, w) =
1608  let
1609  (
1610  n = get_fn(r),
1611  d = polygon_regular_perimeter(n, r) / n
1612  )
1613  r - sqrt( pow(r,2) - pow(w/2, 2) - pow(d/2, 2) );
1614 
1615  if ( c > 0 )
1616  {
1617  da = f[1];
1618  w = f[2];
1619  df = f[3];
1620  hf = f[4];
1621  vr = f[5];
1622  vrm = f[6];
1623 
1624  b = d * df;
1625  l = h * hf;
1626 
1627  if (verb > 2)
1628  echo(strl(["post-inst-cylinder-fins: [d, h, t, f] = ", [d, h, t, f]]));
1629 
1630  f_in = fin_embed(d/2, w);
1631 
1632  // triangular fins
1633  if ( t == 0 )
1634  {
1635  for (i = [0:c-1])
1636  {
1637  rotate([90, 0, da/c * i + 180])
1638  translate([-d/2 - b + f_in, 0, 0])
1639  extrude_linear_mss(w, center=true)
1640  pg_triangle_sas([l, 90, b], vr=vr, vrm=vrm);
1641  }
1642  }
1643 
1644  // rectangular fins
1645  if ( t == 1 )
1646  {
1647  for (i = [0:c-1])
1648  {
1649  rotate([0, 0, da/c * i])
1650  translate([b/2 + d/2 - f_in, 0, 0])
1652  pg_rectangle( [b, w], vr=vr, vrm=vrm, center=true);
1653  }
1654  }
1655 
1656  }
1657  }
1658 
1659  // construct a cylinder with optional fins
1660  module construct_cylinder ( en, c, r, ft, f, eps_h=0 )
1661  {
1662  if (en == true)
1663  {
1664  d = c[0];
1665  h = c[1];
1666  ho = c[2];
1667  da = c[3];
1668  ha = c[4];
1669  vr = c[5];
1670  vrm = c[6];
1671 
1672  if (verb > 2)
1673  echo(strl(["post-inst-cylinder: [c, eps_h] = ", [c, eps_h]]));
1674 
1675  difference()
1676  {
1677  // construct post and fins
1678  translate([0, 0, ho - eps_h/2])
1679  {
1680  // late diameter and height adjustments
1681  d_adj = d + da;
1682  h_adj = h + ha;
1683 
1684  rotate_extrude()
1685  pg_rectangle([d_adj/2, h_adj + eps_h], vr=vr, vrm=vrm);
1686 
1687  construct_fins(d_adj, h_adj, ft, f);
1688  }
1689 
1690  // removal post section
1691  eps_h_a = ( add == true ) ? +eps*8 : 0;
1692  eps_h_r = ( remove == true ) ? -eps*8 : 0;
1693 
1694  remove_cylinder(d, r, eps_h=eps_h_a + eps_h_r, eps_d=d);
1695  }
1696  }
1697  }
1698 
1699  // pre-processing message
1700  if (verb > 0)
1701  {
1702  post_cfm = ( add == true && remove == false ) ? "add"
1703  : ( add == false && remove == true ) ? "remove"
1704  : ( add == true && remove == true ) ? "add & remove"
1705  : "do nothing";
1706 
1707  echo(strl(["post: construction phase = ", post_cfm]));
1708  }
1709 
1710  //
1711  // process 'post' instance list
1712  //
1713  translate(post_lo)
1714  for (inst=inst_l)
1715  {
1716  inst_t = defined_e_or(inst, 0, inst); // type
1717 
1718  inst_a = defined_e_or(inst, 1, zero3d); // align [x, y, z]
1719  inst_m = defined_e_or(inst, 2, zero3d); // move [x, y, z]
1720  inst_r = defined_e_or(inst, 3, zero3d); // rotate [x, y, z]
1721 
1722  inst_h0 = defined_e_or(inst, 4, undef); // hole0
1723  inst_h1 = defined_e_or(inst, 5, undef); // hole1
1724  inst_p = defined_e_or(inst, 6, undef); // post
1725 
1726  inst_f = defined_e_or(inst, 7, undef); // fins
1727 
1728  // alignment
1729  inst_ax = defined_e_or(inst_a, 0, 0);
1730  inst_ay = defined_e_or(inst_a, 1, 0);
1731  inst_az = defined_e_or(inst_a, 2, 0);
1732 
1733  inst_zx = limit(inst_ax, -1, 1) * (max_x + wth)/2;
1734  inst_zy = limit(inst_ay, -1, 1) * (max_y + wth)/2;
1735  inst_zz = limit(inst_az, -1, 0) * lid_h;
1736 
1737  //
1738  // default value updates based on types
1739  //
1740 
1741  // B0: post-type
1742  inst_pt = binary_iw2i(inst_t, 0, 1);
1743 
1744  // hole0:
1745 
1746  // hole1:
1747  tdef_h1_d_c = (inst_pt == 0) ? def_h1_d_c : def_h2_d_c;
1748  tdef_h1_d = (inst_pt == 0) ? def_h1_d : def_h2_d;
1749  tdef_h1_h = (inst_pt == 0) ? def_h1_h : def_h2_h;
1750  tdef_h1_ho = (inst_pt == 0) ? def_h1_ho : def_h2_ho;
1751  tdef_h1_da = (inst_pt == 0) ? def_h1_da : def_h2_da;
1752  tdef_h1_ha = (inst_pt == 0) ? def_h1_ha : def_h2_ha;
1753  tdef_h1_vr = (inst_pt == 0) ? def_h1_vr : def_h2_vr;
1754  tdef_h1_vrm = (inst_pt == 0) ? def_h1_vrm : def_h2_vrm;
1755 
1756  tdef_h1_rh = (inst_pt == 0) ? def_h1_rh : def_h2_rh;
1757  tdef_h1_ro = (inst_pt == 0) ? def_h1_ro : def_h2_ro;
1758  tdef_h1_rta = (inst_pt == 0) ? def_h1_rta : def_h2_rta;
1759  tdef_h1_rba = (inst_pt == 0) ? def_h1_rba : def_h2_rba;
1760 
1761  // post:
1762  tdef_p_d_c = (inst_pt == 0) ? def_p1_d_c : def_p2_d_c;
1763  tdef_p_d = (inst_pt == 0) ? def_p1_d : def_p2_d;
1764  tdef_p_h = (inst_pt == 0) ? def_p1_h : def_p2_h;
1765  tdef_p_ho = (inst_pt == 0) ? def_p1_ho : def_p2_ho;
1766  tdef_p_da = (inst_pt == 0) ? def_p1_da : def_p2_da;
1767  tdef_p_ha = (inst_pt == 0) ? def_p1_ha : def_p2_ha;
1768  tdef_p_vr = (inst_pt == 0) ? def_p1_vr : def_p2_vr;
1769  tdef_p_vrm = (inst_pt == 0) ? def_p1_vrm : def_p2_vrm;
1770 
1771  tdef_p_rh = (inst_pt == 0) ? def_p1_rh : def_p2_rh;
1772  tdef_p_ro = (inst_pt == 0) ? def_p1_ro : def_p2_ro;
1773  tdef_p_rta = (inst_pt == 0) ? def_p1_rta : def_p2_rta;
1774  tdef_p_rba = (inst_pt == 0) ? def_p1_rba : def_p2_rba;
1775 
1776  // B1: fins-type
1777  inst_ft = binary_iw2i(inst_t, 1, 1);
1778 
1779  // fins:
1780  tdef_f_c = (inst_ft == 0) ? def_f0_c : def_f1_c;
1781  tdef_f_da = (inst_ft == 0) ? def_f0_da : def_f1_da;
1782  tdef_f_w = (inst_ft == 0) ? def_f0_w : def_f1_w;
1783  tdef_f_d_sf = (inst_ft == 0) ? def_f0_d_sf : def_f1_d_sf;
1784  tdef_f_h_sf = (inst_ft == 0) ? def_f0_h_sf : def_f1_h_sf;
1785  tdef_f_vr = (inst_ft == 0) ? def_f0_vr : def_f1_vr;
1786  tdef_f_vrm = (inst_ft == 0) ? def_f0_vrm : def_f1_vrm;
1787 
1788  //
1789  // assign defaults when not specified with post instance
1790  //
1791 
1792  // hole0: common screw hole (for all post types)
1793  h0_en = (remove == true);
1794 
1795  h0_d = defined_e_or(inst_h0, 0, def_h0_d);
1796  h0_h = defined_e_or(inst_h0, 1, def_h0_h);
1797  h0_ho = defined_e_or(inst_h0, 2, def_h0_ho);
1798  h0_da = defined_e_or(inst_h0, 3, def_h0_da);
1799  h0_ha = defined_e_or(inst_h0, 4, def_h0_ha);
1800  h0_vr = defined_e_or(inst_h0, 5, def_h0_vr);
1801  h0_vrm = defined_e_or(inst_h0, 6, def_h0_vrm);
1802 
1803  h0 = [h0_d, h0_h, h0_ho, h0_da, h0_ha, h0_vr, h0_vrm];
1804 
1805  h0_rh = defined_e_or(inst_h0, 7, def_h0_rh);
1806  h0_ro = defined_e_or(inst_h0, 8, def_h0_ro);
1807  h0_rta = defined_e_or(inst_h0, 9, def_h0_rta);
1808  h0_rba = defined_e_or(inst_h0,10, def_h0_rba);
1809 
1810  h0r = [h0_rh, h0_ro, h0_rta, h0_rba];
1811 
1812  //
1813  // assign hole and post defaults based on selected mode 'cfg_hp_idr'
1814  //
1815  tdef_h1_ims = (cfg_hp_idr == true) ? h0_d + tdef_h1_d_c : tdef_h1_d;
1816  tdef_p_ims = (cfg_hp_idr == true) ? h0_d + tdef_p_d_c : tdef_p_d;
1817 
1818  // hole1: aux screw hole or thru lid access hole
1819  h1_en = (remove == true);
1820 
1821  h1_d = defined_e_or(inst_h1, 0, tdef_h1_ims);
1822  h1_h = defined_e_or(inst_h1, 1, tdef_h1_h);
1823  h1_ho = defined_e_or(inst_h1, 2, tdef_h1_ho);
1824  h1_da = defined_e_or(inst_h1, 3, tdef_h1_da);
1825  h1_ha = defined_e_or(inst_h1, 4, tdef_h1_ha);
1826  h1_vr = defined_e_or(inst_h1, 5, tdef_h1_vr);
1827  h1_vrm = defined_e_or(inst_h1, 6, tdef_h1_vrm);
1828 
1829  h1 = [h1_d, h1_h, h1_ho, h1_da, h1_ha, h1_vr, h1_vrm];
1830 
1831  h1_rh = defined_e_or(inst_h1, 7, tdef_h1_rh);
1832  h1_ro = defined_e_or(inst_h1, 8, tdef_h1_ro);
1833  h1_rta = defined_e_or(inst_h1, 9, tdef_h1_rta);
1834  h1_rba = defined_e_or(inst_h1,10, tdef_h1_rba);
1835 
1836  h1r = [h1_rh, h1_ro, h1_rta, h1_rba];
1837 
1838  // post: post and fins
1839  p_en = (add == true);
1840 
1841  p_d = defined_e_or(inst_p, 0, tdef_p_ims);
1842  p_h = defined_e_or(inst_p, 1, tdef_p_h);
1843  p_ho = defined_e_or(inst_p, 2, tdef_p_ho);
1844  p_da = defined_e_or(inst_p, 3, tdef_p_da);
1845  p_ha = defined_e_or(inst_p, 4, tdef_p_ha);
1846  p_vr = defined_e_or(inst_p, 5, tdef_p_vr);
1847  p_vrm = defined_e_or(inst_p, 6, tdef_p_vrm);
1848 
1849  p = [p_d, p_h, p_ho, p_da, p_ha, p_vr, p_vrm];
1850 
1851  p_rh = defined_e_or(inst_p, 7, tdef_p_rh);
1852  p_ro = defined_e_or(inst_p, 8, tdef_p_ro);
1853  p_rta = defined_e_or(inst_p, 9, tdef_p_rta);
1854  p_rba = defined_e_or(inst_p,10, tdef_p_rba);
1855 
1856  pr = [p_rh, p_ro, p_rta, p_rba];
1857 
1858  // post-fins
1859  f_c = defined_e_or(inst_f, 0, tdef_f_c);
1860  f_da = defined_e_or(inst_f, 1, tdef_f_da);
1861  f_w = defined_e_or(inst_f, 2, tdef_f_w);
1862  f_d_sf = defined_e_or(inst_f, 3, tdef_f_d_sf);
1863  f_h_sf = defined_e_or(inst_f, 4, tdef_f_h_sf);
1864  f_vr = defined_e_or(inst_f, 5, tdef_f_vr);
1865  f_vrm = defined_e_or(inst_f, 6, tdef_f_vrm);
1866 
1867  f = [f_c, f_da, f_w, f_d_sf, f_h_sf, f_vr, f_vrm];
1868 
1869  //
1870  // construct post instance
1871  //
1872 
1873  translate(inst_m) // do separately to allow for 2d
1874  translate([inst_zx, inst_zy, inst_zz]) // moves in 'inst_m' of form [x, y]
1875  rotate(inst_r)
1876  union()
1877  {
1878  construct_cylinder(p_en, p, pr, inst_ft, f);
1879 
1880  construct_cylinder(h0_en, h0, h0r, eps_h=eps*8);
1881  construct_cylinder(h1_en, h1, h1r, eps_h=eps*8);
1882  }
1883 
1884  if (verb > 1)
1885  echo(strl(["post-inst: [type, align, move, rotation, hole0, hole1, post, fins] = ",
1886  [inst_t, inst_a, inst_m, inst_r, inst_h0, inst_h1, inst_p, inst_f]]));
1887  }
1888 
1889  // post-processing message
1890  if (verb > 0)
1891  {
1892  // handle special case: a single post
1893  post_cnt = is_list(inst_l) ? len(inst_l) : 1;
1894 
1895  echo(strl(["post: configuration = ", config]));
1896  echo(strl(["post: mode = ", post_m]));
1897 
1898  echo(strl(["post: count = ", post_cnt]));
1899  }
1900  }
1901 
1902  // limit child to wall interior volume
1903  module envelop_assembly( envelop=false )
1904  {
1905  if ( envelop == true )
1906  {
1907  intersection()
1908  {
1909  union()
1910  {
1911  construct_exterior_walls( true );
1912  construct_lips( true );
1913  }
1914 
1915  children();
1916  }
1917  }
1918  else
1919  {
1920  children();
1921  }
1922  }
1923 
1924  // assembly feature additions
1925  module assembly_add()
1926  {
1927  if ( wall_h > 0 && binary_bit_is(mode, 4, 0) )
1928  {
1929  construct_exterior_walls();
1930  }
1931 
1932  if ( is_defined( lip ) && binary_bit_is(mode, 5, 0) )
1933  {
1934  construct_lips();
1935  }
1936 
1937  if ( lid_h > 0 && binary_bit_is(mode, 6, 0) )
1938  {
1939  construct_lid();
1940  }
1941 
1942  //
1943  // better to apply envelop_assembly() to union of all?
1944  //
1945 
1946  if ( is_defined( rib ) && binary_bit_is(mode, 7, 0) )
1947  {
1948  envelop_assembly( mode_int_mask == true )
1949  construct_ribs();
1950  }
1951 
1952  if ( is_defined( wall ) && binary_bit_is(mode, 8, 0) )
1953  {
1954  envelop_assembly( mode_int_mask == true )
1955  construct_interior_walls();
1956  }
1957 
1958  if ( is_defined( post ) && binary_bit_is(mode, 9, 0) )
1959  {
1960  envelop_assembly( mode_int_mask == true )
1961  construct_posts( add=true );
1962  }
1963 
1964  if ( is_defined( shape ) && binary_bit_is(mode, 11, 0) )
1965  {
1966  // should not be enclosure envelope limited
1967  construct_shapes();
1968  }
1969  }
1970 
1971  // assembly feature removals
1972  module assembly_remove()
1973  {
1974  if ( is_defined( post ) && binary_bit_is(mode, 9, 0) )
1975  {
1976  construct_posts( remove=true );
1977  }
1978 
1979  if ( is_defined( hole ) && binary_bit_is(mode, 10, 0) )
1980  {
1981  construct_holes();
1982  }
1983  }
1984 
1985  //
1986  //
1987  // global parameter calculation
1988  //
1989  //
1990 
1991  // decode mode configurations
1992  mode_size_in = binary_bit_is(mode, 0, 1);
1993  mode_int_mask = binary_bit_is(mode, 1, 1);
1994  mode_scale_io = binary_bit_is(mode, 2, 1);
1995  mode_lmt_vrm = binary_bit_is(mode, 3, 0);
1996 
1997  // specified wall extrusion height
1998  // calculate total extrusion 'h_h' height of all sections
1999  h_h = extrude_linear_mss_eht( h );
2000 
2001  // specified base size
2002  size_x = defined_e_or(size, 0, size);
2003  size_y = defined_e_or(size, 1, size_x);
2004 
2005  // limit rounding mode to those options that make sense; set={0, 5, 1}
2006  // limit each element when 'vrm' is a list
2007  vrm_ci = (mode_lmt_vrm == false) ? vrm
2008  : is_list(vrm) ?
2009  [for (e=vrm) select_ci(v=[0, 5, 1], i=e, l=false)]
2010  : select_ci(v=[0, 5, 1], i=vrm, l=false);
2011 
2012  // wall lip default height (set to zero when there is no lip)
2013  // 'lip_h', bit '1', is set globally (ensure coherency with bits of 'lip')
2014  lip_hd = is_defined(lip) ? wth : 0;
2015  lip_h = defined_e_or(lip, 1, lip_hd);
2016 
2017  // lid extrusion height (calculate total height of all sections)
2018  lid_h = extrude_linear_mss_eht( lid );
2019 
2020  // wall height
2021  wall_h = (mode_size_in == true) ? h_h - lip_h : h_h - lip_h - lid_h;
2022 
2023  // wall x and y insets (usually negative, but allow positive)
2024  wall_od = ( is_defined(inset) && is_scalar(inset) ) ? inset : 0;
2025  wall_ox = defined_e_or(inset, 0, wall_od) * -1;
2026  wall_oy = defined_e_or(inset, 1, wall_od) * -1;
2027 
2028  // exterior envelope of enclosure [encl_x, encl_y, encl_z]
2029  encl_x = (mode_size_in == true) ? size_x + 2*wth - wall_ox : size_x;
2030  encl_y = (mode_size_in == true) ? size_y + 2*wth - wall_oy : size_y;
2031  encl_z = (mode_size_in == true) ? wall_h + lip_h + lid_h : h_h;
2032 
2033  // exterior size of wall x and y
2034  wall_xy = [encl_x + wall_ox, encl_y + wall_oy];
2035 
2036  // interior size of enclosure
2037  szint_x = first (wall_xy) - 2*wth;
2038  szint_y = second(wall_xy) - 2*wth;
2039  szint_z = wall_h + lip_h;
2040 
2041  if (verb > 0)
2042  {
2043  echo(strl(["box: construction begin"]));
2044 
2045  echo(strl(["box: exterior dimensions [x, y, z] = ", [encl_x, encl_y, encl_z]]));
2046  echo(strl(["box: interior dimensions [x, y, z] = ", [szint_x, szint_y, szint_z]]));
2047  }
2048 
2049  //
2050  //
2051  // box and feature construction
2052  //
2053  //
2054 
2055  alignments =
2056  [
2057  [0, -encl_x, -szint_x -wth*2, -szint_x, +szint_x, +szint_x +wth*2, +encl_x ]/2,
2058  [0, -encl_y, -szint_y -wth*2, -szint_y, +szint_y, +szint_y +wth*2, +encl_y ]/2,
2059  [lid_h, 0, lid_h -encl_z/2, -wall_h/2, -wall_h, -wall_h -lip_h]
2060  ];
2061 
2062  align_x = select_ci ( alignments.x, defined_e_or(align, 0, 0), false );
2063  align_y = select_ci ( alignments.y, defined_e_or(align, 1, 0), false );
2064  align_z = select_ci ( alignments.z, defined_e_or(align, 2, 0), false );
2065 
2066  translate([align_x, align_y, align_z])
2067  difference()
2068  {
2069  assembly_add();
2070  assembly_remove();
2071  }
2072 
2073  if (verb > 0)
2074  {
2075  echo(strl(["box: alignment [x, y, z] = ", [align_x, align_y, align_z]]));
2076 
2077  echo(strl(["box: construction end"]));
2078  }
2079 }
2080 
2081 //! Get the bounding box dimensions for a specified project box.
2082 /***************************************************************************//**
2083  \param bbox <integer> bounding box selections {0=exterior, 1=interior}.
2084 
2085  \details
2086 
2087  This function returns the bounding box dimensions for the specified
2088  project box. The parameter values must correspond to those of the
2089  box in question. Detailed descriptions of the undocumented
2090  parameters can be found in project_box_rectangle().
2091 *******************************************************************************/
2093 (
2094  wth,
2095  h,
2096  size,
2097 
2098  inset,
2099 
2100  lid,
2101  lip,
2102 
2103  mode = 0,
2104 
2105  bbox = 0
2106 ) =
2107  let
2108  (
2109  //
2110  // maintain coherence with
2111  // global parameter calculation in project_box_rectangle()
2112  //
2113 
2114  mode_size_in = binary_bit_is(mode, 0, 1),
2115 
2116  h_h = extrude_linear_mss_eht( h ),
2117 
2118  size_x = defined_e_or(size, 0, size),
2119  size_y = defined_e_or(size, 1, size_x),
2120 
2121  lip_hd = is_defined(lip) ? wth : 0,
2122  lip_h = defined_e_or(lip, 1, lip_hd),
2123 
2124  lid_h = extrude_linear_mss_eht( lid ),
2125 
2126  wall_h = (mode_size_in == true) ? h_h - lip_h : h_h - lip_h - lid_h,
2127 
2128  wall_od = ( is_defined(inset) && is_scalar(inset) ) ? inset : 0,
2129  wall_ox = defined_e_or(inset, 0, wall_od) * -1,
2130  wall_oy = defined_e_or(inset, 1, wall_od) * -1,
2131 
2132  encl_x = (mode_size_in == true) ? size_x + 2*wth - wall_ox : size_x,
2133  encl_y = (mode_size_in == true) ? size_y + 2*wth - wall_oy : size_y,
2134  encl_z = (mode_size_in == true) ? wall_h + lip_h + lid_h : h_h,
2135 
2136  wall_xy = [encl_x + wall_ox, encl_y + wall_oy],
2137 
2138  szint_x = first (wall_xy) - 2*wth,
2139  szint_y = second(wall_xy) - 2*wth,
2140  szint_z = wall_h + lip_h
2141  )
2142  bbox ? [szint_x, szint_y, szint_z] : [encl_x, encl_y, encl_z];
2143 
2144 
2145 //! @}
2146 //! @}
2147 
2148 
2149 //----------------------------------------------------------------------------//
2150 // openscad-amu auxiliary scripts
2151 //----------------------------------------------------------------------------//
2152 
2153 /*
2154 BEGIN_SCOPE example_multiple;
2155  BEGIN_OPENSCAD;
2156  include <omdl-base.scad>;
2157  include <shapes/select_common_2d.scad>;
2158  include <shapes/select_common_3d.scad>;
2159  include <transforms/base_cs.scad>;
2160  include <transforms/layout.scad>;
2161  include <parts/3d/enclosure/project_box_rectangle.scad>;
2162 
2163  wth = 2; h = 8; sx = 75; sy = 50; vr = 5; vrm = 2;
2164 
2165  translate([0,0,0]) // bottom
2166  project_box_rectangle( wth=wth, h=h, size=[sx,sy], vr=vr, vrm=vrm, lip=1, lid=wth );
2167 
2168  for (z = [1,3]) translate([0,0, (h+4)*z]) // mid-sections
2169  project_box_rectangle( wth=wth, h=h, size=[sx,sy], vr=vr, vrm=vrm, lip=9 );
2170 
2171  translate([0,0,(h+4)*4 + h]) mirror([0,0,1]) // top
2172  project_box_rectangle( wth=wth, h=h, size=[sx,sy], vr=vr, vrm=vrm, lip=2, lid=wth );
2173 
2174  // end_include
2175  END_OPENSCAD;
2176 
2177  BEGIN_MFSCRIPT;
2178  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
2179  table_unset_all sizes;
2180 
2181  images name "sizes" types "sxga";
2182  views name "views" views "front diag";
2183 
2184  variables set_opts_combine "sizes views";
2185  variables add_opts "--viewall --autocenter --view=axes";
2186 
2187  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
2188  END_MFSCRIPT;
2189 END_SCOPE;
2190 */
2191 
2192 /*
2193 BEGIN_SCOPE example_bottom;
2194  BEGIN_OPENSCAD;
2195  include <omdl-base.scad>;
2196  include <shapes/select_common_2d.scad>;
2197  include <shapes/select_common_3d.scad>;
2198  include <transforms/base_cs.scad>;
2199  include <transforms/layout.scad>;
2200  include <parts/3d/enclosure/project_box_rectangle.scad>;
2201 
2202  $fn = 18;
2203 
2204  wth = 1;
2205 
2206  lid_rounding = [for (s=[0:1/32:1/8]) 1-pow(s,2)/2];
2207  lid_profile = [[wth/3, reverse(lid_rounding)], wth/3, [wth/3, lid_rounding]];
2208 
2209  // set default post support fin count to '0' and assign 'undef'
2210  // to other parameters to use their default values.
2211  function post(x, o) =
2212  [
2213  [0, [undef, undef, undef, undef, undef, [0]]],
2214  [
2215  [x, [-1,-1], [+1,+1]*o],
2216  [x, [-1,+1], [+1,-1]*o],
2217  [x, [+1,-1], [-1,+1]*o],
2218  [x, [+1,+1], [-1,-1]*o],
2219  ]
2220  ];
2221 
2222  project_box_rectangle
2223  (
2224  wth = wth,
2225  size = [100, 60],
2226  h = [[1, [1.05,1]], 7, [5, [1,.99,1]], 6, [6, [1,1.05,1]]],
2227  vr = 2,
2228  vrm = 1,
2229 
2230  inset = 5,
2231 
2232  lip = 1,
2233  lid = lid_profile,
2234  rib = 0,
2235  wall = [13, [[1, -11.5], [1, +11.5] ]],
2236  post = post(0, wth*3.25),
2237 
2238  mode = 1
2239  );
2240 
2241  // end_include
2242  END_OPENSCAD;
2243 
2244  BEGIN_MFSCRIPT;
2245  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
2246  table_unset_all sizes;
2247 
2248  images name "sizes" types "sxga";
2249  views name "views" views "top front right diag";
2250 
2251  variables set_opts_combine "sizes views";
2252  variables add_opts "--viewall --autocenter --view=axes";
2253 
2254  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
2255  END_MFSCRIPT;
2256 END_SCOPE;
2257 */
2258 
2259 /*
2260 BEGIN_SCOPE example_lip;
2261  BEGIN_OPENSCAD;
2262  include <omdl-base.scad>;
2263  include <shapes/select_common_2d.scad>;
2264  include <shapes/select_common_3d.scad>;
2265  include <transforms/base_cs.scad>;
2266  include <transforms/layout.scad>;
2267  include <parts/3d/enclosure/project_box_rectangle.scad>;
2268 
2269  wth = 2;
2270  size = [20, 40];
2271  lid = wth;
2272  rib = [0, wth/2];
2273 
2274  vr = 5;
2275  vrm = 2;
2276 
2277  lip1 = [1, undef, 49.0, 10, [3,5,2], [ [0, [0,1], [0,-1]] ] ];
2278  lip2 = [2, undef, 49.0, 10, [3,5,2], [ [1, [0,1], [0,+1]] ] ];
2279 
2280  translate([-size.x*2/3, 0, 0])
2281  project_box_rectangle ( wth=wth, size=size, h=6, lid=lid, rib=rib, vr=vr, vrm=vrm, lip = lip1 );
2282 
2283  translate([+size.x*2/3, 0, 0])
2284  project_box_rectangle ( wth=wth, size=size, h=5, lid=lid, rib=rib, vr=vr, vrm=vrm, lip = lip2 );
2285 
2286  // end_include
2287  END_OPENSCAD;
2288 
2289  BEGIN_MFSCRIPT;
2290  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
2291  table_unset_all sizes;
2292 
2293  images name "sizes" types "sxga";
2294  views name "views" views "top front right back diag";
2295 
2296  variables set_opts_combine "sizes views";
2297  variables add_opts "--viewall --autocenter --view=axes";
2298 
2299  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
2300  END_MFSCRIPT;
2301 END_SCOPE;
2302 */
2303 
2304 //----------------------------------------------------------------------------//
2305 // end of file
2306 //----------------------------------------------------------------------------//
2307 
zero3d
<decimal-list-2> A 3d zero vector (a list with three zeros).
Definition: constants.scad:422
eps
<decimal> Epsilon, small distance to deal with overlapping shapes.
Definition: constants.scad:195
empty_lst
<list> A list with no values (the empty list).
Definition: constants.scad:304
function binary_iw2i(v, s, w)
Decode the binary bits of a bit window to an integer value.
function binary_bit_is(v, b, t=1)
Test if a binary bit position of an integer value equals a test bit.
function defined_ei_or(v, i, d, n)
Returns an element from an iterable if it exists and is a iterable, otherwise returns a default value...
function defined_e_or(v, i, d)
Returns an element from an iterable if it exists, or a default value if not.
function third(v)
Return the third element of an iterable value.
function last(v)
Return the last element of an iterable value.
function second(v)
Return the second element of an iterable value.
function defined_eon_or(v, i, d)
Returns the list element or scalar numeric value if defined, otherwise returns the default value.
function first(v)
Return the first element of an iterable value.
function shift_cd(v, n=0, r=true, d=false)
Circularly shift the elements of an iterable, with an optional element drop.
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 limit(v, l, u)
Limit a list of numbers between an upper and lower bounds.
function strl(v)
Convert a list of values to a concatenated string.
function merge_s(v, r=false)
Serially merge the elements of a list.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function defined_or(v, d)
Return given value, if defined, or a secondary value, if primary is not defined.
function is_scalar(v)
Test if a value is a single non-iterable value.
function is_defined(v)
Test if a value is defined.
function polygon_regular_perimeter(n, r, a)
Compute the perimeter of an n-sided regular polygon in 2D.
function get_fn(r=0)
Return facets number for the given arc radius.
module project_box_rectangle(wth, h, size, vr, vrm, inset, lid, lip, rib, wall, post, hole, shape, align, mode=0, verb=0)
A rectangular box maker for project boxes, enclosures and housings.
function project_box_rectangle_size(wth, h, size, inset, lid, lip, mode=0, bbox=0)
Get the bounding box dimensions for a specified project box.
module pg_triangle_sas(v, a=x_axis_ci, o, vr, vrm=1, vfn, cm=0)
A polygon triangle specified by size-angle-size with vertex rounding.
Definition: polygon.scad:1112
module pg_rectangle(size=1, o, vr, vrm=1, vfn, center=false)
A polygon rectangle with vertex rounding.
Definition: polygon.scad:773
module select_common_2d_shape(type=0, argv, center=false, verb=0)
Select configure and construct one of the available 2d shapes.
module select_common_3d_shape(type=0, argv, center=false, verb=0)
Select configure and construct one of the available 3d shapes.
module difference_cs(c=true, s)
Conditionally apply the difference boolean operation.
Definition: base_cs.scad:264
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
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 extrude_linear_mss_eht(h, s=false)
Return the total extrusion height for extrude_linear_mss().
module layout_grid_rp(t, s, b=zero3d, center=false, mode=0, verb=0)
A transform sequence to replicate and place a child shape.
Definition: layout.scad:507