juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

layout.c (26160B)


      1 /* layout.c - disposicion 2D (ver layout.h). */
      2 #include <string.h>
      3 #include "layout.h"
      4 
      5 /* ------------------------------------------------------------ glifos */
      6 
      7 static const struct { uint16_t cp; uint8_t code; } CPS[] = {
      8     { 0x03C0, CF_PI }, { 0x221A, CF_SQRT }, { 0x207B, CF_SUPMINUS }, { 0x2264, CF_LE },
      9     { 0x2265, CF_GE }, { 0x2260, CF_NE }, { 0x2192, CF_ARROW }, { 0x2220, CF_ANGLE },
     10     { 0x25A1, CF_BOX }, { 0x03A3, CF_SIGMA }, { 0x03C3, CF_SIGMAS }, { 0x03B8, CF_THETA },
     11     { 0x221E, CF_INF }, { 0x21D4, CF_SD }, { 0x25B6, CF_TRI_R }, { 0x25BC, CF_TRI_D },
     12     { 0x25B2, CF_TRI_U }, { 0x25C0, CF_TRI_L }, { 0x1D07, CF_SMALLE },
     13     { 0x2190, CF_LARROW }, { 0x2191, CF_UARROW }, { 0x2193, CF_DARROW }, { 0x221B, CF_CBRT }, { 0x2248, CF_APPROX },
     14     { 0x03A0, CF_BIGPI }, { 0x222B, CF_INTEG }, { 0x2032, CF_PRIME }, { 0x2033, CF_DPRIME },
     15 };
     16 static const char *const CF_TXT[] = {
     17     [CF_XHAT - CF_PI] = "x\xcc\x82", [CF_YHAT - CF_PI] = "y\xcc\x82",
     18     [CF_XBAR - CF_PI] = "x\xcc\x84", [CF_YBAR - CF_PI] = "y\xcc\x84",
     19 };
     20 
     21 int cf_code(int cp)
     22 {
     23     if (cp >= 32 && cp < 256 && !(cp >= 0x80 && cp < 0xA0)) return cp;
     24     for (size_t i = 0; i < sizeof CPS / sizeof CPS[0]; i++) if (CPS[i].cp == cp) return CPS[i].code;
     25     return '?';
     26 }
     27 
     28 static int put_utf8(int cp, char *o)
     29 {
     30     if (cp < 0x80) { o[0] = (char)cp; o[1] = 0; return 1; }
     31     if (cp < 0x800) { o[0] = (char)(0xC0 | (cp >> 6)); o[1] = (char)(0x80 | (cp & 63)); o[2] = 0; return 2; }
     32     o[0] = (char)(0xE0 | (cp >> 12)); o[1] = (char)(0x80 | ((cp >> 6) & 63)); o[2] = (char)(0x80 | (cp & 63)); o[3] = 0;
     33     return 3;
     34 }
     35 
     36 int cf_utf8(int code, char *out)
     37 {
     38     if (code >= CF_PI && code < 0xA0) {
     39         for (size_t i = 0; i < sizeof CPS / sizeof CPS[0]; i++) if (CPS[i].code == code) return put_utf8(CPS[i].cp, out);
     40         if (code - CF_PI < (int)(sizeof CF_TXT / sizeof CF_TXT[0]) && CF_TXT[code - CF_PI]) {
     41             strcpy(out, CF_TXT[code - CF_PI]);
     42             return (int)strlen(out);
     43         }
     44         out[0] = '?'; out[1] = 0;
     45         return 1;
     46     }
     47     return put_utf8(code, out);
     48 }
     49 
     50 int cf_decode(const char *s, uint8_t *codes, int max)
     51 {
     52     int n = 0;
     53     const unsigned char *p = (const unsigned char *)s;
     54     while (*p && n < max) {
     55         int cp;
     56         if (*p < 0x80) cp = *p++;
     57         else if ((*p & 0xE0) == 0xC0 && p[1]) { cp = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F); p += 2; }
     58         else if ((*p & 0xF0) == 0xE0 && p[1] && p[2]) { cp = ((p[0] & 0x0F) << 12) | ((p[1] & 0x3F) << 6) | (p[2] & 0x3F); p += 3; }
     59         else { p++; continue; }
     60         if (cp == 0x302 || cp == 0x304) {                     /* acento combinado: x̂ ŷ x̄ ȳ */
     61             if (n > 0 && (codes[n - 1] == 'x' || codes[n - 1] == 'y'))
     62                 codes[n - 1] = (uint8_t)(cp == 0x302 ? (codes[n - 1] == 'x' ? CF_XHAT : CF_YHAT)
     63                                                     : (codes[n - 1] == 'x' ? CF_XBAR : CF_YBAR));
     64             continue;
     65         }
     66         codes[n++] = (uint8_t)cf_code(cp);
     67     }
     68     return n;
     69 }
     70 
     71 /* glifos de un token comun (sin el "(" de las funciones). */
     72 static int tok_glyphs(int t, uint8_t *g)
     73 {
     74     switch (t) {
     75     case T_MUL: g[0] = 0xD7; return 1;
     76     case T_DIV: g[0] = 0xF7; return 1;
     77     case T_PI: g[0] = CF_PI; return 1;
     78     case T_E: g[0] = 'e'; return 1;
     79     case T_ANS: memcpy(g, "Ans", 3); return 3;
     80     case T_PREANS: memcpy(g, "PreAns", 6); return 6;
     81     case T_RAN: memcpy(g, "Ran#", 4); return 4;
     82     case T_E10: g[0] = CF_SMALLE; return 1;
     83     case T_INV: g[0] = CF_SUPMINUS; g[1] = 0xB9; return 2;
     84     case T_DEGS: g[0] = 0xB0; return 1;
     85     case T_MIN: g[0] = CF_PRIME; return 1;
     86     case T_ANGLE: g[0] = CF_ANGLE; return 1;
     87     case T_MATANS: memcpy(g, "MatAns", 6); return 6;
     88     case T_VCTANS: memcpy(g, "VctAns", 6); return 6;
     89     case T_SEC: g[0] = CF_DPRIME; return 1;
     90     case T_NPR: g[0] = 'P'; return 1;
     91     case T_NCR: g[0] = 'C'; return 1;
     92     }
     93     if (t >= T_VAR && t < T_VAR + NVARS) { g[0] = (uint8_t)VAR_NAMES[t - T_VAR]; return 1; }
     94     if (t >= T_MAT && t < T_MATANS) { memcpy(g, "Mat", 3); g[3] = (uint8_t)('A' + t - T_MAT); return 4; }
     95     if (t >= T_VCT && t < T_VCTANS) { memcpy(g, "Vct", 3); g[3] = (uint8_t)('A' + t - T_VCT); return 4; }
     96     if (t >= T_FN && t < T_FN + FN_COUNT) {
     97         int n = cf_decode(FN_INFO[t - T_FN].shown, g, 10);
     98         return n > 0 && g[n - 1] == '(' ? n - 1 : n;
     99     }
    100     if (t >= 32 && t < 127) { g[0] = (uint8_t)t; return 1; }
    101     g[0] = '?';
    102     return 1;
    103 }
    104 
    105 /* ------------------------------------------------------------ medir */
    106 
    107 static const Metric *M;
    108 static const Expr *EX;
    109 static int16_t bw[EXPR_MAX], ba[EXPR_MAX], bd[EXPR_MAX];     /* caja de plantilla o de campo (en SEP/END) */
    110 static int16_t pa[EXPR_MAX], pd[EXPR_MAX], pm[EXPR_MAX];     /* parentesis: contenido; ")" -> "(" */
    111 static uint8_t simple[EXPR_MAX];                              /* exponente "simple" (terminal: superindice) */
    112 
    113 #define NEST 32                 /* plantillas anidadas (mas profundo no se dibuja bien, pero no rompe) */
    114 #define PNEST 12                /* parentesis abiertos por fila */
    115 typedef struct {
    116     int16_t tok, type, field, lvl, plvl;
    117     int16_t w, a, d;
    118     int16_t ps[PNEST], psa[PNEST], psd[PNEST], psp;
    119     int16_t fend[3];
    120     bool sub;
    121 } MFrame;
    122 
    123 static MFrame MF[NEST + 1];
    124 
    125 static int fld_lvl(int type, int f, int l)
    126 {
    127     if (tok_is_pow(type)) return 1;
    128     if ((type == T_ROOT || type == T_LOGB) && f == 0) return 1;
    129     if ((type == T_INTEG || type == T_SUM || type == T_PROD) && f < 2) return 1;
    130     if (type == T_DERIV && f == 1) return 1;
    131     return l;
    132 }
    133 
    134 /* campos que van como subindice (en la terminal: ₀₁₂) */
    135 static bool fld_sub(int type, int f)
    136 {
    137     return (type == T_LOGB && f == 0) || ((type == T_INTEG || type == T_SUM || type == T_PROD) && f == 0) ||
    138            (type == T_DERIV && f == 1);
    139 }
    140 
    141 static int imax(int a, int b) { return a > b ? a : b; }
    142 
    143 /* alto del signo de la integral: el integrando, y que entren los dos limites */
    144 static void integ_dims(int l, int f0, int f1, int f2, int *As, int *Ds)
    145 {
    146     int ex = M->cells ? 1 : 3;
    147     *As = imax(ba[f2], M->asc[l]) + ex;
    148     *Ds = imax(bd[f2], M->desc[l]) + ex;
    149     int need = ba[f0] + bd[f0] + ba[f1] + bd[f1] + (M->cells ? 0 : 2);
    150     while (*As + *Ds < need) { (*As)++; if (*As + *Ds < need) (*Ds)++; }
    151 }
    152 
    153 /* medidas comunes de ∫, Σ/Π y d/dx */
    154 static int big_w(int l) { return M->cells ? 1 : (l ? 9 : 12); }
    155 
    156 
    157 static void row_add(MFrame *F, int w, int a, int d)
    158 {
    159     F->w = (int16_t)(F->w + w);
    160     if (a > F->a) F->a = (int16_t)a;
    161     if (d > F->d) F->d = (int16_t)d;
    162 }
    163 
    164 static void glyph_box(MFrame *F, int n)
    165 {
    166     int l = F->lvl > 0 ? 1 : 0;
    167     row_add(F, n * M->cw[l], M->asc[l], M->desc[l]);
    168 }
    169 
    170 static void close_parens(MFrame *F)
    171 {
    172     int l = F->lvl > 0 ? 1 : 0;
    173     while (F->psp) {
    174         F->psp--;
    175         int o = F->ps[F->psp];
    176         pa[o] = (int16_t)imax(F->a, M->asc[l]);
    177         pd[o] = (int16_t)imax(F->d, M->desc[l]);
    178         F->a = (int16_t)imax(F->a, F->psa[F->psp]);
    179         F->d = (int16_t)imax(F->d, F->psd[F->psp]);
    180     }
    181 }
    182 
    183 /* exponente de solo digitos / signo (en la terminal va como superindice) */
    184 static bool is_simple(int from, int to)
    185 {
    186     if (from >= to) return false;
    187     for (int k = from; k < to; k++) {
    188         int t = EX->t[k];
    189         if (!((t >= '0' && t <= '9') || t == '-' || t == '+' || t == '.' || (t >= 'a' && t <= 'z') ||
    190               (t >= T_VAR && t < T_VAR + NVARS) || t == T_PI)) return false;
    191     }
    192     return true;
    193 }
    194 
    195 static void tpl_box(MFrame *T, int *W, int *A, int *D)
    196 {
    197     int l = T->plvl > 0 ? 1 : 0, t = T->type;
    198     int f0 = T->fend[0], f1 = T->fend[1], f2 = T->fend[2];
    199     int w0 = bw[f0], a0 = ba[f0], d0 = bd[f0];
    200     int w1 = f1 >= 0 ? bw[f1] : 0, a1 = f1 >= 0 ? ba[f1] : 0, d1 = f1 >= 0 ? bd[f1] : 0;
    201     switch (t) {
    202     case T_FRAC: case T_FRACL:
    203         *W = imax(w0, w1) + 2 * M->pad;
    204         *A = M->ax[l] + M->gap + a0 + d0;
    205         *D = imax(M->bar + M->gap + a1 + d1 - M->ax[l], 0);
    206         break;
    207     case T_SQRT:
    208         *W = M->rad_w + w0 + M->pad;
    209         *A = a0 + M->rad_gap + M->bar;
    210         *D = d0;
    211         break;
    212     case T_ROOT: {
    213         int As = a1 + M->rad_gap + M->bar, Ds = d1, extra = imax(0, w0 - M->rad_w / 2);
    214         int ib = -As + (As + Ds) / 2, it = ib - (a0 + d0);
    215         *W = extra + M->rad_w + w1 + M->pad;
    216         *A = imax(As, -it);
    217         *D = Ds;
    218         break;
    219     }
    220     case T_POW: case T_POWL: {
    221         int r = simple[T->tok] ? 0 : M->raise[l];
    222         *W = w0;
    223         *A = r + a0;
    224         *D = imax(0, d0 - r);
    225         break;
    226     }
    227     case T_LOGB: {
    228         int drop = simple[T->tok] ? 0 : M->drop;
    229         *W = 3 * M->cw[l] + w0 + 2 * M->cw[l] + w1;
    230         *A = imax(imax(M->asc[l], a1), a0 - drop);
    231         *D = imax(imax(M->desc[l], d1), drop + d0);
    232         break;
    233     }
    234     case T_ABS:
    235         *W = 2 * M->abs_w + w0;
    236         *A = a0 + (M->cells ? 0 : 1);
    237         *D = d0 + (M->cells ? 0 : 1);
    238         break;
    239     case T_MIXED: {
    240         int w2 = bw[f2], a2 = ba[f2], d2 = bd[f2];
    241         *W = w0 + imax(w1, w2) + 2 * M->pad;
    242         *A = imax(a0, M->ax[l] + M->gap + a1 + d1);
    243         *D = imax(d0, M->bar + M->gap + a2 + d2 - M->ax[l]);
    244         break;
    245     }
    246     case T_INTEG: {
    247         int w2 = bw[f2], As, Ds;
    248         integ_dims(l, f0, f1, f2, &As, &Ds);
    249         *W = M->rad_w + imax(w0, w1) + (M->cells ? 1 : 2) + w2 + (M->cells ? 1 : 2) + 2 * M->cw[l];
    250         *A = As;                                    /* el limite de arriba va adentro */
    251         *D = Ds;
    252         break;
    253     }
    254     case T_SUM: case T_PROD: {
    255         int w2 = bw[f2], a2 = ba[f2], d2 = bd[f2], g = M->cells ? 0 : 1;
    256         int As = M->cells ? 1 : M->asc[l] + 1, Ds = M->cells ? 0 : M->desc[l] + 1;
    257         int wl = 2 * M->cw[1] + w0, col = imax(imax(big_w(l), wl), w1);
    258         *W = col + (M->cells ? 0 : 2) + M->cw[l] + w2 + M->cw[l];
    259         *A = imax(a2, As + g + a1 + d1);
    260         *D = imax(d2, Ds + g + a0 + d0);
    261         break;
    262     }
    263     case T_DERIV: {
    264         int h1 = M->asc[1] + M->desc[1];
    265         int pw = M->cells ? 4 : 2 * M->cw[1] + 2;
    266         int Ap = M->cells ? 1 : M->ax[l] + 1 + h1, Dp = M->cells ? 0 : 1 + h1 - M->ax[l];
    267         int drop = simple[T->tok] ? 0 : M->drop;
    268         *W = pw + M->cw[l] + w0 + M->cw[l] + M->abs_w + 2 * M->cw[1] + w1;
    269         *A = imax(imax(Ap, a0), M->asc[l]);
    270         *D = imax(imax(Dp, d0), drop + d1);
    271         break;
    272     }
    273     default: *W = w0; *A = a0; *D = d0;
    274     }
    275 }
    276 
    277 static void measure(int *W, int *A, int *D)
    278 {
    279     int sp = 0;
    280     memset(&MF[0], 0, sizeof MF[0]);
    281     MF[0].tok = -1;
    282     const Expr *e = EX;
    283     for (int i = 0; i < e->n; i++) {
    284         int t = e->t[i];
    285         MFrame *F = &MF[sp];
    286         if (tok_is_tpl(t)) {
    287             if (sp >= NEST) continue;
    288             MFrame *N = &MF[++sp];
    289             memset(N, 0, sizeof *N);
    290             N->tok = (int16_t)i; N->type = (int16_t)t; N->plvl = F->lvl;
    291             N->lvl = (int16_t)fld_lvl(t, 0, F->lvl);
    292             N->fend[0] = N->fend[1] = N->fend[2] = -1;
    293             N->sub = F->sub || fld_sub(t, 0);
    294             continue;
    295         }
    296         if ((t == T_SEP || t == T_END) && sp > 0) {
    297             close_parens(F);
    298             int l = F->lvl > 0 ? 1 : 0;
    299             if (F->w == 0) { F->w = M->cw[l]; F->a = M->asc[l]; F->d = M->desc[l]; }   /* casilla vacia */
    300             bw[i] = F->w; ba[i] = F->a; bd[i] = F->d;
    301             if (F->field < 3) F->fend[F->field] = (int16_t)i;
    302             int fstart = F->field == 0 ? F->tok + 1 : F->fend[F->field - 1] + 1;
    303             if ((tok_is_pow(F->type) || (F->type == T_LOGB && F->field == 0) || (F->type == T_DERIV && F->field == 1)) &&
    304                 M->cells && is_simple(fstart, i))
    305                 simple[F->tok] = 1;
    306             if (t == T_SEP) {
    307                 F->field++;
    308                 F->lvl = (int16_t)fld_lvl(F->type, F->field, F->plvl);
    309                 F->w = F->a = F->d = 0;
    310                 F->psp = 0;
    311                 F->sub = MF[sp - 1].sub || fld_sub(F->type, F->field);
    312                 continue;
    313             }
    314             int Wt, At, Dt;
    315             tpl_box(F, &Wt, &At, &Dt);
    316             bw[F->tok] = (int16_t)Wt; ba[F->tok] = (int16_t)At; bd[F->tok] = (int16_t)Dt;
    317             sp--;
    318             row_add(&MF[sp], Wt, At, Dt);
    319             continue;
    320         }
    321         if (t == '(' || (t >= T_FN && t < T_FN + FN_COUNT)) {
    322             uint8_t g[12];
    323             if (t != '(') glyph_box(F, tok_glyphs(t, g));
    324             glyph_box(F, 1);
    325             if (F->psp < PNEST) {
    326                 F->ps[F->psp] = (int16_t)i; F->psa[F->psp] = F->a; F->psd[F->psp] = F->d;
    327                 F->psp++;
    328                 F->a = F->d = 0;
    329             }
    330             continue;
    331         }
    332         if (t == ')' && F->psp) {
    333             int l = F->lvl > 0 ? 1 : 0;
    334             F->psp--;
    335             int o = F->ps[F->psp];
    336             pa[o] = (int16_t)imax(F->a, M->asc[l]);
    337             pd[o] = (int16_t)imax(F->d, M->desc[l]);
    338             pm[i] = (int16_t)o;
    339             F->a = (int16_t)imax(pa[o], F->psa[F->psp]);
    340             F->d = (int16_t)imax(pd[o], F->psd[F->psp]);
    341             glyph_box(F, 1);
    342             continue;
    343         }
    344         pm[i] = -1;
    345         uint8_t g[12];
    346         glyph_box(F, tok_glyphs(t, g));
    347     }
    348     while (sp > 0) sp--;                     /* plantilla sin cerrar (no deberia pasar) */
    349     close_parens(&MF[0]);
    350     if (!MF[0].w) { MF[0].a = M->asc[0]; MF[0].d = M->desc[0]; }
    351     *W = MF[0].w; *A = MF[0].a; *D = MF[0].d;
    352 }
    353 
    354 /* ------------------------------------------------------------ ubicar */
    355 
    356 typedef struct { int tok, type, field, lvl, plvl, x, base, X0, B, W, A, D; int fend[3]; bool sub; } PFrame;
    357 static PFrame PF[NEST + 1];
    358 static Layout *L;
    359 
    360 static void item(int kind, int code, int lvl, int x, int y, int w, int h)
    361 {
    362     if (L->n >= LAYOUT_ITEMS) return;
    363     LItem *it = &L->it[L->n++];
    364     it->kind = (uint8_t)kind; it->code = (uint8_t)code; it->lvl = (uint8_t)lvl;
    365     it->x = (int16_t)x; it->y = (int16_t)y; it->w = (int16_t)w; it->h = (int16_t)h;
    366 }
    367 
    368 static void glyphs(PFrame *F, const uint8_t *g, int n)
    369 {
    370     int l = F->lvl > 0 ? 1 : 0, show = F->sub && F->lvl > 0 ? 2 : F->lvl > 0 ? 1 : 0;
    371     /* limites de ∫ Σ Π en la terminal: van en su propia fila, con letra normal */
    372     if (M->cells && F->field < 2 && (F->type == T_INTEG || F->type == T_SUM || F->type == T_PROD)) show = 3;
    373     if (M->cells && F->type == T_DERIV && F->field == 1) show = 3;
    374     for (int k = 0; k < n; k++) {
    375         item(LI_GLYPH, g[k], show, F->x, F->base - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    376         F->x += M->cw[l];
    377     }
    378 }
    379 
    380 static void paren(PFrame *F, int code, int open)
    381 {
    382     int l = F->lvl > 0 ? 1 : 0;
    383     int a = pa[open], d = pd[open];
    384     if (a + d > M->asc[l] + M->desc[l]) {
    385         item(LI_PAREN, code, l, F->x, F->base - a, M->cw[l], a + d);
    386         F->x += M->cw[l];
    387     } else {
    388         uint8_t g = (uint8_t)code;
    389         glyphs(F, &g, 1);
    390     }
    391 }
    392 
    393 /* origen del campo f de la plantilla */
    394 static void field_origin(PFrame *T, int f, int *x, int *base)
    395 {
    396     int l = T->plvl > 0 ? 1 : 0;
    397     int f0 = T->fend[0], f1 = T->fend[1], f2 = T->fend[2];
    398     int X0 = T->X0, B = T->B, W = T->W;
    399     *x = X0; *base = B;
    400     switch (T->type) {
    401     case T_FRAC: case T_FRACL: {
    402         int inner = W - 2 * M->pad;
    403         if (f == 0) { *x = X0 + M->pad + (inner - bw[f0]) / 2; *base = B - M->ax[l] - M->gap - bd[f0]; }
    404         else { *x = X0 + M->pad + (inner - bw[f1]) / 2; *base = B - M->ax[l] + M->bar + M->gap + ba[f1]; }
    405         break;
    406     }
    407     case T_SQRT: *x = X0 + M->rad_w; break;
    408     case T_ROOT: {
    409         int As = ba[f1] + M->rad_gap + M->bar, Ds = bd[f1], extra = imax(0, bw[f0] - M->rad_w / 2);
    410         if (f == 0) {
    411             int ib = -As + (As + Ds) / 2;
    412             *x = X0 + extra + M->rad_w / 2 - bw[f0];
    413             *base = B + ib - bd[f0];
    414         } else *x = X0 + extra + M->rad_w;
    415         break;
    416     }
    417     case T_POW: case T_POWL:
    418         *base = B - (simple[T->tok] ? 0 : M->raise[l]);
    419         break;
    420     case T_LOGB:
    421         if (f == 0) { *x = X0 + 3 * M->cw[l]; *base = B + (simple[T->tok] ? 0 : M->drop); }
    422         else *x = X0 + 3 * M->cw[l] + bw[f0] + M->cw[l];
    423         break;
    424     case T_ABS: *x = X0 + M->abs_w; break;
    425     case T_INTEG: {
    426         int As, Ds;
    427         integ_dims(l, f0, f1, f2, &As, &Ds);
    428         if (f == 0) { *x = X0 + M->rad_w; *base = B + Ds - bd[f0]; }
    429         else if (f == 1) { *x = X0 + M->rad_w; *base = B - As + ba[f1]; }
    430         else *x = X0 + M->rad_w + imax(bw[f0], bw[f1]) + (M->cells ? 1 : 2);
    431         break;
    432     }
    433     case T_SUM: case T_PROD: {
    434         int g = M->cells ? 0 : 1, As = M->cells ? 1 : M->asc[l] + 1, Ds = M->cells ? 0 : M->desc[l] + 1;
    435         int wl = 2 * M->cw[1] + bw[f0], col = imax(imax(big_w(l), wl), bw[f1]);
    436         if (f == 0) { *x = X0 + (col - wl) / 2 + 2 * M->cw[1]; *base = B + Ds + g + ba[f0]; }
    437         else if (f == 1) { *x = X0 + (col - bw[f1]) / 2; *base = B - As - g - bd[f1]; }
    438         else *x = X0 + col + (M->cells ? 0 : 2) + M->cw[l];
    439         break;
    440     }
    441     case T_DERIV: {
    442         int pw = M->cells ? 4 : 2 * M->cw[1] + 2;
    443         if (f == 0) *x = X0 + pw + M->cw[l];
    444         else { *x = X0 + pw + 2 * M->cw[l] + bw[f0] + M->abs_w + 2 * M->cw[1]; *base = B + (simple[T->tok] ? 0 : M->drop); }
    445         break;
    446     }
    447     case T_MIXED: {
    448         if (f == 0) break;
    449         int fx = X0 + bw[f0], inner = W - bw[f0] - 2 * M->pad;
    450         if (f == 1) { *x = fx + M->pad + (inner - bw[f1]) / 2; *base = B - M->ax[l] - M->gap - bd[f1]; }
    451         else { *x = fx + M->pad + (inner - bw[f2]) / 2; *base = B - M->ax[l] + M->bar + M->gap + ba[f2]; }
    452         break;
    453     }
    454     }
    455 }
    456 
    457 /* dibujo propio de la plantilla (barras, signo de raiz, "log", parentesis del log) */
    458 static void tpl_items(PFrame *T)
    459 {
    460     int l = T->plvl > 0 ? 1 : 0;
    461     int X0 = T->X0, B = T->B, W = T->W, A = T->A, D = T->D;
    462     int f0 = T->fend[0], f1 = T->fend[1];
    463     switch (T->type) {
    464     case T_FRAC: case T_FRACL:
    465         item(LI_HBAR, 0, l, X0, B - M->ax[l], W, M->bar);
    466         break;
    467     case T_SQRT:
    468         item(LI_RAD, 0, l, X0, B - A, M->rad_w, A + D);
    469         item(LI_HBAR, 0, l, X0 + M->rad_w, B - A, bw[f0] + M->pad, M->bar);
    470         break;
    471     case T_ROOT: {
    472         int As = ba[f1] + M->rad_gap + M->bar, extra = imax(0, bw[f0] - M->rad_w / 2);
    473         item(LI_RAD, 0, l, X0 + extra, B - As, M->rad_w, As + D);
    474         item(LI_HBAR, 0, l, X0 + extra + M->rad_w, B - As, bw[f1] + M->pad, M->bar);
    475         break;
    476     }
    477     case T_LOGB: {
    478         static const uint8_t lg[3] = { 'l', 'o', 'g' };
    479         for (int k = 0; k < 3; k++)
    480             item(LI_GLYPH, lg[k], l, X0 + k * M->cw[l], B - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    481         int px = X0 + 3 * M->cw[l] + bw[f0];
    482         int a = imax(ba[f1], M->asc[l]), d = imax(bd[f1], M->desc[l]);
    483         bool tall = a + d > M->asc[l] + M->desc[l];
    484         if (tall) {
    485             item(LI_PAREN, '(', l, px, B - a, M->cw[l], a + d);
    486             item(LI_PAREN, ')', l, X0 + W - M->cw[l], B - a, M->cw[l], a + d);
    487         } else {
    488             item(LI_GLYPH, '(', l, px, B - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    489             item(LI_GLYPH, ')', l, X0 + W - M->cw[l], B - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    490         }
    491         break;
    492     }
    493     case T_ABS:
    494         item(LI_ABS, 0, l, X0, B - A, M->abs_w, A + D);
    495         item(LI_ABS, 0, l, X0 + W - M->abs_w, B - A, M->abs_w, A + D);
    496         break;
    497     case T_MIXED: {
    498         int fx = X0 + bw[f0];
    499         item(LI_HBAR, 0, l, fx, B - M->ax[l], W - bw[f0], M->bar);
    500         break;
    501     }
    502     case T_INTEG: {
    503         int f2 = T->fend[2], As, Ds;
    504         integ_dims(l, f0, f1, f2, &As, &Ds);
    505         item(LI_INTEG, 0, l, X0, B - As, M->rad_w, As + Ds);
    506         item(LI_GLYPH, 'd', l, X0 + W - 2 * M->cw[l], B - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    507         item(LI_GLYPH, 'x', l, X0 + W - M->cw[l], B - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    508         break;
    509     }
    510     case T_SUM: case T_PROD: {
    511         int f2 = T->fend[2], g = M->cells ? 0 : 1, As = M->cells ? 1 : M->asc[l] + 1, Ds = M->cells ? 0 : M->desc[l] + 1;
    512         int wl = 2 * M->cw[1] + bw[f0], col = imax(imax(big_w(l), wl), bw[f1]);
    513         item(LI_BIGOP, T->type == T_SUM ? 'S' : 'P', l, X0 + (col - big_w(l)) / 2, B - As, big_w(l), As + Ds);
    514         int lb = B + Ds + g + ba[f0], lx = X0 + (col - wl) / 2, sub = M->cells ? 2 : 1;
    515         (void)sub;
    516         item(LI_GLYPH, 'x', M->cells ? 3 : 1, lx, lb - M->asc[1], M->cw[1], M->asc[1] + M->desc[1]);
    517         item(LI_GLYPH, '=', M->cells ? 3 : 1, lx + M->cw[1], lb - M->asc[1], M->cw[1], M->asc[1] + M->desc[1]);
    518         int px = X0 + col + (M->cells ? 0 : 2);
    519         int a = imax(ba[f2], M->asc[l]), d = imax(bd[f2], M->desc[l]);
    520         int kind = a + d > M->asc[l] + M->desc[l] ? LI_PAREN : LI_GLYPH;
    521         int py = kind == LI_PAREN ? B - a : B - M->asc[l], ph = kind == LI_PAREN ? a + d : M->asc[l] + M->desc[l];
    522         item(kind, '(', l, px, py, M->cw[l], ph);
    523         item(kind, ')', l, X0 + W - M->cw[l], py, M->cw[l], ph);
    524         break;
    525     }
    526     case T_DERIV: {
    527         int h1 = M->asc[1] + M->desc[1], pw = M->cells ? 4 : 2 * M->cw[1] + 2;
    528         if (M->cells) {
    529             static const uint8_t dd[4] = { 'd', '/', 'd', 'x' };
    530             for (int k = 0; k < 4; k++) item(LI_GLYPH, dd[k], l, X0 + k, B - 1, 1, 1);
    531         } else {
    532             int yb = B - M->ax[l];
    533             item(LI_GLYPH, 'd', 1, X0 + 1 + M->cw[1] / 2, yb - h1, M->cw[1], h1);
    534             item(LI_HBAR, 0, l, X0, yb, pw, 1);
    535             item(LI_GLYPH, 'd', 1, X0 + 1, yb + 1, M->cw[1], h1);
    536             item(LI_GLYPH, 'x', 1, X0 + 1 + M->cw[1], yb + 1, M->cw[1], h1);
    537         }
    538         int px = X0 + pw;
    539         int a = imax(ba[f0], M->asc[l]), d = imax(bd[f0], M->desc[l]);
    540         int kind = a + d > M->asc[l] + M->desc[l] ? LI_PAREN : LI_GLYPH;
    541         int py = kind == LI_PAREN ? B - a : B - M->asc[l], ph = kind == LI_PAREN ? a + d : M->asc[l] + M->desc[l];
    542         item(kind, '(', l, px, py, M->cw[l], ph);
    543         int cx = px + M->cw[l] + bw[f0];
    544         item(kind, ')', l, cx, py, M->cw[l], ph);
    545         item(LI_ABS, 0, l, cx + M->cw[l], B - a, M->abs_w, a + d + (simple[T->tok] ? 0 : M->drop / 2));
    546         int xb = B + (simple[T->tok] ? 0 : M->drop), xx = cx + M->cw[l] + M->abs_w, sub = M->cells ? 2 : 1;
    547         item(LI_GLYPH, 'x', sub, xx, xb - M->asc[1], M->cw[1], h1);
    548         item(LI_GLYPH, '=', sub, xx + M->cw[1], xb - M->asc[1], M->cw[1], h1);
    549         break;
    550     }
    551     }
    552 }
    553 
    554 static void gap(PFrame *F, int i)
    555 {
    556     int l = F->lvl > 0 ? 1 : 0;
    557     L->gx[i] = (int16_t)F->x;
    558     L->gy[i] = (int16_t)(F->base - M->asc[l]);
    559     L->gh[i] = (int16_t)(M->asc[l] + M->desc[l]);
    560     L->glvl[i] = (uint8_t)l;
    561 }
    562 
    563 /* casilla vacia: el campo que empieza en 'from' termina en 'to' sin nada */
    564 static void empty_box(PFrame *F)
    565 {
    566     int l = F->lvl > 0 ? 1 : 0;
    567     item(LI_BOX, CF_BOX, F->sub && F->lvl > 0 ? 2 : l, F->x, F->base - M->asc[l], M->cw[l], M->asc[l] + M->desc[l]);
    568 }
    569 
    570 static void fields_of(int i, int fend[3])
    571 {
    572     int d = 0, f = 0;
    573     fend[0] = fend[1] = fend[2] = -1;
    574     for (int k = i + 1; k < EX->n; k++) {
    575         int t = EX->t[k];
    576         if (tok_is_tpl(t)) d++;
    577         else if (t == T_END) { if (d == 0) { if (f < 3) fend[f] = k; return; } d--; }
    578         else if (t == T_SEP && d == 0 && f < 3) fend[f++] = k;
    579     }
    580 }
    581 
    582 static void place(int A0)
    583 {
    584     int sp = 0;
    585     PFrame *F = &PF[0];
    586     memset(F, 0, sizeof *F);
    587     F->tok = -1; F->x = 0; F->base = A0;
    588     const Expr *e = EX;
    589     for (int i = 0; i < e->n; i++) {
    590         int t = e->t[i];
    591         F = &PF[sp];
    592         gap(F, i);
    593         if (tok_is_tpl(t)) {
    594             if (sp >= NEST) continue;
    595             PFrame *T = &PF[++sp];
    596             memset(T, 0, sizeof *T);
    597             T->tok = i; T->type = t; T->plvl = F->lvl;
    598             T->X0 = F->x; T->B = F->base; T->W = bw[i]; T->A = ba[i]; T->D = bd[i];
    599             T->sub = F->sub || fld_sub(t, 0);
    600             fields_of(i, T->fend);
    601             tpl_items(T);
    602             T->field = 0;
    603             T->lvl = fld_lvl(t, 0, F->lvl);
    604             field_origin(T, 0, &T->x, &T->base);
    605             if (e->t[i + 1] == T_SEP || e->t[i + 1] == T_END) empty_box(T);
    606             continue;
    607         }
    608         if ((t == T_SEP || t == T_END) && sp > 0) {
    609             if (t == T_SEP) {
    610                 F->field++;
    611                 F->lvl = fld_lvl(F->type, F->field, F->plvl);
    612                 F->sub = PF[sp - 1].sub || fld_sub(F->type, F->field);
    613                 field_origin(F, F->field, &F->x, &F->base);
    614                 if (i + 1 < e->n && (e->t[i + 1] == T_SEP || e->t[i + 1] == T_END)) empty_box(F);
    615                 continue;
    616             }
    617             sp--;
    618             PF[sp].x = F->X0 + F->W;
    619             continue;
    620         }
    621         if (t == '(' || (t >= T_FN && t < T_FN + FN_COUNT)) {
    622             uint8_t g[12];
    623             if (t != '(') glyphs(F, g, tok_glyphs(t, g));
    624             paren(F, '(', i);
    625             continue;
    626         }
    627         if (t == ')' && pm[i] >= 0) { paren(F, ')', pm[i]); continue; }
    628         uint8_t g[12];
    629         glyphs(F, g, tok_glyphs(t, g));
    630     }
    631     gap(&PF[0], e->n);
    632     if (sp) { /* sin cerrar */ }
    633 }
    634 
    635 void layout_expr(const Expr *e, const Metric *m, Layout *out)
    636 {
    637     M = m; EX = e; L = out;
    638     memset(simple, 0, sizeof simple);
    639     for (int i = 0; i < e->n; i++) pm[i] = -1;
    640     int W, A, D;
    641     measure(&W, &A, &D);
    642     out->n = 0;
    643     out->w = (int16_t)W; out->asc = (int16_t)A; out->desc = (int16_t)D;
    644     place(A);
    645     if (!e->n) {                                     /* vacia: solo el cursor */
    646         out->gx[0] = 0; out->gy[0] = 0; out->gh[0] = (int16_t)(m->asc[0] + m->desc[0]); out->glvl[0] = 0;
    647     }
    648 }
    649 
    650 int layout_cursor_vert(const Layout *Lt, const Expr *e, int cur, int dir)
    651 {
    652     int y0 = Lt->gy[cur], y1 = y0 + Lt->gh[cur], x0 = Lt->gx[cur];
    653     int best = -1, bdy = 1 << 30, bdx = 1 << 30;
    654     for (int k = 0; k <= e->n; k++) {
    655         int ky0 = Lt->gy[k], ky1 = ky0 + Lt->gh[k], dy;
    656         if (dir < 0) { if (ky1 > y0) continue; dy = y0 - ky1; }
    657         else { if (ky0 < y1) continue; dy = ky0 - y1; }
    658         int dx = Lt->gx[k] - x0;
    659         if (dx < 0) dx = -dx;
    660         if (dy < bdy || (dy == bdy && dx < bdx)) { best = k; bdy = dy; bdx = dx; }
    661     }
    662     return best;
    663 }