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.
542 lines
16 KiB
542 lines
16 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkPicker.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 "vtkPicker.h"
|
|
|
|
#include "vtkActor.h"
|
|
#include "vtkAssemblyNode.h"
|
|
#include "vtkAssemblyPath.h"
|
|
#include "vtkCamera.h"
|
|
#include "vtkCommand.h"
|
|
#include "vtkImageData.h"
|
|
#include "vtkLODProp3D.h"
|
|
#include "vtkMapper.h"
|
|
#include "vtkMath.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkPoints.h"
|
|
#include "vtkProp3DCollection.h"
|
|
#include "vtkProperty.h"
|
|
#include "vtkRenderWindow.h"
|
|
#include "vtkRenderer.h"
|
|
#include "vtkTransform.h"
|
|
#include "vtkVertex.h"
|
|
#include "vtkVolume.h"
|
|
#include "vtkAbstractVolumeMapper.h"
|
|
#include "vtkBox.h"
|
|
#include "vtkImageActor.h"
|
|
|
|
vtkCxxRevisionMacro(vtkPicker, "$Revision: 1.93 $");
|
|
vtkStandardNewMacro(vtkPicker);
|
|
|
|
// Construct object with initial tolerance of 1/40th of window. There are no
|
|
// pick methods and picking is performed from the renderer's actors.
|
|
vtkPicker::vtkPicker()
|
|
{
|
|
this->Tolerance = 0.025; // 1/40th of the renderer window
|
|
|
|
this->MapperPosition[0] = 0.0;
|
|
this->MapperPosition[1] = 0.0;
|
|
this->MapperPosition[2] = 0.0;
|
|
|
|
this->Mapper = NULL;
|
|
this->DataSet = NULL;
|
|
this->GlobalTMin = VTK_DOUBLE_MAX;
|
|
this->Actors = vtkActorCollection::New();
|
|
this->Prop3Ds = vtkProp3DCollection::New();
|
|
this->PickedPositions = vtkPoints::New();
|
|
this->Transform = vtkTransform::New();
|
|
}
|
|
|
|
vtkPicker::~vtkPicker()
|
|
{
|
|
this->Actors->Delete();
|
|
this->Prop3Ds->Delete();
|
|
this->PickedPositions->Delete();
|
|
this->Transform->Delete();
|
|
}
|
|
|
|
// Update state when prop3D is picked.
|
|
void vtkPicker::MarkPicked(vtkAssemblyPath *path, vtkProp3D *prop3D,
|
|
vtkAbstractMapper3D *m,
|
|
double tMin, double mapperPos[3])
|
|
{
|
|
int i;
|
|
vtkMapper *mapper;
|
|
vtkAbstractVolumeMapper *volumeMapper;
|
|
|
|
this->SetPath(path);
|
|
this->GlobalTMin = tMin;
|
|
|
|
for (i=0; i < 3; i++)
|
|
{
|
|
this->MapperPosition[i] = mapperPos[i];
|
|
}
|
|
if ( (mapper=vtkMapper::SafeDownCast(m)) != NULL )
|
|
{
|
|
this->DataSet = mapper->GetInput();
|
|
this->Mapper = mapper;
|
|
}
|
|
else if ( (volumeMapper=vtkAbstractVolumeMapper::SafeDownCast(m)) != NULL )
|
|
{
|
|
this->DataSet = volumeMapper->GetDataSetInput();
|
|
this->Mapper = volumeMapper; }
|
|
else
|
|
{
|
|
this->DataSet = NULL;
|
|
}
|
|
|
|
// The point has to be transformed back into world coordinates.
|
|
// Note: it is assumed that the transform is in the correct state.
|
|
this->Transform->TransformPoint(mapperPos,this->PickPosition);
|
|
|
|
// Invoke pick method if one defined - actor goes first
|
|
prop3D->Pick();
|
|
this->InvokeEvent(vtkCommand::PickEvent,NULL);
|
|
|
|
}
|
|
|
|
// Perform pick operation with selection point provided. Normally the
|
|
// first two values for the selection point are x-y pixel coordinate, and
|
|
// the third value is =0. Return non-zero if something was successfully picked.
|
|
int vtkPicker::Pick(double selectionX, double selectionY, double selectionZ,
|
|
vtkRenderer *renderer)
|
|
{
|
|
int i;
|
|
vtkProp *prop;
|
|
vtkCamera *camera;
|
|
vtkAbstractMapper3D *mapper = NULL;
|
|
double p1World[4], p2World[4], p1Mapper[4], p2Mapper[4];
|
|
int picked=0;
|
|
int *winSize;
|
|
double x, y, t;
|
|
double *viewport;
|
|
double cameraPos[4], cameraFP[4];
|
|
double *displayCoords, *worldCoords;
|
|
double *clipRange;
|
|
double ray[3], rayLength;
|
|
int pickable;
|
|
int LODId;
|
|
double windowLowerLeft[4], windowUpperRight[4];
|
|
double bounds[6], tol;
|
|
double tF, tB;
|
|
double hitPosition[3];
|
|
double cameraDOP[3];
|
|
|
|
// Initialize picking process
|
|
this->Initialize();
|
|
this->Renderer = renderer;
|
|
this->SelectionPoint[0] = selectionX;
|
|
this->SelectionPoint[1] = selectionY;
|
|
this->SelectionPoint[2] = selectionZ;
|
|
|
|
// Invoke start pick method if defined
|
|
this->InvokeEvent(vtkCommand::StartPickEvent,NULL);
|
|
|
|
if ( renderer == NULL )
|
|
{
|
|
vtkErrorMacro(<<"Must specify renderer!");
|
|
return 0;
|
|
}
|
|
|
|
// Get camera focal point and position. Convert to display (screen)
|
|
// coordinates. We need a depth value for z-buffer.
|
|
//
|
|
camera = renderer->GetActiveCamera();
|
|
camera->GetPosition((double *)cameraPos); cameraPos[3] = 1.0;
|
|
camera->GetFocalPoint((double *)cameraFP); cameraFP[3] = 1.0;
|
|
|
|
renderer->SetWorldPoint(cameraFP[0],cameraFP[1],cameraFP[2],cameraFP[3]);
|
|
renderer->WorldToDisplay();
|
|
displayCoords = renderer->GetDisplayPoint();
|
|
selectionZ = displayCoords[2];
|
|
|
|
// Convert the selection point into world coordinates.
|
|
//
|
|
renderer->SetDisplayPoint(selectionX, selectionY, selectionZ);
|
|
renderer->DisplayToWorld();
|
|
worldCoords = renderer->GetWorldPoint();
|
|
if ( worldCoords[3] == 0.0 )
|
|
{
|
|
vtkErrorMacro(<<"Bad homogeneous coordinates");
|
|
return 0;
|
|
}
|
|
for (i=0; i < 3; i++)
|
|
{
|
|
this->PickPosition[i] = worldCoords[i] / worldCoords[3];
|
|
}
|
|
|
|
// Compute the ray endpoints. The ray is along the line running from
|
|
// the camera position to the selection point, starting where this line
|
|
// intersects the front clipping plane, and terminating where this
|
|
// line intersects the back clipping plane.
|
|
for (i=0; i<3; i++)
|
|
{
|
|
ray[i] = this->PickPosition[i] - cameraPos[i];
|
|
}
|
|
for (i=0; i<3; i++)
|
|
{
|
|
cameraDOP[i] = cameraFP[i] - cameraPos[i];
|
|
}
|
|
|
|
vtkMath::Normalize(cameraDOP);
|
|
|
|
if (( rayLength = vtkMath::Dot(cameraDOP,ray)) == 0.0 )
|
|
{
|
|
vtkWarningMacro("Cannot process points");
|
|
return 0;
|
|
}
|
|
|
|
clipRange = camera->GetClippingRange();
|
|
|
|
if ( camera->GetParallelProjection() )
|
|
{
|
|
tF = clipRange[0] - rayLength;
|
|
tB = clipRange[1] - rayLength;
|
|
for (i=0; i<3; i++)
|
|
{
|
|
p1World[i] = this->PickPosition[i] + tF*cameraDOP[i];
|
|
p2World[i] = this->PickPosition[i] + tB*cameraDOP[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tF = clipRange[0] / rayLength;
|
|
tB = clipRange[1] / rayLength;
|
|
for (i=0; i<3; i++)
|
|
{
|
|
p1World[i] = cameraPos[i] + tF*ray[i];
|
|
p2World[i] = cameraPos[i] + tB*ray[i];
|
|
}
|
|
}
|
|
p1World[3] = p2World[3] = 1.0;
|
|
|
|
// Compute the tolerance in world coordinates. Do this by
|
|
// determining the world coordinates of the diagonal points of the
|
|
// window, computing the width of the window in world coordinates, and
|
|
// multiplying by the tolerance.
|
|
//
|
|
viewport = renderer->GetViewport();
|
|
winSize = renderer->GetRenderWindow()->GetSize();
|
|
x = winSize[0] * viewport[0];
|
|
y = winSize[1] * viewport[1];
|
|
renderer->SetDisplayPoint(x, y, selectionZ);
|
|
renderer->DisplayToWorld();
|
|
renderer->GetWorldPoint(windowLowerLeft);
|
|
|
|
x = winSize[0] * viewport[2];
|
|
y = winSize[1] * viewport[3];
|
|
renderer->SetDisplayPoint(x, y, selectionZ);
|
|
renderer->DisplayToWorld();
|
|
renderer->GetWorldPoint(windowUpperRight);
|
|
|
|
for (tol=0.0,i=0; i<3; i++)
|
|
{
|
|
tol += (windowUpperRight[i] - windowLowerLeft[i]) *
|
|
(windowUpperRight[i] - windowLowerLeft[i]);
|
|
}
|
|
|
|
tol = sqrt (tol) * this->Tolerance;
|
|
|
|
// Loop over all props. Transform ray (defined from position of
|
|
// camera to selection point) into coordinates of mapper (not
|
|
// transformed to actors coordinates! Reduces overall computation!!!).
|
|
// Note that only vtkProp3D's can be picked by vtkPicker.
|
|
//
|
|
vtkPropCollection *props;
|
|
vtkProp *propCandidate;
|
|
if ( this->PickFromList )
|
|
{
|
|
props = this->GetPickList();
|
|
}
|
|
else
|
|
{
|
|
props = renderer->GetViewProps();
|
|
}
|
|
|
|
vtkActor *actor;
|
|
vtkLODProp3D *prop3D;
|
|
vtkVolume *volume;
|
|
vtkImageActor *imageActor = 0;
|
|
vtkAssemblyPath *path;
|
|
vtkProperty *tempProperty;
|
|
this->Transform->PostMultiply();
|
|
vtkCollectionSimpleIterator pit;
|
|
double scale[3];
|
|
for ( props->InitTraversal(pit); (prop=props->GetNextProp(pit)); )
|
|
{
|
|
for ( prop->InitPathTraversal(); (path=prop->GetNextPath()); )
|
|
{
|
|
pickable = 0;
|
|
actor = NULL;
|
|
propCandidate = path->GetLastNode()->GetViewProp();
|
|
if ( propCandidate->GetPickable() && propCandidate->GetVisibility() )
|
|
{
|
|
pickable = 1;
|
|
if ( (actor=vtkActor::SafeDownCast(propCandidate)) != NULL )
|
|
{
|
|
mapper = actor->GetMapper();
|
|
if ( actor->GetProperty()->GetOpacity() <= 0.0 )
|
|
{
|
|
pickable = 0;
|
|
}
|
|
}
|
|
else if ( (prop3D=vtkLODProp3D::SafeDownCast(propCandidate)) != NULL )
|
|
{
|
|
LODId = prop3D->GetPickLODID();
|
|
mapper = prop3D->GetLODMapper(LODId);
|
|
|
|
// if the mapper is a vtkMapper (as opposed to a vtkVolumeMapper),
|
|
// then check the transparency to see if the object is pickable
|
|
if ( vtkMapper::SafeDownCast(mapper) != NULL)
|
|
{
|
|
prop3D->GetLODProperty(LODId, &tempProperty);
|
|
if ( tempProperty->GetOpacity() <= 0.0 )
|
|
{
|
|
pickable = 0;
|
|
}
|
|
}
|
|
}
|
|
else if ( (volume=vtkVolume::SafeDownCast(propCandidate)) != NULL )
|
|
{
|
|
mapper = volume->GetMapper();
|
|
}
|
|
else if ( (imageActor=vtkImageActor::SafeDownCast(propCandidate)) )
|
|
{
|
|
mapper = 0;
|
|
}
|
|
else
|
|
{
|
|
pickable = 0; //only vtkProp3D's (actors and volumes) can be picked
|
|
}
|
|
}
|
|
|
|
// If actor can be picked, get its composite matrix, invert it, and
|
|
// use the inverted matrix to transform the ray points into mapper
|
|
// coordinates.
|
|
if ( pickable && mapper != NULL )
|
|
{
|
|
vtkMatrix4x4 *LastMatrix = path->GetLastNode()->GetMatrix();
|
|
if (LastMatrix == NULL)
|
|
{
|
|
vtkErrorMacro (<< "Pick: Null matrix.");
|
|
return 0;
|
|
}
|
|
this->Transform->SetMatrix(LastMatrix);
|
|
this->Transform->Push();
|
|
this->Transform->Inverse();
|
|
this->Transform->GetScale(scale); //need to scale the tolerance
|
|
|
|
this->Transform->TransformPoint(p1World,p1Mapper);
|
|
this->Transform->TransformPoint(p2World,p2Mapper);
|
|
|
|
for (i=0; i<3; i++)
|
|
{
|
|
ray[i] = p2Mapper[i] - p1Mapper[i];
|
|
}
|
|
|
|
this->Transform->Pop();
|
|
|
|
// Have the ray endpoints in mapper space, now need to compare this
|
|
// with the mapper bounds to see whether intersection is possible.
|
|
//
|
|
// Get the bounding box of the modeller. Note that the tolerance is
|
|
// added to the bounding box to make sure things on the edge of the
|
|
// bounding box are picked correctly.
|
|
mapper->GetBounds(bounds);
|
|
bounds[0] -= tol; bounds[1] += tol;
|
|
bounds[2] -= tol; bounds[3] += tol;
|
|
bounds[4] -= tol; bounds[5] += tol;
|
|
if ( vtkBox::IntersectBox(bounds, (double *)p1Mapper,
|
|
ray, hitPosition, t) )
|
|
{
|
|
t = this->IntersectWithLine((double *)p1Mapper, (double *)p2Mapper,
|
|
tol*0.333*(scale[0]+scale[1]+scale[2]), path,
|
|
(vtkProp3D *)propCandidate, mapper);
|
|
if ( t < VTK_DOUBLE_MAX )
|
|
{
|
|
picked = 1;
|
|
if ( ! this->Prop3Ds->IsItemPresent(prop) )
|
|
{
|
|
this->Prop3Ds->AddItem((vtkProp3D *)prop);
|
|
}
|
|
this->PickedPositions->InsertNextPoint
|
|
((1.0 - t)*p1World[0] + t*p2World[0],
|
|
(1.0 - t)*p1World[1] + t*p2World[1],
|
|
(1.0 - t)*p1World[2] + t*p2World[2]);
|
|
|
|
// backwards compatibility: also add to this->Actors
|
|
if (actor)
|
|
{
|
|
this->Actors->AddItem(actor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if ( pickable && imageActor )
|
|
{ // special case for imageActor, which has no mapper
|
|
vtkMatrix4x4 *LastMatrix = path->GetLastNode()->GetMatrix();
|
|
if (LastMatrix == NULL)
|
|
{
|
|
vtkErrorMacro (<< "Pick: Null matrix.");
|
|
return 0;
|
|
}
|
|
this->Transform->SetMatrix(LastMatrix);
|
|
this->Transform->Push();
|
|
this->Transform->Inverse();
|
|
|
|
this->Transform->TransformPoint(p1World,p1Mapper);
|
|
this->Transform->TransformPoint(p2World,p2Mapper);
|
|
|
|
this->Transform->Pop();
|
|
|
|
// Have the ray endpoints in data space, now need to compare this
|
|
// with the displayed image bounds.
|
|
imageActor->GetDisplayBounds(bounds);
|
|
|
|
t = VTK_DOUBLE_MAX;
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (bounds[2*i] == bounds[2*i+1] && p2Mapper[i] != p1Mapper[i])
|
|
{
|
|
t = (p2World[i] - bounds[2*i])/(p2Mapper[i] - p1Mapper[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (t < VTK_DOUBLE_MAX)
|
|
{
|
|
hitPosition[0] = (1.0 - t)*p1Mapper[0] + t*p2Mapper[0];
|
|
hitPosition[1] = (1.0 - t)*p1Mapper[1] + t*p2Mapper[1];
|
|
hitPosition[2] = (1.0 - t)*p1Mapper[2] + t*p2Mapper[2];
|
|
if ((bounds[0] == bounds[1] || (hitPosition[0] >= bounds[0]-tol &&
|
|
hitPosition[0] <= bounds[1]+tol)) &&
|
|
(bounds[2] == bounds[3] || (hitPosition[1] >= bounds[2]-tol &&
|
|
hitPosition[1] <= bounds[3]+tol)) &&
|
|
(bounds[4] == bounds[5] || (hitPosition[2] >= bounds[4]-tol &&
|
|
hitPosition[2] <= bounds[5]+tol)))
|
|
{
|
|
picked = 1;
|
|
|
|
// the following code is handled in MarkPicked for other Prop3Ds
|
|
this->Mapper = mapper; // mapper is null
|
|
this->DataSet = imageActor->GetInput();
|
|
this->MapperPosition[0] = hitPosition[0];
|
|
this->MapperPosition[1] = hitPosition[1];
|
|
this->MapperPosition[2] = hitPosition[2];
|
|
this->Transform->TransformPoint(hitPosition,this->PickPosition);
|
|
imageActor->Pick();
|
|
this->InvokeEvent(vtkCommand::PickEvent,NULL);
|
|
|
|
this->Prop3Ds->AddItem(imageActor);
|
|
this->PickedPositions->InsertNextPoint
|
|
((1.0 - t)*p1World[0] + t*p2World[0],
|
|
(1.0 - t)*p1World[1] + t*p2World[1],
|
|
(1.0 - t)*p1World[2] + t*p2World[2]);
|
|
}
|
|
}
|
|
}//if visible and pickable not transparent and has mapper
|
|
}//for all parts
|
|
}//for all actors
|
|
|
|
// Invoke end pick method if defined
|
|
this->InvokeEvent(vtkCommand::EndPickEvent,NULL);
|
|
|
|
return picked;
|
|
}
|
|
|
|
// Intersect data with specified ray.
|
|
double vtkPicker::IntersectWithLine(double p1[3], double p2[3],
|
|
double vtkNotUsed(tol),
|
|
vtkAssemblyPath *path,
|
|
vtkProp3D *prop3D,
|
|
vtkAbstractMapper3D *mapper)
|
|
{
|
|
int i;
|
|
double center[3], t, ray[3], rayFactor;
|
|
|
|
// Get the data from the modeler
|
|
mapper->GetCenter(center);
|
|
|
|
for (i=0; i<3; i++)
|
|
{
|
|
ray[i] = p2[i] - p1[i];
|
|
}
|
|
if (( rayFactor = vtkMath::Dot(ray,ray)) == 0.0 )
|
|
{
|
|
return 2.0;
|
|
}
|
|
|
|
// Project the center point onto the ray and determine its parametric value
|
|
//
|
|
t = (ray[0]*(center[0]-p1[0]) + ray[1]*(center[1]-p1[1])
|
|
+ ray[2]*(center[2]-p1[2])) / rayFactor;
|
|
|
|
if ( t >= 0.0 && t <= 1.0 && t < this->GlobalTMin )
|
|
{
|
|
this->MarkPicked(path, prop3D, mapper, t, center);
|
|
}
|
|
return t;
|
|
}
|
|
|
|
// Initialize the picking process.
|
|
void vtkPicker::Initialize()
|
|
{
|
|
this->vtkAbstractPropPicker::Initialize();
|
|
|
|
this->Actors->RemoveAllItems();
|
|
this->Prop3Ds->RemoveAllItems();
|
|
this->PickedPositions->Reset();
|
|
|
|
this->MapperPosition[0] = 0.0;
|
|
this->MapperPosition[1] = 0.0;
|
|
this->MapperPosition[2] = 0.0;
|
|
|
|
this->Mapper = NULL;
|
|
this->GlobalTMin = VTK_DOUBLE_MAX;
|
|
}
|
|
|
|
|
|
vtkActorCollection *vtkPicker::GetActors()
|
|
{
|
|
if (this->Actors->GetNumberOfItems() !=
|
|
this->PickedPositions->GetNumberOfPoints())
|
|
{
|
|
vtkWarningMacro(<<"Not all Prop3Ds are actors, use GetProp3Ds instead");
|
|
}
|
|
return this->Actors;
|
|
}
|
|
|
|
|
|
void vtkPicker::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os,indent);
|
|
|
|
if ( this->DataSet )
|
|
{
|
|
os << indent << "DataSet: " << this->DataSet << "\n";
|
|
}
|
|
else
|
|
{
|
|
os << indent << "DataSet: (none)";
|
|
}
|
|
|
|
os << indent << "Mapper: " << this->Mapper << "\n";
|
|
|
|
os << indent << "Tolerance: " << this->Tolerance << "\n";
|
|
|
|
os << indent << "Mapper Position: (" << this->MapperPosition[0] << ","
|
|
<< this->MapperPosition[1] << ","
|
|
<< this->MapperPosition[2] << ")\n";
|
|
}
|
|
|