UNPKG

agentscape

Version:

Agentscape is a library for creating agent-based simulations. It provides a simple API for defining agents and their behavior, and for defining the environment in which the agents interact. Agentscape is designed to be flexible and extensible, allowing

156 lines (133 loc) 4.05 kB
import Render2D, { Render2DConstructor } from './Render2D' export type KeydownCallbackMap = { [key: string]: { callback: (e: KeyboardEvent) => void, options: { preventDefault?: boolean } } } export interface Animate2DConstructor extends Render2DConstructor { frameRenderCallback: (tick: number, renderer: Render2D) => void keydownCallbackMap?: KeydownCallbackMap } export default class Animate2D extends Render2D { private frameRenderCallback: (tick: number, renderer: Render2D) => void private runState: 'play'|'pause' = 'play' public tick: number = 0 private defaultKeydownCallbackMap: KeydownCallbackMap = { // space bar pauses animation 'Space': { callback: () => { if (this.runState === 'play') { // emit a pause event const event = new CustomEvent('pause') window.dispatchEvent(event) } else { // emit a play event const event = new CustomEvent('play') window.dispatchEvent(event) } }, options: {preventDefault: true} }, // return key steps animation forward 'Enter': { callback: () => { // emit a step event const event = new CustomEvent('step') window.dispatchEvent(event) }, options: {preventDefault: true} } } constructor(opts: Animate2DConstructor) { const { frameRenderCallback, autoPlay = true, frameRate = 60, title = 'Canvas', id = 'canvas_0', } = opts super({ ...opts, frameRate, autoPlay, title, id }) this.frameRenderCallback = frameRenderCallback this.frameRate = frameRate window.addEventListener('render', () => { this.frameRenderCallback(this.tick, this) }) window.addEventListener('stop', () => { this.pause() }) window.addEventListener('pause', () => { this.pause() }) window.addEventListener('play', () => { this.play() this.step() }) window.addEventListener('step', () => { if (this.runState === 'play') { return } this.step() }) window.addEventListener('fps', (e: Event) => { const customEvent = e as CustomEvent this.frameRate = customEvent.detail }) // create a keydown event handler window.addEventListener('keydown', (e: KeyboardEvent) => { const mergedMaps = {...this.defaultKeydownCallbackMap, ...opts.keydownCallbackMap} const codeAction = mergedMaps?.[e.code] if (codeAction) { const {callback, options} = codeAction callback(e) if (options.preventDefault) { e.preventDefault() } } }) if (autoPlay) { this.runState = 'play' this.play() } else { this.runState = 'pause' this.pause() } this.step() } public step() { this.frameRenderCallback(this.tick, this) this.tick++ if (this.runState === 'play') { setTimeout( () => requestAnimationFrame(() => this.step()), 1000 / this.frameRate ) } } public play() { this.runState = 'play' this.step() } public pause() { this.runState = 'pause' } public toggle() { if (this.runState === 'play') { this.pause() } else { this.play() } } public stepForward() { this.pause() this.step() } }