curves now sending/receiving correctly. Y values are now converted to Int16 values to allow handles to dip below 0

node_mode
realrobots 2025-10-10 23:11:10 +08:00
parent d67cba4c6f
commit 55b2c178e6
3 changed files with 56 additions and 26 deletions

View File

@ -8,6 +8,8 @@ export class CurveEditor {
this.offset = { x: 0, y: 0 }; this.offset = { x: 0, y: 0 };
this.pixelsPerSecond = 48; this.pixelsPerSecond = 48;
this.exportRange = [0, 4095];
this.currentTime = timelineLength * this.pixelsPerSecond / 2; this.currentTime = timelineLength * this.pixelsPerSecond / 2;
this.motorButtons = { this.motorButtons = {
@ -40,15 +42,15 @@ export class CurveEditor {
this.panStart = { x: 0, y: 0 }; this.panStart = { x: 0, y: 0 };
// Default curve // Default curve
// this.setCurves([ this.setCurves([
// { {
// startPoint: { x: this.valueToX(0), y: this.valueToY(0) }, startPoint: { x: this.valueToX(0), y: this.valueToY(0) },
// startPointHandle: { x: this.valueToX(timelineLength / 2), y: this.valueToY(0.5) }, startPointHandle: { x: this.valueToX(timelineLength / 2), y: this.valueToY(0.5) },
// endPointHandle: { x: this.valueToX(timelineLength / 2), y: this.valueToY(-0.5) }, endPointHandle: { x: this.valueToX(timelineLength / 2), y: this.valueToY(-0.5) },
// endPoint: { x: this.valueToX(timelineLength), y: this.valueToY(0) } endPoint: { x: this.valueToX(timelineLength), y: this.valueToY(0) }
// } }
// ]); ]);
// this.curveSets[this.selectedMotorID] = this.curves; this.curveSets[this.selectedMotorID] = this.curves;
// let othercurves = [ // let othercurves = [
// { // {
@ -71,14 +73,14 @@ export class CurveEditor {
// this.curveSets[7] = othercurves; // this.curveSets[7] = othercurves;
// this.setSelectedMotor(5); this.setSelectedMotor(4);
this.initEvents(); this.initEvents();
this.draw(); this.draw();
console.log(this.curveSets); console.log(this.curveSets);
} }
setLength(endTime){ setLength(endTime) {
this.timelineLength = endTime / this.pixelsPerSecond; this.timelineLength = endTime / this.pixelsPerSecond;
console.log("new endtime: " + endTime); console.log("new endtime: " + endTime);
//this.currentTime = this.timelineLength * this.pixelsPerSecond / 2; //this.currentTime = this.timelineLength * this.pixelsPerSecond / 2;
@ -115,10 +117,31 @@ export class CurveEditor {
return (this.canvas.height / 2 - v * (this.canvas.height / 2)) * this.scale + this.offset.y; return (this.canvas.height / 2 - v * (this.canvas.height / 2)) * this.scale + this.offset.y;
} }
// Maps pixel value of y axis to -1 to 1 normalised value
yToValue(y) { yToValue(y) {
return ((this.canvas.height / 2 - (y - this.offset.y) / this.scale) / (this.canvas.height / 2)); return ((this.canvas.height / 2 - (y - this.offset.y) / this.scale) / (this.canvas.height / 2));
} }
// Maps normalised -1 to 1 value to motor range (0, 4095)
yToExportRange(y) {
const [minOut, maxOut] = this.exportRange;
// Normalize y from [-1, 1] to [0, 1]
const normalized = (this.yToValue(y) + 1) / 2;
// Scale to export range
return Math.round(normalized * (maxOut - minOut) + minOut);
}
exportRangeToY(value) {
const [minOut, maxOut] = this.exportRange;
// Reverse the scaling
const normalized = (value - minOut) / (maxOut - minOut);
// Convert from [0, 1] back to [-1, 1]
const yValue = normalized * 2 - 1;
// Apply inverse mapping from value space to editor Y
return this.valueToY(yValue);
}
valueToX(t) { valueToX(t) {
return t * this.pixelsPerSecond * this.scale + this.offset.x; return t * this.pixelsPerSecond * this.scale + this.offset.x;
} }
@ -635,6 +658,8 @@ export class CurveEditor {
curve.startPointHandle.y += deltaY; curve.startPointHandle.y += deltaY;
this.dragControlPoint(curve, 'startPointHandle', curve.startPointHandle.x, curve.startPointHandle.y, index); this.dragControlPoint(curve, 'startPointHandle', curve.startPointHandle.x, curve.startPointHandle.y, index);
console.log(this.yToExportRange(curve.startPoint.y));
} else if (key === 'endPoint') { } else if (key === 'endPoint') {
const oldX = curve.endPoint.x; const oldX = curve.endPoint.x;
const oldY = curve.endPoint.y; const oldY = curve.endPoint.y;
@ -660,6 +685,7 @@ export class CurveEditor {
this.dragControlPoint(curve, 'endPointHandle', curve.endPointHandle.x, curve.endPointHandle.y, index); this.dragControlPoint(curve, 'endPointHandle', curve.endPointHandle.x, curve.endPointHandle.y, index);
const nextCurve = this.curves[index + 1]; const nextCurve = this.curves[index + 1];
console.log(curve.endPoint.y);
if (nextCurve) { if (nextCurve) {
nextCurve.startPoint.x = curve.endPoint.x; nextCurve.startPoint.x = curve.endPoint.x;
nextCurve.startPoint.y = curve.endPoint.y; nextCurve.startPoint.y = curve.endPoint.y;

View File

@ -155,7 +155,7 @@
<div class="tab-pane fade" id="animation" role="tabpanel" aria-labelledby="animation-tab"> <div class="tab-pane fade" id="animation" role="tabpanel" aria-labelledby="animation-tab">
<canvas id="curveCanvas" width="900" height="300"></canvas> <canvas id="curveCanvas" width="900" height="600"></canvas>
<div style="margin-top: 10px; text-align: center;"> <div style="margin-top: 10px; text-align: center;">
<input type="range" id="timeSlider" min="0" step="1" style="width: 80%;"> <input type="range" id="timeSlider" min="0" step="1" style="width: 80%;">
</div> </div>

View File

@ -424,34 +424,34 @@ window.onload = () => {
offset += 1; offset += 1;
const startTime = view.getUint16(offset, true); offset += 2; const startTime = view.getUint16(offset, true); offset += 2;
const endTime = view.getUint16(offset, true); offset += 2; const endTime = view.getUint16(offset, true); offset += 2;
const startPointY = view.getUint16(offset, true); offset += 2; const startPointY = view.getInt16(offset, true); offset += 2;
const startHandleX = view.getUint16(offset, true); offset += 2; const startHandleX = view.getUint16(offset, true); offset += 2;
const startHandleY = view.getUint16(offset, true); offset += 2; const startHandleY = view.getInt16(offset, true); offset += 2;
const endHandleX = view.getUint16(offset, true); offset += 2; const endHandleX = view.getUint16(offset, true); offset += 2;
const endHandleY = view.getUint16(offset, true); offset += 2; const endHandleY = view.getInt16(offset, true); offset += 2;
const endPointY = view.getUint16(offset, true); offset += 2; const endPointY = view.getInt16(offset, true); offset += 2;
console.log("RECEIVED VALUES RAW:"); console.log("RECEIVED VALUES RAW:");
console.log(startTime, endTime); console.log(startTime, endTime, startPointY, endPointY);
const toFloat = v => (v / 65535) * 2 - 1; const toFloat = v => (v / 65535) * 2 - 1;
const curve = { const curve = {
startPoint: { startPoint: {
x: startTime, x: startTime,
y: curveEditor.valueToY(toFloat(startPointY)) y: curveEditor.exportRangeToY(startPointY)
}, },
startPointHandle: { startPointHandle: {
x: startHandleX, x: startHandleX,
y: curveEditor.valueToY(toFloat(startHandleY)) y: curveEditor.exportRangeToY(startHandleY)
}, },
endPointHandle: { endPointHandle: {
x: endHandleX, x: endHandleX,
y: curveEditor.valueToY(toFloat(endHandleY)) y: curveEditor.exportRangeToY(endHandleY)
}, },
endPoint: { endPoint: {
x: endTime, x: endTime,
y: curveEditor.valueToY(toFloat(endPointY)) y: curveEditor.exportRangeToY(endPointY)
} }
}; };
@ -561,23 +561,27 @@ window.onload = () => {
view.setUint16(offset, curveCount, true); offset += 2; view.setUint16(offset, curveCount, true); offset += 2;
// 🔹 Curve segments // 🔹 Curve segments
curveSegments.forEach(seg => { curveSegments.forEach(seg => {
view.setUint8(offset++, seg.motorID); view.setUint8(offset++, seg.motorID);
view.setUint16(offset, seg.startTime, true); offset += 2; view.setUint16(offset, seg.startTime, true); offset += 2;
view.setUint16(offset, seg.endTime, true); offset += 2; view.setUint16(offset, seg.endTime, true); offset += 2;
view.setUint16(offset, seg.startPointY, true); offset += 2; view.setInt16(offset, curveEditor.yToExportRange(seg.startPointY), true); offset += 2;
console.log(curveEditor.yToExportRange(seg.startPointY));
view.setUint16(offset, seg.startHandleX, true); offset += 2; view.setUint16(offset, seg.startHandleX, true); offset += 2;
view.setUint16(offset, seg.startHandleY, true); offset += 2; view.setInt16(offset, curveEditor.yToExportRange(seg.startHandleY), true); offset += 2;
console.log("STARTHANDLEY: " + seg.startHandleY);
view.setUint16(offset, seg.endHandleX, true); offset += 2; view.setUint16(offset, seg.endHandleX, true); offset += 2;
view.setUint16(offset, seg.endHandleY, true); offset += 2; view.setInt16(offset, curveEditor.yToExportRange(seg.endHandleY), true); offset += 2;
view.setUint16(offset, seg.endPointY, true); offset += 2; view.setInt16(offset, curveEditor.yToExportRange(seg.endPointY), true); offset += 2;
}); });
console.log("🧵 Curve segments packed:", curveSegments.length); console.log("🧵 Curve segments packed:", curveSegments.length);
console.log(curveSegments); console.log(curveSegments);
// 🔹 Send to ESP32 // 🔹 Send to ESP32
const payload = new Uint8Array(buffer); const payload = new Uint8Array(buffer);
console.log()
console.log(payload); console.log(payload);
serial.saveFile(payload); // CMD_SAVE_ANIMATION serial.saveFile(payload); // CMD_SAVE_ANIMATION
} }