Last active
February 3, 2022 20:14
Star
You must be signed in to star a gist
A function plotter in C++. You give it a function in Lua via stdin, it plots it. Compile using Visual Studio. Requires lua.hpp.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <windows.h> | |
#include <iostream> | |
#include <string> | |
using namespace std; | |
#include <lua.hpp> // hpp to plik nagłówkowy C++, taki ukłon w naszą stronę ze strony autorów Lua | |
extern "C" // lua jest pisana w C, uzyj wiec odpowiedniego importu | |
{ | |
#include "lualib.h" | |
#include "lauxlib.h" | |
} | |
/* ------------------ | |
PASKUDNIE ZAAWANSOWANA APLIKACJA | |
Wykorzystująca: | |
a) Wielowątkowość i wykorzystanie muteksów do synchronizacji wątków | |
b) Obsługę WinAPI i tworzenie konsoli oraz powiązywanie jej z std::cout, std::cin std::cerr w projekcie WinAPI | |
c) Osadzanie i dwustronną komunikację z interpreterem Lua w swoim projekcie C++ | |
d) Rysowanie poprzez interfejs GDI | |
*/ | |
lua_State* luaState; // kontekst lua | |
char windowName[] = "Plot"; // nazwa klasy okna WinAPI | |
HWND hwnd; // hwnd okna | |
HANDLE hMoznaRysowacMutex; // mutexy do koordynacji watkow winProc i parsera | |
HANDLE hSkonczylemRysowacMutex; | |
WNDCLASSEX wc; // klasa okna winapi | |
bool firstDraw = true; // czy to pierwsze wywolanie WM_PAINT? | |
DWORD WINAPI main(LPVOID parametr); // musi mieć taki nagłówek - jest odpalana jako nowy wątek | |
static int plot(lua_State *L); // nasza funkcja dostępna z poziomu lua | |
static int clear(lua_State *L); | |
static int about(lua_State *L); | |
int sxClient = 400; // rozmiar okna | |
int syClient = 400; | |
int hxClient = 200; | |
int hyClient = 200; // polowa poprzednich wartosci | |
lua_Number prescaleX; // lua_Number to alias do double, z tego co mówi IntelliSense | |
lua_Number prescaleY; // prescale do plot() | |
LRESULT CALLBACK winProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch(message) | |
{ | |
case WM_PAINT: | |
ReleaseMutex(hMoznaRysowacMutex); // pozwol rysowac plot() lub clear() | |
WaitForSingleObject(hSkonczylemRysowacMutex, INFINITE); // i poczekaj az skonczy | |
break; | |
case WM_SIZE: | |
if (wParam == 0) | |
{ | |
sxClient = LOWORD(lParam); | |
syClient = HIWORD(lParam); | |
hxClient = sxClient/2; | |
hyClient = syClient/2; | |
} | |
break; | |
case WM_DESTROY: | |
PostQuitMessage(0); | |
break; | |
default: | |
return DefWindowProc(hwnd, message, wParam, lParam); | |
} | |
return 0; | |
} | |
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrev,LPSTR lpszCmd,int nCmd) | |
{ | |
AllocConsole(); | |
freopen("CONIN$","rb",stdin); | |
freopen("CONOUT$","wb",stdout); | |
freopen("CONOUT$","wb",stderr); // konsolki | |
luaState = luaL_newstate(); | |
luaL_openlibs(luaState); // stworz kontekst Lua | |
lua_pushstring(luaState, "plot"); | |
lua_pushcfunction(luaState, plot); | |
lua_settable(luaState, LUA_GLOBALSINDEX); // zarejestruj plot() jako funkcje globalna | |
lua_pushstring(luaState, "clear"); | |
lua_pushcfunction(luaState, clear); | |
lua_settable(luaState, LUA_GLOBALSINDEX); // zarejestruj clear() jako funkcje globalna | |
lua_pushstring(luaState, "about"); | |
lua_pushcfunction(luaState, about); | |
lua_settable(luaState, LUA_GLOBALSINDEX); // zarejestruj about() jako funkcje globalna | |
luaL_dostring(luaState, "sqrt = math.sqrt"); | |
luaL_dostring(luaState, "sin = math.sin"); | |
luaL_dostring(luaState, "cos = math.cos"); | |
luaL_dostring(luaState, "abs = math.abs"); | |
luaL_dostring(luaState, "pi = math.pi"); | |
luaL_dostring(luaState, "deg = math.deg"); | |
luaL_dostring(luaState, "rad = math.rad"); | |
luaL_dostring(luaState, "exp = math.exp"); | |
luaL_dostring(luaState, "log = math.log"); | |
luaL_dostring(luaState, "log10 = math.log10"); // aliasy | |
luaL_dostring(luaState, "prescaleX = 5"); | |
luaL_dostring(luaState, "prescaleY = 5"); // ustaw prescale | |
// przygotuj WinAPI | |
// mutex | |
hMoznaRysowacMutex = CreateMutex(NULL, TRUE, NULL); | |
hSkonczylemRysowacMutex = CreateMutex(NULL, TRUE, NULL); | |
//okno | |
MSG msg; | |
wc.hInstance = hInst; | |
wc.lpszClassName = windowName; | |
wc.lpfnWndProc = winProc; | |
wc.style = 0; // 0 zeby winAPI nie spamowalo tak WM_PAINT | |
wc.cbSize = sizeof(WNDCLASSEX); | |
wc.cbClsExtra = 0; | |
wc.cbWndExtra = 0; | |
if (!RegisterClassEx(&wc)) | |
{ | |
MessageBox(NULL, "Nie zarejestrowalem klasy okna!", "LOL EPIC FAIL" ,MB_OK | MB_ICONEXCLAMATION); | |
return 0; | |
} | |
hwnd = CreateWindow(windowName, "PLOT", WS_OVERLAPPED | WS_SIZEBOX, 20, 20, 400, 400, HWND_DESKTOP, NULL, hInst, NULL); | |
ShowWindow(hwnd, nCmd); | |
UpdateWindow(hwnd); | |
// odpal wątek do obsługi parsera | |
DWORD idwatku; | |
CreateThread(NULL, 0, main, NULL, 0, &idwatku); | |
while(GetMessage(&msg, NULL, 0, 0)) | |
{ | |
TranslateMessage(&msg); | |
DispatchMessage(&msg); | |
} | |
FreeConsole(); | |
return msg.wParam; | |
} | |
static int clear(lua_State *L) | |
{ | |
if (lua_gettop(L) != 0) | |
{ | |
lua_pushstring(L, "clear() musi byc wywolywane bez argumentow!"); | |
lua_error(L); | |
} | |
InvalidateRect(hwnd, NULL, TRUE); // wymuś WM_PAINT | |
WaitForSingleObject(hMoznaRysowacMutex, INFINITE); // poczekaj na pozwolenie do rysowania | |
HDC hdc; | |
HDC memDC; | |
PAINTSTRUCT ps; | |
hdc = BeginPaint(hwnd, &ps); | |
memDC = CreateCompatibleDC(hdc); | |
HBRUSH br; // wez brusha, wez rozmiar okna i wyczysc na zero | |
RECT big; | |
GetClientRect(hwnd, &big); | |
br = CreateSolidBrush(RGB(255, 255, 255)); | |
FillRect(hdc, &big, br); | |
DeleteObject(br); | |
MoveToEx(hdc, hxClient, 0, NULL); // narysuj ośki | |
LineTo(hdc, hxClient, syClient); | |
MoveToEx(hdc, 0, hyClient, NULL); | |
LineTo(hdc, sxClient, hyClient); | |
EndPaint(hwnd, &ps); | |
DeleteDC(memDC); | |
ReleaseMutex(hSkonczylemRysowacMutex); | |
return 0; | |
} | |
static int about(lua_State *L) | |
{ | |
int n = lua_gettop(L); | |
if (n != 0) | |
{ | |
lua_pushstring(L, "Bledna liczba argumentow!"); // komunikat o bledzie... | |
lua_error(L); // nawal paskudnie | |
} | |
lua_pushstring(L, "Autorem programu PLOT v1.0 jest Piotr Maslanka z klasy 4H roku szkolnego 2010/2011\nMilej zabawy!"); | |
return 1; // zwroc jedna wartosc - powyzszy string | |
} | |
static int plot(lua_State *L) | |
{ // moglbym zamiast L pisac luaState, ale po to to L dostaje zeby nie bylo balaganu. Porzadek musi byc. | |
int n = lua_gettop(L); // pobierz indeks najwyzszej wartosci na stosie. Zupelnie przypadkiem ta wartosc jest tozsama z iloscia argumentow do naszej funkcji :D | |
if (n != 1) | |
{ | |
lua_pushstring(L, "Bledna liczba argumentow!"); // komunikat o bledzie... | |
lua_error(L); // nawal paskudnie | |
} | |
if (lua_isfunction(L, 1) == 0) // nasz pierwszy argument nie jest funkcja.. | |
{ | |
lua_pushstring(L, "Argument musi byc funkcja!"); | |
lua_error(L); | |
} | |
InvalidateRect(hwnd, NULL, TRUE); | |
WaitForSingleObject(hMoznaRysowacMutex, INFINITE); | |
lua_Number iterate_from, iterate_step, iterate_to; | |
iterate_from = -(lua_Number)hxClient/prescaleX; | |
iterate_to = (lua_Number)hxClient/prescaleX; | |
iterate_step = 1/prescaleX; | |
HDC hdc; | |
HDC memDC; | |
PAINTSTRUCT ps; | |
hdc = BeginPaint(hwnd, &ps); | |
memDC = CreateCompatibleDC(hdc); | |
lua_Number x, y; // bierzmy pierwszy punkt zeby wykonac MoveToEx() | |
x = iterate_from; | |
lua_pushvalue(L, 1); // wyjasnienie do tego jest dalej | |
lua_pushnumber(L, x); | |
lua_call(L, 1, 1); | |
y = lua_tonumber(L, -1); | |
lua_pop(L, 1); // ten numer ciagle jest na stosie! Zdejmij 1 element - czyli jego! | |
MoveToEx(hdc, (int)(x*prescaleX)+hxClient, syClient-((int)(y*prescaleY)+hyClient), NULL); // rusz sie na pierwszy punkt | |
for (x=iterate_from; x<iterate_to; x+=iterate_step) | |
{ | |
lua_pushvalue(L, 1); // zduplikuj wskaznik do funkcji na stosie, bo przy pcallu() go wyssie i nie bedziemy miec do nastepnych wywolan | |
lua_pushnumber(L, x); // wrzuc parametr | |
lua_call(L, 1, 1); // wywolaj | |
y = lua_tonumber(L, -1); // wynik jest na szczycie stosu i jest liczba, pobierz... | |
LineTo(hdc, (int)(x*prescaleX)+hxClient, syClient-((int)(y*prescaleY)+hyClient)); | |
lua_pop(L, 1); // to LineTo() fachowo nazywaloby sie interpolacja liniowa, na szczescie iterate_step jest ustawiony tak ze fizycznie funkcja odpytywana jest | |
} // co jeden rzeczywisty piksel - biore pod uwage prescaleX. Wciaz, LineTo() pozwoli uniknac brzydkich artefaktow przy szybkozmiennych funkcjach. | |
EndPaint(hwnd, &ps); | |
DeleteDC(memDC); | |
ReleaseMutex(hSkonczylemRysowacMutex); | |
return 0; | |
} | |
DWORD WINAPI main(LPVOID parametr) | |
{ | |
cout << "Domyslnie prescaleX = 5; prescaleY = 5;" << endl; | |
clear(luaState); // wyczysc okno jakbys sie nudzil | |
std::string input = ""; | |
std::string command = ""; | |
while (true) | |
{ | |
// odswiez nasza znajomosc prescale. Uzytkownik wszakze mogl cos zmienic :) | |
lua_getfield(luaState, LUA_GLOBALSINDEX, "prescaleX"); // pobierz z globalek prescaleX | |
prescaleX = lua_tonumber(luaState, -1); // i zapisz jako liczbe | |
lua_pop(luaState, 1); // usun liczbe ze stosu | |
lua_getfield(luaState, LUA_GLOBALSINDEX, "prescaleY"); | |
prescaleY = lua_tonumber(luaState, -1); | |
lua_pop(luaState, 1); // usun liczbe i tablice globalnych | |
input = ""; | |
getline(std::cin, input, '\n'); | |
input.erase(--input.end()); // usun ostatni znak ('\n') | |
command = "return "+input; // dopisz returna bo user zwykle wpisuje 2+2. To nie ma sensu, ale return 2+2 juz ma sens. | |
if (luaL_dostring(luaState, command.c_str()) == 1) // zrob polecenie z dopisanym returnem i sprawdz blad ( 0 to kod ze poprawnie) | |
{ // nie powinnismy chyba dopisywac tego returna - wystapil blad | |
luaL_dostring(luaState, input.c_str()); | |
lua_settop(luaState, 0); // wyczysc stos Lua | |
} else // blad nie wystapil | |
{ | |
while (lua_gettop(luaState) > 0) // dopoki COS jest na stosie(funkcja moze zwracac wiele wartosci) | |
{ | |
const char* wynik; | |
wynik = lua_tostring(luaState, -1); // zrob to COS na stringa | |
// uwaga! wynik ma tylko sens dopoty dopoki ta wartosc jest na stosie | |
// gdyz jest tylko pointerem gdzies na stos Lua. Ostroznie. | |
if (wynik != NULL) // czy konwersja sie powiodla? | |
{ | |
cout << wynik << endl; // tak, owszem | |
} else | |
{ // nie powiodla sie, idziemy na Rambo | |
if (lua_isnil(luaState, -1) == 1) cout << "Wartosc typu NIL" << endl; | |
else if (lua_isfunction(luaState, -1) == 1) cout << "Wartosc typu funkcja" << endl; | |
else cout << "Nieznany typ wartosci" << endl; | |
} | |
lua_pop(luaState, 1); // zabierz ta wartosc ze stosu | |
} | |
cout << endl; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment