kieler / elkjs

ELK's layout algorithms for JavaScript
Other
1.74k stars 94 forks source link

Node wrapping at certain indexes #242

Open nlafleur opened 11 months ago

nlafleur commented 11 months ago

I am looking for a method to force "newlines" at certain nodes. I want to create some kind of "step" method when clicking the nodes. The steps are indicated at the top-left of the nodes.

Here is an example of my ideal outcome: image

The only problem is that the nodes will be placed next to eachother whenever there is enough space for it. This is my outcome right now: image I am trying to make a dynamic component, this happends when I select some big data: image I want to create a kind of "flex-direction" "column" type, so that the steps would be rendered below eachother.

I tried several methods, this is my current config:

const elkOptions = {
  "algorithm": "layered",
  "org.eclipse.elk.spacing.componentComponent": 100,
  "org.eclipse.layered.spacing.nodeNodeBetweenLayers": 40,
  "org.eclipse.elk.layered.considerModelOrder.components": "INSIDE_PORT_SIDE_GROUPS",

  "org.eclipse.elk.layered.wrapping.cutting.strategy": "MANUAL",
  // "org.eclipse.elk.layered.wrapping.cutting.cuts": [3, 4] < this doesn't work
}

Preferably I would even create groups and give the nodes the "parentNode" property, so that it the steps are more clear, but I cant seem to determine the width and height of this group dynamically.

Any other out of the box ideas are welcome.

ELK Version 0.8.2

soerendomroes commented 11 months ago

Regarding the wrapping cutting strategy:

The cutting strategy works only for connected components, moreover, the manual mode is broken in 0.8.2 and a list of cuts required for the manual mode can only be added programmatically.

Regarding placement:

I suggest putting each of our groups in its own node. This way you can just manually place the boxes under each other. The box or rectangle packing algorithms "box" and "rectpacking" will however, similar to the separate connected component packing algorithm that you are currently using be selecting the layered algorithm, always use available width to pack other components in there.

You can however circumvent that by increasing the width of the dummy parent nodes such that two of them cannot fit in the same row and by setting the desired aspect ratio of the packing or for rectpacking the target width for the row accordingly. Content alignment ensures that the inner graph is aligned according to your preferences. With elkjs 0.9.0 you could also use the "in new row" constraint for rectpacking.

Another option would be to just place them yourself and use the fixed layout algorithm.

nlafleur commented 11 months ago

Hi, thanks for your reply.

I tried using the "box" and "rectpacking" algorithms but these won't result in the way I want to (I am really looking for the tree-kind algorithms) So I tried grouping the nodes by the following:

  1. creating group nodes like this:
    {
    id: `group_${newStep}`,
    data: { label: `Step ${newStep}` },
    style: { backgroundColor: "rgba(255,255,255,0)" },
    type: "group"
    }
  2. And setting the following properties for the nodes (excluding the group nodes):
    {
    ...
    parentNode: `group_${stepId}`,
    expandParent: true
    }

And this is how I create my elk js graph:

const graph = {
    id: "root",
    layoutOptions: options,
    children: nodes.map((node) => {
      return ({
        ...node,
        targetPosition: isHorizontal ? "left" : "top",
        sourcePosition: isHorizontal ? "right" : "bottom",
        animated: animated,
        width: DEFAULT_WIDTH,
        height: DEFAULT_HEIGHT
      })
    }),
    edges: edges.map((edge) => ({
      ...edge,
      animated: animated
    }))
  }

  return elk
    .layout(graph)
    .then((layoutedGraph) => ({
      nodes: layoutedGraph.children.map((node) => {
        return ({
          ...node,
          position: { x: node.x, y: node.y }
        })
      }),
      edges: layoutedGraph.edges
    }))
    .catch(console.error)

Steps:

!help!

soerendomroes commented 11 months ago

Can you please build your example graph in elklive or elklive json to be sure we are talking about the same issues?

As far as I understand it, you want something like this.

nlafleur commented 11 months ago

Hi that is indeed almost what I am looking for! Here is my "graph" output in the "elklive json" you linked above: elklive json

Is there a way to convert your "this" example into a json example? I guess my grouping is all wrong

Thanks

soerendomroes commented 11 months ago

You can use the converter. Note however that you need to use elkg as an intermediate representation in some cases.

nlafleur commented 11 months ago

Maybe I should've mentioned that I am using the "ReactFlow" package to visualize the elkjs graph.

So maybe it is not a elkjs problem but a ReactFlow problem. This is the "layoutedGraph" returned by the "elk.layout(graph)":

{
    "id": "root",
    "layoutOptions": {
        "elk.direction": "DOWN",
        "elk.animate": false,
        "aspectRatio": "0.1",
        "algorithm": "rectpacking"
    },
    "children": [
        {
            "id": "group1",
            "children": [
                {
                    "id": "n1",
                    "width": 30,
                    "height": 30,
                    "$H": 279,
                    "x": 22,
                    "y": 12
                },
                {
                    "id": "n2",
                    "width": 30,
                    "height": 30,
                    "$H": 281,
                    "x": 162,
                    "y": 92
                },
                {
                    "id": "n3",
                    "width": 30,
                    "height": 30,
                    "$H": 283,
                    "x": 212,
                    "y": 92
                },
                {
                    "id": "n4",
                    "width": 30,
                    "height": 30,
                    "$H": 285,
                    "x": 12,
                    "y": 92
                },
                {
                    "id": "n5",
                    "width": 30,
                    "height": 30,
                    "$H": 287,
                    "x": 62,
                    "y": 92
                },
                {
                    "id": "n6",
                    "width": 30,
                    "height": 30,
                    "$H": 289,
                    "x": 112,
                    "y": 92
                }
            ],
            "layoutOptions": {
                "nodeSize.constraints": "[MINIMUM_SIZE]",
                "elk.direction": "DOWN",
                "contentAlignment": "[V_TOP, H_LEFT]",
                "nodeSize.minimum": "(300.0,20.0)",
                "algorithm": "layered"
            },
            "edges": [
                {
                    "id": "e0",
                    "sources": [
                        "n1"
                    ],
                    "targets": [
                        "n2"
                    ],
                    "sections": [
                        {
                            "id": "e0_s0",
                            "startPoint": {
                                "x": 42,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 177,
                                "y": 92
                            },
                            "bendPoints": [
                                {
                                    "x": 42,
                                    "y": 62
                                },
                                {
                                    "x": 177,
                                    "y": 62
                                }
                            ],
                            "incomingShape": "n1",
                            "outgoingShape": "n2"
                        }
                    ],
                    "container": "group1"
                },
                {
                    "id": "e1",
                    "sources": [
                        "n1"
                    ],
                    "targets": [
                        "n3"
                    ],
                    "sections": [
                        {
                            "id": "e1_s0",
                            "startPoint": {
                                "x": 47,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 227,
                                "y": 92
                            },
                            "bendPoints": [
                                {
                                    "x": 47,
                                    "y": 52
                                },
                                {
                                    "x": 227,
                                    "y": 52
                                }
                            ],
                            "incomingShape": "n1",
                            "outgoingShape": "n3"
                        }
                    ],
                    "container": "group1"
                },
                {
                    "id": "e2",
                    "sources": [
                        "n1"
                    ],
                    "targets": [
                        "n4"
                    ],
                    "sections": [
                        {
                            "id": "e2_s0",
                            "startPoint": {
                                "x": 27,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 27,
                                "y": 92
                            },
                            "incomingShape": "n1",
                            "outgoingShape": "n4"
                        }
                    ],
                    "container": "group1"
                },
                {
                    "id": "e3",
                    "sources": [
                        "n1"
                    ],
                    "targets": [
                        "n5"
                    ],
                    "sections": [
                        {
                            "id": "e3_s0",
                            "startPoint": {
                                "x": 32,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 77,
                                "y": 92
                            },
                            "bendPoints": [
                                {
                                    "x": 32,
                                    "y": 82
                                },
                                {
                                    "x": 77,
                                    "y": 82
                                }
                            ],
                            "incomingShape": "n1",
                            "outgoingShape": "n5"
                        }
                    ],
                    "container": "group1"
                },
                {
                    "id": "e4",
                    "sources": [
                        "n1"
                    ],
                    "targets": [
                        "n6"
                    ],
                    "sections": [
                        {
                            "id": "e4_s0",
                            "startPoint": {
                                "x": 37,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 127,
                                "y": 92
                            },
                            "bendPoints": [
                                {
                                    "x": 37,
                                    "y": 72
                                },
                                {
                                    "x": 127,
                                    "y": 72
                                }
                            ],
                            "incomingShape": "n1",
                            "outgoingShape": "n6"
                        }
                    ],
                    "container": "group1"
                }
            ],
            "targetPosition": "top",
            "sourcePosition": "bottom",
            "animated": false,
            "$H": 277,
            "x": 15,
            "y": 15,
            "width": 300,
            "height": 134
        },
        {
            "id": "group2",
            "children": [
                {
                    "id": "n1_g512902",
                    "width": 30,
                    "height": 30,
                    "$H": 293,
                    "x": 12,
                    "y": 12
                },
                {
                    "id": "n2_g916742",
                    "width": 30,
                    "height": 30,
                    "$H": 295,
                    "x": 12,
                    "y": 62
                }
            ],
            "layoutOptions": {
                "nodeSize.constraints": "[MINIMUM_SIZE]",
                "elk.direction": "DOWN",
                "contentAlignment": "[V_TOP, H_LEFT]",
                "nodeSize.minimum": "(300.0,20.0)",
                "algorithm": "layered"
            },
            "edges": [
                {
                    "id": "e5",
                    "sources": [
                        "n1_g512902"
                    ],
                    "targets": [
                        "n2_g916742"
                    ],
                    "sections": [
                        {
                            "id": "e5_s0",
                            "startPoint": {
                                "x": 27,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 27,
                                "y": 62
                            },
                            "incomingShape": "n1_g512902",
                            "outgoingShape": "n2_g916742"
                        }
                    ],
                    "container": "group2"
                }
            ],
            "targetPosition": "top",
            "sourcePosition": "bottom",
            "animated": false,
            "$H": 291,
            "x": 15,
            "y": 164,
            "width": 300,
            "height": 104
        },
        {
            "id": "group3",
            "children": [
                {
                    "id": "n1_g992924",
                    "width": 30,
                    "height": 30,
                    "$H": 299,
                    "x": 12,
                    "y": 12
                },
                {
                    "id": "n2_g682435",
                    "width": 30,
                    "height": 30,
                    "$H": 301,
                    "x": 12,
                    "y": 62
                }
            ],
            "layoutOptions": {
                "nodeSize.constraints": "[MINIMUM_SIZE]",
                "elk.direction": "DOWN",
                "contentAlignment": "[V_TOP, H_LEFT]",
                "algorithm": "layered"
            },
            "edges": [
                {
                    "id": "e6",
                    "sources": [
                        "n1_g992924"
                    ],
                    "targets": [
                        "n2_g682435"
                    ],
                    "sections": [
                        {
                            "id": "e6_s0",
                            "startPoint": {
                                "x": 27,
                                "y": 42
                            },
                            "endPoint": {
                                "x": 27,
                                "y": 62
                            },
                            "incomingShape": "n1_g992924",
                            "outgoingShape": "n2_g682435"
                        }
                    ],
                    "container": "group3"
                }
            ],
            "targetPosition": "top",
            "sourcePosition": "bottom",
            "animated": false,
            "$H": 297,
            "x": 15,
            "y": 283,
            "width": 54,
            "height": 104
        }
    ],
    "edges": [],
    "$H": 13,
    "x": 0,
    "y": 0,
    "width": 330,
    "height": 402
}

But this is how my nodes are rendered: (only groups (first objects in children array) are rendered) image