Функции мыши D3 Force Layout - PullRequest
       32

Функции мыши D3 Force Layout

0 голосов
/ 23 октября 2019

Я пытаюсь сделать мои соседние узлы видимыми, включая ссылки, в то время как другие узлы невидимы, однако я не могу получить правильный список соседних узлов. Я пытаюсь выбрать подключенные узлы в connectedNodes и соседней функции. Однако соседние узлы выбраны неправильно.

var colors = d3.scaleOrdinal(d3.schemeCategory10);

    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        node,
        link,
        ggraph;
    var toggle = 0;
        //Create an array logging what is connected to what
    var linkedByIndex = {};

    svg.append('defs').append('marker')
        .attrs({'id':'arrowhead',
            'viewBox':'-0 -5 10 10',
            'refX':43,
            'refY':0,
            'orient':'auto',
            'markerWidth':13,
            'markerHeight':13,
            'xoverflow':'visible'})
        .append('svg:path')
        .attr('d', 'M 0,-5 L 10 ,0 L 0,5')
        .attr('fill', '#999')
        .style('stroke','none');

    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function (d) {return d.id;}).distance(500).strength(1))
        .force("charge", d3.forceManyBody())
        .force("center", d3.forceCenter(width / 2, height / 2));

    d3.json("miserables.json", function (error, graph) {
        if (error) throw error;

        for (i = 0; i < graph.nodes.length; i++) {
            linkedByIndex[i + "," + i] = 1;
            // console.log(linkedByIndex)
            // console.log(i)
        };

        graph.links.forEach(function (d) {
            linkedByIndex[d.source.index + "," + d.target.index] = 1;
            // console.log('array ', linkedByIndex)
        });

        update(graph.links, graph.nodes);
    })

    function update(links, nodes) {
        link = svg.selectAll(".link")
            .data(links)
            .enter()
            .append("line")
            .attr("class", "link")
            .attr('marker-end','url(#arrowhead)')

        link.append("title")
            .text(function (d) {return d.type;});

        edgepaths = svg.selectAll(".edgepath")
            .data(links)
            .enter()
            .append('path')
            .attrs({
                'class': 'edgepath',
                'fill-opacity': 0,
                'stroke-opacity': 0,
                'id': function (d, i) {return 'edgepath' + i}
            })
            .style("pointer-events", "none");

        edgelabels = svg.selectAll(".edgelabel")
            .data(links)
            .enter()
            .append('text')
            .style("pointer-events", "none")
            .attrs({
                'class': 'edgelabel',
                'id': function (d, i) {return 'edgelabel' + i},
                'font-size': 10,
                'fill': '#aaa'
            });

        edgelabels.append('textPath')
            .attr('xlink:href', function (d, i) {return '#edgepath' + i})
            .style("text-anchor", "middle")
            .style("pointer-events", "none")
            .attr("startOffset", "50%")
            .text(function (d) {return d.type});

        node = svg.selectAll(".node")
            .data(nodes)
            .enter()
            .append("g")
            .attr("class", "node")
            .call(d3.drag()
                    .on("start", dragstarted)
                    .on("drag", dragged)
                    //.on("end", dragended)
            ).on("mouseover", function(d) { 
                // I would like to insert an if statement to do all of these things to the connected nodes
                // if(isConnected(d, o)) {
                    d3.select(this).select("circle").style("stroke-width", 6); 

                    // Figure out the neighboring node id's with brute strength because the graph is small
                    var nodeNeighbors = links.filter(function(link) {
                        // Filter the list of links to only those links that have our target 
                        // node as a source or target
                        return link.source.index === d.index || link.target.index === d.index;})
                    .map(function(link) {
                        // Map the list of links to a simple array of the neighboring indices - this is
                        // technically not required but makes the code below simpler because we can use         
                        // indexOf instead of iterating and searching ourselves.
                        return link.source.index === d.index ? link.target.index : link.source.index; });

                    // Reset all circles - we will do this in mouseout also
                    // svg.selectAll('circle').style('stroke', 'gray');

                    // now we select the neighboring circles and apply whatever style we want. 
                    // Note that we could also filter a selection of links in this way if we want to 
                    // Highlight those as well
                    svg.selectAll('circle').filter(function(node) {
                        // I filter the selection of all circles to only those that hold a node with an
                        // index in my listg of neighbors
                        return nodeNeighbors.indexOf(node.index) > -1;
                    })
                    .style('stroke', 'purple').style('stroke-width', 6);


                    d3.select(this).select("circle").style("stroke", "purple"); 
                    // d3.select(this).select("circle").style("fill", 'red'); 
                    // d3.select(this).select("text").style("font", "20px sans-serif");
                    // d3.selectAll("rect." + d.location).style("stroke", "orange");
                    // d3.selectAll("text." + d.location).style("font", "20px sans-serif");
                    // d3.selectAll("tr." + d.name).style("background-color", "orange");
                    //}
                })
            .on("mouseout",  function(d) { 
                // if(isConnected(d, o)) {
                    svg.selectAll('circle').style('stroke', 'none');

                    // d3.select(this).select("circle").style("stroke-width", 0); 
                    // d3.select(this).select("circle").style("stroke", "none"); 
                    // d3.select(this).select("text").style("font", "12px sans-serif");
                    // d3.selectAll("rect." + d.location).style("stroke-width", 1.5);
                    // d3.selectAll("rect." + d.location).style("stroke", "gray");
                    // d3.selectAll("text." + d.location).style("font", "12px sans-serif");
                    // d3.selectAll("tr." + d.name).style("background-color", "white");
                    //}
                })
            .on('click', connectedNodes);

        node.append("circle")
            .attr("r", 45)
            .style("fill", function (d, i) {return colors(d.group);})

        node.append("title")
            .text(function (d) {return d.id;});

        node.append("text")
            .attr("text-anchor", "middle")
            .attr("class", "nodeTitle")
            .attr("dy", -5)
            .text(function (d) {return d.name});

        //node subhead 
        node.append("text")
            .attr("text-anchor", "middle")
            .attr("class", "subheading")
            .attr("dy", 10)
            .text(function (d) {return ('Label: ' + d.label)})

        //node body text 
        node.append("text")
            .attr("text-anchor", "middle")
            .attr("class", "bodyText")
            .attr("dy", 25)
            .text(function (d) {return ('ID: ' + d.id)});


        simulation
            .nodes(nodes)
            .on("tick", ticked);

        simulation.force("link")
            .links(links);
    }


    //This function looks up whether a pair are neighbours
    function neighboring(a, b) {
        return linkedByIndex[a.index + "," + b.index];
    }

    function connectedNodes() {
        if (toggle == 0) {
            //Reduce the opacity of all but the neighbouring nodes
            d = d3.select(this).node().__data__;

            // console.log('d ' , d)

            node.style("opacity", function (o) {
                // console.log('d and o ', neighboring(d,o))
                return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
            });

            // console.log(node)

            link.style("opacity", function (o) {
                return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
            });
            //Reduce the op
            toggle = 1;
        } else {
            //Put them back to opacity=1
            node.style("opacity", 1);
            link.style("opacity", 1);
            toggle = 0;
        }
    }


    function ticked() {
        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("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";});

        edgepaths.attr('d', function (d) {
            return 'M ' + d.source.x + ' ' + d.source.y + ' L ' + d.target.x + ' ' + d.target.y;
        });

        edgelabels.attr('transform', function (d) {
            if (d.target.x < d.source.x) {
                var bbox = this.getBBox();

                rx = bbox.x + bbox.width / 2;
                ry = bbox.y + bbox.height / 2;
                return 'rotate(180 ' + rx + ' ' + ry + ')';
            }
            else {
                return 'rotate(0)';
            }
        });
    }

    function dragstarted(d) {
        if (!d3.event.active) simulation.alphaTarget(0.3).restart()
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }


//    function dragended(d) {
//        if (!d3.event.active) simulation.alphaTarget(0);
//        d.fx = undefined;
//        d.fy = undefined;
//    }

выборка данных

{
  "nodes": [
    {
      "name": "Peter",
      "label": "Person",
      "group" : 1,
      "id": 1
    },
    {
      "name": "Michael",
      "label": "Person",
      "group" : 1,
      "id": 2
    },
    {
      "name": "Neo4j",
      "label": "Database",
      "group" : 2,
      "id": 3
    },
    {
      "name": "Graph Database",
      "label": "Database",
      "group" : 2,
      "id": 4
    },
    {
      "name": "Bob",
      "label": "Person",
      "group" : 3,
      "id": 5
    },
    {
      "name": "Christine",
      "label": "Person",
      "group" : 1,
      "id": 6
    },
    {
      "name": "Ted",
      "label": "Person",
      "group" : 1,
      "id": 7
    }
  ],
  "links": [
    {
      "source": 1,
      "target": 2,
      "type": "KNOWS",
      "since": 2010
    },
    {
      "source": 2,
      "target": 3,
      "type": "FOUNDED"
    },
    {
      "source": 3,
      "target": 4,
      "type": "WORKS_ON"
    },
    {
      "source": 4,
      "target": 3,
      "type": "IS_A"
    },
    {
      "source": 5,
      "target": 6,
      "type": "cb"
    },
    {
      "source": 6,
      "target": 2,
      "type": "duh"
    },
    {
      "source": 7,
      "target": 6,
      "type": "Bla"
    }
  ]
}

...