omdl  v1.0
OpenSCAD Mechanical Design Library
length.scad
Go to the documentation of this file.
1 //! Length 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 (Length Units)
31  \amu_define group_brief (Length 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 v is the numeric value to convert. It has no default and must
66  always be supplied by the caller. For \p d = 1 it may be a scalar
67  or a list; the conversion is applied element-wise. For \p d = 2
68  or \p d = 3 it must be a scalar.
69  - \p from identifies the source unit; defaults to
70  \ref length_unit_default.
71  - \p to identifies the target unit; defaults to
72  \ref length_unit_base.
73  - \p d is the dimensional exponent: 1 (linear), 2 (area), 3
74  (volume). Values outside [1|2|3] yield \b undef.
75  - \p u is a unit-identifier string used in name-lookup functions;
76  defaults to \ref length_unit_default.
77  - \p w selects word (\b true) vs symbol (\b false) format in
78  name-return functions; defaults to \b false.
79 
80  \b Return \b values
81 
82  - All functions return \b undef for unrecognised unit identifiers
83  or out-of-range dimension values. Callers are responsible for
84  testing return values when inputs may be dynamic.
85 
86  \b Global \b configuration
87 
88  - \ref length_unit_base sets the storage base unit for the entire
89  design. It defaults to \c "mm" and is intended to be overridden
90  at the top of a design file or within a child scope before any
91  length function is called.
92  - \ref length_unit_default sets the assumed input unit when \p from
93  or \p u is not specified. It also defaults to \c "mm" and may be
94  overridden in the same way.
95  - Both variables must be assigned before any length function is
96  called; changing them after assignments that depend on them have
97  already been evaluated has no effect on those prior results.
98 
99  \b Unit \b identifiers
100 
101  - Unit identifier strings are case-sensitive and lowercase
102  (e.g. \c "mm", \c "in").
103  - The identifiers \c "thou" and \c "mil" denote the same physical
104  unit (one thousandth of an inch, 0.0254 mm) and are
105  interchangeable in all conversion functions.
106 
107  \b Shorthand \b functions
108 
109  - The \c l_mm() and \c l_in() helpers are convenience wrappers
110  that fix \p from to their named unit and \p to to
111  \ref length_unit_base. The \p d parameter is forwarded and
112  defaults to 1. Use the full \ref length function when a
113  non-default target unit is required.
114 
115  These functions allow for lengths to be specified with units. Lengths
116  specified with units are independent of (\ref length_unit_base).
117  There are also unit conversion functions for converting from one unit
118  to another.
119 
120  The table below enumerates the supported units.
121 
122  units id | description
123  :---------:|:----------------------:
124  pm | picometer
125  nm | nanometer
126  um | micrometer
127  mm | millimeter
128  cm | centimeter
129  dm | decimeter
130  m | meter
131  km | kilometer
132  thou, mil | thousandth of an inch
133  in | inch
134  ft | feet
135  yd | yard
136  mi | mile
137 
138  \amu_define title (Length base units example)
139  \amu_define scope_id (example)
140  \amu_define output_scad (true)
141  \amu_define output_console (false)
142  \amu_include (include/amu/scope.amu)
143 
144  \amu_define output_scad (false)
145  \amu_define output_console (true)
146 
147  \amu_define title (length_unit_base=mm)
148  \amu_define scope_id (example_mm)
149  \amu_include (include/amu/scope.amu)
150 
151  \amu_define title (length_unit_base=cm)
152  \amu_define scope_id (example_cm)
153  \amu_include (include/amu/scope.amu)
154 
155  \amu_define title (length_unit_base=mil)
156  \amu_define scope_id (example_mil)
157  \amu_include (include/amu/scope.amu)
158 
159  \amu_define title (length_unit_base=in)
160  \amu_define scope_id (example_in)
161  \amu_include (include/amu/scope.amu)
162 
163  /+
164  include diagram
165  +/
166  \amu_define title (Equivalent lengths)
167  \amu_define image_views (top)
168  \amu_define image_size (qvga)
169  \amu_define scope_id (equivalents)
170  \amu_include (include/amu/scope_diagrams_3d.amu)
171 *******************************************************************************/
172 
173 //----------------------------------------------------------------------------//
174 // members
175 //----------------------------------------------------------------------------//
176 
177 //! <string> The base units for value storage.
178 //! \note This variable is intended to be overridden at the top of a
179 //! design file or in a child scope. All length functions that omit
180 //! the \p to parameter will convert to this unit.
181 length_unit_base = "mm";
182 
183 //! <string> The default units when unspecified.
184 //! \note This variable is intended to be overridden at the top of a
185 //! design file or in a child scope. All length functions that omit
186 //! the \p from or \p u parameter will assume this unit.
187 length_unit_default = "mm";
188 
189 //! Return the long name for a length unit identifier.
190 /***************************************************************************//**
191  \param u <string> A length unit identifier.
192 
193  \returns <string> The long name for a length unit identifier.
194  Returns \b undef for identifiers that are not defined.
195 
196  \private
197 *******************************************************************************/
198 function _length_unit_name_1d
199 (
201 ) = u == "pm" ? "picometer"
202  : u == "nm" ? "nanometer"
203  : u == "um" ? "micrometer"
204  : u == "mm" ? "millimeter"
205  : u == "cm" ? "centimeter"
206  : u == "dm" ? "decimeter"
207  : u == "m" ? "meter"
208  : u == "km" ? "kilometer"
209  : u == "thou" ? "thou (thousandth)"
210  : u == "mil" ? "mil (thousandth)"
211  : u == "in" ? "inch"
212  : u == "ft" ? "feet"
213  : u == "yd" ? "yard"
214  : u == "mi" ? "mile"
215  : undef;
216 
217 //! Return the name for a length unit identifier with dimension.
218 /***************************************************************************//**
219  \param u <string> A length unit identifier.
220  \param d <integer> The unit dimension. One of [1|2|3].
221  \param w <boolean> \b true for word and \b false for symbol format.
222 
223  \returns <string> The long name for a length unit identifier with
224  dimension. Returns \b undef for identifiers or dimensions
225  that are not defined.
226 *******************************************************************************/
227 function length_unit_name
228 (
230  d = 1,
231  w = false
232 ) = (w == false) ?
233  (
234  d == 1 ? _length_unit_name_1d( u )
235  : d == 2 ? str( _length_unit_name_1d( u ), "^2" )
236  : d == 3 ? str( _length_unit_name_1d( u ), "^3" )
237  : undef
238  )
239  :
240  (
241  d == 1 ? _length_unit_name_1d( u )
242  : d == 2 ? str( "square ", _length_unit_name_1d( u ) )
243  : d == 3 ? str( "cubic ", _length_unit_name_1d( u ) )
244  : undef
245  );
246 
247 //! Convert a value from millimeters to other units.
248 /***************************************************************************//**
249  \param v <decimal-list | decimal> The value to convert.
250  \param to <string> The units to which the value should be converted.
251  Defaults to \ref length_unit_base.
252 
253  \returns <decimal-list | decimal> The conversion result.
254  Returns \b undef for identifiers that are not defined.
255 
256  \private
257 *******************************************************************************/
258 function _length_unit_mm2
259 (
260  v,
261  to = length_unit_base
262 ) = to == "pm" ? ( v * 1000000000.0 )
263  : to == "nm" ? ( v * 1000000.0 )
264  : to == "um" ? ( v * 1000.0 )
265  : to == "mm" ? ( v )
266  : to == "cm" ? ( v / 10.0 )
267  : to == "dm" ? ( v / 100.0 )
268  : to == "m" ? ( v / 1000.0 )
269  : to == "km" ? ( v / 1000000.0 )
270  : to == "thou" ? ( v / 0.0254 )
271  : to == "mil" ? ( v / 0.0254 )
272  : to == "in" ? ( v / 25.4 )
273  : to == "ft" ? ( v / 304.8 )
274  : to == "yd" ? ( v / 914.4 )
275  : to == "mi" ? ( v / 1609344.0 )
276  : undef;
277 
278 //! Convert a value from some units to millimeters.
279 /***************************************************************************//**
280  \param v <decimal-list | decimal> The value to convert.
281  \param from <string> The units of the value to be converted.
282  Defaults to \ref length_unit_default.
283 
284  \returns <decimal-list | decimal> The conversion result.
285  Returns \b undef for identifiers that are not defined.
286 
287  \private
288 *******************************************************************************/
289 function _length_unit_2mm
290 (
291  v,
292  from = length_unit_default
293 ) = let( factor = _length_unit_mm2( 1, from ) )
294  (factor == undef) ? undef : v / factor;
295 
296 //! Convert a value from one unit to another (1-dimensional).
297 /***************************************************************************//**
298  \param v <decimal-list | decimal> The value to convert.
299  \param from <string> The units of the value to be converted.
300  Defaults to \ref length_unit_default.
301  \param to <string> The units to which the value should be
302  converted. Defaults to \ref length_unit_base.
303  Conversion to \c "mm" is short-circuited: the
304  intermediate mm to step is skipped.
305 
306  \returns <decimal-list | decimal> The conversion result.
307  Returns \b undef for identifiers that are not defined.
308 
309  \private
310 *******************************************************************************/
311 function _length_1d
312 (
313  v,
314  from = length_unit_default,
315  to = length_unit_base
316 ) = (from == to) ? v
317  // short-circuit: 2mm is the final result when target is millimeters
318  : (to == "mm") ? _length_unit_2mm( v, from )
319  : _length_unit_mm2( _length_unit_2mm( v, from ), to );
320 
321 //! Convert a length value from one unit to another, with optional dimensional scaling.
322 /***************************************************************************//**
323  \param v <decimal-list | decimal> The value to convert. May be
324  a list when \p d = 1; must be a scalar when \p d = 2
325  or 3.
326  \param from <string> The units of the value to be converted.
327  Defaults to \ref length_unit_default.
328  \param to <string> The units to which the value should be
329  converted. Defaults to \ref length_unit_base.
330  \param d <integer> The unit dimension. One of [1|2|3].
331 
332  \returns <decimal> The conversion result. For \p d = 1 returns the
333  converted value; for \p d = 2 or 3 returns the converted
334  linear value raised to the corresponding power. Returns \b
335  undef for identifiers, dimensions, or input types that are
336  not defined.
337 *******************************************************************************/
338 function length
339 (
340  v,
341  from = length_unit_default,
342  to = length_unit_base,
343  d = 1
344 ) =
345  // for d>1, 'v' must be a scalar; lists are only valid for d=1
346  (d != 1 && is_list(v)) ? undef
347  // d=1: scalar or list, element-wise conversion via _length_1d
348  : d == 1 ? _length_1d(v, from, to)
349  // d=2,3: convert the linear value once, then raise to the power
350  : let( c = _length_1d(v, from, to) )
351  d == 2 ? pow( c, 2 )
352  : d == 3 ? pow( c, 3 )
353  // undefined for other dimensions
354  : undef;
355 
356 //! Recover a linear length from an area or volume value, then convert units.
357 /***************************************************************************//**
358  \param v <decimal> The area or volume value to invert. Must be
359  a scalar for \p d = 2 or 3; may be a list for \p d = 1.
360  \param from <string> The units of the input value \p v.
361  Defaults to \ref length_unit_base.
362  \param to <string> The units to which the recovered linear value
363  should be converted. Defaults to \ref length_unit_default.
364  \param d <integer> The unit dimension. One of [1|2|3].
365 
366  \returns <decimal> The linear conversion result. For \p d = 1 converts
367  \p v directly; for \p d = 2 takes the square root of \p v
368  before converting; for \p d = 3 takes the cube root.
369  Returns \b undef for identifiers, dimensions, or input
370  types that are not defined.
371 *******************************************************************************/
372 function length_inv
373 (
374  v,
375  from = length_unit_base,
376  to = length_unit_default,
377  d = 1
378 ) =
379  // for d>1, 'v' must be a scalar; lists are only valid for d=1
380  (d != 1 && is_list(v)) ? undef
381  // d=1: scalar or list, element-wise conversion via _length_1d
382  : d == 1 ? _length_1d(v, from, to)
383  : d == 2 ? _length_1d(sqrt(v), from, to)
384  : d == 3 ? _length_1d(pow(v, 1/3.0), from, to)
385  // undefined for other dimensions
386  : undef;
387 
388 //----------------------------------------------------------------------------//
389 // shorthand conversions
390 //----------------------------------------------------------------------------//
391 
392 //! \name Shorts
393 //! @{
394 //!
395 //! Convenience wrappers around \ref length that fix the \p from unit
396 //! to a named unit and the \p to unit to \ref length_unit_base.
397 //! The \p d parameter is forwarded (default 1). Use \ref length
398 //! directly when a non-default target unit or list input with d > 1
399 //! is required.
400 
401 //! Shorthand length conversion for millimeters.
402 /***************************************************************************//**
403  \param v <decimal> The value to convert.
404  \param d <integer> The unit dimension. One of [1|2|3].
405 
406  \returns <decimal> The conversion result.
407 *******************************************************************************/
408 function l_mm(v, d=1) = length(v=v, from="mm", d=d);
409 
410 //! Shorthand length conversion for inches.
411 /***************************************************************************//**
412  \param v <decimal> The value to convert.
413  \param d <integer> The unit dimension. One of [1|2|3].
414 
415  \returns <decimal> The conversion result.
416 *******************************************************************************/
417 function l_in(v, d=1) = length(v=v, from="in", d=d);
418 
419 //! @}
420 
421 //! @}
422 //! @}
423 
424 //----------------------------------------------------------------------------//
425 // openscad-amu auxiliary scripts
426 //----------------------------------------------------------------------------//
427 
428 /*
429 BEGIN_SCOPE validate;
430  BEGIN_OPENSCAD;
431  include <omdl-base.scad>;
432  include <common/validation.scad>;
433 
434  echo( str("openscad version ", version()) );
435  for (i=[1:3]) echo( "not tested:" );
436 
437  // end_include
438  END_OPENSCAD;
439 
440  BEGIN_MFSCRIPT;
441  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
442  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
443  END_MFSCRIPT;
444 END_SCOPE;
445 */
446 
447 /*
448 BEGIN_SCOPE example;
449  BEGIN_OPENSCAD;
450  include <omdl-base.scad>;
451 
452  length_unit_base = "mm";
453  length_unit_default = "in";
454 
455  // get unit names
456  bu = length_unit_name(length_unit_base);
457  du = length_unit_name();
458 
459  // absolute length measurements in base unit.
460  c1 = length(1/8);
461  c2 = length(3.175, "mm");
462  c3 = length(25, "mil");
463  c4 = length(1, "ft", d=3);
464 
465  // convert between units.
466  c5 = length(10, from="mil", to="in");
467  c6 = length(10, from="ft", to="mm");
468 
469  // end_include
470 
471  echo( bu=bu );
472  echo( du=du );
473  echo( );
474  echo( c1=c1 );
475  echo( c2=c2 );
476  echo( c3=c3 );
477  echo( c4=c4 );
478  echo( c5=c5 );
479  echo( c6=c6 );
480  END_OPENSCAD;
481 
482  BEGIN_MFSCRIPT;
483  include --path "${INCLUDE_PATH}" {var_init,var_gen_term}.mfs;
484 
485  defines name "units" define "length_unit_base" strings "mm cm mil in";
486  variables add_opts_combine "units";
487 
488  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
489  END_MFSCRIPT;
490 END_SCOPE;
491 */
492 
493 /*
494 BEGIN_SCOPE equivalents;
495  BEGIN_OPENSCAD;
496  include <omdl-base.scad>;
497 
498  module dim( uv=1, un="cm" ) {
499  mx = 200.0;
500  my = 1;
501  tx = my;
502  ty = mx / 10;
503  ts = mx / 25;
504 
505  color( "black" )
506  union() {
507  square( [mx, my], true );
508  translate([-mx/2,0,0]) square( [tx, ty], true );
509  translate([+mx/2,0,0]) square( [tx, ty], true );
510  }
511 
512  l1l = length( uv, "in", un );
513  l1u = length_unit_name( un );
514  l1s = str( l1l, " ", l1u );
515 
516  translate( [0, ts, 0] )
517  text( text=l1s, size=ts, font="Courier:style=bold italic", halign="center", valign="center" );
518  }
519 
520  unv = ["um", "mm", "cm", "mil", "in"];
521  for( un = unv )
522  translate( [0, 30 * search([un], unv)[0], 0] ) dim( 1, un );
523  END_OPENSCAD;
524 
525  BEGIN_MFSCRIPT;
526  include --path "${INCLUDE_PATH}" {var_init,var_gen_png2eps}.mfs;
527 
528  views name "views" translate "0,60,0" distance "400" views "top";
529  variables add_opts_combine "views";
530 
531  include --path "${INCLUDE_PATH}" scr_make_mf.mfs;
532  END_MFSCRIPT;
533 END_SCOPE;
534 */
535 
536 //----------------------------------------------------------------------------//
537 // end of file
538 //----------------------------------------------------------------------------//
function length(v, from=length_unit_default, to=length_unit_base, d=1)
Convert a length value from one unit to another, with optional dimensional scaling.
function length_unit_name(u=length_unit_default, d=1, w=false)
Return the name for a length unit identifier with dimension.
length_unit_default
Definition: length.scad:988
function length_inv(v, from=length_unit_base, to=length_unit_default, d=1)
Recover a linear length from an area or volume value, then convert units.
function l_mm(v, d=1)
Shorthand length conversion for millimeters.
function l_in(v, d=1)
Shorthand length conversion for inches.
length_unit_base
Definition: length.scad:982