Files
academy2/modules/world/world_map_data.cpp
2021-07-31 03:37:28 +03:00

214 lines
5.1 KiB
C++

#include <cmath>
#include <core/image.h>
#include <core/math/random_number_generator.h>
#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<int>();
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<int>::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<int> 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<int>();
}
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<int> &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<RandomNumberGenerator> 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<Vector2i> 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<Vector2i>::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> image;
HashMap<int, Color> 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<int> 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