import type { BrushRect } from './types' /** * Returns integer dataIndices covered by a brush rectangle on a category axis. * * `xStart` / `xEnd` come from `instance.convertFromPixel({ xAxisIndex: 0 }, …)` * which, for a category axis, returns fractional positions around integer * category indices (e.g. `1.3` lands between categories 1 and 2). We snap the * range outward (`floor` / `ceil`) so a drag that visually covers any part of * a category's bar includes that index. * * Results are clamped to `[0, dataLength - 1]` and returned in ascending order. */ export function hitTestCategoryRange( xStart: number, xEnd: number, dataLength: number, ): number[] { if (dataLength <= 0) return [] const [lo, hi] = xStart <= xEnd ? [xStart, xEnd] : [xEnd, xStart] const start = Math.max(0, Math.floor(lo)) const end = Math.min(dataLength - 1, Math.ceil(hi)) if (start > end) return [] const indices: number[] = [] for (let i = start; i <= end; i += 1) indices.push(i) return indices } /** * Union of `hitTestCategoryRange` across multiple rectangles. Used for * multi-brush selections where each drawn rectangle contributes to the * combined selection. */ export function hitTestRects(rects: BrushRect[], dataLength: number): number[] { if (rects.length === 0) return [] const seen = new Set() for (const rect of rects) { for (const i of hitTestCategoryRange(rect.xStart, rect.xEnd, dataLength)) { seen.add(i) } } return Array.from(seen).sort((a, b) => a - b) }