omdl  v0.6.1
OpenSCAD Mechanical Design Library
math_polytope.scad
Go to the documentation of this file.
1 //! Polygon and polyhedron mathematical functions.
2 /***************************************************************************//**
3  \file math_polytope.scad
4  \author Roy Allen Sutton
5  \date 2017
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  \ingroup math math_polytope
31 *******************************************************************************/
32 
33 include <math-base.scad>;
34 
35 //----------------------------------------------------------------------------//
36 /***************************************************************************//**
37  \addtogroup math
38  @{
39 
40  \defgroup math_polytope Polytopes
41  \brief Polygon and polyhedron mathematical functions.
42  @{
43 *******************************************************************************/
44 //----------------------------------------------------------------------------//
45 
46 //----------------------------------------------------------------------------//
47 // polytope
48 //----------------------------------------------------------------------------//
49 
50 //! List the edge coordinate index pairs of a polytope.
51 /***************************************************************************//**
52  \param f <integer-list-list> A list of faces (or paths) that enclose
53  the shape where each face is a list of coordinate indexes.
54 
55  \returns <integer-list-2-list> A list of edges where each edge is
56  a list of two coordinate indexes that form the shape.
57 
58  \details
59 
60  \note Although the edge list is not sorted, each pair is sorted
61  with the smallest index first.
62 *******************************************************************************/
63 function polytope_faces2edges
64 (
65  f
66 ) =
67  let
68  (
69  el =
70  [
71  for (ip = [for (fi = f) for (ai = nssequence(fi, n=2, s=1, w=true)) ai])
72  [min(ip), max(ip)]
73  ]
74  )
75  unique(el);
76 
77 //! Determine the bounding limits of a polytope.
78 /***************************************************************************//**
79  \param c <coords-3d|coords-2d> A list of 3d or 2d cartesian
80  coordinates [[x, y (, z)], ...].
81  \param f <integer-list-list> A list of faces (or paths) that enclose
82  the shape where each face is a list of coordinate indexes.
83  \param a <decimal-list-1:3|decimal> The box padding.
84  A list of lengths to equally pad the box dimensions.
85  \param d <range|list|integer> The dimensions to consider. A range
86  of dimensions, a list of dimensions, or a single dimension.
87  \param s <boolean> Return box size rather than coordinate limits.
88 
89  \returns <datastruct> A list with the bounding-box limits (see: table).
90 
91  \details
92 
93  The returned list will be of the following form:
94 
95  | | s | x | y | z | datastruct form |
96  |:---:|:-----:|:---------:|:---------:|:---------:|:---------------------:|
97  | 2d | false | [min,max] | [min,max] | - | decimal-list-2-list-2 |
98  | 2d | true | max-min | max-min | - | decimal-list-list-2 |
99  | 3d | false | [min,max] | [min,max] | [min,max] | decimal-list-2-list-3 |
100  | 3d | true | max-min | max-min | max-min | decimal-list-list-3 |
101 
102  \note When \p f is not specified, all coordinates are used to
103  determine the geometric limits, which, simplifies the
104  calculation. Parameter \p f is needed when a subset of the
105  coordinates should be considered.
106  \warning This function does not track secondary shapes subtraction as
107  implemented by the polygon() function.
108 *******************************************************************************/
109 function polytope_limits
110 (
111  c,
112  f,
113  a,
114  d = [0:2],
115  s = true
116 ) =
117  [
118  let
119  (
120  ax = is_range(d) ? [for (di=d) di]
121  : is_list(d) ? d
122  : is_integer(d) ? [d]
123  : undef,
124 
125  ad = (is_defined(a) && is_scalar(a)) ? a : 0,
126  ap = [for (j = ax) edefined_or(a, j, ad)],
127 
128  pm = is_defined(f)
129  ? [for (j = ax) [for (m = f) for (i=[0 : len(m)-1]) c[m[i]][j]]]
130  : [for (j = ax) [for (i = c) i[j]]],
131 
132  b = [for (j = ax) [min(pm[j]), max(pm[j])] + [-ap[j]/2, +ap[j]/2]]
133  )
134  for (j = ax)
135  (s == true) ? b[j][1] - b[j][0] : [b[j][0], b[j][1]]
136  ];
137 
138 //! Generate a bounding box polytope for another polytope in 3d or 2d.
139 /***************************************************************************//**
140  \param c <coords-3d|coords-2d> A list of 3d or 2d cartesian
141  coordinates [[x, y (, z)], ...].
142  \param f <integer-list-list> A list of faces (or paths) that enclose
143  the shape where each face is a list of coordinate indexes.
144  \param a <decimal-list-1:3|decimal> The box padding.
145  A list of lengths to equally pad the box dimensions.
146 
147  \returns <datastruct> A structure: (1) <tt>[points, faces]</tt>,
148  where \c points are <coords-3d> and \c faces are a
149  <integer-list-list>, that define the bounding box of the
150  given polyhedron. Or: (2) <tt>[points, path]</tt>, where
151  \c points are <coords-2d> and \c path is a
152  <integer-list-list>, that define the bounding box of the
153  given polygon.
154 
155  \details
156 
157  Polyhedron faces will be ordered \em clockwise when looking from
158  outside the shape inwards. Polygon path will be ordered clockwise
159  when looking from the top (positive z) downwards.
160 
161  \note When \p f is not specified, all coordinates are used to
162  determine the geometric limits, which, simplifies the
163  calculation. Parameter \p f is needed when a subset of the
164  coordinates should be considered.
165 
166  \sa polytope_limits for warning about secondary shapes.
167 *******************************************************************************/
168 function polytope_bbox_pf
169 (
170  c,
171  f,
172  a
173 ) = let
174  (
175  b = polytope_limits(c=c, f=f, a=a, d=[0:2], s=false),
176  d = len([for (i=b) if (i != [undef, undef]) i])
177  )
178  (d == 3) ?
179  [ [for (x=b[0], y=b[1], z=b[2]) [x, y, z]],
180  [[0,2,3,1], [4,0,1,5], [6,4,5,7], [2,6,7,3], [0,4,6,2], [3,7,5,1]] ]
181  : [ [for (x=b[0], y=b[1]) [x, y]],
182  [[0,1,3,2]] ];
183 
184 //! Get a line from an edge or any two vetices of a polytope.
185 /***************************************************************************//**
186  \param c <coords-3d|coords-2d> A list of 3d or 2d coordinate points.
187 
188  \param f <integer-list-list> A list of faces (or paths) that enclose
189  the shape where each face is a list of coordinate indexes.
190  \param e <integer-list-2-list> A list of edges where each edge is
191  a list of two coordinate indexes.
192 
193  \param i <integer> A line specified as an edge index.
194  \param l <integer-list-2> A line specified as a list of coordinate
195  index pairs.
196 
197  \param r <boolean> Reverse the line start and end points.
198 
199  \returns <line-3d|line-2d> The line as a pair of coordinates.
200 
201  \details
202 
203  \note Parameter \p f is optional for polygons. When it is not
204  given, the listed order of the coordinates \p c establishes
205  the polygon path.
206  \note When \p e is not specified, it is computed from \p f using
207  polytope_faces2edges() iff the line is identified by \p i.
208 *******************************************************************************/
209 function polytope_line
210 (
211  c,
212  f,
213  e,
214  i,
215  l,
216  r = false
217 ) = is_defined(l) ? [c[first(l)], c[second(l)]]
218  : not_defined(i) ? undef
219  : let
220  (
221  fm = defined_or(f, [consts(len(c))]),
222  el = defined_or(e, polytope_faces2edges(fm)),
223  sl = el[i]
224  )
225  (r == false)
226  ? [c[second(sl)], c[first(sl)]]
227  : [c[first(sl)], c[second(sl)]];
228 
229 //! List the adjacent vertices for a given polytope vertex.
230 /***************************************************************************//**
231  \param f <integer-list-list> A list of faces (or paths) that enclose
232  the shape where each face is a list of coordinate indexes.
233 
234  \param i <integer> A vertex index.
235 
236  \returns <integer-list> The list of adjacent vertex indexes for the
237  given vertex index.
238 
239  \details
240 
241  The adjacent vertices are those neighboring vertices that are
242  directly connected to the given vertex by a common edge.
243 
244  \note Parameter \p f is optional for polygons. When it is not
245  given, the listed order of the coordinates \p c establishes
246  the polygon path.
247 *******************************************************************************/
248 function polytope_vertex_av
249 (
250  f,
251  i
252 ) =
253  let
254  (
255  vn =
256  [
257  for (fi = f) let (fn = len(fi))
258  for (j = [0:fn-1])
259  if (i == fi[j])
260  for (k = [-1, 1])
261  fi[circular_index(j + k, fn)]
262  ]
263  )
264  unique(vn);
265 
266 //! List the adjacent face indexes for a polytope vertex.
267 /***************************************************************************//**
268  \param f <integer-list-list> A list of faces (or paths) that enclose
269  the shape where each face is a list of coordinate indexes.
270  \param i <integer> The vertex index.
271 
272  \returns <integer-list> The list of face indexes adjacent to the
273  given polytope vertex.
274 *******************************************************************************/
275 function polytope_vertex_af
276 (
277  f,
278  i
279 ) = [for (fi = [0:len(f)-1]) if (exists(i, f[fi])) fi];
280 
281 //! List the adjacent face indexes for a polytope edge.
282 /***************************************************************************//**
283  \param f <integer-list-list> A list of faces (or paths) that enclose
284  the shape where each face is a list of coordinate indexes.
285  \param e <integer-list-2-list> A list of edges where each edge is
286  a list of two coordinate indexes.
287  \param i <integer> The edge index.
288 
289  \returns <integer-list> The list of face indexes adjacent to the
290  given polytope edge.
291 
292  \details
293 
294  \note When \p e is not specified, it is computed from \p f using
295  polytope_faces2edges().
296 *******************************************************************************/
297 function polytope_edge_af
298 (
299  f,
300  e,
301  i
302 ) = let (el = is_defined(e) ? e : polytope_faces2edges(f))
303  [
304  for (fi = [0:len(f)-1])
305  if (exists(first(el[i]), f[fi]) && exists(second(el[i]), f[fi]))
306  fi
307  ];
308 
309 //! Get a normal vector for a polytope vertex.
310 /***************************************************************************//**
311  \param c <coords-3d|coords-2d> A list of 3d or 2d coordinate points.
312  \param f <integer-list-list> A list of faces (or paths) that enclose
313  the shape where each face is a list of coordinate indexes.
314  \param i <integer> The vertex index.
315 
316  \returns <vector-3d> A normal vector for the polytope vertex.
317 
318  \details
319 
320  The normal is computed as the mean of the adjacent faces.
321 
322  \note Parameter \p f is optional for polygons. When it is not
323  given, the listed order of the coordinates \p c establishes
324  the polygon path.
325 *******************************************************************************/
326 function polytope_vertex_n
327 (
328  c,
329  f,
330  i
331 ) = let
332  (
333  fm = defined_or(f, [consts(len(c))])
334  )
335  mean([for (j=polytope_vertex_af(fm, i)) unit_l(polytope_face_n(c, l=fm[j]))]);
336 
337 //! Get a normal vector for a polytope edge.
338 /***************************************************************************//**
339  \param c <coords-3d|coords-2d> A list of 3d or 2d coordinate points.
340  \param f <integer-list-list> A list of faces (or paths) that enclose
341  the shape where each face is a list of coordinate indexes.
342  \param e <integer-list-2-list> A list of edges where each edge is
343  a list of two coordinate indexes.
344  \param i <integer> The edge index.
345 
346  \returns <vector-3d> A normal vector for the polytope edge.
347 
348  \details
349 
350  The normal is computed as the mean of the adjacent faces.
351 
352  \note Parameter \p f is optional for polygons. When it is not
353  given, the listed order of the coordinates \p c establishes
354  the polygon path.
355  \note When \p e is not specified, it is computed from \p f using
356  polytope_faces2edges() iff the line is identified by \p i.
357 *******************************************************************************/
358 function polytope_edge_n
359 (
360  c,
361  f,
362  e,
363  i
364 ) = let
365  (
366  fm = defined_or(f, [consts(len(c))]),
367  el = is_defined(e) ? e : polytope_faces2edges(fm)
368  )
369  mean([for (j=polytope_edge_af(fm, el, i)) unit_l(polytope_face_n(c, l=fm[j]))]);
370 
371 //! Get the normal vector of a polytope face.
372 /***************************************************************************//**
373  \param c <coords-3d|coords-2d> A list of 3d or 2d coordinate points.
374  \param f <integer-list-list> A list of faces (or paths) that enclose
375  the shape where each face is a list of coordinate indexes.
376 
377  \param i <integer> The face specified as an face index.
378  \param l <integer-list> The face-plane specified as a list of three
379  or more coordinate indexes that are a part of the face.
380 
381  \param cw <boolean> Face vertex ordering.
382 
383  \returns <vector-3d> The normal vector of a polytope face.
384 
385  \details
386 
387  The face can be identified using either parameter \p i or \p l.
388  When using \p l, the parameter \p f is not required.
389 
390  \note Parameter \p f is optional for polygons. When it is not
391  given, the listed order of the coordinates \p c establishes
392  the polygon path.
393 *******************************************************************************/
394 function polytope_face_n
395 (
396  c,
397  f,
398  i,
399  l,
400  cw = true
401 ) = let
402  (
403  ci = is_defined(l) ? l : defined_or(f, [consts(len(c))])[i],
404  pc = [for (i = [0:2]) let (p = c[ci[i]]) (len(p) == 3) ? p : [p[0], p[1], 0]]
405  )
406  cross(pc[0]-pc[1], pc[2]-pc[1]) * ((cw == true) ? 1 : -1);
407 
408 //! Get the mean coordinate of all vertices of a polytope face.
409 /***************************************************************************//**
410  \param c <coords-3d|coords-2d> A list of 3d or 2d coordinate points.
411  \param f <integer-list-list> A list of faces (or paths) that enclose
412  the shape where each face is a list of coordinate indexes.
413 
414  \param i <integer> The face specified as an face index.
415  \param l <integer-list> The face specified as a list of all the
416  coordinate indexes that define it.
417 
418  \returns <coords-3d> The mean coordinate of a polytope face.
419 
420  \details
421 
422  The face can be identified using either parameter \p i or \p l.
423  When using \p l, the parameter \p f is not required.
424 
425  \note Parameter \p f is optional for polygons. When it is not
426  given, the listed order of the coordinates \p c establishes
427  the polygon path.
428 *******************************************************************************/
429 function polytope_face_m
430 (
431  c,
432  f,
433  i,
434  l
435 ) = let
436  (
437  ci = is_defined(l) ? l : defined_or(f, [consts(len(c))])[i],
438  pc = [for (i = ci) c[i]]
439  )
440  mean(pc);
441 
442 //! Get the mean coordinate and normal vector of a polytope face.
443 /***************************************************************************//**
444  \param c <coords-3d|coords-2d> A list of 3d or 2d coordinate points.
445  \param f <integer-list-list> A list of faces (or paths) that enclose
446  the shape where each face is a list of coordinate indexes.
447 
448  \param i <integer> The face specified as an face index.
449  \param l <integer-list> The face specified as a list of all the
450  coordinate indexes that define it.
451 
452  \param cw <boolean> Face vertex ordering.
453 
454  \returns <plane> <tt>[mp, nv]</tt>, where \c mp is \c coords-3d, the
455  mean coordinate, and \c nv is \c vector-3d, the normal
456  vector, of the polytope face-plane.
457 
458  \details
459 
460  The face can be identified using either parameter \p i or \p l.
461  When using \p l, the parameter \p f is not required.
462 
463  \note Parameter \p f is optional for polygons. When it is not
464  given, the listed order of the coordinates \p c establishes
465  the polygon path.
466 *******************************************************************************/
467 function polytope_face_mn
468 (
469  c,
470  f,
471  i,
472  l,
473  cw = true
474 ) = [polytope_face_m(c, f, i, l), polytope_face_n(c, f, i, l, cw)];
475 
476 //! Get a plane for a polytope face.
477 /***************************************************************************//**
478  \copydetails polytope_face_mn()
479 *******************************************************************************/
480 function polytope_plane
481 (
482  c,
483  f,
484  i,
485  l,
486  cw=true
487 ) = polytope_face_mn(c, f, i, l, cw);
488 
489 //! List the vertex counts for all polytope faces.
490 /***************************************************************************//**
491  \param f <integer-list-list> A list of faces (or paths) that enclose
492  the shape where each face is a list of coordinate indexes.
493 
494  \returns <integer-list> A list with a vertex count of every face.
495 *******************************************************************************/
496 function polytope_face_vcounts
497 (
498  f
499 ) = [for (fi=f) len(fi)];
500 
501 //! List the angles between all adjacent faces of a polyhedron.
502 /***************************************************************************//**
503  \param c <coords-3d> A list of 3d cartesian coordinates
504  [[x, y, z], ...].
505  \param f <integer-list-list> A list of faces that enclose
506  the shape where each face is a list of coordinate indexes.
507 
508  \returns <decimal-list> A list of the polyhedron adjacent face angles.
509 
510  \details
511 
512  See [Wikipedia] for more information on dihedral angles.
513 
514  [Wikipedia]: https://en.wikipedia.org/wiki/Dihedral_angle
515 *******************************************************************************/
516 function polytope_face_angles
517 (
518  c,
519  f
520 ) =
521  [
522  for (i=[0 : len(f)-1])
523  let
524  (
525  n1 = cross_ll([c[f[i][0]], c[f[i][1]]], [c[f[i][0]], c[f[i][2]]]),
526  af = [for (v=f[i]) for (j=[0 : len(f)-1]) if (j != i && exists(v, f[j])) j]
527  )
528  for (u=unique(af))
529  let
530  (
531  n2 = cross_ll([c[f[u][0]], c[f[u][1]]], [c[f[u][0]], c[f[u][2]]])
532  )
533  angle_ll(n1, n2)
534  ];
535 
536 //! List the edge lengths of a polytope.
537 /***************************************************************************//**
538  \param c <coords-3d|coords-2d> A list of 3d or 2d cartesian
539  coordinates [[x, y (, z)], ...].
540  \param e <integer-list-2-list> A list of edges where each edge is
541  a list of two coordinate indexes.
542 
543  \returns <decimal-list> A list of the polytope edge lengths.
544 *******************************************************************************/
545 function polytope_edge_lengths
546 (
547  c,
548  e
549 ) = [for (ei=e) distance_pp(c[ei[0]], c[ei[1]])];
550 
551 //! List the adjacent edge angles for each polytope vertex.
552 /***************************************************************************//**
553  \param c <coords-3d|coords-2d> A list of 3d or 2d cartesian
554  coordinates [[x, y (, z)], ...].
555  \param f <integer-list-list> A list of faces (or paths) that enclose
556  the shape where each face is a list of coordinate indexes.
557 
558  \returns <decimal-list> A list of the polytope adjacent edge angles.
559 *******************************************************************************/
560 function polytope_edge_angles
561 (
562  c,
563  f
564 ) =
565  [
566  for (k=[for (j=f) for (i=nssequence(j, n=3, s=1, w=true)) i])
567  angle_ll([c[k[0]], c[k[1]]], [c[k[1]], c[k[2]]])
568  ];
569 
570 //! Test if the faces of a polytope are all regular.
571 /***************************************************************************//**
572  \param c <coords-3d|coords-2d> A list of 3d or 2d cartesian
573  coordinates [[x, y (, z)], ...].
574  \param f <integer-list-list> A list of faces (or paths) that enclose
575  the shape where each face is a list of coordinate indexes.
576  \param e <integer-list-2-list> A list of edges where each edge is
577  a list of two coordinate indexes.
578  \param d <integer> The number of significant figures used when
579  comparing lengths and angles.
580 
581  \returns <boolean> \b true when there is both a single edge length
582  and a single edge angle and \b false otherwise.
583 
584  \details
585 
586  \note When \p e is not specified, it is computed from \p f using
587  polytope_faces2edges().
588 *******************************************************************************/
590 (
591  c,
592  f,
593  e,
594  d = 6
595 ) =
596  let
597  (
598  ce = is_defined(e) ? e : polytope_faces2edges(f),
599 
600  ul = unique(sround(polytope_edge_lengths(c, ce), d)),
601  ua = unique(sround(polytope_edge_angles(c, f), d))
602  )
603  ((len(ul) == 1) && (len(ua) == 1));
604 
605 //! Triangulate the faces of a convex polytope using fan triangulation.
606 /***************************************************************************//**
607  \param f <integer-list-list> A list of faces (or paths) that enclose
608  the shape where each face is a list of coordinate indexes.
609 
610  \returns <integer-list-3-list> A list of triangular faces that enclose
611  the polytope where each face is a list of three coordinate
612  indexes with vertex ordering is maintained.
613 
614  \details
615 
616  See [Wikipedia] for more information on [fan triangulation].
617 
618  [Wikipedia]: https://en.wikipedia.org/wiki/Polygon_triangulation
619  [fan triangulation]: https://en.wikipedia.org/wiki/Fan_triangulation
620 
621  \warning This method does not support concave polytopes.
622 *******************************************************************************/
624 (
625  f
626 ) =
627  [
628  for (fi = f)
629  for (ci = [1 : len(fi)-2]) [fi[0], fi[ci], fi[ci+1]]
630  ];
631 
632 //----------------------------------------------------------------------------//
633 // polygon
634 //----------------------------------------------------------------------------//
635 
636 //! Calculate the perimeter length of a polygon in 2d.
637 /***************************************************************************//**
638  \param c <coords-2d> A list of 2d cartesian coordinates
639  [[x, y], ...].
640  \param p <integer-list-list> An \em optional list of paths that
641  define one or more closed shapes where each is a list of
642  coordinate indexes.
643 
644  \returns <decimal> The sum of all polygon primary and secondary
645  perimeter lengths.
646 
647  \details
648 
649  \note When \p p is not given, the listed order of the coordinates
650  \p c establishes the path.
651 *******************************************************************************/
652 function polygon2d_perimeter
653 (
654  c,
655  p
656 ) =
657  let
658  (
659  pm = defined_or(p, [consts(len(c))]),
660 
661  lv =
662  [
663  for (k = pm) let (n = len(k))
664  for (i=[0 : n-1]) let (j = (i == 0) ? n-1 : i-1)
665  distance_pp(c[k[j]], c[k[i]])
666  ]
667  )
668  sum(lv);
669 
670 //! Compute the signed area of a polygon in a Euclidean 2d-space.
671 /***************************************************************************//**
672  \param c <coords-2d> A list of 2d cartesian coordinates
673  [[x, y], ...].
674  \param p <integer-list-list> An \em optional list of paths that
675  define one or more closed shapes where each is a list of
676  coordinate indexes.
677  \param s <boolean> Return the vertex ordering sign.
678 
679  \returns <decimal> The area of the given polygon.
680 
681  \details
682 
683  See [Wikipedia] for more information.
684 
685  \note When \p p is not given, the listed order of the coordinates
686  \p c establishes the path.
687  \warning This function does not track secondary shapes subtraction as
688  implemented by the polygon() function.
689 
690  [Wikipedia]: https://en.wikipedia.org/wiki/Shoelace_formula
691 *******************************************************************************/
692 function polygon2d_area
693 (
694  c,
695  p,
696  s = false
697 ) =
698  let
699  (
700  pm = defined_or(p, [consts(len(c))]),
701 
702  av =
703  [
704  for (k = pm) let (n = len(k))
705  for (i=[0 : n-1]) let (j = (i == 0) ? n-1 : i-1)
706  (c[k[j]][0] + c[k[i]][0]) * (c[k[i]][1] - c[k[j]][1])
707  ],
708 
709  sa = sum(av)/2
710  )
711  (s == false) ? abs(sa) : sa;
712 
713 //! Compute the area of a polygon in a Euclidean 3d-space.
714 /***************************************************************************//**
715  \param c <coords-3d> A list of 3d cartesian coordinates
716  [[x, y, z], ...].
717  \param p <integer-list-list> An \em optional list of paths that
718  define one or more closed shapes where each is a list of
719  coordinate indexes.
720  \param n <vector-3d> An \em optional normal vector, [x, y, z],
721  to the polygon plane. When not given, a normal vector is
722  constructed from the first three points of the primary path.
723 
724  \returns <decimal> The area of the given polygon.
725 
726  \details
727 
728  Function patterned after [Dan Sunday, 2012].
729 
730  \note When \p p is not given, the listed order of the coordinates
731  \p c establishes the path.
732  \warning This function does not track secondary shapes subtraction as
733  implemented by the polygon() function.
734 
735  [Dan Sunday, 2012]: http://geomalgorithms.com/a01-_area.html
736 *******************************************************************************/
737 function polygon3d_area
738 (
739  c,
740  p,
741  n
742 ) =
743  let
744  (
745  pm = defined_or(p, [consts(len(c))]),
746  nv = defined_or(n, cross_ll([c[pm[0][0]], c[pm[0][1]]], [c[pm[0][0]], c[pm[0][2]]])),
747 
748  ac = [abs(nv[0]), abs(nv[1]), abs(nv[2])],
749  am = max(ac),
750  ai = (am == ac[2]) ? 2 : (am == ac[1]) ? 1 : 0,
751 
752  pv = [
753  for (k = pm) let (m = len(k))
754  for (i=[1 : m])
755  c[k[i%m]][(ai+1)%3] * (c[k[(i+1)%m]][(ai+2)%3] - c[k[(i-1)%m]][(ai+2)%3])
756  ],
757 
758  sf = (distance_pp(nv)/(2*nv[ai]))
759  )
760  (sum(pv) * sf);
761 
762 //! Compute the center of mass of a polygon in a Euclidean 2d-space.
763 /***************************************************************************//**
764  \param c <coords-2d> A list of 2d cartesian coordinates
765  [[x, y], ...].
766  \param p <integer-list-list> An \em optional list of paths that
767  define one or more closed shapes where each is a list of
768  coordinate indexes.
769 
770  \returns <point-2d> The center of mass of the given polygon.
771 
772  \details
773 
774  See [Wikipedia] for more information.
775 
776  \note When \p p is not given, the listed order of the coordinates
777  \p c establishes the path.
778  \warning This function does not track secondary shapes subtraction as
779  implemented by the polygon() function.
780 
781  [Wikipedia]: https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
782 *******************************************************************************/
783 function polygon2d_centroid
784 (
785  c,
786  p
787 ) =
788  let
789  (
790  pm = defined_or(p, [consts(len(c))]),
791 
792  cv =
793  [
794  for (k = pm) let (n = len(k))
795  for (i=[0 : n-1])
796  let
797  (
798  j = (i == 0) ? n-1 : i-1,
799 
800  xc = c[k[j]][0],
801  yc = c[k[j]][1],
802 
803  xn = c[k[i]][0],
804  yn = c[k[i]][1],
805 
806  cd = (xc*yn - xn*yc)
807  )
808  [(xc + xn) * cd, (yc + yn) * cd]
809  ],
810 
811  sc = sum(cv),
812  sa = polygon2d_area(c, pm, true)
813  )
814  sc/(6*sa);
815 
816 //! Test the vertex ordering of a polygon in a Euclidean 2d-space.
817 /***************************************************************************//**
818  \param c <coords-2d> A list of 2d cartesian coordinates
819  [[x, y], ...].
820  \param p <integer-list-list> An \em optional list of paths that
821  define one or more closed shapes where each is a list of
822  coordinate indexes.
823 
824  \returns <boolean> \b true if the vertex are ordered \em clockwise,
825  \b false if the vertex are \em counterclockwise ordered, and
826  \b undef if the ordering can not be determined.
827 
828  \details
829 
830  \note When \p p is not given, the listed order of the coordinates
831  \p c establishes the path.
832 *******************************************************************************/
833 function polygon2d_is_cw
834 (
835  c,
836  p
837 ) =
838  let
839  (
840  sa = polygon2d_area(c, p, true)
841  )
842  (sa < 0) ? true
843  : (sa > 0) ? false
844  : undef;
845 
846 //! Test the convexity of a polygon in a Euclidean 2d-space.
847 /***************************************************************************//**
848  \param c <coords-2d> A list of 2d cartesian coordinates
849  [[x, y], ...].
850  \param p <integer-list-list> An \em optional list of paths that
851  define one or more closed shapes where each is a list of
852  coordinate indexes.
853 
854  \returns <boolean> \b true if the polygon is \em convex, \b false
855  otherwise.
856 
857  \details
858 
859  \note When \p p is not given, the listed order of the coordinates
860  \p c establishes the path.
861 *******************************************************************************/
862 function polygon2d_is_convex
863 (
864  c,
865  p
866 ) = not_defined(c) ? undef
867  : len(c) < 3 ? undef
868  : !all_len(c, 2) ? undef
869  : let
870  (
871  pm = defined_or(p, [consts(len(c))]),
872 
873  sv =
874  [
875  for (k = pm) let (n = len(k))
876  for (i=[0 : n-1])
877  sign(cross_ll([c[k[i]], c[k[(i+1)%n]]], [c[k[(i+1)%n]], c[k[(i+2)%n]]]))
878  ],
879 
880  us = unique(sv)
881  )
882  (len(us) == 1);
883 
884 //! Compute the winding number of a polygon about a point in a Euclidean 2d-space.
885 /***************************************************************************//**
886  \param c <coords-2d> A list of 2d cartesian coordinates
887  [[x, y], ...].
888  \param p <integer-list-list> An \em optional list of paths that
889  define one or more closed shapes where each is a list of
890  coordinate indexes.
891  \param t <point-2d> A test point coordinate [x, y].
892 
893  \returns <integer> The winding number.
894 
895  \details
896 
897  Computes the [winding number], the total number of counterclockwise
898  turns that the polygon paths makes around the test point in a
899  Euclidean 2d-space. Will be 0 \em iff the point is outside of the
900  polygon. Function patterned after [Dan Sunday, 2012].
901 
902  \copyright
903 
904  Copyright 2000 softSurfer, 2012 Dan Sunday
905  This code may be freely used and modified for any purpose
906  providing that this copyright notice is included with it.
907  iSurfer.org makes no warranty for this code, and cannot be held
908  liable for any real or imagined damage resulting from its use.
909  Users of this code must verify correctness for their application.
910 
911  [Dan Sunday, 2012]: http://geomalgorithms.com/a03-_inclusion.html
912  [winding number]: https://en.wikipedia.org/wiki/Winding_number
913 
914  \note When \p p is not given, the listed order of the coordinates
915  \p c establishes the path.
916  \warning Where there are secondary paths, the vertex ordering of each
917  must be the same as the primary path.
918 *******************************************************************************/
919 function polygon2d_winding
920 (
921  c,
922  p,
923  t
924 ) =
925  let
926  (
927  pm = defined_or(p, [consts(len(c))]),
928 
929  wv =
930  [
931  for (k = pm) let (n = len(k))
932  for (i=[0 : n-1])
933  let
934  (
935  j = (i == 0) ? n-1 : i-1,
936 
937  t = (
938  (c[k[j]][1] <= t[1]) && (c[k[i]][1] > t[1])
939  && (is_left_ppp(c[k[j]], c[k[i]], t) > 0)
940  ) ? +1
941  : (
942  (c[k[j]][1] > t[1]) && (c[k[i]][1] <= t[1])
943  && (is_left_ppp(c[k[j]], c[k[i]], t) < 0)
944  ) ? -1
945  : 0
946  )
947  t
948  ]
949  )
950  sum(wv);
951 
952 //! Test if a point is inside a polygon in a Euclidean 2d-space using winding number.
953 /***************************************************************************//**
954  \param c <coords-2d> A list of 2d cartesian coordinates
955  [[x, y], ...].
956  \param p <integer-list-list> An \em optional list of paths that
957  define one or more closed shapes where each is a list of
958  coordinate indexes.
959  \param t <point-2d> A test point coordinate [x, y].
960 
961  \returns <boolean> \b true when the point is \em inside the polygon and
962  \b false otherwise.
963 
964  \details
965 
966  \note When \p p is not given, the listed order of the coordinates
967  \p c establishes the path.
968 
969  \sa polygon2d_winding for warning about secondary shapes.
970 *******************************************************************************/
971 function polygon2d_is_pip_wn
972 (
973  c,
974  p,
975  t
976 ) = (polygon2d_winding(c=c, p=p, t=t) != 0);
977 
978 //! Test if a point is inside a polygon in a Euclidean 2d-space using angle summation.
979 /***************************************************************************//**
980  \param c <coords-2d> A list of 2d cartesian coordinates
981  [[x, y], ...].
982  \param p <integer-list-list> An \em optional list of paths that
983  define one or more closed shapes where each is a list of
984  coordinate indexes.
985  \param t <point-2d> A test point coordinate [x, y].
986 
987  \returns <boolean> \b true when the point is \em inside the polygon and
988  \b false otherwise.
989 
990  \details
991 
992  See [Wikipedia] for more information.
993 
994  \note When \p p is not given, the listed order of the coordinates
995  \p c establishes the path.
996  \warning This function does not track secondary shapes subtraction as
997  implemented by the polygon() function.
998 
999  [Wikipedia]: https://en.wikipedia.org/wiki/Point_in_polygon
1000 *******************************************************************************/
1001 function polygon2d_is_pip_as
1002 (
1003  c,
1004  p,
1005  t
1006 ) =
1007  let
1008  (
1009  pm = defined_or(p, [consts(len(c))]),
1010 
1011  av =
1012  [
1013  for (k = pm) let (n = len(k))
1014  for (i=[0 : n-1])
1015  let
1016  (
1017  j = (i == 0) ? n-1 : i-1
1018  )
1019  angle_ll([t, c[k[i]]], [t, c[k[j]]])
1020  ],
1021 
1022  sa = abs(sum(av))
1023  )
1024  (sa > 180);
1025 
1026 //----------------------------------------------------------------------------//
1027 // polyhedron
1028 //----------------------------------------------------------------------------//
1029 
1030 //! Compute the surface area of a polyhedron in a Euclidean 3d-space.
1031 /***************************************************************************//**
1032  \param c <coords-3d> A list of 3d cartesian coordinates
1033  [[x, y, z], ...].
1034  \param f <integer-list-list> A list of faces that enclose
1035  the shape where each face is a list of coordinate indexes.
1036 
1037  \returns <decimal> The surface area of the given polyhedron.
1038 *******************************************************************************/
1039 function polyhedron_area
1040 (
1041  c,
1042  f
1043 ) = sum([for (fi = f) polygon3d_area(c, [fi])]);
1044 
1045 //! Compute the volume of a triangulated polyhedron in a Euclidean 3d-space.
1046 /***************************************************************************//**
1047  \param c <coords-3d> A list of 3d cartesian coordinates
1048  [[x, y, z], ...].
1049  \param f <integer-list-3-list> A list of triangular faces that
1050  enclose the polyhedron where each face is a list of three
1051  coordinate indexes.
1052 
1053  \returns <decimal> The volume of the given polyhedron.
1054 
1055  \details
1056 
1057  See [Wikipedia] for more information on volumes determined using
1058  the [divergence theorem].
1059 
1060  \note All faces are assumed to be a union of triangles oriented
1061  clockwise from the outside inwards.
1062 
1063  [Wikipedia]: https://en.wikipedia.org/wiki/Polyhedron#Volume
1064  [divergence theorem]: https://en.wikipedia.org/wiki/Divergence_theorem
1065 *******************************************************************************/
1066 function polyhedron_volume_tf
1067 (
1068  c,
1069  f
1070 ) =
1071  let
1072  (
1073  vv =
1074  [
1075  for (fi = f)
1076  (len(fi) != 3) ? undef
1077  : let
1078  (
1079  a = c[fi[1]],
1080  b = c[fi[0]],
1081  c = c[fi[2]]
1082  )
1083  a * cross_ll([a, b], [a, c])
1084  ],
1085 
1086  sv = sum(vv)
1087  )
1088  sv/6;
1089 
1090 //! Compute the center of mass of a triangulated polyhedron in a Euclidean 3d-space.
1091 /***************************************************************************//**
1092  \param c <coords-3d> A list of 3d cartesian coordinates
1093  [[x, y, z], ...].
1094  \param f <integer-list-3-list> A list of triangular faces that
1095  enclose the polyhedron where each face is a list of three
1096  coordinate indexes.
1097 
1098  \returns <point-3d> The center of mass of the given polyhedron.
1099 
1100  \details
1101 
1102  See [Wikipedia] for more information on centroid determined via
1103  the [divergence theorem] and midpoint quadrature.
1104 
1105  \note All faces are assumed to be a union of triangles oriented
1106  clockwise from the outside inwards.
1107 
1108  [Wikipedia]: https://en.wikipedia.org/wiki/Centroid
1109  [divergence theorem]: https://en.wikipedia.org/wiki/Divergence_theorem
1110 *******************************************************************************/
1111 function polyhedron_centroid_tf
1112 (
1113  c,
1114  f
1115 ) =
1116  let
1117  (
1118  wv =
1119  [
1120  for (fi = f)
1121  let
1122  (
1123  a = c[fi[1]],
1124  b = c[fi[0]],
1125  c = c[fi[2]],
1126 
1127  r = a * cross_ll([a, b], [a, c])
1128  )
1129  (a+b+c) * r
1130  ],
1131 
1132  ws = sum(wv),
1133  tv = polyhedron_volume_tf(c, f)
1134  )
1135  ws/(24*tv);
1136 
1137 //----------------------------------------------------------------------------//
1138 // other: polygon to polyhedron
1139 //----------------------------------------------------------------------------//
1140 
1141 //! Convert a polygon to a polyhedron by adding a height dimension.
1142 /***************************************************************************//**
1143  \param c <coords-2d> A list of 2d cartesian coordinates
1144  [[x, y], ...].
1145  \param p <integer-list-list> An \em optional list of paths that
1146  define one or more closed shapes where each is a list of
1147  coordinate indexes.
1148  \param h <decimal> The polyhedron height.
1149  \param centroid <boolean> Center polygon centroid at z-axis.
1150  \param center <boolean> Center polyhedron height about xy-plane.
1151 
1152  \returns <datastruct> A structure <tt>[points, faces]</tt>, where
1153  \c points are <coords-3d> and \c faces are a
1154  <integer-list-list>, that define the bounding box of the
1155  given polyhedron.
1156 
1157  \details
1158 
1159  \note When \p p is not given, the listed order of the coordinates
1160  \p c establishes the path.
1161 *******************************************************************************/
1162 function linear_extrude_pp2pf
1163 (
1164  c,
1165  p,
1166  h = 1,
1167  centroid = false,
1168  center = false
1169 ) =
1170  let
1171  (
1172  pm = defined_or(p, [consts(len(c))]),
1173  pn = len([for (pi = pm) for (ci = pi) 1]),
1174 
1175  po = (centroid == true) ? polygon2d_centroid(c, p) : origin2d,
1176  zr = (center == true) ? [-h/2, h/2] : [0, h],
1177 
1178  cw = polygon2d_is_cw (c, p),
1179 
1180  pp = [for (zi = zr) for (pi = pm) for (ci = pi) concat(c[ci] - po, zi)],
1181  pf =
1182  [
1183  [for (pi = pm) for (ci = pi) ci],
1184  [for (pi = pm) for (cn = [len(pi)-1 : -1 : 0]) pi[cn] + pn],
1185  for (pi = pm) for (ci = pi)
1186  (cw == true)
1187  ? [ci, ci+pn, (ci+1)%pn+pn, (ci+1)%pn]
1188  : [ci, (ci+1)%pn, (ci+1)%pn+pn, ci+pn]
1189  ]
1190  )
1191  [pp, pf];
1192 
1193 //! @}
1194 //! @}
1195 
1196 //----------------------------------------------------------------------------//
1197 // end of file
1198 //----------------------------------------------------------------------------//
function polytope_line(c, f, e, i, l, r=false)
Get a line from an edge or any two vetices of a polytope.
function polytope_face_angles(c, f)
List the angles between all adjacent faces of a polyhedron.
function polyhedron_area(c, f)
Compute the surface area of a polyhedron in a Euclidean 3d-space.
function is_left_ppp(p1, p2, p3)
Test if a point is left, on, or right of an infinite line in a Euclidean 2d-space.
function distance_pp(p1, p2)
Compute the distance between two Euclidean points.
function polytope_edge_n(c, f, e, i)
Get a normal vector for a polytope edge.
function polygon2d_centroid(c, p)
Compute the center of mass of a polygon in a Euclidean 2d-space.
function cross_ll(l1, l2)
Compute the cross product of two lines (or vectors) in a Euclidean 3d or 2d-space.
function mean(v)
Compute the mean/average of a list of numbers.
function polytope_vertex_n(c, f, i)
Get a normal vector for a polytope vertex.
function nssequence(v, n=1, s=1, w=false)
Return a list of all n-element sequential-subsets of an iterable value.
function defined_or(v, d)
Return a value when it is defined or a default value when it is not.
function linear_extrude_pp2pf(c, p, h=1, centroid=false, center=false)
Convert a polygon to a polyhedron by adding a height dimension.
function is_integer(v)
Test if a value is an integer.
function polytope_edge_lengths(c, e)
List the edge lengths of a polytope.
function second(v)
Return the second element of an iterable value.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function unique(v)
Return the unique elements of an iterable value.
function all_len(v, l)
Test if all elements of a list of values are lists of a specified length.
function is_scalar(v)
Test if a value is a single non-iterable value.
function polytope_faces_are_regular(c, f, e, d=6)
Test if the faces of a polytope are all regular.
function polytope_edge_af(f, e, i)
List the adjacent face indexes for a polytope edge.
function edefined_or(v, i, d)
Return an iterable element when it exists or a default value when it does not.
function polytope_bbox_pf(c, f, a)
Generate a bounding box polytope for another polytope in 3d or 2d.
function polyhedron_volume_tf(c, f)
Compute the volume of a triangulated polyhedron in a Euclidean 3d-space.
function polytope_face_m(c, f, i, l)
Get the mean coordinate of all vertices of a polytope face.
function polygon2d_is_cw(c, p)
Test the vertex ordering of a polygon in a Euclidean 2d-space.
function polytope_triangulate_ft(f)
Triangulate the faces of a convex polytope using fan triangulation.
function not_defined(v)
Test if a value is not defined.
function exists(mv, v, s=true, i)
Check for the existence of a match value in an iterable value.
function polyhedron_centroid_tf(c, f)
Compute the center of mass of a triangulated polyhedron in a Euclidean 3d-space.
function unit_l(l)
Compute the normalized unit vector of a Euclidean line (or vector).
function is_defined(v)
Test if a value is defined.
function polytope_plane(c, f, i, l, cw=true)
Get a plane for a polytope face.
function polytope_vertex_af(f, i)
List the adjacent face indexes for a polytope vertex.
function is_range(v)
Test if a value is a range definition.
function angle_ll(l1, l2)
Compute the angle between two lines (or vectors) in a Euclidean 3d or 2d-space.
function sround(v, d=6)
Round all numerical values of a list to a fixed number of significant figures.
function is_list(v)
Test if a value is an iterable list of values.
function first(v)
Return the first element of an iterable value.
function polygon2d_perimeter(c, p)
Calculate the perimeter length of a polygon in 2d.
function polytope_faces2edges(f)
List the edge coordinate index pairs of a polytope.
function polygon2d_is_convex(c, p)
Test the convexity of a polygon in a Euclidean 2d-space.
function polytope_face_vcounts(f)
List the vertex counts for all polytope faces.
function polytope_face_mn(c, f, i, l, cw=true)
Get the mean coordinate and normal vector of a polytope face.
function polygon2d_is_pip_wn(c, p, t)
Test if a point is inside a polygon in a Euclidean 2d-space using winding number. ...
function circular_index(i, l, f=0)
Map an index position into a circularly indexed list.
function consts(l, v, u=false)
Create a sequence of constant or incrementing elements.
function polytope_face_n(c, f, i, l, cw=true)
Get the normal vector of a polytope face.
function polygon2d_area(c, p, s=false)
Compute the signed area of a polygon in a Euclidean 2d-space.
function polytope_limits(c, f, a, d=[0:2], s=true)
Determine the bounding limits of a polytope.
function polytope_edge_angles(c, f)
List the adjacent edge angles for each polytope vertex.
function polygon2d_is_pip_as(c, p, t)
Test if a point is inside a polygon in a Euclidean 2d-space using angle summation.
function polygon3d_area(c, p, n)
Compute the area of a polygon in a Euclidean 3d-space.
function polygon2d_winding(c, p, t)
Compute the winding number of a polygon about a point in a Euclidean 2d-space.
function polytope_vertex_av(f, i)
List the adjacent vertices for a given polytope vertex.