1 | |
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | import * as go from '../release/go-module.js';
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | export class ParallelLayout extends go.TreeLayout {
|
28 | private _splitNode: go.Node | null = null;
|
29 | private _mergeNode: go.Node | null = null;
|
30 |
|
31 | |
32 |
|
33 |
|
34 |
|
35 |
|
36 |
|
37 |
|
38 |
|
39 | constructor() {
|
40 | super();
|
41 | this.isRealtime = false;
|
42 | this.alignment = go.TreeLayout.AlignmentCenterChildren;
|
43 | this.compaction = go.TreeLayout.CompactionNone;
|
44 | this.alternateAlignment = go.TreeLayout.AlignmentCenterChildren;
|
45 | this.alternateCompaction = go.TreeLayout.CompactionNone;
|
46 | }
|
47 |
|
48 | |
49 |
|
50 |
|
51 | get splitNode(): go.Node | null { return this._splitNode; }
|
52 | set splitNode(val: go.Node | null) { this._splitNode = val; }
|
53 |
|
54 | |
55 |
|
56 |
|
57 | get mergeNode(): go.Node | null { return this._mergeNode; }
|
58 | set mergeNode(val: go.Node | null) { this._mergeNode = val; }
|
59 |
|
60 | |
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | isSplit(node: go.Node | null): boolean {
|
68 | if (!(node instanceof go.Node)) return false;
|
69 | var cat = node.category;
|
70 | return (cat === "Split" || cat === "Start" || cat === "For" || cat === "While" || cat === "If" || cat === "Switch");
|
71 | }
|
72 |
|
73 | |
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | isMerge(node: go.Node | null): boolean {
|
81 | if (!(node instanceof go.Node)) return false;
|
82 | var cat = node.category;
|
83 | return (cat === "Merge" || cat === "End" || cat === "EndFor" || cat === "EndWhile" || cat === "EndIf" || cat === "EndSwitch");
|
84 | }
|
85 |
|
86 | |
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 | isConditional(node: go.Node | null): boolean {
|
93 | if (!(node instanceof go.Node)) return false;
|
94 | return node.category === "If";
|
95 | }
|
96 |
|
97 | |
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | isSwitch(node: go.Node | null): boolean {
|
104 | if (!(node instanceof go.Node)) return false;
|
105 | return node.category === "Switch";
|
106 | }
|
107 |
|
108 | |
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 | findSplitMerge(vertexes: go.Iterable<go.TreeVertex>): void {
|
116 | var split = null;
|
117 | var merge = null;
|
118 | var it = vertexes.iterator;
|
119 | while (it.next()) {
|
120 | var v = it.value;
|
121 | if (!v.node) continue;
|
122 | if (this.isSplit(v.node)) {
|
123 | if (split) throw new Error("Split node already exists in " + this + " -- existing: " + split + " new: " + v.node);
|
124 | split = v.node;
|
125 | } else if (this.isMerge(v.node)) {
|
126 | if (merge) throw new Error("Merge node already exists in " + this + " -- existing: " + merge + " new: " + v.node);
|
127 | merge = v.node;
|
128 | }
|
129 | }
|
130 | if (!split) throw new Error("Missing Split node in " + this);
|
131 | if (!merge) throw new Error("Missing Merge node in " + this);
|
132 | this._splitNode = split;
|
133 | this._mergeNode = merge;
|
134 | }
|
135 |
|
136 | |
137 |
|
138 |
|
139 | public makeNetwork(coll: go.Iterable<go.Part>): go.TreeNetwork {
|
140 | const net = super.makeNetwork(coll) as go.TreeNetwork;
|
141 |
|
142 | var it = net.vertexes.iterator;
|
143 | while (it.next()) {
|
144 | var v = it.value;
|
145 | var g = v.node;
|
146 | if (g instanceof go.Group && g.isSubGraphExpanded && g.placeholder !== null && g.layout instanceof ParallelLayout) {
|
147 | var split = g.layout.splitNode;
|
148 | if (split) {
|
149 | if (this.angle === 0) {
|
150 | v.focusY = split.location.y - g.position.y;
|
151 | } else if (this.angle === 90) {
|
152 | v.focusX = split.location.x - g.position.x;
|
153 | }
|
154 | }
|
155 | }
|
156 | }
|
157 | if (this.group && !this.group.isSubGraphExpanded) return net;
|
158 |
|
159 | this.findSplitMerge(net.vertexes.iterator as go.Iterator<go.TreeVertex>);
|
160 |
|
161 | if (this.mergeNode) net.deleteNode(this.mergeNode);
|
162 |
|
163 |
|
164 | if (this.splitNode) {
|
165 | var splitv = net.findVertex(this.splitNode);
|
166 | net.vertexes.each(function(v) {
|
167 | if (splitv === null || v === splitv) return;
|
168 | if (v.sourceEdges.count === 0) {
|
169 | net.linkVertexes(splitv, v, null);
|
170 | }
|
171 | });
|
172 | }
|
173 | return net;
|
174 | }
|
175 |
|
176 | |
177 |
|
178 |
|
179 | public commitNodes(): void {
|
180 | super.commitNodes();
|
181 |
|
182 | var mergeNode = this.mergeNode;
|
183 | var splitNode = this.splitNode;
|
184 | if (mergeNode === null || splitNode === null || this.network === null) return;
|
185 | var splitVertex = this.network.findVertex(splitNode) as go.TreeVertex;
|
186 | if (splitVertex === null) return;
|
187 | if (this.angle === 0) {
|
188 | mergeNode.location = new go.Point(splitVertex.x + splitVertex.subtreeSize.width + this.layerSpacing + mergeNode.actualBounds.width/2,
|
189 | splitVertex.centerY);
|
190 | } else if (this.angle === 90) {
|
191 | mergeNode.location = new go.Point(splitVertex.centerX,
|
192 | splitVertex.y + splitVertex.subtreeSize.height + this.layerSpacing + mergeNode.actualBounds.height/2);
|
193 | }
|
194 | mergeNode.ensureBounds();
|
195 | }
|
196 |
|
197 | |
198 |
|
199 |
|
200 | public commitLinks(): void {
|
201 | const splitNode = this.splitNode;
|
202 | const mergeNode = this.mergeNode;
|
203 | if (splitNode === null || mergeNode === null || this.network === null) return;
|
204 |
|
205 | const it = this.network.edges.iterator;
|
206 | while (it.next()) {
|
207 | const e = it.value;
|
208 | const link = e.link;
|
209 | if (!link) continue;
|
210 | if (this.angle === 0) {
|
211 | if (this.setsPortSpot) link.fromSpot = go.Spot.Right;
|
212 | if (this.setsChildPortSpot) link.toSpot = go.Spot.Left;
|
213 | } else if (this.angle === 90) {
|
214 | if (this.setsPortSpot) link.fromSpot = go.Spot.Bottom;
|
215 | if (this.setsChildPortSpot) link.toSpot = go.Spot.Top;
|
216 | }
|
217 | }
|
218 |
|
219 | if (splitNode) {
|
220 |
|
221 | const cond = this.isConditional(splitNode);
|
222 | const swtch = this.isSwitch(splitNode);
|
223 |
|
224 | let first = true;
|
225 | const lit = splitNode.findLinksOutOf();
|
226 | while (lit.next()) {
|
227 | const link = lit.value;
|
228 | if (this.angle === 0) {
|
229 | if (this.setsPortSpot) link.fromSpot = cond ? (first ? go.Spot.Top : go.Spot.Bottom) : (swtch ? go.Spot.RightSide : go.Spot.Right);
|
230 | if (this.setsChildPortSpot) link.toSpot = go.Spot.Left;
|
231 | } else if (this.angle === 90) {
|
232 | if (this.setsPortSpot) link.fromSpot = cond ? (first ? go.Spot.Left : go.Spot.Right) : (swtch ? go.Spot.BottomSide : go.Spot.Bottom);
|
233 | if (this.setsChildPortSpot) link.toSpot = go.Spot.Top;
|
234 | }
|
235 | first = false;
|
236 | }
|
237 | }
|
238 | if (mergeNode) {
|
239 |
|
240 | const iit = mergeNode.findLinksInto();
|
241 | while (iit.next()) {
|
242 | const link = iit.value;
|
243 | if (!this.isSplit(link.fromNode)) {
|
244 | if (this.angle === 0) {
|
245 | if (this.setsPortSpot) link.fromSpot = go.Spot.Right;
|
246 | if (this.setsChildPortSpot) link.toSpot = go.Spot.Left;
|
247 | } else if (this.angle === 90) {
|
248 | if (this.setsPortSpot) link.fromSpot = go.Spot.Bottom;
|
249 | if (this.setsChildPortSpot) link.toSpot = go.Spot.Top;
|
250 | }
|
251 | }
|
252 | if (!link.isOrthogonal) continue;
|
253 |
|
254 |
|
255 | link.updateRoute();
|
256 | if (link.pointsCount >= 6) {
|
257 | const pts = link.points.copy();
|
258 | const p2 = pts.elt(pts.length - 4);
|
259 | const p3 = pts.elt(pts.length - 3);
|
260 | if (this.angle === 0 && p2.x === p3.x) {
|
261 | const x = mergeNode.position.x - this.layerSpacing / 2;
|
262 | pts.setElt(pts.length - 4, new go.Point(x, p2.y));
|
263 | pts.setElt(pts.length - 3, new go.Point(x, p3.y));
|
264 | } else if (this.angle === 90 && p2.y === p3.y) {
|
265 | const y = mergeNode.position.y - this.layerSpacing / 2;
|
266 | pts.setElt(pts.length - 4, new go.Point(p2.x, y));
|
267 | pts.setElt(pts.length - 3, new go.Point(p3.x, y));
|
268 | }
|
269 | link.points = pts;
|
270 | }
|
271 | }
|
272 |
|
273 | const oit = mergeNode.findLinksOutOf();
|
274 | while (oit.next()) {
|
275 | const link = oit.value;
|
276 |
|
277 | if (link.toNode && link.toNode.containingGroup !== mergeNode.containingGroup) continue;
|
278 | if (this.angle === 0) {
|
279 | if (this.setsPortSpot) link.fromSpot = go.Spot.TopBottomSides;
|
280 | if (this.setsChildPortSpot) link.toSpot = go.Spot.TopBottomSides;
|
281 | } else if (this.angle === 90) {
|
282 | if (this.setsPortSpot) link.fromSpot = go.Spot.LeftRightSides;
|
283 | if (this.setsChildPortSpot) link.toSpot = go.Spot.LeftRightSides;
|
284 | }
|
285 | link.routing = go.Link.AvoidsNodes;
|
286 | }
|
287 | }
|
288 | }
|
289 | }
|