svn merge ^/trunk/blender -r48749:48754
[blender.git] / source / blender / blenkernel / intern / unit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/blenkernel/intern/unit.c
24  *  \ingroup bke
25  */
26
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <string.h>
31 #include <assert.h>
32 #include "BKE_unit.h"
33
34 #include "BLI_math.h"
35 #include "BLI_string.h"
36 #include "BLI_string_utf8.h"
37
38 #ifdef WIN32
39 #  include "BLI_winstuff.h"
40 #endif
41
42 #define TEMP_STR_SIZE 256
43
44 #define SEP_CHR         '#'
45 #define SEP_STR         "#"
46
47 #define EPS 0.00001
48
49 #define UN_SC_KM        1000.0f
50 #define UN_SC_HM        100.0f
51 #define UN_SC_DAM       10.0f
52 #define UN_SC_M         1.0f
53 #define UN_SC_DM        0.1f
54 #define UN_SC_CM        0.01f
55 #define UN_SC_MM        0.001f
56 #define UN_SC_UM        0.000001f
57
58 #define UN_SC_MI        1609.344f
59 #define UN_SC_FUR       201.168f
60 #define UN_SC_CH        20.1168f
61 #define UN_SC_YD        0.9144f
62 #define UN_SC_FT        0.3048f
63 #define UN_SC_IN        0.0254f
64 #define UN_SC_MIL       0.0000254f
65
66 #define UN_SC_MTON      1000.0f /* metric ton */
67 #define UN_SC_QL        100.0f
68 #define UN_SC_KG        1.0f
69 #define UN_SC_HG        0.1f
70 #define UN_SC_DAG       0.01f
71 #define UN_SC_G         0.001f
72
73 #define UN_SC_ITON      907.18474f /* imperial ton */
74 #define UN_SC_CWT       45.359237f
75 #define UN_SC_ST        6.35029318f
76 #define UN_SC_LB        0.45359237f
77 #define UN_SC_OZ        0.028349523125f
78
79 /* define a single unit */
80 typedef struct bUnitDef {
81         const char *name;
82         const char *name_plural; /* abused a bit for the display name */
83         const char *name_short; /* this is used for display*/
84         const char *name_alt; /* keyboard-friendly ASCII-only version of name_short, can be NULL */
85         /* if name_short has non-ASCII chars, name_alt should be present */
86
87         const char *name_display; /* can be NULL */
88
89         double scalar;
90         double bias; /* not used yet, needed for converting temperature */
91         int flag;
92 } bUnitDef;
93
94 #define B_UNIT_DEF_NONE 0
95 #define B_UNIT_DEF_SUPPRESS 1 /* Use for units that are not used enough to be translated into for common use */
96
97 /* define a single unit */
98 typedef struct bUnitCollection {
99         struct bUnitDef *units;
100         int base_unit; /* basic unit index (when user doesn't specify unit explicitly) */
101         int flag; /* options for this system */
102         int length; /* to quickly find the last item */
103 } bUnitCollection;
104
105 /* Dummy */
106 static struct bUnitDef buDummyDef[] = { {"", NULL, "", NULL, NULL, 1.0, 0.0}, {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}};
107 static struct bUnitCollection buDummyCollecton = {buDummyDef, 0, 0, sizeof(buDummyDef)};
108
109 /* Lengths */
110 static struct bUnitDef buMetricLenDef[] = {
111         {"kilometer", "kilometers",     "km",  NULL, "Kilometers", UN_SC_KM, 0.0,     B_UNIT_DEF_NONE},
112         {"hectometer", "hectometers",   "hm",  NULL, "100 Meters", UN_SC_HM, 0.0,     B_UNIT_DEF_SUPPRESS},
113         {"dekameter", "dekameters",     "dam", NULL, "10 Meters",  UN_SC_DAM, 0.0,    B_UNIT_DEF_SUPPRESS},
114         {"meter", "meters",             "m",   NULL, "Meters",     UN_SC_M, 0.0,      B_UNIT_DEF_NONE},     /* base unit */
115         {"decimeter", "decimeters",     "dm",  NULL, "10 Centimeters", UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
116         {"centimeter", "centimeters",   "cm",  NULL, "Centimeters", UN_SC_CM, 0.0,    B_UNIT_DEF_NONE},
117         {"millimeter", "millimeters",   "mm",  NULL, "Millimeters", UN_SC_MM, 0.0,    B_UNIT_DEF_NONE},
118         {"micrometer", "micrometers",   "µm",  "um", "Micrometers", UN_SC_UM, 0.0,    B_UNIT_DEF_NONE},     // micron too?
119
120         /* These get displayed because of float precision problems in the transform header,
121          * could work around, but for now probably people wont use these */
122 #if 0
123         {"nanometer", "Nanometers",     "nm", NULL, 0.000000001, 0.0,   B_UNIT_DEF_NONE},
124         {"picometer", "Picometers",     "pm", NULL, 0.000000000001, 0.0, B_UNIT_DEF_NONE},
125 #endif
126         {NULL, NULL, NULL,      NULL, NULL, 0.0, 0.0}
127 };
128 static struct bUnitCollection buMetricLenCollecton = {buMetricLenDef, 3, 0, sizeof(buMetricLenDef) / sizeof(bUnitDef)};
129
130 static struct bUnitDef buImperialLenDef[] = {
131         {"mile", "miles",       "mi", "m", "Miles",      UN_SC_MI, 0.0,  B_UNIT_DEF_NONE},
132         {"furlong", "furlongs", "fur", NULL, "Furlongs", UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
133         {"chain", "chains",     "ch", NULL, "Chains",    UN_SC_CH, 0.0,  B_UNIT_DEF_SUPPRESS},
134         {"yard", "yards",       "yd", NULL, "Yards",     UN_SC_YD, 0.0,  B_UNIT_DEF_SUPPRESS},
135         {"foot", "feet",        "'", "ft", "Feet",       UN_SC_FT, 0.0,  B_UNIT_DEF_NONE}, /* base unit */
136         {"inch", "inches",      "\"", "in", "Inches",    UN_SC_IN, 0.0,  B_UNIT_DEF_NONE},
137         {"thou", "thou",        "thou", "mil", "Thou",   UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* plural for thou has no 's' */
138         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
139 };
140 static struct bUnitCollection buImperialLenCollecton = {buImperialLenDef, 4, 0, sizeof(buImperialLenDef) / sizeof(bUnitDef)};
141
142 /* Areas */
143 static struct bUnitDef buMetricAreaDef[] = {
144         {"square kilometer",  "square kilometers",  "km²", "km2",   "Square Kilometers", UN_SC_KM * UN_SC_KM, 0.0,    B_UNIT_DEF_NONE},
145         {"square hectometer", "square hectometers", "hm²", "hm2",   "Square Hectometers", UN_SC_HM * UN_SC_HM, 0.0,   B_UNIT_DEF_NONE},   /* hectare */
146         {"square dekameter",  "square dekameters",  "dam²", "dam2",  "Square Dekameters", UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},  /* are */
147         {"square meter",      "square meters",      "m²",  "m2",    "Square Meters", UN_SC_M * UN_SC_M, 0.0,          B_UNIT_DEF_NONE},   /* base unit */
148         {"square decimeter",  "square decimetees",  "dm²", "dm2",   "Square Decimeters", UN_SC_DM * UN_SC_DM, 0.0,    B_UNIT_DEF_SUPPRESS},
149         {"square centimeter", "square centimeters", "cm²", "cm2",   "Square Centimeters", UN_SC_CM * UN_SC_CM, 0.0,   B_UNIT_DEF_NONE},
150         {"square millimeter", "square millimeters", "mm²", "mm2",   "Square Millimeters", UN_SC_MM * UN_SC_MM, 0.0,   B_UNIT_DEF_NONE},
151         {"square micrometer", "square micrometers", "µm²", "um2",   "Square Micrometers", UN_SC_UM * UN_SC_UM, 0.0,   B_UNIT_DEF_NONE},
152         {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
153 };
154 static struct bUnitCollection buMetricAreaCollecton = {buMetricAreaDef, 3, 0, sizeof(buMetricAreaDef) / sizeof(bUnitDef)};
155
156 static struct bUnitDef buImperialAreaDef[] = {
157         {"square mile", "square miles",       "sq mi", "sq m", "Square Miles", UN_SC_MI * UN_SC_MI, 0.0,      B_UNIT_DEF_NONE},
158         {"square furlong", "square furlongs", "sq fur", NULL,  "Square Furlongs", UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
159         {"square chain", "square chains",     "sq ch",  NULL,  "Square Chains", UN_SC_CH * UN_SC_CH, 0.0,     B_UNIT_DEF_SUPPRESS},
160         {"square yard", "square yards",       "sq yd",  NULL,  "Square Yards", UN_SC_YD * UN_SC_YD, 0.0,      B_UNIT_DEF_NONE},
161         {"square foot", "square feet",        "sq ft",  NULL,  "Square Feet", UN_SC_FT * UN_SC_FT, 0.0,       B_UNIT_DEF_NONE}, /* base unit */
162         {"square inch", "square inches",      "sq in",  NULL,  "Square Inches", UN_SC_IN * UN_SC_IN, 0.0,     B_UNIT_DEF_NONE},
163         {"square thou", "square thous",       "sq mil", NULL,  "Square Thous", UN_SC_MIL * UN_SC_MIL, 0.0,    B_UNIT_DEF_NONE},
164         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
165 };
166 static struct bUnitCollection buImperialAreaCollecton = {buImperialAreaDef, 4, 0, sizeof(buImperialAreaDef) / sizeof(bUnitDef)};
167
168 /* Volumes */
169 static struct bUnitDef buMetricVolDef[] = {
170         {"cubic kilometer",  "cubic kilometers",  "km³",  "km3",  "Cubic Kilometers", UN_SC_KM * UN_SC_KM * UN_SC_KM, 0.0,    B_UNIT_DEF_NONE},
171         {"cubic hectometer", "cubic hectometers", "hm³",  "hm3",  "Cubic Hectometers", UN_SC_HM * UN_SC_HM * UN_SC_HM, 0.0,   B_UNIT_DEF_NONE},
172         {"cubic dekameter",  "cubic dekameters",  "dam³", "dam3", "Cubic Dekameters", UN_SC_DAM * UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
173         {"cubic meter",      "cubic meters",      "m³",   "m3",   "Cubic Meters", UN_SC_M * UN_SC_M * UN_SC_M, 0.0,           B_UNIT_DEF_NONE}, /* base unit */
174         {"cubic decimeter",  "cubic decimeters",  "dm³",  "dm3",  "Cubic Decimeters", UN_SC_DM * UN_SC_DM * UN_SC_DM, 0.0,    B_UNIT_DEF_SUPPRESS},
175         {"cubic centimeter", "cubic centimeters", "cm³",  "cm3",  "Cubic Centimeters", UN_SC_CM * UN_SC_CM * UN_SC_CM, 0.0,   B_UNIT_DEF_NONE},
176         {"cubic millimeter", "cubic millimeters", "mm³",  "mm3",  "Cubic Millimeters", UN_SC_MM * UN_SC_MM * UN_SC_MM, 0.0,   B_UNIT_DEF_NONE},
177         {"cubic micrometer", "cubic micrometers", "µm³",  "um3",  "Cubic Micrometers", UN_SC_UM * UN_SC_UM * UN_SC_UM, 0.0,   B_UNIT_DEF_NONE},
178         {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
179 };
180 static struct bUnitCollection buMetricVolCollecton = {buMetricVolDef, 3, 0, sizeof(buMetricVolDef) / sizeof(bUnitDef)};
181
182 static struct bUnitDef buImperialVolDef[] = {
183         {"cubic mile", "cubic miles",       "cu mi", "cu m", "Cubic Miles", UN_SC_MI * UN_SC_MI * UN_SC_MI, 0.0,     B_UNIT_DEF_NONE},
184         {"cubic furlong", "cubic furlongs", "cu fur", NULL,  "Cubic Furlongs", UN_SC_FUR * UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
185         {"cubic chain", "cubic chains",     "cu ch", NULL,   "Cubic Chains", UN_SC_CH * UN_SC_CH * UN_SC_CH, 0.0,    B_UNIT_DEF_SUPPRESS},
186         {"cubic yard", "cubic yards",       "cu yd", NULL,   "Cubic Yards", UN_SC_YD * UN_SC_YD * UN_SC_YD, 0.0,     B_UNIT_DEF_NONE},
187         {"cubic foot", "cubic feet",        "cu ft", NULL,   "Cubic Feet", UN_SC_FT * UN_SC_FT * UN_SC_FT, 0.0,      B_UNIT_DEF_NONE}, /* base unit */
188         {"cubic inch", "cubic inches",      "cu in", NULL ,  "Cubic Inches", UN_SC_IN * UN_SC_IN * UN_SC_IN, 0.0,    B_UNIT_DEF_NONE},
189         {"cubic thou", "cubic thous",       "cu mil", NULL,  "Cubic Thous", UN_SC_MIL * UN_SC_MIL * UN_SC_MIL, 0.0,  B_UNIT_DEF_NONE},
190         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
191 };
192 static struct bUnitCollection buImperialVolCollecton = {buImperialVolDef, 4, 0, sizeof(buImperialVolDef) / sizeof(bUnitDef)};
193
194 /* Mass */
195 static struct bUnitDef buMetricMassDef[] = {
196         {"ton", "tonnes",           "ton", "t",  "1000 Kilograms", UN_SC_MTON, 0.0,  B_UNIT_DEF_NONE},
197         {"quintal", "quintals",     "ql",  "q",  "100 Kilograms", UN_SC_QL, 0.0,     B_UNIT_DEF_NONE},
198         {"kilogram", "kilograms",   "kg",  NULL, "Kilograms", UN_SC_KG, 0.0,         B_UNIT_DEF_NONE}, /* base unit */
199         {"hectogram", "hectograms", "hg",  NULL, "Hectograms", UN_SC_HG, 0.0,        B_UNIT_DEF_NONE},
200         {"dekagram", "dekagrams",   "dag", NULL, "10 Grams", UN_SC_DAG, 0.0,         B_UNIT_DEF_SUPPRESS},
201         {"gram", "grams",           "g",   NULL, "Grams", UN_SC_G, 0.0,              B_UNIT_DEF_NONE},
202         {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
203 };
204 static struct bUnitCollection buMetricMassCollecton = {buMetricMassDef, 2, 0, sizeof(buMetricMassDef) / sizeof(bUnitDef)};
205
206 static struct bUnitDef buImperialMassDef[] = {
207         {"ton", "tonnes",   "ton", "t", "Tonnes", UN_SC_ITON, 0.0,      B_UNIT_DEF_NONE},
208         {"centum weight", "centum weights", "cwt", NULL, "Centum weights", UN_SC_CWT, 0.0, B_UNIT_DEF_NONE},
209         {"stone", "stones", "st", NULL,     "Stones", UN_SC_ST, 0.0,    B_UNIT_DEF_NONE},
210         {"pound", "pounds", "lb", NULL,     "Pounds", UN_SC_LB, 0.0,    B_UNIT_DEF_NONE}, /* base unit */
211         {"ounce", "ounces", "oz", NULL,     "Ounces", UN_SC_OZ, 0.0,    B_UNIT_DEF_NONE},
212         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
213 };
214 static struct bUnitCollection buImperialMassCollecton = {buImperialMassDef, 3, 0, sizeof(buImperialMassDef) / sizeof(bUnitDef)};
215
216 /* Even if user scales the system to a point where km^3 is used, velocity and
217  * acceleration aren't scaled: that's why we have so few units for them */
218
219 /* Velocity */
220 static struct bUnitDef buMetricVelDef[] = {
221         {"meter per second", "meters per second",       "m/s",  NULL,   "Meters per second", UN_SC_M, 0.0,            B_UNIT_DEF_NONE}, /* base unit */
222         {"kilometer per hour", "kilometers per hour",   "km/h", NULL,   "Kilometers per hour", UN_SC_KM / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
223         {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
224 };
225 static struct bUnitCollection buMetricVelCollecton = {buMetricVelDef, 0, 0, sizeof(buMetricVelDef) / sizeof(bUnitDef)};
226
227 static struct bUnitDef buImperialVelDef[] = {
228         {"foot per second", "feet per second",  "ft/s", "fps",  "Feet per second", UN_SC_FT, 0.0,       B_UNIT_DEF_NONE}, /* base unit */
229         {"mile per hour", "miles per hour",     "mph", NULL,    "Miles per hour", UN_SC_MI / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
230         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
231 };
232 static struct bUnitCollection buImperialVelCollecton = {buImperialVelDef, 0, 0, sizeof(buImperialVelDef) / sizeof(bUnitDef)};
233
234 /* Acceleration */
235 static struct bUnitDef buMetricAclDef[] = {
236         {"meter per second squared", "meters per second squared", "m/s²", "m/s2", "Meters per second squared", UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* base unit */
237         {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
238 };
239 static struct bUnitCollection buMetricAclCollecton = {buMetricAclDef, 0, 0, sizeof(buMetricAclDef) / sizeof(bUnitDef)};
240
241 static struct bUnitDef buImperialAclDef[] = {
242         {"foot per second squared", "feet per second squared", "ft/s²", "ft/s2", "Feet per second squared", UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* base unit */
243         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
244 };
245 static struct bUnitCollection buImperialAclCollecton = {buImperialAclDef, 0, 0, sizeof(buImperialAclDef) / sizeof(bUnitDef)};
246
247 /* Time */
248 static struct bUnitDef buNaturalTimeDef[] = {
249         /* weeks? - probably not needed for blender */
250         {"day", "days",                 "d", NULL,  "Days",         90000.0, 0.0,   B_UNIT_DEF_NONE},
251         {"hour", "hours",               "hr", "h",  "Hours",        3600.0, 0.0,    B_UNIT_DEF_NONE},
252         {"minute", "minutes",           "min", "m", "Minutes",      60.0, 0.0,      B_UNIT_DEF_NONE},
253         {"second", "seconds",           "sec", "s", "Seconds",      1.0, 0.0,       B_UNIT_DEF_NONE}, /* base unit */
254         {"millisecond", "milliseconds", "ms", NULL, "Milliseconds", 0.001, 0.0,     B_UNIT_DEF_NONE},
255         {"microsecond", "microseconds", "µs", "us", "Microseconds", 0.000001, 0.0,  B_UNIT_DEF_NONE},
256         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
257 };
258 static struct bUnitCollection buNaturalTimeCollecton = {buNaturalTimeDef, 3, 0, sizeof(buNaturalTimeDef) / sizeof(bUnitDef)};
259
260
261 static struct bUnitDef buNaturalRotDef[] = {
262         {"degree", "degrees",                   "°", NULL, "Degrees",          M_PI/180.0, 0.0,        B_UNIT_DEF_NONE},
263 //      {"radian", "radians",                   "r", NULL, "Radians",           1.0, 0.0,                       B_UNIT_DEF_NONE},
264 //      {"turn", "turns",                               "t", NULL, "Turns",                     1.0/(M_PI*2.0), 0.0,B_UNIT_DEF_NONE},
265         {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
266 };
267 static struct bUnitCollection buNaturalRotCollection = {buNaturalRotDef, 0, 0, sizeof(buNaturalRotDef) / sizeof(bUnitDef)};
268
269 #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / 9) / sizeof(void *)) - 1)
270 static struct bUnitCollection *bUnitSystems[][9] = {
271         {NULL, NULL, NULL, NULL, NULL, &buNaturalRotCollection, &buNaturalTimeCollecton, NULL, NULL},
272         {NULL, &buMetricLenCollecton, &buMetricAreaCollecton, &buMetricVolCollecton, &buMetricMassCollecton, &buNaturalRotCollection, &buNaturalTimeCollecton, &buMetricVelCollecton, &buMetricAclCollecton}, /* metric */
273         {NULL, &buImperialLenCollecton, &buImperialAreaCollecton, &buImperialVolCollecton, &buImperialMassCollecton, &buNaturalRotCollection, &buNaturalTimeCollecton, &buImperialVelCollecton, &buImperialAclCollecton}, /* imperial */
274         {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
275 };
276
277
278
279 /* internal, has some option not exposed */
280 static bUnitCollection *unit_get_system(int system, int type)
281 {
282         assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) && (type < B_UNIT_TYPE_TOT));
283         return bUnitSystems[system][type]; /* select system to use, metric/imperial/other? */
284 }
285
286 static bUnitDef *unit_default(bUnitCollection *usys)
287 {
288         return &usys->units[usys->base_unit];
289 }
290
291 static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *unit_start, int suppress)
292 {
293         bUnitDef *unit;
294         double value_abs = value > 0.0 ? value : -value;
295
296         for (unit = unit_start ? unit_start : usys->units; unit->name; unit++) {
297
298                 if (suppress && (unit->flag & B_UNIT_DEF_SUPPRESS))
299                         continue;
300
301                 /* scale down scalar so 1cm doesnt convert to 10mm because of float error */
302                 if (value_abs >= unit->scalar * (1.0 - EPS))
303                         return unit;
304         }
305
306         return unit_default(usys);
307 }
308
309 /* convert into 2 units and 2 values for "2ft, 3inch" syntax */
310 static void unit_dual_convert(double value, bUnitCollection *usys, bUnitDef **unit_a, bUnitDef **unit_b,
311                               double *value_a, double *value_b)
312 {
313         bUnitDef *unit = unit_best_fit(value, usys, NULL, 1);
314
315         *value_a = (value < 0.0 ? ceil : floor)(value / unit->scalar) * unit->scalar;
316         *value_b = value - (*value_a);
317
318         *unit_a = unit;
319         *unit_b = unit_best_fit(*value_b, usys, *unit_a, 1);
320 }
321
322 static int unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys,
323                           /* non exposed options */
324                           bUnitDef *unit, char pad)
325 {
326         double value_conv;
327         int len, i;
328
329         if (unit) {
330                 /* use unit without finding the best one */
331         }
332         else if (value == 0.0) {
333                 /* use the default units since there is no way to convert */
334                 unit = unit_default(usys);
335         }
336         else {
337                 unit = unit_best_fit(value, usys, NULL, 1);
338         }
339
340         value_conv = value / unit->scalar;
341
342         /* Convert to a string */
343         {
344                 len = BLI_snprintf(str, len_max, "%.*f", prec, value_conv);
345
346                 if (len >= len_max)
347                         len = len_max;
348         }
349
350         /* Add unit prefix and strip zeros */
351
352         /* replace trailing zero's with spaces
353          * so the number is less complicated but allignment in a button wont
354          * jump about while dragging */
355         i = len - 1;
356
357         while (i > 0 && str[i] == '0') { /* 4.300 -> 4.3 */
358                 str[i--] = pad;
359         }
360
361         if (i > 0 && str[i] == '.') { /* 10. -> 10 */
362                 str[i--] = pad;
363         }
364
365         /* Now add the suffix */
366         if (i < len_max) {
367                 int j = 0;
368                 i++;
369                 while (unit->name_short[j] && (i < len_max)) {
370                         str[i++] = unit->name_short[j++];
371                 }
372
373                 if (pad) {
374                         /* this loop only runs if so many zeros were removed that
375                          * the unit name only used padded chars,
376                          * In that case add padding for the name. */
377
378                         while (i <= len + j && (i < len_max)) {
379                                 str[i++] = pad;
380                         }
381                 }
382         }
383
384         /* terminate no matter whats done with padding above */
385         if (i >= len_max)
386                 i = len_max - 1;
387
388         str[i] = '\0';
389         return i;
390 }
391
392 /* Used for drawing number buttons, try keep fast */
393 void bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, int split, int pad)
394 {
395         bUnitCollection *usys = unit_get_system(system, type);
396
397         if (usys == NULL || usys->units[0].name == NULL)
398                 usys = &buDummyCollecton;
399
400         /* split output makes sense only for length, mass and time */
401         if (split && (type == B_UNIT_LENGTH || type == B_UNIT_MASS || type == B_UNIT_TIME)) {
402                 bUnitDef *unit_a, *unit_b;
403                 double value_a, value_b;
404
405                 unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b);
406
407                 /* check the 2 is a smaller unit */
408                 if (unit_b > unit_a) {
409                         int i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0');
410
411                         /* is there enough space for at least 1 char of the next unit? */
412                         if (i + 2 < len_max) {
413                                 str[i++] = ' ';
414
415                                 /* use low precision since this is a smaller unit */
416                                 unit_as_string(str + i, len_max - i, value_b, prec ? 1 : 0, usys, unit_b, '\0');
417                         }
418                         return;
419                 }
420         }
421
422         unit_as_string(str, len_max, value, prec, usys, NULL, pad ? ' ' : '\0');
423 }
424
425 BLI_INLINE int isalpha_or_utf8(const int ch)
426 {
427         return (ch >= 128 || isalpha(ch));
428 }
429
430 static const char *unit_find_str(const char *str, const char *substr)
431 {
432         const char *str_found;
433
434         if (substr && substr[0] != '\0') {
435                 str_found = strstr(str, substr);
436                 if (str_found) {
437                         /* previous char cannot be a letter */
438                         if (str_found == str ||
439                             /* weak unicode support!, so "µm" won't match up be replaced by "m"
440                              * since non ascii utf8 values will NEVER return TRUE */
441                             isalpha_or_utf8(*BLI_str_prev_char_utf8(str_found)) == 0)
442                         {
443                                 /* next char cannot be alphanum */
444                                 int len_name = strlen(substr);
445
446                                 if (!isalpha_or_utf8(*(str_found + len_name))) {
447                                         return str_found;
448                                 }
449                         }
450                 }
451
452         }
453         return NULL;
454
455 }
456
457 /* Note that numbers are added within brackets
458  * ") " - is used to detect numbers we added so we can detect if commas need to be added
459  *
460  * "1m1cm+2mm"                          - Original value
461  * "1*1#1*0.01#+2*0.001#"       - Replace numbers
462  * "1*1,1*0.01 +2*0.001 "       - Add comma's if ( - + * / % ^ < > ) not found in between
463  *
464  */
465
466 /* not too strict, (- = * /) are most common  */
467 static int ch_is_op(char op)
468 {
469         switch (op) {
470                 case '+':
471                 case '-':
472                 case '*':
473                 case '/':
474                 case '|':
475                 case '&':
476                 case '~':
477                 case '<':
478                 case '>':
479                 case '^':
480                 case '!':
481                 case '=':
482                 case '%':
483                         return 1;
484                 default:
485                         return 0;
486         }
487 }
488
489 static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit,
490                           const char *replace_str)
491 {
492         char *str_found;
493
494         if ((len_max > 0) && (str_found = (char *)unit_find_str(str, replace_str))) {
495                 /* XXX - investigate, does not respect len_max properly  */
496
497                 int len, len_num, len_name, len_move, found_ofs;
498
499                 found_ofs = (int)(str_found - str);
500
501                 len = strlen(str);
502
503                 len_name = strlen(replace_str);
504                 len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator */
505                 len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%g"SEP_STR, unit->scalar / scale_pref); /* # removed later */
506
507                 if (len_num > len_max)
508                         len_num = len_max;
509
510                 if (found_ofs + len_num + len_move > len_max) {
511                         /* can't move the whole string, move just as much as will fit */
512                         len_move -= (found_ofs + len_num + len_move) - len_max;
513                 }
514
515                 if (len_move > 0) {
516                         /* resize the last part of the string */
517                         memmove(str_found + len_num, str_found + len_name, len_move); /* may grow or shrink the string */
518                 }
519
520                 if (found_ofs + len_num > len_max) {
521                         /* not even the number will fit into the string, only copy part of it */
522                         len_num -= (found_ofs + len_num) - len_max;
523                 }
524
525                 if (len_num > 0) {
526                         /* its possible none of the number could be copied in */
527                         memcpy(str_found, str_tmp, len_num); /* without the string terminator */
528                 }
529
530                 /* since the null terminator wont be moved if the stringlen_max
531                  * was not long enough to fit everything in it */
532                 str[len_max - 1] = '\0';
533                 return found_ofs + len_num;
534         }
535         return 0;
536 }
537
538 static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit)
539 {
540         int ofs = 0;
541         ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_short);
542         ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_plural);
543         ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_alt);
544         ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name);
545         return ofs;
546 }
547
548 static int unit_find(const char *str, bUnitDef *unit)
549 {
550         if (unit_find_str(str, unit->name_short))   return 1;
551         if (unit_find_str(str, unit->name_plural))  return 1;
552         if (unit_find_str(str, unit->name_alt))     return 1;
553         if (unit_find_str(str, unit->name))         return 1;
554
555         return 0;
556 }
557
558 /* make a copy of the string that replaces the units with numbers
559  * this is used before parsing
560  * This is only used when evaluating user input and can afford to be a bit slower
561  *
562  * This is to be used before python evaluation so..
563  * 10.1km -> 10.1*1000.0
564  * ...will be resolved by python.
565  *
566  * values will be split by a comma's
567  * 5'2" -> 5'0.0254, 2*0.3048
568  *
569  * str_prev is optional, when valid it is used to get a base unit when none is set.
570  *
571  * return true of a change was made.
572  */
573 int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type)
574 {
575         bUnitCollection *usys = unit_get_system(system, type);
576
577         bUnitDef *unit;
578         char str_tmp[TEMP_STR_SIZE];
579         int change = 0;
580
581         if (usys == NULL || usys->units[0].name == NULL) {
582                 return 0;
583         }
584
585         { /* make lowercase */
586                 int i;
587                 char *ch = str;
588
589                 for (i = 0; (i >= len_max || *ch == '\0'); i++, ch++)
590                         if ((*ch >= 'A') && (*ch <= 'Z'))
591                                 *ch += ('a' - 'A');
592         }
593
594         for (unit = usys->units; unit->name; unit++) {
595                 /* in case there are multiple instances */
596                 while (unit_replace(str, len_max, str_tmp, scale_pref, unit))
597                         change = 1;
598         }
599         unit = NULL;
600
601         {
602                 /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */
603                 bUnitCollection *usys_iter;
604                 int system_iter;
605
606                 for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) {
607                         if (system_iter != system) {
608                                 usys_iter = unit_get_system(system_iter, type);
609                                 if (usys_iter) {
610                                         for (unit = usys_iter->units; unit->name; unit++) {
611                                                 int ofs = 0;
612                                                 /* in case there are multiple instances */
613                                                 while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref, unit)))
614                                                         change = 1;
615                                         }
616                                 }
617                         }
618                 }
619         }
620         unit = NULL;
621
622         if (change == 0) {
623                 /* no units given so infer a unit from the previous string or default */
624                 if (str_prev) {
625                         /* see which units the original value had */
626                         for (unit = usys->units; unit->name; unit++) {
627                                 if (unit_find(str_prev, unit))
628                                         break;
629                         }
630                 }
631
632                 if (unit == NULL || unit->name == NULL)
633                         unit = unit_default(usys);
634
635                 /* add the unit prefix and re-run, use brackets in case there was an expression given */
636                 if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)%s", str, unit->name) < sizeof(str_tmp)) {
637                         strncpy(str, str_tmp, len_max);
638                         return bUnit_ReplaceString(str, len_max, NULL, scale_pref, system, type);
639                 }
640                 else {
641                         /* BLI_snprintf would not fit into str_tmp, cant do much in this case
642                          * check for this because otherwise bUnit_ReplaceString could call its self forever */
643                         return 0;
644                 }
645
646         }
647
648         /* replace # with commas when there is no operator between it and the next number
649          *
650          * "1*1# 3*100# * 3"  ->  "1 *1, 3 *100  * 3"
651          *
652          * */
653         {
654                 char *str_found = str;
655                 char *ch = str;
656
657                 while ((str_found = strchr(str_found, SEP_CHR))) {
658
659                         int op_found = 0;
660                         /* any operators after this?*/
661                         for (ch = str_found + 1; *ch != '\0'; ch++) {
662
663                                 if (*ch == ' ' || *ch == '\t') {
664                                         /* do nothing */
665                                 }
666                                 else if (ch_is_op(*ch) || *ch == ',') { /* found an op, no need to insert a ',' */
667                                         op_found = 1;
668                                         break;
669                                 }
670                                 else { /* found a non-op character */
671                                         op_found = 0;
672                                         break;
673                                 }
674                         }
675
676                         *str_found++ = op_found ? ' ' : ',';
677                 }
678         }
679
680         return change;
681 }
682
683 /* 45µm --> 45um */
684 void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type)
685 {
686         bUnitCollection *usys = unit_get_system(system, type);
687
688         bUnitDef *unit;
689         bUnitDef *unit_def = unit_default(usys);
690
691         /* find and substitute all units */
692         for (unit = usys->units; unit->name; unit++) {
693                 if (len_max > 0 && (unit->name_alt || unit == unit_def)) {
694                         const char *found = unit_find_str(orig_str, unit->name_short);
695                         if (found) {
696                                 int offset = (int)(found - orig_str);
697                                 int len_name = 0;
698
699                                 /* copy everything before the unit */
700                                 offset = (offset < len_max ? offset : len_max);
701                                 strncpy(str, orig_str, offset);
702
703                                 str += offset;
704                                 orig_str += offset + strlen(unit->name_short);
705                                 len_max -= offset;
706
707                                 /* print the alt_name */
708                                 if (unit->name_alt)
709                                         len_name = BLI_snprintf(str, len_max, "%s", unit->name_alt);
710                                 else
711                                         len_name = 0;
712
713                                 len_name = (len_name < len_max ? len_name : len_max);
714                                 str += len_name;
715                                 len_max -= len_name;
716                         }
717                 }
718         }
719
720         /* finally copy the rest of the string */
721         strncpy(str, orig_str, len_max);
722 }
723
724 double bUnit_ClosestScalar(double value, int system, int type)
725 {
726         bUnitCollection *usys = unit_get_system(system, type);
727         bUnitDef *unit;
728
729         if (usys == NULL)
730                 return -1;
731
732         unit = unit_best_fit(value, usys, NULL, 1);
733         if (unit == NULL)
734                 return -1;
735
736         return unit->scalar;
737 }
738
739 double bUnit_BaseScalar(int system, int type)
740 {
741         bUnitCollection *usys = unit_get_system(system, type);
742         return unit_default(usys)->scalar;
743 }
744
745 /* external access */
746 int bUnit_IsValid(int system, int type)
747 {
748         return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT);
749 }
750
751 void bUnit_GetSystem(void **usys_pt, int *len, int system, int type)
752 {
753         bUnitCollection *usys = unit_get_system(system, type);
754         *usys_pt = usys;
755
756         if (usys == NULL) {
757                 *len = 0;
758                 return;
759         }
760
761         *len = usys->length;
762 }
763
764 int bUnit_GetBaseUnit(void *usys_pt)
765 {
766         return ((bUnitCollection *)usys_pt)->base_unit;
767 }
768
769 const char *bUnit_GetName(void *usys_pt, int index)
770 {
771         return ((bUnitCollection *)usys_pt)->units[index].name;
772 }
773 const char *bUnit_GetNameDisplay(void *usys_pt, int index)
774 {
775         return ((bUnitCollection *)usys_pt)->units[index].name_display;
776 }
777
778 double bUnit_GetScaler(void *usys_pt, int index)
779 {
780         return ((bUnitCollection *)usys_pt)->units[index].scalar;
781 }