Using a driver on parent empty to drive gears
Firstly I've set up my rack and pinion like so.
- Named the pinion "Pinion" (as long as it starts with "Pinion") and placed the pinion above the left edge of rack, by its radius.
- Similarly named the rack "Rack" and set the origin of the rack to the centre of the pinion.
- Added an empty at that location.
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.
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.
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
pinion_radius = pinion.dimensions.y / 2
if geared:
pinion.delta_rotation_euler.x = -1 * pinion.location.y / pinion_radius
return 1
# add to driver namespace
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)