我已經構建了一個節制指向圖,但是,在我的情況下,我也有一個80px * 80px的網格框。我希望圖中的每個節點不僅可以根據現有的重力和力量進行定位,而且可以在最近的網格框中間定位(不固定)。 在d3js中可以這樣做嗎?D3節點位於網格中的節點佈局圖
4
A
回答
1
您要申請自定義部隊
force.on("tick", function() {
node.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; });
});
所以沒有內置的方式做這樣的事情......
所以你的情況,你必須要找到接近電網盒子中心並且使用節點和盒子中心之間的距離以及一些重力方程來計算x和y值。
在你的情況下
node
.attr("cx", function(d) {
d.x += f(d).x;
return d.x;
})
.attr("cy", function(d) {
d.y += f(d).y;
return d.y;
});
其中f(d)
是你的重力的矢量取決於箱子中心和實際節點d
之間的距離。例如
var blackHole = function (d) {
var gc = {
x: 100,
y: 100
};
var k = 0.1;
var dx = gc.x - d.px;
var dy = gc.y - d.py;
return {
x: k * dx,
y: k * dy
};
};
這是很難找到出f(d)
其真正起作用由多個重心,所以我建議你閱讀有關這種力量的算法。我嘗試了一些有趣的例子,但是沒有一個能按照你想要的方式工作。 ;-)
至少現在:
var grid = function (d) {
var fx = d.px % 100;
if (fx < 0)
fx += 100;
if (fx > 50)
fx -= 100;
var fy = d.py % 100;
if (fy < 0)
fy += 100;
if (fy > 50)
fy -= 100;
var k = -1;
return {
x: k * fx,
y: k * fy
};
};
這是一個100px的密集網格非常簡單的力量......但我猜的結果不是你所期望的,節點可以重疊,因爲力佈局只有具有共同鏈接的節點相互排斥,至少這是我的經驗(編輯:這是因爲負電荷)...我認爲使用d3四邊形構建自定義佈局佈局會容易得多...
2
Moritz Stefaner想出了一個辦法來做到這一點
code:https://github.com/moritzstefaner/gridexperiments/
演示:http://moritzstefaner.github.io/gridexperiments/
編輯:
由@altocumulus提到,這並沒有代碼的副本。通常我只複製個人網站的代碼,因爲它們比github上的東西更容易消失。或者我會在短時間內複製它(少於50個loc?)。無論如何,由於代碼的肉可能會被拉出,我已經複製下面的mortiz的index.html文件。其他引用的js文件可以很容易地在別處找到。 (只是注意,你應該拉每個庫的版本爲12月9日的,2011)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<meta charset="utf-8">
<title>Forces and grids</title>
<script type="text/javascript" src="d3.min.js"></script>
<script type="text/javascript" src="d3.layout.min.js"></script>
<script type="text/javascript" src="d3.geom.min.js"></script>
<script type="text/javascript" src="underscore-min.js"></script>
<script src="jquery-1.7.2.min.js" charset="utf-8"></script>
<style type="text/css" media="screen">
.menu { position:absolute; top :20px; right:20px; }
</style>
</head>
<body>
<script type="text/javascript" charset="utf-8">
var w = 700, h = 700;
var vis = d3.select("body").append("svg:svg").attr("width", w).attr("height", h);
var background = vis.append("g");
var nodes = [];
var links = [];
var USE_GRID = true;
var GRID_SIZE = 60;
var GRID_TYPE = "HEXA";
// set up event handlers
$(document).ready(function(){
$("#USE_GRID").click(
function(){
USE_GRID = $(this).is(":checked");
$(this).blur();
force.start();
}
);
//$("#CELL_SIZE").rangeinput();
$("#CELL_SIZE").bind("change",
function(){
console.log($(this).attr("value"));
GRID_SIZE = $(this).attr("value");
grid.init();
force.start();
}
);
$("[name=GRID_TYPE]").click(
function(){
GRID_TYPE = $(this).attr("value");
grid.init();
force.start();
}
);
});
for(var i = 0; i < 30; i++) {
var node = {
label : "node " + i
};
nodes.push(node);
};
for(var i = 0; i < nodes.length; i++) {
for(var j = 0; j < i; j++) {
if(Math.random() > .99-Math.sqrt(i)*.02)
links.push({
source : i,
target : j,
weight :1
});
}
};
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links).gravity(1).linkDistance(function(d){return (1-d.weight)*100}).charge(-3000).linkStrength(function(x) {
return x.weight * 5
});
force.start();
var link = vis.selectAll("line.link").data(links).enter().append("svg:line").attr("class", "link").style("stroke-width", 1.5).style("stroke", "#555").style("opacity", function(d){return d.weight*.7});
var node = vis.selectAll("g.node").data(force.nodes()).enter().append("svg:g").attr("class", "node");
node.append("svg:circle").attr("r", 6).style("fill", "#555").style("stroke", "#FFF").style("stroke-width", "4px");
node.call(force.drag);
var updateLink = function() {
this.attr("x1", function(d) {
return d.source.screenX;
}).attr("y1", function(d) {
return d.source.screenY;
}).attr("x2", function(d) {
return d.target.screenX;
}).attr("y2", function(d) {
return d.target.screenY;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
if(USE_GRID) {
var gridpoint = grid.occupyNearest(d);
if(gridpoint) {
d.screenX = d.screenX || gridpoint.x;
d.screenY = d.screenY || gridpoint.y;
d.screenX += (gridpoint.x - d.screenX) * .2;
d.screenY += (gridpoint.y - d.screenY) * .2;
d.x += (gridpoint.x - d.x) * .05;
d.y += (gridpoint.y - d.y) * .05;
}
} else {
d.screenX = d.x;
d.screenY = d.y;
}
return "translate(" + d.screenX + "," + d.screenY + ")";
});
};
var grid = function(width, height) {
return {
cells : [],
init : function() {
this.cells = [];
for(var i = 0; i < width/GRID_SIZE; i++) {
for(var j = 0; j < height/GRID_SIZE; j++) {
// HACK: ^should be a better way to determine number of rows and cols
var cell;
switch (GRID_TYPE) {
case "PLAIN":
cell = {
x : i * GRID_SIZE,
y : j * GRID_SIZE
};
break;
case "SHIFT_ODD_ROWS":
cell = {
x : i * GRID_SIZE,
y : 1.5 * (j * GRID_SIZE + (i % 2) * GRID_SIZE * .5)
};
break;
case "HEXA":
cell = {
x : i * GRID_SIZE + (j % 2) * GRID_SIZE * .5,
y : j * GRID_SIZE * .85
};
break;
}
this.cells.push(cell);
};
};
},
sqdist : function(a, b) {
return Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2);
},
occupyNearest : function(p) {
var minDist = 1000000;
var d;
var candidate = null;
for(var i = 0; i < this.cells.length; i++) {
if(!this.cells[i].occupied && (d = this.sqdist(p, this.cells[i])) < minDist) {
minDist = d;
candidate = this.cells[i];
}
}
if(candidate)
candidate.occupied = true;
return candidate;
}
}
}(w, h);
force.on("tick", function() {
vis.select("g.gridcanvas").remove();
if(USE_GRID) {
grid.init();
var gridCanvas = vis.append("svg:g").attr("class", "gridcanvas");
_.each(grid.cells, function(c) {
gridCanvas.append("svg:circle").attr("cx", c.x).attr("cy", c.y).attr("r", 2).style("fill", "#555").style("opacity", .3);
});
}
node.call(updateNode);
link.call(updateLink);
});
</script>
<div class="menu">
<div>
<input type="checkbox" id="USE_GRID" checked>use grid</input>
</div>
<div>
<input type="range" min="30" step="10" max="150" id="CELL_SIZE" value="60"></input>
</div>
<div>
<input type="radio" name="GRID_TYPE" value="PLAIN">plain</input>
<input type="radio" name="GRID_TYPE" value="SHIFT_ODD_ROWS">Shift odd rows</input>
<input type="radio" name="GRID_TYPE" value="HEXA" checked>Hexa</input>
</div>
</div>
</body>
相關問題
- 1. D3節點佈局的節點大小
- 2. d3強制佈局 - 定位節點
- 3. D3 Force佈局圖 - 自連接節點
- 4. 更新節點'D3強制佈局中的子元素位置
- 5. d3.js:強制佈局中的建議節點位置
- 6. d3強制佈局鏈接在節點中的位置
- 7. D3 v4 force佈局中節點和鏈接位置的轉換
- 8. 如何在D3中組織節點位置Force佈局
- 9. D3 JS - 強制圖 - 刪除節點後不重新啓動佈局節點
- 10. javafx佈局中的節點
- 11. D3強制佈局應在點擊節點時添加節點和鏈接
- 12. 固定節點位置在D3力導向佈局
- 13. D3強制定向佈局+所有節點到原始位置
- 14. D3選擇基於節點
- 15. D3樹/節點
- 16. 驗證點是否位於指定節點的網格
- 17. D3樹圖節點順序
- 18. 在d3中計算節點的佈局深度
- 19. 如何刪除D3強制佈局中的節點?
- 20. 刪除從D3列表中的節點,並重新佈局
- 21. D3強制佈局修復中心的根節點
- 22. cytoscape.js佈局複合節點
- 23. D3js力佈局 - 與節點
- 24. 節點紅色UI佈局
- 25. 節點和子節點在D3.js
- 26. D3:問題與節點= vis.selectAll(「。節點」)
- 27. D3.js - 用畫布縮放網絡圖,然後拖動節點
- 28. 從d3js樹佈局中刪除節點及其子節點
- 29. 刪除並添加節點,點擊d3.js中的節點
- 30. 在d3中拖動多個節點強制定向佈局
這是一個非常好的例子!但是,由於鏈接唯一的答案被視爲VLQ,因此在SO上皺起眉頭,你是否介意至少分享一些代碼細節和見解? – altocumulus 2017-06-13 10:03:07
@altocumulus - 我從github複製了核心代碼。我已經對它的工作原理做了任何真正的分析,只是它應該與OP所期望的結果緊密匹配。 – viggity 2017-06-13 18:06:18