curve animation save/load implemented (needs troubleshooting, y axis flattening somewher

node_mode
Jake 2025-10-09 00:05:02 +08:00
parent 8698f47536
commit d67cba4c6f
2 changed files with 52 additions and 42 deletions

View File

@ -26,7 +26,7 @@ export class CurveEditor {
slider.value = 0; slider.value = 0;
slider.addEventListener('input', () => { slider.addEventListener('input', () => {
this.currentTime = parseFloat(slider.value); this.currentTime = parseFloat(slider.value);
console.log(slider.value); //console.log(slider.value);
this.draw(); this.draw();
}); });

View File

@ -417,6 +417,7 @@ window.onload = () => {
const SEGMENT_SIZE = 17; const SEGMENT_SIZE = 17;
const curveSets = {}; const curveSets = {};
let latestEndTime = 1; let latestEndTime = 1;
console.log(raw);
for (let i = 0; i < curveCount; i++) { for (let i = 0; i < curveCount; i++) {
const motorID = 10; const motorID = 10;
@ -430,6 +431,9 @@ window.onload = () => {
const endHandleY = view.getUint16(offset, true); offset += 2; const endHandleY = view.getUint16(offset, true); offset += 2;
const endPointY = view.getUint16(offset, true); offset += 2; const endPointY = view.getUint16(offset, true); offset += 2;
console.log("RECEIVED VALUES RAW:");
console.log(startTime, endTime);
const toFloat = v => (v / 65535) * 2 - 1; const toFloat = v => (v / 65535) * 2 - 1;
const curve = { const curve = {
@ -498,27 +502,43 @@ window.onload = () => {
const filename = sanitizeFilename(rawInput); const filename = sanitizeFilename(rawInput);
console.log("Sanitized filename:", filename); console.log("Sanitized filename:", filename);
const frameCount = 800; // or whatever your timeline length is
const frameCount = 500;
const numChannels = 5;
const frameRate = 50; const frameRate = 50;
const version = 1; const version = 1;
const headerSize = 16; const headerSize = 16;
const frameDataSize = frameCount * numChannels * 2; const curveSegments = [];
const keyframeCount = dialKeyframes.reduce((sum, channel) => {
return sum + Object.keys(channel).length;
}, 0);
const keyframeDataSize = keyframeCount * 5;
// 🔹 Filename encoding // 🔹 Collect all curve segments
Object.entries(curveEditor.curveSets).forEach(([motorIDStr, segments]) => {
const motorID = parseInt(motorIDStr, 10);
segments.forEach(segment => {
const toTime = x => Math.max(0, Math.round(curveEditor.xToValue(x) * 100)); // seconds → centiseconds
const toUint16 = v => {
const normalized = Math.max(-1, Math.min(1, v)); // clamp to [-1, 1]
return Math.round((normalized + 1) / 2 * 65535);
};
curveSegments.push({
motorID,
startTime: segment.startPoint.x,
endTime: segment.endPoint.x,
startPointY: segment.startPoint.y,
startHandleX: segment.startPointHandle.x,
startHandleY: segment.startPointHandle.y,
endHandleX: segment.endPointHandle.x,
endHandleY: segment.endPointHandle.y,
endPointY: segment.endPoint.y
});
});
});
const curveCount = curveSegments.length;
const segmentSize = 17;
const filenameBytes = new TextEncoder().encode(filename); const filenameBytes = new TextEncoder().encode(filename);
const filenameLength = filenameBytes.length; const filenameLength = filenameBytes.length;
const totalSize = 2 + filenameLength + headerSize + 2 + curveCount * segmentSize;
// Total packet size
const totalSize = 2 + filenameLength + headerSize + 2 + keyframeDataSize;
const buffer = new ArrayBuffer(totalSize); const buffer = new ArrayBuffer(totalSize);
const view = new DataView(buffer); const view = new DataView(buffer);
let offset = 0; let offset = 0;
@ -527,7 +547,6 @@ window.onload = () => {
view.setUint16(offset, filenameLength, true); offset += 2; view.setUint16(offset, filenameLength, true); offset += 2;
filenameBytes.forEach(byte => view.setUint8(offset++, byte)); filenameBytes.forEach(byte => view.setUint8(offset++, byte));
// 🔹 Header // 🔹 Header
view.setUint8(offset++, "A".charCodeAt(0)); view.setUint8(offset++, "A".charCodeAt(0));
view.setUint8(offset++, "N".charCodeAt(0)); view.setUint8(offset++, "N".charCodeAt(0));
@ -538,41 +557,32 @@ window.onload = () => {
view.setUint8(offset++, frameRate); view.setUint8(offset++, frameRate);
offset += 8; // reserved offset += 8; // reserved
// 🔹 Frame data (all zeroes) // 🔹 Curve count
//offset += frameDataSize; view.setUint16(offset, curveCount, true); offset += 2;
// 🔹 Keyframe count // 🔹 Curve segments
view.setUint16(offset, keyframeCount, true); offset += 2; curveSegments.forEach(seg => {
view.setUint8(offset++, seg.motorID);
const keyframeList = []; view.setUint16(offset, seg.startTime, true); offset += 2;
view.setUint16(offset, seg.endTime, true); offset += 2;
view.setUint16(offset, seg.startPointY, true); offset += 2;
Object.entries(dialKeyframes).forEach(([motorIdStr, frameMap]) => { view.setUint16(offset, seg.startHandleX, true); offset += 2;
const motorId = parseInt(motorIdStr, 10); view.setUint16(offset, seg.startHandleY, true); offset += 2;
console.log("STARTHANDLEY: " + seg.startHandleY);
Object.entries(frameMap).forEach(([frameStr, position]) => { view.setUint16(offset, seg.endHandleX, true); offset += 2;
const frame = parseInt(frameStr, 10); view.setUint16(offset, seg.endHandleY, true); offset += 2;
if (position !== undefined) { view.setUint16(offset, seg.endPointY, true); offset += 2;
keyframeList.push({ motorId, frame, position });
}
});
}); });
console.log("🧵 Curve segments packed:", curveSegments.length);
// 🔹 Keyframes console.log(curveSegments);
keyframeList.forEach(({ motorId, frame, position }) => {
view.setUint8(offset++, motorId);
view.setUint16(offset, frame, true); offset += 2;
view.setUint16(offset, position, true); offset += 2;
});
console.log("Keyframe count: " + keyframeCount);
console.log(keyframeList);
// 🔹 Send to ESP32 // 🔹 Send to ESP32
const payload = new Uint8Array(buffer); const payload = new Uint8Array(buffer);
console.log(payload);
serial.saveFile(payload); // CMD_SAVE_ANIMATION serial.saveFile(payload); // CMD_SAVE_ANIMATION
} }
async function sendDeleteToESP32() { async function sendDeleteToESP32() {
if (!selectedFile) return; if (!selectedFile) return;