findPyramidSegment static method

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

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
  • animationProgress - Animation progress (0.0 to 1.0)

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 (!_isValidSize(size) || !_isValidPosition(tapPosition)) {
    return null;
  }
  if (!animationProgress.isFinite ||
      animationProgress < 0.0 ||
      animationProgress > 1.0) {
    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.0, (sum, item) => sum + item.value);
  if (!_isValidPositiveValue(total)) return null;

  final chartWidth = size.width - _defaultPadding * 2;
  final chartHeight = size.height - _defaultPadding * 2;
  final centerX = size.width / 2;

  // Validate chart dimensions after padding
  if (chartWidth <= 0 || chartHeight <= 0) return null;

  final baseWidth = chartWidth;
  final topWidth = chartWidth * _pyramidTopWidthRatio;
  final widthDifference = baseWidth - topWidth;

  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 currentY = cumulativeHeight;
    final nextY = cumulativeHeight + segmentHeight;

    final progress = currentY / chartHeight;
    final nextProgress = nextY / chartHeight;
    final currentWidth = baseWidth - widthDifference * progress;
    final nextWidth = baseWidth - widthDifference * nextProgress;

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

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

    cumulativeHeight += segmentHeight;
  }

  return null;
}