Fcurves to 3D bezier.
AFAIK blender doesn't make any "3D bezier keyframe points"
from fcurves. For one there is no requirement for there to be an fcurve nor any or matching frame keyframe points from one location component fcurve to another.
Sample script to turn 3 location component FCurves into 1 3D bezier curve
- Each fcurve must have matching number of points keyed at same frames.
- The default handle type for fcurves is
'AUTO_CLAMPED'
there is no
such option for bezier curves, have used 'AUTO'
instead. Need to
make an edit such as ht = 'AUTO' if p.handle_left == 'AUTO_CLAMPED' else p.handle_Left_type
My experments lead me to using 'FREE'
emulates well the differing handle types of fcurves.
- The handles in an fcurve (frame, value) represent the first derivative or tangent at that point. Each fcurve handle y value is used component wise on the 3D handle on 3D curve. Once again there is a chance that handle types don't match. AFAIK a handle cannot have different type per component.
- Basically this makes a motion path with the same amount of points in the bezier as keyframe points in single fcurve.
Code cobbled together using @Samblers answer as a base.
import bpy
anim_fcurves = bpy.context.object.animation_data.action.fcurves
# index 1 is the Y location fcurve
src_fcurve = anim_fcurves.find('location', index=1)
vis_curve = bpy.data.curves.new('YLocCurve', 'CURVE')
vis_curve.dimensions = '3D'
vis_spline = vis_curve.splines.new('BEZIER')
vis_spline.bezier_points.add(count=len(src_fcurve.keyframe_points)-1)
fcurves = zip(*(anim_fcurves.find('location', index=i).keyframe_points[:] for i in range(3)))
i = 0
for xp, yp, zp in fcurves:
p = vis_spline.bezier_points[i]
p.co = (xp.co.y, yp.co.y, zp.co.y)
p.handle_left = (xp.handle_left.y, yp.handle_left.y, zp.handle_left.y)
#ht = 'AUTO' if xp.handle_left == 'AUTO_CLAMPED' else xp.handle_left_type
p.handle_left_type = 'FREE'
p.handle_right = (xp.handle_right.y, yp.handle_right.y, zp.handle_right.y)
#ht = 'AUTO' if xp.handle_right == 'AUTO_CLAMPED' else xp.handle_right_type
p.handle_right_type = 'FREE'
i += 1
data = bpy.data.objects.new('YLocCurve', vis_curve)
bpy.context.collection.objects.link(data)
Simple 4 keyframe animation on cube, all handles VECTOR
type.
Mixture of FREE, VECTOR
and ALIGNED
handle types
EDIT
In response to comment below,
Thank you for making this very detailed answer - it looks very similar
to the solution i use right now, but from what i can tell, it still
doesn't solve the entire problem. You use only the Y values of the
handles as 3D handle coordinates, but as i mentioned earlier, the X
value also affects the shape of the curve in the graph as well as the
animated path (I have some nice images illustrating this but don't
know how to attach them). Does your cube animation exactly follow the
generated spline even if you drag some FCurve handles along the X
axis? Because i'm not sure that it does.
the x value in an fcurve is analogous to time, time is the frame in 3d view. Could arbitrarily move each fcurve handle, (particularly for vector type) such that the x component is a constant, and calculate it's new y for each fcurve point for each fcurve.
For example if the tangent is 1, ie 45 degrees, at the point (0, 0) knot could have a handle (4, 4) which to standardize to a 1 unit time step, is a handle at (1, 1).
This will standardize what is the tangent or first derivative of the curve at the knot for the arbitrary value of time chosen. For time is one this is derivative dv / dt in blender units per frame.
Remember the first derivative is the limit as the time approaches zero, so perhaps choose a smaller value than 1.
fcurve.evaluate()
to get blender to calculate the value at each frame. $\endgroup$