summaryrefslogtreecommitdiff
path: root/main.js
diff options
context:
space:
mode:
Diffstat (limited to 'main.js')
-rw-r--r--main.js153
1 files changed, 153 insertions, 0 deletions
diff --git a/main.js b/main.js
new file mode 100644
index 0000000..26741ea
--- /dev/null
+++ b/main.js
@@ -0,0 +1,153 @@
+const X_START = '8*cos(t)';
+const Y_START = '8*sin(t)';
+const equations = {
+ x: math.compile(X_START),
+ y: math.compile(Y_START),
+}
+
+window.onload = () => {
+ const root = document.getElementById('root');
+
+ const launchButton = document.createElement('input');
+ launchButton.type = 'button';
+ launchButton.value = 'Launch';
+ const xeq = document.createElement('input');
+ xeq.value = X_START;
+ xeq.onchange = (e) => {
+ equations.x = math.compile(e.target.value);
+ running = false;
+ path = computePath(equations, 0, 100, 0.1);
+ render(ctx, path, 0);
+ };
+ xeq.onkeydown = xeq.onchange;
+ xeq.onkeyup = xeq.onchange;
+ const yeq = document.createElement('input');
+ yeq.value = Y_START;
+ yeq.onchange = (e) => {
+ equations.y = math.compile(e.target.value);
+ running = false;
+ path = computePath(equations, 0, 100, 0.1);
+ render(ctx, path, 0);
+ };
+ yeq.onkeydown = yeq.onchange;
+ yeq.onkeyup = yeq.onchange;
+
+ root.appendChild(xeq);
+ root.appendChild(yeq);
+ root.appendChild(launchButton);
+
+ const canvas = document.createElement('canvas');
+ canvas.width = 600;
+ canvas.height = 600;
+ root.appendChild(canvas);
+
+ const ctx = canvas.getContext('2d');
+ ctx.translate(canvas.width/2, canvas.height/2)
+ ctx.scale(canvas.width/20, -canvas.height/20);
+ let path = computePath(equations, 0, 100, 0.1);
+
+ let running = false;
+ const step = (index) => {
+ render(ctx, path, index);
+ if (running && index < path.length-1) {
+ setTimeout(() => step(index+1), 10);
+ } else {
+ running = false;
+ render(ctx, path, 0);
+ }
+ }
+ launchButton.onclick = () => {
+ running = false;
+ setTimeout(() => { running=true; step(0); }, 20);
+ }
+ render(ctx, path, 0);
+}
+
+
+function render(ctx, path, index) {
+ const { width, height } = ctx.canvas;
+ ctx.fillStyle = 'black';
+ ctx.fillRect(-width, -height, 2*width, 2*height);
+ const [x0, y0] = path[index];
+ const [x1, y1] = path[index+1] || path[index];
+ const angle = Math.atan2(y1-y0, x1-x0);
+ drawGrid(ctx);
+ drawPath(ctx, path);
+ drawShip(ctx, [x0, y0], angle);
+}
+
+
+function drawShip(ctx, pos, angle) {
+ const transform = ctx.getTransform();
+ const { width, height } = ctx.canvas;
+ const [x, y] = pos;
+ ctx.translate(x, y);
+ ctx.rotate(angle);
+ ctx.fillStyle = 'white';
+ const STEP = 120/Math.min(width,height);
+ ctx.beginPath();
+ ctx.moveTo(0, 0);
+ ctx.lineTo(-STEP, -STEP);
+ ctx.lineTo(2*STEP, 0);
+ ctx.lineTo(-STEP, STEP);
+ ctx.lineTo(0, 0);
+ ctx.fill();
+ ctx.setTransform(transform);
+}
+
+
+function computePath(equations, start, end, step) {
+ const ts = [...Array(Math.floor((end-start)/step)).keys()].map(k => step * (start+k))
+ return ts.map(t => [ equations.x.eval({t}), equations.y.eval({t}) ]);
+}
+
+function drawGrid(ctx) {
+ ctx.scale(1, -1);
+ ctx.strokeStyle = 'white';
+ ctx.fillStyle = 'white';
+ const { width, height } = ctx.canvas;
+ ctx.font = `${200/Math.max(width, height)}px serif`;
+ ctx.lineWidth = 4 / Math.max(width, height);
+ const drawLine = (start, end) => {
+ ctx.beginPath();
+ ctx.moveTo(start[0], start[1]);
+ ctx.lineTo(end[0], end[1]);
+ ctx.stroke();
+ }
+ const drawXLabel = (x, y) => {
+ ctx.textAlign = 'right';
+ ctx.textBaseline = 'bottom';
+ ctx.fillText(`${x} `, x, y);
+ }
+
+ const drawYLabel = (x, y) => {
+ ctx.rotate(Math.PI/2);
+ ctx.textAlign = 'right';
+ ctx.textBaseline = 'bottom';
+ ctx.fillText(`${y} `, y, x);
+ ctx.rotate(-Math.PI/2);
+ }
+
+ const drawGrid = (start, end, step) => {
+ [...Array(1+Math.floor((end-start)/step)).keys()].forEach(z => {
+ z = (step*z)+start;
+ drawLine([z, start], [z, end]);
+ drawLine([start, z], [end, z]);
+ drawXLabel(z, end);
+ drawYLabel(end, z);
+ });
+ }
+ drawGrid(-10, 10, 2);
+ ctx.scale(1, -1);
+}
+
+function drawPath(ctx, path) {
+ const [ start, ...line ] = path;
+ const { width, height } = ctx.canvas;
+ ctx.strokeStyle = 'white';
+ ctx.lineWidth = 8 / Math.max(width, height);
+ ctx.beginPath();
+ ctx.moveTo(start[0], start[1]);
+ line.forEach(p => ctx.lineTo(p[0], p[1]));
+ ctx.stroke();
+}