modes_gfx.c (17072B)
1 /* modes_gfx.c - pantallas de los modos en pixeles (Ecuaciones, Tabla, Grafico, Estadistica). */ 2 #include <string.h> 3 #include "calc_gfx.h" 4 #include "dmath.h" 5 #include "str.h" 6 7 #define BG RGB(18, 22, 30) 8 #define PANEL RGB(30, 36, 50) 9 #define FG RGB(236, 236, 236) 10 #define DIM RGB(130, 138, 156) 11 #define ACC RGB(255, 200, 60) 12 #define RESC RGB(140, 220, 255) 13 #define SEL RGB(48, 58, 80) 14 #define CURS RGB(255, 226, 0) 15 #define ERRC RGB(255, 100, 90) 16 #define FCOL RGB(120, 200, 255) 17 #define GCOL RGB(255, 140, 90) 18 19 int eq_cells(int type); 20 const char *stat_type_name(int t); 21 void graph_trace(Calc *c); 22 23 extern Layout g_lay[]; /* render.c */ 24 #define L_in g_lay[0] 25 #define L_v (g_lay + 1) 26 static Expr tmp; 27 28 /* z = re + im i como expresion */ 29 static void cplx_expr(const Calc *c, const Cplx *z, Expr *out) 30 { 31 static Expr a; 32 bool im0 = !z->im.v.m; 33 calc_result_expr(c, &z->re, false, out); 34 if (im0) return; 35 Num im = z->im; 36 bool neg = im.v.neg; 37 if (neg) im = num_neg(im); 38 calc_result_expr(c, &im, false, &a); 39 out->cur = out->n; 40 if (!z->re.v.m) { ex_clear(out); if (neg) ex_insert_raw(out, '-'); } 41 else ex_insert_raw(out, neg ? '-' : '+'); 42 bool one = im.exact && ex_is_int(&im.x) && im.x.a == 1; 43 if (!one) ex_insert_expr(out, &a); 44 ex_insert_raw(out, 'i'); 45 } 46 47 static void val_text(const Calc *c, const Num *v, bool err, char *b, int n) 48 { 49 if (err || num_bad(v)) { str_put(b, n, 0, "Error"); return; } 50 fmt_plain(v, &c->fmt, false, b, n); 51 } 52 53 static void header(Surface *s, const Calc *c, const char *title) 54 { 55 gfx_rect(s, 0, 0, 320, 320, BG); 56 gfx_rect(s, 0, 0, 320, 14, PANEL); 57 cf_text(s, &cf_small, 3, 1, calc_ang_name(c->ang), FG); 58 if (c->shift) { gfx_rect(s, 14, 1, 9, 12, ACC); cf_text(s, &cf_small, 16, 1, "S", BG); } 59 const char *t = c->msg[0] ? c->msg : title; 60 cf_text(s, &cf_small, 160 - cf_text_w(&cf_small, t) / 2, 1, t, c->msg[0] ? ACC : DIM); 61 } 62 63 static void help(Surface *s, const char *t) 64 { 65 gfx_rect(s, 0, 306, 320, 14, PANEL); 66 cf_text(s, &cf_small, 4, 307, t, DIM); 67 } 68 69 /* caja de edicion abajo (con la entrada c->in) */ 70 static int input_box(Surface *s, const Calc *c, const char *label, int y) 71 { 72 int h = L_in.asc + L_in.desc; 73 if (h < 15) h = 15; 74 gfx_rect(s, 0, y, 320, h + 8, PANEL); 75 gfx_rect(s, 0, y, 320, 1, ACC); 76 int x = 6; 77 if (label) x += cf_text(s, &cf_big, 6, y + 4 + L_in.asc - 11, label, ACC) + 4; 78 int sc = 0, cx = L_in.gx[c->in.cur]; 79 if (x + cx > 310) sc = x + cx - 310; 80 gfx_clip(s, x, y + 1, 316 - x, h + 6); 81 layout_draw(s, &L_in, x - sc, y + 4, FG, c->in.cur, CURS); 82 gfx_noclip(s); 83 return h + 8; 84 } 85 86 /* ------------------------------------------------------------ ecuaciones */ 87 88 static const char *const EQ_TITLE[] = { "Sistema 2×2", "Sistema 3×3", "Polinomio de grado 2", "Polinomio de grado 3", "SOLVE" }; 89 90 static void eqn_paint(Surface *s, Calc *c) 91 { 92 EqnState *q = &c->eq; 93 header(s, c, EQ_TITLE[q->type]); 94 char b[80]; 95 if (q->type == EQ_SOLVE && q->phase == 0) { 96 cf_text(s, &cf_bold, 8, 24, "Ecuación con X (sin = es = 0):", ACC); 97 int p = str_put(b, sizeof b, 0, "Empieza a buscar en X = "); 98 fmt_plain(&c->vars[VAR_X], &c->fmt, true, b + p, (int)sizeof b - p); 99 cf_text(s, &cf_small, 8, 44, b, DIM); 100 input_box(s, c, 0, 200); 101 help(s, "Enter resuelve Esc borra / cambia el tipo"); 102 return; 103 } 104 if (q->phase == 0) { 105 static const char *const HDR[][4] = { 106 { "a", "b", "c" }, { "a", "b", "c", "d" }, { "a", "b", "c" }, { "a", "b", "c", "d" } }; 107 static const char *const FORM_TXT[] = { "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" }; 108 cf_text(s, &cf_big, 8, 22, FORM_TXT[q->type], DIM); 109 int cols = q->type == EQ_SYS2 || q->type == EQ_POLY2 ? 3 : 4; 110 int rows = eq_cells(q->type) / cols, cw = 300 / cols; 111 for (int j = 0; j < cols; j++) cf_text(s, &cf_bold, 12 + j * cw + cw / 2 - 4, 46, HDR[q->type][j], ACC); 112 for (int i = 0; i < rows; i++) 113 for (int j = 0; j < cols; j++) { 114 int k = i * cols + j, x = 10 + j * cw, y = 64 + i * 26; 115 bool sel = k == q->sel; 116 gfx_box(s, x, y, cw - 4, 22, sel ? SEL : PANEL, sel ? ACC : RGB(50, 58, 76)); 117 val_text(c, &q->coef[k], false, b, sizeof b); 118 gfx_clip(s, x + 2, y, cw - 8, 22); 119 cf_text(s, &cf_big, x + cw - 8 - cf_text_w(&cf_big, b), y + 5, b, FG); 120 gfx_noclip(s); 121 } 122 if (q->editing) input_box(s, c, "=", 250); 123 help(s, q->editing ? "Enter guarda Esc cancela" : "Tipeá un valor Enter resuelve Esc tipo"); 124 return; 125 } 126 /* soluciones */ 127 int y = 26; 128 if (q->note[0]) { cf_text(s, &cf_bold, 10, 40, q->note, ERRC); help(s, "Esc vuelve"); return; } 129 static const char *const VN[] = { "x", "y", "z" }; 130 static const char *const XN[] = { "x1", "x2", "x3" }; 131 int n = q->nsol; 132 if (q->type == EQ_SOLVE) { 133 calc_result_expr(c, &q->solve_x, false, &tmp); 134 layout_expr(&tmp, &METRIC_PX, &L_v[0]); 135 cf_text(s, &cf_bold, 10, y + L_v[0].asc - 11, "X =", ACC); 136 layout_draw(s, &L_v[0], 50, y, RESC, -1, 0); 137 y += L_v[0].asc + L_v[0].desc + 12; 138 int p = str_put(b, sizeof b, 0, "L-R = "); 139 fmt_plain(&q->solve_lr, &c->fmt, true, b + p, (int)sizeof b - p); 140 cf_text(s, &cf_big, 10, y, b, DIM); 141 help(s, "Esc vuelve a la ecuación (X quedó guardado)"); 142 return; 143 } 144 for (int i = 0; i < n; i++) { 145 cplx_expr(c, &q->sol[i], &tmp); 146 layout_expr(&tmp, &METRIC_PX, &L_v[i]); 147 const char *nm = q->type <= EQ_SYS3 ? VN[i] : XN[i]; 148 bool sel = i == q->shown; 149 int h = L_v[i].asc + L_v[i].desc; 150 if (sel) gfx_rect(s, 4, y - 3, 312, h + 6, SEL); 151 cf_text(s, &cf_bold, 10, y + L_v[i].asc - 11, nm, ACC); 152 cf_text(s, &cf_big, 34, y + L_v[i].asc - 11, "=", DIM); 153 layout_draw(s, &L_v[i], 50, y, RESC, -1, 0); 154 y += h + 12; 155 } 156 if (q->type == EQ_POLY2 && q->nsol) { 157 bool mx = q->coef[0].v.neg; 158 cf_text(s, &cf_small, 10, y, mx ? "Máximo:" : "Mínimo:", DIM); 159 y += 14; 160 calc_result_expr(c, &q->sol[2].re, false, &tmp); 161 layout_expr(&tmp, &METRIC_PX, &L_v[4]); 162 calc_result_expr(c, &q->sol[2].im, false, &tmp); 163 layout_expr(&tmp, &METRIC_PX, &L_v[5]); 164 cf_text(s, &cf_big, 10, y + L_v[4].asc - 11, "x =", DIM); 165 layout_draw(s, &L_v[4], 50, y, FG, -1, 0); 166 y += L_v[4].asc + L_v[4].desc + 8; 167 cf_text(s, &cf_big, 10, y + L_v[5].asc - 11, "y =", DIM); 168 layout_draw(s, &L_v[5], 50, y, FG, -1, 0); 169 } 170 help(s, "↑↓ elige Tab decimal Esc vuelve"); 171 } 172 173 /* ------------------------------------------------------------ tabla */ 174 175 static void table_paint(Surface *s, Calc *c) 176 { 177 TableState *t = &c->tb; 178 header(s, c, "Tabla"); 179 char b[64]; 180 if (t->phase < 2) { 181 cf_text(s, &cf_bold, 8, 24, t->phase == 0 ? "f(x) =" : "g(x) =", ACC); 182 cf_text(s, &cf_small, 8, 44, "Escribí la función con x (o X). Enter sigue.", DIM); 183 if (t->phase == 1) { 184 cf_text(s, &cf_small, 8, 60, "f(x) ya está. ↑ vuelve a f(x).", DIM); 185 } 186 input_box(s, c, t->phase == 0 ? "f(x)=" : "g(x)=", 200); 187 help(s, "Enter: rango de la tabla Esc borra"); 188 return; 189 } 190 int cols = t->use_g ? 3 : 2, cw = 310 / cols; 191 static const char *const H[] = { "x", "f(x)", "g(x)" }; 192 for (int j = 0; j < cols; j++) cf_text(s, &cf_bold, 8 + j * cw + cw - 10 - cf_text_w(&cf_bold, H[j]), 18, H[j], ACC); 193 gfx_rect(s, 4, 33, 312, 1, DIM); 194 int rows = 15; 195 if (t->sel < t->top) t->top = t->sel; 196 if (t->sel >= t->top + rows) t->top = t->sel - rows + 1; 197 for (int r = t->top; r < t->rows && r < t->top + rows; r++) { 198 int y = 36 + (r - t->top) * 16; 199 if (r == t->sel) gfx_rect(s, 4, y - 1, 312, 16, SEL); 200 for (int j = 0; j < cols; j++) { 201 const Num *v = j == 0 ? &t->x[r] : j == 1 ? &t->fx[r] : &t->gx[r]; 202 bool err = j == 1 ? t->ferr[r] : j == 2 ? t->gerr[r] : false; 203 val_text(c, v, err, b, sizeof b); 204 gfx_clip(s, 8 + j * cw, y, cw - 4, 16); 205 int w = cf_text_w(&cf_big, b); 206 cf_text(s, &cf_big, 8 + j * cw + cw - 10 - w, y + 1, b, err ? ERRC : j ? RESC : FG); 207 gfx_noclip(s); 208 } 209 } 210 if (!t->rows) cf_text(s, &cf_big, 10, 60, "No hay filas (revisá el rango)", ERRC); 211 /* valor completo de la fila elegida */ 212 if (t->rows) { 213 calc_result_expr(c, &t->fx[t->sel], false, &tmp); 214 layout_expr(&tmp, &METRIC_PX, &L_v[0]); 215 gfx_rect(s, 0, 280 - L_v[0].asc - L_v[0].desc, 320, L_v[0].asc + L_v[0].desc + 26, PANEL); 216 if (!t->ferr[t->sel]) layout_draw(s, &L_v[0], 312 - L_v[0].w, 296 - L_v[0].asc - L_v[0].desc, RESC, -1, 0); 217 } 218 help(s, "↑↓ recorre Enter/Esc edita f(x)"); 219 } 220 221 /* ------------------------------------------------------------ grafico */ 222 223 #define GY0 15 224 #define GH 262 225 226 static int gy_of(Dec y, const GraphState *g) 227 { 228 Dec r = dec_div(dec_mul_int(dec_sub(g->ymax, y), GH - 1), dec_sub(g->ymax, g->ymin)); 229 int64_t v; 230 if (!dec_to_int(dec_round_int(r), &v) || v < -10000 || v > 10000) return r.neg ? -10000 : 10000; 231 return (int)v; 232 } 233 234 static int gx_of(Dec x, const GraphState *g) 235 { 236 Dec r = dec_div(dec_mul_int(dec_sub(x, g->xmin), 319), dec_sub(g->xmax, g->xmin)); 237 int64_t v; 238 if (!dec_to_int(dec_round_int(r), &v) || v < -10000 || v > 10000) return r.neg ? -10000 : 10000; 239 return (int)v; 240 } 241 242 /* paso "lindo" de la grilla: 1, 2 o 5 por 10^k, unas 8 divisiones */ 243 static Dec nice_step(Dec span) 244 { 245 Dec s = dec_div_int(span, 8); 246 if (!s.m) return DEC_ONE; 247 Dec p = dec_scale10(DEC_ONE, s.e); 248 Dec m = dec_div(s, p); 249 int64_t k; 250 dec_to_int(dec_round_int(m), &k); 251 k = k <= 1 ? 1 : k <= 3 ? 2 : k <= 7 ? 5 : 10; 252 return dec_mul_int(p, k); 253 } 254 255 static void curve(Surface *s, const int16_t *ys, Color col) 256 { 257 for (int i = 0; i < 320; i++) { 258 if (ys[i] == INT16_MIN) continue; 259 int y = ys[i]; 260 if (i > 0 && ys[i - 1] != INT16_MIN) { 261 int py = ys[i - 1]; 262 if ((py < -GH || py > 2 * GH) && (y < -GH || y > 2 * GH)) continue; 263 if (py - y < GH && y - py < GH) { 264 int a = py < y ? py : y, b = py < y ? y : py; 265 if (a < 0) a = 0; 266 if (b > GH - 1) b = GH - 1; 267 if (a <= b && b - a > 0) gfx_rect(s, i, GY0 + a, 1, b - a + 1, col); 268 continue; 269 } 270 } 271 if (y >= 0 && y < GH) gfx_rect(s, i, GY0 + y, 1, 1, col); 272 } 273 } 274 275 static void graph_paint(Surface *s, Calc *c) 276 { 277 GraphState *g = &c->gr; 278 header(s, c, "Gráfico"); 279 char b[96]; 280 if (g->phase < 2) { 281 cf_text(s, &cf_bold, 8, 24, g->phase == 0 ? "f(x) =" : "g(x) =", g->phase == 0 ? FCOL : GCOL); 282 cf_text(s, &cf_small, 8, 44, "Escribí la función con x. Enter grafica.", DIM); 283 cf_text(s, &cf_small, 8, 58, g->phase == 0 ? "↓ agrega una segunda función g(x)." : "↑ vuelve a f(x). Esc la saca.", DIM); 284 input_box(s, c, g->phase == 0 ? "f(x)=" : "g(x)=", 200); 285 help(s, "Enter grafica w ventana (en el gráfico)"); 286 return; 287 } 288 gfx_clip(s, 0, GY0, 320, GH); 289 /* grilla */ 290 Dec stx = nice_step(dec_sub(g->xmax, g->xmin)), sty = nice_step(dec_sub(g->ymax, g->ymin)); 291 Dec x0 = dec_mul(dec_floor(dec_div(g->xmin, stx)), stx); 292 for (int i = 0; i < 40; i++) { 293 Dec x = dec_add(x0, dec_mul_int(stx, i)); 294 if (dec_cmp(x, g->xmax) > 0) break; 295 int px = gx_of(x, g); 296 if (px >= 0 && px < 320) gfx_rect(s, px, GY0, 1, GH, RGB(30, 36, 48)); 297 } 298 Dec y0 = dec_mul(dec_floor(dec_div(g->ymin, sty)), sty); 299 for (int i = 0; i < 40; i++) { 300 Dec y = dec_add(y0, dec_mul_int(sty, i)); 301 if (dec_cmp(y, g->ymax) > 0) break; 302 int py = gy_of(y, g); 303 if (py >= 0 && py < GH) gfx_rect(s, 0, GY0 + py, 320, 1, RGB(30, 36, 48)); 304 } 305 /* ejes */ 306 int ax = gx_of(DEC_ZERO, g), ay = gy_of(DEC_ZERO, g); 307 if (ax >= 0 && ax < 320) gfx_rect(s, ax, GY0, 1, GH, DIM); 308 if (ay >= 0 && ay < GH) gfx_rect(s, 0, GY0 + ay, 320, 1, DIM); 309 if (g->use_g) curve(s, g->gy, GCOL); 310 curve(s, g->fy, FCOL); 311 /* trazo */ 312 gfx_rect(s, g->tx, GY0, 1, GH, RGB(70, 70, 40)); 313 const int16_t *ys = g->curve ? g->gy : g->fy; 314 if (ys[g->tx] != INT16_MIN && ys[g->tx] >= 0 && ys[g->tx] < GH) gfx_disc(s, g->tx, GY0 + ys[g->tx], 3, CURS); 315 gfx_noclip(s); 316 /* datos abajo */ 317 gfx_rect(s, 0, GY0 + GH, 320, 306 - GY0 - GH, PANEL); 318 Dec x = dec_add(g->xmin, dec_div_int(dec_mul_int(dec_sub(g->xmax, g->xmin), g->tx), 319)); 319 Num xn = num_dec(dec_round_sig(x, 10)); 320 int p = str_put(b, sizeof b, 0, "x="); 321 p = fmt_plain(&xn, &c->fmt, true, b + p, 40) + p; 322 p = str_put(b, sizeof b, p, g->curve ? " g=" : " f="); 323 if (num_bad(&g->tval)) str_put(b, sizeof b, p, "-"); 324 else fmt_plain(&g->tval, &c->fmt, true, b + p, (int)sizeof b - p); 325 cf_text(s, &cf_small, 4, GY0 + GH + 1, b, g->curve ? GCOL : FCOL); 326 help(s, "← → traza ↑↓ curva + - zoom w ventana Esc edita"); 327 } 328 329 /* ------------------------------------------------------------ estadistica */ 330 331 static void stat_paint(Surface *s, Calc *c) 332 { 333 StatState *t = &c->st; 334 header(s, c, stat_type_name(t->type)); 335 char b[64]; 336 if (t->phase == 1) { 337 int rows = 18; 338 if (t->rtop > t->nres - rows) t->rtop = t->nres > rows ? t->nres - rows : 0; 339 for (int i = t->rtop; i < t->nres && i < t->rtop + rows; i++) { 340 int y = 20 + (i - t->rtop) * 16; 341 cf_text(s, &cf_bold, 10, y, t->rlabel[i], ACC); 342 val_text(c, &t->rval[i], t->rerr[i], b, sizeof b); 343 cf_text(s, &cf_big, 310 - cf_text_w(&cf_big, b), y, b, RESC); 344 } 345 help(s, "↑↓ recorre Esc vuelve a los datos"); 346 return; 347 } 348 int cols = t->type == ST_1VAR ? 1 : 2; 349 cf_text(s, &cf_bold, 40, 18, "x", ACC); 350 if (cols == 2) cf_text(s, &cf_bold, 190, 18, "y", ACC); 351 gfx_rect(s, 4, 33, 312, 1, DIM); 352 int rows = t->editing ? 12 : 15; 353 if (t->row < t->top) t->top = t->row; 354 if (t->row >= t->top + rows) t->top = t->row - rows + 1; 355 for (int r = t->top; r <= t->n && r < t->top + rows; r++) { 356 int y = 36 + (r - t->top) * 16; 357 str_int(b, sizeof b, 0, r + 1); 358 cf_text(s, &cf_small, 6, y + 2, b, DIM); 359 for (int j = 0; j < cols; j++) { 360 int x = 30 + j * 150; 361 bool sel = r == t->row && j == t->col; 362 if (sel) gfx_rect(s, x, y - 1, 146, 16, SEL); 363 if (r < t->n) { 364 val_text(c, j ? &t->y[r] : &t->x[r], false, b, sizeof b); 365 cf_text(s, &cf_big, x + 140 - cf_text_w(&cf_big, b), y, b, FG); 366 } 367 } 368 } 369 if (t->editing) input_box(s, c, "=", 250); 370 help(s, t->editing ? "Enter guarda Esc cancela" : "Tipeá valores Supr borra fila Enter resultados"); 371 } 372 373 /* ------------------------------------------------------------ matrices */ 374 375 static void matrix_paint(Surface *s, Calc *c) 376 { 377 MatState *t = &c->mt; 378 const MVal *m = t->target < 4 ? &t->m[t->target] : &t->v[t->target - 4]; 379 char title[24], b[64]; 380 int p = str_put(title, sizeof title, 0, t->target < 4 ? "Mat" : "Vct"); 381 char l[2] = { (char)('A' + t->target % 4), 0 }; 382 str_put(title, sizeof title, p, l); 383 header(s, c, title); 384 cf_text(s, &cf_bold, 10, 20, title, ACC); 385 int cw = 300 / m->c; 386 if (cw > 90) cw = 90; 387 for (int i = 0; i < m->r; i++) 388 for (int j = 0; j < m->c; j++) { 389 int k = i * m->c + j, x = 10 + j * cw, y = 44 + i * 26; 390 bool sel = k == t->sel; 391 gfx_box(s, x, y, cw - 4, 22, sel ? SEL : PANEL, sel ? ACC : RGB(50, 58, 76)); 392 val_text(c, mv_get(m, i, j), false, b, sizeof b); 393 gfx_clip(s, x + 2, y, cw - 8, 22); 394 cf_text(s, &cf_big, x + cw - 8 - cf_text_w(&cf_big, b), y + 5, b, FG); 395 gfx_noclip(s); 396 } 397 if (t->editing) input_box(s, c, "=", 250); 398 help(s, t->editing ? "Enter guarda Esc cancela" : "Tipeá un valor = otra Enter/Esc a las cuentas"); 399 } 400 401 /* ------------------------------------------------------------ despacho */ 402 403 void modes_prepare(Calc *c) 404 { 405 layout_expr(&c->in, &METRIC_PX, &L_in); 406 if (c->mode == MODE_GRAPH && c->gr.phase == 2) { 407 c->gr.w = 320; 408 c->gr.h = GH; 409 if (!c->gr.ready) { graph_compute(c); graph_trace(c); } 410 } 411 } 412 413 void modes_paint(Surface *s, Calc *c) 414 { 415 switch (c->mode) { 416 case MODE_EQN: eqn_paint(s, c); break; 417 case MODE_TABLE: table_paint(s, c); break; 418 case MODE_GRAPH: graph_paint(s, c); break; 419 case MODE_STAT: stat_paint(s, c); break; 420 case MODE_MATRIX: matrix_paint(s, c); break; 421 } 422 }