I want to use the folowing driver:
abs(((frame+45) % 90)-45)
But it does nothing. I tried also #abs
and #frame
etc. But nothing, the thing is purple as expected. How can I debug this?
Blender Stack Exchange is a question and answer site for people who use Blender to create 3D graphics, animations, or games. It only takes a minute to sign up.
Sign up to join this communityI want to use the folowing driver:
abs(((frame+45) % 90)-45)
But it does nothing. I tried also #abs
and #frame
etc. But nothing, the thing is purple as expected. How can I debug this?
Either:
%
with fmod
for a simple (FAST) expression:radians(abs(fmod(frame+45, 90) -45))
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)
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.
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:
all
, any
, len
;max
, min
, pow
, round
, sum
;bool
, float
, int
;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:
+
, -
, not
, ~
(UNARY_POSITIVE
, UNARY_NEGATIVE
, UNARY_NOT
, UNARY_INVERT
);@
, **
, *
, %
, +
, -
, [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
);<
, <=
, >
, >=
, ==
, !=
(COMPARE_OP(<)
, COMPARE_OP(<=)
etc.);(1, 2)
, [1, 2]
, {1, 2}
, {'a': 1, 'b': 2}
(BUILD_TUPLE
, BUILD_LIST
, BUILD_SET
, BUILD_MAP
);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!
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.
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.
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.
* - the differences between fmod
and modulo operator %
:
>>> vs = lambda a, b: (a % b, fmod(a, b))
>>> vs(5, 2)
(1, 1.0)
>>> vs(5, -2)
(-1, 1.0)
>>> vs(-5, 2)
(1, -1.0)
>>> vs(-5, -2)
(-1, -1.0)
So beware of the sign. %
uses the sign of second operand, and fmod
uses the sign of first operand. If you really need exactly %
functionality, replace a % b
with abs(fmod(a, b))*(b/abs(b))
or fmod(-abs(a), b) if b < 0 else fmod(abs(a), b)
:
>>> vs = lambda a, b: (a % b, abs(fmod(a, b))*(b/abs(b)) ,fmod(-abs(a), b) if b < 0 else fmod(abs(a), b))
>>> vs(5, 2)
(1, 1.0, 1.0)
>>> vs(5, -2)
(-1, -1.0, -1.0)
>>> vs(-5, 2)
(1, 1.0, 1.0)
>>> vs(-5, -2)
(-1, -1.0, -1.0)
Make sure you have this checked: "Auto run python scripts" in your preferences.
Note: i assume you want to drive a rotation? be aware that you have to work with radians, not degrees, so just change your formula so that it calculates in radians, then it should work
To sum up the discussion in the comments of the two existing answers:
There are two approaches you can take:
You can follow the recommendation in this answer and enable Auto-Run. But that produces a different problem: you've decreased the security of your Blender usage because now anytime you download a Blender file and open it your vulnerable to Python scripting attacks.
You can follow the recommendation in this answer, ignore the actual text of the error message, and find a scripted expression that doesn't trigger the warning. But that's not well documented and as you can see by the confusion in the comments might not be easy to do.
I would recommend, and have upvoted the second answer, ie, use radians(abs(fmod(frame+45, 90) -45))
in your driver to avoid the error. But I would not be honest if I did not admit that most Blender users never see this kind of problem simply because we ignore the security implication and enable auto-run.
You have to decide how much security risk you are willing to tolerate. Select one of those two approaches, accept that answer, and good luck.