animation length can now be controlled with slider
parent
7a619886c6
commit
e50e5cd7f6
170
curveEditor.js
170
curveEditor.js
|
|
@ -6,6 +6,7 @@ export class CurveEditor {
|
|||
this.ctx = canvas.getContext('2d');
|
||||
this.timelineLength = timelineLength;
|
||||
|
||||
|
||||
this.scale = 1;
|
||||
this.scaleX = 1;
|
||||
this.scaleY = 1;
|
||||
|
|
@ -16,6 +17,14 @@ export class CurveEditor {
|
|||
|
||||
this.currentTime = timelineLength * this.pixelsPerSecond / 2;
|
||||
|
||||
this.durationHandle = {
|
||||
x: timelineLength, // or maxTimeCS
|
||||
y: 30, // top of canvas
|
||||
radius: 10,
|
||||
type: "duration"
|
||||
};
|
||||
|
||||
|
||||
this.motorButtons = {
|
||||
width: 30,
|
||||
height: 30,
|
||||
|
|
@ -25,17 +34,17 @@ export class CurveEditor {
|
|||
|
||||
|
||||
// COMMENT OUT AND READ FROM EXISTING TIMELINE LATER
|
||||
const slider = _slider;//document.getElementById('timeSlider');
|
||||
slider.max = this.timelineLength * this.pixelsPerSecond;
|
||||
this.slider = _slider;//document.getElementById('timeSlider');
|
||||
this.slider.max = this.timelineLength * this.pixelsPerSecond;
|
||||
|
||||
console.log(slider.max);
|
||||
slider.value = 0;
|
||||
slider.addEventListener('input', () => {
|
||||
this.currentTime = parseFloat(slider.value);
|
||||
console.log(this.slider.max);
|
||||
this.slider.value = 0;
|
||||
this.slider.addEventListener('input', () => {
|
||||
this.currentTime = parseFloat(this.slider.value);
|
||||
//console.log(slider.value);
|
||||
this.draw();
|
||||
});
|
||||
|
||||
this.setLength(timelineLength * this.pixelsPerSecond);
|
||||
|
||||
|
||||
this.curveSets = {}; // motorID → array of curves
|
||||
|
|
@ -45,39 +54,6 @@ export class CurveEditor {
|
|||
this.isPanning = false;
|
||||
this.panStart = { x: 0, y: 0 };
|
||||
|
||||
// Default curve
|
||||
// this.setCurves([
|
||||
// {
|
||||
// startPoint: { x: this.valueToX(0), y: this.valueToY(0) },
|
||||
// startPointHandle: { 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) }
|
||||
// }
|
||||
// ]);
|
||||
// this.curveSets[this.selectedMotorID] = this.curves;
|
||||
|
||||
// let othercurves = [
|
||||
// {
|
||||
// startPoint: { x: this.valueToX(0), y: this.valueToY(.3) },
|
||||
// startPointHandle: { 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) }
|
||||
// }
|
||||
// ]
|
||||
// this.curveSets[5] = othercurves;
|
||||
|
||||
// othercurves = [
|
||||
// {
|
||||
// startPoint: { x: this.valueToX(0), y: this.valueToY(-.5) },
|
||||
// startPointHandle: { 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) }
|
||||
// }
|
||||
// ]
|
||||
// this.curveSets[7] = othercurves;
|
||||
|
||||
|
||||
//this.setSelectedMotor(4);
|
||||
|
||||
this.initEvents();
|
||||
this.draw();
|
||||
|
|
@ -150,8 +126,15 @@ export class CurveEditor {
|
|||
|
||||
setLength(endTime) {
|
||||
this.timelineLength = endTime / this.pixelsPerSecond;
|
||||
console.log("new endtime: " + endTime);
|
||||
this.slider.max = this.timelineLength * this.pixelsPerSecond;
|
||||
console.log(this.slider.max, this.slider.value);
|
||||
if (this.slider.value > this.slider.max) {
|
||||
this.slider.value = this.slider.max;
|
||||
}
|
||||
this.durationHandle.x = this.timelineLength;
|
||||
//console.log("new endtime: " + endTime);
|
||||
//this.currentTime = this.timelineLength * this.pixelsPerSecond / 2;
|
||||
this.draw();
|
||||
}
|
||||
|
||||
loadCurveSets(curveSets) {
|
||||
|
|
@ -171,6 +154,8 @@ export class CurveEditor {
|
|||
// Optional: update motor selector UI or redraw timeline
|
||||
//this.refreshMotorSelector?.(); // if you have a method for that
|
||||
//this.drawTimelineMarkers?.();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -245,17 +230,48 @@ export class CurveEditor {
|
|||
this.drawGrid(ctx);
|
||||
|
||||
// Draw inactive curves dimmed
|
||||
// Draw inactive curves dimmed only if there are curve sets
|
||||
if (this.curveSets && Object.keys(this.curveSets).length > 0) {
|
||||
for (const [motorID, curves] of Object.entries(this.curveSets)) {
|
||||
if (!curves || curves.length === 0) continue; // skip empty sets
|
||||
const isSelected = motorID == this.selectedMotorID;
|
||||
this.drawCurves(this.ctx, curves, isSelected ? 1.0 : 0.3, isSelected);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.drawCurves(ctx, this.curves);
|
||||
|
||||
this.drawMotorButtons();
|
||||
|
||||
|
||||
this.drawDurationHandle(ctx);
|
||||
}
|
||||
|
||||
drawDurationHandle() {
|
||||
const ctx = this.ctx;
|
||||
const x = this.valueToX(this.durationHandle.x); // e.g. derived from header.frameCount or maxTime
|
||||
const y = 20; // top of canvas
|
||||
ctx.save();
|
||||
|
||||
// vertical guide line
|
||||
ctx.strokeStyle = "red";
|
||||
ctx.lineWidth = 1;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, 0);
|
||||
ctx.lineTo(x, this.canvas.height);
|
||||
ctx.stroke();
|
||||
|
||||
// draggable knob at the top
|
||||
ctx.fillStyle = "red";
|
||||
ctx.beginPath();
|
||||
ctx.arc(x, this.durationHandle.y, this.durationHandle.radius, 0, Math.PI * 2); // circle 10px down from top
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
|
||||
drawMotorButtons() {
|
||||
const ctx = this.ctx;
|
||||
const canvas = this.canvas;
|
||||
|
|
@ -353,7 +369,9 @@ export class CurveEditor {
|
|||
drawCurves(ctx, curves, opacity = 1.0, showControlPoints = true) {
|
||||
ctx.save();
|
||||
ctx.globalAlpha = opacity;
|
||||
|
||||
if (!curves) {
|
||||
return;
|
||||
}
|
||||
for (let curve of curves) {
|
||||
const p0 = this.transform(curve.startPoint);
|
||||
const h0 = this.transform(curve.startPointHandle);
|
||||
|
|
@ -548,6 +566,15 @@ export class CurveEditor {
|
|||
const mx = e.offsetX;
|
||||
const my = e.offsetY;
|
||||
|
||||
// Check duration handle
|
||||
const handleX = this.valueToX(this.durationHandle.x);
|
||||
const handleY = this.durationHandle.y; // same as draw
|
||||
if (Math.hypot(e.offsetX - handleX, e.offsetY - handleY) < this.durationHandle.radius) {
|
||||
this.dragging = { type: "durationHandle" };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (let btn of buttons) {
|
||||
if (
|
||||
mx >= btn.x &&
|
||||
|
|
@ -566,10 +593,6 @@ export class CurveEditor {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const mouse = this.inverseTransform({ x: e.offsetX, y: e.offsetY });
|
||||
|
||||
if (e.button === 1) {
|
||||
|
|
@ -580,7 +603,7 @@ export class CurveEditor {
|
|||
}
|
||||
|
||||
for (let curve of this.curves) {
|
||||
for (let key of ['startPoint', 'startPointHandle', 'endPointHandle', 'endPoint']) {
|
||||
for (let key of ['startPointHandle', 'endPointHandle', 'startPoint', 'endPoint']) {
|
||||
const p = curve[key];
|
||||
if (Math.hypot(p.x - mouse.x, p.y - mouse.y) < 10 / this.scale) {
|
||||
this.dragging = { curve, key };
|
||||
|
|
@ -588,6 +611,7 @@ export class CurveEditor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -608,6 +632,19 @@ export class CurveEditor {
|
|||
|
||||
if (key === 'startPoint' || key === 'endPoint') {
|
||||
this.dragEndpoint(curve, key, mouse.x, mouse.y, index);
|
||||
} else if (this.dragging.type === "durationHandle") {
|
||||
// Update duration in timeline units
|
||||
const newTime = Math.max(0, mouse.x); // timeline units
|
||||
|
||||
|
||||
// Optionally update timeline length
|
||||
this.setLength(newTime);
|
||||
this.adjustAllCurvesToDuration(newTime); // truncate/extend curves
|
||||
|
||||
//console.log(this.durationHandle.x);
|
||||
this.draw();
|
||||
return;
|
||||
|
||||
} else {
|
||||
this.dragControlPoint(curve, key, mouse.x, mouse.y, index);
|
||||
}
|
||||
|
|
@ -707,6 +744,45 @@ export class CurveEditor {
|
|||
});
|
||||
}
|
||||
|
||||
adjustAllCurvesToDuration(newTime) {
|
||||
for (const [motorID, curves] of Object.entries(this.curveSets)) {
|
||||
if (!curves || curves.length === 0) continue;
|
||||
|
||||
for (let i = 0; i < curves.length; i++) {
|
||||
const curve = curves[i];
|
||||
|
||||
// If a curve starts beyond the new duration, drop it
|
||||
if (curve.startPoint.x >= newTime) {
|
||||
curves.splice(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// If a curve ends beyond the new duration, truncate it
|
||||
if (curve.endPoint.x > newTime) {
|
||||
curve.endPoint.x = newTime;
|
||||
curve.endPointHandle.x = newTime;
|
||||
curves.splice(i + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If extending, push the last curve’s end point out
|
||||
const last = curves[curves.length - 1];
|
||||
if (last.endPoint.x < newTime) {
|
||||
const delta = newTime - last.endPoint.x;
|
||||
last.endPoint.x = newTime;
|
||||
last.endPointHandle.x += delta;
|
||||
}
|
||||
|
||||
this.curveSets[motorID] = curves;
|
||||
}
|
||||
|
||||
// Update the active reference too
|
||||
this.curves = this.curveSets[this.selectedMotorID] || [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
dragControlPoint(curve, key, mouseX, mouseY, index) {
|
||||
curve[key].y = mouseY;
|
||||
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@
|
|||
|
||||
<div>
|
||||
<label>Frame: <span id="frameDisplay">0</span></label><br>
|
||||
<input type="range" id="frameSlider" min="0" max="399" value="0" style="width: 80%">
|
||||
<input type="range" id="frameSlider" min="0" max="100" value="0" style="width: 80%">
|
||||
</div>
|
||||
|
||||
<label for="filenameInput">Filename:</label>
|
||||
|
|
|
|||
|
|
@ -663,7 +663,8 @@ window.onload = () => {
|
|||
const filename = sanitizeFilename(rawInput);
|
||||
console.log("Sanitized filename:", filename);
|
||||
|
||||
const frameCount = 480; // or whatever your timeline length is
|
||||
const frameCount = curveEditor.timelineLength * curveEditor.pixelsPerSecond; // or whatever your timeline length is
|
||||
console.log(frameCount);
|
||||
const frameRate = 48;
|
||||
const version = 1;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue