00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051 #include "qtcolortriangle.h"
00052
00053 #include <QtCore/QEvent>
00054 #include <QtCore/QMap>
00055 #include <QtCore/QVarLengthArray>
00056 #include <QtGui/QConicalGradient>
00057 #include <QtGui/QFrame>
00058 #include <QtGui/QImage>
00059 #include <QtGui/QKeyEvent>
00060 #include <QtGui/QLayout>
00061 #include <QtGui/QMouseEvent>
00062 #include <QtGui/QPainter>
00063 #include <QtGui/QPainterPath>
00064 #include <QtGui/QPixmap>
00065 #include <QtGui/QResizeEvent>
00066 #include <QtGui/QToolTip>
00067 #include <QtGui/QVBoxLayout>
00068
00069 #include <math.h>
00070
00093 const double PI = 3.14159265358979323846264338327950288419717;
00094 const double TWOPI = 2.0*PI;
00095
00096
00097
00098
00099 struct DoubleColor
00100 {
00101 double r, g, b;
00102
00103 DoubleColor() : r(0.0), g(0.0), b(0.0) {}
00104 DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
00105 DoubleColor(const DoubleColor &c) : r(c.r), g(c.g), b(c.b) {}
00106 };
00107
00108
00109
00110
00111 struct Vertex {
00112 DoubleColor color;
00113 QPointF point;
00114
00115 Vertex(const DoubleColor &c, const QPointF &p) : color(c), point(p) {}
00116 Vertex(const QColor &c, const QPointF &p)
00117 : color(DoubleColor((double) c.red(), (double) c.green(),
00118 (double) c.blue())), point(p) {}
00119 };
00120
00125 static void swap(Vertex **a, Vertex **b)
00126 {
00127 Vertex *tmp = *a;
00128 *a = *b;
00129 *b = tmp;
00130 }
00131
00135 QtColorTriangle::QtColorTriangle(QWidget *parent)
00136 : QWidget(parent), bg(sizeHint(), QImage::Format_RGB32), selMode(Idle)
00137 {
00138 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
00139 setFocusPolicy(Qt::StrongFocus);
00140
00141 mustGenerateBackground = true;
00142
00143 QColor tmp;
00144 tmp.setHsv(76, 184, 206);
00145 setColor(tmp);
00146 }
00147
00151 QtColorTriangle::~QtColorTriangle()
00152 {
00153 }
00154
00160 void QtColorTriangle::polish()
00161 {
00162 outerRadius = (contentsRect().width() - 1) / 2;
00163 if ((contentsRect().height() - 1) / 2 < outerRadius)
00164 outerRadius = (contentsRect().height() - 1) / 2;
00165
00166 penWidth = (int) floor(outerRadius / 50.0);
00167 ellipseSize = (int) floor(outerRadius / 12.5);
00168
00169 double cx = (double) contentsRect().center().x();
00170 double cy = (double) contentsRect().center().y();
00171
00172 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00173 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00174 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00175 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00176 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00177 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00178 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00179 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00180
00181
00182 selectorPos = pointFromColor(curColor);
00183
00184 update();
00185 }
00186
00189 QSize QtColorTriangle::sizeHint() const
00190 {
00191 return QSize(100, 100);
00192 }
00193
00198 int QtColorTriangle::heightForWidth(int w) const
00199 {
00200 return w;
00201 }
00202
00209 void QtColorTriangle::genBackground()
00210 {
00211
00212 double innerRadius = outerRadius - outerRadius / 5;
00213
00214
00215 bg = QImage(contentsRect().size(), QImage::Format_ARGB32_Premultiplied);
00216 bg.fill(qRgba(0,0,0,0));
00217 QPainter p(&bg);
00218 p.setRenderHint(QPainter::Antialiasing);
00219
00220 QConicalGradient gradient(bg.rect().center(), 90);
00221 QColor color;
00222 for (double i = 0; i <= 1.0; i += 0.1) {
00223 #if QT_VERSION < 0x040100
00224 color.setHsv(int(i * 360.0), 255, 255);
00225 #else
00226 color.setHsv(int(360.0 - (i * 360.0)), 255, 255);
00227 #endif
00228 gradient.setColorAt(i, color);
00229 }
00230
00231 QRectF innerRadiusRect(bg.rect().center().x() - innerRadius, bg.rect().center().y() - innerRadius,
00232 innerRadius * 2 + 1, innerRadius * 2 + 1);
00233 QRectF outerRadiusRect(bg.rect().center().x() - outerRadius, bg.rect().center().y() - outerRadius,
00234 outerRadius * 2 + 1, outerRadius * 2 + 1);
00235 QPainterPath path;
00236 path.addEllipse(innerRadiusRect);
00237 path.addEllipse(outerRadiusRect);
00238
00239 p.save();
00240 p.setClipPath(path);
00241 p.fillRect(bg.rect(), gradient);
00242 p.restore();
00243
00244 double penThickness = bg.width() / 400.0;
00245 for (int f = 0; f <= 5760; f += 20) {
00246 int value = int((0.5 + cos(((f - 1800) / 5760.0) * TWOPI) / 2) * 255.0);
00247
00248 color.setHsv(int((f / 5760.0) * 360.0), 128 + (255 - value)/2, 255 - (255 - value)/4);
00249 p.setPen(QPen(color, penThickness));
00250 p.drawArc(innerRadiusRect, 1440 - f, 20);
00251
00252 color.setHsv(int((f / 5760.0) * 360.0), 128 + value/2, 255 - value/4);
00253 p.setPen(QPen(color, penThickness));
00254 p.drawArc(outerRadiusRect, 2880 - 1440 - f, 20);
00255 }
00256
00257 return;
00258 }
00259
00266 void QtColorTriangle::mouseMoveEvent(QMouseEvent *e)
00267 {
00268 if ((e->buttons() & Qt::LeftButton) == 0)
00269 return;
00270
00271 QPointF depos((double) e->pos().x(), (double) e->pos().y());
00272 bool newColor = false;
00273
00274 if (selMode == SelectingHue) {
00275
00276
00277
00278 a = angleAt(depos, contentsRect());
00279 b = a + TWOPI / 3.0;
00280 c = b + TWOPI / 3.0;
00281 if (b > TWOPI) b -= TWOPI;
00282 if (c > TWOPI) c -= TWOPI;
00283
00284 double am = a - PI/2;
00285 if (am < 0) am += TWOPI;
00286
00287 curHue = 360 - (int) (((am) * 360.0) / TWOPI);
00288 int h,s,v;
00289 curColor.getHsv(&h, &s, &v);
00290
00291 if (curHue != h) {
00292 newColor = true;
00293 curColor.setHsv(curHue, s, v, curColor.alpha());
00294 }
00295
00296 double cx = (double) contentsRect().center().x();
00297 double cy = (double) contentsRect().center().y();
00298
00299 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00300 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00301 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00302 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00303 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00304 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00305 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00306 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00307
00308 selectorPos = pointFromColor(curColor);
00309 } else {
00310 Vertex aa(Qt::black, pa);
00311 Vertex bb(Qt::black, pb);
00312 Vertex cc(Qt::black, pc);
00313
00314 Vertex *p1 = &aa;
00315 Vertex *p2 = &bb;
00316 Vertex *p3 = &cc;
00317 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00318 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00319 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00320
00321 selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
00322 QColor col = colorFromPoint(selectorPos);
00323 if (col != curColor) {
00324
00325
00326 int h,s,v;
00327 col.getHsv(&h, &s, &v);
00328 curColor.setHsv(curHue, s, v, curColor.alpha());
00329 newColor = true;
00330 }
00331 }
00332
00333 if (newColor)
00334 internalSetNewColor(curColor);
00335
00336 update();
00337 }
00338
00347 void QtColorTriangle::mousePressEvent(QMouseEvent *e)
00348 {
00349
00350 if (e->button() != Qt::LeftButton)
00351 return;
00352
00353 QPointF depos((double) e->pos().x(), (double) e->pos().y());
00354 double rad = radiusAt(depos, contentsRect());
00355 bool newColor = false;
00356
00357
00358
00359 if (rad > (outerRadius - (outerRadius / 5))) {
00360 selMode = SelectingHue;
00361
00362 a = angleAt(depos, contentsRect());
00363 b = a + TWOPI / 3.0;
00364 c = b + TWOPI / 3.0;
00365 if (b > TWOPI) b -= TWOPI;
00366 if (c > TWOPI) c -= TWOPI;
00367
00368 double am = a - PI/2;
00369 if (am < 0) am += TWOPI;
00370
00371 curHue = 360 - (int) ((am * 360.0) / TWOPI);
00372 int h,s,v;
00373 curColor.getHsv(&h, &s, &v);
00374
00375 if (h != curHue) {
00376 newColor = true;
00377 curColor.setHsv(curHue, s, v, curColor.alpha());
00378 }
00379
00380 double cx = (double) contentsRect().center().x();
00381 double cy = (double) contentsRect().center().y();
00382
00383 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00384 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00385 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00386 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00387 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00388 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00389 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00390 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00391
00392 selectorPos = pointFromColor(curColor);
00393 internalSetNewColor(curColor);
00394 } else {
00395 selMode = SelectingSatValue;
00396
00397 Vertex aa(Qt::black, pa);
00398 Vertex bb(Qt::black, pb);
00399 Vertex cc(Qt::black, pc);
00400
00401 Vertex *p1 = &aa;
00402 Vertex *p2 = &bb;
00403 Vertex *p3 = &cc;
00404 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00405 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00406 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00407
00408 selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
00409 QColor col = colorFromPoint(selectorPos);
00410 if (col != curColor) {
00411 int tempalpha = curColor.alpha();
00412 curColor = col;
00413 curColor.setAlpha(tempalpha);
00414 newColor = true;
00415 }
00416 }
00417
00418 if (newColor)
00419 internalSetNewColor(curColor);
00420
00421 update();
00422 }
00423
00429 void QtColorTriangle::mouseReleaseEvent(QMouseEvent *e)
00430 {
00431 if (e->button() == Qt::LeftButton)
00432 selMode = Idle;
00433 }
00434
00438 void QtColorTriangle::keyPressEvent(QKeyEvent *e)
00439 {
00440 switch (e->key()) {
00441 case Qt::Key_Left: {
00442 --curHue;
00443 if (curHue < 0) curHue += 360;
00444 int h,s,v;
00445 curColor.getHsv(&h, &s, &v);
00446 QColor tmp;
00447 tmp.setHsv(curHue, s, v);
00448 setColor(tmp);
00449 }
00450 break;
00451 case Qt::Key_Right: {
00452 ++curHue;
00453 if (curHue > 359) curHue -= 360;
00454 int h,s,v;
00455 curColor.getHsv(&h, &s, &v);
00456 QColor tmp;
00457 tmp.setHsv(curHue, s, v);
00458 setColor(tmp);
00459 }
00460 break;
00461 case Qt::Key_Up: {
00462 int h,s,v;
00463 curColor.getHsv(&h, &s, &v);
00464 QColor tmp;
00465 if (e->modifiers() & Qt::ShiftModifier) {
00466 if (s > 5) s -= 5;
00467 else s = 0;
00468 } else {
00469 if (v > 5) v -= 5;
00470 else v = 0;
00471 }
00472 tmp.setHsv(curHue, s, v);
00473 setColor(tmp);
00474 }
00475 break;
00476 case Qt::Key_Down: {
00477 int h,s,v;
00478 curColor.getHsv(&h, &s, &v);
00479 QColor tmp;
00480 if (e->modifiers() & Qt::ShiftModifier) {
00481 if (s < 250) s += 5;
00482 else s = 255;
00483 } else {
00484 if (v < 250) v += 5;
00485 else v = 255;
00486 }
00487 tmp.setHsv(curHue, s, v);
00488 setColor(tmp);
00489 }
00490 break;
00491 };
00492 }
00493
00499 void QtColorTriangle::resizeEvent(QResizeEvent *)
00500 {
00501 outerRadius = (contentsRect().width() - 1) / 2;
00502 if ((contentsRect().height() - 1) / 2 < outerRadius)
00503 outerRadius = (contentsRect().height() - 1) / 2;
00504
00505 penWidth = (int) floor(outerRadius / 50.0);
00506 ellipseSize = (int) floor(outerRadius / 12.5);
00507
00508 double cx = (double) contentsRect().center().x();
00509 double cy = (double) contentsRect().center().y();
00510
00511 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00512 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00513 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00514 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00515 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00516 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00517 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00518 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00519
00520
00521 selectorPos = pointFromColor(curColor);
00522
00523 mustGenerateBackground = true;
00524 update();
00525 }
00526
00533 void QtColorTriangle::paintEvent(QPaintEvent *e)
00534 {
00535 QPainter p(this);
00536 if (e->rect().intersects(contentsRect()))
00537 p.setClipRegion(e->region().intersect(contentsRect()));
00538 if (mustGenerateBackground) {
00539 genBackground();
00540 mustGenerateBackground = false;
00541 }
00542
00543
00544
00545 QImage buf = bg.copy();
00546
00547
00548 int h,s,v;
00549 curColor.getHsv(&h, &s, &v);
00550
00551
00552 QColor hueColor;
00553 hueColor.setHsv(curHue, 255, 255);
00554
00555
00556 drawTrigon(&buf, pa, pb, pc, hueColor);
00557
00558
00559 QPixmap pix = QPixmap::fromImage(buf);
00560 QPainter painter(&pix);
00561 painter.setRenderHint(QPainter::Antialiasing);
00562
00563
00564 QColor halfAlpha(0, 0, 0, 128);
00565 painter.setPen(QPen(halfAlpha, 0));
00566 painter.drawLine(pa, pb);
00567 painter.drawLine(pb, pc);
00568 painter.drawLine(pc, pa);
00569
00570 int ri, gi, bi;
00571 hueColor.getRgb(&ri, &gi, &bi);
00572 if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
00573 painter.setPen(QPen(Qt::black, penWidth));
00574 else
00575 painter.setPen(QPen(Qt::white, penWidth));
00576 painter.drawEllipse((int) (pd.x() - ellipseSize / 2.0),
00577 (int) (pd.y() - ellipseSize / 2.0),
00578 ellipseSize, ellipseSize);
00579
00580 curColor.getRgb(&ri, &gi, &bi);
00581
00582
00583
00584 if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
00585 painter.setPen(QPen(Qt::black, penWidth));
00586 else
00587 painter.setPen(QPen(Qt::white, penWidth));
00588
00589
00590 painter.drawEllipse(QRectF(selectorPos.x() - ellipseSize / 2.0,
00591 selectorPos.y() - ellipseSize / 2.0,
00592 ellipseSize + 0.5, ellipseSize + 0.5));
00593
00594
00595 p.drawPixmap(contentsRect().topLeft(), pix);
00596 }
00597
00598
00599 void QtColorTriangle::internalSetNewColor(const QColor& color)
00600 {
00601 emit colorChanged(color);
00602 }
00603
00604
00605
00615 void QtColorTriangle::drawTrigon(QImage *buf, const QPointF &pa,
00616 const QPointF &pb, const QPointF &pc,
00617 const QColor &color)
00618 {
00619
00620
00621
00622
00623
00624 Vertex aa(color, pa);
00625 Vertex bb(Qt::black, pb);
00626 Vertex cc(Qt::white, pc);
00627
00628
00629
00630 Vertex *p1 = &aa;
00631 Vertex *p2 = &bb;
00632 Vertex *p3 = &cc;
00633 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00634 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00635 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00636
00637
00638 double p1p2ydist = p2->point.y() - p1->point.y();
00639 double p1p3ydist = p3->point.y() - p1->point.y();
00640 double p2p3ydist = p3->point.y() - p2->point.y();
00641 double p1p2xdist = p2->point.x() - p1->point.x();
00642 double p1p3xdist = p3->point.x() - p1->point.x();
00643 double p2p3xdist = p3->point.x() - p2->point.x();
00644
00645
00646
00647 bool lefty = p1p2xdist < 0;
00648
00649
00650
00651
00652
00653 QVarLengthArray<DoubleColor, 2000> leftColors;
00654 QVarLengthArray<DoubleColor, 2000> rightColors;
00655 QVarLengthArray<double, 2000> leftX;
00656 QVarLengthArray<double, 2000> rightX;
00657
00658 leftColors.resize(int(floor(p3->point.y() + 1)));
00659 rightColors.resize(int(floor(p3->point.y() + 1)));
00660 leftX.resize(int(floor(p3->point.y() + 1)));
00661 rightX.resize(int(floor(p3->point.y() + 1)));
00662
00663
00664
00665 DoubleColor source;
00666 DoubleColor dest;
00667 double r, g, b;
00668 double rdelta, gdelta, bdelta;
00669 double x;
00670 double xdelta;
00671 int y1, y2;
00672
00673
00674 x = p1->point.x();
00675 source = p1->color;
00676 dest = p3->color;
00677 r = source.r;
00678 g = source.g;
00679 b = source.b;
00680 y1 = (int) floor(p1->point.y());
00681 y2 = (int) floor(p3->point.y());
00682
00683
00684
00685 xdelta = p1p3ydist == 0.0 ? 0.0 : p1p3xdist / p1p3ydist;
00686 rdelta = p1p3ydist == 0.0 ? 0.0 : (dest.r - r) / p1p3ydist;
00687 gdelta = p1p3ydist == 0.0 ? 0.0 : (dest.g - g) / p1p3ydist;
00688 bdelta = p1p3ydist == 0.0 ? 0.0 : (dest.b - b) / p1p3ydist;
00689
00690
00691 int y;
00692 for (y = y1; y < y2; ++y) {
00693 if (lefty) {
00694 rightColors[y] = DoubleColor(r, g, b);
00695 rightX[y] = x;
00696 } else {
00697 leftColors[y] = DoubleColor(r, g, b);
00698 leftX[y] = x;
00699 }
00700
00701 r += rdelta;
00702 g += gdelta;
00703 b += bdelta;
00704 x += xdelta;
00705 }
00706
00707
00708
00709 x = p1->point.x();
00710 source = p1->color;
00711 dest = p2->color;
00712 r = source.r;
00713 g = source.g;
00714 b = source.b;
00715 y1 = (int) floor(p1->point.y());
00716 y2 = (int) floor(p2->point.y());
00717
00718
00719
00720 xdelta = p1p2ydist == 0.0 ? 0.0 : p1p2xdist / p1p2ydist;
00721 rdelta = p1p2ydist == 0.0 ? 0.0 : (dest.r - r) / p1p2ydist;
00722 gdelta = p1p2ydist == 0.0 ? 0.0 : (dest.g - g) / p1p2ydist;
00723 bdelta = p1p2ydist == 0.0 ? 0.0 : (dest.b - b) / p1p2ydist;
00724
00725
00726 for (y = y1; y < y2; ++y) {
00727 if (lefty) {
00728 leftColors[y] = DoubleColor(r, g, b);
00729 leftX[y] = x;
00730 } else {
00731 rightColors[y] = DoubleColor(r, g, b);
00732 rightX[y] = x;
00733 }
00734
00735 r += rdelta;
00736 g += gdelta;
00737 b += bdelta;
00738 x += xdelta;
00739 }
00740
00741
00742
00743 x = p2->point.x();
00744 source = p2->color;
00745 dest = p3->color;
00746 r = source.r;
00747 g = source.g;
00748 b = source.b;
00749 y1 = (int) floor(p2->point.y());
00750 y2 = (int) floor(p3->point.y());
00751
00752
00753
00754 xdelta = p2p3ydist == 0.0 ? 0.0 : p2p3xdist / p2p3ydist;
00755 rdelta = p2p3ydist == 0.0 ? 0.0 : (dest.r - r) / p2p3ydist;
00756 gdelta = p2p3ydist == 0.0 ? 0.0 : (dest.g - g) / p2p3ydist;
00757 bdelta = p2p3ydist == 0.0 ? 0.0 : (dest.b - b) / p2p3ydist;
00758
00759
00760 for (y = y1; y < y2; ++y) {
00761 if (lefty) {
00762 leftColors[y] = DoubleColor(r, g, b);
00763 leftX[y] = x;
00764 } else {
00765 rightColors[y] = DoubleColor(r, g, b);
00766 rightX[y] = x;
00767 }
00768
00769 r += rdelta;
00770 g += gdelta;
00771 b += bdelta;
00772 x += xdelta;
00773 }
00774
00775
00776
00777 const int p3yfloor = int(floor(p3->point.y()));
00778 for (int y = int(floor(p1->point.y())); y < p3yfloor; ++y) {
00779 double lx = leftX[y];
00780 double rx = rightX[y];
00781
00782 int lxi = (int) floor(lx);
00783 int rxi = (int) floor(rx);
00784 DoubleColor rc = rightColors[y];
00785 DoubleColor lc = leftColors[y];
00786
00787
00788 double xdist = rx - lx;
00789 if (xdist != 0.0) {
00790 double r = lc.r;
00791 double g = lc.g;
00792 double b = lc.b;
00793 double rdelta = (rc.r - r) / xdist;
00794 double gdelta = (rc.g - g) / xdist;
00795 double bdelta = (rc.b - b) / xdist;
00796
00797 QRgb *scanline = reinterpret_cast<QRgb *>(buf->scanLine(y));
00798 scanline += lxi;
00799
00800
00801 for (int i = lxi; i < rxi; ++i) {
00802 *scanline++ = qRgb((int) r, (int) g, (int) b);
00803 r += rdelta;
00804 g += gdelta;
00805 b += bdelta;
00806 }
00807 }
00808 }
00809 }
00810
00815 void QtColorTriangle::setColor(const QColor &col)
00816 {
00817 if (col == curColor)
00818 return;
00819
00820 curColor = col;
00821
00822 int h, s, v;
00823 curColor.getHsv(&h, &s, &v);
00824
00825
00826 if (h != -1)
00827 curHue = h;
00828
00829 a = (((360 - curHue) * TWOPI) / 360.0);
00830 a += PI / 2.0;
00831 if (a > TWOPI) a -= TWOPI;
00832
00833 b = a + TWOPI/3;
00834 c = b + TWOPI/3;
00835
00836 if (b > TWOPI) b -= TWOPI;
00837 if (c > TWOPI) c -= TWOPI;
00838
00839 double cx = (double) contentsRect().center().x();
00840 double cy = (double) contentsRect().center().y();
00841 double innerRadius = outerRadius - (outerRadius / 5.0);
00842 double pointerRadius = outerRadius - (outerRadius / 10.0);
00843
00844 pa = QPointF(cx + (cos(a) * innerRadius), cy - (sin(a) * innerRadius));
00845 pb = QPointF(cx + (cos(b) * innerRadius), cy - (sin(b) * innerRadius));
00846 pc = QPointF(cx + (cos(c) * innerRadius), cy - (sin(c) * innerRadius));
00847 pd = QPointF(cx + (cos(a) * pointerRadius), cy - (sin(a) * pointerRadius));
00848
00849 selectorPos = pointFromColor(curColor);
00850 update();
00851
00852 internalSetNewColor(curColor);
00853 }
00854
00859 QColor QtColorTriangle::color() const
00860 {
00861 return curColor;
00862 }
00863
00869 double QtColorTriangle::radiusAt(const QPointF &pos, const QRect &rect) const
00870 {
00871 double mousexdist = pos.x() - (double) rect.center().x();
00872 double mouseydist = pos.y() - (double) rect.center().y();
00873 return sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
00874 }
00875
00883 double QtColorTriangle::angleAt(const QPointF &pos, const QRect &rect) const
00884 {
00885 double mousexdist = pos.x() - (double) rect.center().x();
00886 double mouseydist = pos.y() - (double) rect.center().y();
00887 double mouserad = sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
00888 if (mouserad == 0.0)
00889 return 0.0;
00890
00891 double angle = acos(mousexdist / mouserad);
00892 if (mouseydist >= 0)
00893 angle = TWOPI - angle;
00894
00895 return angle;
00896 }
00897
00902 inline double qsqr(double a)
00903 {
00904 return a * a;
00905 }
00906
00911 inline double vlen(double x, double y)
00912 {
00913 return sqrt(qsqr(x) + qsqr(y));
00914 }
00915
00920 inline double vprod(double x1, double y1, double x2, double y2)
00921 {
00922 return x1 * x2 + y1 * y2;
00923 }
00924
00930 bool angleBetweenAngles(double p, double a1, double a2)
00931 {
00932 if (a1 > a2) {
00933 a2 += TWOPI;
00934 if (p < PI) p += TWOPI;
00935 }
00936
00937 return p >= a1 && p < a2;
00938 }
00939
00957 static bool pointAbovePoint(double x, double y, double px, double py,
00958 double ax, double ay, double bx, double by)
00959 {
00960 bool result = false;
00961
00962 if (floor(ax) > floor(bx)) {
00963 if (floor(ay) < floor(by)) {
00964
00965 if (floor(x) < floor(px) || floor(y) < floor(py))
00966 result = true;
00967 } else if (floor(ay) > floor(by)) {
00968
00969 if (floor(x) > floor(px) || floor(y) < floor(py))
00970 result = true;
00971 } else {
00972
00973 if (y < ay) result = true;
00974 }
00975 } else if (floor(ax) < floor(bx)) {
00976 if (floor(ay) < floor(by)) {
00977
00978 if (floor(x) < floor(px) || floor(y) > floor(py))
00979 result = true;
00980 } else if (floor(ay) > floor(by)) {
00981
00982 if (floor(x) > floor(px) || floor(y) > floor(py))
00983 result = true;
00984 } else {
00985
00986 if (y > ay)
00987 result = true;
00988 }
00989 } else {
00990
00991 if (floor(ay) < floor(by)) {
00992 if (x < ax) result = true;
00993 } else if (floor(ay) > floor(by)) {
00994 if (x > ax) result = true;
00995 } else {
00996 if (!(x == ax && y == ay))
00997 result = true;
00998 }
00999 }
01000
01001 return result;
01002 }
01003
01011 static int pointInLine(double x, double y, double ax, double ay,
01012 double bx, double by)
01013 {
01014 if (ax > bx) {
01015 if (ay < by) {
01016
01017
01018
01019
01020 if (y <= ay && x >= ax)
01021 return -1;
01022
01023
01024
01025 if (y >= by && x <= bx)
01026 return 1;
01027 } else {
01028
01029
01030
01031 if (floor(ay) == floor(by)) {
01032
01033
01034
01035 if (x >= ax)
01036 return -1;
01037 else if (x <= bx)
01038 return 1;
01039 } else {
01040
01041
01042 if (y >= ay && x >= ax)
01043 return -1;
01044
01045
01046
01047 if (y <= by && x <= bx)
01048 return 1;
01049 }
01050 }
01051 } else {
01052 if (ay < by) {
01053
01054
01055
01056
01057 if (y <= ay && x <= ax)
01058 return -1;
01059
01060
01061
01062 if (y >= by && x >= bx)
01063 return 1;
01064 } else {
01065
01066
01067
01068 if (floor(ay) == floor(by)) {
01069 if (x <= ax)
01070 return -1;
01071 else if (x >= bx)
01072 return 1;
01073 } else {
01074
01075
01076 if (y >= ay && x <= ax)
01077 return -1;
01078
01079
01080
01081 if (y <= by && x >= bx)
01082 return 1;
01083 }
01084 }
01085 }
01086
01087
01088
01089 return 0;
01090 }
01091
01107 QPointF QtColorTriangle::movePointToTriangle(double x, double y, const Vertex &a,
01108 const Vertex &b, const Vertex &c) const
01109 {
01110
01111
01112
01113 double v1xA = x - a.point.x();
01114 double v1yA = y - a.point.y();
01115 double v2xA = b.point.x() - a.point.x();
01116 double v2yA = b.point.y() - a.point.y();
01117 double vpA = vprod(v1xA, v1yA, v2xA, v2yA);
01118 double cosA = vpA / (vlen(v1xA, v1yA) * vlen(v2xA, v2yA));
01119 double alphaA = acos(cosA);
01120
01121
01122
01123 double v1xB = x - b.point.x();
01124 double v1yB = y - b.point.y();
01125 double v2xB = c.point.x() - b.point.x();
01126 double v2yB = c.point.y() - b.point.y();
01127 double vpB = vprod(v1xB, v1yB, v2xB, v2yB);
01128 double cosB = vpB / (vlen(v1xB, v1yB) * vlen(v2xB, v2yB));
01129 double alphaB = acos(cosB);
01130
01131
01132
01133 double v1xC = x - c.point.x();
01134 double v1yC = y - c.point.y();
01135 double v2xC = a.point.x() - c.point.x();
01136 double v2yC = a.point.y() - c.point.y();
01137 double vpC = vprod(v1xC, v1yC, v2xC, v2yC);
01138 double cosC = vpC / (vlen(v1xC, v1yC) * vlen(v2xC, v2yC));
01139 double alphaC = acos(cosC);
01140
01141
01142
01143
01144 double angleA = angleAt(a.point, contentsRect());
01145 double angleB = angleAt(b.point, contentsRect());
01146 double angleC = angleAt(c.point, contentsRect());
01147 double angleP = angleAt(QPointF(x, y), contentsRect());
01148
01149
01150 if (angleBetweenAngles(angleP, angleA, angleB)) {
01151
01152
01153
01154
01155 double pdist = sqrt(qsqr(x - a.point.x()) + qsqr(y - a.point.y()));
01156
01157
01158 double p0x = a.point.x() + ((b.point.x() - a.point.x()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
01159 double p0y = a.point.y() + ((b.point.y() - a.point.y()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
01160
01161
01162
01163 if (pointAbovePoint(x, y, p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y())) {
01164
01165
01166 int n = pointInLine(p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y());
01167 if (n < 0)
01168 return a.point;
01169 else if (n > 0)
01170 return b.point;
01171
01172 return QPointF(p0x, p0y);
01173 }
01174 } else if (angleBetweenAngles(angleP, angleB, angleC)) {
01175
01176 double pdist = sqrt(qsqr(x - b.point.x()) + qsqr(y - b.point.y()));
01177
01178
01179 double p0x = b.point.x() + ((c.point.x() - b.point.x()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
01180 double p0y = b.point.y() + ((c.point.y() - b.point.y()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
01181
01182 if (pointAbovePoint(x, y, p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y())) {
01183 int n = pointInLine(p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y());
01184 if (n < 0)
01185 return b.point;
01186 else if (n > 0)
01187 return c.point;
01188 return QPointF(p0x, p0y);
01189 }
01190 } else if (angleBetweenAngles(angleP, angleC, angleA)) {
01191
01192 double pdist = sqrt(qsqr(x - c.point.x()) + qsqr(y - c.point.y()));
01193
01194
01195 double p0x = c.point.x() + ((a.point.x() - c.point.x()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
01196 double p0y = c.point.y() + ((a.point.y() - c.point.y()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
01197
01198 if (pointAbovePoint(x, y, p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y())) {
01199 int n = pointInLine(p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y());
01200 if (n < 0)
01201 return c.point;
01202 else if (n > 0)
01203 return a.point;
01204 return QPointF(p0x, p0y);
01205 }
01206 }
01207
01208
01209 return QPointF(x, y);
01210 }
01211
01226 QPointF QtColorTriangle::pointFromColor(const QColor &col) const
01227 {
01228
01229 if (col == Qt::black)
01230 return pb;
01231 else if (col == Qt::white)
01232 return pc;
01233
01234
01235 double ab_deltax = pb.x() - pa.x();
01236 double ab_deltay = pb.y() - pa.y();
01237 double bc_deltax = pc.x() - pb.x();
01238 double bc_deltay = pc.y() - pb.y();
01239 double ac_deltax = pc.x() - pa.x();
01240 double ac_deltay = pc.y() - pa.y();
01241
01242
01243 int hue,sat,val;
01244 col.getHsv(&hue, &sat, &val);
01245
01246
01247
01248 double p1 = pa.x() + (ab_deltax * (double) (255 - val)) / 255.0;
01249 double q1 = pa.y() + (ab_deltay * (double) (255 - val)) / 255.0;
01250 double p2 = pb.x() + (bc_deltax * (double) val) / 255.0;
01251 double q2 = pb.y() + (bc_deltay * (double) val) / 255.0;
01252
01253
01254
01255 double p3 = pa.x() + (ac_deltax * (double) (255 - sat)) / 255.0;
01256 double q3 = pa.y() + (ac_deltay * (double) (255 - sat)) / 255.0;
01257 double p4 = pb.x();
01258 double q4 = pb.y();
01259
01260
01261 double x = 0;
01262 double y = 0;
01263 if (p1 != p2) {
01264 double a = (q2 - q1) / (p2 - p1);
01265 double c = (q4 - q3) / (p4 - p3);
01266 double b = q1 - a * p1;
01267 double d = q3 - c * p3;
01268
01269 x = (d - b) / (a - c);
01270 y = a * x + b;
01271 }
01272 else {
01273 x = p1;
01274 y = q3 + (x - p3) * (q4 - q3) / (p4 - p3);
01275 }
01276
01277 return QPointF(x, y);
01278 }
01279
01287 QColor QtColorTriangle::colorFromPoint(const QPointF &p) const
01288 {
01289
01290 int outerRadius = (contentsRect().width() - 1) / 2;
01291 if ((contentsRect().height() - 1) / 2 < outerRadius)
01292 outerRadius = (contentsRect().height() - 1) / 2;
01293
01294
01295 double cx = (double) contentsRect().center().x();
01296 double cy = (double) contentsRect().center().y();
01297
01298
01299
01300 QPointF pa(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
01301 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
01302 QPointF pb(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
01303 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
01304 QPointF pc(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
01305 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
01306
01307
01308 double angle = a - PI/2.0;
01309 if (angle < 0) angle += TWOPI;
01310 double hue = (360.0 * angle) / TWOPI;
01311
01312
01313
01314 QColor color;
01315 color.setHsv(360 - (int) floor(hue), 255, 255);
01316
01317
01318
01319 Vertex aa(color, pa);
01320 Vertex bb(Qt::black, pb);
01321 Vertex cc(Qt::white, pc);
01322
01323
01324 Vertex *p1 = &aa;
01325 Vertex *p2 = &bb;
01326 Vertex *p3 = &cc;
01327 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
01328 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
01329 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
01330
01331
01332
01333 double p1p2ydist = p2->point.y() - p1->point.y();
01334 double p1p3ydist = p3->point.y() - p1->point.y();
01335 double p2p3ydist = p3->point.y() - p2->point.y();
01336 double p1p2xdist = p2->point.x() - p1->point.x();
01337 double p1p3xdist = p3->point.x() - p1->point.x();
01338 double p2p3xdist = p3->point.x() - p2->point.x();
01339
01340
01341
01342
01343
01344
01345 bool lefty = p1p2xdist < 0;
01346
01347
01348
01349
01350 bool firstshorty = (p.y() >= p1->point.y() && p.y() < p2->point.y());
01351
01352
01353
01354 double leftx;
01355 double rightx;
01356 if (lefty) {
01357 if (firstshorty) {
01358 leftx = p1->point.x();
01359 if (floor(p1p2ydist) != 0.0) {
01360 leftx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
01361 } else {
01362 leftx = qMin(p1->point.x(), p2->point.x());
01363 }
01364 } else {
01365 leftx = p2->point.x();
01366 if (floor(p2p3ydist) != 0.0) {
01367 leftx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
01368 } else {
01369 leftx = qMin(p2->point.x(), p3->point.x());
01370 }
01371 }
01372
01373 rightx = p1->point.x();
01374 rightx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
01375 } else {
01376 leftx = p1->point.x();
01377 leftx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
01378
01379 if (firstshorty) {
01380 rightx = p1->point.x();
01381 if (floor(p1p2ydist) != 0.0) {
01382 rightx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
01383 } else {
01384 rightx = qMax(p1->point.x(), p2->point.x());
01385 }
01386 } else {
01387 rightx = p2->point.x();
01388 if (floor(p2p3ydist) != 0.0) {
01389 rightx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
01390 } else {
01391 rightx = qMax(p2->point.x(), p3->point.x());
01392 }
01393 }
01394 }
01395
01396
01397
01398 double rshort = 0, gshort = 0, bshort = 0;
01399 double rlong = 0, glong = 0, blong = 0;
01400 if (firstshorty) {
01401 if (floor(p1p2ydist) != 0.0) {
01402 rshort = p2->color.r * (p.y() - p1->point.y()) / p1p2ydist;
01403 gshort = p2->color.g * (p.y() - p1->point.y()) / p1p2ydist;
01404 bshort = p2->color.b * (p.y() - p1->point.y()) / p1p2ydist;
01405 rshort += p1->color.r * (p2->point.y() - p.y()) / p1p2ydist;
01406 gshort += p1->color.g * (p2->point.y() - p.y()) / p1p2ydist;
01407 bshort += p1->color.b * (p2->point.y() - p.y()) / p1p2ydist;
01408 } else {
01409 if (lefty) {
01410 if (p1->point.x() <= p2->point.x()) {
01411 rshort = p1->color.r;
01412 gshort = p1->color.g;
01413 bshort = p1->color.b;
01414 } else {
01415 rshort = p2->color.r;
01416 gshort = p2->color.g;
01417 bshort = p2->color.b;
01418 }
01419 } else {
01420 if (p1->point.x() > p2->point.x()) {
01421 rshort = p1->color.r;
01422 gshort = p1->color.g;
01423 bshort = p1->color.b;
01424 } else {
01425 rshort = p2->color.r;
01426 gshort = p2->color.g;
01427 bshort = p2->color.b;
01428 }
01429 }
01430 }
01431 } else {
01432 if (floor(p2p3ydist) != 0.0) {
01433 rshort = p3->color.r * (p.y() - p2->point.y()) / p2p3ydist;
01434 gshort = p3->color.g * (p.y() - p2->point.y()) / p2p3ydist;
01435 bshort = p3->color.b * (p.y() - p2->point.y()) / p2p3ydist;
01436 rshort += p2->color.r * (p3->point.y() - p.y()) / p2p3ydist;
01437 gshort += p2->color.g * (p3->point.y() - p.y()) / p2p3ydist;
01438 bshort += p2->color.b * (p3->point.y() - p.y()) / p2p3ydist;
01439 } else {
01440 if (lefty) {
01441 if (p2->point.x() <= p3->point.x()) {
01442 rshort = p2->color.r;
01443 gshort = p2->color.g;
01444 bshort = p2->color.b;
01445 } else {
01446 rshort = p3->color.r;
01447 gshort = p3->color.g;
01448 bshort = p3->color.b;
01449 }
01450 } else {
01451 if (p2->point.x() > p3->point.x()) {
01452 rshort = p2->color.r;
01453 gshort = p2->color.g;
01454 bshort = p2->color.b;
01455 } else {
01456 rshort = p3->color.r;
01457 gshort = p3->color.g;
01458 bshort = p3->color.b;
01459 }
01460 }
01461 }
01462 }
01463
01464
01465 rlong = p3->color.r * (p.y() - p1->point.y()) / p1p3ydist;
01466 glong = p3->color.g * (p.y() - p1->point.y()) / p1p3ydist;
01467 blong = p3->color.b * (p.y() - p1->point.y()) / p1p3ydist;
01468 rlong += p1->color.r * (p3->point.y() - p.y()) / p1p3ydist;
01469 glong += p1->color.g * (p3->point.y() - p.y()) / p1p3ydist;
01470 blong += p1->color.b * (p3->point.y() - p.y()) / p1p3ydist;
01471
01472
01473
01474
01475
01476 double rl, gl, bl, rr, gr, br;
01477 if (lefty) {
01478 rl = rshort; gl = gshort; bl = bshort;
01479 rr = rlong; gr = glong; br = blong;
01480 } else {
01481 rl = rlong; gl = glong; bl = blong;
01482 rr = rshort; gr = gshort; br = bshort;
01483 }
01484
01485
01486
01487
01488
01489 double xdist = rightx - leftx;
01490 double saxdist = p.x() - leftx;
01491 double saxdist2 = xdist - saxdist;
01492
01493
01494
01495 double r, g, b;
01496 if (xdist != 0.0) {
01497 r = (saxdist2 * rl / xdist) + (saxdist * rr / xdist);
01498 g = (saxdist2 * gl / xdist) + (saxdist * gr / xdist);
01499 b = (saxdist2 * bl / xdist) + (saxdist * br / xdist);
01500 } else {
01501
01502
01503
01504
01505
01506 r = (rl + rr) / 2;
01507 g = (gl + gr) / 2;
01508 b = (bl + br) / 2;
01509 }
01510
01511
01512
01513
01514 int ri = (int) floor(r);
01515 int gi = (int) floor(g);
01516 int bi = (int) floor(b);
01517 if (ri < 0) ri = 0;
01518 else if (ri > 255) ri = 255;
01519 if (gi < 0) gi = 0;
01520 else if (gi > 255) gi = 255;
01521 if (bi < 0) bi = 0;
01522 else if (bi > 255) bi = 255;
01523
01524
01525 return QColor(ri, gi, bi);
01526 }