omdl  v1.0
OpenSCAD Mechanical Design Library
angle.scad
Go to the documentation of this file.
1 //! Angle units and conversions.
2 /***************************************************************************//**
3  \file
4  \author Roy Allen Sutton
5  \date 2015-2024
6 
7  \copyright
8 
9  This file is part of [omdl] (https://github.com/royasutton/omdl),
10  an OpenSCAD mechanical design library.
11 
12  The \em omdl is free software; you can redistribute it and/or modify
13  it under the terms of the [GNU Lesser General Public License]
14  (http://www.gnu.org/licenses/lgpl.html) as published by the Free
15  Software Foundation; either version 2.1 of the License, or (at
16  your option) any later version.
17 
18  The \em omdl is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with the \em omdl; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26  02110-1301, USA; or see <http://www.gnu.org/licenses/>.
27 
28  \details
29 
30  \amu_define group_name (Angle Units)
31  \amu_define group_brief (Angle units and conversions.)
32 
33  \amu_include (include/amu/doxyg_init_pd_gds_ipg.amu)
34 *******************************************************************************/
35 
36 // auto-tests (append 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  \b Parameter \b naming
64 
65  - \p a is the angle value to convert. It has no default and must
66  always be supplied by the caller. It may be a scalar for \c "r"
67  or \c "d", or a 3-element list for \c "dms"; the required type
68  depends on the source unit.
69  - \p from identifies the source unit; defaults to
70  \ref angle_unit_default.
71  - \p to identifies the target unit; defaults to
72  \ref angle_unit_base.
73  - \p u is a unit-identifier string used in name-lookup functions;
74  defaults to \ref angle_unit_default.
75 
76  \b Return \b values
77 
78  - All functions return \b undef for unrecognised unit identifiers.
79  Callers are responsible for testing return values when inputs may
80  be dynamic.
81  - The return type matches the target unit: a scalar for \c "r" or
82  \c "d", and a 3-element list for \c "dms".
83 
84  \b DMS \b format
85 
86  - The \c "dms" (degree, minute, second) type is represented as a
87  3-element list \c [degrees, minutes, seconds].
88  - The sign of the angle is carried exclusively by the \p degrees
89  component. The \p minutes and \p seconds components are always
90  non-negative. For example, −30°15′20″ is encoded as
91  \c [-30, 15, 20].
92  - Supplying a \c "dms" value with mixed signs (e.g. \c [-30,-15,0])
93  is not supported and produces undefined results.
94 
95  \b Global \b configuration
96 
97  - \ref angle_unit_base sets the storage base unit for the entire
98  design. It defaults to \c "d" and is intended to be overridden
99  at the top of a design file or within a child scope before any
100  angle function is called.
101  - \ref angle_unit_default sets the assumed input unit when \p from
102  or \p u is not specified. It also defaults to \c "d".
103  - Both variables must be assigned before any angle function is
104  called; changing them after dependent assignments have already
105  been evaluated has no effect on those prior results.
106 
107  \b Unit \b identifiers
108 
109  - Unit identifier strings are case-sensitive and lowercase
110  (e.g. \c "r", \c "d", \c "dms").
111 
112  \b Shorthand \b functions
113 
114  - The \c a_deg() and \c a_rad() helpers are convenience wrappers
115  that fix \p from to their named unit. The \p to parameter
116  defaults to \ref angle_unit_base and may be overridden when a
117  non-default target unit is needed.
118 
119  These functions allow for angles to be specified with units.
120  Angles specified with units are independent of (\ref angle_unit_base).
121  There are also unit conversion functions for converting from one unit
122  to another.
123 
124  The table below enumerates the supported units.
125 
126  units id | description | type |
127  :---------:|:----------------------:|:---------------:|
128  r | radian | decimal |
129  d | degree | decimal |
130  dms | degree, minute, second | decimal-list-3 |
131 
132 
133  \amu_define title (Angle base unit example)
134  \amu_define scope_id (example)
135  \amu_define output_scad (true)
136  \amu_define output_console (false)
137  \amu_include (include/amu/scope.amu)
138 
139  \amu_define output_scad (false)
140  \amu_define output_console (true)
141 
142  \amu_define title (angle_unit_base=r)
143  \amu_define scope_id (example_r)
144  \amu_include (include/amu/scope.amu)
145 
146  \amu_define title (angle_unit_base=d)
147  \amu_define scope_id (example_d)
148  \amu_include (include/amu/scope.amu)
149 
150  \amu_define title (angle_unit_base=dms)
151  \amu_define scope_id (example_dms)
152  \amu_include (include/amu/scope.amu)
153 *******************************************************************************/
154 
155 //----------------------------------------------------------------------------//
156 // members
157 //----------------------------------------------------------------------------//
158 
159 //! <string> The base units for value storage.
160 //! \note This variable is intended to be overridden at the top of a
161 //! design file or in a child scope. All angle functions that omit
162 //! the \p to parameter will convert to this unit.
163 angle_unit_base = "d";
164 
165 //! <string> The default units when unspecified.
166 //! \note This variable is intended to be overridden at the top of a
167 //! design file or in a child scope. All angle functions that omit
168 //! the \p from or \p u parameter will assume this unit.
169 angle_unit_default = "d";
170 
171 //! Return the name of an angle unit identifier.
172 /***************************************************************************//**
173  \param u <string> An angle unit identifier.
174 
175  \returns <string> The units name for the given angle unit identifier.
176  Returns \b undef for identifiers that are not defined.
177 *******************************************************************************/
178 function angle_unit_name
179 (
181 ) = u == "r" ? "radian"
182  : u == "d" ? "degree"
183  : u == "dms" ? "degree, minute, second"
184  : undef;
185 
186 //! Convert an angle from degrees to other units.
187 /***************************************************************************//**
188  \param a <decimal | decimal-list-3> An angle to convert.
189  \param to <string> The units to which the angle should be converted.
190  Defaults to \ref angle_unit_base.
191 
192  \returns <decimal | decimal-list-3> The conversion result.
193  Returns \b undef for identifiers that are not defined. For
194  \c "dms" output the sign is carried by the degrees
195  component only; minutes and seconds are always
196  non-negative.
197 
198  \note The radian conversion uses \c tau (= 2π), which is defined
199  in the omdl base library (\c omdl-base.scad).
200 
201  \private
202 *******************************************************************************/
203 function _angle_unit_d2
204 (
205  a,
206  to = angle_unit_base
207 ) = to == "r" ? (a * tau / 360)
208  : to == "d" ? (a)
209  : to == "dms" ?
210  // use abs(a) so floor() behaves correctly for negative angles;
211  // sign is preserved on the degrees component only
212  let
213  (
214  sign = (a < 0) ? -1 : 1,
215  aa = abs(a),
216  deg = floor(aa),
217  min = floor((aa - deg) * 60),
218  sec = (aa - deg - min/60) * 3600
219  )
220  [ sign * deg, min, sec ]
221  : undef;
222 
223 //! Convert an angle from some units to degrees.
224 /***************************************************************************//**
225  \param a <decimal | decimal-list-3> An angle to convert.
226  \param from <string> The units of the angle to be converted.
227  Defaults to \ref angle_unit_default.
228 
229  \returns <decimal> The conversion result in degrees.
230  Returns \b undef for identifiers that are not defined. For
231  \c "dms" input the sign must be on the degrees component
232  only; minutes and seconds must be non-negative.
233 
234  \note The radian conversion uses \c tau (= 2π), which is defined
235  in the omdl base library (\c omdl-base.scad).
236 
237  \private
238 *******************************************************************************/
239 function _angle_unit_2d
240 (
241  a,
242  from = angle_unit_default
243 ) = from == "r" ? (a * 360 / tau)
244  : from == "d" ? (a)
245  : from == "dms" ?
246  // sign lives on the degrees component only; minutes and seconds
247  // are always non-negative magnitudes
248  let( sign = (a[0] < 0) ? -1 : 1, adeg = abs(a[0]) )
249  sign * (adeg + a[1]/60 + a[2]/3600)
250  : undef;
251 
252 //! Convert an angle value from one unit to another.
253 /***************************************************************************//**
254  \param a <decimal | decimal-list-3> The angle to convert.
255  \param from <string> The units of the angle to be converted.
256  Defaults to \ref angle_unit_default.
257  \param to <string> The units to which the angle should be converted.
258  Defaults to \ref angle_unit_base. Conversion to \c "d"
259  is short-circuited: the intermediate d to step is
260  skipped.
261 
262  \returns <decimal | decimal-list-3> The conversion result. Return
263  type is a scalar for \c "r" or \c "d", and a 3-element list
264  for \c "dms". Returns \b undef for identifiers that are not
265  defined.
266 *******************************************************************************/
267 function angle
268 (
269  a,
270  from = angle_unit_default,
271  to = angle_unit_base
272 ) = (from == to) ? a
273  : let( d = _angle_unit_2d( a, from ) )
274  (d == undef) ? undef
275  // short-circuit: 2d result is already the answer when target is degrees
276  : (to == "d") ? d
277  : _angle_unit_d2( d, to );
278 
279 //! Convert an angle value from one unit to another (direction-swapped defaults).
280 /***************************************************************************//**
281  \param a <decimal | decimal-list-3> The angle to convert.
282  \param from <string> The units of the angle to be converted.
283  Defaults to \ref angle_unit_base.
284  \param to <string> The units to which the angle should be converted.
285  Defaults to \ref angle_unit_default.
286 
287  \returns <decimal | decimal-list-3> The conversion result. Return type
288  is a scalar for \c "r" or \c "d", and a 3-element list for
289  \c "dms". Returns \b undef for identifiers that are not
290  defined.
291 
292  \note This is a convenience alias for \ref angle with \p from and
293  \p to defaults swapped. It is useful when the natural
294  direction of a design is from the base unit back to a
295  display or input unit.
296 *******************************************************************************/
297 function angle_inv
298 (
299  a,
300  from = angle_unit_base,
301  to = angle_unit_default
302 ) = angle(a=a, from=from, to=to);
303 
304 //----------------------------------------------------------------------------//
305 // shorthand conversions
306 //----------------------------------------------------------------------------//
307 
308 //! \name Shorts
309 //! @{
310 //!
311 //! Convenience wrappers around \ref angle that fix the \p from unit
312 //! to a named unit. The \p to parameter defaults to \ref angle_unit_base
313 //! and may be overridden when a non-default target unit is needed.
314 //! Use \ref angle directly when the source unit is also non-default.
315 
316 //! Shorthand angle conversion for degrees.
317 /***************************************************************************//**
318  \param a <decimal> The angle to convert.
319  \param to <string> The units to which the angle should be converted.
320  Defaults to \ref angle_unit_base.
321 
322  \returns <decimal> The conversion result.
323 *******************************************************************************/
324 function a_deg(a, to=angle_unit_base) = angle(a=a, from="d", to=to);
325 
326 //! Shorthand angle conversion for radians.
327 /***************************************************************************//**
328  \param a <decimal> The angle to convert.
329  \param to <string> The units to which the angle should be converted.
330  Defaults to \ref angle_unit_base.
331 
332  \returns <decimal> The conversion result.
333 *******************************************************************************/
334 function a_rad(a, to=angle_unit_base) = angle(a=a, from="r", to=to);
335 
336 //! @}
337 
338 //! @}
339 //! @}
340 
341 //----------------------------------------------------------------------------//
342 // openscad-amu auxiliary scripts
343 //----------------------------------------------------------------------------//
344 
345 /*
346 BEGIN_SCOPE validate;
347  BEGIN_OPENSCAD;
348  include <omdl-base.scad>;
349  include <common/validation.scad>;
350 
351  echo( str("openscad version ", version()) );
352  for (i=[1:3]) echo( "not tested:" );
353 
354  // end_include
355  END_OPENSCAD;
356 
357  BEGIN_MFSCRIPT;
358  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
359  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
360  END_MFSCRIPT;
361 END_SCOPE;
362 */
363 
364 /*
365 BEGIN_SCOPE example;
366  BEGIN_OPENSCAD;
367  include <omdl-base.scad>;
368 
369  angle_unit_base = "d";
370  angle_unit_default = "r";
371 
372  // get unit names
373  bu = angle_unit_name(angle_unit_base);
374  du = angle_unit_name();
375 
376  // absolute angle measurements in base unit.
377  c1 = angle(pi/6);
378  c2 = angle(pi/4);
379  c3 = angle(180, "d");
380  c4 = angle([30, 15, 50], "dms");
381 
382  // convert between units.
383  c5 = angle([30, 15, 50], from="dms", to="r");
384  c6 = angle(0.528205, to="dms");
385 
386  // end_include
387 
388  echo( bu=bu );
389  echo( du=du );
390  echo( );
391  echo( c1=c1 );
392  echo( c2=c2 );
393  echo( c3=c3 );
394  echo( c4=c4 );
395  echo( c5=c5 );
396  echo( c6=c6 );
397  END_OPENSCAD;
398 
399  BEGIN_MFSCRIPT;
400  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
401 
402  defines name "units" define "angle_unit_base" strings "r d dms";
403  variables add_opts_combine "units";
404 
405  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
406  END_MFSCRIPT;
407 END_SCOPE;
408 */
409 
410 //----------------------------------------------------------------------------//
411 // end of file
412 //----------------------------------------------------------------------------//
tau
<decimal> The ratio of a circle's circumference to its radius.
Definition: constants.scad:201
function angle(a, from=angle_unit_default, to=angle_unit_base)
Convert an angle value from one unit to another.
function a_rad(a, to=angle_unit_base)
Shorthand angle conversion for radians.
angle_unit_default
Definition: angle.scad:747
function angle_unit_name(u=angle_unit_default)
Return the name of an angle unit identifier.
angle_unit_base
Definition: angle.scad:741
function a_deg(a, to=angle_unit_base)
Shorthand angle conversion for degrees.
function angle_inv(a, from=angle_unit_base, to=angle_unit_default)
Convert an angle value from one unit to another (direction-swapped defaults).