omdl  v0.9.5
OpenSCAD Mechanical Design Library
euclidean.scad
Go to the documentation of this file.
1 //! Tests and operations for Euclidean 3d space.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2015-2023
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 (Euclidean)
31  \amu_define group_brief (Tests and operations for Euclidean 3d space.)
32 
33  \amu_include (include/amu/pgid_path_pstem_pg.amu)
34 *******************************************************************************/
35 
36 //----------------------------------------------------------------------------//
37 // validation.
38 //----------------------------------------------------------------------------//
39 
40 /***************************************************************************//**
41  \amu_include (include/amu/validate_log_th.amu)
42  \amu_include (include/amu/validate_log_td.amu)
43  \amu_include (include/amu/validate_results.amu)
44 *******************************************************************************/
45 
46 //----------------------------------------------------------------------------//
47 // group.
48 //----------------------------------------------------------------------------//
49 
50 /***************************************************************************//**
51  \amu_include (include/amu/group_in_parent_start.amu)
52  \amu_include (include/amu/includes_required.amu)
53 
54  \details
55 
56  \amu_include (include/amu/validate_summary.amu)
57 *******************************************************************************/
58 
59 //----------------------------------------------------------------------------//
60 
61 //----------------------------------------------------------------------------//
62 // set 1: identify
63 //----------------------------------------------------------------------------//
64 
65 //! Test if a value defines a point.
66 /***************************************************************************//**
67  \param v <list-2d | list-3d> A 2d or 3d list of numbers.
68 
69  \returns (1) <boolean> \b true when the \p v can be interpreted as a
70  point and \b false otherwise.
71 
72  \details
73 
74  See \ref dt_line for argument specification and conventions.
75 *******************************************************************************/
76 function is_point
77 (
78  v
79 ) = !is_list(v) ? false
80  : !all_numbers(v) ? false
81  : !is_between(len(v), 2, 3) ? false
82  : true;
83 
84 //! Test if a value defines a Euclidean vector.
85 /***************************************************************************//**
86  \param v <list-2d | list-3d> A 2d or 3d list of numbers.
87 
88  \returns (1) <boolean> \b true when the \p v can be interpreted as a
89  Euclidean vector and \b false otherwise.
90 
91  \details
92 
93  See \ref dt_line for argument specification and conventions.
94 *******************************************************************************/
95 function is_vector
96 (
97  v
98 ) = !is_list(v) ? false
99  : (len(v) == 1) && !is_point(v[0]) ? false
100  : !is_point(v) ? false
101  : true;
102 
103 //! Test if a value defines a line.
104 /***************************************************************************//**
105  \param v <list-2d | list-3d> A 2d or 3d list of numbers.
106 
107  \returns (1) <boolean> \b true when the \p v can be interpreted as a
108  line and \b false otherwise.
109 
110  \details
111 
112  See \ref dt_line for argument specification and conventions.
113 *******************************************************************************/
114 function is_line
115 (
116  v
117 ) = !is_list(v) ? false
118  : (len(v) != 2) ? false
119  : !is_point(v[0]) ? false
120  : !is_point(v[1]) ? false
121  : (len(v[0]) != len(v[1])) ? false
122  : true;
123 
124 //! Test if a value defines a vector or a line.
125 /***************************************************************************//**
126  \param v <list-2d | list-3d> A 2d or 3d list of numbers.
127 
128  \returns (1) <boolean> \b true when the \p v can be interpreted as a
129  vector or a line and \b false otherwise.
130 
131  \details
132 
133  See \ref dt_line for argument specification and conventions.
134 *******************************************************************************/
135 function is_vol
136 (
137  v
138 ) = is_vector(v) ? true
139  : is_line(v) ? true
140  : false;
141 
142 //! Test if a value defines a plane.
143 /***************************************************************************//**
144  \param v <list-2d | list-3d> A 2d or 3d list of numbers.
145 
146  \returns (1) <boolean> \b true when the \p v can be interpreted as a
147  plane and \b false otherwise.
148 
149  \details
150 
151  See \ref dt_line for argument specification and conventions.
152 *******************************************************************************/
153 function is_plane
154 (
155  v
156  // predetermined normal vector
157 ) = is_vector(v) ? true
158 
159  // intersecting vectors [v1, v2] like line.
160  : is_line(v) ? true
161 
162  // otherwise, 3 points of same dimension.
163  : !is_list(v) ? false
164  : (len(v) != 3) ? false
165  : !is_point(v[0]) ? false
166  : !is_point(v[1]) ? false
167  : !is_point(v[2]) ? false
168  : (len(v[0]) != len(v[1])) ? false
169  : (len(v[1]) != len(v[2])) ? false
170  : true;
171 
172 //----------------------------------------------------------------------------//
173 // set 2: point
174 //----------------------------------------------------------------------------//
175 
176 //! Compute the distance between two points.
177 /***************************************************************************//**
178  \param p1 <point> A point coordinate 1.
179  \param p2 <point> A point coordinate 2.
180 
181  \returns (1) <decimal> The distance between the two points.
182  (2) Returns \b undef when \p p1 or \p p2 are not points or
183  do not have equal dimensions.
184 
185  \details
186 
187  When \p p2 is not given, it is assumed to be at the origin. This
188  function is similar to [norm].
189 
190  [norm]: https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Mathematical_Functions#norm
191 *******************************************************************************/
192 function distance_pp
193 (
194  p1,
195  p2
196 ) = !is_point(p1) ? undef
197  : !is_undef(p2) && !is_point(p2) ? undef
198  : is_undef(p2) ?
199  sqrt(sum([for (i=[0:len(p1)-1]) p1[i]*p1[i]]))
200  : sqrt(sum([for (i=[0:len(p1)-1]) (p1[i]-p2[i])*(p1[i]-p2[i])]));
201 
202 //! Compute the shortest distance between a point and a line.
203 /***************************************************************************//**
204  \param p <point> A point coordinate.
205  \param l <line> A line or vector.
206 
207  \returns (1) <decimal> The shortest distance between the point and
208  the line.
209  (2) Returns \b undef when \p p is not a point or \p l is
210  not a line.
211 
212  \details
213 
214  \sa point_closest_pl().
215 *******************************************************************************/
216 function distance_pl
217 (
218  p,
219  l
220 ) = distance_pp(p, point_closest_pl(p, l));
221 
222 //! Compute the shortest distance between a point and a plane.
223 /***************************************************************************//**
224  \param p <point> A point coordinate.
225  \param n <pnorm> A plane normal [specification].
226  \param np <point> A point coordinate on the plane \p n.
227 
228  \returns (1) <decimal> The shortest distance between the point and
229  the plane.
230  (2) Returns \b undef when \p p is not a point or \p l is
231  not a line.
232 
233  \details
234 
235  \sa point_closest_pn().
236 
237  [specification]: \ref dt_pnorm
238 *******************************************************************************/
239 function distance_pn
240 (
241  p,
242  n,
243  np
244 ) = distance_pp(p, point_closest_pn(p, n, np));
245 
246 //! Test if a point is left, on, or right of an infinite line in a 2d-space.
247 /***************************************************************************//**
248  \param p1 <point-2d> A 2d point coordinate 1.
249  \param p2 <point-2d> A 2d point coordinate 2.
250  \param p3 <point-2d> A 2d point coordinate 3.
251 
252  \returns (1) <decimal> (\b > 0) for \p p3 \em left of the line
253  through \p p1 and \p p2, (\b = 0) for p3 \em on the
254  line, and (\b < 0) for p3 right of the line.
255  (2) Returns \b undef when \p p1, \p p2, or \p p3 is not a
256  point.
257 
258  \details
259 
260  Function patterned after [Dan Sunday, 2012].
261 
262  [Dan Sunday, 2012]: http://geomalgorithms.com/a01-_area.html
263 *******************************************************************************/
264 function is_left_ppp
265 (
266  p1,
267  p2,
268  p3
269 ) = !is_point(p1) ? undef
270  : !is_point(p2) ? undef
271  : !is_point(p3) ? undef
272  : ((p2[0]-p1[0]) * (p3[1]-p1[1]) - (p3[0]-p1[0]) * (p2[1]-p1[1]));
273 
274 //! Compute the coordinates of the closest point on a line to a given point.
275 /***************************************************************************//**
276  \param p <point> A point coordinate.
277  \param l <line> A line or vector.
278 
279  \returns (1) <point-3d> The coordinates of the point on the line
280  closest to the given point.
281  (2) Returns \b undef when \p p is not a point or \p l is
282  not a line.
283 *******************************************************************************/
284 function point_closest_pl
285 (
286  p,
287  l
288 ) = !is_point(p) ? undef
289  : !is_line(l) ? undef
290  : let
291  (
292  t = line_tp(l),
293  i = line_ip(l),
294 
295  v = t - i,
296  w = p - i,
297 
298  x = (w * v) / (v * v)
299  )
300  i + x * v;
301 
302 //! Compute the coordinates of the closest point on a plane to a given point.
303 /***************************************************************************//**
304  \param p <point> A point coordinate.
305  \param n <pnorm> A plane normal [specification].
306  \param np <point> A point coordinate on the plane \p n.
307 
308  \returns (1) <point-3d> The coordinates of the point on the plane
309  closest to the given point.
310  (2) Returns \b undef when \p p or \p np is not a point or
311  when \p n is not a plane.
312 
313  [specification]: \ref dt_pnorm
314 *******************************************************************************/
315 function point_closest_pn
316 (
317  p,
318  n,
319  np
320 ) = !is_point(p) ? undef
321  : !is_plane(n) ? undef
322  : !is_point(np) ? undef
323  : let
324  (
325  m = plane_to_normal(n),
326 
327  q = m * (np - p),
328  r = m * m,
329 
330  s = q / r
331  )
332  p + s * m;
333 
334 //! Return 3d point unchanged or add a zeroed third dimension to 2d point.
335 /***************************************************************************//**
336  \param p <point-3d | point-2d> A point.
337 
338  \returns (1) <point-3d> The 3d point or the 2d point converted to 3d
339  with its third dimension assigned zero.
340  (2) Returns \b undef when \p p is not a point.
341 
342  \details
343 
344  This function assumes any point that is not 3d to be 2d.
345 *******************************************************************************/
346 function point_to_3d
347 (
348  p
349 ) = !is_point(p) ? undef
350  : (len(p) == 3) ? p : [p[0], p[1], 0];
351 
352 //! Linearly interpolate along a line established by two points in 2d.
353 /***************************************************************************//**
354  \param p1 <point-2d> The line initial coordinate [x, y].
355  \param p2 <point-2d> The line terminal coordinate [x, y].
356 
357  \param y <decimal> The \p y coordinate at which to interpolate
358  along the line.
359  \param x <decimal> The \p x coordinate at which to interpolate
360  along the line.
361 
362  \returns (1) <point-2d> The interpolated coordinates point [x, y].
363  (2) Returns \b undef when \p p1 or \p p2 is not a point or
364  when \p x or \p y are not numbers.
365 
366  \details
367 
368  The order of precedence for interpolation is: \p y, \p x. See
369  [Wikipedia] for more information.
370 
371  [Wikipedia]: https://en.wikipedia.org/wiki/Linear_interpolation
372 *******************************************************************************/
373 function interpolate2d_l_pp
374 (
375  p1,
376  p2,
377  x,
378  y
379 ) = !is_point(p1) ? undef
380  : !is_point(p1) ? undef
381 
382  // 'y' is given, get 'lx' for given 'y'
383  : !is_undef(y) && !is_number(y) ? undef
384  : !is_undef(y) ?
385  let( lx = (p1[0]*(p2[1]-y) + p2[0]*(y-p1[1])) / (p2[1]-p1[1]) )
386  [lx, y]
387 
388  // 'y' not given, get 'ly' for given 'x'
389  : !is_number(x) ? undef
390  : let( ly = (p1[1]*(p2[0]-x) + p2[1]*(x-p1[0])) / (p2[0]-p1[0]) )
391  [x, ly];
392 
393 //----------------------------------------------------------------------------//
394 // set 3: vector
395 //----------------------------------------------------------------------------//
396 
397 //----------------------------------------------------------------------------//
398 // set 4: line (or vector)
399 //----------------------------------------------------------------------------//
400 
401 //! Construct a 2 dimensional directed line.
402 /***************************************************************************//**
403  \param m <decimal> The magnitude.
404  \param a <decimal> The azmuthal angle.
405  \param p1 <point-2d> The initial point.
406  \param p2 <point-2d> The terminal point.
407  \param v <vector-2d> An orientation line or vector.
408 
409  \returns (1) <line-2d> The 2d directed line.
410  (2) Returns \b undef when required parameters are not
411  defined.
412 
413  \details
414 
415  The initial point (or tail) of the line is specified by \p p1. The
416  terminal point (or head) can be specified by one of, in order of
417  precedence, \p p2, \p v, or \p a.
418 
419  See \ref dt_line for argument specification and conventions.
420 *******************************************************************************/
421 function line2d_new
422 (
423  m = 1,
424  a = 0,
425  p1 = origin2d,
426  p2,
427  v
428 ) = !is_point(p1) ? undef
429 
430  // using 'p2' ('p1')
431  : !is_undef(p2) ? !is_point(p2) ? undef
432  : [p1, p2]
433 
434  // using 'v' ('p1' and 'm')
435  : !is_number(m) ? undef
436  : !is_undef(v) ? !is_vol(v) ? undef
437  : [p1, p1 + m*unit_l(v)]
438 
439  // using 'a' ('p1' and 'm')
440  : !is_number(a) ? undef
441  : [p1, p1 + m*[cos(a), sin(a)]];
442 
443 //! Construct a 3 dimensional directed line.
444 /***************************************************************************//**
445  \param m <decimal> The magnitude.
446  \param a <decimal> The azmuthal angle.
447  \param t <decimal> The polar angle.
448  \param p1 <point-3d> The initial point.
449  \param p2 <point-3d> The terminal point.
450  \param v <vector-3d> An orientation line or vector.
451 
452  \returns (1) <line-3d> The 3d directed line.
453  (2) Returns \b undef when required parameters are not
454  defined.
455 
456  \details
457 
458  The initial point (or tail) of the line is specified by \p p1. The
459  terminal point (or head) can be specified by one of, in order of
460  precedence, \p p2, \p v, or \p a and \p t.
461 
462  See \ref dt_line for argument specification and conventions.
463 *******************************************************************************/
464 function line3d_new
465 (
466  m = 1,
467  a = 0,
468  t = 90,
469  p1 = origin3d,
470  p2,
471  v
472 ) = !is_point(p1) ? undef
473 
474  // using 'p2' ('p1')
475  : !is_undef(p2) ? !is_point(p2) ? undef
476  : [p1, p2]
477 
478  // using 'v' ('p1' and 'm')
479  : !is_number(m) ? undef
480  : !is_undef(v) ? !is_vol(v) ? undef
481  : [p1, p1 + m*unit_l(v)]
482 
483  // using 'a' and 't' ('p1' and 'm')
484  : (!is_number(a) || !is_number(t)) ? undef
485  : [p1, p1 + m*[sin(t)*cos(a), sin(t)*sin(a), cos(t)]];
486 
487 //! Construct a directed line.
488 /***************************************************************************//**
489  \param m <decimal> The magnitude.
490  \param a <decimal> The azmuthal angle.
491  \param t <decimal> The polar angle.
492  \param p1 <point> The initial point.
493  \param p2 <point> The terminal point.
494  \param v <vector> An orientation line or vector.
495 
496  \returns (1) <line> The directed line.
497  (2) Returns \b undef when required parameters are not
498  defined.
499 
500  \details
501 
502  The initial point (or tail) of the line is specified by \p p1. The
503  terminal point (or head) can be specified by one of, in order of
504  precedence, \p p2, \p v, or \p a and \p t.
505 
506  This function will construct a 2d or 3d line as required based on
507  the supplied arguments. A 3d line is constructed only when
508  required. Because the argument dimensions are inspected, this
509  function has more overhead when compared to line2d_new() and
510  line3d_new().
511 
512  See \ref dt_line for argument specification and conventions.
513 *******************************************************************************/
514 function line_new
515 (
516  m = 1,
517  a = 0,
518  t,
519  p1,
520  p2,
521  v
522  // using 'p2'
523 ) = !is_undef(p2) ? !is_point(p2) ? undef
524  : let
525  (
526  pd = !is_undef(p1) ? p1
527  : (len(p2) == 2) ? origin2d : origin3d
528  )
529  [pd, p2]
530 
531  // using 'v' ('m')
532  : !is_number(m) ? undef
533  : !is_undef(v) ? !is_vol(v) ? undef
534  : let
535  (
536  pd = !is_undef(p1) ? p1
537  : (len(v) == 2) ? origin2d : origin3d
538  )
539  [pd, pd + m*unit_l(v)]
540 
541  // using 'a' ('m')
542  : is_undef(t) ? !is_number(a) ? undef
543  : let
544  (
545  pd = !is_undef(p1) ? p1 : origin2d
546  )
547  [pd, pd + m*[cos(a), sin(a)]]
548 
549  // using 'a' and 't' ('m')
550  : (!is_number(a) || !is_number(t)) ? undef
551  : let
552  (
553  pd = !is_undef(p1) ? p1 : origin3d
554  )
555  [pd, pd + m*[sin(t)*cos(a), sin(t)*sin(a), cos(t)]];
556 
557 //! Return the number of dimensions of a line or vector.
558 /***************************************************************************//**
559  \param l <line> A line or vector.
560 
561  \returns (1) <integer> The line or vector dimensions.
562  (2) Returns \b undef when \p l is not a line or vector.
563 
564  \details
565 
566  See \ref dt_line for argument specification and conventions.
567 *******************************************************************************/
568 function line_dim
569 (
570  l
571 ) = is_vector(l) ? (len(l) == 1) ? len( l[0] ) : len( l )
572  : is_line(l) ? len( l[0] )
573  : undef;
574 
575 //! Return the terminal point of a line or vector.
576 /***************************************************************************//**
577  \param l <line> A line or vector.
578 
579  \returns (1) <point> The terminal point of the line or vector.
580  (2) Returns \b undef when \p l is not a line or vector.
581 
582  \details
583 
584  See \ref dt_line for argument specification and conventions.
585 *******************************************************************************/
586 function line_tp
587 (
588  l
589 ) = is_vector(l) ? (len(l) == 1) ? l[0] : l
590  : is_line(l) ? l[1]
591  : undef;
592 
593 //! Return the initial point of a line or vector.
594 /***************************************************************************//**
595  \param l <line> A line or vector.
596 
597  \returns (1) <point> The initial point of the line or vector.
598  (2) Returns \b undef when \p l is not a line or vector.
599 
600  \details
601 
602  See \ref dt_line for argument specification and conventions.
603 *******************************************************************************/
604 function line_ip
605 (
606  l
607 ) = is_vector(l) ?
608  let
609  (
610  d = (len(l) == 1) ? len(l[0]) : len(l)
611  )
612  consts(d, 0)
613  : is_line(l) ? l[0]
614  : undef;
615 
616 //! Convert line to vector by shifting it to the origin.
617 /***************************************************************************//**
618  \param l <line> A line or vector.
619 
620  \returns (1) <vector> The line shifted to the origin.
621  (2) Returns \b undef when \p l is not a line or vector.
622 
623  \details
624 
625  See \ref dt_line for argument specification and conventions.
626 *******************************************************************************/
627 function vol_to_origin
628 (
629  l
630 ) = is_vector(l) ? (len(l) == 1) ? l[0] : l
631  : !is_line(l) ? undef
632  : (len(l) == 1) ? l[0]
633  : (len(l) == 2) ? (l[1]-l[0])
634  : undef;
635 
636 //! Convert a vector to a line or move a line to another coordinate point.
637 /***************************************************************************//**
638  \param l <line> A line or vector.
639  \param p <point> The new initial point.
640 
641  \returns (1) <line> The line or vector moved to \p p.
642  (2) Returns \b undef when \p l is not a line or vector or
643  when \p p is not a point.
644 
645  \details
646 
647  When \p p is not specified, the line or vector is moved to the
648  origin.
649 
650  See \ref dt_line for argument specification and conventions.
651 *******************************************************************************/
652 function vol_to_point
653 (
654  l,
655  p
656 ) = !is_line(l) ? undef
657  : !is_undef(p) && !is_point(p) ? undef
658  : let
659  (
660  d = !is_undef(p) ? p
661  : (line_dim(l) == 2) ? origin2d
662  : origin3d
663  )
664  [d, d + vol_to_origin(l)];
665 
666 //! Compute the dot product of two lines or vectors in a 3d or 2d-space.
667 /***************************************************************************//**
668  \param l1 <line-3d | line-2d> A 3d or 2d line or vector 1.
669  \param l2 <line-3d | line-2d> A 3d or 2d line or vector 2.
670 
671  \returns (1) <decimal> The dot product of \p l1 with \p l2.
672  (2) Returns \b undef when \p l1 or \p l2 is not a line or
673  vector or have different dimensions.
674 
675  \details
676 
677  This function supports the abstraction outlined in \ref dt_line.
678  The built-in operation will be more efficient in situations that do
679  not make use of this abstraction.
680 
681  See \ref dt_line for argument specification and conventions. See
682  [Wikipedia] for more information.
683 
684  [Wikipedia]: https://en.wikipedia.org/wiki/Dot_product
685 *******************************************************************************/
686 function dot_ll
687 (
688  l1,
689  l2
690 ) = !is_vol(l1) ? undef
691  : !is_vol(l2) ? undef
692  : (len(l1) != len(l2)) ? undef
693  : (vol_to_origin(l1) * vol_to_origin(l2));
694 
695 //! Compute the cross product of two lines or vectors in a 3d or 2d-space.
696 /***************************************************************************//**
697  \param l1 <line-3d | line-2d> A 3d or 2d line or vector 1.
698  \param l2 <line-3d | line-2d> A 3d or 2d line or vector 2.
699 
700  \returns (1) <decimal | vector-2d> The cross product of \p l1 with
701  \p l2.
702  (2) Returns \b undef when \p l1 or \p l2 is not a line or
703  vector or have different dimensions.
704 
705  \details
706 
707  This function supports the abstraction outlined in \ref dt_line.
708  The built-in operation will be more efficient in situations that do
709  not make use of the aforementioned abstraction.
710 
711  See \ref dt_line for argument specification and conventions. See
712  Wikipedia [cross] and [determinant] for more information.
713 
714  \note This function returns the 2x2 determinant for 2d vectors.
715 
716  [cross]: https://en.wikipedia.org/wiki/Cross_product
717  [determinant]: https://en.wikipedia.org/wiki/Determinant
718 *******************************************************************************/
719 function cross_ll
720 (
721  l1,
722  l2
723 ) = !is_vol(l1) ? undef
724  : !is_vol(l2) ? undef
725  : (len(l1) != len(l2)) ? undef
726  : cross(vol_to_origin(l1), vol_to_origin(l2));
727 
728 //! Compute the scalar triple product of three lines or vectors in a 3d or 2d-space.
729 /***************************************************************************//**
730  \param l1 <line-3d | line-2d> A 3d or 2d line or vector 1.
731  \param l2 <line-3d | line-2d> A 3d or 2d line or vector 2.
732  \param l3 <line-3d | line-2d> A 3d or 2d line or vector 3.
733 
734  \returns (1) <decimal | vector-2d> The scalar triple product.
735  (2) Returns \b undef when \p l1, \p l2, or \p l3 is not a
736  line or vector or have different dimensions,
737 
738  \details
739 
740  Returns a 2d vector result for 2d vectors. The cross product
741  computes the 2x2 determinant of the vectors <tt>(l2 x l3)</tt>, a
742  scalar value, which is then \e multiplied by \c l1.
743 
744  \verbatim
745  [l1, l2, l3] = l1 * (l2 x l3)
746  \endverbatim
747 
748  See \ref dt_line for argument specification and conventions. See
749  [Wikipedia] for more information.
750 
751  [Wikipedia]: https://en.wikipedia.org/wiki/Triple_product
752 *******************************************************************************/
753 function striple_lll
754 (
755  l1,
756  l2,
757  l3
758 ) = !is_vol(l1) ? undef
759  : !is_vol(l2) ? undef
760  : !is_vol(l3) ? undef
761  : (len(l1) != len(l2)) ? undef
762  : (len(l2) != len(l3)) ? undef
763  : (vol_to_origin(l1) * cross_ll(l2, l3));
764 
765 //! Compute the angle between two lines or vectors in a 3d or 2d-space.
766 /***************************************************************************//**
767  \param l1 <line-3d | line-2d> A 3d or 2d line or vector 1.
768  \param l2 <line-3d | line-2d> A 3d or 2d line or vector 2.
769  \param s <boolean> Return the 2d signed angle.
770 
771  \returns (1) <decimal> The angle between the two lines or vectors in
772  degrees.
773  (2) Returns \b undef when \p l1 or \p l2 is not a line or
774  vector or have different dimensions or when they do not
775  intersect.
776 
777  \details
778 
779  For 2d lines or vectors, the signed angle is calculated. The
780  parameter \p s determines if the \em signed (\p s == \b true) or
781  \em positive (\p s == \b false) angle is returned. For 3d lines or
782  vectors, a normal is required to uniquely identify the
783  perpendicular plane and axis of rotation. This function calculates
784  the positive angle, and the plane and axis of rotation will be that
785  which fits this assumed positive angle.
786 
787  See \ref dt_line for argument specification and conventions.
788 
789  \sa angle_lll().
790 *******************************************************************************/
791 function angle_ll
792 (
793  l1,
794  l2,
795  s = true
796 ) = !is_vol(l1) ? undef
797  : !is_vol(l2) ? undef
798  : let
799  (
800  d = line_dim(l1) + line_dim(l2)
801  )
802  // two 2d
803  (d == 4) ?
804  let
805  (
806  sa = atan2(cross_ll(l1, l2), dot_ll(l1, l2))
807  )
808  ((sa < 0) && (s == false)) ? sa+360
809  : sa
810  // two 3d
811  : (d == 6) ?
812  atan2(distance_pp(cross_ll(l1, l2)), dot_ll(l1, l2))
813  : undef;
814 
815 //! Compute the angle between two lines or vectors in a 3d-space.
816 /***************************************************************************//**
817  \param l1 <line-3d> A 3d line or vector 1.
818  \param l2 <line-3d> A 3d line or vector 2.
819  \param n <line-3d> A 3d normal line or vector.
820 
821  \returns (1) <decimal> The angle between the two lines or vectors in
822  degrees.
823  (2) Returns \b undef when \p l1, \p l2, or \p n is not a
824  line or vector or have different dimensions or when
825  they do not intersect.
826 
827  \details
828 
829  See \ref dt_line for argument specification and conventions.
830 
831  \sa angle_ll().
832 *******************************************************************************/
833 function angle_lll
834 (
835  l1,
836  l2,
837  n
838 ) = !is_vol(l1) ? undef
839  : !is_vol(l2) ? undef
840  : !is_vol(n) ? undef
841  : (len(l1) != len(l2)) ? undef
842  : (len(l2) != len(n)) ? undef
843  : atan2(striple_lll(n, l1, l2), dot_ll(l1, l2));
844 
845 //! Compute the normalized unit vector of a line or vector.
846 /***************************************************************************//**
847  \param l <line> A line or vector.
848 
849  \returns (1) <vector> The normalized unit vector.
850  (2) Returns \b undef when \p l is not a line or vector.
851 
852  \details
853 
854  See \ref dt_line for argument specification and conventions.
855 *******************************************************************************/
856 function unit_l
857 (
858  l
859 ) = !is_vol(l) ? undef
861 
862 //! Test if three lines or vectors are coplanar in 3d-space.
863 /***************************************************************************//**
864  \param l1 <line-3d> A 3d line or vector 1.
865  \param l2 <line-3d> A 3d line or vector 2.
866  \param l3 <line-3d> A 3d line or vector 3.
867  \param d <integer> The number of decimal places to consider.
868 
869  \returns (1) <boolean> \b true when all three lines or vectors are
870  coplanar, and \b false otherwise.
871  (2) Returns \b undef when \p l1, \p l2, or \p l3 is not a
872  line or vector or have different dimensions,
873 
874  \details
875 
876  See \ref dt_line for argument specification and conventions. See
877  [Wikipedia] for more information.
878 
879  \note When lines or vectors are specified with start and end
880  points, this function tests if they are in a planes
881  parallel to the coplanar.
882 
883  [Wikipedia]: https://en.wikipedia.org/wiki/Coplanarity
884 *******************************************************************************/
885 function are_coplanar_lll
886 (
887  l1,
888  l2,
889  l3,
890  d = 6
891 ) = !is_vol(l1) ? undef
892  : !is_vol(l2) ? undef
893  : !is_vol(l3) ? undef
894  : (len(l1) != len(l2)) ? undef
895  : (len(l2) != len(l3)) ? undef
896  : (round_d(striple_lll(l1, l2, l3), d) == 0);
897 
898 //----------------------------------------------------------------------------//
899 // set 5: plane and pnorm
900 //----------------------------------------------------------------------------//
901 
902 //! Convert a planes' normal specification into a normal vector.
903 /***************************************************************************//**
904  \param n <pnorm> A plane normal [specification].
905 
906  \param cw <boolean> Point ordering. When the plane specified as
907  non-collinear points, this indicates ordering.
908 
909  \returns (1) <normal> A vector-3d normal to the plane.
910  (2) Returns \b undef when \p n is not a plane.
911 
912  \details
913 
914  When \p n is not a valid plane, \b undef is returned. The computed
915  normal vector is not normalized to its unit vector.
916 
917  See \ref dt_pnorm for argument specification and conventions.
918 
919  [specification]: \ref dt_pnorm
920 *******************************************************************************/
921 function plane_to_normal
922 (
923  n,
924  cw = true
925 ) = !is_plane(n) ? undef
926 
927  // n is normal
929 
930  // make 3d
931  : let
932  (
933  q = [for (i=n) point_to_3d(i)]
934  )
935  (len(n) == 2) ?
936  // vectors [v1, v2].
937  cross(q[0], q[1])
938 
939  // 3 points.
940  : cross(q[0]-q[1], q[2]-q[1]) * ((cw == true) ? 1 : -1);
941 
942 //! @}
943 //! @}
944 
945 //----------------------------------------------------------------------------//
946 // openscad-amu auxiliary scripts
947 //----------------------------------------------------------------------------//
948 
949 /*
950 BEGIN_SCOPE validate;
951  BEGIN_OPENSCAD;
952  include <omdl-base.scad>;
953  include <common/validation.scad>;
954 
955  echo( str("openscad version ", version()) );
956 
957  // test-values columns
958  test_c =
959  [
960  ["id", "identifier"],
961  ["td", "description"],
962  ["tv", "test value"]
963  ];
964 
965  // test-values rows
966  test_r =
967  [
968  ["fac", "Function argument count", undef
969  ],
970  ["crp", "Result precision", undef
971  ],
972  ["t01", "All undefined", [undef,undef,undef,undef,undef,undef]
973  ],
974  ["t02", "All empty lists", [empty_lst,empty_lst,empty_lst,empty_lst,empty_lst,empty_lst]
975  ],
976  ["t03", "All scalars", [60, 50, 40, 30, 20, 10]
977  ],
978  ["t04", "All 1d vectors", [[99], [58], [12], [42], [15], [1]]
979  ],
980  ["t05", "All 2d vectors", [
981  [99,2], [58,16], [12,43],
982  [42,13], [15,59], [1,85]
983  ]
984  ],
985  ["t06", "All 3d vectors", [
986  [199,20,55], [158,116,75], [12,43,90],
987  [42,13,34], [15,59,45], [62,33,69]
988  ]
989  ],
990  ["t07", "All 4d vectors", [
991  [169,27,35,10], [178,016,25,20], [12,43,90,30],
992  [42,13,34,60], [15,059,45,50], [62,33,69,40]
993  ]
994  ],
995  ["t08", "Orthogonal vectors", [
996  +x_axis3d_uv, +y_axis3d_uv, +z_axis3d_uv,
997  -x_axis3d_uv, -y_axis3d_uv, -z_axis3d_uv,
998  ]
999  ],
1000  ["t09", "Coplanar vectors", [
1001  +x_axis3d_uv, +y_axis3d_uv, [2,2,0],
1002  origin3d, origin3d, origin3d,
1003  ]
1004  ]
1005  ];
1006 
1007  test_ids = table_get_row_ids( test_r );
1008 
1009  // expected columns: ("id" + one column for each test)
1010  good_c = merge_p([concat("id", test_ids), concat("identifier", test_ids)]);
1011 
1012  // expected rows: ("golden" test results), use 'skip' to skip test
1013  skip = -1; // skip test
1015  good_r =
1016  [ // function
1017  ["distance_pp",
1018  2, // fac
1019  4, // crp
1020  undef, // t01
1021  undef, // t02
1022  undef, // t03
1023  undef, // t04
1024  43.3244, // t05
1025  106.2873, // t06
1026  undef, // t07
1027  1.4142, // t08
1028  1.4142 // t09
1029  ],
1030  ["is_left_ppp",
1031  3, // fac
1032  4, // crp
1033  undef, // t01
1034  undef, // t02
1035  undef, // t03
1036  undef, // t04
1037  -463, // t05
1038  17009, // t06
1039  undef, // t07
1040  1, // t08
1041  -3 // t09
1042  ],
1043  ["point_to_3d",
1044  1, // fac
1045  4, // crp
1046  undef, // t01
1047  undef, // t02
1048  undef, // t03
1049  undef, // t04
1050  [99,2,0], // t05
1051  [199,20,55], // t06
1052  undef, // t07
1053  x_axis3d_uv, // t08
1054  x_axis3d_uv // t09
1055  ],
1056  ["line_dim",
1057  2, // fac
1058  4, // crp
1059  undef, // t01
1060  undef, // t02
1061  2, // t03
1062  undef, // t04
1063  2, // t05
1064  3, // t06
1065  undef, // t07
1066  3, // t08
1067  3 // t09
1068  ],
1069  ["line_tp",
1070  2, // fac
1071  4, // crp
1072  undef, // t01
1073  undef, // t02
1074  [60,50], // t03
1075  undef, // t04
1076  [58,16], // t05
1077  [158,116,75], // t06
1078  undef, // t07
1079  y_axis3d_uv, // t08
1080  y_axis3d_uv // t09
1081  ],
1082  ["line_ip",
1083  2, // fac
1084  4, // crp
1085  undef, // t01
1086  undef, // t02
1087  origin2d, // t03
1088  undef, // t04
1089  [99,2], // t05
1090  [199,20,55], // t06
1091  undef, // t07
1092  x_axis3d_uv, // t08
1093  x_axis3d_uv // t09
1094  ],
1095  ["vol_to_origin",
1096  2, // fac
1097  4, // crp
1098  undef, // t01
1099  undef, // t02
1100  [60,50], // t03
1101  undef, // t04
1102  [-41,14], // t05
1103  [-41,96,20], // t06
1104  undef, // t07
1105  [-1,1,0], // t08
1106  [-1,1,0] // t09
1107  ],
1108  ["dot_ll",
1109  4, // fac
1110  4, // crp
1111  undef, // t01
1112  undef, // t02
1113  3900, // t03
1114  undef, // t04
1115  -1650, // t05
1116  -5230, // t06
1117  undef, // t07
1118  1, // t08
1119  0 // t09
1120  ],
1121  ["cross_ll",
1122  4, // fac
1123  4, // crp
1124  skip, // t01
1125  skip, // t02
1126  skip, // t03
1127  skip, // t04
1128  810, // t05
1129  [-4776,-1696,-1650], // t06
1130  skip, // t07
1131  [-1,-1,1], // t08
1132  [0,0,4] // t09
1133  ],
1134  ["striple_lll",
1135  6, // fac
1136  4, // crp
1137  skip, // t01
1138  skip, // t02
1139  skip, // t03
1140  skip, // t04
1141  [-14760,5040], // t05
1142  -219976, // t06
1143  skip, // t07
1144  -2, // t08
1145  0 // t09
1146  ],
1147  ["angle_ll",
1148  4, // fac
1149  4, // crp
1150  undef, // t01
1151  undef, // t02
1152  -2.9357, // t03
1153  undef, // t04
1154  153.8532, // t05
1155  134.4573, // t06
1156  undef, // t07
1157  60, // t08
1158  90 // t09
1159  ],
1160  ["angle_lll",
1161  6, // fac
1162  4, // crp
1163  skip, // t01
1164  skip, // t02
1165  skip, // t03
1166  skip, // t04
1167  skip, // t05
1168  -91.362, // t06
1169  skip, // t07
1170  -63.4349, // t08
1171  0 // t09
1172  ],
1173  ["unit_l",
1174  2, // fac
1175  4, // crp
1176  undef, // t01
1177  undef, // t02
1178  [.7682,0.6402], // t03
1179  undef, // t04
1180  [-0.9464,0.3231], // t05
1181  [-0.3857,0.9032,0.1882], // t06
1182  undef, // t07
1183  [-0.7071,0.7071,0], // t08
1184  [-0.7071,0.7071,0] // t09
1185  ],
1186  ["are_coplanar_lll",
1187  6, // fac
1188  4, // crp
1189  skip, // t01
1190  skip, // t02
1191  skip, // t03
1192  skip, // t04
1193  skip, // t05
1194  false, // t06
1195  skip, // t07
1196  false, // t08
1197  true // t09
1198  ],
1199  ["plane_to_normal",
1200  2, // fac
1201  4, // crp
1202  skip, // t01
1203  skip, // t02
1204  [60,50,0], // t03
1205  skip, // t04
1206  [0,0,1468], // t05
1207  [-4880,-6235,19924], // t06
1208  skip, // t07
1209  z_axis3d_uv, // t08
1210  z_axis3d_uv // t09
1211  ]
1212  ];
1213 
1214  // sanity-test tables
1215  table_check( test_r, test_c, false );
1216  table_check( good_r, good_c, false );
1217 
1218  // validate helper function and module
1219  function get_value( vid ) = table_get_value(test_r, test_c, vid, "tv");
1220  function gv( vid, e ) = get_value( vid )[e];
1221  module log_test( m ) { log_type ( "test", m ); }
1222  module log_skip( f ) { log_test ( str("ignore: '", f, "'") ); }
1223  module run( fname, vid )
1224  {
1225  value_text = table_get_value(test_r, test_c, vid, "td");
1226 
1227  if ( table_get_value(good_r, good_c, fname, vid) != skip )
1228  children();
1229  else
1230  log_test( str(vid, " -skip-: '", fname, "(", value_text, ")'") );
1231  }
1232  module test( fname, fresult, vid, pair )
1233  {
1234  value_text = table_get_value(test_r, test_c, vid, "td");
1235  fname_argc = table_get_value(good_r, good_c, fname, "fac");
1236  comp_prcsn = table_get_value(good_r, good_c, fname, "crp");
1237  pass_value = table_get_value(good_r, good_c, fname, vid);
1238 
1239  test_pass = validate(cv=fresult, t="almost", ev=pass_value, p=comp_prcsn, pf=true);
1240  farg_text = (pair == true)
1241  ? strl(append_e(", ", sequence_ns(select_r(get_value(vid), [0:fname_argc-1]), n=2, s=2), r=false, j=false, l=false))
1242  : strl(append_e(", ", select_r(get_value(vid), [0:fname_argc-1]), r=false, j=false, l=false));
1243  test_text = validate(str(fname, "(", farg_text, ")=", pass_value), fresult, "almost", pass_value, comp_prcsn);
1244 
1245  if ( pass_value != skip )
1246  {
1247  if ( !test_pass )
1248  log_test( str(vid, " ", test_text, " (", value_text, ")") );
1249  else
1250  log_test( str(vid, " ", test_text) );
1251  }
1252  else
1253  log_test( str(vid, " -skip-: '", fname, "(", value_text, ")'") );
1254  }
1255 
1256  // Indirect function calls would be very useful here!!!
1257  run_ids = delete( test_ids, mv=["fac", "crp"] );
1258 
1259  // set 1: identify
1260  log_skip( "is_point()" );
1261  log_skip( "is_vector()" );
1262  log_skip( "is_line()" );
1263  log_skip( "is_vol()" );
1264  log_skip( "is_plane()" );
1265 
1266  // set 2: point
1267  for (vid=run_ids) run("distance_pp",vid) test( "distance_pp", distance_pp(gv(vid,0),gv(vid,1)), vid, false );
1268  log_skip( "distance_pl()" );
1269  log_skip( "distance_pn()" );
1270  for (vid=run_ids) run("is_left_ppp",vid) test( "is_left_ppp", is_left_ppp(gv(vid,0),gv(vid,1),gv(vid,2)), vid, false );
1271  log_skip( "point_closest_pl()" );
1272  log_skip( "point_closest_pn()" );
1273  for (vid=run_ids) run("point_to_3d",vid) test( "point_to_3d", point_to_3d(gv(vid,0)), vid, false );
1274  log_skip( "interpolate2d_l_pp()" );
1275 
1276  // set 3: vector
1277 
1278  // set 4: line (or vector)
1279  log_skip( "line2d_new()" );
1280  log_skip( "line3d_new()" );
1281  log_skip( "line_new()" );
1282  for (vid=run_ids) run("line_dim",vid) test( "line_dim", line_dim([gv(vid,0),gv(vid,1)]), vid, true );
1283  for (vid=run_ids) run("line_tp",vid) test( "line_tp", line_tp([gv(vid,0),gv(vid,1)]), vid, true );
1284  for (vid=run_ids) run("line_ip",vid) test( "line_ip", line_ip([gv(vid,0),gv(vid,1)]), vid, true );
1285  for (vid=run_ids) run("vol_to_origin",vid) test( "vol_to_origin", vol_to_origin([gv(vid,0),gv(vid,1)]), vid, true );
1286  log_skip( "vol_to_point()" );
1287  for (vid=run_ids) run("dot_ll",vid) test( "dot_ll", dot_ll([gv(vid,0),gv(vid,1)],[gv(vid,2),gv(vid,3)]), vid, true );
1288  for (vid=run_ids) run("cross_ll",vid) test( "cross_ll", cross_ll([gv(vid,0),gv(vid,1)],[gv(vid,2),gv(vid,3)]), vid, true );
1289  for (vid=run_ids) run("striple_lll",vid) test( "striple_lll", striple_lll([gv(vid,0),gv(vid,1)],[gv(vid,2),gv(vid,3)],[gv(vid,4),gv(vid,5)]), vid, true );
1290  for (vid=run_ids) run("angle_ll",vid) test( "angle_ll", angle_ll([gv(vid,0),gv(vid,1)],[gv(vid,2),gv(vid,3)]), vid, true );
1291  for (vid=run_ids) run("angle_lll",vid) test( "angle_lll", angle_lll([gv(vid,0),gv(vid,1)],[gv(vid,2),gv(vid,3)],[gv(vid,4),gv(vid,5)]), vid, true );
1292  for (vid=run_ids) run("unit_l",vid) test( "unit_l", unit_l([gv(vid,0),gv(vid,1)]), vid, true );
1293  for (vid=run_ids) run("are_coplanar_lll",vid) test( "are_coplanar_lll", are_coplanar_lll([gv(vid,0),gv(vid,1)],[gv(vid,2),gv(vid,3)],[gv(vid,4),gv(vid,5)]), vid, true );
1294 
1295  // set 5: plane and pnorm
1296  for (vid=run_ids) run("plane_to_normal",vid) test( "plane_to_normal", plane_to_normal([gv(vid,0),gv(vid,1)]), vid, true );
1297 
1298  // end_include
1299  END_OPENSCAD;
1300 
1301  BEGIN_MFSCRIPT;
1302  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
1303  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
1304  END_MFSCRIPT;
1305 END_SCOPE;
1306 */
1307 
1308 //----------------------------------------------------------------------------//
1309 // end of file
1310 //----------------------------------------------------------------------------//
origin2d
<point-2d> The origin point coordinate in 2d Euclidean space.
Definition: constants.scad:407
origin3d
<point-3d> The origin point coordinate in 3-dimensional Euclidean space.
Definition: constants.scad:425
function is_vector(v)
Test if a value defines a Euclidean vector.
function line_tp(l)
Return the terminal point of a line or vector.
function line_new(m=1, a=0, t, p1, p2, v)
Construct a directed line.
function dot_ll(l1, l2)
Compute the dot product of two lines or vectors in a 3d or 2d-space.
function are_coplanar_lll(l1, l2, l3, d=6)
Test if three lines or vectors are coplanar in 3d-space.
function point_closest_pn(p, n, np)
Compute the coordinates of the closest point on a plane to a given point.
function angle_ll(l1, l2, s=true)
Compute the angle between two lines or vectors in a 3d or 2d-space.
function line3d_new(m=1, a=0, t=90, p1=origin3d, p2, v)
Construct a 3 dimensional directed line.
function line_dim(l)
Return the number of dimensions of a line or vector.
function striple_lll(l1, l2, l3)
Compute the scalar triple product of three lines or vectors in a 3d or 2d-space.
function angle_lll(l1, l2, n)
Compute the angle between two lines or vectors in a 3d-space.
function is_point(v)
Test if a value defines a point.
function distance_pn(p, n, np)
Compute the shortest distance between a point and a plane.
function vol_to_origin(l)
Convert line to vector by shifting it to the origin.
function point_to_3d(p)
Return 3d point unchanged or add a zeroed third dimension to 2d point.
function vol_to_point(l, p)
Convert a vector to a line or move a line to another coordinate point.
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 plane_to_normal(n, cw=true)
Convert a planes' normal specification into a normal vector.
function point_closest_pl(p, l)
Compute the coordinates of the closest point on a line to a given point.
function is_line(v)
Test if a value defines a line.
function distance_pl(p, l)
Compute the shortest distance between a point and a line.
function is_plane(v)
Test if a value defines a plane.
function is_vol(v)
Test if a value defines a vector or a line.
function distance_pp(p1, p2)
Compute the distance between two points.
function all_numbers(v, c=0)
Test if all elements of an iterable value are numbers.
function round_d(v, d=6)
Round a list of numbers to a fixed number of decimal point digits.
function consts(l, v, u=false)
Create a list of constant or incrementing elements.
function sum(v, i1, i2)
Compute the sum of a list of numbers.
function is_between(v, l, u)
Test if a numerical value is between an upper and lower bounds.
function is_number(v)
Test if a value is a number.