Comparing version 0.2.0 to 0.3.0
@@ -0,1 +1,14 @@ | ||
v0.3.0 | ||
====== | ||
**Backwards incompatible** changes: | ||
* Dagre now takes a `dagre.Digraph` or `dagre.Graph` as input for layout. See | ||
[README.md](README.md) for details. | ||
* `util` is no longer exported from dagre. | ||
Backwards compatible changes: | ||
* Dagre can now perform layout for undirected graphs (dagre.Graph). | ||
v0.2.0 | ||
@@ -2,0 +15,0 @@ ====== |
@@ -22,5 +22,5 @@ /* | ||
*/ | ||
exports.dot = require("./lib/dot"); | ||
exports.Digraph = require("graphlib").Digraph; | ||
exports.Graph = require("graphlib").Graph; | ||
exports.layout = require("./lib/layout/layout"); | ||
exports.util = require("./lib/util"); | ||
exports.version = require("./lib/version"); |
var util = require("../util"), | ||
rank = require("./rank"), | ||
acyclic = require("./acyclic"), | ||
Digraph = require("graphlib").Digraph; | ||
Digraph = require("graphlib").Digraph, | ||
Graph = require("graphlib").Graph, | ||
Set = require("graphlib").data.Set; | ||
@@ -9,6 +11,2 @@ module.exports = function() { | ||
var config = { | ||
// Nodes to lay out. At minimum must have `width` and `height` attributes. | ||
nodes: [], | ||
// Edges to lay out. At mimimum must have `source` and `target` attributes. | ||
edges: [], | ||
// How much debug information to include? | ||
@@ -28,5 +26,2 @@ debugLevel: 0, | ||
self.nodes = util.propertyAccessor(self, config, "nodes"); | ||
self.edges = util.propertyAccessor(self, config, "edges"); | ||
self.orderIters = delegateProperty(order.iterations); | ||
@@ -66,49 +61,29 @@ | ||
*/ | ||
function buildAdjacencyGraph() { | ||
function buildAdjacencyGraph(inputGraph) { | ||
var g = new Digraph(); | ||
var nextId = 0; | ||
// Get the node id for the type ("source" or "target") or throw if we | ||
// haven't seen the node. | ||
function safeGetNodeId(type, edge) { | ||
var nodeId; | ||
if (type in edge) { | ||
nodeId = edge[type].dagre.id; | ||
} else { | ||
if (!(type + "Id" in edge)) { | ||
throw new Error("Edge must have either a " + type + " or " + type + "Id attribute"); | ||
} | ||
nodeId = edge[type + "Id"]; | ||
edge[type] = g.node(nodeId); | ||
} | ||
if (!g.hasNode(nodeId)) { | ||
throw new Error(type + " node for '" + e + "' not in node list"); | ||
} | ||
return nodeId; | ||
} | ||
// Tag each node so that we can properly represent relationships when | ||
// we add edges. Also copy relevant dimension information. | ||
config.nodes.forEach(function(u) { | ||
var id = "id" in u ? u.id : "_N" + nextId++; | ||
u.dagre = { id: id, width: u.width, height: u.height }; | ||
g.addNode(id, u.dagre); | ||
inputGraph.eachNode(function(u, value) { | ||
if (value === undefined) value = {}; | ||
g.addNode(u, { | ||
width: value.width, | ||
height: value.height | ||
}); | ||
}); | ||
config.edges.forEach(function(e) { | ||
var source = safeGetNodeId("source", e); | ||
var target = safeGetNodeId("target", e); | ||
inputGraph.eachEdge(function(e, u, v, value) { | ||
if (value === undefined) value = {}; | ||
var newValue = { | ||
e: e, | ||
minLen: value.minLen || 1, | ||
width: value.width || 0, | ||
height: value.height || 0, | ||
points: [] | ||
}; | ||
e.dagre = { points: [] }; | ||
g.addEdge(null, u, v, newValue); | ||
// Track edges that aren't self loops - layout does nothing for self | ||
// loops, so they can be skipped. | ||
if (source !== target) { | ||
var id = "id" in e ? e.id : "_E" + nextId++; | ||
e.dagre.id = id; | ||
e.dagre.minLen = e.minLen || 1; | ||
e.dagre.width = e.width || 0; | ||
e.dagre.height = e.height || 0; | ||
g.addEdge(id, source, target, e.dagre); | ||
// If input graph is not directed, we create also add a reverse edge. | ||
// After we've run the acyclic algorithm we'll remove one of these edges. | ||
if (!inputGraph.isDirected()) { | ||
g.addEdge(null, v, u, newValue); | ||
} | ||
@@ -120,12 +95,13 @@ }); | ||
function run () { | ||
function run(inputGraph) { | ||
var rankSep = self.rankSep(); | ||
var g; | ||
try { | ||
if (!config.nodes.length) { | ||
return; | ||
// Build internal graph | ||
g = buildAdjacencyGraph(inputGraph); | ||
if (g.order() === 0) { | ||
return g; | ||
} | ||
// Build internal graph | ||
var g = buildAdjacencyGraph(); | ||
// Make space for edge labels | ||
@@ -141,2 +117,10 @@ g.eachEdge(function(e, s, t, a) { | ||
// Our intermediate graph is always directed. However, the input graph | ||
// may be undirected, so we create duplicate edges in opposite directions | ||
// in the buildAdjacencyGraph function. At this point one of each pair of | ||
// edges was reversed, so we remove the redundant edge. | ||
if (!inputGraph.isDirected()) { | ||
removeDupEdges(g); | ||
} | ||
// Determine the rank for each node. Nodes with a lower rank will appear | ||
@@ -166,7 +150,18 @@ // above nodes of higher rank. | ||
acyclic.undo(g); | ||
// Construct final result graph and return it | ||
return createFinalGraph(g, inputGraph.isDirected()); | ||
} finally { | ||
self.rankSep(rankSep); | ||
} | ||
} | ||
return self; | ||
function removeDupEdges(g) { | ||
var visited = new Set(); | ||
g.eachEdge(function(e, u, v, value) { | ||
if (visited.has(value.e)) { | ||
g.delEdge(e); | ||
} | ||
visited.add(value.e); | ||
}); | ||
} | ||
@@ -222,4 +217,2 @@ | ||
function undoNormalize(g) { | ||
var visited = {}; | ||
g.eachNode(function(u, a) { | ||
@@ -246,2 +239,12 @@ if (a.dummy && "index" in a) { | ||
function createFinalGraph(g, isDirected) { | ||
var out = isDirected ? new Digraph() : new Graph(); | ||
g.eachNode(function(u, value) { out.addNode(u, value); }); | ||
g.eachEdge(function(e, u, v, value) { | ||
out.addEdge("e" in value ? value.e : e, u, v, value); | ||
delete value.e; | ||
}); | ||
return out; | ||
} | ||
/* | ||
@@ -248,0 +251,0 @@ * Given a function, a new function is returned that invokes the given |
var util = require("../util"), | ||
components = require("../algo/components"), | ||
prim = require("../algo/prim"), | ||
PriorityQueue = require("../data/PriorityQueue"), | ||
Set = require("../data/Set"); | ||
components = require("graphlib").alg.components, | ||
filter = require("graphlib").filter; | ||
PriorityQueue = require("graphlib").data.PriorityQueue, | ||
Set = require("graphlib").data.Set; | ||
@@ -13,3 +13,3 @@ module.exports = function(g, debugLevel) { | ||
components(g).forEach(function(cmpt) { | ||
var subgraph = g.subgraph(cmpt); | ||
var subgraph = g.filterNodes(filter.nodesFromList(cmpt)); | ||
feasibleTree(subgraph); | ||
@@ -16,0 +16,0 @@ normalize(subgraph); |
/* | ||
* Copies attributes from `src` to `dst`. If an attribute name is in both | ||
* `src` and `dst` then the attribute value from `src` takes precedence. | ||
*/ | ||
exports.mergeAttributes = function(src, dst) { | ||
Object.keys(src).forEach(function(k) { dst[k] = src[k]; }); | ||
}; | ||
/* | ||
* Returns the smallest value in the array. | ||
@@ -45,9 +37,2 @@ */ | ||
/* | ||
* Joins all of the given arrays into a single array. | ||
*/ | ||
exports.concat = function(arrays) { | ||
return Array.prototype.concat.apply([], arrays); | ||
}; | ||
/* | ||
* Returns an array of all values in the given object. | ||
@@ -59,63 +44,2 @@ */ | ||
/* | ||
* Treats each input array as a set and returns the union of all of the arrays. | ||
* This function biases towards the last array. That is, if an "equivalent" | ||
* key appears in more than on array, the resulting array will contain the last | ||
* "equivalent" key. | ||
*/ | ||
exports.union = function(arrays) { | ||
var obj = {}; | ||
for (var i = 0; i < arrays.length; ++i) { | ||
var a = arrays[i]; | ||
for (var j = 0; j < a.length; ++j) { | ||
var v = a[j]; | ||
obj[v] = v; | ||
} | ||
} | ||
var results = []; | ||
for (var k in obj) { | ||
results.push(obj[k]); | ||
} | ||
return results; | ||
}; | ||
exports.intersectRect = function(rect, point) { | ||
var x = rect.x; | ||
var y = rect.y; | ||
// For now we only support rectangles | ||
// Rectangle intersection algorithm from: | ||
// http://math.stackexchange.com/questions/108113/find-edge-between-two-boxes | ||
var dx = point.x - x; | ||
var dy = point.y - y; | ||
var w = rect.width / 2; | ||
var h = rect.height / 2; | ||
var sx, sy; | ||
if (Math.abs(dy) * w > Math.abs(dx) * h) { | ||
// Intersection is top or bottom of rect. | ||
if (dy < 0) { | ||
h = -h; | ||
} | ||
sx = dy === 0 ? 0 : h * dx / dy; | ||
sy = h; | ||
} else { | ||
// Intersection is left or right of rect. | ||
if (dx < 0) { | ||
w = -w; | ||
} | ||
sx = w; | ||
sy = dx === 0 ? 0 : w * dy / dx; | ||
} | ||
return {x: x + sx, y: y + sy}; | ||
}; | ||
exports.pointStr = function(point) { | ||
return point.x + "," + point.y; | ||
}; | ||
exports.createTimer = function(enabled) { | ||
@@ -122,0 +46,0 @@ var self = {}; |
@@ -1,1 +0,1 @@ | ||
module.exports = '0.2.0'; | ||
module.exports = '0.3.0'; |
{ | ||
"name": "dagre", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "Directed graph rendering", | ||
@@ -11,3 +11,9 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "make test" | ||
"blanket": { | ||
"pattern": "lib", | ||
"data-cover-never": [ | ||
"node_modules", | ||
"test" | ||
] | ||
} | ||
}, | ||
@@ -18,11 +24,12 @@ "keywords": [ | ||
"devDependencies": { | ||
"blanket": "1.x.x", | ||
"browserify": "2.28.x", | ||
"chai": "1.7.x", | ||
"graphlib-dot": "0.4.1", | ||
"mocha": "1.12.x", | ||
"semver": "2.1.x", | ||
"uglify-js": "2.4.x" | ||
"uglify-js": "1.2.3" | ||
}, | ||
"dependencies": { | ||
"graphlib": "0.1.x", | ||
"graphlib-dot": "0.0.x" | ||
"graphlib": "0.5.7" | ||
}, | ||
@@ -29,0 +36,0 @@ "author": "Chris Pettitt <chris@samsarin.com>", |
113
README.md
@@ -30,11 +30,2 @@ # dagre - Directed graph rendering | ||
## Demo | ||
Try our [interactive demo](http://cpettitt.github.com/project/dagre/latest/demo/demo.html)! | ||
Or see an example of rendering the [TCP state diagram](http://cpettitt.github.com/project/dagre/latest/demo/demo-d3.html). | ||
These demos and more can be found in the `demo` folder of the project. Simply | ||
open them in your browser - there is no need to start a web server. | ||
## Getting Dagre | ||
@@ -76,18 +67,9 @@ | ||
For a simple starting point, we suggest looking at the user contributed | ||
`demo/dagre-d3-simple.js` script for doing good but easy rendering. To get | ||
started look at `demo/sentence-tokenization.html`. | ||
There are a couple of options for rendering: | ||
Another option is to use [JointJS][]. See the link for an article about how to | ||
use it with Dagre. The ability to manipulate nodes and edges after they are | ||
layed out and renderer can be very useful! | ||
* [dagre-d3](https://github.com/cpettitt/dagre-d3) is a D3-based renderer for | ||
dagre. | ||
* [JointJS][] is a renderer that provides facilities for editing a graph after | ||
it has been rendered. | ||
For even more control over rendering you can write a custom renderer or take | ||
advantage or a library like [D3][], which makes it easier to creates graphs | ||
with SVG. The code in `demo/dagre-d3-simple.js` uses D3, so this may be a | ||
good place to start. | ||
Ultimately we plan to make available some basic renderers for Dagre in the | ||
near future. | ||
### An Example Layout | ||
@@ -108,7 +90,8 @@ | ||
We use [graphlib](https://github.com/cpettitt/graphlib) to create graphs in | ||
dagre, so its probably worth taking a look at its | ||
[API](http://cpettitt.github.io/project/graphlib/latest/doc/index.html). | ||
Graphlib comes bundled with dagre-d3. In this section, we'll show you how to | ||
create a simple graph. | ||
The current version of Dagre takes an array of nodes and edges, along with some | ||
optional configuration, and attaches layout information to the nodes and edges | ||
in the `dagre` property. | ||
A node must be an object with the following properties: | ||
@@ -122,31 +105,27 @@ | ||
An edge must be an object with either references to its adjacent nodes: | ||
* `source` - a reference to the source node | ||
* `target` - a reference to the target node | ||
Or with the ids of its adjacent nodes: | ||
* `sourceId` - the id of the source node | ||
* `targetId` - the id of the target node | ||
Here's a quick example of how to set up nodes and edges: | ||
```js | ||
var nodes = [ | ||
{ id: "kspacey", label: "Kevin Spacey", width: 144, height: 100 }, | ||
{ id: "swilliams", label: "Saul Williams", width: 168, height: 100 }, | ||
{ id: "bpitt", label: "Brad Pitt", width: 108, height: 100 }, | ||
{ id: "hford", label: "Harrison Ford", width: 168, height: 100 }, | ||
{ id: "oplat", label: "Oliver Platt", width: 144, height: 100 }, | ||
{ id: "kbacon", label: "Kevin Bacon", width: 121, height: 100 }, | ||
// Create a new directed graph | ||
var g = new dagre.Digraph(); | ||
]; | ||
var edges = [ | ||
{ sourceId: "kspacey", targetId: "swilliams" }, | ||
{ sourceId: "swilliams", targetId: "kbacon" }, | ||
{ sourceId: "bpitt", targetId: "kbacon" }, | ||
{ sourceId: "hford", targetId: "oplat" }, | ||
{ sourceId: "oplat", targetId: "kbacon" } | ||
]; | ||
// Add nodes to the graph. The first argument is the node id. The second is | ||
// metadata about the node. In this case we're going to add labels to each of | ||
// our nodes. | ||
g.addNode("kspacey", { label: "Kevin Spacey", width: 144, height: 100 }); | ||
g.addNode("swilliams", { label: "Saul Williams", width: 160, height: 100 }); | ||
g.addNode("bpitt", { label: "Brad Pitt", width: 108, height: 100 }); | ||
g.addNode("hford", { label: "Harrison Ford", width: 168, height: 100 }); | ||
g.addNode("lwilson", { label: "Luke Wilson", width: 144, height: 100 }); | ||
g.addNode("kbacon", { label: "Kevin Bacon", width: 121, height: 100 }); | ||
// Add edges to the graph. The first argument is the edge id. Here we use null | ||
// to indicate that an arbitrary edge id can be assigned automatically. The | ||
// second argument is the source of the edge. The third argument is the target | ||
// of the edge. | ||
g.addEdge(null, "kspacey", "swilliams"); | ||
g.addEdge(null, "swilliams", "kbacon"); | ||
g.addEdge(null, "bpitt", "kbacon"); | ||
g.addEdge(null, "hford", "lwilson"); | ||
g.addEdge(null, "lwilson", "kbacon"); | ||
``` | ||
@@ -158,6 +137,3 @@ | ||
```js | ||
dagre.layout() | ||
.nodes(nodes) | ||
.edges(edges) | ||
.run(); | ||
var layout = dagre.layout().run(g); | ||
``` | ||
@@ -182,8 +158,7 @@ | ||
```js | ||
nodes.forEach(function(u) { | ||
console.log("Node " + u.dagre.id + ": " + JSON.stringify(u.dagre)); | ||
layout.eachNode(function(u, value) { | ||
console.log("Node " + u + ": " + JSON.stringify(value)); | ||
}); | ||
edges.forEach(function(e) { | ||
console.log("Edge " + e.sourceId + " -> " + e.targetId + ": " + | ||
JSON.stringify(e.dagre)); | ||
layout.eachEdge(function(e, u, v, value) { | ||
console.log("Edge " + u + " -> " + v + ": " + JSON.stringify(value)); | ||
}); | ||
@@ -199,3 +174,3 @@ ``` | ||
Node hford: {"id":"hford","width":168,"height":100,"rank":0,"order":1,"ul":364,"ur":364,"dl":364,"dr":364,"x":448,"y":50} | ||
Node oplat: {"id":"oplat","width":144,"height":100,"rank":2,"order":2,"ul":364,"ur":364,"dl":364,"dr":364,"x":448,"y":180} | ||
Node lwilson: {"id":"lwilson","width":144,"height":100,"rank":2,"order":2,"ul":364,"ur":364,"dl":364,"dr":364,"x":448,"y":180} | ||
Node kbacon: {"id":"kbacon","width":121,"height":100,"rank":4,"order":0,"ul":188,"ur":188,"dl":0,"dr":364,"x":272,"y":310} | ||
@@ -206,4 +181,4 @@ | ||
Edge bpitt -> kbacon: {"points":[{"x":272,"y":245,"ul":188,"ur":188,"dl":188,"dr":188}],"id":"_E2","minLen":2,"width":0,"height":0} | ||
Edge hford -> oplat: {"points":[{"x":448,"y":115,"ul":364,"ur":364,"dl":364,"dr":364}],"id":"_E3","minLen":2,"width":0,"height":0} | ||
Edge oplat -> kbacon: {"points":[{"x":448,"y":245,"ul":364,"ur":364,"dl":364,"dr":364}],"id":"_E4","minLen":2,"width":0,"height":0} | ||
Edge hford -> lwilson: {"points":[{"x":448,"y":115,"ul":364,"ur":364,"dl":364,"dr":364}],"id":"_E3","minLen":2,"width":0,"height":0} | ||
Edge lwilson -> kbacon: {"points":[{"x":448,"y":245,"ul":364,"ur":364,"dl":364,"dr":364}],"id":"_E4","minLen":2,"width":0,"height":0} | ||
``` | ||
@@ -229,8 +204,6 @@ | ||
```js | ||
dagre.layout() | ||
.nodes(nodes) | ||
.edges(edges) | ||
.nodeSep(20) | ||
.rankDir("LR") | ||
.run(); | ||
var layout = dagre.layout() | ||
.nodeSep(20) | ||
.rankDir("LR") | ||
.run(g); | ||
``` | ||
@@ -237,0 +210,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1
47696
7
14
1008
258
1
+ Addedgraphlib@0.5.7(transitive)
- Removedgraphlib-dot@0.0.x
- Removedgraphlib@0.1.10.2.1(transitive)
- Removedgraphlib-dot@0.0.4(transitive)
Updatedgraphlib@0.5.7