Changeset View
Standalone View
source/blender/blenkernel/intern/unit.c
| Show First 20 Lines • Show All 74 Lines • ▼ Show 20 Lines | |||||
| #define UN_SC_MG 0.000001f | #define UN_SC_MG 0.000001f | ||||
| #define UN_SC_ITON 907.18474f /* Imperial ton. */ | #define UN_SC_ITON 907.18474f /* Imperial ton. */ | ||||
| #define UN_SC_CWT 45.359237f | #define UN_SC_CWT 45.359237f | ||||
| #define UN_SC_ST 6.35029318f | #define UN_SC_ST 6.35029318f | ||||
| #define UN_SC_LB 0.45359237f | #define UN_SC_LB 0.45359237f | ||||
| #define UN_SC_OZ 0.028349523125f | #define UN_SC_OZ 0.028349523125f | ||||
| #define UN_SC_FAH 0.555555555555f | |||||
JacquesLucke: You can add a couple more 5s here. | |||||
| /* clang-format on */ | /* clang-format on */ | ||||
| /* Define a single unit. */ | /* Define a single unit. */ | ||||
| typedef struct bUnitDef { | typedef struct bUnitDef { | ||||
| const char *name; | const char *name; | ||||
| /** Abused a bit for the display name. */ | /** Abused a bit for the display name. */ | ||||
| const char *name_plural; | const char *name_plural; | ||||
| /** This is used for display. */ | /** This is used for display. */ | ||||
| ▲ Show 20 Lines • Show All 83 Lines • ▼ Show 20 Lines | |||||
| /* Areas. */ | /* Areas. */ | ||||
| static struct bUnitDef buMetricAreaDef[] = { | static struct bUnitDef buMetricAreaDef[] = { | ||||
| {"square kilometer", "square kilometers", "km²", "km2", "Square Kilometers", NULL, UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, | {"square kilometer", "square kilometers", "km²", "km2", "Square Kilometers", NULL, UN_SC_KM * UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, | ||||
| {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* Hectare. */ | {"square hectometer", "square hectometers", "hm²", "hm2", "Square Hectometers", NULL, UN_SC_HM * UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, /* Hectare. */ | ||||
| {"square dekameter", "square dekameters", "dam²", "dam2", "Square Dekameters", NULL, UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, /* are */ | {"square dekameter", "square dekameters", "dam²", "dam2", "Square Dekameters", NULL, UN_SC_DAM * UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, /* are */ | ||||
| {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ | {"square meter", "square meters", "m²", "m2", "Square Meters", NULL, UN_SC_M * UN_SC_M, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ | ||||
| {"square decimeter", "square decimetees", "dm²", "dm2", "Square Decimeters", NULL, UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS}, | {"square decimeter", "square decimetees", "dm²", "dm2", "Square Decimeters", NULL, UN_SC_DM * UN_SC_DM, 0.0, B_UNIT_DEF_SUPPRESS}, | ||||
| {"square centimeter", "square centimeters", "cm²", "cm2", "Square Centimeters", NULL, UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE}, | {"square centimeter", "square centimeters", "cm²", "cm2", "Square Centimeters", NULL, UN_SC_CM * UN_SC_CM, 0.0, B_UNIT_DEF_NONE}, | ||||
Done Inline ActionsThanks for those typo fixes. I'll commit those separately. JacquesLucke: Thanks for those typo fixes. I'll commit those separately. | |||||
| {"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}, | {"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}, | ||||
| {"square micrometer", "square micrometers", "µm²", "um2", "Square Micrometers", NULL, UN_SC_UM * UN_SC_UM, 0.0, B_UNIT_DEF_NONE}, | {"square micrometer", "square micrometers", "µm²", "um2", "Square Micrometers", NULL, UN_SC_UM * UN_SC_UM, 0.0, B_UNIT_DEF_NONE}, | ||||
| NULL_UNIT, | NULL_UNIT, | ||||
| }; | }; | ||||
| static struct bUnitCollection buMetricAreaCollection = {buMetricAreaDef, 3, 0, UNIT_COLLECTION_LENGTH(buMetricAreaDef)}; | static struct bUnitCollection buMetricAreaCollection = {buMetricAreaDef, 3, 0, UNIT_COLLECTION_LENGTH(buMetricAreaDef)}; | ||||
| static struct bUnitDef buImperialAreaDef[] = { | static struct bUnitDef buImperialAreaDef[] = { | ||||
| {"square mile", "square miles", "sq mi", "sq m", "Square Miles", NULL, UN_SC_MI * UN_SC_MI, 0.0, B_UNIT_DEF_NONE}, | {"square mile", "square miles", "sq mi", "sq m", "Square Miles", NULL, UN_SC_MI * UN_SC_MI, 0.0, B_UNIT_DEF_NONE}, | ||||
| ▲ Show 20 Lines • Show All 119 Lines • ▼ Show 20 Lines | static struct bUnitDef buCameraLenDef[] = { | ||||
| {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ | {"meter", "meters", "m", NULL, "Meters", NULL, UN_SC_KM, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ | ||||
| {"decimeter", "decimeters", "dm", NULL, "10 Centimeters", NULL, UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, | {"decimeter", "decimeters", "dm", NULL, "10 Centimeters", NULL, UN_SC_HM, 0.0, B_UNIT_DEF_SUPPRESS}, | ||||
| {"centimeter", "centimeters", "cm", NULL, "Centimeters", NULL, UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, | {"centimeter", "centimeters", "cm", NULL, "Centimeters", NULL, UN_SC_DAM, 0.0, B_UNIT_DEF_SUPPRESS}, | ||||
| {"millimeter", "millimeters", "mm", NULL, "Millimeters", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, | {"millimeter", "millimeters", "mm", NULL, "Millimeters", NULL, UN_SC_M, 0.0, B_UNIT_DEF_NONE}, | ||||
| {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS}, | {"micrometer", "micrometers", "µm", "um", "Micrometers", NULL, UN_SC_MM, 0.0, B_UNIT_DEF_SUPPRESS}, | ||||
| NULL_UNIT, | NULL_UNIT, | ||||
| }; | }; | ||||
| static struct bUnitCollection buCameraLenCollection = {buCameraLenDef, 3, 0, UNIT_COLLECTION_LENGTH(buCameraLenDef)}; | static struct bUnitCollection buCameraLenCollection = {buCameraLenDef, 3, 0, UNIT_COLLECTION_LENGTH(buCameraLenDef)}; | ||||
Done Inline ActionsI wonder if we should use oC or just C. The latter is more comfortable to type. JacquesLucke: I wonder if we should use `oC` or just `C`. The latter is more comfortable to type. | |||||
| /* (Light) Power. */ | /* (Light) Power. */ | ||||
| static struct bUnitDef buPowerDef[] = { | static struct bUnitDef buPowerDef[] = { | ||||
| {"gigawatt", "gigawatts", "GW", NULL, "Gigawatts", NULL, 1e9f, 0.0, B_UNIT_DEF_NONE}, | {"gigawatt", "gigawatts", "GW", NULL, "Gigawatts", NULL, 1e9f, 0.0, B_UNIT_DEF_NONE}, | ||||
| {"megawatt", "megawatts", "MW", NULL, "Megawatts", NULL, 1e6f, 0.0, B_UNIT_DEF_CASE_SENSITIVE}, | {"megawatt", "megawatts", "MW", NULL, "Megawatts", NULL, 1e6f, 0.0, B_UNIT_DEF_CASE_SENSITIVE}, | ||||
| {"kilowatt", "kilowatts", "kW", NULL, "Kilowatts", NULL, 1e3f, 0.0, B_UNIT_DEF_SUPPRESS}, | {"kilowatt", "kilowatts", "kW", NULL, "Kilowatts", NULL, 1e3f, 0.0, B_UNIT_DEF_SUPPRESS}, | ||||
| {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ | {"watt", "watts", "W", NULL, "Watts", NULL, 1.0f, 0.0, B_UNIT_DEF_NONE}, /* Base unit. */ | ||||
Done Inline ActionsUnfortunately, the conversion is not that simple. Currently switching between the unit systems changes the absolute value of the temperature, i.e. it thinks 5C = 5F. A bit more work has to be done to get this conversion working, because it is different from all the other conversions we have so far. Also there is a missing white space. JacquesLucke: Unfortunately, the conversion is not that simple. Currently switching between the unit systems… | |||||
| {"milliwatt", "milliwatts", "mW", NULL, "Milliwatts", NULL, 1e-3f, 0.0, B_UNIT_DEF_CASE_SENSITIVE}, | {"milliwatt", "milliwatts", "mW", NULL, "Milliwatts", NULL, 1e-3f, 0.0, B_UNIT_DEF_CASE_SENSITIVE}, | ||||
| {"microwatt", "microwatts", "µW", "uW", "Microwatts", NULL, 1e-6f, 0.0, B_UNIT_DEF_NONE}, | {"microwatt", "microwatts", "µW", "uW", "Microwatts", NULL, 1e-6f, 0.0, B_UNIT_DEF_NONE}, | ||||
| {"nanowatt", "nanowatts", "nW", NULL, "Nanowatts", NULL, 1e-9f, 0.0, B_UNIT_DEF_NONE}, | {"nanowatt", "nanowatts", "nW", NULL, "Nanowatts", NULL, 1e-9f, 0.0, B_UNIT_DEF_NONE}, | ||||
| NULL_UNIT, | NULL_UNIT, | ||||
| }; | }; | ||||
| static struct bUnitCollection buPowerCollection = {buPowerDef, 3, 0, UNIT_COLLECTION_LENGTH(buPowerDef)}; | static struct bUnitCollection buPowerCollection = {buPowerDef, 3, 0, UNIT_COLLECTION_LENGTH(buPowerDef)}; | ||||
| /* Temperature */ | |||||
Done Inline ActionsIt's probably a good time to split this into multiple lines now, one line per unit collection. JacquesLucke: It's probably a good time to split this into multiple lines now, one line per unit collection. | |||||
| static struct bUnitDef buMetricTempDef[] = { | |||||
| {"kelvin", "kelvin", "K", NULL, "Kelvin", "KELVIN", 1.0f, 0.0, B_UNIT_DEF_NONE}, /* base unit */ | |||||
| {"celsius", "celsius", "°C", "C", "Celsius", "CELCIUS", 1.0f, 273.15, B_UNIT_DEF_NONE}, | |||||
| NULL_UNIT, | |||||
| }; | |||||
| static struct bUnitCollection buMetricTempCollection = {buMetricTempDef, 0, 0, UNIT_COLLECTION_LENGTH(buMetricTempDef)}; | |||||
| static struct bUnitDef buImperialTempDef[] = { | |||||
| {"fahrenheit", "fahrenheit", "°F", "F", "Fahrenheit", "FAHRENHEIT", UN_SC_FAH, 32.0, B_UNIT_DEF_NONE}, /* base unit */ | |||||
brechtUnsubmitted Done Inline ActionsI think Kelvin should also be the base unit in imperial? Something like color temperature you would still want to see as Kelvin even if you use imperial length units. brecht: I think Kelvin should also be the base unit in imperial? Something like color temperature you… | |||||
HooglyBooglyAuthorUnsubmitted Done Inline ActionsI was thinking about that too, I'll add that. HooglyBoogly: I was thinking about that too, I'll add that. | |||||
| NULL_UNIT, | |||||
| }; | |||||
| static struct bUnitCollection buImperialTempCollection = { | |||||
| buImperialTempDef, 1, 0, UNIT_COLLECTION_LENGTH(buImperialTempDef)}; | |||||
| /* clang-format on */ | /* clang-format on */ | ||||
| #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1) | #define UNIT_SYSTEM_TOT (((sizeof(bUnitSystems) / B_UNIT_TYPE_TOT) / sizeof(void *)) - 1) | ||||
| static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { | static const struct bUnitCollection *bUnitSystems[][B_UNIT_TYPE_TOT] = { | ||||
| /* Natural. */ | |||||
| {NULL, | {NULL, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| &buNaturalRotCollection, | &buNaturalRotCollection, | ||||
| &buNaturalTimeCollection, | &buNaturalTimeCollection, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| NULL, | NULL, | ||||
| NULL, | |||||
| NULL}, | NULL}, | ||||
| /* Metric. */ | |||||
| {NULL, | {NULL, | ||||
| &buMetricLenCollection, | &buMetricLenCollection, | ||||
| &buMetricAreaCollection, | &buMetricAreaCollection, | ||||
| &buMetricVolCollection, | &buMetricVolCollection, | ||||
| &buMetricMassCollection, | &buMetricMassCollection, | ||||
| &buNaturalRotCollection, | &buNaturalRotCollection, | ||||
| &buNaturalTimeCollection, | &buNaturalTimeCollection, | ||||
| &buMetricVelCollection, | &buMetricVelCollection, | ||||
| &buMetricAclCollection, | &buMetricAclCollection, | ||||
| &buCameraLenCollection, | &buCameraLenCollection, | ||||
| &buPowerCollection}, | &buPowerCollection, | ||||
| /* Imperial. */ | &buMetricTempCollection}, /* metric */ | ||||
| {NULL, | {NULL, | ||||
| &buImperialLenCollection, | &buImperialLenCollection, | ||||
| &buImperialAreaCollection, | &buImperialAreaCollection, | ||||
| &buImperialVolCollection, | &buImperialVolCollection, | ||||
| &buImperialMassCollection, | &buImperialMassCollection, | ||||
| &buNaturalRotCollection, | &buNaturalRotCollection, | ||||
| &buNaturalTimeCollection, | &buNaturalTimeCollection, | ||||
| &buImperialVelCollection, | &buImperialVelCollection, | ||||
| &buImperialAclCollection, | &buImperialAclCollection, | ||||
| &buCameraLenCollection, | &buCameraLenCollection, | ||||
| &buPowerCollection}, | &buPowerCollection, | ||||
| &buImperialTempCollection}, /* imperial */ | |||||
| {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, | ||||
| }; | }; | ||||
| /* internal, has some option not exposed */ | /* internal, has some option not exposed */ | ||||
| static const bUnitCollection *unit_get_system(int system, int type) | static const bUnitCollection *unit_get_system(int system, int type) | ||||
| { | { | ||||
| assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) && (type < B_UNIT_TYPE_TOT)); | assert((system > -1) && (system < UNIT_SYSTEM_TOT) && (type > -1) && (type < B_UNIT_TYPE_TOT)); | ||||
| return bUnitSystems[system][type]; /* select system to use, metric/imperial/other? */ | return bUnitSystems[system][type]; /* select system to use, metric/imperial/other? */ | ||||
| } | } | ||||
| static const bUnitDef *unit_default(const bUnitCollection *usys) | static const bUnitDef *unit_default(const bUnitCollection *usys) | ||||
| { | { | ||||
| return &usys->units[usys->base_unit]; | return &usys->units[usys->base_unit]; | ||||
| } | } | ||||
| static const bUnitDef *unit_best_fit(double value, | static const bUnitDef *unit_best_fit(double value, | ||||
| const bUnitCollection *usys, | const bUnitCollection *usys, | ||||
| const bUnitDef *unit_start, | const bUnitDef *unit_start, | ||||
| int suppress) | int suppress) | ||||
| { | { | ||||
| const bUnitDef *unit; | |||||
| double value_abs = value > 0.0 ? value : -value; | double value_abs = value > 0.0 ? value : -value; | ||||
| for (unit = unit_start ? unit_start : usys->units; unit->name; unit++) { | for (const bUnitDef *unit = unit_start ? unit_start : usys->units; unit->name; unit++) { | ||||
| if (suppress && (unit->flag & B_UNIT_DEF_SUPPRESS)) { | if (suppress && (unit->flag & B_UNIT_DEF_SUPPRESS)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* Scale down scalar so 1cm doesn't convert to 10mm because of float error. */ | /* Scale down scalar so 1cm doesn't convert to 10mm because of float error. */ | ||||
| if (UNLIKELY(unit->flag & B_UNIT_DEF_TENTH)) { | if (UNLIKELY(unit->flag & B_UNIT_DEF_TENTH)) { | ||||
| if (value_abs >= unit->scalar * (0.1 - EPS)) { | if (value_abs >= unit->scalar * (0.1 - EPS)) { | ||||
| Show All 38 Lines | static size_t unit_as_string(char *str, | ||||
| int len_max, | int len_max, | ||||
| double value, | double value, | ||||
| int prec, | int prec, | ||||
| const bUnitCollection *usys, | const bUnitCollection *usys, | ||||
| /* Non exposed options. */ | /* Non exposed options. */ | ||||
| const bUnitDef *unit, | const bUnitDef *unit, | ||||
| char pad) | char pad) | ||||
| { | { | ||||
| double value_conv; | |||||
| size_t len, i; | |||||
| if (unit) { | if (unit) { | ||||
| /* Use unit without finding the best one. */ | /* Use unit without finding the best one. */ | ||||
| } | } | ||||
| else if (value == 0.0) { | else if (value == 0.0) { | ||||
| /* Use the default units since there is no way to convert. */ | /* Use the default units since there is no way to convert. */ | ||||
| unit = unit_default(usys); | unit = unit_default(usys); | ||||
| } | } | ||||
| else { | else { | ||||
| unit = unit_best_fit(value, usys, NULL, 1); | unit = unit_best_fit(value, usys, NULL, 1); | ||||
| } | } | ||||
| value_conv = value / unit->scalar; | double value_conv = (value / unit->scalar) - unit->bias; | ||||
| /* Adjust precision to expected number of significant digits. | /* Adjust precision to expected number of significant digits. | ||||
| * Note that here, we shall not have to worry about very big/small numbers, units are expected | * Note that here, we shall not have to worry about very big/small numbers, units are expected | ||||
| * to replace 'scientific notation' in those cases. */ | * to replace 'scientific notation' in those cases. */ | ||||
| prec -= integer_digits_d(value_conv); | prec -= integer_digits_d(value_conv); | ||||
| CLAMP(prec, 0, 6); | CLAMP(prec, 0, 6); | ||||
| /* Convert to a string. */ | /* Convert to a string. */ | ||||
| len = BLI_snprintf_rlen(str, len_max, "%.*f", prec, value_conv); | size_t len = BLI_snprintf_rlen(str, len_max, "%.*f", prec, value_conv); | ||||
| /* Add unit prefix and strip zeros. */ | /* Add unit prefix and strip zeros. */ | ||||
| /* Replace trailing zero's with spaces so the number | /* Replace trailing zero's with spaces so the number | ||||
| * is less complicated but alignment in a button won't | * is less complicated but alignment in a button won't | ||||
| * jump about while dragging. */ | * jump about while dragging. */ | ||||
| i = len - 1; | size_t i = len - 1; | ||||
| if (prec > 0) { | if (prec > 0) { | ||||
| while (i > 0 && str[i] == '0') { /* 4.300 -> 4.3 */ | while (i > 0 && str[i] == '0') { /* 4.300 -> 4.3 */ | ||||
| str[i--] = pad; | str[i--] = pad; | ||||
| } | } | ||||
| if (i > 0 && str[i] == '.') { /* 10. -> 10 */ | if (i > 0 && str[i] == '.') { /* 10. -> 10 */ | ||||
| str[i--] = pad; | str[i--] = pad; | ||||
| Show All 30 Lines | |||||
| typedef struct { | typedef struct { | ||||
| int system; | int system; | ||||
| int rotation; | int rotation; | ||||
| /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection. */ | /* USER_UNIT_ADAPTIVE means none, otherwise the value is the index in the collection. */ | ||||
| int length; | int length; | ||||
| int mass; | int mass; | ||||
| int time; | int time; | ||||
| int temperature; | |||||
| } PreferredUnits; | } PreferredUnits; | ||||
| static PreferredUnits preferred_units_from_UnitSettings(const UnitSettings *settings) | static PreferredUnits preferred_units_from_UnitSettings(const UnitSettings *settings) | ||||
| { | { | ||||
| PreferredUnits units = {0}; | PreferredUnits units = {0}; | ||||
| units.system = settings->system; | units.system = settings->system; | ||||
| units.rotation = settings->system_rotation; | units.rotation = settings->system_rotation; | ||||
| units.length = settings->length_unit; | units.length = settings->length_unit; | ||||
| units.mass = settings->mass_unit; | units.mass = settings->mass_unit; | ||||
| units.time = settings->time_unit; | units.time = settings->time_unit; | ||||
| units.temperature = settings->temperature_unit; | |||||
| return units; | return units; | ||||
| } | } | ||||
| static size_t unit_as_string_split_pair(char *str, | static size_t unit_as_string_split_pair(char *str, | ||||
| int len_max, | int len_max, | ||||
| double value, | double value, | ||||
| int prec, | int prec, | ||||
| const bUnitCollection *usys, | const bUnitCollection *usys, | ||||
| const bUnitDef *main_unit) | const bUnitDef *main_unit) | ||||
| { | { | ||||
| const bUnitDef *unit_a, *unit_b; | const bUnitDef *unit_a, *unit_b; | ||||
| double value_a, value_b; | double value_a, value_b; | ||||
| unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b, main_unit); | unit_dual_convert(value, usys, &unit_a, &unit_b, &value_a, &value_b, main_unit); | ||||
| /* Check the 2 is a smaller unit. */ | /* Check the 2 is a smaller unit. */ | ||||
| if (unit_b > unit_a) { | if (unit_b > unit_a) { | ||||
| size_t i; | size_t i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0'); | ||||
| i = unit_as_string(str, len_max, value_a, prec, usys, unit_a, '\0'); | |||||
| prec -= integer_digits_d(value_a / unit_b->scalar) - | prec -= integer_digits_d(value_a / unit_b->scalar) - | ||||
| integer_digits_d(value_b / unit_b->scalar); | integer_digits_d(value_b / unit_b->scalar); | ||||
| prec = max_ii(prec, 0); | prec = max_ii(prec, 0); | ||||
| /* Is there enough space for at least 1 char of the next unit? */ | /* Is there enough space for at least 1 char of the next unit? */ | ||||
| if (i + 2 < len_max) { | if (i + 2 < len_max) { | ||||
| str[i++] = ' '; | str[i++] = ' '; | ||||
| ▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | switch (type) { | ||||
| case B_UNIT_ROTATION: | case B_UNIT_ROTATION: | ||||
| if (units.rotation == 0) { | if (units.rotation == 0) { | ||||
| return usys->units + 0; | return usys->units + 0; | ||||
| } | } | ||||
| else if (units.rotation == USER_UNIT_ROT_RADIANS) { | else if (units.rotation == USER_UNIT_ROT_RADIANS) { | ||||
| return usys->units + 3; | return usys->units + 3; | ||||
| } | } | ||||
| break; | break; | ||||
| case B_UNIT_TEMPERATURE: | |||||
| if (units.temperature == USER_UNIT_ADAPTIVE) { | |||||
| return NULL; | |||||
| } | |||||
| return usys->units + MIN2(units.temperature, max_offset); | |||||
| default: | default: | ||||
| break; | break; | ||||
| } | } | ||||
| return NULL; | return NULL; | ||||
| } | } | ||||
| /* Return the length of the generated string. */ | /* Return the length of the generated string. */ | ||||
| static size_t unit_as_string_main(char *str, | static size_t unit_as_string_main(char *str, | ||||
| Show All 30 Lines | size_t bUnit_AsString( | ||||
| char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad) | char *str, int len_max, double value, int prec, int system, int type, bool split, bool pad) | ||||
| { | { | ||||
| PreferredUnits units; | PreferredUnits units; | ||||
| units.system = system; | units.system = system; | ||||
| units.rotation = 0; | units.rotation = 0; | ||||
| units.length = USER_UNIT_ADAPTIVE; | units.length = USER_UNIT_ADAPTIVE; | ||||
| units.mass = USER_UNIT_ADAPTIVE; | units.mass = USER_UNIT_ADAPTIVE; | ||||
| units.time = USER_UNIT_ADAPTIVE; | units.time = USER_UNIT_ADAPTIVE; | ||||
| units.temperature = USER_UNIT_ADAPTIVE; | |||||
| return unit_as_string_main(str, len_max, value, prec, type, split, pad, units); | return unit_as_string_main(str, len_max, value, prec, type, split, pad, units); | ||||
| } | } | ||||
| size_t bUnit_AsString2(char *str, | size_t bUnit_AsString2(char *str, | ||||
| int len_max, | int len_max, | ||||
| double value, | double value, | ||||
| int prec, | int prec, | ||||
| int type, | int type, | ||||
| ▲ Show 20 Lines • Show All 182 Lines • ▼ Show 20 Lines | while ((remaining_str = find_next_negative(str, remaining_str)) != NULL) { | ||||
| /* Only move forward by 1 even though we added two characters. Minus signs need to be able to | /* Only move forward by 1 even though we added two characters. Minus signs need to be able to | ||||
| * apply to the next block of values too. */ | * apply to the next block of values too. */ | ||||
| remaining_str += 1; | remaining_str += 1; | ||||
| } | } | ||||
| return changed; | return changed; | ||||
| } | } | ||||
| static int find_previous_non_digit_char(const char *str, const int start_ofs) | |||||
| { | |||||
| for (int i = start_ofs; i > 0; i--) { | |||||
| if (ch_is_op(str[i - 1]) || strchr("( )", str[i - 1])) { | |||||
| return i; | |||||
Not Done Inline ActionsBased on the name, I'd expect this function to compare chars to digits, but that does not seem to be what it is doing. JacquesLucke: Based on the name, I'd expect this function to compare chars to digits, but that does not seem… | |||||
Done Inline ActionsI used "value" instead for both the helper functions. Hopefully that's more clear. HooglyBoogly: I used "value" instead for both the helper functions. Hopefully that's more clear. | |||||
| } | |||||
| } | |||||
| return 0; | |||||
| } | |||||
| static int find_end_of_num_chars(const char *str, const int len_max, const int start_ofs) | |||||
| { | |||||
| int i; | |||||
| for (i = start_ofs; i < len_max; i++) { | |||||
| if (!strchr("0123456789eE.", str[i])) { | |||||
| return i; | |||||
| } | |||||
| } | |||||
| return i; | |||||
| } | |||||
| static int unit_scale_str(char *str, | static int unit_scale_str(char *str, | ||||
| int len_max, | int len_max, | ||||
| char *str_tmp, | char *str_tmp, | ||||
| double scale_pref, | double scale_pref, | ||||
| const bUnitDef *unit, | const bUnitDef *unit, | ||||
| const char *replace_str, | const char *replace_str, | ||||
| bool case_sensitive) | bool case_sensitive) | ||||
| { | { | ||||
| char *str_found; | if (len_max < 0) { | ||||
| return 0; | |||||
| } | |||||
| if ((len_max > 0) && (str_found = (char *)unit_find_str(str, replace_str, case_sensitive))) { | |||||
| /* XXX - investigate, does not respect len_max properly. */ | /* XXX - investigate, does not respect len_max properly. */ | ||||
| char *str_found = (char *)unit_find_str(str, replace_str, case_sensitive); | |||||
| int len, len_num, len_name, len_move, found_ofs; | if (str_found == NULL) { | ||||
| return 0; | |||||
| } | |||||
| found_ofs = (int)(str_found - str); | int len = strlen(str); | ||||
| len = strlen(str); | int found_ofs = (int)(str_found - str); | ||||
| len_name = strlen(replace_str); | /* Deal with unit bias for temperature units. Order of operations is important so we have to | ||||
| len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */ | * add parentheses, add the bias, then multiply by the scalar like usual. | ||||
| * | |||||
| * Note: If these changes don't fit in the buffer properly unit evaluation has failed, | |||||
| * just try not to destroy anything while failing. */ | |||||
| // printf("Before Bias: %s\n", str); | |||||
| if (unit->bias != 0.0) { | |||||
Done Inline Actionstypo JacquesLucke: typo | |||||
| /* Add the open paranthesis. */ | |||||
| int prev_op_ofs = find_previous_non_digit_char(str, found_ofs); | |||||
| if (len + 1 < len_max) { | |||||
| memmove(str + prev_op_ofs + 1, str + prev_op_ofs, len - prev_op_ofs + 1); | |||||
| str[prev_op_ofs] = '('; | |||||
| len++; | |||||
| found_ofs++; | |||||
| str_found++; | |||||
| } /* If this doesn't fit, we have failed. */ | |||||
Done Inline Actionstypo JacquesLucke: typo | |||||
| /* Add the addition sign, the bias, and the close paranthesis after the value. */ | |||||
| int value_end_ofs = find_end_of_num_chars(str, len_max, prev_op_ofs + 2); | |||||
| int len_bias_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "+%.9g)", unit->bias); | |||||
| if (value_end_ofs + len_bias_num < len_max) { | |||||
| memmove(str + value_end_ofs + len_bias_num, str + value_end_ofs, len - value_end_ofs + 1); | |||||
| memcpy(str + value_end_ofs, str_tmp, len_bias_num); | |||||
| len += len_bias_num; | |||||
| found_ofs += len_bias_num; | |||||
| str_found += len_bias_num; | |||||
| } /* If this doesn't fit, we have failed. */ | |||||
| } | |||||
| // printf("After Bias: %s\n", str); | |||||
| int len_name = strlen(replace_str); | |||||
| int len_move = (len - (found_ofs + len_name)) + 1; /* 1+ to copy the string terminator. */ | |||||
| /* "#" Removed later */ | /* "#" Removed later */ | ||||
| len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); | int len_num = BLI_snprintf(str_tmp, TEMP_STR_SIZE, "*%.9g" SEP_STR, unit->scalar / scale_pref); | ||||
| if (len_num > len_max) { | if (len_num > len_max) { | ||||
| len_num = len_max; | len_num = len_max; | ||||
| } | } | ||||
| if (found_ofs + len_num + len_move > len_max) { | if (found_ofs + len_num + len_move > len_max) { | ||||
| /* Can't move the whole string, move just as much as will fit. */ | /* Can't move the whole string, move just as much as will fit. */ | ||||
| len_move -= (found_ofs + len_num + len_move) - len_max; | len_move -= (found_ofs + len_num + len_move) - len_max; | ||||
| } | } | ||||
| if (len_move > 0) { | if (len_move > 0) { | ||||
| /* Resize the last part of the string. | /* Resize the last part of the string. | ||||
| * May grow or shrink the string. */ | * May grow or shrink the string. */ | ||||
| memmove(str_found + len_num, str_found + len_name, len_move); | memmove(str_found + len_num, str_found + len_name, len_move); | ||||
| } | } | ||||
| if (found_ofs + len_num > len_max) { | if (found_ofs + len_num > len_max) { | ||||
| /* Not even the number will fit into the string, only copy part of it. */ | /* Not even the number will fit into the string, only copy part of it. */ | ||||
| len_num -= (found_ofs + len_num) - len_max; | len_num -= (found_ofs + len_num) - len_max; | ||||
| } | } | ||||
| if (len_num > 0) { | if (len_num > 0) { | ||||
| /* It's possible none of the number could be copied in. */ | /* It's possible none of the number could be copied in. */ | ||||
| memcpy(str_found, str_tmp, len_num); /* Without the string terminator. */ | memcpy(str_found, str_tmp, len_num); /* Without the string terminator. */ | ||||
| } | } | ||||
| /* Since the null terminator wont be moved if the stringlen_max | /* Since the null terminator wont be moved if the stringlen_max | ||||
| * was not long enough to fit everything in it. */ | * was not long enough to fit everything in it. */ | ||||
| str[len_max - 1] = '\0'; | str[len_max - 1] = '\0'; | ||||
| // printf("After Scale: %s\n", str); | |||||
| return found_ofs + len_num; | return found_ofs + len_num; | ||||
| } | } | ||||
| return 0; | |||||
| } | |||||
| static int unit_replace( | static int unit_replace( | ||||
| char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit) | char *str, int len_max, char *str_tmp, double scale_pref, const bUnitDef *unit) | ||||
| { | { | ||||
| const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0; | const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0; | ||||
| int ofs = 0; | int ofs = 0; | ||||
| ofs += unit_scale_str( | ofs += unit_scale_str( | ||||
| str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_short, case_sensitive); | str + ofs, len_max - ofs, str_tmp, scale_pref, unit, unit->name_short, case_sensitive); | ||||
| ▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | for (int system = 0; system < UNIT_SYSTEM_TOT; system++) { | ||||
| if (!is_valid_unit_collection(usys)) { | if (!is_valid_unit_collection(usys)) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| for (int i = 0; i < usys->length; i++) { | for (int i = 0; i < usys->length; i++) { | ||||
| if (unit_find(str, usys->units + i)) { | if (unit_find(str, usys->units + i)) { | ||||
| return true; | return true; | ||||
| } | } | ||||
| if (usys->units[i].bias != 0.0) { | |||||
| return true; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| return false; | return false; | ||||
| } | } | ||||
| double bUnit_PreferredInputUnitScalar(const struct UnitSettings *settings, int type) | double bUnit_PreferredInputUnitScalar(const struct UnitSettings *settings, int type) | ||||
| { | { | ||||
| PreferredUnits units = preferred_units_from_UnitSettings(settings); | PreferredUnits units = preferred_units_from_UnitSettings(settings); | ||||
| Show All 24 Lines | |||||
| bool bUnit_ReplaceString( | bool bUnit_ReplaceString( | ||||
| char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) | char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) | ||||
| { | { | ||||
| const bUnitCollection *usys = unit_get_system(system, type); | const bUnitCollection *usys = unit_get_system(system, type); | ||||
| if (!is_valid_unit_collection(usys)) { | if (!is_valid_unit_collection(usys)) { | ||||
| return false; | return false; | ||||
| } | } | ||||
| const bUnitDef *unit = NULL, *default_unit; | |||||
| double scale_pref_base = scale_pref; | double scale_pref_base = scale_pref; | ||||
| char str_tmp[TEMP_STR_SIZE]; | char str_tmp[TEMP_STR_SIZE]; | ||||
| bool changed = false; | bool changed = false; | ||||
| /* Fix cases like "-1m50cm" which would evaluate to -0.5m without this. */ | /* Fix cases like "-1m50cm" which would evaluate to -0.5m without this. */ | ||||
| changed |= unit_distribute_negatives(str, len_max); | changed |= unit_distribute_negatives(str, len_max); | ||||
| /* Try to find a default unit from current or previous string. */ | /* Try to find a default unit from current or previous string. */ | ||||
| default_unit = unit_detect_from_str(usys, str, str_prev); | const bUnitDef *default_unit = unit_detect_from_str(usys, str, str_prev); | ||||
| /* We apply the default unit to the whole expression (default unit is now the reference | /* We apply the default unit to the whole expression (default unit is now the reference | ||||
| * '1.0' one). */ | * '1.0' one). */ | ||||
| scale_pref_base *= default_unit->scalar; | scale_pref_base *= default_unit->scalar; | ||||
| /* Apply the default unit on the whole expression, this allows to handle nasty cases like | /* Apply the default unit on the whole expression, this allows to handle nasty cases like | ||||
| * '2+2in'. */ | * '2+2in'. */ | ||||
| if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)*%.9g", str, default_unit->scalar) < | if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)*%.9g", str, default_unit->scalar) < | ||||
| sizeof(str_tmp)) { | sizeof(str_tmp)) { | ||||
| strncpy(str, str_tmp, len_max); | strncpy(str, str_tmp, len_max); | ||||
| } | } | ||||
| else { | else { | ||||
| /* BLI_snprintf would not fit into str_tmp, cant do much in this case. | /* BLI_snprintf would not fit into str_tmp, cant do much in this case. | ||||
| * Check for this because otherwise bUnit_ReplaceString could call its self forever. */ | * Check for this because otherwise bUnit_ReplaceString could call its self forever. */ | ||||
| return changed; | return changed; | ||||
| } | } | ||||
| for (unit = usys->units; unit->name; unit++) { | for (const bUnitDef *unit = usys->units; unit->name; unit++) { | ||||
| /* In case there are multiple instances. */ | /* In case there are multiple instances. */ | ||||
| while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) { | while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) { | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| unit = NULL; | |||||
| { | |||||
| /* Try other unit systems now, so we can evaluate imperial when metric is set for eg. */ | /* Try other unit systems now, so we can evaluate imperial when metric is set for eg. */ | ||||
| /* Note that checking other systems at that point means we do not support their units as | /* Note that checking other systems at that point means we do not support their units as | ||||
| * 'default' one. In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches, | * 'default' one. In other words, when in metrics, typing '2+2in' will give 2 meters 2 inches, | ||||
| * not 4 inches. I do think this is the desired behavior! | * not 4 inches. I do think this is the desired behavior! | ||||
| */ | */ | ||||
| const bUnitCollection *usys_iter; | for (int system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { | ||||
| int system_iter; | |||||
| for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { | |||||
| if (system_iter != system) { | if (system_iter != system) { | ||||
| usys_iter = unit_get_system(system_iter, type); | const bUnitCollection *usys_iter = unit_get_system(system_iter, type); | ||||
| if (usys_iter) { | if (usys_iter) { | ||||
| for (unit = usys_iter->units; unit->name; unit++) { | for (const bUnitDef *unit = usys_iter->units; unit->name; unit++) { | ||||
| int ofs = 0; | int ofs = 0; | ||||
| /* In case there are multiple instances. */ | /* In case there are multiple instances. */ | ||||
| while ( | while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) { | ||||
| (ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) { | |||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| unit = NULL; | |||||
| /* Replace # with add sign when there is no operator between it and the next number. | /* Replace # with add sign when there is no operator between it and the next number. | ||||
| * | * | ||||
| * "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3" | * "1*1# 3*100# * 3" -> "1*1+ 3*100 * 3" | ||||
| * | * | ||||
| * */ | * */ | ||||
| { | { | ||||
| char *str_found = str; | char *str_found = str; | ||||
| Show All 19 Lines | bool bUnit_ReplaceString( | ||||
| return changed; | return changed; | ||||
| } | } | ||||
| /* 45µm --> 45um */ | /* 45µm --> 45um */ | ||||
| void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type) | void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type) | ||||
| { | { | ||||
| const bUnitCollection *usys = unit_get_system(system, type); | const bUnitCollection *usys = unit_get_system(system, type); | ||||
| const bUnitDef *unit; | |||||
| /* Find and substitute all units. */ | /* Find and substitute all units. */ | ||||
| for (unit = usys->units; unit->name; unit++) { | for (const bUnitDef *unit = usys->units; unit->name; unit++) { | ||||
| if (len_max > 0 && unit->name_alt) { | if (len_max > 0 && unit->name_alt) { | ||||
| const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0; | const bool case_sensitive = (unit->flag & B_UNIT_DEF_CASE_SENSITIVE) != 0; | ||||
| const char *found = unit_find_str(orig_str, unit->name_short, case_sensitive); | const char *found = unit_find_str(orig_str, unit->name_short, case_sensitive); | ||||
| if (found) { | if (found) { | ||||
| int offset = (int)(found - orig_str); | int offset = (int)(found - orig_str); | ||||
| int len_name = 0; | int len_name = 0; | ||||
| /* Copy everything before the unit. */ | /* Copy everything before the unit. */ | ||||
| Show All 21 Lines | void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type) | ||||
| /* Finally copy the rest of the string. */ | /* Finally copy the rest of the string. */ | ||||
| strncpy(str, orig_str, len_max); | strncpy(str, orig_str, len_max); | ||||
| } | } | ||||
| double bUnit_ClosestScalar(double value, int system, int type) | double bUnit_ClosestScalar(double value, int system, int type) | ||||
| { | { | ||||
| const bUnitCollection *usys = unit_get_system(system, type); | const bUnitCollection *usys = unit_get_system(system, type); | ||||
| const bUnitDef *unit; | |||||
| if (usys == NULL) { | if (usys == NULL) { | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| unit = unit_best_fit(value, usys, NULL, 1); | const bUnitDef *unit = unit_best_fit(value, usys, NULL, 1); | ||||
| if (unit == NULL) { | if (unit == NULL) { | ||||
| return -1; | return -1; | ||||
| } | } | ||||
| return unit->scalar; | return unit->scalar; | ||||
| } | } | ||||
| double bUnit_BaseScalar(int system, int type) | double bUnit_BaseScalar(int system, int type) | ||||
| ▲ Show 20 Lines • Show All 64 Lines • Show Last 20 Lines | |||||
You can add a couple more 5s here.