summaryrefslogtreecommitdiff
path: root/2d/_collisions/pill_line/#main.cpp#
diff options
context:
space:
mode:
authorMatthew Kosarek <mattkae@protonmail.com>2021-05-16 19:50:15 -0400
committerMatthew Kosarek <mattkae@protonmail.com>2021-05-16 19:50:15 -0400
commita00c0aab1eb5a7a55bef8ca08115bdd722ab5699 (patch)
tree45b5c4cc8c380d0630a8e0185af7229f26dc754a /2d/_collisions/pill_line/#main.cpp#
parent4941a1874b6ca9d142d94df70b2aec5e0b35b94e (diff)
Moved the frontend directory up so that it no longer exists
Diffstat (limited to '2d/_collisions/pill_line/#main.cpp#')
-rw-r--r--2d/_collisions/pill_line/#main.cpp#360
1 files changed, 360 insertions, 0 deletions
diff --git a/2d/_collisions/pill_line/#main.cpp# b/2d/_collisions/pill_line/#main.cpp#
new file mode 100644
index 0000000..a3ede40
--- /dev/null
+++ b/2d/_collisions/pill_line/#main.cpp#
@@ -0,0 +1,360 @@
+#include "../../../shared_cpp/OrthographicRenderer.h"
+#include "../../../shared_cpp/types.h"
+#include "../../../shared_cpp/WebglContext.h"
+#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;
+ float32 cofOfRestition = 1.f;
+ float32 momentOfInertia = 0.f;
+
+ void reset() {
+ force = { 0, 0 };
+ velocity = { 0, 0 };
+ rotationalVelocity = 0.f;
+ rotation = 0.f;
+ }
+
+ void applyForce(Vector2 f) {
+ force += f;
+ }
+
+ void applyGravity() {
+ force += Vector2 { 0.f, -100.f };
+ }
+
+ void update(float32 deltaTimeSeconds) {
+ Vector2 acceleration = force / mass;
+ velocity += (acceleration * deltaTimeSeconds);
+ position += (velocity * deltaTimeSeconds);
+ force = Vector2 { 0.f, 0.f };
+
+ rotation += (rotationalVelocity * deltaTimeSeconds);
+ }
+
+ void setMomentOfInertia(float32 moi) {
+ momentOfInertia = moi;
+ }
+};
+
+struct Pill {
+ OrthographicShape shape;
+ Rigidbody body;
+ float32 a = 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.
+ // 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();
+
+ // https://byjus.com/jee/moment-of-inertia-of-ellipse/
+ body.momentOfInertia = (body.mass * (a * a + b * b)) / 4.f;
+
+ 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;
+ Rigidbody body;
+ Vector2 start;
+ Vector2 end;
+ float32 length;
+ Vector2 normal;
+ OrthographicVertex vertices[2];
+
+ void load(OrthographicRenderer* renderer, Vector4 color, Vector2 inStart, Vector2 inEnd) {
+ start = inStart;
+ end = inEnd;
+ length = (start - end).length();
+ 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);
+
+ body.reset();
+ body.mass = 1000000000.f;
+ body.cofOfRestition = 1.f;
+ body.rotationalVelocity = 0;
+ body.velocity = Vector2();
+ body.momentOfInertia = body.mass * (length / 2.f);
+ }
+
+ void render(OrthographicRenderer* renderer) {
+ shape.render(renderer, GL_LINES);
+ }
+
+ void unload() {
+ shape.unload();
+ }
+
+ Vector2 getPointOnLine(float32 t) {
+ return {
+ start.x + (end.x - start.x) * t,
+ start.y + (end.y - start.y) * t,
+ };
+ }
+
+ Vector2 getNormal() {
+ return (end - start).getPerp().normalize();
+ }
+};
+
+struct IntersectionResult {
+ bool intersect = false;
+ Vector2 collisionNormal;
+ Vector2 relativeVelocity;
+ Vector2 firstPointOfApplication;
+ Vector2 secondPointOfApplication;
+};
+
+EM_BOOL onPlayClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
+EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData);
+
+void load();
+void update(float32 time, void* userData);
+void unload();
+IntersectionResult getIntersection(Pill* pill, LineSegment* segment);
+void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir);
+
+// Global Variables
+WebglContext context;
+OrthographicRenderer renderer;
+Pill pill;
+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;
+}
+
+void load() {
+ renderer.load(&context);
+
+ pill.body.position = Vector2 { context.width / 2.f, context.height / 2.f };
+ pill.load(&renderer, 64, 100.f, 50.f);
+ pill.body.rotationalVelocity = 0.3f;
+
+ 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;
+ Mat4x4 inverseModel = pill->shape.model.inverse();
+ Vector2 start = inverseModel * segment->start;
+ Vector2 end = inverseModel * segment->end;
+ Vector2 diff = end - start;
+ float32 A = (diff.x * diff.x) / (pill->a * pill->a) + (diff.y * diff.y) / (pill->b * pill->b);
+ float32 B = ((2 * start.x) * (end.x - start.x)) / (pill->a * pill->a) + ((2 * start.y) * (end.y - start.y)) / (pill->b * pill->b);
+ float32 C = (start.x * start.x) / (pill->a * pill->a) + (start.y * start.y) / (pill->b * pill->b) - 1.f;
+
+ float32 determinant = B * B - 4 * A * C;
+ if (determinant < 0.f) {
+ result.intersect = false;
+ return result;
+ }
+
+ float32 t1 = (-B + sqrtf(determinant)) / (2 * A);
+ float32 t2 = (-B - sqrtf(determinant)) / (2 * A);
+
+ Vector2 pointOnLine = segment->getPointOnLine(t1); // This point is in world space and line space, since the line is in world space
+ Vector2 pointOnEllipse = pointOnLine - pill->body.position; // This point is in ellipse space
+
+ //float32 parametricTEllipse = atan2f( pointOnEllipse.y / pill->b , pointOnEllipse.x / pill->a );
+ //Vector2 tangent = { -pill->a * sinf(parametricTEllipse), pill->b * cosf(parametricTEllipse) };
+ //Vector2 normal = tangent.getPerp();
+ // Or, simply: Vector2 normal = { pointOnEllipse.x * (pill->b / pill->a), pointOnEllipse.y * (pill->a / pill->b) };
+ Vector2 normal = segment->getNormal();
+
+ result.intersect = true;
+ result.relativeVelocity = pill->body.velocity - segment->body.velocity;;
+ result.collisionNormal = normal;
+ result.firstPointOfApplication = pointOnEllipse;
+ result.secondPointOfApplication = pointOnLine;
+
+ return result;
+}
+
+void resolveCollision(Rigidbody* first, Rigidbody* second, IntersectionResult* ir) {
+ Vector2 relativeVelocity = ir->relativeVelocity;
+ Vector2 collisionNormal = ir->collisionNormal;
+ Vector2 firstPerp = ir->firstPointOfApplication.getPerp();
+ Vector2 secondPerp = ir->secondPointOfApplication.getPerp();
+
+ float32 cofOfRestition = (first->cofOfRestition + second->cofOfRestition) / 2.f;
+ float32 lNumerator = (relativeVelocity * -(1.f + cofOfRestition)).dot(collisionNormal);
+ float32 lLinearDenomPart = collisionNormal.dot(collisionNormal * (1.f / first->mass + 1.f / second->mass));
+ float32 lRotationalDenomPart = powf(firstPerp.dot(collisionNormal), 2.f) / first->momentOfInertia + powf(secondPerp.dot(collisionNormal), 2) / second->momentOfInertia;
+
+ float32 lImpulseMagnitude = lNumerator / (lLinearDenomPart + lRotationalDenomPart);
+
+ first->velocity = first->velocity + (collisionNormal * (lImpulseMagnitude / first->mass));
+ second->velocity = second->velocity - (collisionNormal * (lImpulseMagnitude / second->mass));
+
+ printf("%f\n", firstPerp.dot(collisionNormal * lImpulseMagnitude) / first->momentOfInertia);
+
+ first->rotationalVelocity = first->rotationalVelocity + firstPerp.dot(collisionNormal * lImpulseMagnitude) / first->momentOfInertia;
+ second->rotationalVelocity = second->rotationalVelocity - secondPerp.dot(collisionNormal * lImpulseMagnitude) / second->momentOfInertia;
+}
+
+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++) {
+ 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
+ IntersectionResult subIr = ir;
+ float32 subdividedTimeSeconds = deltaTimeSeconds;
+ do {
+ ir = subIr;
+
+ pill = copyPill.copy();
+ subdividedTimeSeconds /= 2.f;
+ pill.update(subdividedTimeSeconds);
+
+ subIr = getIntersection(&pill, &segmentList[lineIdx]);
+ if (subdividedTimeSeconds == 0.f) {
+ printf("Error: Should not be happening.\n");
+ break;
+ }
+ } while (subIr.intersect);
+
+ printf("Found intersection at timestamp: %f\n", subdividedTimeSeconds);
+ resolveCollision(&pill.body, &segmentList[lineIdx].body, &ir);
+ pill.update(deltaTimeSeconds - subdividedTimeSeconds);
+ }
+ }
+
+ // Render
+ renderer.render();
+ pill.shape.render(&renderer);
+
+ for (int32 segmentIndex = 0; segmentIndex < 4; segmentIndex++) {
+ segmentList[segmentIndex].render(&renderer);
+ }
+}
+
+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");
+
+ load();
+ return true;
+}
+
+EM_BOOL onStopClicked(int eventType, const EmscriptenMouseEvent* mouseEvent, void* userData) {
+ printf("Stop clicked\n");
+ unload();
+ return true;
+}