/*========================================================================= Program: Visualization Toolkit Module: pqMultiSliceView.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "pqMultiSliceView.h" #include #include #include #include "QVTKWidget.h" #include "pqDataRepresentation.h" #include "pqMultiSliceAxisWidget.h" #include "pqPropertyLinks.h" #include "pqRepresentation.h" #include "vtkAxis.h" #include "vtkChartXY.h" #include "vtkDataRepresentation.h" #include "vtkMath.h" #include "vtkPlot.h" #include "vtkPVDataInformation.h" #include "vtkPVRenderView.h" #include "vtkSMMultiSliceViewProxy.h" #include "vtkSMProperty.h" #include "vtkSMPropertyHelper.h" #include "vtkSMRenderViewProxy.h" #include "vtkSMRepresentationProxy.h" #include "vtkView.h" #define MULTI_SLICE_AXIS_THIKNESS 80 #define MULTI_SLICE_AXIS_ACTIVE_SIZE 20 #define MULTI_SLICE_AXIS_EDGE_MARGIN 10 //----------------------------------------------------------------------------- pqMultiSliceView::pqMultiSliceView( const QString& viewType, const QString& group, const QString& name, vtkSMViewProxy* viewProxy, pqServer* server, QObject* p) : pqRenderView(viewType, group, name, viewProxy, server, p) { // Nothing is changing anything. this->UserIsInteracting = false; // When data change make sure the bounds are updated QObject::connect(this, SIGNAL(updateDataEvent()), this, SLOT(updateAxisBounds())); // Make sure if the proxy change by undo-redo, we properly update the Qt UI viewProxy->GetProperty("XSlicesValues")->AddObserver( vtkCommand::ModifiedEvent, this, &pqMultiSliceView::updateViewModelCallBack); viewProxy->GetProperty("YSlicesValues")->AddObserver( vtkCommand::ModifiedEvent, this, &pqMultiSliceView::updateViewModelCallBack); viewProxy->GetProperty("ZSlicesValues")->AddObserver( vtkCommand::ModifiedEvent, this, &pqMultiSliceView::updateViewModelCallBack); for(int i=0;i<9;++i) { this->NormalValuesHolder[i] = this->OriginValuesHolder[i] = 0; } } //----------------------------------------------------------------------------- pqMultiSliceView::~pqMultiSliceView() { } //----------------------------------------------------------------------------- QWidget* pqMultiSliceView::createWidget() { pqPropertyLinks *links = new pqPropertyLinks(this); vtkSMProxy* smproxy = this->getProxy(); // Get the internal widget that we want to decorate this->InternalWidget = qobject_cast(this->pqRenderView::createWidget()); // Build the widget hierarchy QWidget* container = new QWidget(); container->setStyleSheet("background-color: white"); container->setAutoFillBackground(true); QGridLayout* gridLayout = new QGridLayout(container); this->InternalWidget->setParent(container); // Init top axis this->AxisX = new pqMultiSliceAxisWidget(container); this->AxisX->setAxisType(vtkAxis::LEFT); this->AxisX->setRange(-10,10); this->AxisX->setTitle("X"); this->AxisX->SetEdgeMargin(MULTI_SLICE_AXIS_EDGE_MARGIN); this->AxisX->SetActiveSize(MULTI_SLICE_AXIS_ACTIVE_SIZE); this->AxisX->setFixedWidth(MULTI_SLICE_AXIS_THIKNESS); this->AxisX->renderView(); this->AxisY = new pqMultiSliceAxisWidget(container); this->AxisY->setAxisType(vtkAxis::TOP); this->AxisY->setRange(-10,10); this->AxisY->setTitle("Y"); this->AxisY->SetEdgeMargin(MULTI_SLICE_AXIS_EDGE_MARGIN); this->AxisY->SetActiveSize(MULTI_SLICE_AXIS_ACTIVE_SIZE); this->AxisY->setFixedHeight(MULTI_SLICE_AXIS_THIKNESS - 4); this->AxisY->renderView(); this->AxisZ = new pqMultiSliceAxisWidget(container); this->AxisZ->setAxisType(vtkAxis::RIGHT); this->AxisZ->setRange(-10,10); this->AxisZ->setTitle("Z"); this->AxisZ->SetEdgeMargin(MULTI_SLICE_AXIS_EDGE_MARGIN); this->AxisZ->SetActiveSize(MULTI_SLICE_AXIS_ACTIVE_SIZE); this->AxisZ->setFixedWidth(MULTI_SLICE_AXIS_THIKNESS); this->AxisZ->renderView(); this->AxisXYZ[0] = this->AxisX; this->AxisXYZ[1] = this->AxisY; this->AxisXYZ[2] = this->AxisZ; // Setup links so the UI updates when the property changes. links->addPropertyLink(this->AxisX, "title", SIGNAL(titleChanged(const QString&)), smproxy, smproxy->GetProperty("XTitle")); this->AxisX->connect(links, SIGNAL(smPropertyChanged()), SLOT(renderView())); links->addPropertyLink(this->AxisY, "title", SIGNAL(titleChanged(const QString&)), smproxy, smproxy->GetProperty("YTitle")); this->AxisY->connect(links, SIGNAL(smPropertyChanged()), SLOT(renderView())); links->addPropertyLink(this->AxisZ, "title", SIGNAL(titleChanged(const QString&)), smproxy, smproxy->GetProperty("ZTitle")); this->AxisZ->connect(links, SIGNAL(smPropertyChanged()), SLOT(renderView())); gridLayout->addWidget(this->AxisY, 0, 1); // TOP gridLayout->addWidget(this->AxisX, 1, 0); // LEFT gridLayout->addWidget(this->AxisZ, 1, 2); // RIGHT gridLayout->addWidget(this->InternalWidget,1,1); gridLayout->setContentsMargins(0,0,0,0); gridLayout->setSpacing(0); // Properly do the binding between the proxy and the 3D widget vtkSMRenderViewProxy* renModule = this->getRenderViewProxy(); if (this->InternalWidget && renModule) { this->InternalWidget->SetRenderWindow(renModule->GetRenderWindow()); } for (int cc=0; cc < 3; cc++) { // Make sure we are aware when the slice positions changes this->connect(this->AxisXYZ[cc], SIGNAL(sliceAdded(int)), SLOT(onSliceAdded(int))); this->connect(this->AxisXYZ[cc], SIGNAL(sliceRemoved(int)), SLOT(onSliceRemoved(int))); this->connect(this->AxisXYZ[cc], SIGNAL(sliceModified(int)), SLOT(onSliceModified(int))); // Attach click listener to slice marks this->connect(this->AxisXYZ[cc], SIGNAL(markClicked(int,int,double)), SLOT(onSliceClicked(int,int,double))); } // Make sure the UI reflect the proxy state this->updateViewModelCallBack(NULL, 0, NULL); return container; } //----------------------------------------------------------------------------- void pqMultiSliceView::updateAxisBounds() { double bounds[6]; vtkSMMultiSliceViewProxy* viewPxy = vtkSMMultiSliceViewProxy::SafeDownCast( this->getProxy()); Q_ASSERT(viewPxy); viewPxy->GetDataBounds(bounds); if (vtkMath::AreBoundsInitialized(bounds)) { this->AxisX->setRange(bounds[0], bounds[1]); this->AxisY->setRange(bounds[2], bounds[3]); this->AxisZ->setRange(bounds[4], bounds[5]); } else { this->AxisX->setRange(-10, 10); this->AxisY->setRange(-10, 10); this->AxisZ->setRange(-10, 10); } // Make sure we render the new range this->AxisX->renderView(); this->AxisY->renderView(); this->AxisZ->renderView(); } //----------------------------------------------------------------------------- void pqMultiSliceView::updateSlices() { // Make sure we know that the origin of the changes come from the UI this->UserIsInteracting = true; int nbSliceX = 0; const double* sliceX = this->AxisX->getVisibleSlices(nbSliceX); int nbSliceY = 0; const double* sliceY = this->AxisY->getVisibleSlices(nbSliceY); int nbSliceZ = 0; const double* sliceZ = this->AxisZ->getVisibleSlices(nbSliceZ); // Update view which will notify its representation that care about those info vtkSMViewProxy* view = this->getViewProxy(); vtkSMPropertyHelper(view, "XSlicesValues").Set(sliceX, nbSliceX); vtkSMPropertyHelper(view, "YSlicesValues").Set(sliceY, nbSliceY); vtkSMPropertyHelper(view, "ZSlicesValues").Set(sliceZ, nbSliceZ); view->UpdateVTKObjects(); this->render(); // Get back to an idle mode this->UserIsInteracting = false; } //----------------------------------------------------------------------------- int pqMultiSliceView::getAxisIndex(QObject* axis) { for (int cc=0; cc < 3; cc++) { if (this->AxisXYZ[cc] == axis) { return cc; } } return -1; } //----------------------------------------------------------------------------- void pqMultiSliceView::onSliceAdded(int activeSliceIndex) { QObject* aSender = this->sender(); int axisIndex = this->getAxisIndex(aSender); Q_ASSERT(axisIndex >= 0 && axisIndex <=2); this->updateSlices(); // Notify that the slices location have changed emit sliceAdded(axisIndex, activeSliceIndex); } //----------------------------------------------------------------------------- void pqMultiSliceView::onSliceRemoved(int activeSliceIndex) { QObject* aSender = this->sender(); int axisIndex = this->getAxisIndex(aSender); Q_ASSERT(axisIndex >= 0 && axisIndex <=2); this->updateSlices(); // Notify that the slices location have changed emit sliceRemoved(axisIndex, activeSliceIndex); } //----------------------------------------------------------------------------- void pqMultiSliceView::onSliceModified(int activeSliceIndex) { QObject* aSender = this->sender(); int axisIndex = this->getAxisIndex(aSender); Q_ASSERT(axisIndex >= 0 && axisIndex <=2); this->updateSlices(); // Notify that the slices location have changed emit sliceModified(axisIndex, activeSliceIndex); } //----------------------------------------------------------------------------- void pqMultiSliceView::updateViewModelCallBack(vtkObject*,unsigned long, void*) { if(this->UserIsInteracting) { return; // We don't want to update our data model } std::vector xSlices = vtkSMPropertyHelper(this->getViewProxy(), "XSlicesValues").GetDoubleArray(); std::vector ySlices = vtkSMPropertyHelper(this->getViewProxy(), "YSlicesValues").GetDoubleArray(); std::vector zSlices = vtkSMPropertyHelper(this->getViewProxy(), "ZSlicesValues").GetDoubleArray(); assert("The maximum number of slice can not be bigger than 255" && ( 255 > std::max( std::max( xSlices.size(), ySlices.size()), zSlices.size()))); // Build a tmp array of visibility set to true bool visibility[255]; memset(visibility, true, 255); double emptyDouble = 0; if( xSlices.size() > 0 ) { this->AxisX->updateSlices(&xSlices[0], visibility, static_cast(xSlices.size())); } else // Reset slices { this->AxisX->updateSlices(&emptyDouble, visibility, 0); } if( ySlices.size() > 0 ) { this->AxisY->updateSlices(&ySlices[0], visibility, static_cast(ySlices.size())); } else // Reset slices { this->AxisY->updateSlices(&emptyDouble, visibility, 0); } if( zSlices.size() > 0 ) { this->AxisZ->updateSlices(&zSlices[0], visibility, static_cast(zSlices.size())); } else // Reset slices { this->AxisZ->updateSlices(&emptyDouble, visibility, 0); } this->render(); } //----------------------------------------------------------------------------- const double* pqMultiSliceView::GetVisibleSlices(int axisIndex, int &numberOfSlices) { switch(axisIndex) { case 0: return this->AxisX->getVisibleSlices(numberOfSlices); case 1: return this->AxisY->getVisibleSlices(numberOfSlices); case 2: return this->AxisZ->getVisibleSlices(numberOfSlices); } // Invalid axis numberOfSlices = 0; return NULL; } //----------------------------------------------------------------------------- const double* pqMultiSliceView::GetAllSlices(int axisIndex, int &numberOfSlices) { switch(axisIndex) { case 0: return this->AxisX->getSlices(numberOfSlices); case 1: return this->AxisY->getSlices(numberOfSlices); case 2: return this->AxisZ->getSlices(numberOfSlices); } // Invalid axis numberOfSlices = 0; return NULL; } //----------------------------------------------------------------------------- const double* pqMultiSliceView::GetSliceNormal(int axisIndex) { if(axisIndex < 0 || axisIndex > 2) { return NULL; } const char* propertyNames[3] = {"XSlicesNormal", "YSlicesNormal", "ZSlicesNormal"}; std::vector values = vtkSMPropertyHelper(this->getViewProxy(), propertyNames[axisIndex]).GetDoubleArray(); for(int i=0;i<3;++i) { this->NormalValuesHolder[i+ (3*axisIndex)] = values[i]; } return &this->NormalValuesHolder[(3*axisIndex)]; } //----------------------------------------------------------------------------- const double* pqMultiSliceView::GetSliceOrigin(int axisIndex) { if(axisIndex < 0 || axisIndex > 2) { return NULL; } const char* propertyNames[3] = {"XSlicesOrigin", "YSlicesOrigin", "ZSlicesOrigin"}; std::vector values = vtkSMPropertyHelper(this->getViewProxy(), propertyNames[axisIndex]).GetDoubleArray(); for(int i=0;i<3;++i) { this->OriginValuesHolder[i+ (3*axisIndex)] = values[i]; } return &this->OriginValuesHolder[(3*axisIndex)]; } //----------------------------------------------------------------------------- void pqMultiSliceView::onSliceClicked(int button, int modifier, double value) { QObject* senderObject = QObject::sender(); if(senderObject == this->AxisX.data()) { emit sliceClicked(0, value, button, modifier); } else if(senderObject == this->AxisY.data()) { emit sliceClicked(1, value, button, modifier); } else if(senderObject == this->AxisZ.data()) { emit sliceClicked(2, value, button, modifier); } } //----------------------------------------------------------------------------- void pqMultiSliceView::setCursor(const QCursor &c) { if(this->InternalWidget) { this->InternalWidget->setCursor(c); } } //----------------------------------------------------------------------------- QVTKWidget* pqMultiSliceView::getInternalWidget() { return this->InternalWidget; } //----------------------------------------------------------------------------- bool pqMultiSliceView::getOutlineVisibility() { return vtkSMPropertyHelper(this->getViewProxy(), "ShowOutline").GetAsInt() != 0; } //----------------------------------------------------------------------------- void pqMultiSliceView::setOutlineVisibility(bool visible) { vtkSMPropertyHelper(this->getViewProxy(), "ShowOutline").Set(visible ? 1 : 0); this->getViewProxy()->UpdateVTKObjects(); }