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.
683 lines
19 KiB
683 lines
19 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
Program: Visualization Toolkit
|
||
|
Module: $RCSfile: vtkInteractorStyleUnicam.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.
|
||
|
|
||
|
=========================================================================*/
|
||
|
|
||
|
/*
|
||
|
* This work was produced under a grant from the Department of Energy to Brown
|
||
|
* University. Neither Brown University nor the authors assert any copyright
|
||
|
* with respect to this work and it may be used, reproduced, and distributed
|
||
|
* without permission.
|
||
|
*/
|
||
|
|
||
|
#include "vtkInteractorStyleUnicam.h"
|
||
|
|
||
|
#include "vtkActor.h"
|
||
|
#include "vtkCamera.h"
|
||
|
#include "vtkMath.h"
|
||
|
#include "vtkObjectFactory.h"
|
||
|
#include "vtkPolyDataMapper.h"
|
||
|
#include "vtkProperty.h"
|
||
|
#include "vtkRenderWindow.h"
|
||
|
#include "vtkRenderWindowInteractor.h"
|
||
|
#include "vtkRenderer.h"
|
||
|
#include "vtkSphereSource.h"
|
||
|
#include "vtkTransform.h"
|
||
|
#include "vtkWorldPointPicker.h"
|
||
|
|
||
|
vtkCxxRevisionMacro(vtkInteractorStyleUnicam, "$Revision: 1.36 $");
|
||
|
vtkStandardNewMacro(vtkInteractorStyleUnicam);
|
||
|
|
||
|
// define 'TheTime()' function-- returns time in elapsed seconds
|
||
|
#if defined(_WIN32) || defined(WIN32)
|
||
|
# include "vtkWindows.h"
|
||
|
|
||
|
static double TheTime()
|
||
|
{return double(GetTickCount())/1000.0;}
|
||
|
#else
|
||
|
#include <sys/time.h>
|
||
|
|
||
|
static double TheTime()
|
||
|
{
|
||
|
struct timeval ts; struct timezone tz;
|
||
|
gettimeofday(&ts, &tz);
|
||
|
return (double)(ts.tv_sec + ts.tv_usec/1e6);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
vtkInteractorStyleUnicam::vtkInteractorStyleUnicam()
|
||
|
{
|
||
|
// use z-buffer picking
|
||
|
this->InteractionPicker = vtkWorldPointPicker::New();
|
||
|
|
||
|
// set to default modes
|
||
|
this->IsDot = 0;
|
||
|
this->ButtonDown = VTK_UNICAM_NONE;
|
||
|
state = 0; // which camera mode is being used?
|
||
|
|
||
|
// create focus sphere actor
|
||
|
vtkSphereSource *sphere = vtkSphereSource::New();
|
||
|
sphere->SetThetaResolution(6);
|
||
|
sphere->SetPhiResolution(6);
|
||
|
vtkPolyDataMapper *sphereMapper = vtkPolyDataMapper::New();
|
||
|
sphereMapper->SetInput(sphere->GetOutput());
|
||
|
sphere->Delete();
|
||
|
|
||
|
// XXX - would like to make the focus sphere not be affected by
|
||
|
// XXX - the lights-- i.e., always be easily easily seen. i'm not sure
|
||
|
// XXX - how to do that.
|
||
|
this->FocusSphere = vtkActor::New();
|
||
|
this->FocusSphere->SetMapper(sphereMapper);
|
||
|
this->FocusSphere->GetProperty()->SetColor(0.8900,0.6600,0.4100);
|
||
|
this->FocusSphere->GetProperty()->SetRepresentationToWireframe();
|
||
|
sphereMapper->Delete();
|
||
|
|
||
|
// set WorldUpVector to be z-axis by default
|
||
|
WorldUpVector[0] = 0;
|
||
|
WorldUpVector[1] = 0;
|
||
|
WorldUpVector[2] = 1;
|
||
|
}
|
||
|
|
||
|
vtkInteractorStyleUnicam::~vtkInteractorStyleUnicam()
|
||
|
{
|
||
|
this->InteractionPicker->Delete();
|
||
|
this->FocusSphere->Delete();
|
||
|
}
|
||
|
|
||
|
void vtkInteractorStyleUnicam::PrintSelf(ostream& os, vtkIndent indent)
|
||
|
{
|
||
|
this->Superclass::PrintSelf(os,indent);
|
||
|
|
||
|
os << indent << "Interaction Picker: " << this->InteractionPicker;
|
||
|
// os << indent << "WorldUpVector: " << this->WorldUpVector;
|
||
|
}
|
||
|
|
||
|
void vtkInteractorStyleUnicam::OnTimer()
|
||
|
{
|
||
|
if (this->ButtonDown != VTK_UNICAM_NONE)
|
||
|
{
|
||
|
// restart timer-- we want to keep getting 'OnMouseMove' events
|
||
|
if (this->UseTimers)
|
||
|
{
|
||
|
this->Interactor->CreateTimer(VTKI_TIMER_UPDATE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void vtkInteractorStyleUnicam::SetWorldUpVector(double x, double y, double z)
|
||
|
{
|
||
|
WorldUpVector[0] = x;
|
||
|
WorldUpVector[1] = y;
|
||
|
WorldUpVector[2] = z;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::OnLeftButtonDown()
|
||
|
{
|
||
|
int x = this->Interactor->GetEventPosition()[0];
|
||
|
int y = this->Interactor->GetEventPosition()[1];
|
||
|
|
||
|
this->ButtonDown = VTK_UNICAM_BUTTON_LEFT;
|
||
|
if (this->UseTimers)
|
||
|
{
|
||
|
this->Interactor->CreateTimer(VTKI_TIMER_UPDATE);
|
||
|
}
|
||
|
|
||
|
this->DTime = TheTime();
|
||
|
this->Dist = 0;
|
||
|
|
||
|
// cam manip init
|
||
|
double curpt[2];
|
||
|
this->NormalizeMouseXY(x, y, &curpt[0], &curpt[1]);
|
||
|
this->LastPos[0] = curpt[0];
|
||
|
this->LastPos[1] = curpt[1];
|
||
|
|
||
|
this->StartPix[0] = this->LastPix[0] = x;
|
||
|
this->StartPix[1] = this->LastPix[1] = y;
|
||
|
|
||
|
// Find 'this->DownPt' (point in world space under the cursor tip)
|
||
|
//
|
||
|
// Note: If no object has been rendered to the pixel (X, Y), then
|
||
|
// vtkWorldPointPicker will return a z-value with depth equal
|
||
|
// to the distance from the camera's position to the focal point.
|
||
|
// This seems like an arbitrary, but perhaps reasonable, default value.
|
||
|
//
|
||
|
this->FindPokedRenderer(x, y);
|
||
|
this->InteractionPicker->Pick(x, y, 0.0, this->CurrentRenderer);
|
||
|
this->InteractionPicker->GetPickPosition(this->DownPt);
|
||
|
|
||
|
// if someone has already clicked to make a dot and they're not clicking
|
||
|
// on it now, OR if the user is clicking on the perimeter of the screen,
|
||
|
// then we want to go into rotation mode.
|
||
|
if ((fabs(curpt[0]) > .85 || fabs(curpt[1]) > .9) || this->IsDot)
|
||
|
{
|
||
|
if (this->IsDot)
|
||
|
{
|
||
|
this->FocusSphere->GetPosition(this->Center);
|
||
|
}
|
||
|
state = VTK_UNICAM_CAM_INT_ROT;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
state = VTK_UNICAM_CAM_INT_CHOOSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
double vtkInteractorStyleUnicam::WindowAspect()
|
||
|
{
|
||
|
double w = Interactor->GetRenderWindow()->GetSize()[0];
|
||
|
double h = Interactor->GetRenderWindow()->GetSize()[1];
|
||
|
|
||
|
return w/h;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::NormalizeMouseXY(int X, int Y,
|
||
|
double *NX, double *NY)
|
||
|
{
|
||
|
double w = Interactor->GetRenderWindow()->GetSize()[0];
|
||
|
double h = Interactor->GetRenderWindow()->GetSize()[1];
|
||
|
|
||
|
*NX = -1.0 + 2.0 * double(X) / w;
|
||
|
*NY = -1.0 + 2.0 * double(Y) / h;
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::OnMouseMove()
|
||
|
{
|
||
|
int x = this->Interactor->GetEventPosition()[0];
|
||
|
int y = this->Interactor->GetEventPosition()[1];
|
||
|
|
||
|
// filter out any repeated events
|
||
|
static int last_X = 0;
|
||
|
static int last_Y = 0;
|
||
|
if (x == last_X && y == last_Y)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// channel event to right method handler.
|
||
|
switch (this->ButtonDown)
|
||
|
{
|
||
|
case VTK_UNICAM_BUTTON_LEFT:
|
||
|
OnLeftButtonMove();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
last_X = x;
|
||
|
last_Y = y;
|
||
|
|
||
|
this->Interactor->Render(); // re-draw scene.. it should have changed
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::OnLeftButtonUp()
|
||
|
{
|
||
|
int x = this->Interactor->GetEventPosition()[0];
|
||
|
int y = this->Interactor->GetEventPosition()[1];
|
||
|
|
||
|
this->ButtonDown = VTK_UNICAM_NONE;
|
||
|
|
||
|
if (state == VTK_UNICAM_CAM_INT_ROT && this->IsDot )
|
||
|
{
|
||
|
this->FocusSphereRenderer->RemoveActor(this->FocusSphere);
|
||
|
this->IsDot = 0;
|
||
|
}
|
||
|
else if (state == VTK_UNICAM_CAM_INT_CHOOSE)
|
||
|
{
|
||
|
if (this->IsDot)
|
||
|
{
|
||
|
this->FocusSphereRenderer->RemoveActor(this->FocusSphere);
|
||
|
this->IsDot = 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
this->FocusSphere->SetPosition(this->DownPt[0],
|
||
|
this->DownPt[1],
|
||
|
this->DownPt[2]);
|
||
|
|
||
|
double from[3];
|
||
|
this->FindPokedRenderer(x, y);
|
||
|
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
|
||
|
camera->GetPosition(from);
|
||
|
|
||
|
double vec[3];
|
||
|
vec[0] = this->DownPt[0] - from[0];
|
||
|
vec[1] = this->DownPt[1] - from[1];
|
||
|
vec[2] = this->DownPt[2] - from[2];
|
||
|
|
||
|
double at_v[4];
|
||
|
camera->GetDirectionOfProjection(at_v);
|
||
|
vtkMath::Normalize(at_v);
|
||
|
|
||
|
// calculate scale so focus sphere always is the same size on the screen
|
||
|
double s = 0.02 * vtkMath::Dot(at_v, vec);
|
||
|
|
||
|
this->FocusSphere->SetScale (s, s, s);
|
||
|
|
||
|
this->FindPokedRenderer(x, y);
|
||
|
this->FocusSphereRenderer = this->CurrentRenderer;
|
||
|
this->FocusSphereRenderer->AddActor(this->FocusSphere);
|
||
|
|
||
|
this->IsDot = 1;
|
||
|
}
|
||
|
this->Interactor->Render();
|
||
|
}
|
||
|
|
||
|
vtkRenderWindowInteractor *rwi = this->Interactor;
|
||
|
rwi->GetRenderWindow()->SetDesiredUpdateRate(rwi->GetStillUpdateRate());
|
||
|
rwi->Render();
|
||
|
if (this->UseTimers)
|
||
|
{
|
||
|
rwi->DestroyTimer();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::OnLeftButtonMove()
|
||
|
{
|
||
|
int x = this->Interactor->GetEventPosition()[0];
|
||
|
int y = this->Interactor->GetEventPosition()[1];
|
||
|
|
||
|
switch (state)
|
||
|
{
|
||
|
case VTK_UNICAM_CAM_INT_CHOOSE: this->ChooseXY(x, y); break;
|
||
|
case VTK_UNICAM_CAM_INT_ROT: this->RotateXY(x, y); break;
|
||
|
case VTK_UNICAM_CAM_INT_PAN: this->PanXY(x, y); break;
|
||
|
case VTK_UNICAM_CAM_INT_DOLLY: this->DollyXY(x, y); break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::ChooseXY( int X, int Y )
|
||
|
{
|
||
|
int te[2]; // pixel location
|
||
|
te[0] = X;
|
||
|
te[1] = Y;
|
||
|
|
||
|
double curpt[2];
|
||
|
this->NormalizeMouseXY(X, Y, &curpt[0], &curpt[1]);
|
||
|
|
||
|
double delta[2];
|
||
|
delta[0] = curpt[0] - this->LastPos[0];
|
||
|
delta[1] = curpt[1] - this->LastPos[1];
|
||
|
this->LastPos[0] = te[0];
|
||
|
this->LastPos[1] = te[1];
|
||
|
|
||
|
double tdelt(TheTime() - this->DTime);
|
||
|
|
||
|
this->Dist += sqrt(delta[0] * delta[0] + delta[1] * delta[1]);
|
||
|
|
||
|
double sdelt[2];
|
||
|
sdelt[0] = te[0] - this->StartPix[0];
|
||
|
sdelt[1] = te[1] - this->StartPix[1];
|
||
|
|
||
|
int xa=0,ya=1;
|
||
|
if (getenv("FLIP_CAM_MANIP"))
|
||
|
{
|
||
|
int tmp = xa;
|
||
|
xa = ya;
|
||
|
ya = tmp;
|
||
|
}
|
||
|
|
||
|
double len = sqrt(sdelt[0] * sdelt[0] + sdelt[1] * sdelt[1]);
|
||
|
if (fabs(sdelt[ya])/len > 0.9 && tdelt > 0.05)
|
||
|
{
|
||
|
state = VTK_UNICAM_CAM_INT_DOLLY;
|
||
|
}
|
||
|
else if (tdelt < 0.1 && this->Dist < 0.03)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (fabs(sdelt[xa])/len > 0.6 )
|
||
|
{
|
||
|
state = VTK_UNICAM_CAM_INT_PAN;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
state = VTK_UNICAM_CAM_INT_DOLLY;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// define some utilty functions
|
||
|
template <class Type>
|
||
|
inline Type clamp(const Type a,
|
||
|
const Type b,
|
||
|
const Type c) { return a > b ? (a < c ? a : c) : b ; }
|
||
|
inline int Sign (double a) { return a > 0 ? 1 : a < 0 ? -1 : 0; }
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::RotateXY( int X, int Y )
|
||
|
{
|
||
|
double cpt[3];
|
||
|
double center[3];
|
||
|
this->FocusSphere->GetPosition(center);
|
||
|
this->ComputeWorldToDisplay(center[0], center[1], center[2], cpt);
|
||
|
this->NormalizeMouseXY(static_cast<int>(cpt[0]), static_cast<int>(cpt[1]),
|
||
|
&cpt[0], &cpt[1]);
|
||
|
|
||
|
double radsq = pow(1.0+fabs(cpt[0]),2.0); // squared rad of virtual cylinder
|
||
|
|
||
|
double tp[2], te[2];
|
||
|
this->NormalizeMouseXY(static_cast<int>(this->LastPix[0]),
|
||
|
static_cast<int>(this->LastPix[1]), &tp[0], &tp[1]);
|
||
|
this->NormalizeMouseXY(X, Y, &te[0], &te[1]);
|
||
|
this->LastPix[0] = X;
|
||
|
this->LastPix[1] = Y;
|
||
|
|
||
|
double op[3], oe[3];
|
||
|
op[0] = tp[0];
|
||
|
op[1] = 0;
|
||
|
op[2] = 0;
|
||
|
oe[0] = te[0];
|
||
|
oe[1] = 0;
|
||
|
oe[2] = 0;
|
||
|
|
||
|
double opsq = op[0] * op[0], oesq = oe[0] * oe[0];
|
||
|
|
||
|
double lop = opsq > radsq ? 0 : sqrt(radsq - opsq);
|
||
|
double loe = oesq > radsq ? 0 : sqrt(radsq - oesq);
|
||
|
|
||
|
double nop[3], noe[3];
|
||
|
nop[0] = op[0];
|
||
|
nop[1] = 0;
|
||
|
nop[2] = lop;
|
||
|
vtkMath::Normalize(nop);
|
||
|
noe[0] = oe[0];
|
||
|
noe[1] = 0;
|
||
|
noe[2] = loe;
|
||
|
vtkMath::Normalize(noe);
|
||
|
|
||
|
double dot = vtkMath::Dot(nop, noe);
|
||
|
|
||
|
if (fabs(dot) > 0.0001)
|
||
|
{
|
||
|
this->FindPokedRenderer(X, Y);
|
||
|
|
||
|
double angle = -2*acos(clamp(dot,(double)-1.0,(double)1.0)) * Sign(te[0]-tp[0]);
|
||
|
|
||
|
double UPvec[3];
|
||
|
UPvec[0] = WorldUpVector[0];
|
||
|
UPvec[1] = WorldUpVector[1];
|
||
|
UPvec[2] = WorldUpVector[2];
|
||
|
vtkMath::Normalize(UPvec);
|
||
|
|
||
|
MyRotateCamera(center[0], center[1], center[2],
|
||
|
UPvec[0], UPvec[1], UPvec[2],
|
||
|
angle);
|
||
|
|
||
|
double dvec[3];
|
||
|
double from[3];
|
||
|
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
|
||
|
camera->GetPosition(from);
|
||
|
for(int i=0; i<3; i++)
|
||
|
{
|
||
|
dvec[i] = from[i] - center[i];
|
||
|
}
|
||
|
|
||
|
double rdist = te[1]-tp[1];
|
||
|
vtkMath::Normalize(dvec);
|
||
|
|
||
|
double atV[4], upV[4], rightV[4];
|
||
|
camera->GetViewPlaneNormal(atV);
|
||
|
camera->GetViewUp(upV);
|
||
|
vtkMath::Cross(upV, atV, rightV);
|
||
|
vtkMath::Normalize(rightV);
|
||
|
|
||
|
//
|
||
|
// The following two tests try to prevent chaotic camera movement
|
||
|
// that results from rotating over the poles defined by the
|
||
|
// "WorldUpVector". The problem is the constraint to keep the
|
||
|
// camera's up vector in line w/ the WorldUpVector is at odds with
|
||
|
// the action of rotating tover the top of the virtual sphere used
|
||
|
// for rotation. The solution here is to prevent the user from
|
||
|
// rotating the last bit required to "go over the top"-- as a
|
||
|
// consequence, you can never look directly down on the poles.
|
||
|
//
|
||
|
// The "0.99" value is somewhat arbitrary, but seems to produce
|
||
|
// reasonable results. (Theoretically, some sort of clamping
|
||
|
// function could probably be used rather than a hard cutoff, but
|
||
|
// time constraints prevent figuring that out right now.)
|
||
|
//
|
||
|
const double OVER_THE_TOP_THRESHOLD = 0.99;
|
||
|
if (vtkMath::Dot(UPvec, atV) > OVER_THE_TOP_THRESHOLD && rdist < 0)
|
||
|
rdist = 0;
|
||
|
if (vtkMath::Dot(UPvec, atV) < -OVER_THE_TOP_THRESHOLD && rdist > 0)
|
||
|
rdist = 0;
|
||
|
|
||
|
MyRotateCamera(center[0], center[1], center[2],
|
||
|
rightV[0], rightV[1], rightV[2],
|
||
|
rdist);
|
||
|
|
||
|
camera->SetViewUp(UPvec[0], UPvec[1], UPvec[2]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
void vtkInteractorStyleUnicam::DollyXY( int X, int Y )
|
||
|
{
|
||
|
int i;
|
||
|
double cn[2], ln[2];
|
||
|
this->NormalizeMouseXY(X, Y, &cn[0], &cn[1]);
|
||
|
this->NormalizeMouseXY(static_cast<int>(this->LastPix[0]),
|
||
|
static_cast<int>(this->LastPix[1]), &ln[0], &ln[1]);
|
||
|
|
||
|
double delta[2];
|
||
|
delta[0] = cn[0] - ln[0];
|
||
|
delta[1] = cn[1] - ln[1];
|
||
|
this->LastPix[0] = X;
|
||
|
this->LastPix[1] = Y;
|
||
|
|
||
|
// 1. handle dollying
|
||
|
// XXX - assume perspective projection for now.
|
||
|
double from[3];
|
||
|
this->FindPokedRenderer(X, Y);
|
||
|
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
|
||
|
camera->GetPosition(from);
|
||
|
|
||
|
double movec[3];
|
||
|
for(i=0; i<3; i++)
|
||
|
{
|
||
|
movec[i] = this->DownPt[i] - from[i];
|
||
|
}
|
||
|
|
||
|
double offset1[3];
|
||
|
for(i=0; i<3; i++)
|
||
|
{
|
||
|
offset1[i] = movec[i] * delta[1] * -4;
|
||
|
}
|
||
|
|
||
|
this->MyTranslateCamera(offset1);
|
||
|
|
||
|
|
||
|
// 2. now handle side-to-side panning
|
||
|
double rightV[3], upV[3];
|
||
|
this->GetRightVandUpV(this->DownPt, camera,
|
||
|
rightV, upV);
|
||
|
|
||
|
double offset2[3];
|
||
|
for(i=0; i<3; i++)
|
||
|
{
|
||
|
offset2[i] = (-delta[0] * rightV[i]);
|
||
|
}
|
||
|
|
||
|
this->MyTranslateCamera(offset2);
|
||
|
}
|
||
|
|
||
|
//----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Transform mouse horizontal & vertical movements to a world
|
||
|
// space offset for the camera that maintains pick correlation.
|
||
|
//
|
||
|
void vtkInteractorStyleUnicam::PanXY( int X, int Y )
|
||
|
{
|
||
|
double delta[2];
|
||
|
double cn[2], ln[2];
|
||
|
int i;
|
||
|
|
||
|
this->NormalizeMouseXY(X, Y, &cn[0], &cn[1]);
|
||
|
this->NormalizeMouseXY(static_cast<int>(this->LastPix[0]),
|
||
|
static_cast<int>(this->LastPix[1]), &ln[0], &ln[1]);
|
||
|
delta[0] = cn[0] - ln[0];
|
||
|
delta[1] = cn[1] - ln[1];
|
||
|
this->LastPix[0] = X;
|
||
|
this->LastPix[1] = Y;
|
||
|
|
||
|
// XXX - assume perspective projection for now
|
||
|
|
||
|
this->FindPokedRenderer(X, Y);
|
||
|
|
||
|
double rightV[3], upV[3];
|
||
|
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
|
||
|
this->GetRightVandUpV(this->DownPt, camera,
|
||
|
rightV, upV);
|
||
|
|
||
|
double offset[3];
|
||
|
for(i=0; i<3; i++)
|
||
|
{
|
||
|
offset[i] = (-delta[0] * rightV[i] +
|
||
|
-delta[1] * upV [i]);
|
||
|
}
|
||
|
|
||
|
this->MyTranslateCamera(offset);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Given a 3D point & a vtkCamera, compute the vectors that extend
|
||
|
// from the projection of the center of projection to the center of
|
||
|
// the right-edge and the center of the top-edge onto the plane
|
||
|
// containing the 3D point & with normal parallel to the camera's
|
||
|
// projection plane.
|
||
|
//
|
||
|
void vtkInteractorStyleUnicam::GetRightVandUpV(double *p, vtkCamera *cam,
|
||
|
double *rightV, double *upV)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
// Compute the horizontal & vertical scaling ('scalex' and 'scaley')
|
||
|
// factors as function of the down point & camera params.
|
||
|
double from[3];
|
||
|
cam->GetPosition(from);
|
||
|
|
||
|
// construct a vector from the viewing position to the picked point
|
||
|
double vec[3];
|
||
|
for(i=0; i<3; i++)
|
||
|
{
|
||
|
vec[i] = p[i] - from[i];
|
||
|
}
|
||
|
|
||
|
// Get shortest distance 'l' between the viewing position and
|
||
|
// plane parallel to the projection plane that contains the 'DownPt'.
|
||
|
double atV[4];
|
||
|
cam->GetViewPlaneNormal(atV);
|
||
|
vtkMath::Normalize(atV);
|
||
|
double l = -vtkMath::Dot(vec, atV);
|
||
|
|
||
|
double view_angle = cam->GetViewAngle() * vtkMath::Pi() / 180.0;
|
||
|
double w = Interactor->GetRenderWindow()->GetSize()[0];
|
||
|
double h = Interactor->GetRenderWindow()->GetSize()[1];
|
||
|
double scalex = w/h*((2*l*tan(view_angle/2))/2);
|
||
|
double scaley = ((2*l*tan(view_angle/2))/2);
|
||
|
|
||
|
// construct the camera offset vector as function of delta mouse X & Y.
|
||
|
cam->GetViewUp(upV);
|
||
|
vtkMath::Cross(upV, atV, rightV);
|
||
|
vtkMath::Cross(atV, rightV, upV); // (make sure 'upV' is orthogonal
|
||
|
// to 'atV' & 'rightV')
|
||
|
vtkMath::Normalize(rightV);
|
||
|
vtkMath::Normalize(upV);
|
||
|
|
||
|
|
||
|
for(i=0; i<3; i++)
|
||
|
{
|
||
|
rightV[i] = rightV[i] * scalex;
|
||
|
upV [i] = upV [i] * scaley;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Rotate the camera by 'angle' degrees about the point <cx, cy, cz>
|
||
|
// and around the vector/axis <ax, ay, az>.
|
||
|
//
|
||
|
void vtkInteractorStyleUnicam::MyRotateCamera(double cx, double cy, double cz,
|
||
|
double ax, double ay, double az,
|
||
|
double angle)
|
||
|
{
|
||
|
angle *= 180.0 / vtkMath::Pi(); // vtk uses degrees, not radians
|
||
|
|
||
|
double p[4], f[4], u[4];
|
||
|
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
|
||
|
camera->GetPosition (p);
|
||
|
camera->GetFocalPoint(f);
|
||
|
camera->GetViewUp (u);
|
||
|
p[3] = f[3] = 1.0; // (points)
|
||
|
u[3] = 0.0; // (a vector)
|
||
|
|
||
|
vtkTransform *t = vtkTransform::New();
|
||
|
t->PostMultiply();
|
||
|
t->Identity();
|
||
|
t->Translate(-cx, -cy, -cz);
|
||
|
t->RotateWXYZ(angle, ax, ay, az);
|
||
|
t->Translate( cx, cy, cz);
|
||
|
|
||
|
double new_p[4], new_f[4];
|
||
|
t->MultiplyPoint(p, new_p);
|
||
|
t->MultiplyPoint(f, new_f);
|
||
|
|
||
|
double new_u[4];
|
||
|
t->Identity();
|
||
|
t->RotateWXYZ(angle, ax, ay, az);
|
||
|
t->MultiplyPoint(u, new_u);
|
||
|
|
||
|
camera->SetPosition (new_p[0], new_p[1], new_p[2]);
|
||
|
camera->SetFocalPoint(new_f[0], new_f[1], new_f[2]);
|
||
|
camera->SetViewUp (new_u[0], new_u[1], new_u[2]);
|
||
|
|
||
|
// IMPORTANT! If you don't re-compute view plane normal, the camera
|
||
|
// view gets all messed up.
|
||
|
camera->ComputeViewPlaneNormal();
|
||
|
|
||
|
t->Delete();
|
||
|
}
|
||
|
|
||
|
// Translate the camera by the offset <v[0], v[1], v[2]>. Update
|
||
|
// the camera clipping range.
|
||
|
//
|
||
|
void vtkInteractorStyleUnicam::MyTranslateCamera(double v[3])
|
||
|
{
|
||
|
double p[3], f[3];
|
||
|
vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
|
||
|
camera->GetPosition (p);
|
||
|
camera->GetFocalPoint(f);
|
||
|
|
||
|
double newP[3], newF[3];
|
||
|
for(int i=0;i<3;i++)
|
||
|
{
|
||
|
newP[i] = p[i] + v[i];
|
||
|
newF[i] = f[i] + v[i];
|
||
|
}
|
||
|
|
||
|
camera->SetPosition (newP);
|
||
|
camera->SetFocalPoint(newF);
|
||
|
|
||
|
if (this->AutoAdjustCameraClippingRange)
|
||
|
{
|
||
|
this->CurrentRenderer->ResetCameraClippingRange();
|
||
|
}
|
||
|
}
|
||
|
|