import { CubicBezier } from '../paths/CubicBezier'; import { Line } from '../paths/Line'; import { QuadraticBezier } from '../paths/QuadraticBezier'; import { Link } from '../renderables/Link'; import { Node } from '../types/Node'; import { distance, Point } from '../types/Point'; import { Router } from './Router'; class BundleRouter extends Router { private _factor: number; constructor () { super(); this._collapse = true; this._factor = 0.25; } calculateLink (link: Link, source: Node, target: Node, group: Link[]): void { const srcPoint: Point = { x: source.x, y: source.y }; const trgPoint: Point = { x: target.x, y: target.y }; const sourceShape = this._nodeShapeMap.get(source); const targetShape = this._nodeShapeMap.get(target); if (source === target && sourceShape) { const points = sourceShape.selfLoopPoints(source); link.path = new CubicBezier(points[0], points[1], points[2], points[3]); } else { const index = this._bundleMap.indexOf(link); const points = index !== undefined && group.length > 1 ? this._bundlePoints(srcPoint, trgPoint, index, group.length) : [srcPoint, trgPoint]; if (points.length === 2) { link.path = new Line(points[0], points[1]); } if (points.length === 3) { link.path = new QuadraticBezier(points[0], points[1], points[2]); } this._calcalateShapeOffsets(link, sourceShape, targetShape); } } getFactor (): number { return this._factor; } setFactor (factor: number): void { this._factor = factor; } private _bundlePoints (source: Point, target: Point, index: number, length: number): Point[] { if (this._factor < 0.005) return [source, target]; // Calculate the bundle width based on distance between source and target nodes const size = this._factor * distance(source, target); // Calculate perpendicular angle let rt = Math.sign(index) || 1; let ng = Math.atan2(target.y - source.y, target.x - source.x) + rt * Math.PI / 2; // Calculate center point const cx = (source.x + target.x) / 2; const cy = (source.y + target.y) / 2; // Calculate "bottom" point const bx = cx + (size / 2) * Math.cos(ng); const by = cy + (size / 2) * Math.sin(ng); // Flip the angle by 180 degrees ng -= Math.PI; // Translate based on index percentage const percent = Math.abs(index) / (length - 1); const mid = { x: bx + percent * size * Math.cos(ng), y: by + percent * size * Math.sin(ng) }; return [source, mid, target]; } } export { BundleRouter }