import { IContainerModel } from '../Interfaces/IContainerModel'; export function * MakeChildrenIterator(containers: Map, childrenIds: string[]): Generator { for (const childId of childrenIds) { const child = FindContainerById(containers, childId); if (child === undefined) { return; } yield child; }; } /** * Returns a Generator iterating of over the children depth-first */ export function * MakeDFSIterator(root: IContainerModel, containers: Map, enableHideChildrenInTreeview = false): Generator { const queue: IContainerModel[] = [root]; const visited = new Set(queue); while (queue.length > 0) { const container = queue.pop() as IContainerModel; yield container; if (enableHideChildrenInTreeview && container.properties.hideChildrenInTreeview) { continue; } for (let i = container.children.length - 1; i >= 0; i--) { const childId = container.children[i]; const child = FindContainerById(containers, childId); if (child === undefined || visited.has(child)) { continue; } visited.add(child); queue.push(child); } } } export interface ContainerAndDepth { container: IContainerModel depth: number } export interface ContainerAndDepthAndTransform extends ContainerAndDepth { currentTransform: [number, number] } /** * Returns a Generator iterating of over the children depth-first */ export function * MakeBFSIterator(root: IContainerModel, containers: Map): Generator { const queue: IContainerModel[] = [root]; let depth = 0; while (queue.length > 0) { let levelSize = queue.length; while (levelSize-- !== 0) { const container = queue.shift() as IContainerModel; yield { container, depth }; for (let i = container.children.length - 1; i >= 0; i--) { const childId = container.children[i]; const child = FindContainerById(containers, childId); if (child === undefined) { continue; } queue.push(child); } } depth++; } } export function * MakeRecursionDFSIterator( root: IContainerModel | null, containers: Map, depth: number, currentTransform: [number, number], enableHideChildrenInTreeview: boolean = false ): Generator { if (root === null) { return; } yield { container: root, depth, currentTransform }; if (enableHideChildrenInTreeview && root.properties.hideChildrenInTreeview) { return; } for (const containerId of root.children) { const container = FindContainerById(containers, containerId); if (container === undefined) { continue; } yield * MakeRecursionDFSIterator( container, containers, depth + 1, [ currentTransform[0] + root.properties.x, currentTransform[1] + root.properties.y ], enableHideChildrenInTreeview ); } } /** * Returns the depth of the container * @returns The depth of the container * @deprecated Please avoid using this function inside an iteration, * use recursive DFS or iterative BFS to get the depth */ export function GetDepth(containers: Map, parent: IContainerModel): number { let depth = 0; let current: IContainerModel | null = parent; while (current != null) { depth++; current = FindContainerById(containers, current.properties.parentId) ?? null; } return depth; } /** * Returns the absolute position by iterating to the parent * @returns The absolute position of the container */ export function GetAbsolutePosition(containers: Map, container: IContainerModel): [number, number] { const x = container.properties.x; const y = container.properties.y; const parent = FindContainerById(containers, container.properties.parentId) ?? null; return CancelParentTransform(containers, parent, x, y); } export function GetContainerLinkedList( containers: Map, container: IContainerModel, stop?: IContainerModel ): IContainerModel[] { const it = MakeContainerLinkedListIterator(containers, container, stop); return [...it]; } export function * MakeContainerLinkedListIterator( containers: Map, container: IContainerModel, stop?: IContainerModel ): Generator { let current: IContainerModel | null = container; while (current !== stop && current != null) { yield current; current = FindContainerById(containers, current.properties.parentId) ?? null; } } /** * Cancel the hierarchic transformations to the given x, y * @param parent Parent of the container to remove its transform * @param x value to be restored * @param y value to be restored * @returns x and y such that the transformations of the parent are cancelled */ export function CancelParentTransform( containers: Map, parent: IContainerModel | null, x: number, y: number, stop?: IContainerModel ): [number, number] { if (parent === null) { return [x, y]; } const it = MakeContainerLinkedListIterator(containers, parent, stop); for (const current of it) { x += current.properties.x; y += current.properties.y; } return [x, y]; } /** * Apply the hierarchic transformations to the given x, y * @param parent Parent of the container to remove its transform * @param x value to be restored * @param y value to be restored * @returns x and y such that the transformations of the parent are applied */ export function ApplyParentTransform( containers: Map, parent: IContainerModel | null, x: number, y: number, stop?: IContainerModel ): [number, number] { if (parent === null) { return [x, y]; } const it = MakeContainerLinkedListIterator(containers, parent, stop); for (const current of it) { x -= current.properties.x; y -= current.properties.y; } return [x, y]; } /** * Returns the container by id * @deprecated Please use FindContainerById * @param root Root of the container tree * @param id Id of the container to find * @returns The container found or undefined if not found */ export function FindContainerByIdDFS(root: IContainerModel, containers: Map, id: string): IContainerModel | undefined { const it = MakeDFSIterator(root, containers); for (const container of it) { if (container.properties.id === id) { return container; } } return undefined; } /** * Returns the container by id * For now, does the same as containers.get(id) * @param containers Map of containers * @param id id of container * @returns Container by id */ export function FindContainerById(containers: Map, id: string): IContainerModel | undefined { return containers.get(id); } export interface IPair { cur: T next: T } export function * Pairwise(arr: T[]): Generator, void, unknown> { for (let i = 0; i < arr.length - 1; i++) { yield { cur: arr[i], next: arr[i + 1] }; } } export function * ReversePairwise(arr: T[]): Generator, void, unknown> { for (let i = arr.length - 1; i > 0; i--) { yield { cur: arr[i], next: arr[i - 1] }; } }