text: /*=========================================================================
  
    Program:   Visualization Toolkit
    Module:    $RCSfile: vtkSphereHandleRepresentation.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 "vtkSphereHandleRepresentation.h"
  #include "vtkSphereSource.h"
  #include "vtkPolyDataMapper.h"
  #include "vtkActor.h"
  #include "vtkCellPicker.h"
  #include "vtkRenderer.h"
  #include "vtkObjectFactory.h"
  #include "vtkProperty.h"
  #include "vtkAssemblyPath.h"
  #include "vtkMath.h"
  #include "vtkInteractorObserver.h"
  #include "vtkLine.h"
  #include "vtkCoordinate.h"
  #include "vtkRenderWindow.h"
  
  vtkCxxRevisionMacro(vtkSphereHandleRepresentation, "$Revision: 1.3 $");
  vtkStandardNewMacro(vtkSphereHandleRepresentation);
  vtkCxxSetObjectMacro(vtkSphereHandleRepresentation,SelectedProperty,vtkProperty);
  
  //----------------------------------------------------------------------
  vtkSphereHandleRepresentation::vtkSphereHandleRepresentation()
  {
    // Initialize state
    this->InteractionState = vtkHandleRepresentation::Outside;
    
    // Represent the line
    this->Sphere = vtkSphereSource::New();
    this->Sphere->SetThetaResolution(16);
    this->Sphere->SetPhiResolution(8);
    
    this->Mapper = vtkPolyDataMapper::New();
    this->Mapper->SetInput(this->Sphere->GetOutput());
  
    // Set up the initial properties
    this->CreateDefaultProperties();
  
    this->Actor = vtkActor::New();
    this->Actor->SetMapper(this->Mapper);
    this->Actor->SetProperty(this->Property);
  
    //Manage the picking stuff
    this->CursorPicker = vtkCellPicker::New();
    this->CursorPicker->PickFromListOn();
    this->CursorPicker->AddPickList(this->Actor);
    this->CursorPicker->SetTolerance(0.01); //need some fluff
  
    // Override superclass'
    this->PlaceFactor = 1.0;
    
    // The size of the hot spot
    this->HotSpotSize = 0.05;
    this->WaitingForMotion = 0;
    this->ConstraintAxis = -1;
    
    // Current handle size
    this->HandleSize = 15.0; //in pixels
    this->CurrentHandleSize = this->HandleSize;
    
    // Translation control
    this->TranslationMode = 1;
  }
  
  //----------------------------------------------------------------------
  vtkSphereHandleRepresentation::~vtkSphereHandleRepresentation()
  {
    this->Sphere->Delete();
    this->CursorPicker->Delete();
    this->Mapper->Delete();
    this->Actor->Delete();
    this->Property->Delete();
    this->SelectedProperty->Delete();
  }
  
  //-------------------------------------------------------------------------
  void vtkSphereHandleRepresentation::PlaceWidget(double bds[6])
  {
    int i;
    double bounds[6], center[3];
  
    this->AdjustBounds(bds, bounds, center);
    
  //  this->Sphere->SetModelBounds(bounds);
    this->SetWorldPosition(center);
  
    for (i=0; i<6; i++)
      {
      this->InitialBounds[i] = bounds[i];
      }
    this->InitialLength = sqrt((bounds[1]-bounds[0])*(bounds[1]-bounds[0]) +
                               (bounds[3]-bounds[2])*(bounds[3]-bounds[2]) +
                               (bounds[5]-bounds[4])*(bounds[5]-bounds[4]));
  }
  
  //-------------------------------------------------------------------------
  void vtkSphereHandleRepresentation::SetSphereRadius(double radius)
  {
    if(radius == this->Sphere->GetRadius())
    {
      return;
    }
  
    this->Sphere->SetRadius(radius);
    this->Modified();
  }
  
  //-------------------------------------------------------------------------
  double vtkSphereHandleRepresentation::GetSphereRadius()
  {
    return this->Sphere->GetRadius();
  }
  
  //-------------------------------------------------------------------------
  double* vtkSphereHandleRepresentation::GetBounds()
  {
    static double bounds[6];
    double center[3];
    double radius = this->Sphere->GetRadius();
    this->Sphere->GetCenter(center);
    
    bounds[0] = this->PlaceFactor*(center[0]+radius);
    bounds[1] = this->PlaceFactor*(center[0]-radius);
    bounds[2] = this->PlaceFactor*(center[1]+radius);
    bounds[3] = this->PlaceFactor*(center[1]-radius);
    bounds[4] = this->PlaceFactor*(center[2]+radius);
    bounds[5] = this->PlaceFactor*(center[2]-radius);
  
    return bounds;
  }
  
  //-------------------------------------------------------------------------
  void vtkSphereHandleRepresentation::SetWorldPosition(double p[3])
  {
    this->Sphere->SetCenter(p); //this may clamp the point
    this->Superclass::SetWorldPosition(this->Sphere->GetCenter());
  }
  
  //-------------------------------------------------------------------------
  void vtkSphereHandleRepresentation::SetDisplayPosition(double p[3])
  {
    this->Superclass::SetDisplayPosition(p);
    this->SetWorldPosition(this->WorldPosition->GetValue());
  }
  
  //-------------------------------------------------------------------------
  void vtkSphereHandleRepresentation::SetHandleSize(double size)
  {
    this->Superclass::SetHandleSize(size);
    this->CurrentHandleSize = this->HandleSize;
  }
  
  //-------------------------------------------------------------------------
  int vtkSphereHandleRepresentation::ComputeInteractionState(int X, int Y, int vtkNotUsed(modify))
  {
    this->VisibilityOn(); //actor must be on to be picked
    this->CursorPicker->Pick(X,Y,0.0,this->Renderer);
    vtkAssemblyPath *path = this->CursorPicker->GetPath();
    if ( path != NULL )
      {
  //    this->InteractionState = vtkHandleRepresentation::Nearby;
        this->InteractionState = vtkHandleRepresentation::Selecting;
      }
    else
      {
      this->InteractionState = vtkHandleRepresentation::Outside;
      if ( this->ActiveRepresentation )
        {
        this->VisibilityOff();
        }
      }
  
    return this->InteractionState;
  }
  
  //-------------------------------------------------------------------------
  int vtkSphereHandleRepresentation::DetermineConstraintAxis(int constraint, double *x)
  {
    // Look for trivial cases
    if ( ! this->Constrained )
      {
      return -1;
      }
    else if ( constraint >= 0 && constraint < 3 )
      {
      return constraint;
      }
    
    // Okay, figure out constraint. First see if the choice is
    // outside the hot spot
    if ( ! this->WaitingForMotion )
      {
      double p[3], d2, tol;
      this->CursorPicker->GetPickPosition(p);
      d2 = vtkMath::Distance2BetweenPoints(p,this->StartEventPosition);
      tol = this->HotSpotSize*this->InitialLength;
      if ( d2 > (tol*tol) )
        {
        this->WaitingForMotion = 0;
        return this->CursorPicker->GetCellId();
        }
      else
        {
        this->WaitingForMotion = 1;
        this->WaitCount = 0;
        return -1;
        }
      }
    else if ( this->WaitingForMotion && x ) 
      {
      double v[3];
      this->WaitingForMotion = 0;
      v[0] = fabs(x[0] - this->StartEventPosition[0]);
      v[1] = fabs(x[1] - this->StartEventPosition[1]);
      v[2] = fabs(x[2] - this->StartEventPosition[2]);
      return ( v[0]>v[1] ? (v[0]>v[2]?0:2) : (v[1]>v[2]?1:2));
      }
    else
      {
      return -1;
      }
  }
  
  //----------------------------------------------------------------------
  // Record the current event position, and the rectilinear wipe position.
  void vtkSphereHandleRepresentation::StartWidgetInteraction(double startEventPos[2])
  {
    this->StartEventPosition[0] = startEventPos[0];
    this->StartEventPosition[1] = startEventPos[1];
    this->StartEventPosition[2] = 0.0;
  
    this->LastEventPosition[0] = startEventPos[0];
    this->LastEventPosition[1] = startEventPos[1];
  
    vtkAssemblyPath *path;
    this->CursorPicker->Pick(startEventPos[0],startEventPos[1],0.0,this->Renderer);
    path = this->CursorPicker->GetPath();
    if ( path != NULL )
      {
  //    this->InteractionState = vtkHandleRepresentation::Nearby;
        this->InteractionState = vtkHandleRepresentation::Selecting;
      this->ConstraintAxis = this->DetermineConstraintAxis(-1,NULL);
      this->CursorPicker->GetPickPosition(this->LastPickPosition);
      }
    else
      {
      this->InteractionState = vtkHandleRepresentation::Outside;
      this->ConstraintAxis = -1;
      }
  }
  
  
  //----------------------------------------------------------------------
  // Based on the displacement vector (computed in display coordinates) and
  // the cursor state (which corresponds to which part of the widget has been
  // selected), the widget points are modified.
  // First construct a local coordinate system based on the display coordinates
  // of the widget.
  void vtkSphereHandleRepresentation::WidgetInteraction(double eventPos[2])
  {
    // Do different things depending on state
    // Calculations everybody does
    double focalPoint[4], pickPoint[4], prevPickPoint[4];
    double z;
  
    // Compute the two points defining the motion vector
    vtkInteractorObserver::ComputeWorldToDisplay(this->Renderer,
                                                 this->LastPickPosition[0], 
                                                 this->LastPickPosition[1],
                                                 this->LastPickPosition[2], focalPoint);
    z = focalPoint[2];
    vtkInteractorObserver::ComputeDisplayToWorld(this->Renderer, this->LastEventPosition[0], 
                                                 this->LastEventPosition[1], z, prevPickPoint);
    vtkInteractorObserver::ComputeDisplayToWorld(this->Renderer, eventPos[0], eventPos[1], z, pickPoint);
  
    // Process the motion
    if ( this->InteractionState == vtkHandleRepresentation::Selecting ||
         this->InteractionState == vtkHandleRepresentation::Translating )
      {
      if ( !this->WaitingForMotion || this->WaitCount++ > 3 )
        {
        this->ConstraintAxis = 
          this->DetermineConstraintAxis(this->ConstraintAxis,pickPoint);
        if ( this->InteractionState == vtkHandleRepresentation::Selecting && !this->TranslationMode )
          {
          this->MoveFocus(prevPickPoint, pickPoint);
          }
        else
          {
          this->Translate(prevPickPoint, pickPoint);
          }
        }
      }
  
    else if ( this->InteractionState == vtkHandleRepresentation::Scaling )
      {
      this->Scale(prevPickPoint, pickPoint, eventPos);
      }
  
    // Book keeping
    this->LastEventPosition[0] = eventPos[0];
    this->LastEventPosition[1] = eventPos[1];
  
    this->Modified();
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::MoveFocus(double *p1, double *p2)
  {
    //Get the motion vector
    double v[3];
    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];
  
    double focus[3];
    this->Sphere->GetCenter(focus);
    if ( this->ConstraintAxis >= 0 )
      {
      focus[this->ConstraintAxis] += v[this->ConstraintAxis];
      }
    else
      {
      focus[0] += v[0];
      focus[1] += v[1];
      focus[2] += v[2];
      }
    
    this->SetWorldPosition(focus);
  }
  
  //----------------------------------------------------------------------
  // Translate everything
  void vtkSphereHandleRepresentation::Translate(double *p1, double *p2)
  {
    //Get the motion vector
    double v[3];
    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];
    
    double *pos = this->Sphere->GetCenter();
    double newFocus[3];
  
    if ( this->ConstraintAxis >= 0 )
      {//move along axis
      for (int i=0; i<3; i++)
        {
        if ( i != this->ConstraintAxis )
          {
          v[i] = 0.0;
          }
        }
      }
    
    for (int i=0; i<3; i++)
      {
      newFocus[i] = pos[i] + v[i];
      }
    this->SetWorldPosition(newFocus);
  
    double radius = this->SizeHandlesInPixels(1.0,newFocus);
    radius *= this->CurrentHandleSize / this->HandleSize;
  
    this->Sphere->SetRadius(radius);
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::SizeBounds()
  {
    double center[3];
    this->Sphere->GetCenter(center);
    double radius = this->SizeHandlesInPixels(1.0,center);
    radius *= this->CurrentHandleSize / this->HandleSize;
  
    this->Sphere->SetRadius(radius);
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::Scale(double *p1, double *p2, double eventPos[2])
  {
    //Get the motion vector
    double v[3];
    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];
  
    double *bounds = this->GetBounds();
  
    // Compute the scale factor
    double sf = vtkMath::Norm(v) / 
      sqrt( (bounds[1]-bounds[0])*(bounds[1]-bounds[0]) +
            (bounds[3]-bounds[2])*(bounds[3]-bounds[2]) +
            (bounds[5]-bounds[4])*(bounds[5]-bounds[4]));
  
    if ( eventPos[1] > this->LastEventPosition[1] )
      {
      sf = 1.0 + sf;
      }
    else
      {
      sf = 1.0 - sf;
      }
    
    this->CurrentHandleSize *= sf;
    this->CurrentHandleSize = (this->CurrentHandleSize < 0.001 ? 0.001 : this->CurrentHandleSize);
    
    this->SizeBounds();
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::Highlight(int highlight)
  {
    if ( highlight )
      {
      this->Actor->SetProperty(this->SelectedProperty);
      }
    else
      {
      this->Actor->SetProperty(this->Property);
      }
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::CreateDefaultProperties()
  {
    this->Property = vtkProperty::New();
    this->Property->SetColor(1, 1, 1);
  
    this->SelectedProperty = vtkProperty::New();
    this->SelectedProperty->SetColor(0, 1, 0);
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::BuildRepresentation()
  {
    // The net effect is to resize the handle
    if ( this->GetMTime() > this->BuildTime || 
         (this->Renderer && this->Renderer->GetVTKWindow() &&
          this->Renderer->GetVTKWindow()->GetMTime() > this->BuildTime) )
      {
      if ( ! this->Placed )
        {
        this->ValidPick = 1;
        this->Placed = 1;
        }
  
      this->SizeBounds();
      this->Sphere->Update();
      this->BuildTime.Modified();
      }
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::ShallowCopy(vtkProp *prop)
  {
    vtkSphereHandleRepresentation *rep = 
      vtkSphereHandleRepresentation::SafeDownCast(prop);
    if ( rep )
      {
      this->SetTranslationMode(rep->GetTranslationMode());
      this->SetProperty(rep->GetProperty());
      this->SetSelectedProperty(rep->GetSelectedProperty());
      this->SetHotSpotSize(rep->GetHotSpotSize());
      }
    this->Superclass::ShallowCopy(prop);
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::GetActors(vtkPropCollection *pc)
  {
    this->Actor->GetActors(pc);
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::ReleaseGraphicsResources(vtkWindow *win)
  {
    this->Actor->ReleaseGraphicsResources(win);
  }
  
  //----------------------------------------------------------------------
  int vtkSphereHandleRepresentation::RenderOpaqueGeometry(vtkViewport *viewport)
  {
    this->BuildRepresentation();
    return this->Actor->RenderOpaqueGeometry(viewport);
  }
  
  //----------------------------------------------------------------------
  //int vtkSphereHandleRepresentation::RenderTranslucentGeometry(vtkViewport *viewport)
  //{
  //  this->BuildRepresentation();
  //  return this->Actor->RenderTranslucentGeometry(viewport);
  //}
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::SetProperty(vtkProperty * p)
  {
    vtkSetObjectBodyMacro(Property, vtkProperty, p);
    if (p) 
      { 
      this->Actor->SetProperty( p ); 
      }
  }
  
  //----------------------------------------------------------------------
  void vtkSphereHandleRepresentation::PrintSelf(ostream& os, vtkIndent indent)
  {
    //Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
    this->Superclass::PrintSelf(os,indent);
    
    os << indent << "Hot Spot Size: " << this->HotSpotSize << "\n";
    if ( this->Property )
      {
      os << indent << "Property: " << this->Property << "\n";
      }
    else
      {
      os << indent << "Property: (none)\n";
      }
    if ( this->SelectedProperty )
      {
      os << indent << "Selected Property: " << this->SelectedProperty << "\n";
      }
    else
      {
      os << indent << "Selected Property: (none)\n";
      }
  
    os << indent << "Translation Mode: " << (this->TranslationMode ? "On\n" : "Off\n");
    os << indent << "Sphere: " << this->Sphere << "\n";
  
    this->Sphere->PrintSelf(os,indent.GetNextIndent());
  }
  