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

  Program:   Visualization Toolkit
  Module:    vtkAffineRepresentation2D.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   vtkAffineRepresentation2D
 * @brief   represent 2D affine transformations
 *
 * This class is used to represent a vtkAffineWidget. This representation
 * consists of three parts: a box, a circle, and a cross. The box is used for
 * scaling and shearing, the circle for rotation, and the cross for
 * translation. These parts are drawn in the overlay plane and maintain a
 * constant size (width and height) specified in terms of normalized viewport
 * coordinates.
 *
 * The representation maintains an internal transformation matrix (see
 * superclass' GetTransform() method). The transformations generated by this
 * widget assume that the representation lies in the x-y plane. If this is
 * not the case, the user is responsible for transforming this
 * representation's matrix into the correct coordinate space (by judicious
 * matrix multiplication). Note that the transformation matrix returned by
 * GetTransform() is relative to the last PlaceWidget() invocation. (The
 * PlaceWidget() sets the origin around which rotation and scaling occurs;
 * the origin is the center point of the bounding box provided.)
 *
 *
 * @sa
 * vtkAffineRepresentation vtkAffineWidget
 */

#ifndef vtkAffineRepresentation2D_h
#define vtkAffineRepresentation2D_h

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

class vtkProperty2D;
class vtkActor2D;
class vtkPolyDataMapper2D;
class vtkPolyData;
class vtkPoints;
class vtkCellArray;
class vtkTextProperty;
class vtkLeaderActor2D;
class vtkTextMapper;
class vtkActor2D;

class VTKINTERACTIONWIDGETS_EXPORT vtkAffineRepresentation2D : public vtkAffineRepresentation
{
public:
  /**
   * Instantiate this class.
   */
  static vtkAffineRepresentation2D* New();

  ///@{
  /**
   * Standard methods for instances of this class.
   */
  vtkTypeMacro(vtkAffineRepresentation2D, vtkAffineRepresentation);
  void PrintSelf(ostream& os, vtkIndent indent) override;
  ///@}

  ///@{
  /**
   * Specify the width of the various parts of the representation (in
   * pixels).  The three parts are of the representation are the translation
   * axes, the rotation circle, and the scale/shear box. Note that since the
   * widget resizes itself so that the width and height are always the
   * same, only the width needs to be specified.
   */
  vtkSetClampMacro(BoxWidth, int, 10, VTK_INT_MAX);
  vtkGetMacro(BoxWidth, int);
  vtkSetClampMacro(CircleWidth, int, 10, VTK_INT_MAX);
  vtkGetMacro(CircleWidth, int);
  vtkSetClampMacro(AxesWidth, int, 10, VTK_INT_MAX);
  vtkGetMacro(AxesWidth, int);
  ///@}

  ///@{
  /**
   * Specify the origin of the widget (in world coordinates). The origin
   * is the point where the widget places itself. Note that rotations and
   * scaling occurs around the origin.
   */
  void SetOrigin(const double o[3]) { this->SetOrigin(o[0], o[1], o[2]); }
  void SetOrigin(double ox, double oy, double oz);
  vtkGetVector3Macro(Origin, double);
  ///@}

  /**
   * Retrieve a linear transform characterizing the affine transformation
   * generated by this widget. This method copies its internal transform into
   * the transform provided. Note that the PlaceWidget() method initializes
   * the internal matrix to identity. All subsequent widget operations (i.e.,
   * scale, translate, rotate, shear) are concatenated with the internal
   * transform.
   */
  void GetTransform(vtkTransform* t) override;

  ///@{
  /**
   * Set/Get the properties when unselected and selected.
   */
  void SetProperty(vtkProperty2D*);
  void SetSelectedProperty(vtkProperty2D*);
  void SetTextProperty(vtkTextProperty*);
  vtkGetObjectMacro(Property, vtkProperty2D);
  vtkGetObjectMacro(SelectedProperty, vtkProperty2D);
  vtkGetObjectMacro(TextProperty, vtkTextProperty);
  ///@}

  ///@{
  /**
   * Enable the display of text with numeric values characterizing the
   * transformation. Rotation and shear are expressed in degrees; translation
   * the distance in world coordinates; and scale normalized (sx,sy) values.
   */
  vtkSetMacro(DisplayText, vtkTypeBool);
  vtkGetMacro(DisplayText, vtkTypeBool);
  vtkBooleanMacro(DisplayText, vtkTypeBool);
  ///@}

  ///@{
  /**
   * Subclasses of vtkAffineRepresentation2D must implement these methods. These
   * are the methods that the widget and its representation use to
   * communicate with each other. Note: PlaceWidget() reinitializes the
   * transformation matrix (i.e., sets it to identity). It also sets the
   * origin for scaling and rotation.
   */
  void PlaceWidget(double bounds[6]) override;
  void StartWidgetInteraction(double eventPos[2]) override;
  void WidgetInteraction(double eventPos[2]) override;
  void EndWidgetInteraction(double eventPos[2]) override;
  int ComputeInteractionState(int X, int Y, int modify = 0) override;
  void BuildRepresentation() override;
  ///@}

  ///@{
  /**
   * Methods to make this class behave as a vtkProp.
   */
  void ShallowCopy(vtkProp* prop) override;
  void GetActors2D(vtkPropCollection*) override;
  void ReleaseGraphicsResources(vtkWindow*) override;
  int RenderOverlay(vtkViewport* viewport) override;
  ///@}

protected:
  vtkAffineRepresentation2D();
  ~vtkAffineRepresentation2D() override;

  // Methods to manipulate the cursor
  void Translate(double eventPos[2]);
  void Scale(double eventPos[2]);
  void Rotate(double eventPos[2]);
  void Shear(double eventPos[2]);
  void Highlight(int highlight) override;
  void UpdateText(const char* text, double eventPos[2]);

  // The width of the widget in normalized viewport coordinates.
  int BoxWidth;
  int CircleWidth;
  int AxesWidth;

  // Display text
  vtkTypeBool DisplayText;

  // Internal variables for bookkeeping (in display coordinates unless noted)
  double CurrentWidth;
  double CurrentRadius;
  double CurrentAxesWidth;

  // The internal transformation matrix
  vtkTransform* CurrentTransform;
  vtkTransform* TotalTransform;
  double Origin[4];             // the current origin in world coordinates
  double DisplayOrigin[3];      // the current origin in display coordinates
  double CurrentTranslation[3]; // translation this movement
  double StartWorldPosition[4]; // Start event position converted to world
  double StartAngle;            // The starting angle (always positive)
  double CurrentAngle;
  double CurrentScale[2];
  double CurrentShear[2];
  void ApplyShear(); // helper method to apply shear to matrix

  // Properties used to control the appearance of selected objects and
  // the manipulator in general.
  vtkProperty2D* Property;
  vtkProperty2D* SelectedProperty;
  vtkTextProperty* TextProperty;
  void CreateDefaultProperties();
  double Opacity;
  double SelectedOpacity;

  // Support picking
  double LastEventPosition[2];

  // These are the classes that form the geometric representation -----------
  // The label
  vtkTextMapper* TextMapper;
  vtkActor2D* TextActor;

  // The outer box
  vtkPoints* BoxPoints;
  vtkCellArray* BoxCellArray;
  vtkPolyData* Box;
  vtkPolyDataMapper2D* BoxMapper;
  vtkActor2D* BoxActor;

  vtkPoints* HBoxPoints;
  vtkCellArray* HBoxCellArray;
  vtkPolyData* HBox;
  vtkPolyDataMapper2D* HBoxMapper;
  vtkActor2D* HBoxActor;

  // The circle
  vtkPoints* CirclePoints;
  vtkCellArray* CircleCellArray;
  vtkPolyData* Circle;
  vtkPolyDataMapper2D* CircleMapper;
  vtkActor2D* CircleActor;

  vtkPoints* HCirclePoints;
  vtkCellArray* HCircleCellArray;
  vtkPolyData* HCircle;
  vtkPolyDataMapper2D* HCircleMapper;
  vtkActor2D* HCircleActor;

  // The translation axes
  vtkLeaderActor2D* XAxis;
  vtkLeaderActor2D* YAxis;
  vtkLeaderActor2D* HXAxis;
  vtkLeaderActor2D* HYAxis;

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

#endif
