You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/3rd/VTK7.1/source/VTK-7.1.1/Rendering/Volume/vtkVolumePicker.cxx

411 lines
12 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkVolumePicker.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 "vtkVolumePicker.h"
#include "vtkObjectFactory.h"
#include "vtkBox.h"
#include "vtkImageData.h"
#include "vtkVolume.h"
#include "vtkVolumeMapper.h"
vtkStandardNewMacro(vtkVolumePicker);
//----------------------------------------------------------------------------
vtkVolumePicker::vtkVolumePicker()
{
this->PickCroppingPlanes = 0;
this->CroppingPlaneId = -1;
}
//----------------------------------------------------------------------------
vtkVolumePicker::~vtkVolumePicker()
{
}
//----------------------------------------------------------------------------
void vtkVolumePicker::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "PickCroppingPlanes: "
<< (this->PickCroppingPlanes ? "On" : "Off") << "\n";
os << indent << "CroppingPlaneId: " << this->CroppingPlaneId << "\n";
}
//----------------------------------------------------------------------------
void vtkVolumePicker::ResetPickInfo()
{
this->Superclass::ResetPickInfo();
this->CroppingPlaneId = -1;
}
//----------------------------------------------------------------------------
// Intersect a vtkVolume with a line by ray casting. Compared to the
// same method in the superclass, this method will look for cropping planes.
double vtkVolumePicker::IntersectVolumeWithLine(const double p1[3],
const double p2[3],
double t1, double t2,
vtkProp3D *prop,
vtkAbstractVolumeMapper *mapper)
{
double tMin = VTK_DOUBLE_MAX;
vtkImageData *data = vtkImageData::SafeDownCast(mapper->GetDataSetInput());
vtkVolumeMapper *vmapper = vtkVolumeMapper::SafeDownCast(mapper);
if (data == 0)
{
// This picker only works with image inputs
return VTK_DOUBLE_MAX;
}
// Convert ray to structured coordinates
double spacing[3], origin[3];
int extent[6];
data->GetSpacing(spacing);
data->GetOrigin(origin);
data->GetExtent(extent);
double x1[3], x2[3];
for (int i = 0; i < 3; i++)
{
x1[i] = (p1[i] - origin[i])/spacing[i];
x2[i] = (p2[i] - origin[i])/spacing[i];
}
// These are set to the plane that the ray enters through
int planeId = -1;
int extentPlaneId = -1;
// There might be multiple regions, depending on cropping flags
int numSegments = 1;
double t1List[16], t2List[16], s1List[16];
int planeIdList[16];
t1List[0] = t1;
t2List[0] = t2;
// s1 is the cropping plane intersection, initialize to large value
double s1 = s1List[0] = VTK_DOUBLE_MAX;
planeIdList[0] = -1;
// Find the cropping bounds in structured coordinates
double bounds[6];
for (int j = 0; j < 6; j++)
{
bounds[j] = extent[j];
}
if (vmapper && vmapper->GetCropping())
{
vmapper->GetCroppingRegionPlanes(bounds);
for (int j = 0; j < 3; j++)
{
double b1 = (bounds[2*j] - origin[j])/spacing[j];
double b2 = (bounds[2*j+1] - origin[j])/spacing[j];
bounds[2*j] = (b1 < b2 ? b1 : b2);
bounds[2*j+1] = (b1 < b2 ? b2 : b1);
if (bounds[2*j] < extent[2*j]) { bounds[2*j] = extent[2*j]; }
if (bounds[2*j+1] > extent[2*j+1]) { bounds[2*j+1] = extent[2*j+1]; }
if (bounds[2*j] > bounds[2*j+1])
{
return VTK_DOUBLE_MAX;
}
}
// Get all of the line segments that intersect the visible blocks
int flags = vmapper->GetCroppingRegionFlags();
if (!this->ClipLineWithCroppingRegion(bounds, extent, flags, x1, x2,
t1, t2, extentPlaneId, numSegments,
t1List, t2List, s1List, planeIdList))
{
return VTK_DOUBLE_MAX;
}
}
else
{
// If no cropping, then use volume bounds
double s2;
if (!this->ClipLineWithExtent(extent, x1, x2, s1, s2, extentPlaneId))
{
return VTK_DOUBLE_MAX;
}
s1List[0] = s1;
t1List[0] = ( (s1 > t1) ? s1 : t1 );
t2List[0] = ( (s2 < t2) ? s2 : t2 );
}
if (this->PickCroppingPlanes && vmapper && vmapper->GetCropping())
{
// Only require information about the first intersection
s1 = s1List[0];
if (s1 > t1)
{
planeId = planeIdList[0];
}
// Set data values at the intersected cropping or clipping plane
if ((tMin = t1List[0]) < this->GlobalTMin)
{
this->ResetPickInfo();
this->DataSet = data;
this->Mapper = vmapper;
double x[3];
for (int j = 0; j < 3; j++)
{
x[j] = x1[j]*(1.0 - tMin) + x2[j]*tMin;
if (planeId >= 0 && j == planeId/2)
{
x[j] = bounds[planeId];
}
else if (planeId < 0 && extentPlaneId >= 0 && j == extentPlaneId/2)
{
x[j] = extent[extentPlaneId];
}
this->MapperPosition[j] = x[j]*spacing[j] + origin[j];
}
this->SetImageDataPickInfo(x, extent);
}
}
else
{
// Go through the segments in order, until a hit occurs
for (int segment = 0; segment < numSegments; segment++)
{
if ((tMin = this->Superclass::IntersectVolumeWithLine(
p1, p2, t1List[segment], t2List[segment], prop, mapper))
< VTK_DOUBLE_MAX)
{
s1 = s1List[segment];
// Keep the first planeId that was set at the first intersection
// that occurred after t1
if (planeId < 0 && s1 > t1)
{
planeId = planeIdList[segment];
}
break;
}
}
}
if (tMin < this->GlobalTMin)
{
this->CroppingPlaneId = planeId;
// If t1 is at a cropping or extent plane, use the plane normal
if (planeId < 0)
{
planeId = extentPlaneId;
}
if (planeId >= 0 && tMin == s1)
{
this->MapperNormal[0] = 0.0;
this->MapperNormal[1] = 0.0;
this->MapperNormal[2] = 0.0;
this->MapperNormal[planeId/2] = 2.0*(planeId%2) - 1.0;
if (spacing[planeId/2] < 0)
{
this->MapperNormal[planeId/2] = - this->MapperNormal[planeId/2];
}
}
}
return tMin;
}
//----------------------------------------------------------------------------
// This method does several things. Given the volume CroppingRegionPlanes
// stored in bounds (in structured coords), and the volume extent, it
// casts a ray through the 27 "blocks" that the volume has been divided into.
// Each "block" is turned on or off by a bit in "flags". The result
// of the ray cast is a collection of line segments: the parametric
// start and end of each segment is stored in t1List and t2List respectively.
// If the segment starts at a cropping plane, the planeIdList will store
// the Id of that plane, otherwise planeIdList will store -1 for that segment.
int vtkVolumePicker::ClipLineWithCroppingRegion(
const double bounds[6], const int extent[6], int flags,
const double x1[3], const double x2[3], double t1, double t2,
int &extentPlaneId, int &numSegments,
double *t1List, double *t2List, double *s1List, int *planeIdList)
{
extentPlaneId = -1;
numSegments = 0;
double s1, s2;
// Start by clipping the line with the volume extent
if (!vtkVolumePicker::ClipLineWithExtent(extent, x1, x2, s1, s2,
extentPlaneId))
{
return 0;
}
if (s1 >= t1) { t1 = s1; }
if (s2 <= t2) { t2 = s2; }
if (t2 < t1)
{
return 0;
}
// Compute the coordinates that correspond to t1
double x[3];
for (int i = 0; i < 3; i++)
{
x[i] = x1[i]*(1.0 - t1) + x2[i]*t1;
// Watch for out-of-bounds due to numerical roundoff
if (x[i] < extent[2*i]) { x[i] = extent[2*i]; }
if (x[i] > extent[2*i+1]) { x[i] = extent[2*i+1]; }
}
if (t1 == s1 && extentPlaneId >= 0)
{
// If right on the boundary, set position exactly
x[extentPlaneId/2] = extent[extentPlaneId];
}
// Find out which block is hit first, store indices and bounds
int xi[3];
double blockBounds[6];
for (int j = 0; j < 3; j++)
{
xi[j] = 0;
blockBounds[2*j] = extent[2*j];
blockBounds[2*j+1] = bounds[2*j];
// Be particular about the ray direction
if (x[j] > bounds[2*j] || (x[j] == bounds[2*j] && x1[j] < x2[j]))
{
xi[j] = 1;
blockBounds[2*j] = bounds[2*j];
blockBounds[2*j+1] = bounds[2*j+1];
}
if (x[j] > bounds[2*j+1] || (x[j] == bounds[2*j+1] && x1[j] < x2[j]))
{
xi[j] = 2;
blockBounds[2*j] = bounds[2*j+1];
blockBounds[2*j+1] = extent[2*j+1];
}
}
// Loop through the blocks along the ray path
int plane1 = -1;
int plane2 = -1;
for (;;)
{
if (!vtkBox::IntersectWithLine(blockBounds, x1, x2,
s1, s2, 0, 0, plane1, plane2))
{
// This should never happen, but if it does, stop here
break;
}
int blockId = xi[0] + xi[1]*3 + xi[2]*9;
if ((flags >> blockId) & 1)
{
t1List[numSegments] = (t1 > s1 ? t1 : s1);
t2List[numSegments] = (t2 < s2 ? t2 : s2);
s1List[numSegments] = s1;
planeIdList[numSegments] = -1;
if (plane1 >= 0)
{
// Compute plane1/2 and plane1%2
int k = (plane1 >> 1);
int l = (plane1 & 1);
// Need to know if the ray is entering the volume, i.e. whether
// the adjacent block that the ray is coming from is "off", because
// we can't define a clip plane unless it is off.
static int blockInc[3] = {1, 3, 9};
int noPlane = 1;
if (xi[k] == 1)
{
noPlane = (flags >> (blockId + blockInc[k]*(2*l - 1)) & 1);
if (!noPlane)
{
planeIdList[numSegments] = plane1;
}
}
else if (xi[k] == 0)
{
noPlane = (flags >> (blockId + blockInc[k]) & 1);
if (!noPlane && l == 1)
{
planeIdList[numSegments] = 2*k;
}
}
else if (xi[k] == 2)
{
noPlane = (flags >> (blockId - blockInc[k]) & 1);
if (!noPlane && l == 0)
{
planeIdList[numSegments] = 2*k + 1;
}
}
}
// Sanity check: allow no segments with negative length
if (t1List[numSegments] <= t2List[numSegments])
{
if (numSegments > 0 && t1List[numSegments] == t2List[numSegments-1])
{
// Concatenate this segment with the previous one
t2List[numSegments-1] = t2List[numSegments];
}
else
{
// Add this segment as a new segment
numSegments++;
}
}
}
// If there is no exit plane, the ray terminated and the search is over
if (plane2 < 0)
{
break;
}
// Use the exit plane to choose the next block
int k = plane2 / 2;
xi[k] += 2*(plane2 - 2*k) - 1;
if (xi[k] == 0)
{
blockBounds[2*k] = extent[2*k];
blockBounds[2*k+1] = bounds[2*k];
}
else if (xi[k] == 1)
{
blockBounds[2*k] = bounds[2*k];
blockBounds[2*k+1] = bounds[2*k+1];
}
else if (xi[k] == 2)
{
blockBounds[2*k] = bounds[2*k+1];
blockBounds[2*k+1] = extent[2*k+1];
}
else
{
// Exit, stage right
break;
}
}
return numSegments;
}