Compare commits
No commits in common. "b8bf93269fb457d09b3f6f9cc63982da4839a801" and "d96aefd3f2779073684baeb59149b470caeaee02" have entirely different histories.
b8bf93269f
...
d96aefd3f2
13
config.js
13
config.js
@ -1,19 +1,14 @@
|
|||||||
export const MASS_CREATION_RATE = 0.001;
|
export const MASS_CREATION_RATE = 0.001;
|
||||||
export const DISPLAY_OBJECTS_INFO = false;
|
export const DISPLAY_OBJECTS_INFO = false;
|
||||||
export const DISPLAY_CURSOR_INFO = true;
|
|
||||||
export const DISPLAY_VELOCITY_VECTORS = true;
|
export const DISPLAY_VELOCITY_VECTORS = true;
|
||||||
export const POINTER_HISTORY_SIZE = 20;
|
export const POINTER_HISTORY_SIZE = 20;
|
||||||
export const VELOCITY_VECTOR_SCALE = 0.2;
|
export const VELOCITY_VECTOR_SCALE = 0.2;
|
||||||
export const VELOCITY_VECTOR_COLOR = 'rgb(150, 150, 150)'; // optionally set to 'object color'
|
export const VELOCITY_VECTOR_COLOR = 'rgb(150, 150, 150)'; // optionally set to 'object color'
|
||||||
export const VELOCITY_VECTOR_WIDTH = 1.5;
|
export const VELOCITY_VECTOR_WIDTH = 1.5;
|
||||||
export const VELOCITY_VECTOR_ARROWHEAD = true;
|
export const VELOCITY_VECTOR_ARROWHEAD = true;
|
||||||
export const ARROWHEAD_LENGTH = 7;
|
export const ARROWHEAD_LENGTH = 10;
|
||||||
export const ARROWHEAD_WIDTH = 5;
|
export const ARROWHEAD_WIDTH = 7;
|
||||||
export const MOTION_TIME_SCALE = 0.001;
|
export const MOTION_TIME_SCALE = 0.001;
|
||||||
export const OFFSCREEN_OBJECT_LINE_SCALE = 7;
|
export const OFFSCREEN_OBJECT_LINE_SCALE = 5;
|
||||||
export const OFFSCREEN_OBJECT_LINE_WIDTH = 2;
|
export const OFFSCREEN_OBJECT_LINE_WIDTH = 1.5;
|
||||||
export const OFFSCREEN_OBJECT_ARROWHEAD_LENGTH = 15;
|
export const OFFSCREEN_OBJECT_ARROWHEAD_LENGTH = 15;
|
||||||
export const ZOOM_IN_FACTOR = 2;
|
|
||||||
export const ZOOM_OUT_FACTOR = 0.5;
|
|
||||||
export const SCALE_MAX = 256;
|
|
||||||
export const SCALE_MIN = 1/256;
|
|
||||||
|
|||||||
52
display.js
52
display.js
@ -110,16 +110,14 @@ export class Display {
|
|||||||
|
|
||||||
const arrowDirection = Math.atan2(py - cy, px - cx);
|
const arrowDirection = Math.atan2(py - cy, px - cx);
|
||||||
// Length of arrow based on distance (logarithmic scale)
|
// Length of arrow based on distance (logarithmic scale)
|
||||||
const distance = Math.sqrt((x - px)**2, (y - py)**2) * this.scale;
|
const distance = Math.sqrt((x - px)**2, (y - py)**2);
|
||||||
const arrowLength = Math.log(distance) * OFFSCREEN_OBJECT_LINE_SCALE / this.scale;
|
const arrowLength = Math.log(distance) * OFFSCREEN_OBJECT_LINE_SCALE / this.scale;
|
||||||
const startAx = px - arrowLength * Math.cos(arrowDirection);
|
const startAx = px - arrowLength * Math.cos(arrowDirection);
|
||||||
const startAy = py - arrowLength * Math.sin(arrowDirection);
|
const startAy = py - arrowLength * Math.sin(arrowDirection);
|
||||||
this.drawArrow(startAx, startAy, px, py, {
|
this.drawArrow(startAx, startAy, px, py, {
|
||||||
style: `rgb(${r}, ${g}, ${b})`,
|
style: `rgb(${r}, ${g}, ${b})`,
|
||||||
width: OFFSCREEN_OBJECT_LINE_WIDTH,
|
width: OFFSCREEN_OBJECT_LINE_WIDTH,
|
||||||
arrowheadLength: OFFSCREEN_OBJECT_ARROWHEAD_LENGTH,
|
arrowheadLength: OFFSCREEN_OBJECT_ARROWHEAD_LENGTH
|
||||||
fill: false,
|
|
||||||
ifShort: 'head',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -138,9 +136,7 @@ export class Display {
|
|||||||
this.drawArrow(x, y, endVx, endVy, {
|
this.drawArrow(x, y, endVx, endVy, {
|
||||||
style,
|
style,
|
||||||
width: VELOCITY_VECTOR_WIDTH,
|
width: VELOCITY_VECTOR_WIDTH,
|
||||||
arrowhead: VELOCITY_VECTOR_ARROWHEAD,
|
arrowhead: VELOCITY_VECTOR_ARROWHEAD
|
||||||
fill: false,
|
|
||||||
ifShort: 'head'
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: Draw arrow for acceleration
|
// TODO: Draw arrow for acceleration
|
||||||
@ -152,55 +148,31 @@ export class Display {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawArrow(startX, startY, endX, endY, {style, width, arrowhead, arrowheadLength, fill, ifShort}) {
|
drawArrow(startX, startY, endX, endY, {style, width, arrowhead, arrowheadLength}) {
|
||||||
|
arrowhead = arrowhead ?? true;
|
||||||
|
arrowheadLength = arrowheadLength ?? ARROWHEAD_LENGTH;
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
ctx.strokeStyle = style;
|
ctx.strokeStyle = style;
|
||||||
// Keep arrows at normal scale
|
// Keep arrows at normal scale
|
||||||
ctx.lineWidth = width / this.scale;
|
ctx.lineWidth = width / this.scale;
|
||||||
arrowhead = arrowhead ?? true;
|
|
||||||
arrowheadLength = arrowheadLength ?? ARROWHEAD_LENGTH;
|
|
||||||
const scaledArrowheadLength = arrowheadLength / this.scale;
|
const scaledArrowheadLength = arrowheadLength / this.scale;
|
||||||
ifShort = ifShort ?? 'tail';
|
|
||||||
const arrowDirection = Math.atan2(endY - startY, endX - startX);
|
const arrowDirection = Math.atan2(endY - startY, endX - startX);
|
||||||
const length = Math.sqrt((endX - startX)**2 + (endY - startY)**2);
|
const endAx = arrowhead ? endX - (scaledArrowheadLength - 1) * Math.cos(arrowDirection) : endX;
|
||||||
let tail = true;
|
const endAy = arrowhead ? endY - (scaledArrowheadLength - 1) * Math.sin(arrowDirection) : endY;
|
||||||
if (!length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (length <= scaledArrowheadLength) {
|
|
||||||
switch (ifShort) {
|
|
||||||
case 'head': {
|
|
||||||
arrowhead = true;
|
|
||||||
tail = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'tail': {
|
|
||||||
arrowhead = false;
|
|
||||||
tail = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tail) {
|
|
||||||
const endAx = arrowhead ? endX - (scaledArrowheadLength) * Math.cos(arrowDirection) : endX;
|
|
||||||
const endAy = arrowhead ? endY - (scaledArrowheadLength) * Math.sin(arrowDirection) : endY;
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(startX, startY);
|
ctx.moveTo(startX, startY);
|
||||||
ctx.lineTo(endAx, endAy);
|
ctx.lineTo(endAx, endAy);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
|
||||||
if (arrowhead) {
|
if (arrowhead) {
|
||||||
this.drawArrowHead(endX, endY, arrowDirection, {style, length: arrowheadLength, fill});
|
this.drawArrowHead(endX, endY, arrowDirection, {style, length: arrowheadLength});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
drawArrowHead(x, y, direction, {style, length, fill}) {
|
drawArrowHead(x, y, direction, {style, length}) {
|
||||||
const arrowheadLength = length ?? ARROWHEAD_LENGTH;
|
const arrowheadLength = length ?? ARROWHEAD_LENGTH;
|
||||||
const arrowheadWidth = arrowheadLength / ARROWHEAD_LENGTH * ARROWHEAD_WIDTH;
|
const arrowheadWidth = arrowheadLength / ARROWHEAD_LENGTH * ARROWHEAD_WIDTH;
|
||||||
const ctx = this.ctx;
|
const ctx = this.ctx;
|
||||||
ctx.fillStyle = style;
|
ctx.fillStyle = style;
|
||||||
ctx.strokeStyle = style;
|
|
||||||
// To make this simple, draw the arrowhead and then rotate and translate it as needed.
|
// To make this simple, draw the arrowhead and then rotate and translate it as needed.
|
||||||
// Keep arrows at normal scale
|
// Keep arrows at normal scale
|
||||||
const scaledArrowheadLength = arrowheadLength / this.scale;
|
const scaledArrowheadLength = arrowheadLength / this.scale;
|
||||||
@ -212,11 +184,7 @@ export class Display {
|
|||||||
ctx.lineTo(-scaledArrowheadLength, -scaledArrowheadWidth / 2);
|
ctx.lineTo(-scaledArrowheadLength, -scaledArrowheadWidth / 2);
|
||||||
ctx.lineTo(-scaledArrowheadLength, scaledArrowheadWidth / 2);
|
ctx.lineTo(-scaledArrowheadLength, scaledArrowheadWidth / 2);
|
||||||
ctx.closePath();
|
ctx.closePath();
|
||||||
if (fill !== false) {
|
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
} else {
|
|
||||||
ctx.stroke();
|
|
||||||
}
|
|
||||||
ctx.resetTransform();
|
ctx.resetTransform();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
find
2
find
@ -1,2 +0,0 @@
|
|||||||
#!/bin/env bash
|
|
||||||
find . -not -path ".git" -name "*.html" -or -name "*.js" -print0 | xargs -0 grep -n --color "$@"
|
|
||||||
@ -15,7 +15,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div[id=simulator] {
|
div[id=simulator] {
|
||||||
position: float;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@ -69,14 +69,12 @@ export class Objects {
|
|||||||
obj.mass += rate * elapsedTime;
|
obj.mass += rate * elapsedTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.sim.playing) {
|
|
||||||
// Update positions. Simple Euler method for now.
|
// Update positions. Simple Euler method for now.
|
||||||
for (let i = 0; i < this.objects.length; i++) {
|
for (let i = 0; i < this.objects.length; i++) {
|
||||||
const obj = this.objects[i];
|
const obj = this.objects[i];
|
||||||
obj.position.x += obj.velocity.x * MOTION_TIME_SCALE;
|
obj.position.x += obj.velocity.x * MOTION_TIME_SCALE;
|
||||||
obj.position.y += obj.velocity.y * MOTION_TIME_SCALE;
|
obj.position.y += obj.velocity.y * MOTION_TIME_SCALE;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Display objects info
|
// Display objects info
|
||||||
if (DISPLAY_OBJECTS_INFO) {
|
if (DISPLAY_OBJECTS_INFO) {
|
||||||
|
|||||||
22
overlay.js
22
overlay.js
@ -7,12 +7,12 @@ export class Overlay {
|
|||||||
const infoBox = document.createElement('div');
|
const infoBox = document.createElement('div');
|
||||||
this.sim.div.appendChild(infoBox);
|
this.sim.div.appendChild(infoBox);
|
||||||
this.infoBox = infoBox;
|
this.infoBox = infoBox;
|
||||||
infoBox.style.position = 'relative';
|
infoBox.style.position = 'absolute';
|
||||||
infoBox.style.top = 0;
|
infoBox.style.top = 0;
|
||||||
infoBox.style.left = 0;
|
infoBox.style.left = 0;
|
||||||
infoBox.width = 'fit-content';
|
infoBox.width = 'fit-content';
|
||||||
infoBox.style.zIndex = 1;
|
infoBox.style.zIndex = 1;
|
||||||
infoBox.classList.add('draggable');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderInfo() {
|
renderInfo() {
|
||||||
@ -34,22 +34,4 @@ export class Overlay {
|
|||||||
this.infoBox.appendChild(table);
|
this.infoBox.appendChild(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update positions of draggable items
|
|
||||||
updateDraggable() {
|
|
||||||
const elements = document.querySelectorAll('.draggable');
|
|
||||||
for (let el of elements) {
|
|
||||||
if (!el.dragging) continue;
|
|
||||||
const {
|
|
||||||
elementStart: {x: x0, y: y0},
|
|
||||||
pointerStart: {x: x1, y: y1},
|
|
||||||
pointerEnd: {x: x2, y: y2}
|
|
||||||
} = el.dragging;
|
|
||||||
const dx = x2 - x1;
|
|
||||||
const dy = y2 - y1;
|
|
||||||
const left = x0 + dx;
|
|
||||||
const top = y0 + dy;
|
|
||||||
el.style.left = `${left}px`;
|
|
||||||
el.style.top = `${top}px`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
72
pointer.js
72
pointer.js
@ -1,89 +1,40 @@
|
|||||||
import {
|
import { POINTER_HISTORY_SIZE } from './config.js';
|
||||||
POINTER_HISTORY_SIZE,
|
|
||||||
ZOOM_IN_FACTOR,
|
|
||||||
ZOOM_OUT_FACTOR,
|
|
||||||
DISPLAY_CURSOR_INFO,
|
|
||||||
} from './config.js';
|
|
||||||
|
|
||||||
function dispatchEvent(target, eventType, data) {
|
|
||||||
const ev = new CustomEvent(eventType, {detail: data});
|
|
||||||
target.dispatchEvent(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Pointer {
|
export class Pointer {
|
||||||
sim = undefined;
|
sim = undefined;
|
||||||
|
|
||||||
pointerHistory = [];
|
pointerHistory = [];
|
||||||
draggingElement = undefined;
|
|
||||||
|
|
||||||
constructor(sim) {
|
constructor(sim) {
|
||||||
this.sim = sim;
|
this.sim = sim;
|
||||||
|
|
||||||
// Monitor mouse movements
|
// Monitor mouse movements
|
||||||
const el = window;
|
const el = window;
|
||||||
|
el.addEventListener('mousemove', e => {
|
||||||
|
this.handlePointerMove(this.sim.screenToSim(e.clientX, e.clientY));
|
||||||
|
});
|
||||||
|
|
||||||
el.addEventListener('pointermove', e => {
|
// Monitor touch events
|
||||||
// const velocity = this.sim.pointer.getPointerVelocity();
|
el.addEventListener('touchmove', e => {
|
||||||
if (this.draggingElement) {
|
const {pageX, pageY} = e.touches[0];
|
||||||
// e.preventDefault();
|
this.handlePointerMove(this.sim.screenToSim(pageX, pageY));
|
||||||
this.draggingElement.dragging.pointerEnd = {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
const {x, y} = this.sim.screenToSim(e.clientX, e.clientY);
|
|
||||||
this.handlePointerMove({x, y});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('pointerdown', e => {
|
el.addEventListener('pointerdown', e => {
|
||||||
// e.preventDefault();
|
|
||||||
let target = e.target;
|
|
||||||
while (target && !target.classList.contains('draggable')) {
|
|
||||||
target = target.parentElement;
|
|
||||||
}
|
|
||||||
if (target?.classList.contains('draggable')) {
|
|
||||||
// e.preventDefault();
|
|
||||||
this.draggingElement = target;
|
|
||||||
this.draggingElement.dragging = {
|
|
||||||
elementStart: {
|
|
||||||
x: parseInt(this.draggingElement.style.left),
|
|
||||||
y: parseInt(this.draggingElement.style.top),
|
|
||||||
},
|
|
||||||
pointerStart: {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
},
|
|
||||||
pointerEnd: {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
this.handlePointerDown(this.sim.screenToSim(e.clientX, e.clientY));
|
this.handlePointerDown(this.sim.screenToSim(e.clientX, e.clientY));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('pointerup', e => {
|
el.addEventListener('pointerup', e => {
|
||||||
// e.preventDefault();
|
|
||||||
if (this.draggingElement) {
|
|
||||||
this.draggingElement.dragging = undefined;
|
|
||||||
this.draggingElement = undefined;
|
|
||||||
this.lastPosition = {x: undefined, y: undefined};
|
|
||||||
} else {
|
|
||||||
this.handlePointerUp(this.sim.screenToSim(e.clientX, e.clientY));
|
this.handlePointerUp(this.sim.screenToSim(e.clientX, e.clientY));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
el.addEventListener('click', e => {
|
el.addEventListener('click', e => {
|
||||||
// e.preventDefault();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Monitor wheel events
|
// Monitor wheel events
|
||||||
el.addEventListener('wheel', e => {
|
el.addEventListener('wheel', e => {
|
||||||
// e.preventDefault();
|
|
||||||
// Wheel scroll down => positive deltaY => ZOOM IN
|
// Wheel scroll down => positive deltaY => ZOOM IN
|
||||||
const factor = e.deltaY > 0 ? ZOOM_IN_FACTOR : ZOOM_OUT_FACTOR;
|
const factor = e.deltaY > 0 ? 2 : 0.5;
|
||||||
this.sim.scheduleZoom(this.sim.screenToSim(e.clientX, e.clientY), factor);
|
this.sim.scheduleZoom(this.sim.screenToSim(e.clientX, e.clientY), factor);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -92,7 +43,7 @@ export class Pointer {
|
|||||||
getPointerVelocity() {
|
getPointerVelocity() {
|
||||||
// Average over pointer history
|
// Average over pointer history
|
||||||
if (this.pointerHistory.length < 2) {
|
if (this.pointerHistory.length < 2) {
|
||||||
return {x: 0, y: 0, dt: 1};
|
return {x: 0, y: 0};
|
||||||
}
|
}
|
||||||
const start = this.pointerHistory[0];
|
const start = this.pointerHistory[0];
|
||||||
const end = this.pointerHistory[this.pointerHistory.length - 1];
|
const end = this.pointerHistory[this.pointerHistory.length - 1];
|
||||||
@ -100,7 +51,6 @@ export class Pointer {
|
|||||||
return {
|
return {
|
||||||
x: (end.x - start.x) / dt,
|
x: (end.x - start.x) / dt,
|
||||||
y: (end.y - start.y) / dt,
|
y: (end.y - start.y) / dt,
|
||||||
dt
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,13 +88,13 @@ export class Pointer {
|
|||||||
|
|
||||||
// Handle cursor (mouse or touch) movement
|
// Handle cursor (mouse or touch) movement
|
||||||
handlePointerMove({x, y}) {
|
handlePointerMove({x, y}) {
|
||||||
this.updatePointer({x, y});
|
|
||||||
// TODO: If e.touches.length > 1, user may be engaging pinch to zoom
|
// TODO: If e.touches.length > 1, user may be engaging pinch to zoom
|
||||||
|
|
||||||
// If the cursor moves while creating an object, or while an object is selected,
|
// If the cursor moves while creating an object, or while an object is selected,
|
||||||
// update the position and velocity of the object
|
// update the position and velocity of the object
|
||||||
const obj = this.sim.objects.getSelectedOrCreating();
|
const obj = this.sim.objects.getSelectedOrCreating();
|
||||||
if (obj !== undefined) {
|
if (obj !== undefined) {
|
||||||
|
this.updatePointer({x, y});
|
||||||
const {x: vx, y: vy} = this.getPointerVelocity();
|
const {x: vx, y: vy} = this.getPointerVelocity();
|
||||||
obj.position.x = x;
|
obj.position.x = x;
|
||||||
obj.position.y = y;
|
obj.position.y = y;
|
||||||
|
|||||||
28
simulator.js
28
simulator.js
@ -2,15 +2,12 @@ import { Display } from './display.js';
|
|||||||
import { Overlay } from './overlay.js';
|
import { Overlay } from './overlay.js';
|
||||||
import { Pointer } from './pointer.js';
|
import { Pointer } from './pointer.js';
|
||||||
import { Objects } from './objects.js';
|
import { Objects } from './objects.js';
|
||||||
import { Toolbar } from './toolbar.js';
|
|
||||||
import { PlayPause } from './tools/play-pause.js';
|
|
||||||
import { SCALE_MAX, SCALE_MIN} from './config.js';
|
|
||||||
|
|
||||||
export class Sim {
|
export class Sim {
|
||||||
info = {};
|
info = {};
|
||||||
|
frame = 0;
|
||||||
time = undefined;
|
time = undefined;
|
||||||
nextZoom = undefined;
|
nextZoom = undefined;
|
||||||
playing = true;
|
|
||||||
|
|
||||||
display = undefined;
|
display = undefined;
|
||||||
overlay = undefined;
|
overlay = undefined;
|
||||||
@ -26,10 +23,6 @@ export class Sim {
|
|||||||
this.overlay = new Overlay(this);
|
this.overlay = new Overlay(this);
|
||||||
this.pointer = new Pointer(this);
|
this.pointer = new Pointer(this);
|
||||||
this.objects = new Objects(this);
|
this.objects = new Objects(this);
|
||||||
this.toolbar = new Toolbar(this);
|
|
||||||
|
|
||||||
// Set up toolbar
|
|
||||||
this.toolbar.addTool(new PlayPause(this.toolbar));
|
|
||||||
|
|
||||||
// Initiate main loop
|
// Initiate main loop
|
||||||
this.time = document.timeline.currentTime;
|
this.time = document.timeline.currentTime;
|
||||||
@ -45,40 +38,31 @@ export class Sim {
|
|||||||
// the new view origin should be x, y minus half the new view width and height
|
// the new view origin should be x, y minus half the new view width and height
|
||||||
// compute new scale
|
// compute new scale
|
||||||
this.display.scale = this.display.scale * factor;
|
this.display.scale = this.display.scale * factor;
|
||||||
if (this.display.scale > SCALE_MAX) this.display.scale = SCALE_MAX;
|
|
||||||
if (this.display.scale < SCALE_MIN) this.display.scale = SCALE_MIN;
|
|
||||||
// compute coordinates of new view frame
|
// compute coordinates of new view frame
|
||||||
this.display.viewOrigin.x = x - this.display.width / 2;
|
this.display.viewOrigin.x = x - this.display.width / 2;
|
||||||
this.display.viewOrigin.y = y - this.display.height / 2;
|
this.display.viewOrigin.y = y - this.display.height / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transform display coordinates to simulator coordinates using scale and viewOrigin
|
|
||||||
screenToSim(x, y) {
|
screenToSim(x, y) {
|
||||||
return this.display.screenToSim(x, y);
|
return this.display.screenToSim(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
play() {
|
|
||||||
this.playing = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pause() {
|
|
||||||
this.playing = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main loop
|
// Main loop
|
||||||
loop(currentTime) {
|
loop(currentTime) {
|
||||||
this.overlay.updateDraggable();
|
|
||||||
const elapsedTime = currentTime - this.time;
|
const elapsedTime = currentTime - this.time;
|
||||||
this.time = currentTime;
|
this.time = currentTime;
|
||||||
if (this.nextZoom) {
|
if (this.nextZoom) {
|
||||||
this.zoom(this.nextZoom);
|
this.zoom(this.nextZoom);
|
||||||
this.nextZoom = undefined;
|
this.nextZoom = undefined;
|
||||||
}
|
}
|
||||||
this.info['scale'] = this.display.scale;
|
|
||||||
this.objects.computeFrame(elapsedTime);
|
|
||||||
this.display.fillCanvas();
|
this.display.fillCanvas();
|
||||||
|
this.objects.computeFrame(elapsedTime);
|
||||||
|
|
||||||
|
this.info['scale'] = this.display.scale;
|
||||||
|
|
||||||
this.display.drawObjects();
|
this.display.drawObjects();
|
||||||
this.overlay.renderInfo();
|
this.overlay.renderInfo();
|
||||||
|
|
||||||
requestAnimationFrame(t => this.loop(t));
|
requestAnimationFrame(t => this.loop(t));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
24
tool.js
24
tool.js
@ -1,24 +0,0 @@
|
|||||||
// Idea here is, tool can declare its parameters;
|
|
||||||
// can call back to toolbar for whatever...
|
|
||||||
// through toolbar can access sim
|
|
||||||
|
|
||||||
export class Tool {
|
|
||||||
toolbar = undefined;
|
|
||||||
sim = undefined;
|
|
||||||
|
|
||||||
constructor(toolbar) {
|
|
||||||
this.toolbar = toolbar;
|
|
||||||
this.sim = this.toolbar.sim;
|
|
||||||
const div = document.createElement('div');
|
|
||||||
this.div = div;
|
|
||||||
div.style.position = 'relative';
|
|
||||||
div.style.top = 0;
|
|
||||||
div.style.left = 0;
|
|
||||||
div.style.border = '1px #0fb solid';
|
|
||||||
div.style.margin = '5px';
|
|
||||||
div.style.padding = '5px';
|
|
||||||
div.classList.add('draggable');
|
|
||||||
}
|
|
||||||
|
|
||||||
frame() {}
|
|
||||||
}
|
|
||||||
31
toolbar.js
31
toolbar.js
@ -1,31 +0,0 @@
|
|||||||
export class Toolbar {
|
|
||||||
sim = undefined;
|
|
||||||
tools = [];
|
|
||||||
|
|
||||||
constructor(sim) {
|
|
||||||
this.sim = sim;
|
|
||||||
|
|
||||||
// Create ourselves a div, as child of sim's div
|
|
||||||
const div = document.createElement('div');
|
|
||||||
this.div = div;
|
|
||||||
this.sim.div.appendChild(div);
|
|
||||||
div.style.position = 'relative';
|
|
||||||
div.style.width = 'fit-content';
|
|
||||||
div.style.top = 0;
|
|
||||||
div.style.left = 0;
|
|
||||||
div.style.zIndex = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tool: instance of Tool
|
|
||||||
addTool(tool) {
|
|
||||||
this.div.appendChild(tool.div);
|
|
||||||
this.tools.push(tool);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame() {
|
|
||||||
for (let tool in this.tools) {
|
|
||||||
// TODO: tool.frame()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { Tool } from '../tool.js';
|
|
||||||
|
|
||||||
export class PlayPause extends Tool {
|
|
||||||
playHTML = 'Play';
|
|
||||||
pauseHTML = 'Pause';
|
|
||||||
|
|
||||||
constructor(toolbar) {
|
|
||||||
super(toolbar);
|
|
||||||
|
|
||||||
// For now, use a regular button
|
|
||||||
const button = document.createElement('button');
|
|
||||||
button.style.width = '50px';
|
|
||||||
button.style.height = '50px';
|
|
||||||
this.div.appendChild(button);
|
|
||||||
if (this.playing) {
|
|
||||||
button.innerHTML = this.pauseHTML;
|
|
||||||
} else {
|
|
||||||
button.innerHTML = this.playHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.addEventListener('click', (e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (this.playing) {
|
|
||||||
button.innerHTML = this.playHTML;
|
|
||||||
this.playing = false;
|
|
||||||
} else {
|
|
||||||
button.innerHTML = this.pauseHTML;
|
|
||||||
this.playing = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get playing() {
|
|
||||||
return this.sim.playing;
|
|
||||||
}
|
|
||||||
|
|
||||||
set playing(playing) {
|
|
||||||
return this.sim.playing = playing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user