summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Kosarek <mattkae@protonmail.com>2021-04-05 21:01:45 -0400
committerMatthew Kosarek <mattkae@protonmail.com>2021-04-05 21:01:45 -0400
commit756b9fdefc1a28ac46aa4b76676c7ffb57c9e11a (patch)
tree0bc1e4889a0bad1568430d4764e10b4412ad822a
parentdbd32f11e2a3df38162c70f946b5bfa9a8dedbfa (diff)
Properly detecting collisions with a pill as far as I can tell
-rw-r--r--frontend/2d/_collisions/pill_line.html8
-rw-r--r--frontend/2d/_collisions/pill_line/dist/output.js90
-rwxr-xr-xfrontend/2d/_collisions/pill_line/dist/output.wasmbin45699 -> 57400 bytes
-rw-r--r--frontend/2d/_collisions/pill_line/main.cpp105
-rw-r--r--frontend/index.css10
-rw-r--r--frontend/shared_cpp/mathlib.h37
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
index 2056ca5..3b6a673 100755
--- a/frontend/2d/_collisions/pill_line/dist/output.wasm
+++ b/frontend/2d/_collisions/pill_line/dist/output.wasm
Binary files differ
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++) {