// 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.
//
// objcurvebound.h -- geometric bounded curve objects


#ifndef __OBJCURVEBOUND_H__
#define __OBJCURVEBOUND_H__
//=========================================================================================================================

#include "OOMathsExport.h"

//=========================================================================================================================
// CMaBoundedCurve - math library bounded curve (abstract base class)

class OOMATHS_API CMaBoundedCurve : public CMaCurve
{
public:
// destructor
   virtual ~CMaBoundedCurve();

// attributes
   // end point positions
   virtual CMaPoint GetStartPoint() const = 0;
   virtual CMaPoint GetEndPoint() const = 0;

   // end points data
   virtual void GetStartData(CMaPoint* p, CMaVector* t, CMaVector* c) const = 0;
   virtual void GetEndData  (CMaPoint* p, CMaVector* t, CMaVector* c) const = 0;
      // note: on end points data sequence
      //    1. point position
      //    2. tangent vector (direction significant)
      //    3. curvature vector (direction and magnitude significant, the latter is curvature radius)

// operations
   // bounding data
   virtual void BoundingBox(CMaPoint* pMin, CMaPoint* pMax) const = 0;
   virtual void BoundingCircle(double* radius, CMaPoint* centre) const = 0;

   // distance calculation
   virtual double DistanceTo(const CMaPoint& other) const = 0;
   virtual double DistanceTo(const CMaBoundedCurve* other, CMaPoint* closestP1, CMaPoint* closestP2) const = 0;
      // note: calculate the shortest distance between a bounded curve and an other object
      //    if they intersect, zero is returned
      //    point closestP1 lies on this object, point closestP2 lies on other object

   // length calculation
   virtual double Length() const = 0;

   // offsetting operations
   virtual CMaBoundedCurve* Offset(double distance) = 0;
   virtual CMaBoundedCurve* Offset(double distance, const CMaVector& direction, int minSize = 2) = 0;
      // note: on memory deallocation
      //    Use the "delete" operator upon function returned variables (of type CMaBoundedCurve*)
      //    to deallocate the memory when the offsetting results are no longer needed

protected:
// constructor
   CMaBoundedCurve();

// transformation
   CMaPoint  GetPointInSpace (const CMaPoint& point) const;
   CMaVector GetVectorInSpace(const CMaVector& vector) const;
};

//=========================================================================================================================
// CMaLineSegment - math library line segment (concrete class)

class OOMATHS_API CMaLineSegment : public CMaBoundedCurve
{
public:
// constructors
   CMaLineSegment();        // default segment: unit line along positive x axis
   CMaLineSegment(const CMaPoint& start, const CMaPoint& end);
   CMaLineSegment(const CMaLineSegment& other);   // copy

// destructor
  ~CMaLineSegment();

// attributes
   CMaPoint  GetStartPoint() const;
   CMaPoint  GetEndPoint() const;
   void GetStartData(CMaPoint* p, CMaVector* t, CMaVector* c) const;
   void GetEndData  (CMaPoint* p, CMaVector* t, CMaVector* c) const;

// operations
   // assignment
   CMaLineSegment& operator= (const CMaLineSegment& other);

   // comparison
   EMaBoolean IsEqual(const CMaLineSegment& other, double tolerance = MaGetLengthTol()) const;

   // parameterisation
   double GetParameter(const CMaPoint& point) const;
   CMaPoint GetPoint(double parameter) const;
      // note: point lies on the host line
      //    If a point lies on a line segment, its parameter is in the closed interval [0, 1].

   // length calculation
   double Length() const;

   // tangent
   CMaVector GetTangent() const;

   // bounding data
   void BoundingBox(CMaPoint* pMin, CMaPoint* pMax) const;
   void BoundingCircle(double* radius, CMaPoint* centre) const;

   // distance calculation
   double DistanceTo(const CMaPoint& other) const;
   double DistanceTo(const CMaPoint& other, CMaPoint* closestP1) const;
   double DistanceTo(const CMaLineSegment&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaBoundedCurve* other, CMaPoint* closestP1, CMaPoint* closestP2) const;
      // note: calculate the shortest distance between a line segment and an other object
      //    if they intersect, zero is returned
      //    point closestP1 lies on this object, point closestP2 lies on other object

   // offsetting
   CMaBoundedCurve* Offset(double distance);
   CMaBoundedCurve* Offset(double distance, const CMaVector& direction, int minSize = 2);
      // note: on offsetting reference direction
      //    the vector cross product of R and T defines the offsetting direction,
      //       where R is reference direction and T segment tangent direction
      //    default reference direction: (0, 0, 1) i.e. positive z axis
      // note: on offsetting distance
      //    positive: defines an offset in direction  R x T, 
      //    negative: defines an offset in direction -R x T, 
      //    zero:     defines no offset
      // note: on parameter minSize
      //    not used
      // note: on offsetting results
      //    always CMaLineSegment

   // intersection calculation (return number of intersections)
   int IntersectWith(const CMaLineSegment& other, CMaPoint* p) const;
   int IntersectWith(const CMaCircularArc& other, CMaPoint* p1, CMaPoint* p2, int reqTangentPoint = 1) const;
   int IntersectWith(const CMaCircle&      other, CMaPoint* p1, CMaPoint* p2) const;

private:
// implementation
   friend class CMaCompositeCurve;
   friend class CMaSimplifiedCurve;
   void Copy(const CMaLineSegment& other);
   bool BinaryDump(ofstream& stream);
   bool BinaryRead(ifstream& stream);

   CMaPoint  m_cP1;   // start point
   CMaPoint  m_cP2;   // end point
};

//=========================================================================================================================
// CMaCircularArc - math library circular arc (concrete class)

class OOMATHS_API CMaCircularArc : public CMaBoundedCurve
{
public:
// constructors
   CMaCircularArc();        // default arc: a quarter of unit circle in the first quadrant on z=0 plane
   CMaCircularArc(const CMaPoint& centre, const CMaPoint& start, const CMaPoint& end, EMaBoolean sense = Ma_TRUE);   // 2D, z axis ignored
   CMaCircularArc(const CMaPoint& centre, const CMaPoint& start, const CMaPoint& end, const CMaVector& normal, EMaBoolean sense = Ma_TRUE);
   CMaCircularArc(const CMaCircularArc& other);   // copy
      // note: on normal to the plane of the arc
      //    default normal: (0, 0, 1) i.e. positive z axis
      // note: on sense of circular arc
      //    looking in a direction opposite to the normal vector, the arc is traversed from start point
      //    to end point counter-clockwise if the sense is TRUE; traversed clockwise otherwise.

// destructor
  ~CMaCircularArc();

// attributes
   double     GetRadius() const;
   CMaPoint   GetCentre() const;
   CMaVector  GetNormal() const;
   CMaCircle  GetCircle() const;
   EMaBoolean GetSense()  const;

   CMaPoint GetStartPoint() const;
   CMaPoint GetEndPoint() const;
   void GetStartData(CMaPoint* p, CMaVector* t, CMaVector* c) const;
   void GetEndData  (CMaPoint* p, CMaVector* t, CMaVector* c) const;

// operations
   // assignment
   CMaCircularArc& operator= (const CMaCircularArc& other);

   // comparison
   EMaBoolean IsEqual(const CMaCircularArc& other, double tol1 = MaGetLengthTol(), double tol2 = MaGetRadianTol()) const;

   // parameterisation
   double GetStartParameter() const;
   double GetEndParameter() const;
   void GetParametricRange(double* t1, double* t2) const;
      // note: 0 < t2 - t1 <= Ma_2PI (radian)

   double GetParameter(const CMaPoint& point) const;
   CMaPoint GetPoint(double parameter) const;
      // note: point lies on the host circle, and
      //    its parameter is in the open interval [0, Ma_2PI)

   // arc length calculation
   double Length() const;

   // Required to create the baseline version of OOMaths
   double GetLength() const;

   // tangents and curvatures
   void GetTangents  (CMaVector* start, CMaVector* end) const;
   void GetCurvatures(CMaVector* start, CMaVector* end) const;
   void GetStartTangentCurvature(CMaVector* startT, CMaVector* startC) const;
   void GetEndTangentCurvature(CMaVector* endT, CMaVector* endC) const;

   CMaVector GetTangent  (double arcDistance) const;
   CMaVector GetCurvature(double arcDistance) const;
   CMaVector GetTangent  (const CMaPoint& point) const;
   CMaVector GetCurvature(const CMaPoint& point) const;
      // note: arcDistance is relative to the start point, must not be greater than the arc length
      // note: point must lie on the arc

   // bounding data
   void BoundingBox(CMaPoint* pMin, CMaPoint* pMax) const;
   void BoundingCircle(double* radius, CMaPoint* centre) const;

   // distance calculation
   double DistanceTo(const CMaPoint& other) const;
   double DistanceTo(const CMaPoint& other, CMaPoint* closestP1) const;
   double DistanceTo(const CMaLineSegment&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaCircularArc&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaBoundedCurve* other, CMaPoint* closestP1, CMaPoint* closestP2) const;
      // note: calculate the shortest distance between a circular arc and an other object
      //    if they intersect, zero is returned
      //    point closestP1 lies on this object, point closestP2 lies on other object

   // conversions
   CMaPolyline   ToPolyline  (int Size) const;   // Size > 1
   CMaPointArray ToPointArray(int Size) const;

   // offsetting
   CMaBoundedCurve* Offset(double distance);
   CMaBoundedCurve* Offset(double distance, const CMaVector& direction, int minSize = 2);
      // note: on offsetting reference direction
      //    default reference direction: normal to the plane of the arc
      // note: on offsetting results
      //    if reference direction is parallel to the normal to the plane of the arc
      //       returns CMaCircularArc;
      //    else returns CMaPolyline with minimum minSize points

   // intersection calculation (return number of intersections)
   int IntersectWith(const CMaLineSegment& other, CMaPoint* p1, CMaPoint* p2, int reqTangentPoint = 1) const;
   int IntersectWith(const CMaCircularArc& other, CMaPoint* p1, CMaPoint* p2, int reqTangentPoint = 1) const;
   int IntersectWith(const CMaLine&        other, CMaPoint* p1, CMaPoint* p2) const;
   int IntersectWith(const CMaCircle&      other, CMaPoint* p1, CMaPoint* p2) const;
   int IntersectWith(const CMaPlane&       other, CMaPoint* p1, CMaPoint* p2) const;
   
private:
// implementation
   friend class CMaCompositeCurve;
   friend class CMaSimplifiedCurve;
   void Copy(const CMaCircularArc& other);

   CMaVector CurvatureAt(double t) const;
   CMaVector TangentAt(double t) const;
   void GetPolylineWithTangents(CMaPolyline* points, CMaPointArray* tangents, int Size, int reqTan = 0) const;
   bool BinaryDump(ofstream& stream);
   bool BinaryRead(ifstream& stream);
   static CMaCircularArc SmallArc(CMaCompositeCurve& result, const CMaPoint& centre,
          const CMaPoint& start, const CMaPoint& end, const CMaVector& normal);

   double m_T1;        // parameters
   double m_T2;
   CMaPoint   m_cP1;   // start point
   CMaPoint   m_cP2;   // end point
   CMaCircle  m_cCircle;
   EMaBoolean m_bSense;
};

//=========================================================================================================================
// CMaPolyline - math library polyline (concrete class)

class OOMATHS_API CMaPolyline : public CMaBoundedCurve
{
public:
// constructors
   CMaPolyline(int Size = 2);                  // number of points > 1
   CMaPolyline(const CMaPolyline& other);      // copy
   CMaPolyline(const CMaPointArray& points);   // conversion

// destructor
  ~CMaPolyline();

// attributes
   int GetSize() const;                        // get number of points
   const CMaPoint* GetData() const;            // return C array
   CMaPoint* GetData();

   CMaPoint GetStartPoint() const;
   CMaPoint GetEndPoint() const;
   void GetStartData(CMaPoint* p, CMaVector* t, CMaVector* c) const;
   void GetEndData  (CMaPoint* p, CMaVector* t, CMaVector* c) const;

// operations
   // assignment and data element
   CMaPolyline& operator= (const CMaPolyline& other);
   CMaPoint& operator[] (int Index);
   CMaPoint  operator[] (int Index) const;

   // length calculation
   double Length() const;

   // bounding data
   void BoundingBox(CMaPoint* pMin, CMaPoint* pMax) const;
   void BoundingCircle(double* radius, CMaPoint* centre) const;

   // distance calculation
   double DistanceTo(const CMaPoint& other) const;
   double DistanceTo(const CMaPoint& other, CMaPoint* closestP1) const;
   double DistanceTo(const CMaLineSegment&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaCircularArc&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaPolyline&     other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaBoundedCurve* other, CMaPoint* closestP1, CMaPoint* closestP2) const;
      // note: calculate the shortest distance between a polyline and an other object
      //    if they intersect, zero is returned
      //    point closestP1 lies on this object, point closestP2 lies on other object

   // ordering
   void Reverse();

   // transformation
   void ReferInSpace();
      // note: on points referred in space coordinates
      //    necessary to be used when the curve has been translated or rotated;
      //    otherwise, operator[] and GetData will return untranslated or unrotated points

   // conversion
   CMaPointArray ToPointArray() const;

   // offsetting
   CMaBoundedCurve* Offset(double distance);
   CMaBoundedCurve* Offset(double distance, const CMaVector& direction, int minSize = 2);
      // note: on parameter minSize
      //    not used
      // note: on offsetting results
      //    always CMaPolyline

private:
// implementation
   void Copy(const CMaPolyline& other);

   int       m_nSize;     // size of array
   CMaPoint* m_pPoints;   // array of points
};

//=========================================================================================================================
// CMaCompositeCurve - math library composite curve (concrete class)

class OOMATHS_API CMaCompositeCurve : public CMaBoundedCurve
{
public:
// constructors
   CMaCompositeCurve();
   CMaCompositeCurve(const CMaCompositeCurve& other);   // copy

// destructor
  ~CMaCompositeCurve();


// Save into bin file

// attributes
   int GetSize() const;                          // get number of segments

   CMaPoint GetStartPoint() const;
   CMaPoint GetEndPoint() const;
   void GetStartData(CMaPoint* p, CMaVector* t, CMaVector* c) const;
   void GetEndData  (CMaPoint* p, CMaVector* t, CMaVector* c) const;

// operations
   // assignment
   CMaCompositeCurve& operator= (const CMaCompositeCurve& other);

   // accessing component curves
   void AddSegment(const CMaBoundedCurve& curve, EMaBoolean sense = Ma_TRUE);
   void GetSegment(int index, CMaBoundedCurve*& pCurve, int* continuity);
   void GetJointTangents  (int index, CMaVector* left, CMaVector* right);
   void GetJointCurvatures(int index, CMaVector* left, CMaVector* right);
      // note: on sense of segment
      //    line segment: if the sense is TRUE, the line is traversed from start point to end point;
      //                  otherwise traversed in opposite direction.
      //    circular arc: indicates whether or not the sense of segment agrees with the sense of the
      //                  component curve (see note in CMaCircularArc)
      // note: on continuity
      //    the attribute conveys transition property between one segment and the next;
      //    the continuity of the last segment defines the transition between its end and the start of the first segment
      //    -1: segments do not join, permitted only at the end of the curve to indicate it is not closed
      //     0: C0 continuous - segments join
      //     1: C1 continuous - segments join, tangent vectors are parallel at the joint
      //     2: C2 continuous - segments join, tangent vectors are parallel, curvature vectors are equal at the joint
      // note: on joint index (starting from 0)
      //    the ith joint: the end point of the ith segment
      //    left Tangent/Curvature: at the end point of the ith segment
      //    right Tangent/Curvature: at the start point of the (i+1)th segment,
      //                             in case that (i+1)th segment does not exist, 0th segment is used

   // length calculation
   double Length() const;

   // bounding data
   void BoundingBox(CMaPoint* pMin, CMaPoint* pMax) const;
   void BoundingCircle(double* radius, CMaPoint* centre) const;

   // distance calculation
   double DistanceTo(const CMaPoint& other) const;
   double DistanceTo(const CMaPoint& other, CMaPoint* closestP1) const;
   double DistanceTo(const CMaLineSegment&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaCircularArc&  other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaPolyline&     other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaCompositeCurve& other, CMaPoint* closestP1, CMaPoint* closestP2) const;
   double DistanceTo(const CMaBoundedCurve* other, CMaPoint* closestP1, CMaPoint* closestP2) const;
      // note: calculate the shortest distance between a composite curve and an other object
      //    if they intersect, zero is returned
      //    point closestP1 lies on this object, point closestP2 lies on other object

   // ordering
   void Reverse();

   // conversions
   CMaPolyline   ToPolyline  (int Size) const;   // Size > number of segments
   CMaPointArray ToPointArray(int Size) const;
      // note: on conversion results
      //    Size <= returned size < Size + number of segments

   // comparison
   EMaBoolean IsPlaneCurve(CMaPlane* plane, double tolerance = MaGetLengthTol()) const;
   EMaBoolean IsSelfIntersecting(int* index1, int* index2) const;
   EMaBoolean DuplicateSegmentsExist(int* index1, int* index2, double tol1 = MaGetLengthTol(), double tol2 = MaGetRadianTol()) const;
      // note: on IsPlaneCurve
      //    try to find a plane on which the curve lies
      // note: on IsSelfIntersecting
      //    try to find two intersecting segments (identified by indices)
      // note: on DuplicateSegmentsExist
      //    try to find two duplicate segments (identified by indices)

   // offsetting
   CMaBoundedCurve* Offset(double distance);
   CMaBoundedCurve* Offset(double distance, const CMaVector& direction, int minSize = 2);
      // note: on offsetting reference direction
      //    default reference direction:
      //       normal to the plane of the composite curve when the plane exists;
      //       otherwise is (0, 0, 1)
      // note: on rounding at joints
      //    extra circular arcs are created when the offset curve is longer than
      //    the original curve at joints
      // note: on offsetting results
      //    the locus of centres of a rolling circle, which moves along the base curve.
      //    possibly CMaLineSegment, CMaCircularArc, CMaPolyline with minimum minSize points

   bool BinaryDump(char* filePath);	// This method writes the binary data of the composite curve into a file mentioned by
									// the filePath.
   bool BinaryRead(char* filePath);	// This method reads the binary data from the file mentioned in filePath.

private:
// implementation
   void Copy(const CMaCompositeCurve& other);

   // transformation
   void ReferInSpace();

   // offset supports
   static void TrimOffset(CMaBoundedCurve*& pOff1, const CMaBoundedCurve* pOff2,
        int* pPrev, int* pNext, CMaPoint* pointPrev, CMaPoint* pointNext, int trimLeft = 0);

   static void TrimOffset(CMaBoundedCurve*& pOff, CMaPoint* offPoint, const CMaPoint& segPoint,
        double distance, const CMaVector& direction, int trimLeft = 0);

   void AddOffset(int index, int* jump, CMaCompositeCurve& result, CMaPoint& resultEnd, CMaPoint& segEnd,
        CMaPointArray& SegPoints, const CMaObjArray& OffsetCurves, CMaBoundedCurve*& pOff,
        const CMaBoundedCurve* pSeg, double distance, const CMaVector& direction) const;

   void AddClassifiedOffset(int k, int* jump, CMaCompositeCurve& result, const CMaBoundedCurve* pOff,
        CMaPoint& resultEnd, CMaPoint& segEnd, CMaPointArray& SegPoints,
        const CMaPoint& start, const CMaPoint& end, const CMaPoint& end2, double distance,
        const CMaVector& direction) const;

   int AddContinuousArcs(CMaCompositeCurve& result, CMaPoint& resultEnd, CMaPoint& segEnd,
        const CMaPointArray& SegPoints, double distance, const CMaVector& direction) const;

   void MoreSegmentPoints(int k, CMaPointArray& SegPoints) const;

   void ValidateSegmentPoints(CMaPointArray& SegPoints, double distance, const CMaVector& direction) const;

   CMaPoint ArcStartPoint(double distance, const CMaPoint& p0, const CMaPoint& p1, const CMaPoint& p2, const CMaVector& normal, int flag = 0) const;

   static int JumpPlus(const CMaBoundedCurve* pOff, const CMaPoint& segStart, const CMaVector& normal, double distance);

   static int IsOffsetInCircle(const CMaBoundedCurve* pOff, double radius, const CMaPoint& centre);

   static int IsFarAway(double distance, CMaPoint p, CMaPoint q);

   int IsOffsetValid(int* left, int* right, double distance, CMaPoint* segPoint, const CMaBoundedCurve* pOff) const;

   int ClassifyCircle(int* left, int* right, double radius, const CMaPoint& centre) const;

   CMaObjArray    m_Curves;
   CMaIntArray    m_Continuities;
   CMaDoubleArray m_BoundingRadii;
   CMaPointArray  m_BoundingCentres;

};

//=========================================================================================================================
// CMaSimplifiedCurve - math library simplified composite curve (concrete class)

class OOMATHS_API CMaSimplifiedCurve : public CMaCurve
{
public:
// constructor
   CMaSimplifiedCurve();

// destructor
  ~CMaSimplifiedCurve();

// operations
   void SetOffsetDistance(double distance);
   void SetOffsetDirection(const CMaVector& direction);
      // note: 
      //    default offsetting distance: 0.5
      //    default reference direction: (0, 0, 1) i.e. positive z axis

   void AddSegment(const CMaBoundedCurve& curve, EMaBoolean sense = Ma_TRUE);
      // note: on continuity
      //    simplified curve is at least a C0 continuous composite curve, and
      //    is normally an open curve

   EMaStatus GetOffset(CMaLineSegment& LineOffset);
   EMaStatus GetOffset(CMaCircularArc& ArcOffset);
   EMaStatus GetExtraArc(CMaCircularArc& ArcOffset) const;
   EMaStatus GetLastOffset(CMaLineSegment& LineOffset) const;
   EMaStatus GetLastOffset(CMaCircularArc& ArcOffset)  const;
      // note: on offsetting calculation
      //    solely based on two adjacent curve segments,
      //    also assume that each segment has a valid offset (but not checked)

// usage: CMaSimplifiedCurve differs from normal CMaCompositeCurve in having only two
//        curve segments stored each time.  It works in the following steps
//    (1) AddSegment
//    (2) AddSegment
//    (3) GetOffset
//    (4) GetExtraArc
//    (5) steps (2), (3), (4) repeated for several times
//    (6) GetLastOffset

private:
// implementation
   // default copy constructor and assignment disabled
   CMaSimplifiedCurve(const CMaSimplifiedCurve&);
   void operator=(const CMaSimplifiedCurve&);

   // transformation disabled
   void Translate(double, double, double) {}
   void Translate(const CMaPoint&) {}
   void Rotate(double, EAxisType) {}
   void Rotate(double, const CMaVector&) {}
   void Rotate(double, EAxisType, const CMaPoint&) {}
   void Rotate(double, const CMaVector&, const CMaPoint&) {}

   // attributes
   int    m_nValueSet;
   double m_Distance;
   CMaVector m_cNormal;
   EMaObjectType  m_nType1, m_nType2,  m_nType3;
   CMaLineSegment m_pLine1, m_pLine2, *m_pLineOff1, *m_pLineOff2;
   CMaCircularArc m_pArc1,  m_pArc2,  *m_pArcOff1,  *m_pArcOff2, m_pArc3;
};

//=========================================================================================================================
#endif // __OBJCURVEBOUND_H__