/**
 * This file describes the 2-D curve geomtry.
 * Classes described here are:
 * 		IGCurve2D, IGConicEndCenterEnd, IGArcThrough3Points, IGCardinalSpline,
 *		IGHermiteSpline, and IGTensionSpline.
 *
 *   IBM Open Class Library
 *   (C) Copyright International Business Machines Corporation,  1997
 *   Licensed Material - Program-Property of IBM - All Rights Reserved.
 *
 */

// Revision: 33 1.22.1.6 source/albert/graph2d/igcurv2d.hpp, 2d, ioc.v400, 980918 

#ifndef _IGCURV2D_
#define _IGCURV2D_

#include <igbase2d.hpp>
#include <irawarr.hpp>
#include <ipt2darr.hpp>
#include <ibcurve.hpp>

#if __IBMCPP__ >= 400
#pragma namemangling(compat)
#endif

class IGEllipse2D;
class IGLine2D;
class IGPolyline2D;
class IGPolygon2D;
class IGrafMatrix;

class IConicInfo;

#pragma pack(push,4)
#pragma enum(4)


/**
 * IGCurve2D is a non-uniform rational B-spline (NURB) of arbitrary degree.
 * IGCurve2D is a non-uniform rational B-spline (NURB) of arbitrary degree.
 * This representation gives exact conic curves, direct control over the continuity of the curve, and
 * no limitations on the number control points.
 * A number of tools are provided for defining and manipulating curves. Constructors are available to
 * make exact arcs, simple beziers(like in MacDraw or Illustrator) or curves.  Other methods refine,
 * evaluate and modify the curve.
 * In the current implementation IGCurve2D should not be subclassed, except for trivial subclasses to
 * add constructor methods(it doesn't use virtual methods).
 *
 * Note: Currently curves of order 7 or higher are not supported.
 *
 * Note: IRawArray does not check whether allocation is successful. Before construction,
 * one should check if there is enough memory and use a reasonable size.
 */

class IGCurve2D : public IBaseCurve<IGRPoint2D, IGPoint2D> {

  public:

    /**
	 * Will not render. The only operators allowed on empty curves are assignment and stream in.
	 */
    IGCurve2D();

    /**
	 */
    IGCurve2D( const IGPoint2D& p0, const IGPoint2D& p1, const IGPoint2D& p2 );

    /**
	 */
    IGCurve2D( const IGPoint2D& p0, const IGPoint2D& p1, const IGPoint2D& p2, const IGPoint2D& p3 );

    /**
	 * If knots is not specified a "pinned" knot vector is created.  If controlPoints (and
	 * knots) are specified, they are copied in.  Assertions fail if  number of points is less
	 * than the order, or if the order is less than two.  If the points are not given, the number
	 * specified in numberPoints are created, and all are set to the origin (w=1).
	 */
    IGCurve2D(unsigned long order, unsigned long numberPoints);

    /**
	 */
    IGCurve2D(
    	unsigned long order,
        unsigned long numberPoints,
        const IRawArray<GParametric>& knots);

    /**
	 */
    IGCurve2D(unsigned long order, const IGRPoint2DArray& points);

    /**
	 */
    IGCurve2D(
    	unsigned long order,
        const IGRPoint2DArray& points,
        const IRawArray<GParametric>& knots);

    /**
	 */
    IGCurve2D(const IGCurve2D& curve);

    /**
	 *  Sets the curve to an elliptical arc from angle1 to angle2.  Now, the Arc always goes
	 * "counterclockwise" (because that's how sin's and cos's go), BUT, because the coordinate
	 * system used is by default "upsidedown" from the mathematical one, the arcs go clockwise.
	 */
     IGCurve2D(
    	const IGEllipse2D& e,
        GDegrees angle1,
        GDegrees angle2);

    /**
	 */
     IGCurve2D(const IGLine2D& line);

    /**
	 */
    IGCurve2D(const IGPolyline2D& polyline);

    virtual ~IGCurve2D();

	/**
	 * Assignment operator.
	 */
    IGCurve2D& operator=(const IGCurve2D& Src);

    /**
     * Returns the point on the IGCurve2D at the specified parametric value. Computes the tangent, second derivative, and curvature at this point.
	 */
    IGPoint2D evaluate(
        GParametric u,
        IGPoint2D& tangent,
        IGPoint2D& deriv2,
        GCoordinate& curvature) const;

   /**
     * Returns the point on the IGCurve2D at the specified parametric value.
	 */
    IGPoint2D evaluate(GParametric u) const;

    /**
     * Returns the point (as a IGRPoint2D) on the IGCurve2D at the specified parametric value.
	 */
    IGRPoint2D evaluateW(GParametric u) const;

    /**
     * Returns the point on the IGCurve2D at the specified parametric value. And computes the tangent at this point.
	 */
    IGPoint2D evaluate( GParametric u, IGPoint2D& tangent ) const;

    /**
     * Returns the point on the IGCurve2D at the specified parametric value. Computes the tangent and second derivative at this point.
	 */
    IGPoint2D evaluate(
    	GParametric u,
    	IGPoint2D& tangent,
    	IGPoint2D& deriv2) const;

    /**
     * Returns, in its argument, the set of control points as a polyline.
	 */
    void controlPolyline(IGPolyline2D& polyline) const;

    /**
     * Returns the bounds of the curve's control points.
     * Returns the bounds of the curve's control points.
	 * Due to the convex hull property, the curve is always contained within its control points.
     * Note this bounds is not cached, but recomputed with every call.
	 */
    virtual IGRect2D bounds() const;

    /**
     * Returns true if the curve intersects the rectangle.
     * Returns true if the curve intersects the rectangle.
	 * NOTE:  This is currently implemented by discretizing the curve in something resembling
	 * screen space.  In the future, the implementation will be changed to a truly device
	 * independant one.
	 */
    bool intersects(const IGRect2D& g) const;

    /**
     * Transform all of the curve's control points by the specified matrix.
	 */
    void transformBy( const IGrafMatrix& mat );

#ifndef NO_CONIC_ACCELERATION_SUPPORT

	/**
	 * Changes the curve shape so that at parameter u, it goes through ToPoint.
	 */
    void dragPosition(
    	GParametric u,
    	const IGPoint2D& toPoint,
        GParametric segmentMinimum = 0.2,
        GParametric segmentMaximum = 0.8);

	/**
	 * Change curve shape so that at parameter u, it has the specified tangent.
	 * Change curve shape so that at parameter u, it has the specified tangent.
	 * Like dragCurve, but instead modifies the -tangent- at the u parameter given.  i.e., Adobe
	 * rocker arms you can put anyplace on the curve.
	 */
    void dragTangent(
    	GParametric u,
    	const IGPoint2D& toTangent,
        GParametric segmentMinimum = 0.2,
        GParametric segmentMaximum = 0.8);

	/**
	 * Reverses the order of a curve's points so the parameterization runs the opposite direction.  The geometric shape is unchanged.
	 */
    void reverseDirection();

    /**
     * Change order of curve. This DOES change the shape, but leaves the number of points intact.
     */
    void setOrder(unsigned long order);

	/**
	 * Replaces this curve's control point at the specified index with the specified point.
	 */
    void setPoint(unsigned long i, const IGRPoint2D& p);

	/**
	 * Copies points in from your heap block. (The heap block must have same number of points).
	 */
    void setPoints(const IRawArray<IGRPoint2D>& controlPoints);

	/**
	 * Sets the knot vector to one of three standard schemes (pinned, floating, or Bezier), without any refinement.
	 */
    void setKnotScheme(EndConditions theType = kPinned);

	/**
	 * Resets this curve's knot vector to the specified array.
	 * Resets this curve's knot vector to the specified array.
	 * The data is copied into the curve, and must have the same number of knots as the curve's
	 * existing knot vector. No refinement or consistency checking is performed on this knot vector.
	 */
    void setKnots(const IRawArray<GParametric>& knots);

    /**
     * Moves the knot at index i to u. May re-sort knots to ensure knot(i+1)>=knot(i) for all i.
	 */
    void moveKnot(unsigned long index, GParametric u);

    /**
     * Inserts a single new knot (and the corresponding control point) at the specified parametric location.
	 */
    void insertKnot(GParametric u);

    IDataStream& operator>>=(IDataStream&) const;
    IDataStream& operator<<=(IDataStream&);
	void writeToStream(IDataStream& toWhere) const;
	void readFromStream(IDataStream& toWhere);

#endif // NO_CONIC_ACCELERATION_SUPPORT

    // Make compatible forces two(or N) curves to have the same
    // order, number of points, and knot vectors, without changing the
    // geometric shape of any of the curves.  This is accomplished by
    // raising the order and refining the knot vector as necessary.
    // static void makeCompatible(ICurveList& curveList);

    /**
     * Modifies both curves to have the same order, the same number of points, and the same knot vector, without changing the shape of either curve.
     * Modifies both curves to have the same order, the same number of points, and the same knot vector, without changing the shape of either curve.
	 *  This is accomplished by raising the order and refining the knot vector as necessary.
	 */
    static void makeCompatible(IGCurve2D& curve1, IGCurve2D& curve2);

  protected:

#ifndef NO_CONIC_ACCELERATION_SUPPORT
	/**
	 * Pre-appends the given curve at the beginning of the current curve.
	 * Pre-appends the given curve at the beginning of the current curve.
  	 * Glue parameter determines whether joining beginning and end points should be merged(glue==true)
	 * or left as double control points.
	 */
    void prAppendCurve(IBaseCurve<IGRPoint2D, IGPoint2D>& crv, bool glue);
#endif // NO_CONIC_ACCELERATION_SUPPORT

private:
friend class IGConicEndCenterEnd;
friend class IGArcThrough3Points;
friend class IGLoop2D;
friend class IGPie2D;
    void makeArc(unsigned long numPoints, IGRPoint2DArray& pts);
	void makeEllipticCurve(IGEllipse2D ellipse);

#ifndef NO_CONIC_ACCELERATION_SUPPORT
friend class IGCardinalSpline;
friend class IGHermiteSpline;
friend class IGTensionSpline;
    void setBasisSpline(const BasisMatrix, unsigned long step);

friend class IGrafDevice;
friend class ConicMaker;
	IConicInfo* fConicInfo;
#endif // NO_CONIC_ACCELERATION_SUPPORT
};

/**
 * IGConicEndCenterEnd is one of several lightweight classes derived from IGCurve2D.
 * IGConicEndCenterEnd is one of several lightweight classes derived from IGCurve2D.
 * Its sole purpose is to disambiguate the constructors for arcs having similar arguments.
 * It creates a curve as a conic with tangents perpendicular to ab and bc and centered about the second point.
 * Once created, all of the curve methods apply.  Applications shouldn't define variables with these
 * class; use IGCurve2D and rely on these only for their constructors
 * (e.g., IGCurve2D myArc = IGConicEndCenterEnd(a, b, c); ). These classes may be replaced in the future
 * with inline constructors contained within the curve class.
 */

class IGConicEndCenterEnd : public IGCurve2D
{
  public:

	/**
	 * Constructs a conic arc with tangents perpendicular to p0p1 and p2p1 and centered about p1.
	 * Constructs a conic arc with tangents perpendicular to p0p1 and p2p1 and centered about p1.
	 * If the vectors p0p1 and p2p1 are equal, then this generates a circular arc of less than 180
	 * degrees.  The arc goes from p0 to p2, with the center of the circle(or ellipse) at center.
	 * If the "otherArc" boolean is set to true, then then all of the circle EXCEPT for the portion
	 * is created.
	 */
    IGConicEndCenterEnd(
            const IGPoint2D& p0,
            const IGPoint2D& centerPt,
            const IGPoint2D& p2,
            bool otherArc = false );

	/**
	 * Default constructor which does nothing.
	 */
    IGConicEndCenterEnd();

	/**
	 * Copy constructor.
	 */
    IGConicEndCenterEnd(const IGConicEndCenterEnd&);

    virtual ~IGConicEndCenterEnd();
};

/**
 * IGArcThrough3Points is one of several lightweight classes derived from IGCurve2D.
 * IGArcThrough3Points is one of several lightweight classes derived from IGCurve2D.
 * Its sole purpose is to disambiguate the constructors for arcs having similar arguments.
 * It creates a curve as a circular arc that passes through the three given points.
 */

class IGArcThrough3Points : public IGCurve2D
{
  public:

	/**
	 * Constructs a circular arc that passes through the three specified points, in the order given.
	 */
    IGArcThrough3Points(
        const IGPoint2D& p0,
        const IGPoint2D& p1,
        const IGPoint2D& p2);

	/**
	 * Default constructor which does nothing.
	 */
    IGArcThrough3Points();

	/**
	 * Copy constructor.
	 */
    IGArcThrough3Points(const IGArcThrough3Points&);

    virtual ~IGArcThrough3Points();
};

/**
 * IGCardinalSpline is a cubic "matrix" spline where the curve interpolates the control points.
 * IGCardinalSpline is a cubic "matrix" spline where the curve interpolates the control points.
 * IGCardinalSpline is one of several lightweight classes derived from IGCurve2D.
 * Its sole purpose is to disambiguate the constructors for cubic splines having similar arguments.
 * The tension parameter controls how tightly the curve interpolates the control polyline.  This value
 * ranges from 0.0(which is so tight it's almost the control mesh) to 1.0 which is fairly loose.
 */

class IGCardinalSpline : public IGCurve2D
{
  public:

	/**
	 * Creates a curve as a cubic matrix spline, where the curve interpolates the control points.
	 * The tension parameter controls how tightly the curve interpolates the points.
	 */
    IGCardinalSpline( const IGPoint2DArray& points,
                      GCoordinate tension = 1.0 );
	/**
	 * Default constructor which does nothing.
	 */
    IGCardinalSpline();

	/**
	 * Copy constructor.
	 */
    IGCardinalSpline(const IGCardinalSpline&);

    virtual ~IGCardinalSpline();
};

/**
 * IGHermiteSpline is a cubic "matrix" spline where the curve is defined by point/tangent pairs.
 * IGHermiteSpline is a cubic "matrix" spline where the curve is defined by point/tangent pairs.
 * IGHermiteSpline is one of several lightweight classes derived from IGCurve2D.
 * Its sole purpose is to disambiguate the constructors for cubic splines having similar arguments.
 * Note the number of points must be a multiple of four (else assertion).
 */

class IGHermiteSpline : public IGCurve2D
{
  public:

	/**
	 * Creates a curve as a cubic matrix spline, where the curve is defined by point/tangent pairs.
	 */
    IGHermiteSpline( const IGPoint2DArray& points );

	/**
	 * Default constructor which does nothing.
	 */
    IGHermiteSpline();

	/**
	 * Copy constructor.
	 */
    IGHermiteSpline(const IGHermiteSpline&);

    virtual ~IGHermiteSpline();
};

/**
 * IGTensionSpline is a cubic matrix spline, where the curve is defined by points, and interpolation is controlled by bias and tension values.
 * IGTensionSpline is a cubic matrix spline, where the curve is defined by points, and interpolation is controlled by bias and tension values.
 * IGTensionSpline is one of several lightweight classes derived from IGCurve2D.
 * Its sole purpose is to disambiguate the constructors for cubic splines having similar arguments.
 * Note the number of points must be a multiple of four (else assertion).
 *
 * Note: IRawArray does not check whether allocation is successful. Before construction,
 * one should check if there is enough memory and use a reasonable size.
 */

class IGTensionSpline : public IGCurve2D
{
  public:

	/**
	 * Constructs a curve as a cubic matrix spline, where the curve is defined by points, and interpolation is controlled by bias and tension values.
	 */
    IGTensionSpline( const IGPoint2DArray& points,
                     GCoordinate tension, GCoordinate bias );

	/**
	 * Default constructor which does nothing.
	 */
    IGTensionSpline();

	/**
	 * Copy constructor.
	 */
    IGTensionSpline(const IGTensionSpline&);

    virtual ~IGTensionSpline();
};

#pragma enum(pop)
#pragma pack(pop)

#if __IBMCPP__ >= 400
#pragma namemangling()
#endif

#include <igcurv2d.inl>

#endif // _IGCURV2D_
