Cytoscape.js силовые макеты и размещение узлов

Недавно я узнал о Cytoscape.js и хотел бы попробовать макеты, ориентированные на силу: беседка и упругий.

Теперь у меня есть пара вопросов:

  1. Можно ли получить график, используя один из макетов, в котором размещение узла основано на некотором алгоритме и координаты узла не нужны? Например, в VivaGraphJS график сходится так, что связанные узлы образуют группы, а одинокие узлы перемещаются на периферию. Если это можно сделать на макете с направлением силы, можно ли отключить силы, чтобы узлы можно было свободно выбирать и перетаскивать на холст?

  2. Насколько большие графы (количество узлов / ребер) может реально обрабатывать Cytoscape?

  3. Скажем, у каждого узла и ребра есть пять атрибутов, и я хочу текстовое поле, которое отображает их при наведении курсора. Какой самый простой способ сделать это?

  4. Вики говорит, что отдельные узлы могут быть добавлены, как показано ниже, но cy не имеет функции узла; это ошибка? var n0 = cy.node("n1"); cy.add(n0); // добавить один элемент, n0

  5. Я попытался создать тривиальный граф случайных данных, который использует Arbor/Springy. Приведенный ниже код выдает ошибку "данные не определены" в "return data.cy". Когда макет "случайный", код работает. Что я делаю неправильно?

<!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>

Файл JSON гласит:

{"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) Да, если вы зададите макет в опциях init, он будет использоваться для первоначального размещения узлов, и вам не нужно будет указывать расположение узлов. ungrabifyWhileSimulating Я думаю, опция arbor layout сделает то, что вы просите относительно интерактивности во время беседки.

(2) С рендером SVG, примерно так же, как и в старой версии - что не так уж много. Сейчас я много занимаюсь рефакторингом, чтобы ускорить ядро ​​и обрабатывать больше элементов, и новый рендерер canvas, над которым мы работаем, позволит нам обработать 100 000 элементов (или, может быть, даже 1 000 000 элементов).

(3) Используйте что-то вроде qtip и считайте значения свойств как обычно через API cytoscape.js (т.е. eles.data()).

(4) К сожалению, API трудно поддерживать в вики, и то, что вы прочитали, устарело, учитывая эволюцию предварительных выпусков. Я строю лучшее решение, и оно скоро будет.

(5) Вероятно, это ошибка в компоновке беседки, когда график пуст. В конце концов, вы начинаете с пустого графика. Возможно, обойти это сейчас, инициализируя cytoscape.js в вашем $.getJSON() Перезвоните?

Другие вопросы по тегам