Skip to content

Instantly share code, notes, and snippets.

@ashnur
Last active November 18, 2018 21:21
Show Gist options
  • Save ashnur/147e70c9e9e60aeb9b831362f40707f8 to your computer and use it in GitHub Desktop.
Save ashnur/147e70c9e9e60aeb9b831362f40707f8 to your computer and use it in GitHub Desktop.
prime factors
license: mit
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://gistcdn.githack.com/ashnur/231249ade434e9bb660836ab8f636c2f/raw/5d71f0198cbcb50dbe06d3ee4009f0b189716b16/numbers.js" ></script>
<script src="https://gistcdn.githack.com/ashnur/4962b2fd6b64143da75a71f980758b48/raw/15071fc25e22a21657e7464eedf1af63e387e237/dimensions.js"></script>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
</style>
</head>
<body>
<canvas></canvas>
<script>const canvas = d3
.select('canvas')
.attr('width', ds.width)
.attr('height', ds.height)
const context = canvas.node().getContext('2d')
context.font = '5px Fira Code'
let integers = []
let links = []
let primes = []
let nodes = []
let loops = {}
let nscale = 13333
const simulation = d3.forceSimulation()
const byIndex = (idx) => (el) => {
return el.p === (idx.p || idx)
}
canvas.call(
d3
.drag()
.container(canvas.node())
.subject(dragsubject)
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended),
)
const add = (a, b) => a + b
// const size = (n) => Math.min(n.lc ** 0.7, 50)
// const size = (n) => (n.lc / nscale) * ((ds.width * ds.height) / nscale) ** 0.8
const angleScale = d3
.scaleLog()
.domain([2, 30000])
.range([0, 360])
const xScale = d3
.scaleLog()
.domain([2, 30000]) // 1499
.range([ds.hw, 0])
const yScale = d3
.scaleLog()
.domain([2, 30000]) // 1499
.range([ds.hh, 0])
const sizeScale = d3
.scaleLog()
.domain([1, 30000])
.range([0, 30])
const strengthScale = d3
.scaleLinear()
.domain([1, 30000])
.range([0, 0.6])
const linkScale = d3
.scaleLog()
.domain([1, 30000])
.range([1, 30])
const lengthScale = d3
.scaleLog()
.domain([30000, 1])
.range([1, 200])
const size = (n) => {
return n.lc
}
let g = 100
let temp_links = []
numbers.forEach((factors) => {
if (!integers.includes(factors[0].i)) integers.push(factors[0].i)
temp_links.push.apply(temp_links, factors)
factors.forEach((link) => {
const { i, source, target, rank } = link
if (!primes.includes(source)) primes.push(source)
if (!primes.includes(target)) primes.push(target)
if ((source === target && loops[source] == null) || loops[source] < rank) loops[source] = rank
})
})
for (let k = 0; k < temp_links.length; k++) {
let idx = links.findIndex(({ source, target }) => {
return source === temp_links[k].source && target === temp_links[k].target
})
if (idx === -1) {
links.push({ ...temp_links[k], lc: 1 })
} else {
links[idx].lc++
}
}
nodes = primes.map((p) => {
return {
p,
lc: links
.filter(({ source, target }) => source === p || target === p)
.map(({ lc }) => lc)
.reduce(add, 0),
}
})
nodes.forEach((n, i) => {
let angle = angleScale(n.lc + n.p)
let x = ds.center.x + xScale(n.lc) * Math.cos(angle)
let y = ds.center.y + yScale(n.lc) * Math.sin(angle)
// if (n.lc === 1 && --g > 0) console.log(n, x, Math.cos(angle))
nodes[i].x = x
nodes[i].y = y
nodes[i].ls = linkScale(n.lc)
nodes[i].lc = sizeScale(n.lc)
})
links.forEach((l, i) => {
links[i].ls = lengthScale(l.lc)
links[i].lc = strengthScale(l.lc)
})
const distance = d3
.forceCollide()
.radius(size)
.strength(1)
const link = d3
.forceLink(links)
.iterations(1)
.id((x) => x.p)
.distance((l) => l.ls)
.strength((l) => {
return l.lc
})
simulation
.nodes(nodes)
.on('tick', ticked)
.force('distance', distance)
// .force('descendants', d3.forceManyBody().strength(0.000001))
.force('boxed', limitToScreen)
.force('link', link)
// .force('center', d3.forceCenter(ds.hw, ds.hh))
.restart()
function ticked() {
context.clearRect(0, 0, ds.width, ds.height)
context.globalAlpha = 0.3
context.beginPath()
links.forEach(drawLink)
context.lineWidth = 0.3
context.strokeStyle = '#666'
context.stroke()
context.beginPath()
context.globalAlpha = 1
nodes.forEach(drawNode)
context.globalAlpha = 0.09
// context.fillStyle = d3.interpolateViridis(0.3)
context.fill()
}
function drawNode(d) {
context.moveTo(d.x, d.y)
context.arc(d.x, d.y, size(d), 0 * Math.PI, 2 * Math.PI)
context.fillText(d.p, d.x, d.y)
}
function drawLink(link) {
const s = nodes.find(byIndex(link.source))
context.moveTo(s.x, s.y)
if (link.target === link.source) {
const r = loops[link.source]
// context.arc(s.x, s.y, (30 * r) / links.length, 1 * Math.PI, 2 * Math.PI)
} else {
const t = nodes.find(byIndex(link.target))
context.lineTo(t.x, t.y)
}
// context.arc(s.x + (t.x - s.x) / 2, s.y, Math.abs(t.x - s.x) / 2, 1 * Math.PI, 2 * Math.PI)
}
function dragsubject() {
return simulation.find(d3.event.x, d3.event.y)
}
function dragstarted() {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
d3.event.subject.fx = d3.event.subject.x
d3.event.subject.fy = d3.event.subject.y
}
function dragged() {
d3.event.subject.fx = d3.event.x
d3.event.subject.fy = d3.event.y
}
function dragended() {
if (!d3.event.active) simulation.alphaTarget(0)
d3.event.subject.fx = null
d3.event.subject.fy = null
}
function moveBack(node, i) {
const dx = node.x / ds.width - 1 / 2
const dy = node.y / ds.height - 1 / 2
if (Math.random() < Math.pow(dx / 0.5, 40)) {
node.vx = -dx * 3
}
if (Math.random() < Math.pow(dy / 0.5, 40)) {
node.vy = -dy * 3
}
}
function limitToScreen() {
nodes.forEach(moveBack)
}
</script>
</body>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment