summaryrefslogtreecommitdiff
path: root/frontend/2d/_collisions/pill_line/main.cpp
diff options
context:
space:
mode:
authorMatthew Kosarek <mattkae@protonmail.com>2021-04-01 17:02:44 -0400
committerMatthew Kosarek <mattkae@protonmail.com>2021-04-01 17:02:44 -0400
commitdbd32f11e2a3df38162c70f946b5bfa9a8dedbfa (patch)
treedf3c1baf5818a641f7c15acbc59d90abcf0360c5 /frontend/2d/_collisions/pill_line/main.cpp
parentb17c518155fae64083eb5b56b78b9eec57603ac0 (diff)
Lots of work in progress on pill intersections, but will have to read more up on it
Diffstat (limited to 'frontend/2d/_collisions/pill_line/main.cpp')
-rw-r--r--frontend/2d/_collisions/pill_line/main.cpp348
1 files changed, 241 insertions, 107 deletions
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