From 1ebb49e633b9e2dd5eb10d0b4de4989d03906df9 Mon Sep 17 00:00:00 2001 From: yuntang <123@qq.com> Date: Fri, 9 May 2025 17:28:39 +0800 Subject: [PATCH] 2025-05-09T17:28:38 --- experiment_data/sample_data/~$锡.xlsx | Bin 0 -> 165 bytes src/.idea/.gitignore | 8 + src/.idea/.name | 1 + src/.idea/editor.xml | 344 ++++++++++++++++++++++++++ src/.idea/misc.xml | 7 + src/.idea/modules.xml | 8 + src/.idea/src.iml | 2 + src/.idea/vcs.xml | 6 + src/AnalysisTool.pro | 4 + src/data/pointcalculate.cpp | 30 +-- src/data/pointcalculate.h | 2 - src/global.cpp | 104 ++++++++ src/global.h | 6 +- src/ui/analysisoperationrecorder.cpp | 15 ++ src/ui/analysisoperationrecorder.h | 27 ++ src/ui/centralwidget.cpp | 316 +++++++++++------------ src/ui/centralwidget.h | 15 +- src/ui/coefficientselectionform.cpp | 147 +---------- src/ui/coefficientselectionform.h | 5 - src/ui/itemmanager.cpp | 72 ++++++ src/ui/itemmanager.h | 33 +++ 21 files changed, 795 insertions(+), 357 deletions(-) create mode 100644 experiment_data/sample_data/~$锡.xlsx create mode 100644 src/.idea/.gitignore create mode 100644 src/.idea/.name create mode 100644 src/.idea/editor.xml create mode 100644 src/.idea/misc.xml create mode 100644 src/.idea/modules.xml create mode 100644 src/.idea/src.iml create mode 100644 src/.idea/vcs.xml create mode 100644 src/ui/analysisoperationrecorder.cpp create mode 100644 src/ui/analysisoperationrecorder.h create mode 100644 src/ui/itemmanager.cpp create mode 100644 src/ui/itemmanager.h diff --git a/experiment_data/sample_data/~$锡.xlsx b/experiment_data/sample_data/~$锡.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..d4a5841882f71e1e9259c5c320e2b84832515279 GIT binary patch literal 165 ZcmZQhOiETD9WXN_G9)o1Gbqpn0s!Zc4-)_Y literal 0 HcmV?d00001 diff --git a/src/.idea/.gitignore b/src/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/src/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/src/.idea/.name b/src/.idea/.name new file mode 100644 index 0000000..cf8f44e --- /dev/null +++ b/src/.idea/.name @@ -0,0 +1 @@ +AnalysisTool \ No newline at end of file diff --git a/src/.idea/editor.xml b/src/.idea/editor.xml new file mode 100644 index 0000000..25c6c37 --- /dev/null +++ b/src/.idea/editor.xml @@ -0,0 +1,344 @@ + + + + + \ No newline at end of file diff --git a/src/.idea/misc.xml b/src/.idea/misc.xml new file mode 100644 index 0000000..0b76fe5 --- /dev/null +++ b/src/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/src/.idea/modules.xml b/src/.idea/modules.xml new file mode 100644 index 0000000..f669a0e --- /dev/null +++ b/src/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/.idea/src.iml b/src/.idea/src.iml new file mode 100644 index 0000000..f08604b --- /dev/null +++ b/src/.idea/src.iml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/.idea/vcs.xml b/src/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/src/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/AnalysisTool.pro b/src/AnalysisTool.pro index 97bd6e8..226cfa3 100644 --- a/src/AnalysisTool.pro +++ b/src/AnalysisTool.pro @@ -28,9 +28,11 @@ SOURCES += \ global.cpp \ logger/logger.cpp \ ui/aboutform.cpp \ + ui/analysisoperationrecorder.cpp \ ui/axissettingform.cpp \ ui/coefficientselectionform.cpp \ ui/enthalpydatacorrectionform.cpp \ + ui/itemmanager.cpp \ ui/localcustomplot.cpp \ ui/printpreviewform.cpp \ ui/rightwidget.cpp \ @@ -62,9 +64,11 @@ HEADERS += \ defines.h \ logger/logger.h \ ui/aboutform.h \ + ui/analysisoperationrecorder.h \ ui/axissettingform.h \ ui/coefficientselectionform.h \ ui/enthalpydatacorrectionform.h \ + ui/itemmanager.h \ ui/localcustomplot.h \ ui/printpreviewform.h \ ui/rightwidget.h \ diff --git a/src/data/pointcalculate.cpp b/src/data/pointcalculate.cpp index 72e98bb..cd733ce 100644 --- a/src/data/pointcalculate.cpp +++ b/src/data/pointcalculate.cpp @@ -259,9 +259,9 @@ float PointCalculate::calculateArea() { float startTemp = _leftSelectedPoint.x(); - float c = Global::_enthalpyCoefficientVtr.at(0); + float a = Global::_enthalpyCoefficientVtr.at(0); float b = Global::_enthalpyCoefficientVtr.at(1); - float a = Global::_enthalpyCoefficientVtr.at(2); + float c = Global::_enthalpyCoefficientVtr.at(2); coefficient = a * startTemp * startTemp + b * startTemp + @@ -663,29 +663,3 @@ double PointCalculate::obtainTimeValueBasedOnTemperatureValue(const double sampl return targetValue; } -QPointF PointCalculate::onsetTemperaturePoint(const double x1, const double x2) -{ - const double threshold = 0.1; - QPointF targetPoint; - - QVector pointVtr = getPointVtrInXRange(x1,x2); - for(int i = 3;i < pointVtr.size();i++){ - QPointF prePoint1 = pointVtr.at(i - 1); - QPointF prePoint2 = pointVtr.at(i - 2); - QPointF prePoint3 = pointVtr.at(i - 3); - - QPointF currentPoint = pointVtr.at(i); - - if(std::abs(prePoint1.y() - currentPoint.y()) > threshold){ - targetPoint = pointVtr.at(i - 3); - } - if(std::abs(prePoint2.y() - currentPoint.y()) > threshold){ - targetPoint = pointVtr.at(i - 3); - } - if(std::abs(prePoint3.y() - currentPoint.y()) > threshold){ - targetPoint = pointVtr.at(i - 3); - } - } - - return targetPoint; -} diff --git a/src/data/pointcalculate.h b/src/data/pointcalculate.h index 70de9fa..b646738 100644 --- a/src/data/pointcalculate.h +++ b/src/data/pointcalculate.h @@ -50,8 +50,6 @@ double calculateSlope(double x1, double y1, double x2, double y2) ; QVector findInflectionPoints(const QVector& x, const QVector& y) ; QMap calculateTangentLine(const QVector& x, const QVector& y) ; -QPointF onsetTemperaturePoint(const double x1,const double x2); - //private void updateStartEndPoint(); QPair calculateMaxDiffPointLeft(); diff --git a/src/global.cpp b/src/global.cpp index b736d78..914f7b4 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -1,3 +1,6 @@ +#include +#include + #include "global.h" namespace Global { @@ -20,6 +23,107 @@ QString converDoubleToStr(const double num) return QString::number(num,'f',3); } +void quadraticLeastSquaresFit(double x[], double y[], int n, double coeff[]) +{ + if (n < 3) { + throw std::invalid_argument("At least 3 data points are required for quadratic fitting"); + } + + double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0; + double sum_y = 0, sum_xy = 0, sum_x2y = 0; + + // 计算各项累加和 + for (int i = 0; i < n; ++i) { + double xi = x[i]; + double xi2 = xi * xi; + double xi3 = xi2 * xi; + double xi4 = xi3 * xi; + double yi = y[i]; + + sum_x += xi; + sum_x2 += xi2; + sum_x3 += xi3; + sum_x4 += xi4; + sum_y += yi; + sum_xy += xi * yi; + sum_x2y += xi2 * yi; + } + + // 构建正规方程组的增广矩阵 + double matrix[3][4] = { + {sum_x4, sum_x3, sum_x2, sum_x2y}, // 对应方程: a*Σx⁴ + b*Σx³ + c*Σx² = Σx²y + {sum_x3, sum_x2, sum_x, sum_xy}, // 对应方程: a*Σx³ + b*Σx² + c*Σx = Σxy + {sum_x2, sum_x, static_cast(n), sum_y} // 对应方程: a*Σx² + b*Σx + c*n = Σy + }; + + // 高斯消元法解方程组 + for (int i = 0; i < 3; ++i) { + // 部分主元选择 + int maxRow = i; + for (int k = i + 1; k < 3; ++k) { + if (std::abs(matrix[k][i]) > std::abs(matrix[maxRow][i])) { + maxRow = k; + } + } + + // 交换行 + for (int k = i; k < 4; ++k) { + std::swap(matrix[i][k], matrix[maxRow][k]); + } + + // 消元 + for (int k = i + 1; k < 3; ++k) { + double factor = matrix[k][i] / matrix[i][i]; + for (int j = i; j < 4; ++j) { + matrix[k][j] -= factor * matrix[i][j]; + } + } + } + + // 回代求解 + coeff[2] = matrix[2][3] / matrix[2][2]; // c + coeff[1] = (matrix[1][3] - matrix[1][2] * coeff[2]) / matrix[1][1]; // b + coeff[0] = (matrix[0][3] - matrix[0][2] * coeff[2] - matrix[0][1] * coeff[1]) / matrix[0][0]; // a +} + +// 计算一元一次函数在给定区间内开始变成负值的点 +double findNegativeStartPoint(double m, double b, double start, double end) { + // 处理斜率为零的情况 + if (m == 0) { + // 当斜率为 0 时,函数值为常数 b + if (b < 0) { + return start; // 若 b 为负,函数在区间起始点就为负 + } else { + return std::numeric_limits::infinity(); // 若 b 非负,函数在区间内不会变负 + } + } + + // 计算零点 + double zeroPoint = -b / m; + + // 检查零点是否在给定区间内 + if (zeroPoint >= start && zeroPoint <= end) { + return zeroPoint; // 零点在区间内,函数在此点开始变负 + } + + // 根据斜率正负判断函数单调性,进而判断函数在区间内是否会变负 + if (m > 0) { + // 斜率大于 0,函数单调递增 + if (m * start + b < 0) { + return start; // 起始点函数值为负,函数在起始点就为负 + } + } else { + // 斜率小于 0,函数单调递减 + if (m * end + b < 0) { + return end; // 结束点函数值为负,函数在结束点开始为负 + } + } + + // 函数在区间内不会变负 + return std::numeric_limits::infinity(); +} + + } diff --git a/src/global.h b/src/global.h index 6fa5bed..cff6340 100644 --- a/src/global.h +++ b/src/global.h @@ -8,7 +8,7 @@ #include "protocol.h" namespace Global { -const QString ConSoftVersion = "0.9.7"; +const QString ConSoftVersion = "0.9.7.1"; const QString ExperimentDirPath = QDir::currentPath()+"/../experiment_data"; @@ -109,8 +109,10 @@ extern bool _displayTimeValue; // common func QString converDoubleToStr(const double); -} +void quadraticLeastSquaresFit(double x[], double y[], int n, double coeff[]); +double findNegativeStartPoint(double m, double b, double start, double end); +} #if 0 class Global:public QObject { diff --git a/src/ui/analysisoperationrecorder.cpp b/src/ui/analysisoperationrecorder.cpp new file mode 100644 index 0000000..b14441d --- /dev/null +++ b/src/ui/analysisoperationrecorder.cpp @@ -0,0 +1,15 @@ +#include "analysisoperationrecorder.h" + +namespace AnalysisOperationRecorder { +void addAnalysisOperation(const AnalysisOperation ao) +{ + _ananlysisOperationVtr.push_back(ao); +} + +void removeTheLastAnalysisOperation() +{ + if (!_ananlysisOperationVtr.isEmpty()) { + _ananlysisOperationVtr.removeLast(); + } +} +} diff --git a/src/ui/analysisoperationrecorder.h b/src/ui/analysisoperationrecorder.h new file mode 100644 index 0000000..1f5eed6 --- /dev/null +++ b/src/ui/analysisoperationrecorder.h @@ -0,0 +1,27 @@ +#ifndef ANALYSISOPERATIONRECORDER_H +#define ANALYSISOPERATIONRECORDER_H + +namespace AnalysisOperationRecorder { +enum AnalysisMode{ + Null, + NumericalLabel, + StartPoint, + StopPoint, + PeakSynthesisAnalysis, + GlassTransition, + OnsetTemperaturePoint, + EndsetTemperaturePoint +}; +struct AnalysisOperation{ + AnalysisMode mode; + QPointF point; + QString str; +}; + +QVector _ananlysisOperationVtr; +void addAnalysisOperation(const AnalysisOperation); +void removeTheLastAnalysisOperation(); + +} + +#endif // ANALYSISOPERATIONRECORDER_H diff --git a/src/ui/centralwidget.cpp b/src/ui/centralwidget.cpp index a7cdb88..c1d9fbc 100644 --- a/src/ui/centralwidget.cpp +++ b/src/ui/centralwidget.cpp @@ -5,11 +5,13 @@ #include #include +#include "analysisoperationrecorder.h" #include "centralwidget.h" #include "filemanager.h" #include "pointcalculate.h" #include "logger.h" #include "xlsxhandler.h" +#include "itemmanager.h" CentralWidget::CentralWidget(QWidget *parent) : QWidget(parent) @@ -246,7 +248,7 @@ void CentralWidget::slotRecvAnalysisFileName(const QString &filePath) return; } - qDebug() << "slotRecvAnalysisFileName" << filePath; + // qDebug() << "slotRecvAnalysisFileName:" << filePath; Global::CurveFileData cfd; if(XlsxHandler::readFile(filePath,cfd) != 0){ @@ -304,13 +306,22 @@ void CentralWidget::slotRecvAnalysisFileName(const QString &filePath) void CentralWidget::slotAnalysisSettingApply() { + double x1 = _line1->point1->coords().x(); + double x2 = _line2->point1->coords().x(); + + if(_x1Record == x1 && _x2Record == x2){ + return; + }else{ + _x1Record = x1; + _x2Record = x2; + } + // switch (_analysisMode) { case AnalysisMode::NumericalLabel: { - QPointF selectPoint = PointCalculate::getClosestPointByX( - _line1->point1->coords().x()); + QPointF selectPoint = PointCalculate::getClosestPointByX(x1); - logde<<"lin1 x:"<<_line1->point1->coords().x(); +// logde<<"lin1 x:"<parent(), "warnning", "曲线选择错误."); @@ -324,8 +335,6 @@ void CentralWidget::slotAnalysisSettingApply() } case AnalysisMode::StartPoint: case AnalysisMode::StopPoint:{ - double x1 = _line1->point1->coords().x(); - double x2 = _line2->point1->coords().x(); PointCalculate::setRegionPointX(x1,x2); QPair startEndPointPair = @@ -343,9 +352,6 @@ void CentralWidget::slotAnalysisSettingApply() } case AnalysisMode::PeakSynthesisAnalysis: { - double x1 = _line1->point1->coords().x(); - double x2 = _line2->point1->coords().x(); - fillGraph(x1,x2); PointCalculate::setRegionPointX(x1,x2); @@ -359,14 +365,14 @@ void CentralWidget::slotAnalysisSettingApply() } } } - logde<<"sample weight:"< startEndPointPair = PointCalculate::calculateStartAndEndPoint(); @@ -396,21 +402,22 @@ void CentralWidget::slotAnalysisSettingApply() } case AnalysisMode::GlassTransition: { - // glassTransitionHandle(); - glassTransitionHandle2(); - // + glassTransitionHandle(x1,x2); + break; } case AnalysisMode::OnsetTemperaturePoint:{ - double x1 = _line1->point1->coords().x(); - double x2 = _line2->point1->coords().x(); - - QPointF point = PointCalculate::onsetTemperaturePoint(x1,x2); +#if 0 + QPointF point = OnsetTemperaturePointHandle(x1,x2); QString str = QString::number(point.y(),'f',3); drawText(point,str); +#endif + QPointF point = OnsetTemperaturePointHandle(x1,x2); + QString str = QString::number(point.x(),'f',3); + drawText(point,str); break; } @@ -424,6 +431,8 @@ void CentralWidget::slotAnalysisSettingApply() void CentralWidget::slotAnalysisSettingConfirm() { + ItemManager::confirm(); + slotAnalysisSettingApply(); setAnalysisMode(CentralWidget::AnalysisMode::Null); @@ -437,6 +446,9 @@ void CentralWidget::slotAnalysisSettingConfirm() void CentralWidget::slotAnalysisSettingUndo() { clearData(ClearDataMode::Undo); + + _x1Record = 0.0; + _x2Record = 0.0; } void CentralWidget::slotAnalysisSettingCancel() @@ -547,128 +559,10 @@ void CentralWidget::contextMenuEvent(QContextMenuEvent *event) emit sigContextMenuShow(point); } -void CentralWidget::glassTransitionHandle() +void CentralWidget::glassTransitionHandle(const double x1,const double x2) { - QPointF point1 = PointCalculate::getClosestPointByX(_line1->point1->coords().x()); - QPointF point2 = PointCalculate::getClosestPointByX(_line2->point1->coords().x()); - - logde<<"point1:"<point1->setCoords(point1.x() - 5,point1.y()); - line1->point2->setCoords(point1.x() + 5,point1.y()); - line1->setPen(QPen(Qt::darkRed)); - line1->setSelectable(false); - line1->setVisible(true); - - QCPItemStraightLine *line2 = new QCPItemStraightLine(_customPlot); - line2->point1->setCoords(point2.x() - 5,point2.y()); - line2->point2->setCoords(point2.x() + 5,point2.y()); - line2->setPen(QPen(Qt::darkGreen)); - line2->setSelectable(false); - line2->setVisible(true); - - _customPlot->replot(); -#endif - // - PointCalculate::Line lineStrc1,lineStrc2,tangetLineStrc; - - lineStrc1.slope = 0.0; - lineStrc1.intercept = point1.y(); - - lineStrc2.slope = 0.0; - lineStrc2.intercept = point2.y(); - - QPair tangentLinePair = PointCalculate::getCurveInflectionPointTangent(point1.x(),point2.x()); - tangetLineStrc.slope = tangentLinePair.first; - tangetLineStrc.intercept = tangentLinePair.second; - - QPointF intersection1 = PointCalculate::getIntersection(tangetLineStrc,lineStrc1); - QPointF intersection2 = PointCalculate::getIntersection(tangetLineStrc,lineStrc2); - - // logde<<"intersection1 x:"< xVtr,yVtr; - xVtr.push_back(intersection1.x()); - xVtr.push_back(point1.x()); - - yVtr.push_back(intersection1.y()); - yVtr.push_back(point1.y()); - - QCPGraph *lineGraph1 = _customPlot->addGraph(); - lineGraph1->setData(xVtr, yVtr); - _customPlot->replot(); - - ///line2 - QVector xVtr2,yVtr2; - xVtr2.push_back(intersection2.x()); - xVtr2.push_back(point2.x()); - - yVtr2.push_back(intersection2.y()); - yVtr2.push_back(point2.y()); - - QCPGraph *lineGraph2 = _customPlot->addGraph(); - lineGraph2->setData(xVtr2, yVtr2); - _customPlot->replot(); - - //draw text - drawText(averagePoint,str); - - _customPlot->replot(); - - -#if 0 - // 创建一个圆形绘图项 - QCPItemEllipse *circle = new QCPItemEllipse(_customPlot); - // _customPlot->addItem(circle); - - // 设置圆形的位置和大小 - circle->topLeft->setCoords(-5, 5); - circle->bottomRight->setCoords(5, -5); - - // 设置圆形的填充和边框颜色 - circle->setBrush(QBrush(Qt::cyan)); - circle->setPen(QPen(Qt::blue)); -#endif - -#if 0 - QCPItemStraightLine *line3 = new QCPItemStraightLine(_customPlot); - line3->point1->setCoords(averagePoint.x() + 1,averagePoint.y() + 1); - line3->point2->setCoords(averagePoint.x() - 1,averagePoint.y() - 1); - line3->setPen(QPen(Qt::black)); - line3->setSelectable(false); - line3->setVisible(true); - - _customPlot->replot(); -#endif -} - -void CentralWidget::glassTransitionHandle2() -{ - QPointF point1 = PointCalculate::getClosestPointByX(_line1->point1->coords().x()); - QPointF point2 = PointCalculate::getClosestPointByX(_line2->point1->coords().x()); + QPointF point1 = PointCalculate::getClosestPointByX(x1); + QPointF point2 = PointCalculate::getClosestPointByX(x2); logde<<"point1:"<addGraph(); lineGraph1->setData(xVtr, yVtr); + + ItemManager::addTemporaryQCPGraph(lineGraph1); + QPen pen; pen.setColor(Qt::darkGreen); lineGraph1->setPen(pen); @@ -728,12 +625,14 @@ void CentralWidget::glassTransitionHandle2() lineGraph2->setData(xVtr2, yVtr2); lineGraph2->setPen(pen); + ItemManager::addTemporaryQCPGraph(lineGraph2); + _customPlot->replot(); #endif // inflection point. QVector originalPointVtr = PointCalculate::getPointVtrInXRange(point1.x(),point2.x()); - // QVector pointVtr = PointCalculate::movingAveragePoint(originalPointVtr,3); + QVector pointVtr = originalPointVtr; xVtr.clear(); @@ -748,6 +647,7 @@ void CentralWidget::glassTransitionHandle2() double targetX = 0.0; PointCalculate::Line targetLine; double maxSlopeAbs = 0.0; // 初始化最大斜率绝对值为0 + #if 0 for (auto it = tangentLines.begin(); it != tangentLines.end(); ++it) { if (std::fabs(it.value().slope) > maxSlopeAbs) { @@ -758,6 +658,7 @@ void CentralWidget::glassTransitionHandle2() } } #endif + // new method for (auto it = tangentLines.begin(); it != tangentLines.end(); ++it) { if (std::fabs(it.value().slope) > maxSlopeAbs) { @@ -801,6 +702,7 @@ void CentralWidget::glassTransitionHandle2() xVtr.push_back(point1.x()); yVtr.push_back(point1.y()); #endif + #if 1 xVtr.push_back(intersection1.x()); yVtr.push_back(intersection1.y()); @@ -812,6 +714,8 @@ void CentralWidget::glassTransitionHandle2() QCPGraph *lineGraph3 = _customPlot->addGraph(); lineGraph3->setData(xVtr, yVtr); lineGraph3->setPen(pen); + + ItemManager::addTemporaryQCPGraph(lineGraph3); #endif _customPlot->replot(); @@ -853,6 +757,32 @@ PointCalculate::Line CentralWidget::calculateLinearRegression(const QVector rowPointVtr = PointCalculate::getPointVtrInXRange(x1,x2); + + QVector pointVtr = PointCalculate::movingAveragePoint(rowPointVtr,5); + + QVector xVtr,yVtr; + for (QPointF &point : pointVtr) { + xVtr.push_back(point.x()); + yVtr.push_back(point.y()); + } + + double coeff[3] = {0}; + Global::quadraticLeastSquaresFit(xVtr.data(),yVtr.data(),xVtr.size(),coeff); + + // derivative + double a = coeff[0] * 2; + double b = coeff[1]; + + double targetX = Global::findNegativeStartPoint(a,b,pointVtr.first().x(),pointVtr.last().x()); + logde<<"traget x:"<& points, double& a, double& b, double& c) { int n = points.size(); @@ -956,6 +886,8 @@ void CentralWidget::drawText(const QPointF point, const QString text) textLabel->setSelectedFont(font); textLabel->setSelectable(true); // 设置为可选 + ItemManager::addTemporaryQCPItemText(textLabel); + // 创建指向点的线段(QCPItemLine) QCPItemLine *arrow = new QCPItemLine(_customPlot); arrow->start->setParentAnchor(textLabel->left); // 线段起点绑定到标注文字底部 @@ -963,6 +895,8 @@ void CentralWidget::drawText(const QPointF point, const QString text) arrow->setHead(QCPLineEnding::esSpikeArrow); // 添加箭头 arrow->setPen(QPen(Qt::red, 1)); + ItemManager::addTemporaryQCPItemLine(arrow); + // 重绘图表以显示文本标签和箭头 _customPlot->replot(); } @@ -981,7 +915,6 @@ void CentralWidget::fillGraph(const double x1, const double x2) QCPGraph *mainGraph = _customPlot->addGraph(); mainGraph->setData(xVtr, yVtr); - mainGraph->setPen(QPen(Qt::red, 1)); QVector curveDataVtr = @@ -995,55 +928,102 @@ void CentralWidget::fillGraph(const double x1, const double x2) fillX<setData(fillX, fillY); fillGraph->setBrush(QBrush(Qt::green, Qt::SolidPattern)); fillGraph->setChannelFillGraph(mainGraph); + ItemManager::addTemporaryQCPGraph(mainGraph); + ItemManager::addTemporaryQCPGraph(fillGraph); + _customPlot->replot(); } void CentralWidget::clearData(const CentralWidget::ClearDataMode mode) { if(mode == ClearDataMode::All){ - // Clear the data of graph. - - for (int i = _customPlot->plottableCount() - 1; i >= 0; --i) { - QCPAbstractPlottable* plottable = _customPlot->plottable(i); - if (auto curve = dynamic_cast(plottable)) { - _customPlot->removePlottable(curve); - } - } - // Clear data. Global::_curveExperimentDataVtr.clear(); // Set lines visiable false. _line1->setVisible(false); _line2->setVisible(false); - } -#if 0 - // Clear graph on plot. - for (int i = _customPlot->graphCount() - 1; i >= 0; --i) { - QCPGraph *graph = _customPlot->graph(i); - _customPlot->removeGraph(graph); - } - _currentCurve = nullptr; -#endif + _currentCurve = nullptr; - // Delete items. - QList itemsToKeep; - itemsToKeep << _line1 << _line2; + //ui + // Clear the data of graph. + for (int i = _customPlot->plottableCount() - 1; i >= 0; --i) { + QCPAbstractPlottable* plottable = _customPlot->plottable(i); + if (auto curve = dynamic_cast(plottable)) { + _customPlot->removePlottable(curve); - for (int i = _customPlot->itemCount() - 1; i >= 0; --i) { - QCPAbstractItem *item = _customPlot->item(i); - if (!itemsToKeep.contains(item)) { - _customPlot->removeItem(item); + ItemManager::removeItem(curve); + } + } + + // Clear graph on plot. + for (int i = _customPlot->graphCount() - 1; i >= 0; --i) { + QCPGraph *graph = _customPlot->graph(i); + _customPlot->removeGraph(graph); + + ItemManager::removeItem(graph); + } + + // Delete items. + QList itemsToKeep; + itemsToKeep << _line1 << _line2; + + for (int i = _customPlot->itemCount() - 1; i >= 0; --i) { + QCPAbstractItem *item = _customPlot->item(i); + if (!itemsToKeep.contains(item)) { + _customPlot->removeItem(item); + + ItemManager::removeItem(item); + } + } + }else if(mode == ClearDataMode::Undo){ + // Clear the data of graph. + for (int i = _customPlot->plottableCount() - 1; i >= 0; --i) { + QCPAbstractPlottable* plottable = _customPlot->plottable(i); + if (auto curve = dynamic_cast(plottable)) { + logde<<"clear data,curve object Name:"<objectName().toStdString(); + if(curve->objectName() == ItemManager::TemporaryStr){ + _customPlot->removePlottable(curve); + + ItemManager::removeItem(curve); + } + } + } + + // Clear graph on plot. + for (int i = _customPlot->graphCount() - 1; i >= 0; --i) { + QCPGraph *graph = _customPlot->graph(i); + logde<<"clear data,graph object Name:"<objectName().toStdString(); + if(graph && graph->objectName() == ItemManager::TemporaryStr){ + _customPlot->removeGraph(graph); + + ItemManager::removeItem(graph); + } + } + + // Delete items. + QList itemsToKeep; + itemsToKeep << _line1 << _line2; + + for (int i = _customPlot->itemCount() - 1; i >= 0; --i) { + QCPAbstractItem *item = _customPlot->item(i); + logde<<"item data,graph object Name:"<objectName().toStdString(); + if(item && !itemsToKeep.contains(item) + && item->objectName() == ItemManager::TemporaryStr){ + _customPlot->removeItem(item); + + ItemManager::removeItem(item); + } } } - // 重绘图表以显示更改 _customPlot->replot(); } diff --git a/src/ui/centralwidget.h b/src/ui/centralwidget.h index f90d050..44559ca 100644 --- a/src/ui/centralwidget.h +++ b/src/ui/centralwidget.h @@ -75,13 +75,13 @@ protected: void timerEvent(QTimerEvent* event); void contextMenuEvent(QContextMenuEvent *event); private: - void glassTransitionHandle(); - - void glassTransitionHandle2(); + void glassTransitionHandle(const double x1,const double x2); void quadraticFit(const QVector& points, double& a, double& b, double& c); double derivativeAt(const double a, const double b, const double x); PointCalculate::Line calculateLinearRegression(const QVector& x, const QVector& y); + QPointF OnsetTemperaturePointHandle(const double x1,const double x2); + void setEventHandlerEnable(const bool); QPointF getTheCoordinatesOfTheTextBox(const QPointF point); void drawText(const QPointF,const QString); @@ -92,20 +92,19 @@ private: Undo }; void clearData(const ClearDataMode); + private: AnalysisMode _analysisMode; LocalCustomPlot *_customPlot; - // QCustomPlot*_customPlot; QCPCurve *_currentCurve; - // QVector _curveVtr; - // QCPGraph* _currentGraph; - // QVector _graphVtr; EventHandler* _eventHandler; QCPItemStraightLine *_line1,*_line2; - // QVector _dataVtr; QVector _lineVtr; AxisMode _axisMode; QVector _analysisFilePathVtr; + + double _x1Record; + double _x2Record; }; #endif // CENTRALWIDGET_H diff --git a/src/ui/coefficientselectionform.cpp b/src/ui/coefficientselectionform.cpp index ae44908..9e39a97 100644 --- a/src/ui/coefficientselectionform.cpp +++ b/src/ui/coefficientselectionform.cpp @@ -89,7 +89,7 @@ void CoefficientSelectionForm::on_pushButtonConfirm_clicked() } double coeff[3] = {0}; - quadraticLeastSquaresFit(xVtr.data(),yVtr.data(),xVtr.size(),coeff); + Global::quadraticLeastSquaresFit(xVtr.data(),yVtr.data(),xVtr.size(),coeff); #if 0 std::cout << "Fitted quadratic polynomial: y = " @@ -105,9 +105,9 @@ void CoefficientSelectionForm::on_pushButtonConfirm_clicked() Global::_enthalpyCoefficientEnableFlag = true; Global::_enthalpyCoefficientVtr.clear(); - Global::_enthalpyCoefficientVtr.push_back(coeff[2]); - Global::_enthalpyCoefficientVtr.push_back(coeff[1]); Global::_enthalpyCoefficientVtr.push_back(coeff[0]); + Global::_enthalpyCoefficientVtr.push_back(coeff[1]); + Global::_enthalpyCoefficientVtr.push_back(coeff[2]); } hide(); @@ -128,67 +128,7 @@ void CoefficientSelectionForm::on_pushButtonExit_clicked() hide(); } -void CoefficientSelectionForm::quadraticLeastSquaresFit(double x[], double y[], int n, double coeff[]) { - if (n < 3) { - throw std::invalid_argument("At least 3 data points are required for quadratic fitting"); - } - double sum_x = 0, sum_x2 = 0, sum_x3 = 0, sum_x4 = 0; - double sum_y = 0, sum_xy = 0, sum_x2y = 0; - - // 计算各项累加和 - for (int i = 0; i < n; ++i) { - double xi = x[i]; - double xi2 = xi * xi; - double xi3 = xi2 * xi; - double xi4 = xi3 * xi; - double yi = y[i]; - - sum_x += xi; - sum_x2 += xi2; - sum_x3 += xi3; - sum_x4 += xi4; - sum_y += yi; - sum_xy += xi * yi; - sum_x2y += xi2 * yi; - } - - // 构建正规方程组的增广矩阵 - double matrix[3][4] = { - {sum_x4, sum_x3, sum_x2, sum_x2y}, // 对应方程: a*Σx⁴ + b*Σx³ + c*Σx² = Σx²y - {sum_x3, sum_x2, sum_x, sum_xy}, // 对应方程: a*Σx³ + b*Σx² + c*Σx = Σxy - {sum_x2, sum_x, static_cast(n), sum_y} // 对应方程: a*Σx² + b*Σx + c*n = Σy - }; - - // 高斯消元法解方程组 - for (int i = 0; i < 3; ++i) { - // 部分主元选择 - int maxRow = i; - for (int k = i + 1; k < 3; ++k) { - if (std::abs(matrix[k][i]) > std::abs(matrix[maxRow][i])) { - maxRow = k; - } - } - - // 交换行 - for (int k = i; k < 4; ++k) { - std::swap(matrix[i][k], matrix[maxRow][k]); - } - - // 消元 - for (int k = i + 1; k < 3; ++k) { - double factor = matrix[k][i] / matrix[i][i]; - for (int j = i; j < 4; ++j) { - matrix[k][j] -= factor * matrix[i][j]; - } - } - } - - // 回代求解 - coeff[2] = matrix[2][3] / matrix[2][2]; // c - coeff[1] = (matrix[1][3] - matrix[1][2] * coeff[2]) / matrix[1][1]; // b - coeff[0] = (matrix[0][3] - matrix[0][2] * coeff[2] - matrix[0][1] * coeff[1]) / matrix[0][0]; // a -} void CoefficientSelectionForm::on_pushButtonSelectFile_clicked() { QString filePath = QFileDialog::getOpenFileName( @@ -256,84 +196,3 @@ void CoefficientSelectionForm::on_radioButtonMultiPointCoefficient_toggled(bool } #endif } - -void CoefficientSelectionForm::cubicLeastSquaresFit( - double x[], double y[], int n, double coeff[]) -{ - int i, j, k; - double atemp[3] = {0}; // 存储x^0, x^1, x^2的和 - double b[3] = {0}; // 右侧向量 - - // 计算各次幂的和 - for (i = 0; i < n; i++) { - double xi = x[i]; - double xi_pow2 = xi * xi; // x^2 - - // 累加atemp[0]到atemp[2] - atemp[0] += 1; // x^0的和等于数据点个数 - atemp[1] += xi; - atemp[2] += xi_pow2; - - // 计算右侧向量b - double yi = y[i]; - b[0] += yi; - b[1] += yi * xi; - b[2] += yi * xi_pow2; - } - - // 构建法方程矩阵 - double a[3][3]; - a[0][0] = atemp[0]; - a[0][1] = atemp[1]; - a[0][2] = atemp[2]; - a[1][0] = atemp[1]; - a[1][1] = atemp[2]; - a[1][2] = 0; - a[2][0] = atemp[2]; - a[2][1] = 0; - a[2][2] = 0; - - // 高斯列主元消元法 - for (k = 0; k < 2; k++) { // 消去第k列 - // 寻找主元行 - int max_row = k; - double max_val = fabs(a[k][k]); - for (i = k + 1; i < 3; i++) { - if (fabs(a[i][k]) > max_val) { - max_val = fabs(a[i][k]); - max_row = i; - } - } - - // 交换行 - if (max_row != k) { - for (j = k; j < 3; j++) { - double temp = a[k][j]; - a[k][j] = a[max_row][j]; - a[max_row][j] = temp; - } - double temp_b = b[k]; - b[k] = b[max_row]; - b[max_row] = temp_b; - } - - // 消元 - for (i = k + 1; i < 3; i++) { - double factor = a[i][k] / a[k][k]; - for (j = k; j < 3; j++) { - a[i][j] -= factor * a[k][j]; - } - b[i] -= factor * b[k]; - } - } - - // 回代求解 - coeff[2] = b[2] / a[2][2]; - for (i = 1; i >= 0; i--) { - double sum = 0.0; - for (j = i + 1; j < 3; j++) { - sum += a[i][j] * coeff[j]; - } - coeff[i] = (b[i] - sum) / a[i][i]; - } -} diff --git a/src/ui/coefficientselectionform.h b/src/ui/coefficientselectionform.h index a56dad1..604091f 100644 --- a/src/ui/coefficientselectionform.h +++ b/src/ui/coefficientselectionform.h @@ -26,11 +26,6 @@ private slots: void on_pushButtonConfirm_clicked(); void on_pushButtonExit_clicked(); private: - // 立方拟合 - void cubicLeastSquaresFit(double x[], double y[], int n, double coeff[4]); - // 平方拟合 - void quadraticLeastSquaresFit(double x[], double y[], int n, double coeff[]); - QString _jsonStr; Ui::CoefficientSelectionForm *ui; float _instrumentCoefficient; diff --git a/src/ui/itemmanager.cpp b/src/ui/itemmanager.cpp new file mode 100644 index 0000000..bd17257 --- /dev/null +++ b/src/ui/itemmanager.cpp @@ -0,0 +1,72 @@ +#include "itemmanager.h" + +namespace ItemManager { +QVector _QCPItemTextVtr; +QVector _QCPItemLineVtr; +QVector _QCPGraphVtr; + +void addTemporaryQCPItemText(QCPItemText *item) +{ + item->setObjectName(TemporaryStr); + _QCPItemTextVtr.push_back(item); +} + +void addTemporaryQCPItemLine(QCPItemLine *item) +{ + item->setObjectName(TemporaryStr); + _QCPItemLineVtr.push_back(item); +} + +void confirm() +{ + for(QCPItemText *item:_QCPItemTextVtr){ + item->setObjectName(ConfirmStr); + } + + for(QCPItemLine *item:_QCPItemLineVtr){ + item->setObjectName(ConfirmStr); + } + + for(QCPGraph *graph:_QCPGraphVtr){ + graph->setObjectName(ConfirmStr); + } +} + +void addTemporaryQCPGraph(QCPGraph * graph) +{ + graph->setObjectName(TemporaryStr); + _QCPGraphVtr.push_back(graph); +} + +void removeItem(QObject *obj) +{ + for (int i = _QCPItemTextVtr.size() - 1; i >= 0; --i) { + QCPItemText *item = _QCPItemTextVtr[i]; + if (item == obj) { + _QCPItemTextVtr.remove(i); + return; + } + } + + for (int i = _QCPItemLineVtr.size() - 1; i >= 0; --i) { + QCPItemLine *item = _QCPItemLineVtr[i]; + if (item == obj) { + _QCPItemLineVtr.remove(i); + return; + } + } + + for (int i = _QCPGraphVtr.size() - 1; i >= 0; --i) { + QCPGraph *item = _QCPGraphVtr[i]; + if (item == obj) { + _QCPGraphVtr.remove(i); + return; + } + } + + +} + + + +} diff --git a/src/ui/itemmanager.h b/src/ui/itemmanager.h new file mode 100644 index 0000000..986bb64 --- /dev/null +++ b/src/ui/itemmanager.h @@ -0,0 +1,33 @@ +#ifndef ITEMMANAGER_H +#define ITEMMANAGER_H + +#include "qcustomplot.h" + +namespace ItemManager { +const QString OriginStr("origin"); +const QString ConfirmStr("confirm"); +const QString TemporaryStr("temporary"); + +#if 0 +enum ItemAttribute{ + Origin, + Last, + Temporary +}; +#endif + +extern QVector _QCPItemTextVtr; +extern QVector _QCPItemLineVtr; +extern QVector _QCPGraphVtr; + +void addTemporaryQCPItemText(QCPItemText*); +void addTemporaryQCPItemLine(QCPItemLine*); +void addTemporaryQCPGraph(QCPGraph*); + +void removeItem(QObject* obj); + +void confirm(); + +} + +#endif // ITEMMANAGER_H