omdl  v1.0
OpenSCAD Mechanical Design Library
polytope.scad
Go to the documentation of this file.
1 //! Polytope shapes, conversions, properties, and tests functions.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2017-2024,2026
6 
7  \copyright
8 
9  This file is part of [omdl] (https://github.com/royasutton/omdl),
10  an OpenSCAD mechanical design library.
11 
12  The \em omdl is free software; you can redistribute it and/or modify
13  it under the terms of the [GNU Lesser General Public License]
14  (http://www.gnu.org/licenses/lgpl.html) as published by the Free
15  Software Foundation; either version 2.1 of the License, or (at
16  your option) any later version.
17 
18  The \em omdl is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with the \em omdl; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  02110-1301, USA; or see <http://www.gnu.org/licenses/>.
27 
28  \details
29 
30  \amu_define group_name (Polytopes)
31  \amu_define group_brief (Polytope mathematical functions.)
32 
33  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
34 *******************************************************************************/
35 
36 // auto-tests (add to test results page)
37 /***************************************************************************//**
38  \amu_include (include/amu/validate_log.amu)
39  \amu_include (include/amu/validate_results.amu)
40 *******************************************************************************/
41 
42 // group(s) begin (test summary and includes-required)
43 /***************************************************************************//**
44  \amu_include (include/amu/doxyg_define_in_parent_open.amu)
45  \amu_include (include/amu/validate_summary.amu)
46  \amu_include (include/amu/includes_required.amu)
47 *******************************************************************************/
48 
49 // member-wide reference definitions
50 /***************************************************************************//**
51  \amu_define group_references
52  (
53  )
54 *******************************************************************************/
55 
56 // member-wide documentation and conventions
57 /***************************************************************************//**
58  \addtogroup \amu_eval(${group})
59  \details
60  \anchor \amu_eval(${group})_conventions
61  \par Conventions
62 
63  - Coordinates are given as \c [[x, y, z], ...] in 3d or \c [[x, y], ...]
64  in 2d. Each face in \p f is an ordered list of coordinate indexes
65  into \p c.
66  - For polygons \p f is optional; when omitted the listed order of \p c
67  forms the single implicit path.
68  - Face vertex indexes are ordered \b clockwise when viewed from
69  \b outside the solid (right-hand rule outward normal). The \p cw
70  parameter defaults to \b true (clockwise); set it to \b false to
71  negate the returned normal for counter-clockwise faces.
72  - Normals are derived from the \b first three vertices of a face only.
73  A collinear first triple produces a zero normal vector silently.
74  Normal vectors are \b not normalised to unit length unless stated.
75  - To identify a face, \p l (explicit index list) takes precedence over
76  \p i (index into \p f). Passing both is permitted; \p l wins silently.
77  Returns \b undef when a required identification is absent.
78  - Edges are represented as \c [[i0, i1], ...] sorted smallest-index-first.
79  The optional \p e parameter is computed on-demand when omitted; callers
80  iterating over edges \b must pre-compute and pass \p e explicitly.
81  - Functions whose names include the \c _ft_ infix use \b fan triangulation,
82  which is correct for \b convex faces only. Fan triangulation preserves
83  vertex winding; use an ear-clipping triangulator for concave faces.
84  - Lengths and bounding-box values are in the units of \p c. Angles are
85  in degrees.
86 *******************************************************************************/
87 
88 //----------------------------------------------------------------------------//
89 // members
90 //----------------------------------------------------------------------------//
91 
92 //----------------------------------------------------------------------------//
93 // General
94 //----------------------------------------------------------------------------//
95 
96 //! \name General
97 //! @{
98 
99 //! List the edge coordinate index pairs of a polytope.
100 /***************************************************************************//**
101  \param f <integer-list-list> A list of faces (or paths) that enclose
102  the shape where each face is a list of coordinate indexes.
103 
104  \returns <integer-list-2-list> A list of edges where each edge is
105  a list of two coordinate indexes that form the shape.
106 
107  \details
108 
109  \note Although the edge list is not sorted, each pair is sorted
110  with the smallest index first.
111 *******************************************************************************/
112 function polytope_faces2edges
113 (
114  f
115 ) =
116  let
117  (
118  el =
119  [
120  for (ip = [for (fi = f) for (ai = sequence_ns(fi, n=2, s=1, w=true)) ai])
121  [min(ip), max(ip)]
122  ]
123  )
124  unique(el);
125 
126 //! Get a line from an edge or any two vetices of a polytope.
127 /***************************************************************************//**
128  \param c <points-3d | points-2d> A list of 3d or 2d coordinate points.
129 
130  \param f <integer-list-list> A list of faces (or paths) that enclose
131  the shape where each face is a list of coordinate indexes.
132  \param e <integer-list-2-list> A list of edges where each edge is
133  a list of two coordinate indexes.
134 
135  \param i <integer> A line specified as an edge index.
136  \param l <integer-list-2> A line specified as a list of coordinate
137  index pairs.
138 
139  \param r <boolean> Reverse the start and end points of the line
140  specified as an edge index \p i.
141 
142  \returns <line-3d | line-2d> The line as a pair of coordinates.
143 
144  \details
145 
146  Two mutually exclusive methods identify the line:
147  - When \p l is defined it takes precedence over \p i. The line is
148  constructed directly from the two coordinate indexes in \p l and
149  neither \p f nor \p e is consulted. \p r has no effect in this path.
150  - When only \p i is given the edge list is used to look up the two
151  endpoint indexes. \p r reverses the returned point order: when
152  \b false (default) the line runs from \c el[i][0] to \c el[i][1];
153  when \b true it runs from \c el[i][1] to \c el[i][0].
154 
155  Returns \b undef when neither \p l nor \p i is provided.
156 
157  \note Parameter \p f is optional for polygons. When it is not
158  given, the listed order of the coordinates \p c establishes
159  the polygon path.
160  \note When \p e is not specified, it is computed from \p f using
161  polytope_faces2edges() iff the line is identified by \p i.
162 *******************************************************************************/
163 function polytope_line
164 (
165  c,
166  f,
167  e,
168  i,
169  l,
170  r = false
171 ) = is_defined(l) ? [c[first(l)], c[second(l)]]
172  : is_undef(i) ? undef
173  : let
174  (
175  fm = defined_or(f, [consts(len(c))]),
176  el = defined_or(e, polytope_faces2edges(fm)),
177  sl = el[i]
178  )
179  (r == false)
180  ? [c[first(sl)], c[second(sl)]]
181  : [c[second(sl)], c[first(sl)]];
182 
183 //! Determine the bounding limits of a polytope.
184 /***************************************************************************//**
185  \param c <points-3d | points-2d> A list of 3d or 2d cartesian
186  coordinates [[x, y (, z)], ...].
187  \param f <integer-list-list> A list of faces (or paths) that enclose
188  the shape where each face is a list of coordinate indexes.
189  \param a <decimal-list-1:3 | decimal> The box padding.
190  A list of lengths to equally pad the box dimensions.
191  \param d <range|list|integer> The dimensions to consider. A range
192  of dimensions, a list of dimensions, or a single dimension.
193  \param s <boolean> Return box size rather than coordinate limits.
194 
195  \returns <datastruct> The bounding-box limits (see: table).
196 
197  \details
198 
199  The returned datastruct will be one of the following forms:
200 
201  | | s | x | y | z | datastruct form |
202  |:---:|:-----:|:---------:|:---------:|:---------:|:---------------------:|
203  | 2d | false | [min,max] | [min,max] | - | decimal-list-2-list-2 |
204  | 2d | true | max-min | max-min | - | decimal-list-list-2 |
205  | 3d | false | [min,max] | [min,max] | [min,max] | decimal-list-2-list-3 |
206  | 3d | true | max-min | max-min | max-min | decimal-list-list-3 |
207 
208  \note When \p f is not specified, all coordinates are used to
209  determine the geometric limits, which, simplifies the
210  calculation. Parameter \p f is needed when a subset of the
211  coordinates should be considered.
212  \note When \p d is not specified, a check will be performed to
213  see if all coordinates of \p c are 3, 2, or 1 dimensional
214  and, if so, all axes for the determined dimension will be
215  used for \p d.
216  \warning This function does not track secondary shapes subtraction as
217  implemented by the polygon() function.
218 *******************************************************************************/
219 function polytope_limits
220 (
221  c,
222  f,
223  a,
224  d,
225  s = true
226 ) =
227  [
228  let
229  (
230  ax = is_undef(d)
231  ? (
232  all_len(c, 3) ? [0,1,2]
233  : all_len(c, 2) ? [0,1]
234  : all_len(c, 1) ? [0]
235  : undef
236  )
237  : is_range(d) ? [for (di=d) di]
238  : is_list(d) ? d
239  : is_integer(d) ? [d]
240  : undef,
241 
242  ad = (is_defined(a) && is_scalar(a)) ? a : 0,
243  ap = [for (j = ax) defined_e_or(a, j, ad)],
244 
245  pm = is_defined(f)
246  ? [for (j = ax) [for (m = f) for (i=[0 : len(m)-1]) c[m[i]][j]]]
247  : [for (j = ax) [for (i = c) i[j]]],
248 
249  b = [for (j = ax) [min(pm[j]), max(pm[j])] + [-ap[j]/2, +ap[j]/2]]
250  )
251  for (j = ax)
252  (s == true) ? b[j][1] - b[j][0] : [b[j][0], b[j][1]]
253  ];
254 
255 //! Generate a bounding box polytope for another polytope in 3d or 2d.
256 /***************************************************************************//**
257  \param c <points-3d | points-2d> A list of 3d or 2d cartesian
258  coordinates [[x, y (, z)], ...].
259  \param f <integer-list-list> A list of faces (or paths) that enclose
260  the shape where each face is a list of coordinate indexes.
261  \param a <decimal-list-1:3 | decimal> The box padding.
262  A list of lengths to equally pad the box dimensions.
263 
264  \returns <datastruct> A structure: (1) <tt>[points, faces]</tt>,
265  where \c points are <points-3d> and \c faces are a
266  <integer-list-list>, that define the bounding box of the
267  given polyhedron. Or: (2) <tt>[points, path]</tt>, where
268  \c points are <points-2d> and \c path is a
269  <integer-list-list>, that define the bounding box of the
270  given polygon.
271 
272  \details
273 
274  Polyhedron faces will be ordered \em clockwise when looking from
275  outside the shape inwards. Polygon path will be ordered clockwise
276  when looking from the top (positive z) downwards.
277 
278  \note When \p f is not specified, all coordinates are used to
279  determine the geometric limits, which, simplifies the
280  calculation. Parameter \p f is needed when a subset of the
281  coordinates should be considered.
282 
283  \sa polytope_limits for warning about secondary shapes.
284 *******************************************************************************/
286 (
287  c,
288  f,
289  a
290 ) = let
291  (
292  b = polytope_limits(c=c, f=f, a=a, s=false),
293  d = len(b)
294  )
295  (d == 3) ?
296  [ [for (x=b[0], y=b[1], z=b[2]) [x, y, z]],
297  [[0,2,3,1], [4,0,1,5], [6,4,5,7], [2,6,7,3], [0,4,6,2], [3,7,5,1]] ]
298  : [ [for (x=b[0], y=b[1]) [x, y]],
299  [[0,1,3,2]] ];
300 
301 //! Triangulate the faces of a convex polytope using fan triangulation.
302 /***************************************************************************//**
303  \param f <integer-list-list> A list of faces (or paths) that enclose
304  the shape where each face is a list of coordinate indexes.
305 
306  \returns <integer-list-3-list> A list of triangular faces that enclose
307  the polytope, where each face is a list of exactly three
308  coordinate indexes.
309 
310  \details
311 
312  Each n-vertex face produces n-2 triangles by fanning from its first
313  vertex: triangle k is \c [fi[0], fi[k], fi[k+1]] for k in [1, n-2].
314  Vertex winding is preserved: all output triangles inherit the winding
315  direction of their source face, so outward normals remain consistent
316  with the input. The total number of output triangles equals the sum
317  of (vertex_count - 2) over all input faces.
318 
319  See [Wikipedia] for more information on [fan triangulation].
320 
321  [Wikipedia]: https://en.wikipedia.org/wiki/Polygon_triangulation
322  [fan triangulation]: https://en.wikipedia.org/wiki/Fan_triangulation
323 
324  \warning Fan triangulation is only correct for convex faces. Concave
325  faces will produce overlapping or inverted triangles. For
326  concave polytopes use an ear-clipping triangulator instead.
327 *******************************************************************************/
329 (
330  f
331 ) =
332  [
333  for (fi = f)
334  for (ci = [1 : len(fi)-2]) [fi[0], fi[ci], fi[ci+1]]
335  ];
336 
337 //! @}
338 
339 //----------------------------------------------------------------------------//
340 // Adjacents
341 //----------------------------------------------------------------------------//
342 
343 //! \name Adjacents
344 //! @{
345 
346 //! List the adjacent vertices for a given polytope vertex.
347 /***************************************************************************//**
348  \param f <integer-list-list> A list of faces (or paths) that enclose
349  the shape where each face is a list of coordinate indexes.
350 
351  \param i <integer> A vertex index.
352 
353  \returns <integer-list> The list of adjacent vertex indexes for the
354  given vertex index.
355 
356  \details
357 
358  The adjacent vertices are those neighboring vertices that are
359  directly connected to the given vertex by a common edge. The
360  returned list contains coordinate indexes, not coordinates; pass
361  them into \p c to obtain the actual points. The result is
362  deduplicated so each adjacent vertex index appears exactly once,
363  even when it is shared by multiple faces.
364 *******************************************************************************/
366 (
367  f,
368  i
369 ) =
370  let
371  (
372  vn =
373  [
374  for (fi = f) let (fn = len(fi))
375  for (j = [0:fn-1])
376  if (i == fi[j])
377  for (k = [-1, 1])
378  fi[index_c(j + k, fn)]
379  ]
380  )
381  unique(vn);
382 
383 //! List the adjacent face indexes for a polytope vertex.
384 /***************************************************************************//**
385  \param f <integer-list-list> A list of faces (or paths) that enclose
386  the shape where each face is a list of coordinate indexes.
387  \param i <integer> The vertex index.
388 
389  \returns <integer-list> The list of face indexes adjacent to the
390  given polytope vertex.
391 *******************************************************************************/
393 (
394  f,
395  i
396 ) = [for (fi = [0:len(f)-1]) if (exists(i, f[fi])) fi];
397 
398 //! List the adjacent face indexes for a polytope edge.
399 /***************************************************************************//**
400  \param f <integer-list-list> A list of faces (or paths) that enclose
401  the shape where each face is a list of coordinate indexes.
402  \param e <integer-list-2-list> A list of edges where each edge is
403  a list of two coordinate indexes.
404  \param i <integer> The edge index.
405 
406  \returns <integer-list> The list of face indexes adjacent to the
407  given polytope edge.
408 
409  \details
410 
411  \note When \p e is not specified, it is computed from \p f using
412  polytope_faces2edges().
413 *******************************************************************************/
415 (
416  f,
417  e,
418  i
419 ) = let (el = is_defined(e) ? e : polytope_faces2edges(f))
420  [
421  for (fi = [0:len(f)-1])
422  if (exists(first(el[i]), f[fi]) && exists(second(el[i]), f[fi]))
423  fi
424  ];
425 
426 //! @}
427 
428 //----------------------------------------------------------------------------//
429 // Normals
430 //----------------------------------------------------------------------------//
431 
432 //! \name Normals
433 //! @{
434 
435 //! Get a normal vector for a polytope vertex.
436 /***************************************************************************//**
437  \param c <points-3d | points-2d> A list of 3d or 2d coordinate points.
438  \param f <integer-list-list> A list of faces (or paths) that enclose
439  the shape where each face is a list of coordinate indexes.
440  \param i <integer> The vertex index.
441 
442  \returns <vector-3d> A normal vector for the polytope vertex.
443 
444  \details
445 
446  The normal is computed as the mean of the adjacent faces.
447 
448  \note Parameter \p f is optional for polygons. When it is not
449  given, the listed order of the coordinates \p c establishes
450  the polygon path.
451 *******************************************************************************/
452 function polytope_vertex_normal
453 (
454  c,
455  f,
456  i
457 ) = let
458  (
459  fm = defined_or(f, [consts(len(c))])
460  )
461  mean([for (j=polytope_vertex_adjacent_faces(fm, i)) unit_l(polytope_face_normal(c, l=fm[j]))]);
462 
463 //! Get a normal vector for a polytope edge.
464 /***************************************************************************//**
465  \param c <points-3d | points-2d> A list of 3d or 2d coordinate points.
466  \param f <integer-list-list> A list of faces (or paths) that enclose
467  the shape where each face is a list of coordinate indexes.
468  \param e <integer-list-2-list> A list of edges where each edge is
469  a list of two coordinate indexes.
470  \param i <integer> The edge index.
471 
472  \returns <vector-3d> A normal vector for the polytope edge.
473 
474  \details
475 
476  The normal is computed as the mean of the adjacent faces.
477 
478  \note Parameter \p f is optional for polygons. When it is not
479  given, the listed order of the coordinates \p c establishes
480  the polygon path.
481  \note When \p e is not specified, it is computed from \p f using
482  polytope_faces2edges().
483 *******************************************************************************/
484 function polytope_edge_normal
485 (
486  c,
487  f,
488  e,
489  i
490 ) = let
491  (
492  fm = defined_or(f, [consts(len(c))]),
493  el = is_defined(e) ? e : polytope_faces2edges(fm)
494  )
495  mean([for (j=polytope_edge_adjacent_faces(fm, el, i)) unit_l(polytope_face_normal(c, l=fm[j]))]);
496 
497 //! Get the normal vector of a polytope face.
498 /***************************************************************************//**
499  \param c <points-3d | points-2d> A list of 3d or 2d coordinate points.
500  \param f <integer-list-list> A list of faces (or paths) that enclose
501  the shape where each face is a list of coordinate indexes.
502 
503  \param i <integer> The face specified as an face index.
504  \param l <integer-list> The face-plane specified as a list of three
505  or more coordinate indexes that are a part of the face.
506 
507  \param cw <boolean> Face vertex ordering. When \b true the face
508  vertices are assumed to be wound \b clockwise when viewed
509  from outside the solid, and the returned normal points
510  outward (away from the interior). When \b false the vertices
511  are assumed counter-clockwise and the normal is negated.
512 
513  \returns <vector-3d> The normal vector of a polytope face.
514 
515  \details
516 
517  The face can be identified using either parameter \p i or \p l.
518  When using \p l, the parameter \p f is not required.
519 
520  The normal is computed from the first three vertices of the resolved
521  face only. For triangulated faces this is exact. For higher-arity
522  faces (quads, n-gons) the first three vertices must not be collinear;
523  if they are, the returned vector will be a zero vector. Callers
524  working with untriangulated meshes should ensure the face is planar
525  and that its first three vertices form a non-degenerate triangle.
526 
527  \note Parameter \p f is optional for polygons. When it is not
528  given, the listed order of the coordinates \p c establishes
529  the polygon path.
530 *******************************************************************************/
531 function polytope_face_normal
532 (
533  c,
534  f,
535  i,
536  l,
537  cw = true
538 ) = let
539  (
540  ci = is_defined(l) ? l : defined_or(f, [consts(len(c))])[i],
541  pc = [for (vi = [0:2]) let (p = c[ci[vi]]) (len(p) == 3) ? p : [p[0], p[1], 0]]
542  )
543  cross(pc[0]-pc[1], pc[2]-pc[1]) * ((cw == true) ? 1 : -1);
544 
545 //! @}
546 
547 //----------------------------------------------------------------------------//
548 // Faces
549 //----------------------------------------------------------------------------//
550 
551 //! \name Faces
552 //! @{
553 
554 /***************************************************************************//**
555  \par Face Identification Convention
556  Several functions in this group accept two mutually exclusive parameters
557  for identifying a face:
558  - \p i <integer> selects the face by its index into \p f.
559  - \p l <integer-list> provides the face directly as an explicit list of
560  coordinate indexes (e.g. a single face from \p f, or a custom subset).
561 
562  When \p l is defined it takes precedence over \p i and \p f is not
563  consulted. When only \p i is given, \p f must be provided (except for
564  polygons, where the implicit single-path default applies). Passing both
565  \p l and \p i is permitted; \p l wins silently.
566 *******************************************************************************/
567 
568 //! Get the mean coordinate of all vertices of a polytope face.
569 /***************************************************************************//**
570  \param c <points-3d | points-2d> A list of 3d or 2d coordinate points.
571  \param f <integer-list-list> A list of faces (or paths) that enclose
572  the shape where each face is a list of coordinate indexes.
573 
574  \param i <integer> The face specified as an face index.
575  \param l <integer-list> The face specified as a list of all the
576  coordinate indexes that define it.
577 
578  \returns <points-3d> The mean coordinate of a polytope face.
579 
580  \details
581 
582  The face can be identified using either parameter \p i or \p l.
583  When using \p l, the parameter \p f is not required.
584 
585  \note Parameter \p f is optional for polygons. When it is not
586  given, the listed order of the coordinates \p c establishes
587  the polygon path.
588 *******************************************************************************/
589 function polytope_face_mean
590 (
591  c,
592  f,
593  i,
594  l
595 ) = let
596  (
597  ci = is_defined(l) ? l : defined_or(f, [consts(len(c))])[i],
598  pc = [for (ci_idx = ci) c[ci_idx]]
599  )
600  mean(pc);
601 
602 //! Get the mean coordinate and normal vector of a polytope face.
603 /***************************************************************************//**
604  \copydetails polytope_face_plane()
605 
606  \note This function is an alias for polytope_face_plane(). It is
607  retained for compatibility with existing call sites. Prefer
608  polytope_face_plane() in new code.
609 *******************************************************************************/
611 (
612  c,
613  f,
614  i,
615  l,
616  cw = true
617 ) = polytope_face_plane(c, f, i, l, cw);
618 
619 //! Get a plane defined by the mean coordinate and normal vector of a polytope face.
620 /***************************************************************************//**
621  \param c <points-3d | points-2d> A list of 3d or 2d coordinate points.
622  \param f <integer-list-list> A list of faces (or paths) that enclose
623  the shape where each face is a list of coordinate indexes.
624 
625  \param i <integer> The face specified as a face index.
626  \param l <integer-list> The face specified as a list of all the
627  coordinate indexes that define it.
628 
629  \param cw <boolean> Face vertex ordering. Passed directly to
630  polytope_face_normal(); see that function for the full
631  description. Default \b true (clockwise, outward normals).
632 
633  \returns <plane> <tt>[mp, nv]</tt>, where \c mp is a \c point-3d
634  giving the mean of all face vertices, and \c nv is a
635  \c vector-3d giving the face normal. Together they fully
636  define the face-plane and can be passed directly to any
637  function that expects a \c plane argument.
638 
639  \details
640 
641  Combines polytope_face_mean() and polytope_face_normal() into a
642  single call. The face can be identified using either \p i or \p l;
643  see the \ref Faces "Face Identification Convention" for the
644  precedence rule.
645 
646  \note Parameter \p f is optional for polygons. When it is not
647  given, the listed order of the coordinates \p c establishes
648  the polygon path.
649 *******************************************************************************/
650 function polytope_face_plane
651 (
652  c,
653  f,
654  i,
655  l,
656  cw = true
657 ) = [polytope_face_mean(c, f, i, l), polytope_face_normal(c, f, i, l, cw)];
658 
659 //! List the vertex counts for all polytope faces.
660 /***************************************************************************//**
661  \param f <integer-list-list> A list of faces (or paths) that enclose
662  the shape where each face is a list of coordinate indexes.
663 
664  \returns <integer-list> A list with a vertex count of every face.
665 *******************************************************************************/
667 (
668  f
669 ) = [for (fi=f) len(fi)];
670 
671 //! List the angles between all adjacent faces of a polyhedron.
672 /***************************************************************************//**
673  \param c <points-3d> A list of 3d cartesian coordinates
674  [[x, y, z], ...].
675  \param f <integer-list-list> A list of faces that enclose
676  the shape where each face is a list of coordinate indexes.
677  \param cw <boolean> Face vertex ordering. Passed directly to
678  polytope_face_normal(); see that function for the full
679  description. Default \b true (clockwise, outward normals).
680 
681  \returns <decimal-list> A list of angles (in degrees) between the
682  normal vectors of every pair of adjacent faces. Each entry
683  is the angle between the outward normals of two faces that
684  share at least one vertex, which equals the supplement of
685  the interior dihedral angle (i.e. 180° minus the interior
686  dihedral angle for a convex edge).
687 
688  \details
689 
690  For each face \p i, all faces that share at least one vertex are
691  found and the angle between their respective normal vectors is
692  computed via angle_ll(). Both normals are obtained by delegating
693  to polytope_face_normal() with the same \p cw convention, so the
694  result is consistent regardless of mesh winding.
695 
696  The normal of each face is derived from its first three vertices
697  only; see polytope_face_normal() for the implications of this for
698  non-triangulated or degenerate faces.
699 
700  See [Wikipedia] for more information on dihedral angles.
701 
702  [Wikipedia]: https://en.wikipedia.org/wiki/Dihedral_angle
703 
704  \warning Each adjacent face pair appears twice in the output list
705  (once from face i's perspective and once from face u's),
706  so the list length equals twice the number of adjacent
707  face pairs.
708 *******************************************************************************/
709 function polytope_face_angles
710 (
711  c,
712  f,
713  cw = true
714 ) =
715  [
716  for (i=[0 : len(f)-1])
717  let
718  (
719  n1 = polytope_face_normal(c, f, i, cw=cw),
720  af = [for (v=f[i]) for (j=[0 : len(f)-1]) if (j != i && exists(v, f[j])) j]
721  )
722  for (u=unique(af))
723  let
724  (
725  n2 = polytope_face_normal(c, f, u, cw=cw)
726  )
727  angle_ll(n1, n2)
728  ];
729 
730 //! @}
731 
732 //----------------------------------------------------------------------------//
733 // Edges
734 //----------------------------------------------------------------------------//
735 
736 //! \name Edges
737 //! @{
738 
739 //! List the edge lengths of a polytope.
740 /***************************************************************************//**
741  \param c <points-3d | points-2d> A list of 3d or 2d cartesian
742  coordinates [[x, y (, z)], ...].
743  \param e <integer-list-2-list> A list of edges where each edge is
744  a list of two coordinate indexes.
745 
746  \returns <decimal-list> A list of the polytope edge lengths.
747 *******************************************************************************/
748 function polytope_edge_lengths
749 (
750  c,
751  e
752 ) = [for (ei=e) distance_pp(c[ei[0]], c[ei[1]])];
753 
754 //! List the adjacent edge angles for each polytope vertex.
755 /***************************************************************************//**
756  \param c <points-3d | points-2d> A list of 3d or 2d cartesian
757  coordinates [[x, y (, z)], ...].
758  \param f <integer-list-list> A list of faces (or paths) that enclose
759  the shape where each face is a list of coordinate indexes.
760 
761  \returns <decimal-list> A list of the polytope adjacent edge angles.
762 *******************************************************************************/
763 function polytope_edge_angles
764 (
765  c,
766  f
767 ) =
768  [
769  for (k=[for (j=f) for (i=sequence_ns(j, n=3, s=1, w=true)) i])
770  angle_ll([c[k[0]], c[k[1]]], [c[k[1]], c[k[2]]])
771  ];
772 
773 //! @}
774 
775 //----------------------------------------------------------------------------//
776 // Tests
777 //----------------------------------------------------------------------------//
778 
779 //! \name Tests
780 //! @{
781 
782 //! Test if the faces of a polytope are all regular.
783 /***************************************************************************//**
784  \param c <points-3d | points-2d> A list of 3d or 2d cartesian
785  coordinates [[x, y (, z)], ...].
786  \param f <integer-list-list> A list of faces (or paths) that enclose
787  the shape where each face is a list of coordinate indexes.
788  \param e <integer-list-2-list> A list of edges where each edge is
789  a list of two coordinate indexes.
790  \param d <integer> The number of significant figures to retain when
791  rounding lengths and angles before comparing them for
792  uniqueness. Increase \p d to tighten the comparison tolerance;
793  decrease it to allow more floating-point variation. Default 6.
794 
795  \returns <boolean> \b true when all edge lengths are equal and all
796  adjacent edge angles are equal (to \p d significant figures),
797  \b false otherwise.
798 
799  \details
800 
801  All edge lengths are collected via polytope_edge_lengths() and all
802  adjacent edge angles via polytope_edge_angles(). Each list is rounded
803  to \p d significant figures with round_s() and then deduplicated with
804  unique(). The polytope is considered regular iff both deduplicated
805  lists contain exactly one distinct value.
806 
807  Note that this tests geometric regularity of the edges and vertex
808  angles, not full face regularity. For a polyhedron with non-planar
809  or non-congruent faces, the test may return \b true even though the
810  faces are not regular polygons.
811 
812  \note When \p e is not specified, it is computed from \p f using
813  polytope_faces2edges().
814 *******************************************************************************/
816 (
817  c,
818  f,
819  e,
820  d = 6
821 ) =
822  let
823  (
824  ce = is_defined(e) ? e : polytope_faces2edges(f),
825 
826  ul = unique(round_s(polytope_edge_lengths(c, ce), d)),
827  ua = unique(round_s(polytope_edge_angles(c, f), d))
828  )
829  ((len(ul) == 1) && (len(ua) == 1));
830 
831 //! @}
832 
833 //! @}
834 //! @}
835 
836 //----------------------------------------------------------------------------//
837 // openscad-amu auxiliary scripts
838 //----------------------------------------------------------------------------//
839 
840 /*
841 BEGIN_SCOPE validate;
842  BEGIN_OPENSCAD;
843  include <omdl-base.scad>;
844  include <common/validation.scad>;
845 
846  echo( str("openscad version ", version()) );
847  for (i=[1:19]) echo( "not tested:" );
848 
849  // end_include
850  END_OPENSCAD;
851 
852  BEGIN_MFSCRIPT;
853  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
854  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
855  END_MFSCRIPT;
856 END_SCOPE;
857 */
858 
859 //----------------------------------------------------------------------------//
860 // end of file
861 //----------------------------------------------------------------------------//
function angle_ll(l1, l2, s=true)
Compute the angle between two lines or vectors in a 3d or 2d-space.
function unit_l(l)
Compute the normalized unit vector of a line or vector.
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)
Returns an element from an iterable if it exists, or a default value if not.
function exists(mv, v, s=true, i)
Check for the existence of a match value in an iterable value.
function second(v)
Return the second element of an iterable value.
function 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 all_len(v, l)
Test if all elements of an iterable value are iterable with a fixed length.
function mean(v)
Compute the mean/average of a list of numbers.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function round_s(v, d=6)
Round a list of numbers to a fixed number of significant figures.
function index_c(i, l, f=0)
Return a circular index position.
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_integer(v)
Test if a value is an integer.
function is_range(v)
Test if a value is a range definition.
function polytope_edge_angles(c, f)
List the adjacent edge angles for each polytope vertex.
function polytope_edge_adjacent_faces(f, e, i)
List the adjacent face indexes for a polytope edge.
function polytope_face_mean_normal(c, f, i, l, cw=true)
Get the mean coordinate and normal vector of a polytope face.
function polytope_face_angles(c, f, cw=true)
List the angles between all adjacent faces of a polyhedron.
function polytope_edge_normal(c, f, e, i)
Get a normal vector for a polytope edge.
function polytope_faces2edges(f)
List the edge coordinate index pairs of a polytope.
function polytope_vertex_adjacent_vertices(f, i)
List the adjacent vertices for a given polytope vertex.
function polytope_vertex_adjacent_faces(f, i)
List the adjacent face indexes for a polytope vertex.
function polytope_face_normal(c, f, i, l, cw=true)
Get the normal vector of a polytope face.
function polytope_face_vertex_counts(f)
List the vertex counts for all polytope faces.
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_mean(c, f, i, l)
Get the mean coordinate of all vertices of a polytope face.
function polytope_faces_are_regular(c, f, e, d=6)
Test if the faces of a polytope are all regular.
function polytope_bounding_box_pf(c, f, a)
Generate a bounding box polytope for another polytope in 3d or 2d.
function polytope_limits(c, f, a, d, s=true)
Determine the bounding limits of a polytope.
function polytope_face_plane(c, f, i, l, cw=true)
Get a plane defined by the mean coordinate and normal vector of a polytope face.
function polytope_vertex_normal(c, f, i)
Get a normal vector for a polytope vertex.
function polytope_edge_lengths(c, e)
List the edge lengths of a polytope.
function polytope_ft_triangulate(f)
Triangulate the faces of a convex polytope using fan triangulation.