From 86e46420463fd4693206723625ed2c158439ed23 Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 19 Jul 2024 14:09:31 +0200 Subject: [PATCH 1/2] fix review comments --- cypress/helpers/util.ts | 2 +- cypress/integration/rendering/block.spec.js | 31 ++-- cypress/integration/rendering/c4.spec.js | 10 +- .../rendering/classDiagram-v2.spec.js | 4 +- cypress/platform/viewer.js | 2 - .../mermaid/src/diagrams/state/dataFetcher.js | 3 - .../layout-algorithms/dagre/index.js | 46 ++--- .../dagre/mermaid-graphlib.js | 159 ++++++------------ .../rendering-elements/edges.js | 110 ++---------- 9 files changed, 106 insertions(+), 261 deletions(-) diff --git a/cypress/helpers/util.ts b/cypress/helpers/util.ts index 9f0812a66..133a35032 100644 --- a/cypress/helpers/util.ts +++ b/cypress/helpers/util.ts @@ -97,7 +97,7 @@ export const openURLAndVerifyRendering = ( const name: string = (options.name ?? cy.state('runnable').fullTitle()).replace(/\s+/g, '-'); cy.visit(url); - // cy.window().should('have.property', 'rendered', true); + cy.window().should('have.property', 'rendered', true); cy.get('svg').should('be.visible'); if (validation) { diff --git a/cypress/integration/rendering/block.spec.js b/cypress/integration/rendering/block.spec.js index 9d62c642d..f5d5103e8 100644 --- a/cypress/integration/rendering/block.spec.js +++ b/cypress/integration/rendering/block.spec.js @@ -236,7 +236,7 @@ describe('Block diagram', () => { ); }); - it('BL16: width alignment - blocks shold be equal in width', () => { + it('BL17: width alignment - blocks shold be equal in width', () => { imgSnapshotTest( `block-beta A("This is the text") @@ -247,7 +247,7 @@ describe('Block diagram', () => { ); }); - it('BL17: block types 1 - square, rounded and circle', () => { + it('BL18: block types 1 - square, rounded and circle', () => { imgSnapshotTest( `block-beta A["square"] @@ -258,7 +258,7 @@ describe('Block diagram', () => { ); }); - it('BL18: block types 2 - odd, diamond and hexagon', () => { + it('BL19: block types 2 - odd, diamond and hexagon', () => { imgSnapshotTest( `block-beta A>"rect_left_inv_arrow"] @@ -269,7 +269,7 @@ describe('Block diagram', () => { ); }); - it('BL19: block types 3 - stadium', () => { + it('BL20: block types 3 - stadium', () => { imgSnapshotTest( `block-beta A(["stadium"]) @@ -278,7 +278,7 @@ describe('Block diagram', () => { ); }); - it('BL20: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { + it('BL21: block types 4 - lean right, lean left, trapezoid and inv trapezoid', () => { imgSnapshotTest( `block-beta A[/"lean right"/] @@ -290,7 +290,7 @@ describe('Block diagram', () => { ); }); - it('BL21: block types 1 - square, rounded and circle', () => { + it('BL22: block types 1 - square, rounded and circle', () => { imgSnapshotTest( `block-beta A["square"] @@ -301,7 +301,7 @@ describe('Block diagram', () => { ); }); - it('BL22: sizing - it should be possible to make a block wider', () => { + it('BL23: sizing - it should be possible to make a block wider', () => { imgSnapshotTest( `block-beta A("rounded"):2 @@ -312,7 +312,7 @@ describe('Block diagram', () => { ); }); - it('BL23: sizing - it should be possible to make a composite block wider', () => { + it('BL24: sizing - it should be possible to make a composite block wider', () => { imgSnapshotTest( `block-beta block:2 @@ -324,7 +324,7 @@ describe('Block diagram', () => { ); }); - it('BL24: block in the middle with space on each side', () => { + it('BL25: block in the middle with space on each side', () => { imgSnapshotTest( `block-beta columns 3 @@ -335,7 +335,7 @@ describe('Block diagram', () => { {} ); }); - it('BL25: space and an edge', () => { + it('BL26: space and an edge', () => { imgSnapshotTest( `block-beta columns 5 @@ -345,7 +345,7 @@ describe('Block diagram', () => { {} ); }); - it('BL26: block sizes for regular blocks', () => { + it('BL27: block sizes for regular blocks', () => { imgSnapshotTest( `block-beta columns 3 @@ -354,7 +354,7 @@ describe('Block diagram', () => { {} ); }); - it('BL27: composite block with a set width - f should use the available space', () => { + it('BL28: composite block with a set width - f should use the available space', () => { imgSnapshotTest( `block-beta columns 3 @@ -363,11 +363,12 @@ describe('Block diagram', () => { f end g - `, + `, {} ); }); - it('BL23: composite block with a set width - f and g should split the available space', () => { + + it('BL29: composite block with a set width - f and g should split the available space', () => { imgSnapshotTest( `block-beta columns 3 @@ -379,7 +380,7 @@ describe('Block diagram', () => { h i j - `, + `, {} ); }); diff --git a/cypress/integration/rendering/c4.spec.js b/cypress/integration/rendering/c4.spec.js index f699bd429..00e71adec 100644 --- a/cypress/integration/rendering/c4.spec.js +++ b/cypress/integration/rendering/c4.spec.js @@ -1,7 +1,7 @@ import { imgSnapshotTest, renderGraph } from '../../helpers/util.ts'; describe('C4 diagram', () => { - it('should render a simple C4Context diagram', () => { + it('C4.1 should render a simple C4Context diagram', () => { imgSnapshotTest( ` C4Context @@ -31,7 +31,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Container diagram', () => { + it('C4.2 should render a simple C4Container diagram', () => { imgSnapshotTest( ` C4Container @@ -50,7 +50,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Component diagram', () => { + it('C4.3 should render a simple C4Component diagram', () => { imgSnapshotTest( ` C4Component @@ -68,7 +68,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Dynamic diagram', () => { + it('C4.4 should render a simple C4Dynamic diagram', () => { imgSnapshotTest( ` C4Dynamic @@ -91,7 +91,7 @@ describe('C4 diagram', () => { {} ); }); - it('should render a simple C4Deployment diagram', () => { + it('C4.5 should render a simple C4Deployment diagram', () => { imgSnapshotTest( ` C4Deployment diff --git a/cypress/integration/rendering/classDiagram-v2.spec.js b/cypress/integration/rendering/classDiagram-v2.spec.js index 20a1aea0a..258f8529f 100644 --- a/cypress/integration/rendering/classDiagram-v2.spec.js +++ b/cypress/integration/rendering/classDiagram-v2.spec.js @@ -76,7 +76,7 @@ describe('Class diagram V2', () => { ); }); - it('should render a simple class diagram with different visibilities', () => { + it('2.1 should render a simple class diagram with different visibilities', () => { imgSnapshotTest( ` classDiagram-v2 @@ -93,7 +93,7 @@ describe('Class diagram V2', () => { ); }); - it('should render multiple class diagrams', () => { + it('3: should render multiple class diagrams', () => { imgSnapshotTest( [ ` diff --git a/cypress/platform/viewer.js b/cypress/platform/viewer.js index 54f8a4cd7..beb7015a5 100644 --- a/cypress/platform/viewer.js +++ b/cypress/platform/viewer.js @@ -1,5 +1,4 @@ import mermaid from './mermaid.esm.mjs'; -// import flowchartELK from './mermaid-flowchart-elk.esm.mjs'; import { layouts } from './mermaid-layout-elk.esm.mjs'; import externalExample from './mermaid-example-diagram.esm.mjs'; import zenUml from './mermaid-zenuml.esm.mjs'; @@ -48,7 +47,6 @@ const contentLoaded = async function () { document.getElementsByTagName('body')[0].appendChild(div); } - // await mermaid.registerExternalDiagrams([externalExample, zenUml, flowchartELK]); await mermaid.registerExternalDiagrams([externalExample, zenUml]); mermaid.registerLayoutLoaders(layouts); diff --git a/packages/mermaid/src/diagrams/state/dataFetcher.js b/packages/mermaid/src/diagrams/state/dataFetcher.js index 760bf9ef8..35c1d933f 100644 --- a/packages/mermaid/src/diagrams/state/dataFetcher.js +++ b/packages/mermaid/src/diagrams/state/dataFetcher.js @@ -198,9 +198,6 @@ function getClassesFromDbInfo(dbInfoItem) { * Get classes from the db for the info item. * If there aren't any or if dbInfoItem isn't defined, return an empty string. * Else create 1 string from the list of classes found - * - * @param {undefined | null | object} dbInfoItem - * @returns {string} */ function getStylesFromDbInfo(dbInfoItem) { if (dbInfoItem === undefined || dbInfoItem === null) { diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js index 9eb8ca5fa..8851a1d95 100644 --- a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js @@ -121,7 +121,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit graph ); log.info(findNonClusterChild(node.id, graph)); - clusterDb[node.id] = { id: findNonClusterChild(node.id, graph), node }; + clusterDb.set(node.id, { id: findNonClusterChild(node.id, graph), node }); // insertCluster(clusters, graph.node(v)); } else { log.trace('Node - the non recursive path XAX', v, node.id, node); @@ -138,7 +138,16 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e))); // Check if link is either from or to a cluster - log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]); + log.info( + 'Fix', + clusterDb, + 'ids:', + e.v, + e.w, + 'Translating: ', + clusterDb.get(e.v), + clusterDb.get(e.w) + ); await insertEdgeLabel(edgeLabels, edge); }); @@ -147,20 +156,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit await processEdges(); - // // 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 - // await graph.edges().forEach(async function (e) { - // const edge = graph.edge(e.v, e.w, e.name); - // log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); - // log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e))); - - // // Check if link is either from or to a cluster - // log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]); - // await insertEdgeLabel(edgeLabels, edge); - // }); - log.info('Graph before layout:', JSON.stringify(graphlibJson.write(graph))); log.info('############################################# XXX'); @@ -198,7 +193,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit node.y, graph.parent(v) ); - clusterDb[node.id].node = node; + clusterDb.get(node.id).node = node; positionNode(node); } else { // A tainted cluster node @@ -222,7 +217,7 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit await insertCluster(clusters, node); // A cluster in the non-recursive way - clusterDb[node.id].node = node; + clusterDb.get(node.id).node = node; } else { // Regular node const parent = graph.node(node.parentId); @@ -255,7 +250,6 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit const edge = graph.edge(e); log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(edge), edge); - // OBS HERE edge.points.forEach((point) => (point.y += subGraphTitleTotalMargin / 2)); const startNode = graph.node(e.v); var endNode = graph.node(e.w); @@ -273,17 +267,8 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit log.warn('Returning from recursive render XAX', elem, diff); return { elem, diff }; }; -/** - * ############################################################### - * Render the graph - * ############################################################### - * @param data4Layout - * @param svg - * @param element - */ + export const render = async (data4Layout, svg, element) => { - // Create the input mermaid.graph - // console.log('XYZ data4Layout', data4Layout); const graph = new graphlib.Graph({ multigraph: true, compound: true, @@ -305,15 +290,12 @@ export const render = async (data4Layout, svg, element) => { return {}; }); - // Org - insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); clearNodes(); clearEdges(); clearClusters(); clearGraphlib(); - // Add the nodes and edges to the graph data4Layout.nodes.forEach((node) => { graph.setNode(node.id, { ...node }); if (node.parentId) { diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js index 1f7ebee9f..456dde1f1 100644 --- a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/mermaid-graphlib.js @@ -3,39 +3,40 @@ import { log } from '$root/logger.js'; import * as graphlib from 'dagre-d3-es/src/graphlib/index.js'; import * as graphlibJson from 'dagre-d3-es/src/graphlib/json.js'; -export let clusterDb = {}; -let descendants = {}; -let parents = {}; +export let clusterDb = new Map(); +let descendants = new Map(); +let parents = new Map(); export const clear = () => { - descendants = {}; - parents = {}; - clusterDb = {}; + descendants.clear(); + parents.clear(); + clusterDb.clear(); }; const isDescendant = (id, ancestorId) => { - log.trace('In isDescendant', ancestorId, ' ', id, ' = ', descendants[ancestorId].includes(id)); - return descendants[ancestorId].includes(id); + const ancestorDescendants = descendants.get(ancestorId) || []; + log.trace('In isDescendant', ancestorId, ' ', id, ' = ', ancestorDescendants.includes(id)); + return ancestorDescendants.includes(id); }; const edgeInCluster = (edge, clusterId) => { - log.info('Descendants of ', clusterId, ' is ', descendants[clusterId]); + const clusterDescendants = descendants.get(clusterId) || []; + log.info('Descendants of ', clusterId, ' is ', clusterDescendants); 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 (!descendants[clusterId]) { + if (!clusterDescendants) { log.debug('Tilt, ', clusterId, ',not in descendants'); return false; } return ( - descendants[clusterId].includes(edge.v) || + clusterDescendants.includes(edge.v) || isDescendant(edge.v, clusterId) || isDescendant(edge.w, clusterId) || - descendants[clusterId].includes(edge.w) + clusterDescendants.includes(edge.w) ); }; @@ -51,7 +52,6 @@ const copy = (clusterId, graph, newGraph, rootId) => { ); const nodes = graph.children(clusterId) || []; - // Include cluster node if it is not the root if (clusterId !== rootId) { nodes.push(clusterId); } @@ -63,7 +63,7 @@ const copy = (clusterId, graph, newGraph, rootId) => { copy(node, graph, newGraph, rootId); } else { const data = graph.node(node); - log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); //,node, data, ' parent is ', clusterId); + log.info('cp ', node, ' to ', rootId, ' with parent ', clusterId); newGraph.setNode(node, data); if (rootId !== graph.parent(node)) { log.warn('Setting parent', node, graph.parent(node)); @@ -91,7 +91,6 @@ const copy = (clusterId, graph, newGraph, rootId) => { const data = graph.edge(edge.v, edge.w, edge.name); 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.info('Copying as ', edge.v, edge.w, data, edge.name); newGraph.setEdge(edge.v, edge.w, data, edge.name); @@ -117,25 +116,19 @@ const copy = (clusterId, graph, newGraph, rootId) => { graph.removeNode(node); }); }; + export const extractDescendants = (id, graph) => { - // log.debug('Extracting ', id); const children = graph.children(id); let res = [...children]; for (const child of children) { - parents[child] = id; + parents.set(child, id); res = [...res, ...extractDescendants(child, graph)]; } return res; }; -/** - * Validates the graph, checking that all parent child relation points to existing nodes and that - * edges between nodes also ia correct. When not correct the function logs the discrepancies. - * - * @param graph - */ export const validate = (graph) => { const edges = graph.edges(); log.trace('Edges: ', edges); @@ -168,13 +161,6 @@ const findCommonEdges = (graph, id1, id2) => { return result; }; -/** - * Finds a child that is not a cluster. When faking an edge between a node and a cluster. - * - * @param id - * @param {any} graph - * @param {string} clusterId - */ export const findNonClusterChild = (id, graph, clusterId) => { const children = graph.children(id); log.trace('Searching children of id ', id, children); @@ -185,19 +171,10 @@ export const findNonClusterChild = (id, graph, clusterId) => { for (const child of children) { const _id = findNonClusterChild(child, graph, clusterId); - // Edge chase where the cluster has an edge to a node and the selected - // child has a link to the same node const commonEdges = findCommonEdges(graph, clusterId, _id); if (_id) { if (commonEdges.length > 0) { - // console.log( - // '\x1B[44;93;4m abc24 The replacement also has an edge', - // clusterId, - // ' => ', - // _id, - // graph.edges() - // ); reserve = _id; } else { return _id; @@ -208,17 +185,15 @@ export const findNonClusterChild = (id, graph, clusterId) => { }; const getAnchorId = (id) => { - if (!clusterDb[id]) { + if (!clusterDb.has(id)) { return id; } - // If the cluster has no external connections - if (!clusterDb[id].externalConnections) { + if (!clusterDb.get(id).externalConnections) { return id; } - // Return the replacement node - if (clusterDb[id]) { - return clusterDb[id].id; + if (clusterDb.has(id)) { + return clusterDb.get(id).id; } return id; }; @@ -230,8 +205,7 @@ export const adjustClustersAndEdges = (graph, depth) => { } else { log.debug('Opting in, graph '); } - // Go through the nodes and for each cluster found, save a replacement node, this can be used when - // faking a link to a cluster + graph.nodes().forEach(function (id) { const children = graph.children(id); if (children.length > 0) { @@ -241,34 +215,24 @@ export const adjustClustersAndEdges = (graph, depth) => { ' Replacement id in edges: ', findNonClusterChild(id, graph, id) ); - descendants[id] = extractDescendants(id, graph); - clusterDb[id] = { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) }; + descendants.set(id, extractDescendants(id, graph)); + clusterDb.set(id, { id: findNonClusterChild(id, graph, id), clusterData: graph.node(id) }); } }); - // Check incoming and outgoing edges for each cluster graph.nodes().forEach(function (id) { const children = graph.children(id); const edges = graph.edges(); if (children.length > 0) { log.debug('Cluster identified', id, descendants); edges.forEach((edge) => { - // log.debug('Edge, descendants: ', edge, descendants[id]); + const d1 = isDescendant(edge.v, id); + const d2 = isDescendant(edge.w, id); - // Check if any edge leaves the cluster (not the actual cluster, that's a link from the box) - if (edge.v !== id && edge.w !== id) { - // Any edge where either the one of the nodes is descending to the cluster but not the other - // if (descendants[id].indexOf(edge.v) < 0 && descendants[id].indexOf(edge.w) < 0) { - - const d1 = isDescendant(edge.v, id); - const d2 = isDescendant(edge.w, id); - - // d1 xor d2 - if either d1 is true and d2 is false or the other way around - if (d1 ^ d2) { - log.warn('Edge: ', edge, ' leaves cluster ', id); - log.warn('Descendants of XXX ', id, ': ', descendants[id]); - clusterDb[id].externalConnections = true; - } + if (d1 ^ d2) { + log.warn('Edge: ', edge, ' leaves cluster ', id); + log.warn('Descendants of XXX ', id, ': ', descendants.get(id)); + clusterDb.get(id).externalConnections = true; } }); } else { @@ -276,18 +240,15 @@ export const adjustClustersAndEdges = (graph, depth) => { } }); - for (let id of Object.keys(clusterDb)) { - const nonClusterChild = clusterDb[id].id; + for (let id of clusterDb.keys()) { + const nonClusterChild = clusterDb.get(id).id; const parent = graph.parent(nonClusterChild); - // Change replacement node of id to parent of current replacement node if valid - if (parent !== id && clusterDb[parent] && !clusterDb[parent].externalConnections) { - clusterDb[id].id = parent; + if (parent !== id && clusterDb.has(parent) && !clusterDb.get(parent).externalConnections) { + clusterDb.get(id).id = parent; } } - // For clusters with incoming and/or outgoing edges translate those edges to a real node - // in the cluster in order to fake the edge graph.edges().forEach(function (e) { const edge = graph.edge(e); log.warn('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e)); @@ -295,7 +256,6 @@ export const adjustClustersAndEdges = (graph, depth) => { let v = e.v; let w = e.w; - // Check if link is either from or to a cluster log.warn( 'Fix XXX', clusterDb, @@ -303,14 +263,13 @@ export const adjustClustersAndEdges = (graph, depth) => { e.v, e.w, 'Translating: ', - clusterDb[e.v], + clusterDb.get(e.v), ' --- ', - clusterDb[e.w] + clusterDb.get(e.w) ); - if (clusterDb[e.v] && clusterDb[e.w] && clusterDb[e.v] === clusterDb[e.w]) { - // cspell:ignore trixing - log.warn('Fixing and trixing link to self - removing XXX', e.v, e.w, e.name); - log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name); + if (clusterDb.get(e.v) && clusterDb.get(e.w) && clusterDb.get(e.v) === clusterDb.get(e.w)) { + log.warn('Fixing and trying link to self - removing XXX', e.v, e.w, e.name); + log.warn('Fixing and trying - removing XXX', e.v, e.w, e.name); v = getAnchorId(e.v); w = getAnchorId(e.w); graph.removeEdge(e.v, e.w, e.name); @@ -352,19 +311,19 @@ export const adjustClustersAndEdges = (graph, depth) => { graph.setEdge(v, specialId1, edge1, e.name + '-cyclic-special-0'); graph.setEdge(specialId1, specialId2, edgeMid, e.name + '-cyclic-special-1'); graph.setEdge(specialId2, w, edge2, e.name + '-cyclic-special-2'); - } else if (clusterDb[e.v] || clusterDb[e.w]) { - log.warn('Fixing and trixing - removing XXX', e.v, e.w, e.name); + } else if (clusterDb.get(e.v) || clusterDb.get(e.w)) { + log.warn('Fixing and trying - 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) { const parent = graph.parent(v); - clusterDb[parent].externalConnections = true; + clusterDb.get(parent).externalConnections = true; edge.fromCluster = e.v; } if (w !== e.w) { const parent = graph.parent(w); - clusterDb[parent].externalConnections = true; + clusterDb.get(parent).externalConnections = true; edge.toCluster = e.w; } log.warn('Fix Replacing with XXX', v, w, e.name); @@ -390,9 +349,6 @@ export const extractor = (graph, depth) => { log.error('Bailing out'); return; } - // For clusters without incoming and/or outgoing edges, create a new cluster-node - // containing the nodes and edges in the custer in a new graph - // for (let i = 0;) let nodes = graph.nodes(); let hasChildren = false; for (const node of nodes) { @@ -404,30 +360,23 @@ export const extractor = (graph, depth) => { log.debug('Done, no node has children', graph.nodes()); return; } - // const clusters = Object.keys(clusterDb); - // clusters.forEach(clusterId => { log.debug('Nodes = ', nodes, depth); for (const node of nodes) { log.debug( 'Extracting node', node, clusterDb, - clusterDb[node] && !clusterDb[node].externalConnections, + clusterDb.has(node) && !clusterDb.get(node).externalConnections, !graph.parent(node), graph.node(node), graph.children('D'), ' Depth ', depth ); - // Note that the node might have been removed after the Object.keys call so better check - // that it still is in the game - if (!clusterDb[node]) { - // Skip if the node is not a cluster + if (!clusterDb.has(node)) { log.debug('Not a cluster', node, depth); - // break; } else if ( - !clusterDb[node].externalConnections && - // !graph.parent(node) && + !clusterDb.get(node).externalConnections && graph.children(node) && graph.children(node).length > 0 ) { @@ -439,9 +388,9 @@ export const extractor = (graph, depth) => { const graphSettings = graph.graph(); let dir = graphSettings.rankdir === 'TB' ? 'LR' : 'TB'; - if (clusterDb[node]?.clusterData?.dir) { - dir = clusterDb[node].clusterData.dir; - log.warn('Fixing dir', clusterDb[node].clusterData.dir, dir); + if (clusterDb.get(node)?.clusterData?.dir) { + dir = clusterDb.get(node).clusterData.dir; + log.warn('Fixing dir', clusterDb.get(node).clusterData.dir, dir); } const clusterGraph = new graphlib.Graph({ @@ -449,7 +398,7 @@ export const extractor = (graph, depth) => { compound: true, }) .setGraph({ - rankdir: dir, // Todo: set proper spacing + rankdir: dir, nodesep: 50, ranksep: 50, marginx: 8, @@ -464,8 +413,8 @@ export const extractor = (graph, depth) => { graph.setNode(node, { clusterNode: true, id: node, - clusterData: clusterDb[node].clusterData, - label: clusterDb[node].label, + clusterData: clusterDb.get(node).clusterData, + label: clusterDb.get(node).label, graph: clusterGraph, }); log.warn('New graph after copy node: (', node, ')', graphlibJson.write(clusterGraph)); @@ -475,7 +424,7 @@ export const extractor = (graph, depth) => { 'Cluster ** ', node, ' **not meeting the criteria !externalConnections:', - !clusterDb[node].externalConnections, + !clusterDb.get(node).externalConnections, ' no parent: ', !graph.parent(node), ' children ', @@ -502,7 +451,7 @@ const sorter = (graph, nodes) => { if (nodes.length === 0) { return []; } - let result = Object.assign(nodes); + let result = Object.assign([], nodes); nodes.forEach((node) => { const children = graph.children(node); const sorted = sorter(graph, children); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js index ca59ec2a5..4acfa4335 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js @@ -154,7 +154,6 @@ export const positionEdgeLabel = (edge, paths) => { let x = edge.x; let y = edge.y; if (path) { - // // debugger; const pos = utils.calcLabelPosition(path); log.debug( 'Moving label ' + edge.label + ' from (', @@ -175,13 +174,11 @@ export const positionEdgeLabel = (edge, paths) => { el.attr('transform', `translate(${x}, ${y + subGraphTitleTotalMargin / 2})`); } - //let path = paths.updatedPath ? paths.updatedPath : paths.originalPath; if (edge.startLabelLeft) { const el = terminalLabels.get(edge.id).startLeft; let x = edge.x; let y = edge.y; if (path) { - // debugger; const pos = utils.calcTerminalLabelPosition(edge.arrowTypeStart ? 10 : 0, 'start_left', path); x = pos.x; y = pos.y; @@ -193,7 +190,6 @@ export const positionEdgeLabel = (edge, paths) => { let x = edge.x; let y = edge.y; if (path) { - // debugger; const pos = utils.calcTerminalLabelPosition( edge.arrowTypeStart ? 10 : 0, 'start_right', @@ -209,7 +205,6 @@ export const positionEdgeLabel = (edge, paths) => { let x = edge.x; let y = edge.y; if (path) { - // debugger; const pos = utils.calcTerminalLabelPosition(edge.arrowTypeEnd ? 10 : 0, 'end_left', path); x = pos.x; y = pos.y; @@ -275,21 +270,18 @@ export const intersection = (node, outsidePoint, insidePoint) => { res.y = outsidePoint.y; } - log.debug(`abc89 topp/bott calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); // cspell: disable-line + log.debug(`abc89 top/bottom calc, Q ${Q}, q ${q}, R ${R}, r ${r}`, res); return res; } else { - // Intersection onn sides of rect + // Intersection on sides of rect if (insidePoint.x < outsidePoint.x) { r = outsidePoint.x - w - x; } else { r = x - w - outsidePoint.x; } let q = (Q * r) / R; - // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x + dx - w; - // OK let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : insidePoint.x - R + r; - // let _x = insidePoint.x < outsidePoint.x ? insidePoint.x + R - r : outsidePoint.x + r; let _y = insidePoint.y < outsidePoint.y ? insidePoint.y + q : insidePoint.y - q; log.debug(`sides calc abc89, Q ${Q}, q ${q}, R ${R}, r ${r}`, { _x, _y }); if (r === 0) { @@ -306,49 +298,34 @@ export const intersection = (node, outsidePoint, insidePoint) => { return { x: _x, y: _y }; } }; -/** - * This function will page a path and node where the last point(s) in the path is inside the node - * and return an update path ending by the border of the node. - * - * @param {Array} _points - * @param {any} boundaryNode - * @returns {Array} Points - */ + const cutPathAtIntersect = (_points, boundaryNode) => { log.warn('abc88 cutPathAtIntersect', _points, boundaryNode); let points = []; let lastPointOutside = _points[0]; let isInside = false; _points.forEach((point) => { - // const node = clusterDb[edge.toCluster].node; log.info('abc88 checking point', point, boundaryNode); - // check if point is inside the boundary rect if (!outsideNode(boundaryNode, point) && !isInside) { - // First point inside the rect found - // Calc the intersection coord between the point anf the last point outside the rect const inter = intersection(boundaryNode, lastPointOutside, point); log.debug('abc88 inside', point, lastPointOutside, inter); log.debug('abc88 intersection', inter, boundaryNode); - // // Check case where the intersection is the same as the last point let pointPresent = false; points.forEach((p) => { pointPresent = pointPresent || (p.x === inter.x && p.y === inter.y); }); - // // if (!pointPresent) { + if (!points.some((e) => e.x === inter.x && e.y === inter.y)) { points.push(inter); } else { log.warn('abc88 no intersect', inter, points); } - // points.push(inter); isInside = true; } else { - // Outside log.warn('abc88 outside', point, lastPointOutside); lastPointOutside = point; - // points.push(point); if (!isInside) { points.push(point); } @@ -358,13 +335,6 @@ const cutPathAtIntersect = (_points, boundaryNode) => { return points; }; -/** - * Given an edge, this function will return the corner points of the edge. This is defined as: - * one point that has a previous point and a next point such as the angle between the previous - * point and the next point is 90 degrees. Meaning that the previous point has the same x coordinate - * as the center point and at the same time the next point has the same y coordinate or vice versa. - * @param points - */ function extractCornerPoints(points) { const cornerPoints = []; const cornerPointPositions = []; @@ -401,11 +371,6 @@ const findAdjacentPoint = function (pointA, pointB, distance) { return { x: pointB.x - ratio * xDiff, y: pointB.y - ratio * yDiff }; }; -/** - * Given an array of points, this function will return a new array of points where the corners have been removed and replaced with - * adjacent points in each direction. SO a corder will be replaced with a point before and the point after the corner. - */ - const fixCorners = function (lineData) { const { cornerPointPositions } = extractCornerPoints(lineData); const newLineData = []; @@ -415,7 +380,6 @@ const fixCorners = function (lineData) { const nextPoint = lineData[i + 1]; const cornerPoint = lineData[i]; - // Find a new point on the line point 5 points back and push it to the new array const newPrevPoint = findAdjacentPoint(prevPoint, cornerPoint, 5); const newNextPoint = findAdjacentPoint(nextPoint, cornerPoint, 5); @@ -466,9 +430,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod var head = endNode; if (head.intersect && tail.intersect) { - // log.info('abc88 InsertEdge: 0.5', edge.start, '-->', edge.end, JSON.stringify(points)); points = points.slice(1, edge.points.length - 1); - // log.info('abc88 InsertEdge APA12: 0.7', edge.start, '-->', edge.end, JSON.stringify(points)); points.unshift(tail.intersect(points[0])); log.debug( 'Last point APA12', @@ -481,59 +443,43 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod ); points.push(head.intersect(points[points.length - 1])); } - // log.info('abc88 InsertEdge 2 SPLIT: ', points); if (edge.toCluster) { - log.info('to cluster abc88', clusterDb[edge.toCluster]); - points = cutPathAtIntersect(edge.points, clusterDb[edge.toCluster].node); + log.info('to cluster abc88', clusterDb.get(edge.toCluster)); + points = cutPathAtIntersect(edge.points, clusterDb.get(edge.toCluster).node); pointsHasChanged = true; } if (edge.fromCluster) { - log.debug('from cluster abc88', clusterDb[edge.fromCluster], JSON.stringify(points, null, 2)); - points = cutPathAtIntersect(points.reverse(), clusterDb[edge.fromCluster].node).reverse(); - // log.info('from cluster abc88 fixed', JSON.stringify(points, null, 2)); + log.debug( + 'from cluster abc88', + clusterDb.get(edge.fromCluster), + JSON.stringify(points, null, 2) + ); + points = cutPathAtIntersect(points.reverse(), clusterDb.get(edge.fromCluster).node).reverse(); pointsHasChanged = true; } - // The data for our line let lineData = points.filter((p) => !Number.isNaN(p.y)); - // const { cornerPoints, cornerPointPositions } = extractCornerPoints(lineData); lineData = fixCorners(lineData); let lastPoint = lineData[lineData.length - 1]; if (lineData.length > 1) { lastPoint = lineData[lineData.length - 1]; const secondLastPoint = lineData[lineData.length - 2]; - // Calculate the mid point of the last two points const diffX = (lastPoint.x - secondLastPoint.x) / 2; const diffY = (lastPoint.y - secondLastPoint.y) / 2; const midPoint = { x: secondLastPoint.x + diffX, y: secondLastPoint.y + diffY }; lineData.splice(-1, 0, midPoint); } - // This is the accessor function we talked about above let curve = curveBasis; - // curve = curveCardinal; - // curve = curveLinear; - // curve = curveNatural; - // curve = curveCatmullRom.alpha(0.5); - // curve = curveCatmullRom; - // curve = curveCardinal.tension(0.7); - // curve = curveMonotoneY; - // let curve = interpolateToCurve([5], curveNatural, 0.01, 10); - - // Currently only flowcharts get the curve from the settings, perhaps this should - // be expanded to a common setting? Restricting it for now in order not to cause side-effects that - // have not been thought through if (edge.curve) { curve = edge.curve; } const { x, y } = getLineFunctionsWithOffset(edge); - // const lineFunction = edge.curve ? line().x(x).y(y).curve(curve) : roundedCornersLine; const lineFunction = line().x(x).y(y).curve(curve); - // Construct stroke classes based on properties let strokeClasses; switch (edge.thickness) { case 'normal': @@ -563,16 +509,11 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod } let svgPath; let linePath = lineFunction(lineData); - //convert to array (if not already) const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style]; if (edge.look === 'handdrawn') { const rc = rough.svg(elem); Object.assign([], lineData); - // const svgPathNode = rc.path(lineFunction(ld.splice(0, ld.length-1)), { - // const svgPathNode = rc.path(lineFunction(ld), { - // roughness: 0.3, - // seed: handdrawnSeed, - // }); + const svgPathNode = rc.path(linePath, { roughness: 0.3, seed: handdrawnSeed, @@ -582,7 +523,6 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod svgPath = select(svgPathNode) .select('path') - // .attr('d', lineFunction(lineData)) .attr('id', edge.id) .attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '')) .attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); @@ -597,29 +537,8 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod .attr('class', ' ' + strokeClasses + (edge.classes ? ' ' + edge.classes : '')) .attr('style', edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : ''); } - // DEBUG code, adds a red circle at each edge coordinate - // cornerPoints.forEach((point) => { - // elem - // .append('circle') - // .style('stroke', 'blue') - // .style('fill', 'blue') - // .attr('r', 3) - // .attr('cx', point.x) - // .attr('cy', point.y); - // }); - - // lineData.forEach((point) => { - // elem - // .append('circle') - // .style('stroke', 'red') - // .style('fill', 'red') - // .attr('r', 1) - // .attr('cx', point.x) - // .attr('cy', point.y); - // }); let url = ''; - // // TODO: Can we load this config only from the rendered graph type? if (getConfig().flowchart.arrowMarkerAbsolute || getConfig().state.arrowMarkerAbsolute) { url = window.location.protocol + @@ -627,8 +546,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod window.location.host + window.location.pathname + window.location.search; - url = url.replace(/\(/g, '\\('); - url = url.replace(/\)/g, '\\)'); + url = url.replace(/\(/g, '\\(').replace(/\)/g, '\\)'); } log.info('arrowTypeStart', edge.arrowTypeStart); log.info('arrowTypeEnd', edge.arrowTypeEnd); From 2800f555c7bd7663987c77ab0fced778d683722e Mon Sep 17 00:00:00 2001 From: Ashish Jain Date: Fri, 19 Jul 2024 14:12:19 +0200 Subject: [PATCH 2/2] fix lint issue --- packages/mermaid-layout-elk/src/render.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index 311c359d6..d2fdb6c09 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -925,7 +925,6 @@ export const intersection = (node, outsidePoint, insidePoint) => { } }; const outsideNode = (node, point) => { - console.log('Checking bounds ', node, point); const x = node.x; const y = node.y; const dx = Math.abs(point.x - x);