2013-05-21 4 views
14

Я новичок в D3, и я пытаюсь создать визуализацию интерактивной сети. Я скопировал большие части примера this, но я изменил изогнутые линии на прямые, используя SVG «линии», а не «пути», и я также масштабировал узлы в соответствии с данными, которые они представляют. Проблема в том, что мои стрелки (созданные с помощью SVG-маркеров) находятся на концах строк. Поскольку некоторые узлы большие, стрелки скрываются за ними. Я хотел бы, чтобы мои стрелки отображались прямо у внешнего края узла, на который они указывают.Получить стрелки, чтобы указать на внешний край узла в D3

Вот как я создаю маркеры и ссылки:

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

Я заметил, что атрибут «REFx» определяет, как далеко от конца линии стрелолист должен появиться. Как я могу сделать это зависимым от радиуса узла, на который он указывает? Если я не могу этого сделать, могу ли я вместо этого изменить конечные точки самих линий? Я предполагаю, что я хотел бы сделать, что в этой функции, которая сбрасывает концы линий, как все движется:

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

Какой подход имеет смысл, и как бы я это осуществить?

+0

[Этот вопрос] (http://stackoverflow.com/questions/16568313/arrows-on-links-in-d3js-force-layout/16568625) должен помочь. –

ответ

14

Thanks Lars Kotthoff, я получил это, чтобы работать по совету другого вопроса! Сначала я переключился с использования линий на пути. Я не думаю, что я действительно должен был это сделать, но мне было легче следовать другим примерам, на которые я смотрел, потому что они использовали пути.

Затем я добавил поле «радиус» на мои узлы. Я просто сделал это, когда я установил атрибут радиус, добавив его в качестве фактического поля, а не возвращать значения сразу:

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; 

Я тогда отредактирован мою функцию клеща(), чтобы принять этот радиус во внимание. Это потребовало немного простой геометрии ...

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

В основном, треугольник, образованный на пути, это полное изменение х (diffX), и это общее у изменения (diffY) является таким же треугольником, который образован сегмент пути внутри целевого узла (т. е. радиус узла), изменение x внутри целевого узла (offsetX) и изменение y внутри целевого узла (offsetY). Это означает, что отношение радиуса целевого узла к общей длине пути равно отношению offsetX к diffX и отношению offsetY к diffY.

Я также изменил значение refX на 10 для стрелок. Я не уверен, почему это было необходимо, но теперь это работает!

+2

Стрелка, нарисованная в вашем вопросе, имеет форму '>', начиная с «начало» ('(-5,0)') с линией до оси x ('(10,0)'), заканчивая " ниже "происхождение (' (5,0) '). Обратите внимание, что SVG не следует декартовой системе координат, верхний левый угол - это источник в виде окна просмотра. 'refX = 10' компенсирует горизонтальное смещение и обеспечивает, чтобы стрелка указывала на конец пути (для которого вам все еще нужно удалить длину радиуса). – Lekensteyn

1

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