/// <inheritdocs />
        public Interpolant Find(Vector3 pos)
        {
            Interpolant bary = FindTriangleOrEdgeOrVertex(pos);

            AdjustForBoundingIndices(bary);
            return(bary);
        }
        /// <summary>
        /// Query for a triangle containing the projection of this position, or failing that,
        /// find the closest point on the closest edge.
        /// </summary>
        /// <param name="pos">The query position.</param>
        /// <returns>The interpolant, or null if there is no data to query.</returns>
        private Interpolant FindTriangleOrEdgeOrVertex(Vector3 pos)
        {
            if (PointInsideBounds(pos))
            {
                Interpolant bary = FindTriangle(pos).bary;

                if (IsInteriorTriangle(bary))
                {
                    return(bary);
                }
            }
            return(FindClosestExteriorEdge(pos));
        }
 /// <summary>
 /// Determine if the interpolant is interpolating between real vertices, or is
 /// exterior (i.e. one or more vertices are bounding dummy vertices).
 /// </summary>
 /// <param name="bary"></param>
 /// <returns></returns>
 private bool IsInteriorTriangle(Interpolant bary)
 {
     if (bary == null)
     {
         return(false);
     }
     for (int i = 0; i < bary.idx.Length; ++i)
     {
         if (IsBoundary(bary.idx[i]))
         {
             return(false);
         }
     }
     return(true);
 }
 /// <summary>
 /// Adjust the indices accounting for the 4 dummy verts introduced in SetBounds()/SeedQuad().
 /// </summary>
 /// <param name="bary">The interpolant whose indices need adjusting.</param>
 /// <remarks>
 /// Note that with this current implementation, once the indices are corrected, it's no longer possible to tell whether they are boundary vertices.
 /// </remarks>
 private void AdjustForBoundingIndices(Interpolant bary)
 {
     if (bary != null)
     {
         for (int i = 0; i < 3; ++i)
         {
             if (!IsBoundary(bary.idx[i]))
             {
                 bary.idx[i] = bary.idx[i] - 4;
             }
             else
             {
                 Debug.Assert(bary.weights[i] == 0.0f);
             }
         }
     }
 }
        /// <summary>
        /// Find the exterior edge with the closest point on the edge to the query position.
        /// </summary>
        /// <param name="pos">The query position.</param>
        /// <returns>A triangle interpolant that will evaluate to the interpolation between the two edge endpoints. Returns null if there are no exterior edges.</returns>
        /// <remarks>
        /// The third triangle vertex always has index 0 and weight 0.
        /// </remarks>
        private Interpolant FindClosestExteriorEdge(Vector3 pos)
        {
            if (exteriorEdges.Count == 0)
            {
                if (vertices.Count == 5)
                {
                    /// There is a single real vertex, so no exterior edges.
                    /// That's okay, the single vertex wins all the weight.
                    Interpolant singleVert = new Interpolant();
                    singleVert.idx[0]     = singleVert.idx[1] = singleVert.idx[2] = 4;
                    singleVert.weights[0] = 1.0f;
                    singleVert.weights[1] = singleVert.weights[2] = 0.0f;
                    return(singleVert);
                }
                return(null);
            }
            int   closestEdge     = -1;
            float closestDistance = float.MaxValue;
            float closestParm     = 0.0f;

            for (int i = 0; i < exteriorEdges.Count; ++i)
            {
                PointOnEdge point = PositionOnEdge(exteriorEdges[i], pos);

                if (point.distanceSqr < closestDistance)
                {
                    closestEdge     = i;
                    closestDistance = point.distanceSqr;
                    closestParm     = point.parm;
                }
            }
            Debug.Assert(closestEdge >= 0, "If there are any edges, there must be a closest one.");
            Edge        edge = exteriorEdges[closestEdge];
            Interpolant bary = new Interpolant();

            bary.idx[0]     = edge.idx0;
            bary.idx[1]     = edge.idx1;
            bary.idx[2]     = 0;
            bary.weights[0] = 1.0f - closestParm;
            bary.weights[1] = closestParm;
            bary.weights[2] = 0;

            return(bary);
        }
        private List <WeightedPose> ComputePoseWeights(Vector3 lockedHeadPosition)
        {
            weightedPoses.Clear();

            Triangulator.Interpolant bary = triangulator.Find(lockedHeadPosition);
            if (bary != null)
            {
                for (int i = 0; i < 3; ++i)
                {
                    weightedPoses.Add(new WeightedPose()
                    {
                        pose   = ComputePinnedFromLocked(activePoses[bary.idx[i]]),
                        weight = bary.weights[i]
                    });
                }
            }
            else
            {
                Debug.Assert(activePoses.Count == 0, "Failed to find an interpolant even though there are pins active.");
            }
            return(weightedPoses);
        }