D3 Force-Directed Graph: сила применяется только к некоторым узлам - PullRequest
0 голосов
/ 18 июня 2020

Я работаю над проектом, в котором мне нужно визуализировать некоторые данные, извлеченные из телешоу. Инструмент показывает данные в виде графика с принудительной ориентацией, и пользователь имеет возможность добавлять или удалять данные из определенного c эпизода.

Моя симуляция силы работает, но не для добавленных данных. Я объясняю: когда я запускаю инструмент, он фильтрует данные из эпизода 1, добавляются узлы и ссылки, но сила на них не действует, поэтому они застревают на x = 0, y = 0. Если я добавлю данные из эпизода 2, сила начнет действовать в эпизоде ​​1, и оно будет отображаться правильно, но не во 2 эпизоде ​​и так далее. Таким образом, он не работает с последними добавленными данными, но хорошо работает с другими данными.

Вот код функции, которая отображает данные:

module.exports.renderView = (cont = canvas) => {

    //get the datas from the correct episodes
    let filteredData = getFilteredData(data);
    console.log("FilteredData : ");
    console.log(filteredData);

    //create scales
    const xScale = d3.scaleLinear()
                    .domain([d3.min(filteredData, d => d.Scene),d3.max(filteredData, d => d.Scene)])
                    .range([0,innerWidth]);
    const yScale = d3.scaleBand()
                    .domain(stories.map(d => d.name))
                    .range([0,innerHeight])
                    .paddingInner(0.5);
    const colorScale = d3.scaleOrdinal()
                    .domain(mainPlots)
                    .range(["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#ffff33","#a65628","#f781bf","#999999"]);

    //show the axis
    rightAxis.call(d3.axisBottom(xScale))
                        .attr('transform', `translate(0,${innerHeight})`);


    //extract the links from the dataset
    let linkarray = []
    for(let i = 0; i<filteredData.length; i++){
        filteredData[i].Scene_Links.forEach(e => {
            if (e>=0 && filteredData.some(k => {return e==k.Scene})){
                linkarray.push({source : filteredData[i].Scene, target : e});
            }
        });
    }     


    //setup of the force simulation
    force = d3.forceSimulation(filteredData)
                .force('link', d3.forceLink()
                                .id(d => {return d.Scene})
                                .links(linkarray))
                .force("charge", d3.forceManyBody());



    //circles, or nodes                   
    let circles = circles_g.selectAll('circle').data(filteredData);
        circles.join('circle') 
            .attr('r', yScale.bandwidth)
            .attr('id', d => "scene_" + d.Scene)
            .attr('fill', "#A8A8A8")
            .attr('fill-opacity', 0.7)
            .on('mouseover', mouseOverScene)
            .on("mouseout", mouseOutScene);

    //when hovering a circle
    function mouseOverScene(d, i) {
        d3.select(this).attr('fill','orange');
        evaluationSection.showInfo(d, i);
    }
    function mouseOutScene(d, i) {
        d3.select(this).attr('fill','#A8A8A8');
    }


    //links
    let links = links_g.selectAll('.link').data(linkarray);

    links.join('line')
        .attr('class', 'link')
        .attr("stroke", d => {
            let sharedStory;
            d.source.Specific_Stories.forEach(e1 => {
                sharedStory = d.target.Specific_Stories.find(e2 => e1.split("-")[0]==e2.split("-")[0]);
            });
            return (sharedStory==undefined) ? "#000000" : colorScale(sharedStory);
        })
        .attr("stroke-width", "2");


    //action of the force simulation
    force.on('tick', ticked);  
    function ticked() {
        links.attr('x1',d => xScale(d.source.Scene))
            .attr('x2',d => xScale(d.target.Scene))
            .attr("y1", function(d) { return d.source.y; })
            .attr("y2", function(d) { return d.target.y; });

        circles.attr('cx', d => "" + xScale(d.Scene))
            .attr('cy', d => {return d.y})
    }

    //additional infos to make the visualization more clear   
    let episodeNames = infos_g.selectAll('.name').data(episodeList.filter(d => {return d.show}));
    episodeNames.join('text')
            .attr('transform', d => `translate(${xScale(d.firstScene)},5)`)
            .attr('class', 'name')
            .text(d => 'S' + d.season + '-E' + d.episode);

    let separators = infos_g.selectAll('.separator').data(episodeList.filter(d => {return d.show}).slice(1));
    separators.join('line')
            .attr('class', 'separator')
            .attr('x1',d => xScale(d.firstScene)-xScale(xScale.domain()[0]+1)/2)
            .attr('x2',d => xScale(d.firstScene)-xScale(xScale.domain()[0]+1)/2)
            .attr('y1',0)
            .attr('y2',innerHeight)
            .attr("stroke-dasharray","5, 3")
            .attr("style","stroke:rgb(0,0,0);stroke-width:2;");

}

Примечание: я использую d3 v5. Помимо силы, все работает: правильные данные извлекаются с помощью getFilteredData (), ссылки привязываются к нужным узлам, и когда сила применяется, она применяется правильно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...