// This is a part of the Object Oriented Mathematical Library
// Copyright (C) 1999-2008 Renishaw plc
//
// This source file is the confidential property of Renishaw plc.
// Reproduction or transmission in whole or in part, in any form or
// by any means, electronic, mechanical or otherwise, is prohibited
// without the prior written consent of the copyright owner.
//
// objgeome.h -- geometric objects


#ifndef __OBJGEOME_H__
#define __OBJGEOME_H__


// ========================================================================================================================
#include "OOMathsExport.h"
#include "ObjExcep.h"
////////////////////////////////////////////////////////////////////////////////
// CMaGeometry - math library geometry (abstract base class)

class OOMATHS_API CMaGeometry : public CMaObject
{
public:
// types of coordinate axis, curve side and surface side
   enum EAxisType {Xaxis, Yaxis, Zaxis};
   enum ELineSideType {Leftside, Rightside, OnL};
   enum ECurveSideType {Inside, Outside, OnC};
   enum ESurfaceSideType {Positive, Negative, OnS};
      // note: on line side
      //    In a given plane, looking along a line direction vector, the line divides
      //    points on the plane into three sets:
      //    leftside of the line, rightside of the line, and on the line.
      // note: on curve side
      //    A conic section divides points on its plane into three sets:
      //    inside of the curve, outside of the curve, and on the curve.
      //    The curve's standard algebraic expression has value negative,
      //    positive and zero respectively.
      // note: on surface side
      //    A quadric surface (plane included) divides all points in space into three sets:
      //    on positive side of the surface, on negative side of the surface,
      //    and on the surface.  The surface's standard algebraic expression has
      //    value positive, negative and zero respectively.

// destructor
   virtual ~CMaGeometry(){}

// fitting data check and projection
   static int AreCoplanar (const CMaPointArray& points, double tolerance = MaGetLengthTol());
   static int AreCoplanar (const CMaPointArray& points, CMaPlane* plane, double tolerance = MaGetLengthTol());
   static int AreCollinear(const CMaPointArray& points, CMaVector* planeNormal, double tolerance = MaGetLengthTol());
   
   // If Points are distinct return 1 else return 0
   static int NumberDistinctPoints(const CMaPointArray& points, int n, double tolerance = MaGetLengthTol());

   static CMaPointArray Projection2D(const CMaPointArray& points, CMaVector& zaxis);
   static CMaPointArray Projection2D(const CMaPointArray& points, const CMaPoint& centre, const CMaVector& xaxis, const CMaVector& yaxis);

// rotation matrix
   static CMaMatrix RotationMatrix(double angle, EAxisType axis);
   static CMaMatrix RotationMatrix(double angle, const CMaVector& axis);

protected:
// constructor
	CMaGeometry(){}
};

// ========================================================================================================================
////////////////////////////////////////////////////////////////////////////////
// CMaNonLinearGeometry - math library non-linear geometry (abstract base class)

class OOMATHS_API CMaNonLinearGeometry : public CMaGeometry
{
public:
// destructor
   virtual ~CMaNonLinearGeometry();

// transformation operations
   virtual void Translate(double x, double y, double z = 0.0);   // incremental translation of object's reference point
   virtual void Translate(const CMaPoint& newpos);               // absolute translation of object's reference point
   virtual void Rotate(double angle, EAxisType axis);            // rotate around a Cartesian coordinate axis
   virtual void Rotate(double angle, const CMaVector& axis);     // rotate around an arbitrary axis
   virtual void Rotate(double angle, EAxisType axis, const CMaPoint& centre);       // rotation centre specified
   virtual void Rotate(double angle, const CMaVector& axis, const CMaPoint& centre);
      // note: on rotation angle
      //    looking in a direction opposite to the rotation axis vector, positive angle defines
      //    a counter-clockwise rotation and negative angle defines clockwise rotation;
      //    all angles are in degrees by default
      // note: on rotation centre
      //    centre should be on the rotation axis

// transformation and its inverse
   CMaMatrix GetRotation() const;                // from local to space
   CMaVector GetTranslation() const;
   CMaMatrix GetTransformation() const;
   CMaMatrix GetInverseRotation() const;         // from space to local
   CMaVector GetInverseTranslation() const;
   CMaMatrix GetInverseTransformation() const;

// attribute
   EMaBoolean IsPositionSet() const;
   void CopyPosition(const CMaNonLinearGeometry& other);

protected:
// constructor
   CMaNonLinearGeometry();

// attributes of local coordinate system
   void GetPosition(CMaPoint* origin, CMaVector* xaxis, CMaVector* zaxis) const;
   void SetPosition(const CMaPoint& origin, const CMaVector& xaxis, const CMaVector& zaxis);

// implementation
   CMaCartesianCoord* m_pPosition;   // location and orientation of object in space

private:
// transformation matrix
   void Rotate_1(const CMaMatrix& R);
   void Rotate_2(const CMaMatrix& R, const CMaPoint& centre);
};

// ========================================================================================================================
////////////////////////////////////////////////////////////////////////////////
// CMaPoint - math library point (concrete class)

class OOMATHS_API CMaPoint : public CMaGeometry
{
public:
// constructors
   CMaPoint(double x = 0.0, double y = 0.0, double z = 0.0);
   CMaPoint(const CMaPoint& other);   // copy

// destructor
   ~CMaPoint(){}

// attributes
   inline double x() const{ return m_X; }
   inline double y() const{ return m_Y; }
   inline double z() const{ return m_Z; }
   inline double& x(){ return m_X; }
   inline double& y(){ return m_Y; }
   inline double& z(){ return m_Z; }
   void GetData(double* x, double* y, double* z) const;

// operations
   
   //Assignment operation
   CMaPoint& operator= (const CMaPoint& other);
   
   // conversions
   operator CMaVector() const;        // equivalent to To3DVector
   CMaVector To3DVector() const;
   CMaVector To4DVector() const;

   // comparison
   EMaBoolean IsZero(double tolerance = MaGetZero()) const;
   EMaBoolean IsEqual(const CMaPoint& other, double tolerance = MaGetLengthTol()) const;

   // arithmetic
   CMaPoint operator+ (const CMaPoint& other) const;
   CMaPoint operator- (const CMaPoint& other) const;
   CMaPoint operator* (double number)         const;
   CMaPoint operator/ (double number)         const;

   CMaPoint operator- () const;

   CMaPoint& operator+= (const CMaPoint& other);
   CMaPoint& operator-= (const CMaPoint& other);
   CMaPoint& operator*= (double number);
   CMaPoint& operator/= (double number);

   // transformation
   void Translate(double x, double y, double z = 0.0);    // incremental translation
   void Translate(const CMaPoint& newpos);                // absolute translation to a new position
   void Rotate(double angle, EAxisType axis);             // rotate around a Cartesian coordinate axis
   void Rotate(double angle, const CMaVector& axis);      // rotate around an arbitrary axis
   void Rotate(double angle, EAxisType axis, const CMaPoint& centre);       // rotation centre specified
   void Rotate(double angle, const CMaVector& axis, const CMaPoint& centre);
   void MoveTo(const CMaNonLinearGeometry& other);        // transform into other's local coordinate system
   void MoveTo(const CMaPoint& origin, const CMaVector& xaxis, const CMaVector& zaxis);   // to new coordinate system

   // symmetry calculation
   CMaPoint MidPoint(const CMaPoint& other) const;
   CMaPoint MidPoint(const CMaLine&  other) const;
   CMaPoint MidPoint(const CMaPlane& other) const;
   CMaPlane MidPlane(const CMaPoint& other) const;
   CMaPlane MidPlane(const CMaLine&  other) const;
      // note: on MidPlane
      //    normal pointed to the first point

   // distance calculation
   double DistanceTo(const CMaPoint& other) const;
   double DistanceTo(const CMaLine&  other) const;
   double DistanceTo(const CMaPlane& other) const;
   double DistanceTo(const CMaCurve& curve) const;
   double DistanceTo(const CMaSurface& surface) const;

   // projection, classification
   CMaPoint ProjectionOn(const CMaLine& other) const;
   CMaPoint ProjectionOn(const CMaPlane& other) const;
   CMaPoint ProjectionOn(const CMaSurface& other) const;

   ELineSideType    LineSide(const CMaLine& other, double tolerance = MaGetLengthTol()) const;
   ELineSideType    LineSide(const CMaLine& other, const CMaVector& normal, double tolerance = MaGetLengthTol()) const;
   ECurveSideType   CurveSide(const CMaGeometry& ConicCurve, double tolerance = MaGetLengthTol()) const;
   ESurfaceSideType SurfaceSide(const CMaGeometry& QuadricSurface, double tolerance = MaGetLengthTol()) const;

   // exchange
   static void Swap(CMaPoint& p1, CMaPoint& p2);
   
   bool BinaryDump(ofstream& stream);
   bool BinaryRead(ifstream& stream);
private:  

// implementation
   double m_X;
   double m_Y;
   double m_Z;
};

// ========================================================================================================================
////////////////////////////////////////////////////////////////////////////////
// CMaVector - math library vector (concrete class)

class OOMATHS_API CMaVector : public CMaGeometry
{
public:
// constructors
   CMaVector(double x = 0.0, double y = 0.0, double z = 0.0);   // default 3D vector
   CMaVector(int Size);                     // dimension specified (> 0) zero vector
   CMaVector(const CMaVector& other);       // copy

// destructor
   ~CMaVector();

// attributes
   int GetSize() const;                     // get dimension
   const double* GetData() const;           // return C array
   double* GetData();

// operations
   // assignment and data element
   CMaVector& operator= (const CMaVector& other);
   inline double& operator[] (int Index)
   {
	   CheckIndex(Index);
	   return m_pV[Index];
   }
   inline double  operator[] (int Index) const
   {
	   CheckIndex(Index);
	   return m_pV[Index];
   }

   // access to subset
   CMaVector SubVector(int i, int j) const;
      // note:
      //    return a vector with elements [i]..[j], i <= j

   // conversions
   void Homogeneous();           // vector promotion from 3D to 4D, in format of (hx, hy, hz, h)
   void NonHomogeneous();        // vector  demotion from 4D to 3D, in format of (hx, hy, hz) where h=1
   CMaPoint To3DPoint() const;   // from 3D/4D vector
   operator CMaComplexVector() const;

   // comparison
   EMaBoolean IsPositive() const;
   EMaBoolean IsNonNegative() const;
   EMaBoolean IsZero(double tolerance = MaGetZero()) const;
   EMaBoolean IsEqual(const CMaVector& other, double tolerance = MaGetZero()) const;
   EMaBoolean IsParallelTo  (const CMaVector& other, double tolerance = MaGetRadianTol()) const;
   EMaBoolean IsOrthogonalTo(const CMaVector& other, double tolerance = MaGetRadianTol()) const;

   // arithmetic
   CMaVector operator+ (const CMaVector& other) const;   // addition
   CMaVector operator- (const CMaVector& other) const;   // subtraction
   double    operator% (const CMaVector& other) const;   // dot product
   CMaVector operator& (const CMaVector& other) const;   // element-by-element product
   CMaVector operator* (const CMaVector& other) const;   // cross product
   CMaVector operator* (const CMaMatrix& other) const;   // vector matrix product
   CMaVector operator* (double number)          const;   // scalar product
   CMaVector operator/ (double number)          const;   // scalar division

   CMaVector operator- () const;                         // unary negation

   CMaVector& operator+= (const CMaVector& other);       // add equal
   CMaVector& operator-= (const CMaVector& other);       // subtract equal
   CMaVector& operator*= (double number);                // multiply equal
   CMaVector& operator/= (double number);                // divide equal

   OOMATHS_API friend CMaVector operator* (double number, const CMaVector& vector);   // scalar product

   // transformation
   void Translate(double, double, double) const;         // translation has no effect on vector
   void Translate(const CMaPoint&) const;
   void Rotate(double angle, EAxisType axis);            // rotate around a Cartesian coordinate axis
   void Rotate(double angle, const CMaVector& axis);     // rotate around an arbitrary axis
   void Rotate(double angle, EAxisType axis, const CMaPoint& centre);          // rotation centre has no effect
   void Rotate(double angle, const CMaVector& axis, const CMaPoint& centre);   // rotation centre has no effect

   // angle calculation
   double AngleWith(const CMaVector& other) const;
      // note: on return value
      //    0 <= return value <= 180 (degree)
      // note: on angle between two vectors
      //    the angle is formed by rotating counter-clockwise the first vector to the position of
      //    the second vector, taking the cross product of two vectors as the rotation axis.
   double AngleWith(const CMaVector& other, const CMaVector& axis) const;
      // note: on return value
      //    0 <= return value < 360 (degree)
      // note: on rotation axis
      //    two vectors have a same angle with the rotation axis, otherwise the axis is invalid

   // other related calculation
   void Normalise();
   double Magnitude() const;
   double Sum() const;

   // orthonormal set
   void OrthonormalY(CMaVector* yaxis);
      // for 2D vector: this (as xaxis) and yaxis

   void OrthonormalX(CMaVector* xaxis);
   void OrthonormalXY(CMaVector* xaxis, CMaVector* yaxis);
      // for 3D vector: xaxis, yaxis and this (as zaxis)

   void SymmetricAxis(double tolerance = MaGetLengthTol(), int positive = 1);
      // note: make largest component positive and small components zero

   // ordering
   void Sort();                           // in ascending order
   void Sort(CMaIntArray* indexTable) const;

   // searching
   double Max() const;
   double Min() const;

   bool BinaryDump(char* filename) const;
   bool BinaryRead(char* filename);

private:
   
// implementation
   void Init(int Size);
   void Copy(const CMaVector& other);
	
   double* m_pV;         // array of vector data V[maxsize]
   int     m_nSize;      // number of elements
   int     m_nMaxSize;   // max number of elements allocated

   private:
   /// Inline private functions for speed gain
   inline void CheckIndex(const int& nIndex)const
   {
	   if (CMaException::IsAssertEnabled()) assert(0 <= nIndex && nIndex < m_nSize);
	   if (nIndex < 0 || m_nSize <= nIndex)
		   throw CMaOperationException(CMaOperationException::badIndex,
		   "element operator[]", "CMaVector", 0, m_nSize-1);
   }

};

// ========================================================================================================================
////////////////////////////////////////////////////////////////////////////////
// CMaCartesianCoord - math library Cartesian coordinate system (concrete class)

class OOMATHS_API CMaCartesianCoord : public CMaGeometry
{
public:
// constructor
   CMaCartesianCoord();

// destructor
   ~CMaCartesianCoord();

// attributes
   CMaPoint  GetOrigin() const;
   CMaVector GetLocalXaxis() const;
   CMaVector GetLocalZaxis() const;
   void GetData(CMaPoint* origin, CMaVector* xaxis, CMaVector* zaxis) const;

   void SetOrigin    (const CMaPoint& origin);
   void SetLocalXaxis(const CMaVector& xaxis);
   void SetLocalZaxis(const CMaVector& zaxis);
   void SetData(const CMaPoint& origin, const CMaVector& xaxis, const CMaVector& zaxis);

private:
// implementation
   CMaPoint  m_cOrigin;   // local coordinate system: position and orientation
   CMaVector m_cXaxis;
   CMaVector m_cZaxis;
};

// ========================================================================================================================

#endif // __OBJGEOME_H__
// ========================================================================================================================
