mermaid-js / mermaid

Generation of diagrams like flowcharts or sequence diagrams from text in a similar manner as markdown
https://mermaid.js.org
MIT License
71.8k stars 6.51k forks source link

language/API for manual layout and design #2483

Open maiermic opened 2 years ago

maiermic commented 2 years ago

Prolog

There are a lot of issues about changing the layout and/or design, e.g.

I like the concise syntax of Mermaid's languages that are focused on a certain kind of diagram. However, the diagrams can only be extended/modified to a limited degree. The auto layout is handy in some case, but I often find the layout not appealing (e.g. the alignment of nodes).

I'd like to suggest a new way to create diagrams that does not use dagre. My suggestion is not primarily about syntax, but about the concept behind it. Hence, I use JavaScript code to describe my examples.

Concept (Main Idea)

My idea is inspired by the way how TikZ works in Latex (example). Even though I wouldn't use the same syntax :wink:

The position of nodes are defined in relation to each other. A node has a bounding box with a coordinate (top left corner) and a dimension (width and height). Further, we define the following coordinates based on the bounding box definition:

coordinates.svg

An edge is basically just defined by it's start and end position.

Base Example

Let's write something similar to this flowchart

flowchart LR
    A-->B
flowchart-example.svg
function rightOf(node, gap=50) {
  return {
    x: node.east.x + gap,
    y: node.y,
  }
}

const n1 = rect()
const n2 = Object.assign(rect(), rightOf(n1))

const svgExample = renderDiagram({
  nodes: [n1, n2],
  edges: [edge(n1.east, n2.west)],
})

Runnable example

Different Directions

That syntax is kind of verbose and the example is boring :unamused: Let's add some functions to spice it up :hot_pepper: :grin: Our diagram will look like this

flowchart-example-2.svg
const n1 = rect({fill: 'red', stroke: 'darkred'})
const n2 = rect({fill: 'yellow', stroke: 'orange'})
const n3 = rect({fill: 'lightblue', stroke: 'blue'})
const n4 = rect({fill: 'green', stroke: 'darkgreen'})

const diagram = createDiagram([
  [n1, right`-->`, n2],
  [n2, right`-->`, n3],
  [n2, below`-->`, n4],
])
const svgExample = renderDiagram(diagram)

Runnable example

Related Example

I'm still playing around with the technology. It works quite well so far. The objects used in the calculations require a bounding box. A mapping of those objects to (virtual) SVG elements is required to render them. While I was playing around, I re-implemented this diagram that I found in a related way

My implementation renders like this

git-workflow-example.svg

It's missing the tags at the top and some details look different (e.g. stroke width), but this could be adjusted :wink: I decided to calculate the layout in renderGitWorkflow. However, this function/logic could be split in its two concerns/responsibilities: creating and rendering diagram. For example, logic could be extracted as function createGitWorkflowDiagram in such a way that the returned result could be passed to renderDiagram, i.e.

const svgExample = renderDiagram(createGitWorkflowDiagram({
  branches: [
    masterBranch,
    hotfixBranch,
    releaseBranch,
    ...
  ],
  commitOrder: [
    [masterCommit1],
    [hotfixCommit1, developCommit1, feature2Commit1],
    [masterCommit2, developCommit2, feature2Commit2],
    ...
  ],
  edges: [
    edge(masterCommit1, hotfixCommit1),
    edge(masterCommit1, masterCommit2),
    ...
  ],
}))

Runnable example

This example shows that it is easy to define custom layouts or DSLs in JS :nerd_face: renderDiagram could be re-used between the different examples if it is designed well. Probably, it may be a class DiagramRenderer that could be configured and extended as needed. I'm still thinking about how the API should look like :sweat_smile:

Epilog

I created this issue to get feedback on the approach and start a discussion. It is quite different to the current approaches (custom language + rendering and layout with dagre). Even though a new language could be added for my approach to hide the details in the library, I think that it is more powerful to re-use JavaScript and only provide an API (like shown in my examples). I'm still unsure how it should look like, but I learn from every example that I implement. Maybe you like to play with it, too, or share your thoughts :relaxed:

maiermic commented 2 years ago

I used https://www.diagrams.net/ to create the SVG that visualizes the cardinal points:

coordinates.svg

I now rewrote it using my approach (running example) and the result looks like this:

coordinates.svg
abitrolly commented 2 years ago

It looks nice. However, my use case is fixing mermaid to respect order of nodes mentioned in flowchart definition, and with dagre being archived, the question is - how it all works? Comparing dagre with plan JS that does manual layout could greatly help to understand how mermaid works better.

Maybe even help to draw rhe diagram that shows how mermaid itself works. :D

dnlmc commented 1 year ago

I have to imagine there is significant demand for more manual customization such as this, has the core team taken a perspective on this sort of direction?

jgreywolf commented 1 year ago

We are very interested in adding such a solution. That might have to be a different type of diagram though with different syntax dedicated specifically for this purpose.

A host of new possibilities has opened up with the lazy loading of diagrams, as well as support from Mermaid Chart

inoas-nbw commented 2 months ago

This would be a game changer for us. What would also be great is if certain parts of a diagram are manually managed in layouting, aka sub-parts of the diagram. As a note, plantuml also does some manual layouting, just maybe take a peek there or support their manual layouting also for mermaid to be able to load their format.

rngtng commented 1 month ago

Thanks for the effort to explore options here. For me, the missing option of manual layout is the biggest drawback of mermaid. Quite often the auto-layout is just too messing and has e.g. crossing lines without reasons...

I very much like the way structurizr solved it. They support an (optional) additional file to save the coordinates. See here https://docs.structurizr.com/ui/diagrams/manual-layout