#include "graphplot.h"
#include "error.h"
#include "evalfn.h"
#include "rtconf.h"
#include "term.h"
constexpr double fontrow = 2;
constexpr double fontcol = 1;
constexpr double font_ratio = fontcol / fontrow;
static void drawAxisX(double xn, int disp_size, double dx) {
putchar('\t');
putchar('+');
for (int i = 0; i < disp_size / font_ratio; i++) putchar('-');
putchar('\n');
putchar('\t');
printf("%.3lf", xn);
for (int i = 0; i < disp_size / font_ratio / 2; i++) putchar(' ');
printf("%.3lf", xn + dx * disp_size / 2 / font_ratio);
putchar('\n');
}
plotbuf_t plotBuf(size_t row, size_t col) {
char **b = xalloc(char *, row);
for (size_t i = 0; i < row; i++) {
b[i] = xalloc(char, col + 1);
memset(b[i], ' ', col);
b[i][col] = 0;
}
return (plotbuf_t){.buf = b, .row = row, .col = col};
}
void freePlotBuf(plotbuf_t *pb) {
for (size_t i = 0; i < pb->row; i++) free(pb->buf[i]);
free(pb->buf);
}
void displayPlotBuf(plotbuf_t pb, double yn, double dy) {
for (int i = (int)pb.row - 1; 0 <= i; i--) {
double y = yn + dy * i;
printf("%.3lf\t|", y);
puts(pb.buf[i]);
}
}
[[gnu::nonnull]] void plotexpr(char const *restrict expr) {
plotcfg_t const pcfg = getPlotCfg();
machine_t m;
size_t col = (size_t)(pcfg.dispx / font_ratio);
real_t stack = {.elem = {.real = pcfg.xn}, .isnum = true};
plotbuf_t pb ondrop(freePlotBuf) = plotBuf((size_t)pcfg.dispy, col);
m.e.args = &stack - 7;
m.c.expr = expr;
initEvalinfo(&m);
rpxEval(&m);
double y0 = m.s.rsp->elem.real;
for (size_t i = 0; i < col; i++) {
double x1 = pcfg.xn + pcfg.dx * (double)i;
stack.elem.real = x1;
initEvalinfo(&m);
rpxEval(&m);
double y1 = m.s.rsp->elem.real;
double gt = more(y0, y1);
double lt = less(y0, y1);
if (gt < pcfg.yn || pcfg.yx < lt) continue;
size_t gt_index = (size_t)less(pcfg.dispy - 1, (gt - pcfg.yn) / pcfg.dy);
size_t lt_index = (size_t)more(0, (lt - pcfg.yn) / pcfg.dy);
for (size_t j = lt_index; j <= gt_index; j++) pb.buf[j][i] = '*';
y0 = y1;
}
displayPlotBuf(pb, pcfg.yn, pcfg.dy);
drawAxisX(pcfg.xn, pcfg.dispx, pcfg.dx);
}
[[gnu::nonnull]] void plotexprImplicit(char const *restrict expr) {
plotcfg_t const pcfg = getPlotCfg();
machine_t m;
m.c.expr = expr;
double err = pcfg.dy / 2; double x0 = pcfg.xn - pcfg.dx;
real_t stack[2] = {{.isnum = true}, {.isnum = true}};
m.e.args = (real_t *)stack - 6;
for (int i = 0; i < pcfg.dispy; i++) {
double y0 = pcfg.yx - pcfg.dy * i;
printf("%.3lf\t|", y0);
stack[0].elem.real = y0;
stack[1].elem.real = x0;
initEvalinfo(&m);
rpxEval(&m);
double y1 = y0 - pcfg.dy;
stack[0].elem.real = y1;
double res0 = m.s.rsp->elem.real;
for (int j = 0; j < pcfg.dispx / font_ratio; j++) {
double x1 = pcfg.xn + pcfg.dx * j;
stack[1].elem.real = x1;
initEvalinfo(&m);
rpxEval(&m);
double res1 = m.s.rsp->elem.real;
putchar(-err <= more(res0, res1) && less(res0, res1) <= err ? '*' : ' ');
res0 = res1;
}
putchar('\n');
}
drawAxisX(pcfg.xn, pcfg.dispx, pcfg.dx);
}
static void setPlotBounds(double xx, double xn, double yx, double yn) {
plotcfg_t pcfg = getPlotCfg();
pcfg.xx = xx;
pcfg.xn = xn;
pcfg.yx = yx;
pcfg.yn = yn;
pcfg.dx = (xx - xn) / pcfg.dispx * font_ratio;
pcfg.dy = (yx - yn) / pcfg.dispy;
setPlotCfg(pcfg);
}
void initPlotCfg() {
struct winsize w = getWinSize();
int dispsz = (int)less((double)w.ws_row, w.ws_col * font_ratio);
plotcfg_t pcfg = getPlotCfg();
pcfg.dispx = pcfg.dispy = dispsz - 5;
pcfg.plotexpr = plotexpr;
setPlotCfg(pcfg);
setPlotBounds(1, -1, 1, -1);
}
[[gnu::nonnull]] void changePlotCfg(char const *restrict cmd) {
switch (*cmd++) {
case 'd': { plotcfg_t pcfg = getPlotCfg();
stack_t s = evalExprRealStack(cmd);
double newx = (s.rsp - 1)->elem.real;
double newy = (s.rsp - 0)->elem.real;
double centerx = (pcfg.xx + pcfg.xn) / 2;
double centery = (pcfg.yx + pcfg.yn) / 2;
double coefx = newx / pcfg.dispx;
double coefy = newy / pcfg.dispy;
pcfg.xx = centerx + (pcfg.xx - centerx) * coefx;
pcfg.xn = centerx + (pcfg.xn - centerx) * coefx;
pcfg.yx = centery + (pcfg.yx - centery) * coefy;
pcfg.yn = centery + (pcfg.yn - centery) * coefy;
pcfg.dispx = (int)newx;
pcfg.dispy = (int)newy;
setPlotCfg(pcfg);
} break;
case 'r': { stack_t s = evalExprRealStack(cmd);
if (s.rsp - 4 != s.rbp) DISPERR("expected 4 params");
double newxx = (s.rsp - 3)->elem.real;
double newxn = (s.rsp - 2)->elem.real;
double newyx = (s.rsp - 1)->elem.real;
double newyn = (s.rsp - 0)->elem.real;
setPlotBounds(newxx, newxn, newyx, newyn);
} break;
default:
[[clang::unlikely]];
}
}