234 lines
9.7 KiB
C++
234 lines
9.7 KiB
C++
#pragma once
|
|
#include "../Axis/FrequentAxis.h"
|
|
#include "../RenderAble.h"
|
|
#include "../base/PerformanceShower_p.h"
|
|
#include "../base/Plot_p.h"
|
|
#include "HoverInfo.h"
|
|
#include "Spectrum.h"
|
|
#include "YSGraphic_Core/base/algorithm.hpp"
|
|
|
|
|
|
namespace YSG {
|
|
|
|
struct Marker {
|
|
Marker() = default;
|
|
Marker(double frequent, bool drawLine) : frequent(frequent), drawLine(drawLine) {
|
|
|
|
}
|
|
double frequent{};
|
|
bool drawLine = true;
|
|
bool operator==(const Marker &other) const {
|
|
return qFuzzyCompare(frequent, other.frequent) && drawLine == other.drawLine;
|
|
}
|
|
};
|
|
struct SpectrumTempData : TempData {};
|
|
|
|
struct SpectrumRenderState : RenderState, HoverInfoRenderState, Spectrum_PROP {
|
|
QVector<Marker> markerList;
|
|
int curSelectIndex = -1;
|
|
double minMarkerKey{};
|
|
double minMarkerValue{};
|
|
double maxMarkerKey{};
|
|
double maxMarkerValue{};
|
|
QVector<double> frequents;
|
|
QVector<double> maxPowers, curPowers, minPowers;
|
|
|
|
void unSelect() {
|
|
curSelectIndex = -1;
|
|
}
|
|
void selectNextMarker() {
|
|
if(markerList.empty()) return;
|
|
curSelectIndex = (curSelectIndex + 1) % markerList.size();
|
|
}
|
|
void selectPrevMarker() {
|
|
if(markerList.empty()) return;
|
|
curSelectIndex = (curSelectIndex - 1 + markerList.size()) % markerList.size();
|
|
}
|
|
void setSelectIndex(int index) {
|
|
if (index < 0 || index >= markerList.size()) {
|
|
qDebug() << "SpectrogramLineCache::setSelectIndex index out of range";
|
|
return;
|
|
}
|
|
curSelectIndex = index;
|
|
}
|
|
|
|
void setCurFrequent(double frequent) {
|
|
setFrequent(curSelectIndex, frequent);
|
|
}
|
|
|
|
void setFrequent(int index, double frequent) {
|
|
if (index < 0 || index >= markerList.size()) {
|
|
qDebug() << "SpectrogramLineCache::setSelectIndex index out of range";
|
|
return;
|
|
}
|
|
markerList[index].frequent = frequent;
|
|
}
|
|
|
|
void updateFrequentPointSize(int frequentPointSize, Range frequentRange) {
|
|
if(frequentPointSize == 0) return;
|
|
maxPowers.resize(frequentPointSize);
|
|
curPowers.resize(frequentPointSize);
|
|
minPowers.resize(frequentPointSize);
|
|
frequents.resize(frequentPointSize);
|
|
double cur = frequentRange.lower;
|
|
double step = frequentRange.length() / (frequentPointSize - 1);
|
|
frequents[0] = frequentRange.lower;
|
|
for(int i = 1; i < frequentPointSize; ++i) {
|
|
cur += step;
|
|
frequents[i] = cur;
|
|
}
|
|
maxPowers.fill(frequentRange.lower);
|
|
minPowers.fill(frequentRange.upper);
|
|
}
|
|
|
|
void setFrenquentData(const QVector<double>& lineData) {
|
|
if(lineData.size() != frequentPointSize) {
|
|
qDebug() << "setFrenquentData 数据大小不对 " << lineData.size() << " mFrequentPointSize == " << frequentPointSize;
|
|
return;
|
|
}
|
|
curPowers = lineData;
|
|
curPowers.detach();
|
|
double min = std::numeric_limits<double>::max();
|
|
double max = std::numeric_limits<double>::min();
|
|
int minIndex = 0, maxIndex = 0;
|
|
for (int i = 0; i < frequentPointSize; ++i) {
|
|
double &curData = curPowers[i];
|
|
if (useMaxLine && curData > maxPowers.at(i)) {
|
|
maxPowers[i] = curData;
|
|
}
|
|
if (useMinLine && curData < minPowers.at(i)) {
|
|
minPowers[i] = curData;
|
|
}
|
|
if (useMaxMarker) {
|
|
if (curData > max) {
|
|
max = curData;
|
|
maxIndex = i;
|
|
}
|
|
}
|
|
if (useMinMarker) {
|
|
if (curData < min) {
|
|
min = curData;
|
|
minIndex = i;
|
|
}
|
|
}
|
|
}
|
|
maxMarkerKey = frequents[maxIndex];
|
|
maxMarkerValue = curPowers[maxIndex];
|
|
minMarkerKey = frequents[minIndex];
|
|
minMarkerValue = curPowers[minIndex];
|
|
}
|
|
|
|
[[nodiscard]] double getY(const double &x, bool& ok) {
|
|
SpinLockGuard dd(&dPtr->mBufferLock);
|
|
int i = binary_search(x, frequents, ok);
|
|
if(!ok) return -1;
|
|
const double &x1 = frequents.at(i);
|
|
const double &x2 = frequents.at(i + 1);
|
|
const double &y1 = curPowers.at(i);
|
|
const double &y2 = curPowers.at(i + 1);
|
|
double k = (y2 - y1) / (x2 - x1);
|
|
double b = y1 - k * x1;
|
|
return (int) (k * x + b);
|
|
}
|
|
|
|
void drawLine(QPainter* painter, const QVector<double>& ys, const QPen& pen, const QBrush& brush) {
|
|
if(frequents.empty()) return;
|
|
int n = frequents.size();
|
|
QVector<QPointF> points(n+2);
|
|
double upperBoundPixel = powerAxis->coordToPixel(powerAxis->coordRange().upper, SRC::Render);
|
|
points.first() = QPointF(frequentAxis->coordToPixel(frequents.first(), SRC::Render), upperBoundPixel);
|
|
points.last() = QPointF(frequentAxis->coordToPixel(frequents.last(), SRC::Render), upperBoundPixel);
|
|
for (int i = 0; i < n; ++i) {
|
|
double x = frequentAxis->coordToPixel(frequents.at(i), SRC::Render);
|
|
double y = powerAxis->coordToPixel(ys.at(i), SRC::Render);
|
|
points[i + 1] = QPointF(x, y);
|
|
}
|
|
painter->setPen(pen);
|
|
if(pen.style() != Qt::NoPen) painter->drawPolyline(points.data() + 1, n);
|
|
painter->setBrush(brush);
|
|
if(brush.style() != Qt::NoBrush) painter->drawPolygon(points.data(), n + 2);
|
|
}
|
|
|
|
void draw(QPainter* painter) {
|
|
if(useMaxLine) drawLine(painter, maxPowers, maxPen, maxBrush);
|
|
drawLine(painter, curPowers, curPen, curBrush);
|
|
if(useMinLine) drawLine(painter, minPowers, minPen, minBrush);
|
|
if (useMaxMarker) drawMarker(painter, maxMarkerKey, maxMarkerValue, false, false);
|
|
if (useMinMarker) drawMarker(painter, minMarkerKey, minMarkerValue, false, false);
|
|
for (Marker &customMarker: markerList) {
|
|
bool ok;
|
|
double value = getY(customMarker.frequent, ok);
|
|
if(ok) drawMarker(painter, customMarker.frequent, value, customMarker.drawLine,
|
|
markerList.indexOf(customMarker) == curSelectIndex);
|
|
}
|
|
if (useSweepFrequentRect) {
|
|
drawAxisRect(painter, sweepFrequentRange, middleSweepFrequent);
|
|
}
|
|
}
|
|
|
|
void drawMarker(QPainter *painter, double key, double value, bool isLine = false, bool isSelected = false) {
|
|
double x = frequentAxis->coordToPixel(key, SRC::Render);
|
|
double y = powerAxis->coordToPixel(value, SRC::Render);
|
|
painter->setPen(isSelected ? selectMarkerPen : markerPen);
|
|
QString text1 = QString::number(key, 'f', 2) + frequentAxis->unitText();
|
|
QString text2 = QString::number(value, 'f', 2) + powerAxis->unitText();
|
|
QFontMetrics fm(painter->font());
|
|
int h = fm.height();
|
|
int w = qMax(fm.horizontalAdvance(text1), fm.horizontalAdvance(text2));
|
|
painter->drawText(QPointF(x - (double) w / 2, y - h * 2), text1);
|
|
painter->drawText(QPointF(x - (double) w / 2, y - h), text2);
|
|
if (!isLine) {
|
|
painter->drawEllipse(QPointF(x, y), 2, 2);
|
|
} else {
|
|
painter->drawLine(QPointF(x, powerAxis->y()), QPointF(x, powerAxis->pixelSize()));
|
|
}
|
|
}
|
|
|
|
void drawAxisRect(QPainter *painter, Range range, double middleX) const {
|
|
painter->save();
|
|
painter->setPen(Qt::NoPen);
|
|
painter->setBrush(sweepRectBrush);
|
|
double pixelMin = frequentAxis->coordToPixel(range.lower, SRC::Render);
|
|
double pixelMax = frequentAxis->coordToPixel(range.upper, SRC::Render);
|
|
double pixelMiddle = frequentAxis->coordToPixel(middleX, SRC::Render);
|
|
Range r = powerAxis->coordRange();
|
|
double min = powerAxis->coordToPixel(r.lower, SRC::Render);
|
|
double max = powerAxis->coordToPixel(r.upper, SRC::Render);
|
|
painter->drawRect(QRectF(pixelMin, min, pixelMax - pixelMin, max - min));
|
|
painter->setPen(middleFrequentPen);
|
|
painter->setPen(QPen(QColor(255, 0, 0)));
|
|
painter->drawLine(QPointF(pixelMiddle, min), QPointF(pixelMiddle, max));
|
|
painter->restore();
|
|
}
|
|
};
|
|
|
|
struct SpectrumPrivate : RenderData {
|
|
D_Ptr_3(Spectrum)
|
|
Spectrum* q();
|
|
void draw(QPainter* painter) override;
|
|
void prepareData() override {
|
|
SpinLockGuard guard(&mBufferLock);
|
|
syncStatePipeline();
|
|
auto s = renderState();
|
|
auto sc = editState();
|
|
|
|
}
|
|
|
|
bool selectTest(const QPointF& pos) override {
|
|
auto sc = editState();
|
|
double frequent = sc->frequentAxis->pixelToCoord(pos.x(), SRC::Render);
|
|
bool ok;
|
|
double power = sc->getY(frequent, ok);
|
|
if(!ok) return false;
|
|
return qAbs(power - sc->powerAxis->pixelToCoord(pos.y(), SRC::Render)) < 4;
|
|
}
|
|
void setHoverState(const QPoint& pos, bool active) override {
|
|
SpinLockGuard guard(&mBufferLock);
|
|
RenderEditGuard editGuard(this);
|
|
SpectrumRenderState* sc = editState();
|
|
sc->hoverInfoPos = pos;
|
|
sc->hoverInfoActive = active;
|
|
}
|
|
};
|
|
}
|