#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 markerList; int curSelectIndex = -1; double minMarkerKey{}; double minMarkerValue{}; double maxMarkerKey{}; double maxMarkerValue{}; QVector frequents; QVector 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& lineData) { if(lineData.size() != frequentPointSize) { qDebug() << "setFrenquentData 数据大小不对 " << lineData.size() << " mFrequentPointSize == " << frequentPointSize; return; } curPowers = lineData; curPowers.detach(); double min = std::numeric_limits::max(); double max = std::numeric_limits::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& ys, const QPen& pen, const QBrush& brush) { if(frequents.empty()) return; int n = frequents.size(); QVector 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); loadCache(); auto s = renderState(); auto sc = renderStateCache(); } bool selectTest(const QPointF& pos) override { auto sc = renderStateCache(); 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; } }; }