physics/draw.js

155 lines
3.5 KiB
JavaScript

class Drawing {
constructor() {
const div = document.createElement('div');
const buttonsDiv = document.createElement('div');
this.startButton = document.createElement('button');
this.startButton.onclick = () => this.start();
this.startButton.innerHTML = "Start";
this.stopButton = document.createElement('button');
this.stopButton.onclick = () => this.stop();
this.stopButton.innerHTML = "Stop";
this.canvas = document.createElement('canvas');
this.canvas.width = 200;
this.canvas.height = 200;
buttonsDiv.appendChild(this.startButton);
buttonsDiv.appendChild(this.stopButton);
div.appendChild(buttonsDiv);
div.appendChild(this.canvas);
document.body.appendChild(div);
this.ctx = this.canvas.getContext('2d');
this.offset = {x: 0, y: 0};
this.sequence = [];
this.t = 0;
this.rt = 0;
this.dt = 0;
this.points = {};
this.stopped = true;
}
pixel([x, y]) {
return [
this.offset.x + x,
this.canvas.height - this.offset.y - y
];
}
setOffset([x, y]) {
this.sequence.push(() => {
this.offset = {x, y};
});
}
setStroke(style, width) {
this.sequence.push(() => {
this.ctx.strokeStyle = style;
this.ctx.lineWidth = width;
});
}
setFill(style) {
this.sequence.push(() => {
this.ctx.fillStyle = style;
});
}
line(p1, p2) {
this.sequence.push(() => {
this.ctx.beginPath();
this.ctx.moveTo(...this.pixel(this.getPoint(p1)));
this.ctx.lineTo(...this.pixel(this.getPoint(p2)));
this.ctx.stroke();
});
}
polyline(...points) {
this.sequence.push(() => {
this.ctx.beginPath();
this.ctx.moveTo(...this.pixel(points[0]));
for (let i = 1; i < points.length; i++) {
this.ctx.lineTo(...this.pixel(points[i]));
}
this.ctx.stroke();
});
}
oscillatingValue(x1, x2, period) {
return x1 + (x2 - x1) * Math.sin(2*Math.PI*this.t/period);
}
oscillatingPoint([x1, y1], [x2, y2], period) {
const x = this.oscillatingValue(x1, x2, period);
const y = this.oscillatingValue(y1, y2, period);
return [x, y];
}
square(p) {
this.sequence.push(() => {
const [x, y] = this.getPoint(p);
this.ctx.fillRect(...this.pixel([x - 5, y - 5]), 10, 10);
})
}
circle(p) {
this.sequence.push(() => {
this.ctx.beginPath();
this.ctx.ellipse(...this.pixel(this.getPoint(p)), 5, 5, 0, 0, 2*Math.PI);
this.ctx.fill();
})
}
definePoint(name, fn) {
this.points[name] = fn;
}
getPoint(p) {
if (typeof p === 'string') {
const fn = this.points[p];
if (!fn) {
const e = new Error;
e.message = `Point '${p}' is not defined`;
throw e;
}
return fn();
} else {
return p;
}
}
render() {
for (let action of this.sequence) {
action();
}
}
animate() {
this.ctx.reset();
this.render();
if (!this.stopped) {
requestAnimationFrame((prevt) => {
const rt = document.timeline.currentTime;
const elapsed = rt - prevt;
if (this.elideInterval) {
this.dt = 0;
this.elideInterval = false;
} else {
this.dt = rt - this.rt + elapsed;
}
this.t += this.dt;
this.rt = rt;
this.animate();
});
}
}
stop() {
this.stopped = true;
}
start() {
if (this.stopped) {
this.elideInterval = true;
this.stopped = false;
}
this.animate();
}
}