"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var rectangle_1 = require("./rectangle"); var vpsc_1 = require("./vpsc"); var shortestpaths_1 = require("./shortestpaths"); var NodeWrapper = (function () { function NodeWrapper(id, rect, children) { this.id = id; this.rect = rect; this.children = children; this.leaf = typeof children === 'undefined' || children.length === 0; } return NodeWrapper; }()); exports.NodeWrapper = NodeWrapper; var Vert = (function () { function Vert(id, x, y, node, line) { if (node === void 0) { node = null; } if (line === void 0) { line = null; } this.id = id; this.x = x; this.y = y; this.node = node; this.line = line; } return Vert; }()); exports.Vert = Vert; var LongestCommonSubsequence = (function () { function LongestCommonSubsequence(s, t) { this.s = s; this.t = t; var mf = LongestCommonSubsequence.findMatch(s, t); var tr = t.slice(0).reverse(); var mr = LongestCommonSubsequence.findMatch(s, tr); if (mf.length >= mr.length) { this.length = mf.length; this.si = mf.si; this.ti = mf.ti; this.reversed = false; } else { this.length = mr.length; this.si = mr.si; this.ti = t.length - mr.ti - mr.length; this.reversed = true; } } LongestCommonSubsequence.findMatch = function (s, t) { var m = s.length; var n = t.length; var match = { length: 0, si: -1, ti: -1 }; var l = new Array(m); for (var i = 0; i < m; i++) { l[i] = new Array(n); for (var j = 0; j < n; j++) if (s[i] === t[j]) { var v = l[i][j] = (i === 0 || j === 0) ? 1 : l[i - 1][j - 1] + 1; if (v > match.length) { match.length = v; match.si = i - v + 1; match.ti = j - v + 1; } ; } else l[i][j] = 0; } return match; }; LongestCommonSubsequence.prototype.getSequence = function () { return this.length >= 0 ? this.s.slice(this.si, this.si + this.length) : []; }; return LongestCommonSubsequence; }()); exports.LongestCommonSubsequence = LongestCommonSubsequence; var GridRouter = (function () { function GridRouter(originalnodes, accessor, groupPadding) { var _this = this; if (groupPadding === void 0) { groupPadding = 12; } this.originalnodes = originalnodes; this.groupPadding = groupPadding; this.leaves = null; this.nodes = originalnodes.map(function (v, i) { return new NodeWrapper(i, accessor.getBounds(v), accessor.getChildren(v)); }); this.leaves = this.nodes.filter(function (v) { return v.leaf; }); this.groups = this.nodes.filter(function (g) { return !g.leaf; }); this.cols = this.getGridLines('x'); this.rows = this.getGridLines('y'); this.groups.forEach(function (v) { return v.children.forEach(function (c) { return _this.nodes[c].parent = v; }); }); this.root = { children: [] }; this.nodes.forEach(function (v) { if (typeof v.parent === 'undefined') { v.parent = _this.root; _this.root.children.push(v.id); } v.ports = []; }); this.backToFront = this.nodes.slice(0); this.backToFront.sort(function (x, y) { return _this.getDepth(x) - _this.getDepth(y); }); var frontToBackGroups = this.backToFront.slice(0).reverse().filter(function (g) { return !g.leaf; }); frontToBackGroups.forEach(function (v) { var r = rectangle_1.Rectangle.empty(); v.children.forEach(function (c) { return r = r.union(_this.nodes[c].rect); }); v.rect = r.inflate(_this.groupPadding); }); var colMids = this.midPoints(this.cols.map(function (r) { return r.pos; })); var rowMids = this.midPoints(this.rows.map(function (r) { return r.pos; })); var rowx = colMids[0], rowX = colMids[colMids.length - 1]; var coly = rowMids[0], colY = rowMids[rowMids.length - 1]; var hlines = this.rows.map(function (r) { return ({ x1: rowx, x2: rowX, y1: r.pos, y2: r.pos }); }) .concat(rowMids.map(function (m) { return ({ x1: rowx, x2: rowX, y1: m, y2: m }); })); var vlines = this.cols.map(function (c) { return ({ x1: c.pos, x2: c.pos, y1: coly, y2: colY }); }) .concat(colMids.map(function (m) { return ({ x1: m, x2: m, y1: coly, y2: colY }); })); var lines = hlines.concat(vlines); lines.forEach(function (l) { return l.verts = []; }); this.verts = []; this.edges = []; hlines.forEach(function (h) { return vlines.forEach(function (v) { var p = new Vert(_this.verts.length, v.x1, h.y1); h.verts.push(p); v.verts.push(p); _this.verts.push(p); var i = _this.backToFront.length; while (i-- > 0) { var node = _this.backToFront[i], r = node.rect; var dx = Math.abs(p.x - r.cx()), dy = Math.abs(p.y - r.cy()); if (dx < r.width() / 2 && dy < r.height() / 2) { p.node = node; break; } } }); }); lines.forEach(function (l, li) { _this.nodes.forEach(function (v, i) { v.rect.lineIntersections(l.x1, l.y1, l.x2, l.y2).forEach(function (intersect, j) { var p = new Vert(_this.verts.length, intersect.x, intersect.y, v, l); _this.verts.push(p); l.verts.push(p); v.ports.push(p); }); }); var isHoriz = Math.abs(l.y1 - l.y2) < 0.1; var delta = function (a, b) { return isHoriz ? b.x - a.x : b.y - a.y; }; l.verts.sort(delta); for (var i = 1; i < l.verts.length; i++) { var u = l.verts[i - 1], v = l.verts[i]; if (u.node && u.node === v.node && u.node.leaf) continue; _this.edges.push({ source: u.id, target: v.id, length: Math.abs(delta(u, v)) }); } }); } GridRouter.prototype.avg = function (a) { return a.reduce(function (x, y) { return x + y; }) / a.length; }; GridRouter.prototype.getGridLines = function (axis) { var columns = []; var ls = this.leaves.slice(0, this.leaves.length); while (ls.length > 0) { var overlapping = ls.filter(function (v) { return v.rect['overlap' + axis.toUpperCase()](ls[0].rect); }); var col = { nodes: overlapping, pos: this.avg(overlapping.map(function (v) { return v.rect['c' + axis](); })) }; columns.push(col); col.nodes.forEach(function (v) { return ls.splice(ls.indexOf(v), 1); }); } columns.sort(function (a, b) { return a.pos - b.pos; }); return columns; }; GridRouter.prototype.getDepth = function (v) { var depth = 0; while (v.parent !== this.root) { depth++; v = v.parent; } return depth; }; GridRouter.prototype.midPoints = function (a) { var gap = a[1] - a[0]; var mids = [a[0] - gap / 2]; for (var i = 1; i < a.length; i++) { mids.push((a[i] + a[i - 1]) / 2); } mids.push(a[a.length - 1] + gap / 2); return mids; }; GridRouter.prototype.findLineage = function (v) { var lineage = [v]; do { v = v.parent; lineage.push(v); } while (v !== this.root); return lineage.reverse(); }; GridRouter.prototype.findAncestorPathBetween = function (a, b) { var aa = this.findLineage(a), ba = this.findLineage(b), i = 0; while (aa[i] === ba[i]) i++; return { commonAncestor: aa[i - 1], lineages: aa.slice(i).concat(ba.slice(i)) }; }; GridRouter.prototype.siblingObstacles = function (a, b) { var _this = this; var path = this.findAncestorPathBetween(a, b); var lineageLookup = {}; path.lineages.forEach(function (v) { return lineageLookup[v.id] = {}; }); var obstacles = path.commonAncestor.children.filter(function (v) { return !(v in lineageLookup); }); path.lineages .filter(function (v) { return v.parent !== path.commonAncestor; }) .forEach(function (v) { return obstacles = obstacles.concat(v.parent.children.filter(function (c) { return c !== v.id; })); }); return obstacles.map(function (v) { return _this.nodes[v]; }); }; GridRouter.getSegmentSets = function (routes, x, y) { var vsegments = []; for (var ei = 0; ei < routes.length; ei++) { var route = routes[ei]; for (var si = 0; si < route.length; si++) { var s = route[si]; s.edgeid = ei; s.i = si; var sdx = s[1][x] - s[0][x]; if (Math.abs(sdx) < 0.1) { vsegments.push(s); } } } vsegments.sort(function (a, b) { return a[0][x] - b[0][x]; }); var vsegmentsets = []; var segmentset = null; for (var i = 0; i < vsegments.length; i++) { var s = vsegments[i]; if (!segmentset || Math.abs(s[0][x] - segmentset.pos) > 0.1) { segmentset = { pos: s[0][x], segments: [] }; vsegmentsets.push(segmentset); } segmentset.segments.push(s); } return vsegmentsets; }; GridRouter.nudgeSegs = function (x, y, routes, segments, leftOf, gap) { var n = segments.length; if (n <= 1) return; var vs = segments.map(function (s) { return new vpsc_1.Variable(s[0][x]); }); var cs = []; for (var i = 0; i < n; i++) { for (var j = 0; j < n; j++) { if (i === j) continue; var s1 = segments[i], s2 = segments[j], e1 = s1.edgeid, e2 = s2.edgeid, lind = -1, rind = -1; if (x == 'x') { if (leftOf(e1, e2)) { if (s1[0][y] < s1[1][y]) { lind = j, rind = i; } else { lind = i, rind = j; } } } else { if (leftOf(e1, e2)) { if (s1[0][y] < s1[1][y]) { lind = i, rind = j; } else { lind = j, rind = i; } } } if (lind >= 0) { cs.push(new vpsc_1.Constraint(vs[lind], vs[rind], gap)); } } } var solver = new vpsc_1.Solver(vs, cs); solver.solve(); vs.forEach(function (v, i) { var s = segments[i]; var pos = v.position(); s[0][x] = s[1][x] = pos; var route = routes[s.edgeid]; if (s.i > 0) route[s.i - 1][1][x] = pos; if (s.i < route.length - 1) route[s.i + 1][0][x] = pos; }); }; GridRouter.nudgeSegments = function (routes, x, y, leftOf, gap) { var vsegmentsets = GridRouter.getSegmentSets(routes, x, y); for (var i = 0; i < vsegmentsets.length; i++) { var ss = vsegmentsets[i]; var events = []; for (var j = 0; j < ss.segments.length; j++) { var s = ss.segments[j]; events.push({ type: 0, s: s, pos: Math.min(s[0][y], s[1][y]) }); events.push({ type: 1, s: s, pos: Math.max(s[0][y], s[1][y]) }); } events.sort(function (a, b) { return a.pos - b.pos + a.type - b.type; }); var open = []; var openCount = 0; events.forEach(function (e) { if (e.type === 0) { open.push(e.s); openCount++; } else { openCount--; } if (openCount == 0) { GridRouter.nudgeSegs(x, y, routes, open, leftOf, gap); open = []; } }); } }; GridRouter.prototype.routeEdges = function (edges, nudgeGap, source, target) { var _this = this; var routePaths = edges.map(function (e) { return _this.route(source(e), target(e)); }); var order = GridRouter.orderEdges(routePaths); var routes = routePaths.map(function (e) { return GridRouter.makeSegments(e); }); GridRouter.nudgeSegments(routes, 'x', 'y', order, nudgeGap); GridRouter.nudgeSegments(routes, 'y', 'x', order, nudgeGap); GridRouter.unreverseEdges(routes, routePaths); return routes; }; GridRouter.unreverseEdges = function (routes, routePaths) { routes.forEach(function (segments, i) { var path = routePaths[i]; if (path.reversed) { segments.reverse(); segments.forEach(function (segment) { segment.reverse(); }); } }); }; GridRouter.angleBetween2Lines = function (line1, line2) { var angle1 = Math.atan2(line1[0].y - line1[1].y, line1[0].x - line1[1].x); var angle2 = Math.atan2(line2[0].y - line2[1].y, line2[0].x - line2[1].x); var diff = angle1 - angle2; if (diff > Math.PI || diff < -Math.PI) { diff = angle2 - angle1; } return diff; }; GridRouter.isLeft = function (a, b, c) { return ((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) <= 0; }; GridRouter.getOrder = function (pairs) { var outgoing = {}; for (var i = 0; i < pairs.length; i++) { var p = pairs[i]; if (typeof outgoing[p.l] === 'undefined') outgoing[p.l] = {}; outgoing[p.l][p.r] = true; } return function (l, r) { return typeof outgoing[l] !== 'undefined' && outgoing[l][r]; }; }; GridRouter.orderEdges = function (edges) { var edgeOrder = []; for (var i = 0; i < edges.length - 1; i++) { for (var j = i + 1; j < edges.length; j++) { var e = edges[i], f = edges[j], lcs = new LongestCommonSubsequence(e, f); var u, vi, vj; if (lcs.length === 0) continue; if (lcs.reversed) { f.reverse(); f.reversed = true; lcs = new LongestCommonSubsequence(e, f); } if ((lcs.si <= 0 || lcs.ti <= 0) && (lcs.si + lcs.length >= e.length || lcs.ti + lcs.length >= f.length)) { edgeOrder.push({ l: i, r: j }); continue; } if (lcs.si + lcs.length >= e.length || lcs.ti + lcs.length >= f.length) { u = e[lcs.si + 1]; vj = e[lcs.si - 1]; vi = f[lcs.ti - 1]; } else { u = e[lcs.si + lcs.length - 2]; vi = e[lcs.si + lcs.length]; vj = f[lcs.ti + lcs.length]; } if (GridRouter.isLeft(u, vi, vj)) { edgeOrder.push({ l: j, r: i }); } else { edgeOrder.push({ l: i, r: j }); } } } return GridRouter.getOrder(edgeOrder); }; GridRouter.makeSegments = function (path) { function copyPoint(p) { return { x: p.x, y: p.y }; } var isStraight = function (a, b, c) { return Math.abs((b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x)) < 0.001; }; var segments = []; var a = copyPoint(path[0]); for (var i = 1; i < path.length; i++) { var b = copyPoint(path[i]), c = i < path.length - 1 ? path[i + 1] : null; if (!c || !isStraight(a, b, c)) { segments.push([a, b]); a = b; } } return segments; }; GridRouter.prototype.route = function (s, t) { var _this = this; var source = this.nodes[s], target = this.nodes[t]; this.obstacles = this.siblingObstacles(source, target); var obstacleLookup = {}; this.obstacles.forEach(function (o) { return obstacleLookup[o.id] = o; }); this.passableEdges = this.edges.filter(function (e) { var u = _this.verts[e.source], v = _this.verts[e.target]; return !(u.node && u.node.id in obstacleLookup || v.node && v.node.id in obstacleLookup); }); for (var i = 1; i < source.ports.length; i++) { var u = source.ports[0].id; var v = source.ports[i].id; this.passableEdges.push({ source: u, target: v, length: 0 }); } for (var i = 1; i < target.ports.length; i++) { var u = target.ports[0].id; var v = target.ports[i].id; this.passableEdges.push({ source: u, target: v, length: 0 }); } var getSource = function (e) { return e.source; }, getTarget = function (e) { return e.target; }, getLength = function (e) { return e.length; }; var shortestPathCalculator = new shortestpaths_1.Calculator(this.verts.length, this.passableEdges, getSource, getTarget, getLength); var bendPenalty = function (u, v, w) { var a = _this.verts[u], b = _this.verts[v], c = _this.verts[w]; var dx = Math.abs(c.x - a.x), dy = Math.abs(c.y - a.y); if (a.node === source && a.node === b.node || b.node === target && b.node === c.node) return 0; return dx > 1 && dy > 1 ? 1000 : 0; }; var shortestPath = shortestPathCalculator.PathFromNodeToNodeWithPrevCost(source.ports[0].id, target.ports[0].id, bendPenalty); var pathPoints = shortestPath.reverse().map(function (vi) { return _this.verts[vi]; }); pathPoints.push(this.nodes[target.id].ports[0]); return pathPoints.filter(function (v, i) { return !(i < pathPoints.length - 1 && pathPoints[i + 1].node === source && v.node === source || i > 0 && v.node === target && pathPoints[i - 1].node === target); }); }; GridRouter.getRoutePath = function (route, cornerradius, arrowwidth, arrowheight) { var result = { routepath: 'M ' + route[0][0].x + ' ' + route[0][0].y + ' ', arrowpath: '' }; if (route.length > 1) { for (var i = 0; i < route.length; i++) { var li = route[i]; var x = li[1].x, y = li[1].y; var dx = x - li[0].x; var dy = y - li[0].y; if (i < route.length - 1) { if (Math.abs(dx) > 0) { x -= dx / Math.abs(dx) * cornerradius; } else { y -= dy / Math.abs(dy) * cornerradius; } result.routepath += 'L ' + x + ' ' + y + ' '; var l = route[i + 1]; var x0 = l[0].x, y0 = l[0].y; var x1 = l[1].x; var y1 = l[1].y; dx = x1 - x0; dy = y1 - y0; var angle = GridRouter.angleBetween2Lines(li, l) < 0 ? 1 : 0; var x2, y2; if (Math.abs(dx) > 0) { x2 = x0 + dx / Math.abs(dx) * cornerradius; y2 = y0; } else { x2 = x0; y2 = y0 + dy / Math.abs(dy) * cornerradius; } var cx = Math.abs(x2 - x); var cy = Math.abs(y2 - y); result.routepath += 'A ' + cx + ' ' + cy + ' 0 0 ' + angle + ' ' + x2 + ' ' + y2 + ' '; } else { var arrowtip = [x, y]; var arrowcorner1, arrowcorner2; if (Math.abs(dx) > 0) { x -= dx / Math.abs(dx) * arrowheight; arrowcorner1 = [x, y + arrowwidth]; arrowcorner2 = [x, y - arrowwidth]; } else { y -= dy / Math.abs(dy) * arrowheight; arrowcorner1 = [x + arrowwidth, y]; arrowcorner2 = [x - arrowwidth, y]; } result.routepath += 'L ' + x + ' ' + y + ' '; if (arrowheight > 0) { result.arrowpath = 'M ' + arrowtip[0] + ' ' + arrowtip[1] + ' L ' + arrowcorner1[0] + ' ' + arrowcorner1[1] + ' L ' + arrowcorner2[0] + ' ' + arrowcorner2[1]; } } } } else { var li = route[0]; var x = li[1].x, y = li[1].y; var dx = x - li[0].x; var dy = y - li[0].y; var arrowtip = [x, y]; var arrowcorner1, arrowcorner2; if (Math.abs(dx) > 0) { x -= dx / Math.abs(dx) * arrowheight; arrowcorner1 = [x, y + arrowwidth]; arrowcorner2 = [x, y - arrowwidth]; } else { y -= dy / Math.abs(dy) * arrowheight; arrowcorner1 = [x + arrowwidth, y]; arrowcorner2 = [x - arrowwidth, y]; } result.routepath += 'L ' + x + ' ' + y + ' '; if (arrowheight > 0) { result.arrowpath = 'M ' + arrowtip[0] + ' ' + arrowtip[1] + ' L ' + arrowcorner1[0] + ' ' + arrowcorner1[1] + ' L ' + arrowcorner2[0] + ' ' + arrowcorner2[1]; } } return result; }; return GridRouter; }()); exports.GridRouter = GridRouter; //# sourceMappingURL=gridrouter.js.map