text: /*=========================================================================
  
   medInria
  
   Copyright (c) INRIA 2013 - 2018. All rights reserved.
   See LICENSE.txt for details.
  
    This software is distributed WITHOUT ANY WARRANTY; without even
    the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
    PURPOSE.
  
  =========================================================================*/
  
  #include "vtkAxes2DWidget.h"
  #include "vtkPointHandleRepresentation2D.h"
  #include "vtkCommand.h"
  #include "vtkCallbackCommand.h"
  #include "vtkRenderWindowInteractor.h"
  #include "vtkObjectFactory.h"
  #include "vtkRenderer.h"
  #include "vtkRenderWindow.h"
  #include "vtkWidgetEventTranslator.h"
  #include "vtkWidgetCallbackMapper.h"
  #include "vtkEvent.h"
  #include "vtkWidgetEvent.h"
  #include "vtkCursor2D.h"
  #include "vtkProperty2D.h"
  #include <vtkCommand.h>
  #include <vtkImageView2D.h>
  #include <vtkImageActor.h>
  #include <vtkPointData.h>
  #include <vtkPropCollection.h>
  #include <vtkActor.h>
  #include <vtkPolyDataMapper.h>
  #include <vtkPlane.h>
  #include <vtkCamera.h>
  #include <vtkMath.h>
  #include <vtkRenderer.h>
  
  
  vtkStandardNewMacro(vtkAxes2DWidget);
  
  //----------------------------------------------------------------------------------
  vtkAxes2DWidget::vtkAxes2DWidget()
  {
      this->ImageView = 0;
      this->RenderWindow = 0;
  
      this->PlaneXmin = vtkPlane::New();
      this->PlaneXmax = vtkPlane::New();
      this->PlaneYmin = vtkPlane::New();
      this->PlaneYmax = vtkPlane::New();
  
      this->Source = vtkPolyData::New();
      this->Points = vtkPoints::New();
      this->Mapper = vtkPolyDataMapper::New();
      this->Actor  = vtkActor::New();
      this->Renderer = vtkRenderer::New();
  
  
      this->Source->SetPoints (this->Points);
      this->Mapper->SetInputData (this->Source);
      this->Actor->SetMapper (this->Mapper);
      this->Renderer->AddViewProp (this->Actor);
  
      this->Renderer->SetLayer (1);
      this->Renderer->InteractiveOff();
      this->Renderer->SetViewport (0.0, 0.0, 1.0, 1.0);
  
      this->Points->InsertNextPoint (0, 0, 0);
      this->Points->InsertNextPoint (0, 0, 0);
      this->Points->InsertNextPoint (0, 0, 0);
      this->Points->InsertNextPoint (0, 0, 0);
  
      this->Source->Allocate();
      vtkIdType line[2];
      line[0] = 0; line[1] = 1;
      this->Source->InsertNextCell (VTK_LINE, 2, line);
      line[0] = 2; line[1] = 3;
      this->Source->InsertNextCell (VTK_LINE, 2, line);
  
      this->Command = vtkAxes2DWidgetCommand::New();
      this->Command->SetWidget (this);
  
      this->ColorArray = vtkUnsignedCharArray::New();
      this->ColorArray->SetName ("Colors");
      this->ColorArray->SetNumberOfComponents (3);
  
      unsigned char red[3]   = {255, 0, 0};
      this->ColorArray->InsertNextTypedTuple (red);
      this->ColorArray->InsertNextTypedTuple (red);
      this->ColorArray->InsertNextTypedTuple (red);
      this->ColorArray->InsertNextTypedTuple (red);
  
      this->Source->GetPointData()->SetScalars (this->ColorArray);
  
      this->Enabled = 0;
  }
  
  //----------------------------------------------------------------------------------
  vtkAxes2DWidget::~vtkAxes2DWidget()
  {
      this->PlaneXmin->Delete();
      this->PlaneXmax->Delete();
      this->PlaneYmin->Delete();
      this->PlaneYmax->Delete();
      this->Source->Delete();
      this->Points->Delete();
      this->Mapper->Delete();
      this->Actor->Delete();
      this->Renderer->Delete();
      this->Command->Delete();
      this->ColorArray->Delete();
      if (this->RenderWindow)
          this->RenderWindow->Delete();
  }
  
  //----------------------------------------------------------------------
  void vtkAxes2DWidget::SetImageView(vtkImageView2D *arg)
  {
      if (this->ImageView && arg != this->ImageView)
      {
          this->ImageView->RemoveObserver (this->Command);
      }
  
      this->ImageView = arg;
  
      if (this->ImageView)
      {
          this->ComputePlanes();
      }
  }
  
  //----------------------------------------------------------------------
  void vtkAxes2DWidget::SetRenderWindow(vtkRenderWindow *arg)
  {
      if (this->RenderWindow && arg!=this->RenderWindow)
      {
          this->RenderWindow->RemoveRenderer(this->Renderer);
          this->Renderer->SetRenderWindow(nullptr);
      }
  
      vtkSetObjectBodyMacro (RenderWindow, vtkRenderWindow, arg);
  }
  
  //----------------------------------------------------------------------
  void vtkAxes2DWidget::SetEnabled(int enabling)
  {
      this->Enabled = enabling;
  
      if( !this->ImageView || (this->ImageView && !this->RenderWindow) )
      {
          vtkErrorMacro ( << "Please set Render Window before enabling the widget");
          return;
      }
  
      if (this->RenderWindow->GetNeverRendered())
          return;
  
      if (this->ImageView)
      {
          if (enabling)
          {
              if (!this->ImageView->HasObserver (vtkImageView2D::CurrentPointChangedEvent, this->Command))
              {
                  this->ImageView->AddObserver (vtkImageView2D::CurrentPointChangedEvent, this->Command, 20.0);
                  this->ImageView->AddObserver (vtkImageView2D::OrientationChangedEvent, this->Command, 20.0);
              }
  
              if (this->RenderWindow->GetNumberOfLayers() < 2)
                  this->RenderWindow->SetNumberOfLayers( 2 );
  
              this->RenderWindow->AddRenderer (this->Renderer);
  
              this->Renderer->SetActiveCamera (this->ImageView->GetRenderer()->GetActiveCamera());
  
              this->ComputePlanes();
          }
          else
          {
              this->ImageView->RemoveObserver (this->Command);
              if( this->RenderWindow )
                  this->RenderWindow->RemoveRenderer (this->Renderer);
          }
      }
  }
  
  //----------------------------------------------------------------------
  void vtkAxes2DWidget::ComputePlanes()
  {
      if (!this->ImageView || !this->ImageView->GetRenderer() || !this->ImageView->GetImageActor())
          return;
  
      unsigned char red[3]   = {0, 0, 0};
      unsigned char green[3] = {0, 0, 0};
      unsigned char blue[3]  = {0, 0, 0};
      red[0] = 255; green[1] = 255; blue[2] = 255;
  
      unsigned char *colorX, *colorY;
      switch (this->ImageView->GetViewOrientation())
      {
          case vtkImageView2D::VIEW_ORIENTATION_SAGITTAL:
              colorX = blue;
              colorY = green;
              break;
          case vtkImageView2D::VIEW_ORIENTATION_CORONAL:
              colorX = blue;
              colorY = red;
              break;
          case vtkImageView2D::VIEW_ORIENTATION_AXIAL:
          default:
              colorX = green;
              colorY = red;
              break;
      }
  
      this->ColorArray->SetTypedTuple (0, colorX);
      this->ColorArray->SetTypedTuple (1, colorX);
      this->ColorArray->SetTypedTuple (2, colorY);
      this->ColorArray->SetTypedTuple (3, colorY);
  
      vtkCamera *cam = this->ImageView->GetRenderer()->GetActiveCamera();
      double *normal = cam->GetViewPlaneNormal();
      double *axisy = cam->GetViewUp();
      double axisx[3];
      vtkMath::Cross (axisy, normal, axisx);
  
      this->PlaneXmin->SetNormal (axisx);
      this->PlaneXmax->SetNormal (axisx);
      this->PlaneYmin->SetNormal (axisy);
      this->PlaneYmax->SetNormal (axisy);
  
      int *ext = this->ImageView->GetImageActor()->GetDisplayExtent();
  
      int imageposition[3];
  
      imageposition[0] = ext[0]; imageposition[1] = ext[2]; imageposition[2] = ext[4];
      double p1[3];
      this->ImageView->GetWorldCoordinatesFromImageCoordinates (imageposition, p1);
      imageposition[0] = ext[1]; imageposition[1] = ext[3]; imageposition[2] = ext[5];
      double p2[3];
      this->ImageView->GetWorldCoordinatesFromImageCoordinates (imageposition, p2);
  
      double d1[3], d2[3];
      this->ImageView->GetRenderer()->SetWorldPoint (p1[0], p1[1], p1[2], 0);
      this->ImageView->GetRenderer()->WorldToDisplay();
      this->ImageView->GetRenderer()->GetDisplayPoint (d1);
      this->ImageView->GetRenderer()->SetWorldPoint (p2[0], p2[1], p2[2], 0);
      this->ImageView->GetRenderer()->WorldToDisplay();
      this->ImageView->GetRenderer()->GetDisplayPoint (d2);
  
      this->PlaneXmin->SetOrigin (d1[0] < d2[0] ? p1 : p2);
      this->PlaneXmax->SetOrigin (d1[0] < d2[0] ? p2 : p1);
      this->PlaneYmin->SetOrigin (d1[1] < d2[1] ? p1 : p2);
      this->PlaneYmax->SetOrigin (d1[1] < d2[1] ? p2 : p1);
  }
  
  //----------------------------------------------------------------------
  void vtkAxes2DWidget::ComputeLyingPoints(double *pos)
  {
      //this->ComputePlanes(); // image may have changed, so we need to re-compute planes
  
      double p0[3], p1[3], p2[3], p3[3];
  
      vtkPlane::ProjectPoint (pos, this->PlaneXmin->GetOrigin(), this->PlaneXmin->GetNormal(), p0);
      vtkPlane::ProjectPoint (pos, this->PlaneXmax->GetOrigin(), this->PlaneXmax->GetNormal(), p1);
      vtkPlane::ProjectPoint (pos, this->PlaneYmin->GetOrigin(), this->PlaneYmin->GetNormal(), p2);
      vtkPlane::ProjectPoint (pos, this->PlaneYmax->GetOrigin(), this->PlaneYmax->GetNormal(), p3);
  
      if (this->PlaneYmax->EvaluateFunction (pos) > 0)
      {
          vtkPlane::ProjectPoint (p0, this->PlaneYmax->GetOrigin(), this->PlaneYmax->GetNormal(), p0);
          vtkPlane::ProjectPoint (p1, this->PlaneYmax->GetOrigin(), this->PlaneYmax->GetNormal(), p1);
      }
      if (this->PlaneYmin->EvaluateFunction (pos) < 0)
      {
          vtkPlane::ProjectPoint (p0, this->PlaneYmin->GetOrigin(), this->PlaneYmin->GetNormal(), p0);
          vtkPlane::ProjectPoint (p1, this->PlaneYmin->GetOrigin(), this->PlaneYmin->GetNormal(), p1);
      }
      if (this->PlaneXmax->EvaluateFunction (pos) > 0)
      {
          vtkPlane::ProjectPoint (p2, this->PlaneXmax->GetOrigin(), this->PlaneXmax->GetNormal(), p2);
          vtkPlane::ProjectPoint (p3, this->PlaneXmax->GetOrigin(), this->PlaneXmax->GetNormal(), p3);
      }
      if (this->PlaneXmin->EvaluateFunction (pos) < 0)
      {
          vtkPlane::ProjectPoint (p2, this->PlaneXmin->GetOrigin(), this->PlaneXmin->GetNormal(), p2);
          vtkPlane::ProjectPoint (p3, this->PlaneXmin->GetOrigin(), this->PlaneXmin->GetNormal(), p3);
      }
  
      this->Points->SetPoint (0, p0);
      this->Points->SetPoint (1, p1);
      this->Points->SetPoint (2, p2);
      this->Points->SetPoint (3, p3);
  
      this->Points->Modified();
  }
  
  //----------------------------------------------------------------------------------
  void vtkAxes2DWidget::PrintSelf(ostream& os, vtkIndent indent)
  {
      //Superclass typedef defined in vtkTypeMacro() found in vtkSetGet.h
      this->Superclass::PrintSelf(os, indent);
  }
  
  
  
  
  
  
  //----------------------------------------------------------------------------------
  void vtkAxes2DWidgetCommand::Execute(vtkObject *caller, unsigned long event, void *callData)
  {
      Q_UNUSED(callData);
      if (event == vtkImageView2D::CurrentPointChangedEvent)
      {
          if (!this->Widget)
              return;
  
          vtkImageView *view = vtkImageView::SafeDownCast(caller);
          if (!view)
              return;
  
          double *pos = view->GetCurrentPoint();
          this->Widget->ComputeLyingPoints (pos);
      }
      if (event == vtkImageView2D::OrientationChangedEvent)
      {
          if (!this->Widget)
              return;
          vtkImageView *view = vtkImageView::SafeDownCast(caller);
          if (!view)
              return;
  
          double *pos = view->GetCurrentPoint();
          this->Widget->SetEnabled(this->Widget->GetEnabled());
          this->Widget->ComputePlanes();
          this->Widget->ComputeLyingPoints (pos);
      }
  }
  