#include "symbol.h"
#include <QDebug>
#include <QtMath>
#include <QTransform>


class PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbol)
public:
    PcbCamSymbolPrivate() { }
    virtual ~PcbCamSymbolPrivate() { }

    QString name;

    PcbCam::SymbolType type {PcbCam::Sym_User};

    mutable QPainterPath shape;
    mutable QRectF boundingRect;

public:
    PcbCamSymbol *q_ptr {nullptr};
};

PcbCamSymbol::PcbCamSymbol()
    : d_ptr(new PcbCamSymbolPrivate)
{
    d_ptr->q_ptr = this;

}

PcbCamSymbol::~PcbCamSymbol()
{

}

QString PcbCamSymbol::name() const
{
    Q_D(const PcbCamSymbol);
    return d->name;
}


QRectF PcbCamSymbol::boundingRect() const
{
    Q_D(const PcbCamSymbol);
    if (d->boundingRect.isEmpty()) {
        d->boundingRect = shape().controlPointRect();
    }
    return d->boundingRect;
}

PcbCam::SymbolType PcbCamSymbol::type() const
{
    Q_D(const PcbCamSymbol);
    return d->type;
}

PcbCamSymbol::PcbCamSymbol(PcbCamSymbolPrivate &dd)
    : d_ptr(&dd)
{
    d_ptr->q_ptr = this;
}


/********************* r10 *****************/
class PcbCamSymbolRPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolR)
public:
    PcbCamSymbolRPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolRPrivate() { }

    qreal size {0.0};
};

PcbCamSymbolR::PcbCamSymbolR(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolRPrivate)
{
    Q_D(PcbCamSymbolR);
    d->type = PcbCam::Sym_R;
    d->name = iName;
    QRegularExpression re("^r([0-9.]+)$");
    d->size = re.match(d->name).captured(1).toDouble()/1000.0;
}

PcbCamSymbolR::~PcbCamSymbolR()
{

}

QPainterPath PcbCamSymbolR::shape() const
{
    Q_D(const PcbCamSymbolR);
    if (d->shape.isEmpty()) {
        d->shape.addEllipse(QRectF(-d->size/2.0, -d->size/2.0, d->size, d->size));
    }
    return d->shape;
}

qreal PcbCamSymbolR::size() const
{
    Q_D(const PcbCamSymbolR);
    return d->size;
}


/********************* s10 *****************/
class PcbCamSymbolSPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolS)
public:
    PcbCamSymbolSPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolSPrivate() { }

    qreal size {0.0};
};


PcbCamSymbolS::PcbCamSymbolS(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolSPrivate)
{
    Q_D(PcbCamSymbolS);
    d->type = PcbCam::Sym_S;
    d->name = iName;
    QRegularExpression re("^s([0-9.]+)$");
    d->size = re.match(d->name).captured(1).toDouble()/1000.0;
}

PcbCamSymbolS::~PcbCamSymbolS()
{

}

QPainterPath PcbCamSymbolS::shape() const
{
    Q_D(const PcbCamSymbolS);
    if (d->shape.isEmpty()) {
        d->shape.addRect(QRectF(-d->size/2.0, -d->size/2.0, d->size, d->size));
    }
    return d->shape;
}


qreal PcbCamSymbolS::size() const
{
    Q_D(const PcbCamSymbolS);
    return d->size;
}


/********************* rect10x20 *****************/
class PcbCamSymbolRectPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolRect)
public:
    PcbCamSymbolRectPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolRectPrivate() { }

    QSizeF size ;
};


PcbCamSymbolRect::PcbCamSymbolRect(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolRectPrivate)
{
    Q_D(PcbCamSymbolRect);
    d->type = PcbCam::Sym_Rect;
    d->name = iName;
    QRegularExpression re("^rect([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->size = QSizeF(match.captured(1).toDouble()/1000.0, match.captured(2).toDouble()/1000.0);
}

PcbCamSymbolRect::~PcbCamSymbolRect()
{

}

QPainterPath PcbCamSymbolRect::shape() const
{
    Q_D(const PcbCamSymbolRect);
    if (d->shape.isEmpty()) {
        d->shape.addRect(QRectF(-d->size.width()/2.0, -d->size.height()/2.0, d->size.width(), d->size.height()));
    }
    return d->shape;
}

QSizeF PcbCamSymbolRect::size() const
{
    Q_D(const PcbCamSymbolRect);
    return d->size;
}

/********************* rect60x30xr5x13 *****************/
class PcbCamSymbolRectRPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolRectR)
public:
    PcbCamSymbolRectRPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolRectRPrivate() { }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal radius {0.0};
    QString corners {""};
};


PcbCamSymbolRectR::PcbCamSymbolRectR(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolRectRPrivate)
{
    Q_D(PcbCamSymbolRectR);
    d->type = PcbCam::Sym_RectR;
    d->name = iName;
    QRegularExpression re("^rect([0-9.]+)x([0-9.]+)xr([0-9.]+)(?:x([1-4]+))?$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->radius = match.captured(3).toDouble()/1000.0;
    d->corners = match.captured(4);
}

PcbCamSymbolRectR::~PcbCamSymbolRectR()
{

}

QPainterPath PcbCamSymbolRectR::shape() const
{
    Q_D(const PcbCamSymbolRectR);
    if (d->shape.isEmpty()) {
        QRectF rect(-d->width/2.0, -d->height/2.0, d->width, d->height);
        QRectF r = rect.normalized();
        qreal x = r.x();
        qreal y = r.y();
        qreal w = r.width();
        qreal h = r.height();

        QString cor = d->corners;
        if (cor.isEmpty()) cor = "1234";

        if (cor.contains('3')) {
            d->shape.arcMoveTo(x, y, d->radius*2.0, d->radius*2.0, 180);
            d->shape.arcTo(x, y, d->radius*2.0, d->radius*2.0, 180, -90);
        }
        else {
            d->shape.moveTo(x, y);
            d->shape.lineTo(x + w - d->radius, y);
        }

        if (cor.contains('4')) {
            d->shape.arcTo(x + w - d->radius*2.0, y, d->radius*2.0 , d->radius*2.0, 90, -90);
        }
        else {
            d->shape.lineTo(x + w, y);
            d->shape.lineTo(x + w, y + h - d->radius);
        }

        if (cor.contains('1')) {
            d->shape.arcTo(x + w - d->radius*2.0, y + h - d->radius*2.0, d->radius*2.0, d->radius*2.0, 0, -90);
        }
        else {
            d->shape.lineTo(x + w, y + h);
            d->shape.lineTo(x + d->radius, y + h);
        }

        if (cor.contains('2')) {
            d->shape.arcTo(x, y + h - d->radius*2.0, d->radius*2.0, d->radius*2.0, 270, -90);
        }
        else {
            d->shape.lineTo(x, y + h);
        }
        d->shape.closeSubpath();

    }
    return d->shape;
}

qreal PcbCamSymbolRectR::width() const
{
    Q_D(const PcbCamSymbolRectR);
    return d->width;
}

qreal PcbCamSymbolRectR::height() const
{
    Q_D(const PcbCamSymbolRectR);
    return d->height;
}

qreal PcbCamSymbolRectR::radius() const
{
    Q_D(const PcbCamSymbolRectR);
    return d->radius;
}

QString PcbCamSymbolRectR::corners() const
{
    Q_D(const PcbCamSymbolRectR);
    return d->corners;
}

/********************* rect60x30xc5x13 *****************/
class PcbCamSymbolRectCPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolRectC)
public:
    PcbCamSymbolRectCPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolRectCPrivate() { }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal radius {0.0};
    QString corners {""};
};


PcbCamSymbolRectC::PcbCamSymbolRectC(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolRectCPrivate)
{
    Q_D(PcbCamSymbolRectC);
    d->type = PcbCam::Sym_RectC;
    d->name = iName;
    QRegularExpression re("^rect([0-9.]+)x([0-9.]+)xc([0-9.]+)(?:x([1-4]+))?$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->radius = match.captured(3).toDouble()/1000.0;
    d->corners = match.captured(4);
}

PcbCamSymbolRectC::~PcbCamSymbolRectC()
{

}

QPainterPath PcbCamSymbolRectC::shape() const
{
    Q_D(const PcbCamSymbolRectC);
    if (d->shape.isEmpty()) {
        QRectF rect(-d->width/2.0, -d->height/2.0, d->width, d->height);
        QRectF r = rect.normalized();
        qreal x = r.x();
        qreal y = r.y();
        qreal w = r.width();
        qreal h = r.height();

        QString cor = d->corners;
        if (cor.isEmpty()) cor = "1234";

        if (cor.contains('3')) {
            d->shape.moveTo(x, y + d->radius);
            d->shape.lineTo(x + d->radius, y);
            d->shape.lineTo(x + w - d->radius, y);
        }
        else {
            d->shape.moveTo(x, y);
            d->shape.lineTo(x + w - d->radius, y);
        }

        if (cor.contains('4')) {
            d->shape.lineTo(x + w, y + d->radius);
            d->shape.lineTo(x + w, y + h - d->radius);
        }
        else {
            d->shape.lineTo(x + w, y);
            d->shape.lineTo(x + w, y + h - d->radius);
        }

        if (cor.contains('1')) {
            d->shape.lineTo(x + w - d->radius, y + h);
            d->shape.lineTo(x + d->radius, y + h);
        }
        else {
            d->shape.lineTo(x + w, y + h);
            d->shape.lineTo(x + d->radius, y + h);
        }

        if (cor.contains('2')) {
            d->shape.lineTo(x, y + h - d->radius);
        }
        else {
            d->shape.lineTo(x, y + h);
        }
        d->shape.closeSubpath();

    }
    return d->shape;
}


qreal PcbCamSymbolRectC::width() const
{
    Q_D(const PcbCamSymbolRectC);
    return d->width;
}

qreal PcbCamSymbolRectC::height() const
{
    Q_D(const PcbCamSymbolRectC);
    return d->height;
}

qreal PcbCamSymbolRectC::radius() const
{
    Q_D(const PcbCamSymbolRectC);
    return d->radius;
}

QString PcbCamSymbolRectC::corners() const
{
    Q_D(const PcbCamSymbolRectC);
    return d->corners;
}


/********************* oval100x50 *****************/
class PcbCamSymbolOvalPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolOval)
public:
    PcbCamSymbolOvalPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolOvalPrivate() { }

    QSizeF size ;
};


PcbCamSymbolOval::PcbCamSymbolOval(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolOvalPrivate)
{
    Q_D(PcbCamSymbolOval);
    d->type = PcbCam::Sym_Oval;
    d->name = iName;
    QRegularExpression re("^oval([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->size = QSizeF(match.captured(1).toDouble()/1000.0, match.captured(2).toDouble()/1000.0);
}

PcbCamSymbolOval::~PcbCamSymbolOval()
{

}

QPainterPath PcbCamSymbolOval::shape() const
{
    Q_D(const PcbCamSymbolOval);
    if (d->shape.isEmpty()) {
        qreal w = d->size.width();
        qreal h = d->size.height();
        if (w > h) {
            qreal rad = h / 2.0;
            d->shape.moveTo(-w/2.0 + rad, -h/2.0);
            d->shape.lineTo(w/2.0 - rad, -h/2.0);
            d->shape.arcTo(w/2.0 -rad*2.0, -h/2.0, h, h, 90, -180);
            d->shape.lineTo(-w/2.0 + rad, h/2.0);
            d->shape.arcTo(-w/2.0, -h/2.0, h, h, -90, -180);
            d->shape.closeSubpath();
        }
        else {
            qreal rad = w/2.0;
            d->shape.moveTo(w/2.0, -h/2.0 + rad);
            d->shape.lineTo(w/2.0, h/2.0 - rad);
            d->shape.arcTo(-w/2.0, h/2.0 - rad*2.0, w, w, 0, -180);
            d->shape.lineTo(-w/2.0,-h/2.0 + rad);
            d->shape.arcTo(-w/2.0, -h/2.0, w, w, 180, -180);
            d->shape.closeSubpath();
        }
    }
    return d->shape;
}

QSizeF PcbCamSymbolOval::size() const
{
    Q_D(const PcbCamSymbolOval);
    return d->size;
}

/********************* di100x50 *****************/
class PcbCamSymbolDiPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolDi)
public:
    PcbCamSymbolDiPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolDiPrivate() { }

    QSizeF size ;
};


PcbCamSymbolDi::PcbCamSymbolDi(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolDiPrivate)
{
    Q_D(PcbCamSymbolDi);
    d->type = PcbCam::Sym_Di;
    d->name = iName;
    QRegularExpression re("^di([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->size = QSizeF(match.captured(1).toDouble()/1000.0, match.captured(2).toDouble()/1000.0);
}

PcbCamSymbolDi::~PcbCamSymbolDi()
{

}

QPainterPath PcbCamSymbolDi::shape() const
{
    Q_D(const PcbCamSymbolDi);
    if (d->shape.isEmpty()) {
        qreal w = d->size.width();
        qreal h = d->size.height();
        qreal x = -w/2.0;
        qreal y = -h/2.0;
        qreal wh = w/2.0;
        qreal hh = h/2.0;

        d->shape.moveTo(x, y+hh);
        d->shape.lineTo(x+wh, y);
        d->shape.lineTo(x+w, y + hh);
        d->shape.lineTo(x + wh, y + h);
        d->shape.closeSubpath();

    }
    return d->shape;
}

QSizeF PcbCamSymbolDi::size() const
{
    Q_D(const PcbCamSymbolDi);
    return d->size;
}



/********************* oct100x50x20 *****************/
class PcbCamSymbolOctPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolOct)
public:
    PcbCamSymbolOctPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolOctPrivate() { }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal radius {0.0};
};


PcbCamSymbolOct::PcbCamSymbolOct(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolOctPrivate)
{
    Q_D(PcbCamSymbolOct);
    d->type = PcbCam::Sym_Oct;
    d->name = iName;
    QRegularExpression re("^oct([0-9.]+)x([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->radius = match.captured(3).toDouble()/1000.0;
}

PcbCamSymbolOct::~PcbCamSymbolOct()
{

}

QPainterPath PcbCamSymbolOct::shape() const
{
    Q_D(const PcbCamSymbolOct);
    if (d->shape.isEmpty()) {
        qreal x = -d->width/2.0;
        qreal y = -d->height/2.0;
        d->shape.moveTo(x, y + d->height - d->radius);
        d->shape.lineTo(x, y + d->radius);
        d->shape.lineTo(x + d->radius, y);
        d->shape.lineTo(x + d->width - d->radius, y);
        d->shape.lineTo(x + d->width, y + d->radius);
        d->shape.lineTo(x + d->width, y + d->height - d->radius);
        d->shape.lineTo(x + d->width - d->radius, y + d->height);
        d->shape.lineTo(x + d->radius, y + d->height);
        d->shape.closeSubpath();
    }
    return d->shape;
}

qreal PcbCamSymbolOct::width() const
{
    Q_D(const PcbCamSymbolOct);
    return d->width;
}

qreal PcbCamSymbolOct::height() const
{
    Q_D(const PcbCamSymbolOct);
    return d->height;
}

qreal PcbCamSymbolOct::radius() const
{
    Q_D(const PcbCamSymbolOct);
    return d->radius;
}


/********************* donut_r100x50 *****************/
class PcbCamSymbolDonutRPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolDonutR)
public:
    PcbCamSymbolDonutRPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolDonutRPrivate() { }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
};


PcbCamSymbolDonutR::PcbCamSymbolDonutR(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolDonutRPrivate)
{
    Q_D(PcbCamSymbolDonutR);
    d->type = PcbCam::Sym_DonutR;
    d->name = iName;
    QRegularExpression re("^donut_r([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
}

PcbCamSymbolDonutR::~PcbCamSymbolDonutR()
{

}

QPainterPath PcbCamSymbolDonutR::shape() const
{
    Q_D(const PcbCamSymbolDonutR);
    if (d->shape.isEmpty()) {
        d->shape.addEllipse(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam);
        d->shape.addEllipse(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam);
    }
    return d->shape;
}

qreal PcbCamSymbolDonutR::outerDiam() const
{
    Q_D(const PcbCamSymbolDonutR);
    return d->outerDiam;
}

qreal PcbCamSymbolDonutR::innerDiam() const
{
    Q_D(const PcbCamSymbolDonutR);
    return d->innerDiam;
}

/********************* donut_s100x50 *****************/
class PcbCamSymbolDonutSPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolDonutS)
public:
    PcbCamSymbolDonutSPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolDonutSPrivate() { }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
};


PcbCamSymbolDonutS::PcbCamSymbolDonutS(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolDonutSPrivate)
{
    Q_D(PcbCamSymbolDonutS);
    d->type = PcbCam::Sym_DonutS;
    d->name = iName;
    QRegularExpression re("^donut_s([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
}

PcbCamSymbolDonutS::~PcbCamSymbolDonutS()
{

}

QPainterPath PcbCamSymbolDonutS::shape() const
{
    Q_D(const PcbCamSymbolDonutS);
    if (d->shape.isEmpty()) {
        d->shape.addRect(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam);
        d->shape.addRect(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam);
    }
    return d->shape;
}

qreal PcbCamSymbolDonutS::outerDiam() const
{
    Q_D(const PcbCamSymbolDonutS);
    return d->outerDiam;
}

qreal PcbCamSymbolDonutS::innerDiam() const
{
    Q_D(const PcbCamSymbolDonutS);
    return d->innerDiam;
}



/********************* hex_l100x50x20 *****************/
class PcbCamSymbolHexLPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolHexL)
public:
    PcbCamSymbolHexLPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolHexLPrivate() { }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal radius {0.0};
};


PcbCamSymbolHexL::PcbCamSymbolHexL(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolHexLPrivate)
{
    Q_D(PcbCamSymbolHexL);
    d->type = PcbCam::Sym_HexL;
    d->name = iName;
    QRegularExpression re("^hex_l([0-9.]+)x([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->radius = match.captured(3).toDouble()/1000.0;
}

PcbCamSymbolHexL::~PcbCamSymbolHexL()
{

}

QPainterPath PcbCamSymbolHexL::shape() const
{
    Q_D(const PcbCamSymbolHexL);
    if (d->shape.isEmpty()) {
        d->shape.moveTo(-d->width/2.0 + d->radius, -d->height/2.0);
        d->shape.lineTo(-d->width/2.0, 0);
        d->shape.lineTo(-d->width/2.0 + d->radius, d->height/2.0);
        d->shape.lineTo(d->width/2.0 - d->radius, d->height/2.0);
        d->shape.lineTo(d->width/2.0, 0);
        d->shape.lineTo(d->width/2.0 - d->radius, -d->height/2.0);
        d->shape.closeSubpath();
    }
    return d->shape;
}

qreal PcbCamSymbolHexL::width() const
{
    Q_D(const PcbCamSymbolHexL);
    return d->width;
}

qreal PcbCamSymbolHexL::height() const
{
    Q_D(const PcbCamSymbolHexL);
    return d->height;
}

qreal PcbCamSymbolHexL::radius() const
{
    Q_D(const PcbCamSymbolHexL);
    return d->radius;
}

/********************* hex_s100x50x20 *****************/
class PcbCamSymbolHexSPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolHexS)
public:
    PcbCamSymbolHexSPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolHexSPrivate() { }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal radius {0.0};
};


PcbCamSymbolHexS::PcbCamSymbolHexS(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolHexSPrivate)
{
    Q_D(PcbCamSymbolHexS);
    d->type = PcbCam::Sym_HexS;
    d->name = iName;
    QRegularExpression re("^hex_s([0-9.]+)x([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->radius = match.captured(3).toDouble()/1000.0;
}

PcbCamSymbolHexS::~PcbCamSymbolHexS()
{

}

QPainterPath PcbCamSymbolHexS::shape() const
{
    Q_D(const PcbCamSymbolHexS);
    if (d->shape.isEmpty()) {
        d->shape.moveTo(0, -d->height/2.0);
        d->shape.lineTo(-d->width/2.0, -d->height/2.0 + d->radius);
        d->shape.lineTo(-d->width/2.0, d->height/2.0 - d->radius);
        d->shape.lineTo(0, d->height/2.0);
        d->shape.lineTo(d->width/2.0, d->height/2.0 - d->radius);
        d->shape.lineTo(d->width/2.0, -d->height/2.0 + d->radius);
        d->shape.closeSubpath();
    }
    return d->shape;
}

qreal PcbCamSymbolHexS::width() const
{
    Q_D(const PcbCamSymbolHexS);
    return d->width;
}

qreal PcbCamSymbolHexS::height() const
{
    Q_D(const PcbCamSymbolHexS);
    return d->height;
}

qreal PcbCamSymbolHexS::radius() const
{
    Q_D(const PcbCamSymbolHexS);
    return d->radius;
}

/********************* bfr10 *****************/
class PcbCamSymbolBfrPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolBfr)
public:
    PcbCamSymbolBfrPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolBfrPrivate() { }

    qreal size {0.0};
};

PcbCamSymbolBfr::PcbCamSymbolBfr(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolBfrPrivate)
{
    Q_D(PcbCamSymbolBfr);
    d->type = PcbCam::Sym_Bfr;
    d->name = iName;
    QRegularExpression re("^bfr([0-9.]+)$");
    d->size = re.match(d->name).captured(1).toDouble()/1000.0;
}

PcbCamSymbolBfr::~PcbCamSymbolBfr()
{

}

QPainterPath PcbCamSymbolBfr::shape() const
{
    Q_D(const PcbCamSymbolBfr);
    if (d->shape.isEmpty()) {
        d->shape.moveTo(0, 0);
        d->shape.arcTo(-d->size/2.0, -d->size/2.0, d->size, d->size, 0, 90);
        d->shape.closeSubpath();
        d->shape.moveTo(0, 0);
        d->shape.arcTo(-d->size/2.0, -d->size/2.0, d->size, d->size, -90, -90);
        d->shape.closeSubpath();
    }
    return d->shape;
}

qreal PcbCamSymbolBfr::size() const
{
    Q_D(const PcbCamSymbolBfr);
    return d->size;
}


/********************* bfs10 *****************/
class PcbCamSymbolBfsPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolBfs)
public:
    PcbCamSymbolBfsPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolBfsPrivate() { }

    qreal size {0.0};
};

PcbCamSymbolBfs::PcbCamSymbolBfs(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolBfsPrivate)
{
    Q_D(PcbCamSymbolBfs);
    d->type = PcbCam::Sym_Bfs;
    d->name = iName;
    QRegularExpression re("^bfs([0-9.]+)$");
    d->size = re.match(d->name).captured(1).toDouble()/1000.0;
}

PcbCamSymbolBfs::~PcbCamSymbolBfs()
{

}

QPainterPath PcbCamSymbolBfs::shape() const
{
    Q_D(const PcbCamSymbolBfs);
    if (d->shape.isEmpty()) {
        d->shape.addRect(0, -d->size/2.0, d->size/2.0, d->size/2.0);
        d->shape.addRect(-d->size/2.0, 0, d->size/2.0, d->size/2.0);
    }
    return d->shape;
}

qreal PcbCamSymbolBfs::size() const
{
    Q_D(const PcbCamSymbolBfs);
    return d->size;
}


/********************* tri10x20 *****************/
class PcbCamSymbolTriPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolTri)
public:
    PcbCamSymbolTriPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolTriPrivate() { }

    QSizeF size ;
};


PcbCamSymbolTri::PcbCamSymbolTri(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolTriPrivate)
{
    Q_D(PcbCamSymbolTri);
    d->type = PcbCam::Sym_Tri;
    d->name = iName;
    QRegularExpression re("^tri([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->size = QSizeF(match.captured(1).toDouble()/1000.0, match.captured(2).toDouble()/1000.0);
}

PcbCamSymbolTri::~PcbCamSymbolTri()
{

}

QPainterPath PcbCamSymbolTri::shape() const
{
    Q_D(const PcbCamSymbolTri);
    if (d->shape.isEmpty()) {
        qreal w = d->size.width();
        qreal h = d->size.height();
        d->shape.moveTo(0, h/2.0);
        d->shape.lineTo(-w/2.0, -h/2.0);
        d->shape.lineTo(w/2.0, -h/2.0);
        d->shape.closeSubpath();
    }
    return d->shape;
}

QSizeF PcbCamSymbolTri::size() const
{
    Q_D(const PcbCamSymbolTri);
    return d->size;
}

/********************* orval_h100x20 *****************/
class PcbCamSymbolOvalHPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolOvalH)
public:
    PcbCamSymbolOvalHPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolOvalHPrivate() { }

    QSizeF size ;
};


PcbCamSymbolOvalH::PcbCamSymbolOvalH(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolOvalHPrivate)
{
    Q_D(PcbCamSymbolOvalH);
    d->type = PcbCam::Sym_OvalH;
    d->name = iName;
    QRegularExpression re("^oval_h([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->size = QSizeF(match.captured(1).toDouble()/1000.0, match.captured(2).toDouble()/1000.0);
}

PcbCamSymbolOvalH::~PcbCamSymbolOvalH()
{

}

QPainterPath PcbCamSymbolOvalH::shape() const
{
    Q_D(const PcbCamSymbolOvalH);
    if (d->shape.isEmpty()) {
        //TODO may have bug
        qreal w = d->size.width();
        qreal h = d->size.height();
        d->shape.moveTo(w/2.0 - h, -h/2.0);
        d->shape.arcTo(w/2.0 - h, -h/2.0, h, h, 90, -180);
        d->shape.lineTo(-w/2.0, h/2.0);
        d->shape.lineTo(-w/2.0, -h/2.0);
        d->shape.closeSubpath();
    }
    return d->shape;
}

QSizeF PcbCamSymbolOvalH::size() const
{
    Q_D(const PcbCamSymbolOvalH);
    return d->size;
}


/********************* thr100x70x30x5x20 *****************/
class PcbCamSymbolThrPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolThr)
public:
    PcbCamSymbolThrPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolThrPrivate() { }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
};


PcbCamSymbolThr::PcbCamSymbolThr(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolThrPrivate)
{
    Q_D(PcbCamSymbolThr);
    d->type = PcbCam::Sym_Thr;
    d->name = iName;
    QRegularExpression re("^thr([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
}

PcbCamSymbolThr::~PcbCamSymbolThr()
{

}

QPainterPath PcbCamSymbolThr::shape() const
{
    Q_D(const PcbCamSymbolThr);
    if (d->shape.isEmpty()) {
        qreal lineRadius = (d->outerDiam - d->innerDiam) / 4.0;
        qreal centerRadius = d->innerDiam/2.0 + lineRadius;
        qreal x1 = qSqrt(centerRadius*centerRadius - (lineRadius+d->spokesGap/2.0)*(lineRadius+d->spokesGap/2.0));
        QPointF p1 = QPointF(x1, lineRadius+d->spokesGap/2.0);
        qreal angle1 = qAtan2(p1.y(), p1.x()) * 180/M_PI;
        QPainterPath path0;
        path0.arcMoveTo(-centerRadius, -centerRadius, 2*centerRadius, 2*centerRadius, angle1);
        path0.arcTo(-centerRadius, -centerRadius, 2*centerRadius, 2*centerRadius, angle1, 360.0/d->numSpokes - 2*angle1);

        QPainterPathStroker stroker;
        stroker.setWidth(lineRadius*2);
        stroker.setJoinStyle(Qt::RoundJoin);
        stroker.setCapStyle(Qt::RoundCap);
        path0 = stroker.createStroke(path0);

        for (int i = 0; i < d->numSpokes; ++i) {
            QTransform trans;
            trans.rotate(d->startAngle + (360.0 / d->numSpokes) * i);
            d->shape.addPath(trans.map(path0));
        }
    }
    return d->shape;
}

qreal PcbCamSymbolThr::outerDiam() const
{
    Q_D(const PcbCamSymbolThr);
    return d->outerDiam;
}

qreal PcbCamSymbolThr::innerDiam() const
{
    Q_D(const PcbCamSymbolThr);
    return d->innerDiam;
}

qreal PcbCamSymbolThr::startAngle() const
{
    Q_D(const PcbCamSymbolThr);
    return d->startAngle;
}

int PcbCamSymbolThr::numSpokes() const
{
    Q_D(const PcbCamSymbolThr);
    return d->numSpokes;
}

qreal PcbCamSymbolThr::spokesGap() const
{
    Q_D(const PcbCamSymbolThr);
    return d->spokesGap;
}

/********************* ths100x70x30x5x20 *****************/
class PcbCamSymbolThsPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolThs)
public:
    PcbCamSymbolThsPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolThsPrivate() { }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
};


PcbCamSymbolThs::PcbCamSymbolThs(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolThsPrivate)
{
    Q_D(PcbCamSymbolThs);
    d->type = PcbCam::Sym_Ths;
    d->name = iName;
    QRegularExpression re("^ths([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
}

PcbCamSymbolThs::~PcbCamSymbolThs()
{

}

QPainterPath PcbCamSymbolThs::shape() const
{
    Q_D(const PcbCamSymbolThs);
    if (d->shape.isEmpty()) {
        qreal pie_angle = 360.0/d->numSpokes;
        qreal half_inner_gap_angle = (180.0/M_PI) * (qAsin(d->spokesGap/d->innerDiam));
        qreal half_outer_gap_angle = (180.0/M_PI) * (qAsin(d->spokesGap/d->outerDiam));
        qreal inner_start_angle = d->startAngle +  half_inner_gap_angle;
        qreal inner_pie_angle = pie_angle - 2 * half_inner_gap_angle;
        qreal outer_start_angle = d->startAngle + half_outer_gap_angle;
        qreal outer_pie_angle = pie_angle - 2 * half_outer_gap_angle;

        for (int i = 0; i < d->numSpokes; ++i) {
            d->shape.arcMoveTo(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam, outer_start_angle);
            d->shape.arcTo(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam, outer_start_angle, outer_pie_angle);
            d->shape.arcTo(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam, inner_start_angle + inner_pie_angle, 0);
            d->shape.arcTo(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam, inner_start_angle + inner_pie_angle, -inner_pie_angle);
            d->shape.closeSubpath();
            inner_start_angle += pie_angle;
            outer_start_angle += pie_angle;
        }

        QTransform trans;
        trans.scale(1, -1);
        d->shape = trans.map(d->shape);

    }
    return d->shape;
}

qreal PcbCamSymbolThs::outerDiam() const
{
    Q_D(const PcbCamSymbolThs);
    return d->outerDiam;
}

qreal PcbCamSymbolThs::innerDiam() const
{
    Q_D(const PcbCamSymbolThs);
    return d->innerDiam;
}

qreal PcbCamSymbolThs::startAngle() const
{
    Q_D(const PcbCamSymbolThs);
    return d->startAngle;
}

int PcbCamSymbolThs::numSpokes() const
{
    Q_D(const PcbCamSymbolThs);
    return d->numSpokes;
}

qreal PcbCamSymbolThs::spokesGap() const
{
    Q_D(const PcbCamSymbolThs);
    return d->spokesGap;
}

/********************* s_ths100x70x30x5x20 *****************/
class PcbCamSymbolSThsPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolSThs)
public:
    PcbCamSymbolSThsPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolSThsPrivate() { }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
};


PcbCamSymbolSThs::PcbCamSymbolSThs(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolSThsPrivate)
{
    Q_D(PcbCamSymbolSThs);
    d->type = PcbCam::Sym_SThs;
    d->name = iName;
    QRegularExpression re("^s_ths([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
}

PcbCamSymbolSThs::~PcbCamSymbolSThs()
{

}

QPainterPath PcbCamSymbolSThs::shape() const
{
    Q_D(const PcbCamSymbolSThs);
    if (d->shape.isEmpty()) {
        QPainterPath path;
        path.addRect(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam);
        path.addRect(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam);

        QPainterPath bar;
        bar.addRect(0, -d->spokesGap/2.0, d->outerDiam + 0.0001, d->spokesGap);

        QPainterPath sub;
        QTransform mat;
        mat.rotate(d->startAngle);
        qreal angle_div = 360.0 / d->numSpokes;
        for (int i = 0; i < d->numSpokes; ++i) {
            sub.addPath(mat.map(bar));
            mat.rotate(angle_div);
        }
        d->shape = path.subtracted(sub);

    }
    return d->shape;
}

qreal PcbCamSymbolSThs::outerDiam() const
{
    Q_D(const PcbCamSymbolSThs);
    return d->outerDiam;
}

qreal PcbCamSymbolSThs::innerDiam() const
{
    Q_D(const PcbCamSymbolSThs);
    return d->innerDiam;
}

qreal PcbCamSymbolSThs::startAngle() const
{
    Q_D(const PcbCamSymbolSThs);
    return d->startAngle;
}

int PcbCamSymbolSThs::numSpokes() const
{
    Q_D(const PcbCamSymbolSThs);
    return d->numSpokes;
}

qreal PcbCamSymbolSThs::spokesGap() const
{
    Q_D(const PcbCamSymbolSThs);
    return d->spokesGap;
}


/********************* s_tho100x70x30x5x20 *****************/
class PcbCamSymbolSThoPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolSTho)
public:
    PcbCamSymbolSThoPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolSThoPrivate() { }

    int sign(qreal val) const {
      return (val >= 0) + (val < 0) * (-1);
    }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
};


PcbCamSymbolSTho::PcbCamSymbolSTho(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolSThoPrivate)
{
    Q_D(PcbCamSymbolSTho);
    d->type = PcbCam::Sym_STho;
    d->name = iName;
    QRegularExpression re("^s_tho([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
}

PcbCamSymbolSTho::~PcbCamSymbolSTho()
{

}

QPainterPath PcbCamSymbolSTho::shape() const
{
    Q_D(const PcbCamSymbolSTho);
    if (d->shape.isEmpty()) {
        if ((d->numSpokes != 1 && d->numSpokes != 2 && d->numSpokes != 4)
                || ((int)d->startAngle % 45 != 0))
        {
            //TOP8LOG.error() << "invalid symbol " << d->name;
            return QPainterPath();
        }

        QPainterPath path;
        path.addRect(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam);
        path.addRect(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam);

        QPainterPath sub;
        qreal angle_div = 360.0/d->numSpokes;
        if ((int)d->startAngle % 90 == 0) {
            QPainterPath bar;
            bar.addRect(0, -d->spokesGap/2.0, d->outerDiam, d->spokesGap);
            QTransform mat;
            mat.rotate(-d->startAngle);
            for (int i = 0; i < d->numSpokes; ++i) {
                sub.addPath(mat.map(bar));
                mat.rotate(-angle_div);
            }
        }
        else {
            qreal side = d->spokesGap * qCos(M_PI/4.0) + (d->outerDiam - d->innerDiam)/2.0;
            qreal offset = (d->outerDiam - side) /2.0;

            QPainterPath box;
            box.addRect(-side/2.0, -side/2.0, side, side);

            for (int i = 0; i < d->numSpokes; ++i) {
                QTransform mat;
                mat.translate(offset * d->sign(qCos((d->startAngle + angle_div * i) * (M_PI/180.0))),
                        -offset * d->sign(qSin((d->startAngle + angle_div * i) * (M_PI/180.0))));
                sub.addPath(mat.map(box));
            }
        }

        QTransform mat;
        mat.scale(1, -1);
        d->shape = mat.map(path.subtracted(sub));

    }
    return d->shape;
}

qreal PcbCamSymbolSTho::outerDiam() const
{
    Q_D(const PcbCamSymbolSTho);
    return d->outerDiam;
}

qreal PcbCamSymbolSTho::innerDiam() const
{
    Q_D(const PcbCamSymbolSTho);
    return d->innerDiam;
}

qreal PcbCamSymbolSTho::startAngle() const
{
    Q_D(const PcbCamSymbolSTho);
    return d->startAngle;
}

int PcbCamSymbolSTho::numSpokes() const
{
    Q_D(const PcbCamSymbolSTho);
    return d->numSpokes;
}

qreal PcbCamSymbolSTho::spokesGap() const
{
    Q_D(const PcbCamSymbolSTho);
    return d->spokesGap;
}

/********************* sr_ths100x70x30x5x20 *****************/
class PcbCamSymbolSrThsPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolSrThs)
public:
    PcbCamSymbolSrThsPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolSrThsPrivate() { }

    int sign(qreal val) const {
      return (val >= 0) + (val < 0) * (-1);
    }

    qreal outerDiam {0.0} ;
    qreal innerDiam {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
};


PcbCamSymbolSrThs::PcbCamSymbolSrThs(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolSrThsPrivate)
{
    Q_D(PcbCamSymbolSrThs);
    d->type = PcbCam::Sym_SrThs;
    d->name = iName;
    QRegularExpression re("^sr_ths([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->outerDiam = match.captured(1).toDouble()/1000.0;
    d->innerDiam = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
}

PcbCamSymbolSrThs::~PcbCamSymbolSrThs()
{

}

QPainterPath PcbCamSymbolSrThs::shape() const
{
    Q_D(const PcbCamSymbolSrThs);
    if (d->shape.isEmpty()) {
        QPainterPath path;
        path.addRect(-d->outerDiam/2.0, -d->outerDiam/2.0, d->outerDiam, d->outerDiam);
        path.addEllipse(-d->innerDiam/2.0, -d->innerDiam/2.0, d->innerDiam, d->innerDiam);

        QPainterPath bar;
        bar.addRect(0, -d->spokesGap/2.0, d->outerDiam + 0.0001, d->spokesGap);

        QPainterPath sub;
        QTransform mat;
        mat.rotate(d->startAngle);
        qreal angle_div = 360.0 / d->numSpokes;
        for (int i = 0; i < d->numSpokes; ++i) {
            sub.addPath(mat.map(bar));
            mat.rotate(angle_div);
        }
        //因为substracted会打段圆弧，造成精度丢失，所以我们放大1000倍来处理；
        QTransform mat2;
        mat2.scale(1000, 1000);
        d->shape = mat2.map(path).subtracted(mat2.map(sub));
        QTransform mat3;
        mat3.scale(0.001, 0.001);
        d->shape = mat3.map(d->shape);

    }
    return d->shape;
}

qreal PcbCamSymbolSrThs::outerDiam() const
{
    Q_D(const PcbCamSymbolSrThs);
    return d->outerDiam;
}

qreal PcbCamSymbolSrThs::innerDiam() const
{
    Q_D(const PcbCamSymbolSrThs);
    return d->innerDiam;
}

qreal PcbCamSymbolSrThs::startAngle() const
{
    Q_D(const PcbCamSymbolSrThs);
    return d->startAngle;
}

int PcbCamSymbolSrThs::numSpokes() const
{
    Q_D(const PcbCamSymbolSrThs);
    return d->numSpokes;
}

qreal PcbCamSymbolSrThs::spokesGap() const
{
    Q_D(const PcbCamSymbolSrThs);
    return d->spokesGap;
}

/********************* rc_ths100x70x30x5x20 *****************/
class PcbCamSymbolRcThsPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolRcThs)
public:
    PcbCamSymbolRcThsPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolRcThsPrivate() { }

    int sign(qreal val) const {
      return (val >= 0) + (val < 0) * (-1);
    }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
    qreal airGap {0.0};
};


PcbCamSymbolRcThs::PcbCamSymbolRcThs(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolRcThsPrivate)
{
    Q_D(PcbCamSymbolRcThs);
    d->type = PcbCam::Sym_RcThs;
    d->name = iName;
    QRegularExpression re("^rc_ths([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
    d->airGap = match.captured(6).toDouble()/1000.0;
}

PcbCamSymbolRcThs::~PcbCamSymbolRcThs()
{

}

QPainterPath PcbCamSymbolRcThs::shape() const
{
    Q_D(const PcbCamSymbolRcThs);
    if (d->shape.isEmpty()) {
        //TODO 当开口数量为3时可能有Bug;
        QPainterPath path;

        path.addRect(-d->width / 2, -d->height / 2, d->width, d->height);
        path.addRect(-d->width / 2 + d->airGap, -d->height / 2 + d->airGap,
            d->width - 2 * d->airGap, d->height - 2 * d->airGap);

        QPainterPath bar;
        bar.addRect(0, -d->spokesGap / 2, d->width, d->spokesGap);

        QPainterPath sub;

        qreal angle_div = 360.0 / d->numSpokes;

        qreal ang = d->startAngle;
        for (int i = 0; i < d->numSpokes; ++i, ang += angle_div) {
          QTransform mat;
          ang = qCeil(ang / 45) * 45.0;

          if ((int)ang % 90 != 0) {
            if (d->width > d->height) {
              mat.translate((d->width - d->height) / 2 * d->sign(qCos((ang) * (M_PI/180.0))), 0);
            } else {
              mat.translate(0, -(d->height - d->width) / 2 * d->sign(qSin((ang) * (M_PI/180.0))));
            }
          }
          mat.rotate(-ang);
          sub.addPath(mat.map(bar));
        }

        path = path.subtracted(sub);

        QTransform mat;
        mat.scale(1, -1);
        d->shape = mat.map(path);

    }
    return d->shape;
}

qreal PcbCamSymbolRcThs::width() const
{
    Q_D(const PcbCamSymbolRcThs);
    return d->width;
}

qreal PcbCamSymbolRcThs::height() const
{
    Q_D(const PcbCamSymbolRcThs);
    return d->height;
}

qreal PcbCamSymbolRcThs::startAngle() const
{
    Q_D(const PcbCamSymbolRcThs);
    return d->startAngle;
}

int PcbCamSymbolRcThs::numSpokes() const
{
    Q_D(const PcbCamSymbolRcThs);
    return d->numSpokes;
}

qreal PcbCamSymbolRcThs::spokesGap() const
{
    Q_D(const PcbCamSymbolRcThs);
    return d->spokesGap;
}

qreal PcbCamSymbolRcThs::airGap() const
{
    Q_D(const PcbCamSymbolRcThs);
    return d->airGap;
}


/********************* rc_tho100x70x30x5x20 *****************/
class PcbCamSymbolRcThoPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolRcTho)
public:
    PcbCamSymbolRcThoPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolRcThoPrivate() { }

    int sign(qreal val) const {
      return (val >= 0) + (val < 0) * (-1);
    }

    qreal width {0.0} ;
    qreal height {0.0} ;
    qreal startAngle {0.0} ;
    int numSpokes {1};
    qreal spokesGap {0.0};
    qreal airGap {0.0};
};


PcbCamSymbolRcTho::PcbCamSymbolRcTho(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolRcThoPrivate)
{
    Q_D(PcbCamSymbolRcTho);
    d->type = PcbCam::Sym_RcTho;
    d->name = iName;
    QRegularExpression re("^rc_tho([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9]+)x([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->width = match.captured(1).toDouble()/1000.0;
    d->height = match.captured(2).toDouble()/1000.0;
    d->startAngle = match.captured(3).toDouble();
    d->numSpokes = match.captured(4).toInt();
    d->spokesGap = match.captured(5).toDouble()/1000.0;
    d->airGap = match.captured(6).toDouble()/1000.0;
}

PcbCamSymbolRcTho::~PcbCamSymbolRcTho()
{

}

QPainterPath PcbCamSymbolRcTho::shape() const
{
    Q_D(const PcbCamSymbolRcTho);
    if (d->shape.isEmpty()) {
        QPainterPath path;

        qreal angle_div = 360.0 / d->numSpokes;
        QPainterPath sub;
        QTransform mat;

        if ((d->numSpokes != 1 && d->numSpokes != 2 && d->numSpokes != 4) ||
            ((int)d->startAngle % 45 != 0))
        {
            //TOP8LOG.error() << "invalid symbol " << d->name;
            return QPainterPath();
        }

        path.addRect(-d->width / 2, -d->height / 2, d->width, d->height);
        path.addRect(-d->width / 2 + d->airGap, -d->height / 2 + d->airGap,
            d->width - 2 * d->airGap, d->height - 2 * d->airGap);

        if ((int)d->startAngle % 90 == 0) {
          QPainterPath bar;
          bar.addRect(0, -d->spokesGap / 2, d->width / 2, d->spokesGap);

          for (int i = 0; i < d->numSpokes; ++i) {
            sub.addPath(mat.map(bar));
            mat.rotate(-angle_div);
          }
        } else {
          qreal side = d->spokesGap * qCos(M_PI / 4) + d->airGap;
          qreal offset_w = (d->width - side) / 2, offset_h = (d->height - side) / 2;

          QPainterPath box;
          box.addRect(-side / 2, -side / 2, side, side);

          for (int i = 0; i < d->numSpokes; ++i) {
            QTransform mat;
            mat.translate(offset_w * d->sign(qCos((d->startAngle + angle_div * i) * (M_PI/180.0))),
                          -offset_h * d->sign(qSin((d->startAngle + angle_div * i) * (M_PI/180.0))));
            sub.addPath(mat.map(box));
          }
        }

        path = path.subtracted(sub);

        QTransform mat2;
        mat2.scale(1, -1);
        d->shape = mat2.map(path);

    }
    return d->shape;
}

qreal PcbCamSymbolRcTho::width() const
{
    Q_D(const PcbCamSymbolRcTho);
    return d->width;
}

qreal PcbCamSymbolRcTho::height() const
{
    Q_D(const PcbCamSymbolRcTho);
    return d->height;
}

qreal PcbCamSymbolRcTho::startAngle() const
{
    Q_D(const PcbCamSymbolRcTho);
    return d->startAngle;
}

int PcbCamSymbolRcTho::numSpokes() const
{
    Q_D(const PcbCamSymbolRcTho);
    return d->numSpokes;
}

qreal PcbCamSymbolRcTho::spokesGap() const
{
    Q_D(const PcbCamSymbolRcTho);
    return d->spokesGap;
}

qreal PcbCamSymbolRcTho::airGap() const
{
    Q_D(const PcbCamSymbolRcTho);
    return d->airGap;
}

/********************* el10x20 *****************/
class PcbCamSymbolElPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolEl)
public:
    PcbCamSymbolElPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolElPrivate() { }

    QSizeF size ;
};


PcbCamSymbolEl::PcbCamSymbolEl(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolElPrivate)
{
    Q_D(PcbCamSymbolEl);
    d->type = PcbCam::Sym_El;
    d->name = iName;
    QRegularExpression re("^el([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->size = QSizeF(match.captured(1).toDouble()/1000.0, match.captured(2).toDouble()/1000.0);
}

PcbCamSymbolEl::~PcbCamSymbolEl()
{

}

QPainterPath PcbCamSymbolEl::shape() const
{
    Q_D(const PcbCamSymbolEl);
    if (d->shape.isEmpty()) {
        d->shape.addEllipse(QRectF(-d->size.width()/2.0, -d->size.height()/2.0, d->size.width(), d->size.height()));
    }
    return d->shape;
}

QSizeF PcbCamSymbolEl::size() const
{
    Q_D(const PcbCamSymbolEl);
    return d->size;
}


/********************* moire10x10x3x1x100x30 *****************/
class PcbCamSymbolMoirePrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolMoire)
public:
    PcbCamSymbolMoirePrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolMoirePrivate() { }

    qreal ringWidth {0.0};
    qreal ringGap {0.0};
    int numRings {1};
    qreal lineWidth {0.0};
    qreal lineLength {0.0};
    qreal lineAngle {0.0};
};


PcbCamSymbolMoire::PcbCamSymbolMoire(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolMoirePrivate)
{
    Q_D(PcbCamSymbolMoire);
    d->type = PcbCam::Sym_Moire;
    d->name = iName;
    QRegularExpression re("^moire([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9.]+)x([0-9.]+)$");
    QRegularExpressionMatch match = re.match(d->name);
    d->ringWidth = match.captured(1).toDouble()/1000.0;
    d->ringGap = match.captured(2).toDouble()/1000.0;
    d->numRings = match.captured(3).toInt();
    d->lineWidth = match.captured(4).toDouble()/1000.0;
    d->lineLength = match.captured(5).toDouble()/1000.0;
    d->lineAngle = match.captured(6).toDouble();
}

PcbCamSymbolMoire::~PcbCamSymbolMoire()
{

}

QPainterPath PcbCamSymbolMoire::shape() const
{
    Q_D(const PcbCamSymbolMoire);
    if (d->shape.isEmpty()) {
        QPainterPath ringpath;

        ringpath.addEllipse(-d->ringWidth/2.0, -d->ringWidth/2.0, d->ringWidth, d->ringWidth);
        QPainterPathStroker rstroker;
        rstroker.setWidth(d->ringWidth);
        rstroker.setJoinStyle(Qt::RoundJoin);
        rstroker.setCapStyle(Qt::RoundCap);
        for (int i = 1; i <= d->numRings; ++i) {
            qreal rad = d->ringWidth/2.0 + (d->ringGap + d->ringWidth/2.0) * i;
            QPainterPath p;
            p.addEllipse(-rad, -rad, rad*2.0, rad*2.0);
            ringpath.addPath(rstroker.createStroke(p));
        }


        QPainterPath linepath;
        linepath.moveTo(-d->lineLength/2.0, 0);
        linepath.lineTo(d->lineLength/2.0, 0);
        linepath.moveTo(0, -d->lineLength/2.0);
        linepath.lineTo(0, d->lineLength/2.0);
        QPainterPathStroker lstroker;
        lstroker.setWidth(d->lineWidth);
        lstroker.setJoinStyle(Qt::RoundJoin);
        lstroker.setCapStyle(Qt::RoundCap);
        linepath = lstroker.createStroke(linepath);

        QTransform mat1;
        mat1.scale(1000, 1000);
        ringpath = mat1.map(ringpath).simplified();
        linepath = mat1.map(linepath).simplified();

        QPainterPath path = ringpath.united(linepath);
        QTransform mat2;
        mat2.scale(0.001, 0.001);
        mat2.rotate(-d->lineAngle);
        d->shape = mat2.map(path);

    }
    return d->shape;
}

qreal PcbCamSymbolMoire::ringWidth() const
{
    Q_D(const PcbCamSymbolMoire);
    return d->ringWidth;
}

qreal PcbCamSymbolMoire::ringGap() const
{
    Q_D(const PcbCamSymbolMoire);
    return d->ringGap;
}

int PcbCamSymbolMoire::numRings() const
{
    Q_D(const PcbCamSymbolMoire);
    return d->numRings;
}

qreal PcbCamSymbolMoire::lineWidth() const
{
    Q_D(const PcbCamSymbolMoire);
    return d->lineWidth;
}

qreal PcbCamSymbolMoire::lineLength() const
{
    Q_D(const PcbCamSymbolMoire);
    return d->lineLength;
}

qreal PcbCamSymbolMoire::lineAngle() const
{
    Q_D(const PcbCamSymbolMoire);
    return d->lineAngle;
}






/********************* user symbol *****************/
class PcbCamSymbolUserPrivate : public PcbCamSymbolPrivate
{
    Q_DECLARE_PUBLIC(PcbCamSymbolUser)
public:
    PcbCamSymbolUserPrivate() : PcbCamSymbolPrivate() { }
    ~PcbCamSymbolUserPrivate() { }

    void destoryFeatures() {
        qDeleteAll(features);
        features.clear();
    }

    QList<PcbCamFeature *> features;

    int maxIndex {0};

};


PcbCamSymbolUser::PcbCamSymbolUser(const QString iName)
    : PcbCamSymbol(*new PcbCamSymbolUserPrivate)
{
    Q_D(PcbCamSymbolUser);
    d->type = PcbCam::Sym_User;
    d->name = iName;
}

PcbCamSymbolUser::~PcbCamSymbolUser()
{

}


QPainterPath PcbCamSymbolUser::shape() const
{
    Q_D(const PcbCamSymbolUser);
    if (d->shape.isEmpty()) {
        for (auto feat : d->features) {
            d->shape.addPath(feat->shape());
        }
    }
    return d->shape;
}

QList<PcbCamFeature *> PcbCamSymbolUser::features() const
{
    Q_D(const PcbCamSymbolUser);
    return d->features;
}

void PcbCamSymbolUser::clearFeatures()
{
    Q_D(PcbCamSymbolUser);
    d->destoryFeatures();
    d->maxIndex = 0;
}

void PcbCamSymbolUser::addFeature(PcbCamFeature *iFeature)
{
    Q_D(PcbCamSymbolUser);
    iFeature->setIndex(++d->maxIndex);
    d->features.append(iFeature);
}

void PcbCamSymbolUser::addFeatures(const QList<PcbCamFeature *> &iFeatures)
{
    for (PcbCamFeature *feat : iFeatures) {
        addFeature(feat);
    }
}

void PcbCamSymbolUser::deleteFeature(int iIndex)
{
    Q_D(PcbCamSymbolUser);
    for (int i = 0; i < d->features.count(); ++i) {
        PcbCamFeature *feat = d->features[i];
        if (feat->index() == iIndex) {
            d->features.removeAt(i);
            delete feat;
            return;
        }
    }
}
