findPyramidSegment static method

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

Find pyramid segment at tap location Find pyramid chart segment at tap location.

Determines which segment of a pyramid chart contains the tap position by checking if the point is within the trapezoid shape of each segment.

Parameters:

  • tapPosition - The position where the user tapped
  • data - List of pie data segments (used for pyramid data)
  • size - Size of the chart area

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

Implementation

static ChartInteractionResult? findPyramidSegment(
  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 pyramid)
  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 baseWidth = chartWidth;
    final topWidth = chartWidth * 0.3;
    final currentY = cumulativeHeight;
    final nextY = cumulativeHeight + segmentHeight;

    final progress = currentY / chartHeight;
    final nextProgress = nextY / chartHeight;
    final currentWidth = baseWidth - (baseWidth - topWidth) * progress;
    final nextWidth = baseWidth - (baseWidth - topWidth) * 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;
}