findFunnelSegment static method

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

Find funnel segment at tap location

Implementation

static ChartInteractionResult? findFunnelSegment(
  Offset tapPosition,
  List<PieData> data,
  Size size,
  double animationProgress,
) {
  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;
  }

  // Sort data by value (largest to smallest for funnel)
  final sortedData = List<PieData>.from(data)
    ..sort((a, b) => b.value.compareTo(a.value));

  final total = sortedData.fold<double>(0, (sum, item) => sum + item.value);
  if (total <= 0 || !total.isFinite) return null;

  const padding = 40.0;
  final chartWidth = size.width - padding * 2;
  final chartHeight = size.height - padding * 2;
  final centerX = size.width / 2;

  double cumulativeHeight = 0.0;

  for (int i = 0; i < sortedData.length; i++) {
    final segment = sortedData[i];
    final percentage = segment.value / total;
    final segmentHeight = chartHeight * percentage * animationProgress;

    final topWidth = chartWidth;
    final bottomWidth = chartWidth * 0.3;
    final currentY = cumulativeHeight;
    final nextY = cumulativeHeight + segmentHeight;

    final progress = currentY / chartHeight;
    final nextProgress = nextY / chartHeight;
    final currentWidth = topWidth - (topWidth - bottomWidth) * progress;
    final nextWidth = topWidth - (topWidth - bottomWidth) * nextProgress;

    // Check if tap is within trapezoid bounds
    final topLeft = Offset(centerX - currentWidth / 2, padding + currentY);
    final topRight = Offset(centerX + currentWidth / 2, padding + currentY);
    final bottomRight = Offset(centerX + nextWidth / 2, padding + nextY);
    final bottomLeft = Offset(centerX - nextWidth / 2, padding + nextY);

    if (_isPointInTrapezoid(
      tapPosition,
      topLeft,
      topRight,
      bottomRight,
      bottomLeft,
    )) {
      // Find original index in unsorted data
      final originalIndex = data.indexOf(segment);
      return ChartInteractionResult(
        segment: segment,
        elementIndex: originalIndex >= 0 ? originalIndex : i,
        isHit: true,
      );
    }

    cumulativeHeight += segmentHeight;
  }

  return null;
}