Fix T74552: Distribute negatives in number input
This applies a relatively simple solution for fixing some unintuitive cases in unit handling. Currently entering -1m50cm evaluates to -0.5m, and similarly 1'6" evaulates to just half a foot. So effectively there's an implied + just between the numbers, which is quite confusing. This works by adding parentheses so the negative distributes to the block of values before the next operator. For example: | Before | After | | `-1m50cm + 1m -2m50cm` | `-(1m50cm) + 1m -(2m50cm)` | | `-4m + 0.5 / -1.1` | `-(4m) + 0.5 / -(1.1)` | | `-1'6"` | `-(1'6")` | | `-1e-2cm` | `-(1e-2cm) ` | Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D7813
This commit is contained in:
@@ -713,6 +713,116 @@ static bool ch_is_op(char op)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for #unit_distribute_negatives to find the next negative to distribute.
|
||||||
|
*
|
||||||
|
* \note This unecessarily skips the next space if it comes right after the "-"
|
||||||
|
* just to make a more predictable output.
|
||||||
|
*/
|
||||||
|
static char *find_next_negative(const char *str, const char *remaining_str)
|
||||||
|
{
|
||||||
|
char *str_found = strstr(remaining_str, "-");
|
||||||
|
|
||||||
|
if (str_found == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Don't use the "-" from scientific notation, but make sure we can look backwards first. */
|
||||||
|
if ((str_found != str) && (*(str_found - 1) == 'e' || *(str_found - 1) == 'E')) {
|
||||||
|
return find_next_negative(str, str_found + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*(str_found + 1) == ' ') {
|
||||||
|
str_found++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return str_found + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for #unit_distribute_negatives to find the next operation, including "-".
|
||||||
|
*
|
||||||
|
* \note This unecessarily skips the space before the operation character
|
||||||
|
* just to make a more predictable output.
|
||||||
|
*/
|
||||||
|
static char *find_next_op(const char *str, char *remaining_str, int len_max)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
bool scientific_notation = false;
|
||||||
|
for (i = 0; i < len_max; i++) {
|
||||||
|
if (remaining_str[i] == '\0') {
|
||||||
|
return remaining_str + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ch_is_op(remaining_str[i])) {
|
||||||
|
if (scientific_notation) {
|
||||||
|
scientific_notation = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we don't look backwards before the start of the string. */
|
||||||
|
if (remaining_str != str && i != 0) {
|
||||||
|
/* Check for scientific notation. */
|
||||||
|
if (remaining_str[i - 1] == 'e' || remaining_str[i - 1] == 'E') {
|
||||||
|
scientific_notation = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return position before a space character. */
|
||||||
|
if (remaining_str[i - 1] == ' ') {
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return remaining_str + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BLI_assert(!"String should be NULL terminated");
|
||||||
|
return remaining_str + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put parentheses around blocks of values after negative signs to get rid of an implied "+"
|
||||||
|
* between numbers without an operation between them. For example:
|
||||||
|
*
|
||||||
|
* "-1m50cm + 1 - 2m50cm" -> "-(1m50cm) + 1 - (2m50cm)"
|
||||||
|
*/
|
||||||
|
static bool unit_distribute_negatives(char *str, const int len_max)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
char *remaining_str = str;
|
||||||
|
int remaining_str_len = len_max;
|
||||||
|
int ofs = 0;
|
||||||
|
while ((remaining_str = find_next_negative(str, remaining_str)) != NULL) {
|
||||||
|
ofs = (int)(remaining_str - str);
|
||||||
|
|
||||||
|
/* Exit early in the unlikely situation that we've run out of length to add the parentheses. */
|
||||||
|
remaining_str_len = len_max - ofs;
|
||||||
|
if (remaining_str_len <= 2) {
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
/* Add '(', shift the following characters to the right to make space. */
|
||||||
|
memmove(remaining_str + 1, remaining_str, remaining_str_len - 1);
|
||||||
|
*remaining_str = '(';
|
||||||
|
|
||||||
|
/* Add the ')' before the next operation or at the end. */
|
||||||
|
remaining_str = find_next_op(str, remaining_str + 1, remaining_str_len);
|
||||||
|
memmove(remaining_str + 1, remaining_str, remaining_str_len - 3);
|
||||||
|
*remaining_str = ')';
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
remaining_str += 1;
|
||||||
|
remaining_str_len -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
@@ -896,6 +1006,9 @@ bool bUnit_ReplaceString(
|
|||||||
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. */
|
||||||
|
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);
|
default_unit = unit_detect_from_str(usys, str, str_prev);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user