/// <summary>
        /// For mesh g, create a new mesh from the passed selected faces,
        /// discarding un-referenced data and generating new index, vertex & face buffers.
        /// </summary>
        public static IGeometryAttributes SelectFaces(this IGeometryAttributes g, IArray <int> faces)
        {
            // Early exit, if all selected no need to do anything
            if (g.NumFaces == faces.Count)
            {
                return(g);
            }

            // Early exit, if none selected no need to do anything
            if (faces.Count == 0)
            {
                return(null);
            }

            // First, get all the indices for this array of faces
            var oldIndices    = g.GetAttributeIndex();
            var oldSelIndices = new int[faces.Count * g.NumCornersPerFace];

            // var oldIndices = faces.SelectMany(f => f.Indices());
            for (int i = 0; i < faces.Count; i++)
            {
                for (int t = 0; t < 3; t++)
                {
                    oldSelIndices[i * 3 + t] = oldIndices.Data[faces[i] * g.NumCornersPerFace + t];
                }
            }

            // We need to create list of newIndices, and remapping
            // of oldVertices to newVertices
            var newIndices = (-1).Repeat(oldSelIndices.Length).ToArray();
            // Each index could potentially be in the new mesh
            var indexLookup = (-1).Repeat(oldIndices.ElementCount).ToArray();

            // Build mapping.  For each index, if the vertex it has
            // already been referred to has been mapped, use the
            // mapped index.  Otherwise, remember that we need this vertex
            var numUsedVertices = 0;
            // remapping from old vert array => new vert array
            // Cache built of the old indices of vertices that are used
            // should be equivalent to indexLookup.Where(i => i != -1)
            var oldVertices  = g.GetAttributePosition();
            var usedVertices = (-1).Repeat(oldVertices.ElementCount).ToArray();

            for (var i = 0; i < oldSelIndices.Length; ++i)
            {
                var oldIndex = oldSelIndices[i];
                var newIndex = indexLookup[oldIndex];
                if (newIndex < 0)
                {
                    // remapping from old vert array => new vert array
                    usedVertices[numUsedVertices] = oldIndex;
                    newIndex = indexLookup[oldIndex] = numUsedVertices++;
                }
                newIndices[i] = newIndex;
            }

            //var faceRemapping = faces.Select(f => f.Index);
            var vertRemapping   = usedVertices.Take(numUsedVertices).ToIArray();
            var cornerRemapping = g.FaceIndicesToCornerIndices(faces);

            return(g.VertexAttributes()
                   .Select(attr => attr.Remap(vertRemapping))
                   .Concat(g.NoneAttributes())
                   .Concat(g.FaceAttributes().Select(attr => attr.Remap(faces)))
                   .Concat(g.EdgeAttributes().Select(attr => attr.Remap(cornerRemapping)))
                   .Concat(g.CornerAttributes().Select(attr => attr.Remap(cornerRemapping)))
                   .Concat(g.WholeGeometryAttributes())
                   .ToGeometryAttributes()
                   .SetAttribute(newIndices.ToIndexAttribute()));
        }
 /// <summary>
 /// Leaves the vertex buffer intact and creates a new geometry that remaps all of the group, corner, and face data.
 /// The newFaces array is a list of indices into the old face array.
 /// Note: sub-geometries are lost.
 /// </summary>
 public static IGeometryAttributes RemapFaces(this IGeometryAttributes g, IArray <int> faceRemap)
 => g.RemapFacesAndCorners(faceRemap, g.FaceIndicesToCornerIndices(faceRemap));