Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/unit.c
| Show First 20 Lines • Show All 487 Lines • ▼ Show 20 Lines | |||||
| } | } | ||||
| /* Note that numbers are added within brackets | /* Note that numbers are added within brackets | ||||
| * ") " - is used to detect numbers we added so we can detect if commas need to be added | * ") " - is used to detect numbers we added so we can detect if commas need to be added | ||||
| * | * | ||||
| * "1m1cm+2mm" - Original value | * "1m1cm+2mm" - Original value | ||||
| * "1*1#1*0.01#+2*0.001#" - Replace numbers | * "1*1#1*0.01#+2*0.001#" - Replace numbers | ||||
| * "1*1,1*0.01 +2*0.001 " - Add comma's if ( - + * / % ^ < > ) not found in between | * "1*1+1*0.01 +2*0.001 " - Add add signs if ( + - * / | & ~ < > ^ ! = % ) not found in between | ||||
| * | * | ||||
| */ | */ | ||||
| /* not too strict, (- = * /) are most common */ | /* not too strict, (+ - * /) are most common */ | ||||
| static bool ch_is_op(char op) | static bool ch_is_op(char op) | ||||
| { | { | ||||
| switch (op) { | switch (op) { | ||||
| case '+': | case '+': | ||||
| case '-': | case '-': | ||||
| case '*': | case '*': | ||||
| case '/': | case '/': | ||||
| case '|': | case '|': | ||||
| ▲ Show 20 Lines • Show All 75 Lines • ▼ Show 20 Lines | static int unit_find(const char *str, bUnitDef *unit) | ||||
| if (unit_find_str(str, unit->name_short)) return 1; | if (unit_find_str(str, unit->name_short)) return 1; | ||||
| if (unit_find_str(str, unit->name_plural)) return 1; | if (unit_find_str(str, unit->name_plural)) return 1; | ||||
| if (unit_find_str(str, unit->name_alt)) return 1; | if (unit_find_str(str, unit->name_alt)) return 1; | ||||
| if (unit_find_str(str, unit->name)) return 1; | if (unit_find_str(str, unit->name)) return 1; | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| static bUnitDef *unit_detect_from_str(bUnitCollection *usys, const char *str, const char *str_prev) | |||||
| { | |||||
| /* Try to find a default unit from current or previous string. | |||||
| * This allows us to handle cases like 2 + 2mm, people would expect to get 4mm, not 2.002m! | |||||
| * Note this does not handle corner cases like 2 + 2cm + 1 + 2.5mm... We can't support everything. */ | |||||
| bUnitDef *unit = NULL; | |||||
| /* see which units the new value has */ | |||||
| for (unit = usys->units; unit->name; unit++) { | |||||
| if (unit_find(str, unit)) | |||||
| break; | |||||
| } | |||||
| /* Else, try to infer the default unit from the previous string. */ | |||||
| if (str_prev && (unit == NULL || unit->name == NULL)) { | |||||
| /* see which units the original value had */ | |||||
| for (unit = usys->units; unit->name; unit++) { | |||||
| if (unit_find(str_prev, unit)) | |||||
| break; | |||||
| } | |||||
| } | |||||
| /* Else, fall back to default unit. */ | |||||
| if (unit == NULL || unit->name == NULL) { | |||||
| unit = unit_default(usys); | |||||
| } | |||||
| return unit; | |||||
| } | |||||
| /* make a copy of the string that replaces the units with numbers | /* make a copy of the string that replaces the units with numbers | ||||
| * this is used before parsing | * this is used before parsing | ||||
| * This is only used when evaluating user input and can afford to be a bit slower | * This is only used when evaluating user input and can afford to be a bit slower | ||||
| * | * | ||||
| * This is to be used before python evaluation so.. | * This is to be used before python evaluation so.. | ||||
| * 10.1km -> 10.1*1000.0 | * 10.1km -> 10.1*1000.0 | ||||
| * ...will be resolved by python. | * ...will be resolved by python. | ||||
| * | * | ||||
| * values will be split by a comma's | * values will be split by an add sign | ||||
| * 5'2" -> 5*0.3048, 2*0.0254 | * 5'2" -> 5*0.3048 + 2*0.0254 | ||||
| * | * | ||||
| * str_prev is optional, when valid it is used to get a base unit when none is set. | * str_prev is optional, when valid it is used to get a base unit when none is set. | ||||
| * | * | ||||
| * return true of a change was made. | * return true of a change was made. | ||||
| */ | */ | ||||
| int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) | int bUnit_ReplaceString(char *str, int len_max, const char *str_prev, double scale_pref, int system, int type) | ||||
| { | { | ||||
| bUnitCollection *usys = unit_get_system(system, type); | bUnitCollection *usys = unit_get_system(system, type); | ||||
| bUnitDef *unit; | bUnitDef *unit = NULL, *default_unit; | ||||
| double scale_pref_base = scale_pref; | |||||
campbellbarton: odd name. better call `scale_pref_base` / `scale_pref_default` ? | |||||
| char str_tmp[TEMP_STR_SIZE]; | char str_tmp[TEMP_STR_SIZE]; | ||||
| int changed = 0; | int changed = 0; | ||||
| if (usys == NULL || usys->units[0].name == NULL) { | if (usys == NULL || usys->units[0].name == NULL) { | ||||
| return 0; | return 0; | ||||
| } | } | ||||
| /* make lowercase */ | /* make lowercase */ | ||||
| BLI_ascii_strtolower(str, len_max); | BLI_ascii_strtolower(str, len_max); | ||||
| /* Try to find a default unit from current or previous string. */ | |||||
Not Done Inline Actions*picky*, previously this was isolated in its own block, could move this into own static func? unit_detect_from_str campbellbarton: *picky*, previously this was isolated in its own block,
could move this into own static func? | |||||
| 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 '1.0' one). */ | |||||
| scale_pref_base *= default_unit->scalar; | |||||
| /* Apply the default unit on the whole expression, this allows to handle nasty cases like '2+2in'. */ | |||||
| if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)*%.9g", str, default_unit->scalar) < sizeof(str_tmp)) { | |||||
| strncpy(str, str_tmp, len_max); | |||||
| } | |||||
| else { | |||||
Not Done Inline Actions*picky*, With this I worry that it may be haphazard which unit is found... Would rather have some logic here...
campbellbarton: *picky*, With this I worry that it may be haphazard which unit is found...
Would rather have… | |||||
Not Done Inline ActionsWell, it does select the biggest (largest) unit, just like code above? We first search for km, then m, then cm, then mm, etc. mont29: Well, it **does** select the biggest (largest) unit, just like code above? We first search for… | |||||
| /* 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 */ | |||||
| return 0; | |||||
| } | |||||
| for (unit = usys->units; unit->name; unit++) { | for (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, unit)) | while (unit_replace(str, len_max, str_tmp, scale_pref_base, unit)) | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| unit = NULL; | 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 '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! | |||||
| */ | |||||
| bUnitCollection *usys_iter; | bUnitCollection *usys_iter; | ||||
| int system_iter; | int system_iter; | ||||
| for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; 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); | usys_iter = unit_get_system(system_iter, type); | ||||
| if (usys_iter) { | if (usys_iter) { | ||||
| for (unit = usys_iter->units; unit->name; unit++) { | for (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 ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref, unit))) | while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref_base, unit))) | ||||
| changed = true; | changed = true; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| unit = NULL; | unit = NULL; | ||||
| if (changed == 0) { | /* replace # with add sign when there is no operator between it and the next number | ||||
| /* no units given so infer a unit from the previous string or default */ | |||||
| if (str_prev) { | |||||
| /* see which units the original value had */ | |||||
| for (unit = usys->units; unit->name; unit++) { | |||||
| if (unit_find(str_prev, unit)) | |||||
| break; | |||||
| } | |||||
| } | |||||
| if (unit == NULL || unit->name == NULL) | |||||
| unit = unit_default(usys); | |||||
| /* add the unit prefix and re-run, use brackets in case there was an expression given */ | |||||
| if (BLI_snprintf(str_tmp, sizeof(str_tmp), "(%s)%s", str, unit->name) < sizeof(str_tmp)) { | |||||
| strncpy(str, str_tmp, len_max); | |||||
| return bUnit_ReplaceString(str, len_max, NULL, scale_pref, system, type); | |||||
| } | |||||
| else { | |||||
| /* 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 */ | |||||
| return 0; | |||||
| } | |||||
| } | |||||
| /* replace # with commas 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; | ||||
| const char *ch = str; | const char *ch = str; | ||||
| while ((str_found = strchr(str_found, SEP_CHR))) { | while ((str_found = strchr(str_found, SEP_CHR))) { | ||||
| bool op_found = false; | |||||
| int op_found = 0; | |||||
| /* any operators after this?*/ | /* any operators after this? */ | ||||
| for (ch = str_found + 1; *ch != '\0'; ch++) { | for (ch = str_found + 1; *ch != '\0'; ch++) { | ||||
| if (*ch == ' ' || *ch == '\t') { | if (*ch == ' ' || *ch == '\t') { | ||||
| /* do nothing */ | continue; | ||||
| } | |||||
| else if (ch_is_op(*ch) || *ch == ',') { /* found an op, no need to insert a ',' */ | |||||
| op_found = 1; | |||||
| break; | |||||
| } | } | ||||
| else { /* found a non-op character */ | op_found = (ch_is_op(*ch) || ELEM(*ch, ',', ')')); | ||||
| op_found = 0; | |||||
| break; | break; | ||||
| } | } | ||||
| } | |||||
| *str_found++ = op_found ? ' ' : ','; | /* If found an op, comma or closing parenthesis, no need to insert a '+', else we need it. */ | ||||
| *str_found++ = op_found ? ' ' : '+'; | |||||
| } | } | ||||
| } | } | ||||
| 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) | ||||
| ▲ Show 20 Lines • Show All 97 Lines • Show Last 20 Lines | |||||
odd name. better call scale_pref_base / scale_pref_default ?