// XDrawChem
// Copyright (C) 2004-2005  Bryan Herger <bherger@users.sourceforge.net>
// Copyright (C) 2020  Yaman Qalieh <ybq987@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include <QBitmap>
#include <QPainter>
#include <cmath>

#include "chemdata.h"
#include "defs.h"
#include "paintable.h"
#include "render2d.h"

double Render2D::getAngle(QPointF a, QPointF b) {
    return atan2(b.y() - a.y(), b.x() - a.x()) * 180 / M_PI;
}

void Render2D::drawLine(QPointF a, QPointF b, int t, QColor c1, int s) {
    if (s == 0) // solid
        painter->setPen(QPen(c1, t));
    else // dashed -- always apply thickness
        painter->setPen(QPen(c1, t, Qt::DotLine));

    painter->drawLine(a, b);
}

// This function exists to turn Polyline into bits for drawLine(...)
// (to avoid reimplementing EPS/Printer stuff for now :)
void Render2D::drawPolyline(QPainterPath a1, QColor c1) {
    /// TODO
    painter->drawPath(a1);
}

void Render2D::drawPolyline(QVector<QPoint> p, QColor c1) {
    if (!p.isEmpty()) {
        painter->setPen(c1);
        painter->drawLines(p);
    }
}

// note that this only works on screen and bitmap (for now)
void Render2D::drawBezier(QVector<QPoint> a1, QColor c1, bool drawPoints, int arrowHead) {
    qDebug() << "Render2D::drawBezier()";
    /*
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.scale(zoomFactor, zoomFactor);
     */
    /*    painter->setPen( c1 );
        int xp1, yp1;

        if ( drawPoints ) {
            for ( int l1 = 0; l1 <= bezier_count; l1++ ) {
                a1.point( l1, &xp1, &yp1 );
                painter->drawRect( xp1 - 1, yp1 - 1, 3, 3 );
            }
        }
        if ( drawPoints == false ) {
            painter->drawCubicBezier( a1 );
            QPoint ar1, ar2;        // apparent arrowhead shaft:  (ar1)-->(ar2)

            ar1 = a1.point( a1.count() - 2 );
            ar2 = a1.point( a1.count() - 1 );

            double ang;

            ang = getAngle( ar2, ar1 );

            double newang1 = ang + 30.0;
            double newang2 = ang - 30.0;

            QPoint p1( qRound( ar2.x() + ( cos( newang1 / MOL_ARAD ) * 10.0 ) ),
       qRound( ar2.y() + ( sin( newang1 / MOL_ARAD ) * 10.0 ) ) ); QPoint p2(
       qRound( ar2.x() + ( cos( newang2 / MOL_ARAD ) * 10.0 ) ), qRound( ar2.y()
       + ( sin( newang2 / MOL_ARAD ) * 10.0 ) ) );

            if ( arrowHead > 0 ) {
                // draw half arrowhead
                drawLine( ar2, p1, 1, c1 );
            }
            if ( arrowHead > 1 ) {
                // draw other half
                drawLine( ar2, p2, 1, c1 );
            }
            return;
        }
        if ( ( mode == MODE_DRAWBEZIER ) && ( bezier_count < 3 ) )
            return;
        painter->drawCubicBezier( a1 );*/
}

void Render2D::drawBracket(QPoint a, QPoint b, QColor c1, int type, QColor fillColor) {
    int tl_x, tl_y, br_x, br_y, swp;

    tl_x = a.x();
    tl_y = a.y();
    br_x = b.x();
    br_y = b.y();
    if (tl_x > br_x) {
        swp = tl_x;
        tl_x = br_x;
        br_x = swp;
    }
    if (tl_y > br_y) {
        swp = tl_y;
        tl_y = br_y;
        br_y = swp;
    }
    double sf = 0.08 * (double)(br_y - tl_y);
    double sf2 = sf / 2.0;
    QPoint tl(tl_x, tl_y);
    QPoint bl(tl_x, br_y);
    QPoint tr(br_x, tl_y);
    QPoint br(br_x, br_y);

    if ((type == BRACKET_BOX) || (type == BRACKET_CLOSEDSQUARE)) {
        /*
           drawLine(tl, bl, 1, c1);
           drawLine(bl, br, 1, c1);
           drawLine(tr, br, 1, c1);
           drawLine(tl, tr, 1, c1);
         */
        if (fillColor.isValid() == true) {
            drawFillBox(tl, br, fillColor, true, c1, 0);
        } else {
            drawBox(tl, br, c1);
        }
    }
    if ((type == BRACKET_ELLIPSE) || (type == BRACKET_CIRCLE)) {
        /*
           QPointArray el;
           el.makeEllipse(tl.x(), tl.y(), br.x() - tl.x(), br.y() - tl.y() );
           drawPolyline(el, c1);
         */
        if (fillColor.isValid() == true) {
            drawEllipse(tl, br, true, c1, true, fillColor);
        } else {
            drawEllipse(tl, br, true, c1, false, fillColor);
        }
    }
    if (type == BRACKET_SQUARE) {
        drawLine(tl, bl, 1, c1);
        drawLine(tr, br, 1, c1);
        drawLine(tl, QPoint(tl_x + qRound(sf), tl_y), 1, c1);
        drawLine(bl, QPoint(tl_x + qRound(sf), br_y), 1, c1);
        drawLine(tr, QPoint(br_x - qRound(sf), tl_y), 1, c1);
        drawLine(br, QPoint(br_x - qRound(sf), br_y), 1, c1);
    }
    if (type == BRACKET_CURVE) {
        // calculate center of circle
        double cx = (tl_x + br_x) / 2.0;
        double cy = (tl_y + br_y) / 2.0;

        // calculate box enclosing circle
        double wx = br_x - tl_x;
        double wy2 = (br_y - tl_y) / 2.0;
        double box_x = cx - wx;
        double box_y = cy - wx;
        double ang1 = acos(wy2 / wx) * 180.0 / M_PI;

        ang1 = 90.0 - ang1;
        // ang1 *= 16.0;
        QPainterPath pp;

        pp.moveTo(br);
        pp.arcTo(qRound(box_x), qRound(box_y), qRound(wx * 2.0), qRound(wx * 2.0), qRound(-ang1),
                 qRound(2.0 * ang1));
        drawPolyline(pp, c1);
        pp.moveTo(tl);
        pp.arcTo(qRound(box_x), qRound(box_y), qRound(wx * 2.0), qRound(wx * 2.0),
                 qRound(-ang1 + 180.0), qRound(2.0 * ang1));
        drawPolyline(pp, c1);
    }
    if (type == BRACKET_BRACE) {
        drawLine(QPoint(tl_x + qRound(sf2), tl_y), QPoint(tl_x + qRound(sf2), br_y), 1, c1);
        drawLine(QPoint(br_x - qRound(sf2), tl_y), QPoint(br_x - qRound(sf2), br_y), 1, c1);
        drawLine(QPoint(tl_x + qRound(sf2), tl_y), QPoint(tl_x + qRound(sf), tl_y), 1, c1);
        drawLine(QPoint(tl_x + qRound(sf2), br_y), QPoint(tl_x + qRound(sf), br_y), 1, c1);
        drawLine(QPoint(br_x - qRound(sf2), tl_y), QPoint(br_x - qRound(sf), tl_y), 1, c1);
        drawLine(QPoint(br_x - qRound(sf2), br_y), QPoint(br_x - qRound(sf), br_y), 1, c1);
        double midy = (tl_y + br_y) / 2.0;

        drawLine(QPoint(tl_x, qRound(midy)), QPoint(tl_x + qRound(sf2), qRound(midy)), 1, c1);
        drawLine(QPoint(br_x, qRound(midy)), QPoint(br_x - qRound(sf2), qRound(midy)), 1, c1);
    }
}

void Render2D::drawWavyLine(QPointF a, QPointF b, QColor c1) {
    /*
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.setPen(c1);
     */

    double dampl = 3.0;
    QPointF last, swpc;

    last.setX(-10);
    if (a.x() > b.x()) {
        swpc = a;
        a = b;
        b = swpc;
    }
    double ia = getAngle(a, b);
    double dx = b.x() - a.x();
    double dy = b.y() - a.y();
    double dmag = sqrt(dx * dx + dy * dy);
    int nwaves = qRound(dmag / 6.0);
    double wwidth = dmag / (double)nwaves;
    int cn;
    double dx1, dy1, ia_rad, dx2, dy2;

    for (cn = 0; cn <= (int)dmag; cn++) {
        dx1 = (double)cn;
        dy1 = dampl * sin(cn * M_PI / wwidth);
        ia_rad = ia * M_PI / 180.0;
        dx2 = dx1 * cos(ia_rad) - dy1 * sin(ia_rad);
        dy2 = dx1 * sin(ia_rad) + dy1 * cos(ia_rad);
        dx2 += (double)a.x();
        dy2 += (double)a.y();
        swpc.setX((int)dx2);
        swpc.setY((int)dy2);
        // drawLine(swpc, swpc, 1, c1, 0);
        if (last.x() > 0) {
            // p.drawPoint(swpc);
            drawLine(last, swpc, 1, c1, 0);
        }
        last = swpc;
    }
}

void Render2D::drawUpLine(QPointF a, QPointF b, QColor c1) {
    double ang = getAngle(a, b);
    double dx = b.x() - a.x();
    double dy = b.y() - a.y();
    double angvar = asin(3.0 / sqrt(dx * dx + dy * dy)) * 180 / M_PI;
    double newang1 = ang + angvar;
    double newang2 = ang - angvar;
    double newlen = sqrt(dx * dx + dy * dy);
    double mysign = 1.0;

    dx = mysign * newlen * cos(newang1 / MOL_ARAD);
    dy = mysign * newlen * sin(newang1 / MOL_ARAD);
    QPointF t1(a.x() + dx, a.y() + dy);

    dx = mysign * newlen * cos(newang2 / MOL_ARAD);
    dy = mysign * newlen * sin(newang2 / MOL_ARAD);
    QPointF t2(a.x() + dx, a.y() + dy);

    painter->setPen(c1);
    painter->setBrush(QBrush(c1));
    QPolygonF triangle;
    triangle << t1 << t2 << a;
    painter->drawPolygon(triangle);
    painter->setBrush(QBrush(Qt::NoBrush));
}

void Render2D::drawDownLine(QPointF a, QPointF b, QColor c1) {

    double ang = getAngle(a, b) / MOL_ARAD;
    double dx = b.x() - a.x();
    double dy = b.y() - a.y();

    double angvar = asin(4.0 / sqrt(dx * dx + dy * dy));
    double upang = ang + angvar;
    double cosup = cos(upang);
    double sinup = sin(upang);

    double downang = ang - angvar;
    double cosdown = cos(downang);
    double sindown = sin(downang);

    double newlen = sqrt(dx * dx + dy * dy);
    QPointF t1, t2;

    for (double scalefactor = 1.0; scalefactor < newlen; scalefactor = scalefactor + 3.0) {
        t1.setX((a.x() + scalefactor * cosup));
        t1.setY((a.y() + scalefactor * sinup));

        t2.setX((a.x() + scalefactor * cosdown));
        t2.setY((a.y() + scalefactor * sindown));
        drawLine(t1, t2, 1, c1);
    }
}

void Render2D::drawArrow(QPointF a, QPointF b, QColor c1, int s1, int th1) {
    // Build arrowhead(s)
    double ang, oppang, myangle, x2, y2;

    oppang = getAngle(a, b);
    ang = getAngle(b, a);

    double newang1 = ang + 30.0;
    double newang2 = ang - 30.0;

    double newang3 = oppang + 30.0;
    double newang4 = oppang - 30.0;

    QPointF a1(b.x() + (cos(newang1 / MOL_ARAD) * 10.0), b.y() + (sin(newang1 / MOL_ARAD) * 10.0));
    QPointF a2(b.x() + (cos(newang2 / MOL_ARAD) * 10.0), b.y() + (sin(newang2 / MOL_ARAD) * 10.0));

    QPointF a3(a.x() + (cos(newang3 / MOL_ARAD) * 10.0), a.y() + (sin(newang3 / MOL_ARAD) * 10.0));
    QPointF a4(a.x() + (cos(newang4 / MOL_ARAD) * 10.0), a.y() + (sin(newang4 / MOL_ARAD) * 10.0));

    if (s1 == ARROW_REGULAR) {
        drawLine(a, b, th1, c1);
        drawLine(b, a1, th1, c1);
        drawLine(b, a2, th1, c1);
    }
    if (s1 == ARROW_TOPHARPOON) {
        drawLine(a, b, th1, c1);
        if (a1.y() < a2.y())
            drawLine(b, a1, th1, c1);
        else
            drawLine(b, a2, th1, c1);
    }
    if (s1 == ARROW_BOTTOMHARPOON) {
        drawLine(a, b, th1, c1);
        if (a1.y() > a2.y())
            drawLine(b, a1, th1, c1);
        else
            drawLine(b, a2, th1, c1);
    }
    if (s1 == ARROW_MIDDLE) {
        QPointF m1 = Midpoint(a, b);
        double newang5 = ang + 45.0;
        double newang6 = ang - 45.0;
        double newang7 = oppang + 45.0;
        double newang8 = oppang - 45.0;
        QPointF a5(m1.x() + (cos(newang5 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang5 / MOL_ARAD) * 10.0));
        QPointF a6(m1.x() + (cos(newang6 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang6 / MOL_ARAD) * 10.0));
        QPointF a7(m1.x() + (cos(newang7 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang7 / MOL_ARAD) * 10.0));
        QPointF a8(m1.x() + (cos(newang8 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang8 / MOL_ARAD) * 10.0));

        drawLine(a, b, th1, c1);
        // drawLine(b, a1, th1, c1);
        // drawLine(b, a2, th1, c1);
        drawLine(m1, a5, th1, c1);
        drawLine(m1, a6, th1, c1);
        // drawLine(m1, a7, th1, c1);
        // drawLine(m1, a8, th1, c1);
    }
    if (s1 == ARROW_DIDNT_WORK) {
        QPointF m1 = Midpoint(a, b);
        double newang5 = ang + 45.0;
        double newang6 = ang - 45.0;
        double newang7 = oppang + 45.0;
        double newang8 = oppang - 45.0;
        QPointF a5(m1.x() + (cos(newang5 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang5 / MOL_ARAD) * 10.0));
        QPointF a6(m1.x() + (cos(newang6 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang6 / MOL_ARAD) * 10.0));
        QPointF a7(m1.x() + (cos(newang7 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang7 / MOL_ARAD) * 10.0));
        QPointF a8(m1.x() + (cos(newang8 / MOL_ARAD) * 10.0),
                   m1.y() + (sin(newang8 / MOL_ARAD) * 10.0));

        drawLine(a, b, th1, c1);
        drawLine(b, a1, th1, c1);
        drawLine(b, a2, th1, c1);
        drawLine(m1, a5, th1, c1);
        drawLine(m1, a6, th1, c1);
        drawLine(m1, a7, th1, c1);
        drawLine(m1, a8, th1, c1);
    }
    if (s1 == ARROW_DASH) {
        drawLine(a, b, th1, c1, 1);
        drawLine(b, a1, th1, c1);
        drawLine(b, a2, th1, c1);
    }
    if (s1 == ARROW_BI1) {
        drawLine(a, b, 1, c1);
        drawLine(b, a1, 1, c1);
        drawLine(b, a2, 1, c1);
        drawLine(a, a3, 1, c1);
        drawLine(a, a4, 1, c1);
    }
    if (s1 == ARROW_BI2) {
        myangle = getAngle(a, b);
        myangle += 90.0;
        myangle = myangle * (M_PI / 180.0);
        double offs = DistanceBetween(a, b) / 25.0;

        if (offs > 2.0)
            offs = 2.0;
        x2 = cos(myangle) * 2.5 * offs;
        y2 = sin(myangle) * 2.5 * offs;
        QPointF sp1(a.x() + x2, a.y() + y2);
        QPointF ep1(b.x() + x2, b.y() + y2);
        QPointF sp2(a.x() - x2, a.y() - y2);
        QPointF ep2(b.x() - x2, b.y() - y2);
        QPointF a8(ep2.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   ep2.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a9(sp1.x() + (cos(newang3 / MOL_ARAD) * 10.0),
                   sp1.y() + (sin(newang3 / MOL_ARAD) * 10.0));

        drawLine(sp1, ep1, 1, c1);
        drawLine(ep2, a8, 1, c1);
        drawLine(sp2, ep2, 1, c1);
        drawLine(sp1, a9, 1, c1);
    }
    if (s1 == ARROW_RETRO) {
        myangle = getAngle(a, b);
        myangle += 90.0;
        myangle = myangle * (M_PI / 180.0);
        double offs = DistanceBetween(a, b) / 25.0;

        if (offs > 2.0)
            offs = 2.0;
        x2 = cos(myangle) * 4.0 * offs;
        y2 = sin(myangle) * 4.0 * offs;
        QPointF sp1(a.x() + x2, a.y() + y2);
        QPointF ep1(b.x() + x2, b.y() + y2);
        QPointF sp2(a.x() - x2, a.y() - y2);
        QPointF ep2(b.x() - x2, b.y() - y2);
        double dx = ep1.x() - sp1.x();
        double dy = ep1.y() - sp1.y();

        dx = dx / DistanceBetween(sp1, ep1);
        dy = dy / DistanceBetween(sp1, ep1);
        dx = dx * (DistanceBetween(sp1, ep1) - (4.0 * offs));
        dy = dy * (DistanceBetween(sp1, ep1) - (4.0 * offs));
        ep1.setX(sp1.x() + dx);
        ep1.setY(sp1.y() + dy);
        ep2.setX(sp2.x() + dx);
        ep2.setY(sp2.y() + dy);
        double newang5 = ang + 45.0;
        double newang6 = ang - 45.0;
        QPointF a8(b.x() + (cos(newang5 / MOL_ARAD) * 10.0 * offs),
                   b.y() + (sin(newang5 / MOL_ARAD) * 10.0 * offs));
        QPointF a9(b.x() + (cos(newang6 / MOL_ARAD) * 10.0 * offs),
                   b.y() + (sin(newang6 / MOL_ARAD) * 10.0 * offs));

        drawLine(sp1, ep1, 1, c1);
        drawLine(sp2, ep2, 1, c1);
        drawLine(sp1, sp2, 1, c1);
        drawLine(b, a8, 1, c1);
        drawLine(b, a9, 1, c1);
    }
}

void Render2D::drawBox(QPoint a, QPoint b, QColor c1) {
    /*
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.scale(zoomFactor, zoomFactor);
     */
    painter->setPen(c1);
    painter->setBrush(QBrush(Qt::NoBrush));
    painter->drawRect(QRect(a, b));
}

void Render2D::drawFillBox(QPoint a, QPoint b, QColor c1) {
    drawFillBox(a, b, c1, false, QColor(255, 255, 255), 0);
}
void Render2D::drawFillBox(QPoint a, QPoint b, QColor c1, bool border, QColor bordercolor,
                           int style) {
    painter->setPen(c1);
    painter->fillRect(QRect(a, b), c1);
    if (border) {
        if (style == 0)
            painter->setPen(bordercolor);
        if (style == 1)
            painter->setPen(QPen(bordercolor, 0, Qt::DotLine));
        painter->drawRect(QRect(a, b));
    }
}

void Render2D::drawCircle(QPoint a, int rad, QColor cfill) {
    /*
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.scale(zoomFactor, zoomFactor);
     */
    painter->setBrush(cfill);
    painter->setPen(QColor(0, 0, 0));
    painter->drawEllipse(a.x(), a.y(), rad * 2, rad * 2);
    painter->setBrush(QBrush(Qt::NoBrush));
}

void Render2D::drawEllipse(QPoint tl, QPoint br, bool drawLine, QColor lineColor, bool drawFill,
                           QColor fillColor) {
    /*
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.scale(zoomFactor, zoomFactor);
     */
    if (drawFill)
        painter->setBrush(fillColor);
    if (drawLine)
        painter->setPen(lineColor);
    painter->drawEllipse(QRect(tl, br));
    painter->setBrush(QBrush(Qt::NoBrush));
}

void Render2D::drawText(QChar ch, QPoint a, QColor c1, QFont f) {
    qDebug() << "drawText: " << ch;
    int fsize, dy;

    fsize = f.pointSize();
    if (fsize < 1)
        fsize = f.pixelSize();
    painter->setPen(c1);
    painter->setFont(f);
    painter->drawText(a, ch);
}

void Render2D::drawString(QString s1, QPoint a, QColor c1, QFont f) {
    painter->setPen(c1);
    painter->setFont(f);
    painter->drawText(a, s1);
}

void Render2D::drawTextReverse(QChar ch, QPoint a, QColor c1, QFont f) {
    /*
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.scale(zoomFactor, zoomFactor);
     */
    painter->setPen(c1);
    painter->setFont(f);
    QFontMetrics fm = painter->fontMetrics();
    QPoint topleft(a.x(), a.y() - fm.ascent());
    QPoint bottomright(a.x() + fm.horizontalAdvance(ch), a.y() + fm.descent());
    QRect fr(topleft, bottomright);

    painter->fillRect(fr, c1);
    painter->setPen(bgcolor);
    painter->drawText(a, ch);
}

void Render2D::drawPixmap(QPoint a, QPixmap pix) {
    painter->drawPixmap(a, pix);
}

void Render2D::drawCurveArrow(QPointF a, QPointF b, QColor c1, QString wh) {
    /*
       if (outputDevice == OUTPUT_PRINTER) {
       // put curve on paintqueue to render in Print()
       Paintable *pa = new Paintable;
       if (wh == "CW90")
       pa->op = OP_CURVE_CW90;
       if (wh == "CCW90")
       pa->op = OP_CURVE_CCW90;
       if (wh == "CW180")
       pa->op = OP_CURVE_CW180;
       if (wh == "CCW180")
       pa->op = OP_CURVE_CCW180;
       if (wh == "CW270")
       pa->op = OP_CURVE_CW270;
       if (wh == "CCW270")
       pa->op = OP_CURVE_CCW270;
       pa->a = a;
       pa->b = b;
       pa->c = c1;
       paintqueue.append(pa);
       return;
       }
       if (directdraw)
       p.begin(this);
       else
       p.begin(&dbuffer);
       p.scale(zoomFactor, zoomFactor);
     */
    QPainterPath pp;
    painter->setPen(QPen(c1, 1));
    if (wh == "CW180") {
        // calculate curve
        QPointF ce = Midpoint(a, b);
        double d1 = DistanceBetween(a, ce);
        double sa = getAngle(ce, a);

        pp.moveTo(a);
        pp.arcTo(ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, -sa, -180);
        painter->drawPath(pp);
        // painter->drawArc( ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, qRound(-sa *
        // 16), -180 * 16 ); painter->drawArc( 0, 0, 100, 100, 0, 180 );
        // drawPolyline( pp, c1 );
        // calculate arrowhead
        // if curve too small, don't draw arrowhead
        QPointF realb(pp.currentPosition().toPoint());
        sa = getAngle(b, a);
        double newang1 = sa + 60.0;
        double newang2 = sa + 120.0;
        QPointF a1(realb.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a2(realb.x() + (cos(newang2 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang2 / MOL_ARAD) * 10.0));
        drawLine(realb, a1, 1, c1);
        drawLine(realb, a2, 1, c1);
        return;
    }
    if (wh == "CCW180") {
        // calculate curve
        QPointF ce = Midpoint(a, b);
        double d1 = DistanceBetween(a, ce);
        double sa = getAngle(ce, a);

        pp.moveTo(a);
        pp.arcTo(ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, -sa, 180);
        painter->drawPath(pp);
        // painter->drawArc( ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, qRound(-sa *
        // 16), 180 * 16 );
        // calculate arrowhead
        // if curve too small, don't draw arrowhead
        QPointF realb(pp.currentPosition().toPoint());
        sa = getAngle(b, a);
        double newang1 = sa + 240.0;
        double newang2 = sa + 300.0;
        QPointF a1(realb.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a2(realb.x() + (cos(newang2 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang2 / MOL_ARAD) * 10.0));
        drawLine(realb, a1, 1, c1);
        drawLine(realb, a2, 1, c1);
        return;
    }
    if (wh == "CW90") {
        // first, figure out where middle of circle is
        double d1 = DistanceBetween(a, b);
        double ia = getAngle(a, b);

        d1 = d1 / 1.4142136;
        ia = 45.0;
        double dx1 = ((double)b.x() - (double)a.x()) / 1.4142136;
        double dy1 = ((double)b.y() - (double)a.y()) / 1.4142136;

        // rotate vector (dx1, dy1) 45 degrees clockwise
        double ia_rad = ia * M_PI / 180.0;
        double dx2 = dx1 * cos(ia_rad) - dy1 * sin(ia_rad);
        double dy2 = dx1 * sin(ia_rad) + dy1 * cos(ia_rad);
        QPointF ce(a.x() + dx2, a.y() + dy2);
        double sa = getAngle(ce, a);

        pp.moveTo(a);
        pp.arcTo(ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, -sa, -90);
        drawPolyline(pp, c1);
        // painter->drawChord( QRect( a, b ), 180 * 16, -90 * 16 );
        // calculate arrowhead
        // if curve too small, don't draw arrowhead
        //     if (pa.count() == 0) return;
        // QPointF realb(pa.at(pa.count() - 1));
        sa = getAngle(b, a);
        double newang1 = sa + 15.0;
        double newang2 = sa + 75.0;
        QPointF a1(b.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   b.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a2(b.x() + (cos(newang2 / MOL_ARAD) * 10.0),
                   b.y() + (sin(newang2 / MOL_ARAD) * 10.0));
        drawLine(b, a1, 1, c1);
        drawLine(b, a2, 1, c1);
    }
    if (wh == "CCW90") {
        // first, figure out where middle of circle is
        double d1 = DistanceBetween(a, b);
        double ia = getAngle(a, b);

        d1 = d1 / 1.4142136;
        ia = -45.0;
        double dx1 = ((double)b.x() - (double)a.x()) / 1.4142136;
        double dy1 = ((double)b.y() - (double)a.y()) / 1.4142136;

        // rotate vector (dx1, dy1) 45 degrees clockwise
        double ia_rad = ia * M_PI / 180.0;
        double dx2 = dx1 * cos(ia_rad) - dy1 * sin(ia_rad);
        double dy2 = dx1 * sin(ia_rad) + dy1 * cos(ia_rad);
        QPointF ce(a.x() + dx2, a.y() + dy2);
        double sa = getAngle(ce, a);

        pp.moveTo(a);
        pp.arcTo(ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, -sa, 90);
        drawPolyline(pp, c1);
        // calculate arrowhead
        // if curve too small, don't draw arrowhead
        //    if (pa.count() == 0) return;
        // QPointF realb(pa.at(pa.count() - 1));
        sa = getAngle(b, a);
        double newang1 = sa - 15.0;
        double newang2 = sa - 75.0;
        QPointF a1(b.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   b.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a2(b.x() + (cos(newang2 / MOL_ARAD) * 10.0),
                   b.y() + (sin(newang2 / MOL_ARAD) * 10.0));
        drawLine(b, a1, 1, c1);
        drawLine(b, a2, 1, c1);
    }
    if (wh == "CW270") {
        // first, figure out where middle of circle is
        double d1 = DistanceBetween(a, b);
        double ia = getAngle(a, b);

        d1 = d1 / 1.4142136;
        ia = -45.0;
        double dx1 = ((double)b.x() - (double)a.x()) / 1.4142136;
        double dy1 = ((double)b.y() - (double)a.y()) / 1.4142136;

        // rotate vector (dx1, dy1) 45 degrees clockwise
        double ia_rad = ia * M_PI / 180.0;
        double dx2 = dx1 * cos(ia_rad) - dy1 * sin(ia_rad);
        double dy2 = dx1 * sin(ia_rad) + dy1 * cos(ia_rad);
        QPointF ce(a.x() + dx2, a.y() + dy2);
        double sa = getAngle(ce, a);

        pp.moveTo(a);
        pp.arcTo(ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, -sa, -270);
        drawPolyline(pp, c1);
        // calculate arrowhead
        // if curve too small, don't draw arrowhead
        //    if (pa.count() == 0) return;
        // QPointF realb(pa.at(pa.count() - 1));
        QPointF realb(pp.currentPosition().toPoint());
        sa = getAngle(b, a);
        double newang1 = sa + 165.0;
        double newang2 = sa + 105.0;
        QPointF a1(realb.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a2(realb.x() + (cos(newang2 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang2 / MOL_ARAD) * 10.0));
        drawLine(realb, a1, 1, c1);
        drawLine(realb, a2, 1, c1);
    }
    if (wh == "CCW270") {
        // first, figure out where middle of circle is
        double d1 = DistanceBetween(a, b);
        double ia = getAngle(a, b);

        d1 = d1 / 1.4142136;
        ia = 45.0;
        double dx1 = ((double)b.x() - (double)a.x()) / 1.4142136;
        double dy1 = ((double)b.y() - (double)a.y()) / 1.4142136;

        // rotate vector (dx1, dy1) 45 degrees clockwise
        double ia_rad = ia * M_PI / 180.0;
        double dx2 = dx1 * cos(ia_rad) - dy1 * sin(ia_rad);
        double dy2 = dx1 * sin(ia_rad) + dy1 * cos(ia_rad);
        QPointF ce(a.x() + dx2, a.y() + dy2);
        double sa = getAngle(ce, a);

        pp.moveTo(a);
        pp.arcTo(ce.x() - d1, ce.y() - d1, 2 * d1, 2 * d1, -sa, 270);
        drawPolyline(pp, c1);
        // calculate arrowhead
        // if curve too small, don't draw arrowhead
        //    if (pa.count() == 0) return;
        // QPointF realb(pa.at(pa.count() - 1));
        QPointF realb(pp.currentPosition().toPoint());
        sa = getAngle(b, a);
        double newang1 = sa - 165.0;
        double newang2 = sa - 105.0;
        QPointF a1(realb.x() + (cos(newang1 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang1 / MOL_ARAD) * 10.0));
        QPointF a2(realb.x() + (cos(newang2 / MOL_ARAD) * 10.0),
                   realb.y() + (sin(newang2 / MOL_ARAD) * 10.0));
        drawLine(realb, a1, 1, c1);
        drawLine(realb, a2, 1, c1);
    }
}

// Make QPixmap of this drawing.
// just the drawing if page_size set to paper (LETTER, LEGAL, A4, ...)
// whole area if set to a screen size (640, 800, 1024)
QPixmap Render2D::MakePixmap(bool transp) {
    double oldspace = preferences.getDoubleBondOffset();

    preferences.setDoubleBondOffset(oldspace * 2.0);
    font_size_kludge = true;
    prev_mode = mode;
    QRect r;

    c->SelectAll();

    DPoint dp1;

    selectionBox = c->selectionBox();
    QRect finalsize = selectionBox;

    dp1.set(selectionBox.topLeft());
    c->Resize(&dp1, 2.0);

    if (preferences.getPageSize() < PAGE_640)
        r = c->selectionBox();
    else
        r = QRect(0, 0, renderWidth, renderHeight);
    c->DeselectAll();
    mode = MODE_SELECT; // so selection box will not appear
    finishedPainting = false;
    update();
    r.setTopLeft(zoomCorrect(r.topLeft()));
    r.setBottomRight(zoomCorrect(r.bottomRight()));

    // hack to avoid cropping text
    r.adjust(-10, -10, 10, 10);

    QPixmap pm(r.size());

    qDebug() << "X1:" << r.left() << " Y1:" << r.top();
    qDebug() << "W:" << r.width() << " H:" << r.height();

    // while ( !finishedPainting ) {
    //    qInfo() << "Waiting...";
    //}
    // bitBlt( &pm, 0, 0, this, r.left(), r.top(), r.width(), r.height() );
    // deprecated: pm = QPixmap::grabWidget( this, r.left(), r.top(), r.width(),
    // r.height() );
    pm = grab(r);

    pm = pm.scaled(finalsize.width(), finalsize.height(), Qt::KeepAspectRatio,
                   Qt::SmoothTransformation);

    if (transp) {
        QBitmap bm(r.size());

        bm = pm;
        QImage bi;

        bi = bm.toImage();
        // bi.invertPixels();
        bm = QBitmap::fromImage(bi);
        pm.setMask(bm);
    }

    mode = prev_mode;

    c->SelectAll();
    selectionBox = c->selectionBox();
    dp1.set(selectionBox.topLeft());
    c->Resize(&dp1, 0.5);
    c->DeselectAll();
    font_size_kludge = false;
    preferences.setDoubleBondOffset(oldspace);

    return pm;
}

// Make QPixmap of this drawing.
QPixmap Render2D::MakeFullPixmap() {
    c->DeselectAll();
    mode = MODE_SELECT; // so selection box will not appear
    update();
    QPixmap pm(size());

    // deprecated: pm = QPixmap::grabWidget( this, rect() );
    pm = grab(rect());

    return pm;
}
