summaryrefslogtreecommitdiff
path: root/tools/mq_editor/mq_extract.js
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mq_editor/mq_extract.js')
-rw-r--r--tools/mq_editor/mq_extract.js56
1 files changed, 20 insertions, 36 deletions
diff --git a/tools/mq_editor/mq_extract.js b/tools/mq_editor/mq_extract.js
index 237f1ab..878b2bc 100644
--- a/tools/mq_editor/mq_extract.js
+++ b/tools/mq_editor/mq_extract.js
@@ -182,46 +182,30 @@ function trackPartials(frames, sampleRate) {
return partials;
}
-// Fit cubic bezier curve to trajectory
+// Fit cubic bezier curve to trajectory using Catmull-Rom tangents.
+// Estimates end tangents via forward/backward differences, then converts
+// Hermite form to Bezier: B1 = P0 + m0*dt/3, B2 = P3 - m3*dt/3.
+// This guarantees the curve passes through both endpoints.
function fitBezier(times, values) {
- if (times.length < 4) {
- // Not enough points, just use linear segments
- return {
- t0: times[0], v0: values[0],
- t1: times[0], v1: values[0],
- t2: times[times.length-1], v2: values[values.length-1],
- t3: times[times.length-1], v3: values[values.length-1]
- };
- }
-
- // Fix endpoints
- const t0 = times[0];
- const t3 = times[times.length - 1];
- const v0 = values[0];
- const v3 = values[values.length - 1];
+ const n = times.length - 1;
+ const t0 = times[0], v0 = values[0];
+ const t3 = times[n], v3 = values[n];
+ const dt = t3 - t0;
- // Solve for interior control points via least squares
- // Simplification: place at 1/3 and 2/3 positions
- const t1 = t0 + (t3 - t0) / 3;
- const t2 = t0 + 2 * (t3 - t0) / 3;
+ if (dt <= 0 || n === 0) {
+ return {t0, v0, t1: t0, v1: v0, t2: t3, v2: v3, t3, v3};
+ }
- // Find v1, v2 by evaluating at nearest data points
- let v1 = v0, v2 = v3;
- let minDist1 = Infinity, minDist2 = Infinity;
+ // Catmull-Rom endpoint tangents (forward diff at start, backward at end)
+ const dt0 = times[1] - times[0];
+ const m0 = dt0 > 0 ? (values[1] - values[0]) / dt0 : 0;
- for (let i = 0; i < times.length; ++i) {
- const dist1 = Math.abs(times[i] - t1);
- const dist2 = Math.abs(times[i] - t2);
+ const dtn = times[n] - times[n - 1];
+ const m3 = dtn > 0 ? (values[n] - values[n - 1]) / dtn : 0;
- if (dist1 < minDist1) {
- minDist1 = dist1;
- v1 = values[i];
- }
- if (dist2 < minDist2) {
- minDist2 = dist2;
- v2 = values[i];
- }
- }
+ // Hermite -> Bezier control points
+ const v1 = v0 + m0 * dt / 3;
+ const v2 = v3 - m3 * dt / 3;
- return {t0, v0, t1, v1, t2, v2, t3, v3};
+ return {t0, v0, t1: t0 + dt / 3, v1, t2: t0 + 2 * dt / 3, v2, t3, v3};
}