From 609d2c4d63a3f2032b1c1181ff1ff0cce4d9bfbf Mon Sep 17 00:00:00 2001 From: MartinFillon Date: Wed, 19 Mar 2025 17:59:31 +0100 Subject: [PATCH 1/4] feat(ecs): add type for components --- packages/ecs/lib/libecs.d.ts | 14 ++++++------- packages/ecs/src/ecs-library.ts | 16 ++++++++------- packages/ecs/test/ecs-library.spec.ts | 1 + packages/ecs/test/wasm/IndexedZipper.spec.ts | 2 ++ packages/ecs/test/wasm/Registry.spec.ts | 2 ++ packages/ecs/test/wasm/Zipper.spec.ts | 2 ++ packages/ecs/wasm/Registry.cpp | 21 +++++++++----------- packages/ecs/wasm/Registry.hpp | 18 ++++++++--------- packages/ecs/wasm/Utils.hpp | 3 +++ 9 files changed, 44 insertions(+), 35 deletions(-) diff --git a/packages/ecs/lib/libecs.d.ts b/packages/ecs/lib/libecs.d.ts index 8de38659..f66b1deb 100644 --- a/packages/ecs/lib/libecs.d.ts +++ b/packages/ecs/lib/libecs.d.ts @@ -77,21 +77,21 @@ export interface IndexedZipper extends ClassHandle { } export interface Registry extends ClassHandle { + registerComponent(_0: {name: string, [key: string]: any}): SparseArray; + getComponentsConst(_0: {name: string, [key: string]: any}): SparseArray; + getComponents(_0: {name: string, [key: string]: any}): SparseArray; spawnEntity(): Entity; killEntity(_0: Entity): void; clearEntities(): void; + removeComponent(_0: Entity, _1: {name: string, [key: string]: any}): void; runSystems(): void; clearSystems(): void; entityFromIndex(_0: number): Entity; removeSystem(_0: number): void; maxEntities(): number; - registerComponent(_0: any): SparseArray; - getComponentsConst(_0: any): SparseArray; - getComponents(_0: any): SparseArray; - getEntityComponentConst(_0: Entity, _1: any): any | undefined; - getEntityComponent(_0: Entity, _1: any): any | undefined; - addComponent(_0: Entity, _1: any): any | undefined; - removeComponent(_0: Entity, _1: any): void; + getEntityComponentConst(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; + getEntityComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; + addComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; addSystem(_0: any): void; getZipper(_0: any): Zipper; getIndexedZipper(_0: any): IndexedZipper; diff --git a/packages/ecs/src/ecs-library.ts b/packages/ecs/src/ecs-library.ts index f7504ac0..b03b73d1 100644 --- a/packages/ecs/src/ecs-library.ts +++ b/packages/ecs/src/ecs-library.ts @@ -4,6 +4,8 @@ import { BaseComponentSystemLibrary, type InitContext } from "@nanoforge/common" import type { Entity, MainModule, Registry, SparseArray, Zipper } from "../lib"; import { Module } from "../lib"; +type Component = { name: string; [key: string]: any }; + export class ECSLibrary extends BaseComponentSystemLibrary { private module: MainModule; private registry: Registry; @@ -29,7 +31,7 @@ export class ECSLibrary extends BaseComponentSystemLibrary { return Promise.resolve(); } - addComponent(entity: Entity, component: any): void { + addComponent(entity: Entity, component: Component): void { this.registry.addComponent(entity, component); } @@ -37,19 +39,19 @@ export class ECSLibrary extends BaseComponentSystemLibrary { return this.registry.spawnEntity(); } - getComponents(component: any): SparseArray { + getComponents(component: Component): SparseArray { return this.registry.getComponents(component); } - removeComponent(entity: Entity, component: any): void { + removeComponent(entity: Entity, component: Component): void { this.registry.removeComponent(entity, component); } - getEntityComponent(entity: Entity, component: any): any | undefined { + getEntityComponent(entity: Entity, component: Component): any | undefined { return this.registry.getEntityComponent(entity, component); } - getEntityComponentConst(entity: Entity, component: any): any | undefined { + getEntityComponentConst(entity: Entity, component: Component): any | undefined { return this.registry.getEntityComponentConst(entity, component); } @@ -89,11 +91,11 @@ export class ECSLibrary extends BaseComponentSystemLibrary { this.registry.addSystem(system); } - getZipper(types: [any]): Zipper { + getZipper(types: [Component]): Zipper { return this.registry.getZipper(types); } - getIndexedZipper(types: [any]): Zipper { + getIndexedZipper(types: [Component]): Zipper { return this.registry.getIndexedZipper(types); } } diff --git a/packages/ecs/test/ecs-library.spec.ts b/packages/ecs/test/ecs-library.spec.ts index aff0a937..daf7b53b 100644 --- a/packages/ecs/test/ecs-library.spec.ts +++ b/packages/ecs/test/ecs-library.spec.ts @@ -4,6 +4,7 @@ import { EditableLibraryManager } from "@nanoforge/core/src/common/library/manag import { ECSLibrary } from "@nanoforge/ecs/src/ecs-library"; class Position { + name: string = "Position"; constructor( public x: number, public y: number, diff --git a/packages/ecs/test/wasm/IndexedZipper.spec.ts b/packages/ecs/test/wasm/IndexedZipper.spec.ts index 36f4991e..43669e17 100644 --- a/packages/ecs/test/wasm/IndexedZipper.spec.ts +++ b/packages/ecs/test/wasm/IndexedZipper.spec.ts @@ -1,6 +1,7 @@ import Module from "../../lib/libecs.js"; class Velocity { + name: string = "Velocity"; x: number; y: number; @@ -11,6 +12,7 @@ class Velocity { } class Position { + name: string = "Position"; x: number; y: number; diff --git a/packages/ecs/test/wasm/Registry.spec.ts b/packages/ecs/test/wasm/Registry.spec.ts index 10f36a60..a5edffaa 100644 --- a/packages/ecs/test/wasm/Registry.spec.ts +++ b/packages/ecs/test/wasm/Registry.spec.ts @@ -1,6 +1,7 @@ import Module from "../../lib/libecs.js"; class Velocity { + name: string = "Velocity"; x: number; y: number; @@ -11,6 +12,7 @@ class Velocity { } class Position { + name: string = "Position"; x: number; y: number; diff --git a/packages/ecs/test/wasm/Zipper.spec.ts b/packages/ecs/test/wasm/Zipper.spec.ts index 151dbb5f..20fd739a 100644 --- a/packages/ecs/test/wasm/Zipper.spec.ts +++ b/packages/ecs/test/wasm/Zipper.spec.ts @@ -1,6 +1,7 @@ import Module from "../../lib/libecs.js"; class Velocity { + name: string = "Velocity"; x: number; y: number; @@ -11,6 +12,7 @@ class Velocity { } class Position { + name: string = "Position"; x: number; y: number; diff --git a/packages/ecs/wasm/Registry.cpp b/packages/ecs/wasm/Registry.cpp index 41d6b7e0..462f25e5 100644 --- a/packages/ecs/wasm/Registry.cpp +++ b/packages/ecs/wasm/Registry.cpp @@ -13,31 +13,30 @@ #include #include +#include "Utils.hpp" #include "Registry.hpp" namespace nfo { EMSCRIPTEN_BINDINGS(Registry) { + emscripten::register_type("{name: string, [key: string]: any}"); + emscripten::class_("Registry") .constructor() .function("registerComponent", &Registry::register_component) .function( "getComponentsConst", - emscripten::select_overload const &(const emscripten::val &) const, Registry>(&Registry::get_components) - ) - .function( - "getComponents", emscripten::select_overload &(const emscripten::val &), Registry>(&Registry::get_components) + emscripten::select_overload const &(const Component &) const, Registry>(&Registry::get_components) ) + .function("getComponents", emscripten::select_overload &(const Component &), Registry>(&Registry::get_components)) .function( "getEntityComponentConst", - emscripten::select_overload const &(Entity, const emscripten::val &) const, Registry>( - &Registry::get_entity_component - ) + emscripten::select_overload const &(Entity, const Component &) const, Registry>(&Registry::get_entity_component) ) .function( "getEntityComponent", - emscripten::select_overload &(Entity, const emscripten::val &), Registry>(&Registry::get_entity_component) + emscripten::select_overload &(Entity, const Component &), Registry>(&Registry::get_entity_component) ) .function("spawnEntity", &Registry::spawn_entity) .function("entityFromIndex", &Registry::entity_from_index) @@ -45,11 +44,9 @@ namespace nfo { .function("clearEntities", &Registry::clear_entities) .function( "addComponent", - emscripten::select_overload::reference_type &(const Entity &, emscripten::val &&), Registry>( - &Registry::add_component - ) + emscripten::select_overload::reference_type &(const Entity &, Component &&), Registry>(&Registry::add_component) ) - .function("removeComponent", emscripten::select_overload(&Registry::remove_component)) + .function("removeComponent", emscripten::select_overload(&Registry::remove_component)) .function("addSystem", emscripten::select_overload(&Registry::add_system)) .function("runSystems", &Registry::run_systems) .function("removeSystem", &Registry::remove_system) diff --git a/packages/ecs/wasm/Registry.hpp b/packages/ecs/wasm/Registry.hpp index 1bd5601f..28a534bf 100644 --- a/packages/ecs/wasm/Registry.hpp +++ b/packages/ecs/wasm/Registry.hpp @@ -29,7 +29,7 @@ namespace nfo { class Registry { public: - SparseArray ®ister_component(const emscripten::val &c) + SparseArray ®ister_component(const Component &c) { std::string component_type(get_js_class_name(c)); if (component_type == "entity" || component_type == "id") @@ -55,7 +55,7 @@ namespace nfo { return std::any_cast &>(_components_arrays[component_type]); } - SparseArray &get_components(const emscripten::val &c) + SparseArray &get_components(const Component &c) { const std::string component_type(get_js_class_name(c)); if (!_components_arrays.contains(component_type)) @@ -64,7 +64,7 @@ namespace nfo { return std::any_cast &>(components); } - [[nodiscard]] SparseArray const &get_components(const emscripten::val &c) const + [[nodiscard]] SparseArray const &get_components(const Component &c) const { const std::string component_type(get_js_class_name(c)); if (!_components_arrays.contains(component_type)) @@ -73,7 +73,7 @@ namespace nfo { return std::any_cast &>(components); } - std::optional &get_entity_component(const Entity e, const emscripten::val &c) + std::optional &get_entity_component(const Entity e, const Component &c) { const std::string component_type(get_js_class_name(c)); if (!_components_arrays.contains(component_type)) @@ -82,7 +82,7 @@ namespace nfo { return std::any_cast &>(components)[e]; } - [[nodiscard]] std::optional const &get_entity_component(const Entity e, const emscripten::val &c) const + [[nodiscard]] std::optional const &get_entity_component(const Entity e, const Component &c) const { const std::string component_type(get_js_class_name(c)); if (!_components_arrays.contains(component_type)) @@ -125,7 +125,7 @@ namespace nfo { _components_arrays.clear(); } - SparseArray::reference_type add_component(Entity const &to, emscripten::val &&c) + SparseArray::reference_type add_component(Entity const &to, Component &&c) { const std::string component_type(get_js_class_name(c)); if (!_components_arrays.contains(component_type)) { @@ -134,7 +134,7 @@ namespace nfo { return get_components(c).insert_at(to, c); } - void remove_component(Entity const &from, emscripten::val &&c) + void remove_component(Entity const &from, Component &&c) { const std::string component_type(get_js_class_name(c)); if (!_components_arrays.contains(component_type)) @@ -188,7 +188,7 @@ namespace nfo { std::map *> arrays; for (int i = 0; i < comps["length"].as(); i++) { - arrays[get_js_class_name(comps[i])] = &get_components(comps[i]); + arrays[get_js_class_name(comps[i])] = &get_components(Component(comps[i])); } return Zipper(arrays); } @@ -200,7 +200,7 @@ namespace nfo { std::map *> arrays; for (int i = 0; i < comps["length"].as(); i++) { - arrays[get_js_class_name(comps[i])] = &get_components(comps[i]); + arrays[get_js_class_name(comps[i])] = &get_components(Component(comps[i])); } return IndexedZipper(arrays); } diff --git a/packages/ecs/wasm/Utils.hpp b/packages/ecs/wasm/Utils.hpp index 3b45d3c2..1b7bd097 100644 --- a/packages/ecs/wasm/Utils.hpp +++ b/packages/ecs/wasm/Utils.hpp @@ -19,6 +19,9 @@ #define UNKNOWN_COMPONENT_TYPE "__magic_unkown_component_type" +EMSCRIPTEN_DECLARE_VAL_TYPE(Component); +EMSCRIPTEN_DECLARE_VAL_TYPE(ComponentArray); + std::optional json_to_str(const emscripten::val &c); std::optional get_js_member(const emscripten::val &c, const std::string &member); std::string get_js_class_name(const emscripten::val &c); From 724e9f4f7a1f018fb7beb2141c041b2aeed7894c Mon Sep 17 00:00:00 2001 From: MartinFillon Date: Thu, 20 Mar 2025 11:38:22 +0100 Subject: [PATCH 2/4] feat(systems): add ts type for systems --- packages/ecs/lib/libecs.d.ts | 61 ++++++++++++++++----------------- packages/ecs/src/ecs-library.ts | 9 ++--- packages/ecs/wasm/Registry.cpp | 5 ++- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/packages/ecs/lib/libecs.d.ts b/packages/ecs/lib/libecs.d.ts index f66b1deb..a7217fac 100644 --- a/packages/ecs/lib/libecs.d.ts +++ b/packages/ecs/lib/libecs.d.ts @@ -1,21 +1,20 @@ // TypeScript bindings for emscripten-generated code. Automatically generated at compile time. declare namespace RuntimeExports { - let HEAPF32: any; - let HEAPF64: any; - let HEAP_DATA_VIEW: any; - let HEAP8: any; - let HEAPU8: any; - let HEAP16: any; - let HEAPU16: any; - let HEAP32: any; - let HEAPU32: any; - let HEAP64: any; - let HEAPU64: any; -} -interface WasmModule { + let HEAPF32: any; + let HEAPF64: any; + let HEAP_DATA_VIEW: any; + let HEAP8: any; + let HEAPU8: any; + let HEAP16: any; + let HEAPU16: any; + let HEAP32: any; + let HEAPU32: any; + let HEAP64: any; + let HEAPU64: any; } +interface WasmModule {} -type EmbindString = ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string; +type EmbindString = ArrayBuffer | Uint8Array | Uint8ClampedArray | Int8Array | string; export interface ClassHandle { isAliasOf(other: ClassHandle): boolean; delete(): void; @@ -77,52 +76,52 @@ export interface IndexedZipper extends ClassHandle { } export interface Registry extends ClassHandle { - registerComponent(_0: {name: string, [key: string]: any}): SparseArray; - getComponentsConst(_0: {name: string, [key: string]: any}): SparseArray; - getComponents(_0: {name: string, [key: string]: any}): SparseArray; + registerComponent(_0: { name: string; [key: string]: any }): SparseArray; + getComponentsConst(_0: { name: string; [key: string]: any }): SparseArray; + getComponents(_0: { name: string; [key: string]: any }): SparseArray; spawnEntity(): Entity; killEntity(_0: Entity): void; clearEntities(): void; - removeComponent(_0: Entity, _1: {name: string, [key: string]: any}): void; + removeComponent(_0: Entity, _1: { name: string; [key: string]: any }): void; + addSystem(_0: (registry: Registry) => void): void; runSystems(): void; clearSystems(): void; entityFromIndex(_0: number): Entity; removeSystem(_0: number): void; maxEntities(): number; - getEntityComponentConst(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; - getEntityComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; - addComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; - addSystem(_0: any): void; + getEntityComponentConst(_0: Entity, _1: { name: string; [key: string]: any }): any | undefined; + getEntityComponent(_0: Entity, _1: { name: string; [key: string]: any }): any | undefined; + addComponent(_0: Entity, _1: { name: string; [key: string]: any }): any | undefined; getZipper(_0: any): Zipper; getIndexedZipper(_0: any): IndexedZipper; } interface EmbindModule { container: { - new(): container; + new (): container; }; SparseArray: { - new(): SparseArray; + new (): SparseArray; }; Entity: { - new(_0: number): Entity; + new (_0: number): Entity; }; MapStringSparseArray: { - new(): MapStringSparseArray; + new (): MapStringSparseArray; }; VectorString: { - new(): VectorString; + new (): VectorString; }; Zipper: { - new(_0: MapStringSparseArray): Zipper; + new (_0: MapStringSparseArray): Zipper; }; IndexedZipper: { - new(_0: MapStringSparseArray): IndexedZipper; + new (_0: MapStringSparseArray): IndexedZipper; }; Registry: { - new(): Registry; + new (): Registry; }; } export type MainModule = WasmModule & typeof RuntimeExports & EmbindModule; -export default function MainModuleFactory (options?: unknown): Promise; +export default function MainModuleFactory(options?: unknown): Promise; diff --git a/packages/ecs/src/ecs-library.ts b/packages/ecs/src/ecs-library.ts index b03b73d1..f19d27cb 100644 --- a/packages/ecs/src/ecs-library.ts +++ b/packages/ecs/src/ecs-library.ts @@ -1,10 +1,11 @@ import { type AssetManagerLibrary } from "@nanoforge/asset-manager"; import { BaseComponentSystemLibrary, type InitContext } from "@nanoforge/common"; -import type { Entity, MainModule, Registry, SparseArray, Zipper } from "../lib"; +import type { Entity, IndexedZipper, MainModule, Registry, SparseArray, Zipper } from "../lib"; import { Module } from "../lib"; -type Component = { name: string; [key: string]: any }; +export type Component = { name: string; [key: string]: any }; +export type System = (registry: Registry) => void; export class ECSLibrary extends BaseComponentSystemLibrary { private module: MainModule; @@ -87,7 +88,7 @@ export class ECSLibrary extends BaseComponentSystemLibrary { return this.registry.maxEntities(); } - addSystem(system: any): void { + addSystem(system: System): void { this.registry.addSystem(system); } @@ -95,7 +96,7 @@ export class ECSLibrary extends BaseComponentSystemLibrary { return this.registry.getZipper(types); } - getIndexedZipper(types: [Component]): Zipper { + getIndexedZipper(types: [Component]): IndexedZipper { return this.registry.getIndexedZipper(types); } } diff --git a/packages/ecs/wasm/Registry.cpp b/packages/ecs/wasm/Registry.cpp index 462f25e5..dcaa0500 100644 --- a/packages/ecs/wasm/Registry.cpp +++ b/packages/ecs/wasm/Registry.cpp @@ -18,9 +18,12 @@ #include "Registry.hpp" namespace nfo { + EMSCRIPTEN_DECLARE_VAL_TYPE(System); + EMSCRIPTEN_BINDINGS(Registry) { emscripten::register_type("{name: string, [key: string]: any}"); + emscripten::register_type("(registry: Registry) => void"); emscripten::class_("Registry") .constructor() @@ -47,7 +50,7 @@ namespace nfo { emscripten::select_overload::reference_type &(const Entity &, Component &&), Registry>(&Registry::add_component) ) .function("removeComponent", emscripten::select_overload(&Registry::remove_component)) - .function("addSystem", emscripten::select_overload(&Registry::add_system)) + .function("addSystem", emscripten::select_overload(&Registry::add_system)) .function("runSystems", &Registry::run_systems) .function("removeSystem", &Registry::remove_system) .function("clearSystems", &Registry::clear_systems) From 68bdafbfa211f9ae755b7e36f11ceeb1ef8134e9 Mon Sep 17 00:00:00 2001 From: MartinFillon Date: Thu, 20 Mar 2025 12:21:44 +0100 Subject: [PATCH 3/4] feat(ecs): type getEntityComponent family of functions --- packages/ecs/src/ecs-library.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ecs/src/ecs-library.ts b/packages/ecs/src/ecs-library.ts index f19d27cb..d1b0e9c1 100644 --- a/packages/ecs/src/ecs-library.ts +++ b/packages/ecs/src/ecs-library.ts @@ -48,11 +48,11 @@ export class ECSLibrary extends BaseComponentSystemLibrary { this.registry.removeComponent(entity, component); } - getEntityComponent(entity: Entity, component: Component): any | undefined { + getEntityComponent(entity: Entity, component: Component): Component | undefined { return this.registry.getEntityComponent(entity, component); } - getEntityComponentConst(entity: Entity, component: Component): any | undefined { + getEntityComponentConst(entity: Entity, component: Component): Component | undefined { return this.registry.getEntityComponentConst(entity, component); } From d666441b1819fe46338cafbc71b4a4329ab9e718 Mon Sep 17 00:00:00 2001 From: Leo Date: Thu, 20 Mar 2025 12:44:53 +0100 Subject: [PATCH 4/4] feat(ecs/zipper): merge zipper in types --- packages/ecs/Makefile | 2 - packages/ecs/lib/libecs.d.ts | 90 ++++-------- packages/ecs/src/ecs-library.ts | 8 +- packages/ecs/test/wasm/IndexedZipper.spec.ts | 137 ------------------- packages/ecs/test/wasm/Zipper.spec.ts | 54 +++----- packages/ecs/wasm/IndexedZipper.cpp | 27 ---- packages/ecs/wasm/IndexedZipper.hpp | 76 ---------- packages/ecs/wasm/Registry.cpp | 1 - packages/ecs/wasm/Registry.hpp | 36 ++--- packages/ecs/wasm/Zipper.cpp | 30 ---- packages/ecs/wasm/Zipper.hpp | 77 ----------- 11 files changed, 67 insertions(+), 471 deletions(-) delete mode 100644 packages/ecs/test/wasm/IndexedZipper.spec.ts delete mode 100644 packages/ecs/wasm/IndexedZipper.cpp delete mode 100644 packages/ecs/wasm/IndexedZipper.hpp delete mode 100644 packages/ecs/wasm/Zipper.cpp delete mode 100644 packages/ecs/wasm/Zipper.hpp diff --git a/packages/ecs/Makefile b/packages/ecs/Makefile index 7f06c530..ce863f6f 100644 --- a/packages/ecs/Makefile +++ b/packages/ecs/Makefile @@ -1,8 +1,6 @@ SRC = wasm/SparseArray.cpp\ wasm/Entity.cpp\ wasm/Utils.cpp\ - wasm/Zipper.cpp\ - wasm/IndexedZipper.cpp\ wasm/Registry.cpp NAME := libecs diff --git a/packages/ecs/lib/libecs.d.ts b/packages/ecs/lib/libecs.d.ts index a7217fac..6839d53d 100644 --- a/packages/ecs/lib/libecs.d.ts +++ b/packages/ecs/lib/libecs.d.ts @@ -1,20 +1,20 @@ // TypeScript bindings for emscripten-generated code. Automatically generated at compile time. declare namespace RuntimeExports { - let HEAPF32: any; - let HEAPF64: any; - let HEAP_DATA_VIEW: any; - let HEAP8: any; - let HEAPU8: any; - let HEAP16: any; - let HEAPU16: any; - let HEAP32: any; - let HEAPU32: any; - let HEAP64: any; - let HEAPU64: any; + let HEAPF32: any; + let HEAPF64: any; + let HEAP_DATA_VIEW: any; + let HEAP8: any; + let HEAPU8: any; + let HEAP16: any; + let HEAPU16: any; + let HEAP32: any; + let HEAPU32: any; + let HEAP64: any; + let HEAPU64: any; +} +interface WasmModule { } -interface WasmModule {} -type EmbindString = ArrayBuffer | Uint8Array | Uint8ClampedArray | Int8Array | string; export interface ClassHandle { isAliasOf(other: ClassHandle): boolean; delete(): void; @@ -50,78 +50,40 @@ export interface Entity extends ClassHandle { getId(): number; } -export interface MapStringSparseArray extends ClassHandle { - keys(): VectorString; - size(): number; - get(_0: EmbindString): SparseArray | undefined; - set(_0: EmbindString, _1: SparseArray | null): void; -} - -export interface VectorString extends ClassHandle { - size(): number; - get(_0: number): EmbindString | undefined; - push_back(_0: EmbindString): void; - resize(_0: number, _1: EmbindString): void; - set(_0: number, _1: EmbindString): boolean; -} - -export interface Zipper extends ClassHandle { - next(): any; - getValue(): any; -} - -export interface IndexedZipper extends ClassHandle { - next(): any; - getValue(): any; -} - export interface Registry extends ClassHandle { - registerComponent(_0: { name: string; [key: string]: any }): SparseArray; - getComponentsConst(_0: { name: string; [key: string]: any }): SparseArray; - getComponents(_0: { name: string; [key: string]: any }): SparseArray; + registerComponent(_0: {name: string, [key: string]: any}): SparseArray; + getComponentsConst(_0: {name: string, [key: string]: any}): SparseArray; + getComponents(_0: {name: string, [key: string]: any}): SparseArray; spawnEntity(): Entity; killEntity(_0: Entity): void; clearEntities(): void; - removeComponent(_0: Entity, _1: { name: string; [key: string]: any }): void; + removeComponent(_0: Entity, _1: {name: string, [key: string]: any}): void; addSystem(_0: (registry: Registry) => void): void; runSystems(): void; clearSystems(): void; entityFromIndex(_0: number): Entity; removeSystem(_0: number): void; maxEntities(): number; - getEntityComponentConst(_0: Entity, _1: { name: string; [key: string]: any }): any | undefined; - getEntityComponent(_0: Entity, _1: { name: string; [key: string]: any }): any | undefined; - addComponent(_0: Entity, _1: { name: string; [key: string]: any }): any | undefined; - getZipper(_0: any): Zipper; - getIndexedZipper(_0: any): IndexedZipper; + getEntityComponentConst(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; + getEntityComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; + addComponent(_0: Entity, _1: {name: string, [key: string]: any}): any | undefined; + getZipper(_0: any): any; } interface EmbindModule { container: { - new (): container; + new(): container; }; SparseArray: { - new (): SparseArray; + new(): SparseArray; }; Entity: { - new (_0: number): Entity; - }; - MapStringSparseArray: { - new (): MapStringSparseArray; - }; - VectorString: { - new (): VectorString; - }; - Zipper: { - new (_0: MapStringSparseArray): Zipper; - }; - IndexedZipper: { - new (_0: MapStringSparseArray): IndexedZipper; + new(_0: number): Entity; }; Registry: { - new (): Registry; + new(): Registry; }; } export type MainModule = WasmModule & typeof RuntimeExports & EmbindModule; -export default function MainModuleFactory(options?: unknown): Promise; +export default function MainModuleFactory (options?: unknown): Promise; diff --git a/packages/ecs/src/ecs-library.ts b/packages/ecs/src/ecs-library.ts index d1b0e9c1..f38588cf 100644 --- a/packages/ecs/src/ecs-library.ts +++ b/packages/ecs/src/ecs-library.ts @@ -1,7 +1,7 @@ import { type AssetManagerLibrary } from "@nanoforge/asset-manager"; import { BaseComponentSystemLibrary, type InitContext } from "@nanoforge/common"; -import type { Entity, IndexedZipper, MainModule, Registry, SparseArray, Zipper } from "../lib"; +import type { Entity, MainModule, Registry, SparseArray } from "../lib"; import { Module } from "../lib"; export type Component = { name: string; [key: string]: any }; @@ -92,11 +92,7 @@ export class ECSLibrary extends BaseComponentSystemLibrary { this.registry.addSystem(system); } - getZipper(types: [Component]): Zipper { + getZipper(types: [Component]): [any] { return this.registry.getZipper(types); } - - getIndexedZipper(types: [Component]): IndexedZipper { - return this.registry.getIndexedZipper(types); - } } diff --git a/packages/ecs/test/wasm/IndexedZipper.spec.ts b/packages/ecs/test/wasm/IndexedZipper.spec.ts deleted file mode 100644 index 43669e17..00000000 --- a/packages/ecs/test/wasm/IndexedZipper.spec.ts +++ /dev/null @@ -1,137 +0,0 @@ -import Module from "../../lib/libecs.js"; - -class Velocity { - name: string = "Velocity"; - x: number; - y: number; - - constructor(x: number, y: number) { - this.x = x; - this.y = y; - } -} - -class Position { - name: string = "Position"; - x: number; - y: number; - - constructor(x: number, y: number) { - this.x = x; - this.y = y; - } -} - -describe("IndexedZipper", () => { - test("basic instantation", async () => { - const m = await Module(); - const v = new m.MapStringSparseArray(); - - const zip = new m.IndexedZipper(v); - - expect(zip).toBeDefined(); - expect(zip.getValue()).toBeUndefined(); - }); - - test("single simple sparse array instantation", async () => { - const m = await Module(); - const r = new m.Registry(); - expect(r).toBeDefined(); - - for (let i = 0; i < 5; i++) { - const e = r.spawnEntity(); - r.addComponent(e, new Velocity(i, i)); - } - - const zip = r.getIndexedZipper([Velocity]); - expect(zip).toBeDefined(); - - for (let i = 0; i < 5; i++, zip.next()) { - expect(zip.getValue()).toStrictEqual({ entity: i, Velocity: new Velocity(i, i) }); - } - expect(zip.getValue()).toBeUndefined(); - }); - - test("single complex sparse array instantation", async () => { - const m = await Module(); - const r = new m.Registry(); - expect(r).toBeDefined(); - - for (let i = 0; i < 5; i++) { - const e = new m.Entity(i * 5); - r.addComponent(e, new Velocity(i, i)); - } - - const zip = r.getIndexedZipper([Velocity]); - expect(zip).toBeDefined(); - - for (let i = 0; i < 5; i++) { - expect(zip.getValue()).toStrictEqual({ entity: i * 5, Velocity: new Velocity(i, i) }); - zip.next(); - } - expect(zip.getValue()).toBeUndefined(); - }); - - test("multiple complex sparse array instantation", async () => { - const m = await Module(); - const r = new m.Registry(); - expect(r).toBeDefined(); - - for (let i = 0; i < 20; i++) { - const e = r.spawnEntity(); - if (i % 5 === 0) r.addComponent(e, new Velocity(0, i)); - if (i % 3 === 0) r.addComponent(e, new Position(i, 0)); - } - - const zip = r.getIndexedZipper([Velocity, Position]); - expect(zip).toBeDefined(); - - for (let i = 0; i < 20; i++) { - if (i % 3 === 0 && i % 5 === 0) { - expect(zip.getValue()).toStrictEqual({ - entity: i, - Velocity: new Velocity(0, i), - Position: new Position(i, 0), - }); - zip.next(); - } - } - expect(zip.getValue()).toBeUndefined(); - }); - - test("simple indexed zipper modification", async () => { - const m = await Module(); - const r = new m.Registry(); - expect(r).toBeDefined(); - - for (let i = 0; i < 20; i++) { - const e = r.spawnEntity(); - if (i % 5 === 0) { - r.addComponent(e, new Velocity(0, i)); - } - } - - let zip = r.getIndexedZipper([Velocity]); - expect(zip).toBeDefined(); - - for (let i = 0; i < 20; i++) { - if (i % 5 === 0) { - const vel = zip.getValue()["Velocity"]; - vel.y *= 2; - zip.next(); - } - } - - zip = r.getIndexedZipper([Velocity]); - for (let i = 0; i < 20; i++) { - if (i % 5 === 0) { - expect(zip.getValue()).toStrictEqual({ - entity: i, - Velocity: new Velocity(0, i * 2), - }); - zip.next(); - } - } - expect(zip.getValue()).toBeUndefined(); - }); -}); diff --git a/packages/ecs/test/wasm/Zipper.spec.ts b/packages/ecs/test/wasm/Zipper.spec.ts index 20fd739a..f200b1fd 100644 --- a/packages/ecs/test/wasm/Zipper.spec.ts +++ b/packages/ecs/test/wasm/Zipper.spec.ts @@ -23,16 +23,6 @@ class Position { } describe("Zipper", () => { - test("basic instantation", async () => { - const m = await Module(); - const v = new m.MapStringSparseArray(); - - const zip = new m.Zipper(v); - - expect(zip).toBeDefined(); - expect(zip.getValue()).toBeUndefined(); - }); - test("single simple sparse array instantation", async () => { const m = await Module(); const r = new m.Registry(); @@ -44,12 +34,15 @@ describe("Zipper", () => { } const zip = r.getZipper([Velocity]); - expect(zip).toBeDefined(); - for (let i = 0; i < 5; i++, zip.next()) { - expect(zip.getValue()).toStrictEqual({ Velocity: new Velocity(i, i) }); - } - expect(zip.getValue()).toBeUndefined(); + expect(zip).toBeDefined(); + expect(zip).toStrictEqual([ + { Velocity: new Velocity(0, 0) }, + { Velocity: new Velocity(1, 1) }, + { Velocity: new Velocity(2, 2) }, + { Velocity: new Velocity(3, 3) }, + { Velocity: new Velocity(4, 4) }, + ]); }); test("single complex sparse array instantation", async () => { @@ -65,11 +58,11 @@ describe("Zipper", () => { const zip = r.getZipper([Velocity]); expect(zip).toBeDefined(); - for (let i = 0; i < 5; i++) { - expect(zip.getValue()).toStrictEqual({ Velocity: new Velocity(i, i) }); - zip.next(); - } - expect(zip.getValue()).toBeUndefined(); + expect(zip[0]).toStrictEqual({ Velocity: new Velocity(0, 0) }); + expect(zip[5]).toStrictEqual({ Velocity: new Velocity(1, 1) }); + expect(zip[10]).toStrictEqual({ Velocity: new Velocity(2, 2) }); + expect(zip[15]).toStrictEqual({ Velocity: new Velocity(3, 3) }); + expect(zip[20]).toStrictEqual({ Velocity: new Velocity(4, 4) }); }); test("multiple complex sparse array instantation", async () => { @@ -79,12 +72,8 @@ describe("Zipper", () => { for (let i = 0; i < 20; i++) { const e = r.spawnEntity(); - if (i % 5 === 0) { - r.addComponent(e, new Velocity(0, i)); - } - if (i % 3 === 0) { - r.addComponent(e, new Position(i, 0)); - } + if (i % 5 === 0) r.addComponent(e, new Velocity(0, i)); + if (i % 3 === 0) r.addComponent(e, new Position(i, 0)); } const zip = r.getZipper([Velocity, Position]); @@ -92,17 +81,15 @@ describe("Zipper", () => { for (let i = 0; i < 20; i++) { if (i % 3 === 0 && i % 5 === 0) { - expect(zip.getValue()).toStrictEqual({ + expect(zip[i]).toStrictEqual({ Velocity: new Velocity(0, i), Position: new Position(i, 0), }); - zip.next(); } } - expect(zip.getValue()).toBeUndefined(); }); - test("simple zipper modification", async () => { + test("simple indexed zipper modification", async () => { const m = await Module(); const r = new m.Registry(); expect(r).toBeDefined(); @@ -119,21 +106,18 @@ describe("Zipper", () => { for (let i = 0; i < 20; i++) { if (i % 5 === 0) { - const vel = zip.getValue()["Velocity"]; + const vel = zip[i]["Velocity"]; vel.y *= 2; - zip.next(); } } zip = r.getZipper([Velocity]); for (let i = 0; i < 20; i++) { if (i % 5 === 0) { - expect(zip.getValue()).toStrictEqual({ + expect(zip[i]).toStrictEqual({ Velocity: new Velocity(0, i * 2), }); - zip.next(); } } - expect(zip.getValue()).toBeUndefined(); }); }); diff --git a/packages/ecs/wasm/IndexedZipper.cpp b/packages/ecs/wasm/IndexedZipper.cpp deleted file mode 100644 index 48729991..00000000 --- a/packages/ecs/wasm/IndexedZipper.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/*⠀ ⠀⠀ ⠀⠀⠀⠀⢀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀ -**⠀⠀ ⠀⢀⣠⣾⡿⠿⠛⠛⠛⠛⠿⢿⣷⣄⡀⠀⠀⠀ -** _ __ ______ ⠀ ⠀ ⣰⣿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣆⠀⠀ -** / | / /___ _____ ____ / ____/___ _________ ____ ⠀ ⣾⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣷⠀ -** / |/ / __ `/ __ \/ __ \/ /_ / __ \/ ___/ __ `/ _ \ ⢰⣿⠃⠀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⠤⠀⠀⠘⣿⡆ -** / /| / /_/ / / / / /_/ / __/ / /_/ / / / /_/ / __/ ⢸⣿⠀⠀⠀⠉⠛⠛⢻⣿⣿⣿⠉⠀⠀⠀⠀⠀⣿⡇ -** /_/ |_/\__,_/_/ /_/\____/_/ \____/_/ \__, /\___/ ⠸⣿⡄⠀⠀⠀⠀⣠⣾⣿⣿⣿⣤⠀⠀⠀⠀⢠⣿⠇ -** /____/ ⠀ ⢿⣷⡀⠀⠀⠀⠉⠁⠀⠀⠈⠉⠀⠀⠀⢀⣾⡿⠀ -** ⠀⠀⠹⣿⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣿⠏⠀⠀ -** 2025 ⠀⠀⠀⠈⠙⢿⣷⣶⣤⣤⣤⣤⣶⣾⡿⠋⠁⠀⠀⠀ -**⠀ ⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀ -*/ - -#include -#include - -#include "IndexedZipper.hpp" - -namespace nfo { - EMSCRIPTEN_BINDINGS(IndexedZipper) - { - emscripten::class_("IndexedZipper") - .constructor *> &>() - .function("next", &IndexedZipper::next) - .function("getValue", &IndexedZipper::get_value); - } -} // namespace nfo diff --git a/packages/ecs/wasm/IndexedZipper.hpp b/packages/ecs/wasm/IndexedZipper.hpp deleted file mode 100644 index 2fc9cea8..00000000 --- a/packages/ecs/wasm/IndexedZipper.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/*⠀ ⠀⠀ ⠀⠀⠀⠀⢀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀ -**⠀⠀ ⠀⢀⣠⣾⡿⠿⠛⠛⠛⠛⠿⢿⣷⣄⡀⠀⠀⠀ -** _ __ ______ ⠀ ⠀ ⣰⣿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣆⠀⠀ -** / | / /___ _____ ____ / ____/___ _________ ____ ⠀ ⣾⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣷⠀ -** / |/ / __ `/ __ \/ __ \/ /_ / __ \/ ___/ __ `/ _ \ ⢰⣿⠃⠀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⠤⠀⠀⠘⣿⡆ -** / /| / /_/ / / / / /_/ / __/ / /_/ / / / /_/ / __/ ⢸⣿⠀⠀⠀⠉⠛⠛⢻⣿⣿⣿⠉⠀⠀⠀⠀⠀⣿⡇ -** /_/ |_/\__,_/_/ /_/\____/_/ \____/_/ \__, /\___/ ⠸⣿⡄⠀⠀⠀⠀⣠⣾⣿⣿⣿⣤⠀⠀⠀⠀⢠⣿⠇ -** /____/ ⠀ ⢿⣷⡀⠀⠀⠀⠉⠁⠀⠀⠈⠉⠀⠀⠀⢀⣾⡿⠀ -** ⠀⠀⠹⣿⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣿⠏⠀⠀ -** 2025 ⠀⠀⠀⠈⠙⢿⣷⣶⣤⣤⣤⣤⣶⣾⡿⠋⠁⠀⠀⠀ -**⠀ ⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀ -*/ - -#pragma once - -#include -#include - -#include "SparseArray.hpp" -#include "Utils.hpp" - -namespace nfo { - class IndexedZipper { - public: - explicit IndexedZipper(const std::map *> &arrays) : _arrays(arrays), _max(0), _idx(0) - { - _max = arrays.empty() ? 0 : arrays.begin()->second->size(); - for (SparseArray *&arr : _arrays | std::views::values) { - _max = (std::min)(arr->size(), _max); - } - if (_idx < _max && !all_set()) - incr(); - } - - [[nodiscard]] emscripten::val get_value() const - { - if (_idx >= _max) - return emscripten::val::undefined(); - - emscripten::val res = emscripten::val::object(); - for (SparseArray *const &arr : _arrays | std::views::values) { - res.set(get_js_class_name((*arr)[_idx].value()), (*arr)[_idx].value_or(emscripten::val::undefined())); - } - res.set("entity", _idx); - return res; - } - - emscripten::val next() - { - incr(); - return get_value(); - } - - private: - void incr() - { - if (_idx >= _max) - return; - do { - _idx++; - } while (_idx < _max && !all_set()); - } - - [[nodiscard]] bool all_set() const - { - if (_idx >= _max) - return false; - - return std::ranges::all_of(_arrays | std::views::values, [this](SparseArray *const &arr) { return (*arr)[_idx].has_value(); }); - } - - std::map *> _arrays; - std::size_t _max; - std::size_t _idx; - }; -} // namespace nfo diff --git a/packages/ecs/wasm/Registry.cpp b/packages/ecs/wasm/Registry.cpp index dcaa0500..25f14a18 100644 --- a/packages/ecs/wasm/Registry.cpp +++ b/packages/ecs/wasm/Registry.cpp @@ -55,7 +55,6 @@ namespace nfo { .function("removeSystem", &Registry::remove_system) .function("clearSystems", &Registry::clear_systems) .function("getZipper", &Registry::get_zipper) - .function("getIndexedZipper", &Registry::get_indexed_zipper) .function("maxEntities", &Registry::max_entities); } } // namespace nfo diff --git a/packages/ecs/wasm/Registry.hpp b/packages/ecs/wasm/Registry.hpp index 28a534bf..d0253021 100644 --- a/packages/ecs/wasm/Registry.hpp +++ b/packages/ecs/wasm/Registry.hpp @@ -21,10 +21,8 @@ #include #include "Entity.hpp" -#include "IndexedZipper.hpp" #include "SparseArray.hpp" #include "Utils.hpp" -#include "Zipper.hpp" namespace nfo { class Registry { @@ -181,28 +179,34 @@ namespace nfo { return _next_entity; } - Zipper get_zipper(const emscripten::val &comps) + emscripten::val get_zipper(const emscripten::val &comps) { if (!comps.isArray()) - throw std::runtime_error("get_zipper: comps is not an array"); + throw std::runtime_error("getZipper: need an array of comps as parameter"); + std::size_t max = SIZE_MAX; std::map *> arrays; for (int i = 0; i < comps["length"].as(); i++) { - arrays[get_js_class_name(comps[i])] = &get_components(Component(comps[i])); + SparseArray &components = get_components(Component(comps[i])); + arrays[get_js_class_name(comps[i])] = &components; + max = (std::min)(components.size(), max); } - return Zipper(arrays); - } - - IndexedZipper get_indexed_zipper(const emscripten::val &comps) - { - if (!comps.isArray()) - throw std::runtime_error("get_zipper: comps is not an array"); - std::map *> arrays; - for (int i = 0; i < comps["length"].as(); i++) { - arrays[get_js_class_name(comps[i])] = &get_components(Component(comps[i])); + emscripten::val arr = emscripten::val::array(); + for (std::size_t idx = 0; idx < max; idx++) { + emscripten::val obj = emscripten::val::object(); + bool need_to_add = true; + for (const auto &[name, sparse_array] : arrays) { + if (!(*sparse_array)[idx].has_value()) { + need_to_add = false; + break; + } + obj.set(name, (*sparse_array)[idx].value()); + } + if (need_to_add) + arr.set(idx, obj); } - return IndexedZipper(arrays); + return arr; } private: diff --git a/packages/ecs/wasm/Zipper.cpp b/packages/ecs/wasm/Zipper.cpp deleted file mode 100644 index 5b654f7e..00000000 --- a/packages/ecs/wasm/Zipper.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/*⠀ ⠀⠀ ⠀⠀⠀⠀⢀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀ -**⠀⠀ ⠀⢀⣠⣾⡿⠿⠛⠛⠛⠛⠿⢿⣷⣄⡀⠀⠀⠀ -** _ __ ______ ⠀ ⠀ ⣰⣿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣆⠀⠀ -** / | / /___ _____ ____ / ____/___ _________ ____ ⠀ ⣾⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣷⠀ -** / |/ / __ `/ __ \/ __ \/ /_ / __ \/ ___/ __ `/ _ \ ⢰⣿⠃⠀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⠤⠀⠀⠘⣿⡆ -** / /| / /_/ / / / / /_/ / __/ / /_/ / / / /_/ / __/ ⢸⣿⠀⠀⠀⠉⠛⠛⢻⣿⣿⣿⠉⠀⠀⠀⠀⠀⣿⡇ -** /_/ |_/\__,_/_/ /_/\____/_/ \____/_/ \__, /\___/ ⠸⣿⡄⠀⠀⠀⠀⣠⣾⣿⣿⣿⣤⠀⠀⠀⠀⢠⣿⠇ -** /____/ ⠀ ⢿⣷⡀⠀⠀⠀⠉⠁⠀⠀⠈⠉⠀⠀⠀⢀⣾⡿⠀ -** ⠀⠀⠹⣿⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣿⠏⠀⠀ -** 2025 ⠀⠀⠀⠈⠙⢿⣷⣶⣤⣤⣤⣤⣶⣾⡿⠋⠁⠀⠀⠀ -**⠀ ⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀ -*/ - -#include -#include - -#include "Zipper.hpp" - -namespace nfo { - EMSCRIPTEN_BINDINGS(Zipper) - { - emscripten::register_map *>("MapStringSparseArray"); - emscripten::register_vector("VectorString"); - - emscripten::class_("Zipper") - .constructor *> &>() - .function("next", &Zipper::next) - .function("getValue", &Zipper::get_value); - } -} // namespace nfo diff --git a/packages/ecs/wasm/Zipper.hpp b/packages/ecs/wasm/Zipper.hpp deleted file mode 100644 index ec79c2b2..00000000 --- a/packages/ecs/wasm/Zipper.hpp +++ /dev/null @@ -1,77 +0,0 @@ -/*⠀ ⠀⠀ ⠀⠀⠀⠀⢀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀ -**⠀⠀ ⠀⢀⣠⣾⡿⠿⠛⠛⠛⠛⠿⢿⣷⣄⡀⠀⠀⠀ -** _ __ ______ ⠀ ⠀ ⣰⣿⠛⠁⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⣿⣆⠀⠀ -** / | / /___ _____ ____ / ____/___ _________ ____ ⠀ ⣾⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢿⣷⠀ -** / |/ / __ `/ __ \/ __ \/ /_ / __ \/ ___/ __ `/ _ \ ⢰⣿⠃⠀⣤⣤⣤⣤⣤⣤⣤⣤⣤⣤⠤⠀⠀⠘⣿⡆ -** / /| / /_/ / / / / /_/ / __/ / /_/ / / / /_/ / __/ ⢸⣿⠀⠀⠀⠉⠛⠛⢻⣿⣿⣿⠉⠀⠀⠀⠀⠀⣿⡇ -** /_/ |_/\__,_/_/ /_/\____/_/ \____/_/ \__, /\___/ ⠸⣿⡄⠀⠀⠀⠀⣠⣾⣿⣿⣿⣤⠀⠀⠀⠀⢠⣿⠇ -** /____/ ⠀ ⢿⣷⡀⠀⠀⠀⠉⠁⠀⠀⠈⠉⠀⠀⠀⢀⣾⡿⠀ -** ⠀⠀⠹⣿⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⣿⠏⠀⠀ -** 2025 ⠀⠀⠀⠈⠙⢿⣷⣶⣤⣤⣤⣤⣶⣾⡿⠋⠁⠀⠀⠀ -**⠀ ⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀⠀ -*/ - -#pragma once - -#include -#include - -#include "SparseArray.hpp" -#include "Utils.hpp" - -namespace nfo { - class Zipper { - public: - explicit Zipper(const std::map *> &arrays) : _arrays(arrays), _max(0), _idx(0) - { - _max = arrays.empty() ? 0 : arrays.begin()->second->size(); - for (SparseArray *&arr : _arrays | std::views::values) { - _max = (std::min)(arr->size(), _max); - } - if (_idx < _max && !all_set()) - incr(); - } - - [[nodiscard]] emscripten::val get_value() const - { - if (_idx >= _max) - return emscripten::val::undefined(); - - emscripten::val res = emscripten::val::object(); - for (SparseArray *const &arr : _arrays | std::views::values) { - res.set(get_js_class_name((*arr)[_idx].value()), (*arr)[_idx].value_or(emscripten::val::undefined())); - } - return res; - } - - emscripten::val next() - { - incr(); - return get_value(); - } - - private: - void incr() - { - if (_idx >= _max) - return; - do { - _idx++; - } while (_idx < _max && !all_set()); - } - - [[nodiscard]] bool all_set() const - { - if (_idx >= _max) - return false; - - return std::ranges::all_of(_arrays | std::views::values, [this](SparseArray *const &arr) { - return (*arr)[_idx].has_value(); - }); - } - - std::map *> _arrays; - std::size_t _max; - std::size_t _idx; - }; -} // namespace nfo