MCPcopy Index your code
hub / github.com/ChartGPU/ChartGPU / findNearestPoint

Function findNearestPoint

src/interaction/findNearestPoint.ts:476–874  ·  view source on GitHub ↗
(
  series: ReadonlyArray<ResolvedSeriesConfig>,
  x: number,
  y: number,
  xScale: LinearScale,
  yScale: LinearScale,
  maxDistance: number = DEFAULT_MAX_DISTANCE_PX,
)

Source from the content-addressed store, hash-verified

474 * - Skips non-finite points and any points whose scaled coordinates are NaN.
475 */
476export function findNearestPoint(
477 series: ReadonlyArray<ResolvedSeriesConfig>,
478 x: number,
479 y: number,
480 xScale: LinearScale,
481 yScale: LinearScale,
482 maxDistance: number = DEFAULT_MAX_DISTANCE_PX,
483): NearestPointMatch | null {
484 if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
485
486 const md = Number.isFinite(maxDistance)
487 ? Math.max(0, maxDistance)
488 : DEFAULT_MAX_DISTANCE_PX;
489 const maxDistSq = md * md;
490
491 const xTarget = xScale.invert(x);
492 if (!Number.isFinite(xTarget)) return null;
493
494 let bestSeriesIndex = -1;
495 let bestDataIndex = -1;
496 let bestPoint: DataPoint | null = null;
497 let bestDistSq = Number.POSITIVE_INFINITY;
498
499 // Story 4.6: Bar hit-testing (range-space bounds).
500 // - Only counts as a match when cursor is inside a bar rect.
501 // - For stacked bars, uses the same stacking bucket logic as the bar renderer (xKey bucketing).
502 // - If multiple segments match (shared edges), prefer visually topmost (smallest `top` in CSS px).
503 // If still tied, prefer larger `seriesIndex` for determinism.
504 const barSeriesConfigs: ResolvedBarSeriesConfig[] = [];
505 const barSeriesIndexByBar: number[] = [];
506 for (let s = 0; s < series.length; s++) {
507 const cfg = series[s];
508 if (cfg?.type === 'bar') {
509 barSeriesConfigs.push(cfg);
510 barSeriesIndexByBar.push(s);
511 }
512 }
513
514 if (barSeriesConfigs.length > 0) {
515 const layoutPx = computeBarLayoutPx(barSeriesConfigs, xScale);
516 if (layoutPx.barWidthPx > 0 && layoutPx.clusterWidthPx >= 0) {
517 const plotHeightPx = inferPlotHeightPxForBarHitTesting(barSeriesConfigs, yScale);
518 const { baselineDomain, baselinePx } = computeBaselineDomainAndPx(barSeriesConfigs, yScale, plotHeightPx);
519
520 const { clusterSlots, barWidthPx, gapPx, clusterWidthPx, categoryWidthPx, categoryStep } = layoutPx;
521 const stackSumsByStackId = new Map<string, Map<number, { posSum: number; negSum: number }>>();
522
523 let bestBarHit:
524 | {
525 readonly seriesIndex: number;
526 readonly dataIndex: number;
527 readonly top: number;
528 }
529 | null = null;
530
531 for (let b = 0; b < barSeriesConfigs.length; b++) {
532 const seriesCfg = barSeriesConfigs[b];
533 const originalSeriesIndex = barSeriesIndexByBar[b] ?? -1;

Callers 3

renderFunction · 0.90
handlePointerEventFunction · 0.90

Calls 12

computeBarLayoutPxFunction · 0.85
bucketStackedXKeyFunction · 0.85
isPointInBarFunction · 0.85
getMaxScatterRadiusCssPxFunction · 0.85
getScatterRadiusCssPxFunction · 0.85
invertMethod · 0.80
getPointXYFunction · 0.70
lowerBoundTupleFunction · 0.70
lowerBoundObjectFunction · 0.70
scaleMethod · 0.65

Tested by

no test coverage detected