getHandleOffsets method

List<Offset> getHandleOffsets(
  1. Rect bounds,
  2. PriceRange priceRange,
  3. CoordinateConverter converter
)

Returns pixel positions of all interactive handles for this drawing.

Implementation

List<Offset> getHandleOffsets(
  Rect bounds,
  PriceRange priceRange,
  CoordinateConverter converter,
) {
  if (points.isEmpty) return const [];

  switch (tool) {
    case DrawingTool.pointer:
      return const [];

    case DrawingTool.trendline:
    case DrawingTool.extendedTrendline:
    case DrawingTool.arrow:
    case DrawingTool.ruler:
      if (points.length < 2) {
        final x = converter.indexToX(points[0].candleIndex);
        final y = CoordinateConverter.priceToY(
          points[0].price,
          bounds,
          priceRange,
        );
        return [Offset(x, y)];
      }
      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 [Offset(x1, y1), Offset(x2, y2)];

    case DrawingTool.horizontalLine:
      final y = CoordinateConverter.priceToY(
        points[0].price,
        bounds,
        priceRange,
      );
      return [
        Offset(bounds.left + 50.0, y),
        Offset(bounds.center.dx, y),
        Offset(bounds.right - 50.0, y),
      ];

    case DrawingTool.horizontalRay:
      final x = converter.indexToX(points[0].candleIndex);
      final y = CoordinateConverter.priceToY(
        points[0].price,
        bounds,
        priceRange,
      );
      return [
        Offset(x, y),
        Offset(((x + bounds.right) / 2).clamp(x + 20.0, bounds.right), y),
      ];

    case DrawingTool.verticalLine:
      final x = converter.indexToX(points[0].candleIndex);
      return [
        Offset(x, bounds.top + 40.0),
        Offset(x, bounds.center.dy),
        Offset(x, bounds.bottom - 40.0),
      ];

    case DrawingTool.rectangle:
      if (points.length < 2) {
        final x = converter.indexToX(points[0].candleIndex);
        final y = CoordinateConverter.priceToY(
          points[0].price,
          bounds,
          priceRange,
        );
        return [Offset(x, y)];
      }
      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 left = math.min(x1, x2);
      final right = math.max(x1, x2);
      final top = math.min(y1, y2);
      final bottom = math.max(y1, y2);
      final midX = (left + right) / 2;
      final midY = (top + bottom) / 2;

      return [
        Offset(left, top), // 0: Top-Left corner
        Offset(right, top), // 1: Top-Right corner
        Offset(right, bottom), // 2: Bottom-Right corner
        Offset(left, bottom), // 3: Bottom-Left corner
        Offset(midX, top), // 4: Top Edge
        Offset(right, midY), // 5: Right Edge
        Offset(midX, bottom), // 6: Bottom Edge
        Offset(left, midY), // 7: Left Edge
      ];

    case DrawingTool.fibonacci:
      if (points.length < 2) return const [];
      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 [Offset(x1, y1), Offset(x2, y2)];

    case DrawingTool.longPosition:
    case DrawingTool.shortPosition:
      final isLong = tool == DrawingTool.longPosition;
      final entry = points.first;
      final entryX = converter.indexToX(entry.candleIndex);
      final entryY = CoordinateConverter.priceToY(
        entry.price,
        bounds,
        priceRange,
      );
      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,
      );

      return [
        Offset((entryX + rightX) / 2, targetY),
        Offset((entryX + rightX) / 2, stopY),
        Offset(entryX, entryY),
        Offset(rightX, entryY),
      ];

    case DrawingTool.parallelChannel:
      if (points.length < 2) return const [];
      final px1 = converter.indexToX(points[0].candleIndex);
      final py1 = CoordinateConverter.priceToY(points[0].price, bounds, priceRange);
      final px2 = converter.indexToX(points[1].candleIndex);
      final py2 = CoordinateConverter.priceToY(points[1].price, bounds, priceRange);
      if (points.length >= 3) {
        final py3 = CoordinateConverter.priceToY(points[2].price, bounds, priceRange);
        return [Offset(px1, py1), Offset(px2, py2), Offset(px1, py3)];
      }
      return [Offset(px1, py1), Offset(px2, py2)];

    case DrawingTool.textLabel:
      if (points.isEmpty) return const [];
      final lx = converter.indexToX(points[0].candleIndex);
      final ly = CoordinateConverter.priceToY(points[0].price, bounds, priceRange);
      return [Offset(lx, ly)];
  }
}