collides method

  1. @override
CollisionReport<D> collides(
  1. List<Tick<D>>? ticks,
  2. AxisOrientation? orientation
)
override

Returns a CollisionReport indicating if there are any collisions.

Implementation

@override
CollisionReport<D> collides(
  List<Tick<D>>? ticks,
  AxisOrientation? orientation,
) {
  // TODO: Collision analysis for rotated labels are not
  // supported yet.

  // If there are no ticks, they do not collide.
  if (ticks == null) {
    return CollisionReport(
      ticksCollide: false,
      ticks: ticks,
      alternateTicksUsed: false,
    );
  }

  final vertical = orientation == AxisOrientation.left ||
      orientation == AxisOrientation.right;

  ticks = [
    for (final tick in ticks)
      if (tick.locationPx != null) tick,
  ]

    // First sort ticks by smallest locationPx first (NOT sorted by value).
    // This allows us to only check if a tick collides with the previous tick.
    ..sort((a, b) => a.locationPx!.compareTo(b.locationPx!));

  var previousEnd = double.negativeInfinity;
  var collides = false;

  for (final tick in ticks) {
    final tickSize = tick.textElement?.measurement;
    final tickLocationPx = tick.locationPx!;

    if (vertical) {
      final adjustedHeight = (tickSize?.verticalSliceWidth ?? 0.0) +
          minimumPaddingBetweenLabelsPx;

      if (_defaultTickLabelAnchor == TickLabelAnchor.inside) {
        if (identical(tick, ticks.first)) {
          // Top most tick draws down from the location
          collides = false;
          previousEnd = tickLocationPx + adjustedHeight;
        } else if (identical(tick, ticks.last)) {
          // Bottom most tick draws up from the location
          collides = previousEnd > tickLocationPx - adjustedHeight;
          previousEnd = tickLocationPx;
        } else {
          // All other ticks is centered.
          final halfHeight = adjustedHeight / 2;
          collides = previousEnd > tickLocationPx - halfHeight;
          previousEnd = tickLocationPx + halfHeight;
        }
      } else {
        collides = previousEnd > tickLocationPx;
        previousEnd = tickLocationPx + adjustedHeight;
      }
    } else {
      // Use the text direction the ticks specified, unless the label anchor
      // is set to [TickLabelAnchor.inside]. When 'inside' is set, the text
      // direction is normalized such that the left most tick is drawn ltr,
      // the last tick is drawn rtl, and all other ticks are in the center.
      // This is not set until it is painted, so collision check needs to get
      // the value also.
      final textDirection = _normalizeHorizontalAnchor(
        _defaultTickLabelAnchor,
        chartContext.isRtl,
        identical(tick, ticks.first),
        identical(tick, ticks.last),
      );
      final adjustedWidth = (tickSize?.horizontalSliceWidth ?? 0.0) +
          minimumPaddingBetweenLabelsPx;
      switch (textDirection) {
        case TextDirection.ltr:
          collides = previousEnd > tickLocationPx;
          previousEnd = tickLocationPx + adjustedWidth;
        case TextDirection.rtl:
          collides = previousEnd > (tickLocationPx - adjustedWidth);
          previousEnd = tickLocationPx;
        case TextDirection.center:
          final halfWidth = adjustedWidth / 2;
          collides = previousEnd > tickLocationPx - halfWidth;
          previousEnd = tickLocationPx + halfWidth;
      }
    }

    if (collides) {
      return CollisionReport(
        ticksCollide: true,
        ticks: ticks,
        alternateTicksUsed: false,
      );
    }
  }

  return CollisionReport(
    ticksCollide: false,
    ticks: ticks,
    alternateTicksUsed: false,
  );
}