[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 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 colorChanged(const QColor &col)
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.9.1