elkjs icon indicating copy to clipboard operation
elkjs copied to clipboard

Support `ShapeCoords` option

Open peekxc opened this issue 9 months ago • 10 comments

Currently, the exported layout format assigns node/edges coordinates using relative/inherited parent coordinate system (well, allegedly, basic examples suggest this isn't even true).

It would be nice to support exporting coordinates to their global/absolute values, which [seems already supported by ELK](org.eclipse.elk.json.shapeCoords and org.eclipse.elk.json.edgeCoords). I've tried specifying these options with elkjs but to no avail:

const elk = new ELK(defaultLayoutOptions = { "shapeCoords": "ROOT" });
const graph = JSON.parse(data.toString());
const options = {
    "shapeCoords": "ROOT",
    "org.eclipse.elk.json.shapeCoords": "ROOT"
};
elk.layout(graph, layoutOptions = options).then(...)

I've also verified these options are not listed in the layout options:

elk.knownLayoutOptions().then(console.log)

But maybe that's to be expected.

peekxc avatar Mar 18 '25 17:03 peekxc

What version of elkjs are you using?

skieffer avatar Mar 18 '25 22:03 skieffer

0.9.3

peekxc avatar Mar 19 '25 13:03 peekxc

shapeCoords was only added for ELK 0.10.0 https://github.com/eclipse-elk/elk/pull/1071. It should work with the current 0.10.0 release

Eddykasp avatar Mar 19 '25 13:03 Eddykasp

~~Ok, I upgraded to 0.10.0 and verified it does not work.~~

Ahh, it looks the options may need to be specified inside the graph itself. So for example this will not give absolute coordinates:

const graph = { 
    "layoutOptions": { "algorithm": "elk.layered"}, 
    ... 
}
shape_coords = {
    "shapeCoords": "ROOT",
    "org.eclipse.elk.json.shapeCoords": "ROOT"
}

const elk = new ELK(defaultLayoutOptions = shape_coords)
const to_json = (obj) => JSON.stringify(obj, null, 4);
elk.layout(graph, layoutOptions = shape_coords).then((obj) => console.log(to_json(obj)))

But this will

const graph = { 
    "layoutOptions": { "algorithm": "elk.layered", "shapeCoords": "ROOT"}, 
    ... 
}
shape_coords = {
    "shapeCoords": "ROOT",
    "org.eclipse.elk.json.shapeCoords": "ROOT"
}

const elk = new ELK(defaultLayoutOptions = shape_coords)
const to_json = (obj) => JSON.stringify(obj, null, 4);
elk.layout(graph, layoutOptions = shape_coords).then((obj) => console.log(to_json(obj)))

So I'm not sure what the defaultLayoutOptions and the layoutOptions actually do

peekxc avatar Mar 19 '25 18:03 peekxc

The JsonImporter class that is responsible for generating the desired coordinates looks only at properties set in the ElkNode instances lying at and below the root ElkNode that is passed to it here:

https://github.com/eclipse-elk/elk/blob/4341aadda0464712ef7bb632954e121f4f4fea08/plugins/org.eclipse.elk.graph.json/src/org/eclipse/elk/graph/json/JsonImporter.xtend#L544-L554

~~So yes, the shapeCoords and edgeCoords settings, unlike others, can only be made within the graph.~~

~~@Eddykasp do you know if it's possible to access the global layout options through the root ElkNode instance?~~ ~~If so, then setting the coordinate properties globally could potentially be supported in a future release.~~

EDIT: Global layout options should be getting populated into the graph, so it should work -- see below.

skieffer avatar Mar 21 '25 23:03 skieffer

I would need to look into it, but of the top of my head I think it shouldn't be an issue to just also transfer the properties from the graph object. In ELK itself properties set at the root are used commonly. So unless I am missing something I think this is mainly a shortcoming of the JSONImporter.

Eddykasp avatar Mar 22 '25 04:03 Eddykasp

I think it's something we should try and fix though for 0.10.1 if it's a desired use case.

Eddykasp avatar Mar 22 '25 04:03 Eddykasp

Okay, I've looked into this more closely, and found a couple of things.

(1) I think it's actually already working. @peekxc can you try it again with the syntax

elk.layout(graph, {layoutOptions: shape_coords})

For me that's working.

If that works, I'll point out as well that any defaultLayoutOptions you set when you form the ELK instance will be overridden if you pass a layoutOptions when you call layout().

(2) Just to correct what I wrote earlier -- and if I now understand correctly -- the support for "layout options" is a feature of elkjs only, implemented in elk-api.js. This is not a part of ELK at all.

In that case, JsonImporter is already implemented correctly. It looks only at the nodes of the graph, because in ELK that's all there is to look at.

In elkjs, properties passed as layout options are populated into the graph here:

https://github.com/kieler/elkjs/blob/4156746ae9078cfa68b64772af466ed407c0194a/src/java/org/eclipse/elk/js/ElkJs.java#L170-L176

skieffer avatar Mar 24 '25 21:03 skieffer

I think there's no issue here, and this can be closed.

I've added a demonstrator page in #298 to help show new users how to use the different coordinate modes. In particular, it shows that coordinate settings can be passed via elk.layout(graph, {layoutOptions: ...}).

skieffer avatar Apr 06 '25 12:04 skieffer

@skieffer thanks for looking into it. I'll be back in the office next week and will try to clean up the issues then

Eddykasp avatar Apr 06 '25 13:04 Eddykasp