kieler / klayjs

(deprecated) KIELER's layout algorithms for JavaScript
Eclipse Public License 1.0
231 stars 22 forks source link

Force edge to leave and re-enter parent source node when targeting descendant node #17

Closed jbeard4 closed 8 years ago

jbeard4 commented 8 years ago

Hello,

I am trying to find a way to prompt klay to layout an edge that originates in a parent node and targets a descendant node, such that the edge exits and re-enters the parent node.

My use case is as follows. In the Statecharts formalism, there are two varieties of transitions: internal and external. This distinction is relevant when a transition originates from a parent state and targets a substate. The difference between these two transition types, semantically, is that when a transition is taken, an external transition causes the source state to be exited and re-entered, while the internal transition does not cause the source state to be exited.

I have found that the KlayJS default layout works very well for internal transitions:

I have tried to force klay to exit the parent node by creating an invisible "pseudonode" with width and height 0, however this results in a graphical artifact, as a bend point is created which overlaps with the edge originating from the pseudonode. You can see the results below:

Input KGraph JSON:

{
    "id": "root",
    "labels": [
        {
            "text": "root"
        }
    ],
    "edges": [
        {
            "id": "a_a2",
            "source": "a",
            "target": "$generated-1",
            "labels": [
                {
                    "text": "t1",
                    "width": 3.109375,
                    "height": 4.5
                }
            ],
            "$type": "hyperlink"
        },
        {
            "id": "a2_b",
            "source": "a2",
            "target": "b",
            "labels": [
                {
                    "text": "t2",
                    "width": 3.109375,
                    "height": 4.5
                }
            ]
        },
        {
            "id": "b_c",
            "source": "b",
            "target": "c",
            "labels": [
                {
                    "text": "t3",
                    "width": 3.109375,
                    "height": 4.5
                }
            ]
        },
        {
            "id": "$generated-1_a2",
            "source": "$generated-1",
            "target": "a2"
        }
    ],
    "width": 11.4375,
    "height": 9.5,
    "$type": "scxml",
    "children": [
        {
            "id": "a",
            "labels": [
                {
                    "text": "a"
                }
            ],
            "edges": [],
            "width": 6.765625,
            "height": 9.5,
            "children": [
                {
                    "id": "a1",
                    "labels": [
                        {
                            "text": "a1"
                        }
                    ],
                    "edges": [],
                    "width": 8.765625,
                    "height": 9.5,
                    "x": 0,
                    "y": 0
                },
                {
                    "id": "a2",
                    "labels": [
                        {
                            "text": "a2"
                        }
                    ],
                    "edges": [],
                    "width": 8.765625,
                    "height": 9.5,
                    "x": 0,
                    "y": 0
                }
            ],
            "x": 0,
            "y": 0
        },
        {
            "id": "b",
            "labels": [
                {
                    "text": "b"
                }
            ],
            "edges": [],
            "width": 7,
            "height": 9.5,
            "x": 0,
            "y": 0
        },
        {
            "id": "c",
            "labels": [
                {
                    "text": "c"
                }
            ],
            "edges": [],
            "width": 6.765625,
            "height": 9.5,
            "x": 0,
            "y": 0
        },
        {
            "id": "$generated-1",
            "$type": "pseudonode",
            "width": 0,
            "height": 0,
            "edges": [],
            "x": 0,
            "y": 0
        }
    ]
}

Kgraph after layout:

{
    "id": "root",
    "labels": [
        {
            "text": "root"
        }
    ],
    "edges": [
        {
            "id": "a_a2",
            "source": "a",
            "target": "$generated-1",
            "labels": [
                {
                    "text": "t1",
                    "width": 3,
                    "height": 4,
                    "x": 23,
                    "y": 56
                }
            ],
            "$type": "hyperlink",
            "sourcePoint": {
                "x": 87,
                "y": 46
            },
            "targetPoint": {
                "x": 10,
                "y": 36
            },
            "bendPoints": [
                {
                    "x": 96,
                    "y": 46
                },
                {
                    "x": 96,
                    "y": 63
                },
                {
                    "x": 16,
                    "y": 63
                },
                {
                    "x": 16,
                    "y": 36
                }
            ],
            "junctionPoints": []
        },
        {
            "id": "a2_b",
            "source": "a2",
            "target": "b",
            "labels": [
                {
                    "text": "t2",
                    "width": 3,
                    "height": 4,
                    "x": 64,
                    "y": 18
                }
            ],
            "sourcePoint": {
                "x": 37,
                "y": 26
            },
            "targetPoint": {
                "x": 77,
                "y": 26
            },
            "junctionPoints": []
        },
        {
            "id": "b_c",
            "source": "b",
            "target": "c",
            "labels": [
                {
                    "text": "t3",
                    "width": 3,
                    "height": 4,
                    "x": 133,
                    "y": 28
                }
            ],
            "sourcePoint": {
                "x": 123,
                "y": 36
            },
            "targetPoint": {
                "x": 146,
                "y": 36
            },
            "junctionPoints": []
        },
        {
            "id": "$generated-1_a2",
            "source": "$generated-1",
            "target": "a2",
            "sourcePoint": {
                "x": 10,
                "y": 36
            },
            "targetPoint": {
                "x": 68,
                "y": 36
            },
            "junctionPoints": []
        }
    ],
    "width": 163,
    "height": 74,
    "$type": "scxml",
    "children": [
        {
            "id": "a",
            "labels": [
                {
                    "text": "a"
                }
            ],
            "edges": [],
            "width": 47,
            "height": 46,
            "children": [
                {
                    "id": "a1",
                    "labels": [
                        {
                            "text": "a1"
                        }
                    ],
                    "edges": [],
                    "width": 8,
                    "height": 9,
                    "x": 10,
                    "y": 10
                },
                {
                    "id": "a2",
                    "labels": [
                        {
                            "text": "a2"
                        }
                    ],
                    "edges": [],
                    "width": 8,
                    "height": 9,
                    "x": 28,
                    "y": 21
                }
            ],
            "x": 39,
            "y": 10,
            "$H": 25
        },
        {
            "id": "b",
            "labels": [
                {
                    "text": "b"
                }
            ],
            "edges": [],
            "width": 7,
            "height": 9,
            "x": 116,
            "y": 31
        },
        {
            "id": "c",
            "labels": [
                {
                    "text": "c"
                }
            ],
            "edges": [],
            "width": 6,
            "height": 9,
            "x": 146,
            "y": 31
        },
        {
            "id": "$generated-1",
            "$type": "pseudonode",
            "width": 0,
            "height": 0,
            "edges": [],
            "x": 10,
            "y": 36
        }
    ],
    "$H": 1
}

I was wondering if there is a better way to use the klay API to support this use case? Thank you for your guidance on this.

uruuru commented 8 years ago

One option is to add ports to the parent node a that serve as explicit entry and exit points, say p_in and p_out. Then add two edge a2 -> a:p_out and a:p_out -> a:p_in. From the layout algorithm's perspective the latter is a self loop.

jbeard4 commented 8 years ago

This issue has been resolved. Thank you for your help.