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/nmPebiResultWellAnnotationL...

236 lines
8.6 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_pActiveAnnotationGroup(nullptr),
m_pCandidateSnapshotAssembly(nullptr),
m_pCandidateAnnotationGroup(nullptr),
m_bWellNamesVisible(true)
{
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);
}
}
pAnnotationGroup->SetVisibility(m_bWellNamesVisible ? 1 : 0);
pSnapshotAssembly->AddPart(pAnnotationGroup);
// 所有校验和 VTK 对象构造成功后才记录候选;活动子树仍未触碰。
m_pCandidateSnapshotAssembly = pSnapshotAssembly;
m_pCandidateAnnotationGroup = pAnnotationGroup;
return true;
}
void nmPebiResultWellAnnotationLayer::discardCandidate()
{
Q_ASSERT(isWellAnnotationGuiThread());
m_pCandidateAnnotationGroup = nullptr;
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_pActiveAnnotationGroup = m_pCandidateAnnotationGroup;
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_pActiveAnnotationGroup = nullptr;
m_pActiveSnapshotAssembly = nullptr;
}
bool nmPebiResultWellAnnotationLayer::hasActiveAnnotations() const
{
return m_pActiveSnapshotAssembly != nullptr;
}
void nmPebiResultWellAnnotationLayer::setWellNamesVisible(bool bVisible)
{
Q_ASSERT(isWellAnnotationGuiThread());
if(!isWellAnnotationGuiThread()) {
return;
}
m_bWellNamesVisible = bVisible;
if(m_pActiveAnnotationGroup != nullptr) {
m_pActiveAnnotationGroup->SetVisibility(bVisible ? 1 : 0);
}
if(m_pCandidateAnnotationGroup != nullptr) {
m_pCandidateAnnotationGroup->SetVisibility(bVisible ? 1 : 0);
}
}
bool nmPebiResultWellAnnotationLayer::areWellNamesVisible() const
{
return m_bWellNamesVisible;
}