BRL-CAD
units.c
Go to the documentation of this file.
1 /* U N I T S . C
2  * BRL-CAD
3  *
4  * Copyright (c) 1990-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 
21 #include "common.h"
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <float.h>
28 
29 #include "bu/log.h"
30 #include "bu/malloc.h"
31 #include "bu/str.h"
32 #include "bu/units.h"
33 #include "bu/vls.h"
34 
35 
36 /* done specifically to avoid a libbn dependency */
37 #define NEAR_ZERO(val, epsilon) (((val) > -epsilon) && ((val) < epsilon))
38 #define ZERO(val) NEAR_ZERO((val), SMALL_FASTF)
39 
40 
41 struct cvt_tab {
42  double val;
43  char name[32];
44 };
45 
46 struct conv_table {
47  struct cvt_tab *cvttab;
48 };
49 
50 static struct cvt_tab bu_units_length_tab[] = {
51  {0.0, "none"},
52  {1.0e-21, "ym"},
53  {1.0e-21, "yoctometer"},
54  {1.0e-18, "zm"},
55  {1.0e-18, "zeptometer"},
56  {1.0e-15, "am"},
57  {1.0e-15, "attometer"},
58  {1.0e-12, "fm"},
59  {1.0e-12, "femtometer"},
60  {1.0e-9, "pm"},
61  {1.0e-9, "picometer"},
62  {1.0e-7, "angstrom"},
63  {1.0e-7, "decinanometer"},
64  {1.0e-6, "nm"},
65  {1.0e-6, "nanometer"},
66  {1.0e-3, "um"},
67  {1.0e-3, "micrometer"},
68  {1.0e-3, "micron"},
69  {1.0, "mm"},
70  {1.0, "millimeter"},
71  {10.0, "cm"},
72  {10.0, "centimeter"},
73  {100.0, "dm"},
74  {100.0, "decimeter"},
75  {1000.0, "m"},
76  {1000.0, "meter"},
77  {10000.0, "Dm"},
78  {10000.0, "decameter"},
79  {100000.0, "hm"},
80  {100000.0, "hectometer"},
81  {1000000.0, "km"},
82  {1000000.0, "kilometer"},
83  {1.0e+9, "Mm"},
84  {1.0e+9, "megameter"},
85  {1.0e+12, "Gm"},
86  {1.0e+12, "gigameter"},
87  {1.0e+15, "Tm"},
88  {1.0e+15, "terameter"},
89  {1.0e+18, "Pm"},
90  {1.0e+18, "petameter"},
91  {1.0e+21, "Em"},
92  {1.0e+21, "exameter"},
93  {1.0e+24, "Zm"},
94  {1.0e+24, "zettameter"},
95  {1.0e+27, "Ym"},
96  {1.0e+27, "yottameter"},
97  {25.4, "in"},
98  {25.4, "inch"},
99  {25.4, "inches"}, /* plural */
100  {101.6, "hand"},
101  {304.8, "ft"},
102  {304.8, "foot"},
103  {304.8, "feet"}, /* plural */
104  {456.2, "cubit"},
105  {914.4, "yd"},
106  {914.4, "yard"},
107  {5029.2, "rd"},
108  {5029.2, "rod"},
109  {20116.8, "chain"},
110  {201168.0, "furlong"},
111  {1609344.0, "mi"},
112  {1609344.0, "mile"},
113  {1852000.0, "nmile"},
114  {1852000.0, "nautical mile"},
115  {5556000.0, "league"},
116  {2.99792458e+11, "light second"},
117  {1.79875475e+13, "light minute"},
118  {1.495979e+14, "AU"},
119  {1.495979e+14, "astronomical unit"},
120  {1.07925285e+15, "light hour"},
121  {2.59020684e+16, "light day"},
122  {9.4605284+18, "light year"},
123  {3.08568025e+19, "pc"},
124  {3.08568025e+19, "parsec"},
125  {0.0, ""} /* LAST ENTRY */
126 };
127 #define BU_UNITS_TABLE_SIZE (sizeof(bu_units_length_tab) / sizeof(struct cvt_tab) - 1)
128 
129 static struct cvt_tab bu_units_volume_tab[] = {
130  {0.0, "none"},
131  {1.0, "mm^3"}, /* default */
132  {1.0, "cu mm"},
133  {1.0e3, "cm^3"},
134  {1.0e3, "cu cm"},
135  {1.0e3, "cc"},
136  {1.0e6, "l"},
137  {1.0e6, "liter"},
138  {1.0e6, "litre"},
139  {1.0e9, "m^3"},
140  {1.0e9, "cu m"},
141  {16387.064, "in^3"},
142  {16387.064, "cu in"},
143  {28316846.592, "ft^3"},
144  {28316846.592, "cu ft"},
145  {764554857.984, "yds^3"},
146  {764554857.984, "yards^3"},
147  {764554857.984, "cu yards"},
148  {0.0, ""} /* LAST ENTRY */
149 };
150 
151 static struct cvt_tab bu_units_mass_tab[] = {
152  {0.0, "none"},
153  {1.0, "grams"}, /* default */
154  {1.0, "g"},
155  {1.0e3, "kilogram"},
156  {1.0e3, "kg"},
157  {0.0648, "gr"},
158  {0.0648, "grain"},
159  {453.6, "lb"},
160  {28.35, "oz"},
161  {28.35, "ounce"},
162  {0.0, ""} /* LAST ENTRY */
163 };
164 
165 static const struct conv_table unit_lists[4] = {
166  {bu_units_length_tab}, {bu_units_volume_tab}, {bu_units_mass_tab}, {NULL}
167 };
168 
169 
170 /**
171  * compares an input units string to a reference units name and
172  * returns truthfully if they match. the comparison ignores any
173  * embedded whitespace and is case insensitive.
174  */
175 static int
176 units_name_matches(const char *input, const char *name)
177 {
178  const char *cp;
179  int match;
180  struct bu_vls normalized_input = BU_VLS_INIT_ZERO;
181  struct bu_vls normalized_name = BU_VLS_INIT_ZERO;
182 
183  /* convert NULL */
184  if (!input)
185  input = "";
186  if (!name)
187  name = "";
188 
189  /* skip spaces */
190  while (isspace((unsigned char)*input))
191  input++;
192  while (isspace((unsigned char)*name))
193  name++;
194 
195  /* quick exit */
196  if (tolower((unsigned char)input[0]) != tolower((unsigned char)name[0]))
197  return 0;
198 
199  cp = input;
200  /* skip spaces, convert to lowercase */
201  while (*cp != '\0') {
202  if (!isspace((unsigned char)*cp))
203  bu_vls_putc(&normalized_input, tolower((unsigned char)*cp));
204  cp++;
205  }
206 
207  cp = name;
208  /* skip spaces, convert to lowercase */
209  while (*cp != '\0') {
210  if (!isspace((unsigned char)*cp))
211  bu_vls_putc(&normalized_name, tolower((unsigned char)*cp));
212  cp++;
213  }
214 
215  /* trim any trailing 's' for plurality */
216  if (bu_vls_addr(&normalized_input)[bu_vls_strlen(&normalized_input)-1] == 's') {
217  bu_vls_trunc(&normalized_input, -1);
218  }
219  if (bu_vls_addr(&normalized_name)[bu_vls_strlen(&normalized_name)-1] == 's') {
220  bu_vls_trunc(&normalized_name, -1);
221  }
222 
223  /* compare */
224  match = BU_STR_EQUAL(bu_vls_addr(&normalized_input), bu_vls_addr(&normalized_name));
225 
226  bu_vls_free(&normalized_input);
227  bu_vls_free(&normalized_name);
228 
229  return match;
230 }
231 
232 
233 double
234 bu_units_conversion(const char *str)
235 {
236  register const struct cvt_tab *tp;
237  register const struct conv_table *cvtab;
238  char ubuf[256];
239 
240  /* Copy the given string */
241  bu_strlcpy(ubuf, str, sizeof(ubuf));
242 
243  /* Search for this string in the table */
244  for (cvtab=unit_lists; cvtab->cvttab; cvtab++) {
245  for (tp=cvtab->cvttab; tp->name[0]; tp++) {
246  if (!units_name_matches(ubuf, tp->name))
247  continue;
248  return tp->val;
249  }
250  }
251  return 0.0; /* Unable to find it */
252 }
253 
254 
255 const char *
256 bu_units_string(register const double mm)
257 {
258  register const struct cvt_tab *tp;
259 
260  if (UNLIKELY(mm <= 0))
261  return (const char *)NULL;
262 
263  /* Search for this string in the table */
264  for (tp=bu_units_length_tab; tp->name[0]; tp++) {
265  fastf_t diff, bigger;
266 
267  if (ZERO(mm - tp->val))
268  return tp->name;
269 
270  /* Check for near-miss */
271  if (mm > tp->val) {
272  bigger = mm;
273  diff = mm - tp->val;
274  } else {
275  bigger = tp->val;
276  diff = tp->val - mm;
277  }
278 
279  /* Absolute difference less than 0.1 angstrom */
280  if (diff < 1.0e-8)
281  return tp->name;
282 
283  /* Relative difference less than 1 part per billion */
284  if (diff < 0.000000001 * bigger)
285  return tp->name;
286  }
287  return (const char *)NULL;
288 }
289 
290 struct bu_vls *
292 {
293  register const struct cvt_tab *tp;
294  struct bu_vls *vlsp;
295  double prev_val = 0.0;
296 
297  BU_ALLOC(vlsp, struct bu_vls);
298  bu_vls_init(vlsp);
299  for (tp=bu_units_length_tab; tp->name[0]; tp++) {
300  if (ZERO(prev_val - tp->val))
301  continue;
302 
303  bu_vls_printf(vlsp, "%s, ", tp->name);
304  prev_val = tp->val;
305  }
306 
307  /* Remove the last ", " */
308  bu_vls_trunc(vlsp, -2);
309 
310  return vlsp;
311 }
312 
313 
314 const char *
315 bu_nearest_units_string(register const double mm)
316 {
317  register const struct cvt_tab *tp;
318 
319  const char *nearest = NULL;
320  double nearer = DBL_MAX;
321 
322  if (UNLIKELY(mm <= 0))
323  return (const char *)NULL;
324 
325  /* Search for this unit in the table */
326  for (tp=bu_units_length_tab; tp->name[0]; tp++) {
327  double nearness;
328 
329  /* skip zero so we don't return 'none' */
330  if (ZERO(tp->val))
331  continue;
332 
333  /* break early on perfect match */
334  if (ZERO(mm - tp->val))
335  return tp->name;
336 
337  /* Check for nearness */
338  if (mm > tp->val) {
339  nearness = mm - tp->val;
340  } else {
341  nearness = tp->val - mm;
342  }
343 
344  /* :-) */
345  if (nearness < nearer) {
346  nearer = nearness;
347  nearest = tp->name;
348  }
349  }
350  return nearest;
351 }
352 
353 
354 double
355 bu_mm_value(const char *s)
356 {
357  double v;
358  char *ptr;
359  register const struct cvt_tab *tp;
360 
361  v = strtod(s, &ptr);
362 
363  if (ptr == s) {
364  /* No number could be found, unity is implied */
365  /* e.g. interpret "ft" as "1ft" */
366  v = 1.0;
367  }
368  if (! *ptr) {
369  /* There are no characters following the scanned number */
370  return v;
371  }
372 
373  for (tp=bu_units_length_tab; tp->name[0]; tp++) {
374  if (units_name_matches(ptr, tp->name)) {
375  v *= tp->val;
376  return v;
377  }
378  }
379 
380  /* A string was seen, but not found in the table. Signal error */
381  return -1.0;
382 }
383 
384 
385 void
386 bu_mm_cvt(const struct bu_structparse *sdp,
387  const char *name,
388  void *base,
389  const char *value,
390  void *UNUSED(data))
391 /* structure description */
392 /* struct member name */
393 /* beginning of structure */
394 /* string containing value */
395 {
396  register double *p = (double *)((char *)base + sdp->sp_offset);
397 
398  if (UNLIKELY(!name)) {
399  bu_log("bu_mm_cvt: NULL name encountered\n");
400  }
401 
402  /* reconvert with optional units */
403  *p = bu_mm_value(value);
404 }
405 
406 /*
407  * Local Variables:
408  * mode: C
409  * tab-width: 8
410  * indent-tabs-mode: t
411  * c-file-style: "stroustrup"
412  * End:
413  * ex: shiftwidth=4 tabstop=8
414  */
void bu_vls_init(struct bu_vls *vp)
Definition: vls.c:56
void bu_log(const char *,...) _BU_ATTR_PRINTF12
Definition: log.c:176
if lu s
Definition: nmg_mod.c:3860
Definition: clone.c:90
const char * bu_units_string(register const double mm)
Definition: units.c:256
void bu_vls_trunc(struct bu_vls *vp, int len)
Definition: vls.c:198
Header file for the BRL-CAD common definitions.
void bu_mm_cvt(const struct bu_structparse *sdp, const char *name, void *base, const char *value, void *data)
Definition: units.c:386
double val
Definition: units.c:42
double bu_mm_value(const char *s)
Definition: units.c:355
void bu_vls_free(struct bu_vls *vp)
Definition: vls.c:248
COMPLEX data[64]
Definition: fftest.c:34
char name[32]
Definition: units.c:43
double bu_units_conversion(const char *str)
Definition: units.c:234
struct cvt_tab * cvttab
Definition: units.c:47
#define BU_ALLOC(_ptr, _type)
Definition: malloc.h:223
#define bu_strlcpy(dst, src, size)
Definition: str.h:60
struct bu_vls * bu_units_strings_vls()
Definition: units.c:291
size_t bu_vls_strlen(const struct bu_vls *vp)
Definition: vls.c:189
Definition: units.c:41
#define UNUSED(parameter)
Definition: common.h:239
size_t sp_offset
Definition: parse.h:141
char * bu_vls_addr(const struct bu_vls *vp)
Definition: vls.c:111
#define ZERO(val)
Definition: units.c:38
void bu_vls_printf(struct bu_vls *vls, const char *fmt,...) _BU_ATTR_PRINTF23
Definition: vls.c:694
#define BU_VLS_INIT_ZERO
Definition: vls.h:84
Definition: vls.h:56
double fastf_t
Definition: defines.h:300
void bu_vls_putc(struct bu_vls *vp, int c)
Definition: vls.c:666
const char * bu_nearest_units_string(register const double mm)
Definition: units.c:315
#define UNLIKELY(expression)
Definition: common.h:282
#define BU_STR_EQUAL(s1, s2)
Definition: str.h:126