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

869 lines
32 KiB
C++

This file contains ambiguous Unicode characters!

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

#include "nmWellEditorController.h"
#include "nmDataWellBase.h"
#include "nmDataVerticalWell.h"
#include "nmDataVerticalFracturedWell.h"
#include "nmDataHorizontalFracturedWell.h"
#include "nmWxWellboreTrajectoryDisplay.h"
#include "nmDataAnalyzeManager.h"
nmWellEditorController::nmWellEditorController(nmDataWellBase* pModel, nmWxWellboreTrajectoryDisplay* pView, QObject *parent)
: QObject(parent),
m_pModel(pModel),
m_pView(pView),
m_eCurrentMode(WellEditMode::None)
{
}
nmWellEditorController::~nmWellEditorController()
{
}
void nmWellEditorController::clearAllEditModes()
{
// 关闭所有可能激活的视图模式
if(m_pView) {
m_pView->setEditWellMode(false);
m_pView->setPerforationEditMode(false);
m_pView->setAddPerforationMode(false);
m_pView->setDeletePerforationMode(false);
m_pView->setSnapPerforationMode(false);
}
m_eCurrentMode = WellEditMode::None; // 重置控制器内部模式状态
}
void nmWellEditorController::setMode(WellEditMode eNewMode)
{
if(!m_pView) return;
// 如果请求激活的模式与当前模式相同,则视为取消该模式
if(m_eCurrentMode == eNewMode && eNewMode != WellEditMode::None) {
clearAllEditModes();
return;
}
// 首先,清理所有当前的编辑模式
clearAllEditModes();
// 然后,激活新请求的模式
m_eCurrentMode = eNewMode;
switch(eNewMode) {
case WellEditMode::EditWellboreTrajectory:
m_pView->setEditWellMode(true);
break;
case WellEditMode::EditPerforation:
m_pView->setPerforationEditMode(true);
break;
case WellEditMode::AddPerforation:
m_pView->setAddPerforationMode(true);
break;
case WellEditMode::DeletePerforation:
m_pView->setDeletePerforationMode(true);
break;
case WellEditMode::SnapPerforation:
m_pView->setSnapPerforationMode(true);
// 处理吸附逻辑
snapPerforationToWellbore();
break;
case WellEditMode::None:
default:
// 已经在 clearAllEditModes 中处理
break;
}
}
void nmWellEditorController::onRequestEditWellMode(int nCurrentViewId)
{
// 控制器决定是否允许在当前视图下编辑井筒/裂缝
setMode(WellEditMode::EditWellboreTrajectory);
}
void nmWellEditorController::onRequestEditPerforationsMode(int nCurrentViewId)
{
setMode(WellEditMode::EditPerforation);
}
void nmWellEditorController::onRequestNewPerforationMode(int nCurrentViewId)
{
setMode(WellEditMode::AddPerforation);
}
void nmWellEditorController::onRequestDeletePerforationMode(int nCurrentViewId)
{
setMode(WellEditMode::DeletePerforation);
}
void nmWellEditorController::onRequestSnapPerforationMode(int nCurrentViewId)
{
setMode(WellEditMode::SnapPerforation);
}
void nmWellEditorController::onViewChanged(int nNewViewId)
{
// 视图切换时,控制器应清空所有当前模式,确保状态同步
clearAllEditModes();
// 视图本身已经重置了其工具栏按钮的选中状态
// 如果需要,控制器可以在这里根据 nNewViewId 激活某个视图的默认模式
}
void nmWellEditorController::slotClearViewStates()
{
clearAllEditModes();
}
// 响应井筒点移动信号
void nmWellEditorController::slotWellboreTrajectoryPointMoved(const QPointF& newScenePos)
{
if(!m_pModel) {
return;
}
// 1. 只有直井类型可以修改井点位置
nmDataVerticalWell* pVerticalWell = dynamic_cast<nmDataVerticalWell*>(m_pModel);
if(!pVerticalWell) {
return;
}
double dNewX = newScenePos.x();
double dNewY = newScenePos.y();
// 临时阻塞 m_pModel 的信号
bool bOldSignalsBlocked = m_pModel->blockSignals(true);
// 2. 修改数据模型
// 直接更新 nmDataVerticalWell 中的 X 和 Y 值
pVerticalWell->getX().setValue(dNewX);
pVerticalWell->getY().setValue(dNewY);
// 恢复原始阻塞状态
m_pModel->blockSignals(bOldSignalsBlocked);
// 刷新纵截面视图
m_pView->updataCrossSectionView();
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
m_pModel->notifyParameterChanged();
}
}
void nmWellEditorController::slotPerforationDragFinished(nmDataPerforation* pPerforationData, double proposedMdStart, double proposedMdEnd)
{
if(!pPerforationData) {
return;
}
// 临时阻塞 pPerforationData 的信号
bool bOldSignalsBlocked = pPerforationData->blockSignals(true);
// 2. 修改数据模型
// 直接更新 nmDataVerticalWell 中的 X 和 Y 值
pPerforationData->getMdStart().setValue(proposedMdStart);
pPerforationData->getMdEnd().setValue(proposedMdEnd);
// 恢复原始阻塞状态
pPerforationData->blockSignals(bOldSignalsBlocked);
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
m_pModel->notifyParameterChanged();
}
}
void nmWellEditorController::slotTryAddNewPerforation(const QPointF& scenePos,
double fractureTopMd,
double fractureBottomMd,
const QLineF& wellboreVisualLine)
{
// 1. 确保当前模式是添加射孔模式和由模型数据
if(m_eCurrentMode != WellEditMode::AddPerforation || !m_pModel) {
return;
}
// 是否添加成功
bool bIsAddSuccess = false;
// 2. 根据井筒类型进行分发
switch(m_pModel->getWellType()) {
case NM_WELL_MODEL::Vertical_Well: {
// 垂直井,只需处理场景坐标
bIsAddSuccess = tryAddNewPerforationInVerticalWell(scenePos);
break;
}
case NM_WELL_MODEL::Vertical_Fractured_Well: {
// 垂直裂缝井需要裂缝MD范围
bIsAddSuccess = tryAddNewPerforationInVerticalFracturedWell(scenePos, fractureTopMd, fractureBottomMd);
break;
}
case NM_WELL_MODEL::Horizontal_Fractured_Well: {
// 水平裂缝井需要井筒线段来计算MD
bIsAddSuccess = tryAddNewPerforationInHorizontalFracturedWell(scenePos, wellboreVisualLine);
break;
}
default:
// 未知或不支持的井类型,可以打印警告
qWarning() << "Unsupported well type for adding perforation.";
break;
}
// 添加成功后,退出添加模式
if(bIsAddSuccess) {
setMode(WellEditMode::None);
m_pView->setAddPerforationMode(false);
// 通知发送信号,更新左侧参数视图
m_pModel->notifyParameterChanged();
}
}
bool nmWellEditorController::tryAddNewPerforationInVerticalWell(const QPointF& scenePos)
{
nmDataVerticalWell* pVerticalWell = dynamic_cast<nmDataVerticalWell*>(m_pModel);
if(!pVerticalWell) {
return false;
}
// --- 横坐标距离检查 ---
double dWellboreCenterX = pVerticalWell->getX().getValue().toDouble();
const double dClickToleranceX = 5.0; // 允许点击位置距离井筒中心线的最大水平距离
if(qAbs(scenePos.x() - dWellboreCenterX) > dClickToleranceX) {
// 如果点击位置距离井筒中心太远,则不处理
// 控制器可以选择在这里发出一个信号,通知视图“添加失败”或显示提示
return false;
}
// 1. 获取点击位置对应的测量深度 (MD)
// 对于垂直井场景的Y坐标直接对应MD
double dClickedMd = scenePos.y();
// 2. 检查点击位置是否在井筒的有效MD范围内
double dWellboreTopMd = pVerticalWell->getBottomholeMD().getValue().toDouble();
double dWellboreBottomMd = dWellboreTopMd + pVerticalWell->getWellLength().getValue().toDouble();
if(dClickedMd < dWellboreTopMd || dClickedMd > dWellboreBottomMd) {
return false;
}
// 3. 计算空白区域长度并检查重叠
double dNearestPerforationAboveMd = dWellboreTopMd;
double dNearestPerforationBelowMd = dWellboreBottomMd;
foreach(nmDataPerforation* pExistingPerforation, pVerticalWell->getPerforations()) {
double dExistingStartMd = pExistingPerforation->getMdStart().getValue().toDouble();
double dExistingEndMd = pExistingPerforation->getMdEnd().getValue().toDouble();
if(dClickedMd >= dExistingStartMd && dClickedMd <= dExistingEndMd) {
return false; // 点击在现有射孔段内部,不允许添加
}
if(dExistingEndMd < dClickedMd) {
dNearestPerforationAboveMd = std::max(dNearestPerforationAboveMd, dExistingEndMd);
}
if(dExistingStartMd > dClickedMd) {
dNearestPerforationBelowMd = std::min(dNearestPerforationBelowMd, dExistingStartMd);
}
}
double dAvailableBlankAreaStartMd = dNearestPerforationAboveMd;
double dAvailableBlankAreaEndMd = dNearestPerforationBelowMd;
if(dAvailableBlankAreaStartMd >= dAvailableBlankAreaEndMd) {
return false; // 没有足够的空白空间
}
// 4. 计算新射孔段的长度和位置
double dMaxAvailableLength = dAvailableBlankAreaEndMd - dAvailableBlankAreaStartMd;
double dActualPerforationLength = dMaxAvailableLength / 3.0; // 长度为可用空白区域的三分之一
double dNewPerforationStartMd = dClickedMd - dActualPerforationLength / 2.0;
double dNewPerforationEndMd = dClickedMd + dActualPerforationLength / 2.0;
// 调整确保新射孔段完全在计算出的空白区域内
if(dNewPerforationStartMd < dAvailableBlankAreaStartMd) {
dNewPerforationStartMd = dAvailableBlankAreaStartMd;
dNewPerforationEndMd = dNewPerforationStartMd + dActualPerforationLength;
}
if(dNewPerforationEndMd > dAvailableBlankAreaEndMd) {
dNewPerforationEndMd = dAvailableBlankAreaEndMd;
dNewPerforationStartMd = dNewPerforationEndMd - dActualPerforationLength;
}
// 再次检查最终计算出的射孔段是否仍然有效且不重叠
// 主要是防止由于浮点数计算误差导致边界问题
if(dNewPerforationStartMd >= dNewPerforationEndMd || dNewPerforationStartMd < dAvailableBlankAreaStartMd || dNewPerforationEndMd > dAvailableBlankAreaEndMd) {
return false;
}
// 5. 创建并添加新的射孔数据模型 (nmDataPerforation)
// 临时阻塞 Model 的信号,避免在添加过程中重复触发视图刷新
bool bOldSignalsBlocked = pVerticalWell->blockSignals(true);
nmDataPerforation* dNewPerforationData = new nmDataPerforation();
dNewPerforationData->getName().setValue("Perforation#" + QString::number(pVerticalWell->getPerforations().size() + 1));
dNewPerforationData->getMdStart().setValue(dNewPerforationStartMd);
dNewPerforationData->getMdEnd().setValue(dNewPerforationEndMd);
dNewPerforationData->getSkin().setValue(0.0);
// 恢复 Model 的信号
pVerticalWell->blockSignals(bOldSignalsBlocked);
// 添加射孔,会触发井数据改变信号,刷新视图
pVerticalWell->addPerforation(dNewPerforationData);
return true;
}
bool nmWellEditorController::tryAddNewPerforationInVerticalFracturedWell(const QPointF& scenePos, const double& fractureTopMd, const double& fractureBottomMd)
{
nmDataVerticalFracturedWell* pVFWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pModel);
if(!pVFWell) {
return false;
}
// === 检查裂缝MD范围 ===
// 如果没有有效的裂缝MD范围则无法添加射孔段。
if(fractureTopMd == -1.0 || fractureBottomMd == -1.0 || fractureTopMd >= fractureBottomMd) {
return false;
}
// --- 横坐标距离检查 ---
double dWellboreCenterX = pVFWell->getX().getValue().toDouble();
const double dClickToleranceX = 5.0; // 允许点击位置距离井筒中心线的最大水平距离
if(qAbs(scenePos.x() - dWellboreCenterX) > dClickToleranceX) {
return false;
}
// 1. 获取点击位置对应的测量深度 (MD)
double dClickedMd = scenePos.y();
// 2. 获取井筒的起点和终点MD
double dWellboreTopMd = pVFWell->getBottomholeMD().getValue().toDouble();
double dWellboreBottomMd = dWellboreTopMd + pVFWell->getWellLength().getValue().toDouble();
// 合并约束初始可用空白区域必须同时在井筒和裂缝的MD范围内
double dAvailableBlankAreaStartMd = std::max(dWellboreTopMd, fractureTopMd);
double dAvailableBlankAreaEndMd = std::min(dWellboreBottomMd, fractureBottomMd);
// 如果计算出的初始可用区域无效,说明没有有效空间来添加射孔
if(dAvailableBlankAreaStartMd >= dAvailableBlankAreaEndMd) {
return false;
}
// 3. 检查点击位置是否在最终确定的有效MD范围内
if(dClickedMd < dAvailableBlankAreaStartMd || dClickedMd > dAvailableBlankAreaEndMd) {
return false;
}
// 4. 计算空白区域长度并检查与现有射孔段的重叠
double dNearestPerforationAboveMd = dAvailableBlankAreaStartMd;
double dNearestPerforationBelowMd = dAvailableBlankAreaEndMd;
foreach(nmDataPerforation* pExistingPerforation, pVFWell->getPerforations()) {
double dExistingStartMd = pExistingPerforation->getMdStart().getValue().toDouble();
double dExistingEndMd = pExistingPerforation->getMdEnd().getValue().toDouble();
if(dClickedMd >= dExistingStartMd && dClickedMd <= dExistingEndMd) {
return false; // 点击在现有射孔段内部,不允许添加
}
if(dExistingEndMd < dClickedMd) {
dNearestPerforationAboveMd = std::max(dNearestPerforationAboveMd, dExistingEndMd);
}
if(dExistingStartMd > dClickedMd) {
dNearestPerforationBelowMd = std::min(dNearestPerforationBelowMd, dExistingStartMd);
}
}
// 再次合并所有约束(井筒、裂缝、现有射孔段)以确定最终可用的空白区域
dAvailableBlankAreaStartMd = std::max(dAvailableBlankAreaStartMd, dNearestPerforationAboveMd);
dAvailableBlankAreaEndMd = std::min(dAvailableBlankAreaEndMd, dNearestPerforationBelowMd);
// 再次检查最终计算出的空白区域是否有效
if(dAvailableBlankAreaStartMd >= dAvailableBlankAreaEndMd) {
return false;
}
// 5. 计算新射孔段的长度和位置
double dMaxAvailableLength = dAvailableBlankAreaEndMd - dAvailableBlankAreaStartMd;
double dActualPerforationLength = dMaxAvailableLength / 3.0; // 长度为可用空白区域的三分之一
double dNewPerforationStartMd = dClickedMd - dActualPerforationLength / 2.0;
double dNewPerforationEndMd = dClickedMd + dActualPerforationLength / 2.0;
// 调整确保新射孔段完全在计算出的空白区域内
if(dNewPerforationStartMd < dAvailableBlankAreaStartMd) {
dNewPerforationStartMd = dAvailableBlankAreaStartMd;
dNewPerforationEndMd = dNewPerforationStartMd + dActualPerforationLength;
}
if(dNewPerforationEndMd > dAvailableBlankAreaEndMd) {
dNewPerforationEndMd = dAvailableBlankAreaEndMd;
dNewPerforationStartMd = dNewPerforationEndMd - dActualPerforationLength;
}
// 再次检查最终计算出的射孔段是否仍然有效且不重叠
if(dNewPerforationStartMd >= dNewPerforationEndMd ||
dNewPerforationStartMd < dAvailableBlankAreaStartMd || dNewPerforationEndMd > dAvailableBlankAreaEndMd) {
return false;
}
// 6. 创建并添加新的射孔数据模型 (nmDataPerforation)
nmDataPerforation* dNewPerforationData = new nmDataPerforation();
dNewPerforationData->getName().setValue("Perforation#" + QString::number(pVFWell->getPerforations().size() + 1));
dNewPerforationData->getMdStart().setValue(dNewPerforationStartMd);
dNewPerforationData->getMdEnd().setValue(dNewPerforationEndMd);
dNewPerforationData->getSkin().setValue(0.0);
pVFWell->addPerforation(dNewPerforationData); // 添加到模型
return true;
}
bool nmWellEditorController::tryAddNewPerforationInHorizontalFracturedWell(const QPointF& scenePos, const QLineF& wellboreVisualLine)
{
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pModel);
if(!pHFWell || qFuzzyIsNull(wellboreVisualLine.length())) {
return false;
}
// --- 1. 检查点击位置是否在井筒周围 ---
const double dClickToleranceVisual = 5.0; // 允许点击位置距离井筒视觉线段的最大垂直距离
QPointF wellboreStartScene = wellboreVisualLine.p1();
QPointF wellboreEndScene = wellboreVisualLine.p2();
// 计算点击点到井筒线段的最近点 (投影点)
QVector2D vecAP = QVector2D(scenePos - wellboreStartScene);
QVector2D vecAB = QVector2D(wellboreEndScene - wellboreStartScene);
double lenSq_AB = vecAB.lengthSquared();
double t = 0.0;
if(!qFuzzyIsNull(lenSq_AB)) {
t = QVector2D::dotProduct(vecAP, vecAB) / lenSq_AB;
}
// 限制 t 在 [0, 1] 之间,确保投影点在线段上
t = qBound(0.0, t, 1.0);
// 计算投影点在井筒上的场景坐标
QPointF projectedPoint = wellboreStartScene + vecAB.toPointF() * t;
// 计算点击点到投影点的距离
double distanceToWellbore = QLineF(scenePos, projectedPoint).length();
if(distanceToWellbore > dClickToleranceVisual) {
return false; // 点击位置离井筒太远
}
// --- 2. 获取点击位置对应的测量深度 (MD) ---
// 从模型数据中获取井筒的MD起点和总长度
double dWellboreStartAbsoluteMd = pHFWell->getPerforations()[0]->getMdStart().getValue().toDouble();
double dTotalWellboreMdLength = pHFWell->getWellLength().getValue().toDouble();
if(qFuzzyIsNull(dTotalWellboreMdLength) || dTotalWellboreMdLength < 0) {
return false; // 井筒MD长度无效无法映射MD
}
// 计算投影点对应的MD
double dClickedMd = dWellboreStartAbsoluteMd + (t * dTotalWellboreMdLength);
// 3. 检查点击MD是否在井筒的有效MD范围内
double dWellboreTopMd = dWellboreStartAbsoluteMd;
double dWellboreBottomMd = dWellboreStartAbsoluteMd + dTotalWellboreMdLength;
// 确保点击MD在井筒的有效范围内 (使用DBL_EPSILON进行浮点比较容差)
if(dClickedMd < (dWellboreTopMd - DBL_EPSILON) || dClickedMd > (dWellboreBottomMd + DBL_EPSILON)) {
return false;
}
// 4. 计算空白区域长度并检查重叠
double dNearestPerforationAboveMd = dWellboreTopMd; // 上方最近射孔段的结束MD (初始为井筒顶部MD)
double dNearestPerforationBelowMd = dWellboreBottomMd; // 下方最近射孔段的开始MD (初始为井筒底部MD)
foreach(nmDataPerforation* pExistingPerforation, pHFWell->getPerforations()) {
double dExistingStartMd = pExistingPerforation->getMdStart().getValue().toDouble();
double dExistingEndMd = pExistingPerforation->getMdEnd().getValue().toDouble();
// 如果点击MD在现有射孔段内部则不允许添加点击在射孔段上无效
if(dClickedMd >= (dExistingStartMd - DBL_EPSILON) && dClickedMd <= (dExistingEndMd + DBL_EPSILON)) {
return false;
}
// 更新上方最近的射孔段的结束MD
if(dExistingEndMd < dClickedMd) {
dNearestPerforationAboveMd = std::max(dNearestPerforationAboveMd, dExistingEndMd);
}
// 更新下方最近的射孔段的开始MD
if(dExistingStartMd > dClickedMd) {
dNearestPerforationBelowMd = std::min(dNearestPerforationBelowMd, dExistingStartMd);
}
}
// 确认最终可用的空白区域范围
double dAvailableBlankAreaStartMd = dNearestPerforationAboveMd;
double dAvailableBlankAreaEndMd = dNearestPerforationBelowMd;
// 如果计算出的空白区域无效(起点大于等于终点),说明没有足够空间
if(dAvailableBlankAreaStartMd >= dAvailableBlankAreaEndMd - DBL_EPSILON) {
return false;
}
// 5. 计算新射孔段的长度和位置
double dMaxAvailableLength = dAvailableBlankAreaEndMd - dAvailableBlankAreaStartMd;
double dActualPerforationLength = dMaxAvailableLength / 3.0;
// 确保新射孔段至少有一个最小长度,避免创建零长度或负长度的射孔
// const double dMinPerforationLength = 0.5;
// if(dActualPerforationLength < dMinPerforationLength) {
// return false;
// }
// 如果计算出的长度大于最大可用长度,则限制
dActualPerforationLength = std::min(dActualPerforationLength, dMaxAvailableLength);
// 计算新射孔段的起点和终点MD以点击位置为中心
double dNewPerforationStartMd = dClickedMd - dActualPerforationLength / 2.0;
double dNewPerforationEndMd = dClickedMd + dActualPerforationLength / 2.0;
// 调整确保新射孔段完全在计算出的空白区域内
// 情况1: 如果新射孔段的起点超出了空白区域的起始点(向上越界)
if(dNewPerforationStartMd < dAvailableBlankAreaStartMd - DBL_EPSILON) {
dNewPerforationStartMd = dAvailableBlankAreaStartMd;
dNewPerforationEndMd = dNewPerforationStartMd + dActualPerforationLength;
}
// 情况2: 如果新射孔段的终点超出了空白区域的结束点(向下越界)
if(dNewPerforationEndMd > dAvailableBlankAreaEndMd + DBL_EPSILON) {
dNewPerforationEndMd = dAvailableBlankAreaEndMd;
dNewPerforationStartMd = dNewPerforationEndMd - dActualPerforationLength;
}
// 再次检查最终计算出的射孔段是否仍然有效且不重叠
// 主要是防止由于浮点数计算误差导致边界问题
// 确保长度为正,且完全在空白区域内
if(dNewPerforationStartMd >= dNewPerforationEndMd - DBL_EPSILON ||
dNewPerforationStartMd < dAvailableBlankAreaStartMd - DBL_EPSILON ||
dNewPerforationEndMd > dAvailableBlankAreaEndMd + DBL_EPSILON) {
return false;
}
// 6. 创建并添加新的射孔数据模型
nmDataPerforation* dNewPerforationData = new nmDataPerforation();
dNewPerforationData->getName().setValue("Perforation#" + QString::number(pHFWell->getPerforations().size() + 1));
dNewPerforationData->getMdStart().setValue(dNewPerforationStartMd);
dNewPerforationData->getMdEnd().setValue(dNewPerforationEndMd);
dNewPerforationData->getSkin().setValue(0.0);
// 将新的射孔数据添加到井筒的数据模型中
pHFWell->addPerforation(dNewPerforationData);
return true;
}
void nmWellEditorController::slotDeletePerforation(nmDataPerforation* pPerforationData)
{
// 验证数据有效性
if(!m_pModel || !pPerforationData || m_eCurrentMode != WellEditMode::DeletePerforation) {
return;
}
// <-- 将所有删除的业务逻辑集中在这里
// 井数据类型判断和规则检查
nmDataHorizontalFracturedWell* pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pModel);
// 如果是水平压裂井类型
if(pHFWell) {
// 获取所有射孔段,并检查待删除的射孔段是否为第一条
QVector<nmDataPerforation*>& vecPerforations = pHFWell->getPerforations();
// 检查射孔列表是否非空,并且待删除的射孔是第一条
if(!vecPerforations.isEmpty() && vecPerforations.first() == pPerforationData) {
// 是第一条射孔,不允许删除
// 退出删除模式
setMode(WellEditMode::None);
m_pView->setDeletePerforationMode(false);
return;
}
}
// 如果通过了所有检查,执行实际的删除操作
QVector<nmDataPerforation*>& vecPerforations = m_pModel->getPerforations();
// 查找并删除
for(int i = 0; i < vecPerforations.size(); ++i) {
if(vecPerforations.at(i) == pPerforationData) {
m_pModel->removePerforation(i);
break;
}
}
// 退出删除模式
setMode(WellEditMode::None);
m_pView->setDeletePerforationMode(false);
// 通知发送信号,更新左侧参数视图
m_pModel->notifyParameterChanged();
}
void nmWellEditorController::snapPerforationToWellbore()
{
// 检查井数据有效性
if(!m_pModel) {
return;
}
// 已经是吸附的射孔段了,则直接返回
if(m_pModel->isBasePerforation()) {
// 退出吸附模式
setMode(WellEditMode::None);
m_pView->setSnapPerforationMode(false);
return;
}
// 临时阻塞 m_pModel 的信号
bool bOldSignalsBlocked = m_pModel->blockSignals(true);
// 删除所有的射孔段数据
m_pModel->clearPerforations();
// 创建并添加新的射孔数据模型
nmDataPerforation* dNewPerforationData = new nmDataPerforation();
dNewPerforationData->getMdStart().setValue(m_pModel->getBottomholeMD().getValue().toDouble());
dNewPerforationData->getMdEnd().setValue(m_pModel->getBottomholeMD().getValue().toDouble() + m_pModel->getWellLength().getValue().toDouble());
dNewPerforationData->getSkin().setValue(0.0);
// 恢复原始阻塞状态
m_pModel->blockSignals(bOldSignalsBlocked);
// 将新的射孔数据添加到井筒的数据模型中
m_pModel->addPerforation(dNewPerforationData);
// 退出吸附模式
setMode(WellEditMode::None);
m_pView->setSnapPerforationMode(false);
// 通知发送信号,更新左侧参数视图
m_pModel->notifyParameterChanged();
}
void nmWellEditorController::slotFractureGeometryChanged(double newHalfLength, double newAngle)
{
// 在这里,控制器接收到来自视图的信号
if(!m_pModel) {
return;
}
// 临时阻塞 m_pModel 的信号
bool bOldSignalsBlocked = m_pModel->blockSignals(true);
// 检查井数据类型
nmDataVerticalFracturedWell* pVFWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pModel);
if(pVFWell) {
// 制器负责调用模型的方法来修改数据
pVFWell->getFractureHalfLength().setValue(newHalfLength);
pVFWell->getFractureAngle().setValue(newAngle);
}
// 恢复原始阻塞状态
m_pModel->blockSignals(bOldSignalsBlocked);
// 刷新截面图
m_pView->updataCrossSectionView();
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
m_pModel->notifyParameterChanged();
}
}
// 原有的槽函数可以简化,只负责更新裂缝自身的数据
void nmWellEditorController::slotHandleFractureRectUpdate(qreal newTopMD, qreal newBottomMD)
{
if(!m_pModel || m_pModel->getWellType() != NM_WELL_MODEL::Vertical_Fractured_Well) {
return;
}
// 1. 验证和修正裂缝数据
qreal dFinalTopMD = newTopMD;
qreal dFinalBottomMD = newBottomMD;
// 获取储层位置
qreal dMinReservoirTop = nmDataAnalyzeManager::getCurrentInstance()->getMinLayerTop();
qreal dMaxReservoirBottom = nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
// 应用储层边界限制
if(dFinalTopMD < dMinReservoirTop) {
dFinalTopMD = dMinReservoirTop;
}
if(dFinalBottomMD > dMaxReservoirBottom) {
dFinalBottomMD = dMaxReservoirBottom;
}
// 2. 更新数据模型中的裂缝数据
nmDataVerticalFracturedWell *pVFWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pModel);
if(pVFWell) {
// 临时阻塞 pVFWell 的信号
bool bOldSignalsBlocked = pVFWell->blockSignals(true);
pVFWell->getFractureHeight().setValue(dFinalBottomMD - dFinalTopMD);
qreal newMidPointMD = (dFinalTopMD + dFinalBottomMD) / 2.0;
pVFWell->getFractureMidPointHeight().setValue(dMaxReservoirBottom - newMidPointMD);
// 恢复原始阻塞状态
pVFWell->blockSignals(bOldSignalsBlocked);
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
pVFWell->notifyParameterChanged();
}
}
}
void nmWellEditorController::slotUpdatePerforations(const QMap<nmDataPerforation*, QPair<double, double>>& updatedPerforations)
{
if(!m_pModel || m_pModel->getWellType() != NM_WELL_MODEL::Vertical_Fractured_Well) {
return;
}
nmDataVerticalFracturedWell *pVFWell = dynamic_cast<nmDataVerticalFracturedWell*>(m_pModel);
if(!pVFWell) {
return;
}
// 临时阻塞 pVFWell 的信号,防止多次触发更新
bool bOldSignalsBlocked = pVFWell->blockSignals(true);
// 从你的数据模型中获取射孔列表
QVector<nmDataPerforation*>& perforations = pVFWell->getPerforations();
// 遍历所有射孔,更新或删除
for(int i = perforations.size() - 1; i >= 0; --i) {
nmDataPerforation* pPerf = perforations[i];
if(updatedPerforations.contains(pPerf)) {
// 如果映射中包含此射孔则更新其MD值
QPair<double, double> newMdRange = updatedPerforations.value(pPerf);
pPerf->getMdStart().setValue(newMdRange.first);
pPerf->getMdEnd().setValue(newMdRange.second);
} else {
// 如果映射中不包含此射孔,说明它在视图中被删除了,
// 在数据模型中也应该被删除
pVFWell->removePerforation(i);
}
}
// 恢复信号
pVFWell->blockSignals(bOldSignalsBlocked);
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
pVFWell->notifyParameterChanged();
}
}
void nmWellEditorController::slotUpdateFractureHalfLength(double newHalfLength)
{
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pModel);
if(!pHFWell) {
return;
}
// 临时阻塞 pVFWell 的信号,防止多次触发更新
bool bOldSignalsBlocked = pHFWell->blockSignals(true);
// 更新裂缝半长
pHFWell->getFractureHalfLength().setValue(newHalfLength);
// 恢复信号
pHFWell->blockSignals(bOldSignalsBlocked);
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
pHFWell->notifyParameterChanged();
}
}
void nmWellEditorController::slotUpdateWellborePos(QPointF finalWellboreEndPos)
{
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pModel);
if(!pHFWell) return;
// 临时阻塞 pVFWell 的信号,防止多次触发更新
bool bOldSignalsBlocked = pHFWell->blockSignals(true);
// 1. 获取井筒起点
QPointF wellboreStartScene(pHFWell->getX().getValue().toDouble(), pHFWell->getY().getValue().toDouble());
// 2. 重新计算井身长度和角度
double newWellLength = QLineF(wellboreStartScene, finalWellboreEndPos).length();
double deltaX = finalWellboreEndPos.x() - wellboreStartScene.x();
double deltaY = finalWellboreEndPos.y() - wellboreStartScene.y();
double newDarinAngleRad = atan2(deltaY, deltaX);
double newDarinAngleDeg = newDarinAngleRad * 180.0 / M_PI;
// 3. 更新井筒长度和倾角的数据模型
pHFWell->getWellLength().setValue(newWellLength);
pHFWell->getDrainAngle().setValue(newDarinAngleDeg);
// 4. 更新射孔位置
pHFWell->slotWellLengthChanged();
// 恢复信号
pHFWell->blockSignals(bOldSignalsBlocked);
// 刷新截面图
m_pView->updataCrossSectionView();
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
pHFWell->notifyParameterChanged();
}
}
void nmWellEditorController::slotHFWellFractureGeometryChanged(double newHeight, double newMidPointY)
{
nmDataHorizontalFracturedWell *pHFWell = dynamic_cast<nmDataHorizontalFracturedWell*>(m_pModel);
if(!pHFWell) return;
// 临时阻塞 pVFWell 的信号,防止多次触发更新
bool bOldSignalsBlocked = pHFWell->blockSignals(true);
// 在控制器层执行业务逻辑,更新数据模型
pHFWell->getFractureHeight().setValue(newHeight);
qreal maxReservoirDepth = nmDataAnalyzeManager::getCurrentInstance()->getMaxLayerBottom();
double newMidPointHeightFromBottom = maxReservoirDepth - newMidPointY;
pHFWell->getFractureMidPointHeight().setValue(newMidPointHeightFromBottom);
// 恢复信号
pHFWell->blockSignals(bOldSignalsBlocked);
// 通知发送信号,更新左侧参数视图
if (!bOldSignalsBlocked) {
pHFWell->notifyParameterChanged();
}
}