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 (!size.width.isFinite ||
      !size.height.isFinite ||
      size.width <= 0 ||
      size.height <= 0) {
    return null;
  }
  if (!tapPosition.dx.isFinite || !tapPosition.dy.isFinite) {
    return null;
  }

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

  // Validate radius
  if (radius <= 0 || !radius.isFinite) return null;

  // Check if tap is within chart bounds
  final distanceFromCenter = (tapPosition - center).distance;
  if (!distanceFromCenter.isFinite) return null;
  if (distanceFromCenter < centerSpaceRadius || distanceFromCenter > radius) {
    return null;
  }

  final total = data.map((d) => d.value).reduce((a, b) => a + b);
  if (total <= 0 || !total.isFinite) return null;

  double startAngle = -math.pi / 2;

  // 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 starting from top
  final normalizedAngle =
      (tapAngle + math.pi / 2 + 2 * math.pi) % (2 * math.pi);

  for (int i = 0; i < data.length; i++) {
    final item = data[i];
    final sweepAngle = (item.value / total) * 2 * math.pi;
    final endAngle = startAngle + sweepAngle;

    // Normalize start and end angles
    final normalizedStart = (startAngle + 2 * math.pi) % (2 * math.pi);
    final normalizedEnd = (endAngle + 2 * math.pi) % (2 * math.pi);

    // Check if tap angle is within segment
    bool isInSegment = false;
    if (normalizedEnd > normalizedStart) {
      isInSegment = normalizedAngle >= normalizedStart &&
          normalizedAngle <= normalizedEnd;
    } else {
      // Segment wraps around
      isInSegment = normalizedAngle >= normalizedStart ||
          normalizedAngle <= normalizedEnd;
    }

    if (isInSegment) {
      return ChartInteractionResult(
        segment: item,
        elementIndex: i,
        isHit: true,
      );
    }

    startAngle = endAngle;
  }

  return null;
}