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