From c8d704a06c714895daeb3ec462a5a3561503c4a2 Mon Sep 17 00:00:00 2001 From: Kizarm Date: Sun, 26 Nov 2023 15:56:00 +0100 Subject: [PATCH] first commit --- .gitignore | 9 +++ Makefile | 33 ++++++++++ bin/index.html | 46 +++++++++++++ bin/index.js | 126 ++++++++++++++++++++++++++++++++++++ bin/test.txt | 7 ++ calc.l | 31 +++++++++ calc.y | 68 +++++++++++++++++++ calculator.cpp | 82 +++++++++++++++++++++++ calculator.h | 121 ++++++++++++++++++++++++++++++++++ heap.c | 46 +++++++++++++ heap.h | 15 +++++ newdel.cpp | 23 +++++++ symbols.txt | 3 + test.txt | 3 + unix.cpp | 59 +++++++++++++++++ unix.mk | 7 ++ wasm.cpp | 172 +++++++++++++++++++++++++++++++++++++++++++++++++ wasm.mk | 20 ++++++ 18 files changed, 871 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 bin/index.html create mode 100644 bin/index.js create mode 100644 bin/test.txt create mode 100644 calc.l create mode 100644 calc.y create mode 100644 calculator.cpp create mode 100644 calculator.h create mode 100644 heap.c create mode 100644 heap.h create mode 100644 newdel.cpp create mode 100644 symbols.txt create mode 100644 test.txt create mode 100644 unix.cpp create mode 100644 unix.mk create mode 100644 wasm.cpp create mode 100644 wasm.mk diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d4ea656 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# kdevelop +.kde* +*.kdev4 +# other +*.zip +*.wasm +*.lst +*.map +*.o diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b1be6d9 --- /dev/null +++ b/Makefile @@ -0,0 +1,33 @@ +CC = clang +CX = clang++ +CODE ?= wasm + +MOBJS = calc.yy.o calc.tab.o calculator.o + +ifeq ($(CODE),wasm) +include wasm.mk +else +include unix.mk +endif + +OBJS = $(MOBJS) $(MAIN) +CFLAGS += -Wno-unused-function + +all: $(PR) +%.o: %.cpp + $(CX) -std=c++14 -c $(CFLAGS) -fno-exceptions -fno-rtti $< -o $@ +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ +calc.tab.o: calc.tab.c + $(CX) -std=c++14 -c $(CFLAGS) -Wno-deprecated -Wno-writable-strings -fno-exceptions -fno-rtti $< -o $@ +$(PR): $(OBJS) $(WALIB) + $(LD) $(LFLAGS) $(OBJS) -o $(PR) $(LDLIBS) +calc.yy.cpp: calc.l calc.tab.h + flex calc.l +calc.tab.c calc.tab.h: calc.y + bison -d calc.y +clean: + rm -f *.o *.yy.* *.tab.* +distclean: clean + rm -f $(PR) +.PHONY: all clean distclean diff --git a/bin/index.html b/bin/index.html new file mode 100644 index 0000000..237e5bd --- /dev/null +++ b/bin/index.html @@ -0,0 +1,46 @@ + + + + + CALC + + + + +
+
+
+ + + +
+
+
+

Kalkulátor s grafickým výstupem.

+

Když jsem přepisoval z nudy syntax highlighter pro C++ z pythonu do C++, zjistil jsem, že regulární výrazy + jsou kupodivu v tom pythonu efektivnější. V tomto ohledu je STL knihovna asi dost naprd. Ale vzpomněl jsem si + na prastarý pár flex a bison, který umí nejen regulární výrazy, ale jde s tím parsovat dost jednoduše gramatika. + Mělo by to jít i v C++, ale příklady na webu byly dost zamotané a bylo nutné použít STL, kterou jsem pro tento + účel neměl k dispozici. Vyřešilo se to jednoduše - vygenerovaný C-čkový kód se přeloží jako C++, přičemž je + nutné povypínat něktetré warningy. +

+

Je to tedy jednoduchý kalkulátor, jde napsat výraz s normální notací (+-*/^), obsahující čísla (i desetinná), + který to normálně vyhodnotí. Postupně přibyly proměnné (jen písmenkové řetězce), které mohou mít i rozsah ve + kterém se pak výraz zobrazí jako funkce. Komentáře jsou ve složených závorkách. Vložené matematické funkce + jsou sin(), cos(), exp(), log() (přirozené).Na konci výrazu musí být ENTER. +

+

Jsou v tom chyby, celé je to vlasně hloupost, celé by to šlo napsat v javascriptu mnohem jednodušeji, + ale v podstatě to funguje a jde si podle toho udělat představu, jak daná funkce vypadá. Zdrojáky v licenci + MIT přikládám. Pro kompilaci je použit jen clang a jím kompilovaná C-čková knihovna + newlib. +

+
+ + diff --git a/bin/index.js b/bin/index.js new file mode 100644 index 0000000..ead361f --- /dev/null +++ b/bin/index.js @@ -0,0 +1,126 @@ +var gWASM; // globální proměnná + elementy stránky +const Outs = document.getElementById('stdout'); +const Btnt = document.getElementById('buttonTest'); +const Canvas = document.getElementById('canvas'); +// async/await z příkladu na webu +window.onload = async function() { + Btnt.disabled = true; + Outs.value = 'Compiling ...¨\n'; // presets + // Build WebAssembly instance - WebAssembly.instantiateStreaming problem + const memory = new WebAssembly.Memory({ initial: 4 }); + const importObject = { + env: { memory }, // nutné pro práci a pamětí + imports: { // importované funkce do wasm + printout : (ptr, len) => { + // pohled do paměti - ptr je vlastně číslo + const view = new Uint8Array (memory.buffer, ptr, len); + const utf8decoder = new TextDecoder(); + Outs.value += utf8decoder.decode(view); // to String + }, + memoryGrow : (len) => { + console.log ('Growing the memory by ' + len.toString() + '. 64K blocks'); + memory.grow (len); // patrně to jde volat přímo z C/C++ kódu, ale tohle funguje + }, + drawPoints : (px, py, len, pj, jl) => { + const xview = new Float32Array (memory.buffer, px, len); + const yview = new Float32Array (memory.buffer, py, len); + const view = new Uint8Array (memory.buffer, pj, jl); + const utf8decoder = new TextDecoder(); + const obj = JSON.parse (utf8decoder.decode(view)); + // console.log (obj); + if (!obj) obj = '{"name":"nothing"}'; + polyLine (xview, yview, len, obj); + }, + }, + }; + const response = await fetch('./module.wasm'); + const bytes = await response.arrayBuffer(); + const module = await WebAssembly.instantiate(bytes, importObject); + gWASM = { + asm : module.instance.exports, + mem : memory, + }; + gWASM.asm.init(memory.buffer.byteLength); + Btnt.onclick = () => { + Outs.value = ''; + const expression = document.getElementById('input').value; + // console.log(expression); + stringToModule (expression, gWASM.asm.compute); + }; + Outs.value = 'Module compiled - [insert formula and] press button Submit\n'; + Btnt.disabled = false; + // console.log (gWASM); + window.addEventListener('resize', resizeCanvas, false); + resizeCanvas(); + getFile ('test.txt'); + Splash (); +}; +function stringToModule (str, mfunc) { + const utf8EncodeText = new TextEncoder(); + const bytes = utf8EncodeText.encode(str); + // alokovat pamet v modulu je nutne, aby bylo kam kopirovat + const cArrayPointer = gWASM.asm.cAlloc(bytes.length); + if (!cArrayPointer) return; + const cArray = new Uint8Array(gWASM.mem.buffer, cArrayPointer, bytes.length); + cArray.set(bytes); // naplnit dekodovanym stringem + mfunc (cArrayPointer, cArray.length); +} +function resizeCanvas () { + const width = canvas.clientWidth; + const height = canvas.clientHeight; + canvas.width = width; + canvas.height = height; + // console.log (width, height); + gWASM.asm.resizeCanvas (width, height); +} +function drawLine (ctx, line, x, y) { + ctx.beginPath(); + ctx.moveTo(line.b[0], line.b[1]); + ctx.lineTo(line.e[0], line.e[1]); + ctx.lineWidth = line.w; + ctx.strokeStyle = line.color; + if (line.lbl) { + //console.log (line.lbl); + ctx.fillStyle = line.color; + ctx.fillText (line.lbl, line.b[0] + x, line.b[1] - y); + } + ctx.stroke(); +} +function polyLine (ax, ay, len, obj) { + // console.log (ax, ay); + const ctx = canvas.getContext("2d"); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.font = '16px serif'; + ctx.textAlign = 'left'; + if (obj.name === "axes") { + drawLine (ctx, obj.x, 0, 0); + drawLine (ctx, obj.y, 0, 0); + const ndotsx = obj.xdots.length; + for (let n=0; n +#include "calc.tab.h" +#include "calculator.h" +%} +%option outfile="calc.yy.cpp" header-file="calc.yy.h" +%option case-sensitive +%option array + +D [0-9] +E [Ee][+-]?{D}+ + +%% +pi { return MATHPI; } +sin { return FCESIN; } +cos { return FCECOS; } +exp { return FCEEXP; } +log { return FCELOG; } +[a-zA-Z]+ { yylval.svalue = strdup (yytext); return IDENT; } +{D}+ { yylval.svalue = strdup (yytext); return INTEGER; } +{D}*\.{D}+({E})? { yylval.svalue = strdup (yytext); return DOUBLE; } +{D}*\.{D}*({E})? { yylval.svalue = strdup (yytext); return DOUBLE; } +\{.*\} +[ \t]+ /* ignored */ +\n { return ENDLINE; } +. { return yytext[0]; } +%% +void prevent_unused (void) { + (void) yyunput; +/* (void) input; */ +} diff --git a/calc.y b/calc.y new file mode 100644 index 0000000..f979fb5 --- /dev/null +++ b/calc.y @@ -0,0 +1,68 @@ +%{ +#include +#include "calculator.h" +int yylex (void); +int yyerror (char *); +%} +%language "C" +%union { + char * svalue; + double dvalue; + class Expression * evalue; +} + +%token ENDLINE +%token DOUBLE +%token INTEGER +%token MATHPI +%token FCESIN +%token FCECOS +%token FCEEXP +%token FCELOG +%token IDENT +%token TEXT +%left '+' '-' +%left '*' '/' +%left '^' + +%type ENDLINE DOUBLE INTEGER IDENT line lines +%type expr +%type constnt +%start input +%% + +input: /* empty */ + | lines + ; +lines: line + | lines line + ; +line: ENDLINE + | IDENT '=' expr ENDLINE { addVariable ($1, $3->eval()); free ($1); } + | IDENT '=' expr ',' expr ',' expr ENDLINE { addRange ($1, $3->eval(), $5->eval(), $7->eval()); free ($1); } + | expr ENDLINE { addExpression ($1); } + | error ENDLINE { printf ("what:%s\n", $$); } + ; +constnt: DOUBLE { $$ = str_to_double ($1); free ($1); } + | INTEGER { $$ = str_to_long ($1); free ($1); } + | MATHPI { $$ = 3.14159265359; } + ; +expr: constnt { $$ = new Constant ($1); } + | IDENT { $$ = new Variable ($1); free ($1); } + | '-' expr { $$ = new Unary ($2, UNARY_MINUS); } + | expr '+' expr { $$ = new Binary ($1, $3, BINARY_PLUS); } + | expr '-' expr { $$ = new Binary ($1, $3, BINARY_MINUS);} + | expr '*' expr { $$ = new Binary ($1, $3, BINARY_MULT); } + | expr '/' expr { $$ = new Binary ($1, $3, BINARY_DIV); } + | expr '^' expr { $$ = new Binary ($1, $3, BINARY_POW); } + | '(' expr ')' { $$ = $2; } + | FCESIN '(' expr ')' { $$ = new Unary ($3, UNARY_SIN); } + | FCECOS '(' expr ')' { $$ = new Unary ($3, UNARY_COS); } + | FCEEXP '(' expr ')' { $$ = new Unary ($3, UNARY_EXP); } + | FCELOG '(' expr ')' { $$ = new Unary ($3, UNARY_LOG); } + ; +%% +int yyerror(char * err) { + printf("Error:\"%s\"\n", err); + return 0; +} diff --git a/calculator.cpp b/calculator.cpp new file mode 100644 index 0000000..4eccc2a --- /dev/null +++ b/calculator.cpp @@ -0,0 +1,82 @@ +#include +#include +#include +#include "calculator.h" + +double str_to_double (const char * str) { + return strtod (str, NULL); +} +long str_to_long (const char * str) { + return strtol (str, NULL, 10); +} + +static double u_minus (const double x) { return -x; } +const unary_function unary_function_table [UNARY_max] = { + u_minus, + sin, cos, exp, log, +}; +static double b_plus (const double a, const double b) { return a + b; } +static double b_minus (const double a, const double b) { return a - b; } +static double b_multiply (const double a, const double b) { return a * b; } +static double b_divide (const double a, const double b) { return b !=0.0 ? a / b : 1.0; } +const binary_function binary_function_table [BINARY_max] = { + b_plus, b_minus, b_multiply, b_divide, pow, +}; +static CommonData * main_data = nullptr; +void addVariable (const char * str, const double x) { + printf ("\"%s\"\t= %g\n", str, x); + if (main_data->list == nullptr) { + main_data->list = new VariableList (str, x); + return; + } + main_data->list->add(str, x); +} +void addRange (const char * str, const double x, const double e, const double s) { + printf ("\"%s\"\t= [%g:%g],%g\n", str, x, e, s); + if (main_data->list == nullptr) { + main_data->list = new VariableList (str, x, e, s); + return; + } + main_data->list->add(str, x, e, s); +} +static Range zero = {0.0, 0.0, 0.0}; +Variable::Variable(const char * name) { + data = main_data->list->find (name); + if (!data) { + printf("variable \"%s\" not assigned, zero used\n", name); + data = & zero; + } +} +extern void emitData (double * x, double * y, const int len); +void addExpression (Expression * e) { + if (main_data->root) delete main_data->root; + main_data->root = e; + + VariableList * vl = main_data->list->find_ranged(); + if (!vl) { + printf("f() = %g\n", e->eval()); + return; + } + Range * r = vl->get(); + const double x0 = r->value, x1 = r->end, s = fabs (r->step); + int n = (x1 - x0) / s; + if (n <= 0) return; + double * vx = new double [n + 1]; + double * vy = new double [n + 1]; + // printf("n=%d\n", n); + for (int i=0; ivalue = x; + const double y = e->eval(); + vx[i] = x; vy[i] = y; + } + emitData (vx, vy, n+1); + delete [] vx; delete [] vy; +} +void initData () { + main_data = new CommonData(); +} +void finiData () { + delete main_data; +} + diff --git a/calculator.h b/calculator.h new file mode 100644 index 0000000..55d3576 --- /dev/null +++ b/calculator.h @@ -0,0 +1,121 @@ +/* inclusion guard */ +#ifndef __CALCULATOR_H__ +#define __CALCULATOR_H__ +#include +#include +extern double str_to_double (const char * str); +extern long str_to_long (const char * str); +extern void addVariable (const char * str, const double x); +extern void addRange (const char * str, const double x, const double e, const double s); + +extern void initData (); +extern void finiData (); + +class Expression { + public: + virtual double eval () = 0; + virtual ~Expression () {} +}; +class Constant : public Expression { + double data; + public: + explicit Constant (const double x) : data (x) {} + explicit Constant (const long x) : data (x) {} + double eval () override { /*printf("c=%g", data);*/ return data; } + virtual ~Constant() {} +}; +extern void addExpression (Expression * e); +enum UNARY_FNCS { + UNARY_MINUS = 0, UNARY_SIN, UNARY_COS, UNARY_EXP, UNARY_LOG, UNARY_max +}; +enum BINARY_FNCS { + BINARY_PLUS = 0, BINARY_MINUS, BINARY_MULT, BINARY_DIV, BINARY_POW, BINARY_max +}; +typedef double (*unary_function) (const double); +typedef double (*binary_function) (const double, const double); +extern const unary_function unary_function_table [UNARY_max]; +extern const binary_function binary_function_table [BINARY_max]; +class Unary : public Expression { + Expression * m_expr; + unary_function m_f; + public: + explicit Unary (Expression * e, const UNARY_FNCS nf) : m_expr(e), m_f (unary_function_table[nf]) {} + double eval () override { /*printf("(*%p)(%p)", m_f, m_expr);*/ return (m_f (m_expr->eval())); } + virtual ~Unary () { delete m_expr; } +}; +class Binary : public Expression { + Expression * m_l, * m_r; + binary_function m_f; + public: + explicit Binary (Expression * l, Expression * r, const BINARY_FNCS nf) : m_l(l), m_r(r), m_f(binary_function_table[nf]) {} + double eval () override { /*printf("{%p}[%p]{%p}", m_l, m_f, m_r);*/ return (m_f (m_l->eval(), m_r->eval())); } + virtual ~Binary() { delete m_l; delete m_r; } +}; +struct Range { + double value, end, step; +}; +class VariableList { + VariableList * next; + char * name; + Range data; + public: + explicit VariableList (const char * _name, const double _value) : next(nullptr) { + name = strdup (_name); + data.value = _value; + data.end = _value; + data.step = 0.0; + } + explicit VariableList (const char * _name, const double _value, const double _end, const double steps) : next(nullptr) { + name = strdup (_name); + if (_value > _end) { + data.value = _end; + data.end = _value; + } else { + data.value = _value; + data.end = _end; + } + if (steps != 0.0) data.step = (data.end - data.value) / steps; + else data.step = 1.0; + } + ~VariableList () { + if (next) delete next; + free (name); + } + void add (const char * _name, const double _value) { + if (!next) next = new VariableList (_name, _value); + else next->add (_name, _value); + } + void add (const char * _name, const double _value, const double _end, const double steps) { + if (!next) next = new VariableList (_name, _value, _end, steps); + else next->add (_name, _value, _end, steps); + } + Range * find (const char * _name) { + if (!strcmp (name, _name)) return & data; + if (next) return next->find (_name); + return nullptr; + } + VariableList * find_ranged () { + if (data.step) return this; + if (next) return next->find_ranged(); + return nullptr; + } + Range * get () { return & data; } +}; +class Variable : public Expression { + Range * data; + public: + explicit Variable (const char * name); + double eval() override { return data->value; } + virtual ~Variable () {} +}; +struct CommonData { + VariableList * list; + Expression * root; + explicit CommonData() : list(nullptr), root(nullptr) {} + ~CommonData () { + if (list) delete list; + if (root) delete root; + } +}; + +#endif /* __CALCULATOR_H__ */ diff --git a/heap.c b/heap.c new file mode 100644 index 0000000..ce93675 --- /dev/null +++ b/heap.c @@ -0,0 +1,46 @@ +#include "heap.h" +extern void IMPORT(memoryGrow) (const int block); +extern char __heap_base; +typedef __SIZE_TYPE__ size_t; + +static const char * _HEAP_START = &__heap_base; +char * _HEAP_MAX = &__heap_base; + +void * sbrk (unsigned long size) { + static const char * heap_ptr; + const char * old_heap_ptr; + static unsigned int init_sbrk = 0; + /* heap_ptr is initialized to HEAP_START */ + if (init_sbrk == 0) { + heap_ptr = _HEAP_START; + init_sbrk = 1; + } + old_heap_ptr = heap_ptr; + /* Tohle je jen zkusmo, uvidíme, zatím se zdá, že to chodí. + * Těžko říct, co to udělá, když dojde paměť, ale pár MiB to zvládne. + */ + if ((heap_ptr + size) > _HEAP_MAX) { + const int blocks = (((heap_ptr + size) - _HEAP_MAX) >> 16) + 1; + memoryGrow (blocks); + _HEAP_MAX += blocks << 16; + } + heap_ptr += size; + return (void *)old_heap_ptr; +} +/* Následující je někde použito v parseru, ale nemusí to moc fungovat. + */ +int write(int fd, const void * b, size_t l) { + if ((fd == 1) || (fd == 2)) printout(b, l); + return l; +} +// formátování pro long double asi nebudu používat +extern void exit (int x)__attribute__((noreturn)); +void _exit (int x) __attribute__ ((noreturn)); +void _exit (int x) { exit(x); } +double __trunctfdf2(long double a) { return (double)(a); } +int isatty () { return 1; } + +void close (int fd) {} +int read (int fd, void * b, size_t l) { return l; } +size_t lseek(int fd, size_t offset, int whence) { return 0; } +int fstat(int fd, void * statbuf) { return 0; } diff --git a/heap.h b/heap.h new file mode 100644 index 0000000..edf8002 --- /dev/null +++ b/heap.h @@ -0,0 +1,15 @@ +#ifndef _HEAP_H +#define _HEAP_H +#define EXPORT(name) __attribute__((used, export_name(#name))) name +// "imports" odpovídá importObject.imports v JS, default je to importObject.env +#define IMPORT(name) __attribute__((import_module("imports"),import_name(#name))) name +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + extern void IMPORT(printout) (const char * ptr, const int len); // external javascript function + extern void * sbrk (unsigned long size); + extern char * _HEAP_MAX; +#ifdef __cplusplus +}; +#endif //__cplusplus +#endif // _HEAP_H diff --git a/newdel.cpp b/newdel.cpp new file mode 100644 index 0000000..5c59a3e --- /dev/null +++ b/newdel.cpp @@ -0,0 +1,23 @@ +#include + + +void* operator new (unsigned long size) { + return calloc(1,size); +} +void operator delete (void* p) { + free (p); +} + +void* operator new[] (unsigned long size) { + return calloc(1,size); +} + +void operator delete[] (void* p) { + free (p); +} + +void operator delete (void* p, unsigned size) { + (void) size; + free (p); +} + diff --git a/symbols.txt b/symbols.txt new file mode 100644 index 0000000..01a14a0 --- /dev/null +++ b/symbols.txt @@ -0,0 +1,3 @@ +printout +memoryGrow +drawPoints diff --git a/test.txt b/test.txt new file mode 100644 index 0000000..36e8f30 --- /dev/null +++ b/test.txt @@ -0,0 +1,3 @@ +x=2,8,20 +y=1 +y*x^2 + 5*y*x + 1 diff --git a/unix.cpp b/unix.cpp new file mode 100644 index 0000000..cf07c78 --- /dev/null +++ b/unix.cpp @@ -0,0 +1,59 @@ +#include +#include +#include +#include +#include "calc.yy.h" +#include "calculator.h" + +extern void yyparse (); +extern "C" int yywrap () { + return 1; +} + +static char * from_file (const char * filename) { + struct stat statbuf; + int r = stat (filename, & statbuf); + if (r) return nullptr; + char * buffer = (char*) malloc (statbuf.st_size + 1); + FILE * in = fopen (filename,"r"); + r = fread (buffer, 1, statbuf.st_size, in); + if (r != statbuf.st_size) { + free (buffer); + fclose (in); + return nullptr; + } + buffer [r] = '\0'; + fclose (in); + return buffer; +} +void emitData (double * x, double * y, const int len) { + for (int n=0; n +#include +#include +#include +#include +#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(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 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 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