#1295 Fix for edges in subgraphs and handling of concurrent states in statediagram-v2

This commit is contained in:
Knut Sveidqvist
2020-04-22 20:03:41 +02:00
parent 10fdc45dea
commit 59f3d2a11e
9 changed files with 205 additions and 34 deletions

View File

@@ -18,6 +18,14 @@
</head>
<body>
<h1>info below</h1>
<div class="mermaid2" style="width: 100%; height: 20%;">
flowchart LR
a --> b
subgraph b [Test]
c --> d -->e
end
</div>
<div class="mermaid2" style="width: 100%; height: 20%;">
flowchart LR
a --> b
@@ -40,7 +48,7 @@
Eating
}
</div>
<div class="mermaid2" style="width: 50%; height: 20%;">
<div class="mermaid" style="width: 50%; height: 20%;">
stateDiagram-v2
state Active {
[*] --> NumLockOff
@@ -73,8 +81,8 @@
<div class="mermaid2" style="width: 100%; height: 100%;">
stateDiagram-v2
[*] --> First
First --> Second
% First --> Third
First --> Third
First --> sec
state First {
[*] --> fir
@@ -83,9 +91,14 @@ stateDiagram-v2
state Second {
[*] --> sec
sec --> [*]
}
}
state Third {
[*] --> thi
thi --> [*]
}
thi --> sec
</div>
<div class="mermaid" style="width: 100%; height: 100%;">
<div class="mermaid2" style="width: 100%; height: 100%;">
flowchart TD
subgraph A
a
@@ -101,7 +114,7 @@ flowchart TD
A -- oAo --o B
A --> C
</div>
<div class="mermaid" style="width: 100%; height: 100%;">
<div class="mermaid2" style="width: 100%; height: 100%;">
flowchart TD
subgraph A
a
@@ -133,28 +146,27 @@ stateDiagram-v2
</div>
<div class="mermaid2" style="width: 100%; height: 100%;">
stateDiagram-v2
[*]-->TV
[*]-->TV
state TV {
[*] --> Off: Off to start with
On --> Off : Turn off
Off --> On : Turn on
}
state TV {
[*] --> Off: Off to start with
On --> Off : Turn off
Off --> On : Turn on
}
TV--> Console
TV--> Console
state Console {
[*] --> Off2: Off to start with
On2--> Off2 : Turn off
Off2 --> On2 : Turn on
On2-->Playing
state Playing {
Alive --> Dead
Dead-->Alive
}
}
state Console {
[*] --> Off2: Off to start with
On2--> Off2 : Turn off
Off2 --> On2 : Turn on
On2-->Playing
state Playing {
Alive --> Dead
Dead-->Alive
}
}
</div>
<div style="display: flex;flex-direction:column;width: 100%; height: 100%">

View File

@@ -149,7 +149,64 @@ const roundedWithTitle = (parent, node) => {
return shapeSvg;
};
const shapes = { rect, roundedWithTitle, noteGroup };
const divider = (parent, node) => {
// Add outer g element
const shapeSvg = parent
.insert('g')
.attr('class', node.classes)
.attr('id', node.id);
// add the rect
const rect = shapeSvg.insert('rect', ':first-child');
// Create the label and insert it after the rect
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
const innerRect = shapeSvg.append('rect');
const text = label.node().appendChild(createLabel(node.labelText, node.labelStyle));
// Get the size of the label
const bbox = text.getBBox();
const padding = 0 * node.padding;
const halfPadding = padding / 2;
// center the rect around its coordinate
rect
.attr('class', 'divider')
.attr('x', node.x - node.width / 2 - halfPadding)
.attr('y', node.y - node.height / 2 - halfPadding)
.attr('width', node.width + padding)
.attr('height', node.height + padding);
// innerRect
// .attr('class', 'inner')
// .attr('x', node.x - node.width / 2 - halfPadding)
// .attr('y', node.y - node.height / 2 - halfPadding + bbox.height - 1)
// .attr('width', node.width + padding)
// .attr('height', node.height + padding - bbox.height);
// Center the label
label.attr(
'transform',
'translate(' +
(node.x - bbox.width / 2) +
', ' +
(node.y - node.height / 2 - node.padding / 3 + 3) +
')'
);
const rectBox = rect.node().getBBox();
node.width = rectBox.width;
node.height = rectBox.height;
node.intersect = function(point) {
return intersectRect(node, point);
};
return shapeSvg;
};
const shapes = { rect, roundedWithTitle, noteGroup, divider };
let clusterElems = {};

View File

@@ -14,7 +14,7 @@ import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } f
import { logger as log } from '../logger';
const recursiveRender = (_elem, graph, diagramtype, parentCluster) => {
log.trace('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
log.info('Graph in recursive render:', graphlib.json.write(graph), parentCluster);
const elem = _elem.insert('g').attr('class', 'root'); // eslint-disable-line
if (!graph.nodes()) {
log.trace('No nodes found for', graph);

View File

@@ -31,13 +31,17 @@ const isDecendant = (id, ancenstorId) => {
};
const edgeInCluster = (edge, clusterId) => {
log.info('Decendants of ', clusterId, ' is ', decendants[clusterId]);
log.info('Edge is ', edge);
// Edges to/from the cluster is not in the cluster, they are in the parent
if (!(edge.v === clusterId || edge.w === clusterId)) return false;
if (edge.v === clusterId) return false;
if (edge.w === clusterId) return false;
if (!decendants[clusterId]) {
log.debug('Tilt, ', clusterId, ',not in decendants');
return false;
}
log.info('Here ');
if (decendants[clusterId].indexOf(edge.v) >= 0) return true;
if (isDecendant(edge.v, clusterId)) return true;
@@ -80,17 +84,26 @@ const copy = (clusterId, graph, newGraph, rootId) => {
const edges = graph.edges(node);
log.debug('Copying Edges', edges);
edges.forEach(edge => {
log.trace('Edge', edge);
log.info('Edge', edge);
const data = graph.edge(edge.v, edge.w, edge.name);
log.trace('Edge data', data, rootId);
log.info('Edge data', data, rootId);
try {
// Do not copy edges in and out of the root cluster, they belong to the parent graph
if (edgeInCluster(edge, rootId)) {
log.trace('Copying as ', edge.v, edge.w, data, edge.name);
log.info('Copying as ', edge.v, edge.w, data, edge.name);
newGraph.setEdge(edge.v, edge.w, data, edge.name);
log.trace('newGraph edges ', newGraph.edges(), newGraph.edge(newGraph.edges()[0]));
log.info('newGraph edges ', newGraph.edges(), newGraph.edge(newGraph.edges()[0]));
} else {
log.trace('Skipping copy of edge as ', rootId, edge.v, edge.w, clusterId);
log.info(
'Skipping copy of edge ',
edge.v,
'-->',
edge.w,
' rootId: ',
rootId,
' clusterId:',
clusterId
);
}
} catch (e) {
log.error(e);

View File

@@ -314,6 +314,34 @@ describe('Graphlib decorations', () => {
// expect(edgeData.data).toBe('link2');
// expect(validate(g)).toBe(true);
});
it('adjustClustersAndEdges the extracted graphs shall contain the correct links GLB20', function () {
/*
a --> b
subgraph b [Test]
c --> d -->e
end
*/
g.setNode('a', { data: 1 });
g.setNode('b', { data: 2 });
g.setNode('c', { data: 3 });
g.setNode('d', { data: 3 });
g.setNode('e', { data: 3 });
g.setParent('c', 'b');
g.setParent('d', 'b');
g.setParent('e', 'b');
g.setEdge('a', 'b', { data: 'link1' }, '1');
g.setEdge('c', 'd', { data: 'link2' }, '2');
g.setEdge('d', 'e', { data: 'link2' }, '2');
logger.info('Graph before', graphlib.json.write(g))
adjustClustersAndEdges(g);
const bGraph = g.node('b').graph;
// logger.trace('Graph after', graphlib.json.write(g))
logger.info('Graph after', graphlib.json.write(bGraph));
expect(bGraph.nodes().length).toBe(3);
expect(bGraph.edges().length).toBe(2);
});
});
});
describe('extractDecendants', function () {

View File

@@ -253,6 +253,7 @@ const rect = (parent, node) => {
const rect = shapeSvg.insert('rect', ':first-child');
rect
.attr('class', 'basic')
.attr('rx', node.rx)
.attr('ry', node.ry)
.attr('x', -bbox.width / 2 - halfPadding)

View File

@@ -0,0 +1,54 @@
/**
* Setup arrow head and define the marker. The result is appended to the svg.
*/
import { logger } from '../logger';
// Only add the number of markers that the diagram needs
const insertPatterns = (elem, patternArray, type, id) => {
patternArray.forEach(patternName => {
patterns[patternName](elem, type, id);
});
};
{
/* <svg height="10" width="10" xmlns="http://www.w3.org/2000/svg" version="1.1">
{' '}
<defs>
{' '}
<pattern id="circles-1" patternUnits="userSpaceOnUse" width="10" height="10">
{' '}
<image
xlink:href="data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHdpZHRoPScxMCcgaGVpZ2h0PScxMCc+CiAgPHJlY3Qgd2lkdGg9JzEwJyBoZWlnaHQ9JzEwJyBmaWxsPSJ3aGl0ZSIgLz4KICA8Y2lyY2xlIGN4PSIxIiBjeT0iMSIgcj0iMSIgZmlsbD0iYmxhY2siLz4KPC9zdmc+"
x="0"
y="0"
width="10"
height="10"
>
{' '}
</image>{' '}
</pattern>{' '}
</defs>{' '}
</svg>; */
}
const dots = (elem, type) => {
elem
.append('defs')
.append('marker')
.attr('id', type + '-barbEnd')
.attr('refX', 19)
.attr('refY', 7)
.attr('markerWidth', 20)
.attr('markerHeight', 14)
.attr('markerUnits', 0)
.attr('orient', 'auto')
.append('path')
.attr('d', 'M 19,7 L9,13 L14,7 L9,1 Z');
};
// TODO rename the class diagram markers to something shape descriptive and semanitc free
const patterns = {
dots
};
export default insertPatterns;

View File

@@ -64,7 +64,7 @@ const setupNode = (g, parent, node, altFlag) => {
if (!nodeDb[node.id].type && node.doc) {
logger.info('Setting cluser for ', node.id);
nodeDb[node.id].type = 'group';
nodeDb[node.id].shape = 'roundedWithTitle';
nodeDb[node.id].shape = node.type === 'divider' ? 'divider' : 'roundedWithTitle';
nodeDb[node.id].classes =
nodeDb[node.id].classes +
' ' +

View File

@@ -85,6 +85,8 @@ g.stateGroup line {
fill: $nodeBkg;
stroke: $nodeBorder;
stroke-width: 1px;
}
.statediagram-cluster rect.outer {
rx: 5px;
ry: 5px;
}
@@ -100,10 +102,14 @@ g.stateGroup line {
ry:0;
}
.statediagram-state rect {
.statediagram-state rect.basic {
rx: 5px;
ry: 5px;
}
.statediagram-state rect.divider {
stroke-dasharray: 10,10;
fill: #efefef;
}
.note-edge {
stroke-dasharray: 5;