/* * Compute velocity due to vortons, for a subset of points in a uniform grid * izStart - starting value for z index * izEnd - ending value for z index * * This routine assumes CreateInfluenceTree has already executed, * and that the velocity grid has been allocated. */ void ComputeVelocityGridSlice(uint izStart, uint izEnd) { int numLayers = mInfluenceTree.GetDepth(); Vector3 vMinCorner = mVelGrid.GetMinCorner(); float nudge = 1.0f - 2.0f * global.GlobalVar.FLT_EPSILON; Vector3 vSpacing = mVelGrid.GetCellSpacing() * nudge; uint[] dims = { mVelGrid.GetNumPoints(0) , mVelGrid.GetNumPoints(1) , mVelGrid.GetNumPoints(2) }; uint numXY = dims[0] * dims[1]; uint[] idx = new uint[3]; for (idx[2] = izStart; idx[2] < izEnd; ++idx[2]) { // For subset of z index values... Vector3 vPosition; // Compute the z-coordinate of the world-space position of this gridpoint. vPosition.z = vMinCorner.z + (float)(idx[2]) * vSpacing.z; // Precompute the z contribution to the offset into the velocity grid. uint offsetZ = idx[2] * numXY; for (idx[1] = 0; idx[1] < dims[1]; ++idx[1]) { // For every gridpoint along the y-axis... // Compute the y-coordinate of the world-space position of this gridpoint. vPosition.y = vMinCorner.y + (float)(idx[1]) * vSpacing.y; // Precompute the y contribution to the offset into the velocity grid. uint offsetYZ = idx[1] * dims[0] + offsetZ; for (idx[0] = 0; idx[0] < dims[0]; ++idx[0]) { // For every gridpoint along the x-axis... // Compute the x-coordinate of the world-space position of this gridpoint. vPosition.x = vMinCorner.x + (float)(idx[0]) * vSpacing.x; // Compute the offset into the velocity grid. uint offsetXYZ = idx[0] + offsetYZ; // Compute the fluid flow velocity at this gridpoint, due to all vortons. uint[] zeros = { 0, 0, 0 }; // Starter indices for recursive algorithm mVelGrid[offsetXYZ] = ComputeVelocity(vPosition, zeros, numLayers - 1); } } } }
public static void ComputeJacobian(ref UniformGrid <Matrix3x3> jacobian, UniformGrid <Vector> vec) { Vector3 spacing = vec.GetCellSpacing(); // Avoid divide-by-zero when z size is effectively 0 (for 2D domains) Vector3 reciprocalSpacing = new Vector3(1.0f / spacing.x, 1.0f / spacing.y, spacing.z > float.Epsilon ? 1.0f / spacing.z : 0.0f); Vector3 halfReciprocalSpacing = (0.5f * reciprocalSpacing); uint[] dims = { vec.GetNumPoints(0), vec.GetNumPoints(1), vec.GetNumPoints(2) }; uint[] dimsMinus1 = { vec.GetNumPoints(0) - 1, vec.GetNumPoints(1) - 1, vec.GetNumPoints(2) - 1 }; uint numXY = dims[0] * dims[1]; uint[] index = new uint[3]; // Compute derivatives for interior (i.e. away from boundaries). for (index[2] = 1; index[2] < dimsMinus1[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 1; index[1] < dimsMinus1[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 1; index[0] < dimsMinus1[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; Matrix3x3 rMatrix = jacobian[offsetX0Y0Z0]; /* Compute d/dx */ rMatrix.x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; /* Compute d/dy */ rMatrix.y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; /* Compute d/dz */ rMatrix.z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } #region boundariesDerivatives // Compute derivatives for boundaries: 6 faces of box. ------------------------------------------------------------ // In some situations, these macros compute extraneous data. // A tiny bit more efficiency could be squeezed from this routine, // but it turns out to be well under 1% of the total expense. // Compute derivatives for -X boundary. index[0] = 0; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for -Y boundary. index[1] = 0; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for -Z boundary. index[2] = 0; { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for +X boundary. index[0] = dimsMinus1[0]; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for +Y boundary. index[1] = dimsMinus1[1]; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for +Z boundary. index[2] = dimsMinus1[2]; { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } #endregion } }
/* * Compute velocity at a given point in space, due to influence of vortons * * vPosition - point in space whose velocity to evaluate * indices - indices of cell to visit in the given layer * iLayer - which layer to process * * returns velocity at vPosition, due to influence of vortons * This is a recursive algorithm with time complexity O(log(N)). * The outermost caller should pass in mInfluenceTree.GetDepth(). * */ Vector ComputeVelocity(Vector3 vPosition, uint[] indices, int iLayer) { Vector velocityAccumulator = new Vector(); UniformGrid <Vorton> rChildLayer = mInfluenceTree[(uint)iLayer - 1]; uint[] pClusterDims = mInfluenceTree.GetDecimations(iLayer); uint[] clusterMinIndices = mInfluenceTree.GetChildClusterMinCornerIndex(pClusterDims, indices); Vector3 vGridMinCorner = rChildLayer.GetMinCorner(); Vector3 vSpacing = rChildLayer.GetCellSpacing(); uint[] increment = new uint[3]; uint numXchild = rChildLayer.GetNumPoints(0); uint numXYchild = numXchild * rChildLayer.GetNumPoints(1); // The larger this is, the more accurate (and slower) the evaluation. // Reasonable values lie in [0.00001,4.0]. // Setting this to 0 leads to very bad errors, but values greater than (tiny) lead to drastic improvements. // Changes in margin have a quantized effect since they effectively indicate how many additional // cluster subdivisions to visit. float marginFactor = 0.0001f; // 0.4f ; // ship with this number: 0.0001f ; test with 0.4 // When domain is 2D in XY plane, min.z==max.z so vPos.z test below would fail unless margin.z!=0. Vector3 margin = marginFactor * vSpacing + (0.0f == vSpacing.z ? new Vector3(0, 0, float.Epsilon) : Vector3.zero); // For each cell of child layer in this grid cluster... for (increment[2] = 0; increment[2] < pClusterDims[2]; ++increment[2]) { uint[] idxChild = new uint[3]; idxChild[2] = clusterMinIndices[2] + increment[2]; Vector3 vCellMinCorner, vCellMaxCorner; vCellMinCorner.z = vGridMinCorner.z + (float)(idxChild[2]) * vSpacing.z; vCellMaxCorner.z = vGridMinCorner.z + (float)(idxChild[2] + 1) * vSpacing.z; uint offsetZ = idxChild[2] * numXYchild; for (increment[1] = 0; increment[1] < pClusterDims[1]; ++increment[1]) { idxChild[1] = clusterMinIndices[1] + increment[1]; vCellMinCorner.y = vGridMinCorner.y + (float)(idxChild[1]) * vSpacing.y; vCellMaxCorner.y = vGridMinCorner.y + (float)(idxChild[1] + 1) * vSpacing.y; uint offsetYZ = idxChild[1] * numXchild + offsetZ; for (increment[0] = 0; increment[0] < pClusterDims[0]; ++increment[0]) { idxChild[0] = clusterMinIndices[0] + increment[0]; vCellMinCorner.x = vGridMinCorner.x + (float)(idxChild[0]) * vSpacing.x; vCellMaxCorner.x = vGridMinCorner.x + (float)(idxChild[0] + 1) * vSpacing.x; if ( (iLayer > 1) && (vPosition.x >= vCellMinCorner.x - margin.x) && (vPosition.y >= vCellMinCorner.y - margin.y) && (vPosition.z >= vCellMinCorner.z - margin.z) && (vPosition.x < vCellMaxCorner.x + margin.x) && (vPosition.y < vCellMaxCorner.y + margin.y) && (vPosition.z < vCellMaxCorner.z + margin.z) ) { // Test position is inside childCell and currentLayer > 0... // Recurse child layer. Vector upVelocicy = ComputeVelocity(vPosition, idxChild, iLayer - 1); velocityAccumulator += upVelocicy; } else { // Test position is outside childCell, or reached leaf node. // Compute velocity induced by cell at corner point x. // Accumulate influence, storing in velocityAccumulator. uint offsetXYZ = idxChild[0] + offsetYZ; // Add velocity due to this vorton to the accumulator rChildLayer[offsetXYZ].AccumulateVelocity(ref velocityAccumulator, vPosition); } } } } return(velocityAccumulator); }
public static void ComputeJacobian(ref UniformGrid<Matrix3x3> jacobian, UniformGrid<Vector> vec) { Vector3 spacing = vec.GetCellSpacing(); // Avoid divide-by-zero when z size is effectively 0 (for 2D domains) Vector3 reciprocalSpacing = new Vector3(1.0f / spacing.x , 1.0f / spacing.y, spacing.z > float.Epsilon ? 1.0f / spacing.z : 0.0f ) ; Vector3 halfReciprocalSpacing = (0.5f * reciprocalSpacing) ; uint[] dims = { vec.GetNumPoints(0), vec.GetNumPoints(1), vec.GetNumPoints(2) }; uint[] dimsMinus1 = { vec.GetNumPoints(0) - 1, vec.GetNumPoints(1) - 1, vec.GetNumPoints(2) - 1 }; uint numXY = dims[0] * dims[1]; uint[] index = new uint[3]; // Compute derivatives for interior (i.e. away from boundaries). for (index[2] = 1; index[2] < dimsMinus1[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 1; index[1] < dimsMinus1[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 1; index[0] < dimsMinus1[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; Matrix3x3 rMatrix = jacobian[offsetX0Y0Z0]; /* Compute d/dx */ rMatrix.x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; /* Compute d/dy */ rMatrix.y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; /* Compute d/dz */ rMatrix.z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } #region boundariesDerivatives // Compute derivatives for boundaries: 6 faces of box. ------------------------------------------------------------ // In some situations, these macros compute extraneous data. // A tiny bit more efficiency could be squeezed from this routine, // but it turns out to be well under 1% of the total expense. // Compute derivatives for -X boundary. index[0] = 0; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for -Y boundary. index[1] = 0; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for -Z boundary. index[2] = 0; { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for +X boundary. index[0] = dimsMinus1[0]; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for +Y boundary. index[1] = dimsMinus1[1]; for (index[2] = 0; index[2] < dims[2]; ++index[2]) { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } } // Compute derivatives for +Z boundary. index[2] = dimsMinus1[2]; { //ASSIGN_Z_OFFSETS uint offsetZM = numXY * (index[2] - 1); uint offsetZ0 = numXY * index[2]; uint offsetZP = numXY * (index[2] + 1); for (index[1] = 0; index[1] < dims[1]; ++index[1]) { //ASSIGN_YZ_OFFSETS uint offsetYMZ0 = dims[0] * (index[1] - 1) + offsetZ0; uint offsetY0Z0 = dims[0] * index[1] + offsetZ0; uint offsetYPZ0 = dims[0] * (index[1] + 1) + offsetZ0; uint offsetY0ZM = dims[0] * index[1] + offsetZM; uint offsetY0ZP = dims[0] * index[1] + offsetZP; for (index[0] = 0; index[0] < dims[0]; ++index[0]) { //ASSIGN_XYZ_OFFSETS uint offsetX0Y0Z0 = index[0] + offsetY0Z0; uint offsetXMY0Z0 = index[0] - 1 + offsetY0Z0; uint offsetXPY0Z0 = index[0] + 1 + offsetY0Z0; uint offsetX0YMZ0 = index[0] + offsetYMZ0; uint offsetX0YPZ0 = index[0] + offsetYPZ0; uint offsetX0Y0ZM = index[0] + offsetY0ZM; uint offsetX0Y0ZP = index[0] + offsetY0ZP; //COMPUTE_FINITE_DIFF if (index[0] == 0) { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.x; } else if (index[0] == dimsMinus1[0]) { jacobian[offsetX0Y0Z0].x = (vec[offsetX0Y0Z0].v - vec[offsetXMY0Z0].v) * reciprocalSpacing.x; } else { jacobian[offsetX0Y0Z0].x = (vec[offsetXPY0Z0].v - vec[offsetXMY0Z0].v) * halfReciprocalSpacing.x; } if (index[1] == 0) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.y; } else if (index[1] == dimsMinus1[1]) { jacobian[offsetX0Y0Z0].y = (vec[offsetX0Y0Z0].v - vec[offsetX0YMZ0].v) * reciprocalSpacing.y; } else { jacobian[offsetX0Y0Z0].y = (vec[offsetX0YPZ0].v - vec[offsetX0YMZ0].v) * halfReciprocalSpacing.y; } if (index[2] == 0) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0Z0].v) * reciprocalSpacing.z; } else if (index[2] == dimsMinus1[2]) { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0Z0].v - vec[offsetX0Y0ZM].v) * reciprocalSpacing.z; } else { jacobian[offsetX0Y0Z0].z = (vec[offsetX0Y0ZP].v - vec[offsetX0Y0ZM].v) * halfReciprocalSpacing.z; } } } #endregion } }