diff --git a/packages/ngn/public/demo.js b/packages/ngn/public/demo.js index f95c23a..abbb234 100755 --- a/packages/ngn/public/demo.js +++ b/packages/ngn/public/demo.js @@ -90,7 +90,13 @@ var createWorld = () => { let raf = null; let craf = null; if (typeof window !== "undefined") { - raf = requestAnimationFrame; + let now = performance.now(); + raf = (cb) => { + return requestAnimationFrame((timestamp) => { + now = timestamp; + cb(now); + }); + }; craf = cancelAnimationFrame; } else { let now = 0; diff --git a/packages/ngn/public/demo.js.map b/packages/ngn/public/demo.js.map index 40dfdd5..4a40c1d 100755 --- a/packages/ngn/public/demo.js.map +++ b/packages/ngn/public/demo.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../src/misc.ts", "../src/ids.ts", "../src/ngn.ts", "../src/packages/2d/canvas.ts", "../src/packages/2d/draw.ts", "../src/packages/2d/create2d.ts", "../src/packages/emitter/index.ts", "../src/demo.ts"], - "sourcesContent": ["/**\n * Generates a sinusoidal pulse between a minimum and maximum value at a specified frequency.\n *\n * @param time - The time variable, typically representing elapsed time.\n * @param freq - The frequency of the pulse in cycles per unit time (default is 1).\n * @param min - The minimum value of the pulse (default is 0).\n * @param max - The maximum value of the pulse (default is 1).\n * @returns The calculated pulse value at the given time.\n */\nexport function pulse(time: number, freq: number = 1, min: number = 0, max: number = 1): number {\n const halfRange = (max - min) / 2;\n return min + halfRange * (1 + Math.sin(2 * Math.PI * freq * time));\n}\n\n/**\n * Performs a linear interpolation between two numbers.\n * @param a The start value.\n * @param b The end value.\n * @param t The interpolation factor (0-1).\n * @returns The interpolated value.\n */\nexport function lerp(a: number, b: number, t: number): number {\n return (1 - t) * a + t * b;\n}\n\n/**\n * Performs spherical linear interpolation between two numbers.\n * @param a The start value.\n * @param b The end value.\n * @param t The interpolation factor, between 0 and 1.\n * @returns The interpolated value.\n */\nexport function slerp(a: number, b: number, t: number): number {\n const theta = Math.acos(Math.min(Math.max(a / b, -1), 1)) * t;\n return a * Math.cos(theta) + b * Math.sin(theta);\n}\n\nexport function extend(component: () => T) {\n return (overrides: Partial): (() => T) => {\n const extendedCompponent = () => ({ ...component(), ...overrides });\n Object.defineProperty(extendedCompponent, \"name\", {\n value: component.name,\n });\n return extendedCompponent;\n };\n}\n", "/*\n This is a mashup of github.com/lukeed/hexoid and github.com/paralleldrive/cuid\n Both are MIT licensed.\n\n ~ https://github.com/paralleldrive/cuid/blob/f507d971a70da224d3eb447ed87ddbeb1b9fd097/LICENSE\n --\n MIT License\n Copyright (c) 2012 Eric Elliott\n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n ~ https://github.com/lukeed/hexoid/blob/1070447cdc62d1780d2a657b0df64348fc1e5ec5/license\n --\n MIT License\n Copyright (c) Luke Edwards (lukeed.com)\n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\nconst HEX: string[] = [];\n\nfor (let i = 0; i < 256; i++) {\n HEX[i] = (i + 256).toString(16).substring(1);\n}\n\nfunction pad(str: string, size: number) {\n const s = \"000000\" + str;\n return s.substring(s.length - size);\n}\n\nconst SHARD_COUNT = 32;\n\nexport function getCreateId(opts: { init: number; len: number }) {\n const len = opts.len || 16;\n let str = \"\";\n let num = 0;\n const discreteValues = 1_679_616; // Math.pow(36, 4)\n let current = opts.init + Math.ceil(discreteValues / 2);\n\n function counter() {\n current = current <= discreteValues ? current : 0;\n current++;\n return (current - 1).toString(16);\n }\n\n return () => {\n if (!str || num === 256) {\n str = \"\";\n num = ((1 + len) / 2) | 0;\n while (num--) str += HEX[(256 * Math.random()) | 0];\n str = str.substring((num = 0), len);\n }\n\n const date = Date.now().toString(36);\n const paddedCounter = pad(counter(), 6);\n const hex = HEX[num++];\n\n const shardKey = parseInt(hex, 16) % SHARD_COUNT;\n\n return `ngn${date}${paddedCounter}${hex}${str}${shardKey}`;\n };\n}\n", "import { getCreateId } from \"./ids\";\n\nconst createId = getCreateId({ init: 0, len: 4 });\n\n/**\n * entity.id -> component.name -> index of component in entity.components\n *\n * This map stores indices of components in the entity component array.\n * The purpose of this map is to allow for fast lookups of components in the\n * entity.components array (e.g. entity.getComponent()).\n */\nexport const $eciMap = Symbol();\n\n/**\n * component.name -> array of entity.ids that have this component\n */\nexport const $ceMap = Symbol();\nexport const $eMap = Symbol();\nexport const $queryResults = Symbol();\nexport const $dirtyQueries = Symbol();\nexport const $queryDependencies = Symbol();\nexport const $systems = Symbol();\nexport const $running = Symbol();\nexport const $onEntityCreated = Symbol();\nexport const $mainLoop = Symbol();\n\n//#region Types\nexport type Component = () => {};\nexport type ComponentInstance = () => {\n __ngn__?: {\n parent: string;\n name: string;\n };\n} & {\n [key: string]: any;\n};\n\nexport type QueryConfig = Readonly<\n Partial<{\n /** Matches entities as long as the entity has all of the components in the provided array. */\n and: Component[];\n /** Matches entities as long as the entity has at least one of the components in the provided array. */\n or: Component[];\n /** Matches entities as long as the entity has none of the components in the provided array. */\n not: Component[];\n /** Matches entities that have any of these tag strings. */\n tag: string[];\n }>\n>;\n\nexport type Entity = Readonly<{\n id: string;\n components: ReturnType[];\n addTag: (tag: string) => Entity;\n removeTag: () => Entity;\n getTag: () => string;\n addComponent: (component: Component, defaults?: object) => Entity;\n removeComponent: (component: Component) => Entity;\n getComponent: (arg: T) => ReturnType;\n hasComponent: (component: Component) => boolean;\n destroy: () => void;\n}>;\n\nexport type QueryResults = {\n results: {\n entity: Entity;\n [componentName: string]: any;\n }[];\n};\n\nexport type SystemFn = (w: WorldState) => void;\nexport type SystemCls = { update: (w: WorldState) => void };\nexport type System = SystemCls | SystemFn;\n\nexport type WorldState = {\n [$eciMap]: { [key: number]: { [componentName: string]: number } };\n [$ceMap]: { [key: string]: string[] };\n [$eMap]: { [key: number]: any };\n [$dirtyQueries]: Set;\n [$queryDependencies]: Map>;\n [$queryResults]: { [key: string]: QueryResults };\n [$systems]: ((w: WorldState) => void)[];\n [$mainLoop]: (w: WorldState) => void;\n time: {\n /** The total elapsed time in seconds since the game loop started. */\n elapsed: number;\n /** The time in milliseconds since the last frame. */\n delta: number;\n /** The time in milliseconds since the last time the main loop was called. */\n loopDelta: number;\n /** The time in milliseconds of the last call to the main loop. */\n lastLoopDelta: number;\n /** The time scale of the game loop. */\n scale: number;\n /** The current frames per second. */\n fps: number;\n };\n [$running]: boolean;\n [$onEntityCreated]: ((e: Entity) => void)[];\n};\n\n//#region World API\nexport const createWorld = () => {\n const state: WorldState = {\n [$eciMap]: {},\n [$ceMap]: {},\n [$eMap]: {},\n [$dirtyQueries]: new Set(),\n [$queryDependencies]: new Map(),\n [$queryResults]: {},\n [$systems]: [],\n [$mainLoop]: null,\n time: {\n elapsed: 0,\n delta: 0,\n loopDelta: 0,\n lastLoopDelta: 0,\n scale: 1,\n fps: 0,\n },\n [$running]: false,\n [$onEntityCreated]: [],\n };\n\n const defineMain = (callback: (w?: WorldState) => void) => {\n state[$mainLoop] = callback;\n };\n\n //#region Frametime logic\n /**\n * start - starts the game loop.\n * @returns - a function to stop the loop.\n */\n const start = () => {\n let then = 0;\n let accumulator = 0;\n const boundLoop = handler.bind(start);\n let loopHandler = -1;\n const { time } = state;\n time.delta = 0;\n time.elapsed = 0;\n time.fps = 0;\n state[$running] = true;\n\n // type FPS = {\n // sampleSize: number;\n // value: number;\n // _sample_: number[];\n // _index_: number;\n // _lastTick_: number | false;\n // tick: () => number;\n // };\n\n // const fps: FPS = {\n // sampleSize: 60,\n // value: 0,\n // _sample_: [],\n // _index_: 0,\n // _lastTick_: false,\n // tick() {\n // if (!this._lastTick_) {\n // this._lastTick_ = performance.now();\n // return 0;\n // }\n\n // const now = performance.now();\n // const delta = (now - this._lastTick_) / 1000;\n // const fps = 1 / delta;\n // this._sample_[this._index_] = Math.round(fps);\n\n // const average = Math.round(\n // this._sample_.reduce((acc: number, value: number) => acc + value, 0) / this._sample_.length,\n // );\n\n // this.value = average;\n // this._lastTick_ = now;\n // this._index_ = (this._index_ + 1) % this.sampleSize;\n // return this.value;\n // },\n // };\n\n let raf: ((cb: FrameRequestCallback) => number) | null = null;\n let craf: ((handle: number) => void) | null = null;\n\n /**\n * Fake requestAnimationFrame and cancelAnimationFrame\n * so that we can run tests for this in node.\n */\n if (typeof window !== \"undefined\") {\n raf = requestAnimationFrame;\n craf = cancelAnimationFrame;\n } else {\n let now = 0;\n raf = (cb: FrameRequestCallback) => {\n return setTimeout(() => {\n now += 16.67;\n cb(now);\n }, 16.67);\n };\n\n craf = (id: number) => {\n clearTimeout(id);\n };\n }\n\n let xfps = 1;\n const xtimes = [];\n\n function handler(now: number) {\n if (!state[$running]) return craf(loopHandler);\n\n while (xtimes.length > 0 && xtimes[0] <= now - 1000) {\n xtimes.shift();\n }\n\n xtimes.push(now);\n xfps = xtimes.length;\n time.fps = xfps;\n\n time.delta = now - then;\n then = now;\n\n // const fpsValue = fps.tick();\n // time.fps = fpsValue;\n\n // time.fps = time.delta > 0 ? Math.round(1000 / time.delta) : 0;\n\n accumulator += time.delta * time.scale;\n\n // Calculate the threshold for stepping the world based on the current frame rate\n const stepThreshold = 1000 / (time.fps || 60);\n\n // Step the world only when the accumulated scaled time exceeds the threshold\n while (accumulator >= stepThreshold) {\n time.loopDelta = now - time.lastLoopDelta;\n time.lastLoopDelta = now;\n\n state[$mainLoop](state);\n accumulator -= stepThreshold;\n }\n\n time.elapsed += time.delta * 0.001;\n\n loopHandler = raf(boundLoop);\n }\n\n loopHandler = raf(boundLoop);\n\n return () => (state[$running] = false);\n };\n\n const stop = () => {\n state[$running] = false;\n };\n\n function step() {\n for (const system of state[$systems]) {\n system(state);\n }\n }\n\n /**\n * Adds one or more systems to the ECS world.\n * A system can be either a @see SystemFn or a @see SystemCls.\n * @param systems An array of system classes or functions.\n * @throws {Error} If a system is not a valid system class or function.\n */\n function addSystem(...systems: (SystemCls | SystemFn)[]) {\n for (const system of systems) {\n // If the system is a function, add it to the world systems array\n if (typeof system === \"function\") {\n state[$systems].push(system);\n // If the system has an `update` method, add that method to the world systems array\n } else if (system.update && typeof system.update === \"function\") {\n state[$systems].push(system.update);\n // If the system is not a valid system class or function, throw an error\n } else {\n throw new Error(`Not a valid system: ${JSON.stringify(system)}`);\n }\n }\n }\n\n /**\n * Removes one or more systems from the world.\n *\n * @param {...(SystemCls | SystemFn)[]} systems - The system or systems to remove.\n * @throws {TypeError} Throws an error if the system parameter is not a function or an object with an update function.\n * @returns {void}\n */\n function removeSystem(...systems: (SystemCls | SystemFn)[]): void {\n for (const system of systems) {\n if (typeof system === \"function\") {\n state[$systems] = state[$systems].filter((s) => s !== system);\n } else if (system.update && typeof system.update === \"function\") {\n state[$systems] = state[$systems].filter((s) => s !== system.update);\n } else {\n throw new TypeError(\"Parameter must be a function or an object with an update function.\");\n }\n }\n }\n\n //#region Query logic\n /**\n * Retrieves query results based on the given configuration and query name.\n * If non-dirty query results exist for this queryName, returns them. Otherwise, filters entities based on the queryConfig\n * and updates the state with the new query results before returning them.\n *\n * @param {QueryConfig} queryConfig - The configuration object containing 'and', 'or', 'not' and 'tag' arrays of component names.\n * @param {string} queryName - The name of the query to retrieve or update results for.\n * @returns {any[]} An array of result objects, each containing an entity and its components as properties.\n */\n const getQuery = (queryConfig: QueryConfig, queryName: string) => {\n // If we have non-dirty query results for this queryName, return them\n if (!state[$dirtyQueries].has(queryName) && state[$queryResults][queryName]) {\n return state[$queryResults][queryName].results;\n }\n\n const { and = [], or = [], not = [], tag = [] } = queryConfig;\n const entities: Entity[] = Object.values(state[$eMap]).filter((entity) => {\n return (\n (!not.length || !not.some((component) => entity.hasComponent(component))) &&\n (!and.length || and.every((component) => entity.hasComponent(component))) &&\n (!or.length || or.some((component) => entity.hasComponent(component))) &&\n (!tag.length || tag.some((t) => entity.tag === t))\n );\n });\n\n state[$queryResults][queryName] = {\n results: entities.map((entity) => {\n const result: any = { entity };\n\n entity.components.forEach((component) => {\n result[component.__ngn__.name] = component;\n });\n\n return result;\n }),\n };\n\n state[$dirtyQueries].delete(queryName);\n\n return state[$queryResults][queryName].results;\n };\n\n const markQueryDirty = (queryName: string) => {\n state[$dirtyQueries].add(queryName);\n };\n\n /**\n * Defines a query for filtering entities based on a combination of criteria.\n * @param queryConfig The configuration for the query. Contains and, or, not and tag criteria.\n * @throws {Error} Invalid query if any criteria in the query config does not have a 'name' property.\n * @returns A function that takes a query implementation and returns the results of the query.\n */\n const query = ({ and = [], or = [], not = [], tag = [] }: QueryConfig) => {\n // Checks if a criteria object has a 'name' property\n const validQuery = (c: Component) => Object.prototype.hasOwnProperty.call(c, \"name\");\n\n // Throws an error if any criteria object in the query config does not have a 'name' property\n if (![...and, ...or, ...not].every(validQuery)) throw new Error(\"Invalid query\");\n\n // Constructs a string representing the query name based on the criteria in the query config\n const queryName = [\"and\", ...and.map((c) => c.name), \"or\", ...or.map((c) => c.name), \"not\", ...not.map((c) => c.name), \"tag\", ...tag].join(\"\");\n\n // Component dependencies\n [...and, ...or, ...not].forEach((c) => {\n const dependencies = state[$queryDependencies].get(c.name) || new Set();\n dependencies.add(queryName);\n state[$queryDependencies].set(c.name, dependencies);\n });\n\n // Tag dependencies\n tag.forEach((t) => {\n const tagKey = `tag:${t}`;\n const dependencies = state[$queryDependencies].get(tagKey) || new Set();\n dependencies.add(queryName);\n state[$queryDependencies].set(tagKey, dependencies);\n });\n\n return (queryImpl: (results: { entity: Entity }[]) => void) => queryImpl(getQuery({ and, or, not, tag }, queryName));\n };\n\n function destroyEntity(e: Entity) {\n const exists = state[$eMap][e.id];\n\n if (!exists) return false;\n\n const componentsToRemove: string[] = Object.keys(state[$eciMap][e.id]);\n\n componentsToRemove.forEach((componentName) => {\n state[$ceMap][componentName] = state[$ceMap][componentName].filter((id) => id !== e.id);\n });\n\n delete state[$eciMap][e.id];\n delete state[$eMap][e.id];\n\n componentsToRemove.forEach((componentName) => {\n const affectedQueries = state[$queryDependencies].get(componentName);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n });\n\n return true;\n }\n\n function onEntityCreated(fn: any) {\n if (typeof fn !== \"function\") return;\n\n state[$onEntityCreated].push(fn);\n\n return () => {\n state[$onEntityCreated] = state[$onEntityCreated].filter((f) => f !== fn);\n };\n }\n\n /**\n * Creates a new component for the given entity and adds it to the world.\n * @param entity The entity to add the component to.\n * @param component The component function to add.\n * @param defaults (optional) Default values to apply to the component.\n * @returns The modified entity with the new component added.\n */\n function createComponent(entity: Entity, component: Function, defaults: object = {}): Entity {\n // If the entity already has this component, return the unmodified entity.\n if (state[$eciMap]?.[entity.id]?.[component.name] !== undefined) return entity;\n\n const affectedQueries = state[$queryDependencies].get(component.name);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n\n const componentInstance = component();\n\n if (componentInstance.onAttach && typeof componentInstance.onAttach === \"function\") {\n componentInstance.onAttach(entity);\n }\n\n // Create the component, assigning defaults and a reference to the parent entity.\n entity.components.push(\n Object.assign(\n {},\n {\n ...componentInstance,\n ...defaults,\n __ngn__: {\n parent: entity.id,\n name: component.name,\n },\n },\n ) as ComponentInstance,\n );\n\n // Add the component index to the entity's index map.\n state[$eciMap][entity.id] = state[$eciMap][entity.id] || {};\n state[$eciMap][entity.id][component.name] = entity.components.length - 1;\n\n // Add the entity to the component's entity map.\n state[$ceMap][component.name] = state[$ceMap][component.name] || [];\n state[$ceMap][component.name].push(entity.id);\n\n return entity;\n }\n\n //#region Entity API\n /**\n * Creates an entity with the given specification object.\n * @param {object} spec - Optional data to be stored on the entity.\n * @returns {any} - Returns the created entity.\n */\n function createEntity(spec: T & { id?: string } = {} as T): T & Entity {\n const id = spec.id ?? createId();\n const components: any[] = [];\n\n const tagKey = (t: string) => `tag:${t}`;\n\n function updateTagQueries(tagKey: string) {\n const affectedQueries = state[$queryDependencies].get(tagKey);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n }\n\n function addTag(t: string): Entity {\n const previousTagKey = tagKey(this.tag);\n\n this.tag = t;\n\n updateTagQueries(tagKey(t));\n updateTagQueries(previousTagKey);\n\n return this;\n }\n\n function removeTag(): Entity {\n const previousTagKey = tagKey(this.tag);\n this.tag = \"\";\n\n updateTagQueries(previousTagKey);\n\n return this;\n }\n\n function getTag() {\n return this.tag;\n }\n\n function addComponent(c: Component, defaults = {}) {\n return createComponent(this, c, defaults);\n }\n\n function hasComponent(component: Component) {\n return state[$eciMap]?.[id]?.[component.name] !== undefined;\n }\n\n function getComponent(arg: T): ReturnType {\n const index = state[$eciMap][id][arg.name];\n return components[index];\n }\n\n /**\n * Removes the specified component from the entity and updates the world state accordingly.\n *\n * @param component The component to remove from the entity.\n * @returns The modified entity.\n */\n function removeComponent(component: Component | string): Entity {\n const name = typeof component === \"string\" ? component : component.name;\n\n const componentInstance = getComponent(typeof component === \"string\" ? ({ name } as any) : component);\n\n if (componentInstance && componentInstance.onDetach && typeof componentInstance.onDetach === \"function\") {\n componentInstance.onDetach(this);\n }\n\n const affectedQueries = state[$queryDependencies].get(name);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n\n // Set the entity's component index for the specified component to undefined.\n state[$eciMap][id][name] = undefined;\n\n // Remove the entity's ID from the component's entity list.\n state[$ceMap][name] = state[$ceMap][name].filter((e) => e !== id);\n\n // Remove the component from the entity's component list.\n const index = state[$eciMap][id][name];\n components.splice(index, 1);\n\n // Update the entity's component indices for all components after the removed component.\n Object.keys(state[$eciMap][id]).forEach((componentName) => {\n if (state[$eciMap][id][componentName] > components.findIndex((c) => c.name === componentName)) {\n state[$eciMap][id][componentName]--;\n }\n });\n\n // Return the modified entity.\n return this;\n }\n\n function destroy() {\n return destroyEntity(this);\n }\n\n const entity = Object.assign({}, spec, {\n id,\n components,\n addTag,\n removeTag,\n getTag,\n addComponent,\n hasComponent,\n getComponent,\n removeComponent,\n destroy,\n });\n\n // If we are focing a specific entity id, we need to migrate any\n // entity that might already occupy this space.\n if (spec.id !== undefined && state[$eMap][spec.id]) {\n migrateEntityId(spec.id, createId());\n }\n\n state[$eMap][id] = entity;\n state[$eciMap][id] = {};\n\n state[$onEntityCreated].forEach((fn) => {\n fn(entity);\n });\n\n return entity as unknown as T & Entity;\n }\n\n /**\n * migrateEntityId updates the id of an entity in the world, and all\n * associated world maps.\n * @param oldId The id of the entity to migrate.\n * @param newId The id to migrate the entity to.\n */\n function migrateEntityId(oldId: string, newId: string) {\n const entity = state[$eMap][oldId];\n\n if (!entity) return;\n\n entity.id = newId;\n\n state[$eMap][newId] = entity;\n delete state[$eMap][oldId];\n\n state[$eciMap][newId] = state[$eciMap][oldId];\n delete state[$eciMap][oldId];\n }\n\n function getEntity(id: string): Entity {\n return state[$eMap][id];\n }\n\n return {\n state,\n query,\n createEntity,\n getEntity,\n onEntityCreated,\n addSystem,\n removeSystem,\n start,\n stop,\n step,\n defineMain,\n };\n};\n", "export type CreateCanvasOptions = Partial<{\n width: number;\n height: number;\n fullscreen: boolean;\n target: HTMLElement;\n}>;\n\nexport const createCanvas = (options: CreateCanvasOptions) => {\n const canvas = document.createElement(\"canvas\");\n const { target, fullscreen } = options;\n const { body } = window.document;\n\n if (target && fullscreen) {\n options.target = null;\n } else if (!target && !fullscreen) {\n options.fullscreen = true;\n }\n\n if (fullscreen) {\n Object.assign(canvas.style, {\n position: \"absolute\",\n top: \"0\",\n left: \"0\",\n });\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n body.appendChild(canvas);\n Object.assign(body.style, {\n margin: \"0\",\n padding: \"0\",\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n });\n }\n\n if (target) {\n target.appendChild(canvas);\n target.style.overflow = \"hidden\";\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n }\n\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n\n const existingMeta = window.document.querySelector(`meta[name=\"viewport\"]`);\n if (existingMeta) {\n Object.assign(existingMeta, {\n content: \"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\",\n });\n } else {\n const meta = Object.assign(window.document.createElement(\"meta\"), {\n name: \"viewport\",\n content: \"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\",\n });\n window.document.head.appendChild(meta);\n }\n\n return canvas;\n};\n", "export type Vector2 = {\n x: number;\n y: number;\n};\n\nconst fastRound = (num: number): number => ~~(0.5 + num);\n\nconst fastRoundVector2 = (v: Vector2): Vector2 => ({\n x: fastRound(v.x),\n y: fastRound(v.y),\n});\n\nexport const createDraw = (context: CanvasRenderingContext2D) => {\n const text = (v: Vector2, text: string, color: string = \"black\", size: number = 16) => {\n v = fastRoundVector2(v);\n context.save();\n context.fillStyle = color;\n context.font = `${size}px sans-serif`;\n context.fillText(text, v.x, v.y);\n context.restore();\n };\n\n const line = (from: Vector2, to: Vector2, color: string = \"black\", lineWidth: number = 1) => {\n from = fastRoundVector2(from);\n to = fastRoundVector2(to);\n context.save();\n context.beginPath();\n context.moveTo(from.x, from.y);\n context.lineTo(to.x, to.y);\n context.strokeStyle = color; // Use the color parameter\n context.lineWidth = lineWidth;\n context.stroke();\n context.closePath();\n context.restore();\n };\n\n const rectangle = (pos: Vector2, dimensions: Vector2, color: string = \"black\", lineWidth: number = 1) => {\n pos = fastRoundVector2(pos);\n dimensions = fastRoundVector2(dimensions);\n context.save();\n context.beginPath();\n context.rect(pos.x, pos.y, dimensions.x, dimensions.y);\n context.lineWidth = lineWidth;\n context.strokeStyle = color;\n context.stroke();\n context.closePath();\n context.restore();\n };\n\n const circle = (pos: Vector2, radius: number = 25, color: string = \"black\", lineWidth: number = 1) => {\n pos = fastRoundVector2(pos);\n context.save();\n context.beginPath();\n context.strokeStyle = color;\n context.lineWidth = lineWidth;\n context.arc(pos.x, pos.y, radius, 0, Math.PI * 2, true);\n context.stroke();\n context.closePath();\n context.restore();\n };\n\n return {\n text,\n line,\n rectangle,\n circle,\n };\n};\n", "import { createCanvas, CreateCanvasOptions } from \"./canvas\";\nimport { createDraw } from \"./draw\";\n\ntype Create2DOptions = Partial<{\n canvas: CreateCanvasOptions;\n}>;\n\nexport const create2D = (options: Create2DOptions) => {\n const { width = 800, height = 600, fullscreen = false, target = null } = options.canvas;\n const canvas = createCanvas({ width, height, fullscreen, target });\n const context = canvas.getContext(\"2d\");\n const draw = createDraw(context);\n\n const onWindowResize = () => {\n if (fullscreen) {\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n return;\n }\n\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n };\n\n window.addEventListener(\"resize\", onWindowResize);\n\n const destroy = () => {\n window.removeEventListener(\"resize\", onWindowResize);\n canvas.parentElement.removeChild(canvas);\n };\n\n return { canvas, context, draw, destroy };\n};\n", "import { WorldState } from \"../../ngn\";\n\nexport type Particle = {\n x: number;\n y: number;\n size: number;\n color: string;\n colorStart: string;\n colorEnd: string;\n lifetime: number;\n speedX: number;\n speedY: number;\n scaleX: number;\n scaleY: number;\n onInit?: (particle: Particle, state: WorldState) => void;\n onRemove?: (particle: Particle, state: WorldState) => void;\n onUpdate?: (particle: Particle, state: WorldState) => void;\n};\n\nexport enum ColorEasing {\n LINEAR = \"linear\",\n EASE_IN = \"easeIn\",\n EASE_OUT = \"easeOut\",\n EASE_IN_OUT = \"easeInOut\",\n}\n\nexport type FadeEasing = ColorEasing;\n\ntype BlendMode =\n | \"color\"\n | \"color-burn\"\n | \"color-dodge\"\n | \"copy\"\n | \"darken\"\n | \"destination-atop\"\n | \"destination-in\"\n | \"destination-out\"\n | \"destination-over\"\n | \"difference\"\n | \"exclusion\"\n | \"hard-light\"\n | \"hue\"\n | \"lighten\"\n | \"lighter\"\n | \"luminosity\"\n | \"multiply\"\n | \"overlay\"\n | \"saturation\"\n | \"screen\"\n | \"soft-light\"\n | \"source-atop\"\n | \"source-in\"\n | \"source-out\"\n | \"source-over\"\n | \"xor\";\n\nexport type ParticleEmitterOptions = {\n x?: number; // X position\n y?: number; // Y position\n maxParticles?: number; // Max number of particles\n rate?: number; // Particles per second\n lifetime?: number; // Lifetime of each particle\n lifetimeVariation?: number; // Variation in lifetime\n size?: number; // Size of each particle\n sizeVariation?: number; // Variation in size\n colorStart?: string | string[]; // Start color\n colorEnd?: string | string[]; // End color\n colorEasing?: ColorEasing; // Easing function for color\n fadeOutEasing?: FadeEasing;\n speed?: number; // Speed of each particle\n speedVariation?: number; // Variation in speed\n angle?: number; // Angle of emission\n spread?: number; // Spread of emission\n gravity?: { x: number; y: number }; // Gravity affecting the particles\n blendMode?: BlendMode; // Blend mode\n canvas: HTMLCanvasElement; // Canvas to draw on\n burst?: boolean; // If true, emit all particles at once and then stop\n /** Per-particle initialization callback. */\n onInit?: (particle: Particle, state: WorldState) => void; // Callback for particle initialization\n /** Per-particle update callback. */\n onUpdate?: (particle: Particle, state: WorldState) => void; // Callback for particle update\n /** Per-particle removal callback. */\n onRemove?: (particle: Particle, state: WorldState) => void; // Callback for particle removal\n};\n\nconst getDefaultParticleEmitterOptions = (opts: Partial): ParticleEmitterOptions => ({\n ...opts,\n x: opts.x ?? 0,\n y: opts.y ?? 0,\n maxParticles: opts.maxParticles ?? 100,\n rate: opts.rate ?? 1,\n lifetime: opts.lifetime ?? 1000,\n lifetimeVariation: opts.lifetimeVariation ?? 0,\n size: opts.size ?? 5,\n sizeVariation: opts.sizeVariation ?? 0,\n colorStart: opts.colorStart ?? \"#000000\",\n colorEnd: opts.colorEnd ?? \"#000000\",\n colorEasing: opts.colorEasing ?? ColorEasing.LINEAR,\n angle: opts.angle ?? 0,\n spread: opts.spread ?? 0,\n gravity: opts.gravity ?? { x: 0, y: 0 },\n speed: opts.speed ?? 0.1,\n speedVariation: opts.speedVariation ?? 0,\n canvas: opts.canvas,\n burst: opts.burst ?? false,\n});\n\nexport type ParticleEmitter = {\n particles: Particle[];\n update: (state: WorldState) => void;\n destroy: () => void;\n pause: () => void;\n resume: () => void;\n x: number;\n y: number;\n};\n\nexport type ParticleSystemOptions = {\n x: number;\n y: number;\n canvas: HTMLCanvasElement;\n // A property to determine whether or not it starts immediately\n start?: boolean;\n};\n\nconst interpolateColor = (colorStart: string, colorEnd: string, factor: number, easing: ColorEasing): string => {\n switch (easing) {\n case ColorEasing.EASE_IN:\n factor = Math.pow(factor, 2);\n break;\n case ColorEasing.EASE_OUT:\n factor = 1 - Math.pow(1 - factor, 2);\n break;\n case ColorEasing.EASE_IN_OUT:\n if (factor < 0.5) {\n factor = 2 * Math.pow(factor, 2);\n } else {\n factor = 1 - 2 * Math.pow(1 - factor, 2);\n }\n break;\n case ColorEasing.LINEAR:\n default:\n // No adjustment needed for linear\n break;\n }\n\n // Assuming colorStart and colorEnd are in the format \"#RRGGBB\"\n const color1 = parseInt(colorStart.slice(1), 16);\n const color2 = parseInt(colorEnd.slice(1), 16);\n\n const r1 = (color1 >> 16) & 0xff;\n const g1 = (color1 >> 8) & 0xff;\n const b1 = color1 & 0xff;\n\n const r2 = (color2 >> 16) & 0xff;\n const g2 = (color2 >> 8) & 0xff;\n const b2 = color2 & 0xff;\n\n const r = Math.round(r1 + factor * (r2 - r1));\n const g = Math.round(g1 + factor * (g2 - g1));\n const b = Math.round(b1 + factor * (b2 - b1));\n\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;\n};\n\nconst hexToRgb = (hex: string): string => {\n const color = parseInt(hex.slice(1), 16);\n const r = (color >> 16) & 0xff;\n const g = (color >> 8) & 0xff;\n const b = color & 0xff;\n return `${r}, ${g}, ${b}`;\n};\n\nexport const createParticleEmitter = (opts: ParticleEmitterOptions): ParticleEmitter => {\n opts = getDefaultParticleEmitterOptions(opts);\n const particles = [];\n let timeSinceLastEmission = 0;\n const emissionInterval = 1 / opts.rate;\n const lifetimeVariation = opts.lifetimeVariation ?? 0;\n const context = opts.canvas.getContext(\"2d\");\n const angleInRadians = opts.angle * (Math.PI / 180);\n let dead = false;\n let paused = false;\n\n const update = (state: WorldState) => {\n if (dead) return;\n\n context.globalCompositeOperation = opts.blendMode ?? \"source-over\";\n\n const { loopDelta } = state.time;\n\n for (let i = particles.length - 1; i >= 0; i--) {\n const particle = particles[i];\n const lifeFactor = particle.lifetime / opts.lifetime;\n let opacity = 1;\n\n particle.color = interpolateColor(particle.colorStart, particle.colorEnd, 1 - lifeFactor, opts.colorEasing);\n\n if (opts.fadeOutEasing) {\n switch (opts.fadeOutEasing) {\n case ColorEasing.EASE_IN:\n opacity = Math.pow(lifeFactor, 2);\n break;\n case ColorEasing.EASE_OUT:\n opacity = 1 - Math.pow(1 - lifeFactor, 2);\n break;\n case ColorEasing.EASE_IN_OUT:\n if (lifeFactor < 0.5) {\n opacity = 2 * Math.pow(lifeFactor, 2);\n } else {\n opacity = 1 - 2 * Math.pow(1 - lifeFactor, 2);\n }\n break;\n case ColorEasing.LINEAR:\n default:\n opacity = lifeFactor;\n break;\n }\n }\n\n if (!paused) {\n particle.x += particle.speedX * loopDelta;\n particle.y += particle.speedY * loopDelta;\n\n particle.speedX += (opts.gravity.x * loopDelta) / 1000;\n particle.speedY += (opts.gravity.y * loopDelta) / 1000;\n\n particle.lifetime -= state.time.loopDelta;\n\n if (opts.onUpdate) {\n opts.onUpdate(particle, state);\n }\n\n if (particle.onUpdate) {\n particle.onUpdate(particle, state);\n }\n }\n\n if (particle.lifetime <= 0) {\n if (opts.onRemove) {\n opts.onRemove(particle, state);\n }\n\n if (particle.onRemove) {\n particle.onRemove(particle, state);\n }\n\n particles.splice(i, 1);\n } else {\n context.fillStyle = `rgba(${hexToRgb(particle.color)}, ${opacity})`;\n context.beginPath();\n // context.arc(particle.x * particle.scaleX, particle.y * particle.scaleY, particle.size, 0, Math.PI * 2);\n // draw a rectangel instead:\n context.rect(particle.x * particle.scaleX, particle.y * particle.scaleY, particle.size, particle.size);\n context.closePath();\n context.fill();\n }\n }\n\n const emitParticle = () => {\n const lifetimeVariationAmount = lifetimeVariation ? opts.lifetime * lifetimeVariation * Math.random() : 0;\n const particleLifetime = opts.lifetime + lifetimeVariationAmount * (Math.random() < 0.5 ? -1 : 1);\n const colorStart = Array.isArray(opts.colorStart) ? opts.colorStart[Math.floor(Math.random() * opts.colorStart.length)] : opts.colorStart;\n const colorEnd = Array.isArray(opts.colorEnd) ? opts.colorEnd[Math.floor(Math.random() * opts.colorEnd.length)] : opts.colorEnd;\n const particle = spawnParticle({\n x: opts.x,\n y: opts.y,\n colorStart: colorStart,\n colorEnd: colorEnd,\n color: colorStart,\n lifetime: particleLifetime,\n size: Math.max(0, opts.size + (Math.random() - 0.5) * opts.sizeVariation),\n speedX: opts.speed * (Math.sin(angleInRadians) + (Math.random() - 0.5) * opts.spread),\n speedY: -opts.speed * (Math.cos(angleInRadians) + (Math.random() - 0.5) * opts.spread),\n scaleX: 1,\n scaleY: 1,\n });\n\n if (opts.onInit) {\n opts.onInit(particle, state);\n }\n\n if (particle.onInit) {\n particle.onInit(particle, state);\n }\n };\n\n if (!paused) {\n if (opts.burst && timeSinceLastEmission === 0) {\n for (let i = 0; i < opts.maxParticles; i++) {\n emitParticle();\n }\n\n timeSinceLastEmission = -1;\n } else if (!opts.burst) {\n timeSinceLastEmission += loopDelta;\n\n while (timeSinceLastEmission >= emissionInterval && particles.length < opts.maxParticles) {\n emitParticle();\n timeSinceLastEmission -= emissionInterval;\n }\n }\n }\n\n if (opts.burst && particles.length === 0) {\n destroy();\n }\n\n context.globalCompositeOperation = \"source-over\";\n };\n\n const destroy = () => {\n dead = true;\n particles.length = 0;\n return;\n };\n\n const spawnParticle = (p: Particle): Particle => {\n particles.push(p);\n return p;\n };\n\n const pause = () => {\n paused = true;\n };\n\n const resume = () => {\n paused = false;\n };\n\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"visible\") {\n resume();\n } else {\n pause();\n }\n });\n\n return {\n particles,\n update,\n destroy,\n pause,\n resume,\n set x(value: number) {\n opts.x = value;\n },\n get x() {\n return opts.x;\n },\n set y(value: number) {\n opts.y = value;\n },\n get y() {\n return opts.y;\n },\n };\n};\n\nexport const createParticleSystem = (opts: ParticleSystemOptions) => {\n let _x = opts.x;\n let _y = opts.y;\n const emitters: ParticleEmitter[] = [];\n const startImmediately = opts.start ?? true;\n\n const update = (state: WorldState) => {\n emitters.forEach((emitter) => {\n emitter.update(state);\n });\n };\n\n const destroy = () => {\n emitters.forEach((emitter) => {\n emitter.destroy();\n });\n };\n\n const createEmitter = (opts: ParticleEmitterOptions): ParticleEmitter => {\n const emitter = createParticleEmitter({\n ...opts,\n /* x: _x, */\n /* y: _y, */\n });\n emitters.push(emitter);\n\n if (!startImmediately) {\n emitter.pause();\n }\n\n return emitter;\n };\n\n const pause = () => {\n emitters.forEach((emitter) => {\n emitter.pause();\n });\n };\n\n const start = () => {\n emitters.forEach((emitter) => {\n emitter.resume();\n });\n };\n\n return {\n update,\n destroy,\n createEmitter,\n pause,\n start,\n set x(value: number) {\n _x = value;\n },\n get x() {\n return _x;\n },\n set y(value: number) {\n _y = value;\n },\n get y() {\n return _y;\n },\n get numParticles() {\n return emitters.reduce((acc, emitter) => acc + emitter.particles.length, 0);\n },\n };\n};\n", "import { pulse } from \"./misc\";\nimport { createWorld, type WorldState } from \"./ngn\";\nimport { create2D } from \"./packages/2d\";\nimport { ColorEasing, createParticleSystem, Particle } from \"./packages/emitter\";\n\nconst { canvas, draw } = create2D({ canvas: { fullscreen: true } });\n\nconst particleSystem = createParticleSystem({\n x: canvas.width / 2,\n y: canvas.height / 2,\n canvas,\n});\n\nconst emitter = particleSystem.createEmitter({\n x: canvas.width / 2,\n y: canvas.height / 2,\n maxParticles: 100,\n rate: 0.1,\n lifetime: 1000,\n lifetimeVariation: 0.2,\n size: 20,\n sizeVariation: 10,\n colorStart: [\"#FF0000\", \"#ff5100\"],\n colorEnd: \"#222222\",\n colorEasing: ColorEasing.EASE_IN,\n fadeOutEasing: ColorEasing.EASE_OUT,\n speed: 0.1,\n speedVariation: 1,\n angle: 0,\n spread: 0.75,\n gravity: { x: 0, y: 0 },\n canvas,\n burst: false,\n\n onInit: (particle: Particle, state: WorldState) => {\n particle.x += Math.random() < 0.5 ? -6 : 6;\n particle.y += Math.random() < 0.5 ? -6 : 6;\n\n if (!(Math.random() < 0.02)) {\n return;\n }\n\n particle.size = 15;\n particle.speedY = -0.3;\n particle.lifetime = 1000;\n particle.colorEnd = \"#ff0000\";\n\n particle.onRemove = () => {\n particleSystem.createEmitter({\n x: particle.x,\n y: particle.y,\n maxParticles: 3,\n lifetimeVariation: 0.2,\n size: 3,\n sizeVariation: 2,\n colorStart: [\"#FF0000\", \"#ff5100\"],\n colorEnd: \"#222222\",\n colorEasing: ColorEasing.EASE_IN,\n fadeOutEasing: ColorEasing.EASE_OUT,\n speed: 0.02,\n speedVariation: 1,\n spread: 6,\n angle: 180,\n canvas,\n burst: true,\n });\n };\n\n particle.onUpdate = () => {\n particle.size = Math.max(0, particle.size + 0.25);\n };\n },\n onUpdate: (particle: Particle, state: WorldState) => {\n particle.size = Math.max(0, particle.size - 0.35);\n const v = pulse(state.time.elapsed, 0.25, -1, 1);\n particle.x += v * 1;\n },\n onRemove: (particle: Particle, state: WorldState) => {},\n});\n\nconst { addSystem, start, step, defineMain } = createWorld();\n\nconst clearCanvasSystem = () => {\n const context = canvas.getContext(\"2d\");\n context.clearRect(0, 0, canvas.width, canvas.height);\n\n context.fillStyle = \"#111\";\n context.fillRect(0, 0, canvas.width, canvas.height);\n};\n\nconst fpsDrawSystem = (state: WorldState) => {\n draw.text({ x: 10, y: 20 }, `FPS: ${state.time.fps.toFixed(2)}`, \"white\");\n};\n\nconst particleCountSystem = (state: WorldState) => {\n draw.text({ x: 10, y: 40 }, `Particle count: ${particleSystem.numParticles}`, \"white\");\n};\n\nconst particlePositionSystem = (state: WorldState) => {\n const { time } = state;\n const xPos = pulse(time.elapsed, 0.25, canvas.width / 2 - 100, canvas.width / 2 + 100);\n // const yPos = pulse(time.elapsed, 0.25, canvas.height / 2 - 100, canvas.height / 2 + 100);\n emitter.x = xPos;\n // emitter.y = yPos;\n};\n\naddSystem(clearCanvasSystem, fpsDrawSystem, particleCountSystem, particlePositionSystem, particleSystem);\n\ndefineMain(() => {\n step();\n});\n\nstart();\n\nconst container = document.createElement(\"div\");\ncontainer.style.position = \"fixed\";\ncontainer.style.bottom = \"0\";\ncontainer.style.left = \"0\";\ncontainer.style.padding = \"10px\";\ncontainer.style.display = \"flex\";\ncontainer.style.justifyContent = \"space-between\";\ncontainer.style.alignItems = \"center\";\ndocument.body.appendChild(container);\n\nconst pauseButton = document.createElement(\"button\");\npauseButton.innerText = \"Pause\";\npauseButton.onclick = () => {\n particleSystem.pause();\n};\ncontainer.appendChild(pauseButton);\n\nconst startButton = document.createElement(\"button\");\nstartButton.innerText = \"Start\";\nstartButton.style.marginLeft = \"5px\";\nstartButton.onclick = () => {\n particleSystem.start();\n};\ncontainer.appendChild(startButton);\n\nconst destroyButton = document.createElement(\"button\");\ndestroyButton.innerText = \"Destroy\";\ndestroyButton.style.marginLeft = \"5px\";\ndestroyButton.onclick = () => {\n particleSystem.destroy();\n};\ncontainer.appendChild(destroyButton);\n"], - "mappings": "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", + "sourcesContent": ["/**\n * Generates a sinusoidal pulse between a minimum and maximum value at a specified frequency.\n *\n * @param time - The time variable, typically representing elapsed time.\n * @param freq - The frequency of the pulse in cycles per unit time (default is 1).\n * @param min - The minimum value of the pulse (default is 0).\n * @param max - The maximum value of the pulse (default is 1).\n * @returns The calculated pulse value at the given time.\n */\nexport function pulse(time: number, freq: number = 1, min: number = 0, max: number = 1): number {\n const halfRange = (max - min) / 2;\n return min + halfRange * (1 + Math.sin(2 * Math.PI * freq * time));\n}\n\n/**\n * Performs a linear interpolation between two numbers.\n * @param a The start value.\n * @param b The end value.\n * @param t The interpolation factor (0-1).\n * @returns The interpolated value.\n */\nexport function lerp(a: number, b: number, t: number): number {\n return (1 - t) * a + t * b;\n}\n\n/**\n * Performs spherical linear interpolation between two numbers.\n * @param a The start value.\n * @param b The end value.\n * @param t The interpolation factor, between 0 and 1.\n * @returns The interpolated value.\n */\nexport function slerp(a: number, b: number, t: number): number {\n const theta = Math.acos(Math.min(Math.max(a / b, -1), 1)) * t;\n return a * Math.cos(theta) + b * Math.sin(theta);\n}\n\nexport function extend(component: () => T) {\n return (overrides: Partial): (() => T) => {\n const extendedCompponent = () => ({ ...component(), ...overrides });\n Object.defineProperty(extendedCompponent, \"name\", {\n value: component.name,\n });\n return extendedCompponent;\n };\n}\n", "/*\n This is a mashup of github.com/lukeed/hexoid and github.com/paralleldrive/cuid\n Both are MIT licensed.\n\n ~ https://github.com/paralleldrive/cuid/blob/f507d971a70da224d3eb447ed87ddbeb1b9fd097/LICENSE\n --\n MIT License\n Copyright (c) 2012 Eric Elliott\n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n ~ https://github.com/lukeed/hexoid/blob/1070447cdc62d1780d2a657b0df64348fc1e5ec5/license\n --\n MIT License\n Copyright (c) Luke Edwards (lukeed.com)\n Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*/\n\nconst HEX: string[] = [];\n\nfor (let i = 0; i < 256; i++) {\n HEX[i] = (i + 256).toString(16).substring(1);\n}\n\nfunction pad(str: string, size: number) {\n const s = \"000000\" + str;\n return s.substring(s.length - size);\n}\n\nconst SHARD_COUNT = 32;\n\nexport function getCreateId(opts: { init: number; len: number }) {\n const len = opts.len || 16;\n let str = \"\";\n let num = 0;\n const discreteValues = 1_679_616; // Math.pow(36, 4)\n let current = opts.init + Math.ceil(discreteValues / 2);\n\n function counter() {\n current = current <= discreteValues ? current : 0;\n current++;\n return (current - 1).toString(16);\n }\n\n return () => {\n if (!str || num === 256) {\n str = \"\";\n num = ((1 + len) / 2) | 0;\n while (num--) str += HEX[(256 * Math.random()) | 0];\n str = str.substring((num = 0), len);\n }\n\n const date = Date.now().toString(36);\n const paddedCounter = pad(counter(), 6);\n const hex = HEX[num++];\n\n const shardKey = parseInt(hex, 16) % SHARD_COUNT;\n\n return `ngn${date}${paddedCounter}${hex}${str}${shardKey}`;\n };\n}\n", "import { getCreateId } from \"./ids\";\n\nconst createId = getCreateId({ init: 0, len: 4 });\n\n/**\n * entity.id -> component.name -> index of component in entity.components\n *\n * This map stores indices of components in the entity component array.\n * The purpose of this map is to allow for fast lookups of components in the\n * entity.components array (e.g. entity.getComponent()).\n */\nexport const $eciMap = Symbol();\n\n/**\n * component.name -> array of entity.ids that have this component\n */\nexport const $ceMap = Symbol();\nexport const $eMap = Symbol();\nexport const $queryResults = Symbol();\nexport const $dirtyQueries = Symbol();\nexport const $queryDependencies = Symbol();\nexport const $systems = Symbol();\nexport const $running = Symbol();\nexport const $onEntityCreated = Symbol();\nexport const $mainLoop = Symbol();\n\nexport type Component = () => {};\nexport type ComponentInstance = () => {\n __ngn__?: {\n parent: string;\n name: string;\n };\n} & {\n [key: string]: any;\n};\n\nexport type QueryConfig = Readonly<\n Partial<{\n /** Matches entities as long as the entity has all of the components in the provided array. */\n and: Component[];\n /** Matches entities as long as the entity has at least one of the components in the provided array. */\n or: Component[];\n /** Matches entities as long as the entity has none of the components in the provided array. */\n not: Component[];\n /** Matches entities that have any of these tag strings. */\n tag: string[];\n }>\n>;\n\nexport type Entity = Readonly<{\n id: string;\n components: ReturnType[];\n addTag: (tag: string) => Entity;\n removeTag: () => Entity;\n getTag: () => string;\n addComponent: (component: Component, defaults?: object) => Entity;\n removeComponent: (component: Component) => Entity;\n getComponent: (arg: T) => ReturnType;\n hasComponent: (component: Component) => boolean;\n destroy: () => void;\n}>;\n\nexport type QueryResults = {\n results: {\n entity: Entity;\n [componentName: string]: any;\n }[];\n};\n\nexport type SystemFn = (w: WorldState) => void;\nexport type SystemCls = { update: (w: WorldState) => void };\nexport type System = SystemCls | SystemFn;\n\nexport type WorldState = {\n [$eciMap]: { [key: number]: { [componentName: string]: number } };\n [$ceMap]: { [key: string]: string[] };\n [$eMap]: { [key: number]: any };\n [$dirtyQueries]: Set;\n [$queryDependencies]: Map>;\n [$queryResults]: { [key: string]: QueryResults };\n [$systems]: ((w: WorldState) => void)[];\n [$mainLoop]: (w: WorldState) => void;\n time: {\n /** The total elapsed time in seconds since the game loop started. */\n elapsed: number;\n /** The time in milliseconds since the last frame. */\n delta: number;\n /** The time in milliseconds since the last time the main loop was called. */\n loopDelta: number;\n /** The time in milliseconds of the last call to the main loop. */\n lastLoopDelta: number;\n /** The time scale of the game loop. */\n scale: number;\n /** The current frames per second. */\n fps: number;\n };\n [$running]: boolean;\n [$onEntityCreated]: ((e: Entity) => void)[];\n};\n\nexport const createWorld = () => {\n const state: WorldState = {\n [$eciMap]: {},\n [$ceMap]: {},\n [$eMap]: {},\n [$dirtyQueries]: new Set(),\n [$queryDependencies]: new Map(),\n [$queryResults]: {},\n [$systems]: [],\n [$mainLoop]: null,\n time: {\n elapsed: 0,\n delta: 0,\n loopDelta: 0,\n lastLoopDelta: 0,\n scale: 1,\n fps: 0,\n },\n [$running]: false,\n [$onEntityCreated]: [],\n };\n\n const defineMain = (callback: (w?: WorldState) => void) => {\n state[$mainLoop] = callback;\n };\n\n /**\n * start - starts the game loop.\n * @returns - a function to stop the loop.\n */\n const start = () => {\n let then = 0;\n let accumulator = 0;\n const boundLoop = handler.bind(start);\n let loopHandler = -1;\n const { time } = state;\n time.delta = 0;\n time.elapsed = 0;\n time.fps = 0;\n state[$running] = true;\n\n let raf: ((cb: FrameRequestCallback) => number) | null = null;\n let craf: ((handle: number) => void) | null = null;\n\n /**\n * Fake requestAnimationFrame and cancelAnimationFrame\n * so that we can run tests for this in node.\n */\n if (typeof window !== \"undefined\") {\n let now = performance.now();\n raf = (cb: FrameRequestCallback): number => {\n return requestAnimationFrame((timestamp) => {\n now = timestamp;\n cb(now);\n });\n };\n craf = cancelAnimationFrame;\n } else {\n let now = 0;\n raf = (cb: FrameRequestCallback): number => {\n return setTimeout(() => {\n now += 16.67;\n cb(now);\n }, 16.67) as unknown as number;\n };\n\n craf = (id: number) => {\n clearTimeout(id);\n };\n }\n\n let xfps = 1;\n const xtimes = [];\n\n function handler(now: number) {\n if (!state[$running]) return craf(loopHandler);\n\n while (xtimes.length > 0 && xtimes[0] <= now - 1000) {\n xtimes.shift();\n }\n\n xtimes.push(now);\n xfps = xtimes.length;\n time.fps = xfps;\n\n time.delta = now - then;\n then = now;\n\n accumulator += time.delta * time.scale;\n\n // Calculate the threshold for stepping the world based on the current frame rate\n const stepThreshold = 1000 / (time.fps || 60);\n\n // Step the world only when the accumulated scaled time exceeds the threshold\n while (accumulator >= stepThreshold) {\n time.loopDelta = now - time.lastLoopDelta;\n time.lastLoopDelta = now;\n\n state[$mainLoop](state);\n accumulator -= stepThreshold;\n }\n\n time.elapsed += time.delta * 0.001;\n\n loopHandler = raf(boundLoop);\n }\n\n loopHandler = raf(boundLoop);\n\n return () => (state[$running] = false);\n };\n\n const stop = () => {\n state[$running] = false;\n };\n\n function step() {\n for (const system of state[$systems]) {\n system(state);\n }\n }\n\n /**\n * Adds one or more systems to the ECS world.\n * A system can be either a @see SystemFn or a @see SystemCls.\n * @param systems An array of system classes or functions.\n * @throws {Error} If a system is not a valid system class or function.\n */\n function addSystem(...systems: (SystemCls | SystemFn)[]) {\n for (const system of systems) {\n // If the system is a function, add it to the world systems array\n if (typeof system === \"function\") {\n state[$systems].push(system);\n // If the system has an `update` method, add that method to the world systems array\n } else if (system.update && typeof system.update === \"function\") {\n state[$systems].push(system.update);\n // If the system is not a valid system class or function, throw an error\n } else {\n throw new Error(`Not a valid system: ${JSON.stringify(system)}`);\n }\n }\n }\n\n /**\n * Removes one or more systems from the world.\n *\n * @param {...(SystemCls | SystemFn)[]} systems - The system or systems to remove.\n * @throws {TypeError} Throws an error if the system parameter is not a function or an object with an update function.\n * @returns {void}\n */\n function removeSystem(...systems: (SystemCls | SystemFn)[]): void {\n for (const system of systems) {\n if (typeof system === \"function\") {\n state[$systems] = state[$systems].filter((s) => s !== system);\n } else if (system.update && typeof system.update === \"function\") {\n state[$systems] = state[$systems].filter((s) => s !== system.update);\n } else {\n throw new TypeError(\"Parameter must be a function or an object with an update function.\");\n }\n }\n }\n\n /**\n * Retrieves query results based on the given configuration and query name.\n * If non-dirty query results exist for this queryName, returns them. Otherwise, filters entities based on the queryConfig\n * and updates the state with the new query results before returning them.\n *\n * @param {QueryConfig} queryConfig - The configuration object containing 'and', 'or', 'not' and 'tag' arrays of component names.\n * @param {string} queryName - The name of the query to retrieve or update results for.\n * @returns {any[]} An array of result objects, each containing an entity and its components as properties.\n */\n const getQuery = (queryConfig: QueryConfig, queryName: string) => {\n // If we have non-dirty query results for this queryName, return them\n if (!state[$dirtyQueries].has(queryName) && state[$queryResults][queryName]) {\n return state[$queryResults][queryName].results;\n }\n\n const { and = [], or = [], not = [], tag = [] } = queryConfig;\n const entities: Entity[] = Object.values(state[$eMap]).filter((entity) => {\n return (\n (!not.length || !not.some((component) => entity.hasComponent(component))) &&\n (!and.length || and.every((component) => entity.hasComponent(component))) &&\n (!or.length || or.some((component) => entity.hasComponent(component))) &&\n (!tag.length || tag.some((t) => entity.tag === t))\n );\n });\n\n state[$queryResults][queryName] = {\n results: entities.map((entity) => {\n const result: any = { entity };\n\n entity.components.forEach((component) => {\n result[component.__ngn__.name] = component;\n });\n\n return result;\n }),\n };\n\n state[$dirtyQueries].delete(queryName);\n\n return state[$queryResults][queryName].results;\n };\n\n const markQueryDirty = (queryName: string) => {\n state[$dirtyQueries].add(queryName);\n };\n\n /**\n * Defines a query for filtering entities based on a combination of criteria.\n * @param queryConfig The configuration for the query. Contains and, or, not and tag criteria.\n * @throws {Error} Invalid query if any criteria in the query config does not have a 'name' property.\n * @returns A function that takes a query implementation and returns the results of the query.\n */\n const query = ({ and = [], or = [], not = [], tag = [] }: QueryConfig) => {\n // Checks if a criteria object has a 'name' property\n const validQuery = (c: Component) => Object.prototype.hasOwnProperty.call(c, \"name\");\n\n // Throws an error if any criteria object in the query config does not have a 'name' property\n if (![...and, ...or, ...not].every(validQuery)) throw new Error(\"Invalid query\");\n\n // Constructs a string representing the query name based on the criteria in the query config\n const queryName = [\"and\", ...and.map((c) => c.name), \"or\", ...or.map((c) => c.name), \"not\", ...not.map((c) => c.name), \"tag\", ...tag].join(\"\");\n\n // Component dependencies\n [...and, ...or, ...not].forEach((c) => {\n const dependencies = state[$queryDependencies].get(c.name) || new Set();\n dependencies.add(queryName);\n state[$queryDependencies].set(c.name, dependencies);\n });\n\n // Tag dependencies\n tag.forEach((t) => {\n const tagKey = `tag:${t}`;\n const dependencies = state[$queryDependencies].get(tagKey) || new Set();\n dependencies.add(queryName);\n state[$queryDependencies].set(tagKey, dependencies);\n });\n\n return (queryImpl: (results: { entity: Entity }[]) => void) => queryImpl(getQuery({ and, or, not, tag }, queryName));\n };\n\n function destroyEntity(e: Entity) {\n const exists = state[$eMap][e.id];\n\n if (!exists) return false;\n\n const componentsToRemove: string[] = Object.keys(state[$eciMap][e.id]);\n\n componentsToRemove.forEach((componentName) => {\n state[$ceMap][componentName] = state[$ceMap][componentName].filter((id) => id !== e.id);\n });\n\n delete state[$eciMap][e.id];\n delete state[$eMap][e.id];\n\n componentsToRemove.forEach((componentName) => {\n const affectedQueries = state[$queryDependencies].get(componentName);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n });\n\n return true;\n }\n\n function onEntityCreated(fn: any) {\n if (typeof fn !== \"function\") return;\n\n state[$onEntityCreated].push(fn);\n\n return () => {\n state[$onEntityCreated] = state[$onEntityCreated].filter((f) => f !== fn);\n };\n }\n\n /**\n * Creates a new component for the given entity and adds it to the world.\n * @param entity The entity to add the component to.\n * @param component The component function to add.\n * @param defaults (optional) Default values to apply to the component.\n * @returns The modified entity with the new component added.\n */\n function createComponent(entity: Entity, component: Function, defaults: object = {}): Entity {\n // If the entity already has this component, return the unmodified entity.\n if (state[$eciMap]?.[entity.id]?.[component.name] !== undefined) return entity;\n\n const affectedQueries = state[$queryDependencies].get(component.name);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n\n const componentInstance = component();\n\n if (componentInstance.onAttach && typeof componentInstance.onAttach === \"function\") {\n componentInstance.onAttach(entity);\n }\n\n // Create the component, assigning defaults and a reference to the parent entity.\n entity.components.push(\n Object.assign(\n {},\n {\n ...componentInstance,\n ...defaults,\n __ngn__: {\n parent: entity.id,\n name: component.name,\n },\n },\n ) as ComponentInstance,\n );\n\n // Add the component index to the entity's index map.\n state[$eciMap][entity.id] = state[$eciMap][entity.id] || {};\n state[$eciMap][entity.id][component.name] = entity.components.length - 1;\n\n // Add the entity to the component's entity map.\n state[$ceMap][component.name] = state[$ceMap][component.name] || [];\n state[$ceMap][component.name].push(entity.id);\n\n return entity;\n }\n\n /**\n * Creates an entity with the given specification object.\n * @param {object} spec - Optional data to be stored on the entity.\n * @returns {any} - Returns the created entity.\n */\n function createEntity(spec: T & { id?: string } = {} as T): T & Entity {\n const id = spec.id ?? createId();\n const components: any[] = [];\n\n const tagKey = (t: string) => `tag:${t}`;\n\n function updateTagQueries(tagKey: string) {\n const affectedQueries = state[$queryDependencies].get(tagKey);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n }\n\n function addTag(t: string): Entity {\n const previousTagKey = tagKey(this.tag);\n\n this.tag = t;\n\n updateTagQueries(tagKey(t));\n updateTagQueries(previousTagKey);\n\n return this;\n }\n\n function removeTag(): Entity {\n const previousTagKey = tagKey(this.tag);\n this.tag = \"\";\n\n updateTagQueries(previousTagKey);\n\n return this;\n }\n\n function getTag() {\n return this.tag;\n }\n\n function addComponent(c: Component, defaults = {}) {\n return createComponent(this, c, defaults);\n }\n\n function hasComponent(component: Component) {\n return state[$eciMap]?.[id]?.[component.name] !== undefined;\n }\n\n function getComponent(arg: T): ReturnType {\n const index = state[$eciMap][id][arg.name];\n return components[index];\n }\n\n /**\n * Removes the specified component from the entity and updates the world state accordingly.\n *\n * @param component The component to remove from the entity.\n * @returns The modified entity.\n */\n function removeComponent(component: Component | string): Entity {\n const name = typeof component === \"string\" ? component : component.name;\n\n const componentInstance = getComponent(typeof component === \"string\" ? ({ name } as any) : component);\n\n if (componentInstance && componentInstance.onDetach && typeof componentInstance.onDetach === \"function\") {\n componentInstance.onDetach(this);\n }\n\n const affectedQueries = state[$queryDependencies].get(name);\n\n if (affectedQueries) {\n affectedQueries.forEach(markQueryDirty);\n }\n\n // Set the entity's component index for the specified component to undefined.\n state[$eciMap][id][name] = undefined;\n\n // Remove the entity's ID from the component's entity list.\n state[$ceMap][name] = state[$ceMap][name].filter((e) => e !== id);\n\n // Remove the component from the entity's component list.\n const index = state[$eciMap][id][name];\n components.splice(index, 1);\n\n // Update the entity's component indices for all components after the removed component.\n Object.keys(state[$eciMap][id]).forEach((componentName) => {\n if (state[$eciMap][id][componentName] > components.findIndex((c) => c.name === componentName)) {\n state[$eciMap][id][componentName]--;\n }\n });\n\n // Return the modified entity.\n return this;\n }\n\n function destroy() {\n return destroyEntity(this);\n }\n\n const entity = Object.assign({}, spec, {\n id,\n components,\n addTag,\n removeTag,\n getTag,\n addComponent,\n hasComponent,\n getComponent,\n removeComponent,\n destroy,\n });\n\n // If we are focing a specific entity id, we need to migrate any\n // entity that might already occupy this space.\n if (spec.id !== undefined && state[$eMap][spec.id]) {\n migrateEntityId(spec.id, createId());\n }\n\n state[$eMap][id] = entity;\n state[$eciMap][id] = {};\n\n state[$onEntityCreated].forEach((fn) => {\n fn(entity);\n });\n\n return entity as unknown as T & Entity;\n }\n\n /**\n * migrateEntityId updates the id of an entity in the world, and all\n * associated world maps.\n * @param oldId The id of the entity to migrate.\n * @param newId The id to migrate the entity to.\n */\n function migrateEntityId(oldId: string, newId: string) {\n const entity = state[$eMap][oldId];\n\n if (!entity) return;\n\n entity.id = newId;\n\n state[$eMap][newId] = entity;\n delete state[$eMap][oldId];\n\n state[$eciMap][newId] = state[$eciMap][oldId];\n delete state[$eciMap][oldId];\n }\n\n function getEntity(id: string): Entity {\n return state[$eMap][id];\n }\n\n return {\n state,\n query,\n createEntity,\n getEntity,\n onEntityCreated,\n addSystem,\n removeSystem,\n start,\n stop,\n step,\n defineMain,\n };\n};\n", "export type CreateCanvasOptions = Partial<{\n width: number;\n height: number;\n fullscreen: boolean;\n target: HTMLElement;\n}>;\n\nexport const createCanvas = (options: CreateCanvasOptions) => {\n const canvas = document.createElement(\"canvas\");\n const { target, fullscreen } = options;\n const { body } = window.document;\n\n if (target && fullscreen) {\n options.target = null;\n } else if (!target && !fullscreen) {\n options.fullscreen = true;\n }\n\n if (fullscreen) {\n Object.assign(canvas.style, {\n position: \"absolute\",\n top: \"0\",\n left: \"0\",\n });\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n body.appendChild(canvas);\n Object.assign(body.style, {\n margin: \"0\",\n padding: \"0\",\n width: \"100%\",\n height: \"100%\",\n overflow: \"hidden\",\n });\n }\n\n if (target) {\n target.appendChild(canvas);\n target.style.overflow = \"hidden\";\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n }\n\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n\n const existingMeta = window.document.querySelector(`meta[name=\"viewport\"]`);\n if (existingMeta) {\n Object.assign(existingMeta, {\n content: \"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\",\n });\n } else {\n const meta = Object.assign(window.document.createElement(\"meta\"), {\n name: \"viewport\",\n content: \"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0\",\n });\n window.document.head.appendChild(meta);\n }\n\n return canvas;\n};\n", "export type Vector2 = {\n x: number;\n y: number;\n};\n\nconst fastRound = (num: number): number => ~~(0.5 + num);\n\nconst fastRoundVector2 = (v: Vector2): Vector2 => ({\n x: fastRound(v.x),\n y: fastRound(v.y),\n});\n\nexport const createDraw = (context: CanvasRenderingContext2D) => {\n const text = (v: Vector2, text: string, color: string = \"black\", size: number = 16) => {\n v = fastRoundVector2(v);\n context.save();\n context.fillStyle = color;\n context.font = `${size}px sans-serif`;\n context.fillText(text, v.x, v.y);\n context.restore();\n };\n\n const line = (from: Vector2, to: Vector2, color: string = \"black\", lineWidth: number = 1) => {\n from = fastRoundVector2(from);\n to = fastRoundVector2(to);\n context.save();\n context.beginPath();\n context.moveTo(from.x, from.y);\n context.lineTo(to.x, to.y);\n context.strokeStyle = color; // Use the color parameter\n context.lineWidth = lineWidth;\n context.stroke();\n context.closePath();\n context.restore();\n };\n\n const rectangle = (pos: Vector2, dimensions: Vector2, color: string = \"black\", lineWidth: number = 1) => {\n pos = fastRoundVector2(pos);\n dimensions = fastRoundVector2(dimensions);\n context.save();\n context.beginPath();\n context.rect(pos.x, pos.y, dimensions.x, dimensions.y);\n context.lineWidth = lineWidth;\n context.strokeStyle = color;\n context.stroke();\n context.closePath();\n context.restore();\n };\n\n const circle = (pos: Vector2, radius: number = 25, color: string = \"black\", lineWidth: number = 1) => {\n pos = fastRoundVector2(pos);\n context.save();\n context.beginPath();\n context.strokeStyle = color;\n context.lineWidth = lineWidth;\n context.arc(pos.x, pos.y, radius, 0, Math.PI * 2, true);\n context.stroke();\n context.closePath();\n context.restore();\n };\n\n return {\n text,\n line,\n rectangle,\n circle,\n };\n};\n", "import { createCanvas, CreateCanvasOptions } from \"./canvas\";\nimport { createDraw } from \"./draw\";\n\ntype Create2DOptions = Partial<{\n canvas: CreateCanvasOptions;\n}>;\n\nexport const create2D = (options: Create2DOptions) => {\n const { width = 800, height = 600, fullscreen = false, target = null } = options.canvas;\n const canvas = createCanvas({ width, height, fullscreen, target });\n const context = canvas.getContext(\"2d\");\n const draw = createDraw(context);\n\n const onWindowResize = () => {\n if (fullscreen) {\n canvas.style.width = \"100%\";\n canvas.style.height = \"100%\";\n canvas.width = window.innerWidth;\n canvas.height = window.innerHeight;\n return;\n }\n\n canvas.width = canvas.offsetWidth;\n canvas.height = canvas.offsetHeight;\n };\n\n window.addEventListener(\"resize\", onWindowResize);\n\n const destroy = () => {\n window.removeEventListener(\"resize\", onWindowResize);\n canvas.parentElement.removeChild(canvas);\n };\n\n return { canvas, context, draw, destroy };\n};\n", "import { WorldState } from \"../../ngn\";\n\nexport type Particle = {\n x: number;\n y: number;\n size: number;\n color: string;\n colorStart: string;\n colorEnd: string;\n lifetime: number;\n speedX: number;\n speedY: number;\n scaleX: number;\n scaleY: number;\n onInit?: (particle: Particle, state: WorldState) => void;\n onRemove?: (particle: Particle, state: WorldState) => void;\n onUpdate?: (particle: Particle, state: WorldState) => void;\n};\n\nexport enum ColorEasing {\n LINEAR = \"linear\",\n EASE_IN = \"easeIn\",\n EASE_OUT = \"easeOut\",\n EASE_IN_OUT = \"easeInOut\",\n}\n\nexport type FadeEasing = ColorEasing;\n\ntype BlendMode =\n | \"color\"\n | \"color-burn\"\n | \"color-dodge\"\n | \"copy\"\n | \"darken\"\n | \"destination-atop\"\n | \"destination-in\"\n | \"destination-out\"\n | \"destination-over\"\n | \"difference\"\n | \"exclusion\"\n | \"hard-light\"\n | \"hue\"\n | \"lighten\"\n | \"lighter\"\n | \"luminosity\"\n | \"multiply\"\n | \"overlay\"\n | \"saturation\"\n | \"screen\"\n | \"soft-light\"\n | \"source-atop\"\n | \"source-in\"\n | \"source-out\"\n | \"source-over\"\n | \"xor\";\n\nexport type ParticleEmitterOptions = {\n x?: number; // X position\n y?: number; // Y position\n maxParticles?: number; // Max number of particles\n rate?: number; // Particles per second\n lifetime?: number; // Lifetime of each particle\n lifetimeVariation?: number; // Variation in lifetime\n size?: number; // Size of each particle\n sizeVariation?: number; // Variation in size\n colorStart?: string | string[]; // Start color\n colorEnd?: string | string[]; // End color\n colorEasing?: ColorEasing; // Easing function for color\n fadeOutEasing?: FadeEasing;\n speed?: number; // Speed of each particle\n speedVariation?: number; // Variation in speed\n angle?: number; // Angle of emission\n spread?: number; // Spread of emission\n gravity?: { x: number; y: number }; // Gravity affecting the particles\n blendMode?: BlendMode; // Blend mode\n canvas: HTMLCanvasElement; // Canvas to draw on\n burst?: boolean; // If true, emit all particles at once and then stop\n /** Per-particle initialization callback. */\n onInit?: (particle: Particle, state: WorldState) => void; // Callback for particle initialization\n /** Per-particle update callback. */\n onUpdate?: (particle: Particle, state: WorldState) => void; // Callback for particle update\n /** Per-particle removal callback. */\n onRemove?: (particle: Particle, state: WorldState) => void; // Callback for particle removal\n};\n\nconst getDefaultParticleEmitterOptions = (opts: Partial): ParticleEmitterOptions => ({\n ...opts,\n x: opts.x ?? 0,\n y: opts.y ?? 0,\n maxParticles: opts.maxParticles ?? 100,\n rate: opts.rate ?? 1,\n lifetime: opts.lifetime ?? 1000,\n lifetimeVariation: opts.lifetimeVariation ?? 0,\n size: opts.size ?? 5,\n sizeVariation: opts.sizeVariation ?? 0,\n colorStart: opts.colorStart ?? \"#000000\",\n colorEnd: opts.colorEnd ?? \"#000000\",\n colorEasing: opts.colorEasing ?? ColorEasing.LINEAR,\n angle: opts.angle ?? 0,\n spread: opts.spread ?? 0,\n gravity: opts.gravity ?? { x: 0, y: 0 },\n speed: opts.speed ?? 0.1,\n speedVariation: opts.speedVariation ?? 0,\n canvas: opts.canvas,\n burst: opts.burst ?? false,\n});\n\nexport type ParticleEmitter = {\n particles: Particle[];\n update: (state: WorldState) => void;\n destroy: () => void;\n pause: () => void;\n resume: () => void;\n x: number;\n y: number;\n};\n\nexport type ParticleSystemOptions = {\n x: number;\n y: number;\n canvas: HTMLCanvasElement;\n // A property to determine whether or not it starts immediately\n start?: boolean;\n};\n\nconst interpolateColor = (colorStart: string, colorEnd: string, factor: number, easing: ColorEasing): string => {\n switch (easing) {\n case ColorEasing.EASE_IN:\n factor = Math.pow(factor, 2);\n break;\n case ColorEasing.EASE_OUT:\n factor = 1 - Math.pow(1 - factor, 2);\n break;\n case ColorEasing.EASE_IN_OUT:\n if (factor < 0.5) {\n factor = 2 * Math.pow(factor, 2);\n } else {\n factor = 1 - 2 * Math.pow(1 - factor, 2);\n }\n break;\n case ColorEasing.LINEAR:\n default:\n // No adjustment needed for linear\n break;\n }\n\n // Assuming colorStart and colorEnd are in the format \"#RRGGBB\"\n const color1 = parseInt(colorStart.slice(1), 16);\n const color2 = parseInt(colorEnd.slice(1), 16);\n\n const r1 = (color1 >> 16) & 0xff;\n const g1 = (color1 >> 8) & 0xff;\n const b1 = color1 & 0xff;\n\n const r2 = (color2 >> 16) & 0xff;\n const g2 = (color2 >> 8) & 0xff;\n const b2 = color2 & 0xff;\n\n const r = Math.round(r1 + factor * (r2 - r1));\n const g = Math.round(g1 + factor * (g2 - g1));\n const b = Math.round(b1 + factor * (b2 - b1));\n\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;\n};\n\nconst hexToRgb = (hex: string): string => {\n const color = parseInt(hex.slice(1), 16);\n const r = (color >> 16) & 0xff;\n const g = (color >> 8) & 0xff;\n const b = color & 0xff;\n return `${r}, ${g}, ${b}`;\n};\n\nexport const createParticleEmitter = (opts: ParticleEmitterOptions): ParticleEmitter => {\n opts = getDefaultParticleEmitterOptions(opts);\n const particles = [];\n let timeSinceLastEmission = 0;\n const emissionInterval = 1 / opts.rate;\n const lifetimeVariation = opts.lifetimeVariation ?? 0;\n const context = opts.canvas.getContext(\"2d\");\n const angleInRadians = opts.angle * (Math.PI / 180);\n let dead = false;\n let paused = false;\n\n const update = (state: WorldState) => {\n if (dead) return;\n\n context.globalCompositeOperation = opts.blendMode ?? \"source-over\";\n\n const { loopDelta } = state.time;\n\n for (let i = particles.length - 1; i >= 0; i--) {\n const particle = particles[i];\n const lifeFactor = particle.lifetime / opts.lifetime;\n let opacity = 1;\n\n particle.color = interpolateColor(particle.colorStart, particle.colorEnd, 1 - lifeFactor, opts.colorEasing);\n\n if (opts.fadeOutEasing) {\n switch (opts.fadeOutEasing) {\n case ColorEasing.EASE_IN:\n opacity = Math.pow(lifeFactor, 2);\n break;\n case ColorEasing.EASE_OUT:\n opacity = 1 - Math.pow(1 - lifeFactor, 2);\n break;\n case ColorEasing.EASE_IN_OUT:\n if (lifeFactor < 0.5) {\n opacity = 2 * Math.pow(lifeFactor, 2);\n } else {\n opacity = 1 - 2 * Math.pow(1 - lifeFactor, 2);\n }\n break;\n case ColorEasing.LINEAR:\n default:\n opacity = lifeFactor;\n break;\n }\n }\n\n if (!paused) {\n particle.x += particle.speedX * loopDelta;\n particle.y += particle.speedY * loopDelta;\n\n particle.speedX += (opts.gravity.x * loopDelta) / 1000;\n particle.speedY += (opts.gravity.y * loopDelta) / 1000;\n\n particle.lifetime -= state.time.loopDelta;\n\n if (opts.onUpdate) {\n opts.onUpdate(particle, state);\n }\n\n if (particle.onUpdate) {\n particle.onUpdate(particle, state);\n }\n }\n\n if (particle.lifetime <= 0) {\n if (opts.onRemove) {\n opts.onRemove(particle, state);\n }\n\n if (particle.onRemove) {\n particle.onRemove(particle, state);\n }\n\n particles.splice(i, 1);\n } else {\n context.fillStyle = `rgba(${hexToRgb(particle.color)}, ${opacity})`;\n context.beginPath();\n // context.arc(particle.x * particle.scaleX, particle.y * particle.scaleY, particle.size, 0, Math.PI * 2);\n // draw a rectangel instead:\n context.rect(particle.x * particle.scaleX, particle.y * particle.scaleY, particle.size, particle.size);\n context.closePath();\n context.fill();\n }\n }\n\n const emitParticle = () => {\n const lifetimeVariationAmount = lifetimeVariation ? opts.lifetime * lifetimeVariation * Math.random() : 0;\n const particleLifetime = opts.lifetime + lifetimeVariationAmount * (Math.random() < 0.5 ? -1 : 1);\n const colorStart = Array.isArray(opts.colorStart) ? opts.colorStart[Math.floor(Math.random() * opts.colorStart.length)] : opts.colorStart;\n const colorEnd = Array.isArray(opts.colorEnd) ? opts.colorEnd[Math.floor(Math.random() * opts.colorEnd.length)] : opts.colorEnd;\n const particle = spawnParticle({\n x: opts.x,\n y: opts.y,\n colorStart: colorStart,\n colorEnd: colorEnd,\n color: colorStart,\n lifetime: particleLifetime,\n size: Math.max(0, opts.size + (Math.random() - 0.5) * opts.sizeVariation),\n speedX: opts.speed * (Math.sin(angleInRadians) + (Math.random() - 0.5) * opts.spread),\n speedY: -opts.speed * (Math.cos(angleInRadians) + (Math.random() - 0.5) * opts.spread),\n scaleX: 1,\n scaleY: 1,\n });\n\n if (opts.onInit) {\n opts.onInit(particle, state);\n }\n\n if (particle.onInit) {\n particle.onInit(particle, state);\n }\n };\n\n if (!paused) {\n if (opts.burst && timeSinceLastEmission === 0) {\n for (let i = 0; i < opts.maxParticles; i++) {\n emitParticle();\n }\n\n timeSinceLastEmission = -1;\n } else if (!opts.burst) {\n timeSinceLastEmission += loopDelta;\n\n while (timeSinceLastEmission >= emissionInterval && particles.length < opts.maxParticles) {\n emitParticle();\n timeSinceLastEmission -= emissionInterval;\n }\n }\n }\n\n if (opts.burst && particles.length === 0) {\n destroy();\n }\n\n context.globalCompositeOperation = \"source-over\";\n };\n\n const destroy = () => {\n dead = true;\n particles.length = 0;\n return;\n };\n\n const spawnParticle = (p: Particle): Particle => {\n particles.push(p);\n return p;\n };\n\n const pause = () => {\n paused = true;\n };\n\n const resume = () => {\n paused = false;\n };\n\n document.addEventListener(\"visibilitychange\", () => {\n if (document.visibilityState === \"visible\") {\n resume();\n } else {\n pause();\n }\n });\n\n return {\n particles,\n update,\n destroy,\n pause,\n resume,\n set x(value: number) {\n opts.x = value;\n },\n get x() {\n return opts.x;\n },\n set y(value: number) {\n opts.y = value;\n },\n get y() {\n return opts.y;\n },\n };\n};\n\nexport const createParticleSystem = (opts: ParticleSystemOptions) => {\n let _x = opts.x;\n let _y = opts.y;\n const emitters: ParticleEmitter[] = [];\n const startImmediately = opts.start ?? true;\n\n const update = (state: WorldState) => {\n emitters.forEach((emitter) => {\n emitter.update(state);\n });\n };\n\n const destroy = () => {\n emitters.forEach((emitter) => {\n emitter.destroy();\n });\n };\n\n const createEmitter = (opts: ParticleEmitterOptions): ParticleEmitter => {\n const emitter = createParticleEmitter({\n ...opts,\n /* x: _x, */\n /* y: _y, */\n });\n emitters.push(emitter);\n\n if (!startImmediately) {\n emitter.pause();\n }\n\n return emitter;\n };\n\n const pause = () => {\n emitters.forEach((emitter) => {\n emitter.pause();\n });\n };\n\n const start = () => {\n emitters.forEach((emitter) => {\n emitter.resume();\n });\n };\n\n return {\n update,\n destroy,\n createEmitter,\n pause,\n start,\n set x(value: number) {\n _x = value;\n },\n get x() {\n return _x;\n },\n set y(value: number) {\n _y = value;\n },\n get y() {\n return _y;\n },\n get numParticles() {\n return emitters.reduce((acc, emitter) => acc + emitter.particles.length, 0);\n },\n };\n};\n", "import { pulse } from \"./misc\";\nimport { createWorld, type WorldState } from \"./ngn\";\nimport { create2D } from \"./packages/2d\";\nimport { ColorEasing, createParticleSystem, Particle } from \"./packages/emitter\";\n\nconst { canvas, draw } = create2D({ canvas: { fullscreen: true } });\n\nconst particleSystem = createParticleSystem({\n x: canvas.width / 2,\n y: canvas.height / 2,\n canvas,\n});\n\nconst emitter = particleSystem.createEmitter({\n x: canvas.width / 2,\n y: canvas.height / 2,\n maxParticles: 100,\n rate: 0.1,\n lifetime: 1000,\n lifetimeVariation: 0.2,\n size: 20,\n sizeVariation: 10,\n colorStart: [\"#FF0000\", \"#ff5100\"],\n colorEnd: \"#222222\",\n colorEasing: ColorEasing.EASE_IN,\n fadeOutEasing: ColorEasing.EASE_OUT,\n speed: 0.1,\n speedVariation: 1,\n angle: 0,\n spread: 0.75,\n gravity: { x: 0, y: 0 },\n canvas,\n burst: false,\n\n onInit: (particle: Particle, state: WorldState) => {\n particle.x += Math.random() < 0.5 ? -6 : 6;\n particle.y += Math.random() < 0.5 ? -6 : 6;\n\n if (!(Math.random() < 0.02)) {\n return;\n }\n\n particle.size = 15;\n particle.speedY = -0.3;\n particle.lifetime = 1000;\n particle.colorEnd = \"#ff0000\";\n\n particle.onRemove = () => {\n particleSystem.createEmitter({\n x: particle.x,\n y: particle.y,\n maxParticles: 3,\n lifetimeVariation: 0.2,\n size: 3,\n sizeVariation: 2,\n colorStart: [\"#FF0000\", \"#ff5100\"],\n colorEnd: \"#222222\",\n colorEasing: ColorEasing.EASE_IN,\n fadeOutEasing: ColorEasing.EASE_OUT,\n speed: 0.02,\n speedVariation: 1,\n spread: 6,\n angle: 180,\n canvas,\n burst: true,\n });\n };\n\n particle.onUpdate = () => {\n particle.size = Math.max(0, particle.size + 0.25);\n };\n },\n onUpdate: (particle: Particle, state: WorldState) => {\n particle.size = Math.max(0, particle.size - 0.35);\n const v = pulse(state.time.elapsed, 0.25, -1, 1);\n particle.x += v * 1;\n },\n onRemove: (particle: Particle, state: WorldState) => {},\n});\n\nconst { addSystem, start, step, defineMain } = createWorld();\n\nconst clearCanvasSystem = () => {\n const context = canvas.getContext(\"2d\");\n context.clearRect(0, 0, canvas.width, canvas.height);\n\n context.fillStyle = \"#111\";\n context.fillRect(0, 0, canvas.width, canvas.height);\n};\n\nconst fpsDrawSystem = (state: WorldState) => {\n draw.text({ x: 10, y: 20 }, `FPS: ${state.time.fps.toFixed(2)}`, \"white\");\n};\n\nconst particleCountSystem = (state: WorldState) => {\n draw.text({ x: 10, y: 40 }, `Particle count: ${particleSystem.numParticles}`, \"white\");\n};\n\nconst particlePositionSystem = (state: WorldState) => {\n const { time } = state;\n const xPos = pulse(time.elapsed, 0.25, canvas.width / 2 - 100, canvas.width / 2 + 100);\n // const yPos = pulse(time.elapsed, 0.25, canvas.height / 2 - 100, canvas.height / 2 + 100);\n emitter.x = xPos;\n // emitter.y = yPos;\n};\n\naddSystem(clearCanvasSystem, fpsDrawSystem, particleCountSystem, particlePositionSystem, particleSystem);\n\ndefineMain(() => {\n step();\n});\n\nstart();\n\nconst container = document.createElement(\"div\");\ncontainer.style.position = \"fixed\";\ncontainer.style.bottom = \"0\";\ncontainer.style.left = \"0\";\ncontainer.style.padding = \"10px\";\ncontainer.style.display = \"flex\";\ncontainer.style.justifyContent = \"space-between\";\ncontainer.style.alignItems = \"center\";\ndocument.body.appendChild(container);\n\nconst pauseButton = document.createElement(\"button\");\npauseButton.innerText = \"Pause\";\npauseButton.onclick = () => {\n particleSystem.pause();\n};\ncontainer.appendChild(pauseButton);\n\nconst startButton = document.createElement(\"button\");\nstartButton.innerText = \"Start\";\nstartButton.style.marginLeft = \"5px\";\nstartButton.onclick = () => {\n particleSystem.start();\n};\ncontainer.appendChild(startButton);\n\nconst destroyButton = document.createElement(\"button\");\ndestroyButton.innerText = \"Destroy\";\ndestroyButton.style.marginLeft = \"5px\";\ndestroyButton.onclick = () => {\n particleSystem.destroy();\n};\ncontainer.appendChild(destroyButton);\n"], + "mappings": "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", "names": ["defineMain", "start", "step", "addSystem", "tagKey", "canvas", "text", "canvas", "draw", "emitter", "opts", "start"] } diff --git a/packages/ngn/src/index.ts b/packages/ngn/src/index.ts index 65bc572..1a3f99e 100644 --- a/packages/ngn/src/index.ts +++ b/packages/ngn/src/index.ts @@ -1,5 +1,6 @@ export { Component, createWorld, Entity, QueryConfig, type ComponentInstance, type WorldState } from "./ngn"; export { create2D, createCanvas, CreateCanvasOptions, createDraw, type Vector2 } from "./packages/2d"; +export * from "./packages/input"; export { onGamepadConnected, onGamepadDisconnected } from "./packages/input/devices/gamepad"; export { GamepadMapping, PlayStation4, PlayStation5, SCUFVantage2, Xbox } from "./packages/input/devices/mappings/gamepad"; export { KeyboardKey, KeyboardMapping, StandardKeyboard } from "./packages/input/devices/mappings/keyboard"; diff --git a/packages/ngn/src/ngn.ts b/packages/ngn/src/ngn.ts index 0c081d6..8818515 100644 --- a/packages/ngn/src/ngn.ts +++ b/packages/ngn/src/ngn.ts @@ -146,28 +146,28 @@ export const createWorld = () => { * Fake requestAnimationFrame and cancelAnimationFrame * so that we can run tests for this in node. */ - if (typeof window !== "undefined") { - let now = performance.now(); - raf = (cb: FrameRequestCallback): number => { - return requestAnimationFrame((timestamp) => { - now = timestamp; - cb(now); - }); - }; - craf = cancelAnimationFrame; - } else { - let now = 0; - raf = (cb: FrameRequestCallback): number => { - return setTimeout(() => { - now += 16.67; - cb(now); - }, 16.67) as unknown as number; - }; + if (typeof window !== "undefined") { + let now = performance.now(); + raf = (cb: FrameRequestCallback): number => { + return requestAnimationFrame((timestamp) => { + now = timestamp; + cb(now); + }); + }; + craf = cancelAnimationFrame; + } else { + let now = 0; + raf = (cb: FrameRequestCallback): number => { + return setTimeout(() => { + now += 16.67; + cb(now); + }, 16.67) as unknown as number; + }; - craf = (id: number) => { - clearTimeout(id); - }; - } + craf = (id: number) => { + clearTimeout(id); + }; + } let xfps = 1; const xtimes = []; @@ -216,7 +216,9 @@ export const createWorld = () => { function step() { for (const system of state[$systems]) { - system(state); + if (system(state) === null) { + break; + } } } diff --git a/packages/ngn/src/packages/input/index.ts b/packages/ngn/src/packages/input/index.ts index c975368..ad90ff4 100644 --- a/packages/ngn/src/packages/input/index.ts +++ b/packages/ngn/src/packages/input/index.ts @@ -16,14 +16,6 @@ export interface GamepadButtonState extends ButtonState { value: number; } -let $mousemove = null; -let $mousedown = null; -let $mouseup = null; -let $mousewheel = null; -let $keydown = null; -let $keyup = null; -let $gamepadconnected = null; -let $gamepaddisconnected = null; let boundEvents = false; const setDefaultStates = () => { @@ -53,14 +45,14 @@ export const destroyInput = () => { }; const bindEvents = () => { - $mousemove = window.addEventListener("mousemove", onMouseMove); - $mousedown = window.addEventListener("mousedown", onMouseDown); - $mouseup = window.addEventListener("mouseup", onMouseUp); - $mousewheel = window.addEventListener("mousewheel", onMouseWheel); - $keydown = window.addEventListener("keydown", onKeyDown); - $keyup = window.addEventListener("keyup", onKeyUp); - $gamepadconnected = window.addEventListener("gamepadconnected", onGamepadConnected); - $gamepaddisconnected = window.addEventListener("gamepaddisconnected", onGamepadDisconnected); + window.addEventListener("mousemove", onMouseMove); + window.addEventListener("mousedown", onMouseDown); + window.addEventListener("mouseup", onMouseUp); + window.addEventListener("mousewheel", onMouseWheel); + window.addEventListener("keydown", onKeyDown); + window.addEventListener("keyup", onKeyUp); + window.addEventListener("gamepadconnected", onGamepadConnected); + window.addEventListener("gamepaddisconnected", onGamepadDisconnected); }; const destroyEvents = () => {