omdl  v0.9.5
OpenSCAD Mechanical Design Library
polygon.scad
Go to the documentation of this file.
1 //! Polygon shapes, conversions, properties, and tests functions.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2015-2024
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 (Polygons)
31  \amu_define group_brief (Polygon mathematical functions; 2-polytope.)
32 
33  \amu_include (include/amu/pgid_path_pstem_pg.amu)
34 *******************************************************************************/
35 
36 //----------------------------------------------------------------------------//
37 // group.
38 //----------------------------------------------------------------------------//
39 
40 /***************************************************************************//**
41  \amu_include (include/amu/group_in_parent_start.amu)
42  \amu_include (include/amu/includes_required.amu)
43 *******************************************************************************/
44 
45 //----------------------------------------------------------------------------//
46 // shape generation
47 //----------------------------------------------------------------------------//
48 
49 //! \name Shapes
50 //! @{
51 
52 //! Compute coordinates for an n-sided regular polygon in 2D.
53 /***************************************************************************//**
54  \param n <integer> The number of sides.
55  \param r <decimal> The circumradius of the circumcircle.
56  \param a <decimal> The inradius of the incircle.
57  \param c <point-2d> The center coordinate [x, y].
58  \param o <decimal> The rotational angular offset.
59  \param vr <decimal> The vertex rounding radius.
60  \param cw <boolean> Use clockwise point ordering.
61 
62  \returns <coords-2d> A list of coordinates points [[x, y], ...].
63 
64  \details
65 
66  The radius can be specified by either the circumradius \p r or the
67  inradius \p a. If both are specified, \p r is used.
68 
69  \b Example
70  \code{.C}
71  vr=5;
72 
73  hull()
74  {
75  for ( p = polygon_regular_p( r=20, n=5, vr=vr ) )
76  translate( p )
77  circle( r=vr );
78  }
79  \endcode
80 
81  See [Wikipedia] for more information.
82 
83  [Wikipedia]: https://en.wikipedia.org/wiki/Regular_polygon
84 *******************************************************************************/
85 function polygon_regular_p
86 (
87  n,
88  r,
89  a,
90  c = origin2d,
91  o = 0,
92  vr,
93  cw = true
94 ) =
95  let
96  (
97  s = is_defined(r) ? r
98  : is_defined(a) ? a / cos(180/n)
99  : 0,
100 
101  b = (cw == true) ? [360:-(360/n):1] : [0:(360/n):359]
102  )
103  [
104  for (a = b)
105  let( v = [s*cos(a+o), s*sin(a+o)] + c )
106  is_undef(vr) ? v : v - vr/cos(180/n) * unit_l(v)
107  ];
108 
109 //! Compute coordinates along a line in 2D.
110 /***************************************************************************//**
111  \param p1 <point-2d> The line initial coordinate [x, y].
112  \param p2 <point-2d> The line terminal coordinate [x, y].
113  \param l <line-2d> The line or vector.
114 
115  \param x <decimal-list | decimal> A list of \p x coordinates
116  [\p x1, \p x2, ...] or a single \p x coordinate at which to
117  interpolate along the line.
118  \param y <decimal-list | decimal> A list of \p y coordinates
119  [\p y1, \p y2, ...] or a single \p y coordinate at which to
120  interpolate along the line.
121 
122  \param r <decimal-list | decimal> A list of ratios
123  [\p r1, \p r2, ...] or a single ratio \p r. The position
124  ratio along line \p p1 (\p r=\b 0) to \p p2 (\p r=\b 1).
125 
126  \param fs <decimal> A fixed segment size between each point along
127  the line.
128  \param ft <decimal> A fixed segment size between each point,
129  centered, beginning at \p p1 and terminating at \p p2.
130  \param fn <integer> A fixed number of equally spaced points.
131 
132  \returns <coords-2d> A list of coordinates points [[x, y], ...].
133 
134  \details
135 
136  Linear interpolation is used to compute each point along the line.
137  The order of precedence for line specification is: \p l then \p p1
138  and \p p2. The order of precedence for interpolation is: \p x, \p
139  y, \p r, \p fs, \p ft, \p fn.
140 *******************************************************************************/
141 function polygon_line_p
142 (
143  p1 = origin2d,
144  p2 = x_axis2d_uv,
145  l,
146 
147  x, // coordinate x [or vector of]
148  y, // coordinate y [or vector of]
149  r, // factor [0,1] [or vector of]
150 
151  fs, // fixed size
152  ft, // fixed size centered and terminating
153  fn = 1 // number
154 ) =
155  let
156  (
157  ip = is_defined(l) ? line_ip(l) : p1,
158  tp = is_defined(l) ? line_tp(l) : p2,
159 
160  //
161  // zdx = (tp[0] == ip[0]), // is delta-x zero
162  // zdy = (tp[1] == ip[1]), // is delta-y zero
163  //
164  zdx = almost_eq_nv(tp[0], ip[0]), // is delta-x zero
165  zdy = almost_eq_nv(tp[1], ip[1]), // is delta-y zero
166 
167  // axis: 'y' if zdx, else use 'x'
168  a = zdx ? 1 : 0,
169 
170  // sign / direction of line
171  s = (tp[a] > ip[a]) ? +1 : -1,
172 
173  pl = is_defined(x) ? is_list(x) ? x : [x]
174  : is_defined(y) ? is_list(y) ? y : [y]
175 
176  // list of ratios
177  : is_defined(r) ?
178  [for (i=is_list(r) ? r : [r]) (i*(tp[a]-ip[a])+ip[a])]
179 
180  // fixed segment size
181  : is_defined(fs) ?
182  let
183  (
184  // scale by line x-projection iff using 'x' (ie: zdx != 0)
185  sx = fs * (zdx ? 1 : cos(angle_ll(x_axis2d_uv, s*[ip, tp])))
186  )
187  [for (i=[ip[a] : s*sx : tp[a]]) i]
188 
189  // fixed segment size centered
190  : is_defined(ft) ?
191  let
192  (
193  // scale by line x-projection iff using 'x' (ie: zdx != 0)
194  sx = ft * (zdx ? 1 : cos(angle_ll(x_axis2d_uv, s*[ip, tp]))),
195  // center offset
196  co = ( abs(tp[a]-ip[a]) - sx*floor(abs(tp[a]-ip[a])/sx) )/2
197  )
198  [ip[a], for (i=[ip[a] + s*co : s*sx : tp[a]]) i, tp[a]]
199 
200  // fixed number
201  : [for (i=[0:fn]) (i*(tp[a]-ip[a])/fn+ip[a])]
202  )
203  (a == 1) ?
204  is_defined(x) ? undef // (a == 1)
205  : [ for (py = pl) interpolate2d_l_pp(ip, tp, y=py) ] // interpolate for 'x'
206  : is_defined(y) && zdy ? undef // (a == 0)
207  : [ for (px = pl) interpolate2d_l_pp(ip, tp, x=px) ]; // interpolate for 'y'
208 
209 //! Compute coordinates of an arc with constant radius between two vectors in 2D.
210 /***************************************************************************//**
211  \param r <decimal> The arc radius.
212  \param c <point-2d> The arc center coordinate [x, y].
213  \param v1 <line-2d | decimal> The arc start angle.
214  A 2d line, vector, or decimal angle 1.
215  \param v2 <line-2d | decimal> The arc end angle.
216  A 2d line, vector, or decimal angle 2.
217  \param fn <integer> The number of [facets] (optional).
218  \param cw <boolean> Sweep clockwise along arc from the head of
219  vector \p v1 to the head of vector \p v2 when \p cw =
220  \b true, and counter clockwise when \p cw = \b false.
221 
222  \returns <coords-2d> A list of coordinates points [[x, y], ...].
223 
224  \details
225 
226  The arc coordinates will have radius \p r centered about \p c
227  contained within the heads of vectors \p v1 and \p v2. The arc will
228  start at the point coincident to \p v1 and will end at the point
229  coincident to \p v2. When vectors \p v1 and \p v2 are parallel, the
230  arc will be a complete circle. When \p fn is undefined, its value
231  is determined by get_fn().
232 
233  [facets]: \ref get_fn()
234 *******************************************************************************/
235 function polygon_arc_p
236 (
237  r = 1,
238  c = origin2d,
239  v1 = x_axis2d_uv,
240  v2 = x_axis2d_uv,
241  fn,
242  cw = true
243 ) =
244  let
245  (
246  // number of arc facets
247  naf = defined_or(fn, get_fn(r)),
248 
249  // create vectors if numerical angles have been specified.
250  va1 = is_number(v1) ? [cos(v1), sin(v1)] : v1,
251  va2 = is_number(v2) ? [cos(v2), sin(v2)] : v2,
252 
253  // arc positive start angle
254  iap = angle_ll(x_axis2d_uv, va1, false),
255 
256  // positive arc sweep angle
257  vas = angle_ll(va2, va1, false),
258  vap = (vas == 0) ? 360 : vas,
259 
260  // arc cw and ccw signed sweep step
261  sas = (((cw == true) ? 0 : 360) - vap)/naf
262  )
263  [
264  for (as = [0 : naf])
265  let (aa = iap + as * sas)
266  c + r * [cos(aa), sin(aa)]
267  ];
268 
269 //! Compute coordinates for an elliptical sector in 2D.
270 /***************************************************************************//**
271  \param r <decimal-list-2 | decimal> The elliptical radius. A list
272  [rx, ry] of decimals or a single decimal for (rx=ry).
273  \param c <point-2d> The center coordinate [x, y].
274  \param v1 <line-2d | decimal> The sector angle 1.
275  A 2d line, vector, or decimal.
276  \param v2 <line-2d | decimal> The sector angle 2.
277  A 2d line, vector, or decimal.
278  \param s <boolean> Use signed vector angle conversions. When
279  \b false, positive angle conversion will be used.
280  \param fn <integer> The number of [facets] (optional).
281  \param cw <boolean> The coordinate point ordering.
282 
283  \returns <coords-2d> A list of coordinates points [[x, y], ...].
284 
285  \details
286 
287  The coordinates will be between angle 1 and angle 2 and will be
288  ordered clockwise. The sector sweep direction can be controlled by
289  the sign of the angles. When \p fn is undefined, its value is
290  determined by get_fn().
291 
292  [facets]: \ref get_fn()
293 *******************************************************************************/
295 (
296  r = 1,
297  c = origin2d,
298  v1 = x_axis2d_uv,
299  v2 = x_axis2d_uv,
300  s = true,
301  fn,
302  cw = true
303 ) =
304  let
305  (
306  rx = defined_e_or(r, 0, r),
307  ry = defined_e_or(r, 1, rx),
308 
309  va1 = is_number(v1) ? v1 : angle_ll(x_axis2d_uv, v1, s),
310  va2 = is_number(v2) ? v2 : angle_ll(x_axis2d_uv, v2, s),
311 
312  // full return when angles are equal
313  va3 = (va1 == va2) ? va2+360 : va2,
314 
315  // number of arc facets
316  af = defined_or(fn, get_fn((rx+ry)/2)),
317 
318  // point generation ordering
319  as = (va3 > va1) ? [af:-1:0] : [0:af],
320 
321  // cw ordering
322  pp =
323  [
324  if (va1 != va2) c,
325  for (i = as)
326  let (pa = ((af-i)*va1 + i*va3) / af)
327  c + [rx*cos(pa), ry*sin(pa)]
328  ]
329  )
330  (cw == true) ? pp : reverse(pp);
331 
332 //! Compute the coordinates for a rounded trapezoid in 2D space.
333 /***************************************************************************//**
334  \param b <decimal-list-2 | decimal> The base lengths. A list [b1, b2]
335  of 2 decimals or a single decimal for (b1=b2).
336  \param h <decimal> The perpendicular height between bases.
337  \param l <decimal> The left side leg length.
338  \param a <decimal> The angle between the lower base and left leg.
339  \param o <point-2d> The origin offset coordinate [x, y].
340  \param cw <boolean> Polygon vertex ordering.
341 
342  \returns <coords-2d> A list of coordinates points [[x, y], ...].
343 
344  \details
345 
346  When both \p h and \p l are specified, \p h has precedence. The
347  function generates [parallelograms], rectangles, and squares with
348  the appropriate parameter assignments. See [Wikipedia] for more
349  general information on trapezoids.
350 
351  [Wikipedia]: https://en.wikipedia.org/wiki/Trapezoid
352  [parallelograms]: https://en.wikipedia.org/wiki/Parallelogram
353 *******************************************************************************/
354 function polygon_trapezoid_p
355 (
356  b = 1,
357  h,
358  l = 1,
359  a = 90,
360  o = origin2d,
361  cw = true
362 ) =
363  let
364  (
365  b1 = defined_e_or(b, 0, b),
366  b2 = defined_e_or(b, 1, b1),
367 
368  // trapezoid vertices from origin
369  p1 = o,
370  p2 = o + (is_undef(h) ? l*[cos(a), sin(a)] : h*[cos(a), 1]),
371  p3 = p2 + [b2, 0],
372  p4 = o + [b1, 0],
373 
374  // cw ordering
375  pp = [p4, p1, p2, p3]
376  )
377  (cw == true) ? pp : reverse(pp);
378 
379 //! @}
380 
381 //----------------------------------------------------------------------------//
382 // shape properties
383 //----------------------------------------------------------------------------//
384 
385 //! \name Properties
386 //! @{
387 
388 //! Compute the perimeter of an n-sided regular polygon in 2D.
389 /***************************************************************************//**
390  \param n <integer> The number of sides.
391  \param r <decimal> The vertex circumradius of the circumcircle.
392  \param a <decimal> The inradius of the incircle.
393 
394  \returns <decimal> Perimeter length of the n-sided regular polygon.
395 
396  \details
397 
398  The radius can be specified by either the circumradius \p r or the
399  inradius \p a. If both are specified, \p r is used.
400 *******************************************************************************/
402 (
403  n,
404  r,
405  a
406 ) = is_defined(r) ? 2 * n * r * sin(180/n)
407  : is_defined(a) ? 2 * n * a * tan(180/n)
408  : 0;
409 
410 //! Compute the area of an n-sided regular polygon in 2D.
411 /***************************************************************************//**
412  \param n <integer> The number of sides.
413  \param r <decimal> The vertex circumradius of the circumcircle.
414  \param a <decimal> The inradius of the incircle.
415 
416  \returns <decimal> Area of the n-sided regular polygon.
417 
418  \details
419 
420  The radius can be specified by either the circumradius \p r or the
421  inradius \p a. If both are specified, \p r is used.
422 *******************************************************************************/
423 function polygon_regular_area
424 (
425  n,
426  r,
427  a
428 ) = is_defined(r) ? pow(r, 2) * n * sin(360/n) / 2
429  : is_defined(a) ? pow(a, 2) * n * tan(180/n)
430  : 0;
431 
432 //! Calculate the perimeter length of a polygon in 2d.
433 /***************************************************************************//**
434  \param c <coords-2d> A list of 2d cartesian coordinates
435  [[x, y], ...].
436  \param p <integer-list-list> An \em optional list of paths that
437  define one or more closed shapes where each is a list of
438  coordinate indexes.
439 
440  \returns <decimal> The sum of all polygon primary and secondary
441  perimeter lengths.
442 
443  \details
444 
445  When \p p is not defined, the listed order of the coordinates will
446  be used.
447 *******************************************************************************/
448 function polygon_perimeter
449 (
450  c,
451  p
452 ) =
453  let
454  (
455  pm = defined_or(p, [consts(len(c))]),
456 
457  lv =
458  [
459  for (k = pm) let (n = len(k))
460  for (i=[0 : n-1]) let (j = (i == 0) ? n-1 : i-1)
461  distance_pp(c[k[j]], c[k[i]])
462  ]
463  )
464  sum(lv);
465 
466 //! Compute the signed area of a polygon in a Euclidean 2d-space.
467 /***************************************************************************//**
468  \param c <coords-2d> A list of 2d cartesian coordinates
469  [[x, y], ...].
470  \param p <integer-list-list> An \em optional list of paths that
471  define one or more closed shapes where each is a list of
472  coordinate indexes.
473  \param s <boolean> Return the vertex ordering sign.
474 
475  \returns <decimal> The area of the given polygon.
476 
477  \details
478 
479  See [Wikipedia] for more information.
480 
481  When \p p is not defined, the listed order of the coordinates will
482  be used.
483 
484  \warning This function does not track secondary shapes subtraction as
485  implemented by the polygon() function.
486 
487  [Wikipedia]: https://en.wikipedia.org/wiki/Shoelace_formula
488 *******************************************************************************/
489 function polygon_area
490 (
491  c,
492  p,
493  s = false
494 ) =
495  let
496  (
497  pm = defined_or(p, [consts(len(c))]),
498 
499  av =
500  [
501  for (k = pm) let (n = len(k))
502  for (i=[0 : n-1]) let (j = (i == 0) ? n-1 : i-1)
503  (c[k[j]][0] + c[k[i]][0]) * (c[k[i]][1] - c[k[j]][1])
504  ],
505 
506  sa = sum(av)/2
507  )
508  (s == false) ? abs(sa) : sa;
509 
510 //! Compute the area of a polygon in a Euclidean 3d-space.
511 /***************************************************************************//**
512  \param c <coords-3d> A list of 3d cartesian coordinates
513  [[x, y, z], ...].
514  \param p <integer-list-list> An \em optional list of paths that
515  define one or more closed shapes where each is a list of
516  coordinate indexes.
517  \param n <vector-3d> An \em optional normal vector, [x, y, z],
518  to the polygon plane. When not given, a normal vector is
519  constructed from the first three points of the primary path.
520 
521  \returns <decimal> The area of the given polygon.
522 
523  \details
524 
525  Function patterned after [Dan Sunday, 2012].
526 
527  When \p p is not defined, the listed order of the coordinates will
528  be used.
529 
530  \warning This function does not track secondary shapes subtraction as
531  implemented by the polygon() function.
532 
533  [Dan Sunday, 2012]: http://geomalgorithms.com/a01-_area.html
534 *******************************************************************************/
535 function polygon3d_area
536 (
537  c,
538  p,
539  n
540 ) =
541  let
542  (
543  pm = defined_or(p, [consts(len(c))]),
544  nv = defined_or(n, cross_ll([c[pm[0][0]], c[pm[0][1]]], [c[pm[0][0]], c[pm[0][2]]])),
545 
546  ac = [abs(nv[0]), abs(nv[1]), abs(nv[2])],
547  am = max(ac),
548  ai = (am == ac[2]) ? 2 : (am == ac[1]) ? 1 : 0,
549 
550  pv = [
551  for (k = pm) let (m = len(k))
552  for (i=[1 : m])
553  c[k[i%m]][(ai+1)%3] * (c[k[(i+1)%m]][(ai+2)%3] - c[k[(i-1)%m]][(ai+2)%3])
554  ],
555 
556  sf = (distance_pp(nv)/(2*nv[ai]))
557  )
558  (sum(pv) * sf);
559 
560 //! Compute the center of mass of a polygon in a Euclidean 2d-space.
561 /***************************************************************************//**
562  \param c <coords-2d> A list of 2d cartesian coordinates
563  [[x, y], ...].
564  \param p <integer-list-list> An \em optional list of paths that
565  define one or more closed shapes where each is a list of
566  coordinate indexes.
567 
568  \returns <point-2d> The center of mass of the given polygon.
569 
570  \details
571 
572  See [Wikipedia] for more information.
573 
574  When \p p is not defined, the listed order of the coordinates will
575  be used.
576 
577  \warning This function does not track secondary shapes subtraction as
578  implemented by the polygon() function.
579 
580  [Wikipedia]: https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
581 *******************************************************************************/
582 function polygon_centroid
583 (
584  c,
585  p
586 ) =
587  let
588  (
589  pm = defined_or(p, [consts(len(c))]),
590 
591  cv =
592  [
593  for (k = pm) let (n = len(k))
594  for (i=[0 : n-1])
595  let
596  (
597  j = (i == 0) ? n-1 : i-1,
598 
599  xc = c[k[j]][0],
600  yc = c[k[j]][1],
601 
602  xn = c[k[i]][0],
603  yn = c[k[i]][1],
604 
605  cd = (xc*yn - xn*yc)
606  )
607  [(xc + xn) * cd, (yc + yn) * cd]
608  ],
609 
610  sc = sum(cv),
611  sa = polygon_area(c, pm, true)
612  )
613  sc/(6*sa);
614 
615 //! Compute the winding number of a polygon about a point in a Euclidean 2d-space.
616 /***************************************************************************//**
617  \param c <coords-2d> A list of 2d cartesian coordinates
618  [[x, y], ...].
619  \param p <integer-list-list> An \em optional list of paths that
620  define one or more closed shapes where each is a list of
621  coordinate indexes.
622  \param t <point-2d> A test point coordinate [x, y].
623 
624  \returns <integer> The winding number.
625 
626  \details
627 
628  Computes the [winding number], the total number of counterclockwise
629  turns that the polygon paths makes around the test point in a
630  Euclidean 2d-space. Will be 0 \em iff the point is outside of the
631  polygon. Function patterned after [Dan Sunday, 2012].
632 
633  \copyright
634 
635  Copyright 2000 softSurfer, 2012 Dan Sunday
636  This code may be freely used and modified for any purpose
637  providing that this copyright notice is included with it.
638  iSurfer.org makes no warranty for this code, and cannot be held
639  liable for any real or imagined damage resulting from its use.
640  Users of this code must verify correctness for their application.
641 
642  [Dan Sunday, 2012]: http://geomalgorithms.com/a03-_inclusion.html
643  [winding number]: https://en.wikipedia.org/wiki/Winding_number
644 
645  When \p p is not defined, the listed order of the coordinates will
646  be used.
647 
648  \warning Where there are secondary paths, the vertex ordering of each
649  must be the same as the primary path.
650 *******************************************************************************/
651 function polygon_winding
652 (
653  c,
654  p,
655  t
656 ) =
657  let
658  (
659  pm = defined_or(p, [consts(len(c))]),
660 
661  wv =
662  [
663  for (k = pm) let (n = len(k))
664  for (i=[0 : n-1])
665  let
666  (
667  j = (i == 0) ? n-1 : i-1,
668 
669  t = (
670  (c[k[j]][1] <= t[1]) && (c[k[i]][1] > t[1])
671  && (is_left_ppp(c[k[j]], c[k[i]], t) > 0)
672  ) ? +1
673  : (
674  (c[k[j]][1] > t[1]) && (c[k[i]][1] <= t[1])
675  && (is_left_ppp(c[k[j]], c[k[i]], t) < 0)
676  ) ? -1
677  : 0
678  )
679  t
680  ]
681  )
682  sum(wv);
683 
684 //! @}
685 
686 //----------------------------------------------------------------------------//
687 // shape property tests
688 //----------------------------------------------------------------------------//
689 
690 //! \name Tests
691 //! @{
692 
693 //! Test the vertex ordering of a polygon in a Euclidean 2d-space.
694 /***************************************************************************//**
695  \param c <coords-2d> A list of 2d cartesian coordinates
696  [[x, y], ...].
697  \param p <integer-list-list> An \em optional list of paths that
698  define one or more closed shapes where each is a list of
699  coordinate indexes.
700 
701  \returns <boolean> \b true if the vertex are ordered \em clockwise,
702  \b false if the vertex are \em counterclockwise ordered, and
703  \b undef if the ordering can not be determined.
704 
705  \details
706 
707  When \p p is not defined, the listed order of the coordinates will
708  be used.
709 *******************************************************************************/
710 function polygon_is_clockwise
711 (
712  c,
713  p
714 ) =
715  let
716  (
717  sa = polygon_area(c, p, true)
718  )
719  (sa < 0) ? true
720  : (sa > 0) ? false
721  : undef;
722 
723 //! Test the convexity of a polygon in a Euclidean 2d-space.
724 /***************************************************************************//**
725  \param c <coords-2d> A list of 2d cartesian coordinates
726  [[x, y], ...].
727  \param p <integer-list-list> An \em optional list of paths that
728  define one or more closed shapes where each is a list of
729  coordinate indexes.
730 
731  \returns <boolean> \b true if the polygon is \em convex, \b false
732  otherwise.
733 
734  \details
735 
736  When \p p is not defined, the listed order of the coordinates will
737  be used.
738 *******************************************************************************/
739 function polygon_is_convex
740 (
741  c,
742  p
743 ) = is_undef(c) ? undef
744  : len(c) < 3 ? undef
745  : !all_len(c, 2) ? undef
746  : let
747  (
748  pm = defined_or(p, [consts(len(c))]),
749 
750  sv =
751  [
752  for (k = pm) let (n = len(k))
753  for (i=[0 : n-1])
754  sign(cross_ll([c[k[i]], c[k[(i+1)%n]]], [c[k[(i+1)%n]], c[k[(i+2)%n]]]))
755  ],
756 
757  us = unique(sv)
758  )
759  (len(us) == 1);
760 
761 //! Test if a point is inside a polygon in a Euclidean 2d-space using winding number.
762 /***************************************************************************//**
763  \param c <coords-2d> A list of 2d cartesian coordinates
764  [[x, y], ...].
765  \param p <integer-list-list> An \em optional list of paths that
766  define one or more closed shapes where each is a list of
767  coordinate indexes.
768  \param t <point-2d> A test point coordinate [x, y].
769 
770  \returns <boolean> \b true when the point is \em inside the polygon and
771  \b false otherwise.
772 
773  \details
774 
775  When \p p is not defined, the listed order of the coordinates will
776  be used.
777 
778  \sa polygon_winding for warning about secondary shapes.
779 *******************************************************************************/
780 function polygon_wn_is_p_inside
781 (
782  c,
783  p,
784  t
785 ) = (polygon_winding(c=c, p=p, t=t) != 0);
786 
787 //! Test if a point is inside a polygon in a Euclidean 2d-space using angle summation.
788 /***************************************************************************//**
789  \param c <coords-2d> A list of 2d cartesian coordinates
790  [[x, y], ...].
791  \param p <integer-list-list> An \em optional list of paths that
792  define one or more closed shapes where each is a list of
793  coordinate indexes.
794  \param t <point-2d> A test point coordinate [x, y].
795 
796  \returns <boolean> \b true when the point is \em inside the polygon and
797  \b false otherwise.
798 
799  \details
800 
801  See [Wikipedia] for more information.
802 
803  When \p p is not defined, the listed order of the coordinates will
804  be used.
805 
806  \warning This function does not track secondary shapes subtraction as
807  implemented by the polygon() function.
808 
809  [Wikipedia]: https://en.wikipedia.org/wiki/Point_in_polygon
810 *******************************************************************************/
811 function polygon_as_is_p_inside
812 (
813  c,
814  p,
815  t
816 ) =
817  let
818  (
819  pm = defined_or(p, [consts(len(c))]),
820 
821  av =
822  [
823  for (k = pm) let (n = len(k))
824  for (i=[0 : n-1])
825  let
826  (
827  j = (i == 0) ? n-1 : i-1
828  )
829  angle_ll([t, c[k[i]]], [t, c[k[j]]])
830  ],
831 
832  sa = abs(sum(av))
833  )
834  (sa > 180);
835 
836 //! @}
837 
838 //----------------------------------------------------------------------------//
839 // shape transforms
840 //----------------------------------------------------------------------------//
841 
842 //! \name Transforms
843 //! @{
844 
845 //! Convert a polygon in 2D to a polyhedron by adding a height dimension.
846 /***************************************************************************//**
847  \param c <coords-2d> A list of 2d cartesian coordinates
848  [[x, y], ...].
849  \param p <integer-list-list> An \em optional list of paths that
850  define one or more closed shapes where each is a list of
851  coordinate indexes.
852  \param h <decimal> The polyhedron height.
853  \param centroid <boolean> Center polygon centroid at z-axis.
854  \param center <boolean> Center polyhedron height about xy-plane.
855 
856  \returns <datastruct> A structure <tt>[points, faces]</tt>, where
857  \c points are <coords-3d> and \c faces are a
858  <integer-list-list>, that define the bounding box of the
859  given polyhedron.
860 
861  \details
862 
863  When \p p is not defined, the listed order of the coordinates will
864  be used.
865 *******************************************************************************/
867 (
868  c,
869  p,
870  h = 1,
871  centroid = false,
872  center = false
873 ) =
874  let
875  (
876  pm = defined_or(p, [consts(len(c))]),
877  pn = len([for (pi = pm) for (ci = pi) 1]),
878 
879  po = (centroid == true) ? polygon_centroid(c, p) : origin2d,
880  zr = (center == true) ? [-h/2, h/2] : [0, h],
881 
882  cw = polygon_is_clockwise (c, p),
883 
884  pp = [for (zi = zr) for (pi = pm) for (ci = pi) concat(c[ci] - po, zi)],
885  pf =
886  [
887  [for (pi = pm) for (ci = pi) ci],
888  [for (pi = pm) for (cn = [len(pi)-1 : -1 : 0]) pi[cn] + pn],
889  for (pi = pm) for (ci = pi)
890  (cw == true)
891  ? [ci, ci+pn, (ci+1)%pn+pn, (ci+1)%pn]
892  : [ci, (ci+1)%pn, (ci+1)%pn+pn, ci+pn]
893  ]
894  )
895  [pp, pf];
896 
897 //! @}
898 
899 //----------------------------------------------------------------------------//
900 // shape rounding
901 //----------------------------------------------------------------------------//
902 
903 //! \name Rounding
904 //! @{
905 
906 //! Compute coordinates for a constant radius vertex round between two edge vectors in 2D.
907 /***************************************************************************//**
908  \param r <decimal> The round radius.
909  \param m <integer> The round mode.
910  \param c <point-2d> The round center coordinate [x, y].
911  \param v1 <line-2d | decimal> The round start angle.
912  A 2d line, vector, or decimal angle 1.
913  \param v2 <line-2d | decimal> The round end angle.
914  A 2d line, vector, or decimal angle 2.
915  \param fn <integer> The number of [facets].
916  \param cw <boolean> The coordinate point ordering.
917 
918  \returns <coords-2d> A list of coordinates points [[x, y], ...].
919 
920  \details
921 
922  Normally, angle 1 should be less than angle 2. The edge coordinates
923  will start at angle 1, end at angle 2, and will have radius \p r
924  along a rounded transition from edge 1 to 2. When \p cw = \b true
925  the coordinates will start at edge 1 and increase toward edge 2.
926  When \p cw = \b false this ordering is reversed.
927 
928  The round mode may be one of the following:
929 
930  mode | name | description
931  :---:|:-----------:|:--------------------------------
932  1 | fillet | fillet from one edge to the next
933  2 | round | round from one edge to the next
934  3 | chamfer | bevel from one edge to the next
935 
936  [facets]: \ref get_fn()
937 *******************************************************************************/
938 function polygon_round_eve_p
939 (
940  r = 1,
941  m = 1,
942  c = origin2d,
943  v1 = x_axis2d_uv,
944  v2 = y_axis2d_uv,
945  fn,
946  cw = true
947 ) =
948  let
949  (
950  // create vectors if numerical angles have been specified.
951  va1 = is_number(v1) ? [cos(v1), sin(v1)] : v1,
952  va2 = is_number(v2) ? [cos(v2), sin(v2)] : v2,
953 
954  // triangle coordinates for edge corner in cw order
955  etc = [c + r*unit_l(va1), c, c + r*unit_l(va2)],
956 
957  // tangent circle radius
958  tcr = (m == 1) ?triangle2d_exradius(etc, 2) : 0,
959 
960  // tangent circle center coordinate
961  tcc = (m == 1) ?(c-r/(r-tcr) * triangle2d_excenter(etc, 2)) * (tcr-r)/tcr : c,
962 
963  // distance from vertex to inflection points
964  vim = (m == 1) ? sqrt( pow(distance_pp(c, tcc),2) - pow(r,2) ) : r,
965 
966  // inflection coordinates
967  tc1 = c + vim*unit_l(va1),
968  tc2 = c + vim*unit_l(va2),
969 
970  // vertex rounding coordinate point list
971  vpl = (m == 1) ? polygon_arc_p(r=r, c=tcc, v1=[tcc, tc1], v2=[tcc, tc2], fn=fn, cw=true)
972  : (m == 2) ? polygon_arc_p(r=r, c=tcc, v1=[tcc, tc1], v2=[tcc, tc2], fn=fn, cw=false)
973  : empty_lst,
974 
975  // cw ordering
976  pp = concat([tc1], vpl, [tc2])
977  )
978  (cw == true) ? pp : reverse(pp);
979 
980 //! Compute coordinates that round all of the vertices between each adjacent edges in 2D.
981 /***************************************************************************//**
982  \param c <coords-2d> A list of \em n 2d cartesian coordinates
983  [[x1, y1], [x2, y2], ..., [xn, yn]].
984  \param vr <decimal-list-n | decimal> The vertices rounding radius.
985  A list [v1r, v2r, v3r, ... vnr] of \em n decimals or a
986  single decimal for (v1r=v2r=v3r= ... =vnr). Undefined
987  vertices are not rounded.
988  \param vrm <integer-list-n | integer> The vertices rounding mode.
989  A list [v1rm, v2rm, v3rm, ... vnrm] of \em n integers or a
990  single integer for (v1rm=v2rm=v3rm= ... =vnrm). Undefined
991  vertices are not rounded.
992  \param vfn <integer-list-n> The vertices arc fragment number.
993  A list [v1fn, v2fn, v3fn, ... vnfn] of \em n integers or a
994  single integer for (v1fn=v2fn=v3fn= ... =vnfn).
995  \param w <boolean> Wrap-at-end during 3-point coordinate selection.
996  \param cw <boolean> Polygon vertex ordering.
997 
998  \returns <coords-2d> A new list of coordinates points [[x, y], ...]
999  that define the polygon with rounded vertices.
1000 
1001  \details
1002 
1003  Assumes polygon is defined in 2D space on the x-y plane. There
1004  should be no repeating adjacent vertices along the polygon path
1005  (ie: no adjacent vertex with identical coordinates). Any vertex
1006  determined to be collinear with its adjacent previous and next
1007  vertex is returned unmodified.
1008 
1009  Each vertex may be individually rounded using one of the following
1010  modes:
1011 
1012  mode | name | description
1013  :---:|:-------------------:|:--------------------------------------
1014  0 | none | return vertex unchanged
1015  1 | round | previous to next edge round
1016  2 | e-hollow / i-circle | previous to next edge inverse round
1017  3 | n-fillet | next edge pass return fillet
1018  4 | p-fillet | previous edge pass return fillet
1019  5 | chamfer | previous to next edge bevel
1020  6 | e-circle / i-hollow | previous to next edge inverse round
1021  7 | n-round | next edge pass return round
1022  8 | p-round | previous edge pass return round
1023  9 | n-chamfer | next edge pass return bevel
1024  10 | p-chamfer | previous edge pass return bevel
1025 
1026  The following diagrams demonstrate each rounding mode by on the
1027  upper right vertex of a rectangular polygon.
1028 
1029  \amu_define title (Rounding modes)
1030  \amu_combine image_views (prefix="top" "1 2 3 4 5 6 7 8 9 10")
1031  \amu_define image_size (vga)
1032  \amu_define scope_id (polygon_round_eve_all_p_modes)
1033  \amu_define output_scad (false)
1034  \amu_define html_image_w (128)
1035  \amu_define image_columns (5)
1036 
1037  \amu_include (include/amu/scope_diagrams_3d.amu)
1038 
1039  \amu_undefine (html_image_w image_columns)
1040 
1041  Vertex arc fragments can be specified using \p vfn. When any \p
1042  vnfn is \b undef, the special variables \p $fa, \p $fs, and \p $fn
1043  control facet generation. Each vertex is processed using 3-point
1044  (the previous and following vertex). The resulting triangle \ref
1045  triangle2d_incenter "incircles" and \ref triangle2d_excenter
1046  "excircles" are used to create the round and fillet \ref
1047  polygon_arc_p "arc" segments. All arcs and chamfers use constant
1048  radius.
1049 
1050  \amu_define title (Rounding example)
1051  \amu_define image_views (top)
1052  \amu_define image_size (sxga)
1053  \amu_define scope_id (polygon_round_eve_all_p_example)
1054  \amu_define output_scad (true)
1055 
1056  \amu_include (include/amu/scope_diagrams_3d.amu)
1057 *******************************************************************************/
1058 function polygon_round_eve_all_p
1059 (
1060  c,
1061  vr = 0,
1062  vrm = 1,
1063  vfn,
1064  w = true,
1065  cw = true
1066 ) =
1067  let
1068  (
1069  // constant vertex rounding radius, mode, and facets
1070  crr = is_scalar(vr) ? vr : 0,
1071  crm = is_scalar(vrm) ? vrm : 0,
1072  cfn = is_scalar(vfn) ? vfn : undef,
1073 
1074  // function assumes cw order, reverse if required
1075  cp = (cw == true) ? c : reverse(c),
1076 
1077  // adjacent vertices sequence [ [v[n-1], v[n], v[n+1]] ... ]
1078  avl = sequence_ns(cp, 3, w=w),
1079 
1080  // polygon coordinate point list
1081  ppl =
1082  [
1083  for ( i = [0 : len(avl)-1] )
1084  let
1085  (
1086  av = avl[i], // vertices [vp, vc, vn]
1087 
1088  vp = first(av), // vertex coordinate v[n-1]
1089  vc = second(av), // vertex coordinate v[n]
1090  vn = third(av), // vertex coordinate v[n+1]
1091 
1092  il = is_left_ppp(vp, vn, vc), // identify position of vc
1093 
1094  rr = defined_e_or(vr, i, crr), // vertex rounding radius
1095  rm = (rr == 0) ? 0 // vertex rounding mode
1096  : (il == 0) ? 0 // vp,vc,vn collinear, set rm=0
1097  : defined_e_or(vrm, i, crm),
1098  fn = defined_e_or(vfn, i, cfn), // vertex rounding arc fragments
1099 
1100  // reverse arc sweep on interior corners
1101  // not relevant for rm={0|5|9|10}
1102  ras = (il < 0),
1103 
1104  // tangent circle radius
1105  tcr = (rm == 0) ? 0
1106  : (rm == 1 || rm == 2) ?
1108  : (rm == 3) ?
1109  triangle2d_exradius(av, 1)
1110  : (rm == 4) ?
1111  triangle2d_exradius(av, 3)
1112  : 0,
1113 
1114  // tangent circle center coordinate
1115  tcc = (rm == 0) ? origin2d
1116  : (rm == 1 || rm == 2) ?
1117  (vc-rr/(rr-tcr) * triangle2d_incenter(av)) * (tcr-rr)/tcr
1118  : (rm == 3) ?
1119  (vc-rr/(rr-tcr) * triangle2d_excenter(av, 1)) * (tcr-rr)/tcr
1120  : (rm == 4) ?
1121  (vc-rr/(rr-tcr) * triangle2d_excenter(av, 3)) * (tcr-rr)/tcr
1122  : origin2d,
1123 
1124  // distance from vertex to inflection points
1125  vim = (rm == 0) ? 0
1126  : (rm <= 4) ?
1127  sqrt( pow(distance_pp(vc, tcc),2) - pow(rr,2) )
1128  : rr,
1129 
1130  // inflection coordinates
1131  tc1 = (rm == 0 || rm > 10) ? origin2d
1132  : (rm == 3 || rm == 7 || rm == 9) ?
1133  vc + vim * unit_l([vp, vc])
1134  : vc + vim * unit_l([vc, vp]),
1135 
1136  tc2 = (rm == 0 || rm > 10) ? origin2d
1137  : (rm == 4 || rm == 8 || rm == 10) ?
1138  vc + vim * unit_l([vn, vc])
1139  : vc + vim * unit_l([vc, vn]),
1140 
1141  // vertex rounding coordinate point list
1142  vpl = (rm == 0 || rm > 10) ? [vc]
1143  : (rm == 1) ?
1144  polygon_arc_p(r=rr, c=tcc, v1=[tcc, tc1], v2=[tcc, tc2], fn=fn, cw=!ras)
1145  : (rm == 2 || rm == 3 || rm == 4) ?
1146  polygon_arc_p(r=rr, c=tcc, v1=[tcc, tc1], v2=[tcc, tc2], fn=fn, cw=ras)
1147  : (rm == 6 || rm == 7 || rm == 8) ?
1148  polygon_arc_p(r=rr, c=vc, v1=[vc, tc1], v2=[vc, tc2], fn=fn, cw=!ras)
1149  : [tc1, tc2]
1150  )
1151  vpl
1152  ],
1153 
1154  // polygon points
1155  pp = merge_s( ppl )
1156  )
1157  (cw == true) ? pp : reverse(pp);
1158 
1159 //! @}
1160 
1161 //----------------------------------------------------------------------------//
1162 // interpreter
1163 //----------------------------------------------------------------------------//
1164 
1165 //! \name Interpreter
1166 //! @{
1167 
1168 //! Generate list of coordinate points from simple operation step notation.
1169 /***************************************************************************//**
1170  \param s <datastruct> The list of steps.
1171  \param i <point-2d> The initial coordinate [x, y].
1172  \param c <integer> (an internal recursion step count)
1173 
1174  \returns <point-2d-list> The list of coordinate points.
1175 
1176  \details
1177 
1178  This function is a simple interpreter that converts a list of
1179  operation steps into coordinate points for the construction of
1180  polygons. It provides a convenient way to construct polygons using
1181  a simple notation. It is inspired by the implementation of the
1182  [Turtle graphics] geometric drawing language. Each step produces a
1183  new output point or points and follow the following schema:
1184 
1185  Data structure schema:
1186 
1187  name | schema
1188  ---------------:|:----------------------------------------------
1189  s | [ step, step, ..., step ]
1190  step | [ operation, arguments ]
1191  arguments | [ arg, arg, ..., arg ]
1192 
1193  The following table summarized the available operations and their
1194  semantics.
1195 
1196  operation | short | arguments | output coordinate point
1197  :-----------|:-----:|:-----------------:|:-----------------------
1198  move_xy | mxy | [x, y] | [x, y]
1199  move_x | mx | x | [x, i.y]
1200  move_y | my | y | [i.x, y]
1201  delta_xy | dxy | [x, y] | i + [x, y]
1202  delta_x | dx | x | i + [x, 0]
1203  delta_y | dy | y | i + [0, y]
1204  delta_xa | dxa | [x, a] | i + [ x, x * tan(a) ]
1205  delta_ya | dya | [y, a] | i + l y / tan(a), y ]
1206  delta_v | dv | [m, a] | i + line(m, a)
1207  arc_pv | apv | [c, v, cw] | (see below)
1208  arc_vv | avv | [v, v, cw] | (see below)
1209 
1210  When an operation requires only one argument, the argument can be
1211  specified as a scalar-value or a single-element list.
1212 
1213  ## Operations
1214 
1215  ### arc_pv
1216 
1217  e | data type | parameter description
1218  ---:|:-------------------------------------:|:------------------------------------
1219  c | <point-2d> | arc center point [x, y]
1220  v | <point-2d> \| <decimal> | arc stop angle [x, y] or a
1221  cw | <boolean> | arc sweep direction
1222 
1223  This operation constructs an arc about the center point, specified
1224  as a point coordinate. The arc begins at the angle formed by the
1225  vector <tt>[c, i]</tt> and ends at the vector formed by either
1226  <tt>[c, v]</tt> or the angle \p v (specified in degrees). The arc
1227  sweep direction is controlled by the parameter \p cw. When \p cw is
1228  assigned \b true, the arc is swept clockwise from the start angle
1229  to the stop angle.
1230 
1231  ### arc_vv
1232 
1233  e | data type | parameter description
1234  ---:|:-------------------------------------:|:------------------------------------
1235  c | <point-2d> | arc center point [m, a]
1236  v | <point-2d> \| <decimal> | arc stop angle [x, y] or a
1237  cw | <boolean> | arc sweep direction
1238 
1239  This operation constructs an arc about the center point, specified
1240  as a vector <tt>[m, a]</tt> beginning from the start point. The arc
1241  begins at the angle formed by the vector <tt>[c, i]</tt> and ends
1242  at the vector formed by either <tt>[c, v]</tt> or the angle \p v
1243  (specified in degrees). The arc sweep direction is controlled by
1244  the parameter \p cw. When \p cw is assigned \p true, the arc is
1245  swept clockwise from the start angle to the stop angle.
1246 
1247  \amu_define title (Motor mount plate design example)
1248  \amu_define image_views (top diag)
1249  \amu_define image_size (sxga)
1250  \amu_define scope_id (polygon_turtle_p)
1251  \amu_define output_scad (true)
1252 
1253  \amu_include (include/amu/scope_diagrams_3d.amu)
1254 
1255  The corners of this example 2d design plate have been rounded with
1256  the library function polygon_round_eve_all_p().
1257 
1258  [Turtle graphics]: https://en.wikipedia.org/wiki/Turtle_(robot)
1259 *******************************************************************************/
1260 function polygon_turtle_p
1261 (
1262  s,
1263  i = origin2d,
1264  c = 0
1265 ) = ! is_list( s ) ? empty_lst
1266  : let
1267  ( // get current step
1268  stp = first( s ),
1269 
1270  // get operation and argument vector
1271  opr = first( stp ),
1272  arv = second( stp ),
1273  arc = is_undef( arv ) ? 0 : is_list( arv ) ? len( arv ) : 1,
1274 
1275  // assign arguments
1276  a1 = defined_e_or( arv, 0, arv ),
1277  a2 = defined_e_or( arv, 1, undef ),
1278  a3 = defined_e_or( arv, 2, undef ),
1279 
1280  // compute coordinate point(s) for current operation
1281  p = (opr == "mxy" || opr == "move_xy" ) && (arc == 2) ? [a1, a2]
1282 
1283  : (opr == "mx" || opr == "move_x" ) && (arc == 1) ? [a1, i.y]
1284  : (opr == "my" || opr == "move_y" ) && (arc == 1) ? [i.x, a1]
1285 
1286  : (opr == "dxy" || opr == "delta_xy" ) && (arc == 2) ? i + [a1, a2]
1287 
1288  : (opr == "dx" || opr == "delta_x" ) && (arc == 1) ? i + [a1, 0]
1289  : (opr == "dy" || opr == "delta_y" ) && (arc == 1) ? i + [0, a1]
1290 
1291  : (opr == "dxa" || opr == "delta_xa" ) && (arc == 2) ? i + [a1, a1 * tan(a2)]
1292  : (opr == "dya" || opr == "delta_ya" ) && (arc == 2) ? i + [a1 / tan(a2), a1]
1293 
1294  : (opr == "dv" || opr == "delta_v" ) && (arc == 2) ? line_tp( line2d_new(m=a1, a=a2, p1=i) )
1295 
1296  : (opr == "apv" || opr == "arc_pv" ) && (arc == 3) ?
1297  let
1298  ( // handle scalar angle or compute angle from vector
1299  v2 = is_list(a2) ? [a1, a2] : a2
1300  )
1301  polygon_arc_p( r=distance_pp(i, a1), c=a1, v1=[a1, i], v2=v2, cw=a3 )
1302 
1303  : (opr == "avv" || opr == "arc_vv" ) && (arc == 3) ?
1304  let
1305  ( // calculate center point 'b1' from given vector [m, a] in 'a1'
1306  b1 = line_tp( line2d_new(m=first(a1), a=second(a1), p1=i) ),
1307  v2 = is_list(a2) ? [b1, a2] : a2
1308  )
1309  polygon_arc_p( r=distance_pp(i, b1), c=b1, v1=[b1, i], v2=v2, cw=a3 )
1310 
1311  : assert
1312  ( false,
1313  str ( "ERROR at '", stp, "', num='", c, "', operation='", opr
1314  , "', argc='", arc, "', argv='", arv,"'" )
1315  ),
1316 
1317  ls = len( s ), // current step count
1318  lp = len( p ), // points count in current step
1319  cp = (lp > 2) ? p : [p], // point-list for current step
1320  ni = (lp > 2) ? last(p) : p // initial point for next step
1321  )
1322  // check if have reached last step (ls == 1)?
1323  // yes : terminate recursion
1324  // no : pop current step and process remaining
1325  ( ls == 1 ) ? cp : concat( cp, polygon_turtle_p( tailn(s), ni, c+1 ) );
1326 
1327 //! @}
1328 
1329 //! @}
1330 //! @}
1331 
1332 //----------------------------------------------------------------------------//
1333 // openscad-amu auxiliary scripts
1334 //----------------------------------------------------------------------------//
1335 
1336 /*
1337 BEGIN_SCOPE polygon_turtle_p;
1338  BEGIN_OPENSCAD;
1339  include <omdl-base.scad>;
1340 
1341  $fn=36;
1342 
1343  h1 = 7.5; h2 = 5; h3 = 7.5;
1344  w1 = 5; w2 = 20; w3 = 5;
1345  r1 = 5; r2 = 1/2; rr = 1;
1346 
1347  sm =
1348  [
1349  ["delta_y", h1],
1350  ["delta_x", w1],
1351  ["delta_y", h2],
1352  ["delta_xy", [w3, h3]],
1353  ["delta_x", w1+w2-w3*2],
1354  ["delta_xy", [w3, -h3]],
1355  ["move_y", 0],
1356  ["move_x", 0],
1357  ];
1358 
1359  // convert the step moves into coordinates
1360  pp = polygon_turtle_p( sm );
1361 
1362  // round all of the vertices
1363  rp = polygon_round_eve_all_p( pp, rr );
1364 
1365  difference()
1366  {
1367  polygon( rp );
1368 
1369  c = [w1+w2/2+r1/2, h1+h2/2];
1370  translate(c) circle(r=r1);
1371  for(x=[-1,1], y=[-1,1]) translate(c+[x,y]*r1) circle(r=r2);
1372  }
1373 
1374  // end_include
1375  END_OPENSCAD;
1376 
1377  BEGIN_MFSCRIPT;
1378  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
1379  table_unset_all sizes;
1380 
1381  images name "sizes" types "sxga";
1382  views name "views" views "top diag";
1383 
1384  variables set_opts_combine "sizes views";
1385  variables add_opts "--viewall --autocenter";
1386 
1387  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1388  END_MFSCRIPT;
1389 END_SCOPE;
1390 */
1391 
1392 /*
1393 BEGIN_SCOPE polygon_round_eve_all_p_modes;
1394  BEGIN_OPENSCAD;
1395  include <omdl-base.scad>;
1396 
1397  $fn=36;
1398 
1399  mode = 0;
1400 
1401  pp = polygon_trapezoid_p(10, 7);
1402  rp = polygon_round_eve_all_p( pp, 2.5, [0, 0, mode, 0] );
1403 
1404  polygon( rp );
1405 
1406  // end_include
1407  END_OPENSCAD;
1408 
1409  BEGIN_MFSCRIPT;
1410  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
1411  table_unset_all sizes;
1412 
1413  images name "sizes" types "vga";
1414  views name "views" views "top";
1415  defines name "modes" define "mode" integers "0 1 2 3 4 5 6 7 8 9 10";
1416 
1417  variables set_opts_combine "sizes views modes";
1418  variables add_opts "--viewall --autocenter";
1419 
1420  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1421  END_MFSCRIPT;
1422 END_SCOPE;
1423 
1424 BEGIN_SCOPE polygon_round_eve_all_p_example;
1425  BEGIN_OPENSCAD;
1426  include <omdl-base.scad>;
1427 
1428  $fn=36;
1429 
1430  c = [[1,1], [1,10], [10,12], [18,2]];
1431  r = [1, 1, 5, 8];
1432  m = [2, 3, 4, 3];
1433  n = [3, 8, undef, undef];
1434 
1435  p = polygon_round_eve_all_p(c=c, vr=r, vrm=m, vfn=n);
1436 
1437  polygon( p );
1438 
1439  // end_include
1440  END_OPENSCAD;
1441 
1442  BEGIN_MFSCRIPT;
1443  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
1444  table_unset_all sizes;
1445 
1446  images name "sizes" types "sxga";
1447  views name "views" views "top";
1448 
1449  variables set_opts_combine "sizes views";
1450  variables add_opts "--viewall --autocenter";
1451 
1452  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1453  END_MFSCRIPT;
1454 END_SCOPE;
1455 */
1456 
1457 //----------------------------------------------------------------------------//
1458 // end of file
1459 //----------------------------------------------------------------------------//
origin2d
<point-2d> The origin point coordinate in 2d Euclidean space.
Definition: constants.scad:407
x_axis2d_uv
<vector-2d> The unit vector of the positive x-axis in 2d Euclidean space.
Definition: constants.scad:410
y_axis2d_uv
<vector-2d> The unit vector of the positive y-axis in 2d Euclidean space.
Definition: constants.scad:413
pi
<decimal> The ratio of a circle's circumference to its diameter.
Definition: constants.scad:198
empty_lst
<list> A list with no values (the empty list).
Definition: constants.scad:304
function line_tp(l)
Return the terminal point of a line or vector.
function 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 cross_ll(l1, l2)
Compute the cross product of two lines or vectors in a 3d or 2d-space.
function is_left_ppp(p1, p2, p3)
Test if a point is left, on, or right of an infinite line in a 2d-space.
function line2d_new(m=1, a=0, p1=origin2d, p2, v)
Construct a 2 dimensional directed line.
function unit_l(l)
Compute the normalized unit vector of a line or vector.
function interpolate2d_l_pp(p1, p2, x, y)
Linearly interpolate along a line established by two points in 2d.
function distance_pp(p1, p2)
Compute the distance between two points.
function unique(v)
Return a list of the unique elements of an iterable value.
function defined_e_or(v, i, d)
Return an element of an iterable when it exists or a default value otherwise.
function third(v)
Return the third element of an iterable value.
function last(v)
Return the last element of an iterable value.
function second(v)
Return the second element of an iterable value.
function first(v)
Return the first element of an iterable value.
function sequence_ns(v, n=1, s=1, w=false)
Return a list of all n-element sequential-subsets of an iterable value.
function tailn(v, n=1)
Return a list containing all but the first n elements of an iterable value.
function reverse(v)
Reverse the elements of an iterable value.
function all_len(v, l, c=0)
Test if all elements of an iterable value are iterable with a fixed length.
function almost_eq_nv(v1, v2, p=6)
Test to see if two numerical vectors are sufficiently equal.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function merge_s(v, r=false)
Serially merge the elements of a list.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function defined_or(v, d)
Return given value, if defined, or a secondary value, if primary is not defined.
function is_scalar(v)
Test if a value is a single non-iterable value.
function is_defined(v)
Test if a value is defined.
function is_number(v)
Test if a value is a number.
function polygon_arc_p(r=1, c=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_perimeter(n, r, a)
Compute the perimeter of an n-sided regular polygon in 2D.
function polygon3d_area(c, p, n)
Compute the area of a polygon in a Euclidean 3d-space.
function polygon_regular_area(n, r, a)
Compute the area of an n-sided regular polygon in 2D.
function polygon_winding(c, p, t)
Compute the winding number of a polygon about a point in a Euclidean 2d-space.
function polygon_trapezoid_p(b=1, h, l=1, a=90, o=origin2d, cw=true)
Compute the coordinates for a rounded trapezoid in 2D space.
function polygon_linear_extrude_pf(c, p, h=1, centroid=false, center=false)
Convert a polygon in 2D to a polyhedron by adding a height dimension.
function polygon_regular_p(n, r, a, c=origin2d, o=0, vr, cw=true)
Compute coordinates for an n-sided regular polygon in 2D.
function polygon_as_is_p_inside(c, p, t)
Test if a point is inside a polygon in a Euclidean 2d-space using angle summation.
function polygon_round_eve_all_p(c, vr=0, vrm=1, vfn, w=true, cw=true)
Compute coordinates that round all of the vertices between each adjacent edges in 2D.
function polygon_is_convex(c, p)
Test the convexity of a polygon in a Euclidean 2d-space.
function polygon_round_eve_p(r=1, m=1, c=origin2d, v1=x_axis2d_uv, v2=y_axis2d_uv, fn, cw=true)
Compute coordinates for a constant radius vertex round between two edge vectors in 2D.
function polygon_wn_is_p_inside(c, p, t)
Test if a point is inside a polygon in a Euclidean 2d-space using winding number.
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 polygon_centroid(c, p)
Compute the center of mass of a polygon in a Euclidean 2d-space.
function polygon_turtle_p(s, i=origin2d, c=0)
Generate list of coordinate points from simple operation step notation.
function polygon_area(c, p, s=false)
Compute the signed area of a polygon in a Euclidean 2d-space.
function polygon_perimeter(c, p)
Calculate the perimeter length of a polygon in 2d.
function polygon_is_clockwise(c, p)
Test the vertex ordering of a polygon in a Euclidean 2d-space.
function polygon_elliptical_sector_p(r=1, c=origin2d, v1=x_axis2d_uv, v2=x_axis2d_uv, s=true, fn, cw=true)
Compute coordinates for an elliptical sector in 2D.
function triangle2d_inradius(c)
Compute the inradius of a triangle's incircle in 2D.
function triangle2d_excenter(c, v=1)
Compute the center coordinate of a triangle's excircle in 2D.
function triangle2d_exradius(c, v=1)
Compute the exradius of a triangle's excircle in 2D.
function triangle2d_incenter(c)
Compute the center coordinate of a triangle's incircle in 2D.
function get_fn(r)
Return facets number for the given arc radius.