findPieSegment static method

ChartInteractionResult? findPieSegment(
  1. Offset tapPosition,
  2. List<PieData> data,
  3. Size size,
  4. double centerSpaceRadius,
)

Find pie/donut chart segment at tap location.

Determines which segment of a pie or donut chart contains the tap position by calculating the angle from the center. Returns null if tap is outside the chart or in the center hole (for donut charts).

Parameters:

  • tapPosition - The position where the user tapped
  • data - List of pie data segments
  • size - Size of the chart area
  • centerSpaceRadius - Inner radius for donut charts (0 for pie charts)

Returns a ChartInteractionResult if a segment is found, null otherwise.

Implementation

static ChartInteractionResult? findPieSegment(
  Offset tapPosition,
  List<PieData> data,
  Size size,
  double centerSpaceRadius,
) {
  // Validate inputs
  if (data.isEmpty) return null;
  if (!_isValidSize(size) || !_isValidPosition(tapPosition)) {
    return null;
  }

  final center = Offset(size.width / 2, size.height / 2);
  final radius = math.min(size.width, size.height) / 2 - _defaultRadiusOffset;

  // Validate radius
  if (!_isValidPositiveValue(radius)) return null;

  // Check if tap is within chart bounds using squared distance
  final distanceSquared = _squaredDistance(tapPosition, center);
  if (distanceSquared == null) return null;

  final centerSpaceRadiusSquared = centerSpaceRadius * centerSpaceRadius;
  final radiusSquared = radius * radius;

  if (distanceSquared < centerSpaceRadiusSquared ||
      distanceSquared > radiusSquared) {
    return null;
  }

  // Calculate total value
  final total = data.fold<double>(0.0, (sum, item) => sum + item.value);
  if (!_isValidPositiveValue(total)) return null;

  // Pre-calculate constants
  const startAngleOffset = -math.pi / 2;
  const twoPi = 2 * math.pi;

  double startAngle = startAngleOffset;

  // Calculate angle from center to tap point
  final dx = tapPosition.dx - center.dx;
  final dy = tapPosition.dy - center.dy;
  final tapAngle = math.atan2(dy, dx);
  // Normalize to 0-2π range
  final normalizedAngle = (tapAngle + twoPi) % twoPi;

  // Pre-calculate value-to-angle conversion factor
  final valueToAngle = twoPi / total;

  for (int i = 0; i < data.length; i++) {
    final item = data[i];
    final sweepAngle = item.value * valueToAngle;
    final endAngle = startAngle + sweepAngle;

    // Normalize start and end angles
    final normalizedStart = (startAngle + twoPi) % twoPi;
    final normalizedEnd = (endAngle + twoPi) % twoPi;

    // Check if tap angle is within segment
    final isInSegment = normalizedEnd > normalizedStart
        ? normalizedAngle >= normalizedStart &&
            normalizedAngle <= normalizedEnd
        : normalizedAngle >= normalizedStart ||
            normalizedAngle <= normalizedEnd;

    if (isInSegment) {
      return ChartInteractionResult(
        segment: item,
        datasetIndex: 0, // Pie charts have a single dataset
        elementIndex: i,
        isHit: true,
      );
    }

    startAngle = endAngle;
  }

  return null;
}