#include #include #include #include "world_map_data.h" static WorldMapData *g_world_map_data = NULL; WorldMapData *WorldMapData::get_singleton() { return g_world_map_data; } void WorldMapData::create_singleton() { g_world_map_data = memnew(WorldMapData); } void WorldMapData::destroy_singleton() { memdelete(g_world_map_data); g_world_map_data = NULL; } void WorldMapData::_bind_methods() { } void WorldMapData::add_circle(int x, int y, float radius) { struct area a; int grid_x_min, grid_x_max, grid_x, grid_y_min, grid_y_max, grid_y; int i; Vector2i grid_pos; a.x = x; a.y = y; a.radius = radius; if (last_area >= areas.size()) areas.resize(areas.size() + alloc_count); areas.write[last_area] = a; grid_x_min = floorf(((float)x - radius) / (float)grid_size); grid_x_max = ceilf(((float)x + radius) / (float)grid_size); grid_y_min = floorf(((float)y - radius) / (float)grid_size); grid_y_max = ceilf(((float)y + radius) / (float)grid_size); for (grid_y = grid_y_min; grid_y < grid_y_max + 1; grid_y++) for (grid_x = grid_x_min; grid_x < grid_x_max + 1; grid_x++) { grid_pos.x = grid_x; grid_pos.y = grid_y; if (!grid.has(grid_pos)) grid[grid_pos] = List(); if (grid[grid_pos].find(last_area) == NULL) { int oid = get_sorted_index(grid[grid_pos], radius); int xsize = grid[grid_pos].size(); if (oid == 0) grid[grid_pos].push_front(last_area); else if (oid >= xsize) grid[grid_pos].push_back(last_area); else { i = 0; List::Element *e = grid[grid_pos].front(); while (i != oid && e) { e = e->next(); i++; } if (e) grid[grid_pos].insert_before(e, last_area); } } } last_area++; } List WorldMapData::get_circles_for_pos(int x, int y) { Vector2i grid_pos; grid_pos.x = x / grid_size; grid_pos.y = y / grid_size; if (grid.has(grid_pos)) return grid[grid_pos]; else return List(); } bool WorldMapData::in_circle(int id, int x, int y) { if (id >= areas.size()) return false; struct area a = areas[id]; int xx = x - a.x; int yy = y - a.y; int rsq = xx * xx + yy * yy; if ((float)rsq < a.radius * a.radius) return true; return false; } int WorldMapData::get_sorted_index(const List &items, float radius) const { int low = 0; int high = items.size(); while (low < high) { int mid = (low + high) >> 1; if (areas[items[mid]].radius < radius) low = mid + 1; else high = mid; } return low; } void WorldMapData::clear() { last_area = 0; alloc_count = 1000; areas.resize(alloc_count); grid.clear(); } WorldMapData::WorldMapData() { grid_size = 100; last_area = 0; alloc_count = 1000; areas.resize(alloc_count); #ifdef WORLD_MAP_TESTS tests_run = false; #endif world_x = 2048; world_y = 2048; seed = 1038; } WorldMapData::~WorldMapData() { } void WorldMapData::build_point_clusters_iteration(Vector2i seed_point, int bound, int count) { Ref rnd; rnd.instance(); rnd->set_seed(seed); int mind = MIN(bound / count / 4, 1); int rmax = bound / mind; if (rmax == 0) return; float radius = (float)bound / (float)count; float radiussq = radius * radius; List points; int n = 0; int max_steps = 10000; while (n < count && max_steps-- > 0) { int x = seed_point.x + rnd->randi() % rmax; int y = seed_point.y + rnd->randi() % rmax; if (x >= world_x || y >= world_y) continue; bool good = true; List::Element *e = points.front(); while (e) { Vector2i p = e->get(); int dx = x - p.x; int dy = y - p.y; if (dx * dx + dy * dy < (int)radiussq) { good = false; break; } e = e->next(); } if (good) { add_circle(x, y, radius); points.push_back(Vector2i(x, y)); n++; } } } void WorldMapData::save_debug_image() { int i, j; Ref image; HashMap colors; image.instance(); image->create(2048, 2048, false, Image::FORMAT_RGB8); image->lock(); Color newcolor(0.1f, 0.2f, 1.0f); printf("world %d %d\n", world_x, world_y); for (i = 0; i < image->get_width(); i++) for (j = 0; j < image->get_height(); j++) { List data = get_circles_for_pos(i * world_x / 2048, j * world_y / 2048); while (data.size() > 0) { int id = data.front()->get(); if (in_circle(id, i * world_x / 2048, j * world_y / 2048)) { if (!colors.has(id)) { colors[id] = newcolor; newcolor.r = (newcolor.r + fmodf(1321.27f * (float)i, 13.0f) / 13.0f) / 1.9f + 0.1f; newcolor.g = (newcolor.r + newcolor.g + fmodf(5321.23f * (float)i, 10.0f) / 10.0f) / 2.9f + 0.1f; newcolor.b = (newcolor.r + newcolor.g + newcolor.b + fmodf(1121.13f * (float)i, 11.0f) / 11.0f) / 3.9f + 0.1f; } image->set_pixel(i, j, colors[id]); break; } data.pop_front(); } } image->unlock(); image->save_png("world_map_test.png"); } #ifdef WORLD_MAP_TESTS void WorldMapData::unit_test() { int i; add_circle(100, 100, 50.0f); add_circle(200, 200, 100.0f); add_circle(100, 300, 30.0f); add_circle(300, 100, 30.0f); for (i = 0; i < 1000; i++) { int x = (i * 100) % 1000; int y = (i * 100) / 1000; add_circle(x, y, 90.0f); } save_debug_image(); } #endif