diff --git a/src/Icon/diameter.png b/src/Icon/diameter.png new file mode 100644 index 0000000..2fc1f35 Binary files /dev/null and b/src/Icon/diameter.png differ diff --git a/src/QDicomViewer.qrc b/src/QDicomViewer.qrc index b8a387b..f8bbca3 100644 --- a/src/QDicomViewer.qrc +++ b/src/QDicomViewer.qrc @@ -57,6 +57,7 @@ Icon/Reset.png Icon/Settings.png Icon/preset.png + Icon/diameter.png Icon/pq/pqBold24.png diff --git a/src/src/Common/QGlobals.h b/src/src/Common/QGlobals.h index fffa3b0..80e3efc 100644 --- a/src/src/Common/QGlobals.h +++ b/src/src/Common/QGlobals.h @@ -81,6 +81,7 @@ enum AnnotationActorType OpenPolygonAnn, ArrowAnn, EllipseAnn, + DiameterAnn, TextAnn, }; enum AnnotationDeleteType{ diff --git a/src/src/Rendering/Core/ControlPointActor.cpp b/src/src/Rendering/Core/ControlPointActor.cpp index d4784f6..b9419b8 100644 --- a/src/src/Rendering/Core/ControlPointActor.cpp +++ b/src/src/Rendering/Core/ControlPointActor.cpp @@ -70,6 +70,11 @@ void ControlPointActor::SetWorldPosition(double x, double y, double z) { BaseDataPoints->Reset(); BaseDataPoints->SetNumberOfPoints(1); BaseDataPoints->SetPoint(0, x, y, z); +} + +double *ControlPointActor::GetRenderPosition() +{ + return renderPoints->GetPoint(0); }; double *ControlPointActor::GetWorldPosition() { diff --git a/src/src/Rendering/Core/ControlPointActor.h b/src/src/Rendering/Core/ControlPointActor.h index 6ab9c2f..a5dd561 100644 --- a/src/src/Rendering/Core/ControlPointActor.h +++ b/src/src/Rendering/Core/ControlPointActor.h @@ -24,6 +24,8 @@ public: void SetWorldPosition(double x, double y, double z); + double *GetRenderPosition(); + void SetWorldPosition(double *pos) { SetWorldPosition(pos[0], pos[1], pos[2]); } diff --git a/src/src/Rendering/Measure/EllipseAnnotationActor.cpp b/src/src/Rendering/Measure/EllipseAnnotationActor.cpp index 5a8135b..09449c5 100644 --- a/src/src/Rendering/Measure/EllipseAnnotationActor.cpp +++ b/src/src/Rendering/Measure/EllipseAnnotationActor.cpp @@ -13,7 +13,21 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include + #include +#include + + +#include "omp.h" + +#include "Interaction/ActorDraggableInteractorStyle.h" #include "Rendering/Core/ControlPointActor.h" #include "Common/QGlobals.h" @@ -26,30 +40,30 @@ namespace { void EllipseAnnotationActor::BuildShape() { if (!BaseDataPoints->GetNumberOfPoints()) return; + QString stamp = QString("%1-%2").arg(BaseDataPoints->GetMTime()) + .arg(Renderer->GetActiveCamera()->GetMTime()); + if (mRenderTime == stamp) return; + QStringList stamps = stamp.split("-"); + QStringList renderTimes = mRenderTime.split("-"); + mRenderTime = stamp; RebuildRenderPoint(); - - double *p_lt = controlP_lt->GetWorldPosition(); - double *p_rb = controlP_rb->GetWorldPosition(); - if (!transforming) - { - double area = abs((p_lt[0] - p_rb[0]) * (p_lt[1] - p_rb[1])) * vtkMath::Pi() * 0.25; - bool unitFlag2 = area>1000; - QString num = QString::number(area, 'f', 2); - QString textValue = QString("%1: %2 %3") - .arg(mAreaName) - .arg(unitFlag2?area/100:area,0,'f',2).arg(unitFlag2?mUnitcm2:mUnitmm2); - vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(textValue.toUtf8().constData()); - + //base data change change message + if (stamps[0] != renderTimes[0]) + { double p_lt[3] = {0, 0, 0}; + double* disp_lt = controlP_lt->GetRenderPosition(); + MapScreenPointToWorld(disp_lt[0], disp_lt[1], this->Renderer, p_lt); + double p_rb[3] = {0, 0, 0}; + double* disp_rb = controlP_rb->GetRenderPosition(); + MapScreenPointToWorld(disp_rb[0], disp_rb[1], this->Renderer, p_rb); + analyzePixel(p_lt,p_rb); } double *rp = renderPoints->GetPoint(0); text->SetDisplayPosition(rp[0] + 10, rp[1] - 20); - vtkNew source; source->SetPoints(renderPoints); source->Update(); - renderData->DeepCopy(source->GetOutput()); if (Measure::Hidden) { @@ -83,8 +97,8 @@ EllipseAnnotationActor::EllipseAnnotationActor() { //BaseDataPoints->SetPoint(0, 0, 0, 0); //BaseDataPoints->SetPoint(1, 512, 512, 0); - renderPoints->SetNumberOfPoints(GRANULARITY + 1); + BaseDataPoints->Modified(); controlP_lt->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); controlP_rt->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); controlP_lb->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::controlPointCb); @@ -95,10 +109,8 @@ EllipseAnnotationActor::EllipseAnnotationActor() { controlP_lb->SetcontrolledActor(this); controlP_rb->SetcontrolledActor(this); - //this->AddObserver(DraggableActorEvents::DragEvent, this, &EllipseAnnotationActor::selfDragCb); - text = vtkActor2D::New(); vtkNew textMapper; textMapper->SetInput("0"); @@ -147,72 +159,378 @@ void EllipseAnnotationActor::SetRenderer(vtkRenderer *ren) { } void EllipseAnnotationActor::controlPointCb(vtkObject *sender, unsigned long event, void *data) { - - + if (transforming) return; vtkPoints *pts = static_cast(data); double *pos = pts->GetPoint(0); - + double displayP[3] = {pos[0], pos[1], 0}; double result[3] = {0, 0, 0}; - MapScreenPointToWorld(pos[0], pos[1], this->Renderer, result); - - //BaseDataPoints->SetPoint(index, result); - + MapScreenPointToWorld(pos[0]+0.5, pos[1]+0.5, this->Renderer, result); + double * dp = Renderer->GetActiveCamera()->GetDirectionOfProjection(); + int nIndex[2] = {0,1}; + if (abs(dp[1])>0.0){ + nIndex[1] = 2; + } + if (abs(dp[0])>0.0){ + nIndex[0] = 1; + nIndex[1] = 2; + } double *p1; double *p2; + double displaypos[3]={0}; - if (sender == controlP_lt) { + if (sender == controlP_lt ) { p1 = result; p2 = controlP_rb->GetWorldPosition(); - - double p_rt[3] = {p2[0], p1[1], 0}; - double p_lb[3] = {p1[0], p2[1], 0}; + Renderer->SetWorldPoint(p2); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayp_rb = displaypos; + double p_rt[3] = {0, 0, 0}; + MapScreenPointToWorld(displayp_rb[0], displayP[1], Renderer,p_rt); + double p_lb[3] = {0, 0, 0}; + MapScreenPointToWorld( displayP[0], displayp_rb[1], Renderer,p_lb); controlP_rt->SetWorldPosition(p_rt); controlP_lb->SetWorldPosition(p_lb); - } else if (sender == controlP_rt) { + drawCircle(p1, p2); + } else if (sender == controlP_rt ) { p1 = result; p2 = controlP_lb->GetWorldPosition(); - - double p_lt[3] = {p2[0], p1[1], 0}; - double p_rb[3] = {p1[0], p2[1], 0}; + Renderer->SetWorldPoint(p2); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayp_lb = displaypos; + double p_lt[3] = {0, 0, 0}; + MapScreenPointToWorld(displayp_lb[0], displayP[1], Renderer,p_lt); + double p_rb[3] = {0, 0, 0}; + MapScreenPointToWorld( displayP[0], displayp_lb[1], Renderer,p_rb); controlP_lt->SetWorldPosition(p_lt); controlP_rb->SetWorldPosition(p_rb); + drawCircle(p_lt, p_rb); - } else if (sender == controlP_lb) { - p1 = result; - p2 = controlP_rt->GetWorldPosition(); - - double p_lt[3] = {p1[0], p2[1], 0}; - double p_rb[3] = {p2[0], p1[1], 0}; + } else if (sender == controlP_lb ) { + p2 = result; + p1 = controlP_rt->GetWorldPosition(); + Renderer->SetWorldPoint(p1); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayp_rt = displaypos; + double p_lt[3] = {0, 0, 0}; + MapScreenPointToWorld(displayP[0],displayp_rt[1], Renderer,p_lt); + double p_rb[3] = {0, 0, 0}; + MapScreenPointToWorld( displayp_rt[0], displayP[1], Renderer,p_rb); controlP_lt->SetWorldPosition(p_lt); controlP_rb->SetWorldPosition(p_rb); - } else if (sender == controlP_rb) { - p1 = result; - p2 = controlP_lt->GetWorldPosition(); - - double p_rt[3] = {p1[0], p2[1], 0}; - double p_lb[3] = {p2[0], p1[1], 0}; + drawCircle(p_lt, p_rb); + } else if (sender == controlP_rb ) { + p2 = result; + p1 = controlP_lt->GetWorldPosition(); + Renderer->SetWorldPoint(p1); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayp_lt = displaypos; + double p_rt[3] = {0, 0, 0}; + MapScreenPointToWorld(displayP[0], displayp_lt[1], Renderer,p_rt); + double p_lb[3] = {0, 0, 0}; + MapScreenPointToWorld(displayp_lt[0], displayP[1], Renderer,p_lb); controlP_rt->SetWorldPosition(p_rt); controlP_lb->SetWorldPosition(p_lb); + drawCircle(p1, p2); } else { + } - drawCircle(p1, p2); + } void EllipseAnnotationActor::drawCircle(double *p1, double *p2) { - int CenterX = (p1[0] + p2[0]) / 2.0; - int CenterY = (p1[1] + p2[1]) / 2.0; - double r1 = abs(p1[0] - p2[0]) / 2.0; - double r2 = abs(p1[1] - p2[1]) / 2.0; + double * dp = Renderer->GetActiveCamera()->GetDirectionOfProjection(); + + int nIndex[2] = {0,1}; + if (abs(dp[1])>0.0){ + nIndex[1] = 2; + } + if (abs(dp[0])>0.0){ + nIndex[0] = 1; + nIndex[1] = 2; + } + int CenterX = (p1[nIndex[0]] + p2[nIndex[0]] ) / 2.0; + int CenterY = (p1[nIndex[1]] + p2[nIndex[1]] ) / 2.0; + double r1 = round(abs(p1[nIndex[0]] - p2[nIndex[0]]) / 2.0*100.0)/100; + double r2 = round(abs(p1[nIndex[1]]- p2[nIndex[1]]) / 2.0*100.0)/100; double angle = 0; int id = 0; while (angle <= 2.0 * vtkMath::Pi() + (2.0 * vtkMath::Pi() / (GRANULARITY * 1.0))) { - BaseDataPoints->SetPoint(id, r1 * cos(angle) + CenterX, r2 * sin(angle) + CenterY, 0); + double p[3]={0}; + p[0] = p1[0]; + p[1] = p1[1]; + p[2] = p1[2]; + p[nIndex[0]] = r1 * cos(angle) + CenterX; + p[nIndex[1]] = r2 * sin(angle) + CenterY; + + BaseDataPoints->SetPoint(id, p); angle = angle + (2.0 * vtkMath::Pi() / (GRANULARITY * 1.0)); id++; } + BaseDataPoints->Modified(); +} + +template +void makeMessage(int count[4], double avg[4], double sq2[4], T max[4], T min[4], QString &message, double area, const QString& aAreaUnitName) +{ + long totalCount = count[0]; + double avgV = avg[0]; + double sq2V = sq2[0]; + bool hasValue = false; + for (size_t i = 1; i < 4; i++) + { + long lastCount = totalCount; + if (totalCount == 0) + { + totalCount = count[i]; + avgV = avg[i]; + sq2V = sq2[i]; + continue; + } + hasValue = true; + totalCount += count[i]; + double delta = avg[i] - avgV; + avgV = (lastCount * avgV + count[i] * avg[i]) / totalCount; + sq2V += sq2[i] + delta * delta * lastCount * count[i] / totalCount; + } + if (!hasValue) + { + double maxV = std::max({max[0], max[1], max[2], max[3]}); + double minV = std::min({min[0], min[1], min[2], min[3]}); + double sd = std::sqrt(sq2V / totalCount); + message = message + .arg(area) + .arg(0) + .arg(0) + .arg(0) + .arg(0) + .arg(0) + .arg(aAreaUnitName); + } + else{ + double maxV = std::max({max[0], max[1], max[2], max[3]}); + double minV = std::min({min[0], min[1], min[2], min[3]}); + double sd = std::sqrt(sq2V / totalCount); + message = message + .arg(area, 0, 'f', 0) + .arg(totalCount) + .arg(maxV, 0, 'f', 0) + .arg(minV, 0, 'f', 0) + .arg(avgV, 0, 'f', 0) + .arg(sd, 0, 'f', 0) + .arg(aAreaUnitName); + } +} + +template +void calcLoopContent(int count[4], T *row_pointer, size_t j, int pixelStride, T max[4], T min[4], double avg[4], double sq2[4]) +{ + count[omp_get_thread_num()]++; + T value = row_pointer[j * pixelStride]; + max[omp_get_thread_num()] = std::max(value, max[omp_get_thread_num()]); + min[omp_get_thread_num()] = std::min(value, min[omp_get_thread_num()]); + double lastavg = avg[omp_get_thread_num()]; + avg[omp_get_thread_num()] = (count[omp_get_thread_num()] - 1) * avg[omp_get_thread_num()] / ((double)count[omp_get_thread_num()]) + ((double)value) / ((double)count[omp_get_thread_num()]); + sq2[omp_get_thread_num()] = sq2[omp_get_thread_num()] + ((double)value - avg[omp_get_thread_num()]) * ((double)value - lastavg); +} + +template +void EllipseAnnotationActor::vtkValueCalcTemplate(vtkImageData *image, int sliceNumber, double *pt1, double *pt2, double *normals, QString &message) +{ + double spacings[3] ={0}; + int dims[3] = {0}; + image->GetSpacing(spacings); + image->GetDimensions(dims); + T *pointer = (T *)image->GetScalarPointer(); + if (!pointer) { + return; + } + int count[4] = {0, 0, 0, 0}; + T max[4] = {std::numeric_limits::min(),std::numeric_limits::min(),std::numeric_limits::min(),std::numeric_limits::min()}; + T min[4] = {std::numeric_limits::max(),std::numeric_limits::max(),std::numeric_limits::max(),std::numeric_limits::max()}; + double avg[4] = {0, 0, 0, 0}; + double sq2[4] = {0, 0, 0, 0}; + + if (abs(normals[2])>0.5) + { + int CenterX = (pt1[0] + pt2[0]) / 2.0; + int CenterY = (pt1[1] + pt2[1]) / 2.0; + + double r1= abs((pt1[0] - pt2[0]) / 2.0); + double r2= abs((pt1[1] - pt2[1]) / 2.0); + + double r2_1 = pow(r1, 2.0); + double r2_2 = pow(r2, 2.0); + long long sliceStride = dims[0] * dims[1]; + int pixelStride = 1; + int rowStride = dims[0]; + + pointer += sliceStride * (sliceNumber); + + double maxX = std::max({pt1[0], pt2[0]}); + double minX = std::min({pt1[0], pt2[0]}); + double maxY = std::max({pt1[1], pt2[1]}); + double minY = std::min({pt1[1], pt2[1]}); + double maxZ = 0.5; + double minZ = 0.5; + + #pragma omp parallel for num_threads(4) + for (int i = 0; i < dims[1]; i++) + { + T *row_pointer = pointer + i * rowStride; + double posY = i * spacings[1]; + if (posY < minY) + continue; + if (posY > maxY) + break; + for (size_t j = 0; j < dims[0]; j++) + { + double posX = j * spacings[0]; + if (posX < minX) + continue; + if (posX > maxX) + break; + if (pow((posX - CenterX), 2.0) / r2_1 + pow((posY - CenterY), 2.0) / r2_2 <= 1) + { + calcLoopContent(count, row_pointer, j, pixelStride, max, min, avg, sq2); + } + } + } + double area = vtkMath::Pi()*r1*r2; + + makeMessage(count, avg, sq2, max, min, message, area>1000?area/100:area,area>100?mUnitcm2:mUnitmm2); + } + else if (abs(normals[1])>0.5) + { + int CenterX = (pt1[0] + pt2[0]) / 2.0; + int CenterZ = (pt1[2] + pt2[2]) / 2.0; + + double r1= abs((pt1[0] - pt2[0]) / 2.0); + double r2= abs((pt1[2] - pt2[2]) / 2.0); + + double r2_1 = pow(r1, 2.0); + double r2_2 = pow(r2, 2.0); + + long long sliceStride = dims[0]; + int pixelStride = 1; + int rowStride = dims[0] * dims[1]; + pointer += sliceStride * (sliceNumber); + + double maxX = std::max({pt1[0], pt2[0]}); + double minX = std::min({pt1[0], pt2[0]}); + double maxY = 0.5; + double minY = 0.5; + double maxZ = std::max({pt1[2], pt2[2]}); + double minZ = std::min({pt1[2], pt2[2]}); + + #pragma omp parallel for num_threads(4) + for (int i = 0; i < dims[2]; i++) + { + T* row_pointer = pointer + i * rowStride; + double posZ = i * spacings[2]; + if (posZ < minZ) + continue; + if (posZ > maxZ) + break; + for (size_t j = 0; j < dims[0]; j++) + { + double posX = j * spacings[0]; + if (posX < minX) + continue; + if (posX > maxX) + break; + if (pow((posX - CenterX), 2.0) / r2_1 + pow((posZ - CenterZ), 2.0) / r2_2 <= 1) + { + calcLoopContent(count, row_pointer, j, pixelStride, max, min, avg, sq2); + } + } + } + double area = vtkMath::Pi()*r1*r2; + makeMessage(count, avg, sq2, max, min, message, area>1000?area/100:area,area>100?mUnitcm2:mUnitmm2); + } + else{ + int CenterY = (pt1[1] + pt2[1]) / 2.0; + int CenterZ = (pt1[2] + pt2[2]) / 2.0; + + double r1= abs((pt1[1] - pt2[1]) / 2.0); + double r2= abs((pt1[2] - pt2[2]) / 2.0); + + double r2_1 = pow((pt1[1] - pt2[1]) / 2.0,2.0); + double r2_2 = pow((pt1[2] - pt2[2]) / 2.0, 2.0); + long long sliceStride = 1; + int pixelStride = dims[0]; + int rowStride = dims[0]*dims[1]; + + pointer += sliceStride*(sliceNumber); + + double maxY = std::max({pt1[1], pt2[1]}); + double minY = std::min({pt1[1], pt2[1]}); + double maxZ = std::max({pt1[2], pt2[2]}); + double minZ = std::min({pt1[2], pt2[2]}); + + #pragma omp parallel for num_threads(4) + for (int i = 0; i < dims[2]; i++) + { + T *row_pointer = pointer + i * rowStride; + double posZ = i * spacings[2]; + if (posZ < minZ) + continue; + if (posZ > maxZ) + break; + for (size_t j = 0; j < dims[1]; j++) + { + double posY = j * spacings[1]; + if (posY < minY) + continue; + if (posY > maxY) + break; + if (pow((posY - CenterY), 2.0) / r2_1 + pow((posZ - CenterZ), 2.0) / r2_2 <= 1) + { + calcLoopContent(count, row_pointer, j, pixelStride, max, min, avg, sq2); + } + } + } + double area = vtkMath::Pi()*r1*r2; + makeMessage(count, avg, sq2, max, min, message, area>1000?area/100:area,area>100?mUnitcm2:mUnitmm2); + } +} + + +void EllipseAnnotationActor::analyzePixel(double *p1, double *p2) +{ + if (!Renderer) return; + auto renderWin = Renderer->GetRenderWindow(); + if (!renderWin)return; + auto interactor = renderWin->GetInteractor(); + if (!interactor)return; + auto style = interactor->GetInteractorStyle(); + if (!style) return; + auto castedStyle = ActorDraggableInteractorStyle::SafeDownCast(style); + if (!castedStyle) return; + + auto imageSlice = castedStyle->GetCurrentImageSlice(); + if (!imageSlice) return; + auto sliceMapper = vtkImageSliceMapper::SafeDownCast(imageSlice->GetMapper()); + if (!sliceMapper) return; + int sliceNumber = sliceMapper->GetSliceNumber(); + vtkImageData* data = imageSlice->GetMapper()->GetInput(); + if (!data) return; + double normals[3]={0}; + Renderer->GetActiveCamera()->GetDirectionOfProjection(normals); + QString message = QCoreApplication::translate("EllipseAnnotationActor","Area:%1 %7, Pixel:%2,\nMax:%3, Min:%4,\nAvg:%5, SD:%6"); + switch (data->GetScalarType()) + { + vtkTemplateMacro(vtkValueCalcTemplate(data, sliceNumber, p1, p2, normals, message)); + default: + break; + } + vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(message.toUtf8().constData()); } void EllipseAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { @@ -220,12 +538,12 @@ void EllipseAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) } void EllipseAnnotationActor::controlPointsTransform(float x, float y) { - //no need to trigger renderpoint repaint DraggableActor::SafeDownCast(controlP_lt)->Transform(x, y); DraggableActor::SafeDownCast(controlP_rt)->Transform(x, y); DraggableActor::SafeDownCast(controlP_lb)->Transform(x, y); DraggableActor::SafeDownCast(controlP_rb)->Transform(x, y); + BaseDataPoints->Modified(); } void EllipseAnnotationActor::controlPointsApplyTransform() { @@ -242,34 +560,37 @@ void EllipseAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) int y = iren->GetEventPosition()[1]; vtkRenderer *renderer = iren->FindPokedRenderer(x, y); if (!renderer) return; - renderer->SetDisplayPoint(x, y, 0.0); - renderer->DisplayToWorld(); - - double *p_lt = controlP_lt->GetWorldPosition(); - double *p_rb = renderer->GetWorldPoint(); - + double p_rb[3] = {0, 0, 0}; + double displayp_rb[3] = {x+0.5, y+0.5, 0}; + double* p_lt = controlP_lt->GetWorldPosition(); + MapScreenPointToWorld(x+0.5, y+0.5, renderer, p_rb); + renderer->SetWorldPoint(p_lt); + renderer->WorldToDisplay(); + double displayp_lt[3] ={0}; + renderer->GetDisplayPoint(displayp_lt); //if ctrl key is pressed ,draw a standard circle if (iren->GetControlKey()) { - double xlen = p_rb[0] - p_lt[0]; - double ylen = p_rb[1] - p_lt[1]; + double xlen = x - displayp_lt[0]; + double ylen = y - displayp_lt[1]; if (abs(ylen) < abs(xlen)) { - if (xlen > 0) { p_rb[0] = p_lt[0] + abs(ylen); } - if (xlen < 0) { p_rb[0] = p_lt[0] - abs(ylen); } + if (xlen > 0) { displayp_rb[0] = displayp_lt[0] + abs(ylen); } + if (xlen < 0) { displayp_rb[0] = displayp_lt[0] - abs(ylen); } } else { - if (ylen > 0) { p_rb[1] = p_lt[1] + abs(xlen); } - if (ylen < 0) { p_rb[1] = p_lt[1] - abs(xlen); } + if (ylen > 0) { displayp_rb[1] = displayp_lt[1] + abs(xlen); } + if (ylen < 0) { displayp_rb[1] = displayp_lt[1] - abs(xlen); } } - + MapScreenPointToWorld(displayp_rb[0], displayp_rb[1], renderer, p_rb); } - double p_rt[3] = {p_rb[0], p_lt[1], 0}; - double p_lb[3] = {p_lt[0], p_rb[1], 0}; + double p_rt[3] = {0}; + MapScreenPointToWorld(displayp_rb[0], displayp_lt[1], renderer, p_rt); + double p_lb[3] = {0}; + MapScreenPointToWorld(displayp_lt[0], displayp_rb[1], renderer, p_lb); controlP_rt->SetWorldPosition(p_rt); controlP_lb->SetWorldPosition(p_lb); controlP_rb->SetWorldPosition(p_rb); - drawCircle(p_lt, p_rb); //this->SetWorldPosition2(p); iren->Render(); @@ -278,20 +599,21 @@ void EllipseAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) bool EllipseAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { int x = iren->GetEventPosition()[0]; int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); if (!renderer) return false; - renderer->SetDisplayPoint(x, y, 0.0); - renderer->DisplayToWorld(); - double *p = renderer->GetWorldPoint(); - controlP_lt->SetWorldPosition(p); + double result[3] = {0, 0, 0}; + MapScreenPointToWorld(x+0.5, y+0.5, renderer, result); + printf("lt:%d,%d,world lt:%f.2,%f.2,%f.2\r\n", x,y, result[0], result[1], result[2]); + controlP_lt->SetWorldPosition(result); controlP_lt->Highlight(0); - controlP_rt->SetWorldPosition(p); - controlP_lb->SetWorldPosition(p); - controlP_rb->SetWorldPosition(p); + controlP_rt->SetWorldPosition(result); + controlP_lb->SetWorldPosition(result); + controlP_rb->SetWorldPosition(result); for (int id = 0; id <= GRANULARITY; id++) { - BaseDataPoints->SetPoint(id, p[0], p[1], p[2]); + BaseDataPoints->SetPoint(id, result[0], result[1], result[2]); } this->SetRenderer(renderer); iren->Render(); @@ -312,19 +634,3 @@ void EllipseAnnotationActor::ApplyTransform() { this->controlPointsApplyTransform(); } -//void EllipseAnnotationActor::TransformOnly(float x, float y) { -// if (!tempStorePoints) { -// tempStorePoints = vtkPoints::New(); -// tempStorePoints->DeepCopy(renderPoints); -// transforming = true; -// } -// vtkNew trans; -// trans->Translate(x, y, 0); -// vtkNew filter; -// filter->SetTransform(trans); -// vtkNew poly; -// poly->SetPoints(tempStorePoints); -// filter->SetInputData(poly); -// filter->Update(); -// renderPoints->DeepCopy(filter->GetPolyDataOutput()->GetPoints()); -//} diff --git a/src/src/Rendering/Measure/EllipseAnnotationActor.h b/src/src/Rendering/Measure/EllipseAnnotationActor.h index 7e41f9f..f6d6da8 100644 --- a/src/src/Rendering/Measure/EllipseAnnotationActor.h +++ b/src/src/Rendering/Measure/EllipseAnnotationActor.h @@ -9,6 +9,8 @@ class vtkTextProperty; class ControlPointActor; +class vtkImageData; + class EllipseAnnotationActor : public DraggableActor, public Measure { public: //@{ @@ -56,11 +58,17 @@ private: void drawCircle(double *p1, double *p2); + void analyzePixel(double *p1, double *p2); + vtkTextProperty *textProperty; QString mUnitmm2 ; QString mUnitcm2 ; QString mAreaName; + + QString mRenderTime; + + template + void vtkValueCalcTemplate(vtkImageData *image, int sliceNumber, double *pt1, double *pt2, double *normals, QString &message); }; - #endif //OMEGAV_EllipseANNOTATIONACTOR_H diff --git a/src/src/Rendering/Measure/MeasureFactory.cpp b/src/src/Rendering/Measure/MeasureFactory.cpp index 6375e1f..4a6ec7f 100644 --- a/src/src/Rendering/Measure/MeasureFactory.cpp +++ b/src/src/Rendering/Measure/MeasureFactory.cpp @@ -7,6 +7,8 @@ #include "TextAnnotationActor.h" #include "ArrowAnnotationActor.h" #include "EllipseAnnotationActor.h" +#include "RoundAnnotationActor.h" + Measure *MeasureFactory::getMeasure(AnnotationActorType type) { switch (type) { @@ -25,6 +27,8 @@ Measure *MeasureFactory::getMeasure(AnnotationActorType type) { return ArrowAnnotationActor::New(); case AnnotationActorType::EllipseAnn: return EllipseAnnotationActor::New(); + case AnnotationActorType::DiameterAnn: + return RoundAnnotationActor::New(); case AnnotationActorType::TextAnn: return TextAnnotationActor::New(); default: diff --git a/src/src/Rendering/Measure/OpenPolyAnnotationActor.cpp b/src/src/Rendering/Measure/OpenPolyAnnotationActor.cpp index b6dccf5..11ff37b 100644 --- a/src/src/Rendering/Measure/OpenPolyAnnotationActor.cpp +++ b/src/src/Rendering/Measure/OpenPolyAnnotationActor.cpp @@ -192,7 +192,7 @@ void OpenPolyAnnotationActor::UpdatePerimeterAndAreaText() if(!Closed) { bool unitFlag = distance>100; - QString textValue = QString("%1: %2 %3").arg(mDistanceName) + QString textValue = QString("%1: %2%3").arg(mDistanceName) .arg(unitFlag?distance/10:distance,0,'f',2).arg(unitFlag?mUnitcm:mUnitmm); vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(textValue.toUtf8().constData()); text->SetVisibility(1); @@ -206,7 +206,7 @@ void OpenPolyAnnotationActor::UpdatePerimeterAndAreaText() bool unitFlag1 = distance>100; bool unitFlag2 = area>1000; - QString textValue = QString("%1: %2 %3, %4: %5 %6").arg(mDistanceName) + QString textValue = QString("%1: %2%3, %4: %5%6").arg(mDistanceName) .arg(unitFlag1?distance/10:distance,0,'f',2).arg(unitFlag1?mUnitcm:mUnitmm) .arg(mAreaName) .arg(unitFlag2?area/100:area,0,'f',2).arg(unitFlag2?mUnitcm2:mUnitmm2); @@ -220,3 +220,7 @@ void OpenPolyAnnotationActor::UpdatePerimeterAndAreaText() bool OpenPolyAnnotationActor::Valid() { return Closed == 0 ? BaseDataPoints->GetNumberOfPoints() > 1 : BaseDataPoints->GetNumberOfPoints() > 2; } + +void OpenPolyAnnotationActor::onTerminate(vtkRenderWindowInteractor *){ + +} diff --git a/src/src/Rendering/Measure/OpenPolyAnnotationActor.h b/src/src/Rendering/Measure/OpenPolyAnnotationActor.h index a960cab..fdaa261 100644 --- a/src/src/Rendering/Measure/OpenPolyAnnotationActor.h +++ b/src/src/Rendering/Measure/OpenPolyAnnotationActor.h @@ -38,6 +38,8 @@ public: bool onMeasureLeftButtonUp(vtkRenderWindowInteractor *) override; + void onTerminate(vtkRenderWindowInteractor *) override; + Measure *GetNextMeasure() override { auto m = OpenPolyAnnotationActor::New(); diff --git a/src/src/Rendering/Measure/RoundAnnotationActor.cpp b/src/src/Rendering/Measure/RoundAnnotationActor.cpp new file mode 100644 index 0000000..e404611 --- /dev/null +++ b/src/src/Rendering/Measure/RoundAnnotationActor.cpp @@ -0,0 +1,411 @@ + +#include "RoundAnnotationActor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include "omp.h" + +#include "Interaction/ActorDraggableInteractorStyle.h" +#include "Rendering/Core/ControlPointActor.h" +#include "Common/QGlobals.h" + + +vtkStandardNewMacro(RoundAnnotationActor) + +namespace { + const int GRANULARITY = 60; +} + +void RoundAnnotationActor::BuildShape() { + if (!BaseDataPoints->GetNumberOfPoints()) return; + QString stamp = QString("%1-%2").arg(BaseDataPoints->GetMTime()) + .arg(Renderer->GetActiveCamera()->GetMTime()); + if (mRenderTime == stamp) return; + QStringList stamps = stamp.split("-"); + QStringList renderTimes = mRenderTime.split("-"); + mRenderTime = stamp; + RebuildRenderPoint(); + //base data change change message + if (stamps[0] != renderTimes[0]) + { double p_lt[3] = {0, 0, 0}; + double* disp_lt = controlP_lt->GetRenderPosition(); + MapScreenPointToWorld(disp_lt[0], disp_lt[1], this->Renderer, p_lt); + double p_rb[3] = {0, 0, 0}; + double* disp_rb = controlP_rb->GetRenderPosition(); + MapScreenPointToWorld(disp_rb[0], disp_rb[1], this->Renderer, p_rb); + // analyzePixel(p_lt,p_rb); + } + double *rp = renderPoints->GetPoint(0); + + text->SetDisplayPosition(rp[0] + 10, rp[1] - 20); + + vtkNew source; + source->SetPoints(renderPoints); + source->Update(); + renderData->DeepCopy(source->GetOutput()); + + if (Measure::Hidden) { + this->Hide(); + controlP_rt->Hide(); + controlP_lb->Hide(); + controlP_rb->Hide(); + controlP_lt->Hide(); + + } else { + this->Show(); + controlP_rt->Show(); + controlP_lb->Show(); + controlP_rb->Show(); + controlP_lt->Show(); + } +} + +RoundAnnotationActor::RoundAnnotationActor() { + controlP_rt = ControlPointActor::New(); + controlP_lb = ControlPointActor::New(); + controlP_rb = ControlPointActor::New(); + controlP_lt = ControlPointActor::New(); + + controlP_lt->SetWorldPosition(0, 0, 0); + controlP_rt->SetWorldPosition(512, 0, 0); + controlP_lb->SetWorldPosition(0, 512, 0); + controlP_rb->SetWorldPosition(512, 512, 0); + + BaseDataPoints->SetNumberOfPoints(GRANULARITY + 1); + //BaseDataPoints->SetPoint(0, 0, 0, 0); + //BaseDataPoints->SetPoint(1, 512, 512, 0); + + renderPoints->SetNumberOfPoints(GRANULARITY + 1); + BaseDataPoints->Modified(); + controlP_lt->AddObserver(DraggableActorEvents::DragEvent, this, &RoundAnnotationActor::controlPointCb); + controlP_rt->AddObserver(DraggableActorEvents::DragEvent, this, &RoundAnnotationActor::controlPointCb); + controlP_lb->AddObserver(DraggableActorEvents::DragEvent, this, &RoundAnnotationActor::controlPointCb); + controlP_rb->AddObserver(DraggableActorEvents::DragEvent, this, &RoundAnnotationActor::controlPointCb); + + controlP_lt->SetcontrolledActor(this); + controlP_rt->SetcontrolledActor(this); + controlP_lb->SetcontrolledActor(this); + controlP_rb->SetcontrolledActor(this); + + //this->AddObserver(DraggableActorEvents::DragEvent, this, &RoundAnnotationActor::selfDragCb); + + text = vtkActor2D::New(); + vtkNew textMapper; + textMapper->SetInput("0"); + text->SetMapper(textMapper); + textProperty = textMapper->GetTextProperty(); + if (LanguageHelper::getLanguage() == ChineseSimple && QFile::exists(FONT_FILE_PATH)) + { + textProperty->SetFontFamily(VTK_FONT_FILE); + textProperty->SetFontFile(FONT_FILE_PATH); + } + else{ + textProperty->SetFontFamilyToArial(); + textProperty->SetBold(1); + } + textProperty->SetFontSize(16); + textProperty->SetColor(0.8, 0.8, 0.0); + textProperty->SetOpacity(0.75); + textProperty->SetFrame(false); + //textProperty->SetFrameColor(1.0,0.0,0.0); + textProperty->SetBackgroundColor(1.0, 0.0, 0.0); + textProperty->SetBackgroundOpacity(0.3); + + mUnitmm2 = QCoreApplication::translate("RoundAnnotationActor","mm²"); + mUnitcm2 = QCoreApplication::translate("RoundAnnotationActor","cm²"); + mAreaName = QCoreApplication::translate("RoundAnnotationActor", "Area"); + mUnitcm3 = QCoreApplication::translate("RoundAnnotationActor","cm³"); + mUnitmm3 = QCoreApplication::translate("RoundAnnotationActor","mm³"); + mVolumeName = QCoreApplication::translate("RoundAnnotationActor", "Volume"); +} + +RoundAnnotationActor::~RoundAnnotationActor() { + controlP_lt->Delete(); + controlP_rt->Delete(); + controlP_lb->Delete(); + controlP_rb->Delete(); + + controlP_lt = nullptr; + controlP_rt = nullptr; + controlP_lb = nullptr; + controlP_rb = nullptr; +} + +void RoundAnnotationActor::SetRenderer(vtkRenderer *ren) { + DraggableActor::SetRenderer(ren); + controlP_lt->SetRenderer(ren); + controlP_rt->SetRenderer(ren); + controlP_lb->SetRenderer(ren); + controlP_rb->SetRenderer(ren); +} + +void RoundAnnotationActor::controlPointCb(vtkObject *sender, unsigned long event, void *data) { + if (transforming) return; + vtkPoints *pts = static_cast(data); + double *pos = pts->GetPoint(0); + double displayP[3] = {pos[0], pos[1], 0}; + double result[3] = {0, 0, 0}; + MapScreenPointToWorld(pos[0]+0.5, pos[1]+0.5, this->Renderer, result); + double * dp = Renderer->GetActiveCamera()->GetDirectionOfProjection(); + + int nIndex[2] = {0,1}; + if (abs(dp[1])>0.0){ + nIndex[1] = 2; + } + if (abs(dp[0])>0.0){ + nIndex[0] = 1; + nIndex[1] = 2; + } + double *p1; + double *p2; + double displaypos[3]={0}; + + if (sender == controlP_lt ) { + p1 = result; + p2 = controlP_rb->GetWorldPosition(); + Renderer->SetWorldPoint(p2); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayP2 = displaypos; + drawCircle(p1, p2); + double d = std::min({abs(displayP[0] - displayP2[0]), abs(displayP[1] - displayP2[1])}); + pos[0] = displayP[0]>displayP2[0]?displayP2[0]+d:displayP2[0]-d; + pos[1] = displayP[1]>displayP2[1]?displayP2[1]+d:displayP2[1]-d; + pts->SetPoint(0, pos); + double p_rt[3] = {0, 0, 0}; + MapScreenPointToWorld(displayP2[0], pos[1], Renderer,p_rt); + double p_lb[3] = {0, 0, 0}; + MapScreenPointToWorld(pos[0], displayP2[1], Renderer,p_lb); + controlP_rt->SetWorldPosition(p_rt); + controlP_lb->SetWorldPosition(p_lb); + } else if (sender == controlP_rt ) { + p1 = result; + p2 = controlP_lb->GetWorldPosition(); + Renderer->SetWorldPoint(p2); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayP2 = displaypos; + drawCircle(p1, p2); + double d = std::min({abs(displayP[0] - displayP2[0]), abs(displayP[1] - displayP2[1])}); + pos[0] = displayP[0]>displayP2[0]?displayP2[0]+d:displayP2[0]-d; + pos[1] = displayP[1]>displayP2[1]?displayP2[1]+d:displayP2[1]-d; + pts->SetPoint(0, pos); + double p_lt[3] = {0, 0, 0}; + MapScreenPointToWorld(displayP2[0], pos[1], Renderer,p_lt); + double p_rb[3] = {0, 0, 0}; + MapScreenPointToWorld( pos[0], displayP2[1], Renderer,p_rb); + controlP_lt->SetWorldPosition(p_lt); + controlP_rb->SetWorldPosition(p_rb); + } else if (sender == controlP_lb ) { + p1 = result; + p2 = controlP_rt->GetWorldPosition(); + Renderer->SetWorldPoint(p2); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayP2 = displaypos; + drawCircle(p1, p2); + double d = std::min({abs(displayP[0] - displayP2[0]), abs(displayP[1] - displayP2[1])}); + pos[0] = displayP[0]>displayP2[0]?displayP2[0]+d:displayP2[0]-d; + pos[1] = displayP[1]>displayP2[1]?displayP2[1]+d:displayP2[1]-d; + pts->SetPoint(0, pos); + + double p_lt[3] = {0, 0, 0}; + MapScreenPointToWorld(pos[0],displayP2[1], Renderer,p_lt); + double p_rb[3] = {0, 0, 0}; + MapScreenPointToWorld( displayP2[0], pos[1], Renderer,p_rb); + controlP_lt->SetWorldPosition(p_lt); + controlP_rb->SetWorldPosition(p_rb); + } else if (sender == controlP_rb ) { + p1 = result; + p2 = controlP_lt->GetWorldPosition(); + Renderer->SetWorldPoint(p2); + Renderer->WorldToDisplay(); + Renderer->GetDisplayPoint(displaypos); + double *displayP2 = displaypos; + drawCircle(p1, p2); + double d = std::min({abs(displayP[0] - displayP2[0]), abs(displayP[1] - displayP2[1])}); + pos[0] = displayP[0]>displayP2[0]?displayP2[0]+d:displayP2[0]-d; + pos[1] = displayP[1]>displayP2[1]?displayP2[1]+d:displayP2[1]-d; + pts->SetPoint(0, pos); + double p_rt[3] = {0, 0, 0}; + MapScreenPointToWorld(pos[0], displayP2[1], Renderer,p_rt); + double p_lb[3] = {0, 0, 0}; + MapScreenPointToWorld(displayP2[0], pos[1], Renderer,p_lb); + controlP_rt->SetWorldPosition(p_rt); + controlP_lb->SetWorldPosition(p_lb); + } else { + + } + +} + + +void RoundAnnotationActor::drawCircle(double *p1, double *p2) { + double * dp = Renderer->GetActiveCamera()->GetDirectionOfProjection(); + + int nIndex[2] = {0,1}; + if (abs(dp[1])>0.0){ + nIndex[1] = 2; + } + if (abs(dp[0])>0.0){ + nIndex[0] = 1; + nIndex[1] = 2; + } + double r1 = round(abs(p1[nIndex[0]] - p2[nIndex[0]]) / 2.0*100.0)/100; + double r2 = round(abs(p1[nIndex[1]]- p2[nIndex[1]]) / 2.0*100.0)/100; + double r = r1r){ + CenterX = CenterX > p2[nIndex[0]]?(r+p2[nIndex[0]]):(p2[nIndex[0]]-r); + } + int CenterY = (p1[nIndex[1]] + p2[nIndex[1]] ) / 2.0; + if (abs(CenterY-p2[nIndex[1]])>r){ + CenterY = CenterY > p2[nIndex[1]]?(r+p2[nIndex[1]]):(p2[nIndex[1]]-r); + } + + double angle = 0; + int id = 0; + while (angle <= 2.0 * vtkMath::Pi() + (2.0 * vtkMath::Pi() / (GRANULARITY * 1.0))) { + double p[3]={0}; + p[0] = p1[0]; + p[1] = p1[1]; + p[2] = p1[2]; + p[nIndex[0]] = r * cos(angle) + CenterX; + p[nIndex[1]] = r * sin(angle) + CenterY; + + BaseDataPoints->SetPoint(id, p); + angle = angle + (2.0 * vtkMath::Pi() / (GRANULARITY * 1.0)); + id++; + } + BaseDataPoints->Modified(); + double area = vtkMath::Pi()*r*r; + double volume = area*3*r/4; + QString textValue = QString("%1: %2%3\r\n%4: %5%6").arg(mAreaName) + .arg(area>1000?area/100:area,0,'f',2).arg(area>1000?mUnitcm2:mUnitmm2) + .arg(mVolumeName) + .arg(volume>10000?volume/1000:volume,0,'f',2).arg(volume>10000?mUnitcm3:mUnitmm3); + vtkTextMapper::SafeDownCast(text->GetMapper())->SetInput(textValue.toUtf8().constData()); +} + +void RoundAnnotationActor::selfDragCb(vtkObject *, unsigned long, void *data) { +//control point drag realized by father +} + +void RoundAnnotationActor::controlPointsTransform(float x, float y) { + //no need to trigger renderpoint repaint + DraggableActor::SafeDownCast(controlP_lt)->Transform(x, y); + DraggableActor::SafeDownCast(controlP_rt)->Transform(x, y); + DraggableActor::SafeDownCast(controlP_lb)->Transform(x, y); + DraggableActor::SafeDownCast(controlP_rb)->Transform(x, y); + BaseDataPoints->Modified(); +} + +void RoundAnnotationActor::controlPointsApplyTransform() { + //restore base point + DraggableActor::SafeDownCast(controlP_lt)->ApplyTransform(); + DraggableActor::SafeDownCast(controlP_rt)->ApplyTransform(); + DraggableActor::SafeDownCast(controlP_lb)->ApplyTransform(); + DraggableActor::SafeDownCast(controlP_rb)->ApplyTransform(); +} + + +void RoundAnnotationActor::onMeasureMouseMove(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return; + double p_rb[3] = {0, 0, 0}; + double displayp_rb[3] = {x+0.5, y+0.5, 0}; + double* p_lt = controlP_lt->GetWorldPosition(); + MapScreenPointToWorld(x+0.5, y+0.5, renderer, p_rb); + renderer->SetWorldPoint(p_lt); + renderer->WorldToDisplay(); + double displayp_lt[3] ={0}; + renderer->GetDisplayPoint(displayp_lt); + + double xlen = x - displayp_lt[0]; + double ylen = y - displayp_lt[1]; + if (abs(ylen) < abs(xlen)) { + if (xlen > 0) { displayp_rb[0] = displayp_lt[0] + abs(ylen); } + if (xlen < 0) { displayp_rb[0] = displayp_lt[0] - abs(ylen); } + } else { + if (ylen > 0) { displayp_rb[1] = displayp_lt[1] + abs(xlen); } + if (ylen < 0) { displayp_rb[1] = displayp_lt[1] - abs(xlen); } + } + MapScreenPointToWorld(displayp_rb[0], displayp_rb[1], renderer, p_rb); + + double p_rt[3] = {0}; + MapScreenPointToWorld(displayp_rb[0], displayp_lt[1], renderer, p_rt); + double p_lb[3] = {0}; + MapScreenPointToWorld(displayp_lt[0], displayp_rb[1], renderer, p_lb); + + controlP_rt->SetWorldPosition(p_rt); + controlP_lb->SetWorldPosition(p_lb); + controlP_rb->SetWorldPosition(p_rb); + + drawCircle(p_lt, p_rb); + //this->SetWorldPosition2(p); + iren->Render(); +} + +bool RoundAnnotationActor::onMeasureLeftButtonDown(vtkRenderWindowInteractor *iren) { + int x = iren->GetEventPosition()[0]; + int y = iren->GetEventPosition()[1]; + + vtkRenderer *renderer = iren->FindPokedRenderer(x, y); + if (!renderer) return false; + double result[3] = {0, 0, 0}; + MapScreenPointToWorld(x+0.5, y+0.5, renderer, result); + printf("lt:%d,%d,world lt:%f.2,%f.2,%f.2\r\n", x,y, result[0], result[1], result[2]); + controlP_lt->SetWorldPosition(result); + controlP_lt->Highlight(0); + + controlP_rt->SetWorldPosition(result); + controlP_lb->SetWorldPosition(result); + controlP_rb->SetWorldPosition(result); + + for (int id = 0; id <= GRANULARITY; id++) { + BaseDataPoints->SetPoint(id, result[0], result[1], result[2]); + } + this->SetRenderer(renderer); + iren->Render(); + return true; +} + +bool RoundAnnotationActor::onMeasureLeftButtonUp(vtkRenderWindowInteractor *iren) { + return false; +} + +void RoundAnnotationActor::Transform(float x, float y) { + DraggableActor::Transform(x, y); + this->controlPointsTransform(x, y); +} + +void RoundAnnotationActor::ApplyTransform() { + DraggableActor::ApplyTransform(); + this->controlPointsApplyTransform(); +} + diff --git a/src/src/Rendering/Measure/RoundAnnotationActor.h b/src/src/Rendering/Measure/RoundAnnotationActor.h new file mode 100644 index 0000000..e95e3e3 --- /dev/null +++ b/src/src/Rendering/Measure/RoundAnnotationActor.h @@ -0,0 +1,70 @@ +#ifndef OMEGAV_ROUNDANNOTATIONACTOR_H +#define OMEGAV_ROUNDANNOTATIONACTOR_H + +#include "Rendering/Core/DraggableActor.h" +#include "Rendering/Measure/Measure.h" + +class vtkTextProperty; + +class ControlPointActor; + +class RoundAnnotationActor : public DraggableActor, public Measure { +public: + //@{ + /** + * Standard methods for instances of this class. + */ + static RoundAnnotationActor *New(); + + vtkTypeMacro(RoundAnnotationActor, DraggableActor); + + //@} + void controlPointsTransform(float x, float y); + + void controlPointsApplyTransform(); + + void SetRenderer(vtkRenderer *ren) override; + + void BuildShape() override; + + void Transform(float x, float y) override; + + void ApplyTransform() override; + + bool onMeasureLeftButtonDown(vtkRenderWindowInteractor *) override; + + void onMeasureMouseMove(vtkRenderWindowInteractor *) override; + + bool onMeasureLeftButtonUp(vtkRenderWindowInteractor *) override; + + NextMeasureMacro(RoundAnnotationActor); +protected: + RoundAnnotationActor(); + + ~RoundAnnotationActor() override; + +private: + ControlPointActor *controlP_rt = nullptr; + ControlPointActor *controlP_lb = nullptr; + ControlPointActor *controlP_rb = nullptr; + ControlPointActor *controlP_lt = nullptr; + + void selfDragCb(vtkObject *, unsigned long event, void *data); + + void controlPointCb(vtkObject *sender, unsigned long event, void *data); + + void drawCircle(double *p1, double *p2); + + // void analyzePixel(double *p1, double *p2); + + vtkTextProperty *textProperty; + QString mUnitmm2 ; + QString mUnitcm2 ; + QString mUnitmm3 ; + QString mUnitcm3 ; + QString mAreaName; + QString mVolumeName; + QString mRenderTime; +}; + +#endif /* OMEGAV_ROUNDANNOTATIONACTOR_H */ diff --git a/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp b/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp index 7fd9fff..38b065e 100644 --- a/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp +++ b/src/src/UI/Widget/ToolBar/DefaultToolBar.cpp @@ -17,7 +17,7 @@ namespace { const char *SYNC_MANUAL_URL = ":/InfiniteViewer/Icon/sync/sync_manual.png"; const char *SYNC_AUTO_URL = ":/InfiniteViewer/Icon/sync/sync_auto.png"; const char *SYNC_DIS_URL = ":/InfiniteViewer/Icon/sync/sync_dis.png"; - const int ACTION_COUNT = 7; + const int ACTION_COUNT = 8; const ActionProperty MEASURE_ACTIIONS[ACTION_COUNT] = { { ":/InfiniteViewer/Icon/distance.png", AnnotationActorType::RulerAnn}, { ":/InfiniteViewer/Icon/angle.png", AnnotationActorType::AngleAnn}, @@ -25,6 +25,7 @@ namespace { { ":/InfiniteViewer/Icon/polyline.png", AnnotationActorType::OpenPolygonAnn}, { ":/InfiniteViewer/Icon/arrow.png", AnnotationActorType::ArrowAnn}, { ":/InfiniteViewer/Icon/ellipse.png", AnnotationActorType::EllipseAnn}, + { ":/InfiniteViewer/Icon/diameter.png", AnnotationActorType::DiameterAnn}, { ":/InfiniteViewer/Icon/text.png", AnnotationActorType::TextAnn} }; } diff --git a/src/translations/zh_CN.ts b/src/translations/zh_CN.ts index 8af9618..15e0617 100644 --- a/src/translations/zh_CN.ts +++ b/src/translations/zh_CN.ts @@ -500,6 +500,15 @@ Area 面积 + + + Area:%1 %7, Pixel:%2, +Max:%3, Min:%4, +Avg:%5, SD:%6 + 面积:%1 %7, 像素:%2, +最大值:%3, 最小值:%4, +平均值:%5, 标准差:%6 + ExportDialog @@ -1037,6 +1046,21 @@ Area 面积 + + + cm³ + 立方厘米 + + + + mm³ + 立方毫米 + + + + Volume + 体积 + RulerAnnotationActor