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

220 lines
5.5 KiB
C++

#include "voronoi.h"
#define JC_VORONOI_IMPLEMENTATION
#include "thirdparty/voronoi/src/jc_voronoi.h"
#include <cstdio>
Voronoi *Voronoi::get_singleton()
{
static Voronoi *mo = NULL;
if (!mo)
mo = memnew(Voronoi);
return mo;
}
static inline Vector2 fill_point(const jcv_point *p)
{
Vector2 point(p->x, p->y);
return point;
}
static inline Dictionary fill_graphedge(const jcv_graphedge *hedge)
{
Dictionary ret;
printf("neighbor: %p\n", hedge->neighbor);
if (hedge->neighbor)
ret["neighbor"] = hedge->neighbor->index;
else
ret["neighbor"] = -1;
ret["a"] = fill_point(&hedge->pos[0]);
ret["b"] = fill_point(&hedge->pos[1]);
return ret;
}
static inline void sort_site(Dictionary &site)
{
struct polygon {
Vector2 center;
_FORCE_INLINE_ bool operator()(const Vector2 &a, const Vector2 &b) const {
float a1 = (a - center).angle();
float a2 = (a - center).angle();
if (a1 < 0)
a1 += 2.0 * Math_PI;
if (a2 < 0)
a2 += 2.0 * Math_PI;
return (a1 < a2);
}
void prep(const Vector<Vector2> &data)
{
int i;
if (data.size() > 0) {
for (i = 0; i < data.size(); i++)
center += data[i];
center /= (float)data.size();
}
}
};
Vector<Vector2> region_data;
Vector<Vector2> vertices = site["vertices"].duplicate();
SortArray<Vector2, struct polygon> sorter;
sorter.compare.center = site["pos"];
sorter.compare.prep(vertices);
sorter.sort(vertices.ptrw(), vertices.size());
site["polygon"] = vertices;
}
static inline int vertices_get_index(const Vector<Vector2> &vertices, const Vector2 &pt)
{
const Vector2 *ptr = vertices.ptr();
int i;
for (i = 0; i < vertices.size(); i++) {
if (ptr[i].distance_squared_to(pt) < 0.1f * 0.1f)
return i;
}
return -1;
}
static inline int vertices_get_index(const Array &vertices, const Vector2 &pt)
{
int i;
for (i = 0; i < vertices.size(); i++) {
const Vector2 &v = vertices[i];
if (v.distance_squared_to(pt) < 0.1f * 0.1f)
return i;
}
return -1;
}
static inline int vertices_get_index(const PoolVector<Vector2> &vertices, const Vector2 &pt)
{
const Vector2 *ptr = vertices.read().ptr();
int i;
for (i = 0; i < vertices.size(); i++) {
if (ptr[i].distance_squared_to(pt) < 0.1f * 0.1f)
return i;
}
return -1;
}
static inline Dictionary fill_diagram_dict(const jcv_diagram *diagram)
{
Dictionary ret;
HashMap<uint64_t, int> edge_data;
int point_id_count = 0, edge_id_count = 0, i;
const jcv_edge *edge = jcv_diagram_get_edges(diagram);
Dictionary ret_edges;
while(edge) {
int id;
if (jcv_point_eq(&edge->pos[0], &edge->pos[1])) {
edge = edge->next;
continue;
}
Dictionary e;
if (edge_data.has((uint64_t)edge))
id = edge_data[(uint64_t)edge];
else {
id = edge_id_count++;
edge_data[(uint64_t)edge] = id;
}
Vector2 p1 = fill_point(&edge->pos[0]);
Vector2 p2 = fill_point(&edge->pos[1]);
e["a"] = p1;
e["b"] = p2;
e["id"] = id;
ret_edges[id] = e;
edge = edge->next;
}
Array sites;
for (i = 0; i < diagram->numsites; i++) {
const jcv_site* dsites = jcv_diagram_get_sites(diagram);
const jcv_site *site = &dsites[i];
jcv_graphedge *hedge = site->edges;
Array ges;
Vector<Vector2> vertices;
Dictionary s;
while (hedge) {
Dictionary ge = fill_graphedge(hedge);
ge["edge"] = edge_data[(uint64_t)hedge->edge];
if (vertices_get_index(vertices, ge["a"]) < 0)
vertices.push_back(ge["a"]);
if (vertices_get_index(vertices, ge["b"]) < 0)
vertices.push_back(ge["b"]);
ge["a"] = vertices_get_index(vertices, ge["a"]);
ge["b"] = vertices_get_index(vertices, ge["b"]);
ges.push_back(ge);
hedge = hedge->next;
}
s["index"] = site->index;
s["pos"] = fill_point(&site->p);
s["graphedges"] = ges;
s["vertices"] = vertices;
sort_site(s);
sites.push_back(s);
}
ret["edges"] = ret_edges;
ret["sites"] = sites;
return ret;
}
Dictionary Voronoi::generate_diagram(const PoolVector<Vector2> &points, int relaxations)
{
int i;
printf("alive 1\n");
jcv_diagram diagram;
Dictionary ret;
Vector<jcv_point> _points;
PoolVector<Vector2> ret_points;
_points.resize(points.size());
ret_points.resize(points.size());
for (i = 0; i < points.size(); i++) {
_points.write[i].x = points.read()[i].x;
_points.write[i].y = points.read()[i].y;
}
for (i = 0; i < relaxations; i++) {
memset(&diagram, 0, sizeof(jcv_diagram));
jcv_diagram_generate(_points.size(), _points.ptr(), NULL, NULL, &diagram);
relax_points(&diagram, _points.ptrw());
jcv_diagram_free(&diagram);
}
for (i = 0; i < points.size(); i++) {
ret_points.write()[i].x = _points[i].x;
ret_points.write()[i].y = _points[i].y;
}
printf("alive 2\n");
memset(&diagram, 0, sizeof(jcv_diagram));
jcv_diagram_generate(_points.size(), _points.ptr(), NULL, NULL, &diagram);
ret = fill_diagram_dict(&diagram);
ret["points"] = ret_points;
jcv_diagram_free(&diagram);
printf("alive 5\n");
return ret;
}
void Voronoi::relax_points(const jcv_diagram* diagram, jcv_point* points)
{
const jcv_site* sites = jcv_diagram_get_sites(diagram);
for( int i = 0; i < diagram->numsites; ++i )
{
const jcv_site* site = &sites[i];
jcv_point sum = site->p;
int count = 1;
const jcv_graphedge* edge = site->edges;
while( edge )
{
sum.x += edge->pos[0].x;
sum.y += edge->pos[0].y;
++count;
edge = edge->next;
}
points[site->index].x = sum.x / count;
points[site->index].y = sum.y / count;
}
}
void Voronoi::_bind_methods()
{
ClassDB::bind_method(D_METHOD("generate_diagram", "points", "relaxations"),
&Voronoi::generate_diagram);
}