[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 
98 const double PI = 3.14159265358979323846264338327950288419717;
99 const double TWOPI = 2.0*PI;
100 
101 /*
102  Used to store color values in the range 0..255 as doubles.
103 */
104 struct 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 */
116 struct 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 
130 static 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 
157 {
158 }
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 
214 void 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);
226  QColor color;
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 
874 double 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 
888 double 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 
907 inline double qsqr(double a)
908 {
909  return a * a;
910 }
911 
916 inline double vlen(double x, double y)
917 {
918  return sqrt(qsqr(x) + qsqr(y));
919 }
920 
925 inline double vprod(double x1, double y1, double x2, double y2)
926 {
927  return x1 * x2 + y1 * y2;
928 }
929 
935 bool 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 
962 static 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 
1016 static 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 
1112 QPointF 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 
1231 QPointF 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 
1292 QColor 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 addEllipse(const QRectF &boundingRectangle)
uchar * scanLine(int i)
Qt::KeyboardModifiers modifiers() const
double qsqr(double a)
void paintEvent(QPaintEvent *)
void setClipPath(const QPainterPath &path, Qt::ClipOperation operation)
void fillRect(const QRectF &rectangle, const QBrush &brush)
void resizeEvent(QResizeEvent *)
void setRenderHint(RenderHint hint, bool on)
const QRegion & region() const
void keyPressEvent(QKeyEvent *e)
bool intersects(const QRect &rectangle) const
double vlen(double x, double y)
void save()
QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags)
void setAlpha(int alpha)
void setClipRegion(const QRegion &region, Qt::ClipOperation operation)
Qt::MouseButtons buttons() const
QColor color() const
void drawLine(const QLineF &line)
QImage copy(const QRect &rectangle) const
void mouseMoveEvent(QMouseEvent *)
void getHsv(int *h, int *s, int *v, int *a) const
int x() const
int y() const
const QRect & rect() const
qreal x() const
qreal y() const
void internalSetNewColor(const QColor &color)
void fill(uint pixelValue)
void drawArc(const QRectF &rectangle, int startAngle, int spanAngle)
int red() const
void setPen(const QColor &color)
int width() const
void drawEllipse(const QRectF &rectangle)
Qt::MouseButton button() const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
QRect rect() const
QRegion intersected(const QRegion &r) const
int heightForWidth(int w) const
QPoint center() const
void colorChanged(const QColor &col)
int alpha() const
const double PI
void setColor(const QColor &col)
int green() const
int key() const
void restore()
void mousePressEvent(QMouseEvent *)
int blue() const
double vprod(double x1, double y1, double x2, double y2)
void getRgb(int *r, int *g, int *b, int *a) const
void mouseReleaseEvent(QMouseEvent *)
void setHsv(int h, int s, int v, int a)
QtColorTriangle(QWidget *parent=0)
QPoint pos() const
QSize sizeHint() const
const double TWOPI
void resize(int size)
bool angleBetweenAngles(double p, double a1, double a2)
void drawTrigon(QImage *p, const QPointF &a, const QPointF &b, const QPointF &c, const QColor &color)

Generated by doxygen 1.8.13