Cytoscape.js force-directed layouts and node placement

浪子不回头ぞ 提交于 2019-12-10 16:16:52

问题


I recently found out about Cytoscape.js and would like to try out the force-directed layouts: arbor and springy.

Now, I have a couple of questions:

  1. Is it possible to achieve a graph, by using one of the layouts, in which the node placement is based on some algorithm and node coordinates are not necessary? For example, in VivaGraphJS the graph converges so that the connected nodes form groups and lone nodes move to the periphery. If this can be done on force-directed layout, can the forces be turned off so the nodes can be selected and dragged freely on the canvas?

  2. How large graphs (number of nodes/edges) can Cytoscape realistically handle?

  3. Say that every node and edge has five attributes and I want a textbox that displays these on mouseover. What's the easiest way to do this?

  4. Wiki says that single nodes can be added like shown below, but cy doesn't have node function; is this an error? var n0 = cy.node("n1"); cy.add(n0); // add a single element, n0

  5. I tried to create a trivial random data graph that uses Arbor/Springy. The code below gives an error "data is undefined" at "return data.cy". When layout is "random" the code works. What am I doing wrong?

<!DOCTYPE html>
<html>
<head>
    <title>Test</title>
    <style type="text/css">
    .full-height { height: 600px; margin: 0 0; padding: 0 0; }
    </style>

    <script src="jquery-1.7.2.js"></script>
    <script src="demo/cytoscape.all.js"></script>
    <script src="demo/extensions/cytoscape.layout.arbor.js"></script>
    <script src="demo/extensions/cytoscape.layout.springy.js"></script>


    <script src="demo/arbor.js"></script>
    <script src="demo/arbor-tween.js"></script>
    <script src="demo/springy.js"></script>


    <script type='text/javascript'>
        function onLoad() {

             // create a mapper for node size
            var nodeSizeMapper = {
                continuousMapper: {
                    attr: {
                        name: "weight",
                        min: 0,
                        max: 40
                    },
                    mapped: {
                        min: 5,
                        max: 25
                    }
                }
            };

            $("#cy").cytoscape( {
                layout: { name: "arbor" }, // works when "random"
                style: {
                    selectors: {
                        "node":{
                            shape: "ellipse",
                            fillColor: "#3366FF",
                            height: nodeSizeMapper,
                            width: nodeSizeMapper,
                            labelText: {
                                passthroughMapper: "id"
                            }
                        },
                        "edge": {
                            lineColor: "#CCFF33",//"#ccc",
                            targetArrowColor: "#CCFF33", //"#ccc",
                            width: {
                                continuousMapper: {
                                    attr: {
                                        name: "weight",
                                        min: 20,
                                        max: 35
                                    },
                                    mapped: {
                                        min: 1,
                                        max: 3
                                    }
                                }
                            },
                            targetArrowShape: "triangle",
                            // labelText: {
                            //     passthroughMapper: "weight"
                            // }
                        },
                        "node:selected": {
                            fillColor: "#333"
                        },
                        "edge:selected":{
                            lineColor: "#666",
                            targetArrowColor: "#666"
                        }
                    }
                },                    
                ready: function(cy) { },
            });
            window.cy = $("#cy").cytoscape("get");

            $.getJSON('edges_nodes.json', function(elements) {
                //console.log(elements);
                window.elements = elements;
                cy.load( elements );
            });
        }
    </script>
</head>

<body onload="onLoad()">
        <div class="full-height" id="cy">
        </div>
</body>
</html>

The JSON file reads:

{"nodes": [{"classes": "b", "data": {"id": "n0", "weight": 40}}, {"classes": "b", "data": {"id": "n1", "weight": 19}}, {"classes": "c", "data": {"id": "n2", "weight": 0}}, {"classes": "d", "data": {"id": "n3", "weight": 19}}, {"classes": "e", "data": {"id": "n4", "weight": 4}}, {"classes": "a", "data": {"id": "n5", "weight": 21}}, {"classes": "e", "data": {"id": "n6", "weight": 13}}, {"classes": "a", "data": {"id": "n7", "weight": 6}}, {"classes": "a", "data": {"id": "n8", "weight": 10}}, {"classes": "b", "data": {"id": "n9", "weight": 6}}, {"classes": "c", "data": {"id": "n10", "weight": 24}}, {"classes": "c", "data": {"id": "n11", "weight": 14}}, {"classes": "e", "data": {"id": "n12", "weight": 11}}, {"classes": "b", "data": {"id": "n13", "weight": 6}}, {"classes": "e", "data": {"id": "n14", "weight": 24}}, {"classes": "b", "data": {"id": "n15", "weight": 26}}, {"classes": "b", "data": {"id": "n16", "weight": 6}}, {"classes": "c", "data": {"id": "n17", "weight": 36}}, {"classes": "a", "data": {"id": "n18", "weight": 7}}, {"classes": "b", "data": {"id": "n19", "weight": 37}}, {"classes": "c", "data": {"id": "n20", "weight": 28}}, {"classes": "d", "data": {"id": "n21", "weight": 11}}, {"classes": "d", "data": {"id": "n22", "weight": 20}}, {"classes": "e", "data": {"id": "n23", "weight": 7}}, {"classes": "b", "data": {"id": "n24", "weight": 31}}, {"classes": "b", "data": {"id": "n25", "weight": 1}}, {"classes": "e", "data": {"id": "n26", "weight": 29}}, {"classes": "d", "data": {"id": "n27", "weight": 31}}, {"classes": "d", "data": {"id": "n28", "weight": 34}}, {"classes": "b", "data": {"id": "n29", "weight": 40}}, {"classes": "d", "data": {"id": "n30", "weight": 38}}, {"classes": "b", "data": {"id": "n31", "weight": 17}}, {"classes": "a", "data": {"id": "n32", "weight": 39}}, {"classes": "d", "data": {"id": "n33", "weight": 4}}, {"classes": "c", "data": {"id": "n34", "weight": 38}}, {"classes": "d", "data": {"id": "n35", "weight": 13}}, {"classes": "b", "data": {"id": "n36", "weight": 15}}, {"classes": "a", "data": {"id": "n37", "weight": 29}}, {"classes": "a", "data": {"id": "n38", "weight": 2}}, {"classes": "d", "data": {"id": "n39", "weight": 35}}, {"classes": "c", "data": {"id": "n40", "weight": 24}}, {"classes": "c", "data": {"id": "n41", "weight": 7}}, {"classes": "e", "data": {"id": "n42", "weight": 24}}, {"classes": "c", "data": {"id": "n43", "weight": 4}}, {"classes": "d", "data": {"id": "n44", "weight": 40}}, {"classes": "a", "data": {"id": "n45", "weight": 19}}, {"classes": "b", "data": {"id": "n46", "weight": 17}}, {"classes": "b", "data": {"id": "n47", "weight": 36}}, {"classes": "b", "data": {"id": "n48", "weight": 26}}, {"classes": "a", "data": {"id": "n49", "weight": 18}}], "edges": [{"data": {"source": "n5", "id": "e0", "weight": 31, "target": "n19"}}, {"data": {"source": "n37", "id": "e1", "weight": 31, "target": "n25"}}, {"data": {"source": "n19", "id": "e2", "weight": 31, "target": "n2"}}, {"data": {"source": "n16", "id": "e3", "weight": 23, "target": "n27"}}, {"data": {"source": "n29", "id": "e4", "weight": 17, "target": "n4"}}, {"data": {"source": "n1", "id": "e5", "weight": 33, "target": "n12"}}, {"data": {"source": "n13", "id": "e6", "weight": 38, "target": "n33"}}, {"data": {"source": "n12", "id": "e7", "weight": 34, "target": "n4"}}, {"data": {"source": "n32", "id": "e8", "weight": 34, "target": "n13"}}, {"data": {"source": "n44", "id": "e9", "weight": 32, "target": "n19"}}, {"data": {"source": "n31", "id": "e10", "weight": 24, "target": "n19"}}, {"data": {"source": "n35", "id": "e11", "weight": 18, "target": "n48"}}, {"data": {"source": "n25", "id": "e12", "weight": 19, "target": "n15"}}, {"data": {"source": "n31", "id": "e13", "weight": 18, "target": "n16"}}, {"data": {"source": "n24", "id": "e14", "weight": 39, "target": "n27"}}, {"data": {"source": "n47", "id": "e15", "weight": 22, "target": "n3"}}, {"data": {"source": "n1", "id": "e16", "weight": 34, "target": "n35"}}, {"data": {"source": "n22", "id": "e17", "weight": 15, "target": "n5"}}, {"data": {"source": "n37", "id": "e18", "weight": 40, "target": "n10"}}, {"data": {"source": "n37", "id": "e19", "weight": 21, "target": "n29"}}]}

回答1:


(1) Yes, if you specify a layout in the init options, it will be used to place nodes initially and you won't have to specify node locations. The ungrabifyWhileSimulating arbor layout option will do what you're asking regarding interactivity during arbor, I think.

(2) With the SVG renderer, about the same as the old version -- which isn't all that many. I'm doing a lot of refactoring now to make the core faster and handle more elements, and the new canvas renderer we're working on will hopefully let us handle 100,000s of elements (or maybe even 1,000,000s of elements).

(3) Use something like qtip, and read the property values as normal via the cytoscape.js API (i.e. eles.data()).

(4) Unfortunately, the API is hard to maintain on the wiki, and what you read was out-of-date, given the evolving nature of the prerelease builds. I'm building a better solution, and it will be up soon.

(5) It's probably a bug in the arbor layout when the graph is empty. You're starting with an empty graph, after all. Perhaps, work around it for now by initialising cytoscape.js in your $.getJSON() callback?



来源:https://stackoverflow.com/questions/11109168/cytoscape-js-force-directed-layouts-and-node-placement

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!