/*========================================================================= Program: Visualization Toolkit Module: $RCSfile: vtkInteractorStyleRubberBandZoom1.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 "vtkInteractorStyleRubberBandZoom1.h" #include "vtkCamera.h" #include "vtkObjectFactory.h" #include "vtkRenderer.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkUnsignedCharArray.h" #include "vtkMath.h" vtkCxxRevisionMacro(vtkInteractorStyleRubberBandZoom1, "$Revision: 1.10 $"); vtkStandardNewMacro(vtkInteractorStyleRubberBandZoom1); vtkInteractorStyleRubberBandZoom1::vtkInteractorStyleRubberBandZoom1() { this->StartPosition[0] = this->StartPosition[1] = 0; this->EndPosition[0] = this->EndPosition[1] = 0; this->Moving = 0; // Store the pixels for each edge of the rubber band box. this->StartPosXPixels = vtkUnsignedCharArray::New(); this->StartPosYPixels = vtkUnsignedCharArray::New(); this->EndPosXPixels = vtkUnsignedCharArray::New(); this->EndPosYPixels = vtkUnsignedCharArray::New(); } //-------------------------------------------------------------------------- vtkInteractorStyleRubberBandZoom1::~vtkInteractorStyleRubberBandZoom1() { this->StartPosXPixels->Delete(); this->StartPosYPixels->Delete(); this->EndPosXPixels->Delete(); this->EndPosYPixels->Delete(); } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::OnLeftButtonDown() { if (!this->Interactor) { return; } this->Moving = 1; vtkRenderWindow *renWin = this->Interactor->GetRenderWindow(); this->StartPosition[0] = this->Interactor->GetEventPosition()[0]; this->StartPosition[1] = this->Interactor->GetEventPosition()[1]; this->EndPosition[0] = this->StartPosition[0]; this->EndPosition[1] = this->StartPosition[1]; // Remove the previous contents of the pixel line arrays because the viewport // may have been resized. this->StartPosXPixels->Initialize(); this->StartPosYPixels->Initialize(); this->EndPosXPixels->Initialize(); this->EndPosYPixels->Initialize(); this->StartPosXPixels->SetNumberOfComponents( 3 ); this->StartPosYPixels->SetNumberOfComponents( 3 ); this->EndPosXPixels->SetNumberOfComponents( 3 ); this->EndPosYPixels->SetNumberOfComponents( 3 ); int* size = renWin->GetSize(); this->StartPosXPixels->SetNumberOfTuples( size[0] ); this->StartPosYPixels->SetNumberOfTuples( size[1] ); this->EndPosXPixels->SetNumberOfTuples( size[0] ); this->EndPosYPixels->SetNumberOfTuples( size[1] ); // Copy the pixels for each side of the rubber band. const int front = 1; renWin->GetPixelData( 0, this->StartPosition[1], size[0]-1, this->StartPosition[1], front, this->StartPosXPixels ); renWin->GetPixelData( this->StartPosition[0], 0, this->StartPosition[0], size[1]-1, front, this->StartPosYPixels ); renWin->GetPixelData( 0, this->EndPosition[1], size[0]-1, this->EndPosition[1], front, this->EndPosXPixels ); renWin->GetPixelData( this->EndPosition[0], 0, this->EndPosition[0], size[1]-1, front, this->EndPosYPixels ); this->FindPokedRenderer(this->StartPosition[0], this->StartPosition[1]); } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::OnMouseMove() { if (!this->Interactor || !this->Moving) { return; } // Erase the rubber band drawn on the previous call by redrawing. The 2nd // XOR operation has the effect of restoring the original pixels. RedrawRubberBand(); // Get the new END position of the rubber band. Note that the viewport // coordinates (x-y pixel) can be -ve or exceed the viewport size if the // mouse is not constrained to moved inside the viewport. this->EndPosition[0] = this->Interactor->GetEventPosition()[0]; this->EndPosition[1] = this->Interactor->GetEventPosition()[1]; vtkRenderWindow* renWin = this->Interactor->GetRenderWindow(); int *size = renWin->GetSize(); if (this->EndPosition[0] > (size[0]-1)) { this->EndPosition[0] = size[0]-1; } if (this->EndPosition[0] < 0) { this->EndPosition[0] = 0; } if (this->EndPosition[1] > (size[1]-1)) { this->EndPosition[1] = size[1]-1; } if (this->EndPosition[1] < 0) { this->EndPosition[1] = 0; } // Get the new line pixels at the current END position. const int front = 1; renWin->GetPixelData( 0, this->EndPosition[1], size[0]-1, this->EndPosition[1], front, this->EndPosXPixels ); renWin->GetPixelData( this->EndPosition[0], 0, this->EndPosition[0], size[1]-1, front, this->EndPosYPixels ); // Draw the rubber band at the new end position. RedrawRubberBand(); } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::OnLeftButtonUp() { if (!this->Interactor || !this->Moving) { return; } if ( (this->StartPosition[0] != this->EndPosition[0]) || (this->StartPosition[1] != this->EndPosition[1]) ) { if (this->Interactor->GetShiftKey()) { this->ZoomOut(); } else { this->ZoomIn(); } } this->Moving = 0; } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::RedrawRubberBand() { vtkRenderWindow* renWin = this->Interactor->GetRenderWindow(); int* size = renWin->GetSize(); int min[2], max[2]; min[0] = this->StartPosition[0] <= this->EndPosition[0] ? this->StartPosition[0] : this->EndPosition[0]; if (min[0] < 0) { min[0] = 0; } if (min[0] >= size[0]) { min[0] = size[0] - 1; } min[1] = this->StartPosition[1] <= this->EndPosition[1] ? this->StartPosition[1] : this->EndPosition[1]; if (min[1] < 0) { min[1] = 0; } if (min[1] >= size[1]) { min[1] = size[1] - 1; } max[0] = this->EndPosition[0] > this->StartPosition[0] ? this->EndPosition[0] : this->StartPosition[0]; if (max[0] < 0) { max[0] = 0; } if (max[0] >= size[0]) { max[0] = size[0] - 1; } max[1] = this->EndPosition[1] > this->StartPosition[1] ? this->EndPosition[1] : this->StartPosition[1]; if (max[1] < 0) { max[1] = 0; } if (max[1] >= size[1]) { max[1] = size[1] - 1; } // Draw each side of the rubber band. Note that the XOR gives a colour dependent // on the existing pixel colour. const int front = 1; unsigned char* pixels = this->StartPosXPixels->GetPointer(0); for ( int i = min[0]; i <= max[0]; i++ ) { pixels[3*i] = 255 ^ pixels[3*i]; pixels[3*i+1] = 255 ^ pixels[3*i+1]; pixels[3*i+2] = 255 ^ pixels[3*i+2]; } renWin->SetPixelData( 0, this->StartPosition[1], size[0]-1, this->StartPosition[1], pixels, front ); pixels = this->StartPosYPixels->GetPointer(0); for ( int j = min[1]+1; j < max[1]; j++ ) { pixels[3*j] = 255 ^ pixels[3*j]; pixels[3*j+1] = 255 ^ pixels[3*j+1]; pixels[3*j+2] = 255 ^ pixels[3*j+2]; } renWin->SetPixelData( this->StartPosition[0], 0, this->StartPosition[0], size[1]-1, pixels, front ); pixels = this->EndPosXPixels->GetPointer(0); for ( int i = min[0]; i <= max[0]; i++ ) { pixels[3*i] = 255 ^ pixels[3*i]; pixels[3*i+1] = 255 ^ pixels[3*i+1]; pixels[3*i+2] = 255 ^ pixels[3*i+2]; } renWin->SetPixelData( 0, this->EndPosition[1], size[0]-1, this->EndPosition[1], pixels, front ); pixels = this->EndPosYPixels->GetPointer(0); for ( int j = min[1]+1; j < max[1]; j++ ) { pixels[3*j] = 255 ^ pixels[3*j]; pixels[3*j+1] = 255 ^ pixels[3*j+1]; pixels[3*j+2] = 255 ^ pixels[3*j+2]; } renWin->SetPixelData( this->EndPosition[0], 0, this->EndPosition[0], size[1]-1, pixels, front ); } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::ZoomIn() { int width, height; width = abs(this->EndPosition[0] - this->StartPosition[0]); height = abs(this->EndPosition[1] - this->StartPosition[1]); int *size = this->CurrentRenderer->GetSize(); int *origin = this->CurrentRenderer->GetOrigin(); vtkCamera *cam = this->CurrentRenderer->GetActiveCamera(); int min[2]; double rbcenter[3]; min[0] = this->StartPosition[0] < this->EndPosition[0] ? this->StartPosition[0] : this->EndPosition[0]; min[1] = this->StartPosition[1] < this->EndPosition[1] ? this->StartPosition[1] : this->EndPosition[1]; rbcenter[0] = min[0] + 0.5*width; rbcenter[1] = min[1] + 0.5*height; rbcenter[2] = 0; this->CurrentRenderer->SetDisplayPoint(rbcenter); this->CurrentRenderer->DisplayToView(); this->CurrentRenderer->ViewToWorld(); double invw; double worldRBCenter[4]; this->CurrentRenderer->GetWorldPoint(worldRBCenter); invw = 1.0/worldRBCenter[3]; worldRBCenter[0] *= invw; worldRBCenter[1] *= invw; worldRBCenter[2] *= invw; double winCenter[3]; winCenter[0] = origin[0] + 0.5*size[0]; winCenter[1] = origin[1] + 0.5*size[1]; winCenter[2] = 0; this->CurrentRenderer->SetDisplayPoint(winCenter); this->CurrentRenderer->DisplayToView(); this->CurrentRenderer->ViewToWorld(); double worldWinCenter[4]; this->CurrentRenderer->GetWorldPoint(worldWinCenter); invw = 1.0/worldWinCenter[3]; worldWinCenter[0] *= invw; worldWinCenter[1] *= invw; worldWinCenter[2] *= invw; double translation[3]; translation[0] = worldRBCenter[0] - worldWinCenter[0]; translation[1] = worldRBCenter[1] - worldWinCenter[1]; translation[2] = worldRBCenter[2] - worldWinCenter[2]; double pos[3], fp[3]; cam->GetPosition(pos); cam->GetFocalPoint(fp); pos[0] += translation[0]; pos[1] += translation[1]; pos[2] += translation[2]; fp[0] += translation[0]; fp[1] += translation[1]; fp[2] += translation[2]; cam->SetPosition(pos); cam->SetFocalPoint(fp); if (width > height) { cam->Zoom(size[0] / static_cast(width)); } else { cam->Zoom(size[1] / static_cast(height)); } this->Interactor->Render(); } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::ZoomOut() { // Get the size of the rubber band in Display coordinates (x,y pixel edges // relative to the lower-left corner of the render window). const int rbWidth = abs( this->EndPosition[0] - this->StartPosition[0] ) + 1; const int rbHeight = abs( this->EndPosition[1] - this->StartPosition[1] ) + 1; if ( rbWidth == 0 || rbHeight == 0 ) return; // Compute the rectangle containing the visible 3D props in Display // coordinates. Note that these are not pixel edges and therefore the width // and height calculations do not include the extra pixel. double propsDisplayX[2], propsDisplayY[2]; this->ComputeVisiblePropsDisplayRect( propsDisplayX, propsDisplayY ); double propsWidth = propsDisplayX[1] - propsDisplayX[0]; double propsHeight = propsDisplayY[1] - propsDisplayY[0]; // Scale the the visible 3D props to fill either the width or height of the // the rubber band. double scale = static_cast( rbHeight )/propsHeight; if ( scale*propsWidth > rbWidth ) scale = static_cast( rbWidth )/propsWidth; vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); camera->Zoom( scale ); // Get the centre of the rubber band in Display coordinates. double rbCentreX = 0.5*( this->EndPosition[0] + this->StartPosition[0] ); double rbCentreY = 0.5*( this->EndPosition[1] + this->StartPosition[1] ); // Transform this position to World coordinates. Assume that the rubber band // is in the focal plane (not the View plane). double* focalPoint = camera->GetFocalPoint(); double focalPointDisplay[3]; this->ComputeWorldToDisplay( focalPoint[0], focalPoint[1], focalPoint[2], focalPointDisplay ); // Note that the world point is a homogeneous coordinate and must be allocated // as a [4] vector. double rbCentreWorld[4]; this->ComputeDisplayToWorld( rbCentreX, rbCentreY, focalPointDisplay[2], rbCentreWorld ); // Shift the view so that the centre of the visible 3D actors coincides with // the centre of the rubber band. double allBounds[6]; this->CurrentRenderer->ComputeVisiblePropBounds( allBounds ); double propsCentreWorld[3]; propsCentreWorld[0] = ( allBounds[0] + allBounds[1] )/2.0; propsCentreWorld[1] = ( allBounds[2] + allBounds[3] )/2.0; propsCentreWorld[2] = ( allBounds[4] + allBounds[5] )/2.0; // Find the required view shift (the camera shift is the reverse). double shift[3]; shift[0] = rbCentreWorld[0] - propsCentreWorld[0]; shift[1] = rbCentreWorld[1] - propsCentreWorld[1]; shift[2] = rbCentreWorld[2] - propsCentreWorld[2]; this->TranslateView( shift ); this->Interactor->Render(); } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::TranslateView( const double shift[3] ) { if ( NULL != this->CurrentRenderer ) { vtkCamera* camera = this->CurrentRenderer->GetActiveCamera(); // The camera focal point and position are defined in World coordinates // and therefore the shift must be in this coordinate system also. // Note that the camera must be moved in the opposite direction to the // required shift. double* focalPoint = camera->GetFocalPoint(); camera->SetFocalPoint( focalPoint[0] - shift[0], focalPoint[1] - shift[1], focalPoint[2] - shift[2] ); double* position = camera->GetPosition(); camera->SetPosition( position[0] - shift[0], position[1] - shift[1], position[2] - shift[2] ); } } //-------------------------------------------------------------------------- bool vtkInteractorStyleRubberBandZoom1::ComputeVisiblePropsDisplayRect( double rangeX[2], double rangeY[2] ) { // Find the (x,y) bounds of all the visible 3D props in Display coordinates. bool validBounds = false; rangeX[0] = rangeY[0] = VTK_DOUBLE_MAX; rangeX[1] = rangeY[1] = VTK_DOUBLE_MIN; // Compute the bounding box of all the visible 3D props in World coordinates. double allBounds[6]; this->CurrentRenderer->ComputeVisiblePropBounds( allBounds ); // Only proceed if there are visisble 3D props. if ( vtkMath::AreBoundsInitialized( allBounds ) ) { // Find the closest / furthest bounding box vertex. for ( int k = 0; k < 2; k++ ) { for ( int j = 0; j < 2; j++ ) { for ( int i = 0; i < 2; i++ ) { // The View coordinates overwrite the World coordinates and // therfore the copying cannot be done in the outer loops. double x = allBounds[i]; double y = allBounds[2+j]; double z = allBounds[4+k]; double xyzDisplay[3]; this->ComputeWorldToDisplay( x, y, z, xyzDisplay ); if ( xyzDisplay[0] < rangeX[0] ) rangeX[0] = xyzDisplay[0]; if ( xyzDisplay[0] > rangeX[1] ) rangeX[1] = xyzDisplay[0]; if ( xyzDisplay[1] < rangeY[0] ) rangeY[0] = xyzDisplay[1]; if ( xyzDisplay[1] > rangeY[1] ) rangeY[1] = xyzDisplay[1]; } } } validBounds = true; } return validBounds; } //-------------------------------------------------------------------------- void vtkInteractorStyleRubberBandZoom1::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); }