// Given a grid cell and an isolevel, calculate the triangular facets required to represent the isosurface through the cell. // Return the number of triangular facets, the array "triangles" will be loaded up with the vertices at most 5 triangular facets. // 0 will be returned if the grid cell is either totally above of totally below the isolevel. public static void Polygonise(GridCell grid, double isolevel, ref List <Triangle> theTriangleList) { // Determine the index into the edge table which tells us which vertices are inside of the surface int cubeindex = 0; if (grid.val[0] < isolevel) { cubeindex |= 1; } if (grid.val[1] < isolevel) { cubeindex |= 2; } if (grid.val[2] < isolevel) { cubeindex |= 4; } if (grid.val[3] < isolevel) { cubeindex |= 8; } if (grid.val[4] < isolevel) { cubeindex |= 16; } if (grid.val[5] < isolevel) { cubeindex |= 32; } if (grid.val[6] < isolevel) { cubeindex |= 64; } if (grid.val[7] < isolevel) { cubeindex |= 128; } // Cube is entirely in/out of the surface if (EdgeTable.LookupTable[cubeindex] == 0) { return; } Point3D[] vertlist = new Point3D[12]; // Find the vertices where the surface intersects the cube if ((EdgeTable.LookupTable[cubeindex] & 1) > 0) { vertlist[0] = VertexInterp(isolevel, grid.p[0], grid.p[1], grid.val[0], grid.val[1]); } if ((EdgeTable.LookupTable[cubeindex] & 2) > 0) { vertlist[1] = VertexInterp(isolevel, grid.p[1], grid.p[2], grid.val[1], grid.val[2]); } if ((EdgeTable.LookupTable[cubeindex] & 4) > 0) { vertlist[2] = VertexInterp(isolevel, grid.p[2], grid.p[3], grid.val[2], grid.val[3]); } if ((EdgeTable.LookupTable[cubeindex] & 8) > 0) { vertlist[3] = VertexInterp(isolevel, grid.p[3], grid.p[0], grid.val[3], grid.val[0]); } if ((EdgeTable.LookupTable[cubeindex] & 16) > 0) { vertlist[4] = VertexInterp(isolevel, grid.p[4], grid.p[5], grid.val[4], grid.val[5]); } if ((EdgeTable.LookupTable[cubeindex] & 32) > 0) { vertlist[5] = VertexInterp(isolevel, grid.p[5], grid.p[6], grid.val[5], grid.val[6]); } if ((EdgeTable.LookupTable[cubeindex] & 64) > 0) { vertlist[6] = VertexInterp(isolevel, grid.p[6], grid.p[7], grid.val[6], grid.val[7]); } if ((EdgeTable.LookupTable[cubeindex] & 128) > 0) { vertlist[7] = VertexInterp(isolevel, grid.p[7], grid.p[4], grid.val[7], grid.val[4]); } if ((EdgeTable.LookupTable[cubeindex] & 256) > 0) { vertlist[8] = VertexInterp(isolevel, grid.p[0], grid.p[4], grid.val[0], grid.val[4]); } if ((EdgeTable.LookupTable[cubeindex] & 512) > 0) { vertlist[9] = VertexInterp(isolevel, grid.p[1], grid.p[5], grid.val[1], grid.val[5]); } if ((EdgeTable.LookupTable[cubeindex] & 1024) > 0) { vertlist[10] = VertexInterp(isolevel, grid.p[2], grid.p[6], grid.val[2], grid.val[6]); } if ((EdgeTable.LookupTable[cubeindex] & 2048) > 0) { vertlist[11] = VertexInterp(isolevel, grid.p[3], grid.p[7], grid.val[3], grid.val[7]); } // Create the triangle for (int i = 0; TriTable.LookupTable[cubeindex, i] != -1; i += 3) { Triangle aTriangle = new Triangle(); aTriangle.p[0] = vertlist[TriTable.LookupTable[cubeindex, i]]; aTriangle.p[1] = vertlist[TriTable.LookupTable[cubeindex, i + 1]]; aTriangle.p[2] = vertlist[TriTable.LookupTable[cubeindex, i + 2]]; theTriangleList.Add(aTriangle); } }
private static List<Triangle> ComputeTriangles(CTSliceInfo[] slices, int theIsoValue) { // 1. Calculate the Center Point // ============================= // For moving the 3D model with the mouse, the implementation is taken from Code Project 'WPF 3D Primer'. // See also: 'http://www.codeproject.com/Articles/23332/WPF-3D-Primer#' // However, this implementation needs the 3D model to be centered in the origin of the coordinate system. // As a consequence, all CT Slices have to be shifted by the Center Point (method 'AdjustPatientPositionToCenterPoint()') CTSliceInfo firstCT = slices[0]; CTSliceInfo lastCT = slices[slices.Length - 1]; double Center_X = firstCT.UpperLeft_X + (firstCT.PixelSpacing_X * firstCT.ColumnCount / 2); double Center_Y = firstCT.UpperLeft_Y + (firstCT.PixelSpacing_Y * firstCT.RowCount / 2); // CT Slices are already sorted ascending in Z direction double Center_Z = firstCT.UpperLeft_Z + ((lastCT.UpperLeft_Z - firstCT.UpperLeft_Z) / 2); // Create the Center Point Point3D aCenterPoint = new Point3D(Center_X, Center_Y, Center_Z); // 2. The Marching Cubes algorithm // =============================== // For each Voxel of the CT Slice, an own GridCell is created. // The IsoValue and the x/y/z information for each corner of the GridCell is taken from the direct neighbor voxels (of the same or adjacant CT Slice). // Looping has to be done over all CT Slices. List<Triangle> triangles = new List<Triangle>(); CTSliceInfo slice1 = null; CTSliceInfo slice2 = slices[0]; slice2.AdjustPatientPositionToCenterPoint(aCenterPoint); for (int sliceIdx = 1; sliceIdx != slices.Length; ++sliceIdx) { slice1 = slice2; slice2 = slices[sliceIdx]; slice2.AdjustPatientPositionToCenterPoint(aCenterPoint); for (int r = 0; r != slice1.RowCount - 1; ++r) { for (int c = 0; c != slice1.ColumnCount - 1; ++c) { GridCell aGridCell = new GridCell(sliceIdx, r, c, slice1, slice2); MarchingCubes.Polygonise(aGridCell, theIsoValue, ref triangles); } } } return triangles; }
// Given a grid cell and an isolevel, calculate the triangular facets required to represent the isosurface through the cell. // Return the number of triangular facets, the array "triangles" will be loaded up with the vertices at most 5 triangular facets. // 0 will be returned if the grid cell is either totally above of totally below the isolevel. public static void Polygonise(GridCell grid, double isolevel, ref List<Triangle> theTriangleList) { // Determine the index into the edge table which tells us which vertices are inside of the surface int cubeindex = 0; if (grid.val[0] < isolevel) cubeindex |= 1; if (grid.val[1] < isolevel) cubeindex |= 2; if (grid.val[2] < isolevel) cubeindex |= 4; if (grid.val[3] < isolevel) cubeindex |= 8; if (grid.val[4] < isolevel) cubeindex |= 16; if (grid.val[5] < isolevel) cubeindex |= 32; if (grid.val[6] < isolevel) cubeindex |= 64; if (grid.val[7] < isolevel) cubeindex |= 128; // Cube is entirely in/out of the surface if (EdgeTable.LookupTable[cubeindex] == 0) return; Point3D[] vertlist = new Point3D[12]; // Find the vertices where the surface intersects the cube if ((EdgeTable.LookupTable[cubeindex] & 1) > 0) vertlist[0] = VertexInterp(isolevel, grid.p[0], grid.p[1], grid.val[0], grid.val[1]); if ((EdgeTable.LookupTable[cubeindex] & 2) > 0) vertlist[1] = VertexInterp(isolevel, grid.p[1], grid.p[2], grid.val[1], grid.val[2]); if ((EdgeTable.LookupTable[cubeindex] & 4) > 0) vertlist[2] = VertexInterp(isolevel, grid.p[2], grid.p[3], grid.val[2], grid.val[3]); if ((EdgeTable.LookupTable[cubeindex] & 8) > 0) vertlist[3] = VertexInterp(isolevel, grid.p[3], grid.p[0], grid.val[3], grid.val[0]); if ((EdgeTable.LookupTable[cubeindex] & 16) > 0) vertlist[4] = VertexInterp(isolevel, grid.p[4], grid.p[5], grid.val[4], grid.val[5]); if ((EdgeTable.LookupTable[cubeindex] & 32) > 0) vertlist[5] = VertexInterp(isolevel, grid.p[5], grid.p[6], grid.val[5], grid.val[6]); if ((EdgeTable.LookupTable[cubeindex] & 64) > 0) vertlist[6] = VertexInterp(isolevel, grid.p[6], grid.p[7], grid.val[6], grid.val[7]); if ((EdgeTable.LookupTable[cubeindex] & 128) > 0) vertlist[7] = VertexInterp(isolevel, grid.p[7], grid.p[4], grid.val[7], grid.val[4]); if ((EdgeTable.LookupTable[cubeindex] & 256) > 0) vertlist[8] = VertexInterp(isolevel, grid.p[0], grid.p[4], grid.val[0], grid.val[4]); if ((EdgeTable.LookupTable[cubeindex] & 512) > 0) vertlist[9] = VertexInterp(isolevel, grid.p[1], grid.p[5], grid.val[1], grid.val[5]); if ((EdgeTable.LookupTable[cubeindex] & 1024) > 0) vertlist[10] = VertexInterp(isolevel, grid.p[2], grid.p[6], grid.val[2], grid.val[6]); if ((EdgeTable.LookupTable[cubeindex] & 2048) > 0) vertlist[11] = VertexInterp(isolevel, grid.p[3], grid.p[7], grid.val[3], grid.val[7]); // Create the triangle for (int i = 0; TriTable.LookupTable[cubeindex, i] != -1; i += 3) { Triangle aTriangle = new Triangle(); aTriangle.p[0] = vertlist[TriTable.LookupTable[cubeindex, i]]; aTriangle.p[1] = vertlist[TriTable.LookupTable[cubeindex, i + 1]]; aTriangle.p[2] = vertlist[TriTable.LookupTable[cubeindex, i + 2]]; theTriangleList.Add(aTriangle); } }