tui_modes.c (12497B)
1 /* tui_modes.c - los modos (Ecuaciones, Tabla, Grafico, Estadistica) en la terminal. */ 2 #include <stdio.h> 3 #include <string.h> 4 #include "tuikit.h" 5 #include "calc.h" 6 #include "layout.h" 7 #include "dmath.h" 8 9 #define BG RGB(18, 22, 30) 10 #define PANEL RGB(30, 36, 50) 11 #define FG RGB(236, 236, 236) 12 #define DIM RGB(130, 138, 156) 13 #define ACC RGB(255, 200, 60) 14 #define RESC RGB(140, 220, 255) 15 #define SEL RGB(48, 58, 80) 16 #define ERRC RGB(255, 100, 90) 17 #define FCOL RGB(120, 200, 255) 18 #define GCOL RGB(255, 140, 90) 19 20 void draw_layout(const Layout *L, int y, int x, Color fg, Color bg, int x0, int w); 21 void text(int y, int x, const char *s, Color fg, Color bg); 22 void fill_row(int y, Color bg); 23 void status(const Calc *c); 24 void help(const char *s); 25 const Metric *tui_metric(void); 26 int eq_cells(int type); 27 const char *stat_type_name(int t); 28 void graph_trace(Calc *c); 29 extern int cur_y, cur_x; 30 31 static Layout L_in; 32 33 static void clear_all(void) 34 { 35 tk_attc(FG, BG, 0); 36 for (int y = 0; y < LINES; y++) tk_fill(y, 0, COLS, " "); 37 } 38 39 static void val(const Calc *c, const Num *v, bool err, char *b, int n) 40 { 41 if (err || num_bad(v)) snprintf(b, n, "Error"); 42 else fmt_plain(v, &c->fmt, false, b, n); 43 } 44 45 /* caja de edicion con la entrada; devuelve las filas usadas */ 46 static int input_box(const Calc *c, const char *label, int y) 47 { 48 layout_expr(&c->in, tui_metric(), &L_in); 49 int h = L_in.asc + L_in.desc; 50 if (h < 1) h = 1; 51 for (int k = 0; k < h + 2; k++) fill_row(y + k, PANEL); 52 tk_attc(ACC, PANEL, 0); 53 tk_fill(y, 0, COLS, "─"); 54 int x = 2; 55 if (label) { text(y + 1 + L_in.asc - 1, 2, label, ACC, PANEL); x += tk_ulen(label) + 1; } 56 int avail = COLS - x - 2, cx = L_in.gx[c->in.cur], sc = cx > avail - 1 ? cx - avail + 1 : 0; 57 draw_layout(&L_in, y + 1, x - sc, FG, PANEL, x, avail); 58 cur_y = y + 1 + L_in.gy[c->in.cur]; 59 cur_x = x - sc + cx; 60 return h + 2; 61 } 62 63 static void cplx_text(const Calc *c, const Cplx *z, char *b, int n) 64 { 65 char re[48], im[48]; 66 fmt_plain(&z->re, &c->fmt, false, re, sizeof re); 67 if (!z->im.v.m) { snprintf(b, n, "%s", re); return; } 68 Num a = z->im; 69 bool neg = a.v.neg; 70 if (neg) a = num_neg(a); 71 fmt_plain(&a, &c->fmt, false, im, sizeof im); 72 bool one = a.exact && ex_is_int(&a.x) && a.x.a == 1; 73 if (!z->re.v.m) snprintf(b, n, "%s%si", neg ? "-" : "", one ? "" : im); 74 else snprintf(b, n, "%s %s %si", re, neg ? "-" : "+", one ? "" : im); 75 } 76 77 /* ------------------------------------------------------------ ecuaciones */ 78 79 static void eqn(Calc *c) 80 { 81 EqnState *q = &c->eq; 82 static const char *const T[] = { "Sistema 2×2", "Sistema 3×3", "Polinomio de grado 2", "Polinomio de grado 3", "SOLVE" }; 83 clear_all(); 84 status(c); 85 text(2, 2, T[q->type], ACC, BG); 86 char b[160]; 87 if (q->type == EQ_SOLVE && q->phase == 0) { 88 text(4, 2, "Ecuación con X (sin = es = 0). Busca cerca del valor actual de X.", DIM, BG); 89 input_box(c, 0, 7); 90 help("Enter resuelve Esc borra / cambia el tipo"); 91 return; 92 } 93 if (q->phase == 0) { 94 static const char *const F[] = { "a·x + b·y = c", "a·x + b·y + c·z = d", "a·x² + b·x + c = 0", "a·x³ + b·x² + c·x + d = 0" }; 95 text(4, 2, F[q->type], DIM, BG); 96 int cols = q->type == EQ_SYS2 || q->type == EQ_POLY2 ? 3 : 4, rows = eq_cells(q->type) / cols, cw = 16; 97 for (int j = 0; j < cols; j++) { char h[2] = { (char)('a' + j), 0 }; text(6, 4 + j * cw + cw - 3, h, ACC, BG); } 98 for (int i = 0; i < rows; i++) 99 for (int j = 0; j < cols; j++) { 100 int k = i * cols + j; 101 bool sel = k == q->sel; 102 tk_attc(FG, sel ? SEL : PANEL, 0); 103 tk_fill(7 + i, 4 + j * cw, cw - 1, " "); 104 val(c, &q->coef[k], false, b, sizeof b); 105 text(7 + i, 4 + j * cw + cw - 2 - tk_ulen(b), b, sel ? ACC : FG, sel ? SEL : PANEL); 106 } 107 if (q->editing) input_box(c, "=", 12); 108 help(q->editing ? "Enter guarda Esc cancela" : "Tipeá un valor flechas Enter resuelve Esc tipo"); 109 return; 110 } 111 if (q->note[0]) { text(5, 2, q->note, ERRC, BG); help("Esc vuelve"); return; } 112 if (q->type == EQ_SOLVE) { 113 fmt_plain(&q->solve_x, &c->fmt, false, b, sizeof b); 114 text(5, 2, "X = ", ACC, BG); text(5, 6, b, RESC, BG); 115 fmt_plain(&q->solve_lr, &c->fmt, true, b, sizeof b); 116 text(7, 2, "L-R = ", DIM, BG); text(7, 8, b, DIM, BG); 117 help("Esc vuelve a la ecuación (X quedó guardado)"); 118 return; 119 } 120 static const char *const VN[] = { "x", "y", "z" }; 121 for (int i = 0; i < q->nsol; i++) { 122 char nm[8]; 123 if (q->type <= EQ_SYS3) snprintf(nm, sizeof nm, "%s", VN[i]); 124 else snprintf(nm, sizeof nm, "x%d", i + 1); 125 cplx_text(c, &q->sol[i], b, sizeof b); 126 Color bg = i == q->shown ? SEL : BG; 127 tk_attc(FG, bg, 0); 128 tk_fill(5 + i * 2, 1, COLS - 2, " "); 129 text(5 + i * 2, 2, nm, ACC, bg); 130 text(5 + i * 2, 6, "= ", DIM, bg); 131 text(5 + i * 2, 8, b, RESC, bg); 132 } 133 if (q->type == EQ_POLY2 && q->nsol) { 134 char x[48], y[48]; 135 fmt_plain(&q->sol[2].re, &c->fmt, false, x, sizeof x); 136 fmt_plain(&q->sol[2].im, &c->fmt, false, y, sizeof y); 137 snprintf(b, sizeof b, "%s: x = %s y = %s", q->coef[0].v.neg ? "Máximo" : "Mínimo", x, y); 138 text(11, 2, b, DIM, BG); 139 } 140 help("↑↓ elige Tab decimal Esc vuelve"); 141 } 142 143 /* ------------------------------------------------------------ tabla */ 144 145 static void table(Calc *c) 146 { 147 TableState *t = &c->tb; 148 clear_all(); 149 status(c); 150 char b[96]; 151 if (t->phase < 2) { 152 text(2, 2, t->phase == 0 ? "Tabla: f(x)" : "Tabla: g(x)", ACC, BG); 153 text(4, 2, "Escribí la función con x. Enter sigue con el rango.", DIM, BG); 154 input_box(c, t->phase == 0 ? "f(x)=" : "g(x)=", 7); 155 help("Enter: rango Esc borra"); 156 return; 157 } 158 int cols = t->use_g ? 3 : 2, cw = (COLS - 4) / cols; 159 static const char *const H[] = { "x", "f(x)", "g(x)" }; 160 for (int j = 0; j < cols; j++) text(2, 2 + j * cw + cw - 1 - tk_ulen(H[j]), H[j], ACC, BG); 161 int rows = LINES - 6; 162 if (t->sel < t->top) t->top = t->sel; 163 if (t->sel >= t->top + rows) t->top = t->sel - rows + 1; 164 for (int r = t->top; r < t->rows && r < t->top + rows; r++) { 165 int y = 3 + r - t->top; 166 Color bg = r == t->sel ? SEL : BG; 167 tk_attc(FG, bg, 0); 168 tk_fill(y, 1, COLS - 2, " "); 169 for (int j = 0; j < cols; j++) { 170 const Num *v = j == 0 ? &t->x[r] : j == 1 ? &t->fx[r] : &t->gx[r]; 171 bool err = j == 1 ? t->ferr[r] : j == 2 ? t->gerr[r] : false; 172 val(c, v, err, b, sizeof b); 173 text(y, 2 + j * cw + cw - 1 - tk_ulen(b), b, err ? ERRC : j ? RESC : FG, bg); 174 } 175 } 176 help("↑↓ recorre Enter/Esc edita f(x)"); 177 } 178 179 /* ------------------------------------------------------------ grafico */ 180 181 static void graph(Calc *c) 182 { 183 GraphState *g = &c->gr; 184 clear_all(); 185 status(c); 186 char b[160]; 187 if (g->phase < 2) { 188 text(2, 2, g->phase == 0 ? "Gráfico: f(x)" : "Gráfico: g(x)", g->phase == 0 ? FCOL : GCOL, BG); 189 text(4, 2, g->phase == 0 ? "Enter grafica. ↓ agrega g(x)." : "Enter grafica. ↑ vuelve a f(x). Esc la saca.", DIM, BG); 190 input_box(c, g->phase == 0 ? "f(x)=" : "g(x)=", 7); 191 help("Enter grafica w ventana (en el gráfico)"); 192 return; 193 } 194 int W = COLS < GRAPH_W ? COLS : GRAPH_W, Hc = LINES - 4, H = Hc * 2; 195 if (g->w != W || g->h != H) { g->w = W; g->h = H; g->ready = false; } 196 if (!g->ready) { graph_compute(c); graph_trace(c); } 197 Surface *s = tk_pix_begin(W, Hc); 198 if (!s) return; 199 gfx_rect(s, 0, 0, W, H, BG); 200 /* ejes */ 201 Dec xr = dec_sub(g->xmax, g->xmin), yr = dec_sub(g->ymax, g->ymin); 202 int64_t v; 203 if (dec_to_int(dec_round_int(dec_div(dec_mul_int(dec_neg(g->xmin), W - 1), xr)), &v) && v >= 0 && v < W) gfx_rect(s, (int)v, 0, 1, H, DIM); 204 if (dec_to_int(dec_round_int(dec_div(dec_mul_int(g->ymax, H - 1), yr)), &v) && v >= 0 && v < H) gfx_rect(s, 0, (int)v, W, 1, DIM); 205 for (int k = g->use_g ? 1 : 0; k >= 0; k--) { 206 const int16_t *ys = k ? g->gy : g->fy; 207 Color col = k ? GCOL : FCOL; 208 for (int i = 0; i < W; i++) { 209 if (ys[i] == INT16_MIN) continue; 210 int y = ys[i]; 211 if (i && ys[i - 1] != INT16_MIN && ys[i - 1] - y < H && y - ys[i - 1] < H) { 212 int a = ys[i - 1] < y ? ys[i - 1] : y, bb = ys[i - 1] < y ? y : ys[i - 1]; 213 if (a < 0) a = 0; 214 if (bb > H - 1) bb = H - 1; 215 if (a <= bb) gfx_rect(s, i, a, 1, bb - a + 1, col); 216 } else if (y >= 0 && y < H) gfx_rect(s, i, y, 1, 1, col); 217 } 218 } 219 const int16_t *ys = g->curve ? g->gy : g->fy; 220 gfx_rect(s, g->tx, 0, 1, H, RGB(70, 70, 40)); 221 if (ys[g->tx] != INT16_MIN && ys[g->tx] >= 0 && ys[g->tx] < H) gfx_rect(s, g->tx - 1, ys[g->tx] - 1, 3, 3, ACC); 222 tk_pix_blit(1, 0); 223 Dec x = dec_add(g->xmin, dec_div_int(dec_mul_int(xr, g->tx), W - 1)); 224 Num xn = num_dec(dec_round_sig(x, 10)); 225 char xs[48], ys2[48]; 226 fmt_plain(&xn, &c->fmt, true, xs, sizeof xs); 227 if (num_bad(&g->tval)) snprintf(ys2, sizeof ys2, "-"); 228 else fmt_plain(&g->tval, &c->fmt, true, ys2, sizeof ys2); 229 fill_row(LINES - 2, PANEL); 230 snprintf(b, sizeof b, "x = %s %s = %s", xs, g->curve ? "g" : "f", ys2); 231 text(LINES - 2, 2, b, g->curve ? GCOL : FCOL, PANEL); 232 help("← → traza ↑↓ curva + - zoom w ventana Esc edita"); 233 } 234 235 /* ------------------------------------------------------------ estadistica */ 236 237 static void stat(Calc *c) 238 { 239 StatState *t = &c->st; 240 clear_all(); 241 status(c); 242 char b[96]; 243 text(1, 2, stat_type_name(t->type), ACC, BG); 244 if (t->phase == 1) { 245 int rows = LINES - 4; 246 for (int i = t->rtop; i < t->nres && i < t->rtop + rows; i++) { 247 text(2 + i - t->rtop, 3, t->rlabel[i], ACC, BG); 248 val(c, &t->rval[i], t->rerr[i], b, sizeof b); 249 text(2 + i - t->rtop, 12, b, RESC, BG); 250 } 251 help("↑↓ recorre Esc vuelve a los datos"); 252 return; 253 } 254 int cols = t->type == ST_1VAR ? 1 : 2, cw = 22; 255 text(2, 8 + cw - 2, "x", ACC, BG); 256 if (cols == 2) text(2, 8 + 2 * cw - 2, "y", ACC, BG); 257 int rows = LINES - (t->editing ? 9 : 5); 258 if (t->row < t->top) t->top = t->row; 259 if (t->row >= t->top + rows) t->top = t->row - rows + 1; 260 for (int r = t->top; r <= t->n && r < t->top + rows; r++) { 261 int y = 3 + r - t->top; 262 snprintf(b, sizeof b, "%3d", r + 1); 263 text(y, 2, b, DIM, BG); 264 for (int j = 0; j < cols; j++) { 265 bool sel = r == t->row && j == t->col; 266 Color bg = sel ? SEL : BG; 267 tk_attc(FG, bg, 0); 268 tk_fill(y, 7 + j * cw, cw - 1, " "); 269 if (r < t->n) { 270 val(c, j ? &t->y[r] : &t->x[r], false, b, sizeof b); 271 text(y, 7 + j * cw + cw - 2 - tk_ulen(b), b, FG, bg); 272 } 273 } 274 } 275 if (t->editing) input_box(c, "=", LINES - 5); 276 help(t->editing ? "Enter guarda Esc cancela" : "Tipeá valores Supr borra la fila Enter resultados Esc tipo"); 277 } 278 279 static void matrix(Calc *c) 280 { 281 MatState *t = &c->mt; 282 const MVal *m = t->target < 4 ? &t->m[t->target] : &t->v[t->target - 4]; 283 clear_all(); 284 status(c); 285 char b[96]; 286 snprintf(b, sizeof b, "%s%c (%d×%d)", t->target < 4 ? "Mat" : "Vct", 'A' + t->target % 4, m->r, m->c); 287 text(2, 2, b, ACC, BG); 288 int cw = 16; 289 for (int i = 0; i < m->r; i++) 290 for (int j = 0; j < m->c; j++) { 291 int k = i * m->c + j; 292 bool sel = k == t->sel; 293 tk_attc(FG, sel ? SEL : PANEL, 0); 294 tk_fill(4 + i, 4 + j * cw, cw - 1, " "); 295 val(c, mv_get(m, i, j), false, b, sizeof b); 296 text(4 + i, 4 + j * cw + cw - 2 - tk_ulen(b), b, sel ? ACC : FG, sel ? SEL : PANEL); 297 } 298 if (t->editing) input_box(c, "=", 10); 299 help(t->editing ? "Enter guarda Esc cancela" : "Tipeá un valor flechas = otra Enter/Esc a las cuentas"); 300 } 301 302 void tui_modes_paint(Calc *c) 303 { 304 switch (c->mode) { 305 case MODE_EQN: eqn(c); break; 306 case MODE_TABLE: table(c); break; 307 case MODE_GRAPH: graph(c); break; 308 case MODE_STAT: stat(c); break; 309 case MODE_MATRIX: matrix(c); break; 310 } 311 }