269 lines
7.3 KiB
TypeScript
269 lines
7.3 KiB
TypeScript
import { IContainerModel } from '../Interfaces/IContainerModel';
|
|
|
|
export function * MakeChildrenIterator(containers: Map<string, IContainerModel>, childrenIds: string[]): Generator<IContainerModel, void, unknown> {
|
|
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<string, IContainerModel>, enableHideChildrenInTreeview = false): Generator<IContainerModel, void, unknown> {
|
|
const queue: IContainerModel[] = [root];
|
|
const visited = new Set<IContainerModel>(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<string, IContainerModel>): Generator<ContainerAndDepth, void, unknown> {
|
|
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<string, IContainerModel>,
|
|
depth: number,
|
|
currentTransform: [number, number],
|
|
enableHideChildrenInTreeview: boolean = false
|
|
): Generator<ContainerAndDepthAndTransform, void, unknown> {
|
|
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<string, IContainerModel>, 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<string, IContainerModel>, 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<string, IContainerModel>,
|
|
container: IContainerModel,
|
|
stop?: IContainerModel
|
|
): IContainerModel[] {
|
|
const it = MakeContainerLinkedListIterator(containers, container, stop);
|
|
return [...it];
|
|
}
|
|
|
|
export function * MakeContainerLinkedListIterator(
|
|
containers: Map<string, IContainerModel>,
|
|
container: IContainerModel,
|
|
stop?: IContainerModel
|
|
): Generator<IContainerModel, void, unknown> {
|
|
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<string, IContainerModel>,
|
|
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<string, IContainerModel>,
|
|
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<string, IContainerModel>, 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<string, IContainerModel>, id: string): IContainerModel | undefined {
|
|
return containers.get(id);
|
|
}
|
|
|
|
export interface IPair<T> {
|
|
cur: T
|
|
next: T
|
|
}
|
|
|
|
export function * Pairwise<T>(arr: T[]): Generator<IPair<T>, void, unknown> {
|
|
for (let i = 0; i < arr.length - 1; i++) {
|
|
yield { cur: arr[i], next: arr[i + 1] };
|
|
}
|
|
}
|
|
|
|
export function * ReversePairwise<T>(arr: T[]): Generator<IPair<T>, void, unknown> {
|
|
for (let i = arr.length - 1; i > 0; i--) {
|
|
yield { cur: arr[i], next: arr[i - 1] };
|
|
}
|
|
}
|