#include "pch.h" #include #include #include "ColorGradient.h" #include "util.h" #define LeftRightGap 10 #define ColorExampleStart 30 #define ColorExmapleHeight 80 #define SliderBeginHeight 83 #define SliderBody 14 #define SliderPeak 6 #define SelectedPreview 20 #define SelectedHeight 115 ColorGradient::ColorGradient(QWidget *parent) : QWidget(parent) { interpolationCombobox = new QComboBox(this); interpolationCombobox->addItem("RGB"); interpolationCombobox->addItem("HLS"); connect(interpolationCombobox, static_cast(&QComboBox::currentIndexChanged), this, [this](int index) {this->setInterpolationMode((ColorGradient::ColorInterpolationMode)index); }); modeCombobox = new QComboBox(this); modeCombobox->addItem("Interpolate"); modeCombobox->addItem("Discrete"); connect(modeCombobox, static_cast(&QComboBox::currentIndexChanged), this, [this](int index) {this->setMode((ColorGradient::ColorMode)index);}); colorButton = new ColorButton(this, QColor(0,0,0)); connect(colorButton, &ColorButton::colorChanged, this, [this](QColor color) { if (selected != nullptr) setColorPointColor(*selected, color); }); QVBoxLayout* vBoxLayout = new QVBoxLayout(this); vBoxLayout->setContentsMargins(0,0,0,0); QGridLayout* layoutTop = new QGridLayout(); vBoxLayout->addLayout(layoutTop); layoutTop->setContentsMargins(0, 0, 0, 0); layoutTop->setAlignment(Qt::AlignTop); spacerH = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); layoutTop->addWidget(new QLabel("ObjectiveFunction-Gradient:", this), 0, 0); layoutTop->addItem(spacerH, 0, 1); layoutTop->addWidget(modeCombobox, 0, 6); layoutTop->addWidget(interpolationCombobox, 0, 7); QGridLayout* layoutBottom = new QGridLayout(); vBoxLayout->addLayout(layoutBottom); layoutBottom->setContentsMargins(0, 0, 0, 0); layoutBottom->setAlignment(Qt::AlignBottom); layoutBottom->addItem(new QSpacerItem(10, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 0); colorLabel = new QLabel("Color:"); layoutBottom->addWidget(colorLabel, 0, 1); layoutBottom->addWidget(colorButton, 0, 2); alphaLabel = new QLabel("Alpha:"); layoutBottom->addWidget(alphaLabel, 0, 3); layoutTop->addWidget(new QLabel("ValueRange:"), 0, 2); minEdit = new QLineEdit(); minEdit->setMaximumWidth(80); layoutTop->addWidget(minEdit, 0, 3); layoutTop->addWidget(new QLabel(":"), 0, 4); maxEdit = new QLineEdit(); maxEdit->setMaximumWidth(80); layoutTop->addWidget(maxEdit, 0, 5); valueLabel = new QLabel("Value:"); layoutBottom->addWidget(valueLabel, 0,5); QRegExp rxMinMax("[+-]?[0-9]*(\\.[0-9]{0,6})?"); QRegExpValidator* validatorMinMax = new QRegExpValidator(rxMinMax, 0); minEdit->setValidator(validatorMinMax); maxEdit->setValidator(validatorMinMax); minEdit->setText(QString::number(minValue)); maxEdit->setText(QString::number(maxValue)); connect(minEdit, &QLineEdit::editingFinished, this, &ColorGradient::minEditChangeEvent); connect(maxEdit, &QLineEdit::editingFinished, this, &ColorGradient::maxEditChangeEvent); alphaLineEdit = new QLineEdit(this); alphaLineEdit->setMaximumWidth(80); layoutBottom->addWidget(alphaLineEdit, 0, 4); valueEdit = new QLineEdit(); valueEdit->setMaximumWidth(80); valueEdit->setValidator(validatorMinMax); connect(valueEdit, &QLineEdit::editingFinished, this, &ColorGradient::setValueOfSelected); layoutBottom->addWidget(valueEdit, 0, 6); QRegExp rx("0(\\.[0-9]{0,6})?|1(\\.0*)?"); QRegExpValidator* v = new QRegExpValidator(rx, 0); alphaLineEdit->setValidator(v); connect(alphaLineEdit, &QLineEdit::editingFinished, this, &ColorGradient::setAlphaOfSelected); layoutBottom->addItem(new QSpacerItem(10, 20, QSizePolicy::Expanding, QSizePolicy::Minimum), 0, 7); //this->setMinimumHeight(150); this->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); this->setMinimumHeight(150); //Dummy Data //TODO(ColorGradient #1) get gradient from last time or save project pointList.push_back(ColorPoint(0.0, QColor(0, 100, 200))); pointList.push_back(ColorPoint(0.5, QColor(0, 100, 50))); pointList.push_back(ColorPoint(1.0, QColor(200, 10, 10))); //select first selectColorPoint(&pointList.front()); } ColorGradient::~ColorGradient() { } QColor ColorGradient::getColorFromAlpha(double alpha) { alpha = std::clamp(alpha, 0.0, 1.0); switch(mode) { case ColorMode::Discrete: { QColor result(255,255,255); for (ColorPoint& point : pointList) { if (point.alpha >= alpha) { result = point.color; break; } } return result; } case ColorMode::Interpolate: { ColorPoint before(0.0, QColor(255, 255, 255)); ColorPoint last(1.0, QColor(255, 255, 255)); ColorPoint* first = &before; ColorPoint* second = &last; for (ColorPoint& point : pointList) { if (point.alpha >= alpha) { second = &point; break; } first = &point; } double alphaBetween = util::inverseLinearInterpolation(first->alpha, second->alpha, alpha); switch (interpolationMode) { case ColorInterpolationMode::HSL: return util::interpolateHSL(first->color, second->color, alphaBetween); case ColorInterpolationMode::RGB: default: return util::interpolateRGB(first->color, second->color, alphaBetween); } } default: { return QColor(0, 0, 0); } } } QColor ColorGradient::getColorFromValue(double value) { double alpha = util::inverseLinearInterpolation(minValue, maxValue, value); return getColorFromAlpha(alpha); } void ColorGradient::addColorPoint(double alpha) { auto iter = pointList.begin(); for (; iter != pointList.end(); iter++) { if (iter->alpha >= alpha) { break; } } selectColorPoint(&*pointList.insert(iter, ColorPoint(alpha, getColorFromAlpha(alpha)))); emit gradientChanged(); } void ColorGradient::removeColorPoint(ColorPoint& pointToRemove) { pointList.erase(std::remove_if(pointList.begin(), pointList.end(), [&pointToRemove](ColorPoint& point) {return &point == &pointToRemove; })); //colorButton->setVisible(false); selected = nullptr; hideSelectionEdit(true); emit gradientChanged(); } void ColorGradient::setColorPointAlpha(ColorPoint& point, double alpha) { alpha = std::clamp(alpha, 0.0, 1.0); if (alpha == point.alpha) return; point.alpha = alpha; pointList.sort([](ColorPoint& a, ColorPoint& b) { return a.alpha < b.alpha; }); if (&point == selected) { alphaLineEdit->setText(QString::number(alpha)); valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, alpha))); } emit gradientChanged(); update(); } void ColorGradient::setColorPointColor(ColorPoint& point, QColor newColor) { if (newColor == point.color) return; point.color = newColor; emit gradientChanged(); update(); } void ColorGradient::setMode(ColorMode mode) { this->mode = mode; update(); emit gradientChanged(); interpolationCombobox->setEnabled(mode != ColorGradient::ColorMode::Discrete); } void ColorGradient::setInterpolationMode(ColorInterpolationMode mode) { this->interpolationMode = mode; update(); emit gradientChanged(); } void ColorGradient::setMinValue(double value) { if (value > maxValue) { minValue = maxValue; maxValue = value; minEdit->setText(QString::number(minValue)); maxEdit->setText(QString::number(maxValue)); } else { minValue = value; minEdit->setText(QString::number(minValue)); } if (selected != nullptr) { valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha))); } emit gradientChanged(); } void ColorGradient::setMaxValue(double value) { if (value < minValue) { maxValue = minValue; minValue = value; minEdit->setText(QString::number(minValue)); maxEdit->setText(QString::number(maxValue)); } else { maxValue = value; maxEdit->setText(QString::number(maxValue)); } if (selected != nullptr) { valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha))); } emit gradientChanged(); } void ColorGradient::hideSelectionEdit(bool value) { colorButton->setHidden(value); alphaLineEdit->setHidden(value); alphaLabel->setHidden(value); colorLabel->setHidden(value); valueEdit->setHidden(value); valueLabel->setHidden(value); } void ColorGradient::setAlphaOfSelected() { if (selected == nullptr) return; bool success = false; double alpha = alphaLineEdit->text().toDouble(&success); if (success) { setColorPointAlpha(*selected, alpha); } } void ColorGradient::setValueOfSelected() { if (selected == nullptr) return; bool success = false; double value = valueEdit->text().toDouble(&success); if (success) { if (value < minValue) { value = minValue; } else if (value > maxValue) { value = maxValue; } setColorPointAlpha(*selected, util::inverseLinearInterpolation(minValue, maxValue, value)); } } void ColorGradient::selectColorPoint(ColorPoint* point) { selected = point; colorButton->setColor(selected->color); alphaLineEdit->setText(QString::number(selected->alpha)); valueEdit->setText(QString::number(util::linearInterpolate(minValue, maxValue, selected->alpha))); hideSelectionEdit(false); } void ColorGradient::paintEvent(QPaintEvent* event) { QPainter painter(this); painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing); QRect colorExampleRect(QPoint(rect().left() + LeftRightGap, ColorExampleStart), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight)); painter.drawRect(colorExampleRect); //drawSliders QPen sliderPen; sliderPen.setWidth(2); painter.setPen(Qt::NoPen); for (ColorPoint& point : pointList) { QPainterPath slider; if (&point == selected) { sliderPen.setColor(QColor(0, 0, 0)); painter.setPen(sliderPen); } painter.setBrush(point.color); int middlePosition = util::linearInterpolate(colorExampleRect.left(), colorExampleRect.right(), point.alpha); slider.moveTo(middlePosition, SliderBeginHeight); slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak); slider.lineTo(middlePosition - SliderPeak, SliderBeginHeight + SliderPeak + SliderBody); slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak + SliderBody); slider.lineTo(middlePosition + SliderPeak, SliderBeginHeight + SliderPeak); slider.closeSubpath(); painter.drawPath(slider); if (&point == selected) { painter.setPen(Qt::NoPen); } } for (int pixelX = colorExampleRect.left() + 1; pixelX < colorExampleRect.right()+1; pixelX++) { double alpha = util::inverseLinearInterpolation(colorExampleRect.left(), colorExampleRect.right()+1, pixelX); QColor color = getColorFromAlpha(alpha); painter.setPen(color); painter.drawLine(pixelX, colorExampleRect.top() + 1, pixelX, colorExampleRect.bottom()); } } void ColorGradient::mousePressEvent(QMouseEvent* event) { if (childAt(event->pos()) != nullptr) { return; } QPoint mousePos = event->pos(); QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight+SliderBody+SliderPeak)); QRect responisveArea(QPoint(area.left() - SliderPeak * 2, area.top()), QPoint(area.right() + SliderPeak * 2, area.bottom())); if (responisveArea.contains(mousePos)) { // Check for existing color point // Select with left mouse Button // Remove Point with right mouse Button for (ColorPoint& point : pointList) { double posX = util::linearInterpolate(area.left(), area.right() + 1, point.alpha); if (posX - SliderPeak * 2 < mousePos.x() && mousePos.x() < posX + SliderPeak * 2) { if (event->buttons() & Qt::LeftButton) { selectColorPoint(&point); } else if(event->buttons() & Qt::RightButton){ removeColorPoint(point); } update(); return; } } // Insert new Color Point if (event->buttons() & Qt::LeftButton) { double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x()); addColorPoint(alpha); update(); } } } void ColorGradient::mouseMoveEvent(QMouseEvent* event) { if (selected == nullptr) return; if (childAt(event->pos()) != nullptr) { return; } //TODO(Bug #1) when mouse press button mouse move gets emitted and move the selected //currently a workarround to not move the selected colorpoint when selecting a button //if (event->pos().y() < ColorExmapleHeight) return; QPoint mousePos = event->pos(); QRect area(QPoint(rect().left() + LeftRightGap, ColorExmapleHeight), QPoint(rect().right() - LeftRightGap, ColorExmapleHeight + SliderBody + SliderPeak)); double alpha = util::inverseLinearInterpolation(area.left(), area.right() + 1, mousePos.x()); this->setColorPointAlpha(*selected, alpha); } void ColorGradient::minEditChangeEvent() { bool success = false; double value = minEdit->text().toDouble(&success); if (success) { this->setMinValue(value); } } void ColorGradient::maxEditChangeEvent() { bool success = false; double value = maxEdit->text().toDouble(&success); if (success) { this->setMaxValue(value); } }