static bUnitDef *unit_best_fit(double value, bUnitCollection *usys, bUnitDef *unit_start, int suppress) { bUnitDef *unit; double value_abs = value > 0.0 ? value : -value; for (unit = unit_start ? unit_start : usys->units; unit->name; unit++) { if (suppress && (unit->flag & B_UNIT_DEF_SUPPRESS)) continue; /* scale down scalar so 1cm doesnt convert to 10mm because of float error */ if (UNLIKELY(unit->flag & B_UNIT_DEF_TENTH)) { if (value_abs >= unit->scalar * (0.1 - EPS)) { return unit; } } else { if (value_abs >= unit->scalar * (1.0 - EPS)) { return unit; } } } return unit_default(usys); }
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; }
/* 45µm --> 45um */ void bUnit_ToUnitAltName(char *str, int len_max, const char *orig_str, int system, int type) { bUnitCollection *usys = unit_get_system(system, type); bUnitDef *unit; bUnitDef *unit_def = unit_default(usys); /* find and substitute all units */ for (unit = usys->units; unit->name; unit++) { if (len_max > 0 && (unit->name_alt || unit == unit_def)) { const char *found = unit_find_str(orig_str, unit->name_short); if (found) { int offset = (int)(found - orig_str); int len_name = 0; /* copy everything before the unit */ offset = (offset < len_max ? offset : len_max); strncpy(str, orig_str, offset); str += offset; orig_str += offset + strlen(unit->name_short); len_max -= offset; /* print the alt_name */ if (unit->name_alt) len_name = BLI_strncpy_rlen(str, unit->name_alt, len_max); else len_name = 0; len_name = (len_name < len_max ? len_name : len_max); str += len_name; len_max -= len_name; } } } /* finally copy the rest of the string */ strncpy(str, orig_str, len_max); }
double bUnit_BaseScalar(int system, int type) { bUnitCollection *usys = unit_get_system(system, type); return unit_default(usys)->scalar; }
/* make a copy of the string that replaces the units with numbers * this is used before parsing * 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.. * 10.1km -> 10.1*1000.0 * ...will be resolved by python. * * values will be split by a comma's * 5'2" -> 5'0.0254, 2*0.3048 * * 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. */ 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); bUnitDef *unit; char str_tmp[TEMP_STR_SIZE]; int changed = 0; if (usys == NULL || usys->units[0].name == NULL) { return 0; } /* make lowercase */ BLI_ascii_strtolower(str, len_max); for (unit = usys->units; unit->name; unit++) { /* in case there are multiple instances */ while (unit_replace(str, len_max, str_tmp, scale_pref, unit)) changed = true; } unit = NULL; { /* try other unit systems now, so we can evaluate imperial when metric is set for eg. */ bUnitCollection *usys_iter; int system_iter; for (system_iter = 0; system_iter < UNIT_SYSTEM_TOT; system_iter++) { if (system_iter != system) { usys_iter = unit_get_system(system_iter, type); if (usys_iter) { for (unit = usys_iter->units; unit->name; unit++) { int ofs = 0; /* in case there are multiple instances */ while ((ofs = unit_replace(str + ofs, len_max - ofs, str_tmp, scale_pref, unit))) changed = true; } } } } } unit = NULL; if (changed == 0) { /* 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" * * */ { char *str_found = str; char *ch = str; while ((str_found = strchr(str_found, SEP_CHR))) { int op_found = 0; /* any operators after this?*/ for (ch = str_found + 1; *ch != '\0'; ch++) { if (*ch == ' ' || *ch == '\t') { /* do nothing */ } 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 = 0; break; } } *str_found++ = op_found ? ' ' : ','; } } return changed; }
static size_t unit_as_string(char *str, int len_max, double value, int prec, bUnitCollection *usys, /* non exposed options */ bUnitDef *unit, char pad) { double value_conv; size_t len, i; if (unit) { /* use unit without finding the best one */ } else if (value == 0.0) { /* use the default units since there is no way to convert */ unit = unit_default(usys); } else { unit = unit_best_fit(value, usys, NULL, 1); } value_conv = value / unit->scalar; /* Convert to a string */ { len = BLI_snprintf(str, len_max, "%.*f", prec, value_conv); if (len >= len_max) len = len_max; } /* Add unit prefix and strip zeros */ /* replace trailing zero's with spaces * so the number is less complicated but alignment in a button wont * jump about while dragging */ i = len - 1; if (prec > 0) { while (i > 0 && str[i] == '0') { /* 4.300 -> 4.3 */ str[i--] = pad; } if (i > 0 && str[i] == '.') { /* 10. -> 10 */ str[i--] = pad; } } /* Now add the suffix */ if (i < len_max) { int j = 0; i++; while (unit->name_short[j] && (i < len_max)) { str[i++] = unit->name_short[j++]; } #if 0 if (pad) { /* this loop only runs if so many zeros were removed that * the unit name only used padded chars, * In that case add padding for the name. */ while (i <= len + j && (i < len_max)) { str[i++] = pad; } } #endif } /* terminate no matter whats done with padding above */ if (i >= len_max) i = len_max - 1; str[i] = '\0'; return i; }