implements separate x and y zoom on curve editor
parent
06633fa7b5
commit
0fa8e04493
|
|
@ -1,10 +1,14 @@
|
|||
export class CurveEditor {
|
||||
constructor(canvas, timelineLength = 10, _slider) {
|
||||
constructor(canvas, timelineLength = 6, _slider) {
|
||||
this.canvas = canvas;
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
this.ctx = canvas.getContext('2d');
|
||||
this.timelineLength = timelineLength;
|
||||
|
||||
this.scale = 1;
|
||||
this.scaleX = 1;
|
||||
this.scaleY = 1;
|
||||
this.offset = { x: 0, y: 0 };
|
||||
this.pixelsPerSecond = 48;
|
||||
|
||||
|
|
@ -133,15 +137,17 @@ export class CurveEditor {
|
|||
addChannel(motorID) {
|
||||
this.setCurves([
|
||||
{
|
||||
startPoint: { x: this.valueToX(0), y: this.valueToY(0) },
|
||||
startPointHandle: { x: this.valueToX(this.timelineLength * 0.25), y: this.valueToY(0) },
|
||||
endPointHandle: { x: this.valueToX(this.timelineLength * 0.75), y: this.valueToY(0) },
|
||||
endPoint: { x: this.valueToX(this.timelineLength), y: this.valueToY(0) }
|
||||
startPoint: { x: 0, y: this.canvas.height / 2 },
|
||||
startPointHandle: { x: this.timelineLength * this.pixelsPerSecond * 0.25, y: this.canvas.height / 2 },
|
||||
endPointHandle: { x: this.timelineLength * this.pixelsPerSecond * 0.75, y: this.canvas.height / 2 },
|
||||
endPoint: { x: this.timelineLength * this.pixelsPerSecond, y: this.canvas.height / 2 }
|
||||
}
|
||||
]);
|
||||
console.log("TL LENGTH: " + this.timelineLength);
|
||||
this.curveSets[motorID] = this.curves;
|
||||
}
|
||||
|
||||
|
||||
setLength(endTime) {
|
||||
this.timelineLength = endTime / this.pixelsPerSecond;
|
||||
console.log("new endtime: " + endTime);
|
||||
|
|
@ -178,12 +184,12 @@ export class CurveEditor {
|
|||
|
||||
|
||||
valueToY(v) {
|
||||
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.scaleY + this.offset.y;
|
||||
}
|
||||
|
||||
// Maps pixel value of y axis to -1 to 1 normalised value
|
||||
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.scaleY) / (this.canvas.height / 2));
|
||||
}
|
||||
|
||||
// Maps normalised -1 to 1 value to motor range (0, 4095)
|
||||
|
|
@ -206,25 +212,27 @@ export class CurveEditor {
|
|||
}
|
||||
|
||||
|
||||
valueToX(t) {
|
||||
return t * this.pixelsPerSecond * this.scale + this.offset.x;
|
||||
valueToX(value) {
|
||||
return value * this.pixelsPerSecond * this.scaleX + this.offset.x;
|
||||
}
|
||||
|
||||
|
||||
xToValue(x) {
|
||||
return (x - this.offset.x) / (this.pixelsPerSecond * this.scale);
|
||||
return (x - this.offset.x) / (this.pixelsPerSecond * this.scaleX);
|
||||
}
|
||||
|
||||
|
||||
transform(p) {
|
||||
return {
|
||||
x: p.x * this.scale + this.offset.x,
|
||||
y: p.y * this.scale + this.offset.y
|
||||
x: p.x * this.scaleX + this.offset.x,
|
||||
y: p.y * this.scaleY + this.offset.y
|
||||
};
|
||||
}
|
||||
|
||||
inverseTransform(p) {
|
||||
return {
|
||||
x: (p.x - this.offset.x) / this.scale,
|
||||
y: (p.y - this.offset.y) / this.scale
|
||||
x: (p.x - this.offset.x) / this.scaleX,
|
||||
y: (p.y - this.offset.y) / this.scaleY
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -389,14 +397,6 @@ export class CurveEditor {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Draw control points (optional)
|
||||
if (showControlPoints) {
|
||||
// Draw handles
|
||||
|
|
@ -448,7 +448,7 @@ export class CurveEditor {
|
|||
ctx.lineWidth = 1.5;
|
||||
ctx.font = "12px sans-serif";
|
||||
ctx.fillStyle = "#666";
|
||||
ctx.fillText(`${time}s`, x + 2, 12);
|
||||
ctx.fillText(`${time}s`, x + 0 / this.scaleX, 12 / this.scaleY);
|
||||
} else if (s % 2 === 0) {
|
||||
ctx.strokeStyle = "#bbb";
|
||||
ctx.lineWidth = 1;
|
||||
|
|
@ -475,7 +475,8 @@ export class CurveEditor {
|
|||
|
||||
ctx.font = "12px sans-serif";
|
||||
ctx.fillStyle = "#666";
|
||||
ctx.fillText(v.toFixed(2), 4, y - 4);
|
||||
ctx.fillText(v.toFixed(2), 4 / this.scaleX + 10, y - 6 / this.scaleY);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -572,6 +573,7 @@ export class CurveEditor {
|
|||
const mouse = this.inverseTransform({ x: e.offsetX, y: e.offsetY });
|
||||
|
||||
if (e.button === 1) {
|
||||
e.preventDefault(); // Prevent page panning
|
||||
this.isPanning = true;
|
||||
this.panStart = { x: e.offsetX, y: e.offsetY };
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@
|
|||
|
||||
|
||||
<div class="tab-pane fade show active" id="animation" role="tabpanel" aria-labelledby="animation-tab">
|
||||
<canvas id="curveCanvas" width="1920" height="800"></canvas>
|
||||
<canvas id="curveCanvas"></canvas>
|
||||
<!-- <div style="margin-top: 10px; text-align: center;">
|
||||
<input type="range" id="timeSlider" min="0" step="1" style="width: 80%;">
|
||||
</div> -->
|
||||
|
|
@ -199,7 +199,7 @@
|
|||
|
||||
|
||||
|
||||
<canvas id="nodeeditor" width="800" height="600"></canvas>
|
||||
<canvas id="nodeeditor"></canvas>
|
||||
<div id="contextMenu" style="
|
||||
position: absolute;
|
||||
display: none;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { ServoNode, CurveNode, VariableNode, NoiseNode, MathNode, MapNode, NODE_
|
|||
export class NodeEditor {
|
||||
constructor(canvas, options = {}) {
|
||||
this.canvas = canvas;
|
||||
canvas.width = canvas.offsetWidth;
|
||||
canvas.height = canvas.offsetHeight;
|
||||
this.ctx = canvas.getContext("2d");
|
||||
this.nodes = [];
|
||||
this.connections = [];
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ window.onload = () => {
|
|||
|
||||
|
||||
const curveCanvas = document.getElementById('curveCanvas');
|
||||
const curveEditor = new CurveEditor(curveCanvas, 10, frameSlider);
|
||||
const curveEditor = new CurveEditor(curveCanvas, 6, frameSlider);
|
||||
|
||||
|
||||
// Animation File List
|
||||
|
|
|
|||
15
style.css
15
style.css
|
|
@ -74,7 +74,17 @@ body {
|
|||
}
|
||||
|
||||
|
||||
#curveCanvas {
|
||||
width: 80%;
|
||||
height: 800px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#nodeeditor {
|
||||
width: 80%;
|
||||
height: 1200px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
canvas {
|
||||
|
|
@ -82,6 +92,8 @@ canvas {
|
|||
/* light grey */
|
||||
border: 1px solid #ccc;
|
||||
/* optional subtle border */
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -97,8 +109,7 @@ canvas {
|
|||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #ddd;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue