mirror of https://gitee.com/openkylin/vtk9.git
771 lines
23 KiB
C++
771 lines
23 KiB
C++
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: vtkVolumeOutlineSource.cxx
|
|
|
|
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 "vtkVolumeOutlineSource.h"
|
|
|
|
#include "vtkCellArray.h"
|
|
#include "vtkCellArrayIterator.h"
|
|
#include "vtkCellData.h"
|
|
#include "vtkDataSet.h"
|
|
#include "vtkDemandDrivenPipeline.h"
|
|
#include "vtkIdList.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkInformation.h"
|
|
#include "vtkInformationVector.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkNew.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPoints.h"
|
|
#include "vtkPolyData.h"
|
|
#include "vtkStreamingDemandDrivenPipeline.h"
|
|
#include "vtkUnsignedCharArray.h"
|
|
#include "vtkVolumeMapper.h"
|
|
|
|
vtkStandardNewMacro(vtkVolumeOutlineSource);
|
|
|
|
vtkCxxSetObjectMacro(vtkVolumeOutlineSource, VolumeMapper, vtkVolumeMapper);
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkVolumeOutlineSource::vtkVolumeOutlineSource()
|
|
{
|
|
this->VolumeMapper = nullptr;
|
|
this->GenerateScalars = 0;
|
|
this->GenerateOutline = 1;
|
|
this->GenerateFaces = 0;
|
|
this->ActivePlaneId = -1;
|
|
|
|
this->Color[0] = 1.0;
|
|
this->Color[1] = 0.0;
|
|
this->Color[2] = 0.0;
|
|
|
|
this->ActivePlaneColor[0] = 1.0;
|
|
this->ActivePlaneColor[1] = 1.0;
|
|
this->ActivePlaneColor[2] = 0.0;
|
|
|
|
this->SetNumberOfInputPorts(0);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
vtkVolumeOutlineSource::~vtkVolumeOutlineSource()
|
|
{
|
|
if (this->VolumeMapper)
|
|
{
|
|
this->VolumeMapper->Delete();
|
|
this->VolumeMapper = nullptr;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeOutlineSource::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
|
|
os << indent << "VolumeMapper: ";
|
|
if (this->VolumeMapper)
|
|
{
|
|
os << this->VolumeMapper << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << "(none)\n";
|
|
}
|
|
|
|
os << indent << "GenerateFaces: " << (this->GenerateFaces ? "On\n" : "Off\n");
|
|
|
|
os << indent << "GenerateOutline: " << (this->GenerateOutline ? "On\n" : "Off\n");
|
|
|
|
os << indent << "GenerateScalars: " << (this->GenerateScalars ? "On\n" : "Off\n");
|
|
|
|
os << indent << "Color: " << this->Color[0] << ", " << this->Color[1] << ", " << this->Color[2]
|
|
<< "\n";
|
|
|
|
os << indent << "ActivePlaneId: " << this->ActivePlaneId << "\n";
|
|
|
|
os << indent << "ActivePlaneColor: " << this->ActivePlaneColor[0] << ", "
|
|
<< this->ActivePlaneColor[1] << ", " << this->ActivePlaneColor[2] << "\n";
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeOutlineSource::ComputeCubePlanes(
|
|
double planes[3][4], double croppingPlanes[6], double bounds[6])
|
|
{
|
|
// Combine the CroppingRegionPlanes and the Bounds to create
|
|
// a single array. For each dimension, store the planes in
|
|
// the following order: lo_bound, lo_crop_plane, hi_crop_plane, hi_bound.
|
|
// Also do range checking to ensure that the cropping planes
|
|
// are clamped to the bound limits.
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
int j0 = 2 * i;
|
|
int j1 = 2 * i + 1;
|
|
|
|
double a = bounds[j0];
|
|
double b = croppingPlanes[j0];
|
|
double c = croppingPlanes[j1];
|
|
double d = bounds[j1];
|
|
|
|
// Sanity check
|
|
if (a > d || b > c)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// Clamp cropping planes to bounds
|
|
if (b < a)
|
|
{
|
|
b = a;
|
|
}
|
|
if (b > d)
|
|
{
|
|
b = d;
|
|
}
|
|
if (c < a)
|
|
{
|
|
c = a;
|
|
}
|
|
if (c > d)
|
|
{
|
|
c = d;
|
|
}
|
|
|
|
planes[i][0] = a;
|
|
planes[i][1] = b;
|
|
planes[i][2] = c;
|
|
planes[i][3] = d;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeOutlineSource::ComputePipelineMTime(vtkInformation* vtkNotUsed(request),
|
|
vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector),
|
|
int vtkNotUsed(requestFromOutputPort), vtkMTimeType* mtime)
|
|
{
|
|
vtkMTimeType mTime = this->GetMTime();
|
|
if (this->VolumeMapper)
|
|
{
|
|
vtkMTimeType mapperMTime = this->VolumeMapper->GetMTime();
|
|
if (mapperMTime > mTime)
|
|
{
|
|
mTime = mapperMTime;
|
|
}
|
|
vtkDemandDrivenPipeline* input =
|
|
vtkDemandDrivenPipeline::SafeDownCast(this->VolumeMapper->GetInputExecutive());
|
|
if (input)
|
|
{
|
|
// Need to do this because we are not formally connected
|
|
// to the Mapper's pipeline
|
|
input->UpdateInformation();
|
|
vtkMTimeType pipelineMTime = input->GetPipelineMTime();
|
|
if (pipelineMTime > mTime)
|
|
{
|
|
mTime = pipelineMTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
*mtime = mTime;
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeOutlineSource::RequestInformation(vtkInformation* vtkNotUsed(request),
|
|
vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* vtkNotUsed(outputVector))
|
|
{
|
|
// Get the mapper's input, since this is the most convenient
|
|
// place to do so.
|
|
|
|
if (!this->VolumeMapper)
|
|
{
|
|
vtkWarningMacro("No VolumeMapper has been set.");
|
|
return 1;
|
|
}
|
|
|
|
vtkInformation* mapInfo = this->VolumeMapper->GetInputInformation();
|
|
|
|
if (!mapInfo)
|
|
{
|
|
vtkWarningMacro("The VolumeMapper does not have an input set.");
|
|
return 1;
|
|
}
|
|
|
|
// Don't have to update mapper's input, since it was done in
|
|
// ComputePipelineMTime.
|
|
// data->UpdateInformation();
|
|
|
|
// Don't call GetBounds because we need WholeExtent, while
|
|
// GetBounds only returns the bounds for Extent.
|
|
|
|
double spacing[3];
|
|
double origin[3];
|
|
int extent[6];
|
|
|
|
mapInfo->Get(vtkDataObject::SPACING(), spacing);
|
|
mapInfo->Get(vtkDataObject::ORIGIN(), origin);
|
|
mapInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), extent);
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
int j0 = 2 * i;
|
|
int j1 = j0 + 1;
|
|
|
|
if (extent[j0] > extent[j1])
|
|
{
|
|
vtkMath::UninitializeBounds(this->Bounds);
|
|
break;
|
|
}
|
|
|
|
if (spacing[i] > 0)
|
|
{
|
|
this->Bounds[j0] = origin[i] + spacing[i] * extent[j0];
|
|
this->Bounds[j1] = origin[i] + spacing[i] * extent[j1];
|
|
}
|
|
else
|
|
{
|
|
this->Bounds[j0] = origin[i] + spacing[i] * extent[j1];
|
|
this->Bounds[j1] = origin[i] + spacing[i] * extent[j0];
|
|
}
|
|
|
|
this->CroppingRegionPlanes[j0] = this->Bounds[j0];
|
|
this->CroppingRegionPlanes[j1] = this->Bounds[j1];
|
|
}
|
|
|
|
this->CroppingRegionFlags = 0x0002000;
|
|
|
|
this->Cropping = this->VolumeMapper->GetCropping();
|
|
if (this->Cropping)
|
|
{
|
|
this->CroppingRegionFlags = this->VolumeMapper->GetCroppingRegionFlags();
|
|
this->VolumeMapper->GetCroppingRegionPlanes(this->CroppingRegionPlanes);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
int vtkVolumeOutlineSource::RequestData(vtkInformation* vtkNotUsed(request),
|
|
vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
|
|
{
|
|
// get the info object
|
|
vtkInformation* outInfo = outputVector->GetInformationObject(0);
|
|
|
|
// get the output
|
|
vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
|
|
|
|
vtkDebugMacro(<< "Creating cropping region outline");
|
|
|
|
// For each of the 3 dimensions, there are 4 planes: two bounding planes
|
|
// on the outside, and two cropping region planes inside.
|
|
double planes[3][4];
|
|
|
|
if (!this->VolumeMapper || !this->VolumeMapper->GetInput() ||
|
|
!this->ComputeCubePlanes(planes, this->CroppingRegionPlanes, this->Bounds))
|
|
{
|
|
// If the bounds or the cropping planes are invalid, clear the data
|
|
output->SetPoints(nullptr);
|
|
output->SetLines(nullptr);
|
|
output->GetCellData()->SetScalars(nullptr);
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Compute the tolerance for considering points or planes to be coincident
|
|
double tol = 0;
|
|
for (int planeDim = 0; planeDim < 3; planeDim++)
|
|
{
|
|
double d = planes[planeDim][3] - planes[planeDim][0];
|
|
tol += d * d;
|
|
}
|
|
tol = sqrt(tol) * 1e-5;
|
|
|
|
// Create an array to nudge crop planes over to the bounds if they are
|
|
// within tolerance of the bounds
|
|
int tolPtId[3][4];
|
|
vtkVolumeOutlineSource::NudgeCropPlanesToBounds(tolPtId, planes, tol);
|
|
|
|
// The all-important cropping flags
|
|
int flags = this->CroppingRegionFlags;
|
|
|
|
// The active plane, which gets a special color for its scalars
|
|
int activePlane = this->ActivePlaneId;
|
|
if (activePlane > 5)
|
|
{
|
|
activePlane = -1;
|
|
}
|
|
|
|
// Convert the colors to unsigned char for scalars
|
|
unsigned char colors[2][3];
|
|
vtkVolumeOutlineSource::CreateColorValues(colors, this->Color, this->ActivePlaneColor);
|
|
|
|
// Create the scalars used to color the lines
|
|
vtkUnsignedCharArray* scalars = nullptr;
|
|
|
|
if (this->GenerateScalars)
|
|
{
|
|
scalars = vtkUnsignedCharArray::New();
|
|
scalars->SetNumberOfComponents(3);
|
|
}
|
|
|
|
// Generate all the lines for the outline.
|
|
vtkCellArray* lines = nullptr;
|
|
|
|
if (this->GenerateOutline)
|
|
{
|
|
lines = vtkCellArray::New();
|
|
vtkVolumeOutlineSource::GenerateLines(lines, scalars, colors, activePlane, flags, tolPtId);
|
|
}
|
|
|
|
// Generate the polys for the outline
|
|
vtkCellArray* polys = nullptr;
|
|
|
|
if (this->GenerateFaces)
|
|
{
|
|
polys = vtkCellArray::New();
|
|
vtkVolumeOutlineSource::GeneratePolys(polys, scalars, colors, activePlane, flags, tolPtId);
|
|
}
|
|
|
|
// Generate the points that are used by the lines.
|
|
vtkPoints* points = vtkPoints::New();
|
|
vtkVolumeOutlineSource::GeneratePoints(points, lines, polys, planes, tol);
|
|
|
|
output->SetPoints(points);
|
|
points->Delete();
|
|
|
|
output->SetPolys(polys);
|
|
if (polys)
|
|
{
|
|
polys->Delete();
|
|
}
|
|
|
|
output->SetLines(lines);
|
|
if (lines)
|
|
{
|
|
lines->Delete();
|
|
}
|
|
|
|
output->GetCellData()->SetScalars(scalars);
|
|
if (scalars)
|
|
{
|
|
scalars->Delete();
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeOutlineSource::GeneratePolys(vtkCellArray* polys, vtkUnsignedCharArray* scalars,
|
|
unsigned char colors[2][3], int activePlane, int flags, int tolPtId[3][4])
|
|
{
|
|
// Loop over the three dimensions and create the face rectangles
|
|
for (int dim0 = 0; dim0 < 3; dim0++)
|
|
{
|
|
// Compute the other two dimension indices
|
|
int dim1 = (dim0 + 1) % 3;
|
|
int dim2 = (dim0 + 2) % 3;
|
|
|
|
// Indices into the cubes
|
|
int idx[3];
|
|
|
|
// Loop over the "dim+2" dimension
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
idx[dim2] = i;
|
|
|
|
// Loop over the "dim+1" dimension
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
idx[dim1] = j;
|
|
|
|
// Make sure that the rect dim is not less than tolerance
|
|
if ((j == 0 && tolPtId[dim1][1] == 0) || (j == 2 && tolPtId[dim1][2] == 3))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Loop over rectangle along the "dim" dimension
|
|
for (int k = 0; k < 3; k++)
|
|
{
|
|
idx[dim0] = k;
|
|
|
|
// Make sure that the rect dim is not less than tolerance
|
|
if ((k == 0 && tolPtId[dim0][1] == 0) || (k == 2 && tolPtId[dim0][2] == 3))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// The points in the rectangle, which are nudged over to the
|
|
// volume bounds if the cropping planes are within tolerance
|
|
// of the volume bounds.
|
|
int pointId[4];
|
|
pointId[0] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
|
|
idx[dim0] = k + 1;
|
|
pointId[1] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
|
|
idx[dim1] = j + 1;
|
|
pointId[2] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
|
|
idx[dim0] = k;
|
|
pointId[3] = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
|
|
idx[dim1] = j;
|
|
|
|
// Loop through the two cubes adjacent to the rectangle,
|
|
// in order to determine whether the rectangle is internal:
|
|
// only external faces will be drawn. The "bitCheck"
|
|
// holds a bit for each of these two cubes.
|
|
int bitCheck = 0;
|
|
int cidx[3];
|
|
cidx[dim0] = idx[dim0];
|
|
cidx[dim1] = idx[dim1];
|
|
for (int ii = 0; ii < 2; ii++)
|
|
{
|
|
// First get idx[dim2]-1, then idx[dim2]
|
|
cidx[dim2] = idx[dim2] + ii - 1;
|
|
int flagval = 0;
|
|
if (cidx[dim2] >= 0 && cidx[dim2] < 3)
|
|
{
|
|
int flagbit = cidx[2] * 9 + cidx[1] * 3 + cidx[0];
|
|
flagval = ((flags >> flagbit) & 1);
|
|
}
|
|
bitCheck <<= 1;
|
|
bitCheck |= flagval;
|
|
}
|
|
|
|
// Whether we need to create a face depends on bitCheck.
|
|
// Values 00, 11 don't need lines, while 01 and 10 do.
|
|
|
|
// If our rect isn't an internal rect
|
|
if (bitCheck != 0x0 && bitCheck != 0x3)
|
|
{
|
|
// Check if the rect is on our active plane
|
|
int active = 0;
|
|
if (activePlane >= 0)
|
|
{
|
|
int planeDim = (activePlane >> 1); // same as "/ 2"
|
|
int planeIdx = 1 + (activePlane & 1); // same as "% 2"
|
|
if (planeDim == dim2 && i == planeIdx)
|
|
{
|
|
active = 1;
|
|
}
|
|
}
|
|
|
|
// Insert the rectangle with the correct sense
|
|
polys->InsertNextCell(4);
|
|
if (bitCheck == 0x2)
|
|
{
|
|
polys->InsertCellPoint(pointId[0]);
|
|
polys->InsertCellPoint(pointId[1]);
|
|
polys->InsertCellPoint(pointId[2]);
|
|
polys->InsertCellPoint(pointId[3]);
|
|
}
|
|
else // (bitCheck == 0x1)
|
|
{
|
|
polys->InsertCellPoint(pointId[3]);
|
|
polys->InsertCellPoint(pointId[2]);
|
|
polys->InsertCellPoint(pointId[1]);
|
|
polys->InsertCellPoint(pointId[0]);
|
|
}
|
|
|
|
// Color the face
|
|
if (scalars)
|
|
{
|
|
scalars->InsertNextTypedTuple(colors[active]);
|
|
}
|
|
}
|
|
|
|
} // loop over k
|
|
} // loop over j
|
|
} // loop over i
|
|
} // loop over dim0
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeOutlineSource::GenerateLines(vtkCellArray* lines, vtkUnsignedCharArray* scalars,
|
|
unsigned char colors[2][3], int activePlane, int flags, int tolPtId[3][4])
|
|
{
|
|
// Loop over the three dimensions and create the lines
|
|
for (int dim0 = 0; dim0 < 3; dim0++)
|
|
{
|
|
// Compute the other two dimension indices
|
|
int dim1 = (dim0 + 1) % 3;
|
|
int dim2 = (dim0 + 2) % 3;
|
|
|
|
// Indices into the cubes
|
|
int idx[3];
|
|
|
|
// Loop over the "dim+2" dimension
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
idx[dim2] = i;
|
|
|
|
// Loop over the "dim+1" dimension
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
idx[dim1] = j;
|
|
|
|
// Loop over line segments along the "dim" dimension
|
|
for (int k = 0; k < 3; k++)
|
|
{
|
|
idx[dim0] = k;
|
|
|
|
// Make sure that the segment length is not less than tolerance
|
|
if ((k == 0 && tolPtId[dim0][1] == 0) || (k == 2 && tolPtId[dim0][2] == 3))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// The endpoints of the segment, which are nudged over to the
|
|
// volume bounds if the cropping planes are within tolerance
|
|
// of the volume bounds.
|
|
int pointId0 = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
|
|
idx[dim0] = k + 1;
|
|
int pointId1 = (tolPtId[2][idx[2]] * 16 + tolPtId[1][idx[1]] * 4 + tolPtId[0][idx[0]]);
|
|
idx[dim0] = k;
|
|
|
|
// Loop through the four cubes adjacent to the line segment,
|
|
// in order to determine whether the line segment is on an
|
|
// edge: only the edge lines will be drawn. The "bitCheck"
|
|
// holds a bit for each of these four cubes.
|
|
int bitCheck = 0;
|
|
int cidx[3];
|
|
cidx[dim0] = idx[dim0];
|
|
for (int ii = 0; ii < 2; ii++)
|
|
{
|
|
// First get idx[dim1]-1, then idx[dim1]
|
|
cidx[dim1] = idx[dim1] + ii - 1;
|
|
for (int jj = 0; jj < 2; jj++)
|
|
{
|
|
// First get idx[dim2]-1, then idx[dim2], but reverse
|
|
// the order when ii loop is on its second iteration
|
|
cidx[dim2] = idx[dim2] + (ii ^ jj) - 1;
|
|
int flagval = 0;
|
|
if (cidx[dim1] >= 0 && cidx[dim1] < 3 && cidx[dim2] >= 0 && cidx[dim2] < 3)
|
|
{
|
|
int flagbit = cidx[2] * 9 + cidx[1] * 3 + cidx[0];
|
|
flagval = ((flags >> flagbit) & 1);
|
|
}
|
|
bitCheck <<= 1;
|
|
bitCheck |= flagval;
|
|
}
|
|
}
|
|
|
|
// Whether we need a line depends on the value of bitCheck.
|
|
// Values 0000, 0011, 0110, 1100, 1001, 1111 don't need lines.
|
|
// Build a bitfield to check our bitfield values against, each
|
|
// set bit in this new bitfield corresponds to a non-edge case.
|
|
const int noLineValues =
|
|
((1 << 0x0) | (1 << 0x3) | (1 << 0x6) | (1 << 0x9) | (1 << 0xc) | (1 << 0xf));
|
|
|
|
// If our line segment is an edge, there is lots of work to do.
|
|
if (((noLineValues >> bitCheck) & 1) == 0)
|
|
{
|
|
// Check if the line segment is on our active plane
|
|
int active = 0;
|
|
if (activePlane >= 0)
|
|
{
|
|
int planeDim = (activePlane >> 1); // same as "/ 2"
|
|
int planeIdx = 1 + (activePlane & 1); // same as "% 2"
|
|
if ((planeDim == dim2 && i == planeIdx) || (planeDim == dim1 && j == planeIdx))
|
|
{
|
|
active = 1;
|
|
}
|
|
}
|
|
|
|
// Check to make sure line segment isn't already there
|
|
int foundDuplicate = 0;
|
|
lines->InitTraversal();
|
|
vtkIdType npts;
|
|
const vtkIdType* pts;
|
|
for (int cellId = 0; lines->GetNextCell(npts, pts); cellId++)
|
|
{
|
|
if (pts[0] == pointId0 && pts[1] == pointId1)
|
|
{
|
|
// Change color if current segment is on active plane
|
|
if (scalars && active)
|
|
{
|
|
scalars->SetTypedTuple(cellId, colors[active]);
|
|
}
|
|
foundDuplicate = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundDuplicate)
|
|
{
|
|
// Insert the line segment
|
|
lines->InsertNextCell(2);
|
|
lines->InsertCellPoint(pointId0);
|
|
lines->InsertCellPoint(pointId1);
|
|
|
|
// Color the line segment
|
|
if (scalars)
|
|
{
|
|
scalars->InsertNextTypedTuple(colors[active]);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // loop over k
|
|
} // loop over j
|
|
} // loop over i
|
|
} // loop over dim0
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeOutlineSource::GeneratePoints(
|
|
vtkPoints* points, vtkCellArray* lines, vtkCellArray* polys, double planes[3][4], double tol)
|
|
{
|
|
// Use a bitfield to store which of the 64 points we need.
|
|
// Two 32-bit ints are a convenient, portable way to do this.
|
|
unsigned int pointBits1 = 0;
|
|
unsigned int pointBits2 = 0;
|
|
|
|
vtkIdType npts;
|
|
const vtkIdType* pts;
|
|
vtkCellArray* cellArrays[2];
|
|
cellArrays[0] = lines;
|
|
cellArrays[1] = polys;
|
|
|
|
for (int arrayId = 0; arrayId < 2; arrayId++)
|
|
{
|
|
if (cellArrays[arrayId])
|
|
{
|
|
cellArrays[arrayId]->InitTraversal();
|
|
while (cellArrays[arrayId]->GetNextCell(npts, pts))
|
|
{
|
|
for (int ii = 0; ii < npts; ii++)
|
|
{
|
|
int pointId = pts[ii];
|
|
if (pointId < 32)
|
|
{
|
|
pointBits1 |= (1 << pointId);
|
|
}
|
|
else
|
|
{
|
|
pointBits2 |= (1 << (pointId - 32));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create the array of up to 64 points, and use the pointBits bitfield
|
|
// to find out which points were used. It is also necessary to go through
|
|
// and update the cells with the modified point ids.
|
|
unsigned int pointBits = pointBits1;
|
|
int ptId = 0;
|
|
int newPtId = 0;
|
|
|
|
vtkNew<vtkIdList> repCell;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
// If we're halfway done, switch over to the next 32 bits
|
|
if (i == 2)
|
|
{
|
|
pointBits = pointBits2;
|
|
}
|
|
|
|
for (int j = 0; j < 4; j++)
|
|
{
|
|
for (int k = 0; k < 4; k++)
|
|
{
|
|
// Check to see if this point was actually used
|
|
if ((pointBits & 1))
|
|
{
|
|
// Add or subtract tolerance as an offset to help depth check
|
|
double x = planes[0][k] + tol * (1 - 2 * (k < 2));
|
|
double y = planes[1][j] + tol * (1 - 2 * (j < 2));
|
|
double z = planes[2][i] + tol * (1 - 2 * (i < 2));
|
|
|
|
points->InsertNextPoint(x, y, z);
|
|
|
|
for (int arrayId = 0; arrayId < 2; arrayId++)
|
|
{
|
|
// Go through the cells, substitute old Id for new Id
|
|
vtkCellArray* cells = cellArrays[arrayId];
|
|
if (cells)
|
|
{
|
|
auto cellIter = vtk::TakeSmartPointer(cells->NewIterator());
|
|
for (cellIter->GoToFirstCell(); !cellIter->IsDoneWithTraversal();
|
|
cellIter->GoToNextCell())
|
|
{
|
|
cellIter->GetCurrentCell(repCell);
|
|
for (int ii = 0; ii < repCell->GetNumberOfIds(); ii++)
|
|
{
|
|
if (repCell->GetId(ii) == ptId)
|
|
{
|
|
repCell->SetId(ii, newPtId);
|
|
}
|
|
}
|
|
cellIter->ReplaceCurrentCell(repCell);
|
|
}
|
|
}
|
|
}
|
|
newPtId++;
|
|
}
|
|
pointBits >>= 1;
|
|
ptId++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeOutlineSource::NudgeCropPlanesToBounds(
|
|
int tolPtId[3][4], double planes[3][4], double tol)
|
|
{
|
|
for (int dim = 0; dim < 3; dim++)
|
|
{
|
|
tolPtId[dim][0] = 0;
|
|
tolPtId[dim][1] = 1;
|
|
tolPtId[dim][2] = 2;
|
|
tolPtId[dim][3] = 3;
|
|
if (planes[dim][1] - planes[dim][0] < tol)
|
|
{
|
|
tolPtId[dim][1] = 0;
|
|
}
|
|
if (planes[dim][3] - planes[dim][2] < tol)
|
|
{
|
|
tolPtId[dim][2] = 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
void vtkVolumeOutlineSource::CreateColorValues(
|
|
unsigned char colors[2][3], double color1[3], double color2[3])
|
|
{
|
|
// Convert the two colors to unsigned char
|
|
double* dcolors[2];
|
|
dcolors[0] = color1;
|
|
dcolors[1] = color2;
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
for (int j = 0; j < 3; j++)
|
|
{
|
|
double val = dcolors[i][j];
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
if (val > 1)
|
|
{
|
|
val = 1;
|
|
}
|
|
colors[i][j] = static_cast<unsigned char>(val * 255);
|
|
}
|
|
}
|
|
}
|