ecspresso
Version:
A minimal Entity-Component-System library for typescript and javascript.
130 lines (129 loc) • 5.42 kB
TypeScript
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>;
}