Browse Source

working algorithm and basic visualization

Kajetan Johannes Hammerle 3 years ago
parent
commit
c36eed44e4
7 changed files with 246 additions and 23 deletions
  1. 207 15
      Game.cpp
  2. 31 0
      Game.h
  3. 2 2
      Main.cpp
  4. 2 2
      input/Keys.cpp
  5. 1 1
      input/Keys.h
  6. 2 2
      rendering/GL.cpp
  7. 1 1
      rendering/GL.h

+ 207 - 15
Game.cpp

@@ -1,37 +1,229 @@
 #include <GL/glew.h>
 #include <GLFW/glfw3.h>
 #include <iostream>
+#include <algorithm>
 
 #include "Game.h"
 #include "utils/Random.h"
 
-Game::Game(Keys& keys) : keys(keys) {
+bool Game::Point::operator<(const Point& other) {
+    return x < other.x;
+}
+
+Game::Game(Keys& keys) : keys(keys), keyNext(keys.add(GLFW_KEY_N)), active(0) {
     std::cout << "register on " << keys.add(GLFW_KEY_W) << "\n";
+
+    Random r(6476);
+    for(int i = 0; i < 1000; i++) {
+        data.push_back({r.nextFloat() * 1.9f - 0.95f, r.nextFloat() * 1.9f - 0.95f});
+    }
+
+    std::cout << data.size() << "\n";
+
+    split();
+}
+
+void Game::merge() {
+    auto start = groups[active].begin();
+    auto end = groups[active].end();
+    if(start == end) {
+        if(groups[1 - active].begin() + 1 == groups[1 - active].end()) {
+            return;
+        }
+        active = 1 - active;
+    } else if(start + 1 == end) {
+        std::vector<Point> group = groups[active].front();
+        groups[active].pop_front();
+        groups[1 - active].push_back(group);
+        return;
+    }
+
+    std::vector<Point> hullA = groups[active].front();
+    groups[active].pop_front();
+    std::vector<Point> hullB = groups[active].front();
+    groups[active].pop_front();
+
+    std::vector<Point> mergedHull;
+    mergedHull.reserve(hullA.size() + hullB.size());
+
+    int lowerIndexA;
+    int lowerIndexB;
+    findLowerTangent(hullA, hullB, lowerIndexA, lowerIndexB);
+
+    int upperIndexA;
+    int upperIndexB;
+    findUpperTangent(hullA, hullB, upperIndexA, upperIndexB);
+
+    while(lowerIndexA != upperIndexA) {
+        mergedHull.push_back(hullA[lowerIndexA]);
+        lowerIndexA = (lowerIndexA + 1) % hullA.size();
+    }
+    mergedHull.push_back(hullA[upperIndexA]);
+
+    while(upperIndexB != lowerIndexB) {
+        mergedHull.push_back(hullB[upperIndexB]);
+        upperIndexB = (upperIndexB + 1) % hullB.size();
+    }
+    mergedHull.push_back(hullB[lowerIndexB]);
+
+    groups[1 - active].push_back(mergedHull);
+}
+
+void Game::split() {
+    std::sort(data.begin(), data.end());
+
+    int index = 0;
+    while(index + 2 < static_cast<int> (data.size())) {
+        groups[active].push_back(std::vector<Point>());
+        std::vector<Point>& v = groups[active].back();
+        v.push_back(data[index++]);
+        v.push_back(data[index++]);
+        v.push_back(data[index++]);
+        float f = det(groups[active].back()[0], groups[active].back()[1], groups[active].back()[2]);
+        if(std::abs(f) < 0.0001f) {
+            float minX = v[0].x;
+            float maxX = v[2].x;
+            if(std::abs(minX - maxX) < 0.0001f) {
+                float minY = std::min(v[0].y, std::min(v[1].y, v[2].y));
+                float maxY = std::max(v[0].y, std::max(v[1].y, v[2].y));
+                if(minY < v[0].y && v[0].y < maxY) {
+                    v.erase(v.begin());
+                } else if(minY < v[1].y && v[1].y < maxY) {
+                    v.erase(v.begin() + 1);
+                } else {
+                    v.pop_back();
+                }
+            } else {
+                v.erase(v.begin() + 1);
+            }
+        } else if(f > 0.0f) {
+            std::swap(groups[active].back()[1], groups[active].back()[2]);
+        }
+    }
+    int diff = data.size() - index;
+    if(diff > 0) {
+        groups[active].push_back(std::vector<Point>());
+        std::vector<Point>& v = groups[active].back();
+        while(index < static_cast<int> (data.size())) {
+            v.push_back(data[index++]);
+        }
+    }
+}
+
+bool Game::isLowerTangent(const Point& a, const Point& b, const std::vector<Point>& hull) const {
+    for(const Point& p : hull) {
+        float det = (a.x - p.x) * (b.y - p.y) - (b.x - p.x) * (a.y - p.y);
+        if(det < 0.0f) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool Game::isUpperTangent(const Point& a, const Point& b, const std::vector<Point>& hull) const {
+    for(const Point& p : hull) {
+        float det = (a.x - p.x) * (b.y - p.y) - (b.x - p.x) * (a.y - p.y);
+        if(det > 0.0f) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void Game::findLowerTangent(const std::vector<Point>& hullA, const std::vector<Point>& hullB, int& a, int& b) const {
+    a = findMaxX(hullA);
+    b = findMinX(hullB);
+
+    while(!isLowerTangent(hullA[a], hullB[b], hullA) || !isLowerTangent(hullA[a], hullB[b], hullB)) {
+        while(!isLowerTangent(hullA[a], hullB[b], hullA)) {
+            a = (a + 1) % hullA.size();
+        }
+        while(!isLowerTangent(hullA[a], hullB[b], hullB)) {
+            b = (b == 0) ? hullB.size() - 1 : b - 1;
+        }
+    }
+}
+
+void Game::findUpperTangent(const std::vector<Point>& hullA, const std::vector<Point>& hullB, int& a, int& b) const {
+    a = findMaxX(hullA);
+    b = findMinX(hullB);
+    while(!isUpperTangent(hullA[a], hullB[b], hullA) || !isUpperTangent(hullA[a], hullB[b], hullB)) {
+        while(!isUpperTangent(hullA[a], hullB[b], hullA)) {
+            a = (a == 0) ? hullA.size() - 1 : a - 1;
+        }
+        while(!isUpperTangent(hullA[a], hullB[b], hullB)) {
+            b = (b + 1) % hullB.size();
+        }
+    }
+}
+
+int Game::findMaxX(const std::vector<Point>& hull) const {
+    if(hull.size() == 0) {
+        return -1;
+    }
+    int index = 0;
+    for(int i = 1; i < static_cast<int> (hull.size()); i++) {
+        if(hull[i].x > hull[index].x) {
+            index = i;
+        }
+    }
+    return index;
+}
+
+int Game::findMinX(const std::vector<Point>& hull) const {
+    if(hull.size() == 0) {
+        return -1;
+    }
+    int index = 0;
+    for(int i = 1; i < static_cast<int> (hull.size()); i++) {
+        if(hull[i].x < hull[index].x) {
+            index = i;
+        }
+    }
+    return index;
 }
 
 void Game::tick() {
-    std::cout << keys.getDownTime(0) << " - 0 \n";
-    std::cout << keys.getDownTime(1) << " - 1 \n";
-    std::cout << keys.getDownTime(5335) << " - 5335\n";
+    if(keys.getDownTime(keyNext) == 1) {
+        merge();
+    }
+    if(keys.getDownTime(keyNext) >= 40) {
+        merge();
+    }
 }
 
 void Game::render(float lag, Renderer& renderer) const {
     (void) lag;
-    renderer.setPointSize(4);
-    renderer.drawPoint(0.5f, 0.5f, 0xFF0000);
-    renderer.drawPoint(0.0f, 0.5f, 0x00FF00);
-    renderer.drawPoint(0.5f, 0.0f, 0x0000FF);
-    renderer.drawLine(0.5f, 0.0f, 1.0f, 1.0f, 0xFFFF00);
+    renderer.setPointSize(7);
+    for(const Point& p : data) {
+        renderer.drawPoint(p.x, p.y, 0x707070);
+    }
 
-    Random r(0);
-    for(uint i = 0; i < 1000; i++) {
-        renderer.drawPoint(
-                r.nextFloat() * 2.0f - 1.0f,
-                r.nextFloat() * 2.0f - 1.0f,
-                0xFF0000);
+    static unsigned int color[] = {
+        0xFF0000, 0x00FF00, 0x0000FF
+    };
+    for(int k = 0; k < 2; k++) {
+        for(const std::vector<Point> group : groups[k]) {
+            for(int i = 0; i < static_cast<int> (group.size()); i++) {
+                int next = (i + 1) % group.size();
+                renderer.drawLine(
+                        group[i].x,
+                        group[i].y,
+                        group[next].x,
+                        group[next].y,
+                        0xFFFFFF);
+            }
+            for(int i = 0; i < static_cast<int> (group.size()); i++) {
+                renderer.drawPoint(group[i].x, group[i].y, color[i % 3]);
+            }
+        }
     }
 }
 
 bool Game::isRunning() const {
     return true;
+}
+
+float Game::det(const Point& a, const Point& b, const Point& c) const {
+    return (a.x - c.x) * (b.y - c.y) - (b.x - c.x) * (a.y - c.y);
 }

+ 31 - 0
Game.h

@@ -1,6 +1,9 @@
 #ifndef GAME_H
 #define GAME_H
 
+#include <vector>
+#include <deque>
+
 #include "input/Keys.h"
 #include "rendering/Renderer.h"
 
@@ -13,7 +16,35 @@ public:
     bool isRunning() const;
 
 private:
+    struct Point {
+        bool operator<(const Point& other);
+
+        float x;
+        float y;
+    };
+    
+    bool isLowerTangent(const Point& a, const Point& b, const std::vector<Point>& hull) const;
+    bool isUpperTangent(const Point& a, const Point& b, const std::vector<Point>& hull) const;
+    
+    void findLowerTangent(const std::vector<Point>& hullA, const std::vector<Point>& hullB, int& a, int& b) const;
+    void findUpperTangent(const std::vector<Point>& hullA, const std::vector<Point>& hullB, int& a, int& b) const;
+    
+    int findMaxX(const std::vector<Point>& hull) const;
+    int findMinX(const std::vector<Point>& hull) const;
+    
+    void split();
+    
+    float det(const Point& a, const Point& b, const Point& c) const;
+    void merge();
+
     Keys& keys;
+    int keyNext;
+    
+    std::vector<Point> data;
+    std::vector<Point> convexHull;
+    
+    int active;
+    std::deque<std::vector<Point>> groups[2];
 };
 
 #endif

+ 2 - 2
Main.cpp

@@ -53,10 +53,10 @@ int main() {
     initCallbacks(window, keys);
     window.show();
     
-    GL::enableDepthTesting();
+    GL::disableDepthTesting();
     GL::checkAndPrintError("setup error");
 
-    const u64 nanosPerTick = 50000000;
+    const u64 nanosPerTick = 25000000;
     u64 lastTime = GLFW::getTimeNanos();
     u64 lag = 0;
     while(!window.shouldClose() && game.isRunning()) {

+ 2 - 2
input/Keys.cpp

@@ -18,8 +18,8 @@ uint Keys::getIndex(uint key) const {
     return key * (key < keys.size());
 }
 
-uint Keys::add(int glfwKey) {
-    uint index = searchKey(-1);
+int Keys::add(int glfwKey) {
+    int index = searchKey(-1);
     keys[index].glfwKey = glfwKey;
     return index;
 }

+ 1 - 1
input/Keys.h

@@ -6,7 +6,7 @@
 #include "utils/Types.h"
 
 struct Keys final {
-    uint add(int glfwKey);
+    int add(int glfwKey);
     bool isDown(uint key) const;
     uint getDownTime(uint key) const;
 

+ 2 - 2
rendering/GL.cpp

@@ -35,8 +35,8 @@ bool GL::checkAndPrintError(const char* message) {
     return true;
 }
 
-void GL::enableDepthTesting() {
-    glEnable(GL_DEPTH_TEST);
+void GL::disableDepthTesting() {
+    glDisable(GL_DEPTH_TEST);
 }
 
 void GL::prepareMainFramebuffer() {

+ 1 - 1
rendering/GL.h

@@ -3,7 +3,7 @@
 
 namespace GL {
     bool checkAndPrintError(const char* message);
-    void enableDepthTesting();
+    void disableDepthTesting();
     void prepareMainFramebuffer();
 }