From dbd32f11e2a3df38162c70f946b5bfa9a8dedbfa Mon Sep 17 00:00:00 2001
From: Matthew Kosarek <mattkae@protonmail.com>
Date: Thu, 1 Apr 2021 17:02:44 -0400
Subject: Lots of work in progress on pill intersections, but will have to read
 more up on it

---
 frontend/2d/_collisions/pill_line.html             |   9 +-
 frontend/2d/_collisions/pill_line/dist/output.js   |  90 +++---
 frontend/2d/_collisions/pill_line/dist/output.wasm | Bin 40465 -> 45699 bytes
 frontend/2d/_collisions/pill_line/main.cpp         | 348 ++++++++++++++-------
 frontend/shared_cpp/MainLoop.cpp                   |   3 +-
 frontend/shared_cpp/MainLoop.h                     |   4 +-
 frontend/shared_cpp/mathlib.h                      |  29 ++
 7 files changed, 335 insertions(+), 148 deletions(-)

(limited to 'frontend')

diff --git a/frontend/2d/_collisions/pill_line.html b/frontend/2d/_collisions/pill_line.html
index 7f7027e..2afa802 100644
--- a/frontend/2d/_collisions/pill_line.html
+++ b/frontend/2d/_collisions/pill_line.html
@@ -24,9 +24,6 @@
                     </p>
                     <div class="opengl_canvas_container">
                         <canvas id="gl_canvas" width="640" height="480"></canvas>
-                        <div class="opengl_canvas_sidebar">
-
-                        </div>
                         <button id="gl_canvas_play" class="play_button">
                             Play
                         </button>
@@ -34,6 +31,12 @@
                             Stop
                         </button>
                     </div>
+                    <p>
+                        References
+                        <ul>
+                            <li><a href="http://csharphelper.com/blog/2017/08/calculate-where-a-line-segment-and-an-ellipse-intersect-in-c/#intersection_code">Line Segment-Ellipse Intersection</a></li>
+                        </ul>
+                    </p>
                 </article>
             </section>
         </main>
diff --git a/frontend/2d/_collisions/pill_line/dist/output.js b/frontend/2d/_collisions/pill_line/dist/output.js
index e1dfee3..eb8b559 100644
--- a/frontend/2d/_collisions/pill_line/dist/output.js
+++ b/frontend/2d/_collisions/pill_line/dist/output.js
@@ -609,7 +609,7 @@ if (typeof WebAssembly !== 'object') {
 function setValue(ptr, value, type, noSafe) {
   type = type || 'i8';
   if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
-    switch(type) {
+    switch (type) {
       case 'i1': HEAP8[((ptr)>>0)] = value; break;
       case 'i8': HEAP8[((ptr)>>0)] = value; break;
       case 'i16': HEAP16[((ptr)>>1)] = value; break;
@@ -627,7 +627,7 @@ function setValue(ptr, value, type, noSafe) {
 function getValue(ptr, type, noSafe) {
   type = type || 'i8';
   if (type.charAt(type.length-1) === '*') type = 'i32'; // pointers are 32-bit
-    switch(type) {
+    switch (type) {
       case 'i1': return HEAP8[((ptr)>>0)];
       case 'i8': return HEAP8[((ptr)>>0)];
       case 'i16': return HEAP16[((ptr)>>1)];
@@ -808,7 +808,7 @@ function UTF8ArrayToString(heap, idx, maxBytesToRead) {
       if ((u0 & 0xF0) == 0xE0) {
         u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
       } else {
-        if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string on the asm.js/wasm heap to a JS string!');
+        if ((u0 & 0xF8) != 0xF0) warnOnce('Invalid UTF-8 leading byte 0x' + u0.toString(16) + ' encountered when deserializing a UTF-8 string in wasm memory to a JS string!');
         u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63);
       }
 
@@ -884,7 +884,7 @@ function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) {
       heap[outIdx++] = 0x80 | (u & 63);
     } else {
       if (outIdx + 3 >= endIdx) break;
-      if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to an UTF-8 string on the asm.js/wasm heap! (Valid unicode code points should be in range 0-0x1FFFFF).');
+      if (u >= 0x200000) warnOnce('Invalid Unicode code point 0x' + u.toString(16) + ' encountered when serializing a JS string to a UTF-8 string in wasm memory! (Valid unicode code points should be in range 0-0x1FFFFF).');
       heap[outIdx++] = 0xF0 | (u >> 18);
       heap[outIdx++] = 0x80 | ((u >> 12) & 63);
       heap[outIdx++] = 0x80 | ((u >> 6) & 63);
@@ -1252,12 +1252,12 @@ function checkStackCookie() {
 // include: runtime_assertions.js
 
 
-// Endianness check (note: assumes compiler arch was little-endian)
+// Endianness check
 (function() {
   var h16 = new Int16Array(1);
   var h8 = new Int8Array(h16.buffer);
   h16[0] = 0x6373;
-  if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!';
+  if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian! (Run with -s SUPPORT_BIG_ENDIAN=1 to bypass)';
 })();
 
 function abortFnPtrError(ptr, sig) {
@@ -1274,8 +1274,6 @@ var __ATPOSTRUN__ = []; // functions called after the main() is called
 var runtimeInitialized = false;
 var runtimeExited = false;
 
-__ATINIT__.push({ func: function() { ___wasm_call_ctors() } });
-
 function preRun() {
 
   if (Module['preRun']) {
@@ -1292,6 +1290,7 @@ function initRuntime() {
   checkStackCookie();
   assert(!runtimeInitialized);
   runtimeInitialized = true;
+
   
   callRuntimeCallbacks(__ATINIT__);
 }
@@ -1616,6 +1615,8 @@ function createWasm() {
     wasmTable = Module['asm']['__indirect_function_table'];
     assert(wasmTable, "table not found in wasm exports");
 
+    addOnInit(Module['asm']['__wasm_call_ctors']);
+
     removeRunDependency('wasm-instantiate');
   }
   // we can't run yet (except in a pthread, where we have a custom sync instantiator)
@@ -1637,7 +1638,8 @@ function createWasm() {
 
   function instantiateArrayBuffer(receiver) {
     return getBinaryPromise().then(function(binary) {
-      return WebAssembly.instantiate(binary, info);
+      var result = WebAssembly.instantiate(binary, info);
+      return result;
     }).then(receiver, function(reason) {
       err('failed to asynchronously prepare wasm: ' + reason);
 
@@ -1704,12 +1706,8 @@ var ASM_CONSTS = {
 
 
 
-  function abortStackOverflow(allocSize) {
-      abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (_emscripten_stack_get_free() + allocSize) + ' bytes available!');
-    }
-
   function callRuntimeCallbacks(callbacks) {
-      while(callbacks.length > 0) {
+      while (callbacks.length > 0) {
         var callback = callbacks.shift();
         if (typeof callback == 'function') {
           callback(Module); // Pass the module as the first argument.
@@ -1760,6 +1758,11 @@ var ASM_CONSTS = {
       return error.stack.toString();
     }
 
+  var runtimeKeepaliveCounter=0;
+  function keepRuntimeAlive() {
+      return noExitRuntime || runtimeKeepaliveCounter > 0;
+    }
+
   function stackTrace() {
       var js = jsStackTrace();
       if (Module['extraStackTrace']) js += '\n' + Module['extraStackTrace']();
@@ -1783,10 +1786,6 @@ var ASM_CONSTS = {
       return requestAnimationFrame(tick);
     }
 
-  function _emscripten_get_heap_size() {
-      return HEAPU8.length;
-    }
-  
   function emscripten_realloc_buffer(size) {
       try {
         // round size grow request up to wasm page size (fixed 64KB per spec)
@@ -1800,7 +1799,7 @@ var ASM_CONSTS = {
       // anyhow)
     }
   function _emscripten_resize_heap(requestedSize) {
-      var oldSize = _emscripten_get_heap_size();
+      var oldSize = HEAPU8.length;
       // With pthreads, races can happen (another thread might increase the size in between), so return a failure, and let the caller retry.
       assert(requestedSize > oldSize);
   
@@ -1827,7 +1826,7 @@ var ASM_CONSTS = {
   
       // Loop through potential heap size increases. If we attempt a too eager reservation that fails, cut down on the
       // attempted size and reserve a smaller bump instead. (max 3 times, chosen somewhat arbitrarily)
-      for(var cutDown = 1; cutDown <= 4; cutDown *= 2) {
+      for (var cutDown = 1; cutDown <= 4; cutDown *= 2) {
         var overGrownHeapSize = oldSize * (1 + 0.2 / cutDown); // ensure geometric growth
         // but limit overreserving (default to capping at +96MB overgrowth at most)
         overGrownHeapSize = Math.min(overGrownHeapSize, requestedSize + 100663296 );
@@ -1845,7 +1844,7 @@ var ASM_CONSTS = {
     }
 
   var JSEvents={inEventHandler:0,removeAllEventListeners:function() {
-        for(var i = JSEvents.eventHandlers.length-1; i >= 0; --i) {
+        for (var i = JSEvents.eventHandlers.length-1; i >= 0; --i) {
           JSEvents._removeHandler(i);
         }
         JSEvents.eventHandlers = [];
@@ -1859,13 +1858,13 @@ var ASM_CONSTS = {
         function arraysHaveEqualContent(arrA, arrB) {
           if (arrA.length != arrB.length) return false;
   
-          for(var i in arrA) {
+          for (var i in arrA) {
             if (arrA[i] != arrB[i]) return false;
           }
           return true;
         }
         // Test if the given call was already queued, and if so, don't add it again.
-        for(var i in JSEvents.deferredCalls) {
+        for (var i in JSEvents.deferredCalls) {
           var call = JSEvents.deferredCalls[i];
           if (call.targetFunction == targetFunction && arraysHaveEqualContent(call.argsList, argsList)) {
             return;
@@ -1879,7 +1878,7 @@ var ASM_CONSTS = {
   
         JSEvents.deferredCalls.sort(function(x,y) { return x.precedence < y.precedence; });
       },removeDeferredCalls:function(targetFunction) {
-        for(var i = 0; i < JSEvents.deferredCalls.length; ++i) {
+        for (var i = 0; i < JSEvents.deferredCalls.length; ++i) {
           if (JSEvents.deferredCalls[i].targetFunction == targetFunction) {
             JSEvents.deferredCalls.splice(i, 1);
             --i;
@@ -1891,14 +1890,14 @@ var ASM_CONSTS = {
         if (!JSEvents.canPerformEventHandlerRequests()) {
           return;
         }
-        for(var i = 0; i < JSEvents.deferredCalls.length; ++i) {
+        for (var i = 0; i < JSEvents.deferredCalls.length; ++i) {
           var call = JSEvents.deferredCalls[i];
           JSEvents.deferredCalls.splice(i, 1);
           --i;
           call.targetFunction.apply(null, call.argsList);
         }
       },eventHandlers:[],removeAllHandlersOnTarget:function(target, eventTypeString) {
-        for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {
+        for (var i = 0; i < JSEvents.eventHandlers.length; ++i) {
           if (JSEvents.eventHandlers[i].target == target && 
             (!eventTypeString || eventTypeString == JSEvents.eventHandlers[i].eventTypeString)) {
              JSEvents._removeHandler(i--);
@@ -1929,7 +1928,7 @@ var ASM_CONSTS = {
           JSEvents.eventHandlers.push(eventHandler);
           JSEvents.registerRemoveEventListeners();
         } else {
-          for(var i = 0; i < JSEvents.eventHandlers.length; ++i) {
+          for (var i = 0; i < JSEvents.eventHandlers.length; ++i) {
             if (JSEvents.eventHandlers[i].target == eventHandler.target
              && JSEvents.eventHandlers[i].eventTypeString == eventHandler.eventTypeString) {
                JSEvents._removeHandler(i--);
@@ -2085,7 +2084,7 @@ var ASM_CONSTS = {
         }
         return ret;
       },MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:function(i) {
-        return 32 - Math.clz32(i-1);
+        return 32 - Math.clz32(i === 0 ? 0 : i - 1);
       },generateTempBuffers:function(quads, context) {
         var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE);
         context.tempVertexBufferCounters1 = [];
@@ -2213,6 +2212,19 @@ var ASM_CONSTS = {
         }
       },createContext:function(canvas, webGLContextAttributes) {
   
+        // BUG: Workaround Safari WebGL issue: After successfully acquiring WebGL context on a canvas,
+        // calling .getContext() will always return that context independent of which 'webgl' or 'webgl2'
+        // context version was passed. See https://bugs.webkit.org/show_bug.cgi?id=222758 and
+        // https://github.com/emscripten-core/emscripten/issues/13295.
+        // TODO: Once the bug is fixed and shipped in Safari, adjust the Safari version field in above check.
+        if (!canvas.getContextSafariWebGL2Fixed) {
+          canvas.getContextSafariWebGL2Fixed = canvas.getContext;
+          canvas.getContext = function(ver, attrs) {
+            var gl = canvas.getContextSafariWebGL2Fixed(ver, attrs);
+            return ((ver == 'webgl') == (gl instanceof WebGLRenderingContext)) ? gl : null;
+          }
+        }
+  
         var ctx = 
           (webGLContextAttributes.majorVersion > 1)
           ?
@@ -2386,7 +2398,7 @@ var ASM_CONSTS = {
   function _emscripten_webgl_init_context_attributes(attributes) {
       assert(attributes);
       var a = attributes >> 2;
-      for(var i = 0; i < (56>>2); ++i) {
+      for (var i = 0; i < (56>>2); ++i) {
         HEAP32[a+i] = 0;
       }
   
@@ -3004,6 +3016,10 @@ if (!Object.getOwnPropertyDescriptor(Module, "ENV")) Module["ENV"] = function()
 if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_CODES")) Module["ERRNO_CODES"] = function() { abort("'ERRNO_CODES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "ERRNO_MESSAGES")) Module["ERRNO_MESSAGES"] = function() { abort("'ERRNO_MESSAGES' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "setErrNo")) Module["setErrNo"] = function() { abort("'setErrNo' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "inetPton4")) Module["inetPton4"] = function() { abort("'inetPton4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "inetNtop4")) Module["inetNtop4"] = function() { abort("'inetNtop4' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "inetPton6")) Module["inetPton6"] = function() { abort("'inetPton6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "inetNtop6")) Module["inetNtop6"] = function() { abort("'inetNtop6' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "readSockaddr")) Module["readSockaddr"] = function() { abort("'readSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "writeSockaddr")) Module["writeSockaddr"] = function() { abort("'writeSockaddr' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "DNS")) Module["DNS"] = function() { abort("'DNS' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
@@ -3027,7 +3043,12 @@ if (!Object.getOwnPropertyDescriptor(Module, "dynCallLegacy")) Module["dynCallLe
 if (!Object.getOwnPropertyDescriptor(Module, "getDynCaller")) Module["getDynCaller"] = function() { abort("'getDynCaller' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "dynCall")) Module["dynCall"] = function() { abort("'dynCall' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "callRuntimeCallbacks")) Module["callRuntimeCallbacks"] = function() { abort("'callRuntimeCallbacks' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
-if (!Object.getOwnPropertyDescriptor(Module, "abortStackOverflow")) Module["abortStackOverflow"] = function() { abort("'abortStackOverflow' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "runtimeKeepaliveCounter")) Module["runtimeKeepaliveCounter"] = function() { abort("'runtimeKeepaliveCounter' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "keepRuntimeAlive")) Module["keepRuntimeAlive"] = function() { abort("'keepRuntimeAlive' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "runtimeKeepalivePush")) Module["runtimeKeepalivePush"] = function() { abort("'runtimeKeepalivePush' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "runtimeKeepalivePop")) Module["runtimeKeepalivePop"] = function() { abort("'runtimeKeepalivePop' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "callUserCallback")) Module["callUserCallback"] = function() { abort("'callUserCallback' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
+if (!Object.getOwnPropertyDescriptor(Module, "maybeExit")) Module["maybeExit"] = function() { abort("'maybeExit' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "reallyNegative")) Module["reallyNegative"] = function() { abort("'reallyNegative' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "unSign")) Module["unSign"] = function() { abort("'unSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
 if (!Object.getOwnPropertyDescriptor(Module, "reSign")) Module["reSign"] = function() { abort("'reSign' was not exported. add it to EXTRA_EXPORTED_RUNTIME_METHODS (see the FAQ)") };
@@ -3217,7 +3238,6 @@ function callMain(args) {
       return;
     } else if (e == 'unwind') {
       // running an evented main loop, don't immediately exit
-      noExitRuntime = true;
       return;
     } else {
       var toLog = e;
@@ -3327,17 +3347,19 @@ function checkUnflushedContent() {
 
 /** @param {boolean|number=} implicit */
 function exit(status, implicit) {
+  EXITSTATUS = status;
+
   checkUnflushedContent();
 
   // if this is just main exit-ing implicitly, and the status is 0, then we
   // don't need to do anything here and can just leave. if the status is
   // non-zero, though, then we need to report it.
   // (we may have warned about this earlier, if a situation justifies doing so)
-  if (implicit && noExitRuntime && status === 0) {
+  if (implicit && keepRuntimeAlive() && status === 0) {
     return;
   }
 
-  if (noExitRuntime) {
+  if (keepRuntimeAlive()) {
     // if exit() was called, we may warn the user if the runtime isn't actually being shut down
     if (!implicit) {
       var msg = 'program exited (with status: ' + status + '), but EXIT_RUNTIME is not set, so halting execution but not exiting the runtime or preventing further async execution (build with EXIT_RUNTIME=1, if you want a true shutdown)';
@@ -3345,8 +3367,6 @@ function exit(status, implicit) {
     }
   } else {
 
-    EXITSTATUS = status;
-
     exitRuntime();
 
     if (Module['onExit']) Module['onExit'](status);
diff --git a/frontend/2d/_collisions/pill_line/dist/output.wasm b/frontend/2d/_collisions/pill_line/dist/output.wasm
index e282f2c..2056ca5 100755
Binary files a/frontend/2d/_collisions/pill_line/dist/output.wasm and b/frontend/2d/_collisions/pill_line/dist/output.wasm differ
diff --git a/frontend/2d/_collisions/pill_line/main.cpp b/frontend/2d/_collisions/pill_line/main.cpp
index 4187435..9123b50 100644
--- a/frontend/2d/_collisions/pill_line/main.cpp
+++ b/frontend/2d/_collisions/pill_line/main.cpp
@@ -4,94 +4,167 @@
 #include "../../../shared_cpp/mathlib.h"
 #include "../../../shared_cpp/MainLoop.h"
 #include <cstdio>
+#include <cmath>
 #include <emscripten/html5.h>
 #include <unistd.h>
 #include <pthread.h>
 #include <cmath>
 
 // Side note: It is Eastertime, so I chose this easter color palette. Enjoy: https://htmlcolors.com/palette/144/easter
+struct Rigidbody {
+    Vector2 force = { 0, 0 };
+    Vector2 velocity = { 0, 0 };
+    Vector2 position = { 0, 0 };
+    float32 rotationalVelocity  = 0.f;
+    float32 rotation = 0.f;
+    float32 mass = 1.f;
+
+    void reset() {
+        force = { 0, 0 };
+        velocity = { 0, 0 };
+    }
+
+    void applyForce(Vector2 f) {
+        force += f;
+    }
+
+    void applyGravity() {
+        force += Vector2 { 0.f, -25.f };
+    }
+
+    void update(float32 deltaTimeSeconds) {
+        Vector2 acceleration = force / mass;
+        velocity += (acceleration * deltaTimeSeconds);
+        position += (velocity * deltaTimeSeconds);
+        force = Vector2 { 0.f, 0.f };
+
+        rotation += (rotationalVelocity * deltaTimeSeconds);
+    }
+};
 
-//
-// Pill object 
-//
 struct Pill {
-	OrthographicShape shape;
-	float32 width = 1.f;
-	float32 height = 1.f;
-
-	void load(OrthographicRenderer* renderer, float32 numSegments) {
-		// Note that a so-called "pill" is simply an ellipse.
-		// Equation of an ellipse is:
-		//
-		//	x^2 / a^2 + y^2 / b^2 = 1
-		//
-		// or, in parametric form:
-		//
-		// x = a * cos(t), y = b * sin(t)
-		//
-		float32 angleIncrements = (2.f * PI) / numSegments;
-		uint32 numVertices = static_cast<uint32>(numSegments * 3.f);
-		OrthographicVertex* vertices = new OrthographicVertex[numVertices];
-
-		float32 a = width / 2.f;
-		float32 b = height / 2.f;
-
-		Vector4 color = Vector4().fromColor(243,166,207, 255);
-
-		for (uint32 vertexIndex = 0; vertexIndex < numVertices; vertexIndex += 3) {
-			// Create a single "slice" of the ellipse (like a pizza)
-			float32 currAngle = (vertexIndex / 3.f) * angleIncrements;
-			float32 nextAngle = (vertexIndex / 3.f + 1.f) * angleIncrements;
-
-			vertices[vertexIndex].position = Vector2 { 0.f, 0.f };
-			vertices[vertexIndex].color = color;
-
-			vertices[vertexIndex + 1].position = Vector2 { a * cosf(currAngle), b * sinf(currAngle) };
-			vertices[vertexIndex + 1].color = color;
-
-			vertices[vertexIndex + 2].position = Vector2 { a * cosf(nextAngle), b * sinf(nextAngle) };
-			vertices[vertexIndex + 2].color = color;
-		}
-
-		shape.load(vertices, numVertices, renderer);
-		delete[] vertices;
-	}
-
-	void render(OrthographicRenderer* renderer) {
-		shape.render(renderer);
-	}
-
-	void unload() {
-		shape.unload();
-	}
-
-	float32 getArea() {
-		return 0.f;
-	}
+    OrthographicShape shape;
+    Rigidbody body;
+    float32 a = 0;
+    float32 b = 0;;
+
+    void load(OrthographicRenderer* renderer, float32 numSegments, float32 width, float32 height) {
+        // Note that a so-called "pill" is simply an ellipse.
+        // Equation of an ellipse is:
+        //
+        //	x^2 / a^2 + y^2 / b^2 = 1
+        //
+        // or, in parametric form:
+        //
+        // x = a * cos(t), y = b * sin(t)
+        //
+        float32 angleIncrements = (2.f * PI) / numSegments;
+        uint32 numVertices = static_cast<uint32>(numSegments * 3.f);
+        OrthographicVertex* vertices = new OrthographicVertex[numVertices];
+
+        a = width / 2.f;
+        b = height / 2.f;
+
+        Vector4 color = Vector4().fromColor(243,166,207, 255);
+
+        for (uint32 vertexIndex = 0; vertexIndex < numVertices; vertexIndex += 3) {
+            // Create a single "slice" of the ellipse (like a pizza)
+            float32 currAngle = (vertexIndex / 3.f) * angleIncrements;
+            float32 nextAngle = (vertexIndex / 3.f + 1.f) * angleIncrements;
+
+            vertices[vertexIndex].position = Vector2 { 0.f, 0.f };
+            vertices[vertexIndex].color = color;
+
+            vertices[vertexIndex + 1].position = Vector2 { a * cosf(currAngle), b * sinf(currAngle) };
+            vertices[vertexIndex + 1].color = color;
+
+            vertices[vertexIndex + 2].position = Vector2 { a * cosf(nextAngle), b * sinf(nextAngle) };
+            vertices[vertexIndex + 2].color = color;
+        }
+
+        shape.load(vertices, numVertices, renderer);
+        body.reset();
+
+        a = width / 2.f;
+        b = height / 2.f;
+
+        delete[] vertices;
+    }
+
+    void update(float32 deltaTimeSeconds) {
+        body.update(deltaTimeSeconds);
+        shape.model = Mat4x4().translateByVec2(body.position).rotate2D(body.rotation);
+    }
+
+    void render(OrthographicRenderer* renderer) {
+        shape.render(renderer);
+    }
+
+    void unload() {
+        shape.unload();
+    }
+
+    float32 getArea() {
+        return 0.f;
+    }
 };
 
 struct LineSegment {
-	OrthographicShape shape;
-	Vector2 start;
-	Vector2 end;
-	OrthographicVertex vertices[2];
-
-	void load(OrthographicRenderer* renderer, Vector4 color, Vector2 start, Vector2 end) {
-		vertices[0].position = start;
-		vertices[0].color = color;
-		vertices[1].position = end;
-		vertices[1].color = color;
-		shape.load(vertices, 2, renderer);
-	}
-
-	void render(OrthographicRenderer* renderer) {
-		shape.render(renderer, GL_LINES);
-	}
+    OrthographicShape shape;
+    Vector2 start;
+    Vector2 end;
+    Vector2 normal;
+    float32 slope;
+    float32 yIntercept;
+    OrthographicVertex vertices[2];
+
+    void load(OrthographicRenderer* renderer, Vector4 color, Vector2 inStart, Vector2 inEnd) {
+        start = inStart;
+        end = inEnd;
+        slope = (end.y - start.y) / (end.x - start.x);
+        yIntercept = (end.y - slope * end.x);
+        vertices[0].position = start;
+        vertices[0].color = color;
+        vertices[1].position = end;
+        vertices[1].color = color;
+        normal = (end - start).getPerp().normalize();
+        shape.load(vertices, 2, renderer);
+    }
+
+    void render(OrthographicRenderer* renderer) {
+        shape.render(renderer, GL_LINES);
+    }
+
+    void unload() {
+        shape.unload();
+    }
+
+    bool isPointOnLine(Vector2 p) {
+        // If the dot product is nearly zero, that means it is in the direction of the line
+        if (ABS((end - start).dot(p - start)) <= 0.001) {
+
+            // We're on the general line, now let's see if we're within the proper bounds:
+            return p.x >= start.x && p.x <= end.x && p.x >= start.y && p.y <= start.y;
+        }
+        
+        return false;
+    }
+};
+
+struct IntersectionResult {
+    bool intersect = false;
+    Vector2 pointOfIntersection;
+    Vector2 collisionNormal;
+    Vector2 relativeVelocity;
 };
 
 EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
 EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
-EM_BOOL update(float time, void* userData);
+
+void load();
+void update(float32 time, void* userData);
+void unload();
+IntersectionResult getIntersection(Pill* pill, LineSegment* segment);
 
 // Global Variables
 WebglContext context;
@@ -101,51 +174,112 @@ MainLoop mainLoop;
 LineSegment segmentList[4];
 
 int main() {
-	context.init("#gl_canvas");
-	emscripten_set_click_callback("#gl_canvas_play", NULL, false, onPlayClicked);
-	emscripten_set_click_callback("#gl_canvas_stop", NULL, false, onStopClicked);
-	return 0;
+    context.init("#gl_canvas");
+    emscripten_set_click_callback("#gl_canvas_play", NULL, false, onPlayClicked);
+    emscripten_set_click_callback("#gl_canvas_stop", NULL, false, onStopClicked);
+    return 0;
+}
+
+void load() {
+    renderer.load(&context);
+
+    pill.body.position = Vector2 { context.width / 2.f, context.height / 2.f };
+    pill.body.rotationalVelocity = 0.3f;
+    pill.load(&renderer, 64, 100.f, 50.f);
+
+    segmentList[0].load(&renderer, Vector4().fromColor(191, 251, 146, 255.f), Vector2 { 50.f, 0.f }, Vector2 { 50.f, static_cast<float>(context.height) });
+    segmentList[1].load(&renderer, Vector4().fromColor(159, 224, 210, 255.f), Vector2 { context.width - 50.f, 0.f }, Vector2 { context.width - 50.f, static_cast<float>(context.height) });
+    segmentList[2].load(&renderer, Vector4().fromColor(248, 255, 156, 255.f), Vector2 { 50.f, 50.f }, Vector2 { context.width - 50.f, 150.f });
+    segmentList[3].load(&renderer, Vector4().fromColor(205, 178, 214, 255.f), Vector2 { 50.f, 150.f }, Vector2 { context.width - 50.f, 50.f });
+
+    mainLoop.run(update);
+}
+
+float32 areaOfTriangle(Vector2 a, Vector2 b, Vector2 c) {
+    // Refernce for this for the formula: https://www.onlinemath4all.com/area-of-triangle-using-determinant-formula.html
+    return ABS(0.5 * (a.x * b.y - b.x * a.y + b.x * c.y - c.x * b.y + c.x * a.y - a.x * c.y));
+}
+
+IntersectionResult getIntersection(Pill* pill, LineSegment* segment) {
+    IntersectionResult result;
+
+    // Solve quadratic equation to see if the imaginary line defined by our line segment intersects the ellipse.
+    // Equation: x^2 / a^2 + y^2 / b^2 = 1
+    // (x^2 / a^2) + (line equation) ^2 / b^2 = 1
+    // => x^2 / (pill->a * pill->a) + (segment->slope * x + segment->yIntercept) / (pill->b * pill ->b) = 1.f;
+    
+
+    // Build a triangle defined by the following to vectors:
+    Vector2 startToCenter = pill->body.position - segment->start;
+    Vector2 startToEnd = segment->end - segment->start;
+    Vector2 endToCenter = pill->body.position - segment->end;
+
+    // Get the area of this triangle using the properties of the determinant:
+    float32 area = areaOfTriangle(startToCenter, startToEnd, endToCenter);
+    float32 base = startToEnd.length();
+
+    // Knowning that Area = 0.5 Base * Height
+    float32 height = (2.f * area) / base;     // Distance from center of pill to line
+
+    if (height <= MAX(pill->b, pill->a) && height >= MIN(pill->b, pill->a)) {
+        // We at least have an intersection: Half the problem solved!
+        result.intersect = true;
+    } else {
+        result.intersect = false;
+        return result;
+    }
+
+    // Time to find where we intersected. We can do this by getting the intersection depth.
+
+    
+    Vector2 transformedX = pill->shape.model.multByVec2(Vector2 { pill->a / 2.f, 0.f });
+    Vector2 transformedY = pill->shape.model.multByVec2(Vector2 { pill->b / 2.f, 0.f });
+
+    return result;
 }
 
-EM_BOOL update(float deltaTimeSeconds, void* userData) {
-	renderer.render();
-	pill.shape.render(&renderer);
+void update(float32 deltaTimeSeconds, void* userData) {
+    // Input
+    pill.body.applyGravity();
+
+    // Update
+    pill.update(deltaTimeSeconds);
 
-	for (int segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
-		segmentList[segmentIndex].render(&renderer);
-	}
+    // Intersections
+    for (int32 lineIdx = 0; lineIdx < 4; lineIdx++) {
+        getIntersection(&pill, &segmentList[lineIdx]);
+    }
+
+    // Render
+    renderer.render();
+    pill.shape.render(&renderer);
+
+    for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
+        segmentList[segmentIndex].render(&renderer);
+    }
+}
 
-	return true;
+void unload() {
+    mainLoop.stop();
+    pill.unload();
+    renderer.unload();
+    for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
+        segmentList[segmentIndex].unload();
+    }
 }
 
 //
 // Interactions with DOM handled below
 //
 EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
-	printf("Play clicked\n");
-	
-	renderer.load(&context);
-
-	// Add the pill
-	pill.width = 100.f;
-	pill.height = 50.f;
-	pill.shape.model = Mat4x4().translateByVec2(Vector2 { context.width / 2.f, context.height / 2.f });
-	pill.load(&renderer, 64);
-
-	// Add the lines
-	segmentList[0].load(&renderer, Vector4().fromColor(191, 251, 146, 255.f), Vector2 { 50.f, 0.f }, Vector2 { 50.f, static_cast<float>(context.height) });
-	segmentList[1].load(&renderer, Vector4().fromColor(159, 224, 210, 255.f), Vector2 { context.width - 50.f, 0.f }, Vector2 { context.width - 50.f, static_cast<float>(context.height) });
-	segmentList[2].load(&renderer, Vector4().fromColor(248, 255, 156, 255.f), Vector2 { 50.f, 50.f }, Vector2 { context.width - 50.f, 150.f });
-	segmentList[3].load(&renderer, Vector4().fromColor(205, 178, 214, 255.f), Vector2 { 50.f, 150.f }, Vector2 { context.width - 50.f, 50.f });
-
-	mainLoop.run(update);
-	return true;
+    printf("Play clicked\n");
+    
+    load();
+    return true;
 }
 
 EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
-	printf("Stop clicked\n");
-	mainLoop.stop();
-	pill.unload();
-	renderer.unload();
-	return true;
+    printf("Stop clicked\n");
+    unload();
+    return true;
 }
\ No newline at end of file
diff --git a/frontend/shared_cpp/MainLoop.cpp b/frontend/shared_cpp/MainLoop.cpp
index 82a24b5..09aa643 100644
--- a/frontend/shared_cpp/MainLoop.cpp
+++ b/frontend/shared_cpp/MainLoop.cpp
@@ -26,5 +26,6 @@ EM_BOOL loop(double time, void* loop) {
         mainLoop->numFrames = 0;
     }
 
-    return mainLoop->updateFunc(deltaTimeSeconds, NULL);
+    mainLoop->updateFunc(deltaTimeSeconds, NULL);
+    return true;
 }
\ No newline at end of file
diff --git a/frontend/shared_cpp/MainLoop.h b/frontend/shared_cpp/MainLoop.h
index 7300f6b..2573bb8 100644
--- a/frontend/shared_cpp/MainLoop.h
+++ b/frontend/shared_cpp/MainLoop.h
@@ -11,9 +11,9 @@ struct MainLoop {
     bool isRunning = false;
 	double lastTime = 0, elapsedTime = 0;
 	int numFrames = 0;
-	EM_BOOL (*updateFunc)(float dtSeconds, void *userData);
+	void (*updateFunc)(float dtSeconds, void *userData);
 
-    void run(EM_BOOL (*cb)(float dtSeconds, void *userData)) {
+    void run(void (*cb)(float dtSeconds, void *userData)) {
 		isRunning = true;
 		lastTime = 0;
 		elapsedTime = 0;
diff --git a/frontend/shared_cpp/mathlib.h b/frontend/shared_cpp/mathlib.h
index 7595045..2784f6a 100644
--- a/frontend/shared_cpp/mathlib.h
+++ b/frontend/shared_cpp/mathlib.h
@@ -2,6 +2,11 @@
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
+#include <cmath>
+
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#define ABS(x) (x < 0 ? -x : x)
 
 struct Vector2 {
     float x = 0;
@@ -11,6 +16,12 @@ struct Vector2 {
         return { x + other.x, y + other.y };
     }
 
+    Vector2& operator+=(Vector2 other) {
+        x += other.x;
+        y += other.y;
+        return *this;
+    }
+
     Vector2 operator-(Vector2 other) {
         return { x - other.x, y - other.y };
     }
@@ -19,6 +30,10 @@ struct Vector2 {
         return { x * s, y * s };
     }
 
+    Vector2 operator/(float s) {
+        return { x / s, y / s };
+    }
+
     float dot(Vector2 other) {
         return x * other.x + y * other.y;
     }
@@ -45,6 +60,20 @@ struct Vector2 {
     void printDebug(const char* name) {
         printf("%s=Vector2(%f, %f)\n", name, x, y);
     }
+
+    float determinant(Vector2 other) {
+        //
+        // [ a b ]
+        // [ c d ]
+        //
+        // [ x other.x ]
+        // [ y other.y ]
+        //
+        // det = a * d - b * c
+        // det = x * other.y - other.x * y
+        //
+        return x * other.y - other.x * y;
+    }
 };
 
 struct Vector3 {
-- 
cgit v1.2.1