gpt4 book ai didi

d3.js - 箭头不接触 d3.js 中的节点

转载 作者:行者123 更新时间:2023-12-05 01:01:40 25 4
gpt4 key购买 nike

我在 d3v4 中创建了力布局。链接上添加的标记以表示方向,如给定的 Jfiddle https://jsfiddle.net/rjyk72ea/ 中所示
要求是箭头应该与节点接触,但当链接在节点的对角线方向时,箭头隐藏在节点下方(部分或完全)。如何解决此问题?

var mark = diagramLayout.append("svg:defs").selectAll("marker")//
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", markerRefx)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.attr("stroke", "#000")
.attr("fill", "#000")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("stroke-width", "0.3px")
}

最佳答案

您的圆角方 block 有问题。您有两个选择:将它们视为圆圈并执行 as I did in this question .或者把它们当作长方形(正方形),找到与正方形的交点。既然,我已经给出了关于圆圈的先前答案,让我们来谈谈正方形(矩形)/线的交叉点。

现在,我不会详细介绍矩形/直线交叉点背后的数学原理(谷歌搜索的资源很多),所以让我们从 this great answer 中的函数开始(它值得更多的支持)并将其应用于您的问题。

首先,我将您的链接代码更改为使用 svg path 而不是 line。在我看来,只是更清洁、更容易。使用我在上面链接的函数变得很简单:

 function ticked(e) {

link.attr("d", function(d) {

var inter = pointOnRect(d.source.x, d.source.y,
d.target.x - 20, d.target.y - 20,
d.target.x + 20, d.target.y + 20);

return "M" + d.source.x + "," + d.source.y + "L" + inter.x + "," + inter.y;
});

....

这是完整的运行代码:

<!DOCTYPE html>
<html>

<head>
<script data-require="d3@4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}

.link {
stroke: #000;
stroke-opacity: .6;
}
</style>
</head>

<body>
<div id="mainScreen" style="height:100%;width:100%;position:absolute;">
<svg id="diagramLayout" style="height:100%;width:100%;position:absolute;"></svg>
</div>
<script>
var width = 500;
var height = 500;
var nodeWidth = 40;
var nodeHeight = 40;
var circleRadius = 5;
var diagramLayout;
var graphData = {
"nodes": [{
"uid": "Term20",
"name": "Term20",
"image": "images/Term.png"
}, {
"uid": "glossforArrow",
"name": "glossforArrow",
"image": "images/Glossary.png"
}, {
"uid": "Term43",
"name": "Term43",
"image": "images/Term.png"
}, {
"uid": "Term1",
"name": "Term43",
"image": "images/Term.png"
}, {
"uid": "Term2",
"name": "Term43",
"image": "images/Term.png"
}],
"links": [{
"source": "glossforArrow",
"target": "Term20",
"direction": "output",
"label": "Owned Terms"
}, {
"source": "glossforArrow",
"target": "Term43",
"direction": "output",
"label": "Owned Terms"
}, {
"source": "glossforArrow",
"target": "Term1",
"direction": "output",
"label": "Owned Terms"
}, {
"source": "glossforArrow",
"target": "Term2",
"direction": "output",
"label": "Owned Terms"
}]
};

forceInitialize(graphData)


function forceInitialize(graphData) {

diagramLayout = d3.select("#diagramLayout")
.attr("id", "diagramLayout") //set id
.attr("width", width) //set width
.attr("height", height) //set height
.append("g")
.attr("transform", "translate(" + 20 + "," + 20 + ")")

markerRefx = 35;

simulation = d3.forceSimulation();
alphaMulti = 1;
simulation.force("link", d3.forceLink().id(function(d) {
return d.uid;
}).distance(70).strength(0))
.force("charge", d3.forceManyBody().distanceMin(20).distanceMax(50))
.force("centre", d3.forceCenter(width / 2, height / 2))
.force("x", d3.forceX(2))
.force("y", d3.forceY(10))
.force("collide", d3.forceCollide().radius(function(d) {
return 80;
}).iterations(2))
simulation.on('end', function() {
simulation.force("link", d3.forceLink().id(function(d) {
return d.uid;
}).distance(30).strength(0.0).iterations(10))
.force("x", d3.forceX().strength(0))
.force("y", d3.forceX().strength(0))
});

force(graphData);
}


//Force Layout
function force(graphData) {

var linkEnter = diagramLayout.selectAll(".links");
linkEnter = linkEnter.data(graphData.links)
.enter().append("g")
.attr("class", "links")

var link = linkEnter.append("path")
.attr("stroke-width", function(d) {
return Math.sqrt(2);
})
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000")

graphData.links.forEach(function(d) {
if (d.direction == "input") {
var mark = diagramLayout.append("svg:defs").selectAll("marker") //
.data(["start"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 0)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.attr("stroke", "#000")
.attr("fill", "#000")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("stroke-width", "0.3px")
.attr("transform", "rotate(180,5, 0)");
} else if (d.direction == "output") {
var mark = diagramLayout.append("svg:defs").selectAll("marker") //
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 9)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.attr("stroke", "#000")
.attr("fill", "#000")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("stroke-width", "0.3px")
}
});

link.attr("marker-end", function(d) {
if (d.direction === "input")
return "";
else
return "url(#end)";
})

link.attr("marker-start", function(d) {
if (d.direction === "input")
return "url(#start)";
else
return "";
})

var node = diagramLayout.selectAll(".node");
node = node.data(graphData.nodes, function(d) {
return d.uid;
});

var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("height", nodeHeight)
.attr("width", nodeWidth)

var nodeIcon = nodeEnter.append("rect")
.attr("class", "rect")
.attr("x", -20)
.attr("y", -20)
.attr("rx", 10)
.attr("width", 40)
.attr("height", 40)
.attr("stroke-width", function(d) {
return Math.sqrt(2);
})
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000")
.attr("fill", "steelblue")
nodeIcon.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));

simulation
.nodes(graphData.nodes)
.on("tick", ticked);

setTimeout(function tick() {
simulation.tick();
if (simulation.alpha() >= .005);
setTimeout(tick, 0);
}, 0);

simulation.force("link")
.links(graphData.links);
simulation.restart();

function ticked(e) {

link.attr("d", function(d) {

var inter = pointOnRect(d.source.x, d.source.y,
d.target.x - 20, d.target.y - 20,
d.target.x + 20, d.target.y + 20);

return "M" + d.source.x + "," + d.source.y + "L" + inter.x + "," + inter.y;
})


nodeEnter.attr("transform", function(d) {
d.fixed = true;
return "translate(" + d.x + "," + d.y + ")";
});
}

function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}

function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}

function dragended(d) {
d3.select(this).classed("fixed", d.fixed = false);
d3.selectAll(".node").fixed = true;
}

/**
* Finds the intersection point between
* * the rectangle
* with parallel sides to the x and y axes
* * the half-line pointing towards (x,y)
* originating from the middle of the rectangle
*
* Note: the function works given min[XY] <= max[XY],
* even though minY may not be the "top" of the rectangle
* because the coordinate system is flipped.
*
* @param (x,y):Number point to build the line segment from
* @param minX:Number the "left" side of the rectangle
* @param minY:Number the "top" side of the rectangle
* @param maxX:Number the "right" side of the rectangle
* @param maxY:Number the "bottom" side of the rectangle
* @param check:boolean (optional) whether to treat point inside the rect as error
* @return an object with x and y members for the intersection
* @throws if check == true and (x,y) is inside the rectangle
* @author TWiStErRob
* @see <a href="https://stackoverflow.com/a/31254199/253468">source</a>
* @see <a href="https://stackoverflow.com/a/18292964/253468">based on</a>
*/
function pointOnRect(x, y, minX, minY, maxX, maxY, check) {
//assert minX <= maxX;
//assert minY <= maxY;
if (check && (minX <= x && x <= maxX) && (minY <= y && y <= maxY))
throw "Point " + [x, y] + "cannot be inside " + "the rectangle: " + [minX, minY] + " - " + [maxX, maxY] + ".";
var midX = (minX + maxX) / 2;
var midY = (minY + maxY) / 2;
// if (midX - x == 0) -> m == ±Inf -> minYx/maxYx == x (because value / ±Inf = ±0)
var m = (midY - y) / (midX - x);

if (x <= midX) { // check "left" side
var minXy = m * (minX - x) + y;
if (minY <= minXy && minXy <= maxY)
return {
x: minX,
y: minXy
};
}

if (x >= midX) { // check "right" side
var maxXy = m * (maxX - x) + y;
if (minY <= maxXy && maxXy <= maxY)
return {
x: maxX,
y: maxXy
};
}

if (y <= midY) { // check "top" side
var minYx = (minY - y) / m + x;
if (minX <= minYx && minYx <= maxX)
return {
x: minYx,
y: minY
};
}

if (y >= midY) { // check "bottom" side
var maxYx = (maxY - y) / m + x;
if (minX <= maxYx && maxYx <= maxX)
return {
x: maxYx,
y: maxY
};
}

// Should never happen :) If it does, please tell me!
throw "Cannot find intersection for " + [x, y] + " inside rectangle " + [minX, minY] + " - " + [maxX, maxY] + ".";
}

}
</script>
</body>

</html>

关于d3.js - 箭头不接触 d3.js 中的节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41500953/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com