Skip to content

binning — Parallel 2-D Histogram Binning

Source: crates/fastdpplot-core/src/binning.rs


bin_points

pub fn bin_points(points: &[DotPoint], request: &TileRequest) -> BinGrid

Bin points into a canvas_width × canvas_height grid for the given viewport.

Algorithm

  1. Compute the bin index for each point: bx = floor((x - x_min) / x_span * width).
  2. Each rayon thread accumulates into its own local (counts, value_sums) pair.
  3. Thread-local arrays are reduced by element-wise addition.
  4. Mean values are computed as value_sums[i] / counts[i] where count > 0.

The parallel fold-reduce strategy avoids per-point mutex locks and keeps floating-point accumulation deterministic up to thread-scheduling.

Performance

For a 4 000 × 4 000 canvas with 10⁸ points, a typical run on an 8-core machine takes ≈1–2 seconds end-to-end (I/O + binning).

Degenerate ranges

If x_span ≤ 0 or y_span ≤ 0 or width == 0 or height == 0, an all-zero BinGrid is returned immediately (no panic).

Index safety note

The single unsafe { get_unchecked_mut } inside the fold closure is sound because bx = min(floor(…), w-1) < w and by = min(floor(…), h-1) < h, so idx = by * w + bx < w * h == n.


compute_zoom_tiles

pub fn compute_zoom_tiles(
    points: &[DotPoint],
    max_zoom: u32,
    tile_size: usize,
) -> Vec<(u32, BinGrid)>

Pre-compute a zoom pyramid: for each level 0..=max_zoom produce a BinGrid at resolution tile_size × 2^level.

Returns a Vec<(zoom_level, BinGrid)> sorted by zoom level.

Example

// Levels 0–4 at base tile size 256
let pyramid = compute_zoom_tiles(&points, 4, 256);
// resolutions: 256, 512, 1024, 2048, 4096

Note

This function is not currently called by the Panel interactive server (which re-bins on demand) but is useful for pre-generating cached tiles for a tile server.