void GW_Mesh::GetBoundingBox( GW_Vector3D& min, GW_Vector3D& max ) { min.SetValue( GW_INFINITE ); max.SetValue( -GW_INFINITE ); for( GW_U32 i=0; i<this->GetNbrVertex(); ++i ) { if( this->GetVertex(i)!=NULL ) { GW_Vector3D& pos = this->GetVertex(i)->GetPosition(); min[0] = GW_MIN( min[0], pos[0] ); min[1] = GW_MIN( min[1], pos[1] ); min[2] = GW_MIN( min[2], pos[2] ); max[0] = GW_MAX( max[0], pos[0] ); max[1] = GW_MAX( max[1], pos[1] ); max[2] = GW_MAX( max[2], pos[2] ); } } }
/*------------------------------------------------------------------------------*/ void GW_Vertex::BuildRawNormal() { GW_Vector3D FaceNormal; Normal_.SetZero(); GW_U32 nIter = 0; for( GW_FaceIterator it = this->BeginFaceIterator(); it!=this->EndFaceIterator(); ++it ) { GW_Face* pFace = *it; GW_ASSERT( pFace!=NULL ); FaceNormal = (pFace->GetVertex(0)->GetPosition()-pFace->GetVertex(1)->GetPosition()) ^ (pFace->GetVertex(0)->GetPosition()-pFace->GetVertex(2)->GetPosition()); FaceNormal.Normalize(); Normal_ += FaceNormal; nIter++; if( nIter>20 ) break; } Normal_.Normalize(); }
/*------------------------------------------------------------------------------*/ void GW_TriangularInterpolation_Cubic::ComputeLocalGradient( GW_GeodesicVertex& Vert ) { /* compute the total angle */ GW_Vector3D PrevEdge; GW_Float rTotalAngle = 0; for( GW_VertexIterator it=Vert.BeginVertexIterator(); it!=Vert.EndVertexIterator(); ++it ) { GW_Vertex* pVert = *it; GW_ASSERT( pVert!=NULL ); if( it==Vert.BeginVertexIterator() ) { PrevEdge = pVert->GetPosition() - Vert.GetPosition(); PrevEdge.Normalize(); } else { GW_Vector3D NextEdge = pVert->GetPosition() - Vert.GetPosition(); NextEdge.Normalize(); rTotalAngle += acos( NextEdge*PrevEdge ); PrevEdge = NextEdge; } } /* matrix and RHS for least square minimusation */ GW_Float M[2][2] = {{0,0},{0,0}}; GW_Float b[2] = {0,0}; GW_Float rCurAngle = 0; PrevEdge.SetZero(); for( GW_VertexIterator it=Vert.BeginVertexIterator(); it!=Vert.EndVertexIterator(); ++it ) { GW_GeodesicVertex* pVert = (GW_GeodesicVertex*) *it; GW_ASSERT( pVert!=NULL ); GW_Vector3D Edge = pVert->GetPosition() - Vert.GetPosition(); GW_Float a = Edge.Norm(); Edge /= a; if( it!=Vert.BeginVertexIterator() ) { /* update the angle */ rCurAngle += acos( Edge*PrevEdge ); } /* directional gradient estimation */ GW_Float delta = (pVert->GetDistance() - Vert.GetDistance())/a; /* coordonate of the edge on (u,v) [flatened coords] */ GW_Float eu = a*cos( rCurAngle/rTotalAngle ); GW_Float ev = a*sin( rCurAngle/rTotalAngle ); /* update the matrix */ M[0][0] += eu*eu; M[0][1] += eu*ev; M[1][0] += eu*ev; M[1][1] += ev*ev; b[0] += delta*eu; b[1] += delta*ev; PrevEdge = Edge; } /* invert the system */ GW_Float det = M[0][0]*M[1][1] - M[0][1]*M[1][0]; GW_ASSERT( det!=0 ); GW_Float gu = 1/det * ( M[1][1]*b[0] - M[0][1]*b[1] ); GW_Float gv = 1/det * (-M[1][0]*b[0] + M[0][0]*b[1] ); /* set the gradient in local coords for each surrounding face */ rCurAngle = 0; for( GW_FaceIterator it = Vert.BeginFaceIterator(); it!=Vert.EndFaceIterator(); ++it ) { GW_GeodesicFace* pFace = (GW_GeodesicFace*) *it; GW_ASSERT( pFace!=NULL ); GW_Vertex* pVert1 = it.GetLeftVertex(); GW_ASSERT( pVert1!=NULL ); GW_Vertex* pVert2 = it.GetRightVertex(); GW_ASSERT( pVert1!=NULL ); GW_Vector3D e1 = pVert1->GetPosition() - Vert.GetPosition(); GW_Vector3D e2 = pVert2->GetPosition() - Vert.GetPosition(); GW_Float a1 = e1.Norm(); GW_Float a2 = e2.Norm(); e1 /= a1; e2 /= a2; GW_Float rInnerAngle = acos( e1*e2 ); /* flattened position of the two vertex */ GW_Float p1[2], p2[2]; p1[0] = cos( rCurAngle ); p1[1] = sin( rCurAngle ); p2[0] = cos( rCurAngle+rInnerAngle ); p2[1] = sin( rCurAngle+rInnerAngle ); /* we have grad = gu*u + gv*v we are searching for grad = g1*p1 + g2*p2, so: gu = g1*<p1,u> + g2*<p2,u> gv = g1*<p1,v> + g2*<p2,v> i.e. |p1[0] p2[0]| |g1| |gu| |p1[1] p2[1]|*|g2| = |gv| */ det = p1[0]*p2[1]-p1[1]*p2[0]; GW_ASSERT( det!=0 ); GW_Float g1 = 1/det * ( p2[1]*gu - p2[0]*gv ); GW_Float g2 = 1/det * (-p1[1]*gu + p1[0]*gv ); /* now compute the gradient in world coords */ GW_Vector3D LocGrad = e1*g1 + e2*g2; GW_TriangularInterpolation_ABC* pInterp = pFace->GetTriangularInterpolation(); if( pInterp==NULL ) { pInterp = new GW_TriangularInterpolation_Cubic; pFace->SetTriangularInterpolation( *pInterp ); } GW_ASSERT( pInterp->GetType()==kCubicTriangulationInterpolation ); ((GW_TriangularInterpolation_Cubic*) pInterp)->SetLocalGradient( LocGrad, *pFace, Vert ); rCurAngle += rInnerAngle; } }
/*------------------------------------------------------------------------------*/ void GW_Vertex::ComputeCurvatureDirections( GW_Float rArea ) { GW_Vector3D CurEdge; GW_Vector3D CurEdgeNormalized; GW_Float rCurEdgeLength; GW_Float rCotan; GW_Vector3D TempEdge1, TempEdge2; GW_Vertex* pTempVert = NULL; GW_Float rDotP; /***********************************************************************************/ /* compute the two curvature directions */ /* (v1,v2) form a basis of the tangent plante */ GW_Vector3D v1 = Normal_ ^ GW_Vector3D(0,0,1); GW_Float rNorm = v1.Norm(); if( rNorm<GW_EPSILON ) { /* orthogonalize using another direction */ v1 = Normal_ ^ GW_Vector3D(0,1,0); rNorm = v1.Norm(); GW_ASSERT( rNorm>GW_EPSILON ); } v1 /= rNorm; GW_Vector3D v2 = Normal_ ^ v1; /* now we must find the curvature matrix entry by minimising a mean square problem the 3 entry of the symetric curvature matrix in (v1,v2) basis are (a,b,c), stored in vector x. IMPORTANT : we must ensure a<c, so that eigenvalues are in correct order. */ GW_Float a = 0, b = 0, c = 0; // the vector (a,b,c) we are searching. We use a+c=2*MeanCurv so we don't take care of c. GW_Float D[2] = {0,0}; // the right side of the equation. GW_Float M00 = 0, M11 = 0, M01 = 0; // the positive-definite matrix entries of the mean-square problem. GW_Float d1, d2; // decomposition of current edge on (v1,v2) basis GW_Float w, k; // current weight and current approximated directional curvature. /* now build the matrix by iterating around the vertex */ for( GW_VertexIterator it = this->BeginVertexIterator(); it!=this->EndVertexIterator(); ++it ) { GW_Vertex* pVert = *it; GW_ASSERT( pVert!=NULL ); CurEdge = pVert->GetPosition() - this->GetPosition(); rCurEdgeLength = CurEdge.Norm(); CurEdgeNormalized = CurEdge/rCurEdgeLength; /* here we store the value of the sum of the cotan */ rCotan = 0; /* compute projection onto v1,v2 basis */ d1 = v1*CurEdge; d2 = v2*CurEdge; rNorm = sqrt(d1*d1 + d2*d2); if( rNorm>0 ) { d1 /= rNorm; d2 /= rNorm; } /* compute left angle */ pTempVert = it.GetLeftVertex(); if( pTempVert!=NULL ) { TempEdge1 = this->GetPosition() - pTempVert->GetPosition(); TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition(); TempEdge1.Normalize(); TempEdge2.Normalize(); /* we use tan(acos(x))=sqrt(1-x^2)/x */ rDotP = TempEdge1*TempEdge2; if( rDotP!=1 && rDotP!=-1 ) rCotan += rDotP/sqrt(1-rDotP*rDotP); } /* compute right angle AND Gaussian contribution */ pTempVert = it.GetRightVertex(); if( pTempVert!=NULL ) { TempEdge1 = this->GetPosition() - pTempVert->GetPosition(); TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition(); TempEdge1.Normalize(); TempEdge2.Normalize(); /* we use tan(acos(x))=sqrt(1-x^2)/x */ rDotP = TempEdge1*TempEdge2; if( rDotP!=1 && rDotP!=-1 ) rCotan += (GW_Float) rDotP/sqrt(1-rDotP*rDotP); } GW_CHECK_MATHSBIT(); /*compute weight */ w = 0.125/rArea*rCotan*rCurEdgeLength*rCurEdgeLength; /* compute directional curvature */ k = -2*(CurEdge*Normal_)/(rCurEdgeLength*rCurEdgeLength); k = k-(rMinCurv_+rMaxCurv_); // modified by the fact that we use a+c=2*MeanCurv /* add contribution to M matrix and D vector*/ M00 += w*(d1*d1-d2*d2)*(d1*d1-d2*d2); M11 += 4*w*d1*d1*d2*d2; M01 += 2*w*(d1*d1-d2*d2)*d1*d2; D[0] += w*k*( d1*d1-d2*d2); D[1] += 2*w*k*d1*d2; } GW_CHECK_MATHSBIT(); /* solve the system */ GW_Float rDet = M00*M11 - M01*M01; if( rDet!=0 ) { /* The inverse matrix is : | M11 -M01| 1/rDet * |-M01 M00| */ a = 1/rDet * ( M11*D[0] - M01*D[1] ); b = 1/rDet * (-M01*D[0] + M00*D[1] ); } c = (rMinCurv_+rMaxCurv_) - a; // GW_ORDER(a,c); /* compute the direction via Givens rotations */ GW_Float rTheta; if( GW_ABS(c-a) < GW_EPSILON ) { if( b==0 ) rTheta = 0; else rTheta = GW_HALFPI; } else { rTheta = (GW_Float) 2.0f*b/(c-a); rTheta = (GW_Float) 0.5f*atan(rTheta); } GW_CHECK_MATHSBIT(); CurvDirMin_ = v1*cos(rTheta) - v2*sin(rTheta); CurvDirMax_ = v1*sin(rTheta) + v2*cos(rTheta); GW_Float vp1 = 0, vp2 = 0; if( rTheta!=0 ) { GW_Float r1 = a*cos(rTheta) - b*sin(rTheta); GW_Float r2 = b*cos(rTheta) - c*sin(rTheta); r1 =(GW_Float) r1/cos(rTheta); r2 = (GW_Float) -r2/sin(rTheta); vp1 = r1; // GW_ASSERT( GW_ABS(r1-r2)<0.001*GW_ABS(r1) ); r1 = (GW_Float) a*sin(rTheta) + b*cos(rTheta); r2 = (GW_Float) b*sin(rTheta) + c*cos(rTheta); r1 = (GW_Float) r1/sin(rTheta); r2 = (GW_Float) r2/cos(rTheta); vp2 = r2; // GW_ASSERT( GW_ABS(r1-r2)<0.001*GW_ABS(r1) ); } if( vp1>vp2 ) { GW_Vector3D vtemp = CurvDirMin_; CurvDirMin_ = CurvDirMax_; CurvDirMax_ = vtemp; } }
/*------------------------------------------------------------------------------*/ void GW_Vertex::ComputeNormalAndCurvature( GW_Float& rArea ) { GW_Vector3D CurEdge; GW_Vector3D CurEdgeNormalized; GW_Float rCurEdgeLength; GW_Float rCotan; GW_Vector3D TempEdge1, TempEdge2; GW_Float rTempEdge1Length, rTempEdge2Length; GW_Float rAngle, rInnerAngle; GW_Vertex* pTempVert = NULL; GW_Float rDotP; Normal_.SetZero(); rArea = 0; GW_Float rGaussianCurv = 0; for( GW_VertexIterator it = this->BeginVertexIterator(); it!=this->EndVertexIterator(); ++it ) { GW_Vertex* pVert = *it; GW_ASSERT( pVert!=NULL ); CurEdge = pVert->GetPosition() - this->GetPosition(); rCurEdgeLength = CurEdge.Norm(); CurEdgeNormalized = CurEdge/rCurEdgeLength; /* here we store the value of the sum of the cotan */ rCotan = 0; /* compute left angle */ pTempVert = it.GetLeftVertex(); if( pTempVert!=NULL ) { TempEdge1 = this->GetPosition() - pTempVert->GetPosition(); TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition(); TempEdge1.Normalize(); TempEdge2.Normalize(); /* we use tan(acos(x))=sqrt(1-x^2)/x */ rDotP = TempEdge1*TempEdge2; if( rDotP!=1 && rDotP!=-1 ) rCotan += (GW_Float) rDotP/((GW_Float) sqrt(1-rDotP*rDotP)); } /* compute right angle AND Gaussian contribution */ pTempVert = it.GetRightVertex(); if( pTempVert!=NULL ) { TempEdge1 = this->GetPosition() - pTempVert->GetPosition(); TempEdge2 = pVert->GetPosition() - pTempVert->GetPosition(); rTempEdge1Length = TempEdge1.Norm(); rTempEdge2Length = TempEdge2.Norm(); TempEdge1 /= rTempEdge1Length; TempEdge2 /= rTempEdge2Length; rAngle = (GW_Float) acos( -(TempEdge1 * CurEdgeNormalized) ); /* we use tan(acos(x))=sqrt(1-x^2)/x */ rDotP = TempEdge1*TempEdge2; rInnerAngle = (GW_Float) acos( rDotP ); if( rDotP!=1 && rDotP!=-1 ) rCotan += rDotP/((GW_Float) sqrt(1-rDotP*rDotP)); rGaussianCurv += rAngle; /* compute the contribution to area, testing for special obtuse angle */ if( rAngle<GW_HALFPI // condition on 1st angle && rInnerAngle<GW_HALFPI // condition on 2nd angle && (GW_PI-rAngle-rInnerAngle)<GW_HALFPI ) // condition on 3rd angle { /* non-obtuse : 1/8*( |PR|²cot(Q)+|PQ|²cot(R) ) where P=this, Q=pVert, R=pTempVert */ rArea += ( rCurEdgeLength*rCurEdgeLength*rDotP/sqrt(1-rDotP*rDotP) + rTempEdge1Length*rTempEdge1Length/tan(GW_PI-rAngle-rInnerAngle) )*0.125; } else if( rAngle>=GW_HALFPI ) { /* obtuse at the central vertex : 0.5*area(T) */ rArea += 0.25*rCurEdgeLength*rTempEdge1Length* ~(CurEdgeNormalized^TempEdge1); } else { /* obtuse at one of side vertex */ rArea += 0.125*rCurEdgeLength*rTempEdge1Length* ~(CurEdgeNormalized^TempEdge1); } } GW_CHECK_MATHSBIT(); /* add the contribution to Normal */ Normal_ -= CurEdge*rCotan; } GW_CHECK_MATHSBIT(); GW_ASSERT( rArea!=0 ); // remove this ! /* the Gaussian curv */ rGaussianCurv = (GW_TWOPI - rGaussianCurv)/rArea; /* compute Normal and mean curv */ Normal_ /= 4.0*rArea; GW_Float rMeanCurv = Normal_.Norm(); if( GW_ABS(rMeanCurv)>GW_EPSILON ) { GW_Vector3D Normal = Normal_/rMeanCurv; /* see if we need to flip the normal */ this->BuildRawNormal(); if( Normal*Normal_<0 ) Normal_ = -Normal; else Normal_ = Normal; } else { /* we must use another method to compute normal */ this->BuildRawNormal(); } GW_Vertex::rTotalArea_ += rArea; /* compute the two curv values */ GW_Float rDelta = rMeanCurv*rMeanCurv - rGaussianCurv; if( rDelta<0 ) rDelta = 0; rDelta = sqrt(rDelta); rMinCurv_ = rMeanCurv - rDelta; rMaxCurv_ = rMeanCurv + rDelta; }