
/* CenterPointArc.hpp - mathematically convenient partial elliptical arc representations */

#ifndef __CenterPointArc_hpp__
#define __CenterPointArc_hpp__

#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif

#include <Cg/vector/xyzw.hpp>
#include <Cg/double.hpp>
#include <Cg/vector.hpp>

#include "EndPointArc.hpp"  // provides SVG's end point arc paramterization

using namespace Cg;

// CenterPointArc is an alternative partial elliptical arc parameterization that
// is more convenient for evaluation of points along the arc's path and recognizing
// degenerate cases.
// http://www.w3.org/TR/SVG/implnote.html#ArcParameterizationAlternatives
class CenterPointArc {
public:
    enum FormType {
        BEHAVED,          // draw an arc
        DEGENERATE_LINE,  // draw a line connecting p0 and p1
        DEGENERATE_POINT  // don't draw anything (p0 == p1)
    };

private:
    FormType form;
    float2 center;
    float2 radii;
    float2 p[2]; // end-points
    float theta1;
    float delta_theta;
    float psi;

public:
    // Construct the center-point parameterization from an end-point parameterization.
    CenterPointArc(const EndPointArc &src);

    CenterPointArc() {};

    static CenterPointArc makeCircularCenterPointArc(
        const float2& center,
        const float2& start_direction,  // normalized
        const float2& end_direction,  // normalized
        float radius);

    // Inline const accessors.
    inline FormType Form() const {
        return form;
    }
    inline const float2& Center() const {
        return center;
    }
    inline const float2& Radii() const {
        return radii;
    }
    inline const float2* EndPoints() const {
        return p;
    }
    inline double2 initialPoint() const {
        return double2(p[0]);
    }
    inline double2 terminalPoint() const {
        return double2(p[1]);
    }
    inline float2 initialPointf() const {
        return p[0];
    }
    inline float2 terminalPointf() const {
        return p[1];
    }
    inline float Theta1() const {
        return theta1;
    }
    inline float DeltaTheta() const {
        return delta_theta;
    }
    inline float Psi() const {
        return psi;
    }

    inline bool isDegeneratePoint() const {
        return form == CenterPointArc::DEGENERATE_POINT;
    }
    inline bool notDegeneratePoint() const {
        return form != CenterPointArc::DEGENERATE_POINT;
    }
    inline bool isDegenerateLine() const {
        return form == CenterPointArc::DEGENERATE_LINE;
    }
    inline bool isBehaved() const {
        return form == CenterPointArc::BEHAVED;
    }
    inline bool isCircle() const {
        // Ellipse with identical major and minor radii is a circle.
        return radii.x == radii.y;
    }

    // Evaluate a point on the arc at a given theta.
    double2 eval(double theta) const;
    double2 evalPercent(double t) const;
    double2 tangent(double theta) const;
    double2 initialTangent() const;
    float2 initialTangentf() const;
    float2 terminalTangentf() const;
    double2 terminalTangent() const;
    double approximateArcLength() const;

    void splitPercent(double t, CenterPointArc arc_pair[2]) const;

    typedef void (*ConicSplitFunc)(const float2 cp[3], float w, void* handle);
    void splitToConics(ConicSplitFunc func, float max_angle_per_conic, void* handle) const;

    friend class PathSegmentProcessor;  // XXX need ctors for corners
};

#endif // __CenterPointArc_hpp__
