using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
namespace UnityEngine.ProBuilder
{
///
/// Represents a boundary edge where three faces meet. It holds references to:
/// - The connected to this edge
/// - An for finding coincident vertices on this edge
/// - The previous and next winged edges in its triangle
/// - The common ([coincident](../manual/gloss.html#coincident)) winged edge, called the 'opposite' edge
///
///
/// ```
/// . / (face) /
/// . prev / / next
/// . / edge /
/// . /_ _ _ _ _ _ _/
/// . |- - - - - - -|
/// . | opposite |
/// . | |
/// . | |
/// . | |
/// ```
///
public sealed class WingedEdge : IEquatable
{
static readonly Dictionary k_OppositeEdgeDictionary = new Dictionary();
///
/// Gets the local and shared edge that this edge belongs to.
///
public EdgeLookup edge { get; private set; }
///
/// Gets the connected face that this wing belongs to.
///
public Face face { get; private set; }
///
/// Gets the WingedEdge that is connected to the `edge.y` vertex.
///
public WingedEdge next { get; private set; }
///
/// Gets the WingedEdge that is connected to the `edge.x` vertex.
///
public WingedEdge previous { get; private set; }
///
/// Gets the WingedEdge that is on the "opposite" side of this edge.
///
public WingedEdge opposite { get; private set; }
WingedEdge() {}
///
/// Tests to see whether the specified Edge is equal to the local edge, 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);
}
///
/// Tests to see whether the specified object is equal to the local edge, 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;
}
///
/// Returns the 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();
}
///
/// Returns the number of edges connected in this sequence.
///
/// The number of WingedEdges found with the 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;
}
///
/// Returns a string representation of the winged edge.
///
/// String formatted as follows:
///
/// `Common: [common]`
///
/// `Local: [local]`
///
/// `Opposite: [opposite]`
///
/// `Face: [face]`
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;
}
///
/// Returns WingedEdge.previous or
/// WingedEdge.next if it contains the specified 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;
}
///
/// Orders a face's edges in sequence, starting from the first edge.
///
/// The source face.
/// A new set of edges where each edge's Y value matches the next edge's X value.
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;
}
///
/// Sorts the specified list of edges by adjacency, such that each edge's common Y value matches the next edge's common X value.
///
/// 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;
}
}
}
}
///
/// Returns a dictionary of common indices and all WingedEdge values that touch each common index.
///
/// The list of WingedEdges to search.
/// A dictionary where each key is a common index mapped to a list of each winged edge that touches 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.ConvertAll(x => x.a);
}
///
/// Creates a new list of WingedEdge values from all faces on the specified ProBuilder mesh.
///
/// The mesh containing the faces to analyze.
/// Set this parameter to true to restrict the list to include only one WingedEdge per face. The default value is false.
/// A new list of WingedEdge values gathered from the ProBuilderMesh.faces.
public static List GetWingedEdges(ProBuilderMesh mesh, bool oneWingPerFace = false)
{
if (mesh == null)
throw new ArgumentNullException("mesh");
return GetWingedEdges(mesh, mesh.facesInternal, oneWingPerFace);
}
///
/// Creates a new list of WingedEdge values from a specific set of faces on the specified ProBuilder mesh.
///
/// The mesh containing the faces to analyze.
/// The collection of faces to include in the WingedEdge list.
/// Set this parameter to true to restrict the list to include only one WingedEdge per face. The default value is false.
/// A new list of WingedEdge values gathered from the specified 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();
k_OppositeEdgeDictionary.Clear();
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 (k_OppositeEdgeDictionary.TryGetValue(w.edge.common, out opp))
{
opp.opposite = w;
w.opposite = opp;
}
else
{
w.opposite = null;
k_OppositeEdgeDictionary.Add(w.edge.common, w);
}
if (!oneWingPerFace || n < 1)
winged.Add(w);
}
}
return winged;
}
}
}