svg-layout-designer-react/src/utils/itertools.ts
2022-11-21 13:47:04 +01:00

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] };
}
}