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

  Program:   Visualization Toolkit
  Module:    vtkImplicitPlaneWidget.h

  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.

=========================================================================*/
/**
 * @class   vtkImplicitPlaneWidget
 * @brief   3D widget for manipulating an infinite plane
 *
 * This 3D widget defines an infinite plane that can be interactively placed
 * in a scene. The widget is represented by a plane with a normal vector; the
 * plane is contained by a bounding box, and where the plane intersects the
 * bounding box the edges are shown (possibly tubed). The normal can be
 * selected and moved to rotate the plane; the plane itself can be selected
 * and translated in various directions. As the plane is moved, the implicit
 * plane function and polygon (representing the plane cut against the bounding
 * box) is updated.
 *
 * To use this object, just invoke SetInteractor() with the argument of the
 * method a vtkRenderWindowInteractor.  You may also wish to invoke
 * "PlaceWidget()" to initially position the widget. If the "i" key (for
 * "interactor") is pressed, the vtkImplicitPlaneWidget will appear. (See
 * superclass documentation for information about changing this behavior.)
 * If you select the normal vector, the plane can be arbitrarily rotated. The
 * plane can be translated along the normal by selecting the plane and moving
 * it. The plane (the plane origin) can also be arbitrary moved by selecting
 * the plane with the middle mouse button. The right mouse button can be used
 * to uniformly scale the bounding box (moving "up" the box scales larger;
 * moving "down" the box scales smaller). Events that occur outside of the
 * widget (i.e., no part of the widget is picked) are propagated to any other
 * registered obsevers (such as the interaction style).  Turn off the widget
 * by pressing the "i" key again (or invoke the Off() method).
 *
 * The vtkImplicitPlaneWidget has several methods that can be used in
 * conjunction with other VTK objects.  The GetPolyData() method can be used
 * to get a polygonal representation (the single polygon clipped by the
 * bounding box).  Typical usage of the widget is to make use of the
 * StartInteractionEvent, InteractionEvent, and EndInteractionEvent
 * events. The InteractionEvent is called on mouse motion; the other two
 * events are called on button down and button up (either left or right
 * button). (Note: there is also a PlaceWidgetEvent that is invoked when
 * the widget is placed with PlaceWidget().)
 *
 * Some additional features of this class include the ability to control the
 * properties of the widget. You do this by setting property values on the
 * normal vector (selected and unselected properties); the plane (selected
 * and unselected properties); the outline (selected and unselected
 * properties); and the edges. The edges may also be tubed or not.
 *
 * @sa
 * vtk3DWidget vtkBoxWidget vtkPlaneWidget vtkLineWidget vtkPointWidget
 * vtkSphereWidget vtkImagePlaneWidget
 */

#ifndef vtkImplicitPlaneWidget_h
#define vtkImplicitPlaneWidget_h

#include "vtkInteractionWidgetsModule.h" // For export macro
#include "vtkPolyDataSourceWidget.h"

class vtkActor;
class vtkPolyDataMapper;
class vtkCellPicker;
class vtkConeSource;
class vtkLineSource;
class vtkSphereSource;
class vtkTubeFilter;
class vtkPlane;
class vtkCutter;
class vtkProperty;
class vtkImageData;
class vtkOutlineFilter;
class vtkFeatureEdges;
class vtkPolyData;
class vtkTransform;

class VTKINTERACTIONWIDGETS_EXPORT vtkImplicitPlaneWidget : public vtkPolyDataSourceWidget
{
public:
  /**
   * Instantiate the object.
   */
  static vtkImplicitPlaneWidget* New();

  vtkTypeMacro(vtkImplicitPlaneWidget, vtkPolyDataSourceWidget);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  //@{
  /**
   * Methods that satisfy the superclass' API.
   */
  void SetEnabled(int) override;
  void PlaceWidget(double bounds[6]) override;
  void PlaceWidget() override { this->Superclass::PlaceWidget(); }
  void PlaceWidget(
    double xmin, double xmax, double ymin, double ymax, double zmin, double zmax) override
  {
    this->Superclass::PlaceWidget(xmin, xmax, ymin, ymax, zmin, zmax);
  }
  //@}

  //@{
  /**
   * Get the origin of the plane.
   */
  virtual void SetOrigin(double x, double y, double z);
  virtual void SetOrigin(double x[3]);
  double* GetOrigin() VTK_SIZEHINT(3);
  void GetOrigin(double xyz[3]);
  //@}

  //@{
  /**
   * Get the normal to the plane.
   */
  void SetNormal(double x, double y, double z);
  void SetNormal(double x[3]);
  double* GetNormal() VTK_SIZEHINT(3);
  void GetNormal(double xyz[3]);
  //@}

  //@{
  /**
   * Force the plane widget to be aligned with one of the x-y-z axes.
   * If one axis is set on, the other two will be set off.
   * Remember that when the state changes, a ModifiedEvent is invoked.
   * This can be used to snap the plane to the axes if it is originally
   * not aligned.
   */
  void SetNormalToXAxis(vtkTypeBool);
  vtkGetMacro(NormalToXAxis, vtkTypeBool);
  vtkBooleanMacro(NormalToXAxis, vtkTypeBool);
  void SetNormalToYAxis(vtkTypeBool);
  vtkGetMacro(NormalToYAxis, vtkTypeBool);
  vtkBooleanMacro(NormalToYAxis, vtkTypeBool);
  void SetNormalToZAxis(vtkTypeBool);
  vtkGetMacro(NormalToZAxis, vtkTypeBool);
  vtkBooleanMacro(NormalToZAxis, vtkTypeBool);
  //@}

  //@{
  /**
   * Turn on/off tubing of the wire outline of the plane. The tube thickens
   * the line by wrapping with a vtkTubeFilter.
   */
  vtkSetMacro(Tubing, vtkTypeBool);
  vtkGetMacro(Tubing, vtkTypeBool);
  vtkBooleanMacro(Tubing, vtkTypeBool);
  //@}

  //@{
  /**
   * Enable/disable the drawing of the plane. In some cases the plane
   * interferes with the object that it is operating on (i.e., the
   * plane interferes with the cut surface it produces producing
   * z-buffer artifacts.)
   */
  void SetDrawPlane(vtkTypeBool plane);
  vtkGetMacro(DrawPlane, vtkTypeBool);
  vtkBooleanMacro(DrawPlane, vtkTypeBool);
  //@}

  //@{
  /**
   * Turn on/off the ability to translate the bounding box by grabbing it
   * with the left mouse button.
   */
  vtkSetMacro(OutlineTranslation, vtkTypeBool);
  vtkGetMacro(OutlineTranslation, vtkTypeBool);
  vtkBooleanMacro(OutlineTranslation, vtkTypeBool);
  //@}

  //@{
  /**
   * Turn on/off the ability to move the widget outside of the input's bound
   */
  vtkSetMacro(OutsideBounds, vtkTypeBool);
  vtkGetMacro(OutsideBounds, vtkTypeBool);
  vtkBooleanMacro(OutsideBounds, vtkTypeBool);
  //@}

  //@{
  /**
   * Turn on/off the ability to scale with the mouse
   */
  vtkSetMacro(ScaleEnabled, vtkTypeBool);
  vtkGetMacro(ScaleEnabled, vtkTypeBool);
  vtkBooleanMacro(ScaleEnabled, vtkTypeBool);
  //@}

  //@{
  /**
   * Turn on/off the ability to translate the origin (sphere)
   * with the left mouse button.
   */
  vtkSetMacro(OriginTranslation, vtkTypeBool);
  vtkGetMacro(OriginTranslation, vtkTypeBool);
  vtkBooleanMacro(OriginTranslation, vtkTypeBool);
  //@}

  //@{
  /**
   * By default the arrow is 30% of the diagonal length. DiagonalRatio control
   * this ratio in the interval [0-2]
   */
  vtkSetClampMacro(DiagonalRatio, double, 0, 2);
  vtkGetMacro(DiagonalRatio, double);
  //@}

  /**
   * Grab the polydata that defines the plane. The polydata contains a single
   * polygon that is clipped by the bounding box.
   */
  void GetPolyData(vtkPolyData* pd);

  /**
   * Satisfies superclass API.  This returns a pointer to the underlying
   * PolyData (which represents the plane).
   */
  vtkPolyDataAlgorithm* GetPolyDataAlgorithm() override;

  /**
   * Get the implicit function for the plane. The user must provide the
   * instance of the class vtkPlane. Note that vtkPlane is a subclass of
   * vtkImplicitFunction, meaning that it can be used by a variety of filters
   * to perform clipping, cutting, and selection of data.
   */
  void GetPlane(vtkPlane* plane);

  /**
   * Satisfies the superclass API.  This will change the state of the widget
   * to match changes that have been made to the underlying PolyDataSource
   */
  void UpdatePlacement() override;

  /**
   * Control widget appearance
   */
  void SizeHandles() override;

  //@{
  /**
   * Get the properties on the normal (line and cone).
   */
  vtkGetObjectMacro(NormalProperty, vtkProperty);
  vtkGetObjectMacro(SelectedNormalProperty, vtkProperty);
  //@}

  //@{
  /**
   * Get the plane properties. The properties of the plane when selected
   * and unselected can be manipulated.
   */
  vtkGetObjectMacro(PlaneProperty, vtkProperty);
  vtkGetObjectMacro(SelectedPlaneProperty, vtkProperty);
  //@}

  //@{
  /**
   * Get the property of the outline.
   */
  vtkGetObjectMacro(OutlineProperty, vtkProperty);
  vtkGetObjectMacro(SelectedOutlineProperty, vtkProperty);
  //@}

  //@{
  /**
   * Get the property of the intersection edges. (This property also
   * applies to the edges when tubed.)
   */
  vtkGetObjectMacro(EdgesProperty, vtkProperty);
  //@}

protected:
  vtkImplicitPlaneWidget();
  ~vtkImplicitPlaneWidget() override;

  // Manage the state of the widget
  int State;
  enum WidgetState
  {
    Start = 0,
    MovingPlane,
    MovingOutline,
    MovingOrigin,
    Scaling,
    Pushing,
    Rotating,
    Outside
  };

  // handles the events
  static void ProcessEvents(
    vtkObject* object, unsigned long event, void* clientdata, void* calldata);

  // ProcessEvents() dispatches to these methods.
  void OnLeftButtonDown();
  void OnLeftButtonUp();
  void OnMiddleButtonDown();
  void OnMiddleButtonUp();
  void OnRightButtonDown();
  void OnRightButtonUp();
  void OnMouseMove();

  // Controlling ivars
  vtkTypeBool NormalToXAxis;
  vtkTypeBool NormalToYAxis;
  vtkTypeBool NormalToZAxis;
  void UpdateRepresentation();

  // The actual plane which is being manipulated
  vtkPlane* Plane;

  // The bounding box is represented by a single voxel image data
  vtkImageData* Box;
  vtkOutlineFilter* Outline;
  vtkPolyDataMapper* OutlineMapper;
  vtkActor* OutlineActor;
  void HighlightOutline(int highlight);
  vtkTypeBool OutlineTranslation; // whether the outline can be moved
  vtkTypeBool ScaleEnabled;       // whether the widget can be scaled
  vtkTypeBool OutsideBounds;      // whether the widget can be moved outside input's bounds

  // The cut plane is produced with a vtkCutter
  vtkCutter* Cutter;
  vtkPolyDataMapper* CutMapper;
  vtkActor* CutActor;
  vtkTypeBool DrawPlane;
  virtual void HighlightPlane(int highlight);

  // Optional tubes are represented by extracting boundary edges and tubing
  vtkFeatureEdges* Edges;
  vtkTubeFilter* EdgesTuber;
  vtkPolyDataMapper* EdgesMapper;
  vtkActor* EdgesActor;
  vtkTypeBool Tubing; // control whether tubing is on

  // Control final length of the arrow:
  double DiagonalRatio;

  // The + normal cone
  vtkConeSource* ConeSource;
  vtkPolyDataMapper* ConeMapper;
  vtkActor* ConeActor;
  void HighlightNormal(int highlight);

  // The + normal line
  vtkLineSource* LineSource;
  vtkPolyDataMapper* LineMapper;
  vtkActor* LineActor;

  // The - normal cone
  vtkConeSource* ConeSource2;
  vtkPolyDataMapper* ConeMapper2;
  vtkActor* ConeActor2;

  // The - normal line
  vtkLineSource* LineSource2;
  vtkPolyDataMapper* LineMapper2;
  vtkActor* LineActor2;

  // The origin positioning handle
  vtkSphereSource* Sphere;
  vtkPolyDataMapper* SphereMapper;
  vtkActor* SphereActor;
  vtkTypeBool OriginTranslation; // whether the origin (sphere) can be moved

  // Do the picking
  vtkCellPicker* Picker;

  // Register internal Pickers within PickingManager
  void RegisterPickers() override;

  // Transform the normal (used for rotation)
  vtkTransform* Transform;

  // Methods to manipulate the plane
  void ConstrainOrigin(double x[3]);
  void Rotate(int X, int Y, double* p1, double* p2, double* vpn);
  void TranslatePlane(double* p1, double* p2);
  void TranslateOutline(double* p1, double* p2);
  void TranslateOrigin(double* p1, double* p2);
  void Push(double* p1, double* p2);
  void Scale(double* p1, double* p2, int X, int Y);

  // Properties used to control the appearance of selected objects and
  // the manipulator in general.
  vtkProperty* NormalProperty;
  vtkProperty* SelectedNormalProperty;
  vtkProperty* PlaneProperty;
  vtkProperty* SelectedPlaneProperty;
  vtkProperty* OutlineProperty;
  vtkProperty* SelectedOutlineProperty;
  vtkProperty* EdgesProperty;
  void CreateDefaultProperties();

  void GeneratePlane();

private:
  vtkImplicitPlaneWidget(const vtkImplicitPlaneWidget&) = delete;
  void operator=(const vtkImplicitPlaneWidget&) = delete;
};

#endif
