Blender V2.61 - r43446

unit.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * Contributor(s): Campbell Barton
00019  *
00020  * ***** END GPL LICENSE BLOCK *****
00021  */
00022 
00027 #include <stdlib.h>
00028 #include <stdio.h>
00029 #include <ctype.h>
00030 #include <string.h>
00031 #include <assert.h>
00032 #include "BKE_unit.h"
00033 
00034 #include "BLI_math.h"
00035 #include "BLI_string.h"
00036 #include "BLI_winstuff.h"
00037 
00038 
00039 #define TEMP_STR_SIZE 256
00040 
00041 #define SEP_CHR     '#'
00042 #define SEP_STR     "#"
00043 
00044 #define EPS 0.00001
00045 
00046 #define UN_SC_KM    1000.0f
00047 #define UN_SC_HM    100.0f
00048 #define UN_SC_DAM   10.0f
00049 #define UN_SC_M     1.0f
00050 #define UN_SC_DM    0.1f
00051 #define UN_SC_CM    0.01f
00052 #define UN_SC_MM    0.001f
00053 #define UN_SC_UM    0.000001f
00054 
00055 #define UN_SC_MI    1609.344f
00056 #define UN_SC_FUR   201.168f
00057 #define UN_SC_CH    20.1168f
00058 #define UN_SC_YD    0.9144f
00059 #define UN_SC_FT    0.3048f
00060 #define UN_SC_IN    0.0254f
00061 #define UN_SC_MIL   0.0000254f
00062 
00063 #define UN_SC_MTON  1000.0f /* metric ton */
00064 #define UN_SC_QL    100.0f
00065 #define UN_SC_KG    1.0f
00066 #define UN_SC_HG    0.1f
00067 #define UN_SC_DAG   0.01f
00068 #define UN_SC_G     0.001f
00069 
00070 #define UN_SC_ITON  907.18474f /* imperial ton */
00071 #define UN_SC_CWT   45.359237f
00072 #define UN_SC_ST    6.35029318f
00073 #define UN_SC_LB    0.45359237f
00074 #define UN_SC_OZ    0.028349523125f
00075 
00076 /* define a single unit */
00077 typedef struct bUnitDef {
00078     const char *name;
00079     const char *name_plural;    /* abused a bit for the display name */
00080     const char *name_short; /* this is used for display*/
00081     const char *name_alt;       /* keyboard-friendly ASCII-only version of name_short, can be NULL */
00082                         /* if name_short has non-ASCII chars, name_alt should be present */
00083     
00084     const char *name_display;       /* can be NULL */
00085 
00086     double scalar;
00087     double bias;        /* not used yet, needed for converting temperature */
00088     int flag;
00089 } bUnitDef;
00090 
00091 #define B_UNIT_DEF_NONE 0
00092 #define B_UNIT_DEF_SUPPRESS 1 /* Use for units that are not used enough to be translated into for common use */
00093 
00094 /* define a single unit */
00095 typedef struct bUnitCollection {
00096     struct bUnitDef *units;
00097     int base_unit;              /* basic unit index (when user doesn't specify unit explicitly) */
00098     int flag;                   /* options for this system */
00099     int length;                 /* to quickly find the last item */
00100 } bUnitCollection;
00101 
00102 /* Dummy */
00103 static struct bUnitDef buDummyDef[] = {
00104     {"",    NULL, "",   NULL, NULL, 1.0, 0.0},
00105     {NULL,  NULL, NULL, NULL, NULL, 0.0, 0.0}
00106 };
00107 static struct bUnitCollection buDummyCollecton = {buDummyDef, 0, 0, sizeof(buDummyDef)};
00108 
00109 
00110 /* Lengths */
00111 static struct bUnitDef buMetricLenDef[] = {
00112     {"kilometer", "kilometers",     "km", NULL, "Kilometers", UN_SC_KM, 0.0,    B_UNIT_DEF_NONE},
00113     {"hectometer", "hectometers",   "hm", NULL, "100 Meters", UN_SC_HM, 0.0,    B_UNIT_DEF_SUPPRESS},
00114     {"dekameter", "dekameters",     "dam",NULL, "10 Meters",  UN_SC_DAM, 0.0,   B_UNIT_DEF_SUPPRESS},
00115     {"meter", "meters",             "m",  NULL, "Meters",     UN_SC_M, 0.0,     B_UNIT_DEF_NONE}, /* base unit */
00116     {"decimetre", "decimetres",     "dm", NULL, "10 Centimeters", UN_SC_DM, 0.0,    B_UNIT_DEF_SUPPRESS},
00117     {"centimeter", "centimeters",   "cm", NULL, "Centimeters", UN_SC_CM, 0.0,   B_UNIT_DEF_NONE},
00118     {"millimeter", "millimeters",   "mm", NULL, "Millimeters", UN_SC_MM, 0.0,   B_UNIT_DEF_NONE},
00119     {"micrometer", "micrometers",   "µm", "um",    "Micrometers", UN_SC_UM, 0.0,   B_UNIT_DEF_NONE}, // micron too?
00120 
00121     /* These get displayed because of float precision problems in the transform header,
00122      * could work around, but for now probably people wont use these */
00123     /*
00124     {"nanometer", "Nanometers",     "nm", NULL, 0.000000001, 0.0,   B_UNIT_DEF_NONE},
00125     {"picometer", "Picometers",     "pm", NULL, 0.000000000001, 0.0,B_UNIT_DEF_NONE},
00126     */
00127     {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
00128 };
00129 static struct bUnitCollection buMetricLenCollecton = {buMetricLenDef, 3, 0, sizeof(buMetricLenDef)/sizeof(bUnitDef)};
00130 
00131 static struct bUnitDef buImperialLenDef[] = {
00132     {"mile", "miles",       "mi", "m", "Miles",     UN_SC_MI, 0.0,  B_UNIT_DEF_NONE},
00133     {"furlong", "furlongs", "fur", NULL, "Furlongs",UN_SC_FUR, 0.0, B_UNIT_DEF_SUPPRESS},
00134     {"chain", "chains",     "ch", NULL, "Chains",   UN_SC_CH, 0.0,  B_UNIT_DEF_SUPPRESS},
00135     {"yard", "yards",       "yd", NULL, "Yards",    UN_SC_YD, 0.0,  B_UNIT_DEF_NONE},
00136     {"foot", "feet",        "'", "ft", "Feet",      UN_SC_FT, 0.0,  B_UNIT_DEF_NONE}, /* base unit */
00137     {"inch", "inches",      "\"", "in", "Inches",   UN_SC_IN, 0.0,  B_UNIT_DEF_NONE},
00138     {"thou", "thou",        "thou", "mil", "Thou",  UN_SC_MIL, 0.0, B_UNIT_DEF_NONE}, /* plural for thou has no 's' */
00139     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00140 };
00141 static struct bUnitCollection buImperialLenCollecton = {buImperialLenDef, 4, 0, sizeof(buImperialLenDef)/sizeof(bUnitDef)};
00142 
00143 /* Areas */
00144 static struct bUnitDef buMetricAreaDef[] = {
00145     {"square kilometer", "square kilometers",   "km²", "km2",  "Square Kilometers", UN_SC_KM*UN_SC_KM, 0.0,    B_UNIT_DEF_NONE},
00146     {"square hectometer","square hectometers",  "hm²", "hm2",  "Square Hectometers", UN_SC_HM*UN_SC_HM, 0.0,   B_UNIT_DEF_NONE}, /* hectare */
00147     {"square dekameter", "square dekameters",   "dam²","dam2", "Square Dekameters", UN_SC_DAM*UN_SC_DAM, 0.0,  B_UNIT_DEF_SUPPRESS}, /* are */
00148     {"square meter", "square meters",           "m²",  "m2",   "Square Meters", UN_SC_M*UN_SC_M, 0.0,          B_UNIT_DEF_NONE}, /* base unit */
00149     {"square decimetre", "square decimetres",   "dm²", "dm2",  "Square Decimetres", UN_SC_DM*UN_SC_DM, 0.0,    B_UNIT_DEF_SUPPRESS},
00150     {"square centimeter", "square centimeters", "cm²", "cm2",  "Square Centimeters", UN_SC_CM*UN_SC_CM, 0.0,   B_UNIT_DEF_NONE},
00151     {"square millimeter", "square millimeters", "mm²", "mm2",  "Square Millimeters", UN_SC_MM*UN_SC_MM, 0.0,   B_UNIT_DEF_NONE},
00152     {"square micrometer", "square micrometers", "µm²", "um2", "Square Micrometers", UN_SC_UM*UN_SC_UM, 0.0,   B_UNIT_DEF_NONE},
00153     {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
00154 };
00155 static struct bUnitCollection buMetricAreaCollecton = {buMetricAreaDef, 3, 0, sizeof(buMetricAreaDef)/sizeof(bUnitDef)};
00156 
00157 static struct bUnitDef buImperialAreaDef[] = {
00158     {"square mile", "square miles",         "sq mi", "sq m","Square Miles", UN_SC_MI*UN_SC_MI, 0.0,     B_UNIT_DEF_NONE},
00159     {"square furlong", "square furlongs",   "sq fur",NULL,  "Square Furlongs", UN_SC_FUR*UN_SC_FUR, 0.0,B_UNIT_DEF_SUPPRESS},
00160     {"square chain", "square chains",       "sq ch", NULL,  "Square Chains", UN_SC_CH*UN_SC_CH, 0.0,    B_UNIT_DEF_SUPPRESS},
00161     {"square yard", "square yards",         "sq yd", NULL,  "Square Yards", UN_SC_YD*UN_SC_YD, 0.0,     B_UNIT_DEF_NONE},
00162     {"square foot", "square feet",          "sq ft", NULL,  "Square Feet", UN_SC_FT*UN_SC_FT, 0.0,      B_UNIT_DEF_NONE}, /* base unit */
00163     {"square inch", "square inches",        "sq in", NULL,  "Square Inches", UN_SC_IN*UN_SC_IN, 0.0,    B_UNIT_DEF_NONE},
00164     {"square thou", "square thous",         "sq mil",NULL,  "Square Thous", UN_SC_MIL*UN_SC_MIL, 0.0,   B_UNIT_DEF_NONE},
00165     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00166 };
00167 static struct bUnitCollection buImperialAreaCollecton = {buImperialAreaDef, 4, 0, sizeof(buImperialAreaDef)/sizeof(bUnitDef)};
00168 
00169 /* Volumes */
00170 static struct bUnitDef buMetricVolDef[] = {
00171     {"cubic kilometer", "cubic kilometers",     "km³", "km3",  "Cubic Kilometers", UN_SC_KM*UN_SC_KM*UN_SC_KM, 0.0,    B_UNIT_DEF_NONE},
00172     {"cubic hectometer","cubic hectometers",    "hm³", "hm3",  "Cubic Hectometers", UN_SC_HM*UN_SC_HM*UN_SC_HM, 0.0,   B_UNIT_DEF_NONE},
00173     {"cubic dekameter", "cubic dekameters",     "dam³","dam3", "Cubic Dekameters", UN_SC_DAM*UN_SC_DAM*UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS},
00174     {"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 */
00175     {"cubic decimetre", "cubic decimetres",     "dm³", "dm3",  "Cubic Decimetres", UN_SC_DM*UN_SC_DM*UN_SC_DM, 0.0,    B_UNIT_DEF_SUPPRESS},
00176     {"cubic centimeter", "cubic centimeters",   "cm³", "cm3",  "Cubic Centimeters", UN_SC_CM*UN_SC_CM*UN_SC_CM, 0.0,   B_UNIT_DEF_NONE},
00177     {"cubic millimeter", "cubic millimeters",   "mm³", "mm3",  "Cubic Millimeters", UN_SC_MM*UN_SC_MM*UN_SC_MM, 0.0,   B_UNIT_DEF_NONE},
00178     {"cubic micrometer", "cubic micrometers",   "µm³", "um3", "Cubic Micrometers", UN_SC_UM*UN_SC_UM*UN_SC_UM, 0.0,   B_UNIT_DEF_NONE},
00179     {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
00180 };
00181 static struct bUnitCollection buMetricVolCollecton = {buMetricVolDef, 3, 0, sizeof(buMetricVolDef)/sizeof(bUnitDef)};
00182 
00183 static struct bUnitDef buImperialVolDef[] = {
00184     {"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},
00185     {"cubic furlong", "cubic furlongs", "cu fur",NULL,  "Cubic Furlongs", UN_SC_FUR*UN_SC_FUR*UN_SC_FUR, 0.0,B_UNIT_DEF_SUPPRESS},
00186     {"cubic chain", "cubic chains",     "cu ch", NULL,  "Cubic Chains", UN_SC_CH*UN_SC_CH*UN_SC_CH, 0.0,    B_UNIT_DEF_SUPPRESS},
00187     {"cubic yard", "cubic yards",       "cu yd", NULL,  "Cubic Yards", UN_SC_YD*UN_SC_YD*UN_SC_YD, 0.0,     B_UNIT_DEF_NONE},
00188     {"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 */
00189     {"cubic inch", "cubic inches",      "cu in", NULL,  "Cubic Inches", UN_SC_IN*UN_SC_IN*UN_SC_IN, 0.0,    B_UNIT_DEF_NONE},
00190     {"cubic thou", "cubic thous",       "cu mil",NULL,  "Cubic Thous", UN_SC_MIL*UN_SC_MIL*UN_SC_MIL, 0.0,  B_UNIT_DEF_NONE},
00191     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00192 };
00193 static struct bUnitCollection buImperialVolCollecton = {buImperialVolDef, 4, 0, sizeof(buImperialVolDef)/sizeof(bUnitDef)};
00194 
00195 /* Mass */
00196 static struct bUnitDef buMetricMassDef[] = {
00197     {"ton", "tonnes",           "ton", "t", "1000 Kilograms", UN_SC_MTON, 0.0,  B_UNIT_DEF_NONE},
00198     {"quintal", "quintals",     "ql", "q",  "100 Kilograms", UN_SC_QL, 0.0,     B_UNIT_DEF_NONE},
00199     {"kilogram", "kilograms",   "kg", NULL, "Kilograms", UN_SC_KG, 0.0,         B_UNIT_DEF_NONE}, /* base unit */
00200     {"hectogram", "hectograms", "hg", NULL, "Hectograms", UN_SC_HG, 0.0,        B_UNIT_DEF_NONE},
00201     {"dekagram", "dekagrams",   "dag",NULL, "10 Grams", UN_SC_DAG, 0.0,         B_UNIT_DEF_SUPPRESS},
00202     {"gram", "grams",           "g",  NULL, "Grams", UN_SC_G, 0.0,              B_UNIT_DEF_NONE},
00203     {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
00204 };
00205 static struct bUnitCollection buMetricMassCollecton = {buMetricMassDef, 2, 0, sizeof(buMetricMassDef)/sizeof(bUnitDef)};
00206 
00207 static struct bUnitDef buImperialMassDef[] = {
00208     {"ton", "tonnes",   "ton", "t", "Tonnes", UN_SC_ITON, 0.0,      B_UNIT_DEF_NONE},
00209     {"centum weight", "centum weights", "cwt", NULL, "Centum weights", UN_SC_CWT, 0.0, B_UNIT_DEF_NONE},
00210     {"stone", "stones", "st", NULL,     "Stones", UN_SC_ST, 0.0,    B_UNIT_DEF_NONE},
00211     {"pound", "pounds", "lb", NULL,     "Pounds", UN_SC_LB, 0.0,    B_UNIT_DEF_NONE}, /* base unit */
00212     {"ounce", "ounces", "oz", NULL,     "Ounces", UN_SC_OZ, 0.0,    B_UNIT_DEF_NONE},
00213     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00214 };
00215 static struct bUnitCollection buImperialMassCollecton = {buImperialMassDef, 3, 0, sizeof(buImperialMassDef)/sizeof(bUnitDef)};
00216 
00217 /* Even if user scales the system to a point where km^3 is used, velocity and
00218  * acceleration aren't scaled: that's why we have so few units for them */
00219 
00220 /* Velocity */
00221 static struct bUnitDef buMetricVelDef[] = {
00222     {"meter per second", "meters per second",       "m/s",  NULL,   "Meters per second", UN_SC_M, 0.0,            B_UNIT_DEF_NONE}, /* base unit */
00223     {"kilometer per hour", "kilometers per hour",   "km/h", NULL,   "Kilometers per hour", UN_SC_KM/3600.0f, 0.0, B_UNIT_DEF_SUPPRESS},
00224     {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
00225 };
00226 static struct bUnitCollection buMetricVelCollecton = {buMetricVelDef, 0, 0, sizeof(buMetricVelDef)/sizeof(bUnitDef)};
00227 
00228 static struct bUnitDef buImperialVelDef[] = {
00229     {"foot per second", "feet per second",  "ft/s", "fps",  "Feet per second", UN_SC_FT, 0.0,       B_UNIT_DEF_NONE}, /* base unit */
00230     {"mile per hour", "miles per hour",     "mph", NULL,    "Miles per hour", UN_SC_MI/3600.0f, 0.0,B_UNIT_DEF_SUPPRESS},
00231     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00232 };
00233 static struct bUnitCollection buImperialVelCollecton = {buImperialVelDef, 0, 0, sizeof(buImperialVelDef)/sizeof(bUnitDef)};
00234 
00235 /* Acceleration */
00236 static struct bUnitDef buMetricAclDef[] = {
00237     {"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 */
00238     {NULL, NULL, NULL,  NULL, NULL, 0.0, 0.0}
00239 };
00240 static struct bUnitCollection buMetricAclCollecton = {buMetricAclDef, 0, 0, sizeof(buMetricAclDef)/sizeof(bUnitDef)};
00241 
00242 static struct bUnitDef buImperialAclDef[] = {
00243     {"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 */
00244     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00245 };
00246 static struct bUnitCollection buImperialAclCollecton = {buImperialAclDef, 0, 0, sizeof(buImperialAclDef)/sizeof(bUnitDef)};
00247 
00248 /* Time */
00249 static struct bUnitDef buNaturalTimeDef[] = {
00250     /* weeks? - probably not needed for blender */
00251     {"day", "days",                 "d", NULL,  "Days",         90000.0, 0.0,   B_UNIT_DEF_NONE},
00252     {"hour", "hours",               "hr", "h",  "Hours",        3600.0, 0.0,    B_UNIT_DEF_NONE},
00253     {"minute", "minutes",           "min", "m", "Minutes",      60.0, 0.0,      B_UNIT_DEF_NONE},
00254     {"second", "seconds",           "sec", "s", "Seconds",      1.0, 0.0,       B_UNIT_DEF_NONE}, /* base unit */
00255     {"millisecond", "milliseconds", "ms", NULL, "Milliseconds", 0.001, 0.0  ,   B_UNIT_DEF_NONE},
00256     {"microsecond", "microseconds", "µs", "us",    "Microseconds", 0.000001, 0.0,  B_UNIT_DEF_NONE},
00257     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00258 };
00259 static struct bUnitCollection buNaturalTimeCollecton = {buNaturalTimeDef, 3, 0, sizeof(buNaturalTimeDef)/sizeof(bUnitDef)};
00260 
00261 
00262 static struct bUnitDef buNaturalRotDef[] = {
00263     {"degree", "degrees",           "°", NULL, "Degrees",      M_PI/180.0, 0.0,    B_UNIT_DEF_NONE},
00264 //  {"radian", "radians",           "r", NULL, "Radians",       1.0, 0.0,           B_UNIT_DEF_NONE},
00265 //  {"turn", "turns",               "t", NULL, "Turns",         1.0/(M_PI*2.0), 0.0,B_UNIT_DEF_NONE},
00266     {NULL, NULL, NULL, NULL, NULL, 0.0, 0.0}
00267 };
00268 static struct bUnitCollection buNaturalRotCollection = {buNaturalRotDef, 0, 0, sizeof(buNaturalRotDef)/sizeof(bUnitDef)};
00269 
00270 #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / 9) / sizeof(void *)) - 1)
00271 static struct bUnitCollection *bUnitSystems[][9] = {
00272     {NULL, NULL, NULL, NULL, NULL, &buNaturalRotCollection, &buNaturalTimeCollecton, NULL, NULL},
00273     {NULL, &buMetricLenCollecton, &buMetricAreaCollecton, &buMetricVolCollecton, &buMetricMassCollecton, &buNaturalRotCollection, &buNaturalTimeCollecton, &buMetricVelCollecton, &buMetricAclCollecton}, /* metric */
00274     {NULL, &buImperialLenCollecton, &buImperialAreaCollecton, &buImperialVolCollecton, &buImperialMassCollecton, &buNaturalRotCollection, &buNaturalTimeCollecton, &buImperialVelCollecton, &buImperialAclCollecton}, /* imperial */
00275     {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
00276 };
00277 
00278 
00279 
00280 /* internal, has some option not exposed */
00281 static bUnitCollection *unit_get_system(int system, int type)
00282 {
00283     assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) && (type < B_UNIT_TYPE_TOT));
00284     return bUnitSystems[system][type]; /* select system to use, metric/imperial/other? */
00285 }
00286 
00287 static bUnitDef *unit_default(bUnitCollection *usys)
00288 {
00289     return &usys->units[usys->base_unit];
00290 }
00291 
00292 static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *unit_start, int suppress)
00293 {
00294     bUnitDef *unit;
00295     double value_abs= value>0.0?value:-value;
00296 
00297     for(unit= unit_start ? unit_start:usys->units; unit->name; unit++) {
00298 
00299         if(suppress && (unit->flag & B_UNIT_DEF_SUPPRESS))
00300             continue;
00301 
00302         /* scale down scalar so 1cm doesnt convert to 10mm because of float error */
00303         if (value_abs >= unit->scalar*(1.0-EPS))
00304             return unit;
00305     }
00306 
00307     return unit_default(usys);
00308 }
00309 
00310 
00311 
00312 /* convert into 2 units and 2 values for "2ft, 3inch" syntax */
00313 static void unit_dual_convert(double value, bUnitCollection *usys,
00314         bUnitDef **unit_a, bUnitDef **unit_b, double *value_a, double *value_b)
00315 {
00316     bUnitDef *unit= unit_best_fit(value, usys, NULL, 1);
00317 
00318     *value_a=  (value < 0.0 ? ceil:floor)(value/unit->scalar) * unit->scalar;
00319     *value_b= value - (*value_a);
00320 
00321     *unit_a=    unit;
00322     *unit_b=    unit_best_fit(*value_b, usys, *unit_a, 1);
00323 }
00324 
00325 static int unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys,
00326         /* non exposed options */
00327         bUnitDef *unit, char pad)
00328 {
00329     double value_conv;
00330     int len, i;
00331     
00332     if(unit) {
00333         /* use unit without finding the best one */
00334     }
00335     else if(value == 0.0) {
00336         /* use the default units since there is no way to convert */
00337         unit= unit_default(usys);
00338     }
00339     else {
00340         unit= unit_best_fit(value, usys, NULL, 1);
00341     }
00342 
00343     value_conv= value/unit->scalar;
00344 
00345     /* Convert to a string */
00346     {
00347         len= BLI_snprintf(str, len_max, "%.*f", prec, value_conv);
00348 
00349         if(len >= len_max)
00350             len= len_max;
00351     }
00352     
00353     /* Add unit prefix and strip zeros */
00354 
00355     /* replace trailing zero's with spaces
00356      * so the number is less complicated but allignment in a button wont
00357      * jump about while dragging */
00358     i= len-1;
00359 
00360     while(i>0 && str[i]=='0') { /* 4.300 -> 4.3 */
00361         str[i--]= pad;
00362     }
00363 
00364     if(i>0 && str[i]=='.') { /* 10. -> 10 */
00365         str[i--]= pad;
00366     }
00367     
00368     /* Now add the suffix */
00369     if(i<len_max) {
00370         int j=0;
00371         i++;
00372         while(unit->name_short[j] && (i < len_max)) {
00373             str[i++]= unit->name_short[j++];
00374         }
00375 
00376         if(pad) {
00377             /* this loop only runs if so many zeros were removed that
00378              * the unit name only used padded chars,
00379              * In that case add padding for the name. */
00380 
00381             while(i<=len+j && (i < len_max)) {
00382                 str[i++]= pad;
00383             }
00384         }
00385     }
00386 
00387     /* terminate no matter whats done with padding above */
00388     if(i >= len_max)
00389         i= len_max-1;
00390 
00391     str[i] = '\0';
00392     return i;
00393 }
00394 
00395 
00396 /* Used for drawing number buttons, try keep fast */
00397 void bUnit_AsString(char *str, int len_max, double value, int prec, int system, int type, int split, int pad)
00398 {
00399     bUnitCollection *usys = unit_get_system(system, type);
00400 
00401     if(usys==NULL || usys->units[0].name==NULL)
00402         usys= &buDummyCollecton;
00403    
00404     /* split output makes sense only for length, mass and time */
00405     if(split && (type==B_UNIT_LENGTH || type==B_UNIT_MASS || type==B_UNIT_TIME)) {
00406         bUnitDef *unit_a, *unit_b;
00407         double value_a, value_b;
00408 
00409         unit_dual_convert(value, usys,      &unit_a, &unit_b, &value_a, &value_b);
00410 
00411         /* check the 2 is a smaller unit */
00412         if(unit_b > unit_a) {
00413             int i= unit_as_string(str, len_max, value_a, prec, usys,  unit_a, '\0');
00414 
00415             /* is there enough space for at least 1 char of the next unit? */
00416             if(i+2 < len_max) {
00417                 str[i++]= ' ';
00418 
00419                 /* use low precision since this is a smaller unit */
00420                 unit_as_string(str+i, len_max-i, value_b, prec?1:0, usys,  unit_b, '\0');
00421             }
00422             return;
00423         }
00424     }
00425 
00426     unit_as_string(str, len_max, value, prec, usys,    NULL, pad?' ':'\0');
00427 }
00428 
00429 
00430 static const char *unit_find_str(const char *str, const char *substr)
00431 {
00432     const char *str_found;
00433 
00434     if(substr && substr[0] != '\0') {
00435         str_found= strstr(str, substr);
00436         if(str_found) {
00437             /* previous char cannot be a letter */
00438             if (str_found == str || isalpha(*(str_found-1))==0) {
00439                 /* next char cannot be alphanum */
00440                 int len_name = strlen(substr);
00441 
00442                 if (!isalpha(*(str_found+len_name))) {
00443                     return str_found;
00444                 }
00445             }
00446         }
00447 
00448     }
00449     return NULL;
00450 
00451 }
00452 
00453 /* Note that numbers are added within brackets
00454  * ") " - is used to detect numbers we added so we can detect if commas need to be added
00455  *
00456  * "1m1cm+2mm"              - Original value
00457  * "1*1#1*0.01#+2*0.001#"   - Replace numbers
00458  * "1*1,1*0.01 +2*0.001 "   - Add comma's if ( - + * / % ^ < > ) not found in between
00459  *
00460  */
00461 
00462 /* not too strict, (- = * /) are most common  */
00463 static int ch_is_op(char op)
00464 {
00465     switch(op) {
00466     case '+':
00467     case '-':
00468     case '*':
00469     case '/':
00470     case '|':
00471     case '&':
00472     case '~':
00473     case '<':
00474     case '>':
00475     case '^':
00476     case '!':
00477     case '=':
00478     case '%':
00479         return 1;
00480     default:
00481         return 0;
00482     }
00483 }
00484 
00485 static int unit_scale_str(char *str, int len_max, char *str_tmp,
00486                           double scale_pref, bUnitDef *unit, const char *replace_str)
00487 {
00488     char *str_found;
00489 
00490     if((len_max>0) && (str_found= (char *)unit_find_str(str, replace_str))) {
00491         /* XXX - investigate, does not respect len_max properly  */
00492 
00493         int len, len_num, len_name, len_move, found_ofs;
00494 
00495         found_ofs = (int)(str_found-str);
00496 
00497         len= strlen(str);
00498 
00499         len_name = strlen(replace_str);
00500         len_move= (len - (found_ofs+len_name)) + 1; /* 1+ to copy the string terminator */
00501         len_num= BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%g"SEP_STR, unit->scalar/scale_pref); /* # removed later */
00502 
00503         if(len_num > len_max)
00504             len_num= len_max;
00505 
00506         if(found_ofs+len_num+len_move > len_max) {
00507             /* can't move the whole string, move just as much as will fit */
00508             len_move -= (found_ofs+len_num+len_move) - len_max;
00509         }
00510 
00511         if(len_move>0) {
00512             /* resize the last part of the string */
00513             memmove(str_found+len_num, str_found+len_name, len_move); /* may grow or shrink the string */
00514         }
00515 
00516         if(found_ofs+len_num > len_max) {
00517             /* not even the number will fit into the string, only copy part of it */
00518             len_num -= (found_ofs+len_num) - len_max;
00519         }
00520 
00521         if(len_num > 0) {
00522             /* its possible none of the number could be copied in */
00523             memcpy(str_found, str_tmp, len_num); /* without the string terminator */
00524         }
00525 
00526         /* since the null terminator wont be moved if the stringlen_max
00527          * was not long enough to fit everything in it */
00528         str[len_max-1]= '\0';
00529         return found_ofs + len_num;
00530     }
00531     return 0;
00532 }
00533 
00534 static int unit_replace(char *str, int len_max, char *str_tmp, double scale_pref, bUnitDef *unit)
00535 {   
00536     int ofs= 0;
00537     ofs += unit_scale_str(str+ofs, len_max-ofs, str_tmp, scale_pref, unit, unit->name_short);
00538     ofs += unit_scale_str(str+ofs, len_max-ofs, str_tmp, scale_pref, unit, unit->name_plural);
00539     ofs += unit_scale_str(str+ofs, len_max-ofs, str_tmp, scale_pref, unit, unit->name_alt);
00540     ofs += unit_scale_str(str+ofs, len_max-ofs, str_tmp, scale_pref, unit, unit->name);
00541     return ofs;
00542 }
00543 
00544 static int unit_find(const char *str, bUnitDef *unit)
00545 {
00546     if (unit_find_str(str, unit->name_short))   return 1;
00547     if (unit_find_str(str, unit->name_plural))  return 1;
00548     if (unit_find_str(str, unit->name_alt))     return 1;
00549     if (unit_find_str(str, unit->name))         return 1;
00550 
00551     return 0;
00552 }
00553 
00554 /* make a copy of the string that replaces the units with numbers
00555  * this is used before parsing
00556  * This is only used when evaluating user input and can afford to be a bit slower
00557  * 
00558  * This is to be used before python evaluation so..
00559  * 10.1km -> 10.1*1000.0
00560  * ...will be resolved by python.
00561  * 
00562  * values will be split by a comma's
00563  * 5'2" -> 5'0.0254, 2*0.3048
00564  *
00565  * str_prev is optional, when valid it is used to get a base unit when none is set.
00566  *
00567  * return true of a change was made.
00568  */
00569 int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type)
00570 {
00571     bUnitCollection *usys = unit_get_system(system, type);
00572 
00573     bUnitDef *unit;
00574     char str_tmp[TEMP_STR_SIZE];
00575     int change= 0;
00576 
00577     if(usys==NULL || usys->units[0].name==NULL) {
00578         return 0;
00579     }
00580     
00581 
00582     {   /* make lowercase */
00583         int i;
00584         char *ch= str;
00585 
00586         for(i=0; (i>=len_max || *ch=='\0'); i++, ch++)
00587             if((*ch>='A') && (*ch<='Z'))
00588                 *ch += ('a'-'A');
00589     }
00590 
00591 
00592     for(unit= usys->units; unit->name; unit++) {
00593         /* incase there are multiple instances */
00594         while(unit_replace(str, len_max, str_tmp, scale_pref, unit))
00595             change= 1;
00596     }
00597     unit= NULL;
00598 
00599     {
00600         /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */
00601         bUnitCollection *usys_iter;
00602         int system_iter;
00603 
00604         for(system_iter= 0; system_iter<UNIT_SYSTEM_TOT; system_iter++) {
00605             if (system_iter != system) {
00606                 usys_iter= unit_get_system(system_iter, type);
00607                 if (usys_iter) {
00608                     for(unit= usys_iter->units; unit->name; unit++) {
00609                         int ofs = 0;
00610                         /* incase there are multiple instances */
00611                         while((ofs=unit_replace(str+ofs, len_max-ofs, str_tmp, scale_pref, unit)))
00612                             change= 1;
00613                     }
00614                 }
00615             }
00616         }
00617     }
00618     unit= NULL;
00619     
00620     if(change==0) {
00621         /* no units given so infer a unit from the previous string or default */
00622         if(str_prev) {
00623             /* see which units the original value had */
00624             for(unit= usys->units; unit->name; unit++) {
00625                 if (unit_find(str_prev, unit))
00626                     break;
00627             }
00628         }
00629 
00630         if(unit==NULL || unit->name == NULL)
00631             unit= unit_default(usys);
00632 
00633 
00634         /* add the unit prefix and re-run, use brackets incase there was an expression given */
00635         if(BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)%s", str, unit->name) < sizeof(str_tmp)) {
00636             strncpy(str, str_tmp, len_max);
00637             return bUnit_ReplaceString(str, len_max, NULL, scale_pref, system, type);
00638         }
00639         else {
00640             /* BLI_snprintf would not fit into str_tmp, cant do much in this case
00641              * check for this because otherwise bUnit_ReplaceString could call its self forever */
00642             return 0;
00643         }
00644 
00645     }
00646 
00647     /* replace # with commas when there is no operator between it and the next number
00648      *
00649      * "1*1# 3*100# * 3"  ->  "1 *1, 3 *100  * 3"
00650      *
00651      * */
00652     {
00653         char *str_found= str;
00654         char *ch= str;
00655 
00656         while((str_found= strchr(str_found, SEP_CHR))) {
00657 
00658             int op_found= 0;
00659             /* any operators after this?*/
00660             for(ch= str_found+1; *ch!='\0'; ch++) {
00661 
00662                 if(*ch==' ' || *ch=='\t') {
00663                     /* do nothing */
00664                 }
00665                 else if (ch_is_op(*ch) || *ch==',') { /* found an op, no need to insert a ,*/
00666                     op_found= 1;
00667                     break;
00668                 }
00669                 else { /* found a non-op character */
00670                     op_found= 0;
00671                     break;
00672                 }
00673             }
00674 
00675             *str_found++ = op_found ? ' ':',';
00676         }
00677     }
00678 
00679     return change;
00680 }
00681 
00682 /* 45µm --> 45um */
00683 void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type)
00684 {
00685     bUnitCollection *usys = unit_get_system(system, type);
00686 
00687     bUnitDef *unit;
00688     bUnitDef *unit_def= unit_default(usys);
00689 
00690     /* find and substitute all units */
00691     for(unit= usys->units; unit->name; unit++) {
00692         if(len_max > 0 && (unit->name_alt || unit == unit_def))
00693         {
00694             const char *found= unit_find_str(orig_str, unit->name_short);
00695             if(found) {
00696                 int offset= (int)(found - orig_str);
00697                 int len_name= 0;
00698 
00699                 /* copy everything before the unit */
00700                 offset= (offset<len_max? offset: len_max);
00701                 strncpy(str, orig_str, offset);
00702 
00703                 str+= offset;
00704                 orig_str+= offset + strlen(unit->name_short);
00705                 len_max-= offset;
00706 
00707                 /* print the alt_name */
00708                 if(unit->name_alt)
00709                     len_name= BLI_snprintf(str, len_max, "%s", unit->name_alt);
00710                 else
00711                     len_name= 0;
00712 
00713                 len_name= (len_name<len_max? len_name: len_max);
00714                 str+= len_name;
00715                 len_max-= len_name;
00716             }
00717         }
00718     }
00719 
00720     /* finally copy the rest of the string */
00721     strncpy(str, orig_str, len_max);
00722 }
00723 
00724 double bUnit_ClosestScalar(double value, int system, int type)
00725 {
00726     bUnitCollection *usys = unit_get_system(system, type);
00727     bUnitDef *unit;
00728 
00729     if(usys==NULL)
00730         return -1;
00731 
00732     unit= unit_best_fit(value, usys, NULL, 1);
00733     if(unit==NULL)
00734         return -1;
00735 
00736     return unit->scalar;
00737 }
00738 
00739 double bUnit_BaseScalar(int system, int type)
00740 {
00741     bUnitCollection *usys = unit_get_system(system, type);
00742     return unit_default(usys)->scalar;
00743 }
00744 
00745 /* external access */
00746 int bUnit_IsValid(int system, int type)
00747 {
00748     return !(system < 0 || system > UNIT_SYSTEM_TOT || type < 0 || type > B_UNIT_TYPE_TOT);
00749 }
00750 
00751 
00752 void bUnit_GetSystem(void **usys_pt, int *len, int system, int type)
00753 {
00754     bUnitCollection *usys = unit_get_system(system, type);
00755     *usys_pt= usys;
00756 
00757     if(usys==NULL) {
00758         *len= 0;
00759         return;
00760     }
00761 
00762     *len= usys->length;
00763 }
00764 
00765 int bUnit_GetBaseUnit(void *usys_pt)
00766 {
00767     return ((bUnitCollection *)usys_pt)->base_unit;
00768 }
00769 
00770 const char *bUnit_GetName(void *usys_pt, int index)
00771 {
00772     return ((bUnitCollection *)usys_pt)->units[index].name;
00773 }
00774 const char *bUnit_GetNameDisplay(void *usys_pt, int index)
00775 {
00776     return ((bUnitCollection *)usys_pt)->units[index].name_display;
00777 }
00778 
00779 double bUnit_GetScaler(void *usys_pt, int index)
00780 {
00781     return ((bUnitCollection *)usys_pt)->units[index].scalar;
00782 }