/// <summary> /// Computes all traits on the mesh. /// </summary> public void ComputeAllTraits() { HalfedgeDynamicTrait <double> cornerArea = new HalfedgeDynamicTrait <double>(this); VertexDynamicTrait <double> pointArea = new VertexDynamicTrait <double>(this); ComputeBoundingSphereAndBox(); ComputePointCornerArea(cornerArea, pointArea); ComputeNormals(cornerArea, pointArea); #if CURVATURE ComputePrincipleCurvatures(cornerArea, pointArea); #endif if (!Traits.HasFaceVertexNormals) { foreach (Halfedge h in Halfedges) { h.Traits.Normal = h.ToVertex.Traits.Normal; } } if (Traits.HasTextureCoordinates) { this.ComputeTangents(); } }
/// <summary> /// Computes vertex normals. /// </summary> public void ComputeNormals() { HalfedgeDynamicTrait <double> cornerArea = new HalfedgeDynamicTrait <double>(this); VertexDynamicTrait <double> pointArea = new VertexDynamicTrait <double>(this); ComputePointCornerArea(cornerArea, pointArea); ComputeNormals(cornerArea, pointArea); }
/// <summary> /// Computes principle curvatures on the vertices. /// </summary> public void ComputePrincipleCurvatures() { HalfedgeDynamicTrait <double> cornerArea = new HalfedgeDynamicTrait <double>(this); VertexDynamicTrait <double> pointArea = new VertexDynamicTrait <double>(this); ComputePointCornerArea(cornerArea, pointArea); ComputePrincipleCurvatures(cornerArea, pointArea); }
/// <summary> /// Computes vertex normals. /// </summary> /// <param name="cornerArea">A halfedge dynamic trait with face vertex angular areas.</param> /// <param name="pointArea">A vertex dynamic trait with vertex angular areas.</param> public void ComputeNormals(HalfedgeDynamicTrait <double> cornerArea, VertexDynamicTrait <double> pointArea) { this.Traits.HasFaceVertexNormals = true; FaceDynamicTrait <Vector3D> normal = new FaceDynamicTrait <Vector3D>(this); Vertex[] fv = new Vertex[3]; // Compute normal for each face foreach (Face f in Faces) { int i = 0; foreach (Vertex v in f.Vertices) { fv[i] = v; ++i; } // dynamic traits // Compute normal for this face var n = Vector3D.Cross(fv[2].Traits.Position - fv[1].Traits.Position, fv[0].Traits.Position - fv[2].Traits.Position); n.Normalize(); f.Traits.Normal = n; normal[f] = n; } // Compute normal for each vertex foreach (Halfedge h in Halfedges) { if (!h.OnBoundary) // Ignore halfedges that don't have a face { var weight = (float)(cornerArea[h] / pointArea[h.ToVertex]); h.ToVertex.Traits.Normal += weight * normal[h.Face]; //h.ToVertex.Traits.Normal += weight * h.Traits.Normal; } } // Normalize normals foreach (TriangleMesh.Vertex v in Vertices) { if (v.Traits.Normal.LengthSquared() > 0.0f) // Ignore isolated points { v.Traits.Normal.Normalize(); } } }
/// <summary> /// Computes the angular area for each face vertex and the point area for each vertex. /// </summary> /// <param name="cornerArea">A halfedge dynamic trait to store face vertex angular areas.</param> /// <param name="pointArea">A vertex dynamic trait to store vertex angular areas.</param> public void ComputePointCornerArea(HalfedgeDynamicTrait <double> cornerArea, VertexDynamicTrait <double> pointArea) { Halfedge fh0, fh1, fh2; Vertex fv0, fv1, fv2; Vector3D e0, e1, e2; double length0, length1, length2, oneOverTotalLength; foreach (Face f in Faces) { // Get halfedges for this face fh0 = f.Halfedge; fh1 = fh0.Next; fh2 = fh1.Next; // Get vertices for this face fv0 = fh0.ToVertex; fv1 = fh1.ToVertex; fv2 = fh2.ToVertex; // Edge vectors e0 = fv2.Traits.Position - fv1.Traits.Position; e1 = fv0.Traits.Position - fv2.Traits.Position; e2 = fv1.Traits.Position - fv0.Traits.Position; // Triangle area double area = 0.5f * Vector3D.Cross(e0, e1).Length(); // Edge lengths length0 = e0.Length(); length1 = e1.Length(); length2 = e2.Length(); // Approximate corner area by fraction of triangle area given by opposite edge length oneOverTotalLength = 1.0 / (length0 + length1 + length2); cornerArea[fh0] = length2 * oneOverTotalLength; cornerArea[fh1] = length0 * oneOverTotalLength; cornerArea[fh2] = length1 * oneOverTotalLength; // Add corner areas to areas for vertices pointArea[fv0] += cornerArea[fh0]; pointArea[fv1] += cornerArea[fh1]; pointArea[fv2] += cornerArea[fh2]; } }
/// <summary> /// Computes principle curvatures on the vertices. /// </summary> /// <param name="cornerArea">A halfedge dynamic trait with face vertex angular areas.</param> /// <param name="pointArea">A vertex dynamic trait with vertex angular areas.</param> /// <remarks> /// Portions of this method are based on code from the C++ trimesh2 library /// (from TriMesh_curvature.cc). /// </remarks> public void ComputePrincipleCurvatures(HalfedgeDynamicTrait <double> cornerArea, VertexDynamicTrait <double> pointArea) { // Add dynamic trait for principle curvature computation VertexDynamicTrait <double> curv = new VertexDynamicTrait <double>(this); // Initialize principle curvatures to zero foreach (Vertex v in Vertices) { v.Traits.MaxCurvature = 0.0f; v.Traits.MinCurvature = 0.0f; } // Initialize a coordinate system for each vertex foreach (Vertex v in Vertices) { if (v.Traits.Normal.GetLengthSquared() > 0.0f) // Ignore isolated points { // Vector that points from this vertex to an adjacent one v.Traits.MaxCurvatureDirection = v.Halfedge.ToVertex.Traits.Position - v.Traits.Position; v.Traits.MaxCurvatureDirection.Normalize(); // Get a vector orthogonal to this vector and the vertex normal v.Traits.MinCurvatureDirection = Vector3D.CrossProduct(v.Traits.Normal, v.Traits.MaxCurvatureDirection); } } Halfedge[] fh = new Halfedge[3]; Vertex[] fv = new Vertex[3]; Vector3D[] e = new Vector3D[3]; Vector3D t, b, dn, faceNormal; // Compute curvature for each face foreach (Face f in Faces) { // Get halfedges for this face fh[0] = f.Halfedge; fh[1] = fh[0].Next; fh[2] = fh[1].Next; // Get vertices for this face fv[0] = fh[0].ToVertex; fv[1] = fh[1].ToVertex; fv[2] = fh[2].ToVertex; // Edge vectors e[0] = fv[2].Traits.Position - fv[1].Traits.Position; e[1] = fv[0].Traits.Position - fv[2].Traits.Position; e[2] = fv[1].Traits.Position - fv[0].Traits.Position; t = e[0]; t.Normalize(); faceNormal = Vector3D.CrossProduct(e[0], e[1]); faceNormal.Normalize(); b = Vector3D.CrossProduct(faceNormal, t); b.Normalize(); // Estimate curvature by variation of normals along edges float[] m = new float[3]; float[,] w = new float[3, 3]; for (int i = 0; i < 3; ++i) { float u = Vector3D.DotProduct(e[i], t); float v = Vector3D.DotProduct(e[i], b); w[0, 0] += u * u; w[0, 1] += u * v; w[2, 2] += v * v; dn = fv[(i + 2) % 3].Traits.Normal - fv[(i + 1) % 3].Traits.Normal; float dnu = Vector3D.DotProduct(dn, t); float dnv = Vector3D.DotProduct(dn, b); m[0] += dnu * u; m[1] += dnu * v + dnv * u; m[2] += dnv * v; } w[1, 1] = w[0, 0] + w[2, 2]; w[1, 2] = w[0, 1]; // Least squares solution float[] diag = new float[3]; if (Curvature.LdlTransposeDecomp(w, diag)) { Curvature.LdlTransposeSolveInPlace(w, diag, m); // Adjust curvature for vertices of this face for (int i = 0; i < 3; ++i) { float c1, c12, c2; Curvature.ProjectCurvature(t, b, m[0], m[1], m[2], fv[i].Traits.MaxCurvatureDirection, fv[i].Traits.MinCurvatureDirection, out c1, out c12, out c2); float weight = cornerArea[fh[i]] / pointArea[fv[i]]; fv[i].Traits.MaxCurvature += weight * c1; curv[fv[i]] += weight * c12; fv[i].Traits.MinCurvature += weight * c2; } } } // Compute curvature for each vertex foreach (Vertex v in Vertices) { if (v.Traits.Normal.GetLengthSquared() > 0.0f) // Ignore isolated points { Curvature.DiagonalizeCurvature(v.Traits.MaxCurvatureDirection, v.Traits.MinCurvatureDirection, v.Traits.MaxCurvature, curv[v], v.Traits.MinCurvature, v.Traits.Normal, out v.Traits.MaxCurvatureDirection, out v.Traits.MinCurvatureDirection, out v.Traits.MaxCurvature, out v.Traits.MinCurvature); } } }