hitTest method

bool hitTest(
  1. Offset pos,
  2. Rect bounds,
  3. PriceRange priceRange,
  4. CoordinateConverter converter, {
  5. double threshold = 8.0,
})

Checks if pos hits this drawing (near line, inside box, or near handles).

Implementation

bool hitTest(
  Offset pos,
  Rect bounds,
  PriceRange priceRange,
  CoordinateConverter converter, {
  double threshold = 8.0,
}) {
  if (points.isEmpty) return false;

  if (hitTestHandle(
        pos,
        bounds,
        priceRange,
        converter,
        threshold: threshold + 4,
      ) !=
      null) {
    return true;
  }

  switch (tool) {
    case DrawingTool.pointer:
      return false;

    case DrawingTool.trendline:
    case DrawingTool.ruler:
      if (points.length < 2) return false;
      final x1 = converter.indexToX(points[0].candleIndex);
      final y1 = CoordinateConverter.priceToY(
        points[0].price,
        bounds,
        priceRange,
      );
      final x2 = converter.indexToX(points[1].candleIndex);
      final y2 = CoordinateConverter.priceToY(
        points[1].price,
        bounds,
        priceRange,
      );
      return distanceToSegment(pos, Offset(x1, y1), Offset(x2, y2)) <=
          threshold;

    case DrawingTool.horizontalLine:
      final y = CoordinateConverter.priceToY(
        points[0].price,
        bounds,
        priceRange,
      );
      return (pos.dy - y).abs() <= math.max(threshold, 10.0) &&
          pos.dx >= bounds.left - 10 &&
          pos.dx <= bounds.right + 10;

    case DrawingTool.horizontalRay:
      final x = converter.indexToX(points[0].candleIndex);
      final y = CoordinateConverter.priceToY(
        points[0].price,
        bounds,
        priceRange,
      );
      return (pos.dy - y).abs() <= math.max(threshold, 10.0) &&
          pos.dx >= x - 10 &&
          pos.dx <= bounds.right + 10;

    case DrawingTool.verticalLine:
      final x = converter.indexToX(points[0].candleIndex);
      return (pos.dx - x).abs() <= math.max(threshold, 10.0) &&
          pos.dy >= bounds.top - 10 &&
          pos.dy <= bounds.bottom + 10;

    case DrawingTool.rectangle:
      if (points.length < 2) return false;
      final x1 = converter.indexToX(points[0].candleIndex);
      final y1 = CoordinateConverter.priceToY(
        points[0].price,
        bounds,
        priceRange,
      );
      final x2 = converter.indexToX(points[1].candleIndex);
      final y2 = CoordinateConverter.priceToY(
        points[1].price,
        bounds,
        priceRange,
      );
      final rect = Rect.fromLTRB(
        math.min(x1, x2),
        math.min(y1, y2),
        math.max(x1, x2),
        math.max(y1, y2),
      );
      return rect.inflate(threshold).contains(pos);

    case DrawingTool.fibonacci:
      if (points.length < 2) return false;
      final p1 = points[0];
      final p2 = points[1];
      final x1 = converter.indexToX(p1.candleIndex);
      final x2 = converter.indexToX(p2.candleIndex);
      final minX = math.min(x1, x2) - threshold;
      final maxX = math.max(bounds.right, math.max(x1, x2)) + threshold;

      if (pos.dx < minX || pos.dx > maxX) return false;

      final priceDiff = p2.price - p1.price;
      const ratios = [0.0, 0.236, 0.382, 0.500, 0.618, 0.786, 1.0];
      for (final r in ratios) {
        final levelPrice = p1.price + (priceDiff * r);
        final y = CoordinateConverter.priceToY(
          levelPrice,
          bounds,
          priceRange,
        );
        if ((pos.dy - y).abs() <= threshold) return true;
      }
      return false;

    case DrawingTool.longPosition:
    case DrawingTool.shortPosition:
      final isLong = tool == DrawingTool.longPosition;
      final entry = points.first;
      final entryX = converter.indexToX(entry.candleIndex);
      final widthSpan = (properties['widthSpan'] as double?) ?? (40 * 11.0);
      final rightX = (entryX + widthSpan).clamp(entryX + 50.0, bounds.right);

      final targetPrice = properties['targetPrice'] as double? ??
          (isLong ? entry.price * 1.015 : entry.price * 0.985);
      final stopPrice = properties['stopPrice'] as double? ??
          (isLong ? entry.price * 0.9925 : entry.price * 1.0075);

      final targetY = CoordinateConverter.priceToY(
        targetPrice,
        bounds,
        priceRange,
      );
      final stopY = CoordinateConverter.priceToY(
        stopPrice,
        bounds,
        priceRange,
      );

      final minY = math.min(targetY, stopY);
      final maxY = math.max(targetY, stopY);
      final totalRect = Rect.fromLTRB(entryX, minY, rightX, maxY);
      return totalRect.inflate(threshold).contains(pos);

    case DrawingTool.extendedTrendline:
    case DrawingTool.arrow:
      // Same as trendline — hits the segment
      if (points.length < 2) return false;
      final ax1 = converter.indexToX(points[0].candleIndex);
      final ay1 = CoordinateConverter.priceToY(points[0].price, bounds, priceRange);
      final ax2 = converter.indexToX(points[1].candleIndex);
      final ay2 = CoordinateConverter.priceToY(points[1].price, bounds, priceRange);
      return distanceToSegment(pos, Offset(ax1, ay1), Offset(ax2, ay2)) <= threshold;

    case DrawingTool.parallelChannel:
      if (points.length < 2) return false;
      final cx1 = converter.indexToX(points[0].candleIndex);
      final cy1 = CoordinateConverter.priceToY(points[0].price, bounds, priceRange);
      final cx2 = converter.indexToX(points[1].candleIndex);
      final cy2 = CoordinateConverter.priceToY(points[1].price, bounds, priceRange);
      if (distanceToSegment(pos, Offset(cx1, cy1), Offset(cx2, cy2)) <= threshold) return true;
      if (points.length >= 3) {
        final cy3 = CoordinateConverter.priceToY(points[2].price, bounds, priceRange);
        final midY = (cy1 + cy2) / 2;
        final offset = cy3 - midY;
        return distanceToSegment(
            pos, Offset(cx1, cy1 + offset), Offset(cx2, cy2 + offset)) <= threshold;
      }
      return false;

    case DrawingTool.textLabel:
      if (points.isEmpty) return false;
      final lx = converter.indexToX(points[0].candleIndex);
      final ly = CoordinateConverter.priceToY(points[0].price, bounds, priceRange);
      return (Offset(lx, ly) - pos).distance <= threshold * 2.5;
  }
}