rj-action-library/Runtime/Structures/Spatial/Voronoi/Voronoi2D.cs

155 lines
3.4 KiB
C#
Raw Normal View History

2025-10-24 11:38:51 +00:00
using System.Collections;
using System.Collections.Generic;
using Godot;
using System;
using System.Linq;
using TriangleNet.Geometry;
using TriangleNet.Meshing;
using TriangleNet.Voronoi;
using TriangleNet.Topology.DCEL;
namespace Rokojori
{
public class Voronoi2D
{
public class Edge
{
public int start;
public int end;
}
public List<Vector2> cellPoints;
public List<Vector2> boundaryPoints = new List<Vector2>();
public List<Cell> cells = new List<Cell>();
public List<Edge> edges = new List<Edge>();
public class Cell
{
public int index;
public List<int> neighbors = new List<int>();
public List<int> boundaries = new List<int>();
protected Voronoi2D _voronoi2D;
public Cell( Voronoi2D v, int index )
{
_voronoi2D = v;
this.index = index;
}
public List<Vector2> GetBoundaryPositions()
{
return boundaries.Map( b => _voronoi2D.boundaryPoints[ b ] );
}
}
public static Voronoi2D CreateFromCellPoints( List<Vector2> cellPoints )
{
var polygon = new Polygon();
cellPoints.ForEach( c => polygon.Add( new TriangleNet.Geometry.Vertex( c.X, c.Y ) ) );
var v = new Voronoi2D();
v.cellPoints = cellPoints;
var mesh = (TriangleNet.Mesh) polygon.Triangulate();
var voronoi = new StandardVoronoi( mesh );
var boundaryMap = new Dictionary<int,Vector2>();
var edgesMap = new Dictionary<string,Edge>();
foreach ( var face in voronoi.Faces )
{
var site = face.generator;
var cell = new Cell( v, site.ID );
var edge = face.edge;
if ( edge == null )
{
continue;
}
var start = edge;
AddEdge( cell, start, boundaryMap, edgesMap );
var it = start.next;
while ( it != start && it != null )
{
AddEdge( cell, it, boundaryMap, edgesMap );
it = it.next;
}
v.cells.Add( cell );
}
for ( int i = 0; i < boundaryMap.Count; i++ )
{
if ( ! boundaryMap.ContainsKey( i ) )
{
RJLog.Log( "Invalid index for boundary", i );
continue;
}
v.boundaryPoints.Add( boundaryMap[ i ] );
}
foreach ( var edge in edgesMap )
{
v.edges.Add( edge.Value );
}
return v;
}
static void AddEdge( Cell cell, HalfEdge halfEdge, Dictionary<int,Vector2> boundaryMap, Dictionary<string,Edge> edgeMap )
{
if ( halfEdge == null )
{
return;
}
if ( ! boundaryMap.ContainsKey( halfEdge.origin.id ) )
{
boundaryMap[ halfEdge.origin.id ] = new Vector2( (float) halfEdge.origin.x, (float) halfEdge.origin.y );
}
var end = halfEdge.next;
if ( end != null )
{
int[] ids = [ halfEdge.origin.id, halfEdge.next.origin.id ];
if ( ids[ 0 ] > ids[ 1 ] )
{
var b = ids[ 0 ];
ids[ 0 ] = ids[ 1 ];
ids[ 1 ] = b;
}
var edgeID = ids[ 0 ] + " " + ids[ 1 ];
if ( ! edgeMap.ContainsKey( edgeID ) )
{
var edge = new Edge();
edge.start = halfEdge.origin.id;
edge.end = halfEdge.next.origin.id;
edgeMap[ edgeID ] = edge;
}
}
cell.neighbors.Add( halfEdge.Twin.id );
cell.boundaries.Add( halfEdge.origin.id );
}
}
}