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

Hierarchical layout, layer constraints not respected #327

Open
simon-slowik opened this issue Mar 3, 2025 · 3 comments
Open

Hierarchical layout, layer constraints not respected #327

simon-slowik opened this issue Mar 3, 2025 · 3 comments
Labels

Comments

@simon-slowik
Copy link

Describe the bug
I want to achieve a layered layout with swim lanes, orthogonal to the direction of layers (see screenshot). To that end, I create a hierarchical graph where parent nodes represent the swim lanes.
Requirements:

  • Each swim lane has a root node that should be aligned to the left.
  • Individual nodes may have dependencies (i.e. edges) across swim lanes. Those nodes should still be layered as a global directional flow without backward edges.

Simply setting "elk.hierarchyHandling": "INCLUDE_CHILDREN" does not do the trick.
So I tried an approach with two consecutive runs:

  • the first one ignores hierarchies entirely and assigns layers globally
  • the second run uses those layers as constraints and performs cross minimization and node placement with hierarchical nodes.

However, ELK does not respect either the layer constraints, or the layer choice constraints.
I'd appreciate your help.

Expected behavior

Image

Screenshots
Failure modes:

Image

Image

ELK Version
elkjs: 0.9.3

Additional context
JSON to try:

{
    "id": "root",
    "x": 0,
    "y": 0,
    "width": 853,
    "height": 293,
    "layoutOptions": {
        "elk.algorithm": "layered",
        "elk.direction": "RIGHT",
        "elk.randomSeed": "42",
        "interactiveLayout": "true",
        "elk.layered.layering.strategy": "INTERACTIVE",
        "elk.hierarchyHandling": "INCLUDE_CHILDREN",
    },
    "edges": [
        {
            "id": "47e32bb7-fe1b-41d8-8d95-e3f0a7e53775",
            "sources": [
                "46e580fc-6c68-488c-9bbf-31749b6e25ba"
            ],
            "targets": [
                "47bcfb03-b2de-40cc-9b1b-659c3720e5ba"
            ]
        }
    ],
    "children": [
        {
            "id": "__GROUP__560bc4eb-f6ae-46b2-abb3-0b5f8924523e",
            "x": 12,
            "y": 137,
            "width": 814,
            "height": 144,
            "layoutOptions": {
                "elk.layered.layering.layerConstraint": "FIRST_SEPARATE"
            },
            "edges": [
                {
                    "id": "68461930-5183-4c5b-aab5-c107556dc259",
                    "sources": [
                        "560bc4eb-f6ae-46b2-abb3-0b5f8924523e"
                    ],
                    "targets": [
                        "2f32c7f5-f11d-4701-9a70-384415423bd2"
                    ]
                },
                {
                    "id": "3156e27f-3437-445d-b73b-88f239c5ef4d",
                    "sources": [
                        "2f32c7f5-f11d-4701-9a70-384415423bd2"
                    ],
                    "targets": [
                        "46e580fc-6c68-488c-9bbf-31749b6e25ba"
                    ]
                },
                {
                    "id": "d65ec8a3-0735-4f3e-8906-191ce4f8b7f7",
                    "sources": [
                        "2f32c7f5-f11d-4701-9a70-384415423bd2"
                    ],
                    "targets": [
                        "1390f39a-1f90-4662-b894-2718d0bd17f8"
                    ]
                }
            ],
            "children": [
                {
                    "id": "560bc4eb-f6ae-46b2-abb3-0b5f8924523e",
                    "x": 12,
                    "y": 18,
                    "width": 250,
                    "height": 54,
                    "layoutOptions": {
                        "elk.layered.layering.layerConstraint": "FIRST_SEPARATE",
                        "elk.layered.layering.layerChoiceConstraint": "0"
                    }
                },
                {
                    "id": "2f32c7f5-f11d-4701-9a70-384415423bd2",
                    "x": 282,
                    "y": 20,
                    "width": 250,
                    "height": 50,
                    "layoutOptions": {
                        "elk.layered.layering.layerConstraint": "NONE",
                        "elk.layered.layering.layerChoiceConstraint": "1"
                    }
                },
                {
                    "id": "46e580fc-6c68-488c-9bbf-31749b6e25ba",
                    "x": 552,
                    "y": 82,
                    "width": 250,
                    "height": 50,
                    "layoutOptions": {
                        "elk.layered.layering.layerConstraint": "NONE",
                        "elk.layered.layering.layerChoiceConstraint": "2"
                    }
                },
                {
                    "id": "1390f39a-1f90-4662-b894-2718d0bd17f8",
                    "x": 552,
                    "y": 12,
                    "width": 250,
                    "height": 50,
                    "layoutOptions": {
                        "elk.layered.layering.layerConstraint": "NONE",
                        "elk.layered.layering.layerChoiceConstraint": "2"
                    }
                }
            ]
        },
        {
            "id": "__GROUP__c51ee751-a350-4b6d-ab16-5b1837d905c5",
            "x": 17,
            "y": 12,
            "width": 544,
            "height": 105,
            "layoutOptions": {
                "elk.layered.layering.layerConstraint": "FIRST_SEPARATE"
            },
            "edges": [
                {
                    "id": "92fb90f9-d7a8-48c0-931a-9df30793a68e",
                    "sources": [
                        "c51ee751-a350-4b6d-ab16-5b1837d905c5"
                    ],
                    "targets": [
                        "47bcfb03-b2de-40cc-9b1b-659c3720e5ba"
                    ]
                }
            ],
            "children": [
                {
                    "id": "c51ee751-a350-4b6d-ab16-5b1837d905c5",
                    "x": 12,
                    "y": 12,
                    "width": 250,
                    "height": 54,
                    "layoutOptions": {
                        "elk.layered.layering.layerConstraint": "FIRST_SEPARATE",
                        "elk.layered.layering.layerChoiceConstraint": "0"
                    }
                },
                {
                    "id": "47bcfb03-b2de-40cc-9b1b-659c3720e5ba",
                    "x": 282,
                    "y": 43,
                    "width": 250,
                    "height": 50,
                    "layoutOptions": {
                        "elk.layered.layering.layerConstraint": "NONE",
                        "elk.layered.layering.layerChoiceConstraint": "3"
                    }
                }
            ]
        }
    ]
}
@soerendomroes
Copy link
Member

Hi, ELK layered does not support this, since it does not support swimlanes. However, I remember that swimlanes were once conceptualized for a student thesis. Hence, it would be helpful, if you would add some examples and what should happen in this case such that we could potentially integrate swimlanes into ELK layered in the future.

@simon-slowik
Copy link
Author

Hi Sören, thanks for the quick response.
I understand that ELK does not have dedicated support for swim lanes, but I am not sure it needs to.
By grouping nodes into separate parent nodes and then setting elk.hierarchyHandling: INCLUDE_CHILDREN, I would expect it to look exactly like the desired outcome.

Image

From the docs: "Setting a node’s hierarchy handling to INCLUDE_CHILDREN will lay out that node and all of its descendants in a single layout run".

That means all nodes are layered as if there were no hierarchies, and layer(N4) > layer(N3). Also, constraining child-nodes ("Root 1" and "Root 2") to "FIRST_SEPARATE" should result in a single global first layer.

That is why I tagged this issue as a bug, because the current behaviour is unexpected to me. (Note that there are no edges between parents, only between child nodes, but those may cross parents. Maybe there is some complexity in that?)

@soerendomroes
Copy link
Member

Hi, INCLUDE_CHILDREN only considers the whole hierarchy for edge crossings but not for the placement of nodes and the direction of edges. Hence, Lane 2 will never be as long as you want it to be.

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