// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkLineWidget.h"

#include "vtkActor.h"
#include "vtkAssemblyNode.h"
#include "vtkAssemblyPath.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCellPicker.h"
#include "vtkCommand.h"
#include "vtkDoubleArray.h"
#include "vtkFloatArray.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPickingManager.h"
#include "vtkPlanes.h"
#include "vtkPointWidget.h"
#include "vtkPolyData.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkLineWidget);

//------------------------------------------------------------------------------
// This class is used to coordinate the interaction between the point widget
// at the center of the line and the line widget. When the line is selected
// (as compared to the handles), a point widget appears at the selection
// point, which can be manipulated in the usual way.
class vtkPWCallback : public vtkCommand
{
public:
  static vtkPWCallback* New() { return new vtkPWCallback; }
  void Execute(vtkObject* vtkNotUsed(caller), unsigned long, void*) override
  {
    double x[3];
    this->PointWidget->GetPosition(x);
    this->LineWidget->SetLinePosition(x);
  }
  vtkPWCallback()
    : LineWidget(nullptr)
    , PointWidget(nullptr)
  {
  }
  vtkLineWidget* LineWidget;
  vtkPointWidget* PointWidget;
};

//------------------------------------------------------------------------------
// This class is used to coordinate the interaction between the point widget
// (point 1) and the line widget.
class vtkPW1Callback : public vtkCommand
{
public:
  static vtkPW1Callback* New() { return new vtkPW1Callback; }
  void Execute(vtkObject* vtkNotUsed(caller), unsigned long, void*) override
  {
    double x[3];
    this->PointWidget->GetPosition(x);
    this->LineWidget->SetPoint1(x);
  }
  vtkPW1Callback()
    : LineWidget(nullptr)
    , PointWidget(nullptr)
  {
  }
  vtkLineWidget* LineWidget;
  vtkPointWidget* PointWidget;
};

//------------------------------------------------------------------------------
// This class is used to coordinate the interaction between the point widget
// (point 2) and the line widget.
class vtkPW2Callback : public vtkCommand
{
public:
  static vtkPW2Callback* New() { return new vtkPW2Callback; }
  void Execute(vtkObject* vtkNotUsed(caller), unsigned long, void*) override
  {
    double x[3];
    this->PointWidget->GetPosition(x);
    this->LineWidget->SetPoint2(x);
  }
  vtkPW2Callback()
    : LineWidget(nullptr)
    , PointWidget(nullptr)
  {
  }
  vtkLineWidget* LineWidget;
  vtkPointWidget* PointWidget;
};

//------------------------------------------------------------------------------
// Begin the definition of the vtkLineWidget methods
//
vtkLineWidget::vtkLineWidget()
{
  this->State = vtkLineWidget::Start;
  this->EventCallbackCommand->SetCallback(vtkLineWidget::ProcessEvents);

  this->Align = vtkLineWidget::XAxis;

  // Build the representation of the widget
  int i;
  // Represent the line
  this->LineSource = vtkLineSource::New();
  this->LineSource->SetResolution(5);
  this->LineMapper = vtkPolyDataMapper::New();
  this->LineMapper->SetInputConnection(this->LineSource->GetOutputPort());
  this->LineActor = vtkActor::New();
  this->LineActor->SetMapper(this->LineMapper);

  // Create the handles
  this->Handle = new vtkActor*[2];
  this->HandleMapper = new vtkPolyDataMapper*[2];
  this->HandleGeometry = new vtkSphereSource*[2];
  for (i = 0; i < 2; i++)
  {
    this->HandleGeometry[i] = vtkSphereSource::New();
    this->HandleGeometry[i]->SetThetaResolution(16);
    this->HandleGeometry[i]->SetPhiResolution(8);
    this->HandleMapper[i] = vtkPolyDataMapper::New();
    this->HandleMapper[i]->SetInputConnection(this->HandleGeometry[i]->GetOutputPort());
    this->Handle[i] = vtkActor::New();
    this->Handle[i]->SetMapper(this->HandleMapper[i]);
  }

  // Define the point coordinates
  double bounds[6];
  bounds[0] = -0.5;
  bounds[1] = 0.5;
  bounds[2] = -0.5;
  bounds[3] = 0.5;
  bounds[4] = -0.5;
  bounds[5] = 0.5;
  this->PlaceFactor = 1.0; // overload parent's value

  // Initial creation of the widget, serves to initialize it
  this->PlaceWidget(bounds);
  this->ClampToBounds = 0;

  // Manage the picking stuff
  this->HandlePicker = vtkCellPicker::New();
  this->HandlePicker->SetTolerance(0.001);
  for (i = 0; i < 2; i++)
  {
    this->HandlePicker->AddPickList(this->Handle[i]);
  }
  this->HandlePicker->PickFromListOn();

  this->LinePicker = vtkCellPicker::New();
  this->LinePicker->SetTolerance(0.005); // need some fluff
  this->LinePicker->AddPickList(this->LineActor);
  this->LinePicker->PickFromListOn();

  this->CurrentHandle = nullptr;

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

  // Create the point widgets and associated callbacks
  this->PointWidget = vtkPointWidget::New();
  this->PointWidget->AllOff();
  this->PointWidget->SetHotSpotSize(0.5);

  this->PointWidget1 = vtkPointWidget::New();
  this->PointWidget1->AllOff();
  this->PointWidget1->SetHotSpotSize(0.5);

  this->PointWidget2 = vtkPointWidget::New();
  this->PointWidget2->AllOff();
  this->PointWidget2->SetHotSpotSize(0.5);

  this->PWCallback = vtkPWCallback::New();
  this->PWCallback->LineWidget = this;
  this->PWCallback->PointWidget = this->PointWidget;
  this->PW1Callback = vtkPW1Callback::New();
  this->PW1Callback->LineWidget = this;
  this->PW1Callback->PointWidget = this->PointWidget1;
  this->PW2Callback = vtkPW2Callback::New();
  this->PW2Callback->LineWidget = this;
  this->PW2Callback->PointWidget = this->PointWidget2;

  // Very tricky, the point widgets watch for their own
  // interaction events.
  this->PointWidget->AddObserver(vtkCommand::InteractionEvent, this->PWCallback, 0.0);
  this->PointWidget1->AddObserver(vtkCommand::InteractionEvent, this->PW1Callback, 0.0);
  this->PointWidget2->AddObserver(vtkCommand::InteractionEvent, this->PW2Callback, 0.0);
  this->CurrentPointWidget = nullptr;
}

//------------------------------------------------------------------------------
vtkLineWidget::~vtkLineWidget()
{
  this->LineActor->Delete();
  this->LineMapper->Delete();
  this->LineSource->Delete();

  for (int i = 0; i < 2; i++)
  {
    this->HandleGeometry[i]->Delete();
    this->HandleMapper[i]->Delete();
    this->Handle[i]->Delete();
  }
  delete[] this->Handle;
  delete[] this->HandleMapper;
  delete[] this->HandleGeometry;

  this->HandlePicker->Delete();
  this->LinePicker->Delete();

  this->HandleProperty->Delete();
  this->SelectedHandleProperty->Delete();
  this->LineProperty->Delete();
  this->SelectedLineProperty->Delete();

  this->PointWidget->RemoveObserver(this->PWCallback);
  this->PointWidget1->RemoveObserver(this->PW1Callback);
  this->PointWidget2->RemoveObserver(this->PW2Callback);
  this->PointWidget->Delete();
  this->PointWidget1->Delete();
  this->PointWidget2->Delete();
  this->PWCallback->Delete();
  this->PW1Callback->Delete();
  this->PW2Callback->Delete();
}

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

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

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

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

    this->PointWidget->SetCurrentRenderer(this->CurrentRenderer);
    this->PointWidget1->SetCurrentRenderer(this->CurrentRenderer);
    this->PointWidget2->SetCurrentRenderer(this->CurrentRenderer);

    this->Enabled = 1;

    // listen for the following events
    vtkRenderWindowInteractor* i = this->Interactor;
    i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::MiddleButtonPressEvent, this->EventCallbackCommand, this->Priority);
    i->AddObserver(
      vtkCommand::MiddleButtonReleaseEvent, this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::RightButtonPressEvent, this->EventCallbackCommand, this->Priority);
    i->AddObserver(vtkCommand::RightButtonReleaseEvent, this->EventCallbackCommand, this->Priority);

    // Add the line
    this->CurrentRenderer->AddActor(this->LineActor);
    this->LineActor->SetProperty(this->LineProperty);

    // turn on the handles
    for (int j = 0; j < 2; j++)
    {
      this->CurrentRenderer->AddActor(this->Handle[j]);
      this->Handle[j]->SetProperty(this->HandleProperty);
    }

    this->BuildRepresentation();
    this->SizeHandles();
    this->RegisterPickers();

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

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

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

    this->Enabled = 0;

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

    // turn off the line
    this->CurrentRenderer->RemoveActor(this->LineActor);

    // turn off the handles
    for (int i = 0; i < 2; i++)
    {
      this->CurrentRenderer->RemoveActor(this->Handle[i]);
    }

    if (this->CurrentPointWidget)
    {
      this->CurrentPointWidget->EnabledOff();
    }

    this->CurrentHandle = nullptr;
    this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
    this->SetCurrentRenderer(nullptr);
    this->UnRegisterPickers();
  }

  this->Interactor->Render();
}

//------------------------------------------------------------------------------
void vtkLineWidget::RegisterPickers()
{
  vtkPickingManager* pm = this->GetPickingManager();
  if (!pm)
  {
    return;
  }
  pm->AddPicker(this->HandlePicker, this);
  pm->AddPicker(this->LinePicker, this);
}

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

  // okay, let's do the right thing
  switch (event)
  {
    case vtkCommand::LeftButtonPressEvent:
      self->OnLeftButtonDown();
      break;
    case vtkCommand::LeftButtonReleaseEvent:
      self->OnLeftButtonUp();
      break;
    case vtkCommand::MiddleButtonPressEvent:
      self->OnMiddleButtonDown();
      break;
    case vtkCommand::MiddleButtonReleaseEvent:
      self->OnMiddleButtonUp();
      break;
    case vtkCommand::RightButtonPressEvent:
      self->OnRightButtonDown();
      break;
    case vtkCommand::RightButtonReleaseEvent:
      self->OnRightButtonUp();
      break;
    case vtkCommand::MouseMoveEvent:
      self->OnMouseMove();
      break;
  }
}

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

  if (this->HandleProperty)
  {
    os << indent << "Handle Property: " << this->HandleProperty << "\n";
  }
  else
  {
    os << indent << "Handle Property: (none)\n";
  }
  if (this->SelectedHandleProperty)
  {
    os << indent << "Selected Handle Property: " << this->SelectedHandleProperty << "\n";
  }
  else
  {
    os << indent << "Selected Handle Property: (none)\n";
  }

  if (this->LineProperty)
  {
    os << indent << "Line Property: " << this->LineProperty << "\n";
  }
  else
  {
    os << indent << "Line Property: (none)\n";
  }
  if (this->SelectedLineProperty)
  {
    os << indent << "Selected Line Property: " << this->SelectedLineProperty << "\n";
  }
  else
  {
    os << indent << "Selected Line Property: (none)\n";
  }

  os << indent << "Constrain To Bounds: " << (this->ClampToBounds ? "On\n" : "Off\n");

  os << indent << "Align with: ";
  switch (this->Align)
  {
    case XAxis:
      os << "X Axis";
      break;
    case YAxis:
      os << "Y Axis";
      break;
    case ZAxis:
      os << "Z Axis";
      break;
    default:
      os << "None";
  }
  int res = this->LineSource->GetResolution();
  double* pt1 = this->LineSource->GetPoint1();
  double* pt2 = this->LineSource->GetPoint2();

  os << indent << "Resolution: " << res << "\n";
  os << indent << "Point 1: (" << pt1[0] << ", " << pt1[1] << ", " << pt1[2] << ")\n";
  os << indent << "Point 2: (" << pt2[0] << ", " << pt2[1] << ", " << pt2[2] << ")\n";
}

//------------------------------------------------------------------------------
void vtkLineWidget::BuildRepresentation()
{
  // int res = this->LineSource->GetResolution();
  double* pt1 = this->LineSource->GetPoint1();
  double* pt2 = this->LineSource->GetPoint2();

  this->HandleGeometry[0]->SetCenter(pt1);
  this->HandleGeometry[1]->SetCenter(pt2);
}

//------------------------------------------------------------------------------
void vtkLineWidget::SizeHandles()
{
  double radius = this->vtk3DWidget::SizeHandles(1.0);
  this->HandleGeometry[0]->SetRadius(radius);
  this->HandleGeometry[1]->SetRadius(radius);
}

//------------------------------------------------------------------------------
int vtkLineWidget::HighlightHandle(vtkProp* prop)
{
  // first unhighlight anything picked
  if (this->CurrentHandle)
  {
    this->CurrentHandle->SetProperty(this->HandleProperty);
  }

  // set the current handle
  this->CurrentHandle = static_cast<vtkActor*>(prop);

  // find the current handle
  if (this->CurrentHandle)
  {
    this->ValidPick = 1;
    this->HandlePicker->GetPickPosition(this->LastPickPosition);
    this->CurrentHandle->SetProperty(this->SelectedHandleProperty);
    return (this->CurrentHandle == this->Handle[0] ? 0 : 1);
  }
  return -1;
}

//------------------------------------------------------------------------------
int vtkLineWidget::ForwardEvent(unsigned long event)
{
  if (!this->CurrentPointWidget)
  {
    return 0;
  }

  vtkPointWidget::ProcessEvents(this, event, this->CurrentPointWidget, nullptr);

  return 1;
}

//------------------------------------------------------------------------------
// assumed current handle is set
void vtkLineWidget::EnablePointWidget()
{
  // Set up the point widgets
  double x[3];
  if (this->CurrentHandle) // picking the handles
  {
    if (this->CurrentHandle == this->Handle[0])
    {
      this->CurrentPointWidget = this->PointWidget1;
      this->LineSource->GetPoint1(x);
    }
    else
    {
      this->CurrentPointWidget = this->PointWidget2;
      this->LineSource->GetPoint2(x);
    }
  }
  else // picking the line
  {
    this->CurrentPointWidget = this->PointWidget;
    this->LinePicker->GetPickPosition(x);
    this->LastPosition[0] = x[0];
    this->LastPosition[1] = x[1];
    this->LastPosition[2] = x[2];
  }

  double bounds[6];
  for (int i = 0; i < 3; i++)
  {
    bounds[2 * i] = x[i] - 0.1 * this->InitialLength;
    bounds[2 * i + 1] = x[i] + 0.1 * this->InitialLength;
  }

  // Note: translation mode is disabled and enabled to control
  // the proper positioning of the bounding box.
  this->CurrentPointWidget->SetInteractor(this->Interactor);
  this->CurrentPointWidget->TranslationModeOff();
  this->CurrentPointWidget->SetPlaceFactor(1.0);
  this->CurrentPointWidget->PlaceWidget(bounds);
  this->CurrentPointWidget->TranslationModeOn();
  this->CurrentPointWidget->SetPosition(x);
  this->CurrentPointWidget->SetCurrentRenderer(this->CurrentRenderer);
  this->CurrentPointWidget->On();
}

//------------------------------------------------------------------------------
// assumed current handle is set
void vtkLineWidget::DisablePointWidget()
{
  if (this->CurrentPointWidget)
  {
    this->CurrentPointWidget->Off();
  }
  this->CurrentPointWidget = nullptr;
}

//------------------------------------------------------------------------------
void vtkLineWidget::HighlightHandles(int highlight)
{
  if (highlight)
  {
    this->ValidPick = 1;
    this->HandlePicker->GetPickPosition(this->LastPickPosition);
    this->Handle[0]->SetProperty(this->SelectedHandleProperty);
    this->Handle[1]->SetProperty(this->SelectedHandleProperty);
  }
  else
  {
    this->Handle[0]->SetProperty(this->HandleProperty);
    this->Handle[1]->SetProperty(this->HandleProperty);
  }
}

//------------------------------------------------------------------------------
void vtkLineWidget::HighlightLine(int highlight)
{
  if (highlight)
  {
    this->ValidPick = 1;
    this->LinePicker->GetPickPosition(this->LastPickPosition);
    this->LineActor->SetProperty(this->SelectedLineProperty);
  }
  else
  {
    this->LineActor->SetProperty(this->LineProperty);
  }
}

//------------------------------------------------------------------------------
void vtkLineWidget::OnLeftButtonDown()
{
  int forward = 0;

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

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

  // Okay, we can process this. Try to pick handles first;
  // if no handles picked, then try to pick the line.
  vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

  if (path != nullptr)
  {
    this->EventCallbackCommand->SetAbortFlag(1);
    this->StartInteraction();
    this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    this->State = vtkLineWidget::MovingHandle;
    this->HighlightHandle(path->GetFirstNode()->GetViewProp());
    this->EnablePointWidget();
    forward = this->ForwardEvent(vtkCommand::LeftButtonPressEvent);
  }
  else
  {
    path = this->GetAssemblyPath(X, Y, 0., this->LinePicker);

    if (path != nullptr)
    {
      this->EventCallbackCommand->SetAbortFlag(1);
      this->StartInteraction();
      this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
      this->State = vtkLineWidget::MovingLine;
      this->HighlightLine(1);
      this->EnablePointWidget();
      forward = this->ForwardEvent(vtkCommand::LeftButtonPressEvent);
    }
    else
    {
      this->State = vtkLineWidget::Outside;
      this->HighlightHandle(nullptr);
      return;
    }
  }

  if (!forward)
  {
    this->Interactor->Render();
  }
}

//------------------------------------------------------------------------------
void vtkLineWidget::OnLeftButtonUp()
{
  if (this->State == vtkLineWidget::Outside || this->State == vtkLineWidget::Start)
  {
    return;
  }

  this->State = vtkLineWidget::Start;
  this->HighlightHandle(nullptr);
  this->HighlightLine(0);

  this->SizeHandles();

  int forward = this->ForwardEvent(vtkCommand::LeftButtonReleaseEvent);
  this->DisablePointWidget();

  this->EventCallbackCommand->SetAbortFlag(1);
  this->EndInteraction();
  this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
  if (!forward)
  {
    this->Interactor->Render();
  }
}

//------------------------------------------------------------------------------
void vtkLineWidget::OnMiddleButtonDown()
{
  int forward = 0;

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

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

  // Okay, we can process this. Try to pick handles first;
  // if no handles picked, then pick the bounding box.
  vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

  if (path != nullptr)
  {
    this->EventCallbackCommand->SetAbortFlag(1);
    this->StartInteraction();
    this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    this->State = vtkLineWidget::MovingLine;
    this->HighlightHandles(1);
    this->HighlightLine(1);
    this->EnablePointWidget();
    forward = this->ForwardEvent(vtkCommand::LeftButtonPressEvent);
  }
  else
  {
    path = this->GetAssemblyPath(X, Y, 0., this->LinePicker);

    if (path != nullptr)
    {
      this->EventCallbackCommand->SetAbortFlag(1);
      this->StartInteraction();
      this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
      // The highlight methods set the LastPickPosition, so they are ordered
      this->HighlightHandles(1);
      this->HighlightLine(1);
      this->State = vtkLineWidget::MovingLine;
      this->EnablePointWidget();
      forward = this->ForwardEvent(vtkCommand::LeftButtonPressEvent);
    }
    else
    {
      this->State = vtkLineWidget::Outside;
      return;
    }
  }

  if (!forward)
  {
    this->Interactor->Render();
  }
}

//------------------------------------------------------------------------------
void vtkLineWidget::OnMiddleButtonUp()
{
  if (this->State == vtkLineWidget::Outside || this->State == vtkLineWidget::Start)
  {
    return;
  }

  this->State = vtkLineWidget::Start;
  this->HighlightLine(0);
  this->HighlightHandles(0);

  this->SizeHandles();

  int forward = this->ForwardEvent(vtkCommand::LeftButtonReleaseEvent);
  this->DisablePointWidget();

  this->EventCallbackCommand->SetAbortFlag(1);
  this->EndInteraction();
  this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
  if (!forward)
  {
    this->Interactor->Render();
  }
}

//------------------------------------------------------------------------------
void vtkLineWidget::OnRightButtonDown()
{
  int X = this->Interactor->GetEventPosition()[0];
  int Y = this->Interactor->GetEventPosition()[1];

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

  // Okay, we can process this. Try to pick handles first;
  // if no handles picked, then pick the bounding box.
  vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->HandlePicker);

  if (path != nullptr)
  {
    this->HighlightLine(1);
    this->HighlightHandles(1);
    this->State = vtkLineWidget::Scaling;
  }
  else
  {
    path = this->GetAssemblyPath(X, Y, 0., this->LinePicker);

    if (path != nullptr)
    {
      this->HighlightHandles(1);
      this->HighlightLine(1);
      this->State = vtkLineWidget::Scaling;
    }
    else
    {
      this->State = vtkLineWidget::Outside;
      this->HighlightLine(0);
      return;
    }
  }

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

//------------------------------------------------------------------------------
void vtkLineWidget::OnRightButtonUp()
{
  if (this->State == vtkLineWidget::Outside || this->State == vtkLineWidget::Start)
  {
    return;
  }

  this->State = vtkLineWidget::Start;
  this->HighlightLine(0);
  this->HighlightHandles(0);

  this->SizeHandles();

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

//------------------------------------------------------------------------------
void vtkLineWidget::OnMouseMove()
{
  // See whether we're active
  if (this->State == vtkLineWidget::Outside || this->State == vtkLineWidget::Start)
  {
    return;
  }

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

  // Do different things depending on state
  // Calculations everybody does
  double focalPoint[4], pickPoint[4], prevPickPoint[4];
  double z;

  vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
  if (!camera)
  {
    return;
  }

  // Compute the two points defining the motion vector
  this->ComputeWorldToDisplay(
    this->LastPickPosition[0], this->LastPickPosition[1], this->LastPickPosition[2], focalPoint);
  z = focalPoint[2];
  this->ComputeDisplayToWorld(double(this->Interactor->GetLastEventPosition()[0]),
    double(this->Interactor->GetLastEventPosition()[1]), z, prevPickPoint);
  this->ComputeDisplayToWorld(double(X), double(Y), z, pickPoint);

  // Process the motion
  int forward = 0;
  if (this->State == vtkLineWidget::MovingHandle)
  {
    forward = this->ForwardEvent(vtkCommand::MouseMoveEvent);
  }
  else if (this->State == vtkLineWidget::MovingLine)
  {
    forward = this->ForwardEvent(vtkCommand::MouseMoveEvent);
  }
  else if (this->State == vtkLineWidget::Scaling)
  {
    this->Scale(prevPickPoint, pickPoint, X, Y);
  }

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

//------------------------------------------------------------------------------
void vtkLineWidget::Scale(double* p1, double* p2, int vtkNotUsed(X), int Y)
{
  // 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];

  // int res = this->LineSource->GetResolution();
  double* pt1 = this->LineSource->GetPoint1();
  double* pt2 = this->LineSource->GetPoint2();

  double center[3];
  center[0] = (pt1[0] + pt2[0]) / 2.0;
  center[1] = (pt1[1] + pt2[1]) / 2.0;
  center[2] = (pt1[2] + pt2[2]) / 2.0;

  // Compute the scale factor
  double sf = vtkMath::Norm(v) / sqrt(vtkMath::Distance2BetweenPoints(pt1, pt2));
  if (Y > this->Interactor->GetLastEventPosition()[1])
  {
    sf = 1.0 + sf;
  }
  else
  {
    sf = 1.0 - sf;
  }

  // Move the end points
  double point1[3], point2[3];
  for (int i = 0; i < 3; i++)
  {
    point1[i] = sf * (pt1[i] - center[i]) + center[i];
    point2[i] = sf * (pt2[i] - center[i]) + center[i];
  }

  this->LineSource->SetPoint1(point1);
  this->LineSource->SetPoint2(point2);
  this->LineSource->Update();

  this->BuildRepresentation();
}

//------------------------------------------------------------------------------
void vtkLineWidget::CreateDefaultProperties()
{
  // Handle properties
  this->HandleProperty = vtkProperty::New();
  this->HandleProperty->SetColor(1, 1, 1);

  this->SelectedHandleProperty = vtkProperty::New();
  this->SelectedHandleProperty->SetColor(1, 0, 0);

  // Line properties
  this->LineProperty = vtkProperty::New();
  this->LineProperty->SetRepresentationToWireframe();
  this->LineProperty->SetAmbient(1.0);
  this->LineProperty->SetAmbientColor(1.0, 1.0, 1.0);
  this->LineProperty->SetLineWidth(2.0);

  this->SelectedLineProperty = vtkProperty::New();
  this->SelectedLineProperty->SetRepresentationToWireframe();
  this->SelectedLineProperty->SetAmbient(1.0);
  this->SelectedLineProperty->SetAmbientColor(0.0, 1.0, 0.0);
  this->SelectedLineProperty->SetLineWidth(2.0);
}

//------------------------------------------------------------------------------
void vtkLineWidget::PlaceWidget(double bds[6])
{
  int i;
  double bounds[6], center[3];

  this->AdjustBounds(bds, bounds, center);

  if (this->Align == vtkLineWidget::YAxis)
  {
    this->LineSource->SetPoint1(center[0], bounds[2], center[2]);
    this->LineSource->SetPoint2(center[0], bounds[3], center[2]);
  }
  else if (this->Align == vtkLineWidget::ZAxis)
  {
    this->LineSource->SetPoint1(center[0], center[1], bounds[4]);
    this->LineSource->SetPoint2(center[0], center[1], bounds[5]);
  }
  else if (this->Align == vtkLineWidget::XAxis) // default or x-aligned
  {
    this->LineSource->SetPoint1(bounds[0], center[1], center[2]);
    this->LineSource->SetPoint2(bounds[1], center[1], center[2]);
  }
  this->LineSource->Update();

  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]));

  // Position the handles at the end of the lines
  this->BuildRepresentation();
  this->SizeHandles();
}

//------------------------------------------------------------------------------
void vtkLineWidget::SetPoint1(double x, double y, double z)
{
  double xyz[3];
  xyz[0] = x;
  xyz[1] = y;
  xyz[2] = z;

  if (this->ClampToBounds)
  {
    this->ClampPosition(xyz);
    this->PointWidget1->SetPosition(xyz);
  }
  this->LineSource->SetPoint1(xyz);
  this->BuildRepresentation();
}

//------------------------------------------------------------------------------
void vtkLineWidget::SetPoint2(double x, double y, double z)
{
  double xyz[3];
  xyz[0] = x;
  xyz[1] = y;
  xyz[2] = z;

  if (this->ClampToBounds)
  {
    this->ClampPosition(xyz);
    this->PointWidget2->SetPosition(xyz);
  }
  this->LineSource->SetPoint2(xyz);
  this->BuildRepresentation();
}

//------------------------------------------------------------------------------
void vtkLineWidget::SetLinePosition(double x[3])
{
  double p1[3], p2[3], v[3];

  // vector of motion
  v[0] = x[0] - this->LastPosition[0];
  v[1] = x[1] - this->LastPosition[1];
  v[2] = x[2] - this->LastPosition[2];

  // update position
  this->GetPoint1(p1);
  this->GetPoint2(p2);
  for (int i = 0; i < 3; i++)
  {
    p1[i] += v[i];
    p2[i] += v[i];
  }

  // See whether we can move
  if (this->ClampToBounds && (!this->InBounds(p1) || !this->InBounds(p2)))
  {
    this->PointWidget->SetPosition(this->LastPosition);
    return;
  }

  this->SetPoint1(p1);
  this->SetPoint2(p2);

  // remember last position
  this->LastPosition[0] = x[0];
  this->LastPosition[1] = x[1];
  this->LastPosition[2] = x[2];
}

//------------------------------------------------------------------------------
void vtkLineWidget::ClampPosition(double x[3])
{
  for (int i = 0; i < 3; i++)
  {
    x[i] = std::max(x[i], this->InitialBounds[2 * i]);
    x[i] = std::min(x[i], this->InitialBounds[2 * i + 1]);
  }
}

//------------------------------------------------------------------------------
int vtkLineWidget::InBounds(double x[3])
{
  for (int i = 0; i < 3; i++)
  {
    if (x[i] < this->InitialBounds[2 * i] || x[i] > this->InitialBounds[2 * i + 1])
    {
      return 0;
    }
  }
  return 1;
}

//------------------------------------------------------------------------------
void vtkLineWidget::GetPolyData(vtkPolyData* pd)
{
  pd->ShallowCopy(this->LineSource->GetOutput());
}
VTK_ABI_NAMESPACE_END
