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; } } }