From 0a7faaad3d8ae5651f63c00d78f413a4b8dad9cd Mon Sep 17 00:00:00 2001 From: Krad Date: Tue, 9 Aug 2022 09:24:38 +0800 Subject: [PATCH] Add ReferenceLine.(need fix resize logic) --- .../Legend/ReferenceLineLegendActor.cpp | 86 +++++++------- src/src/Rendering/infinitiViewer.cxx | 52 ++++++++- src/src/Rendering/infinitiViewer.h | 12 +- src/src/UI/Manager/ImageViewManager.cpp | 109 ++++++++++++++++-- src/src/UI/Manager/ImageViewManager.h | 12 +- src/src/UI/Widget/ImageView/dicomimageview.h | 17 ++- src/src/UI/Window/QDicomViewer.cpp | 4 +- 7 files changed, 224 insertions(+), 68 deletions(-) diff --git a/src/src/Rendering/Legend/ReferenceLineLegendActor.cpp b/src/src/Rendering/Legend/ReferenceLineLegendActor.cpp index 1c06583..d28668b 100644 --- a/src/src/Rendering/Legend/ReferenceLineLegendActor.cpp +++ b/src/src/Rendering/Legend/ReferenceLineLegendActor.cpp @@ -27,9 +27,11 @@ ReferenceLineLegendActor::ReferenceLineLegendActor() { shadow2D->SetMapper(mapper2); actor2D->GetProperty()->SetLineWidth(1.0); //马尔斯绿 - actor2D->GetProperty()->SetColor(0.004, 0.515, 0.5); - shadow2D->GetProperty()->SetColor(0.0, 0.0, 0.0); + actor2D->GetProperty()->SetColor(0.98, 0.82, 0.414); + shadow2D->GetProperty()->SetColor(0.68, 0.52, 0.114); +// shadow2D->GetProperty()->SetColor(0.0, 0.0, 0.0); shadow2D->GetProperty()->SetLineWidth(3.0); + shadow2D->GetProperty()->SetOpacity(0.75); linePoints->SetNumberOfPoints(4); } @@ -51,47 +53,51 @@ int ReferenceLineLegendActor::RenderOverlay(vtkViewport *viewport) { auto renderer = vtkRenderer::SafeDownCast(viewport); if (!renderer) return 0; BuildShape(renderer); - shadow2D->RenderOverlay(viewport); - return actor2D->RenderOverlay(viewport); + if (Visibility){ + shadow2D->RenderOverlay(viewport); + return actor2D->RenderOverlay(viewport); + } + return 0; } void ReferenceLineLegendActor::BuildShape(vtkRenderer *renderer) { - if(mModifyTime == MTime)return; - vtkNew pts; - for(vtkIdType i = 0; iGetNumberOfPoints(); ++i) - { - double p[3] = {.0, .0, .0}; - renderer->SetWorldPoint(linePoints->GetPoint(i)); - renderer->WorldToDisplay(); - renderer->GetDisplayPoint(p); - pts->InsertNextPoint(p); + if (mModifyTime == MTime)return; + if (Visibility) { + + vtkNew pts; + for (vtkIdType i = 0; i < linePoints->GetNumberOfPoints(); ++i) { + double p[3] = {.0, .0, .0}; + renderer->SetWorldPoint(linePoints->GetPoint(i)); + renderer->WorldToDisplay(); + renderer->GetDisplayPoint(p); + pts->InsertNextPoint(p); + } + double p1[3] = {.0, .0, .0}; + double p2[3] = {.0, .0, .0}; + pts->GetPoint(0, p1); + pts->GetPoint(2, p1); + double dis2 = vtkMath::Distance2BetweenPoints(p1, p2); + pts->GetPoint(1, p1); + pts->GetPoint(3, p1); + double dis2_2 = vtkMath::Distance2BetweenPoints(p1, p2); + vtkNew displayPts; + if (dis2 > dis2_2) { + pts->GetPoint(0, p1); + pts->GetPoint(2, p2); + displayPts->InsertNextPoint(round(p1[0]) + 0.5, round(p1[1]) + 0.5, round(p1[2]) + 0.5); + displayPts->InsertNextPoint(round(p2[0]) + 0.5, round(p2[1]) + 0.5, round(p2[2]) + 0.5); + } else { + pts->GetPoint(1, p1); + pts->GetPoint(3, p2); + displayPts->InsertNextPoint(round(p1[0]) ,round(p1[1]) , round(p1[2]) ); + displayPts->InsertNextPoint(round(p2[0]) , round(p2[1]) , round(p2[2]) ); + } + vtkNew source; + source->SetPoints(displayPts); + source->Update(); + renderData->DeepCopy(source->GetOutput()); + shadow2D->GetMapper()->SetInputDataObject(renderData); + actor2D->GetMapper()->SetInputDataObject(renderData); } - double p1[3] = {.0, .0, .0}; - double p2[3] = {.0, .0, .0}; - pts->GetPoint(0, p1); - pts->GetPoint(1, p1); - double dis2 = vtkMath::Distance2BetweenPoints(p1,p2); - pts->GetPoint(2, p1); - pts->GetPoint(3, p1); - double dis2_2 = vtkMath::Distance2BetweenPoints(p1,p2); - vtkNew displayPts; - if (dis2>dis2_2){ - pts->GetPoint(0,p1); - pts->GetPoint(1, p2); - displayPts->InsertNextPoint(round(p1[0])+0.5,round(p1[1])+0.5, round(p1[2])+0.5 ); - displayPts->InsertNextPoint( round(p2[0])+0.5,round(p2[1])+0.5, round(p2[2])+0.5); - } - else{ - pts->GetPoint(2,p1); - pts->GetPoint(3, p2); - displayPts->InsertNextPoint( round(p1[0])+0.5,round(p1[1])+0.5, round(p1[2])+0.5); - displayPts->InsertNextPoint( round(p2[0])+0.5,round(p2[1])+0.5, round(p2[2])+0.5); - } - vtkNew source; - source->SetPoints(displayPts); - source->Update(); - renderData->DeepCopy(source->GetOutput()); - shadow2D->GetMapper()->SetInputDataObject(renderData); - actor2D->GetMapper()->SetInputDataObject(renderData); mModifyTime = MTime; } diff --git a/src/src/Rendering/infinitiViewer.cxx b/src/src/Rendering/infinitiViewer.cxx index 4b97b73..e4398a1 100644 --- a/src/src/Rendering/infinitiViewer.cxx +++ b/src/src/Rendering/infinitiViewer.cxx @@ -40,7 +40,7 @@ infinitiViewer::infinitiViewer() SliceOrientation(infinitiViewer::SLICE_ORIENTATION_XY), FirstRender(1), Slice(0), loadedMeasureSlice(0), currentPresetIndex(1), Fusion(false), firstFusion(true), FusionOpacity(0.5), list(nullptr), measureStore(MeasureStore::Instance()), - rulerActive(false) { + rulerActive(false){ this->ImageMapper->SliceAtFocalPointOn(); this->ImageMapper->SliceFacesCameraOn(); this->ImageActor->SetPickable(true); @@ -185,7 +185,8 @@ void infinitiViewer::SetRenderWindow(vtkRenderWindow *arg) { if (this->RenderWindow) { this->RenderWindow->Register(this); } - this->RenderWindow->AddObserver(vtkCommand::EventIds::RenderEvent, this, &infinitiViewer::RenderRuler); + //add legend to render + this->RenderWindow->AddObserver(vtkCommand::EventIds::RenderEvent, this, &infinitiViewer::RenderLegend); this->InstallPipeline(); } @@ -220,6 +221,7 @@ void infinitiViewer::SetInputData(vtkImageData *in) { this->ImageActor->GetMapper()->SetInputData(in); this->RemoveFusionData(); this->ActiveRuler(); + this-> ActiveReferenceLine(); double zVec = in->GetSpacing()[2]; defaultProjection[2][2] = zVec>0.0?1.0:-1.0; } @@ -503,13 +505,14 @@ void infinitiViewer::applySliceOffset(double offset, double direction){ UpdateTopLeftCornerInfo(); } -vtkSmartPointer infinitiViewer::GetSliceBoundPoints() { +vtkPoints* infinitiViewer::GetSliceBoundPoints() { double bounds[6] = {.0, .0, .0, .0, .0, .0}; ImageMapper->GetBounds(bounds); vtkCamera *camera = this->Renderer->GetActiveCamera(); double fpt[3] = {.0, .0, .0}; camera->GetFocalPoint(fpt); bounds[SliceOrientation * 2] = fpt[SliceOrientation]; + bounds[SliceOrientation * 2+1] = fpt[SliceOrientation]; vtkSmartPointer pts = vtkSmartPointer::New(); //只取4个点 pts->InsertNextPoint(bounds[0], bounds[2], bounds[4]); @@ -530,7 +533,34 @@ vtkSmartPointer infinitiViewer::GetSliceBoundPoints() { pts->InsertNextPoint(bounds[0], bounds[2], bounds[5]); } } - return pts; + vtkPoints* worldPts = vtkPoints::New(); + //model to world + for (vtkIdType i = 0; i < pts->GetNumberOfPoints(); ++i) { + double temp[4] = {.0, .0, .0, 1.}; + pts->GetPoint(i, temp); + matrix->MultiplyPoint(temp, temp); + temp[0] = temp[0] + imageDataOrigin[0]; + temp[1] = temp[1] + imageDataOrigin[1]; + temp[2] = temp[2] + imageDataOrigin[2]; + worldPts->InsertNextPoint(temp); + } + return worldPts; +} + +void infinitiViewer::updateReferenceLine(vtkPoints* worldPts){ + //model to world + matrix->Invert(); + for (vtkIdType i = 0; i < worldPts->GetNumberOfPoints(); ++i) { + double temp[4] = {.0, .0, .0, 1.}; + worldPts->GetPoint(i, temp); + temp[0] = temp[0] - imageDataOrigin[0]; + temp[1] = temp[1] - imageDataOrigin[1]; + temp[2] = temp[2] - imageDataOrigin[2]; + matrix->MultiplyPoint(temp, temp); + referenceLine->setPoint(i,temp); + } + matrix->Invert(); + Render(); } void infinitiViewer::SyncSlicePoint(double *point) { @@ -1216,6 +1246,7 @@ void infinitiViewer::SetSliceOrientation(int orientation) { SetSlice(0); } +//TODO:暂时只有正交数据!!!否则强制转型会有一些问题 int infinitiViewer::GetWorldSliceOrientation() { double orientations[4] = {.0, .0, .0, 1.0}; orientations[SliceOrientation] = 1.0; @@ -1310,10 +1341,19 @@ void infinitiViewer::UnActiveRuler() { rulerActive = false; } -void infinitiViewer::RenderRuler() { +void infinitiViewer::ActiveReferenceLine() { + referenceLine->SetVisibility(true); +} + +void infinitiViewer::UnActiveReferenceLine() { + referenceLine->SetVisibility(false); +} + +void infinitiViewer::RenderLegend() { if (AnnoHelper::IsAnno()) { if (Renderer) { - ruler->RenderOverlay(Renderer); + if (rulerActive)ruler->RenderOverlay(Renderer); + referenceLine->RenderOverlay(Renderer); } } } diff --git a/src/src/Rendering/infinitiViewer.h b/src/src/Rendering/infinitiViewer.h index f321bb8..8cdd79a 100644 --- a/src/src/Rendering/infinitiViewer.h +++ b/src/src/Rendering/infinitiViewer.h @@ -11,6 +11,7 @@ //for convert vtkEvent to Qt signal #include "Events/vtkSignalRaiser.h" #include "Rendering/Legend/RulerLegendActor.h" +#include "Rendering/Legend/ReferenceLineLegendActor.h" #include "Common/QGlobals.h" class vtkAlgorithm; @@ -159,7 +160,9 @@ vtkTypeMacro(infinitiViewer, vtkObject); void ChangeSlice(vtkObject *, unsigned long eventid, void *calldata); - vtkSmartPointer GetSliceBoundPoints(); + vtkPoints* GetSliceBoundPoints(); + + void updateReferenceLine(vtkPoints* pts); //@{ /** @@ -369,6 +372,10 @@ vtkTypeMacro(infinitiViewer, vtkObject); void UnActiveRuler(); + void ActiveReferenceLine(); + + void UnActiveReferenceLine(); + //for convert vtkEvent to Qt signal vtkSignalRaiser *GetSignalRaiser() { return &raiser; @@ -429,7 +436,7 @@ private: void UpdateTopLeftCornerInfo(); - void RenderRuler(); + void RenderLegend(); vtkRenderWindow *RenderWindow; vtkRenderer *Renderer; @@ -445,6 +452,7 @@ private: vtkScalarBarActor *bar; vtkNew ruler; + vtkNew referenceLine; vtkNew matrix; DicomCornerInfo m_cornerInfo; diff --git a/src/src/UI/Manager/ImageViewManager.cpp b/src/src/UI/Manager/ImageViewManager.cpp index 8ea3bec..56fd7c9 100644 --- a/src/src/UI/Manager/ImageViewManager.cpp +++ b/src/src/UI/Manager/ImageViewManager.cpp @@ -105,31 +105,51 @@ void ImageViewManager::setCurrentView(DicomImageView *view) { if (currentView != view) { if (currentView) currentView->setHighlight(false); currentView = view; + currentView->setHighlight(true); - emit currentViewReload(view); + reloadCurrentView(view); // emit currentViewChanged(currentView->getSeriesInstance()->getUniqueID()); } } void ImageViewManager::smartDo(SmartDoCallback cb, DicomImageView *sourceView, void *callData, DoScope scope) { + smartDo(cb, nullptr, sourceView, callData, scope); +} + +void ImageViewManager::smartDo(SmartDoCallback cb,SmartDoCallback otherCb, DicomImageView *sourceView, void *callData, DoScope scope) { switch (scope) { case DoScope::Current: { if (currentView) cb(currentView, callData); + else{ + if (otherCb){ + std::for_each(vList.begin(), vList.end(), [=](auto v) { + //check series + if (v == sourceView) return; + if (!v->hasSeries()) return; + otherCb(v, callData); + }); + } + } break; } case DoScope::SameSeries: { std::for_each(vList.begin(), vList.end(), [=](auto v) { + if (v == sourceView) return; + if (!v->hasSeries()) return; //check series auto series = sourceView->getSeriesInstance(); if (v->getSeriesInstance() == series && v->getSliceOrientation() == sourceView->getSliceOrientation()) { cb(v, callData); } + else{ + if (otherCb) otherCb(v, callData); + } }); break; } - case DoScope::SameStudySeries: { + case DoScope::SameStudySameOrientation: { std::for_each(vList.begin(), vList.end(), [=](auto v) { if (v == sourceView) return; if (!v->hasSeries()) return; @@ -141,6 +161,7 @@ void ImageViewManager::smartDo(SmartDoCallback cb, DicomImageView *sourceView, v //equal slice orientation && v->getSliceOrientation() == sourceView->getSliceOrientation()) { cb(v, callData); + return; } //equal study else if (strcmp(currentSeries->getStudyUID(), series->getStudyUID()) == 0 @@ -149,7 +170,9 @@ void ImageViewManager::smartDo(SmartDoCallback cb, DicomImageView *sourceView, v //Intersect bounds && currentSeries->IntersectWorldBounds(series)) { cb(v, callData); + return; } + if (otherCb) otherCb(v, callData); }); break; } @@ -161,6 +184,35 @@ void ImageViewManager::smartDo(SmartDoCallback cb, DicomImageView *sourceView, v if (v->CompareWorldSliceOrientation(sourceView)) { cb(v, callData); } + else if (otherCb) otherCb(v, callData); + }); + break; + } + case DoScope::SameStudyOrthogonalSeries: { + std::for_each(vList.begin(), vList.end(), [=](auto v) { + if (v == sourceView) return; + if (!v->hasSeries()) return; + //check series + auto series = sourceView->getSeriesInstance(); + if (!series) return; + auto currentSeries = v->getSeriesInstance(); + //same series + if (series == currentSeries + //equal slice orientation + && v->getSliceOrientation() != sourceView->getSliceOrientation()) { + cb(v, callData); + return; + } + //equal study + else if (strcmp(currentSeries->getStudyUID(), series->getStudyUID()) == 0 + //no equal world slice orientation + && !v->CompareWorldSliceOrientation(sourceView) + //Intersect bounds + && currentSeries->IntersectWorldBounds(series)) { + cb(v, callData); + return; + } + if (otherCb) otherCb(v, callData); }); break; } @@ -179,6 +231,7 @@ void ImageViewManager::smartDo(SmartDoCallback cb, DicomImageView *sourceView, v } } + //Slots and actions------------------------------------------------------------ void ImageViewManager::viewClicked(DicomImageView *view) { if (!view) return; @@ -203,7 +256,7 @@ void ImageViewManager::viewDoubleClicked(DicomImageView *view) { void ImageViewManager::viewSliced(DicomImageView *src, void *sliceData) { //Sync slice - if (SyncHelper::getSyncItem(SLICE_POS)) { + if (sliceData && SyncHelper::getSyncItem(SLICE_POS)) { if (SyncHelper::getSyncState() == AUTO_SYNC) { this->smartDo([](auto v, auto callData) { if (v->hasSeries()) { @@ -211,7 +264,7 @@ void ImageViewManager::viewSliced(DicomImageView *src, void *sliceData) { v->syncSlicePoint(r); v->SyncScrollBar(); } - }, src, sliceData, ImageViewManager::SameStudySeries); + }, src, sliceData, ImageViewManager::SameStudySameOrientation); } else if (SyncHelper::getSyncState() == MANUAL_SYNC) { this->smartDo([](auto v, auto callData) { @@ -224,7 +277,28 @@ void ImageViewManager::viewSliced(DicomImageView *src, void *sliceData) { }, src, sliceData, ImageViewManager::SameOrientationSeries); } } - //TODO: reference line + //reference line + if (!currentView->hasSeries()) { + smartDo([](auto v, auto callData) { + v->updateReferenceLine(nullptr); + },nullptr, nullptr, ImageViewManager::All); + return ; + } + renderReferenceLine(); +} + +void ImageViewManager::renderReferenceLine() { + vtkPoints* pts = currentView->hasSeries() ? currentView->getSliceBoundPoints() : nullptr; + smartDo([](auto v, auto callData) { + if (v->hasSeries()) { + //disable global trigger slot + vtkPoints *p = (vtkPoints *) callData; + v->updateReferenceLine(p); + } + }, [](auto v, auto callData){ + v->updateReferenceLine(nullptr); + },currentView, pts, SameStudyOrthogonalSeries); + currentView->updateReferenceLine(nullptr); } void ImageViewManager::viewPaned(DicomImageView *src, void* offsetVector) { @@ -244,7 +318,7 @@ void ImageViewManager::viewPaned(DicomImageView *src, void* offsetVector) { double vector[3] = {d[3], d[4] , d[5]}; v->shiftCamera(vector); } - }, src, offsetVector, ImageViewManager::SameStudySeries); + }, src, offsetVector, ImageViewManager::SameStudySameOrientation); } } } @@ -265,7 +339,7 @@ void ImageViewManager::viewZoomed(DicomImageView *src, double* scaleFactor) { double d = ((double *) callData)[1]; v->setZoomScale(d); } - }, src, scaleFactor, ImageViewManager::SameStudySeries); + }, src, scaleFactor, ImageViewManager::SameStudySameOrientation); } } } @@ -278,7 +352,7 @@ void ImageViewManager::viewEndWindowLevel(DicomImageView *src, double level, dou double *d = (double *) callData; v->setWindowLevel(d[0], d[1]); } - }, src, data, ImageViewManager::SameStudySeries); + }, src, data, ImageViewManager::SameStudySameOrientation); } } @@ -288,11 +362,17 @@ void ImageViewManager::viewReload(const std::string &unique_info) { DicomLoader *helper = DicomLoader::GetInstance(); currentView->loadSeries(helper->getSeriesImageSet(unique_info)); currentView->render(); - emit currentViewReload(currentView); + reloadCurrentView(currentView); } void ImageViewManager::viewCleared(DicomImageView* view) { - emit currentViewReload(view); + smartDo([](auto v, auto callData) { + auto src = (DicomImageView*)callData; + if (v->isFusion() && v->getFusionInput() == src){ + v->unloadFusion(); + } + }, view, view, AllExceptSelf); + reloadCurrentView(view); } void ImageViewManager::setInteractionMode(int InteractionMode) { @@ -347,7 +427,7 @@ void ImageViewManager::deleteMeasure(EventObject* deleteType){ void ImageViewManager::switchSliceOrientation() { currentView->setSliceOrientation((currentView->getSliceOrientation() + 1) % 3); - emit currentViewReload(currentView); + reloadCurrentView(currentView); } //Corner about----------------------------------------------------------------- @@ -411,7 +491,7 @@ int ImageViewManager::checkSyncAbility(DicomImageView* view) { if (v->hasSeries()) { d[0] = true; } - }, view, &flag, ImageViewManager::SameStudySeries); + }, view, &flag, ImageViewManager::SameStudySameOrientation); if (flag) return AUTO_SYNC; this->smartDo([](auto v, auto callData) { bool *d = (bool *) callData; @@ -429,3 +509,8 @@ void ImageViewManager::clearCurrentView() { currentView->viewCleared(); } +void ImageViewManager::reloadCurrentView(DicomImageView * view){ + renderReferenceLine(); + emit currentViewReloaded(view); +} + diff --git a/src/src/UI/Manager/ImageViewManager.h b/src/src/UI/Manager/ImageViewManager.h index 5160c47..6dd3e55 100644 --- a/src/src/UI/Manager/ImageViewManager.h +++ b/src/src/UI/Manager/ImageViewManager.h @@ -104,8 +104,9 @@ public: Current, SameSeries, //Equal Study, Equal World Bounds series - SameStudySeries, + SameStudySameOrientation, SameOrientationSeries, + SameStudyOrthogonalSeries, AllExceptSelf, All }; @@ -114,11 +115,11 @@ public: void smartDo(SmartDoCallback cb, DicomImageView *sourceView = nullptr, void *callData = nullptr, DoScope scope = Current); - + void smartDo(SmartDoCallback cb, SmartDoCallback otherCb, DicomImageView *sourceView = nullptr, void *callData = nullptr, + DoScope scope = Current); + void reloadCurrentView(DicomImageView * view); signals: -// void currentViewChanged(const std::string& seriesID); - void currentViewReload( DicomImageView * view); - void fusionCheckStateChanged(bool able); + void currentViewReloaded( DicomImageView * view); private: QList vList; @@ -127,6 +128,7 @@ private: bool mMaxed; + void renderReferenceLine(); }; diff --git a/src/src/UI/Widget/ImageView/dicomimageview.h b/src/src/UI/Widget/ImageView/dicomimageview.h index 8298487..01eebf2 100644 --- a/src/src/UI/Widget/ImageView/dicomimageview.h +++ b/src/src/UI/Widget/ImageView/dicomimageview.h @@ -1,4 +1,4 @@ -#ifndef OMEGAV_DICOM_IMAGE_VIEW_H +#ifndef OMEGAV_DICOM_IMAGE_VIEW_H #define OMEGAV_DICOM_IMAGE_VIEW_H #include @@ -172,6 +172,21 @@ public: mImageViewer->applySliceOffset(offset, direction); } + vtkPoints* getSliceBoundPoints(){ + return mImageViewer->GetSliceBoundPoints(); + } + + void updateReferenceLine(vtkPoints* pts){ + if (!mImageViewer) return; + if (!pts){ + mImageViewer->UnActiveReferenceLine(); + mImageViewer->Render(); + return; + } + mImageViewer->ActiveReferenceLine(); + mImageViewer->updateReferenceLine(pts); + } + bool checkMPRAble(){ return DicomImageView::checkMPRAble(this); } diff --git a/src/src/UI/Window/QDicomViewer.cpp b/src/src/UI/Window/QDicomViewer.cpp index e5f7929..9b75270 100644 --- a/src/src/UI/Window/QDicomViewer.cpp +++ b/src/src/UI/Window/QDicomViewer.cpp @@ -65,12 +65,12 @@ void QDicomViewer::SetupConnections() { initCine(); initScreenControl(); - connect(ui->viewContainer->getViewManager(),&ImageViewManager::currentViewReload, + connect(ui->viewContainer->getViewManager(), &ImageViewManager::currentViewReloaded, ui->toolBar, &DefaultToolBar::resetNeedCheckFunctionButtons); worker.setManager(ui->viewContainer->getViewManager()); connect(ui->viewContainer->getViewManager(), - &ImageViewManager::currentViewReload, + &ImageViewManager::currentViewReloaded, &worker,&ImageViewStateCheckWorker::checkImageViewState, Qt::QueuedConnection); connect(&worker,&ImageViewStateCheckWorker::imageViewStateChanged, ui->toolBar,&DefaultToolBar::updateNeedCheckFunctionButtons, Qt::QueuedConnection);