From a6d901d3f0062ad3971d5aa42a83ef425c47f215 Mon Sep 17 00:00:00 2001 From: Jake Wilkinson Date: Mon, 24 Nov 2025 14:24:11 +0800 Subject: [PATCH] tweaked curve point dragging thresholds --- curveEditor.js | 223 ++++++++++++++++++++++++------------------------- serial.js | 2 +- 2 files changed, 111 insertions(+), 114 deletions(-) diff --git a/curveEditor.js b/curveEditor.js index c52e611..49a0e6f 100644 --- a/curveEditor.js +++ b/curveEditor.js @@ -663,26 +663,26 @@ export class CurveEditor { } for (let curve of this.curves) { - // Pass 1: check handles first - for (let key of ['startPointHandle', 'endPointHandle']) { - const p = curve[key]; - if (Math.hypot(p.x - mouse.x, p.y - mouse.y) < 10 / this.scale) { - this.dragging = { curve, key }; - return; - } - } -} + // Pass 1: check handles first + for (let key of ['startPointHandle', 'endPointHandle']) { + const p = curve[key]; + if (Math.hypot(p.x - mouse.x, p.y - mouse.y) < 10 / this.scale) { + this.dragging = { curve, key }; + return; + } + } + } -// If no handle was hit, then check points -for (let curve of this.curves) { - for (let key of ['startPoint', 'endPoint']) { - const p = curve[key]; - if (Math.hypot(p.x - mouse.x, p.y - mouse.y) < 10 / this.scale) { - this.dragging = { curve, key }; - return; - } - } -} + // If no handle was hit, then check points + for (let curve of this.curves) { + for (let key of ['startPoint', 'endPoint']) { + const p = curve[key]; + if (Math.hypot(p.x - mouse.x, p.y - mouse.y) < 10 / this.scale) { + this.dragging = { curve, key }; + return; + } + } + } }); @@ -771,106 +771,103 @@ for (let curve of this.curves) { } splitCurveAtTime(motorID, timeX, yPosition = null) { - const curves = this.curveSets[motorID]; - if (!curves) return; + const curves = this.curveSets[motorID]; + if (!curves) return; - const tolerance = 2; // pixels/frames tolerance for matching existing points + const tolerance = 4; // pixels/frames tolerance for matching existing points - for (let i = 0; i < curves.length; i++) { - const curve = curves[i]; + for (let i = 0; i < curves.length; i++) { + const curve = curves[i]; - // --- check startPoint --- - if (Math.abs(curve.startPoint.x - timeX) < tolerance) { - if (yPosition !== null) { - // move existing point - const yEditor = (yPosition * 800 / 4095); - curve.startPoint.y = yEditor; - if (curve.startPointHandle) curve.startPointHandle.y = yEditor; - } else { - // remove point by merging with previous - if (i > 0) { - const prev = curves[i - 1]; - const merged = { - startPoint: prev.startPoint, - startPointHandle: prev.startPointHandle, - endPointHandle: curve.endPointHandle, - endPoint: curve.endPoint - }; - curves.splice(i - 1, 2, merged); - } else { - curves.splice(i, 1); // drop if it's the very first - } - } - this.curveSets[motorID] = curves; - this.draw(); - return; - } - - // --- check endPoint --- - if (Math.abs(curve.endPoint.x - timeX) < tolerance) { - if (yPosition !== null) { - // move existing point - const yEditor = (yPosition * 800 / 4095); - curve.endPoint.y = yEditor; - if (curve.endPointHandle) curve.endPointHandle.y = yEditor; - } else { - // remove point by merging with next - if (i < curves.length - 1) { - const next = curves[i + 1]; - const merged = { - startPoint: curve.startPoint, - startPointHandle: curve.startPointHandle, - endPointHandle: next.endPointHandle, - endPoint: next.endPoint - }; - curves.splice(i, 2, merged); - } else { - curves.splice(i, 1); // drop if it's the very last - } - } - this.curveSets[motorID] = curves; - this.draw(); - return; - } - - // --- split inside curve if timeX lies between start and end --- - const x0 = curve.startPoint.x; - const x3 = curve.endPoint.x; - - if (timeX > x0 && timeX < x3) { - // binary search for t where curve.x ≈ timeX - let t = 0.5, minT = 0, maxT = 1; - for (let j = 0; j < 10; j++) { - const pt = this.cubicBezier( - t, - curve.startPoint, - curve.startPointHandle, - curve.endPointHandle, - curve.endPoint - ); - if (pt.x < timeX) minT = t; - else maxT = t; - t = (minT + maxT) / 2; - } - - let [left, right] = this.splitCurve(curve, t); - - if (yPosition !== null) { - const yEditor = (yPosition * 800 / 4095); - left.endPoint.y = yEditor; - right.startPoint.y = yEditor; - if (left.endPointHandle) left.endPointHandle.y = yEditor; - if (right.startPointHandle) right.startPointHandle.y = yEditor; - } - - curves.splice(i, 1, left, right); - this.curveSets[motorID] = curves; - this.draw(); - return; - } + // --- check startPoint --- + if (Math.abs(curve.startPoint.x - timeX) < tolerance) { + if (yPosition !== null) { + // move existing point + const yEditor = (yPosition * 800 / 4095); + curve.startPoint.y = yEditor; + if (curve.startPointHandle) curve.startPointHandle.y = yEditor; + } else { + // 🚫 don't allow deleting the very first control point + if (i > 0) { + const prev = curves[i - 1]; + const merged = { + startPoint: prev.startPoint, + startPointHandle: prev.startPointHandle, + endPointHandle: curve.endPointHandle, + endPoint: curve.endPoint + }; + curves.splice(i - 1, 2, merged); } + } + this.curveSets[motorID] = curves; + this.draw(); + return; } + // --- check endPoint --- + if (Math.abs(curve.endPoint.x - timeX) < tolerance) { + if (yPosition !== null) { + // move existing point + const yEditor = (yPosition * 800 / 4095); + curve.endPoint.y = yEditor; + if (curve.endPointHandle) curve.endPointHandle.y = yEditor; + } else { + // 🚫 don't allow deleting the very last control point + if (i < curves.length - 1) { + const next = curves[i + 1]; + const merged = { + startPoint: curve.startPoint, + startPointHandle: curve.startPointHandle, + endPointHandle: next.endPointHandle, + endPoint: next.endPoint + }; + curves.splice(i, 2, merged); + } + } + this.curveSets[motorID] = curves; + this.draw(); + return; + } + + // --- split inside curve if timeX lies between start and end --- + const x0 = curve.startPoint.x; + const x3 = curve.endPoint.x; + + if (timeX > x0 && timeX < x3) { + // binary search for t where curve.x ≈ timeX + let t = 0.5, minT = 0, maxT = 1; + for (let j = 0; j < 10; j++) { + const pt = this.cubicBezier( + t, + curve.startPoint, + curve.startPointHandle, + curve.endPointHandle, + curve.endPoint + ); + if (pt.x < timeX) minT = t; + else maxT = t; + t = (minT + maxT) / 2; + } + + let [left, right] = this.splitCurve(curve, t); + + if (yPosition !== null) { + const yEditor = (yPosition * 800 / 4095); + left.endPoint.y = yEditor; + right.startPoint.y = yEditor; + if (left.endPointHandle) left.endPointHandle.y = yEditor; + if (right.startPointHandle) right.startPointHandle.y = yEditor; + } + + curves.splice(i, 1, left, right); + this.curveSets[motorID] = curves; + this.draw(); + return; + } + } +} + + adjustAllCurvesToDuration(newTime) { for (const [motorID, curves] of Object.entries(this.curveSets)) { diff --git a/serial.js b/serial.js index 137a116..0a0d078 100644 --- a/serial.js +++ b/serial.js @@ -43,7 +43,7 @@ export class SerialManager { } const message = [HEADER1, HEADER2, commandCode, lengthHigh, lengthLow, ...payload, checksum]; - //console.log(new Uint8Array(message)); + console.log(new Uint8Array(message)); await this.writer.write(new Uint8Array(message)); }