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

render.c (16940B)


      1 /* render.c - la calculadora en pixeles (320x320). */
      2 #include <string.h>
      3 #include "calc_gfx.h"
      4 #include "str.h"
      5 
      6 /* fuente 8x13 (base 11) y 6x12 (base 10) */
      7 const Metric METRIC_PX = {
      8     .cw = { 8, 6 }, .asc = { 11, 10 }, .desc = { 2, 2 }, .ax = { 4, 3 },
      9     .gap = 1, .bar = 1, .pad = 2, .rad_w = 8, .rad_gap = 1, .raise = { 6, 4 }, .drop = 4,
     10     .abs_w = 3, .cells = false,
     11 };
     12 
     13 #define BG      RGB(18, 22, 30)
     14 #define PANEL   RGB(30, 36, 50)
     15 #define FG      RGB(236, 236, 236)
     16 #define DIM     RGB(130, 138, 156)
     17 #define ACC     RGB(255, 200, 60)
     18 #define RESC    RGB(140, 220, 255)
     19 #define SEL     RGB(48, 58, 80)
     20 #define CURS    RGB(255, 226, 0)
     21 #define ERRC    RGB(255, 100, 90)
     22 
     23 /* ------------------------------------------------------------ glifos */
     24 
     25 static void glyph(Surface *s, const Font *f, int x, int y, int ch, Color c)
     26 {
     27     if (ch < 32 || ch > 255) ch = '?';
     28     const uint8_t *g = f->bits + (ch - 32) * f->h;
     29     for (int row = 0; row < f->h; row++) {
     30         uint8_t b = g[row];
     31         int col = 0;
     32         while (b && col < 8) {
     33             if (b & 0x80) {
     34                 int st = col;
     35                 while (col < 8 && (b & 0x80)) { b <<= 1; col++; }
     36                 gfx_rect(s, x + st, y + row, col - st, 1, c);
     37             } else { b <<= 1; col++; }
     38         }
     39     }
     40 }
     41 
     42 int cf_draw(Surface *s, const Font *f, int x, int y, const uint8_t *codes, int n, Color c)
     43 {
     44     for (int i = 0; i < n; i++) glyph(s, f, x + i * f->w, y, codes[i], c);
     45     return n * f->w;
     46 }
     47 
     48 int cf_text(Surface *s, const Font *f, int x, int y, const char *t, Color c)
     49 {
     50     uint8_t g[96];
     51     int n = cf_decode(t, g, sizeof g);
     52     return cf_draw(s, f, x, y, g, n, c);
     53 }
     54 
     55 int cf_text_w(const Font *f, const char *t)
     56 {
     57     uint8_t g[96];
     58     return cf_decode(t, g, sizeof g) * f->w;
     59 }
     60 
     61 /* ------------------------------------------------------------ layout */
     62 
     63 void layout_draw(Surface *s, const Layout *L, int x, int y, Color fg, int cursor, Color cc)
     64 {
     65     for (int i = 0; i < L->n; i++) {
     66         const LItem *it = &L->it[i];
     67         int X = x + it->x, Y = y + it->y;
     68         switch (it->kind) {
     69         case LI_GLYPH: {
     70             const Font *f = it->lvl ? &cf_small : &cf_big;
     71             glyph(s, f, X, Y, it->code, fg);
     72             break;
     73         }
     74         case LI_HBAR: gfx_rect(s, X, Y, it->w, it->h, fg); break;
     75         case LI_RAD: {
     76             int h = it->h, w = it->w;
     77             gfx_line(s, X, Y + h - h / 3, X + 2, Y + h - h / 3 - 1, 1, fg);
     78             gfx_line(s, X + 2, Y + h - h / 3 - 1, X + w / 2, Y + h - 1, 1, fg);
     79             gfx_line(s, X + w / 2, Y + h - 1, X + w - 1, Y, 1, fg);
     80             break;
     81         }
     82         case LI_PAREN: {
     83             int h = it->h, w = it->w, open = it->code == '(';
     84             int xo = open ? X + w - 2 : X + 1, xi = open ? X + 2 : X + w - 3;
     85             gfx_line(s, xo, Y + 1, xi, Y + 4, 1, fg);
     86             gfx_rect(s, xi, Y + 4, 1, h - 8, fg);
     87             gfx_line(s, xi, Y + h - 5, xo, Y + h - 2, 1, fg);
     88             break;
     89         }
     90         case LI_ABS: gfx_rect(s, X + it->w / 2, Y, 1, it->h, fg); break;
     91         case LI_BOX: gfx_frame(s, X + 1, Y + 2, it->w - 2, it->h - 3, DIM); break;
     92         case LI_INTEG: {
     93             int cx = X + it->w / 2, b = Y + it->h - 1;
     94             gfx_rect(s, cx, Y + 3, 1, it->h - 6, fg);
     95             gfx_line(s, cx, Y + 3, cx + 1, Y + 1, 1, fg);
     96             gfx_rect(s, cx + 2, Y, 2, 1, fg);
     97             gfx_line(s, cx, b - 3, cx - 1, b - 1, 1, fg);
     98             gfx_rect(s, cx - 3, b, 2, 1, fg);
     99             break;
    100         }
    101         case LI_BIGOP: {
    102             int w = it->w, h = it->h;
    103             gfx_rect(s, X, Y, w, 1, fg);
    104             if (it->code == 'S') {
    105                 gfx_line(s, X, Y, X + w / 2, Y + h / 2, 1, fg);
    106                 gfx_line(s, X + w / 2, Y + h / 2, X, Y + h - 1, 1, fg);
    107                 gfx_rect(s, X, Y + h - 1, w, 1, fg);
    108             } else {
    109                 gfx_rect(s, X + 2, Y, 1, h, fg);
    110                 gfx_rect(s, X + w - 3, Y, 1, h, fg);
    111             }
    112             break;
    113         }
    114         }
    115     }
    116     if (cursor >= 0) {
    117         int cx = x + L->gx[cursor], cy = y + L->gy[cursor];
    118         gfx_rect(s, cx > x ? cx - 1 : cx, cy, 2, L->gh[cursor], cc);
    119     }
    120 }
    121 
    122 /* ------------------------------------------------------------ preparar */
    123 
    124 #define HV 4                       /* entradas del historial que se pueden ver */
    125 /* un solo pozo de layouts para todas las pantallas (la RAM de la Pico es poca) */
    126 Layout g_lay[2 + 2 * HV];
    127 #define L_in   g_lay[0]
    128 #define L_res  g_lay[1]
    129 #define L_hin  (g_lay + 2)
    130 #define L_hres (g_lay + 2 + HV)
    131 static int hv_idx[HV], hv_n;
    132 static int in_scroll;
    133 static int bat_pct = -1;
    134 static Expr tmp;
    135 
    136 /* entrada del historial que se ve en el panel (o -1) y ultima del listado */
    137 static int panel_hist(const Calc *c)
    138 {
    139     if (c->hsel >= 0) return c->hsel;
    140     return c->shown && c->nh ? c->nh - 1 : -1;
    141 }
    142 
    143 void calc_prepare(Calc *c, int bat)
    144 {
    145     bat_pct = bat;
    146     if (c->scr != SCR_MAIN || !calc_main_screen(c)) {
    147         extern void modes_prepare(Calc *c);
    148         if (c->scr == SCR_MAIN) modes_prepare(c);
    149         return;
    150     }
    151     layout_expr(&c->in, &METRIC_PX, &L_in);
    152     if (c->shown && !c->err) {
    153         calc_res_expr(c, &tmp);
    154         layout_expr(&tmp, &METRIC_PX, &L_res);
    155     } else L_res.n = 0;
    156     /* historial anterior al panel, del mas nuevo al mas viejo */
    157     int last = panel_hist(c);
    158     last = last >= 0 ? last - 1 : c->nh - 1;
    159     hv_n = 0;
    160     for (int i = last; i >= 0 && hv_n < HV; i--, hv_n++) {
    161         hv_idx[hv_n] = i;
    162         layout_expr(&c->hist[i].in, &METRIC_PX, &L_hin[hv_n]);
    163         if (!c->hist[i].err) {
    164             calc_hist_expr(c, &c->hist[i], &tmp);
    165             layout_expr(&tmp, &METRIC_PX, &L_hres[hv_n]);
    166         } else L_hres[hv_n].n = 0;
    167     }
    168     /* desplazamiento horizontal para que se vea el cursor */
    169     int avail = 304, cx = L_in.gx[c->in.cur];
    170     if (L_in.w <= avail) in_scroll = 0;
    171     else {
    172         if (cx - in_scroll > avail - 8) in_scroll = cx - avail + 8;
    173         if (cx - in_scroll < 8) in_scroll = cx - 8;
    174         if (in_scroll < 0) in_scroll = 0;
    175         if (in_scroll > L_in.w - avail + 4) in_scroll = L_in.w - avail + 4;
    176     }
    177 }
    178 
    179 bool calc_vert_px(Calc *c, int dir)
    180 {
    181     if (c->scr != SCR_MAIN || c->shown) return false;
    182     layout_expr(&c->in, &METRIC_PX, &L_in);
    183     int k = layout_cursor_vert(&L_in, &c->in, c->in.cur, dir);
    184     if (k < 0) return false;
    185     c->in.cur = k;
    186     c->version++;
    187     return true;
    188 }
    189 
    190 /* ------------------------------------------------------------ pintar */
    191 
    192 static void status_bar(Surface *s, const Calc *c)
    193 {
    194     gfx_rect(s, 0, 0, 320, 14, PANEL);
    195     int x = 3;
    196     if (c->shift) { gfx_rect(s, x - 1, 1, 9, 12, ACC); cf_text(s, &cf_small, x + 1, 1, "S", BG); }
    197     x += 12;
    198     cf_text(s, &cf_small, x, 1, calc_ang_name(c->ang), FG);
    199     x += 10;
    200     static const char *const FM[] = { "", "", "FIX", "SCI", "ENG" };
    201     if (c->fmt.mode >= FMT_FIX) { cf_text(s, &cf_small, x, 1, FM[c->fmt.mode], FG); x += 22; }
    202     if (c->dec_default) { cf_text(s, &cf_small, x, 1, "Dec", FG); x += 22; }
    203     if (c->vars[VAR_M].v.m) { cf_text(s, &cf_small, x, 1, "M", FG); x += 10; }
    204     const char *mn = calc_mode_name(c->mode);
    205     if (c->msg[0]) cf_text(s, &cf_small, 320 - 34 - cf_text_w(&cf_small, c->msg), 1, c->msg, ACC);
    206     else cf_text(s, &cf_small, 160 - cf_text_w(&cf_small, mn) / 2, 1, mn, DIM);
    207     if (bat_pct >= 0) {
    208         char b[8];
    209         int p = str_int(b, sizeof b, 0, bat_pct);
    210         str_put(b, sizeof b, p, "%");
    211         cf_text(s, &cf_small, 318 - cf_text_w(&cf_small, b), 1, b, DIM);
    212     }
    213 }
    214 
    215 static void help_line(Surface *s, const char *t)
    216 {
    217     gfx_rect(s, 0, 306, 320, 14, PANEL);
    218     cf_text(s, &cf_small, 4, 307, t, DIM);
    219 }
    220 
    221 /* entero en base 2/8/16 con el ancho de bits (complemento a 2) o en decimal */
    222 int prog_text(int64_t v, int base, int bits, char *b, int n)
    223 {
    224     if (base == 10) return i64_text(b, n, 0, v);
    225     uint64_t u = (uint64_t)v;
    226     if (bits < 64) u &= (1ull << bits) - 1;
    227     char t[80];
    228     int k = 0, digits = 0;
    229     do {
    230         if (base == 2 && digits && digits % 4 == 0) t[k++] = ' ';
    231         t[k++] = "0123456789ABCDEF"[u % (uint64_t)base];
    232         u /= (uint64_t)base;
    233         digits++;
    234     } while (u && k < 78);
    235     int p = 0;
    236     for (int i = k - 1; i >= 0 && p < n - 1; i--) b[p++] = t[i];
    237     b[p] = 0;
    238     return p;
    239 }
    240 
    241 static void prog_result(Surface *s, const Calc *c, int y)
    242 {
    243     int64_t v;
    244     if (!dec_to_int(c->res.v, &v)) { cf_text(s, &cf_big, 10, y, "No es entero", ERRC); return; }
    245     static const int B[] = { 10, 16, 8, 2 };
    246     static const char *const N[] = { "DEC", "HEX", "OCT", "BIN" };
    247     char b[96];
    248     for (int i = 0; i < 4; i++) {
    249         prog_text(v, B[i], c->pg.bits, b, sizeof b);
    250         bool cur = B[i] == c->pg.base;
    251         cf_text(s, &cf_small, 6, y + i * 13 + 1, N[i], cur ? ACC : DIM);
    252         const Font *f = B[i] == 2 && strlen(b) > 36 ? &cf_small : &cf_big;
    253         cf_text(s, f, 314 - cf_text_w(f, b), y + i * 13, b, cur ? RESC : FG);
    254     }
    255 }
    256 
    257 /* matriz o vector con corchetes, alineado a la derecha en xr; devuelve el alto */
    258 int draw_matrix(Surface *s, const Calc *c, const MVal *m, int xr, int y, Color col)
    259 {
    260     char b[48];
    261     int cw[MAT_N] = { 0 }, w = 0, rows = m->r, h = rows * 14 + 2;
    262     const Font *f = &cf_big;
    263     for (int pass = 0; pass < 2; pass++) {
    264         w = 0;
    265         for (int j = 0; j < m->c; j++) {
    266             cw[j] = 0;
    267             for (int i = 0; i < rows; i++) {
    268                 fmt_plain(mv_get(m, i, j), &c->fmt, false, b, sizeof b);
    269                 int t = cf_text_w(f, b);
    270                 if (t > cw[j]) cw[j] = t;
    271             }
    272             w += cw[j] + 10;
    273         }
    274         if (w + 8 <= xr || f == &cf_small) break;
    275         f = &cf_small;                            /* no entra: letra chica */
    276     }
    277     if (!s) return h;
    278     int x = xr - w - 4;
    279     if (x < 2) x = 2;
    280     gfx_rect(s, x, y, 1, h, col); gfx_rect(s, x, y, 3, 1, col); gfx_rect(s, x, y + h - 1, 3, 1, col);
    281     gfx_rect(s, xr, y, 1, h, col); gfx_rect(s, xr - 2, y, 3, 1, col); gfx_rect(s, xr - 2, y + h - 1, 3, 1, col);
    282     int cx = x + 6;
    283     for (int j = 0; j < m->c; j++) {
    284         for (int i = 0; i < rows; i++) {
    285             fmt_plain(mv_get(m, i, j), &c->fmt, false, b, sizeof b);
    286             cf_text(s, f, cx + cw[j] - cf_text_w(f, b), y + 1 + i * 14, b, col);
    287         }
    288         cx += cw[j] + 10;
    289     }
    290     return h;
    291 }
    292 
    293 static void main_screen(Surface *s, Calc *c)
    294 {
    295     gfx_rect(s, 0, 0, 320, 320, BG);
    296     status_bar(s, c);
    297     /* panel de abajo: entrada y resultado */
    298     bool prog = c->mode == MODE_PROG;
    299     bool mres = c->mode == MODE_MATRIX && c->shown && !c->err && c->mt.res.kind != MV_SCALAR;
    300     int res_h = c->shown ? (c->err ? 16 : prog ? 54 : mres ? draw_matrix(0, c, &c->mt.res, 312, 0, 0) + 6 :
    301                                                   L_res.asc + L_res.desc + 6) : 18;
    302     int in_h = L_in.asc + L_in.desc;
    303     if (in_h < 15) in_h = 15;
    304     int panel_h = in_h + res_h + 10, py = 304 - panel_h;
    305     gfx_rect(s, 0, py, 320, panel_h, PANEL);
    306     gfx_rect(s, 0, py, 320, 1, c->hsel >= 0 ? ACC : DIM);
    307     gfx_clip(s, 4, py + 2, 312, in_h + 4);
    308     layout_draw(s, &L_in, 8 - in_scroll, py + 4, FG, c->shown ? -1 : c->in.cur, CURS);
    309     gfx_noclip(s);
    310     if (in_scroll > 0) cf_text(s, &cf_small, 0, py + 4, "\xe2\x97\x80", ACC);
    311     if (L_in.w - in_scroll > 304) cf_text(s, &cf_small, 314, py + 4, "\xe2\x96\xb6", ACC);
    312     int ry = py + in_h + 8;
    313     if (c->shown) {
    314         if (c->err) cf_text(s, &cf_big, 312 - cf_text_w(&cf_big, calc_err_text(c->err)), ry, calc_err_text(c->err), ERRC);
    315         else if (prog) prog_result(s, c, ry);
    316         else if (mres) draw_matrix(s, c, &c->mt.res, 312, ry + 2, RESC);
    317         else {
    318             int rx = 312 - L_res.w;
    319             if (rx < 4) rx = 4;
    320             layout_draw(s, &L_res, rx, ry + 2, RESC, -1, 0);
    321             if (calc_res_has_exact(c)) cf_text(s, &cf_small, 4, ry + 2, c->want_dec ? "≈" : "", DIM);
    322         }
    323     }
    324     /* historial hacia arriba */
    325     int y = py - 4;
    326     gfx_clip(s, 0, 15, 320, py - 15);
    327     for (int k = 0; k < hv_n && y > 16; k++) {
    328         const Layout *li = &L_hin[k], *lr = &L_hres[k];
    329         int hr = lr->n ? lr->asc + lr->desc : 13, hi = li->asc + li->desc;
    330         y -= hr;
    331         if (lr->n) layout_draw(s, lr, 312 - lr->w, y, RESC, -1, 0);
    332         else cf_text(s, &cf_small, 312 - cf_text_w(&cf_small, "Error"), y, "Error", ERRC);
    333         y -= hi + 1;
    334         layout_draw(s, li, 6, y, DIM, -1, 0);
    335         y -= 4;
    336         gfx_rect(s, 6, y + 1, 308, 1, RGB(34, 40, 54));
    337     }
    338     gfx_noclip(s);
    339     if (prog) {
    340         static const char *const BN[] = { "", "", "BIN", "", "", "", "", "", "OCT", "", "DEC", "", "", "", "", "", "HEX" };
    341         char b[64];
    342         int p = str_put(b, sizeof b, 0, "Entrada en ");
    343         p = str_put(b, sizeof b, p, BN[c->pg.base]);
    344         p = str_put(b, sizeof b, p, ", ");
    345         p = str_int(b, sizeof b, p, c->pg.bits);
    346         str_put(b, sizeof b, p, " bits. Tab cambia");
    347         help_line(s, b);
    348         return;
    349     }
    350     help_line(s, c->sto ? "Elegí la variable: A-F, M, X, Y" :
    351                  c->shown ? "Tab S⇔D  > guardar  ↑ historial  @ menú" :
    352                  "/ fracción ^ potencia # √  @ menú ? funciones");
    353 }
    354 
    355 static void menu_screen(Surface *s, const Calc *c)
    356 {
    357     static const char *const SUB[] = { "cuentas, ∫ d/dx Σ", "sistemas, polinomios, SOLVE", "f(x) en una tabla",
    358                                        "funciones", "hex, bin, bits", "datos, regresión", "a+bi, r∠θ",
    359                                        "matrices y vectores", "ángulo, formato" };
    360     gfx_rect(s, 0, 0, 320, 320, BG);
    361     status_bar(s, c);
    362     cf_text(s, &cf_bold, 12, 20, "Menú", ACC);
    363     for (int i = 0; i <= NMODES; i++) {
    364         int y = 38 + i * 29;
    365         bool sel = i == c->menu_sel;
    366         gfx_box(s, 8, y, 304, 26, sel ? SEL : PANEL, sel ? ACC : RGB(50, 58, 76));
    367         char num[4] = { (char)('1' + i), 0 };
    368         cf_text(s, &cf_bold, 16, y + 7, num, sel ? ACC : DIM);
    369         cf_text(s, &cf_big, 34, y + 7, i < NMODES ? calc_mode_name(i) : "Ajustes", FG);
    370         cf_text(s, &cf_small, 140, y + 8, SUB[i], DIM);
    371     }
    372     help_line(s, "Flechas y Enter, o el número.  Esc vuelve");
    373 }
    374 
    375 static void catalog_screen(Surface *s, const Calc *c)
    376 {
    377     gfx_rect(s, 0, 0, 320, 320, BG);
    378     status_bar(s, c);
    379     const char *title = c->cat < 0 ? "Funciones" : CATALOG[c->cat].name;
    380     cf_text(s, &cf_bold, 12, 20, title, ACC);
    381     int n = c->cat < 0 ? CATALOG_N : CATALOG[c->cat].n;
    382     int rows = 17, top = c->cat_sel >= rows ? c->cat_sel - rows + 1 : 0;
    383     for (int i = top; i < n && i < top + rows; i++) {
    384         int y = 40 + (i - top) * 15;
    385         bool sel = i == c->cat_sel;
    386         if (sel) gfx_rect(s, 8, y - 1, 304, 15, SEL);
    387         char num[4] = { (char)(i < 9 ? '1' + i : ' '), 0 };
    388         cf_text(s, &cf_small, 12, y + 1, num, DIM);
    389         const char *t = c->cat < 0 ? CATALOG[i].name : CATALOG[c->cat].items[i].label;
    390         cf_text(s, &cf_big, 26, y, t, sel ? ACC : FG);
    391     }
    392     help_line(s, c->cat < 0 ? "Enter abre  Esc vuelve" : "Enter inserta  ← vuelve  Esc vuelve");
    393 }
    394 
    395 static void keytest_screen(Surface *s, const Calc *c)
    396 {
    397     gfx_rect(s, 0, 0, 320, 320, BG);
    398     status_bar(s, c);
    399     cf_text(s, &cf_bold, 12, 22, "Probar el teclado", ACC);
    400     cf_text(s, &cf_small, 12, 44, "Apretá teclas: muestra el código que llega.", DIM);
    401     cf_text(s, &cf_small, 12, 56, "Esc dos veces seguidas para salir.", DIM);
    402     char b[48];
    403     int k = c->keytest, p = 0;
    404     if (k < 0) p = str_put(b, sizeof b, 0, "-");
    405     else if (k >= K_RAW) { p = str_put(b, sizeof b, 0, "crudo 0x"); static const char H[] = "0123456789ABCDEF"; char h[3] = { H[(k >> 4) & 15], H[k & 15], 0 }; p = str_put(b, sizeof b, p, h); }
    406     else if (k >= K_F1 && k < K_F1 + 10) { p = str_put(b, sizeof b, 0, "F"); p = str_int(b, sizeof b, p, k - K_F1 + 1); }
    407     else if (k >= 0x100) {
    408         static const char *const N[] = { "arriba", "abajo", "izquierda", "derecha", "Enter", "Esc", "Tab", "+", "-",
    409                                          "Retroceso", "Supr", "Inicio", "Fin", "RePág", "AvPág" };
    410         p = str_put(b, sizeof b, 0, k - 0x100 < 15 ? N[k - 0x100] : "?");
    411     } else { char ch[2] = { (char)k, 0 }; p = str_put(b, sizeof b, 0, "'"); p = str_put(b, sizeof b, p, ch); p = str_put(b, sizeof b, p, "' "); p = str_int(b, sizeof b, p, k); }
    412     (void)p;
    413     int w = cf_text_w(&cf_bold, b) * 2;
    414     for (int i = 0; b[i]; i++) {}
    415     cf_text(s, &cf_bold, 160 - w / 4, 150, b, FG);
    416 }
    417 
    418 void calc_paint(Surface *s, void *ctx)
    419 {
    420     Calc *c = ctx;
    421     switch (c->scr) {
    422     case SCR_MENU: menu_screen(s, c); return;
    423     case SCR_FORM: gfx_form(s, &c->form, 0); return;
    424     case SCR_CATALOG: catalog_screen(s, c); return;
    425     case SCR_KEYTEST: keytest_screen(s, c); return;
    426     }
    427     if (calc_main_screen(c)) { main_screen(s, c); return; }
    428     extern void modes_paint(Surface *s, Calc *c);
    429     modes_paint(s, c);
    430 }