/// <summary> /// Add a new vertex to the field, breaking existing triangles as necessary. /// </summary> /// <param name="vtx">Position of the new vertex to add.</param> private void AddVertexSubdividing(Vector3 vtx) { vertices.Add(vtx); int newVertIdx = vertices.Count - 1; // Find closest triangle IndexedBary bary = FindTriangle(vtx); Debug.Assert(bary.bary.IsInterior, "Should be contained by background seed vertices."); // Find closest edge Edge edge = ClosestEdge(bary); // Find any other triangle with that edge int oppositieTriIdx = FindTriangleWithEdge(edge, bary.triangle); bool canSplit = CanSplit(edge, oppositieTriIdx, newVertIdx); if (canSplit) { AddVertexSplitEdge(edge, bary.triangle, oppositieTriIdx, newVertIdx); } else { AddVertexMidTriangle(bary.triangle, newVertIdx); } }
/// <summary> /// Brute force search to find a triangle that the query position projects onto. /// </summary> /// <param name="pos">The query position.</param> /// <returns>The triangle projected onto, with interpolation information.</returns> /// <remarks> /// Note the returned value is never null. Also, if there is any data to query (Add() has been /// called with a vertex list of length > 0), then the returned value.bary will also be non-null /// and valid. /// However, if there is no data to query, then returned_value.bary will be null. /// </remarks> private IndexedBary FindTriangle(Vector3 pos) { IndexedBary bary = new IndexedBary(); bary.bary = new Interpolant(); for (int i = 0; i < triangles.Count; ++i) { Triangle tri = triangles[i]; Vector3 p0 = vertices[tri.idx0]; p0.y = 0; Vector3 p1 = vertices[tri.idx1]; p1.y = 0; Vector3 p2 = vertices[tri.idx2]; p2.y = 0; /// Note area will be negative, because this is xz, not xy, but that will cancel with the negative cross products below. float area = Vector3.Cross(p2 - p1, p0 - p1).y; if (area >= 0) { area = 0; } Debug.Assert(area < 0, "Degenerate triangle in Find"); Vector3 ps = new Vector3(pos.x, 0, pos.z); bary.bary.weights[0] = Vector3.Cross(p2 - p1, ps - p1).y / area; bary.bary.weights[1] = Vector3.Cross(p0 - p2, ps - p2).y / area; bary.bary.weights[2] = Vector3.Cross(p1 - p0, ps - p0).y / area; if (bary.bary.IsInterior) { bary.triangle = i; bary.bary.idx[0] = tri.idx0; bary.bary.idx[1] = tri.idx1; bary.bary.idx[2] = tri.idx2; return(bary); } } Debug.Assert(false, "Found no triangle for which this position is interior."); bary.bary = null; return(bary); }
/// <summary> /// Find the edge closest to the interpolation point, or equivalently the edge opposite the /// vertex with the weakest influence. /// </summary> /// <param name="bary">The triangular interpolation.</param> /// <returns>Edge data for strongest edge.</returns> private Edge ClosestEdge(IndexedBary bary) { Triangle tri = triangles[bary.triangle]; Edge edge; edge.idx0 = tri.idx1; edge.idx1 = tri.idx2; float minWeight = bary.bary.weights[0]; if (bary.bary.weights[1] < minWeight) { edge.idx0 = tri.idx0; edge.idx1 = tri.idx2; minWeight = bary.bary.weights[1]; } if (bary.bary.weights[2] < minWeight) { edge.idx0 = tri.idx0; edge.idx1 = tri.idx1; } return(edge); }