FCurve Architecture: Tangent Pipeline#
Overview#
This document defines the architecture for FCurve tangent and keyframe handling.
TL;DR#
UIManager: Manages keyframe/tangent widgets, events, and pixel↔model conversion.
process_curve: Single entry point that builds KeyframeGestureData and calls process_keyframes.
Model (FCurve.keys): Source of truth. Mutated in place. No return value — model is the result.
Architecture After Refactor#
UIManager (events, pixel↔model)
└── process_curve(curve, bounds, threshold, key_positions, tangent_positions)
└── builds KeyframeGestureData, calls process_keyframes()
└── mutates curve.keys in place
Model = source of truth (FCurve.keys). Mutated in place.
UI = reads from model after process. Ghost lines: model position vs raw widget position when they differ.
No return value — model is the result.
Core Principles#
Model is the source of truth. All tangent and keyframe data lives in FCurve.keys. process_curve mutates keys in place.
Model space everywhere. All layers work in model coordinates (time, value as floats). Only UIManager knows about pixels/screen positions.
Single pipeline. process_keyframes (via process_curve) handles all tangent type computation, mirroring, and bounds clamping.
Tangent Type Computation (process_keyframes)#
process_keyframes computes tangent positions based on tangent TYPE:
LINEAR: Points directly at neighbor keyframe (midpoint)
FLAT: Horizontal tangent (Y offset = 0)
STEP: Instant value change (no interpolation)
AUTO: Uses Catmull-Rom to compute angle based on neighbors
SMOOTH: User controls angle, length == half distance to neighbor keyframe
CUSTOM: User-defined values, no automatic computation
See TANGENT_BEHAVIOR.md for full specification.
Ghost Line Feedback#
Ghost line feedback is shown when the user is dragging and the model position differs from the raw widget position.
During drag:
if raw_ui_drag_position != model_position:
show a ghost marker at model position
show line from model to raw_ui_drag_position
On release:
model is already updated by process_curve
widget reads from model
Key Constraints#
Component |
Responsibility |
|---|---|
UIManager |
Events, pixel↔model, widget creation, calls process_curve |
process_curve |
Builds KeyframeGestureData from overrides, calls process_keyframes |
process_keyframes |
Mutates curve.keys in place (tangent types, mirroring, clamping) |
Model (FCurve.keys) |
Single source of truth |