2013-08-06 1 views
2

В настоящее время я реализую стрелки в своем силовом макете, как это делается в этом примере (http://bl.ocks.org/mbostock/1153292), и это отлично работает. Тем не менее, можно быстро понять, что расположение и размер стрелок жестко закодированы здесь, поскольку узлы никогда не меняют размер.Как я могу динамически изменять положение и размер стрелок направления на направленном макете d3.js?

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

Существует только одно сообщение, которое я нашел (linking nodes of variable radius with arrows), в котором говорится об этой проблеме. Однако на него не ответили, и ответ, который дает один плакат с краями, заканчивающимися радиусом узла, а не центром, не является тем, что я хочу сделать. Это потребовало бы постоянной перекомпоновки краевых позиций, которые, учитывая количество ребер, которые у меня есть, непрактичны.

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

То, что я пытаюсь (грубый пример, мой код немного сложнее)

var path = svg.append("svg:g").selectAll("path") 
    .data(force.links()) 
    .enter().append("svg:path") 
    .attr("class", function(d) { return d.target.nodeID; }); 

var circle = svg.append("svg:g").selectAll("circle") 
    .data(force.nodes()) 
     .enter().append("svg:circle") 
    .attr("r", nodeSize) //Dynamically determine size 
    .call(force.drag); 

// Per-node markers, as each node could potentially have a unique size 
svg.append("svg:defs").selectAll("marker") 
    .data(nodes, function(d) { return d.nodeID; }) 
    .enter().append("svg:marker") 
    .attr("id", function(d) { return d.nodeID; }) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", function(d) { return d.r; }) //Offset by the radius of the node 
    .attr("refY", 0) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("svg:path") 
    .attr("d", "M0,-5L10,0L0,5"); 

//Now that we know radius data for nodes, add the arrows in 
path.each(function(d) { 
    d.attr("marker-end", function(d) { return "url(#" + d.target.nodeID + ")"; }) 
}); 

Кто-нибудь есть идея о том, как лучше идти об этом? Заранее спасибо!

Обновление: по запросу, я создал jsfiddle (http://jsfiddle.net/herbstmb/j5wJ7/). Это базовый силовой макет, который я пытаюсь использовать для динамических размеров стрелок.

+0

Не можете ли вы превратить этот фрагмент кода в рабочий [скрипка] (http://jsfiddle.net/)? Похоже, что не так много, и было бы проще, если бы другие попытались найти решение. – Jake

+0

Я создал один. Проверьте обновление в сообщении. Это очень простой силовой макет, который я просто пытаюсь добавить стрелки с динамическим размером. –

+2

Консоль ошибки синтаксиса жалуется на предыдущую строку, где вы открываете строку с одной кавычкой и закрываете ее двойной кавычкой. Что касается вашей проблемы, я заметил, что вы сделали пару комментариев об отказе от потенциальных решений из-за потенциальных проблем производительности, запущенных на сотнях узлов. Вероятно, вы должны попросить простейшее решение и проверить его, прежде чем отбрасывать его. Вы будете удивлены, сколько вы можете сделать против пары сотен очков за долю секунды.В наши дни браузеры быстрые (даже мобильные, относительно говоря). –

ответ

3

Это старый вопрос, но вот мое решение. Идея состоит в том, чтобы нарисовать путь, соединяющий узлы таким образом, чтобы конечные точки находились на ребрах узлов, а не в центрах узлов. Начиная с мобильного патента Подходит пример (http://bl.ocks.org/mbostock/1153292), я заменил метод linkArc с:

function drawCurve(d) { 
    var sourceX = d.source.x; 
    var sourceY = d.source.y; 
    var targetX = d.target.x; 
    var targetY = d.target.y; 

    var theta = Math.atan((targetX - sourceX)/(targetY - sourceY)); 
    var phi = Math.atan((targetY - sourceY)/(targetX - sourceX)); 

    var sinTheta = d.source.r * Math.sin(theta); 
    var cosTheta = d.source.r * Math.cos(theta); 
    var sinPhi = d.target.r * Math.sin(phi); 
    var cosPhi = d.target.r * Math.cos(phi); 

    // Set the position of the link's end point at the source node 
    // such that it is on the edge closest to the target node 
    if (d.target.y > d.source.y) { 
     sourceX = sourceX + sinTheta; 
     sourceY = sourceY + cosTheta; 
    } 
    else { 
     sourceX = sourceX - sinTheta; 
     sourceY = sourceY - cosTheta; 
    } 

    // Set the position of the link's end point at the target node 
    // such that it is on the edge closest to the source node 
    if (d.source.x > d.target.x) { 
     targetX = targetX + cosPhi; 
     targetY = targetY + sinPhi;  
    } 
    else { 
     targetX = targetX - cosPhi; 
     targetY = targetY - sinPhi; 
    } 

    // Draw an arc between the two calculated points 
    var dx = targetX - sourceX, 
     dy = targetY - sourceY, 
     dr = Math.sqrt(dx * dx + dy * dy); 
    return "M" + sourceX + "," + sourceY + "A" + dr + "," + dr + " 0 0,1 " + targetX + "," + targetY; 
} 

Обратите внимание, что этот код ожидает «R», или радиуса, атрибут, чтобы быть в данных узлов. Это будет эквивалентно атрибуту size вашего jsfiddle. Если вы хотите нарисовать ссылки как прямые линии, вы можете вернуть эту строку вместо:

return "M" + sourceX + "," + sourceY + "L" + targetX + "," + targetY; 

Для размещения точек стрелок на правильных позициях, я изменил REFx и refY атрибуты так, что точка стрелки находится на краю узла:

svg.append("defs").selectAll("marker") 
    .data(["suit", "licensing", "resolved"]) 
    .enter().append("marker") 
    .attr("id", function(d) { return d; }) 
    .attr("viewBox", "0 -5 10 10") 
    .attr("refX", 10) 
    .attr("refY", 0) 
    .attr("markerWidth", 6) 
    .attr("markerHeight", 6) 
    .attr("orient", "auto") 
    .append("path") 
    .attr("d", "M0,-5L10,0L0,5"); 

 Смежные вопросы

  • Нет связанных вопросов^_^