I’m trying to write an expression to do per-character text animation in Natron (you may remember I did this recently).
In my expression I have a bit of code to offset the animation by 15 frames and now I’m trying to slow it down (usually one character appears per frame). Here’s my code:
text = Source.text.get()
letter= 0
# what frame to start triggering the write-on effect
trigger = 15
# this should ensure that the text, regardless of size, fits within 25 frames
speed = len(text)/25
if frame >= trigger:
letter =ceil((letter-trigger)/speed)
ret= text[:letter]
I learnt that frame number must be integers and so I’m using ceil to round up the number to the nearest integer but I’m getting the following error.
ERROR: While executing script:
ret = app1.Text1.text.expression0(16, 0)
Python error:
Python exception: integer division or modulo by zero
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "<string>", line 38, in expression0
ZeroDivisionError: integer division or modulo by zero
I tried this script out in regular desktop Python 3 (substituted frame for an integer) and it worked fine but for some reason it’s not working in Natron.
ERROR: While executing script:
ret = app1.Text1.text.expression0(16, 0)
Python error:
Python exception: global name 'math' is not defined
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "<string>", line 38, in expression0
NameError: global name 'math' is not defined
This is despite Natron automatically importing the math library (from math import *) for all expressions.
Shouldn’t ceil prevent this as it rounds up to the nearest integer?
As a test I tried adding 1 to some of the variables so that letter should always be above 0:
text = Source.text.get()
letter= 1
# what frame to start triggering the write-on effect
trigger = 15
# this should ensure that the text, regardless of size, fits within 25 frames
speed = (len(text)+1)/25
if frame >= trigger:
letter =ceil((letter-trigger)/speed)
ret= text[:letter]
Still got the same zero division error.
I also modified the expression so that it always (in theory) returns an integer:
text = Source.text.get()
letter= 0
trigger = 15
if frame >= trigger:
letter =ceil(4.5)
ret= text[:letter]
But Natron complains about numbers being integers: TypeError: slice indices must be integers or None or have an __index__ method
Python 2 does integer division if you specify two integers so 1 / 25 is 0. Python 3 (maybe only more recent versions than 3.0) does what you expect. To make it work in either case, do:
Thank you all for your help. It helped solve some of my issue but I realise my programming is also flawed! I have taken a new approach and it works. Now the input text will write within a user-specified time frame:
text = Source.text.get()
letter= 0
# what frame to start triggering the write-on effect
trigger = 15
# how many frames it'll take to write the full text
length = 46
# map values. Taken from herehttps://stackoverflow.com/a/1969274
def translate(value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
if frame >= trigger:
letter = int(ceil(translate(frame-trigger, 1, length, 1, len(text))))
else:
letter = 0
ret= text[:letter]