#2028 Getting swimlanes to work again after merge from develop

This commit is contained in:
ashishj 2023-10-25 20:51:31 +02:00
parent a64fd61eb9
commit ef3787c6fc
7 changed files with 3492 additions and 5051 deletions

View File

@ -71,8 +71,7 @@
end
cat --> monkey
cow --> dog
</pre
>
</pre>
<pre id="diagram" class="mermaid">
swimlane LR
subgraph "`one`"
@ -85,27 +84,14 @@ swimlane LR
cow --> horse --> done3
cow --> sheep --> done3
end
subgraph "`four`"
panda -->
kangaroo --> done4
end
cat --> monkey
cow --> dog
kangaroo --> sheep
sheep --> dog
</pre>
<pre id="diagram" class="mermaid2">
swimlane LR
subgraph "`three`"
bat --> cow
cow --> sheep
end
subgraph "`four`"
panda -->
kangaroo --> done4
end
kangaroo --> sheep
</pre>
monkey dog --> cat monkey --> cow -->
<!-- <div id="cy"></div> -->
<!-- <script src="http://localhost:9000/packages/mermaid-mindmap/dist/mermaid-mindmap-detector.js"></script> -->
<!-- <script src="./mermaid-example-diagram-detector.js"></script> -->

View File

@ -14,7 +14,7 @@ import { insertCluster, clear as clearClusters } from './clusters.js';
import { insertEdgeLabel, positionEdgeLabel, insertEdge, clear as clearEdges } from './edges.js';
import { log } from '../logger.js';
const recursiveRender = async (_elem, graph, diagramtype, id, parentCluster) => {
const recursiveRender = async (_elem, graph, diagramtype, parentCluster) => {
log.info('Graph in recursive render: XXX', graphlibJson.write(graph), parentCluster);
const dir = graph.graph().rankdir;
log.trace('Dir in recursive render - dir:', dir);
@ -52,7 +52,7 @@ const recursiveRender = async (_elem, graph, diagramtype, id, parentCluster) =>
if (node && node.clusterNode) {
// const children = graph.children(v);
log.info('Cluster identified', v, node.width, graph.node(v));
const o = await recursiveRender(nodes, node.graph, diagramtype, id, graph.node(v));
const o = await recursiveRender(nodes, node.graph, diagramtype, graph.node(v));
const newEl = o.elem;
updateNodeBounds(node, newEl);
node.diff = o.diff || 0;
@ -134,7 +134,9 @@ const recursiveRender = async (_elem, graph, diagramtype, id, parentCluster) =>
const edge = graph.edge(e);
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph, id);
const paths = insertEdge(edgePaths, e, edge, clusterDb, diagramtype, graph);
positionEdgeLabel(edge, paths);
});
@ -156,11 +158,11 @@ export const render = async (elem, graph, markers, diagramtype, id) => {
clearClusters();
clearGraphlib();
log.warn('Graph at first:', JSON.stringify(graphlibJson.write(graph)));
log.warn('Graph at first:', graphlibJson.write(graph));
adjustClustersAndEdges(graph);
log.warn('Graph after:', JSON.stringify(graphlibJson.write(graph)));
log.warn('Graph after:', graphlibJson.write(graph));
// log.warn('Graph ever after:', graphlibJson.write(graph.node('A').graph));
await recursiveRender(elem, graph, diagramtype, id);
await recursiveRender(elem, graph, diagramtype);
};
// const shapeDefinitions = {};

View File

@ -81,6 +81,7 @@ that id.
<click>[\s\n] this.popState();
<click>[^\s\n]* return 'CLICK';
"swimlane" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"flowchart-elk" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"graph" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}
"flowchart" {if(yy.lex.firstGraph()){this.begin("dir");} return 'GRAPH';}

View File

@ -1,16 +1,21 @@
import type { MermaidConfig } from '../../../config.type.js';
import type { ExternalDiagramDefinition, DiagramDetector } from '../../../diagram-api/types.js';
import type {
ExternalDiagramDefinition,
DiagramDetector,
DiagramLoader,
} from '../../../diagram-api/types.js';
const id = 'swimlane';
const detector: DiagramDetector = (txt: string, config?: MermaidConfig): boolean => {
const detector: DiagramDetector = (txt, config): boolean => {
if (txt.match(/^\s*swimlane/)) {
return true;
}
return false;
};
const loader = async () => {
const loader: DiagramLoader = async () => {
const { diagram } = await import('./swimlane-definition.js');
return { id, diagram };
};

View File

@ -23,7 +23,7 @@ export const setConf = function (cnf) {
* @param {object} g The graph object
* @param diagObj
*/
export const addEdges = function (edges, g, diagObj) {
export const addEdges = function (edges, g, diagObj,svg) {
log.info('abc78 edges = ', edges);
let cnt = 0;
let linkIdCnt = {};

View File

@ -1,6 +1,7 @@
import * as graphlib from 'dagre-d3-es/src/graphlib/index.js';
import { select, curveLinear, selectAll } from 'd3';
import { swimlaneLayout } from './swimlane-layout.js';
import insertMarkers from '../../../dagre-wrapper/markers.js';
import { insertNode } from '../../../dagre-wrapper/nodes.js';
import flowDb from '../flowDb.js';
import { getConfig } from '../../../config.js';
@ -34,12 +35,15 @@ export const setConf = function (cnf) {
* @param element
* @param graph
* @param layout
* @param vert
* @param elem
* @param g
* @param id
* @param conf
*/
async function swimlaneRender(layout,vert, elem,g, id, conf) {
let max
let renderedNodes = [];
// draw nodes from layout.graph to element
const nodes = layout.graph.nodes();
@ -196,10 +200,13 @@ async function swimlaneRender(layout,vert, elem,g, id, conf) {
boundingBox = nodeEl.node().getBBox();
nodeEl.attr('transform', `translate(${nodeObj.x}, ${nodeObj.y / 2})`);
// add to rendered nodes
renderedNodes.push({id: vertex.id, nodeObj: nodeObj, boundingBox: boundingBox});
}
return elem;
return renderedNodes;
}
/**
@ -209,16 +216,28 @@ async function swimlaneRender(layout,vert, elem,g, id, conf) {
* @param diagObj
* @returns {object} ClassDef styles
*/
// export const getClasses = function (text, diagObj) {
// log.info('Extracting classes');
// diagObj.db.clear();
// try {
// // Parse the graph definition
// diagObj.parse(text);
// return diagObj.db.getClasses();
// } catch (e) {
// return;
// }
// };
/**
* Returns the all the styles from classDef statements in the graph definition.
*
* @param text
* @param diagObj
* @returns {Record<string, import('../../../diagram-api/types.js').DiagramStyleClassDef>} ClassDef styles
*/
export const getClasses = function (text, diagObj) {
log.info('Extracting classes');
diagObj.db.clear();
try {
// Parse the graph definition
diagObj.parse(text);
return diagObj.db.getClasses();
} catch (e) {
return;
}
};
/**
@ -288,6 +307,10 @@ console.log('diagObj',diagObj);
console.log('custom layout',layout);
// insert markers
// Define the supported markers for the diagram
const markers = ['point', 'circle', 'cross'];
insertMarkers(svg, markers, 'flowchart', id);
// draw lanes as vertical lines
const lanesElements = svg.insert('g').attr('class', 'lanes');
@ -370,34 +393,45 @@ console.log('diagObj',diagObj);
// add lane headers
const laneHeaders = svg.insert('g').attr('class', 'laneHeaders');
let drawnEdges =[];
addEdges(edges, g, diagObj);
//get edge markers
let renderedNodes = await swimlaneRender(layout,vert, svg,g,id, conf);
let renderedEdgePaths= [];
addEdges(edges, g, diagObj,svg);
g.edges().forEach(function (e) {
const edge = g.edge(e);
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge);
const edgePaths = svg.insert('g').attr('class', 'edgePaths');
//create edge points based on start and end node
//get start node x, y coordinates
const sourceNode = layout.graph.node(e.v)
//get end node x, y coordinates
sourceNode.x = sourceNode.x ;
sourceNode.y = sourceNode.y ;
let sourceNode = {x:layout.graph.node(e.v).x, y:layout.graph.node(e.v).y/2, id: e.v};
//get end node x, y coordinates=
const targetNode = {x:layout.graph.node(e.w).x, y:layout.graph.node(e.w).y/2, id: e.w};
const targetNode = layout.graph.node(e.w)
targetNode.x = targetNode.x ;
targetNode.y = targetNode.y ;
//create edge points based on start and end node
edge.points = getEdgePoints(sourceNode, targetNode, drawnEdges, renderedNodes,renderedEdgePaths);
edge.points = [];
edge.points.push({ x: sourceNode.x, y: sourceNode.y/2 });
edge.points.push({ x: targetNode.x, y: targetNode.y/2 });
// add to drawn edges
drawnEdges.push(edge);
const paths = insertEdge(edgePaths, e, edge, clusterDb, 'flowchart', g);
//positionEdgeLabel(edge, paths);
});
await swimlaneRender(layout,vert, svg,g,id, conf);
// utils.insertTitle(svg, 'flowchartTitleText', conf.titleTopMargin, diagObj.db.getDiagramTitle());
@ -405,6 +439,152 @@ console.log('diagObj',diagObj);
setupGraphViewbox(g, svg, conf.diagramPadding, conf.useMaxWidth);
};
// function to find edge path points based on start and end node
/**
*
* @param startNode
* @param endNode
* @param drawnEdges
* @param renderedNodes
*/
function getEdgePoints(startNode, endNode, drawnEdges, renderedNodes) {
let potentialEdgePaths = [];
for(let i=1;i<=3;i++){
const points = [];
// add start point
points.push({ x: startNode.x, y: startNode.y })
// Point in the middle, if both nodes do not have same x or y
if (startNode.x !== endNode.x && startNode.y !== endNode.y && i!=1) {
if(i==2){
points.push({ x: startNode.x, y: endNode.y });
}else{
points.push({ x: endNode.x, y: startNode.y });
}
}
// add end point
points.push({ x: endNode.x, y: endNode.y });
//print points
console.log('points before intersection', points);
// get start and end node objects from array of rendered nodes
const startNodeObj = renderedNodes.find(node => node.id === startNode.id);
const endNodeObj = renderedNodes.find(node => node.id === endNode.id);
console.log(" intersection startNodeObj", startNodeObj);
console.log(" intersection endNodeObj", endNodeObj);
startNodeObj.nodeObj.x = startNode.x;
startNodeObj.nodeObj.y = startNode.y;
// the first point should be the intersection of the start node and the edge
let startInsection = startNodeObj.nodeObj.intersect(points[1]);
points[0] = startInsection;
//log intersection
console.log('start intersection', startInsection);
endNodeObj.nodeObj.x = endNode.x;
endNodeObj.nodeObj.y = endNode.y;
// the last point should be the intersection of the end node and the edge
let endInsection = endNodeObj.nodeObj.intersect(points[points.length - 2]);
points[points.length - 1] = endInsection;
//log intersection
console.log('end intersection', endInsection);
//push points to potential edge paths
potentialEdgePaths.push({points: points});
}
// Create a new list of renderedNodes without the start and end node
const filteredRenderedNodes = renderedNodes.filter(node => node.id !== startNode.id && node.id !== endNode.id);
//Rank the potential edge path
const rankedEdgePaths = rankEdgePaths(potentialEdgePaths, filteredRenderedNodes);
if(startNode.id==='sheep' && endNode.id === 'dog'){
console.log('sheep--> dog rankedEdgePaths', rankedEdgePaths);
}
return rankedEdgePaths[0].edgePath.points;
}
// Function to check if a point is inside a nodes bounding box
/**
*
* @param point
* @param nodes
*/
function isPointInsideNode(point, nodes) {
let isInside = false;
for (const node of nodes) {
if (
point.x >= node.nodeObj.x &&
point.x <= node.nodeObj.x + node.boundingBox.width &&
point.y >= node.nodeObj.y &&
point.y <= node.nodeObj.y + node.boundingBox.height
) {
isInside = true;
}
}
return isInside;
}
// Ranks edgePaths (points) based on the number of intersections with nodes
/**
*
* @param edgePaths
* @param nodes
*/
function rankEdgePaths(edgePaths, nodes) {
let rankedEdgePaths = [];
for (const edgePath of edgePaths) {
let rank = 10 + edgePath.points.length;
for (const point of edgePath.points) {
if (isPointInsideNode(point, nodes)) {
// remove edge path
}
}
rankedEdgePaths.push({ rank: rank, edgePath: edgePath });
}
//sort on the basis of rank, highest rank first
rankedEdgePaths.sort((a, b) => (a.rank < b.rank ? 1 : -1));
return rankedEdgePaths;
}
/**
* Function to find if edge path is intersecting with any other edge path
* @param edgePath
* @param renderedEdgePaths
* @returns {boolean}
*/
function isEdgePathIntersecting(edgePath, renderedEdgePaths) {
let isIntersecting = false;
for (const renderedEdgePath of renderedEdgePaths) {
// check if line drawn from start point of edge path to start point of rendered edge path is intersecting with any other edge path
if (
common.isLineIntersecting(
edgePath.points[0],
renderedEdgePath.points[0],
edgePath.points[1],
renderedEdgePath.points[1]
)
) {
isIntersecting = true;
}
}
return isIntersecting;
}
export default {

8237
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff