Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can I set defaultLayoutOptions at node level? #27

Closed
tylerlong opened this issue Apr 30, 2018 · 6 comments
Closed

Can I set defaultLayoutOptions at node level? #27

tylerlong opened this issue Apr 30, 2018 · 6 comments
Labels

Comments

@tylerlong
Copy link

tylerlong commented Apr 30, 2018

I know that I can set defaultLayoutOptions when I initialize an ELK instance:

new ELK({
  defaultLayoutOptions: {
    'elk.layered.spacing.nodeNodeBetweenLayers': 33
  }
})

So that every node in the graph will use this defaultLayoutOptions unless you specify layoutOption to override it.

Problem is: layoutOption at node level is not inherited by children nodes. So if I want all children nodes to have the same layoutOptions, I need to set them one by one.

I know that if I set elk.hierarchyHandling to INCLUDE_CHILDREN, the algorithm is responsible to process all hierarchy levels that are contained in the associated parent node. But this is not what I want: I cannot customize each children separately. This issue was reported here: #26

So question is: is here an defaultLayoutOptions setting at node level, not only all its children inherit this setting but also can override it?

@uruuru
Copy link
Member

uruuru commented Apr 30, 2018

In ELK itself, there's no concept of layout option inheritance - you have to set them for each element (apart from the default values).
The elk.hierarchyHandling option is an exception in that it must be propagated prior to layout in order to know which part of the graph should be laid out in a way that supports hierarchical edges (you can stop the propagation by setting SEPARATE_CHILDREN for some child node, which also allows you to use a different layout algorithm, or direction, from that point on).
The defaultLayoutOptions object is convenience added by the elkjs connector code.

@tylerlong
Copy link
Author

tylerlong commented May 2, 2018

I just have an idea. I could "invent" a new concise JSON format for ELK. (I know there is ELK Text Format, problem is it's not JSON) together with a converter. So I can convert between the official JSON format and the concise JSON format.

In the specification of the new concise JSON format, I can add a rule so that every children inherit parent node's layout options (and other options such as sizeOptions).

It won't break anything because I don't feed the concise JSON format to ELK directly. I will process it with the converter first.

I want to do this because ELK is a great project, but it's JSON format is pretty verbose. If we could simplify it, more users will come.

@uruuru
Copy link
Member

uruuru commented May 2, 2018

I'm not sure if a completely new format is required here. Maybe it's enough to add something like propagateLayoutOptions: true or inheritLayoutOptions: true, which can be set as part of the global options and can also be enabled/disabled for each node. The option could then be evaluated by the JsonImporter.

Or are there other things that you feel could be made more concise?

@tylerlong
Copy link
Author

tylerlong commented May 6, 2018

There are four more things in my mind:

  1. The id. Currently id is required. If user really doesn't want to provide one (because he doesn't need to reference it later), I can generate a random ID for him
  2. The size. If most of the nodes has the same size, I can just set sizeOptions to specify it and also have inheritSizeOptions: true
  3. arrays. For example, when I want to set a label, I need to set labels: [...]. It is better to let user specify label: {...} and convert it to array automatically.
  4. edges syntax is complex. edges: [ { sources: ['n1'], target: ['n2'] }, { sources: ['n2'], target: ['n3'] }]. We could support DSL like edges: ['n1==>n2', 'n2==>n3'].

@tylerlong
Copy link
Author

tylerlong commented May 7, 2018

A real sample

You will see that the concise format is much shorter than ELK format.

defaultLayoutOptions

{
      "elk.algorithm": "layered",
      "elk.direction": "RIGHT",
      "elk.padding": "[top=25,left=25,bottom=25,right=25]",
      "elk.spacing.componentComponent": 25,
      "elk.layered.spacing.nodeNodeBetweenLayers": 25,
      "elk.edgeLabels.inline": true,
      "elk.edgeRouting": "SPLINES"
    }

defaultSizeOptions

{
  node: { width: 100, height: 50 },
  edgeLabel: { width: 60, height: 20 }
}

concise JSON format

{
      layoutOptions: {
        'elk.direction': 'DOWN',
        'elk.layered.crossingMinimization.semiInteractive': true
      },
      children: [
        {
          id: 'n1',
          label: 'Chrismas'
        },
        {
          id: 'n2',
          label: 'Go shopping'
        },
        {
          id: 'n3',
          label: 'Let me think'
        },
        {
          id: 'n4',
          label: 'Laptop',
          layoutOptions: { 'elk.position': '(1,0)' }
        },
        {
          id: 'n5',
          label: 'iPhone',
          layoutOptions: { 'elk.position': '(2,0)' }
        },
        {
          id: 'n6',
          label: 'Car',
          layoutOptions: { 'elk.position': '(3,0)' }
        }
      ],
      edges: [
        {
          expr: 'n1 ==> n2',
          label: { width: 80, text: 'Get money' }
        },
        {
          expr: 'n2 ==> n3'
        },
        {
          expr: 'n3 ==> n4',
          label: 'One'
        },
        {
          expr: 'n3 ==> n5',
          label: 'Two'
        },
        {
          expr: 'n3 ==> n6',
          label: 'Three'
        }
      ]
    }

ELK JSON format (auto generated from concise format)

    {
      "layoutOptions": {
        "elk.direction": "DOWN",
        "elk.layered.crossingMinimization.semiInteractive": true
      },
      "children": [
        {
          "id": "n1",
          "label": "Chrismas",
          "layoutOptions": {
            "elk.direction": "DOWN",
            "elk.layered.crossingMinimization.semiInteractive": true
          },
          "labels": [
            {
              "text": "Chrismas"
            }
          ],
          "width": 100,
          "height": 50
        },
        {
          "id": "n2",
          "label": "Go shopping",
          "layoutOptions": {
            "elk.direction": "DOWN",
            "elk.layered.crossingMinimization.semiInteractive": true
          },
          "labels": [
            {
              "text": "Go shopping"
            }
          ],
          "width": 100,
          "height": 50
        },
        {
          "id": "n3",
          "label": "Let me think",
          "layoutOptions": {
            "elk.direction": "DOWN",
            "elk.layered.crossingMinimization.semiInteractive": true
          },
          "labels": [
            {
              "text": "Let me think"
            }
          ],
          "width": 100,
          "height": 50
        },
        {
          "id": "n4",
          "label": "Laptop",
          "layoutOptions": {
            "elk.direction": "DOWN",
            "elk.layered.crossingMinimization.semiInteractive": true,
            "elk.position": "(1,0)"
          },
          "labels": [
            {
              "text": "Laptop"
            }
          ],
          "width": 100,
          "height": 50
        },
        {
          "id": "n5",
          "label": "iPhone",
          "layoutOptions": {
            "elk.direction": "DOWN",
            "elk.layered.crossingMinimization.semiInteractive": true,
            "elk.position": "(2,0)"
          },
          "labels": [
            {
              "text": "iPhone"
            }
          ],
          "width": 100,
          "height": 50
        },
        {
          "id": "n6",
          "label": "Car",
          "layoutOptions": {
            "elk.direction": "DOWN",
            "elk.layered.crossingMinimization.semiInteractive": true,
            "elk.position": "(3,0)"
          },
          "labels": [
            {
              "text": "Car"
            }
          ],
          "width": 100,
          "height": 50
        }
      ],
      "edges": [
        {
          "expr": "n1 ==> n2",
          "label": {
            "width": 80,
            "text": "Get money",
            "height": 20
          },
          "id": "427643b0-51fe-11e8-abb6-2b5e42789f70",
          "labels": [
            {
              "width": 80,
              "text": "Get money",
              "height": 20
            }
          ],
          "sources": [
            "n1"
          ],
          "targets": [
            "n2"
          ],
          "markers": [
            {
              "id": ">",
              "position": "end"
            }
          ]
        },
        {
          "expr": "n2 ==> n3",
          "id": "42766ac0-51fe-11e8-abb6-2b5e42789f70",
          "sources": [
            "n2"
          ],
          "targets": [
            "n3"
          ],
          "markers": [
            {
              "id": ">",
              "position": "end"
            }
          ]
        },
        {
          "expr": "n3 ==> n4",
          "label": "One",
          "id": "42766ac1-51fe-11e8-abb6-2b5e42789f70",
          "labels": [
            {
              "text": "One",
              "width": 60,
              "height": 20
            }
          ],
          "sources": [
            "n3"
          ],
          "targets": [
            "n4"
          ],
          "markers": [
            {
              "id": ">",
              "position": "end"
            }
          ]
        },
        {
          "expr": "n3 ==> n5",
          "label": "Two",
          "id": "42766ac2-51fe-11e8-abb6-2b5e42789f70",
          "labels": [
            {
              "text": "Two",
              "width": 60,
              "height": 20
            }
          ],
          "sources": [
            "n3"
          ],
          "targets": [
            "n5"
          ],
          "markers": [
            {
              "id": ">",
              "position": "end"
            }
          ]
        },
        {
          "expr": "n3 ==> n6",
          "label": "Three",
          "id": "42766ac3-51fe-11e8-abb6-2b5e42789f70",
          "labels": [
            {
              "text": "Three",
              "width": 60,
              "height": 20
            }
          ],
          "sources": [
            "n3"
          ],
          "targets": [
            "n6"
          ],
          "markers": [
            {
              "id": ">",
              "position": "end"
            }
          ]
        }
      ],
      "id": "42761ca0-51fe-11e8-abb6-2b5e42789f70"
    }

Ready to run code

https://github.com/mermaidjs/ariel-diagrams/blob/master/test/demo/compact.spec.js

You can run the code and generate an SVG file like below:

image

@uruuru
Copy link
Member

uruuru commented May 8, 2018

Edges and labels are indeed a good point. Let me think about your suggestion for a bit. I'll also open another ticket for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants