svg d3.js

svg - Obtener puntas de flecha para apuntar al borde exterior del nodo en D3



d3.js (2)

Contesté la misma pregunta aquí . La respuesta utiliza la matemática vectorial, también es bastante útil para otros cálculos.

Soy nuevo en D3 y estoy tratando de crear una visualización de red interactiva. He copiado gran parte de this ejemplo, pero he cambiado las líneas curvas a líneas rectas mediante el uso de "líneas" SVG en lugar de "rutas", y también he escalado los nodos según los datos que representan. El problema es que mis puntas de flecha (creadas con marcadores SVG) están al final de las líneas. Como algunos de los nodos son grandes, las flechas se ocultan detrás de ellos. Me gustaría que mis puntas de flecha aparezcan justo en el borde exterior del nodo al que apuntan.

Así es como estoy creando los marcadores y enlaces:

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

Noté que el atributo "refX" especifica qué tan lejos del final de la línea debería aparecer la punta de flecha. ¿Cómo puedo hacer que esto dependa del radio del nodo al que apunta? Si no puedo hacer eso, ¿podría cambiar los puntos finales de las líneas? Supongo que lo haría en esta función, que restablece los puntos finales de las líneas a medida que todo se mueve:

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

¿Qué enfoque tiene más sentido y cómo lo implementaría?


Gracias Lars Kotthoff, ¡conseguí que esto funcionara siguiendo los consejos de la otra pregunta! Primero cambié de usar líneas a caminos. No creo que realmente tuviera que hacer eso, pero hizo más fácil seguir los otros ejemplos que estaba viendo porque usaban caminos.

Luego, agregué un campo de "radio" a mis nodos. Simplemente hice esto cuando establecí el atributo de radio, agregándolo como un campo real en lugar de devolver el valor de inmediato:

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;

Luego edité mi función tick () para tener en cuenta este radio. Esto requería un poco de geometría simple ...

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

Básicamente, el triángulo formado por la ruta, su cambio total de x (diffX), y su cambio total de y (diff) es un triángulo similar al formado por el segmento de la ruta dentro del nodo objetivo (es decir, el radio del nodo), el x cambio dentro del nodo objetivo (offsetX), y el cambio dentro del nodo objetivo (offsetY). Esto significa que la relación entre el radio del nodo objetivo y la longitud total de la trayectoria es igual a la relación de offsetX a diffX y a la relación de offsetY a diffY.

También cambié el valor de refX a 10 para las flechas. No estoy seguro de por qué era necesario, ¡pero ahora parece funcionar!