diff --git a/cypress/platform/current.html b/cypress/platform/current.html index cc4b9e862..6b52388a7 100644 --- a/cypress/platform/current.html +++ b/cypress/platform/current.html @@ -32,13 +32,31 @@ G-->c
- flowchart LR - subgraph id1 [Test] - b - end - a-->id1 + stateDiagram-v2 + [*] --> monkey + state monkey { + Sitting + -- + Eating + }
-
+
+ stateDiagram-v2 + state Active { + [*] --> NumLockOff + NumLockOff --> NumLockOn : EvNumLockPressed + NumLockOn --> NumLockOff : EvNumLockPressed + -- + [*] --> CapsLockOff + CapsLockOff --> CapsLockOn : EvCapsLockPressed + CapsLockOn --> CapsLockOff : EvCapsLockPressed + -- + [*] --> ScrollLockOff + ScrollLockOff --> ScrollLockOn : EvCapsLockPressed + ScrollLockOn --> ScrollLockOff : EvCapsLockPressed + } +
+
stateDiagram [*] --> Still Still --> [*] @@ -52,15 +70,39 @@ Moving --> Crash Crash --> [*]
+
+stateDiagram-v2 + [*] --> First + First --> Second +% First --> Third + + state First { + [*] --> fir + fir --> [*] + } + state Second { + [*] --> sec + sec --> [*] + } +
- stateDiagram-v2 - State1: The state with a note - note right of State1 - Important information! You can write - notes. - end note - State1 --> State2 - note left of State2 : This is the note to the left. +stateDiagram-v2 + [*] --> First + First --> Second + First --> Third + + state First { + [*] --> fir + fir --> [*] + } + state Second { + [*] --> sec + sec --> [*] + } + state Third { + [*] --> thi + thi --> [*] + }
stateDiagram-v2 diff --git a/src/dagre-wrapper/GraphObjects.md b/src/dagre-wrapper/GraphObjects.md index 8821c7c36..b21d7efef 100644 --- a/src/dagre-wrapper/GraphObjects.md +++ b/src/dagre-wrapper/GraphObjects.md @@ -7,12 +7,10 @@ Explains the representation of various objects used to render the flow charts an Sample object: ```json { - "labelType":"svg", - "labelStyle":"", "shape":"rect", - "label":{}, "labelText":"Test", - "rx":0,"ry":0, + "rx":0, + "ry":0, "class":"default", "style":"", "id":"Test", @@ -24,18 +22,16 @@ This is set by the renderer of the diagram and insert the data that the wrapper | property | description | | ---------- | ----------------------------------------------------------------------------------------------------------- | -| labelType | If the label should be html label or a svg label. Should we continue to support both? | -| labelStyle | Css styles for the label. Not currently used. | -| shape | The shape of the node. Currently on rect is suppoerted. This will change. | -| label | ?? | +| labelStyle | Css styles for the label. User for instance for stylling the labels for clusters | +| shape | The shape of the node. | | labelText | The text on the label | -| rx | The corner radius - maybe part of the shape instead? | -| ry | The corner radius - maybe part of the shape instead? | -| class | Class to be set for the shape | +| rx | The corner radius - maybe part of the shape instead? Used for rects. | +| ry | The corner radius - maybe part of the shape instead? Used for rects. | +| classes | Classes to be set for the shape. Not used | | style | Css styles for the actual shape | | id | id of the shape | -| type | if set to group then this node indicates *a cluster*. | -| padding | Padding. Passed from the renderr as this might differ between react for different diagrams. Maybe obsolete. | +| type | if set to group then this node indicates *a cluster*. | +| padding | Padding. Passed from the render as this might differ between different diagrams. Maybe obsolete. | # edge diff --git a/src/dagre-wrapper/createLabel.js b/src/dagre-wrapper/createLabel.js index b7edb1572..a029f59fd 100644 --- a/src/dagre-wrapper/createLabel.js +++ b/src/dagre-wrapper/createLabel.js @@ -1,8 +1,10 @@ const createLabel = (vertexText, style) => { const svgLabel = document.createElementNS('http://www.w3.org/2000/svg', 'text'); svgLabel.setAttribute('style', style.replace('color:', 'fill:')); - - const rows = vertexText.split(/\n|/gi); + let rows = []; + if (vertexText) { + rows = vertexText.split(/\n|/gi); + } for (let j = 0; j < rows.length; j++) { const tspan = document.createElementNS('http://www.w3.org/2000/svg', 'tspan'); diff --git a/src/dagre-wrapper/edges.js b/src/dagre-wrapper/edges.js index ecf4254a8..6b141a863 100644 --- a/src/dagre-wrapper/edges.js +++ b/src/dagre-wrapper/edges.js @@ -63,34 +63,17 @@ const outsideNode = (node, point) => { return false; }; -// const intersection = (node, outsidePoint, insidePoint) => { -// const x = node.x; -// const y = node.y; - -// const dx = Math.abs(x - insidePoint.x); -// const w = node.width / 2; -// let r = w - dx; -// const dy = Math.abs(y - insidePoint.y); -// const h = node.height / 2; -// const q = h - dy; - -// const Q = Math.abs(outsidePoint.y - insidePoint.y); -// const R = Math.abs(outsidePoint.x - insidePoint.x); -// r = (R * q) / Q; - -// return { x: insidePoint.x + r, y: insidePoint.y + q }; -// }; const intersection = (node, outsidePoint, insidePoint) => { - // logger.info('intersection', outsidePoint, insidePoint, node); + logger.info('intersection o:', outsidePoint, ' i:', insidePoint, node); const x = node.x; const y = node.y; const dx = Math.abs(x - insidePoint.x); const w = node.width / 2; - let r = w - dx; + let r = insidePoint.x < outsidePoint.x ? w - dx : w + dx; const dy = Math.abs(y - insidePoint.y); const h = node.height / 2; - let q = h - dy; + let q = insidePoint.y < outsidePoint.y ? h - dy : h - dy; const Q = Math.abs(outsidePoint.y - insidePoint.y); const R = Math.abs(outsidePoint.x - insidePoint.x); @@ -105,9 +88,10 @@ const intersection = (node, outsidePoint, insidePoint) => { }; } else { q = (Q * r) / R; + r = (R * q) / Q; return { - x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x - r, + x: insidePoint.x < outsidePoint.x ? insidePoint.x + r : insidePoint.x + dx - w, y: insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q }; } @@ -117,8 +101,8 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { logger.info('\n\n\n\n'); let points = edge.points; if (edge.toCluster) { - // logger.info('edge', edge); - // logger.info('to cluster', clusterDb[edge.toCluster]); + logger.info('edge', edge); + logger.info('to cluster', clusterDb[edge.toCluster]); points = []; let lastPointOutside; let isInside = false; @@ -126,13 +110,12 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { const node = clusterDb[edge.toCluster].node; if (!outsideNode(node, point) && !isInside) { - // logger.info('inside', edge.toCluster, point); + logger.info('inside', edge.toCluster, point, lastPointOutside); // First point inside the rect const insterection = intersection(node, lastPointOutside, point); - // logger.info('intersect', inter.rect(node, lastPointOutside)); + logger.info('intersect', insterection); points.push(insterection); - // points.push(insterection); isInside = true; } else { if (!isInside) points.push(point); @@ -142,8 +125,8 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { } if (edge.fromCluster) { - // logger.info('edge', edge); - // logger.info('from cluster', clusterDb[edge.toCluster]); + logger.info('edge', edge); + logger.info('from cluster', clusterDb[edge.toCluster]); const updatedPoints = []; let lastPointOutside; let isInside = false; @@ -152,7 +135,7 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { const node = clusterDb[edge.fromCluster].node; if (!outsideNode(node, point) && !isInside) { - // logger.info('inside', edge.toCluster, point); + logger.info('inside', edge.toCluster, point); // First point inside the rect const insterection = intersection(node, lastPointOutside, point); @@ -162,7 +145,7 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { isInside = true; } else { // at the outside - // logger.info('Outside point', point); + logger.info('Outside point', point); if (!isInside) updatedPoints.unshift(point); } lastPointOutside = point; @@ -170,10 +153,6 @@ export const insertEdge = function(elem, edge, clusterDb, diagramType) { points = updatedPoints; } - // logger.info('Poibts', points); - - // logger.info('Edge', edge); - // The data for our line const lineData = points.filter(p => !Number.isNaN(p.y)); diff --git a/src/dagre-wrapper/index.js b/src/dagre-wrapper/index.js index 8f5a5c731..be024b4d1 100644 --- a/src/dagre-wrapper/index.js +++ b/src/dagre-wrapper/index.js @@ -7,8 +7,28 @@ import { logger } from '../logger'; let clusterDb = {}; -const translateClusterId = id => { - if (clusterDb[id]) return clusterDb[id].id; +const getAnchorId = (id, graph, nodes) => { + // 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; }; @@ -24,24 +44,24 @@ export const render = (elem, graph, markers, diagramtype, id) => { const edgeLabels = elem.insert('g').attr('class', 'edgeLabels'); const nodes = elem.insert('g').attr('class', 'nodes'); - logger.warn('graph', graph); - // Insert nodes, this will insert them into the dom and each node will get a size. The size is updated // to the abstract node and is later used by dagre for the layout graph.nodes().forEach(function(v) { const node = graph.node(v); - logger.warn('Node ' + v + ': ' + JSON.stringify(graph.node(v))); + logger.info('Node ' + v + ': ' + JSON.stringify(graph.node(v))); if (node.type !== 'group') { insertNode(nodes, graph.node(v)); } else { // const width = getClusterTitleWidth(clusters, node); const children = graph.children(v); + logger.info('Cluster identified', node.id, children[0]); // nodes2expand.push({ id: children[0], width }); clusterDb[node.id] = { id: children[0] }; - logger.info('Clusters ', clusterDb); + // clusterDb[node.id] = { id: node.id + '_anchor' }; } }); + logger.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 @@ -49,21 +69,37 @@ export const render = (elem, graph, markers, diagramtype, id) => { // TODO: pick optimal child in the cluster to us as link anchor graph.edges().forEach(function(e) { const edge = graph.edge(e); - logger.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); - // logger.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(graph.edge(e))); - const v = translateClusterId(e.v); - const w = translateClusterId(e.w); - if (v !== e.v || w !== e.w) { + logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); + logger.info('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 + logger.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); + 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); graph.setEdge(v, w, edge, e.name); } insertEdgeLabel(edgeLabels, edge); }); graph.edges().forEach(function(e) { - logger.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); + logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); }); logger.info('#############################################'); logger.info('### Layout ###'); @@ -74,7 +110,7 @@ export const render = (elem, graph, markers, diagramtype, id) => { // Move the nodes to the correct place graph.nodes().forEach(function(v) { const node = graph.node(v); - logger.info('Node ' + v + ': ' + JSON.stringify(graph.node(v))); + logger.trace('Node ' + v + ': ' + JSON.stringify(graph.node(v))); if (node.type !== 'group') { positionNode(node); } else { @@ -86,7 +122,7 @@ export const render = (elem, graph, markers, diagramtype, id) => { // Move the edge labels to the correct place after layout graph.edges().forEach(function(e) { const edge = graph.edge(e); - logger.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge); + logger.trace('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge); insertEdge(edgePaths, edge, clusterDb, diagramtype); positionEdgeLabel(edge); diff --git a/src/diagrams/flowchart/flowRenderer-v2.js b/src/diagrams/flowchart/flowRenderer-v2.js index e8915fc08..e0cce8df8 100644 --- a/src/diagrams/flowchart/flowRenderer-v2.js +++ b/src/diagrams/flowchart/flowRenderer-v2.js @@ -130,10 +130,8 @@ export const addVertices = function(vert, g, svgId) { } // Add the node g.setNode(vertex.id, { - labelType: 'svg', labelStyle: styles.labelStyle, shape: _shape, - label: vertexNode, labelText: vertexText, rx: radious, ry: radious, @@ -146,10 +144,8 @@ export const addVertices = function(vert, g, svgId) { }); logger.info('setNode', { - labelType: 'svg', labelStyle: styles.labelStyle, shape: _shape, - label: vertexNode, labelText: vertexText, rx: radious, ry: radious, diff --git a/src/diagrams/state/stateDb.js b/src/diagrams/state/stateDb.js index d72e8f00c..c56dcd1a3 100644 --- a/src/diagrams/state/stateDb.js +++ b/src/diagrams/state/stateDb.js @@ -1,4 +1,7 @@ import { logger } from '../../logger'; +import { generateId } from '../../utils'; + +const clone = o => JSON.parse(JSON.stringify(o)); let rootDoc = []; const setRootDoc = o => { @@ -22,6 +25,34 @@ const docTranslator = (parent, node, first) => { } if (node.doc) { + const doc = []; + // Check for concurrency + let i = 0; + let currentDoc = []; + for (i = 0; i < node.doc.length; i++) { + if (node.doc[i].type === 'divider') { + // debugger; + const newNode = clone(node.doc[i]); + newNode.doc = clone(currentDoc); + doc.push(newNode); + currentDoc = []; + } else { + currentDoc.push(node.doc[i]); + } + } + + // If any divider was encountered + if (doc.length > 0 && currentDoc.length > 0) { + const newNode = { + stmt: 'state', + id: generateId(), + type: 'divider', + doc: clone(currentDoc) + }; + doc.push(clone(newNode)); + node.doc = doc; + } + node.doc.forEach(docNode => docTranslator(node, docNode, true)); } } @@ -31,8 +62,14 @@ const getRootDocV2 = () => { return { id: 'root', doc: rootDoc }; }; -const extract = doc => { +const extract = _doc => { // const res = { states: [], relations: [] }; + let doc; + if (_doc.doc) { + doc = _doc.doc; + } else { + doc = _doc; + } // let doc = root.doc; // if (!doc) { // doc = root; @@ -40,6 +77,8 @@ const extract = doc => { logger.info(doc); clear(); + logger.info('Extract', doc); + doc.forEach(item => { if (item.stmt === 'state') { addState(item.id, item.type, item.doc, item.description, item.note); diff --git a/src/diagrams/state/stateRenderer-v2.js b/src/diagrams/state/stateRenderer-v2.js index 76dc2f186..f69d94ed3 100644 --- a/src/diagrams/state/stateRenderer-v2.js +++ b/src/diagrams/state/stateRenderer-v2.js @@ -72,10 +72,8 @@ const setupNode = (g, parent, node, altFlag) => { } const nodeData = { - labelType: 'svg', labelStyle: '', shape: nodeDb[node.id].shape, - label: node.id, labelText: nodeDb[node.id].description, classes: nodeDb[node.id].classes, //classStr, style: '', //styles.style, @@ -87,10 +85,8 @@ const setupNode = (g, parent, node, altFlag) => { if (node.note) { // Todo: set random id const noteData = { - labelType: 'svg', labelStyle: '', shape: 'note', - label: node.id, labelText: node.note.text, classes: 'statediagram-note', //classStr, style: '', //styles.style, @@ -99,10 +95,8 @@ const setupNode = (g, parent, node, altFlag) => { padding: 15 //getConfig().flowchart.padding }; const groupData = { - labelType: 'svg', labelStyle: '', shape: 'noteGroup', - label: node.id + '----parent', labelText: node.note.text, classes: nodeDb[node.id].classes, //classStr, style: '', //styles.style, @@ -133,8 +127,7 @@ const setupNode = (g, parent, node, altFlag) => { classes: 'note-edge', arrowheadStyle: 'fill: #333', labelpos: 'c', - labelType: 'text', - label: '' + labelType: 'text' }); } else { g.setNode(node.id, nodeData); @@ -143,12 +136,12 @@ const setupNode = (g, parent, node, altFlag) => { if (parent) { if (parent.id !== 'root') { - logger.trace('Setting node ', node.id, ' to be child of its parent ', parent.id); + logger.info('Setting node ', node.id, ' to be child of its parent ', parent.id); g.setParent(node.id, parent.id); } } if (node.doc) { - logger.trace('Adding nodes children '); + logger.info('Adding nodes children '); setupDoc(g, node, node.doc, !altFlag); } }; @@ -168,8 +161,7 @@ const setupDoc = (g, parent, doc, altFlag) => { labelStyle: '', arrowheadStyle: 'fill: #333', labelpos: 'c', - labelType: 'text', - label: '' + labelType: 'text' }; let startId = item.state1.id; let endId = item.state2.id; @@ -214,7 +206,7 @@ export const draw = function(text, id) { compound: true }) .setGraph({ - rankdir: 'LR', + rankdir: 'TB', nodesep: nodeSpacing, ranksep: rankSpacing, marginx: 8, @@ -224,8 +216,8 @@ export const draw = function(text, id) { return {}; }); - // logger.info(stateDb.getRootDoc()); - stateDb.extract(stateDb.getRootDocV2().doc); + logger.info(stateDb.getRootDocV2()); + stateDb.extract(stateDb.getRootDocV2()); logger.info(stateDb.getRootDocV2()); setupNode(g, undefined, stateDb.getRootDocV2(), true); diff --git a/src/utils.js b/src/utils.js index 823191ffc..1adcb86f0 100644 --- a/src/utils.js +++ b/src/utils.js @@ -210,6 +210,19 @@ export const getStylesFromArray = arr => { return { style: style, labelStyle: labelStyle }; }; +let cnt = 0; +export const generateId = () => { + cnt++; + return ( + 'id-' + + Math.random() + .toString(36) + .substr(2, 12) + + '-' + + cnt + ); +}; + export default { detectType, isSubstringInArray, @@ -217,5 +230,6 @@ export default { calcLabelPosition, calcCardinalityPosition, formatUrl, - getStylesFromArray + getStylesFromArray, + generateId };