From 376e1a7f9207fffb1ec3027ac1e7f32db5de4922 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Sat, 6 Feb 2021 18:39:48 -0500 Subject: Initial commit --- backend/.vs/ProjectSettings.json | 3 + backend/.vs/VSWorkspaceState.json | 6 + backend/.vs/backend/v16/.suo | Bin 0 -> 15872 bytes backend/.vs/backend/v16/Browse.VC.db | Bin 0 -> 253952 bytes backend/.vs/slnx.sqlite | Bin 0 -> 90112 bytes backend/Server/.vs/Server/v16/.suo | Bin 0 -> 32768 bytes backend/Server/.vs/Server/v16/Browse.VC.db | Bin 0 -> 34717696 bytes .../v16/ipch/AutoPCH/8c0889d90d2b555a/SERVER.ipch | Bin 0 -> 45285376 bytes backend/Server/Debug/Server.exe | Bin 0 -> 49152 bytes backend/Server/Debug/Server.ilk | Bin 0 -> 570180 bytes backend/Server/Debug/Server.pdb | Bin 0 -> 634880 bytes backend/Server/Server.sln | 31 ++ backend/Server/Server/Debug/Server.exe.recipe | 11 + backend/Server/Server/Debug/Server.log | 5 + .../Server/Debug/Server.tlog/CL.command.1.tlog | Bin 0 -> 884 bytes .../Server/Server/Debug/Server.tlog/CL.read.1.tlog | Bin 0 -> 27356 bytes .../Server/Debug/Server.tlog/CL.write.1.tlog | Bin 0 -> 560 bytes .../Server/Debug/Server.tlog/Server.lastbuildstate | 2 + .../Server/Debug/Server.tlog/Server.write.1u.tlog | Bin 0 -> 26450 bytes .../Server/Debug/Server.tlog/link.command.1.tlog | Bin 0 -> 1606 bytes .../Server/Debug/Server.tlog/link.read.1.tlog | Bin 0 -> 11262 bytes .../Server/Debug/Server.tlog/link.write.1.tlog | Bin 0 -> 562 bytes backend/Server/Server/Debug/server.obj | Bin 0 -> 53092 bytes backend/Server/Server/Debug/vc142.idb | Bin 0 -> 265216 bytes backend/Server/Server/Debug/vc142.pdb | Bin 0 -> 167936 bytes backend/Server/Server/Debug/vcpkg.applocal.log | 1 + backend/Server/Server/Server.vcxproj | 147 +++++++++ backend/Server/Server/Server.vcxproj.filters | 22 ++ backend/Server/Server/Server.vcxproj.user | 7 + backend/server.cpp | 360 +++++++++++++++++++++ backend/server.obj | Bin 0 -> 2927 bytes frontend/_rigidbody/circle.js | 58 ++++ frontend/_rigidbody/mat4.js | 43 +++ frontend/_rigidbody/program_common.js | 72 +++++ frontend/_rigidbody/rigidbody_1.js | 106 ++++++ frontend/_rigidbody/rigidbody_2.js | 78 +++++ frontend/_rigidbody/shader.js | 60 ++++ frontend/_rigidbody/shaders/orthographic.frag | 5 + frontend/_rigidbody/shaders/orthographic.vert | 13 + frontend/_rigidbody/vec2.js | 41 +++ frontend/dog-114-203766.png | Bin 0 -> 8343 bytes frontend/dog-120-203766.png | Bin 0 -> 8262 bytes frontend/dog-144-203766.png | Bin 0 -> 9834 bytes frontend/dog-152-203766.png | Bin 0 -> 8748 bytes frontend/dog-16-203766.png | Bin 0 -> 969 bytes frontend/dog-24-203766.png | Bin 0 -> 1203 bytes frontend/dog-32-203766.png | Bin 0 -> 1742 bytes frontend/dog-48-203766.png | Bin 0 -> 3026 bytes frontend/dog-512-203766.png | Bin 0 -> 41072 bytes frontend/dog-57-203766.png | Bin 0 -> 3686 bytes frontend/dog-64-203766.png | Bin 0 -> 4111 bytes frontend/dog-72-203766.png | Bin 0 -> 4493 bytes frontend/favicon.ico | Bin 0 -> 32038 bytes frontend/index.css | 211 ++++++++++++ frontend/index.html | 23 ++ frontend/index.js | 10 + frontend/readme.txt | 26 ++ frontend/rigidbody.html | 152 +++++++++ frontend/scripts/jquery-3.5.1.min.js | 1 + 59 files changed, 1494 insertions(+) create mode 100644 backend/.vs/ProjectSettings.json create mode 100644 backend/.vs/VSWorkspaceState.json create mode 100644 backend/.vs/backend/v16/.suo create mode 100644 backend/.vs/backend/v16/Browse.VC.db create mode 100644 backend/.vs/slnx.sqlite create mode 100644 backend/Server/.vs/Server/v16/.suo create mode 100644 backend/Server/.vs/Server/v16/Browse.VC.db create mode 100644 backend/Server/.vs/Server/v16/ipch/AutoPCH/8c0889d90d2b555a/SERVER.ipch create mode 100644 backend/Server/Debug/Server.exe create mode 100644 backend/Server/Debug/Server.ilk create mode 100644 backend/Server/Debug/Server.pdb create mode 100644 backend/Server/Server.sln create mode 100644 backend/Server/Server/Debug/Server.exe.recipe create mode 100644 backend/Server/Server/Debug/Server.log create mode 100644 backend/Server/Server/Debug/Server.tlog/CL.command.1.tlog create mode 100644 backend/Server/Server/Debug/Server.tlog/CL.read.1.tlog create mode 100644 backend/Server/Server/Debug/Server.tlog/CL.write.1.tlog create mode 100644 backend/Server/Server/Debug/Server.tlog/Server.lastbuildstate create mode 100644 backend/Server/Server/Debug/Server.tlog/Server.write.1u.tlog create mode 100644 backend/Server/Server/Debug/Server.tlog/link.command.1.tlog create mode 100644 backend/Server/Server/Debug/Server.tlog/link.read.1.tlog create mode 100644 backend/Server/Server/Debug/Server.tlog/link.write.1.tlog create mode 100644 backend/Server/Server/Debug/server.obj create mode 100644 backend/Server/Server/Debug/vc142.idb create mode 100644 backend/Server/Server/Debug/vc142.pdb create mode 100644 backend/Server/Server/Debug/vcpkg.applocal.log create mode 100644 backend/Server/Server/Server.vcxproj create mode 100644 backend/Server/Server/Server.vcxproj.filters create mode 100644 backend/Server/Server/Server.vcxproj.user create mode 100644 backend/server.cpp create mode 100644 backend/server.obj create mode 100644 frontend/_rigidbody/circle.js create mode 100644 frontend/_rigidbody/mat4.js create mode 100644 frontend/_rigidbody/program_common.js create mode 100644 frontend/_rigidbody/rigidbody_1.js create mode 100644 frontend/_rigidbody/rigidbody_2.js create mode 100644 frontend/_rigidbody/shader.js create mode 100644 frontend/_rigidbody/shaders/orthographic.frag create mode 100644 frontend/_rigidbody/shaders/orthographic.vert create mode 100644 frontend/_rigidbody/vec2.js create mode 100644 frontend/dog-114-203766.png create mode 100644 frontend/dog-120-203766.png create mode 100644 frontend/dog-144-203766.png create mode 100644 frontend/dog-152-203766.png create mode 100644 frontend/dog-16-203766.png create mode 100644 frontend/dog-24-203766.png create mode 100644 frontend/dog-32-203766.png create mode 100644 frontend/dog-48-203766.png create mode 100644 frontend/dog-512-203766.png create mode 100644 frontend/dog-57-203766.png create mode 100644 frontend/dog-64-203766.png create mode 100644 frontend/dog-72-203766.png create mode 100644 frontend/favicon.ico create mode 100644 frontend/index.css create mode 100644 frontend/index.html create mode 100644 frontend/index.js create mode 100644 frontend/readme.txt create mode 100644 frontend/rigidbody.html create mode 100644 frontend/scripts/jquery-3.5.1.min.js diff --git a/backend/.vs/ProjectSettings.json b/backend/.vs/ProjectSettings.json new file mode 100644 index 0000000..0cf5ea5 --- /dev/null +++ b/backend/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": "No Configurations" +} \ No newline at end of file diff --git a/backend/.vs/VSWorkspaceState.json b/backend/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..6b61141 --- /dev/null +++ b/backend/.vs/VSWorkspaceState.json @@ -0,0 +1,6 @@ +{ + "ExpandedNodes": [ + "" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/backend/.vs/backend/v16/.suo b/backend/.vs/backend/v16/.suo new file mode 100644 index 0000000..b4714d2 Binary files /dev/null and b/backend/.vs/backend/v16/.suo differ diff --git a/backend/.vs/backend/v16/Browse.VC.db b/backend/.vs/backend/v16/Browse.VC.db new file mode 100644 index 0000000..f98ab3e Binary files /dev/null and b/backend/.vs/backend/v16/Browse.VC.db differ diff --git a/backend/.vs/slnx.sqlite b/backend/.vs/slnx.sqlite new file mode 100644 index 0000000..40c8fc4 Binary files /dev/null and b/backend/.vs/slnx.sqlite differ diff --git a/backend/Server/.vs/Server/v16/.suo b/backend/Server/.vs/Server/v16/.suo new file mode 100644 index 0000000..5ca583f Binary files /dev/null and b/backend/Server/.vs/Server/v16/.suo differ diff --git a/backend/Server/.vs/Server/v16/Browse.VC.db b/backend/Server/.vs/Server/v16/Browse.VC.db new file mode 100644 index 0000000..e2d4096 Binary files /dev/null and b/backend/Server/.vs/Server/v16/Browse.VC.db differ diff --git a/backend/Server/.vs/Server/v16/ipch/AutoPCH/8c0889d90d2b555a/SERVER.ipch b/backend/Server/.vs/Server/v16/ipch/AutoPCH/8c0889d90d2b555a/SERVER.ipch new file mode 100644 index 0000000..5f68daa Binary files /dev/null and b/backend/Server/.vs/Server/v16/ipch/AutoPCH/8c0889d90d2b555a/SERVER.ipch differ diff --git a/backend/Server/Debug/Server.exe b/backend/Server/Debug/Server.exe new file mode 100644 index 0000000..7845e21 Binary files /dev/null and b/backend/Server/Debug/Server.exe differ diff --git a/backend/Server/Debug/Server.ilk b/backend/Server/Debug/Server.ilk new file mode 100644 index 0000000..5f58ea9 Binary files /dev/null and b/backend/Server/Debug/Server.ilk differ diff --git a/backend/Server/Debug/Server.pdb b/backend/Server/Debug/Server.pdb new file mode 100644 index 0000000..2df8dd6 Binary files /dev/null and b/backend/Server/Debug/Server.pdb differ diff --git a/backend/Server/Server.sln b/backend/Server/Server.sln new file mode 100644 index 0000000..f8ce6ca --- /dev/null +++ b/backend/Server/Server.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{9CF802AF-02FE-48AA-9198-1DC6B51817FF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Debug|x64.ActiveCfg = Debug|x64 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Debug|x64.Build.0 = Debug|x64 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Debug|x86.ActiveCfg = Debug|Win32 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Debug|x86.Build.0 = Debug|Win32 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Release|x64.ActiveCfg = Release|x64 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Release|x64.Build.0 = Release|x64 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Release|x86.ActiveCfg = Release|Win32 + {9CF802AF-02FE-48AA-9198-1DC6B51817FF}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AAA96BC8-E6A5-4E6A-8EFA-92ADECB2E8D6} + EndGlobalSection +EndGlobal diff --git a/backend/Server/Server/Debug/Server.exe.recipe b/backend/Server/Server/Debug/Server.exe.recipe new file mode 100644 index 0000000..22aff66 --- /dev/null +++ b/backend/Server/Server/Debug/Server.exe.recipe @@ -0,0 +1,11 @@ + + + + + C:\Users\mattykae\Projects\website\backend\Server\Debug\Server.exe + + + + + + \ No newline at end of file diff --git a/backend/Server/Server/Debug/Server.log b/backend/Server/Server/Debug/Server.log new file mode 100644 index 0000000..099ba46 --- /dev/null +++ b/backend/Server/Server/Debug/Server.log @@ -0,0 +1,5 @@ + server.cpp +C:\Users\mattykae\Projects\website\backend\server.cpp(54,28): warning C4305: '=': truncation from 'int' to 'char' +C:\Users\mattykae\Projects\website\backend\server.cpp(54,22): warning C4309: '=': truncation of constant value +C:\Users\mattykae\Projects\website\backend\server.cpp(208,11): warning C4101: 'file': unreferenced local variable + Server.vcxproj -> C:\Users\mattykae\Projects\website\backend\Server\Debug\Server.exe diff --git a/backend/Server/Server/Debug/Server.tlog/CL.command.1.tlog b/backend/Server/Server/Debug/Server.tlog/CL.command.1.tlog new file mode 100644 index 0000000..a695c28 Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/CL.command.1.tlog differ diff --git a/backend/Server/Server/Debug/Server.tlog/CL.read.1.tlog b/backend/Server/Server/Debug/Server.tlog/CL.read.1.tlog new file mode 100644 index 0000000..5cbb32c Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/CL.read.1.tlog differ diff --git a/backend/Server/Server/Debug/Server.tlog/CL.write.1.tlog b/backend/Server/Server/Debug/Server.tlog/CL.write.1.tlog new file mode 100644 index 0000000..1a45dfc Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/CL.write.1.tlog differ diff --git a/backend/Server/Server/Debug/Server.tlog/Server.lastbuildstate b/backend/Server/Server/Debug/Server.tlog/Server.lastbuildstate new file mode 100644 index 0000000..adc7782 --- /dev/null +++ b/backend/Server/Server/Debug/Server.tlog/Server.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v142:VCToolArchitecture=Native32Bit:VCToolsVersion=14.28.29333:TargetPlatformVersion=10.0.18362.0: +Debug|Win32|C:\Users\mattykae\Projects\website\backend\Server\| diff --git a/backend/Server/Server/Debug/Server.tlog/Server.write.1u.tlog b/backend/Server/Server/Debug/Server.tlog/Server.write.1u.tlog new file mode 100644 index 0000000..d14d9c7 Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/Server.write.1u.tlog differ diff --git a/backend/Server/Server/Debug/Server.tlog/link.command.1.tlog b/backend/Server/Server/Debug/Server.tlog/link.command.1.tlog new file mode 100644 index 0000000..9320e07 Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/link.command.1.tlog differ diff --git a/backend/Server/Server/Debug/Server.tlog/link.read.1.tlog b/backend/Server/Server/Debug/Server.tlog/link.read.1.tlog new file mode 100644 index 0000000..5347f94 Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/link.read.1.tlog differ diff --git a/backend/Server/Server/Debug/Server.tlog/link.write.1.tlog b/backend/Server/Server/Debug/Server.tlog/link.write.1.tlog new file mode 100644 index 0000000..8108896 Binary files /dev/null and b/backend/Server/Server/Debug/Server.tlog/link.write.1.tlog differ diff --git a/backend/Server/Server/Debug/server.obj b/backend/Server/Server/Debug/server.obj new file mode 100644 index 0000000..5b7c6c9 Binary files /dev/null and b/backend/Server/Server/Debug/server.obj differ diff --git a/backend/Server/Server/Debug/vc142.idb b/backend/Server/Server/Debug/vc142.idb new file mode 100644 index 0000000..7f41488 Binary files /dev/null and b/backend/Server/Server/Debug/vc142.idb differ diff --git a/backend/Server/Server/Debug/vc142.pdb b/backend/Server/Server/Debug/vc142.pdb new file mode 100644 index 0000000..21c2483 Binary files /dev/null and b/backend/Server/Server/Debug/vc142.pdb differ diff --git a/backend/Server/Server/Debug/vcpkg.applocal.log b/backend/Server/Server/Debug/vcpkg.applocal.log new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/backend/Server/Server/Debug/vcpkg.applocal.log @@ -0,0 +1 @@ + diff --git a/backend/Server/Server/Server.vcxproj b/backend/Server/Server/Server.vcxproj new file mode 100644 index 0000000..c673f44 --- /dev/null +++ b/backend/Server/Server/Server.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {9cf802af-02fe-48aa-9198-1dc6b51817ff} + Server + 10.0 + + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_CRT_SECURE_NO_WARNINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + + + + + \ No newline at end of file diff --git a/backend/Server/Server/Server.vcxproj.filters b/backend/Server/Server/Server.vcxproj.filters new file mode 100644 index 0000000..77af75f --- /dev/null +++ b/backend/Server/Server/Server.vcxproj.filters @@ -0,0 +1,22 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + \ No newline at end of file diff --git a/backend/Server/Server/Server.vcxproj.user b/backend/Server/Server/Server.vcxproj.user new file mode 100644 index 0000000..c72ed2e --- /dev/null +++ b/backend/Server/Server/Server.vcxproj.user @@ -0,0 +1,7 @@ + + + + $(SolutionDir) + WindowsLocalDebugger + + \ No newline at end of file diff --git a/backend/server.cpp b/backend/server.cpp new file mode 100644 index 0000000..eb371d3 --- /dev/null +++ b/backend/server.cpp @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include + +#pragma comment (lib, "ws2_32.lib") + +#define WIN32_LEAN_AND_MEAN +#define PORT 80 + +enum HttpRequestType { + HttpRequestType_None, + HttpRequestType_Get +}; + +enum HttpRequestError { + HttpRequestError_None, + HttpRequestError_UnsupportedRequest, + HttpRequestError_NoBuffer, + HttpRequestError_ClientDisconnect +}; + +struct HeaderParseResult { + HttpRequestType requestType; + char resource[512]; + HttpRequestError error = HttpRequestError_None; +}; + +void copyUntilStr(char* destination, char* source, const char* cmpstr) { + int index = 0; + char* ptr = source; + int cmpStrLen = strlen(cmpstr); + while (strncmp(ptr, cmpstr, cmpStrLen) != 0) { + destination[index++] = *ptr; + ptr++; + } + + destination[index] = '\0'; +} + +int endsWith(const char *str, const char *suffix) { + size_t str_len = strlen(str); + size_t suffix_len = strlen(suffix); + + return (str_len >= suffix_len) && + (!memcmp(str + str_len - suffix_len, suffix, suffix_len)); +} + +int fixNewLines(char* str, int strSize) { + int removed = 0; + for(int i = 0; i < strSize; i++) { + if(str[i] == '\n') { + str[i] = '\r\n'; + removed++; + } + } + + return removed; +} + +struct HeaderParser { + char buffer[4096]; + int bufferPtr = 0; + int bufferSize = 0; + char line[512]; + int lineIndex = 0 ; + + bool tryReadLine() { + int startIndex = bufferPtr; + int endIndex = startIndex; + while (true) { + if (bufferPtr >= bufferSize) { + endIndex = bufferPtr; + break; + } + + if (strncmp(&buffer[bufferPtr], "\r\n", 2) == 0) { + endIndex = bufferPtr; + bufferPtr += 2; // Move past the new line character + break; + } + + bufferPtr++; + } + + int lineLength = endIndex - startIndex; + if (lineLength == 0) { + return false; + } + + assert(lineLength < 512 && lineLength > 0); + strncpy_s(line, &buffer[startIndex], lineLength); + line[lineLength] = '\0'; + return true; + } + + HeaderParseResult readHeader(SOCKET clientSocket) { + HeaderParseResult retval; + ZeroMemory(buffer, 4096); + + // Wait for client to send data + bufferSize = recv(clientSocket, buffer, 4096, 0); + if (bufferSize == SOCKET_ERROR) { + printf("Error in receive. Quitting\n"); + retval.error = HttpRequestError_NoBuffer; + return retval; + } else if (bufferSize == 0) { + printf("Client disconnected\n"); + retval.error = HttpRequestError_ClientDisconnect; + return retval; + } + + printf("Received message (%d bytes): %s", bufferSize, buffer); + + while (tryReadLine()) { + if (lineIndex == 0) { + // Parse request, only supporting GETs for now + int linePtr = 0; + if (strncmp(line, "GET ", 3) == 0) { + retval.requestType = HttpRequestType_Get; + linePtr += 4; // Move past teh get + } else { + retval.error = HttpRequestError_UnsupportedRequest; + return retval; + } + + copyUntilStr(retval.resource, &line[linePtr], " "); + printf("Get request on resource: %s\n", retval.resource); + } + + lineIndex++; + } + + return retval; + } +}; + +void getCurrentDateStr(char* text) { + time_t now = time(NULL); + sprintf(text, "%s GMT", ctime(&now)); +} + +enum FileType { + FileType_None, + FileType_HTML, + FileType_CSS, + FileType_JS +}; + +enum HttpStatusCode { + HttpStatusCode_OK = 200, + HttpStatusCode_BADREQUEST = 400, + HttpStatusCode_NOTFOUND = 404 +}; + +void sendErrorMessage(SOCKET socket, HttpStatusCode status, const char* errorHtml) { + char timeStr[100]; + char header[512]; + + ZeroMemory(timeStr, 100); + ZeroMemory(header, 100); + + const char* errorMessage; + switch (status) { + case HttpStatusCode_BADREQUEST: + errorMessage = "Bad Request"; + break; + case HttpStatusCode_NOTFOUND: + errorMessage = "Not found"; + break; + default: + errorMessage = "Unknown"; + break; + } + + getCurrentDateStr(timeStr); + int contentLen = strlen(errorHtml); + sprintf(header, "HTTP/1.1 %d %s\r\nDate: %s\r\nContent-Type: text/html; charset=utf-8\r\nConnection: keep-alive\r\nContent-Length: %d\r\n\r\n", status, errorMessage, timeStr, contentLen); + send(socket, header, strlen(header), 0); + + send(socket, errorHtml, contentLen, 0); +} + +#define MAXBUFLEN 1000000 + +int readFileToMemory(char* filepath, char source[MAXBUFLEN + 1]) { + FILE* file; + fopen_s(&file, filepath, "r+"); + + if(file == NULL) { + printf("Failed to read the file\n"); + return -1; + + } + size_t newLen = fread(source, sizeof(char), MAXBUFLEN, file); + if ( ferror( file ) != 0 ) { + fputs("Error reading file", stderr); + return -1; + } + + fclose(file); + return newLen; +} + + +bool trySendFile(SOCKET clientSocket, char* filename) { + FILE* file; + char filePath[128]; + sprintf(filePath, "../../frontend%s", filename); + + char source[MAXBUFLEN + 1]; + int fileSizeBytes = readFileToMemory(filePath, source); + + if (fileSizeBytes <= 0) { + return false; + } + + + const char* contentType; + if (endsWith(filename, ".html")) { + contentType = "text/html"; + } else if (endsWith(filename, ".js")) { + contentType = "application/javascript"; + } else if (endsWith(filename, ".css")) { + contentType = "text/css"; + } else if (endsWith(filename, ".ico")) { + contentType = "image/x-icon"; + } else { + contentType = "text/plain"; + } + + char timeStr[100]; + char header[512]; + + ZeroMemory(timeStr, 100); + ZeroMemory(header, 100); + + getCurrentDateStr(timeStr); + sprintf(header, "HTTP/1.1 200 OK\r\nDate: %s\r\nContent-Type: %s; charset=utf-8\r\nConnection: keep-alive\r\nContent-Length: %d\r\n\r\n", timeStr, contentType, fileSizeBytes); + send(clientSocket, header, strlen(header), 0); + + + int bytesSent = 0; + int bytesRemaining = fileSizeBytes; + while (bytesSent < fileSizeBytes) { + int amountToSend = bytesRemaining >= 4096 ? 4096 : bytesRemaining; // Send 4K bytes at a time max + send(clientSocket, &source[bytesSent], amountToSend, 0); + bytesRemaining -= amountToSend; + bytesSent += amountToSend; + } + + return true; +} + + +int main() { + // Initializing Winsock + WSADATA wsaData; + WORD version = MAKEWORD(2, 2); + + int wsOK = WSAStartup(version, &wsaData); + if (wsOK != 0) { + printf("Cannot initialize winsock: %d\n", wsOK); + return EXIT_FAILURE; + } + + // Creating a socket + SOCKET listeningSocket = socket(AF_INET, SOCK_STREAM, 0); + if (listeningSocket == INVALID_SOCKET) { + printf("Cannot create socket\n"); + return EXIT_FAILURE; + } + + // Bind an IP address and port to a socket + sockaddr_in hint = { 0 }; + hint.sin_family = AF_INET; + hint.sin_port = htons(PORT); // Networking is big endian, while PCs are little endian + hint.sin_addr.S_un.S_addr = htonl(INADDR_ANY); + + bind(listeningSocket, (sockaddr*)&hint, sizeof(hint)); + + // Tell Winsock the socket is for listening + listen(listeningSocket, SOMAXCONN); + + while (true) { + + // Wait for a connection + sockaddr_in client; + int clientSize = sizeof(client); + + SOCKET clientSocket = accept(listeningSocket, (sockaddr*)&client, &clientSize); + if (clientSocket == INVALID_SOCKET) { + printf("Cannot create the client socket\n"); + return EXIT_FAILURE; + } + + char host[NI_MAXHOST]; // Client's remote name + char service[NI_MAXSERV]; // Service (i.e. port) the client is connected on + + ZeroMemory(host, NI_MAXHOST); + ZeroMemory(service, NI_MAXSERV); + + if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) { + printf("Host %s connected on port %s\n", host, service); + } else { + inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST); + printf("Host %s connected on port %d\n", host, ntohs(client.sin_port)); + } + + // Accept and echo message back to client + while (true) { + HeaderParser parser; + HeaderParseResult parseResult = parser.readHeader(clientSocket); + bool shouldBreak = false; + + switch (parseResult.error) { + case HttpRequestError_ClientDisconnect: { + shouldBreak = true; + break; + } + case HttpRequestError_UnsupportedRequest: { + shouldBreak = true; + break; + } + } + + if (shouldBreak) { + break; + } + + switch (parseResult.requestType) { + case HttpRequestType_Get: { + if (strcmp(parseResult.resource, "/") == 0) { + trySendFile(clientSocket, (char*)"/index.html"); + } else { + trySendFile(clientSocket, parseResult.resource); + } + shouldBreak = true; + break; + } + } + + if (shouldBreak) { + break; + } + } + + // Close the socket + closesocket(clientSocket); + } + + // Close listening socket + closesocket(listeningSocket); + + // Cleanup winsock + WSACleanup(); + + return EXIT_SUCCESS; +} \ No newline at end of file diff --git a/backend/server.obj b/backend/server.obj new file mode 100644 index 0000000..59b2db2 Binary files /dev/null and b/backend/server.obj differ diff --git a/frontend/_rigidbody/circle.js b/frontend/_rigidbody/circle.js new file mode 100644 index 0000000..c044597 --- /dev/null +++ b/frontend/_rigidbody/circle.js @@ -0,0 +1,58 @@ +const BYTES_PER_FLOAT = 4; + +function circle(pGl, pRadius, pSegments, pColorList, pInitialPosition) { + const lBuffer = pGl.createBuffer(); + + pGl.bindBuffer(pGl.ARRAY_BUFFER, lBuffer); + + var lBufferedData = []; + vertexCount = 0; + + const lAngleIncrements = (360.0 / pSegments) * (Math.PI / 180.0); + for (let lSegIdx = 0; lSegIdx < pSegments; lSegIdx++) { + const lAngle = lAngleIncrements * lSegIdx, + lNextAngle = lAngleIncrements * (lSegIdx + 1), + lColorIndex = Math.floor(pColorList.length * (lSegIdx / pSegments)), + lColor = pColorList[lColorIndex]; // TODO: Calculate which one to use + + lBufferedData = lBufferedData.concat([ + 0, 0, lColor.x, lColor.y, lColor.z, lColor.w, + pRadius * Math.sin(lAngle), pRadius * Math.cos(lAngle), lColor.x, lColor.y, lColor.z, lColor.w, + pRadius * Math.sin(lNextAngle), pRadius * Math.cos(lNextAngle), lColor.x, lColor.y, lColor.z, lColor.w + ]); + + vertexCount += 3; + } + + pGl.bufferData(pGl.ARRAY_BUFFER, new Float32Array(lBufferedData), pGl.STATIC_DRAW) + pGl.bindBuffer(pGl.ARRAY_BUFFER, undefined); + + return { + buffer: lBuffer, + vertexCount: vertexCount, + position: pInitialPosition || vec2(), + velocity: vec2(), + force: vec2(), + mass: 1, + rotationRadians: 0, + model: mat4(), + radius: pRadius + }; +} + +function renderCircle(pGl, pProgramInfo, pCircle) { + pGl.bindBuffer(pGl.ARRAY_BUFFER, pCircle.buffer); + { + pGl.enableVertexAttribArray(pProgramInfo.attributeLocations.position); + pGl.vertexAttribPointer(pProgramInfo.attributeLocations.position, 2, pGl.FLOAT, false, BYTES_PER_FLOAT * 6, 0); + + pGl.enableVertexAttribArray(pProgramInfo.attributeLocations.color); + pGl.vertexAttribPointer(pProgramInfo.attributeLocations.color, 4, pGl.FLOAT, false, BYTES_PER_FLOAT * 6, BYTES_PER_FLOAT * 2); + } + + pGl.drawArrays(pGl.TRIANGLE_STRIP, 0, pCircle.vertexCount); +} + +function getMomentOfInertia(pCircle) { + return (Math.PI * Math.pow(pCircle.radius, 4)) / 4; +} \ No newline at end of file diff --git a/frontend/_rigidbody/mat4.js b/frontend/_rigidbody/mat4.js new file mode 100644 index 0000000..6ab29e2 --- /dev/null +++ b/frontend/_rigidbody/mat4.js @@ -0,0 +1,43 @@ +function mat4() { + return [ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ] +} + +function orthographic(pLeft, pRight, pBottom, pTop) { + const lResult = mat4(); + lResult[0] = 2.0 / (pRight - pLeft); + lResult[5] = 2.0 / (pTop - pBottom); + lResult[10] = 1.0; + lResult[12] = -(pRight + pLeft) / (pRight - pLeft); + lResult[13] = -(pTop + pBottom) / (pTop - pBottom); + return lResult; +} + +function translateMatrix(m, x, y, z) { + m = [...m]; + m[12] += x; + m[13] += y; + m[14] += z; + return m; +} + +function rotateMatrix2d(m, angle) { + m = [...m]; + m[0] = Math.cos(angle); + m[1] = -Math.sin(angle); + m[4] = Math.sin(angle); + m[5] = Math.cos(angle); + return m; +} + +function scaleMatrix(m, x, y, z) { + m = [...m]; + m[0] = m[0] * x; + m[5] = m[5] * y; + m[10] = m[10] * z; + return m; +} \ No newline at end of file diff --git a/frontend/_rigidbody/program_common.js b/frontend/_rigidbody/program_common.js new file mode 100644 index 0000000..9d097e2 --- /dev/null +++ b/frontend/_rigidbody/program_common.js @@ -0,0 +1,72 @@ +/// +/// + +function getContext(pId, pOnRun) { + const lCanvas = $(pId).find('canvas'), + lPlayButton = $(pId).find('.play_button'), + lStopButton = $(pId).find('.stop_button'), + lGl = lCanvas[0].getContext('webgl'), + lWidth = lCanvas.width(), + lHeight = lCanvas.height(); + + return { + canvas: lCanvas, + playButton: lPlayButton, + stopButton: lStopButton, + gl: lGl, + width: lWidth, + height: lHeight, + perspective: orthographic(0, lWidth, 0, lHeight), + load: function() { + lPlayButton.empty().append($('
').addClass('spin-loader')); + return loadOrthographicShader(lGl).then(function(pProgramInfo) { + lPlayButton.css('display', 'none'); + lStopButton.css('display', 'block'); + return pProgramInfo; + }); + }, + reset: function() { + lPlayButton.css('display', 'block'); + lPlayButton.empty().text('Play'); + lStopButton.css('display', 'none'); + lStopButton.on('click', undefined); + } + }; +} + +function requestUpdateLoop(pFunction, pOnExit) { + let lDeltaTimeSeconds = undefined, + lStartTimeSeconds = undefined, + lIsRunning = true; + + function update(pTimeStamp) { + if (!lIsRunning) { + if (pOnExit) { + pOnExit(); + } + return; + } + + pTimeStamp = pTimeStamp / 1000.0; // Convert to seconds + + // Time calculation + if (lStartTimeSeconds === undefined) { + lStartTimeSeconds = pTimeStamp; + lDeltaTimeSeconds = 0; + } else { + lDeltaTimeSeconds = lStartTimeSeconds - pTimeStamp; + lStartTimeSeconds = pTimeStamp; + } + + pFunction(lDeltaTimeSeconds); + requestAnimationFrame(update); + } + + requestAnimationFrame(update); + + function lExit() { + lIsRunning = false; + } + + return lExit; +} \ No newline at end of file diff --git a/frontend/_rigidbody/rigidbody_1.js b/frontend/_rigidbody/rigidbody_1.js new file mode 100644 index 0000000..5021162 --- /dev/null +++ b/frontend/_rigidbody/rigidbody_1.js @@ -0,0 +1,106 @@ +/// +/// +/// +/// +/// +/// + +function main() { + // Define Constants + const CIRCLE_RADIUS = 16; + const GRAVITY = 9.8; + + // Retrieve context + const lProgramContext = getContext('#rigidbody_1'); + + if (lProgramContext.gl === null) { + console.error('Unable to initialize WebGL. Your browser or machine may not support it.'); + return; + } + + lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); + lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT); + + function run() { + console.log('Running Rigid Body 1'); + lProgramContext.load().then(function(pProgramInfo) { + const lCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [{ x: 0.3, y: 0.3, z: 0.3, w: 1 }], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0)); + + function update(pDeltaTimeSeconds) { + // Physics updates + const lGravityForce = vec2(0, -1.0 * (lCircle.mass * GRAVITY)); + + // Add up the forces acting on the circle + lCircle.force = addVec2(lCircle.force, lGravityForce); + + // Figure out acceleration (a = F / m) + const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass); + + // Calculate the new velocity: v = v0 + a * t + lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds)); + + // Update the position based on velocity: x = x0 + v * t + lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, pDeltaTimeSeconds)); + + // Update the model matrix accordingly + lCircle.model = translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0); + + // Report the current state to the frontend + $('#rigidbody_1_force_field').text(vec2str(lCircle.force)); + $('#rigidbody_1_acceleration_field').text(vec2str(lCurrentAcceleration)); + $('#rigidbody_1_velocity_field').text(vec2str(lCircle.velocity)); + $('#rigidbody_1_position_field').text(vec2str(lCircle.position)); + + // Reset the circle's force vector + lCircle.force = vec2(); + + // Render Code only + lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque + lProgramContext.gl.clearDepth(1.0); // Clear everything + lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST); // Enable depth testing + lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL); // Near things obscure far things + lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT); + + lProgramContext.gl.useProgram(pProgramInfo.program); + lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective); + lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, lCircle.model); + + renderCircle(lProgramContext.gl, pProgramInfo, lCircle); + } + + function addForce(ev) { + ev.preventDefault(); + ev.stopPropagation(); + + const lXValue = $(this).find('.vec2_x_input').val(), + lYValue = $(this).find('.vec2_y_input').val(); + + console.log('Applying force: ' + lXValue + ', ' + lYValue); + lCircle.force = addVec2(lCircle.force, vec2(Number(lXValue), Number(lYValue))); + } + + function cleanup() { + lProgramContext.gl.deleteBuffer(lCircle.buffer); + lProgramContext.gl.deleteProgram(pProgramInfo.program); + lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); + lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT); + } + + function reset() { + lExitRequestFunc(); + lProgramContext.reset(); + $('#rigidbody_1_force_submit_button').unbind('submit').submit(false); + } + + const lExitRequestFunc = requestUpdateLoop(update, cleanup); + lProgramContext.stopButton.on('click', reset); + $('#rigidbody_1_force_submit_button').submit(addForce); + }); + } + + lProgramContext.playButton.on('click', run); + $('#rigidbody_1_force_submit_button').submit(false); + +} + +$(document).ready(main); \ No newline at end of file diff --git a/frontend/_rigidbody/rigidbody_2.js b/frontend/_rigidbody/rigidbody_2.js new file mode 100644 index 0000000..b41fa92 --- /dev/null +++ b/frontend/_rigidbody/rigidbody_2.js @@ -0,0 +1,78 @@ +/// +/// +/// +/// +/// + +function main() { + // Define Constants + const CIRCLE_RADIUS = 16; + const GRAVITY = 9.8; + + // Retrieve context + const lProgramContext = getContext('#rigidbody_2'); + + if (lProgramContext.gl === null) { + console.error('Unable to initialize WebGL. Your browser or machine may not support it.'); + return; + } + + lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); + lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT); + + function run() { + console.log('Running Rigid Body 2'); + lProgramContext.load().then(function(pProgramInfo) { + const lCircle = circle(lProgramContext.gl, CIRCLE_RADIUS, 30, [ + { x: 1, y: 1, z: 0, w: 1 }, + { x: 1, y: 0, z: 1, w: 1 }, + { x: 0, y: 1, z: 1, w: 1 }, + { x: 0, y: 1, z: 0, w: 1 } + ], vec2(lProgramContext.width / 2.0, lProgramContext.height / 2.0)); + + function update(pDeltaTimeSeconds) { + // Same physics updates from previously + const lGravityForce = vec2(0, -1.0 * (lCircle.mass * GRAVITY)); + lCircle.force = addVec2(lCircle.force, lGravityForce); + const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass); + lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, pDeltaTimeSeconds)); + lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, pDeltaTimeSeconds)); + lCircle.rotationRadians += Math.PI * pDeltaTimeSeconds; + lCircle.model = rotateMatrix2d(translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0), lCircle.rotationRadians); + lCircle.force = vec2(); + + // Render Code only + lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); + lProgramContext.gl.clearDepth(1.0); + lProgramContext.gl.enable(lProgramContext.gl.DEPTH_TEST); + lProgramContext.gl.depthFunc(lProgramContext.gl.LEQUAL); + lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT | lProgramContext.gl.DEPTH_BUFFER_BIT); + + lProgramContext.gl.useProgram(pProgramInfo.program); + lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.projection, false, lProgramContext.perspective); + lProgramContext.gl.uniformMatrix4fv(pProgramInfo.uniformLocations.model, false, lCircle.model); + + renderCircle(lProgramContext.gl, pProgramInfo, lCircle); + } + + function cleanup() { + lProgramContext.gl.deleteBuffer(lCircle.buffer); + lProgramContext.gl.deleteProgram(pProgramInfo.program); + lProgramContext.gl.clearColor(0.0, 0.0, 0.0, 1.0); + lProgramContext.gl.clear(lProgramContext.gl.COLOR_BUFFER_BIT); + } + + function reset() { + lExitRequestFunc(); + lProgramContext.reset(); + } + + const lExitRequestFunc = requestUpdateLoop(update, cleanup); + lProgramContext.stopButton.on('click', reset); + }); + } + + lProgramContext.playButton.on('click', run); +} + +$(document).ready(main); \ No newline at end of file diff --git a/frontend/_rigidbody/shader.js b/frontend/_rigidbody/shader.js new file mode 100644 index 0000000..bf08638 --- /dev/null +++ b/frontend/_rigidbody/shader.js @@ -0,0 +1,60 @@ +function loadShader(pGl, pShaderType, pShaderSource) { + const lShader = pGl.createShader(pShaderType); + pGl.shaderSource(lShader, pShaderSource); + pGl.compileShader(lShader); + if (!pGl.getShaderParameter(lShader, pGl.COMPILE_STATUS)) { + alert('An error occurred compiling the shaders: ' + pGl.getShaderInfoLog(lShader)); + pGl.deleteShader(lShader); + return null; + } + + return lShader; +} + +function loadOrthographicShader(pGl) { + var lVertex, lFragment; + + function lLoadShaders() { + const lVertexShader = loadShader(pGl, pGl.VERTEX_SHADER, lVertex); + const lFragmentShader = loadShader(pGl, pGl.FRAGMENT_SHADER, lFragment); + + const lShaderProgram = pGl.createProgram(); + pGl.attachShader(lShaderProgram, lVertexShader); + pGl.attachShader(lShaderProgram, lFragmentShader); + pGl.linkProgram(lShaderProgram); + + if (!pGl.getProgramParameter(lShaderProgram, pGl.LINK_STATUS)) { + alert('Unable to initialize the shader program: ' + pGl.getProgramInfoLog(lShaderProgram)); + return null; + } + + return { + program: lShaderProgram, + attributeLocations: { + position: pGl.getAttribLocation(lShaderProgram, 'position'), + color: pGl.getAttribLocation(lShaderProgram, 'color') + }, + uniformLocations: { + projection: pGl.getUniformLocation(lShaderProgram, 'projection'), + model: pGl.getUniformLocation(lShaderProgram, 'model') + } + } + } + + return fetch('/_rigidbody/shaders/orthographic.vert').then(function(pResponse) { + if (pResponse.status === 200) { + return pResponse.text().then(function(pShader) { + lVertex = pShader; + + return fetch('/_rigidbody/shaders/orthographic.frag').then(function(pResponse) { + if (pResponse.status === 200) { + return pResponse.text().then(function(pShader) { + lFragment = pShader; + return lLoadShaders(); + }); + } + }); + }); + } + }); +} \ No newline at end of file diff --git a/frontend/_rigidbody/shaders/orthographic.frag b/frontend/_rigidbody/shaders/orthographic.frag new file mode 100644 index 0000000..84b6b2e --- /dev/null +++ b/frontend/_rigidbody/shaders/orthographic.frag @@ -0,0 +1,5 @@ +varying lowp vec4 VertexColor; + +void main() { + gl_FragColor = VertexColor; +} \ No newline at end of file diff --git a/frontend/_rigidbody/shaders/orthographic.vert b/frontend/_rigidbody/shaders/orthographic.vert new file mode 100644 index 0000000..0356f9c --- /dev/null +++ b/frontend/_rigidbody/shaders/orthographic.vert @@ -0,0 +1,13 @@ +attribute vec2 position; +attribute vec4 color; + +uniform mat4 projection; +uniform mat4 model; + +varying lowp vec4 VertexColor; + +void main() { + vec4 fragmentPosition = projection * model * vec4(position, 1, 1); + gl_Position = fragmentPosition; + VertexColor = color; +} \ No newline at end of file diff --git a/frontend/_rigidbody/vec2.js b/frontend/_rigidbody/vec2.js new file mode 100644 index 0000000..3b24371 --- /dev/null +++ b/frontend/_rigidbody/vec2.js @@ -0,0 +1,41 @@ +function vec2(x = 0, y = 0) { + return { x: x, y: y}; +} + +function addVec2(v1, v2) { + return { + x: v1.x + v2.x, + y: v1.y + v2.y + }; +} + +function subVec2(v1, v2) { + return { + x: v1.x - v2.x, + y: v1.y - v2.y + }; +} + +function scaleVec2(v, s) { + return { + x: v.x * s, + y: v.y * s + }; +} + +function dot2(v1, v2) { + return v1.x * v2.x + v1.y * v2.y; +} + +function length2(v) { + return Math.sqrt(v.x * v.x + v.y * v.y); +} + +function normalize2(v) { + const lLength = 1.0 / length2(v); + return { x: v.x * lLength, y: v.y * lLength }; +} + +function vec2str(v) { + return `(${v.x.toFixed(2)}, ${v.y.toFixed(2)})`; +} \ No newline at end of file diff --git a/frontend/dog-114-203766.png b/frontend/dog-114-203766.png new file mode 100644 index 0000000..1bed84f Binary files /dev/null and b/frontend/dog-114-203766.png differ diff --git a/frontend/dog-120-203766.png b/frontend/dog-120-203766.png new file mode 100644 index 0000000..8630006 Binary files /dev/null and b/frontend/dog-120-203766.png differ diff --git a/frontend/dog-144-203766.png b/frontend/dog-144-203766.png new file mode 100644 index 0000000..f99123a Binary files /dev/null and b/frontend/dog-144-203766.png differ diff --git a/frontend/dog-152-203766.png b/frontend/dog-152-203766.png new file mode 100644 index 0000000..c0e2c85 Binary files /dev/null and b/frontend/dog-152-203766.png differ diff --git a/frontend/dog-16-203766.png b/frontend/dog-16-203766.png new file mode 100644 index 0000000..bed9af9 Binary files /dev/null and b/frontend/dog-16-203766.png differ diff --git a/frontend/dog-24-203766.png b/frontend/dog-24-203766.png new file mode 100644 index 0000000..b70836f Binary files /dev/null and b/frontend/dog-24-203766.png differ diff --git a/frontend/dog-32-203766.png b/frontend/dog-32-203766.png new file mode 100644 index 0000000..46b6f77 Binary files /dev/null and b/frontend/dog-32-203766.png differ diff --git a/frontend/dog-48-203766.png b/frontend/dog-48-203766.png new file mode 100644 index 0000000..ba10a5b Binary files /dev/null and b/frontend/dog-48-203766.png differ diff --git a/frontend/dog-512-203766.png b/frontend/dog-512-203766.png new file mode 100644 index 0000000..814c34e Binary files /dev/null and b/frontend/dog-512-203766.png differ diff --git a/frontend/dog-57-203766.png b/frontend/dog-57-203766.png new file mode 100644 index 0000000..0ee3c44 Binary files /dev/null and b/frontend/dog-57-203766.png differ diff --git a/frontend/dog-64-203766.png b/frontend/dog-64-203766.png new file mode 100644 index 0000000..d28a1be Binary files /dev/null and b/frontend/dog-64-203766.png differ diff --git a/frontend/dog-72-203766.png b/frontend/dog-72-203766.png new file mode 100644 index 0000000..25c5c50 Binary files /dev/null and b/frontend/dog-72-203766.png differ diff --git a/frontend/favicon.ico b/frontend/favicon.ico new file mode 100644 index 0000000..7c96d7e Binary files /dev/null and b/frontend/favicon.ico differ diff --git a/frontend/index.css b/frontend/index.css new file mode 100644 index 0000000..9139963 --- /dev/null +++ b/frontend/index.css @@ -0,0 +1,211 @@ +body { + font-size: 16px; + font-family: "Open Sans", sans-serif; + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; + align-items: center; + margin: 0rem; + padding: 0rem; +} + +header { + padding-top: 1rem; + width: 1260px; + flex: 0 1 auto; + text-align: center; +} + +header > h1 { + margin: 0rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; +} + +main { + color: #1a1a1a; + width: 1260px; + background-color: white; + flex: 1 1 100%; + height: 100%; + margin-bottom: 1rem; + overflow-y: auto; +} + +nav { + width: 1260px; + text-align: left; + font-size: 1rem; + border-bottom: 1px solid #404040; +} + +nav > a { + color: #404040; + text-decoration: none; + display: inline-block; + min-width: 196px; + padding: 0.25rem; +} + +nav > a:hover { + color: #1a1a1a; + cursor: pointer; +} + +.nav-selected { + color: black; + font-weight: bold; +} + +section { + padding: 1rem; +} + +hr { + width: 90%; +} + +.spin-loader { + border: 5px solid #f3f3f3; + border-top: 5px solid #555; + border-radius: 50%; + width: 50px; + height: 50px; + animation: spin 2s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +section > p { + padding-left: 2rem; + padding-right: 2rem; +} + +/* + Helpful inputs +*/ +.vec2_input_group { + display: flex; + flex-direction: row; + width: 100%; + justify-content: space-evenly; + padding: 0.25rem; +} + +.vec2_input_group > input { + width: 45%; +} + +/* + WebGL container styling from here on. +*/ +section .opengl_canvas_container { + position: relative; + width: 100%; + text-align: center; + display: flex; + flex-direction: row; + justify-content: center; +} + +section .opengl_canvas_container .play_button { + all: unset; + font-size: 2rem; + width: 128px; + height: 128px; + position: absolute; + top: calc(50% - 56px); + left: calc(50% - 220px); + border: 1px solid white; + color: white; + background-color: transparent; + border-radius: 50%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; +} + +section .opengl_canvas_container .play_button:hover { + cursor: pointer; +} + +section .opengl_canvas_container .stop_button { + all: unset; + font-size: 1rem; + background-color: red; + color: white; + position: absolute; + width: 128px; + height: 2rem; + border-radius: 2px; + top: 0.5rem; + left: 612px; + display: none; +} + +section .opengl_canvas_container .stop_button:hover { + cursor: pointer; +} + +section pre { + width: 90%; + overflow-x: auto; + padding-left: 2rem; +} + +.opengl_canvas_sidebar { + width: 312px; + height: 480px; + border: 1px solid #1a1a1a; + border-left: none; + + display: flex; + flex-direction: column; + text-align: left; + justify-content: space-between; +} + +.opengl_canvas_sidebar .opengl_value_tracker { + font-size: 14px; + display: flex; + flex-direction: column; + text-align: left; +} + +.opengl_canvas_sidebar .opengl_value_tracker > li { + padding-bottom: 0.5rem; +} + +.opengl_canvas_sidebar .opengl_value_tracker > li > b { + display: inline-block; + width: 128px; +} + +.opengl_canvas_sidebar .opengl_value_tracker > li > span { + display: inline-block; +} + +/* + Code styles +*/ +.code_keyword { + color: blue; + font-weight: bold; +} + +.code_constant { + font-weight: bold; +} + +.code_str { + color: orange; +} + +.code_comment { + color: green; +} \ No newline at end of file diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..d57d69b --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,23 @@ + + + + + + + + + Simple Website + + +
+

Matt Kae's Programming Blog

+
+ +
+
+ + \ No newline at end of file diff --git a/frontend/index.js b/frontend/index.js new file mode 100644 index 0000000..0cbd896 --- /dev/null +++ b/frontend/index.js @@ -0,0 +1,10 @@ +/// + +function main() { + console.log('Document ready.'); + const lPathName = window.location.pathname.length > 1 && window.location.pathname.startsWith('/') ? window.location.pathname.substr(1) : window.location.pathname; + console.log(lPathName); + $('nav').find('a[href="' + lPathName + '"').addClass('nav-selected'); +} + +$(document).ready(main); \ No newline at end of file diff --git a/frontend/readme.txt b/frontend/readme.txt new file mode 100644 index 0000000..aa00c22 --- /dev/null +++ b/frontend/readme.txt @@ -0,0 +1,26 @@ +Thank you for downloading a favicon from Free Favicon! This favicon was created from an image at http://openclipart.org/ and converted to a favicon and other sizes for you to use in your projects. For more details about this image visit this page: https://openclipart.org/detail/203766/dog + +For more information about how you can use this image from OpenClipArt see this page: http://openclipart.org/may-clipart-be-used-comparison + +Here are the contents of this compressed package: + +* favicon.ico -- The favicon file (supports both 16*16 and 32*32 dimensions). You will need to rename this to favicon.ico and upload to your web site. + + * You can add a favicon to your web page by uploading favicon.ico to Root of your website and inserting the following HTML tag between the ... tags of your web page. + + + +* There are many different sized png files for use a icons for smartphones, tablets and desktop use. + +If you are having any problems installing your favicon we have tips on the Free Favicon blog. http://www.freefavicon.com/blog/ + +Thank you once again for download from Free Favicon! If you like your favicon we appreciate you taking the time to mention our service to others, blogging about us and of course by linking to us. + +We also encourage you to check out Backblaze online backup. We use Backblaze to backup our computers and keep our data safe. They offer a free 15 day trial and are the easiest online backup service we have used. + +Use this link http://www.freefavicon.com/blog/outgoing/backblaze.php to try Backblaze for Free! + +By signing up for Backblaze you help keep Free Favicon free! + +Thank you, +FreeFavicon.com \ No newline at end of file diff --git a/frontend/rigidbody.html b/frontend/rigidbody.html new file mode 100644 index 0000000..510c667 --- /dev/null +++ b/frontend/rigidbody.html @@ -0,0 +1,152 @@ + + + + + + + + + Simple Website + + + + + + + + + + +
+

Matt Kae's Programming Blog

+
+ +
+
+

+

+

+
+
+

Introduction: Rigid Body Physics

+

+ You're most likely here because you have some interest in the world of rigid body physics. Maybe you have some knowledge of rendering via OpenGL or Vulkan, + and you want to begin watching your up-until-now static scene come to life. Well, you're in the right place! In the course of this tutorial series I will walk + you through the entirety of a 2D rigid body physics system entirely in the web. All of this information will be extendable to other languages, but we will use + JavaScript and WebGL in these blog posts. Additionally, much of the information presented here can be extended to 3 dimensions, but 3D carries some complications + with it, that we will discuss in future blog posts. +

+

+ In implementing a rigidy body physics system, we're primarily interested in two sub-fields of physics, namely dynamics and kinematics. Although I'm + far as can be from being an expert in either of these fields, I will explain - from a programmer's persepctive - what they mean to me: +

    +
  • + Kinematics is the study of how an object's movement changes over time. These are the classic position, velocity, and acceleration equations + that you're most likely familiar with from high school or college physics. +
  • +
  • + Dynamics is the study of whats causes kinematic movement. These are the classic force and momentum equations that you may already be familiar + with as well. +
  • +
+

+

+ Finally, I must provide a disclaimer that all of rigid body systems are very math-y. You will need to know a decent amount of vector calculus and linear algebra to really understand + what's going on here. I am going to assume that you have this knowledge. If you don't already have this knowledge, I will try and provide some resources on the Books + n' References page of the website. +

+
+
+
+

Part 1: Linear Forces

+

+ The first - and perhaps easiest - part of implementing any rigid body physics system is getting the entities in your scene to move in response to linear forces. + With this implementation alone, you can achieve an interesting level of realism in your 2D (and even 3D) scene. +

+

+ Let's begin by recalling the relationships between acceleration, velocity, and position. +

+

+ Knowing all this, you should be able to understand the following source code fairly easily; +

+                        
+function update(dtSeconds) {
+    // Add up the forces acting on the circle
+    const GRAVITY = 9.8;
+    const lGravityForce = vec2(0, -1.0 * (lCircle.mass * GRAVITY));
+    lCircle.force = addVec2(lCircle.force, lGravityForce);
+
+    // Figure out acceleration (a = F / m)
+    const lCurrentAcceleration = scaleVec2(lCircle.force, 1.0 / lCircle.mass);
+
+    // Calculate the new velocity: v = v0 + a * t
+    lCircle.velocity = addVec2(lCircle.velocity, scaleVec2(lCurrentAcceleration, dtSeconds));
+
+    // Update the position based on velocity: x = x0 + v * t
+    lCircle.position = addVec2(lCircle.position, scaleVec2(lCircle.velocity, dtSeconds));
+
+    // Update the model matrix accordingly
+    lCircle.model = translateMatrix(mat4(), lCircle.position.x, lCircle.position.y, 0);
+
+    // Reset the force vector for the next update
+    lCircle.force = vec2()
+}
+                        
+                    
+

+
+ +
+
    +
  • Force:N/A
  • +
  • Acceleration:N/A
  • +
  • Velocity:N/A
  • +
  • Position:N/A
  • +
+
+
+ + +
+ +
+
+ + +
+
+
+
+

Part 2: Rotational Forces

+

+

+
+ +
+ +
+ + +
+
+ +
+
+ + \ No newline at end of file diff --git a/frontend/scripts/jquery-3.5.1.min.js b/frontend/scripts/jquery-3.5.1.min.js new file mode 100644 index 0000000..4e83e2d --- /dev/null +++ b/frontend/scripts/jquery-3.5.1.min.js @@ -0,0 +1 @@ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0