You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							468 lines
						
					
					
						
							18 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							468 lines
						
					
					
						
							18 KiB
						
					
					
				| // David Eberly, Geometric Tools, Redmond WA 98052 | |
| // Copyright (c) 1998-2021 | |
| // Distributed under the Boost Software License, Version 1.0. | |
| // https://www.boost.org/LICENSE_1_0.txt | |
| // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt | |
| // Version: 4.0.2020.11.16 | |
|  | |
| #pragma once | |
|  | |
| #include <Mathematics/Delaunay2.h> | |
| #include <Mathematics/ContScribeCircle2.h> | |
| #include <Mathematics/DistPointAlignedBox.h> | |
|  | |
| // Quadratic interpolation of a network of triangles whose vertices are of | |
| // the form (x,y,f(x,y)).  This code is an implementation of the algorithm | |
| // found in | |
| // | |
| //   Zoltan J. Cendes and Steven H. Wong, | |
| //   C1 quadratic interpolation over arbitrary point sets, | |
| //   IEEE Computer Graphics & Applications, | |
| //   pp. 8-16, 1987 | |
| // | |
| // The TriangleMesh interface must support the following: | |
| //   int GetNumVertices() const; | |
| //   int GetNumTriangles() const; | |
| //   Vector2<Real> const* GetVertices() const; | |
| //   int const* GetIndices() const; | |
| //   bool GetVertices(int, std::array<Vector2<Real>, 3>&) const; | |
| //   bool GetIndices(int, std::array<int, 3>&) const; | |
| //   bool GetAdjacencies(int, std::array<int, 3>&) const; | |
| //   bool GetBarycentrics(int, Vector2<Real> const&, | |
| //      std::array<Real, 3>&) const; | |
| //   int GetContainingTriangle(Vector2<Real> const&) const; | |
|  | |
| namespace gte | |
| { | |
|     template <typename Real, typename TriangleMesh> | |
|     class IntpQuadraticNonuniform2 | |
|     { | |
|     public: | |
|         // Construction. | |
|         // | |
|         // The first constructor requires only F and a measure of the rate of | |
|         // change of the function values relative to changes in the spatial | |
|         // variables.  The df/dx and df/dy values are estimated at the sample | |
|         // points using mesh normals and spatialDelta. | |
|         // | |
|         // The second constructor requires you to specify function values F | |
|         // and first-order partial derivative values df/dx and df/dy. | |
|  | |
|         IntpQuadraticNonuniform2(TriangleMesh const& mesh, Real const* F, Real spatialDelta) | |
|             : | |
|             mMesh(&mesh), | |
|             mF(F), | |
|             mFX(nullptr), | |
|             mFY(nullptr) | |
|         { | |
|             EstimateDerivatives(spatialDelta); | |
|             ProcessTriangles(); | |
|         } | |
| 
 | |
|         IntpQuadraticNonuniform2(TriangleMesh const& mesh, Real const* F, Real const* FX, Real const* FY) | |
|             : | |
|             mMesh(&mesh), | |
|             mF(F), | |
|             mFX(FX), | |
|             mFY(FY) | |
|         { | |
|             ProcessTriangles(); | |
|         } | |
| 
 | |
|         // Quadratic interpolation.  The return value is 'true' if and only if | |
|         // the input point is in the convex hull of the input vertices, in | |
|         // which case the interpolation is valid. | |
|         bool operator()(Vector2<Real> const& P, Real& F, Real& FX, Real& FY) const | |
|         { | |
|             auto t = mMesh->GetContainingTriangle(P); | |
|             if (t == mMesh->GetInvalidIndex()) | |
|             { | |
|                 // The point is outside the triangulation. | |
|                 return false; | |
|             } | |
| 
 | |
|             // Get the vertices of the triangle. | |
|             std::array<Vector2<Real>, 3> V; | |
|             mMesh->GetVertices(t, V); | |
| 
 | |
|             // Get the additional information for the triangle. | |
|             TriangleData const& tData = mTData[t]; | |
| 
 | |
|             // Determine which of the six subtriangles contains the target | |
|             // point.  Theoretically, P must be in one of these subtriangles. | |
|             Vector2<Real> sub0 = tData.center; | |
|             Vector2<Real> sub1; | |
|             Vector2<Real> sub2 = tData.intersect[2]; | |
|             Vector3<Real> bary; | |
|             int index; | |
| 
 | |
|             Real const zero = (Real)0, one = (Real)1; | |
|             AlignedBox3<Real> barybox({ zero, zero, zero }, { one, one, one }); | |
|             DCPQuery<Real, Vector3<Real>, AlignedBox3<Real>> pbQuery; | |
|             int minIndex = 0; | |
|             Real minDistance = (Real)-1; | |
|             Vector3<Real> minBary{ (Real)0, (Real)0, (Real)0 }; | |
|             Vector2<Real> minSub0{ (Real)0, (Real)0 }; | |
|             Vector2<Real> minSub1{ (Real)0, (Real)0 }; | |
|             Vector2<Real> minSub2{ (Real)0, (Real)0 }; | |
| 
 | |
|             for (index = 1; index <= 6; ++index) | |
|             { | |
|                 sub1 = sub2; | |
|                 if (index % 2) | |
|                 { | |
|                     sub2 = V[index / 2]; | |
|                 } | |
|                 else | |
|                 { | |
|                     sub2 = tData.intersect[index / 2 - 1]; | |
|                 } | |
| 
 | |
|                 bool valid = ComputeBarycentrics(P, sub0, sub1, sub2, &bary[0]); | |
|                 if (valid | |
|                     && zero <= bary[0] && bary[0] <= one | |
|                     && zero <= bary[1] && bary[1] <= one | |
|                     && zero <= bary[2] && bary[2] <= one) | |
|                 { | |
|                     // P is in triangle <Sub0,Sub1,Sub2> | |
|                     break; | |
|                 } | |
| 
 | |
|                 // When computing with floating-point arithmetic, rounding | |
|                 // errors can cause us to reach this code when, theoretically, | |
|                 // the point is in the subtriangle.  Keep track of the | |
|                 // (b0,b1,b2) that is closest to the barycentric cube [0,1]^3 | |
|                 // and choose the triangle corresponding to it when all 6 | |
|                 // tests previously fail. | |
|                 Real distance = pbQuery(bary, barybox).distance; | |
|                 if (minIndex == 0 || distance < minDistance) | |
|                 { | |
|                     minDistance = distance; | |
|                     minIndex = index; | |
|                     minBary = bary; | |
|                     minSub0 = sub0; | |
|                     minSub1 = sub1; | |
|                     minSub2 = sub2; | |
|                 } | |
|             } | |
| 
 | |
|             // If the subtriangle was not found, rounding errors caused | |
|             // problems.  Choose the barycentric point closest to the box. | |
|             if (index > 6) | |
|             { | |
|                 index = minIndex; | |
|                 bary = minBary; | |
|                 sub0 = minSub0; | |
|                 sub1 = minSub1; | |
|                 sub2 = minSub2; | |
|             } | |
| 
 | |
|             // Fetch Bezier control points. | |
|             Real bez[6] = | |
|             { | |
|                 tData.coeff[0], | |
|                 tData.coeff[12 + index], | |
|                 tData.coeff[13 + (index % 6)], | |
|                 tData.coeff[index], | |
|                 tData.coeff[6 + index], | |
|                 tData.coeff[1 + (index % 6)] | |
|             }; | |
| 
 | |
|             // Evaluate Bezier quadratic. | |
|             F = bary[0] * (bez[0] * bary[0] + bez[1] * bary[1] + bez[2] * bary[2]) + | |
|                 bary[1] * (bez[1] * bary[0] + bez[3] * bary[1] + bez[4] * bary[2]) + | |
|                 bary[2] * (bez[2] * bary[0] + bez[4] * bary[1] + bez[5] * bary[2]); | |
| 
 | |
|             // Evaluate barycentric derivatives of F. | |
|             Real FU = ((Real)2) * (bez[0] * bary[0] + bez[1] * bary[1] + | |
|                 bez[2] * bary[2]); | |
|             Real FV = ((Real)2) * (bez[1] * bary[0] + bez[3] * bary[1] + | |
|                 bez[4] * bary[2]); | |
|             Real FW = ((Real)2) * (bez[2] * bary[0] + bez[4] * bary[1] + | |
|                 bez[5] * bary[2]); | |
|             Real duw = FU - FW; | |
|             Real dvw = FV - FW; | |
| 
 | |
|             // Convert back to (x,y) coordinates. | |
|             Real m00 = sub0[0] - sub2[0]; | |
|             Real m10 = sub0[1] - sub2[1]; | |
|             Real m01 = sub1[0] - sub2[0]; | |
|             Real m11 = sub1[1] - sub2[1]; | |
|             Real inv = ((Real)1) / (m00 * m11 - m10 * m01); | |
| 
 | |
|             FX = inv * (m11 * duw - m10 * dvw); | |
|             FY = inv * (m00 * dvw - m01 * duw); | |
|             return true; | |
|         } | |
| 
 | |
|     private: | |
|         void EstimateDerivatives(Real spatialDelta) | |
|         { | |
|             auto numVertices = mMesh->GetNumVertices(); | |
|             Vector2<Real> const* vertices = mMesh->GetVertices(); | |
|             auto numTriangles = mMesh->GetNumTriangles(); | |
|             int const* indices = mMesh->GetIndices(); | |
| 
 | |
|             mFXStorage.resize(numVertices); | |
|             mFYStorage.resize(numVertices); | |
|             std::vector<Real> FZ(numVertices); | |
|             std::fill(mFXStorage.begin(), mFXStorage.end(), (Real)0); | |
|             std::fill(mFYStorage.begin(), mFYStorage.end(), (Real)0); | |
|             std::fill(FZ.begin(), FZ.end(), (Real)0); | |
| 
 | |
|             mFX = &mFXStorage[0]; | |
|             mFY = &mFYStorage[0]; | |
| 
 | |
|             // Accumulate normals at spatial locations (averaging process). | |
|             for (auto t = 0; t < numTriangles; ++t) | |
|             { | |
|                 // Get three vertices of triangle. | |
|                 int v0 = *indices++; | |
|                 int v1 = *indices++; | |
|                 int v2 = *indices++; | |
| 
 | |
|                 // Compute normal vector of triangle (with positive | |
|                 // z-component). | |
|                 Real dx1 = vertices[v1][0] - vertices[v0][0]; | |
|                 Real dy1 = vertices[v1][1] - vertices[v0][1]; | |
|                 Real dz1 = mF[v1] - mF[v0]; | |
|                 Real dx2 = vertices[v2][0] - vertices[v0][0]; | |
|                 Real dy2 = vertices[v2][1] - vertices[v0][1]; | |
|                 Real dz2 = mF[v2] - mF[v0]; | |
|                 Real nx = dy1 * dz2 - dy2 * dz1; | |
|                 Real ny = dz1 * dx2 - dz2 * dx1; | |
|                 Real nz = dx1 * dy2 - dx2 * dy1; | |
|                 if (nz < (Real)0) | |
|                 { | |
|                     nx = -nx; | |
|                     ny = -ny; | |
|                     nz = -nz; | |
|                 } | |
| 
 | |
|                 mFXStorage[v0] += nx;  mFYStorage[v0] += ny;  FZ[v0] += nz; | |
|                 mFXStorage[v1] += nx;  mFYStorage[v1] += ny;  FZ[v1] += nz; | |
|                 mFXStorage[v2] += nx;  mFYStorage[v2] += ny;  FZ[v2] += nz; | |
|             } | |
| 
 | |
|             // Scale the normals to form (x,y,-1). | |
|             for (auto i = 0; i < numVertices; ++i) | |
|             { | |
|                 if (FZ[i] != (Real)0) | |
|                 { | |
|                     Real inv = -spatialDelta / FZ[i]; | |
|                     mFXStorage[i] *= inv; | |
|                     mFYStorage[i] *= inv; | |
|                 } | |
|                 else | |
|                 { | |
|                     mFXStorage[i] = (Real)0; | |
|                     mFYStorage[i] = (Real)0; | |
|                 } | |
|             } | |
|         } | |
| 
 | |
|         void ProcessTriangles() | |
|         { | |
|             // Add degenerate triangles to boundary triangles so that | |
|             // interpolation at the boundary can be treated in the same way | |
|             // as interpolation in the interior. | |
|  | |
|             // Compute centers of inscribed circles for triangles. | |
|             Vector2<Real> const* vertices = mMesh->GetVertices(); | |
|             auto numTriangles = mMesh->GetNumTriangles(); | |
|             int const* indices = mMesh->GetIndices(); | |
|             mTData.resize(numTriangles); | |
|             for (auto t = 0; t < numTriangles; ++t) | |
|             { | |
|                 int v0 = *indices++; | |
|                 int v1 = *indices++; | |
|                 int v2 = *indices++; | |
|                 Circle2<Real> circle; | |
|                 Inscribe(vertices[v0], vertices[v1], vertices[v2], circle); | |
|                 mTData[t].center = circle.center; | |
|             } | |
| 
 | |
|             // Compute cross-edge intersections. | |
|             for (auto t = 0; t < numTriangles; ++t) | |
|             { | |
|                 ComputeCrossEdgeIntersections(t); | |
|             } | |
| 
 | |
|             // Compute Bezier coefficients. | |
|             for (auto t = 0; t < numTriangles; ++t) | |
|             { | |
|                 ComputeCoefficients(t); | |
|             } | |
|         } | |
| 
 | |
|         void ComputeCrossEdgeIntersections(int t) | |
|         { | |
|             // Get the vertices of the triangle. | |
|             std::array<Vector2<Real>, 3> V; | |
|             mMesh->GetVertices(t, V); | |
| 
 | |
|             // Get the centers of adjacent triangles. | |
|             TriangleData& tData = mTData[t]; | |
|             std::array<int, 3> adjacencies = { 0, 0, 0 }; | |
|             mMesh->GetAdjacencies(t, adjacencies); | |
|             for (int j0 = 2, j1 = 0; j1 < 3; j0 = j1++) | |
|             { | |
|                 int a = adjacencies[j0]; | |
|                 if (a >= 0) | |
|                 { | |
|                     // Get center of adjacent triangle's inscribing circle. | |
|                     Vector2<Real> U = mTData[a].center; | |
|                     Real m00 = V[j0][1] - V[j1][1]; | |
|                     Real m01 = V[j1][0] - V[j0][0]; | |
|                     Real m10 = tData.center[1] - U[1]; | |
|                     Real m11 = U[0] - tData.center[0]; | |
|                     Real r0 = m00 * V[j0][0] + m01 * V[j0][1]; | |
|                     Real r1 = m10 * tData.center[0] + m11 * tData.center[1]; | |
|                     Real invDet = ((Real)1) / (m00 * m11 - m01 * m10); | |
|                     tData.intersect[j0][0] = (m11 * r0 - m01 * r1) * invDet; | |
|                     tData.intersect[j0][1] = (m00 * r1 - m10 * r0) * invDet; | |
|                 } | |
|                 else | |
|                 { | |
|                     // No adjacent triangle, use center of edge. | |
|                     tData.intersect[j0] = (Real)0.5 * (V[j0] + V[j1]); | |
|                 } | |
|             } | |
|         } | |
| 
 | |
|         void ComputeCoefficients(int t) | |
|         { | |
|             // Get the vertices of the triangle. | |
|             std::array<Vector2<Real>, 3> V; | |
|             mMesh->GetVertices(t, V); | |
| 
 | |
|             // Get the additional information for the triangle. | |
|             TriangleData& tData = mTData[t]; | |
| 
 | |
|             // Get the sample data at main triangle vertices. | |
|             std::array<int, 3> indices = { 0, 0, 0 }; | |
|             mMesh->GetIndices(t, indices); | |
|             std::array<Jet, 3> jet; | |
|             for (int j = 0; j < 3; ++j) | |
|             { | |
|                 int k = indices[j]; | |
|                 jet[j].F = mF[k]; | |
|                 jet[j].FX = mFX[k]; | |
|                 jet[j].FY = mFY[k]; | |
|             } | |
| 
 | |
|             // Get centers of adjacent triangles. | |
|             std::array<int, 3> adjacencies = { 0, 0, 0 }; | |
|             mMesh->GetAdjacencies(t, adjacencies); | |
|             Vector2<Real> U[3]; | |
|             for (int j0 = 2, j1 = 0; j1 < 3; j0 = j1++) | |
|             { | |
|                 int a = adjacencies[j0]; | |
|                 if (a >= 0) | |
|                 { | |
|                     // Get center of adjacent triangle's circumscribing  | |
|                     // circle. | |
|                     U[j0] = mTData[a].center; | |
|                 } | |
|                 else | |
|                 { | |
|                     // No adjacent triangle, use center of edge. | |
|                     U[j0] = ((Real)0.5) * (V[j0] + V[j1]); | |
|                 } | |
|             } | |
| 
 | |
|             // Compute intermediate terms. | |
|             std::array<Real, 3> cenT, cen0, cen1, cen2; | |
|             mMesh->GetBarycentrics(t, tData.center, cenT); | |
|             mMesh->GetBarycentrics(t, U[0], cen0); | |
|             mMesh->GetBarycentrics(t, U[1], cen1); | |
|             mMesh->GetBarycentrics(t, U[2], cen2); | |
| 
 | |
|             Real alpha = (cenT[1] * cen1[0] - cenT[0] * cen1[1]) / (cen1[0] - cenT[0]); | |
|             Real beta = (cenT[2] * cen2[1] - cenT[1] * cen2[2]) / (cen2[1] - cenT[1]); | |
|             Real gamma = (cenT[0] * cen0[2] - cenT[2] * cen0[0]) / (cen0[2] - cenT[2]); | |
|             Real oneMinusAlpha = (Real)1 - alpha; | |
|             Real oneMinusBeta = (Real)1 - beta; | |
|             Real oneMinusGamma = (Real)1 - gamma; | |
| 
 | |
|             Real const zero = static_cast<Real>(0); | |
|             std::array<Real, 9> A = { zero, zero, zero, zero, zero, zero, zero, zero, zero }; | |
|             std::array<Real, 9> B = { zero, zero, zero, zero, zero, zero, zero, zero, zero }; | |
| 
 | |
|             Real tmp = cenT[0] * V[0][0] + cenT[1] * V[1][0] + cenT[2] * V[2][0]; | |
|             A[0] = (Real)0.5 * (tmp - V[0][0]); | |
|             A[1] = (Real)0.5 * (tmp - V[1][0]); | |
|             A[2] = (Real)0.5 * (tmp - V[2][0]); | |
|             A[3] = (Real)0.5 * beta * (V[2][0] - V[0][0]); | |
|             A[4] = (Real)0.5 * oneMinusGamma * (V[1][0] - V[0][0]); | |
|             A[5] = (Real)0.5 * gamma * (V[0][0] - V[1][0]); | |
|             A[6] = (Real)0.5 * oneMinusAlpha * (V[2][0] - V[1][0]); | |
|             A[7] = (Real)0.5 * alpha * (V[1][0] - V[2][0]); | |
|             A[8] = (Real)0.5 * oneMinusBeta * (V[0][0] - V[2][0]); | |
| 
 | |
|             tmp = cenT[0] * V[0][1] + cenT[1] * V[1][1] + cenT[2] * V[2][1]; | |
|             B[0] = (Real)0.5 * (tmp - V[0][1]); | |
|             B[1] = (Real)0.5 * (tmp - V[1][1]); | |
|             B[2] = (Real)0.5 * (tmp - V[2][1]); | |
|             B[3] = (Real)0.5 * beta * (V[2][1] - V[0][1]); | |
|             B[4] = (Real)0.5 * oneMinusGamma * (V[1][1] - V[0][1]); | |
|             B[5] = (Real)0.5 * gamma * (V[0][1] - V[1][1]); | |
|             B[6] = (Real)0.5 * oneMinusAlpha * (V[2][1] - V[1][1]); | |
|             B[7] = (Real)0.5 * alpha * (V[1][1] - V[2][1]); | |
|             B[8] = (Real)0.5 * oneMinusBeta * (V[0][1] - V[2][1]); | |
| 
 | |
|             // Compute Bezier coefficients. | |
|             tData.coeff[2] = jet[0].F; | |
|             tData.coeff[4] = jet[1].F; | |
|             tData.coeff[6] = jet[2].F; | |
| 
 | |
|             tData.coeff[14] = jet[0].F + A[0] * jet[0].FX + B[0] * jet[0].FY; | |
|             tData.coeff[7] = jet[0].F + A[3] * jet[0].FX + B[3] * jet[0].FY; | |
|             tData.coeff[8] = jet[0].F + A[4] * jet[0].FX + B[4] * jet[0].FY; | |
|             tData.coeff[16] = jet[1].F + A[1] * jet[1].FX + B[1] * jet[1].FY; | |
|             tData.coeff[9] = jet[1].F + A[5] * jet[1].FX + B[5] * jet[1].FY; | |
|             tData.coeff[10] = jet[1].F + A[6] * jet[1].FX + B[6] * jet[1].FY; | |
|             tData.coeff[18] = jet[2].F + A[2] * jet[2].FX + B[2] * jet[2].FY; | |
|             tData.coeff[11] = jet[2].F + A[7] * jet[2].FX + B[7] * jet[2].FY; | |
|             tData.coeff[12] = jet[2].F + A[8] * jet[2].FX + B[8] * jet[2].FY; | |
| 
 | |
|             tData.coeff[5] = alpha * tData.coeff[10] + oneMinusAlpha * tData.coeff[11]; | |
|             tData.coeff[17] = alpha * tData.coeff[16] + oneMinusAlpha * tData.coeff[18]; | |
|             tData.coeff[1] = beta * tData.coeff[12] + oneMinusBeta * tData.coeff[7]; | |
|             tData.coeff[13] = beta * tData.coeff[18] + oneMinusBeta * tData.coeff[14]; | |
|             tData.coeff[3] = gamma * tData.coeff[8] + oneMinusGamma * tData.coeff[9]; | |
|             tData.coeff[15] = gamma * tData.coeff[14] + oneMinusGamma * tData.coeff[16]; | |
|             tData.coeff[0] = cenT[0] * tData.coeff[14] + cenT[1] * tData.coeff[16] + cenT[2] * tData.coeff[18]; | |
|         } | |
| 
 | |
|         class TriangleData | |
|         { | |
|         public: | |
|             Vector2<Real> center; | |
|             std::array<Vector2<Real>, 3> intersect; | |
|             std::array<Real, 19> coeff; | |
|         }; | |
| 
 | |
|         class Jet | |
|         { | |
|         public: | |
|             Jet() | |
|                 : | |
|                 F(static_cast<Real>(0)), | |
|                 FX(static_cast<Real>(0)), | |
|                 FY(static_cast<Real>(0)) | |
|             { | |
|             } | |
| 
 | |
|             Real F, FX, FY; | |
|         }; | |
| 
 | |
|         TriangleMesh const* mMesh; | |
|         Real const* mF; | |
|         Real const* mFX; | |
|         Real const* mFY; | |
|         std::vector<Real> mFXStorage; | |
|         std::vector<Real> mFYStorage; | |
|         std::vector<TriangleData> mTData; | |
|     }; | |
| }
 | |
| 
 |