348 lines
9.7 KiB
C++
348 lines
9.7 KiB
C++
#include "imageControl.h"
|
||
#include "RasterLayer.h"
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
|
||
ImageControl::ImageControl(QWidget* parent)
|
||
: QDialog(parent)
|
||
{
|
||
ui.setupUi(this);
|
||
|
||
// Spinbox valueChanged: only sync the paired slider (no render)
|
||
connect(ui.spinRed, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ImageControl::onSpinRedValueChanged);
|
||
connect(ui.spinGreen, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ImageControl::onSpinGreenValueChanged);
|
||
connect(ui.spinBlue, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &ImageControl::onSpinBlueValueChanged);
|
||
|
||
// Spinbox editingFinished: commit on Enter key / focus lost (trigger render)
|
||
connect(ui.spinRed, &QDoubleSpinBox::editingFinished, this, &ImageControl::onSpinRedEditingFinished);
|
||
connect(ui.spinGreen, &QDoubleSpinBox::editingFinished, this, &ImageControl::onSpinGreenEditingFinished);
|
||
connect(ui.spinBlue, &QDoubleSpinBox::editingFinished, this, &ImageControl::onSpinBlueEditingFinished);
|
||
|
||
// Slider valueChanged: only sync the paired spinbox (no render)
|
||
// Slider now represents band index (0 .. N-1)
|
||
connect(ui.sliderRed, &QSlider::valueChanged, this, &ImageControl::onSliderRedValueChanged);
|
||
connect(ui.sliderGreen, &QSlider::valueChanged, this, &ImageControl::onSliderGreenValueChanged);
|
||
connect(ui.sliderBlue, &QSlider::valueChanged, this, &ImageControl::onSliderBlueValueChanged);
|
||
|
||
// Slider sliderReleased: commit on mouse release (trigger render)
|
||
connect(ui.sliderRed, &QSlider::sliderReleased, this, &ImageControl::onSliderRedReleased);
|
||
connect(ui.sliderGreen, &QSlider::sliderReleased, this, &ImageControl::onSliderGreenReleased);
|
||
connect(ui.sliderBlue, &QSlider::sliderReleased, this, &ImageControl::onSliderBlueReleased);
|
||
|
||
// Connect preset buttons
|
||
connect(ui.btnTrueColor, &QPushButton::clicked, this, &ImageControl::onTrueColorClicked);
|
||
connect(ui.btnColorInfrared, &QPushButton::clicked, this, &ImageControl::onColorInfraredClicked);
|
||
|
||
// Spinbox only commits on Enter, not on every keystroke
|
||
ui.spinRed->setKeyboardTracking(false);
|
||
ui.spinGreen->setKeyboardTracking(false);
|
||
ui.spinBlue->setKeyboardTracking(false);
|
||
|
||
ui.groupAdjustments->setStyleSheet(R"(
|
||
QDoubleSpinBox {
|
||
border: 1px solid #999;
|
||
border-radius: 4px;
|
||
padding: 2px 20px 2px 6px; /* <20>Ҳ<EFBFBD><D2B2><EFBFBD><EFBFBD>ռ<EFBFBD><D5BC><EFBFBD><EFBFBD><EFBFBD>ť */
|
||
background: #0e1c4c;
|
||
selection-background-color: #0078d7;
|
||
font-size: 12px;
|
||
color:#ACCDFF ;
|
||
}
|
||
|
||
QDoubleSpinBox::up-button {
|
||
subcontrol-origin: border;
|
||
subcontrol-position: top right;
|
||
width: 16px;
|
||
border-left: 1px solid #ccc;
|
||
}
|
||
|
||
QDoubleSpinBox::down-button {
|
||
subcontrol-origin: border;
|
||
subcontrol-position: bottom right;
|
||
width: 16px;
|
||
border-left: 1px solid #ccc;
|
||
}
|
||
|
||
QDoubleSpinBox::up-arrow {
|
||
image: url(:/svg/resources/icons/svg/arrow_up.svg);
|
||
width: 10px;
|
||
height: 10px;
|
||
}
|
||
|
||
QDoubleSpinBox::down-arrow {
|
||
image: url(:/svg/resources/icons/svg/arrow_down.svg);
|
||
width: 10px;
|
||
height: 10px;
|
||
}
|
||
|
||
QDoubleSpinBox::up-button:hover,
|
||
QDoubleSpinBox::down-button:hover {
|
||
background: #e6f2ff;
|
||
}
|
||
|
||
QDoubleSpinBox::up-button:pressed,
|
||
QDoubleSpinBox::down-button:pressed {
|
||
background: #cce4ff;
|
||
}
|
||
)");
|
||
}
|
||
|
||
ImageControl::~ImageControl()
|
||
{
|
||
}
|
||
|
||
void ImageControl::setActiveLayer(RasterLayer* layer)
|
||
{
|
||
m_activeLayer = layer;
|
||
|
||
if (!layer) {
|
||
setEnabled(false);
|
||
m_wavelengths.clear();
|
||
return;
|
||
}
|
||
setEnabled(true);
|
||
|
||
// Get band wavelengths from the layer's header
|
||
m_wavelengths = layer->bandWavelengths();
|
||
std::sort(m_wavelengths.begin(), m_wavelengths.end());
|
||
|
||
if (m_wavelengths.empty()) {
|
||
setEnabled(false);
|
||
return;
|
||
}
|
||
|
||
m_minWave = m_wavelengths.front();
|
||
m_maxWave = m_wavelengths.back();
|
||
|
||
// Compute spinbox step as the average wavelength interval between adjacent bands
|
||
double step = 1.0;
|
||
if (m_wavelengths.size() >= 2) {
|
||
step = (m_maxWave - m_minWave) / (m_wavelengths.size() - 1);
|
||
}
|
||
|
||
blockAllSignals(true);
|
||
|
||
// Configure spinbox ranges and step
|
||
ui.spinRed->setMinimum(m_minWave);
|
||
ui.spinRed->setMaximum(m_maxWave);
|
||
ui.spinRed->setSingleStep(step);
|
||
ui.spinGreen->setMinimum(m_minWave);
|
||
ui.spinGreen->setMaximum(m_maxWave);
|
||
ui.spinGreen->setSingleStep(step);
|
||
ui.spinBlue->setMinimum(m_minWave);
|
||
ui.spinBlue->setMaximum(m_maxWave);
|
||
ui.spinBlue->setSingleStep(step);
|
||
|
||
// Slider now represents band index (0 .. N-1), step = 1
|
||
int maxIdx = static_cast<int>(m_wavelengths.size()) - 1;
|
||
ui.sliderRed->setMinimum(0);
|
||
ui.sliderRed->setMaximum(maxIdx);
|
||
ui.sliderRed->setSingleStep(1);
|
||
ui.sliderRed->setPageStep(1);
|
||
ui.sliderGreen->setMinimum(0);
|
||
ui.sliderGreen->setMaximum(maxIdx);
|
||
ui.sliderGreen->setSingleStep(1);
|
||
ui.sliderGreen->setPageStep(1);
|
||
ui.sliderBlue->setMinimum(0);
|
||
ui.sliderBlue->setMaximum(maxIdx);
|
||
ui.sliderBlue->setSingleStep(1);
|
||
ui.sliderBlue->setPageStep(1);
|
||
|
||
// Set current values from layer's render params
|
||
auto params = layer->currentRenderParams();
|
||
|
||
int rIdx = nearestBandIndex(params.rWave);
|
||
int gIdx = nearestBandIndex(params.gWave);
|
||
int bIdx = nearestBandIndex(params.bWave);
|
||
|
||
ui.spinRed->setValue(m_wavelengths[rIdx]);
|
||
ui.spinGreen->setValue(m_wavelengths[gIdx]);
|
||
ui.spinBlue->setValue(m_wavelengths[bIdx]);
|
||
|
||
ui.sliderRed->setValue(rIdx);
|
||
ui.sliderGreen->setValue(gIdx);
|
||
ui.sliderBlue->setValue(bIdx);
|
||
|
||
blockAllSignals(false);
|
||
}
|
||
|
||
RasterLayer* ImageControl::activeLayer() const
|
||
{
|
||
return m_activeLayer;
|
||
}
|
||
|
||
int ImageControl::nearestBandIndex(double wave) const
|
||
{
|
||
if (m_wavelengths.empty()) return 0;
|
||
int best = 0;
|
||
double bestDiff = std::abs(m_wavelengths[0] - wave);
|
||
for (int i = 1; i < static_cast<int>(m_wavelengths.size()); ++i) {
|
||
double d = std::abs(m_wavelengths[i] - wave);
|
||
if (d < bestDiff) {
|
||
bestDiff = d;
|
||
best = i;
|
||
}
|
||
}
|
||
return best;
|
||
}
|
||
|
||
void ImageControl::setControlsToBandIndex(QDoubleSpinBox* spin, QSlider* slider, int idx)
|
||
{
|
||
if (idx < 0 || idx >= static_cast<int>(m_wavelengths.size())) return;
|
||
double wv = m_wavelengths[idx];
|
||
spin->blockSignals(true);
|
||
spin->setValue(wv);
|
||
spin->blockSignals(false);
|
||
slider->blockSignals(true);
|
||
slider->setValue(idx);
|
||
slider->blockSignals(false);
|
||
}
|
||
|
||
// --- Spinbox valueChanged: snap to nearest band, sync slider, no render ---
|
||
|
||
void ImageControl::onSpinRedValueChanged(double val)
|
||
{
|
||
int idx = nearestBandIndex(val);
|
||
ui.sliderRed->blockSignals(true);
|
||
ui.sliderRed->setValue(idx);
|
||
ui.sliderRed->blockSignals(false);
|
||
}
|
||
|
||
void ImageControl::onSpinGreenValueChanged(double val)
|
||
{
|
||
int idx = nearestBandIndex(val);
|
||
ui.sliderGreen->blockSignals(true);
|
||
ui.sliderGreen->setValue(idx);
|
||
ui.sliderGreen->blockSignals(false);
|
||
}
|
||
|
||
void ImageControl::onSpinBlueValueChanged(double val)
|
||
{
|
||
int idx = nearestBandIndex(val);
|
||
ui.sliderBlue->blockSignals(true);
|
||
ui.sliderBlue->setValue(idx);
|
||
ui.sliderBlue->blockSignals(false);
|
||
}
|
||
|
||
// --- Slider valueChanged: map band index to wavelength, sync spinbox, no render ---
|
||
|
||
void ImageControl::onSliderRedValueChanged(int val)
|
||
{
|
||
if (val < 0 || val >= static_cast<int>(m_wavelengths.size())) return;
|
||
ui.spinRed->blockSignals(true);
|
||
ui.spinRed->setValue(m_wavelengths[val]);
|
||
ui.spinRed->blockSignals(false);
|
||
}
|
||
|
||
void ImageControl::onSliderGreenValueChanged(int val)
|
||
{
|
||
if (val < 0 || val >= static_cast<int>(m_wavelengths.size())) return;
|
||
ui.spinGreen->blockSignals(true);
|
||
ui.spinGreen->setValue(m_wavelengths[val]);
|
||
ui.spinGreen->blockSignals(false);
|
||
}
|
||
|
||
void ImageControl::onSliderBlueValueChanged(int val)
|
||
{
|
||
if (val < 0 || val >= static_cast<int>(m_wavelengths.size())) return;
|
||
ui.spinBlue->blockSignals(true);
|
||
ui.spinBlue->setValue(m_wavelengths[val]);
|
||
ui.spinBlue->blockSignals(false);
|
||
}
|
||
|
||
// --- Spinbox editingFinished: snap to nearest band wavelength, then commit ---
|
||
|
||
void ImageControl::onSpinRedEditingFinished()
|
||
{
|
||
int idx = nearestBandIndex(ui.spinRed->value());
|
||
setControlsToBandIndex(ui.spinRed, ui.sliderRed, idx);
|
||
emitBandChange();
|
||
}
|
||
|
||
void ImageControl::onSpinGreenEditingFinished()
|
||
{
|
||
int idx = nearestBandIndex(ui.spinGreen->value());
|
||
setControlsToBandIndex(ui.spinGreen, ui.sliderGreen, idx);
|
||
emitBandChange();
|
||
}
|
||
|
||
void ImageControl::onSpinBlueEditingFinished()
|
||
{
|
||
int idx = nearestBandIndex(ui.spinBlue->value());
|
||
setControlsToBandIndex(ui.spinBlue, ui.sliderBlue, idx);
|
||
emitBandChange();
|
||
}
|
||
|
||
// --- Slider sliderReleased: commit on mouse release ---
|
||
|
||
void ImageControl::onSliderRedReleased()
|
||
{
|
||
emitBandChange();
|
||
}
|
||
|
||
void ImageControl::onSliderGreenReleased()
|
||
{
|
||
emitBandChange();
|
||
}
|
||
|
||
void ImageControl::onSliderBlueReleased()
|
||
{
|
||
emitBandChange();
|
||
}
|
||
|
||
// --- Preset buttons ---
|
||
|
||
void ImageControl::onTrueColorClicked()
|
||
{
|
||
blockAllSignals(true);
|
||
int rIdx = nearestBandIndex(665.0);
|
||
int gIdx = nearestBandIndex(560.0);
|
||
int bIdx = nearestBandIndex(490.0);
|
||
setControlsToBandIndex(ui.spinRed, ui.sliderRed, rIdx);
|
||
setControlsToBandIndex(ui.spinGreen, ui.sliderGreen, gIdx);
|
||
setControlsToBandIndex(ui.spinBlue, ui.sliderBlue, bIdx);
|
||
blockAllSignals(false);
|
||
emitBandChange();
|
||
}
|
||
|
||
void ImageControl::onColorInfraredClicked()
|
||
{
|
||
blockAllSignals(true);
|
||
int rIdx = nearestBandIndex(800.0);
|
||
int gIdx = nearestBandIndex(665.0);
|
||
int bIdx = nearestBandIndex(560.0);
|
||
setControlsToBandIndex(ui.spinRed, ui.sliderRed, rIdx);
|
||
setControlsToBandIndex(ui.spinGreen, ui.sliderGreen, gIdx);
|
||
setControlsToBandIndex(ui.spinBlue, ui.sliderBlue, bIdx);
|
||
blockAllSignals(false);
|
||
emitBandChange();
|
||
}
|
||
|
||
void ImageControl::emitBandChange()
|
||
{
|
||
double r = ui.spinRed->value();
|
||
double g = ui.spinGreen->value();
|
||
double b = ui.spinBlue->value();
|
||
|
||
// Update active layer's stored render params
|
||
if (m_activeLayer) {
|
||
auto params = m_activeLayer->currentRenderParams();
|
||
params.rWave = r;
|
||
params.gWave = g;
|
||
params.bWave = b;
|
||
m_activeLayer->setCurrentRenderParams(params);
|
||
}
|
||
|
||
emit bandSelectionChanged(r, g, b);
|
||
}
|
||
|
||
void ImageControl::blockAllSignals(bool block)
|
||
{
|
||
ui.spinRed->blockSignals(block);
|
||
ui.spinGreen->blockSignals(block);
|
||
ui.spinBlue->blockSignals(block);
|
||
ui.sliderRed->blockSignals(block);
|
||
ui.sliderGreen->blockSignals(block);
|
||
ui.sliderBlue->blockSignals(block);
|
||
}
|