From 4878f0fc6a039d220dd7adecb18d19c688ae50b0 Mon Sep 17 00:00:00 2001 From: Matthew Kosarek Date: Thu, 1 Jul 2021 19:46:08 -0400 Subject: (mkosarek) Decent SAT description for now --- 2d/_collisions/polygon_polygon.html | 177 +++++++++++-- 2d/_collisions/polygon_polygon.html.content | 61 +++-- 2d/_collisions/polygon_polygon/snippet2.cpp | 117 +++++++++ index.css | 13 +- index.html | 2 +- index.html.content | 2 +- roadmap.html | 2 +- roadmap.html.content | 2 +- run_transpiler.sh | 2 +- tools/transpiler/List.h | 222 ++++++++++++++++ tools/transpiler/Logger.cpp | 123 +++++++++ tools/transpiler/Logger.h | 43 +++ tools/transpiler/MathHelper.h | 33 +++ tools/transpiler/MyString.cpp | 392 ++++++++++++++++++++++++++++ tools/transpiler/MyString.h | 76 ++++++ tools/transpiler/build.sh | 1 + tools/transpiler/pages.txt | 16 ++ tools/transpiler/replacer.cpp | 128 +++++++++ tools/transpiler/replacer.h | 3 + tools/transpiler/transpiler | Bin 0 -> 52160 bytes tools/transpiler/transpiler.cpp | 255 ++++++++++++++++++ tools/zipper/zipper.cpp | 6 + transpiler/List.h | 222 ---------------- transpiler/Logger.cpp | 123 --------- transpiler/Logger.h | 43 --- transpiler/MathHelper.h | 33 --- transpiler/MyString.cpp | 392 ---------------------------- transpiler/MyString.h | 76 ------ transpiler/build.sh | 1 - transpiler/pages.txt | 16 -- transpiler/replacer.cpp | 128 --------- transpiler/replacer.h | 3 - transpiler/transpiler | Bin 52160 -> 0 bytes transpiler/transpiler.cpp | 255 ------------------ 34 files changed, 1630 insertions(+), 1338 deletions(-) create mode 100644 2d/_collisions/polygon_polygon/snippet2.cpp create mode 100644 tools/transpiler/List.h create mode 100644 tools/transpiler/Logger.cpp create mode 100644 tools/transpiler/Logger.h create mode 100644 tools/transpiler/MathHelper.h create mode 100644 tools/transpiler/MyString.cpp create mode 100644 tools/transpiler/MyString.h create mode 100755 tools/transpiler/build.sh create mode 100644 tools/transpiler/pages.txt create mode 100644 tools/transpiler/replacer.cpp create mode 100644 tools/transpiler/replacer.h create mode 100755 tools/transpiler/transpiler create mode 100644 tools/transpiler/transpiler.cpp create mode 100644 tools/zipper/zipper.cpp delete mode 100644 transpiler/List.h delete mode 100644 transpiler/Logger.cpp delete mode 100644 transpiler/Logger.h delete mode 100644 transpiler/MathHelper.h delete mode 100644 transpiler/MyString.cpp delete mode 100644 transpiler/MyString.h delete mode 100755 transpiler/build.sh delete mode 100644 transpiler/pages.txt delete mode 100644 transpiler/replacer.cpp delete mode 100644 transpiler/replacer.h delete mode 100755 transpiler/transpiler delete mode 100644 transpiler/transpiler.cpp diff --git a/2d/_collisions/polygon_polygon.html b/2d/_collisions/polygon_polygon.html index f401385..69938ff 100644 --- a/2d/_collisions/polygon_polygon.html +++ b/2d/_collisions/polygon_polygon.html @@ -72,9 +72,21 @@

The Separating Axis Theorem (SAT) provides a way to find the intersection between any n-sided convex polygon or circle. In this tutorial, I will explain how this theorem works, and how you can use it to both detect and resolve collisions in your simulation.

+ +
-

Explanation of Separating Axis Theorem

+

Explanation of Separating Axis Theorem

SAT makes use of vector projection to figure out whether or not two concave polygons are intersecting. I believe the best way to describe it is to show you the instance where two polygons do not intersect. @@ -147,7 +159,7 @@

-

Algorithm for Finding the Intersection

+

Algorithm for Finding the Intersection

Given two polygons A and B: @@ -215,7 +227,7 @@

-

SAT Collision Resolution

+

SAT Collision Resolution

Now that we know that our objects have intersected, we want to send them tumbling away from one another in order to simulate a collision. To do this, we will need to find the following things:

- To find these values, we must first find both the shape and the edge that caused the intersection in the first place. To do this, we will think about what we know so far and try to arrive at some intutitive understanding of it. Keep in mind that I am not a proof-minded person, so you will not be finding that here. -
- -

Finding the Intersecting Edge

+

Finding the Intersecting Edge

We can already figure out that the following two triangles intersect one another:

@@ -238,7 +247,9 @@

- We know that A can only intersect B if: (1) a vertex from A is inside of B, (2) an edge of A flatly intersects an edge of B, or (3) a vertex of A overlaps exactly a vertex of B. Honestly, for our purposes, scenarios 2 and 3 are quite unlikely, but we can explore them a bit just to see how we might resolve them. We will start with the first case, since it is more likely. We will start by drawing the axis defined by the leftmost edge of B with both polygons projected onto it: + We know that A can only intersect B if: (1) a vertex from A is inside of B, (2) an edge of A flatly intersects an edge of B, or (3) a vertex of A overlaps exactly a vertex of B. For our purposes, scenario 3 is quite unlikely, so we can ignore it for now. We will start with the first case, since it is the most likely. +

+ The following drawing shows both of our objects projected onto the axis defined by object B's leftmost edge:

@@ -247,22 +258,150 @@

- This is a poorly drawn picture, but you should be able to see that the bit in green represents the intersection between the projections of the two polygons. If we were to repeat this same exercise for every edge here, we'll begin to see something very interesting. And, if we take new shapes and continue this stategy, we can begin to come to a very elegant conclusion: -
-
- - The intersecting edge will be the one where the projection of triangle A overlaps with the projection of triangle B the least! - -

- I'm sure someone more inclined to proving mathematical truities would love to describe this to you, but, for all intents and purposes, this intutitive understanding is good enough for us. We just want to make games, anyhow. -
-

Finding which point causes the intersection

+ If you were to repeat this exercise of projecting the shapes onto every axis of both shapes, you will begin to unconver an interesting truth: The intersecting edge will be the one where the projection of triangle A overlaps with the projection of triangle B the least! This should be unsurprising if you consider it. If our simulation is running quickly enough, the point of object A that is penetrating the edge of object B will just barely be inside of B. At the same time, it should be the only point that is currently inside of B. Hence, the overlap of the projection on that axis will be from that edge of B to that point on A. Knowing that this edge caused the intersection, we now have the collision normal. +
+

Finding which point causes the intersection

+ This part is made easy by the fact that we know which edge of object B caused the intersection in the first place. We know, then, that the intersecting point on object A must be the point that is currently inside of B. Simply put, this point will be the vertex of A that was projected onto that axis that is closest to the edge of B. This will be true so long as the simulation is properly fast and the object is not infinitesmley small (in which case, we're dealing with particle physics, and you shouldn't be using this tutorial).

-

+

Algorithm for Collision Resolution

+ +

+ + Given two polygons A and B: +

    +
  1. Find the intersecton as before
  2. +
  3. While finding the intersection, keep track of the axis that the projection on object A overlapped with the corresponding projection on object B the least. Remember the edge that this that held this axis.
  4. +
  5. Using this axis, find out which point on the other object inside of the edge by comparing the distances from the two points on the edge to the two points projected onto the axis created by that edge.
  6. +
  7. Use the calcualted edge to find the normal and the overlap point to find the point of application.
  8. +
+ + And that is it! Here is the code for this algorithm: + +
struct IntersectionResult {
+    bool intersect = false;
+    Vector2 collisionNormal;
+    Vector2 relativeVelocity;
+    Vector2 firstPointOfApplication;
+    Vector2 secondPointOfApplication;
+};
+
+struct Edge {
+	Vector2 normal;
+	Vector2 start;
+	Vector2 end;
+};
+
+struct SATResult {
+    Edge* minOverlapEdge = NULL;        // Edge that caused the intersection (on the 'first' shape)
+    Vector2 overlapPoint;               // Point that caused the intersection (on the 'second' shape)
+    float32 minOverlap = FLT_MAX;       // Smallest projection overlap
+};
+
+struct ProjectionResult {
+    Vector2 minVertex;
+    Vector2 maxVertex;
+    Vector2 projection;
+};
+
+// Given the vertices in the shape, we find the min and max vertices that project
+// onto the axis. We also save which vertices were which so that we can resolve
+// the collision later.
+ProjectionResult getProjection(Vector2* vertices, int numVertices, Vector2 axis) {
+    ProjectionResult pr;
+    float32 min = axis.dot(vertices[0]);
+    float32 max = min;
+
+    for (int v = 1; v < numVertices; v++) {
+        float32 d = axis.dot(vertices[v]);
+
+        if (d < min) {
+            pr.minVertex = vertices[v];
+            min = d;
+        } else if (d > max) {
+            pr.maxVertex = vertices[v];
+            max = d;
+        }
+    }
+    
+    pr.projection = Vector2 { min, max };
+    return pr;
+}
+
+bool projectionsOverlap(Vector2 first, Vector2 second) {
+    return first.x <= second.y && second.x <= first.y;
+}
+
+float32 getProjectionOverlap(Vector2 first, Vector2 second) {
+    float32 e = MIN(first.y, second.y);
+    float32 f = MAX(first.x, second.x);
+    return e - f;
+}
+
+bool runSatForShapesEdges(SATResult* result, ConvexPolygon* first, ConvexPolygon* second) {
+    for (int i = 0; i < first->numVertices; i++) {
+        Vector2 normal = first->edges[i].normal;
+
+        ProjectionResult firstProj = getProjection(first->transformedVertices, first->numVertices, normal);
+        ProjectionResult secondProj = getProjection(second->transformedVertices, second->numVertices, normal);
+
+        if (!projectionsOverlap(firstProj.projection, secondProj.projection)) {
+            return false;
+        }
+
+        float32 overlap = getProjectionOverlap(firstProj.projection, secondProj.projection);
+        if (overlap < result->minOverlap) {
+            result->minOverlap = overlap;
+            result->minOverlapEdge = &first->edges[i];
+            
+            // The overlapPoint will be the point on the other shape that penetrated the edge.
+            // If we caught the intersection reasonably early, it should be the point on 'second'
+            // that is nearest to the points on 'first'.
+            float32 min1min2 = (firstProj.minVertex - secondProj.minVertex).length();
+            float32 min1max2 = (firstProj.minVertex - secondProj.maxVertex).length();
+            float32 max1max2 = (firstProj.maxVertex - secondProj.maxVertex).length();
+            float32 max1min2 = (firstProj.maxVertex - secondProj.minVertex).length();
+            
+            float32 closest = MIN(min1min2, MIN(min1max2, MIN(max1max2, max1min2)));
+            if (closest == min1min2 || closest == max1min2) {
+                result->overlapPoint = secondProj.minVertex;
+            } else {
+                result->overlapPoint = secondProj.maxVertex;
+            }
+        }
+    }
+
+    return true;
+}
+
+const float32 EPSILON = 1.f;
+IntersectionResult getIntersection(ConvexPolygon* first, ConvexPolygon* second) {
+    IntersectionResult ir;
+    SATResult sat;
+    
+    if (!runSatForShapesEdges(&sat, first, second)) {
+        return ir;
+    }
+
+    if (!runSatForShapesEdges(&sat, second, first)) {
+        return ir;
+    }
+
+	ir.intersect = true;
+	ir.relativeVelocity = first->body.velocity - second->body.velocity;
+	ir.collisionNormal = sat.minOverlapEdge->normal;
+    ir.firstPointOfApplication = sat.overlapPoint - first->body.position;
+    ir.secondPointOfApplication = sat.overlapPoint - second->body.position;;
+
+	return ir;
+}
+

+
+
+

Live Example of Intersection Detection

diff --git a/2d/_collisions/polygon_polygon.html.content b/2d/_collisions/polygon_polygon.html.content index 4f99d71..f2a9253 100644 --- a/2d/_collisions/polygon_polygon.html.content +++ b/2d/_collisions/polygon_polygon.html.content @@ -20,9 +20,21 @@

The Separating Axis Theorem (SAT) provides a way to find the intersection between any n-sided convex polygon or circle. In this tutorial, I will explain how this theorem works, and how you can use it to both detect and resolve collisions in your simulation.

+ +
-

Explanation of Separating Axis Theorem

+

Explanation of Separating Axis Theorem

SAT makes use of vector projection to figure out whether or not two concave polygons are intersecting. I believe the best way to describe it is to show you the instance where two polygons do not intersect. @@ -95,7 +107,7 @@

-

Algorithm for Finding the Intersection

+

Algorithm for Finding the Intersection

Given two polygons A and B: @@ -113,7 +125,7 @@

-

SAT Collision Resolution

+

SAT Collision Resolution

Now that we know that our objects have intersected, we want to send them tumbling away from one another in order to simulate a collision. To do this, we will need to find the following things:

- To find these values, we must first find both the shape and the edge that caused the intersection in the first place. To do this, we will think about what we know so far and try to arrive at some intutitive understanding of it. Keep in mind that I am not a proof-minded person, so you will not be finding that here. -
- -

Finding the Intersecting Edge

+

Finding the Intersecting Edge

We can already figure out that the following two triangles intersect one another:

@@ -136,7 +145,9 @@

- We know that A can only intersect B if: (1) a vertex from A is inside of B, (2) an edge of A flatly intersects an edge of B, or (3) a vertex of A overlaps exactly a vertex of B. Honestly, for our purposes, scenarios 2 and 3 are quite unlikely, but we can explore them a bit just to see how we might resolve them. We will start with the first case, since it is more likely. We will start by drawing the axis defined by the leftmost edge of B with both polygons projected onto it: + We know that A can only intersect B if: (1) a vertex from A is inside of B, (2) an edge of A flatly intersects an edge of B, or (3) a vertex of A overlaps exactly a vertex of B. For our purposes, scenario 3 is quite unlikely, so we can ignore it for now. We will start with the first case, since it is the most likely. +

+ The following drawing shows both of our objects projected onto the axis defined by object B's leftmost edge:

@@ -145,22 +156,34 @@

- This is a poorly drawn picture, but you should be able to see that the bit in green represents the intersection between the projections of the two polygons. If we were to repeat this same exercise for every edge here, we'll begin to see something very interesting. And, if we take new shapes and continue this stategy, we can begin to come to a very elegant conclusion: -
-
- - The intersecting edge will be the one where the projection of triangle A overlaps with the projection of triangle B the least! - -

- I'm sure someone more inclined to proving mathematical truities would love to describe this to you, but, for all intents and purposes, this intutitive understanding is good enough for us. We just want to make games, anyhow. -
-

Finding which point causes the intersection

+ If you were to repeat this exercise of projecting the shapes onto every axis of both shapes, you will begin to unconver an interesting truth: The intersecting edge will be the one where the projection of triangle A overlaps with the projection of triangle B the least! This should be unsurprising if you consider it. If our simulation is running quickly enough, the point of object A that is penetrating the edge of object B will just barely be inside of B. At the same time, it should be the only point that is currently inside of B. Hence, the overlap of the projection on that axis will be from that edge of B to that point on A. Knowing that this edge caused the intersection, we now have the collision normal. +
+

Finding which point causes the intersection

+ This part is made easy by the fact that we know which edge of object B caused the intersection in the first place. We know, then, that the intersecting point on object A must be the point that is currently inside of B. Simply put, this point will be the vertex of A that was projected onto that axis that is closest to the edge of B. This will be true so long as the simulation is properly fast and the object is not infinitesmley small (in which case, we're dealing with particle physics, and you shouldn't be using this tutorial). +

+
+
+

Algorithm for Collision Resolution

+ +

+ + Given two polygons A and B: +

    +
  1. Find the intersecton as before
  2. +
  3. While finding the intersection, keep track of the axis that the projection on object A overlapped with the corresponding projection on object B the least. Remember the edge that this that held this axis.
  4. +
  5. Using this axis, find out which point on the other object inside of the edge by comparing the distances from the two points on the edge to the two points projected onto the axis created by that edge.
  6. +
  7. Use the calcualted edge to find the normal and the overlap point to find the point of application.
  8. +
+ + And that is it! Here is the code for this algorithm: + + #SNIPPET polygon_polygon/snippet2.cpp

-

+

Live Example of Intersection Detection

diff --git a/2d/_collisions/polygon_polygon/snippet2.cpp b/2d/_collisions/polygon_polygon/snippet2.cpp new file mode 100644 index 0000000..8cbdcbe --- /dev/null +++ b/2d/_collisions/polygon_polygon/snippet2.cpp @@ -0,0 +1,117 @@ +struct IntersectionResult { + bool intersect = false; + Vector2 collisionNormal; + Vector2 relativeVelocity; + Vector2 firstPointOfApplication; + Vector2 secondPointOfApplication; +}; + +struct Edge { + Vector2 normal; + Vector2 start; + Vector2 end; +}; + +struct SATResult { + Edge* minOverlapEdge = NULL; // Edge that caused the intersection (on the 'first' shape) + Vector2 overlapPoint; // Point that caused the intersection (on the 'second' shape) + float32 minOverlap = FLT_MAX; // Smallest projection overlap +}; + +struct ProjectionResult { + Vector2 minVertex; + Vector2 maxVertex; + Vector2 projection; +}; + +// Given the vertices in the shape, we find the min and max vertices that project +// onto the axis. We also save which vertices were which so that we can resolve +// the collision later. +ProjectionResult getProjection(Vector2* vertices, int numVertices, Vector2 axis) { + ProjectionResult pr; + float32 min = axis.dot(vertices[0]); + float32 max = min; + + for (int v = 1; v < numVertices; v++) { + float32 d = axis.dot(vertices[v]); + + if (d < min) { + pr.minVertex = vertices[v]; + min = d; + } else if (d > max) { + pr.maxVertex = vertices[v]; + max = d; + } + } + + pr.projection = Vector2 { min, max }; + return pr; +} + +bool projectionsOverlap(Vector2 first, Vector2 second) { + return first.x <= second.y && second.x <= first.y; +} + +float32 getProjectionOverlap(Vector2 first, Vector2 second) { + float32 e = MIN(first.y, second.y); + float32 f = MAX(first.x, second.x); + return e - f; +} + +bool runSatForShapesEdges(SATResult* result, ConvexPolygon* first, ConvexPolygon* second) { + for (int i = 0; i < first->numVertices; i++) { + Vector2 normal = first->edges[i].normal; + + ProjectionResult firstProj = getProjection(first->transformedVertices, first->numVertices, normal); + ProjectionResult secondProj = getProjection(second->transformedVertices, second->numVertices, normal); + + if (!projectionsOverlap(firstProj.projection, secondProj.projection)) { + return false; + } + + float32 overlap = getProjectionOverlap(firstProj.projection, secondProj.projection); + if (overlap < result->minOverlap) { + result->minOverlap = overlap; + result->minOverlapEdge = &first->edges[i]; + + // The overlapPoint will be the point on the other shape that penetrated the edge. + // If we caught the intersection reasonably early, it should be the point on 'second' + // that is nearest to the points on 'first'. + float32 min1min2 = (firstProj.minVertex - secondProj.minVertex).length(); + float32 min1max2 = (firstProj.minVertex - secondProj.maxVertex).length(); + float32 max1max2 = (firstProj.maxVertex - secondProj.maxVertex).length(); + float32 max1min2 = (firstProj.maxVertex - secondProj.minVertex).length(); + + float32 closest = MIN(min1min2, MIN(min1max2, MIN(max1max2, max1min2))); + if (closest == min1min2 || closest == max1min2) { + result->overlapPoint = secondProj.minVertex; + } else { + result->overlapPoint = secondProj.maxVertex; + } + } + } + + return true; +} + +const float32 EPSILON = 1.f; +IntersectionResult getIntersection(ConvexPolygon* first, ConvexPolygon* second) { + IntersectionResult ir; + SATResult sat; + + if (!runSatForShapesEdges(&sat, first, second)) { + return ir; + } + + if (!runSatForShapesEdges(&sat, second, first)) { + return ir; + } + + ir.intersect = true; + ir.relativeVelocity = first->body.velocity - second->body.velocity; + ir.collisionNormal = sat.minOverlapEdge->normal; + ir.firstPointOfApplication = sat.overlapPoint - first->body.position; + ir.secondPointOfApplication = sat.overlapPoint - second->body.position;; + + return ir; +} diff --git a/index.css b/index.css index 83f2e0c..1154475 100644 --- a/index.css +++ b/index.css @@ -2,7 +2,7 @@ Top level styling ************************************/ html { - background: #eee; + background: white; height: 100%; overflow: auto; } @@ -154,8 +154,9 @@ article { article > h1 { width: 100%; - padding-bottom: 0.25rem; font-weight: 500; + color: #100c08; + font-size: 24px; } section > p { @@ -163,11 +164,17 @@ section > p { } section > h2 { - color: #0f3460; + color: #2a52be; font-size: 20px; font-weight: 500; } +section > h3 { + color: #003153; + font-weight: 500; + font-size: 18px; +} + #references a { color: inherit; text-decoration: none; diff --git a/index.html b/index.html index e523685..36df461 100644 --- a/index.html +++ b/index.html @@ -53,7 +53,7 @@

Welcome to physicsforgames.com!

-

🚧This Website is Under Construction

+

🚧 This Website is Under Construction

Feel free to look around at the existing pages! But please be aware that things will be broken/incomplete in many instances. The website itself should have some solid content by the end of the year.

diff --git a/index.html.content b/index.html.content index fec2ea9..d7c93b6 100644 --- a/index.html.content +++ b/index.html.content @@ -1,7 +1,7 @@

Welcome to physicsforgames.com!

-

🚧This Website is Under Construction

+

🚧 This Website is Under Construction

Feel free to look around at the existing pages! But please be aware that things will be broken/incomplete in many instances. The website itself should have some solid content by the end of the year.

diff --git a/roadmap.html b/roadmap.html index f4c7113..9fa574f 100644 --- a/roadmap.html +++ b/roadmap.html @@ -105,7 +105,7 @@
-

July 2021: 3D headway (Planning)

+

July 2021: 2D loose ends and 3D headway

  • Finish SAT Collision Info (very close)
  • 2D rigidbody explanation 3
  • diff --git a/roadmap.html.content b/roadmap.html.content index 460828f..461cd88 100644 --- a/roadmap.html.content +++ b/roadmap.html.content @@ -53,7 +53,7 @@
    -

    July 2021: 3D headway (Planning)

    +

    July 2021: 2D loose ends and 3D headway

    • Finish SAT Collision Info (very close)
    • 2D rigidbody explanation 3
    • diff --git a/run_transpiler.sh b/run_transpiler.sh index 4d51ba6..0275633 100755 --- a/run_transpiler.sh +++ b/run_transpiler.sh @@ -1 +1 @@ -cd transpiler && ./build.sh && cd .. && ./transpiler/transpiler \ No newline at end of file +cd tools/transpiler && ./build.sh && cd ../.. && ./tools/transpiler/transpiler diff --git a/tools/transpiler/List.h b/tools/transpiler/List.h new file mode 100644 index 0000000..00a466a --- /dev/null +++ b/tools/transpiler/List.h @@ -0,0 +1,222 @@ +#pragma once +#include +#include +#include "Logger.h" + +#define FOREACH(list) \ + for (size_t idx = 0; idx < list.numElements; idx++) \ + if (auto value = list.getValue(idx)) \ + + +template +struct List { + T* data = nullptr; + size_t capacity = 0; + size_t numElements = 0; + bool growDynamically = true; + + void allocate(size_t size); + void add(T* element); + void add(T& element); + void add(T&& element); + bool grow(size_t newSize); + void set(T* value, size_t index); + void remove(size_t index); + void clear(); + void deallocate(); + bool isEmpty() { + return data == nullptr || numElements == 0; + } + T* getValue(int index) const; + T& operator[](int idx) const; + void binarySort(int (*f)(T *first, T* second)); + void setFromArray(T* arry, int size) { + allocate(size); + memcpy(data, arry, size * sizeof(T)); + numElements = size; + } + void remove(int index) { + if (index >= numElements) { + logger_error("Cannot remove element at index: %d", index); + return; + } + + + if (index == numElements - 1) { + numElements--; + return; + } + + memmove(data[index], data[index + 1], sizeof(T) * (numElements - index)); + numElements--; + } +}; + +template +void List::allocate(size_t size) { + if (size == 0 || size == capacity) { + numElements = 0; + return; + } + + if (data != nullptr) { + deallocate(); + } + + data = static_cast(malloc(sizeof(T) * size)); + capacity = size; + numElements = 0; +} + +template +bool List::grow(size_t newSize) { + if (!growDynamically) { + return false; + } + + if (newSize == 0) { + return false; + } + + T* newData = static_cast(malloc(sizeof(T) * newSize)); + + if (data != nullptr) { + memcpy(newData, data, numElements * sizeof(T)); + delete data; + } + + data = newData; + capacity = newSize; + return true; +} + +template +void List::set(T* value, size_t index) { + if (index >= capacity && !grow(index * 2)) { + return; + } + + memcpy(&data[index], value, sizeof(T)); +} + +template +void List::add(T* element) { + if (data == nullptr) { + allocate(2); + } + + if (element == nullptr) { + logger_error("Element not defined"); + return; + } + + size_t newNumElements = numElements + 1; + if (newNumElements > capacity) { + if (!grow(2 * capacity)) { + logger_error("Trying to add to list but unable to grow the array"); + return; + } + } + + memcpy(&data[numElements], element, sizeof(T)); + numElements = newNumElements; +} + +template +void List::add(T& element) { + if (data == nullptr) { + allocate(2); + } + + size_t newNumElements = numElements + 1; + if (newNumElements > capacity) { + if (!grow(2 * capacity)) { + logger_error("Trying to add to list but unable to grow the array"); + return; + } + } + + memcpy(&data[numElements], &element, sizeof(T)); + numElements = newNumElements; +} + +template +void List::add(T&& element) { + if (data == nullptr) { + allocate(2); + } + + size_t newNumElements = numElements + 1; + if (newNumElements > capacity) { + if (!grow(2 * capacity)) { + logger_error("Trying to add to list but unable to grow the array"); + return; + } + } + + memcpy(&data[numElements], &element, sizeof(T)); + numElements = newNumElements; +} + +template +void List::remove(size_t idx) { + if (idx >= numElements) { + logger_error("Index is outside of the list: %d >= %d", idx, numElements); + return; + } + + for (; idx < numElements - 1; idx++) { + data[idx] = data[idx + 1]; + } + + numElements--; +} + +template +void List::deallocate() { + if (data != nullptr) { + free(data); + data = nullptr; + } + + capacity = 0; + numElements = 0; +} + +template +void List::clear() { + numElements = 0; +} + +template +T* List::getValue(int idx) const { + return &data[idx]; +} + +template +T& List::operator[](int idx) const { + return data[idx]; +} + +template +void List::binarySort(int (*f)(T *first, T* second)) { + if (data == nullptr) { + return; + } + + for (size_t idx = 0; idx < numElements - 1; idx++) { + int minIdx = idx; + T firstValue = data[idx]; + + for (int innerIdx = idx + 1; innerIdx < numElements; innerIdx++) {\ + T secondValue = data[innerIdx]; + if (f(&firstValue, &secondValue) > 0) { + minIdx= innerIdx; + } + } + + T temp = data[minIdx]; + memmove(&data[minIdx], &data[idx], sizeof(T)); + memmove(&data[idx], &temp, sizeof(T)); + } +} diff --git a/tools/transpiler/Logger.cpp b/tools/transpiler/Logger.cpp new file mode 100644 index 0000000..1068d88 --- /dev/null +++ b/tools/transpiler/Logger.cpp @@ -0,0 +1,123 @@ +#include "Logger.h" +#include +#include +#include + +namespace Logger { + LogLevel gLogLevel = LogLevel_Debug; + FILE* gFilePointer = NULL; + + void initialize(LoggerOptions options) { + setLevel(options.level); + if (options.logToFile) { +#ifdef WIN32 + fopen_s(&gFilePointer, options.filePath, "a"); +#else + gFilePointer = fopen(options.filePath, "a"); +#endif + } + } + + void setLevel(LogLevel level) { + gLogLevel = level; + } + + LogLevel getLevel() { + return gLogLevel; + } + + void printHeader(const char* levelStr, const char* fileName, int lineNumber) { + time_t t = time(0); + tm now; +#ifdef WIN32 + localtime_s(&now, &t); +#else + now = *localtime(&t); +#endif + + printf("%s:%d [%d-%d-%d %d:%d:%d] %s: ", fileName, lineNumber, (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); + if (gFilePointer != NULL) { + fprintf(gFilePointer, "[%d-%d-%d %d:%d:%d] %s: ", (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); + } + } + + void logInternal(const char* file, int lineNumber,LogLevel level, const char* format, va_list args) { + if (level < gLogLevel) { + return; + } + + + const char* levelStr; + switch (level) { + case LogLevel_Debug: + levelStr = "Debug"; + break; + case LogLevel_Info: + levelStr = "Info"; + break; + case LogLevel_Warn: + levelStr = "Warning"; + break; + case LogLevel_Error: + levelStr = "Error"; + break; + default: + levelStr = "Unknown"; + break; + } + + if (gFilePointer != NULL) { + va_list fileArgs; + va_copy(fileArgs, args); + vfprintf(gFilePointer, format, fileArgs); + fprintf(gFilePointer, "\n"); + } + + printHeader(levelStr, file, lineNumber); + + vprintf(format, args); + printf("\n"); + } + + void doLog(const char* file, int lineNumber,LogLevel level, const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, level, format, args); + va_end(args); + } + + void doDebug(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Debug, format, args); + va_end(args); + } + + void doInfo(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Info, format, args); + va_end(args); + } + + void doWarning(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Warn, format, args); + va_end(args); + } + + void doError(const char* file, int lineNumber,const char* format, ...) { + va_list args; + va_start(args, format); + logInternal(file, lineNumber, LogLevel_Error, format, args); + va_end(args); + } + + void free() { + if (gFilePointer) { + fclose(gFilePointer); + gFilePointer = NULL; + } + } +} diff --git a/tools/transpiler/Logger.h b/tools/transpiler/Logger.h new file mode 100644 index 0000000..7596b6f --- /dev/null +++ b/tools/transpiler/Logger.h @@ -0,0 +1,43 @@ +#ifndef LOGGER_H +#define LOGGER_H + +#include + +enum LogLevel { + LogLevel_Debug = 0, + LogLevel_Info = 1, + LogLevel_Warn = 2, + LogLevel_Error = 3 +}; + +struct LoggerOptions { + LogLevel level = LogLevel_Debug; + bool logToFile = false; + const char* filePath = "debug.log"; +}; + +namespace Logger { + void initialize(LoggerOptions options); + void setLevel(LogLevel level); + LogLevel getLevel(); + void doLog(const char* file, int lineNumber, LogLevel level, const char* format, ...); + void doDebug(const char* file, int lineNumber, const char* format, ...); + void doInfo(const char* file, int lineNumber, const char* format, ...); + void doWarning(const char* file, int lineNumber, const char* format, ...); + void doError(const char* file, int lineNumber, const char* format, ...); + void free(); +}; + +#if WIN32 +#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) +#else +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +#define logger_log(level, format, ...) Logger::doLog(__FILENAME__, __LINE__, level, format, ## __VA_ARGS__) +#define logger_debug(format, ...) Logger::doDebug(__FILENAME__, __LINE__, format, ## __VA_ARGS__) +#define logger_info(format, ...) Logger::doInfo(__FILENAME__, __LINE__, format, ## __VA_ARGS__) +#define logger_warning(format, ...) Logger::doWarning(__FILENAME__, __LINE__, format, ## __VA_ARGS__) +#define logger_error(format, ...) Logger::doError(__FILENAME__, __LINE__, format, ## __VA_ARGS__) + +#endif diff --git a/tools/transpiler/MathHelper.h b/tools/transpiler/MathHelper.h new file mode 100644 index 0000000..b9c9cb6 --- /dev/null +++ b/tools/transpiler/MathHelper.h @@ -0,0 +1,33 @@ +#pragma once + +#define PI 3.14159265358979323846f + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) +#define DTOR (PI / 180.0f) +#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0) + +namespace MathHelper { + inline float radiansToDegrees(float radians) { + return radians * 180.f / PI; + } + + inline float degreesToRadians(float degrees) { + return degrees * PI / 180.f; + } + + inline int nearestPowerOfTwo(int n) { + int v = n; + + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; // next power of 2 + + return v; + } +} diff --git a/tools/transpiler/MyString.cpp b/tools/transpiler/MyString.cpp new file mode 100644 index 0000000..1a63960 --- /dev/null +++ b/tools/transpiler/MyString.cpp @@ -0,0 +1,392 @@ +#include "MyString.h" +#include "MathHelper.h" +#include +#include +#include +#include +#include + +int StringBuffer::add(const char* str) { + int spaceNeeded = strlen(str); + int spaceAvailable = BUFFER_SIZE - pointer; + + int amountToCopy = 0; + if (spaceAvailable >= spaceNeeded) { + amountToCopy = spaceNeeded; + } else { + amountToCopy = spaceAvailable; + } + + memcpy(&buffer[pointer], str, sizeof(char) * amountToCopy); + pointer += amountToCopy; + buffer[pointer] = '\0'; + return amountToCopy; +} + +void StringBuffer::reset() { + buffer[0] = '\0'; + pointer = 0; +} + +bool StringBuffer::isFull() { + return pointer == BUFFER_SIZE; +} + +StringBuffer* StringBuilder::getCurrentBuffer() { + if (bufferPointer == 0) { + if (!defaultBuffer.isFull()) { + return &defaultBuffer; + } + + StringBuffer newBuffer; + dynamicBuffer.allocate(2); + bufferPointer++; + dynamicBuffer.add(&newBuffer); + } + + if (dynamicBuffer[bufferPointer - 1].isFull()) { + StringBuffer newBuffer; + dynamicBuffer.add(&newBuffer); + bufferPointer++; + } + + return &dynamicBuffer[bufferPointer - 1]; +} + +StringBuffer* StringBuilder::getBufferAtIdx(int index) { + if (index == 0) { + return &defaultBuffer; + } + + index = index - 1; + if (index < static_cast(dynamicBuffer.numElements)) { + return &dynamicBuffer[index]; + } + + return nullptr; +} + +const StringBuffer* StringBuilder::getBufferAtIdxConst(int index) const { + if (index == 0) { + return &defaultBuffer; + } + + index = index - 1; + if (index < static_cast(dynamicBuffer.numElements)) { + return &dynamicBuffer[index]; + } + + return nullptr; +} + +String StringBuilder::toString() { + int strSize = (bufferPointer + 1) * StringBuffer::BUFFER_SIZE; + String retval; + + if (strSize > String::SSO_SIZE) { + retval.dynamicBuffer = new char[strSize + 1]; + memcpy(retval.dynamicBuffer, defaultBuffer.buffer, sizeof(char) * StringBuffer::BUFFER_SIZE); + FOREACH(dynamicBuffer) { + memcpy(&retval.dynamicBuffer[(idx + 1) * StringBuffer::BUFFER_SIZE], value->buffer, sizeof(char) * StringBuffer::BUFFER_SIZE); + } + retval.isSSO = false; + } else { + memcpy(retval.defaultBuffer, defaultBuffer.buffer, sizeof(char) * strSize); + retval.isSSO = true; + } + + retval.capacity = strSize; + retval.length = length; + retval.getValue()[retval.length] = '\0'; + return retval; +} + +void StringBuilder::addStr(String* str) { + addStr(str->getValue()); +} + +void StringBuilder::addStr(const char* str, int inLength) { + int amountLeft = inLength < 0 ? strlen(str) : inLength; + length += amountLeft; + int ptr = 0; + + do { + StringBuffer* currentBuffer = getCurrentBuffer(); + int amtAdded = currentBuffer->add(&str[ptr]); + amountLeft = amountLeft - amtAdded; + ptr += amtAdded; + } while (amountLeft > 0); +} + +void StringBuilder::addChar(char c) { + char wrapper[2] = { c, '\0' }; + addStr(wrapper); +} + +const int DEFAULT_NUMBER_BUFFER_SIZE = 32; + +void StringBuilder::addInt(int value) { + char buffer[DEFAULT_NUMBER_BUFFER_SIZE]; + sprintf(buffer, "%d", value); + addStr(buffer); +} + +// Pulled from here: https://stackoverflow.com/questions/7228438/convert-double-float-to-string +inline int n_tu(int number, int count) { + int result = 1; + while(count-- > 0) + result *= number; + + return result; +} + +void StringBuilder::addFloat(float value) { + char buffer[DEFAULT_NUMBER_BUFFER_SIZE]; + sprintf(buffer, "%f", value); + addStr(buffer); +} + +void StringBuilder::replace(const char* strToReplace, const char* replaceStr) { + +} + +int StringBuilder::indexOf(const char* str) { + return -1; +} + +void StringBuilder::removeAt(int index, int count) { + if (index >= length) { + printf("Index is larger than the length"); + return; + } + + if (count <= 0) { + printf("Count was <= zero"); + return; + } + + int newLength = length - count; + if (newLength == 0) { + clear(); + return; + } + + // Easiest way to do this is to move the entire string to the left, starting at index by count + int currentIndex = index; + int copyIndex = index + count; + + while (currentIndex < newLength) { + int bufferPos = floor(static_cast(currentIndex) / static_cast(StringBuffer::BUFFER_SIZE)); + int pointerIdx = currentIndex % StringBuffer::BUFFER_SIZE; + StringBuffer* buffer = getBufferAtIdx(bufferPos); + + char newChar = '\0'; + int copyBufferPos = floor(static_cast(copyIndex) / static_cast(StringBuffer::BUFFER_SIZE)); + StringBuffer* copyBuffer = getBufferAtIdx(copyBufferPos); + if (copyBuffer != nullptr) { + int bufferPointerIdx = copyIndex % StringBuffer::BUFFER_SIZE; + if (bufferPointerIdx < copyBuffer->pointer) { + newChar = copyBuffer->buffer[bufferPointerIdx]; + } + } + + buffer->buffer[pointerIdx] = newChar; + + currentIndex++; + copyIndex++; + } + + // Brute force update the buffer that we're currently using + length = newLength; + int newNumBuffers = ceil(static_cast(length) / static_cast(StringBuffer::BUFFER_SIZE)); // This is how many buffers we should have + if (newNumBuffers <= 1) { + dynamicBuffer.numElements = 0; + bufferPointer = 0; + } else { + dynamicBuffer.numElements = newNumBuffers - 1; + bufferPointer = newNumBuffers - 1; + } + + StringBuffer* lastBuffer = getBufferAtIdx(bufferPointer); + if (lastBuffer->pointer != StringBuffer::BUFFER_SIZE) { + lastBuffer->pointer = length % StringBuffer::BUFFER_SIZE; + } + lastBuffer->buffer[lastBuffer->pointer] = '\0'; +} + +void StringBuilder::format(const char* str, ...) { + va_list args; + va_start(args, str); + + while (*str != '\0') { + if (*str == '%') { + str++; + switch (*str) { + case 'd': + addInt(va_arg(args, int)); + break; + case 'f': + addFloat(static_cast(va_arg(args, double))); + break; + case 's': + addStr(va_arg(args, char*), -1); + default: + break; + } + } else { + addChar(*str); + } + + str++; + } + + va_end(args); +} + +void StringBuilder::clear() { + bufferPointer = 0; + length = 0; + defaultBuffer.reset(); + + if (!dynamicBuffer.isEmpty()) { + FOREACH(dynamicBuffer) { + value->reset(); + } + } + + dynamicBuffer.clear(); +} + +void StringBuilder::free() { + dynamicBuffer.deallocate(); +} + +char StringBuilder::getCharAtIdx(int index) const { + int bufferPos = floor(static_cast(index) / static_cast(StringBuffer::BUFFER_SIZE)); + int pointerIdx = index % StringBuffer::BUFFER_SIZE; + const StringBuffer* buffer = getBufferAtIdxConst(bufferPos); + if (buffer != nullptr && pointerIdx < buffer->pointer) { + return buffer->buffer[pointerIdx]; + } + + return '\0'; +} + +void StringBuilder::insert(char c, int index) { + if (index >= length) { + addChar(c); + return; + } + + // Move all of the characters forward by one + char currentChar = c; + for (int cIdx = index; cIdx < length + 1; cIdx++) { + int bufferPos = floor(static_cast(cIdx) / static_cast(StringBuffer::BUFFER_SIZE)); + int pointerIdx = cIdx % StringBuffer::BUFFER_SIZE; + StringBuffer* buffer = getBufferAtIdx(bufferPos); + if (buffer == nullptr) { + buffer = getCurrentBuffer(); // Get a new buffer if this one is filled up + } + + char nextCurrent = buffer->buffer[pointerIdx]; + buffer->buffer[pointerIdx] = currentChar; + currentChar = nextCurrent; + } + + // Move the final buffer forward + StringBuffer* finalBuffer = getCurrentBuffer(); + finalBuffer->pointer++; + finalBuffer->buffer[finalBuffer->pointer] = '\0'; + length = length + 1; +} + +String::String() { +} + +String::String(const char* str) { + set(str); +} + +void String::operator =(const char* str) { + set(str); +} + +char* String::getValue() { + return isSSO ? defaultBuffer : dynamicBuffer; +} + +const char* String::getValueConst() const { + return isSSO ? defaultBuffer : dynamicBuffer; +} + +void String::set(const char* str) { + int inLen = strlen(str); + + if (inLen <= SSO_SIZE) { + isSSO = true; + memcpy(defaultBuffer, str, inLen); + defaultBuffer[inLen] = '\0'; + } else { + if (capacity >= inLen) { + memcpy(dynamicBuffer, str, inLen); + } else { + free(); + capacity = MathHelper::nearestPowerOfTwo(inLen + 1); + dynamicBuffer = new char[capacity]; + memcpy(dynamicBuffer, str, inLen); + } + isSSO = false; + dynamicBuffer[inLen] = '\0'; + } + + length = inLen; +} + +void String::free() { + capacity = 0; + length = 0; + isSSO = true; + if (dynamicBuffer != nullptr) { + delete dynamicBuffer; + } + + defaultBuffer[0] = '\0'; +} + +int String::toInteger() { + return atoi(getValue()); +} + +float String::toFloat() { + return static_cast(atof(getValue())); +} + +int String::indexOf(char c) { + char* buffer = getValue(); + int idx = 0; + while (buffer[idx] != '\0') { + if (buffer[idx] == c) { + return idx; + } + idx++; + } + + return -1; +} + +StringView String::substring(int start, int end) { + StringView retval; + if (start >= length) { + retval.error = true; + return retval; + } + + if (end >= length) { + end = length - 1; + } + + char* value = getValue(); + retval.value = &value[start]; + retval.length = end - start; + return retval; +} diff --git a/tools/transpiler/MyString.h b/tools/transpiler/MyString.h new file mode 100644 index 0000000..805f5de --- /dev/null +++ b/tools/transpiler/MyString.h @@ -0,0 +1,76 @@ +#pragma once +#include "List.h" + +struct StringView { + bool error = false; + char* value = nullptr; + size_t length = 0; +}; + +struct String { + const static int SSO_SIZE = 31; + + char defaultBuffer[String::SSO_SIZE + 1] = { '\0' }; + char* dynamicBuffer = nullptr; + + int length = 0; + int capacity = 0; + bool isSSO = true; + + String(); + String(const char* str); + char* getValue(); + const char* getValueConst() const; + void operator =(const char* str); + void set(const char* str); + void free(); + int toInteger(); + float toFloat(); + int indexOf(char c); + inline bool equals(const String& other) { return strcmp(getValueConst(), other.getValueConst()) == 0; }; + inline bool equalsCstr(const char* str) { return strcmp(getValueConst(), str) == 0; }; + StringView substring(int start, int end); +}; + +struct StringBuffer { + const static int BUFFER_SIZE = 31; + + int pointer = 0; + char buffer[StringBuffer::BUFFER_SIZE + 1]; // Leave space for trailing escape character + + /* + * Appends the string to the buffer + * @param str + * @returns number of characters copied + */ + int add(const char* str); + bool isFull(); + void reset(); +}; + +struct StringBuilder { + int bufferPointer = 0; + int length = 0; + + StringBuffer defaultBuffer; + List dynamicBuffer; + + StringBuffer* getCurrentBuffer(); + StringBuffer* getBufferAtIdx(int index); + const StringBuffer* getBufferAtIdxConst(int index) const; + void addStr(String* str); + void addStr(const char* str, int length = -1); + void addChar(char c); + void format(const char* str, ...); + void addInt(int value); + void addFloat(float value); + void replace(const char* strToReplace, const char* replaceStr); + void removeAt(int index, int count); + int indexOf(char c); + int indexOf(const char* str); + String toString(); + void clear(); + char getCharAtIdx(int index) const; + void insert(char c, int index); + void free(); +}; diff --git a/tools/transpiler/build.sh b/tools/transpiler/build.sh new file mode 100755 index 0000000..ea1cb93 --- /dev/null +++ b/tools/transpiler/build.sh @@ -0,0 +1 @@ +g++ -o transpiler transpiler.cpp Logger.cpp MyString.cpp replacer.cpp diff --git a/tools/transpiler/pages.txt b/tools/transpiler/pages.txt new file mode 100644 index 0000000..cea23dd --- /dev/null +++ b/tools/transpiler/pages.txt @@ -0,0 +1,16 @@ +🏀 "2D" +>"Rigidbody" +>>/2d/rigidbody/rigidbody_1.html "Linear Forces" +>>/2d/rigidbody/rigidbody_2.html "Rotational Forces" +>>/2d/rigidbody/rigidbody_3.html "Collisions" +>"Collisions" +>>/2d/_collisions/rectangle_line.html "Rectangle-Line" +>>/2d/_collisions/rectangle_rectangle.html "Rectangle-Rectangle" +>>/2d/_collisions/polygon_polygon.html "Separating Axis Theorem" +🌠 "3D" +>"Rigidbody" +>>/3d/rigidbody.html "Rigidbody in 3D" +🔧 "WebAssembly" +>>/intro/intro.html "Introduction" +🛈 "About" +>>/roadmap.html "Roadmap" diff --git a/tools/transpiler/replacer.cpp b/tools/transpiler/replacer.cpp new file mode 100644 index 0000000..3f48089 --- /dev/null +++ b/tools/transpiler/replacer.cpp @@ -0,0 +1,128 @@ +#include "replacer.h" +#include "Logger.h" +#include + +const char* keywords[] = { + "cosnt", "struct", "static", "return" +}; + +const char* types[] = { + "void", + "int", "int8", "int16", "int32", "int64", + "float", "float8", "float16", "float32", "float64", + "long", + "double", + "bool", + "Vector2", "Vector3", "Vector4", + "struct", "class", "static", "return", "const" +}; + +const char* commentType = "//"; + +bool isType(char* str, int size) { + if (size == 0) { + return false; + } + + for (int i = 0; i < sizeof(types) / sizeof(char*); i++) { + if (size == strlen(types[i]) && strncmp(str, types[i], size) == 0) { + return true; + } + } + + return false; +} + +bool isTokenDelim(char c) { + return isspace(c) || c == '\0' || c == ';' || c == ','; +} + +// @TODO: This has brought me to two realizations: +// 1) C++ string manipulaton is difficult for no apparent reason +// 2) My current implementation of String is lacking so many features that I would want +// 3) I have no plans to update my current implementation any time soon, so I will +// begrudginly give my heart and soul over to the standard library. +std::string insertCodeSnippets(char* workingDirectory, char* bodyContent) { + std::string strWorkingDirectory (workingDirectory); + strWorkingDirectory = strWorkingDirectory.substr(0, strWorkingDirectory.find_last_of("/") + 1); + std::string strContent(bodyContent); + + size_t found = -1; + do { + found = strContent.find("#SNIPPET", found + 1); + if (found == std::string::npos) { + break; + } + int startFound = found; + found += strlen("#SNIPPET "); + + std::string fileName(strWorkingDirectory); + while (!isspace(strContent[found])) { + fileName += strContent[found]; + found++; + } + + size_t endFound = found + 1; + + FILE* snippetFile = fopen(fileName.c_str(), "r+"); + if (snippetFile == NULL) { + logger_warning("Could not find snippet: %s", fileName.c_str()); + continue; + } + + fseek(snippetFile, 0, SEEK_END); + long fsize = ftell(snippetFile); + fseek(snippetFile, 0, SEEK_SET); + + char* snippetContent = new char[fsize + 1]; + fread(snippetContent, 1, fsize, snippetFile); + snippetContent[fsize] = '\0'; + + std::string s; + int tokenStart = 0, tokenEnd = 0; + while (snippetContent[tokenEnd] != '\0') { + while (!isTokenDelim(snippetContent[tokenEnd])) { + tokenEnd++; + } + + int tokenLength = (tokenEnd - tokenStart); + + if (tokenLength == strlen(commentType) && strncmp(&snippetContent[tokenStart], commentType, tokenLength) == 0) { + // @NOTE: Assuming comments are always on a single line + s.append(""); + while (snippetContent[tokenEnd] != '\n') { + tokenEnd++; + } + s.append(&snippetContent[tokenStart], tokenEnd - tokenStart); + s.append(""); + } else if (isType(&snippetContent[tokenStart], tokenLength)) { + s.append(""); + s.append(&snippetContent[tokenStart], tokenLength); + s.append(""); + } else { + s.append(&snippetContent[tokenStart], tokenLength); + } + + s += snippetContent[tokenEnd]; + + tokenStart = tokenEnd + 1; + tokenEnd = tokenStart; + } + + while (s[0] == '\n') { + s.erase(0, 1); + } + + s.insert(0, ""); + s.insert(0, "
      ");
      +        s.append("");
      +        s.append("
      "); + + delete [] snippetContent; + + strContent.replace(startFound, endFound - startFound, s); + + } while (true); + + return strContent; +} diff --git a/tools/transpiler/replacer.h b/tools/transpiler/replacer.h new file mode 100644 index 0000000..f795bca --- /dev/null +++ b/tools/transpiler/replacer.h @@ -0,0 +1,3 @@ +#include + +std::string insertCodeSnippets(char* workingDirectory, char* bodyContent); diff --git a/tools/transpiler/transpiler b/tools/transpiler/transpiler new file mode 100755 index 0000000..dda66e9 Binary files /dev/null and b/tools/transpiler/transpiler differ diff --git a/tools/transpiler/transpiler.cpp b/tools/transpiler/transpiler.cpp new file mode 100644 index 0000000..c27ce51 --- /dev/null +++ b/tools/transpiler/transpiler.cpp @@ -0,0 +1,255 @@ +#include "MyString.h" +#include "List.h" +#include "Logger.h" +#include "replacer.h" +#include +#include +#include + +struct InnerCategory { + bool isLabel = false; + String label; + String path; + + void toCode(StringBuilder& sb) { + if (isLabel) { + sb.format("\t\t\t\t\t
    • \n", label.getValue()); + } else { + sb.format("\t\t\t\t\t
    • %s
    • \n", path.getValue(), path.getValue(), label.getValue()); + } + } + + void free() { + label.free(); + path.free(); + } +}; + +struct OuterCategory { + String outerName; + String outerSymbol; + List items; + + void toCode(StringBuilder& sb) { + sb.addStr("\t\t\t
    • \n"); + sb.format("\t\t\t\t%s%s\n", outerSymbol.getValue(), outerName.getValue()); + sb.addStr("\t\t\t\t
        \n"); + + FOREACH(items) { + value->toCode(sb); + } + + sb.addStr("\t\t\t\t
      \n"); + sb.addStr("\t\t\t
    • \n"); + } + + void reset() { + outerName.free(); + outerSymbol.free(); + items.clear(); + } + + void free() { + outerName.free(); + outerSymbol.free(); + + FOREACH(items) { + value->free(); + } + items.deallocate(); + } +}; + +int main() { + logger_info("Running transpiler"); + FILE* pagesFile = fopen("tools/transpiler/pages.txt", "r+"); + + char * cLine = NULL; + size_t len = 0; + ssize_t read; + + StringBuilder sb; + StringBuilder otherSb; + + sb.addStr("\t\t\n"); + oc.reset(); + fclose(pagesFile); + + String navbarRetval = sb.toString(); + sb.clear(); + + logger_info("Creating served files"); + + // Create the header + FOREACH(servedFiles) { + otherSb.clear(); + otherSb.format("%s.content", value->getValue()); + String contentFileName = otherSb.toString(); + FILE* contentFile = fopen(contentFileName.getValue(), "r+"); + if (contentFile == NULL) { + logger_warning("Could not output file, most likely unimplemented: %s", contentFileName.getValue()); + continue; + } + + logger_info("Outputting file: %s", contentFileName.getValue()); + + // Header + sb.addStr("\n" + "\n" + "\t\n" + "\t\t\n" + "\t\t\n" + "\t\tPhysics for Games\n" + "\t\t\n" + "\t\t\n" + "\t\t\n" + "\t\n" + "\t\n" + "\t\t
      \n" + "\t\t\t

      Physics for Games

      \n" + "\t\t
      \n" + "\t\t
      \n"); + + // Write navigation bar + sb.addStr(navbarRetval.getValue()); + // Read body + + fseek(contentFile, 0, SEEK_END); + long fsize = ftell(contentFile); + fseek(contentFile, 0, SEEK_SET); + + char* bodyContent = new char[fsize + 1]; + fread(bodyContent, 1, fsize, contentFile); + bodyContent[fsize] = '\0'; + + auto processedBody = insertCodeSnippets(contentFileName.getValue(), bodyContent); + + sb.addStr(processedBody.c_str()); + delete [] bodyContent; + + + contentFileName.free(); + fclose(contentFile); + + // Footer + sb.addStr("\t\t
      \n" + "\t\n" + "\n"); + + FILE* outFile = fopen(value->getValue(), "w+"); + String outStr = sb.toString(); + fwrite(outStr.getValue(), 1, outStr.length, outFile); + outStr.free(); + fclose(outFile); + sb.clear(); + } + + logger_info("Outputting files to their proper directories..."); + sb.free(); + navbarRetval.free(); + otherSb.free(); + + FOREACH(servedFiles) { + value->free(); + } + + servedFiles.deallocate(); + + return 0; +} diff --git a/tools/zipper/zipper.cpp b/tools/zipper/zipper.cpp new file mode 100644 index 0000000..1b54c40 --- /dev/null +++ b/tools/zipper/zipper.cpp @@ -0,0 +1,6 @@ +#include +#include + +int main() { + return 0; +} diff --git a/transpiler/List.h b/transpiler/List.h deleted file mode 100644 index 00a466a..0000000 --- a/transpiler/List.h +++ /dev/null @@ -1,222 +0,0 @@ -#pragma once -#include -#include -#include "Logger.h" - -#define FOREACH(list) \ - for (size_t idx = 0; idx < list.numElements; idx++) \ - if (auto value = list.getValue(idx)) \ - - -template -struct List { - T* data = nullptr; - size_t capacity = 0; - size_t numElements = 0; - bool growDynamically = true; - - void allocate(size_t size); - void add(T* element); - void add(T& element); - void add(T&& element); - bool grow(size_t newSize); - void set(T* value, size_t index); - void remove(size_t index); - void clear(); - void deallocate(); - bool isEmpty() { - return data == nullptr || numElements == 0; - } - T* getValue(int index) const; - T& operator[](int idx) const; - void binarySort(int (*f)(T *first, T* second)); - void setFromArray(T* arry, int size) { - allocate(size); - memcpy(data, arry, size * sizeof(T)); - numElements = size; - } - void remove(int index) { - if (index >= numElements) { - logger_error("Cannot remove element at index: %d", index); - return; - } - - - if (index == numElements - 1) { - numElements--; - return; - } - - memmove(data[index], data[index + 1], sizeof(T) * (numElements - index)); - numElements--; - } -}; - -template -void List::allocate(size_t size) { - if (size == 0 || size == capacity) { - numElements = 0; - return; - } - - if (data != nullptr) { - deallocate(); - } - - data = static_cast(malloc(sizeof(T) * size)); - capacity = size; - numElements = 0; -} - -template -bool List::grow(size_t newSize) { - if (!growDynamically) { - return false; - } - - if (newSize == 0) { - return false; - } - - T* newData = static_cast(malloc(sizeof(T) * newSize)); - - if (data != nullptr) { - memcpy(newData, data, numElements * sizeof(T)); - delete data; - } - - data = newData; - capacity = newSize; - return true; -} - -template -void List::set(T* value, size_t index) { - if (index >= capacity && !grow(index * 2)) { - return; - } - - memcpy(&data[index], value, sizeof(T)); -} - -template -void List::add(T* element) { - if (data == nullptr) { - allocate(2); - } - - if (element == nullptr) { - logger_error("Element not defined"); - return; - } - - size_t newNumElements = numElements + 1; - if (newNumElements > capacity) { - if (!grow(2 * capacity)) { - logger_error("Trying to add to list but unable to grow the array"); - return; - } - } - - memcpy(&data[numElements], element, sizeof(T)); - numElements = newNumElements; -} - -template -void List::add(T& element) { - if (data == nullptr) { - allocate(2); - } - - size_t newNumElements = numElements + 1; - if (newNumElements > capacity) { - if (!grow(2 * capacity)) { - logger_error("Trying to add to list but unable to grow the array"); - return; - } - } - - memcpy(&data[numElements], &element, sizeof(T)); - numElements = newNumElements; -} - -template -void List::add(T&& element) { - if (data == nullptr) { - allocate(2); - } - - size_t newNumElements = numElements + 1; - if (newNumElements > capacity) { - if (!grow(2 * capacity)) { - logger_error("Trying to add to list but unable to grow the array"); - return; - } - } - - memcpy(&data[numElements], &element, sizeof(T)); - numElements = newNumElements; -} - -template -void List::remove(size_t idx) { - if (idx >= numElements) { - logger_error("Index is outside of the list: %d >= %d", idx, numElements); - return; - } - - for (; idx < numElements - 1; idx++) { - data[idx] = data[idx + 1]; - } - - numElements--; -} - -template -void List::deallocate() { - if (data != nullptr) { - free(data); - data = nullptr; - } - - capacity = 0; - numElements = 0; -} - -template -void List::clear() { - numElements = 0; -} - -template -T* List::getValue(int idx) const { - return &data[idx]; -} - -template -T& List::operator[](int idx) const { - return data[idx]; -} - -template -void List::binarySort(int (*f)(T *first, T* second)) { - if (data == nullptr) { - return; - } - - for (size_t idx = 0; idx < numElements - 1; idx++) { - int minIdx = idx; - T firstValue = data[idx]; - - for (int innerIdx = idx + 1; innerIdx < numElements; innerIdx++) {\ - T secondValue = data[innerIdx]; - if (f(&firstValue, &secondValue) > 0) { - minIdx= innerIdx; - } - } - - T temp = data[minIdx]; - memmove(&data[minIdx], &data[idx], sizeof(T)); - memmove(&data[idx], &temp, sizeof(T)); - } -} diff --git a/transpiler/Logger.cpp b/transpiler/Logger.cpp deleted file mode 100644 index 1068d88..0000000 --- a/transpiler/Logger.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "Logger.h" -#include -#include -#include - -namespace Logger { - LogLevel gLogLevel = LogLevel_Debug; - FILE* gFilePointer = NULL; - - void initialize(LoggerOptions options) { - setLevel(options.level); - if (options.logToFile) { -#ifdef WIN32 - fopen_s(&gFilePointer, options.filePath, "a"); -#else - gFilePointer = fopen(options.filePath, "a"); -#endif - } - } - - void setLevel(LogLevel level) { - gLogLevel = level; - } - - LogLevel getLevel() { - return gLogLevel; - } - - void printHeader(const char* levelStr, const char* fileName, int lineNumber) { - time_t t = time(0); - tm now; -#ifdef WIN32 - localtime_s(&now, &t); -#else - now = *localtime(&t); -#endif - - printf("%s:%d [%d-%d-%d %d:%d:%d] %s: ", fileName, lineNumber, (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); - if (gFilePointer != NULL) { - fprintf(gFilePointer, "[%d-%d-%d %d:%d:%d] %s: ", (now.tm_year + 1900), (now.tm_mon + 1), now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, levelStr); - } - } - - void logInternal(const char* file, int lineNumber,LogLevel level, const char* format, va_list args) { - if (level < gLogLevel) { - return; - } - - - const char* levelStr; - switch (level) { - case LogLevel_Debug: - levelStr = "Debug"; - break; - case LogLevel_Info: - levelStr = "Info"; - break; - case LogLevel_Warn: - levelStr = "Warning"; - break; - case LogLevel_Error: - levelStr = "Error"; - break; - default: - levelStr = "Unknown"; - break; - } - - if (gFilePointer != NULL) { - va_list fileArgs; - va_copy(fileArgs, args); - vfprintf(gFilePointer, format, fileArgs); - fprintf(gFilePointer, "\n"); - } - - printHeader(levelStr, file, lineNumber); - - vprintf(format, args); - printf("\n"); - } - - void doLog(const char* file, int lineNumber,LogLevel level, const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, level, format, args); - va_end(args); - } - - void doDebug(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Debug, format, args); - va_end(args); - } - - void doInfo(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Info, format, args); - va_end(args); - } - - void doWarning(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Warn, format, args); - va_end(args); - } - - void doError(const char* file, int lineNumber,const char* format, ...) { - va_list args; - va_start(args, format); - logInternal(file, lineNumber, LogLevel_Error, format, args); - va_end(args); - } - - void free() { - if (gFilePointer) { - fclose(gFilePointer); - gFilePointer = NULL; - } - } -} diff --git a/transpiler/Logger.h b/transpiler/Logger.h deleted file mode 100644 index 7596b6f..0000000 --- a/transpiler/Logger.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef LOGGER_H -#define LOGGER_H - -#include - -enum LogLevel { - LogLevel_Debug = 0, - LogLevel_Info = 1, - LogLevel_Warn = 2, - LogLevel_Error = 3 -}; - -struct LoggerOptions { - LogLevel level = LogLevel_Debug; - bool logToFile = false; - const char* filePath = "debug.log"; -}; - -namespace Logger { - void initialize(LoggerOptions options); - void setLevel(LogLevel level); - LogLevel getLevel(); - void doLog(const char* file, int lineNumber, LogLevel level, const char* format, ...); - void doDebug(const char* file, int lineNumber, const char* format, ...); - void doInfo(const char* file, int lineNumber, const char* format, ...); - void doWarning(const char* file, int lineNumber, const char* format, ...); - void doError(const char* file, int lineNumber, const char* format, ...); - void free(); -}; - -#if WIN32 -#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__) -#else -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#endif - -#define logger_log(level, format, ...) Logger::doLog(__FILENAME__, __LINE__, level, format, ## __VA_ARGS__) -#define logger_debug(format, ...) Logger::doDebug(__FILENAME__, __LINE__, format, ## __VA_ARGS__) -#define logger_info(format, ...) Logger::doInfo(__FILENAME__, __LINE__, format, ## __VA_ARGS__) -#define logger_warning(format, ...) Logger::doWarning(__FILENAME__, __LINE__, format, ## __VA_ARGS__) -#define logger_error(format, ...) Logger::doError(__FILENAME__, __LINE__, format, ## __VA_ARGS__) - -#endif diff --git a/transpiler/MathHelper.h b/transpiler/MathHelper.h deleted file mode 100644 index b9c9cb6..0000000 --- a/transpiler/MathHelper.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#define PI 3.14159265358979323846f - -#define MIN(a,b) (((a)<(b))?(a):(b)) -#define MAX(a,b) (((a)>(b))?(a):(b)) -#define CLAMP(x, upper, lower) (MIN(upper, MAX(x, lower))) -#define DTOR (PI / 180.0f) -#define SWAP(x, y, T) do { T SWAP = x; x = y; y = SWAP; } while (0) - -namespace MathHelper { - inline float radiansToDegrees(float radians) { - return radians * 180.f / PI; - } - - inline float degreesToRadians(float degrees) { - return degrees * PI / 180.f; - } - - inline int nearestPowerOfTwo(int n) { - int v = n; - - v--; - v |= v >> 1; - v |= v >> 2; - v |= v >> 4; - v |= v >> 8; - v |= v >> 16; - v++; // next power of 2 - - return v; - } -} diff --git a/transpiler/MyString.cpp b/transpiler/MyString.cpp deleted file mode 100644 index 1a63960..0000000 --- a/transpiler/MyString.cpp +++ /dev/null @@ -1,392 +0,0 @@ -#include "MyString.h" -#include "MathHelper.h" -#include -#include -#include -#include -#include - -int StringBuffer::add(const char* str) { - int spaceNeeded = strlen(str); - int spaceAvailable = BUFFER_SIZE - pointer; - - int amountToCopy = 0; - if (spaceAvailable >= spaceNeeded) { - amountToCopy = spaceNeeded; - } else { - amountToCopy = spaceAvailable; - } - - memcpy(&buffer[pointer], str, sizeof(char) * amountToCopy); - pointer += amountToCopy; - buffer[pointer] = '\0'; - return amountToCopy; -} - -void StringBuffer::reset() { - buffer[0] = '\0'; - pointer = 0; -} - -bool StringBuffer::isFull() { - return pointer == BUFFER_SIZE; -} - -StringBuffer* StringBuilder::getCurrentBuffer() { - if (bufferPointer == 0) { - if (!defaultBuffer.isFull()) { - return &defaultBuffer; - } - - StringBuffer newBuffer; - dynamicBuffer.allocate(2); - bufferPointer++; - dynamicBuffer.add(&newBuffer); - } - - if (dynamicBuffer[bufferPointer - 1].isFull()) { - StringBuffer newBuffer; - dynamicBuffer.add(&newBuffer); - bufferPointer++; - } - - return &dynamicBuffer[bufferPointer - 1]; -} - -StringBuffer* StringBuilder::getBufferAtIdx(int index) { - if (index == 0) { - return &defaultBuffer; - } - - index = index - 1; - if (index < static_cast(dynamicBuffer.numElements)) { - return &dynamicBuffer[index]; - } - - return nullptr; -} - -const StringBuffer* StringBuilder::getBufferAtIdxConst(int index) const { - if (index == 0) { - return &defaultBuffer; - } - - index = index - 1; - if (index < static_cast(dynamicBuffer.numElements)) { - return &dynamicBuffer[index]; - } - - return nullptr; -} - -String StringBuilder::toString() { - int strSize = (bufferPointer + 1) * StringBuffer::BUFFER_SIZE; - String retval; - - if (strSize > String::SSO_SIZE) { - retval.dynamicBuffer = new char[strSize + 1]; - memcpy(retval.dynamicBuffer, defaultBuffer.buffer, sizeof(char) * StringBuffer::BUFFER_SIZE); - FOREACH(dynamicBuffer) { - memcpy(&retval.dynamicBuffer[(idx + 1) * StringBuffer::BUFFER_SIZE], value->buffer, sizeof(char) * StringBuffer::BUFFER_SIZE); - } - retval.isSSO = false; - } else { - memcpy(retval.defaultBuffer, defaultBuffer.buffer, sizeof(char) * strSize); - retval.isSSO = true; - } - - retval.capacity = strSize; - retval.length = length; - retval.getValue()[retval.length] = '\0'; - return retval; -} - -void StringBuilder::addStr(String* str) { - addStr(str->getValue()); -} - -void StringBuilder::addStr(const char* str, int inLength) { - int amountLeft = inLength < 0 ? strlen(str) : inLength; - length += amountLeft; - int ptr = 0; - - do { - StringBuffer* currentBuffer = getCurrentBuffer(); - int amtAdded = currentBuffer->add(&str[ptr]); - amountLeft = amountLeft - amtAdded; - ptr += amtAdded; - } while (amountLeft > 0); -} - -void StringBuilder::addChar(char c) { - char wrapper[2] = { c, '\0' }; - addStr(wrapper); -} - -const int DEFAULT_NUMBER_BUFFER_SIZE = 32; - -void StringBuilder::addInt(int value) { - char buffer[DEFAULT_NUMBER_BUFFER_SIZE]; - sprintf(buffer, "%d", value); - addStr(buffer); -} - -// Pulled from here: https://stackoverflow.com/questions/7228438/convert-double-float-to-string -inline int n_tu(int number, int count) { - int result = 1; - while(count-- > 0) - result *= number; - - return result; -} - -void StringBuilder::addFloat(float value) { - char buffer[DEFAULT_NUMBER_BUFFER_SIZE]; - sprintf(buffer, "%f", value); - addStr(buffer); -} - -void StringBuilder::replace(const char* strToReplace, const char* replaceStr) { - -} - -int StringBuilder::indexOf(const char* str) { - return -1; -} - -void StringBuilder::removeAt(int index, int count) { - if (index >= length) { - printf("Index is larger than the length"); - return; - } - - if (count <= 0) { - printf("Count was <= zero"); - return; - } - - int newLength = length - count; - if (newLength == 0) { - clear(); - return; - } - - // Easiest way to do this is to move the entire string to the left, starting at index by count - int currentIndex = index; - int copyIndex = index + count; - - while (currentIndex < newLength) { - int bufferPos = floor(static_cast(currentIndex) / static_cast(StringBuffer::BUFFER_SIZE)); - int pointerIdx = currentIndex % StringBuffer::BUFFER_SIZE; - StringBuffer* buffer = getBufferAtIdx(bufferPos); - - char newChar = '\0'; - int copyBufferPos = floor(static_cast(copyIndex) / static_cast(StringBuffer::BUFFER_SIZE)); - StringBuffer* copyBuffer = getBufferAtIdx(copyBufferPos); - if (copyBuffer != nullptr) { - int bufferPointerIdx = copyIndex % StringBuffer::BUFFER_SIZE; - if (bufferPointerIdx < copyBuffer->pointer) { - newChar = copyBuffer->buffer[bufferPointerIdx]; - } - } - - buffer->buffer[pointerIdx] = newChar; - - currentIndex++; - copyIndex++; - } - - // Brute force update the buffer that we're currently using - length = newLength; - int newNumBuffers = ceil(static_cast(length) / static_cast(StringBuffer::BUFFER_SIZE)); // This is how many buffers we should have - if (newNumBuffers <= 1) { - dynamicBuffer.numElements = 0; - bufferPointer = 0; - } else { - dynamicBuffer.numElements = newNumBuffers - 1; - bufferPointer = newNumBuffers - 1; - } - - StringBuffer* lastBuffer = getBufferAtIdx(bufferPointer); - if (lastBuffer->pointer != StringBuffer::BUFFER_SIZE) { - lastBuffer->pointer = length % StringBuffer::BUFFER_SIZE; - } - lastBuffer->buffer[lastBuffer->pointer] = '\0'; -} - -void StringBuilder::format(const char* str, ...) { - va_list args; - va_start(args, str); - - while (*str != '\0') { - if (*str == '%') { - str++; - switch (*str) { - case 'd': - addInt(va_arg(args, int)); - break; - case 'f': - addFloat(static_cast(va_arg(args, double))); - break; - case 's': - addStr(va_arg(args, char*), -1); - default: - break; - } - } else { - addChar(*str); - } - - str++; - } - - va_end(args); -} - -void StringBuilder::clear() { - bufferPointer = 0; - length = 0; - defaultBuffer.reset(); - - if (!dynamicBuffer.isEmpty()) { - FOREACH(dynamicBuffer) { - value->reset(); - } - } - - dynamicBuffer.clear(); -} - -void StringBuilder::free() { - dynamicBuffer.deallocate(); -} - -char StringBuilder::getCharAtIdx(int index) const { - int bufferPos = floor(static_cast(index) / static_cast(StringBuffer::BUFFER_SIZE)); - int pointerIdx = index % StringBuffer::BUFFER_SIZE; - const StringBuffer* buffer = getBufferAtIdxConst(bufferPos); - if (buffer != nullptr && pointerIdx < buffer->pointer) { - return buffer->buffer[pointerIdx]; - } - - return '\0'; -} - -void StringBuilder::insert(char c, int index) { - if (index >= length) { - addChar(c); - return; - } - - // Move all of the characters forward by one - char currentChar = c; - for (int cIdx = index; cIdx < length + 1; cIdx++) { - int bufferPos = floor(static_cast(cIdx) / static_cast(StringBuffer::BUFFER_SIZE)); - int pointerIdx = cIdx % StringBuffer::BUFFER_SIZE; - StringBuffer* buffer = getBufferAtIdx(bufferPos); - if (buffer == nullptr) { - buffer = getCurrentBuffer(); // Get a new buffer if this one is filled up - } - - char nextCurrent = buffer->buffer[pointerIdx]; - buffer->buffer[pointerIdx] = currentChar; - currentChar = nextCurrent; - } - - // Move the final buffer forward - StringBuffer* finalBuffer = getCurrentBuffer(); - finalBuffer->pointer++; - finalBuffer->buffer[finalBuffer->pointer] = '\0'; - length = length + 1; -} - -String::String() { -} - -String::String(const char* str) { - set(str); -} - -void String::operator =(const char* str) { - set(str); -} - -char* String::getValue() { - return isSSO ? defaultBuffer : dynamicBuffer; -} - -const char* String::getValueConst() const { - return isSSO ? defaultBuffer : dynamicBuffer; -} - -void String::set(const char* str) { - int inLen = strlen(str); - - if (inLen <= SSO_SIZE) { - isSSO = true; - memcpy(defaultBuffer, str, inLen); - defaultBuffer[inLen] = '\0'; - } else { - if (capacity >= inLen) { - memcpy(dynamicBuffer, str, inLen); - } else { - free(); - capacity = MathHelper::nearestPowerOfTwo(inLen + 1); - dynamicBuffer = new char[capacity]; - memcpy(dynamicBuffer, str, inLen); - } - isSSO = false; - dynamicBuffer[inLen] = '\0'; - } - - length = inLen; -} - -void String::free() { - capacity = 0; - length = 0; - isSSO = true; - if (dynamicBuffer != nullptr) { - delete dynamicBuffer; - } - - defaultBuffer[0] = '\0'; -} - -int String::toInteger() { - return atoi(getValue()); -} - -float String::toFloat() { - return static_cast(atof(getValue())); -} - -int String::indexOf(char c) { - char* buffer = getValue(); - int idx = 0; - while (buffer[idx] != '\0') { - if (buffer[idx] == c) { - return idx; - } - idx++; - } - - return -1; -} - -StringView String::substring(int start, int end) { - StringView retval; - if (start >= length) { - retval.error = true; - return retval; - } - - if (end >= length) { - end = length - 1; - } - - char* value = getValue(); - retval.value = &value[start]; - retval.length = end - start; - return retval; -} diff --git a/transpiler/MyString.h b/transpiler/MyString.h deleted file mode 100644 index 805f5de..0000000 --- a/transpiler/MyString.h +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once -#include "List.h" - -struct StringView { - bool error = false; - char* value = nullptr; - size_t length = 0; -}; - -struct String { - const static int SSO_SIZE = 31; - - char defaultBuffer[String::SSO_SIZE + 1] = { '\0' }; - char* dynamicBuffer = nullptr; - - int length = 0; - int capacity = 0; - bool isSSO = true; - - String(); - String(const char* str); - char* getValue(); - const char* getValueConst() const; - void operator =(const char* str); - void set(const char* str); - void free(); - int toInteger(); - float toFloat(); - int indexOf(char c); - inline bool equals(const String& other) { return strcmp(getValueConst(), other.getValueConst()) == 0; }; - inline bool equalsCstr(const char* str) { return strcmp(getValueConst(), str) == 0; }; - StringView substring(int start, int end); -}; - -struct StringBuffer { - const static int BUFFER_SIZE = 31; - - int pointer = 0; - char buffer[StringBuffer::BUFFER_SIZE + 1]; // Leave space for trailing escape character - - /* - * Appends the string to the buffer - * @param str - * @returns number of characters copied - */ - int add(const char* str); - bool isFull(); - void reset(); -}; - -struct StringBuilder { - int bufferPointer = 0; - int length = 0; - - StringBuffer defaultBuffer; - List dynamicBuffer; - - StringBuffer* getCurrentBuffer(); - StringBuffer* getBufferAtIdx(int index); - const StringBuffer* getBufferAtIdxConst(int index) const; - void addStr(String* str); - void addStr(const char* str, int length = -1); - void addChar(char c); - void format(const char* str, ...); - void addInt(int value); - void addFloat(float value); - void replace(const char* strToReplace, const char* replaceStr); - void removeAt(int index, int count); - int indexOf(char c); - int indexOf(const char* str); - String toString(); - void clear(); - char getCharAtIdx(int index) const; - void insert(char c, int index); - void free(); -}; diff --git a/transpiler/build.sh b/transpiler/build.sh deleted file mode 100755 index ea1cb93..0000000 --- a/transpiler/build.sh +++ /dev/null @@ -1 +0,0 @@ -g++ -o transpiler transpiler.cpp Logger.cpp MyString.cpp replacer.cpp diff --git a/transpiler/pages.txt b/transpiler/pages.txt deleted file mode 100644 index cea23dd..0000000 --- a/transpiler/pages.txt +++ /dev/null @@ -1,16 +0,0 @@ -🏀 "2D" ->"Rigidbody" ->>/2d/rigidbody/rigidbody_1.html "Linear Forces" ->>/2d/rigidbody/rigidbody_2.html "Rotational Forces" ->>/2d/rigidbody/rigidbody_3.html "Collisions" ->"Collisions" ->>/2d/_collisions/rectangle_line.html "Rectangle-Line" ->>/2d/_collisions/rectangle_rectangle.html "Rectangle-Rectangle" ->>/2d/_collisions/polygon_polygon.html "Separating Axis Theorem" -🌠 "3D" ->"Rigidbody" ->>/3d/rigidbody.html "Rigidbody in 3D" -🔧 "WebAssembly" ->>/intro/intro.html "Introduction" -🛈 "About" ->>/roadmap.html "Roadmap" diff --git a/transpiler/replacer.cpp b/transpiler/replacer.cpp deleted file mode 100644 index 3f48089..0000000 --- a/transpiler/replacer.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include "replacer.h" -#include "Logger.h" -#include - -const char* keywords[] = { - "cosnt", "struct", "static", "return" -}; - -const char* types[] = { - "void", - "int", "int8", "int16", "int32", "int64", - "float", "float8", "float16", "float32", "float64", - "long", - "double", - "bool", - "Vector2", "Vector3", "Vector4", - "struct", "class", "static", "return", "const" -}; - -const char* commentType = "//"; - -bool isType(char* str, int size) { - if (size == 0) { - return false; - } - - for (int i = 0; i < sizeof(types) / sizeof(char*); i++) { - if (size == strlen(types[i]) && strncmp(str, types[i], size) == 0) { - return true; - } - } - - return false; -} - -bool isTokenDelim(char c) { - return isspace(c) || c == '\0' || c == ';' || c == ','; -} - -// @TODO: This has brought me to two realizations: -// 1) C++ string manipulaton is difficult for no apparent reason -// 2) My current implementation of String is lacking so many features that I would want -// 3) I have no plans to update my current implementation any time soon, so I will -// begrudginly give my heart and soul over to the standard library. -std::string insertCodeSnippets(char* workingDirectory, char* bodyContent) { - std::string strWorkingDirectory (workingDirectory); - strWorkingDirectory = strWorkingDirectory.substr(0, strWorkingDirectory.find_last_of("/") + 1); - std::string strContent(bodyContent); - - size_t found = -1; - do { - found = strContent.find("#SNIPPET", found + 1); - if (found == std::string::npos) { - break; - } - int startFound = found; - found += strlen("#SNIPPET "); - - std::string fileName(strWorkingDirectory); - while (!isspace(strContent[found])) { - fileName += strContent[found]; - found++; - } - - size_t endFound = found + 1; - - FILE* snippetFile = fopen(fileName.c_str(), "r+"); - if (snippetFile == NULL) { - logger_warning("Could not find snippet: %s", fileName.c_str()); - continue; - } - - fseek(snippetFile, 0, SEEK_END); - long fsize = ftell(snippetFile); - fseek(snippetFile, 0, SEEK_SET); - - char* snippetContent = new char[fsize + 1]; - fread(snippetContent, 1, fsize, snippetFile); - snippetContent[fsize] = '\0'; - - std::string s; - int tokenStart = 0, tokenEnd = 0; - while (snippetContent[tokenEnd] != '\0') { - while (!isTokenDelim(snippetContent[tokenEnd])) { - tokenEnd++; - } - - int tokenLength = (tokenEnd - tokenStart); - - if (tokenLength == strlen(commentType) && strncmp(&snippetContent[tokenStart], commentType, tokenLength) == 0) { - // @NOTE: Assuming comments are always on a single line - s.append(""); - while (snippetContent[tokenEnd] != '\n') { - tokenEnd++; - } - s.append(&snippetContent[tokenStart], tokenEnd - tokenStart); - s.append(""); - } else if (isType(&snippetContent[tokenStart], tokenLength)) { - s.append(""); - s.append(&snippetContent[tokenStart], tokenLength); - s.append(""); - } else { - s.append(&snippetContent[tokenStart], tokenLength); - } - - s += snippetContent[tokenEnd]; - - tokenStart = tokenEnd + 1; - tokenEnd = tokenStart; - } - - while (s[0] == '\n') { - s.erase(0, 1); - } - - s.insert(0, ""); - s.insert(0, "
      ");
      -        s.append("");
      -        s.append("
      "); - - delete [] snippetContent; - - strContent.replace(startFound, endFound - startFound, s); - - } while (true); - - return strContent; -} diff --git a/transpiler/replacer.h b/transpiler/replacer.h deleted file mode 100644 index f795bca..0000000 --- a/transpiler/replacer.h +++ /dev/null @@ -1,3 +0,0 @@ -#include - -std::string insertCodeSnippets(char* workingDirectory, char* bodyContent); diff --git a/transpiler/transpiler b/transpiler/transpiler deleted file mode 100755 index 60e0eec..0000000 Binary files a/transpiler/transpiler and /dev/null differ diff --git a/transpiler/transpiler.cpp b/transpiler/transpiler.cpp deleted file mode 100644 index b10ca08..0000000 --- a/transpiler/transpiler.cpp +++ /dev/null @@ -1,255 +0,0 @@ -#include "MyString.h" -#include "List.h" -#include "Logger.h" -#include "replacer.h" -#include -#include -#include - -struct InnerCategory { - bool isLabel = false; - String label; - String path; - - void toCode(StringBuilder& sb) { - if (isLabel) { - sb.format("\t\t\t\t\t
    • \n", label.getValue()); - } else { - sb.format("\t\t\t\t\t
    • %s
    • \n", path.getValue(), path.getValue(), label.getValue()); - } - } - - void free() { - label.free(); - path.free(); - } -}; - -struct OuterCategory { - String outerName; - String outerSymbol; - List items; - - void toCode(StringBuilder& sb) { - sb.addStr("\t\t\t
    • \n"); - sb.format("\t\t\t\t%s%s\n", outerSymbol.getValue(), outerName.getValue()); - sb.addStr("\t\t\t\t
        \n"); - - FOREACH(items) { - value->toCode(sb); - } - - sb.addStr("\t\t\t\t
      \n"); - sb.addStr("\t\t\t
    • \n"); - } - - void reset() { - outerName.free(); - outerSymbol.free(); - items.clear(); - } - - void free() { - outerName.free(); - outerSymbol.free(); - - FOREACH(items) { - value->free(); - } - items.deallocate(); - } -}; - -int main() { - logger_info("Running transpiler"); - FILE* pagesFile = fopen("transpiler/pages.txt", "r+"); - - char * cLine = NULL; - size_t len = 0; - ssize_t read; - - StringBuilder sb; - StringBuilder otherSb; - - sb.addStr("\t\t\n"); - oc.reset(); - fclose(pagesFile); - - String navbarRetval = sb.toString(); - sb.clear(); - - logger_info("Creating served files"); - - // Create the header - FOREACH(servedFiles) { - otherSb.clear(); - otherSb.format("%s.content", value->getValue()); - String contentFileName = otherSb.toString(); - FILE* contentFile = fopen(contentFileName.getValue(), "r+"); - if (contentFile == NULL) { - logger_warning("Could not output file, most likely unimplemented: %s", contentFileName.getValue()); - continue; - } - - logger_info("Outputting file: %s", contentFileName.getValue()); - - // Header - sb.addStr("\n" - "\n" - "\t\n" - "\t\t\n" - "\t\t\n" - "\t\tPhysics for Games\n" - "\t\t\n" - "\t\t\n" - "\t\t\n" - "\t\n" - "\t\n" - "\t\t
      \n" - "\t\t\t

      Physics for Games

      \n" - "\t\t
      \n" - "\t\t
      \n"); - - // Write navigation bar - sb.addStr(navbarRetval.getValue()); - // Read body - - fseek(contentFile, 0, SEEK_END); - long fsize = ftell(contentFile); - fseek(contentFile, 0, SEEK_SET); - - char* bodyContent = new char[fsize + 1]; - fread(bodyContent, 1, fsize, contentFile); - bodyContent[fsize] = '\0'; - - auto processedBody = insertCodeSnippets(contentFileName.getValue(), bodyContent); - - sb.addStr(processedBody.c_str()); - delete [] bodyContent; - - - contentFileName.free(); - fclose(contentFile); - - // Footer - sb.addStr("\t\t
      \n" - "\t\n" - "\n"); - - FILE* outFile = fopen(value->getValue(), "w+"); - String outStr = sb.toString(); - fwrite(outStr.getValue(), 1, outStr.length, outFile); - outStr.free(); - fclose(outFile); - sb.clear(); - } - - logger_info("Outputting files to their proper directories..."); - sb.free(); - navbarRetval.free(); - otherSb.free(); - - FOREACH(servedFiles) { - value->free(); - } - - servedFiles.deallocate(); - - return 0; -} -- cgit v1.2.1