text: /*=========================================================================
  
    Program:   Visualization Toolkit
    Module:    TestDiagram.cxx
  
    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 "vtkGraphItem.h"
  
  #include "vtkBrush.h"
  #include "vtkCallbackCommand.h"
  #include "vtkContext2D.h"
  #include "vtkContextMouseEvent.h"
  #include "vtkContextScene.h"
  #include "vtkGraph.h"
  #include "vtkImageData.h"
  #include "vtkIncrementalForceLayout.h"
  #include "vtkMarkerUtilities.h"
  #include "vtkObjectFactory.h"
  #include "vtkPen.h"
  #include "vtkPoints.h"
  #include "vtkRenderWindowInteractor.h"
  #include "vtkTooltipItem.h"
  #include "vtkTransform2D.h"
  #include "vtkVectorOperators.h"
  
  #include <vector>
  
  vtkStandardNewMacro(vtkGraphItem);
  vtkCxxSetObjectMacro(vtkGraphItem, Graph, vtkGraph);
  
  struct vtkGraphItem::Internals
  {
    std::vector<float> VertexSizes;
    std::vector<vtkVector2f> VertexPositions;
    std::vector<vtkColor4ub> VertexColors;
    std::vector<int> VertexMarkers;
  
    std::vector<std::vector<vtkVector2f>> EdgePositions;
    std::vector<std::vector<vtkColor4ub>> EdgeColors;
    std::vector<float> EdgeWidths;
  
    bool Animating;
    bool AnimationCallbackInitialized;
    vtkRenderWindowInteractor* Interactor;
    vtkNew<vtkCallbackCommand> AnimationCallback;
    int TimerId;
    bool GravityPointSet;
  
    float CurrentScale[2];
    vtkVector2f LastMousePos;
  
    float LayoutAlphaStart;
    float LayoutAlphaCoolDown;
    float LayoutAlphaStop;
  };
  
  vtkGraphItem::vtkGraphItem()
  {
    this->Graph = nullptr;
    this->GraphBuildTime = 0;
    this->Internal = new Internals();
    this->Internal->Interactor = nullptr;
    this->Internal->Animating = false;
    this->Internal->AnimationCallbackInitialized = false;
    this->Internal->TimerId = 0;
    this->Internal->CurrentScale[0] = 1.0f;
    this->Internal->CurrentScale[1] = 1.0f;
    this->Internal->LastMousePos = vtkVector2f(0, 0);
    this->Internal->LayoutAlphaStart = 0.1f;
    this->Internal->LayoutAlphaCoolDown = 0.99f;
    this->Internal->LayoutAlphaStop = 0.005f;
    this->Internal->GravityPointSet = false;
    this->Tooltip->SetVisible(false);
    this->AddItem(this->Tooltip);
  }
  
  vtkGraphItem::~vtkGraphItem()
  {
    if (this->Internal->Animating)
    {
      this->StopLayoutAnimation();
    }
    if (this->Internal->AnimationCallbackInitialized)
    {
      this->Internal->Interactor->RemoveObserver(this->Internal->AnimationCallback);
    }
    delete this->Internal;
    if (this->Graph)
    {
      this->Graph->Delete();
    }
  }
  
  vtkIncrementalForceLayout* vtkGraphItem::GetLayout()
  {
    return this->Layout;
  }
  
  vtkColor4ub vtkGraphItem::VertexColor(vtkIdType vtkNotUsed(item))
  {
    return vtkColor4ub(128, 128, 128, 255);
  }
  
  vtkVector2f vtkGraphItem::VertexPosition(vtkIdType item)
  {
    double* p = this->Graph->GetPoints()->GetPoint(item);
    return vtkVector2f(static_cast<float>(p[0]), static_cast<float>(p[1]));
  }
  
  float vtkGraphItem::VertexSize(vtkIdType vtkNotUsed(item))
  {
    return 10.0f;
  }
  
  int vtkGraphItem::VertexMarker(vtkIdType vtkNotUsed(item))
  {
    return vtkMarkerUtilities::CIRCLE;
  }
  
  vtkStdString vtkGraphItem::VertexTooltip(vtkIdType vtkNotUsed(item))
  {
    return "";
  }
  
  vtkColor4ub vtkGraphItem::EdgeColor(vtkIdType vtkNotUsed(edgeIdx), vtkIdType vtkNotUsed(point))
  {
    return vtkColor4ub(0, 0, 0, 255);
  }
  
  vtkVector2f vtkGraphItem::EdgePosition(vtkIdType edgeIdx, vtkIdType point)
  {
    double* p;
    if (point == 0)
    {
      vtkPoints* points = this->Graph->GetPoints();
      p = points->GetPoint(this->Graph->GetSourceVertex(edgeIdx));
    }
    else if (point == this->NumberOfEdgePoints(edgeIdx) - 1)
    {
      vtkPoints* points = this->Graph->GetPoints();
      p = points->GetPoint(this->Graph->GetTargetVertex(edgeIdx));
    }
    else
    {
      p = this->Graph->GetEdgePoint(edgeIdx, point - 1);
    }
    return vtkVector2f(static_cast<float>(p[0]), static_cast<float>(p[1]));
  }
  
  float vtkGraphItem::EdgeWidth(vtkIdType vtkNotUsed(line), vtkIdType vtkNotUsed(point))
  {
    return 0.0f;
  }
  
  void vtkGraphItem::RebuildBuffers()
  {
    vtkIdType numEdges = this->NumberOfEdges();
    this->Internal->EdgePositions = std::vector<std::vector<vtkVector2f>>(numEdges);
    this->Internal->EdgeColors = std::vector<std::vector<vtkColor4ub>>(numEdges);
    this->Internal->EdgeWidths = std::vector<float>(numEdges);
    for (vtkIdType edgeIdx = 0; edgeIdx < numEdges; ++edgeIdx)
    {
      vtkIdType numPoints = this->NumberOfEdgePoints(edgeIdx);
      this->Internal->EdgePositions[edgeIdx] = std::vector<vtkVector2f>(numPoints);
      this->Internal->EdgeColors[edgeIdx] = std::vector<vtkColor4ub>(numPoints);
      this->Internal->EdgeWidths[edgeIdx] = this->EdgeWidth(edgeIdx, 0);
      for (vtkIdType pointIdx = 0; pointIdx < numPoints; ++pointIdx)
      {
        this->Internal->EdgePositions[edgeIdx][pointIdx] = this->EdgePosition(edgeIdx, pointIdx);
        this->Internal->EdgeColors[edgeIdx][pointIdx] = this->EdgeColor(edgeIdx, pointIdx);
      }
    }
  
    vtkIdType numVertices = this->NumberOfVertices();
    this->Internal->VertexPositions = std::vector<vtkVector2f>(numVertices);
    this->Internal->VertexColors = std::vector<vtkColor4ub>(numVertices);
    this->Internal->VertexSizes = std::vector<float>(numVertices);
    this->Internal->VertexMarkers = std::vector<int>(numVertices);
    vtkMarkerUtilities::GenerateMarker(
      this->Sprite, this->VertexMarker(0), static_cast<int>(this->VertexSize(0)));
    for (vtkIdType vertexIdx = 0; vertexIdx < numVertices; ++vertexIdx)
    {
      this->Internal->VertexPositions[vertexIdx] = this->VertexPosition(vertexIdx);
      this->Internal->VertexColors[vertexIdx] = this->VertexColor(vertexIdx);
      this->Internal->VertexSizes[vertexIdx] = this->VertexSize(vertexIdx);
      this->Internal->VertexMarkers[vertexIdx] = this->VertexMarker(vertexIdx);
    }
  }
  
  void vtkGraphItem::PaintBuffers(vtkContext2D* painter)
  {
    if (this->Internal->EdgePositions.empty())
    {
      return;
    }
    vtkIdType numEdges = static_cast<vtkIdType>(this->Internal->EdgePositions.size());
    for (vtkIdType edgeIdx = 0; edgeIdx < numEdges; ++edgeIdx)
    {
      if (this->Internal->EdgePositions[edgeIdx].empty())
      {
        continue;
      }
      painter->GetPen()->SetWidth(this->Internal->EdgeWidths[edgeIdx]);
      painter->DrawPoly(this->Internal->EdgePositions[edgeIdx][0].GetData(),
        static_cast<int>(this->Internal->EdgePositions[edgeIdx].size()),
        this->Internal->EdgeColors[edgeIdx][0].GetData(), 4);
    }
  
    if (this->Internal->VertexPositions.empty())
    {
      return;
    }
    painter->GetPen()->SetWidth(this->Internal->VertexSizes[0]);
    painter->GetBrush()->SetTextureProperties(vtkBrush::Linear);
    painter->DrawPointSprites(this->Sprite, this->Internal->VertexPositions[0].GetData(),
      static_cast<int>(this->Internal->VertexPositions.size()),
      this->Internal->VertexColors[0].GetData(), 4);
  }
  
  vtkIdType vtkGraphItem::NumberOfVertices()
  {
    if (!this->Graph)
    {
      return 0;
    }
    return this->Graph->GetNumberOfVertices();
  }
  
  vtkIdType vtkGraphItem::NumberOfEdges()
  {
    if (!this->Graph)
    {
      return 0;
    }
    return this->Graph->GetNumberOfEdges();
  }
  
  vtkIdType vtkGraphItem::NumberOfEdgePoints(vtkIdType edgeIdx)
  {
    if (!this->Graph)
    {
      return 0;
    }
    return this->Graph->GetNumberOfEdgePoints(edgeIdx) + 2;
  }
  
  bool vtkGraphItem::IsDirty()
  {
    if (!this->Graph)
    {
      return false;
    }
    if (this->Graph->GetMTime() > this->GraphBuildTime)
    {
      this->GraphBuildTime = this->Graph->GetMTime();
      return true;
    }
    return false;
  }
  
  bool vtkGraphItem::Paint(vtkContext2D* painter)
  {
    if (this->IsDirty())
    {
      this->RebuildBuffers();
    }
    this->PaintBuffers(painter);
    this->PaintChildren(painter);
  
    // Keep the current scale so we can use it in event handlers.
    painter->GetTransform()->GetScale(this->Internal->CurrentScale);
  
    return true;
  }
  
  void vtkGraphItem::ProcessEvents(
    vtkObject* vtkNotUsed(caller), unsigned long event, void* clientData, void* callerData)
  {
    vtkGraphItem* self = reinterpret_cast<vtkGraphItem*>(clientData);
    switch (event)
    {
      case vtkCommand::TimerEvent:
      {
        // We must filter the events to ensure we actually get the timer event we
        // created. I would love signals and slots...
        int timerId = *static_cast<int*>(callerData); // Seems to work.
        if (self->Internal->Animating && timerId == static_cast<int>(self->Internal->TimerId))
        {
          self->UpdateLayout();
          vtkIdType v = self->HitVertex(self->Internal->LastMousePos);
          self->PlaceTooltip(v);
          self->GetScene()->SetDirty(true);
        }
        break;
      }
      default:
        break;
    }
  }
  
  void vtkGraphItem::StartLayoutAnimation(vtkRenderWindowInteractor* interactor)
  {
    // Start a simple repeating timer
    if (!this->Internal->Animating && interactor)
    {
      if (!this->Internal->AnimationCallbackInitialized)
      {
        this->Internal->AnimationCallback->SetClientData(this);
        this->Internal->AnimationCallback->SetCallback(vtkGraphItem::ProcessEvents);
        interactor->AddObserver(vtkCommand::TimerEvent, this->Internal->AnimationCallback, 0);
        this->Internal->Interactor = interactor;
        this->Internal->AnimationCallbackInitialized = true;
      }
      this->Internal->Animating = true;
      // This defines the interval at which the animation will proceed. 60Hz?
      this->Internal->TimerId = interactor->CreateRepeatingTimer(1000 / 60);
      if (!this->Internal->GravityPointSet)
      {
        vtkVector2f screenPos(
          this->Scene->GetSceneWidth() / 2.0f, this->Scene->GetSceneHeight() / 2.0f);
        vtkVector2f pos = this->MapFromScene(screenPos);
        this->Layout->SetGravityPoint(pos);
        this->Internal->GravityPointSet = true;
      }
      this->Layout->SetAlpha(this->Internal->LayoutAlphaStart);
    }
  }
  
  void vtkGraphItem::StopLayoutAnimation()
  {
    this->Internal->Interactor->DestroyTimer(this->Internal->TimerId);
    this->Internal->TimerId = 0;
    this->Internal->Animating = false;
  }
  
  void vtkGraphItem::UpdateLayout()
  {
    if (this->Graph)
    {
      this->Layout->SetGraph(this->Graph);
      this->Layout->SetAlpha(this->Layout->GetAlpha() * this->Internal->LayoutAlphaCoolDown);
      this->Layout->UpdatePositions();
      this->Graph->Modified();
      if (this->Internal->Animating && this->Layout->GetAlpha() < this->Internal->LayoutAlphaStop)
      {
        this->StopLayoutAnimation();
      }
    }
  }
  
  vtkIdType vtkGraphItem::HitVertex(const vtkVector2f& pos)
  {
    vtkIdType numVert = static_cast<vtkIdType>(this->Internal->VertexPositions.size());
    for (vtkIdType v = 0; v < numVert; ++v)
    {
      if ((pos - this->Internal->VertexPositions[v]).Norm() <
        this->Internal->VertexSizes[v] / this->Internal->CurrentScale[0] / 2.0)
      {
        return v;
      }
    }
    return -1;
  }
  
  bool vtkGraphItem::MouseMoveEvent(const vtkContextMouseEvent& event)
  {
    this->Internal->LastMousePos = event.GetPos();
    if (event.GetButton() == vtkContextMouseEvent::NO_BUTTON)
    {
      vtkIdType v = this->HitVertex(event.GetPos());
      this->Scene->SetDirty(true);
      if (v < 0)
      {
        this->Tooltip->SetVisible(false);
        return true;
      }
      vtkStdString text = this->VertexTooltip(v);
      if (text.empty())
      {
        this->Tooltip->SetVisible(false);
        return true;
      }
      this->PlaceTooltip(v);
      this->Tooltip->SetText(text);
      this->Tooltip->SetVisible(true);
      return true;
    }
    if (event.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
      if (this->Layout->GetFixed() >= 0)
      {
        this->Layout->SetAlpha(this->Internal->LayoutAlphaStart);
        this->Graph->GetPoints()->SetPoint(
          this->Layout->GetFixed(), event.GetPos()[0], event.GetPos()[1], 0.0);
      }
      return true;
    }
  
    if (this->Tooltip->GetVisible())
    {
      vtkIdType v = this->HitVertex(event.GetPos());
      this->PlaceTooltip(v);
      this->Scene->SetDirty(true);
    }
  
    return false;
  }
  
  bool vtkGraphItem::MouseEnterEvent(const vtkContextMouseEvent& vtkNotUsed(event))
  {
    return true;
  }
  
  bool vtkGraphItem::MouseLeaveEvent(const vtkContextMouseEvent& vtkNotUsed(event))
  {
    this->Tooltip->SetVisible(false);
    return true;
  }
  
  bool vtkGraphItem::MouseButtonPressEvent(const vtkContextMouseEvent& event)
  {
    this->Tooltip->SetVisible(false);
    if (event.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
      vtkIdType hitVertex = this->HitVertex(event.GetPos());
      this->Layout->SetFixed(hitVertex);
      if (hitVertex >= 0 && this->Internal->Interactor)
      {
        this->Layout->SetAlpha(this->Internal->LayoutAlphaStart);
        if (!this->Internal->Animating && this->Internal->Interactor)
        {
          this->StartLayoutAnimation(this->Internal->Interactor);
        }
      }
      return true;
    }
    return false;
  }
  
  bool vtkGraphItem::MouseButtonReleaseEvent(const vtkContextMouseEvent& event)
  {
    if (event.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
      this->Layout->SetFixed(-1);
      return true;
    }
    return false;
  }
  
  bool vtkGraphItem::MouseWheelEvent(const vtkContextMouseEvent& event, int vtkNotUsed(delta))
  {
    if (this->Tooltip->GetVisible())
    {
      vtkIdType v = this->HitVertex(event.GetPos());
      this->PlaceTooltip(v);
      this->Scene->SetDirty(true);
    }
  
    return false;
  }
  
  bool vtkGraphItem::Hit(const vtkContextMouseEvent& event)
  {
    vtkIdType v = this->HitVertex(event.GetPos());
    return (v >= 0);
  }
  
  void vtkGraphItem::PlaceTooltip(vtkIdType v)
  {
    if (v >= 0)
    {
      vtkVector2f pos = this->Internal->VertexPositions[v];
      this->Tooltip->SetPosition(
        pos[0] + 5 / this->Internal->CurrentScale[0], pos[1] + 5 / this->Internal->CurrentScale[1]);
    }
    else
    {
      this->Tooltip->SetVisible(false);
    }
  }
  
  void vtkGraphItem::PrintSelf(ostream& os, vtkIndent indent)
  {
    this->Superclass::PrintSelf(os, indent);
    os << "Graph: " << (this->Graph ? "" : "(null)") << std::endl;
    if (this->Graph)
    {
      this->Graph->PrintSelf(os, indent.GetNextIndent());
    }
    os << "GraphBuildTime: " << this->GraphBuildTime << std::endl;
  }
  