/**
 * This file describes the 2-D loop geomtry.
 * Classes described here are:
 * 		IGLoop2D and IGPie2D.
 *
 *   IBM Open Class Library
 *   (C) Copyright International Business Machines Corporation,  1997
 *   Licensed Material - Program-Property of IBM - All Rights Reserved.
 *
 */

// Revision: 36 1.17.1.6 source/albert/graph2d/igloop2d.hpp, 2d, ioc.v400, 980918 

#ifndef _IGLOOP2D_
#define _IGLOOP2D_

#include <igbase2d.hpp>
#include <irawarr.hpp>
#include <ipt2darr.hpp>
#include <igcurv2d.hpp>
#include <iprimtyp.hpp>

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

class ICurveList;   // defined in CurveList.h

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

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

/**
 * IGLoop2D is a subclass of IGCurve2D that draws a filled shape.
 * IGLoop2D is a subclass of IGCurve2D that draws a filled shape.
 * If no knot vector is given, a "floating closed" loop is created.  This uses a uniform knot vector
 * that wraps around in a continous fasion. If a pinned knot vector is used, then the loop behaves
 * like an IGCurve2D that is closed with a straight line.
 * For continously "floating closed" loops, IGCurve2D methods that deal with the parameter range,
 * evaluation, etc. are modified to take into account the continously closed nature of the loop.  If
 * the loop is modified so that it has a pinned knot vector (or the ends of the knot vector range don't
 * match) then the spell is broken, and the loop again behaves as an IGCurve2D, closed with a straight
 * line at render time.
 * For some applications, a closed loop where the start and end control points are coincedent may be
 * preferable; calling "closeLoop()" on a loop (floating or pinned) will create this configuration
 * without changing the shape. If c0 discontinuities are encountered in the curve defining the loop,
 * they are closed separately when the loop is rendered (internal segments are closed with straight
 * lines). The direction of each piece is used to determine whether a point is inside or outside the
 * loop, along with the setting of the EOFill flag.
 *
 * 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 IGLoop2D : public IGCurve2D {

	public :
        IDataStream&    operator>>= (IDataStream&) const;
        IDataStream&    operator<<= (IDataStream&);
		void writeToStream( IDataStream& toWhere ) const;
		void readFromStream( IDataStream& toWhere );
  public:
    /**
     * The only operators allowed on empty loops are assignment and stream in.
     */
    IGLoop2D();

	/**
	 */
    IGLoop2D( const IGLoop2D& );

	/**
	 */
    IGLoop2D( const IGCurve2D&, bool EOFill = true );

	/**
	 */
    IGLoop2D( unsigned long order,
            unsigned long numberPoints,
            bool EOFill = true );

	/**
	 */
    IGLoop2D( unsigned long order,
            unsigned long numberPoints,
            const IRawArray<GParametric>& knots,
            bool EOFill = true );

	/**
	 */
    IGLoop2D( unsigned long order,
            const IGRPoint2DArray& points,
            bool EOFill = true );

	/**
	 */
    IGLoop2D( unsigned long order,
            const IGRPoint2DArray& points,
            const IRawArray<GParametric>& knots,
            bool EOFill = true );

    /**
     * The "seam" is at the top left corner.
     */
    IGLoop2D( const IGRect2D& rect );

    /**
     * The "seam" is at the rightmost edge of the ellipse.
     */
    IGLoop2D( const IGEllipse2D& ellipse );

    /**
	 * The "seam" is at the bottom of the top right corner.
	 * The rounded rectangle has sixteen control points: four at the corners, four at the midpoints
	 * of the sides, and two on either side of each corner. The aspect parameter controls how far
	 * the latter points are from each corner.
     */
    IGLoop2D( const IGRect2D& roundRect, const IGPoint2D& aspect );

    /**
     * The "seam" is at the bottom of the top right corner.
     */
    IGLoop2D( const IGPolygon2D& polygon );

    virtual ~IGLoop2D();

	/**
	 * Determines which of the two filling rules is to be used.
	 */
    bool    eOFill() const;

	/**
	 * Sets the even-odd rule fill flag to the specified value.
	 */
    void        setEOFill(bool EOFill);

	/**
	 * CloseLoop refines the loop so that the start and ending control points are coincident.
	 * CloseLoop refines the loop so that the start and ending control points are coincident.
	 * Forces a loop to be closed, so the endpoints of the control polygon actually meet.  If the
	 * loop was defined with a floating knot vector, then points are added to retain the original
	 * (continous) shape.  If a loop is aready closed (last point equals first, pinned knot vector)
	 * nothing changes.  If a loop has a pinned (or other) knot vector, a straight line segment is
	 * added to close it.
	 */
    void        closeLoop();

	/**
	 * Determines whether this IGLoop2D intersects the specified rectangle.
	 */
    bool        intersects( const IGRect2D& g) const;

	/**
	 * Determines whether this IGLoop2D contains the specified point.
	 */
    bool        contains( const IGPoint2D& p ) const;

	/**
	 * Returns the bounds of the loop's control points.
	 */
    virtual IGRect2D bounds() const;

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

    /**
	 * Tests two IGLoop2D objects for equality, by seeing whether the geometries are identical.
	 */
    bool        operator==( const IGLoop2D& Src ) const;

    /**
	 * Tests two IGLoop2D objects for inequality, by seeing whether the geometries are identical.
	 */
    bool        operator!=( const IGLoop2D& Src ) const;

    // The following are duplicated from the IGCurve2D interface to provide
    // expected behavior for floating closed loops.

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

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

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

    /**
     * Returns the point on the IGLoop2D at the specified parametric value. Computes the tangent and second derivative at this point.
	 */
    IGPoint2D evaluate( GParametric u,
                          IGPoint2D& tangent,
                          IGPoint2D& deriv2 ) const;
    /**
     * Returns the point on the IGLoop2D 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 arc length of (an optionally specified) section of the loop.
	 */
    GCoordinate     arcLength( GParametric uFrom,
                               GParametric uTo ) const;

    /**
     * Returns the arc length of the loop.
	 */
    GCoordinate     arcLength() const;

    /**
     * Approximates the parameter, given a particular arc length along the loop.
	 */
    GParametric     approximateParameterFromArcLength(
                                GCoordinate length,
                                GCoordinate tolerance = 0.02 ) const;

    /**
     * Returns the value of the extended curve representing the Loop.
	 */
    GParametric     maxParameter() const;

    /**
     * Returns the parametric value of the closest point on the extended loop to the center of the rectangle.
	 */
    GParametric nearestParametric( const IGPoint2D& test ) const;

    /**
     * Inserts a single new knot/point at given parametric location after closing the loop.
	 */
    void insertKnot( GParametric u );

    /**
     * Adds a discontinuity at a parametric location u after closing the loop.
	 */
    void makeDiscontinuity( GParametric u, EDiscontinuity cont );

    /**
     * Turns the curve into a collection of piecewise Bezier segments after closing the loop.
	 */
    void refineToBeziers();

    /**
     * Creates a pinned curve and knot vector after closing the loop.
	 */
    void refineToPinned();

    /**
     * Modifies both loops to have the same order, the same number of points, and the same knot vector, without changing the shape of either loop.
	 */
    static void makeCompatible( IGLoop2D& curve1,
                                        IGLoop2D& curve2 );

    /**
     * Returns the section of a loop, in the parametric range "from" to "to" after closing the loop.
     * Returns the section of a loop, in the parametric range "from" to "to" after closing the loop.
     * If the section crosses the seam, you'll get the rest of the loop.
	 */
    void        sectionOfCurve(
                    GParametric from,
                    GParametric to,
                    IGCurve2D& section ) const;
    /**
     * Reverses the order of a curve's points so the parameterization runs the opposite direction. The geometric shape is unchanged.
     */
    void reverseDirection();

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

    /**
     * Changes loop shape so that at parameter u, it has the specified tangent.
	 */
    void dragTangent(  GParametric u,  const IGPoint2D& toTangent,
            GParametric segmentMinimum = 0.2,GParametric segmentMaximum = 0.8);

    /**
     * Raises the degree of this IGLoop2D to the specified order.
	 */
    void raiseOrder( unsigned long newOrder );

    /**
     * Approximates this IGLoop2D with a lower-order one.
	 */
    void approximateLowerOrder( unsigned long newOrder,
                                GCoordinate tolerance = 0.2 );

  private:
friend class ISamplingExtractor;
    // This returns true if the loop can be closed with Ck
    // continuity (k=order).  This is true if the first k-2
    // intervals are equal to the last k-2 intervals, and these
    // intervals are non-zero (i.e., not pinned)
    bool        isCloseable() const;
  private:
    void extend();
    void retract();

    enum EHitFlagOp { kDoContains, kDoIntersects };
    bool fEOFill;

    bool doPolygonTest(
    	EHitFlagOp op,
        const IGPoint2D& p,
        const IGRect2D& r) const;

};

class IGEllipse2D;

/**
 * IGPie2D represents a pie-shaped wedge.
 * IGPie2D represents a pie-shaped wedge.
 * The arc always goes "counterclockwise" (because that's how sin's and cos's go). However, because the
 * coordinate system in Graphics is by default "upsidedown" from the mathematical one, the arcs go
 * clockwise.
 */

class IGPie2D : public IGLoop2D
{
  public:

	/**
	 * Sets the curve to an elliptical arc from angle1 to angle2 and then connects the end points
	 * with c1 discontinuity through the center of the ellipse. The arc always goes "counterclockwise."
	 */
    IGPie2D(const IGEllipse2D& e, GDegrees angle1, GDegrees angle2, bool EOFill = true);

    /**
     * The only operators allowed on empty pie are assignment and stream in.
     */
    IGPie2D();

	/**
	 */
    IGPie2D(const IGPie2D&);

    virtual ~IGPie2D();
};

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

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

#endif // _IGLOOP2D_
