final = {
const svg = this || html`<svg width=${width} height=${width}></svg>`
mutable graph = calculateGraph(graph)
const link = d3.select(svg).selectAll('.link')
.data(graph.links, d => d.id)
.join('line')
.classed('link', true)
.attr('stroke', '#ccc')
.attr('opacity', 0.5)
const flower = d3.select(svg).selectAll('.flower')
.data(graph.nodes, d => d.title)
.join(
enter => {
const g = enter.append('g')
.classed('flower', true)
g.selectAll('path')
.data(d => _.times(d.numPetals, i =>
Object.assign({}, d, {rotate: i * (360 / d.numPetals)})))
.join('path')
.attr('transform', d => `rotate(${d.rotate})scale(${0.5 * d.scale})`)
.attr('fill-opacity', 0.5)
.attr('d', d => d.path)
.attr('fill', d => d.color)
.attr('stroke', d => d.color)
g.append('text')
.attr('text-anchor', 'middle')
.attr('dy', '.35em')
.style('font-size', '.5em')
.style('font-style', 'italic')
.text(d => _.truncate(d.title, {length: 20}))
return g
}
)
const genre = d3.select(svg).selectAll('.genre')
.data(graph.genres, d => d.label)
.join('text')
.classed('genre', true)
.attr('text-anchor', 'middle')
.attr('dy', '.35em')
.text(d => d.label)
const nodes = _.union(graph.nodes, graph.genres)
const simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody(-300))
.force('collide', d3.forceCollide(d => 150 * d.scale || 50))
.force('link', d3.forceLink(graph.links).distance(100))
.force('center', d3.forceCenter(width / 2, width / 2))
.alpha(0.5).alphaMin(0.1)
.on('tick', () => {
console.log('tick')
flower.attr('transform', d => `translate(${d.x}, ${d.y})`)
genre.attr('transform', d => `translate(${d.x}, ${d.y})`)
link.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y)
})
invalidation.then(() => simulation.stop())
return svg
}