package com.rizoma.trazo.model import kotlin.math.atan2 import kotlin.math.hypot /** Las 9 direcciones de una tecla: centro (toque) + 8 swipes. */ enum class Dir { C, N, NE, E, SE, S, SW, W, NW } /** * Una tecla del grid 3x3. [center] es el toque; [swipes] mapea cada dirección * de deslizamiento a su carácter. Todo es dato → la distribución se ajusta sin tocar UI. */ data class KeyDef( val center: Char, val swipes: Map = emptyMap(), ) { fun charFor(dir: Dir): Char? = if (dir == Dir.C) center else swipes[dir] } object Layouts { /** * Letras — remaster del espíritu MessagEase para español. * Los 9 toques son las letras más frecuentes en su posición MessagEase clásica * (A N I / H O R / T E S). Las 8 direcciones del centro (O) llevan las secundarias * frecuentes; las raras y la Ñ van en los bordes. Tweakable a gusto. */ val letters: List = listOf( // fila superior KeyDef('a', mapOf(Dir.E to 'q', Dir.S to 'z')), KeyDef('n', mapOf(Dir.S to 'f', Dir.E to 'v')), KeyDef('i', mapOf(Dir.S to 'j', Dir.W to 'k')), // fila media KeyDef('h', mapOf(Dir.N to 'w', Dir.S to 'x')), KeyDef( 'o', mapOf( Dir.N to 'u', Dir.NE to 'b', Dir.E to 'c', Dir.SE to 'd', Dir.S to 'g', Dir.SW to 'p', Dir.W to 'm', Dir.NW to 'l', ), ), KeyDef('r', mapOf(Dir.S to 'y')), // fila inferior KeyDef('t', mapOf(Dir.N to 'ñ')), KeyDef('e', emptyMap()), KeyDef('s', emptyMap()), ) /** * Números y símbolos. Dígitos 1-9 en posición de marcador telefónico; 0 y la * puntuación frecuente como swipes. */ val symbols: List = listOf( KeyDef('1', mapOf(Dir.N to '!', Dir.E to '@')), KeyDef('2', mapOf(Dir.N to '"', Dir.E to '#')), KeyDef('3', mapOf(Dir.N to '\'', Dir.W to '$')), KeyDef('4', mapOf(Dir.N to '%', Dir.E to '&')), KeyDef('5', mapOf(Dir.N to '*', Dir.E to '+', Dir.W to '-', Dir.S to '=')), KeyDef('6', mapOf(Dir.N to '/', Dir.W to '\\')), KeyDef('7', mapOf(Dir.N to '(', Dir.E to ')')), KeyDef('8', mapOf(Dir.N to ',', Dir.E to '.', Dir.W to '?')), KeyDef('9', mapOf(Dir.N to ';', Dir.W to ':', Dir.E to '0')), ) } /** Geometría: convierte el delta de un gesto en una de las 9 direcciones. */ fun directionOf(dx: Float, dy: Float, tapRadiusPx: Float): Dir { if (hypot(dx, dy) < tapRadiusPx) return Dir.C var deg = Math.toDegrees(atan2(dy.toDouble(), dx.toDouble())) // 0=E, 90=S (y hacia abajo) if (deg < 0) deg += 360.0 // 8 sectores de 45° centrados en cada eje. val order = arrayOf(Dir.E, Dir.SE, Dir.S, Dir.SW, Dir.W, Dir.NW, Dir.N, Dir.NE) val idx = (((deg + 22.5) / 45.0).toInt()) % 8 return order[idx] }