|
@@ -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});
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
+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;
|
|
|
}
|