我想创建我认为最能描述为力导向直方图(或点/气泡直方图)或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>

10-06 00:03