[vtkusers] point picking on vtkImageActors

Dean Inglis dean.inglis at camris.ca
Tue Aug 8 12:49:33 EDT 2006


Hi Rashed,

I wrote a widget to do this type of
thing with vtkImageActor: I call it
vtkImageCoordinateWidget, which basically
combines the picking functionality of vtkImageTracerWidget
and the cursoring functionality of vtkImagePlaneWidget
into one simple widget.  Unfortunately, it
is not part of VTK/Widgets as the widgets are
currently undergoing a revision in how they
are designed and this widget is written in
the "old" widget design paradigm.  You could
still compile it by adding it to your own
external build, as shown in VTK/Examples/Build/vtkMy.
See below for the .h and .cxx files.  I am not
supporting this code publicly or privately, so
please rememeber: yer on yer own!


Dean

////////////////////////// vtkImageCoordinateWidget.h file
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkImageCoordinateWidget.h,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.

=========================================================================*/
// .NAME vtkImageCoordinateWidget - 3D widget for probing image data
// .SECTION Description
// Choose between voxel centered or continuous cursor probing by clicking
// left or right mouse buttons respectively.  With voxel
// centered probing, the cursor snaps to the nearest voxel and the reported
// cursor coordinates are extent based.  With continuous probing, voxel data
// is interpolated using vtkDataSetAttributes' InterpolatePoint method and
// the reported coordinates are 3D spatial continuous.

// .SECTION Caveats
// Note that handles and plane can be picked even when they are "behind"
other
// actors.  This is an intended feature and not a bug.  The probe only
// works on single component image data for now.

// .SECTION See Also
// vtk3DWidget vtkBoxWidget vtkLineWidget  vtkPlaneWidget vtkPointWidget
// vtkPolyDataSourceWidget vtkSphereWidget vtkImplicitPlaneWidget

// .SECTION Thanks
// This class was developed by Dean Inglis

#ifndef __vtkImageCoordinateWidget_h
#define __vtkImageCoordinateWidget_h

#include "vtkLocalHybridWin32Header.h" // Include configuration header.
// note that you should use the build structure in /VTK/EXAMPLES/BUILD/vtkMy
// and NOT vtkLocal, I just call my build vtkLocal because I don't like
"vtkMy"
// See the readme documentation in /VTK/EXAMPLES/BUILD/vtkMy
// You may also have to rename VTK_LOCAL_HYBRID_EXPORT depending on your
// specific naming convention.

#include "vtk3DWidget.h"

class vtkActor;
class vtkImageData;
class vtkPolyData;
class vtkProp;
class vtkProperty;
class vtkPropPicker;
class vtkTextActor;
class vtkTextProperty;

class VTK_LOCAL_HYBRID_EXPORT vtkImageCoordinateWidget : public vtk3DWidget
{
public:
  // Description:
  // Instantiate the object.
  static vtkImageCoordinateWidget *New();

  vtkTypeRevisionMacro(vtkImageCoordinateWidget,vtk3DWidget);
  void PrintSelf(ostream& os, vtkIndent indent);

  // Description:
  // Methods that satisfy the superclass' API.
  virtual void SetEnabled(int);
  virtual void PlaceWidget(double bounds[6]){}; // do nothing
  void PlaceWidget(){};
  void PlaceWidget(double xmin, double xmax, double ymin, double ymax,
                   double zmin, double zmax){};

  // Description:
  // Set the prop, a vtkImageActor, to trace over.
  void SetProp(vtkProp* prop);

  // Description:
  // Set the vtkImageData* input for probing.  Usually, since vtkImageActor
  // takes vtkImageData scaled and shifted vtkImageData to unsigned char or
  // unsigned short, we may want to probe the original data which could be
  // float, short, int etc.
  void SetInput(vtkDataSet* input);

  // Description:
  // Enable/disable text display of window-level, image coords and values in
a
  // render window.
  vtkSetMacro(DisplayText,int);
  vtkGetMacro(DisplayText,int);
  vtkBooleanMacro(DisplayText,int);

  // Description:
  // Set/Get the line properties. The properties of the cross hair lines can
  // be manipulated.
  virtual void SetLineProperty(vtkProperty*);
  vtkGetObjectMacro(LineProperty, vtkProperty);

  // Description:
  // Set/Get the text property for the image data annotation.
  virtual void SetTextProperty(vtkTextProperty*);
  vtkGetObjectMacro(TextProperty, vtkTextProperty);

  // Description:
  // Get the image coordinate position and voxel value.  Currently only
  // supports single component image data.
  int GetCursorData(double xyzv[4]);

  // Description:
  // turn on/off display of cross hair lines.
  vtkSetMacro(DisplayLines,int);
  vtkGetMacro(DisplayLines,int);
  vtkBooleanMacro(DisplayLines,int);


protected:
  vtkImageCoordinateWidget();
  ~vtkImageCoordinateWidget();

  //BTX - manage the state of the widget
  int State;
  enum WidgetState
  {
    Start=0,
    Cursoring,
    Outside
  };
  //ETX

  // Handles the events
  static void ProcessEvents(vtkObject* object,
                            unsigned long event,
                            void* clientdata,
                            void* calldata);

  // internal utility method that adds observers to the
RenderWindowInteractor
  // so that our ProcessEvents is eventually called.  this method is called
  // by SetEnabled as well as SetInteraction
  void AddObservers();

  // ProcessEvents() dispatches to these methods.
  virtual void OnMouseMove();
  virtual void OnButtonDown( unsigned long& event );
  virtual void OnButtonUp( );

  // controlling ivars
  int   DisplayText;
  int   DisplayLines;

  // The cross-hair cursor
  vtkPolyData *LineData;
  vtkActor    *LineActor;
  vtkProperty *LineProperty;

  double      CurrentCursorPosition[3];
  double      CurrentImageValue; // Set to VTK_DOUBLE_MAX when invalid
  void        UpdateCursor(int,int);
  void        ActivateCursor(int);
  int         UpdateContinuousCursor(double*);
  int         UpdateDiscreteCursor(double*);
  int         UseContinuousCursor;

  // The text to display image data
  vtkTextActor    *TextActor;
  vtkTextProperty *TextProperty;

  char          TextBuff[128];
  void          ManageTextDisplay();
  void          ActivateText(int);

  vtkProp       *Prop;        // the prop we want to pick on
  vtkPropPicker *PropPicker;  // the prop's picker
  vtkImageData  *ImageData; // the data of the input prop, a vtkImageActor


  // Properties used to control the appearance of selected objects and
  // the manipulator in general.

  void CreateDefaultProperties();

private:
  vtkImageCoordinateWidget(const vtkImageCoordinateWidget&);  //Not
implemented
  void operator=(const vtkImageCoordinateWidget&);  //Not implemented
};

#endif
//////////////////////////

////////////////////////// vtkImageCoordinateWidget.cxx file
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    $RCSfile: vtkImageCoordinateWidget.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 "vtkImageCoordinateWidget.h"

#include "vtkActor.h"
#include "vtkAssemblyNode.h"
#include "vtkAssemblyPath.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCellArray.h"
#include "vtkPropPicker.h"
#include "vtkImageActor.h"
#include "vtkImageData.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTextActor.h"
#include "vtkTextProperty.h"

vtkCxxRevisionMacro(vtkImageCoordinateWidget, "$Revision: 1.1 $");
vtkStandardNewMacro(vtkImageCoordinateWidget);

vtkCxxSetObjectMacro(vtkImageCoordinateWidget, LineProperty, vtkProperty);
vtkCxxSetObjectMacro(vtkImageCoordinateWidget, TextProperty,
vtkTextProperty);

vtkImageCoordinateWidget::vtkImageCoordinateWidget() : vtk3DWidget()
{
  this->State = vtkImageCoordinateWidget::Start;
  this->EventCallbackCommand->SetCallback(
vtkImageCoordinateWidget::ProcessEvents );

  this->PlaceFactor              = 1.0;
  this->DisplayText              = 1;
  this->DisplayLines             = 1;

  this->CurrentCursorPosition[0] = 0;
  this->CurrentCursorPosition[1] = 0;
  this->CurrentCursorPosition[2] = 0;
  this->CurrentImageValue        = VTK_DOUBLE_MAX;

  this->Prop = NULL;
  this->ImageData = NULL;
  this->LineProperty = NULL;
  this->TextProperty = NULL;

  this->PropPicker = vtkPropPicker::New();
  this->PropPicker->PickFromListOn();

  // Represent the text: annotation for cursor position
  //
  this->TextActor = vtkTextActor::New();

  // Represent the cross hair cursor
  //

  vtkPoints* points = vtkPoints::New( VTK_DOUBLE );
  points->SetNumberOfPoints( 4 );
  for ( int i = 0; i < 4; ++i )
    {
    points->SetPoint( i, 0.0, 0.0, 0.0 );
    }

  vtkCellArray *cells = vtkCellArray::New();
  cells->Allocate( cells->EstimateSize( 2, 2 ) );
  vtkIdType pts[2];
  pts[0] = 0; pts[1] = 1;       // horizontal segment
  cells->InsertNextCell( 2, pts );
  pts[0] = 2; pts[1] = 3;       // vertical segment
  cells->InsertNextCell( 2, pts );

  this->LineData = vtkPolyData::New();
  this->LineData->SetPoints( points );
  points->Delete();
  this->LineData->SetLines( cells );
  cells->Delete();

  vtkPolyDataMapper* mapper   = vtkPolyDataMapper::New();
  mapper->SetInput( this->LineData );
  mapper->SetResolveCoincidentTopologyToPolygonOffset();

  this->LineActor = vtkActor::New();
  this->LineActor->SetMapper( mapper );
  mapper->Delete();

  this->LineActor->PickableOff();
  this->LineActor->VisibilityOff();

  sprintf( this->TextBuff, "NA" );
  this->TextActor->SetInput( this->TextBuff );
  this->TextActor->ScaledTextOff();

  vtkCoordinate* coord = this->TextActor->GetPositionCoordinate();
  coord->SetCoordinateSystemToNormalizedViewport();
  coord->SetValue( 0.05, 0.05 );

  this->TextActor->VisibilityOff();

  // Set up the initial properties
  //
  this->CreateDefaultProperties();
}

vtkImageCoordinateWidget::~vtkImageCoordinateWidget()
{
  if ( this->LineProperty )
    {
    this->LineProperty->Delete();
    }
  if ( this->TextProperty )
    {
    this->TextProperty->Delete();
    }
  if ( this->Prop )
    {
    this->Prop->UnRegister( this );
    this->Prop = NULL;
    }

  if ( this->ImageData )
    {
    this->ImageData = NULL;
    }

  this->LineActor->Delete();
  this->LineData->Delete();
  this->TextActor->Delete();
  this->PropPicker->Delete();
}

void vtkImageCoordinateWidget::SetProp(vtkProp* prop)
{
  if ( !prop->IsA( "vtkImageActor" ) )
    {
    vtkErrorMacro(<<"Error: input prop must be a vtkImageActor" );
    return;
    }
  if ( this->Prop != prop )
    {
    // Avoid destructor recursion
    vtkProp *temp = this->Prop;
    this->Prop = prop;
    if ( temp )
      {
      temp->UnRegister( this );
      }
    if ( this->Prop )
      {
      this->Prop->Register( this );
      this->PropPicker->InitializePickList();
      this->PropPicker->AddPickList( this->Prop );
      this->Prop->PickableOn();
      }
    }
}

void vtkImageCoordinateWidget::SetInput(vtkDataSet* input)
{
  this->ImageData = vtkImageData::SafeDownCast( input );
}

void vtkImageCoordinateWidget::SetEnabled(int enabling)
{

  if ( !this->Interactor )
    {
    vtkErrorMacro(<<"The interactor must be set prior to enabling/disabling
widget");
    return;
    }

  if ( enabling )
//----------------------------------------------------------
    {
    vtkDebugMacro(<<"Enabling widget");

    if ( this->Enabled ) //already enabled, just return
      {
      return;
      }

    if ( !this->CurrentRenderer )
      {
      this->SetCurrentRenderer( this->Interactor->FindPokedRenderer(
        this->Interactor->GetLastEventPosition()[0],
        this->Interactor->GetLastEventPosition()[1]) );
      if ( this->CurrentRenderer == NULL )
        {
        return;
        }
      }

    this->Enabled = 1;

    // we have to honour this ivar: it could be that this->Interaction was
    // set to off when we were disabled

    this->AddObservers();

    // Add the cross-hair cursor
    this->CurrentRenderer->AddProp( this->LineActor );
    this->LineActor->SetProperty( this->LineProperty );

    // Add the image data annotation
    this->CurrentRenderer->AddProp( this->TextActor );
    this->TextActor->SetTextProperty( this->TextProperty );

    this->InvokeEvent( vtkCommand::EnableEvent, NULL );
    }

  else //disabling----------------------------------------------------------
    {
    vtkDebugMacro(<<"Disabling plane widget");

    if ( ! this->Enabled ) //already disabled, just return
      {
      return;
      }

    this->Enabled = 0;

    // don't listen for events any more
    this->Interactor->RemoveObserver( this->EventCallbackCommand );

    //turn off the cursor
    this->CurrentRenderer->RemoveProp( this->LineActor );

    //turn off the image data annotation
    this->CurrentRenderer->RemoveProp( this->TextActor );

    this->InvokeEvent( vtkCommand::DisableEvent, NULL );
    this->SetCurrentRenderer( NULL );
    }

  this->Interactor->Render();
}

void vtkImageCoordinateWidget::ProcessEvents(vtkObject* vtkNotUsed(object),
                                        unsigned long event,
                                        void* clientdata,
                                        void* vtkNotUsed(calldata))
{
  vtkImageCoordinateWidget* self =
    reinterpret_cast<vtkImageCoordinateWidget *>( clientdata );

  //okay, let's do the right thing
  switch ( event )
    {
    case vtkCommand::LeftButtonPressEvent:
      self->OnButtonDown( event);
      break;
    case vtkCommand::LeftButtonReleaseEvent:
      self->OnButtonUp();
      break;
    case vtkCommand::RightButtonPressEvent:
      self->OnButtonDown( event );
      break;
    case vtkCommand::RightButtonReleaseEvent:
      self->OnButtonUp();
      break;
    case vtkCommand::MouseMoveEvent:
      self->OnMouseMove();
      break;
    }
}

void vtkImageCoordinateWidget::AddObservers(void)
{
    // listen for the following events
  vtkRenderWindowInteractor *i = this->Interactor;
  if (i)
    {
    i->AddObserver( vtkCommand::MouseMoveEvent, this->EventCallbackCommand,
                   this->Priority );
    i->AddObserver( vtkCommand::LeftButtonPressEvent,
                   this->EventCallbackCommand, this->Priority );
    i->AddObserver( vtkCommand::LeftButtonReleaseEvent,
                   this->EventCallbackCommand, this->Priority );
    i->AddObserver( vtkCommand::RightButtonPressEvent,
                   this->EventCallbackCommand, this->Priority );
    i->AddObserver( vtkCommand::RightButtonReleaseEvent,
                   this->EventCallbackCommand, this->Priority );
    }
}

void vtkImageCoordinateWidget::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

}

void vtkImageCoordinateWidget::OnButtonDown( unsigned long& event )
{
  int X = this->Interactor->GetEventPosition()[0];
  int Y = this->Interactor->GetEventPosition()[1];

  // Okay, make sure that the pick is in the current renderer
  if ( !this->CurrentRenderer || !this->CurrentRenderer->IsInViewport( X,
Y ) )
    {
    this->State = vtkImageCoordinateWidget::Outside;
    return;
    }

  // Okay, we can process this. If anything is picked, then we
  // can start cursoring.
  this->PropPicker->Pick( X, Y, 0.0, this->CurrentRenderer );
  vtkAssemblyPath* path = this->PropPicker->GetPath();

  int found = 0;
  int i;
  if ( path != NULL )
    {
    // Deal with the possibility that we may be using a shared picker
    vtkCollectionSimpleIterator sit;
    path->InitTraversal(sit);
    vtkAssemblyNode *node;
    for ( i = 0; i < path->GetNumberOfItems() && !found ; ++i )
      {
      node = path->GetNextNode( sit );
      if ( node->GetProp() == this->Prop )
        {
        found = 1;
        }
      }
    }

  if( found )
    {
    this->State = vtkImageCoordinateWidget::Cursoring;
    this->UseContinuousCursor = ( event ==
vtkCommand::LeftButtonPressEvent ) ? 0 : 1;
    this->ActivateCursor(1);
    this->ActivateText(1);
    this->UpdateCursor(X,Y);
    this->ManageTextDisplay();
    }
  else
    {
    this->State = vtkImageCoordinateWidget::Outside;
    this->ActivateCursor(0);
    this->ActivateText(0);
    return;
    }

  this->EventCallbackCommand->SetAbortFlag(1);
  this->StartInteraction();
  this->InvokeEvent( vtkCommand::StartInteractionEvent, NULL );
  this->Interactor->Render();
}

void vtkImageCoordinateWidget::OnButtonUp()
{
  if ( this->State == vtkImageCoordinateWidget::Outside ||
       this->State == vtkImageCoordinateWidget::Start )
    {
    return;
    }

  this->State = vtkImageCoordinateWidget::Start;
  this->ActivateCursor(0);
  this->ActivateText(0);

  this->EventCallbackCommand->SetAbortFlag(1);
  this->EndInteraction();
  this->InvokeEvent( vtkCommand::EndInteractionEvent, NULL );
  this->Interactor->Render();
}

void vtkImageCoordinateWidget::OnMouseMove()
{
  // See whether we're active
  //
  if ( this->State == vtkImageCoordinateWidget::Outside ||
       this->State == vtkImageCoordinateWidget::Start )
    {
    // if not try to get started
    return;
    }

  int X = this->Interactor->GetEventPosition()[0];
  int Y = this->Interactor->GetEventPosition()[1];

  if ( this->State == vtkImageCoordinateWidget::Cursoring )
    {
    this->UpdateCursor( X, Y );
    this->ManageTextDisplay();
    }

  // Interact, if desired
  //
  this->EventCallbackCommand->SetAbortFlag( 1 );
  this->InvokeEvent( vtkCommand::InteractionEvent, NULL );
  this->Interactor->Render();
}

int vtkImageCoordinateWidget::GetCursorData( double xyzv[4] )
{
  if ( this->State != vtkImageCoordinateWidget::Cursoring  || \
    this->CurrentImageValue == VTK_DOUBLE_MAX )
    {
    return 0;
    }

  xyzv[0] = this->CurrentCursorPosition[0];
  xyzv[1] = this->CurrentCursorPosition[1];
  xyzv[2] = this->CurrentCursorPosition[2];
  xyzv[3] = this->CurrentImageValue;

  return 1;
}

void vtkImageCoordinateWidget::ManageTextDisplay()
{
  if ( !this->DisplayText )
    {
    return;
    }

  if( this->CurrentImageValue == VTK_DOUBLE_MAX )
    {
    sprintf( this->TextBuff, "( %g, %g, %g ) Off
Image",this->CurrentCursorPosition[0],
               this->CurrentCursorPosition[1],
               this->CurrentCursorPosition[2] );
    }
  else
    {
    sprintf( this->TextBuff,"( %g, %g, %g ): %g",
               this->CurrentCursorPosition[0],
               this->CurrentCursorPosition[1],
               this->CurrentCursorPosition[2], this->CurrentImageValue );
    }

  this->TextActor->SetInput( this->TextBuff );
  this->TextActor->Modified();
}

void vtkImageCoordinateWidget::CreateDefaultProperties()
{
  if ( !this->LineProperty )
    {
    this->LineProperty = vtkProperty::New();
    this->LineProperty->SetAmbient( 1 );
    this->LineProperty->SetDiffuse( 0 );
    this->LineProperty->SetColor( 0, 1, 0 );
    this->LineProperty->SetLineWidth( 2 );
    this->LineProperty->SetRepresentationToWireframe();
    this->LineProperty->SetInterpolationToFlat();
    }
  if ( !this->TextProperty )
    {
    this->TextProperty = vtkTextProperty::New();
    this->TextProperty->SetColor( 1, 1, 1 );
    this->TextProperty->SetFontFamilyToArial();
    this->TextProperty->SetFontSize( 18 );
    this->TextProperty->BoldOff();
    this->TextProperty->ItalicOff();
    this->TextProperty->ShadowOff();
    this->TextProperty->SetJustificationToLeft();
    this->TextProperty->SetVerticalJustificationToBottom();
    }
}

void vtkImageCoordinateWidget::ActivateCursor(int i)
{

  if( !this->CurrentRenderer ||  !this->DisplayLines )
    {
    return;
    }

  if( i == 0 )
    {
    this->LineActor->VisibilityOff();
    }
  else
    {
    this->LineActor->VisibilityOn();
    }
}

void vtkImageCoordinateWidget::ActivateText(int i)
{
  if( !this->CurrentRenderer || !this->DisplayText )
    {
    return;
    }

  if( i == 0 )
    {
    this->TextActor->VisibilityOff();
    }
  else
    {
    this->TextActor->VisibilityOn();
    }
}

void vtkImageCoordinateWidget::UpdateCursor( int X, int Y )
{
  this->CurrentImageValue = VTK_DOUBLE_MAX;

  if ( !this->Prop ){ return; }
  vtkImageActor* actor = vtkImageActor::SafeDownCast( this->Prop );
  if ( !this->ImageData ) // try to use the actor's image data
    {
    this->ImageData = actor->GetInput();
    if(!this->ImageData){ return;}
    }

  // We're going to be extracting values with GetScalarComponentAsDouble(),
  // we might as well make sure that the data is there.  If the data is
  // up to date already, this call doesn't cost very much.  If we don't make
  // this call and the data is not up to date, the GetScalar... call will
  // cause a segfault.
  this->ImageData->Update();

  this->PropPicker->Pick( X, Y, 0.0, this->CurrentRenderer );
  vtkAssemblyPath* path = this->PropPicker->GetPath();

  int found = 0;
  int i;
  if ( path  )
    {
    // Deal with the possibility that we may be using a shared picker
    vtkCollectionSimpleIterator sit;
    path->InitTraversal( sit );
    vtkAssemblyNode *node;
    for ( i = 0; i< path->GetNumberOfItems() && !found ; ++i )
      {
      node = path->GetNextNode(sit);
      if ( node->GetProp() == this->Prop )
        {
        found = 1;
        }
      }
    }

  if( found )
    {
    if ( this->DisplayLines ) { this->LineActor->VisibilityOn(); }
    }
  else
    {
    if ( this->DisplayLines ) { this->LineActor->VisibilityOff(); }
    return;
    }

  double q[3];
  this->PropPicker->GetPickPosition( q );

  double bounds[6];
  actor->GetBounds( bounds );

  double p1[3];
  double p2[3];

  if ( bounds[0] == bounds[1] )      //YZ
    {
    p1[0] = bounds[1];
    p1[1] = bounds[3];
    p1[2] = bounds[4];
    p2[0] = bounds[1];
    p2[1] = bounds[2];
    p2[2] = bounds[5];
    q[0] = bounds[0];   // fixes some numerical problems with continuous
cursoring
    }
  else if ( bounds[2] == bounds[3] ) //XZ
    {
    p1[0] = bounds[1];
    p1[1] = bounds[3];
    p1[2] = bounds[4];
    p2[0] = bounds[0];
    p2[1] = bounds[3];
    p2[2] = bounds[5];
    q[1] = bounds[2];
    }
  else if ( bounds[4] == bounds[5] ) //XY
    {
    p1[0] = bounds[1];
    p1[1] = bounds[2];
    p1[2] = bounds[4];
    p2[0] = bounds[0];
    p2[1] = bounds[3];
    p2[2] = bounds[5];
    q[2] = bounds[4];
    }

  if ( this->UseContinuousCursor )
    {
    found = this->UpdateContinuousCursor( q );
    }
  else
    {
    found = this->UpdateDiscreteCursor( q );
    }

  if ( !found )
    {
    if ( this->DisplayLines ) { this->LineActor->VisibilityOff(); }
    return;
    }

  // update the cursor line geometry

  double o[3] = { bounds[0], bounds[2], bounds[4] };
  // q relative to the plane origin
  //
  double qro[3];
  qro[0]= q[0] - o[0];
  qro[1]= q[1] - o[1];
  qro[2]= q[2] - o[2];

  double p1o[3] = { p1[0] - o[0], p1[1] - o[1], p1[2] - o[2] };
  double p2o[3] = { p2[0] - o[0], p2[1] - o[1], p2[2] - o[2] };

  double Lp1 = vtkMath::Dot( qro, p1o )/vtkMath::Dot( p1o, p1o );
  double Lp2 = vtkMath::Dot( qro, p2o )/vtkMath::Dot( p2o, p2o );

  double a[3];
  double b[3];
  double c[3];
  double d[3];

  for ( i = 0; i < 3; ++i )
    {
    a[i] = o[i]  + Lp2*p2o[i];   // left
    b[i] = p1[i] + Lp2*p2o[i];   // right
    c[i] = o[i]  + Lp1*p1o[i];   // bottom
    d[i] = p2[i] + Lp1*p1o[i];   // top
    }

  vtkPoints* pts = this->LineData->GetPoints();

  pts->SetPoint( 0, a );
  pts->SetPoint( 1, b );
  pts->SetPoint( 2, c );
  pts->SetPoint( 3, d );

  this->LineData->Modified();
}

int vtkImageCoordinateWidget::UpdateContinuousCursor(double *q)
{
  double tol2;
  vtkCell *cell;
  vtkPointData *pd;
  int subId;
  double pcoords[3], weights[8];

  this->CurrentCursorPosition[0] = q[0];
  this->CurrentCursorPosition[1] = q[1];
  this->CurrentCursorPosition[2] = q[2];

  pd = this->ImageData->GetPointData();
  if ( !pd )
    {
    vtkErrorMacro(<<"no image point data to query!");
    return 0;
    }

  vtkPointData* outPD = vtkPointData::New();
  outPD->InterpolateAllocate( pd, 1, 1 );

  // Use tolerance as a function of size of source data
  //
  tol2 = this->ImageData->GetLength();
  tol2 = tol2 ? tol2*tol2 / 1000.0 : 0.001;

  // Find the cell that contains q and get it
  //
  cell = this->ImageData->FindAndGetCell( q, NULL, -1, tol2, subId, pcoords,
weights );
  int found = 0;
  if ( cell )
    {
    // Interpolate the point data
    //
    outPD->InterpolatePoint( pd, 0, cell->PointIds, weights );
    this->CurrentImageValue = outPD->GetScalars()->GetTuple1( 0 );
    found = 1;
    }

  outPD->Delete();
  return found;
}

int vtkImageCoordinateWidget::UpdateDiscreteCursor(double *q)
{
  // vtkImageData will find the nearest implicit point to q
  //
  vtkIdType ptId = this->ImageData->FindPoint( q );

  if ( ptId == -1 )
    {
    return 0;
    }

  double closestPt[3];
  this->ImageData->GetPoint( ptId, closestPt );

  double origin[3];
  this->ImageData->GetOrigin( origin );
  double spacing[3];
  this->ImageData->GetSpacing( spacing );
  int extent[6];
  this->ImageData->GetExtent( extent) ;

  int iq[3];
  int iqtemp;
  for ( int i = 0; i < 3; ++i )
    {
  // compute world to image coords
    iqtemp = vtkMath::Round( ( closestPt[i] - origin[i] ) / spacing[i] );

  // we have a valid pick already, just enforce bounds check
    iq[i] = ( iqtemp < extent[2*i] ) ? extent[2*i] : \
          ( ( iqtemp > extent[2*i+1] ) ? extent[2*i+1] : iqtemp );

  // compute image to world coords
    q[i] = iq[i]*spacing[i] + origin[i];

    this->CurrentCursorPosition[i] = iq[i];
    }

  this->CurrentImageValue = this->ImageData->GetScalarComponentAsDouble( \
                   static_cast<int>( this->CurrentCursorPosition[0] ),
                   static_cast<int>( this->CurrentCursorPosition[1] ),
                   static_cast<int>( this->CurrentCursorPosition[2] ),0 );
  return 1;
}






More information about the vtkusers mailing list