#include "voronoi.h" #define JC_VORONOI_IMPLEMENTATION #include "thirdparty/voronoi/src/jc_voronoi.h" #include 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 &data) { int i; if (data.size() > 0) { for (i = 0; i < data.size(); i++) center += data[i]; center /= (float)data.size(); } } }; Vector region_data; Vector vertices = site["vertices"].duplicate(); SortArray 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 &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 &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 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 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 &points, int relaxations) { int i; printf("alive 1\n"); jcv_diagram diagram; Dictionary ret; Vector _points; PoolVector 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); }