Отобразить стрелку в середине ссылки компоновки D3 - программирование
Подтвердить что ты не робот

Отобразить стрелку в середине ссылки компоновки D3

Я использую D3 для рисования ориентированного по силе графа, который очень похож на этот пример: http://bl.ocks.org/mbostock/1153292

Я пытаюсь поместить стрелки в середину ссылок, а не в конец.

Воспроизведение с attr("refX", 0) маркера не очень помогает, потому что оно абсолютное и не относительное к длине ссылки - мои ссылки имеют различную длину.

Я искал поисковые запросы, и моя лучшая идея заключалась в замене link.attr("marker-end", ...) на link.attr("marker-segment", ...) в соответствии с этим пример (ищите кресты в середине графиков). Но это, похоже, не работает. Я предполагаю, что это часть проекта SVG2, но мой браузер поддерживает более низкую версию? (Я использую самую последнюю версию браузера Chrome).

Как разместить стрелки в середине ссылок?

4b9b3361

Ответ 1

Вместо того, чтобы поместить маркер на конец, создайте свои строки (<polyline> или <path>) с одной точкой в ​​середине строки и используйте marker-mid, чтобы применить стрелки.

Эта демонстрация для этого ответа моей использует эту технику для создания нескольких внутренних точек вдоль длины пути вместе с marker-mid. Вы просто создали бы одну "путевую точку" вместо многих.

Изменить. Здесь простой взлом существующей демонстрации, показывающий, что я имею в виду:

Демо: http://jsfiddle.net/tk7Wv/2/

Arrowheads in the middle of curves

Единственные изменения:

  • Изменение marker-end на marker-mid и
  • Изменение кода генерации пути для создания двух дуг (соединенных в середине) вместо одного.

Это потребует некоторой простой тригонометрии, такой как Superboggly, которая отображает подходящую среднюю точку, основанную на размере поклонения, которое вы хотите в своих линиях.

Ответ 2

Я OP, этот ответ - просто дополнение к превосходным ответам выше ради других с тем же вопросом.

Ответы показывают, как достичь этого для графика с дуги. Если у вас прямые ссылки, принятый ответ нужно немного изменить. Вот как это делается:

Ваши прямые ссылки, вероятно, реализованы с помощью line, их нужно преобразовать в polyline. Например:

// old: svg.selectAll(".link").data(links).enter().append("line")
svg.selectAll(".link").data(links).enter().append("polyline")

Затем мы должны закодировать полилинию в соответствии с этим , поэтому ваш исходный код, который кодирует line:

force.on("tick", function() {
   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;});

Изменяется на:

force.on("tick", function() {
   link.attr("points", function(d) {
      return d.source.x + "," + d.source.y + " " + 
             (d.source.x + d.target.x)/2 + "," + (d.source.y + d.target.y)/2 + " " +
             d.target.x + "," + d.target.y; });

И последнее, не забывайте преобразовать marker-end в marker-mid:

// old: link.attr("marker-end",
link.attr("marker-mid",

Кредиты для @Phrogz для показа способа.

Ответ 3

Я сделал несколько иной подход от Phrogz. Я попробовал удалить маркер пути и вместо этого нарисую его, используя новый путь, который идет к средней точке дуги и чей ход невидим. Вычисление средней точки немного затруднительно, поэтому, если вы хотите изменить характеристики дуги, вам может быть лучше подан подход Phrogz или какой-то гибрид.

В этом случае триггер не так уж плох, потому что если вы посмотрите внимательно, вы заметите, что дуга, используемая для генерации ссылок, связана с кругом с одинаковым радиусом и расстоянием между узлами. Таким образом, у вас есть равносторонний треугольник, и все, что вам нужно сделать, это вычислить расстояние, пройденное срединной точкой сегмента линии линии связи до средней точки дуги.

Мне кажется, мне нужна диаграмма для объяснения:

Find arc midpoint Таким образом, треугольник ABC является равносторонним и EC bisect AB. Так как у нас есть координаты для А и В, то найти M легко (усредним координаты). Это означает, что мы также знаем наклон от A до B (delta y/delta x), поэтому наклон от M до E является отрицательным обратным. Знание M и наклон к E означает, что мы почти там, нам просто нужно знать, как далеко продвинуться.

Для этого мы используем тот факт, что ACM представляет собой треугольник 30-60-90 и поэтому

| CM | = sqrt (3) * | AM |

но | AM | = | AB |/2 и | CE | = | AB |

Итак,

| ME | = | AB | - sqrt (3) * | AB |/2

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

В любом случае, поставив все вместе, вы получите:

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


... later in the tick handler

markerPath.attr("d", function(d) {
  var dx = d.target.x - d.source.x,
      dy = d.target.y - d.source.y,
      dr = Math.sqrt(dx * dx + dy * dy);

  // We know the center of the arc will be some distance perpendicular from the
  // link segment midpoint. The midpoint is computed as:
  var endX = (d.target.x + d.source.x) / 2;
  var endY = (d.target.y + d.source.y) / 2;

  // Notice that the paths are the arcs generated by a circle whose 
  // radius is the same as the distance between the nodes. This simplifies the 
  // trig as we can simply apply the 30-60-90 triangle rule to find the difference
  // between the radius and the distance to the segment midpoint from the circle 
  // center.
  var len = dr - ((dr/2) * Math.sqrt(3));

  // Remember that is we have a line slope then the perpendicular slope is the 
  // negative inverse.
  endX = endX + (dy * len/dr);
  endY = endY + (-dx * len/dr);

  return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + endX + "," + endY;
});

Проверьте здесь. Обратите внимание, что мой путь css для пути маркера установлен на прозрачный красный цвет, обычно вы хотите установить штрих в "none", чтобы скрыть строки.