From fa6bcd8b30456b8cc1bda249631911bf315ba5cb Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Fri, 24 May 2024 13:28:33 +0200 Subject: [PATCH 1/4] #5237 Adding new rendering for flowcharts --- cypress/platform/knsv2.html | 12 ++- packages/mermaid/src/dagre-wrapper/nodes.js | 2 - .../mermaid/src/diagrams/flowchart/flowDb.ts | 43 ++++++++++ .../src/diagrams/flowchart/flowDiagram-v2.ts | 8 +- .../flowchart/flowDiagram-v3-unified.ts | 25 ++++++ .../flowchart/flowRenderer-v3-unified.ts | 81 +++++++++++++++++++ .../mermaid/src/diagrams/state/stateDb.js | 1 + .../rendering-elements/nodes.js | 9 ++- .../shapes/{rect.ts => drawRect.ts} | 8 +- .../rendering-elements/shapes/roundedRect.ts | 13 +++ .../rendering-elements/shapes/squareRect.ts | 11 +++ .../rendering-elements/shapes/state.ts | 11 +++ .../mermaid/src/rendering-util/types.d.ts | 8 +- 13 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 packages/mermaid/src/diagrams/flowchart/flowDiagram-v3-unified.ts create mode 100644 packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts rename packages/mermaid/src/rendering-util/rendering-elements/shapes/{rect.ts => drawRect.ts} (94%) create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts diff --git a/cypress/platform/knsv2.html b/cypress/platform/knsv2.html index 6ce618046..1c46866c5 100644 --- a/cypress/platform/knsv2.html +++ b/cypress/platform/knsv2.html @@ -136,15 +136,19 @@ sequenceDiagram -
+    
       %%{init: {"layout": "elk", "mergeEdges": true} }%%
 stateDiagram
-  direction TB
-  T00 --> T0
-  T00 --> T1
+      A --> B
       
+      %%{init: {"layout": "elk", "mergeEdges": true} }%%
+flowchart
+      A --> B(This is B)
+      
+
       %%{init: {"layout": "elk", "mergeEdges": false, "elk.nodePlacement.strategy": "NETWORK_SIMPLEX"} }%%
 stateDiagram
   State T0 {
diff --git a/packages/mermaid/src/dagre-wrapper/nodes.js b/packages/mermaid/src/dagre-wrapper/nodes.js
index 66117369c..cb5b35be6 100644
--- a/packages/mermaid/src/dagre-wrapper/nodes.js
+++ b/packages/mermaid/src/dagre-wrapper/nodes.js
@@ -1135,8 +1135,6 @@ export const insertNode = async (elem, node, dir) => {
   let newEl;
   let el;
 
-  console.log('insertNode element', elem, elem.node());
-  // debugger;
   // Add link when appropriate
   if (node.link) {
     let target;
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts
index 797130e71..ef4ae0a76 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts
@@ -2,6 +2,7 @@ import { select } from 'd3';
 import utils from '../../utils.js';
 import { getConfig, defaultConfig } from '../../diagram-api/diagramAPI.js';
 import common from '../common/common.js';
+import type { LayoutData, LayoutMethod, Node, Edge } from '../../rendering-util/types.js';
 import { log } from '../../logger.js';
 import {
   setAccTitle,
@@ -755,11 +756,53 @@ export const lex = {
   firstGraph,
 };
 
+const getTypeFromVertex = (vertex: FlowVertex) => {
+  if (vertex.type === 'square') {
+    return 'squareRect';
+  }
+  if (vertex.type === 'round') {
+    return 'roundedRect';
+  }
+
+  return vertex.type || 'squareRect';
+};
+
+export const getData = () => {
+  const config = getConfig();
+  const nodes: Node[] = [];
+  const edges: Edge[] = [];
+
+  // extract(getRootDocV2());
+  // const diagramStates = getStates();
+  const n = getVertices();
+  n.forEach((vertex) => {
+    const node: Node = {
+      id: vertex.id,
+      label: vertex.text,
+      labelStyle: '',
+      padding: config.flowchart?.padding || 8,
+      cssStyles: vertex.styles.join(' '),
+      cssClasses: vertex.classes.join(' '),
+      shape: getTypeFromVertex(vertex),
+      dir: vertex.dir,
+      domId: vertex.domId,
+      type: undefined,
+      isGroup: false,
+    };
+    nodes.push(node);
+  });
+
+  const useRough = config.look === 'handdrawn';
+
+  return { nodes, edges, other: {}, config };
+};
+
 export default {
   defaultConfig: () => defaultConfig.flowchart,
   setAccTitle,
   getAccTitle,
   getAccDescription,
+  getData,
   setAccDescription,
   addVertex,
   lookUpDomId,
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts b/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts
index 368a98cca..e3db21540 100644
--- a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts
+++ b/packages/mermaid/src/diagrams/flowchart/flowDiagram-v2.ts
@@ -1,7 +1,8 @@
 // @ts-ignore: JISON doesn't support types
 import flowParser from './parser/flow.jison';
 import flowDb from './flowDb.js';
-import flowRendererV2 from './flowRenderer-v2.js';
+// import flowRendererV2 from './flowRenderer-v2.js';
+import flowRendererV3 from './flowRenderer-v3-unified.js';
 import flowStyles from './styles.js';
 import type { MermaidConfig } from '../../config.type.js';
 import { setConfig } from '../../diagram-api/diagramAPI.js';
@@ -9,7 +10,8 @@ import { setConfig } from '../../diagram-api/diagramAPI.js';
 export const diagram = {
   parser: flowParser,
   db: flowDb,
-  renderer: flowRendererV2,
+  // renderer: flowRendererV2,
+  renderer: flowRendererV3,
   styles: flowStyles,
   init: (cnf: MermaidConfig) => {
     if (!cnf.flowchart) {
@@ -18,7 +20,7 @@ export const diagram = {
     cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
     // flowchart-v2 uses dagre-wrapper, which doesn't have access to flowchart cnf
     setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
-    flowRendererV2.setConf(cnf.flowchart);
+    flowRendererV3.setConf(cnf.flowchart);
     flowDb.clear();
     flowDb.setGen('gen-2');
   },
diff --git a/packages/mermaid/src/diagrams/flowchart/flowDiagram-v3-unified.ts b/packages/mermaid/src/diagrams/flowchart/flowDiagram-v3-unified.ts
new file mode 100644
index 000000000..368a98cca
--- /dev/null
+++ b/packages/mermaid/src/diagrams/flowchart/flowDiagram-v3-unified.ts
@@ -0,0 +1,25 @@
+// @ts-ignore: JISON doesn't support types
+import flowParser from './parser/flow.jison';
+import flowDb from './flowDb.js';
+import flowRendererV2 from './flowRenderer-v2.js';
+import flowStyles from './styles.js';
+import type { MermaidConfig } from '../../config.type.js';
+import { setConfig } from '../../diagram-api/diagramAPI.js';
+
+export const diagram = {
+  parser: flowParser,
+  db: flowDb,
+  renderer: flowRendererV2,
+  styles: flowStyles,
+  init: (cnf: MermaidConfig) => {
+    if (!cnf.flowchart) {
+      cnf.flowchart = {};
+    }
+    cnf.flowchart.arrowMarkerAbsolute = cnf.arrowMarkerAbsolute;
+    // flowchart-v2 uses dagre-wrapper, which doesn't have access to flowchart cnf
+    setConfig({ flowchart: { arrowMarkerAbsolute: cnf.arrowMarkerAbsolute } });
+    flowRendererV2.setConf(cnf.flowchart);
+    flowDb.clear();
+    flowDb.setGen('gen-2');
+  },
+};
diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts
new file mode 100644
index 000000000..bf2d92176
--- /dev/null
+++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts
@@ -0,0 +1,81 @@
+import { log } from '../../logger.js';
+import type { DiagramStyleClassDef } from '../../diagram-api/types.js';
+import type { LayoutData, LayoutMethod } from '../../rendering-util/types.js';
+import { getConfig } from '../../diagram-api/diagramAPI.js';
+import { render } from '../../rendering-util/render.js';
+import { getDiagramElements } from '../../rendering-util/inserElementsForSize.js';
+import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js';
+import { getDirection } from './flowDb.js';
+
+import utils from '../../utils.js';
+
+// Configuration
+const conf: Record = {};
+
+export const setConf = function (cnf: Record) {
+  const keys = Object.keys(cnf);
+  for (const key of keys) {
+    conf[key] = cnf[key];
+  }
+};
+
+export const getClasses = function (
+  text: string,
+  diagramObj: any
+): Record {
+  // diagramObj.db.extract(diagramObj.db.getRootDocV2());
+  return diagramObj.db.getClasses();
+};
+
+export const draw = async function (text: string, id: string, _version: string, diag: any) {
+  log.info('REF0:');
+  log.info('Drawing state diagram (v2)', id);
+  const { securityLevel, state: conf, layout } = getConfig();
+
+  const DIR = getDirection();
+
+  // The getData method provided in all supported diagrams is used to extract the data from the parsed structure
+  // into the Layout data format
+  console.log('Before getData: ');
+  const data4Layout = diag.db.getData() as LayoutData;
+  console.log('Data: ', data4Layout);
+  // Create the root SVG - the element is the div containing the SVG element
+  const { element, svg } = getDiagramElements(id, securityLevel);
+
+  // // For some diagrams this call is not needed, but in the state diagram it is
+  // await insertElementsForSize(element, data4Layout);
+
+  // console.log('data4Layout:', data4Layout);
+
+  // // Now we have layout data with real sizes, we can perform the layout
+  // const data4Rendering = doLayout(data4Layout, id, _version, 'dagre-wrapper');
+
+  // // The performRender method provided in all supported diagrams is used to render the data
+  // performRender(data4Rendering);
+
+  data4Layout.type = diag.type;
+  // data4Layout.layoutAlgorithm = 'dagre-wrapper';
+  // data4Layout.layoutAlgorithm = 'elk';
+  data4Layout.layoutAlgorithm = layout;
+  data4Layout.direction = DIR;
+  data4Layout.nodeSpacing = conf?.nodeSpacing || 50;
+  data4Layout.rankSpacing = conf?.rankSpacing || 50;
+  data4Layout.markers = ['barb'];
+  data4Layout.diagramId = id;
+  console.log('REF1:', data4Layout);
+  await render(data4Layout, svg, element);
+  const padding = 8;
+  utils.insertTitle(
+    element,
+    'statediagramTitleText',
+    conf?.titleTopMargin || 0,
+    diag.db.getDiagramTitle()
+  );
+  setupViewPortForSVG(svg, padding, 'flowchart', conf?.useMaxWidth || false);
+};
+
+export default {
+  setConf,
+  getClasses,
+  draw,
+};
diff --git a/packages/mermaid/src/diagrams/state/stateDb.js b/packages/mermaid/src/diagrams/state/stateDb.js
index cda36f2ad..7193820c0 100644
--- a/packages/mermaid/src/diagrams/state/stateDb.js
+++ b/packages/mermaid/src/diagrams/state/stateDb.js
@@ -585,6 +585,7 @@ export const getData = () => {
   const useRough = config.look === 'handdrawn';
   dataFetcher(undefined, getRootDocV2(), diagramStates, nodes, edges, true, useRough);
 
+  console.log('State Nodes XDX:', nodes);
   return { nodes, edges, other: {}, config };
 };
 
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js
index 7216749c8..52219ac08 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js
+++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js
@@ -1,5 +1,7 @@
 import { log } from '$root/logger.js';
-import { rect } from './shapes/rect.ts';
+import { state } from './shapes/state.ts';
+import { roundedRect } from './shapes/roundedRect.ts';
+import { squareRect } from './shapes/squareRect.ts';
 import { stateStart } from './shapes/stateStart.ts';
 import { stateEnd } from './shapes/stateEnd.ts';
 import { forkJoin } from './shapes/forkJoin.ts';
@@ -15,13 +17,15 @@ const formatClass = (str) => {
 };
 
 const shapes = {
-  rect,
+  state,
   stateStart,
   stateEnd,
   fork: forkJoin,
   join: forkJoin,
   choice,
   note,
+  roundedRect,
+  squareRect,
 };
 
 let nodeElems = {};
@@ -30,7 +34,6 @@ export const insertNode = async (elem, node, dir) => {
   let newEl;
   let el;
 
-  // debugger;
   // Add link when appropriate
   if (node.link) {
     let target;
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts
similarity index 94%
rename from packages/mermaid/src/rendering-util/rendering-elements/shapes/rect.ts
rename to packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts
index 9ced7ee1f..675ccee09 100644
--- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rect.ts
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts
@@ -1,7 +1,7 @@
 import { log } from '$root/logger.js';
 import { labelHelper, updateNodeBounds } from './util.js';
 import intersect from '../intersect/index.js';
-import type { Node } from '$root/rendering-util/types.d.ts';
+import type { Node, RectOptions } from '$root/rendering-util/types.d.ts';
 import { createRoundedRectPathD } from './roundedRectPath.js';
 import { getConfig } from '$root/diagram-api/diagramAPI.js';
 import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
@@ -58,7 +58,7 @@ function applyNodePropertyBorders(
   rect.attr('stroke-dasharray', strokeDashArray.join(' '));
 }
 
-export const rect = async (parent: SVGAElement, node: Node) => {
+export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => {
   const { themeVariables, handdrawnSeed } = getConfig();
   const { nodeBorder, mainBkg } = themeVariables;
 
@@ -75,7 +75,7 @@ export const rect = async (parent: SVGAElement, node: Node) => {
   const y = -bbox.height / 2 - halfPadding;
 
   let rect;
-  const { rx, ry, style: cssStyles, useRough } = node;
+  const { rx, ry, cssStyles, useRough } = node;
   if (useRough) {
     const rc = rough.svg(shapeSvg);
     const options = userNodeOverrides(node, {
@@ -101,6 +101,8 @@ export const rect = async (parent: SVGAElement, node: Node) => {
       .attr('class', 'basic label-container')
       .attr('style', cssStyles)
       .attr('rx', rx)
+      .attr('data-id', 'abc')
+      .attr('data-et', 'node')
       .attr('ry', ry)
       .attr('x', x)
       .attr('y', y)
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts
new file mode 100644
index 000000000..aa254c000
--- /dev/null
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/roundedRect.ts
@@ -0,0 +1,13 @@
+import type { Node, RectOptions } from '$root/rendering-util/types.d.ts';
+import { drawRect } from './drawRect.js';
+
+export const roundedRect = async (parent: SVGAElement, node: Node) => {
+  const options = {
+    rx: 5,
+    ry: 5,
+    classes: '',
+  } as RectOptions;
+
+  console.log('roundedRect XDX');
+  return drawRect(parent, node, options);
+};
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts
new file mode 100644
index 000000000..8946b141c
--- /dev/null
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/squareRect.ts
@@ -0,0 +1,11 @@
+import type { Node, RectOptions } from '$root/rendering-util/types.d.ts';
+import { drawRect } from './drawRect.js';
+
+export const squareRect = async (parent: SVGAElement, node: Node) => {
+  const options = {
+    rx: 0,
+    ry: 0,
+    classes: '',
+  } as RectOptions;
+  return drawRect(parent, node, options);
+};
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts
new file mode 100644
index 000000000..73d119045
--- /dev/null
+++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/state.ts
@@ -0,0 +1,11 @@
+import type { Node } from '$root/rendering-util/types.d.ts';
+import { drawRect } from './drawRect.js';
+
+export const state = async (parent: SVGAElement, node: Node) => {
+  const options = {
+    rx: 5,
+    ry: 5,
+    classes: 'flowchart-node',
+  };
+  return drawRect(parent, node, options);
+};
diff --git a/packages/mermaid/src/rendering-util/types.d.ts b/packages/mermaid/src/rendering-util/types.d.ts
index 1276c41d8..997d93d62 100644
--- a/packages/mermaid/src/rendering-util/types.d.ts
+++ b/packages/mermaid/src/rendering-util/types.d.ts
@@ -1,5 +1,5 @@
 import config from '../../dist/defaultConfig';
-import { MermaidConfig } from '../../dist/config.type';
+import type { MermaidConfig } from '../../dist/config.type';
 export type MarkdownWordType = 'normal' | 'strong' | 'emphasis';
 export interface MarkdownWord {
   content: string;
@@ -90,6 +90,12 @@ interface Edge {
   useRough?: boolean;
 }
 
+interface RectOptions {
+  rx: number;
+  ry: number;
+  classes: string;
+}
+
 // Extending the Node interface for specific types if needed
 interface ClassDiagramNode extends Node {
   memberData: any; // Specific property for class diagram nodes

From 4f6586873fb0563c4a3cba81604b1cbb0888709e Mon Sep 17 00:00:00 2001
From: Ashish Jain 
Date: Mon, 27 May 2024 15:17:59 +0200
Subject: [PATCH 2/4] wip: Broke out flowchar shapes for common rendering

---
 cypress/platform/flowchart-refactor.html      | 775 ++++++++++++++++++
 cypress/platform/flowchart-sate.html          | 183 +++++
 .../mermaid/src/diagrams/flowchart/flowDb.ts  |   4 +-
 .../flowchart/flowRenderer-v3-unified.ts      |   2 +-
 .../rendering-elements/nodes.js               |  37 +-
 .../rendering-elements/shapes/circle.ts       |  58 ++
 .../rendering-elements/shapes/cylinder.ts     | 116 +++
 .../rendering-elements/shapes/doubleCircle.ts |  81 ++
 .../rendering-elements/shapes/drawRect.ts     |  11 +-
 .../rendering-elements/shapes/hexagon.ts      | 105 +++
 .../shapes/insertPolygonShape.ts              |  25 +
 .../shapes/invertedTrapezoid.ts               |  96 +++
 .../rendering-elements/shapes/leanLeft.ts     |  96 +++
 .../rendering-elements/shapes/leanRight.ts    |  96 +++
 .../rendering-elements/shapes/question.ts     |  90 ++
 .../shapes/rectLeftInvArrow.ts                |  94 +++
 .../rendering-elements/shapes/stadium.ts      | 111 +++
 .../rendering-elements/shapes/subroutine.ts   |  97 +++
 .../rendering-elements/shapes/trapezoid.ts    |  96 +++
 .../mermaid/src/rendering-util/types.d.ts     |   4 +
 20 files changed, 2173 insertions(+), 4 deletions(-)
 create mode 100644 cypress/platform/flowchart-refactor.html
 create mode 100644 cypress/platform/flowchart-sate.html
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts
 create mode 100644 packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts

diff --git a/cypress/platform/flowchart-refactor.html b/cypress/platform/flowchart-refactor.html
new file mode 100644
index 000000000..7eb2e545a
--- /dev/null
+++ b/cypress/platform/flowchart-refactor.html
@@ -0,0 +1,775 @@
+
+
+
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+
+  
+
+
+
+  
+    
+      
+      
+      
+      
+      
+    
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+        
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+    
+      
+      
+      
+      
+      
+    
+    
+    
+      
+    
+
+  
DagreDagre with roughELKELK with rough
+ +
+
+
+      flowchart LR
+    id1([This is the text in the box])
+
+  
+
+
+
+flowchart LR
+    id1([This is the text in the box])
+
+      
+
+
+%%{init: {"look": "handdrawn"} }%%
+flowchart LR
+    id1([This is the text in the box])
+
+
+      
+
+
+%%{init: {"handdrawn": false, "layout": "elk"} }%%
+flowchart LR
+    id1([This is the text in the box])
+
+
+      
+
+
+%%{init: {"look": "handdrawn", "layout": "elk"} }%%
+flowchart LR
+    id1([This is the text in the box])
+
+
+      
+
+ +
+
+
+      flowchart LR
+    id1[[This is the text in the box]]
+    
+
+
+
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+
+%%{init: {"look": "handdrawn"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+
+%%{init: {"handdrawn": false, "layout": "elk"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+
+%%{init: {"look": "handdrawn", "layout": "elk"} }%%
+flowchart LR
+    id1[[This is the text in the box]]
+      
+
+ +
+
+
+              flowchart LR
+    id1[(Database)]
+    
+
+
+
+          flowchart LR
+    id1[(Database)]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1[(Database)]
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1[(Database)]
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1[(Database)]
+      
+
+ +
+
+
+              flowchart LR
+    id1((This is the text in the circle))
+    
+
+
+
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1((This is the text in the circle))
+      
+
+ +
+
+
+              flowchart TD
+    id1(((This is the text in the circle)))
+    
+
+
+
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart TD
+    id1(((This is the text in the circle)))
+      
+
+ +
+
+
+              flowchart LR
+    id1>This is the text in the box]
+    
+
+
+
+          flowchart LR
+    id1>This is the text in the box]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1>This is the text in the box]  
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1>This is the text in the box]  
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1>This is the text in the box]
+      
+
+ +
+
+
+              flowchart LR
+    id1{This is the text in the box}
+    
+
+
+
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1{This is the text in the box}
+      
+
+ +
+
+
+              flowchart LR
+    id1{{This is the text in the box}}
+    
+
+
+
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1{{This is the text in the box}}
+      
+
+ +
+
+
+              flowchart TD
+    id1[/This is the text in the box/]
+    
+
+
+
+          flowchart TD
+    id1[/This is the text in the box/]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart TD
+    id1[/This is the text in the box/]  
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    id1[/This is the text in the box/] 
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart TD
+    id1[/This is the text in the box/]
+      
+
+ +
+
+
+              flowchart TD
+    id1[\This is the text in the box\]
+    
+
+
+
+          flowchart TD
+    id1[\This is the text in the box\]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart TD
+    id1[\This is the text in the box\]
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    id1[\This is the text in the box\]
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart TD
+    id1[\This is the text in the box\]
+
+      
+
+ +
+
+
+              flowchart TD
+    A[/Christmas\]
+    
+
+
+
+          flowchart TD
+    A[/Christmas\]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart TD
+    A[/Christmas\]
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    A[/Christmas\]
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart TD
+    A[/Christmas\]
+      
+
+ +
+
+
+              flowchart TD
+    A[\Christmas/]
+    
+
+
+
+          flowchart TD
+    A[\Christmas/]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart TD
+    A[\Christmas/]
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart TD
+    A[\Christmas/]
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart TD
+    A[\Christmas/]  
+      
+
+ +
+
+
+              flowchart LR
+    id1(This is the text in the box)
+    
+
+
+
+          flowchart LR
+    id1(This is the text in the box)
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1(This is the text in the box)
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1(This is the text in the box)
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1(This is the text in the box) 
+      
+
+ +
+
+
+              flowchart LR
+    id1[This is the text in the box]
+    
+
+
+
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+
+          %%{init: {"look": "handdrawn"} }%%
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+
+          %%{init: {"handdrawn": false, "layout": "elk"} }%%
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+
+          %%{init: {"look": "handdrawn", "layout": "elk"} }%%
+          flowchart LR
+    id1[This is the text in the box]
+      
+
+ + + + + + \ No newline at end of file diff --git a/cypress/platform/flowchart-sate.html b/cypress/platform/flowchart-sate.html new file mode 100644 index 000000000..83e46363a --- /dev/null +++ b/cypress/platform/flowchart-sate.html @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DagreDagre with roughELKELK with rough
+ +
+
+
+      flowchart LR
+    id1([This is the text in the box])
+
+  
+
+
+
+%%{init: {"look": "handdrawn"} }%%
+stateDiagram-v2 
+    stateA
+
+      
+
+
+%%{init: {"look": "handdrawn"} }%%
+flowchart LR
+    id1([This is the text in the box])
+
+
+      
+
+ + + + + + \ No newline at end of file diff --git a/packages/mermaid/src/diagrams/flowchart/flowDb.ts b/packages/mermaid/src/diagrams/flowchart/flowDb.ts index ef4ae0a76..605e8d5ea 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowDb.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowDb.ts @@ -774,6 +774,7 @@ export const getData = () => { // extract(getRootDocV2()); // const diagramStates = getStates(); + const useRough = config.look === 'handdrawn'; const n = getVertices(); n.forEach((vertex) => { const node: Node = { @@ -788,11 +789,12 @@ export const getData = () => { domId: vertex.domId, type: undefined, isGroup: false, + useRough, }; nodes.push(node); }); - const useRough = config.look === 'handdrawn'; + //const useRough = config.look === 'handdrawn'; return { nodes, edges, other: {}, config }; }; diff --git a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts index bf2d92176..01bf79fcb 100644 --- a/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts +++ b/packages/mermaid/src/diagrams/flowchart/flowRenderer-v3-unified.ts @@ -3,7 +3,7 @@ import type { DiagramStyleClassDef } from '../../diagram-api/types.js'; import type { LayoutData, LayoutMethod } from '../../rendering-util/types.js'; import { getConfig } from '../../diagram-api/diagramAPI.js'; import { render } from '../../rendering-util/render.js'; -import { getDiagramElements } from '../../rendering-util/inserElementsForSize.js'; +import { getDiagramElements } from '../../rendering-util/insertElementsForSize.js'; import { setupViewPortForSVG } from '../../rendering-util/setupViewPortForSVG.js'; import { getDirection } from './flowDb.js'; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js index 52219ac08..d03fd257a 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/nodes.js +++ b/packages/mermaid/src/rendering-util/rendering-elements/nodes.js @@ -7,8 +7,19 @@ import { stateEnd } from './shapes/stateEnd.ts'; import { forkJoin } from './shapes/forkJoin.ts'; import { choice } from './shapes/choice.ts'; import { note } from './shapes/note.ts'; +import { stadium } from './shapes/stadium.js'; import { getConfig } from '$root/diagram-api/diagramAPI.js'; - +import { subroutine } from './shapes/subroutine.js'; +import { cylinder } from './shapes/cylinder.js'; +import { circle } from './shapes/circle.js'; +import { doublecircle } from './shapes/doubleCircle.js'; +import { rect_left_inv_arrow } from './shapes/rectLeftInvArrow.js'; +import { question } from './shapes/question.js'; +import { hexagon } from './shapes/hexagon.js'; +import { lean_right } from './shapes/leanRight.js'; +import { lean_left } from './shapes/leanLeft.js'; +import { trapezoid } from './shapes/trapezoid.js'; +import { inv_trapezoid } from './shapes/invertedTrapezoid.js'; const formatClass = (str) => { if (str) { return ' ' + str; @@ -26,6 +37,18 @@ const shapes = { note, roundedRect, squareRect, + stadium, + subroutine, + cylinder, + circle, + doublecircle, + odd: rect_left_inv_arrow, + diamond: question, + hexagon, + lean_right, + lean_left, + trapezoid, + inv_trapezoid, }; let nodeElems = {}; @@ -34,6 +57,18 @@ export const insertNode = async (elem, node, dir) => { let newEl; let el; + if (node) { + console.log('BLA: rect node', JSON.stringify(node)); + } + //special check for rect shape (with or without rounded corners) + if (node.shape === 'rect') { + if (node.rx && node.ry) { + node.shape = 'roundedRect'; + } else { + node.shape = 'squareRect'; + } + } + // Add link when appropriate if (node.link) { let target; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts new file mode 100644 index 000000000..0574ab6cb --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts @@ -0,0 +1,58 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; + +export const circle = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const radius = bbox.width / 2 + halfPadding; + let circleElem; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Circle: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.9, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const roughNode = rc.circle(0, 0, radius * 2, options); + + circleElem = shapeSvg.insert(() => roughNode, ':first-child'); + circleElem.attr('class', 'basic label-container').attr('style', cssStyles); + } else { + circleElem = shapeSvg + .insert('circle', ':first-child') + .attr('class', 'basic label-container') + .attr('style', cssStyles) + .attr('r', radius) + .attr('cx', 0) + .attr('cy', 0); + } + + updateNodeBounds(node, circleElem); + + node.intersect = function (point) { + log.info('Circle intersect', node, radius, point); + return intersect.circle(node, radius, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts new file mode 100644 index 000000000..cd12792c6 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts @@ -0,0 +1,116 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; + +/** + * Creates an SVG path for a cylindrical shape. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the cylinder. + * @param {number} height - The height of the cylinder. + * @param {number} rx - The x-radius of the cylinder's ends. + * @param {number} ry - The y-radius of the cylinder's ends. + * @returns {string} The path data for the cylindrical shape. + */ +export const createCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; + +export const cylinder = async (parent: SVGAElement, node: Node) => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, // + ' ' + node.class, + true + ); + + const w = bbox.width + node.padding; + const rx = w / 2; + const ry = rx / (2.5 + w / 50); + const h = bbox.height + ry + node.padding; + + let cylinder: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Cylinder: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createCylinderPathD(0, 0, w, h, rx, ry); + const roughNode = rc.path(pathData, options); + + cylinder = shapeSvg.insert(() => roughNode, ':first-child'); + cylinder.attr('class', 'basic label-container'); + if (cssStyles) { + cylinder.attr('style', cssStyles); + } + } else { + const pathData = createCylinderPathD(0, 0, w, h, rx, ry); + cylinder = shapeSvg + .insert('path', ':first-child') + .attr('d', pathData) + .attr('class', 'basic label-container') + .attr('style', cssStyles); + } + + cylinder.attr('label-offset-y', ry); + cylinder.attr('transform', `translate(${-w / 2}, ${-(h / 2 + ry)})`); + + updateNodeBounds(node, cylinder); + + node.intersect = function (point) { + const pos = intersect.rect(node, point); + const x = pos.x - (node.x ?? 0); + + if ( + rx != 0 && + (Math.abs(x) < (node.width ?? 0) / 2 || + (Math.abs(x) == (node.width ?? 0) / 2 && + Math.abs(pos.y - (node.y ?? 0)) > (node.height ?? 0) / 2 - ry)) + ) { + let y = ry * ry * (1 - (x * x) / (rx * rx)); + if (y != 0) { + y = Math.sqrt(y); + } + y = ry - y; + if (point.y - (node.y ?? 0) > 0) { + y = -y; + } + + pos.y += y; + } + + return pos; + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts new file mode 100644 index 000000000..49e4051c3 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts @@ -0,0 +1,81 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +//import d3 from 'd3'; + +export const doublecircle = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + const gap = 5; + const outerRadius = bbox.width / 2 + halfPadding + gap; + const innerRadius = bbox.width / 2 + halfPadding; + + let circleGroup; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('DoubleCircle: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const outerOptions = userNodeOverrides(node, { + roughness: 0.9, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + + const innerOptions = { ...outerOptions, fill: mainBkg }; + const outerRoughNode = rc.circle(0, 0, outerRadius * 2, outerOptions); + const innerRoughNode = rc.circle(0, 0, innerRadius * 2, innerOptions); + + circleGroup = shapeSvg.insert('g', ':first-child'); + circleGroup.attr('class', node.cssClasses).attr('style', cssStyles); + // d3.select(outerRoughNode).attr('class', 'outer-circle').attr('style', cssStyles); + // d3.select(innerRoughNode).attr('class', 'inner-circle').attr('style', cssStyles); + + circleGroup.node()?.appendChild(outerRoughNode); + circleGroup.node()?.appendChild(innerRoughNode); + } else { + circleGroup = shapeSvg.insert('g', ':first-child'); + const outerCircle = circleGroup.insert('circle', ':first-child'); + const innerCircle = circleGroup.insert('circle', ':first-child'); + + circleGroup.attr('class', 'basic label-container').attr('style', cssStyles); + + outerCircle + .attr('class', 'outer-circle') + .attr('style', cssStyles) + .attr('r', outerRadius) + .attr('cx', 0) + .attr('cy', 0); + + innerCircle + .attr('class', 'inner-circle') + .attr('style', cssStyles) + .attr('r', innerRadius) + .attr('cx', 0) + .attr('cy', 0); + } + + updateNodeBounds(node, circleGroup); + + node.intersect = function (point) { + log.info('DoubleCircle intersect', node, outerRadius, point); + return intersect.circle(node, outerRadius, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts index ac1b690c1..70b3fa6a1 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/drawRect.ts @@ -68,7 +68,14 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt const y = -bbox.height / 2 - halfPadding; let rect; - const { rx, ry, cssStyles, useRough } = node; + let { rx, ry, cssStyles, useRough } = node; + + //use options rx, ry overrides if present + if (options && options.rx && options.ry) { + rx = options.rx; + ry = options.ry; + } + if (useRough) { // @ts-ignore TODO: Fix rough typings const rc = rough.svg(shapeSvg); @@ -81,6 +88,8 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt stroke: nodeBorder, seed: handdrawnSeed, }); + + console.log('rect options: ', options); const roughNode = rx || ry ? rc.path(createRoundedRectPathD(x, y, totalWidth, totalHeight, rx || 0), options) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts new file mode 100644 index 000000000..e9070d8d3 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts @@ -0,0 +1,105 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; + +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for a hexagon shape. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the hexagon. + * @param {number} height - The height of the hexagon. + * @param {number} m - The margin size for the hexagon. + * @returns {string} The path data for the hexagon shape. + */ +export const createHexagonPathD = ( + x: number, + y: number, + width: number, + height: number, + m: number +): string => { + return [ + `M${x + m},${y}`, + `L${x + width - m},${y}`, + `L${x + width},${y - height / 2}`, + `L${x + width - m},${y - height}`, + `L${x + m},${y - height}`, + `L${x},${y - height / 2}`, + 'Z', + ].join(' '); +}; + +export const hexagon = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const f = 4; + const h = bbox.height + node.padding; + const m = h / f; + const w = bbox.width + 2 * m + node.padding; + const points = [ + { x: m, y: 0 }, + { x: w - m, y: 0 }, + { x: w, y: -h / 2 }, + { x: w - m, y: -h }, + { x: m, y: -h }, + { x: 0, y: -h / 2 }, + ]; + + let polygon: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Hexagon: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createHexagonPathD(0, 0, w, h, m); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts new file mode 100644 index 000000000..6d121b3e1 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/insertPolygonShape.ts @@ -0,0 +1,25 @@ +/** + * @param parent + * @param w + * @param h + * @param points + */ +export function insertPolygonShape( + parent: any, + w: number, + h: number, + points: { x: number; y: number }[] +) { + return parent + .insert('polygon', ':first-child') + .attr( + 'points', + points + .map(function (d) { + return d.x + ',' + d.y; + }) + .join(' ') + ) + .attr('class', 'label-container') + .attr('transform', 'translate(' + -w / 2 + ',' + h / 2 + ')'); +} diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts new file mode 100644 index 000000000..809d6dd07 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/invertedTrapezoid.ts @@ -0,0 +1,96 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for an inverted trapezoid shape. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the shape. + * @param {number} height - The height of the shape. + * @returns {string} The path data for the inverted trapezoid shape. + */ +export const createInvertedTrapezoidPathD = ( + x: number, + y: number, + width: number, + height: number +): string => { + return [ + `M${x + height / 6},${y}`, + `L${x + width - height / 6},${y}`, + `L${x + width + (2 * height) / 6},${y - height}`, + `L${x - (2 * height) / 6},${y - height}`, + 'Z', + ].join(' '); +}; + +export const inv_trapezoid = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: h / 6, y: 0 }, + { x: w - h / 6, y: 0 }, + { x: w + (2 * h) / 6, y: -h }, + { x: (-2 * h) / 6, y: -h }, + ]; + + let polygon: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Inverted Trapezoid: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createInvertedTrapezoidPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts new file mode 100644 index 000000000..0cfaf7c01 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanLeft.ts @@ -0,0 +1,96 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for a lean left shape. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the shape. + * @param {number} height - The height of the shape. + * @returns {string} The path data for the lean left shape. + */ +export const createLeanLeftPathD = ( + x: number, + y: number, + width: number, + height: number +): string => { + return [ + `M${x + (2 * height) / 6},${y}`, + `L${x + width + height / 6},${y}`, + `L${x + width - (2 * height) / 6},${y - height}`, + `L${x - height / 6},${y - height}`, + 'Z', + ].join(' '); +}; + +export const lean_left = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (2 * h) / 6, y: 0 }, + { x: w + h / 6, y: 0 }, + { x: w - (2 * h) / 6, y: -h }, + { x: -h / 6, y: -h }, + ]; + + let polygon: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Lean Left: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createLeanLeftPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts new file mode 100644 index 000000000..711be91f7 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/leanRight.ts @@ -0,0 +1,96 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for a lean right shape. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the shape. + * @param {number} height - The height of the shape. + * @returns {string} The path data for the lean right shape. + */ +export const createLeanRightPathD = ( + x: number, + y: number, + width: number, + height: number +): string => { + return [ + `M${x - (2 * height) / 6},${y}`, + `L${x + width - height / 6},${y}`, + `L${x + width + (2 * height) / 6},${y - height}`, + `L${x + height / 6},${y - height}`, + 'Z', + ].join(' '); +}; + +export const lean_right = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (-2 * h) / 6, y: 0 }, + { x: w - h / 6, y: 0 }, + { x: w + (2 * h) / 6, y: -h }, + { x: h / 6, y: -h }, + ]; + + let polygon: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Lean Right: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createLeanRightPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts new file mode 100644 index 000000000..fd68766e5 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/question.ts @@ -0,0 +1,90 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for a decision box shape (question shape). + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} size - The size of the shape. + * @returns {string} The path data for the decision box shape. + */ +export const createDecisionBoxPathD = (x: number, y: number, size: number): string => { + return [ + `M${x + size / 2},${y}`, + `L${x + size},${y - size / 2}`, + `L${x + size / 2},${y - size}`, + `L${x},${y - size / 2}`, + 'Z', + ].join(' '); +}; + +export const question = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const s = w + h; + + const points = [ + { x: s / 2, y: 0 }, + { x: s, y: -s / 2 }, + { x: s / 2, y: -s }, + { x: 0, y: -s / 2 }, + ]; + + let polygon: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('DecisionBox: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createDecisionBoxPathD(0, 0, s); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-s / 2}, ${s / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, s, s, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + log.warn('Intersect called'); + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts new file mode 100644 index 000000000..afcc1fb7d --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/rectLeftInvArrow.ts @@ -0,0 +1,94 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for a special polygon shape with a left-inverted arrow. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the shape. + * @param {number} height - The height of the shape. + * @returns {string} The path data for the special polygon shape. + */ +export const createPolygonPathD = (x: number, y: number, width: number, height: number): string => { + return [ + `M${x - height / 2},${y}`, + `L${x + width},${y}`, + `L${x + width},${y - height}`, + `L${x - height / 2},${y - height}`, + `L${x},${y - height / 2}`, + 'Z', + ].join(' '); +}; + +export const rect_left_inv_arrow = async ( + parent: SVGAElement, + node: Node +): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: -h / 2, y: 0 }, + { x: w, y: 0 }, + { x: w, y: -h }, + { x: -h / 2, y: -h }, + { x: 0, y: -h / 2 }, + ]; + + let polygon; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Polygon: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createPolygonPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + node.width = w + h; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts new file mode 100644 index 000000000..423f2392a --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts @@ -0,0 +1,111 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { createRoundedRectPathD } from './roundedRectPath.js'; + +export const createStadiumPathD = ( + x: number, + y: number, + totalWidth: number, + totalHeight: number +) => { + const radius = totalHeight / 2; + return [ + 'M', + x + radius, + y, // Move to the start of the top-left arc + 'H', + x + totalWidth - radius, // Draw horizontal line to the start of the top-right arc + 'A', + radius, + radius, + 0, + 0, + 1, + x + totalWidth, + y + radius, // Draw top-right arc + 'H', + x, // Draw horizontal line to the start of the bottom-right arc + 'A', + radius, + radius, + 0, + 0, + 1, + x + totalWidth - radius, + y + totalHeight, // Draw bottom-right arc + 'H', + x + radius, // Draw horizontal line to the start of the bottom-left arc + 'A', + radius, + radius, + 0, + 0, + 1, + x, + y + radius, // Draw bottom-left arc + 'Z', // Close the path + ].join(' '); +}; + +export const stadium = async (parent: SVGAElement, node: Node) => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, // + ' ' + node.class, + true + ); + + const h = bbox.height + node.padding; + const w = bbox.width + h / 4 + node.padding; + + let rect; + const { cssStyles, useRough } = node; + if (useRough) { + console.log('Stadium:Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', // solid fill + fillWeight: 3.5, + stroke: nodeBorder, + seed: handdrawnSeed, + // strokeWidth: 1, + }); + + console.log('Stadium options: ', options); + const pathData = createRoundedRectPathD(-w / 2, -h / 2, w, h, h / 3); + const roughNode = rc.path(pathData, options); + + rect = shapeSvg.insert(() => roughNode, ':first-child'); + rect.attr('class', 'basic label-container').attr('style', cssStyles); + } else { + rect = shapeSvg.insert('rect', ':first-child'); + + rect + .attr('class', 'basic label-container') + .attr('style', cssStyles) + .attr('rx', h / 2) + .attr('ry', h / 2) + .attr('x', -w / 2) + .attr('y', -h / 2) + .attr('width', w) + .attr('height', h); + } + + updateNodeBounds(node, rect); + + node.intersect = function (point) { + return intersect.rect(node, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts new file mode 100644 index 000000000..eacd53e27 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts @@ -0,0 +1,97 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +export const createSubroutinePathD = ( + x: number, + y: number, + width: number, + height: number +): string => { + const offset = 8; + return [ + `M${x - offset},${y}`, + `H${x + width + offset}`, + `V${y + height}`, + `H${x - offset}`, + `V${y}`, + 'M', + x, + y, + 'H', + x + width, + 'V', + y + height, + 'H', + x, + 'Z', + ].join(' '); +}; + +export const subroutine = async (parent: SVGAElement, node: Node) => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, // + ' ' + node.class, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + + let rect; + const { cssStyles, useRough } = node; + if (useRough) { + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }); + const pathData = createSubroutinePathD(-w / 2, -h / 2, w, h); + const roughNode = rc.path(pathData, options); + + rect = shapeSvg.insert(() => roughNode, ':first-child'); + rect.attr('class', 'basic label-container').attr('style', cssStyles); + } + + const points = [ + { x: 0, y: 0 }, + { x: w, y: 0 }, + { x: w, y: -h }, + { x: 0, y: -h }, + { x: 0, y: 0 }, + { x: -8, y: 0 }, + { x: w + 8, y: 0 }, + { x: w + 8, y: -h }, + { x: -8, y: -h }, + { x: -8, y: 0 }, + ]; + + const el = insertPolygonShape(shapeSvg, w, h, points); + if (cssStyles) { + el.attr('style', cssStyles); + } + + updateNodeBounds(node, el); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; + +export default subroutine; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts new file mode 100644 index 000000000..6a26f83e1 --- /dev/null +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts @@ -0,0 +1,96 @@ +import { log } from '$root/logger.js'; +import { labelHelper, updateNodeBounds } from './util.js'; +import intersect from '../intersect/index.js'; +import { getConfig } from '$root/diagram-api/diagramAPI.js'; +import type { Node } from '$root/rendering-util/types.d.ts'; +import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; +import rough from 'roughjs'; +import { insertPolygonShape } from './insertPolygonShape.js'; + +/** + * Creates an SVG path for a trapezoid shape. + * @param {number} x - The x coordinate of the top-left corner. + * @param {number} y - The y coordinate of the top-left corner. + * @param {number} width - The width of the shape. + * @param {number} height - The height of the shape. + * @returns {string} The path data for the trapezoid shape. + */ +export const createTrapezoidPathD = ( + x: number, + y: number, + width: number, + height: number +): string => { + return [ + `M${x - (2 * height) / 6},${y}`, + `L${x + width + (2 * height) / 6},${y}`, + `L${x + width - height / 6},${y - height}`, + `L${x + height / 6},${y - height}`, + 'Z', + ].join(' '); +}; + +export const trapezoid = async (parent: SVGAElement, node: Node): Promise => { + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + + const { shapeSvg, bbox, halfPadding } = await labelHelper( + parent, + node, + 'node ' + node.cssClasses, + true + ); + + const w = bbox.width + node.padding; + const h = bbox.height + node.padding; + const points = [ + { x: (-2 * h) / 6, y: 0 }, + { x: w + (2 * h) / 6, y: 0 }, + { x: w - h / 6, y: -h }, + { x: h / 6, y: -h }, + ]; + + let polygon: d3.Selection; + const { cssStyles, useRough } = node; + + if (useRough) { + console.log('Trapezoid: Inside use useRough'); + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', + fillWeight: 1.5, + //stroke: nodeBorder, + seed: handdrawnSeed, + //strokeWidth: 1, + }); + const pathData = createTrapezoidPathD(0, 0, w, h); + const roughNode = rc.path(pathData, options); + + polygon = shapeSvg + .insert(() => roughNode, ':first-child') + .attr('transform', `translate(${-w / 2}, ${h / 2})`); + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + } else { + polygon = insertPolygonShape(shapeSvg, w, h, points); + } + + if (cssStyles) { + polygon.attr('style', cssStyles); + } + + node.width = w; + node.height = h; + + updateNodeBounds(node, polygon); + + node.intersect = function (point) { + return intersect.polygon(node, points, point); + }; + + return shapeSvg; +}; diff --git a/packages/mermaid/src/rendering-util/types.d.ts b/packages/mermaid/src/rendering-util/types.d.ts index 997d93d62..4b8750725 100644 --- a/packages/mermaid/src/rendering-util/types.d.ts +++ b/packages/mermaid/src/rendering-util/types.d.ts @@ -57,6 +57,10 @@ interface Node { borderStyle?: string; borderWidth?: number; labelTextColor?: string; + + // Flowchart specific properties + x?: number; + y?: number; } // Common properties for any edge in the system From b8dd4b9048b967d563e463f94dfc26f9dc76ee26 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Mon, 27 May 2024 17:42:07 +0200 Subject: [PATCH 3/4] #5237 Finishing the roughup of the nodes --- cypress/platform/flowchart-sate.html | 14 ++--- .../rendering-elements/shapes/circle.ts | 20 ++---- .../rendering-elements/shapes/cylinder.ts | 61 ++++++++++-------- .../rendering-elements/shapes/doubleCircle.ts | 20 ++---- .../rendering-elements/shapes/drawRect.ts | 62 +------------------ .../shapes/handdrawnStyles.ts | 23 ++++++- .../rendering-elements/shapes/hexagon.ts | 20 ++---- .../shapes/invertedTrapezoid.ts | 24 ++----- .../rendering-elements/shapes/leanLeft.ts | 25 ++------ .../rendering-elements/shapes/leanRight.ts | 25 ++------ .../rendering-elements/shapes/question.ts | 25 ++------ .../shapes/rectLeftInvArrow.ts | 25 ++------ .../rendering-elements/shapes/stadium.ts | 28 ++------- .../rendering-elements/shapes/subroutine.ts | 62 ++++++++----------- .../rendering-elements/shapes/trapezoid.ts | 24 ++----- .../rendering-elements/shapes/util.js | 3 + 16 files changed, 136 insertions(+), 325 deletions(-) diff --git a/cypress/platform/flowchart-sate.html b/cypress/platform/flowchart-sate.html index 83e46363a..61c3c167d 100644 --- a/cypress/platform/flowchart-sate.html +++ b/cypress/platform/flowchart-sate.html @@ -91,10 +91,8 @@ - - - - + + - + - +
DagreDagre with roughELKELK with roughState roughFlowchart rough
@@ -111,7 +109,7 @@
 %%{init: {"look": "handdrawn"} }%%
-stateDiagram-v2 
+stateDiagram-v2
     stateA
 
       
@@ -120,14 +118,14 @@ stateDiagram-v2
 %%{init: {"look": "handdrawn"} }%%
 flowchart LR
-    id1([This is the text in the box])
+    id1[[This is the text in the box]]
 
 
       
diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts index 0574ab6cb..48054cce3 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/circle.ts @@ -1,19 +1,15 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; export const circle = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - const { shapeSvg, bbox, halfPadding } = await labelHelper( parent, node, - 'node ' + node.cssClasses, + getNodeClasses(node), true ); @@ -22,17 +18,9 @@ export const circle = async (parent: SVGAElement, node: Node): Promise roughNode, ':first-child'); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts index cd12792c6..f8e849382 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/cylinder.ts @@ -1,7 +1,6 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; @@ -33,17 +32,35 @@ export const createCylinderPathD = ( `l0,${-height}`, ].join(' '); }; - +export const createOuterCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [ + `M${x},${y + ry}`, + `M${x + width},${y + ry}`, + `a${rx},${ry} 0,0,0 ${-width},0`, + `l0,${height}`, + `a${rx},${ry} 0,0,0 ${width},0`, + `l0,${-height}`, + ].join(' '); +}; +export const createInnerCylinderPathD = ( + x: number, + y: number, + width: number, + height: number, + rx: number, + ry: number +): string => { + return [`M${x - width / 2},${-height / 2}`, `a${rx},${ry} 0,0,0 ${width},0`].join(' '); +}; export const cylinder = async (parent: SVGAElement, node: Node) => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, // + ' ' + node.class, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const rx = w / 2; @@ -54,21 +71,15 @@ export const cylinder = async (parent: SVGAElement, node: Node) => { const { cssStyles, useRough } = node; if (useRough) { - console.log('Cylinder: Inside use useRough'); + // @ts-ignore const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { - roughness: 0.7, - fill: mainBkg, - fillStyle: 'hachure', - fillWeight: 1.5, - stroke: nodeBorder, - seed: handdrawnSeed, - strokeWidth: 1, - }); - const pathData = createCylinderPathD(0, 0, w, h, rx, ry); - const roughNode = rc.path(pathData, options); + const outerPathData = createOuterCylinderPathD(0, 0, w, h, rx, ry); + const innerPathData = createInnerCylinderPathD(0, ry, w, h, rx, ry); + const outerNode = rc.path(outerPathData, userNodeOverrides(node, {})); + const innerLine = rc.path(innerPathData, userNodeOverrides(node, { fill: 'none' })); - cylinder = shapeSvg.insert(() => roughNode, ':first-child'); + cylinder = shapeSvg.insert(() => innerLine, ':first-child'); + cylinder = shapeSvg.insert(() => outerNode, ':first-child'); cylinder.attr('class', 'basic label-container'); if (cssStyles) { cylinder.attr('style', cssStyles); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts index 49e4051c3..b32420cc0 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts @@ -1,5 +1,5 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; @@ -8,8 +8,8 @@ import rough from 'roughjs'; //import d3 from 'd3'; export const doublecircle = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; + const { themeVariables } = getConfig(); + const { mainBkg } = themeVariables; const { shapeSvg, bbox, halfPadding } = await labelHelper( parent, @@ -25,17 +25,9 @@ export const doublecircle = async (parent: SVGAElement, node: Node): Promise, -// borders: string | undefined, -// totalWidth: number, -// totalHeight: number -// ) { -// if (!borders) { -// return; -// } -// const strokeDashArray: number[] = []; -// const addBorder = (length: number) => { -// strokeDashArray.push(length, 0); -// }; -// const skipBorder = (length: number) => { -// strokeDashArray.push(0, length); -// }; -// if (borders.includes('t')) { -// log.debug('add top border'); -// addBorder(totalWidth); -// } else { -// skipBorder(totalWidth); -// } -// if (borders.includes('r')) { -// log.debug('add right border'); -// addBorder(totalHeight); -// } else { -// skipBorder(totalHeight); -// } -// if (borders.includes('b')) { -// log.debug('add bottom border'); -// addBorder(totalWidth); -// } else { -// skipBorder(totalWidth); -// } -// if (borders.includes('l')) { -// log.debug('add left border'); -// addBorder(totalHeight); -// } else { -// skipBorder(totalHeight); -// } - -// rect.attr('stroke-dasharray', strokeDashArray.join(' ')); -// } - export const drawRect = async (parent: SVGAElement, node: Node, options: RectOptions) => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - const { shapeSvg, bbox, halfPadding } = await labelHelper( parent, node, - 'node ' + node.cssClasses, // + ' ' + node.class, + getNodeClasses(node), true ); @@ -79,15 +31,7 @@ export const drawRect = async (parent: SVGAElement, node: Node, options: RectOpt if (useRough) { // @ts-ignore TODO: Fix rough typings const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { - roughness: 0.7, - fill: mainBkg, - // fillStyle: 'solid', // solid fill' - fillStyle: 'hachure', // solid fill' - fillWeight: 3.5, - stroke: nodeBorder, - seed: handdrawnSeed, - }); + const options = userNodeOverrides(node, {}); console.log('rect options: ', options); const roughNode = diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts index 462ce9f3d..21e8f2835 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts @@ -18,8 +18,25 @@ export const solidStateFill = (color: string) => { // Striped fill like start or fork nodes in state diagrams // TODO remove any export const userNodeOverrides = (node: Node, options: any) => { - const result = Object.assign({}, options); - result.fill = node.backgroundColor || options.fill; - result.stroke = node.borderColor || options.stroke; + const { themeVariables, handdrawnSeed } = getConfig(); + const { nodeBorder, mainBkg } = themeVariables; + const result = Object.assign( + { + roughness: 0.7, + fill: mainBkg, + fillStyle: 'hachure', // solid fill + fillWeight: 3.5, + stroke: nodeBorder, + seed: handdrawnSeed, + strokeWidth: 1, + }, + options + ); + if (node.backgroundColor) { + result.fill = node.backgroundColor; + } + if (node.borderColor) { + result.stroke = node.borderColor; + } return result; }; diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts index e9070d8d3..0678e6013 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/hexagon.ts @@ -1,7 +1,6 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; @@ -36,13 +35,10 @@ export const createHexagonPathD = ( }; export const hexagon = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - const { shapeSvg, bbox, halfPadding } = await labelHelper( parent, node, - 'node ' + node.cssClasses, + getNodeClasses(node), true ); @@ -63,17 +59,9 @@ export const hexagon = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -54,17 +46,9 @@ export const inv_trapezoid = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -54,17 +45,9 @@ export const lean_left = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -54,17 +45,9 @@ export const lean_right = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -50,17 +41,9 @@ export const question = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -54,17 +45,9 @@ export const rect_left_inv_arrow = async ( const { cssStyles, useRough } = node; if (useRough) { - console.log('Polygon: Inside use useRough'); + // @ts-ignore const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { - roughness: 0.7, - fill: mainBkg, - fillStyle: 'hachure', - fillWeight: 1.5, - stroke: nodeBorder, - seed: handdrawnSeed, - strokeWidth: 1, - }); + const options = userNodeOverrides(node, {}); const pathData = createPolygonPathD(0, 0, w, h); const roughNode = rc.path(pathData, options); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts index 423f2392a..102e49069 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/stadium.ts @@ -1,7 +1,6 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; @@ -53,15 +52,7 @@ export const createStadiumPathD = ( }; export const stadium = async (parent: SVGAElement, node: Node) => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, // + ' ' + node.class, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const h = bbox.height + node.padding; const w = bbox.width + h / 4 + node.padding; @@ -69,20 +60,11 @@ export const stadium = async (parent: SVGAElement, node: Node) => { let rect; const { cssStyles, useRough } = node; if (useRough) { - console.log('Stadium:Inside use useRough'); + // @ts-ignore const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { - roughness: 0.7, - fill: mainBkg, - fillStyle: 'hachure', // solid fill - fillWeight: 3.5, - stroke: nodeBorder, - seed: handdrawnSeed, - // strokeWidth: 1, - }); + const options = userNodeOverrides(node, {}); - console.log('Stadium options: ', options); - const pathData = createRoundedRectPathD(-w / 2, -h / 2, w, h, h / 3); + const pathData = createRoundedRectPathD(-w / 2, -h / 2, w, h, h / 2); const roughNode = rc.path(pathData, options); rect = shapeSvg.insert(() => roughNode, ':first-child'); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts index eacd53e27..4046cfcc9 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/subroutine.ts @@ -1,7 +1,6 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; @@ -34,39 +33,14 @@ export const createSubroutinePathD = ( }; export const subroutine = async (parent: SVGAElement, node: Node) => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, // + ' ' + node.class, - true - ); - + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); + const halfPadding = (node?.padding || 0) / 2; const w = bbox.width + node.padding; const h = bbox.height + node.padding; - + const x = -bbox.width / 2 - halfPadding; + const y = -bbox.height / 2 - halfPadding; let rect; const { cssStyles, useRough } = node; - if (useRough) { - const rc = rough.svg(shapeSvg); - const options = userNodeOverrides(node, { - roughness: 0.7, - fill: mainBkg, - fillStyle: 'hachure', - fillWeight: 1.5, - stroke: nodeBorder, - seed: handdrawnSeed, - strokeWidth: 1, - }); - const pathData = createSubroutinePathD(-w / 2, -h / 2, w, h); - const roughNode = rc.path(pathData, options); - - rect = shapeSvg.insert(() => roughNode, ':first-child'); - rect.attr('class', 'basic label-container').attr('style', cssStyles); - } - const points = [ { x: 0, y: 0 }, { x: w, y: 0 }, @@ -80,12 +54,28 @@ export const subroutine = async (parent: SVGAElement, node: Node) => { { x: -8, y: 0 }, ]; - const el = insertPolygonShape(shapeSvg, w, h, points); - if (cssStyles) { - el.attr('style', cssStyles); - } + if (useRough) { + // @ts-ignore + const rc = rough.svg(shapeSvg); + const options = userNodeOverrides(node, {}); + const pathData = createSubroutinePathD(-w / 2, -h / 2, w, h); - updateNodeBounds(node, el); + const roughNode = rc.rectangle(x - 8, y, w + 16, h, options); + const l1 = rc.line(x, y, x, y + h, options); + const l2 = rc.line(x + w, y, x + w, y + h, options); + + shapeSvg.insert(() => l1, ':first-child'); + shapeSvg.insert(() => l2, ':first-child'); + rect = shapeSvg.insert(() => roughNode, ':first-child'); + + rect.attr('class', 'basic label-container').attr('style', cssStyles); + } else { + const el = insertPolygonShape(shapeSvg, w, h, points); + if (cssStyles) { + el.attr('style', cssStyles); + } + updateNodeBounds(node, el); + } node.intersect = function (point) { return intersect.polygon(node, points, point); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts index 6a26f83e1..1a6a2d1c2 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/trapezoid.ts @@ -1,7 +1,6 @@ import { log } from '$root/logger.js'; -import { labelHelper, updateNodeBounds } from './util.js'; +import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; @@ -31,15 +30,7 @@ export const createTrapezoidPathD = ( }; export const trapezoid = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables, handdrawnSeed } = getConfig(); - const { nodeBorder, mainBkg } = themeVariables; - - const { shapeSvg, bbox, halfPadding } = await labelHelper( - parent, - node, - 'node ' + node.cssClasses, - true - ); + const { shapeSvg, bbox } = await labelHelper(parent, node, getNodeClasses(node), true); const w = bbox.width + node.padding; const h = bbox.height + node.padding; @@ -55,16 +46,9 @@ export const trapezoid = async (parent: SVGAElement, node: Node): Promise + (node.useRough ? 'rough-node' : 'node') + ' ' + node.cssClasses + ' ' + (extra || ''); From 8fb9b21f59814dfa90879e1ed3a17554b6e89e30 Mon Sep 17 00:00:00 2001 From: Knut Sveidqvist Date: Tue, 28 May 2024 08:50:16 +0200 Subject: [PATCH 4/4] #5237 Fix four double circle background --- .../rendering-elements/shapes/doubleCircle.ts | 11 ++++------- .../rendering-elements/shapes/handdrawnStyles.ts | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts index b32420cc0..d3bda9c86 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/doubleCircle.ts @@ -1,20 +1,16 @@ import { log } from '$root/logger.js'; import { labelHelper, updateNodeBounds, getNodeClasses } from './util.js'; import intersect from '../intersect/index.js'; -import { getConfig } from '$root/diagram-api/diagramAPI.js'; import type { Node } from '$root/rendering-util/types.d.ts'; import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js'; import rough from 'roughjs'; //import d3 from 'd3'; export const doublecircle = async (parent: SVGAElement, node: Node): Promise => { - const { themeVariables } = getConfig(); - const { mainBkg } = themeVariables; - const { shapeSvg, bbox, halfPadding } = await labelHelper( parent, node, - 'node ' + node.cssClasses, + getNodeClasses(node), true ); const gap = 5; @@ -27,13 +23,14 @@ export const doublecircle = async (parent: SVGAElement, node: Node): Promise outerRoughNode, ':first-child'); circleGroup.attr('class', node.cssClasses).attr('style', cssStyles); circleGroup.node()?.appendChild(outerRoughNode); diff --git a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts index 21e8f2835..45bc2e5ba 100644 --- a/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts +++ b/packages/mermaid/src/rendering-util/rendering-elements/shapes/handdrawnStyles.ts @@ -28,7 +28,7 @@ export const userNodeOverrides = (node: Node, options: any) => { fillWeight: 3.5, stroke: nodeBorder, seed: handdrawnSeed, - strokeWidth: 1, + strokeWidth: 1.3, }, options );