我想创建我认为最能描述为力导向直方图(或点/气泡直方图)或y值沿x轴约束的蜂群。
也就是说,使用力导向布局,根据一些x值分配x位置,并根据该值的计数分配y位置,其中y值必须保持在某个较低边界之上。
我创建了几乎是我想要的force-directed beeswarm plot。我还没有弄清楚y轴约束。
感谢您的帮助/建议!
最佳答案
要应用这样的约束,您可以直接限制d.y
值-只需替换
.attr('cy', d => d.y)
例如
.attr('cy', d => d.y = Math.min(d.y, height / 2))
这样,节点被强制高于
height / 2
。重要的是不仅要约束cy
,而且还要更新d.y
,以便在下次迭代时考虑到这个新位置。如果您不希望在return语句中进行赋值,则可以使用d.y
运算符分别修改each
。forceCenter
在这里可能会使事情变得混乱,我不确定是否有必要。我也怀疑的必要性-为什么各圈必须互相吸引(您使用积极力量=吸引力)?最好用轴(manyBody
)吸引它们。在圆的精确
forceY
位置,x轴的吸引力和收敛速度(迭代)之间存在折衷。可能需要对力量进行一些调整。强度的影响在x
自述文件:https://github.com/d3/d3-force#x_strength中进行了描述。对于大多数力,建议使用[0,1]范围内的强度。const width = 500
const height = 150
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
const colorScale = d3.scaleOrdinal()
.range(['#FCFCFC', '#F7567C', '#FFFAE3', '#99E1D9', '#5D576B'])
const radius = 7
const sampleData = d3.range(150).map(() => ({r: radius,
value: width/2 + d3.randomNormal(0,75)()}))
// set params for force layout
//const manyBody = d3.forceManyBody().strength(2)
//const center = d3.forceCenter().x((width/2)).y((height/2))
// define force
let force = d3.forceSimulation()
//.force('charge', manyBody)
//.force('center', center)
.force('collision', d3.forceCollide(d => d.r).strength(1))
.velocityDecay(.48)
.force('x', d3.forceX(d => d.value).strength(3))
.force('y', d3.forceY(height - radius).strength(0.2))
.nodes(sampleData)
.on('tick', changeNetwork)
svg.selectAll('circle')
.data(sampleData)
.enter()
.append('circle')
.style('fill', (d,i) => colorScale(i))
.attr('r', d => d.r)
.attr('stroke', 'black')
.attr('stroke-width', .1)
function changeNetwork() {
d3.selectAll('circle')
.attr('cx', d => d.x)
.attr('cy', d => d.y = Math.min(d.y, height - radius - 1))
}
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>