diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 90ecd20ea..26ca690f3 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -105,11 +105,11 @@
-+flowchart LR AB["apa@apa@"] --> B(("`apa@apa`"))-+flowchart D(("for D"))@@ -119,6 +119,208 @@ e1@{ animate: true}+--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@==> BB + e1@{ animate: true} ++
++ flowchart LR + A e1@==> B + e1@{ animate: false} +++--- +config: + theme: neo + look: handDrawn +--- + flowchart LR + A e1@==> BB + e1@{ animate: false} ++ + +
++ flowchart LR + slow e2@--> B + e2@{ animation: slow} +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@--> BB + e1@{ animation: fast} ++
++ flowchart LR + A e1@==> B + classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite; + class e1 animate +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@==> BB + classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite; + class e1 animate ++ +
+ ++ flowchart LR + A e1@==o B + e1@{ animate: true} +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@==o BB + e1@{ animate: true} ++
++ flowchart LR + A e1@==o B + e1@{ animate: false} +++--- +config: + theme: neo + look: handDrawn +--- + flowchart LR + A e1@==o BB + e1@{ animate: false} ++ + +
++ flowchart LR + slow e2@--o B + e2@{ animation: slow} +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@--o BB + e1@{ animation: fast} ++
++ flowchart LR + A e1@==o B + classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite; + class e1 animate +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@==o BB + classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite; + class e1 animate ++ +
+ ++ flowchart LR + A e1@==x B + e1@{ animate: true} +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@==x BB + e1@{ animate: true} ++
++ flowchart LR + A e1@==x B + e1@{ animate: false} +++--- +config: + theme: neo + look: handDrawn +--- + flowchart LR + A e1@==x BB + e1@{ animate: false} ++ + +
++ flowchart LR + slow e2@--x B + e2@{ animation: slow} +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@--x BB + e1@{ animation: fast} ++
++ flowchart LR + A e1@==x B + classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite; + class e1 animate +++--- +config: + theme: forest + look: classic +--- + flowchart LR + A e1@==x BB + classDef animate stroke-dasharray: 9\,5,stroke-dashoffset: 900,animation: dash 25s linear infinite; + class e1 animate ++flowchart LR A e1@--> B classDef animate stroke-width:2,stroke-dasharray:10\,8,stroke-dashoffset:-180,animation: edge-animation-frame 6s linear infinite, stroke-linecap: round diff --git a/packages/mermaid-layout-elk/src/render.ts b/packages/mermaid-layout-elk/src/render.ts index 3817d15fb..26d720fad 100644 --- a/packages/mermaid-layout-elk/src/render.ts +++ b/packages/mermaid-layout-elk/src/render.ts @@ -781,13 +781,7 @@ export const render = async ( const elk = new ELK(); const element = svg.select('g'); // Add the arrowheads to the svg - insertMarkers( - element, - data4Layout.markers, - data4Layout.type, - data4Layout.diagramId, - data4Layout.config - ); + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); // Setup the graph with the layout options and the data for the layout let elkGraph: any = { 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 6be8ba9c3..c117fe6c2 100644 --- a/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js +++ b/packages/mermaid/src/rendering-util/layout-algorithms/dagre/index.js @@ -290,13 +290,7 @@ export const render = async (data4Layout, svg) => { return {}; }); const element = svg.select('g'); - insertMarkers( - element, - data4Layout.markers, - data4Layout.type, - data4Layout.diagramId, - data4Layout.config - ); + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); clearNodes(); clearEdges(); clearClusters(); diff --git a/packages/mermaid/src/rendering-util/layout-algorithms/fixed/index.js b/packages/mermaid/src/rendering-util/layout-algorithms/fixed/index.js index 88ce71e05..145424e48 100644 --- a/packages/mermaid/src/rendering-util/layout-algorithms/fixed/index.js +++ b/packages/mermaid/src/rendering-util/layout-algorithms/fixed/index.js @@ -94,10 +94,10 @@ export const calcNodeIntersections = async (targetNodeId, _node1, _node2) => { } // Insert node will not give any widths as the element is not in the DOM - node1.width = _node1.width || 50; - node1.height = _node1.height || 50; - node2.width = _node2.width || 50; - node2.height = _node2.height || 50; + node1.width = _node1.width ?? 50; + node1.height = _node1.height ?? 50; + node2.width = _node2.width ?? 50; + node2.height = _node2.height ?? 50; const startIntersection = calcIntersectionPoint(node1, { x: node2.x, y: node2.y }); const endIntersection = calcIntersectionPoint(node2, { x: node1.x, y: node1.y }); @@ -402,13 +402,7 @@ const doRender = async (_elem, data4Layout, siteConfig, positions) => { export const render = async (data4Layout, svg, _internalHelpers, _algorithm, positions) => { const element = svg.select('g'); // Org - insertMarkers( - element, - data4Layout.markers, - data4Layout.type, - data4Layout.diagramId, - data4Layout.config - ); + insertMarkers(element, data4Layout.markers, data4Layout.type, data4Layout.diagramId); clearNodes(); clearEdges(); clearClusters(); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts b/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts index 5371ac32d..d28a33bbb 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/edgeMarker.ts @@ -15,13 +15,14 @@ export const addEdgeMarkers = ( edge: Pick, url: string, id: string, - diagramType: string + diagramType: string, + useMargin = false ) => { if (edge.arrowTypeStart) { - addEdgeMarker(svgPath, 'start', edge.arrowTypeStart, url, id, diagramType); + addEdgeMarker(svgPath, 'start', edge.arrowTypeStart, url, id, diagramType, useMargin); } if (edge.arrowTypeEnd) { - addEdgeMarker(svgPath, 'end', edge.arrowTypeEnd, url, id, diagramType); + addEdgeMarker(svgPath, 'end', edge.arrowTypeEnd, url, id, diagramType, useMargin); } }; @@ -37,15 +38,19 @@ const arrowTypesMap = { lollipop: 'lollipop', } as const; +const arrowTypesWithMarginSupport = ['cross', 'point', 'circle']; + const addEdgeMarker = ( svgPath: SVG, position: 'start' | 'end', arrowType: string, url: string, id: string, - diagramType: string + diagramType: string, + useMargin = false ) => { const endMarkerType = arrowTypesMap[arrowType as keyof typeof arrowTypesMap]; + const marginSupport = arrowTypesWithMarginSupport.includes(endMarkerType); if (!endMarkerType) { log.warn(`Unknown arrow type: ${arrowType}`); @@ -53,5 +58,9 @@ const addEdgeMarker = ( } const suffix = position === 'start' ? 'Start' : 'End'; - svgPath.attr(`marker-${position}`, `url(${url}#${id}_${diagramType}-${endMarkerType}${suffix})`); + const offset = useMargin && marginSupport ? '-margin' : ''; + svgPath.attr( + `marker-${position}`, + `url(${url}#${id}_${diagramType}-${endMarkerType}${suffix}${offset})` + ); }; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/edges.js b/packages/mermaid/src/rendering-util/rendering-elements/edges.js index 767f85ddd..68e374305 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/edges.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/edges.js @@ -544,7 +544,7 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod ? generateRoundedPath(applyMarkerOffsetsToPoints(lineData, edge), 5) : lineFunction(lineData); const edgeStyles = Array.isArray(edge.style) ? edge.style : [edge.style]; - + let animatedEdge = false; if (edge.look === 'handDrawn') { const rc = rough.svg(elem); Object.assign([], lineData); @@ -592,11 +592,13 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod ';' + (edgeStyles ? edgeStyles.reduce((acc, style) => acc + ';' + style, '') : '') ); + + animatedEdge = + edge.animate === true || !!edge.animation || stylesFromClasses.includes('animation'); const len = svgPath.node().getTotalLength(); const oValueS = markerOffsets2[edge.arrowTypeStart] || 0; const oValueE = markerOffsets2[edge.arrowTypeEnd] || 0; - - if (edge.look === 'neo') { + if (edge.look === 'neo' && !animatedEdge) { const dashArray = `0 ${oValueS} ${len - oValueS - oValueE} ${oValueE}`; // No offset needed because we already start with a zero-length dash that effectively sets us up for a gap at the start. @@ -646,7 +648,9 @@ export const insertEdge = function (elem, edge, clusterDb, diagramType, startNod log.info('arrowTypeStart', edge.arrowTypeStart); log.info('arrowTypeEnd', edge.arrowTypeEnd); - addEdgeMarkers(svgPath, edge, url, id, diagramType); + const useMargin = !animatedEdge && edge?.look === 'neo'; + + addEdgeMarkers(svgPath, edge, url, id, diagramType, useMargin); let paths = {}; if (pointsHasChanged) { diff --git a/packages/mermaid/src/rendering-util/rendering-elements/markers.js b/packages/mermaid/src/rendering-util/rendering-elements/markers.js index 98d918660..7797f13e8 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/markers.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/markers.js @@ -2,9 +2,9 @@ import { log } from '../../logger.js'; // Only add the number of markers that the diagram needs -const insertMarkers = (elem, markerArray, type, id, config) => { +const insertMarkers = (elem, markerArray, type, id) => { markerArray.forEach((markerName) => { - markers[markerName](elem, type, id, config); + markers[markerName](elem, type, id); }); }; @@ -153,13 +153,13 @@ const lollipop = (elem, type, id) => { .attr('cy', 7) .attr('r', 6); }; -const point = (elem, type, id, options) => { +const point = (elem, type, id) => { elem .append('marker') .attr('id', id + '_' + type + '-pointEnd') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 11.5 14') - .attr('refX', options?.look === 'neo' ? 11.5 : 7.75) // Adjust to position the arrowhead relative to the line + .attr('refX', 7.75) // Adjust to position the arrowhead relative to the line .attr('refY', 7) // Half of 14 for vertical center .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 10.5) @@ -175,7 +175,39 @@ const point = (elem, type, id, options) => { .attr('id', id + '_' + type + '-pointStart') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 11.5 14') - .attr('refX', options?.look === 'neo' ? 1 : 4) + .attr('refX', 4) + .attr('refY', 7) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 11.5) + .attr('markerHeight', 14) + .attr('orient', 'auto') + .append('polygon') + .attr('points', '0,7 11.5,14 11.5,0') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 0) + .style('stroke-dasharray', '1,0'); + elem + .append('marker') + .attr('id', id + '_' + type + '-pointEnd-margin') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 11.5 14') + .attr('refX', 11.5) // Adjust to position the arrowhead relative to the line + .attr('refY', 7) // Half of 14 for vertical center + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 10.5) + .attr('markerHeight', 14) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 0 0 L 11.5 7 L 0 14 z') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 0) + .style('stroke-dasharray', '1,0'); + elem + .append('marker') + .attr('id', id + '_' + type + '-pointStart-margin') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 11.5 14') + .attr('refX', 1) .attr('refY', 7) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 11.5) @@ -187,14 +219,14 @@ const point = (elem, type, id, options) => { .style('stroke-width', 0) .style('stroke-dasharray', '1,0'); }; -const circle = (elem, type, id, options) => { +const circle = (elem, type, id) => { elem .append('marker') .attr('id', id + '_' + type + '-circleEnd') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 10 10') .attr('refY', 5) // What!!!?? - .attr('refX', options?.look === 'neo' ? 12.25 : 10.75) + .attr('refX', 10.75) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 14) .attr('markerHeight', 14) @@ -212,7 +244,44 @@ const circle = (elem, type, id, options) => { .attr('id', id + '_' + type + '-circleStart') .attr('class', 'marker ' + type) .attr('viewBox', '0 0 10 10') - .attr('refX', options?.look === 'neo' ? -2 : 0) + .attr('refX', 0) + .attr('refY', 5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 14) + .attr('markerHeight', 14) + .attr('orient', 'auto') + .append('circle') + .attr('cx', '5') + .attr('cy', '5') + .attr('r', '5') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 0) + .style('stroke-dasharray', '1,0'); + elem + .append('marker') + .attr('id', id + '_' + type + '-circleEnd-margin') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 10 10') + .attr('refY', 5) // What!!!?? + .attr('refX', 12.25) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 14) + .attr('markerHeight', 14) + .attr('orient', 'auto') + .append('circle') + .attr('cx', '5') + .attr('cy', '5') + .attr('r', '5') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 0) + .style('stroke-dasharray', '1,0'); + + elem + .append('marker') + .attr('id', id + '_' + type + '-circleStart-margin') + .attr('class', 'marker ' + type) + .attr('viewBox', '0 0 10 10') + .attr('refX', -2) .attr('refY', 5) .attr('markerUnits', 'userSpaceOnUse') .attr('markerWidth', 14) @@ -259,6 +328,38 @@ const cross = (elem, type, id) => { .attr('class', 'arrowMarkerPath') .style('stroke-width', 2.5) .style('stroke-dasharray', '1,0'); + elem + .append('marker') + .attr('id', id + '_' + type + '-crossEnd-margin') + .attr('class', 'marker cross ' + type) + .attr('viewBox', '0 0 15 15') + .attr('refX', 17.7) + .attr('refY', 7.5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 1,1 L 14,14 M 1,14 L 14,1') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 2.5); + + elem + .append('marker') + .attr('id', id + '_' + type + '-crossStart-margin') + .attr('class', 'marker cross ' + type) + .attr('viewBox', '0 0 15 15') + .attr('refX', -3.5) + .attr('refY', 7.5) + .attr('markerUnits', 'userSpaceOnUse') + .attr('markerWidth', 12) + .attr('markerHeight', 12) + .attr('orient', 'auto') + .append('path') + .attr('d', 'M 1,1 L 14,14 M 1,14 L 14,1') + .attr('class', 'arrowMarkerPath') + .style('stroke-width', 2.5) + .style('stroke-dasharray', '1,0'); }; const barb = (elem, type, id) => { elem