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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more