"use strict"; /* * Copyright (C) 1998-2017 by Northwoods Software Corporation. All Rights Reserved. */ import * as go from "../release/go"; // A custom TreeLayout that requires a "Split" node and a "Merge" node, by category. // The "Split" node should be the root of a tree-like structure if one excludes links to the "Merge" node. // This will position the "Merge" node to line up with the "Split" node. /** * @constructor * @extends TreeLayout * @class * Assume there is a pair of nodes that "Split" and "Merge", * along with any number of nodes extending in a tree-structure from the "Split" node. * You can set all of the TreeLayout properties that you like, * except that for simplicity this code just works for angle === 0 or angle === 90. */ export class ParallelLayout extends go.TreeLayout { public isRealtime = false; // these are desired for the Parallel Layout: public alignment = go.TreeLayout.AlignmentCenterChildren; public compaction = go.TreeLayout.CompactionNone; public alternateAlignment = go.TreeLayout.AlignmentCenterChildren; public alternateCompaction = go.TreeLayout.CompactionNone; public makeNetwork(coll: go.Iterable): go.LayoutNetwork { var net = super.makeNetwork.call(this, coll); // look for and remember the one "Split" node and the one "Merge" node for (var it = net.vertexes.iterator; it.next();) { var v = it.value; // handle asymmetric Groups, where the Placeholder is not centered if (v.node instanceof go.Group && v.node.isSubGraphExpanded && v.node.placeholder !== null) { v.focus = v.node.placeholder.getDocumentPoint(go.Spot.Center).subtract(v.node.position); } if (v.node.category === "Split") { if (net.splitNode) throw new Error("Split node already exists in " + this + " -- existing: " + net.splitNode + " new: " + v.node); net.splitNode = v.node; } else if (v.node.category === "Merge") { if (net.mergeNode) throw new Error("Merge node already exists in " + this + " -- existing: " + net.mergeNode + " new: " + v.node); net.mergeNode = v.node; } } if (net.splitNode || net.mergeNode) { if (!net.splitNode) throw new Error("Missing Split node in " + this); if (!net.mergeNode) throw new Error("Missing Merge node in " + this); } // don't lay out the Merge node if (net.mergeNode) net.deleteNode(net.mergeNode); return net; }; public commitNodes() { super.commitNodes.call(this); var mergeNode = (this.network as any).mergeNode; var splitNode = (this.network as any).splitNode; var splitVertex: any = this.network.findVertex(splitNode); if (mergeNode && splitVertex) { // line up the "Merge" node to the center of the "Split" node if (this.angle === 0) { mergeNode.position = new go.Point(splitVertex.x + splitVertex.subtreeSize.width + this.layerSpacing, splitVertex.centerY - mergeNode.actualBounds.height / 2); } else if (this.angle === 90) { mergeNode.position = new go.Point(splitVertex.centerX - mergeNode.actualBounds.width / 2, splitVertex.y + splitVertex.subtreeSize.height + this.layerSpacing); } } }; public commitLinks() { super.commitLinks.call(this); var mergeNode = (this.network as any).mergeNode; if (mergeNode) { for (var it = mergeNode.findLinksInto(); it.next();) { var link = it.value; if (this.angle === 0) { link.fromSpot = go.Spot.Right; link.toSpot = go.Spot.Left; } else if (this.angle === 90) { link.fromSpot = go.Spot.Bottom; link.toSpot = go.Spot.Top; } if (!link.isOrthogonal) continue; // have all of the links coming into the "Merge" node have segments // that share a common X (or if angle==90, Y) coordinate link.updateRoute(); if (link.pointsCount >= 6) { var pts = link.points.copy(); var p2 = pts.elt(pts.length - 4); var p3 = pts.elt(pts.length - 3); if (this.angle === 0) { var x = mergeNode.position.x - this.layerSpacing / 2; pts.setElt(pts.length - 4, new go.Point(x, p2.y)); pts.setElt(pts.length - 3, new go.Point(x, p3.y)); } else if (this.angle === 90) { var y = mergeNode.position.y - this.layerSpacing / 2; pts.setElt(pts.length - 4, new go.Point(p2.x, y)); pts.setElt(pts.length - 3, new go.Point(p3.x, y)); } link.points = pts; } } } }; }