Skip to content

Instantly share code, notes, and snippets.

@piotrmaslanka
Last active February 3, 2022 20:14
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save piotrmaslanka/6534e55bf8eb2a46d6b324365759a9f1 to your computer and use it in GitHub Desktop.
A function plotter in C++. You give it a function in Lua via stdin, it plots it. Compile using Visual Studio. Requires lua.hpp.
#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