omdl  v1.0
OpenSCAD Mechanical Design Library
operation.scad
Go to the documentation of this file.
1 //! Drafting: tools and general operations.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2019-2023,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_include (include/amu/doxyg_init_pd_gds_ip.amu)
31 *******************************************************************************/
32 
33 // sub-group begin
34 /***************************************************************************//**
35  \amu_include (include/amu/doxyg_add_to_parent_open.amu)
36 *******************************************************************************/
37 
38 // sub-group reference definitions
39 /***************************************************************************//**
40  \amu_include (include/amu/scope_diagram_2d_object.amu)
41 
42  \amu_define group_references
43  (
44  [style]: \ref draft_line()
45  [arrow]: \ref draft_arrow()
46 
47  [styles]: \ref draft_line()
48  [arrows]: \ref draft_arrow()
49 
50  [conventions]: \ref tools_2d_drafting_conventions
51  )
52 *******************************************************************************/
53 
54 // sub-group documentation and conventions
55 /***************************************************************************//**
56  /+
57  \addtogroup \amu_eval(${parent})
58  \details
59  +/
60 *******************************************************************************/
61 
62 //----------------------------------------------------------------------------//
63 // members
64 //----------------------------------------------------------------------------//
65 
66 //----------------------------------------------------------------------------//
67 // layer assignment
68 //----------------------------------------------------------------------------//
69 
70 //! \name Layer Assignment
71 //! @{
72 
73 //! Assign one or more layers to child objects.
74 /***************************************************************************//**
75  \param layers <string-list> The list of drafting layer names to
76  render this object on. Defaults to the "default"; the
77  general-purpose layer. See \ref draft_layers_show and the
78  layer [conventions].
79 
80  \details
81 
82  All children will be assigned the specified layer or layers and
83  will be subsequently shown only when one of these layers are active
84  as indicated by \ref draft_layers_show.
85 
86  \amu_eval(${group_references})
87 *******************************************************************************/
88 module draft_in_layers
89 (
90  layers = _draft_get_config("layers-default")
91 )
92 {
93  if (_draft_layers_any_active(layers))
94  children();
95 }
96 
97 //! @}
98 
99 //----------------------------------------------------------------------------//
100 // object placement
101 //----------------------------------------------------------------------------//
102 
103 //! \name Object Placement
104 //! @{
105 
106 //! Move one or more child objects to a sheet reference zone.
107 /***************************************************************************//**
108  \param list <datastruct-list> A list of alignment references, zones,
109  and child object indexes.
110 
111  \details
112 
113  Each list element specifies the placement of one child object and
114  has the form:
115 
116  \verbatim
117  <datastruct> = [ 0:<alignment-point>, 1:<zone-reference>, 2:<child-index> ]
118  \endverbatim
119 
120  field | data type | default | description
121  :------:|:-------------------------------:|:-------------:|:-------------------------
122  0 | decimal-list-2 | [0, 0] | alignment point [px, py]
123  1 | string-list-2 \| decimal-list-2 | sheet center | zone: [rx,ry]=string or [ix,iy]=int
124  2 | integer | loop index | child object index
125 
126  The alignment point \c [px, py] linearly scales the position within
127  the target zone: \b -1 = left/bottom edge, \b 0 = center/middle,
128  \b +1 = right/top edge.
129 
130  The zone reference field accepts either string labels (e.g.
131  \c ["H","4"]) that match the zone label identifiers configured in
132  \ref draft_sheet_config, or integer indexes (e.g. \c [7,3]) that
133  address zones by their numeric position. When field 1 is omitted,
134  the object is placed at the sheet center.
135 
136  When field 2 is omitted, child \c i is mapped to list entry \c i
137  in document order. When the list is longer than the number of
138  children, the last child is reused for surplus entries.
139 
140  \b Example:
141 
142  \code{.C}
143  draft_move
144  (
145  [
146  [[-1, 1]], // child 0 to sheet top-left
147  [[ 1, -1]], // child 1 to sheet bottom-right
148  [[ 0, 0], ["H","4"]], // child 2 to center of zone H4
149  [[ 0, 0], [3, 1], 0] // child 0 again to center of zone [3,1]
150  ]
151  )
152  { ... }
153  \endcode
154 
155  \amu_eval ( html_image_w=512 latex_image_w="3.00in" object=draft_move ${object_diagram_2d} )
156 *******************************************************************************/
157 module draft_move
158 (
159  list
160 )
161 {
162  // do nothing when no children
163  if ( $children )
164  let( imax = max($children-1, len(list)-1) )
165  for ( i = [0:imax] )
166  {
167  e = list[i];
168 
169  // default alignment
170  p = defined_or(e[0], [0, 0]);
171 
172  // numerical or string references
173  n = all_numbers(e[1]) ? e[1] : [undef, undef];
174  s = all_strings(e[1]) ? e[1] : [undef, undef];
175 
176  // child index default
177  c = defined_or(e[2], i);
178 
179  translate
180  (
181  _draft_sheet_get_zone
182  (
183  rx=s[0], ry=s[1],
184  ix=n[0], iy=n[1],
185  zp=p,
186  frame=false, window=false
187  )
188  )
189  children(c);
190  }
191 }
192 
193 //! @}
194 
195 //----------------------------------------------------------------------------//
196 // sheet construction
197 //----------------------------------------------------------------------------//
198 
199 //! \name Sheet Construction
200 //! @{
201 
202 //! Construct a drafting sheet with frame, zone, grid, and origin.
203 /***************************************************************************//**
204  \param sheet <value-list-2> A sheet line configuration that
205  overrides sheet line <width, [style]>.
206  \param frame <value-list-2> A frame line configuration that
207  overrides frame line <width, [style]>.
208  \param zone <value-list-2> A zone line configuration that
209  overrides zone line <width, [style]>.
210  \param grid <value-list-2> A grid line configuration that
211  overrides grid line <width, [style]>.
212  \param origin <value-list-4> An origin line configuration that
213  overrides origin line <width, [style], length, [arrow]>.
214 
215  \param check <boolean> When \b true, validates the current sheet
216  configuration and size tables and prints a diagnostic
217  report to the console via \c table_check(). No geometry is
218  affected. Useful during design setup to confirm that \ref
219  draft_sheet_config and \ref draft_sheet_size are pointing
220  to recognized entries.
221 
222  \param layers <string-list> The list of drafting layer names to
223  render this object on. Defaults to the "sheet"; rendered
224  with sheet frame and rulers. See \ref draft_layers_show and
225  the layer [conventions].
226 
227  \details
228 
229  When a parameter is not specified, the default value is used for the
230  current sheet configuration. The sheet configuration defaults are
231  set by \ref draft_sheet_config.
232 
233  The parameters \p sheet, \p frame, \p zone, and \p grid each accept
234  a list of two values. The first value sets the construction line
235  width and the second sets the construction line style; <width,
236  [style]>. The style value may also be a list to configure the details
237  of the style as documented in draft_line(). The parameter \p origin
238  accepts a list of four values: <width, [style], length, [arrow]>.
239  The style may be any of those available in draft_line() and the
240  arrow may be any available in draft_arrow().
241 
242  \amu_eval ( html_image_w=512 latex_image_w="3.00in" object=draft_sheet ${object_diagram_2d} )
243 
244  \amu_eval(${group_references})
245 *******************************************************************************/
246 module draft_sheet
247 (
248  sheet,
249  frame,
250  zone,
251  grid,
252  origin,
253  check = false,
254  layers = _draft_get_config("layers-sheet")
255 )
256 {
257  assert
258  (
259  table_exists( r=draft_sheet_config_tr, ri=draft_sheet_config ),
260  str("unknown sheet configuration [", draft_sheet_config, "]")
261  );
262 
263  if (_draft_layers_any_active(layers))
264  _draft_make_3d_if_configured()
265  {
266  // check tables
267  if ( check )
268  {
269  table_check( r=draft_sheet_config_tr, c=draft_sheet_config_tc );
270  table_check( r=draft_sheet_size_tr, c=draft_sheet_size_tc );
271  }
272 
273  //
274  // get configuration values (scale all lengths)
275  //
276 
277  // sheet size
278  sdx = _draft_get_sheet_size(ci="sdx") * draft_sheet_scale;
279  sdy = _draft_get_sheet_size(ci="sdy") * draft_sheet_scale;
280 
281  // sheet layout
282  sll = _draft_get_sheet_config(ci="sll");
283 
284  // sheet frame and zone margins
285  smx = _draft_get_sheet_config(ci="smx") * draft_sheet_scale;
286  smy = _draft_get_sheet_config(ci="smy") * draft_sheet_scale;
287  szm = _draft_get_sheet_config(ci="szm") * draft_sheet_scale;
288 
289  // reference zone labels
290  zox = _draft_get_sheet_config(ci="zox");
291  zoy = _draft_get_sheet_config(ci="zoy");
292  zlx = _draft_get_sheet_config(ci="zlx");
293  zly = _draft_get_sheet_config(ci="zly");
294  zrf = _draft_get_sheet_config(ci="zrf");
295  zfs = _draft_get_sheet_config(ci="zfs");
296 
297  // sheet, frame, zone, grid, and origin line configuration
298  slc = _draft_get_sheet_config(ci="slc");
299  flc = _draft_get_sheet_config(ci="flc");
300  zlc = _draft_get_sheet_config(ci="zlc");
301  glc = _draft_get_sheet_config(ci="glc");
302  olc = _draft_get_sheet_config(ci="olc");
303 
304  // set draft scale for sheet
306 
307  //
308  // derived values
309  //
310 
311  // sheet layout dimensions
312  ldx = sll ? sdy : sdx;
313  ldy = sll ? sdx : sdy;
314 
315  // sheet frame dimensions
316  fdx = ldx - smx;
317  fdy = ldy - smy;
318 
319  //
320  // sheet layout
321  //
322  sheet_w = defined_e_or(defined_or(sheet, slc), 0, defined_or(sheet, slc));
323  if ( sheet_w )
324  {
325  sheet_s = defined_e_or(defined_or(sheet, slc), 1, 4);
326 
327  // layout
328  draft_rectangle( d=[ldx, ldy], w=sheet_w, s=sheet_s );
329  }
330 
331  //
332  // sheet frame
333  //
334  frame_w = defined_e_or(defined_or(frame, flc), 0, defined_or(frame, flc));
335  if ( frame_w )
336  {
337  frame_s = defined_e_or(defined_or(frame, flc), 1, 1);
338 
339  // frame
340  draft_rectangle( d=[fdx, fdy], w=frame_w, s=frame_s );
341  }
342 
343  //
344  // zone reference
345  //
346  zone_w = defined_e_or(defined_or(zone, zlc), 0, defined_or(zone, zlc));
347  if ( zone_w )
348  {
349  zone_s = defined_e_or(defined_or(zone, zlc), 1, 1);
350 
351  // zone frame
352  draft_rectangle( d=[fdx, fdy]-2*[szm, szm], w=zone_w, s=zone_s );
353 
354  // zone x-grid and references
355  for ( ix = [0:len(zlx)-1], iyo = [1,-1] )
356  {
357  x = ix * fdx/len(zlx) - fdx/2;
358  yo = iyo * (fdy/2 - szm/2);
359 
360  if (ix > 0)
361  draft_line( l=[ [x, yo-szm/2], [x, yo+szm/2] ], w=zone_w, s=zone_s );
362 
363  zlt = (zox > 0) ? zlx[ix] : zlx[len(zlx)-1-ix];
364  translate( [x + fdx/len(zlx)/2, yo] )
365  text(zlt, font=zrf, size=szm*zfs, halign="center", valign="center");
366  }
367 
368  // zone y-grid and references
369  for ( iy = [0:len(zly)-1], ixo = [1,-1] )
370  {
371  y = iy * fdy/len(zly) - fdy/2;
372  xo = ixo * (fdx/2 - szm/2);
373 
374  if (iy > 0)
375  draft_line( l=[ [xo-szm/2, y], [xo+szm/2, y] ], w=zone_w, s=zone_s );
376 
377  zlt = (zoy > 0) ? zly[iy] : zly[len(zly)-1-iy];
378  translate( [xo, y + fdy/len(zly)/2] )
379  text(zlt, font=zrf, size=szm*zfs, halign="center", valign="center");
380  }
381  }
382 
383  //
384  // field grid
385  //
386  grid_w = defined_e_or(defined_or(grid, glc), 0, defined_or(grid, glc));
387  if ( grid_w )
388  {
389  grid_s = defined_e_or(defined_or(grid, glc), 1, 2);
390 
391  // x-grid
392  for ( ix = [1:len(zlx)-1], iyo = [1,-1] )
393  {
394  x = ix * fdx/len(zlx) - fdx/2;
395  draft_line( l=[ [x, -fdy/2], [x, fdy/2] ], w=grid_w, s=grid_s );
396  }
397  // y-grid
398  for ( iy = [1:len(zly)-1], ixo = [1,-1] )
399  {
400  y = iy * fdy/len(zly) - fdy/2;
401  draft_line( l=[ [-fdx/2, y], [fdx/2, y] ], w=grid_w, s=grid_s );
402  }
403  }
404 
405  //
406  // origin tics
407  //
408  origin_w = defined_e_or(defined_or(origin, olc), 0, defined_or(origin, olc));
409  if ( origin_w )
410  {
411  origin_s = defined_e_or(defined_or(origin, olc), 1, 1); // line style
412  origin_l = defined_e_or(defined_or(origin, olc), 2, 1); // length factor
413  origin_a = defined_e_or(defined_or(origin, olc), 3, 1); // arrow style
414 
415  // x-origin
416  for ( ix = [1,-1] )
417  draft_line
418  (
419  l=ix*[[fdx/2-szm, 0], [fdx/2-szm*origin_l, 0]],
420  w=origin_w, s=origin_s, a2=origin_a
421  );
422 
423  // y-origin
424  for ( iy = [1,-1] )
425  draft_line
426  (
427  l=iy*[[0, fdy/2-szm], [0, fdy/2-szm*origin_l]],
428  w=origin_w, s=origin_s, a2=origin_a
429  );
430  }
431  }
432 }
433 
434 //! Construct drafting sheet axes.
435 /***************************************************************************//**
436  \param size <decimal-list-2-list-2 | decimal-list-2 | decimal>
437  An optional list [[-x, +x], [-y, +y]] or [-x/y, +x/y] of
438  decimals or a single decimal for (-+x=-+y). The x and y
439  negative and positive axes lengths.
440 
441  \param w <decimal-list-2 | decimal> A list [-w, +w] or a single
442  decimal for (-w=+w). The negative and positive axes segment
443  weights.
444  \param s <integer-list-2-list | integer-list-2 | integer> A list
445  [-[s], +[s]] or [-s, +s] of integers or a single integer
446  for (-s=+s). The negative and positive axes [styles].
447  \param a <integer-list-2-list | integer-list-2 | integer> A list
448  [-[a], +[a]] or [-a, +a] of integers or a single integer
449  for (-a=+a). The negative and positive axes [arrows].
450 
451  \param ts <decimal> The axes label text size.
452 
453  \param layers <string-list> The list of drafting layer names to
454  render this object on. Defaults to the "sheet"; rendered
455  with sheet frame and rulers. See \ref draft_layers_show and
456  the layer [conventions].
457 
458  \details
459 
460  \amu_eval ( html_image_w=384 latex_image_w="2.25in" object=draft_axes ${object_diagram_2d} )
461 
462  When \p size is not specified, the axes will span the entire sheet
463  frame.
464 
465  \amu_eval(${group_references})
466 *******************************************************************************/
467 module draft_axes
468 (
469  size,
470 
471  w = 1,
472  s = 2,
473  a = 0,
474 
475  ts = 0,
476 
477  layers = _draft_get_config("layers-sheet")
478 )
479 {
480  if (_draft_layers_any_active(layers))
481  _draft_make_3d_if_configured()
482  {
483  axy = is_defined(size) ? size
484  : let
485  (
486  //
487  // use sheet frame when size is not specified.
488  //
489 
490  // sheet size
491  sdx = _draft_get_sheet_size(ci="sdx") * draft_sheet_scale,
492  sdy = _draft_get_sheet_size(ci="sdy") * draft_sheet_scale,
493 
494  // sheet layout
495  sll = _draft_get_sheet_config(ci="sll"),
496 
497  // sheet frame and zone margins
498  smx = _draft_get_sheet_config(ci="smx") * draft_sheet_scale,
499  smy = _draft_get_sheet_config(ci="smy") * draft_sheet_scale,
500  szm = _draft_get_sheet_config(ci="szm") * draft_sheet_scale,
501 
502  //
503  // derived values
504  //
505 
506  // sheet layout dimensions
507  ldx = sll ? sdy : sdx,
508  ldy = sll ? sdx : sdy,
509 
510  // sheet frame dimensions
511  fdx = ldx - smx,
512  fdy = ldy - smy
513  )
514  // sheet frame less zone margins
515  [fdx/2-szm, fdy/2-szm];
516 
517  for (i = [ [x_axis_ci, x_axis2d_uv, "x"], [y_axis_ci, y_axis2d_uv, "y"] ])
518  {
519  li = defined_e_or(axy, first(i), axy);
520 
521  // negative axes
522  ni = defined_e_or(li, 0, li);
523  if ( ni < 0 )
524  draft_line
525  (
526  l = second(i) * ni,
527  s = defined_e_or(s, 0, s),
528  w = defined_e_or(w, 0, w),
529  a2 = defined_e_or(a, 0, a)
530  );
531 
532  // positive axes
533  pi = defined_e_or(li, 1, ni);
534  if ( pi > 0 )
535  {
536  draft_line
537  (
538  l = +second(i) * pi,
539  s = defined_e_or(s, 1, s),
540  w = defined_e_or(w, 1, w),
541  a2 = defined_e_or(a, 1, a)
542  );
543 
544  // labels
545  if ( ts > 0 )
546  {
547  translate(second(i) * pi * (1 + ts/pi))
548  text(third(i), valign="center", halign="center", size=ts);
549  }
550  }
551  }
552  }
553 }
554 
555 //! Construct a drafting sheet ruler.
556 /***************************************************************************//**
557  \param units <string> The ruler units.
558  \param marks <integer> The number of unit marks per group.
559  \param groups <integer> The number of groups.
560 
561  \param mark_size <decimal> The distance between unit marks
562  (in \p units).
563  \param group_height <decimal> The group-line mark height.
564 
565  \param label_scale <decimal> The text label size scaler.
566  \param label_hide <boolean> Hide ruler text label.
567 
568  \param order <integer-list-2 | integer> The ruler marks horizontal
569  and vertical direction. A list [x, y] of decimals or a
570  single decimal for (x=y).
571 
572  \param w <decimal> The line segment weight.
573 
574  \param layers <string-list> The list of drafting layer names to
575  render this object on. Defaults to the "sheet"; rendered
576  with sheet frame and rulers. See \ref draft_layers_show and
577  the layer [conventions].
578 
579  \details
580 
581  \amu_eval ( html_image_w=512 latex_image_w="3.00in" object=draft_ruler ${object_diagram_2d} )
582 
583  \amu_eval(${group_references})
584 *******************************************************************************/
585 module draft_ruler
586 (
587  units = length_unit_base,
588  marks = 10,
589  groups = 5,
590  mark_size = 1,
591  group_height = 5,
592  label_scale = 2/3,
593  label_hide = false,
594  order = 1,
595  w = 1,
596  layers = _draft_get_config("layers-sheet")
597 )
598 {
599 
600  if (_draft_layers_any_active(layers))
601  _draft_make_3d_if_configured()
602  {
603  // one mark unit length
604  ul = length(mark_size, units);
605 
606  // group-line mark size
607  s = ul * group_height * $draft_scale;
608 
609  // order
610  ox = defined_e_or(order, 0, order);
611  oy = defined_e_or(order, 1, ox);
612  oo = [ox, oy];
613 
614  // draw marks and group ticks
615  // initial group begins mid-group to avoid 2D ruler crossing
616  for ( i=[marks/2:groups*marks], j=[0, 1] )
617  {
618  p = i * ul * oo[j] * $draft_scale;
619  l = line2d_new
620  (
621  m = (
622  (i%marks == marks/2) ? s*2/3
623  : (i%marks) ? s/2
624  : s
625  ) * oo[(j==0)?1:0],
626  p1 = (j == 0) ? [p, 0] : [0, p],
627  v = (j == 0) ? y_axis2d_uv : x_axis2d_uv
628  );
629 
630  // draft tick
631  draft_line (l=l, w=(i%marks) ? w/2 : w, s=1 );
632 
633  // label measurement
634  if ((i == groups*marks) && !label_hide)
635  {
636  // text offset from group tick
637  offset = ul * $draft_scale;
638 
639  translate
640  (
641  (line_ip(l) + line_tp(l))/2 +
642  (j == 0 ? [offset, 0] : [0, offset])
643  )
644  rotate( (j == 0) ? 0 : 90 )
645  text
646  (
647  str
648  (
649  mark_size * marks * groups * $draft_scale,
650  " ", units
651  ),
652  valign="center", size=s*label_scale
653  );
654  }
655  }
656  }
657 }
658 
659 //! @}
660 
661 //----------------------------------------------------------------------------//
662 // table construction
663 //----------------------------------------------------------------------------//
664 
665 //! \name Table Construction
666 //! @{
667 
668 //! Construct a text table that is populated by rows and columns.
669 /***************************************************************************//**
670  \param map <map> A table definition map.
671  \param fmap <map> A table format map.
672 
673  \param zp <integer-list-2 | integer> The center coordinate scaler. A
674  list [zpx, zpy] of decimals or a single decimal for (zpx=zpy).
675 
676  \param window <boolean> Return table window rectangle.
677 
678  \param layers <string-list> The list of drafting layer names to
679  render this object on. Defaults to the "table"; rendered
680  with tables. See \ref draft_layers_show and the layer
681  [conventions].
682 
683  \details
684 
685  \amu_eval ( html_image_w=256 latex_image_w="1.50in" object=draft_table ${object_diagram_2d} )
686 
687  | see: \ref draft_config_map |
688  |:---------------------------:|
689  | table-text-format |
690 
691  \amu_eval(${group_references})
692 *******************************************************************************/
693 module draft_table
694 (
695  map,
696  fmap,
697  zp = 0,
698  window = false,
699  layers = _draft_get_config("layers-table")
700 )
701 {
702  if (_draft_layers_any_active(layers))
703  _draft_make_3d_if_configured()
704  {
705  translate( -_draft_table_get_cell(zp=zp, map=map, fmap=fmap) )
706  if (window)
707  { // solid rectangular window
708  polygon(_draft_table_get_cell(window=true, map=map, fmap=fmap));
709  }
710  else
711  {
712  //
713  // get table format
714  //
715  cmh = map_get_firstof2_or(map, fmap, "cmh", _draft_get_config("table-cmh")) * $draft_scale;
716  cmv = map_get_firstof2_or(map, fmap, "cmv", _draft_get_config("table-cmv")) * $draft_scale;
717 
718  coh = map_get_firstof2_or(map, fmap, "coh", _draft_get_config("table-coh"));
719  cov = map_get_firstof2_or(map, fmap, "cov", _draft_get_config("table-cov"));
720 
721  //
722  // default lines when not in map nor fmap:
723  // no horizontal or vertical lines.
724  //
725  hlines = map_get_firstof2_or(map, fmap, "hlines", _draft_get_config("table-hlines"));
726  vlines = map_get_firstof2_or(map, fmap, "vlines", _draft_get_config("table-vlines"));
727 
728  //
729  // cell default text format when not in map nor fmap:
730  //
731  tdefs = map_get_firstof2_or(map, fmap, "tdefs", _draft_get_config("table-text-format"));
732  hdefs = map_get_firstof2_or(map, fmap, "hdefs", _draft_get_config("table-text-format"));
733  edefs = map_get_firstof2_or(map, fmap, "edefs", _draft_get_config("table-text-format"));
734 
735  //
736  // get table contents
737  //
738  title = map_get_value(map, "title");
739  heads = map_get_value(map, "heads");
740  cols = map_get_value(map, "cols");
741  rows = map_get_value(map, "rows");
742 
743  // Pre-compute all vertical-line x-coordinates and horizontal-line y-coordinates
744  // once here so the per-iteration calls to _draft_table_get_point (which each
745  // re-traverse map/fmap) are replaced by simple index lookups.
746  // Each _draft_table_get_point call re-resolves cmh, cmv, coh, cov and sums
747  // column/row units, so hoisting these arrays eliminates O(cols * rows) redundant
748  // map traversals.
749  nc = len(cols);
750  nr = len(rows);
751  xs = [ for( i=[0:nc] ) _draft_table_get_point( ix=i, map=map, fmap=fmap )[0] ];
752  ys = [ for( i=[0:nr+2] ) _draft_table_get_point( iy=i, map=map, fmap=fmap )[1] ];
753 
754  // draw hlines
755  for( i=[0:nr+2] )
756  {
757  ic = 0; // from left
758  tc = nc; // to right
759 
760  // get line configuration
761  lc = (i == 0) ? hlines[0] // top
762  : (i == nr+2) ? hlines[1] // bottom
763  : (i == 1) ? hlines[2] // title
764  : (i == 2) ? hlines[3] // headings
765  : hlines[4]; // rows
766 
767  //
768  // don't draw title or headings lines when they are
769  // not defined. set line style to '0' no line.
770  //
771  lw = lc[0];
772  ls = (is_undef(title) && (i==1)) ? 0
773  : (is_undef(heads) && (i==2)) ? 0
774  : lc[1];
775 
776  ip = [ xs[ic], ys[i] ];
777  tp = [ xs[tc], ys[i] ];
778 
779  draft_line (l=[ip, tp], w=lw, s=ls );
780  }
781 
782  // draw vlines
783  for( i=[0:nc] )
784  {
785  ic = (i==0||i==nc) ? 0 : 1; // left & right start at top
786  tc = nr+2; // to bottom of table
787 
788  // get line configuration
789  lc = (i == 0) ? vlines[0] // left
790  : (i == nc) ? vlines[1] // right
791  : vlines[2]; // columns
792 
793  lw = lc[0]; // line weight
794  ls = lc[1]; // line style
795 
796  ip = [ xs[i], ys[ic] ];
797  tp = [ xs[i], ys[tc] ];
798 
799  draft_line (l=[ip, tp], w=lw, s=ls );
800  }
801 
802  // add title text
803  _draft_table_text( 0, 0, title[0], cmh, tdefs, map, fmap );
804 
805  // add heading entries text
806  for ( c = [0:nc-1] )
807  _draft_table_text( c, 1, heads[0][c], cmh/2, hdefs, map, fmap );
808 
809  // add cell entries text
810  for ( r = [2:nr+1], c = [0:nc-1] )
811  _draft_table_text( c, r, rows[r-2][0][c], cmh/2, edefs, map, fmap );
812 
813  } // window
814  } // layers
815 }
816 
817 //! Construct a text table that is populated by predefined zones.
818 /***************************************************************************//**
819  \param text <value-list> The list of zone values, where each value
820  is <string | string-list>, a single or multi-line string for
821  the corresponding zone.
822 
823  \param map <map> A zone table definition map.
824 
825  \param zp <integer-list-2 | integer> The center coordinate scaler. A
826  list [zpx, zpy] of decimals or a single decimal for (zpx=zpy).
827 
828  \param number <boolean> Number the defined table zones.
829  \param window <boolean> Return table window rectangle.
830 
831  \param layers <string-list> The list of drafting layer names to
832  render this object on. Defaults to the "table"; rendered
833  with tables. See \ref draft_layers_show and the layer
834  [conventions].
835 
836  \details
837 
838  \amu_eval ( html_image_w=768 latex_image_w="4.50in" object=draft_ztable ${object_diagram_2d} )
839 
840  \amu_eval(${group_references})
841 *******************************************************************************/
842 module draft_ztable
843 (
844  text,
845  map,
846  zp = 0,
847  number = false,
848  window = false,
849  layers = _draft_get_config("layers-table")
850 )
851 {
852  if (_draft_layers_any_active(layers))
853  _draft_make_3d_if_configured()
854  {
855  translate( -_draft_ztable_get_zone(zp=zp, map=map) )
856  if (window)
857  { // solid rectangular window
858  polygon(_draft_ztable_get_zone(window=true, map=map));
859  }
860  else
861  {
862  // number: zones, hlines, and vlines
863  num_zn = defined_e_or(number, 0, number);
864  num_hl = defined_e_or(number, 1, num_zn);
865  num_vl = defined_e_or(number, 2, num_hl);
866 
867  // get title block configuration
868  cmh = map_get_value(map, "cmh") * $draft_scale;
869  cmv = map_get_value(map, "cmv") * $draft_scale;
870 
871  // coh = map_get_value(map, "coh");
872  cov = map_get_value(map, "cov");
873 
874  hldata = map_get_value(map, "hlines");
875  vldata = map_get_value(map, "vlines");
876 
877  htdefs = map_get_value(map, "hdefs");
878  etdefs = map_get_value(map, "edefs");
879  zddata = map_get_value(map, "zones");
880 
881  lts = cmv/6;
882  tts = cmv/6;
883 
884  // draw hlines
885  for( i=[0:len(hldata)-1] )
886  {
887  ic = hldata[i][1][0]; // start hline
888  tc = hldata[i][1][1]; // end hline
889  lw = hldata[i][2]; // line weight
890  ls = hldata[i][3]; // line style
891 
892  ip = _draft_ztable_get_point( ix=ic, iy=i, map=map );
893  tp = _draft_ztable_get_point( ix=tc, iy=i, map=map );
894 
895  draft_line (l=[ip, tp], w=lw, s=ls );
896 
897  // number horizontal lines
898  if ( num_hl )
899  {
900  translate(tp + [lts, lts/2, 0])
901  text(str("H", i), size=lts);
902  }
903  }
904 
905  // draw vlines
906  for( i=[0:len(vldata)-1] )
907  {
908  ic = vldata[i][1][0]; // start vline
909  tc = vldata[i][1][1]; // end vline
910  lw = vldata[i][2]; // line weight
911  ls = vldata[i][3]; // line style
912 
913  ip = _draft_ztable_get_point( ix=i, iy=ic, map=map );
914  tp = _draft_ztable_get_point( ix=i, iy=tc, map=map );
915 
916  draft_line (l=[ip, tp], w=lw, s=ls );
917 
918  // number vertical lines
919  if ( num_vl )
920  {
921  translate(ip - [lts/2, -lts, 0]) rotate(90)
922  text(str("V", i), size=lts);
923  }
924  }
925 
926  // add zone text
927  for( i=[0:len(zddata)-1] )
928  {
929  hidden = zddata[i][2];
930 
931  if ( !zddata[i][2] )
932  {
933  // number zone
934  if ( num_zn )
935  {
936  translate
937  (
938  _draft_ztable_get_zone( i=i, zp=[1,1], map=map ) - [1/2, 1] * lts
939  )
940  text ( str( "Z", i ), valign="center", halign="right", size=lts );
941  }
942 
943  // zone heading-text
944  _draft_ztable_text( i, zddata[i][3][0], tts, htdefs, zddata[i][3], map );
945 
946  // zone entry-text
947  et = (is_empty(text[i]) || is_undef(text[i])) ?
948  zddata[i][4][0]
949  : text[i];
950 
951  _draft_ztable_text( i, et, tts, etdefs, zddata[i][4], map );
952  } // not hidden
953  } // zone text
954  } // window
955  } // layers
956 }
957 
958 //! Construct a text note with optional heading and border.
959 /***************************************************************************//**
960  \param head <string> The optional note heading.
961  \param note <string | string-list> A single or multi-line note
962  text string.
963 
964  \param size <decimal-list-3> A list of decimals that define the
965  <width, line-height, heading-height> of the note.
966  \param line <value-list-2> The border line configuration override
967  that sets the line construction width and style;
968  <width, [style]>.
969 
970  \param halign <string> The text horizontal alignment. One of the
971  predefined strings: < \b "left" | \b "center" | \b "right" >.
972 
973  \param cmh <decimal> The horizontal width minimum unit cell size.
974  \param cmv <decimal> The vertical height minimum unit cell size.
975 
976  \param zp <integer-list-2 | integer> The center coordinate scaler. A
977  list [zpx, zpy] of decimals or a single decimal for (zpx=zpy).
978 
979  \param window <boolean> Return table window rectangle.
980 
981  \param layers <string-list> The list of drafting layer names to
982  render this object on. Defaults to the "note"; rendered
983  with text notes. See \ref draft_layers_show and the layer
984  [conventions].
985 
986  \details
987 
988  The border line style value may be configured as documented in
989  draft_line().
990 
991  \amu_eval ( html_image_w=768 latex_image_w="4.50in" object=draft_note ${object_diagram_2d} )
992 
993  \amu_eval(${group_references})
994 *******************************************************************************/
995 module draft_note
996 (
997  head,
998  note,
999  size,
1000  line,
1001  halign = "left",
1002  cmh = _draft_get_config("note-cmh"),
1003  cmv = _draft_get_config("note-cmv"),
1004  zp = 0,
1005  window = false,
1006  layers = _draft_get_config("layers-note")
1007 )
1008 {
1009  if (_draft_layers_any_active(layers))
1010  // _draft_make_3d_if_configured() handled by draft_table()
1011  {
1012  // default line configuration
1013  lnd = defined_or(line, [1, 1]);
1014  lnc = is_list(lnd[0]) ? [lnd[0], lnd[1]] : [lnd, lnd];
1015 
1016  // default heading text when size specified without text
1017  htd = is_defined(size[2]) && is_undef(head) ? empty_str : head;
1018 
1019  // local table map
1020  map =
1021  [
1022  ["cmh", cmh],
1023  ["cmv", cmv],
1024 
1025  is_undef(head) ? empty_lst :
1026  ["heads", [[htd], defined_e_or(size, 2, 1)]],
1027 
1028  ["cols", [defined_e_or(size, 0, defined_or(size, 1))]],
1029  ["rows", [[[note], defined_e_or(size, 1, 1)]]],
1030 
1031  ["hlines", concat(consts(3,lnc[0]), consts(2,lnc[1]))],
1032  ["vlines", consts(3,lnc[0])]
1033  ];
1034 
1035  // local table format map
1036  fmap = (halign == "center") ? draft_table_format_map_ccc
1037  : (halign == "left") ? draft_table_format_map_cll
1038  : (halign == "right") ? draft_table_format_map_crr
1039  : undef;
1040 
1041  draft_table
1042  (
1043  map=map, fmap=fmap, zp=zp, window=window, layers=layers
1044  );
1045  } // layers
1046 }
1047 
1048 //! Construct a sheet title block.
1049 /***************************************************************************//**
1050  \copydetails draft_ztable()
1051 
1052  \details
1053 
1054  A title block is a ztable with a predetermined layout specified by
1055  a style map.
1056 
1057  \sa draft_title_block_map_style1.
1058 *******************************************************************************/
1059 module draft_title_block
1060 (
1061  text,
1063  zp = 0,
1064  number = false,
1065  window = false,
1066  layers = _draft_get_config("layers-titleblock")
1067 )
1068 {
1069  draft_ztable
1070  (
1071  text=text, map=map, zp=zp, number=number, window=window, layers=layers
1072  );
1073 }
1074 
1075 //! @}
1076 
1077 //! @}
1078 
1079 //----------------------------------------------------------------------------//
1080 // openscad-amu auxiliary scripts
1081 //----------------------------------------------------------------------------//
1082 
1083 /*
1084 BEGIN_SCOPE diagram;
1085  BEGIN_OPENSCAD;
1086  include <omdl-base.scad>;
1087  include <transforms/base_cs.scad>;
1088  include <tools/common/polytope.scad>;
1089  include <tools/2d/drafting/draft-base.scad>;
1090 
1091  object = "draft_move";
1092 
1093  if (object == "draft_move") {
1094  draft_sheet();
1095  draft_move ( [for (x=[0:7], y=[0:3]) [[0,0], [x,y], is_even(x)?0:1]] )
1096  {
1097  square(10, center=true);
1098  circle(d=10);
1099  }
1100  }
1101 
1102  if (object == "draft_sheet") {
1103  draft_sheet( sheet=[5, [5, 4, 5, 5, 25]] );
1104  }
1105 
1106  if (object == "draft_axes") {
1107  draft_axes([[-10,100], [-10,60]], w=[1/2, 3], s=[2, 1], a=[0, [1,1,2,2]], ts=5);
1108  }
1109 
1110  if (object == "draft_ruler") {
1111  draft_ruler();
1112 
1113  translate([75,0])
1114  draft_ruler(units="in", marks=16, groups=2, mark_size=length(1/16/25.4, "in"));
1115  }
1116 
1117  if (object == "draft_table") {
1118  map= [ [ "title", [ "TABLE", 3/2 ] ],
1119  [ "heads", [ ["col1", "col2", "col3"], 3/4 ] ],
1120  [ "cols", [ 2, 2, 2 ] ],
1121  [ "rows", [ [ ["d00", "d01", "d02"], 1 ],
1122  [ ["d10", "d11", "d12"], 1 ],
1123  [ ["d20", "d21", "d22"], 1 ],
1124  [ ["d30", "d31", "d32"], 1 ] ] ] ];
1125 
1126  draft_table( map=map, fmap=draft_table_format_map_ccc );
1127  }
1128 
1129  if (object == "draft_ztable")
1130  {
1131  text= [ [ "data0.0", "data0.1" ],
1132  "data1", "data2", "data3", "data4", "data5", "data6",
1133  "data7", "data8", "data9", "data10", "data11",
1134  [ "data12.0", "data12.1", "data12.2", "data12.3" ] ];
1135 
1136  draft_ztable( text=text, map=draft_title_block_map_style1 );
1137  }
1138 
1139  if (object == "draft_note")
1140  {
1141  for (i = [[-40, "left", [1,0]], [0, "center", [4,0]], [40, "right", 3]] )
1142  translate([first(i), 0])
1143  draft_note
1144  (
1145  head="NOTE",
1146  note=["note 1", "note 2", "note 3"],
1147  size=[3, 2, 3/4],
1148  halign = second(i),
1149  line = [1, third(i)]
1150  );
1151  }
1152  END_OPENSCAD;
1153 
1154  BEGIN_MFSCRIPT;
1155  include --path "${INCLUDE_PATH}" {var_init,var_gen_svg}.mfs;
1156 
1157  defines name "objects" define "object"
1158  strings "
1159  draft_move
1160  draft_sheet
1161  draft_axes
1162  draft_ruler
1163  draft_table
1164  draft_ztable
1165  draft_note
1166  ";
1167  variables add_opts_combine "objects";
1168  variables add_opts "--viewall --autocenter";
1169 
1170  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1171  END_MFSCRIPT;
1172 END_SCOPE;
1173 */
1174 
1175 //----------------------------------------------------------------------------//
1176 // end of file
1177 //----------------------------------------------------------------------------//
x_axis_ci
<integer> The coordinate axis index for the Euclidean space x-axis.
Definition: constants.scad:395
y_axis_ci
<integer> The coordinate axis index for the Euclidean space y-axis.
Definition: constants.scad:398
x_axis2d_uv
<vector-2d> The unit vector of the positive x-axis in 2d Euclidean space.
Definition: constants.scad:410
y_axis2d_uv
<vector-2d> The unit vector of the positive y-axis in 2d Euclidean space.
Definition: constants.scad:413
pi
<decimal> The ratio of a circle's circumference to its diameter.
Definition: constants.scad:198
empty_str
<string> A string with no characters (the empty string).
Definition: constants.scad:301
empty_lst
<list> A list with no values (the empty list).
Definition: constants.scad:304
function line_tp(l)
Return the terminal point of a line or vector.
function line_ip(l)
Return the initial point of a line or vector.
function line2d_new(m=1, a=0, p1=origin2d, p2, v)
Construct a 2 dimensional directed line.
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 second(v)
Return the second element of an iterable value.
function first(v)
Return the first element of an iterable value.
function is_empty(v)
Test if an iterable value is empty.
function all_strings(v)
Test if all elements of an iterable value are strings.
function all_numbers(v)
Test if all elements of an iterable value are numbers.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function map_get_value(m, k)
Get the map value associated with a key.
function map_get_firstof2_or(m1, m2, k, d)
Get the the first value associated with an existing key in one of two maps.
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 table_exists(r, c, ri, ci)
Test the existence of a table row identifier, table column identifier, or both.
module table_check(r, c, verbose=false)
Perform basic format checks on a table and output errors to console.
Definition: table.scad:839
module draft_line(l=x_axis2d_ul, w=1, s=1, a1=0, a2=0)
Draft a line with configurable style and optional arrowheads.
module draft_in_layers(layers=_draft_get_config("layers-default"))
Assign one or more layers to child objects.
Definition: operation.scad:209
draft_table_format_map_cll
<map> Table format map; centered, left, left –justified.
Definition: config.scad:1396
draft_table_format_map_crr
<map> Table format map; centered, right, right –justified.
Definition: config.scad:1420
module draft_sheet(sheet, frame, zone, grid, origin, check=false, layers=_draft_get_config("layers-sheet"))
Construct a drafting sheet with frame, zone, grid, and origin.
Definition: operation.scad:383
module draft_ztable(text, map, zp=0, number=false, window=false, layers=_draft_get_config("layers-table"))
Construct a text table that is populated by predefined zones.
module draft_table(map, fmap, zp=0, window=false, layers=_draft_get_config("layers-table"))
Construct a text table that is populated by rows and columns.
Definition: operation.scad:866
module draft_move(list)
Move one or more child objects to a sheet reference zone.
Definition: operation.scad:282
$draft_scale
<integer> Line construction drafting scale multiplier.
Definition: config.scad:129
draft_table_format_map_ccc
<map> Table format map; centered, centered, centered –justified.
Definition: config.scad:1372
module draft_ruler(units=length_unit_base, marks=10, groups=5, mark_size=1, group_height=5, label_scale=2/3, label_hide=false, order=1, w=1, layers=_draft_get_config("layers-sheet"))
Construct a drafting sheet ruler.
Definition: operation.scad:746
module draft_axes(size, w=1, s=2, a=0, ts=0, layers=_draft_get_config("layers-sheet"))
Construct drafting sheet axes.
Definition: operation.scad:616
draft_title_block_map_style1
<map> A title block map; style 1.
Definition: config.scad:1221
module draft_title_block(text, map=draft_title_block_map_style1, zp=0, number=false, window=false, layers=_draft_get_config("layers-titleblock"))
Construct a sheet title block.
module draft_rectangle(d=1, o=origin2d, w=1, s=1)
Draft a rectangle with configurable style.
module draft_note(head, note, size, line, halign="left", cmh=_draft_get_config("note-cmh"), cmv=_draft_get_config("note-cmv"), zp=0, window=false, layers=_draft_get_config("layers-note"))
Construct a text note with optional heading and border.
draft_sheet_config
<string> Drafting sheet configuration identifier.
Definition: config.scad:596
draft_sheet_scale
<integer> Sheet construction drafting scale multiplier.
Definition: config.scad:159
function length(v, from=length_unit_default, to=length_unit_base, d=1)
Convert a length value from one unit to another, with optional dimensional scaling.
length_unit_base
Definition: length.scad:982