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

  Program:   Visualization Toolkit
  Module:    vtkCoordinate.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   vtkCoordinate
 * @brief   perform coordinate transformation, and represent position, in a variety of vtk
 * coordinate systems
 *
 * vtkCoordinate represents position in a variety of coordinate systems, and
 * converts position to other coordinate systems. It also supports relative
 * positioning, so you can create a cascade of vtkCoordinate objects (no loops
 * please!) that refer to each other. The typical usage of this object is
 * to set the coordinate system in which to represent a position (e.g.,
 * SetCoordinateSystemToNormalizedDisplay()), set the value of the coordinate
 * (e.g., SetValue()), and then invoke the appropriate method to convert to
 * another coordinate system (e.g., GetComputedWorldValue()).
 *
 * The coordinate systems in vtk are as follows:
 * <PRE>
 *   DISPLAY -             x-y pixel values in window
 *      0, 0 is the lower left of the first pixel,
 *      size, size is the upper right of the last pixel
 *   NORMALIZED DISPLAY -  x-y (0,1) normalized values
 *      0, 0 is the lower left of the first pixel,
 *      1, 1 is the upper right of the last pixel
 *   VIEWPORT -            x-y pixel values in viewport
 *      0, 0 is the lower left of the first pixel,
 *      size, size is the upper right of the last pixel
 *   NORMALIZED VIEWPORT - x-y (0,1) normalized value in viewport
 *      0, 0 is the lower left of the first pixel,
 *      1, 1 is the upper right of the last pixel
 *   VIEW -                x-y-z (-1,1) values in pose coordinates. (z is depth)
 *   POSE -                world coords translated and rotated to the camera
 *                         position and view direction
 *   WORLD -               x-y-z global coordinate values
 *   USERDEFINED -         x-y-z in User defined space
 * </PRE>
 *
 * If you cascade vtkCoordinate objects, you refer to another vtkCoordinate
 * object which in turn can refer to others, and so on. This allows you to
 * create composite groups of things like vtkActor2D that are positioned
 * relative to one another. Note that in cascaded sequences, each
 * vtkCoordinate object may be specified in different coordinate systems!
 *
 * @sa
 * vtkActor2D vtkScalarBarActor
 */

#ifndef vtkCoordinate_h
#define vtkCoordinate_h

#include "vtkObject.h"
#include "vtkRenderingCoreModule.h" // For export macro
class vtkViewport;

#define VTK_DISPLAY 0
#define VTK_NORMALIZED_DISPLAY 1
#define VTK_VIEWPORT 2
#define VTK_NORMALIZED_VIEWPORT 3
#define VTK_VIEW 4
#define VTK_POSE 5
#define VTK_WORLD 6
#define VTK_USERDEFINED 7

class VTKRENDERINGCORE_EXPORT vtkCoordinate : public vtkObject
{
public:
  vtkTypeMacro(vtkCoordinate, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent) override;

  /**
   * Creates an instance of this class with the following defaults:
   * value of  (0,0,0) in world coordinates.
   */
  static vtkCoordinate* New();

  //@{
  /**
   * Set/get the coordinate system which this coordinate
   * is defined in. The options are Display, Normalized Display,
   * Viewport, Normalized Viewport, View, and World.
   */
  vtkSetMacro(CoordinateSystem, int);
  vtkGetMacro(CoordinateSystem, int);
  void SetCoordinateSystemToDisplay() { this->SetCoordinateSystem(VTK_DISPLAY); }
  void SetCoordinateSystemToNormalizedDisplay()
  {
    this->SetCoordinateSystem(VTK_NORMALIZED_DISPLAY);
  }
  void SetCoordinateSystemToViewport() { this->SetCoordinateSystem(VTK_VIEWPORT); }
  void SetCoordinateSystemToNormalizedViewport()
  {
    this->SetCoordinateSystem(VTK_NORMALIZED_VIEWPORT);
  }
  void SetCoordinateSystemToView() { this->SetCoordinateSystem(VTK_VIEW); }
  void SetCoordinateSystemToPose() { this->SetCoordinateSystem(VTK_POSE); }
  void SetCoordinateSystemToWorld() { this->SetCoordinateSystem(VTK_WORLD); }
  //@}

  const char* GetCoordinateSystemAsString();

  //@{
  /**
   * Set/get the value of this coordinate. This can be thought of as
   * the position of this coordinate in its coordinate system.
   */
  vtkSetVector3Macro(Value, double);
  vtkGetVector3Macro(Value, double);
  void SetValue(double a, double b) { this->SetValue(a, b, 0.0); }
  //@}

  //@{
  /**
   * If this coordinate is relative to another coordinate,
   * then specify that coordinate as the ReferenceCoordinate.
   * If this is NULL the coordinate is assumed to be absolute.
   */
  virtual void SetReferenceCoordinate(vtkCoordinate*);
  vtkGetObjectMacro(ReferenceCoordinate, vtkCoordinate);
  //@}

  //@{
  /**
   * If you want this coordinate to be relative to a specific
   * vtkViewport (vtkRenderer) then you can specify that here.
   * NOTE: this is a raw pointer, not a weak pointer nor a reference counted
   * object, to avoid reference cycle loop between rendering classes and filter
   * classes.
   */
  void SetViewport(vtkViewport* viewport);
  vtkGetObjectMacro(Viewport, vtkViewport);
  //@}

  //@{
  /**
   * Return the computed value in a specified coordinate system.
   */
  double* GetComputedWorldValue(vtkViewport*) VTK_SIZEHINT(3);
  int* GetComputedViewportValue(vtkViewport*) VTK_SIZEHINT(2);
  int* GetComputedDisplayValue(vtkViewport*) VTK_SIZEHINT(2);
  int* GetComputedLocalDisplayValue(vtkViewport*) VTK_SIZEHINT(2);
  //@}

  double* GetComputedDoubleViewportValue(vtkViewport*) VTK_SIZEHINT(2);
  double* GetComputedDoubleDisplayValue(vtkViewport*) VTK_SIZEHINT(2);

  /**
   * GetComputedValue() will return either World, Viewport or
   * Display based on what has been set as the coordinate system.
   * This is good for objects like vtkLineSource, where the
   * user might want to use them as World or Viewport coordinates.
   */
  double* GetComputedValue(vtkViewport*) VTK_SIZEHINT(3);

  /**
   * GetComputedUserDefinedValue() is to be used only when
   * the coordinate system is VTK_USERDEFINED. The user
   * must subclass vtkCoordinate and override this function,
   * when set as the TransformCoordinate in 2D-Mappers, the user
   * can customize display of 2D polygons
   */
  virtual double* GetComputedUserDefinedValue(vtkViewport*) VTK_SIZEHINT(3) { return this->Value; }

protected:
  vtkCoordinate();
  ~vtkCoordinate() override;

  double Value[3];
  int CoordinateSystem;
  vtkCoordinate* ReferenceCoordinate;
  vtkViewport* Viewport;
  double ComputedWorldValue[3];
  int ComputedDisplayValue[2];
  int ComputedViewportValue[2];
  int Computing;

  double ComputedDoubleDisplayValue[2];
  double ComputedDoubleViewportValue[2];
  double ComputedUserDefinedValue[3];

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

#endif
