173 lines
6.5 KiB
C++
173 lines
6.5 KiB
C++
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <math.h>
|
||
|
#include "calculator.h"
|
||
|
#include "heap.h"
|
||
|
extern "C" void EXPORT(init) (const int memlen);
|
||
|
extern "C" int EXPORT(cAlloc) (int len);
|
||
|
extern "C" void EXPORT(compute) (char * ptr, int len);
|
||
|
extern "C" void EXPORT(resizeCanvas)(int x, int y);
|
||
|
extern "C" void IMPORT(drawPoints) (float * x, float * y, int len, char * json, int jl);
|
||
|
extern "C" void __wasm_call_ctors ();
|
||
|
extern "C++" {
|
||
|
typedef struct yy_buffer_state * YY_BUFFER_STATE;
|
||
|
extern int yyparse();
|
||
|
extern YY_BUFFER_STATE yy_scan_string(const char * str);
|
||
|
extern void yy_delete_buffer(YY_BUFFER_STATE buffer);
|
||
|
};
|
||
|
extern "C" int yywrap () {
|
||
|
return 1;
|
||
|
}
|
||
|
void init (const int memlen) {
|
||
|
_HEAP_MAX = reinterpret_cast<char*>(memlen);
|
||
|
__wasm_call_ctors();
|
||
|
printf("Module initialized\n\n");
|
||
|
}
|
||
|
int cAlloc (int len) {
|
||
|
return int (malloc(len + 1));
|
||
|
}
|
||
|
void compute (char * ptr, int len) {
|
||
|
ptr [len] = '\0';
|
||
|
|
||
|
initData();
|
||
|
YY_BUFFER_STATE result = yy_scan_string(ptr);
|
||
|
yyparse();
|
||
|
yy_delete_buffer(result);
|
||
|
free (ptr);
|
||
|
finiData();
|
||
|
}
|
||
|
struct Canvas {
|
||
|
double width, height;
|
||
|
double xscale, yscale;
|
||
|
double xofset, yofset;
|
||
|
float xt (const double x) {
|
||
|
return float (xscale * (x - xofset));
|
||
|
}
|
||
|
float yt (const double y) {
|
||
|
return float (height - yscale * (y - yofset));
|
||
|
}
|
||
|
};
|
||
|
static const char * multipliers [] = {"f","p","n","μ","m","","k","M","G","T","P"};
|
||
|
struct Stepping {
|
||
|
double b,e,s;
|
||
|
char fmtbuf [16];
|
||
|
explicit Stepping (const double from, const double to) {
|
||
|
double lp;
|
||
|
const double z = log10 (fabs (to - from));
|
||
|
const double fp = modf (z, & lp);
|
||
|
if (fp < 0.30103) s = 1.0;
|
||
|
else if (fp > 0.69897) s = 5.0;
|
||
|
else s = 2.0;
|
||
|
int ip = int (lp) - 1;
|
||
|
if (z < 0.0) ip -= 1;
|
||
|
s *= ::pow (10.0, double (ip));
|
||
|
do {
|
||
|
const int k = int (fabs(to - from) / s);
|
||
|
b = round (from / s) * s;
|
||
|
e = round (to / s) * s;
|
||
|
if (k > 50) s *= 10.0; // prevence přeplnění osy, nevím proč, ale tohle to odstraní
|
||
|
else break; // patrně log10() nedává přesně to, co bych čekal
|
||
|
} while (true);
|
||
|
}
|
||
|
void f () {printf("stepping = %g, b = %g, e = %g\n", s, b, e);}
|
||
|
char * ing (const double x, int n=0) {
|
||
|
if (fabs(x) < 0.5 * s) {
|
||
|
fmtbuf[n++] = ' ';
|
||
|
fmtbuf[n++] = '\0';
|
||
|
return fmtbuf;
|
||
|
}
|
||
|
if (x < 0.0) {
|
||
|
fmtbuf[n++] = '-';
|
||
|
return ing (-x, n);
|
||
|
}
|
||
|
if (x > 0.0) {
|
||
|
double ip;
|
||
|
const double fp = modf(log10(x), & ip);
|
||
|
int pi = ip, ofs = 5 * 3;
|
||
|
if (pi < -ofs) pi = -ofs;
|
||
|
if (pi > 18 ) pi = 18;
|
||
|
const div_t dt = div(pi + ofs, 3);
|
||
|
n += snprintf (fmtbuf + n, 16 - n, "%g%s", ::pow (10.0, fp + double (dt.rem)), multipliers [dt.quot]);
|
||
|
}
|
||
|
return fmtbuf;
|
||
|
}
|
||
|
protected:
|
||
|
};
|
||
|
static Canvas canvas;
|
||
|
void resizeCanvas (int x, int y) {
|
||
|
printf("resizeCanvas: x=%d, y=%d\n", x, y);
|
||
|
canvas.width = x;
|
||
|
canvas.height = y;
|
||
|
}
|
||
|
void emitData (double * x, double * y, const int len) {
|
||
|
double ymin = 1.0e100, ymax = -1.0e100;
|
||
|
for (int n=0; n<len; n++) {
|
||
|
if (y[n] < ymin) ymin = y[n];
|
||
|
if (y[n] > ymax) ymax = y[n];
|
||
|
// printf("f(%g) = %g\n", x[n], y[n]);
|
||
|
}
|
||
|
const double xmin = x[0];
|
||
|
const double xmax = x[len - 1];
|
||
|
printf("xmin = %g, xmax = %g, ymin = %g, ymax = %g\n", xmin, xmax, ymin, ymax);
|
||
|
const double f = 1.0;
|
||
|
ymin *= f;
|
||
|
ymax *= f;
|
||
|
canvas.xofset = xmin;
|
||
|
canvas.yofset = ymin;
|
||
|
canvas.xscale = canvas.width / (xmax - xmin);
|
||
|
canvas.yscale = canvas.height / (ymax - ymin);
|
||
|
Stepping sx (xmin, xmax), sy (ymin, ymax);
|
||
|
// sx.f(); sy.f();
|
||
|
|
||
|
float * ix = new float [len]; // float32 stačí
|
||
|
float * iy = new float [len];
|
||
|
for (int n=0; n<len; n++) {
|
||
|
ix[n] = canvas.xt (x[n]); // přepočteno na velikost canvasu
|
||
|
iy[n] = canvas.yt (y[n]);
|
||
|
// printf("f(%d) = %d\n", ix[n], iy[n]);
|
||
|
}
|
||
|
const int json_max = 0x8000; // string bude poměrně dlouhý
|
||
|
char * json = new char [json_max];
|
||
|
int n = 0;
|
||
|
const uint32_t xColor = 0xffff00;
|
||
|
const uint32_t yColor = 0x00ffff;
|
||
|
const double wd = 4.0; // šířka čárky na osách v px
|
||
|
const double pos0x = canvas.xt(0.0) > 0 and canvas.xt(0.0) < canvas.width ? canvas.xt(0.0) : 0.0;
|
||
|
const double pos0y = canvas.yt(0.0) > 0 and canvas.yt(0.0) < canvas.height ? canvas.yt(0.0) : canvas.height;
|
||
|
n += snprintf(json + n, json_max - n, "{ \"name\":\"axes\", ");
|
||
|
n += snprintf(json + n, json_max - n, "\"x\":{ \"color\":\"#%06X\", \"w\":%d, ", xColor, 2);
|
||
|
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], ", 0.0, pos0y);
|
||
|
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, ", canvas.width, pos0y);
|
||
|
n += snprintf(json + n, json_max - n, "\"y\": { \"color\":\"#%06X\", \"w\":%d, ", yColor, 2);
|
||
|
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], ", pos0x, 0.0);
|
||
|
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, \"xdots\": [", pos0x, canvas.height);
|
||
|
for (double dx=sx.b; dx<sx.e; dx+=sx.s) {
|
||
|
n += snprintf(json + n, json_max - n, "{ \"color\":\"#%06X\", \"w\":%d, \"lbl\":\"%s\", ", xColor, 2, sx.ing(dx));
|
||
|
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], " , canvas.xt(dx), pos0y - wd);
|
||
|
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, ", canvas.xt(dx), pos0y + wd);
|
||
|
}
|
||
|
json [n-2] = ' '; // přepiš poslední čárku, jinak json nefunguje
|
||
|
n += snprintf(json + n, json_max - n, "], \"ydots\": [\n");
|
||
|
for (double dy=sy.b; dy<sy.e; dy+=sy.s) {
|
||
|
n += snprintf(json + n, json_max - n, "{ \"color\":\"#%06X\", \"w\":%d, \"lbl\":\"%s\", ", yColor, 2, sy.ing(dy));
|
||
|
n += snprintf(json + n, json_max - n, "\"b\": [%g,%g], " , pos0x - wd, canvas.yt(dy));
|
||
|
n += snprintf(json + n, json_max - n, "\"e\": [%g,%g] }, ", pos0x + wd, canvas.yt(dy));
|
||
|
}
|
||
|
json [n-2] = ' ';
|
||
|
n += snprintf(json + n, json_max - n, "] }\n");
|
||
|
// printf("%d\n", n);
|
||
|
/* Pro kreslení na canvas se používají 2 metody:
|
||
|
* 1. Přímé předání dat pomocí ukazatelů.
|
||
|
* Ukázalo se, že typ int nestačí, zaokrouhlení je kostrbaté, double je zbytečně velký,
|
||
|
* takže stačí float (32.bit), je to normováno na velikost canvas, takže tam nejsou skoky v řádech
|
||
|
* 2. Z dat uděláme JSON a ten zpracuje javascript. Není to moc efektivní, ale funguje.
|
||
|
* (3. zde nepoužito) Vytvoříme přímo javascript zdroják, který to celé vykreslí
|
||
|
* a ten pak spustíme v javascriptu pomocí eval(). Funguje také, ale je to divné.
|
||
|
* */
|
||
|
drawPoints (ix, iy, len, json, n);
|
||
|
delete [] ix; delete [] iy;
|
||
|
delete [] json;
|
||
|
}
|