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.
208 lines
7.7 KiB
C++
208 lines
7.7 KiB
C++
#include "nmPebiResultWellAnnotationLayer.h"
|
|
|
|
#include "nmPebiResultSnapshot.h"
|
|
|
|
#include <QByteArray>
|
|
#include <QCoreApplication>
|
|
#include <QSet>
|
|
#include <QThread>
|
|
|
|
#include <vtkActor2D.h>
|
|
#include <vtkCoordinate.h>
|
|
#include <vtkLeaderActor2D.h>
|
|
#include <vtkMath.h>
|
|
#include <vtkPropAssembly.h>
|
|
#include <vtkProperty2D.h>
|
|
#include <vtkTextMapper.h>
|
|
#include <vtkTextProperty.h>
|
|
|
|
namespace {
|
|
|
|
bool isWellAnnotationGuiThread()
|
|
{
|
|
QCoreApplication* pApplication = QCoreApplication::instance();
|
|
return pApplication == nullptr ||
|
|
pApplication->thread() == QThread::currentThread();
|
|
}
|
|
|
|
// Saphir 风格标注使用固定像素偏移,缩放相机时文字和引线长度保持不变。
|
|
const int WELL_NAME_FONT_SIZE = 16;
|
|
const int WELL_NAME_OFFSET_X = 18;
|
|
const int WELL_NAME_OFFSET_Y = 40;
|
|
const int WELL_LEADER_OFFSET_X = 14;
|
|
const int WELL_LEADER_OFFSET_Y = 36;
|
|
|
|
const double WELL_LEADER_RED = 140.0 / 255.0;
|
|
const double WELL_LEADER_GREEN = 87.0 / 255.0;
|
|
const double WELL_LEADER_BLUE = 59.0 / 255.0;
|
|
|
|
}
|
|
|
|
nmPebiResultWellAnnotationLayer::nmPebiResultWellAnnotationLayer(
|
|
const QString& sLayerId)
|
|
: nmVTKRenderLayer(sLayerId),
|
|
m_pRootAssembly(vtkSmartPointer<vtkPropAssembly>::New()),
|
|
m_pActiveSnapshotAssembly(nullptr),
|
|
m_pCandidateSnapshotAssembly(nullptr)
|
|
{
|
|
Q_ASSERT(isWellAnnotationGuiThread());
|
|
// setRenderProp 只在未挂载时可调用,因此根 Assembly 在构造时一次固定。
|
|
setRenderProp(m_pRootAssembly);
|
|
}
|
|
|
|
nmPebiResultWellAnnotationLayer::~nmPebiResultWellAnnotationLayer()
|
|
{
|
|
Q_ASSERT(isWellAnnotationGuiThread());
|
|
detach();
|
|
clear();
|
|
m_pRootAssembly = nullptr;
|
|
}
|
|
|
|
bool nmPebiResultWellAnnotationLayer::prepareCandidate(
|
|
const QSharedPointer<const nmPebiResultSnapshot>& pSnapshot)
|
|
{
|
|
Q_ASSERT(isWellAnnotationGuiThread());
|
|
discardCandidate();
|
|
if(!isWellAnnotationGuiThread() || pSnapshot.isNull() ||
|
|
!pSnapshot->isComplete()) {
|
|
return false;
|
|
}
|
|
|
|
QSet<QString> setWellIds;
|
|
for(int nIndex = 0; nIndex < pSnapshot->getWellCount(); ++nIndex) {
|
|
const nmPebiResultWellSnapshot* pWell = pSnapshot->getWellAt(nIndex);
|
|
if(pWell == nullptr || pWell->m_sWellInstanceId.isEmpty() ||
|
|
pWell->m_sWellName.isEmpty() ||
|
|
!vtkMath::IsFinite(pWell->m_oLocation.x()) ||
|
|
!vtkMath::IsFinite(pWell->m_oLocation.y()) ||
|
|
setWellIds.contains(pWell->m_sWellInstanceId)) {
|
|
discardCandidate();
|
|
return false;
|
|
}
|
|
setWellIds.insert(pWell->m_sWellInstanceId);
|
|
}
|
|
|
|
vtkSmartPointer<vtkPropAssembly> pSnapshotAssembly =
|
|
vtkSmartPointer<vtkPropAssembly>::New();
|
|
vtkSmartPointer<vtkPropAssembly> pAnnotationGroup =
|
|
vtkSmartPointer<vtkPropAssembly>::New();
|
|
|
|
if(pSnapshot->getWellCount() > 0) {
|
|
for(int nIndex = 0; nIndex < pSnapshot->getWellCount(); ++nIndex) {
|
|
const nmPebiResultWellSnapshot* pWell =
|
|
pSnapshot->getWellAt(nIndex);
|
|
vtkSmartPointer<vtkCoordinate> pWorldAnchor =
|
|
vtkSmartPointer<vtkCoordinate>::New();
|
|
// Z=0 仅表示冻结 XY 所在平面,不表示井深、井斜或井轨迹。
|
|
pWorldAnchor->SetCoordinateSystemToWorld();
|
|
pWorldAnchor->SetValue(
|
|
pWell->m_oLocation.x(),
|
|
pWell->m_oLocation.y(), 0.0);
|
|
|
|
vtkSmartPointer<vtkPropAssembly> pWellAnnotation =
|
|
vtkSmartPointer<vtkPropAssembly>::New();
|
|
vtkSmartPointer<vtkLeaderActor2D> pLeader =
|
|
vtkSmartPointer<vtkLeaderActor2D>::New();
|
|
|
|
// 引线两个端点都引用同一 World 锚点,只叠加固定 Display 像素偏移。
|
|
vtkCoordinate* pStartCoordinate =
|
|
pLeader->GetPositionCoordinate();
|
|
pStartCoordinate->SetCoordinateSystemToDisplay();
|
|
pStartCoordinate->SetReferenceCoordinate(pWorldAnchor);
|
|
pStartCoordinate->SetValue(0.0, 0.0, 0.0);
|
|
vtkCoordinate* pEndCoordinate =
|
|
pLeader->GetPosition2Coordinate();
|
|
pEndCoordinate->SetCoordinateSystemToDisplay();
|
|
pEndCoordinate->SetReferenceCoordinate(pWorldAnchor);
|
|
pEndCoordinate->SetValue(
|
|
WELL_LEADER_OFFSET_X, WELL_LEADER_OFFSET_Y, 0.0);
|
|
|
|
pLeader->AutoLabelOff();
|
|
pLeader->SetLabel("");
|
|
pLeader->SetArrowPlacementToNone();
|
|
pLeader->SetRadius(0.0);
|
|
pLeader->GetProperty()->SetColor(
|
|
WELL_LEADER_RED,
|
|
WELL_LEADER_GREEN,
|
|
WELL_LEADER_BLUE);
|
|
pLeader->GetProperty()->SetLineWidth(1.0);
|
|
// 二维引线始终绘制在三维网格前方,避免被致密单元边线遮挡。
|
|
pLeader->GetProperty()->SetDisplayLocationToForeground();
|
|
pWellAnnotation->AddPart(pLeader);
|
|
|
|
vtkSmartPointer<vtkTextMapper> pNameMapper =
|
|
vtkSmartPointer<vtkTextMapper>::New();
|
|
const QByteArray aWellName = pWell->m_sWellName.toUtf8();
|
|
pNameMapper->SetInput(aWellName.constData());
|
|
vtkTextProperty* pTextProperty =
|
|
pNameMapper->GetTextProperty();
|
|
pTextProperty->SetFontFamilyToArial();
|
|
pTextProperty->SetFontSize(WELL_NAME_FONT_SIZE);
|
|
pTextProperty->SetColor(1.0, 1.0, 0.0);
|
|
pTextProperty->BoldOff();
|
|
pTextProperty->ItalicOff();
|
|
pTextProperty->ShadowOn();
|
|
pTextProperty->SetShadowOffset(1, -1);
|
|
pTextProperty->SetJustificationToLeft();
|
|
pTextProperty->SetVerticalJustificationToBottom();
|
|
|
|
vtkSmartPointer<vtkActor2D> pNameActor =
|
|
vtkSmartPointer<vtkActor2D>::New();
|
|
pNameActor->SetMapper(pNameMapper);
|
|
// 井名与引线使用相同的前景层级,保证顶视图下仍清晰可读。
|
|
pNameActor->GetProperty()->SetDisplayLocationToForeground();
|
|
vtkCoordinate* pNameCoordinate =
|
|
pNameActor->GetPositionCoordinate();
|
|
pNameCoordinate->SetCoordinateSystemToDisplay();
|
|
pNameCoordinate->SetReferenceCoordinate(pWorldAnchor);
|
|
pNameCoordinate->SetValue(
|
|
WELL_NAME_OFFSET_X, WELL_NAME_OFFSET_Y, 0.0);
|
|
pWellAnnotation->AddPart(pNameActor);
|
|
|
|
// 每口井的文字与引线形成一个不可拆分的候选注记单元。
|
|
pAnnotationGroup->AddPart(pWellAnnotation);
|
|
}
|
|
}
|
|
|
|
pSnapshotAssembly->AddPart(pAnnotationGroup);
|
|
|
|
// 所有校验和 VTK 对象构造成功后才记录候选;活动子树仍未触碰。
|
|
m_pCandidateSnapshotAssembly = pSnapshotAssembly;
|
|
return true;
|
|
}
|
|
|
|
void nmPebiResultWellAnnotationLayer::discardCandidate()
|
|
{
|
|
Q_ASSERT(isWellAnnotationGuiThread());
|
|
m_pCandidateSnapshotAssembly = nullptr;
|
|
}
|
|
|
|
void nmPebiResultWellAnnotationLayer::commitCandidate()
|
|
{
|
|
Q_ASSERT(isWellAnnotationGuiThread());
|
|
Q_ASSERT(m_pCandidateSnapshotAssembly != nullptr);
|
|
|
|
if(m_pActiveSnapshotAssembly != nullptr) {
|
|
m_pRootAssembly->RemovePart(m_pActiveSnapshotAssembly);
|
|
}
|
|
m_pActiveSnapshotAssembly = m_pCandidateSnapshotAssembly;
|
|
m_pRootAssembly->AddPart(m_pActiveSnapshotAssembly);
|
|
discardCandidate();
|
|
}
|
|
|
|
void nmPebiResultWellAnnotationLayer::clear()
|
|
{
|
|
Q_ASSERT(isWellAnnotationGuiThread());
|
|
discardCandidate();
|
|
if(m_pRootAssembly != nullptr &&
|
|
m_pActiveSnapshotAssembly != nullptr) {
|
|
m_pRootAssembly->RemovePart(m_pActiveSnapshotAssembly);
|
|
}
|
|
m_pActiveSnapshotAssembly = nullptr;
|
|
}
|
|
|
|
bool nmPebiResultWellAnnotationLayer::hasActiveAnnotations() const
|
|
{
|
|
return m_pActiveSnapshotAssembly != nullptr;
|
|
}
|