UNPKG

ecspresso

Version:

A minimal Entity-Component-System library for typescript and javascript.

130 lines (129 loc) 5.42 kB
import type { HierarchyEntry, HierarchyIteratorOptions } from "./types"; /** * Manages parent-child relationships between entities. * Handles hierarchy storage, validation, and traversal operations. */ export default class HierarchyManager { /** childId -> parentId */ private parentMap; /** parentId -> ordered childIds */ private childrenMap; /** Pre-allocated flat BFS queue for forEachInHierarchy [entityId, parentId (-1=null), depth, ...] */ private _bfsQueue; /** * Set the parent of an entity. * @param childId The entity to set as a child * @param parentId The entity to set as the parent * @throws Error if this would create a circular reference or self-parenting */ setParent(childId: number, parentId: number): this; /** * Remove the parent relationship for an entity (orphan it). * @param childId The entity to orphan * @returns true if a parent was removed, false if entity had no parent */ removeParent(childId: number): boolean; /** * Get the parent of an entity. * @param entityId The entity to get the parent of * @returns The parent entity ID, or null if no parent */ getParent(entityId: number): number | null; /** * Get all children of an entity in insertion order. * @param parentId The parent entity * @returns Readonly array of child entity IDs */ getChildren(parentId: number): readonly number[]; /** * Get a child at a specific index. * @param parentId The parent entity * @param index The index of the child * @returns The child entity ID, or null if index is out of bounds */ getChildAt(parentId: number, index: number): number | null; /** * Get the index of a child within its parent's children list. * @param parentId The parent entity * @param childId The child entity to find * @returns The index of the child, or -1 if not found */ getChildIndex(parentId: number, childId: number): number; /** * Remove an entity from the hierarchy (called when entity is destroyed). * Orphans any children and removes from parent's children list. * @param entityId The entity being removed * @returns Information about the removal (oldParent and orphanedChildren) */ removeEntity(entityId: number): { oldParent: number | null; orphanedChildren: number[]; }; /** * Get all ancestors of an entity in order [parent, grandparent, ...]. * @param entityId The entity to get ancestors of * @returns Readonly array of ancestor entity IDs */ getAncestors(entityId: number): readonly number[]; /** * Get all descendants of an entity in depth-first order. * @param entityId The entity to get descendants of * @returns Readonly array of descendant entity IDs */ getDescendants(entityId: number): readonly number[]; /** * Get the root ancestor of an entity (topmost parent), or self if no parent. * @param entityId The entity to get the root of * @returns The root entity ID */ getRoot(entityId: number): number; /** * Get siblings of an entity (other children of the same parent). * @param entityId The entity to get siblings of * @returns Readonly array of sibling entity IDs */ getSiblings(entityId: number): readonly number[]; /** * Check if an entity is a descendant of another entity. * @param entityId The potential descendant * @param ancestorId The potential ancestor * @returns true if entityId is a descendant of ancestorId */ isDescendantOf(entityId: number, ancestorId: number): boolean; /** * Check if an entity is an ancestor of another entity. * @param entityId The potential ancestor * @param descendantId The potential descendant * @returns true if entityId is an ancestor of descendantId */ isAncestorOf(entityId: number, descendantId: number): boolean; /** * Returns true when at least one parent-child relationship exists. * O(1) — checks Map size, no iteration. */ get hasHierarchy(): boolean; /** * Get all root entities (entities that have children but no parent). * @returns Readonly array of root entity IDs */ getRootEntities(): readonly number[]; /** * Check if setting a parent would create a cycle. * A cycle would occur if the prospective parent is a descendant of the child. */ private wouldCreateCycle; /** * Traverse the hierarchy in parent-first (breadth-first) order. * Parents are guaranteed to be visited before their children. * @param callback Function called for each entity with (entityId, parentId, depth) * @param options Optional traversal options (roots to filter to specific subtrees) */ forEachInHierarchy(callback: (entityId: number, parentId: number | null, depth: number) => void, options?: HierarchyIteratorOptions): void; /** * Generator-based hierarchy traversal in parent-first (breadth-first) order. * Supports early termination via break. * @param options Optional traversal options (roots to filter to specific subtrees) * @yields HierarchyEntry for each entity in parent-first order */ hierarchyIterator(options?: HierarchyIteratorOptions): Generator<HierarchyEntry, void, unknown>; }