I am a bit of a Blender novice and started getting into Blender using the Blender Python API (bpy). I am trying to animate multiple objects on a set of paths defined by mathematical equations (the paths resemble Mobius strips). As a first experiment I am trying to move just a cone along a simple circle with its apex pointed in the direction away from the center of the circle (would be along the normal to the curve).
The animation of the location works great but I am not able to figure out how to define the rotation for the cone so it doesn’t do weird flips as it rotates. I have tried defining Euler as well as Quaternion rotations without any luck.
Say I define the following keyframes (with rotations defined with respect to the Z-axis)
0 degree
90 degrees
180 degrees
270 degrees
0 degrees
I would also like the rotation to repeat without any glitches in between. Clearly the transition from 270 to 0 is causing the object to move in the wrong direction but even if I use 360 instead of 0, I am not sure how the transition from one loop of the animation to the next can be made to go smoothly.
I realize that there are possibly solutions to this using the graph editor, geometry nodes, modifiers and what not, but I was looking for the best way to handle this using bpy.
I think the simplest way to approach the issue is to use constraints.
I’ve written a small script that does what I think you want, and put in comments to explain everything:
import bpy
# Add a curve Circle, give it a random radius
bpy.ops.curve.primitive_bezier_circle_add(radius=5)
# Set the length of the curve's Path Animation to the length of the scene
# This will allow a seamless loop
bpy.context.object.data.path_duration = bpy.context.scene.frame_end
# Add your cone
bpy.ops.mesh.primitive_cone_add()
# Add a 'Follow Path' constraint to the Cone
bpy.ops.object.constraint_add(type='FOLLOW_PATH')
follow = bpy.context.object.constraints["Follow Path"]
# Set the path it follows to the curve Circle
follow.target = bpy.data.objects["BezierCircle"]
# Tell it to follow that path
follow.use_curve_follow = True
# Set the forward axis to Y, this handles part of the rotation
follow.forward_axis = 'FORWARD_Y'
# Create the path animation
bpy.ops.constraint.followpath_path_animate(constraint="Follow Path", owner='OBJECT')
# Add a 'Damped Track' constraint
bpy.ops.object.constraint_add(type='DAMPED_TRACK')
track = bpy.context.object.constraints["Damped Track"]
# Set the track axis to -Z, this causes the bottom of cone to point
# towards the tracked object
track.track_axis = 'TRACK_NEGATIVE_Z'
# Set the tracking target to the curve Circle
track.target = bpy.data.objects["BezierCircle"]
# Add a 'Limit Rotation' constraint
bpy.ops.object.constraint_add(type='LIMIT_ROTATION')
limit = bpy.context.object.constraints["Limit Rotation"]
# Lock the X axis
limit.use_limit_x = True
# Set the min/max to your required keyframe value
# using math.radians() so we can use degrees like in the editor
limit.max_x = limit.min_x = math.radians(180)
# Change the order of the axes, if not rotating the way you want
limit.euler_order = 'ZXY'
This may not fit 100% of the time, I think I have had issues where the rotation of the individual curve points can cause issues like you originally described when the curves are very complex, twisty shapes, but it’s worth a shot.
Geometry Nodes would probably be my next step, but coding node graphs can be challenging.
EDIT: reread the request and realized my solution didn’t allow for keyframing rotations, added another constraint to allow it