diff options
author | Matthew Kosarek <mattkae@protonmail.com> | 2021-04-05 21:01:45 -0400 |
---|---|---|
committer | Matthew Kosarek <mattkae@protonmail.com> | 2021-04-05 21:01:45 -0400 |
commit | 756b9fdefc1a28ac46aa4b76676c7ffb57c9e11a (patch) | |
tree | 0bc1e4889a0bad1568430d4764e10b4412ad822a | |
parent | dbd32f11e2a3df38162c70f946b5bfa9a8dedbfa (diff) |
Properly detecting collisions with a pill as far as I can tell
-rw-r--r-- | frontend/2d/_collisions/pill_line.html | 8 | ||||
-rw-r--r-- | frontend/2d/_collisions/pill_line/dist/output.js | 90 | ||||
-rwxr-xr-x | frontend/2d/_collisions/pill_line/dist/output.wasm | bin | 45699 -> 57400 bytes | |||
-rw-r--r-- | frontend/2d/_collisions/pill_line/main.cpp | 105 | ||||
-rw-r--r-- | frontend/index.css | 10 | ||||
-rw-r--r-- | frontend/shared_cpp/mathlib.h | 37 |
6 files changed, 160 insertions, 90 deletions
diff --git a/frontend/2d/_collisions/pill_line.html b/frontend/2d/_collisions/pill_line.html index 2afa802..5152ba6 100644 --- a/frontend/2d/_collisions/pill_line.html +++ b/frontend/2d/_collisions/pill_line.html @@ -31,12 +31,14 @@ Stop </button> </div> - <p> - References + <footer id="references"> + <h2>References</h2> <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> + <li><a href="https://www.maa.org/external_archive/joma/Volume8/Kalman/TransformedEqn.html">Translated Ellipse Equation</a></li> + <li><a href="https://www.maa.org/external_archive/joma/Volume8/Kalman/General.html">General Ellipse Equation</a></li> </ul> - </p> + </footer> </article> </section> </main> diff --git a/frontend/2d/_collisions/pill_line/dist/output.js b/frontend/2d/_collisions/pill_line/dist/output.js index eb8b559..e1dfee3 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 in wasm memory 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 on the asm.js/wasm heap 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 a UTF-8 string in wasm memory! (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 an UTF-8 string on the asm.js/wasm heap! (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 +// Endianness check (note: assumes compiler arch was little-endian) (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! (Run with -s SUPPORT_BIG_ENDIAN=1 to bypass)'; + if (h8[0] !== 0x73 || h8[1] !== 0x63) throw 'Runtime error: expected the system to be little-endian!'; })(); function abortFnPtrError(ptr, sig) { @@ -1274,6 +1274,8 @@ 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']) { @@ -1290,7 +1292,6 @@ function initRuntime() { checkStackCookie(); assert(!runtimeInitialized); runtimeInitialized = true; - callRuntimeCallbacks(__ATINIT__); } @@ -1615,8 +1616,6 @@ 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) @@ -1638,8 +1637,7 @@ function createWasm() { function instantiateArrayBuffer(receiver) { return getBinaryPromise().then(function(binary) { - var result = WebAssembly.instantiate(binary, info); - return result; + return WebAssembly.instantiate(binary, info); }).then(receiver, function(reason) { err('failed to asynchronously prepare wasm: ' + reason); @@ -1706,8 +1704,12 @@ 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. @@ -1758,11 +1760,6 @@ 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'](); @@ -1786,6 +1783,10 @@ 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) @@ -1799,7 +1800,7 @@ var ASM_CONSTS = { // anyhow) } function _emscripten_resize_heap(requestedSize) { - var oldSize = HEAPU8.length; + var oldSize = _emscripten_get_heap_size(); // 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); @@ -1826,7 +1827,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 ); @@ -1844,7 +1845,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 = []; @@ -1858,13 +1859,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; @@ -1878,7 +1879,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; @@ -1890,14 +1891,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--); @@ -1928,7 +1929,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--); @@ -2084,7 +2085,7 @@ var ASM_CONSTS = { } return ret; },MAX_TEMP_BUFFER_SIZE:2097152,numTempVertexBuffersPerSize:64,log2ceilLookup:function(i) { - return 32 - Math.clz32(i === 0 ? 0 : i - 1); + return 32 - Math.clz32(i-1); },generateTempBuffers:function(quads, context) { var largestIndex = GL.log2ceilLookup(GL.MAX_TEMP_BUFFER_SIZE); context.tempVertexBufferCounters1 = []; @@ -2212,19 +2213,6 @@ 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) ? @@ -2398,7 +2386,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; } @@ -3016,10 +3004,6 @@ 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)") }; @@ -3043,12 +3027,7 @@ 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, "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, "abortStackOverflow")) Module["abortStackOverflow"] = function() { abort("'abortStackOverflow' 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)") }; @@ -3238,6 +3217,7 @@ function callMain(args) { return; } else if (e == 'unwind') { // running an evented main loop, don't immediately exit + noExitRuntime = true; return; } else { var toLog = e; @@ -3347,19 +3327,17 @@ 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 && keepRuntimeAlive() && status === 0) { + if (implicit && noExitRuntime && status === 0) { return; } - if (keepRuntimeAlive()) { + if (noExitRuntime) { // 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)'; @@ -3367,6 +3345,8 @@ 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 Binary files differindex 2056ca5..3b6a673 100755 --- a/frontend/2d/_collisions/pill_line/dist/output.wasm +++ b/frontend/2d/_collisions/pill_line/dist/output.wasm diff --git a/frontend/2d/_collisions/pill_line/main.cpp b/frontend/2d/_collisions/pill_line/main.cpp index 9123b50..5aa0059 100644 --- a/frontend/2d/_collisions/pill_line/main.cpp +++ b/frontend/2d/_collisions/pill_line/main.cpp @@ -29,7 +29,7 @@ struct Rigidbody { } void applyGravity() { - force += Vector2 { 0.f, -25.f }; + force += Vector2 { 0.f, -100.f }; } void update(float32 deltaTimeSeconds) { @@ -46,7 +46,16 @@ struct Pill { OrthographicShape shape; Rigidbody body; float32 a = 0; - float32 b = 0;; + float32 b = 0; + + Pill copy() { + Pill retval; + retval.shape = shape; + retval.body = body; + retval.a = a; + retval.b = b; + return retval; + } void load(OrthographicRenderer* renderer, float32 numSegments, float32 width, float32 height) { // Note that a so-called "pill" is simply an ellipse. @@ -114,15 +123,11 @@ struct LineSegment { 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; @@ -203,51 +208,87 @@ float32 areaOfTriangle(Vector2 a, Vector2 b, Vector2 c) { 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; + /*float32 rotationAngleOfPill = pill->body.rotation; + Vector2 translationOfPill = pill->body.position; - - // 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 { + float32 cosRotation = cosf(rotationAngleOfPill); + float32 sinRotation = sinf(rotationAngleOfPill); + float32 cosRotationSquared = cosRotation * cosRotation; + float32 sinRotationSquared = sinRotation * sinRotation; + float32 aSquared = pill->a * pill->a; + float32 bSquared = pill->b * pill->b; + + float32 x = (segment->end.x - segment->start.x) - translationOfPill.x; + float32 xSquared = x * x; + float32 y = (segment->end.y - segment->start.y) - translationOfPill.y; + float32 ySquared = y * y; + + float32 A_XPart = (segment->end.x - segment->start.x - translationOfPill.x) * cosRotationSquared; + float32 A_YPart = (segment->end.y - segment->start.y - translationOfPill.y) * sinRotationSquared; + float32 A = ((A_XPart * A_XPart) / aSquared) + ((A_YPart * A_YPart) / bSquared); + + float32 B_XPart = 2 * (segment->start.x - translationOfPill.x) * (segment->end.x - segment->start.x - translationOfPill.x); + float32 B = 2 * cosRotation * sinRotation * (1.f / aSquared - 1.f / bSquared) * (x * y); + float32 C = (sinRotationSquared / aSquared + cosRotationSquared / bSquared) * ySquared;*/ + + Mat4x4 inverseModel = pill->shape.model.inverse(); + Vector2 start = inverseModel * segment->start; + Vector2 end = inverseModel * segment->end; + float32 A = pow((end.x - start.x), 2.f) / pow(pill->a, 2) + pow((end.y - start.y), 2.f) / pow(pill->b, 2); + float32 B = ((2 * start.x) * (end.x - start.x)) / pow(pill->a, 2) + ((2 * start.y) * (end.y - start.y)) / pow(pill->b, 2); + float32 C = pow(start.x, 2) / pow(pill->a, 2) + pow(start.y, 2) / pow(pill->b, 2) - 1.f; + + float32 determinant = B * B - 4 * A * C; + if (determinant < 0.f) { result.intersect = false; return result; } - // Time to find where we intersected. We can do this by getting the intersection depth. + float32 t1 = -B + determinant / (2 * A); + float32 t2 = -B - determinant / (2 * A); - - 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 }); + Vector2 pointAtT1 = { segment->start.x + (t1 * (segment->end.x - segment->start.x)), segment->start.y + (t1 * (segment->end.y - segment->start.y)) }; + Vector2 pointAtT2 = { segment->start.x + (t2 * (segment->end.x - segment->start.x)), segment->start.y + (t2 * (segment->end.y - segment->start.y)) }; + + printf("Intersecting\n"); + result.intersect = true; + result.pointOfIntersection = pointAtT1 - pill->body.position;; + result.relativeVelocity = Vector2() - pill->body.velocity; + result.collisionNormal = (pointAtT1 - pill->body.position).normalize(); return result; } void update(float32 deltaTimeSeconds, void* userData) { + // Input pill.body.applyGravity(); // Update + Pill copyPill = pill.copy(); pill.update(deltaTimeSeconds); // Intersections for (int32 lineIdx = 0; lineIdx < 4; lineIdx++) { - getIntersection(&pill, &segmentList[lineIdx]); + IntersectionResult ir = getIntersection(&pill, &segmentList[lineIdx]); + if (ir.intersect) { + + // Find the exact moment that the intersection happens by rewinding the simulation until we're not intersecting + while (ir.intersect) { + pill = copyPill.copy(); + deltaTimeSeconds /= 2.f; + pill.update(deltaTimeSeconds); + + ir = getIntersection(&pill, &segmentList[lineIdx]); + + if (deltaTimeSeconds <= 0.f) { + printf("Error: Should not be happening.\n"); + break; + } + } + + printf("Found intersection at timestamp: %f\n", deltaTimeSeconds); + } } // Render diff --git a/frontend/index.css b/frontend/index.css index 03221de..dbe4128 100644 --- a/frontend/index.css +++ b/frontend/index.css @@ -280,6 +280,16 @@ article pre { display: inline-block; } +#references a, +#references a:visited { + color: #EDE68A; + opacity: 0.8; +} + +#references a:hover { + opacity: 1.0; +} + /* Code styles */ diff --git a/frontend/shared_cpp/mathlib.h b/frontend/shared_cpp/mathlib.h index 2784f6a..383c880 100644 --- a/frontend/shared_cpp/mathlib.h +++ b/frontend/shared_cpp/mathlib.h @@ -159,6 +159,10 @@ struct Mat4x4 { }; } + Vector2 operator*(Vector2 v) { + return multByVec2(v); + } + Mat4x4 getOrthographicMatrix(float left, float right, float bottom, float top) { Mat4x4 result; result.m[0] = 2.0 / (right - left); @@ -169,6 +173,39 @@ struct Mat4x4 { return result; } + Mat4x4 inverse() { + Mat4x4 inv; + + inv.m[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10]; + inv.m[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10]; + inv.m[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9]; + inv.m[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9]; + inv.m[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10]; + inv.m[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10]; + inv.m[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9]; + inv.m[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9]; + inv.m[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6]; + inv.m[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6]; + inv.m[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5]; + inv.m[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5]; + inv.m[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6]; + inv.m[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6]; + inv.m[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5]; + inv.m[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5]; + + float det = m[0] * inv.m[0] + m[1] * inv.m[4] + m[2] * inv.m[8] + m[3] * inv.m[12]; + + if (det == 0) + return Mat4x4(); + + det = 1.f / det; + + for (int i = 0; i < 16; i++) + inv.m[i] = inv.m[i] * det; + + return inv; + } + void print() { printf("[ "); for (int idx = 0; idx < 16; idx++) { |