// Wild Magic Source Code // David Eberly // http://www.geometrictools.com // Copyright (c) 1998-2007 // // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation; either version 2.1 of the License, or (at // your option) any later version. The license is available for reading at // either of the locations: // http://www.gnu.org/copyleft/lgpl.html // http://www.geometrictools.com/License/WildMagicLicense.pdf // The license applies to versions 0 through 4 of Wild Magic. // // Version: 4.0.1 (2006/07/23) #include "Wm4FoundationPCH.h" #include "Wm4MeshCurvature.h" namespace Wm4 { //---------------------------------------------------------------------------- template MeshCurvature::MeshCurvature (int iVQuantity, const Vector3* akVertex, int iTQuantity, const int* aiIndex) { m_iVQuantity = iVQuantity; m_akVertex = akVertex; m_iTQuantity = iTQuantity; m_aiIndex = aiIndex; // compute normal vectors m_akNormal = WM4_NEW Vector3[m_iVQuantity]; std::fill_n(m_akNormal,m_iVQuantity,Vector3{0,0,0}); int i, iV0, iV1, iV2; for (i = 0; i < m_iTQuantity; i++) { // get vertex indices iV0 = *aiIndex++; iV1 = *aiIndex++; iV2 = *aiIndex++; // compute the normal (length provides a weighted sum) Vector3 kEdge1 = m_akVertex[iV1] - m_akVertex[iV0]; Vector3 kEdge2 = m_akVertex[iV2] - m_akVertex[iV0]; Vector3 kNormal = kEdge1.Cross(kEdge2); m_akNormal[iV0] += kNormal; m_akNormal[iV1] += kNormal; m_akNormal[iV2] += kNormal; } for (i = 0; i < m_iVQuantity; i++) { m_akNormal[i].Normalize(); } // compute the matrix of normal derivatives Matrix3* akDNormal = WM4_NEW Matrix3[m_iVQuantity]; Matrix3* akWWTrn = WM4_NEW Matrix3[m_iVQuantity]; Matrix3* akDWTrn = WM4_NEW Matrix3[m_iVQuantity]; std::fill_n(akWWTrn,m_iVQuantity,Matrix3{0,0,0,0,0,0,0,0,0}); std::fill_n(akDWTrn,m_iVQuantity,Matrix3{0,0,0,0,0,0,0,0,0}); int iRow, iCol; aiIndex = m_aiIndex; for (i = 0; i < m_iTQuantity; i++) { // get vertex indices int aiV[3]; aiV[0] = *aiIndex++; aiV[1] = *aiIndex++; aiV[2] = *aiIndex++; for (int j = 0; j < 3; j++) { iV0 = aiV[j]; iV1 = aiV[(j+1)%3]; iV2 = aiV[(j+2)%3]; // Compute edge from V0 to V1, project to tangent plane of vertex, // and compute difference of adjacent normals. Vector3 kE = m_akVertex[iV1] - m_akVertex[iV0]; Vector3 kW = kE - (kE.Dot(m_akNormal[iV0]))*m_akNormal[iV0]; Vector3 kD = m_akNormal[iV1] - m_akNormal[iV0]; for (iRow = 0; iRow < 3; iRow++) { for (iCol = 0; iCol < 3; iCol++) { akWWTrn[iV0][iRow][iCol] += kW[iRow]*kW[iCol]; akDWTrn[iV0][iRow][iCol] += kD[iRow]*kW[iCol]; } } // Compute edge from V0 to V2, project to tangent plane of vertex, // and compute difference of adjacent normals. kE = m_akVertex[iV2] - m_akVertex[iV0]; kW = kE - (kE.Dot(m_akNormal[iV0]))*m_akNormal[iV0]; kD = m_akNormal[iV2] - m_akNormal[iV0]; for (iRow = 0; iRow < 3; iRow++) { for (iCol = 0; iCol < 3; iCol++) { akWWTrn[iV0][iRow][iCol] += kW[iRow]*kW[iCol]; akDWTrn[iV0][iRow][iCol] += kD[iRow]*kW[iCol]; } } } } // Add in N*N^T to W*W^T for numerical stability. In theory 0*0^T gets // added to D*W^T, but of course no update needed in the implementation. // Compute the matrix of normal derivatives. for (i = 0; i < m_iVQuantity; i++) { for (iRow = 0; iRow < 3; iRow++) { for (iCol = 0; iCol < 3; iCol++) { akWWTrn[i][iRow][iCol] = ((Real)0.5)*akWWTrn[i][iRow][iCol] + m_akNormal[i][iRow]*m_akNormal[i][iCol]; akDWTrn[i][iRow][iCol] *= (Real)0.5; } } akDNormal[i] = akDWTrn[i]*akWWTrn[i].Inverse(); } WM4_DELETE[] akWWTrn; WM4_DELETE[] akDWTrn; // If N is a unit-length normal at a vertex, let U and V be unit-length // tangents so that {U, V, N} is an orthonormal set. Define the matrix // J = [U | V], a 3-by-2 matrix whose columns are U and V. Define J^T // to be the transpose of J, a 2-by-3 matrix. Let dN/dX denote the // matrix of first-order derivatives of the normal vector field. The // shape matrix is // S = (J^T * J)^{-1} * J^T * dN/dX * J = J^T * dN/dX * J // where the superscript of -1 denotes the inverse. (The formula allows // for J built from non-perpendicular vectors.) The matrix S is 2-by-2. // The principal curvatures are the eigenvalues of S. If k is a principal // curvature and W is the 2-by-1 eigenvector corresponding to it, then // S*W = k*W (by definition). The corresponding 3-by-1 tangent vector at // the vertex is called the principal direction for k, and is J*W. m_afMinCurvature = WM4_NEW Real[m_iVQuantity]; m_afMaxCurvature = WM4_NEW Real[m_iVQuantity]; m_akMinDirection = WM4_NEW Vector3[m_iVQuantity]; m_akMaxDirection = WM4_NEW Vector3[m_iVQuantity]; for (i = 0; i < m_iVQuantity; i++) { // compute U and V given N Vector3 kU, kV; Vector3::GenerateComplementBasis(kU,kV,m_akNormal[i]); // Compute S = J^T * dN/dX * J. In theory S is symmetric, but // because we have estimated dN/dX, we must slightly adjust our // calculations to make sure S is symmetric. Real fS01 = kU.Dot(akDNormal[i]*kV); Real fS10 = kV.Dot(akDNormal[i]*kU); Real fSAvr = ((Real)0.5)*(fS01+fS10); Matrix2 kS ( kU.Dot(akDNormal[i]*kU), fSAvr, fSAvr, kV.Dot(akDNormal[i]*kV) ); // compute the eigenvalues of S (min and max curvatures) Real fTrace = kS[0][0] + kS[1][1]; Real fDet = kS[0][0]*kS[1][1] - kS[0][1]*kS[1][0]; Real fDiscr = fTrace*fTrace - ((Real)4.0)*fDet; Real fRootDiscr = Math::Sqrt(Math::FAbs(fDiscr)); m_afMinCurvature[i] = ((Real)0.5)*(fTrace - fRootDiscr); m_afMaxCurvature[i] = ((Real)0.5)*(fTrace + fRootDiscr); // compute the eigenvectors of S Vector2 kW0(kS[0][1],m_afMinCurvature[i]-kS[0][0]); Vector2 kW1(m_afMinCurvature[i]-kS[1][1],kS[1][0]); if (kW0.SquaredLength() >= kW1.SquaredLength()) { kW0.Normalize(); m_akMinDirection[i] = kW0.X()*kU + kW0.Y()*kV; } else { kW1.Normalize(); m_akMinDirection[i] = kW1.X()*kU + kW1.Y()*kV; } kW0 = Vector2(kS[0][1],m_afMaxCurvature[i]-kS[0][0]); kW1 = Vector2(m_afMaxCurvature[i]-kS[1][1],kS[1][0]); if (kW0.SquaredLength() >= kW1.SquaredLength()) { kW0.Normalize(); m_akMaxDirection[i] = kW0.X()*kU + kW0.Y()*kV; } else { kW1.Normalize(); m_akMaxDirection[i] = kW1.X()*kU + kW1.Y()*kV; } } WM4_DELETE[] akDNormal; } //---------------------------------------------------------------------------- template MeshCurvature::~MeshCurvature () { WM4_DELETE[] m_akNormal; WM4_DELETE[] m_afMinCurvature; WM4_DELETE[] m_afMaxCurvature; WM4_DELETE[] m_akMinDirection; WM4_DELETE[] m_akMaxDirection; } //---------------------------------------------------------------------------- template int MeshCurvature::GetVQuantity () const { return m_iVQuantity; } //---------------------------------------------------------------------------- template const Vector3* MeshCurvature::GetVertices () const { return m_akVertex; } //---------------------------------------------------------------------------- template int MeshCurvature::GetTQuantity () const { return m_iTQuantity; } //---------------------------------------------------------------------------- template const int* MeshCurvature::GetIndices () const { return m_aiIndex; } //---------------------------------------------------------------------------- template const Vector3* MeshCurvature::GetNormals () const { return m_akNormal; } //---------------------------------------------------------------------------- template const Real* MeshCurvature::GetMinCurvatures () const { return m_afMinCurvature; } //---------------------------------------------------------------------------- template const Real* MeshCurvature::GetMaxCurvatures () const { return m_afMaxCurvature; } //---------------------------------------------------------------------------- template const Vector3* MeshCurvature::GetMinDirections () const { return m_akMinDirection; } //---------------------------------------------------------------------------- template const Vector3* MeshCurvature::GetMaxDirections () const { return m_akMaxDirection; } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- // explicit instantiation //---------------------------------------------------------------------------- template WM4_FOUNDATION_ITEM class MeshCurvature; template WM4_FOUNDATION_ITEM class MeshCurvature; //---------------------------------------------------------------------------- }