devexpress-diagram
Version:
DevExpress Diagram Control
59 lines (56 loc) • 2.85 kB
text/typescript
import { IKeyOwner } from "../Interfaces";
import { Graph } from "./Graph";
import { ConnectionMode } from "./Structures";
import { ItemKey } from "../Model/DiagramItem";
export class Tree<T extends IKeyOwner> {
private parentToChildren: {[parentKey: string]: T[]};
private childToParent: {[childKey: string]: ItemKey} = {};
root: T;
constructor(root: T, parentToChildren: {[parentKey: string]: T[]}) {
this.root = root;
this.parentToChildren = parentToChildren;
for(const key in parentToChildren) {
if(!Object.prototype.hasOwnProperty.call(parentToChildren, key)) continue;
parentToChildren[key].forEach(c => this.childToParent[c.key] = key);
}
}
getChildren(node: T): T[] {
return node && this.parentToChildren[node.key] ? this.parentToChildren[node.key] : [];
}
hasChildren(node: T): boolean {
return this.parentToChildren[node.key] && this.parentToChildren[node.key].length > 0;
}
iterate(callback: (node: T, level: number) => void) {
this.iterateCore(this.root, 0, callback);
}
static createSpanningTree<T extends IKeyOwner>(component: Graph<T>): Tree<T> {
const rootKey = Tree.findRoot(component);
const iterator = component.createIterator(ConnectionMode.Outgoing);
const parentToChildren: { [parentKey: string]: T[] } = {};
iterator.skipEdge = (e => e.to === undefined || iterator.isNodeVisited(e.to));
iterator.onNode = n => parentToChildren[n.key] = [];
iterator.onEdge = e => {
const node = component.getNode(e.to);
node && parentToChildren[e.from].push(node);
};
iterator.iterate(rootKey);
return new Tree(component.getNode(rootKey), parentToChildren);
}
private iterateCore(node: T, level: number, callback: (node: T, level: number) => void) {
callback(node, level);
this.getChildren(node).forEach(n => this.iterateCore(n, level + 1, callback));
}
private static findRoot<T extends IKeyOwner>(component: Graph<T>): ItemKey {
return component.nodes.reduce<{inc: number, out: number, candidate: ItemKey}>((aggregator, cur) => {
const edges = component.getAdjacentEdges(cur);
const inc = edges.filter(l => l.to === cur).length;
const out = edges.filter(l => l.from === cur).length;
if(aggregator.candidate === undefined || (inc === 0 && aggregator.inc > 0) || (aggregator.inc !== 0 && aggregator.out - aggregator.inc < out - inc)) {
aggregator.candidate = cur;
aggregator.inc = inc;
aggregator.out = out;
}
return aggregator;
}, { inc: -1, out: -1, candidate: undefined }).candidate;
}
}