TLDR
Either:
- Replace
%
with fmod
for a simple (FAST) expression:
radians(abs(fmod(frame+45, 90) -45))
- Replace
abs
with fabs
for a Python (SLOW) expression:
radians(fabs((frame+45) % 90 - 45))
(In both cases I added radians
, because that's probably what you actually intended)
Blender fails to parse the driver expression
When you try your driver: abs(((frame+45) % 90)-45)
, Blender first tries to tokenize it using its simple evaluator. You can read about it in the Blender docs:
Drivers Panel > Simple Expressions
Source code: source/blender/blenlib/intern/expr_pylike_eval.c
Notice there's no %
operator listed in the documentation (or binary modulo opcode in the code), Blender detects it and falls back to allow the expression to be run by much more powerful Python interpreter.
'Slow expression' (Python code) deemed unsafe
Since Python is Turing-complete, its code can do everything, including formatting your hard drive (and worse!). To protect against malicious code in .blend files downloaded from the Internet, Blender checks all names and opcodes (basically operators) used against a whitelist of things that are definitely safe:
names:
- builtins (basic):
all
, any
, len
;
- builtins (numeric):
max
, min
, pow
, round
, sum
;
- types:
bool
, float
, int
;
- from
bl_math
module: clamp
, lerp
, smoothstep
,
Additionally, all functions from math
module are added:
acos
, acosh
, asin
, asinh
, atan
, atan2
, atanh
, ceil
, copysign
, cos
, cosh
, degrees
, dist
, erf
, erfc
, exp
, expm1
, fabs
, factorial
, floor
, fmod
, frexp
, fsum
, gamma
, gcd
, hypot
, isclose
, isfinite
, isinf
, isnan
, isqrt
, lcm
, ldexp
, lgamma
, log
, log1p
, log10
, log2
, modf
, pow
, radians
, remainder
, sin
, sinh
, sqrt
, tan
, tanh
, trunc
, prod
, perm
, comb
, nextafter
, ulp
opcodes:
- unary operators:
+
, -
, not
, ~
(UNARY_POSITIVE
, UNARY_NEGATIVE
, UNARY_NOT
, UNARY_INVERT
);
- binary operators:
@
, **
, *
, %
, +
, -
, [x]
(subscription operator), //
, /
, <<
, >>
, &
, ^
, |
(BINARY_MATRIX_MULTIPLY
, BINARY_POWER
, BINARY_MULTIPLY
, BINARY_MODULO
, BINARY_ADD
, BINARY_SUBTRACT
, BINARY_SUBSCR
, BINARY_FLOOR_DIVIDE
, BINARY_TRUE_DIVIDE
, BINARY_LSHIFT
, BINARY_RSHIFT
, BINARY_AND
, BINARY_XOR
, BINARY_OR
);
- comparison operators:
<
, <=
, >
, >=
, ==
, !=
(COMPARE_OP(<)
, COMPARE_OP(<=)
etc.);
- collection constructors:
(1, 2)
, [1, 2]
, {1, 2}
, {'a': 1, 'b': 2}
(BUILD_TUPLE
, BUILD_LIST
, BUILD_SET
, BUILD_MAP
);
- ternary conditional operator:
a if c else b
(POP_JUMP_IF_FALSE
, POP_JUMP_IF_TRUE
);
As well as some low-level opcodes you probably don't need to know:
inplace versions of the binary operators - not assignment operators! (INPLACE_MATRIX_MULTIPLY
, INPLACE_FLOOR_DIVIDE
, INPLACE_TRUE_DIVIDE
, INPLACE_ADD
, INPLACE_SUBTRACT
, INPLACE_MULTIPLY
, INPLACE_MODULO
, INPLACE_POWER
, INPLACE_LSHIFT
, INPLACE_RSHIFT
, INPLACE_AND
, INPLACE_XOR
, INPLACE_OR
);
stack management opcodes: POP_TOP
, ROT_TWO
, ROT_THREE
, DUP_TOP
, DUP_TOP_TWO
, NOP
;
code flow opcodes: RETURN_VALUE
, JUMP_FORWARD
, JUMP_IF_FALSE_OR_POP
, JUMP_IF_TRUE_OR_POP
, JUMP_ABSOLUTE
;
other low level opcodes: LOAD_GLOBAL
, LOAD_FAST
, STORE_FAST
, DELETE_FAST
, LOAD_DEREF
, STORE_DEREF
;
"special cases" (which qualifies really as the same as previous group, but could be dangerous without additional checks done in the Blender code): LOAD_CONST
, LOAD_NAME
, CALL_FUNCTION
, CALL_FUNCTION_KW
, CALL_FUNCTION_EX
,
Source code: source/blender/python/intern/bpy_driver.c
Notice there's no abs
name, so your driver won't pass the security check!
Problem summary
You can see the result in the console (Window > Toggle System Console)
BPY_driver_eval() - restricted access disallows name 'abs', enable auto-execution to support
If your code had only abs
name without %
operator, it would parse as a simple expression. If it would have only %
without abs
, it would fail to parse as a simple expression, but would be allowed as a Slow Expression:
.
However your code first fails to be parsed as a simple expression due to unsupported %
operator, and then fails again at the security check due to abs
name not being on a slow expression whitelist.
The solution
Having both %
and abs
forces you to either disable the security checks, as suggested by Chris - you should read about the security considerations here:
How to randomize any value every frame between specific interval?
Or alternatively you can redesign your formula, which in this case is trivial:
first you should consider if you can satisfy the fast expression requirements, by replacing your %
operator. Most of the time* a % x
works exactly the same as fmod(a, x)
, therefore the fix is trivial: instead of abs(((frame+45) % 90)-45)
do abs(fmod(frame+45, 90)-45)
.
if you can't make the simple expression, forget about all of its requirements altogether, and focus on the slow expression requirements written directly in this post. It's probably a mistake that abs
is not allowed and I'll suggest a change. However, there's fabs
available.
Side notes
Chris noticed how the values you use are typical angles expressed in degrees, and yet inside drivers (or any other form of scripting) the angle values are interpreted as radians. Therefore what you probably want is radians(value_expressed_in_degrees)
to convert the degrees to radians.
If operands (dividend and divisor) have different signs, fmod and modulo operator %
will produce different results! The fix is to replace a % n
not with fmod(a, n)
but with fmod(fmod(a, n)+n, n)
:
How to replicate %
operator in simple driver expressions?