public static void DrawSceneGUIMouseOver(SkinAttachmentTarget driver)
        {
            var mouseScreen   = Event.current.mousePosition;
            var mouseWorldRay = HandleUtility.GUIPointToWorldRay(mouseScreen);

            var objectRayPos = driver.transform.InverseTransformPoint(mouseWorldRay.origin);
            var objectRayDir = driver.transform.InverseTransformDirection(mouseWorldRay.direction);

            var meshInfo = driver.GetCachedMeshInfo();

            if (meshInfo.valid == false)
            {
                return;
            }

            var vertex = meshInfo.meshVertexBSP.RaycastApprox(ref objectRayPos, ref objectRayDir, 10);

            if (vertex == -1)
            {
                return;
            }

            using (var scope = new Handles.DrawingScope(Color.blue, driver.transform.localToWorldMatrix))
            {
                const int maxDepth = 3;

                var triangleColor = Handles.color;
                var triangleDepth = 0;

                using (var triangleBFS = new UnsafeBFS(meshInfo.meshAdjacency.triangleCount))
                {
                    foreach (var triangle in meshInfo.meshAdjacency.vertexTriangles[vertex])
                    {
                        triangleBFS.Insert(triangle);
                    }

                    while (triangleBFS.MoveNext() && triangleBFS.depth < maxDepth)
                    {
                        if (triangleDepth < triangleBFS.depth)
                        {
                            triangleDepth = triangleBFS.depth;
                            Handles.color = Color.Lerp(triangleColor, Color.clear, Mathf.InverseLerp(0, maxDepth, triangleDepth));
                        }

                        var _e = meshInfo.meshAdjacency.triangleVertices[triangleBFS.position].GetEnumerator();
                        int v0 = _e.ReadNext();
                        int v1 = _e.ReadNext();
                        int v2 = _e.ReadNext();

                        Handles.DrawLine(meshInfo.meshBuffers.vertexPositions[v0], meshInfo.meshBuffers.vertexPositions[v1]);
                        Handles.DrawLine(meshInfo.meshBuffers.vertexPositions[v1], meshInfo.meshBuffers.vertexPositions[v2]);
                        Handles.DrawLine(meshInfo.meshBuffers.vertexPositions[v2], meshInfo.meshBuffers.vertexPositions[v0]);

                        foreach (var triangle in meshInfo.meshAdjacency.triangleTriangles[triangleBFS.position])
                        {
                            triangleBFS.Insert(triangle);
                        }
                    }
                }
            }
        }
        public void BuildFrom(MeshAdjacency meshAdjacency, int[] roiIndices, int roiBoundaryLevels, int[] roiConstraintIndices = null)
        {
            unsafe
            {
                using (var visited = new UnsafeArrayBool(meshAdjacency.vertexCount))
                    using (var visitedBoundary = new UnsafeArrayBool(meshAdjacency.vertexCount))
                        using (var visitor = new UnsafeBFS(meshAdjacency.vertexCount))
                        {
                            // find boundary
                            visited.Clear(false);
                            visitedBoundary.Clear(false);
                            visitor.Clear();

                            int visitedCount         = 0;
                            int visitedBoundaryCount = 0;

                            foreach (int i in roiIndices)
                            {
                                visited.val[i] = true;
                                visitedCount++;
                                visitor.Ignore(i);
                            }

                            foreach (int i in roiIndices)
                            {
                                foreach (var j in meshAdjacency.vertexVertices[i])
                                {
                                    visitor.Insert(j);
                                }
                            }

                            // step boundary
                            while (visitor.MoveNext())
                            {
                                int i = visitor.position;

                                visited.val[i] = true;
                                visitedCount++;
                                visitedBoundary.val[i] = true;
                                visitedBoundaryCount++;

                                if (visitor.depth < roiBoundaryLevels)
                                {
                                    foreach (var j in meshAdjacency.vertexVertices[i])
                                    {
                                        visitor.Insert(j);
                                    }
                                }
                            }

                            // add constraints
                            if (roiConstraintIndices != null)
                            {
                                foreach (int i in roiConstraintIndices)
                                {
                                    if (visited.val[i])
                                    {
                                        if (visitedBoundary.val[i] == false)
                                        {
                                            visitedBoundary.val[i] = true;
                                            visitedBoundaryCount++;
                                        }
                                    }
                                    else
                                    {
                                        Debug.LogWarning("ignoring user constraint outside ROI: vertex " + i);
                                    }
                                }
                            }

                            // build translations
                            internalCount = 0;
                            externalCount = meshAdjacency.vertexCount;

                            internalFromExternal = new int[externalCount];
                            externalFromInternal = new int[visitedCount];

                            for (int i = 0; i != meshAdjacency.vertexCount; i++)
                            {
                                if (visited.val[i])
                                {
                                    int internalIndex = internalCount++;
                                    externalFromInternal[internalIndex] = i;
                                    internalFromExternal[i]             = internalIndex;
                                }
                                else
                                {
                                    internalFromExternal[i] = -1;
                                }
                            }

                            // find constraint indices
                            constraintIndices = new int[visitedBoundaryCount];
                            constraintWeight  = 1.0;

                            int constraintCount = 0;
                            for (int i = 0; i != internalCount; i++)
                            {
                                if (visitedBoundary.val[externalFromInternal[i]])
                                {
                                    constraintIndices[constraintCount++] = i;
                                }
                            }

                            // count unconstrained laplacian non-zero fields
                            int nzmax = internalCount;
                            for (int i = 0; i != internalCount; i++)
                            {
                                nzmax += InternalValence(meshAdjacency, i);
                            }

                            // build Ls
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Ls", 0.0f);
                            var Ls_storage = new CoordinateStorage <double>(internalCount, internalCount, nzmax);
                            for (int i = 0; i != internalCount; i++)            // D
                            {
                                //TODO proper fix
                                //Ls_storage.At(i, i, InternalValence(meshAdjacency, i));
                                Ls_storage.At(i, i, Mathf.Max(1, InternalValence(meshAdjacency, i)));
                            }
                            for (int i = 0; i != internalCount; i++)            // A
                            {
                                foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]])
                                {
                                    int j = internalFromExternal[k];
                                    if (j != -1)
                                    {
                                        Ls_storage.At(i, j, -1.0);
                                    }
                                }
                            }
                            Ls = Converter.ToCompressedColumnStorage(Ls_storage) as SparseMatrix;

                            // build Lc
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build Lc", 0.0f);
                            var Lc_storage = new CoordinateStorage <double>(internalCount + constraintCount, internalCount, nzmax + constraintCount);
                            for (int i = 0; i != internalCount; i++)
                            {
                                //TODO proper fix
                                //Lc_storage.At(i, i, InternalValence(meshAdjacency, i));
                                Lc_storage.At(i, i, Mathf.Max(1, InternalValence(meshAdjacency, i)));
                            }
                            for (int i = 0; i != internalCount; i++)
                            {
                                foreach (var k in meshAdjacency.vertexVertices[externalFromInternal[i]])
                                {
                                    int j = internalFromExternal[k];
                                    if (j != -1)
                                    {
                                        Lc_storage.At(i, j, -1.0);
                                    }
                                }
                            }
                            for (int i = 0; i != constraintIndices.Length; i++)
                            {
                                Lc_storage.At(internalCount + i, constraintIndices[i], constraintWeight);
                            }
                            Lc = Converter.ToCompressedColumnStorage(Lc_storage) as SparseMatrix;

                            // build LcT
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT", 0.0f);
                            LcT = Lc.Transpose() as SparseMatrix;

                            // build LcT_Lc
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc", 0.0f);
                            LcT_Lc = LcT.Multiply(Lc) as SparseMatrix;

                            // build LcT_Lc_chol
                            EditorUtilityProxy.DisplayProgressBar("MeshLaplacian", "build LcT_Lc_chol", 0.0f);
                            LcT_Lc_chol = SparseCholesky.Create(LcT_Lc, ColumnOrdering.MinimumDegreeAtPlusA);

                            // done
                            EditorUtilityProxy.ClearProgressBar();
                        }
            }
        }