diff --git a/source/blender/blenkernel/intern/unit.cc b/source/blender/blenkernel/intern/unit.cc index a89806b3801..9c4db7c77da 100644 --- a/source/blender/blenkernel/intern/unit.cc +++ b/source/blender/blenkernel/intern/unit.cc @@ -1844,6 +1844,18 @@ static bool ch_is_op(char op) } } +static bool ch_is_op_unary(char op) +{ + switch (op) { + case '+': + case '-': + case '~': + return true; + default: + return false; + } +} + /** * Helper function for #unit_distribute_negatives to find the next negative to distribute. * @@ -1912,6 +1924,18 @@ static char *find_next_op(const char *str, char *remaining_str, int remaining_st return remaining_str + i; } +/** + * Skip over multiple successive unary operators (typically `-`), skipping spaces. + * This allows for `--90d` to be handled properly, see: #117783. + */ +static char *skip_unary_op(char *str) +{ + while (*str == ' ' || ch_is_op_unary(*str)) { + str++; + } + return str; +} + /** * Put parentheses around blocks of values after negative signs to get rid of an implied "+" * between numbers without an operation between them. For example: @@ -1937,8 +1961,9 @@ static bool unit_distribute_negatives(char *str, const int str_maxncpy) memmove(remaining_str + 1, remaining_str, remaining_str_maxncpy - 2); *remaining_str = '('; - /* Add the ')' before the next operation or at the end. */ - remaining_str = find_next_op(str, remaining_str + 1, remaining_str_maxncpy); + /* Add the ')' before the next operation or at the end. + * Unary operators are skipped to allow `--` to be a supported prefix. */ + remaining_str = find_next_op(str, skip_unary_op(remaining_str + 1), remaining_str_maxncpy); remaining_str_maxncpy = str_maxncpy - int(remaining_str - str); memmove(remaining_str + 1, remaining_str, remaining_str_maxncpy - 2); *remaining_str = ')'; diff --git a/tests/python/bl_pyapi_bpy_utils_units.py b/tests/python/bl_pyapi_bpy_utils_units.py index e80aa833ec6..ab86b22fab9 100644 --- a/tests/python/bl_pyapi_bpy_utils_units.py +++ b/tests/python/bl_pyapi_bpy_utils_units.py @@ -29,6 +29,24 @@ class UnitsTesting(unittest.TestCase): ('METRIC', 'LENGTH', "", "1+1ft", 1.3048), # no metric units, we default to meters. ('IMPERIAL', 'LENGTH', "", "3+1in+1ft", 0.3048 * 4 + 0.0254), # bigger unit becomes default one! ('IMPERIAL', 'LENGTH', "", "(3+1)in+1ft", 0.3048 + 0.0254 * 4), + + # Support successive leading unary operators. + ('IMPERIAL', 'LENGTH', "", "-1ft", -0.3048), + ('IMPERIAL', 'LENGTH', "", "--1ft", --0.3048), + ('IMPERIAL', 'LENGTH', "", "---1ft", ---0.3048), + + ('IMPERIAL', 'LENGTH', "", "- 1ft", -0.3048), + ('IMPERIAL', 'LENGTH', "", "- - 1ft", --0.3048), + ('IMPERIAL', 'LENGTH', "", "- - - 1ft", ---0.3048), + + ('IMPERIAL', 'LENGTH', "", "-+1ft", -+0.3048), + ('IMPERIAL', 'LENGTH', "", "+-1ft", +-0.3048), + + ('METRIC', 'LENGTH', "", "~+-32m", ~+-32), + ('METRIC', 'LENGTH', "", "-+~32m", -+~32), + + ('METRIC', 'LENGTH', "", "~ + - 32m", ~+-32), + ('METRIC', 'LENGTH', "", "- + ~ 32m", -+~32), ) # From 'internal' Blender value to user-friendly printing