chart = {
const height = width * 0.6;
const bounds = [0, 0, width, height];
const svg = d3.create("svg")
.attr("width", bounds[2])
.attr("height", bounds[3])
.attr("viewBox", bounds)
.style('background-color','#333333');
var line = d3.line().curve(d3.curveBasisClosed);
var points = randomPointsNormal(nCells, width/2, width/4, height/2, height/4);
points = relax(points, bounds, 1);
const delaunay = d3.Delaunay.from(points);
const voronoi = delaunay.voronoi(bounds);
const cells = svg.append('g')
const g = cells.selectAll("g")
.data([... voronoi.cellPolygons()].map(d => subdivide(d.slice(0, -1)).slice(0, -1)))
.join(
(enter) => {
const g = enter.append("g");
g.append("path")
.attr('class', 'cellWall')
.attr("d", (d, i) => line(contractDistance(d, points[i], Math.sqrt(-d3.polygonArea(d)) / 40)))
.attr("stroke", d3.color("#E0E4CC").darker())
.attr("fill","#E0E4CC")
g.append("path")
.attr('class', 'plasma')
.attr("d", (d, i) => line(contractDistance(d, points[i], Math.sqrt(-d3.polygonArea(d)) / 40 + 10)))
.attr("stroke","#A7DBD8")
.attr("fill","#A7DBD8")
g.append("circle")
.attr('class', 'nucleus')
.attr("cx", (d, i) => points[i][0])
.attr("cy", (d, i) => points[i][1])
.attr("r", (d, i) => Math.sqrt(-d3.polygonArea(d)) / 10)
.attr("stroke","#F38630")
.attr("stroke", d3.color("#F38630").darker())
.attr("fill","#F38630")
return g;
},
(update) => {
update.select('.cellWall')
.attr("d", d =>line(d))
.attr("stroke","#E0E4CC")
.attr("fill","#E0E4CC");
update.select('.plasma')
.attr("d", (d, i) => line(contractDistance(d, points[i], 10)))
.attr("stroke","#A7DBD8")
.attr("fill","#A7DBD8")
update.select('.nucleus')
.attr("cx", (d, i) => points[i][0])
.attr("cy", (d, i) => points[i][1])
.attr("r", (d, i) => Math.sqrt(-d3.polygonArea(d)) / 10)
.attr("stroke", d3.color("#F38630").darker())
.attr("fill","#F38630");
return update;
},
(exit) => exit.remove()
)
return svg.node()
}