diff --git a/experiment_data/analysis_state/~$peak.xlsx b/experiment_data/analysis_state/~$peak.xlsx deleted file mode 100644 index d4a5841..0000000 Binary files a/experiment_data/analysis_state/~$peak.xlsx and /dev/null differ diff --git a/experiment_data/analysis_state/玻璃化转变.xlsx b/experiment_data/analysis_state/玻璃化转变.xlsx new file mode 100644 index 0000000..8ad172a Binary files /dev/null and b/experiment_data/analysis_state/玻璃化转变.xlsx differ diff --git a/src/data/pointcalculate.cpp b/src/data/pointcalculate.cpp index 8a36033..62d30ca 100644 --- a/src/data/pointcalculate.cpp +++ b/src/data/pointcalculate.cpp @@ -363,7 +363,7 @@ QPair PointCalculate::getCurveInflectionPointTangent(const float x1 dataVtr.push_back(ed.sampleTemp); } } - // + // 5 std::vector processedVtr = movingAverage(dataVtr,5); // find max slope. @@ -387,7 +387,7 @@ QPair PointCalculate::getCurveInflectionPointTangent(const float x1 return qMakePair(maxSlope,b); } -QPointF PointCalculate::getIntersectionBySlope(const LineStruct& line1, const LineStruct& line2) { +QPointF PointCalculate::getIntersection(const Line& line1, const Line& line2) { float x = (line2.intercept - line1.intercept) / (line1.slope - line2.slope); float y = line1.slope * x + line1.intercept; // return {x, y}; @@ -541,3 +541,81 @@ QVector PointCalculate::getDataInXRange(const float x1, return targetVtr; } + +QVector PointCalculate::getNearbyPointGroupByX(const float targetX) +{ + const int conCount = 10; + QVector tmpPointVtr,targetPointVtr; + float minDiff = std::numeric_limits::max(); + + for(Global::ExperimentData &ed:_dataVtr){ + float diff = std::abs(ed.sampleTemp - targetX); + if(diff > 10){ + continue; + } + + tmpPointVtr.push_back({ed.sampleTemp,ed.dsc}); + + if (diff < minDiff) { + minDiff = diff; + + targetPointVtr = QVector(tmpPointVtr.end() - conCount, tmpPointVtr.end()); + } + } + + + return targetPointVtr; +} + +// 计算两点之间的斜率 +double PointCalculate::calculateSlope(double x1, double y1, double x2, double y2) { + return (y2 - y1) / (x2 - x1); +} + +// 寻找第一个拐点 +QVector PointCalculate::findInflectionPoints( + const QVector& x, const QVector& y) { + QVector inflectionPointsX; + for (int i = 2; i < x.size() - 2; ++i) { + double d1 = calculateSlope(x[i - 2], y[i - 2], x[i - 1], y[i - 1]); + double d2 = calculateSlope(x[i - 1], y[i - 1], x[i], y[i]); + double d3 = calculateSlope(x[i], y[i], x[i + 1], y[i + 1]); + double d4 = calculateSlope(x[i + 1], y[i + 1], x[i + 2], y[i + 2]); + + // 检查二阶导数的符号变化 + if (((d2 - d1) * (d3 - d2) < 0) && ((d3 - d2) * (d4 - d3) < 0)) { + inflectionPointsX.append(x[i]); + } + } + return inflectionPointsX; +} + +// 计算切线方程 +QMap PointCalculate::calculateTangentLine( + const QVector& x, const QVector& y) { + QMap tangentLines; + QVector inflectionPointsX = findInflectionPoints(x, y); + + for (double xInflection : inflectionPointsX) { + int i = std::distance(x.begin(), std::find(x.begin(), x.end(), xInflection)); + double slope = calculateSlope(x[i - 1], y[i - 1], x[i + 1], y[i + 1]); + double yInflection = y[i]; + double intercept = yInflection - slope * xInflection; + tangentLines[xInflection] = {slope, intercept}; + } + + return tangentLines; +} +QVector PointCalculate::getPointVtrInXRange(const float x1, const float x2) +{ + QVector targetVtr; + for(const Global::ExperimentData &ed : _dataVtr) { + if(x1 < ed.sampleTemp && ed.sampleTemp < x2){ + targetVtr.push_back({ed.sampleTemp,ed.dsc}); + }else if (ed.sampleTemp > x2){ + break; + } + } + + return targetVtr; +} diff --git a/src/data/pointcalculate.h b/src/data/pointcalculate.h index ce4750b..7a08b0e 100644 --- a/src/data/pointcalculate.h +++ b/src/data/pointcalculate.h @@ -10,9 +10,13 @@ void setExperimentData(const QVector&); QPair getStartAndEndPoint(); QVector getDataInXRange(const float, const float); +QVector getPointVtrInXRange(const float, const float); void setRegionPointX(const float,const float); + QPointF getClosestPointByX(const float); +QVector getNearbyPointGroupByX(const float); + QPointF getClosestPointByY(const float left,const float right,const float valueY); QPointF getPeakPoint(); QPair getMaxMinValue(); @@ -32,11 +36,14 @@ QString textFormatGlassTranstion(const float t1,const float tg,const float t2); // glass transition QPair getCurveInflectionPointTangent(const float,const float); -struct LineStruct { - float slope; // - float intercept; // +struct Line { + double slope; // + double intercept; // }; -QPointF getIntersectionBySlope(const LineStruct& line1, const LineStruct& line2); +QPointF getIntersection(const Line& line1, const Line& line2); +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) ; //private void updateStartEndPoint(); diff --git a/src/ui/centralwidget.cpp b/src/ui/centralwidget.cpp index a120b4d..32694c3 100644 --- a/src/ui/centralwidget.cpp +++ b/src/ui/centralwidget.cpp @@ -352,7 +352,8 @@ void CentralWidget::slotAnalysisSettingApply() } case AnalysisMode::GlassTransition: { - glassTransitionHandle(); + // glassTransitionHandle(); + glassTransitionHandle2(); // break; } @@ -452,7 +453,7 @@ void CentralWidget::glassTransitionHandle() _customPlot->replot(); #endif // - PointCalculate::LineStruct lineStrc1,lineStrc2,tangetLineStrc; + PointCalculate::Line lineStrc1,lineStrc2,tangetLineStrc; lineStrc1.slope = 0.0; lineStrc1.intercept = point1.y(); @@ -464,8 +465,8 @@ void CentralWidget::glassTransitionHandle() tangetLineStrc.slope = tangentLinePair.first; tangetLineStrc.intercept = tangentLinePair.second; - QPointF intersection1 = PointCalculate::getIntersectionBySlope(tangetLineStrc,lineStrc1); - QPointF intersection2 = PointCalculate::getIntersectionBySlope(tangetLineStrc,lineStrc2); + 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); @@ -515,6 +545,192 @@ void CentralWidget::glassTransitionHandle() #endif } +void CentralWidget::glassTransitionHandle2() +{ + QPointF point1 = PointCalculate::getClosestPointByX(_line1->point1->coords().x()); + QPointF point2 = PointCalculate::getClosestPointByX(_line2->point1->coords().x()); + + logde<<"point1:"< point1Vtr = PointCalculate::getNearbyPointGroupByX(point1.x()); + QVector point2Vtr = PointCalculate::getNearbyPointGroupByX(point2.x()); + + QVector xVtr,yVtr; + for(QPointF &p:point1Vtr){ + xVtr.append(p.x()); + yVtr.append(p.y()); + } + + PointCalculate::Line line1 = calculateLinearRegression(xVtr,yVtr); + +#if 1 + ///line1 + // QVector xVtr,yVtr; + xVtr.clear(); + yVtr.clear(); + + xVtr.push_back(point1.x()); + yVtr.push_back(point1.y()); + + xVtr.push_back(point1.x() + 10); + double y1 = line1.slope * (point1.x() + 10) + line1.intercept; + yVtr.push_back(y1); + + QCPGraph *lineGraph1 = _customPlot->addGraph(); + lineGraph1->setData(xVtr, yVtr); + QPen pen; + pen.setColor(Qt::darkGreen); + lineGraph1->setPen(pen); + +#endif + + xVtr.clear(); + yVtr.clear(); + for(QPointF &p:point2Vtr){ + xVtr.append(p.x()); + yVtr.append(p.y()); + } + + PointCalculate::Line line2 = calculateLinearRegression(xVtr,yVtr); + +#if 1 + ///line2 + QVector xVtr2,yVtr2; + xVtr2.push_back(point2.x()); + yVtr2.push_back(point2.y()); + + xVtr2.push_back(point2.x() - 10); + double y2 = line2.slope * (point2.x() - 10) + line2.intercept; + yVtr2.push_back(y2); + + QCPGraph *lineGraph2 = _customPlot->addGraph(); + lineGraph2->setData(xVtr2, yVtr2); + lineGraph2->setPen(pen); + + _customPlot->replot(); +#endif + + // inflection point. + QVector pointVtr = PointCalculate::getPointVtrInXRange(point1.x(),point2.x()); + + xVtr.clear(); + yVtr.clear(); + for(QPointF& p:pointVtr){ + xVtr.append(p.x()); + yVtr.append(p.y()); + } + QMap tangentLines = + PointCalculate::calculateTangentLine(xVtr,yVtr); + + double targetX = 0.0; + PointCalculate::Line targetLine; + double maxSlopeAbs = 0.0; // 初始化最大斜率绝对值为0 + for (auto it = tangentLines.begin(); it != tangentLines.end(); ++it) { + if (std::fabs(it.value().slope) > maxSlopeAbs) { + maxSlopeAbs = std::fabs(it.value().slope); // 更新最大斜率绝对值 + + targetX = it.key(); + targetLine = it.value(); + } + } + +#if 1 + QPointF intersection1 = PointCalculate::getIntersection(targetLine,line1); + QPointF intersection2 = PointCalculate::getIntersection(targetLine,line2); + + // graph 3 + xVtr.clear(); + yVtr.clear(); + +#if 0 + xVtr.push_back(point1.x()); + yVtr.push_back(point1.y()); + + xVtr.push_back(point1.x()); + yVtr.push_back(point1.y()); +#endif +#if 1 + xVtr.push_back(intersection1.x()); + yVtr.push_back(intersection1.y()); + + xVtr.push_back(intersection2.x()); + yVtr.push_back(intersection2.y()); +#endif + + QCPGraph *lineGraph3 = _customPlot->addGraph(); + lineGraph3->setData(xVtr, yVtr); + lineGraph3->setPen(pen); +#endif + + _customPlot->replot(); + + // point value + double averageY = (point1.y() + point2.y()) / 2; + + QPointF averagePoint = PointCalculate::getClosestPointByY(point1.x(),point2.x(),averageY); + + + QString str = PointCalculate::textFormatGlassTranstion(intersection2.x(), + averagePoint.x(), + intersection1.x()); + + drawText(averagePoint,str); + +} +// 使用最小二乘法计算线性回归 +PointCalculate::Line CentralWidget::calculateLinearRegression(const QVector& x, const QVector& y) { + PointCalculate::Line line; + double sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0; + size_t n = x.size(); + + // 计算所需的总和 + for (size_t i = 0; i < n; ++i) { + sum_x += x[i]; + sum_y += y[i]; + sum_xy += x[i] * y[i]; + sum_xx += x[i] * x[i]; + } + + // 计算斜率和截距 + double x_mean = sum_x / n; + double y_mean = sum_y / n; + double numerator = n * sum_xy - sum_x * sum_y; + double denominator = n * sum_xx - sum_x * sum_x; + line.slope = numerator / denominator; + line.intercept = y_mean - line.slope * x_mean; + + return line; +} + +// 二次多项式拟合函数 +void CentralWidget::quadraticFit(const QVector& points, double& a, double& b, double& c) { + int n = points.size(); + if (n < 3) { + throw std::invalid_argument("At least three points are required for quadratic fit."); + } + + double sumX = 0, sumY = 0, sumXX = 0, sumXY = 0, sumX2Y = 0; + for (const QPointF& p : points) { + sumX += p.x(); + sumY += p.y(); + sumXX += p.x() * p.x(); + sumXY += p.x() * p.y(); + sumX2Y += p.x() * p.x() * p.y(); + } + + double det = n * (sumXX * sumY - sumX * sumXY) - sumX * sumX * sumY + sumX * sumX2Y; + a = (sumXX * sumY - sumX * sumXY) / det; + b = (sumX2Y - n * sumXY) / det; + c = (sumY - a * sumX - b * sumX) / n; +} + +// 计算二次多项式拟合曲线的导数(切线斜率) +double CentralWidget::derivativeAt(const double a, const double b, const double x) { + return 2 * a * x + b; +} + + void CentralWidget::setEventHandlerEnable(const bool flag) { logde<<"setEventHandlerEnable..."<graphCount() - 1; i >= 0; --i) { QCPGraph *graph = _customPlot->graph(i); - _customPlot->removeGraph(graph); + _customPlot->removeGraph(graph); } #endif diff --git a/src/ui/centralwidget.h b/src/ui/centralwidget.h index 5cc2e43..fabf77f 100644 --- a/src/ui/centralwidget.h +++ b/src/ui/centralwidget.h @@ -9,6 +9,7 @@ #include "global.h" #include "eventhandler.h" #include "filemanager.h" +#include "pointcalculate.h" class CentralWidget:public QWidget { @@ -51,6 +52,13 @@ protected: void contextMenuEvent(QContextMenuEvent *event); private: void glassTransitionHandle(); + + void glassTransitionHandle2(); + 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); + + void setEventHandlerEnable(const bool); void drawText(const QPointF,const QString); void fillGraph(const double x1,const double x2);