Skip to content

Commit

Permalink
Add dagre layout
Browse files Browse the repository at this point in the history
  • Loading branch information
rexrainbow committed Oct 29, 2024
1 parent dac3d53 commit e50d852
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 26 deletions.
5 changes: 5 additions & 0 deletions examples/graph/dagre-layout.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@echo off
set main=./examples/graph/dagre-layout.js
cd ..
cd ..
npm run dev
85 changes: 85 additions & 0 deletions examples/graph/dagre-layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import phaser from 'phaser/src/phaser.js';
import GraphPlugin from '../../plugins/graph-plugin.js';

class Demo extends Phaser.Scene {
constructor() {
super({
key: 'examples'
})
}

preload() { }

create() {
var nodeA = CreateNode(this, 0xFFFF00);
var nodeB = CreateNode(this);
var nodeC = CreateNode(this);
var nodeD = CreateNode(this);
var edgeAB = CreateEdge(this);
var edgeAC = CreateEdge(this);
var edgeBD = CreateEdge(this);
var edgeCD = CreateEdge(this);

var graph = this.rexGraph.add.graph()
.addNodes([nodeA, nodeB, nodeC, nodeD], { padding: 3 })
.addEdge(edgeAB, nodeA, nodeB)
.addEdge(edgeAC, nodeA, nodeC)
.addEdge(edgeBD, nodeB, nodeD)
.addEdge(edgeCD, nodeC, nodeD)

graph.on('layout.edge', function (edgeGameObject, path) {
var startPoint = path[0];
var endPoint = path[path.length - 1];
edgeGameObject
.setPosition(startPoint.x, startPoint.y)
.setTo(0, 0, endPoint.x - startPoint.x, endPoint.y - startPoint.y)
});


graph.once('layout.complete', function () {
console.log('layout.complete')
})

this.rexGraph.DagreLayout(graph, { rankdir: 'LR' })

console.log('done')

}

update() {
}
}

var CreateNode = function (scene, color) {
if (color === undefined) {
color = 0x888888;
}
return scene.add.rectangle(0, 0, 100, 100).setStrokeStyle(3, color)
}

var CreateEdge = function (scene) {
return scene.add.line(0, 0, 0, 0, 0, 0, 0xff0000).setLineWidth(2).setOrigin(0)
}

var config = {
type: Phaser.AUTO,
parent: 'phaser-example',
width: 800,
height: 600,
scale: {
mode: Phaser.Scale.FIT,
autoCenter: Phaser.Scale.CENTER_BOTH,
},
scene: Demo,
plugins: {
scene: [
{
key: 'rexGraph',
plugin: GraphPlugin,
mapping: 'rexGraph'
}
]
}
};

var game = new Phaser.Game(config);
10 changes: 7 additions & 3 deletions examples/graph/elk-layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,18 @@ class Demo extends Phaser.Scene {
.setTo(0, 0, endPoint.x - startPoint.x, endPoint.y - startPoint.y)
});


graph.once('layout.complete', function () {
console.log('layout.complete')
})

this.rexGraph.ELKLayout(graph, {
layoutOptions: {
// 'elk.direction': 'DOWN'
}
})
.once('layout.complete', function () {
console.log('layout.complete')
})

console.log('done')

}

Expand Down
23 changes: 21 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"dependencies": {
"eventemitter3": "^3.1.2",
"graphology": "^0.25.4",
"dagre": "^0.8.5",
"i18next": "^22.5.1",
"i18next-http-backend": "^2.5.2",
"js-yaml": "^4.1.0",
Expand Down
4 changes: 3 additions & 1 deletion plugins/graph-components.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Graph from './graph/graph/Graph.js';
import ELKLayout from './graph/layout/elkjs/Layout.js';
import DagreLayout from './graph/layout/dagre/Layout.js';

export {
Graph,
ELKLayout
ELKLayout,
DagreLayout
}
8 changes: 7 additions & 1 deletion plugins/graph-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import ObjectFactory from './graph/ObjectFactory.js';

import GraphFactory from './graph/graph/Factory.js';
import ELKLayout from './graph/layout/elkjs/Layout.js';
import DagreLayout from './graph/layout/dagre/Layout.js';

class GraphPlugin extends Phaser.Plugins.ScenePlugin {
constructor(scene, pluginManager) {
Expand All @@ -26,7 +27,12 @@ class GraphPlugin extends Phaser.Plugins.ScenePlugin {

ELKLayout(graph, config) {
ELKLayout(graph, config);
return graph
return graph;
}

DagreLayout(graph, config) {
DagreLayout(graph, config);
return graph;
}
}

Expand Down
51 changes: 51 additions & 0 deletions plugins/graph/layout/dagre/BuildGraphData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import dagre from 'dagre';
import UIDToObj from '../../graphitem/UIDToObj.js';
import GetBoundsConfig from '../../../utils/bounds/GetBoundsConfig.js';

var BuildGraphData = function (graph, config) {
var graphData = new dagre.graphlib.Graph();
graphData.setGraph(config);
graphData.setDefaultEdgeLabel(function () { });

var nodeGameObjectMap = {};
graph.graph.forEachNode(function (uid, attributes) {
var nodeGameObject = UIDToObj(uid);
if (!nodeGameObject) {
return;
}

var padding = GetBoundsConfig(attributes.padding);
var width = nodeGameObject.displayWidth + padding.left + padding.right;
var height = nodeGameObject.displayHeight + padding.top + padding.bottom;

graphData.setNode(uid, {
gameObject: nodeGameObject, padding: padding,
width: width, height: height,
})

nodeGameObjectMap[uid] = nodeGameObject;
})

graph.graph.forEachEdge(function (uid, attributes, sourceUID, targetUID) {
var sourceGameObject = nodeGameObjectMap[sourceUID];
var targetGameObject = nodeGameObjectMap[targetUID];

if (!sourceGameObject || !targetGameObject) {
return;
}
var edgeGameObject = UIDToObj(uid);
if (!edgeGameObject) {
return;
}

graphData.setEdge(sourceUID, targetUID, {
gameObject: edgeGameObject,
sourceGameObject: sourceGameObject,
targetGameObject: targetGameObject,
})
})

return graphData;
}

export default BuildGraphData;
5 changes: 5 additions & 0 deletions plugins/graph/layout/dagre/GetPath.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
var GetPath = function (edgeData) {
return edgeData.points;
}

export default GetPath;
20 changes: 20 additions & 0 deletions plugins/graph/layout/dagre/Layout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import LayoutBase from '../utils/Layout.js';
import BuildGraphData from './BuildGraphData.js';
import RunLayout from './RunLayout.js';
import PlaceGameObjects from './PlaceGameObjects.js';

var callbacks = {
buildGraphData: BuildGraphData,
isAsyncRunLayout: false,
runLayout: RunLayout,
placeGameObjects: PlaceGameObjects,
}

var Layout = async function (graph, config) {
if (config === undefined) {
config = {};
}
await LayoutBase(callbacks, graph, config);
}

export default Layout;
26 changes: 26 additions & 0 deletions plugins/graph/layout/dagre/PlaceGameObjects.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import AlignIn from '../../../utils/actions/AlignIn.js';
import GetPath from './GetPath.js';

const ALIGN_CENTER = Phaser.Display.Align.CENTER;

var PlaceGameObjects = function (graph, graphData, config) {
graphData.nodes().forEach(function (nodeKey) {
var nodeData = graphData.node(nodeKey);
var gameObject = nodeData.gameObject;
var padding = nodeData.padding;
var x = nodeData.x - (nodeData.width / 2) + padding.left; // nodeData.x is centerX
var y = nodeData.y - (nodeData.height / 2) + padding.top; // nodeData.y is centerY
var width = nodeData.width - padding.left - padding.right;
var height = nodeData.height - padding.top - padding.bottom;
AlignIn(gameObject, x, y, width, height, ALIGN_CENTER);
graph.emit('layout.node', nodeData.gameObject);
});

graphData.edges().forEach(function (edgeKey) {
var edgeData = graphData.edge(edgeKey);
var path = GetPath(edgeData);
graph.emit('layout.edge', edgeData.gameObject, path, edgeData.sourceGameObject, edgeData.targetGameObject);
});
}

export default PlaceGameObjects;
7 changes: 7 additions & 0 deletions plugins/graph/layout/dagre/RunLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import dagre from 'dagre';

var RunLayout = async function (graphData, config) {
await dagre.layout(graphData);
}

export default RunLayout;
29 changes: 10 additions & 19 deletions plugins/graph/layout/elkjs/Layout.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,20 @@
import ELK from '../../../utils/elkjs/elk.bundled.js';
import LayoutBase from '../utils/Layout.js';
import BuildGraphData from './BuildGraphData.js';
import RunLayout from './RunLayout.js';
import PlaceGameObjects from './PlaceGameObjects.js';

var callbacks = {
buildGraphData: BuildGraphData,
isAsyncRunLayout: true,
runLayout: RunLayout,
placeGameObjects: PlaceGameObjects,
}

var Layout = async function (graph, config) {
if (config === undefined) {
config = {};
}

graph.emit('layout.start', graph);

var graphData = BuildGraphData(graph, config);

graph.emit('layout.prelayout', graph);

var elk = new ELK();
graphData = await elk.layout(graphData, {
layoutOptions: config.layoutOptions,

});

graph.emit('layout.postlayout', graph);

PlaceGameObjects(graph, graphData, config);

graph.emit('layout.complete', graph);
await LayoutBase(callbacks, graph, config);
}

export default Layout;
10 changes: 10 additions & 0 deletions plugins/graph/layout/elkjs/RunLayout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import ELK from '../../../utils/elkjs/elk.bundled.js';

var RunLayout = async function (graphData, config) {
var elk = new ELK();
graphData = await elk.layout(graphData, {
layoutOptions: config.layoutOptions,
});
}

export default RunLayout;
Loading

0 comments on commit e50d852

Please sign in to comment.