ColorGradient.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. #include "pch.h"
  2. #include <QLayout>
  3. #include <QDoubleValidator>
  4. #include "ColorGradient.h"
  5. #include "util.h"
  6. #define LeftRightGap 10
  7. #define ColorExampleStart 30
  8. #define ColorExmapleHeight 80
  9. #define SliderBeginHeight 83
  10. #define SliderBody 14
  11. #define SliderPeak 6
  12. #define SelectedPreview 20
  13. #define SelectedHeight 115
  14. ColorGradient::ColorGradient(QWidget *parent)
  15. : QWidget(parent)
  16. {
  17. interpolationCombobox = new QComboBox(this);
  18. interpolationCombobox->addItem("RGB");
  19. interpolationCombobox->addItem("HLS");
  20. connect(interpolationCombobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
  21. this, [this](int index) {this->setInterpolationMode((ColorGradient::ColorInterpolationMode)index); });
  22. modeCombobox = new QComboBox(this);
  23. modeCombobox->addItem("Interpolate");
  24. modeCombobox->addItem("Discrete");
  25. connect(modeCombobox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
  26. this, [this](int index) {this->setMode((ColorGradient::ColorMode)index);});
  27. colorButton = new ColorButton(this, QColor(0,0,0));
  28. connect(colorButton, &ColorButton::colorChanged, this, [this](QColor color) {
  29. if (selected != nullptr) setColorPointColor(*selected, color);
  30. });
  31. QVBoxLayout* vBoxLayout = new QVBoxLayout(this);
  32. vBoxLayout->setContentsMargins(0,0,0,0);
  33. QGridLayout* layoutTop = new QGridLayout();
  34. vBoxLayout->addLayout(layoutTop);
  35. layoutTop->setContentsMargins(0, 0, 0, 0);
  36. layoutTop->setAlignment(Qt::AlignTop);
  37. spacerH = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
  38. layoutTop->addWidget(new QLabel("ObjectiveFunction-Gradient:", this), 0, 0);
  39. layoutTop->addItem(spacerH, 0, 1);
  40. layoutTop->addWidget(modeCombobox, 0, 6);
  41. layoutTop->addWidget(interpolationCombobox, 0, 7);
  42. QGridLayout* layoutBottom = new QGridLayout();
  43. vBoxLayout->addLayout(layoutBottom);
  44. layoutBottom->setContentsMargins(0, 0, 0, 0);
  45. layoutBottom->setAlignment(Qt::AlignBottom);
  46. layoutBottom->addItem(new QSpacerItem(10, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 0);
  47. colorLabel = new QLabel("Color:");
  48. layoutBottom->addWidget(colorLabel, 0, 1);
  49. layoutBottom->addWidget(colorButton, 0, 2);
  50. alphaLabel = new QLabel("Alpha:");
  51. layoutBottom->addWidget(alphaLabel, 0, 3);
  52. layoutTop->addWidget(new QLabel("ValueRange:"), 0, 2);
  53. minEdit = new QLineEdit();
  54. minEdit->setMaximumWidth(80);
  55. layoutTop->addWidget(minEdit, 0, 3);
  56. layoutTop->addWidget(new QLabel(":"), 0, 4);
  57. maxEdit = new QLineEdit();
  58. maxEdit->setMaximumWidth(80);
  59. layoutTop->addWidget(maxEdit, 0, 5);
  60. valueLabel = new QLabel("Value:");
  61. layoutBottom->addWidget(valueLabel, 0,5);
  62. QRegExp rxMinMax("[+-]?[0-9]*(\\.[0-9]{0,6})?");
  63. QRegExpValidator* validatorMinMax = new QRegExpValidator(rxMinMax, 0);
  64. minEdit->setValidator(validatorMinMax);
  65. maxEdit->setValidator(validatorMinMax);
  66. minEdit->setText(QString::number(minValue));
  67. maxEdit->setText(QString::number(maxValue));
  68. connect(minEdit, &QLineEdit::editingFinished, this, &ColorGradient::minEditChangeEvent);
  69. connect(maxEdit, &QLineEdit::editingFinished, this, &ColorGradient::maxEditChangeEvent);
  70. alphaLineEdit = new QLineEdit(this);
  71. alphaLineEdit->setMaximumWidth(80);
  72. layoutBottom->addWidget(alphaLineEdit, 0, 4);
  73. valueEdit = new QLineEdit();
  74. valueEdit->setMaximumWidth(80);
  75. valueEdit->setValidator(validatorMinMax);
  76. connect(valueEdit, &QLineEdit::editingFinished, this, &ColorGradient::setValueOfSelected);
  77. layoutBottom->addWidget(valueEdit, 0, 6);
  78. QRegExp rx("0(\\.[0-9]{0,6})?|1(\\.0*)?");
  79. QRegExpValidator* v = new QRegExpValidator(rx, 0);
  80. alphaLineEdit->setValidator(v);
  81. connect(alphaLineEdit, &QLineEdit::editingFinished, this, &ColorGradient::setAlphaOfSelected);
  82. layoutBottom->addItem(new QSpacerItem(10, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 7);
  83. //this->setMinimumHeight(150);
  84. this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
  85. this->setMinimumHeight(150);
  86. //Dummy Data
  87. //TODO(ColorGradient #1) get gradient from last time or save project
  88. pointList.push_back(ColorPoint(0.0, QColor(0, 100, 200)));
  89. pointList.push_back(ColorPoint(0.5, QColor(0, 100, 50)));
  90. pointList.push_back(ColorPoint(1.0, QColor(200, 10, 10)));
  91. //select first
  92. selectColorPoint(&pointList.front());
  93. }
  94. ColorGradient::~ColorGradient()
  95. {
  96. }
  97. QColor ColorGradient::getColorFromAlpha(double alpha)
  98. {
  99. alpha = std::clamp(alpha, 0.0, 1.0);
  100. switch(mode) {
  101. case ColorMode::Discrete: {
  102. QColor result(255,255,255);
  103. for (ColorPoint& point : pointList) {
  104. if (point.alpha >= alpha) {
  105. result = point.color;
  106. break;
  107. }
  108. }
  109. return result;
  110. }
  111. case ColorMode::Interpolate: {
  112. ColorPoint before(0.0, QColor(255, 255, 255));
  113. ColorPoint last(1.0, QColor(255, 255, 255));
  114. ColorPoint* first = &before;
  115. ColorPoint* second = &last;
  116. for (ColorPoint& point : pointList) {
  117. if (point.alpha >= alpha) {
  118. second = &point;
  119. break;
  120. }
  121. first = &point;
  122. }
  123. double alphaBetween = util::inverseLinearInterpolation(first->alpha, second->alpha, alpha);
  124. switch (interpolationMode) {
  125. case ColorInterpolationMode::HSL:
  126. return util::interpolateHSL(first->color, second->color, alphaBetween);
  127. case ColorInterpolationMode::RGB:
  128. default:
  129. return util::interpolateRGB(first->color, second->color, alphaBetween);
  130. }
  131. }
  132. default: {
  133. return QColor(0, 0, 0);
  134. }
  135. }
  136. }
  137. QColor ColorGradient::getColorFromValue(double value)
  138. {
  139. double alpha = util::inverseLinearInterpolation(minValue, maxValue, value);
  140. return getColorFromAlpha(alpha);
  141. }
  142. void ColorGradient::addColorPoint(double alpha)
  143. {
  144. auto iter = pointList.begin();
  145. for (; iter != pointList.end(); iter++) {
  146. if (iter->alpha >= alpha) {
  147. break;
  148. }
  149. }
  150. selectColorPoint(&*pointList.insert(iter, ColorPoint(alpha, getColorFromAlpha(alpha))));
  151. emit gradientChanged();
  152. }
  153. void ColorGradient::removeColorPoint(ColorPoint& pointToRemove)
  154. {
  155. pointList.erase(std::remove_if(pointList.begin(), pointList.end(),
  156. [&pointToRemove](ColorPoint& point) {return &point == &pointToRemove; }));
  157. //colorButton->setVisible(false);
  158. selected = nullptr;
  159. hideSelectionEdit(true);
  160. emit gradientChanged();
  161. }
  162. void ColorGradient::setColorPointAlpha(ColorPoint& point, double alpha)
  163. {
  164. alpha = std::clamp(alpha, 0.0, 1.0);
  165. if (alpha == point.alpha) return;
  166. point.alpha = alpha;
  167. pointList.sort([](ColorPoint& a, ColorPoint& b) {
  168. return a.alpha < b.alpha;
  169. });
  170. if (&point == selected) {
  171. alphaLineEdit->setText(QString::number(alpha));
  172. valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, alpha)));
  173. }
  174. emit gradientChanged();
  175. update();
  176. }
  177. void ColorGradient::setColorPointColor(ColorPoint& point, QColor newColor)
  178. {
  179. if (newColor == point.color) return;
  180. point.color = newColor;
  181. emit gradientChanged();
  182. update();
  183. }
  184. void ColorGradient::setMode(ColorMode mode)
  185. {
  186. this->mode = mode;
  187. update();
  188. emit gradientChanged();
  189. interpolationCombobox->setEnabled(mode != ColorGradient::ColorMode::Discrete);
  190. }
  191. void ColorGradient::setInterpolationMode(ColorInterpolationMode mode)
  192. {
  193. this->interpolationMode = mode;
  194. update();
  195. emit gradientChanged();
  196. }
  197. void ColorGradient::setMinValue(double value)
  198. {
  199. if (value > maxValue) {
  200. minValue = maxValue;
  201. maxValue = value;
  202. minEdit->setText(QString::number(minValue));
  203. maxEdit->setText(QString::number(maxValue));
  204. }
  205. else {
  206. minValue = value;
  207. minEdit->setText(QString::number(minValue));
  208. }
  209. if (selected != nullptr) {
  210. valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha)));
  211. }
  212. emit gradientChanged();
  213. }
  214. void ColorGradient::setMaxValue(double value)
  215. {
  216. if (value < minValue) {
  217. maxValue = minValue;
  218. minValue = value;
  219. minEdit->setText(QString::number(minValue));
  220. maxEdit->setText(QString::number(maxValue));
  221. }
  222. else {
  223. maxValue = value;
  224. maxEdit->setText(QString::number(maxValue));
  225. }
  226. if (selected != nullptr) {
  227. valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha)));
  228. }
  229. emit gradientChanged();
  230. }
  231. void ColorGradient::hideSelectionEdit(bool value)
  232. {
  233. colorButton->setHidden(value);
  234. alphaLineEdit->setHidden(value);
  235. alphaLabel->setHidden(value);
  236. colorLabel->setHidden(value);
  237. valueEdit->setHidden(value);
  238. valueLabel->setHidden(value);
  239. }
  240. void ColorGradient::setAlphaOfSelected()
  241. {
  242. if (selected == nullptr) return;
  243. bool success = false;
  244. double alpha = alphaLineEdit->text().toDouble(&success);
  245. if (success) {
  246. setColorPointAlpha(*selected, alpha);
  247. }
  248. }
  249. void ColorGradient::setValueOfSelected()
  250. {
  251. if (selected == nullptr) return;
  252. bool success = false;
  253. double value = valueEdit->text().toDouble(&success);
  254. if (success) {
  255. if (value < minValue) {
  256. value = minValue;
  257. }
  258. else if (value > maxValue) {
  259. value = maxValue;
  260. }
  261. setColorPointAlpha(*selected, util::inverseLinearInterpolation(minValue, maxValue, value));
  262. }
  263. }
  264. void ColorGradient::selectColorPoint(ColorPoint* point)
  265. {
  266. selected = point;
  267. colorButton->setColor(selected->color);
  268. alphaLineEdit->setText(QString::number(selected->alpha));
  269. valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha)));
  270. hideSelectionEdit(false);
  271. }
  272. void ColorGradient::paintEvent(QPaintEvent* event)
  273. {
  274. QPainter painter(this);
  275. painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
  276. QRect colorExampleRect(QPoint(rect().left() + LeftRightGap, ColorExampleStart), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight));
  277. painter.drawRect(colorExampleRect);
  278. //drawSliders
  279. QPen sliderPen;
  280. sliderPen.setWidth(2);
  281. painter.setPen(Qt::NoPen);
  282. for (ColorPoint& point : pointList) {
  283. QPainterPath slider;
  284. if (&point == selected) {
  285. sliderPen.setColor(QColor(0, 0, 0));
  286. painter.setPen(sliderPen);
  287. }
  288. painter.setBrush(point.color);
  289. int middlePosition = util::linearInterpolate(colorExampleRect.left(), colorExampleRect.right(), point.alpha);
  290. slider.moveTo(middlePosition, SliderBeginHeight);
  291. slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak);
  292. slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak + SliderBody);
  293. slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak + SliderBody);
  294. slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak);
  295. slider.closeSubpath();
  296. painter.drawPath(slider);
  297. if (&point == selected) {
  298. painter.setPen(Qt::NoPen);
  299. }
  300. }
  301. for (int pixelX = colorExampleRect.left() + 1; pixelX < colorExampleRect.right()+1; pixelX++) {
  302. double alpha = util::inverseLinearInterpolation(colorExampleRect.left(), colorExampleRect.right()+1, pixelX);
  303. QColor color = getColorFromAlpha(alpha);
  304. painter.setPen(color);
  305. painter.drawLine(pixelX, colorExampleRect.top() + 1, pixelX, colorExampleRect.bottom());
  306. }
  307. }
  308. void ColorGradient::mousePressEvent(QMouseEvent* event)
  309. {
  310. if (childAt(event->pos()) != nullptr) {
  311. return;
  312. }
  313. QPoint mousePos = event->pos();
  314. QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight+SliderBody+SliderPeak));
  315. QRect responisveArea(QPoint(area.left() - SliderPeak * 2, area.top()), QPoint(area.right() + SliderPeak * 2, area.bottom()));
  316. if (responisveArea.contains(mousePos)) {
  317. // Check for existing color point
  318. // Select with left mouse Button
  319. // Remove Point with right mouse Button
  320. for (ColorPoint& point : pointList) {
  321. double posX = util::linearInterpolate(area.left(), area.right() + 1, point.alpha);
  322. if (posX - SliderPeak * 2 < mousePos.x() && mousePos.x() < posX + SliderPeak * 2) {
  323. if (event->buttons() & Qt::LeftButton) {
  324. selectColorPoint(&point);
  325. }
  326. else if(event->buttons() & Qt::RightButton){
  327. removeColorPoint(point);
  328. }
  329. update();
  330. return;
  331. }
  332. }
  333. // Insert new Color Point
  334. if (event->buttons() & Qt::LeftButton) {
  335. double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x());
  336. addColorPoint(alpha);
  337. update();
  338. }
  339. }
  340. }
  341. void ColorGradient::mouseMoveEvent(QMouseEvent* event)
  342. {
  343. if (selected == nullptr) return;
  344. if (childAt(event->pos()) != nullptr) {
  345. return;
  346. }
  347. //TODO(Bug #1) when mouse press button mouse move gets emitted and move the selected
  348. //currently a workarround to not move the selected colorpoint when selecting a button
  349. //if (event->pos().y() < ColorExmapleHeight) return;
  350. QPoint mousePos = event->pos();
  351. QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight + SliderBody + SliderPeak));
  352. double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x());
  353. this->setColorPointAlpha(*selected, alpha);
  354. }
  355. void ColorGradient::minEditChangeEvent()
  356. {
  357. bool success = false;
  358. double value = minEdit->text().toDouble(&success);
  359. if (success) {
  360. this->setMinValue(value);
  361. }
  362. }
  363. void ColorGradient::maxEditChangeEvent()
  364. {
  365. bool success = false;
  366. double value = maxEdit->text().toDouble(&success);
  367. if (success) {
  368. this->setMaxValue(value);
  369. }
  370. }