Public
Edited
Nov 13, 2022
9 stars
Also listed in…
SMB Prototypes
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
margin = ({top: 20, right: 20, bottom: 20, left: 20})
Insert cell
height = Math.ceil(width * screen.height / screen.width)
Insert cell
x = d3.scaleLinear()
.domain(d3.extent(umap, d => d.x)).nice()
.range([margin.left, width - margin.right])
Insert cell
y = d3.scaleLinear()
.domain(d3.extent(umap, d => d.y)).nice()
.range([height - margin.bottom, margin.top])
Insert cell
Insert cell
umapProjection = umap.map((d,i) => ({
id: d.id,
x: x(d.x),
y: y(d.y),
scale,
alpha: 1,
zIndex: 0,
visible: true
}))
Insert cell
project(umapProjection)
Insert cell
Insert cell
Insert cell
zoomBarrier = 9
Insert cell
snappyDistance = 5
Insert cell
scoreCutoff = 0.3
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
mutable state = 1
Insert cell
function getListProjection(similarsMap, similars){
const space = 20
const paddingTop = 100
let projection = umapProjection.map(d => {
const similar = similarsMap.get(d.id)
const visible = similar.score > scoreCutoff
let x = d.x
let y = d.y
let scale = d.scale
let alpha = 0
if(visible) {
x = width / 2
y = paddingTop + similar.rank * (textureRes + space)
scale = 1
alpha = 1
//console.log(similar)
}
return { ...d, scale, visible, x, y, alpha }
})
return projection
}
Insert cell
transitionIntoList = {
let canvas
let lastProjection
return async function(selected,similarsMap, similars, _lastProjection, that){
lastProjection = _lastProjection
if(!canvas) canvas = d3.select(that)
console.log("here we go", selected,similarsMap, lastProjection, that)
canvas.on("click", function() {
console.log("click back")
that.reset(2000)
.on("end", () => {
})
d3.selection()
.interrupt()
.transition()
.duration(2000)
//.ease(d3.easePolyInOut)
.tween("list", function() {
const newProjection = umapProjection
const interpolate = d3.interpolate(lastProjection, newProjection)
return function(t) {
lastProjection = interpolate(t)
project(lastProjection)
};
})
.on("end", () => {
mutable state = 1
})
return
})
// setTimout hack
setTimeout(() => {
that.reset(2000)
.on("end", () => {
d3.selection()
.interrupt()
.transition()
.duration(2000)
//.ease(d3.easePolyInOut)
.tween("list", function() {
const newProjection = getListProjection(similarsMap, similars)
const interpolate = d3.interpolate(lastProjection, newProjection)
return function(t) {
lastProjection = interpolate(t)
project(lastProjection)
};
})
})
}, 50)
}
}
Insert cell
transition = {
let lastSelected
let lastProjection = umapProjection
let timer
let similarsMap
let canvas
let animationFinished = false
let similars
//
return async function(selected, distance, that){
if(distance >= snappyDistance) selected = null
if(lastSelected === selected) return
if(!canvas) canvas = d3.select(that)
canvas.style("cursor", distance >= snappyDistance ? "default" : "pointer")
const alted = d3.event.altKey
canvas.on("click", () => {
console.log("click", selected)
if(selected){
const transform = d3.zoomTransform(that)
if(transform.k >= zoomBarrier){
console.log("animate!")
if(animationFinished) {
lastProjection = umapProjection
d3.selection()
.interrupt()
transitionIntoList(selected,similarsMap,similars, lastProjection, that)
mutable state = 2
}
} else {
canvas
.transition()
.duration(1000)
.call(zoom.scaleTo, zoomBarrier, transform.apply([selected.x, selected.y]))
}
} else {
canvas
.transition()
.duration(200)
.call(
zoom.transform,
d3.zoomIdentity
.translate(0,0)
.scale(1)
);
}
})
animationFinished = false
// create similar map
if(selected){
similars = await getImageDistancesForId(selected.id)
//console.log(similars)
similarsMap = new Map(similars.map(d => [d.id, d]))
}
if(selected && alted) {
lastProjection = umapProjection.map(d => {
const similar = similarsMap.get(d.id)
const alpha = similar.score > scoreCutoff ? 1 : 0.1
const active = selected.id == d.id
return { ...d, scale: active ? scale * 2 : scale, zIndex: active ? 1 : 0, alpha }
})
project(lastProjection)
return
}
const transform = d3.zoomTransform(that)
if(transform.k !== zoomBarrier){
return
}
d3.selection()
.interrupt()
.transition()
.duration(200)
//.ease(d3.easePolyInOut)
.tween("close", function() {
const newProjection = umapProjection.map(d => {
const active = selected ? selected.id == d.id : false
return { ...d, scale: active ? scale * 1.2 : d.scale }
})
const interpolate = d3.interpolate(lastProjection, newProjection)
return function(t) {
lastProjection = interpolate(t)
project(lastProjection)
};
})
.filter(() => distance < snappyDistance)
.transition()
//.delay(30)
.delay(100)
.duration(700)
.ease(d3.easeExpIn)
.tween("open", function() {
const newProjection = lastProjection.map(d => {
const active = selected.id == d.id
return { ...d, scale: active ? scale * 2 : scale, zIndex: active ? 1 : 0 }
})
const interpolate = d3.interpolate(lastProjection, newProjection)
return function(t) {
lastProjection = interpolate(t)
project(lastProjection)
};
})
.transition()
.duration(0)
.tween("appear", function() {
const newProjection = lastProjection.map(d => {
const similar = similarsMap.get(d.id)
const alpha = similar.score > scoreCutoff ? 1 : 0.1
return { ...d, alpha }
})
const interpolate = d3.interpolate(lastProjection, newProjection)
return function(t) {
lastProjection = interpolate(t)
project(lastProjection)
};
})
.on("end", () => { animationFinished = true })
renderer.render(container)
lastSelected = selected
}
//asdasasd
}
Insert cell
function mousemove() {
const m = d3.mouse(this)
const p = d3.zoomTransform(this).invert(m)
const q = quadtree.find(p[0], p[1])
const distance = Math.hypot(p[0] - q.x, p[1] - q.y)
if(state === 1) transition(q, distance, this)
else if (state === 2) {
console.log("mouse2")
}
//as dasdasdasdasddsf
}
Insert cell
function wheeled() {
d3.event.preventDefault();
if(state === 1) return
const delta = d3.event.deltaY * (d3.event.deltaMode === 1 ? 0.05 : d3.event.deltaMode ? 1 : 0.002);
zoom.translateBy(d3.select(this), 0, -delta*230)
}
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
import { renderer, container, PIXI } from "@cpietsch/pixi-renderer"
Insert cell
import { getTextureStream, sprites } with {loadDelay as delay } from "68b54e3f54ebd551"
Insert cell
Insert cell
import {getDistancesForId as getImageDistancesForId, umapUrl} from "09aee975378da6d9"
Insert cell
d3 = require("d3@5")
Insert cell
loadDelay = 0
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