first working version

This commit is contained in:
gmamaladze 2017-09-01 14:54:55 +02:00
parent 958b7e0d0f
commit d873eacbcc
10 changed files with 5199 additions and 145 deletions

2491
build/d3-dot-graph.js vendored Normal file

File diff suppressed because it is too large Load diff

1
build/d3-dot-graph.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -23,8 +23,7 @@
</style> </style>
<svg width="960" height="600"></svg> <svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script> <script src="https://d3js.org/d3.v4.min.js"></script>
<script src="/grammar/dot.js"></script> <script src="../build/d3-dot-graph.js"></script>
<script src="/d3-dot-graph.js"></script>
<script> <script>
var line = d3.line() var line = d3.line()
@ -52,7 +51,7 @@ svg.append("svg:defs").selectAll("marker")
.attr("id", String) .attr("id", String)
.attr("viewBox", "0 -5 10 10") .attr("viewBox", "0 -5 10 10")
.attr("refX", 15) .attr("refX", 15)
.attr("refY", -1.5) .attr("refY", -0.5)
.attr("markerWidth", 6) .attr("markerWidth", 6)
.attr("markerHeight", 6) .attr("markerHeight", 6)
.attr("orient", "auto") .attr("orient", "auto")
@ -102,10 +101,17 @@ var path = svg.append("svg:g").attr("class", "links").selectAll("path")
.radius(function(d) { return d.y; }); .radius(function(d) { return d.y; });
function ticked() { function ticked() {
path.attr("d", path.attr("d", function(d) {
(d)=>linkGen(d)) var dx = d.target.x - d.source.x,
//(d) => line([[d.source.x, d.source.y], [d.target.x, d.target.y]])); dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" +
d.source.x + "," +
d.source.y + "A" +
dr + "," + dr + " 0 0,1 " +
d.target.x + "," +
d.target.y;
});
node node
.attr("transform", function(d) { .attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; }); return "translate(" + d.x + "," + d.y + ")"; });

173
grammar/dot.pegjs Normal file
View file

@ -0,0 +1,173 @@
// Simplified DOT grammar
//
// Not supported (yet):
//
// * HTML IDs
{
function merge(a, b, key) {
function x(a) {
a.forEach(function (b) {
if (!(b[key] in obj)) {
obj[b[key]] = obj[b[key]] || {};
array.push(obj[b[key]]);
}
Object.keys(b).forEach(function (k) {
obj[b[key]][k] = b[k];
});
});
}
var array = [],
obj = {};
x(a);
x(b);
return array;
}
var directed;
}
start
= graphStmt+
graphStmt
= _* strict:(strict _)? type:graphType _* id:(id)? _* '{' _* stmts:stmtList? _* '}' _* {
return {type: type, id: id, strict: strict !== null, stmts: stmts};
}
stmtList
= first:stmt _* ';'? rest:(_* inner:stmt _* ';'?)* {
var result = [first];
for (var i = 0; i < rest.length; ++i) {
result.push(rest[i][1]);
}
return result;
}
stmt
= attrStmt
/ edgeStmt
/ subgraphStmt
/ inlineAttrStmt
/ nodeStmt
attrStmt
= type:(graph / node /edge) _* attrs:attrList {
return { type: "attr", attrType: type, attrs: attrs || {}};
}
inlineAttrStmt
= k:id _* '=' _* v:id {
var attrs = {};
attrs[k] = v;
return { type: "inlineAttr", attrs: attrs };
}
nodeStmt
= id:nodeId _* attrs:attrList? { return {type: "node", id: id, attrs: attrs || {}}; }
edgeStmt
= lhs:(nodeIdOrSubgraph) _* rhs:edgeRHS _* attrs:attrList? {
var elems = [lhs];
for (var i = 0; i < rhs.length; ++i) {
elems.push(rhs[i]);
}
return { type: "edge", elems: elems, attrs: attrs || {} };
}
subgraphStmt
= id:(subgraph _* (id _*)?)? '{' _* stmts:stmtList? _* '}' {
id = (id && id[2]) || [];
return { type: "subgraph", id: id[0], stmts: stmts };
}
attrList
= first:attrListBlock rest:(_* attrListBlock)* {
var result = first;
for (var i = 0; i < rest.length; ++i) {
merge(result, rest[i][1]);
}
return result;
}
attrListBlock
= '[' _* aList:aList? _* ']' { return aList; }
aList
= first:idDef rest:(_* ','? _* idDef)* {
var result = first;
for (var i = 0; i < rest.length; ++i) {
_.merge(result, rest[i][3]);
}
return result;
}
edgeRHS
= ("--" !{ return directed; } / "->" &{ return directed; }) _* rhs:(nodeIdOrSubgraph) _* rest:edgeRHS? {
var result = [rhs];
if (rest) {
for (var i = 0; i < rest.length; ++i) {
result.push(rest[i]);
}
}
return result;
}
idDef
= k:id v:(_* '=' _* id)? {
var result = {};
result[k] = v[3];
return result;
}
nodeIdOrSubgraph
= subgraphStmt
/ id:nodeId { return { type: "node", id: id, attrs: {} }; }
nodeId
= id:id _* port? { return id; }
port
= ':' _* id _* (':' _* compassPt)?
compassPt
= "ne" / "se" / "sw" / "nw" / "n" / "e" / "s" / "w" / "c" / "_"
id "identifier"
= fst:[a-zA-Z\u0200-\u0377_] rest:[a-zA-Z\u0200-\u0377_0-9]* { return fst + rest.join(""); }
/ sign:'-'? dot:'.' after:[0-9]+ {
return (sign || "") + dot + after.join("");
}
/ sign:'-'? before:[0-9]+ after:('.' [0-9]*)? {
return (sign || "") + before.join("") + (after ? after[0] : "") + (after ? after[1].join("") : "");
}
/ '"' id:("\\\"" { return '"'; } / "\\" ch:[^"] { return "\\" + ch; } / [^"])* '"' {
return id.join("");
}
node = k:"node"i { return k.toLowerCase(); }
edge = k:"edge"i { return k.toLowerCase(); }
graph = k:"graph"i { return k.toLowerCase(); }
digraph = k:"digraph"i { return k.toLowerCase(); }
subgraph = k:"subgraph"i { return k.toLowerCase(); }
strict = k:"strict"i { return k.toLowerCase(); }
graphType
= graph:graph / graph:digraph {
directed = graph === "digraph";
return graph;
}
whitespace "whitespace"
= [ \t\r\n]+
comment "comment"
= "//" ([^\n])*
/ "/*" (!"*/" .)* "*/"
_
= whitespace
/ comment

View file

@ -5,17 +5,17 @@
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "mocha test/*.js", "test": "mocha test/*.js",
"build": "pegjs --format globals --export-var d3dot grammar/dot.pegjs ", "build": "pegjs --format globals --export-var d3dotparser1 --output src/dot-parser.js grammar/dot.pegjs; rollup -c",
"prepublish": "rollup -c && uglifyjs build/d3-dot-graph.js -c negate_iife=false -m -o build/d3-dot-graph.min.js",
"start": "npm run build" "start": "npm run build"
}, },
,
"keywords": [ "keywords": [
"dot", "dot",
"graphviz", "graphviz",
"d3", "d3",
"graph" "graph"
], ],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/gmamaladze/d3-dot-graph.git" "url": "git+https://github.com/gmamaladze/d3-dot-graph.git"
}, },
@ -25,5 +25,9 @@
"url": "https://github.com/gmamaladze/d3-dot-graph/issues" "url": "https://github.com/gmamaladze/d3-dot-graph/issues"
}, },
"homepage": "https://github.com/gmamaladze/d3-dot-graph#readme", "homepage": "https://github.com/gmamaladze/d3-dot-graph#readme",
"dependencies": {} "dependencies": {
"pegjs": "^0.10.0",
"rollup": "^0.49.2",
"rollup-watch": "^4.3.1"
}
} }

9
rollup.config.js Normal file
View file

@ -0,0 +1,9 @@
export default {
input: "src/index.js",
name: "d3dot",
context: "window",
output: {
format: "umd",
file: "build/d3-dot-graph.js"
}
};

2491
src/dot-parser.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,131 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links path {
fill: none;
stroke: #666;
stroke-width: 1.5px;
}
.nodes circle {
fill: #ccc;
stroke: #fff;
stroke-width: 1.5px;
}
.nodes text {
fill: #000;
font: 10px sans-serif;
pointer-events: none;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="/grammar/dot.js"></script>
<script src="/d3-dot-graph.js"></script>
<script>
var line = d3.line()
.curve(d3.curveCatmullRom.alpha(0.5));
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().distance(100).id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-10))
.force("collide", d3.forceCollide(50))
.force("center", d3.forceCenter(width / 2, height / 2));
d3.dot("summary.dot", function(graph) {
//if (error) throw error;
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");;
// add the links and the arrows
var path = svg.append("svg:g").attr("class", "links").selectAll("path")
.data(graph.links)
.enter().append("svg:path")
// .attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", "url(#end)");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("g")
node
.append("circle")
.attr("r", 5)
//.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// add the text
node.append("text")
.attr("x", 12)
.attr("dy", ".35em")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
let linkGen = d3.linkVertical().x(function(d) { return d.x; })
.y(function(d) { return d.y; });;
var linkRad = d3.linkRadial()
.angle(function(d) { return d.x; })
.radius(function(d) { return d.y; });
function ticked() {
path.attr("d",
(d)=>linkGen(d))
//(d) => line([[d.source.x, d.source.y], [d.target.x, d.target.y]]));
node
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")"; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>

5
src/index.js Normal file
View file

@ -0,0 +1,5 @@
'use strict';
import load from "./load.js";
d3.dot = load;

View file

@ -1,11 +1,16 @@
d3.dot = 'use strict';
function(url, converter, callback) {
import "./dot-parser.js";
const dotparser = d3dotparser1;
export default function(url, converter, callback) {
if (arguments.length < 3) callback = converter, converter = simple; if (arguments.length < 3) callback = converter, converter = simple;
var r = d3 var r = d3
.request(url) .request(url)
.mimeType("text/vnd.graphviz") .mimeType("text/vnd.graphviz")
.response(function(xhr) { .response(function(xhr) {
return converter(d3dot.parse(xhr.responseText)); }); return converter(dotparser.parse(xhr.responseText)); });
return callback ? r.get(callback) : r; return callback ? r.get(callback) : r;
}; };