2013-05-21 9 views
14

Jestem nowym użytkownikiem D3 i próbuję utworzyć interaktywną wizualizację sieci. Skopiowałem duże fragmenty przykładu this, ale zmieniłem zakrzywione linie na proste za pomocą "linii" SVG, a nie "ścieżek", a także skalowałem węzły zgodnie z danymi, które reprezentują. Problem polega na tym, że moje groty strzałek (utworzone za pomocą znaczników SVG) znajdują się na końcach linii. Ponieważ niektóre z węzłów są duże, strzałki są ukryte za nimi. Chciałbym, aby moje groty strzałek pokazały się na zewnętrznej krawędzi węzła, na który wskazują.Uzyskiwanie strzałek do punktu na zewnętrznej krawędzi węzła w D3

Oto jak tworzę znaczniki i linki:

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 + ")"; }); 

zauważyłem, że atrybut „reFX” określa, jak daleko od końca linii grot powinien pokazać się. Jak mogę to uzależnić od promienia węzła, na który wskazuje? Jeśli nie mogę tego zrobić, czy mogę zmienić punkty końcowe linii? Zgaduję, chciałbym zrobić w tej funkcji, która resetuje punkty końcowe linii, jak porusza się wszystko:

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 + ")"; 
     }); 
    } 

Które podejście jest bardziej sensowne, a jak bym go wdrożyć?

+0

[To pytanie] (http://stackoverflow.com/questions/16568313/arrows-on-links-in-d3js-force-layout/16568625) powinno pomóc. –

Odpowiedz

14

Dzięki Lars Kotthoff, mam to do pracy zgodnie z poradą z drugiego pytania! Najpierw przełączyłem się z używania linii na ścieżki. Nie sądzę, żebym musiał to zrobić, ale łatwiej było śledzić inne przykłady, na które patrzyłem, ponieważ wykorzystywały ścieżki.

Następnie dodałem pole "promień" do moich węzłów. I właśnie to zrobił, kiedy ustawić atrybut promienia, przez natychmiastowe dodanie go jako rzeczywistego pola zamiast zwrotu wartości:

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; 

I wtedy edytowane moją funkcję kleszcz(), aby wykonać ten promień pod uwagę. Wymagało to trochę prostej geometrii ...

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); 
     }); 

Zasadniczo, trójkąt utworzony przez ścieżkę, to całkowity x zmiana (diffX), i to całkowity y zmiana (diffY) jest podobny trójkąt, który tworzą segment ścieżki wewnątrz węzła docelowego (tj. promień węzła), zmiana x wewnątrz węzła docelowego (offsetX) i zmiana y wewnątrz węzła docelowego (offsetY). Oznacza to, że stosunek promienia węzła docelowego do całkowitej długości ścieżki jest równy stosunkowi przesunięcia X do diffX i stosunku przesunięcia Y do diffY.

Zmieniłem również wartość refX na 10 dla strzałek. Nie jestem pewien, dlaczego to było konieczne, ale teraz wydaje się działać!

+2

Grot narysowany w pytaniu ma postać '>', rozpoczynając "powyżej" początku ('(-5,0)') z linią na osi X ('(10,0)'), kończąc " poniżej "pochodzenie (' (5,0) '). Zwróć uwagę, że SVG nie działa zgodnie z kartezjańskim układem współrzędnych, lewy górny róg jest punktem początkowym rzutni. 'refX = 10' wyrównuje poziome przesunięcie i zapewnia, że ​​strzałka wskazuje na koniec ścieżki (dla której nadal trzeba usunąć długość promienia). – Lekensteyn

1

Odpowiedziałem na to samo pytanie over here. Odpowiedź używa matematyki wektorowej, jest całkiem przydatna również w innych obliczeniach.

Powiązane problemy