// Pure drag → adjacent-swap intent mapping (Phase 5 input). Turns a touch drag — // a down position and an up/move position, both in the same view-local coordinate // space BoardLayout uses — into an optional swap intent (A, B): A is the cell // under the drag start, B its orthogonal neighbour along the drag's dominant axis. // Owns no rendering and mutates no model, so it is unit-testable headless; P5.2 // feeds the resulting Swap straight into `commit_swap`. #import "modules/std.sx"; #import "modules/math"; #import "modules/ui/types.sx"; #import "board.sx"; #import "board_layout.sx"; // A drag whose dominant-axis travel is below this fraction of a cell is treated // as a tap, not a swipe, and yields no intent. Scaling to cell_size keeps the // feel constant across screen sizes, since the layout sizes cells to the device. SWIPE_THRESHOLD_FRACTION :f32: 0.5; // Map a drag to the adjacent-swap intent it expresses, or null when the gesture // is not a board swipe. Returns null when: the start point is off the board; the // dominant-axis travel is below the swipe threshold (a tap, not a swipe); or the // resolved neighbour B would fall off the board. A and B are always orthogonally // adjacent — the intent never spans more than one cell. The dominant axis is the // larger of |dx|, |dy| (an exact tie resolves horizontal), and its sign picks the // direction: +x → right, -x → left, +y → down, -y → up (screen y grows downward, // matching `cell_frame`). Reuses `point_to_cell` so the start resolves to exactly // the cell drawn under the finger. swipe_intent :: (layout: *BoardLayout, start: Point, end: Point) -> ?Swap { if a := layout.point_to_cell(start) { dx := end.x - start.x; dy := end.y - start.y; adx := abs(dx); ady := abs(dy); threshold := layout.cell_size * SWIPE_THRESHOLD_FRACTION; if adx < threshold and ady < threshold { return null; } // a tap, not a swipe bcol := a.col; brow := a.row; if adx >= ady { if dx > 0.0 { bcol += 1; } else { bcol -= 1; } } else { if dy > 0.0 { brow += 1; } else { brow -= 1; } } if bcol < 0 or bcol >= BOARD_COLS or brow < 0 or brow >= BOARD_ROWS { return null; // neighbour off the board } return Swap.{ a = a, b = Cell.{ col = bcol, row = brow } }; } null // start off the board }