findNearestPoint static method
Find nearest point to tap location (optimized with early exit and squared distance).
Searches through all data sets to find the point closest to the tap position within the specified tap radius. Returns null if no point is found.
Parameters:
tapPosition- The position where the user tappeddataSets- List of chart data sets to search throughchartSize- Size of the chart areaminX,maxX,minY,maxY- Data bounds for coordinate conversiontapRadius- Maximum distance from tap position to consider a hit
Returns a ChartInteractionResult if a point is found, null otherwise.
Implementation
static ChartInteractionResult? findNearestPoint(
Offset tapPosition,
List<ChartDataSet> dataSets,
Size chartSize,
double minX,
double maxX,
double minY,
double maxY,
double tapRadius,
) {
// Early exit if no data
if (dataSets.isEmpty) return null;
// Validate bounds (check for NaN or Infinity)
if (!minX.isFinite || !maxX.isFinite || !minY.isFinite || !maxY.isFinite) {
return null;
}
final xRange = maxX - minX;
final yRange = maxY - minY;
if (xRange == 0 || yRange == 0 || !xRange.isFinite || !yRange.isFinite) {
return null;
}
// Validate chart size
if (!chartSize.width.isFinite ||
!chartSize.height.isFinite ||
chartSize.width <= 0 ||
chartSize.height <= 0) {
return null;
}
// Validate tap position
if (!tapPosition.dx.isFinite || !tapPosition.dy.isFinite) {
return null;
}
// Use squared distance to avoid expensive sqrt calculation
final tapRadiusSquared = tapRadius * tapRadius;
double minDistanceSquared = double.infinity;
ChartInteractionResult? nearestResult;
for (int dsIndex = 0; dsIndex < dataSets.length; dsIndex++) {
final dataSet = dataSets[dsIndex];
if (dataSet.dataPoints.isEmpty) continue;
for (int ptIndex = 0; ptIndex < dataSet.dataPoints.length; ptIndex++) {
final point = dataSet.dataPoints[ptIndex];
// Validate point values
if (!point.x.isFinite || !point.y.isFinite) continue;
// Convert to canvas coordinates with NaN protection
final canvasX = ((point.x - minX) / xRange) * chartSize.width;
final canvasY =
chartSize.height - ((point.y - minY) / yRange) * chartSize.height;
// Validate calculated coordinates
if (!canvasX.isFinite || !canvasY.isFinite) continue;
// Quick bounds check before distance calculation
final dx = tapPosition.dx - canvasX;
final dy = tapPosition.dy - canvasY;
// Validate dx and dy
if (!dx.isFinite || !dy.isFinite) continue;
// Early exit if point is clearly outside radius
if (dx.abs() > tapRadius || dy.abs() > tapRadius) continue;
// Calculate squared distance (faster than distance)
final distanceSquared = dx * dx + dy * dy;
// Validate distance
if (!distanceSquared.isFinite) continue;
if (distanceSquared < tapRadiusSquared &&
distanceSquared < minDistanceSquared) {
minDistanceSquared = distanceSquared;
nearestResult = ChartInteractionResult(
point: point,
datasetIndex: dsIndex,
elementIndex: ptIndex,
isHit: true,
);
}
}
}
return nearestResult;
}