clientIO / joint

A proven SVG-based JavaScript diagramming library powering exceptional UIs
https://jointjs.com
Mozilla Public License 2.0
4.72k stars 853 forks source link

[Bug]: Link tool Connect({}) issues with DirectedGraph.layout #2797

Open Naveena1321 opened 1 week ago

Naveena1321 commented 1 week ago

Current versus expected behaviour

Cannot read properties of undefined (reading 'x'): is returned by chrome on trying layout :

 layoutDirectedGraph() {

        DirectedGraph.layout(this.graph, {
            setVertices: true,
            rankDir: 'TB',
            marginX: 100,
            marginY: 100
        });

        this.paperScroller.centerContent({ useModelGeometry: true });
    }

Steps to reproduce

selectPrimaryLink(linkView: joint.dia.LinkView) {
        if (this.editPermission) {
            const ns = joint.linkTools;
            const tools = [
                // new ns.Vertices(),
                new ns.SourceAnchor(),
                new ns.TargetAnchor(),
                new ns.SourceArrowhead({
                    attributes: {
                        type: 'path',
                        d: 'M 0,0 m -5, 0 a 5,5 0 1,0 10,0 a 5,5 0 1,0 -10,0',
                        stroke: 'none',
                        fill: 'red',
                    },
                }),
                new ns.TargetArrowhead({
                    attributes: {
                        type: 'path',
                        d: 'M 0,0 m -5, 0 a 5,5 0 1,0 10,0 a 5,5 0 1,0 -10,0',
                        stroke: 'none',
                        fill: 'red',
                    },
                }),
                new ns.Boundary({ padding: 15 }),
                new ns.Remove({ offset: -20, distance: 40 }),
                new ns.Connect({})
            ]

            const { name } = linkView.model.router() ?? { name: null };
            // Add segments tool for every router except the rightAngle router.
            // if (name !== 'rightAngle') tools.push(new ns.Segments());

            const toolsView = new joint.dia.ToolsView({
                name: 'link-pointerdown',
                tools
            });

            linkView.addTools(toolsView);
        }
    }

Version

4.0.0

What browsers are you seeing the problem on?

Chrome

What operating system are you seeing the problem on?

Linux

kumilingus commented 5 days ago

Are you saying that if I add the linkTools.Connect() tool to a link view and call DirectedGraph layout, it will throw an exception?

Can you create a JSFiddle that shows the error? Feel free to use this fiddle as base: https://jsfiddle.net/kumilingus/yfbc5knv/

Naveena1321 commented 3 days ago

Sorry I couldn't able to recreate it but the exception is because the connect tools created link does not have a glNode x: glNode.x - glNode.width / 2, y: glNode.y - glNode.height / 2

kumilingus commented 3 days ago

How and what point exactly you call the layout? The error originates in DirectedGraph package.

Naveena1321 commented 2 days ago

I have a button in toolbar and toolbar.on({'layout:pointerclick' i call the layout function imports are=>

import { DirectedGraph } from '@joint/layout-directed-graph'; import * as joint from '@joint/plus';

const graph = this.graph = new joint.dia.Graph({},{
        cellNamespace: appShapes,
    });

    this.commandManager = new joint.dia.CommandManager({
        graph: graph,
    });

    const paper = this.paper = new joint.dia.Paper({
        width: 1000,
        height: 1000,
        gridSize: 16,
        drawGrid: {
            name: 'dot',
            thickness: 3,
            color: '#aaaaaa'
        },
        model: graph,
        cellViewNamespace: appShapes,
        defaultLink: <joint.dia.Link><unknown>new appShapes.app.Link(),
        defaultConnectionPoint: appShapes.app.Link.connectionPoint,
        routerNamespace: joint.routers,
        async: true,
        sorting: joint.dia.Paper.sorting.APPROX,
        background: {
            color: '#f5f6f8'
        },
        defaultRouter: {
            name: "manhattan",
            args: {
                padding: 20,
            },
        },
        linkPinning: true,
        snapLinks: {
            radius: 10,
        },
        multiLinks: true,
        embeddingMode: true,
        frontParentOnly: false,
        clickThreshold: 10,
        highlighting: {
            embedding: {
                name: "mask",
                options: this.highlighterOptions
            }
        },
        validateEmbedding: (childView, parentView) => this.isContainer(parentView.model),
        sync: true,
    });
kumilingus commented 2 days ago

How do you call the layout?

Naveena1321 commented 2 days ago
  this.toolbarService.toolbar.on({
                'layout:pointerclick': this.layoutDirectedGraph.bind(this),})

 layoutDirectedGraph() {

        DirectedGraph.layout(this.graph, {
            setVertices: true,
            rankDir: 'TB',
            marginX: 100,
            marginY: 100
        });

        this.paperScroller.centerContent({ useModelGeometry: true });
    }
kumilingus commented 2 days ago

That looks good. In order for us to help you, you need to be able to reproduce it.

You can try removing different parts of the apps until the issue stops manifesting. I think the graph is already invalid when you call the layout.

Last attempt. Does this return false?

graph.getCells().some(cell => cell.graph !== graph);
Naveena1321 commented 2 days ago

Thanks for all the help and yes it does return false

kumilingus commented 2 days ago

Can you send the JSON of the graph, you are trying to lay out?

Naveena1321 commented 1 day ago
{
    "infinitePaper": false,
    "snaplines": true,
    "grid": {
        "color": "#aaaaaa",
        "size": 16,
        "draw": "Dot",
        "thickness": 3
    },
    "background": {
        "color": "#f5f6f8",
        "image": "",
        "position": "",
        "size": {
            "width": "225",
            "height": "225"
        },
        "repeat": "no-repeat",
        "opacity": 1
    },
    "cells": [
        {
            "type": "standard.Rectangle",
            "position": {
                "x": 80,
                "y": 256
            },
            "size": {
                "width": 80,
                "height": 80
            },
            "angle": 0,
            "name": "Square",
            "id": "52c379de-7caf-422e-aefe-aba1828918d4",
            "z": 1,
            "attrs": {
                "body": {
                    "stroke": "#353535",
                    "rx": 2,
                    "ry": 2,
                    "strokeDasharray": "0",
                    "rotateClockwise": {
                        "rotateAngle": 0
                    },
                    "changeLayer": {
                        "changeTo": ""
                    },
                    "visibility": "visible"
                },
                "label": {
                    "fontSize": 11,
                    "fill": "#353535",
                    "fontFamily": "Open Sans",
                    "fontStyle": "none",
                    "fontWeight": "Normal",
                    "strokeWidth": 0,
                    "text": "",
                    "textDecoration": "none",
                    "format": [
                        ""
                    ],
                    "visibility": "visible",
                    "textWrap": {
                        "ellipsis": true,
                        "height": "calc(h-20)",
                        "width": "calc(w-20)"
                    }
                }
            }
        },
        {
            "type": "standard.Rectangle",
            "position": {
                "x": 384,
                "y": 304
            },
            "size": {
                "width": 120,
                "height": 50
            },
            "angle": 0,
            "name": "Rectangle",
            "id": "85e0c230-542e-4dd4-85c5-52e6904c13e8",
            "z": 2,
            "attrs": {
                "body": {
                    "stroke": "#353535",
                    "rx": 2,
                    "ry": 2,
                    "strokeDasharray": "0",
                    "rotateClockwise": {
                        "rotateAngle": 0
                    },
                    "changeLayer": {
                        "changeTo": ""
                    },
                    "visibility": "visible"
                },
                "label": {
                    "fontSize": 11,
                    "fill": "#353535",
                    "fontFamily": "Open Sans",
                    "fontStyle": "none",
                    "fontWeight": "Normal",
                    "strokeWidth": 0,
                    "text": "",
                    "textDecoration": "none",
                    "format": [
                        ""
                    ],
                    "visibility": "visible",
                    "textWrap": {
                        "ellipsis": true,
                        "height": "calc(h-20)",
                        "width": "calc(w-20)"
                    }
                }
            }
        },
        {
            "type": "app.Link",
            "name": "Dotted arrow",
            "router": {
                "name": "orthogonal"
            },
            "connector": {
                "name": "rounded"
            },
            "labels": [],
            "originalConnections": {
                "sourceId": "",
                "targetId": "",
                "tempSourceId": "85e0c230-542e-4dd4-85c5-52e6904c13e8",
                "tempTargetId": "52c379de-7caf-422e-aefe-aba1828918d4",
                "sourcElementIndex": -1,
                "targetElementIndex": -1
            },
            "originalConnectionsPosition": {
                "sourceId": -1,
                "targetId": -1,
                "tempSourceId": -1,
                "tempTargetId": -1
            },
            "source": {
                "id": "85e0c230-542e-4dd4-85c5-52e6904c13e8"
            },
            "target": {
                "id": "52c379de-7caf-422e-aefe-aba1828918d4"
            },
            "id": "403e61e7-b544-4e75-acb8-cd41a45bde6f",
            "z": 3,
            "attrs": {}
        },
        {
            "type": "standard.Rectangle",
            "position": {
                "x": 224,
                "y": 480
            },
            "size": {
                "width": 80,
                "height": 80
            },
            "angle": 0,
            "name": "Square",
            "id": "ee9b3aee-e856-4dfb-ab79-c81562639ca1",
            "z": 4,
            "attrs": {
                "body": {
                    "stroke": "#353535",
                    "rx": 2,
                    "ry": 2,
                    "strokeDasharray": "0",
                    "rotateClockwise": {
                        "rotateAngle": 0
                    },
                    "changeLayer": {
                        "changeTo": ""
                    },
                    "visibility": "visible"
                },
                "label": {
                    "fontSize": 11,
                    "fill": "#353535",
                    "fontFamily": "Open Sans",
                    "fontStyle": "none",
                    "fontWeight": "Normal",
                    "strokeWidth": 0,
                    "text": "",
                    "textDecoration": "none",
                    "format": [
                        ""
                    ],
                    "visibility": "visible",
                    "textWrap": {
                        "ellipsis": true,
                        "height": "calc(h-20)",
                        "width": "calc(w-20)"
                    }
                }
            }
        },
        {
            "type": "app.Link",
            "name": "Dotted arrow",
            "router": {
                "name": "orthogonal"
            },
            "connector": {
                "name": "rounded"
            },
            "labels": [],
            "originalConnections": {
                "sourceId": "",
                "targetId": "",
                "tempSourceId": "403e61e7-b544-4e75-acb8-cd41a45bde6f",
                "sourcElementIndex": -1,
                "targetElementIndex": -1
            },
            "originalConnectionsPosition": {
                "sourceId": -1,
                "targetId": -1,
                "tempSourceId": -1,
                "tempTargetId": -1
            },
            "source": {
                "id": "403e61e7-b544-4e75-acb8-cd41a45bde6f"
            },
            "target": {
                "id": "ee9b3aee-e856-4dfb-ab79-c81562639ca1"
            },
            "id": "2461de4a-bd9f-4a24-9371-08d8b9552614",
            "z": 5,
            "attrs": {}
        }
    ]
}
kumilingus commented 1 day ago

I see. You use the link to link connection. That is not supported by the DirectedGraph layout. You will have to reconnect the link that connects the link to the source of the other link.

In your example:

const link = app.graph.getCell('2461de4a-bd9f-4a24-9371-08d8b9552614');
link.source(link.getSourceElement())
// --> call layout
// --> reconnect the link back to link
Naveena1321 commented 1 day ago

Thank you for patience and constant support