Skip to content

Phase 5 slice D3 — Curve editor + Bezier handles #378

Description

@fernandotonon

Overview

Final chunk of slice D from #260. Adds a curve editor view alongside the dope sheet: per-channel curves with Bezier handles, interpolation-mode-per-keyframe, multi-channel display with color coding.

This is the largest D-slice — landing it as one PR may be impractical. Plan to split into D3a (data model + read-only view) and D3b (handle-drag + interp modes + resample) if implementation grows too big.

Why a side-table?

Ogre's TransformKeyFrame stores translate, rotation, and scale at a time — but no Bezier tangent handles, no interpolation mode flag. We need to keep that data on the side and re-emit linear samples into the underlying TransformKeyFrame whenever the curve changes, so Ogre's existing playback path keeps working.

Scope

Data model (C++)

  • New CurveEditModel class. Keyed by (skeleton, animationName, boneName, channelId, time) with channelId ∈ {tx, ty, tz, rw, rx, ry, rz, sx, sy, sz}.
  • Per-keyframe entry: { inTangent: float, outTangent: float, mode: enum }.
  • Modes: Bezier, Linear, Stepped, Auto (Catmull-Rom auto-tangents from neighbors).
  • API:
    • tangentsAt(skel, anim, bone, channel, time) → {in, out, mode}
    • setTangent(skel, anim, bone, channel, time, in, out)
    • setMode(skel, anim, bone, channel, time, mode)
    • evaluate(skel, anim, bone, channel, time) → float — sample the curve
  • Side-table is in-memory only in this slice. Persistence (sidecar JSON) is a follow-up issue.

Resampler

When a tangent or mode changes, resample N points per second across the affected segment(s) into the underlying TransformKeyFrame. N is adaptive to local curvature (more samples where the second derivative is large) to keep file size reasonable.

  • Default N = 30 samples per second; bumped to 60 around high-curvature regions.
  • Limit to ±200 keyframes per segment to bound worst-case insertion.

QML — AnimationCurveEditor.qml

  • New QML view, hosted in the same dock area as the Dope Sheet (toggle between them, or a tab widget).
  • X axis = time (seconds), Y axis = channel value.
  • Renders the selected channels as colored curves (red/green/blue T, magenta+RGB R, orange-tinted RGB S — matches D2 sub-row colors).
  • Each keyframe shows as a square; drag to retime + change value.
  • Each keyframe has two handles (in / out tangent); drag to adjust curve shape.
  • Per-keyframe interp-mode dropdown shown on hover.
  • Wheel = zoom around cursor (X + Y independently); middle-drag = pan.

Selection integration

  • Reuses Dope Sheet's selection state — selected keyframe diamonds in the Dope Sheet appear as selected squares in the Curve Editor.
  • Multi-select / bulk-drag still operates time-only (consistent with D1).

Tests

  • Pure-data: tangent storage round-trip; auto-tangent computation matches Catmull-Rom math; evaluate(t) at a keyframe equals stored value.
  • Ogre-fixture: setting a Bezier handle resamples into TransformKeyFrames; the resampled animation matches the curve at sample points within tolerance.

Out of scope

  • Sidecar persistence (separate issue).
  • Multi-channel batch editing (e.g. set all selected to "Linear" at once) — natural follow-up.
  • Per-channel marquee selection (mostly redundant with Dope Sheet's selection).

Acceptance criteria

  • Curve editor toggleable from View menu next to Dope Sheet
  • Selecting channels in Dope Sheet draws those channels' curves in the Editor
  • Each keyframe shows in/out handles; drag adjusts curve shape
  • Interp-mode dropdown per keyframe (Bezier / Linear / Stepped / Auto)
  • After editing, playback in viewport reflects the new curve shape
  • Linux CI: side-table + resampler tests pass

Plan (#260)

Follows: #373 (slice C), #375 (D1), #377 (D2). Last animation slice in Phase 5.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions