#ifndef TITAN_PCBCAM_MATH_H
#define TITAN_PCBCAM_MATH_H
#include "../titanpcbcamglobal.h"
#include <QPointF>
#include <QLineF>
#include <QPolygonF>
#include <cmath>


class TITAN_PCBCAM_EXPORT PcbCamMath
{
public:

    /* 计算两点之间距离 */
    static inline double point2pointDist(const QPointF &p1, const QPointF &p2)
    {
        return sqrt( (p1.x()-p2.x())*(p1.x()-p2.x())+(p1.y()-p2.y())*(p1.y()-p2.y()));
    }

    /* 计算矢量(sp->op)和(ep->op)的叉积,其结果r的几何意义如下
     * r>0: ep在矢量(os->sp)的逆时针方向
     * r=0: op,sp,ep三点共线
     * r<0: ep在矢量(os->sp)的顺时针方向
     */
    static inline double crossMultiply(const QPointF &sp, const QPointF &ep, const QPointF &op)
    {
        return((sp.x()-op.x())*(ep.y()-op.y())-(ep.x()-op.x())*(sp.y()-op.y()));
    }

    /* 计算矢量(p1->p0)和(p2->p0)的点积，其结果r的几何含意如下
     * r<0: 两矢量的夹角为锐角
     * r=0: 两矢量的夹角为直角
     * r>0: 两矢量的夹角为钝角
     */
    static inline double dotMultiply(const QPointF &p1, const QPointF &p2, const QPointF &p0)
    {
        return ((p1.x()-p0.x())*(p2.x()-p0.x())+(p1.y()-p0.y())*(p2.y()-p0.y()));
    }

    /* 判断点是否在直线上*/
    static inline bool isPointOnLine(const QPointF &p, const QLineF &l)
    {
        //判断条件: (点在线段所在的直线上） 并且 (点在线段对角线的矩形范围内)
        return (crossMultiply(l.p2(), p, l.p1())==0) && ( ( (p.x()-l.p1().x())*(p.x()-l.p2().x())<=0 )&&( (p.y()-l.p1().y())*(p.y()-l.p2().y())<=0 ) );
    }

    /* 计算点到线段的垂足系数
     *
     *        AC dot AB
     *  r = ---------------
     *        ||AB||^2
     *
     *  结果r的几何含意如下:
     *  r=0:    垂足=A
     *  r=1:    垂足=B
     *  r<0:    垂足在线段AB的反向延长线上
     *  r>1:    垂足在线段AB的正向延长线上
     *  0<r<1:  垂足在线段AB中
     */
    static inline double pedalFactor(const QPointF &p, const QLineF &l)
    {
        QLineF tl(l.p1(), p);
        return dotMultiply(tl.p2(),l.p2(),l.p1())/(point2pointDist(l.p1(),l.p2())*point2pointDist(l.p1(),l.p2()));
    }

    /* 计算点到线段的垂足 */
    static inline QPointF pedalPoint(const QPointF &p, const QLineF &l)
    {
        double r = pedalFactor(p, l);
        return QPointF(l.p1().x()+r*(l.p2().x()-l.p1().x()), l.p1().y()+r*(l.p2().y()-l.p1().y()));
    }

    /* 计算点到线段的最近距离和点 */
    static inline double point2lineDist(const QPointF &p, const QLineF &l, QPointF &np)
    {
        double r=pedalFactor(p, l);
        if(r<0) np = l.p1();
        else if(r>1) np = l.p2();
        else np = pedalPoint(p, l);
        return point2pointDist(p, np);
    }

    /* 计算点到多段线的最近距离和点 */
    static inline double point2polylineDist(const QPointF &p, const QPolygonF &polyline, QPointF &np)
    {
        double dist = 9999999;
        double tmpdist;
        QLineF line;
        QPointF tnp;
        for (int i = 0; i < polyline.count() -1; ++i) {
            line.setP1(polyline.at(i));
            line.setP2(polyline.at(i+1));
            tmpdist = point2lineDist(p, line, tnp);
            if (tmpdist < dist) {
                dist = tmpdist;
                np = tnp;
            }
        }
        return dist;
    }

};


#endif // TITAN_PCBCAM_MATH_H
