/// <summary> /// Flips the given triangle edge in the triangulation. /// Flipping for Delaunay means changing the edge for the adjacent two triangles to the other possibility. /// </summary> /// <param name="T"></param> /// <param name="a_Edge"></param> private static void Flip(Triangulation T, TriangleEdge a_Edge) { var a_Triangle = a_Edge.T; var a_Twin = a_Edge.Twin.T; if (a_Triangle == null || a_Twin == null) { throw new GeomException("Cannot flip edge if neighbouring triangles don't exist"); } // retrieve other adjacent edges to edge vertices // e3 . e1 // / | \ // . | . // \ | / // e2 . e0 var e0 = a_Triangle.OtherEdge(a_Edge, a_Edge.Point1); var e1 = a_Triangle.OtherEdge(a_Edge, a_Edge.Point2); var e2 = a_Twin.OtherEdge(a_Edge.Twin, a_Edge.Point1); var e3 = a_Twin.OtherEdge(a_Edge.Twin, a_Edge.Point2); // create new triangle edges var euv = new TriangleEdge(e0.Point1, e2.Point2, null, null); var evu = new TriangleEdge(e2.Point2, e0.Point1, euv, null); euv.Twin = evu; // Remove old triangles T.RemoveTriangle(a_Triangle); T.RemoveTriangle(a_Twin); // add new triangles T.AddTriangle(new Triangle(e0, e2, evu)); T.AddTriangle(new Triangle(e3, e1, euv)); }
internal static Vector3 GetUncommonPoint(ITriangle triangle, TriangleEdge edge0, TriangleEdge edge1) { Vector3[] points0 = new Vector3[] { triangle.GetPoint(edge0, true), triangle.GetPoint(edge0, false) }; Vector3[] points1 = new Vector3[] { triangle.GetPoint(edge1, true), triangle.GetPoint(edge1, false) }; // Find exact for (int cntr0 = 0; cntr0 < points0.Length; cntr0++) { for (int cntr1 = 0; cntr1 < points1.Length; cntr1++) { if (points0[cntr0] == points1[cntr1]) { return(points0[cntr0 == 0 ? 1 : 0]); // return the one that isn't common } } } // Find close - execution should never get here, just being safe for (int cntr0 = 0; cntr0 < points0.Length; cntr0++) { for (int cntr1 = 0; cntr1 < points1.Length; cntr1++) { if (points0[cntr0].IsNearValue(points1[cntr1])) { return(points0[cntr0 == 0 ? 1 : 0]); // return the one that isn't common } } } throw new ApplicationException("Didn't find a common point"); }
public Edge AddEdge(Surface surface, TriangleEdge edge) { if (edges.ContainsKey(edge)) { var e = edges[edge]; e.Surfaces.ForEach(s => { s.AdjacentSurfaces.Add(surface); surface.AdjacentSurfaces.Add(s); }); e.Surfaces.Add(surface); return(e); } else { var e = new Edge() { edge = edge }; e.Surfaces.Add(surface); edges[edge] = e; e.v1 = AddVertex(e, edge.a); e.v2 = AddVertex(e, edge.b); e.v1.Edges.Add(e); e.v1.AdjacentVerts.Add(e.v2); e.v2.Edges.Add(e); e.v2.AdjacentVerts.Add(e.v1); return(e); } }
internal static float GetEdgeLength(ITriangle triangle, TriangleEdge edge) { Vector3 point0 = triangle.GetPoint(edge, true); Vector3 point1 = triangle.GetPoint(edge, false); return((point1 - point0).magnitude); }
internal static Vector3 GetEdgeMidpoint(ITriangle triangle, TriangleEdge edge) { Vector3 point0 = triangle.GetPoint(edge, true); Vector3 point1 = triangle.GetPoint(edge, false); Vector3 halfLength = (point1 - point0) * .5f; return(point0 + halfLength); }
private static LinkedList <int> OrderVertices(Triangle[] triangles) { var result = new LinkedList <int>(); var usedTriangles = new bool[triangles.Length]; var queue = new Queue <LinkedListNode <int> >(); var edgesDict = new TrianglesEdgesDictionary(triangles.Length); foreach (var triangle in triangles) { edgesDict.AddTriangle(triangle); } var firstVertex = triangles.SelectMany(it => it.Vertices).First(it => it.Triangles.Count == 1); var firstTriangle = firstVertex.Triangles.Single(); usedTriangles[firstTriangle.Id] = true; result.AddLast(firstVertex.Id); result.AddLast(firstTriangle.Vertices.First(it => it.Id != firstVertex.Id).Id); result.AddLast(firstTriangle.Vertices.Last(it => it.Id != firstVertex.Id).Id); queue.Enqueue(result.Last); while (result.Count < triangles.Length + 2) { var edgeEndNode = queue.Dequeue(); var edge = new TriangleEdge() { A = edgeEndNode.Value, B = edgeEndNode.Previous.Value }; foreach (var ownerTriangle in edgesDict[edge]) { if (usedTriangles[ownerTriangle.TriangleId]) { continue; } usedTriangles[ownerTriangle.TriangleId] = true; var newNode = result.AddBefore(edgeEndNode, ownerTriangle.ThirdVertex); queue.Enqueue(newNode); queue.Enqueue(edgeEndNode); } } return(result); }
internal static Vector3 GetOppositePoint(ITriangle triangle, TriangleEdge edge) { switch (edge) { case TriangleEdge.Edge_01: return(triangle.Point2); case TriangleEdge.Edge_12: return(triangle.Point0); case TriangleEdge.Edge_20: return(triangle.Point1); default: throw new ApplicationException("Unknown TriangleEdge: " + edge.ToString()); } }
private void AddEdgeWitOwnerByVertexIndexes(Triangle triangle, int a, int b, int c) { var key = new TriangleEdge() { A = triangle.Vertices[a].Id, B = triangle.Vertices[b].Id }; var value = new EdgeOwnerTriangle() { TriangleId = triangle.Id, ThirdVertex = triangle.Vertices[c].Id }; if (!base.TryGetValue(key, out var list)) { base[key] = list = new List <EdgeOwnerTriangle>(2); } list.Add(value); }
public TriangleTest() { v1 = new Vector2(0, 0); v2 = new Vector2(1, 1); v3 = new Vector2(2, 0); v4 = new Vector2(-1, -1); e1 = new TriangleEdge(v1, v2); e2 = new TriangleEdge(v2, v3); e3 = new TriangleEdge(v3, v1); t1 = new Triangle(e1, e2, e3); e4 = new TriangleEdge(v1, v3); e5 = new TriangleEdge(v3, v4); e6 = new TriangleEdge(v4, v1); t2 = new Triangle(e4, e5, e6); e3.Twin = e4; e4.Twin = e3; }
void DrawEdge(TriangleEdge e, bool gray, bool first) { var c = gray ? Color.gray : Color.white; if (ShowEdgeVertices) { Gizmos.color = Color.white * c; DrawCube(e.a); DrawCube(e.b); } if (ShowEdges) { Gizmos.color = Color.white * c; if (first) { Gizmos.color = Color.red; } Gizmos.DrawLine(new Vector3(e.a.x, e.a.y), new Vector3(e.b.x, e.b.y)); } if (ShowCircumcenter) { Gizmos.color = Color.blue * c; DrawCube(e.circumcenter, 0.05f); } if (ShowCircle) { Gizmos.color = Color.white * c; DrawCircle(e.circumcenter, e.innerCircleR); } if (ShowRoots) { Gizmos.color = Color.green * c; DrawCube(e.root, 0.05f); DrawCircle(e.root, 3); } }
public TriangulationTest() { v1 = new Vector2(0, 0); v2 = new Vector2(1, 1); v3 = new Vector2(2, 0); v4 = new Vector2(1, -1); e1 = new TriangleEdge(v1, v2); e2 = new TriangleEdge(v2, v3); e3 = new TriangleEdge(v3, v1); t1 = new Triangle(e1, e2, e3); e4 = new TriangleEdge(v1, v3); e5 = new TriangleEdge(v3, v4); e6 = new TriangleEdge(v4, v1); t2 = new Triangle(e4, e5, e6); T = new Triangulation(); T.AddTriangle(t1); T.AddTriangle(t2); }
/// <summary> /// Checks if the Delaunay property holds for the edge. /// </summary> /// <param name="T"></param> /// <returns>Whether the triangle edge is valid for a Delaunay triangulation.</returns> public static bool IsValid(TriangleEdge a_Edge) { // outer edge always valid if (a_Edge != null && a_Edge.IsOuter) { return(true); } if (a_Edge == null || a_Edge.T == null || a_Edge.Twin == null || a_Edge.Twin.T == null) { throw new GeomException("Invalid triangle edge - null pointers"); } var a_triangle = a_Edge.T; var a_Twin = a_Edge.Twin.T; // Points to test var u = a_Edge.T.OtherVertex(a_Edge).Value; var v = a_Edge.Twin.T.OtherVertex(a_Edge.Twin).Value; return(!a_triangle.InsideCircumcircle(v) && !a_Twin.InsideCircumcircle(u)); }
internal static Vector3 GetPoint(ITriangle triangle, TriangleEdge edge, bool isFrom) { switch (edge) { case TriangleEdge.Edge_01: if (isFrom) { return(triangle.Point0); } else { return(triangle.Point1); } case TriangleEdge.Edge_12: if (isFrom) { return(triangle.Point1); } else { return(triangle.Point2); } case TriangleEdge.Edge_20: if (isFrom) { return(triangle.Point2); } else { return(triangle.Point0); } default: throw new ApplicationException("Unknown TriangleEdge: " + edge.ToString()); } }
/// <summary> /// Makes an edge legal accorinding to the Delaunay condition. /// Recurses whenever flipping an edge since adjacent edges can be made illegal. /// </summary> /// <param name="T"></param> /// <param name="a_Vertex"></param> /// <param name="a_Edge"></param> private static void LegalizeEdge(Triangulation T, Vector2 a_Vertex, TriangleEdge a_Edge) { // do not legalize outer edge if (a_Edge != null && a_Edge.IsOuter) { return; } if (a_Edge == null || a_Edge.T == null || a_Edge.Twin == null || a_Edge.Twin.T == null) { throw new GeomException("Invalid triangle edge - Cannot legalize edge"); } var a_triangle = a_Edge.T; var a_Twin = a_Edge.Twin.T; if (!IsValid(a_Edge)) { Flip(T, a_Edge); LegalizeEdge(T, a_Vertex, a_Twin.OtherEdge(a_Edge.Twin, a_Edge.Point1)); LegalizeEdge(T, a_Vertex, a_Twin.OtherEdge(a_Edge.Twin, a_Edge.Point2)); } }
private TriangleLinked DetermineBestNextNeighbour(TriangleLinked triangle, List<TriangleLinked> neighbours, TriangleEdge edge, bool tieBreak = false) { if (neighbours.Count == 0) return null; if (neighbours.Count == 1) return neighbours[0]; neighbours.Sort((a, b) => GetNumLinked(a).CompareTo(GetNumLinked(b))); TriangleLinked[] twoTop = new TriangleLinked[2]; twoTop[0] = neighbours[0]; twoTop[1] = neighbours[1]; int[] numLinkedOfNeighbours = new int[] { GetNumLinked(twoTop[0]), GetNumLinked(twoTop[1]) }; if (numLinkedOfNeighbours[0] < numLinkedOfNeighbours[1]) { return twoTop[0]; } else if (numLinkedOfNeighbours[1] < numLinkedOfNeighbours[0]) { return twoTop[1]; } else if (!tieBreak) { List<TriangleLinked> firstLinked = GetLinked(twoTop[0])[(int)edge]; List<TriangleLinked> secondLinked = GetLinked(twoTop[1])[(int)edge]; TriangleLinked firstBest = DetermineBestNextNeighbour(twoTop[0], firstLinked, edge, true); TriangleLinked secondBest = DetermineBestNextNeighbour(twoTop[1], secondLinked, edge, true); if (GetNumLinked(firstBest) < GetNumLinked(secondBest)) return twoTop[0]; else return twoTop[1]; } else if (tieBreak) { return twoTop[0]; } else { return null; } }
public Vector3 GetPoint(TriangleEdge edge, bool isFrom) { return(Triangle.GetPoint(this, edge, isFrom)); }
private static bool need_rasterize(TriangleEdge edge1, TriangleEdge edge2, TriangleEdge edge3, float2 pixelpositon, float area, out float A, out float B, out float C) { A = TriangleDoubleArea(edge2.rtpos1, edge2.rtpos2, pixelpositon); B = TriangleDoubleArea(edge3.rtpos1, edge3.rtpos2, pixelpositon); C = TriangleDoubleArea(edge1.rtpos1, edge1.rtpos2, pixelpositon); return(need_rasterize(edge1.rtpos1, edge1.rtpos2, edge1.anotherrtpos, pixelpositon)); //按照定义判断,会由于浮点数出现错误的点和缝隙 if (area * A < 0 || area * B < 0 || area * C < 0) { //不在三角形内 return(false); } //应用TOP-LEFT规则 if (A == 0) { if (B == 0) { if (!((edge2.istopedge && edge3.isleftedge) || (edge2.isleftedge && edge3.istopedge))) { return(false); } } else if (C == 0) { if (!((edge2.istopedge && edge1.isleftedge) || (edge2.isleftedge && edge1.istopedge))) { return(false); } } else { if (!(edge2.istopedge || edge2.isleftedge)) { return(false); } } } if (B == 0) { if (A == 0) { if (!((edge2.istopedge && edge3.isleftedge) || (edge2.isleftedge && edge3.istopedge))) { return(false); } } else if (C == 0) { if (!((edge1.istopedge && edge3.isleftedge) || (edge1.isleftedge && edge3.istopedge))) { return(false); } } else { if (!(edge3.istopedge || edge3.isleftedge)) { return(false); } } } if (C == 0) { if (A == 0) { if (!((edge2.istopedge && edge1.isleftedge) || (edge2.isleftedge && edge1.istopedge))) { return(false); } } else if (B == 0) { if (!((edge1.istopedge && edge2.isleftedge) || (edge1.isleftedge && edge2.istopedge))) { return(false); } } else { if (!(edge1.istopedge || edge1.isleftedge)) { return(false); } } } return(true); }
public static void Triangle(IRenderTarget renderBuffer, v2f p1, v2f p2, v2f p3, Raster[] rasters, out int totalrasters) { /* * D3D将点按z字形分成两个三角形做光栅化,而三角形的光栅化遵守TOP-LEFT规则: * 在屏幕上,若某条边位于三角形的左侧,则这条边称为LEFT边;若某条边是平行边,且位于三角形的上侧,则这条边称为TOP边。简单地讲,LEFT边是“左侧的边”,TOP边是“上面的平行边”。 * D3D规定: * (1)如果一个像素中心刚好落在三角形的一条边上,则仅当这条边为TOP或LEFT边时才画该像素; * (2)如果一个像素中心刚好落在三角形两条边的交点,则仅当两条边分别为TOP和LEFT边时才画该像素。 * TOP-LEFT规则保证了当两个三角形有重合边时,像素不会被重复渲染。对于拆分成两个三角形的点,则保证了一个顶点只覆盖一个像素。 */ totalrasters = 0; float2 pos1 = toRenderTargetPos(renderBuffer, p1.SV_POSITION.xy / p1.SV_POSITION.w); float2 pos2 = toRenderTargetPos(renderBuffer, p2.SV_POSITION.xy / p2.SV_POSITION.w); float2 pos3 = toRenderTargetPos(renderBuffer, p3.SV_POSITION.xy / p3.SV_POSITION.w); float area = TriangleDoubleArea(pos1, pos2, pos3); if (area == 0) { return; } //****确定矩形区域**** int top = (int)Math.Floor(Mathf.min(Mathf.min(pos1.y, pos2.y), pos3.y)); int bottom = (int)Math.Ceiling(Mathf.max(Mathf.max(pos1.y, pos2.y), pos3.y)); int left = (int)Math.Floor(Mathf.min(Mathf.min(pos1.x, pos2.x), pos3.x)); int right = (int)Math.Ceiling(Mathf.max(Mathf.max(pos1.x, pos2.x), pos3.x)); TriangleEdge edge1 = new TriangleEdge() { rtpos1 = pos1, rtpos2 = pos2, anotherrtpos = pos3 }; TriangleEdge edge2 = new TriangleEdge() { rtpos1 = pos2, rtpos2 = pos3, anotherrtpos = pos1 }; TriangleEdge edge3 = new TriangleEdge() { rtpos1 = pos3, rtpos2 = pos1, anotherrtpos = pos2 }; //如果用定义判断,需要处理顶边和左边,这里不用定义判断,所以不需要执行了 //edge1.checkedgeattribue(); //edge2.checkedgeattribue(); //edge3.checkedgeattribue(); if (left < 0) { left = 0; } if (top < 0) { top = 0; } //***将矩形区域扩展成2的倍数 if (left % 2 == 1) { left--; } if (right % 2 == 1) { right++; } if (top % 2 == 1) { top--; } if (bottom % 2 == 1) { bottom++; } for (int i = left; i < right; i += 2) { if (i < 0 || i >= renderBuffer.rt_width) { continue; } for (int j = top; j < bottom; j += 2) { if (j < 0 || j >= renderBuffer.rt_height) { continue; } float2 pixelpositon = new float2(i + 0.5f, j + 0.5f); //***检测相邻的四个像素。片段着色器阶段四个相邻像素Z形执行 float[] A = new float[4]; float[] B = new float[4]; float[] C = new float[4]; bool[] r_pass = new bool[4]; r_pass[0] = need_rasterize(edge1, edge2, edge3, new float2(pixelpositon.x, pixelpositon.y), area, out A[0], out B[0], out C[0]); r_pass[1] = need_rasterize(edge1, edge2, edge3, new float2(pixelpositon.x + 1, pixelpositon.y), area, out A[1], out B[1], out C[1]); r_pass[2] = need_rasterize(edge1, edge2, edge3, new float2(pixelpositon.x, pixelpositon.y + 1), area, out A[2], out B[2], out C[2]); r_pass[3] = need_rasterize(edge1, edge2, edge3, new float2(pixelpositon.x + 1, pixelpositon.y + 1), area, out A[3], out B[3], out C[3]); if (!(r_pass[0] || r_pass[1] || r_pass[2] || r_pass[3])) { continue; } for (int jj = 0; jj < 2; jj++) { for (int ii = 0; ii < 2; ii++) { int idx = jj * 2 + ii; //重心坐标系 float a = A[idx] / area; float b = B[idx] / area; float c = C[idx] / area; //返回光栅化结果 var r = rasters[totalrasters]; r.isclippass = false; r.rasterize = r_pass[idx]; r.x = i + ii; r.y = j + jj; v2f v2f = new v2f(); interpolation(ref v2f, ref p1, ref p2, ref p3, a, b, c); r.vsout = v2f; totalrasters++; } } } } }
private TriangleLinked DetermineBestNextNeighbour(TriangleLinked triangle, List <TriangleLinked> neighbours, TriangleEdge edge, bool tieBreak = false) { if (neighbours.Count == 0) { return(null); } if (neighbours.Count == 1) { return(neighbours[0]); } neighbours.Sort((a, b) => GetNumLinked(a).CompareTo(GetNumLinked(b))); TriangleLinked[] twoTop = new TriangleLinked[2]; twoTop[0] = neighbours[0]; twoTop[1] = neighbours[1]; int[] numLinkedOfNeighbours = new int[] { GetNumLinked(twoTop[0]), GetNumLinked(twoTop[1]) }; if (numLinkedOfNeighbours[0] < numLinkedOfNeighbours[1]) { return(twoTop[0]); } else if (numLinkedOfNeighbours[1] < numLinkedOfNeighbours[0]) { return(twoTop[1]); } else if (!tieBreak) { List <TriangleLinked> firstLinked = GetLinked(twoTop[0])[(int)edge]; List <TriangleLinked> secondLinked = GetLinked(twoTop[1])[(int)edge]; TriangleLinked firstBest = DetermineBestNextNeighbour(twoTop[0], firstLinked, edge, true); TriangleLinked secondBest = DetermineBestNextNeighbour(twoTop[1], secondLinked, edge, true); if (firstBest == null) { return(twoTop[1]); } else if (secondBest == null) { return(twoTop[0]); } else if (GetNumLinked(firstBest) < GetNumLinked(secondBest)) { return(twoTop[0]); } else { return(twoTop[1]); } } else if (tieBreak) { return(twoTop[0]); } else { return(null); } }
Tuple <ModelBase.FaceListDef, List <int> > GetStripAndIndicesForStartingEvenEdge( TriangleLinked start, TriangleEdge startForwardEdge) { List <int> stripIndices = new List <int>(); List <TriangleRotation> stripRotations = new List <TriangleRotation>(); List <TriangleLinked> linked = GetLinked(start)[(int)startForwardEdge]; TriangleLinked bestNeighbour = DetermineBestNextNeighbour(start, linked, startForwardEdge); if (bestNeighbour == null) { return(new Tuple <ModelBase.FaceListDef, List <int> >(new ModelBase.FaceListDef(), new List <int>())); } TriangleRotation startRotation = (TriangleRotation)((int)(startForwardEdge - TriangleEdge.Edge_BC + 3) % 3); TriangleLinked t = start; TriangleEdge currentEdge = startForwardEdge; TriangleRotation currentRotation = startRotation; bool even = true; int index_t = -1; while (t != null && !stripIndices.Contains((index_t = m_Triangles.IndexOf(t)))) { stripIndices.Add(index_t); stripRotations.Add(currentRotation); linked = GetLinked(t)[(int)currentEdge]; bestNeighbour = DetermineBestNextNeighbour(t, linked, currentEdge); t = bestNeighbour; even = !even; if (t != null) { // Determine rotation and the edge to be used to get the next face ModelBase.FaceDef triangleC_CW = new ModelBase.FaceDef(3); if (even) { triangleC_CW.m_Vertices[0] = t.m_Triangle.m_Vertices[0]; triangleC_CW.m_Vertices[1] = t.m_Triangle.m_Vertices[1]; triangleC_CW.m_Vertices[2] = t.m_Triangle.m_Vertices[2]; } else { triangleC_CW.m_Vertices[0] = t.m_Triangle.m_Vertices[2]; triangleC_CW.m_Vertices[1] = t.m_Triangle.m_Vertices[1]; triangleC_CW.m_Vertices[2] = t.m_Triangle.m_Vertices[0]; } // The edge of the vertices which match the preceding triangle's TriangleEdge linkBackEdge = TriangleEdge.Edge_AB; // The vertices which match the preceding triangle's ModelBase.VertexDef[] currentMatchedEdge = new ModelBase.VertexDef[2]; TriangleLinked previous = m_Triangles[stripIndices[stripIndices.Count - 1]]; currentMatchedEdge[0] = previous.m_Triangle.m_Vertices[(int)(currentEdge + 0) % 3]; currentMatchedEdge[1] = previous.m_Triangle.m_Vertices[(int)(currentEdge + 1) % 3]; // Find the edge in the current triangle which if odd has been made CW which matches // that from the preceding triangle. This will be set as the current triangle's first, // or 'AB' edge and the next edge (next two vertices) will be used to match the next // triangle. for (int i = 0; i < 3; i++) { ModelBase.VertexDef[] edge = new ModelBase.VertexDef[2]; edge[0] = triangleC_CW.m_Vertices[(i + 0) % 3]; edge[1] = triangleC_CW.m_Vertices[(i + 1) % 3]; if (edge.Except(currentMatchedEdge).Count() == 0) { linkBackEdge = (TriangleEdge)i; break; } } TriangleEdge nextEdgeNoC_CW = (TriangleEdge)((int)(linkBackEdge + 1) % 3); TriangleEdge nextEdge = nextEdgeNoC_CW; if (!even) { // If odd, nextEdgeNoC_CW points to the edge to be used if written CW, however // all triangles have been read in as CCW so need to get the corresponding edge // in CCW version. ModelBase.VertexDef[] nextEdgeNoC_CW_Vertices = new ModelBase.VertexDef[2]; nextEdgeNoC_CW_Vertices[0] = triangleC_CW.m_Vertices[(int)(nextEdgeNoC_CW + 0) % 3]; nextEdgeNoC_CW_Vertices[1] = triangleC_CW.m_Vertices[(int)(nextEdgeNoC_CW + 1) % 3]; for (int i = 0; i < 3; i++) { ModelBase.VertexDef[] ccwEdge = new ModelBase.VertexDef[2]; ccwEdge[0] = t.m_Triangle.m_Vertices[(i + 0) % 3]; ccwEdge[1] = t.m_Triangle.m_Vertices[(i + 1) % 3]; if (nextEdgeNoC_CW_Vertices.Except(ccwEdge).Count() == 0) { nextEdge = (TriangleEdge)i; break; } } } // Now we need to determine the required rotation of the current triangle so that for // even triangles the new vertex in at index 0 and for odd triangles it occurs at // index 2. ModelBase.VertexDef uniqueVertex = t.m_Triangle.m_Vertices.Except(previous.m_Triangle.m_Vertices).ElementAt(0); int uniqueVertexIndex = Array.IndexOf(t.m_Triangle.m_Vertices, uniqueVertex); TriangleRotation requiredRotation = (even) ? (TriangleRotation)((uniqueVertexIndex - 2 + 3) % 3) : (TriangleRotation)(uniqueVertexIndex); currentRotation = requiredRotation; currentEdge = nextEdge; // To best understand how this works, debug and step-through how the following model is handled: // // An example: // Faces as defined in model (all Counter-Clockwise (CCW)): // f 1 2 3 // f 4 1 3 // f 4 5 1 // Build strip from edge CA. // # 2 3 1 (LS) <- Need to Left Shift vertices so that CA is the second edge (in a tri. strip it's // always the second edge that's shared - see diagram at top). // # 3 1 4 (4 1 3) <- For odd faces the new vertex must be at index [0] // No Rot required, link-back CW: AB, CW forward: AB + 1 = BC, // CCW forward: edge in CCW that contains vertices in (CW forward) = AB // The next triangle is the one that shares the CCW edge AB (vertices 1 and 4) // # 1 4 5 (RS) <- Even face the new vertex needs to be in index [2] so need to Right Shift vertices // Repeat steps as for above face but don't need to worry about converting between CCW and CW order } } ModelBase.FaceListDef tStrip = new ModelBase.FaceListDef(ModelBase.PolyListType.TriangleStrip); for (int i = 0; i < stripIndices.Count; i++) { TriangleRotation requiredRotation = (TriangleRotation)stripRotations[i]; ModelBase.FaceDef rotated = new ModelBase.FaceDef(3); rotated.m_Vertices[0] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(0 + requiredRotation) % 3)]; rotated.m_Vertices[1] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(1 + requiredRotation) % 3)]; rotated.m_Vertices[2] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(2 + requiredRotation) % 3)]; tStrip.m_Faces.Add(rotated); } return(new Tuple <ModelBase.FaceListDef, List <int> >(tStrip, stripIndices)); }
public Vector3 GetUncommonPoint(TriangleEdge edge0, TriangleEdge edge1) { return(Triangle.GetUncommonPoint(this, edge0, edge1)); }
public Vector3 GetOppositePoint(TriangleEdge edge) { return(Triangle.GetOppositePoint(this, edge)); }
private static double GetLength(ITriangleIndexed triangle, TriangleEdge edge, SortedList<Tuple<int, int>, double> lengths) { return GetLength(triangle.GetIndex(edge, true), triangle.GetIndex(edge, false), lengths); }
private static void PruneBrainLinks_Remove(TriangleIndexed triangle, TriangleEdge edge, List<Tuple<int[], int[]>> links) { int index1 = triangle.GetIndex(edge, true); int index2 = triangle.GetIndex(edge, false); Tuple<int[], int[]> existing = null; bool? is1in1 = null; // Find and remove the link that contains this edge for (int cntr = 0; cntr < links.Count; cntr++) { if (links[cntr].Item1.Contains(index1) && links[cntr].Item2.Contains(index2)) { is1in1 = true; } else if (links[cntr].Item1.Contains(index2) && links[cntr].Item2.Contains(index1)) { is1in1 = false; } else { continue; } existing = links[cntr]; links.RemoveAt(cntr); break; } if (existing == null) { //throw new ArgumentException("Didn't find the link"); // A neighbor triangle probably removed it return; } // Add back if there were more than 2 involved if (existing.Item1.Length == 1 && existing.Item2.Length == 1) { // This link only holds one item on each side. It's already removed from the list, so there is nothing left to do return; } int[] newItem1 = PruneBrainLinks_Remove_Reduce(existing.Item1, index1, index2, is1in1.Value); int[] newItem2 = PruneBrainLinks_Remove_Reduce(existing.Item2, index2, index1, is1in1.Value); links.Add(Tuple.Create(newItem1, newItem2)); }
private static void PruneBrainLinks_Merge(TriangleIndexed triangle, TriangleEdge edge, List<Tuple<int[], int[]>> links) { // Figure out which indexes to look for int[] pair = new[] { triangle.GetIndex(edge, true), triangle.GetIndex(edge, false) }; int other = triangle.IndexArray.First(o => !pair.Contains(o)); // Extract the affected links out of the list List<Tuple<int[], int[]>> affected = new List<Tuple<int[], int[]>>(); int index = 0; while (index < links.Count) { var current = links[index]; if (current.Item1.Contains(other) && current.Item2.Any(o => pair.Contains(o))) { affected.Add(current); links.RemoveAt(index); } else if (current.Item2.Contains(other) && current.Item1.Any(o => pair.Contains(o))) { affected.Add(Tuple.Create(current.Item2, current.Item1)); // reversing them so that Item1 is always other links.RemoveAt(index); } else { index++; } } // Combine the affected links (there shouldn't be more than two) var merged = Tuple.Create( affected.SelectMany(o => o.Item1).Distinct().ToArray(), affected.SelectMany(o => o.Item2).Distinct().ToArray()); links.Add(merged); }
Tuple<ModelBase.FaceListDef, List<int>> GetStripAndIndicesForStartingEvenEdge( TriangleLinked start, TriangleEdge startForwardEdge) { List<int> stripIndices = new List<int>(); List<TriangleRotation> stripRotations = new List<TriangleRotation>(); List<TriangleLinked> linked = GetLinked(start)[(int)startForwardEdge]; TriangleLinked bestNeighbour = DetermineBestNextNeighbour(start, linked, startForwardEdge); if (bestNeighbour == null) return new Tuple<ModelBase.FaceListDef, List<int>>(new ModelBase.FaceListDef(), new List<int>()); TriangleRotation startRotation = (TriangleRotation)((int)(startForwardEdge - TriangleEdge.Edge_BC + 3) % 3); TriangleLinked t = start; TriangleEdge currentEdge = startForwardEdge; TriangleRotation currentRotation = startRotation; bool even = true; int index_t = -1; while (t != null && !stripIndices.Contains((index_t = m_Triangles.IndexOf(t)))) { stripIndices.Add(index_t); stripRotations.Add(currentRotation); linked = GetLinked(t)[(int)currentEdge]; bestNeighbour = DetermineBestNextNeighbour(t, linked, currentEdge); t = bestNeighbour; even = !even; if (t != null) { // Determine rotation and the edge to be used to get the next face ModelBase.FaceDef triangleC_CW = new ModelBase.FaceDef(3); if (even) { triangleC_CW.m_Vertices[0] = t.m_Triangle.m_Vertices[0]; triangleC_CW.m_Vertices[1] = t.m_Triangle.m_Vertices[1]; triangleC_CW.m_Vertices[2] = t.m_Triangle.m_Vertices[2]; } else { triangleC_CW.m_Vertices[0] = t.m_Triangle.m_Vertices[2]; triangleC_CW.m_Vertices[1] = t.m_Triangle.m_Vertices[1]; triangleC_CW.m_Vertices[2] = t.m_Triangle.m_Vertices[0]; } // The edge of the vertices which match the preceding triangle's TriangleEdge linkBackEdge = TriangleEdge.Edge_AB; // The vertices which match the preceding triangle's ModelBase.VertexDef[] currentMatchedEdge = new ModelBase.VertexDef[2]; TriangleLinked previous = m_Triangles[stripIndices[stripIndices.Count - 1]]; currentMatchedEdge[0] = previous.m_Triangle.m_Vertices[(int)(currentEdge + 0) % 3]; currentMatchedEdge[1] = previous.m_Triangle.m_Vertices[(int)(currentEdge + 1) % 3]; // Find the edge in the current triangle which if odd has been made CW which matches // that from the preceding triangle. This will be set as the current triangle's first, // or 'AB' edge and the next edge (next two vertices) will be used to match the next // triangle. for (int i = 0; i < 3; i++) { ModelBase.VertexDef[] edge = new ModelBase.VertexDef[2]; edge[0] = triangleC_CW.m_Vertices[(i + 0) % 3]; edge[1] = triangleC_CW.m_Vertices[(i + 1) % 3]; if (edge.Except(currentMatchedEdge).Count() == 0) { linkBackEdge = (TriangleEdge)i; break; } } TriangleEdge nextEdgeNoC_CW = (TriangleEdge)((int)(linkBackEdge + 1) % 3); TriangleEdge nextEdge = nextEdgeNoC_CW; if (!even) { // If odd, nextEdgeNoC_CW points to the edge to be used if written CW, however // all triangles have been read in as CCW so need to get the corresponding edge // in CCW version. ModelBase.VertexDef[] nextEdgeNoC_CW_Vertices = new ModelBase.VertexDef[2]; nextEdgeNoC_CW_Vertices[0] = triangleC_CW.m_Vertices[(int)(nextEdgeNoC_CW + 0) % 3]; nextEdgeNoC_CW_Vertices[1] = triangleC_CW.m_Vertices[(int)(nextEdgeNoC_CW + 1) % 3]; for (int i = 0; i < 3; i++) { ModelBase.VertexDef[] ccwEdge = new ModelBase.VertexDef[2]; ccwEdge[0] = t.m_Triangle.m_Vertices[(i + 0) % 3]; ccwEdge[1] = t.m_Triangle.m_Vertices[(i + 1) % 3]; if (nextEdgeNoC_CW_Vertices.Except(ccwEdge).Count() == 0) { nextEdge = (TriangleEdge)i; break; } } } // Now we need to determine the required rotation of the current triangle so that for // even triangles the new vertex in at index 0 and for odd triangles it occurs at // index 2. ModelBase.VertexDef uniqueVertex = t.m_Triangle.m_Vertices.Except(previous.m_Triangle.m_Vertices).ElementAt(0); int uniqueVertexIndex = Array.IndexOf(t.m_Triangle.m_Vertices, uniqueVertex); TriangleRotation requiredRotation = (even) ? (TriangleRotation)((uniqueVertexIndex - 2 + 3) % 3) : (TriangleRotation)(uniqueVertexIndex); currentRotation = requiredRotation; currentEdge = nextEdge; // To best understand how this works, debug and step-through how the following model is handled: // // An example: // Faces as defined in model (all Counter-Clockwise (CCW)): // f 1 2 3 // f 4 1 3 // f 4 5 1 // Build strip from edge CA. // # 2 3 1 (LS) <- Need to Left Shift vertices so that CA is the second edge. // # 3 1 4 (4 1 3) <- For odd faces the new vertex must be at index [0] // No Rot required, link-back CW: AB, CW forward: AB + 1 = BC, // CCW forward: edge in CCW that contains vertices in (CW forward) = AB // The next triangle is the one that shares the CCW edge AB (vertices 1 and 4) // # 1 4 5 (RS) <- Even face the new vertex needs to be in index [2] so need to Right Shift vertices // Repeat steps as for above face but don't need to worry about converting between CCW and CW order } } ModelBase.FaceListDef tStrip = new ModelBase.FaceListDef(ModelBase.PolyListType.TriangleStrip); even = true; for (int i = 0; i < stripIndices.Count; i++) { TriangleRotation requiredRotation = (TriangleRotation)stripRotations[i]; ModelBase.FaceDef rotated = new ModelBase.FaceDef(3); rotated.m_Vertices[0] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(0 + requiredRotation) % 3)]; rotated.m_Vertices[1] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(1 + requiredRotation) % 3)]; rotated.m_Vertices[2] = m_Triangles[stripIndices[i]].m_Triangle.m_Vertices[((int)(2 + requiredRotation) % 3)]; tStrip.m_Faces.Add(rotated); even = !even; } return new Tuple<ModelBase.FaceListDef, List<int>>(tStrip, stripIndices); }
public Vector3 GetEdgeMidpoint(TriangleEdge edge) { return(Triangle.GetEdgeMidpoint(this, edge)); }
public float GetEdgeLength(TriangleEdge edge) { return(Triangle.GetEdgeLength(this, edge)); }