import 'dart:math' as math; import 'dart:ui'; import '../../game/models/grid.dart'; import '../../game/models/piece.dart'; /// Pure mapping between board-local pixels and grid cells. class BoardGeometry { const BoardGeometry({required this.boardSize}); final double boardSize; double get cellSize => boardSize / GridState.size; Rect cellRect(int x, int y) => Rect.fromLTWH(x * cellSize, y * cellSize, cellSize, cellSize); /// Cell under a local point, or null outside the board. (int, int)? cellAt(Offset local) { if (local.dx < 0 || local.dy < 0) return null; if (local.dx >= boardSize || local.dy >= boardSize) return null; return (local.dx ~/ cellSize, local.dy ~/ cellSize); } /// Nearest anchor for a piece whose visual top-left sits at [pieceTopLeft], /// clamped so the piece's bounding box stays on the board. (int, int) snapAnchor(Piece piece, Offset pieceTopLeft) { var maxDx = 0; var maxDy = 0; for (final (dx, dy) in piece.offsets) { maxDx = math.max(maxDx, dx); maxDy = math.max(maxDy, dy); } final x = (pieceTopLeft.dx / cellSize).round(); final y = (pieceTopLeft.dy / cellSize).round(); return ( x.clamp(0, GridState.size - 1 - maxDx), y.clamp(0, GridState.size - 1 - maxDy), ); } }