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

Function findPointsAtX

src/interaction/findPointsAtX.ts:146–404  ·  view source on GitHub ↗
(
  series: ReadonlyArray<ResolvedSeriesConfig>,
  xValue: number,
  xScale: LinearScale,
  tolerance?: number,
)

Source from the content-addressed store, hash-verified

144 * back to the existing nearest-x behavior (so axis-trigger tooltips still work away from bars).
145 */
146export function findPointsAtX(
147 series: ReadonlyArray<ResolvedSeriesConfig>,
148 xValue: number,
149 xScale: LinearScale,
150 tolerance?: number,
151): ReadonlyArray<PointsAtXMatch> {
152 if (!Number.isFinite(xValue)) return [];
153
154 const maxDx =
155 tolerance === undefined || !Number.isFinite(tolerance) ? Number.POSITIVE_INFINITY : Math.max(0, tolerance);
156 const maxDxSq = maxDx * maxDx;
157
158 const xTarget = xScale.invert(xValue);
159 if (!Number.isFinite(xTarget)) return [];
160
161 const matches: PointsAtXMatch[] = [];
162 const barLayout = computeBarHitTestLayout(series, xScale);
163
164 for (let s = 0; s < series.length; s++) {
165 const seriesConfig = series[s];
166 // Pie and candlestick are non-cartesian (or not yet implemented); they can't match an x position.
167 if (seriesConfig.type === 'pie' || seriesConfig.type === 'candlestick') continue;
168
169 const data = seriesConfig.data;
170 const n = data.length;
171 if (n === 0) continue;
172
173 const first = data[0];
174 const isTuple = Array.isArray(first);
175
176 // Bar series: return the correct bar dataIndex for xValue when inside the bar interval.
177 // When tolerance is finite: require an (expanded) interval hit.
178 // When tolerance is non-finite: attempt exact hit, otherwise fall back to nearest-x behavior below.
179 if (seriesConfig.type === 'bar' && barLayout) {
180 const clusterIndex = barLayout.clusterIndexByGlobalSeriesIndex.get(s);
181 if (clusterIndex !== undefined) {
182 const { barWidth, gap, clusterWidth } = barLayout;
183 const offsetLeftFromCategoryCenter = -clusterWidth / 2 + clusterIndex * (barWidth + gap);
184
185 const hitTol =
186 tolerance === undefined || !Number.isFinite(tolerance) ? 0 : Math.max(0, tolerance);
187
188 // If we can't safely compute an interval hit, don't guess when tolerance is finite.
189 if (Number.isFinite(barWidth) && barWidth > 0 && Number.isFinite(offsetLeftFromCategoryCenter)) {
190 let hitIndex = -1;
191
192 const isHit = (xCenterRange: number): boolean => {
193 if (!Number.isFinite(xCenterRange)) return false;
194 const left = xCenterRange + offsetLeftFromCategoryCenter;
195 const right = left + barWidth;
196 // Expanded interval: [left - tol, right + tol)
197 return xValue >= left - hitTol && xValue < right + hitTol;
198 };
199
200 if (seriesHasNaNX(data, isTuple)) {
201 // NaN breaks ordering; linear scan for correctness.
202 if (isTuple) {
203 const tupleData = data as ReadonlyArray<TuplePoint>;

Callers 1

renderFunction · 0.90

Calls 10

computeBarHitTestLayoutFunction · 0.85
seriesHasNaNXFunction · 0.85
isHitFunction · 0.85
getXCenterAtFunction · 0.85
dxSqAtFunction · 0.85
invertMethod · 0.80
lowerBoundTupleFunction · 0.70
lowerBoundObjectFunction · 0.70
tryUpdateFunction · 0.70
scaleMethod · 0.65

Tested by

no test coverage detected