Плагин NetworkX D3 Force Layout для mpld3

Я нахожусь в процессе создания плагина mpld3 для преобразования NetworkX Graph в Force Layout. У меня возникли проблемы с пониманием того, как масштабирование по осям работает в mpld3 и как я могу заставить его преобразовать график принудительного макета.

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import mpld3
from mpld3 import plugins, utils

from networkx.readwrite.json_graph import node_link_data

class NetworkXD3ForceLayoutView(plugins.PluginBase):
    """A simple plugin showing how multiple axes can be linked"""

    JAVASCRIPT = """
    mpld3.register_plugin("networkxd3forcelayoutview", NetworkXD3ForceLayoutViewPlugin);
    NetworkXD3ForceLayoutViewPlugin.prototype = Object.create(mpld3.Plugin.prototype);
    NetworkXD3ForceLayoutViewPlugin.prototype.constructor = NetworkXD3ForceLayoutViewPlugin;
    NetworkXD3ForceLayoutViewPlugin.prototype.requiredProps = ["graph", "charge", "linkDistance", "gravity"];

    function NetworkXD3ForceLayoutViewPlugin(fig, props){
        mpld3.Plugin.call(this, fig, props);
    };

    var color = d3.scale.category20();


    NetworkXD3ForceLayoutViewPlugin.prototype.draw = function(){

        var zoom = d3.behavior.zoom();

        var height = this.fig.height
        var width = this.fig.width

        var graph = this.props.graph     
        var gravity = this.props.gravity.toFixed()
        var charge = this.props.charge.toFixed()
        var linkDistance = this.props.linkDistance.toFixed()

        console.log(graph)
        var ax = this.fig.axes[0] // axis required for zoomx and zoomy presumably?

        var g = d3.select('.mpld3-axes').append('g') // This is right? 

        var vis = g.append('svg')
            .attr('width', this.width)
            .attr('height', this.height);

        force = d3.layout.force()
        .gravity(gravity)
        .charge(charge)
        .linkDistance(linkDistance)
        .nodes(graph.nodes)
        .links(graph.links)
        .size([width, height])
        .start()

        var link = vis.selectAll("line.link")
          .data(graph.links)
        .enter().append("svg:line")
          .attr("class", "link")
          .attr("stroke", "black")
          .style("stroke-width", function(d) { return Math.sqrt(d.value); })
          .attr("x1", function(d) { return d.source.x; })
          .attr("y1", function(d) { return d.source.y; })
          .attr("x2", function(d) { return d.target.x; })
          .attr("y2", function(d) { return d.target.y; });

        var node = vis.selectAll("circle.node")
          .data(graph.nodes)
        .enter().append("svg:circle")
          .attr("class", "node")
          .attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; })
          .attr("r", 5)
          .style("fill", function(d) { return d.color; })
          .call(force.drag);

        node.append("svg:title")
          .text(function(d) { return d.name; });

        vis.style("opacity", 1e-6)
        .transition()
          .duration(1000)
          .style("opacity", 1);
        force.on("tick", function() {
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; });
        });

        zoom.on("zoom", function() {
            g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
        })

        g.call(zoom)

    };
    """

    def __init__(self, G, gravity=0.5, link_distance=20, charge=-10):
        self.dict_ = {"type": "networkxd3forcelayoutview",
                      "graph": node_link_data(G),
                      "gravity": gravity,
                      "charge": charge,
                      "linkDistance": link_distance}


fig, ax = plt.subplots(1, 1)

# scatter periods and amplitudes

np.random.seed(0)

import networkx as nx
G=nx.Graph()
G.add_node(1, color='red')
G.add_edge(1,2)

plugins.connect(fig, NetworkXD3ForceLayoutView(G))

mpld3.display()

Выше приведен минимальный рабочий пример, который мне удалось запустить в блокноте. Я добавил обратный вызов масштабирования к элементу группы, который в данный момент содержит график, поэтому график будет масштабироваться, если мышь находится над узлом. Как мне заставить его работать, когда я использую масштабирование на пользовательской панели инструментов. Это правильный подход к созданию плагина принудительной компоновки? Я также разместил здесь, но, возможно, SO лучше подходит для этого вопроса. .


person kdheepak    schedule 30.09.2016    source источник
comment
О, круто, я всегда хотел это! Объекты zoom очень сложные, и я не помню, как они работают. Вы можете найти их в своем объекте ax как ax.zoom, ax.zoom_x и ax.zoom_y. Хотел бы я лучше помнить, что они делают, но чтобы узнать, я бы начал копаться в коде js здесь: github.com/mpld3/mpld3/blob/   -  person Abraham D Flaxman    schedule 01.10.2016
comment
Спасибо за ответ! Я начинаю копаться в коде и натыкаюсь на стены. Я прокомментирую здесь, как только у меня будет лучшее представление о том, что происходит. Было бы хорошо иметь это в mpld3 :)   -  person kdheepak    schedule 02.10.2016
comment
Хорошо, у меня версия рабочая! :) Еще несколько перегибов, чтобы сгладить, и все должно быть хорошо. Как вы думаете, это должно делать в основном репо? Я думаю, было бы хорошо, если бы мы создали в организации репозиторий плагинов, созданных пользователями. Мысли?   -  person kdheepak    schedule 02.10.2016
comment
Круто, мне нравится! Выберите основной репозиторий, либо в качестве примера, либо в качестве дополнительного плагина, в зависимости от того, насколько сложным и общим он оказался.   -  person Abraham D Flaxman    schedule 03.10.2016


Ответы (1)


Я разместил рабочий пример здесь для тех, кто ищет что-то похожий.

person kdheepak    schedule 02.10.2016
comment
Ответы, содержащие только ссылки, должны иметь соответствующий краткий обзор связанного содержимого в теле ответа. График также доступен здесь. - person ggorlen; 17.05.2020
comment
Спасибо за ссылку на это. Я думаю, что отправил этот PR в их примеры после того, как написал сообщение в блоге. - person kdheepak; 18.05.2020