Example #1
0
        /// <summary>
        /// Creates a square uniform n x n Laplacian matrix
        /// eye*[ I ] + lambda*[ L ];
        /// Note that depending on lambda you can use this function to create both:
        /// [I - λL]
        /// [I + λL]
        /// L is such that it has negative values on the diagonal and non-negative values elsewhere
        /// If the normalized flag is true, each diagonal element of L is normalized to -1


        /// <summary>
        /// Creates a square cotangent n x n Laplacian matrix
        /// eye*[ I ] + lambda*[ L ];
        /// Note that depending on lambda you can use this function to create both:
        /// [I - λL]
        /// [I + λL]
        /// L is such that it has negative values on the diagonal and non-negative values elsewhere
        /// If the normalized flag is true, each diagonal element of L is normalized to -1
        /// </summary>
        public static TripletMatrix CreateCotanLaplacian(TriangleMesh mesh, double lambda = 1.0, double eye = 0.0, bool normalized = false)
        {
            var n  = mesh.Vertices.Count;
            int nz = mesh.Vertices.Aggregate(0, (c, x) => c += x.VertexCount());
            var L  = new TripletMatrix(n, n, nz + n);

            PrecomputeTraits(mesh);

            /// compute the matrix
            for (int i = 0; i < n; i++)
            {
                var vi = mesh.Vertices[i];

                /// cotan weight sum
                double wsum = 0;

                foreach (var hj in vi.Halfedges)
                {
                    ///right
                    var cotA = hj.Opposite.Previous.Traits.Cotan;
                    /// left
                    var cotB = hj.Previous.Traits.Cotan;
                    /// sum the weights for one ring
                    wsum += (cotA + cotB) / 2;
                }

                foreach (var hj in vi.Halfedges)
                {
                    /// the other vertex index
                    var j = hj.ToVertex.Index;
                    ///right
                    var cotA = hj.Opposite.Previous.Traits.Cotan;
                    /// left
                    var cotB = hj.Previous.Traits.Cotan;
                    /// weight wj normalized by the wsum
                    var wij = (cotA + cotB) / 2;

                    /// set the cotan values
                    if (normalized)
                    {
                        L[i, j] = lambda * (wij / wsum);
                    }
                    else
                    {
                        L[i, j] = lambda * (wij);
                    }
                }

                if (normalized)
                {
                    L[i, i] = eye - lambda * 1.0;
                }
                else
                {
                    L[i, i] = eye - lambda * wsum;
                }
            }

            return(L);
        }
Example #2
0
        public static TripletMatrix CreateBoundedMeanLaplacian(TriangleMesh mesh, double lambda = 0.0, double eye = 0.0, bool normalized = false)
        {
            PrecomputeTraits(mesh);

            var vertexCount   = mesh.Vertices.Count;
            int neighborCount = mesh.Vertices.Aggregate(0, (c, x) => x.VertexCount());
            var L             = new TripletMatrix(vertexCount, vertexCount, neighborCount, true);

            foreach (var currentVertex in mesh.Vertices)
            {
                if (currentVertex.OnBoundary)
                {
                    L.Entry(currentVertex.Index, currentVertex.Index, 1d);
                    continue;
                }

                // Diagonal entry v(i,i) = eye*1 + λ*L(i,i)
                var edgeSum     = currentVertex.Halfedges.Sum(e => TanWeights(e));
                var diagonalVal = eye + GetNormalized(edgeSum, edgeSum, normalized) * lambda;
                L.Entry(currentVertex.Index, currentVertex.Index, diagonalVal);
                currentVertex.Halfedges.Apply(e =>
                                              L.Entry(currentVertex.Index, e.ToVertex.Index, -GetNormalized(TanWeights(e), edgeSum, normalized) * lambda));
            }

            return(L);
        }
Example #3
0
        /// <summary>
        ///
        /// This method is not relevant for Assignment 1 yet!
        ///
        /// Creates a square mean-value n x n Laplacian matrix
        /// eye*[ I ] + lambda*[ L ];
        /// Note that depending on lambda you can use this function to create both:
        /// [I - λL]
        /// [I + λL]
        /// L is such that it has negative values on the diagonal and non-negative values elsewhere
        /// If the normalized flag is true, each diagonal element of L is normalized to -1
        /// </summary>
        public static TripletMatrix CreateMeanValueLaplacian(TriangleMesh mesh, double lambda = 1.0, double eye = 0.0, bool normalized = false)
        {
            var n  = mesh.Vertices.Count;
            int nz = mesh.Vertices.Aggregate(0, (c, x) => c += x.VertexCount());
            var L  = new TripletMatrix(n, n, nz + n);

            return(L);
        }
Example #4
0
        /// <summary>
        /// Creates a cotangent n x n laplacian matrix extended by a n x n weight-matrix:
        ///        [ L ]
        /// lambda*[ I ]
        /// If the normalized flag is true, each diagonal element of L is normalized to -1
        /// </summary>
        public static TripletMatrix CreateExtendedCotanLaplacian(TriangleMesh mesh, double lambda = 1.0, bool normalized = true)
        {
            var n  = mesh.Vertices.Count;
            int nz = mesh.Vertices.Aggregate(0, (c, x) => c += x.VertexCount());
            var L  = new TripletMatrix(2 * n, n, nz + 2 * n, true);



            return(L);
        }
Example #5
0
        /// <summary>
        ///
        /// </summary>
        public static TripletMatrix CreateAdjacenceMatrix(TriangleMesh mesh)
        {
            var n  = mesh.Vertices.Count;
            int nz = mesh.Vertices.Aggregate(0, (c, x) => c += x.VertexCount());
            var A  = new TripletMatrix(n, n, nz + n);

            /// build adjacence matrix
            for (int i = 0; i < n; i++)
            {
                var v = mesh.Vertices[i];
                foreach (var vj in v.Vertices)
                {
                    int j = vj.Index;
                    A[i, j] = 1;
                }
            }
            return(A);
        }
Example #6
0
        /// </summary>
        public static TripletMatrix CreateUniformLaplacian(TriangleMesh mesh, double lambda = 0.0, double eye = 0.0, bool normalized = false)
        {
            int n  = mesh.Vertices.Count;
            int nz = mesh.Vertices.Aggregate(0, (c, x) => x.VertexCount());
            var L  = new TripletMatrix(n, n, nz, true);

            /// build adjacence A and uniform L matrix
            for (int i = 0; i < n; i++)
            {
                var v    = mesh.Vertices[i];
                var wsum = (double)v.VertexCount();
                var wij  = 1.0;

                foreach (var vj in v.Vertices)
                {
                    int j = vj.Index;

                    if (normalized)
                    {
                        L[i, j] = lambda * (wij / wsum);
                    }
                    else
                    {
                        L[i, j] = lambda * (wij);
                    }
                }

                if (normalized)
                {
                    L[i, i] = eye - lambda * 1;
                }
                else
                {
                    L[i, i] = eye - lambda * wsum;
                }
            }

            return(L);
        }
Example #7
0
        /// </summary>
        public static TripletMatrix CreateBoundedUniformLaplacian(TriangleMesh mesh, double lambda = 0.0, double eye = 0.0, bool normalized = false)
        {
            var vertexCount   = mesh.Vertices.Count;
            int neighborCount = mesh.Vertices.Aggregate(0, (c, x) => x.VertexCount());
            var L             = new TripletMatrix(vertexCount, vertexCount, neighborCount, true);

            foreach (var currentVertex in mesh.Vertices)
            {
                if (currentVertex.OnBoundary)
                {
                    L.Entry(currentVertex.Index, currentVertex.Index, 1d);
                    continue;
                }

                // Diagonal entry v(i,i) = eye*1 + λ*L(i,i)
                var diagonalVal = eye + GetNormalized(currentVertex.Vertices.Count(), currentVertex.VertexCount(), normalized) * lambda;
                L.Entry(currentVertex.Index, currentVertex.Index, diagonalVal);
                currentVertex.Vertices.Apply(nb =>
                                             L.Entry(currentVertex.Index, nb.Index, GetNormalized(-1, currentVertex.VertexCount(), normalized) * lambda));
            }

            return(L);
        }
Example #8
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);
        }
Example #9
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);
        }