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 }