[KLF Backend][KLF Tools][KLF Home]
KLatexFormula Project
qtcolortriangle.cpp
Go to the documentation of this file.
1/*
2 * This file was very slightly modified by Philippe Faist for KLatexFormula. (april 2009)
3 * In order for integration into KLatexFormula, this code is relicensed
4 * to **GPL Version 2.1 or higher** as described in the footnote to the GPL
5 * compatibility table found at
6 * http://www.gnu.org/licenses/gpl-faq.html#compat-matrix-footnote-7
7 *
8 */
9
10/****************************************************************************
11**
12** This file is part of a Qt Solutions component.
13**
14** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
15**
16** Contact: Qt Software Information (qt-info@nokia.com)
17**
18** Commercial Usage
19** Licensees holding valid Qt Commercial licenses may use this file in
20** accordance with the Qt Solutions Commercial License Agreement provided
21** with the Software or, alternatively, in accordance with the terms
22** contained in a written agreement between you and Nokia.
23**
24** GNU Lesser General Public License Usage
25** Alternatively, this file may be used under the terms of the GNU Lesser
26** General Public License version 2.1 as published by the Free Software
27** Foundation and appearing in the file LICENSE.LGPL included in the
28** packaging of this file. Please review the following information to
29** ensure the GNU Lesser General Public License version 2.1 requirements
30** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
31**
32** In addition, as a special exception, Nokia gives you certain
33** additional rights. These rights are described in the Nokia Qt LGPL
34** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
35** package.
36**
37** GNU General Public License Usage
38** Alternatively, this file may be used under the terms of the GNU
39** General Public License version 3.0 as published by the Free Software
40** Foundation and appearing in the file LICENSE.GPL included in the
41** packaging of this file. Please review the following information to
42** ensure the GNU General Public License version 3.0 requirements will be
43** met: http://www.gnu.org/copyleft/gpl.html.
44**
45** Please note Third Party Software included with Qt Solutions may impose
46** additional restrictions and it is the user's responsibility to ensure
47** that they have met the licensing requirements of the GPL, LGPL, or Qt
48** Solutions Commercial license and the relevant license of the Third
49** Party Software they are using.
50**
51** If you are unsure which license is appropriate for your use, please
52** contact the sales department at qt-sales@nokia.com.
53**
54****************************************************************************/
55
56#include "qtcolortriangle.h"
57
58#include <QEvent>
59#include <QMap>
60#include <QVarLengthArray>
61#include <QConicalGradient>
62#include <QFrame>
63#include <QImage>
64#include <QKeyEvent>
65#include <QLayout>
66#include <QMouseEvent>
67#include <QPainter>
68#include <QPainterPath>
69#include <QPixmap>
70#include <QResizeEvent>
71#include <QToolTip>
72#include <QVBoxLayout>
73
74#include <math.h>
75
98const double PI = 3.14159265358979323846264338327950288419717;
99const double TWOPI = 2.0*PI;
100
101/*
102 Used to store color values in the range 0..255 as doubles.
103*/
104struct DoubleColor
105{
106 double r, g, b;
107
108 DoubleColor() : r(0.0), g(0.0), b(0.0) {}
109 DoubleColor(double red, double green, double blue) : r(red), g(green), b(blue) {}
110 DoubleColor(const DoubleColor &c) : r(c.r), g(c.g), b(c.b) {}
111};
112
113/*
114 Used to store pairs of DoubleColor and DoublePoint in one structure.
115*/
116struct Vertex {
117 DoubleColor color;
118 QPointF point;
119
120 Vertex(const DoubleColor &c, const QPointF &p) : color(c), point(p) {}
121 Vertex(const QColor &c, const QPointF &p)
122 : color(DoubleColor((double) c.red(), (double) c.green(),
123 (double) c.blue())), point(p) {}
124};
125
130static void swap(Vertex **a, Vertex **b)
131{
132 Vertex *tmp = *a;
133 *a = *b;
134 *b = tmp;
135}
136
141 : QWidget(parent), bg(sizeHint(), QImage::Format_RGB32), selMode(Idle)
142{
143 setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
144 setFocusPolicy(Qt::StrongFocus);
145
146 mustGenerateBackground = true;
147
148 QColor tmp;
149 tmp.setHsv(76, 184, 206);
150 setColor(tmp);
151}
152
159
166{
167 outerRadius = (contentsRect().width() - 1) / 2;
168 if ((contentsRect().height() - 1) / 2 < outerRadius)
169 outerRadius = (contentsRect().height() - 1) / 2;
170
171 penWidth = (int) floor(outerRadius / 50.0);
172 ellipseSize = (int) floor(outerRadius / 12.5);
173
174 double cx = (double) contentsRect().center().x();
175 double cy = (double) contentsRect().center().y();
176
177 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
178 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
179 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
180 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
181 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
182 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
183 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
184 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
185
186 // Find the current position of the selector
187 selectorPos = pointFromColor(curColor);
188
189 update();
190}
191
195{
196 return QSize(100, 100);
197}
198
204{
205 return w;
206}
207
214void QtColorTriangle::genBackground()
215{
216 // Find the inner radius of the hue donut.
217 double innerRadius = outerRadius - outerRadius / 5;
218
219 // Create an image of the same size as the contents rect.
220 bg = QImage(contentsRect().size(), QImage::Format_ARGB32_Premultiplied);
221 bg.fill(qRgba(0,0,0,0));
222 QPainter p(&bg);
223 p.setRenderHint(QPainter::Antialiasing);
224
225 QConicalGradient gradient(bg.rect().center(), 90);
227 for (double i = 0; i <= 1.0; i += 0.1) {
228#if QT_VERSION < 0x040100
229 color.setHsv(int(i * 360.0), 255, 255);
230#else
231 color.setHsv(int(360.0 - (i * 360.0)), 255, 255);
232#endif
233 gradient.setColorAt(i, color);
234 }
235
236 QRectF innerRadiusRect(bg.rect().center().x() - innerRadius, bg.rect().center().y() - innerRadius,
237 innerRadius * 2 + 1, innerRadius * 2 + 1);
238 QRectF outerRadiusRect(bg.rect().center().x() - outerRadius, bg.rect().center().y() - outerRadius,
239 outerRadius * 2 + 1, outerRadius * 2 + 1);
240 QPainterPath path;
241 path.addEllipse(innerRadiusRect);
242 path.addEllipse(outerRadiusRect);
243
244 p.save();
245 p.setClipPath(path);
246 p.fillRect(bg.rect(), gradient);
247 p.restore();
248
249 double penThickness = bg.width() / 400.0;
250 for (int f = 0; f <= 5760; f += 20) {
251 int value = int((0.5 + cos(((f - 1800) / 5760.0) * TWOPI) / 2) * 255.0);
252
253 color.setHsv(int((f / 5760.0) * 360.0), 128 + (255 - value)/2, 255 - (255 - value)/4);
254 p.setPen(QPen(color, penThickness));
255 p.drawArc(innerRadiusRect, 1440 - f, 20);
256
257 color.setHsv(int((f / 5760.0) * 360.0), 128 + value/2, 255 - value/4);
258 p.setPen(QPen(color, penThickness));
259 p.drawArc(outerRadiusRect, 2880 - 1440 - f, 20);
260 }
261
262 return;
263}
264
272{
273 if ((e->buttons() & Qt::LeftButton) == 0)
274 return;
275
276 QPointF depos((double) e->pos().x(), (double) e->pos().y());
277 bool newColor = false;
278
279 if (selMode == SelectingHue) {
280 // If selecting hue, find the new angles for the points a,b,c
281 // of the triangle. The following update() will then redraw
282 // the triangle.
283 a = angleAt(depos, contentsRect());
284 b = a + TWOPI / 3.0;
285 c = b + TWOPI / 3.0;
286 if (b > TWOPI) b -= TWOPI;
287 if (c > TWOPI) c -= TWOPI;
288
289 double am = a - PI/2;
290 if (am < 0) am += TWOPI;
291
292 curHue = 360 - (int) (((am) * 360.0) / TWOPI);
293 int h,s,v;
294 curColor.getHsv(&h, &s, &v);
295
296 if (curHue != h) {
297 newColor = true;
298 curColor.setHsv(curHue, s, v, curColor.alpha());
299 }
300
301 double cx = (double) contentsRect().center().x();
302 double cy = (double) contentsRect().center().y();
303
304 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
305 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
306 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
307 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
308 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
309 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
310 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
311 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
312
313 selectorPos = pointFromColor(curColor);
314 } else {
315 Vertex aa(Qt::black, pa);
316 Vertex bb(Qt::black, pb);
317 Vertex cc(Qt::black, pc);
318
319 Vertex *p1 = &aa;
320 Vertex *p2 = &bb;
321 Vertex *p3 = &cc;
322 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
323 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
324 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
325
326 selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
327 QColor col = colorFromPoint(selectorPos);
328 if (col != curColor) {
329 // Ensure that hue does not change when selecting
330 // saturation and value.
331 int h,s,v;
332 col.getHsv(&h, &s, &v);
333 curColor.setHsv(curHue, s, v, curColor.alpha());
334 newColor = true;
335 }
336 }
337
338 if (newColor)
339 internalSetNewColor(curColor);
340
341 update();
342}
343
353{
354 // Only respond to the left mouse button.
355 if (e->button() != Qt::LeftButton)
356 return;
357
358 QPointF depos((double) e->pos().x(), (double) e->pos().y());
359 double rad = radiusAt(depos, contentsRect());
360 bool newColor = false;
361
362 // As in mouseMoveEvent, either find the a,b,c angles or the
363 // radian position of the selector, then order an update.
364 if (rad > (outerRadius - (outerRadius / 5))) {
365 selMode = SelectingHue;
366
367 a = angleAt(depos, contentsRect());
368 b = a + TWOPI / 3.0;
369 c = b + TWOPI / 3.0;
370 if (b > TWOPI) b -= TWOPI;
371 if (c > TWOPI) c -= TWOPI;
372
373 double am = a - PI/2;
374 if (am < 0) am += TWOPI;
375
376 curHue = 360 - (int) ((am * 360.0) / TWOPI);
377 int h,s,v;
378 curColor.getHsv(&h, &s, &v);
379
380 if (h != curHue) {
381 newColor = true;
382 curColor.setHsv(curHue, s, v, curColor.alpha());
383 }
384
385 double cx = (double) contentsRect().center().x();
386 double cy = (double) contentsRect().center().y();
387
388 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
389 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
390 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
391 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
392 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
393 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
394 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
395 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
396
397 selectorPos = pointFromColor(curColor);
398 internalSetNewColor(curColor);
399 } else {
400 selMode = SelectingSatValue;
401
402 Vertex aa(Qt::black, pa);
403 Vertex bb(Qt::black, pb);
404 Vertex cc(Qt::black, pc);
405
406 Vertex *p1 = &aa;
407 Vertex *p2 = &bb;
408 Vertex *p3 = &cc;
409 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
410 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
411 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
412
413 selectorPos = movePointToTriangle(depos.x(), depos.y(), aa, bb, cc);
414 QColor col = colorFromPoint(selectorPos);
415 if (col != curColor) {
416 int tempalpha = curColor.alpha();
417 curColor = col;
418 curColor.setAlpha(tempalpha);
419 newColor = true;
420 }
421 }
422
423 if (newColor)
424 internalSetNewColor(curColor);
425
426 update();
427}
428
435{
436 if (e->button() == Qt::LeftButton)
437 selMode = Idle;
438}
439
444{
445 switch (e->key()) {
446 case Qt::Key_Left: {
447 --curHue;
448 if (curHue < 0) curHue += 360;
449 int h,s,v;
450 curColor.getHsv(&h, &s, &v);
451 QColor tmp;
452 tmp.setHsv(curHue, s, v);
453 setColor(tmp);
454 }
455 break;
456 case Qt::Key_Right: {
457 ++curHue;
458 if (curHue > 359) curHue -= 360;
459 int h,s,v;
460 curColor.getHsv(&h, &s, &v);
461 QColor tmp;
462 tmp.setHsv(curHue, s, v);
463 setColor(tmp);
464 }
465 break;
466 case Qt::Key_Up: {
467 int h,s,v;
468 curColor.getHsv(&h, &s, &v);
469 QColor tmp;
470 if (e->modifiers() & Qt::ShiftModifier) {
471 if (s > 5) s -= 5;
472 else s = 0;
473 } else {
474 if (v > 5) v -= 5;
475 else v = 0;
476 }
477 tmp.setHsv(curHue, s, v);
478 setColor(tmp);
479 }
480 break;
481 case Qt::Key_Down: {
482 int h,s,v;
483 curColor.getHsv(&h, &s, &v);
484 QColor tmp;
485 if (e->modifiers() & Qt::ShiftModifier) {
486 if (s < 250) s += 5;
487 else s = 255;
488 } else {
489 if (v < 250) v += 5;
490 else v = 255;
491 }
492 tmp.setHsv(curHue, s, v);
493 setColor(tmp);
494 }
495 break;
496 };
497}
498
505{
506 outerRadius = (contentsRect().width() - 1) / 2;
507 if ((contentsRect().height() - 1) / 2 < outerRadius)
508 outerRadius = (contentsRect().height() - 1) / 2;
509
510 penWidth = (int) floor(outerRadius / 50.0);
511 ellipseSize = (int) floor(outerRadius / 12.5);
512
513 double cx = (double) contentsRect().center().x();
514 double cy = (double) contentsRect().center().y();
515
516 pa = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
517 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
518 pb = QPointF(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
519 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
520 pc = QPointF(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
521 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
522 pd = QPointF(cx + (cos(a) * (outerRadius - (outerRadius / 10.0))),
523 cy - (sin(a) * (outerRadius - (outerRadius / 10.0))));
524
525 // Find the current position of the selector
526 selectorPos = pointFromColor(curColor);
527
528 mustGenerateBackground = true;
529 update();
530}
531
539{
540 QPainter p(this);
541 if (e->rect().intersects(contentsRect()))
542 p.setClipRegion(e->region().intersected(contentsRect()));
543 if (mustGenerateBackground) {
544 genBackground();
545 mustGenerateBackground = false;
546 }
547
548 // Blit the static generated background with the hue gradient onto
549 // the double buffer.
550 QImage buf = bg.copy();
551
552 // Draw the trigon
553 int h,s,v;
554 curColor.getHsv(&h, &s, &v);
555
556 // Find the color with only the hue, and max value and saturation
557 QColor hueColor;
558 hueColor.setHsv(curHue, 255, 255);
559
560 // Draw the triangle
561 drawTrigon(&buf, pa, pb, pc, hueColor);
562
563 // Slow step: convert the image to a pixmap
564 QPixmap pix = QPixmap::fromImage(buf);
565 QPainter painter(&pix);
566 painter.setRenderHint(QPainter::Antialiasing);
567
568 // Draw an outline of the triangle
569 QColor halfAlpha(0, 0, 0, 128);
570 painter.setPen(QPen(halfAlpha, 0));
571 painter.drawLine(pa, pb);
572 painter.drawLine(pb, pc);
573 painter.drawLine(pc, pa);
574
575 int ri, gi, bi;
576 hueColor.getRgb(&ri, &gi, &bi);
577 if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
578 painter.setPen(QPen(Qt::black, penWidth));
579 else
580 painter.setPen(QPen(Qt::white, penWidth));
581 painter.drawEllipse((int) (pd.x() - ellipseSize / 2.0),
582 (int) (pd.y() - ellipseSize / 2.0),
583 ellipseSize, ellipseSize);
584
585 curColor.getRgb(&ri, &gi, &bi);
586
587 // Find a color for painting the selector based on the brightness
588 // value of the color.
589 if ((ri * 30) + (gi * 59) + (bi * 11) > 12800)
590 painter.setPen(QPen(Qt::black, penWidth));
591 else
592 painter.setPen(QPen(Qt::white, penWidth));
593
594 // Draw the selector ellipse.
595 painter.drawEllipse(QRectF(selectorPos.x() - ellipseSize / 2.0,
596 selectorPos.y() - ellipseSize / 2.0,
597 ellipseSize + 0.5, ellipseSize + 0.5));
598
599 // Blit
600 p.drawPixmap(contentsRect().topLeft(), pix);
601}
602
603
605{
606 emit colorChanged(color);
607}
608
609
610
621 const QPointF &pb, const QPointF &pc,
622 const QColor &color)
623{
624 // Create three Vertex objects. A Vertex contains a double-point
625 // coordinate and a color.
626 // pa is the tip of the arrow
627 // pb is the black corner
628 // pc is the white corner
629 Vertex aa(color, pa);
630 Vertex bb(Qt::black, pb);
631 Vertex cc(Qt::white, pc);
632
633 // Sort. Make p1 above p2, which is above p3 (using y coordinate).
634 // Bubble sorting is fastest here.
635 Vertex *p1 = &aa;
636 Vertex *p2 = &bb;
637 Vertex *p3 = &cc;
638 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
639 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
640 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
641
642 // All the three y deltas are >= 0
643 double p1p2ydist = p2->point.y() - p1->point.y();
644 double p1p3ydist = p3->point.y() - p1->point.y();
645 double p2p3ydist = p3->point.y() - p2->point.y();
646 double p1p2xdist = p2->point.x() - p1->point.x();
647 double p1p3xdist = p3->point.x() - p1->point.x();
648 double p2p3xdist = p3->point.x() - p2->point.x();
649
650 // The first x delta decides wether we have a lefty or a righty
651 // trigon.
652 bool lefty = p1p2xdist < 0;
653
654 // Left and right colors and X values. The key in this map is the
655 // y values. Our goal is to fill these structures with all the
656 // information needed to do a single pass top-to-bottom,
657 // left-to-right drawing of the trigon.
662
663 leftColors.resize(int(floor(p3->point.y() + 1)));
664 rightColors.resize(int(floor(p3->point.y() + 1)));
665 leftX.resize(int(floor(p3->point.y() + 1)));
666 rightX.resize(int(floor(p3->point.y() + 1)));
667
668 // Scan longy - find all left and right colors and X-values for
669 // the tallest edge (p1-p3).
670 DoubleColor source;
671 DoubleColor dest;
672 double r, g, b;
673 double rdelta, gdelta, bdelta;
674 double x;
675 double xdelta;
676 int y1, y2;
677
678 // Initialize with known values
679 x = p1->point.x();
680 source = p1->color;
681 dest = p3->color;
682 r = source.r;
683 g = source.g;
684 b = source.b;
685 y1 = (int) floor(p1->point.y());
686 y2 = (int) floor(p3->point.y());
687
688 // Find slopes (notice that if the y dists are 0, we don't care
689 // about the slopes)
690 xdelta = p1p3ydist == 0.0 ? 0.0 : p1p3xdist / p1p3ydist;
691 rdelta = p1p3ydist == 0.0 ? 0.0 : (dest.r - r) / p1p3ydist;
692 gdelta = p1p3ydist == 0.0 ? 0.0 : (dest.g - g) / p1p3ydist;
693 bdelta = p1p3ydist == 0.0 ? 0.0 : (dest.b - b) / p1p3ydist;
694
695 // Calculate gradients using linear approximation
696 int y;
697 for (y = y1; y < y2; ++y) {
698 if (lefty) {
699 rightColors[y] = DoubleColor(r, g, b);
700 rightX[y] = x;
701 } else {
702 leftColors[y] = DoubleColor(r, g, b);
703 leftX[y] = x;
704 }
705
706 r += rdelta;
707 g += gdelta;
708 b += bdelta;
709 x += xdelta;
710 }
711
712 // Scan top shorty - find all left and right colors and x-values
713 // for the topmost of the two not-tallest short edges.
714 x = p1->point.x();
715 source = p1->color;
716 dest = p2->color;
717 r = source.r;
718 g = source.g;
719 b = source.b;
720 y1 = (int) floor(p1->point.y());
721 y2 = (int) floor(p2->point.y());
722
723 // Find slopes (notice that if the y dists are 0, we don't care
724 // about the slopes)
725 xdelta = p1p2ydist == 0.0 ? 0.0 : p1p2xdist / p1p2ydist;
726 rdelta = p1p2ydist == 0.0 ? 0.0 : (dest.r - r) / p1p2ydist;
727 gdelta = p1p2ydist == 0.0 ? 0.0 : (dest.g - g) / p1p2ydist;
728 bdelta = p1p2ydist == 0.0 ? 0.0 : (dest.b - b) / p1p2ydist;
729
730 // Calculate gradients using linear approximation
731 for (y = y1; y < y2; ++y) {
732 if (lefty) {
733 leftColors[y] = DoubleColor(r, g, b);
734 leftX[y] = x;
735 } else {
736 rightColors[y] = DoubleColor(r, g, b);
737 rightX[y] = x;
738 }
739
740 r += rdelta;
741 g += gdelta;
742 b += bdelta;
743 x += xdelta;
744 }
745
746 // Scan bottom shorty - find all left and right colors and
747 // x-values for the bottommost of the two not-tallest short edges.
748 x = p2->point.x();
749 source = p2->color;
750 dest = p3->color;
751 r = source.r;
752 g = source.g;
753 b = source.b;
754 y1 = (int) floor(p2->point.y());
755 y2 = (int) floor(p3->point.y());
756
757 // Find slopes (notice that if the y dists are 0, we don't care
758 // about the slopes)
759 xdelta = p2p3ydist == 0.0 ? 0.0 : p2p3xdist / p2p3ydist;
760 rdelta = p2p3ydist == 0.0 ? 0.0 : (dest.r - r) / p2p3ydist;
761 gdelta = p2p3ydist == 0.0 ? 0.0 : (dest.g - g) / p2p3ydist;
762 bdelta = p2p3ydist == 0.0 ? 0.0 : (dest.b - b) / p2p3ydist;
763
764 // Calculate gradients using linear approximation
765 for (y = y1; y < y2; ++y) {
766 if (lefty) {
767 leftColors[y] = DoubleColor(r, g, b);
768 leftX[y] = x;
769 } else {
770 rightColors[y] = DoubleColor(r, g, b);
771 rightX[y] = x;
772 }
773
774 r += rdelta;
775 g += gdelta;
776 b += bdelta;
777 x += xdelta;
778 }
779
780 // Inner loop. For each y in the left map of x-values, draw one
781 // line from left to right.
782 const int p3yfloor = int(floor(p3->point.y()));
783 for (int y = int(floor(p1->point.y())); y < p3yfloor; ++y) {
784 double lx = leftX[y];
785 double rx = rightX[y];
786
787 int lxi = (int) floor(lx);
788 int rxi = (int) floor(rx);
789 DoubleColor rc = rightColors[y];
790 DoubleColor lc = leftColors[y];
791
792 // if the xdist is 0, don't draw anything.
793 double xdist = rx - lx;
794 if (xdist != 0.0) {
795 double r = lc.r;
796 double g = lc.g;
797 double b = lc.b;
798 double rdelta = (rc.r - r) / xdist;
799 double gdelta = (rc.g - g) / xdist;
800 double bdelta = (rc.b - b) / xdist;
801
802 QRgb *scanline = reinterpret_cast<QRgb *>(buf->scanLine(y));
803 scanline += lxi;
804
805 // Inner loop 2. Draws the line from left to right.
806 for (int i = lxi; i < rxi; ++i) {
807 *scanline++ = qRgb((int) r, (int) g, (int) b);
808 r += rdelta;
809 g += gdelta;
810 b += bdelta;
811 }
812 }
813 }
814}
815
821{
822 if (col == curColor)
823 return;
824
825 curColor = col;
826
827 int h, s, v;
828 curColor.getHsv(&h, &s, &v);
829
830 // Never use an invalid hue to display colors
831 if (h != -1)
832 curHue = h;
833
834 a = (((360 - curHue) * TWOPI) / 360.0);
835 a += PI / 2.0;
836 if (a > TWOPI) a -= TWOPI;
837
838 b = a + TWOPI/3;
839 c = b + TWOPI/3;
840
841 if (b > TWOPI) b -= TWOPI;
842 if (c > TWOPI) c -= TWOPI;
843
844 double cx = (double) contentsRect().center().x();
845 double cy = (double) contentsRect().center().y();
846 double innerRadius = outerRadius - (outerRadius / 5.0);
847 double pointerRadius = outerRadius - (outerRadius / 10.0);
848
849 pa = QPointF(cx + (cos(a) * innerRadius), cy - (sin(a) * innerRadius));
850 pb = QPointF(cx + (cos(b) * innerRadius), cy - (sin(b) * innerRadius));
851 pc = QPointF(cx + (cos(c) * innerRadius), cy - (sin(c) * innerRadius));
852 pd = QPointF(cx + (cos(a) * pointerRadius), cy - (sin(a) * pointerRadius));
853
854 selectorPos = pointFromColor(curColor);
855 update();
856
857 internalSetNewColor(curColor);
858}
859
865{
866 return curColor;
867}
868
874double QtColorTriangle::radiusAt(const QPointF &pos, const QRect &rect) const
875{
876 double mousexdist = pos.x() - (double) rect.center().x();
877 double mouseydist = pos.y() - (double) rect.center().y();
878 return sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
879}
880
888double QtColorTriangle::angleAt(const QPointF &pos, const QRect &rect) const
889{
890 double mousexdist = pos.x() - (double) rect.center().x();
891 double mouseydist = pos.y() - (double) rect.center().y();
892 double mouserad = sqrt(mousexdist * mousexdist + mouseydist * mouseydist);
893 if (mouserad == 0.0)
894 return 0.0;
895
896 double angle = acos(mousexdist / mouserad);
897 if (mouseydist >= 0)
898 angle = TWOPI - angle;
899
900 return angle;
901}
902
907inline double qsqr(double a)
908{
909 return a * a;
910}
911
916inline double vlen(double x, double y)
917{
918 return sqrt(qsqr(x) + qsqr(y));
919}
920
925inline double vprod(double x1, double y1, double x2, double y2)
926{
927 return x1 * x2 + y1 * y2;
928}
929
935bool angleBetweenAngles(double p, double a1, double a2)
936{
937 if (a1 > a2) {
938 a2 += TWOPI;
939 if (p < PI) p += TWOPI;
940 }
941
942 return p >= a1 && p < a2;
943}
944
962static bool pointAbovePoint(double x, double y, double px, double py,
963 double ax, double ay, double bx, double by)
964{
965 bool result = false;
966
967 if (floor(ax) > floor(bx)) {
968 if (floor(ay) < floor(by)) {
969 // line is draw upright-to-downleft
970 if (floor(x) < floor(px) || floor(y) < floor(py))
971 result = true;
972 } else if (floor(ay) > floor(by)) {
973 // line is draw downright-to-upleft
974 if (floor(x) > floor(px) || floor(y) < floor(py))
975 result = true;
976 } else {
977 // line is flat horizontal
978 if (y < ay) result = true;
979 }
980 } else if (floor(ax) < floor(bx)) {
981 if (floor(ay) < floor(by)) {
982 // line is draw upleft-to-downright
983 if (floor(x) < floor(px) || floor(y) > floor(py))
984 result = true;
985 } else if (floor(ay) > floor(by)) {
986 // line is draw downleft-to-upright
987 if (floor(x) > floor(px) || floor(y) > floor(py))
988 result = true;
989 } else {
990 // line is flat horizontal
991 if (y > ay)
992 result = true;
993 }
994 } else {
995 // line is vertical
996 if (floor(ay) < floor(by)) {
997 if (x < ax) result = true;
998 } else if (floor(ay) > floor(by)) {
999 if (x > ax) result = true;
1000 } else {
1001 if (!(x == ax && y == ay))
1002 result = true;
1003 }
1004 }
1005
1006 return result;
1007}
1008
1016static int pointInLine(double x, double y, double ax, double ay,
1017 double bx, double by)
1018{
1019 if (ax > bx) {
1020 if (ay < by) {
1021 // line is draw upright-to-downleft
1022
1023 // if (x,y) is in on or above the upper right point,
1024 // return -1.
1025 if (y <= ay && x >= ax)
1026 return -1;
1027
1028 // if (x,y) is in on or below the lower left point,
1029 // return 1.
1030 if (y >= by && x <= bx)
1031 return 1;
1032 } else {
1033 // line is draw downright-to-upleft
1034
1035 // If the line is flat, only use the x coordinate.
1036 if (floor(ay) == floor(by)) {
1037 // if (x is to the right of the rightmost point,
1038 // return -1. otherwise if x is to the left of the
1039 // leftmost point, return 1.
1040 if (x >= ax)
1041 return -1;
1042 else if (x <= bx)
1043 return 1;
1044 } else {
1045 // if (x,y) is on or below the lower right point,
1046 // return -1.
1047 if (y >= ay && x >= ax)
1048 return -1;
1049
1050 // if (x,y) is on or above the upper left point,
1051 // return 1.
1052 if (y <= by && x <= bx)
1053 return 1;
1054 }
1055 }
1056 } else {
1057 if (ay < by) {
1058 // line is draw upleft-to-downright
1059
1060 // If (x,y) is on or above the upper left point, return
1061 // -1.
1062 if (y <= ay && x <= ax)
1063 return -1;
1064
1065 // If (x,y) is on or below the lower right point, return
1066 // 1.
1067 if (y >= by && x >= bx)
1068 return 1;
1069 } else {
1070 // line is draw downleft-to-upright
1071
1072 // If the line is flat, only use the x coordinate.
1073 if (floor(ay) == floor(by)) {
1074 if (x <= ax)
1075 return -1;
1076 else if (x >= bx)
1077 return 1;
1078 } else {
1079 // If (x,y) is on or below the lower left point, return
1080 // -1.
1081 if (y >= ay && x <= ax)
1082 return -1;
1083
1084 // If (x,y) is on or above the upper right point, return
1085 // 1.
1086 if (y <= by && x >= bx)
1087 return 1;
1088 }
1089 }
1090 }
1091
1092 // No tests proved that (x,y) was outside [(ax,ay),(bx,by)], so we
1093 // assume it's inside the line's bounds.
1094 return 0;
1095}
1096
1112QPointF QtColorTriangle::movePointToTriangle(double x, double y, const Vertex &a,
1113 const Vertex &b, const Vertex &c) const
1114{
1115 // Let v1A be the vector from (x,y) to a.
1116 // Let v2A be the vector from a to b.
1117 // Find the angle alphaA between v1A and v2A.
1118 double v1xA = x - a.point.x();
1119 double v1yA = y - a.point.y();
1120 double v2xA = b.point.x() - a.point.x();
1121 double v2yA = b.point.y() - a.point.y();
1122 double vpA = vprod(v1xA, v1yA, v2xA, v2yA);
1123 double cosA = vpA / (vlen(v1xA, v1yA) * vlen(v2xA, v2yA));
1124 double alphaA = acos(cosA);
1125
1126 // Let v1B be the vector from x to b.
1127 // Let v2B be the vector from b to c.
1128 double v1xB = x - b.point.x();
1129 double v1yB = y - b.point.y();
1130 double v2xB = c.point.x() - b.point.x();
1131 double v2yB = c.point.y() - b.point.y();
1132 double vpB = vprod(v1xB, v1yB, v2xB, v2yB);
1133 double cosB = vpB / (vlen(v1xB, v1yB) * vlen(v2xB, v2yB));
1134 double alphaB = acos(cosB);
1135
1136 // Let v1C be the vector from x to c.
1137 // Let v2C be the vector from c back to a.
1138 double v1xC = x - c.point.x();
1139 double v1yC = y - c.point.y();
1140 double v2xC = a.point.x() - c.point.x();
1141 double v2yC = a.point.y() - c.point.y();
1142 double vpC = vprod(v1xC, v1yC, v2xC, v2yC);
1143 double cosC = vpC / (vlen(v1xC, v1yC) * vlen(v2xC, v2yC));
1144 double alphaC = acos(cosC);
1145
1146 // Find the radian angles between the (1,0) vector and the points
1147 // A, B, C and (x,y). Use this information to determine which of
1148 // the edges we should project (x,y) onto.
1149 double angleA = angleAt(a.point, contentsRect());
1150 double angleB = angleAt(b.point, contentsRect());
1151 double angleC = angleAt(c.point, contentsRect());
1152 double angleP = angleAt(QPointF(x, y), contentsRect());
1153
1154 // If (x,y) is in the a-b area, project onto the a-b vector.
1155 if (angleBetweenAngles(angleP, angleA, angleB)) {
1156 // Find the distance from (x,y) to a. Then use the slope of
1157 // the a-b vector with this distance and the angle between a-b
1158 // and a-(x,y) to determine the point of intersection of the
1159 // perpendicular projection from (x,y) onto a-b.
1160 double pdist = sqrt(qsqr(x - a.point.x()) + qsqr(y - a.point.y()));
1161
1162 // the length of all edges is always > 0
1163 double p0x = a.point.x() + ((b.point.x() - a.point.x()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
1164 double p0y = a.point.y() + ((b.point.y() - a.point.y()) / vlen(v2xB, v2yB)) * cos(alphaA) * pdist;
1165
1166 // If (x,y) is above the a-b line, which basically means it's
1167 // outside the triangle, then return its projection onto a-b.
1168 if (pointAbovePoint(x, y, p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y())) {
1169 // If the projection is "outside" a, return a. If it is
1170 // outside b, return b. Otherwise return the projection.
1171 int n = pointInLine(p0x, p0y, a.point.x(), a.point.y(), b.point.x(), b.point.y());
1172 if (n < 0)
1173 return a.point;
1174 else if (n > 0)
1175 return b.point;
1176
1177 return QPointF(p0x, p0y);
1178 }
1179 } else if (angleBetweenAngles(angleP, angleB, angleC)) {
1180 // If (x,y) is in the b-c area, project onto the b-c vector.
1181 double pdist = sqrt(qsqr(x - b.point.x()) + qsqr(y - b.point.y()));
1182
1183 // the length of all edges is always > 0
1184 double p0x = b.point.x() + ((c.point.x() - b.point.x()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
1185 double p0y = b.point.y() + ((c.point.y() - b.point.y()) / vlen(v2xC, v2yC)) * cos(alphaB) * pdist;
1186
1187 if (pointAbovePoint(x, y, p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y())) {
1188 int n = pointInLine(p0x, p0y, b.point.x(), b.point.y(), c.point.x(), c.point.y());
1189 if (n < 0)
1190 return b.point;
1191 else if (n > 0)
1192 return c.point;
1193 return QPointF(p0x, p0y);
1194 }
1195 } else if (angleBetweenAngles(angleP, angleC, angleA)) {
1196 // If (x,y) is in the c-a area, project onto the c-a vector.
1197 double pdist = sqrt(qsqr(x - c.point.x()) + qsqr(y - c.point.y()));
1198
1199 // the length of all edges is always > 0
1200 double p0x = c.point.x() + ((a.point.x() - c.point.x()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
1201 double p0y = c.point.y() + ((a.point.y() - c.point.y()) / vlen(v2xA, v2yA)) * cos(alphaC) * pdist;
1202
1203 if (pointAbovePoint(x, y, p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y())) {
1204 int n = pointInLine(p0x, p0y, c.point.x(), c.point.y(), a.point.x(), a.point.y());
1205 if (n < 0)
1206 return c.point;
1207 else if (n > 0)
1208 return a.point;
1209 return QPointF(p0x, p0y);
1210 }
1211 }
1212
1213 // (x,y) is inside the triangle (inside a-b, b-c and a-c).
1214 return QPointF(x, y);
1215}
1216
1231QPointF QtColorTriangle::pointFromColor(const QColor &col) const
1232{
1233 // Simplifications for the corner cases.
1234 if (col == Qt::black)
1235 return pb;
1236 else if (col == Qt::white)
1237 return pc;
1238
1239 // Find the x and y slopes
1240 double ab_deltax = pb.x() - pa.x();
1241 double ab_deltay = pb.y() - pa.y();
1242 double bc_deltax = pc.x() - pb.x();
1243 double bc_deltay = pc.y() - pb.y();
1244 double ac_deltax = pc.x() - pa.x();
1245 double ac_deltay = pc.y() - pa.y();
1246
1247 // Extract the h,s,v values of col.
1248 int hue,sat,val;
1249 col.getHsv(&hue, &sat, &val);
1250
1251 // Find the line that passes through the triangle where the value
1252 // is equal to our color's value.
1253 double p1 = pa.x() + (ab_deltax * (double) (255 - val)) / 255.0;
1254 double q1 = pa.y() + (ab_deltay * (double) (255 - val)) / 255.0;
1255 double p2 = pb.x() + (bc_deltax * (double) val) / 255.0;
1256 double q2 = pb.y() + (bc_deltay * (double) val) / 255.0;
1257
1258 // Find the line that passes through the triangle where the
1259 // saturation is equal to our color's value.
1260 double p3 = pa.x() + (ac_deltax * (double) (255 - sat)) / 255.0;
1261 double q3 = pa.y() + (ac_deltay * (double) (255 - sat)) / 255.0;
1262 double p4 = pb.x();
1263 double q4 = pb.y();
1264
1265 // Find the intersection between these lines.
1266 double x = 0;
1267 double y = 0;
1268 if (p1 != p2) {
1269 double a = (q2 - q1) / (p2 - p1);
1270 double c = (q4 - q3) / (p4 - p3);
1271 double b = q1 - a * p1;
1272 double d = q3 - c * p3;
1273
1274 x = (d - b) / (a - c);
1275 y = a * x + b;
1276 }
1277 else {
1278 x = p1;
1279 y = q3 + (x - p3) * (q4 - q3) / (p4 - p3);
1280 }
1281
1282 return QPointF(x, y);
1283}
1284
1292QColor QtColorTriangle::colorFromPoint(const QPointF &p) const
1293{
1294 // Find the outer radius of the hue gradient.
1295 int outerRadius = (contentsRect().width() - 1) / 2;
1296 if ((contentsRect().height() - 1) / 2 < outerRadius)
1297 outerRadius = (contentsRect().height() - 1) / 2;
1298
1299 // Find the center coordinates
1300 double cx = (double) contentsRect().center().x();
1301 double cy = (double) contentsRect().center().y();
1302
1303 // Find the a, b and c from their angles, the center of the rect
1304 // and the radius of the hue gradient donut.
1305 QPointF pa(cx + (cos(a) * (outerRadius - (outerRadius / 5.0))),
1306 cy - (sin(a) * (outerRadius - (outerRadius / 5.0))));
1307 QPointF pb(cx + (cos(b) * (outerRadius - (outerRadius / 5.0))),
1308 cy - (sin(b) * (outerRadius - (outerRadius / 5.0))));
1309 QPointF pc(cx + (cos(c) * (outerRadius - (outerRadius / 5.0))),
1310 cy - (sin(c) * (outerRadius - (outerRadius / 5.0))));
1311
1312 // Find the hue value from the angle of the 'a' point.
1313 double angle = a - PI/2.0;
1314 if (angle < 0) angle += TWOPI;
1315 double hue = (360.0 * angle) / TWOPI;
1316
1317 // Create the color of the 'a' corner point. We know that b is
1318 // black and c is white.
1319 QColor color;
1320 color.setHsv(360 - (int) floor(hue), 255, 255);
1321
1322 // See also drawTrigon(), which basically does exactly the same to
1323 // determine all colors in the trigon.
1324 Vertex aa(color, pa);
1325 Vertex bb(Qt::black, pb);
1326 Vertex cc(Qt::white, pc);
1327
1328 // Make sure p1 is above p2, which is above p3.
1329 Vertex *p1 = &aa;
1330 Vertex *p2 = &bb;
1331 Vertex *p3 = &cc;
1332 if (p1->point.y() > p2->point.y()) swap(&p1, &p2);
1333 if (p1->point.y() > p3->point.y()) swap(&p1, &p3);
1334 if (p2->point.y() > p3->point.y()) swap(&p2, &p3);
1335
1336 // Find the slopes of all edges in the trigon. All the three y
1337 // deltas here are positive because of the above sorting.
1338 double p1p2ydist = p2->point.y() - p1->point.y();
1339 double p1p3ydist = p3->point.y() - p1->point.y();
1340 double p2p3ydist = p3->point.y() - p2->point.y();
1341 double p1p2xdist = p2->point.x() - p1->point.x();
1342 double p1p3xdist = p3->point.x() - p1->point.x();
1343 double p2p3xdist = p3->point.x() - p2->point.x();
1344
1345 // The first x delta decides wether we have a lefty or a righty
1346 // trigon. A lefty trigon has its tallest edge on the right hand
1347 // side of the trigon. The righty trigon has it on its left side.
1348 // This property determines wether the left or the right set of x
1349 // coordinates will be continuous.
1350 bool lefty = p1p2xdist < 0;
1351
1352 // Find whether the selector's y is in the first or second shorty,
1353 // counting from the top and downwards. This is used to find the
1354 // color at the selector point.
1355 bool firstshorty = (p.y() >= p1->point.y() && p.y() < p2->point.y());
1356
1357 // From the y value of the selector's position, find the left and
1358 // right x values.
1359 double leftx;
1360 double rightx;
1361 if (lefty) {
1362 if (firstshorty) {
1363 leftx = p1->point.x();
1364 if (floor(p1p2ydist) != 0.0) {
1365 leftx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
1366 } else {
1367 leftx = qMin(p1->point.x(), p2->point.x());
1368 }
1369 } else {
1370 leftx = p2->point.x();
1371 if (floor(p2p3ydist) != 0.0) {
1372 leftx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
1373 } else {
1374 leftx = qMin(p2->point.x(), p3->point.x());
1375 }
1376 }
1377
1378 rightx = p1->point.x();
1379 rightx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
1380 } else {
1381 leftx = p1->point.x();
1382 leftx += (p1p3xdist * (p.y() - p1->point.y())) / p1p3ydist;
1383
1384 if (firstshorty) {
1385 rightx = p1->point.x();
1386 if (floor(p1p2ydist) != 0.0) {
1387 rightx += (p1p2xdist * (p.y() - p1->point.y())) / p1p2ydist;
1388 } else {
1389 rightx = qMax(p1->point.x(), p2->point.x());
1390 }
1391 } else {
1392 rightx = p2->point.x();
1393 if (floor(p2p3ydist) != 0.0) {
1394 rightx += (p2p3xdist * (p.y() - p2->point.y())) / p2p3ydist;
1395 } else {
1396 rightx = qMax(p2->point.x(), p3->point.x());
1397 }
1398 }
1399 }
1400
1401 // Find the r,g,b values of the points on the trigon's edges that
1402 // are to the left and right of the selector.
1403 double rshort = 0, gshort = 0, bshort = 0;
1404 double rlong = 0, glong = 0, blong = 0;
1405 if (firstshorty) {
1406 if (floor(p1p2ydist) != 0.0) {
1407 rshort = p2->color.r * (p.y() - p1->point.y()) / p1p2ydist;
1408 gshort = p2->color.g * (p.y() - p1->point.y()) / p1p2ydist;
1409 bshort = p2->color.b * (p.y() - p1->point.y()) / p1p2ydist;
1410 rshort += p1->color.r * (p2->point.y() - p.y()) / p1p2ydist;
1411 gshort += p1->color.g * (p2->point.y() - p.y()) / p1p2ydist;
1412 bshort += p1->color.b * (p2->point.y() - p.y()) / p1p2ydist;
1413 } else {
1414 if (lefty) {
1415 if (p1->point.x() <= p2->point.x()) {
1416 rshort = p1->color.r;
1417 gshort = p1->color.g;
1418 bshort = p1->color.b;
1419 } else {
1420 rshort = p2->color.r;
1421 gshort = p2->color.g;
1422 bshort = p2->color.b;
1423 }
1424 } else {
1425 if (p1->point.x() > p2->point.x()) {
1426 rshort = p1->color.r;
1427 gshort = p1->color.g;
1428 bshort = p1->color.b;
1429 } else {
1430 rshort = p2->color.r;
1431 gshort = p2->color.g;
1432 bshort = p2->color.b;
1433 }
1434 }
1435 }
1436 } else {
1437 if (floor(p2p3ydist) != 0.0) {
1438 rshort = p3->color.r * (p.y() - p2->point.y()) / p2p3ydist;
1439 gshort = p3->color.g * (p.y() - p2->point.y()) / p2p3ydist;
1440 bshort = p3->color.b * (p.y() - p2->point.y()) / p2p3ydist;
1441 rshort += p2->color.r * (p3->point.y() - p.y()) / p2p3ydist;
1442 gshort += p2->color.g * (p3->point.y() - p.y()) / p2p3ydist;
1443 bshort += p2->color.b * (p3->point.y() - p.y()) / p2p3ydist;
1444 } else {
1445 if (lefty) {
1446 if (p2->point.x() <= p3->point.x()) {
1447 rshort = p2->color.r;
1448 gshort = p2->color.g;
1449 bshort = p2->color.b;
1450 } else {
1451 rshort = p3->color.r;
1452 gshort = p3->color.g;
1453 bshort = p3->color.b;
1454 }
1455 } else {
1456 if (p2->point.x() > p3->point.x()) {
1457 rshort = p2->color.r;
1458 gshort = p2->color.g;
1459 bshort = p2->color.b;
1460 } else {
1461 rshort = p3->color.r;
1462 gshort = p3->color.g;
1463 bshort = p3->color.b;
1464 }
1465 }
1466 }
1467 }
1468
1469 // p1p3ydist is never 0
1470 rlong = p3->color.r * (p.y() - p1->point.y()) / p1p3ydist;
1471 glong = p3->color.g * (p.y() - p1->point.y()) / p1p3ydist;
1472 blong = p3->color.b * (p.y() - p1->point.y()) / p1p3ydist;
1473 rlong += p1->color.r * (p3->point.y() - p.y()) / p1p3ydist;
1474 glong += p1->color.g * (p3->point.y() - p.y()) / p1p3ydist;
1475 blong += p1->color.b * (p3->point.y() - p.y()) / p1p3ydist;
1476
1477 // rshort,gshort,bshort is the color on one of the shortys.
1478 // rlong,glong,blong is the color on the longy. So depending on
1479 // wether we have a lefty trigon or not, we can determine which
1480 // colors are on the left and right edge.
1481 double rl, gl, bl, rr, gr, br;
1482 if (lefty) {
1483 rl = rshort; gl = gshort; bl = bshort;
1484 rr = rlong; gr = glong; br = blong;
1485 } else {
1486 rl = rlong; gl = glong; bl = blong;
1487 rr = rshort; gr = gshort; br = bshort;
1488 }
1489
1490 // Find the distance from the left x to the right x (xdist). Then
1491 // find the distances from the selector to each of these (saxdist
1492 // and saxdist2). These distances are used to find the color at
1493 // the selector.
1494 double xdist = rightx - leftx;
1495 double saxdist = p.x() - leftx;
1496 double saxdist2 = xdist - saxdist;
1497
1498 // Now determine the r,g,b values of the selector using a linear
1499 // approximation.
1500 double r, g, b;
1501 if (xdist != 0.0) {
1502 r = (saxdist2 * rl / xdist) + (saxdist * rr / xdist);
1503 g = (saxdist2 * gl / xdist) + (saxdist * gr / xdist);
1504 b = (saxdist2 * bl / xdist) + (saxdist * br / xdist);
1505 } else {
1506 // In theory, the left and right color will be equal here. But
1507 // because of the loss of precision, we get an error on both
1508 // colors. The best approximation we can get is from adding
1509 // the two errors, which in theory will eliminate the error
1510 // but in practise will only minimize it.
1511 r = (rl + rr) / 2;
1512 g = (gl + gr) / 2;
1513 b = (bl + br) / 2;
1514 }
1515
1516 // Now floor the color components and fit them into proper
1517 // boundaries. This again is to compensate for the error caused by
1518 // loss of precision.
1519 int ri = (int) floor(r);
1520 int gi = (int) floor(g);
1521 int bi = (int) floor(b);
1522 if (ri < 0) ri = 0;
1523 else if (ri > 255) ri = 255;
1524 if (gi < 0) gi = 0;
1525 else if (gi > 255) gi = 255;
1526 if (bi < 0) bi = 0;
1527 else if (bi > 255) bi = 255;
1528
1529 // Voila, we have the color at the point of the selector.
1530 return QColor(ri, gi, bi);
1531}
void colorChanged(const QColor &col)
void internalSetNewColor(const QColor &color)
QSize sizeHint() const
void keyPressEvent(QKeyEvent *e)
void drawTrigon(QImage *p, const QPointF &a, const QPointF &b, const QPointF &c, const QColor &color)
QtColorTriangle(QWidget *parent=0)
int heightForWidth(int w) const
void mousePressEvent(QMouseEvent *)
void setColor(const QColor &col)
void mouseReleaseEvent(QMouseEvent *)
void mouseMoveEvent(QMouseEvent *)
void resizeEvent(QResizeEvent *)
void paintEvent(QPaintEvent *)
int alpha() const
void getHsv(int *h, int *s, int *v, int *a) const
void getRgb(int *r, int *g, int *b, int *a) const
void setAlpha(int alpha)
void setHsv(int h, int s, int v, int a)
QImage copy(const QRect &rectangle) const
void fill(uint pixelValue)
QRect rect() const
uchar * scanLine(int i)
int width() const
int key() const
Qt::KeyboardModifiers modifiers() const
Qt::MouseButton button() const
Qt::MouseButtons buttons() const
QPoint pos() const
void drawEllipse(const QRectF &rectangle)
void drawLine(const QLineF &line)
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void setClipRegion(const QRegion &region, Qt::ClipOperation operation)
void setPen(const QColor &color)
void setRenderHint(RenderHint hint, bool on)
void addEllipse(const QRectF &boundingRectangle)
const QRect & rect() const
const QRegion & region() const
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
int x() const
int y() const
qreal x() const
qreal y() const
QPoint center() const
bool intersects(const QRect &rectangle) const
QRegion intersected(const QRegion &r) const
double vprod(double x1, double y1, double x2, double y2)
double vlen(double x, double y)
double qsqr(double a)
const double PI
bool angleBetweenAngles(double p, double a1, double a2)
const double TWOPI
void resize(int size)

Generated by doxygen 1.12.0