# Keyframe a driver / Triggering scripted expression with a keyframe

I want to keyframe a driver. I have a driver on the X rotation that is driven based on the Y position. So it rotates along the X when it moves along the Y. However, i don't need it to rotate all the time when it moves along that axis but only at a certain time. So, for example: don't evaluate the driver until you're on frame 32 or beyond. (If frame >= 32 then evaluate driver's expression)

I want to go from this:

To this:

The desirable effect is achieved by baking down the animation and just tweaking the keyframes in the Dope Sheet... but, as you can understand, it removes all the procedural data, which is not desirable.

And, i don't know if it deserves a separate question, how do i calculate a delta with python? If there is a way to keyframe a driver, then i don't want just to grab the current value from the position and use it to calculate rotation; i want it to be like that: (If frame >= 32 then PositionOnFrame32 - PositionOnCurrentFrame). And i think i need it to put in the modulus (absolute value) function so to eliminate negative values.

• Let's say the gear is traveling along theY axis. I would drive rotation when gear.y is within strip.y to strip.y + strip.dimensions.y rather than use a certain frame Mar 6 '18 at 10:22
• @batFINGER In this case the trigger is a position in 3d space rather than a frame. But still, how do u turn a driver on and off with a trigger? (whatever the trigger may be) Mar 6 '18 at 10:53

Using a driver on parent empty to drive gears

Firstly I've set up my rack and pinion like so.

1. Named the pinion "Pinion" (as long as it starts with "Pinion") and placed the pinion above the left edge of rack, by its radius.
2. Similarly named the rack "Rack" and set the origin of the rack to the centre of the pinion.
3. Added an empty at that location.
4. Made the empty the parent of the rack and pinion. Now when the pinion is at pinion.location.y = 0 it's directly above the left end of rack, and other end when its y location is rack.dimensions.y. Our driver method will use this to determine if the pinions in the rack.

5. Added a custom property to the Empty and given it a driver. Checked the "use self" checkbox, and passed self via the driver expression rackandpinion(self). Simply returns 1 if in gear, else 0. The driver indirectly drives the pinion.

6. Defined the driver expression. Make sure "use self" is checked. Might need to update dependencies if it shows an error.

The script. Checks for rack and pinion children of the empty. If the pinion is over the rack, it uses arc length formula to rotate its delta rotation about the x axix. Adding a method / variable to the bpy.app.driver_namespace dictionary makes it available to driver expressions. (just as are a lot of math and other methods and vars)

import bpy

def rackandpinion(self):
def get_gear(name):
gears = [o for o in self.children
if o.name.startswith(name)]
return None if not gears else gears[0]

rack = get_gear("Rack")
pinion = get_gear("Pinion")
if not (rack or pinion):
return 0

# on the pinion if 0 <= pinion.y >= rack_len
racklength = rack.dimensions.y
geared = 0 <= pinion.location.y <= racklength
if geared:
pinion.delta_rotation_euler.x = -1 * pinion.location.y / pinion_radius
return 1

bpy.app.driver_namespace["rackandpinion"] = rackandpinion


This is very similar to using a frame change handler. The driver expression, with use self, gives us the ability to pass the object.

Possibly a more object oriented approach would be for the empty to set custom properties for calculated rotation, and have driver expressions on the pinion using this as a driver variable. (Could produce cyclic redundancy by getting and setting in a loop in drivers)

• So just i understand it correctly: u create an empty and reference a method (rackandpinion) in its driver expression, and u define that method/function with a simple python code with the name rackandpinion and then register that method/function in the Blender's namespace, so that u can write any elaborate code in the comfort of a text editor rather than cramming it into a one-line expression field. Am i somewhat correct? :) Mar 6 '18 at 14:11
• blender.stackexchange.com/a/34120/15543 Suggest typing in the text editor and copy / paste into driver expression (if using long expressions) In hindsight could have passed the names of the rack, pinion. btw didn't use frame in expression, if you want it at start of rack keyframe in y loc = 0 at frame N. Mar 6 '18 at 14:18

You can use Python's ternary if-else operator:

function_of(y_location) if frame >= 32 else 0


You can nest these.

If you need to evaluate some object's position at given frame, you have to use PyDriver.

Like batFINGER suggested, binding this to the start and end position of the teeth rack (or to 2 empties) would work well also. That would look like this:

(rot_fce(y_loc) if y_loc < end_loc else rot_fce(end_loc)) if y_loc >= start_loc else 0

• I've done some reading on PyDriver. Isn't PyDriver a relic of Blender < 2.5 versions; so u could load ur expressions at loadtime from a script file... Do i understand it correctly? Or PyDrive is just a codeword for the Scripted Expression in the Drivers tab? Mar 6 '18 at 13:01
• @BlenderSpot It's not a relic. It's just a name we give a driver that uses custom scripted functions that have to be added to the driver namespace. Mar 6 '18 at 13:20