Пример #1
0
        /// <summary>
        /// Barycentric Parameterization
        /// Covers barycentric methods which need a fully defined boundary
        /// A particular method can be chosen by creating an appropriate Laplacian matrix
        /// See also (Floater 2003)
        /// </summary>
        /// <param name="meshin">input mesh, after solving its texture coordinates in vertex traits will be adjusted</param>
        /// <param name="meshout">an flattened output mesh with only X,Y coordinates set, Z is set to 0</param>
        private void BarycentricMapping(TriangleMesh meshin, out TriangleMesh meshout)
        {
            /// init an mesh that serves for output of the 2d parametrized mesh
            meshout = meshin.Copy();
            //meshOut = meshIn;

            /// get lenghts
            var vertexCount      = meshout.Vertices.Count;
            var boundaryVertices = meshout.Vertices.Where(x => x.OnBoundary).ToList();

            /// right hand side (RHS)
            var bu = new double[vertexCount];
            var bv = new double[vertexCount];
            var b0 = new double[vertexCount];

            // TODO : For geometry images, L mapped edges require splitting. Adaptive length parameterization should be sufficient for crack prediction however
            FixBoundaryToShape(boundaryVertices, bu, bv);

            var laplacian = MeshLaplacian.SelectedLaplacian == MeshLaplacian.Type.Harmonic ?
                            MeshLaplacian.CreateBoundedHarmonicLaplacian(meshin, 1d, 0d, true) :
                            MeshLaplacian.SelectedLaplacian == MeshLaplacian.Type.MeanValue ?
                            MeshLaplacian.CreateBoundedMeanLaplacian(meshin, 1d, 0d, true) :
                            MeshLaplacian.CreateBoundedUniformLaplacian(meshin, 1d, 0d, true);

            var qrSolver = QR.Create(laplacian.Compress());
            var success  = qrSolver.Solve(bu) && qrSolver.Solve(bv);

            /// update mesh positions
            MeshLaplacian.UpdateMesh(meshout, bu, bv, b0, bu, bv);
            MeshLaplacian.UpdateMesh(meshin, bu, bv);
        }
Пример #2
0
        /// <summary>
        /// Performs global least-squares mesh smoothing with the extended matrix
        /// [ L  ] [x'] =       [ 0 ]
        /// [ Wp ]        [ Wp ][ x ]
        /// </summary>
        private void GlobalLeastSquaresSmoothing(TriangleMesh mesh)
        {
            /// output vertex positions (after solving/smoothing)
            double[] xx, xy, xz;
            xx = xy = xz = null;



            /// update mesh
            MeshLaplacian.UpdateMesh(mesh, xx, xy, xz);
        }
Пример #3
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="mesh"></param>
        private void TriangleQualityOptimization(TriangleMesh mesh)
        {
            /// get n
            var n = mesh.Vertices.Count;

            /// output vertex positions (after solving/smoothing)
            double[] xx, xy, xz;
            xx = xy = xz = null;



            /// update mesh
            MeshLaplacian.UpdateMesh(mesh, xx, xy, xz);
        }
Пример #4
0
        /// <summary>
        /// performs the Explicit Euler (Taubin) smoothing
        /// </summary>
        private void ExplicitEulerSmoothing(TriangleMesh mesh, int iterations)
        {
            /// output vertex positions (after solving/smoothing)
            double[] xx, xy, xz;
            xx = xy = xz = null;

            /// get current world positions
            MeshLaplacian.GetEuclideanCoordinates(mesh, out xx, out xy, out xz);



            /// update mesh
            MeshLaplacian.UpdateMesh(mesh, xx, xy, xz);
        }
Пример #5
0
        /// <summary>
        /// Linear Angle Based Parameterization (LinABF), see (Zayer et al. 2007)
        /// </summary>
        /// <param name="meshin"></param>
        /// <param name="meshout"></param>
        private void LinearABF(TriangleMesh meshin, out TriangleMesh meshout)
        {
            /// copy the mesh
            meshout = meshin.Copy();

            /// counters
            int n = meshout.Vertices.Count;
            int m = meshout.Faces.Count;

            /// output uv-coordinates
            var bu = new double[n];
            var bv = new double[n];
            var b0 = new double[n];

            /// TODO_A2 Task 6
            /// implement Linear Angle Based Smoothing (LinABF)

            /// update mesh positions and uv's
            MeshLaplacian.UpdateMesh(meshout, bu, bv, b0, bu, bv);
            MeshLaplacian.UpdateMesh(meshin, bu, bv);
        }
Пример #6
0
        /// <summary>
        /// The Direct Conformal Parameterization (DCP) method, see (Desbrun et al. 2002)
        /// </summary>
        /// <param name="meshin"></param>
        /// <param name="meshout"></param>
        private void DCP(TriangleMesh meshin, out TriangleMesh meshout)
        {
            MeshLaplacian.PrecomputeTraits(meshin);

            /// copy the mesh
            meshout = meshin.Copy();

            /// counters
            var vertexCount = meshout.Vertices.Count;

            /// output uv-coordinates
            var bu = new double[vertexCount];
            var bv = new double[vertexCount];
            var b0 = new double[vertexCount];

            // A * x = b
            var x = new double[vertexCount * 2 + 4];

            var M_A = new TripletMatrix(2 * (vertexCount + 2), 2 * vertexCount, 4 * (vertexCount + 1) * vertexCount);
            var M_X = new TripletMatrix(2 * (vertexCount + 2), 2 * vertexCount, 4 * (vertexCount + 1) * vertexCount);

            foreach (var vertex in meshin.Vertices.Where(v => !v.OnBoundary))
            {
                var angleWeightSum = 0d;
                var areaWeightSum  = 0d;

                foreach (var halfEdge in vertex.Halfedges)
                {
                    // cot(alpha) + cot(beta)
                    var angleWeight = halfEdge.Previous.Traits.Cotan + halfEdge.Opposite.Previous.Traits.Cotan;
                    // cot(gamma) + cot(delta)
                    var areaWeight = (halfEdge.Next.Traits.Cotan + halfEdge.Opposite.Traits.Cotan);
                    areaWeight /= (halfEdge.FromVertex.Traits.Position - halfEdge.ToVertex.Traits.Position).LengthSquared();

                    M_A.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, angleWeight);
                    M_A.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, angleWeight);
                    M_X.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, areaWeight);
                    M_X.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, areaWeight);

                    angleWeightSum += angleWeight;
                    areaWeightSum  += areaWeight;
                }

                M_A.Entry(vertex.Index * 2, vertex.Index * 2, -angleWeightSum);
                M_A.Entry(vertex.Index * 2 + 1, vertex.Index * 2 + 1, -angleWeightSum);
                M_X.Entry(vertex.Index * 2, vertex.Index * 2, -areaWeightSum);
                M_X.Entry(vertex.Index * 2 + 1, vertex.Index * 2 + 1, -areaWeightSum);
            }

            // Free boundary
            foreach (var vertex in meshin.Vertices.Where(v => v.OnBoundary))
            {
                var weightSum = 0d;

                // Inner edges
                foreach (var halfEdge in vertex.Halfedges.Where(he => !he.Edge.OnBoundary))
                {
                    // cot(alpha) + cot(beta)
                    var borderWeight = halfEdge.Previous.Traits.Cotan + halfEdge.Opposite.Previous.Traits.Cotan;

                    M_A.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, -borderWeight);
                    M_A.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, -borderWeight);
                    M_X.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, -borderWeight);
                    M_X.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, -borderWeight);

                    weightSum += borderWeight;
                }

                // Boundary edges
                foreach (var halfEdge in vertex.Halfedges.Where(he => he.Edge.OnBoundary))
                {
                    // Last edge
                    if (halfEdge.OnBoundary)
                    {
                        var borderWeight = halfEdge.Opposite.Previous.Traits.Cotan;

                        // Weight edge by cotan once and substract the rotated uv vector
                        M_A.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, -borderWeight);
                        M_A.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2 + 1, -1);
                        M_A.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, -borderWeight);
                        M_A.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2, 1);

                        M_X.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, -borderWeight);
                        M_X.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2 + 1, -1);
                        M_X.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, -borderWeight);
                        M_X.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2, 1);

                        weightSum += borderWeight;
                    }
                    // First edge
                    else
                    {
                        // cot(alpha) + cot(beta)
                        var borderWeight = halfEdge.Previous.Traits.Cotan;

                        // Weight edge by cotan once and substract the rotated uv vector
                        M_A.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, -borderWeight);
                        M_A.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2 + 1, 1);
                        M_A.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, -borderWeight);
                        M_A.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2, -1);

                        M_X.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2, -borderWeight);
                        M_X.Entry(vertex.Index * 2, halfEdge.ToVertex.Index * 2 + 1, 1);
                        M_X.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2 + 1, -borderWeight);
                        M_X.Entry(vertex.Index * 2 + 1, halfEdge.ToVertex.Index * 2, -1);

                        weightSum += borderWeight;
                    }
                }

                M_A.Entry(vertex.Index * 2, vertex.Index * 2, weightSum);
                M_A.Entry(vertex.Index * 2 + 1, vertex.Index * 2 + 1, weightSum);
                M_X.Entry(vertex.Index * 2, vertex.Index * 2, weightSum);
                M_X.Entry(vertex.Index * 2 + 1, vertex.Index * 2 + 1, weightSum);
            }

            // Fixed vertices

            // M^n
            M_A.Entry(2 * vertexCount, 2 * P1Index, 1);
            M_A.Entry(2 * vertexCount + 1, 2 * P1Index + 1, 1);
            M_X.Entry(2 * vertexCount, 2 * P1Index, 1);
            M_X.Entry(2 * vertexCount + 1, 2 * P1Index + 1, 1);

            M_A.Entry(2 * vertexCount + 2, 2 * P2Index, 1);
            M_A.Entry(2 * vertexCount + 3, 2 * P2Index + 1, 1);
            M_X.Entry(2 * vertexCount + 2, 2 * P2Index, 1);
            M_X.Entry(2 * vertexCount + 3, 2 * P2Index + 1, 1);

            // M^n transp
            M_A.Entry(2 * P1Index, 2 * vertexCount, 1);
            M_A.Entry(2 * P1Index + 1, 2 * vertexCount + 1, 1);
            M_X.Entry(2 * P1Index, 2 * vertexCount, 1);
            M_X.Entry(2 * P1Index + 1, 2 * vertexCount + 1, 1);

            M_A.Entry(2 * P2Index, 2 * vertexCount + 2, 1);
            M_A.Entry(2 * P2Index + 1, 2 * vertexCount + 3, 1);
            M_X.Entry(2 * P2Index, 2 * vertexCount + 2, 1);
            M_X.Entry(2 * P2Index + 1, 2 * vertexCount + 3, 1);

            // b^n
            x[2 * vertexCount]     = P1UV.X;
            x[2 * vertexCount + 1] = P1UV.Y;
            x[2 * vertexCount + 2] = P2UV.X;
            x[2 * vertexCount + 3] = P2UV.Y;

            var matrix = SparseMatrix.Add(M_A.Compress(), M_X.Compress(), AngleToAreaRatio, 1 - AngleToAreaRatio);
            var solver = QR.Create(matrix);

            solver.Solve(x);

            for (var vertexIndex = 0; vertexIndex < vertexCount; vertexIndex++)
            {
                bu[vertexIndex] = x[2 * vertexIndex];
                bv[vertexIndex] = x[2 * vertexIndex + 1];
            }

            /// update mesh positions and uv's
            MeshLaplacian.UpdateMesh(meshout, bu, bv, b0, bu, bv);
            MeshLaplacian.UpdateMesh(meshin, bu, bv);
        }
Пример #7
0
        /// <summary>
        /// The Least-Squares Conformal Mapping method, see (Levy et al. 2002)
        /// Performs linear mapping with free boundary
        /// </summary>
        /// <param name="meshIn">input mesh, after solving its texture coordinates in vertex traits will be adjusted</param>
        /// <param name="meshOut">an flattened output mesh with only X,Y coordinates set, Z is set to 0</param>
        private void LSCM(TriangleMesh meshin, out TriangleMesh meshout)
        {
            /// copy mesh for output
            meshout = meshin.Copy();

            /// provide uv's for fixed 2 points
            var b = new double[]
            {
                this.P1UV.X, this.P1UV.Y, // u1,v1
                this.P2UV.X, this.P2UV.Y, // u2,v2
            };

            /// get counts
            int n = meshout.Vertices.Count;
            int m = meshout.Faces.Count;

            /// output uv-coordinates
            var bu = new double[n];
            var bv = new double[n];
            var b0 = new double[n];

            var A1 = new TripletMatrix(2 * m, 2 * n - 4, 6 * 2 * m);
            var A2 = new TripletMatrix(2 * m, 4, 4 * 2 * m);

            foreach (var face in meshin.Faces)
            {
                var v1_global = face.Vertices.ElementAt(0).Traits.Position;
                var v2_global = face.Vertices.ElementAt(1).Traits.Position;
                var v3_global = face.Vertices.ElementAt(2).Traits.Position;

                var xDir       = v2_global - v1_global;
                var skewedZDir = v3_global - v1_global;
                var yDir       = Vector3.Cross(xDir, skewedZDir);

                xDir.Normalize();
                yDir.Normalize();

                var zDir = Vector3.Cross(yDir, xDir);

                var transform = new Matrix(new[]
                {
                    xDir.X, xDir.Y, xDir.Z, 0,
                    yDir.X, yDir.Y, yDir.Z, 0,
                    zDir.X, zDir.Y, zDir.Z, 0,
                    0, 0, 0, 1,
                });
                transform.Transpose();

                var v1 = Vector3.Transform(v1_global, transform);
                var v2 = Vector3.Transform(v2_global, transform);
                var v3 = Vector3.Transform(v3_global, transform);

                var areaTriangle =
                    ((double)v1.X * v2.Z - (double)v1.Z * v2.X) +
                    ((double)v2.X * v3.Z - (double)v2.Z * v3.X) +
                    ((double)v3.X * v1.Z - (double)v3.Z * v1.X);

                var mImaginary = new Vector3(v3.Z - v2.Z, v1.Z - v3.Z, v2.Z - v1.Z) * (float)(1d / areaTriangle);
                var mReal      = new Vector3(v3.X - v2.X, v1.X - v3.X, v2.X - v1.X) * (float)(1d / areaTriangle);

                var subIndex = 0;

                // Expected x layout : u1 v1 u2 v2...ui vi ui+2 vi+2...uj vj uj+2 vj+2...un vn
                foreach (var vertex in face.Vertices)
                {
                    if (vertex.Index == P1Index || vertex.Index == P2Index)
                    {
                        var entryIndex = vertex.Index == P1Index ? 0 : 1;

                        // -R*MT * u (dx,dy)
                        A2.Entry(2 * face.Index, 2 * entryIndex, -mReal[subIndex]);
                        A2.Entry(2 * face.Index + 1, 2 * entryIndex, -mImaginary[subIndex]);

                        // MT * v (dx,dy)
                        A2.Entry(2 * face.Index, 2 * entryIndex + 1, mImaginary[subIndex]);
                        A2.Entry(2 * face.Index + 1, 2 * entryIndex + 1, -mReal[subIndex]);
                    }
                    else
                    {
                        var entryIndex = AdaptedIndexFor(vertex.Index);

                        // -R*MT * u (dx,dy)
                        A1.Entry(2 * face.Index, 2 * entryIndex, mReal[subIndex]);
                        A1.Entry(2 * face.Index + 1, 2 * entryIndex, mImaginary[subIndex]);

                        // MT * v (dx,dy)
                        A1.Entry(2 * face.Index, 2 * entryIndex + 1, -mImaginary[subIndex]);
                        A1.Entry(2 * face.Index + 1, 2 * entryIndex + 1, mReal[subIndex]);
                    }

                    subIndex++;
                }
            }

            double[] bPrime;
            A2.Compress().Ax(b, out bPrime);

            var solver = QR.Create(A1.Compress());

            solver.Solve(bPrime);

            for (var vertIndex = 0; vertIndex < n; vertIndex++)
            {
                if (vertIndex == P1Index)
                {
                    bu[vertIndex] = P1UV[0];
                    bv[vertIndex] = P1UV[1];
                }
                else if (vertIndex == P2Index)
                {
                    bu[vertIndex] = P2UV[0];
                    bv[vertIndex] = P2UV[1];
                }
                else
                {
                    var adaptedIndex = AdaptedIndexFor(vertIndex);
                    bu[vertIndex] = bPrime[2 * adaptedIndex];
                    bv[vertIndex] = bPrime[2 * adaptedIndex + 1];
                }
            }

            /// update mesh positions and uv's
            MeshLaplacian.UpdateMesh(meshout, bu, bv, b0, bu, bv);
            MeshLaplacian.UpdateMesh(meshin, bu, bv);
        }