mirror of
https://gitgud.io/AbstractConcept/rimworld-animation-studio.git
synced 2024-08-15 00:43:27 +00:00
Initial commit
This commit is contained in:
commit
3c7cc0c973
8391 changed files with 704313 additions and 0 deletions
|
@ -0,0 +1,183 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="BoundedVoronoi.cs">
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
using Animation.TriangleNet.Tools;
|
||||
using Animation.TriangleNet.Topology.DCEL;
|
||||
|
||||
using HVertex = Animation.TriangleNet.Topology.DCEL.Vertex;
|
||||
using TVertex = Animation.TriangleNet.Geometry.Vertex;
|
||||
|
||||
internal class BoundedVoronoi : VoronoiBase
|
||||
{
|
||||
int offset;
|
||||
|
||||
public BoundedVoronoi(Mesh mesh)
|
||||
: this(mesh, new DefaultVoronoiFactory(), RobustPredicates.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public BoundedVoronoi(Mesh mesh, IVoronoiFactory factory, IPredicates predicates)
|
||||
: base(mesh, factory, predicates, true)
|
||||
{
|
||||
// We explicitly told the base constructor to call the Generate method, so
|
||||
// at this point the basic Voronoi diagram is already created.
|
||||
offset = base.vertices.Count;
|
||||
|
||||
// Each vertex of the hull will be part of a Voronoi cell.
|
||||
base.vertices.Capacity = offset + mesh.hullsize;
|
||||
|
||||
// Create bounded Voronoi diagram.
|
||||
PostProcess();
|
||||
|
||||
ResolveBoundaryEdges();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes edge intersections with mesh boundary edges.
|
||||
/// </summary>
|
||||
private void PostProcess()
|
||||
{
|
||||
foreach (var edge in rays)
|
||||
{
|
||||
var twin = edge.twin;
|
||||
|
||||
var v1 = (TVertex)edge.face.generator;
|
||||
var v2 = (TVertex)twin.face.generator;
|
||||
|
||||
double dir = predicates.CounterClockwise(v1, v2, edge.origin);
|
||||
|
||||
if (dir <= 0)
|
||||
{
|
||||
HandleCase1(edge, v1, v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
HandleCase2(edge, v1, v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Case 1: edge origin lies inside the domain.
|
||||
/// </summary>
|
||||
private void HandleCase1(HalfEdge edge, TVertex v1, TVertex v2)
|
||||
{
|
||||
//int mark = GetBoundaryMark(v1);
|
||||
|
||||
// The infinite vertex.
|
||||
var v = (Point)edge.twin.origin;
|
||||
|
||||
// The half-edge is the bisector of v1 and v2, so the projection onto the
|
||||
// boundary segment is actually its midpoint.
|
||||
v.x = (v1.x + v2.x) / 2.0;
|
||||
v.y = (v1.y + v2.y) / 2.0;
|
||||
|
||||
// Close the cell connected to edge.
|
||||
var gen = factory.CreateVertex(v1.x, v1.y);
|
||||
|
||||
var h1 = factory.CreateHalfEdge(edge.twin.origin, edge.face);
|
||||
var h2 = factory.CreateHalfEdge(gen, edge.face);
|
||||
|
||||
edge.next = h1;
|
||||
h1.next = h2;
|
||||
h2.next = edge.face.edge;
|
||||
|
||||
gen.leaving = h2;
|
||||
|
||||
// Let the face edge point to the edge leaving at generator.
|
||||
edge.face.edge = h2;
|
||||
|
||||
base.edges.Add(h1);
|
||||
base.edges.Add(h2);
|
||||
|
||||
int count = base.edges.Count;
|
||||
|
||||
h1.id = count;
|
||||
h2.id = count + 1;
|
||||
|
||||
gen.id = offset++;
|
||||
base.vertices.Add(gen);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Case 2: edge origin lies outside the domain.
|
||||
/// </summary>
|
||||
private void HandleCase2(HalfEdge edge, TVertex v1, TVertex v2)
|
||||
{
|
||||
// The vertices of the infinite edge.
|
||||
var p1 = (Point)edge.origin;
|
||||
var p2 = (Point)edge.twin.origin;
|
||||
|
||||
// The two edges leaving p1, pointing into the mesh.
|
||||
var e1 = edge.twin.next;
|
||||
var e2 = e1.twin.next;
|
||||
|
||||
// Find the two intersections with boundary edge.
|
||||
IntersectionHelper.IntersectSegments(v1, v2, e1.origin, e1.twin.origin, ref p2);
|
||||
IntersectionHelper.IntersectSegments(v1, v2, e2.origin, e2.twin.origin, ref p1);
|
||||
|
||||
// The infinite edge will now lie on the boundary. Update pointers:
|
||||
e1.twin.next = edge.twin;
|
||||
edge.twin.next = e2;
|
||||
edge.twin.face = e2.face;
|
||||
|
||||
e1.origin = edge.twin.origin;
|
||||
|
||||
edge.twin.twin = null;
|
||||
edge.twin = null;
|
||||
|
||||
// Close the cell.
|
||||
var gen = factory.CreateVertex(v1.x, v1.y);
|
||||
var he = factory.CreateHalfEdge(gen, edge.face);
|
||||
|
||||
edge.next = he;
|
||||
he.next = edge.face.edge;
|
||||
|
||||
// Let the face edge point to the edge leaving at generator.
|
||||
edge.face.edge = he;
|
||||
|
||||
base.edges.Add(he);
|
||||
|
||||
he.id = base.edges.Count;
|
||||
|
||||
gen.id = offset++;
|
||||
base.vertices.Add(gen);
|
||||
}
|
||||
|
||||
/*
|
||||
private int GetBoundaryMark(Vertex v)
|
||||
{
|
||||
Otri tri = default(Otri);
|
||||
Otri next = default(Otri);
|
||||
Osub seg = default(Osub);
|
||||
|
||||
// Get triangle connected to generator.
|
||||
v.tri.Copy(ref tri);
|
||||
v.tri.Copy(ref next);
|
||||
|
||||
// Find boundary triangle.
|
||||
while (next.triangle.id != -1)
|
||||
{
|
||||
next.Copy(ref tri);
|
||||
next.OnextSelf();
|
||||
}
|
||||
|
||||
// Find edge dual to current half-edge.
|
||||
tri.LnextSelf();
|
||||
tri.LnextSelf();
|
||||
|
||||
tri.SegPivot(ref seg);
|
||||
|
||||
return seg.seg.boundary;
|
||||
}
|
||||
//*/
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 76888b2ae85744537a8e1a2e367760bc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,35 @@
|
|||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi
|
||||
{
|
||||
using System;
|
||||
using Animation.TriangleNet.Topology.DCEL;
|
||||
|
||||
/// <summary>
|
||||
/// Default factory for Voronoi / DCEL mesh objects.
|
||||
/// </summary>
|
||||
internal class DefaultVoronoiFactory : IVoronoiFactory
|
||||
{
|
||||
public void Initialize(int vertexCount, int edgeCount, int faceCount)
|
||||
{
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
public Vertex CreateVertex(double x, double y)
|
||||
{
|
||||
return new Vertex(x, y);
|
||||
}
|
||||
|
||||
public HalfEdge CreateHalfEdge(Vertex origin, Face face)
|
||||
{
|
||||
return new HalfEdge(origin, face);
|
||||
}
|
||||
|
||||
public Face CreateFace(Geometry.Vertex vertex)
|
||||
{
|
||||
return new Face(vertex);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 608972c8a79af4e13a2b24b406eacdcb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,18 @@
|
|||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi
|
||||
{
|
||||
using Animation.TriangleNet.Topology.DCEL;
|
||||
|
||||
internal interface IVoronoiFactory
|
||||
{
|
||||
void Initialize(int vertexCount, int edgeCount, int faceCount);
|
||||
|
||||
void Reset();
|
||||
|
||||
Vertex CreateVertex(double x, double y);
|
||||
|
||||
HalfEdge CreateHalfEdge(Vertex origin, Face face);
|
||||
|
||||
Face CreateFace(Geometry.Vertex vertex);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6b45f23bbec334ab6885f2a26888d1a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0266ad8bd8fa944abb6aad512c02e4e6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,693 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="BoundedVoronoi.cs" company="">
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi.Legacy
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Animation.TriangleNet.Topology;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// The Bounded Voronoi Diagram is the dual of a PSLG triangulation.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 2D Centroidal Voronoi Tessellations with Constraints, 2010,
|
||||
/// Jane Tournois, Pierre Alliez and Olivier Devillers
|
||||
/// </remarks>
|
||||
[Obsolete("Use TriangleNet.Voronoi.BoundedVoronoi class instead.")]
|
||||
internal class BoundedVoronoiLegacy : IVoronoi
|
||||
{
|
||||
IPredicates predicates = RobustPredicates.Default;
|
||||
|
||||
Mesh mesh;
|
||||
|
||||
Point[] points;
|
||||
List<VoronoiRegion> regions;
|
||||
|
||||
// Used for new points on segments.
|
||||
List<Point> segPoints;
|
||||
int segIndex;
|
||||
|
||||
Dictionary<int, SubSegment> subsegMap;
|
||||
|
||||
bool includeBoundary = true;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh instance.</param>
|
||||
public BoundedVoronoiLegacy(Mesh mesh)
|
||||
: this(mesh, true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundedVoronoiLegacy" /> class.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Mesh instance.</param>
|
||||
public BoundedVoronoiLegacy(Mesh mesh, bool includeBoundary)
|
||||
{
|
||||
this.mesh = mesh;
|
||||
this.includeBoundary = includeBoundary;
|
||||
|
||||
Generate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Voronoi vertices.
|
||||
/// </summary>
|
||||
public Point[] Points
|
||||
{
|
||||
get { return points; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Voronoi regions.
|
||||
/// </summary>
|
||||
public ICollection<VoronoiRegion> Regions
|
||||
{
|
||||
get { return regions; }
|
||||
}
|
||||
|
||||
public IEnumerable<IEdge> Edges
|
||||
{
|
||||
get { return EnumerateEdges(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the bounded voronoi diagram.
|
||||
/// </summary>
|
||||
private void Generate()
|
||||
{
|
||||
mesh.Renumber();
|
||||
mesh.MakeVertexMap();
|
||||
|
||||
// Allocate space for voronoi diagram
|
||||
this.regions = new List<VoronoiRegion>(mesh.vertices.Count);
|
||||
|
||||
this.points = new Point[mesh.triangles.Count];
|
||||
this.segPoints = new List<Point>(mesh.subsegs.Count * 4);
|
||||
|
||||
ComputeCircumCenters();
|
||||
|
||||
TagBlindTriangles();
|
||||
|
||||
foreach (var v in mesh.vertices.Values)
|
||||
{
|
||||
// TODO: Need a reliable way to check if a vertex is on a segment
|
||||
if (v.type == VertexType.FreeVertex || v.label == 0)
|
||||
{
|
||||
ConstructCell(v);
|
||||
}
|
||||
else if (includeBoundary)
|
||||
{
|
||||
ConstructBoundaryCell(v);
|
||||
}
|
||||
}
|
||||
|
||||
// Add the new points on segments to the point array.
|
||||
int length = points.Length;
|
||||
|
||||
Array.Resize<Point>(ref points, length + segPoints.Count);
|
||||
|
||||
for (int i = 0; i < segPoints.Count; i++)
|
||||
{
|
||||
points[length + i] = segPoints[i];
|
||||
}
|
||||
|
||||
this.segPoints.Clear();
|
||||
this.segPoints = null;
|
||||
}
|
||||
|
||||
private void ComputeCircumCenters()
|
||||
{
|
||||
Otri tri = default(Otri);
|
||||
double xi = 0, eta = 0;
|
||||
Point pt;
|
||||
|
||||
// Compue triangle circumcenters
|
||||
foreach (var item in mesh.triangles)
|
||||
{
|
||||
tri.tri = item;
|
||||
|
||||
pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
|
||||
pt.id = item.id;
|
||||
|
||||
points[item.id] = pt;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tag all blind triangles.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A triangle is said to be blind if the triangle and its circumcenter
|
||||
/// lie on two different sides of a constrained edge.
|
||||
/// </remarks>
|
||||
private void TagBlindTriangles()
|
||||
{
|
||||
int blinded = 0;
|
||||
|
||||
Stack<Triangle> triangles;
|
||||
subsegMap = new Dictionary<int, SubSegment>();
|
||||
|
||||
Otri f = default(Otri);
|
||||
Otri f0 = default(Otri);
|
||||
Osub e = default(Osub);
|
||||
Osub sub1 = default(Osub);
|
||||
|
||||
// Tag all triangles non-blind
|
||||
foreach (var t in mesh.triangles)
|
||||
{
|
||||
// Use the infected flag for 'blinded' attribute.
|
||||
t.infected = false;
|
||||
}
|
||||
|
||||
// for each constrained edge e of cdt do
|
||||
foreach (var ss in mesh.subsegs.Values)
|
||||
{
|
||||
// Create a stack: triangles
|
||||
triangles = new Stack<Triangle>();
|
||||
|
||||
// for both adjacent triangles fe to e tagged non-blind do
|
||||
// Push fe into triangles
|
||||
e.seg = ss;
|
||||
e.orient = 0;
|
||||
e.Pivot(ref f);
|
||||
|
||||
if (f.tri.id != Mesh.DUMMY && !f.tri.infected)
|
||||
{
|
||||
triangles.Push(f.tri);
|
||||
}
|
||||
|
||||
e.Sym();
|
||||
e.Pivot(ref f);
|
||||
|
||||
if (f.tri.id != Mesh.DUMMY && !f.tri.infected)
|
||||
{
|
||||
triangles.Push(f.tri);
|
||||
}
|
||||
|
||||
// while triangles is non-empty
|
||||
while (triangles.Count > 0)
|
||||
{
|
||||
// Pop f from stack triangles
|
||||
f.tri = triangles.Pop();
|
||||
f.orient = 0;
|
||||
|
||||
// if f is blinded by e (use P) then
|
||||
if (TriangleIsBlinded(ref f, ref e))
|
||||
{
|
||||
// Tag f as blinded by e
|
||||
f.tri.infected = true;
|
||||
blinded++;
|
||||
|
||||
// Store association triangle -> subseg
|
||||
subsegMap.Add(f.tri.hash, e.seg);
|
||||
|
||||
// for each adjacent triangle f0 to f do
|
||||
for (f.orient = 0; f.orient < 3; f.orient++)
|
||||
{
|
||||
f.Sym(ref f0);
|
||||
|
||||
f0.Pivot(ref sub1);
|
||||
|
||||
// if f0 is finite and tagged non-blind & the common edge
|
||||
// between f and f0 is unconstrained then
|
||||
if (f0.tri.id != Mesh.DUMMY && !f0.tri.infected && sub1.seg.hash == Mesh.DUMMY)
|
||||
{
|
||||
// Push f0 into triangles.
|
||||
triangles.Push(f0.tri);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
blinded = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if given triangle is blinded by given segment.
|
||||
/// </summary>
|
||||
/// <param name="tri">Triangle.</param>
|
||||
/// <param name="seg">Segments</param>
|
||||
/// <returns>Returns true, if the triangle is blinded.</returns>
|
||||
private bool TriangleIsBlinded(ref Otri tri, ref Osub seg)
|
||||
{
|
||||
Point c, pt;
|
||||
|
||||
Vertex torg = tri.Org();
|
||||
Vertex tdest = tri.Dest();
|
||||
Vertex tapex = tri.Apex();
|
||||
|
||||
Vertex sorg = seg.Org();
|
||||
Vertex sdest = seg.Dest();
|
||||
|
||||
c = this.points[tri.tri.id];
|
||||
|
||||
if (SegmentsIntersect(sorg, sdest, c, torg, out pt, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SegmentsIntersect(sorg, sdest, c, tdest, out pt, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SegmentsIntersect(sorg, sdest, c, tapex, out pt, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void ConstructCell(Vertex vertex)
|
||||
{
|
||||
VoronoiRegion region = new VoronoiRegion(vertex);
|
||||
regions.Add(region);
|
||||
|
||||
Otri f = default(Otri);
|
||||
Otri f_init = default(Otri);
|
||||
Otri f_next = default(Otri);
|
||||
Osub sf = default(Osub);
|
||||
Osub sfn = default(Osub);
|
||||
|
||||
Point cc_f, cc_f_next, p;
|
||||
|
||||
int n = mesh.triangles.Count;
|
||||
|
||||
// Call P the polygon (cell) in construction
|
||||
List<Point> vpoints = new List<Point>();
|
||||
|
||||
// Call f_init a triangle incident to x
|
||||
vertex.tri.Copy(ref f_init);
|
||||
|
||||
if (f_init.Org() != vertex)
|
||||
{
|
||||
throw new Exception("ConstructCell: inconsistent topology.");
|
||||
}
|
||||
|
||||
// Let f be initialized to f_init
|
||||
f_init.Copy(ref f);
|
||||
// Call f_next the next triangle counterclockwise around x
|
||||
f_init.Onext(ref f_next);
|
||||
|
||||
// repeat ... until f = f_init
|
||||
do
|
||||
{
|
||||
// Call Lffnext the line going through the circumcenters of f and f_next
|
||||
cc_f = this.points[f.tri.id];
|
||||
cc_f_next = this.points[f_next.tri.id];
|
||||
|
||||
// if f is tagged non-blind then
|
||||
if (!f.tri.infected)
|
||||
{
|
||||
// Insert the circumcenter of f into P
|
||||
vpoints.Add(cc_f);
|
||||
|
||||
if (f_next.tri.infected)
|
||||
{
|
||||
// Call S_fnext the constrained edge blinding f_next
|
||||
sfn.seg = subsegMap[f_next.tri.hash];
|
||||
|
||||
// Insert point Lf,f_next /\ Sf_next into P
|
||||
if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call Sf the constrained edge blinding f
|
||||
sf.seg = subsegMap[f.tri.hash];
|
||||
|
||||
// if f_next is tagged non-blind then
|
||||
if (!f_next.tri.infected)
|
||||
{
|
||||
// Insert point Lf,f_next /\ Sf into P
|
||||
if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call Sf_next the constrained edge blinding f_next
|
||||
sfn.seg = subsegMap[f_next.tri.hash];
|
||||
|
||||
// if Sf != Sf_next then
|
||||
if (!sf.Equal(sfn))
|
||||
{
|
||||
// Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P
|
||||
if (SegmentsIntersect(sf.Org(), sf.Dest(), cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
|
||||
if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// f <- f_next
|
||||
f_next.Copy(ref f);
|
||||
|
||||
// Call f_next the next triangle counterclockwise around x
|
||||
f_next.Onext();
|
||||
}
|
||||
while (!f.Equals(f_init));
|
||||
|
||||
// Output: Bounded Voronoi cell of x in counterclockwise order.
|
||||
region.Add(vpoints);
|
||||
}
|
||||
|
||||
private void ConstructBoundaryCell(Vertex vertex)
|
||||
{
|
||||
VoronoiRegion region = new VoronoiRegion(vertex);
|
||||
regions.Add(region);
|
||||
|
||||
Otri f = default(Otri);
|
||||
Otri f_init = default(Otri);
|
||||
Otri f_next = default(Otri);
|
||||
Otri f_prev = default(Otri);
|
||||
Osub sf = default(Osub);
|
||||
Osub sfn = default(Osub);
|
||||
|
||||
Vertex torg, tdest, tapex, sorg, sdest;
|
||||
Point cc_f, cc_f_next, p;
|
||||
|
||||
int n = mesh.triangles.Count;
|
||||
|
||||
// Call P the polygon (cell) in construction
|
||||
List<Point> vpoints = new List<Point>();
|
||||
|
||||
// Call f_init a triangle incident to x
|
||||
vertex.tri.Copy(ref f_init);
|
||||
|
||||
if (f_init.Org() != vertex)
|
||||
{
|
||||
throw new Exception("ConstructBoundaryCell: inconsistent topology.");
|
||||
}
|
||||
// Let f be initialized to f_init
|
||||
f_init.Copy(ref f);
|
||||
// Call f_next the next triangle counterclockwise around x
|
||||
f_init.Onext(ref f_next);
|
||||
|
||||
f_init.Oprev(ref f_prev);
|
||||
|
||||
// Is the border to the left?
|
||||
if (f_prev.tri.id != Mesh.DUMMY)
|
||||
{
|
||||
// Go clockwise until we reach the border (or the initial triangle)
|
||||
while (f_prev.tri.id != Mesh.DUMMY && !f_prev.Equals(f_init))
|
||||
{
|
||||
f_prev.Copy(ref f);
|
||||
f_prev.Oprev();
|
||||
}
|
||||
|
||||
f.Copy(ref f_init);
|
||||
f.Onext(ref f_next);
|
||||
}
|
||||
|
||||
if (f_prev.tri.id == Mesh.DUMMY)
|
||||
{
|
||||
// For vertices on the domain boundaray, add the vertex. For
|
||||
// internal boundaries don't add it.
|
||||
p = new Point(vertex.x, vertex.y);
|
||||
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
|
||||
// Add midpoint of start triangles' edge.
|
||||
torg = f.Org();
|
||||
tdest = f.Dest();
|
||||
p = new Point((torg.x + tdest.x) / 2, (torg.y + tdest.y) / 2);
|
||||
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
|
||||
// repeat ... until f = f_init
|
||||
do
|
||||
{
|
||||
// Call Lffnext the line going through the circumcenters of f and f_next
|
||||
cc_f = this.points[f.tri.id];
|
||||
|
||||
if (f_next.tri.id == Mesh.DUMMY)
|
||||
{
|
||||
if (!f.tri.infected)
|
||||
{
|
||||
// Add last circumcenter
|
||||
vpoints.Add(cc_f);
|
||||
}
|
||||
|
||||
// Add midpoint of last triangles' edge (chances are it has already
|
||||
// been added, so post process cell to remove duplicates???)
|
||||
torg = f.Org();
|
||||
tapex = f.Apex();
|
||||
p = new Point((torg.x + tapex.x) / 2, (torg.y + tapex.y) / 2);
|
||||
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
cc_f_next = this.points[f_next.tri.id];
|
||||
|
||||
// if f is tagged non-blind then
|
||||
if (!f.tri.infected)
|
||||
{
|
||||
// Insert the circumcenter of f into P
|
||||
vpoints.Add(cc_f);
|
||||
|
||||
if (f_next.tri.infected)
|
||||
{
|
||||
// Call S_fnext the constrained edge blinding f_next
|
||||
sfn.seg = subsegMap[f_next.tri.hash];
|
||||
|
||||
// Insert point Lf,f_next /\ Sf_next into P
|
||||
if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call Sf the constrained edge blinding f
|
||||
sf.seg = subsegMap[f.tri.hash];
|
||||
|
||||
sorg = sf.Org();
|
||||
sdest = sf.Dest();
|
||||
|
||||
// if f_next is tagged non-blind then
|
||||
if (!f_next.tri.infected)
|
||||
{
|
||||
tdest = f.Dest();
|
||||
tapex = f.Apex();
|
||||
|
||||
// Both circumcenters lie on the blinded side, but we
|
||||
// have to add the intersection with the segment.
|
||||
|
||||
// Center of f edge dest->apex
|
||||
Point bisec = new Point((tdest.x + tapex.x) / 2, (tdest.y + tapex.y) / 2);
|
||||
|
||||
// Find intersection of seg with line through f's bisector and circumcenter
|
||||
if (SegmentsIntersect(sorg, sdest, bisec, cc_f, out p, false))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
|
||||
// Insert point Lf,f_next /\ Sf into P
|
||||
if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Call Sf_next the constrained edge blinding f_next
|
||||
sfn.seg = subsegMap[f_next.tri.hash];
|
||||
|
||||
// if Sf != Sf_next then
|
||||
if (!sf.Equal(sfn))
|
||||
{
|
||||
// Insert Lf,fnext /\ Sf and Lf,fnext /\ Sfnext into P
|
||||
if (SegmentsIntersect(sorg, sdest, cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
|
||||
if (SegmentsIntersect(sfn.Org(), sfn.Dest(), cc_f, cc_f_next, out p, true))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Both circumcenters lie on the blinded side, but we
|
||||
// have to add the intersection with the segment.
|
||||
|
||||
// Center of f_next edge org->dest
|
||||
Point bisec = new Point((torg.x + tdest.x) / 2, (torg.y + tdest.y) / 2);
|
||||
|
||||
// Find intersection of seg with line through f_next's bisector and circumcenter
|
||||
if (SegmentsIntersect(sorg, sdest, bisec, cc_f_next, out p, false))
|
||||
{
|
||||
p.id = n + segIndex++;
|
||||
segPoints.Add(p);
|
||||
vpoints.Add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// f <- f_next
|
||||
f_next.Copy(ref f);
|
||||
|
||||
// Call f_next the next triangle counterclockwise around x
|
||||
f_next.Onext();
|
||||
}
|
||||
while (!f.Equals(f_init));
|
||||
|
||||
// Output: Bounded Voronoi cell of x in counterclockwise order.
|
||||
region.Add(vpoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the intersection point of the line segment defined by points A and B with the
|
||||
/// line segment defined by points C and D.
|
||||
/// </summary>
|
||||
/// <param name="seg">The first segment AB.</param>
|
||||
/// <param name="pc">Endpoint C of second segment.</param>
|
||||
/// <param name="pd">Endpoint D of second segment.</param>
|
||||
/// <param name="p">Reference to the intersection point.</param>
|
||||
/// <param name="strictIntersect">If false, pa and pb represent a line.</param>
|
||||
/// <returns>Returns true if the intersection point was found, and stores that point in X,Y.
|
||||
/// Returns false if there is no determinable intersection point, in which case X,Y will
|
||||
/// be unmodified.
|
||||
/// </returns>
|
||||
private bool SegmentsIntersect(Point p1, Point p2, Point p3, Point p4, out Point p, bool strictIntersect)
|
||||
{
|
||||
p = null; // TODO: Voronoi SegmentsIntersect
|
||||
|
||||
double Ax = p1.x, Ay = p1.y;
|
||||
double Bx = p2.x, By = p2.y;
|
||||
double Cx = p3.x, Cy = p3.y;
|
||||
double Dx = p4.x, Dy = p4.y;
|
||||
|
||||
double distAB, theCos, theSin, newX, ABpos;
|
||||
|
||||
// Fail if either line segment is zero-length.
|
||||
if (Ax == Bx && Ay == By || Cx == Dx && Cy == Dy) return false;
|
||||
|
||||
// Fail if the segments share an end-point.
|
||||
if (Ax == Cx && Ay == Cy || Bx == Cx && By == Cy
|
||||
|| Ax == Dx && Ay == Dy || Bx == Dx && By == Dy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// (1) Translate the system so that point A is on the origin.
|
||||
Bx -= Ax; By -= Ay;
|
||||
Cx -= Ax; Cy -= Ay;
|
||||
Dx -= Ax; Dy -= Ay;
|
||||
|
||||
// Discover the length of segment A-B.
|
||||
distAB = Math.Sqrt(Bx * Bx + By * By);
|
||||
|
||||
// (2) Rotate the system so that point B is on the positive X axis.
|
||||
theCos = Bx / distAB;
|
||||
theSin = By / distAB;
|
||||
newX = Cx * theCos + Cy * theSin;
|
||||
Cy = Cy * theCos - Cx * theSin; Cx = newX;
|
||||
newX = Dx * theCos + Dy * theSin;
|
||||
Dy = Dy * theCos - Dx * theSin; Dx = newX;
|
||||
|
||||
// Fail if segment C-D doesn't cross line A-B.
|
||||
if (Cy < 0 && Dy < 0 || Cy >= 0 && Dy >= 0 && strictIntersect) return false;
|
||||
|
||||
// (3) Discover the position of the intersection point along line A-B.
|
||||
ABpos = Dx + (Cx - Dx) * Dy / (Dy - Cy);
|
||||
|
||||
// Fail if segment C-D crosses line A-B outside of segment A-B.
|
||||
if (ABpos < 0 || ABpos > distAB && strictIntersect) return false;
|
||||
|
||||
// (4) Apply the discovered position to line A-B in the original coordinate system.
|
||||
p = new Point(Ax + ABpos * theCos, Ay + ABpos * theSin);
|
||||
|
||||
// Success.
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Voronoi enumerate edges
|
||||
|
||||
private IEnumerable<IEdge> EnumerateEdges()
|
||||
{
|
||||
// Copy edges
|
||||
Point first, last;
|
||||
var edges = new List<IEdge>(this.Regions.Count * 2);
|
||||
foreach (var region in this.Regions)
|
||||
{
|
||||
first = null;
|
||||
last = null;
|
||||
|
||||
foreach (var pt in region.Vertices)
|
||||
{
|
||||
if (first == null)
|
||||
{
|
||||
first = pt;
|
||||
last = pt;
|
||||
}
|
||||
else
|
||||
{
|
||||
edges.Add(new Edge(last.id, pt.id));
|
||||
|
||||
last = pt;
|
||||
}
|
||||
}
|
||||
|
||||
if (region.Bounded && first != null)
|
||||
{
|
||||
edges.Add(new Edge(last.id, first.id));
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: feb0f1207f0b84a5c9b69df93ebe6f00
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,33 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="IVoronoi.cs" company="">
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi.Legacy
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Voronoi diagram interface.
|
||||
/// </summary>
|
||||
internal interface IVoronoi
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the list of Voronoi vertices.
|
||||
/// </summary>
|
||||
Point[] Points { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Voronoi regions.
|
||||
/// </summary>
|
||||
ICollection<VoronoiRegion> Regions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of edges.
|
||||
/// </summary>
|
||||
IEnumerable<IEdge> Edges { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bdb9c5ef1811c494984e02a884726c96
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,307 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="Voronoi.cs">
|
||||
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi.Legacy
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Animation.TriangleNet.Topology;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
using Animation.TriangleNet.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// The Voronoi Diagram is the dual of a pointset triangulation.
|
||||
/// </summary>
|
||||
[Obsolete("Use TriangleNet.Voronoi.StandardVoronoi class instead.")]
|
||||
internal class SimpleVoronoi : IVoronoi
|
||||
{
|
||||
IPredicates predicates = RobustPredicates.Default;
|
||||
|
||||
Mesh mesh;
|
||||
|
||||
Point[] points;
|
||||
Dictionary<int, VoronoiRegion> regions;
|
||||
|
||||
// Stores the endpoints of rays of unbounded Voronoi cells
|
||||
Dictionary<int, Point> rayPoints;
|
||||
int rayIndex;
|
||||
|
||||
// Bounding box of the triangles circumcenters.
|
||||
Rectangle bounds;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimpleVoronoi" /> class.
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
/// <remarks>
|
||||
/// Be sure MakeVertexMap has been called (should always be the case).
|
||||
/// </remarks>
|
||||
public SimpleVoronoi(Mesh mesh)
|
||||
{
|
||||
this.mesh = mesh;
|
||||
|
||||
Generate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Voronoi vertices.
|
||||
/// </summary>
|
||||
public Point[] Points
|
||||
{
|
||||
get { return points; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of Voronoi regions.
|
||||
/// </summary>
|
||||
public ICollection<VoronoiRegion> Regions
|
||||
{
|
||||
get { return regions.Values; }
|
||||
}
|
||||
|
||||
public IEnumerable<IEdge> Edges
|
||||
{
|
||||
get { return EnumerateEdges(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Voronoi diagram as raw output data.
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// The Voronoi diagram is the geometric dual of the Delaunay triangulation.
|
||||
/// Hence, the Voronoi vertices are listed by traversing the Delaunay
|
||||
/// triangles, and the Voronoi edges are listed by traversing the Delaunay
|
||||
/// edges.
|
||||
///</remarks>
|
||||
private void Generate()
|
||||
{
|
||||
mesh.Renumber();
|
||||
mesh.MakeVertexMap();
|
||||
|
||||
// Allocate space for voronoi diagram
|
||||
this.points = new Point[mesh.triangles.Count + mesh.hullsize];
|
||||
this.regions = new Dictionary<int, VoronoiRegion>(mesh.vertices.Count);
|
||||
|
||||
rayPoints = new Dictionary<int, Point>();
|
||||
rayIndex = 0;
|
||||
|
||||
bounds = new Rectangle();
|
||||
|
||||
// Compute triangles circumcenters and setup bounding box
|
||||
ComputeCircumCenters();
|
||||
|
||||
// Add all Voronoi regions to the map.
|
||||
foreach (var vertex in mesh.vertices.Values)
|
||||
{
|
||||
regions.Add(vertex.id, new VoronoiRegion(vertex));
|
||||
}
|
||||
|
||||
// Loop over the mesh vertices (Voronoi generators).
|
||||
foreach (var region in regions.Values)
|
||||
{
|
||||
//if (item.Boundary == 0)
|
||||
{
|
||||
ConstructCell(region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ComputeCircumCenters()
|
||||
{
|
||||
Otri tri = default(Otri);
|
||||
double xi = 0, eta = 0;
|
||||
Point pt;
|
||||
|
||||
// Compue triangle circumcenters
|
||||
foreach (var item in mesh.triangles)
|
||||
{
|
||||
tri.tri = item;
|
||||
|
||||
pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
|
||||
pt.id = item.id;
|
||||
|
||||
points[item.id] = pt;
|
||||
|
||||
bounds.Expand(pt);
|
||||
}
|
||||
|
||||
double ds = Math.Max(bounds.Width, bounds.Height);
|
||||
bounds.Resize(ds / 10, ds / 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Construct Voronoi region for given vertex.
|
||||
/// </summary>
|
||||
/// <param name="region"></param>
|
||||
private void ConstructCell(VoronoiRegion region)
|
||||
{
|
||||
var vertex = region.Generator as Vertex;
|
||||
|
||||
var vpoints = new List<Point>();
|
||||
|
||||
Otri f = default(Otri);
|
||||
Otri f_init = default(Otri);
|
||||
Otri f_next = default(Otri);
|
||||
Otri f_prev = default(Otri);
|
||||
|
||||
Osub sub = default(Osub);
|
||||
|
||||
// Call f_init a triangle incident to x
|
||||
vertex.tri.Copy(ref f_init);
|
||||
|
||||
f_init.Copy(ref f);
|
||||
f_init.Onext(ref f_next);
|
||||
|
||||
// Check if f_init lies on the boundary of the triangulation.
|
||||
if (f_next.tri.id == Mesh.DUMMY)
|
||||
{
|
||||
f_init.Oprev(ref f_prev);
|
||||
|
||||
if (f_prev.tri.id != Mesh.DUMMY)
|
||||
{
|
||||
f_init.Copy(ref f_next);
|
||||
// Move one triangle clockwise
|
||||
f_init.Oprev();
|
||||
f_init.Copy(ref f);
|
||||
}
|
||||
}
|
||||
|
||||
// Go counterclockwise until we reach the border or the initial triangle.
|
||||
while (f_next.tri.id != Mesh.DUMMY)
|
||||
{
|
||||
// Add circumcenter of current triangle
|
||||
vpoints.Add(points[f.tri.id]);
|
||||
|
||||
region.AddNeighbor(f.tri.id, regions[f.Apex().id]);
|
||||
|
||||
if (f_next.Equals(f_init))
|
||||
{
|
||||
// Voronoi cell is complete (bounded case).
|
||||
region.Add(vpoints);
|
||||
return;
|
||||
}
|
||||
|
||||
f_next.Copy(ref f);
|
||||
f_next.Onext();
|
||||
}
|
||||
|
||||
// Voronoi cell is unbounded
|
||||
region.Bounded = false;
|
||||
|
||||
Vertex torg, tdest, tapex;
|
||||
Point intersection;
|
||||
int sid, n = mesh.triangles.Count;
|
||||
|
||||
// Find the boundary segment id (we use this id to number the endpoints of infinit rays).
|
||||
f.Lprev(ref f_next);
|
||||
f_next.Pivot(ref sub);
|
||||
sid = sub.seg.hash;
|
||||
|
||||
// Last valid f lies at the boundary. Add the circumcenter.
|
||||
vpoints.Add(points[f.tri.id]);
|
||||
region.AddNeighbor(f.tri.id, regions[f.Apex().id]);
|
||||
|
||||
// Check if the intersection with the bounding box has already been computed.
|
||||
if (!rayPoints.TryGetValue(sid, out intersection))
|
||||
{
|
||||
torg = f.Org();
|
||||
tapex = f.Apex();
|
||||
intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], torg.y - tapex.y, tapex.x - torg.x);
|
||||
|
||||
// Set the correct id for the vertex
|
||||
intersection.id = n + rayIndex;
|
||||
|
||||
points[n + rayIndex] = intersection;
|
||||
rayIndex++;
|
||||
|
||||
rayPoints.Add(sid, intersection);
|
||||
}
|
||||
|
||||
vpoints.Add(intersection);
|
||||
|
||||
// Now walk from f_init clockwise till we reach the boundary.
|
||||
vpoints.Reverse();
|
||||
|
||||
f_init.Copy(ref f);
|
||||
f.Oprev(ref f_prev);
|
||||
|
||||
while (f_prev.tri.id != Mesh.DUMMY)
|
||||
{
|
||||
vpoints.Add(points[f_prev.tri.id]);
|
||||
region.AddNeighbor(f_prev.tri.id, regions[f_prev.Apex().id]);
|
||||
|
||||
f_prev.Copy(ref f);
|
||||
f_prev.Oprev();
|
||||
}
|
||||
|
||||
// Find the boundary segment id.
|
||||
f.Pivot(ref sub);
|
||||
sid = sub.seg.hash;
|
||||
|
||||
if (!rayPoints.TryGetValue(sid, out intersection))
|
||||
{
|
||||
// Intersection has not been computed yet.
|
||||
torg = f.Org();
|
||||
tdest = f.Dest();
|
||||
|
||||
intersection = IntersectionHelper.BoxRayIntersection(bounds, points[f.tri.id], tdest.y - torg.y, torg.x - tdest.x);
|
||||
|
||||
// Set the correct id for the vertex
|
||||
intersection.id = n + rayIndex;
|
||||
|
||||
rayPoints.Add(sid, intersection);
|
||||
|
||||
points[n + rayIndex] = intersection;
|
||||
rayIndex++;
|
||||
}
|
||||
|
||||
vpoints.Add(intersection);
|
||||
region.AddNeighbor(intersection.id, regions[f.Dest().id]);
|
||||
|
||||
// Add the new points to the region (in counter-clockwise order)
|
||||
vpoints.Reverse();
|
||||
region.Add(vpoints);
|
||||
}
|
||||
|
||||
// TODO: Voronoi enumerate edges
|
||||
|
||||
private IEnumerable<IEdge> EnumerateEdges()
|
||||
{
|
||||
// Copy edges
|
||||
Point first, last;
|
||||
var edges = new List<IEdge>(this.Regions.Count * 2);
|
||||
foreach (var region in this.Regions)
|
||||
{
|
||||
var ve = region.Vertices.GetEnumerator();
|
||||
|
||||
ve.MoveNext();
|
||||
|
||||
first = last = ve.Current;
|
||||
|
||||
while (ve.MoveNext())
|
||||
{
|
||||
if (region.ID < region.GetNeighbor(last).ID)
|
||||
{
|
||||
edges.Add(new Edge(last.id, ve.Current.id));
|
||||
}
|
||||
|
||||
last = ve.Current;
|
||||
}
|
||||
|
||||
if (region.Bounded && region.ID < region.GetNeighbor(last).ID)
|
||||
{
|
||||
edges.Add(new Edge(last.id, first.id));
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d9c47b7ad095c4d6ea979045d1498235
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,112 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="VoronoiRegion.cs" company="">
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi.Legacy
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Animation.TriangleNet.Topology;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a region in the Voronoi diagram.
|
||||
/// </summary>
|
||||
internal class VoronoiRegion
|
||||
{
|
||||
int id;
|
||||
Point generator;
|
||||
List<Point> vertices;
|
||||
bool bounded;
|
||||
|
||||
// A map (vertex id) -> (neighbor across adjacent edge)
|
||||
Dictionary<int, VoronoiRegion> neighbors;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Voronoi region id (which is the same as the generators vertex id).
|
||||
/// </summary>
|
||||
public int ID
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Voronoi regions generator.
|
||||
/// </summary>
|
||||
public Point Generator
|
||||
{
|
||||
get { return generator; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Voronoi vertices on the regions boundary.
|
||||
/// </summary>
|
||||
public ICollection<Point> Vertices
|
||||
{
|
||||
get { return vertices; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the Voronoi region is bounded.
|
||||
/// </summary>
|
||||
public bool Bounded
|
||||
{
|
||||
get { return bounded; }
|
||||
set { bounded = value; }
|
||||
}
|
||||
|
||||
public VoronoiRegion(Vertex generator)
|
||||
{
|
||||
this.id = generator.id;
|
||||
this.generator = generator;
|
||||
this.vertices = new List<Point>();
|
||||
this.bounded = true;
|
||||
|
||||
this.neighbors = new Dictionary<int, VoronoiRegion>();
|
||||
}
|
||||
|
||||
public void Add(Point point)
|
||||
{
|
||||
this.vertices.Add(point);
|
||||
}
|
||||
|
||||
public void Add(List<Point> points)
|
||||
{
|
||||
this.vertices.AddRange(points);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the neighbouring Voronoi region, that lies across the edge starting at
|
||||
/// given vertex.
|
||||
/// </summary>
|
||||
/// <param name="p">Vertex defining an edge of the region.</param>
|
||||
/// <returns>Neighbouring Voronoi region</returns>
|
||||
/// <remarks>
|
||||
/// The edge starting at p is well defined (vertices are ordered counterclockwise).
|
||||
/// </remarks>
|
||||
public VoronoiRegion GetNeighbor(Point p)
|
||||
{
|
||||
VoronoiRegion neighbor;
|
||||
|
||||
if (neighbors.TryGetValue(p.id, out neighbor))
|
||||
{
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
internal void AddNeighbor(int id, VoronoiRegion neighbor)
|
||||
{
|
||||
this.neighbors.Add(id, neighbor);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("R-ID {0}", id);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 3a77eab5a6c484379b637070c9da9793
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,67 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="StandardVoronoi.cs">
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
using Animation.TriangleNet.Tools;
|
||||
using Animation.TriangleNet.Topology.DCEL;
|
||||
|
||||
internal class StandardVoronoi : VoronoiBase
|
||||
{
|
||||
public StandardVoronoi(Mesh mesh)
|
||||
: this(mesh, mesh.bounds, new DefaultVoronoiFactory(), RobustPredicates.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public StandardVoronoi(Mesh mesh, Rectangle box)
|
||||
: this(mesh, box, new DefaultVoronoiFactory(), RobustPredicates.Default)
|
||||
{
|
||||
}
|
||||
|
||||
public StandardVoronoi(Mesh mesh, Rectangle box, IVoronoiFactory factory, IPredicates predicates)
|
||||
: base(mesh, factory, predicates, true)
|
||||
{
|
||||
// We assume the box to be at least as large as the mesh.
|
||||
box.Expand(mesh.bounds);
|
||||
|
||||
// We explicitly told the base constructor to call the Generate method, so
|
||||
// at this point the basic Voronoi diagram is already created.
|
||||
PostProcess(box);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute edge intersections with bounding box.
|
||||
/// </summary>
|
||||
private void PostProcess(Rectangle box)
|
||||
{
|
||||
foreach (var edge in rays)
|
||||
{
|
||||
// The vertices of the infinite edge.
|
||||
var v1 = (Point)edge.origin;
|
||||
var v2 = (Point)edge.twin.origin;
|
||||
|
||||
if (box.Contains(v1) || box.Contains(v2))
|
||||
{
|
||||
// Move infinite vertex v2 onto the box boundary.
|
||||
IntersectionHelper.BoxRayIntersection(box, v1, v2, ref v2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is actually no easy way to handle the second case. The two edges
|
||||
// leaving v1, pointing towards the mesh, don't have to intersect the box
|
||||
// (the could join with edges of other cells outside the box).
|
||||
|
||||
// A general intersection algorithm (DCEL <-> Rectangle) is needed, which
|
||||
// computes intersections with all edges and discards objects outside the
|
||||
// box.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7698a2e5326da4363ac142814272de0c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,292 @@
|
|||
// -----------------------------------------------------------------------
|
||||
// <copyright file="VoronoiBase.cs">
|
||||
// Original Triangle code by Jonathan Richard Shewchuk, http://www.cs.cmu.edu/~quake/triangle.html
|
||||
// Triangle.NET code by Christian Woltering, http://triangle.codeplex.com/
|
||||
// </copyright>
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
namespace UnityEngine.U2D.Animation.TriangleNet
|
||||
.Voronoi
|
||||
{
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Animation.TriangleNet.Topology;
|
||||
using Animation.TriangleNet.Geometry;
|
||||
using Animation.TriangleNet.Topology.DCEL;
|
||||
|
||||
using Vertex = Animation.TriangleNet.Topology.DCEL.Vertex;
|
||||
|
||||
/// <summary>
|
||||
/// The Voronoi diagram is the dual of a pointset triangulation.
|
||||
/// </summary>
|
||||
internal abstract class VoronoiBase : DcelMesh
|
||||
{
|
||||
protected IPredicates predicates;
|
||||
|
||||
protected IVoronoiFactory factory;
|
||||
|
||||
// List of infinite half-edges, i.e. half-edges that start at circumcenters of triangles
|
||||
// which lie on the domain boundary.
|
||||
protected List<HalfEdge> rays;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="VoronoiBase" /> class.
|
||||
/// </summary>
|
||||
/// <param name="mesh">Triangle mesh.</param>
|
||||
/// <param name="factory">Voronoi object factory.</param>
|
||||
/// <param name="predicates">Geometric predicates implementation.</param>
|
||||
/// <param name="generate">If set to true, the constuctor will call the Generate
|
||||
/// method, which builds the Voronoi diagram.</param>
|
||||
protected VoronoiBase(Mesh mesh, IVoronoiFactory factory, IPredicates predicates,
|
||||
bool generate)
|
||||
: base(false)
|
||||
{
|
||||
this.factory = factory;
|
||||
this.predicates = predicates;
|
||||
|
||||
if (generate)
|
||||
{
|
||||
Generate(mesh);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate the Voronoi diagram from given triangle mesh..
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
/// <param name="bounded"></param>
|
||||
protected void Generate(Mesh mesh)
|
||||
{
|
||||
mesh.Renumber();
|
||||
|
||||
base.edges = new List<HalfEdge>();
|
||||
this.rays = new List<HalfEdge>();
|
||||
|
||||
// Allocate space for Voronoi diagram.
|
||||
var vertices = new Vertex[mesh.triangles.Count + mesh.hullsize];
|
||||
var faces = new Face[mesh.vertices.Count];
|
||||
|
||||
if (factory == null)
|
||||
{
|
||||
factory = new DefaultVoronoiFactory();
|
||||
}
|
||||
|
||||
factory.Initialize(vertices.Length, 2 * mesh.NumberOfEdges, faces.Length);
|
||||
|
||||
// Compute triangles circumcenters.
|
||||
var map = ComputeVertices(mesh, vertices);
|
||||
|
||||
// Create all Voronoi faces.
|
||||
foreach (var vertex in mesh.vertices.Values)
|
||||
{
|
||||
faces[vertex.id] = factory.CreateFace(vertex);
|
||||
}
|
||||
|
||||
ComputeEdges(mesh, vertices, faces, map);
|
||||
|
||||
// At this point all edges are computed, but the (edge.next) pointers aren't set.
|
||||
ConnectEdges(map);
|
||||
|
||||
base.vertices = new List<Vertex>(vertices);
|
||||
base.faces = new List<Face>(faces);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the Voronoi vertices (the circumcenters of the triangles).
|
||||
/// </summary>
|
||||
/// <returns>An empty map, which will map all vertices to a list of leaving edges.</returns>
|
||||
protected List<HalfEdge>[] ComputeVertices(Mesh mesh, Vertex[] vertices)
|
||||
{
|
||||
Otri tri = default(Otri);
|
||||
double xi = 0, eta = 0;
|
||||
Vertex vertex;
|
||||
Point pt;
|
||||
int id;
|
||||
|
||||
// Maps all vertices to a list of leaving edges.
|
||||
var map = new List<HalfEdge>[mesh.triangles.Count];
|
||||
|
||||
// Compue triangle circumcenters
|
||||
foreach (var t in mesh.triangles)
|
||||
{
|
||||
id = t.id;
|
||||
tri.tri = t;
|
||||
|
||||
pt = predicates.FindCircumcenter(tri.Org(), tri.Dest(), tri.Apex(), ref xi, ref eta);
|
||||
|
||||
vertex = factory.CreateVertex(pt.x, pt.y);
|
||||
vertex.id = id;
|
||||
|
||||
vertices[id] = vertex;
|
||||
map[id] = new List<HalfEdge>();
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute the edges of the Voronoi diagram.
|
||||
/// </summary>
|
||||
/// <param name="mesh"></param>
|
||||
/// <param name="vertices"></param>
|
||||
/// <param name="faces"></param>
|
||||
/// <param name="map">Empty vertex map.</param>
|
||||
protected void ComputeEdges(Mesh mesh, Vertex[] vertices, Face[] faces, List<HalfEdge>[] map)
|
||||
{
|
||||
Otri tri, neighbor = default(Otri);
|
||||
Animation.TriangleNet.Geometry.Vertex org, dest;
|
||||
|
||||
double px, py;
|
||||
int id, nid, count = mesh.triangles.Count;
|
||||
|
||||
Face face, neighborFace;
|
||||
HalfEdge edge, twin;
|
||||
Vertex vertex, end;
|
||||
|
||||
// Count infinte edges (vertex id for their endpoints).
|
||||
int j = 0;
|
||||
|
||||
// Count half-edges (edge ids).
|
||||
int k = 0;
|
||||
|
||||
// To loop over the set of edges, loop over all triangles, and look at the
|
||||
// three edges of each triangle. If there isn't another triangle adjacent
|
||||
// to the edge, operate on the edge. If there is another adjacent triangle,
|
||||
// operate on the edge only if the current triangle has a smaller id than
|
||||
// its neighbor. This way, each edge is considered only once.
|
||||
foreach (var t in mesh.triangles)
|
||||
{
|
||||
id = t.id;
|
||||
|
||||
tri.tri = t;
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
tri.orient = i;
|
||||
tri.Sym(ref neighbor);
|
||||
|
||||
nid = neighbor.tri.id;
|
||||
|
||||
if (id < nid || nid < 0)
|
||||
{
|
||||
// Get the endpoints of the current triangle edge.
|
||||
org = tri.Org();
|
||||
dest = tri.Dest();
|
||||
|
||||
face = faces[org.id];
|
||||
neighborFace = faces[dest.id];
|
||||
|
||||
vertex = vertices[id];
|
||||
|
||||
// For each edge in the triangle mesh, there's a corresponding edge
|
||||
// in the Voronoi diagram, i.e. two half-edges will be created.
|
||||
if (nid < 0)
|
||||
{
|
||||
// Unbounded edge, direction perpendicular to the boundary edge,
|
||||
// pointing outwards.
|
||||
px = dest.y - org.y;
|
||||
py = org.x - dest.x;
|
||||
|
||||
end = factory.CreateVertex(vertex.x + px, vertex.y + py);
|
||||
end.id = count + j++;
|
||||
|
||||
vertices[end.id] = end;
|
||||
|
||||
edge = factory.CreateHalfEdge(end, face);
|
||||
twin = factory.CreateHalfEdge(vertex, neighborFace);
|
||||
|
||||
// Make (face.edge) always point to an edge that starts at an infinite
|
||||
// vertex. This will allow traversing of unbounded faces.
|
||||
face.edge = edge;
|
||||
face.bounded = false;
|
||||
|
||||
map[id].Add(twin);
|
||||
|
||||
rays.Add(twin);
|
||||
}
|
||||
else
|
||||
{
|
||||
end = vertices[nid];
|
||||
|
||||
// Create half-edges.
|
||||
edge = factory.CreateHalfEdge(end, face);
|
||||
twin = factory.CreateHalfEdge(vertex, neighborFace);
|
||||
|
||||
// Add to vertex map.
|
||||
map[nid].Add(edge);
|
||||
map[id].Add(twin);
|
||||
}
|
||||
|
||||
vertex.leaving = twin;
|
||||
end.leaving = edge;
|
||||
|
||||
edge.twin = twin;
|
||||
twin.twin = edge;
|
||||
|
||||
edge.id = k++;
|
||||
twin.id = k++;
|
||||
|
||||
this.edges.Add(edge);
|
||||
this.edges.Add(twin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect all edges of the Voronoi diagram.
|
||||
/// </summary>
|
||||
/// <param name="map">Maps all vertices to a list of leaving edges.</param>
|
||||
protected virtual void ConnectEdges(List<HalfEdge>[] map)
|
||||
{
|
||||
int length = map.Length;
|
||||
|
||||
// For each half-edge, find its successor in the connected face.
|
||||
foreach (var edge in this.edges)
|
||||
{
|
||||
var face = edge.face.generator.id;
|
||||
|
||||
// The id of the dest vertex of current edge.
|
||||
int id = edge.twin.origin.id;
|
||||
|
||||
// The edge origin can also be an infinite vertex. Sort them out
|
||||
// by checking the id.
|
||||
if (id < length)
|
||||
{
|
||||
// Look for the edge that is connected to the current face. Each
|
||||
// Voronoi vertex has degree 3, so this loop is actually O(1).
|
||||
foreach (var next in map[id])
|
||||
{
|
||||
if (next.face.generator.id == face)
|
||||
{
|
||||
edge.next = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<IEdge> EnumerateEdges()
|
||||
{
|
||||
var edges = new List<IEdge>(this.edges.Count / 2);
|
||||
|
||||
foreach (var edge in this.edges)
|
||||
{
|
||||
var twin = edge.twin;
|
||||
|
||||
// Report edge only once.
|
||||
if (twin == null)
|
||||
{
|
||||
edges.Add(new Edge(edge.origin.id, edge.next.origin.id));
|
||||
}
|
||||
else if (edge.id < twin.id)
|
||||
{
|
||||
edges.Add(new Edge(edge.origin.id, twin.origin.id));
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c127d0e1e24224577950ebc042c30437
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue