mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-12-09 14:04:14 +01:00
Merge branch 'develop' of https://github.com/knsv/mermaid into 1542_take_two
This commit is contained in:
@@ -6,7 +6,8 @@ import {
|
||||
clear as clearGraphlib,
|
||||
clusterDb,
|
||||
adjustClustersAndEdges,
|
||||
findNonClusterChild
|
||||
findNonClusterChild,
|
||||
sortNodesByHierarchy
|
||||
} from './mermaid-graphlib';
|
||||
import { insertNode, positionNode, clear as clearNodes, setNodeElem } from './nodes';
|
||||
import { insertCluster, clear as clearClusters } from './clusters';
|
||||
@@ -14,7 +15,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
|
||||
import { logger as log } from '../logger';
|
||||
|
||||
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
log.info('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
|
||||
log.info('Graph in recursive render: XXX', graphlib.json.write(graph), parentCluster);
|
||||
const dir = graph.graph().rankdir;
|
||||
log.warn('Dir in recursive render - dir:', dir);
|
||||
|
||||
@@ -22,7 +23,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
if (!graph.nodes()) {
|
||||
log.info('No nodes found for', graph);
|
||||
} else {
|
||||
log.info('Recursive render', graph.nodes());
|
||||
log.info('Recursive render XXX', graph.nodes());
|
||||
}
|
||||
if (graph.edges().length > 0) {
|
||||
log.info('Recursive edges', graph.edge(graph.edges()[0]));
|
||||
@@ -39,11 +40,14 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
if (typeof parentCluster !== 'undefined') {
|
||||
const data = JSON.parse(JSON.stringify(parentCluster.clusterData));
|
||||
// data.clusterPositioning = true;
|
||||
log.info('Setting data for cluster', data);
|
||||
log.info('Setting data for cluster XXX (', v, ') ', data, parentCluster);
|
||||
graph.setNode(parentCluster.id, data);
|
||||
graph.setParent(v, parentCluster.id, data);
|
||||
if (!graph.parent(v)) {
|
||||
log.warn('Setting parent', v, parentCluster.id);
|
||||
graph.setParent(v, parentCluster.id, data);
|
||||
}
|
||||
}
|
||||
log.info('(Insert) Node ' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||
log.info('(Insert) Node XXX' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||
if (node && node.clusterNode) {
|
||||
// const children = graph.children(v);
|
||||
log.info('Cluster identified', v, node, graph.node(v));
|
||||
@@ -56,7 +60,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
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('Cluster - the non recursive path XXX', 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));
|
||||
@@ -91,7 +95,7 @@ const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
|
||||
dagre.layout(graph);
|
||||
log.info('Graph after layout:', graphlib.json.write(graph));
|
||||
// Move the nodes to the correct place
|
||||
graph.nodes().forEach(function(v) {
|
||||
sortNodesByHierarchy(graph).forEach(function(v) {
|
||||
const node = graph.node(v);
|
||||
log.info('Position ' + v + ': ' + JSON.stringify(graph.node(v)));
|
||||
log.info(
|
||||
@@ -138,10 +142,10 @@ export const render = (elem, graph, markers, diagramtype, id) => {
|
||||
clearClusters();
|
||||
clearGraphlib();
|
||||
|
||||
log.warn('Graph before:', graphlib.json.write(graph));
|
||||
log.warn('Graph at first:', graphlib.json.write(graph));
|
||||
adjustClustersAndEdges(graph);
|
||||
log.warn('Graph after:', graphlib.json.write(graph));
|
||||
log.warn('Graph ever after:', graph.graph());
|
||||
// log.warn('Graph ever after:', graphlib.json.write(graph.node('A').graph));
|
||||
recursiveRender(elem, graph, diagramtype);
|
||||
};
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ const edgeInCluster = (edge, clusterId) => {
|
||||
};
|
||||
|
||||
const copy = (clusterId, graph, newGraph, rootId) => {
|
||||
log.info(
|
||||
log.warn(
|
||||
'Copying children of ',
|
||||
clusterId,
|
||||
'root',
|
||||
@@ -68,7 +68,7 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
||||
nodes.push(clusterId);
|
||||
}
|
||||
|
||||
log.debug('Copying (nodes) clusterId', clusterId, 'nodes', nodes);
|
||||
log.warn('Copying (nodes) clusterId', clusterId, 'nodes', nodes);
|
||||
|
||||
nodes.forEach(node => {
|
||||
if (graph.children(node).length > 0) {
|
||||
@@ -77,8 +77,8 @@ const copy = (clusterId, graph, newGraph, rootId) => {
|
||||
const data = graph.node(node);
|
||||
log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); //,node, data, ' parent is ', clusterId);
|
||||
newGraph.setNode(node, data);
|
||||
log.debug('Setting parent', node, graph.parent(node));
|
||||
if (rootId !== graph.parent(node)) {
|
||||
log.warn('Setting parent', node, graph.parent(node));
|
||||
newGraph.setParent(node, graph.parent(node));
|
||||
}
|
||||
|
||||
@@ -245,40 +245,51 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
|
||||
// d1 xor d2 - if either d1 is true and d2 is false or the other way around
|
||||
if (d1 ^ d2) {
|
||||
log.debug('Edge: ', edge, ' leaves cluster ', id);
|
||||
log.debug('Decendants of ', id, ': ', decendants[id]);
|
||||
log.warn('Edge: ', edge, ' leaves cluster ', id);
|
||||
log.warn('Decendants of XXX ', id, ': ', decendants[id]);
|
||||
clusterDb[id].externalConnections = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.debug('Not a cluster ', id, decendants);
|
||||
}
|
||||
});
|
||||
|
||||
extractor(graph, 0);
|
||||
|
||||
// For clusters with incoming and/or outgoing edges translate those edges to a real node
|
||||
// in the cluster inorder to fake the edge
|
||||
graph.edges().forEach(function(e) {
|
||||
const edge = graph.edge(e);
|
||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||
log.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
|
||||
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
|
||||
log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e)));
|
||||
|
||||
let v = e.v;
|
||||
let w = e.w;
|
||||
// Check if link is either from or to a cluster
|
||||
log.trace('Fix', clusterDb, 'ids:', e.v, e.w, 'Translateing: ', clusterDb[e.v], clusterDb[e.w]);
|
||||
log.warn(
|
||||
'Fix XXX',
|
||||
clusterDb,
|
||||
'ids:',
|
||||
e.v,
|
||||
e.w,
|
||||
'Translateing: ',
|
||||
clusterDb[e.v],
|
||||
' --- ',
|
||||
clusterDb[e.w]
|
||||
);
|
||||
if (clusterDb[e.v] || clusterDb[e.w]) {
|
||||
log.warn('Fixing and trixing - removing', e.v, e.w, e.name);
|
||||
log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name);
|
||||
v = getAnchorId(e.v);
|
||||
w = getAnchorId(e.w);
|
||||
graph.removeEdge(e.v, e.w, e.name);
|
||||
if (v !== e.v) edge.fromCluster = e.v;
|
||||
if (w !== e.w) edge.toCluster = e.w;
|
||||
log.warn('Replacing with', v, w, e.name);
|
||||
log.warn('Fix Replacing with XXX', v, w, e.name);
|
||||
graph.setEdge(v, w, edge, e.name);
|
||||
}
|
||||
});
|
||||
log.warn('Adjusted Graph', graphlib.json.write(graph));
|
||||
extractor(graph, 0);
|
||||
|
||||
log.trace(clusterDb);
|
||||
|
||||
@@ -291,7 +302,7 @@ export const adjustClustersAndEdges = (graph, depth) => {
|
||||
};
|
||||
|
||||
export const extractor = (graph, depth) => {
|
||||
log.debug('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
|
||||
log.warn('extractor - ', depth, graphlib.json.write(graph), graph.children('D'));
|
||||
if (depth > 10) {
|
||||
log.error('Bailing out');
|
||||
return;
|
||||
@@ -336,11 +347,11 @@ export const extractor = (graph, depth) => {
|
||||
// break;
|
||||
} else if (
|
||||
!clusterDb[node].externalConnections &&
|
||||
!graph.parent(node) &&
|
||||
// !graph.parent(node) &&
|
||||
graph.children(node) &&
|
||||
graph.children(node).length > 0
|
||||
) {
|
||||
log.debug(
|
||||
log.warn(
|
||||
'Cluster without external connections, without a parent and with children',
|
||||
node,
|
||||
depth
|
||||
@@ -364,7 +375,7 @@ export const extractor = (graph, depth) => {
|
||||
return {};
|
||||
});
|
||||
|
||||
log.debug('Old graph before copy', graphlib.json.write(graph));
|
||||
log.warn('Old graph before copy', graphlib.json.write(graph));
|
||||
copy(node, graph, clusterGraph, node);
|
||||
graph.setNode(node, {
|
||||
clusterNode: true,
|
||||
@@ -373,10 +384,10 @@ export const extractor = (graph, depth) => {
|
||||
labelText: clusterDb[node].labelText,
|
||||
graph: clusterGraph
|
||||
});
|
||||
log.debug('New graph after copy', graphlib.json.write(clusterGraph));
|
||||
log.warn('New graph after copy node: (', node, ')', graphlib.json.write(clusterGraph));
|
||||
log.debug('Old graph after copy', graphlib.json.write(graph));
|
||||
} else {
|
||||
log.debug(
|
||||
log.warn(
|
||||
'Cluster ** ',
|
||||
node,
|
||||
' **not meeting the criteria !externalConnections:',
|
||||
@@ -393,13 +404,27 @@ export const extractor = (graph, depth) => {
|
||||
}
|
||||
|
||||
nodes = graph.nodes();
|
||||
log.debug('New list of nodes', nodes);
|
||||
log.warn('New list of nodes', nodes);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const node = nodes[i];
|
||||
const data = graph.node(node);
|
||||
log.debug(' Now next leveö', node, data);
|
||||
log.warn(' Now next level', node, data);
|
||||
if (data.clusterNode) {
|
||||
extractor(data.graph, depth + 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const sorter = (graph, nodes) => {
|
||||
if (nodes.length === 0) return [];
|
||||
let result = Object.assign(nodes);
|
||||
nodes.forEach(node => {
|
||||
const children = graph.children(node);
|
||||
const sorted = sorter(graph, children);
|
||||
result = result.concat(sorted);
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const sortNodesByHierarchy = graph => sorter(graph, graph.children());
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import graphlib from 'graphlib';
|
||||
import dagre from 'dagre';
|
||||
import { validate, adjustClustersAndEdges, extractDecendants } from './mermaid-graphlib';
|
||||
import { validate, adjustClustersAndEdges, extractDecendants, sortNodesByHierarchy } from './mermaid-graphlib';
|
||||
import { setLogLevel, logger } from '../logger';
|
||||
|
||||
describe('Graphlib decorations', () => {
|
||||
@@ -373,6 +373,31 @@ describe('Graphlib decorations', () => {
|
||||
});
|
||||
|
||||
});
|
||||
it('adjustClustersAndEdges should handle nesting GLB77', function () {
|
||||
/*
|
||||
flowchart TB
|
||||
subgraph A
|
||||
b-->B
|
||||
a-->c
|
||||
end
|
||||
subgraph B
|
||||
c
|
||||
end
|
||||
*/
|
||||
|
||||
const exportedGraph = JSON.parse('{"options":{"directed":true,"multigraph":true,"compound":true},"nodes":[{"v":"A","value":{"labelStyle":"","shape":"rect","labelText":"A","rx":0,"ry":0,"class":"default","style":"","id":"A","width":500,"type":"group","padding":15}},{"v":"B","value":{"labelStyle":"","shape":"rect","labelText":"B","rx":0,"ry":0,"class":"default","style":"","id":"B","width":500,"type":"group","padding":15},"parent":"A"},{"v":"b","value":{"labelStyle":"","shape":"rect","labelText":"b","rx":0,"ry":0,"class":"default","style":"","id":"b","padding":15},"parent":"A"},{"v":"c","value":{"labelStyle":"","shape":"rect","labelText":"c","rx":0,"ry":0,"class":"default","style":"","id":"c","padding":15},"parent":"B"},{"v":"a","value":{"labelStyle":"","shape":"rect","labelText":"a","rx":0,"ry":0,"class":"default","style":"","id":"a","padding":15},"parent":"A"}],"edges":[{"v":"b","w":"B","name":"1","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-b-B","classes":"flowchart-link LS-b LE-B"}},{"v":"a","w":"c","name":"2","value":{"minlen":1,"arrowhead":"normal","arrowTypeStart":"arrow_open","arrowTypeEnd":"arrow_point","thickness":"normal","pattern":"solid","style":"fill:none","labelStyle":"","arrowheadStyle":"fill: #333","labelpos":"c","labelType":"text","label":"","id":"L-a-c","classes":"flowchart-link LS-a LE-c"}}],"value":{"rankdir":"TB","nodesep":50,"ranksep":50,"marginx":8,"marginy":8}}');
|
||||
const gr = graphlib.json.read(exportedGraph)
|
||||
|
||||
logger.info('Graph before', graphlib.json.write(gr))
|
||||
adjustClustersAndEdges(gr);
|
||||
const aGraph = gr.node('A').graph;
|
||||
const bGraph = aGraph.node('B').graph;
|
||||
logger.info('Graph after', graphlib.json.write(aGraph));
|
||||
// logger.trace('Graph after', graphlib.json.write(g))
|
||||
expect(aGraph.parent('c')).toBe('B');
|
||||
expect(aGraph.parent('B')).toBe(undefined);
|
||||
});
|
||||
|
||||
});
|
||||
describe('extractDecendants', function () {
|
||||
let g;
|
||||
@@ -426,3 +451,57 @@ describe('extractDecendants', function () {
|
||||
expect(d3).toEqual(['c']);
|
||||
});
|
||||
});
|
||||
describe('sortNodesByHierarchy', function () {
|
||||
let g;
|
||||
beforeEach(function () {
|
||||
setLogLevel(1);
|
||||
g = new graphlib.Graph({
|
||||
multigraph: true,
|
||||
compound: true
|
||||
});
|
||||
g.setGraph({
|
||||
rankdir: 'TB',
|
||||
nodesep: 10,
|
||||
ranksep: 10,
|
||||
marginx: 8,
|
||||
marginy: 8
|
||||
});
|
||||
g.setDefaultEdgeLabel(function () {
|
||||
return {};
|
||||
});
|
||||
});
|
||||
it('it should sort proper en nodes are in reverse order', function () {
|
||||
/*
|
||||
a -->b
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
*/
|
||||
g.setNode('a', { data: 1 });
|
||||
g.setNode('b', { data: 2 });
|
||||
g.setParent('b', 'B');
|
||||
g.setParent('B', 'A');
|
||||
g.setEdge('a', 'b', '1');
|
||||
expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']);
|
||||
});
|
||||
it('it should sort proper en nodes are in correct order', function () {
|
||||
/*
|
||||
a -->b
|
||||
subgraph B
|
||||
b
|
||||
end
|
||||
subgraph A
|
||||
B
|
||||
end
|
||||
*/
|
||||
g.setNode('a', { data: 1 });
|
||||
g.setParent('B', 'A');
|
||||
g.setParent('b', 'B');
|
||||
g.setNode('b', { data: 2 });
|
||||
g.setEdge('a', 'b', '1');
|
||||
expect(sortNodesByHierarchy(g)).toEqual(['a', 'A', 'B', 'b']);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user