Public
Edited
Jul 14, 2022
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
// filePath = "https://assets.zilliz.com/faiss_ivf_flat_voc_17k_train_10_33e89cfb61.index"
// filePath = "https://assets.zilliz.com/faiss_ivf_flat_voc_17k_train_33_8af246406f.index"
// filePath = "https://assets.zilliz.com/faiss_ivf_flat_voc_17k_train_50_ee88958cff.index"
filePath = "https://assets.zilliz.com/faiss_ivf_flat_voc_17k_ab112eec72.index"
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
transiton_area(2000)
Insert cell
Insert cell
transition_forceCollison(300)
Insert cell
Insert cell
getVoronoiCells_weighted = (svg, opacity) => {
const wVoronoi = weightedVoronoiModule
.weightedVoronoi()
.x((d) => d[0])
.y((d) => d[1])
.weight((d) => d[2])
.clip([
[0, 0],
[0, height],
[width, height],
[width, 0]
]);
const cells = wVoronoi(
clusters.map((cluster) => [
...cluster.pos[posLen - 1],
Math.pow(cluster.r, 2)
])
);

const voronoiG = svg.append("g").attr("id", "voronoi-g");
const voronois = voronoiG
.selectAll("path")
.data(cells)
.join("path")
.attr("d", (d) => `M${d.join("L")}Z`)
.attr("fill", "none")
.attr("stroke", "#666")
.attr("stroke-width", 1)
.attr("opacity", opacity);
return voronois;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
testId = Math.floor(Math.random() * 10000)
Insert cell
Insert cell
searchRes = core.search(testVector)
Insert cell
// clone clusters
searchClusters = clusters.map(({ clusterId, x, y, r }) => ({
clusterId,
x,
y,
r,
overviewPos: [x, y],
inNprobe: searchRes.csResIds.indexOf(clusterId) >= 0,
containTarget: searchRes.csResIds[0] == clusterId
}))
Insert cell
nprobeClusters = searchClusters.filter(
(cluster) => searchRes.csResIds.indexOf(cluster.clusterId) >= 0
)
Insert cell
searchRes.coarse.forEach(({ id, dis }) => (searchClusters[id].dis = dis))
Insert cell
Insert cell
Insert cell
Insert cell
targetClusterId = searchRes.coarse[0].id
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
nprobeClusterPolyCentroidY = nprobeClusters.reduce(
(acc, cluster) => acc + cluster.overviewPos[1],
0
) / nprobe
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
forceSearchClusters = new Promise((resolve) => {
nprobeClusters
.filter((cluster) => !cluster.containTarget)
.forEach((cluster) => {
const order = aroundNprobeClusterOrder.indexOf(cluster.clusterId);
cluster.searchViewAdjustmentPos = [
targetClusterX + biasR * Math.sin(angleStep * order),
targetClusterY + biasR * Math.cos(angleStep * order)
];
});

searchClusters.forEach((cluster) => {
cluster.x = cluster.searchViewAdjustmentPos
? cluster.searchViewAdjustmentPos[0]
: cluster.overviewPos[0];
cluster.y = cluster.searchViewAdjustmentPos
? cluster.searchViewAdjustmentPos[1]
: cluster.overviewPos[1];
cluster.searchViewPos = [[cluster.x, cluster.y]];
});

const simulation = d3
.forceSimulation(searchClusters)
.alphaDecay(1 - Math.pow(0.001, 1 / 100))
.force(
"links",
d3
.forceLink(links)
.id((cluster) => cluster.clusterId)
.strength((_) => 0.25)
)
.force(
"collision",
d3
.forceCollide()
.radius((cluster) => cluster.r)
.strength(0.1)
)
.force("center", d3.forceCenter(width / 2, height / 2))
.on("tick", () => {
// border
searchClusters.forEach((cluster) => {
cluster.x = Math.max(cluster.r, Math.min(width - cluster.r, cluster.x));
cluster.y = Math.max(
cluster.r,
Math.min(height - cluster.r, cluster.y)
);
cluster.searchViewPos.push([cluster.x, cluster.y]);
});
})
.on("end", () => {
const len = searchClusters[0].searchViewPos.length;
const delaunay = d3.Delaunay.from(
searchClusters.map((cluster) => cluster.searchViewPos[len-1])
);
const voronoi = delaunay.voronoi([0, 0, width, height]);
// const cells = wVoronoi(
// searchClusters.map((cluster) => [
// ...cluster.searchViewPos[len - 1],
// Math.pow(cluster.r, 2)
// ])
// );
searchClusters.forEach(
// (cluster, i) => (cluster.searchViewVoronoiPoints = cells[i])
(cluster, i) =>
(cluster.searchViewVoronoiPoints = voronoi.cellPolygon(i))
);
resolve(searchClusters);
});
})
Insert cell
Insert cell
animateSearchView = (duration) => {
const svg = getSvg();

const clustersG = svg.append("g").attr("id", "clusters-g");
const clusters = clustersG
.selectAll("g")
.data(forceSearchClusters)
.join("g")
.attr("id", (d) => d.clusterId)
.attr("transform", (d) => `translate(${d.overviewPos})`)
.attr("opacity", 1);

const areas = clusters
.append("circle")
.attr("r", (d) => d.r)
.attr("fill", (d) =>
d.containTarget ? "red" : d.inNprobe ? "blue" : "#666"
)
.attr("opacity", (d) => (d.containTarget ? 0.2 : 0.1));
const nodes = clusters
.append("circle")
.attr("r", (d) => (d.inNprobe ? 3 : 0))
.attr("fill", "#333")
.attr("opacity", 0.3);

// const texts = clusters
// .append("text")
// .text((d) => d.clusterId)
// .attr("text-anchor", "middle");

const voronoiG = svg.append("g").attr("id", "voronoi-g");
const voronois = voronoiG
.selectAll("path")
.data(forceSearchClusters)
.join("path")
.attr("d", (d) => `M${d.searchViewVoronoiPoints.join("L")}Z`)
.attr("fill", (d, i) =>
d.containTarget ? "red" : d.inNprobe ? "blue" : "#666"
)
.attr("stroke", "#fff")
.attr("stroke-width", 1)
.attr("opacity", 0);

new Promise(async () => {
await new Promise((resolve) => {
clusters
.filter((d) => d.containTarget)
.transition()
.duration(2000)
.attr("transform", (d) => `translate(${d.searchViewPos[0]})`)
.on("end", () => resolve());
});

await new Promise((resolve) => {
clusters
.filter((d) => d.inNprobe && !d.containTarget)
.transition()
.delay(1000)
.duration(2000)
.attr("transform", (d) => `translate(${d.searchViewPos[0]})`)
.on("end", () => resolve());
});

await sleep(1000);

await new Promise((resolve) => {
let t = 0;
let len = forceSearchClusters[0].searchViewPos.length;
const timer = setInterval(() => {
clusters
.transition()
.duration(duration)
.ease(d3.easeLinear)
.attr("transform", (d) => `translate(${d.searchViewPos[t]})`);

t += 1;
if (t >= len) {
clearInterval(timer);
resolve();
}
}, duration);
});

await sleep(1000);

await new Promise((resolve) => {
voronois
.transition()
.duration(2000)
.attr("opacity", 1)
.on("end", () => resolve());
});

await sleep(1000);

await new Promise((resolve) => {
clusters
.transition()
.duration(2000)
.ease(d3.easeLinear)
.attr("opacity", 0)
.on("end", () => resolve());
});
});

return svg.node();
}
Insert cell
animateSearchView(100)
Insert cell
forceSearchClusters[0].searchViewVoronoiPoints
Insert cell
forceSearchClusters[0].clusterId
Insert cell
wVoronoi = weightedVoronoiModule
.weightedVoronoi()
.x((d) => d[0])
.y((d) => d[1])
.weight((d) => d[2])
.clip([
[0, 0],
[0, height],
[width, height],
[width, 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