#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(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(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(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(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(m_pModel); // 如果是水平压裂井类型 if(pHFWell) { // 获取所有射孔段,并检查待删除的射孔段是否为第一条 QVector& vecPerforations = pHFWell->getPerforations(); // 检查射孔列表是否非空,并且待删除的射孔是第一条 if(!vecPerforations.isEmpty() && vecPerforations.first() == pPerforationData) { // 是第一条射孔,不允许删除 // 退出删除模式 setMode(WellEditMode::None); m_pView->setDeletePerforationMode(false); return; } } // 如果通过了所有检查,执行实际的删除操作 QVector& 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(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(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>& updatedPerforations) { if(!m_pModel || m_pModel->getWellType() != NM_WELL_MODEL::Vertical_Fractured_Well) { return; } nmDataVerticalFracturedWell *pVFWell = dynamic_cast(m_pModel); if(!pVFWell) { return; } // 临时阻塞 pVFWell 的信号,防止多次触发更新 bool bOldSignalsBlocked = pVFWell->blockSignals(true); // 从你的数据模型中获取射孔列表 QVector& perforations = pVFWell->getPerforations(); // 遍历所有射孔,更新或删除 for(int i = perforations.size() - 1; i >= 0; --i) { nmDataPerforation* pPerf = perforations[i]; if(updatedPerforations.contains(pPerf)) { // 如果映射中包含此射孔,则更新其MD值 QPair 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(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(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(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(); } }