Public
Edited
Nov 24, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
origin = [500, 700]
Insert cell
Insert cell
chart = {
let mx, my, mouseX = 0, mouseY = 0
let beta = startY, alpha = startX
const drag = d3.drag().on('drag', dragged).on('start', (evt) => {
mx = evt.x
my = evt.y
}).on('end', (evt) => {
mouseX = evt.x - mx + mouseX
mouseY = evt.y - my + mouseY
})
const svgNode = d3.select(DOM.svg(width, 900))
const grid3d = d33d._3d().shape('GRID', 61).origin(origin).scale(scale).rotateY(beta).rotateX(alpha)
const yScale3d = d33d._3d().shape('LINE_STRIP').origin(origin).scale(scale).rotateY(beta).rotateX(alpha)
const point3d = d33d._3d().origin(origin).scale(scale).rotateY(beta).rotateX(alpha).x((d) => d.s).y((d) => d.r).z((d) => d.p)
const key = (d) => d.id

const svg = svgNode.call(drag).append('g')

const render = () => {
const xGrid = svg.selectAll('path.grid').data(grid3d.rotateY(beta).rotateX(alpha)(grid), key)
xGrid.enter().append('path').attr('class', '_3d grid').merge(xGrid)
.attr('stroke', 'black')
.attr('stroke-width', 0.08)
.attr('fill', '#ffffff')
.attr('fill-opacity', 0.2)
.attr('d', grid3d.draw)
xGrid.exit().remove()

const points = svg.selectAll('circle').data(point3d.rotateY(beta).rotateX(alpha)(scatter), key)
points.enter().append('circle').attr('class', '_3d').merge(points)
.attr('r', 3)
.attr('fill', (d) => {
if (d.total < 50) return 'red'
if (d.dist < 45) return 'green'
if (d.dist < 50) return 'orange'
return 'blue'
})
.attr('fill-opacity', (d) => d.total > 50 ? 1 : 0.05)
.attr('cx', (d) => d.projected.x)
.attr('cy', (d) => d.projected.y)
points.exit().remove();

const yScale = svg.selectAll('path.yScale').data(yScale3d.rotateY(beta).rotateX(alpha)([runDuration]))
yScale.enter().append('path').attr('class', '_3d yScale').merge(yScale)
.attr('stroke', 'black')
.attr('stroke-width', .5)
.attr('d', yScale3d.draw)
yScale.exit().remove()

const pad = (n) => `${n}`.padStart(2, '0')
const yText = svg.selectAll('text.yText').data(runDuration)
yText.enter().append('text').attr('class', '_3d yText').merge(yText)
.attr('dx', '.3em')
.attr('x', (d) => d.projected.x)
.attr('y', (d) => d.projected.y)
.each((d) => { d.centroid = { ...d.rotated } })
.text((d) => {
if ((1 + d[1]) % 3) return ''
const sec = (110 - d[1]) * 10
return `${pad(Math.floor(sec / 60))}:${pad(sec % 60)}`
})
yText.exit().remove()

d3.selectAll('._3d').sort(d33d._3d().sort)
}

function dragged(evt) {
beta = (evt.x - mx + mouseX) * Math.PI / 230 + startY
alpha = (evt.y - my + mouseY) * Math.PI / 230 + startX
render()
}

render()
return svgNode.node()
}
Insert cell
Insert cell
Insert cell
function * gen({ s, p, r }) {
for (const [i, _s] of s.entries()) {
for (const [j, _p] of p.entries()) {
for (const [k, _r] of r.entries()) {
if (!(_s || _p || _r)) continue
const total = _s + _p + _r
if (total < 50) continue
if (total > 51) continue
yield { s: i - 30, p: j - 30, r: k, total, i, j, k, dist: Math.sqrt(i * i + j * j + k * k) }
}
}
}
}
Insert cell
scatter = [...gen(scores[2])]
Insert cell
scatter.filter((s) => s.total > 50 && s.dist < 49).sort((a, b) => a.k - b.k || a.j - b.j)
Insert cell
(110 - 21)
Insert cell
runDuration = {
return Array.from({ length: 60 }, (_, i) => [-30, i, -30])
}
Insert cell
grid = {
const base = Array.from({ length: 61 }, (_, i) => i - 30)
return base.flatMap((i) => base.map((j) => [i, 0, j]))
}
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more