2013-05-21 6 views
14

Ich bin neu in D3 und versuche eine interaktive Netzwerkvisualisierung zu erstellen. Ich habe große Teile von this Beispiel kopiert, aber ich habe die gekrümmten Linien zu geraden geändert, indem ich SVG- "Linien" anstelle von "Pfaden" verwendet habe, und ich habe auch die Knoten entsprechend den Daten skaliert, die sie darstellen. Das Problem ist, dass meine Pfeilspitzen (erstellt mit SVG-Markierungen) an den Enden der Linien sind. Da einige der Knoten groß sind, werden die Pfeile hinter ihnen versteckt. Ich möchte, dass meine Pfeilspitzen genau an der äußeren Kante des Knotens angezeigt werden, auf den sie zeigen.Hole Pfeilspitzen, um auf die äußere Kante des Knotens in D3 zu zeigen.

Hier ist, wie ich die Markierungen und Links bin erstellen:

svg.append("svg:defs").selectAll("marker") 
    .data(["prereq", "coreq"]) 
    .enter().append("svg:marker") 
    .attr("id", String) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 15) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("svg:path") 
    .attr("d", "M0,-5L10,0L0,5"); 

var link = svg.selectAll(".link") 
    .data(force.links()) 
    .enter().append("line") 
    .attr("class", "link") 
    .attr("marker-end", function(d) { return "url(#" + d.type + ")"; }); 

Ich bemerkte, dass das „REFX“ Attribut gibt an, wie weit vom Ende der Linie sollte die Pfeilspitze angezeigt. Wie kann ich dies vom Radius des Knotens abhängig machen, auf den es zeigt? Wenn ich das nicht kann, könnte ich stattdessen die Endpunkte der Linien selbst ändern? Ich vermute, ich würde in dieser Funktion das tun, was die Endpunkte der Linien wie alles bewegt sich setzt:

function tick() { 
     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; }); 

     circle.attr("transform", function(d) { 
      return "translate(" + d.x + "," + d.y + ")"; 
     }); 

     text.attr("transform", function(d) { 
      return "translate(" + d.x + "," + d.y + ")"; 
     }); 
    } 

Welche Ansatz macht mehr Sinn, und wie würde ich es umsetzen?

+0

[Diese Frage] (http://stackoverflow.com/questions/16568313/arrows-on-links-in-d3js-force-layout/16568625) sollte helfen. –

Antwort

14

Danke Lars Kotthoff, ich habe das nach dem Ratschlag aus der anderen Frage zur Arbeit gebracht! Zuerst wechselte ich von Linien zu Pfaden. Ich glaube nicht, dass ich das wirklich tun musste, aber es machte es leichter, den anderen Beispielen zu folgen, die ich mir ansah, weil sie Pfade benutzten.

Dann fügte ich meinen Knoten ein "Radius" -Feld hinzu. Ich habe gerade das, wenn ich den Radius Attribut gesetzt, indem sie sie als eigentliches Feld hinzufügen, anstatt die Rückgabe des Werts sofort:

var circle = svg.append("svg:g").selectAll("circle") 
        .data(force.nodes()) 
        .enter().append("svg:circle") 
        .attr("r", function(d) { 
         if (d.logic != null) { 
          d.radius = 5; 
         } else { 
          d.radius = node_scale(d.classSize); 
         } 
         return d.radius; 

ich bearbeitet dann meine tick() Funktion dieses Radius zu berücksichtigen. Dies erforderte ein wenig einfacher Geometrie ...

function tick(e) { 

     path.attr("d", function(d) { 
      // Total difference in x and y from source to target 
      diffX = d.target.x - d.source.x; 
      diffY = d.target.y - d.source.y; 

      // Length of path from center of source node to center of target node 
      pathLength = Math.sqrt((diffX * diffX) + (diffY * diffY)); 

      // x and y distances from center to outside edge of target node 
      offsetX = (diffX * d.target.radius)/pathLength; 
      offsetY = (diffY * d.target.radius)/pathLength; 

      return "M" + d.source.x + "," + d.source.y + "L" + (d.target.x - offsetX) + "," + (d.target.y - offsetY); 
     }); 

Grundsätzlich ist das Dreieck, das durch den Weg gebildet wird, ist es total x Änderung (diffX), und es ist insgesamt y Änderung (Diffy) ist ein ähnliches Dreieck, das gebildet durch das Segment des Pfades innerhalb des Zielknotens (dh der Knotenradius), das x ändert sich innerhalb des Zielknotens (offsetX) und das y ändert sich innerhalb des Zielknotens (offsetY). Dies bedeutet, dass das Verhältnis des Zielknotenradius zu der Gesamtweglänge gleich dem Verhältnis von offsetX zu diffX und zu dem Verhältnis von offsetY zu diffY ist.

Ich habe auch den refX Wert auf 10 für die Pfeile geändert. Ich bin mir nicht sicher, warum das notwendig war, aber jetzt scheint es zu funktionieren!

+2

Die in Ihrer Frage gezeichnete Pfeilspitze hat die Form '>', beginnend "über" dem Ursprung ('(-5,0)') mit einer Linie zur x-Achse ('(10,0)'), endend " unter "der Ursprung (' (5,0) '). Beachten Sie, dass SVG nicht dem kartesischen Koordinatensystem folgt, die obere linke Ecke ist der Ursprung des Ansichtsfensters. 'refX = 10' gleicht die horizontale Verschiebung aus und sorgt dafür, dass die Pfeilspitze auf das Ende des Pfades zeigt (für den Sie noch die Radiuslänge entfernen müssen). – Lekensteyn

1

Ich antwortete auf die gleiche Frage over here. Die Antwort verwendet Vektormathematik, sie ist auch für andere Berechnungen sehr nützlich.

Verwandte Themen