Cloned library of VTK-5.0.0 with extra build files for internal package management.
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.

603 lines
18 KiB

2 years ago
/*=========================================================================
Program: Visualization Toolkit
Module: $RCSfile: vtkRectilinearGridToTetrahedra.cxx,v $
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkRectilinearGridToTetrahedra.h"
#include "vtkCellArray.h"
#include "vtkCellData.h"
#include "vtkSignedCharArray.h"
#include "vtkExecutive.h"
#include "vtkFloatArray.h"
#include "vtkIdList.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkIntArray.h"
#include "vtkObjectFactory.h"
#include "vtkRectilinearGrid.h"
#include "vtkUnstructuredGrid.h"
#include "vtkVoxel.h"
vtkCxxRevisionMacro(vtkRectilinearGridToTetrahedra, "$Revision: 1.14 $");
vtkStandardNewMacro(vtkRectilinearGridToTetrahedra);
// ways to convert to a voxel to tetrahedra.
// Note that the values 0 and 1 and -1 and 2 are important in
// DetermineGridDivisionTypes()
#define VTK_TETRAHEDRALIZE_5 0
#define VTK_TETRAHEDRALIZE_5_FLIP 1
#define VTK_TETRAHEDRALIZE_6 6
#define VTK_TETRAHEDRALIZE_12_CONFORM -1
#define VTK_TETRAHEDRALIZE_12_CONFORM_FLIP 2
#define VTK_TETRAHEDRALIZE_12 10
//-------------------------------------------------------------------------
vtkRectilinearGridToTetrahedra::vtkRectilinearGridToTetrahedra()
{
this->TetraPerCell = VTK_VOXEL_TO_5_TET;
this->RememberVoxelId = 0;
}
//----------------------------------------------------------------------------
void vtkRectilinearGridToTetrahedra::SetInput(const double ExtentX,
const double ExtentY,
const double ExtentZ,
const double SpacingX,
const double SpacingY,
const double SpacingZ,
const double tol)
{
double Extent[3];
double Spacing[3];
Extent[0] = ExtentX; Extent[1] = ExtentY; Extent[2] = ExtentZ;
Spacing[0] = SpacingX; Spacing[1] = SpacingY; Spacing[2] = SpacingZ;
this->SetInput(Extent,Spacing,tol);
}
//----------------------------------------------------------------------------
// Create an input for the filter
void vtkRectilinearGridToTetrahedra::SetInput(const double Extent[3],
const double Spacing[3],
const double tol)
{
//
// Determine the number of points in each direction, and the positions
// The last voxel may have a different spacing to fit inside
// the selected region
//
int i, j;
int NumPointsInDir[3];
vtkFloatArray *Coord[3];
for(i = 0;i<3;i++)
{
double NumRegion = Extent[i]/Spacing[i];
// If we are really close to an integer number of elements, use the
// integer number
if (fabs(NumRegion - floor(NumRegion+0.5)) < tol*Spacing[i])
NumPointsInDir[i] = ((int) floor(NumRegion+0.5)) + 1;
else
NumPointsInDir[i] = (int) ceil(Extent[i]/Spacing[i])+1;
Coord[i] = vtkFloatArray::New();
Coord[i]->SetNumberOfValues(NumPointsInDir[i]+1);
// The last data point inserted is exactly the Extent
// Thus avoiding a bit of numerical error.
for(j=0;j<NumPointsInDir[i]-1;j++)
{
Coord[i]->SetValue(j,Spacing[i]*j);
}
Coord[i]->SetValue(NumPointsInDir[i]-1,Extent[i]);
}
//
// Form the grid
//
vtkRectilinearGrid *RectGrid = vtkRectilinearGrid::New();
RectGrid->SetDimensions(NumPointsInDir);
RectGrid->SetXCoordinates(Coord[0]);
RectGrid->SetYCoordinates(Coord[1]);
RectGrid->SetZCoordinates(Coord[2]);
Coord[0]->Delete();
Coord[1]->Delete();
Coord[2]->Delete();
// Get the reference counting right.
this->Superclass::SetInput(RectGrid);
RectGrid->Delete();
}
//----------------------------------------------------------------------------
// Determine how to Divide each voxel in the vtkRectilinearGrid
void vtkRectilinearGridToTetrahedra::DetermineGridDivisionTypes(
vtkRectilinearGrid *RectGrid,
vtkSignedCharArray *VoxelSubdivisionType,
const int &TetraPerCell)
{
int numRec = RectGrid->GetNumberOfCells();
int NumPointsInDir[3];
RectGrid->GetDimensions(NumPointsInDir);
// How to break into Tetrahedra.
// For division into 5's, we need to flip from one orientation to
// the next
int Rec[3];
int flip;
int i;
switch (TetraPerCell)
{
case (VTK_VOXEL_TO_12_TET):
for(i=0;i<numRec;i++)
{
VoxelSubdivisionType->SetValue(i,VTK_TETRAHEDRALIZE_12);
}
break;
case (VTK_VOXEL_TO_6_TET):
for(i=0;i<numRec;i++)
{
VoxelSubdivisionType->SetValue(i,VTK_TETRAHEDRALIZE_6);
}
break;
case (VTK_VOXEL_TO_5_TET):
for(Rec[0] = 0; Rec[0]<NumPointsInDir[0]-1; Rec[0]++)
{
for(Rec[1] = 0; Rec[1]<NumPointsInDir[1]-1; Rec[1]++)
{
flip = ( Rec[1] + Rec[0] ) % 2;
for(Rec[2] = 0; Rec[2]<NumPointsInDir[2]-1; Rec[2]++)
{
VoxelSubdivisionType->SetValue(RectGrid->ComputeCellId(Rec),flip);
flip = 1 - flip;
}
}
}
break;
case (VTK_VOXEL_TO_5_AND_12_TET):
for(Rec[0] = 0; Rec[0]<NumPointsInDir[0]-1; Rec[0]++)
{
for(Rec[1] = 0; Rec[1]<NumPointsInDir[1]-1; Rec[1]++)
{
flip = ( Rec[1] + Rec[0] ) % 2;
for(Rec[2] = 0; Rec[2]<NumPointsInDir[2]-1; Rec[2]++)
{
int CellId = RectGrid->ComputeCellId(Rec);
if (VoxelSubdivisionType->GetValue(CellId) == 12)
VoxelSubdivisionType->SetValue(CellId,3*flip-1);
else VoxelSubdivisionType->SetValue(CellId,flip);
flip = 1 - flip;
}
}
}
break;
}
}
//----------------------------------------------------------------------------
// Take the grid and make it into a tetrahedral mesh.
void vtkRectilinearGridToTetrahedra::GridToTetMesh(vtkRectilinearGrid *RectGrid,
vtkSignedCharArray *VoxelSubdivisionType,
const int &TetraPerCell,
const int &RememberVoxelId,
vtkUnstructuredGrid *TetMesh)
{
int i, j;
int numPts = RectGrid->GetNumberOfPoints();
int numRec = RectGrid->GetNumberOfCells();
// We need a point list and a cell list
vtkPoints *NodePoints = vtkPoints::New();
vtkCellArray *TetList = vtkCellArray::New();
// Guess number of points and cells!!
// For mixture of 5 and 12 tet per cell,
// it is easier to way overguess to avoid re-allocation
// slowness and range checking during insertion.
switch (TetraPerCell)
{
case (VTK_VOXEL_TO_5_TET):
NodePoints->Allocate(numPts);
TetList->Allocate(numPts*5*5,numPts);
break;
case (VTK_VOXEL_TO_5_AND_12_TET):
case (VTK_VOXEL_TO_12_TET):
NodePoints->Allocate(numPts*2);
TetList->Allocate(numPts*5*12,numPts);
break;
}
// Start by copying over the points
for(i=0;i<numPts;i++)
{
NodePoints->InsertNextPoint(RectGrid->GetPoint(i));
}
// If they want, we can add Scalar Data
// to the Tets indicating the Voxel Id the tet
// came from.
vtkIntArray *TetOriginalVoxel = NULL;
if (RememberVoxelId) {
TetOriginalVoxel = vtkIntArray::New();
TetOriginalVoxel->Allocate(12*numRec);
}
// 9 ids, 8 corners and a possible center to be added later
// during the tet creation
vtkIdList *VoxelCorners = vtkIdList::New();
VoxelCorners->SetNumberOfIds(9);
int NumTetFromVoxel;
for(i=0;i<numRec;i++)
{
RectGrid->GetCellPoints(i,VoxelCorners);
NumTetFromVoxel = TetrahedralizeVoxel(VoxelCorners,
(int)VoxelSubdivisionType->GetValue(i),
NodePoints,TetList);
if (RememberVoxelId)
{
for(j=0;j<NumTetFromVoxel;j++)
{
TetOriginalVoxel->InsertNextValue(i);
}
}
}
//
// It may be there are extra points at the end of the PointList.
//
NodePoints->Squeeze();
//
// Form the Mesh
//
// Need to tell the tet mesh that every cell is a Tetrahedron
int numTet = TetList->GetNumberOfCells();
int *CellTypes = new int[numTet];
for(i=0;i<numTet;i++)
{
CellTypes[i] = VTK_TETRA;
}
TetMesh->SetPoints(NodePoints);
TetMesh->SetCells(CellTypes,TetList);
//
// Add Scalar Types if wanted
//
if(RememberVoxelId)
{
TetOriginalVoxel->Squeeze();
int idx = TetMesh->GetCellData()->AddArray(TetOriginalVoxel);
TetMesh->GetCellData()->SetActiveAttribute(idx, vtkDataSetAttributes::SCALARS);
TetOriginalVoxel->Delete();
}
//
// Clean Up
//
delete[] CellTypes;
NodePoints->Delete();
TetList->Delete();
VoxelCorners->Delete();
TetMesh->Squeeze();
}
//----------------------------------------------------------------------------
// Helper Function for Tetrahedralize Voxel
inline void vtkRectilinearGridToTetrahedra::TetrahedralizeAddCenterPoint(
vtkIdList *VoxelCorners,
vtkPoints *NodeList)
{
// Need to add a center point
double c1[3], c2[3];
NodeList->GetPoint(VoxelCorners->GetId(0), c2);
NodeList->GetPoint(VoxelCorners->GetId(7), c1);
double center[3];
center[0] = (c1[0] + c2[0])/2.0;
center[1] = (c1[1] + c2[1])/2.0;
center[2] = (c1[2] + c2[2])/2.0;
VoxelCorners->InsertId(8,NodeList->InsertNextPoint(center));
}
//----------------------------------------------------------------------------
// Split A Cube into Tetrahedrons
// According to the DivisionType
// There had better be 0..8 voxel corners, though only 0..7 maybe needed.
// Why? This function may add id 8 to VoxelCorners.
// If a point needs to be inserted into the nodelist, itselt
// it at NextPointId. Assume there is space in the nodelist.
// Return the number of Tets Added.
int vtkRectilinearGridToTetrahedra::TetrahedralizeVoxel(vtkIdList *VoxelCorners,
const int &DivisionType,
vtkPoints *NodeList,
vtkCellArray *TetList)
{
// See vtkVoxel::Triangulate
/* Looking at the rect: Corner labeling
0 1
2 3
Directly behind them:
4 5
6 7
and 8 is in the middle of the cube if used
Want right handed Tetrahedra...
*/
// Split voxel in 2 along diagonal, 3 tets on either side
static int tet6[6][4] =
{
{1,6,2,3}, {1,6,7,5}, {1,6,3,7},
{1,6,0,2}, {1,6,5,4}, {1,6,4,0},
};
static int tet5[5][4] =
{ {0,1,4,2},{1,4,7,5},{1,4,2,7},{1,2,3,7},{2,7,4,6} };
static int tet5flip[5][4] =
{ {3,1,0,5}, {0,3,6,2}, {3,5,6,7}, {0,6,5,4}, {0,3,5,6}};
// 12 tet to confirm to tet5
static int tet12_conform[12][4] = {
/* Left side */
{8,2,4,0},
{8,4,2,6},
/* Back side */
{8,7,4,6},
{8,4,7,5},
/* Bottom side */
{8,7,2,3},
{8,2,7,6},
/* Right side */
{8,7,1,5},
{8,1,7,3},
/* Front side */
{8,1,2,0},
{8,2,1,3},
/* Top side */
{8,4,1,0},
{8,1,4,5}};
// 12 tet to confirm to tet5flip
static int tet12_conform_flip[12][4] = {
/* Left side */
{8,0,6,4},
{8,6,0,2},
/* Back side */
{8,5,6,7},
{8,6,5,4},
/* Bottom side */
{8,3,6,2},
{8,6,3,7},
/* Right side */
{8,3,5,7},
{8,5,3,1},
/* Front side */
{8,3,0,1},
{8,0,3,2},
/* Top side */
{8,5,0,4},
{8,0,5,1}};
// 12 tet chosen to have the least number of edges per node
static int tet12[12][4] = {
/* Left side */
{8,2,4,0},
{8,4,2,6},
/* Back side */
{8,7,4,6},
{8,4,7,5},
/* Right side */
{8,3,5,7},
{8,5,3,1},
/* Front side */
{8,3,0,1},
{8,0,3,2},
/* Top side */
{8,5,0,4},
{8,0,5,1},
/* Bottom side */
{8,7,2,3},
{8,2,7,6}};
int i, j;
// Get the point Ids
int numTet = 0; // =0 removes warning messages
vtkIdType TetPts[4];
switch (DivisionType)
{
case (VTK_TETRAHEDRALIZE_6) :
numTet = 6;
for(i=0;i<numTet;i++)
{
for(j=0;j<4;j++)
{
TetPts[j] = VoxelCorners->GetId(tet6[i][j]);
}
TetList->InsertNextCell((vtkIdType)4,TetPts);
}
break;
case (VTK_TETRAHEDRALIZE_5) :
numTet = 5;
for(i=0;i<numTet;i++)
{
for(j=0;j<4;j++)
{
TetPts[j] = VoxelCorners->GetId(tet5[i][j]);
}
TetList->InsertNextCell((vtkIdType)4,TetPts);
}
break;
case (VTK_TETRAHEDRALIZE_5_FLIP) :
numTet = 5;
for(i=0;i<numTet;i++)
{
for(j=0;j<4;j++)
{
TetPts[j] = VoxelCorners->GetId(tet5flip[i][j]);
}
TetList->InsertNextCell((vtkIdType)4,TetPts);
}
break;
case (VTK_TETRAHEDRALIZE_12) :
numTet = 12;
TetrahedralizeAddCenterPoint(VoxelCorners,NodeList);
for(i=0;i<numTet;i++)
{
for(j=0;j<4;j++)
{
TetPts[j] = VoxelCorners->GetId(tet12[i][j]);
}
TetList->InsertNextCell((vtkIdType)4,TetPts);
}
break;
case (VTK_TETRAHEDRALIZE_12_CONFORM) :
numTet = 12;
TetrahedralizeAddCenterPoint(VoxelCorners,NodeList);
for(i=0;i<numTet;i++)
{
for(j=0;j<4;j++)
{
TetPts[j] = VoxelCorners->GetId(tet12_conform[i][j]);
}
TetList->InsertNextCell((vtkIdType)4,TetPts);
}
break;
case (VTK_TETRAHEDRALIZE_12_CONFORM_FLIP) :
numTet = 12;
TetrahedralizeAddCenterPoint(VoxelCorners,NodeList);
for(i=0;i<numTet;i++)
{
for(j=0;j<4;j++)
{
TetPts[j] = VoxelCorners->GetId(tet12_conform_flip[i][j]);
}
TetList->InsertNextCell((vtkIdType)4,TetPts);
}
break;
}
return numTet;
}
//----------------------------------------------------------------------------
int vtkRectilinearGridToTetrahedra::RequestData(
vtkInformation *vtkNotUsed(request),
vtkInformationVector **inputVector,
vtkInformationVector *outputVector)
{
// get the info objects
vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
vtkInformation *outInfo = outputVector->GetInformationObject(0);
// get the input and ouptut
vtkRectilinearGrid *RectGrid = vtkRectilinearGrid::SafeDownCast(
inInfo->Get(vtkDataObject::DATA_OBJECT()));
vtkUnstructuredGrid *output = vtkUnstructuredGrid::SafeDownCast(
outInfo->Get(vtkDataObject::DATA_OBJECT()));
// Create internal version of VoxelSubdivisionType
// VoxelSubdivisionType indicates how to subdivide each cell
vtkSignedCharArray *VoxelSubdivisionType;
VoxelSubdivisionType = vtkSignedCharArray::New();
// If we have a mixture of 5 and 12 Tet, we need to get the information from
// the scalars of the Input. Note that we will modify the array internally
// so we need to copy it.
if (this->TetraPerCell == VTK_VOXEL_TO_5_AND_12_TET)
{
vtkDataArray *TempVoxelSubdivisionType = RectGrid->GetCellData()->GetScalars();
if((TempVoxelSubdivisionType == NULL))
{
vtkErrorMacro(<< "Scalars to input Should be set!");
return 1;
}
VoxelSubdivisionType->SetNumberOfValues(RectGrid->GetNumberOfCells());
VoxelSubdivisionType->vtkSignedCharArray::DeepCopy(TempVoxelSubdivisionType);
}
else
{ // Otherwise, just create the GridDivisionTypes
VoxelSubdivisionType->SetNumberOfValues(RectGrid->GetNumberOfCells());
}
vtkDebugMacro(<<"Number of points: "
<< RectGrid->GetNumberOfPoints());
vtkDebugMacro(<< "Number of voxels in input: "
<< RectGrid->GetNumberOfCells());
// Determine how each Cell should be subdivided
DetermineGridDivisionTypes(RectGrid,VoxelSubdivisionType,
this->TetraPerCell);
// Subdivide each cell to a tetrahedron, forming the TetMesh
GridToTetMesh(RectGrid,VoxelSubdivisionType,this->TetraPerCell,
this->RememberVoxelId,output);
vtkDebugMacro(<< "Number of output points: "
<< output->GetNumberOfPoints());
vtkDebugMacro(<< "Number of output tetrahedra: "
<< output->GetNumberOfCells());
// Clean Up
VoxelSubdivisionType->Delete();
return 1;
}
//----------------------------------------------------------------------------
int
vtkRectilinearGridToTetrahedra
::FillInputPortInformation(int port, vtkInformation* info)
{
if(!this->Superclass::FillInputPortInformation(port, info))
{
return 0;
}
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkRectilinearGrid");
return 1;
}
//----------------------------------------------------------------------------
void vtkRectilinearGridToTetrahedra::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "Mesh Type: " << this->TetraPerCell << "\n";
os << indent << "RememberVoxel Id: " << this->RememberVoxelId << "\n";
}