Browse Source

even more fixes for points on lines, fixed hangup in tangent search

Kajetan Johannes Hammerle 3 years ago
parent
commit
f50a30d646
3 changed files with 290 additions and 108 deletions
  1. 223 84
      Game.cpp
  2. 48 18
      Game.h
  3. 19 6
      Main.cpp

+ 223 - 84
Game.cpp

@@ -6,60 +6,92 @@
 #include "Game.h"
 #include "utils/Random.h"
 
-bool Game::Point::operator<(const Point& other) {
-    if(std::abs(x - other.x) < 0.0001f) {
+constexpr float EPS = 0.00000001f;
+
+bool compare(float a, float b) {
+    return std::abs(a - b) < EPS;
+}
+
+bool Point::operator<(const Point& other) const {
+    if(x == other.x) {
         return y < other.y;
     }
     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";
+float Point::distance(const Point& other) const {
+    return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y);
+}
 
-    Random r(6476);
-    for(int i = 0; i < 25; i++) {
-        data.push_back({r.nextFloat() * 1.9f - 0.95f, r.nextFloat() * 1.9f - 0.95f});
-        //float f = r.nextFloat() * 1.9f - 0.95f;
-        //data.push_back({f * 0.8f, f});
-        //data.push_back({r.nextFloat() * 1.9f - 0.95f, 0});
-        //data.push_back({0, r.nextFloat() * 1.9f - 0.95f});
+Game::Game(Keys& keys, const char* file, bool steps) : keys(keys), keyNext(keys.add(GLFW_KEY_N)), active(0),
+state(SPLIT) {
+    if(file == nullptr) {
+        Random r;
+        int amount = steps ? 50 : 1000000;
+        for(int i = 0; i < amount; i++) {
+            data.push_back({r.nextFloat(), r.nextFloat()});
+        }
+    } else {
     }
 
-    std::cout << data.size() << "\n";
+    if(data.size() > 0) {
+        minX = data[0].x;
+        maxX = data[0].x;
+        minY = data[0].y;
+        maxY = data[0].y;
+        for(const Point& p : data) {
+            if(p.x < minX) {
+                minX = p.x;
+            }
+            if(p.x > maxX) {
+                maxX = p.x;
+            }
+            if(p.y < minY) {
+                minY = p.y;
+            }
+            if(p.y > maxY) {
+                maxY = p.y;
+            }
+        }
+    }
 
-    split();
+    if(!steps) {
+        skip();
+    }
 }
 
-void Game::merge() {
+bool 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;
+        if(groups[1 - active].begin() == groups[1 - active].end() ||
+                groups[1 - active].begin() + 1 == groups[1 - active].end()) {
+            return false;
         }
         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;
+        return merge();
     }
 
-    std::vector<Point> hullA = groups[active].front();
+    hullA = groups[active].front();
     groups[active].pop_front();
-    std::vector<Point> hullB = groups[active].front();
+    hullB = groups[active].front();
     groups[active].pop_front();
 
-    std::vector<Point> mergedHull;
-    mergedHull.reserve(hullA.size() + hullB.size());
+    lowerIndexA = findMaxX(hullA);
+    lowerIndexB = findMinX(hullB);
 
-    int lowerIndexA;
-    int lowerIndexB;
-    findLowerTangent(hullA, hullB, lowerIndexA, lowerIndexB);
+    upperIndexA = lowerIndexA;
+    upperIndexB = lowerIndexB;
+    return true;
+}
 
-    int upperIndexA;
-    int upperIndexB;
-    findUpperTangent(hullA, hullB, upperIndexA, upperIndexB);
+void Game::finalizeMerge() {
+    std::vector<Point> mergedHull;
+    mergedHull.reserve(hullA.size() + hullB.size());
 
     while(lowerIndexA != upperIndexA) {
         mergedHull.push_back(hullA[lowerIndexA]);
@@ -83,23 +115,23 @@ void Game::split() {
     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) {
-            if(std::abs(v[0].y - v[1].y) > 0.0001f || std::abs(v[1].y - v[2].y) > 0.0001f) {
-                v[0].x = (std::abs(v[0].x) < 0.0001f) ? 0.001f : v[0].x * 1.001f;
-            } else {
-                v[0].y = (std::abs(v[0].y) < 0.0001f) ? 0.001f : v[0].y * 1.001f;
-            }
-            f = det(groups[active].back()[0], groups[active].back()[1], groups[active].back()[2]);
-            if(f > 0.0f) {
-                std::swap(groups[active].back()[1], groups[active].back()[2]);
-            }
+        Point& a = data[index];
+        Point& b = data[index + 1];
+        Point& c = data[index + 2];
+        float f = det(a, b, c);
+        if(compare(f, 0.0f)) {
+            v.push_back({std::min(a.x, std::min(b.x, c.x)), std::min(a.y, std::min(b.y, c.y))});
+            v.push_back({std::max(a.x, std::max(b.x, c.x)), std::max(a.y, std::max(b.y, c.y))});
         } else if(f > 0.0f) {
-            std::swap(groups[active].back()[1], groups[active].back()[2]);
+            v.push_back(a);
+            v.push_back(c);
+            v.push_back(b);
+        } else {
+            v.push_back(a);
+            v.push_back(b);
+            v.push_back(c);
         }
+        index += 3;
     }
     int diff = data.size() - index;
     if(diff > 0) {
@@ -111,51 +143,76 @@ void Game::split() {
     }
 }
 
-bool Game::isLowerTangent(const Point& a, const Point& b, const std::vector<Point>& hull) const {
+bool Game::isLowerTangent(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.00001f) {
+        float f = det(hullA[lowerIndexA], hullB[lowerIndexB], p);
+        if(f < -EPS) {
             return false;
         }
     }
     return true;
 }
 
-bool Game::isUpperTangent(const Point& a, const Point& b, const std::vector<Point>& hull) const {
+bool Game::isUpperTangent(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.00001f) {
+        float f = det(hullA[upperIndexA], hullB[upperIndexB], p);
+        if(f > EPS) {
             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();
+bool Game::findLowerTangent() {
+    if(!isLowerTangent(hullA)) {
+        lowerIndexA = (lowerIndexA + 1) % hullA.size();
+    } else if(!isLowerTangent(hullB)) {
+        lowerIndexB = (lowerIndexB == 0) ? hullB.size() - 1 : lowerIndexB - 1;
+    }
+    if(!isLowerTangent(hullA) || !isLowerTangent(hullB)) {
+        return true;
+    }
+    for(int i = 0; i < static_cast<int> (hullA.size()); i++) {
+        if(i != lowerIndexA && compare(det(hullA[lowerIndexA], hullB[lowerIndexB], hullA[i]), 0.0f)) {
+            if(hullB[lowerIndexB].distance(hullA[i]) > hullB[lowerIndexB].distance(hullA[lowerIndexA])) {
+                lowerIndexA = i;
+            }
         }
-        while(!isLowerTangent(hullA[a], hullB[b], hullB)) {
-            b = (b == 0) ? hullB.size() - 1 : b - 1;
+    }
+    for(int i = 0; i < static_cast<int> (hullB.size()); i++) {
+        if(i != lowerIndexB && compare(det(hullA[lowerIndexA], hullB[lowerIndexB], hullB[i]), 0.0f)) {
+            if(hullA[lowerIndexA].distance(hullB[i]) > hullA[lowerIndexA].distance(hullB[lowerIndexB])) {
+                lowerIndexB = i;
+            }
         }
     }
+    return false;
 }
 
-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;
+bool Game::findUpperTangent() {
+    if(!isUpperTangent(hullA)) {
+        upperIndexA = (upperIndexA == 0) ? hullA.size() - 1 : upperIndexA - 1;
+    } else if(!isUpperTangent(hullB)) {
+        upperIndexB = (upperIndexB + 1) % hullB.size();
+    }
+    if(!isUpperTangent(hullA) || !isUpperTangent(hullB)) {
+        return true;
+    }
+    for(int i = 0; i < static_cast<int> (hullA.size()); i++) {
+        if(i != upperIndexA && compare(det(hullA[upperIndexA], hullB[upperIndexB], hullA[i]), 0.0f)) {
+            if(hullB[upperIndexB].distance(hullA[i]) > hullB[upperIndexB].distance(hullA[upperIndexA])) {
+                upperIndexA = i;
+            }
         }
-        while(!isUpperTangent(hullA[a], hullB[b], hullB)) {
-            b = (b + 1) % hullB.size();
+    }
+    for(int i = 0; i < static_cast<int> (hullB.size()); i++) {
+        if(i != upperIndexB && compare(det(hullA[upperIndexA], hullB[upperIndexB], hullB[i]), 0.0f)) {
+            if(hullA[upperIndexA].distance(hullB[i]) > hullA[upperIndexA].distance(hullB[upperIndexB])) {
+                upperIndexB = i;
+            }
         }
     }
+    return false;
 }
 
 int Game::findMaxX(const std::vector<Point>& hull) const {
@@ -186,10 +243,46 @@ int Game::findMinX(const std::vector<Point>& hull) const {
 
 void Game::tick() {
     if(keys.getDownTime(keyNext) == 1) {
-        merge();
+        next();
     }
-    if(keys.getDownTime(keyNext) >= 40) {
-        merge();
+    if(keys.getDownTime(keyNext) >= 60) {
+        for(int i = 0; i < 100; i++) {
+            next();
+        }
+    }
+}
+
+void Game::next() {
+    switch(state) {
+        case SPLIT:
+            split();
+            state = MERGE;
+            break;
+        case MERGE:
+            if(merge()) {
+                state = FIND_LOWER_TANGENT;
+            } else {
+                state = END;
+                hullA.clear();
+                hullB.clear();
+            }
+            break;
+        case FIND_LOWER_TANGENT:
+            if(!findLowerTangent()) {
+                state = FIND_UPPER_TANGENT;
+            }
+            break;
+        case FIND_UPPER_TANGENT:
+            if(!findUpperTangent()) {
+                state = FINALIZE_MERGE;
+            }
+            break;
+        case FINALIZE_MERGE:
+            finalizeMerge();
+            state = MERGE;
+            break;
+        default:
+            break;
     }
 }
 
@@ -197,28 +290,28 @@ void Game::render(float lag, Renderer& renderer) const {
     (void) lag;
     renderer.setPointSize(7);
     for(const Point& p : data) {
-        renderer.drawPoint(p.x, p.y, 0x707070);
+        renderer.drawPoint(normalizeX(p.x), normalizeY(p.y), 0x707070);
     }
 
-    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]);
-            }
+            renderGroup(renderer, group, 0xFFFFFF);
         }
     }
+
+    renderGroup(renderer, hullA, 0xFF00FF);
+    renderGroup(renderer, hullB, 0xFFFF00);
+
+    if(state == FIND_LOWER_TANGENT || state == FIND_UPPER_TANGENT || state == FINALIZE_MERGE) {
+        renderer.drawLine(
+                normalizeX(hullA[lowerIndexA].x), normalizeY(hullA[lowerIndexA].y),
+                normalizeX(hullB[lowerIndexB].x), normalizeY(hullB[lowerIndexB].y),
+                0x00FFFF);
+        renderer.drawLine(
+                normalizeX(hullA[upperIndexA].x), normalizeY(hullA[upperIndexA].y),
+                normalizeX(hullB[upperIndexB].x), normalizeY(hullB[upperIndexB].y),
+                0x00FFFF);
+    }
 }
 
 bool Game::isRunning() const {
@@ -227,4 +320,50 @@ bool Game::isRunning() const {
 
 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);
+}
+
+void Game::renderGroup(Renderer& renderer, const std::vector<Point>& group, uint color) const {
+    static unsigned int colorMap[] = {
+        0xFF0000, 0x00FF00, 0x0000FF
+    };
+    for(int i = 0; i < static_cast<int> (group.size()); i++) {
+        int next = (i + 1) % group.size();
+        renderer.drawLine(
+                normalizeX(group[i].x),
+                normalizeY(group[i].y),
+                normalizeX(group[next].x),
+                normalizeY(group[next].y),
+                color);
+    }
+    for(int i = 0; i < static_cast<int> (group.size()); i++) {
+        renderer.drawPoint(normalizeX(group[i].x), normalizeY(group[i].y), colorMap[i % 3]);
+    }
+}
+
+void Game::skip() {
+    split();
+    while(merge()) {
+        while(findLowerTangent());
+        while(findUpperTangent());
+        finalizeMerge();
+    }
+    state = END;
+    hullA.clear();
+    hullB.clear();
+}
+
+float Game::normalizeX(float f) const {
+    if(compare(maxX, minX)) {
+        return 0.0f;
+    }
+    f = (f - minX) / (maxX - minX);
+    return f * 1.90f - 0.95f;
+}
+
+float Game::normalizeY(float f) const {
+    if(compare(maxY, minY)) {
+        return 0.0f;
+    }
+    f = (f - minY) / (maxY - minY);
+    return f * 1.90f - 0.95f;
 }

+ 48 - 18
Game.h

@@ -7,44 +7,74 @@
 #include "input/Keys.h"
 #include "rendering/Renderer.h"
 
+struct Point {
+    bool operator<(const Point& other) const;
+    float distance(const Point& other) const;
+    
+    float x;
+    float y;
+};
+
 class Game final {
 public:
-    Game(Keys& control);
+    Game(Keys& control, const char* file, bool steps);
 
     void tick();
     void render(float lag, Renderer& renderer) const;
     bool isRunning() const;
 
 private:
-    struct Point {
-        bool operator<(const Point& other);
 
-        float x;
-        float y;
+    enum State {
+        SPLIT, MERGE, FIND_LOWER_TANGENT, FIND_UPPER_TANGENT, FINALIZE_MERGE, END
     };
-    
-    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;
-    
+
+    bool isLowerTangent(const std::vector<Point>& hull) const;
+    bool isUpperTangent(const std::vector<Point>& hull) const;
+
+    bool findLowerTangent();
+    bool findUpperTangent();
+
     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();
+
+    void split();
+    bool merge();
+    void finalizeMerge();
+
+    void next();
+
+    void renderGroup(Renderer& renderer, const std::vector<Point>& hull, uint color) const;
+
+    void skip();
+
+    float normalizeX(float f) const;
+    float normalizeY(float f) const;
 
     Keys& keys;
     int keyNext;
-    
+
     std::vector<Point> data;
     std::vector<Point> convexHull;
-    
+
     int active;
     std::deque<std::vector<Point>> groups[2];
+
+    State state;
+
+    std::vector<Point> hullA;
+    std::vector<Point> hullB;
+    int lowerIndexA;
+    int lowerIndexB;
+    int upperIndexA;
+    int upperIndexB;
+
+    float minX;
+    float maxX;
+    float minY;
+    float maxY;
 };
 
 #endif

+ 19 - 6
Main.cpp

@@ -1,4 +1,5 @@
 #include <iostream>
+#include <cstring>
 
 #include "rendering/GLFW.h"
 #include "rendering/Window.h"
@@ -32,7 +33,19 @@ void initCallbacks(Window& w, Keys& keys) {
     });
 }
 
-int main() {
+int main(int argAmount, const char** args) {
+    const char* file = nullptr;
+    if(argAmount >= 2 && strcmp(args[1], "-r") != 0) {
+        file = args[1];
+    }
+    bool steps = true;
+    for(int i = 1; i < argAmount; i++) {
+        if(strcmp(args[i], "-r") == 0) {
+            steps = false;
+            break;
+        }
+    }
+
     if(GLFW::hasError()) {
         return 0;
     }
@@ -48,11 +61,11 @@ int main() {
     }
 
     Keys keys;
-    Game game(keys);
-    
+    Game game(keys, file, steps);
+
     initCallbacks(window, keys);
     window.show();
-    
+
     GL::disableDepthTesting();
     GL::checkAndPrintError("setup error");
 
@@ -61,12 +74,12 @@ int main() {
     u64 lag = 0;
     while(!window.shouldClose() && game.isRunning()) {
         GL::checkAndPrintError("loop error");
-        
+
         GL::prepareMainFramebuffer();
         shader.use();
         Renderer renderer;
         game.render(static_cast<float> (lag) / nanosPerTick, renderer);
-        
+
         window.swapBuffers();
         u64 currentTime = GLFW::getTimeNanos();
         lag += currentTime - lastTime;