Files
Renderive/YSGraphic_Core/plottable/Spectrum_p.h
T
2026-07-23 11:10:17 +08:00

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;
}
};
}