Blender  V3.3
unit.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
7 #include <ctype.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "BLI_math.h"
13 #include "BLI_string.h"
14 #include "BLI_string_utf8.h"
15 #include "BLI_sys_types.h"
16 
17 #include "DNA_scene_types.h"
18 
19 #include "BKE_unit.h" /* own include */
20 
21 #ifdef WIN32
22 # include "BLI_winstuff.h"
23 #endif
24 
25 /* No BKE or DNA includes! */
26 
27 /* Keep alignment. */
28 /* clang-format off */
29 
30 #define TEMP_STR_SIZE 256
31 
32 #define SEP_CHR '#'
33 #define SEP_STR "#"
34 
35 #define EPS 0.001
36 
37 #define UN_SC_KM 1000.0f
38 #define UN_SC_HM 100.0f
39 #define UN_SC_DAM 10.0f
40 #define UN_SC_M 1.0f
41 #define UN_SC_DM 0.1f
42 #define UN_SC_CM 0.01f
43 #define UN_SC_MM 0.001f
44 #define UN_SC_UM 0.000001f
45 
46 #define UN_SC_MI 1609.344f
47 #define UN_SC_FUR 201.168f
48 #define UN_SC_CH 20.1168f
49 #define UN_SC_YD 0.9144f
50 #define UN_SC_FT 0.3048f
51 #define UN_SC_IN 0.0254f
52 #define UN_SC_MIL 0.0000254f
53 
54 #define UN_SC_MTON 1000.0f /* Metric ton. */
55 #define UN_SC_QL 100.0f
56 #define UN_SC_KG 1.0f
57 #define UN_SC_HG 0.1f
58 #define UN_SC_DAG 0.01f
59 #define UN_SC_G 0.001f
60 #define UN_SC_MG 0.000001f
61 
62 #define UN_SC_ITON 907.18474f /* Imperial ton. */
63 #define UN_SC_CWT 45.359237f
64 #define UN_SC_ST 6.35029318f
65 #define UN_SC_LB 0.45359237f
66 #define UN_SC_OZ 0.028349523125f
67 
68 #define UN_SC_FAH 0.555555555555f
69 
70 /* clang-format on */
71 
72 /* Define a single unit. */
73 typedef struct bUnitDef {
74  const char *name;
76  const char *name_plural;
78  const char *name_short;
83  const char *name_alt;
84 
86  const char *name_display;
88  const char *identifier;
89 
90  double scalar;
92  double bias;
93  int flag;
95 
96 enum {
106 };
107 
108 /* Define a single unit system. */
109 typedef struct bUnitCollection {
110  const struct bUnitDef *units;
114  int flag;
116  int length;
118 
119 /* Keep table Alignment. */
120 /* clang-format off */
121 
122 #define UNIT_COLLECTION_LENGTH(def) (ARRAY_SIZE(def) - 1)
123 #define NULL_UNIT {NULL, NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
124 
125 /* Dummy */
126 static struct bUnitDef buDummyDef[] = { {"", NULL, "", NULL, NULL, NULL, 1.0, 0.0}, NULL_UNIT};
127 static struct bUnitCollection buDummyCollection = {buDummyDef, 0, 0, sizeof(buDummyDef)};
128 
129 /* Lengths. */
130 static struct bUnitDef buMetricLenDef[] = {
131  {"kilometer", "kilometers", "km", NULL, "Kilometers", "KILOMETERS", UN_SC_KM, 0.0, B_UNIT_DEF_NONE},
132  {"hectometer", "hectometers", "hm", NULL, "100 Meters", "HECTOMETERS", UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS},
133  {"dekameter", "dekameters", "dam", NULL, "10 Meters", "DEKAMETERS", UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
134  {"meter", "meters", "m", NULL, "Meters", "METERS", UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
135  {"decimeter", "decimeters", "dm", NULL, "10 Centimeters", "DECIMETERS", UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
136  {"centimeter", "centimeters", "cm", NULL, "Centimeters", "CENTIMETERS", UN_SC_CM, 0.0, B_UNIT_DEF_NONE},
137  {"millimeter", "millimeters", "mm", NULL, "Millimeters", "MILLIMETERS", UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH},
138  {"micrometer", "micrometers", "µm", "um", "Micrometers", "MICROMETERS", UN_SC_UM, 0.0, B_UNIT_DEF_NONE},
139 
140  /* These get displayed because of float precision problems in the transform header,
141  * could work around, but for now probably people won't use these. */
142 #if 0
143  {"nanometer", "Nanometers", "nm", NULL, 0.000000001, 0.0, B_UNIT_DEF_NONE},
144  {"picometer", "Picometers", "pm", NULL, 0.000000000001, 0.0, B_UNIT_DEF_NONE},
145 #endif
146  NULL_UNIT,
147 };
149 
150 static struct bUnitDef buImperialLenDef[] = {
151  {"mile", "miles", "mi", NULL, "Miles", "MILES", UN_SC_MI, 0.0, B_UNIT_DEF_NONE},
152  {"furlong", "furlongs", "fur", NULL, "Furlongs", "FURLONGS", UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
153  {"chain", "chains", "ch", NULL, "Chains", "CHAINS", UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS},
154  {"yard", "yards", "yd", NULL, "Yards", "YARDS", UN_SC_YD, 0.0, B_UNIT_DEF_SUPPRESS},
155  {"foot", "feet", "'", "ft", "Feet", "FEET", UN_SC_FT, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE}, /* Base unit. */
156  {"inch", "inches", "\"", "in", "Inches", "INCHES", UN_SC_IN, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE},
157  {"thou", "thou", "thou", "mil", "Thou", "THOU", UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* Plural for "thou" has no 's'. */
158  NULL_UNIT,
159 };
161 
162 /* Areas. */
163 static struct bUnitDef buMetricAreaDef[] = {
164  {"square kilometer", "square kilometers", "km²", "km2", "Square Kilometers", NULL, UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE},
165  {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* Hectare. */
166  {"square dekameter", "square dekameters", "dam²", "dam2", "Square Dekameters", NULL, UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, /* are */
167  {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
168  {"square decimeter", "square decimetees", "dm²", "dm2", "Square Decimeters", NULL, UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
169  {"square centimeter", "square centimeters", "cm²", "cm2", "Square Centimeters", NULL, UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE},
170  {"square millimeter", "square millimeters", "mm²", "mm2", "Square Millimeters", NULL, UN_SC_MM * UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH},
171  {"square micrometer", "square micrometers", "µm²", "um2", "Square Micrometers", NULL, UN_SC_UM * UN_SC_UM, 0.0, B_UNIT_DEF_NONE},
172  NULL_UNIT,
173 };
175 
176 static struct bUnitDef buImperialAreaDef[] = {
177  {"square mile", "square miles", "sq mi", "sq m", "Square Miles", NULL, UN_SC_MI * UN_SC_MI, 0.0, B_UNIT_DEF_NONE},
178  {"square furlong", "square furlongs", "sq fur", NULL, "Square Furlongs", NULL, UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
179  {"square chain", "square chains", "sq ch", NULL, "Square Chains", NULL, UN_SC_CH * UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS},
180  {"square yard", "square yards", "sq yd", NULL, "Square Yards", NULL, UN_SC_YD * UN_SC_YD, 0.0, B_UNIT_DEF_NONE},
181  {"square foot", "square feet", "sq ft", NULL, "Square Feet", NULL, UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
182  {"square inch", "square inches", "sq in", NULL, "Square Inches", NULL, UN_SC_IN * UN_SC_IN, 0.0, B_UNIT_DEF_NONE},
183  {"square thou", "square thou", "sq mil", NULL, "Square Thou", NULL, UN_SC_MIL * UN_SC_MIL, 0.0, B_UNIT_DEF_NONE},
184  NULL_UNIT,
185 };
187 
188 /* Volumes. */
189 static struct bUnitDef buMetricVolDef[] = {
190  {"cubic kilometer", "cubic kilometers", "km³", "km3", "Cubic Kilometers", NULL, UN_SC_KM * UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE},
191  {"cubic hectometer", "cubic hectometers", "hm³", "hm3", "Cubic Hectometers", NULL, UN_SC_HM * UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS},
192  {"cubic dekameter", "cubic dekameters", "dam³", "dam3", "Cubic Dekameters", NULL, UN_SC_DAM * UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
193  {"cubic meter", "cubic meters", "m³", "m3", "Cubic Meters", NULL, UN_SC_M * UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
194  {"cubic decimeter", "cubic decimeters", "dm³", "dm3", "Cubic Decimeters", NULL, UN_SC_DM * UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS},
195  {"cubic centimeter", "cubic centimeters", "cm³", "cm3", "Cubic Centimeters", NULL, UN_SC_CM * UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE},
196  {"cubic millimeter", "cubic millimeters", "mm³", "mm3", "Cubic Millimeters", NULL, UN_SC_MM * UN_SC_MM * UN_SC_MM, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_TENTH},
197  {"cubic micrometer", "cubic micrometers", "µm³", "um3", "Cubic Micrometers", NULL, UN_SC_UM * UN_SC_UM * UN_SC_UM, 0.0, B_UNIT_DEF_NONE},
198  NULL_UNIT,
199 };
201 
202 static struct bUnitDef buImperialVolDef[] = {
203  {"cubic mile", "cubic miles", "cu mi", "cu m", "Cubic Miles", NULL, UN_SC_MI * UN_SC_MI * UN_SC_MI, 0.0, B_UNIT_DEF_NONE},
204  {"cubic furlong", "cubic furlongs", "cu fur", NULL, "Cubic Furlongs", NULL, UN_SC_FUR * UN_SC_FUR * UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
205  {"cubic chain", "cubic chains", "cu ch", NULL, "Cubic Chains", NULL, UN_SC_CH * UN_SC_CH * UN_SC_CH, 0.0, B_UNIT_DEF_SUPPRESS},
206  {"cubic yard", "cubic yards", "cu yd", NULL, "Cubic Yards", NULL, UN_SC_YD * UN_SC_YD * UN_SC_YD, 0.0, B_UNIT_DEF_NONE},
207  {"cubic foot", "cubic feet", "cu ft", NULL, "Cubic Feet", NULL, UN_SC_FT * UN_SC_FT * UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
208  {"cubic inch", "cubic inches", "cu in", NULL, "Cubic Inches", NULL, UN_SC_IN * UN_SC_IN * UN_SC_IN, 0.0, B_UNIT_DEF_NONE},
209  {"cubic thou", "cubic thou", "cu mil", NULL, "Cubic Thou", NULL, UN_SC_MIL * UN_SC_MIL * UN_SC_MIL, 0.0, B_UNIT_DEF_NONE},
210  NULL_UNIT,
211 };
213 
214 /* Mass. */
215 static struct bUnitDef buMetricMassDef[] = {
216  {"ton", "tonnes", "ton", "t", "Tonnes", "TONNES", UN_SC_MTON, 0.0, B_UNIT_DEF_NONE},
217  {"quintal", "quintals", "ql", "q", "100 Kilograms", "QUINTALS", UN_SC_QL, 0.0, B_UNIT_DEF_SUPPRESS},
218  {"kilogram", "kilograms", "kg", NULL, "Kilograms", "KILOGRAMS", UN_SC_KG, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
219  {"hectogram", "hectograms", "hg", NULL, "Hectograms", "HECTOGRAMS", UN_SC_HG, 0.0, B_UNIT_DEF_SUPPRESS},
220  {"dekagram", "dekagrams", "dag", NULL, "10 Grams", "DEKAGRAMS", UN_SC_DAG, 0.0, B_UNIT_DEF_SUPPRESS},
221  {"gram", "grams", "g", NULL, "Grams", "GRAMS", UN_SC_G, 0.0, B_UNIT_DEF_NONE},
222  {"milligram", "milligrams", "mg", NULL, "Milligrams", "MILLIGRAMS", UN_SC_MG, 0.0, B_UNIT_DEF_NONE},
223  NULL_UNIT,
224 };
226 
227 static struct bUnitDef buImperialMassDef[] = {
228  {"ton", "tonnes", "ton", "t", "Tonnes", "TONNES", UN_SC_ITON, 0.0, B_UNIT_DEF_NONE},
229  {"centum weight", "centum weights", "cwt", NULL, "Centum weights", "CENTUM_WEIGHTS", UN_SC_CWT, 0.0, B_UNIT_DEF_NONE},
230  {"stone", "stones", "st", NULL, "Stones", "STONES", UN_SC_ST, 0.0, B_UNIT_DEF_NONE},
231  {"pound", "pounds", "lb", NULL, "Pounds", "POUNDS", UN_SC_LB, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
232  {"ounce", "ounces", "oz", NULL, "Ounces", "OUNCES", UN_SC_OZ, 0.0, B_UNIT_DEF_NONE},
233  NULL_UNIT,
234 };
236 
237 /* Even if user scales the system to a point where km^3 is used, velocity and
238  * acceleration aren't scaled: that's why we have so few units for them. */
239 
240 /* Velocity. */
241 static struct bUnitDef buMetricVelDef[] = {
242  {"meter per second", "meters per second", "m/s", NULL, "Meters per second", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
243  {"kilometer per hour", "kilometers per hour", "km/h", NULL, "Kilometers per hour", NULL, UN_SC_KM / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
244  NULL_UNIT,
245 };
247 
248 static struct bUnitDef buImperialVelDef[] = {
249  {"foot per second", "feet per second", "ft/s", "fps", "Feet per second", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
250  {"mile per hour", "miles per hour", "mph", NULL, "Miles per hour", NULL, UN_SC_MI / 3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
251  NULL_UNIT,
252 };
254 
255 /* Acceleration. */
256 static struct bUnitDef buMetricAclDef[] = {
257  {"meter per second squared", "meters per second squared", "m/s²", "m/s2", "Meters per second squared", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
258  NULL_UNIT,
259 };
261 
262 static struct bUnitDef buImperialAclDef[] = {
263  {"foot per second squared", "feet per second squared", "ft/s²", "ft/s2", "Feet per second squared", NULL, UN_SC_FT, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
264  NULL_UNIT,
265 };
267 
268 /* Time. */
269 static struct bUnitDef buNaturalTimeDef[] = {
270  /* Weeks? - probably not needed for Blender. */
271  {"day", "days", "d", NULL, "Days", "DAYS", 86400.0, 0.0, B_UNIT_DEF_NONE},
272  {"hour", "hours", "hr", "h", "Hours", "HOURS", 3600.0, 0.0, B_UNIT_DEF_NONE},
273  {"minute", "minutes", "min", "m", "Minutes", "MINUTES", 60.0, 0.0, B_UNIT_DEF_NONE},
274  {"second", "seconds", "sec", "s", "Seconds", "SECONDS", 1.0, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
275  {"millisecond", "milliseconds", "ms", NULL, "Milliseconds", "MILLISECONDS", 0.001, 0.0, B_UNIT_DEF_NONE},
276  {"microsecond", "microseconds", "µs", "us", "Microseconds", "MICROSECONDS", 0.000001, 0.0, B_UNIT_DEF_NONE},
277  NULL_UNIT,
278 };
280 
281 
282 static struct bUnitDef buNaturalRotDef[] = {
283  {"degree", "degrees", "°", "d", "Degrees", "DEGREES", M_PI / 180.0, 0.0, B_UNIT_DEF_NONE | B_UNIT_DEF_NO_SPACE},
284  /* arcminutes/arcseconds are used in Astronomy/Navigation areas... */
285  {"arcminute", "arcminutes", "'", NULL, "Arcminutes", "ARCMINUTES", (M_PI / 180.0) / 60.0, 0.0, B_UNIT_DEF_SUPPRESS | B_UNIT_DEF_NO_SPACE},
286  {"arcsecond", "arcseconds", "\"", NULL, "Arcseconds", "ARCSECONDS", (M_PI / 180.0) / 3600.0, 0.0, B_UNIT_DEF_SUPPRESS | B_UNIT_DEF_NO_SPACE},
287  {"radian", "radians", "r", NULL, "Radians", "RADIANS", 1.0, 0.0, B_UNIT_DEF_NONE},
288 #if 0
289  {"turn", "turns", "t", NULL, "Turns", NULL, 1.0 / (M_PI * 2.0), 0.0, B_UNIT_DEF_NONE},
290 #endif
291  NULL_UNIT,
292 };
294 
295 /* Camera Lengths. */
296 static struct bUnitDef buCameraLenDef[] = {
297  {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
298  {"decimeter", "decimeters", "dm", NULL, "10 Centimeters", NULL, UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS},
299  {"centimeter", "centimeters", "cm", NULL, "Centimeters", NULL, UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
300  {"millimeter", "millimeters", "mm", NULL, "Millimeters", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE},
301  {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS},
302  NULL_UNIT,
303 };
305 
306 /* (Light) Power. */
307 static struct bUnitDef buPowerDef[] = {
308  {"gigawatt", "gigawatts", "GW", NULL, "Gigawatts", NULL, 1e9f, 0.0, B_UNIT_DEF_NONE},
309  {"megawatt", "megawatts", "MW", NULL, "Megawatts", NULL, 1e6f, 0.0, B_UNIT_DEF_CASE_SENSITIVE},
310  {"kilowatt", "kilowatts", "kW", NULL, "Kilowatts", NULL, 1e3f, 0.0, B_UNIT_DEF_SUPPRESS},
311  {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
312  {"milliwatt", "milliwatts", "mW", NULL, "Milliwatts", NULL, 1e-3f, 0.0, B_UNIT_DEF_CASE_SENSITIVE},
313  {"microwatt", "microwatts", "µW", "uW", "Microwatts", NULL, 1e-6f, 0.0, B_UNIT_DEF_NONE},
314  {"nanowatt", "nanowatts", "nW", NULL, "Nanowatts", NULL, 1e-9f, 0.0, B_UNIT_DEF_NONE},
315  NULL_UNIT,
316 };
318 
319 /* Temperature */
320 static struct bUnitDef buMetricTempDef[] = {
321  {"kelvin", "kelvin", "K", NULL, "Kelvin", "KELVIN", 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
322  {"celsius", "celsius", "°C", "C", "Celsius", "CELSIUS", 1.0f, 273.15, B_UNIT_DEF_NONE},
323  NULL_UNIT,
324 };
326 
327 static struct bUnitDef buImperialTempDef[] = {
328  {"kelvin", "kelvin", "K", NULL, "Kelvin", "KELVIN", 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */
329  {"fahrenheit", "fahrenheit", "°F", "F", "Fahrenheit", "FAHRENHEIT", UN_SC_FAH, 459.67, B_UNIT_DEF_NONE},
330  NULL_UNIT,
331 };
334 
335 /* clang-format on */
336 
337 #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1)
338 static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = {
339  /* Natural. */
340  {NULL,
341  NULL,
342  NULL,
343  NULL,
344  NULL,
348  NULL,
349  NULL,
350  NULL,
351  NULL,
352  NULL},
353  /* Metric. */
354  {NULL,
367  /* Imperial. */
368  {NULL,
381  {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
382 };
383 
384 static const bUnitCollection *unit_get_system(int system, int type)
385 {
386  BLI_assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) &&
387  (type < B_UNIT_TYPE_TOT));
388  return bUnitSystems[system][type]; /* Select system to use: metric/imperial/other? */
389 }
390 
391 static const bUnitDef *unit_default(const bUnitCollection *usys)
392 {
393  return &usys->units[usys->base_unit];
394 }
395 
396 static const bUnitDef *unit_best_fit(double value,
397  const bUnitCollection *usys,
398  const bUnitDef *unit_start,
399  int suppress)
400 {
401  double value_abs = value > 0.0 ? value : -value;
402 
403  for (const bUnitDef *unit = unit_start ? unit_start : usys->units; unit->name; unit++) {
404 
405  if (suppress && (unit->flag & B_UNIT_DEF_SUPPRESS)) {
406  continue;
407  }
408 
409  /* Scale down scalar so 1cm doesn't convert to 10mm because of float error. */
410  if (UNLIKELY(unit->flag & B_UNIT_DEF_TENTH)) {
411  if (value_abs >= unit->scalar * (0.1 - EPS)) {
412  return unit;
413  }
414  }
415  else {
416  if (value_abs >= unit->scalar * (1.0 - EPS)) {
417  return unit;
418  }
419  }
420  }
421 
422  return unit_default(usys);
423 }
424 
425 /* Convert into 2 units and 2 values for "2ft, 3inch" syntax. */
426 static void unit_dual_convert(double value,
427  const bUnitCollection *usys,
428  bUnitDef const **r_unit_a,
429  bUnitDef const **r_unit_b,
430  double *r_value_a,
431  double *r_value_b,
432  const bUnitDef *main_unit)
433 {
434  const bUnitDef *unit = (main_unit) ? main_unit : unit_best_fit(value, usys, NULL, 1);
435 
436  *r_value_a = (value < 0.0 ? ceil : floor)(value / unit->scalar) * unit->scalar;
437  *r_value_b = value - (*r_value_a);
438 
439  *r_unit_a = unit;
440  *r_unit_b = unit_best_fit(*r_value_b, usys, *r_unit_a, 1);
441 }
442 
443 static size_t unit_as_string(char *str,
444  int len_max,
445  double value,
446  int prec,
447  const bUnitCollection *usys,
448  /* Non exposed options. */
449  const bUnitDef *unit,
450  char pad)
451 {
452  if (unit == NULL) {
453  if (value == 0.0) {
454  /* Use the default units since there is no way to convert. */
455  unit = unit_default(usys);
456  }
457  else {
458  unit = unit_best_fit(value, usys, NULL, 1);
459  }
460  }
461 
462  double value_conv = (value / unit->scalar) - unit->bias;
463  bool strip_skip = false;
464 
465  /* Negative precision is used to disable stripping of zeroes.
466  * This reduces text jumping when changing values. */
467  if (prec < 0) {
468  strip_skip = true;
469  prec *= -1;
470  }
471 
472  /* Adjust precision to expected number of significant digits.
473  * Note that here, we shall not have to worry about very big/small numbers, units are expected
474  * to replace 'scientific notation' in those cases. */
475  prec -= integer_digits_d(value_conv);
476 
477  CLAMP(prec, 0, 6);
478 
479  /* Convert to a string. */
480  size_t len = BLI_snprintf_rlen(str, len_max, "%.*f", prec, value_conv);
481 
482  /* Add unit prefix and strip zeros. */
483 
484  /* Replace trailing zero's with spaces so the number
485  * is less complicated but alignment in a button won't
486  * jump about while dragging. */
487  size_t i = len - 1;
488 
489  if (prec > 0) {
490  if (!strip_skip) {
491  while (i > 0 && str[i] == '0') { /* 4.300 -> 4.3 */
492  str[i--] = pad;
493  }
494 
495  if (i > 0 && str[i] == '.') { /* 10. -> 10 */
496  str[i--] = pad;
497  }
498  }
499  }
500 
501  /* Now add a space for all units except foot, inch, degree, arcminute, arcsecond. */
502  if (!(unit->flag & B_UNIT_DEF_NO_SPACE)) {
503  str[++i] = ' ';
504  }
505 
506  /* Now add the suffix. */
507  if (i < len_max) {
508  int j = 0;
509  i++;
510  while (unit->name_short[j] && (i < len_max)) {
511  str[i++] = unit->name_short[j++];
512  }
513  }
514 
515  /* Terminate no matter what's done with padding above. */
516  if (i >= len_max) {
517  i = len_max - 1;
518  }
519 
520  str[i] = '\0';
521  return i;
522 }
523 
524 static bool unit_should_be_split(int type)
525 {
527 }
528 
529 typedef struct {
530  int system;
531  int rotation;
532  /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection. */
533  int length;
534  int mass;
535  int time;
538 
540 {
541  PreferredUnits units = {0};
542  units.system = settings->system;
543  units.rotation = settings->system_rotation;
544  units.length = settings->length_unit;
545  units.mass = settings->mass_unit;
546  units.time = settings->time_unit;
547  units.temperature = settings->temperature_unit;
548  return units;
549 }
550 
551 static size_t unit_as_string_split_pair(char *str,
552  int len_max,
553  double value,
554  int prec,
555  const bUnitCollection *usys,
556  const bUnitDef *main_unit)
557 {
558  const bUnitDef *unit_a, *unit_b;
559  double value_a, value_b;
560  unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b, main_unit);
561 
562  /* Check the 2 is a smaller unit. */
563  if (unit_b > unit_a) {
564  size_t i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0');
565 
566  prec -= integer_digits_d(value_a / unit_b->scalar) -
567  integer_digits_d(value_b / unit_b->scalar);
568  prec = max_ii(prec, 0);
569 
570  /* Is there enough space for at least 1 char of the next unit? */
571  if (i + 2 < len_max) {
572  str[i++] = ' ';
573 
574  /* Use low precision since this is a smaller unit. */
575  i += unit_as_string(str + i, len_max - i, value_b, prec, usys, unit_b, '\0');
576  }
577  return i;
578  }
579 
580  return -1;
581 }
582 
584 {
585  return usys != NULL && usys->units[0].name != NULL;
586 }
587 
589 {
590  const bUnitCollection *usys = unit_get_system(units.system, type);
591  if (!is_valid_unit_collection(usys)) {
592  return NULL;
593  }
594 
595  int max_offset = usys->length - 1;
596 
597  switch (type) {
598  case B_UNIT_LENGTH:
599  case B_UNIT_AREA:
600  case B_UNIT_VOLUME:
601  if (units.length == USER_UNIT_ADAPTIVE) {
602  return NULL;
603  }
604  return usys->units + MIN2(units.length, max_offset);
605  case B_UNIT_MASS:
606  if (units.mass == USER_UNIT_ADAPTIVE) {
607  return NULL;
608  }
609  return usys->units + MIN2(units.mass, max_offset);
610  case B_UNIT_TIME:
611  if (units.time == USER_UNIT_ADAPTIVE) {
612  return NULL;
613  }
614  return usys->units + MIN2(units.time, max_offset);
615  case B_UNIT_ROTATION:
616  if (units.rotation == 0) {
617  return usys->units + 0;
618  }
619  else if (units.rotation == USER_UNIT_ROT_RADIANS) {
620  return usys->units + 3;
621  }
622  break;
623  case B_UNIT_TEMPERATURE:
624  if (units.temperature == USER_UNIT_ADAPTIVE) {
625  return NULL;
626  }
627  return usys->units + MIN2(units.temperature, max_offset);
628  default:
629  break;
630  }
631  return NULL;
632 }
633 
634 /* Return the length of the generated string. */
635 static size_t unit_as_string_main(char *str,
636  int len_max,
637  double value,
638  int prec,
639  int type,
640  bool split,
641  bool pad,
642  PreferredUnits units)
643 {
644  const bUnitCollection *usys = unit_get_system(units.system, type);
645  const bUnitDef *main_unit = NULL;
646 
647  if (!is_valid_unit_collection(usys)) {
648  usys = &buDummyCollection;
649  }
650  else {
651  main_unit = get_preferred_display_unit_if_used(type, units);
652  }
653 
654  if (split && unit_should_be_split(type)) {
655  int length = unit_as_string_split_pair(str, len_max, value, prec, usys, main_unit);
656  /* Failed when length is negative, fallback to no split. */
657  if (length >= 0) {
658  return length;
659  }
660  }
661 
662  return unit_as_string(str, len_max, value, prec, usys, main_unit, pad ? ' ' : '\0');
663 }
664 
666  char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad)
667 {
668  PreferredUnits units;
669  units.system = system;
670  units.rotation = 0;
671  units.length = USER_UNIT_ADAPTIVE;
672  units.mass = USER_UNIT_ADAPTIVE;
673  units.time = USER_UNIT_ADAPTIVE;
675  return unit_as_string_main(str, len_max, value, prec, type, split, pad, units);
676 }
677 
679  int len_max,
680  double value,
681  int prec,
682  int type,
683  const UnitSettings *settings,
684  bool pad)
685 {
686  bool do_split = (settings->flag & USER_UNIT_OPT_SPLIT) != 0;
688  return unit_as_string_main(str, len_max, value, prec, type, do_split, pad, units);
689 }
690 
691 BLI_INLINE bool isalpha_or_utf8(const int ch)
692 {
693  return (ch >= 128 || isalpha(ch));
694 }
695 
696 static const char *unit_find_str(const char *str, const char *substr, bool case_sensitive)
697 {
698  if (substr == NULL || substr[0] == '\0') {
699  return NULL;
700  }
701 
702  while (true) {
703  /* Unit detection is case insensitive. */
704  const char *str_found;
705  if (case_sensitive) {
706  str_found = strstr(str, substr);
707  }
708  else {
709  str_found = BLI_strcasestr(str, substr);
710  }
711 
712  if (str_found) {
713  /* Previous char cannot be a letter. */
714  if (str_found == str ||
715  /* Weak unicode support!, so "µm" won't match up be replaced by "m"
716  * since non ascii utf8 values will NEVER return true */
717  isalpha_or_utf8(*BLI_str_find_prev_char_utf8(str_found, str)) == 0) {
718  /* Next char cannot be alphanum. */
719  int len_name = strlen(substr);
720 
721  if (!isalpha_or_utf8(*(str_found + len_name))) {
722  return str_found;
723  }
724  }
725  /* If str_found is not a valid unit, we have to check further in the string... */
726  for (str_found++; isalpha_or_utf8(*str_found); str_found++) {
727  /* Pass. */
728  }
729  str = str_found;
730  }
731  else {
732  break;
733  }
734  }
735 
736  return NULL;
737 }
738 
739 /* Note that numbers are added within brackets.
740  * ") " - is used to detect numbers we added so we can detect if commas need to be added.
741  *
742  * "1m1cm+2mm" - Original value.
743  * "1*1#1*0.01#+2*0.001#" - Replace numbers.
744  * "1*1+1*0.01 +2*0.001 " - Add plus signs if ( + - * / | & ~ < > ^ ! = % ) not found in between.
745  */
746 
747 /* Not too strict, (+ - * /) are most common. */
748 static bool ch_is_op(char op)
749 {
750  switch (op) {
751  case '+':
752  case '-':
753  case '*':
754  case '/':
755  case '|':
756  case '&':
757  case '~':
758  case '<':
759  case '>':
760  case '^':
761  case '!':
762  case '=':
763  case '%':
764  return true;
765  default:
766  return false;
767  }
768 }
769 
776 static char *find_next_negative(const char *str, const char *remaining_str)
777 {
778  char *str_found = strstr(remaining_str, "-");
779 
780  if (str_found == NULL) {
781  return NULL;
782  }
783 
784  /* Don't use the "-" from scientific notation, but make sure we can look backwards first. */
785  if ((str_found != str) && ELEM(*(str_found - 1), 'e', 'E')) {
786  return find_next_negative(str, str_found + 1);
787  }
788 
789  if (*(str_found + 1) == ' ') {
790  str_found++;
791  }
792 
793  return str_found + 1;
794 }
795 
802 static char *find_next_op(const char *str, char *remaining_str, int len_max)
803 {
804  int i;
805  for (i = 0; i < len_max; i++) {
806  if (remaining_str[i] == '\0') {
807  return remaining_str + i;
808  }
809 
810  if (ch_is_op(remaining_str[i])) {
811  /* Make sure we don't look backwards before the start of the string. */
812  if (remaining_str != str && i != 0) {
813  /* Check for velocity or acceleration (e.g. '/' in 'ft/s' is not an op). */
814  if ((remaining_str[i] == '/') && ELEM(remaining_str[i - 1], 't', 'T', 'm', 'M') &&
815  ELEM(remaining_str[i + 1], 's', 'S')) {
816  continue;
817  }
818 
819  /* Check for scientific notation. */
820  if (ELEM(remaining_str[i - 1], 'e', 'E')) {
821  continue;
822  }
823 
824  /* Return position before a space character. */
825  if (remaining_str[i - 1] == ' ') {
826  i--;
827  }
828  }
829 
830  return remaining_str + i;
831  }
832  }
833  BLI_assert_msg(0, "String should be NULL terminated");
834  return remaining_str + i;
835 }
836 
843 static bool unit_distribute_negatives(char *str, const int len_max)
844 {
845  bool changed = false;
846 
847  char *remaining_str = str;
848  int remaining_str_len = len_max;
849  while ((remaining_str = find_next_negative(str, remaining_str)) != NULL) {
850  /* Exit early in the unlikely situation that we've run out of length to add the parentheses. */
851  remaining_str_len = len_max - (int)(remaining_str - str);
852  if (remaining_str_len <= 2) {
853  return changed;
854  }
855 
856  changed = true;
857 
858  /* Add '(', shift the following characters to the right to make space. */
859  memmove(remaining_str + 1, remaining_str, remaining_str_len - 2);
860  *remaining_str = '(';
861 
862  /* Add the ')' before the next operation or at the end. */
863  remaining_str = find_next_op(str, remaining_str + 1, remaining_str_len);
864  remaining_str_len = len_max - (int)(remaining_str - str);
865  memmove(remaining_str + 1, remaining_str, remaining_str_len - 2);
866  *remaining_str = ')';
867 
868  /* Only move forward by 1 even though we added two characters. Minus signs need to be able to
869  * apply to the next block of values too. */
870  remaining_str += 1;
871  }
872 
873  return changed;
874 }
875 
880 static int find_previous_non_value_char(const char *str, const int start_ofs)
881 {
882  for (int i = start_ofs; i > 0; i--) {
883  if (ch_is_op(str[i - 1]) || strchr("( )", str[i - 1])) {
884  return i;
885  }
886  }
887  return 0;
888 }
889 
894 static int find_end_of_value_chars(const char *str, const int len_max, const int start_ofs)
895 {
896  int i;
897  for (i = start_ofs; i < len_max; i++) {
898  if (!strchr("0123456789eE.", str[i])) {
899  return i;
900  }
901  }
902  return i;
903 }
904 
905 static int unit_scale_str(char *str,
906  int len_max,
907  char *str_tmp,
908  double scale_pref,
909  const bUnitDef *unit,
910  const char *replace_str,
911  bool case_sensitive)
912 {
913  if (len_max < 0) {
914  return 0;
915  }
916 
917  /* XXX: investigate, does not respect len_max properly. */
918  char *str_found = (char *)unit_find_str(str, replace_str, case_sensitive);
919 
920  if (str_found == NULL) {
921  return 0;
922  }
923 
924  int found_ofs = (int)(str_found - str);
925 
926  int len = strlen(str);
927 
928  /* Deal with unit bias for temperature units. Order of operations is important, so we
929  * have to add parentheses, add the bias, then multiply by the scalar like usual.
930  *
931  * NOTE: If these changes don't fit in the buffer properly unit evaluation has failed,
932  * just try not to destroy anything while failing. */
933  if (unit->bias != 0.0) {
934  /* Add the open parenthesis. */
935  int prev_op_ofs = find_previous_non_value_char(str, found_ofs);
936  if (len + 1 < len_max) {
937  memmove(str + prev_op_ofs + 1, str + prev_op_ofs, len - prev_op_ofs + 1);
938  str[prev_op_ofs] = '(';
939  len++;
940  found_ofs++;
941  str_found++;
942  } /* If this doesn't fit, we have failed. */
943 
944  /* Add the addition sign, the bias, and the close parenthesis after the value. */
945  int value_end_ofs = find_end_of_value_chars(str, len_max, prev_op_ofs + 2);
946  int len_bias_num = BLI_snprintf_rlen(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias);
947  if (value_end_ofs + len_bias_num < len_max) {
948  memmove(str + value_end_ofs + len_bias_num, str + value_end_ofs, len - value_end_ofs + 1);
949  memcpy(str + value_end_ofs, str_tmp, len_bias_num);
950  len += len_bias_num;
951  found_ofs += len_bias_num;
952  str_found += len_bias_num;
953  } /* If this doesn't fit, we have failed. */
954  }
955 
956  int len_name = strlen(replace_str);
957  int len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */
958 
959  /* "#" Removed later */
960  int len_num = BLI_snprintf_rlen(
961  str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref);
962 
963  if (len_num > len_max) {
964  len_num = len_max;
965  }
966 
967  if (found_ofs + len_num + len_move > len_max) {
968  /* Can't move the whole string, move just as much as will fit. */
969  len_move -= (found_ofs + len_num + len_move) - len_max;
970  }
971 
972  if (len_move > 0) {
973  /* Resize the last part of the string.
974  * May grow or shrink the string. */
975  memmove(str_found + len_num, str_found + len_name, len_move);
976  }
977 
978  if (found_ofs + len_num > len_max) {
979  /* Not even the number will fit into the string, only copy part of it. */
980  len_num -= (found_ofs + len_num) - len_max;
981  }
982 
983  if (len_num > 0) {
984  /* It's possible none of the number could be copied in. */
985  memcpy(str_found, str_tmp, len_num); /* Without the string terminator. */
986  }
987 
988  /* Since the null terminator won't be moved if the stringlen_max
989  * was not long enough to fit everything in it. */
990  str[len_max - 1] = '\0';
991  return found_ofs + len_num;
992 }
993 
994 static int unit_replace(
995  char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit)
996 {
997  const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0;
998  int ofs = 0;
999  ofs += unit_scale_str(
1000  str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_short, case_sensitive);
1001  ofs += unit_scale_str(
1002  str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_plural, false);
1003  ofs += unit_scale_str(
1004  str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_alt, case_sensitive);
1005  ofs += unit_scale_str(str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name, false);
1006  return ofs;
1007 }
1008 
1009 static bool unit_find(const char *str, const bUnitDef *unit)
1010 {
1011  const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0;
1012  if (unit_find_str(str, unit->name_short, case_sensitive)) {
1013  return true;
1014  }
1015  if (unit_find_str(str, unit->name_plural, false)) {
1016  return true;
1017  }
1018  if (unit_find_str(str, unit->name_alt, case_sensitive)) {
1019  return true;
1020  }
1021  if (unit_find_str(str, unit->name, false)) {
1022  return true;
1023  }
1024 
1025  return false;
1026 }
1027 
1035  const char *str,
1036  const char *str_prev)
1037 {
1038  const bUnitDef *unit = NULL;
1039 
1040  /* See which units the new value has. */
1041  for (unit = usys->units; unit->name; unit++) {
1042  if (unit_find(str, unit)) {
1043  break;
1044  }
1045  }
1046  /* Else, try to infer the default unit from the previous string. */
1047  if (str_prev && (unit == NULL || unit->name == NULL)) {
1048  /* See which units the original value had. */
1049  for (unit = usys->units; unit->name; unit++) {
1050  if (unit_find(str_prev, unit)) {
1051  break;
1052  }
1053  }
1054  }
1055  /* Else, fall back to default unit. */
1056  if (unit == NULL || unit->name == NULL) {
1057  unit = unit_default(usys);
1058  }
1059 
1060  return unit;
1061 }
1062 
1064 {
1065  for (int system = 0; system < UNIT_SYSTEM_TOT; system++) {
1066  const bUnitCollection *usys = unit_get_system(system, type);
1067  if (!is_valid_unit_collection(usys)) {
1068  continue;
1069  }
1070 
1071  for (int i = 0; i < usys->length; i++) {
1072  if (unit_find(str, usys->units + i)) {
1073  return true;
1074  }
1075  }
1076  }
1077  return false;
1078 }
1079 
1080 double BKE_unit_apply_preferred_unit(const struct UnitSettings *settings, int type, double value)
1081 {
1083  const bUnitDef *unit = get_preferred_display_unit_if_used(type, units);
1084 
1085  const double scalar = (unit == NULL) ? BKE_unit_base_scalar(units.system, type) : unit->scalar;
1086  const double bias = (unit == NULL) ? 0.0 : unit->bias; /* Base unit shouldn't have a bias. */
1087 
1088  return value * scalar + bias;
1089 }
1090 
1092  char *str, int len_max, const char *str_prev, double scale_pref, int system, int type)
1093 {
1094  const bUnitCollection *usys = unit_get_system(system, type);
1095  if (!is_valid_unit_collection(usys)) {
1096  return false;
1097  }
1098 
1099  double scale_pref_base = scale_pref;
1100  char str_tmp[TEMP_STR_SIZE];
1101  bool changed = false;
1102 
1103  /* Fix cases like "-1m50cm" which would evaluate to -0.5m without this. */
1104  changed |= unit_distribute_negatives(str, len_max);
1105 
1106  /* Try to find a default unit from current or previous string. */
1107  const bUnitDef *default_unit = unit_detect_from_str(usys, str, str_prev);
1108 
1109  /* We apply the default unit to the whole expression (default unit is now the reference
1110  * '1.0' one). */
1111  scale_pref_base *= default_unit->scalar;
1112 
1113  /* Apply the default unit on the whole expression, this allows to handle nasty cases like
1114  * '2+2in'. */
1115  if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)*%.9g", str, default_unit->scalar) <
1116  sizeof(str_tmp)) {
1117  strncpy(str, str_tmp, len_max);
1118  }
1119  else {
1120  /* BLI_snprintf would not fit into str_tmp, can't do much in this case.
1121  * Check for this because otherwise BKE_unit_replace_string could call itself forever. */
1122  return changed;
1123  }
1124 
1125  for (const bUnitDef *unit = usys->units; unit->name; unit++) {
1126  /* In case there are multiple instances. */
1127  while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) {
1128  changed = true;
1129  }
1130  }
1131 
1132  /* Try other unit systems now, so we can evaluate imperial when metric is set for eg. */
1133  /* Note that checking other systems at that point means we do not support their units as
1134  * 'default' one. In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches,
1135  * not 4 inches. I do think this is the desired behavior!
1136  */
1137  for (int system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) {
1138  if (system_iter != system) {
1139  const bUnitCollection *usys_iter = unit_get_system(system_iter, type);
1140  if (usys_iter) {
1141  for (const bUnitDef *unit = usys_iter->units; unit->name; unit++) {
1142  int ofs = 0;
1143  /* In case there are multiple instances. */
1144  while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) {
1145  changed = true;
1146  }
1147  }
1148  }
1149  }
1150  }
1151 
1152  /* Replace # with add sign when there is no operator between it and the next number.
1153  *
1154  * "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3"
1155  */
1156  {
1157  char *str_found = str;
1158  const char *ch = str;
1159 
1160  while ((str_found = strchr(str_found, SEP_CHR))) {
1161  bool op_found = false;
1162 
1163  /* Any operators after this? */
1164  for (ch = str_found + 1; *ch != '\0'; ch++) {
1165  if (ELEM(*ch, ' ', '\t')) {
1166  continue;
1167  }
1168  op_found = (ch_is_op(*ch) || ELEM(*ch, ',', ')'));
1169  break;
1170  }
1171 
1172  /* If found an op, comma or closing parenthesis, no need to insert a '+', else we need it. */
1173  *str_found++ = op_found ? ' ' : '+';
1174  }
1175  }
1176 
1177  return changed;
1178 }
1179 
1180 void BKE_unit_name_to_alt(char *str, int len_max, const char *orig_str, int system, int type)
1181 {
1182  const bUnitCollection *usys = unit_get_system(system, type);
1183 
1184  /* Find and substitute all units. */
1185  for (const bUnitDef *unit = usys->units; unit->name; unit++) {
1186  if (len_max > 0 && unit->name_alt) {
1187  const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0;
1188  const char *found = unit_find_str(orig_str, unit->name_short, case_sensitive);
1189  if (found) {
1190  int offset = (int)(found - orig_str);
1191  int len_name = 0;
1192 
1193  /* Copy everything before the unit. */
1194  offset = (offset < len_max ? offset : len_max);
1195  strncpy(str, orig_str, offset);
1196 
1197  str += offset;
1198  orig_str += offset + strlen(unit->name_short);
1199  len_max -= offset;
1200 
1201  /* Print the alt_name. */
1202  if (unit->name_alt) {
1203  len_name = BLI_strncpy_rlen(str, unit->name_alt, len_max);
1204  }
1205  else {
1206  len_name = 0;
1207  }
1208 
1209  len_name = (len_name < len_max ? len_name : len_max);
1210  str += len_name;
1211  len_max -= len_name;
1212  }
1213  }
1214  }
1215 
1216  /* Finally copy the rest of the string. */
1217  strncpy(str, orig_str, len_max);
1218 }
1219 
1220 double BKE_unit_closest_scalar(double value, int system, int type)
1221 {
1222  const bUnitCollection *usys = unit_get_system(system, type);
1223 
1224  if (usys == NULL) {
1225  return -1;
1226  }
1227 
1228  const bUnitDef *unit = unit_best_fit(value, usys, NULL, 1);
1229  if (unit == NULL) {
1230  return -1;
1231  }
1232 
1233  return unit->scalar;
1234 }
1235 
1236 double BKE_unit_base_scalar(int system, int type)
1237 {
1238  const bUnitCollection *usys = unit_get_system(system, type);
1239  if (usys) {
1240  return unit_default(usys)->scalar;
1241  }
1242 
1243  return 1.0;
1244 }
1245 
1246 bool BKE_unit_is_valid(int system, int type)
1247 {
1248  return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT);
1249 }
1250 
1251 void BKE_unit_system_get(int system, int type, void const **r_usys_pt, int *r_len)
1252 {
1253  const bUnitCollection *usys = unit_get_system(system, type);
1254  *r_usys_pt = usys;
1255 
1256  if (usys == NULL) {
1257  *r_len = 0;
1258  return;
1259  }
1260 
1261  *r_len = usys->length;
1262 }
1263 
1264 int BKE_unit_base_get(const void *usys_pt)
1265 {
1266  return ((bUnitCollection *)usys_pt)->base_unit;
1267 }
1268 
1269 int BKE_unit_base_of_type_get(int system, int type)
1270 {
1271  return unit_get_system(system, type)->base_unit;
1272 }
1273 
1274 const char *BKE_unit_name_get(const void *usys_pt, int index)
1275 {
1276  return ((bUnitCollection *)usys_pt)->units[index].name;
1277 }
1278 const char *BKE_unit_display_name_get(const void *usys_pt, int index)
1279 {
1280  return ((bUnitCollection *)usys_pt)->units[index].name_display;
1281 }
1282 const char *BKE_unit_identifier_get(const void *usys_pt, int index)
1283 {
1284  const bUnitDef *unit = ((const bUnitCollection *)usys_pt)->units + index;
1285  if (unit->identifier == NULL) {
1286  BLI_assert_msg(0, "identifier for this unit is not specified yet");
1287  }
1288  return unit->identifier;
1289 }
1290 
1291 double BKE_unit_scalar_get(const void *usys_pt, int index)
1292 {
1293  return ((bUnitCollection *)usys_pt)->units[index].scalar;
1294 }
1295 
1296 bool BKE_unit_is_suppressed(const void *usys_pt, int index)
1297 {
1298  return (((bUnitCollection *)usys_pt)->units[index].flag & B_UNIT_DEF_SUPPRESS) != 0;
1299 }
@ B_UNIT_AREA
Definition: BKE_unit.h:102
@ B_UNIT_TYPE_TOT
Definition: BKE_unit.h:113
@ B_UNIT_VOLUME
Definition: BKE_unit.h:103
@ B_UNIT_LENGTH
Definition: BKE_unit.h:101
@ B_UNIT_ROTATION
Definition: BKE_unit.h:105
@ B_UNIT_TEMPERATURE
Definition: BKE_unit.h:112
@ B_UNIT_CAMERA
Definition: BKE_unit.h:110
@ B_UNIT_MASS
Definition: BKE_unit.h:104
@ B_UNIT_TIME
Definition: BKE_unit.h:106
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
#define BLI_INLINE
MINLINE int integer_digits_d(double d)
MINLINE int max_ii(int a, int b)
#define M_PI
Definition: BLI_math_base.h:20
size_t BLI_strncpy_rlen(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:120
char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:538
size_t BLI_snprintf_rlen(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
const char * BLI_str_find_prev_char_utf8(const char *p, const char *str_start) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1
#define UNLIKELY(x)
#define ELEM(...)
#define MIN2(a, b)
Compatibility-like things for windows.
#define USER_UNIT_OPT_SPLIT
#define USER_UNIT_ROT_RADIANS
#define USER_UNIT_ADAPTIVE
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
int pad[32 - sizeof(int)]
int len
Definition: draw_manager.c:108
#define str(s)
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_device_inline float3 ceil(const float3 &a)
Definition: math_float3.h:363
void split(const std::string &s, const char delim, std::vector< std::string > &tokens)
Definition: abc_util.cc:92
T length(const vec_base< T, Size > &a)
T floor(const T &a)
int time
Definition: unit.c:535
int mass
Definition: unit.c:534
int temperature
Definition: unit.c:536
int length
Definition: unit.c:533
int rotation
Definition: unit.c:531
int system
Definition: unit.c:530
int base_unit
Definition: unit.c:112
int length
Definition: unit.c:116
const struct bUnitDef * units
Definition: unit.c:110
Definition: unit.c:73
const char * identifier
Definition: unit.c:88
const char * name_short
Definition: unit.c:78
const char * name_display
Definition: unit.c:86
const char * name_alt
Definition: unit.c:83
double scalar
Definition: unit.c:90
int flag
Definition: unit.c:93
const char * name
Definition: unit.c:74
const char * name_plural
Definition: unit.c:76
double bias
Definition: unit.c:92
static struct bUnitCollection buMetricTempCollection
Definition: unit.c:325
static int find_end_of_value_chars(const char *str, const int len_max, const int start_ofs)
Definition: unit.c:894
static struct bUnitDef buImperialLenDef[]
Definition: unit.c:150
int BKE_unit_base_of_type_get(int system, int type)
Definition: unit.c:1269
static struct bUnitCollection buNaturalTimeCollection
Definition: unit.c:279
@ B_UNIT_DEF_SUPPRESS
Definition: unit.c:99
@ B_UNIT_DEF_NO_SPACE
Definition: unit.c:105
@ B_UNIT_DEF_CASE_SENSITIVE
Definition: unit.c:103
@ B_UNIT_DEF_TENTH
Definition: unit.c:101
@ B_UNIT_DEF_NONE
Definition: unit.c:97
#define UN_SC_ITON
Definition: unit.c:62
static const struct bUnitCollection * bUnitSystems[][B_UNIT_TYPE_TOT]
Definition: unit.c:338
static PreferredUnits preferred_units_from_UnitSettings(const UnitSettings *settings)
Definition: unit.c:539
static char * find_next_op(const char *str, char *remaining_str, int len_max)
Definition: unit.c:802
static struct bUnitDef buMetricAreaDef[]
Definition: unit.c:163
static struct bUnitCollection buCameraLenCollection
Definition: unit.c:304
static struct bUnitDef buCameraLenDef[]
Definition: unit.c:296
#define UN_SC_FUR
Definition: unit.c:47
static struct bUnitDef buMetricAclDef[]
Definition: unit.c:256
static struct bUnitCollection buImperialLenCollection
Definition: unit.c:160
#define UN_SC_FT
Definition: unit.c:50
#define UN_SC_DAM
Definition: unit.c:39
static struct bUnitDef buMetricVelDef[]
Definition: unit.c:241
static char * find_next_negative(const char *str, const char *remaining_str)
Definition: unit.c:776
#define UN_SC_CWT
Definition: unit.c:63
static struct bUnitCollection buImperialVelCollection
Definition: unit.c:253
#define UN_SC_DM
Definition: unit.c:41
#define UN_SC_MG
Definition: unit.c:60
bool BKE_unit_is_valid(int system, int type)
Definition: unit.c:1246
static struct bUnitCollection buMetricVolCollection
Definition: unit.c:200
static struct bUnitDef buMetricMassDef[]
Definition: unit.c:215
static const bUnitCollection * unit_get_system(int system, int type)
Definition: unit.c:384
static struct bUnitCollection buDummyCollection
Definition: unit.c:127
#define UN_SC_MTON
Definition: unit.c:54
#define UN_SC_HG
Definition: unit.c:57
size_t BKE_unit_value_as_string_adaptive(char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad)
Definition: unit.c:665
#define SEP_CHR
Definition: unit.c:32
static struct bUnitCollection buMetricAclCollection
Definition: unit.c:260
#define UN_SC_KM
Definition: unit.c:37
static struct bUnitCollection buPowerCollection
Definition: unit.c:317
static void unit_dual_convert(double value, const bUnitCollection *usys, bUnitDef const **r_unit_a, bUnitDef const **r_unit_b, double *r_value_a, double *r_value_b, const bUnitDef *main_unit)
Definition: unit.c:426
static bool is_valid_unit_collection(const bUnitCollection *usys)
Definition: unit.c:583
#define UN_SC_G
Definition: unit.c:59
#define UN_SC_IN
Definition: unit.c:51
const char * BKE_unit_display_name_get(const void *usys_pt, int index)
Definition: unit.c:1278
#define NULL_UNIT
Definition: unit.c:123
static const struct bUnitCollection buMetricLenCollection
Definition: unit.c:148
bool BKE_unit_is_suppressed(const void *usys_pt, int index)
Definition: unit.c:1296
#define UNIT_COLLECTION_LENGTH(def)
Definition: unit.c:122
void BKE_unit_system_get(int system, int type, void const **r_usys_pt, int *r_len)
Definition: unit.c:1251
#define UN_SC_LB
Definition: unit.c:65
#define UN_SC_MIL
Definition: unit.c:52
static struct bUnitCollection buImperialVolCollection
Definition: unit.c:212
#define TEMP_STR_SIZE
Definition: unit.c:30
static const bUnitDef * unit_best_fit(double value, const bUnitCollection *usys, const bUnitDef *unit_start, int suppress)
Definition: unit.c:396
#define EPS
Definition: unit.c:35
static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit)
Definition: unit.c:994
static struct bUnitDef buMetricTempDef[]
Definition: unit.c:320
void BKE_unit_name_to_alt(char *str, int len_max, const char *orig_str, int system, int type)
Definition: unit.c:1180
static struct bUnitCollection buMetricMassCollection
Definition: unit.c:225
#define UN_SC_MI
Definition: unit.c:46
static struct bUnitDef buNaturalTimeDef[]
Definition: unit.c:269
#define UN_SC_CH
Definition: unit.c:48
static struct bUnitDef buImperialAclDef[]
Definition: unit.c:262
static const bUnitDef * unit_detect_from_str(const bUnitCollection *usys, const char *str, const char *str_prev)
Definition: unit.c:1034
const char * BKE_unit_identifier_get(const void *usys_pt, int index)
Definition: unit.c:1282
static size_t unit_as_string_split_pair(char *str, int len_max, double value, int prec, const bUnitCollection *usys, const bUnitDef *main_unit)
Definition: unit.c:551
static bool unit_find(const char *str, const bUnitDef *unit)
Definition: unit.c:1009
int BKE_unit_base_get(const void *usys_pt)
Definition: unit.c:1264
static struct bUnitCollection buImperialAreaCollection
Definition: unit.c:186
static struct bUnitDef buImperialMassDef[]
Definition: unit.c:227
static size_t unit_as_string(char *str, int len_max, double value, int prec, const bUnitCollection *usys, const bUnitDef *unit, char pad)
Definition: unit.c:443
double BKE_unit_scalar_get(const void *usys_pt, int index)
Definition: unit.c:1291
#define UN_SC_YD
Definition: unit.c:49
static struct bUnitDef buPowerDef[]
Definition: unit.c:307
static struct bUnitDef buImperialTempDef[]
Definition: unit.c:327
static bool ch_is_op(char op)
Definition: unit.c:748
double BKE_unit_closest_scalar(double value, int system, int type)
Definition: unit.c:1220
static struct bUnitDef buImperialVolDef[]
Definition: unit.c:202
static struct bUnitCollection buImperialMassCollection
Definition: unit.c:235
#define UN_SC_ST
Definition: unit.c:64
static size_t unit_as_string_main(char *str, int len_max, double value, int prec, int type, bool split, bool pad, PreferredUnits units)
Definition: unit.c:635
static struct bUnitCollection buImperialTempCollection
Definition: unit.c:332
size_t BKE_unit_value_as_string(char *str, int len_max, double value, int prec, int type, const UnitSettings *settings, bool pad)
Definition: unit.c:678
static struct bUnitDef buMetricLenDef[]
Definition: unit.c:130
bool BKE_unit_replace_string(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type)
Definition: unit.c:1091
#define UN_SC_QL
Definition: unit.c:55
#define UN_SC_OZ
Definition: unit.c:66
#define UN_SC_DAG
Definition: unit.c:58
double BKE_unit_apply_preferred_unit(const struct UnitSettings *settings, int type, double value)
Definition: unit.c:1080
static int find_previous_non_value_char(const char *str, const int start_ofs)
Definition: unit.c:880
#define UN_SC_MM
Definition: unit.c:43
static struct bUnitCollection buImperialAclCollection
Definition: unit.c:266
double BKE_unit_base_scalar(int system, int type)
Definition: unit.c:1236
struct bUnitCollection bUnitCollection
#define UN_SC_CM
Definition: unit.c:42
static int unit_scale_str(char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit, const char *replace_str, bool case_sensitive)
Definition: unit.c:905
static struct bUnitDef buMetricVolDef[]
Definition: unit.c:189
static const bUnitDef * unit_default(const bUnitCollection *usys)
Definition: unit.c:391
static const char * unit_find_str(const char *str, const char *substr, bool case_sensitive)
Definition: unit.c:696
bool BKE_unit_string_contains_unit(const char *str, int type)
Definition: unit.c:1063
struct bUnitDef bUnitDef
static struct bUnitCollection buMetricAreaCollection
Definition: unit.c:174
static struct bUnitCollection buMetricVelCollection
Definition: unit.c:246
static struct bUnitDef buImperialVelDef[]
Definition: unit.c:248
const char * BKE_unit_name_get(const void *usys_pt, int index)
Definition: unit.c:1274
#define UN_SC_FAH
Definition: unit.c:68
static struct bUnitCollection buNaturalRotCollection
Definition: unit.c:293
static bool unit_distribute_negatives(char *str, const int len_max)
Definition: unit.c:843
#define UN_SC_UM
Definition: unit.c:44
static struct bUnitDef buImperialAreaDef[]
Definition: unit.c:176
#define UN_SC_KG
Definition: unit.c:56
static const bUnitDef * get_preferred_display_unit_if_used(int type, PreferredUnits units)
Definition: unit.c:588
static struct bUnitDef buDummyDef[]
Definition: unit.c:126
#define UN_SC_HM
Definition: unit.c:38
static struct bUnitDef buNaturalRotDef[]
Definition: unit.c:282
BLI_INLINE bool isalpha_or_utf8(const int ch)
Definition: unit.c:691
#define SEP_STR
Definition: unit.c:33
static bool unit_should_be_split(int type)
Definition: unit.c:524
#define UN_SC_M
Definition: unit.c:40
#define UNIT_SYSTEM_TOT
Definition: unit.c:337