[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project

src/klftools/qtcolortriangle.cpp

Go to the documentation of this file.
00001 /*
00002  * This file was very Slightly modified by Philippe Faist for KLatexFormula. (april 2009)
00003  */
00004 
00005 /****************************************************************************
00006 **
00007 ** This file is part of a Qt Solutions component.
00008 ** 
00009 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
00010 ** 
00011 ** Contact:  Qt Software Information (qt-info@nokia.com)
00012 ** 
00013 ** Commercial Usage  
00014 ** Licensees holding valid Qt Commercial licenses may use this file in
00015 ** accordance with the Qt Solutions Commercial License Agreement provided
00016 ** with the Software or, alternatively, in accordance with the terms
00017 ** contained in a written agreement between you and Nokia.
00018 ** 
00019 ** GNU Lesser General Public License Usage
00020 ** Alternatively, this file may be used under the terms of the GNU Lesser
00021 ** General Public License version 2.1 as published by the Free Software
00022 ** Foundation and appearing in the file LICENSE.LGPL included in the
00023 ** packaging of this file.  Please review the following information to
00024 ** ensure the GNU Lesser General Public License version 2.1 requirements
00025 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
00026 ** 
00027 ** In addition, as a special exception, Nokia gives you certain
00028 ** additional rights. These rights are described in the Nokia Qt LGPL
00029 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
00030 ** package.
00031 ** 
00032 ** GNU General Public License Usage 
00033 ** Alternatively, this file may be used under the terms of the GNU
00034 ** General Public License version 3.0 as published by the Free Software
00035 ** Foundation and appearing in the file LICENSE.GPL included in the
00036 ** packaging of this file.  Please review the following information to
00037 ** ensure the GNU General Public License version 3.0 requirements will be
00038 ** met: http://www.gnu.org/copyleft/gpl.html.
00039 ** 
00040 ** Please note Third Party Software included with Qt Solutions may impose
00041 ** additional restrictions and it is the user's responsibility to ensure
00042 ** that they have met the licensing requirements of the GPL, LGPL, or Qt
00043 ** Solutions Commercial license and the relevant license of the Third
00044 ** Party Software they are using.
00045 ** 
00046 ** If you are unsure which license is appropriate for your use, please
00047 ** contact the sales department at qt-sales@nokia.com.
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     Used to store color values in the range 0..255 as doubles.
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     Used to store pairs of DoubleColor and DoublePoint in one structure.
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     // Find the current position of the selector
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     // Find the inner radius of the hue donut.
00212     double innerRadius = outerRadius - outerRadius / 5;
00213 
00214     // Create an image of the same size as the contents rect.
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         // If selecting hue, find the new angles for the points a,b,c
00276         // of the triangle. The following update() will then redraw
00277         // the triangle.
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             // Ensure that hue does not change when selecting
00325             // saturation and value.
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     // Only respond to the left mouse button.
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     // As in mouseMoveEvent, either find the a,b,c angles or the
00358     // radian position of the selector, then order an update.
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     // Find the current position of the selector
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     // Blit the static generated background with the hue gradient onto
00544     // the double buffer.
00545     QImage buf = bg.copy();
00546 
00547     // Draw the trigon
00548     int h,s,v;
00549     curColor.getHsv(&h, &s, &v);
00550 
00551     // Find the color with only the hue, and max value and saturation
00552     QColor hueColor;
00553     hueColor.setHsv(curHue, 255, 255);
00554 
00555     // Draw the triangle
00556     drawTrigon(&buf, pa, pb, pc, hueColor);
00557 
00558     // Slow step: convert the image to a pixmap
00559     QPixmap pix = QPixmap::fromImage(buf);
00560     QPainter painter(&pix);
00561     painter.setRenderHint(QPainter::Antialiasing);
00562 
00563     // Draw an outline of the triangle
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     // Find a color for painting the selector based on the brightness
00583     // value of the color.
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     // Draw the selector ellipse.
00590     painter.drawEllipse(QRectF(selectorPos.x() - ellipseSize / 2.0,
00591                                selectorPos.y() - ellipseSize / 2.0,
00592                                ellipseSize + 0.5, ellipseSize + 0.5));
00593 
00594     // Blit
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     // Create three Vertex objects. A Vertex contains a double-point
00620     // coordinate and a color.
00621     // pa is the tip of the arrow
00622     // pb is the black corner
00623     // pc is the white corner
00624     Vertex aa(color, pa);
00625     Vertex bb(Qt::black, pb);
00626     Vertex cc(Qt::white, pc);
00627 
00628     // Sort. Make p1 above p2, which is above p3 (using y coordinate).
00629     // Bubble sorting is fastest here.
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     // All the three y deltas are >= 0
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     // The first x delta decides wether we have a lefty or a righty
00646     // trigon.
00647     bool lefty = p1p2xdist < 0;
00648 
00649     // Left and right colors and X values. The key in this map is the
00650     // y values. Our goal is to fill these structures with all the
00651     // information needed to do a single pass top-to-bottom,
00652     // left-to-right drawing of the trigon.
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      // Scan longy - find all left and right colors and X-values for
00664     // the tallest edge (p1-p3).
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     // Initialize with known values
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     // Find slopes (notice that if the y dists are 0, we don't care
00684     // about the slopes)
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     // Calculate gradients using linear approximation
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     // Scan top shorty - find all left and right colors and x-values
00708     // for the topmost of the two not-tallest short edges.
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     // Find slopes (notice that if the y dists are 0, we don't care
00719     // about the slopes)
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     // Calculate gradients using linear approximation
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     // Scan bottom shorty - find all left and right colors and
00742     // x-values for the bottommost of the two not-tallest short edges.
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     // Find slopes (notice that if the y dists are 0, we don't care
00753     // about the slopes)
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     // Calculate gradients using linear approximation
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     // Inner loop. For each y in the left map of x-values, draw one
00776     // line from left to right.
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         // if the xdist is 0, don't draw anything.
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             // Inner loop 2. Draws the line from left to right.
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     // Never use an invalid hue to display colors
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             // line is draw upright-to-downleft
00965             if (floor(x) < floor(px) || floor(y) < floor(py))
00966                 result = true;
00967         } else if (floor(ay) > floor(by)) {
00968             // line is draw downright-to-upleft
00969             if (floor(x) > floor(px) || floor(y) < floor(py))
00970                 result = true;
00971         } else {
00972             // line is flat horizontal
00973             if (y < ay) result = true;
00974         }
00975     } else if (floor(ax) < floor(bx)) {
00976         if (floor(ay) < floor(by)) {
00977             // line is draw upleft-to-downright
00978             if (floor(x) < floor(px) || floor(y) > floor(py))
00979                 result = true;
00980         } else if (floor(ay) > floor(by)) {
00981             // line is draw downleft-to-upright
00982             if (floor(x) > floor(px) || floor(y) > floor(py))
00983                 result = true;
00984         } else {
00985             // line is flat horizontal
00986             if (y > ay)
00987                 result = true;
00988         }
00989     } else {
00990         // line is vertical
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             // line is draw upright-to-downleft
01017 
01018             // if (x,y) is in on or above the upper right point,
01019             // return -1.
01020             if (y <= ay && x >= ax)
01021                 return -1;
01022 
01023             // if (x,y) is in on or below the lower left point,
01024             // return 1.
01025             if (y >= by && x <= bx)
01026                 return 1;
01027         } else {
01028             // line is draw downright-to-upleft
01029 
01030             // If the line is flat, only use the x coordinate.
01031             if (floor(ay) == floor(by)) {
01032                 // if (x is to the right of the rightmost point,
01033                 // return -1. otherwise if x is to the left of the
01034                 // leftmost point, return 1.
01035                 if (x >= ax)
01036                     return -1;
01037                 else if (x <= bx)
01038                     return 1;
01039             } else {
01040                 // if (x,y) is on or below the lower right point,
01041                 // return -1.
01042                 if (y >= ay && x >= ax)
01043                     return -1;
01044 
01045                 // if (x,y) is on or above the upper left point,
01046                 // return 1.
01047                 if (y <= by && x <= bx)
01048                     return 1;
01049             }
01050         }
01051     } else {
01052         if (ay < by) {
01053             // line is draw upleft-to-downright
01054 
01055             // If (x,y) is on or above the upper left point, return
01056             // -1.
01057             if (y <= ay && x <= ax)
01058                 return -1;
01059 
01060             // If (x,y) is on or below the lower right point, return
01061             // 1.
01062             if (y >= by && x >= bx)
01063                 return 1;
01064         } else {
01065             // line is draw downleft-to-upright
01066 
01067             // If the line is flat, only use the x coordinate.
01068             if (floor(ay) == floor(by)) {
01069                 if (x <= ax)
01070                     return -1;
01071                 else if (x >= bx)
01072                     return 1;
01073             } else {
01074                 // If (x,y) is on or below the lower left point, return
01075                 // -1.
01076                 if (y >= ay && x <= ax)
01077                     return -1;
01078 
01079                 // If (x,y) is on or above the upper right point, return
01080                 // 1.
01081                 if (y <= by && x >= bx)
01082                     return 1;
01083             }
01084         }
01085     }
01086 
01087     // No tests proved that (x,y) was outside [(ax,ay),(bx,by)], so we
01088     // assume it's inside the line's bounds.
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     // Let v1A be the vector from (x,y) to a.
01111     // Let v2A be the vector from a to b.
01112     // Find the angle alphaA between v1A and v2A.
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     // Let v1B be the vector from x to b.
01122     // Let v2B be the vector from b to c.
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     // Let v1C be the vector from x to c.
01132     // Let v2C be the vector from c back to a.
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     // Find the radian angles between the (1,0) vector and the points
01142     // A, B, C and (x,y). Use this information to determine which of
01143     // the edges we should project (x,y) onto.
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     // If (x,y) is in the a-b area, project onto the a-b vector.
01150     if (angleBetweenAngles(angleP, angleA, angleB)) {
01151         // Find the distance from (x,y) to a. Then use the slope of
01152         // the a-b vector with this distance and the angle between a-b
01153         // and a-(x,y) to determine the point of intersection of the
01154         // perpendicular projection from (x,y) onto a-b.
01155         double pdist = sqrt(qsqr(x - a.point.x()) + qsqr(y - a.point.y()));
01156 
01157         // the length of all edges is always > 0
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         // If (x,y) is above the a-b line, which basically means it's
01162         // outside the triangle, then return its projection onto a-b.
01163         if (pointAbovePoint(x, y, p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y())) {
01164             // If the projection is "outside" a, return a. If it is
01165             // outside b, return b. Otherwise return the projection.
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         // If (x,y) is in the b-c area, project onto the b-c vector.
01176         double pdist = sqrt(qsqr(x - b.point.x()) + qsqr(y - b.point.y()));
01177 
01178         // the length of all edges is always > 0
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         // If (x,y) is in the c-a area, project onto the c-a vector.
01192         double pdist = sqrt(qsqr(x - c.point.x()) + qsqr(y - c.point.y()));
01193 
01194         // the length of all edges is always > 0
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     // (x,y) is inside the triangle (inside a-b, b-c and a-c).
01209     return QPointF(x, y);
01210 }
01211 
01226 QPointF QtColorTriangle::pointFromColor(const QColor &col) const
01227 {
01228     // Simplifications for the corner cases.
01229     if (col == Qt::black)
01230         return pb;
01231     else if (col == Qt::white)
01232         return pc;
01233 
01234     // Find the x and y slopes
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     // Extract the h,s,v values of col.
01243     int hue,sat,val;
01244     col.getHsv(&hue, &sat, &val);
01245 
01246     // Find the line that passes through the triangle where the value
01247     // is equal to our color's value.
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     // Find the line that passes through the triangle where the
01254     // saturation is equal to our color's value.
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     // Find the intersection between these lines.
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     // Find the outer radius of the hue gradient.
01290     int outerRadius = (contentsRect().width() - 1) / 2;
01291     if ((contentsRect().height() - 1) / 2 < outerRadius)
01292         outerRadius = (contentsRect().height() - 1) / 2;
01293 
01294     // Find the center coordinates
01295     double cx = (double) contentsRect().center().x();
01296     double cy = (double) contentsRect().center().y();
01297 
01298     // Find the a, b and c from their angles, the center of the rect
01299     // and the radius of the hue gradient donut.
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     // Find the hue value from the angle of the 'a' point.
01308     double angle = a - PI/2.0;
01309     if (angle < 0) angle += TWOPI;
01310     double hue = (360.0 * angle) / TWOPI;
01311 
01312     // Create the color of the 'a' corner point. We know that b is
01313     // black and c is white.
01314     QColor color;
01315     color.setHsv(360 - (int) floor(hue), 255, 255);
01316 
01317     // See also drawTrigon(), which basically does exactly the same to
01318     // determine all colors in the trigon.
01319     Vertex aa(color, pa);
01320     Vertex bb(Qt::black, pb);
01321     Vertex cc(Qt::white, pc);
01322 
01323     // Make sure p1 is above p2, which is above p3.
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     // Find the slopes of all edges in the trigon. All the three y
01332     // deltas here are positive because of the above sorting.
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     // The first x delta decides wether we have a lefty or a righty
01341     // trigon. A lefty trigon has its tallest edge on the right hand
01342     // side of the trigon. The righty trigon has it on its left side.
01343     // This property determines wether the left or the right set of x
01344     // coordinates will be continuous.
01345     bool lefty = p1p2xdist < 0;
01346 
01347     // Find whether the selector's y is in the first or second shorty,
01348     // counting from the top and downwards. This is used to find the
01349     // color at the selector point.
01350     bool firstshorty = (p.y() >= p1->point.y() && p.y() < p2->point.y());
01351 
01352     // From the y value of the selector's position, find the left and
01353     // right x values.
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     // Find the r,g,b values of the points on the trigon's edges that
01397     // are to the left and right of the selector.
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     // p1p3ydist is never 0
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     // rshort,gshort,bshort is the color on one of the shortys.
01473     // rlong,glong,blong is the color on the longy. So depending on
01474     // wether we have a lefty trigon or not, we can determine which
01475     // colors are on the left and right edge.
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     // Find the distance from the left x to the right x (xdist). Then
01486     // find the distances from the selector to each of these (saxdist
01487     // and saxdist2). These distances are used to find the color at
01488     // the selector.
01489     double xdist = rightx - leftx;
01490     double saxdist = p.x() - leftx;
01491     double saxdist2 = xdist - saxdist;
01492 
01493     // Now determine the r,g,b values of the selector using a linear
01494     // approximation.
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         // In theory, the left and right color will be equal here. But
01502         // because of the loss of precision, we get an error on both
01503         // colors. The best approximation we can get is from adding
01504         // the two errors, which in theory will eliminate the error
01505         // but in practise will only minimize it.
01506         r = (rl + rr) / 2;
01507         g = (gl + gr) / 2;
01508         b = (bl + br) / 2;
01509     }
01510 
01511     // Now floor the color components and fit them into proper
01512     // boundaries. This again is to compensate for the error caused by
01513     // loss of precision.
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     // Voila, we have the color at the point of the selector.
01525     return QColor(ri, gi, bi);
01526 }

Generated by doxygen 1.7.3