import { Link } from '../renderables/Link'; import { Node } from '../types/Node'; class LinkSet { _all: Map>; // non-directional _map: Map>; // directional _ind: Map; // non-direcitonal indices constructor () { this._all = new Map(); this._map = new Map(); this._ind = new Map(); } /** * Add a link that goes from source to target. * @param source The source Node * @param target The target Node * @param link The link connecting the source and target nodes */ add (source: Node, target: Node, link: Link): void { this._addDirectional(source, target, link); this._addNonDirectional(source, target, link); } /** * Get all links between nodes a and b, regardless of direction * @param a A Node * @param b A node */ allBetween (a: Node, b: Node): Link[] { return this._getNonDirectional(a, b); } /** * Get links beginning at source and ending at target * @param source The source Node * @param target The target Node */ between (source: Node, target: Node): Link[] { const targetmap = this._map.get(source); return targetmap ? targetmap.get(target) || [] : []; } /** * Execute the provided callback for every link in the set * @param callback The function to execute for each link. Arguments are: * * link: The current link being processed * * source: The source Node * * target: The target Node * * group: All link */ forEach (callback: (link: Link, source: Node, target: Node, group: Link[]) => void): void { this._map.forEach((targetmap, source) => { targetmap.forEach((links, target) => { const group = this._getNonDirectional(source, target); links.forEach(link => callback(link, source, target, group)); }); }); } /** * Get the index of this link among all links that connect the same * two nodes. Indices are signed to indicate direction groups. Returns * undefined if link is not in set. * @param link The link */ indexOf (link: Link): number | undefined { return this._ind.get(link); } private _addDirectional (source: Node, target: Node, link: Link): void { if (!this._map.has(source)) this._map.set(source, new Map()); const targetmap = this._map.get(source)!; if (!targetmap.has(target)) targetmap.set(target, []); targetmap.get(target)!.push(link); } private _addNonDirectional (source: Node, target: Node, link: Link): void { // Look for source -> target -> links if (this._all.has(source)) { const targetmap = this._all.get(source)!; if (!targetmap.has(target)) targetmap.set(target, []); const len = targetmap.get(target)!.push(link); this._ind.set(link, len-1); return; } // Look for target -> source -> links if (this._all.has(target)) { const sourcemap = this._all.get(target)!; if (!sourcemap.has(source)) sourcemap.set(source, []); const len = sourcemap.get(source)!.push(link); this._ind.set(link, 1-len); return; } // Neither exists, so create source -> target -> links this._all.set(source, new Map([[target, [link]]])); this._ind.set(link, 0); } private _getNonDirectional (source: Node, target: Node): Link[] { if (this._all.has(source)) { const targetmap = this._all.get(source)!; if (targetmap.has(target)) { return targetmap.get(target)!; } } if (this._all.has(target)) { const sourcemap = this._all.get(target)!; if (sourcemap.has(source)) { return sourcemap.get(source)!; } } return []; } } export { LinkSet }