/** * Point clustering. * * Three decisions, each of which is visible on screen if you get it wrong. * * **1. Greedy radius clustering, not grid bucketing.** Bucketing points into grid * cells is a dozen lines and is what most quick implementations do, but it merges * by cell rather than by distance: two points a few pixels apart sit in different * cells and stay stubbornly separate, while two points at opposite corners of one * cell merge. Readers notice, because the map contradicts what they can see. So a * grid is used only as a *neighbour index*: cells are exactly one radius wide, so * every point within the radius is in the 3x3 cell block around it, and the * clustering itself is a genuine distance test. Same linear cost, correct result. * This is the approach Supercluster takes with a k-d tree. * * **2. Clustering happens in world space, at quantized zoom levels.** World * coordinates do not move when the camera pans, so a pan never reclusters and can * never make clusters shimmer or renumber under the reader's cursor. Zoom is * bucketed to half-levels and cached, so a smooth pinch recomputes a handful of * times rather than sixty times a second. * * **3. Input order decides cluster seeds.** Deterministic: the same data always * produces the same clusters, which matters for snapshot tests and for a reader * who pans away and comes back. * * @module geo/Cluster */ import type { WorldPoint } from '../types'; export interface ClusterInput { /** Index into the caller's own item array. */ index: number; world: WorldPoint; } export interface Cluster { /** Centre of mass of the members, in world space. */ world: WorldPoint; /** Item indices that were merged. Length 1 means an unclustered point. */ members: number[]; count: number; /** World-space bounds of the members, for zoom-to-fit on click. */ bounds: [WorldPoint, WorldPoint]; } export interface ClusterOptions { /** Merge distance in **screen** pixels. */ radius: number; /** Camera scale the clustering is for. Screen distance = world distance * k. */ zoom: number; /** Groups smaller than this are emitted as individual points. */ minPoints?: number; } /** * Quantize a camera scale to a stable clustering level. * * Half-steps of log2 mean a 2x zoom passes through two reclusters, which is often * enough that clusters visibly respond to zooming and rare enough that a pinch * gesture is not a recompute storm. * */ export declare function clusterLevel(zoom: number): number; /** The camera scale a level represents, which is what the merge distance uses. */ export declare function levelScale(level: number): number; /** * Merge points that fall within `radius` screen pixels of each other. * * Returns one entry per drawn mark, in input order of the seeds, so the result is * stable across calls. * */ export declare function clusterPoints(points: readonly ClusterInput[], options: ClusterOptions): Cluster[]; //# sourceMappingURL=Cluster.d.ts.map