#include <iostream>
#include <math.h>
#include <qwidget.h>
#include <qpainter.h>
#include <qpointarray.h>
#include <qrect.h>
#include <qbitmap.h>
#include <qpixmap.h>
#include <qimage.h>

using std::cout;
using std::endl;

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

double Render2D::getAngle(QPoint a, QPoint b) {
  double dx = b.x() - a.x();
  double dy = b.y() - a.y();
  double ang = atan((double)dy/(double)dx) * (180.0 / M_PI);
  if (ang < 0.0) ang *= -1.0;
  
  if ((b.x() > a.x()) && (b.y() > a.y())) ang = 0.0 + ang;
  if ((b.x() < a.x()) && (b.y() > a.y())) ang = 180.0 - ang;
  if ((b.x() < a.x()) && (b.y() < a.y())) ang = 180.0 + ang;
  if ((b.x() > a.x()) && (b.y() < a.y())) ang = 360.0 - ang;
  if (dx == 0.0) {
    if (dy < 0.0)
      ang = 270.0;
    else
      ang = 90.0;
  }
  if (dy == 0.0) {
    if (dx < 0.0)
      ang = 180.0;
    else
      ang = 0.0;
  }

  return ang;
}

void Render2D::drawLine(QPoint a, QPoint b, int t, QColor c1, int s) {
  if (outputDevice == OUTPUT_EPS) {
    a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    b.setY(b.y() + (2 * (selectionBox.center().y() - b.y() ) ) );
    output_ts << "newpath" << endl;
    output_ts << a.x() << " " << a.y() << " moveto" << endl;
    output_ts << b.x() << " " << b.y() << " lineto" << endl;
    output_ts << "closepath stroke" << endl;
    return;
  }
  if (outputDevice == OUTPUT_SVG) {
    //a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    //b.setY(b.y() + (2 * (selectionBox.center().y() - b.y() ) ) );
    a.setX(a.x() - svg_dx);
    a.setY(a.y() - svg_dy);
    b.setX(b.x() - svg_dx);
    b.setY(b.y() - svg_dy);
    output_ts << "<line x1=\"";
    output_ts << a.x() << "\" y1=\"" << a.y() << "\" x2=\"";
    output_ts << b.x() << "\" y2=\"" << b.y() << "\" stroke=\"";
    output_ts << c1.name() << "\" stroke-width=\"" << t << "\"/>" << endl;
    return;
  }
  if (outputDevice == OUTPUT_PRINTER) {
    // put line on paintqueue to render in Print()
    Paintable *pa = new Paintable;
    if (s == 0)
      pa->op = OP_LINE;
    else
      pa->op = OP_DASH_LINE;
    pa->a = a;
    pa->b = b;
    pa->c = c1;
    pa->s = s;
    pa->t = t;
    paintqueue.append(pa);
    return;
  }
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  if (s == 0) // solid
    //p.setPen(QPen(c1, t - 1));
    p.setPen(QPen(c1, t));
  else // dashed
    p.setPen(QPen(c1,1,DotLine));
  p.drawLine(a, b);
}

// This function exists to turn Polyline into bits for drawLine(...)
// (to avoid reimplementing EPS/Printer stuff for now :)
void Render2D::drawPolyline(QPointArray a1, QColor c1) {
  int lastpoint = a1.size() - 1;
  for (int cc = 0; cc < lastpoint; cc++)
    drawLine(a1[cc], a1[cc+1], 1, c1);
}

void Render2D::drawBracket(QPoint a, QPoint b, QColor c1, int type) {
  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_SQUARE) {
    drawLine(tl, bl, 1, c1);
    drawLine(tr, br, 1, c1);
    drawLine(tl, QPoint(tl_x + sf, tl_y), 1, c1);
    drawLine(bl, QPoint(tl_x + sf, br_y), 1, c1);
    drawLine(tr, QPoint(br_x - sf, tl_y), 1, c1);
    drawLine(br, QPoint(br_x - 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;
    QPointArray pa;
    pa.makeArc(box_x, box_y, wx * 2, wx * 2, -ang1, 2.0 * ang1);
    drawPolyline(pa, c1);
    pa.makeArc(box_x, box_y, wx * 2, wx * 2, -ang1 + 2880, 2.0 * ang1);
    drawPolyline(pa, c1);
  }
  if (type == BRACKET_BRACE) {
    drawLine(QPoint(tl_x + sf2, tl_y), QPoint(tl_x + sf2, br_y), 1, c1);
    drawLine(QPoint(br_x - sf2, tl_y), QPoint(br_x - sf2, br_y), 1, c1);
    drawLine(QPoint(tl_x + sf2, tl_y), QPoint(tl_x + sf, tl_y), 1, c1);
    drawLine(QPoint(tl_x + sf2, br_y), QPoint(tl_x + sf, br_y), 1, c1);
    drawLine(QPoint(br_x - sf2, tl_y), QPoint(br_x - sf, tl_y), 1, c1);
    drawLine(QPoint(br_x - sf2, br_y), QPoint(br_x - sf, br_y), 1, c1);
    double midy = (tl_y + br_y) / 2.0;
    drawLine(QPoint(tl_x, midy), QPoint(tl_x + sf2, midy), 1, c1);
    drawLine(QPoint(br_x, midy), QPoint(br_x - sf2, midy), 1, c1);
  }
}

void Render2D::drawUpLine(QPoint a, QPoint 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);
  QPoint t1( (int)(a.x() + dx), (int)(a.y() + dy) );
  dx = mysign * newlen * cos(newang2 / MOL_ARAD);
  dy = mysign * newlen * sin(newang2 / MOL_ARAD);
  QPoint t2( (int)(a.x() + dx), (int)(a.y() + dy) );
  if (outputDevice == OUTPUT_EPS) {
    a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    t1.setY(t1.y() + (2 * (selectionBox.center().y() - t1.y() ) ) );
    t2.setY(t2.y() + (2 * (selectionBox.center().y() - t2.y() ) ) );
    output_ts << "newpath" << endl;
    output_ts << t1.x() << " " << t1.y() << " moveto" << endl;
    output_ts << t2.x() << " " << t2.y() << " lineto" << endl;
    output_ts << a.x() << " " << a.y() << " lineto" << endl;
    output_ts << t1.x() << " " << t1.y() << " lineto" << endl;
    output_ts << "closepath fill" << endl;	
    return;
  }
  if (outputDevice == OUTPUT_SVG) {
    a.setX(a.x() - svg_dx);
    a.setY(a.y() - svg_dy);
    t1.setX(t1.x() - svg_dx);
    t1.setY(t1.y() - svg_dy);
    t2.setX(t2.x() - svg_dx);
    t2.setY(t2.y() - svg_dy);
    output_ts << "<polygon fill=\"" << c1.name() << "\" points=\"";
    output_ts << t1.x() << "," << t1.y() << " ";
    output_ts << t2.x() << "," << t2.y() << " ";
    output_ts << a.x() << "," << a.y() << "\"/>" << endl;
    return;
  }
  if (outputDevice == OUTPUT_PRINTER) {
    // put line on paintqueue to render in Print()
    Paintable *pa = new Paintable;
    pa->op = OP_UP_LINE;
    pa->a = t1;
    pa->b = t2;
    pa->b1 = a;
    pa->c = c1;
    paintqueue.append(pa);
    return;
  }
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  p.setBrush(QBrush(c1));
  QPointArray triangle(3);
  triangle.setPoint(0, t1);
  triangle.setPoint(1, t2);
  triangle.setPoint(2, a);
  p.drawPolygon(triangle);
}

void Render2D::drawDownLine(QPoint a, QPoint 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);
	QPoint t1, t2;

  for (double scalefactor = 1.0; scalefactor < newlen; scalefactor = 
	 scalefactor + 4.0) {
    dx = scalefactor * cos(newang1 / MOL_ARAD);
    dy = scalefactor * sin(newang1 / MOL_ARAD);
    t1.setX( (a.x() + (int)dx) ); 
	t1.setY( (a.y() + (int)dy) );
    dx = scalefactor * cos(newang2 / MOL_ARAD);
    dy = scalefactor * sin(newang2 / MOL_ARAD);
    t2.setX( (a.x() + (int)dx) ); 
	t2.setY( (a.y() + (int)dy) );
    drawLine(t1, t2, 1, c1);
  }
  dx = cos(newang1 / MOL_ARAD);
  dy = sin(newang1 / MOL_ARAD);
    t1.setX( (a.x() + (int)dx) ); 
	t1.setY( (a.y() + (int)dy) );
  dx = cos(newang2 / MOL_ARAD);
  dy = sin(newang2 / MOL_ARAD);
    t2.setX( (a.x() + (int)dx) ); 
	t2.setY( (a.y() + (int)dy) );
  drawLine(t1, t2, 1, c1);
}

void Render2D::drawArrow(QPoint a, QPoint b, QColor c1, int s1) {
  // 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;

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

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

  if (s1 == ARROW_REGULAR) {
    drawLine(a, b, 1, c1);
    drawLine(b, a1, 1, c1);
    drawLine(b, a2, 1, c1);
  }
  if (s1 == ARROW_DASH) {
    drawLine(a, b, 1, c1, 1);
    drawLine(b, a1, 1, c1);
    drawLine(b, a2, 1, 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 = (int)(cos(myangle) * 2.5 * offs);
    y2 = (int)(sin(myangle) * 2.5 * offs);
    QPoint sp1(a.x() + x2, a.y() + y2);
    QPoint ep1(b.x() + x2, b.y() + y2); 
    QPoint sp2(a.x() - x2, a.y() - y2); 
    QPoint ep2(b.x() - x2, b.y() - y2); 
    QPoint a8(ep2.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      ep2.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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 = (int)(cos(myangle) * 4.0 * offs);
    y2 = (int)(sin(myangle) * 4.0 * offs);
    QPoint sp1(a.x() + x2, a.y() + y2);
    QPoint ep1(b.x() + x2, b.y() + y2); 
    QPoint sp2(a.x() - x2, a.y() - y2); 
    QPoint 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;
    QPoint a8(b.x() + (cos(newang5/MOL_ARAD) * 10.0 * offs),
	      b.y() + (sin(newang5/MOL_ARAD) * 10.0 * offs));
    QPoint 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) {
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  p.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) {
  if (outputDevice == OUTPUT_EPS) {
    a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    b.setY(b.y() + (2 * (selectionBox.center().y() - b.y() ) ) );
    output_ts << "1.0 1.0 1.0 setrgbcolor" << endl;
    output_ts << "newpath" << endl;
    output_ts << a.x() << " " << a.y() << " moveto" << endl;
    output_ts << a.x() << " " << b.y() << " lineto" << endl;
    output_ts << b.x() << " " << b.y() << " lineto" << endl;
    output_ts << b.x() << " " << a.y() << " lineto" << endl;
    output_ts << a.x() << " " << a.y() << " lineto" << endl;
    output_ts << "closepath fill" << endl;
    output_ts << "0.0 0.0 0.0 setrgbcolor" << endl;
    return;
  }
  if (outputDevice == OUTPUT_SVG) {
    a.setX(a.x() - svg_dx);
    a.setY(a.y() - svg_dy);
    b.setX(b.x() - svg_dx);
    b.setY(b.y() - svg_dy);
    //a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    //b.setY(b.y() + (2 * (selectionBox.center().y() - b.y() ) ) );
    output_ts << "<rect x=\"" << a.x() << "\" y=\"" << a.y() << "\" width=\""
	      << b.x() - a.x() << "\" height=\"" << b.y() - a.y()
	      << "\" fill=\"" << c1.name() << "\"/>" << endl; 
    return;
  }
  if (outputDevice == OUTPUT_PRINTER) {
    // put line on paintqueue to render in Print()
    Paintable *pa = new Paintable;
    pa->op = OP_FILLBOX;
    pa->a = a;
    pa->b = b;
    pa->c = c1;
    return;
  }
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  p.fillRect(QRect(a, b), c1);
  if (border) {
    if (style == 0) p.setPen(bordercolor);
    if (style == 1) p.setPen(QPen(bordercolor,0,DotLine));
    p.drawRect(QRect(a,b));
  }
}

void Render2D::drawCircle(QPoint a, int rad, QColor cfill) {
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setBrush(cfill);
  p.setPen(QColor(0,0,0));
  p.drawEllipse(a.x(), a.y(), rad * 2, rad * 2);
}

void Render2D::drawText(QChar ch, QPoint a, QColor c1, QFont f) {
  int fsize;
  fsize = f.pointSize();
  if (fsize < 1) fsize = f.pixelSize();
  if (outputDevice == OUTPUT_EPS) {
    // calc flip
    a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    output_ts << "/" << f.family() << " " << fsize;
    output_ts << " selectfont" << endl;
    output_ts << a.x() << " " << a.y() << " moveto" << endl;
    if ( (ch == '(') || (ch == ')') )
      output_ts << "(\\" << ch << ") show" << endl;
    else
      output_ts << "(" << ch << ") show" << endl;
    return;
  }
  if (outputDevice == OUTPUT_SVG) {
    // calc flip
    //a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    a.setX(a.x() - svg_dx);
    a.setY(a.y() - svg_dy);
    output_ts << "<text x=\"" << a.x() << "\" y=\"" << a.y() << 
      "\" font-family=\"" << f.family() << "\" font-size=\"" << 
      fsize << "\" fill=\"" << c1.name() << "\">";
    output_ts << ch << "</text>" << endl;
    return;
  }
  if (outputDevice == OUTPUT_PRINTER) {
    // put line on paintqueue to render in Print()
    Paintable *pa = new Paintable;
    pa->op = OP_TEXT;
    pa->a = a;
    pa->c = c1;
    pa->f = f;
    pa->ch = ch;
    paintqueue.append(pa);
    return;
  }
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  p.setFont(f);
  p.drawText(a, ch);
}

void Render2D::drawString(QString s1, QPoint a, QColor c1, QFont f) {
  if (outputDevice == OUTPUT_EPS) {
    // calc flip
    a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    output_ts << "/" << f.family() << " " << f.pointSize();
    output_ts << " selectfont" << endl;
    output_ts << a.x() << " " << a.y() << " moveto" << endl;
    output_ts << "(" << s1 << ") show" << endl;
    return;
  }
  if (outputDevice == OUTPUT_SVG) {
    // calc flip
    //a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) );
    a.setX(a.x() - svg_dx);
    a.setY(a.y() - svg_dy);
    output_ts << "<text x=\"" << a.x() << "\" y=\"" << a.y() << 
      "\" font-family=\"" << f.family() << "\" font-size=\"" << 
      f.pointSize() << "\" fill=\"" << c1.name() << "\">";
    output_ts << s1 << "</text>" << endl;
    return;
  }
  /*
  if (outputDevice == OUTPUT_PRINTER) {
    // put line on paintqueue to render in Print()
    Paintable *pa = new Paintable;
    pa->op = OP_TEXT;
    pa->a = a;
    pa->c = c1;
    pa->f = f;
    pa->ch = ch;
    paintqueue.append(pa);
    return;
  }
  */
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  p.setFont(f);
  p.drawText(a, s1);
}

void Render2D::drawTextReverse(QChar ch, QPoint a, QColor c1, QFont f) {
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  p.setFont(f);
  QFontMetrics fm = p.fontMetrics();
  QPoint topleft(a.x(), a.y() - fm.ascent());
  QPoint bottomright(a.x() + fm.width(ch), a.y() + fm.descent());
  QRect fr(topleft, bottomright);
  p.fillRect(fr, c1);
  p.setPen(bgcolor);
  p.drawText(a, ch);
}

void Render2D::drawPixmap(QPoint a, QPixmap pix) {
  if (outputDevice == OUTPUT_EPS) {
    // calc flip
    a.setY(a.y() + (2 * (selectionBox.center().y() - a.y() ) ) - 16 );
    output_ts << "20 dict begin" << endl;
    output_ts << "/picstr 16 string def" << endl;
    output_ts << "gsave" << endl;
    output_ts << a.x() << " " << a.y() << " translate" << endl;
    output_ts << "16 16 scale" << endl;
    output_ts << "16 16 1" << endl;
    output_ts << "[16 0 0 -16 0 16]" << endl;
    output_ts << "{currentfile picstr readhexstring pop}" << endl;
    output_ts << "image" << endl;
    QImage im1 = pix.convertToImage();
    long sumtotal;
    int row, col;
    for (row = 0; row < 16; row++) {
      sumtotal = 0;
      for (col = 0; col < 16; col++) {
	if ( qGray(im1.pixel(col, row)) > 127 ) {
	  sumtotal += (long)pow(2.0, (double)(15 - col));
	}
      }
      cout << sumtotal << endl;
      output_ts << hex << sumtotal << dec << endl;
    }
    output_ts << "end" << endl << "grestore" << endl << endl;
    return;
  }
  if (outputDevice == OUTPUT_PRINTER) {
    // put line on paintqueue to render in Print()
    Paintable *pa = new Paintable;
    pa->op = OP_PIXMAP;
    pa->a = a;
    pa->p = pix;
    paintqueue.append(pa);
    return;
  }
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.drawPixmap(a, pix);
}

void Render2D::drawCurveArrow(QPoint a, QPoint 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;
  }
  QPainter p;
  if (directdraw) 
    p.begin(this);
  else
    p.begin(&dbuffer);
  //QPainter p(&dbuffer);
  p.setPen(c1);
  if (wh == "CW180") {
    // calculate curve
    QPoint ce = Midpoint(a, b);
    int d = RoundOff(DistanceBetween(a, ce));
    double sa = getAngle(ce, a);
    QPointArray pa;
    pa.makeArc(ce.x() - d, ce.y() - d, 2*d, 2*d, -sa*16, -2880);
    drawPolyline(pa, c1);
    // calculate arrowhead
    // if curve too small, don't draw arrowhead
    if (pa.count() == 0) return;
    QPoint realb(pa.at(pa.count() - 1));
    sa = getAngle(b, a);
    double newang1 = sa + 60.0;
    double newang2 = sa + 120.0;
    QPoint a1(realb.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      realb.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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 == "CCW180") {
    // calculate curve
    QPoint ce = Midpoint(a, b);
    int d = RoundOff(DistanceBetween(a, ce));
    double sa = getAngle(ce, a);
    QPointArray pa;
    pa.makeArc(ce.x() - d, ce.y() - d, 2*d, 2*d, -sa*16, 2880);
    drawPolyline(pa, c1);
    // calculate arrowhead
    // if curve too small, don't draw arrowhead
    if (pa.count() == 0) return;
    QPoint realb(pa.at(pa.count() - 1));
    sa = getAngle(b, a);
    double newang1 = sa + 240.0;
    double newang2 = sa + 300.0;
    QPoint a1(realb.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      realb.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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 == "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);
    QPoint ce(a.x() + RoundOff(dx2), a.y() + RoundOff(dy2));
    double sa = getAngle(ce, a);
    QPointArray pa;
    int d = RoundOff(d1);
    pa.makeArc(ce.x() - d, ce.y() - d, 2*d, 2*d, -sa*16, -1440);
    drawPolyline(pa, c1);    
    // calculate arrowhead
    // if curve too small, don't draw arrowhead
    if (pa.count() == 0) return;
    QPoint realb(pa.at(pa.count() - 1));
    sa = getAngle(b, a);
    double newang1 = sa + 15.0;
    double newang2 = sa + 75.0;
    QPoint a1(realb.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      realb.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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 == "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);
    QPoint ce(a.x() + RoundOff(dx2), a.y() + RoundOff(dy2));
    double sa = getAngle(ce, a);
    QPointArray pa;
    int d = RoundOff(d1);
    pa.makeArc(ce.x() - d, ce.y() - d, 2*d, 2*d, -sa*16, 1440);
    drawPolyline(pa, c1);    
    // calculate arrowhead
    // if curve too small, don't draw arrowhead
    if (pa.count() == 0) return;
    QPoint realb(pa.at(pa.count() - 1));
    sa = getAngle(b, a);
    double newang1 = sa - 15.0;
    double newang2 = sa - 75.0;
    QPoint a1(realb.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      realb.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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 == "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);
    QPoint ce(a.x() + RoundOff(dx2), a.y() + RoundOff(dy2));
    double sa = getAngle(ce, a);
    QPointArray pa;
    int d = RoundOff(d1);
    pa.makeArc(ce.x() - d, ce.y() - d, 2*d, 2*d, -sa*16, -4320);
    drawPolyline(pa, c1);    
    // calculate arrowhead
    // if curve too small, don't draw arrowhead
    if (pa.count() == 0) return;
    QPoint realb(pa.at(pa.count() - 1));
    sa = getAngle(b, a);
    double newang1 = sa + 165.0;
    double newang2 = sa + 105.0;
    QPoint a1(realb.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      realb.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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);
    QPoint ce(a.x() + RoundOff(dx2), a.y() + RoundOff(dy2));
    double sa = getAngle(ce, a);
    QPointArray pa;
    int d = RoundOff(d1);
    pa.makeArc(ce.x() - d, ce.y() - d, 2*d, 2*d, -sa*16, 4320);
    drawPolyline(pa, c1);    
    // calculate arrowhead
    // if curve too small, don't draw arrowhead
    if (pa.count() == 0) return;
    QPoint realb(pa.at(pa.count() - 1));
    sa = getAngle(b, a);
    double newang1 = sa - 165.0;
    double newang2 = sa - 105.0;
    QPoint a1(realb.x() + (cos(newang1/MOL_ARAD) * 10.0),
	      realb.y() + (sin(newang1/MOL_ARAD) * 10.0));
    QPoint 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) {
  prev_mode = mode;
  QRect r;
  c->SelectAll();
  if (page_size < PAGE_640)
    r = c->selectionBox();
  else
    r = QRect(0, 0, renderWidth, renderHeight);
  c->DeselectAll();
  mode = MODE_SELECT;  // so selection box will not appear
  repaint(false);
  QPixmap pm( r.size() );

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

  bitBlt( &pm, 0, 0, this, r.left(), r.top(), r.width(), r.height() );

  if (transp) {
    QBitmap bm(r.size());
    bm = pm;
    QImage bi;
    bi = bm;
    //bi.invertPixels();
    bm = bi;
    pm.setMask(bm);
  }

  mode = prev_mode;
  return pm;
}

// Make QPixmap of this drawing.
QPixmap Render2D::MakeFullPixmap() {
  c->DeselectAll();
  mode = MODE_SELECT;  // so selection box will not appear
  repaint(false);
  QPixmap pm( size() );
  bitBlt( &pm, QPoint(0, 0), this, rect() );

  return pm;
}
