a few interface tweaks
parent
5a8dbc28a7
commit
105db83017
224
curveEditor.js
224
curveEditor.js
|
|
@ -11,6 +11,14 @@ export class CurveEditor {
|
||||||
this.scaleX = 1;
|
this.scaleX = 1;
|
||||||
this.scaleY = 1;
|
this.scaleY = 1;
|
||||||
this.offset = { x: 0, y: 0 };
|
this.offset = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
this.scaleX = 1.66;
|
||||||
|
this.scaleY = 0.41;
|
||||||
|
this.offset.x = 177;
|
||||||
|
this.offset.y = 38;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.pixelsPerSecond = 48;
|
this.pixelsPerSecond = 48;
|
||||||
|
|
||||||
this.exportRange = [0, 4095];
|
this.exportRange = [0, 4095];
|
||||||
|
|
@ -69,8 +77,8 @@ export class CurveEditor {
|
||||||
const curveSegments = [];
|
const curveSegments = [];
|
||||||
Object.entries(this.curveSets).forEach(([motorIDStr, segments]) => {
|
Object.entries(this.curveSets).forEach(([motorIDStr, segments]) => {
|
||||||
const motorID = parseInt(motorIDStr, 10);
|
const motorID = parseInt(motorIDStr, 10);
|
||||||
segments.forEach(segment => {
|
|
||||||
|
|
||||||
|
segments.forEach(segment => {
|
||||||
curveSegments.push({
|
curveSegments.push({
|
||||||
motorID,
|
motorID,
|
||||||
startTime: segment.startPoint.x,
|
startTime: segment.startPoint.x,
|
||||||
|
|
@ -84,14 +92,14 @@ export class CurveEditor {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
console.log(curveSegments);
|
//console.log(curveSegments);
|
||||||
const curveCount = curveSegments.length;
|
const curveCount = curveSegments.length;
|
||||||
view.setUint16(offset, curveCount, true); offset += 2;
|
view.setUint16(offset, curveCount, true); offset += 2;
|
||||||
|
|
||||||
// 🔹 Curve segments
|
// 🔹 Curve segments
|
||||||
|
|
||||||
curveSegments.forEach(seg => {
|
curveSegments.forEach(seg => {
|
||||||
console.log(offset, seg.motorID);
|
//console.log(offset, seg.motorID);
|
||||||
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;
|
||||||
|
|
@ -101,7 +109,7 @@ export class CurveEditor {
|
||||||
view.setUint16(offset, seg.endHandleX, true); offset += 2;
|
view.setUint16(offset, seg.endHandleX, true); offset += 2;
|
||||||
view.setInt16(offset, this.yToExportRange(seg.endHandleY), true); offset += 2;
|
view.setInt16(offset, this.yToExportRange(seg.endHandleY), true); offset += 2;
|
||||||
view.setInt16(offset, this.yToExportRange(seg.endPointY), true); offset += 2;
|
view.setInt16(offset, this.yToExportRange(seg.endPointY), true); offset += 2;
|
||||||
console.log(seg.startPointY, seg.endPointY);
|
//console.log(seg.startPointY, seg.endPointY);
|
||||||
console.log(this.yToExportRange(seg.startPointY), this.yToExportRange(seg.endPointY));
|
console.log(this.yToExportRange(seg.startPointY), this.yToExportRange(seg.endPointY));
|
||||||
});
|
});
|
||||||
//console.log("🧵 Curve segments packed:", curveSegments.length);
|
//console.log("🧵 Curve segments packed:", curveSegments.length);
|
||||||
|
|
@ -190,7 +198,7 @@ export class CurveEditor {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
valueToYNoOffset(v) {
|
valueToYNoOffset(v) {
|
||||||
return (this.canvas.height / 2 - v * (this.canvas.height / 2));
|
return (this.logicalHeight / 2) * (1 - v); // Keep mapping logic as is
|
||||||
}
|
}
|
||||||
|
|
||||||
yToValueNoOffset(y) {
|
yToValueNoOffset(y) {
|
||||||
|
|
@ -206,42 +214,44 @@ export class CurveEditor {
|
||||||
return ((this.logicalHeight / 2 - (y - this.offset.y) / this.scaleY) / (this.logicalHeight / 2));
|
return ((this.logicalHeight / 2 - (y - this.offset.y) / this.scaleY) / (this.logicalHeight / 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
yToMotorPosition(yCanvas) {
|
||||||
|
const [minOut, maxOut] = this.exportRange; // Output bounds (0 - 4095)
|
||||||
|
|
||||||
// Maps normalised -1 to 1 value to motor range (0, 4095)
|
// Transform yCanvas to the original logical space by adjusting for the current offset and scale.
|
||||||
yToExportRange(yCanvas) {
|
const yLogical = (yCanvas - this.offset.y) / this.scaleY;
|
||||||
|
|
||||||
|
// Normalize the logical value based on the actual height of the logical range
|
||||||
|
const normalized = yLogical / 800; // This assumes yLogical ranges from 0 to 800
|
||||||
|
|
||||||
|
// Flip normalized to convert to desired motor range
|
||||||
|
const flipped = normalized; // This gives the inverted mapping
|
||||||
|
|
||||||
|
// Calculate the final motor position
|
||||||
|
return Math.round(flipped * (maxOut - minOut) + minOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// exportRangeToY(value) {
|
||||||
|
// const [minOut, maxOut] = this.exportRange;
|
||||||
|
// const normalized = 1 - (value - minOut) / (maxOut - minOut);
|
||||||
|
// const logical = normalized * 2 - 1;
|
||||||
|
// const yLogical = this.valueToYNoOffset(logical);
|
||||||
|
// return yLogical * this.scaleY + this.offset.y;
|
||||||
|
// }
|
||||||
|
|
||||||
|
yToExportRange(y) {
|
||||||
const [minOut, maxOut] = this.exportRange;
|
const [minOut, maxOut] = this.exportRange;
|
||||||
|
const clampedY = Math.max(0, Math.min(y, this.logicalHeight)); // optional safety
|
||||||
// undo offset + scale to get logical Y
|
const normalized = clampedY / this.logicalHeight; // maps to [0, 1]
|
||||||
const yLogical = (yCanvas - this.offset.y) / this.scaleY;
|
return Math.round(normalized * (maxOut - minOut) + minOut); // maps to [minOut, maxOut]
|
||||||
|
|
||||||
// invert valueToYNoOffset: get back logical [-1,1]
|
|
||||||
const logical = this.yToValueNoOffset(yLogical);
|
|
||||||
|
|
||||||
// map logical [-1,1] → normalized [0,1]
|
|
||||||
const normalized = (logical + 1) / 2;
|
|
||||||
|
|
||||||
// flip back (since exportRangeToY used 1 - normalized)
|
|
||||||
const flipped = 1 - normalized;
|
|
||||||
|
|
||||||
// map to export range
|
|
||||||
return (flipped * (maxOut - minOut) + minOut)/2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
exportRangeToY(value) {
|
exportRangeToY(value) {
|
||||||
const [minOut, maxOut] = this.exportRange;
|
const [minOut, maxOut] = this.exportRange;
|
||||||
|
// Inverted normalization
|
||||||
// normalize value into [0,1], flipped
|
|
||||||
const normalized = 1 - (value - minOut) / (maxOut - minOut);
|
const normalized = 1 - (value - minOut) / (maxOut - minOut);
|
||||||
|
return this.valueToYNoOffset(normalized * 2 - 1); // maps [0, 1] to [-1, 1]
|
||||||
// map into logical [-1,1]
|
|
||||||
const logical = normalized * 2 - 1;
|
|
||||||
|
|
||||||
// convert logical → logical Y
|
|
||||||
const yLogical = this.valueToYNoOffset(logical);
|
|
||||||
|
|
||||||
// apply scale + offset to get canvas Y
|
|
||||||
return yLogical * this.scaleY + this.offset.y;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -406,7 +416,7 @@ export class CurveEditor {
|
||||||
if (currentTimeX >= minX && currentTimeX <= maxX) {
|
if (currentTimeX >= minX && currentTimeX <= maxX) {
|
||||||
const t = this.solveTForX(currentTimeX, p0, h0, h1, p1);
|
const t = this.solveTForX(currentTimeX, p0, h0, h1, p1);
|
||||||
const pt = this.cubicBezier(t, p0, h0, h1, p1);
|
const pt = this.cubicBezier(t, p0, h0, h1, p1);
|
||||||
return this.yToExportRange(pt.y); // or pt.y if you want raw canvas Y
|
return this.yToMotorPosition(pt.y); // or pt.y if you want raw canvas Y
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -731,7 +741,7 @@ export class CurveEditor {
|
||||||
|
|
||||||
this.offset.x += (after.x - before.x) * this.scaleX;
|
this.offset.x += (after.x - before.x) * this.scaleX;
|
||||||
this.offset.y += (after.y - before.y) * this.scaleY;
|
this.offset.y += (after.y - before.y) * this.scaleY;
|
||||||
|
console.log(this.scaleX, this.scaleY, this.offset.x, this.offset.y);
|
||||||
this.draw();
|
this.draw();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -748,88 +758,74 @@ export class CurveEditor {
|
||||||
}
|
}
|
||||||
|
|
||||||
splitCurveAtTime(motorID, timeX, yPosition = null) {
|
splitCurveAtTime(motorID, timeX, yPosition = null) {
|
||||||
const curves = this.curveSets[motorID];
|
const curves = this.curveSets[motorID];
|
||||||
if (!curves) return;
|
if (!curves) return;
|
||||||
|
|
||||||
for (let i = 0; i < curves.length; i++) {
|
for (let i = 0; i < curves.length; i++) {
|
||||||
const curve = curves[i];
|
const curve = curves[i];
|
||||||
|
|
||||||
// --- merge with previous if near startPoint ---
|
// --- if timeX matches an existing startPoint ---
|
||||||
if (i > 0 && Math.abs(curve.startPoint.x - timeX) < 1e-3) {
|
if (Math.abs(curve.startPoint.x - timeX) < 1e-3) {
|
||||||
const prev = curves[i - 1];
|
if (yPosition !== null) {
|
||||||
const merged = {
|
const yEditor = (yPosition * 800 / 4095);
|
||||||
startPoint: prev.startPoint,
|
curve.startPoint.y = yEditor;
|
||||||
startPointHandle: prev.startPointHandle,
|
if (curve.startPointHandle) curve.startPointHandle.y = yEditor;
|
||||||
endPointHandle: curve.endPointHandle,
|
}
|
||||||
endPoint: curve.endPoint
|
this.draw();
|
||||||
};
|
return;
|
||||||
curves.splice(i - 1, 2, merged);
|
|
||||||
this.curveSets[motorID] = curves;
|
|
||||||
this.draw();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- merge with next if near endPoint ---
|
|
||||||
if (i < curves.length - 1 && Math.abs(curve.endPoint.x - timeX) < 1e-3) {
|
|
||||||
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 provided, override the split point’s Y
|
|
||||||
if (yPosition !== null) {
|
|
||||||
const yEditor = yPosition * 800 / 4095
|
|
||||||
|
|
||||||
// adjust the shared split point
|
|
||||||
left.endPoint.y = yEditor;
|
|
||||||
right.startPoint.y = yEditor;
|
|
||||||
|
|
||||||
// ✅ flatten handles to same Y
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- if timeX matches an existing endPoint ---
|
||||||
|
if (Math.abs(curve.endPoint.x - timeX) < 1e-3) {
|
||||||
|
if (yPosition !== null) {
|
||||||
|
const yEditor = (yPosition * 800 / 4095);
|
||||||
|
curve.endPoint.y = yEditor;
|
||||||
|
if (curve.endPointHandle) curve.endPointHandle.y = yEditor;
|
||||||
|
}
|
||||||
|
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 provided, override the split point’s Y
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ export const NODE_TYPES = {
|
||||||
};
|
};
|
||||||
|
|
||||||
function GetNodeType(node) {
|
function GetNodeType(node) {
|
||||||
console.log(node.constructor.name);
|
//console.log(node.constructor.name);
|
||||||
switch (node.constructor.name) {
|
switch (node.constructor.name) {
|
||||||
case "ServoNode":
|
case "ServoNode":
|
||||||
return NODE_TYPES.Servo;
|
return NODE_TYPES.Servo;
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,12 @@ const SyncMode = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export class URDFEditor {
|
export class URDFEditor {
|
||||||
constructor(canvas, sendMotorPosition, serial, curveEditor) {
|
constructor(canvas, sendMotorPosition, serial, curveEditor, tryConnect) {
|
||||||
this.canvas = canvas;
|
this.canvas = canvas;
|
||||||
this.sendMotorPosition = sendMotorPosition;
|
this.sendMotorPosition = sendMotorPosition;
|
||||||
this.serial = serial;
|
this.serial = serial;
|
||||||
this.curveEditor = curveEditor;
|
this.curveEditor = curveEditor;
|
||||||
|
this.tryConnect = tryConnect;
|
||||||
this.scene = new THREE.Scene();
|
this.scene = new THREE.Scene();
|
||||||
this.scene.background = new THREE.Color(0xaaaaaa);
|
this.scene.background = new THREE.Color(0xaaaaaa);
|
||||||
|
|
||||||
|
|
@ -74,7 +75,7 @@ export class URDFEditor {
|
||||||
|
|
||||||
this.setupScene();
|
this.setupScene();
|
||||||
//this.loadURDF();
|
//this.loadURDF();
|
||||||
this.loadURDFFromIndexedDB();
|
//this.loadURDFFromIndexedDB();
|
||||||
this.setupEvents();
|
this.setupEvents();
|
||||||
|
|
||||||
const editorCallbacks = {}
|
const editorCallbacks = {}
|
||||||
|
|
@ -478,11 +479,14 @@ export class URDFEditor {
|
||||||
let positions = [];
|
let positions = [];
|
||||||
allIDs.forEach(id => {
|
allIDs.forEach(id => {
|
||||||
let pos = this.curveEditor.getMotorPositionAtTime(id, this.curveEditor.currentTime);
|
let pos = this.curveEditor.getMotorPositionAtTime(id, this.curveEditor.currentTime);
|
||||||
this.setMotorPosition(id, pos);
|
if (this.currentSyncMode !== SyncMode.RealToSim) {
|
||||||
|
this.setMotorPosition(id, pos);
|
||||||
|
}
|
||||||
|
|
||||||
ids.push(id);
|
ids.push(id);
|
||||||
positions.push(pos);
|
positions.push(pos);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.currentSyncMode === SyncMode.SimToReal) {
|
if (this.currentSyncMode === SyncMode.SimToReal) {
|
||||||
this.sendMotorPosition(ids, positions);
|
this.sendMotorPosition(ids, positions);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ export class ViewerOverlay {
|
||||||
window.addEventListener('resize', resizeOverlay);
|
window.addEventListener('resize', resizeOverlay);
|
||||||
resizeOverlay();
|
resizeOverlay();
|
||||||
|
|
||||||
this.panels.push(this.createAnimationControlPanel());
|
//this.panels.push(this.createAnimationControlPanel());
|
||||||
this.panels.push(this.createSystemPanel());
|
this.panels.push(this.createSystemPanel());
|
||||||
const handlePointerEvent = (event) => {
|
const handlePointerEvent = (event) => {
|
||||||
const rect = this.overlayCanvas.getBoundingClientRect();
|
const rect = this.overlayCanvas.getBoundingClientRect();
|
||||||
|
|
@ -100,9 +100,13 @@ export class ViewerOverlay {
|
||||||
init(robot) {
|
init(robot) {
|
||||||
this.robot = robot;
|
this.robot = robot;
|
||||||
this.motorListPanel = this.createMotorListPanel(this.overlayCtx);
|
this.motorListPanel = this.createMotorListPanel(this.overlayCtx);
|
||||||
|
this.animationPanel = this.createAnimationControlPanel(this.overlayCtx);
|
||||||
if (this.motorListPanel) {
|
if (this.motorListPanel) {
|
||||||
this.panels.push(this.motorListPanel);
|
this.panels.push(this.motorListPanel);
|
||||||
}
|
}
|
||||||
|
if (this.animationPanel) {
|
||||||
|
this.panels.push(this.animationPanel);
|
||||||
|
}
|
||||||
this.draw();
|
this.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,7 +115,7 @@ export class ViewerOverlay {
|
||||||
|
|
||||||
// filter out the motorListPanel from panels
|
// filter out the motorListPanel from panels
|
||||||
this.panels = this.panels.filter(p => p !== this.motorListPanel);
|
this.panels = this.panels.filter(p => p !== this.motorListPanel);
|
||||||
|
this.panels = this.panels.filter(p => p !== this.animationPanel);
|
||||||
// clear the reference
|
// clear the reference
|
||||||
this.motorListPanel = null;
|
this.motorListPanel = null;
|
||||||
}
|
}
|
||||||
|
|
@ -170,23 +174,23 @@ export class ViewerOverlay {
|
||||||
);
|
);
|
||||||
panel.addElement(slider);
|
panel.addElement(slider);
|
||||||
|
|
||||||
panel.addElement(new Button(panel.x + 200, panel.y + 40, 200, 24, "Record Keyframe ALL", () => {
|
panel.addElement(new Button(panel.x + 200, panel.y + 40, 200, 24, "Record Keyframe ALL", () => {
|
||||||
let allIDs = this.parent.findAllMotorIDs();
|
let allIDs = this.parent.findAllMotorIDs();
|
||||||
for (var i = 0; i < allIDs.length; i++){
|
for (var i = 0; i < allIDs.length; i++) {
|
||||||
this.parent.curveEditor.splitCurveAtTime(allIDs[i], this.parent.curveEditor.currentTime, this.parent.getMotorTicks(allIDs[i]));
|
this.parent.curveEditor.splitCurveAtTime(allIDs[i], this.parent.curveEditor.currentTime, this.parent.getMotorTicks(allIDs[i]));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
panel.addElement(new Button(panel.x + 200, panel.y + 40 + 24*2, 200, 24, "Record Keyframe Selected", () => {
|
panel.addElement(new Button(panel.x + 200, panel.y + 40 + 24 * 2, 200, 24, "Record Keyframe Selected", () => {
|
||||||
if (!this.parent.selectedJoint){
|
if (!this.parent.selectedJoint) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const selectedID = this.parent.findJointAncestor(this.parent.selectedJoint, 0).transmission.motorID
|
const selectedID = this.parent.findJointAncestor(this.parent.selectedJoint, 0).transmission.motorID
|
||||||
const currentTime = this.parent.curveEditor.currentTime;
|
const currentTime = this.parent.curveEditor.currentTime;
|
||||||
const motorPosition = this.parent.getMotorTicks(selectedID)
|
const motorPosition = this.parent.getMotorTicks(selectedID)
|
||||||
this.parent.curveEditor.splitCurveAtTime(selectedID, currentTime, motorPosition);
|
this.parent.curveEditor.splitCurveAtTime(selectedID, currentTime, motorPosition);
|
||||||
console.log(motorPosition, this.parent.curveEditor.exportRangeToY(motorPosition), this.parent.curveEditor.yToExportRange(this.parent.curveEditor.exportRangeToY(motorPosition)));
|
//console.log(motorPosition, this.parent.curveEditor.exportRangeToY(motorPosition), this.parent.curveEditor.yToExportRange(this.parent.curveEditor.exportRangeToY(motorPosition)));
|
||||||
//console.log(selectedID, currentTime, motorPosition);
|
console.log(selectedID, currentTime, motorPosition);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
|
|
@ -202,32 +206,37 @@ export class ViewerOverlay {
|
||||||
|
|
||||||
const panel = new Panel(x, y, w, h, "System");
|
const panel = new Panel(x, y, w, h, "System");
|
||||||
|
|
||||||
|
panel.addElement(new Button(x, y + 28, 50, 24, "Connect", () => {
|
||||||
|
// delegate to editor’s save
|
||||||
|
this.parent.tryConnect();
|
||||||
|
}));
|
||||||
|
|
||||||
// Save button
|
// Save button
|
||||||
panel.addElement(new Button(x, y + 28, 50, 24, "Save", () => {
|
panel.addElement(new Button(x, y + 28 + 24 * 1, 50, 24, "Save", () => {
|
||||||
// delegate to editor’s save
|
// delegate to editor’s save
|
||||||
this.parent.saveURDFToIndexedDB(this.robot);
|
this.parent.saveURDFToIndexedDB(this.robot);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Load button
|
// Load button
|
||||||
panel.addElement(new Button(x, y + 28 + 24 * 1, 50, 24, "Load", () => {
|
panel.addElement(new Button(x, y + 28 + 24 * 2, 50, 24, "Load", () => {
|
||||||
// delegate to editor’s load
|
// delegate to editor’s load
|
||||||
|
|
||||||
this.parent.loadURDFFromIndexedDB();
|
this.parent.loadURDFFromIndexedDB();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
panel.addElement(new Button(x, y + 28 + 24 * 2, 50, 24, "Download", () => {
|
panel.addElement(new Button(x, y + 28 + 24 * 3, 50, 24, "Download", () => {
|
||||||
// delegate to editor’s load
|
// delegate to editor’s load
|
||||||
this.parent.downloadURDF();
|
this.parent.downloadURDF();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
panel.addElement(new Button(x, y + 28 + 24 * 3, 50, 24, "Upload", () => {
|
panel.addElement(new Button(x, y + 28 + 24 * 4, 50, 24, "Upload", () => {
|
||||||
// delegate to editor’s load
|
// delegate to editor’s load
|
||||||
this.parent.uploadURDF();
|
this.parent.uploadURDF();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
panel.addElement(new Button(x, y + 28 + 24 * 5, 50, 24, "Calibrate", () => {
|
panel.addElement(new Button(x, y + 28 + 24 * 6, 50, 24, "Calibrate", () => {
|
||||||
|
|
||||||
this.applyCalibrationOffsets();
|
this.parent.applyCalibrationOffsets();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
11
script.js
11
script.js
|
|
@ -41,7 +41,7 @@ window.onload = () => {
|
||||||
|
|
||||||
const serial = new SerialManager();
|
const serial = new SerialManager();
|
||||||
const urdfCanvas = document.getElementById('urdfCanvas');
|
const urdfCanvas = document.getElementById('urdfCanvas');
|
||||||
const visualEditor = new URDFEditor(urdfCanvas, sendMotorPosition, serial, curveEditor);
|
const visualEditor = new URDFEditor(urdfCanvas, sendMotorPosition, serial, curveEditor, tryConnect);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -94,6 +94,7 @@ window.onload = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
nodeEditor.generateDefaultNodes(curveEditor.curveSets, motorIDList);
|
nodeEditor.generateDefaultNodes(curveEditor.curveSets, motorIDList);
|
||||||
|
visualEditor.loadURDFFromIndexedDB();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -291,8 +292,7 @@ window.onload = () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect button
|
async function tryConnect() {
|
||||||
document.getElementById('connect').addEventListener('click', async () => {
|
|
||||||
try {
|
try {
|
||||||
await serial.connect();
|
await serial.connect();
|
||||||
statusText.textContent = 'Connected ✅';
|
statusText.textContent = 'Connected ✅';
|
||||||
|
|
@ -412,6 +412,11 @@ window.onload = () => {
|
||||||
statusText.textContent = 'Connection failed ❌';
|
statusText.textContent = 'Connection failed ❌';
|
||||||
console.error("Connection error:", err);
|
console.error("Connection error:", err);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect button
|
||||||
|
document.getElementById('connect').addEventListener('click', async () => {
|
||||||
|
tryConnect();
|
||||||
});
|
});
|
||||||
|
|
||||||
function handlePositionStreamPacket(data) {
|
function handlePositionStreamPacket(data) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue