omdl  v1.0
OpenSCAD Mechanical Design Library
table.scad
Go to the documentation of this file.
1 //! Table data structure and operations.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2015-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 (Tables)
31  \amu_define group_brief (Table data type and operations.)
32 
33  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
34 *******************************************************************************/
35 
36 // auto-tests (add to test results page)
37 /***************************************************************************//**
38  \amu_include (include/amu/validate_log.amu)
39  \amu_include (include/amu/validate_results.amu)
40 *******************************************************************************/
41 
42 // group(s) begin (test summary and includes-required)
43 /***************************************************************************//**
44  \amu_include (include/amu/doxyg_define_in_parent_open.amu)
45  \amu_include (include/amu/validate_summary.amu)
46  \amu_include (include/amu/includes_required.amu)
47 *******************************************************************************/
48 
49 // member-wide reference definitions
50 /***************************************************************************//**
51  \amu_define group_references
52  (
53  )
54 *******************************************************************************/
55 
56 // member-wide documentation and conventions
57 /***************************************************************************//**
58  \addtogroup \amu_eval(${group})
59  \details
60  \anchor \amu_eval(${group})_conventions
61  \par Conventions
62 
63  - A table is a pair (\p r, \p c) where \p r is the row matrix and
64  \p c is the column-identifier matrix.
65  - The first column of every table must have the identifier \c "id".
66  Row identity is keyed on this column.
67  - The \c ctable_* functions wrap a compact table \p t = [r, c] and
68  delegate to the corresponding \c table_* function. They are provided
69  for convenience; all semantics are identical.
70  - Row identifiers (\p ri) and column identifiers (\p ci) are the string
71  values stored in the id column and column-id row respectively, not
72  integer indices.
73  - table_get() returns \b undef when the row or column identifier is not
74  found; callers must check the return value before use.
75  - table_errors() returns an empty list for a valid table; a non-empty
76  list contains string error descriptors.
77 
78  The table functions were originally coded with the row and column
79  data as separate parameters. Some equivalent functions are provided
80  that accept the row and column data as a single combined parameter,
81  where <tt>t = [r, c];</tt> for convenience and are prefaced with the
82  letter 'c'.
83 
84  \b Example
85 
86  \amu_define title (Table use)
87  \amu_define scope_id (example_use)
88  \amu_include (include/amu/scope.amu)
89 *******************************************************************************/
90 
91 //----------------------------------------------------------------------------//
92 // members
93 //----------------------------------------------------------------------------//
94 
95 //----------------------------------------------------------------------------//
96 // global configuration variables
97 //----------------------------------------------------------------------------//
98 
99 //! \name Variables
100 //! @{
101 
102 //! <boolean> Enforce strict checking for table value references.
103 $table_strict = false;
104 
105 //! @}
106 
107 //----------------------------------------------------------------------------//
108 // Separate
109 //----------------------------------------------------------------------------//
110 
111 //! \name Functions: Separate parameters
112 //! @{
113 
114 //! Get the table row index that matches a table row identifier.
115 /***************************************************************************//**
116  \param r <table> The table row data matrix (C-columns x R-rows).
117  \param ri <string> The row identifier.
118 
119  \returns <integer> The row index where the identifier exists.
120  Returns \b undef if the identifier does not exists.
121 *******************************************************************************/
122 function table_get_row_index
123 (
124  r,
125  ri
126 ) = let(i = first( search( [ri], r, 1, 0 ) ) )
127  (i == empty_lst) ? undef : i;
128 
129 //! Get the table row that matches a table row identifier.
130 /***************************************************************************//**
131  \param r <table> The table row data matrix (C-columns x R-rows).
132  \param ri <string> The row identifier.
133 
134  \returns <list-C> The table row where the row identifier exists.
135  If the identifier does not exists, returns \b undef.
136 *******************************************************************************/
137 function table_get_row
138 (
139  r,
140  ri
141 ) = let( row_check = ! $table_strict || table_exists(r=r, ri=ri) )
142  assert( row_check, strl(["table row missing [", ri, "]."]) )
143  r[ table_get_row_index(r, ri) ];
144 
145 //! Get the table column index that matches a table column identifier.
146 /***************************************************************************//**
147  \param c <map> The table column identifier matrix (2 x C-columns).
148  \param ci <string> The column identifier.
149 
150  \returns <integer> The column index where the identifier exists.
151  Returns \b undef if the identifier does not exists.
152 *******************************************************************************/
153 function table_get_column_index
154 (
155  c,
156  ci
157 ) = let(i = first( search( [ci], c, 1, 0 ) ) )
158  (i == empty_lst) ? undef : i;
159 
160 //! Get the table column that matches a table column identifier.
161 /***************************************************************************//**
162  \param c <map> The table column identifier matrix (2 x C-columns).
163  \param ci <string> The column identifier.
164 
165  \returns <list-2> The table column where the column identifier exists.
166  If the identifier does not exists, returns \b undef.
167 *******************************************************************************/
168 function table_get_column
169 (
170  c,
171  ci
172 ) = let( column_check = ! $table_strict || table_exists(c=c, ci=ci) )
173  assert( column_check, strl(["table column missing [", ci, "]."]) )
174  c[ table_get_column_index(c, ci) ];
175 
176 //! Get the table cell value for a specified row and column identifier.
177 /***************************************************************************//**
178  \param r <table> The table row data matrix (C-columns x R-rows).
179  \param c <map> The table column identifier matrix (2 x C-columns).
180  \param ri <string> The row identifier.
181  \param ci <string> The column identifier.
182 
183  \returns <value> The value of the table cell <tt>[ri, ci]</tt>.
184  If either identifier does not exists, returns \b undef.
185 *******************************************************************************/
186 function table_get_value
187 (
188  r,
189  c,
190  ri,
191  ci
192 ) = let( row_check = ! $table_strict || table_exists(r=r, ri=ri) )
193  let( column_check = ! $table_strict || table_exists(c=c, ci=ci) )
194  assert( row_check, strl(["table row missing [", ri, "]."]) )
195  assert( column_check, strl(["table column missing [", ci, "]."]) )
196  r[ table_get_row_index(r,ri) ][ table_get_column_index(c,ci) ];
197 
198 //! Form a list of a select column across all table rows.
199 /***************************************************************************//**
200  \param r <table> The table row data matrix (C-columns x R-rows).
201  \param c <map> The table column identifier matrix (2 x C-columns).
202  \param ci <string> The column identifier.
203 
204  \returns <list> The list of a select column across all rows.
205  If the identifier does not exists, returns \b undef.
206 *******************************************************************************/
207 function table_get_columns
208 (
209  r,
210  c,
211  ci
212 ) = let( column_check = ! $table_strict || table_exists(c=c, ci=ci) )
213  assert( column_check, strl(["table column missing [", ci, "]."]) )
214  select_e(table_get_copy(r,c,cs=[ci]), f=true);
215 
216 //! Get a row, a column, or a specific cell value from a table.
217 /***************************************************************************//**
218  \param r <table> The table row data matrix (C-columns x R-rows).
219  \param c <map> The table column identifier matrix (2 x C-columns).
220  \param ri <string> The row identifier.
221  \param ci <string> The column identifier.
222 
223  \returns (1) <value> The value of the table cell <tt>[ri, ci]</tt>
224  when both \p ri and \p ci are defined.
225  (2) The row <list-R> when only \p ri is defined.
226  (3) The column <list-C> when only \p ci is defined.
227  (4) Returns \b undef when the specified row or column
228  identifier is not present in the table.
229  (5) Returns \b undef when both \p ri and \p ci are
230  undefined.
231 
232  \details
233 
234  This function combines the behavior of several other table access
235  functions dependent on the supplied parameters.
236 
237  \b Example
238  \code
239  rows = table_get( r, c, ri );
240  cols = table_get( r, c, ci=ci );
241  cell = table_get( r, c, ri, ci );
242  \endcode
243 *******************************************************************************/
244 function table_get
245 (
246  r,
247  c,
248  ri,
249  ci
250 ) = let ( dr = is_defined(ri), dc = is_defined(ci) )
251  ( dr && dc ) ? table_get_value(r, c, ri, ci)
252  : dr ? table_get_row(r, ri)
253  : dc ? table_get_columns(r, c, ci)
254  : undef;
255 
256 //! Form a list of all table row identifiers.
257 /***************************************************************************//**
258  \param r <table> The table row data matrix (C-columns x R-rows).
259 
260  \returns <list> The list of all row identifiers.
261 
262  \details
263 
264  This functions assumes the first element of each table row to be
265  the row identifier, as enforced by the table_check(). As an
266  alternative, the function table_get_columns(), of the form
267  table_get_columns(r, c, id), may be used without this assumption.
268 *******************************************************************************/
269 function table_get_row_ids
270 (
271  r
272 ) = select_e(r,f=true);
273 
274 //! Form a list of all table column identifiers.
275 /***************************************************************************//**
276  \param c <map> The table column identifier matrix (2 x C-columns).
277 
278  \returns <list> The list of all column identifiers.
279 
280  \details
281 
282  This functions assumes the first element of each table column to be
283  the column identifier.
284 *******************************************************************************/
285 function table_get_column_ids
286 (
287  c
288 ) = select_e(c,f=true);
289 
290 //! Test the existence of a table row identifier, table column identifier, or both.
291 /***************************************************************************//**
292  \param r <table> The table row data matrix (C-columns x R-rows).
293  \param c <map> The table column identifier matrix (2 x C-columns).
294  \param ri <string> The row identifier.
295  \param ci <string> The column identifier.
296 
297  \returns \b true if the specified row and/or column identifier
298  exists, and \b false otherwise.
299 
300  \details
301 
302  The functions can be used to check for a row or a column identifier
303  alone, or can be use to check for the existence of a specific row
304  and column combination.
305 *******************************************************************************/
306 function table_exists
307 (
308  r,
309  c,
310  ri,
311  ci
312 ) = let ( dr = is_defined(ri), dc = is_defined(ci) )
313  ( dr && dc ) ? is_defined(table_get_row_index(r,ri)) &&
315  : dr ? is_defined(table_get_row_index(r,ri))
316  : dc ? is_defined(table_get_column_index(c,ci))
317  : false;
318 
319 //! Get the size of a table.
320 /***************************************************************************//**
321  \param r <table> The table row data matrix (C-columns x R-rows).
322  \param c <map> The table column identifier matrix (2 x C-columns).
323 
324  \returns <integer> The table size.
325 
326  \details
327 
328  The size is reported as: (1) The number of rows when only the \p r
329  parameter is specified. (2) The number of columns when only the \p c
330  parameter is specified. (3) The (r * columns) when both parameters
331  are specified.
332 *******************************************************************************/
333 function table_get_size
334 (
335  r,
336  c
337 ) = ( is_defined(r) && is_undef(c) ) ? len( r )
338  : ( is_undef(r) && is_defined(c) ) ? len( c )
339  : len( r ) * len( c );
340 
341 //! Create a new matrix from select rows and columns of a table.
342 /***************************************************************************//**
343  \param r <table> The table row data matrix (C-columns x R-rows).
344  \param c <map> The table column identifier matrix (2 x C-columns).
345  \param rs <string-list> A list of selected row identifiers.
346  \param cs <string-list> A list of selected column identifiers.
347 
348  \returns <matrix> A matrix of the selected rows and columns.
349 *******************************************************************************/
350 function table_get_copy
351 (
352  r,
353  c,
354  rs,
355  cs
356 ) =
357 [
358  for ( r_iter = r )
359  if
360  (
361  is_undef( rs ) ||
362  is_number( first( search( r_iter, rs, 1, 0 ) ) )
363  )
364  [
365  for ( c_iter = c )
366  if
367  (
368  is_undef( cs ) ||
369  is_number( first( search( c_iter, cs, 1, 0 ) ) )
370  )
371  table_get_value(r, c, r_iter, c_iter)
372 
373  ]
374 ];
375 
376 //! Sum select rows and columns of a table.
377 /***************************************************************************//**
378  \param r <table> The table row data matrix (C-columns x R-rows).
379  \param c <map> The table column identifier matrix (2 x C-columns).
380  \param rs <string-list> A list of selected row identifiers.
381  \param cs <string-list> A list of selected column identifiers.
382 
383  \returns <list> A list with the sum of each selected rows and columns.
384 *******************************************************************************/
385 function table_get_sum
386 (
387  r,
388  c,
389  rs,
390  cs
391 ) = sum( table_get_copy(r, c, rs, cs) );
392 
393 //! Perform basic format checks on a table and return errors.
394 /***************************************************************************//**
395  \param r <table> The table row data matrix (C-columns x R-rows).
396  \param c <map> The table column identifier matrix (2 x C-columns).
397 
398  \returns <list-N> A list of table format errors.
399 
400  \details
401 
402  Check that: (1) the first table column identifier is 'id'. (2) Make
403  sure that each row has the same number of columns as defined in the
404  columns vector. (3) Make sure that there are no repeating column
405  identifiers. (4) Make sure that there are no repeating row
406  identifiers. When there are no errors, the \b empty_lst is
407  returned.
408 *******************************************************************************/
409 function table_errors
410 (
411  r,
412  c
413 ) =
414  let
415  (
416  // (1) first word of first column should be 'id'
417  ec1 =
418  [
419  if ( first( first(c) ) != "id")
420  str ("table column 0 should be 'id'")
421  ],
422 
423  // (2) each row has correct column count
424  ec2 =
425  [
426  let (col_cnt = table_get_size(c=c))
427  for ( r_iter = r )
428  if ( col_cnt != len ( r_iter ) )
429  str
430  (
431  "row ", table_get_row_index(r, r_iter),
432  ", id=[", first(r_iter), "]",
433  ", has incorrect column count=[", len ( r_iter ),"]"
434  )
435  ],
436 
437  // (3) no repeat column identifiers
438  ec3 =
439  [
440  for ( c_iter = c )
441  if ( len(first(search([first(c_iter)], c, 0, 0))) > 1 )
442  str("repeating column identifier [", first(c_iter), "]")
443  ],
444 
445  // (4) no repeat row identifiers
446  ec4 =
447  [
448  for ( r_iter = r )
449  if ( len(first(search([first(r_iter)], r, 0, 0))) > 1 )
450  str("repeating row identifier [", first(r_iter), "]")
451  ]
452  )
453  concat(ec1, ec2, ec3, ec4);
454 
455 
456 //! Perform basic format checks on a table and output errors to console.
457 /***************************************************************************//**
458  \param r <table> The table row data matrix (C-columns x R-rows).
459  \param c <map> The table column identifier matrix (2 x C-columns).
460  \param verbose <boolean> Be verbose during check.
461 
462  \details
463 
464  Check that: (1) the first table column identifier is 'id'. (2) Make
465  sure that each row has the same number of columns as defined in the
466  columns vector. (3) Make sure that there are no repeating column
467  identifiers. (4) Make sure that there are no repeating row
468  identifiers.
469 *******************************************************************************/
470 module table_check
471 (
472  r,
473  c,
474  verbose = false
475 )
476 {
477  if (verbose) log_info("begin table check");
478 
479  // (1) first word of first column should be 'id'
480  if ( first( first(c) ) != "id")
481  {
482  log_warn ("table column 0 should be 'id'");
483  }
484  else
485  {
486  if (verbose) log_info ("row identifier found at column zero.");
487  }
488 
489  // (2) each row must have correct column count
490  if (verbose) log_info ("checking row column counts.");
491  col_cnt = table_get_size(c=c);
492  for ( r_iter = r )
493  {
494  assert
495  (
496  col_cnt == len ( r_iter ),
497  str
498  (
499  "row ", table_get_row_index(r, r_iter),
500  ", id=[", first(r_iter), "]",
501  ", has incorrect column count=[", len ( r_iter ),"]",
502  ", expecting=[", col_cnt, "]"
503  )
504  );
505  }
506 
507  // (3) no repeat column identifiers
508  if (verbose) log_info ("checking for repeat column identifiers.");
509  for ( c_iter = c )
510  if ( len(first(search([first(c_iter)], c, 0, 0))) > 1 )
511  log_warn ( str("repeating column identifier [", first(c_iter), "]") );
512 
513  // (4) no repeat row identifiers
514  if (verbose) log_info ("checking for repeat row identifiers.");
515  for ( r_iter = r )
516  if ( len(first(search([first(r_iter)], r, 0, 0))) > 1 )
517  log_warn ( str("repeating row identifier [", first(r_iter), "]") );
518 
519  if (verbose)
520  {
522  (
523  str (
524  "table size: ",
525  table_get_size(r=r), " rows by ",
526  table_get_size(c=c), " columns."
527  )
528  );
529 
530  log_info("end table check");
531  }
532 }
533 
534 //! Dump a table to the console.
535 /***************************************************************************//**
536  \param r <table> The table row data matrix (C-columns x R-rows).
537  \param c <map> The table column identifier matrix (2 x C-columns).
538  \param rs <string-list> A list of selected row identifiers.
539  \param cs <string-list> A list of selected column identifiers.
540  \param number <boolean> Number the rows.
541  \param align <boolean> pad columns for value alignment.
542 
543  \details
544 
545  Output each table row to the console. To output only select rows and
546  columns, assign the desired identifiers to \p rs and \p cs.
547  For example to output only the column identifiers 'c1' and 'c2', assign
548  <tt>cs = ["c1", "c2"]</tt>.
549 *******************************************************************************/
550 module table_dump
551 (
552  r,
553  c,
554  rs,
555  cs,
556  number = true,
557  align = true
558 )
559 {
560  // determine maximum field lengths when aligning
561  maxr0 = align ?
562  max ( [ for ( r_iter = r )
563  let( v = first(r_iter) )
564  is_string(v) ? len(v) : len( strl([v]) ) ] )
565  : 0;
566 
567  maxc0 = align ?
568  max ( [ for ( c_iter = c )
569  let( v = first(c_iter) )
570  is_string(v) ? len(v) : len( strl([v]) ) ] )
571  : 0;
572 
573  maxc1 = align ?
574  max ( [ for ( c_iter = c )
575  let( v = second(c_iter) )
576  is_string(v) ? len(v) : len( strl([v]) ) ] )
577  : 0;
578 
579  for ( r_iter = r )
580  {
581  if
582  (
583  is_undef( rs ) ||
584  is_number( first( search( r_iter, rs, 1, 0 ) ) )
585  )
586  {
587  // number row
588  if ( number )
589  {
590  log_echo();
591  log_echo( str("row: ", table_get_row_index(r, r_iter)) );
592  }
593 
594  for ( c_iter = c )
595  {
596  if
597  (
598  is_undef( cs ) ||
599  is_number( first( search( c_iter, cs, 1, 0 ) ) )
600  )
601  {
602  rid = first(r_iter);
603  cid = first(c_iter);
604  cdn = second(c_iter);
605 
606  rid_pad = let
607  (
608  s = is_string(rid) ? len(rid) : len( strl( [rid] ) )
609  )
610  align ?
611  chr( consts(maxr0 - s, 32 ) )
613 
614  cid_pad = let
615  (
616  s = is_string(cid) ? len(cid) : len( strl( [cid] ) )
617  )
618  align ?
619  chr( consts(maxc0 - s, 32 ) )
620  : empty_str;
621 
622  cdn_pad = let
623  (
624  s = is_string(cdn) ? len(cdn) : len( strl( [cdn] ) )
625  )
626  align ?
627  chr( consts(maxc1 - s, 32 ) )
628  : empty_str;
629 
630  rc_val = table_get_value(r, c, r_iter, c_iter);
631 
632  log_echo
633  (
634  str
635  (
636  "'", rid, "'", rid_pad, " ",
637  cid_pad, "'", cid, "' (", cdn, ")", cdn_pad,
638  " = '", rc_val, "'"
639  )
640  );
641 
642  } // c-sel
643  } // c
644  } // r-sel
645  } // r
646 
647  if ( number )
648  {
649  log_echo();
650  log_echo
651  (
652  str (
653  "table size: ",
654  table_get_size(r=r), " rows by ",
655  table_get_size(c=c), " columns."
656  )
657  );
658  }
659 }
660 
661 //! Dump table getter functions to the console.
662 /***************************************************************************//**
663  \param r <table> The table row data matrix (C-columns x R-rows).
664  \param c <map> The table column identifier matrix (2 x C-columns).
665  \param tr <string> The table row data matrix variable name.
666  \param tc <string> The table column identifier matrix variable name.
667  \param ri <string | value> The row identifier variable name or value.
668  \param ci <string | value> The column identifier variable name or value.
669  \param vri <boolean> The row identifier \p ri is a value.
670  \param vci <boolean> The column identifier \p ci is a value.
671  \param name <string> The getter function name.
672  \param append <boolean> Append id to names.
673  \param comment <integer> Output comment mode {0, 1, 2}.
674  \param verbose <boolean> Be verbose.
675 
676  \details
677 
678  Output getter functions for a table to the console. The resulting
679  functions can be used within scripts to access table data.
680 
681  \b Example
682  \code
683  table_dump_getters
684  (
685  r=my_config_tr, c=my_config_tc, tr="my_config_tr", tc="my_config_tc",
686  ri="my_config", vri=true, name="my_get_function", comment=2
687  );
688  \endcode
689 *******************************************************************************/
690 module table_dump_getters
691 (
692  r,
693  c,
694 
695  tr = "table_rows",
696  tc = "table_cols",
697 
698  ri = "ri",
699  ci = "ci",
700 
701  vri = false,
702  vci = false,
703 
704  name = "get_helper",
705  append = false,
706  comment = 0,
707  verbose = false
708 )
709 {
710  function qri(ri) = (ri == "ri") ? ri : (vri == true) ? ri : str("\"", ri, "\"");
711  function qci(ci) = (ci == "ci") ? ci : (vci == true) ? ci : str("\"", ci, "\"");
712  function gfn(fn) =
713  str
714  (
715  fn,
716  (ri == "ri")?"":(append?str("_", ri):""),
717  (ci == "ci")?"":(append?str("_", ci):"")
718  );
719 
720  //
721  // check table
722  //
723  echo();
724  echo("Checking table...");
725  table_check(r=r, c=c, verbose=verbose);
726 
727  if ( table_errors(r=r, c=c) == empty_lst )
728  {
729  //
730  // output helper function
731  //
732  echo();
733 
734  // getter function comment
735  gct =
736  str
737  (
738  "// table value getter function",
739  (ri == "ri")?"":str(", constant row ri=", qri(ri)),
740  (ci == "ci")?"":str(", constant column ci=", qci(ci)),
741  "."
742  );
743  if (comment > 0) echo (str(gct));
744 
745  // getter function
746  echo
747  (
748  str
749  (
750  "function ", gfn(name),
751  "(",
752  (ri == "ri")?"ri":"",
753  (ri == "ri") && (ci == "ci")?", ":"",
754  (ci == "ci")?"ci":"",
755  ") = table_get_value (r=", tr,
756  ", c=", tc,
757  ", ri=", qri(ri),
758  ", ci=", qci(ci),
759  ");"
760  )
761  );
762 
763  //
764  // output value functions
765  //
766  echo();
767 
768  // constant row (ri)
769  if ( (ri != "ri") && (ci == "ci") )
770  {
771  if ( vri || table_exists( r=r, c=c, ri=ri ) )
772  {
773  for ( i = table_get_column_ids (c=c) )
774  {
775  cic = table_get_column( c=c, ci=i );
776 
777  gct = str("// get ci=", qci(i), " (", second(cic), ") & ri=", qri(ri), ".");
778  if (comment == 1) echo (str(gct));
779 
780  echo
781  (
782  str
783  (
784  (append?str(ri, "_"):""), i, " = ",
785  gfn(name), "(ci=", qci(i), ");",
786  (comment == 2)?str("\t", gct):""
787  )
788  );
789  }
790  }
791  else
792  {
793  echo( str( "row ri=", qri(ri), " does not exist in table." ) );
794  }
795  }
796 
797  // constant col (ci)
798  else if ( (ri == "ri") && (ci != "ci") )
799  {
800  if ( vci || table_exists( r=r, c=c, ci=ci ) )
801  {
802  for ( i = table_get_row_ids (r=r) )
803  {
804  gct = str("// get ri=", qri(i), " & ci=", qci(ci), ".");
805  if (comment == 1) echo (str(gct));
806 
807  echo
808  (
809  str
810  (
811  i, (append?str("_", ci):""), " = ",
812  gfn(name), "(ri=", qri(i), ");",
813  (comment == 2)?str("\t", gct):""
814  )
815  );
816  }
817  }
818  else
819  {
820  echo( str( "column ci=", qci(ci), " does not exist in table." ) );
821  }
822  }
823 
824  // constant row and col (ri & ci)
825  else if ( (ri != "ri") && (ci != "ci") )
826  {
827  if ( vri || vci || table_exists( r=r, c=c, ri=ri, ci=ci ) )
828  { gct = str("// get ri=", qri(ri), " & ci=", qci(ci), ".");
829  if (comment == 1) echo (str(gct));
830 
831  echo
832  (
833  str
834  (
835  ri, "_", ci, " = ",
836  gfn(name), "(ri=", qri(ri), ", ci=", qci(ci), ");",
837  (comment == 2)?str("\t", gct):""
838  )
839  );
840  }
841  else
842  {
843  echo
844  (
845  str
846  (
847  "row ri=", qri(ri), ", column ci=", qci(ci),
848  " does not exist in table."
849  )
850  );
851  }
852  }
853  }
854  else
855  {
856  echo ( "Table has errors." );
857  }
858 }
859 
860 //! Write formatted map entries to the console.
861 /***************************************************************************//**
862  \param r <table> The table row data matrix (C-columns x R-rows).
863  \param c <map> The table column identifier matrix (2 x C-columns).
864  \param rs <string-list> A list of selected row identifiers.
865  \param cs <string-list> A list of selected column identifiers.
866  \param number <boolean> Number the rows.
867  \param heading_id <boolean> Output table heading identifiers.
868  \param heading_text <boolean> Output table heading description text.
869  \param fs <string> A field separator.
870  \param thn <string> Column heading for numbered row output.
871  \param index_tags <string-list> List of html formatting tags.
872  \param row_id_tags <string-list> List of html formatting tags.
873  \param value_tags <string-list> List of html formatting tags.
874 
875  \details
876 
877  Output each table row to the console. To output only select rows
878  and columns, assign the desired identifiers to \p rs and \p cs. For
879  example to output only the column identifiers 'c1' and 'c2', assign
880  <tt>cs = ["c1", "c2"]</tt>. The output can then be processed to
881  produce documentation tables as shown in the example below.
882 
883  \amu_define title (Table write)
884  \amu_define scope_id (example_table)
885 
886  \amu_define th_line (1)
887  \amu_define td_line_begin (2)
888  \amu_define td_line_end (9)
889  \amu_include (include/amu/scope_table.amu)
890 
891  \amu_define output_scad (false)
892  \amu_define output_console (false)
893  \amu_define th_line (10)
894  \amu_define td_line_begin (11)
895  \amu_define td_line_end (0)
896  \amu_include (include/amu/scope_table.amu)
897 *******************************************************************************/
898 module table_write
899 (
900  r,
901  c,
902  rs,
903  cs,
904  number = false,
905  heading_id = true,
906  heading_text = false,
907  fs = "^",
908  thn = "idx",
909  index_tags = empty_lst,
910  row_id_tags = ["b"],
911  value_tags = empty_lst
912 )
913 {
914  // heading identifiers and/or text descriptions
915  th_text =
916  [
917  if ( number == true )
918  thn,
919 
920  for ( c_iter = c )
921  if
922  ( // when column selected
923  is_undef( cs ) ||
924  is_number( first( search( c_iter, cs, 1, 0 ) ) )
925  )
926  (heading_id && heading_text) ? str(second(c_iter)," (", first(c_iter), ")")
927  : (heading_id ) ? first(c_iter)
928  : ( heading_text) ? second(c_iter)
929  : empty_str
930  ];
931 
932  if ( heading_id || heading_text )
933  // reformat so that 'fs' exists only between fields
934  log_echo ( strl([for ( i = headn(th_text) ) str(i,fs), last(th_text)]) );
935 
936  // row data
937  for ( r_iter = r )
938  {
939  if
940  ( // when row selected
941  is_undef( rs ) ||
942  is_number( first( search( r_iter, rs, 1, 0 ) ) )
943  )
944  {
945  tdr_text =
946  [
947  if (number == true)
948  str(strl_html([table_get_row_index(r, r_iter)], p=[index_tags]),fs),
949 
950  strl_html([first(r_iter)], p=[row_id_tags]), fs,
951  for ( c_iter = tailn(c, n=1) )
952  if
953  ( // when column selected
954  is_undef( cs ) ||
955  is_number( first( search( c_iter, cs, 1, 0 ) ) )
956  )
957  str(strl_html([table_get_value(r, c, r_iter, c_iter)], p=[value_tags]),fs)
958  ];
959 
960  log_echo ( strl( tdr_text ) );
961  }
962  }
963 }
964 
965 //! @}
966 
967 //----------------------------------------------------------------------------//
968 // Combined
969 //----------------------------------------------------------------------------//
970 
971 //! \name Functions: Combined parameters
972 //! @{
973 
974 /***************************************************************************//**
975  \param t <datastruct-list-2> A list [<table>, <map>], [r, c], of
976  the row data matrix (C-columns x R-rows) and column
977  identifier matrix (2 x C-columns).
978 
979  \copydoc table_get()
980 
981  \details
982 
983  \b Example
984  \code
985  rows = ctable_get( t, ri );
986  cols = ctable_get( t, ci=ci );
987  cell = ctable_get( t, ri, ci );
988  \endcode
989 *******************************************************************************/
990 function ctable_get( t, ri, ci ) = table_get( first(t), second(t), ri, ci );
991 
992 /***************************************************************************//**
993  \param t <datastruct-list-2> A list [<table>, <map>], [r, c], of
994  the row data matrix (C-columns x R-rows) and column
995  identifier matrix (2 x C-columns).
996 
997  \copydoc table_exists()
998 *******************************************************************************/
999 function ctable_exists( t, ri, ci ) = table_exists( first(t), second(t), ri, ci );
1000 
1001 /***************************************************************************//**
1002  \param t <datastruct-list-2> A list [<table>, <map>], [r, c], of
1003  the row data matrix (C-columns x R-rows) and column
1004  identifier matrix (2 x C-columns).
1005 
1006  \copydoc table_get_size()
1007 *******************************************************************************/
1008 function ctable_get_size( t ) = table_get_size( first(t), second(t) );
1009 
1010 /***************************************************************************//**
1011  \param t <datastruct-list-2> A list [<table>, <map>], [r, c], of
1012  the row data matrix (C-columns x R-rows) and column
1013  identifier matrix (2 x C-columns).
1014 
1015  \copydoc table_errors()
1016 *******************************************************************************/
1017 function ctable_errors( t ) = table_errors( first(t), second(t) );
1018 
1019 //! @}
1020 
1021 //! @}
1022 //! @}
1023 
1024 //----------------------------------------------------------------------------//
1025 // openscad-amu auxiliary scripts
1026 //----------------------------------------------------------------------------//
1027 
1028 /*
1029 BEGIN_SCOPE validate;
1030  BEGIN_OPENSCAD;
1031  include <omdl-base.scad>;
1032  include <common/validation.scad>;
1033 
1034  echo( str("openscad version ", version()) );
1035  for (i=[1:14]) echo( "not tested:" );
1036 
1037  // end_include
1038  END_OPENSCAD;
1039 
1040  BEGIN_MFSCRIPT;
1041  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1042  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1043  END_MFSCRIPT;
1044 END_SCOPE;
1045 */
1046 
1047 /*
1048 BEGIN_SCOPE example_use;
1049  BEGIN_OPENSCAD;
1050  include <omdl-base.scad>;
1051 
1052  base_unit_length = "mm";
1053 
1054  table_cols =
1055  [ // id, description
1056  ["id", "row identifier"],
1057  ["ht", "head type [r|h|s]"],
1058  ["td", "thread diameter"],
1059  ["tl", "thread length"],
1060  ["hd", "head diameter"],
1061  ["hl", "head length"],
1062  ["nd", "hex nut flat-to-flat width"],
1063  ["nl", "hex nut length"]
1064  ];
1065 
1066  table_rows =
1067  [ // id, ht, td, tl, hd, hl, nd, nl
1068  ["m3r08r", "r", 3.000, 8.00, 5.50, 3.000, 5.50, length(1.00, "in")],
1069  ["m3r14r", "r", 3.000, 14.00, 5.50, 3.000, 5.50, length(1.25, "in")],
1070  ["m3r16r", "r", 3.000, 16.00, 5.50, 3.000, 5.50, length(1.50, "in")],
1071  ["m3r20r", "r", 3.000, 20.00, 5.50, 3.000, 5.50, length(1.75, "in")]
1072  ];
1073 
1074  echo( "### table_check ###" );
1075  table_check( table_rows, table_cols, true );
1076 
1077  echo( "### table_dump ###" );
1078  table_dump( table_rows, table_cols );
1079 
1080  echo( "### table_get_value ###" );
1081  m3r16r_tl = table_get_value( table_rows, table_cols, "m3r16r", "tl" );
1082  echo ( m3r16r_tl=m3r16r_tl );
1083 
1084  echo( "### table_exists ###" );
1085  if ( table_exists( c=table_cols, ci="nl" ) )
1086  echo ( "metric 'nl' available" );
1087  else
1088  echo ( "metric 'nl' not available" );
1089 
1090  echo( "### table_get_row_ids ###" );
1091  table_ids = table_get_row_ids( table_rows );
1092  echo ( table_ids=table_ids );
1093 
1094  echo( "### table_get_columns 'tl' ###" );
1095  table_cols_tl = table_get_columns( table_rows, table_cols, "tl" );
1096  echo ( table_cols_tl=table_cols_tl );
1097 
1098  echo( "### table_get_copy ['tl, 'nl'] ###" );
1099  tnew = table_get_copy( table_rows, table_cols, cs=["tl", "nl"] );
1100  echo ( tnew=tnew );
1101 
1102  echo( "### table_get_sum ['tl, 'nl'] ###" );
1103  tsum = table_get_sum( table_rows, table_cols, cs=["tl", "nl"] );
1104  echo ( tsum=tsum );
1105 
1106  echo( "### table_dump_getters ###" );
1107  table_dump_getters( r=table_rows, c=table_cols,
1108  tr="table_rows", tc="table_cols",
1109  ri="my_config", vri=true, name="get_my_value", comment=2 );
1110 
1111  // end_include
1112  END_OPENSCAD;
1113 
1114  BEGIN_MFSCRIPT;
1115  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1116  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1117  END_MFSCRIPT;
1118 END_SCOPE;
1119 */
1120 
1121 /*
1122 BEGIN_SCOPE example_table;
1123  BEGIN_OPENSCAD;
1124  include <omdl-base.scad>;
1125 
1126  base_unit_length = "mm";
1127 
1128  table_cols =
1129  [ // id, description
1130  ["id", "row identifier"],
1131  ["ht", "head type"],
1132  ["td", "thread diameter"],
1133  ["tl", "thread length"],
1134  ["hd", "head diameter"],
1135  ["hl", "head length"],
1136  ["nd", "nut width"],
1137  ["nl", "nut length"]
1138  ];
1139 
1140  table_rows =
1141  [ // id, ht, td, tl, hd, hl, nd, nl
1142  ["m3r08r", "r", 3.000, 8.00, 5.50, 3.000, 5.50, length(1.00, "in")],
1143  ["m3r14r", "r", 3.000, 14.00, 5.50, 3.000, 5.50, length(1.25, "in")],
1144  ["m4r16s", "s", 4.000, 16.00, 4.50, 4.000, 5.50, length(1.50, "in")],
1145  ["m5r20h", "h", 5.000, 20.00, 6.00, 5.000, 5.50, length(1.75, "in")]
1146  ];
1147 
1148  map_write(table_cols, value_tags=["i"]);
1149  table_write(table_rows, table_cols);
1150 
1151  // end_include
1152  END_OPENSCAD;
1153 
1154  BEGIN_MFSCRIPT;
1155  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1156  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1157  END_MFSCRIPT;
1158 END_SCOPE;
1159 */
1160 
1161 //----------------------------------------------------------------------------//
1162 // end of file
1163 //----------------------------------------------------------------------------//
module log_warn(m)
Output warning message to console.
Definition: console.scad:333
module log_echo(m)
Output message to console.
Definition: console.scad:272
module log_info(m)
Output information message to console.
Definition: console.scad:318
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 headn(v, n=1)
Return a list containing all but the last n elements of an iterable value.
function last(v)
Return the last element of an iterable value.
function count(mv, v, s=true, i)
Count all occurrences of a match value in an iterable value.
function second(v)
Return the second element of an iterable value.
function first(v)
Return the first element of an iterable value.
function tailn(v, n=1)
Return a list containing all but the first n elements of an iterable value.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function strl(v)
Convert a list of values to a concatenated string.
function strl_html(v, b, p, a, f, d=false)
Convert a list of values to a concatenated HTML-formatted string.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function select_e(v, i, f, l)
Select each element at an index position of a list of iterable values.
function is_defined(v)
Test if a value is defined.
function is_number(v)
Test if a value is a number.
function ctable_exists(t, ri, ci)
Test the existence of a table row identifier, table column identifier, or both.
function table_get_size(r, c)
Get the size of a table.
function table_get_copy(r, c, rs, cs)
Create a new matrix from select rows and columns of a table.
function table_exists(r, c, ri, ci)
Test the existence of a table row identifier, table column identifier, or both.
$table_strict
<boolean> Enforce strict checking for table value references.
Definition: table.scad:471
module table_dump_getters(r, c, tr="table_rows", tc="table_cols", ri="ri", ci="ci", vri=false, vci=false, name="get_helper", append=false, comment=0, verbose=false)
Dump table getter functions to the console.
Definition: table.scad:1059
function table_get_columns(r, c, ci)
Form a list of a select column across all table rows.
function table_get_row(r, ri)
Get the table row that matches a table row identifier.
function table_get_row_index(r, ri)
Get the table row index that matches a table row identifier.
function table_get_column_index(c, ci)
Get the table column index that matches a table column identifier.
module table_check(r, c, verbose=false)
Perform basic format checks on a table and output errors to console.
Definition: table.scad:839
module table_write(r, c, rs, cs, number=false, heading_id=true, heading_text=false, fs="^", thn="idx", index_tags=empty_lst, row_id_tags=["b"], value_tags=empty_lst)
Write formatted map entries to the console.
Definition: table.scad:1475
function table_get_sum(r, c, rs, cs)
Sum select rows and columns of a table.
function table_get_row_ids(r)
Form a list of all table row identifiers.
function ctable_get_size(t)
Get the size of a table.
function table_get_column_ids(c)
Form a list of all table column identifiers.
module table_dump(r, c, rs, cs, number=true, align=true)
Dump a table to the console.
Definition: table.scad:919
function table_get(r, c, ri, ci)
Get a row, a column, or a specific cell value from a table.
function table_errors(r, c)
Perform basic format checks on a table and return errors.
function ctable_errors(t)
Perform basic format checks on a table and return errors.
function ctable_get(t, ri, ci)
Get a row, a column, or a specific cell value from a table.
function table_get_column(c, ci)
Get the table column that matches a table column identifier.
function table_get_value(r, c, ri, ci)
Get the table cell value for a specified row and column identifier.