@antv/layout 0.2.0 → 0.2.3
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.
- package/dist/layout.min.js +1 -1
- package/dist/layout.min.js.LICENSE.txt +0 -9
- package/dist/layout.min.js.map +1 -1
- package/es/layout/circular.js.map +1 -1
- package/es/layout/comboCombined.js +3 -19
- package/es/layout/comboCombined.js.map +1 -1
- package/es/layout/concentric.js.map +1 -1
- package/es/layout/dagre/graph.d.ts +91 -0
- package/es/layout/dagre/graph.js +4 -0
- package/es/layout/dagre/graph.js.map +1 -0
- package/es/layout/dagre/index.d.ts +3 -4
- package/es/layout/dagre/index.js +0 -2
- package/es/layout/dagre/index.js.map +1 -1
- package/es/layout/dagre/src/acyclic.d.ts +1 -2
- package/es/layout/dagre/src/acyclic.js +7 -7
- package/es/layout/dagre/src/acyclic.js.map +1 -1
- package/es/layout/dagre/src/add-border-segments.d.ts +1 -2
- package/es/layout/dagre/src/add-border-segments.js +5 -5
- package/es/layout/dagre/src/add-border-segments.js.map +1 -1
- package/es/layout/dagre/src/coordinate-system.d.ts +1 -2
- package/es/layout/dagre/src/coordinate-system.js +15 -5
- package/es/layout/dagre/src/coordinate-system.js.map +1 -1
- package/es/layout/dagre/src/data/list.d.ts +9 -5
- package/es/layout/dagre/src/data/list.js +25 -27
- package/es/layout/dagre/src/data/list.js.map +1 -1
- package/es/layout/dagre/src/debug.d.ts +2 -3
- package/es/layout/dagre/src/debug.js +3 -5
- package/es/layout/dagre/src/debug.js.map +1 -1
- package/es/layout/dagre/src/greedy-fas.d.ts +2 -3
- package/es/layout/dagre/src/greedy-fas.js +16 -15
- package/es/layout/dagre/src/greedy-fas.js.map +1 -1
- package/es/layout/dagre/src/layout.d.ts +2 -3
- package/es/layout/dagre/src/layout.js +170 -91
- package/es/layout/dagre/src/layout.js.map +1 -1
- package/es/layout/dagre/src/nesting-graph.d.ts +1 -2
- package/es/layout/dagre/src/nesting-graph.js +15 -10
- package/es/layout/dagre/src/nesting-graph.js.map +1 -1
- package/es/layout/dagre/src/normalize.d.ts +1 -2
- package/es/layout/dagre/src/normalize.js +12 -11
- package/es/layout/dagre/src/normalize.js.map +1 -1
- package/es/layout/dagre/src/order/add-subgraph-constraints.d.ts +1 -2
- package/es/layout/dagre/src/order/add-subgraph-constraints.js.map +1 -1
- package/es/layout/dagre/src/order/barycenter.d.ts +1 -2
- package/es/layout/dagre/src/order/barycenter.js.map +1 -1
- package/es/layout/dagre/src/order/build-layer-graph.d.ts +2 -3
- package/es/layout/dagre/src/order/build-layer-graph.js +12 -8
- package/es/layout/dagre/src/order/build-layer-graph.js.map +1 -1
- package/es/layout/dagre/src/order/cross-count.d.ts +2 -3
- package/es/layout/dagre/src/order/cross-count.js +13 -12
- package/es/layout/dagre/src/order/cross-count.js.map +1 -1
- package/es/layout/dagre/src/order/index.d.ts +2 -3
- package/es/layout/dagre/src/order/index.js +17 -15
- package/es/layout/dagre/src/order/index.js.map +1 -1
- package/es/layout/dagre/src/order/init-data-order.d.ts +1 -2
- package/es/layout/dagre/src/order/init-data-order.js +3 -5
- package/es/layout/dagre/src/order/init-data-order.js.map +1 -1
- package/es/layout/dagre/src/order/init-order.d.ts +2 -3
- package/es/layout/dagre/src/order/init-order.js +1 -2
- package/es/layout/dagre/src/order/init-order.js.map +1 -1
- package/es/layout/dagre/src/order/resolve-conflicts.d.ts +18 -3
- package/es/layout/dagre/src/order/resolve-conflicts.js +9 -29
- package/es/layout/dagre/src/order/resolve-conflicts.js.map +1 -1
- package/es/layout/dagre/src/order/sort-subgraph.d.ts +6 -3
- package/es/layout/dagre/src/order/sort-subgraph.js +19 -14
- package/es/layout/dagre/src/order/sort-subgraph.js.map +1 -1
- package/es/layout/dagre/src/order/sort.d.ts +6 -1
- package/es/layout/dagre/src/order/sort.js +2 -2
- package/es/layout/dagre/src/order/sort.js.map +1 -1
- package/es/layout/dagre/src/parent-dummy-chains.d.ts +1 -2
- package/es/layout/dagre/src/parent-dummy-chains.js +47 -44
- package/es/layout/dagre/src/parent-dummy-chains.js.map +1 -1
- package/es/layout/dagre/src/position/bk.d.ts +22 -31
- package/es/layout/dagre/src/position/bk.js +49 -83
- package/es/layout/dagre/src/position/bk.js.map +1 -1
- package/es/layout/dagre/src/position/index.d.ts +1 -2
- package/es/layout/dagre/src/position/index.js +12 -14
- package/es/layout/dagre/src/position/index.js.map +1 -1
- package/es/layout/dagre/src/rank/feasible-tree.d.ts +5 -6
- package/es/layout/dagre/src/rank/feasible-tree.js +1 -2
- package/es/layout/dagre/src/rank/feasible-tree.js.map +1 -1
- package/es/layout/dagre/src/rank/index.d.ts +2 -3
- package/es/layout/dagre/src/rank/index.js.map +1 -1
- package/es/layout/dagre/src/rank/network-simplex.d.ts +8 -11
- package/es/layout/dagre/src/rank/network-simplex.js +18 -31
- package/es/layout/dagre/src/rank/network-simplex.js.map +1 -1
- package/es/layout/dagre/src/rank/util.d.ts +4 -5
- package/es/layout/dagre/src/rank/util.js +37 -20
- package/es/layout/dagre/src/rank/util.js.map +1 -1
- package/es/layout/dagre/src/util.d.ts +29 -48
- package/es/layout/dagre/src/util.js +91 -101
- package/es/layout/dagre/src/util.js.map +1 -1
- package/es/layout/dagre.d.ts +1 -1
- package/es/layout/dagre.js +28 -24
- package/es/layout/dagre.js.map +1 -1
- package/es/layout/fruchterman.js.map +1 -1
- package/es/layout/gForce.js +14 -6
- package/es/layout/gForce.js.map +1 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/layout/circular.js.map +1 -1
- package/lib/layout/comboCombined.js +2 -18
- package/lib/layout/comboCombined.js.map +1 -1
- package/lib/layout/comboForce.js +5 -5
- package/lib/layout/comboForce.js.map +1 -1
- package/lib/layout/concentric.js.map +1 -1
- package/lib/layout/dagre/graph.d.ts +91 -0
- package/lib/layout/dagre/graph.js +28 -0
- package/lib/layout/dagre/graph.js.map +1 -0
- package/lib/layout/dagre/index.d.ts +3 -4
- package/lib/layout/dagre/index.js +0 -2
- package/lib/layout/dagre/index.js.map +1 -1
- package/lib/layout/dagre/src/acyclic.d.ts +1 -2
- package/lib/layout/dagre/src/acyclic.js +7 -7
- package/lib/layout/dagre/src/acyclic.js.map +1 -1
- package/lib/layout/dagre/src/add-border-segments.d.ts +1 -2
- package/lib/layout/dagre/src/add-border-segments.js +5 -8
- package/lib/layout/dagre/src/add-border-segments.js.map +1 -1
- package/lib/layout/dagre/src/coordinate-system.d.ts +1 -2
- package/lib/layout/dagre/src/coordinate-system.js +15 -5
- package/lib/layout/dagre/src/coordinate-system.js.map +1 -1
- package/lib/layout/dagre/src/data/list.d.ts +9 -5
- package/lib/layout/dagre/src/data/list.js +25 -26
- package/lib/layout/dagre/src/data/list.js.map +1 -1
- package/lib/layout/dagre/src/debug.d.ts +2 -3
- package/lib/layout/dagre/src/debug.js +6 -11
- package/lib/layout/dagre/src/debug.js.map +1 -1
- package/lib/layout/dagre/src/greedy-fas.d.ts +2 -3
- package/lib/layout/dagre/src/greedy-fas.js +41 -15
- package/lib/layout/dagre/src/greedy-fas.js.map +1 -1
- package/lib/layout/dagre/src/layout.d.ts +2 -3
- package/lib/layout/dagre/src/layout.js +171 -100
- package/lib/layout/dagre/src/layout.js.map +1 -1
- package/lib/layout/dagre/src/nesting-graph.d.ts +1 -2
- package/lib/layout/dagre/src/nesting-graph.js +15 -13
- package/lib/layout/dagre/src/nesting-graph.js.map +1 -1
- package/lib/layout/dagre/src/normalize.d.ts +1 -2
- package/lib/layout/dagre/src/normalize.js +12 -14
- package/lib/layout/dagre/src/normalize.js.map +1 -1
- package/lib/layout/dagre/src/order/add-subgraph-constraints.d.ts +1 -2
- package/lib/layout/dagre/src/order/add-subgraph-constraints.js.map +1 -1
- package/lib/layout/dagre/src/order/barycenter.d.ts +1 -2
- package/lib/layout/dagre/src/order/barycenter.js.map +1 -1
- package/lib/layout/dagre/src/order/build-layer-graph.d.ts +2 -3
- package/lib/layout/dagre/src/order/build-layer-graph.js +13 -12
- package/lib/layout/dagre/src/order/build-layer-graph.js.map +1 -1
- package/lib/layout/dagre/src/order/cross-count.d.ts +2 -3
- package/lib/layout/dagre/src/order/cross-count.js +14 -13
- package/lib/layout/dagre/src/order/cross-count.js.map +1 -1
- package/lib/layout/dagre/src/order/index.d.ts +2 -3
- package/lib/layout/dagre/src/order/index.js +15 -13
- package/lib/layout/dagre/src/order/index.js.map +1 -1
- package/lib/layout/dagre/src/order/init-data-order.d.ts +1 -2
- package/lib/layout/dagre/src/order/init-data-order.js +3 -5
- package/lib/layout/dagre/src/order/init-data-order.js.map +1 -1
- package/lib/layout/dagre/src/order/init-order.d.ts +2 -3
- package/lib/layout/dagre/src/order/init-order.js +1 -2
- package/lib/layout/dagre/src/order/init-order.js.map +1 -1
- package/lib/layout/dagre/src/order/resolve-conflicts.d.ts +18 -3
- package/lib/layout/dagre/src/order/resolve-conflicts.js +9 -29
- package/lib/layout/dagre/src/order/resolve-conflicts.js.map +1 -1
- package/lib/layout/dagre/src/order/sort-subgraph.d.ts +6 -3
- package/lib/layout/dagre/src/order/sort-subgraph.js +16 -11
- package/lib/layout/dagre/src/order/sort-subgraph.js.map +1 -1
- package/lib/layout/dagre/src/order/sort.d.ts +6 -1
- package/lib/layout/dagre/src/order/sort.js +2 -5
- package/lib/layout/dagre/src/order/sort.js.map +1 -1
- package/lib/layout/dagre/src/parent-dummy-chains.d.ts +1 -2
- package/lib/layout/dagre/src/parent-dummy-chains.js +47 -44
- package/lib/layout/dagre/src/parent-dummy-chains.js.map +1 -1
- package/lib/layout/dagre/src/position/bk.d.ts +22 -31
- package/lib/layout/dagre/src/position/bk.js +75 -85
- package/lib/layout/dagre/src/position/bk.js.map +1 -1
- package/lib/layout/dagre/src/position/index.d.ts +1 -2
- package/lib/layout/dagre/src/position/index.js +14 -17
- package/lib/layout/dagre/src/position/index.js.map +1 -1
- package/lib/layout/dagre/src/rank/feasible-tree.d.ts +5 -6
- package/lib/layout/dagre/src/rank/feasible-tree.js +3 -7
- package/lib/layout/dagre/src/rank/feasible-tree.js.map +1 -1
- package/lib/layout/dagre/src/rank/index.d.ts +2 -3
- package/lib/layout/dagre/src/rank/index.js.map +1 -1
- package/lib/layout/dagre/src/rank/network-simplex.d.ts +8 -11
- package/lib/layout/dagre/src/rank/network-simplex.js +27 -35
- package/lib/layout/dagre/src/rank/network-simplex.js.map +1 -1
- package/lib/layout/dagre/src/rank/util.d.ts +4 -5
- package/lib/layout/dagre/src/rank/util.js +36 -19
- package/lib/layout/dagre/src/rank/util.js.map +1 -1
- package/lib/layout/dagre/src/util.d.ts +29 -48
- package/lib/layout/dagre/src/util.js +80 -92
- package/lib/layout/dagre/src/util.js.map +1 -1
- package/lib/layout/dagre.d.ts +1 -1
- package/lib/layout/dagre.js +27 -23
- package/lib/layout/dagre.js.map +1 -1
- package/lib/layout/er/core.js +5 -1
- package/lib/layout/er/core.js.map +1 -1
- package/lib/layout/force/force-in-a-box.js +7 -3
- package/lib/layout/force/force-in-a-box.js.map +1 -1
- package/lib/layout/force/force.js +5 -1
- package/lib/layout/force/force.js.map +1 -1
- package/lib/layout/force/index.js +5 -1
- package/lib/layout/force/index.js.map +1 -1
- package/lib/layout/fruchterman.js.map +1 -1
- package/lib/layout/gForce.js +10 -2
- package/lib/layout/gForce.js.map +1 -1
- package/lib/layout/grid.js +2 -2
- package/lib/layout/grid.js.map +1 -1
- package/lib/layout/index.js +5 -1
- package/lib/layout/index.js.map +1 -1
- package/lib/layout/radial/index.js +5 -1
- package/lib/layout/radial/index.js.map +1 -1
- package/lib/registy/index.js +1 -1
- package/lib/registy/index.js.map +1 -1
- package/lib/util/index.js +5 -1
- package/lib/util/index.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +7 -0
- package/src/layout/base.ts +54 -0
- package/src/layout/circular.ts +369 -0
- package/src/layout/comboCombined.ts +390 -0
- package/src/layout/comboForce.ts +873 -0
- package/src/layout/concentric.ts +289 -0
- package/src/layout/constants.ts +21 -0
- package/src/layout/dagre/graph.ts +104 -0
- package/src/layout/dagre/index.ts +31 -0
- package/src/layout/dagre/src/acyclic.ts +58 -0
- package/src/layout/dagre/src/add-border-segments.ts +47 -0
- package/src/layout/dagre/src/coordinate-system.ts +77 -0
- package/src/layout/dagre/src/data/list.ts +60 -0
- package/src/layout/dagre/src/debug.ts +30 -0
- package/src/layout/dagre/src/greedy-fas.ts +144 -0
- package/src/layout/dagre/src/layout.ts +580 -0
- package/src/layout/dagre/src/nesting-graph.ts +143 -0
- package/src/layout/dagre/src/normalize.ts +96 -0
- package/src/layout/dagre/src/order/add-subgraph-constraints.ts +29 -0
- package/src/layout/dagre/src/order/barycenter.ts +26 -0
- package/src/layout/dagre/src/order/build-layer-graph.ts +82 -0
- package/src/layout/dagre/src/order/cross-count.ts +77 -0
- package/src/layout/dagre/src/order/index.ts +105 -0
- package/src/layout/dagre/src/order/init-data-order.ts +27 -0
- package/src/layout/dagre/src/order/init-order.ts +56 -0
- package/src/layout/dagre/src/order/resolve-conflicts.ts +152 -0
- package/src/layout/dagre/src/order/sort-subgraph.ts +105 -0
- package/src/layout/dagre/src/order/sort.ts +76 -0
- package/src/layout/dagre/src/parent-dummy-chains.ts +102 -0
- package/src/layout/dagre/src/position/bk.ts +494 -0
- package/src/layout/dagre/src/position/index.ts +82 -0
- package/src/layout/dagre/src/rank/feasible-tree.ts +165 -0
- package/src/layout/dagre/src/rank/index.ts +54 -0
- package/src/layout/dagre/src/rank/network-simplex.ts +225 -0
- package/src/layout/dagre/src/rank/util.ts +157 -0
- package/src/layout/dagre/src/util.ts +308 -0
- package/src/layout/dagre.ts +423 -0
- package/src/layout/dagreCompound.ts +518 -0
- package/src/layout/er/core.ts +117 -0
- package/src/layout/er/forceGrid.ts +95 -0
- package/src/layout/er/grid.ts +185 -0
- package/src/layout/er/index.ts +68 -0
- package/src/layout/er/mysqlWorkbench.ts +345 -0
- package/src/layout/er/type.ts +39 -0
- package/src/layout/force/force-in-a-box.ts +400 -0
- package/src/layout/force/force.ts +391 -0
- package/src/layout/force/index.ts +1 -0
- package/src/layout/forceAtlas2/body.ts +115 -0
- package/src/layout/forceAtlas2/index.ts +556 -0
- package/src/layout/forceAtlas2/quad.ts +115 -0
- package/src/layout/forceAtlas2/quadTree.ts +107 -0
- package/src/layout/fruchterman.ts +361 -0
- package/src/layout/gForce.ts +487 -0
- package/src/layout/gpu/fruchterman.ts +314 -0
- package/src/layout/gpu/fruchtermanShader.ts +204 -0
- package/src/layout/gpu/gForce.ts +406 -0
- package/src/layout/gpu/gForceShader.ts +221 -0
- package/src/layout/grid.ts +391 -0
- package/src/layout/index.ts +45 -0
- package/src/layout/layout.ts +75 -0
- package/src/layout/mds.ts +140 -0
- package/src/layout/radial/index.ts +1 -0
- package/src/layout/radial/mds.ts +51 -0
- package/src/layout/radial/radial.ts +500 -0
- package/src/layout/radial/radialNonoverlapForce.ts +189 -0
- package/src/layout/random.ts +75 -0
- package/src/layout/types.ts +421 -0
- package/src/registy/index.ts +43 -0
- package/src/util/array.ts +1 -0
- package/src/util/function.ts +64 -0
- package/src/util/gpu.ts +254 -0
- package/src/util/index.ts +6 -0
- package/src/util/math.ts +158 -0
- package/src/util/number.ts +8 -0
- package/src/util/object.ts +28 -0
- package/src/util/string.ts +18 -0
- package/CHANGELOG.md +0 -78
- package/es/layout/dagre/src/graphlib.d.ts +0 -2
- package/es/layout/dagre/src/graphlib.js +0 -51
- package/es/layout/dagre/src/graphlib.js.map +0 -1
- package/lib/layout/dagre/src/graphlib.d.ts +0 -2
- package/lib/layout/dagre/src/graphlib.js +0 -56
- package/lib/layout/dagre/src/graphlib.js.map +0 -1
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This module provides coordinate assignment based on Brandes and Köpf, "Fast
|
|
3
|
+
* and Simple Horizontal Coordinate Assignment."
|
|
4
|
+
*/
|
|
5
|
+
import { Graph as RawGraph } from "@antv/graphlib";
|
|
6
|
+
import { Graph } from "../../graph";
|
|
7
|
+
import { buildLayerMatrix, minBy } from "../util";
|
|
8
|
+
|
|
9
|
+
class BlockGraph extends RawGraph<string, string, number> {}
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* Marks all edges in the graph with a type-1 conflict with the "type1Conflict"
|
|
13
|
+
* property. A type-1 conflict is one where a non-inner segment crosses an
|
|
14
|
+
* inner segment. An inner segment is an edge with both incident nodes marked
|
|
15
|
+
* with the "dummy" property.
|
|
16
|
+
*
|
|
17
|
+
* This algorithm scans layer by layer, starting with the second, for type-1
|
|
18
|
+
* conflicts between the current layer and the previous layer. For each layer
|
|
19
|
+
* it scans the nodes from left to right until it reaches one that is incident
|
|
20
|
+
* on an inner segment. It then scans predecessors to determine if they have
|
|
21
|
+
* edges that cross that inner segment. At the end a final scan is done for all
|
|
22
|
+
* nodes on the current rank to see if they cross the last visited inner
|
|
23
|
+
* segment.
|
|
24
|
+
*
|
|
25
|
+
* This algorithm (safely) assumes that a dummy node will only be incident on a
|
|
26
|
+
* single node in the layers being scanned.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
type Conflicts = Record<string, Record<string, boolean>>;
|
|
30
|
+
|
|
31
|
+
export const findType1Conflicts = (g: Graph, layering?: string[][]) => {
|
|
32
|
+
const conflicts = {};
|
|
33
|
+
|
|
34
|
+
const visitLayer = (prevLayer: string[], layer: string[]) => {
|
|
35
|
+
// last visited node in the previous layer that is incident on an inner
|
|
36
|
+
// segment.
|
|
37
|
+
let k0 = 0;
|
|
38
|
+
// Tracks the last node in this layer scanned for crossings with a type-1
|
|
39
|
+
// segment.
|
|
40
|
+
let scanPos = 0;
|
|
41
|
+
const prevLayerLength = prevLayer.length;
|
|
42
|
+
const lastNode = layer?.[layer?.length - 1];
|
|
43
|
+
|
|
44
|
+
layer?.forEach((v: string, i: number) => {
|
|
45
|
+
const w = findOtherInnerSegmentNode(g, v);
|
|
46
|
+
const k1 = w ? g.node(w)!.order! : prevLayerLength;
|
|
47
|
+
|
|
48
|
+
if (w || v === lastNode) {
|
|
49
|
+
layer.slice(scanPos, i + 1)?.forEach((scanNode) => {
|
|
50
|
+
g.predecessors(scanNode)?.forEach((u) => {
|
|
51
|
+
const uLabel = g.node(u)!;
|
|
52
|
+
const uPos = uLabel.order as number;
|
|
53
|
+
if (
|
|
54
|
+
(uPos < k0 || k1 < uPos) &&
|
|
55
|
+
!(uLabel.dummy && g.node(scanNode)?.dummy)
|
|
56
|
+
) {
|
|
57
|
+
addConflict(conflicts, u, scanNode);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
scanPos = i + 1;
|
|
62
|
+
k0 = k1;
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return layer;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if (layering?.length) {
|
|
70
|
+
layering.reduce(visitLayer);
|
|
71
|
+
}
|
|
72
|
+
return conflicts;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const findType2Conflicts = (g: Graph, layering?: string[][]) => {
|
|
76
|
+
const conflicts = {};
|
|
77
|
+
|
|
78
|
+
const scan = (
|
|
79
|
+
south: string[],
|
|
80
|
+
southPos: number,
|
|
81
|
+
southEnd: number,
|
|
82
|
+
prevNorthBorder: number,
|
|
83
|
+
nextNorthBorder: number
|
|
84
|
+
) => {
|
|
85
|
+
let v: string;
|
|
86
|
+
const range = [];
|
|
87
|
+
for (let i = southPos; i < southEnd; i++) {
|
|
88
|
+
range.push(i);
|
|
89
|
+
}
|
|
90
|
+
range.forEach((i) => {
|
|
91
|
+
v = south[i];
|
|
92
|
+
if (g.node(v)?.dummy) {
|
|
93
|
+
g.predecessors(v)?.forEach((u) => {
|
|
94
|
+
const uNode = g.node(u)!;
|
|
95
|
+
if (
|
|
96
|
+
uNode.dummy &&
|
|
97
|
+
((uNode.order as number) < prevNorthBorder ||
|
|
98
|
+
(uNode.order as number) > nextNorthBorder)
|
|
99
|
+
) {
|
|
100
|
+
addConflict(conflicts, u, v);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const visitLayer = (north: string[], south: string[]) => {
|
|
108
|
+
let prevNorthPos = -1;
|
|
109
|
+
let nextNorthPos: number;
|
|
110
|
+
let southPos = 0;
|
|
111
|
+
|
|
112
|
+
south?.forEach((v: string, southLookahead: number) => {
|
|
113
|
+
if (g.node(v)?.dummy === "border") {
|
|
114
|
+
const predecessors = g.predecessors(v) || [];
|
|
115
|
+
if (predecessors.length) {
|
|
116
|
+
nextNorthPos = g.node(predecessors[0]!)!.order as number;
|
|
117
|
+
scan(south, southPos, southLookahead, prevNorthPos, nextNorthPos);
|
|
118
|
+
southPos = southLookahead;
|
|
119
|
+
prevNorthPos = nextNorthPos;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
scan(south, southPos, south.length, nextNorthPos, north.length);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
return south;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
if (layering?.length) {
|
|
129
|
+
layering.reduce(visitLayer);
|
|
130
|
+
}
|
|
131
|
+
return conflicts;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export const findOtherInnerSegmentNode = (g: Graph, v: string) => {
|
|
135
|
+
if (g.node(v)?.dummy) {
|
|
136
|
+
return g.predecessors(v)?.find((u) => g.node(u)!.dummy);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export const addConflict = (conflicts: Conflicts, v: string, w: string) => {
|
|
141
|
+
let vv = v;
|
|
142
|
+
let ww = w;
|
|
143
|
+
if (vv > ww) {
|
|
144
|
+
const tmp = vv;
|
|
145
|
+
vv = ww;
|
|
146
|
+
ww = tmp;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let conflictsV = conflicts[vv];
|
|
150
|
+
if (!conflictsV) {
|
|
151
|
+
conflicts[vv] = conflictsV = {};
|
|
152
|
+
}
|
|
153
|
+
conflictsV[ww] = true;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const hasConflict = (conflicts: Conflicts, v: string, w: string) => {
|
|
157
|
+
let vv = v;
|
|
158
|
+
let ww = w;
|
|
159
|
+
if (vv > ww) {
|
|
160
|
+
const tmp = v;
|
|
161
|
+
vv = ww;
|
|
162
|
+
ww = tmp;
|
|
163
|
+
}
|
|
164
|
+
return !!conflicts[vv];
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
/*
|
|
168
|
+
* Try to align nodes into vertical "blocks" where possible. This algorithm
|
|
169
|
+
* attempts to align a node with one of its median neighbors. If the edge
|
|
170
|
+
* connecting a neighbor is a type-1 conflict then we ignore that possibility.
|
|
171
|
+
* If a previous node has already formed a block with a node after the node
|
|
172
|
+
* we're trying to form a block with, we also ignore that possibility - our
|
|
173
|
+
* blocks would be split in that scenario.
|
|
174
|
+
*/
|
|
175
|
+
export const verticalAlignment = (
|
|
176
|
+
g: Graph,
|
|
177
|
+
layering: string[][],
|
|
178
|
+
conflicts: Conflicts,
|
|
179
|
+
neighborFn: (v: string) => string[]
|
|
180
|
+
) => {
|
|
181
|
+
const root: Record<string, string> = {};
|
|
182
|
+
const align: Record<string, string> = {};
|
|
183
|
+
const pos: Record<string, number> = {};
|
|
184
|
+
|
|
185
|
+
// We cache the position here based on the layering because the graph and
|
|
186
|
+
// layering may be out of sync. The layering matrix is manipulated to
|
|
187
|
+
// generate different extreme alignments.
|
|
188
|
+
layering?.forEach((layer) => {
|
|
189
|
+
layer?.forEach((v, order: number) => {
|
|
190
|
+
root[v] = v;
|
|
191
|
+
align[v] = v;
|
|
192
|
+
pos[v] = order;
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
layering?.forEach((layer) => {
|
|
197
|
+
let prevIdx = -1;
|
|
198
|
+
layer?.forEach((v) => {
|
|
199
|
+
let ws = neighborFn(v);
|
|
200
|
+
if (ws.length) {
|
|
201
|
+
ws = ws.sort((a: string, b: string) => pos[a] - pos[b]);
|
|
202
|
+
const mp = (ws.length - 1) / 2;
|
|
203
|
+
for (let i = Math.floor(mp), il = Math.ceil(mp); i <= il; ++i) {
|
|
204
|
+
const w = ws[i];
|
|
205
|
+
if (
|
|
206
|
+
align[v] === v &&
|
|
207
|
+
prevIdx < pos[w] &&
|
|
208
|
+
!hasConflict(conflicts, v, w)
|
|
209
|
+
) {
|
|
210
|
+
align[w] = v;
|
|
211
|
+
align[v] = root[v] = root[w];
|
|
212
|
+
prevIdx = pos[w];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
return { root, align };
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
export const horizontalCompaction = (
|
|
223
|
+
g: Graph,
|
|
224
|
+
layering: string[][],
|
|
225
|
+
root: Record<string, string>,
|
|
226
|
+
align: Record<string, string>,
|
|
227
|
+
reverseSep?: boolean
|
|
228
|
+
) => {
|
|
229
|
+
// This portion of the algorithm differs from BK due to a number of problems.
|
|
230
|
+
// Instead of their algorithm we construct a new block graph and do two
|
|
231
|
+
// sweeps. The first sweep places blocks with the smallest possible
|
|
232
|
+
// coordinates. The second sweep removes unused space by moving blocks to the
|
|
233
|
+
// greatest coordinates without violating separation.
|
|
234
|
+
const xs: Record<string, number> = {};
|
|
235
|
+
const blockG = buildBlockGraph(g, layering, root, reverseSep);
|
|
236
|
+
const borderType = reverseSep ? "borderLeft" : "borderRight";
|
|
237
|
+
|
|
238
|
+
const iterate = (
|
|
239
|
+
setXsFunc: (param: string) => void,
|
|
240
|
+
nextNodesFunc: (param: string) => string
|
|
241
|
+
) => {
|
|
242
|
+
let stack = blockG.nodes();
|
|
243
|
+
let elem = stack.pop();
|
|
244
|
+
const visited: Record<string, boolean> = {};
|
|
245
|
+
while (elem) {
|
|
246
|
+
if (visited[elem]) {
|
|
247
|
+
setXsFunc(elem);
|
|
248
|
+
} else {
|
|
249
|
+
visited[elem] = true;
|
|
250
|
+
stack.push(elem);
|
|
251
|
+
stack = stack.concat(nextNodesFunc(elem));
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
elem = stack.pop();
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// First pass, assign smallest coordinates
|
|
259
|
+
const pass1 = (elem: string) => {
|
|
260
|
+
xs[elem] = (blockG.inEdges(elem) || []).reduce((acc: number, e) => {
|
|
261
|
+
return Math.max(acc, (xs[e.v] || 0) + blockG.edge(e)!);
|
|
262
|
+
}, 0);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// Second pass, assign greatest coordinates
|
|
266
|
+
const pass2 = (elem: string) => {
|
|
267
|
+
const min = (blockG.outEdges(elem) || []).reduce((acc: number, e) => {
|
|
268
|
+
return Math.min(acc, (xs[e.w] || 0) - blockG.edge(e)!);
|
|
269
|
+
}, Number.POSITIVE_INFINITY);
|
|
270
|
+
|
|
271
|
+
const node = g.node(elem)!;
|
|
272
|
+
if (min !== Number.POSITIVE_INFINITY && node.borderType !== borderType) {
|
|
273
|
+
xs[elem] = Math.max(xs[elem], min);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
iterate(pass1, blockG.predecessors.bind(blockG));
|
|
278
|
+
iterate(pass2, blockG.successors.bind(blockG));
|
|
279
|
+
|
|
280
|
+
// Assign x coordinates to all nodes
|
|
281
|
+
Object.values(align)?.forEach((v) => {
|
|
282
|
+
xs[v] = xs[root[v]];
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return xs;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
export const buildBlockGraph = (
|
|
289
|
+
g: Graph,
|
|
290
|
+
layering: string[][],
|
|
291
|
+
root: Record<string, string>,
|
|
292
|
+
reverseSep?: boolean
|
|
293
|
+
) => {
|
|
294
|
+
const blockGraph = new BlockGraph();
|
|
295
|
+
const graphLabel = g.graph();
|
|
296
|
+
const sepFn = sep(
|
|
297
|
+
graphLabel.nodesep as number,
|
|
298
|
+
graphLabel.edgesep as number,
|
|
299
|
+
reverseSep as boolean
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
layering?.forEach((layer) => {
|
|
303
|
+
let u: string;
|
|
304
|
+
layer?.forEach((v) => {
|
|
305
|
+
const vRoot = root[v];
|
|
306
|
+
blockGraph.setNode(vRoot);
|
|
307
|
+
if (u) {
|
|
308
|
+
const uRoot = root[u];
|
|
309
|
+
const prevMax = blockGraph.edgeFromArgs(uRoot, vRoot);
|
|
310
|
+
blockGraph.setEdge(
|
|
311
|
+
uRoot,
|
|
312
|
+
vRoot,
|
|
313
|
+
Math.max(sepFn(g, v, u), prevMax || 0)
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
u = v;
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
return blockGraph;
|
|
321
|
+
};
|
|
322
|
+
/*
|
|
323
|
+
* Returns the alignment that has the smallest width of the given alignments.
|
|
324
|
+
*/
|
|
325
|
+
export const findSmallestWidthAlignment = (
|
|
326
|
+
g: Graph,
|
|
327
|
+
xss: Record<string, Record<string, number>>
|
|
328
|
+
) => {
|
|
329
|
+
return minBy(Object.values(xss), (xs) => {
|
|
330
|
+
let max = Number.NEGATIVE_INFINITY;
|
|
331
|
+
let min = Number.POSITIVE_INFINITY;
|
|
332
|
+
|
|
333
|
+
Object.keys(xs)?.forEach((v: string) => {
|
|
334
|
+
const x = xs[v];
|
|
335
|
+
const halfWidth = width(g, v) / 2;
|
|
336
|
+
|
|
337
|
+
max = Math.max(x + halfWidth, max);
|
|
338
|
+
min = Math.min(x - halfWidth, min);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return max - min;
|
|
342
|
+
});
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
/*
|
|
346
|
+
* Align the coordinates of each of the layout alignments such that
|
|
347
|
+
* left-biased alignments have their minimum coordinate at the same point as
|
|
348
|
+
* the minimum coordinate of the smallest width alignment and right-biased
|
|
349
|
+
* alignments have their maximum coordinate at the same point as the maximum
|
|
350
|
+
* coordinate of the smallest width alignment.
|
|
351
|
+
*/
|
|
352
|
+
export function alignCoordinates(
|
|
353
|
+
xss: Record<string, Record<string, number>>,
|
|
354
|
+
alignTo: Record<string, number>
|
|
355
|
+
) {
|
|
356
|
+
// @ts-ignore
|
|
357
|
+
const alignToVals = Object.values(alignTo) as number[];
|
|
358
|
+
const alignToMin = Math.min(...alignToVals);
|
|
359
|
+
const alignToMax = Math.max(...alignToVals);
|
|
360
|
+
|
|
361
|
+
["u", "d"].forEach((vert) => {
|
|
362
|
+
["l", "r"].forEach((horiz) => {
|
|
363
|
+
const alignment = vert + horiz;
|
|
364
|
+
const xs = xss[alignment];
|
|
365
|
+
let delta: number;
|
|
366
|
+
if (xs === alignTo) return;
|
|
367
|
+
|
|
368
|
+
const xsVals = Object.values(xs) as number[];
|
|
369
|
+
delta =
|
|
370
|
+
horiz === "l"
|
|
371
|
+
? alignToMin - Math.min(...xsVals)
|
|
372
|
+
: alignToMax - Math.max(...xsVals);
|
|
373
|
+
|
|
374
|
+
if (delta) {
|
|
375
|
+
xss[alignment] = {};
|
|
376
|
+
Object.keys(xs).forEach((key) => {
|
|
377
|
+
xss[alignment][key] = xs[key] + delta;
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export const balance = (
|
|
385
|
+
xss: Record<string, Record<string, number>>,
|
|
386
|
+
align?: string
|
|
387
|
+
) => {
|
|
388
|
+
const result: Record<string, number> = {};
|
|
389
|
+
Object.keys(xss.ul).forEach((key) => {
|
|
390
|
+
if (align) {
|
|
391
|
+
result[key] = xss[align.toLowerCase()][key];
|
|
392
|
+
} else {
|
|
393
|
+
const values = Object.values(xss).map((x) => x[key]);
|
|
394
|
+
const xs = values.sort((a: number, b: number) => a - b);
|
|
395
|
+
result[key] = (xs[1] + xs[2]) / 2;
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
return result;
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
export const positionX = (g: Graph) => {
|
|
402
|
+
const layering = buildLayerMatrix(g);
|
|
403
|
+
const conflicts = Object.assign(
|
|
404
|
+
findType1Conflicts(g, layering),
|
|
405
|
+
findType2Conflicts(g, layering)
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
const xss: Record<string, Record<string, number>> = {};
|
|
409
|
+
let adjustedLayering: string[][];
|
|
410
|
+
["u", "d"].forEach((vert) => {
|
|
411
|
+
adjustedLayering =
|
|
412
|
+
vert === "u" ? layering : Object.values(layering).reverse();
|
|
413
|
+
["l", "r"].forEach((horiz) => {
|
|
414
|
+
if (horiz === "r") {
|
|
415
|
+
adjustedLayering = adjustedLayering.map((inner) =>
|
|
416
|
+
Object.values(inner).reverse()
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const neighborFn = (vert === "u" ? g.predecessors : g.successors).bind(g);
|
|
421
|
+
const align = verticalAlignment(
|
|
422
|
+
g,
|
|
423
|
+
adjustedLayering,
|
|
424
|
+
conflicts,
|
|
425
|
+
neighborFn
|
|
426
|
+
);
|
|
427
|
+
const xs = horizontalCompaction(
|
|
428
|
+
g,
|
|
429
|
+
adjustedLayering,
|
|
430
|
+
align.root,
|
|
431
|
+
align.align,
|
|
432
|
+
horiz === "r"
|
|
433
|
+
);
|
|
434
|
+
if (horiz === "r") {
|
|
435
|
+
Object.keys(xs).forEach((key) => {
|
|
436
|
+
xs[key] = -xs[key];
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
xss[vert + horiz] = xs;
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
const smallestWidth = findSmallestWidthAlignment(g, xss);
|
|
444
|
+
alignCoordinates(xss, smallestWidth);
|
|
445
|
+
return balance(xss, g.graph().align as string);
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
export const sep = (nodeSep: number, edgeSep: number, reverseSep: boolean) => {
|
|
449
|
+
return (g: Graph, v: string, w: string) => {
|
|
450
|
+
const vLabel = g.node(v)!;
|
|
451
|
+
const wLabel = g.node(w)!;
|
|
452
|
+
let sum = 0;
|
|
453
|
+
let delta;
|
|
454
|
+
|
|
455
|
+
sum += vLabel.width! / 2;
|
|
456
|
+
if (vLabel.hasOwnProperty("labelpos")) {
|
|
457
|
+
switch ((vLabel.labelpos || "").toLowerCase()) {
|
|
458
|
+
case "l":
|
|
459
|
+
delta = -vLabel.width! / 2;
|
|
460
|
+
break;
|
|
461
|
+
case "r":
|
|
462
|
+
delta = vLabel.width! / 2;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (delta) {
|
|
467
|
+
sum += reverseSep ? delta : -delta;
|
|
468
|
+
}
|
|
469
|
+
delta = 0;
|
|
470
|
+
|
|
471
|
+
sum += (vLabel.dummy ? edgeSep : nodeSep) / 2;
|
|
472
|
+
sum += (wLabel.dummy ? edgeSep : nodeSep) / 2;
|
|
473
|
+
|
|
474
|
+
sum += wLabel.width! / 2;
|
|
475
|
+
if (wLabel.labelpos) {
|
|
476
|
+
switch ((wLabel.labelpos || "").toLowerCase()) {
|
|
477
|
+
case "l":
|
|
478
|
+
delta = wLabel.width! / 2;
|
|
479
|
+
break;
|
|
480
|
+
case "r":
|
|
481
|
+
delta = -wLabel.width! / 2;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (delta) {
|
|
486
|
+
sum += reverseSep ? delta : -delta;
|
|
487
|
+
}
|
|
488
|
+
delta = 0;
|
|
489
|
+
|
|
490
|
+
return sum;
|
|
491
|
+
};
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
export const width = (g: Graph, v: string) => g.node(v)!.width || 0;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Graph } from "../../graph";
|
|
2
|
+
import { asNonCompoundGraph, buildLayerMatrix } from "../util";
|
|
3
|
+
import {
|
|
4
|
+
alignCoordinates,
|
|
5
|
+
balance,
|
|
6
|
+
findSmallestWidthAlignment,
|
|
7
|
+
findType1Conflicts,
|
|
8
|
+
findType2Conflicts,
|
|
9
|
+
horizontalCompaction,
|
|
10
|
+
verticalAlignment,
|
|
11
|
+
} from "./bk";
|
|
12
|
+
|
|
13
|
+
const positionY = (g: Graph) => {
|
|
14
|
+
const layering = buildLayerMatrix(g);
|
|
15
|
+
const rankSep = g.graph().ranksep as number;
|
|
16
|
+
let prevY = 0;
|
|
17
|
+
layering?.forEach((layer) => {
|
|
18
|
+
const heights = layer.map((v) => g.node(v)!.height!);
|
|
19
|
+
const maxHeight = Math.max(...heights);
|
|
20
|
+
layer?.forEach((v: string) => {
|
|
21
|
+
g.node(v)!.y = prevY + maxHeight / 2;
|
|
22
|
+
});
|
|
23
|
+
prevY += maxHeight + rankSep;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const positionX = (g: Graph) => {
|
|
28
|
+
const layering = buildLayerMatrix(g);
|
|
29
|
+
const conflicts = Object.assign(
|
|
30
|
+
findType1Conflicts(g, layering),
|
|
31
|
+
findType2Conflicts(g, layering)
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const xss: Record<string, Record<string, number>> = {};
|
|
35
|
+
let adjustedLayering: string[][] = [];
|
|
36
|
+
["u", "d"].forEach((vert) => {
|
|
37
|
+
adjustedLayering =
|
|
38
|
+
vert === "u" ? layering : Object.values(layering).reverse();
|
|
39
|
+
["l", "r"].forEach((horiz) => {
|
|
40
|
+
if (horiz === "r") {
|
|
41
|
+
adjustedLayering = adjustedLayering.map((inner) =>
|
|
42
|
+
Object.values(inner).reverse()
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const neighborFn = (vert === "u" ? g.predecessors : g.successors).bind(g);
|
|
47
|
+
const align = verticalAlignment(
|
|
48
|
+
g,
|
|
49
|
+
adjustedLayering,
|
|
50
|
+
conflicts,
|
|
51
|
+
neighborFn
|
|
52
|
+
);
|
|
53
|
+
const xs = horizontalCompaction(
|
|
54
|
+
g,
|
|
55
|
+
adjustedLayering,
|
|
56
|
+
align.root,
|
|
57
|
+
align.align,
|
|
58
|
+
horiz === "r"
|
|
59
|
+
);
|
|
60
|
+
if (horiz === "r") {
|
|
61
|
+
Object.keys(xs).forEach((xsKey) => (xs[xsKey] = -xs[xsKey]));
|
|
62
|
+
}
|
|
63
|
+
xss[vert + horiz] = xs;
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const smallestWidth = findSmallestWidthAlignment(g, xss);
|
|
68
|
+
smallestWidth && alignCoordinates(xss, smallestWidth);
|
|
69
|
+
return balance(xss, g.graph().align as string);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const position = (g: Graph) => {
|
|
73
|
+
const ng = asNonCompoundGraph(g);
|
|
74
|
+
|
|
75
|
+
positionY(ng);
|
|
76
|
+
const xs = positionX(ng);
|
|
77
|
+
Object.keys(xs)?.forEach((key: string) => {
|
|
78
|
+
ng.node(key)!.x = xs[key];
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default position;
|