我正在研究d3.js力图,我有一个问题。 是否可以在具有某些坐标的三角形内制作力图?
这是我的代码:
var width = 500;
var height = 500;
//margin
var marginLeft = 10;
var marginTop = 10;
var marginRight = 10;
var marginBottom = 10;
var margin = { left: marginLeft , top: marginTop, right: marginRight, bottom: marginBottom};
//size of canvas
var innerWidth = width - margin.left - margin.right;
var innerHeight = height - margin.top - margin.bottom;
var radius = 10;
var svg = d3.select(".forcechart").append("svg")
.attr("width", width)
.attr("height", height)
.style("background", "#eee");
var tr = svg.append("polygon") // attach a polygon
.style("stroke", "black") // colour the line
.style("fill", "none") // remove any fill colour
.attr("points", "250,0, 12,173, 250,250"); // x,y points
var group = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var graph = {
"nodes": [ { "x": 0, "y": 0 },
{ "x": 100, "y": 100 },
{ "x": 500, "y": 500 },
{ "x": 300, "y": 0 },
{ "x": 300, "y": 0 },
{ "x": 100, "y": 100 },
{ "x": 500, "y": 500 },
{ "x": 300, "y": 0 },
{ "x": 300, "y": 0 },
{ "x": 100, "y": 100 },
{ "x": 500, "y": 500 },
{ "x": 300, "y": 0 },
{ "x": 300, "y": 0 },
{ "x": 100, "y": 100 },
{ "x": 500, "y": 500 },
{ "x": 300, "y": 0 },
{ "x": 300, "y": 0 },
{ "x": 100, "y": 100 },
{ "x": 500, "y": 500 },
{ "x": 300, "y": 0 },
{ "x": 300, "y": 0 },
],
"links": []
};
var nodes = graph.nodes,
links = graph.links;
var force = d3.layout.force()
.size([innerWidth, innerHeight])
.nodes(nodes)
.links(links);
force.linkDistance(100);
force.charge(-200);
var link = group.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = group.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node');
force.on('tick', function() {
node.attr('r', radius)
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
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.start();
完整代码在此处:http://codepen.io/Balzzac/pen/vGWXdQ。现在它是" group"里面的力图,我需要让它在三角形里面" tr"所以没有一个节点在我的三角形的边界之外。
感谢您的帮助!
PS抱歉我的英文=)
答案 0 :(得分:3)
这个问题确实有两个部分。首先,您需要使用力布局来收敛于默认的width/2, height/2不同的焦点。这个新的焦点应该是三角形的质心。幸运的是d3
有a helper method来计算多边形。其次,既然我们正在收敛三角形的质心,我们如何将节点绑定在该三角形内。我在下面使用的方法计算从质心到节点绘制的线与三角形边缘线之间的交点(从此question计算的交点)。所有3条边上没有交点意味着圆形在三角形中,任何边上的交点意味着我们需要将圆圈带到该边缘。
更新 - 稍微概括了代码并将其转换为block here以使用N边多边形。
让我们来看看代码:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
</head>
<body>
<script>
var width = 500,
height = 500,
radius = 10;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.style("background", "#eee");
// our polygon
var trianglePoints = [
[250, 0],
[12, 173],
[250, 250]
];
var tr = svg.append("polygon") // attach a polygon
.style("stroke", "black") // colour the line
.style("fill", "none") // remove any fill colour
.attr("points", trianglePoints.join(" ")); // x,y points
var group = svg.append("g");
var nodes = d3.range(20).map(function(d){ return {} }),
links = [],
cent = d3.geom.polygon(trianglePoints).centroid();
var force = d3.layout.force()
.size([width, height])
.nodes(nodes)
.links(links);
force.linkDistance(100);
force.charge(-200);
var link = group.selectAll('.link')
.data(links)
.enter().append('line')
.attr('class', 'link');
var node = group.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node')
.call(force.drag); //<-- make them draggable to test
force.on('tick', function(e) {
node.attr('r', radius)
.attr('transform', function(d) {
// change focus to the center of the triangle
var x = (d.x - (width / 2 - cent[0])),
y = (d.y - (height / 2 - cent[1]));
// test intersections on all 3 edges
var i =
getLineIntersection(trianglePoints[0][0], trianglePoints[0][1],
trianglePoints[1][0], trianglePoints[1][1], cent[0], cent[1], x, y) ||
getLineIntersection(trianglePoints[1][0], trianglePoints[1][1],
trianglePoints[2][0], trianglePoints[2][1], cent[0], cent[1], x, y) ||
getLineIntersection(trianglePoints[0][0], trianglePoints[0][1],
trianglePoints[2][0], trianglePoints[2][1], cent[0], cent[1], x, y) ||
false;
// set it to intersection
if (i){
x = i.x;
y = i.y;
}
return "translate(" + x + "," + y + ")";
});
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;
});
});
// from https://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
function getLineIntersection(p0_x, p0_y, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y) {
var s1_x, s1_y, s2_x, s2_y;
s1_x = p1_x - p0_x;
s1_y = p1_y - p0_y;
s2_x = p3_x - p2_x;
s2_y = p3_y - p2_y;
var s, t;
s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y);
t = (s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y);
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
var intX = p0_x + (t * s1_x);
var intY = p0_y + (t * s1_y);
return {
x: intX,
y: intY
};
}
return false;
}
force.start();
</script>
</body>
</html>