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

  Program:   Visualization Toolkit
  Module:    vtkStreamer.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.

=========================================================================*/
// .NAME vtkStreamer - abstract object implements integration of massless particle through vector field
// .SECTION Description
// vtkStreamer is a filter that integrates a massless particle through a vector
// field. The integration is performed using second order Runge-Kutta method. 
// vtkStreamer often serves as a base class for other classes that perform 
// numerical integration through a vector field (e.g., vtkStreamLine).
//
// Note that vtkStreamer can integrate both forward and backward in time,
// or in both directions. The length of the streamer is controlled by 
// specifying an elapsed time. (The elapsed time is the time each particle 
// travels.) Otherwise, the integration terminates after exiting the dataset or
// if the particle speed is reduced to a value less than the terminal speed.
//
// vtkStreamer integrates through any type of dataset. As a result, if the 
// dataset contains 2D cells such as polygons or triangles, the integration is
// constrained to lie on the surface defined by the 2D cells.
//
// The starting point of streamers may be defined in three different ways.
// Starting from global x-y-z "position" allows you to start a single streamer
// at a specified x-y-z coordinate. Starting from "location" allows you to 
// start at a specified cell, subId, and parametric coordinate. Finally, you 
// may specify a source object to start multiple streamers. If you start 
// streamers using a source object, for each point in the source that is 
// inside the dataset a streamer is created.
//
// vtkStreamer implements the integration process in the Integrate() method.
// Because vtkStreamer does not implement the Execute() method that its 
// superclass (i.e., Filter) requires, it is an abstract class. Its subclasses
// implement the execute method and use the Integrate() method, and then build
// their own representation of the integration path (i.e., lines, dashed 
// lines, points, etc.).

// .SECTION See Also
// vtkStreamLine vtkDashedStreamLine vtkStreamPoints

#ifndef __vtkStreamer_h
#define __vtkStreamer_h

#include "vtkPolyDataAlgorithm.h"

class vtkInitialValueProblemSolver;
class vtkMultiThreader;

#define VTK_INTEGRATE_FORWARD 0
#define VTK_INTEGRATE_BACKWARD 1
#define VTK_INTEGRATE_BOTH_DIRECTIONS 2

class VTK_GRAPHICS_EXPORT vtkStreamer : public vtkPolyDataAlgorithm
{
public:
  vtkTypeMacro(vtkStreamer,vtkPolyDataAlgorithm);
  void PrintSelf(ostream& os, vtkIndent indent);

  // Description:
  // Specify the start of the streamline in the cell coordinate system. That
  // is, cellId and subId (if composite cell), and parametric coordinates.
  void SetStartLocation(vtkIdType cellId, int subId, double pcoords[3]);

  // Description:
  // Specify the start of the streamline in the cell coordinate system. That
  // is, cellId and subId (if composite cell), and parametric coordinates.
  void SetStartLocation(vtkIdType cellId, int subId, double r, double s,
                        double t);

  // Description:
  // Get the starting location of the streamline in the cell coordinate system.
  vtkIdType GetStartLocation(int& subId, double pcoords[3]);

  // Description:
  // Specify the start of the streamline in the global coordinate
  // system. Search must be performed to find initial cell to start
  // integration from.
  void SetStartPosition(double x[3]);

  // Description:
  // Specify the start of the streamline in the global coordinate
  // system. Search must be performed to find initial cell to start
  // integration from.
  void SetStartPosition(double x, double y, double z);

  // Description:
  // Get the start position in global x-y-z coordinates.
  double *GetStartPosition();

  // Description:
  // Specify the source object used to generate starting points.
  void SetSource(vtkDataSet *source);
  vtkDataSet *GetSource();

  // Description:
  // Specify the maximum length of the Streamer expressed in elapsed time.
  vtkSetClampMacro(MaximumPropagationTime,double,0.0,VTK_DOUBLE_MAX);
  vtkGetMacro(MaximumPropagationTime,double);

  // Description:
  // Specify the direction in which to integrate the Streamer.
  vtkSetClampMacro(IntegrationDirection,int,
                   VTK_INTEGRATE_FORWARD,VTK_INTEGRATE_BOTH_DIRECTIONS);
  vtkGetMacro(IntegrationDirection,int);
  void SetIntegrationDirectionToForward()
    {this->SetIntegrationDirection(VTK_INTEGRATE_FORWARD);};
  void SetIntegrationDirectionToBackward()
    {this->SetIntegrationDirection(VTK_INTEGRATE_BACKWARD);};
  void SetIntegrationDirectionToIntegrateBothDirections()
    {this->SetIntegrationDirection(VTK_INTEGRATE_BOTH_DIRECTIONS);};
  const char *GetIntegrationDirectionAsString();

  // Description:
  // Specify a nominal integration step size (expressed as a fraction of
  // the size of each cell). This value can be larger than 1.
  vtkSetClampMacro(IntegrationStepLength,double,0.0000001,VTK_DOUBLE_MAX);
  vtkGetMacro(IntegrationStepLength,double);

  // Description:
  // Turn on/off the creation of scalar data from velocity magnitude. If off,
  // and input dataset has scalars, input dataset scalars are used.
  vtkSetMacro(SpeedScalars,int);
  vtkGetMacro(SpeedScalars,int);
  vtkBooleanMacro(SpeedScalars,int);

  // Description:
  // Turn on/off the creation of scalar data from vorticity information. 
  // The scalar information is currently the orientation value "theta"
  // used in rotating stream tubes. If off, and input dataset has scalars,
  // then input dataset scalars are used, unless SpeedScalars is also on.
  // SpeedScalars takes precedence over OrientationScalars.
  vtkSetMacro(OrientationScalars, int);
  vtkGetMacro(OrientationScalars, int);
  vtkBooleanMacro(OrientationScalars, int);

  // Description:
  // Set/get terminal speed (i.e., speed is velocity magnitude).  Terminal 
  // speed is speed at which streamer will terminate propagation.
  vtkSetClampMacro(TerminalSpeed,double,0.0,VTK_DOUBLE_MAX);
  vtkGetMacro(TerminalSpeed,double);

  // Description:
  // Turn on/off the computation of vorticity. Vorticity is an indication of
  // the rotation of the flow. In combination with vtkStreamLine and 
  // vtkTubeFilter can be used to create rotated tubes. 
  // If vorticity is turned on, in the output, the velocity vectors 
  // are replaced by vorticity vectors.
  vtkSetMacro(Vorticity,int);
  vtkGetMacro(Vorticity,int);
  vtkBooleanMacro(Vorticity,int);

  vtkSetMacro( NumberOfThreads, int );
  vtkGetMacro( NumberOfThreads, int );

  vtkSetMacro( SavePointInterval, double );
  vtkGetMacro( SavePointInterval, double );

  // Description:
  // Set/get the integrator type to be used in the stream line
  // calculation. The object passed is not actually used but
  // is cloned with NewInstance by each thread/process in the
  // process of integration (prototype pattern). The default is 
  // 2nd order Runge Kutta.
  void SetIntegrator(vtkInitialValueProblemSolver *);
  vtkGetObjectMacro ( Integrator, vtkInitialValueProblemSolver );

  // Description:
  // A positive value, as small as possible for numerical comparison.
  // The initial value is 1E-12.
  vtkSetMacro(Epsilon,double);
  vtkGetMacro(Epsilon,double);
  
protected:
  // Description:
  // Construct object to start from position (0,0,0); integrate forward;
  // terminal speed 0.0; vorticity computation off; integrations step length
  // 0.2; and maximum propagation time 100.0.
  vtkStreamer();
  ~vtkStreamer();

  // Integrate data
  void Integrate(vtkDataSet *input, vtkDataSet *source);

  // Controls where streamlines start from (either position or location).
  int StartFrom;

  // Starting from cell location
  vtkIdType StartCell;
  int StartSubId;
  double StartPCoords[3];

  // starting from global x-y-z position
  double StartPosition[3];

  //
  // Special classes for manipulating data
  //
  //BTX - begin tcl exclude
  //
  class StreamPoint {
  public:
    double    x[3];    // position 
    vtkIdType cellId;  // cell
    int       subId;   // cell sub id
    double    p[3];    // parametric coords in cell 
    double    v[3];    // velocity 
    double    speed;   // velocity norm 
    double    s;       // scalar value 
    double    t;       // time travelled so far 
    double    d;       // distance travelled so far 
    double    omega;   // stream vorticity, if computed
    double    theta;   // rotation angle, if vorticity is computed
  };

  class StreamArray;
  friend class StreamArray;
  class StreamArray { //;prevent man page generation
  public:
    StreamArray();
    ~StreamArray()
      {
        if (this->Array)
          {
          delete [] this->Array;
          }
      };
    vtkIdType GetNumberOfPoints() {return this->MaxId + 1;};
    StreamPoint *GetStreamPoint(vtkIdType i) {return this->Array + i;};
    vtkIdType InsertNextStreamPoint() 
      {
        if ( ++this->MaxId >= this->Size )
          {
          this->Resize(this->MaxId);
          }
        return this->MaxId; //return offset from array
      }
    StreamPoint *Resize(vtkIdType sz); //reallocates data
    void Reset() {this->MaxId = -1;};

    StreamPoint *Array;     // pointer to data
    vtkIdType MaxId;        // maximum index inserted thus far
    vtkIdType Size;         // allocated size of data
    vtkIdType Extend;       // grow array by this amount
    double Direction;       // integration direction
  };
  //ETX
  //

  //array of streamers
  StreamArray *Streamers;
  vtkIdType NumberOfStreamers;

  // length of Streamer is generated by time, or by MaximumSteps
  double MaximumPropagationTime;

  // integration direction
  int IntegrationDirection;

  // the length (fraction of cell size) of integration steps
  double IntegrationStepLength;

  // boolean controls whether vorticity is computed
  int Vorticity;

  // terminal propagation speed
  double TerminalSpeed;

  // boolean controls whether data scalars or velocity magnitude are used
  int SpeedScalars;

  // boolean controls whether data scalars or vorticity orientation are used
  int OrientationScalars;

  // Prototype showing the integrator type to be set by the user.
  vtkInitialValueProblemSolver* Integrator;

  // A positive value, as small as possible for numerical comparison.
  // The initial value is 1E-12.
  double Epsilon;
  
  // Interval with which the stream points will be stored.
  // Useful in reducing the memory footprint. Since the initial
  // value is small, by default, it will store all/most points.
  double SavePointInterval;

  static VTK_THREAD_RETURN_TYPE ThreadedIntegrate( void *arg );

  // Description:
  // These methods were added to allow access to these variables from the
  // threads. 
  vtkGetMacro( NumberOfStreamers, vtkIdType );
  StreamArray *GetStreamers() { return this->Streamers; };

  void InitializeThreadedIntegrate();
  vtkMultiThreader           *Threader;
  int                        NumberOfThreads;

  virtual int FillInputPortInformation(int port, vtkInformation *info);

private:
  vtkStreamer(const vtkStreamer&);  // Not implemented.
  void operator=(const vtkStreamer&);  // Not implemented.
};

// Description:
// Return the integration direction as a character string.
inline const char *vtkStreamer::GetIntegrationDirectionAsString()
{
  if ( this->IntegrationDirection == VTK_INTEGRATE_FORWARD ) 
    {
    return "IntegrateForward";
    }
  else if ( this->IntegrationDirection == VTK_INTEGRATE_BACKWARD ) 
    {
    return "IntegrateBackward";
    }
  else 
    {
    return "IntegrateBothDirections";
    }
}

#endif
