You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/Include/nmNum/nmSubWxs/nmWxPressFlowChartWidget.h

221 lines
6.7 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma once
#include <QWidget>
#include <QVector>
#include <QPointF>
#include <QPainter>
#include <QMouseEvent>
#include <QRect>
#include "nmDataPerforation.h"
#include "nmDataWellBase.h"
enum ChartType {
CHART_TYPE_LINE, // 连续线条(用于压力)
CHART_TYPE_STEP // 阶梯图(用于流量)
};
namespace {
struct AxisTicks {
QVector<double> majors;
QVector<double> minors;
double mainInterval;
double minorInterval;
AxisTicks() : mainInterval(0.0), minorInterval(0.0) {}
};
static inline bool approxEqual(double a, double b, double eps) {
return qAbs(a - b) <= eps;
}
// 生成 [mn, mx] 的主/次刻度
AxisTicks makeTicks(double mn, double mx, double mainInterval, double minorInterval) {
AxisTicks t;
if (mx <= mn || mainInterval <= 0.0) return t;
t.mainInterval = mainInterval;
t.minorInterval = (minorInterval > 0.0 ? minorInterval : 0.0);
const double startMajor = floor(mn / mainInterval) * mainInterval;
double v;
for (v = startMajor; v <= mx + mainInterval * 0.5; v += mainInterval) {
if (v >= mn - 1e-9 && v <= mx + 1e-9) t.majors.append(v);
}
if (t.minorInterval > 0.0) {
const double startMinor = floor(mn / t.minorInterval) * t.minorInterval;
for (v = startMinor; v <= mx + t.minorInterval * 0.5; v += t.minorInterval) {
if (v < mn - 1e-9 || v > mx + 1e-9) continue;
bool isMajor = false;
for (int i = 0; i < t.majors.size(); ++i) {
if (approxEqual(t.majors[i], v, 1e-9)) { isMajor = true; break; }
}
if (!isMajor) t.minors.append(v);
}
}
return t;
}
// 轴标签格式化
QString formatTickLabel(double v, bool preferInt, bool isPressureY) {
if (preferInt) {
const double iv = qRound(v);
if (qAbs(v - iv) < 1e-6) return QString::number((int)iv);
return QString::number(v, 'f', 1);
}
if (isPressureY) {
double twice = qRound(v * 2.0) / 2.0; // 允许 .5
if (qAbs(v - twice) < 1e-6) {
double iv = qRound(twice);
if (qAbs(twice - iv) < 1e-6) return QString::number((int)iv);
return QString::number(twice, 'f', 1);
}
return QString::number(v, 'f', 1);
}
const double iv = qRound(v);
if (qAbs(v - iv) < 1e-6) return QString::number((int)iv);
return QString::number(v, 'f', 1);
}
}
class nmWxPressFlowChartWidget : public QWidget
{
Q_OBJECT
public:
explicit nmWxPressFlowChartWidget(QWidget *parent = 0);
/** @brief 设置图表是否为只读模式。 */
void setReadOnly(bool bReadOnly);
/** @brief 设置是否允许点击绘图区发送时间选择信号。 */
void setTimeSelectionEnabled(bool bEnabled);
/** @brief 设置流动段编辑使用的井,不转移所有权。 */
void setCurrentWellData(nmDataWellBase* pWellData);
/** @brief 固定X轴显示范围用于多图时间轴对齐。 */
void setXAxisRange(double dMinimum, double dMaximum);
/** @brief 恢复根据图表自身数据计算X轴范围。 */
void clearXAxisRange();
/** @brief 高亮指定时间范围,零持续时间显示为竖线。 */
void setHighlightedTimeRange(double dStartTime, double dEndTime);
/** @brief 清除只读时间范围高亮。 */
void clearHighlightedTimeRange();
// 设置数据
void setData(const QVector<QPointF>& data);
void setTitle(const QString& title);
void setAxisLabels(const QString& xLabel, const QString& yLabel);
void setAxisUnits(const QString& xUnit, const QString& yUnit);
void setShowXAxisLabels(bool show);
void setLineColor(const QColor& color);
void setChartType(ChartType type);
double calculateTickInterval(double range, bool isYAxisForPressure);
// 清空数据
void clearData();
void setFlowSegmentData(const nmDataPerforation& perforationData);
// 设置外部拖动状态(当另一个图表正在拖动时调用)
void setExternalDragState(bool isDragging, int boundaryIndex, double boundaryTime);
// 清除外部拖动状态
void clearExternalDragState();
public slots:
void setSnapToRateChanges(bool snap);
signals:
void flowSegmentClicked(int segmentIndex);
void chartPointClicked(double timePoint);
void boundaryMoved(int boundaryIndex, double newTime);
void segmentsMerged(int segmentIndex);
void boundaryMovedFinal(int segmentIndex, double newTime);
protected:
void paintEvent(QPaintEvent *event);
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
void wheelEvent(QWheelEvent *event);
void mouseReleaseEvent(QMouseEvent *event) override;
private:
void drawBackground(QPainter& painter);
void drawGrid(QPainter& painter);
void drawAxes(QPainter& painter);
void drawData(QPainter& painter);
void drawLineData(QPainter& painter); // 绘制连续线条
void drawStepData(QPainter& painter); // 绘制阶梯图
void drawLabels(QPainter& painter);
void drawHighlightedTimeRange(QPainter& painter);
void drawSelectedFlowSegments(QPainter& painter);
int findBoundaryAtPosition(const QPoint& pos, double tolerance = 5.0) const;
bool isBoundaryDraggable(int boundaryIndex) const;
double snapToNearestFlowStep(double time) const;
bool shouldMergeSegments(double newBoundaryTime, int boundaryIndex) const;
void updateLocalBoundaryPosition(int boundaryIndex, double newTime);
void calculateDataRange();
QPointF dataToScreen(const QPointF& dataPoint);
QPointF screenToData(const QPointF& screenPoint);
private:
QVector<QPointF> m_data;
QString m_title;
QString m_xLabel;
QString m_yLabel;
QString m_xUnit;
QString m_yUnit;
QColor m_lineColor;
ChartType m_chartType;
// 数据范围
double m_minX, m_maxX;
double m_minY, m_maxY;
// 绘图区域
QRect m_plotArea;
int m_marginLeft;
int m_marginRight;
int m_marginTop;
int m_marginBottom;
// 交互
QPoint m_lastMousePos;
bool m_showXAxisLabels;
// 流动段数据副本
nmDataPerforation m_perforationData;
bool m_bReadOnly;
bool m_bTimeSelectionEnabled;
bool m_bFixedXAxisRange;
double m_dFixedMinimumX;
double m_dFixedMaximumX;
bool m_bHighlightTimeRange;
double m_dHighlightStartTime;
double m_dHighlightEndTime;
// 当前井仅供旧窗口吸附流动段边界使用,不拥有所有权。
nmDataWellBase* m_pCurrentWell;
// 拖动相关的成员变量
bool m_isDraggingBoundary; // 是否正在拖动分界线
int m_draggingBoundaryIndex; // 正在拖动的分界线索引 (-1表示不在拖动)
double m_draggingBoundaryTime; // 拖动过程中的临时时间位置
bool m_snapToRateChanges; // 是否对齐到流量变化点
// 新增成员变量:跟踪外部拖动状态
bool m_externalDragActive;
int m_externalDragBoundaryIndex;
double m_externalDragBoundaryTime;
};