omdl  v1.0
OpenSCAD Mechanical Design Library
primitive.scad
Go to the documentation of this file.
1 //! Drafting: base functions and primitives.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2019,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  [conventions]: \ref tools_2d_drafting_conventions
45  )
46 *******************************************************************************/
47 
48 // sub-group documentation and conventions
49 /***************************************************************************//**
50  /+
51  \addtogroup \amu_eval(${parent})
52  \details
53  +/
54 *******************************************************************************/
55 
56 //----------------------------------------------------------------------------//
57 // members
58 //----------------------------------------------------------------------------//
59 
60 //----------------------------------------------------------------------------//
61 // primitives: layers
62 //----------------------------------------------------------------------------//
63 
64 //! \name Primitives: Layers
65 //! @{
66 
67 //! Check if any identified layers are active.
68 /***************************************************************************//**
69  \param layers <string-list> The list of drafting layer names to
70  render this object on. Defaults to the "default"; the
71  general-purpose layer. See \ref draft_layers_show and the
72  layer [conventions].
73 
74  \returns <boolean> \b true if any identified layer is active as
75  indicated by \ref draft_layers_show.
76 
77  \amu_eval(${group_references})
78  \private
79 *******************************************************************************/
80 function _draft_layers_any_active
81 (
82  layers = _draft_get_config("layers-default")
83 ) = exists( is_list(layers) ? layers : [layers], draft_layers_show, true );
84 
85 //! @}
86 
87 //----------------------------------------------------------------------------//
88 // primitives: placement
89 //----------------------------------------------------------------------------//
90 
91 //! \name Primitives: Placement
92 //! @{
93 
94 //! Get sheet, sheet-frame, or sheet-zone reference window or limits.
95 /***************************************************************************//**
96  \param rx <string> A sheet x-axis zone reference identifier.
97  \param ry <string> A sheet y-axis zone reference identifier.
98 
99  \param ix <integer> A sheet x-axis zone reference index.
100  \param iy <integer> A sheet x-axis zone reference index.
101 
102  \param limits <boolean> Return window limits rather than coordinates.
103  \param frame <boolean> Use frame when zone not specified.
104 
105  \returns <datastruct> The reference window.
106 
107  \details
108 
109  The returned datastruct will be one of the following forms:
110 
111  limits | data type | description
112  :-------:|:---------------------:|:-----------------------------
113  true | decimal-list-2-list-2 | [[xmin, xmax], [ymin, ymax]]
114  false | point-2d-list-4 | [p0, p1, p2, p3], pn=[x,y]
115 
116  The windows coordinate points [p0, p1, p2, p3] are clockwise
117  ordered with p0=[xmin, ymin].
118 
119  \private
120 *******************************************************************************/
121 function _draft_sheet_get_window
122 (
123  rx,
124  ry,
125  ix,
126  iy,
127  limits = false,
128  frame = false
129 ) =
130  let
131  (
132  //
133  // get configuration values (scale all lengths)
134  //
135 
136  // sheet size
137  sdx = _draft_get_sheet_size(ci="sdx") * draft_sheet_scale,
138  sdy = _draft_get_sheet_size(ci="sdy") * draft_sheet_scale,
139 
140  // sheet layout
141  sll = _draft_get_sheet_config(ci="sll"),
142 
143  // sheet frame and zone margins
144  smx = _draft_get_sheet_config(ci="smx") * draft_sheet_scale,
145  smy = _draft_get_sheet_config(ci="smy") * draft_sheet_scale,
146  szm = _draft_get_sheet_config(ci="szm") * draft_sheet_scale,
147 
148  // reference zone labels
149  zox = _draft_get_sheet_config(ci="zox"),
150  zoy = _draft_get_sheet_config(ci="zoy"),
151  zlx = _draft_get_sheet_config(ci="zlx"),
152  zly = _draft_get_sheet_config(ci="zly"),
153 
154  // sheet layout dimensions
155  ldx = sll ? sdy : sdx,
156  ldy = sll ? sdx : sdy,
157 
158  // sheet frame dimensions
159  fdx = ldx - smx,
160  fdy = ldy - smy,
161 
162  // working dimensions excluding reference zone
163  wdx = fdx - szm * 2,
164  wdy = fdy - szm * 2,
165 
166  // zone start coordinates
167  zxs = is_defined(ix) ?
168  let
169  ( // reference ordering
170  iox = (zox > 0) ? ix : len(zlx)-1-ix
171  )
172  iox * fdx/len(zlx) -fdx/2
173  : is_defined(rx) ?
174  let
175  ( // assign '-1', outside of frame, if not found
176  zxi = defined_or(first(find(rx, zlx)), -1),
177  iox = (zox > 0) ? zxi : len(zlx)-1-zxi
178  )
179  iox * fdx/len(zlx) -fdx/2
180  : 0,
181  zys = is_defined(iy) ?
182  let
183  ( // reference ordering
184  ioy = (zoy > 0) ? iy : len(zly)-1-iy
185  )
186  ioy * fdy/len(zly) -fdy/2
187  : is_defined(ry) ?
188  let
189  ( // assign '-1', outside of frame, if not found
190  zyi = defined_or(first(find(ry, zly)), -1),
191  ioy = (zoy > 0) ? zyi : len(zly)-1-zyi
192  )
193  ioy * fdy/len(zly) - fdy/2
194  : 0,
195 
196  // reference window coordinates
197  wx = is_defined(ix) || is_defined(rx) ?
198  [zxs, zxs + fdx/len(zlx)]
199  : frame ?
200  [-fdx/2, fdx/2]
201  : [-wdx/2, wdx/2],
202  wy = is_defined(iy) || is_defined(ry) ?
203  [zys, zys + fdy/len(zly)]
204  : frame ?
205  [-fdy/2, fdy/2]
206  : [-wdy/2, wdy/2],
207 
208  // limits: [[xmin, xmax], [ymin, ymax]
209  window_limits = [wx, wy],
210 
211  // window points in cw order from [xmin, ymin]
212  window_coords = [ [wx[0],wy[0]], [wx[0],wy[1]], [wx[1],wy[1]], [wx[1],wy[0]] ]
213  )
214  limits ? window_limits : window_coords;
215 
216 //! Get sheet, sheet-frame, or sheet-zone reference coordinates.
217 /***************************************************************************//**
218  \param rx <string-list | string> Sheet x-axis zone reference identifier(s).
219  \param ry <string-list | string> Sheet y-axis zone reference identifier(s).
220 
221  \param ix <integer-list | integer> Sheet x-axis zone reference index(es).
222  \param iy <integer-list | integer> Sheet y-axis zone reference index(es).
223 
224  \param zp <integer-list-2 | integer> The window coordinate scaler. A
225  list [zpx, zpy] of decimals or a single decimal for (zpx=zpy).
226 
227  \param window <boolean> Return window rather than point.
228  \param frame <boolean> Use frame when zone not specified.
229 
230  \returns <datastruct> The reference coordinates.
231 
232  \details
233 
234  The returned datastruct will be one of the following forms:
235 
236  window | data type | description
237  :-------:|:---------------:|:---------------------------
238  true | point-2d-list-4 | [p0, p1, p2, p3], pn=[x,y]
239  false | point-2d | [x, y]
240 
241  The parameter \p zp is used to linearly scale the window
242  coordinate. For both axes, \b -1 = left/bottom, \b 0 =
243  center/middle, and \b +1 = right/top. The windows coordinate points
244  [p0, p1, p2, p3] are clockwise ordered with p0=[xmin, ymin].
245 
246  \private
247 *******************************************************************************/
248 function _draft_sheet_get_zone
249 (
250  rx,
251  ry,
252  ix,
253  iy,
254  zp = 0,
255  window = false,
256  frame = false
257 ) =
258  let
259  (
260  // get reference window xy-limits
261  wl = !is_list(rx) && !is_list(ix) && !is_list(ry) && !is_list(iy) ?
262  // no reference lists, just get single zone window
263  _draft_sheet_get_window( rx=rx, ry=ry, ix=ix, iy=iy, frame=frame, limits=true )
264  : let
265  (
266  // determine x-limits
267  wx = is_list(ix) ?
268  let
269  (
270  w1 = _draft_sheet_get_window( ix=first(ix), frame=frame, limits=true ),
271  w2 = _draft_sheet_get_window( ix=last(ix), frame=frame, limits=true )
272  )
273  [min(w1[0][0], w2[0][0]), max(w1[0][1], w2[0][1])]
274  : is_list(rx) ?
275  let
276  (
277  w1 = _draft_sheet_get_window( rx=first(rx), frame=frame, limits=true ),
278  w2 = _draft_sheet_get_window( rx=last(rx), frame=frame, limits=true )
279  )
280  [min(w1[0][0], w2[0][0]), max(w1[0][1], w2[0][1])]
281  : let
282  (
283  w1 = _draft_sheet_get_window( rx=rx, ix=ix, frame=frame, limits=true )
284  )
285  w1[0],
286 
287  // determine y-limits
288  wy = is_list(iy) ?
289  let
290  (
291  w1 = _draft_sheet_get_window( iy=first(iy), frame=frame, limits=true ),
292  w2 = _draft_sheet_get_window( iy=last(iy), frame=frame, limits=true )
293  )
294  [min(w1[1][0], w2[1][0]), max(w1[1][1], w2[1][1])]
295  : is_list(ry) ?
296  let
297  (
298  w1 = _draft_sheet_get_window( ry=first(ry), frame=frame, limits=true ),
299  w2 = _draft_sheet_get_window( ry=last(ry), frame=frame, limits=true )
300  )
301  [min(w1[1][0], w2[1][0]), max(w1[1][1], w2[1][1])]
302  : let
303  (
304  w1 = _draft_sheet_get_window( ry=ry, iy=iy, frame=frame, limits=true )
305  )
306  w1[1]
307  )
308  // xy-limits
309  [wx, wy]
310  )
311  window ?
312  // window points in cw order from [xmin, ymin]
313  [
314  [wl[0][0], wl[1][0]],
315  [wl[0][0], wl[1][1]],
316  [wl[0][1], wl[1][1]],
317  [wl[0][1], wl[1][0]]
318  ]
319  : let
320  (
321  // linearly scale window by px, py
322  // [-1]=left/bottom, [0]=center/middle, [+1]=right/top]
323  px = defined_e_or(zp, 0, zp),
324  py = defined_e_or(zp, 1, px),
325 
326  cx = ( px * (wl[0][1]-wl[0][0]) + (wl[0][0]+wl[0][1]) )/2,
327  cy = ( py * (wl[1][1]-wl[1][0]) + (wl[1][0]+wl[1][1]) )/2
328  )
329  // point
330  [cx, cy];
331 
332 //! @}
333 
334 //----------------------------------------------------------------------------//
335 // primitives: tables
336 //----------------------------------------------------------------------------//
337 
338 //! \name Primitives: Tables
339 //! @{
340 
341 //! Get a coordinate point for a defined draft table column and row.
342 /***************************************************************************//**
343  \param ix <integer> A table column vertical line index.
344  \param iy <integer> A table row horizontal line index.
345 
346  \param map <map> A table definition map.
347  \param fmap <map> A table format map.
348 
349  \returns <point-2d> The table column and row intersection coordinate
350  point.
351 
352  \private
353 *******************************************************************************/
354 function _draft_table_get_point
355 (
356  ix,
357  iy,
358  map,
359  fmap
360 ) =
361  let
362  (
363  // get table format
364  cmh = map_get_firstof2_or(map, fmap, "cmh", _draft_get_config("table-cmh")) * $draft_scale,
365  cmv = map_get_firstof2_or(map, fmap, "cmv", _draft_get_config("table-cmv")) * $draft_scale,
366 
367  coh = map_get_firstof2_or(map, fmap, "coh", _draft_get_config("table-coh")),
368  cov = map_get_firstof2_or(map, fmap, "cov", _draft_get_config("table-cov")),
369 
370  // get table data
371  title = map_get_value(map, "title"),
372  heads = map_get_value(map, "heads"),
373  cols = map_get_value(map, "cols"),
374  rows = map_get_value(map, "rows"),
375 
376  // vertical line index
377  xu = is_undef(ix) ? 0 // not specified
378  : (ix <= 0) ? 0 // left
379  : sum([for( i=[1:ix] ) cols[i-1]]), // sum column units widths
380 
381  yt = defined_e_or(title, 1, 0), // title: '0' unit default height
382  yh = defined_e_or(heads, 1, 0), // heads: '0' unit default height
383 
384  // horizontal line index
385  yu = is_undef(iy) ? 0 // not specified
386  : (iy <= 0) ? 0 // top
387  : (iy == 1) ? yt // title
388  : (iy == 2) ? sum( [yt, yh] ) // title + heads
389  : sum
390  (
391  [
392  yt, yh, // rows: '1' unit default height
393  for( i=[3:iy] ) defined_e_or(rows[i-3], 1, 1)
394  ]
395  )
396  )
397  [xu*cmh*coh, yu*cmv*cov]; // units * unit-size * order
398 
399 //! Get table cell coordinates given a column and row.
400 /***************************************************************************//**
401  \param ix <integer> A table column vertical line index.
402  \param iy <integer> A table row horizontal line index.
403 
404  \param zp <integer-list-2 | integer> The cell coordinate scaler. A
405  list [zpx, zpy] of decimals or a single decimal for (zpx=zpy).
406 
407  \param limits <boolean> Return cell limits rather than coordinates.
408  \param window <boolean> Return cell window rather than point.
409 
410  \param map <map> A table definition map.
411  \param fmap <map> A table format map.
412 
413  \returns <datastruct> The table cell coordinates.
414 
415  \details
416 
417  The returned datastruct will be one of the following forms:
418 
419  limits | window | data type | description
420  :-------:|:------:|:---------------------:|:-----------------------------
421  true | - | decimal-list-2-list-2 | [[xmin, xmax], [ymin, ymax]]
422  false | true | point-2d-list-4 | [p0, p1, p2, p3], pn=[x,y]
423  false | false | point-2d | [x, y]
424 
425  The parameter \p zp is used to linearly scale the window
426  coordinate. For both axes, \b -1 = left/bottom, \b 0 =
427  center/middle, and \b +1 = right/top. The windows coordinate points
428  [p0, p1, p2, p3] are clockwise ordered with p0=[xmin, ymin].
429 
430  \private
431 *******************************************************************************/
432 function _draft_table_get_cell
433 (
434  ix,
435  iy,
436  zp = 0,
437  limits = false,
438  window = false,
439  map,
440  fmap
441 ) =
442  let
443  (
444  // get table data
445  cols = map_get_value(map, "cols"),
446  rows = map_get_value(map, "rows"),
447 
448  // vertical lines
449  lv = is_undef(ix) ? [0, len(cols)] // table width
450  : (iy <= 0) ? [0, len(cols)] // title cell
451  : [ix, ix+1], // heading or row
452 
453  // horizontal lines
454  lh = is_undef(iy) ? [0, len(rows)+2] // table height
455  : [iy, iy+1], // any row
456 
457  // get cell window xy-limits [min, max]
458  wl =
459  [
460  [ for( i=[0, 1] ) _draft_table_get_point( ix=lv[i], map=map, fmap=fmap )[0] ],
461  [ for( i=[0, 1] ) _draft_table_get_point( iy=lh[i], map=map, fmap=fmap )[1] ]
462  ]
463  )
464  limits ?
465  // limits: [[xmin, xmax], [ymin, ymax]
466  wl
467  : window ?
468  // window points in cw order from [xmin, ymin]
469  [
470  [wl[0][0], wl[1][0]],
471  [wl[0][0], wl[1][1]],
472  [wl[0][1], wl[1][1]],
473  [wl[0][1], wl[1][0]]
474  ]
475  : let
476  (
477  // linearly scale window by px, py
478  // [-1]=left/bottom, [0]=center/middle, [+1]=right/top]
479  px = defined_e_or(zp, 0, zp),
480  py = defined_e_or(zp, 1, px),
481 
482  cx = ( px * (wl[0][1]-wl[0][0]) + (wl[0][0]+wl[0][1]) )/2,
483  cy = ( py * (wl[1][1]-wl[1][0]) + (wl[1][0]+wl[1][1]) )/2
484  )
485  // point
486  [cx, cy];
487 
488 //! Add text to table cell at a given a column and row.
489 /***************************************************************************//**
490  \param ix <integer> A table column vertical line index.
491  \param iy <integer> A table row horizontal line index.
492 
493  \param text <string | string-list> The text to add. A single string
494  or a list of strings for multiple line text.
495  \param size <decimal> The text size.
496 
497  \param dfmt <datastruct> The default text format.
498 
499  \param map <map> A table definition map.
500  \param fmap <map> A table format map.
501 
502  \details
503 
504  The parameter \p dfmt of type <datastruct> is a list of eleven
505  values:
506 
507  \verbatim
508  <datastruct> =
509  [
510  0:text, 1:<align-point>, 2:<align-offset>, 3:<multi-line-offset>,
511  4:rotate, 5:text-scale, 6:<text-align>, 7:font,
512  8:spacing, 9:direction, 10:language, 11:script
513  ]
514  \endverbatim
515 
516  field | data type | description
517  :------:|:--------------:|:---------------------
518  0 | string | text
519  1 | decimal-list-2 | [h-align, v-align]
520  2 | decimal-list-2 | [h-offset, v-offset]
521  3 | decimal-list-2 | [h-offset, v-offset]
522  4 | decimal | rotate
523  5 | decimal | text-scale
524  6 | string-list-2 | [h-align, v-align]
525  7 | string | font
526  8 | decimal | spacing
527  9 | string | direction
528  10 | string | language
529  11 | string | script
530 
531  \b Example
532  \code{.C}
533  dfmt = [empty_str, [-1,-1], [2/5,-9/10], [0,-1-1/5], 0, 1, ["left", "center"]]];
534  \endcode
535 
536  Unassigned fields are initialized with defaults.
537 
538  \private
539 *******************************************************************************/
540 module _draft_table_text
541 (
542  ix,
543  iy,
544  text,
545  size,
546  dfmt,
547  map,
548  fmap
549 )
550 { // element-wise default assignment
551  function erdefined_or (v, r, d) = [for (i = r) defined_or( v[i], d[i] )];
552 
553  /*
554  handled-input-cases tv(text) sf(text)
555  (1) "t1" ["t1"] [ "t1" ]
556  (2) [ "t1" ] ["t1"] [ "t1" ]
557  (3) [ "t1", "t2" ] ["t1", "t2"] [ ["t1", "t2"] ]
558  (4) [ ["t1", "t2"] ] ["t1", "t2"] [ ["t1", "t2"] ]
559  (5) [ ["t1", "t2"], [0,0] ] ["t1", "t2"] [ ["t1", "t2"], [0,0] ]
560  (6) [ "t1", [0,0] ] ["t1"] [ "t1", [0,0] ]
561  */
562 
563  // text format
564  sf = is_string(text) ? [text] // (1)
565  : all_strings(text) ? [text] // (2), (3)
566  : text; // (4), (5), (6)
567 
568  // text list
569  tv = is_string(text) ? [text] // (1)
570  : all_strings(text) ? text // (2), (3)
571  : is_list(text[0]) ? text[0] // (4), (5)
572  : [text[0]]; // (6)
573 
574  // assign defaults where needed
575  df = erdefined_or( sf, [0:11], dfmt );
576 
577  for ( l=[0:len(tv)-1] )
578  translate
579  (
580  // cell coordinates
581  _draft_table_get_cell( ix=ix, iy=iy, zp=df[1], map=map, fmap=fmap )
582  // configured offset
583  + [ df[2][0], df[2][1] ] * size * df[5]
584  // multi-line offset
585  + df[3] * size * df[5] * l
586  )
587  rotate( df[4] )
588  text
589  (
590  text=tv[l],
591  size=df[5]*size,
592  halign=df[6][0],
593  valign=df[6][1],
594  font=df[7],
595  spacing=df[8],
596  direction=df[9],
597  language=df[10],
598  script=df[11]
599  );
600 }
601 
602 //! Get a coordinate point for a defined draft zoned-table column and row.
603 /***************************************************************************//**
604  \param ix <integer> A ztable column vertical line index.
605  \param iy <integer> A ztable row horizontal line index.
606 
607  \param map <map> A ztable definition map.
608 
609  \returns <point-2d> The ztable column and row intersection coordinate
610  point.
611 
612  \private
613 *******************************************************************************/
614 function _draft_ztable_get_point
615 (
616  ix,
617  iy,
618  map
619 ) =
620  let
621  (
622  // get table configuration
623  cmh = map_get_value(map, "cmh") * $draft_scale,
624  cmv = map_get_value(map, "cmv") * $draft_scale,
625 
626  coh = map_get_value(map, "coh"),
627  cov = map_get_value(map, "cov"),
628 
629  vlines = map_get_value(map, "vlines"),
630  hlines = map_get_value(map, "hlines"),
631 
632  x = is_undef(ix) ? 0 : sum([for( i=[0:ix] ) vlines[i][0]]) * cmh * coh,
633  y = is_undef(iy) ? 0 : sum([for( i=[0:iy] ) hlines[i][0]]) * cmv * cov
634  )
635  [x, y];
636 
637 //! Get ztable zone coordinates given a zone index.
638 /***************************************************************************//**
639  \param i <integer> A ztable zone index.
640 
641  \param zp <integer-list-2 | integer> The zone coordinate scaler. A
642  list [zpx, zpy] of decimals or a single decimal for (zpx=zpy).
643 
644  \param limits <boolean> Return zone limits rather than coordinates.
645  \param window <boolean> Return zone window rather than point.
646 
647  \param map <map> A ztable definition map.
648 
649  \returns <datastruct> The ztable cell coordinates.
650 
651  \details
652 
653  The returned datastruct will be one of the following forms:
654 
655  limits | window | data type | description
656  :-------:|:------:|:---------------------:|:-----------------------------
657  true | - | decimal-list-2-list-2 | [[xmin, xmax], [ymin, ymax]]
658  false | true | point-2d-list-4 | [p0, p1, p2, p3], pn=[x,y]
659  false | false | point-2d | [x, y]
660 
661  The parameter \p zp is used to linearly scale the window
662  coordinate. For both axes, \b -1 = left/bottom, \b 0 =
663  center/middle, and \b +1 = right/top. The windows coordinate points
664  [p0, p1, p2, p3] are clockwise ordered with p0=[xmin, ymin].
665 
666  \private
667 *******************************************************************************/
668 function _draft_ztable_get_zone
669 (
670  i,
671  zp = 0,
672  limits = false,
673  window = false,
674  map
675 ) =
676  let
677  (
678  // get table configuration
679  zones = map_get_value(map, "zones"),
680 
681  // [min vline, max vline]
682  zx = is_undef(i) ? [0, len(map_get_value(map, "vlines"))-1] : zones[i][0],
683 
684  // [min hline, max hline]
685  zy = is_undef(i) ? [0, len(map_get_value(map, "hlines"))-1] : zones[i][1],
686 
687  // get zone window xy-limits [min, max]
688  wl =
689  [
690  [ for( i=[1, 0] ) _draft_ztable_get_point( ix=zx[i], map=map )[0] ],
691  [ for( i=[0, 1] ) _draft_ztable_get_point( iy=zy[i], map=map )[1] ]
692  ]
693  )
694  limits ?
695  // limits: [[xmin, xmax], [ymin, ymax]
696  wl
697  : window ?
698  // window points in cw order from [xmin, ymin]
699  [
700  [wl[0][0], wl[1][0]],
701  [wl[0][0], wl[1][1]],
702  [wl[0][1], wl[1][1]],
703  [wl[0][1], wl[1][0]]
704  ]
705  : let
706  (
707  // linearly scale window by px, py
708  // [-1]=left/bottom, [0]=center/middle, [+1]=right/top]
709  px = defined_e_or(zp, 0, zp),
710  py = defined_e_or(zp, 1, px),
711 
712  cx = ( px * (wl[0][1]-wl[0][0]) + (wl[0][0]+wl[0][1]) )/2,
713  cy = ( py * (wl[1][1]-wl[1][0]) + (wl[1][0]+wl[1][1]) )/2
714  )
715  // point
716  [cx, cy];
717 
718 //! Add text to ztable at a given zone index.
719 /***************************************************************************//**
720  \param i <integer> A ztable zone index.
721 
722  \param text <string | string-list> The text to add. A single string
723  or a list of strings for multiple line text.
724  \param size <decimal> The text size.
725 
726  \param fmt <datastruct> The text format.
727  \param dfmt <datastruct> The default text format.
728 
729  \param map <map> A ztable definition map.
730 
731  \details
732 
733  The parameters \p fmt and \p dfmt of type <datastruct> is a list of
734  eleven values:
735 
736  \verbatim
737  <datastruct> =
738  [
739  0:text, 1:<align-point>, 2:<align-offset>, 3:<multi-line-offset>,
740  4:rotate, 5:text-scale, 6:<text-align>, 7:font,
741  8:spacing, 9:direction, 10:language, 11:script
742  ]
743  \endverbatim
744 
745  field | data type | description
746  :------:|:--------------:|:---------------------
747  0 | string | text
748  1 | decimal-list-2 | [h-align, v-align]
749  2 | decimal-list-2 | [h-offset, v-offset]
750  3 | decimal-list-2 | [h-offset, v-offset]
751  4 | decimal | rotate
752  5 | decimal | text-scale
753  6 | string-list-2 | [h-align, v-align]
754  7 | string | font
755  8 | decimal | spacing
756  9 | string | direction
757  10 | string | language
758  11 | string | script
759 
760  \b Example
761  \code{.C}
762  dfmt = [empty_str, [-1,-1], [2/5,-9/10], [0,-1-1/5], 0, 1, ["left", "center"]]];
763  \endcode
764 
765  Unassigned fields are initialized with defaults.
766 
767  \private
768 *******************************************************************************/
769 module _draft_ztable_text
770 (
771  i,
772  text,
773  size,
774  fmt,
775  dfmt,
776  map
777 )
778 { // element-wise default assignment
779  function erdefined_or (v, r, d) = [for (i = r) defined_or( v[i], d[i] )];
780 
781  // multi- or single-line
782  tv = is_list(text) ? text : [text];
783 
784  // assign defaults where needed
785  df = erdefined_or( fmt, [0:11], dfmt );
786 
787  for ( l=[0:len(tv)-1] )
788  translate
789  (
790  // zone coordinates
791  _draft_ztable_get_zone( i=i, zp=df[1], map=map )
792  // configured offset
793  + [ df[2][0], df[2][1] ] * size * df[5]
794  // multi-line offset
795  + df[3] * size * df[5] * l
796  )
797  rotate( df[4] )
798  text
799  (
800  text=tv[l],
801  size=df[5]*size,
802  halign=df[6][0],
803  valign=df[6][1],
804  font=df[7],
805  spacing=df[8],
806  direction=df[9],
807  language=df[10],
808  script=df[11]
809  );
810 }
811 
812 //! @}
813 
814 //----------------------------------------------------------------------------//
815 // primitives: shapes
816 //----------------------------------------------------------------------------//
817 
818 //! \name Shape Primitives
819 //! @{
820 
821 //! Draft a simple line from an initial to a terminal point.
822 /***************************************************************************//**
823  \param i <point-2d> The initial point coordinate [x, y].
824  \param t <point-2d> The terminal point coordinate [x, y].
825  \param w <decimal> The line weight.
826 
827  \details
828 
829  \ref $draft_line_fn sets arc fragment number for line construction.
830 
831  | see: \ref draft_config_map |
832  |:---------------------------:|
833  | line-width-min |
834  | line-use-hull |
835 *******************************************************************************/
836 module draft_line_pp
837 (
838  i,
839  t,
840  w = 1
841 )
842 {
843  $fn = $draft_line_fn;
844  p = _draft_get_config("line-width-min") * w * $draft_scale;
845 
846  if ( _draft_get_config("line-use-hull") )
847  {
848  // hulled end-circles
849  hull() { translate(i) circle(d=p); translate(t) circle(d=p); }
850  }
851  else
852  {
853  // rectangle line
854  align_ll(r=[i, t], rp=2, l=y_axis3d_ul)
855  square([p, distance_pp(i, t)], center=true);
856 
857  // add rounded ends
858  // translate(i) circle(d=p); translate(t) circle(d=p);
859  }
860 }
861 
862 //! Draft an arrowhead at the terminal point of a line.
863 /***************************************************************************//**
864  \param l <line> A line or vector.
865  \param w <decimal> The line segment weight.
866  \param s <integer | integer-list-5> The arrowhead style.
867 
868  \details
869 
870  The style can be customize via the following optional parameter
871  list fields.
872 
873  field | data type | default | description
874  :------:|:-------------------:|:-------------------:|:------------------------------
875  0 | integer | 0 | style
876  1 | integer \| boolean | 0,1 or true,false | fill
877  2 | integer | 0 | side: 0=both, 1=left, 2=right
878  3 | decimal | 1 | length multiplier
879  4 | decimal | 1 | angle multiplier
880 
881  When parameter \p s is assigned a single integer it sets the style
882  and the other fields are assigned their default values.
883 
884  style | description
885  :-----:|:-------------------------
886  0 | no arrowhead
887  1 | closed 3-point arrowhead
888  2 | closed 4-point arrowhead
889  3 | open 3-point arrowhead
890  4 | slash / cross arrowhead
891  5 | circle arrowhead
892 
893  \amu_eval ( object=draft_arrow ${object_diagram_2d} )
894 
895  \ref $draft_arrow_fn sets arc fragment number for arrowhead
896  construction. The line segments are constructed by draft_line_pp().
897 
898  | see: \ref draft_config_map |
899  |:---------------------------:|
900  | arrow-line-length-min |
901  | arrow-angle-min |
902 *******************************************************************************/
903 module draft_arrow
904 (
905  l = x_axis2d_ul,
906  w = 1,
907  s = 1
908 )
909 {
910  s1 = defined_e_or(s, 0, s); // arrow selection
911 
912  if ( s1 )
913  {
914  s2 = defined_e_or(s, 1, 0); // fill [0:1, t:f]
915  s3 = defined_e_or(s, 2, 0); // sections [0:all,1:left,2:right]
916 
917  s4 = defined_e_or(s, 3, 1); // length multiplier
918  s5 = defined_e_or(s, 4, 1); // angle multiplier
919 
920 
921  al = _draft_get_config("arrow-line-length-min") * s4 * $draft_scale;
922  // length
923 
924  ca = _draft_get_config("arrow-angle-min") * s5;
925  // cut angle
926 
927  alx = angle_ll(x_axis2d_uv, l, true); // line angle
928  aa1 = alx+180-ca; // angle a1
929  aa2 = alx-180+ca; // angle a2
930 
931  pah = line_tp(l); // head point
932 
933  ls1 = line2d_new(m=al, a=aa1, p1=pah); // side line1
934  ls2 = line2d_new(m=al, a=aa2, p1=pah); // side line2
935 
936  ps1 = line_tp(ls1); // line1 end point
937  ps2 = line_tp(ls2); // line2 end point
938 
939  ptm = (ps1 + ps2)/2; // tail mid point
940  pam = (ptm + pah)/2; // middle point
941 
942  // update fn for arrow line construction
944 
945  if ( s1 == 1 )
946  { // closed 3-point
947  hull_cs( !s2 )
948  {
949  draft_line_pp(pah, ptm, w=w);
950  if (binary_bit_is(s3, 0, 0))
951  { draft_line_pp(ls1[0], ls1[1], w=w); draft_line_pp(ps1, ptm, w=w); }
952  if (binary_bit_is(s3, 1, 0))
953  { draft_line_pp(ls2[0], ls2[1], w=w); draft_line_pp(ps2, ptm, w=w); }
954  }
955  }
956  else if ( s1 == 2 )
957  { // closed 4-point
958  hull_cs( !s2 )
959  {
960  draft_line_pp(pah, pam, w=w);
961  if (binary_bit_is(s3, 0, 0))
962  { draft_line_pp(ls1[0], ls1[1], w=w); draft_line_pp(ps1, pam, w=w); }
963  }
964  hull_cs( !s2 )
965  {
966  draft_line_pp(pah, pam, w=w);
967  if (binary_bit_is(s3, 1, 0))
968  { draft_line_pp(ls2[0], ls2[1], w=w); draft_line_pp(ps2, pam, w=w); }
969  }
970  }
971  else if ( s1 == 3 )
972  { // open 3-point
973  if (binary_bit_is(s3, 0, 0))
974  draft_line_pp(ls1[0], ls1[1], w=w);
975  if (binary_bit_is(s3, 1, 0))
976  draft_line_pp(ls2[0], ls2[1], w=w);
977  }
978  else if ( s1 == 4 )
979  { // slash / cross
980  if (binary_bit_is(s3+1, 0, 1))
981  {
982  xl1 = line2d_new(m=+al/3*2, a=alx+180-ca*2, p1=pah);
983  xl2 = line2d_new(m=-al/3*2, a=alx+180-ca*2, p1=pah);
984 
985  draft_line_pp(xl1[0], xl1[1], w=w);
986  draft_line_pp(xl2[0], xl2[1], w=w);
987  }
988  if (binary_bit_is(s3+1, 1, 1))
989  {
990  xl1 = line2d_new(m=+al/3*2, a=alx-180+ca*2, p1=pah);
991  xl2 = line2d_new(m=-al/3*2, a=alx-180+ca*2, p1=pah);
992 
993  draft_line_pp(xl1[0], xl1[1], w=w);
994  draft_line_pp(xl2[0], xl2[1], w=w);
995  }
996  }
997  else if ( s1 == 5 )
998  { // circle
999  hull_cs( !s2 )
1000  for ( ls = sequence_ns( polygon_arc_sweep_p( r=al/3, o=pah, fn=$draft_arrow_fn ), 2, 1 ) )
1001  draft_line_pp(ls[0], ls[1], w=w);
1002  }
1003  }
1004 }
1005 
1006 //! Draft a line with configurable style and optional arrowheads.
1007 /***************************************************************************//**
1008  \param l <line> A line or vector.
1009  \param w <decimal> The line weight.
1010  \param s <integer | integer-list> The line style.
1011  \param a1 <integer | integer-list-5> The arrowhead style at initial point.
1012  \param a2 <integer | integer-list-5> The arrowhead style at terminal point.
1013 
1014  \details
1015 
1016  When parameter \p s is assigned a single integer it sets the style
1017  with its default optional values. The line style \p s can be one of
1018  the following:
1019 
1020  style | description
1021  :------:|:------------------------------
1022  0 | no line
1023  1 | solid line
1024  2 | single dash pattern centered
1025  3 | dual overlapped dash patterns
1026  4 | both ends and center
1027  5 | line section break
1028 
1029  Each style can be customize via optional parameter list fields. The
1030  options differ by style:
1031 
1032  <b>style 2</b>
1033 
1034  field | data type | default | description
1035  :------:|:---------:|:-------:|:------------------
1036  1 | decimal | 1 | length multiplier
1037  2 | decimal | 2 | stride
1038 
1039  <b>style 3</b>
1040 
1041  field | data type | default | description
1042  :------:|:---------:|:-------:|:--------------------
1043  1 | decimal | 1 | length multiplier 1
1044  2 | decimal | 2 | stride 1
1045  3 | decimal | 2 | length multiplier 2
1046  4 | decimal | 3 | stride 2
1047 
1048  <b>style 4</b>
1049 
1050  field | data type | default | description
1051  :------:|:---------:|:-------:|:----------------------------
1052  1 | decimal | 1 | number of centered segments
1053  2 | decimal | 1 | centered-length multiplier
1054  3 | decimal | 1 | end-length multiplier
1055 
1056  <b>style 5</b>
1057 
1058  field | data type | default | description
1059  :------:|:---------:|:-------:|:------------------------
1060  1 | decimal | 1 | number of breaks
1061  2 | decimal | 2 | break length multiplier
1062  3 | decimal | 2 | break width multiplier
1063  4 | decimal | 67.5 | break angle
1064 
1065  \amu_eval ( object=draft_line ${object_diagram_2d} )
1066 
1067  The line segments are constructed by draft_line_pp().
1068 
1069  | see: \ref draft_config_map |
1070  |:---------------------------:|
1071  | line-segment-min |
1072 *******************************************************************************/
1073 module draft_line
1074 (
1075  l = x_axis2d_ul,
1076  w = 1,
1077  s = 1,
1078  a1 = 0,
1079  a2 = 0
1080 )
1081 {
1082  s1 = defined_e_or(s, 0, s); // line selection
1083 
1084  if ( !all_equal([s1, a1, a2], 0) )
1085  {
1086  lsm = _draft_get_config("line-segment-min") * $draft_scale;
1087 
1088  i = line_ip(l);
1089  t = line_tp(l);
1090 
1091  if ( s1 == 1 )
1092  { // solid line
1093  draft_line_pp(i, t, w);
1094  }
1095  else if ( s1 == 2 )
1096  { // single pattern centered
1097  s2 = defined_e_or(s, 1, 1)*lsm; // length multiplier
1098  s3 = defined_e_or(s, 2, 2); // stride
1099 
1100  for ( ls = sequence_ns( polygon_line_p(l=l, ft=s2), 2, s3 ) )
1101  draft_line_pp(ls[0], ls[1], w);
1102  }
1103  else if ( s1 == 3 )
1104  { // dual overlapped patterns
1105  s2 = defined_e_or(s, 1, 1)*lsm; // length multiplier 1
1106  s3 = defined_e_or(s, 2, 2); // stride 1
1107  s4 = defined_e_or(s, 3, 2)*lsm; // length multiplier 2
1108  s5 = defined_e_or(s, 4, 3); // stride 2
1109 
1110  for ( ls = sequence_ns( polygon_line_p(l=l, fs=s2), 2, s3 ) )
1111  draft_line_pp(ls[0], ls[1], w);
1112 
1113  for ( ls = sequence_ns( polygon_line_p(l=l, fs=s4), 2, s5 ) )
1114  draft_line_pp(ls[0], ls[1], w);
1115  }
1116  else if ( s1 == 4 )
1117  { // at both ends and 'n' centered
1118  s2 = defined_e_or(s, 1, 1); // number 'n'
1119  s3 = defined_e_or(s, 2, 1)*lsm; // centered-length multiplier
1120  s4 = defined_e_or(s, 3, 1)*lsm; // end-length multiplier
1121 
1122  // at both ends
1123  el1 = line2d_new(s4, p1=i, v=[i, t]);
1124  el2 = line2d_new(s4, p1=t, v=[t, i]);
1125 
1126  draft_line_pp(el1[0], el1[1], w);
1127  draft_line_pp(el2[0], el2[1], w);
1128 
1129  // 'n' centered
1130  if (s2 > 0)
1131  {
1132  mp = polygon_line_p(l=l, fn=s2+1);
1133  for (j = [1:len(mp)-2])
1134  {
1135  cl1 = line2d_new(s3/2, p1=mp[j], v=[t, i]);
1136  cl2 = line2d_new(s3/2, p1=mp[j], v=[i, t]);
1137 
1138  draft_line_pp(cl1[1], cl2[1], w);
1139  }
1140  }
1141  }
1142  else if ( s1 == 5 )
1143  { // line break(s)
1144  s2 = defined_e_or(s, 1, 1); // number of breaks
1145  s3 = defined_e_or(s, 2, 2)*lsm; // length multiplier
1146  s4 = defined_e_or(s, 3, 2)*lsm; // width multiplier
1147  s5 = defined_e_or(s, 4, 67.5); // angle*
1148 
1149  // *s5=90 invokes bug: https://github.com/CGAL/cgal/issues/2631
1150 
1151  la = angle_ll(x_axis2d_uv, l);
1152  bp = polygon_line_p(l=l, fn=1+max(1,s2));
1153 
1154  xp =
1155  [
1156  for (i = [1:len(bp)-2])
1157  let
1158  (
1159  mp = bp[i], // mid, cross, & line points
1160  xp = polygon_regular_p(n=2, r=s4, ao=la+s5, o=mp),
1161  lp = polygon_regular_p(n=2, r=s3, ao=la, o=mp)
1162  )
1163  [ lp[1], xp[0], xp[1], lp[0] ]
1164  ];
1165 
1166  for ( ls = sequence_ns(concat([i],merge_s(xp),[t]), 2, 1 ) )
1167  draft_line_pp(ls[0], ls[1], w);
1168  }
1169 
1170  // arrows
1171  draft_arrow(l=[t, i], w=w, s=a1);
1172  draft_arrow(l=[i, t], w=w, s=a2);
1173  }
1174 }
1175 
1176 //! Draft an arc with configurable style and optional arrowheads.
1177 /***************************************************************************//**
1178  \copydetails polygon_arc_sweep_p()
1179  These coordinates will be used to draft an arc according to the
1180  following additional parameters.
1181 
1182  \param w <decimal> The line weight.
1183  \param s <integer | integer-list> The line style.
1184  \param a1 <integer | integer-list-5> The arrowhead style at initial point.
1185  \param a2 <integer | integer-list-5> The arrowhead style at terminal point.
1186 
1187  \details
1188 
1189  When parameter \p s is assigned a single integer it sets the style
1190  with its default optional values. The line style \p s can be one of
1191  the following:
1192 
1193  style | description
1194  :------:|:--------------------
1195  0 | no line
1196  1 | solid line
1197  2 | single dash pattern
1198 
1199  Style 2 can be customize via optional parameter as shown below:
1200 
1201  <b>style 2</b>
1202 
1203  field | data type | default | description
1204  :------:|:---------:|:-------:|:------------
1205  1 | decimal | 2 | stride
1206 
1207  \amu_eval ( object=draft_arc ${object_diagram_2d} )
1208 
1209  The line segments are constructed by draft_line_pp().
1210 *******************************************************************************/
1211 module draft_arc
1212 (
1213  r = 1,
1214  o = origin2d,
1215  v1 = x_axis2d_uv,
1216  v2 = x_axis2d_uv,
1217  fn,
1218  cw = true,
1219 
1220  w = 1,
1221  s = 1,
1222  a1 = 0,
1223  a2 = 0
1224 )
1225 {
1226  s1 = defined_e_or(s, 0, s); // line selection
1227 
1228  if ( !all_equal([s1, a1, a2], 0) )
1229  {
1230  // Compute the sweep point array once, shared by line and arrow rendering.
1231  // When s1==0 (no line) but arrows are requested we still need the two
1232  // endpoint points; polygon_arc_sweep_p is called with a minimal fn so
1233  // only the head and tail points are generated in that case.
1234  pp = polygon_arc_sweep_p( r=r, o=o, v1=v1, v2=v2, fn=(s1==0 ? 2 : fn), cw=cw );
1235 
1236  if ( s1 == 1 )
1237  { // solid line
1238  for ( ls = sequence_ns( pp, 2, 1 ) )
1239  draft_line_pp(ls[0], ls[1], w);
1240  }
1241  else if ( s1 == 2 )
1242  { // single pattern
1243  s2 = defined_e_or(s, 1, 2); // point stride
1244 
1245  for ( ls = sequence_ns( pp, 2, s2 ) )
1246  draft_line_pp(ls[0], ls[1], w);
1247  }
1248 
1249  // arrows
1250  draft_arrow(l=reverse(firstn(pp, 2)), w=w, s=a1);
1251  draft_arrow(l=lastn(pp, 2), w=w, s=a2);
1252  }
1253 }
1254 
1255 //! Draft a rectangle with configurable style.
1256 /***************************************************************************//**
1257  \param d <decimal-list-2 | decimal> A list [x, y] of decimals
1258  or a single decimal for (x=y).
1259  \param o <point-2d> The center coordinate [x, y].
1260  \param w <decimal> The line weight.
1261  \param s <integer | integer-list> The line style.
1262 
1263  \details
1264 
1265  \amu_eval ( object=draft_rectangle ${object_diagram_2d} )
1266 
1267  The line segments are constructed by draft_line().
1268 *******************************************************************************/
1269 module draft_rectangle
1270 (
1271  d = 1,
1272  o = origin2d,
1273  w = 1,
1274  s = 1
1275 )
1276 {
1277  dx = defined_e_or(d, 0, d);
1278  dy = defined_e_or(d, 1, dx);
1279 
1280  mx = dx/2;
1281  my = dy/2;
1282 
1283  pl =
1284  [
1285  [-mx, -my] + o,
1286  [-mx, my] + o,
1287  [ mx, my] + o,
1288  [ mx, -my] + o
1289  ];
1290 
1291  // draft each edge
1292  for ( cp = sequence_ns(pl, n=2, s=1, w=true) )
1293  draft_line(l=[cp[0], cp[1]], w=w, s=s);
1294 }
1295 
1296 //! Draft a polygon with configurable style.
1297 /***************************************************************************//**
1298  \param c <points-2d> A list of 2d coordinate points.
1299 
1300  \param p <integer-list-list> A list of paths that enclose the
1301  shape where each face is a list of coordinate indexes.
1302  \param e <integer-list-2-list> A list of edges where each edge is
1303  a list of two coordinate indexes.
1304  \param i <index> An index sequence [specification].
1305 
1306  \param w <decimal> The line weight.
1307  \param s <integer | integer-list> The line style.
1308 
1309  \details
1310 
1311  Parameter \p p is optional and when it is not given, the listed
1312  order of the coordinates establishes the polygon path. When
1313  parameter \p e is not specified, it is computed from \p p using
1314  polytope_faces2edges(). Parameter \p i allows coordinate indexes to
1315  be selected using several selection [schemes][specification].
1316 
1317  \amu_eval ( object=draft_polygon ${object_diagram_2d} )
1318 
1319  The line segments are constructed by draft_line().
1320 
1321  [specification]: \ref index_sel()
1322 *******************************************************************************/
1323 module draft_polygon
1324 (
1325  c,
1326  p,
1327  e,
1328  i = true,
1329 
1330  w = 1,
1331  s = 1
1332 )
1333 {
1334  // Fast path: when neither 'p' nor 'e' is supplied, the polygon is a
1335  // simple closed loop of all points in order. Building the edge list
1336  // directly avoids the overhead of polytope_faces2edges([consts(len(c))]),
1337  // which allocates and traverses an intermediate face structure.
1338  el = is_defined(e) ? e // use supplied edge list 'e'
1339  : is_defined(p) ? // use supplied path list 'p'
1341  : let( n = len(c) ) // fast sequential closed loop
1342  [ for( j=[0:n-1] ) [j, (j+1)%n] ];
1343 
1344  // draft each selected edge
1345  for (i = index_sel(el, i)) // allow edge selection index
1346  draft_line(l=[c[first(el[i])], c[second(el[i])]], w=w, s=s);
1347 }
1348 
1349 //! @}
1350 
1351 //----------------------------------------------------------------------------//
1352 // primitives: miscellaneous
1353 //----------------------------------------------------------------------------//
1354 
1355 //! \name Primitives: Miscellaneous
1356 //! @{
1357 
1358 //! Extrude 2D drafted constructions to 3D if configured.
1359 /***************************************************************************//**
1360  \details
1361 
1362  When \ref $draft_make_3d is \b true, all children objects are
1363  extruded to 3D.
1364 
1365  | see: \ref draft_config_map |
1366  |:---------------------------:|
1367  | make-3d-height |
1368 
1369  \private
1370 *******************************************************************************/
1371 module _draft_make_3d_if_configured
1372 (
1373 )
1374 {
1375  if ( $draft_make_3d )
1376  linear_extrude
1377  (
1378  height=_draft_get_config("make-3d-height") * $draft_scale, center=true
1379  )
1380  children();
1381  else
1382  children();
1383 }
1384 
1385 //! @}
1386 
1387 //! @}
1388 
1389 //----------------------------------------------------------------------------//
1390 // openscad-amu auxiliary scripts
1391 //----------------------------------------------------------------------------//
1392 
1393 /*
1394 BEGIN_SCOPE diagram;
1395  BEGIN_OPENSCAD;
1396  include <omdl-base.scad>;
1397  include <transforms/base_cs.scad>;
1398  include <tools/common/polytope.scad>;
1399  include <tools/2d/drafting/draft-base.scad>;
1400 
1401  object = "draft_arrow";
1402 
1403  if (object == "draft_arrow")
1404  {
1405  grid = [20, 10];
1406 
1407  for ( s=[1:5], f=[0:1], p=[0:2] )
1408  translate( [(p+1)*first(grid) + f*3*first(grid), s*second(grid)] )
1409  draft_arrow( l=x_axis2d_ul, s=[s, f, p] );
1410  }
1411 
1412  if (object == "draft_line")
1413  {
1414  line = [[0,0], [100,0]];
1415  for ( s=[1:5] )
1416  translate( [0, s*5] )
1417  draft_line(l=line, s=s);
1418  }
1419 
1420  if (object == "draft_arc")
1421  {
1422  for ( s=[1:2] )
1423  draft_arc (r=50-5*s, v1=[-1,1], v2=45, cw=true, s=s);
1424  }
1425 
1426  if (object == "draft_rectangle")
1427  {
1428  draft_rectangle ([100, 30], s=[4, 3, 2, 5]);
1429  }
1430 
1431  if (object == "draft_polygon")
1432  {
1433  pp = length
1434  (
1435  [ [+1.5 + 1/3, -1.25], [+1.5/4, 0], [+1.5 - 1/3, +1.25],
1436  [-1.5 + 1/3, +1.25], [-1.5/4, 0], [-1.5 - 1/3, -1.25]
1437  ], "in"
1438  );
1439 
1440  polytope_number(pp, vi=true, ei=false, fi=false);
1441 
1442  rp = polygon_round_eve_all_p(c=pp, vr=length(1/4, "in"), vrm=1, cw=false);
1443  draft_polygon(rp, s=2);
1444  }
1445  END_OPENSCAD;
1446 
1447  BEGIN_MFSCRIPT;
1448  include --path "${INCLUDE_PATH}" {var_init,var_gen_svg}.mfs;
1449 
1450  defines name "objects" define "object"
1451  strings "
1452  draft_arrow
1453  draft_line
1454  draft_arc
1455  draft_rectangle
1456  draft_polygon
1457  ";
1458  variables add_opts_combine "objects";
1459  variables add_opts "--viewall --autocenter";
1460 
1461  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1462  END_MFSCRIPT;
1463 END_SCOPE;
1464 */
1465 
1466 //----------------------------------------------------------------------------//
1467 // end of file
1468 //----------------------------------------------------------------------------//
x_axis2d_ul
<line-2d> A positively-directed unit line centered on the x-axis in 2d Euclidean space.
Definition: constants.scad:416
origin2d
<point-2d> The origin point coordinate in 2d Euclidean space.
Definition: constants.scad:407
y_axis3d_ul
<line-3d> A positively-directed unit line centered on the y-axis in 3d Euclidean space.
Definition: constants.scad:440
x_axis2d_uv
<vector-2d> The unit vector of the positive x-axis in 2d Euclidean space.
Definition: constants.scad:410
function binary_bit_is(v, b, t=1)
Test if a binary bit position of an integer value equals a test bit.
function line_tp(l)
Return the terminal point of a line or vector.
function angle_ll(l1, l2, s=true)
Compute the angle between two lines or vectors in a 3d or 2d-space.
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 distance_pp(p1, p2)
Compute the distance between two points.
function firstn(v, n=1)
Return a list containing the first n elements of an iterable value.
function find(mv, v, c=1, i, i1=0, i2)
Find the occurrences of a match value in an iterable value.
function defined_e_or(v, i, d)
Returns an element from an iterable if it exists, or a default value if not.
function last(v)
Return the last element of an iterable value.
function exists(mv, v, s=true, i)
Check for the existence of a match value in an iterable value.
function second(v)
Return the second element of an iterable value.
function lastn(v, n=1)
Return a list containing the last n elements of an iterable value.
function first(v)
Return the first element of an iterable value.
function sequence_ns(v, n=1, s=1, w=false)
Return a list of all n-element sequential-subsets of an iterable value.
function reverse(v)
Reverse the elements of an iterable value.
function all_equal(v, cv)
Test if all elements of an iterable value equal a comparison value.
function all_strings(v)
Test if all elements of an iterable value are strings.
function index_sel(l, s=true, rs)
Selected element indices of a given list according to a selection scheme.
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 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 polygon_arc_sweep_p(r=1, o=origin2d, v1=x_axis2d_uv, v2=x_axis2d_uv, fn, cw=true)
Compute coordinates of an arc with constant radius between two vectors in 2D.
function polygon_regular_p(n, r, a, o=origin2d, ao=0, vr, cw=true)
Compute coordinates for an n-sided regular polygon in 2D.
function polygon_line_p(p1=origin2d, p2=x_axis2d_uv, l, x, y, r, fs, ft, fn=1)
Compute coordinates along a line in 2D.
function polytope_faces2edges(f)
List the edge coordinate index pairs of a polytope.
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_line_pp(i, t, w=1)
Draft a simple line from an initial to a terminal point.
Definition: primitive.scad:951
draft_layers_show
<string-list> List of active drafting layer names.
Definition: config.scad:649
$draft_make_3d
<boolean> Extrude 2D drafted constructions to 3D.
Definition: config.scad:97
$draft_scale
<integer> Line construction drafting scale multiplier.
Definition: config.scad:129
module draft_arrow(l=x_axis2d_ul, w=1, s=1)
Draft an arrowhead at the terminal point of a line.
$draft_line_fn
<integer> Arc fragment size for line construction.
Definition: config.scad:100
module draft_arc(r=1, o=origin2d, v1=x_axis2d_uv, v2=x_axis2d_uv, fn, cw=true, w=1, s=1, a1=0, a2=0)
Draft an arc with configurable style and optional arrowheads.
$draft_arrow_fn
<integer> Arc fragment size for arrowhead construction.
Definition: config.scad:103
module draft_rectangle(d=1, o=origin2d, w=1, s=1)
Draft a rectangle with configurable style.
module draft_polygon(c, p, e, i=true, w=1, s=1)
Draft a polygon with configurable style.
draft_sheet_scale
<integer> Sheet construction drafting scale multiplier.
Definition: config.scad:159
module align_ll(l=z_axis3d_ul, r=z_axis3d_ul, lp=0, rp=0, ar=0, to=origin3d, ro=origin3d, lpo, rpo)
Align a line or vector to a reference line or vector.
Definition: align.scad:282
module hull_cs(c=true, s)
Conditionally apply the convex hull transformation.
Definition: base_cs.scad:169