|
|
#include "nmWxWellboreTrajectoryDisplay.h"
|
|
|
|
|
|
#include <QIcon>
|
|
|
#include <QRadioButton>
|
|
|
#include <QButtonGroup>
|
|
|
#include <QToolBar>
|
|
|
#include <QStackedWidget>
|
|
|
#include <QVBoxLayout>
|
|
|
#include <QHBoxLayout>
|
|
|
#include <QGroupBox>
|
|
|
#include <QAction>
|
|
|
#include <QLabel>
|
|
|
#include <QDebug>
|
|
|
#include <QCoreApplication>
|
|
|
#include <QMouseEvent>
|
|
|
#include <QCursor>
|
|
|
#include <QWheelEvent>
|
|
|
#include <QMessageBox>
|
|
|
|
|
|
#include "nmDataAnalyzeManager.h"
|
|
|
#include "nmDataPerforation.h"
|
|
|
|
|
|
nmWellborePointGraphicsItem::nmWellborePointGraphicsItem(QGraphicsItem *parent)
|
|
|
: QGraphicsEllipseItem(parent),
|
|
|
m_bIsDragging(false)
|
|
|
{
|
|
|
setPen(QPen(Qt::black, 0.5f));
|
|
|
setBrush(QBrush(Qt::darkGreen));
|
|
|
}
|
|
|
|
|
|
void nmWellborePointGraphicsItem::setStyle(const QPen& pen, const QBrush& brush)
|
|
|
{
|
|
|
setPen(pen);
|
|
|
setBrush(brush);
|
|
|
}
|
|
|
|
|
|
void nmWellborePointGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 如果是左键按下,并且允许移动
|
|
|
if(event->button() == Qt::LeftButton && (flags() & QGraphicsItem::ItemIsMovable)) {
|
|
|
m_bIsDragging = true;
|
|
|
// 记录鼠标按下时的场景坐标
|
|
|
m_dragStartPos = event->scenePos();
|
|
|
// 记录图元拖动前的原始位置(左上角)
|
|
|
m_originalPos = pos();
|
|
|
event->accept(); // 接受事件,表示已处理
|
|
|
}
|
|
|
|
|
|
QGraphicsEllipseItem::mousePressEvent(event);
|
|
|
}
|
|
|
|
|
|
void nmWellborePointGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bIsDragging && (event->buttons() & Qt::LeftButton)) {
|
|
|
// 计算偏移量
|
|
|
QPointF delta = event->scenePos() - m_dragStartPos;
|
|
|
setPos(m_originalPos + delta);
|
|
|
emit sigPositionChanging(sceneBoundingRect().center());
|
|
|
event->accept();
|
|
|
}
|
|
|
|
|
|
QGraphicsEllipseItem::mouseMoveEvent(event);
|
|
|
}
|
|
|
|
|
|
void nmWellborePointGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bIsDragging) {
|
|
|
m_bIsDragging = false;
|
|
|
emit sigDragFinished(sceneBoundingRect().center());
|
|
|
event->accept();
|
|
|
}
|
|
|
|
|
|
QGraphicsEllipseItem::mouseReleaseEvent(event);
|
|
|
}
|
|
|
|
|
|
nmWellboreLineGraphicsItem::nmWellboreLineGraphicsItem(QGraphicsItem *parent)
|
|
|
: QGraphicsLineItem(parent)
|
|
|
{
|
|
|
QPen wellborePen(Qt::blue, 2);
|
|
|
wellborePen.setCosmetic(true);
|
|
|
setPen(wellborePen);
|
|
|
}
|
|
|
|
|
|
void nmWellboreLineGraphicsItem::setStyle(const QPen& pen)
|
|
|
{
|
|
|
QPen cosmetic_pen = pen;
|
|
|
cosmetic_pen.setCosmetic(true);
|
|
|
setPen(cosmetic_pen);
|
|
|
}
|
|
|
|
|
|
nmPerforationHandleItem::nmPerforationHandleItem(PerforationHandleType type, QGraphicsItem *parent)
|
|
|
: m_handleType(type),
|
|
|
QGraphicsRectItem(parent)
|
|
|
{
|
|
|
setZValue(5);
|
|
|
}
|
|
|
|
|
|
#include <QPainter>
|
|
|
#include <QStyleOptionGraphicsItem>
|
|
|
#include <QWidget>
|
|
|
#include <QGraphicsScene>
|
|
|
#include <QGraphicsView>
|
|
|
|
|
|
void nmPerforationHandleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
{
|
|
|
// 获取视图缩放比例
|
|
|
if (!scene() || scene()->views().isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QGraphicsView* view = scene()->views().first();
|
|
|
if (!view) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
if (scaleY < 1e-6) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 定义手柄和边框的固定像素大小
|
|
|
const qreal fixedHandleSizeInPixels = 13.0;
|
|
|
const qreal fixedBorderWidthInPixels = 2.0; // 定义边框的像素宽度
|
|
|
|
|
|
// 根据缩放比例计算动态大小
|
|
|
const qreal dynamicHandleSize = fixedHandleSizeInPixels / scaleY;
|
|
|
const qreal dynamicBorderWidth = fixedBorderWidthInPixels / scaleY; // 计算动态边框宽度
|
|
|
|
|
|
// 定义手柄的矩形区域
|
|
|
QRectF handleRect(-dynamicHandleSize / 2, -dynamicHandleSize / 2, dynamicHandleSize, dynamicHandleSize);
|
|
|
|
|
|
// 创建一个新的 QPen 用于绘制边框
|
|
|
QPen pen(Qt::black); // 颜色为黑色
|
|
|
pen.setWidthF(dynamicBorderWidth); // 设置动态计算出的宽度
|
|
|
|
|
|
painter->setPen(pen);
|
|
|
painter->setBrush(Qt::red); // 填充颜色保持为红色
|
|
|
painter->drawRect(handleRect); // 绘制带边框的矩形
|
|
|
}
|
|
|
|
|
|
QRectF nmPerforationHandleItem::boundingRect() const
|
|
|
{
|
|
|
// 获取视图缩放比例
|
|
|
if (!scene() || scene()->views().isEmpty()) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
QGraphicsView* view = scene()->views().first();
|
|
|
if (!view) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
if (scaleY < 1e-6) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
// 使用与paint方法相同的动态大小计算
|
|
|
const qreal fixedHandleSizeInPixels = 13.0;
|
|
|
const qreal dynamicHandleSize = fixedHandleSizeInPixels / scaleY;
|
|
|
|
|
|
// 返回动态计算的矩形,与paint方法绘制的区域一致
|
|
|
return QRectF(-dynamicHandleSize / 2, -dynamicHandleSize / 2,
|
|
|
dynamicHandleSize, dynamicHandleSize);
|
|
|
}
|
|
|
|
|
|
nmPerforationGraphicsItem::nmPerforationGraphicsItem(nmDataPerforation *pPerData, NM_WELL_MODEL wellType, double wellboreStartAbsoluteMd, double totalWellboreMdLength, QGraphicsItem *parent)
|
|
|
: m_pPerforationData(pPerData), // 初始化关联的射孔数据
|
|
|
m_eWellType(wellType), // 初始化井筒类型
|
|
|
m_wellboreStartAbsoluteMd(wellboreStartAbsoluteMd), // 初始化井筒起始MD
|
|
|
m_totalWellboreMdLength(totalWellboreMdLength), // 初始化井筒总长度
|
|
|
QGraphicsRectItem(parent), // 调用基类 QGraphicsRectItem 的构造函数
|
|
|
m_bEditable(false), // 默认不可编辑
|
|
|
m_eCurrentInteractionMode(PerforationInteractionMode::Normal), // 默认正常交互模式
|
|
|
m_dPerWidth(10.0), // 射孔默认宽度
|
|
|
m_handlePen(Qt::black, 1), // 手柄边框笔触
|
|
|
m_handleBrush(Qt::red), // 手柄填充颜色
|
|
|
m_handleSize(0.0), // 手柄尺寸(稍后根据井筒类型设置)
|
|
|
m_pStartHandle(nullptr), // 起点手柄指针初始为空
|
|
|
m_pEndHandle(nullptr), // 终点手柄指针初始为空
|
|
|
m_isDragging(false), // 初始状态,未在拖动
|
|
|
m_tempDragMdStart(0.0), // 拖动过程中使用的临时起点MD
|
|
|
m_tempDragMdEnd(0.0) // 拖动过程中使用的临时终点MD
|
|
|
{
|
|
|
m_initialMdStart = 0.0;
|
|
|
m_initialMdEnd = 0.0;
|
|
|
m_dCroppedMdStart = 0.0;
|
|
|
m_dCroppedMdEnd = 0.0;
|
|
|
|
|
|
// 根据井筒类型设置射孔宽度和手柄大小
|
|
|
|
|
|
if(m_eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
m_dPerWidth = 2.0;
|
|
|
m_handleSize = 10.0;
|
|
|
} else {
|
|
|
m_handleSize = m_dPerWidth + 2; // 垂直井筒手柄略大于射孔宽度
|
|
|
}
|
|
|
|
|
|
QPen perforationPen(Qt::red, 1);
|
|
|
perforationPen.setCosmetic(true);
|
|
|
setPen(perforationPen);// 射孔本体边框
|
|
|
|
|
|
setBrush(QBrush(Qt::red));// 射孔本体填充颜色
|
|
|
setFlags(ItemIsSelectable);// 允许射孔本体被选中
|
|
|
setCacheMode(DeviceCoordinateCache);// 缓存绘图,提高性能
|
|
|
|
|
|
m_handlePen.setCosmetic(true);
|
|
|
|
|
|
// 创建起点和终点手柄,并设置父项为当前射孔图形项的父类
|
|
|
m_pStartHandle = new nmPerforationHandleItem(PerforationHandleType::StartHandle, parent);
|
|
|
m_pEndHandle = new nmPerforationHandleItem(PerforationHandleType::EndHandle, parent);
|
|
|
|
|
|
// 设置手柄的样式
|
|
|
m_pStartHandle->setPen(m_handlePen);
|
|
|
m_pStartHandle->setBrush(m_handleBrush);
|
|
|
m_pEndHandle->setPen(m_handlePen);
|
|
|
m_pEndHandle->setBrush(m_handleBrush);
|
|
|
|
|
|
// 默认隐藏手柄,只有在编辑模式下才显示
|
|
|
m_pStartHandle->setVisible(false);
|
|
|
m_pEndHandle->setVisible(false);
|
|
|
|
|
|
// 在创建时,将裁剪范围初始化为原始 MD 范围
|
|
|
m_dCroppedMdStart = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
m_dCroppedMdEnd = m_pPerforationData->getMdEnd().getValue().toDouble();
|
|
|
|
|
|
}
|
|
|
|
|
|
nmPerforationGraphicsItem::~nmPerforationGraphicsItem()
|
|
|
{
|
|
|
// 手动删除手柄
|
|
|
if(m_pStartHandle) {
|
|
|
delete m_pStartHandle;
|
|
|
m_pStartHandle = nullptr;
|
|
|
}
|
|
|
|
|
|
if(m_pEndHandle) {
|
|
|
delete m_pEndHandle;
|
|
|
m_pEndHandle = nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::setEditMode(bool enable)
|
|
|
{
|
|
|
m_bEditable = enable;
|
|
|
|
|
|
if(m_pStartHandle && m_pEndHandle) {
|
|
|
m_pStartHandle->setVisible(enable); // 根据编辑模式显示或隐藏手柄
|
|
|
m_pEndHandle->setVisible(enable);
|
|
|
}
|
|
|
|
|
|
update(); // 请求重绘以反映手柄的显示/隐藏状态
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::updateGraphics()
|
|
|
{
|
|
|
prepareGeometryChange(); // 通知场景图形项的几何形状即将改变,以便场景更新包围盒和重绘
|
|
|
// 当 Model 变化时,清空拖动标志,确保视图不再使用临时值,而是使用 Model 的真实数据进行渲染
|
|
|
m_isDragging = false;
|
|
|
|
|
|
// 从模型中读取最新的原始 MD 值
|
|
|
double mdStart = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
double mdEnd = m_pPerforationData->getMdEnd().getValue().toDouble();
|
|
|
|
|
|
// 更新裁剪范围为原始 MD,因为这是最终状态
|
|
|
m_dCroppedMdStart = mdStart;
|
|
|
m_dCroppedMdEnd = mdEnd;
|
|
|
|
|
|
// 调用渲染函数进行渲染
|
|
|
updatePerforationAndHandleGeometry(m_dCroppedMdStart, m_dCroppedMdEnd);
|
|
|
}
|
|
|
|
|
|
nmDataPerforation* nmPerforationGraphicsItem::getPerforationData()
|
|
|
{
|
|
|
return m_pPerforationData;
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::updateWellboreVisuals(const QPointF& wellboreStartScene, const QPointF& wellboreEndScene, double wellboreStartAbsoluteMd, double totalWellboreMdLength)
|
|
|
{
|
|
|
m_wellboreStartScenePoint = wellboreStartScene;
|
|
|
m_wellboreEndScenePoint = wellboreEndScene;
|
|
|
m_wellboreStartAbsoluteMd = wellboreStartAbsoluteMd;
|
|
|
m_totalWellboreMdLength = totalWellboreMdLength;
|
|
|
|
|
|
m_wellboreVisualLine.setPoints(wellboreStartScene, wellboreEndScene);
|
|
|
|
|
|
// 如果井筒长度为0,避免后续计算中出现除零
|
|
|
if(qFuzzyIsNull(m_totalWellboreMdLength)) {
|
|
|
m_totalWellboreMdLength = 1.0; // 赋一个非零小值,避免除零,但在实际应用中需要更严谨的错误处理
|
|
|
}
|
|
|
|
|
|
updateGraphics(); // 更新图形项的几何形状
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::updateGraphicsForPreview(qreal fractureTopMD, qreal fractureBottomMD)
|
|
|
{
|
|
|
prepareGeometryChange();
|
|
|
|
|
|
// 射孔的原始 MD 值。
|
|
|
// mdStart 是较小的值(更靠近井筒顶部)
|
|
|
// mdEnd 是较大的值(更靠近井筒底部)
|
|
|
double mdStart = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
//double mdEnd = m_pPerforationData->getMdEnd().getValue().toDouble();
|
|
|
|
|
|
// 之前裁剪后的 MD 值。
|
|
|
double currentCroppedStart = m_dCroppedMdStart;
|
|
|
double currentCroppedEnd = m_dCroppedMdEnd;
|
|
|
|
|
|
// 裂缝当前位置的新 MD 范围。
|
|
|
double newFractureTop = fractureTopMD;
|
|
|
double newFractureBottom = fractureBottomMD;
|
|
|
|
|
|
// --- 核心逻辑:寻找范围的交集 ---
|
|
|
// 新的裁剪起始 MD 是射孔当前起始 MD 和裂缝新顶部 MD 中的较大者。
|
|
|
// 这正确处理了从顶部进行的裁剪。
|
|
|
double newCroppedStart = qMax(currentCroppedStart, newFractureTop);
|
|
|
|
|
|
// 新的裁剪结束 MD 是射孔当前结束 MD 和裂缝新底部 MD 中的较小者。
|
|
|
// 这正确处理了从底部进行的裁剪。
|
|
|
double newCroppedEnd = qMin(currentCroppedEnd, newFractureBottom);
|
|
|
|
|
|
// 如果新的裁剪范围是有效的(起始 < 结束),我们才进行更新。
|
|
|
if(newCroppedStart < newCroppedEnd) {
|
|
|
m_dCroppedMdStart = newCroppedStart;
|
|
|
m_dCroppedMdEnd = newCroppedEnd;
|
|
|
} else {
|
|
|
// 如果新范围无效(例如,完全超出裂缝范围),
|
|
|
// 我们可以将裁剪范围设置为一个无效状态,以便图元被隐藏。
|
|
|
m_dCroppedMdStart = mdStart;
|
|
|
m_dCroppedMdEnd = mdStart;
|
|
|
}
|
|
|
|
|
|
// 调用渲染函数,并传入更新后的裁剪 MD 值。
|
|
|
updatePerforationAndHandleGeometry(m_dCroppedMdStart, m_dCroppedMdEnd);
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::updatePerforationAndHandleGeometry(double mdStartToRender, double mdEndToRender)
|
|
|
{
|
|
|
// 如果没有关联数据,则不进行更新
|
|
|
if(!m_pPerforationData) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 计算射孔本体在场景中的起始和结束点
|
|
|
QPointF sceneStartPoint = getScenePointFromMd(mdStartToRender); // 计算起点MD对应的场景坐标
|
|
|
QPointF sceneEndPoint = getScenePointFromMd(mdEndToRender); // 计算终点MD对应的场景坐标
|
|
|
|
|
|
// 计算射孔的视觉长度(像素)
|
|
|
double perforationVisualLength = QLineF(sceneStartPoint, sceneEndPoint).length();
|
|
|
|
|
|
// 计算井筒的方向向量和角度 (用于射孔本体和手柄的旋转)
|
|
|
QVector2D wellboreVector = QVector2D(m_wellboreVisualLine.p2() - m_wellboreVisualLine.p1());
|
|
|
qreal wellboreAngleRad = qAtan2(wellboreVector.y(), wellboreVector.x());
|
|
|
qreal wellboreAngleDegrees = wellboreAngleRad * 180.0 / M_PI;
|
|
|
|
|
|
// 计算射孔中心点对应的场景坐标
|
|
|
QPointF scenePerforationCenter = getScenePointFromMd((mdStartToRender + mdEndToRender) / 2.0);
|
|
|
|
|
|
// *** 更新射孔本体的几何形状 ***
|
|
|
// 设置射孔图形项的位置和旋转
|
|
|
// 注意:这里的 setPos 和 setRotation 是针对射孔本体 (this) 的
|
|
|
setPos(scenePerforationCenter); // 设置图形项的中心点到射孔中心
|
|
|
setTransformOriginPoint(0, 0); // 旋转的原点设为图形项的中心
|
|
|
setRotation(wellboreAngleDegrees); // 设置旋转角度,使射孔与井筒对齐
|
|
|
|
|
|
// 设置射孔本体的矩形区域(局部坐标系)
|
|
|
// 这个矩形是相对于 setPos 设置的中心点的
|
|
|
QRectF localPerforationRect(-perforationVisualLength / 2.0, // X 轴起点
|
|
|
-m_dPerWidth / 2.0, // Y 轴起点
|
|
|
perforationVisualLength, // 宽度
|
|
|
m_dPerWidth); // 高度
|
|
|
setRect(localPerforationRect);
|
|
|
|
|
|
// *** 更新手柄的几何形状 ***
|
|
|
// 由于手柄和射孔本体是兄弟项(拥有相同的父项),
|
|
|
// 它们的位置需要直接在共同父项的坐标系中(通常是场景坐标系)设置。
|
|
|
if(m_pStartHandle && m_pEndHandle) {
|
|
|
qreal halfHandleSize = m_handleSize / 2.0;
|
|
|
|
|
|
// 手柄的矩形是相对于其自身的 pos() 的。
|
|
|
// 所以,先设置手柄自身的局部矩形(通常以其自身中心为 (0,0)),
|
|
|
// 然后通过 setPos 将手柄移动到场景中的正确位置。
|
|
|
QRectF handleLocalRect(-halfHandleSize, -halfHandleSize, m_handleSize, m_handleSize);
|
|
|
m_pStartHandle->setRect(handleLocalRect);
|
|
|
m_pEndHandle->setRect(handleLocalRect);
|
|
|
|
|
|
// 关键:直接将手柄的 pos() 设置为场景坐标
|
|
|
// getScenePointFromMd 返回的就是场景坐标,直接用于 setPos()
|
|
|
m_pStartHandle->setPos(sceneStartPoint);
|
|
|
m_pEndHandle->setPos(sceneEndPoint);
|
|
|
|
|
|
// 如果手柄也需要与井筒对齐旋转,则它们也需要设置旋转角度
|
|
|
m_pStartHandle->setTransformOriginPoint(0, 0); // 相对自身中心旋转
|
|
|
m_pEndHandle->setTransformOriginPoint(0, 0);
|
|
|
m_pStartHandle->setRotation(wellboreAngleDegrees);
|
|
|
m_pEndHandle->setRotation(wellboreAngleDegrees);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QPointF nmPerforationGraphicsItem::getScenePointFromMd(double md) const
|
|
|
{
|
|
|
// 防止除零或负长度
|
|
|
if(qFuzzyIsNull(m_totalWellboreMdLength) || m_totalWellboreMdLength < 0) {
|
|
|
return m_wellboreStartScenePoint;
|
|
|
}
|
|
|
|
|
|
// 计算相对MD(从井筒起始MD开始算)
|
|
|
double relativeMd = md - m_wellboreStartAbsoluteMd;
|
|
|
// 计算井筒视觉长度
|
|
|
double wellboreVisualLength = QLineF(m_wellboreStartScenePoint, m_wellboreEndScenePoint).length();
|
|
|
|
|
|
if(qFuzzyIsNull(wellboreVisualLength)) {
|
|
|
return m_wellboreStartScenePoint;
|
|
|
}
|
|
|
|
|
|
// 计算MD到像素的缩放比例
|
|
|
double mdToVisualScale = wellboreVisualLength / m_totalWellboreMdLength;
|
|
|
// 计算沿井筒方向的视觉距离
|
|
|
double distanceAlongWellbore = relativeMd * mdToVisualScale;
|
|
|
// 计算井筒方向的单位向量
|
|
|
QVector2D wellboreDirection = QVector2D(m_wellboreEndScenePoint - m_wellboreStartScenePoint);
|
|
|
|
|
|
if(qFuzzyIsNull(wellboreDirection.length())) {
|
|
|
return m_wellboreStartScenePoint;
|
|
|
}
|
|
|
|
|
|
wellboreDirection.normalize(); // 归一化为单位向量
|
|
|
// 返回根据距离和方向计算出的场景点
|
|
|
return m_wellboreStartScenePoint + (wellboreDirection * distanceAlongWellbore).toPointF();
|
|
|
}
|
|
|
|
|
|
double nmPerforationGraphicsItem::getMdFromScenePointOnWellbore(const QPointF& scenePoint) const
|
|
|
{
|
|
|
// 直井筒的简化计算
|
|
|
if(m_eWellType == NM_WELL_MODEL::Vertical_Well || m_eWellType == NM_WELL_MODEL::Vertical_Fractured_Well) {
|
|
|
double yOffset = scenePoint.y() - m_wellboreStartScenePoint.y();
|
|
|
double wellboreVisualLength = QLineF(m_wellboreStartScenePoint, m_wellboreEndScenePoint).length();
|
|
|
|
|
|
if(qFuzzyIsNull(wellboreVisualLength)) {
|
|
|
return m_wellboreStartAbsoluteMd;
|
|
|
}
|
|
|
|
|
|
double mdScale = m_totalWellboreMdLength / wellboreVisualLength;
|
|
|
return m_wellboreStartAbsoluteMd + yOffset * mdScale;
|
|
|
}
|
|
|
|
|
|
// 针对其他井筒类型,使用投影计算
|
|
|
QPointF p1 = m_wellboreStartScenePoint;
|
|
|
QPointF p2 = m_wellboreEndScenePoint;
|
|
|
QLineF wellboreLine(p1, p2);
|
|
|
|
|
|
if(qFuzzyIsNull(wellboreLine.length()) || qFuzzyIsNull(m_totalWellboreMdLength) || m_totalWellboreMdLength <= 0) {
|
|
|
return m_wellboreStartAbsoluteMd;
|
|
|
}
|
|
|
|
|
|
// 计算点在井筒线上的投影比例
|
|
|
QVector2D vecAP = QVector2D(scenePoint - p1);
|
|
|
QVector2D vecAB = QVector2D(p2 - p1);
|
|
|
double dotProduct_AP_AB = QVector2D::dotProduct(vecAP, vecAB);
|
|
|
double lenSq_AB = vecAB.lengthSquared();
|
|
|
double t = 0.0;
|
|
|
|
|
|
if(!qFuzzyIsNull(lenSq_AB)) {
|
|
|
t = dotProduct_AP_AB / lenSq_AB;
|
|
|
}
|
|
|
|
|
|
t = qBound(0.0, t, 1.0); // 将比例限制在 0.0 到 1.0 之间
|
|
|
// 计算投影点沿井筒的距离
|
|
|
double distanceAlongVisualWellbore = wellboreLine.length() * t;
|
|
|
double wellboreVisualLength = wellboreLine.length();
|
|
|
double mdToVisualScale = 0.0;
|
|
|
|
|
|
if(!qFuzzyIsNull(m_totalWellboreMdLength)) {
|
|
|
mdToVisualScale = wellboreVisualLength / m_totalWellboreMdLength;
|
|
|
}
|
|
|
|
|
|
if(qFuzzyIsNull(mdToVisualScale)) {
|
|
|
return m_wellboreStartAbsoluteMd;
|
|
|
}
|
|
|
|
|
|
// 将视觉距离转换为MD
|
|
|
double relativeMd = distanceAlongVisualWellbore / mdToVisualScale;
|
|
|
return m_wellboreStartAbsoluteMd + relativeMd;
|
|
|
}
|
|
|
|
|
|
double nmPerforationGraphicsItem::getCroppedMdStart()
|
|
|
{
|
|
|
return m_dCroppedMdStart;
|
|
|
}
|
|
|
|
|
|
double nmPerforationGraphicsItem::getCroppedMdEnd()
|
|
|
{
|
|
|
return m_dCroppedMdEnd;
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::updateVisualsForWellboreDrag(const QPointF& wellboreStartScene, const QPointF& wellboreEndScene,
|
|
|
double wellboreStartAbsoluteMd, double originalWellboreLength,
|
|
|
double newWellboreLength)
|
|
|
{
|
|
|
// 如果旧井筒MD长度无效,则直接返回
|
|
|
if(qFuzzyIsNull(originalWellboreLength) || originalWellboreLength <= 0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 临时更新井筒的视觉信息,确保 getScenePointFromMd 函数能够使用最新的几何信息
|
|
|
m_wellboreStartScenePoint = wellboreStartScene;
|
|
|
m_wellboreEndScenePoint = wellboreEndScene;
|
|
|
m_wellboreVisualLine.setPoints(wellboreStartScene, wellboreEndScene);
|
|
|
|
|
|
// 从数据模型中获取原始MD值(这些是拖动开始时的旧值)
|
|
|
double originalMdStart = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
double originalMdEnd = m_pPerforationData->getMdEnd().getValue().toDouble();
|
|
|
|
|
|
// 关键修正1: 在预览模式下,我们将井筒的总MD长度临时设置为新的井筒像素长度。
|
|
|
// 这样,MD值和像素长度就有了1:1的比例关系,确保了 MD 到像素转换的正确性。
|
|
|
m_totalWellboreMdLength = newWellboreLength;
|
|
|
|
|
|
// 关键修正2: 基于原始MD和旧MD长度,计算射孔的相对比例
|
|
|
double relativeStart = (originalMdStart - wellboreStartAbsoluteMd) / originalWellboreLength;
|
|
|
double relativeEnd = (originalMdEnd - wellboreStartAbsoluteMd) / originalWellboreLength;
|
|
|
|
|
|
// 关键修正3: 根据新的虚拟MD长度(即newWellboreLength),按比例计算出新的MD值
|
|
|
// 这些MD值是临时的,只用于渲染,并不修改数据模型
|
|
|
double newMdStart = wellboreStartAbsoluteMd + relativeStart * m_totalWellboreMdLength;
|
|
|
double newMdEnd = wellboreStartAbsoluteMd + relativeEnd * m_totalWellboreMdLength;
|
|
|
|
|
|
// 调用渲染函数,传入计算出的新MD值进行渲染
|
|
|
updatePerforationAndHandleGeometry(newMdStart, newMdEnd);
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
{
|
|
|
Q_UNUSED(option);
|
|
|
Q_UNUSED(widget);
|
|
|
|
|
|
// --- 1. 获取视图和缩放比例 ---
|
|
|
// 前提条件检查:必须能获取到场景和视图
|
|
|
if (!scene() || scene()->views().isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
// 获取场景关联的第一个视图
|
|
|
QGraphicsView* view = scene()->views().first();
|
|
|
if (!view) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 从视图获取变换矩阵,这比从painter获取更稳定
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
// 获取垂直缩放因子。使用 qAbs() 来处理坐标系翻转(scaleY为负)的情况
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
// --- 2. 核心计算(与之前相同,但基于更可靠的scaleY) ---
|
|
|
if (scaleY < 1e-6) { // 安全检查,避免除以一个极小或为零的数
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 定义在屏幕上看到的固定厚度
|
|
|
const qreal fixedVisualHeightInPixels = 15.0;
|
|
|
|
|
|
// 动态计算在当前缩放级别下,为达到目标像素厚度,所需的几何高度
|
|
|
const qreal dynamicGeomHeight = fixedVisualHeightInPixels / scaleY;
|
|
|
|
|
|
// --- 3. 创建并绘制矩形 ---
|
|
|
const QRectF originalGeomRect = this->rect();
|
|
|
|
|
|
// 创建一个用于本次绘制的新矩形
|
|
|
QRectF rectToDraw(0, 0, originalGeomRect.width(), dynamicGeomHeight);
|
|
|
rectToDraw.moveCenter(originalGeomRect.center());
|
|
|
|
|
|
// 使用纯色填充来绘制
|
|
|
painter->setPen(Qt::NoPen);
|
|
|
painter->setBrush(Qt::red);
|
|
|
painter->drawRect(rectToDraw);
|
|
|
}
|
|
|
|
|
|
QRectF nmPerforationGraphicsItem::boundingRect() const
|
|
|
{
|
|
|
return this->rect();
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 如果不可编辑,则调用基类事件并返回
|
|
|
if(!m_bEditable) {
|
|
|
QGraphicsRectItem::mousePressEvent(event);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_lastMouseScenePos = event->scenePos(); // 记录鼠标按下的场景坐标
|
|
|
|
|
|
// 获取鼠标点击位置最顶层的图形项
|
|
|
QGraphicsItem* clickedItem = scene()->itemAt(event->scenePos(), QTransform());
|
|
|
|
|
|
// 根据点击的图形项类型设置交互模式
|
|
|
if(clickedItem == m_pStartHandle) { // 点击了起点手柄
|
|
|
double dStartMd = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
bool isStartAtWellboreOrigin = qFuzzyCompare(dStartMd, m_wellboreStartAbsoluteMd);
|
|
|
|
|
|
// 特殊处理水平裂缝井,如果起点在井筒原点,则不允许拖动起点
|
|
|
if(m_eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well && isStartAtWellboreOrigin) {
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::Normal;
|
|
|
event->ignore(); // 忽略事件,不处理
|
|
|
} else {
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::ResizeStart; // 进入调整起点模式
|
|
|
m_initialMdStart = dStartMd; // 记录拖动开始时 Model 的原始起点MD
|
|
|
m_initialMdEnd = m_pPerforationData->getMdEnd().getValue().toDouble(); // 记录拖动开始时 Model 的原始终点MD
|
|
|
// 拖动开始时,设置临时MD值与 Model 真实值一致,并设置拖动标志
|
|
|
m_tempDragMdStart = m_initialMdStart;
|
|
|
m_tempDragMdEnd = m_initialMdEnd;
|
|
|
m_isDragging = true; // 标志开始拖动
|
|
|
event->accept(); // 接受事件,表示已处理
|
|
|
}
|
|
|
} else if(clickedItem == m_pEndHandle) { // 点击了终点手柄
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::ResizeEnd; // 进入调整终点模式
|
|
|
m_initialMdStart = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
m_initialMdEnd = m_pPerforationData->getMdEnd().getValue().toDouble();
|
|
|
// 拖动开始时,设置临时MD值与 Model 真实值一致,并设置拖动标志
|
|
|
m_tempDragMdStart = m_initialMdStart;
|
|
|
m_tempDragMdEnd = m_initialMdEnd;
|
|
|
m_isDragging = true;
|
|
|
event->accept();
|
|
|
} else {
|
|
|
// 如果点击的是射孔本体(而不是手柄)
|
|
|
// 注意:这里的 rect() 和 mapFromScene() 是针对 nmPerforationGraphicsItem 自身的局部坐标系
|
|
|
if(rect().contains(mapFromScene(event->scenePos()))) { // 检查点击点是否在射孔本体的局部矩形内
|
|
|
double dStartMd = m_pPerforationData->getMdStart().getValue().toDouble();
|
|
|
bool isStartAtWellboreOrigin = qFuzzyCompare(dStartMd, m_wellboreStartAbsoluteMd);
|
|
|
|
|
|
// 特殊处理水平裂缝井,如果起点在井筒原点,则不允许移动整个射孔
|
|
|
if(m_eWellType == NM_WELL_MODEL::Horizontal_Fractured_Well && isStartAtWellboreOrigin) {
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::Normal;
|
|
|
event->ignore();
|
|
|
} else {
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::MoveItem; // 进入移动整个项模式
|
|
|
|
|
|
if(flags() & ItemIsSelectable) {
|
|
|
setSelected(true); // 如果可选择,则选中该项
|
|
|
}
|
|
|
|
|
|
// 拖动开始时,设置临时MD值与 Model 真实值一致,并设置拖动标志
|
|
|
m_tempDragMdStart = m_initialMdStart;
|
|
|
m_tempDragMdEnd = m_initialMdEnd;
|
|
|
m_isDragging = true;
|
|
|
event->accept();
|
|
|
}
|
|
|
} else {
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::Normal; // 否则回到正常模式
|
|
|
QGraphicsRectItem::mousePressEvent(event); // 调用基类事件
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 再次检查,如果进入了某种交互模式,则接受事件
|
|
|
if(m_eCurrentInteractionMode != PerforationInteractionMode::Normal) {
|
|
|
event->accept();
|
|
|
} else {
|
|
|
QGraphicsRectItem::mousePressEvent(event); // 否则调用基类事件
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 如果不可编辑,或者不是在拖动,或者不是正在进行交互模式,则调用基类事件并返回
|
|
|
if(!m_bEditable || m_eCurrentInteractionMode == PerforationInteractionMode::Normal || !m_isDragging) {
|
|
|
QGraphicsRectItem::mouseMoveEvent(event);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QPointF currentScenePos = event->scenePos(); // 获取当前鼠标的场景坐标
|
|
|
double newMd = getMdFromScenePointOnWellbore(currentScenePos); // 计算当前鼠标位置对应的MD值
|
|
|
|
|
|
// 应该取自 m_tempDragMdEnd 和 m_tempDragMdStart,以保持与当前拖动状态一致,而不是模型当前值。
|
|
|
// 因为模型尚未更新,如果取模型当前值,可能会导致在拖动过程中再次被旧数据限制。
|
|
|
double dCurrentDragEndMd = m_tempDragMdEnd;
|
|
|
//double dCurrentDragStartMd = m_tempDragMdStart;
|
|
|
|
|
|
double dProposedMdStart = m_initialMdStart; // 默认使用拖动开始时的原始起点MD
|
|
|
double dProposedMdEnd = m_initialMdEnd; // 默认使用拖动开始时的原始终点MD
|
|
|
|
|
|
// 根据当前交互模式计算建议的MD值
|
|
|
if(m_eCurrentInteractionMode == PerforationInteractionMode::ResizeStart) {
|
|
|
dProposedMdStart = newMd; // 如果是调整起点,新起点就是当前鼠标位置的MD
|
|
|
} else if(m_eCurrentInteractionMode == PerforationInteractionMode::ResizeEnd) {
|
|
|
dProposedMdEnd = newMd; // 如果是调整终点,新终点就是当前鼠标位置的MD
|
|
|
} else if(m_eCurrentInteractionMode == PerforationInteractionMode::MoveItem) {
|
|
|
// 计算MD位移量。这里采用了增量计算:当前鼠标位置与上次位置的MD差值
|
|
|
QPointF deltaScene = currentScenePos - m_lastMouseScenePos; // 当前帧的像素位移
|
|
|
|
|
|
double mdDelta;
|
|
|
|
|
|
if(m_eWellType == NM_WELL_MODEL::Vertical_Well) {
|
|
|
mdDelta = deltaScene.y(); // 垂直井筒通常只关注Y轴位移
|
|
|
} else {
|
|
|
QLineF wellboreDirLine(m_wellboreStartScenePoint, m_wellboreEndScenePoint);
|
|
|
|
|
|
if(qFuzzyIsNull(wellboreDirLine.length()) || qFuzzyIsNull(m_totalWellboreMdLength) || m_totalWellboreMdLength <= 0) {
|
|
|
mdDelta = 0.0; // 防止除零
|
|
|
} else {
|
|
|
// 计算鼠标位移在井筒方向上的投影,并转换为MD值
|
|
|
QVector2D wellboreUnitVec = QVector2D(wellboreDirLine.p2() - wellboreDirLine.p1()).normalized();
|
|
|
double visualMoveAlongWellbore = QVector2D::dotProduct(QVector2D(deltaScene), wellboreUnitVec);
|
|
|
mdDelta = visualMoveAlongWellbore / wellboreDirLine.length() * m_totalWellboreMdLength;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 对于移动操作,将计算出的MD位移量累加到当前的临时MD值上
|
|
|
dProposedMdStart = m_tempDragMdStart + mdDelta;
|
|
|
dProposedMdEnd = m_tempDragMdEnd + mdDelta;
|
|
|
}
|
|
|
|
|
|
// 获取父级视图,用于获取MD范围限制
|
|
|
nmAbstractWellboreVisual* parentView = static_cast<nmAbstractWellboreVisual*>(parentItem());
|
|
|
|
|
|
if(parentView) {
|
|
|
double dUpperAllowedMd = -DBL_MAX; // 允许的最小MD值(上限)
|
|
|
double dLowerAllowedMd = DBL_MAX; // 允许的最大MD值(下限)
|
|
|
// 获取射孔允许的MD范围,这个函数应该基于 Model 的当前数据来判断限制
|
|
|
parentView->getWellData()->getPerforationAllowedMdRange(m_pPerforationData, dUpperAllowedMd, dLowerAllowedMd);
|
|
|
|
|
|
// 应用MD范围限制
|
|
|
if(m_eCurrentInteractionMode == PerforationInteractionMode::ResizeStart) {
|
|
|
dProposedMdStart = qMax(dProposedMdStart, dUpperAllowedMd); // 起点不能小于允许的上限
|
|
|
dProposedMdStart = qMin(dProposedMdStart, dCurrentDragEndMd); // 起点不能大于 Model 的当前终点MD
|
|
|
} else if(m_eCurrentInteractionMode == PerforationInteractionMode::ResizeEnd) {
|
|
|
dProposedMdEnd = qMin(dProposedMdEnd, dLowerAllowedMd); // 终点不能大于允许的下限
|
|
|
dProposedMdEnd = qMax(dProposedMdEnd, m_pPerforationData->getMdStart().getValue().toDouble()); // 终点不能小于 Model 的当前起点MD
|
|
|
} else if(m_eCurrentInteractionMode == PerforationInteractionMode::MoveItem) {
|
|
|
// 移动模式下的边界限制
|
|
|
// 使用拖动开始时的原始长度来保持长度不变
|
|
|
double dPerforationLength = m_initialMdEnd - m_initialMdStart;
|
|
|
|
|
|
if(dProposedMdEnd > dLowerAllowedMd) { // 如果终点超出下限
|
|
|
dProposedMdEnd = dLowerAllowedMd; // 将终点设为下限
|
|
|
dProposedMdStart = dProposedMdEnd - dPerforationLength; // 起点也相应调整
|
|
|
}
|
|
|
|
|
|
if(dProposedMdStart < dUpperAllowedMd) { // 如果起点超出上限
|
|
|
dProposedMdStart = dUpperAllowedMd; // 将起点设为上限
|
|
|
dProposedMdEnd = dProposedMdStart + dPerforationLength; // 终点也相应调整
|
|
|
}
|
|
|
|
|
|
if(dProposedMdStart > dProposedMdEnd) { // 确保射孔长度不为负 (起点MD不能大于终点MD)
|
|
|
dProposedMdStart = dProposedMdEnd; // 如果出现这种情况,将起点MD设为终点MD,变为零长度
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 关键:更新临时变量,并使用它们进行局部视图更新
|
|
|
m_tempDragMdStart = dProposedMdStart; // 保存计算出的建议起点MD到临时变量
|
|
|
m_tempDragMdEnd = dProposedMdEnd; // 保存计算出的建议终点MD到临时变量
|
|
|
|
|
|
// 局部更新视图:直接调用 updatePerforationAndHandleGeometry,传入临时MD值进行渲染
|
|
|
// 此时不修改数据模型,也不发射信号,只更新屏幕显示。
|
|
|
// 手柄和射孔本体都会根据这些临时 MD 值重新计算并设置其在场景中的位置。
|
|
|
updatePerforationAndHandleGeometry(m_tempDragMdStart, m_tempDragMdEnd);
|
|
|
|
|
|
m_lastMouseScenePos = currentScenePos; // 更新鼠标位置,为下一次移动做准备
|
|
|
event->accept(); // 接受事件,表示已处理
|
|
|
}
|
|
|
|
|
|
void nmPerforationGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 如果不可编辑,则调用基类事件并返回
|
|
|
if(!m_bEditable) {
|
|
|
QGraphicsRectItem::mouseReleaseEvent(event);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_eCurrentInteractionMode = PerforationInteractionMode::Normal; // 鼠标释放后,回到正常模式
|
|
|
|
|
|
QGraphicsRectItem::mouseReleaseEvent(event); // 调用基类的鼠标释放事件
|
|
|
|
|
|
// 关键:鼠标释放(拖动完成)时,发射最终的请求信号
|
|
|
// 只有在确实进行了拖动操作并且关联了数据模型时才发射信号
|
|
|
if(m_isDragging && m_pPerforationData) {
|
|
|
// 发射信号,请求控制器更新数据。直接传递 m_pPerforationData 指针和最终的临时MD值
|
|
|
emit sigRequestPerforationMdUpdate(m_pPerforationData,
|
|
|
m_tempDragMdStart,
|
|
|
m_tempDragMdEnd);
|
|
|
|
|
|
// 同步 m_initialMdStart 和 m_initialMdEnd
|
|
|
// 确保下一次鼠标按下时的“原始位置”是本次拖动结束后的新位置
|
|
|
m_initialMdStart = m_tempDragMdStart;
|
|
|
m_initialMdEnd = m_tempDragMdEnd;
|
|
|
}
|
|
|
|
|
|
// 重置拖动标志
|
|
|
m_isDragging = false; // 拖动操作结束
|
|
|
|
|
|
event->accept(); // 接受事件,表示已处理
|
|
|
}
|
|
|
|
|
|
|
|
|
nmFractureLineHandleItem::nmFractureLineHandleItem(QGraphicsItem *parent)
|
|
|
: QObject(nullptr), // 初始化 QObject 部分
|
|
|
QGraphicsRectItem(parent), // 初始化 QGraphicsRectItem 部分
|
|
|
m_bIsDragging(false)
|
|
|
{
|
|
|
// 默认样式,可以在外部设置
|
|
|
setBrush(QBrush(Qt::red));
|
|
|
setPen(QPen(Qt::black, 1));
|
|
|
setRect(-5, -5, 10, 10); // 默认大小,居中
|
|
|
setZValue(2.0); // 确保在裂缝之上
|
|
|
}
|
|
|
|
|
|
void nmFractureLineHandleItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(event->button() == Qt::LeftButton) {
|
|
|
m_bIsDragging = true;
|
|
|
m_lastMousePos = event->pos(); // 记录鼠标在手柄局部坐标系中的位置
|
|
|
event->accept(); // 接受这个事件,防止事件传递给父项或视图
|
|
|
} else {
|
|
|
event->ignore(); // 忽略其他鼠标按钮
|
|
|
}
|
|
|
|
|
|
//QGraphicsRectItem::mousePressEvent(event); // 调用基类实现(重要)
|
|
|
}
|
|
|
|
|
|
void nmFractureLineHandleItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bIsDragging) {
|
|
|
// 获取当前鼠标位置(场景坐标)
|
|
|
QPointF currentScenePos = event->scenePos();
|
|
|
// 获取上一次鼠标位置(场景坐标)
|
|
|
QPointF lastScenePos = mapToScene(m_lastMousePos);
|
|
|
// 计算增量(场景坐标)
|
|
|
QPointF deltaScene = currentScenePos - lastScenePos;
|
|
|
// 转换为父项坐标的增量
|
|
|
QPointF deltaParent = parentItem()->mapFromScene(currentScenePos) - parentItem()->mapFromScene(lastScenePos);
|
|
|
// 更新位置(父项坐标)
|
|
|
setPos(pos() + deltaParent);
|
|
|
// 发出信号(传递父项坐标)
|
|
|
emit handlePositionChanged(this, pos(), m_bIsDragging);
|
|
|
// 更新记录(局部坐标)
|
|
|
m_lastMousePos = mapFromScene(currentScenePos);
|
|
|
|
|
|
event->accept(); // 接受事件
|
|
|
} else {
|
|
|
event->ignore();
|
|
|
}
|
|
|
|
|
|
QGraphicsRectItem::mouseMoveEvent(event); // 调用基类实现(重要)
|
|
|
}
|
|
|
|
|
|
void nmFractureLineHandleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bIsDragging) {
|
|
|
m_bIsDragging = false;
|
|
|
|
|
|
// 发出信号通知父级裂缝项,手柄位置已改变
|
|
|
emit handlePositionChanged(this, pos(), m_bIsDragging);
|
|
|
event->accept();
|
|
|
|
|
|
} else {
|
|
|
event->ignore();
|
|
|
}
|
|
|
|
|
|
QGraphicsRectItem::mouseReleaseEvent(event); // 调用基类实现(重要)
|
|
|
}
|
|
|
|
|
|
void nmFractureLineHandleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
{
|
|
|
// 1. 获取视图缩放比例
|
|
|
QGraphicsScene* pScene = scene();
|
|
|
if (!pScene || pScene->views().isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QGraphicsView* view = pScene->views().first();
|
|
|
if (!view) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
// 截面图,依然使用 Y 轴缩放
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
if (scaleY < 1e-6) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 2. 定义手柄和边框的固定像素大小
|
|
|
const qreal fixedHandleSizeInPixels = 13.0;
|
|
|
const qreal fixedBorderWidthInPixels = 2.0; // 定义边框的像素宽度
|
|
|
|
|
|
// 3. 根据缩放比例计算动态大小
|
|
|
const qreal dynamicHandleSize = fixedHandleSizeInPixels / scaleY;
|
|
|
const qreal dynamicBorderWidth = fixedBorderWidthInPixels / scaleY; // 计算动态边框宽度
|
|
|
|
|
|
// 4. 定义手柄的矩形区域
|
|
|
QRectF handleRect(-dynamicHandleSize / 2, -dynamicHandleSize / 2, dynamicHandleSize, dynamicHandleSize);
|
|
|
|
|
|
// 5. 创建一个新的 QPen 用于绘制边框
|
|
|
QPen pen(Qt::black); // 颜色为黑色
|
|
|
pen.setWidthF(dynamicBorderWidth); // 设置动态计算出的宽度
|
|
|
|
|
|
painter->setPen(pen);
|
|
|
painter->setBrush(Qt::red); // 填充颜色保持为红色
|
|
|
painter->drawRect(handleRect); // 绘制带边框的矩形
|
|
|
}
|
|
|
|
|
|
QRectF nmFractureLineHandleItem::boundingRect() const
|
|
|
{
|
|
|
// 1. 获取视图缩放比例
|
|
|
if (!scene() || scene()->views().isEmpty()) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
QGraphicsView* view = scene()->views().first();
|
|
|
if (!view) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
if (scaleY < 1e-6) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
// 2. 使用与paint方法相同的动态大小计算
|
|
|
const qreal fixedHandleSizeInPixels = 13.0;
|
|
|
const qreal dynamicHandleSize = fixedHandleSizeInPixels / scaleY;
|
|
|
|
|
|
// 3. 返回动态计算的矩形
|
|
|
return QRectF(-dynamicHandleSize / 2, -dynamicHandleSize / 2,
|
|
|
dynamicHandleSize, dynamicHandleSize);
|
|
|
}
|
|
|
|
|
|
#include "nmDataVerticalFracturedWell.h"
|
|
|
nmFractureLineGraphicsItem::nmFractureLineGraphicsItem(QGraphicsItem *parent)
|
|
|
: QGraphicsLineItem(parent),
|
|
|
m_bEditMode(false)
|
|
|
{
|
|
|
// 设置裂缝线的样式
|
|
|
//setPen(QPen(QColor("#1e90ff"), 5.0));
|
|
|
//createHandles();
|
|
|
QPen fracturePen(QColor("#1e90ff"), 5.0);
|
|
|
fracturePen.setCosmetic(true);
|
|
|
setPen(fracturePen);
|
|
|
createHandles();
|
|
|
}
|
|
|
|
|
|
void nmFractureLineGraphicsItem::setStyle(const QPen& pen)
|
|
|
{
|
|
|
QPen cosmetic_pen = pen;
|
|
|
cosmetic_pen.setWidth(0);
|
|
|
cosmetic_pen.setCosmetic(true);
|
|
|
setPen(cosmetic_pen);
|
|
|
//setPen(pen);
|
|
|
}
|
|
|
|
|
|
void nmFractureLineGraphicsItem::setEditMode(bool enable)
|
|
|
{
|
|
|
if(m_bEditMode == enable) {
|
|
|
return; // 状态未改变,无需操作
|
|
|
}
|
|
|
|
|
|
m_bEditMode = enable;
|
|
|
|
|
|
if(m_pHandle1 && m_pHandle2) {
|
|
|
m_pHandle1->setVisible(enable); // 根据模式显示或隐藏手柄
|
|
|
m_pHandle2->setVisible(enable);
|
|
|
|
|
|
// 如果进入编辑模式,确保手柄位置是最新的
|
|
|
if(enable) {
|
|
|
updateHandlesPosition();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 强制更新视图,确保手柄的显示/隐藏立即生效
|
|
|
//scene()->update();
|
|
|
}
|
|
|
|
|
|
void nmFractureLineGraphicsItem::createHandles()
|
|
|
{
|
|
|
// 使用裂缝手柄类
|
|
|
m_pHandle1 = new nmFractureLineHandleItem(this); // 以当前裂缝项为父项
|
|
|
m_pHandle2 = new nmFractureLineHandleItem(this);
|
|
|
|
|
|
// 连接手柄的信号到本类的槽函数
|
|
|
connect(m_pHandle1, SIGNAL(handlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)),
|
|
|
this, SLOT(onHandlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)));
|
|
|
connect(m_pHandle2, SIGNAL(handlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)),
|
|
|
this, SLOT(onHandlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)));
|
|
|
|
|
|
// 创建完隐藏手柄
|
|
|
m_pHandle1->setVisible(false);
|
|
|
m_pHandle2->setVisible(false);
|
|
|
}
|
|
|
|
|
|
void nmFractureLineGraphicsItem::updateHandlesPosition()
|
|
|
{
|
|
|
if(m_pHandle1 && m_pHandle2) {
|
|
|
// 因为手柄是子项,它们的pos是相对于父项(裂缝项)的
|
|
|
m_pHandle1->setPos(line().p1());
|
|
|
m_pHandle2->setPos(line().p2());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 槽函数的实现
|
|
|
void nmFractureLineGraphicsItem::onHandlePositionChanged(nmFractureLineHandleItem* handle, QPointF newPos, bool isDragging)
|
|
|
{
|
|
|
// 在这里直接调用更新裂缝的函数
|
|
|
if(m_bEditMode) {
|
|
|
updateFractureFromHandle(handle, newPos, isDragging);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 修改 updateFractureFromHandle 的参数类型
|
|
|
void nmFractureLineGraphicsItem::updateFractureFromHandle(nmFractureLineHandleItem* draggedHandle, const QPointF& newHandlePosInParent, bool isDragging)
|
|
|
{
|
|
|
// 因为 nmFractureLineGraphicsItem 的 pos() 已经被设置到井筒位置,所以这个 (0,0) 就是井筒。
|
|
|
QPointF fractureCenterInLocal = QPointF(0, 0);
|
|
|
QPointF p1_new_local, p2_new_local;
|
|
|
|
|
|
if(draggedHandle == m_pHandle1) {
|
|
|
p1_new_local = newHandlePosInParent;
|
|
|
QPointF vecToDragged = p1_new_local - fractureCenterInLocal;
|
|
|
p2_new_local = fractureCenterInLocal - vecToDragged;
|
|
|
} else { // draggedHandle == m_pHandle2
|
|
|
p2_new_local = newHandlePosInParent;
|
|
|
QPointF vecToDragged = p2_new_local - fractureCenterInLocal;
|
|
|
p1_new_local = fractureCenterInLocal - vecToDragged;
|
|
|
}
|
|
|
|
|
|
// 设置裂缝线和手柄位置(此部分应保持在 mouseMoveEvent 链中)
|
|
|
setLine(QLineF(p1_new_local, p2_new_local));
|
|
|
updateHandlesPosition();
|
|
|
|
|
|
// 拖动结束后发送信号
|
|
|
if(!isDragging) {
|
|
|
// 将局部坐标转换为场景坐标
|
|
|
QPointF fractureCenterInScene = mapToScene(fractureCenterInLocal);
|
|
|
QPointF p1_scene = mapToScene(p1_new_local);
|
|
|
|
|
|
// 计算半长
|
|
|
double newHalfLength = QLineF(fractureCenterInScene, p1_scene).length();
|
|
|
|
|
|
// 计算并归一化裂缝角度
|
|
|
double dx = p1_scene.x() - fractureCenterInScene.x();
|
|
|
double dy = p1_scene.y() - fractureCenterInScene.y();
|
|
|
|
|
|
// 使用 qAtan2 获得角度(从正X轴逆时针旋转)
|
|
|
double angleRadians = qAtan2(dy, dx);
|
|
|
double angleDegrees = angleRadians * 180.0 / M_PI;
|
|
|
|
|
|
// 将角度归一化到 [-90, 90] 的范围
|
|
|
// 这一步简化了你原有的复杂if/else结构,但实现了同样的效果
|
|
|
if(angleDegrees > 90) {
|
|
|
angleDegrees -= 180;
|
|
|
} else if(angleDegrees < -90) {
|
|
|
angleDegrees += 180;
|
|
|
}
|
|
|
|
|
|
double finalAngle = angleDegrees;
|
|
|
|
|
|
// 核心修改: 不再直接修改模型,而是发出信号
|
|
|
emit sigFractureGeometryChanged(newHalfLength, finalAngle);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nmHorizontalFractureGraphicsItem::nmHorizontalFractureGraphicsItem(int index, QGraphicsItem *parent)
|
|
|
: QObject(nullptr), // QObject初始化
|
|
|
QGraphicsLineItem(parent), // QGraphicsLineItem初始化
|
|
|
m_bEditMode(false),
|
|
|
m_fractureIndex(index)
|
|
|
{
|
|
|
// 设置裂缝线的样式
|
|
|
//QPen fracturePen(QColor("#1e90ff"), 3.0); // 裂缝线条颜色为 #1e90ff
|
|
|
//setPen(fracturePen);
|
|
|
QPen fracturePen(QColor("#1e90ff"), 3.0);
|
|
|
fracturePen.setCosmetic(true);
|
|
|
setPen(fracturePen);
|
|
|
|
|
|
// 创建手柄,以当前裂缝项为父项
|
|
|
m_pHandle1 = new nmFractureLineHandleItem(this);
|
|
|
m_pHandle2 = new nmFractureLineHandleItem(this);
|
|
|
|
|
|
// 连接手柄的信号到本类的槽函数
|
|
|
connect(m_pHandle1, SIGNAL(handlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)),
|
|
|
this, SLOT(onHandlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)));
|
|
|
connect(m_pHandle2, SIGNAL(handlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)),
|
|
|
this, SLOT(onHandlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)));
|
|
|
|
|
|
// 初始时手柄隐藏
|
|
|
m_pHandle1->setVisible(false);
|
|
|
m_pHandle2->setVisible(false);
|
|
|
setZValue(3); // 确保在井筒之上,手柄在裂缝之上
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFractureGraphicsItem::setStyle(const QPen& pen)
|
|
|
{
|
|
|
QPen cosmetic_pen = pen;
|
|
|
cosmetic_pen.setWidth(0);
|
|
|
cosmetic_pen.setCosmetic(true);
|
|
|
setPen(cosmetic_pen);
|
|
|
//setPen(pen);
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFractureGraphicsItem::updateFractureLineAndHandles(const QPointF& p1, const QPointF& p2)
|
|
|
{
|
|
|
setLine(QLineF(p1, p2));
|
|
|
|
|
|
if(m_pHandle1 && m_pHandle2) {
|
|
|
// 手柄的pos是相对于父项(裂缝线)的
|
|
|
// 所以直接设置到线的端点即可
|
|
|
m_pHandle1->setPos(p1);
|
|
|
m_pHandle2->setPos(p2);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFractureGraphicsItem::setWellboreInfo(const QPointF& wellboreStartScene, const QPointF& wellboreEndScene)
|
|
|
{
|
|
|
m_wellboreStartScene = wellboreStartScene;
|
|
|
m_wellboreEndScene = wellboreEndScene;
|
|
|
|
|
|
// 将 QPointF 转换为 QVector2D 进行归一化
|
|
|
QVector2D tempWellboreDir = QVector2D(wellboreEndScene - wellboreStartScene);
|
|
|
|
|
|
if(!tempWellboreDir.isNull()) { // 检查是否是零向量
|
|
|
m_wellboreDirection = tempWellboreDir.normalized().toPointF(); // 归一化后转换回 QPointF
|
|
|
} else {
|
|
|
m_wellboreDirection = QPointF(1.0, 0.0); // 默认一个方向
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFractureGraphicsItem::setEditMode(bool enable)
|
|
|
{
|
|
|
m_bEditMode = enable;
|
|
|
|
|
|
if(m_pHandle1 && m_pHandle2) {
|
|
|
m_pHandle1->setVisible(enable);
|
|
|
m_pHandle2->setVisible(enable);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFractureGraphicsItem::onHandlePositionChanged(nmFractureLineHandleItem* handle, QPointF newHandlePosInParent, bool isDragging)
|
|
|
{
|
|
|
if(!m_bEditMode) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 1. 获取裂缝中心点 (交点) 在父项(裂缝线)局部坐标系中的位置
|
|
|
QPointF currentFractureCenterInParent = (line().p1() + line().p2()) / 2.0;
|
|
|
|
|
|
// 将新的手柄位置从父项局部坐标转换为场景坐标
|
|
|
QPointF newHandleScenePos = mapToScene(newHandlePosInParent);
|
|
|
|
|
|
// 将裂缝中心点从父项局部坐标转换为场景坐标
|
|
|
QPointF fractureCenterScene = mapToScene(currentFractureCenterInParent);
|
|
|
|
|
|
// 2. 计算手柄新位置在垂直于井筒方向上的投影点
|
|
|
// 垂直于井筒的方向向量 (使用QPointF直接计算,然后转换为QVector2D进行归一化)
|
|
|
QVector2D rawPerpendicularWellboreDirection = QVector2D(-m_wellboreDirection.y(), m_wellboreDirection.x());
|
|
|
QVector2D perpendicularWellboreDirection = rawPerpendicularWellboreDirection;
|
|
|
|
|
|
if(perpendicularWellboreDirection.isNull()) {
|
|
|
perpendicularWellboreDirection = QVector2D(0.0, 1.0); // 避免零向量
|
|
|
}
|
|
|
|
|
|
perpendicularWellboreDirection = perpendicularWellboreDirection.normalized();
|
|
|
|
|
|
|
|
|
// 向量从裂缝中心到手柄新位置 (转换为QVector2D进行点积运算)
|
|
|
QVector2D vecCenterToHandle = QVector2D(newHandleScenePos - fractureCenterScene);
|
|
|
|
|
|
// 计算vecCenterToHandle在垂直于井筒方向上的投影长度
|
|
|
// 这个 projectionLength 应该带有方向性,表示手柄相对于裂缝中心沿着垂直方向的“位移”
|
|
|
double signedProjectionLength = QVector2D::dotProduct(vecCenterToHandle, perpendicularWellboreDirection);
|
|
|
|
|
|
// 新手柄的有效场景位置 (强制在垂直于井筒的方向上移动)
|
|
|
// 这里的 effectiveNewHandleScenePos 不再直接决定新的半长,而是作为计算端点的中间量
|
|
|
QPointF effectiveNewHandleScenePos = fractureCenterScene + perpendicularWellboreDirection.toPointF() * signedProjectionLength;
|
|
|
|
|
|
// 3. 计算新的裂缝半长
|
|
|
// newHalfLength 现在直接是带符号的投影长度
|
|
|
double newHalfLength = signedProjectionLength;
|
|
|
|
|
|
// 4. 更新图形项自身 (视觉更新)
|
|
|
QPointF p1_new_scene, p2_new_scene;
|
|
|
|
|
|
// 裂缝的两个端点应该分别位于中心点的两侧,距离中心点为 newHalfLength (有符号)
|
|
|
// 根据拖动的是哪个手柄来确定哪个方向是 "正" 半长方向
|
|
|
if(handle == m_pHandle1) { // 如果拖动的是手柄1 (裂缝的p1)
|
|
|
// 手柄1沿着 perpendicularWellboreDirection 的负方向移动
|
|
|
// 所以 newHalfLength 应该取其相反数
|
|
|
newHalfLength = -signedProjectionLength; // 使得 halfLength 总是指向 p2 的方向
|
|
|
} else { // handle == m_pHandle2 (拖动的是手柄2,裂缝的p2)
|
|
|
// 手柄2沿着 perpendicularWellboreDirection 的正方向移动
|
|
|
newHalfLength = signedProjectionLength; // halfLength 保持正值
|
|
|
}
|
|
|
|
|
|
// 计算新的裂缝端点 (场景坐标)
|
|
|
// 注意:裂缝的实际绘制长度是 newHalfLength 的绝对值 * 2
|
|
|
// 但在数据模型中,halfLength 通常是正值。
|
|
|
// 这里我们用 newHalfLength 表示从中心点到被拖动手柄的方向上的“半长”
|
|
|
// 另一个手柄则在反方向
|
|
|
|
|
|
// 裂缝的实际绘制长度(绝对值)
|
|
|
double actualDisplayHalfLength = qAbs(newHalfLength);
|
|
|
|
|
|
// 裂缝的两个端点基于裂缝中心和垂直方向向量
|
|
|
// p1 永远在 perpendicularWellboreDirection 的负方向
|
|
|
// p2 永远在 perpendicularWellboreDirection 的正方向
|
|
|
p1_new_scene = fractureCenterScene - perpendicularWellboreDirection.toPointF() * actualDisplayHalfLength;
|
|
|
p2_new_scene = fractureCenterScene + perpendicularWellboreDirection.toPointF() * actualDisplayHalfLength;
|
|
|
|
|
|
// 如果拖动的是 p1,那么 newHalfLength 应该以 p1 到中心点的距离为准,但数据模型存储的是正的半长。
|
|
|
// 如果是拖动 p1 并且 newHalfLength < 0 (即 p1 移动到原来 p2 的位置),需要反转
|
|
|
// 否则如果拖动 p2 且 newHalfLength < 0,也需要反转
|
|
|
|
|
|
// 最终向数据模型报告的半长必须是正值
|
|
|
double finalHalfLengthForDataModel = qAbs(newHalfLength);
|
|
|
|
|
|
// 将场景坐标转换回局部坐标并设置线段
|
|
|
QPointF p1_local = mapFromScene(p1_new_scene);
|
|
|
QPointF p2_local = mapFromScene(p2_new_scene);
|
|
|
setLine(QLineF(p1_local, p2_local));
|
|
|
|
|
|
// 5. 更新手柄位置以匹配新的线段端点
|
|
|
updateFractureLineAndHandles(p1_local, p2_local); // 更新手柄视觉位置
|
|
|
|
|
|
// 6. 发出信号通知父项(nmHorizontalFracturedWellTopViewItem)
|
|
|
// !!!这里发出的半长应该是正值,表示实际的几何长度
|
|
|
emit fractureHalfLengthChanged(finalHalfLengthForDataModel, isDragging, m_fractureIndex);
|
|
|
}
|
|
|
|
|
|
nmFractureRectHandleItem::nmFractureRectHandleItem(HandleType type, QGraphicsItem *parent)
|
|
|
: QGraphicsRectItem(parent), m_eHandleType(type)
|
|
|
{
|
|
|
// 设置手柄的颜色和样式
|
|
|
setBrush(QColor("#808080")); // 灰色填充
|
|
|
setPen(Qt::NoPen); // 无边框
|
|
|
setRect(-5, -5, 10, 10);
|
|
|
// 设置手柄的Z值,确保它在最前面。
|
|
|
setZValue(5);
|
|
|
}
|
|
|
|
|
|
nmFractureRectHandleItem::~nmFractureRectHandleItem()
|
|
|
{
|
|
|
// 析构函数,目前无需特殊清理
|
|
|
}
|
|
|
|
|
|
// 获取手柄类型
|
|
|
HandleType nmFractureRectHandleItem::getHandleType()
|
|
|
{
|
|
|
return m_eHandleType;
|
|
|
}
|
|
|
|
|
|
void nmFractureRectHandleItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 当鼠标左键按下时
|
|
|
if(event->button() == Qt::LeftButton) {
|
|
|
// 只有上下方向的手柄(包括角上的)才触发拖动
|
|
|
if(m_eHandleType == TopLeftHandle ||
|
|
|
m_eHandleType == TopMiddleHandle ||
|
|
|
m_eHandleType == TopRightHandle ||
|
|
|
m_eHandleType == BottomLeftHandle ||
|
|
|
m_eHandleType == BottomMiddleHandle ||
|
|
|
m_eHandleType == BottomRightHandle) {
|
|
|
m_lastMouseScenePos = event->scenePos(); // 记录鼠标在场景坐标系下的按下位置
|
|
|
// 发送信号通知父项(nmFractureRectGraphicsItem)手柄被按下
|
|
|
emit handlePressed(m_eHandleType, m_lastMouseScenePos);
|
|
|
// 仅接受左键事件,防止其他鼠标按钮产生副作用
|
|
|
setAcceptedMouseButtons(Qt::LeftButton);
|
|
|
}
|
|
|
|
|
|
// 如果是 MiddleLeftHandle 或 MiddleRightHandle,这里不发送信号,也不设置 acceptedMouseButtons
|
|
|
// 这样它们就不会进入“拖动”状态,也不会影响父项的 m_eCurrentHandle
|
|
|
}
|
|
|
|
|
|
// 调用基类的鼠标按下事件处理函数
|
|
|
//QGraphicsRectItem::mousePressEvent(event); // 仍然调用基类,以处理可能的默认行为(如选择)
|
|
|
}
|
|
|
|
|
|
void nmFractureRectHandleItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 当鼠标左键被按住并移动时
|
|
|
if(event->buttons() & Qt::LeftButton) {
|
|
|
// 只有上下方向的手柄才发送移动信号
|
|
|
if(m_eHandleType == TopLeftHandle ||
|
|
|
m_eHandleType == TopMiddleHandle ||
|
|
|
m_eHandleType == TopRightHandle ||
|
|
|
m_eHandleType == BottomLeftHandle ||
|
|
|
m_eHandleType == BottomMiddleHandle ||
|
|
|
m_eHandleType == BottomRightHandle) {
|
|
|
// 发送信号通知父项手柄正在移动,并传递当前鼠标的场景坐标
|
|
|
emit handleMoved(m_eHandleType, event->scenePos());
|
|
|
m_lastMouseScenePos = event->scenePos(); // 更新上一次鼠标位置
|
|
|
}
|
|
|
|
|
|
// 如果是 MiddleLeftHandle 或 MiddleRightHandle,则不发送 handleMoved 信号
|
|
|
}
|
|
|
|
|
|
// 调用基类的鼠标移动事件处理函数
|
|
|
//QGraphicsRectItem::mouseMoveEvent(event);
|
|
|
}
|
|
|
|
|
|
void nmFractureRectHandleItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
// 只有上下方向的手柄才发送释放信号
|
|
|
if(m_eHandleType == TopLeftHandle ||
|
|
|
m_eHandleType == TopMiddleHandle ||
|
|
|
m_eHandleType == TopRightHandle ||
|
|
|
m_eHandleType == BottomLeftHandle ||
|
|
|
m_eHandleType == BottomMiddleHandle ||
|
|
|
m_eHandleType == BottomRightHandle) {
|
|
|
// 当鼠标左键释放时,所有手柄都应发送释放信号,以便父项重置 m_eCurrentHandle
|
|
|
emit handleReleased(m_eHandleType, event->scenePos());
|
|
|
// 恢复接受左键事件
|
|
|
setAcceptedMouseButtons(Qt::LeftButton);
|
|
|
}
|
|
|
|
|
|
// 调用基类的鼠标释放事件处理函数
|
|
|
//QGraphicsRectItem::mouseReleaseEvent(event);
|
|
|
}
|
|
|
|
|
|
void nmFractureRectHandleItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
{
|
|
|
Q_UNUSED(option);
|
|
|
Q_UNUSED(widget);
|
|
|
|
|
|
// 1. 获取视图缩放比例
|
|
|
if (!scene() || scene()->views().isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
QGraphicsView* view = scene()->views().first();
|
|
|
if (!view) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
if (scaleY < 1e-6) { // 避免除零
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 2. 定义固定的像素大小
|
|
|
const qreal fixedHandleSizeInPixels = 15.0; // 调整像素大小
|
|
|
|
|
|
// 3. 根据缩放比例计算动态大小
|
|
|
const qreal dynamicHandleSize = fixedHandleSizeInPixels / scaleY;
|
|
|
|
|
|
// 4. 定义手柄的矩形区域 (以 (0,0) 为中心)
|
|
|
QRectF handleRect(-dynamicHandleSize / 2, -dynamicHandleSize / 2, dynamicHandleSize, dynamicHandleSize);
|
|
|
|
|
|
// 5. 绘制
|
|
|
painter->setPen(Qt::NoPen); // 保持无边框
|
|
|
painter->setBrush(QColor("#808080")); // 保持灰色填充
|
|
|
painter->drawRect(handleRect); // 绘制动态计算的矩形
|
|
|
}
|
|
|
|
|
|
QRectF nmFractureRectHandleItem::boundingRect() const
|
|
|
{
|
|
|
// 1. 获取视图缩放比例
|
|
|
if (!scene() || scene()->views().isEmpty()) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
QGraphicsView* view = scene()->views().first();
|
|
|
if (!view) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
const QTransform viewTransform = view->transform();
|
|
|
const qreal scaleY = qAbs(viewTransform.m22());
|
|
|
|
|
|
if (scaleY < 1e-6) {
|
|
|
return QRectF();
|
|
|
}
|
|
|
|
|
|
// 2. 使用与 paint 方法相同的动态大小计算
|
|
|
const qreal fixedHandleSizeInPixels = 15.0; // 必须与 paint 中的值一致
|
|
|
const qreal dynamicHandleSize = fixedHandleSizeInPixels / scaleY;
|
|
|
|
|
|
// 3. 返回动态计算的矩形
|
|
|
return QRectF(-dynamicHandleSize / 2, -dynamicHandleSize / 2,
|
|
|
dynamicHandleSize, dynamicHandleSize);
|
|
|
}
|
|
|
|
|
|
nmFractureRectGraphicsItem::nmFractureRectGraphicsItem(nmDataVerticalFracturedWell* pDataWell, QGraphicsItem *parent)
|
|
|
: m_pDataWell(pDataWell),
|
|
|
QGraphicsItem(parent),
|
|
|
m_bEditMode(false),
|
|
|
m_handleSize(18.0),
|
|
|
m_eCurrentHandle(NoHandle)
|
|
|
|
|
|
{
|
|
|
// 设置Z值,确保裂缝矩形在储层之上,射孔之下
|
|
|
setZValue(-2);
|
|
|
|
|
|
// 获取储层的边界信息
|
|
|
m_minReservoirTop = nmDataAnalyzeManager::getCurrentInstance()->getMinLayerTop();
|
|
|
m_maxReservoirBottom = nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
|
|
|
|
|
|
// 初始更新裂缝几何信息。手柄的创建和销毁与编辑模式关联。
|
|
|
updateFractureGeometry();
|
|
|
}
|
|
|
|
|
|
nmFractureRectGraphicsItem::~nmFractureRectGraphicsItem()
|
|
|
{
|
|
|
// 析构函数中销毁所有创建的手柄子项,防止内存泄漏
|
|
|
destroyHandles();
|
|
|
}
|
|
|
|
|
|
QRectF nmFractureRectGraphicsItem::boundingRect() const
|
|
|
{
|
|
|
// 边界矩形只包含裂缝矩形本身。
|
|
|
// 手柄是独立的子项,它们有自己的boundingRect,QGraphicsItem 不会自动包含子项的边界。
|
|
|
return m_fractureRect.normalized();
|
|
|
}
|
|
|
|
|
|
void nmFractureRectGraphicsItem::paint(QPainter *painter,
|
|
|
const QStyleOptionGraphicsItem *option,
|
|
|
QWidget *widget)
|
|
|
{
|
|
|
Q_UNUSED(option); // 避免编译器警告,表示此参数未使用
|
|
|
Q_UNUSED(widget); // 避免编译器警告
|
|
|
|
|
|
painter->setRenderHint(QPainter::Antialiasing); // 开启抗锯齿,使绘制更平滑
|
|
|
|
|
|
// 绘制裂缝矩形
|
|
|
//painter->setPen(QPen(QColor("#0c2e14"), 1.0)); // 绘制边框
|
|
|
//painter->setBrush(QColor("#0000ff")); // 设置填充颜色为蓝色
|
|
|
//painter->drawRect(m_fractureRect); // 绘制裂缝矩形
|
|
|
QPen fracturePen(QColor("#0c2e14"), 1.0);
|
|
|
fracturePen.setCosmetic(true);
|
|
|
painter->setPen(fracturePen);
|
|
|
painter->setBrush(QColor("#0000ff"));
|
|
|
painter->drawRect(m_fractureRect);
|
|
|
}
|
|
|
|
|
|
void nmFractureRectGraphicsItem::setEditMode(bool bEdit)
|
|
|
{
|
|
|
// 如果编辑模式状态发生改变
|
|
|
if(m_bEditMode != bEdit) {
|
|
|
m_bEditMode = bEdit; // 更新编辑模式状态
|
|
|
|
|
|
if(m_bEditMode) {
|
|
|
createHandles(); // 如果进入编辑模式,则创建并显示手柄
|
|
|
} else {
|
|
|
destroyHandles(); // 如果退出编辑模式,则销毁手柄
|
|
|
}
|
|
|
|
|
|
update(); // 请求重新绘制图元,以反映手柄的显示/隐藏状态
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 槽函数实现:当手柄被按下时
|
|
|
void nmFractureRectGraphicsItem::onHandlePressed(HandleType type, QPointF scenePos)
|
|
|
{
|
|
|
m_eCurrentHandle = type; // 记录当前正在拖动的手柄类型
|
|
|
m_lastMouseScenePos = scenePos; // 记录鼠标在场景坐标系中的位置
|
|
|
}
|
|
|
|
|
|
// 槽函数实现:当手柄被拖动时
|
|
|
void nmFractureRectGraphicsItem::onHandleMoved(HandleType type, QPointF scenePos)
|
|
|
{
|
|
|
if(m_eCurrentHandle == NoHandle) return; // 如果没有手柄被激活拖动,则直接返回
|
|
|
|
|
|
prepareGeometryChange(); // 通知 QGraphicsItem 框架几何形状即将改变,以便更新场景
|
|
|
|
|
|
qreal dy = scenePos.y() - m_lastMouseScenePos.y(); // 计算鼠标在垂直方向上的位移
|
|
|
|
|
|
qreal newY = m_fractureRect.y(); // 裂缝新的顶部Y坐标
|
|
|
qreal newHeight = m_fractureRect.height(); // 裂缝新的高度
|
|
|
|
|
|
// 根据当前拖动的手柄类型,调整裂缝的高度和/或位置
|
|
|
switch(m_eCurrentHandle) {
|
|
|
case TopLeftHandle:
|
|
|
case TopMiddleHandle:
|
|
|
case TopRightHandle:
|
|
|
// 拖动顶部手柄,顶部Y坐标改变,高度随之变化
|
|
|
newY += dy;
|
|
|
newHeight -= dy;
|
|
|
break;
|
|
|
|
|
|
case BottomLeftHandle:
|
|
|
case BottomMiddleHandle:
|
|
|
case BottomRightHandle:
|
|
|
// 拖动底部手柄,高度改变,顶部Y坐标不变
|
|
|
newHeight += dy;
|
|
|
break;
|
|
|
|
|
|
case MiddleLeftHandle:
|
|
|
case MiddleRightHandle:
|
|
|
// 左右两个手柄在垂直方向上不起作用。
|
|
|
// 在本例中,裂缝的半长(宽度)是由 nmDataVerticalFracturedWell 固定的,
|
|
|
// 因此这里也不处理水平方向的缩放。
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
// --- 应用储层高度限制 ---
|
|
|
qreal potentialTop = newY; // 潜在的裂缝顶部Y坐标
|
|
|
qreal potentialBottom = newY + newHeight; // 潜在的裂缝底部Y坐标
|
|
|
|
|
|
// 1. 如果潜在的新顶部超出了储层顶部边界
|
|
|
if(potentialTop < m_minReservoirTop) {
|
|
|
newY = m_minReservoirTop; // 裂缝顶部固定在储层顶部
|
|
|
newHeight = potentialBottom - newY; // 重新计算高度
|
|
|
}
|
|
|
|
|
|
// 2. 如果潜在的新底部超出了储层底部边界
|
|
|
if(newY + newHeight > m_maxReservoirBottom) {
|
|
|
newHeight = m_maxReservoirBottom - newY; // 重新计算高度
|
|
|
}
|
|
|
|
|
|
// 3. 确保裂缝高度不小于最小值
|
|
|
if(newHeight < 1.0) { // 例如,最小高度设置为1.0像素
|
|
|
newHeight = 1.0;
|
|
|
|
|
|
// 如果是从顶部缩小,且达到了最小高度,则调整 Y 坐标以保持底部不变
|
|
|
if(m_eCurrentHandle == TopLeftHandle || m_eCurrentHandle == TopMiddleHandle ||
|
|
|
m_eCurrentHandle == TopRightHandle) {
|
|
|
newY = m_fractureRect.bottom() - newHeight;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 在拖动过程中,只更新矩形数据,不应用任何业务逻辑(如边界限制) -->
|
|
|
// 只进行实时视觉反馈,不修改模型,也不发送信号。
|
|
|
m_fractureRect.setRect(m_fractureRect.x(), newY, m_fractureRect.width(), newHeight);
|
|
|
|
|
|
|
|
|
updateHandlePositions(); // 裂缝矩形改变后,重新定位所有手柄
|
|
|
m_lastMouseScenePos = scenePos; // 更新上一次鼠标的场景坐标
|
|
|
update(); // 请求重绘整个裂缝矩形图元及其子项(手柄)
|
|
|
|
|
|
// 拖动过程中实时发送裂缝位置
|
|
|
emit sigFractureGeometryChanged(m_fractureRect.top(), m_fractureRect.bottom());
|
|
|
}
|
|
|
|
|
|
// 槽函数实现:当手柄被释放时
|
|
|
void nmFractureRectGraphicsItem::onHandleReleased(HandleType type, QPointF scenePos)
|
|
|
{
|
|
|
// 1. 重置当前拖动的手柄,表示拖动结束
|
|
|
m_eCurrentHandle = NoHandle;
|
|
|
|
|
|
// 2. 发送最终的、经过视图修正后的 MD 值给控制器
|
|
|
// 注意,这里传递的是 m_fractureRect.top() 和 m_fractureRect.bottom(),
|
|
|
// 这些值已经经过了上面的实时修正,所以不会超出视图范围。
|
|
|
// 但是控制器仍然需要再次检查,以确保数据的最终一致性。
|
|
|
emit sigRequestFractureRectUpdate(m_fractureRect.top(), m_fractureRect.bottom());
|
|
|
|
|
|
}
|
|
|
|
|
|
void nmFractureRectGraphicsItem::updateFractureGeometry()
|
|
|
{
|
|
|
if(!m_pDataWell)
|
|
|
return;
|
|
|
|
|
|
// 从数据模型中获取裂缝的相关几何数据
|
|
|
double dWellboreX = m_pDataWell->getX().getValue().toDouble(); // 井的X坐标
|
|
|
double dFHalfLength = m_pDataWell->getFractureHalfLength().getValue().toDouble(); // 裂缝半长
|
|
|
double dFHeight = m_pDataWell->getFractureHeight().getValue().toDouble(); // 裂缝高度
|
|
|
double dMidPointHeight = m_pDataWell->getFractureMidPointHeight().getValue().toDouble(); // 裂缝中点高度
|
|
|
|
|
|
// 裂缝的纵向定位
|
|
|
qreal fractureTopY = m_maxReservoirBottom - dMidPointHeight - dFHeight / 2;
|
|
|
|
|
|
// 设置裂缝矩形的位置和尺寸
|
|
|
// X坐标:井中心X - 裂缝半长
|
|
|
// Y坐标:m_minReservoirTop (矩形顶部Y坐标)
|
|
|
// 宽度:裂缝半长 * 2
|
|
|
// 高度:裂缝高度
|
|
|
m_fractureRect.setRect(dWellboreX - dFHalfLength,
|
|
|
fractureTopY,
|
|
|
dFHalfLength * 2.0, // 裂缝宽度 = 裂缝半长 * 2
|
|
|
dFHeight); // 裂缝高度
|
|
|
}
|
|
|
|
|
|
void nmFractureRectGraphicsItem::updateHandlePositions()
|
|
|
{
|
|
|
qreal x = m_fractureRect.x(); // 裂缝矩形的X坐标
|
|
|
qreal y = m_fractureRect.y(); // 裂缝矩形的Y坐标
|
|
|
qreal w = m_fractureRect.width(); // 裂缝矩形的宽度
|
|
|
qreal h = m_fractureRect.height(); // 裂缝矩形的高度
|
|
|
|
|
|
// 我们的手柄在它们的局部坐标系中是 (0,0) 居中的。
|
|
|
// 我们只需要把它们的 `pos()` 设置到父坐标系中的正确位置。
|
|
|
foreach(nmFractureRectHandleItem *handle, m_handles) {
|
|
|
|
|
|
QPointF handlePos; // 用于存储手柄在父项坐标系中的 *中心点* 位置
|
|
|
|
|
|
switch(handle->getHandleType()) {
|
|
|
case TopLeftHandle:
|
|
|
handlePos.setX(x);
|
|
|
handlePos.setY(y);
|
|
|
break;
|
|
|
|
|
|
case TopMiddleHandle:
|
|
|
handlePos.setX(x + w / 2.0);
|
|
|
handlePos.setY(y);
|
|
|
break;
|
|
|
|
|
|
case TopRightHandle:
|
|
|
handlePos.setX(x + w);
|
|
|
handlePos.setY(y);
|
|
|
break;
|
|
|
|
|
|
case MiddleLeftHandle:
|
|
|
handlePos.setX(x);
|
|
|
handlePos.setY(y + h / 2.0);
|
|
|
break;
|
|
|
|
|
|
case MiddleRightHandle:
|
|
|
handlePos.setX(x + w);
|
|
|
handlePos.setY(y + h / 2.0);
|
|
|
break;
|
|
|
|
|
|
case BottomLeftHandle:
|
|
|
handlePos.setX(x);
|
|
|
handlePos.setY(y + h);
|
|
|
break;
|
|
|
|
|
|
case BottomMiddleHandle:
|
|
|
handlePos.setX(x + w / 2.0);
|
|
|
handlePos.setY(y + h);
|
|
|
break;
|
|
|
|
|
|
case BottomRightHandle:
|
|
|
handlePos.setX(x + w);
|
|
|
handlePos.setY(y + h);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
handle->setPos(handlePos);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//void nmFractureRectGraphicsItem::updateHandlePositions()
|
|
|
//{
|
|
|
// qreal x = m_fractureRect.x(); // 裂缝矩形的X坐标
|
|
|
// qreal y = m_fractureRect.y(); // 裂缝矩形的Y坐标
|
|
|
// qreal w = m_fractureRect.width(); // 裂缝矩形的宽度
|
|
|
// qreal h = m_fractureRect.height(); // 裂缝矩形的高度
|
|
|
//
|
|
|
// qreal hs = m_handleSize / 2.0; // 手柄半宽/高,用于在中心定位手柄
|
|
|
//
|
|
|
// // 遍历所有手柄,并根据其类型设置其在裂缝矩形本地坐标系中的位置和尺寸
|
|
|
// foreach(nmFractureRectHandleItem *handle, m_handles) {
|
|
|
// QRectF handleRect;
|
|
|
//
|
|
|
// switch(handle->getHandleType()) {
|
|
|
// case TopLeftHandle:
|
|
|
// handleRect.setRect(x - hs, y - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case TopMiddleHandle:
|
|
|
// handleRect.setRect(x + w / 2.0 - hs, y - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case TopRightHandle:
|
|
|
// handleRect.setRect(x + w - hs, y - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case MiddleLeftHandle:
|
|
|
// handleRect.setRect(x - hs, y + h / 2.0 - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case MiddleRightHandle:
|
|
|
// handleRect.setRect(x + w - hs, y + h / 2.0 - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case BottomLeftHandle:
|
|
|
// handleRect.setRect(x - hs, y + h - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case BottomMiddleHandle:
|
|
|
// handleRect.setRect(x + w / 2.0 - hs, y + h - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// case BottomRightHandle:
|
|
|
// handleRect.setRect(x + w - hs, y + h - hs, m_handleSize, m_handleSize);
|
|
|
// break;
|
|
|
//
|
|
|
// default:
|
|
|
// break;
|
|
|
// }
|
|
|
//
|
|
|
// handle->setRect(handleRect); // 设置手柄的矩形,QGraphicsRectItem 会自动根据其 rect 绘制
|
|
|
// }
|
|
|
//}
|
|
|
|
|
|
void nmFractureRectGraphicsItem::createHandles()
|
|
|
{
|
|
|
destroyHandles(); // 在创建新手柄之前,先确保销毁所有旧手柄
|
|
|
|
|
|
// 获取 nmFractureRectGraphicsItem 的父项,也就是 nmVerticalFracturedWellCrossSectionItem
|
|
|
// 确保这个父项存在且是一个 QGraphicsItem
|
|
|
QGraphicsItem* pRootParent = this->parentItem();
|
|
|
|
|
|
if(!pRootParent) {
|
|
|
qWarning() << "nmFractureRectGraphicsItem has no parent item. Handles might not be visible.";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 按照 HandleType 枚举的顺序创建八个手柄
|
|
|
// 将 nmFractureRectGraphicsItem 的父项作为手柄的父项
|
|
|
m_handles.append(new nmFractureRectHandleItem(TopLeftHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(TopMiddleHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(TopRightHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(MiddleLeftHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(MiddleRightHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(BottomLeftHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(BottomMiddleHandle, pRootParent));
|
|
|
m_handles.append(new nmFractureRectHandleItem(BottomRightHandle, pRootParent));
|
|
|
|
|
|
// 连接每个手柄的信号到当前类的槽函数,以便响应手柄的拖动事件
|
|
|
foreach(nmFractureRectHandleItem *handle, m_handles) {
|
|
|
connect(handle, SIGNAL(handlePressed(HandleType, QPointF)),
|
|
|
this, SLOT(onHandlePressed(HandleType, QPointF)));
|
|
|
connect(handle, SIGNAL(handleMoved(HandleType, QPointF)),
|
|
|
this, SLOT(onHandleMoved(HandleType, QPointF)));
|
|
|
connect(handle, SIGNAL(handleReleased(HandleType, QPointF)),
|
|
|
this, SLOT(onHandleReleased(HandleType, QPointF)));
|
|
|
}
|
|
|
|
|
|
updateHandlePositions(); // 更新手柄的位置,使其与裂缝矩形对齐
|
|
|
}
|
|
|
|
|
|
void nmFractureRectGraphicsItem::destroyHandles()
|
|
|
{
|
|
|
qDeleteAll(m_handles); // 删除 QList 中所有 nmFractureRectHandleItem 对象
|
|
|
m_handles.clear(); // 清空 QList,使其不包含任何指针
|
|
|
}
|
|
|
|
|
|
|
|
|
nmReservoirGraphicsItems::nmReservoirGraphicsItems(QGraphicsItem *parent)
|
|
|
: QGraphicsItem(parent)
|
|
|
{
|
|
|
// 设置Z值,确保它总是在所有其他井筒图元的下方绘制
|
|
|
setZValue(-1000); // 确保它是最底层的背景图元
|
|
|
|
|
|
// 获取储层数据
|
|
|
QVector<nmDataLayer*> vecLayers = nmDataAnalyzeManager::getCurrentInstance()->getLayers();
|
|
|
this->setReservoirLayers(vecLayers);
|
|
|
}
|
|
|
|
|
|
nmReservoirGraphicsItems::~nmReservoirGraphicsItems()
|
|
|
{
|
|
|
qDeleteAll(m_internalLayers);
|
|
|
m_internalLayers.clear();
|
|
|
}
|
|
|
|
|
|
QRectF nmReservoirGraphicsItems::boundingRect() const
|
|
|
{
|
|
|
double dMinTop = nmDataAnalyzeManager::getCurrentInstance()->getMinLayerTop();
|
|
|
double dMaxBottom = nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
|
|
|
return QRectF(-100000, dMinTop, 200000, dMaxBottom - dMinTop);
|
|
|
//return QRectF(-50, dMinTop, 100, dMaxBottom - dMinTop);
|
|
|
}
|
|
|
|
|
|
void nmReservoirGraphicsItems::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
|
|
{
|
|
|
if(!option) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
painter->setRenderHint(QPainter::Antialiasing);
|
|
|
// visibleRect 表示当前需要绘制的区域(在 Item 的本地坐标系中)
|
|
|
QRectF visibleRect = option->exposedRect;
|
|
|
|
|
|
// 如果没有层数据,则什么都不画
|
|
|
if(m_internalLayers.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 定义绘制层的水平范围,使其覆盖当前可见区域的整个宽度
|
|
|
// 这样层在水平方向上就会看起来“无限大”
|
|
|
qreal layerXStart = visibleRect.left();
|
|
|
qreal layerXEnd = visibleRect.right();
|
|
|
qreal currentDrawWidth = layerXEnd - layerXStart; // 根据当前可见区域计算宽度
|
|
|
|
|
|
double dMinTop = nmDataAnalyzeManager::getCurrentInstance()->getMinLayerTop();
|
|
|
double dMaxBottom = nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
|
|
|
|
|
|
// 1. 绘制内部储层
|
|
|
for(int i = 0; i < m_internalLayers.size(); ++i) {
|
|
|
const nmDataLayer* layer = m_internalLayers.at(i);
|
|
|
|
|
|
if(!layer) {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
qreal topDepth = layer->getTop();
|
|
|
qreal bottomDepth = layer->getBottom();
|
|
|
QColor fillColor = layer->getColor();
|
|
|
|
|
|
if(topDepth >= bottomDepth) {
|
|
|
continue; // 无效的层
|
|
|
}
|
|
|
|
|
|
// 绘制填充矩形,其宽度为当前可见区域的宽度
|
|
|
painter->setPen(Qt::NoPen);
|
|
|
painter->setBrush(fillColor);
|
|
|
painter->drawRect(layerXStart, topDepth, currentDrawWidth, bottomDepth - topDepth);
|
|
|
|
|
|
//// 绘制层与层之间的分隔线
|
|
|
//if(i < m_internalLayers.size() - 1) {
|
|
|
// painter->setPen(QPen(Qt::black, 0.5)); // 细线
|
|
|
// painter->drawLine(layerXStart, bottomDepth, layerXEnd, bottomDepth);
|
|
|
//}
|
|
|
}
|
|
|
|
|
|
// 2. 绘制最外层的上下边框 (现在它们就是 m_minLayerTop 和 m_maxLayerBottom)
|
|
|
//QColor outerBorderColor = QColor("#8b4513");
|
|
|
//qreal outerBorderPenWidth = 5.0;
|
|
|
|
|
|
//// 绘制最顶部的边框 (对应 m_minLayerTop)
|
|
|
//painter->setPen(QPen(outerBorderColor, outerBorderPenWidth));
|
|
|
//// 边框也应覆盖当前可见区域的整个宽度
|
|
|
//painter->drawLine(layerXStart, dMinTop, layerXEnd, dMinTop);
|
|
|
|
|
|
//// 绘制最底部的边框 (对应 m_maxLayerBottom)
|
|
|
//painter->setPen(QPen(outerBorderColor, outerBorderPenWidth));
|
|
|
//// 边框也应覆盖当前可见区域的整个宽度
|
|
|
//painter->drawLine(layerXStart, dMaxBottom, layerXEnd, dMaxBottom);
|
|
|
|
|
|
// 修改边框绘制
|
|
|
QColor outerBorderColor = QColor("#8b4513");
|
|
|
QPen outerBorderPen(outerBorderColor, 5); // 线宽0表示1像素
|
|
|
outerBorderPen.setCosmetic(true); // 不随变换缩放
|
|
|
|
|
|
painter->setPen(outerBorderPen);
|
|
|
painter->drawLine(layerXStart, dMinTop, layerXEnd, dMinTop);
|
|
|
painter->drawLine(layerXStart, dMaxBottom, layerXEnd, dMaxBottom);
|
|
|
}
|
|
|
|
|
|
qreal nmReservoirGraphicsItems::getMinLayerTop()
|
|
|
{
|
|
|
return nmDataAnalyzeManager::getCurrentInstance()->getMinLayerTop();
|
|
|
}
|
|
|
|
|
|
qreal nmReservoirGraphicsItems::getMaxLayerBottom()
|
|
|
{
|
|
|
return nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
|
|
|
}
|
|
|
|
|
|
//void nmReservoirGraphicsItems::setReservoirLayers(const QVector<nmDataLayer*>& layers)
|
|
|
//{
|
|
|
// qDeleteAll(m_internalLayers);
|
|
|
// m_internalLayers.clear();
|
|
|
//
|
|
|
// for(int i = 0; i < layers.size(); ++i) {
|
|
|
// nmDataLayer* layer = layers.at(i);
|
|
|
//
|
|
|
// if(layer) {
|
|
|
// m_internalLayers.append(static_cast<nmDataLayer*>(layer->clone()));
|
|
|
// }
|
|
|
// }
|
|
|
//
|
|
|
// prepareGeometryChange();
|
|
|
// update();
|
|
|
//}
|
|
|
|
|
|
void nmReservoirGraphicsItems::setReservoirLayers(const QVector<nmDataLayer*>& layers)
|
|
|
{
|
|
|
qDeleteAll(m_internalLayers);
|
|
|
m_internalLayers.clear();
|
|
|
|
|
|
for (int i = 0; i < layers.size(); ++i) {
|
|
|
nmDataLayer* layer = layers.at(i);
|
|
|
if (layer) {
|
|
|
// 直接用拷贝构造做深拷贝
|
|
|
m_internalLayers.append(new nmDataLayer(*layer));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
prepareGeometryChange();
|
|
|
update();
|
|
|
}
|
|
|
|
|
|
nmAbstractWellboreVisual::nmAbstractWellboreVisual(QGraphicsItem *parent)
|
|
|
: QGraphicsItemGroup(parent), m_pDataWell(nullptr)
|
|
|
{
|
|
|
// 可以在这里设置一些通用的标志,例如:可选择、可移动等
|
|
|
// setFlags(ItemIsSelectable);
|
|
|
|
|
|
setHandlesChildEvents(false); // 关键:组不处理子项事件
|
|
|
//setFlag(QGraphicsItem::ItemIsMovable, false); // 组本身不可移动
|
|
|
}
|
|
|
|
|
|
// 析构函数实现
|
|
|
nmAbstractWellboreVisual::~nmAbstractWellboreVisual()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
nmDataWellBase* nmAbstractWellboreVisual::getWellData()
|
|
|
{
|
|
|
return m_pDataWell;
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreVisual::removePerforationVisual(nmPerforationGraphicsItem* itemToRemove)
|
|
|
{
|
|
|
if(!itemToRemove) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_listPerforations.removeOne(itemToRemove); // 查找并移除指针
|
|
|
|
|
|
// 删除图形项对象,这会自动将其从场景和其父QGraphicsItemGroup中移除
|
|
|
delete itemToRemove;
|
|
|
itemToRemove = nullptr; // 避免悬空指针
|
|
|
|
|
|
// 通知视图几何形状可能已改变,触发重绘
|
|
|
prepareGeometryChange();
|
|
|
update();
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreVisual::clearAllPerforationItems()
|
|
|
{
|
|
|
// 移除并删除射孔段图元
|
|
|
foreach(nmPerforationGraphicsItem* item, m_listPerforations) {
|
|
|
delete item;
|
|
|
}
|
|
|
|
|
|
m_listPerforations.clear();
|
|
|
}
|
|
|
|
|
|
// 清除所有子项的通用方法
|
|
|
void nmAbstractWellboreVisual::clearAllChildItems()
|
|
|
{
|
|
|
// 遍历当前组的所有子项
|
|
|
foreach(QGraphicsItem *child, childItems()) {
|
|
|
removeFromGroup(child); // 从当前组中移除该子项
|
|
|
delete child; // 删除该子项(因为它们是new出来的)
|
|
|
child = nullptr;
|
|
|
}
|
|
|
|
|
|
// 清空内部的跟踪列表
|
|
|
m_listPoints.clear();
|
|
|
m_listLines.clear();
|
|
|
m_listPerforations.clear();
|
|
|
m_listLineFractures.clear();
|
|
|
m_listRectFractures.clear();
|
|
|
}
|
|
|
|
|
|
// 辅助函数:将 nmWellborePointGraphicsItem 添加到组中并记录
|
|
|
void nmAbstractWellboreVisual::addPoint(nmWellborePointGraphicsItem* item)
|
|
|
{
|
|
|
addToGroup(item); // 将项添加到QGraphicsItemGroup中
|
|
|
m_listPoints.append(item); // 添加到内部跟踪列表
|
|
|
}
|
|
|
|
|
|
// 辅助函数:将 nmWellboreLineGraphicsItem 添加到组中并记录
|
|
|
void nmAbstractWellboreVisual::addLine(nmWellboreLineGraphicsItem* item)
|
|
|
{
|
|
|
addToGroup(item);
|
|
|
m_listLines.append(item);
|
|
|
}
|
|
|
|
|
|
// 辅助函数:将 nmPerforationGraphicsItem 添加到组中并记录
|
|
|
void nmAbstractWellboreVisual::addPerforation(nmPerforationGraphicsItem* item)
|
|
|
{
|
|
|
addToGroup(item);
|
|
|
m_listPerforations.append(item);
|
|
|
}
|
|
|
|
|
|
// 辅助函数:将 nmFractureLineGraphicsItem 添加到组中并记录
|
|
|
void nmAbstractWellboreVisual::addFractureLine(nmFractureLineGraphicsItem* item)
|
|
|
{
|
|
|
addToGroup(item);
|
|
|
m_listLineFractures.append(item);
|
|
|
}
|
|
|
|
|
|
// 辅助函数:将 nmFractureRectGraphicsItem 添加到组中并记录
|
|
|
void nmAbstractWellboreVisual::addFractureRect(nmFractureRectGraphicsItem* item)
|
|
|
{
|
|
|
addToGroup(item);
|
|
|
m_listRectFractures.append(item);
|
|
|
}
|
|
|
|
|
|
#include "nmDataWellBase.h"
|
|
|
#include "nmDataVerticalWell.h"
|
|
|
|
|
|
// --- VerticalWellTopViewItem 实现 ---
|
|
|
nmVerticalWellTopViewItem::nmVerticalWellTopViewItem(QGraphicsItem *parent)
|
|
|
: nmAbstractWellboreVisual(parent)
|
|
|
{
|
|
|
m_pWellPoint = nullptr;
|
|
|
m_pOriginalPoint = nullptr;
|
|
|
m_pDashLine = nullptr;
|
|
|
}
|
|
|
|
|
|
void nmVerticalWellTopViewItem::updateGeometryVisuals(nmDataWellBase *pDataWell)
|
|
|
{
|
|
|
prepareGeometryChange(); // 通知QGraphicsView,此项的几何形状可能发生变化
|
|
|
clearAllChildItems(); // 清除所有已有的子项
|
|
|
m_pDataWell = pDataWell; // 存储几何数据指针
|
|
|
|
|
|
// 检查几何数据是否有效,且类型是否匹配 (垂直井或垂直压裂井在俯视图中显示类似)
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Vertical_Well) {
|
|
|
return; // 不符合类型则不绘制
|
|
|
}
|
|
|
|
|
|
nmDataVerticalWell *pVerticalWell = dynamic_cast<nmDataVerticalWell*>(m_pDataWell);
|
|
|
if(pVerticalWell == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 1. 绘制井筒点 (绿色圆点)
|
|
|
m_pWellPoint = new nmWellborePointGraphicsItem(this); // 创建井点图元,并将其父项设为当前组合图元
|
|
|
//double dRadius = pVerticalWell->getRadius().getValue().toDouble(); // 井点可视化的大小(半径)
|
|
|
double dRadius = 7;
|
|
|
double dX = pVerticalWell->getX().getValue().toDouble(); // 井口横坐标
|
|
|
double dY = pVerticalWell->getY().getValue().toDouble(); // 井口纵坐标
|
|
|
// 设置图元位置到场景坐标系的(dX,dY)
|
|
|
m_pWellPoint->setRect(-dRadius, -dRadius, dRadius * 2, dRadius * 2); // 矩形中心在 (0,0)
|
|
|
m_pWellPoint->setPos(dX, dY); // 将图元移动到场景中的 (dX, dY)
|
|
|
m_pWellPoint->setFlag(QGraphicsItem::ItemIsMovable, false); // 初始不可移动
|
|
|
// 设置Z值,确保在上面
|
|
|
m_pWellPoint->setZValue(1);
|
|
|
|
|
|
// 连接井点拖动相关信号
|
|
|
connect(m_pWellPoint, SIGNAL(sigPositionChanging(QPointF)),
|
|
|
this, SLOT(slotUpdateDashLine(QPointF)));
|
|
|
connect(m_pWellPoint, SIGNAL(sigDragFinished(QPointF)),
|
|
|
this, SLOT(slotWellborePointDragFinished(QPointF)));
|
|
|
|
|
|
addPoint(m_pWellPoint); // 添加到组合图元中并记录
|
|
|
|
|
|
// 绘制原始点 (红色圆点) 和虚线
|
|
|
if(!m_pOriginalPoint) {
|
|
|
m_pOriginalPoint = new nmWellborePointGraphicsItem(this);
|
|
|
}
|
|
|
|
|
|
m_pOriginalPoint->setRect(m_pWellPoint->rect());
|
|
|
m_pOriginalPoint->setPos(m_pWellPoint->pos());
|
|
|
m_pOriginalPoint->setStyle(QPen(Qt::black, 0.5f), QBrush(Qt::red));
|
|
|
m_pOriginalPoint->setFlag(QGraphicsItem::ItemIsMovable, false);
|
|
|
// 设置Z值,确保在绿色图元下面
|
|
|
m_pOriginalPoint->setZValue(0);
|
|
|
m_pOriginalPoint->setVisible(false); // 默认隐藏
|
|
|
|
|
|
if(!m_pDashLine) {
|
|
|
//m_pDashLine = new QGraphicsLineItem(this);
|
|
|
//m_pDashLine->setPen(QPen(Qt::darkGreen, 1, Qt::DashLine));
|
|
|
//m_pDashLine->setZValue(-1);
|
|
|
//m_pDashLine->setVisible(false); // 默认隐藏
|
|
|
m_pDashLine = new QGraphicsLineItem(this);
|
|
|
QPen dashPen(Qt::darkGreen, 0, Qt::DashLine);
|
|
|
dashPen.setCosmetic(true);
|
|
|
m_pDashLine->setPen(dashPen);
|
|
|
m_pDashLine->setZValue(-1);
|
|
|
m_pDashLine->setVisible(false);
|
|
|
}
|
|
|
|
|
|
// 初始虚线连接位置
|
|
|
m_pDashLine->setLine(QLineF(m_pWellPoint->pos(), m_pOriginalPoint->pos()));
|
|
|
|
|
|
update(); // 请求重绘
|
|
|
}
|
|
|
|
|
|
void nmVerticalWellTopViewItem::setEditMode(bool enabled)
|
|
|
{
|
|
|
if(m_pWellPoint) {
|
|
|
m_pWellPoint->setFlag(QGraphicsItem::ItemIsMovable, enabled); // 启用/禁用绿点移动
|
|
|
|
|
|
// 刷新虚线和红点的位置和可见性
|
|
|
if(m_pOriginalPoint) {
|
|
|
m_pOriginalPoint->setRect(m_pWellPoint->rect());
|
|
|
m_pOriginalPoint->setPos(m_pWellPoint->pos());
|
|
|
m_pOriginalPoint->setVisible(enabled);
|
|
|
}
|
|
|
|
|
|
if(m_pDashLine) {
|
|
|
m_pDashLine->setLine(QLineF(m_pWellPoint->pos(), m_pOriginalPoint->pos()));
|
|
|
m_pDashLine->setVisible(enabled);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmVerticalWellTopViewItem::slotUpdateDashLine(const QPointF& newPos)
|
|
|
{
|
|
|
if(m_pDashLine && m_pOriginalPoint) {
|
|
|
// 更新虚线连接位置
|
|
|
m_pDashLine->setLine(QLineF(newPos, m_pOriginalPoint->pos()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmVerticalWellTopViewItem::slotWellborePointDragFinished(const QPointF& finalCenterScenePos)
|
|
|
{
|
|
|
// 此时拖动已经结束,发出信号给 nmWxWellboreTopView,它会再将此信号转发给控制器
|
|
|
emit sigWellborePointDragFinished(finalCenterScenePos);
|
|
|
}
|
|
|
|
|
|
nmVerticalWellCrossSectionItem::nmVerticalWellCrossSectionItem(QGraphicsItem *parent)
|
|
|
: nmAbstractWellboreVisual(parent)
|
|
|
{
|
|
|
// 可以在这里设置一些默认的样式或初始化成员变量
|
|
|
}
|
|
|
|
|
|
void nmVerticalWellCrossSectionItem::updateGeometryVisuals(nmDataWellBase *pDataWell)
|
|
|
{
|
|
|
prepareGeometryChange();
|
|
|
clearAllChildItems(); // 清除所有已有的子项
|
|
|
m_pDataWell = pDataWell; // 存储几何数据指针
|
|
|
|
|
|
// 检查几何数据是否有效,且类型是否匹配
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Vertical_Well) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataVerticalWell *pVerticalWell = dynamic_cast<nmDataVerticalWell*>(m_pDataWell);
|
|
|
|
|
|
if(!pVerticalWell) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 在纵截面视图中,X轴通常代表左右延伸,Y轴代表深度(向下为正)
|
|
|
double dWellboreX = pVerticalWell->getX().getValue().toDouble();
|
|
|
// 获取井底深
|
|
|
double dBottomholeMD = pVerticalWell->getBottomholeMD().getValue().toDouble();
|
|
|
// 获取井身长度
|
|
|
double dWellLength = pVerticalWell->getWellLength().getValue().toDouble();
|
|
|
|
|
|
// 1. 绘制储层矩形 (nmReservoirGraphicsItems) - 最底层 (Z值: -1000)
|
|
|
nmReservoirGraphicsItems* pReservoirs = new nmReservoirGraphicsItems(this); // 创建实例,Z值在构造函数中已设置
|
|
|
addToGroup(pReservoirs); // 直接添加到组
|
|
|
|
|
|
// 2. 绘制井筒线 (nmWellboreLineGraphicsItem) - 中间层 (Z值: 0)
|
|
|
nmWellboreLineGraphicsItem* pWellboreLine = new nmWellboreLineGraphicsItem(this);
|
|
|
pWellboreLine->setLine(dWellboreX, dBottomholeMD, dWellboreX, dBottomholeMD + dWellLength); // 垂直线
|
|
|
|
|
|
|
|
|
//pWellboreLine->setStyle(QPen(QColor("#d3d3d3"), 2));
|
|
|
QPen wellborePen(QColor("#d3d3d3"), 2);
|
|
|
wellborePen.setCosmetic(true);
|
|
|
pWellboreLine->setStyle(wellborePen);
|
|
|
pWellboreLine->setZValue(2); // 设置Z值为2,确保在射孔段之上
|
|
|
addLine(pWellboreLine); // 添加到组中
|
|
|
|
|
|
// 获取整个组合图元的包围矩形
|
|
|
|
|
|
// 井筒的视觉起点和终点
|
|
|
QPointF wellboreStartScene = QPointF(dWellboreX, dBottomholeMD);
|
|
|
QPointF wellboreEndScene = QPointF(dWellboreX, dBottomholeMD + dWellLength); // 垂直线
|
|
|
|
|
|
// 井筒的MD起始值和总MD长度
|
|
|
double wellboreStartAbsoluteMd = dBottomholeMD;
|
|
|
double totalWellboreMdLength = dWellLength;
|
|
|
|
|
|
// 3. 绘制射孔段 (nmPerforationGraphicsItem) - 最上层 (Z值: 1)
|
|
|
QVector<nmDataPerforation*>& perforations = pVerticalWell->getPerforations(); // 获取射孔段集合
|
|
|
|
|
|
for(int i = 0; i < perforations.size(); ++i) {
|
|
|
nmDataPerforation* pPerfData = perforations.at(i);
|
|
|
|
|
|
nmPerforationGraphicsItem* pPerforation = new nmPerforationGraphicsItem(
|
|
|
pPerfData,
|
|
|
NM_WELL_MODEL::Vertical_Well, // 传递井类型
|
|
|
wellboreStartAbsoluteMd, // 传递井筒的MD起始值
|
|
|
totalWellboreMdLength, // 传递井筒的总MD长度
|
|
|
this
|
|
|
);
|
|
|
|
|
|
pPerforation->setZValue(1); // 设置Z值为1,确保在井筒(Z=2)之下
|
|
|
addPerforation(pPerforation); // 添加到组中
|
|
|
|
|
|
// 初始化射孔段的视觉信息
|
|
|
pPerforation->updateWellboreVisuals(wellboreStartScene, wellboreEndScene,
|
|
|
wellboreStartAbsoluteMd, totalWellboreMdLength);
|
|
|
|
|
|
// 连接射孔拖动信号
|
|
|
connect(pPerforation, SIGNAL(sigRequestPerforationMdUpdate(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)));
|
|
|
}
|
|
|
|
|
|
update(); // 请求重绘
|
|
|
}
|
|
|
|
|
|
void nmVerticalWellCrossSectionItem::setPerforationEditMode(bool enabled)
|
|
|
{
|
|
|
// TODO,处于编辑状态要做什么
|
|
|
// 遍历射孔段数组,设置为编辑状态
|
|
|
foreach(nmPerforationGraphicsItem* item, m_listPerforations) {
|
|
|
item->setEditMode(enabled);
|
|
|
// 如果需要,可以在这里根据编辑模式切换选中状态,确保一致性
|
|
|
//item->setSelected(enabled);
|
|
|
//item->setFlag(QGraphicsItem::ItemIsMovable, true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QRectF nmVerticalFracturedWellCrossSectionItem::getFractureMD()
|
|
|
{
|
|
|
return m_pRectFracture->boundingRect();
|
|
|
}
|
|
|
|
|
|
// --- nmVerticalFracturedWellTopViewItem 实现 ---
|
|
|
nmVerticalFracturedWellTopViewItem::nmVerticalFracturedWellTopViewItem(QGraphicsItem *parent)
|
|
|
: nmAbstractWellboreVisual(parent),
|
|
|
m_pFracture(nullptr)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellTopViewItem::updateGeometryVisuals(nmDataWellBase *pDataWell)
|
|
|
{
|
|
|
prepareGeometryChange(); // 通知QGraphicsView,此项的几何形状可能发生变化
|
|
|
clearAllChildItems(); // 清除所有已有的子项
|
|
|
m_pDataWell = pDataWell; // 存储几何数据指针
|
|
|
|
|
|
// 检查几何数据是否有效,且类型是否匹配
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Vertical_Fractured_Well) {
|
|
|
return; // 不符合类型则不绘制
|
|
|
}
|
|
|
|
|
|
nmDataVerticalFracturedWell *pVerticalFracturedWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pDataWell);
|
|
|
if(pVerticalFracturedWell == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 1. 绘制井筒点 (绿色圆点)
|
|
|
nmWellborePointGraphicsItem* pWellPoint = new nmWellborePointGraphicsItem(this); // 创建井点图元,并将其父项设为当前组合图元
|
|
|
//double dRadius = pVerticalWell->getRadius().getValue().toDouble(); // 井点可视化的大小(半径)
|
|
|
double dRadius = 7;
|
|
|
double dX = pVerticalFracturedWell->getX().getValue().toDouble(); // 井口横坐标
|
|
|
double dY = pVerticalFracturedWell->getY().getValue().toDouble(); // 井口纵坐标
|
|
|
// 设置图元位置到场景坐标系的(dX,dY)
|
|
|
pWellPoint->setRect(-dRadius, -dRadius, dRadius * 2, dRadius * 2); // 矩形中心在 (0,0)
|
|
|
pWellPoint->setPos(dX, dY); // 将图元移动到场景中的 (dX, dY)
|
|
|
|
|
|
pWellPoint->setFlag(QGraphicsItem::ItemIsMovable, false); // 初始不可移动
|
|
|
|
|
|
// 设置Z值,确保在上面
|
|
|
pWellPoint->setZValue(1);
|
|
|
|
|
|
addPoint(pWellPoint); // 添加到组合图元中并记录
|
|
|
|
|
|
// 2. 绘制裂缝
|
|
|
m_pFracture = new nmFractureLineGraphicsItem(this);
|
|
|
|
|
|
// 将裂缝图元的局部原点 (0,0) 设置到井筒在父项 (nmVerticalFracturedWellTopViewItem) 中的位置
|
|
|
m_pFracture->setPos(dX, dY);
|
|
|
|
|
|
// 获取裂缝位置信息
|
|
|
QVector<QPointF> vecFractures = pVerticalFracturedWell->getFracs();
|
|
|
|
|
|
// 确保有且仅有两个点来构成一条线
|
|
|
if(vecFractures.size() == 2) {
|
|
|
QPointF startPointScene = vecFractures.at(0); // 裂缝起点 (场景坐标)
|
|
|
QPointF endPointScene = vecFractures.at(1); // 裂缝终点 (场景坐标)
|
|
|
|
|
|
// 裂缝图元的局部原点现在是 (dX, dY),所以需要减去这个偏移量来获得其在 pFracture 内部的相对坐标
|
|
|
QPointF startPointLocal = startPointScene - QPointF(dX, dY);
|
|
|
QPointF endPointLocal = endPointScene - QPointF(dX, dY);
|
|
|
|
|
|
m_pFracture->setLine(QLineF(startPointLocal, endPointLocal)); // 设置裂缝线,它接受局部坐标
|
|
|
} else {
|
|
|
// 如果数据不符合预期,则不绘制裂缝并清理创建的图元
|
|
|
delete m_pFracture;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 设置 Z 值,确保在井点之上
|
|
|
m_pFracture->setZValue(pWellPoint->zValue() + 0.5); // 确保在井点之上,可以稍微高一点
|
|
|
|
|
|
addFractureLine(m_pFracture); // 添加裂缝图元
|
|
|
|
|
|
// 转发裂缝几何位置变化后的位置
|
|
|
connect(m_pFracture, SIGNAL(sigFractureGeometryChanged(double, double)),
|
|
|
this, SIGNAL(sigFracturePosChanged(double, double)));
|
|
|
|
|
|
update(); // 请求重绘
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellTopViewItem::setEditMode(bool enabled)
|
|
|
{
|
|
|
m_pFracture->setEditMode(enabled);
|
|
|
}
|
|
|
|
|
|
// --- nmVerticalFracturedWellCrossSectionItem 实现 ---
|
|
|
nmVerticalFracturedWellCrossSectionItem::nmVerticalFracturedWellCrossSectionItem(QGraphicsItem *parent)
|
|
|
: nmAbstractWellboreVisual(parent),
|
|
|
m_pRectFracture(nullptr)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellCrossSectionItem::updateGeometryVisuals(nmDataWellBase *pDataWell)
|
|
|
{
|
|
|
prepareGeometryChange();
|
|
|
clearAllChildItems(); // 清除所有已有的子项
|
|
|
m_pDataWell = pDataWell; // 存储几何数据指针
|
|
|
|
|
|
// 检查几何数据是否有效,且类型是否匹配
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Vertical_Fractured_Well) {
|
|
|
// 如果类型不匹配或数据无效,清空所有子项和映射
|
|
|
for(auto it = m_mapPerforationItem.begin(); it != m_mapPerforationItem.end(); ++it) {
|
|
|
scene()->removeItem(it.value()); // 从场景中移除 (如果 item 尚未被 clearAllChildItems 清除)
|
|
|
delete it.value(); // 释放内存
|
|
|
}
|
|
|
|
|
|
m_mapPerforationItem.clear(); // 清空映射
|
|
|
m_listPerforations.clear();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataVerticalFracturedWell *pVFWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pDataWell);
|
|
|
|
|
|
if(!pVFWell) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 在纵截面视图中,X轴通常代表左右延伸,Y轴代表深度(向下为正)
|
|
|
double dWellboreX = pVFWell->getX().getValue().toDouble();
|
|
|
// 获取井底深
|
|
|
double dBottomholeMD = pVFWell->getBottomholeMD().getValue().toDouble();
|
|
|
// 获取井身长度
|
|
|
double dWellLength = pVFWell->getWellLength().getValue().toDouble();
|
|
|
|
|
|
// 1. 绘制储层矩形 (nmReservoirGraphicsItems) - 最底层 (Z值: -1000)
|
|
|
nmReservoirGraphicsItems* pReservoirs = new nmReservoirGraphicsItems(this); // 创建实例,Z值在构造函数中已设置
|
|
|
addToGroup(pReservoirs); // 直接添加到组
|
|
|
|
|
|
// 2. 绘制裂缝矩形
|
|
|
m_pRectFracture = new nmFractureRectGraphicsItem(pVFWell, this);
|
|
|
addFractureRect(m_pRectFracture);
|
|
|
|
|
|
// 连接裂缝编辑时,裂缝拖动完成后的信号
|
|
|
connect(m_pRectFracture, SIGNAL(sigRequestFractureRectUpdate(qreal, qreal)),
|
|
|
this, SLOT(slotFractureDragFinished(qreal, qreal)));
|
|
|
// 连接拖动过程中的实时预览信号
|
|
|
connect(m_pRectFracture, SIGNAL(sigFractureGeometryChanged(qreal, qreal)),
|
|
|
this, SLOT(slotFractureConstraintChanged(qreal, qreal)));
|
|
|
|
|
|
// 3. 绘制井筒线 (nmWellboreLineGraphicsItem) - 中间层 (Z值: 0)
|
|
|
// 井筒的总深度
|
|
|
nmWellboreLineGraphicsItem* pWellboreLine = new nmWellboreLineGraphicsItem(this);
|
|
|
pWellboreLine->setLine(dWellboreX, dBottomholeMD, dWellboreX, dBottomholeMD + dWellLength); // 垂直线
|
|
|
//pWellboreLine->setStyle(QPen(QColor("#d3d3d3"), 2));
|
|
|
QPen wellborePen(QColor("#d3d3d3"), 2);
|
|
|
wellborePen.setCosmetic(true);
|
|
|
pWellboreLine->setStyle(wellborePen);
|
|
|
pWellboreLine->setZValue(2); // 设置Z值为2,确保在射孔段之上
|
|
|
addLine(pWellboreLine); // 添加到组中
|
|
|
|
|
|
updatePerforationChildGraphics();
|
|
|
|
|
|
update(); // 请求重绘
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellCrossSectionItem::setFractureEditMode(bool enabled)
|
|
|
{
|
|
|
m_pRectFracture->setEditMode(enabled);
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellCrossSectionItem::setPerforationEditMode(bool enabled)
|
|
|
{
|
|
|
// 遍历射孔段数组,设置为编辑状态
|
|
|
foreach(nmPerforationGraphicsItem* item, m_listPerforations) {
|
|
|
item->setEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellCrossSectionItem::slotFractureConstraintChanged(qreal fractureTopMD, qreal fractureBottomMD)
|
|
|
{
|
|
|
// 这个槽函数只用于实时预览
|
|
|
// 遍历所有射孔图元,从后向前遍历以便安全删除
|
|
|
for(int i = m_listPerforations.size() - 1; i >= 0; --i) {
|
|
|
nmPerforationGraphicsItem* pPerforationItem = m_listPerforations.at(i);
|
|
|
|
|
|
// 获取射孔图元关联的原始数据
|
|
|
// 注意:这里读取的是原始数据,不是裁剪后的数据
|
|
|
nmDataPerforation* pPerfData = pPerforationItem->getPerforationData();
|
|
|
double dPerfStartMD = pPerfData->getMdStart().getValue().toDouble();
|
|
|
double dPerfEndMD = pPerfData->getMdEnd().getValue().toDouble();
|
|
|
|
|
|
// 检查是否需要删除图元(如果射孔的原始范围完全在裂缝之外)
|
|
|
if(dPerfEndMD <= fractureTopMD || dPerfStartMD >= fractureBottomMD) {
|
|
|
// 从场景和父组中移除图元
|
|
|
removeFromGroup(pPerforationItem);
|
|
|
scene()->removeItem(pPerforationItem);
|
|
|
// 从列表中移除并删除
|
|
|
m_listPerforations.removeAt(i);
|
|
|
delete pPerforationItem;
|
|
|
} else {
|
|
|
// 如果图元还在裂缝范围内,调用预览函数进行渲染
|
|
|
pPerforationItem->updateGraphicsForPreview(fractureTopMD, fractureBottomMD);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 请求场景重绘,以显示最新的预览效果
|
|
|
scene()->update();
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellCrossSectionItem::slotFractureDragFinished(qreal fractureTopMD, qreal fractureBottomMD)
|
|
|
{
|
|
|
QMap<nmDataPerforation*, QPair<double, double>> updatedPerforations;
|
|
|
|
|
|
// 遍历所有射孔图元,将它们被裁剪后的MD值收集起来
|
|
|
foreach(nmPerforationGraphicsItem* pPerforationItem, m_listPerforations) {
|
|
|
double croppedStart = pPerforationItem->getCroppedMdStart();
|
|
|
double croppedEnd = pPerforationItem->getCroppedMdEnd();
|
|
|
|
|
|
// 存入映射,用于传递给控制器
|
|
|
updatedPerforations.insert(pPerforationItem->getPerforationData(), QPair<double, double>(croppedStart, croppedEnd));
|
|
|
}
|
|
|
|
|
|
// 1. 向控制器发送信号,传递所有被修改的射孔数据
|
|
|
emit sigFractureDragFinished(updatedPerforations);
|
|
|
|
|
|
// 2. 发出更新裂缝的信号
|
|
|
emit sigRequestFractureRectUpdate(fractureTopMD, fractureBottomMD);
|
|
|
}
|
|
|
|
|
|
void nmVerticalFracturedWellCrossSectionItem::updatePerforationChildGraphics()
|
|
|
{
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Vertical_Fractured_Well) {
|
|
|
// 如果数据无效,清空所有射孔图形项
|
|
|
for(auto it = m_mapPerforationItem.begin(); it != m_mapPerforationItem.end(); ++it) {
|
|
|
// 从场景和父组中移除
|
|
|
removeFromGroup(it.value());
|
|
|
scene()->removeItem(it.value());
|
|
|
delete it.value();
|
|
|
}
|
|
|
|
|
|
m_mapPerforationItem.clear();
|
|
|
m_listPerforations.clear();
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataVerticalFracturedWell *pVFWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pDataWell);
|
|
|
|
|
|
if(!pVFWell) return;
|
|
|
|
|
|
QVector<nmDataPerforation*>& vecCurrentPerforations = pVFWell->getPerforations();
|
|
|
|
|
|
// 1: 找出需要删除的图形项 (数据模型中已不存在的)
|
|
|
QList<nmDataPerforation*> listPerforationsToRemoveFromMap;
|
|
|
|
|
|
for(auto it = m_mapPerforationItem.begin(); it != m_mapPerforationItem.end(); ++it) {
|
|
|
if(!vecCurrentPerforations.contains(it.key())) {
|
|
|
listPerforationsToRemoveFromMap.append(it.key());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
foreach(nmDataPerforation* pPerfData, listPerforationsToRemoveFromMap) {
|
|
|
nmPerforationGraphicsItem* item = m_mapPerforationItem.take(pPerfData);
|
|
|
|
|
|
if(item) {
|
|
|
removeFromGroup(item); // 从父组中移除
|
|
|
scene()->removeItem(item); // 从场景中移除
|
|
|
m_listPerforations.removeOne(item);
|
|
|
delete item; // 释放内存
|
|
|
}
|
|
|
}
|
|
|
|
|
|
double dWellboreX = pVFWell->getX().getValue().toDouble(); // 井口X坐标
|
|
|
|
|
|
// 获取井筒的MD信息
|
|
|
double dWellheadMD = pVFWell->getBottomholeMD().getValue().toDouble(); // 假设有此属性
|
|
|
double dWellLength = pVFWell->getWellLength().getValue().toDouble();
|
|
|
double dWellboreEndMD = dWellheadMD + dWellLength;
|
|
|
|
|
|
// 井筒的MD起始值和总MD长度 (用于nmPerforationGraphicsItem)
|
|
|
double wellboreStartAbsoluteMd = dWellheadMD;
|
|
|
double totalWellboreMdLength = dWellLength;
|
|
|
|
|
|
// 井筒的视觉起点和终点
|
|
|
QPointF wellboreStartScene = QPointF(dWellboreX, dWellheadMD);
|
|
|
QPointF wellboreEndScene = QPointF(dWellboreX, dWellboreEndMD);
|
|
|
|
|
|
// 2: 添加新图形项
|
|
|
foreach(nmDataPerforation* pPerfData, vecCurrentPerforations) {
|
|
|
nmPerforationGraphicsItem* pPerforationItem = nullptr;
|
|
|
|
|
|
// 如果图形项不存在,则创建新的
|
|
|
if(!m_mapPerforationItem.contains(pPerfData)) {
|
|
|
pPerforationItem = new nmPerforationGraphicsItem(
|
|
|
pPerfData,
|
|
|
NM_WELL_MODEL::Vertical_Well, // 传递井类型 (此处仍然是Vertical_Well,因为是纵截面)
|
|
|
wellboreStartAbsoluteMd, // 传递井筒的MD起始值
|
|
|
totalWellboreMdLength, // 传递井筒的总MD长度
|
|
|
this
|
|
|
);
|
|
|
pPerforationItem->setZValue(1); // 设置Z值为1,确保在井筒(Z=2)之下
|
|
|
|
|
|
addPerforation(pPerforationItem); // 添加到父组中
|
|
|
m_mapPerforationItem.insert(pPerfData, pPerforationItem); // 添加到映射中
|
|
|
|
|
|
// 连接射孔拖动信号
|
|
|
connect(pPerforationItem, SIGNAL(sigRequestPerforationMdUpdate(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)));
|
|
|
}
|
|
|
|
|
|
// 无论新建还是现有,都更新其视觉信息,确保实时刷新
|
|
|
if(pPerforationItem) {
|
|
|
pPerforationItem->updateWellboreVisuals(wellboreStartScene, wellboreEndScene,
|
|
|
wellboreStartAbsoluteMd, totalWellboreMdLength);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 请求场景重绘,只重绘受影响的区域
|
|
|
scene()->update();
|
|
|
}
|
|
|
|
|
|
// nmWellboreEndHandleGraphicsItem 类的实现
|
|
|
nmWellboreEndHandleGraphicsItem::nmWellboreEndHandleGraphicsItem(QGraphicsItem *parent)
|
|
|
: QGraphicsRectItem(parent)
|
|
|
{
|
|
|
//// 设置手柄的默认样式
|
|
|
//QPen pen(Qt::black); // 边框为黑色
|
|
|
//QBrush brush(Qt::red); // 填充为红色
|
|
|
//setPen(pen);
|
|
|
//setBrush(brush);
|
|
|
QPen pen(Qt::black, 0);
|
|
|
pen.setCosmetic(true);
|
|
|
QBrush brush(Qt::red);
|
|
|
setPen(pen);
|
|
|
setBrush(brush);
|
|
|
}
|
|
|
|
|
|
void nmWellboreEndHandleGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(event->button() == Qt::LeftButton) {
|
|
|
m_lastMouseScenePos = event->scenePos(); // 记录鼠标按下时的场景坐标
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWellboreEndHandleGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(event->buttons() & Qt::LeftButton) { // 如果左键被按下并拖动
|
|
|
QPointF delta = event->scenePos() - m_lastMouseScenePos; // 计算鼠标移动的增量
|
|
|
moveBy(delta.x(), delta.y()); // 移动手柄图元本身
|
|
|
m_lastMouseScenePos = event->scenePos(); // 更新上次鼠标位置
|
|
|
emit handlePositionChanged(event->scenePos(), true); // 发出信号,通知新的场景位置
|
|
|
}
|
|
|
|
|
|
QGraphicsRectItem::mouseMoveEvent(event); // 调用基类的处理
|
|
|
}
|
|
|
|
|
|
void nmWellboreEndHandleGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
QGraphicsRectItem::mouseReleaseEvent(event); // 调用基类的处理
|
|
|
emit handlePositionChanged(scenePos(), false); // 鼠标释放时,再次发送最终的场景位置,确保精确
|
|
|
}
|
|
|
|
|
|
#include "nmDataHorizontalFracturedWell.h"
|
|
|
|
|
|
// --- nmHorizontalFracturedWellTopViewItem 实现 ---
|
|
|
nmHorizontalFracturedWellTopViewItem::nmHorizontalFracturedWellTopViewItem(QGraphicsItem *parent)
|
|
|
: nmAbstractWellboreVisual(parent),
|
|
|
m_pWellboreLine(nullptr),
|
|
|
m_pEndHandle(nullptr),
|
|
|
m_bEditMode(false)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellTopViewItem::updateGeometryVisuals(nmDataWellBase *pDataWell)
|
|
|
{
|
|
|
prepareGeometryChange(); // 通知QGraphicsView,此项的几何形状可能发生变化
|
|
|
clearAllChildItems(); // 清除所有已有的子项
|
|
|
// 清空 m_listFractures
|
|
|
qDeleteAll(m_listFractures);
|
|
|
m_listFractures.clear();
|
|
|
|
|
|
m_pWellboreLine = nullptr;
|
|
|
m_pEndHandle = nullptr;
|
|
|
|
|
|
m_pDataWell = pDataWell; // 存储几何数据指针
|
|
|
|
|
|
// 检查几何数据是否有效,且类型是否匹配 (垂直井或垂直压裂井在俯视图中显示类似)
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
return; // 不符合类型则不绘制
|
|
|
}
|
|
|
|
|
|
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pDataWell);
|
|
|
if(pHFWell == nullptr) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 1. 绘制井筒点 (绿色圆点)
|
|
|
nmWellborePointGraphicsItem* pWellPoint = new nmWellborePointGraphicsItem(this); // 创建井点图元,并将其父项设为当前组合图元
|
|
|
double dRadius = 7;
|
|
|
double dX = pHFWell->getX().getValue().toDouble(); // 井口横坐标
|
|
|
double dY = pHFWell->getY().getValue().toDouble(); // 井口纵坐标
|
|
|
// 设置图元位置到场景坐标系的(dX,dY)
|
|
|
pWellPoint->setRect(-dRadius, -dRadius, dRadius * 2, dRadius * 2); // 矩形中心在 (0,0)
|
|
|
pWellPoint->setPos(dX, dY); // 将图元移动到场景中的 (dX, dY)
|
|
|
|
|
|
pWellPoint->setFlag(QGraphicsItem::ItemIsMovable, false); // 初始不可移动
|
|
|
|
|
|
// 设置Z值,确保在上面
|
|
|
pWellPoint->setZValue(1);
|
|
|
|
|
|
addPoint(pWellPoint); // 添加到组合图元中并记录
|
|
|
|
|
|
// 2. 绘制井筒(俯视图下)
|
|
|
// 获取几何信息
|
|
|
// 井身长度
|
|
|
double dWellLength = pHFWell->getWellLength().getValue().toDouble();
|
|
|
// 与水平方向的夹角(直角坐标系)
|
|
|
double dDarinAngle = pHFWell->getDrainAngle().getValue().toDouble();
|
|
|
|
|
|
// 将角度从度转换为弧度,因为 Qt 的三角函数通常接受弧度
|
|
|
double dDarinAngleRad = dDarinAngle * M_PI / 180.0;
|
|
|
|
|
|
// 计算井筒的终点坐标
|
|
|
// endX = 起始X + 长度 * cos(角度)
|
|
|
// endY = 起始Y + 长度 * sin(角度)
|
|
|
double endX = dX + dWellLength * qCos(dDarinAngleRad);
|
|
|
double endY = dY + dWellLength * qSin(dDarinAngleRad);
|
|
|
|
|
|
// 创建井筒线段
|
|
|
m_pWellboreLine = new nmWellboreLineGraphicsItem(this);
|
|
|
QPen linePen;
|
|
|
linePen.setColor(QColor("#32cd32")); // 设置线段颜色
|
|
|
linePen.setWidth(5); // 设置线宽
|
|
|
linePen.setCosmetic(true);
|
|
|
m_pWellboreLine->setStyle(linePen); // 应用样式
|
|
|
m_pWellboreLine->setLine(dX, dY, endX, endY); // 设置线段的起始点和终点
|
|
|
m_pWellboreLine->setZValue(2);
|
|
|
addLine(m_pWellboreLine); // 添加到组合图元中并记录
|
|
|
|
|
|
// 创建井筒末端矩形手柄
|
|
|
m_pEndHandle = new nmWellboreEndHandleGraphicsItem(this);
|
|
|
// 设置手柄的矩形区域
|
|
|
m_pEndHandle->setRect(-5, -5, 10, 10);
|
|
|
// 将手柄的中心设置在井筒线的终点
|
|
|
m_pEndHandle->setPos(endX, endY);
|
|
|
m_pEndHandle->setVisible(m_bEditMode);
|
|
|
m_pEndHandle->setZValue(5);
|
|
|
// 连接手柄的 handlePositionChanged 信号到当前类的 onEndHandleMoved 槽函数
|
|
|
connect(m_pEndHandle, SIGNAL(handlePositionChanged(QPointF, bool)),
|
|
|
this, SLOT(onEndHandleMoved(QPointF, bool)));
|
|
|
|
|
|
// 3. 绘制射孔段
|
|
|
QVector<nmDataPerforation*>& vecCurrentPerforations = pHFWell->getPerforations();
|
|
|
|
|
|
// 井筒的MD起始值(多段压裂水平井默认是第一条射孔起点)和总MD长度
|
|
|
double wellboreStartAbsoluteMd = vecCurrentPerforations[0]->getMdStart().getValue().toDouble();
|
|
|
double totalWellboreMdLength = dWellLength;
|
|
|
|
|
|
// 井筒的视觉起点和终点
|
|
|
QPointF wellboreStartScene = QPointF(dX, dY);
|
|
|
QPointF wellboreEndScene = QPointF(endX, endY);
|
|
|
|
|
|
foreach(nmDataPerforation* pPerfData, vecCurrentPerforations) {
|
|
|
nmPerforationGraphicsItem* pPerforationItem = nullptr;
|
|
|
|
|
|
// 构造函数传入最新的MD信息
|
|
|
pPerforationItem = new nmPerforationGraphicsItem(
|
|
|
pPerfData,
|
|
|
NM_WELL_MODEL::Horizontal_Fractured_Well, // 传递井类型
|
|
|
wellboreStartAbsoluteMd, // 传递井筒的MD起始值
|
|
|
totalWellboreMdLength, // 传递井筒的总MD长度
|
|
|
this
|
|
|
);
|
|
|
pPerforationItem->setZValue(3);
|
|
|
|
|
|
addPerforation(pPerforationItem); // 添加到父组中
|
|
|
|
|
|
// 初始化射孔段的视觉信息
|
|
|
pPerforationItem->updateWellboreVisuals(wellboreStartScene, wellboreEndScene,
|
|
|
wellboreStartAbsoluteMd, totalWellboreMdLength);
|
|
|
|
|
|
// 连接射孔拖动信号
|
|
|
connect(pPerforationItem, SIGNAL(sigRequestPerforationMdUpdate(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)));
|
|
|
|
|
|
}
|
|
|
|
|
|
// 4. 绘制裂缝 (使用新的 nmHorizontalFractureGraphicsItem)
|
|
|
// 强制数据模型更新裂缝点,确保其是基于当前井筒几何的最新状态
|
|
|
QVector<QPair<QPointF, QPointF>> vecFracturePoint = pHFWell->getFracs();
|
|
|
|
|
|
int fractureIndex = 0; // 用于给裂缝分配索引
|
|
|
|
|
|
for(QVector<QPair<QPointF, QPointF>>::const_iterator it = vecFracturePoint.constBegin();
|
|
|
it != vecFracturePoint.constEnd(); ++it) {
|
|
|
const QPair<QPointF, QPointF>& fracPair = *it; // 获取当前元素
|
|
|
|
|
|
nmHorizontalFractureGraphicsItem* pFractureItem = new nmHorizontalFractureGraphicsItem(fractureIndex, this);
|
|
|
pFractureItem->updateFractureLineAndHandles(fracPair.first, fracPair.second);
|
|
|
// 传递井筒场景信息和方向,用于裂缝内部的拖动约束
|
|
|
pFractureItem->setWellboreInfo(wellboreStartScene, wellboreEndScene);
|
|
|
pFractureItem->setEditMode(m_bEditMode);
|
|
|
|
|
|
connect(pFractureItem, SIGNAL(fractureHalfLengthChanged(double, bool, int)),
|
|
|
this, SLOT(onSingleFractureHalfLengthChanged(double, bool, int)));
|
|
|
|
|
|
// !!!将裂缝图元添加到本类自己的列表中
|
|
|
m_listFractures.append(pFractureItem);
|
|
|
pFractureItem->setZValue(1.5); // 井点之上、井筒之下
|
|
|
fractureIndex++;
|
|
|
}
|
|
|
|
|
|
// 清空旧缓存
|
|
|
m_vecFractureMds.clear();
|
|
|
// 从数据模型获取MD值
|
|
|
m_vecFractureMds = pHFWell->getFractureMds();
|
|
|
|
|
|
update(); // 请求重绘
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellTopViewItem::setEditMode(bool enabled)
|
|
|
{
|
|
|
m_bEditMode = enabled; // 更新内部状态
|
|
|
|
|
|
if(m_pEndHandle) {
|
|
|
m_pEndHandle->setVisible(enabled); // 设置手柄的可见性
|
|
|
}
|
|
|
|
|
|
// 设置裂缝的编辑状态
|
|
|
foreach(nmHorizontalFractureGraphicsItem* pFractureItem, m_listFractures) {
|
|
|
if(pFractureItem) {
|
|
|
pFractureItem->setEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellTopViewItem::setPerforationEditMode(bool enabled)
|
|
|
{
|
|
|
// 遍历射孔段数组,设置为编辑状态
|
|
|
foreach(nmPerforationGraphicsItem* item, m_listPerforations) {
|
|
|
item->setEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellTopViewItem::onEndHandleMoved(QPointF newScenePos, bool isDragging)
|
|
|
{
|
|
|
|
|
|
if(!m_bEditMode) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if(!m_pDataWell) return;
|
|
|
|
|
|
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pDataWell);
|
|
|
|
|
|
if(!pHFWell) return;
|
|
|
|
|
|
prepareGeometryChange();
|
|
|
|
|
|
// 井筒起点是固定的井口坐标
|
|
|
QPointF wellboreStartScene(pHFWell->getX().getValue().toDouble(), pHFWell->getY().getValue().toDouble());
|
|
|
QPointF wellboreEndScene = newScenePos; // 实时终点
|
|
|
|
|
|
// 实时计算井身长度和方向
|
|
|
double currentWellLength = QLineF(wellboreStartScene, wellboreEndScene).length();
|
|
|
QVector2D wellboreDirection = QVector2D(wellboreEndScene - wellboreStartScene).normalized();
|
|
|
QPointF perpendicularDirection = QPointF(-wellboreDirection.y(), wellboreDirection.x());
|
|
|
|
|
|
// 1. 更新井筒线段的终点(实时)
|
|
|
if(m_pWellboreLine) {
|
|
|
m_pWellboreLine->setLine(wellboreStartScene.x(), wellboreStartScene.y(), wellboreEndScene.x(), wellboreEndScene.y());
|
|
|
}
|
|
|
|
|
|
// 2. 实时更新所有射孔图元的视觉状态
|
|
|
// 从数据模型中获取旧的井筒MD数据作为计算比例的基准
|
|
|
double originalWellheadMd = pHFWell->getPerforations()[0]->getMdStart().getValue().toDouble();
|
|
|
double originalWellLength = pHFWell->getWellLength().getValue().toDouble();
|
|
|
|
|
|
foreach(nmPerforationGraphicsItem* pPerforationItem, m_listPerforations) {
|
|
|
if(pPerforationItem) {
|
|
|
// 调用专门用于实时预览的方法,它会根据旧MD比例和新长度计算位置
|
|
|
pPerforationItem->updateVisualsForWellboreDrag(wellboreStartScene, wellboreEndScene,
|
|
|
originalWellheadMd, originalWellLength,
|
|
|
currentWellLength);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 3. 实时更新所有裂缝图元
|
|
|
if(!m_vecFractureMds.empty()) {
|
|
|
double halfLength = pHFWell->getFractureHalfLength().getValue().toDouble(); // 裂缝半长在拖动井身时是固定的
|
|
|
double fractureRelativeAngleRad = pHFWell->getFractureAngle().getValue().toDouble() * M_PI / 180.0;
|
|
|
|
|
|
// 计算裂缝的绝对角度
|
|
|
qreal wellboreAngleRad = qAtan2(wellboreDirection.y(), wellboreDirection.x());
|
|
|
double fractureAbsoluteAngleRad = wellboreAngleRad + fractureRelativeAngleRad;
|
|
|
|
|
|
// 计算裂缝端点的偏移量
|
|
|
double dx_frac = halfLength * cos(fractureAbsoluteAngleRad);
|
|
|
double dy_frac = halfLength * sin(fractureAbsoluteAngleRad);
|
|
|
|
|
|
// 这里的逻辑是保持裂缝在井筒上的“均匀分布”
|
|
|
int fractureCount = m_vecFractureMds.size();
|
|
|
double intervalRatio = (fractureCount > 1) ? 1.0 / (fractureCount - 1) : 0.0;
|
|
|
|
|
|
for(int i = 0; i < fractureCount; ++i) {
|
|
|
nmHorizontalFractureGraphicsItem* pFractureItem = m_listFractures.at(i);
|
|
|
|
|
|
if(pFractureItem) {
|
|
|
// 根据新的总长度和均匀间隔比例,计算每个裂缝中心点在井筒上的位置
|
|
|
double currentPositionAlongWellbore = i * intervalRatio * currentWellLength;
|
|
|
|
|
|
QPointF fractureCenterScene = wellboreStartScene + wellboreDirection.toPointF() * currentPositionAlongWellbore;
|
|
|
|
|
|
QPointF p1_new_scene = QPointF(fractureCenterScene.x() - dx_frac, fractureCenterScene.y() - dy_frac);
|
|
|
QPointF p2_new_scene = QPointF(fractureCenterScene.x() + dx_frac, fractureCenterScene.y() + dy_frac);
|
|
|
|
|
|
pFractureItem->updateFractureLineAndHandles(p1_new_scene, p2_new_scene);
|
|
|
pFractureItem->setWellboreInfo(wellboreStartScene, wellboreEndScene); // 更新井筒信息,供裂缝内部拖动使用
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 4. 只有当拖动结束时,才发出信号给控制器
|
|
|
if(!isDragging) {
|
|
|
emit sigWellboreDragFinished(newScenePos);
|
|
|
// 更新缓存的裂缝MD,临时,可能后面会有时序问题
|
|
|
m_vecFractureMds.clear();
|
|
|
m_vecFractureMds = pHFWell->getFractureMds();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellTopViewItem::onSingleFractureHalfLengthChanged(double newHalfLength, bool isDragging, int fractureIndex)
|
|
|
{
|
|
|
// 获取井筒的场景坐标信息
|
|
|
QPointF wellboreStartScene = m_pWellboreLine->line().p1();
|
|
|
QPointF wellboreEndScene = m_pWellboreLine->line().p2();
|
|
|
double wellboreVisualLength = QLineF(wellboreStartScene, wellboreEndScene).length();
|
|
|
QVector2D wellboreDirection = QVector2D(wellboreEndScene - wellboreStartScene).normalized();
|
|
|
|
|
|
// 获取井筒的总MD长度(这个值在拖动裂缝半长时是固定的)
|
|
|
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pDataWell);
|
|
|
|
|
|
if(!pHFWell) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
double totalWellboreMdLength = pHFWell->getWellLength().getValue().toDouble();
|
|
|
|
|
|
// 计算垂直于井筒的方向向量
|
|
|
QPointF perpendicularDirection = QPointF(-wellboreDirection.y(), wellboreDirection.x());
|
|
|
|
|
|
// 拖动过程中,实时计算所有裂缝的视觉位置
|
|
|
if(isDragging) {
|
|
|
// 遍历所有裂缝图元,使用缓存的MD值
|
|
|
for(int i = 0; i < m_listFractures.size() && i < m_vecFractureMds.size(); ++i) {
|
|
|
nmHorizontalFractureGraphicsItem* pFractureItem = m_listFractures.at(i);
|
|
|
|
|
|
if(pFractureItem) {
|
|
|
// 根据缓存的MD值计算中心点
|
|
|
double fractureMd = m_vecFractureMds.at(i);
|
|
|
QPointF fractureCenterScene = wellboreStartScene + wellboreDirection.toPointF() * (fractureMd / totalWellboreMdLength * wellboreVisualLength);
|
|
|
|
|
|
// 计算新的裂缝端点
|
|
|
QPointF p1_new_scene = fractureCenterScene - perpendicularDirection * newHalfLength;
|
|
|
QPointF p2_new_scene = fractureCenterScene + perpendicularDirection * newHalfLength;
|
|
|
|
|
|
// 更新图元的视觉状态
|
|
|
pFractureItem->updateFractureLineAndHandles(p1_new_scene, p2_new_scene);
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
// 拖动结束时,发出信号给控制器
|
|
|
emit sigFractureHalfLengthChanged(newHalfLength);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QLineF nmHorizontalFracturedWellTopViewItem::getWellboreLine()
|
|
|
{
|
|
|
return m_pWellboreLine->line();
|
|
|
}
|
|
|
|
|
|
nmCrossSectionFractureItem::nmCrossSectionFractureItem(int fractureIndex, QGraphicsItem *parent)
|
|
|
: QObject(nullptr),
|
|
|
QGraphicsLineItem(parent),
|
|
|
m_pTopHandle(nullptr),
|
|
|
m_pBottomHandle(nullptr),
|
|
|
m_bEditMode(false),
|
|
|
m_fractureIndex(fractureIndex),
|
|
|
m_currentFractureXPosition(0.0),
|
|
|
m_currentFractureMidPointY(0.0),
|
|
|
m_currentFractureHeight(0.0),
|
|
|
m_minReservoirDepth(0.0), // 初始化
|
|
|
m_maxReservoirDepth(0.0), // 初始化
|
|
|
m_isDraggingBody(false)
|
|
|
{
|
|
|
//QPen pen(QColor("#0000ff"), 3);
|
|
|
//setPen(pen);
|
|
|
//setZValue(3); // 确保在井筒和储层之上
|
|
|
QPen pen(QColor("#0000ff"), 3);
|
|
|
pen.setCosmetic(true);
|
|
|
setPen(pen);
|
|
|
setZValue(3);
|
|
|
|
|
|
m_pTopHandle = new nmFractureLineHandleItem(this);
|
|
|
m_pBottomHandle = new nmFractureLineHandleItem(this);
|
|
|
|
|
|
connect(m_pTopHandle, SIGNAL(handlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)),
|
|
|
this, SLOT(onHandlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)));
|
|
|
connect(m_pBottomHandle, SIGNAL(handlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)),
|
|
|
this, SLOT(onHandlePositionChanged(nmFractureLineHandleItem*, QPointF, bool)));
|
|
|
|
|
|
// 默认禁用所有不必要的视觉效果和交互
|
|
|
setEditMode(false);
|
|
|
}
|
|
|
|
|
|
nmCrossSectionFractureItem::~nmCrossSectionFractureItem()
|
|
|
{
|
|
|
m_pTopHandle = nullptr;
|
|
|
m_pBottomHandle = nullptr;
|
|
|
}
|
|
|
|
|
|
// 更新裂缝的视觉表示,并传入储层深度限制
|
|
|
void nmCrossSectionFractureItem::updateFractureVisuals(double fractureXPosition, double midPointY, double fractureHeight,
|
|
|
double minReservoirDepth, double maxReservoirDepth)
|
|
|
{
|
|
|
prepareGeometryChange(); // 准备几何形状变化通知
|
|
|
|
|
|
m_currentFractureXPosition = fractureXPosition;
|
|
|
m_currentFractureMidPointY = midPointY;
|
|
|
m_currentFractureHeight = fractureHeight;
|
|
|
m_minReservoirDepth = minReservoirDepth; // 存储储层深度限制
|
|
|
m_maxReservoirDepth = maxReservoirDepth; // 存储储层深度限制
|
|
|
|
|
|
// 裂缝作为垂直线,X坐标固定,Y坐标从 (midY - halfHeight) 到 (midY + halfHeight)
|
|
|
double halfHeight = fractureHeight / 2.0;
|
|
|
|
|
|
QPointF p1_local(fractureXPosition, midPointY - halfHeight); // 顶部点
|
|
|
QPointF p2_local(fractureXPosition, midPointY + halfHeight); // 底部点 (假设Y轴向下为正)
|
|
|
|
|
|
setLine(QLineF(p1_local, p2_local));
|
|
|
|
|
|
// 更新把手位置
|
|
|
if(m_pTopHandle) {
|
|
|
m_pTopHandle->setPos(p1_local);
|
|
|
}
|
|
|
|
|
|
if(m_pBottomHandle) {
|
|
|
m_pBottomHandle->setPos(p2_local);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 设置编辑模式,并禁用不必要的视觉效果
|
|
|
void nmCrossSectionFractureItem::setEditMode(bool enable)
|
|
|
{
|
|
|
m_bEditMode = enable;
|
|
|
|
|
|
if(m_pTopHandle) {
|
|
|
m_pTopHandle->setVisible(enable);
|
|
|
m_pTopHandle->setEnabled(enable); // 启用/禁用把手交互
|
|
|
// 确保 nmFractureLineHandleItem 也不修改鼠标样式
|
|
|
// 并在其 paint 方法中不绘制选中框
|
|
|
}
|
|
|
|
|
|
if(m_pBottomHandle) {
|
|
|
m_pBottomHandle->setVisible(enable);
|
|
|
m_pBottomHandle->setEnabled(enable);
|
|
|
// 确保 nmFractureLineHandleItem 也不修改鼠标样式
|
|
|
}
|
|
|
|
|
|
// 禁用 ItemIsSelectable 以避免选中虚线框,因为我们不需要编辑线样式
|
|
|
setFlag(ItemIsSelectable, false);
|
|
|
setFlag(ItemIsMovable, enable); // 启用/禁用图元整体拖动
|
|
|
// setFlag(ItemSendsGeometryChanges, false); // 不需要发送几何变化,因为我们手动处理
|
|
|
}
|
|
|
|
|
|
// 槽函数:响应把手位置变化(主要影响高度)
|
|
|
void nmCrossSectionFractureItem::onHandlePositionChanged(nmFractureLineHandleItem* handle, QPointF newHandlePosInParent, bool isDragging)
|
|
|
{
|
|
|
if(!m_bEditMode) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
double currentTopY = line().p1().y();
|
|
|
double currentBottomY = line().p2().y();
|
|
|
|
|
|
double desiredNewHandleY = newHandlePosInParent.y();
|
|
|
double newHeight;
|
|
|
double newMidPointY = m_currentFractureMidPointY;
|
|
|
|
|
|
if(handle == m_pTopHandle) {
|
|
|
// 拖动顶部把手
|
|
|
// 1. 限制不能穿过底部把手:新的顶部Y不能大于当前底部Y
|
|
|
double maxAllowedTopY = currentBottomY;
|
|
|
// 2. 限制不能超过储层最小深度:新的顶部Y不能小于 m_minReservoirDepth
|
|
|
double minAllowedTopY = m_minReservoirDepth;
|
|
|
|
|
|
// 综合限制
|
|
|
double actualNewTopY = qBound(minAllowedTopY, desiredNewHandleY, maxAllowedTopY);
|
|
|
|
|
|
newHeight = currentBottomY - actualNewTopY;
|
|
|
newMidPointY = currentBottomY - (newHeight / 2.0); // 重新计算中点Y
|
|
|
} else { // handle == m_pBottomHandle
|
|
|
// 拖动底部把手
|
|
|
// 1. 限制不能穿过顶部把手:新的底部Y不能小于当前顶部Y
|
|
|
double minAllowedBottomY = currentTopY;
|
|
|
// 2. 限制不能超过储层最大深度:新的底部Y不能大于 m_maxReservoirDepth
|
|
|
double maxAllowedBottomY = m_maxReservoirDepth;
|
|
|
|
|
|
// 综合限制
|
|
|
double actualNewBottomY = qBound(minAllowedBottomY, desiredNewHandleY, maxAllowedBottomY);
|
|
|
|
|
|
newHeight = actualNewBottomY - currentTopY;
|
|
|
newMidPointY = currentTopY + (newHeight / 2.0); // 重新计算中点Y
|
|
|
}
|
|
|
|
|
|
// 确保高度非负,尽管前面的 qBound 应该已经处理了
|
|
|
newHeight = qMax(0.0, newHeight);
|
|
|
|
|
|
// 更新视觉,使用新的高度和计算出的新中点Y,并传入储层限制
|
|
|
updateFractureVisuals(m_currentFractureXPosition, newMidPointY, newHeight,
|
|
|
m_minReservoirDepth, m_maxReservoirDepth);
|
|
|
|
|
|
// 发出信号通知父图元
|
|
|
emit fractureGeometryChanged(newHeight, newMidPointY, isDragging, m_fractureIndex);
|
|
|
}
|
|
|
|
|
|
// 鼠标按下事件:处理整体拖动
|
|
|
void nmCrossSectionFractureItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bEditMode && event->button() == Qt::LeftButton) {
|
|
|
// 检查鼠标是否在把手上。如果把手接受了事件,则不处理整体拖动。
|
|
|
// 这里需要更精确的判断,因为 QGraphicsItem 默认会把事件传递给子项。
|
|
|
// 如果 event->lastAcceptedItem() 是把手,则不处理。
|
|
|
// 为了简化,我们只检查 event->pos() 是否在把手范围内。
|
|
|
if(!m_pTopHandle->contains(event->pos()) && !m_pBottomHandle->contains(event->pos())) {
|
|
|
m_lastMousePos = event->pos(); // 存储在图元局部坐标系中的位置
|
|
|
m_isDraggingBody = true;
|
|
|
// 不修改鼠标样式
|
|
|
// 立即发出信号,表示开始拖动
|
|
|
emit fractureGeometryChanged(m_currentFractureHeight, m_currentFractureMidPointY, true, m_fractureIndex);
|
|
|
event->accept(); // 接受事件,阻止传递给父级或下层
|
|
|
return; // 提前返回,不调用基类
|
|
|
}
|
|
|
}
|
|
|
|
|
|
QGraphicsLineItem::mousePressEvent(event); // 传递给基类,处理把手或其他默认行为
|
|
|
}
|
|
|
|
|
|
// 鼠标移动事件:处理整体拖动
|
|
|
void nmCrossSectionFractureItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bEditMode && m_isDraggingBody) {
|
|
|
qreal dy = event->pos().y() - m_lastMousePos.y(); // 局部坐标系中的垂直位移
|
|
|
|
|
|
// 计算期望的新中点Y
|
|
|
double desiredNewMidPointY = m_currentFractureMidPointY + dy;
|
|
|
|
|
|
// 计算期望的裂缝顶部和底部Y坐标
|
|
|
double desiredNewTopY = desiredNewMidPointY - m_currentFractureHeight / 2.0;
|
|
|
double desiredNewBottomY = desiredNewMidPointY + m_currentFractureHeight / 2.0;
|
|
|
|
|
|
double actualNewMidPointY = desiredNewMidPointY; // 最终实际的中点Y
|
|
|
|
|
|
// 限制顶部不能超过储层最小深度
|
|
|
if(desiredNewTopY < m_minReservoirDepth) {
|
|
|
actualNewMidPointY = m_minReservoirDepth + m_currentFractureHeight / 2.0;
|
|
|
}
|
|
|
|
|
|
// 限制底部不能超过储层最大深度
|
|
|
if(desiredNewBottomY > m_maxReservoirDepth) {
|
|
|
actualNewMidPointY = m_maxReservoirDepth - m_currentFractureHeight / 2.0;
|
|
|
}
|
|
|
|
|
|
// 如果同时超出上下限(这种情况通常不会发生,除非高度大于储层高度)
|
|
|
// 优先根据顶部限制调整,或者根据哪个超出更多来调整
|
|
|
|
|
|
// 更新视觉,使用受限后的中点Y,高度保持不变
|
|
|
updateFractureVisuals(m_currentFractureXPosition, actualNewMidPointY, m_currentFractureHeight,
|
|
|
m_minReservoirDepth, m_maxReservoirDepth);
|
|
|
|
|
|
m_lastMousePos = event->pos(); // 更新上次鼠标位置
|
|
|
|
|
|
// 发出信号通知父图元中点Y已改变,并处于拖动中
|
|
|
emit fractureGeometryChanged(m_currentFractureHeight, actualNewMidPointY, true, m_fractureIndex);
|
|
|
event->accept();
|
|
|
} else {
|
|
|
QGraphicsLineItem::mouseMoveEvent(event); // 传递给基类
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 鼠标释放事件:结束整体拖动
|
|
|
void nmCrossSectionFractureItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
|
|
|
{
|
|
|
if(m_bEditMode && m_isDraggingBody && event->button() == Qt::LeftButton) {
|
|
|
m_isDraggingBody = false;
|
|
|
// 发出信号通知父图元中点Y改变结束
|
|
|
emit fractureGeometryChanged(m_currentFractureHeight, m_currentFractureMidPointY, false, m_fractureIndex);
|
|
|
event->accept();
|
|
|
} else {
|
|
|
QGraphicsLineItem::mouseReleaseEvent(event); // 传递给基类
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nmHorizontalFracturedWellCrossSectionItem::nmHorizontalFracturedWellCrossSectionItem(QGraphicsItem *parent)
|
|
|
: nmAbstractWellboreVisual(parent),
|
|
|
m_pHFWellData(nullptr),
|
|
|
m_listCrossSectionFractures(),
|
|
|
m_pWellboreLine(nullptr),
|
|
|
m_pReservoirsItem(nullptr), // 初始化新增的储层图元指针
|
|
|
m_bEditMode(false)
|
|
|
{
|
|
|
// 可以在这里设置一些默认的样式或初始化成员变量
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellCrossSectionItem::updateGeometryVisuals(nmDataWellBase *pDataWell)
|
|
|
{
|
|
|
prepareGeometryChange();
|
|
|
clearAllChildItems(); // 清除所有已有的子项
|
|
|
|
|
|
// 清除内部管理的裂缝列表和井筒/储层指针
|
|
|
qDeleteAll(m_listCrossSectionFractures);
|
|
|
m_listCrossSectionFractures.clear();
|
|
|
m_pWellboreLine = nullptr;
|
|
|
m_pReservoirsItem = nullptr; // 清空指针
|
|
|
|
|
|
m_pDataWell = pDataWell; // 存储几何数据指针
|
|
|
|
|
|
// 检查几何数据是否有效,且类型是否匹配
|
|
|
if(!m_pDataWell || m_pDataWell->getWellType() != NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
qWarning() << "Invalid data well type or null data for Horizontal Fractured Well.";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pDataWell);
|
|
|
|
|
|
if(!pHFWell) {
|
|
|
qWarning() << "Failed to dynamic_cast to nmDataHorizontalFracturedWell.";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_pHFWellData = pHFWell; // 存储强类型指针
|
|
|
|
|
|
// --- 坐标系统:X轴为井筒水平延伸,Y轴为测深 (MD),向下为正 ---
|
|
|
|
|
|
// 获取井筒数据
|
|
|
double dWellboreX = m_pHFWellData->getX().getValue().toDouble(); // 井筒在视图X轴的起点
|
|
|
double dWellLength = m_pHFWellData->getWellLength().getValue().toDouble(); // 井身长度
|
|
|
|
|
|
// 获取井筒的固定Y坐标(深度),即第一个射孔段的测深起点
|
|
|
// 确保 pHFWell->getPerforations() 不为空,且索引0有效
|
|
|
double dWellboreFixedDepthY = 0.0;
|
|
|
|
|
|
if(!m_pHFWellData->getPerforations().isEmpty() && m_pHFWellData->getPerforations()[0]) {
|
|
|
double dWellboreFixedDepth = m_pHFWellData->getFractureHeight().getValue().toDouble();
|
|
|
dWellboreFixedDepthY = dWellboreFixedDepth/2;
|
|
|
} else {
|
|
|
// 设定一个默认值以避免崩溃
|
|
|
dWellboreFixedDepthY = 6150.0; // 或者一个合理的默认深度
|
|
|
}
|
|
|
|
|
|
// 1. 绘制储层矩形 (nmReservoirGraphicsItems) - 最底层
|
|
|
m_pReservoirsItem = new nmReservoirGraphicsItems(this); // 创建实例
|
|
|
addToGroup(m_pReservoirsItem); // 直接添加到组
|
|
|
|
|
|
// 获取储层的最小和最大深度,传递给裂缝图元
|
|
|
qreal minReservoirDepth = m_pReservoirsItem->getMinLayerTop();
|
|
|
qreal maxReservoirDepth = m_pReservoirsItem->getMaxLayerBottom();
|
|
|
|
|
|
// 2. 绘制水平井筒线 (nmWellboreLineGraphicsItem)
|
|
|
m_pWellboreLine = new nmWellboreLineGraphicsItem(this);
|
|
|
m_pWellboreLine->setLine(dWellboreX, minReservoirDepth + dWellboreFixedDepthY, dWellboreX + dWellLength, minReservoirDepth + dWellboreFixedDepthY);
|
|
|
QPen wellborePen(QColor("#d3d3d3"), 0);
|
|
|
wellborePen.setCosmetic(true);
|
|
|
m_pWellboreLine->setStyle(wellborePen);
|
|
|
m_pWellboreLine->setZValue(2); // 设置Z值为2,确保在射孔段之上
|
|
|
addLine(m_pWellboreLine); // 添加到组中
|
|
|
|
|
|
// 3. 绘制裂缝
|
|
|
//m_pHFWellData->setFracs(); // 确保数据模型中裂缝信息是最新的
|
|
|
|
|
|
int dFNum = m_pHFWellData->getNumberOfFractures().getValue().toInt();
|
|
|
double dFHeight = m_pHFWellData->getFractureHeight().getValue().toDouble();
|
|
|
double dFMidPointHeightFromBottom = m_pHFWellData->getFractureMidPointHeight().getValue().toDouble();
|
|
|
|
|
|
// 计算裂缝中心点的Y坐标(绝对深度)
|
|
|
double fractureCenterY = maxReservoirDepth - dFMidPointHeightFromBottom;
|
|
|
|
|
|
QVector<double> fractureMDs = m_pHFWellData->getFractureMds();
|
|
|
|
|
|
int numToDraw = qMin(dFNum, fractureMDs.size());
|
|
|
|
|
|
for(int i = 0; i < numToDraw; ++i) {
|
|
|
double fractureMd = fractureMDs.at(i);
|
|
|
nmCrossSectionFractureItem* pFractureItem = new nmCrossSectionFractureItem(i, this);
|
|
|
|
|
|
// 更新裂缝视觉,并传递储层深度限制
|
|
|
pFractureItem->updateFractureVisuals(fractureMd, fractureCenterY, dFHeight,
|
|
|
minReservoirDepth, maxReservoirDepth);
|
|
|
pFractureItem->setEditMode(m_bEditMode);
|
|
|
|
|
|
connect(pFractureItem, SIGNAL(fractureGeometryChanged(double, double, bool, int)),
|
|
|
this, SLOT(onSingleFractureGeometryChanged(double, double, bool, int)));
|
|
|
|
|
|
m_listCrossSectionFractures.append(pFractureItem);
|
|
|
}
|
|
|
|
|
|
update(); // 请求重绘
|
|
|
}
|
|
|
|
|
|
// 设置编辑模式
|
|
|
void nmHorizontalFracturedWellCrossSectionItem::setEditMode(bool enabled)
|
|
|
{
|
|
|
m_bEditMode = enabled;
|
|
|
|
|
|
// 控制所有裂缝图元的编辑模式
|
|
|
foreach(nmCrossSectionFractureItem* pFractureItem, m_listCrossSectionFractures) {
|
|
|
if(pFractureItem) {
|
|
|
pFractureItem->setEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// TODO: 如果井筒线或其他图元也需要编辑模式,在这里进行控制
|
|
|
}
|
|
|
|
|
|
void nmHorizontalFracturedWellCrossSectionItem::onSingleFractureGeometryChanged(double newHeight, double newMidPointY, bool isDragging, int fractureIndex)
|
|
|
{
|
|
|
if(!m_bEditMode || !m_pHFWellData || !m_pReservoirsItem) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 实时更新视觉,遍历所有裂缝并应用新的高度和中点位置
|
|
|
// 注意:这里的逻辑需要考虑所有裂缝是否同步更新
|
|
|
// 如果所有裂缝的高度和中点都应该相同,那么这里就应该遍历所有裂缝并调用 updateFractureVisuals
|
|
|
qreal minReservoirDepth = m_pReservoirsItem->getMinLayerTop();
|
|
|
qreal maxReservoirDepth = m_pReservoirsItem->getMaxLayerBottom();
|
|
|
|
|
|
QVector<double> fractureMDs = m_pHFWellData->getFractureMds();
|
|
|
|
|
|
for(int i = 0; i < m_listCrossSectionFractures.size() && i < fractureMDs.size(); ++i) {
|
|
|
nmCrossSectionFractureItem* pFractureItem = m_listCrossSectionFractures.at(i);
|
|
|
|
|
|
if(pFractureItem) {
|
|
|
pFractureItem->updateFractureVisuals(fractureMDs.at(i), newMidPointY, newHeight,
|
|
|
minReservoirDepth, maxReservoirDepth);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 拖动结束时,发出信号给控制器,传递所有更新后的数据
|
|
|
if(!isDragging) {
|
|
|
emit sigFractureGeometryChanged(newHeight, newMidPointY);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// nmAbstractWellboreView 构造函数
|
|
|
nmAbstractWellboreView::nmAbstractWellboreView(QWidget *parent)
|
|
|
: QGraphicsView(parent), m_scene(nullptr), m_currentWellboreVisual(nullptr)
|
|
|
{
|
|
|
setupScene(); // 初始化场景
|
|
|
setRenderHint(QPainter::Antialiasing); // 启用抗锯齿渲染,使图形更平滑
|
|
|
//setTransformationAnchor(AnchorUnderMouse); // 设置缩放和平移的锚点为鼠标位置
|
|
|
//setResizeAnchor(AnchorUnderMouse); // 设置调整大小时的锚点
|
|
|
//setDragMode(QGraphicsView::ScrollHandDrag); // 允许平移场景
|
|
|
//setDragMode(QGraphicsView::NoDrag);
|
|
|
//setInteractive(true); // 确保启用交互
|
|
|
|
|
|
//setDragMode(QGraphicsView::ScrollHandDrag); // 允许平移场景
|
|
|
|
|
|
// 禁止拖拽场景
|
|
|
setDragMode(QGraphicsView::NoDrag);
|
|
|
|
|
|
setInteractive(true); // 确保启用交互
|
|
|
setMouseTracking(true); // 启用鼠标跟踪,以便实时获取鼠标位置
|
|
|
|
|
|
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 关闭水平滚动条
|
|
|
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // 关闭垂直滚动条
|
|
|
|
|
|
// 设置鼠标滚轮缩放和锚点
|
|
|
setTransformationAnchor(QGraphicsView::AnchorUnderMouse); // 缩放和平移的锚点设为鼠标位置
|
|
|
setResizeAnchor(QGraphicsView::AnchorUnderMouse); // 调整大小时的锚点
|
|
|
}
|
|
|
|
|
|
// AbstractWellboreView 析构函数
|
|
|
nmAbstractWellboreView::~nmAbstractWellboreView()
|
|
|
{
|
|
|
// 场景的删除会处理其内部所有图形项的删除
|
|
|
delete m_scene;
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreView::mouseMoveEvent(QMouseEvent *event)
|
|
|
{
|
|
|
// 将视图坐标(像素)转换为场景坐标
|
|
|
// 场景坐标已经包含了视图自身的变换 (如 Y 轴翻转)
|
|
|
QPointF scenePos = mapToScene(event->pos());
|
|
|
emit mouseCoordinatesChanged(scenePos.x(), scenePos.y());
|
|
|
QGraphicsView::mouseMoveEvent(event); // 调用基类实现,确保拖拽等功能正常
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreView::enterEvent(QEvent *event)
|
|
|
{
|
|
|
emit mouseEnteredView(); // 鼠标进入视图区域时通知主窗口
|
|
|
QGraphicsView::enterEvent(event);
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreView::leaveEvent(QEvent *event)
|
|
|
{
|
|
|
emit mouseLeftView(); // 鼠标离开视图区域时通知主窗口
|
|
|
QGraphicsView::leaveEvent(event);
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreView::wheelEvent(QWheelEvent *event)
|
|
|
{
|
|
|
// 获取滚轮旋转的delta值
|
|
|
int nDelta = event->delta();
|
|
|
|
|
|
// 根据delta值判断缩放方向
|
|
|
if(nDelta > 0) {
|
|
|
// 向上/向前滚动,放大
|
|
|
zoomIn();
|
|
|
} else {
|
|
|
// 向下/向后滚动,缩小
|
|
|
zoomOut();
|
|
|
}
|
|
|
|
|
|
//// 计算缩放因子
|
|
|
//qreal factor = (event->delta() > 0) ? 1.15 : 1.0 / 1.15;
|
|
|
|
|
|
//// 执行缩放
|
|
|
//scale(factor, factor);
|
|
|
|
|
|
//// 1. 获取鼠标在场景中的位置(这将是我们的缩放中心点)
|
|
|
//// Qt4 中使用 event->pos() 获取鼠标在视图中的 QPoint (整数坐标)
|
|
|
//const QPointF scenePos = mapToScene(event->pos());
|
|
|
|
|
|
//// 2. 计算缩放因子
|
|
|
//// Qt4 中使用 event->delta() 获取滚轮滚动的量。
|
|
|
//// 正值通常表示向上滚动(放大),负值表示向下滚动(缩小)。
|
|
|
//qreal scaleFactor = event->delta() > 0 ? 1.15 : 1.0 / 1.15; // 每次缩放的比例
|
|
|
|
|
|
//// 3. 构建新的变换矩阵
|
|
|
//QTransform newTransform;
|
|
|
//// a. 将场景的原点平移到鼠标位置 (scenePos)
|
|
|
//newTransform.translate(scenePos.x(), scenePos.y());
|
|
|
//// b. 在鼠标位置(临时原点)处执行缩放
|
|
|
//newTransform.scale(scaleFactor, scaleFactor);
|
|
|
//// c. 将场景的原点再平移回来
|
|
|
//newTransform.translate(-scenePos.x(), -scenePos.y());
|
|
|
|
|
|
//// 4. 应用新的变换到视图的当前变换之上
|
|
|
//// 顺序很重要:newTransform * this->transform() 意味着先应用当前变换,再应用新的缩放平移
|
|
|
//setTransform(newTransform * this->transform());
|
|
|
|
|
|
// 5. 接受事件,防止事件继续传播
|
|
|
//event->accept();
|
|
|
|
|
|
//qreal s=0;
|
|
|
//bool in = true;
|
|
|
//bool out = true;
|
|
|
|
|
|
//qreal scaleValue = 1.5;
|
|
|
|
|
|
//if(event->delta() >= 0)
|
|
|
//{
|
|
|
// scaleValue += 1;
|
|
|
//}
|
|
|
//if(event->delta() < 0)
|
|
|
//{
|
|
|
// scaleValue -= 1;
|
|
|
//}
|
|
|
|
|
|
//if( scaleValue < -5 )
|
|
|
//{
|
|
|
// scaleValue = -5;
|
|
|
// in = false;
|
|
|
// return;
|
|
|
//}else if( scaleValue > 5 )
|
|
|
//{
|
|
|
// scaleValue = 5;
|
|
|
// out = false;
|
|
|
// return;
|
|
|
//}
|
|
|
|
|
|
//if(in)
|
|
|
//{
|
|
|
// s = qPow(1.01,event->delta()/10);
|
|
|
//}
|
|
|
//if(out)
|
|
|
//{
|
|
|
// s = qPow(1/1.01,-event->delta()/10);
|
|
|
//}
|
|
|
//scale(s,s);
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreView::zoomIn()
|
|
|
{
|
|
|
qreal qScaleStepFactor = 1.15; // 每次缩放的步长
|
|
|
scale(qScaleStepFactor, qScaleStepFactor);
|
|
|
}
|
|
|
|
|
|
void nmAbstractWellboreView::zoomOut()
|
|
|
{
|
|
|
qreal qScaleStepFactor = 1.15; // 每次缩放的步长
|
|
|
scale(1.0 / qScaleStepFactor, 1.0 / qScaleStepFactor);
|
|
|
}
|
|
|
|
|
|
// 初始化 QGraphicsScene
|
|
|
void nmAbstractWellboreView::setupScene()
|
|
|
{
|
|
|
m_scene = new QGraphicsScene(this); // 创建新的场景
|
|
|
// 不需要设置场景大小范围,默认无限大
|
|
|
//m_scene->setSceneRect(-1000, -1000, 2000, 2000); // 设定场景范围
|
|
|
setScene(m_scene); // 将场景设置到视图上
|
|
|
}
|
|
|
|
|
|
// 清理旧的井筒图元并准备场景以显示新的图元
|
|
|
void nmAbstractWellboreView::clearAndPrepareScene()
|
|
|
{
|
|
|
// 如果存在当前的井筒组合图元,则从场景中移除并删除它
|
|
|
if(m_currentWellboreVisual) {
|
|
|
m_scene->removeItem(m_currentWellboreVisual);
|
|
|
delete m_currentWellboreVisual;
|
|
|
m_currentWellboreVisual = nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// === nmWxWellboreTopView 的实现 ===
|
|
|
nmWxWellboreTopView::nmWxWellboreTopView(QWidget *parent)
|
|
|
: nmAbstractWellboreView(parent)
|
|
|
{
|
|
|
m_bIsInAddPerforationMode = false;
|
|
|
m_bIsInDeletePerforationMode = false;
|
|
|
}
|
|
|
|
|
|
// 更新俯视图
|
|
|
void nmWxWellboreTopView::updateView(nmDataWellBase* geometry)
|
|
|
{
|
|
|
// 1. 清理旧的图元,并根据新的geometry更新显示
|
|
|
clearAndPrepareScene(); // 清理旧的井筒图元
|
|
|
|
|
|
// 清空场景
|
|
|
m_scene->clear();
|
|
|
|
|
|
if(!geometry) {
|
|
|
return; // 没有数据则返回
|
|
|
}
|
|
|
|
|
|
m_currentWellboreVisual = createWellboreVisual(geometry->getWellType()); // 创建或获取可视化图元
|
|
|
|
|
|
if(m_currentWellboreVisual) {
|
|
|
m_scene->addItem(m_currentWellboreVisual); // 添加到场景
|
|
|
m_currentWellboreVisual->updateGeometryVisuals(geometry); // 更新图元几何形状
|
|
|
|
|
|
// 仅当是 nmVerticalWellTopViewItem 类型时才连接其井筒点拖拽信号
|
|
|
if(nmVerticalWellTopViewItem * pVerticalWellTopView = dynamic_cast<nmVerticalWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
// 连接 nmVerticalWellTopViewItem 的井筒点拖拽完成信号,转发给主视图
|
|
|
connect(pVerticalWellTopView, SIGNAL(sigWellborePointDragFinished(QPointF)),
|
|
|
this, SIGNAL(sigWellborePointMovedInTopView(QPointF)));
|
|
|
}
|
|
|
|
|
|
// 仅当是 nmVerticalFracturedWellTopViewItem 类型时才连接其裂缝拖拽信号
|
|
|
if(nmVerticalFracturedWellTopViewItem * pVFWellTopView = dynamic_cast<nmVerticalFracturedWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
// 连接 nmVerticalFracturedWellTopViewItem 的射孔拖拽完成信号,转发给主视图
|
|
|
connect(pVFWellTopView, SIGNAL(sigFracturePosChanged(double, double)),
|
|
|
this, SIGNAL(sigFracturePosChangedInTopView(double, double)));
|
|
|
}
|
|
|
|
|
|
// 仅当是 nmHorizontalFracturedWellTopViewItem 类型时才连接其射孔拖拽信号
|
|
|
if(nmHorizontalFracturedWellTopViewItem * pHFWellTopView = dynamic_cast<nmHorizontalFracturedWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
// 连接 nmHorizontalFracturedWellTopViewItem 的射孔拖拽完成信号,转发给主视图
|
|
|
connect(pHFWellTopView, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragInTopView(nmDataPerforation*, double, double)));
|
|
|
// 连接 nmHorizontalFracturedWellTopViewItem 的裂缝半长拖拽完成信号,转发给主视图
|
|
|
connect(pHFWellTopView, SIGNAL(sigFractureHalfLengthChanged(double)),
|
|
|
this, SIGNAL(sigFractureHalfLengthChanged(double)));
|
|
|
// 连接 nmHorizontalFracturedWellTopViewItem 的井身拖拽完成信号,转发给主视图
|
|
|
connect(pHFWellTopView, SIGNAL(sigWellboreDragFinished(QPointF)),
|
|
|
this, SIGNAL(sigWellboreDragFinished(QPointF)));
|
|
|
}
|
|
|
|
|
|
// 获取井口坐标
|
|
|
double dX = geometry->getX().getValue().toDouble();
|
|
|
double dY = geometry->getY().getValue().toDouble();
|
|
|
|
|
|
// 定义场景的可见区域大小(根据视图尺寸动态调整)
|
|
|
qreal viewWidth = this->viewport()->width();
|
|
|
qreal viewHeight = this->viewport()->height();
|
|
|
qreal sceneMargin = 1000.0; // 边距
|
|
|
|
|
|
// 计算场景矩形(以井口为中心,范围略大于视图尺寸)
|
|
|
QRectF sceneRect3(
|
|
|
dX - viewWidth / 2.0 - sceneMargin, // 左边界
|
|
|
dY - viewHeight / 2.0 - sceneMargin, // 上边界
|
|
|
viewWidth + 2 * sceneMargin, // 宽度
|
|
|
viewHeight + 2 * sceneMargin // 高度
|
|
|
);
|
|
|
|
|
|
// TODO:后续再修改,先临时设置
|
|
|
m_scene->setSceneRect(sceneRect3);
|
|
|
|
|
|
//// 2. 重置视图变换,让当前井处于视图中心
|
|
|
//resetViewTransform();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTopView::setPerForationEditMode(bool enabled)
|
|
|
{
|
|
|
if(auto topViewItem = dynamic_cast<nmHorizontalFracturedWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
topViewItem->setPerforationEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTopView::setAddPerforationMode(bool enable)
|
|
|
{
|
|
|
m_bIsInAddPerforationMode = enable;
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTopView::setPerforationDeleteMode(bool enabled)
|
|
|
{
|
|
|
m_bIsInDeletePerforationMode = enabled;
|
|
|
}
|
|
|
|
|
|
#include <QTransform>
|
|
|
|
|
|
void nmWxWellboreTopView::resetViewTransform()
|
|
|
{
|
|
|
resetTransform(); // 重置为单位矩阵,Y轴向上为正
|
|
|
|
|
|
// 应用 Y 轴翻转(向上为正)
|
|
|
scale(1, -1);
|
|
|
|
|
|
// 在所有视图变换设置完毕后,再进行中心化操作
|
|
|
if(m_currentWellboreVisual && m_currentWellboreVisual->getWellData()) {
|
|
|
nmDataWellBase* pGeometry = m_currentWellboreVisual->getWellData();
|
|
|
|
|
|
// 如果是多段压裂水平井,则视图中心为井筒中心
|
|
|
if(pGeometry->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
nmDataHorizontalFracturedWell* pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pGeometry);
|
|
|
|
|
|
if(pHFWell) { // 如果是多段压裂水平井
|
|
|
// 获取井的起点
|
|
|
double dStartX = pHFWell->getX().getValue().toDouble();
|
|
|
double dStartY = pHFWell->getY().getValue().toDouble();
|
|
|
|
|
|
// 获取井长和角度
|
|
|
double dWellLength = pHFWell->getWellLength().getValue().toDouble();
|
|
|
double dDrainAngleRad = pHFWell->getDrainAngle().getValue().toDouble() * M_PI / 180.0;
|
|
|
|
|
|
// 计算井的终点
|
|
|
double dEndX = dStartX + dWellLength * qCos(dDrainAngleRad);
|
|
|
double dEndY = dStartY + dWellLength * qSin(dDrainAngleRad);
|
|
|
|
|
|
// 计算井筒的中点
|
|
|
double dCenterX = (dStartX + dEndX) / 2.0;
|
|
|
double dCenterY = (dStartY + dEndY) / 2.0;
|
|
|
|
|
|
centerOn(dCenterX, dCenterY);
|
|
|
}
|
|
|
} else {
|
|
|
double dX = pGeometry->getX().getValue().toDouble();
|
|
|
double dY = pGeometry->getY().getValue().toDouble();
|
|
|
|
|
|
centerOn(dX, dY);
|
|
|
}
|
|
|
} else if(m_scene && !m_scene->items().isEmpty()) {
|
|
|
centerOn(m_scene->itemsBoundingRect().center());
|
|
|
} else {
|
|
|
centerOn(0, 0);
|
|
|
}
|
|
|
|
|
|
update();
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTopView::setEditMode(bool enabled)
|
|
|
{
|
|
|
if(auto vTopViewItem = dynamic_cast<nmVerticalWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
vTopViewItem->setEditMode(enabled);
|
|
|
} else if(auto VFtopViewItem = dynamic_cast<nmVerticalFracturedWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
VFtopViewItem->setEditMode(enabled);
|
|
|
} else if(auto HFtopViewItem = dynamic_cast<nmHorizontalFracturedWellTopViewItem * >(m_currentWellboreVisual)) {
|
|
|
HFtopViewItem->setEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 工厂方法:根据井类型创建适合俯视图的组合图元实例
|
|
|
nmAbstractWellboreVisual* nmWxWellboreTopView::createWellboreVisual(NM_WELL_MODEL type)
|
|
|
{
|
|
|
switch(type) {
|
|
|
case Vertical_Well:
|
|
|
return new nmVerticalWellTopViewItem();
|
|
|
|
|
|
case Vertical_Fractured_Well:
|
|
|
return new nmVerticalFracturedWellTopViewItem();
|
|
|
|
|
|
case Horizontal_Fractured_Well:
|
|
|
return new nmHorizontalFracturedWellTopViewItem();
|
|
|
|
|
|
default:
|
|
|
return nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTopView::mousePressEvent(QMouseEvent *event)
|
|
|
{
|
|
|
// 添加射孔段模式处理
|
|
|
if(m_bIsInAddPerforationMode && event->button() == Qt::LeftButton) {
|
|
|
nmHorizontalFracturedWellTopViewItem* pHFWellItem =
|
|
|
dynamic_cast<nmHorizontalFracturedWellTopViewItem*>(m_currentWellboreVisual);
|
|
|
|
|
|
if(pHFWellItem) {
|
|
|
QPointF scenePos = mapToScene(event->pos()); // 将鼠标位置转换为场景坐标
|
|
|
double fractureTopMd = -1.0;
|
|
|
double fractureBottomMd = -1.0;
|
|
|
QLineF wellboreVisualLine = pHFWellItem->getWellboreLine();
|
|
|
|
|
|
// 发射信号,传递所有参数
|
|
|
emit sigRequestAddPerforation(scenePos, fractureTopMd, fractureBottomMd, wellboreVisualLine);
|
|
|
}
|
|
|
|
|
|
event->accept(); // 阻止事件进一步传播,确保只处理一次点击
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 删除射孔模式的处理
|
|
|
if(m_bIsInDeletePerforationMode && event->button() == Qt::LeftButton) {
|
|
|
QPointF scenePos = mapToScene(event->pos()); // 将鼠标位置转换为场景坐标
|
|
|
|
|
|
// 获取点击位置的所有图形项,默认按Z值从高到低排序
|
|
|
QList<QGraphicsItem*> clickedItems = scene()->items(scenePos);
|
|
|
nmPerforationGraphicsItem *perforationItem = nullptr;
|
|
|
|
|
|
// 遍历所有点击到的项,找到第一个射孔段图元
|
|
|
foreach(QGraphicsItem *item, clickedItems) {
|
|
|
perforationItem = dynamic_cast<nmPerforationGraphicsItem*>(item);
|
|
|
|
|
|
if(perforationItem) {
|
|
|
// 找到了射孔段图元,停止查找
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(perforationItem) { // 如果点击的是一个射孔段图形项
|
|
|
nmDataPerforation* pPerforationData = perforationItem->getPerforationData(); // 获取关联的射孔数据
|
|
|
|
|
|
if(pPerforationData) {
|
|
|
// 直接发射信号,将删除请求发送给控制器
|
|
|
emit sigRequestDeletePerforation(pPerforationData);
|
|
|
event->accept();
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nmAbstractWellboreView::mousePressEvent(event); // 调用基类的鼠标事件处理
|
|
|
}
|
|
|
|
|
|
// === nmWxWellboreCrossSectionView 的实现 ===
|
|
|
nmWxWellboreCrossSectionView::nmWxWellboreCrossSectionView(QWidget *parent) : nmAbstractWellboreView(parent)
|
|
|
{
|
|
|
m_bIsInAddPerforationMode = false;
|
|
|
m_bIsInDeletePerforationMode = false;
|
|
|
m_isInitialFitDone = false;
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::updateView(nmDataWellBase* geometry)
|
|
|
{
|
|
|
clearAndPrepareScene();
|
|
|
m_scene->clear();
|
|
|
|
|
|
m_currentWellboreVisual = createWellboreVisual(geometry->getWellType()); // 创建或获取可视化图元
|
|
|
|
|
|
if(m_currentWellboreVisual) {
|
|
|
|
|
|
m_scene->addItem(m_currentWellboreVisual); // 添加到场景
|
|
|
m_currentWellboreVisual->updateGeometryVisuals(geometry); // 更新图元几何形状
|
|
|
|
|
|
// 仅当是 nmVerticalWellCrossSectionItem和nmVerticalFracturedWellCrossSectionItem 类型时才连接其井筒点拖拽信号
|
|
|
if(nmVerticalWellCrossSectionItem * pVWellCrossView = dynamic_cast<nmVerticalWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
// 连接 nmVerticalWellCrossSectionItem 的射孔拖拽完成信号,转发给主视图
|
|
|
connect(pVWellCrossView, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragInCrossView(nmDataPerforation*, double, double)));
|
|
|
} else if(nmVerticalFracturedWellCrossSectionItem * pVFWellCrossView = dynamic_cast<nmVerticalFracturedWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
// 连接 nmVerticalFracturedWellCrossSectionItem 的射孔拖拽完成信号,转发给主视图
|
|
|
connect(pVFWellCrossView, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragInCrossView(nmDataPerforation*, double, double)));
|
|
|
// 转发裂缝几何位置更新的信号
|
|
|
connect(pVFWellCrossView, SIGNAL(sigRequestFractureRectUpdate(qreal, qreal)),
|
|
|
this, SIGNAL(sigRequestFractureRectUpdate(qreal, qreal)));
|
|
|
// 射孔更新
|
|
|
connect(pVFWellCrossView, SIGNAL(sigFractureDragFinished(const QMap<nmDataPerforation*, QPair<double, double>>&)),
|
|
|
this, SIGNAL(sigFractureDragFinished(const QMap<nmDataPerforation*, QPair<double, double>>&)));
|
|
|
} else if(nmHorizontalFracturedWellCrossSectionItem * pHFWellCrossView = dynamic_cast<nmHorizontalFracturedWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
// 连接 nmHorizontalFracturedWellCrossSectionItem 的裂缝拖拽完成信号,转发给主视图
|
|
|
connect(pHFWellCrossView, SIGNAL(sigFractureGeometryChanged(double, double)),
|
|
|
this, SIGNAL(sigFractureGeometryChanged(double, double)));
|
|
|
}
|
|
|
|
|
|
double dCenterX = geometry->getX().getValue().toDouble();
|
|
|
double dWellLength = geometry->getWellLength().getValue().toDouble();
|
|
|
double dBottomholeMD = geometry->getBottomholeMD().getValue().toDouble();
|
|
|
double dCenterY = dBottomholeMD + dWellLength / 2.0;
|
|
|
|
|
|
// 多段压裂水平井的截面图是正常居中的,不需要额外设置
|
|
|
if (geometry->getWellType() ==Horizontal_Fractured_Well)
|
|
|
{
|
|
|
// 获取井的起点
|
|
|
double dStartX = geometry->getX().getValue().toDouble();
|
|
|
double dStartY = geometry->getPerforations()[0]->getMdStart().getValue().toDouble();
|
|
|
|
|
|
// 获取井长
|
|
|
double dWellLength = geometry->getWellLength().getValue().toDouble();
|
|
|
|
|
|
// 计算井的终点
|
|
|
double dEndX = dStartX + dWellLength;
|
|
|
double dEndY = dStartY;
|
|
|
|
|
|
// 计算井筒的中点
|
|
|
dCenterX = (dStartX + dEndX) / 2.0;
|
|
|
dCenterY = (dStartY + dEndY) / 2.0;
|
|
|
}
|
|
|
|
|
|
// 定义场景的可见区域大小(根据视图尺寸动态调整)
|
|
|
qreal viewWidth = this->viewport()->width();
|
|
|
qreal viewHeight = this->viewport()->height();
|
|
|
qreal sceneMargin = 1000.0; // 边距
|
|
|
|
|
|
// 计算场景矩形(以井口为中心,范围略大于视图尺寸)
|
|
|
QRectF sceneRect3(
|
|
|
dCenterX - viewWidth / 2.0 - sceneMargin, // 左边界
|
|
|
dCenterY - viewHeight / 2.0 - sceneMargin, // 上边界
|
|
|
viewWidth + 2 * sceneMargin, // 宽度
|
|
|
viewHeight + 2 * sceneMargin // 高度
|
|
|
);
|
|
|
|
|
|
// TODO:后续再修改,先临时设置
|
|
|
m_scene->setSceneRect(sceneRect3);
|
|
|
|
|
|
m_isInitialFitDone = false;
|
|
|
// 重新设置视图变换以适应新的数据
|
|
|
//resetViewTransform();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::resizeEvent(QResizeEvent *event)
|
|
|
{
|
|
|
// 调用基类的实现
|
|
|
QGraphicsView::resizeEvent(event);
|
|
|
|
|
|
// 如果场景内容为空,或者初始缩放已经完成,则不做任何事
|
|
|
if (!m_scene || m_scene->items().isEmpty() || m_isInitialFitDone) {
|
|
|
return;
|
|
|
}
|
|
|
// 调用缩放函数
|
|
|
resetViewTransform();
|
|
|
|
|
|
// 将标志位置为 true,防止后续的 resize 事件(如用户拖动窗口)再次触发
|
|
|
m_isInitialFitDone = true;
|
|
|
}
|
|
|
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::setPerForationEditMode(bool enabled)
|
|
|
{
|
|
|
if(auto verticalCrossViewItem = dynamic_cast<nmVerticalWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
verticalCrossViewItem->setPerforationEditMode(enabled);
|
|
|
} else if(auto VerticalFCrossViewItem = dynamic_cast<nmVerticalFracturedWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
VerticalFCrossViewItem->setPerforationEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::setAddPerforationMode(bool enable)
|
|
|
{
|
|
|
m_bIsInAddPerforationMode = enable;
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::setPerforationDeleteMode(bool enabled)
|
|
|
{
|
|
|
m_bIsInDeletePerforationMode = enabled;
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::resetViewTransform()
|
|
|
{
|
|
|
// 1. 检查核心显示对象和场景是否有效
|
|
|
if (!m_currentWellboreVisual || !m_scene || m_scene->items().isEmpty()) {
|
|
|
resetTransform();
|
|
|
centerOn(0, 0);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 2. 从数据管理器获取全局的顶部和底部边界
|
|
|
const qreal minTop = nmDataAnalyzeManager::getCurrentInstance()->getMinLayerTop();
|
|
|
const qreal maxBottom = nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
|
|
|
|
|
|
qreal targetHeight = 0;
|
|
|
QPointF viewCenter;
|
|
|
|
|
|
// 3. 检查获取的全局边界是否有效
|
|
|
if (minTop < maxBottom) {
|
|
|
// 3a. 计算目标高度为所有地层的总跨度
|
|
|
targetHeight = maxBottom - minTop;
|
|
|
|
|
|
// 3b. 计算视图的中心点
|
|
|
// 水平中心 (CenterX): 保持不变,依然是井筒的中心X坐标
|
|
|
//const qreal centerX = m_currentWellboreVisual->boundingRect().center().x();
|
|
|
// 垂直中心 (CenterY): 现在是全局地层范围的中心Y坐标
|
|
|
//const qreal centerY = (minTop + maxBottom) / 2.0;
|
|
|
|
|
|
nmDataWellBase* pGeometry = m_currentWellboreVisual->getWellData();
|
|
|
const qreal centerX = pGeometry->getX().getValue().toDouble(); // 井筒X坐标
|
|
|
const qreal centerY = (minTop + maxBottom) / 2.0;
|
|
|
|
|
|
viewCenter.setX(centerX);
|
|
|
viewCenter.setY(centerY);
|
|
|
|
|
|
} else {
|
|
|
// [后备方案] 如果无法获取有效的全局边界 (例如数据未加载),
|
|
|
// 则退回至基于井筒自身边界的显示逻辑。
|
|
|
viewCenter = m_currentWellboreVisual->boundingRect().center();
|
|
|
targetHeight = m_currentWellboreVisual->boundingRect().height();
|
|
|
// 如果井筒本身高度很小或为0,提供一个最小默认高度
|
|
|
if (targetHeight < 1.0) {
|
|
|
targetHeight = 100.0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 4. 为计算出的目标高度增加垂直边距 (padding),例如总共20%
|
|
|
// 这使得视图的上下边缘与最顶/最底的地层之间有一些空间。
|
|
|
targetHeight *= 1.20;
|
|
|
|
|
|
// 5. 获取视图窗口的宽高比
|
|
|
const QSize viewSize = this->viewport()->size();
|
|
|
if (viewSize.isEmpty() || viewSize.height() == 0) {
|
|
|
centerOn(viewCenter); // 至少执行居中
|
|
|
return;
|
|
|
}
|
|
|
const qreal viewAspectRatio = static_cast<qreal>(viewSize.width()) / viewSize.height();
|
|
|
|
|
|
// 6. 根据目标高度和宽高比,计算目标宽度
|
|
|
const qreal targetWidth = targetHeight * viewAspectRatio;
|
|
|
|
|
|
// 7. 创建目标视野矩形,并将其中心移动到我们计算出的viewCenter
|
|
|
QRectF targetRect(0, 0, targetWidth, targetHeight);
|
|
|
targetRect.moveCenter(viewCenter);
|
|
|
|
|
|
// 8. 应用视图变换,使目标矩形正好填满视图 (逻辑保持不变)
|
|
|
this->fitInView(targetRect, Qt::KeepAspectRatio);
|
|
|
}
|
|
|
|
|
|
//void nmWxWellboreCrossSectionView::resetViewTransform()
|
|
|
//{
|
|
|
// resetTransform();
|
|
|
//
|
|
|
// // 从当前的可视化项中获取数据指针来居中
|
|
|
// if(m_currentWellboreVisual && m_currentWellboreVisual->getWellData()) {
|
|
|
// nmDataWellBase* pGeometry = m_currentWellboreVisual->getWellData();
|
|
|
//
|
|
|
// // 如果是多段压裂水平井,则视图中心为井筒中心
|
|
|
// if(pGeometry->getWellType() == NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
// nmDataHorizontalFracturedWell* pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(pGeometry);
|
|
|
//
|
|
|
// if(pHFWell) { // 如果是多段压裂水平井
|
|
|
// // 获取井的起点
|
|
|
// double dStartX = pHFWell->getX().getValue().toDouble();
|
|
|
// double dStartY = pHFWell->getPerforations()[0]->getMdStart().getValue().toDouble();
|
|
|
//
|
|
|
// // 获取井长
|
|
|
// double dWellLength = pHFWell->getWellLength().getValue().toDouble();
|
|
|
//
|
|
|
// // 计算井的终点
|
|
|
// double dEndX = dStartX + dWellLength;
|
|
|
// double dEndY = dStartY;
|
|
|
//
|
|
|
// // 计算井筒的中点
|
|
|
// double dCenterX = (dStartX + dEndX) / 2.0;
|
|
|
// double dCenterY = (dStartY + dEndY) / 2.0;
|
|
|
//
|
|
|
// centerOn(dCenterX, dCenterY);
|
|
|
// }
|
|
|
// } else {
|
|
|
// double dCenterX = pGeometry->getX().getValue().toDouble();
|
|
|
// double dWellLength = pGeometry->getWellLength().getValue().toDouble();
|
|
|
// double dBottomholeMD = pGeometry->getBottomholeMD().getValue().toDouble();
|
|
|
// double dCenterY = dBottomholeMD + dWellLength / 2.0;
|
|
|
//
|
|
|
// centerOn(dCenterX, dCenterY);
|
|
|
// }
|
|
|
//
|
|
|
// } else if(m_scene && !m_scene->items().isEmpty()) {
|
|
|
// // 如果没有特定的 wellData,但场景中有其他 item,居中到场景的 bounding rect
|
|
|
// centerOn(m_scene->itemsBoundingRect().center());
|
|
|
// } else {
|
|
|
// // 场景为空,居中到 (0,0)
|
|
|
// centerOn(0, 0);
|
|
|
// }
|
|
|
//
|
|
|
// update();
|
|
|
//}
|
|
|
|
|
|
nmAbstractWellboreVisual* nmWxWellboreCrossSectionView::createWellboreVisual(NM_WELL_MODEL type)
|
|
|
{
|
|
|
switch(type) {
|
|
|
case Vertical_Well:
|
|
|
return new nmVerticalWellCrossSectionItem(); // 返回新的纵截面图元实例
|
|
|
|
|
|
case Vertical_Fractured_Well:
|
|
|
return new nmVerticalFracturedWellCrossSectionItem(); // 返回新的纵截面图元实例
|
|
|
|
|
|
case Horizontal_Fractured_Well:
|
|
|
return new nmHorizontalFracturedWellCrossSectionItem(); // 返回新的纵截面图元实例
|
|
|
|
|
|
default:
|
|
|
qWarning() << "Unsupported well type for Cross Section View:" << type;
|
|
|
return nullptr;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::mousePressEvent(QMouseEvent *event)
|
|
|
{
|
|
|
// 添加射孔段模式处理
|
|
|
if(m_bIsInAddPerforationMode && event->button() == Qt::LeftButton) {
|
|
|
QPointF scenePos = mapToScene(event->pos()); // 将鼠标位置转换为场景坐标
|
|
|
|
|
|
// 裂缝MD范围和井筒线段
|
|
|
double fractureTopMd = -1.0;
|
|
|
double fractureBottomMd = -1.0;
|
|
|
QLineF wellboreVisualLine; // 默认是空行
|
|
|
|
|
|
// 根据当前显示的井筒类型,获取特定的参数
|
|
|
if(m_currentWellboreVisual) {
|
|
|
// 尝试将当前图元转换为垂直裂缝井的视图项
|
|
|
nmVerticalFracturedWellCrossSectionItem* pVFWellItem =
|
|
|
dynamic_cast<nmVerticalFracturedWellCrossSectionItem*>(m_currentWellboreVisual);
|
|
|
|
|
|
// 如果转换成功,说明是垂直裂缝井,获取裂缝的MD范围
|
|
|
if(pVFWellItem) {
|
|
|
QRectF fractureRect = pVFWellItem->getFractureMD();
|
|
|
// boundingRect().top() 和 .bottom() 对应于 MD 范围,假设Y轴向上是深度增加
|
|
|
fractureTopMd = fractureRect.top();
|
|
|
fractureBottomMd = fractureRect.bottom();
|
|
|
}
|
|
|
|
|
|
// 如果转换失败,说明是普通的垂直井,fractureTopMd和fractureBottomMd会保持默认值-1.0
|
|
|
}
|
|
|
|
|
|
// 准备好所有参数后,只发射一次信号
|
|
|
emit sigRequestAddPerforation(scenePos, fractureTopMd, fractureBottomMd, wellboreVisualLine);
|
|
|
|
|
|
event->accept(); // 阻止事件进一步传播,确保只处理一次点击
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
// 删除射孔模式的处理
|
|
|
if(m_bIsInDeletePerforationMode && event->button() == Qt::LeftButton) {
|
|
|
QPointF scenePos = mapToScene(event->pos()); // 将鼠标位置转换为场景坐标
|
|
|
|
|
|
// 获取点击位置的所有图形项,默认按Z值从高到低排序
|
|
|
QList<QGraphicsItem*> clickedItems = scene()->items(scenePos);
|
|
|
nmPerforationGraphicsItem *perforationItem = nullptr;
|
|
|
|
|
|
// 遍历所有点击到的项,找到第一个射孔段图元
|
|
|
foreach(QGraphicsItem *item, clickedItems) {
|
|
|
perforationItem = dynamic_cast<nmPerforationGraphicsItem*>(item);
|
|
|
|
|
|
if(perforationItem) {
|
|
|
// 找到了射孔段图元,停止查找
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if(perforationItem) { // 如果点击的是一个射孔段图形项
|
|
|
nmDataPerforation* pPerforationData = perforationItem->getPerforationData(); // 获取关联的射孔数据
|
|
|
|
|
|
if(pPerforationData) {
|
|
|
// 直接发射删除信号
|
|
|
emit sigRequestDeletePerforation(pPerforationData);
|
|
|
event->accept();
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
nmAbstractWellboreView::mousePressEvent(event); // 调用基类的鼠标事件处理
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreCrossSectionView::setFractureEditMode(bool enabled)
|
|
|
{
|
|
|
if(auto VFCrossViewItem = dynamic_cast<nmVerticalFracturedWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
VFCrossViewItem->setFractureEditMode(enabled);
|
|
|
} else if(auto HFCrossViewItem = dynamic_cast<nmHorizontalFracturedWellCrossSectionItem * >(m_currentWellboreVisual)) {
|
|
|
HFCrossViewItem->setEditMode(enabled);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// === nmWxWellbore3DView 的实现 ===
|
|
|
nmWxWellbore3DView::nmWxWellbore3DView(QWidget *parent) : QWidget(parent)
|
|
|
{
|
|
|
QVBoxLayout *pLayout = new QVBoxLayout(this);
|
|
|
QLabel *pLabel = new QLabel("3D View Content Here (3D View)", this);
|
|
|
pLabel->setAlignment(Qt::AlignCenter);
|
|
|
pLayout->addWidget(pLabel);
|
|
|
setLayout(pLayout);
|
|
|
setStyleSheet("background-color: lightcoral;"); // 方便区分
|
|
|
}
|
|
|
|
|
|
|
|
|
nmWxWellboreTrajectoryDisplay::nmWxWellboreTrajectoryDisplay(QWidget *parent) : QWidget(parent), m_pDataWell(nullptr)
|
|
|
{
|
|
|
this->initUI();
|
|
|
}
|
|
|
|
|
|
nmWxWellboreTrajectoryDisplay::~nmWxWellboreTrajectoryDisplay()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::setCurrentWellboreData(nmDataWellBase* pWellData)
|
|
|
{
|
|
|
m_pDataWell = pWellData;
|
|
|
connect(m_pDataWell, SIGNAL(sigWellDataChanged()),
|
|
|
this, SLOT(onWellboreDataChanged()));
|
|
|
|
|
|
// 设置井类型后更新工具栏
|
|
|
this->updateToolBarForCurrentView(m_pStackedWidget->currentIndex());
|
|
|
|
|
|
// 立即触发更新视图
|
|
|
onWellboreDataChanged();
|
|
|
// 视图调整到以井为中心
|
|
|
m_pTopViewWidget->resetViewTransform();
|
|
|
m_pCrossSectionViewWidget->resetViewTransform();
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::setEditWellMode(bool enabled)
|
|
|
{
|
|
|
// 将状态传递给当前激活的子视图
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex(); // 获取当前视图索引
|
|
|
|
|
|
if(nCurrentViewIndex == 0 && m_pTopViewWidget) {
|
|
|
m_pTopViewWidget->setEditMode(enabled);
|
|
|
} else if(nCurrentViewIndex == 1 && m_pCrossSectionViewWidget) {
|
|
|
// 截面图是编辑垂直裂缝井下的裂缝
|
|
|
m_pCrossSectionViewWidget->setFractureEditMode(enabled);
|
|
|
}
|
|
|
|
|
|
// 更新父视图的工具栏按钮选中状态
|
|
|
m_pEditWellAction->setChecked(enabled);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::setPerforationEditMode(bool enabled)
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex(); // 获取当前视图索引
|
|
|
|
|
|
if(nCurrentViewIndex == 0 && m_pTopViewWidget) {
|
|
|
m_pTopViewWidget->setPerForationEditMode(enabled);
|
|
|
} else if(nCurrentViewIndex == 1 && m_pCrossSectionViewWidget) {
|
|
|
m_pCrossSectionViewWidget->setPerForationEditMode(enabled);
|
|
|
}
|
|
|
|
|
|
m_pEditPerforationsAction->setChecked(enabled);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::setAddPerforationMode(bool enabled)
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex(); // 获取当前视图索引
|
|
|
|
|
|
if(nCurrentViewIndex == 0 && m_pTopViewWidget) {
|
|
|
m_pTopViewWidget->setAddPerforationMode(enabled);
|
|
|
} else if(nCurrentViewIndex == 1 && m_pCrossSectionViewWidget) {
|
|
|
m_pCrossSectionViewWidget->setAddPerforationMode(enabled);
|
|
|
}
|
|
|
|
|
|
m_pNewPerforationAction->setChecked(enabled);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::setDeletePerforationMode(bool enabled)
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex(); // 获取当前视图索引
|
|
|
|
|
|
if(nCurrentViewIndex == 0 && m_pTopViewWidget) {
|
|
|
m_pTopViewWidget->setPerforationDeleteMode(enabled);
|
|
|
} else if(nCurrentViewIndex == 1 && m_pCrossSectionViewWidget) {
|
|
|
m_pCrossSectionViewWidget->setPerforationDeleteMode(enabled);
|
|
|
}
|
|
|
|
|
|
m_pDeletePerforationAction->setChecked(enabled);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::setSnapPerforationMode(bool enabled)
|
|
|
{
|
|
|
m_pSnapPerforationAction->setChecked(enabled);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::initUI()
|
|
|
{
|
|
|
// 整体垂直布局
|
|
|
QVBoxLayout *pMainLayout = new QVBoxLayout(this);
|
|
|
pMainLayout->setContentsMargins(5, 5, 5, 5);
|
|
|
pMainLayout->setSpacing(5);
|
|
|
|
|
|
// 1. 创建视图选项区
|
|
|
this->createViewOptions();
|
|
|
pMainLayout->addWidget(m_pViewGroupBox);
|
|
|
|
|
|
// 2. 创建工具栏
|
|
|
this->createToolBar();
|
|
|
pMainLayout->addWidget(m_pToolBar);
|
|
|
|
|
|
// 3. 创建堆栈式视图区域
|
|
|
this->createViewWidgets();
|
|
|
pMainLayout->addWidget(m_pStackedWidget);
|
|
|
|
|
|
// 4. 添加显示鼠标坐标位置的 QLabel
|
|
|
// 为鼠标坐标创建浮动标签
|
|
|
m_pMouseCoordLabel = new QLabel(this); // 父对象设置为当前 widget (nmWxWellboreTrajectoryDisplay)
|
|
|
m_pMouseCoordLabel->setStyleSheet("background-color: lightyellow; border: 1px solid gray; padding: 2px;");
|
|
|
m_pMouseCoordLabel->setVisible(false); // 初始时隐藏
|
|
|
m_pMouseCoordLabel->setWordWrap(false); // 防止文本换行导致宽度变化
|
|
|
m_pMouseCoordLabel->setFixedSize(100, 40); // 设置固定大小
|
|
|
|
|
|
m_bMouseLabelEnabled = false;
|
|
|
|
|
|
this->setLayout(pMainLayout); // 设置主布局
|
|
|
|
|
|
// 5. 连接信号和槽
|
|
|
this->connectSignalsSlots();
|
|
|
|
|
|
// 6. 初始状态:选中顶部视图
|
|
|
m_pTopViewRadio->setChecked(true);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::createViewOptions()
|
|
|
{
|
|
|
m_pViewGroupBox = new QGroupBox(tr("View"), this);
|
|
|
QHBoxLayout *pHLayout = new QHBoxLayout(m_pViewGroupBox);
|
|
|
|
|
|
m_pTopViewRadio = new QRadioButton(tr("Top view"), m_pViewGroupBox);
|
|
|
m_pCrossSectionRadio = new QRadioButton(tr("Cross section"), m_pViewGroupBox);
|
|
|
m_p3DViewRadio = new QRadioButton(tr("3D view"), m_pViewGroupBox);
|
|
|
|
|
|
// 添加到布局器中,并设置伸缩因子
|
|
|
pHLayout->addWidget(m_pTopViewRadio, 1);
|
|
|
pHLayout->addWidget(m_pCrossSectionRadio, 1);
|
|
|
pHLayout->addWidget(m_p3DViewRadio, 1);
|
|
|
|
|
|
m_pViewGroupBox->setLayout(pHLayout);
|
|
|
|
|
|
m_pViewButtonGroup = new QButtonGroup(this);
|
|
|
m_pViewButtonGroup->addButton(m_pTopViewRadio, 0); // ID 0
|
|
|
m_pViewButtonGroup->addButton(m_pCrossSectionRadio, 1); // ID 1
|
|
|
m_pViewButtonGroup->addButton(m_p3DViewRadio, 2); // ID 2
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::createToolBar()
|
|
|
{
|
|
|
QString sAppDir = QCoreApplication::applicationDirPath();
|
|
|
sAppDir = sAppDir.section('/', 0, -2); // 获取上一级目录
|
|
|
// 图标目录
|
|
|
QString sIconDir = sAppDir + "/Res/Icon/";
|
|
|
|
|
|
m_pToolBar = new QToolBar("Tools", this);
|
|
|
m_pToolBar->setIconSize(QSize(35, 35));
|
|
|
|
|
|
m_pToolBar->setFixedHeight(47);
|
|
|
|
|
|
// 创建QAction并添加到工具栏
|
|
|
m_pZoomOutAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore1.png"), "Zoom Out"); // 缩小
|
|
|
m_pHomeViewAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore2.png"), "Home View"); // 复位/恢复
|
|
|
m_pZoomInAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore3.png"), "Zoom In"); // 放大
|
|
|
m_pToolBar->addSeparator();
|
|
|
|
|
|
m_pXYAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore4.png"), "XY"); // 坐标显示
|
|
|
m_pEditWellAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore5.png"), "Edit Well"); // 编辑井筒轨迹
|
|
|
m_pLoadAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore6.png"), "Load"); // 加载
|
|
|
m_pToolBar->addSeparator();
|
|
|
|
|
|
m_pEditPerforationsAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore7.png"), "Edit Perforations"); // 编辑射孔
|
|
|
m_pNewPerforationAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore8.png"), "New Perforation"); // 添加射孔段
|
|
|
m_pDeletePerforationAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore9.png"), "Delete Perforation"); // 删除射孔段
|
|
|
m_pSnapPerforationAction = m_pToolBar->addAction(QIcon(sIconDir + "NmWellbore10.png"), "Snap Perforation"); // 对齐/吸附
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::createViewWidgets()
|
|
|
{
|
|
|
m_pStackedWidget = new QStackedWidget(this);
|
|
|
|
|
|
m_pTopViewWidget = new nmWxWellboreTopView(this);
|
|
|
m_pCrossSectionViewWidget = new nmWxWellboreCrossSectionView(this);
|
|
|
m_p3DViewWidget = new nmWxWellbore3DView(this);
|
|
|
|
|
|
m_pStackedWidget->addWidget(m_pTopViewWidget); // 索引 0
|
|
|
m_pStackedWidget->addWidget(m_pCrossSectionViewWidget); // 索引 1
|
|
|
m_pStackedWidget->addWidget(m_p3DViewWidget); // 索引 2
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::connectSignalsSlots()
|
|
|
{
|
|
|
// 连接QButtonGroup的buttonClicked信号,现在视图切换会发出信号给控制器
|
|
|
connect(m_pViewButtonGroup, SIGNAL(buttonClicked(int)),
|
|
|
this, SLOT(onViewRadioButtonToggled(int)));
|
|
|
|
|
|
// 连接工具栏QAction的triggered信号,现在这些槽函数将发出请求信号给控制器
|
|
|
connect(m_pZoomOutAction, SIGNAL(triggered()), this, SLOT(onZoomOutClicked()));
|
|
|
connect(m_pHomeViewAction, SIGNAL(triggered()), this, SLOT(onHomeViewClicked()));
|
|
|
connect(m_pZoomInAction, SIGNAL(triggered()), this, SLOT(onZoomInClicked()));
|
|
|
|
|
|
connect(m_pXYAction, SIGNAL(triggered()), this, SLOT(onXYClicked())); // XY 坐标显示仍然在视图内管理
|
|
|
|
|
|
// ============== 模式请求信号连接 ==============
|
|
|
// 这些槽函数现在将发出请求给控制器
|
|
|
connect(m_pEditWellAction, SIGNAL(triggered()), this, SLOT(onEditWellClicked()));
|
|
|
connect(m_pEditPerforationsAction, SIGNAL(triggered()), this, SLOT(onEditPerforationsClicked()));
|
|
|
connect(m_pNewPerforationAction, SIGNAL(triggered()), this, SLOT(onNewPerforationClicked()));
|
|
|
connect(m_pDeletePerforationAction, SIGNAL(triggered()), this, SLOT(onDeletePerforationClicked()));
|
|
|
connect(m_pSnapPerforationAction, SIGNAL(triggered()), this, SLOT(onSnapPerforationClicked()));
|
|
|
// ============================================
|
|
|
|
|
|
// 连接来自俯视图的井筒点移动信号 (只在拖动结束时触发)
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigWellborePointMovedInTopView(QPointF)),
|
|
|
this, SIGNAL(sigWellboreTrajectoryPointMoved(QPointF)));
|
|
|
|
|
|
// 连接来自俯视图的射孔段移动信号 (只在拖动结束时触发)
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigPerforationDragInTopView(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)));
|
|
|
// 连接来自纵截面图的射孔段移动信号 (只在拖动结束时触发)
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(sigPerforationDragInCrossView(nmDataPerforation*, double, double)),
|
|
|
this, SIGNAL(sigPerforationDragFinished(nmDataPerforation*, double, double)));
|
|
|
|
|
|
// 连接添加射孔模式下获取到的相关位置
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigRequestAddPerforation(QPointF, double, double, QLineF)),
|
|
|
this, SIGNAL(sigTryAddNewPerforation(QPointF, double, double, QLineF)));
|
|
|
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(sigRequestAddPerforation(QPointF, double, double, QLineF)),
|
|
|
this, SIGNAL(sigTryAddNewPerforation(QPointF, double, double, QLineF)));
|
|
|
|
|
|
// 连接请求删除射孔信号
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigRequestDeletePerforation(nmDataPerforation*)),
|
|
|
this, SIGNAL(sigTryDeletePerforation(nmDataPerforation*)));
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(sigRequestDeletePerforation(nmDataPerforation*)),
|
|
|
this, SIGNAL(sigTryDeletePerforation(nmDataPerforation*)));
|
|
|
|
|
|
// 连接俯视图下更新裂缝位置的信号
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigFracturePosChangedInTopView(double, double)),
|
|
|
this, SIGNAL(sigFractureGeometryDragFinished(double, double)));
|
|
|
// 连接截面图下更新裂缝位置的信号
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(sigRequestFractureRectUpdate(qreal, qreal)),
|
|
|
this, SIGNAL(sigRequestFractureRectUpdate(qreal, qreal)));
|
|
|
// 射孔更新
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(sigFractureDragFinished(const QMap<nmDataPerforation*, QPair<double, double>>&)),
|
|
|
this, SIGNAL(sigFractureDragFinished(const QMap<nmDataPerforation*, QPair<double, double>>&)));
|
|
|
// 多段压裂水平井俯视图裂缝半长更新
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigFractureHalfLengthChanged(double)),
|
|
|
this, SIGNAL(sigFractureHalfLengthChanged(double)));
|
|
|
// 多段压裂水平井俯视图井身位置
|
|
|
connect(m_pTopViewWidget, SIGNAL(sigWellboreDragFinished(QPointF)),
|
|
|
this, SIGNAL(sigWellboreDragFinished(QPointF)));
|
|
|
// 多段压裂水平井截面图裂缝位置更新
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(sigFractureGeometryChanged(double, double)),
|
|
|
this, SIGNAL(sigFractureGeometryChanged(double, double)));
|
|
|
|
|
|
// 连接各个 QGraphicsView 的鼠标坐标和进入/离开信号到主窗口的槽
|
|
|
connect(m_pTopViewWidget, SIGNAL(mouseCoordinatesChanged(double, double)),
|
|
|
this, SLOT(onMouseMovedOnPlot(double, double)));
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(mouseCoordinatesChanged(double, double)),
|
|
|
this, SLOT(onMouseMovedOnPlot(double, double)));
|
|
|
|
|
|
connect(m_pTopViewWidget, SIGNAL(mouseEnteredView()), this, SLOT(onMouseEnteredAnyView()));
|
|
|
connect(m_pTopViewWidget, SIGNAL(mouseLeftView()), this, SLOT(onMouseLeftAnyView()));
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(mouseEnteredView()), this, SLOT(onMouseEnteredAnyView()));
|
|
|
connect(m_pCrossSectionViewWidget, SIGNAL(mouseLeftView()), this, SLOT(onMouseLeftAnyView()));
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onViewRadioButtonToggled(int id)
|
|
|
{
|
|
|
QRadioButton *pSelectedButton = qobject_cast<QRadioButton*>(m_pViewButtonGroup->button(id));
|
|
|
|
|
|
if(pSelectedButton && pSelectedButton->isChecked()) {
|
|
|
m_pStackedWidget->setCurrentIndex(id);
|
|
|
|
|
|
// === 关通知控制器视图已切换,控制器会负责清空模式和重新设置 ===
|
|
|
emit currentViewChanged(id);
|
|
|
|
|
|
// 更新工具栏按钮的启用/禁用状态 (这只与井类型和当前视图有关,与编辑模式状态无关)
|
|
|
updateToolBarForCurrentView(id);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onZoomOutClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
qDebug() << "Zoom Out clicked for current view (ID:" << nCurrentViewIndex << ")";
|
|
|
|
|
|
// 根据当前视图类型调用对应的视图Widget的缩放方法
|
|
|
// 你需要在 nmWxWellboreTopView, nmWxWellboreCrossSectionView, nmWxWellbore3DView 中实现这些方法
|
|
|
switch(nCurrentViewIndex) {
|
|
|
case 0: // Top View
|
|
|
if(m_pTopViewWidget) {
|
|
|
m_pTopViewWidget->zoomOut();
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1: // Cross Section View
|
|
|
if(m_pCrossSectionViewWidget) {
|
|
|
m_pCrossSectionViewWidget->zoomOut();
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2: // 3D View
|
|
|
// if (m_p3DViewWidget) m_p3DViewWidget->zoomOut();
|
|
|
qDebug() << "3D View: Performing Zoom Out";
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onHomeViewClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
qDebug() << "Home View clicked for current view (ID:" << nCurrentViewIndex << ")";
|
|
|
|
|
|
// 类似,调用对应视图的 homeView() 或 resetView() 方法
|
|
|
switch(nCurrentViewIndex) {
|
|
|
case 0: // Top View
|
|
|
if(m_pTopViewWidget) {
|
|
|
// 重置视图变换
|
|
|
m_pTopViewWidget->resetViewTransform();
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1: // Cross Section View
|
|
|
if(m_pCrossSectionViewWidget) {
|
|
|
// 重置视图变换
|
|
|
m_pCrossSectionViewWidget->resetViewTransform();
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2: // 3D View
|
|
|
// if (m_p3DViewWidget) m_p3DViewWidget->homeView();
|
|
|
qDebug() << "3D View: Resetting View";
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onZoomInClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
qDebug() << "Zoom In clicked for current view (ID:" << nCurrentViewIndex << ")";
|
|
|
|
|
|
// 类似 onZoomOutClicked,调用对应视图的 zoomIn() 方法
|
|
|
switch(nCurrentViewIndex) {
|
|
|
case 0: // Top View
|
|
|
if(m_pTopViewWidget) {
|
|
|
m_pTopViewWidget->zoomIn();
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1: // Cross Section View
|
|
|
if(m_pCrossSectionViewWidget) {
|
|
|
m_pCrossSectionViewWidget->zoomIn();
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2: // 3D View
|
|
|
// if (m_p3DViewWidget) m_p3DViewWidget->zoomIn();
|
|
|
qDebug() << "3D View: Performing Zoom In";
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onXYClicked()
|
|
|
{
|
|
|
// 切换全局开关状态
|
|
|
m_bMouseLabelEnabled = !m_bMouseLabelEnabled;
|
|
|
|
|
|
// 根据新的开关状态直接设置标签的可见性
|
|
|
if(m_bMouseLabelEnabled) {
|
|
|
// 如果启用,当鼠标下次进入视图时会显示
|
|
|
// 这里不需要立即设置 m_pMouseCoordLabel->setVisible(true),
|
|
|
// 因为 mouseMoveEvent 会触发
|
|
|
} else {
|
|
|
// 如果禁用,立即隐藏标签并清空文本
|
|
|
m_pMouseCoordLabel->setVisible(false);
|
|
|
m_pMouseCoordLabel->setText("");
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onEditWellClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
// 发出请求信号,控制器会根据 nCurrentViewIndex 决定激活何种模式
|
|
|
emit requestedEditWellMode(nCurrentViewIndex);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onLoadClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
qDebug() << "Load Data clicked for current view (ID:" << nCurrentViewIndex << ")";
|
|
|
// 实现加载数据的逻辑,可能弹出文件对话框或发出信号给控制器
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onEditPerforationsClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
emit requestedEditPerforationsMode(nCurrentViewIndex);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onNewPerforationClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
emit requestedNewPerforationMode(nCurrentViewIndex);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onDeletePerforationClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
emit requestedDeletePerforationMode(nCurrentViewIndex);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onSnapPerforationClicked()
|
|
|
{
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
emit requestedSnapPerforationMode(nCurrentViewIndex);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onWellboreDataChanged()
|
|
|
{
|
|
|
// 1. 通知控制器,清理当前状态
|
|
|
emit sigClearViewStates();
|
|
|
// 2. 更新视图
|
|
|
m_pTopViewWidget->updateView(m_pDataWell);
|
|
|
m_pCrossSectionViewWidget->updateView(m_pDataWell);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onMouseMovedOnPlot(double x, double y)
|
|
|
{
|
|
|
// 获取当前鼠标在 nmWxWellboreTrajectoryDisplay 窗口中的全局位置
|
|
|
QPoint globalMousePos = QCursor::pos();
|
|
|
// 将全局位置转换为 nmWxWellboreTrajectoryDisplay 窗口的局部位置
|
|
|
QPoint localMousePos = this->mapFromGlobal(globalMousePos);
|
|
|
|
|
|
QString axisYLabel;
|
|
|
int nCurrentViewIndex = m_pStackedWidget->currentIndex();
|
|
|
|
|
|
switch(nCurrentViewIndex) {
|
|
|
case 0: // Top View
|
|
|
axisYLabel = "Y";
|
|
|
break;
|
|
|
|
|
|
case 1: // Cross Section View
|
|
|
axisYLabel = "TVD";
|
|
|
break;
|
|
|
|
|
|
case 2: // 3D View (如果它也基于 QGraphicsView)
|
|
|
axisYLabel = "Z";
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
axisYLabel = "Y";
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
m_pMouseCoordLabel->setText(QString("x = %1 m\n%2 = %3 m").arg(x, 0, 'f', 2).arg(axisYLabel).arg(y, 0, 'f', 2));
|
|
|
|
|
|
// 计算 QLabel 的新位置
|
|
|
int labelX = localMousePos.x();
|
|
|
int labelY = localMousePos.y() - m_pMouseCoordLabel->height();
|
|
|
|
|
|
m_pMouseCoordLabel->move(labelX, labelY);
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onMouseEnteredAnyView()
|
|
|
{
|
|
|
// 只有当全局开关m_bMouseLabelEnabled为true时,才显示标签
|
|
|
if(m_bMouseLabelEnabled) {
|
|
|
m_pMouseCoordLabel->setVisible(true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::onMouseLeftAnyView()
|
|
|
{
|
|
|
// 鼠标离开任何一个视图时,隐藏标签并清空文本
|
|
|
m_pMouseCoordLabel->setVisible(false);
|
|
|
m_pMouseCoordLabel->setText("");
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::updateToolBarForCurrentView(int viewIndex)
|
|
|
{
|
|
|
// 默认所有工具都可用,然后根据视图类型进行调整
|
|
|
m_pZoomOutAction->setEnabled(true);
|
|
|
m_pHomeViewAction->setEnabled(true);
|
|
|
m_pZoomInAction->setEnabled(true);
|
|
|
|
|
|
m_pXYAction->setEnabled(true);
|
|
|
m_pEditWellAction->setEnabled(true);
|
|
|
m_pLoadAction->setEnabled(true);
|
|
|
m_pEditPerforationsAction->setEnabled(true);
|
|
|
m_pNewPerforationAction->setEnabled(true);
|
|
|
m_pDeletePerforationAction->setEnabled(true);
|
|
|
m_pSnapPerforationAction->setEnabled(true);
|
|
|
|
|
|
// 默认所有射孔相关工具都禁用
|
|
|
m_pEditPerforationsAction->setEnabled(false);
|
|
|
m_pNewPerforationAction->setEnabled(false);
|
|
|
m_pDeletePerforationAction->setEnabled(false);
|
|
|
m_pSnapPerforationAction->setEnabled(false);
|
|
|
|
|
|
// 获取当前井的类型,如果 m_pDataWell 为空,则默认为未知类型
|
|
|
NM_WELL_MODEL currentWellType = NM_WELL_MODEL::Unknow_Well;
|
|
|
|
|
|
if(m_pDataWell) {
|
|
|
currentWellType = m_pDataWell->getWellType();
|
|
|
}
|
|
|
|
|
|
switch(viewIndex) {
|
|
|
case 0: // nmWxWellboreTopView (俯视图)
|
|
|
qDebug() << "Updating toolbar for Top View";
|
|
|
|
|
|
// 如果是多段压裂水平井,则在俯视图中启用射孔操作
|
|
|
if(currentWellType == NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
m_pEditPerforationsAction->setEnabled(true);
|
|
|
m_pNewPerforationAction->setEnabled(true);
|
|
|
m_pDeletePerforationAction->setEnabled(true);
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 1: // nmWxWellboreCrossSectionView (纵截面视图)
|
|
|
|
|
|
// 如果是直井,则在纵截面视图中启用射孔操作
|
|
|
if(currentWellType != NM_WELL_MODEL::Horizontal_Fractured_Well) {
|
|
|
m_pEditPerforationsAction->setEnabled(true);
|
|
|
m_pNewPerforationAction->setEnabled(true);
|
|
|
m_pDeletePerforationAction->setEnabled(true);
|
|
|
m_pSnapPerforationAction->setEnabled(true);
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 2: // nmWxWellbore3DView (3D视图)
|
|
|
qDebug() << "Updating toolbar for 3D View";
|
|
|
// 3D视图中,所有射孔操作保持禁用状态
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
qDebug() << "Updating toolbar for unknown view index:" << viewIndex;
|
|
|
// 对于未知视图类型,射孔操作保持禁用状态
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void nmWxWellboreTrajectoryDisplay::updataCrossSectionView()
|
|
|
{
|
|
|
m_pCrossSectionViewWidget->updateView(m_pDataWell);
|
|
|
m_pCrossSectionViewWidget->resetViewTransform();
|
|
|
}
|