chart = {
const height = width * 9/16
const margin = 15
const svg = d3.select(DOM.svg(width, height))
const r = 15
const imageSize = r * 2
const strokeWidth = 2
const xScale = d3.scaleLinear()
.range([margin, width - margin])
.domain(d3.extent(pokemonBaseStats.map(d => d.x)))
const yScale = d3.scaleLinear()
.range([margin, height - margin])
.domain(d3.extent(pokemonBaseStats.map(d => -d.y)))
const rScale = d3.scaleLog()
.range([0, r])
.domain(d3.extent(pokemonBaseStats.map(d => d.base_total)))
const data = pokemonBaseStats.map(d => ({...d, x: xScale(d.x), y: yScale(-d.y), xtsne: xScale(d.x), ytsne: yScale(-d.y)}))
const g = svg.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('transform', d => `translate(${xScale(d.x)}, ${yScale(d.y)})`)
const title = g
.append('title')
.text(d => `Name: ${d.name}(${d.pokedex_number})
Attack: ${d.attack}
Defense: ${d.defense}
S.Att: ${d.sp_attack}
S.Def: ${d.sp_defense}
Speed: ${d.speed}
HP: ${d.hp}
Base: ${d.base_total}`)
const pokeballs = g
.append('use')
.attr('href', d => +d.is_legendary ? '#ultraball' : '#pokeball')
.attr('transform', d => `scale(${rScale(d.base_total) / 50})`)
.attr('opacity', 0.7)
const images = g
.append('image')
.attr('href', d => getPokemonPNG(d.pokedex_number))
.attr('height', d => rScale(d.base_total) * 2)
.attr('width', d => rScale(d.base_total) * 2)
const strengthScale = d3.scaleLog()
.range([0.1, 0.1])
.domain([0, d3.max(data, d => d.base_total)])
let tick = 0
const simulation = d3.forceSimulation(data)
.force('collide', d3.forceCollide().radius(d => rScale(d.base_total)))
.force('x', d3.forceX().x(d => d.xtsne))
.force('y', d3.forceY().y(d => d.ytsne))
.on('tick', () => {
data.forEach(d => {
const r = rScale(d.base_total)
d.x = Math.max(r + margin, Math.min(width - margin - r, d.x))
d.y = Math.max(r + margin, Math.min(height - margin - r, d.y))
})
g.attr('transform', d => `translate(${d.x}, ${d.y})`)
tick++
if (tick > 100)
simulation.stop()
})
return svg.node()
}