diff --git a/cypress/platform/current.html b/cypress/platform/current.html
index abc63ea48..50cb3ed3c 100644
--- a/cypress/platform/current.html
+++ b/cypress/platform/current.html
@@ -70,7 +70,7 @@
Moving --> Crash
Crash --> [*]
-
+
stateDiagram-v2
[*] --> First
First --> Second
@@ -84,6 +84,12 @@ stateDiagram-v2
[*] --> sec
sec --> [*]
}
+
+
+flowchart TD
+ subgraph Apa
+ a --> b
+ end
stateDiagram-v2
diff --git a/src/dagre-wrapper/clusters.js b/src/dagre-wrapper/clusters.js
index 55db0b32e..02da85c58 100644
--- a/src/dagre-wrapper/clusters.js
+++ b/src/dagre-wrapper/clusters.js
@@ -1,8 +1,10 @@
import intersectRect from './intersect/intersect-rect';
-import { logger } from '../logger'; // eslint-disable-line
+import { logger as log } from '../logger'; // eslint-disable-line
import createLabel from './createLabel';
const rect = (parent, node) => {
+ log.info('Creating subgraph rect for ', node.id, node);
+
// Add outer g element
const shapeSvg = parent
.insert('g')
@@ -22,7 +24,10 @@ const rect = (parent, node) => {
const padding = 0 * node.padding;
const halfPadding = padding / 2;
+ const width = node.width || 50;
+ const height = node.height || 50;
+ log.info('Data ', node, JSON.stringify(node));
// center the rect around its coordinate
rect
.attr('rx', node.rx)
@@ -32,7 +37,7 @@ const rect = (parent, node) => {
.attr('width', node.width + padding)
.attr('height', node.height + padding);
- // logger.info('bbox', bbox.width, node.x, node.width);
+ // log.info('bbox', bbox.width, node.x, node.width);
// Center the label
// label.attr('transform', 'translate(' + adj + ', ' + (node.y - node.height / 2) + ')');
label.attr(
@@ -127,7 +132,7 @@ const roundedWithTitle = (parent, node) => {
.attr('width', node.width + padding)
.attr('height', node.height + padding - bbox.height - 3);
- // logger.info('bbox', bbox.width, node.x, node.width);
+ // log.info('bbox', bbox.width, node.x, node.width);
// Center the label
// label.attr('transform', 'translate(' + adj + ', ' + (node.y - node.height / 2) + ')');
label.attr(
@@ -155,7 +160,9 @@ const shapes = { rect, roundedWithTitle, noteGroup };
let clusterElems = {};
export const insertCluster = (elem, node) => {
- clusterElems[node.id] = shapes[node.shape](elem, node);
+ log.info('Inserting cluster');
+ const shape = node.shape || 'rect';
+ clusterElems[node.id] = shapes[shape](elem, node);
};
export const getClusterTitleWidth = (elem, node) => {
const label = createLabel(node.labelText, node.labelStyle);
@@ -170,6 +177,8 @@ export const clear = () => {
};
export const positionCluster = node => {
+ log.info('Position cluster');
const el = clusterElems[node.id];
+
el.attr('transform', 'translate(' + node.x + ', ' + node.y + ')');
};
diff --git a/src/dagre-wrapper/index.js b/src/dagre-wrapper/index.js
index eb0427215..f3dd6c885 100644
--- a/src/dagre-wrapper/index.js
+++ b/src/dagre-wrapper/index.js
@@ -1,62 +1,38 @@
import dagre from 'dagre';
+import graphlib from 'graphlib';
import insertMarkers from './markers';
-import { clear as cleargraphlib, clusterDb, adjustClustersAndEdges } from './mermaid-graphlib';
-import { insertNode, positionNode, clear as clearNodes } from './nodes';
-import { insertCluster, clear as clearClusters } from './clusters';
+import { updateNodeBounds } from './shapes/util';
+import {
+ clear as clearGraphlib,
+ clusterDb,
+ adjustClustersAndEdges,
+ findNonClusterChild
+} from './mermaid-graphlib';
+import { insertNode, positionNode, clear as clearNodes, setNodeElem } from './nodes';
+import { insertCluster, clear as clearClusters, positionCluster } from './clusters';
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges';
-import { logger } from '../logger';
+import { logger as log } from '../logger';
// let clusterDb = {};
const getAnchorId = id => {
// Only insert an achor once
if (clusterDb[id]) {
- // if (!clusterDb[id].inserted) {
- // // Create anchor node for cluster
- // const anchorData = {
- // shape: 'start',
- // labelText: '',
- // classes: '',
- // style: '',
- // id: id + '_anchor',
- // type: 'anchor',
- // padding: 0
- // };
- // insertNode(nodes, anchorData);
-
- // graph.setNode(anchorData.id, anchorData);
- // graph.setParent(anchorData.id, id);
- // clusterDb[id].inserted = true;
- // }
return clusterDb[id].id;
}
return id;
};
-const findNonClusterChild = (id, graph) => {
- const node = graph.node(id);
- logger.info('identified node', node);
- if (node.type !== 'group') {
- return node.id;
+const recursiveRender = (_elem, graph, diagramtype) => {
+ const elem = _elem.insert('g').attr('class', 'root'); // eslint-disable-line
+ if (!graph.nodes()) {
+ log.info('No nodes found for', graph);
+ } else {
+ log.info('Recursive render', graph.edges());
}
- logger.info('identified node Not', node.id);
- const children = graph.children(id);
- for (let i = 0; i < children.length; i++) {
- const _id = findNonClusterChild(children[i], graph);
- if (_id) {
- return _id;
- }
+ if (graph.edges().length > 0) {
+ log.info('Recursive edges', graph.edge(graph.edges()[0]));
}
-};
-
-export const render = (elem, graph, markers, diagramtype, id) => {
- insertMarkers(elem, markers, diagramtype, id);
- clearNodes();
- clearEdges();
- clearClusters();
-
- adjustClustersAndEdges(graph);
-
const clusters = elem.insert('g').attr('class', 'clusters'); // eslint-disable-line
const edgePaths = elem.insert('g').attr('class', 'edgePaths');
const edgeLabels = elem.insert('g').attr('class', 'edgeLabels');
@@ -66,81 +42,115 @@ export const render = (elem, graph, markers, diagramtype, id) => {
// to the abstract node and is later used by dagre for the layout
graph.nodes().forEach(function(v) {
const node = graph.node(v);
- logger.info('Node ' + v + ': ' + JSON.stringify(graph.node(v)));
- if (node.type !== 'group') {
- insertNode(nodes, graph.node(v));
- } else {
+ log.info('(Insert) Node ' + v + ': ' + JSON.stringify(graph.node(v)));
+ if (node.clusterNode) {
// const children = graph.children(v);
- // logger.info('Cluster identified', node.id, children[0], findNonClusterChild(node.id, graph));
- // clusterDb[node.id] = { id: findNonClusterChild(node.id, graph) };
+ log.info('Cluster identified', v, node, graph.node(v));
+ const newEl = recursiveRender(clusters, node.graph, diagramtype);
+ updateNodeBounds(node, newEl);
+ setNodeElem(newEl, node);
+
+ log.info('Recursice render complete', newEl, node);
+ } else {
+ if (graph.children(v).length > 0) {
+ // This is a cluster but not to be rendered recusively
+ // Render as before
+ log.info('Cluster - the non recursive path', v, node.id, node, graph);
+ log.info(findNonClusterChild(node.id, graph));
+ clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node };
+ // insertCluster(clusters, graph.node(v));
+ } else {
+ log.info('Node - the non recursive path', v, node.id, node);
+ insertNode(nodes, graph.node(v));
+ }
}
});
- logger.info('Clusters ', clusterDb);
+ log.info('Clusters ', clusterDb);
// Insert labels, this will insert them into the dom so that the width can be calculated
// Also figure out which edges point to/from clusters and adjust them accordingly
// Edges from/to clusters really points to the first child in the cluster.
// TODO: pick optimal child in the cluster to us as link anchor
graph.edges().forEach(function(e) {
- const edge = graph.edge(e);
- logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
- logger.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
+ const edge = graph.edge(e.v, e.w, e.name);
+ log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
+ log.info(
+ 'Edge ' + e.v + ' -> ' + e.w + ': ',
+ e,
+ ' ',
+ +JSON.stringify(graph.edge(e.v, e.w, e.name))
+ );
let v = e.v;
let w = e.w;
// Check if link is either from or to a cluster
- logger.info(
- 'Fix',
- clusterDb,
- 'ids:',
- e.v,
- e.w,
- 'Translateing: ',
- clusterDb[e.v],
- clusterDb[e.w]
- );
+ log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
if (clusterDb[e.v] || clusterDb[e.w]) {
- logger.info('Fixing and trixing - rwemoving', e.v, e.w, e.name);
+ log.info('Fixing and trixing - removing', e.v, e.w, e.name);
v = getAnchorId(e.v, graph, nodes);
w = getAnchorId(e.w, graph, nodes);
graph.removeEdge(e.v, e.w, e.name);
if (v !== e.v) edge.fromCluster = e.v;
if (w !== e.w) edge.toCluster = e.w;
- logger.info('Fixing Replacing with', v, w, e.name);
+ log.info('Fixing Replacing with', v, w, e.name);
graph.setEdge(v, w, edge, e.name);
}
insertEdgeLabel(edgeLabels, edge);
});
graph.edges().forEach(function(e) {
- logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
+ log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
});
- logger.info('#############################################');
- logger.info('### Layout ###');
- logger.info('#############################################');
- logger.info(graph);
+ log.info('#############################################');
+ log.info('### Layout ###');
+ log.info('#############################################');
+ log.info(graph);
dagre.layout(graph);
// Move the nodes to the correct place
graph.nodes().forEach(function(v) {
const node = graph.node(v);
- logger.trace('Node ' + v + ': ' + JSON.stringify(graph.node(v)));
- if (node.type !== 'group') {
- positionNode(node);
+ log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
+ if (node && node.clusterNode) {
+ // clusterDb[node.id].node = node;
+ // positionNode(node);
} else {
- insertCluster(clusters, node);
- clusterDb[node.id].node = node;
+ // Non cluster node
+ if (graph.children(v).length > 0) {
+ // A cluster in the non-recurive way
+ // positionCluster(node);
+ insertCluster(clusters, node);
+ clusterDb[node.id].node = node;
+ } else {
+ positionNode(node);
+ }
}
});
// Move the edge labels to the correct place after layout
graph.edges().forEach(function(e) {
const edge = graph.edge(e);
- logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
+ log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
insertEdge(edgePaths, edge, clusterDb, diagramtype);
positionEdgeLabel(edge);
});
+
+ return elem;
+};
+
+export const render = (elem, graph, markers, diagramtype, id) => {
+ insertMarkers(elem, markers, diagramtype, id);
+ clearNodes();
+ clearEdges();
+ clearClusters();
+ clearGraphlib();
+
+ log.warn('Graph before:', graphlib.json.write(graph));
+ adjustClustersAndEdges(graph);
+ log.warn('Graph after:', graphlib.json.write(graph));
+
+ recursiveRender(elem, graph, diagramtype);
};
// const shapeDefinitions = {};
diff --git a/src/dagre-wrapper/mermaid-graphlib.js b/src/dagre-wrapper/mermaid-graphlib.js
index ae4e4086c..a232c09db 100644
--- a/src/dagre-wrapper/mermaid-graphlib.js
+++ b/src/dagre-wrapper/mermaid-graphlib.js
@@ -13,7 +13,7 @@ export const clear = () => {
};
const copy = (clusterId, graph, newGraph, rootId) => {
- logger.trace('Copying ', clusterId);
+ logger.info('Copying ', clusterId, graph.node(clusterId));
const nodes = graph.children(clusterId);
nodes.forEach(node => {
if (graph.children(node).length > 0) {
@@ -21,22 +21,29 @@ const copy = (clusterId, graph, newGraph, rootId) => {
}
const data = graph.node(node);
- logger.trace(node, data, ' parent is ', clusterId);
+ logger.info(node, data, ' parent is ', clusterId);
newGraph.setNode(node, data);
newGraph.setParent(node, clusterId);
const edges = graph.edges(node);
- graph.removeNode(node);
- logger.trace('Edges', edges);
+ logger.info('Copying Edges', edges);
edges.forEach(edge => {
- const data = graph.edge(edge);
- // Do not copy edges in and out of the root cluster, they belong to the parent graph
- if (!(edge.v === rootId || edge.w === rootId)) {
- logger.trace('Copying as ', rootId, edge.v, edge.w, clusterId);
- newGraph.setEdge(edge.v, edge.w, data);
- } else {
- logger.trace('Skipping copy of edge as ', rootId, edge.v, edge.w, clusterId);
+ logger.info('Edge', edge);
+ const data = graph.edge(edge.v, edge.w, edge.name);
+ logger.info('Edge data', data, rootId);
+ try {
+ // Do not copy edges in and out of the root cluster, they belong to the parent graph
+ if (!(edge.v === rootId || edge.w === rootId)) {
+ logger.info('Copying as ', edge.v, edge.w, data, edge.name);
+ newGraph.setEdge(edge.v, edge.w, data, edge.name);
+ logger.info('newgrapg edges ', newGraph.edges(), newGraph.edge(newGraph.edges()[0]));
+ } else {
+ logger.info('Skipping copy of edge as ', rootId, edge.v, edge.w, clusterId);
+ }
+ } catch (e) {
+ logger.error(e);
}
});
+ graph.removeNode(node);
});
newGraph.setNode(clusterId, graph.node(clusterId));
};
@@ -60,8 +67,8 @@ export const extractGraphFromCluster = (clusterId, graph) => {
.setGraph({
rankdir: 'TB',
// Todo: set proper spacing
- nodesep: 10,
- ranksep: 10,
+ nodesep: 50,
+ ranksep: 50,
marginx: 8,
marginy: 8
})
@@ -69,6 +76,26 @@ export const extractGraphFromCluster = (clusterId, graph) => {
return {};
});
+ // const conf = getConfig().flowchart;
+ // const nodeSpacing = conf.nodeSpacing || 50;
+ // const rankSpacing = conf.rankSpacing || 50;
+
+ // // Create the input mermaid.graph
+ // const g = new graphlib.Graph({
+ // multigraph: true,
+ // compound: true
+ // })
+ // .setGraph({
+ // rankdir: 'TB',
+ // nodesep: nodeSpacing,
+ // ranksep: rankSpacing,
+ // marginx: 8,
+ // marginy: 8
+ // })
+ // .setDefaultEdgeLabel(function() {
+ // return {};
+ // });
+
copy(clusterId, graph, clusterGraph, clusterId);
return clusterGraph;
@@ -100,7 +127,7 @@ export const validate = graph => {
* @param {Finds a } id
* @param {*} graph
*/
-const findNonClusterChild = (id, graph) => {
+export const findNonClusterChild = (id, graph) => {
// const node = graph.node(id);
logger.trace('Searching', id);
const children = graph.children(id);
@@ -141,7 +168,7 @@ export const adjustClustersAndEdges = graph => {
graph.nodes().forEach(function(id) {
const children = graph.children(id);
if (children.length > 0) {
- logger.trace(
+ logger.info(
'Cluster identified',
id,
' Replacement id in edges: ',
@@ -157,13 +184,13 @@ export const adjustClustersAndEdges = graph => {
const children = graph.children(id);
const edges = graph.edges();
if (children.length > 0) {
- logger.trace('Cluster identified', id);
+ logger.info('Cluster identified', id);
edges.forEach(edge => {
- logger.trace('Edge: ', edge, decendants[id]);
+ logger.info('Edge: ', edge, decendants[id]);
// Check if any edge leaves the cluster (not the actual cluster, thats a link from the box)
if (edge.v !== id && edge.w !== id) {
if (decendants[id].indexOf(edge.v) < 0 || decendants[id].indexOf(edge.w) < 0) {
- logger.trace('Edge: ', edge, ' leaves cluster ', id);
+ logger.info('Edge: ', edge, ' leaves cluster ', id);
clusterDb[id].externalConnections = true;
}
}
@@ -189,7 +216,13 @@ export const adjustClustersAndEdges = graph => {
// Create a new node in the original graph, this new node is not a cluster
// but a regular node with the cluster dontent as a new attached graph
- graph.setNode(clusterId, { clusterNode: true, graph: clusterGraph });
+ graph.setNode(clusterId, {
+ clusterNode: true,
+ id: clusterId,
+ clusterData: clusterDb[clusterId],
+ labelText: clusterDb[clusterId].labelText,
+ graph: clusterGraph
+ });
// The original edges in and out of the cluster is applied
edges.forEach(edge => {
@@ -205,7 +238,7 @@ export const adjustClustersAndEdges = graph => {
graph.edges().forEach(function(e) {
const edge = graph.edge(e);
logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
- logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
+ logger.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
let v = e.v;
let w = e.w;
diff --git a/src/dagre-wrapper/mermaid-graphlib.spec.js b/src/dagre-wrapper/mermaid-graphlib.spec.js
index b842a455b..cd0c29b34 100644
--- a/src/dagre-wrapper/mermaid-graphlib.spec.js
+++ b/src/dagre-wrapper/mermaid-graphlib.spec.js
@@ -135,7 +135,7 @@ describe('Graphlib decorations', () => {
expect(newGraph.edges('a')).toEqual([{ v: 'a', w: 'b' }]);
});
- it('It is possible to extract a clusters to a new graph 2', function () {
+ it('It is possible to extract a clusters to a new graph 2 GLB1', function () {
/*
subgraph C1
a --> b
@@ -163,7 +163,7 @@ describe('Graphlib decorations', () => {
expect(g.children('C2')).toEqual([]);
expect(g.edges()).toEqual([{ v: 'C1', w: 'C2' }]);
- logger.info(C1.nodes());
+ logger.info(g.nodes());
expect(C1.nodes()).toEqual(['a', 'C1', 'b']);
expect(C1.children('C1')).toEqual(['a', 'b']);
expect(C1.edges()).toEqual([{ v: 'a', w: 'b' }]);
@@ -200,7 +200,7 @@ describe('Graphlib decorations', () => {
expect(g.children('C1')).toEqual([]);
});
});
- it('Validate should detect edges between clusters and transform clusters', function () {
+ it('Validate should detect edges between clusters and transform clusters GLB4', function () {
/*
a --> b
subgraph C1
@@ -225,4 +225,29 @@ describe('Graphlib decorations', () => {
expect(g.nodes().length).toBe(2);
expect(validate(g)).toBe(true);
});
+ it('Validate should detect edges between clusters and transform clusters GLB5', function () {
+ /*
+ a --> b
+ subgraph C1
+ a
+ end
+ subgraph C2
+ b
+ end
+ C1 -->
+ */
+ g.setNode('a', { data: 1 });
+ g.setNode('b', { data: 2 });
+ g.setParent('a', 'C1');
+ g.setParent('b', 'C2');
+ g.setParent('C1', 'C2');
+ // g.setEdge('a', 'b', { name: 'C1-internal-link' });
+ g.setEdge('C1', 'C2', { name: 'C1-external-link' });
+
+ logger.info(g.nodes())
+ adjustClustersAndEdges(g);
+ logger.info(g.nodes())
+ expect(g.nodes().length).toBe(2);
+ expect(validate(g)).toBe(true);
+ });
});
diff --git a/src/dagre-wrapper/nodes.js b/src/dagre-wrapper/nodes.js
index f1db09a43..47be56991 100644
--- a/src/dagre-wrapper/nodes.js
+++ b/src/dagre-wrapper/nodes.js
@@ -387,6 +387,9 @@ let nodeElems = {};
export const insertNode = (elem, node) => {
nodeElems[node.id] = shapes[node.shape](elem, node);
};
+export const setNodeElem = (elem, node) => {
+ nodeElems[node.id] = elem;
+};
export const clear = () => {
nodeElems = {};
};
diff --git a/src/diagrams/flowchart/flowRenderer-v2.js b/src/diagrams/flowchart/flowRenderer-v2.js
index e0cce8df8..f66bf947c 100644
--- a/src/diagrams/flowchart/flowRenderer-v2.js
+++ b/src/diagrams/flowchart/flowRenderer-v2.js
@@ -314,8 +314,10 @@ export const draw = function(text, id) {
let subG;
const subGraphs = flowDb.getSubGraphs();
+ logger.info('Subgraphs - ', subGraphs);
for (let i = subGraphs.length - 1; i >= 0; i--) {
subG = subGraphs[i];
+ logger.info('Subgraph - ', subG);
flowDb.addVertex(subG.id, subG.title, 'group', undefined, subG.classes);
}
@@ -347,7 +349,7 @@ export const draw = function(text, id) {
// Run the renderer. This is what draws the final graph.
const element = d3.select('#' + id + ' g');
render(element, g, ['point', 'circle', 'cross'], 'flowchart', id);
- dagre.layout(g);
+ // dagre.layout(g);
element.selectAll('g.node').attr('title', function() {
return flowDb.getTooltip(this.id);
@@ -378,27 +380,27 @@ export const draw = function(text, id) {
// Index nodes
flowDb.indexNodes('subGraph' + i);
- // reposition labels
- for (i = 0; i < subGraphs.length; i++) {
- subG = subGraphs[i];
+ // // reposition labels
+ // for (i = 0; i < subGraphs.length; i++) {
+ // subG = subGraphs[i];
- if (subG.title !== 'undefined') {
- const clusterRects = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"] rect');
- const clusterEl = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"]');
+ // if (subG.title !== 'undefined') {
+ // const clusterRects = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"] rect');
+ // const clusterEl = document.querySelectorAll('#' + id + ' [id="' + subG.id + '"]');
- const xPos = clusterRects[0].x.baseVal.value;
- const yPos = clusterRects[0].y.baseVal.value;
- const width = clusterRects[0].width.baseVal.value;
- const cluster = d3.select(clusterEl[0]);
- const te = cluster.select('.label');
- te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
- te.attr('id', id + 'Text');
+ // const xPos = clusterRects[0].x.baseVal.value;
+ // const yPos = clusterRects[0].y.baseVal.value;
+ // const width = clusterRects[0].width.baseVal.value;
+ // const cluster = d3.select(clusterEl[0]);
+ // const te = cluster.select('.label');
+ // te.attr('transform', `translate(${xPos + width / 2}, ${yPos + 14})`);
+ // te.attr('id', id + 'Text');
- for (let j = 0; j < subG.classes.length; j++) {
- clusterEl[0].classList.add(subG.classes[j]);
- }
- }
- }
+ // for (let j = 0; j < subG.classes.length; j++) {
+ // clusterEl[0].classList.add(subG.classes[j]);
+ // }
+ // }
+ // }
// Add label rects for non html labels
if (!conf.htmlLabels) {