web-dev-qa-db-fra.com

Mettez en surbrillance le nœud sélectionné, ses liens et ses enfants dans un graphique dirigé par la force D3

Je travaille sur un graphe orienté force en D3. Je veux mettre en évidence le nœud survolé, ses liens et ses nœuds enfants en définissant tous les autres nœuds et liens sur une opacité inférieure.

Dans cet exemple, http://jsfiddle.net/xReHA/ , je suis capable de faire disparaître tous les liens et nœuds puis de faire fondre les liens connectés, mais, jusqu'à présent, je n'ai pas pu se fondre avec élégance dans les nœuds connectés qui sont des enfants du nœud actuellement survolé.

C'est la fonction clé du code:

function fade(opacity) {
    return function(d, i) {
        //fade all elements
        svg.selectAll("circle, line").style("opacity", opacity);

        var associated_links = svg.selectAll("line").filter(function(d) {
            return d.source.index == i || d.target.index == i;
        }).each(function(dLink, iLink) {
            //unfade links and nodes connected to the current node
            d3.select(this).style("opacity", 1);
            //THE FOLLOWING CAUSES: Uncaught TypeError: Cannot call method 'setProperty' of undefined
            d3.select(dLink.source).style("opacity", 1);
            d3.select(dLink.target).style("opacity", 1);
        });
    };
}

Je reçois un Uncaught TypeError: Cannot call method 'setProperty' of undefined erreur lorsque j'essaie de définir l'opacité sur un élément que j'ai chargé à partir du source.target. Je soupçonne que ce n'est pas la bonne façon de charger ce nœud en tant qu'objet d3, mais je ne peux pas trouver une autre façon de le charger sans recommencer sur tous les nœuds pour trouver ceux qui correspondent à la cible ou à la source du lien. Pour garder les performances raisonnables, je ne veux pas répéter sur tous les nœuds plus que nécessaire.

J'ai pris l'exemple de la décoloration des liens de http://mbostock.github.com/d3/ex/chord.html :

enter image description here

Cependant, cela ne montre pas comment modifier les nœuds enfants connectés.

Toute bonne suggestion sur la façon de résoudre ou d'améliorer cela sera furieusement votée :)

69

L'erreur est due au fait que vous sélectionnez les objets de données (d.source et d.target) plutôt que les éléments DOM associés à ces objets de données.

Vous avez la mise en évidence de la ligne qui fonctionne, mais je combinerais probablement votre code en une seule itération, comme ceci:

 link.style("opacity", function(o) {
   return o.source === d || o.target === d ? 1 : opacity;
 });

Il est plus difficile de mettre en surbrillance les nœuds voisins, car vous devez connaître les voisins de chaque nœud. Ces informations ne sont pas faciles à déterminer avec vos structures de données actuelles, car tout ce que vous avez comme un tableau de nœuds et un tableau de liens. Oubliez le DOM pendant une seconde et demandez-vous comment vous pourriez déterminer si deux nœuds a et b sont voisins?

function neighboring(a, b) {
  // ???
}

Une façon coûteuse de le faire est d'itérer sur tous les liens et de voir s'il existe un lien qui relie a et b:

function neighboring(a, b) {
  return links.some(function(d) {
    return (d.source === a && d.target === b)
        || (d.source === b && d.target === a);
  });
}

(Cela suppose que les liens ne sont pas dirigés. Si vous souhaitez uniquement mettre en surbrillance les voisins connectés vers l'avant, supprimez la seconde moitié du bloc OU.)

Un moyen plus efficace de calculer cela, si vous devez le faire fréquemment, est d'avoir une carte ou une matrice qui permet une recherche en temps constant pour tester si a et b sont voisins. Par exemple:

var linkedByIndex = {};
links.forEach(function(d) {
  linkedByIndex[d.source.index + "," + d.target.index] = 1;
});

Vous pouvez maintenant dire:

function neighboring(a, b) {
  return linkedByIndex[a.index + "," + b.index];
}

Et ainsi, vous pouvez maintenant parcourir les nœuds et mettre à jour leur opacité correctement:

node.style("opacity", function(o) {
  return neighboring(d, o) ? 1 : opacity;
});

(Vous pouvez également souhaiter caser le lien survolé lui-même, soit en définissant un lien automatique pour chaque nœud dans linkedByIndex, soit en testant d directement lors du calcul du style, ou en en utilisant un! important css :hover style.)

La dernière chose que je changerais dans votre code est d'utiliser l'opacité de remplissage et l'opacité du trait plutôt que l'opacité, car celles-ci offrent de bien meilleures performances.

88
mbostock