├── .gitignore ├── register_types.h ├── .gitmodules ├── config.py ├── SCsub ├── register_types.cpp ├── .vscode ├── settings.json └── c_cpp_properties.json ├── LICENSE ├── README.md ├── voronoi.h └── voronoi.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.obj 2 | *.pyc 3 | *.o 4 | -------------------------------------------------------------------------------- /register_types.h: -------------------------------------------------------------------------------- 1 | void register_voronoi_types(); 2 | void unregister_voronoi_types(); 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib"] 2 | path = lib 3 | url = https://github.com/JCash/voronoi 4 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | 2 | def can_build(env, platform): 3 | return True 4 | 5 | 6 | def configure(env): 7 | pass 8 | -------------------------------------------------------------------------------- /SCsub: -------------------------------------------------------------------------------- 1 | Import('env') 2 | 3 | module_env = env.Clone() 4 | module_env.add_source_files(env.modules_sources, "*.cpp") 5 | if env.msvc: 6 | module_env.Append(CXXFLAGS=['/std:c++17']) 7 | else: 8 | module_env.Append(CXXFLAGS=['-std=c++17']) 9 | -------------------------------------------------------------------------------- /register_types.cpp: -------------------------------------------------------------------------------- 1 | #include "register_types.h" 2 | #include "voronoi.h" 3 | 4 | void register_voronoi_types() { 5 | 6 | ClassDB::register_class(); 7 | ClassDB::register_class(); 8 | ClassDB::register_class(); 9 | ClassDB::register_class(); 10 | 11 | } 12 | 13 | void unregister_voronoi_types() { 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "map": "cpp", 4 | "xmemory0": "cpp", 5 | "xtree": "cpp", 6 | "type_traits": "cpp", 7 | "algorithm": "cpp", 8 | "cstddef": "cpp", 9 | "cstdint": "cpp", 10 | "cstdio": "cpp", 11 | "cstdlib": "cpp", 12 | "cstring": "cpp", 13 | "cwchar": "cpp", 14 | "exception": "cpp", 15 | "initializer_list": "cpp", 16 | "limits": "cpp", 17 | "new": "cpp", 18 | "tuple": "cpp", 19 | "utility": "cpp", 20 | "vector": "cpp", 21 | "xmemory": "cpp", 22 | "xstddef": "cpp", 23 | "xtr1common": "cpp", 24 | "xutility": "cpp" 25 | } 26 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "${workspaceFolder}", 7 | "${workspaceFolder}/../../", 8 | "${workspaceFolder}/../../platform/windows" 9 | ], 10 | "defines": [ 11 | "_DEBUG", 12 | "UNICODE", 13 | "_UNICODE" 14 | ], 15 | "windowsSdkVersion": "10.0.17763.0", 16 | "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.21.27702/bin/Hostx64/x64/cl.exe", 17 | "cStandard": "c11", 18 | "cppStandard": "c++17", 19 | "intelliSenseMode": "msvc-x64" 20 | } 21 | ], 22 | "version": 4 23 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Christian Füller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Voronoi Godot integration 2 | ========================= 3 | 4 | Integration of [JCash's Voronoi implementation](https://github.com/JCash/voronoi) as a Godot V3.1.1 module. 5 | 6 | Install 7 | ------- 8 | 9 | Download the contents of this git repository into Godot's module folder and re-build Godot. 10 | The module folder needs to be named `voronoi`, otherwise Godot won't compile properly. 11 | 12 | `git clone https://github.com/rakai93/godot_voronoi.git voronoi --recurse-submodules` 13 | 14 | Example usage 15 | ----------------- 16 | 17 | ```gdscript 18 | # Create voronoi generator 19 | var generator = Voronoi.new() 20 | generator.set_points(list_of_vector2) 21 | # optional: set boundaries for diagram, otherwise boundaries are computed based on points 22 | generator.set_boundaries(rect2_bounds) 23 | # optional: relax points N times, resulting in more equal sites 24 | generator.relax_points(2) 25 | 26 | # Generate diagram 27 | var diagram = generator.generate_diagram() 28 | 29 | # Iterate over sites 30 | for site in diagram.sites(): 31 | draw_circle(site.center()) 32 | 33 | # Iterate over edges 34 | for edge in diagram.edges(): 35 | draw_line(edge.start(), edge.end()) 36 | ``` 37 | -------------------------------------------------------------------------------- /voronoi.h: -------------------------------------------------------------------------------- 1 | #ifndef VORONOI_H 2 | #define VORONOI_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "lib/src/jc_voronoi.h" 17 | 18 | namespace voronoi_detail { 19 | 20 | template 21 | struct GodotAllocator { 22 | using value_type = T; 23 | 24 | constexpr GodotAllocator() noexcept {} 25 | 26 | template 27 | GodotAllocator(const GodotAllocator&) noexcept {} 28 | 29 | template 30 | bool operator==(const GodotAllocator&) const noexcept { 31 | return true; 32 | } 33 | 34 | template 35 | bool operator!=(const GodotAllocator&) const noexcept { 36 | return false; 37 | } 38 | 39 | inline T* allocate(size_t n) const { 40 | return reinterpret_cast(memalloc(sizeof(T) * n)); 41 | } 42 | 43 | inline void deallocate(T* ptr, size_t) const noexcept { 44 | memfree(ptr); 45 | } 46 | }; 47 | 48 | template 49 | using map = std::map, GodotAllocator>>; 50 | 51 | template 52 | using vector = std::vector>; 53 | 54 | } // namespace voronoi_detail 55 | 56 | class VoronoiEdge; 57 | class VoronoiSite; 58 | class VoronoiDiagram; 59 | 60 | class VoronoiEdge : public Object { 61 | GDCLASS(VoronoiEdge, Object) 62 | 63 | public: 64 | const jcv_edge* _edge; 65 | const VoronoiDiagram* _diagram; 66 | 67 | VoronoiEdge() = default; 68 | inline VoronoiEdge(const jcv_edge* edge, const VoronoiDiagram* diagram) 69 | : _edge(edge), _diagram(diagram) { 70 | } 71 | 72 | ~VoronoiEdge() = default; 73 | 74 | Vector sites() const; 75 | Vector2 start() const; 76 | Vector2 end() const; 77 | 78 | protected: 79 | static void _bind_methods(); 80 | }; 81 | 82 | class VoronoiSite : public Object { 83 | GDCLASS(VoronoiSite, Object) 84 | 85 | public: 86 | const jcv_site* _site; 87 | const VoronoiDiagram* _diagram; 88 | 89 | VoronoiSite() = default; 90 | inline VoronoiSite(const jcv_site* site, const VoronoiDiagram* diagram) 91 | : _site(site), _diagram(diagram) { 92 | } 93 | 94 | ~VoronoiSite() = default; 95 | 96 | int index() const; 97 | Vector2 center() const; 98 | Vector edges() const; 99 | Vector neighbors() const; 100 | 101 | protected: 102 | static void _bind_methods(); 103 | }; 104 | 105 | class VoronoiDiagram : public Reference { 106 | GDCLASS(VoronoiDiagram, Reference) 107 | 108 | public: 109 | jcv_diagram _diagram; 110 | 111 | voronoi_detail::vector _edges; 112 | voronoi_detail::vector _sites; 113 | 114 | voronoi_detail::map _edges_by_address; 115 | voronoi_detail::map _sites_by_index; 116 | 117 | VoronoiDiagram(); 118 | ~VoronoiDiagram(); 119 | 120 | void build_objects(); 121 | 122 | Vector edges() const; 123 | Vector sites() const; 124 | 125 | protected: 126 | static void _bind_methods(); 127 | }; 128 | 129 | class Voronoi : public Reference { 130 | GDCLASS(Voronoi, Reference) 131 | 132 | jcv_rect _boundaries; 133 | bool _has_boundaries; 134 | voronoi_detail::vector _points; 135 | 136 | public: 137 | Voronoi() = default; 138 | ~Voronoi() = default; 139 | 140 | void set_points(Vector points); 141 | void set_boundaries(Rect2 boundaries); 142 | void relax_points(int iterations); 143 | Ref generate_diagram() const; 144 | 145 | protected: 146 | static void _bind_methods(); 147 | 148 | }; 149 | 150 | #endif // VORONOI_H 151 | -------------------------------------------------------------------------------- /voronoi.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define JC_VORONOI_IMPLEMENTATION 5 | #include "voronoi.h" 6 | 7 | Vector VoronoiEdge::sites() const { 8 | Vector result; 9 | result.push_back(_diagram->_sites_by_index.at(_edge->sites[0]->index)); 10 | if (_edge->sites[1] != nullptr) 11 | result.push_back(_diagram->_sites_by_index.at(_edge->sites[1]->index)); 12 | return result; 13 | } 14 | 15 | Vector2 VoronoiEdge::start() const { 16 | return Vector2(_edge->pos[0].x, _edge->pos[0].y); 17 | } 18 | 19 | Vector2 VoronoiEdge::end() const { 20 | return Vector2(_edge->pos[1].x, _edge->pos[1].y); 21 | } 22 | 23 | void VoronoiEdge::_bind_methods() { 24 | ClassDB::bind_method(D_METHOD("sites"), &VoronoiEdge::sites); 25 | ClassDB::bind_method(D_METHOD("start"), &VoronoiEdge::start); 26 | ClassDB::bind_method(D_METHOD("end"), &VoronoiEdge::end); 27 | } 28 | 29 | int VoronoiSite::index() const { 30 | return _site->index; 31 | } 32 | 33 | Vector2 VoronoiSite::center() const { 34 | return Vector2(_site->p.x, _site->p.y); 35 | } 36 | 37 | Vector VoronoiSite::edges() const { 38 | Vector result; 39 | const jcv_graphedge* graphedge = _site->edges; 40 | while (graphedge) { 41 | result.push_back(_diagram->_edges_by_address.at( 42 | reinterpret_cast(graphedge->edge) 43 | )); 44 | graphedge = graphedge->next; 45 | } 46 | return result; 47 | } 48 | 49 | Vector VoronoiSite::neighbors() const { 50 | Vector result; 51 | const jcv_graphedge* graphedge = _site->edges; 52 | while (graphedge) { 53 | if (graphedge->neighbor) 54 | result.push_back(_diagram->_sites_by_index.at(graphedge->neighbor->index)); 55 | graphedge = graphedge->next; 56 | } 57 | return result; 58 | } 59 | 60 | void VoronoiSite::_bind_methods() { 61 | ClassDB::bind_method(D_METHOD("index"), &VoronoiSite::index); 62 | ClassDB::bind_method(D_METHOD("center"), &VoronoiSite::center); 63 | ClassDB::bind_method(D_METHOD("edges"), &VoronoiSite::edges); 64 | ClassDB::bind_method(D_METHOD("neighbors"), &VoronoiSite::neighbors); 65 | } 66 | 67 | VoronoiDiagram::VoronoiDiagram() 68 | : _diagram() { 69 | memset(&_diagram, 0, sizeof(jcv_diagram)); 70 | } 71 | 72 | VoronoiDiagram::~VoronoiDiagram() { 73 | for (auto edge : _edges) 74 | memdelete(static_cast(edge)); 75 | for (auto site : _sites) 76 | memdelete(static_cast(site)); 77 | 78 | jcv_diagram_free(&_diagram); 79 | } 80 | 81 | Vector VoronoiDiagram::edges() const { 82 | Vector result; 83 | for (auto edge : _edges) 84 | result.push_back(edge); 85 | return result; 86 | } 87 | 88 | Vector VoronoiDiagram::sites() const { 89 | Vector result; 90 | for (auto site : _sites) 91 | result.push_back(site); 92 | return result; 93 | } 94 | 95 | void VoronoiDiagram::build_objects() { 96 | voronoi_detail::vector gd_edges; 97 | const jcv_edge* edge = jcv_diagram_get_edges(&_diagram); 98 | while (edge) { 99 | // apparent bug in jcv, egdes where start = end are reported as 100 | // diagram edges, but do not exist when iterating over sites 101 | if (edge->pos[0].x != edge->pos[1].x || edge->pos[0].y != edge->pos[1].y) { 102 | VoronoiEdge* gd_edge = memnew(VoronoiEdge(edge, this)); 103 | gd_edges.push_back(gd_edge); 104 | _edges_by_address[reinterpret_cast(edge)] = gd_edge; 105 | } 106 | edge = edge->next; 107 | } 108 | _edges.swap(gd_edges); 109 | 110 | voronoi_detail::vector gd_sites; 111 | const jcv_site* sites = jcv_diagram_get_sites(&_diagram); 112 | for (int i = 0; i < _diagram.numsites; i++) { 113 | VoronoiSite* gd_site = memnew(VoronoiSite(&sites[i], this)); 114 | gd_sites.push_back(gd_site); 115 | _sites_by_index[sites[i].index] = gd_site; 116 | } 117 | _sites.swap(gd_sites); 118 | } 119 | 120 | void VoronoiDiagram::_bind_methods() { 121 | ClassDB::bind_method(D_METHOD("edges"), &VoronoiDiagram::edges); 122 | ClassDB::bind_method(D_METHOD("sites"), &VoronoiDiagram::sites); 123 | } 124 | 125 | void Voronoi::set_points(Vector points) { 126 | assert(points.size()); 127 | 128 | // translate Godot Vector2 points into jcv_points 129 | voronoi_detail::vector new_points; 130 | for (int i = 0; i < points.size(); i++) 131 | new_points.push_back({ points[i].x, points[i].y }); 132 | 133 | _points.swap(new_points); 134 | } 135 | 136 | void Voronoi::set_boundaries(Rect2 boundaries) { 137 | _boundaries = jcv_rect { 138 | jcv_point { boundaries.position.x, boundaries.position.y }, 139 | jcv_point { boundaries.position.x + boundaries.size.x, boundaries.position.y + boundaries.size.y } 140 | }; 141 | _has_boundaries = true; 142 | } 143 | 144 | void* useralloc(void* ctx, size_t size) { 145 | return memalloc(size); 146 | } 147 | 148 | void userfree(void* ctx, void* ptr) { 149 | return memfree(ptr); 150 | } 151 | 152 | void Voronoi::relax_points(int iterations = 1) { 153 | voronoi_detail::vector new_points {_points}; 154 | for (int j = 0; j < iterations; j++) { 155 | jcv_diagram diagram; 156 | memset(&diagram, 0, sizeof(jcv_diagram)); 157 | jcv_diagram_generate_useralloc( 158 | new_points.size(), 159 | new_points.data(), 160 | _has_boundaries ? &_boundaries : NULL, 161 | NULL, 162 | &useralloc, 163 | &userfree, 164 | &diagram 165 | ); 166 | const jcv_site* sites = jcv_diagram_get_sites(&diagram); 167 | const int numsites = diagram.numsites; 168 | new_points.clear(); 169 | for (int i = 0; i < numsites; ++i) { 170 | const jcv_site* site = &sites[i]; 171 | jcv_point sum = site->p; 172 | int count = 1; 173 | 174 | const jcv_graphedge* edge = site->edges; 175 | 176 | while (edge) { 177 | sum.x += edge->pos[0].x; 178 | sum.y += edge->pos[0].y; 179 | ++count; 180 | edge = edge->next; 181 | } 182 | 183 | new_points.push_back({ sum.x / count, sum.y / count }); 184 | } 185 | jcv_diagram_free(&diagram); 186 | } 187 | _points.swap(new_points); 188 | } 189 | 190 | Ref Voronoi::generate_diagram() const { 191 | Ref result { memnew(VoronoiDiagram) }; 192 | jcv_diagram_generate_useralloc( 193 | _points.size(), 194 | _points.data(), 195 | _has_boundaries ? &_boundaries : NULL, 196 | NULL, 197 | &useralloc, 198 | &userfree, 199 | &(result->_diagram) 200 | ); 201 | result->build_objects(); 202 | return result; 203 | } 204 | 205 | void Voronoi::_bind_methods() { 206 | ClassDB::bind_method(D_METHOD("set_points", "points"), &Voronoi::set_points); 207 | ClassDB::bind_method(D_METHOD("set_boundaries", "boundaries"), &Voronoi::set_boundaries); 208 | ClassDB::bind_method(D_METHOD("relax_points", "iterations"), &Voronoi::relax_points); 209 | ClassDB::bind_method(D_METHOD("generate_diagram"), &Voronoi::generate_diagram); 210 | } 211 | --------------------------------------------------------------------------------