/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkControlPointsItem.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 "vtkBrush.h"
#include "vtkCallbackCommand.h"
#include "vtkContext2D.h"
#include "vtkContextDevice2D.h"
#include "vtkContextKeyEvent.h"
#include "vtkContextMouseEvent.h"
#include "vtkContextScene.h"
#include "vtkControlPointsItem.h"
#include "vtkIdTypeArray.h"
#include "vtkObjectFactory.h"
#include "vtkPen.h"
#include "vtkPoints2D.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkTransform2D.h"
#include "vtkVectorOperators.h"

#include <cassert>
#include <limits>

//-----------------------------------------------------------------------------
vtkControlPointsItem::vtkControlPointsItem()
{
  this->Pen->SetLineType(vtkPen::SOLID_LINE);
  this->Pen->SetWidth(2.);
  this->Pen->SetColor(140, 144, 125, 200);
  this->Brush->SetColor(125, 135, 144, 200);

  this->SelectedPointPen = vtkPen::New();
  this->SelectedPointPen->SetWidth(2.);
  // 98, 140, 178
  this->SelectedPointPen->SetColor(63, 90, 115, 200);
  //this->SelectedPointPen->SetColor(98, 140, 178, 200);
  this->SelectedPointBrush = vtkBrush::New();
  this->SelectedPointBrush->SetColor(58, 121, 178, 200);

  this->Selection = vtkIdTypeArray::New();
  this->CurrentPoint = -1;

  this->BlockUpdates = 0;
  this->StartedInteractions = 0;

  this->Callback = vtkCallbackCommand::New();
  this->Callback->SetClientData(this);
  this->Callback->SetCallback(
    vtkControlPointsItem::CallComputePoints);

  this->Bounds[0] = this->Bounds[2] = 0.;
  this->Bounds[1] = this->Bounds[3] = -1.;
  this->UserBounds[0] = this->UserBounds[2] = 0.;
  this->UserBounds[1] = this->UserBounds[3] = -1.;
  this->ValidBounds[0] = this->ValidBounds[2] = 0.;
  this->ValidBounds[1] = this->ValidBounds[3] = -1.;

  this->ScreenPointRadius = 6.f;
  this->Transform = vtkTransform2D::New();

  this->StrokeMode = false;
  this->SwitchPointsMode = false;
  this->MouseMoved = false;
  this->EnforceValidFunction = true;
  this->PointToDelete = -1;
  this->PointAboutToBeDeleted = false;
  this->PointToToggle = -1;
  this->PointAboutToBeToggled = false;
  this->InvertShadow = false;
  this->EndPointsXMovable = true;
  this->EndPointsYMovable = true;
  this->EndPointsRemovable = true;
}

//-----------------------------------------------------------------------------
vtkControlPointsItem::~vtkControlPointsItem()
{
  if (this->Callback)
    {
    this->Callback->Delete();
    this->Callback = 0;
    }
  if (this->SelectedPointPen)
    {
    this->SelectedPointPen->Delete();
    this->SelectedPointPen = 0;
    }
  if (this->SelectedPointBrush)
    {
    this->SelectedPointBrush->Delete();
    this->SelectedPointBrush = 0;
    }
  if (this->Transform)
    {
    this->Transform->Delete();
    this->Transform = 0;
    }
}

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

  os << indent << "EndPointsXMovable: " << this->EndPointsXMovable << endl;
  os << indent << "EndPointsYMovable: " << this->EndPointsYMovable << endl;
  os << indent << "EndPointsRemovable: " << this->EndPointsRemovable << endl;
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::GetBounds(double bounds[4])
{
  // valid user bounds ? use them
  if (this->UserBounds[0] <= this->UserBounds[1] &&
      this->UserBounds[2] <= this->UserBounds[3])
    {
    bounds[0] = this->UserBounds[0];
    bounds[1] = this->UserBounds[1];
    bounds[2] = this->UserBounds[2];
    bounds[3] = this->UserBounds[3];
    return;
    }
  // invalid bounds ? compute them
  if (!(this->Bounds[0] <= this->Bounds[1] &&
        this->Bounds[2] > this->Bounds[3]))
    {
    this->ComputeBounds();
    }
  bounds[0] = this->Bounds[0];
  bounds[1] = this->Bounds[1];
  bounds[2] = this->Bounds[2];
  bounds[3] = this->Bounds[3];
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::ResetBounds()
{
  this->Bounds[0] = 0.;
  this->Bounds[1] = -1.;
  this->Bounds[2] = 0.;
  this->Bounds[3] = -1.;
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::ComputeBounds()
{
  double oldBounds[4];
  oldBounds[0] = this->Bounds[0];
  oldBounds[1] = this->Bounds[1];
  oldBounds[2] = this->Bounds[2];
  oldBounds[3] = this->Bounds[3];

  this->ComputeBounds(this->Bounds);

  if (this->Bounds[0] != oldBounds[0] ||
      this->Bounds[1] != oldBounds[1] ||
      this->Bounds[2] != oldBounds[2] ||
      this->Bounds[3] != oldBounds[3])
    {
    this->Modified();
    }
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::ComputeBounds( double* bounds)
{
  bounds[0] = bounds[2] =  VTK_DOUBLE_MAX;
  bounds[1] = bounds[3] = -VTK_DOUBLE_MAX;
  for (vtkIdType i=0; i < this->GetNumberOfPoints(); ++i)
    {
    double point[4];
    this->GetControlPoint(i, point);
    bounds[0] = std::min(bounds[0], point[0]);
    bounds[1] = std::max(bounds[1], point[0]);
    bounds[2] = std::min(bounds[2], point[1]);
    bounds[3] = std::max(bounds[3], point[1]);
    }
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::Paint(vtkContext2D* painter)
{
  painter->GetDevice()->EnableClipping(false);
  painter->ApplyPen(this->Pen);
  painter->ApplyBrush(this->Brush);
  this->InvertShadow = false;
  this->DrawUnselectedPoints(painter);

  painter->GetPen()->SetLineType(vtkPen::SOLID_LINE);
  //painter->GetPen()->SetColorF(0.87, 0.87, 1.);
  //painter->GetBrush()->SetColorF(0.75, 0.75, 0.95, 0.65);
  //float oldPenWidth = painter->GetPen()->GetWidth();
  painter->ApplyPen(this->SelectedPointPen);
  painter->ApplyBrush(this->SelectedPointBrush);
  this->InvertShadow = true;
  float oldScreenPointRadius = this->ScreenPointRadius;
  if (this->Selection->GetNumberOfTuples())
    {
    //painter->GetPen()->SetWidth(oldPenWidth * 1.4);
    //this->ScreenPointRadius = oldScreenPointRadius * 1.1;
    this->DrawSelectedPoints(painter);
    }
  this->ScreenPointRadius = oldScreenPointRadius;
  this->Transform->SetMatrix(painter->GetTransform()->GetMatrix());

  painter->GetDevice()->EnableClipping(true);
  this->PaintChildren(painter);
  return true;
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::StartChanges()
{
  this->emitEvent(vtkCommand::StartEvent);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::EndChanges()
{
  this->emitEvent(vtkCommand::EndEvent);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::StartInteraction()
{
  ++this->StartedInteractions;
  this->emitEvent(vtkCommand::StartInteractionEvent);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::StartInteractionIfNotStarted()
{
  if (this->GetInteractionsCount() == 0)
    {
    this->StartInteraction();
    }
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::Interaction()
{
  assert(this->StartedInteractions > 0);
  this->emitEvent(vtkCommand::InteractionEvent);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::EndInteraction()
{
  --this->StartedInteractions;
  assert(this->StartedInteractions >= 0);
  this->emitEvent(vtkCommand::EndInteractionEvent);
}

//-----------------------------------------------------------------------------
int vtkControlPointsItem::GetInteractionsCount()const
{
  return this->StartedInteractions;
}

//-----------------------------------------------------------------------------
//void vtkControlPointsItem::emitEvent(unsigned long event, void* params);

//-----------------------------------------------------------------------------
void vtkControlPointsItem::CallComputePoints(
  vtkObject* vtkNotUsed(sender), unsigned long event,
  void* receiver, void* vtkNotUsed(params))
{
  vtkControlPointsItem* item =
    reinterpret_cast<vtkControlPointsItem*>(receiver);
  switch(event)
    {
    case vtkCommand::StartEvent:
      ++item->BlockUpdates;
      break;
    case vtkCommand::EndEvent:
      --item->BlockUpdates;
      if (item->BlockUpdates == 0)
        {
        item->ComputePoints();
        }
      break;
    case vtkCommand::ModifiedEvent:
      item->ComputePoints();
      break;
    default:
      break;
    }
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::ComputePoints()
{
  if (this->BlockUpdates > 0)
    {
    return;
    }

  if (this->GetNumberOfPoints() == 0)
    {
    this->Selection->SetNumberOfTuples(0);
    }

  const int selectedPointCount = this->Selection->GetNumberOfTuples();
  if (selectedPointCount)
    {
    vtkIdTypeArray* oldSelection = this->Selection;
    this->Selection = vtkIdTypeArray::New();
    for (vtkIdType i = 0; i < selectedPointCount; ++i)
      {
      assert(oldSelection->GetValue(i) < this->GetNumberOfPoints());
      this->SelectPoint(oldSelection->GetValue(i));
      }
    oldSelection->Delete();
    }

  if (this->GetScene())
    {
    this->GetScene()->SetDirty(true);
    }
  this->Modified();
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::Hit(const vtkContextMouseEvent &mouse)
{
  double pos[2];
  pos[0] = mouse.GetPos()[0];
  pos[1] = mouse.GetPos()[1];
  double bounds[4];
  this->GetBounds(bounds);
  bool clamped = this->ClampPos(pos, bounds);
  if (!clamped)
    {
    return true;
    }
  // maybe the cursor is over the first or last point (which could be outside
  // the bounds because of the screen point size).
  pos[0] = mouse.GetPos()[0];
  pos[1] = mouse.GetPos()[1];
  for (int i = 0; i < this->GetNumberOfPoints(); ++i)
    {
    if (this->IsOverPoint(pos, i))
      {
      return true;
      }
    }
  return false;
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::ClampPos(double pos[2], double bounds[4])
{
  bool clamped = false;
  if (pos[0] < bounds[0])
    {
    pos[0] = bounds[0];
    clamped = true;
    }
  if (pos[0] > bounds[1])
    {
    pos[0] = bounds[1];
    clamped = true;
    }
  if (pos[1] < 0.)
    {
    pos[1] = 0.;
    clamped = true;
    }
  if (pos[1] > 1.)
    {
    pos[1] = 1.;
    clamped = true;
    }
  return clamped;
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::ClampValidPos(double pos[2])
{
  double validBounds[4];
  this->GetValidBounds(validBounds);
  if (validBounds[0] > this->ValidBounds[1] ||
      validBounds[2] > this->ValidBounds[3])
    {
    double bounds[4];
    this->GetBounds(bounds);
    return this->ClampPos(pos, bounds);
    }
  return this->ClampPos(pos, validBounds);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::DrawUnselectedPoints(vtkContext2D* painter)
{
  const int count = this->GetNumberOfPoints();
  for (vtkIdType i = 0; i < count; ++i)
    {
    vtkIdType idx = this->Selection ? this->Selection->LookupValue(i) : -1;
    if (idx != -1)
      {
      continue;
      }
    this->DrawPoint(painter, i);
    }
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::DrawSelectedPoints(vtkContext2D* painter)
{
  const int count = this->Selection ? this->Selection->GetNumberOfTuples() : 0;
  for (vtkIdType i = 0; i < count; ++i)
    {
    vtkIdType index = this->Selection->GetValue(i);
    assert(index != -1);
    this->DrawPoint(painter, index);
    }
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::DrawPoint(vtkContext2D* painter, vtkIdType index)
{
  assert(index != -1);
  double point[4];
  this->GetControlPoint(index, point);

  double pointInScene[2];
  vtkTransform2D* sceneTransform = painter->GetTransform();
  sceneTransform->TransformPoints(point, pointInScene, 1);

  vtkSmartPointer<vtkTransform2D> translation =
    vtkSmartPointer<vtkTransform2D>::New();
  translation->Translate(pointInScene[0], pointInScene[1]);

  painter->PushMatrix();
  painter->SetTransform(translation);

  unsigned char brushOpacity = painter->GetBrush()->GetOpacity();
  unsigned char penColor[3];
  painter->GetPen()->GetColor(penColor);
  unsigned char penOpacity = painter->GetPen()->GetOpacity();
  //float width = painter->GetPen()->GetWidth();

  float radius = this->ScreenPointRadius;
  bool invertShadow = this->InvertShadow;
  unsigned char color[3] = {penColor[0], penColor[1], penColor[2]};

  if (this->PointToToggle == index && this->PointAboutToBeToggled)
    {
    invertShadow = !invertShadow;
    }
  if (this->PointToDelete == index && this->PointAboutToBeDeleted)
    {
    invertShadow = !invertShadow;
    color[0] = 255;
    color[1] = 0;
    color[2] = 0;
    }
  if (this->CurrentPoint == index)
    {
    radius = this->ScreenPointRadius * 1.3;
    color[0] = 255;
    color[1] = 0;
    color[2] = 255;
    }

  painter->GetPen()->SetColor(color);
  painter->DrawArc(0.f, 0.f, radius, 0.f, 360.f);

  painter->GetBrush()->SetOpacity(0);
  //painter->GetPen()->SetWidth(2.0f);

  unsigned char lightPenColor[4];
  lightPenColor[0] = std::min(color[0] + 100, 255);
  lightPenColor[1] = std::min(color[1] + 100, 255);
  lightPenColor[2] = std::min(color[2] + 100, 255);
  lightPenColor[3] = penOpacity;
  
  unsigned char darkPenColor[4];
  darkPenColor[0] = std::max(color[0] - 50, 0);
  darkPenColor[1] = std::max(color[1] - 50, 0);
  darkPenColor[2] = std::max(color[2] - 50, 0);
  darkPenColor[3] = penOpacity;

  painter->GetPen()->SetColor(invertShadow ? lightPenColor : darkPenColor);
  painter->DrawArc(0.f, 0.f, radius - 1.0, 200.f, 380.f);
  painter->GetPen()->SetColor(invertShadow ? darkPenColor : lightPenColor);
  painter->DrawArc(0.f, 0.f, radius - 1.0, 20.f, 200.f);
  
  painter->GetPen()->SetColor(color);
  if (this->PointToDelete == index && this->PointAboutToBeDeleted)
    {
    painter->DrawLine(-radius, -radius, radius, radius);
    painter->DrawLine(-radius, radius, radius, -radius);
    }

  painter->GetPen()->SetColor(penColor);
  painter->GetPen()->SetOpacity(penOpacity);
  //painter->GetPen()->SetWidth(width);
  painter->GetBrush()->SetOpacity(brushOpacity);
  
  painter->PopMatrix();
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SelectPoint(double* currentPoint)
{
  vtkIdType pointId = this->FindPoint(currentPoint);
  if (pointId == -1)
    {
    vtkErrorMacro( << "try to select a point that doesn't exist");
    return;
    }
  this->SelectPoint(pointId);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SelectPoint(vtkIdType pointId)
{
  if (this->Selection->LookupValue(pointId) != -1)
    {
    return;
    }
  this->Selection->InsertNextValue(pointId);
  this->GetScene()->SetDirty(true);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SelectAllPoints()
{
  this->DeselectAllPoints();
  const int count = this->GetNumberOfPoints();
  for (vtkIdType i = 0; i < count; ++i)
    {
    this->SelectPoint(i);
    }
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::DeselectPoint(double* point)
{
  // make sure the point belongs to the list of points
  vtkIdType pointId = this->FindPoint(point);
  if (pointId == -1)
    {
    vtkErrorMacro( << "try to deselect a point that doesn't exist");
    return;
    }
  this->DeselectPoint(pointId);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::DeselectPoint(vtkIdType pointId)
{
  vtkIdType selectionPointId = this->Selection->LookupValue(pointId);
  if (selectionPointId == -1)
    {
    //vtkErrorMacro(<< "Point:" << pointId << " was not selected");
    return;
    }
  this->Selection->RemoveTuple(selectionPointId);
  this->GetScene()->SetDirty(true);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::DeselectAllPoints()
{
  if (this->GetNumberOfSelectedPoints() == 0)
    {
    return;
    }
  this->Selection->SetNumberOfTuples(0);
  this->GetScene()->SetDirty(true);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::ToggleSelectPoint(double* currentPoint)
{
  // make sure the point belongs to the list of points
  vtkIdType pointId = this->FindPoint(currentPoint);
  if (pointId == -1)
    {
    vtkErrorMacro( << "try to toggle a point that doesn't exist");
    return;
    }
  this->ToggleSelectPoint(pointId);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::ToggleSelectPoint(vtkIdType pointId)
{
  vtkIdType selectionId = this->Selection->LookupValue(pointId);
  if (selectionId != -1)
    {
    this->DeselectPoint(pointId);
    return;
    }
  this->SelectPoint(pointId);
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::SelectPoints(const vtkVector2f& min, const vtkVector2f& max)
{
  bool atLeast1PointSelected = false;
  const int numberOfPoints = this->GetNumberOfPoints();
  for(vtkIdType i = 0; i < numberOfPoints; ++i)
    {
    double point[4];
    this->GetControlPoint(i, point);
    if (point[0] >= min.X() && point[0] <= max.X() &&
        point[1] >= min.Y() && point[1] <= max.Y())
      {
      this->SelectPoint(i);
      atLeast1PointSelected = true;
      }
    else
      {
      this->DeselectPoint(i);
      }
    }
  return atLeast1PointSelected;
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::GetNumberOfSelectedPoints()const
{
  return this->Selection->GetNumberOfTuples();
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::GetCurrentPoint()const
{
  return this->CurrentPoint;
}
/*
//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::FindPoint(double* pos, double tolerance)
{
  if (tolerance < std::numeric_limits<float>::epsilon())
    {
    tolerance = std::numeric_limits<float>::epsilon();
    }
  // make sure the point belongs to the list of points
  vtkIdType pointId = -1;
  double minDist = VTK_DOUBLE_MAX;
  const int numberOfPoints = this->GetNumberOfPoints();
  for(vtkIdType i = 0; i < numberOfPoints; ++i)
    {
    double point[4];
    this->GetControlPoint(i, point);
    double distance = (point[0] - pos[0]) * (point[0] - pos[0]) +
      (point[1] - pos[1]) * (point[1] - pos[1]);
    if (distance <= tolerance)
      {
      if (distance == 0.)
        {// we found the best match ever
        return i;
        }
      else if (distance < minDist)
        {// we found something not too bad, we maybe we can find closer
        pointId = i;
        minDist = distance;
        }
      }
    // don't search any further if the x is already too large
    //if (point[0] > pos[0] + this->ItemPointRadius2)
    //  {
    //  break;
    //  }
    }
  return pointId;
}
*/

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::IsOverPoint(double* pos, vtkIdType pointId)
{
  if (pointId < 0 || pointId >= this->GetNumberOfPoints())
    {
    return false;
    }

  double screenPos[2];
  this->Transform->TransformPoints(pos, screenPos, 1);

  double point[4];
  this->GetControlPoint(pointId, point);
  double screenPoint[2];
  this->Transform->TransformPoints(point, screenPoint, 1);

  double distance2 =
    (screenPoint[0] - screenPos[0]) * (screenPoint[0] - screenPos[0]) +
    (screenPoint[1] - screenPos[1]) * (screenPoint[1] - screenPos[1]);
  double tolerance = 1.3;
  double radius2 = this->ScreenPointRadius * this->ScreenPointRadius
    * tolerance * tolerance;
  return distance2 <= radius2;
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::FindPoint(double* pos)
{
  double tolerance = 1.3;
  double radius2 = this->ScreenPointRadius * this->ScreenPointRadius
    * tolerance * tolerance;
  double screenPos[2];
  this->Transform->TransformPoints(pos, screenPos, 1);
  vtkIdType pointId = -1;
  double minDist = VTK_DOUBLE_MAX;
  const int numberOfPoints = this->GetNumberOfPoints();
  for(vtkIdType i = 0; i < numberOfPoints; ++i)
    {
    double point[4];
    this->GetControlPoint(i, point);
    double screenPoint[2];
    this->Transform->TransformPoints(point, screenPoint, 1);
    double distance2 =
      (screenPoint[0] - screenPos[0]) * (screenPoint[0] - screenPos[0]) +
      (screenPoint[1] - screenPos[1]) * (screenPoint[1] - screenPos[1]);
    if (distance2 <= radius2)
      {
      if (distance2 == 0.)
        {// we found the best match ever
        return i;
        }
      else if (distance2 < minDist)
        {// we found something not too bad, maybe we can find closer
        pointId = i;
        minDist = distance2;
        }
      }
    // don't search any further if the x is already too large
    if (screenPoint[0] > (screenPos[0] + this->ScreenPointRadius * tolerance))
      {
      break;
      }
    }
  return pointId;
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::GetControlPointId(double* point)
{
  const int numberOfPoints = this->GetNumberOfPoints();
  for(vtkIdType i = 0; i < numberOfPoints; ++i)
    {
    double controlPoint[4];
    this->GetControlPoint(i, controlPoint);
    if (controlPoint[0] == point[0])
      {
      return i;
      }
    }
  return -1;
}

//-----------------------------------------------------------------------------
vtkIdTypeArray* vtkControlPointsItem::GetControlPointsIds()const
{
  return this->GetControlPointsIds(false);
}

//-----------------------------------------------------------------------------
vtkIdTypeArray* vtkControlPointsItem
::GetControlPointsIds(bool excludeFirstAndLast)const
{
  vtkIdTypeArray* points = vtkIdTypeArray::New();
  int numberOfPoints = this->GetNumberOfPoints();
  if (excludeFirstAndLast)
    {
    numberOfPoints -= 2;
    }
  if (numberOfPoints <= 0)
    {
    return points;
    }
  points->SetNumberOfTuples(numberOfPoints);
  vtkIdType pointId = excludeFirstAndLast ? 1 : 0;
  for(vtkIdType i = 0; i < numberOfPoints; ++i)
    {
    points->SetValue(i, pointId++);
    }
  return points;
}
//-----------------------------------------------------------------------------
void vtkControlPointsItem::AddPointId(vtkIdType addedPointId)
{
  assert(addedPointId != -1);
  // offset all the point ids
  const int selectionCount = this->Selection->GetNumberOfTuples();
  for (vtkIdType i = 0; i < selectionCount; ++i)
    {
    vtkIdType pointId = this->Selection->GetValue(i);
    if (pointId >= addedPointId)
      {
      this->Selection->SetValue(i, ++pointId);
      }
    }
  if (this->CurrentPoint >= addedPointId)
    {
    this->SetCurrentPoint(this->CurrentPoint + 1);
    }
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::RemovePoint(double* point)
{
  return this->RemovePointId(this->GetControlPointId(point));
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::RemovePoint(vtkIdType pointId)
{
  double point[4];
  this->GetControlPoint(pointId, point);
  return this->RemovePoint(point);
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::RemovePointId(vtkIdType pointId)
{
  if(!this->IsPointRemovable(pointId))
    {
    return pointId;
    }

  assert(pointId != -1);
  // Useless to remove the point here as it will be removed anyway in ComputePoints
  this->DeselectPoint(pointId);

  const vtkIdType selectionCount = this->Selection->GetNumberOfTuples();
  for (vtkIdType i = 0; i < selectionCount; ++i)
    {
    vtkIdType selectedPointId = this->Selection->GetValue(i);
    if (selectedPointId > pointId)
      {
      this->Selection->SetValue(i, --selectedPointId);
      }
    }

  if (this->CurrentPoint > pointId ||
      this->CurrentPoint == this->GetNumberOfPoints() - 1)
    {
    this->SetCurrentPoint(this->CurrentPoint - 1);
    }
  return pointId;
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SetCurrentPoint(vtkIdType index)
{
  if (index == this->CurrentPoint)
    {
    return;
    }
  this->CurrentPoint = index;
  this->InvokeEvent(vtkControlPointsItem::CurrentPointChangedEvent,
                    reinterpret_cast<void *>(this->CurrentPoint));
  this->GetScene()->SetDirty(true);
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::MouseButtonPressEvent(const vtkContextMouseEvent &mouse)
{
  this->MouseMoved = false;
  this->PointToToggle = -1;
  this->PointToDelete = -1;
  double pos[2];
  pos[0] = mouse.GetPos()[0];
  pos[1] = mouse.GetPos()[1];
  vtkIdType pointUnderMouse = this->FindPoint(pos);

  if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
    if (pointUnderMouse != -1)
      {
      this->SetCurrentPoint(pointUnderMouse);
      return true;
      }
    else if (pointUnderMouse == -1
             && this->Selection->GetNumberOfTuples() <= 1
             && !this->StrokeMode)
      {
      this->ClampPos(pos, this->GetValidBounds());
      vtkIdType addedPoint = this->AddPoint(pos);
      this->SetCurrentPoint(addedPoint);
      return true;
      }
    else
      {
      this->SetCurrentPoint(-1);
      }
    return true;
    }

  if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON
      && pointUnderMouse != -1)
    {
    this->PointToToggle = pointUnderMouse;
    this->PointAboutToBeToggled = true;
    this->GetScene()->SetDirty(true);
    return true;
    }

  if (mouse.GetButton() == vtkContextMouseEvent::MIDDLE_BUTTON
      && pointUnderMouse != -1)
    {
    this->PointToDelete = pointUnderMouse;
    this->PointAboutToBeDeleted = true;
    this->GetScene()->SetDirty(true);
    return true;
    }

  return false;
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::MouseDoubleClickEvent(const vtkContextMouseEvent &mouse)
{
  if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON)
    {
    if (this->Selection->GetNumberOfTuples())
      {
      this->DeselectAllPoints();
      }
    else
      {
      this->SelectAllPoints();
      }
    return true;
    }
  bool res = this->MouseButtonPressEvent(mouse);
  if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON
      && this->CurrentPoint != -1)
    {
    this->InvokeEvent(vtkControlPointsItem::CurrentPointEditEvent,
                      reinterpret_cast<void *>(this->CurrentPoint));
    }
  return res;
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::MouseMoveEvent(const vtkContextMouseEvent &mouse)
{
  if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
    if (this->StrokeMode)
      {
      this->StartInteractionIfNotStarted();

      this->Stroke(mouse.GetPos());

      this->Interaction();
      }
    else if (this->CurrentPoint == -1 && this->Selection->GetNumberOfTuples() > 1)
      {
      vtkVector2f deltaPos = mouse.GetPos() - mouse.GetLastPos();
      if(this->IsEndPointPicked())
        {
        if(!this->GetEndPointsMovable())
          {
          return false;
          }
        else if(this->GetEndPointsXMovable())
          {
          deltaPos.SetY(0);
          }
        else if(this->GetEndPointsYMovable())
          {
          deltaPos.SetX(0);
          }
        }

      this->StartInteractionIfNotStarted();

      vtkIdTypeArray* points = this->GetSelection();
      points->Register(this);// must stay valid after each individual point move
      this->MovePoints(deltaPos, points);
      points->UnRegister(this);

      this->Interaction();
      }
    else if (this->CurrentPoint != -1)
      {
      vtkVector2f curPos(mouse.GetPos());
      if(this->IsEndPointPicked())
        {
        double currentPoint[4] = {0.0, 0.0, 0.0, 0.0};
        this->GetControlPoint(this->CurrentPoint, currentPoint);
        if(!this->GetEndPointsMovable())
          {
          return false;
          }
        else if(this->GetEndPointsXMovable())
          {
          curPos.SetY(currentPoint[1]);
          }
        else if(this->GetEndPointsYMovable())
          {
          curPos.SetX(currentPoint[0]);
          }
        }
      this->StartInteractionIfNotStarted();

      this->SetCurrentPointPos(curPos);

      this->Interaction();
      }
    }
  if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON)
    {
    if (this->PointToToggle == -1)
      {
      return false;
      }
    double pos[2];
    pos[0] = mouse.GetPos()[0];
    pos[1] = mouse.GetPos()[1];
    vtkIdType pointUnderCursor = this->FindPoint(pos);
    if ((pointUnderCursor == this->PointToToggle) != this->PointAboutToBeToggled)
      {
      this->PointAboutToBeToggled = !this->PointAboutToBeToggled;
      this->GetScene()->SetDirty(true);
      }
    }
  this->MouseMoved = true;
  if (mouse.GetButton() == vtkContextMouseEvent::MIDDLE_BUTTON)
    {
    if (this->PointToDelete == -1)
      {
      // allow chart ruber band to work
      return false;
      }
    double pos[2];
    pos[0] = mouse.GetPos()[0];
    pos[1] = mouse.GetPos()[1];
    vtkIdType pointUnderCursor = this->FindPoint(pos);
    if ((pointUnderCursor == this->PointToDelete) != this->PointAboutToBeDeleted)
      {
      this->PointAboutToBeDeleted = !this->PointAboutToBeDeleted;
      this->GetScene()->SetDirty(true);
      }
    return true;
    }

  if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON
      && this->CurrentPoint == -1)
    {
    return false;
    }
  return true;
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SetCurrentPointPos(const vtkVector2f& newPos)
{
  vtkIdType movedPoint = this->SetPointPos(this->CurrentPoint, newPos);
  // If the moved point was not CurrentPoint then make it current.
  this->SetCurrentPoint(movedPoint);
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::SetPointPos(vtkIdType point, const vtkVector2f& newPos)
{
  if (point == -1)
    {
    return point;
    }

  // Make sure the new point is inside the boundaries of the function
  double boundedPos[2];
  boundedPos[0] = newPos[0];
  boundedPos[1] = newPos[1];
  this->ClampValidPos(boundedPos);

  if (!this->SwitchPointsMode)
    {
    // Stop mode.
    // You can't move a point past another point.
    if (point > 0)
      {
      double previousPoint[4] = {0.0, 0.0, 0.0, 0.0};
      this->GetControlPoint(point - 1, previousPoint);
      boundedPos[0] = std::max(previousPoint[0], boundedPos[0]);
      }
    if (point < this->GetNumberOfPoints() - 1)
      {
      double nextPoint[4] = {0.0, 0.0, 0.0, 0.0};
      this->GetControlPoint(point + 1, nextPoint);
      boundedPos[0] = std::min(boundedPos[0], nextPoint[0]);
      }
    }
  else
    {
    // Switch mode.
    // Moving a point to the right of the next one, makes it current.
    // and moving a point to the left of the previous one makes it current.
    if (point > 0)
      {
      double previousPoint[4] = {0.0, 0.0, 0.0, 0.0};
      this->GetControlPoint(point - 1, previousPoint);
      while (boundedPos[0] < previousPoint[0])
        {
        point = point - 1;
        if (point == 0)
          {
          break;
          }
        // maybe the move is that important that it went over multiple points
        this->GetControlPoint(point - 1, previousPoint);
        }
      }
    if (point < this->GetNumberOfPoints() - 1)
      {
      double nextPoint[4] = {0.0, 0.0, 0.0, 0.0};
      this->GetControlPoint(point + 1, nextPoint);
      while (boundedPos[0] > nextPoint[0])
        {
        point = point + 1;
        if (point == this->GetNumberOfPoints() - 1)
          {
          break;
          }
        this->GetControlPoint(point + 1, nextPoint);
        }
      }
    }
  double currentPoint[4] = {0.0, 0.0, 0.0, 0.0};
  this->GetControlPoint(point, currentPoint);
  currentPoint[0] = boundedPos[0];
  currentPoint[1] = boundedPos[1];
  this->SetControlPoint(point, currentPoint);
  return point;
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::MoveCurrentPoint(const vtkVector2f& translation)
{
  this->MovePoint(this->CurrentPoint, translation);
}

//-----------------------------------------------------------------------------
vtkIdType vtkControlPointsItem::MovePoint(vtkIdType pointId, const vtkVector2f& translation)
{
  double point[4];
  this->GetControlPoint(pointId, point);
  return this->SetPointPos(pointId, vtkVector2f(point[0] + translation.GetX(), point[1] + translation.GetY()));
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::MovePoints(const vtkVector2f& translation, vtkIdTypeArray* pointIds)
{
  assert(pointIds);
  this->StartChanges();
  // don't support 'switch' mode yet
  //vtkIdTypeArray* addedSelection = vtkIdTypeArray::New();
  bool oldSwitchPoints = this->SwitchPointsMode;
  this->SwitchPointsMode = false;
  // end "don't support 'switch' mode yet"
  const int count = pointIds->GetNumberOfTuples();
  float tX = translation.GetX();
  float tY = translation.GetY();
  int start = tX < 0.f ? 0 : count - 1;
  int end = tX < 0.f ? count : -1;
  int step = tX < 0.f ? 1 : -1;
  for (vtkIdType i = start; i != end; i+=step)
    {
    vtkIdType pointId = pointIds->GetValue(i);
    double currentPoint[4] = {0.0, 0.0, 0.0, 0.0};
    this->GetControlPoint(pointId, currentPoint);
    vtkVector2f newPos(currentPoint[0] + tX, currentPoint[1] + tY);
    //vtkIdType newIdx =
      this->SetPointPos(pointId, newPos);
    // don't support 'switch' mode yet
    //if (newIdx != point)
    //  {
    //  int next = (newIdx > point) ? 1 : -1;
    //  for (int i = point + next; i != newIdx; i+=next)
    //    {
    //    addedSelection->InsertNextValue(i);
    //    }
    //  addedSelection->InsertNextValue(newIdx);
    //  }
    // end "don't support 'switch' mode yet"
    }
  // don't support 'switch' mode yet
  //this->SelectPoints(addedSelection);
  this->SwitchPointsMode = oldSwitchPoints;
  // end "don't support 'switch' mode yet"
  this->EndChanges();
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::MovePoints(const vtkVector2f& translation,
                                      bool dontMoveFirstAndLast)
{
  vtkSmartPointer<vtkIdTypeArray> points;
  points.TakeReference(this->GetControlPointsIds(dontMoveFirstAndLast));
  this->MovePoints(translation, points);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SpreadPoints(float factor, vtkIdTypeArray* pointIds)
{
  assert(pointIds);
  if (pointIds->GetNumberOfTuples() == 0)
    {
    return;
    }
  this->StartChanges();

  double min[2], max[2], center[2];
  double point[4];
  vtkIdType minPointId = pointIds->GetValue(0);
  this->GetControlPoint(minPointId, point);
  min[0] = point[0];
  min[1] = point[1];
  vtkIdType maxPointId = pointIds->GetValue(pointIds->GetNumberOfTuples() - 1);
  this->GetControlPoint(maxPointId, point);
  max[0] = point[0];
  max[1] = point[1];
  center[0] = (min[0] + max[0]) / 2.;
  center[1] = (min[1] + max[1]) / 2.;

  // Left part
  vtkIdType start = 0;
  vtkIdType end = pointIds->GetNumberOfTuples();
  vtkIdType step = 1;
  vtkIdType median = -1; // not needed when factor >= 0
  if (factor < 0.f)
    {
    // search median
    for (vtkIdType i = 0; i < end; ++i)
      {
      vtkIdType pointId = pointIds->GetValue(i);
      this->GetControlPoint(pointId, point);
      if (point[0] > center[0])
        {
        median = i;
        break;
        }
      }
    if (median == -1)
      {
      median = pointIds->GetNumberOfTuples() - 1;
      }
    start = median - 1;
    end = -1;
    step = -1;
    }
  vtkIdType i;
  for (i = start; i != end; i+=step)
    {
    vtkIdType pointId = pointIds->GetValue(i);
    this->GetControlPoint(pointId, point);
    if (point[0] > center[0] ||
        (i != start && point[0] == center[0]))
      {
      break;
      }
    double tX = -factor;
    tX *= (min[0] != center[0]) ?
      (center[0] - point[0]) / (center[0] - min[0]) :
      fabs(point[0]) / 100.;
    vtkVector2f newPos(std::min(point[0] + tX, center[0]), point[1]);
    this->SetPointPos(pointId, newPos);
    }
  // Right part
  start = pointIds->GetNumberOfTuples() - 1;
  end = i - 1;
  step = -1;
  if (factor < 0.f)
    {
    start = median;
    end = pointIds->GetNumberOfTuples();
    step = 1;
    }
  for (i = start; i != end; i+=step)
    {
    vtkIdType pointId = pointIds->GetValue(i);
    this->GetControlPoint(pointId, point);
    assert(point[0] >= center[0]);
    double tX = factor;
    tX *= (max[0] != center[0]) ?
      (point[0] - center[0]) / (max[0] - center[0]) :
      fabs(point[0]) / 100.;
    vtkVector2f newPos(std::max(point[0] + tX, center[0]), point[1]);
    this->SetPointPos(pointId, newPos);
    }
  this->EndChanges();
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::SpreadPoints(float factor, bool dontSpreadFirstAndLast)
{
  vtkSmartPointer<vtkIdTypeArray> points;
  points.TakeReference(this->GetControlPointsIds(dontSpreadFirstAndLast));
  this->SpreadPoints(factor, points);
}

//-----------------------------------------------------------------------------
vtkVector2f vtkControlPointsItem::GetCenterOfMass(vtkIdTypeArray* pointIDs)const
{
  double average[4] = {0., 0., 0., 0.};
  const vtkIdType pointCount = pointIDs->GetNumberOfTuples();
  for (vtkIdType i = 0; i < pointCount; ++i)
    {
    double point[4];
    this->GetControlPoint(pointIDs->GetValue(i), point);
    average[0] += point[0]; // x
    average[1] += point[1]; // y
    average[2] += point[2]; // midpoint
    average[3] += point[3]; // sharpness
    }
  average[0] /= pointCount; // x
  average[1] /= pointCount; // y
  average[2] /= pointCount; // midpoint
  average[3] /= pointCount; // sharpness
  return vtkVector2f(average[0], average[1]);
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::Stroke(const vtkVector2f& newPos)
{
  double pos[2];
  pos[0] = newPos[0];
  pos[1] = newPos[1];
  this->ClampValidPos(pos);

  // last point
  if (this->CurrentPoint != -1)
    {
    vtkIdType lastPointId = this->CurrentPoint;
    double lastPoint[4] = {0.0, 0.0, 0.0, 0.0};
    this->GetControlPoint(lastPointId, lastPoint);
    const double pointSpacing = 1.15;
    float oldScreenPointRadius = this->ScreenPointRadius;
    this->ScreenPointRadius *= pointSpacing;
    // ignore the stroke if it is too close from the last point
    if (this->FindPoint(pos) == lastPointId)
      {
      this->ScreenPointRadius = oldScreenPointRadius;
      return;
      }
    this->ScreenPointRadius = oldScreenPointRadius;
    // For the first case or when the new pos share the same X (but not the
    // same y) then we just have to modify the last point
    if (!this->MouseMoved || lastPoint[0] == pos[0])
      {
      lastPoint[0] = pos[0];
      lastPoint[1] = pos[1];
      this->SetControlPoint(this->CurrentPoint, lastPoint);
      return;
      }
    assert(lastPoint[0] != pos[0]);
    // CurrentPoint != -1 && MouseMoved
    // Starting from the last point, we search points (forward or backward) to see
    // if there are points that can be removed.
    int count = this->GetNumberOfPoints();
    if (pos[0] > lastPoint[0] && lastPointId < count - 1)
      {
      // search if there are points between pos and lastPoint
      double point[4] = {0.0, 0.0, 0.0, 0.0};
      this->GetControlPoint(lastPointId + 1, point);
      while (pos[0] >= point[0])
        {
        this->RemovePoint(point);
        count = this->GetNumberOfPoints();
        if (lastPointId == count - 1)
           {
           break;
           }
        this->GetControlPoint(lastPointId + 1, point);
        }
      }
    else if (pos[0] < lastPoint[0] && lastPointId > 0)
      {
      // search if there are points between pos and lastPoint
      double point[4] = {0.0, 0.0, 0.0, 0.0};
      this->GetControlPoint(lastPointId - 1, point);
      while (pos[0] <= point[0])
        {
        this->RemovePoint(point);
        --lastPointId;
        if (lastPointId == 0)
           {
           break;
           }
        this->GetControlPoint(lastPointId - 1, point);
        }
      }
    }
#ifndef NDEBUG
  const int oldNumberOfPoints = this->GetNumberOfPoints();
#endif
  vtkIdType addedPoint = this->AddPoint(pos);
  this->SetCurrentPoint(addedPoint);
  assert(oldNumberOfPoints + 1 == this->GetNumberOfPoints());
}

//-----------------------------------------------------------------------------
void vtkControlPointsItem::EditPoint(float vtkNotUsed(tX), float vtkNotUsed(tY))
{
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::MouseButtonReleaseEvent(const vtkContextMouseEvent &mouse)
{
  if (this->GetInteractionsCount())
    {
    this->EndInteraction();
    }
  if (mouse.GetButton() == vtkContextMouseEvent::LEFT_BUTTON)
    {
    return true;
    }
  if (mouse.GetButton() == vtkContextMouseEvent::RIGHT_BUTTON
      && this->PointToToggle != -1)
    {
    if (this->PointAboutToBeToggled)
      {
      this->ToggleSelectPoint(this->PointToToggle);
      this->PointToToggle = -1;
      this->PointAboutToBeToggled = false;
      }
    return true;
    }
  if (mouse.GetButton() == vtkContextMouseEvent::MIDDLE_BUTTON
      && this->PointToDelete != -1)
    {
    if (this->PointAboutToBeDeleted)
      {
      // If EnforceValidFunction is true, we don't want less than 2 points
      if (this->IsPointRemovable(this->PointToDelete))
        {
        double point[4];
        this->GetControlPoint(this->PointToDelete, point);
        this->RemovePoint(point);
        this->PointToDelete = -1;
        this->PointAboutToBeDeleted = false;
        }
      else
        {
        this->PointToDelete = -1;
        this->PointAboutToBeDeleted = false;
        this->GetScene()->SetDirty(true);
        }
      }
    return true;
    }
  return false;
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::KeyPressEvent(const vtkContextKeyEvent &key)
{
  bool move = key.GetInteractor()->GetAltKey() != 0 || 
              key.GetInteractor()->GetKeySym() == vtkStdString("plus") ||
              key.GetInteractor()->GetKeySym() == vtkStdString("minus");
  bool select = !move && key.GetInteractor()->GetShiftKey() != 0;
  bool control = key.GetInteractor()->GetControlKey() != 0;
  bool current = !select && !move && !control;
  if (current)
    {
    if (key.GetInteractor()->GetKeySym() == vtkStdString("Right") ||
        key.GetInteractor()->GetKeySym() == vtkStdString("Up"))
      {
      this->SetCurrentPoint(
        std::min(this->GetNumberOfPoints() -1, this->GetCurrentPoint() + 1));
      }
    else if (key.GetInteractor()->GetKeySym() == vtkStdString("Left") ||
             key.GetInteractor()->GetKeySym() == vtkStdString("Down"))
      {
      this->SetCurrentPoint(
        std::max(0, static_cast<int>(this->GetCurrentPoint()) - 1));
      }
    else if (key.GetInteractor()->GetKeySym() == vtkStdString("End"))
      {
      this->SetCurrentPoint(this->GetNumberOfPoints() - 1);
      }
    else if (key.GetInteractor()->GetKeySym() == vtkStdString("Home"))
      {
      this->SetCurrentPoint(0);
      }
    }
  else if (select)
    {
    if (key.GetInteractor()->GetKeySym() == std::string("Right") ||
        key.GetInteractor()->GetKeySym() == std::string("Up"))
      {
      this->SelectPoint(this->CurrentPoint);
      this->SetCurrentPoint(
        std::min(this->GetNumberOfPoints() -1, this->GetCurrentPoint() + 1));
      this->SelectPoint(this->CurrentPoint);
      }
    else if (key.GetInteractor()->GetKeySym() == std::string("Left") ||
             key.GetInteractor()->GetKeySym() == std::string("Down"))
      {
      this->SelectPoint(this->CurrentPoint);
      this->SetCurrentPoint(
        std::max(0, static_cast<int>(this->GetCurrentPoint()) - 1));
      this->SelectPoint(this->CurrentPoint);
      }
    else if (key.GetInteractor()->GetKeySym() == vtkStdString("End"))
      {
      vtkIdType newCurrentPointId = this->GetNumberOfPoints() - 1;
      for (vtkIdType pointId = this->CurrentPoint; pointId < newCurrentPointId; ++pointId)
        {
        this->SelectPoint(pointId);
        }
      this->SelectPoint(newCurrentPointId);
      this->SetCurrentPoint(newCurrentPointId);
      }
    else if (key.GetInteractor()->GetKeySym() == vtkStdString("Home"))
      {
      vtkIdType newCurrentPointId = 0;
      for (vtkIdType pointId = this->CurrentPoint; pointId > newCurrentPointId; --pointId)
        {
        this->SelectPoint(pointId);
        }
      this->SelectPoint(newCurrentPointId);
      this->SetCurrentPoint(newCurrentPointId);
      }
    }
  else if (move)
    {
    vtkVector2f translate(0,0);
    if (key.GetInteractor()->GetKeySym() == std::string("Up"))
      {
      translate = translate + vtkVector2f(0., 1.);
      }
    if (key.GetInteractor()->GetKeySym() == std::string("Down"))
      {
      translate = translate + vtkVector2f(0., -1.);
      }
    if (key.GetInteractor()->GetKeySym() == std::string("Right"))
      {
      translate = translate + vtkVector2f(1., 0.);
      }
    if (key.GetInteractor()->GetKeySym() == std::string("Left"))
      {
      translate = translate + vtkVector2f(-1., 0.);
      }
    if (translate.GetX() != 0.f || translate.GetY() != 0.f)
      {
      double bounds[4];
      this->GetBounds(bounds);
      float step = control ? 0.001 : 0.01;
      translate.SetX( translate.GetX() * (bounds[1] - bounds[0]) * step);
      translate.SetY( translate.GetY() * (bounds[3] - bounds[2]) * step);
      if (this->GetNumberOfSelectedPoints())
        {
        this->StartInteractionIfNotStarted();

        vtkIdTypeArray* points = this->GetSelection();
        points->Register(this); // must stay valid after each individual move
        this->MovePoints(translate, points);
        points->UnRegister(this);

        this->Interaction();
        }
      else
        {
        this->StartInteractionIfNotStarted();

        this->MoveCurrentPoint(translate);

        this->Interaction();
        }
      }
    else if (key.GetInteractor()->GetKeySym() == std::string("plus"))
      {
      this->StartInteractionIfNotStarted();

      vtkIdTypeArray* pointIds = this->GetSelection();
      pointIds->Register(this); // must stay valid after each individual move
      this->SpreadPoints(1., pointIds);
      pointIds->UnRegister(this);

      this->Interaction();
      }
    else if (key.GetInteractor()->GetKeySym() == std::string("minus"))
      {
      this->StartInteractionIfNotStarted();

      vtkIdTypeArray* pointIds = this->GetSelection();
      pointIds->Register(this); // must stay valid after each individual move
      this->SpreadPoints(-1., pointIds);
      pointIds->UnRegister(this);

      this->Interaction();
      }
    }
  else if (control)
    {
    if (key.GetInteractor()->GetKeySym() == std::string("a"))
      {
      this->SelectAllPoints();
      }
    }
  if (key.GetInteractor()->GetKeySym() == vtkStdString("space"))
    {
    this->ToggleSelectPoint(this->GetCurrentPoint());
    }
  else if (key.GetInteractor()->GetKeySym() == vtkStdString("Escape"))
    {
    this->DeselectAllPoints();
    }
  return this->Superclass::KeyPressEvent(key);
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::KeyReleaseEvent(const vtkContextKeyEvent &key)
{
  if (key.GetInteractor()->GetKeySym() == std::string("Delete") ||
      key.GetInteractor()->GetKeySym() == std::string("BackSpace"))
    {
    vtkIdType removedPoint = this->RemovePoint(this->GetCurrentPoint());
    if (key.GetInteractor()->GetKeySym() == std::string("BackSpace"))
      {
      this->SetCurrentPoint(removedPoint > 0 ? removedPoint - 1 : 0);
      }
    return true;
    }
  return this->Superclass::KeyPressEvent(key);
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::GetEndPointsMovable()
{
  return (this->GetEndPointsXMovable() || this->GetEndPointsYMovable());
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::IsEndPointPicked()
{
  int numPts = this->GetNumberOfPoints();
  if(numPts<=0)
    {
    return false;
    }
  if(this->CurrentPoint==0 || this->CurrentPoint==numPts-1)
    {
    return true;
    }
  vtkIdTypeArray* selection = this->GetSelection();
  if(selection && selection->GetNumberOfTuples()>0)
    {
    vtkIdType pid;
    for (vtkIdType i = 0; i < selection->GetNumberOfTuples(); ++i)
      {
      pid=selection->GetValue(i);
      if(pid==0 || pid==numPts-1)
        {
        return true;
        }
      }
    }
  return false;
}

//-----------------------------------------------------------------------------
bool vtkControlPointsItem::IsPointRemovable(vtkIdType pointId)
{
  vtkIdType numPts = this->GetNumberOfPoints();
  if (this->EnforceValidFunction && numPts<= 2)
    {
    return false;
    }
  if(pointId != -1 && !this->GetEndPointsRemovable() &&
    (pointId==0 || pointId==numPts-1))
    {
    return false;
    }
  return true;
}
