[KLF Backend][KLF Tools][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  * In order for integration into KLatexFormula, this code is relicensed
00004  * to **GPL Version 2.1 or higher** as described in the footnote to the GPL
00005  * compatibility table found at
00006  *   http://www.gnu.org/licenses/gpl-faq.html#compat-matrix-footnote-7
00007  *
00008  */
00009 
00010 /****************************************************************************
00011 **
00012 ** This file is part of a Qt Solutions component.
00013 ** 
00014 ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
00015 ** 
00016 ** Contact:  Qt Software Information (qt-info@nokia.com)
00017 ** 
00018 ** Commercial Usage  
00019 ** Licensees holding valid Qt Commercial licenses may use this file in
00020 ** accordance with the Qt Solutions Commercial License Agreement provided
00021 ** with the Software or, alternatively, in accordance with the terms
00022 ** contained in a written agreement between you and Nokia.
00023 ** 
00024 ** GNU Lesser General Public License Usage
00025 ** Alternatively, this file may be used under the terms of the GNU Lesser
00026 ** General Public License version 2.1 as published by the Free Software
00027 ** Foundation and appearing in the file LICENSE.LGPL included in the
00028 ** packaging of this file.  Please review the following information to
00029 ** ensure the GNU Lesser General Public License version 2.1 requirements
00030 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
00031 ** 
00032 ** In addition, as a special exception, Nokia gives you certain
00033 ** additional rights. These rights are described in the Nokia Qt LGPL
00034 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
00035 ** package.
00036 ** 
00037 ** GNU General Public License Usage 
00038 ** Alternatively, this file may be used under the terms of the GNU
00039 ** General Public License version 3.0 as published by the Free Software
00040 ** Foundation and appearing in the file LICENSE.GPL included in the
00041 ** packaging of this file.  Please review the following information to
00042 ** ensure the GNU General Public License version 3.0 requirements will be
00043 ** met: http://www.gnu.org/copyleft/gpl.html.
00044 ** 
00045 ** Please note Third Party Software included with Qt Solutions may impose
00046 ** additional restrictions and it is the user's responsibility to ensure
00047 ** that they have met the licensing requirements of the GPL, LGPL, or Qt
00048 ** Solutions Commercial license and the relevant license of the Third
00049 ** Party Software they are using.
00050 ** 
00051 ** If you are unsure which license is appropriate for your use, please
00052 ** contact the sales department at qt-sales@nokia.com.
00053 ** 
00054 ****************************************************************************/
00055 
00056 #include "qtcolortriangle.h"
00057 
00058 #include <QEvent>
00059 #include <QMap>
00060 #include <QVarLengthArray>
00061 #include <QConicalGradient>
00062 #include <QFrame>
00063 #include <QImage>
00064 #include <QKeyEvent>
00065 #include <QLayout>
00066 #include <QMouseEvent>
00067 #include <QPainter>
00068 #include <QPainterPath>
00069 #include <QPixmap>
00070 #include <QResizeEvent>
00071 #include <QToolTip>
00072 #include <QVBoxLayout>
00073 
00074 #include <math.h>
00075 
00098 const double PI = 3.14159265358979323846264338327950288419717;
00099 const double TWOPI = 2.0*PI;
00100 
00101 /*
00102     Used to store color values in the range 0..255 as doubles.
00103 */
00104 struct DoubleColor
00105 {
00106     double r, g, b;
00107 
00108     DoubleColor() : r(0.0), g(0.0), b(0.0) {}
00109     DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
00110     DoubleColor(const DoubleColor &c) : r(c.r), g(c.g), b(c.b) {}
00111 };
00112 
00113 /*
00114     Used to store pairs of DoubleColor and DoublePoint in one structure.
00115 */
00116 struct Vertex {
00117     DoubleColor color;
00118     QPointF point;
00119 
00120     Vertex(const DoubleColor &c, const QPointF &p) : color(c), point(p) {}
00121     Vertex(const QColor &c, const QPointF &p)
00122         : color(DoubleColor((double) c.red(), (double) c.green(),
00123                             (double) c.blue())), point(p) {}
00124 };
00125 
00130 static void swap(Vertex **a, Vertex **b)
00131 {
00132     Vertex *tmp = *a;
00133     *a = *b;
00134     *b = tmp;
00135 }
00136 
00140 QtColorTriangle::QtColorTriangle(QWidget *parent)
00141     : QWidget(parent), bg(sizeHint(), QImage::Format_RGB32), selMode(Idle)
00142 {
00143     setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
00144     setFocusPolicy(Qt::StrongFocus);
00145 
00146     mustGenerateBackground = true;
00147 
00148     QColor tmp;
00149     tmp.setHsv(76, 184, 206);
00150     setColor(tmp);
00151 }
00152 
00156 QtColorTriangle::~QtColorTriangle()
00157 {
00158 }
00159 
00165 void QtColorTriangle::polish()
00166 {
00167     outerRadius = (contentsRect().width() - 1) / 2;
00168     if ((contentsRect().height() - 1) / 2 < outerRadius)
00169         outerRadius = (contentsRect().height() - 1) / 2;
00170 
00171     penWidth = (int) floor(outerRadius / 50.0);
00172     ellipseSize = (int) floor(outerRadius / 12.5);
00173 
00174     double cx = (double) contentsRect().center().x();
00175     double cy = (double) contentsRect().center().y();
00176 
00177     pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00178                      cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00179     pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00180                      cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00181     pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00182                      cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00183     pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00184                      cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00185 
00186     // Find the current position of the selector
00187     selectorPos = pointFromColor(curColor);
00188 
00189     update();
00190 }
00191 
00194 QSize QtColorTriangle::sizeHint() const
00195 {
00196     return QSize(100, 100);
00197 }
00198 
00203 int QtColorTriangle::heightForWidth(int w) const
00204 {
00205     return w;
00206 }
00207 
00214 void QtColorTriangle::genBackground()
00215 {
00216     // Find the inner radius of the hue donut.
00217     double innerRadius = outerRadius - outerRadius / 5;
00218 
00219     // Create an image of the same size as the contents rect.
00220     bg = QImage(contentsRect().size(), QImage::Format_ARGB32_Premultiplied);
00221     bg.fill(qRgba(0,0,0,0));
00222     QPainter p(&bg);
00223     p.setRenderHint(QPainter::Antialiasing);
00224 
00225     QConicalGradient gradient(bg.rect().center(), 90);
00226     QColor color;
00227     for (double i = 0; i <= 1.0; i += 0.1) {
00228 #if QT_VERSION < 0x040100
00229         color.setHsv(int(i * 360.0), 255, 255);
00230 #else
00231         color.setHsv(int(360.0 - (i * 360.0)), 255, 255);
00232 #endif
00233         gradient.setColorAt(i, color);
00234     }
00235 
00236     QRectF innerRadiusRect(bg.rect().center().x() - innerRadius, bg.rect().center().y() - innerRadius,
00237                            innerRadius * 2 + 1, innerRadius * 2 + 1);
00238     QRectF outerRadiusRect(bg.rect().center().x() - outerRadius, bg.rect().center().y() - outerRadius,
00239                            outerRadius * 2 + 1, outerRadius * 2 + 1);
00240     QPainterPath path;
00241     path.addEllipse(innerRadiusRect);
00242     path.addEllipse(outerRadiusRect);
00243 
00244     p.save();
00245     p.setClipPath(path);
00246     p.fillRect(bg.rect(), gradient);
00247     p.restore();
00248 
00249     double penThickness = bg.width() / 400.0;
00250     for (int f = 0; f <= 5760; f += 20) {
00251         int value = int((0.5 + cos(((f - 1800) / 5760.0) * TWOPI) / 2) * 255.0);
00252 
00253         color.setHsv(int((f / 5760.0) * 360.0), 128 + (255 - value)/2, 255 - (255 - value)/4);
00254         p.setPen(QPen(color, penThickness));
00255         p.drawArc(innerRadiusRect, 1440 - f, 20);
00256         
00257         color.setHsv(int((f / 5760.0) * 360.0), 128 + value/2, 255 - value/4);
00258         p.setPen(QPen(color, penThickness));
00259         p.drawArc(outerRadiusRect, 2880 - 1440 - f, 20);
00260     }
00261 
00262     return;
00263 }
00264 
00271 void QtColorTriangle::mouseMoveEvent(QMouseEvent *e)
00272 {
00273     if ((e->buttons() & Qt::LeftButton) == 0)
00274         return;
00275 
00276     QPointF depos((double) e->pos().x(), (double) e->pos().y());
00277     bool newColor = false;
00278 
00279     if (selMode == SelectingHue) {
00280         // If selecting hue, find the new angles for the points a,b,c
00281         // of the triangle. The following update() will then redraw
00282         // the triangle.
00283         a = angleAt(depos, contentsRect());
00284         b = a + TWOPI / 3.0;
00285         c = b + TWOPI / 3.0;
00286         if (b > TWOPI) b -= TWOPI;
00287         if (c > TWOPI) c -= TWOPI;
00288 
00289         double am = a - PI/2;
00290         if (am < 0) am += TWOPI;
00291 
00292         curHue = 360 - (int) (((am) * 360.0) / TWOPI);
00293         int h,s,v;
00294         curColor.getHsv(&h, &s, &v);
00295 
00296         if (curHue != h) {
00297             newColor = true;
00298             curColor.setHsv(curHue, s, v, curColor.alpha());
00299         }
00300 
00301         double cx = (double) contentsRect().center().x();
00302         double cy = (double) contentsRect().center().y();
00303 
00304         pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00305                      cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00306         pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00307                      cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00308         pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00309                      cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00310         pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00311                      cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00312 
00313         selectorPos = pointFromColor(curColor);
00314     } else {
00315         Vertex aa(Qt::black, pa);
00316         Vertex bb(Qt::black, pb);
00317         Vertex cc(Qt::black, pc);
00318 
00319         Vertex *p1 = &aa;
00320         Vertex *p2 = &bb;
00321         Vertex *p3 = &cc;
00322         if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00323         if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00324         if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00325 
00326         selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
00327         QColor col = colorFromPoint(selectorPos);
00328         if (col != curColor) {
00329             // Ensure that hue does not change when selecting
00330             // saturation and value.
00331             int h,s,v;
00332             col.getHsv(&h, &s, &v);
00333             curColor.setHsv(curHue, s, v, curColor.alpha());
00334             newColor = true;
00335         }
00336     }
00337 
00338     if (newColor)
00339         internalSetNewColor(curColor);
00340 
00341     update();
00342 }
00343 
00352 void QtColorTriangle::mousePressEvent(QMouseEvent *e)
00353 {
00354     // Only respond to the left mouse button.
00355     if (e->button() != Qt::LeftButton)
00356         return;
00357 
00358     QPointF depos((double) e->pos().x(), (double) e->pos().y());
00359     double rad = radiusAt(depos, contentsRect());
00360     bool newColor = false;
00361 
00362     // As in mouseMoveEvent, either find the a,b,c angles or the
00363     // radian position of the selector, then order an update.
00364     if (rad > (outerRadius - (outerRadius / 5))) {
00365         selMode = SelectingHue;
00366 
00367         a = angleAt(depos, contentsRect());
00368         b = a + TWOPI / 3.0;
00369         c = b + TWOPI / 3.0;
00370         if (b > TWOPI) b -= TWOPI;
00371         if (c > TWOPI) c -= TWOPI;
00372 
00373         double am = a - PI/2;
00374         if (am < 0) am += TWOPI;
00375 
00376         curHue = 360 - (int) ((am * 360.0) / TWOPI);
00377         int h,s,v;
00378         curColor.getHsv(&h, &s, &v);
00379 
00380         if (h != curHue) {
00381             newColor = true;
00382             curColor.setHsv(curHue, s, v, curColor.alpha());
00383         }
00384 
00385         double cx = (double) contentsRect().center().x();
00386         double cy = (double) contentsRect().center().y();
00387 
00388         pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00389                          cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00390         pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00391                          cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00392         pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00393                          cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00394         pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00395                          cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00396 
00397         selectorPos = pointFromColor(curColor);
00398         internalSetNewColor(curColor);
00399     } else {
00400         selMode = SelectingSatValue;
00401 
00402         Vertex aa(Qt::black, pa);
00403         Vertex bb(Qt::black, pb);
00404         Vertex cc(Qt::black, pc);
00405 
00406         Vertex *p1 = &aa;
00407         Vertex *p2 = &bb;
00408         Vertex *p3 = &cc;
00409         if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00410         if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00411         if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00412 
00413         selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
00414         QColor col = colorFromPoint(selectorPos);
00415         if (col != curColor) {
00416             int tempalpha = curColor.alpha();
00417             curColor = col;
00418             curColor.setAlpha(tempalpha);
00419             newColor = true;
00420         }
00421     }
00422 
00423     if (newColor)
00424         internalSetNewColor(curColor);
00425 
00426     update();
00427 }
00428 
00434 void QtColorTriangle::mouseReleaseEvent(QMouseEvent *e)
00435 {
00436     if (e->button() == Qt::LeftButton)
00437         selMode = Idle;
00438 }
00439 
00443 void QtColorTriangle::keyPressEvent(QKeyEvent *e)
00444 {
00445     switch (e->key()) {
00446         case Qt::Key_Left: {
00447             --curHue;
00448             if (curHue < 0) curHue += 360;
00449             int h,s,v;
00450             curColor.getHsv(&h, &s, &v);
00451             QColor tmp;
00452             tmp.setHsv(curHue, s, v);
00453             setColor(tmp);
00454         }
00455             break;
00456         case Qt::Key_Right: {
00457             ++curHue;
00458             if (curHue > 359) curHue -= 360;
00459             int h,s,v;
00460             curColor.getHsv(&h, &s, &v);
00461             QColor tmp;
00462             tmp.setHsv(curHue, s, v);
00463             setColor(tmp);
00464         }
00465             break;
00466         case Qt::Key_Up: {
00467             int h,s,v;
00468             curColor.getHsv(&h, &s, &v);
00469             QColor tmp;
00470             if (e->modifiers() & Qt::ShiftModifier) {
00471                 if (s > 5) s -= 5;
00472                 else s = 0;
00473             } else {
00474                 if (v > 5) v -= 5;
00475                 else v = 0;
00476             }
00477             tmp.setHsv(curHue, s, v);
00478             setColor(tmp);
00479         }
00480             break;
00481         case Qt::Key_Down: {
00482             int h,s,v;
00483             curColor.getHsv(&h, &s, &v);
00484             QColor tmp;
00485             if (e->modifiers() & Qt::ShiftModifier) {
00486                 if (s < 250) s += 5;
00487                 else s = 255;
00488             } else {
00489                 if (v < 250) v += 5;
00490                 else v = 255;
00491             }
00492             tmp.setHsv(curHue, s, v);
00493             setColor(tmp);
00494         }
00495             break;
00496     };
00497 }
00498 
00504 void QtColorTriangle::resizeEvent(QResizeEvent *)
00505 {
00506     outerRadius = (contentsRect().width() - 1) / 2;
00507     if ((contentsRect().height() - 1) / 2 < outerRadius)
00508         outerRadius = (contentsRect().height() - 1) / 2;
00509 
00510     penWidth = (int) floor(outerRadius / 50.0);
00511     ellipseSize = (int) floor(outerRadius / 12.5);
00512 
00513     double cx = (double) contentsRect().center().x();
00514     double cy = (double) contentsRect().center().y();
00515 
00516     pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
00517                  cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
00518     pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
00519                  cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
00520     pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
00521                  cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
00522     pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
00523                  cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
00524 
00525     // Find the current position of the selector
00526     selectorPos = pointFromColor(curColor);
00527 
00528     mustGenerateBackground = true;
00529     update();
00530 }
00531 
00538 void QtColorTriangle::paintEvent(QPaintEvent *e)
00539 {
00540     QPainter p(this);
00541     if (e->rect().intersects(contentsRect()))
00542         p.setClipRegion(e->region().intersected(contentsRect()));
00543     if (mustGenerateBackground) {
00544         genBackground();
00545         mustGenerateBackground = false;
00546     }
00547 
00548     // Blit the static generated background with the hue gradient onto
00549     // the double buffer.
00550     QImage buf = bg.copy();
00551 
00552     // Draw the trigon
00553     int h,s,v;
00554     curColor.getHsv(&h, &s, &v);
00555 
00556     // Find the color with only the hue, and max value and saturation
00557     QColor hueColor;
00558     hueColor.setHsv(curHue, 255, 255);
00559 
00560     // Draw the triangle
00561     drawTrigon(&buf, pa, pb, pc, hueColor);
00562 
00563     // Slow step: convert the image to a pixmap
00564     QPixmap pix = QPixmap::fromImage(buf);
00565     QPainter painter(&pix);
00566     painter.setRenderHint(QPainter::Antialiasing);
00567 
00568     // Draw an outline of the triangle
00569     QColor halfAlpha(0, 0, 0, 128);
00570     painter.setPen(QPen(halfAlpha, 0));
00571     painter.drawLine(pa, pb);
00572     painter.drawLine(pb, pc);
00573     painter.drawLine(pc, pa);
00574     
00575     int ri, gi, bi;
00576     hueColor.getRgb(&ri, &gi, &bi);
00577     if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
00578         painter.setPen(QPen(Qt::black, penWidth));
00579     else
00580         painter.setPen(QPen(Qt::white, penWidth));
00581     painter.drawEllipse((int) (pd.x() - ellipseSize / 2.0),
00582                         (int) (pd.y() - ellipseSize / 2.0),
00583                         ellipseSize, ellipseSize);
00584 
00585     curColor.getRgb(&ri, &gi, &bi);
00586 
00587     // Find a color for painting the selector based on the brightness
00588     // value of the color.
00589     if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
00590         painter.setPen(QPen(Qt::black, penWidth));
00591     else
00592         painter.setPen(QPen(Qt::white, penWidth));
00593 
00594     // Draw the selector ellipse.
00595     painter.drawEllipse(QRectF(selectorPos.x() - ellipseSize / 2.0,
00596                                selectorPos.y() - ellipseSize / 2.0,
00597                                ellipseSize + 0.5, ellipseSize + 0.5));
00598 
00599     // Blit
00600     p.drawPixmap(contentsRect().topLeft(), pix);
00601 }
00602 
00603 
00604 void QtColorTriangle::internalSetNewColor(const QColor& color)
00605 {
00606   emit colorChanged(color);
00607 }
00608 
00609 
00610 
00620 void QtColorTriangle::drawTrigon(QImage *buf, const QPointF &pa,
00621                                const QPointF &pb, const QPointF &pc,
00622                                const QColor &color)
00623 {
00624     // Create three Vertex objects. A Vertex contains a double-point
00625     // coordinate and a color.
00626     // pa is the tip of the arrow
00627     // pb is the black corner
00628     // pc is the white corner
00629     Vertex aa(color, pa);
00630     Vertex bb(Qt::black, pb);
00631     Vertex cc(Qt::white, pc);
00632 
00633     // Sort. Make p1 above p2, which is above p3 (using y coordinate).
00634     // Bubble sorting is fastest here.
00635     Vertex *p1 = &aa;
00636     Vertex *p2 = &bb;
00637     Vertex *p3 = &cc;
00638     if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
00639     if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
00640     if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
00641 
00642     // All the three y deltas are >= 0
00643     double p1p2ydist = p2->point.y() - p1->point.y();
00644     double p1p3ydist = p3->point.y() - p1->point.y();
00645     double p2p3ydist = p3->point.y() - p2->point.y();
00646     double p1p2xdist = p2->point.x() - p1->point.x();
00647     double p1p3xdist = p3->point.x() - p1->point.x();
00648     double p2p3xdist = p3->point.x() - p2->point.x();
00649 
00650     // The first x delta decides wether we have a lefty or a righty
00651     // trigon.
00652     bool lefty = p1p2xdist < 0;
00653 
00654     // Left and right colors and X values. The key in this map is the
00655     // y values. Our goal is to fill these structures with all the
00656     // information needed to do a single pass top-to-bottom,
00657     // left-to-right drawing of the trigon.
00658     QVarLengthArray<DoubleColor, 2000> leftColors;
00659     QVarLengthArray<DoubleColor, 2000> rightColors;
00660     QVarLengthArray<double, 2000> leftX; 
00661     QVarLengthArray<double, 2000> rightX; 
00662 
00663     leftColors.resize(int(floor(p3->point.y() + 1)));
00664     rightColors.resize(int(floor(p3->point.y() + 1)));
00665     leftX.resize(int(floor(p3->point.y() + 1)));
00666     rightX.resize(int(floor(p3->point.y() + 1)));
00667 
00668      // Scan longy - find all left and right colors and X-values for
00669     // the tallest edge (p1-p3).
00670     DoubleColor source;
00671     DoubleColor dest;
00672     double r, g, b;
00673     double rdelta, gdelta, bdelta;
00674     double x;
00675     double xdelta;
00676     int y1, y2;
00677 
00678     // Initialize with known values
00679     x = p1->point.x();
00680     source = p1->color;
00681     dest = p3->color;
00682     r = source.r;
00683     g = source.g;
00684     b = source.b;
00685     y1 = (int) floor(p1->point.y());
00686     y2 = (int) floor(p3->point.y());
00687 
00688     // Find slopes (notice that if the y dists are 0, we don't care
00689     // about the slopes)
00690     xdelta = p1p3ydist == 0.0 ? 0.0 : p1p3xdist / p1p3ydist;
00691     rdelta = p1p3ydist == 0.0 ? 0.0 : (dest.r - r) / p1p3ydist;
00692     gdelta = p1p3ydist == 0.0 ? 0.0 : (dest.g - g) / p1p3ydist;
00693     bdelta = p1p3ydist == 0.0 ? 0.0 : (dest.b - b) / p1p3ydist;
00694 
00695     // Calculate gradients using linear approximation
00696     int y;
00697     for (y = y1; y < y2; ++y) {
00698         if (lefty) {
00699             rightColors[y] = DoubleColor(r, g, b);
00700             rightX[y] = x;
00701         } else {
00702             leftColors[y] = DoubleColor(r, g, b);
00703             leftX[y] = x;
00704         }
00705 
00706         r += rdelta;
00707         g += gdelta;
00708         b += bdelta;
00709         x += xdelta;
00710     }
00711 
00712     // Scan top shorty - find all left and right colors and x-values
00713     // for the topmost of the two not-tallest short edges.
00714     x = p1->point.x();
00715     source = p1->color;
00716     dest = p2->color;
00717     r = source.r;
00718     g = source.g;
00719     b = source.b;
00720     y1 = (int) floor(p1->point.y());
00721     y2 = (int) floor(p2->point.y());
00722 
00723     // Find slopes (notice that if the y dists are 0, we don't care
00724     // about the slopes)
00725     xdelta = p1p2ydist == 0.0 ? 0.0 : p1p2xdist / p1p2ydist;
00726     rdelta = p1p2ydist == 0.0 ? 0.0 : (dest.r - r) / p1p2ydist;
00727     gdelta = p1p2ydist == 0.0 ? 0.0 : (dest.g - g) / p1p2ydist;
00728     bdelta = p1p2ydist == 0.0 ? 0.0 : (dest.b - b) / p1p2ydist;
00729 
00730     // Calculate gradients using linear approximation
00731     for (y = y1; y < y2; ++y) {
00732         if (lefty) {
00733             leftColors[y] = DoubleColor(r, g, b);
00734             leftX[y] = x;
00735         } else {
00736             rightColors[y] = DoubleColor(r, g, b);
00737             rightX[y] = x;
00738         }
00739 
00740         r += rdelta;
00741         g += gdelta;
00742         b += bdelta;
00743         x += xdelta;
00744     }
00745 
00746     // Scan bottom shorty - find all left and right colors and
00747     // x-values for the bottommost of the two not-tallest short edges.
00748     x = p2->point.x();
00749     source = p2->color;
00750     dest = p3->color;
00751     r = source.r;
00752     g = source.g;
00753     b = source.b;
00754     y1 = (int) floor(p2->point.y());
00755     y2 = (int) floor(p3->point.y());
00756 
00757     // Find slopes (notice that if the y dists are 0, we don't care
00758     // about the slopes)
00759     xdelta = p2p3ydist == 0.0 ? 0.0 : p2p3xdist / p2p3ydist;
00760     rdelta = p2p3ydist == 0.0 ? 0.0 : (dest.r - r) / p2p3ydist;
00761     gdelta = p2p3ydist == 0.0 ? 0.0 : (dest.g - g) / p2p3ydist;
00762     bdelta = p2p3ydist == 0.0 ? 0.0 : (dest.b - b) / p2p3ydist;
00763 
00764     // Calculate gradients using linear approximation
00765     for (y = y1; y < y2; ++y) {
00766         if (lefty) {
00767             leftColors[y] = DoubleColor(r, g, b);
00768             leftX[y] = x;
00769         } else {
00770             rightColors[y] = DoubleColor(r, g, b);
00771             rightX[y] = x;
00772         }
00773 
00774         r += rdelta;
00775         g += gdelta;
00776         b += bdelta;
00777         x += xdelta;
00778     }
00779 
00780     // Inner loop. For each y in the left map of x-values, draw one
00781     // line from left to right.
00782     const int p3yfloor = int(floor(p3->point.y()));
00783     for (int y = int(floor(p1->point.y())); y < p3yfloor; ++y) {
00784         double lx = leftX[y];
00785         double rx = rightX[y];
00786 
00787         int lxi = (int) floor(lx);
00788         int rxi = (int) floor(rx);
00789         DoubleColor rc = rightColors[y];
00790         DoubleColor lc = leftColors[y];
00791 
00792         // if the xdist is 0, don't draw anything.
00793         double xdist = rx - lx;
00794         if (xdist != 0.0) {
00795             double r = lc.r;
00796             double g = lc.g;
00797             double b = lc.b;
00798             double rdelta = (rc.r - r) / xdist;
00799             double gdelta = (rc.g - g) / xdist;
00800             double bdelta = (rc.b - b) / xdist;
00801 
00802             QRgb *scanline = reinterpret_cast<QRgb *>(buf->scanLine(y));
00803             scanline += lxi;
00804 
00805             // Inner loop 2. Draws the line from left to right.
00806             for (int i = lxi; i < rxi; ++i) {
00807                 *scanline++ = qRgb((int) r, (int) g, (int) b);
00808                 r += rdelta;
00809                 g += gdelta;
00810                 b += bdelta;
00811             }
00812         }
00813     }
00814 }
00815 
00820 void QtColorTriangle::setColor(const QColor &col)
00821 {
00822     if (col == curColor)
00823         return;
00824 
00825     curColor = col;
00826 
00827     int h, s, v;
00828     curColor.getHsv(&h, &s, &v);
00829 
00830     // Never use an invalid hue to display colors
00831     if (h != -1)
00832         curHue = h;
00833 
00834     a = (((360 - curHue) * TWOPI) / 360.0);
00835     a += PI / 2.0;
00836     if (a > TWOPI) a -= TWOPI;
00837 
00838     b = a + TWOPI/3;
00839     c = b + TWOPI/3;
00840 
00841     if (b > TWOPI) b -= TWOPI;
00842     if (c > TWOPI) c -= TWOPI;
00843 
00844     double cx = (double) contentsRect().center().x();
00845     double cy = (double) contentsRect().center().y();
00846     double innerRadius = outerRadius - (outerRadius / 5.0);
00847     double pointerRadius = outerRadius - (outerRadius / 10.0);
00848 
00849     pa = QPointF(cx + (cos(a) * innerRadius), cy - (sin(a) * innerRadius));
00850     pb = QPointF(cx + (cos(b) * innerRadius), cy - (sin(b) * innerRadius));
00851     pc = QPointF(cx + (cos(c) * innerRadius), cy - (sin(c) * innerRadius));
00852     pd = QPointF(cx + (cos(a) * pointerRadius), cy - (sin(a) * pointerRadius));
00853 
00854     selectorPos = pointFromColor(curColor);
00855     update();
00856 
00857     internalSetNewColor(curColor);
00858 }
00859 
00864 QColor QtColorTriangle::color() const
00865 {
00866     return curColor;
00867 }
00868 
00874 double QtColorTriangle::radiusAt(const QPointF &pos, const QRect &rect) const
00875 {
00876     double mousexdist = pos.x() - (double) rect.center().x();
00877     double mouseydist = pos.y() - (double) rect.center().y();
00878     return sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
00879 }
00880 
00888 double QtColorTriangle::angleAt(const QPointF &pos, const QRect &rect) const
00889 {
00890     double mousexdist = pos.x() - (double) rect.center().x();
00891     double mouseydist = pos.y() - (double) rect.center().y();
00892     double mouserad = sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
00893     if (mouserad == 0.0)
00894         return 0.0;
00895 
00896     double angle = acos(mousexdist / mouserad);
00897     if (mouseydist >= 0)
00898         angle = TWOPI - angle;
00899 
00900     return angle;
00901 }
00902 
00907 inline double qsqr(double a)
00908 {
00909     return a * a;
00910 }
00911 
00916 inline double vlen(double x, double y)
00917 {
00918     return sqrt(qsqr(x) + qsqr(y));
00919 }
00920 
00925 inline double vprod(double x1, double y1, double x2, double y2)
00926 {
00927     return x1 * x2 + y1 * y2;
00928 }
00929 
00935 bool angleBetweenAngles(double p, double a1, double a2)
00936 {
00937     if (a1 > a2) {
00938         a2 += TWOPI;
00939         if (p < PI) p += TWOPI;
00940     }
00941 
00942     return p >= a1 && p < a2;
00943 }
00944 
00962 static bool pointAbovePoint(double x, double y, double px, double py,
00963                             double ax, double ay, double bx, double by)
00964 {
00965     bool result = false;
00966 
00967     if (floor(ax) > floor(bx)) {
00968         if (floor(ay) < floor(by)) {
00969             // line is draw upright-to-downleft
00970             if (floor(x) < floor(px) || floor(y) < floor(py))
00971                 result = true;
00972         } else if (floor(ay) > floor(by)) {
00973             // line is draw downright-to-upleft
00974             if (floor(x) > floor(px) || floor(y) < floor(py))
00975                 result = true;
00976         } else {
00977             // line is flat horizontal
00978             if (y < ay) result = true;
00979         }
00980     } else if (floor(ax) < floor(bx)) {
00981         if (floor(ay) < floor(by)) {
00982             // line is draw upleft-to-downright
00983             if (floor(x) < floor(px) || floor(y) > floor(py))
00984                 result = true;
00985         } else if (floor(ay) > floor(by)) {
00986             // line is draw downleft-to-upright
00987             if (floor(x) > floor(px) || floor(y) > floor(py))
00988                 result = true;
00989         } else {
00990             // line is flat horizontal
00991             if (y > ay)
00992                 result = true;
00993         }
00994     } else {
00995         // line is vertical
00996         if (floor(ay) < floor(by)) {
00997             if (x < ax) result = true;
00998         } else if (floor(ay) > floor(by)) {
00999             if (x > ax) result = true;
01000         } else {
01001             if (!(x == ax && y == ay))
01002                 result = true;
01003         }
01004     }
01005 
01006     return result;
01007 }
01008 
01016 static int pointInLine(double x, double y, double ax, double ay,
01017                        double bx, double by)
01018 {
01019     if (ax > bx) {
01020         if (ay < by) {
01021             // line is draw upright-to-downleft
01022 
01023             // if (x,y) is in on or above the upper right point,
01024             // return -1.
01025             if (y <= ay && x >= ax)
01026                 return -1;
01027 
01028             // if (x,y) is in on or below the lower left point,
01029             // return 1.
01030             if (y >= by && x <= bx)
01031                 return 1;
01032         } else {
01033             // line is draw downright-to-upleft
01034 
01035             // If the line is flat, only use the x coordinate.
01036             if (floor(ay) == floor(by)) {
01037                 // if (x is to the right of the rightmost point,
01038                 // return -1. otherwise if x is to the left of the
01039                 // leftmost point, return 1.
01040                 if (x >= ax)
01041                     return -1;
01042                 else if (x <= bx)
01043                     return 1;
01044             } else {
01045                 // if (x,y) is on or below the lower right point,
01046                 // return -1.
01047                 if (y >= ay && x >= ax)
01048                     return -1;
01049 
01050                 // if (x,y) is on or above the upper left point,
01051                 // return 1.
01052                 if (y <= by && x <= bx)
01053                     return 1;
01054             }
01055         }
01056     } else {
01057         if (ay < by) {
01058             // line is draw upleft-to-downright
01059 
01060             // If (x,y) is on or above the upper left point, return
01061             // -1.
01062             if (y <= ay && x <= ax)
01063                 return -1;
01064 
01065             // If (x,y) is on or below the lower right point, return
01066             // 1.
01067             if (y >= by && x >= bx)
01068                 return 1;
01069         } else {
01070             // line is draw downleft-to-upright
01071 
01072             // If the line is flat, only use the x coordinate.
01073             if (floor(ay) == floor(by)) {
01074                 if (x <= ax)
01075                     return -1;
01076                 else if (x >= bx)
01077                     return 1;
01078             } else {
01079                 // If (x,y) is on or below the lower left point, return
01080                 // -1.
01081                 if (y >= ay && x <= ax)
01082                     return -1;
01083 
01084                 // If (x,y) is on or above the upper right point, return
01085                 // 1.
01086                 if (y <= by && x >= bx)
01087                     return 1;
01088             }
01089         }
01090     }
01091 
01092     // No tests proved that (x,y) was outside [(ax,ay),(bx,by)], so we
01093     // assume it's inside the line's bounds.
01094     return 0;
01095 }
01096 
01112 QPointF QtColorTriangle::movePointToTriangle(double x, double y, const Vertex &a,
01113                                                const Vertex &b, const Vertex &c) const
01114 {
01115     // Let v1A be the vector from (x,y) to a.
01116     // Let v2A be the vector from a to b.
01117     // Find the angle alphaA between v1A and v2A.
01118     double v1xA = x - a.point.x();
01119     double v1yA = y - a.point.y();
01120     double v2xA = b.point.x() - a.point.x();
01121     double v2yA = b.point.y() - a.point.y();
01122     double vpA = vprod(v1xA, v1yA, v2xA, v2yA);
01123     double cosA = vpA / (vlen(v1xA, v1yA) * vlen(v2xA, v2yA));
01124     double alphaA = acos(cosA);
01125 
01126     // Let v1B be the vector from x to b.
01127     // Let v2B be the vector from b to c.
01128     double v1xB = x - b.point.x();
01129     double v1yB = y - b.point.y();
01130     double v2xB = c.point.x() - b.point.x();
01131     double v2yB = c.point.y() - b.point.y();
01132     double vpB = vprod(v1xB, v1yB, v2xB, v2yB);
01133     double cosB = vpB / (vlen(v1xB, v1yB) * vlen(v2xB, v2yB));
01134     double alphaB = acos(cosB);
01135 
01136     // Let v1C be the vector from x to c.
01137     // Let v2C be the vector from c back to a.
01138     double v1xC = x - c.point.x();
01139     double v1yC = y - c.point.y();
01140     double v2xC = a.point.x() - c.point.x();
01141     double v2yC = a.point.y() - c.point.y();
01142     double vpC = vprod(v1xC, v1yC, v2xC, v2yC);
01143     double cosC = vpC / (vlen(v1xC, v1yC) * vlen(v2xC, v2yC));
01144     double alphaC = acos(cosC);
01145 
01146     // Find the radian angles between the (1,0) vector and the points
01147     // A, B, C and (x,y). Use this information to determine which of
01148     // the edges we should project (x,y) onto.
01149     double angleA = angleAt(a.point, contentsRect());
01150     double angleB = angleAt(b.point, contentsRect());
01151     double angleC = angleAt(c.point, contentsRect());
01152     double angleP = angleAt(QPointF(x, y), contentsRect());
01153 
01154     // If (x,y) is in the a-b area, project onto the a-b vector.
01155     if (angleBetweenAngles(angleP, angleA, angleB)) {
01156         // Find the distance from (x,y) to a. Then use the slope of
01157         // the a-b vector with this distance and the angle between a-b
01158         // and a-(x,y) to determine the point of intersection of the
01159         // perpendicular projection from (x,y) onto a-b.
01160         double pdist = sqrt(qsqr(x - a.point.x()) + qsqr(y - a.point.y()));
01161 
01162         // the length of all edges is always > 0
01163         double p0x = a.point.x() + ((b.point.x() - a.point.x()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
01164         double p0y = a.point.y() + ((b.point.y() - a.point.y()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
01165 
01166         // If (x,y) is above the a-b line, which basically means it's
01167         // outside the triangle, then return its projection onto a-b.
01168         if (pointAbovePoint(x, y, p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y())) {
01169             // If the projection is "outside" a, return a. If it is
01170             // outside b, return b. Otherwise return the projection.
01171             int n = pointInLine(p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y());
01172             if (n < 0)
01173                 return a.point;
01174             else if (n > 0)
01175                 return b.point;
01176 
01177             return QPointF(p0x, p0y);
01178         }
01179     } else if (angleBetweenAngles(angleP, angleB, angleC)) {
01180         // If (x,y) is in the b-c area, project onto the b-c vector.
01181         double pdist = sqrt(qsqr(x - b.point.x()) + qsqr(y - b.point.y()));
01182 
01183         // the length of all edges is always > 0
01184         double p0x = b.point.x() + ((c.point.x() - b.point.x()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
01185         double p0y = b.point.y() + ((c.point.y() - b.point.y()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
01186 
01187         if (pointAbovePoint(x, y, p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y())) {
01188             int n = pointInLine(p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y());
01189             if (n < 0)
01190                 return b.point;
01191             else if (n > 0)
01192                 return c.point;
01193             return QPointF(p0x, p0y);
01194         }
01195     } else if (angleBetweenAngles(angleP, angleC, angleA)) {
01196         // If (x,y) is in the c-a area, project onto the c-a vector.
01197         double pdist = sqrt(qsqr(x - c.point.x()) + qsqr(y - c.point.y()));
01198 
01199         // the length of all edges is always > 0
01200         double p0x = c.point.x() + ((a.point.x() - c.point.x()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
01201         double p0y = c.point.y() + ((a.point.y() - c.point.y()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
01202 
01203         if (pointAbovePoint(x, y, p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y())) {
01204             int n = pointInLine(p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y());
01205             if (n < 0)
01206                 return c.point;
01207             else if (n > 0)
01208                 return a.point;
01209             return QPointF(p0x, p0y);
01210         }
01211     }
01212 
01213     // (x,y) is inside the triangle (inside a-b, b-c and a-c).
01214     return QPointF(x, y);
01215 }
01216 
01231 QPointF QtColorTriangle::pointFromColor(const QColor &col) const
01232 {
01233     // Simplifications for the corner cases.
01234     if (col == Qt::black)
01235         return pb;
01236     else if (col == Qt::white)
01237         return pc;
01238 
01239     // Find the x and y slopes
01240     double ab_deltax = pb.x() - pa.x();
01241     double ab_deltay = pb.y() - pa.y();
01242     double bc_deltax = pc.x() - pb.x();
01243     double bc_deltay = pc.y() - pb.y();
01244     double ac_deltax = pc.x() - pa.x();
01245     double ac_deltay = pc.y() - pa.y();
01246 
01247     // Extract the h,s,v values of col.
01248     int hue,sat,val;
01249     col.getHsv(&hue, &sat, &val);
01250 
01251     // Find the line that passes through the triangle where the value
01252     // is equal to our color's value.
01253     double p1 = pa.x() + (ab_deltax * (double) (255 - val)) / 255.0;
01254     double q1 = pa.y() + (ab_deltay * (double) (255 - val)) / 255.0;
01255     double p2 = pb.x() + (bc_deltax * (double) val) / 255.0;
01256     double q2 = pb.y() + (bc_deltay * (double) val) / 255.0;
01257 
01258     // Find the line that passes through the triangle where the
01259     // saturation is equal to our color's value.
01260     double p3 = pa.x() + (ac_deltax * (double) (255 - sat)) / 255.0;
01261     double q3 = pa.y() + (ac_deltay * (double) (255 - sat)) / 255.0;
01262     double p4 = pb.x();
01263     double q4 = pb.y();
01264 
01265     // Find the intersection between these lines.
01266         double x = 0;
01267         double y = 0;
01268         if (p1 != p2) {
01269                 double a = (q2 - q1) / (p2 - p1);
01270                 double c = (q4 - q3) / (p4 - p3);
01271                 double b = q1 - a * p1;
01272                 double d = q3 - c * p3;
01273 
01274                 x = (d - b) / (a - c);
01275                 y = a * x + b;
01276         }
01277         else {
01278                 x = p1;
01279                 y = q3 + (x - p3) * (q4 - q3) / (p4 - p3);
01280         }
01281         
01282     return QPointF(x, y);
01283 }
01284 
01292 QColor QtColorTriangle::colorFromPoint(const QPointF &p) const
01293 {
01294     // Find the outer radius of the hue gradient.
01295     int outerRadius = (contentsRect().width() - 1) / 2;
01296     if ((contentsRect().height() - 1) / 2 < outerRadius)
01297         outerRadius = (contentsRect().height() - 1) / 2;
01298 
01299     // Find the center coordinates
01300     double cx = (double) contentsRect().center().x();
01301     double cy = (double) contentsRect().center().y();
01302 
01303     // Find the a, b and c from their angles, the center of the rect
01304     // and the radius of the hue gradient donut.
01305     QPointF pa(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
01306                    cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
01307     QPointF pb(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
01308                    cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
01309     QPointF pc(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
01310                    cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
01311 
01312     // Find the hue value from the angle of the 'a' point.
01313     double angle = a - PI/2.0;
01314     if (angle < 0) angle += TWOPI;
01315     double hue = (360.0 * angle) / TWOPI;
01316 
01317     // Create the color of the 'a' corner point. We know that b is
01318     // black and c is white.
01319     QColor color;
01320     color.setHsv(360 - (int) floor(hue), 255, 255);
01321 
01322     // See also drawTrigon(), which basically does exactly the same to
01323     // determine all colors in the trigon.
01324     Vertex aa(color, pa);
01325     Vertex bb(Qt::black, pb);
01326     Vertex cc(Qt::white, pc);
01327 
01328     // Make sure p1 is above p2, which is above p3.
01329     Vertex *p1 = &aa;
01330     Vertex *p2 = &bb;
01331     Vertex *p3 = &cc;
01332     if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
01333     if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
01334     if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
01335 
01336     // Find the slopes of all edges in the trigon. All the three y
01337     // deltas here are positive because of the above sorting.
01338     double p1p2ydist = p2->point.y() - p1->point.y();
01339     double p1p3ydist = p3->point.y() - p1->point.y();
01340     double p2p3ydist = p3->point.y() - p2->point.y();
01341     double p1p2xdist = p2->point.x() - p1->point.x();
01342     double p1p3xdist = p3->point.x() - p1->point.x();
01343     double p2p3xdist = p3->point.x() - p2->point.x();
01344 
01345     // The first x delta decides wether we have a lefty or a righty
01346     // trigon. A lefty trigon has its tallest edge on the right hand
01347     // side of the trigon. The righty trigon has it on its left side.
01348     // This property determines wether the left or the right set of x
01349     // coordinates will be continuous.
01350     bool lefty = p1p2xdist < 0;
01351 
01352     // Find whether the selector's y is in the first or second shorty,
01353     // counting from the top and downwards. This is used to find the
01354     // color at the selector point.
01355     bool firstshorty = (p.y() >= p1->point.y() && p.y() < p2->point.y());
01356 
01357     // From the y value of the selector's position, find the left and
01358     // right x values.
01359     double leftx;
01360     double rightx;
01361     if (lefty) {
01362         if (firstshorty) {
01363             leftx = p1->point.x();
01364             if (floor(p1p2ydist) != 0.0) {
01365                 leftx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
01366             } else {
01367                 leftx = qMin(p1->point.x(), p2->point.x());
01368             }
01369         } else {
01370             leftx = p2->point.x();
01371             if (floor(p2p3ydist) != 0.0) {
01372                 leftx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
01373             } else {
01374                 leftx = qMin(p2->point.x(), p3->point.x());
01375             }
01376         }
01377 
01378         rightx = p1->point.x();
01379         rightx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
01380     } else {
01381         leftx = p1->point.x();
01382         leftx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
01383 
01384         if (firstshorty) {
01385             rightx = p1->point.x();
01386             if (floor(p1p2ydist) != 0.0) {
01387                 rightx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
01388             } else {
01389                 rightx = qMax(p1->point.x(), p2->point.x());
01390             }
01391         } else {
01392             rightx = p2->point.x(); 
01393             if (floor(p2p3ydist) != 0.0) {
01394                 rightx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
01395             } else {
01396                 rightx = qMax(p2->point.x(), p3->point.x());
01397             }
01398         }
01399     }
01400 
01401     // Find the r,g,b values of the points on the trigon's edges that
01402     // are to the left and right of the selector.
01403     double rshort = 0, gshort = 0, bshort = 0;
01404     double rlong = 0, glong = 0, blong = 0;
01405     if (firstshorty) {
01406         if (floor(p1p2ydist) != 0.0) {
01407             rshort  = p2->color.r * (p.y() - p1->point.y()) / p1p2ydist;
01408             gshort  = p2->color.g * (p.y() - p1->point.y()) / p1p2ydist;
01409             bshort  = p2->color.b * (p.y() - p1->point.y()) / p1p2ydist;
01410             rshort += p1->color.r * (p2->point.y() - p.y()) / p1p2ydist;
01411             gshort += p1->color.g * (p2->point.y() - p.y()) / p1p2ydist;
01412             bshort += p1->color.b * (p2->point.y() - p.y()) / p1p2ydist;
01413         } else {
01414             if (lefty) {
01415                 if (p1->point.x() <= p2->point.x()) {
01416                     rshort  = p1->color.r;
01417                     gshort  = p1->color.g;
01418                     bshort  = p1->color.b;
01419                 } else {
01420                     rshort  = p2->color.r;
01421                     gshort  = p2->color.g;
01422                     bshort  = p2->color.b;
01423                 }
01424             } else {
01425                 if (p1->point.x() > p2->point.x()) {
01426                     rshort  = p1->color.r;
01427                     gshort  = p1->color.g;
01428                     bshort  = p1->color.b;
01429                 } else {
01430                     rshort  = p2->color.r;
01431                     gshort  = p2->color.g;
01432                     bshort  = p2->color.b;
01433                 }
01434             }
01435         }
01436     } else {
01437         if (floor(p2p3ydist) != 0.0) {
01438             rshort  = p3->color.r * (p.y() - p2->point.y()) / p2p3ydist;
01439             gshort  = p3->color.g * (p.y() - p2->point.y()) / p2p3ydist;
01440             bshort  = p3->color.b * (p.y() - p2->point.y()) / p2p3ydist;
01441             rshort += p2->color.r * (p3->point.y() - p.y()) / p2p3ydist;
01442             gshort += p2->color.g * (p3->point.y() - p.y()) / p2p3ydist;
01443             bshort += p2->color.b * (p3->point.y() - p.y()) / p2p3ydist;
01444         } else {
01445             if (lefty) {
01446                 if (p2->point.x() <= p3->point.x()) {
01447                     rshort  = p2->color.r;
01448                     gshort  = p2->color.g;
01449                     bshort  = p2->color.b;
01450                 } else {
01451                     rshort  = p3->color.r;
01452                     gshort  = p3->color.g;
01453                     bshort  = p3->color.b;
01454                 }
01455             } else {
01456                 if (p2->point.x() > p3->point.x()) {
01457                     rshort  = p2->color.r;
01458                     gshort  = p2->color.g;
01459                     bshort  = p2->color.b;
01460                 } else {
01461                     rshort  = p3->color.r;
01462                     gshort  = p3->color.g;
01463                     bshort  = p3->color.b;
01464                 }
01465             }
01466         }
01467     }
01468 
01469     // p1p3ydist is never 0
01470     rlong  = p3->color.r * (p.y() - p1->point.y()) / p1p3ydist;
01471     glong  = p3->color.g * (p.y() - p1->point.y()) / p1p3ydist;
01472     blong  = p3->color.b * (p.y() - p1->point.y()) / p1p3ydist;
01473     rlong += p1->color.r * (p3->point.y() - p.y()) / p1p3ydist;
01474     glong += p1->color.g * (p3->point.y() - p.y()) / p1p3ydist;
01475     blong += p1->color.b * (p3->point.y() - p.y()) / p1p3ydist;
01476 
01477     // rshort,gshort,bshort is the color on one of the shortys.
01478     // rlong,glong,blong is the color on the longy. So depending on
01479     // wether we have a lefty trigon or not, we can determine which
01480     // colors are on the left and right edge.
01481     double rl, gl, bl, rr, gr, br;
01482     if (lefty) {
01483         rl = rshort; gl = gshort; bl = bshort;
01484         rr = rlong; gr = glong; br = blong;
01485     } else {
01486         rl = rlong; gl = glong; bl = blong;
01487         rr = rshort; gr = gshort; br = bshort;
01488     }
01489 
01490     // Find the distance from the left x to the right x (xdist). Then
01491     // find the distances from the selector to each of these (saxdist
01492     // and saxdist2). These distances are used to find the color at
01493     // the selector.
01494     double xdist = rightx - leftx;
01495     double saxdist = p.x() - leftx;
01496     double saxdist2 = xdist - saxdist;
01497 
01498     // Now determine the r,g,b values of the selector using a linear
01499     // approximation.
01500     double r, g, b;
01501     if (xdist != 0.0) {
01502         r = (saxdist2 * rl / xdist) + (saxdist * rr / xdist);
01503         g = (saxdist2 * gl / xdist) + (saxdist * gr / xdist);
01504         b = (saxdist2 * bl / xdist) + (saxdist * br / xdist);
01505     } else {
01506         // In theory, the left and right color will be equal here. But
01507         // because of the loss of precision, we get an error on both
01508         // colors. The best approximation we can get is from adding
01509         // the two errors, which in theory will eliminate the error
01510         // but in practise will only minimize it.
01511         r = (rl + rr) / 2;
01512         g = (gl + gr) / 2;
01513         b = (bl + br) / 2;
01514     }
01515 
01516     // Now floor the color components and fit them into proper
01517     // boundaries. This again is to compensate for the error caused by
01518     // loss of precision.
01519     int ri = (int) floor(r);
01520     int gi = (int) floor(g);
01521     int bi = (int) floor(b);
01522     if (ri < 0) ri = 0;
01523     else if (ri > 255) ri = 255;
01524     if (gi < 0) gi = 0;
01525     else if (gi > 255) gi = 255;
01526     if (bi < 0) bi = 0;
01527     else if (bi > 255) bi = 255;
01528 
01529     // Voila, we have the color at the point of the selector.
01530     return QColor(ri, gi, bi);
01531 }

Generated by doxygen 1.7.6.1. The KLatexFormula website is hosted on sourceforge.net