using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.ProBuilder
{
///
/// A winged-edge data structure holds references to an edge, the previous and next edge in it's triangle, it's connected face, and the opposite edge (common).
///
///
/// ```
/// . / (face) /
/// . prev / / next
/// . / edge /
/// . /_ _ _ _ _ _ _/
/// . |- - - - - - -|
/// . | opposite |
/// . | |
/// . | |
/// . | |
/// ```
///
///
///
public sealed class WingedEdge : IEquatable
{
///
/// The local and shared edge that this edge belongs to.
///
public EdgeLookup edge { get; private set; }
///
/// The connected face that this wing belongs to.
///
public Face face { get; private set; }
///
/// The WingedEdge that is connected to the edge.y vertex.
///
public WingedEdge next { get; private set; }
///
/// The WingedEdge that is connected to the edge.x vertex.
///
public WingedEdge previous { get; private set; }
///
/// The WingedEdge that is on the opposite side of this edge.
///
public WingedEdge opposite { get; private set; }
WingedEdge() {}
///
/// Equality comparision tests for local edge equality, disregarding other values.
///
/// The WingedEdge to compare against.
/// True if the local edges are equal, false if not.
public bool Equals(WingedEdge other)
{
return !ReferenceEquals(other, null) && edge.local.Equals(other.edge.local);
}
///
/// Equality comparision tests for local edge equality, disregarding other values.
///
/// The WingedEdge to compare against.
/// True if the local edges are equal, false if not.
public override bool Equals(object obj)
{
WingedEdge be = obj as WingedEdge;
if (be != null && this.Equals(be))
return true;
if (obj is Edge && this.edge.local.Equals((Edge)obj))
return true;
return false;
}
///
/// Get a hash code for this edge.
///
/// WingedEdge comparison only considers the local edge. As such, this returns the local edge hashcode.
public override int GetHashCode()
{
return edge.local.GetHashCode();
}
///
/// How many edges are in this sequence.
///
/// The number of WingedEdges that are connected by walking the @"UnityEngine.ProBuilder.WingedEdge.next" property.
public int Count()
{
WingedEdge current = this;
int count = 0;
do
{
count++;
current = current.next;
}
while (current != null && !ReferenceEquals(current, this));
return count;
}
/// A string representation of the properties contained within this object.
public override string ToString()
{
return string.Format("Common: {0}\nLocal: {1}\nOpposite: {2}\nFace: {3}",
edge.common.ToString(),
edge.local.ToString(),
opposite == null ? "null" : opposite.edge.ToString(),
face.ToString());
}
///
/// Given two adjacent triangle wings, attempt to create a single quad.
///
///
///
///
internal static int[] MakeQuad(WingedEdge left, WingedEdge right)
{
// Both faces must be triangles in order to be considered a quad when combined
if (left.Count() != 3 || right.Count() != 3)
return null;
EdgeLookup[] all = new EdgeLookup[6]
{
left.edge,
left.next.edge,
left.next.next.edge,
right.edge,
right.next.edge,
right.next.next.edge
};
int[] dup = new int[6];
int matches = 0;
for (int i = 0; i < 3; i++)
{
for (int n = 3; n < 6; n++)
{
if (all[i].Equals(all[n]))
{
matches++;
dup[i] = 1;
dup[n] = 1;
break;
}
}
}
// Edges are either not adjacent, or share more than one edge
if (matches != 1)
return null;
int qi = 0;
EdgeLookup[] edges = new EdgeLookup[4];
for (int i = 0; i < 6; i++)
if (dup[i] < 1)
edges[qi++] = all[i];
int[] quad = new int[4] { edges[0].local.a, edges[0].local.b, -1, -1 };
int c1 = edges[0].common.b, c2 = -1;
if (edges[1].common.a == c1)
{
quad[2] = edges[1].local.b;
c2 = edges[1].common.b;
}
else if (edges[2].common.a == c1)
{
quad[2] = edges[2].local.b;
c2 = edges[2].common.b;
}
else if (edges[3].common.a == c1)
{
quad[2] = edges[3].local.b;
c2 = edges[3].common.b;
}
if (edges[1].common.a == c2)
quad[3] = edges[1].local.b;
else if (edges[2].common.a == c2)
quad[3] = edges[2].local.b;
else if (edges[3].common.a == c2)
quad[3] = edges[3].local.b;
if (quad[2] == -1 || quad[3] == -1)
return null;
return quad;
}
///
/// Return the @"UnityEngine.ProBuilder.WingedEdge.previous" or @"UnityEngine.ProBuilder.WingedEdge.next" WingedEdge if it contains the passed common (shared) index.
///
/// The common index to search next and previous for.
/// The next or previous WingedEdge that contains common, or null if not found.
public WingedEdge GetAdjacentEdgeWithCommonIndex(int common)
{
if (next.edge.common.Contains(common))
return next;
else if (previous.edge.common.Contains(common))
return previous;
return null;
}
///
/// Order a face's edges in sequence.
/// The first edge is used as a starting point.
///
/// The source face.
/// A new set of edges where each edge y value matches the next edge x.
public static List SortEdgesByAdjacency(Face face)
{
if (face == null || face.edgesInternal == null)
throw new ArgumentNullException("face");
List edges = new List(face.edgesInternal);
SortEdgesByAdjacency(edges);
return edges;
}
///
/// Sort edges list by adjacency, such that each edge's common y value matches the next edge's common x.
///
/// The edges to sort in-place.
public static void SortEdgesByAdjacency(List edges)
{
if (edges == null)
throw new ArgumentNullException("edges");
for (int i = 1; i < edges.Count; i++)
{
int want = edges[i - 1].b;
for (int n = i + 1; n < edges.Count; n++)
{
if (edges[n].a == want || edges[n].b == want)
{
Edge swap = edges[n];
edges[n] = edges[i];
edges[i] = swap;
}
}
}
}
///
/// Get a dictionary of common indexes and all WingedEdge values touching the index.
///
/// The wings to search for spokes.
/// A dictionary where each key is a common index with a list of each winged edge touching it.
public static Dictionary> GetSpokes(List wings)
{
if (wings == null)
throw new ArgumentNullException("wings");
Dictionary> spokes = new Dictionary>();
List l = null;
for (int i = 0; i < wings.Count; i++)
{
if (spokes.TryGetValue(wings[i].edge.common.a, out l))
l.Add(wings[i]);
else
spokes.Add(wings[i].edge.common.a, new List() { wings[i] });
if (spokes.TryGetValue(wings[i].edge.common.b, out l))
l.Add(wings[i]);
else
spokes.Add(wings[i].edge.common.b, new List() { wings[i] });
}
return spokes;
}
///
/// Given a set of winged edges and list of common indexes, attempt to create a complete path of indexes where each is connected by edge.
///
/// May be clockwise or counter-clockwise ordered, or null if no path is found.
///
/// The wings to be sorted.
/// The common indexes to be sorted.
///
internal static List SortCommonIndexesByAdjacency(List wings, HashSet common)
{
List matches = wings.Where(x => common.Contains(x.edge.common.a) && common.Contains(x.edge.common.b)).Select(y => y.edge.common).ToList();
// if edge count != index count there isn't a full perimeter
if (matches.Count != common.Count)
return null;
SortEdgesByAdjacency(matches);
return matches.Select(x => x.a).ToList();
}
///
/// Create a new list of WingedEdge values for a ProBuilder mesh.
///
/// The mesh from which faces will read.
/// Optionally restrict the list to only include one WingedEdge per-face.
/// A new list of WingedEdge values gathered from @"UnityEngine.ProBuilder.ProBuilderMesh.faces".
public static List GetWingedEdges(ProBuilderMesh mesh, bool oneWingPerFace = false)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
return GetWingedEdges(mesh, mesh.facesInternal, oneWingPerFace);
}
///
/// Create a new list of WingedEdge values for a ProBuilder mesh.
///
/// Target ProBuilderMesh.
/// Which faces to include in the WingedEdge list.
/// If `oneWingPerFace` is true the returned list will contain a single winged edge per-face (but still point to all edges).
/// A new list of WingedEdge values gathered from faces.
public static List GetWingedEdges(ProBuilderMesh mesh, IEnumerable faces, bool oneWingPerFace = false)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
var lookup = mesh.sharedVertexLookup;
List winged = new List();
Dictionary opposites = new Dictionary();
foreach (Face f in faces)
{
List edges = SortEdgesByAdjacency(f);
int edgeLength = edges.Count;
WingedEdge first = null, prev = null;
for (int n = 0; n < edgeLength; n++)
{
Edge e = edges[n];
WingedEdge w = new WingedEdge();
w.edge = new EdgeLookup(lookup[e.a], lookup[e.b], e.a, e.b);
w.face = f;
if (n < 1)
first = w;
if (n > 0)
{
w.previous = prev;
prev.next = w;
}
if (n == edgeLength - 1)
{
w.next = first;
first.previous = w;
}
prev = w;
WingedEdge opp;
if (opposites.TryGetValue(w.edge.common, out opp))
{
opp.opposite = w;
w.opposite = opp;
}
else
{
w.opposite = null;
opposites.Add(w.edge.common, w);
}
if (!oneWingPerFace || n < 1)
winged.Add(w);
}
}
return winged;
}
}
}