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