@dagrejs/dagre 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/dagre.js +16 -5
  2. package/dist/dagre.min.js +4 -4
  3. package/lib/acyclic.js +18 -10
  4. package/lib/add-border-segments.js +11 -19
  5. package/lib/coordinate-system.js +15 -5
  6. package/lib/data/list.js +7 -8
  7. package/lib/debug.js +14 -25
  8. package/lib/greedy-fas.js +30 -35
  9. package/lib/layout.js +102 -105
  10. package/lib/nesting-graph.js +21 -18
  11. package/lib/normalize.js +18 -22
  12. package/lib/order/add-subgraph-constraints.js +2 -6
  13. package/lib/order/barycenter.js +6 -14
  14. package/lib/order/build-layer-graph.js +13 -19
  15. package/lib/order/cross-count.js +10 -13
  16. package/lib/order/index.js +23 -23
  17. package/lib/order/init-order.js +7 -8
  18. package/lib/order/resolve-conflicts.js +19 -9
  19. package/lib/order/sort-subgraph.js +22 -16
  20. package/lib/order/sort.js +12 -13
  21. package/lib/parent-dummy-chains.js +19 -17
  22. package/lib/position/bk.js +84 -40
  23. package/lib/position/index.js +9 -10
  24. package/lib/rank/feasible-tree.js +17 -14
  25. package/lib/rank/index.js +15 -25
  26. package/lib/rank/network-simplex.js +39 -18
  27. package/lib/rank/util.js +12 -6
  28. package/lib/util.js +57 -42
  29. package/lib/version.js +1 -8
  30. package/package.json +3 -15
  31. package/lib/index.js +0 -38
  32. package/mjs-lib/acyclic.js +0 -62
  33. package/mjs-lib/add-border-segments.js +0 -35
  34. package/mjs-lib/coordinate-system.js +0 -65
  35. package/mjs-lib/data/list.js +0 -56
  36. package/mjs-lib/debug.js +0 -30
  37. package/mjs-lib/greedy-fas.js +0 -125
  38. package/mjs-lib/index.js +0 -9
  39. package/mjs-lib/layout.js +0 -405
  40. package/mjs-lib/nesting-graph.js +0 -120
  41. package/mjs-lib/normalize.js +0 -84
  42. package/mjs-lib/order/add-subgraph-constraints.js +0 -49
  43. package/mjs-lib/order/barycenter.js +0 -24
  44. package/mjs-lib/order/build-layer-graph.js +0 -71
  45. package/mjs-lib/order/cross-count.js +0 -64
  46. package/mjs-lib/order/index.js +0 -70
  47. package/mjs-lib/order/init-order.js +0 -34
  48. package/mjs-lib/order/resolve-conflicts.js +0 -116
  49. package/mjs-lib/order/sort-subgraph.js +0 -71
  50. package/mjs-lib/order/sort.js +0 -54
  51. package/mjs-lib/parent-dummy-chains.js +0 -82
  52. package/mjs-lib/position/bk.js +0 -409
  53. package/mjs-lib/position/index.js +0 -30
  54. package/mjs-lib/rank/feasible-tree.js +0 -93
  55. package/mjs-lib/rank/index.js +0 -46
  56. package/mjs-lib/rank/network-simplex.js +0 -233
  57. package/mjs-lib/rank/util.js +0 -58
  58. package/mjs-lib/util.js +0 -305
  59. package/mjs-lib/version.js +0 -1
@@ -1,65 +0,0 @@
1
- "use strict";
2
-
3
- export function adjust(g) {
4
- let rankDir = g.graph().rankdir.toLowerCase();
5
- if (rankDir === "lr" || rankDir === "rl") {
6
- swapWidthHeight(g);
7
- }
8
- }
9
-
10
- export function undo(g) {
11
- let rankDir = g.graph().rankdir.toLowerCase();
12
- if (rankDir === "bt" || rankDir === "rl") {
13
- reverseY(g);
14
- }
15
-
16
- if (rankDir === "lr" || rankDir === "rl") {
17
- swapXY(g);
18
- swapWidthHeight(g);
19
- }
20
- }
21
-
22
- function swapWidthHeight(g) {
23
- g.nodes().forEach(v => swapWidthHeightOne(g.node(v)));
24
- g.edges().forEach(e => swapWidthHeightOne(g.edge(e)));
25
- }
26
-
27
- function swapWidthHeightOne(attrs) {
28
- let w = attrs.width;
29
- attrs.width = attrs.height;
30
- attrs.height = w;
31
- }
32
-
33
- function reverseY(g) {
34
- g.nodes().forEach(v => reverseYOne(g.node(v)));
35
-
36
- g.edges().forEach(e => {
37
- let edge = g.edge(e);
38
- edge.points.forEach(reverseYOne);
39
- if (edge.hasOwnProperty("y")) {
40
- reverseYOne(edge);
41
- }
42
- });
43
- }
44
-
45
- function reverseYOne(attrs) {
46
- attrs.y = -attrs.y;
47
- }
48
-
49
- function swapXY(g) {
50
- g.nodes().forEach(v => swapXYOne(g.node(v)));
51
-
52
- g.edges().forEach(e => {
53
- let edge = g.edge(e);
54
- edge.points.forEach(swapXYOne);
55
- if (edge.hasOwnProperty("x")) {
56
- swapXYOne(edge);
57
- }
58
- });
59
- }
60
-
61
- function swapXYOne(attrs) {
62
- let x = attrs.x;
63
- attrs.x = attrs.y;
64
- attrs.y = x;
65
- }
@@ -1,56 +0,0 @@
1
- /*
2
- * Simple doubly linked list implementation derived from Cormen, et al.,
3
- * "Introduction to Algorithms".
4
- */
5
-
6
- export default class List {
7
- constructor() {
8
- let sentinel = {};
9
- sentinel._next = sentinel._prev = sentinel;
10
- this._sentinel = sentinel;
11
- }
12
-
13
- dequeue() {
14
- let sentinel = this._sentinel;
15
- let entry = sentinel._prev;
16
- if (entry !== sentinel) {
17
- unlink(entry);
18
- return entry;
19
- }
20
- }
21
-
22
- enqueue(entry) {
23
- let sentinel = this._sentinel;
24
- if (entry._prev && entry._next) {
25
- unlink(entry);
26
- }
27
- entry._next = sentinel._next;
28
- sentinel._next._prev = entry;
29
- sentinel._next = entry;
30
- entry._prev = sentinel;
31
- }
32
-
33
- toString() {
34
- let strs = [];
35
- let sentinel = this._sentinel;
36
- let curr = sentinel._prev;
37
- while (curr !== sentinel) {
38
- strs.push(JSON.stringify(curr, filterOutLinks));
39
- curr = curr._prev;
40
- }
41
- return "[" + strs.join(", ") + "]";
42
- }
43
- }
44
-
45
- function unlink(entry) {
46
- entry._prev._next = entry._next;
47
- entry._next._prev = entry._prev;
48
- delete entry._next;
49
- delete entry._prev;
50
- }
51
-
52
- function filterOutLinks(k, v) {
53
- if (k !== "_next" && k !== "_prev") {
54
- return v;
55
- }
56
- }
package/mjs-lib/debug.js DELETED
@@ -1,30 +0,0 @@
1
- import * as util from "./util.js";
2
-
3
- // web-devs write a <script type="importmap"> to map
4
- // nodejs paths to actual http://paths
5
- import { Graph as Graph } from "@dagrejs/graphlib";
6
-
7
- /* istanbul ignore next */
8
- export default function debugOrdering(g) {
9
- let layerMatrix = util.buildLayerMatrix(g);
10
-
11
- let h = new Graph({ compound: true, multigraph: true }).setGraph({});
12
-
13
- g.nodes().forEach(v => {
14
- h.setNode(v, { label: v });
15
- h.setParent(v, "layer" + g.node(v).rank);
16
- });
17
-
18
- g.edges().forEach(e => h.setEdge(e.v, e.w, {}, e.name));
19
-
20
- layerMatrix.forEach((layer, i) => {
21
- let layerV = "layer" + i;
22
- h.setNode(layerV, { rank: "same" });
23
- layer.reduce((u, v) => {
24
- h.setEdge(u, v, { style: "invis" });
25
- return v;
26
- });
27
- });
28
-
29
- return h;
30
- }
@@ -1,125 +0,0 @@
1
- // web-devs write a <script type="importmap"> to map
2
- // nodejs paths to actual http://paths
3
- import { Graph as Graph } from "@dagrejs/graphlib";
4
- import { default as List } from "./data/list.js";
5
-
6
- /*
7
- * A greedy heuristic for finding a feedback arc set for a graph. A feedback
8
- * arc set is a set of edges that can be removed to make a graph acyclic.
9
- * The algorithm comes from: P. Eades, X. Lin, and W. F. Smyth, "A fast and
10
- * effective heuristic for the feedback arc set problem." This implementation
11
- * adjusts that from the paper to allow for weighted edges.
12
- */
13
-
14
- let DEFAULT_WEIGHT_FN = () => 1;
15
-
16
- export default function greedyFAS(g, weightFn) {
17
- if (g.nodeCount() <= 1) {
18
- return [];
19
- }
20
- let state = buildState(g, weightFn || DEFAULT_WEIGHT_FN);
21
- let results = doGreedyFAS(state.graph, state.buckets, state.zeroIdx);
22
-
23
- // Expand multi-edges
24
- return results.flatMap(e => g.outEdges(e.v, e.w));
25
- }
26
-
27
- function doGreedyFAS(g, buckets, zeroIdx) {
28
- let results = [];
29
- let sources = buckets[buckets.length - 1];
30
- let sinks = buckets[0];
31
-
32
- let entry;
33
- while (g.nodeCount()) {
34
- while ((entry = sinks.dequeue())) { removeNode(g, buckets, zeroIdx, entry); }
35
- while ((entry = sources.dequeue())) { removeNode(g, buckets, zeroIdx, entry); }
36
- if (g.nodeCount()) {
37
- for (let i = buckets.length - 2; i > 0; --i) {
38
- entry = buckets[i].dequeue();
39
- if (entry) {
40
- results = results.concat(removeNode(g, buckets, zeroIdx, entry, true));
41
- break;
42
- }
43
- }
44
- }
45
- }
46
-
47
- return results;
48
- }
49
-
50
- function removeNode(g, buckets, zeroIdx, entry, collectPredecessors) {
51
- let results = collectPredecessors ? [] : undefined;
52
-
53
- g.inEdges(entry.v).forEach(edge => {
54
- let weight = g.edge(edge);
55
- let uEntry = g.node(edge.v);
56
-
57
- if (collectPredecessors) {
58
- results.push({ v: edge.v, w: edge.w });
59
- }
60
-
61
- uEntry.out -= weight;
62
- assignBucket(buckets, zeroIdx, uEntry);
63
- });
64
-
65
- g.outEdges(entry.v).forEach(edge => {
66
- let weight = g.edge(edge);
67
- let w = edge.w;
68
- let wEntry = g.node(w);
69
- wEntry["in"] -= weight;
70
- assignBucket(buckets, zeroIdx, wEntry);
71
- });
72
-
73
- g.removeNode(entry.v);
74
-
75
- return results;
76
- }
77
-
78
- function buildState(g, weightFn) {
79
- let fasGraph = new Graph();
80
- let maxIn = 0;
81
- let maxOut = 0;
82
-
83
- g.nodes().forEach(v => {
84
- fasGraph.setNode(v, { v: v, "in": 0, out: 0 });
85
- });
86
-
87
- // Aggregate weights on nodes, but also sum the weights across multi-edges
88
- // into a single edge for the fasGraph.
89
- g.edges().forEach(e => {
90
- let prevWeight = fasGraph.edge(e.v, e.w) || 0;
91
- let weight = weightFn(e);
92
- let edgeWeight = prevWeight + weight;
93
- fasGraph.setEdge(e.v, e.w, edgeWeight);
94
- maxOut = Math.max(maxOut, fasGraph.node(e.v).out += weight);
95
- maxIn = Math.max(maxIn, fasGraph.node(e.w)["in"] += weight);
96
- });
97
-
98
- let buckets = range(maxOut + maxIn + 3).map(() => new List());
99
- let zeroIdx = maxIn + 1;
100
-
101
- fasGraph.nodes().forEach(v => {
102
- assignBucket(buckets, zeroIdx, fasGraph.node(v));
103
- });
104
-
105
- return { graph: fasGraph, buckets: buckets, zeroIdx: zeroIdx };
106
- }
107
-
108
- function assignBucket(buckets, zeroIdx, entry) {
109
- if (!entry.out) {
110
- buckets[0].enqueue(entry);
111
- } else if (!entry["in"]) {
112
- buckets[buckets.length - 1].enqueue(entry);
113
- } else {
114
- buckets[entry.out - entry["in"] + zeroIdx].enqueue(entry);
115
- }
116
- }
117
-
118
- function range(limit) {
119
- const range = [];
120
- for (let i = 0; i < limit; i++) {
121
- range.push(i);
122
- }
123
-
124
- return range;
125
- }
package/mjs-lib/index.js DELETED
@@ -1,9 +0,0 @@
1
- export * as graphlib from "@dagrejs/graphlib";
2
- export { default as layout } from "./layout.js";
3
- export { default as debug } from "./debug.js";
4
- import { time, notime } from "./util.js";
5
- export const util = {
6
- time: time,
7
- notime: notime
8
- };
9
- export { default as version } from "./version.js";
package/mjs-lib/layout.js DELETED
@@ -1,405 +0,0 @@
1
- "use strict";
2
-
3
- import * as acyclic from "./acyclic.js";
4
- import * as normalize from "./normalize.js";
5
- import { default as rank } from "./rank/index.js";
6
- import { default as parentDummyChains } from "./parent-dummy-chains.js";
7
- import * as nestingGraph from "./nesting-graph.js";
8
- import { default as addBorderSegments } from "./add-border-segments.js";
9
- import * as coordinateSystem from "./coordinate-system.js";
10
- import { default as order } from "./order/index.js";
11
- import { default as position } from "./position/index.js";
12
- import * as util from "./util.js";
13
- const removeEmptyRanks = util.removeEmptyRanks;
14
- const normalizeRanks = util.normalizeRanks;
15
- import { Graph as Graph } from "@dagrejs/graphlib";
16
-
17
-
18
-
19
- export default function layout(g, opts) {
20
- let time = opts && opts.debugTiming ? util.time : util.notime;
21
- time("layout", () => {
22
- let layoutGraph =
23
- time(" buildLayoutGraph", () => buildLayoutGraph(g));
24
- time(" runLayout", () => runLayout(layoutGraph, time));
25
- time(" updateInputGraph", () => updateInputGraph(g, layoutGraph));
26
- });
27
- }
28
-
29
- function runLayout(g, time) {
30
- time(" makeSpaceForEdgeLabels", () => makeSpaceForEdgeLabels(g));
31
- time(" removeSelfEdges", () => removeSelfEdges(g));
32
- time(" acyclic", () => acyclic.run(g));
33
- time(" nestingGraph.run", () => nestingGraph.run(g));
34
- time(" rank", () => rank(util.asNonCompoundGraph(g)));
35
- time(" injectEdgeLabelProxies", () => injectEdgeLabelProxies(g));
36
- time(" removeEmptyRanks", () => removeEmptyRanks(g));
37
- time(" nestingGraph.cleanup", () => nestingGraph.cleanup(g));
38
- time(" normalizeRanks", () => normalizeRanks(g));
39
- time(" assignRankMinMax", () => assignRankMinMax(g));
40
- time(" removeEdgeLabelProxies", () => removeEdgeLabelProxies(g));
41
- time(" normalize.run", () => normalize.run(g));
42
- time(" parentDummyChains", () => parentDummyChains(g));
43
- time(" addBorderSegments", () => addBorderSegments(g));
44
- time(" order", () => order(g));
45
- time(" insertSelfEdges", () => insertSelfEdges(g));
46
- time(" adjustCoordinateSystem", () => coordinateSystem.adjust(g));
47
- time(" position", () => position(g));
48
- time(" positionSelfEdges", () => positionSelfEdges(g));
49
- time(" removeBorderNodes", () => removeBorderNodes(g));
50
- time(" normalize.undo", () => normalize.undo(g));
51
- time(" fixupEdgeLabelCoords", () => fixupEdgeLabelCoords(g));
52
- time(" undoCoordinateSystem", () => coordinateSystem.undo(g));
53
- time(" translateGraph", () => translateGraph(g));
54
- time(" assignNodeIntersects", () => assignNodeIntersects(g));
55
- time(" reversePoints", () => reversePointsForReversedEdges(g));
56
- time(" acyclic.undo", () => acyclic.undo(g));
57
- }
58
-
59
- /*
60
- * Copies final layout information from the layout graph back to the input
61
- * graph. This process only copies whitelisted attributes from the layout graph
62
- * to the input graph, so it serves as a good place to determine what
63
- * attributes can influence layout.
64
- */
65
- function updateInputGraph(inputGraph, layoutGraph) {
66
- inputGraph.nodes().forEach(v => {
67
- let inputLabel = inputGraph.node(v);
68
- let layoutLabel = layoutGraph.node(v);
69
-
70
- if (inputLabel) {
71
- inputLabel.x = layoutLabel.x;
72
- inputLabel.y = layoutLabel.y;
73
- inputLabel.rank = layoutLabel.rank;
74
-
75
- if (layoutGraph.children(v).length) {
76
- inputLabel.width = layoutLabel.width;
77
- inputLabel.height = layoutLabel.height;
78
- }
79
- }
80
- });
81
-
82
- inputGraph.edges().forEach(e => {
83
- let inputLabel = inputGraph.edge(e);
84
- let layoutLabel = layoutGraph.edge(e);
85
-
86
- inputLabel.points = layoutLabel.points;
87
- if (layoutLabel.hasOwnProperty("x")) {
88
- inputLabel.x = layoutLabel.x;
89
- inputLabel.y = layoutLabel.y;
90
- }
91
- });
92
-
93
- inputGraph.graph().width = layoutGraph.graph().width;
94
- inputGraph.graph().height = layoutGraph.graph().height;
95
- }
96
-
97
- let graphNumAttrs = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"];
98
- let graphDefaults = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "tb" };
99
- let graphAttrs = ["acyclicer", "ranker", "rankdir", "align"];
100
- let nodeNumAttrs = ["width", "height"];
101
- let nodeDefaults = { width: 0, height: 0 };
102
- let edgeNumAttrs = ["minlen", "weight", "width", "height", "labeloffset"];
103
- let edgeDefaults = {
104
- minlen: 1, weight: 1, width: 0, height: 0,
105
- labeloffset: 10, labelpos: "r"
106
- };
107
- let edgeAttrs = ["labelpos"];
108
-
109
- /*
110
- * Constructs a new graph from the input graph, which can be used for layout.
111
- * This process copies only whitelisted attributes from the input graph to the
112
- * layout graph. Thus this function serves as a good place to determine what
113
- * attributes can influence layout.
114
- */
115
- function buildLayoutGraph(inputGraph) {
116
- let g = new Graph({ multigraph: true, compound: true });
117
- let graph = canonicalize(inputGraph.graph());
118
-
119
- g.setGraph(Object.assign({},
120
- graphDefaults,
121
- selectNumberAttrs(graph, graphNumAttrs),
122
- util.pick(graph, graphAttrs)));
123
-
124
- inputGraph.nodes().forEach(v => {
125
- let node = canonicalize(inputGraph.node(v));
126
- const newNode = selectNumberAttrs(node, nodeNumAttrs);
127
- Object.keys(nodeDefaults).forEach(k => {
128
- if (newNode[k] === undefined) {
129
- newNode[k] = nodeDefaults[k];
130
- }
131
- });
132
-
133
- g.setNode(v, newNode);
134
- g.setParent(v, inputGraph.parent(v));
135
- });
136
-
137
- inputGraph.edges().forEach(e => {
138
- let edge = canonicalize(inputGraph.edge(e));
139
- g.setEdge(e, Object.assign({},
140
- edgeDefaults,
141
- selectNumberAttrs(edge, edgeNumAttrs),
142
- util.pick(edge, edgeAttrs)));
143
- });
144
-
145
- return g;
146
- }
147
-
148
- /*
149
- * This idea comes from the Gansner paper: to account for edge labels in our
150
- * layout we split each rank in half by doubling minlen and halving ranksep.
151
- * Then we can place labels at these mid-points between nodes.
152
- *
153
- * We also add some minimal padding to the width to push the label for the edge
154
- * away from the edge itself a bit.
155
- */
156
- function makeSpaceForEdgeLabels(g) {
157
- let graph = g.graph();
158
- graph.ranksep /= 2;
159
- g.edges().forEach(e => {
160
- let edge = g.edge(e);
161
- edge.minlen *= 2;
162
- if (edge.labelpos.toLowerCase() !== "c") {
163
- if (graph.rankdir === "TB" || graph.rankdir === "BT") {
164
- edge.width += edge.labeloffset;
165
- } else {
166
- edge.height += edge.labeloffset;
167
- }
168
- }
169
- });
170
- }
171
-
172
- /*
173
- * Creates temporary dummy nodes that capture the rank in which each edge's
174
- * label is going to, if it has one of non-zero width and height. We do this
175
- * so that we can safely remove empty ranks while preserving balance for the
176
- * label's position.
177
- */
178
- function injectEdgeLabelProxies(g) {
179
- g.edges().forEach(e => {
180
- let edge = g.edge(e);
181
- if (edge.width && edge.height) {
182
- let v = g.node(e.v);
183
- let w = g.node(e.w);
184
- let label = { rank: (w.rank - v.rank) / 2 + v.rank, e: e };
185
- util.addDummyNode(g, "edge-proxy", label, "_ep");
186
- }
187
- });
188
- }
189
-
190
- function assignRankMinMax(g) {
191
- let maxRank = 0;
192
- g.nodes().forEach(v => {
193
- let node = g.node(v);
194
- if (node.borderTop) {
195
- node.minRank = g.node(node.borderTop).rank;
196
- node.maxRank = g.node(node.borderBottom).rank;
197
- maxRank = Math.max(maxRank, node.maxRank);
198
- }
199
- });
200
- g.graph().maxRank = maxRank;
201
- }
202
-
203
- function removeEdgeLabelProxies(g) {
204
- g.nodes().forEach(v => {
205
- let node = g.node(v);
206
- if (node.dummy === "edge-proxy") {
207
- g.edge(node.e).labelRank = node.rank;
208
- g.removeNode(v);
209
- }
210
- });
211
- }
212
-
213
- function translateGraph(g) {
214
- let minX = Number.POSITIVE_INFINITY;
215
- let maxX = 0;
216
- let minY = Number.POSITIVE_INFINITY;
217
- let maxY = 0;
218
- let graphLabel = g.graph();
219
- let marginX = graphLabel.marginx || 0;
220
- let marginY = graphLabel.marginy || 0;
221
-
222
- function getExtremes(attrs) {
223
- let x = attrs.x;
224
- let y = attrs.y;
225
- let w = attrs.width;
226
- let h = attrs.height;
227
- minX = Math.min(minX, x - w / 2);
228
- maxX = Math.max(maxX, x + w / 2);
229
- minY = Math.min(minY, y - h / 2);
230
- maxY = Math.max(maxY, y + h / 2);
231
- }
232
-
233
- g.nodes().forEach(v => getExtremes(g.node(v)));
234
- g.edges().forEach(e => {
235
- let edge = g.edge(e);
236
- if (edge.hasOwnProperty("x")) {
237
- getExtremes(edge);
238
- }
239
- });
240
-
241
- minX -= marginX;
242
- minY -= marginY;
243
-
244
- g.nodes().forEach(v => {
245
- let node = g.node(v);
246
- node.x -= minX;
247
- node.y -= minY;
248
- });
249
-
250
- g.edges().forEach(e => {
251
- let edge = g.edge(e);
252
- edge.points.forEach(p => {
253
- p.x -= minX;
254
- p.y -= minY;
255
- });
256
- if (edge.hasOwnProperty("x")) { edge.x -= minX; }
257
- if (edge.hasOwnProperty("y")) { edge.y -= minY; }
258
- });
259
-
260
- graphLabel.width = maxX - minX + marginX;
261
- graphLabel.height = maxY - minY + marginY;
262
- }
263
-
264
- function assignNodeIntersects(g) {
265
- g.edges().forEach(e => {
266
- let edge = g.edge(e);
267
- let nodeV = g.node(e.v);
268
- let nodeW = g.node(e.w);
269
- let p1, p2;
270
- if (!edge.points) {
271
- edge.points = [];
272
- p1 = nodeW;
273
- p2 = nodeV;
274
- } else {
275
- p1 = edge.points[0];
276
- p2 = edge.points[edge.points.length - 1];
277
- }
278
- edge.points.unshift(util.intersectRect(nodeV, p1));
279
- edge.points.push(util.intersectRect(nodeW, p2));
280
- });
281
- }
282
-
283
- function fixupEdgeLabelCoords(g) {
284
- g.edges().forEach(e => {
285
- let edge = g.edge(e);
286
- if (edge.hasOwnProperty("x")) {
287
- if (edge.labelpos === "l" || edge.labelpos === "r") {
288
- edge.width -= edge.labeloffset;
289
- }
290
- switch (edge.labelpos) {
291
- case "l": edge.x -= edge.width / 2 + edge.labeloffset; break;
292
- case "r": edge.x += edge.width / 2 + edge.labeloffset; break;
293
- }
294
- }
295
- });
296
- }
297
-
298
- function reversePointsForReversedEdges(g) {
299
- g.edges().forEach(e => {
300
- let edge = g.edge(e);
301
- if (edge.reversed) {
302
- edge.points.reverse();
303
- }
304
- });
305
- }
306
-
307
- function removeBorderNodes(g) {
308
- g.nodes().forEach(v => {
309
- if (g.children(v).length) {
310
- let node = g.node(v);
311
- let t = g.node(node.borderTop);
312
- let b = g.node(node.borderBottom);
313
- let l = g.node(node.borderLeft[node.borderLeft.length - 1]);
314
- let r = g.node(node.borderRight[node.borderRight.length - 1]);
315
-
316
- node.width = Math.abs(r.x - l.x);
317
- node.height = Math.abs(b.y - t.y);
318
- node.x = l.x + node.width / 2;
319
- node.y = t.y + node.height / 2;
320
- }
321
- });
322
-
323
- g.nodes().forEach(v => {
324
- if (g.node(v).dummy === "border") {
325
- g.removeNode(v);
326
- }
327
- });
328
- }
329
-
330
- function removeSelfEdges(g) {
331
- g.edges().forEach(e => {
332
- if (e.v === e.w) {
333
- var node = g.node(e.v);
334
- if (!node.selfEdges) {
335
- node.selfEdges = [];
336
- }
337
- node.selfEdges.push({ e: e, label: g.edge(e) });
338
- g.removeEdge(e);
339
- }
340
- });
341
- }
342
-
343
- function insertSelfEdges(g) {
344
- var layers = util.buildLayerMatrix(g);
345
- layers.forEach(layer => {
346
- var orderShift = 0;
347
- layer.forEach((v, i) => {
348
- var node = g.node(v);
349
- node.order = i + orderShift;
350
- (node.selfEdges || []).forEach(selfEdge => {
351
- util.addDummyNode(g, "selfedge", {
352
- width: selfEdge.label.width,
353
- height: selfEdge.label.height,
354
- rank: node.rank,
355
- order: i + (++orderShift),
356
- e: selfEdge.e,
357
- label: selfEdge.label
358
- }, "_se");
359
- });
360
- delete node.selfEdges;
361
- });
362
- });
363
- }
364
-
365
- function positionSelfEdges(g) {
366
- g.nodes().forEach(v => {
367
- var node = g.node(v);
368
- if (node.dummy === "selfedge") {
369
- var selfNode = g.node(node.e.v);
370
- var x = selfNode.x + selfNode.width / 2;
371
- var y = selfNode.y;
372
- var dx = node.x - x;
373
- var dy = selfNode.height / 2;
374
- g.setEdge(node.e, node.label);
375
- g.removeNode(v);
376
- node.label.points = [
377
- { x: x + 2 * dx / 3, y: y - dy },
378
- { x: x + 5 * dx / 6, y: y - dy },
379
- { x: x + dx , y: y },
380
- { x: x + 5 * dx / 6, y: y + dy },
381
- { x: x + 2 * dx / 3, y: y + dy }
382
- ];
383
- node.label.x = node.x;
384
- node.label.y = node.y;
385
- }
386
- });
387
- }
388
-
389
- function selectNumberAttrs(obj, attrs) {
390
- return util.mapValues(util.pick(obj, attrs), Number);
391
- }
392
-
393
- function canonicalize(attrs) {
394
- var newAttrs = {};
395
- if (attrs) {
396
- Object.entries(attrs).forEach(([k, v]) => {
397
- if (typeof k === "string") {
398
- k = k.toLowerCase();
399
- }
400
-
401
- newAttrs[k] = v;
402
- });
403
- }
404
- return newAttrs;
405
- }