/***************************************************************************** * $CAMITK_LICENCE_BEGIN$ * * CamiTK - Computer Assisted Medical Intervention ToolKit * (c) 2001-2018 Univ. Grenoble Alpes, CNRS, TIMC-IMAG UMR 5525 (GMCAO) * * Visit http://camitk.imag.fr for more information * * This file is part of CamiTK. * * CamiTK is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * CamiTK is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3 along with CamiTK. If not, see . * * $CAMITK_LICENCE_END$ ****************************************************************************/ // -- Core stuff #include "Action.h" #include "RendererWidget.h" #include "InteractiveViewer.h" #include "SliderSpinBoxWidget.h" #include "Log.h" #include "MeshComponent.h" #include "MeshDataModel.h" #include "PropertyObject.h" // -- stl stuff // -- Qt stuff #include #include #include #include #include #include #include #include #include #include // -- vtk stuff #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace camitk { // ---------------------- singleton ---------------------------- /// the register containing all the viewers (static instantiation, global variable, but global only for this file) QMap InteractiveViewer::viewers; QString InteractiveViewer::defaultNames[5] = {QString("3DViewer"), QString("axialViewer"), QString("coronalViewer"), QString("sagittalViewer"), QString("arbitraryViewer") }; InteractiveViewer* InteractiveViewer::getNewViewer(QString name, ViewerType type) { if (!viewers.contains(name)) { //-- Congratulation, you got yourself a new InteractiveViewer! (but are you really sure you need it?) viewers[name] = new InteractiveViewer(name, type); } return viewers.value(name); } InteractiveViewer* InteractiveViewer::getViewer(QString name) { const std::string nameChar = name.toStdString(); if (viewers.contains(name)) { return viewers.value(name); } else { //-- if this is a default scene, get a new one if it does not exists // perform the singleton instantiation here! unsigned int i = 0; // special case, the first viewer is a 3D viewer if (name == defaultNames[i]) { return getNewViewer(defaultNames[i], GEOMETRY_VIEWER); } InteractiveViewer* singletonInstance = nullptr; i++; while (i < 6 && !singletonInstance) { if (name == defaultNames[i]) { singletonInstance = getNewViewer(defaultNames[i], SLICE_VIEWER); } i++; } // default orientation for axial, sagittal and coronal is not the same // See Image Reorientation Action Documentation... // singletonInstance->toggleOrientationDecorations(false); if (name == defaultNames[1]) { // Axial Viewer singletonInstance->getRendererWidget()->setCameraOrientation(RendererWidget::RIGHT_DOWN); QString letters[4] = {QString("R"), QString("L"), QString("A"), QString("P") }; singletonInstance->getRendererWidget()->setOrientationDecorationsLetters(letters); singletonInstance->toggleOrientationDecorations(true); singletonInstance->getRendererWidget()->getActiveCamera()->ParallelProjectionOn(); } else if (name == defaultNames[2]) { // Coronal Viewer singletonInstance->getRendererWidget()->setCameraOrientation(RendererWidget::LEFT_BACK); QString letters[4] = {QString("R"), QString("L"), QString("S"), QString("I") }; singletonInstance->getRendererWidget()->setOrientationDecorationsLetters(letters); singletonInstance->toggleOrientationDecorations(true); singletonInstance->getRendererWidget()->getActiveCamera()->ParallelProjectionOn(); } else if (name == defaultNames[3]) { // Sagittal Viewer singletonInstance->getRendererWidget()->setCameraOrientation(RendererWidget::BACK_DOWN); QString letters[4] = {QString("A"), QString("P"), QString("S"), QString("I") }; singletonInstance->getRendererWidget()->setOrientationDecorationsLetters(letters); singletonInstance->toggleOrientationDecorations(true); singletonInstance->getRendererWidget()->getActiveCamera()->ParallelProjectionOn(); } else if (name == defaultNames[4]) { // Arbitrary Viewer singletonInstance->getRendererWidget()->setCameraOrientation ( RendererWidget::RIGHT_DOWN ); singletonInstance->toggleOrientationDecorations(true); singletonInstance->getRendererWidget()->getActiveCamera()->ParallelProjectionOn(); } return singletonInstance; } } InteractiveViewer* InteractiveViewer::get3DViewer() { return getViewer(defaultNames[0]); } InteractiveViewer* InteractiveViewer::getAxialViewer() { return getViewer(defaultNames[1]); } InteractiveViewer* InteractiveViewer::getCoronalViewer() { return getViewer(defaultNames[2]); } InteractiveViewer* InteractiveViewer::getSagittalViewer() { return getViewer(defaultNames[3]); } InteractiveViewer* InteractiveViewer::getArbitraryViewer() { return getViewer(defaultNames[4]); } // ---------------------- constructor ---------------------------- InteractiveViewer::InteractiveViewer(QString& name, ViewerType type) : Viewer(name) { myType = type; // frame is NULL, but the rendererWidget and sliceSlider have to be instantiated // (even if the InteractiveViewer is not visible yet, it can still do some actions) frame = nullptr; //-- create and init the RendererWidget object for display and interactions if (myType == SLICE_VIEWER) { // block rotations (interactions) rendererWidget = new RendererWidget(frame, RendererWidget::TRACKBALL_2D); sliceSlider = new SliderSpinBoxWidget(frame); } else { // classic 3D view rendererWidget = new RendererWidget(frame); sliceSlider = nullptr; } cameraMap.insert("default", rendererWidget->getActiveCamera()); //-- create the slice slider if needed viewerMenu = nullptr; viewerToolbar = nullptr; screenshotActionMenu = nullptr; displayedTopLevelComponents = 0; //-- set picking mode pickingEffectIsSelecting = true; // default effect isPicking = false; // by default, viewer is not picking if (myType == SLICE_VIEWER) { // by default, the 2D scenes are set to pick slice pixels initPicking(InteractiveViewer::PIXEL_PICKING); } else { // no picking initPicking(InteractiveViewer::NO_PICKING); } //-- current interaction is not changing slice isChangingSlice = false; //-- connection for selection connector = vtkSmartPointer::New(); connector->Connect(rendererWidget->GetInteractor(), vtkCommand::EndPickEvent, this, SLOT(picked())); // create the different properties of the viewers createProperties(); // init from settings initSettings(); } // ---------------------- destructor ---------------------------- InteractiveViewer::~InteractiveViewer() { viewers.remove(this->getName()); delete viewerMenu; viewerMenu = nullptr; delete viewerToolbar; viewerToolbar = nullptr; delete frame; frame = nullptr; } // ---------------------- initSettings ---------------------------- void InteractiveViewer::initSettings() { QSettings& settings = Application::getSettings(); settings.beginGroup(Application::getName() + ".InteractiveViewer." + objectName().simplified().replace(" ", "")); // the background color QColor bg; if (myType == GEOMETRY_VIEWER) { // default is white bg.setNamedColor(settings.value("backgroundColor", QColor::fromRgbF(1.0, 1.0, 1.0)).toString()); propertyObject->setProperty(backgroundColorProperty->getName().toStdString().c_str(), bg); } else { // default is black bg.setNamedColor(settings.value("backgroundColor", QColor::fromRgbF(0.0, 0.0, 0.0)).toString()); } // the gradient background bool gradientBackground; if (myType == GEOMETRY_VIEWER) { gradientBackground = settings.value("gradientBackground", true).toBool(); propertyObject->setProperty(backgroundGradientColorProperty->getName().toStdString().c_str(), gradientBackground); } else { gradientBackground = settings.value("gradientBackground", false).toBool(); } // the control mode RendererWidget::ControlMode controlMode; if (myType == GEOMETRY_VIEWER) { controlMode = (RendererWidget::ControlMode) settings.value("controlMode", RendererWidget::TRACKBALL).toInt(); } else { controlMode = (RendererWidget::ControlMode) settings.value("controlMode", RendererWidget::TRACKBALL_2D).toInt(); } rendererWidget->setControlMode(controlMode); // the initial camera orientation RendererWidget::CameraOrientation cameraOrientation; cameraOrientation = (RendererWidget::CameraOrientation) settings.value("cameraOrientation", rendererWidget->getCameraOrientation()).toInt(); rendererWidget->setCameraOrientation(cameraOrientation); // highlight mode HighlightMode highlightMode = (HighlightMode) settings.value("highlightMode", SELECTION).toInt(); propertyObject->setProperty(highlightModeProperty->getName().toStdString().c_str(), highlightMode); // lines as tubes bool linesAsTubes = settings.value("linesAsTubes", false).toBool(); // default false propertyObject->setProperty(linesAsTubesProperty->getName().toStdString().c_str(), linesAsTubes); // screenshot Action visibility bool screenshotActionVisible = settings.value("screenshotActionVisible", false).toBool(); propertyObject->setProperty(screenshotActionProperty->getName().toStdString().c_str(), screenshotActionVisible); // backface culling bool backfaceCulling = settings.value("backfaceCulling", false).toBool(); // default false propertyObject->setProperty(backfaceCullingProperty->getName().toStdString().c_str(), backfaceCulling); // point size double pointSize = settings.value("pointSize", 4.0).toDouble(); propertyObject->setProperty(pointSizeProperty->getName().toStdString().c_str(), pointSize); // orientation letters if (myType == GEOMETRY_VIEWER) { rendererWidget->toggleOrientationDecorations(false); } settings.endGroup(); // -- propertyObject event filter delegation // This method changes the properties, which will automatically trigger the // QEvent::DynamicPropertyChange // This in turns will call eventFilter each time // As the settings group is opened, it will generate nested beginGroup // Therefore the event filter should only be installed at the end, // when all properties are set. // propertyObject is monitored by the 3D viewer instance (this) when its properties change // All event filter on the property object are delegated to the InteractiveViewer class // @see eventFilter() if (myType == GEOMETRY_VIEWER) { propertyObject->installEventFilter(this); // trigger change for all the values eventFilter(this, new QEvent(QEvent::DynamicPropertyChange)); } } // ---------------------- getWidget ---------------------------- QWidget* InteractiveViewer::getWidget(QWidget* parent) { if (!frame) { //-- create the actions initActions(); //-- build the frame frame = new InteractiveViewerFrame(parent, this); frame->setFrameStyle(QFrame::StyledPanel | QFrame::Plain); //-- handle layout auto* myLayout = new QHBoxLayout(frame); auto* horizontalSplitter = new QSplitter(frame); myLayout->addWidget(horizontalSplitter); myLayout->setSpacing(0); myLayout->setMargin(0); //-- show the renderer! rendererWidget->setParent(frame); rendererWidget->show(); horizontalSplitter->addWidget(rendererWidget); connect(rendererWidget, SIGNAL(rightButtonPressed()), this, SLOT(rightClick())); //-- create the slider if needed if (myType == SLICE_VIEWER) { auto* rightSideLayout = new QVBoxLayout; rightSideLayout->setSpacing(0); rightSideLayout->setMargin(0); // add snapshot widget screenshotActionMenu = new QToolBar(rendererWidget); screenshotActionMenu->addAction(screenshotAction); screenshotActionMenu->setEnabled(true); screenshotActionMenu->setVisible(false); screenshotActionMenu->layout()->setSpacing(0); screenshotActionMenu->layout()->setMargin(0); rightSideLayout->addWidget(screenshotActionMenu, 0, Qt::AlignCenter); // add slider sliceSlider->setParent(frame); // connect the slider to this scene connect(sliceSlider, SIGNAL(valueChanged(int)), this, SLOT(sliderChanged(int))); rightSideLayout->addWidget(sliceSlider); // add the right side layout sideFrame = new QFrame; /*sideFrame->setSpacing ( 0 ); sideFrame->setMargin ( 0 ); */ sideFrame->setLayout(rightSideLayout); QSizePolicy sideFrameSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); sideFrame->setSizePolicy(sideFrameSizePolicy); sideFrame->setMinimumWidth(15); sideFrame->setMaximumWidth(60); //myLayout->addWidget ( sideFrame ); horizontalSplitter->addWidget(sideFrame); } else { sideFrame = nullptr; } // do this for receiving key-events frame->setFocusPolicy(Qt::ClickFocus); // TODO A feature disappeared when switching to vtk5... // use a vtkOutlineSource to highlight the picked actor (show-hide picked actor bounding box // set the what's this text initWhatsThis(); startWhatsThisSection("Mouse bindings"); addWhatsThisItem("Left", "Rotate"); addWhatsThisItem("Ctrl+Left", "Pick (flip selection flag for point/cell)"); addWhatsThisItem("Shift+Left", "Translate selection"); addWhatsThisItem("Middle", "Pan"); addWhatsThisItem("Right", "Zoom"); endWhatsThisSection(); startWhatsThisSection("Keyboard bindings (upper or lower case)"); addWhatsThisItem("3", "Toggle 3D red/blue stereo display"); addWhatsThisItem("A", "Toggle view axes"); addWhatsThisItem("C", "Toggle color scale"); addWhatsThisItem("F", "Toggle backface culling"); addWhatsThisItem("I", "Toggle image interpolation on slices"); addWhatsThisItem("J", "Joystick interaction mode"); addWhatsThisItem("L", "Toggle view labels"); addWhatsThisItem("Alt+L", "Toggle light follows camera"); addWhatsThisItem("Alt+P", "Toggle point rendering"); addWhatsThisItem("P", "Pick (flip selection flag for point/cell)"); addWhatsThisItem("R", "Reset Image Interactor Look Up Table"); addWhatsThisItem("S", "Take a screenshot"); addWhatsThisItem("Alt+S", "Toggle surfacerendering"); addWhatsThisItem("T", "Trackball interaction mode"); addWhatsThisItem("Alt+W", "Toggle wireframe rendering"); endWhatsThisSection(); startWhatsThisSection(); addWhatsThisItem("SPACE", "Update/refresh view"); addWhatsThisItem("HOME", "Reset camera to default view point or so that everything is visible"); addWhatsThisItem("LEFT", "Turn -5° around camera Y axis"); addWhatsThisItem("Ctrl+LEFT", "Turn -90° around camera Y axis"); addWhatsThisItem("RIGHT", "Turn 5° around camera Y axis"); addWhatsThisItem("Ctrl+RIGHT", "Turn 90° around camera Y axis"); addWhatsThisItem("UP", "Turn -5° around camera X axis"); addWhatsThisItem("Ctrl+UP", "Turn -90° around camera X axis"); addWhatsThisItem("DOWN", "Turn 5° around camera X axis"); addWhatsThisItem("Ctrl+DOWN", "Turn 90° around camera X axis"); addWhatsThisItem("+", "Move slider a step above"); addWhatsThisItem("-", "Move slider a step below"); addWhatsThisItem("PAGE Up", "Move slider a page above"); addWhatsThisItem("PAGE Down", "Move slider a page down"); addWhatsThisItem("ESC", "Clear current selection"); endWhatsThisSection(); startWhatsThisSection("Other Shortcuts"); addWhatsThisItem("F1", "Show this help (i.e. the what's this help)"); addWhatsThisItem("F2", "Print debugging information on console"); endWhatsThisSection(); // add the text as whatsThis frame->setWhatsThis(whatsThis); } else { if (frame->parentWidget() != parent) { bool isVisible = frame->isVisible(); frame->setParent(parent); frame->setVisible(isVisible); } } return frame; } // ---------------------- getPropertyObject ---------------------------- QObject* InteractiveViewer::getPropertyObject() { if (myType == GEOMETRY_VIEWER) { return propertyObject; } else { return nullptr; } } // ---------------------- getName ---------------------------- QString InteractiveViewer::getName() const { return objectName(); } // ---------------- numberOfViewedComponent ---------------- unsigned int InteractiveViewer::numberOfViewedComponent() { return actorMap.size(); } // ---------------------- refresh ---------------------------- void InteractiveViewer::refresh(Viewer* v) { //-- do not refresh if the widget was not created if (!frame) { return; } //-- first remove non active Component (if InteractiveViewer was called by itself, no need to do that) if (v != this) { //-- check all present QList compRendered = actorMap.keys(); foreach (Component* comp, compRendered) { if (!Application::isAlive(comp)) { // remove from the renderer and map removeAllActors(comp); } } } //-- now check the full component list ComponentList allComponents = Application::getAllComponents(); switch (myType) { case GEOMETRY_VIEWER: // check all Components foreach (Component* comp, allComponents) { // remove from the renderer and map removeAllActors(comp); // check if the Component is to be displayed here if (comp->getVisibility(this)) { if (comp->getRepresentation() == Component::GEOMETRY) { //-- set the correct rendering parameters updateSelectionDisplay(comp); //-- check the line as tube representation bool linesAsTubes = propertyObject->property(linesAsTubesProperty->getName().toStdString().c_str()).toBool(); if (linesAsTubes) { comp->setLinesAsTubes(linesAsTubes); } //-- add the correct actors (NULL is return if the actor is not appropriate, i.e. hidden or not in proper mode) addActor(comp, comp->getActor(InterfaceGeometry::Surface)); addActor(comp, comp->getActor(InterfaceGeometry::Wireframe)); addActor(comp, comp->getActor(InterfaceGeometry::Points)); for (unsigned int i = 0; i < comp->getNumberOfProp(); i++) { addActor(comp, comp->getProp(i)); } } else { if (comp->getRepresentation() == Component::SLICE) { addActor(comp, comp->get3DImageActor()); for (unsigned int i = 0; i < comp->getNumberOfProp(); i++) { addActor(comp, comp->getProp(i)); } } } } if (comp->getFrameVisibility(this)) { addActor(comp, comp->getFrameAxisActor()); } } break; case SLICE_VIEWER: // remove all from the renderer and map QList compRendered = actorMap.keys(); foreach (Component* comp, compRendered) { removeAllActors(comp); } // only display the last selected (if exists otherwise the last added) int idOfLastSelected = Application::getSelectedComponents().size() - 1; if (idOfLastSelected >= 0) { while (idOfLastSelected >= 0 && !(Application::getSelectedComponents() [idOfLastSelected]->getVisibility(this) && Application::getSelectedComponents() [idOfLastSelected]->getRepresentation() == Component::SLICE)) { idOfLastSelected--; } } if (idOfLastSelected >= 0) { // there is valid Component to display Component* comp = Application::getSelectedComponents() [idOfLastSelected]; // add both the slices, the plane used for picking and the pixel highlighter addActor(comp, comp->getPixelActor()); addActor(comp, comp->get2DImageActor()); addActor(comp, comp->getPickPlaneActor()); for (unsigned int i = 0; i < comp->getNumberOfProp(); i++) { addActor(comp, comp->getProp(i)); } // set the slider range and update widgets values sliceSlider->setRange(0, comp->getNumberOfSlices() - 1); sliceSlider->setValue(comp->getSlice()); } else { // no selection, get the last added Component idOfLastSelected = allComponents.size() - 1; while (idOfLastSelected >= 0 && !(allComponents[idOfLastSelected]->getVisibility(this) && allComponents[idOfLastSelected]->getRepresentation() == Component::SLICE)) { idOfLastSelected--; } if (idOfLastSelected >= 0) { // there is valid Component to display Component* comp = allComponents[idOfLastSelected]; // add both the slices, the plane used for picking and the pixel highlighter addActor(comp, comp->getPixelActor()); addActor(comp, comp->get2DImageActor()); addActor(comp, comp->getPickPlaneActor()); for (unsigned int i = 0; i < comp->getNumberOfProp(); i++) { addActor(comp, comp->getProp(i)); } // set the slider range and update widgets values sliceSlider->setRange(0, comp->getNumberOfSlices() - 1); sliceSlider->setValue(comp->getSlice()); } } break; } // refresh only camera on slice viewer which are not being picked nor is changing slice or geometry viewer which needs additional refresh if (displayedTopLevelComponents == (unsigned) Application::getTopLevelComponents().size()) { rendererWidget->updateAxes(); // refresh only camera on slice viewer which are not being picked or geometry viewer which needs additional refresh if (((myType == SLICE_VIEWER) && !isPicking)) { if (!isChangingSlice) { resetCamera(); } else // just reset clipping range { rendererWidget->resetClippingPlanes(); } // TODO add a boolean "Reset camera when picking" that can be set on or off // and saved as a setting } rendererWidget->refresh(); } else { // there is some difference => reset camera in case of Geometry Viewer rendererWidget->updateAxes(); resetCamera(); displayedTopLevelComponents = Application::getTopLevelComponents().size(); } //-- update actions updateActions(); } // ---------------------- refreshRenderer ---------------------------- void InteractiveViewer::refreshRenderer() { rendererWidget->refresh(); } // ---------------------- removeAllActors ---------------------------- void InteractiveViewer::removeAllActors(Component* comp) { QList > actorRendered = actorMap.values(comp); foreach (vtkSmartPointer a, actorRendered) { rendererWidget->removeProp(a); } // remove from the list actorMap.remove(comp); } // ---------------------- addActor ---------------------------- void InteractiveViewer::addActor(Component* comp, vtkSmartPointer a) { if (a) { rendererWidget->addProp(a); actorMap.insert(comp, a); } } // ---------------------- getMenu ---------------------------- QMenu* InteractiveViewer::getMenu() { if (frame && !viewerMenu) { //-- create the main menu viewerMenu = new QMenu(objectName()); viewerMenu->setTearOffEnabled(true); //-- add actions to the menu QMenu* options = new QMenu("View Options"); options->addAction(backgroundColorAction); options->addAction(toggleCopyrightAction); options->addAction(toggleAxesAction); if (myType == SLICE_VIEWER) { options->addAction(toggleOrientationDecorationsAction); options->addAction(toggleScreenshotAction); } // AxesView mode QMenu* cameraOrientationMenu = new QMenu("Camera Orientation"); options->addMenu(cameraOrientationMenu); cameraOrientationMenu->addAction(cameraOrientationRightDownAction); cameraOrientationMenu->addAction(cameraOrientationLeftUpAction); cameraOrientationMenu->addAction(cameraOrientationRightUpAction); if (myType == GEOMETRY_VIEWER) { options->addSeparator(); //options->addAction(toggleAxesAction); options->addAction(toggleLabelAction); options->addAction(toggleLinesAsTubesAction); options->addAction(toggleBackfaceCullingAction); // display mode QMenu* highlightMenu = new QMenu("Highlight Mode"); options->addMenu(highlightMenu); highlightMenu->addAction(highlightOffAction); highlightMenu->addAction(highlightSelectionAction); highlightMenu->addAction(highlightSelectionOnlyAction); // camera menu QMenu* controlModeMenu = new QMenu("Camera Control"); controlModeMenu->addAction(controlModeTrackballAction); controlModeMenu->addAction(controlModeJoystickAction); options->addMenu(controlModeMenu); // force toolbar creation and add it options->addSeparator(); options->addAction(getToolBar()->toggleViewAction()); } viewerMenu->addMenu(options); if (myType == GEOMETRY_VIEWER) { viewerMenu->addSeparator(); // picking menu QMenu* pickingMenu = new QMenu("&Picking"); pickingMenu->addAction(pickPointAction); pickingMenu->addAction(pickCellAction); viewerMenu->addMenu(pickingMenu); viewerMenu->addMenu(renderingMenu); } viewerMenu->addAction(screenshotAction); //-- update actions updateActions(); } return viewerMenu; } // ---------------------- getToolBar ---------------------------- QToolBar* InteractiveViewer::getToolBar() { if (frame && !viewerToolbar) { if (myType == GEOMETRY_VIEWER) { viewerToolbar = new QToolBar(objectName() + " Toolbar"); // ensure object name is set for saving the state viewerToolbar->setObjectName(objectName() + " Toolbar"); viewerToolbar->addAction(pickPointAction); viewerToolbar->addAction(pickCellAction); viewerToolbar->addAction(pickPointRegionAction); viewerToolbar->addAction(pickCellRegionAction); viewerToolbar->addSeparator(); viewerToolbar->addAction(surfaceAction); viewerToolbar->addAction(wireframeAction); viewerToolbar->addAction(pointsAction); if (colorAction) { viewerToolbar->addAction(colorAction); } viewerToolbar->addAction(glyphAction); viewerToolbar->addAction(toggleLabelAction); viewerToolbar->addSeparator(); viewerToolbar->addAction(toggleAxesAction); viewerToolbar->addAction(screenshotAction); viewerToolbar->addSeparator(); viewerToolbar->addAction(scalarDataColorAction); } /* if (myType == SLICE_VIEWER) { viewerToolBar->addAction(toggleOrientationDecorationsAction); } */ } return viewerToolbar; } // ---------------------- initActions ---------------------------- void InteractiveViewer::initActions() { //-- screenshot screenshotAction = new QAction(QPixmap(":/snapShot"), tr("Screenshot"), this); screenshotAction->setStatusTip(tr("Take a screenshot/snapshot of the renderer window")); screenshotAction->setWhatsThis(tr("Screenshot\n\nTake a screenshot/snapshot of the renderer viewer")); connect(screenshotAction, SIGNAL(triggered()), this, SLOT(screenshot())); if (myType == GEOMETRY_VIEWER) { //--- actions of the View menu surfaceAction = new QAction(QPixmap(":/surfaceRendering"), tr("&Surface"), this); surfaceAction->setCheckable(true); surfaceAction->setStatusTip(tr("Enables/disables surface rendering")); surfaceAction->setWhatsThis(tr("Enables/disables surface rendering")); connect(surfaceAction, SIGNAL(triggered()), this, SLOT(renderingActorsChanged())); wireframeAction = new QAction(QPixmap(":/wireframeRendering"), tr("&Wireframe"), this); wireframeAction->setCheckable(true); wireframeAction->setStatusTip(tr("Enables/disables wireframe rendering")); wireframeAction->setWhatsThis(tr("Enables/disables wireframe rendering")); connect(wireframeAction, SIGNAL(triggered()), this, SLOT(renderingActorsChanged())); pointsAction = new QAction(QPixmap(":/pointRendering"), tr("&Points"), this); pointsAction->setCheckable(true); pointsAction->setStatusTip(tr("Enables/disables points rendering")); pointsAction->setWhatsThis(tr("Enables/disables points rendering")); connect(pointsAction, SIGNAL(triggered()), this, SLOT(renderingActorsChanged())); toggleLabelAction = new QAction(QPixmap(":/label"), tr("Show &Label"), this); toggleLabelAction->setCheckable(true); toggleLabelAction->setStatusTip(tr("Show/hide label")); toggleLabelAction->setWhatsThis(tr("Show/hide label")); connect(toggleLabelAction, SIGNAL(toggled(bool)), this, SLOT(setLabel(bool))); // check if the action is available Action* changeColorAction = Application::getAction("Change Color"); // the InteractiveViewer colorAction is just the same as the action's QAction if (changeColorAction) { colorAction = changeColorAction->getQAction(); } else { colorAction = nullptr; } // -- show scalar data // scalar data model, filter that will only select the scalars for the point and cell data scalarDataModel = new MeshDataFilterModel(MeshComponent::POINTS | MeshComponent::CELLS, MeshComponent::SCALARS, this); // combo box for viewing the scalar data model scalarDataComboBox = new QComboBox(viewerToolbar); scalarDataComboBox->setModel(scalarDataModel); scalarDataComboBox->setMinimumWidth(150); scalarDataColorAction = new QWidgetAction(this); scalarDataColorAction->setDefaultWidget(scalarDataComboBox); // TODO connect the combobox to the model (so that you can use the checkbox in the combo directly without going to the property tab) glyphAction = new QAction(QPixmap(":/showGlyph"), tr("Show/Hide &Glyph"), this); glyphAction->setCheckable(true); glyphAction->setStatusTip(tr("Show/hide point glyph")); glyphAction->setWhatsThis(tr("Show/hide point glyph")); connect(glyphAction, SIGNAL(toggled(bool)), this, SLOT(setGlyph(bool))); // rendering mode renderingMenu = new QMenu("Rendering Mode"); renderingMenu->addAction(surfaceAction); renderingMenu->addAction(wireframeAction); renderingMenu->addAction(pointsAction); // -- display mode auto* displayGrp = new QActionGroup(this); connect(displayGrp, SIGNAL(triggered(QAction*)), this, SLOT(highlightModeChanged(QAction*))); highlightSelectionAction = new QAction(tr("&Selection"), displayGrp); highlightSelectionAction->setCheckable(true); highlightSelectionAction->setChecked(true); // by default the selected Item and the others are normal highlightSelectionAction->setStatusTip(tr("display the selected component(s) normally while the others are shaded")); highlightSelectionAction->setWhatsThis(tr("display the selected component(s) using their default rendering properties while the others are shaded (i.e., with transparency)")); highlightSelectionOnlyAction = new QAction(tr("Selection &Only"), displayGrp); highlightSelectionOnlyAction->setCheckable(true); highlightSelectionOnlyAction->setStatusTip(tr("display the selected component(s) normally while the others are hidden")); highlightSelectionOnlyAction->setWhatsThis(tr("display the selected component(s) using their default rendering properties while the others are masked (i.e., not displayed)")); highlightOffAction = new QAction(tr("&No Highlight"), displayGrp); highlightOffAction->setCheckable(true); highlightOffAction->setStatusTip(tr("display all component(s) the same way")); highlightOffAction->setWhatsThis(tr("display all component(s) using their default rendering properties, either they are selected or not")); //-- camera mode auto* cameraModeGrp = new QActionGroup(this); connect(cameraModeGrp, SIGNAL(triggered(QAction*)), this, SLOT(viewControlModeChanged(QAction*))); controlModeTrackballAction = new QAction(tr("&Trackball"), cameraModeGrp); controlModeTrackballAction->setCheckable(true); controlModeTrackballAction->setChecked(true); controlModeTrackballAction->setStatusTip(tr("Set the camera control mode to trackball\n(the camera moves with the mouse)")); controlModeTrackballAction->setWhatsThis(tr("Set the camera control mode to trackball\n(the camera moves with the mouse)")); controlModeJoystickAction = new QAction(tr("&Joystick"), cameraModeGrp); controlModeJoystickAction->setCheckable(true); controlModeJoystickAction->setStatusTip(tr("Set the camera control mode to joystick\n(the camera moves when the mouse button is pressed)")); controlModeJoystickAction->setWhatsThis(tr("Set the camera control mode to joystick\n(the camera moves when the mouse button is pressed)")); } //-- camera orientation mode auto* cameraOrientationGrp = new QActionGroup(this); connect(cameraOrientationGrp, SIGNAL(triggered(QAction*)), this, SLOT(cameraOrientationChanged(QAction*))); cameraOrientationRightDownAction = new QAction(tr("x-right y-down z-back"), cameraOrientationGrp); cameraOrientationRightDownAction->setCheckable(true); cameraOrientationRightDownAction->setChecked(rendererWidget->getCameraOrientation() == RendererWidget::RIGHT_DOWN); cameraOrientationRightDownAction->setStatusTip(tr("Set the camera such as x points to the right, y points downward, z points backward")); cameraOrientationRightDownAction->setWhatsThis(tr("Set the camera such as x points to the right, y points downward, z points backward")); cameraOrientationLeftUpAction = new QAction(tr("x-left y-up z-back"), cameraOrientationGrp); cameraOrientationLeftUpAction->setCheckable(true); cameraOrientationLeftUpAction->setChecked(rendererWidget->getCameraOrientation() == RendererWidget::LEFT_UP); cameraOrientationLeftUpAction->setStatusTip(tr("Set the camera such as x points to the left, y points upward, z points backward")); cameraOrientationLeftUpAction->setWhatsThis(tr("Set the camera such as x points to the left, y points upward, z points backward")); cameraOrientationRightUpAction = new QAction(tr("x-right y-up z-front"), cameraOrientationGrp); cameraOrientationRightUpAction->setCheckable(true); cameraOrientationRightUpAction->setChecked(rendererWidget->getCameraOrientation() == RendererWidget::RIGHT_UP); cameraOrientationRightUpAction->setStatusTip(tr("Set the camera such as x points to the right, y points upward, z points to the front")); cameraOrientationRightUpAction->setWhatsThis(tr("Set the camera such as x points to the right, y points upward, z points to the front")); //-- others backgroundColorAction = new QAction(tr("Background &Color"), this); backgroundColorAction->setStatusTip(tr("Change background color")); backgroundColorAction->setWhatsThis(tr("Change the color background of the InteractiveViewer")); connect(backgroundColorAction, SIGNAL(triggered()), this, SLOT(backgroundColor())); toggleCopyrightAction = new QAction(tr("Show &Copyright Text"), this); toggleCopyrightAction->setCheckable(true); toggleCopyrightAction->setChecked(true); toggleCopyrightAction->setStatusTip(tr("Display the copyright text")); toggleCopyrightAction->setWhatsThis(tr("Display the copyright text")); connect(toggleCopyrightAction, SIGNAL(toggled(bool)), this, SLOT(toggleCopyright(bool))); toggleAxesAction = new QAction(QPixmap(":/axes"), tr("Show &Axes"), this); toggleAxesAction->setCheckable(true); toggleAxesAction->setStatusTip(tr("Display the coordinate axes")); toggleAxesAction->setWhatsThis(tr("Display the coordinate axes of the InteractiveViewer")); connect(toggleAxesAction, SIGNAL(toggled(bool)), this, SLOT(toggleAxes(bool))); if (myType == SLICE_VIEWER) { toggleOrientationDecorationsAction = new QAction(tr(" Show Slice Orientation Decoration"), this); toggleOrientationDecorationsAction->setCheckable(true); toggleOrientationDecorationsAction->setStatusTip(tr("Display Slice Orientation Information (Right, Left, Anterior, Posterior, Superior, Inferior")); toggleOrientationDecorationsAction->setWhatsThis(tr("Display Slice Orientation Information (Right, Left, Anterior, Posterior, Superior, Inferior")); connect(toggleOrientationDecorationsAction, SIGNAL(toggled(bool)), this, SLOT(toggleOrientationDecorations(bool))); toggleScreenshotAction = new QAction(tr("&Toggle Screenshot Action"), this); toggleScreenshotAction->setCheckable(true); bool screenshotActionVisible = propertyObject->property(screenshotActionProperty->getName().toStdString().c_str()).toBool(); toggleScreenshotAction->setChecked(screenshotActionVisible); toggleScreenshotAction->setStatusTip(tr("Show/Hide the screenshot button in the side bar of the slice viewer.")); toggleScreenshotAction->setWhatsThis(tr("Show/Hide the screenshot button in the side bar of the slice viewer.")); connect(toggleScreenshotAction, SIGNAL(toggled(bool)), this, SLOT(setScreenshotAction(bool))); } if (myType == GEOMETRY_VIEWER) { toggleLinesAsTubesAction = new QAction(tr("Show Lines as Tubes"), this); toggleLinesAsTubesAction->setCheckable(true); toggleLinesAsTubesAction->setChecked(propertyObject->property(linesAsTubesProperty->getName().toStdString().c_str()).toBool()); toggleLinesAsTubesAction->setStatusTip(tr("Display the lines - if in a vtkDataSet - as tubes")); toggleLinesAsTubesAction->setWhatsThis(tr("Display the lines - if in a vtkDataSet - as tubes")); connect(toggleLinesAsTubesAction, SIGNAL(toggled(bool)), this, SLOT(setLinesAsTubes(bool))); toggleBackfaceCullingAction = new QAction(tr("&Backface Culling"), this); toggleBackfaceCullingAction->setCheckable(true); bool backfaceCulling = propertyObject->property(backfaceCullingProperty->getName().toStdString().c_str()).toBool(); toggleBackfaceCullingAction->setChecked(backfaceCulling); toggleBackfaceCullingAction->setStatusTip(tr("Set cull face on/off\nIf backface culling is on, polygons facing away from camera are not drawn.")); toggleBackfaceCullingAction->setWhatsThis(tr("Set cull face on/off\nIf backface culling is on, polygons facing away from camera are not drawn.")); connect(toggleBackfaceCullingAction, SIGNAL(toggled(bool)), this, SLOT(setBackfaceCulling(bool))); //--- actions of the picking menu auto* pickingGrp = new QActionGroup(this); connect(pickingGrp, SIGNAL(triggered(QAction*)), this, SLOT(pickingModeChanged(QAction*))); pickCellAction = new QAction(QPixmap(":/pick_cell"), tr("Pick &cell"), pickingGrp); pickCellAction->setCheckable(true); pickCellAction->setStatusTip(tr("Picking mode sets to pick cells")); pickCellAction->setWhatsThis(tr("Pick cell\n\nSets the picking mode to pick one or more cells\nUse Ctrl+Left clic to select a cell,\nEscape to reset selection")); pickPointAction = new QAction(QPixmap(":/pick_point"), tr("Pick &Points"), pickingGrp); pickPointAction->setCheckable(true); pickPointAction->setStatusTip(tr("Picking mode sets to pick points")); pickPointAction->setWhatsThis(tr("Pick points\n\nSets the picking mode to pick one or more points\nUse Ctrl+Left clic to select an point\n\nEscape to reset selection")); pickCellRegionAction = new QAction(QPixmap(":/pick_cell_region"), tr("Pick cell region"), pickingGrp); pickCellRegionAction->setCheckable(true); pickCellRegionAction->setStatusTip(tr("Picking mode set to pick cell region")); pickCellRegionAction->setWhatsThis(tr("Pick cells in rectangular region")); pickPointRegionAction = new QAction(QPixmap(":/pick_point_region"), tr("Pick point region"), pickingGrp); pickPointRegionAction->setCheckable(true); pickPointRegionAction->setStatusTip(tr("Picking mode set to pick point region")); pickPointRegionAction->setWhatsThis(tr("Pick points in rectangular region")); } } // ----------------------initWhatsThis ---------------------------- void InteractiveViewer::initWhatsThis() { QColor bgColor = frame->palette().color(QPalette::Button); QColor textColor = frame->palette().color(QPalette::ButtonText); whatsThis = "
3D view interaction Shortcuts

"; } // ----------------------startWhatsThisSection ---------------------------- void InteractiveViewer::startWhatsThisSection(const QString& title) { QColor bgColor = frame->palette().color(QPalette::Button); QColor textColor = frame->palette().color(QPalette::ButtonText); whatsThis += "
" + title + "
\ "; oddWhatsThis = true; } // ----------------------endWhatsThisSection ---------------------------- void InteractiveViewer::endWhatsThisSection() { whatsThis += "
\
"; } // ----------------------addWhatsThisItem ---------------------------- void InteractiveViewer::addWhatsThisItem(const QString& key, const QString& description) { if (oddWhatsThis) { QColor bgColor = frame->palette().color(QPalette::Midlight); whatsThis += ""; } else { whatsThis += ""; } oddWhatsThis = !oddWhatsThis; whatsThis += R"()" + key + "  \ " + description + "\ "; } // ----------------------keyPressEvent ---------------------------- void InteractiveViewer::keyPressEvent(QKeyEvent* e) { /// NOTE: /// /// PLEASE, PLEASE, PLEASE, PLEASE, PLEASE, PLEASE /// /// DO NOT FORGET TO ADD A NEW LINE IN THE "WHAT'S THIS" MESSAGE (see constructor) /// /// The call to methods startWhatsThisSection and addWhatsThisItem is a good comment line /// to add here (see below!). Please use the same order here than in the /// what's this help (by category, then by function, then by key name) /// /// (PLEASE) //-- Look at the key code switch (e->key()) { /// /// startWhatsThisSection("Keyboard bindings (upper or lower case)"); /// case Qt::Key_3: rendererWidget->toogle3DRedBlue(); rendererWidget->refresh(); break; case Qt::Key_A: // addWhatsThisItem("A", "Toggle view axes"); if (e->modifiers() == Qt::NoModifier /*&& myType == GEOMETRY_VIEWER*/) { toggleAxesAction->toggle(); } break; case Qt::Key_C: // addWhatsThisItem("C", "Toggle color scale"); if (e->modifiers() == Qt::NoModifier) { setColorScale(!rendererWidget->getColorScale()); refresh(this); } break; case Qt::Key_F: // addWhatsThisItem("F", "Toggle backface culling"); if (e->modifiers() == Qt::NoModifier) { rendererWidget->setBackfaceCulling(!rendererWidget->getBackfaceCulling()); } break; case Qt::Key_I: // addWhatsThisItem("I", "Toggle image interpolation on slices"); if (e->modifiers() == Qt::NoModifier) { toggleInterpolation(); } break; case Qt::Key_J: if (e->modifiers() == Qt::NoModifier) { // addWhatsThisItem("J", "Joystick interaction mode"); rendererWidget->setControlMode(RendererWidget::JOYSTICK); } break; case Qt::Key_L: // addWhatsThisItem("L", "Toggle view labels"); if (e->modifiers() == Qt::NoModifier && myType == GEOMETRY_VIEWER) { toggleLabelAction->toggle(); } else if (e->modifiers() == Qt::AltModifier) { // addWhatsThisItem("Alt+L", "Toggle light follows camera"); rendererWidget->setLightFollowCamera(!rendererWidget->getLightFollowCamera()); rendererWidget->refresh(); } break; break; case Qt::Key_P: // addWhatsThisItem("Alt+P", "Toggle point rendering"); if (e->modifiers() == Qt::AltModifier) { foreach (Component* comp, Application::getSelectedComponents()) { // check if the Component is to be displayed here if (comp->getVisibility(this) && comp->getRepresentation() == Component::GEOMETRY) { comp->setRenderingModes(comp->getRenderingModes() ^ InterfaceGeometry::Points); //XOR } } refresh(this); } else // addWhatsThisItem("P", "Picking action"); if (e->modifiers() == Qt::NoModifier) { rendererWidget->pick(); } break; case Qt::Key_R: if (myType == SLICE_VIEWER) { resetLUT(); } break; case Qt::Key_S: // addWhatsThisItem("Alt+S", "Toggle surface rendering"); if (e->modifiers() == Qt::AltModifier) { foreach (Component* comp, Application::getSelectedComponents()) { // check if the Component is to be displayed here if (comp->getVisibility(this) && comp->getRepresentation() == Component::GEOMETRY) { comp->setRenderingModes(comp->getRenderingModes() ^ InterfaceGeometry::Surface); //XOR } } refresh(this); } else if (e->modifiers() == Qt::NoModifier) { // addWhatsThisItem("S", "Take a screenshot"); screenshot(); } break; case Qt::Key_T: if (e->modifiers() == Qt::NoModifier) { // addWhatsThisItem("T", "Trackball interaction mode"); rendererWidget->setControlMode(RendererWidget::TRACKBALL); } break; case Qt::Key_W: // addWhatsThisItem("Alt+W", "Toggle wireframe rendering"); if (e->modifiers() == Qt::AltModifier) { foreach (Component* comp, Application::getSelectedComponents()) { // check if the Component is to be displayed here if (comp->getVisibility(this) && comp->getRepresentation() == Component::GEOMETRY) { comp->setRenderingModes(comp->getRenderingModes() ^ InterfaceGeometry::Wireframe); //XOR } } refresh(this); } break; /// /// startWhatsThisSection(); /// case Qt::Key_Space: // addWhatsThisItem("SPACE", "Update/refresh view"); refresh(this); break; case Qt::Key_Home: // addWhatsThisItem("HOME", "Reset camera to default view point or so that everything is visible"); resetCamera(); break; case Qt::Key_Left: if (myType == GEOMETRY_VIEWER) { if (e->modifiers() == Qt::ControlModifier) // addWhatsThisItem("Ctrl+LEFT", "Turn -90° around camera Y axis"); { rendererWidget->rotateCamera(-90, 1); } else // addWhatsThisItem("LEFT", "Turn -5° around camera Y axis"); { rendererWidget->rotateCamera(-5, 1); } } break; case Qt::Key_Right: if (myType == GEOMETRY_VIEWER) { if (e->modifiers() == Qt::ControlModifier) // addWhatsThisItem("Ctrl+RIGHT", "Turn 90° around camera Y axis"); { rendererWidget->rotateCamera(90, 1); } else // addWhatsThisItem("Ctrl+LEFT", "Turn -90° around camera Y axis"); { rendererWidget->rotateCamera(5, 1); } } break; case Qt::Key_Up: if (myType == GEOMETRY_VIEWER) { if (e->modifiers() == Qt::ControlModifier) // addWhatsThisItem("Ctrl+UP", "Turn -90° around camera X axis"); { rendererWidget->rotateCamera(-90, 0); } else // addWhatsThisItem("UP", "Turn -5° around camera X axis"); { rendererWidget->rotateCamera(-5, 0); } } break; case Qt::Key_Down: if (myType == GEOMETRY_VIEWER) { if (e->modifiers() == Qt::ControlModifier) // addWhatsThisItem("Ctrl+DOWN", "Turn 90° around camera X axis"); { rendererWidget->rotateCamera(90, 0); } else // addWhatsThisItem("DOWN", "Turn 5° around camera X axis"); { rendererWidget->rotateCamera(5, 0); } } break; case Qt::Key_Plus: // addWhatsThisItem("+", "Move slider a step above"); if (myType == SLICE_VIEWER) { sliceSlider->addSingleStep(); } break; case Qt::Key_Minus: // addWhatsThisItem("-", "Move slider a step below"); if (myType == SLICE_VIEWER) { sliceSlider->subSingleStep(); } break; case Qt::Key_PageUp: // addWhatsThisItem("PAGE Up", "Move slider a page above"); if (myType == SLICE_VIEWER) { sliceSlider->addPageStep(); } break; case Qt::Key_PageDown: // addWhatsThisItem("PAGE Down", "Move slider a page down"); if (myType == SLICE_VIEWER) { sliceSlider->subPageStep(); } break; case Qt::Key_Escape: { // addWhatsThisItem("ESC", "Clear current selection"); clearSelection(); refresh(this); } break; /// /// startWhatsThisSection("Other Shortcuts"); /// case Qt::Key_F1: if (e->modifiers() == Qt::NoModifier) { //addWhatsThisItem("Shift+F1", "Show this help (i.e. the what's this help)"); QWhatsThis::showText(QCursor::pos(), whatsThis); } break; case Qt::Key_F2: if (e->modifiers() == Qt::NoModifier) { // addWhatsThisItem("F2", "Print debugging information on console"); QString debugString; QTextStream debugStream(&debugString); debugStream << "=== F2 pressed (debug information) ===" << endl; debugStream << "InteractiveViewer: \"" << getName() << "\" (total nr of displayed actors: " << actorMap.size() << ")" << endl; debugStream << endl; double position[3]; double focalPoint[3]; double viewUp[3]; rendererWidget->getCameraSettings(position, focalPoint, viewUp); debugStream << "Camera Position: (" << position[0] << "," << position[1] << "," << position[2] << ")" << endl; debugStream << "Camera Focal Point: (" << focalPoint[0] << "," << focalPoint[1] << "," << focalPoint[2] << ")" << endl; debugStream << "Camera Up Direction: (" << viewUp[0] << "," << viewUp[1] << "," << viewUp[2] << ")" << endl; debugStream << endl; double bounds[6]; rendererWidget->computeVisiblePropBounds(bounds); debugStream << "Bounds Of All Components: xmin=" << bounds[0] << " xmax=" << bounds[1] << " ymin=" << bounds[2] << " ymax=" << bounds[3] << " zmin=" << bounds[4] << " zmax=" << bounds[5] << endl; getBoundsOfSelected(bounds); debugStream << "Bounds Of Selected Components: xmin=" << bounds[0] << " xmax=" << bounds[1] << " ymin=" << bounds[2] << " ymax=" << bounds[3] << " zmin=" << bounds[4] << " zmax=" << bounds[5] << endl; debugStream << endl; debugStream << "== Component vtkProp ==" << endl; foreach (Component* c, actorMap.keys().toSet()) { debugStream << "- Component: \"" << c->getName() << "\" of type \"" << c->metaObject()->className() << "\" has " << actorMap.values(c).size() << " vtkProp:" << endl; std::list > allActors = actorMap.values(c).toStdList(); allActors.sort(); allActors.unique(); foreach (vtkSmartPointer p, allActors) { // get more information by comparing with the getActor/getProp method of c bool found = false; switch (myType) { case GEOMETRY_VIEWER: if (c->getActor(InterfaceGeometry::Surface) == p) { found = true; debugStream << "\t- Surface Actor" << endl; } else if (c->getActor(InterfaceGeometry::Wireframe) == p) { found = true; debugStream << "\t- Wireframe Actor" << endl; } else if (c->getActor(InterfaceGeometry::Points) == p) { found = true; debugStream << "\t- Points Actor" << endl; } break; case SLICE_VIEWER: if (c->getPixelActor() == p) { found = true; debugStream << "\t- Pixel Actor" << endl; } else if (c->get2DImageActor() == p) { found = true; debugStream << "\t- 2D Image Actor" << endl; } else if (c->getPickPlaneActor() == p) { found = true; debugStream << "\t- Picked Plane Actor" << endl; } break; default: // no other thing is possible break; } // check additional prop if (!found) { unsigned int i = 0; while (i < c->getNumberOfProp() && c->getProp(i) != p) { i++; } if (i == c->getNumberOfProp()) { debugStream << "\t- Unknown Prop" << endl; } else { debugStream << "\t- Additional Prop #" << i << endl; } } } } debugStream << "=== (end of debug information) ===" << endl; CAMITK_INFO(tr("InteractiveViewer Debug Information: ").arg(debugString)) } break; /// NOTE: /// /// PLEASE, PLEASE, PLEASE, PLEASE, PLEASE, PLEASE? /// /// DO NOT FORGET TO ADD A NEW LINE IN THE "WHAT'S THIS" MESSAGE (see constructor) /// /// The call to methods startWhatsThisSection and addWhatsThisItem is a good comment line /// to add here (see below!). Please use the same order here than in the /// what's this help (by category, then by function, then by key name) /// /// (PLEASE) default: // If not an interesting key, break; } } //---------------------resetCamera------------------------ void InteractiveViewer::resetCamera() { if (myType == SLICE_VIEWER && actorMap.size() >= 1) { double bounds[6]; // get the first Component to determine the right bounds actorMap.begin().key()->get2DImageActor()->GetBounds(bounds); // scale correctly for square objects like a slice // instead of a bounding box, it transforms the bounds in a enclosed // box, thus the radius used in the VTK ResetCamera is now smaller // and correspond to the longest side of the slice devided by 2.0 // => VTK is "tricked" double max = 0.0; double radius = 0.0; double center[3]; double halfDist[3]; for (unsigned int i = 0; i < 3; i++) { halfDist[i] = (bounds[i * 2 + 1] - bounds[i * 2]) / 2.0; center[i] = bounds[i * 2] + halfDist[i]; radius += halfDist[i] * halfDist[i]; if (halfDist[i] > max) { max = halfDist[i]; } } radius = sqrt(radius); for (unsigned int i = 0; i < 3; i++) { // rescale halfDist[i] *= max / radius; // recompute bounds bounds[i * 2] = center[i] - halfDist[i]; bounds[i * 2 + 1] = center[i] + halfDist[i]; } rendererWidget->resetCamera(bounds); } else // automatically set the camera so that everything is visible { rendererWidget->resetCamera(); } } // ---------------------- setActiveCamera ---------------------------- void InteractiveViewer::setActiveCamera(QString cameraName) { if (cameraMap.contains(cameraName)) { rendererWidget->setActiveCamera(cameraMap.value(cameraName)); } } // ---------------------- getCamera ---------------------------- vtkSmartPointer InteractiveViewer::getCamera(QString cameraName) { if (cameraMap.contains(cameraName)) { return cameraMap.value(cameraName); } else { // create a new camera vtkSmartPointer newCam = vtkSmartPointer::New(); // associate it with the given name cameraMap.insert(cameraName, newCam); return newCam; } } // ---------------------- slotSliderChanged ---------------------------- void InteractiveViewer::sliderChanged(int i) { if (myType == SLICE_VIEWER) { if (actorMap.size() > 0) { // current interaction changed isChangingSlice = true; actorMap.begin().key()->setSlice(i); // the Component was modified in the viewer, ask the Component to update all its other viewers actorMap.begin().key()->refresh(); // user has changed the slide, the selection has changed then emit selectionChanged(); // current interaction is processed isChangingSlice = false; } } } // ---------------------- slotXAngleChanged ---------------------------- void InteractiveViewer::xAngleChanged(double angle) { if (myType == SLICE_VIEWER) { if (actorMap.size() > 0) { //TODO implement this method for the arbitrary slice actorMap.begin().key()->rotate(angle, 0, 0); // the Component was modified in the viewer, ask the Component to update all its other viewers actorMap.begin().key()->refresh(); } } } // ---------------------- slotYAngleChanged ---------------------------- void InteractiveViewer::yAngleChanged(double angle) { if (myType == SLICE_VIEWER) { if (actorMap.size() > 0) { //TODO implement this method for the arbitrary slice actorMap.begin().key()->rotate(0, angle, 0); // the Component was modified in the viewer, ask the Component to update all its other viewers actorMap.begin().key()->refresh(); } } } // ---------------------- slotZAngleChanged ---------------------------- void InteractiveViewer::zAngleChanged(double angle) { if (myType == SLICE_VIEWER) { if (actorMap.size() > 0) { //TODO implement this method for the arbitrary slice actorMap.begin().key()->rotate(0, 0, angle); // the Component was modified in the viewer, ask the Component to update all its other viewers actorMap.begin().key()->refresh(); } } } //--------------------- setGradientBackground -------------- void InteractiveViewer::setGradientBackground(bool g) { propertyObject->setProperty(backgroundGradientColorProperty->getName().toStdString().c_str(), g); } // --------------- setBackgroundColor -------------------- void InteractiveViewer::setBackgroundColor(QColor c) { propertyObject->setProperty(backgroundColorProperty->getName().toStdString().c_str(), c); } //--------------------- getBounds ------------------- void InteractiveViewer::getBounds(double* bounds) { rendererWidget->computeVisiblePropBounds(bounds); } //--------------------- getBoundsOfSelected ------------------- void InteractiveViewer::getBoundsOfSelected(double* bounds) { // booleen which allows one to know if at least 1 element is selected bool oneElementSelected = false; // init the bound to have the bigger box for (unsigned int i = 0; i < 6; i += 2) { bounds[i] = VTK_DOUBLE_MAX; bounds[i + 1] = -VTK_DOUBLE_MAX; } // check the bound of all selected Component in the InteractiveViewer foreach (Component* comp, actorMap.keys()) { if (comp->isSelected()) { //if we are in this loop that is 1 element at least is selected then oneElementSelected = true; // get the bounds double compBounds[6]; comp->getBounds(compBounds); // check against min/max for (unsigned int i = 0; i < 6; i += 2) { if (compBounds[i] < bounds[i]) { bounds[i] = compBounds[i]; } if (compBounds[i + 1] > bounds[i + 1]) { bounds[i + 1] = compBounds[i + 1]; } } } } // if nothing element is selected then we take the bb at 0,1,0,1,0,1 if (!oneElementSelected) for (unsigned int i = 0; i < 6; i += 2) { bounds[i] = 0; bounds[i + 1] = 1; } } //------------------------- setSideFrameVisible ---------------------------- void InteractiveViewer::setSideFrameVisible(bool visibility) { if (sideFrame) { sideFrame->setVisible(visibility); } } //------------------------- toggleInterpolation ---------------------------- void InteractiveViewer::toggleInterpolation() { foreach (Component* comp, actorMap.keys()) { if (comp->getRepresentation() == Component::SLICE) { bool state = comp->get2DImageActor()->GetInterpolate(); comp->get2DImageActor()->SetInterpolate(!state); state = comp->get3DImageActor()->GetInterpolate(); comp->get3DImageActor()->SetInterpolate(!state); // the Component was modified inside the viewer, refresh all its other viewer comp->refresh(); } } } //------------------------- resetLUT ---------------------------- void InteractiveViewer::resetLUT() { foreach (Component* comp, actorMap.keys()) { if (comp->getRepresentation() == Component::SLICE) { // reset property LUT vtkSmartPointer initProp = vtkImageProperty::New(); initProp->SetInterpolationTypeToLinear(); initProp->SetAmbient(1.0); initProp->SetDiffuse(0.0); comp->get2DImageActor()->SetProperty(initProp); // the Component was modified inside the viewer, refresh all its other viewer comp->refresh(); } } } //---------------------updateSelectionDisplay------------------------ void InteractiveViewer::updateSelectionDisplay(Component* comp) { InterfaceGeometry::EnhancedModes m = InterfaceGeometry::Normal; switch ((HighlightMode) propertyObject->property(highlightModeProperty->getName().toStdString().c_str()).toInt()) { //set the option for the item(s) not selected case InteractiveViewer::SELECTION: // nobody is highlighted // shade if not selected if (!comp->isSelected()) { m = InterfaceGeometry::Shaded; } break; case InteractiveViewer::SELECTION_ONLY: // nobody is highlighted, if (!comp->isSelected()) { // hide if not selected m = InterfaceGeometry::Hidden; } break; default: break; } comp->setEnhancedModes(m); } // ---------------------- initPicking ---------------------------- void InteractiveViewer::initPicking(PickingMode pickingMode) { this->pickingMode = pickingMode; switch (pickingMode) { case PIXEL_PICKING: { vtkSmartPointer< vtkPointPicker > picker = vtkSmartPointer::New(); picker->SetTolerance(0.01); rendererWidget->setPicker(picker); break; } case POINT_PICKING: { vtkSmartPointer< vtkPointPicker > picker = vtkSmartPointer< vtkPointPicker >::New(); picker->SetTolerance(0.01); this->rendererWidget->setPicker(picker); this->rendererWidget->setAreaPicking(false); break; } case CELL_PICKING: { vtkSmartPointer< vtkCellPicker > picker = vtkSmartPointer< vtkCellPicker >::New(); picker->SetTolerance(0.0001); this->rendererWidget->setPicker(picker); this->rendererWidget->setAreaPicking(false); break; } case AREA_CELL_PICKING: { vtkSmartPointer< vtkRenderedAreaPicker > picker = vtkSmartPointer< vtkRenderedAreaPicker >::New(); this->rendererWidget->setPicker(picker); this->rendererWidget->setAreaPicking(true); break; } case AREA_POINT_PICKING: { vtkSmartPointer< vtkRenderedAreaPicker > picker = vtkSmartPointer< vtkRenderedAreaPicker >::New(); this->rendererWidget->setPicker(picker); this->rendererWidget->setAreaPicking(true); break; } default: case NO_PICKING: rendererWidget->setPicker(nullptr); break; } // picking effect no yet decided pickingEffectUpdated = true; } //------------------------- screenshot ---------------------------- void InteractiveViewer::screenshot() { //-- build list of supported format QString filter("All supported image formats("); for (unsigned int i = 0; i < RendererWidget::NOT_SUPPORTED; i++) { filter += " *." + RendererWidget::getScreenshotFormatInfo(i)->extension; } filter += QString(");;"); // also add each entry individually for (unsigned int i = 0; i < RendererWidget::NOT_SUPPORTED; i++) { filter += RendererWidget::getScreenshotFormatInfo(i)->description + "(*." + RendererWidget::getScreenshotFormatInfo(i)->extension + ");;"; } // ask the user QString filename = QFileDialog::getSaveFileName(nullptr, tr("Save Screenshot As.."), "", filter); if (!filename.isEmpty()) { screenshot(filename); } } void InteractiveViewer::screenshot(QString filename) { // refresh the screen after the screenshot (so that the dialog does not appear in the screenshot! refresh(this); // take the snapshot rendererWidget->screenshot(filename); } //------------------------- getColorScale ---------------------------- bool InteractiveViewer::getColorScale() const { return rendererWidget->getColorScale(); } //------------------------- setColorScale ---------------------------- void InteractiveViewer::setColorScale(bool state) { rendererWidget->setColorScale(state); } //------------------------- setColorScaleMinMax ---------------------------- void InteractiveViewer::setColorScaleMinMax(double min, double max) { rendererWidget->setColorScaleMinMax(min, max); } //------------------------- setColorScaleTitle ---------------------------- void InteractiveViewer::setColorScaleTitle(QString title) { rendererWidget->setColorScaleTitle(title); } // ------------- updateActions ----------------- void InteractiveViewer::updateActions() { if (myType == GEOMETRY_VIEWER) { // block signals surfaceAction->blockSignals(true); wireframeAction->blockSignals(true); pointsAction->blockSignals(true); glyphAction->blockSignals(true); toggleLabelAction->blockSignals(true); scalarDataColorAction->blockSignals(true); surfaceAction->setEnabled(true); wireframeAction->setEnabled(true); pointsAction->setEnabled(true); glyphAction->setEnabled(true); toggleLabelAction->setEnabled(true); scalarDataColorAction->setEnabled(false); if (colorAction) { colorAction->setEnabled(true); } // update the rendering mode buttons switch (Application::getSelectedComponents().size()) { case 0: surfaceAction->setEnabled(false); wireframeAction->setEnabled(false); pointsAction->setEnabled(false); glyphAction->setEnabled(false); toggleLabelAction->setEnabled(false); if (colorAction) { colorAction->setEnabled(false); } break; case 1: { Component* first = Application::getSelectedComponents().first(); if (first->getVisibility(this) && first->getRepresentation() == Component::GEOMETRY) { surfaceAction->setChecked(first->getRenderingModes() & InterfaceGeometry::Surface); wireframeAction->setChecked(first->getRenderingModes() & InterfaceGeometry::Wireframe); pointsAction->setChecked(first->getRenderingModes() & InterfaceGeometry::Points); glyphAction->setChecked(Application::getSelectedComponents().first()->getProp("glyph")->GetVisibility()); toggleLabelAction->setChecked(Application::getSelectedComponents().first()->getProp("label")->GetVisibility()); auto* mesh = dynamic_cast< MeshComponent* >(first); if (mesh) { // active scalar data colors and populate combobox with scalar data from the component scalarDataColorAction->setEnabled(true); scalarDataModel->setSourceModel(mesh->getDataModel()); } } else { surfaceAction->setEnabled(false); wireframeAction->setEnabled(false); pointsAction->setEnabled(false); glyphAction->setEnabled(false); toggleLabelAction->setEnabled(false); if (colorAction) { colorAction->setEnabled(false); } } break; } default: { // they should all have the same rendering mode to be able to determine a default state InterfaceGeometry::RenderingModes m = Application::getSelectedComponents() [0]->getRenderingModes(); int i = 1; while (i < Application::getSelectedComponents().size() && (m == Application::getSelectedComponents() [i]->getRenderingModes())) { i++; } if (i == Application::getSelectedComponents().size()) { surfaceAction->setChecked(m & InterfaceGeometry::Surface); wireframeAction->setChecked(m & InterfaceGeometry::Wireframe); pointsAction->setChecked(m & InterfaceGeometry::Points); } else { surfaceAction->setChecked(false); pointsAction->setChecked(false); wireframeAction->setChecked(false); } Component* first = Application::getSelectedComponents().first(); if (first->getVisibility(this) && first->getRepresentation() == Component::GEOMETRY) { glyphAction->setChecked(first->getProp("glyph")->GetVisibility()); toggleLabelAction->setChecked(first->getProp("label")->GetVisibility()); } break; } } // unblock signals surfaceAction->blockSignals(false); wireframeAction->blockSignals(false); pointsAction->blockSignals(false); glyphAction->blockSignals(false); toggleLabelAction->blockSignals(false); scalarDataColorAction->blockSignals(false); if (viewerMenu) { renderingMenu->setEnabled(Application::getSelectedComponents().size() > 0); } } } // ------------- slotRenderingActorsChanged ----------------- void InteractiveViewer::renderingActorsChanged() { InterfaceGeometry::RenderingModes m = InterfaceGeometry::None; if (surfaceAction->isChecked()) { m |= InterfaceGeometry::Surface; } if (wireframeAction->isChecked()) { m |= InterfaceGeometry::Wireframe; } if (pointsAction->isChecked()) { m |= InterfaceGeometry::Points; } // update the rendering mode of selected foreach (Component* comp, Application::getSelectedComponents()) { comp->setRenderingModes(m); } refresh(this); } // --------------- setHighlightMode -------------------- void InteractiveViewer::setHighlightMode(InteractiveViewer::HighlightMode h) { HighlightMode highlightMode = (HighlightMode) propertyObject->property(highlightModeProperty->getName().toStdString().c_str()).toInt(); refresh(this); QSettings& settings = Application::getSettings(); settings.beginGroup(Application::getName() + ".InteractiveViewer." + objectName().simplified().replace(" ", "")); settings.setValue("highlightMode", highlightMode); settings.endGroup(); } //// --------------- getHighlightMode -------------------- //InteractiveViewer::HighlightMode InteractiveViewer::getHighlightMode() const { // return highlightMode; //} // --------------- slotHighlightModeChanged ----------------------------------- void InteractiveViewer::highlightModeChanged(QAction* selectedAction) { if (selectedAction == highlightSelectionAction) { propertyObject->setProperty(highlightModeProperty->getName().toStdString().c_str(), InteractiveViewer::SELECTION); } else if (selectedAction == highlightSelectionOnlyAction) { propertyObject->setProperty(highlightModeProperty->getName().toStdString().c_str(), InteractiveViewer::SELECTION_ONLY); } else /* if (selectedAction == highlightModeXtraHotAction) { highlightMode = InteractiveViewer::XTRA_HOT; } else */ if (selectedAction == highlightOffAction) { propertyObject->setProperty(highlightModeProperty->getName().toStdString().c_str(), InteractiveViewer::OFF); } refresh(this); } // --------------- slotCameraOrientationChanged ----------------------------------- void InteractiveViewer::cameraOrientationChanged(QAction* selectedAction) { if (selectedAction == cameraOrientationRightDownAction) { rendererWidget->setCameraOrientation(RendererWidget::RIGHT_DOWN); resetCamera(); rendererWidget->refresh(); } else if (selectedAction == cameraOrientationLeftUpAction) { rendererWidget->setCameraOrientation(RendererWidget::LEFT_UP); resetCamera(); rendererWidget->refresh(); } else if (selectedAction == cameraOrientationRightUpAction) { rendererWidget->setCameraOrientation(RendererWidget::RIGHT_UP); resetCamera(); rendererWidget->refresh(); } QSettings& settings = Application::getSettings(); settings.beginGroup(Application::getName() + ".InteractiveViewer." + objectName().simplified().replace(" ", "")); settings.setValue("cameraOrientation", rendererWidget->getCameraOrientation()); refresh(this); settings.endGroup(); } // -------------- slotViewControlModeChanged -------------- void InteractiveViewer::viewControlModeChanged(QAction* selectedAction) { if (selectedAction == controlModeTrackballAction) { rendererWidget->setControlMode(RendererWidget::TRACKBALL); } else { rendererWidget->setControlMode(RendererWidget::JOYSTICK); } } // ------------- slotBackgroundColor ----------------- void InteractiveViewer::backgroundColor() { QColor oldColor = propertyObject->property(backgroundColorProperty->getName().toStdString().c_str()).value(); QColor newColor = QColorDialog::getColor(oldColor); if (newColor.isValid()) { propertyObject->setProperty(backgroundColorProperty->getName().toStdString().c_str(), newColor); } } // ------------- slotToggleAxes ----------------- void InteractiveViewer::toggleAxes(bool f) { rendererWidget->toggleAxes(f); resetCamera(); rendererWidget->refresh(); } // ------------- toggleOrientationDecorations ----------------- void InteractiveViewer::toggleOrientationDecorations(bool f) { if (myType == SLICE_VIEWER) { rendererWidget->toggleOrientationDecorations(f); rendererWidget->refresh(); } } // ------------- slotToggleCopyright ----------------- void InteractiveViewer::toggleCopyright(bool c) { rendererWidget->toggleCopyright(c); rendererWidget->refresh(); } // ------------- setLabel ----------------- void InteractiveViewer::setLabel(bool b) { // update the rendering mode of selected foreach (Component* comp, Application::getSelectedComponents()) { if (comp->getVisibility(this) && comp->getRepresentation() == Component::GEOMETRY) { comp->getProp("label")->SetVisibility(b); } } refresh(this); } // ------------- setGlyph ----------------- void InteractiveViewer::setGlyph(bool b) { // update the rendering mode of selected foreach (Component* comp, Application::getSelectedComponents()) { if (comp->getVisibility(this) && comp->getRepresentation() == Component::GEOMETRY) { comp->getProp("glyph")->SetVisibility(b); } } refresh(this); } // ------------- setLinesAsTubes ----------------- void InteractiveViewer::setLinesAsTubes(bool tubes) { propertyObject->setProperty(linesAsTubesProperty->getName().toStdString().c_str(), tubes); } // -------------- setBackfaceCulling -------------- void InteractiveViewer::setBackfaceCulling(bool b) { propertyObject->setProperty(backfaceCullingProperty->getName().toStdString().c_str(), b); } // -------------- setScreenshotAction -------------- void InteractiveViewer::setScreenshotAction(bool b) { propertyObject->setProperty(screenshotActionProperty->getName().toStdString().c_str(), b); if (screenshotActionMenu) { screenshotActionMenu->setVisible(b); } } //-------------------- pickingModeChanged --------------------- void InteractiveViewer::pickingModeChanged(QAction* selectedAction) { if (selectedAction == pickCellAction) { initPicking(CELL_PICKING); } else if (selectedAction == pickPointAction) { initPicking(POINT_PICKING); } else if (selectedAction == pickCellRegionAction) { initPicking(AREA_CELL_PICKING); } else if (selectedAction == pickPointRegionAction) { initPicking(AREA_POINT_PICKING); } } //-------------------- picked --------------------- void InteractiveViewer::picked() { vtkSmartPointer picker = vtkAbstractPropPicker::SafeDownCast(this->rendererWidget->GetInteractor()->GetPicker()); if (!picker) { pickingEffectUpdated = false; return; } // get the Component that owns the picked actor Component* comp = actorMap.key(picker->GetProp3D()); // if it is not a prop picker or if the selected actor is not a component one then good bye ! if (!comp || (pickingMode == NO_PICKING)) { return; } vtkSmartPointer itemPicker; if (!pickingEffectUpdated && (itemPicker = vtkPicker::SafeDownCast(picker))) { pickingEffectUpdated = true; // decide of the state: if one of the possible picked is selected, then try unselect bool hasSelected = false; int i = 0; while (i < itemPicker->GetProp3Ds()->GetNumberOfItems() && !hasSelected) { vtkSmartPointer actor = vtkActor::SafeDownCast(itemPicker->GetProp3Ds()->GetItemAsObject(i)); Component* cpt = actorMap.key(actor); hasSelected = (cpt && cpt->isSelected()); i++; } pickingEffectIsSelecting = !hasSelected; } switch (pickingMode) { case PIXEL_PICKING: { vtkSmartPointer pixelPicker = vtkPicker::SafeDownCast(picker); //-- get the points picked vtkSmartPointer pickedPlanePts = pixelPicker->GetPickedPositions(); // Looking for the intersection point that as a zero coordinate in the Z direction. int pointIndex = 0; for (int i = 0 ; i < pickedPlanePts->GetNumberOfPoints(); i++) { if (pickedPlanePts->GetPoint(i) [2] == 0.0) { pointIndex = i; } } if (pickedPlanePts != nullptr && pickedPlanePts->GetNumberOfPoints() > 0) { // picking worked isPicking = true; //-- tells the Component it's been picked in (x,y,z) comp->pixelPicked(pickedPlanePts->GetPoint(pointIndex) [0], pickedPlanePts->GetPoint(pointIndex) [1], pickedPlanePts->GetPoint(pointIndex) [2]); refresh(this); // picking is a kind of selection, but for pixel picking, only one Component is allowed at a time selectionChanged(comp); } } break; case POINT_PICKING: { vtkSmartPointer pointPicker = vtkPointPicker::SafeDownCast(picker); if (pointPicker) { vtkIdType currentId = pointPicker->GetPointId(); if (currentId != -1) { vtkSmartPointer ids = vtkSmartPointer::New(); ids->InsertNextValue(currentId); // tell the component it has been picked comp->pointPicked(currentId, pickingEffectIsSelecting); // show informations on the status bar double* pickedPos; pickedPos = pointPicker->GetPickPosition(); Application::showStatusBarMessage("Picked : " + comp->getName() + ", Point#" + QString::number(currentId) + ", position =(" + QString::number(pickedPos[0]) + "," + QString::number(pickedPos[1]) + "," + QString::number(pickedPos[2]) + ")"); // pointPicked can have changed something auto* mesh = dynamic_cast(comp); if (mesh) { mesh->addToSelectedSelection(vtkSelectionNode::POINT, vtkSelectionNode::INDICES, ids); } refresh(this); emit selectionChanged(); } } } break; case CELL_PICKING : { vtkSmartPointer cellPicker = vtkCellPicker::SafeDownCast(this->rendererWidget->GetInteractor()->GetPicker()); auto* mesh = dynamic_cast(comp); if (cellPicker && mesh) { vtkIdType currentId = cellPicker->GetCellId(); if (currentId != -1) { vtkSmartPointer ids = vtkSmartPointer::New(); ids->InsertNextValue(currentId); // tell the component it has been picked comp->cellPicked(currentId, pickingEffectIsSelecting); Application::showStatusBarMessage("Picked : " + comp->getName() + ", Cell#" + QString::number(currentId)); // cellPicked can have change something mesh->addToSelectedSelection(vtkSelectionNode::CELL, vtkSelectionNode::INDICES, ids); refresh(this); emit selectionChanged(); } } } break; case AREA_CELL_PICKING : { vtkSmartPointer areaPicker = vtkRenderedAreaPicker::SafeDownCast(this->rendererWidget->GetInteractor()->GetPicker()); auto* mesh = dynamic_cast(comp); if (areaPicker && mesh) { vtkSmartPointer extractor = vtkSmartPointer::New(); extractor->SetInputData(mesh->getPointSet()); extractor->PreserveTopologyOff(); extractor->SetFrustum(areaPicker->GetFrustum()); extractor->Update(); vtkDataSet* dataSet = vtkDataSet::SafeDownCast(extractor->GetOutput()); if (dataSet) { Application::showStatusBarMessage("Picked : " + comp->getName() + " (" + QString::number(dataSet->GetCellData()->GetArray(0)->GetNumberOfTuples()) + " cells )"); mesh->addToSelectedSelection(vtkSelectionNode::CELL, vtkSelectionNode::INDICES, dataSet->GetCellData()->GetArray(0)); refresh(this); emit selectionChanged(); } } } break; case AREA_POINT_PICKING : { vtkSmartPointer areaPicker = vtkRenderedAreaPicker::SafeDownCast(this->rendererWidget->GetInteractor()->GetPicker()); auto* mesh = dynamic_cast(comp); if (areaPicker && mesh) { vtkSmartPointer extractor = vtkSmartPointer::New(); extractor->SetInputData(mesh->getPointSet()); extractor->PreserveTopologyOff(); extractor->SetFrustum(areaPicker->GetFrustum()); extractor->SetFieldType(vtkSelection::POINT); extractor->Update(); vtkDataSet* dataSet = vtkDataSet::SafeDownCast(extractor->GetOutput()); if (dataSet) { Application::showStatusBarMessage("Picked : " + comp->getName() + " (" + QString::number(dataSet->GetPointData()->GetArray(0)->GetNumberOfTuples()) + " points )"); mesh->addToSelectedSelection(vtkSelectionNode::POINT, vtkSelectionNode::INDICES, dataSet->GetPointData()->GetArray(0)); refresh(this); emit selectionChanged(); } } } break; case NO_PICKING : default : break; } isPicking = false; comp = nullptr; } //-------------------- rightClick --------------------- void InteractiveViewer::rightClick() { if (QApplication::keyboardModifiers() == Qt::ControlModifier) { // display the popup menu getMenu()->popup(QCursor::pos()); // generate a synthetic event to tell the renderer the right button was released // see Qt Quarterly Issue 11 ยท Q3 2004 QApplication::postEvent(rendererWidget, new QMouseEvent(QEvent::MouseButtonRelease, QCursor::pos(), Qt::RightButton, Qt::RightButton, Qt::ControlModifier)); } } //-------------------- createProperties --------------------- void InteractiveViewer::createProperties() { propertyObject = new PropertyObject(getName()); // 3D Viewer properties // highlight mode highlightModeProperty = new Property("Highlight Mode", InteractiveViewer::SELECTION, "Select the highlight mode for selecting components. \n OFF: both selected and non-selected Components are in default mode \n SELECTION: the selected Components are in default mode, the non-selected Components are shaded \n ELECTION_ONLY: the selected Components are in default mode, the non-selected are hidden", ""); highlightModeProperty->setEnumTypeName("camitk::InteractiveViewer::HighlightMode"); QStringList highlightModeNames; highlightModeNames << "Off" << "Selection" << "Selection only"; highlightModeProperty->setAttribute("enumNames", highlightModeNames); propertyObject->addProperty(highlightModeProperty); // background color backgroundColorProperty = new Property("Background color", QColor(255, 255, 255), "The background color of this viewer.", ""); propertyObject->addProperty(backgroundColorProperty); // background color property backgroundGradientColorProperty = new Property("Use a background gradient color?", true, "Does this viewer use a gradient to render its background color?", ""); propertyObject->addProperty(backgroundGradientColorProperty); // lines as tubes property linesAsTubesProperty = new Property("Lines considered as tube?", false, "Does this viewer replace drawn lines as tubes?", ""); propertyObject->addProperty(linesAsTubesProperty); // screenshot action property screenshotActionProperty = new Property("Screenshot button visible", false, "Is the screenshot button visible in the slice viewer sidebar", ""); propertyObject->addProperty(screenshotActionProperty); // backface culling property backfaceCullingProperty = new Property("Backface culling", false, "Compute the non visible polygons and display them?", ""); propertyObject->addProperty(backfaceCullingProperty); // point size property pointSizeProperty = new Property("Point size?", 4.0, "The 3D point size of each node", ""); propertyObject->addProperty(pointSizeProperty); // TODO BlockZOOM // backface culling property //backfaceCullingProperty = new Property("Backface culling", false, "Compute the non visible polygons and display them?", ""); //propertyObject->addProperty(backfaceCullingProperty); } // ---------------- eventFilter ---------------- bool InteractiveViewer::eventFilter(QObject* object, QEvent* event) { // watch propertyObject instance for dynamic property changes if (event->type() == QEvent::DynamicPropertyChange) { // first perform the proper action // and then save the settings accordingly // highlight mode update HighlightMode highlightMode = (HighlightMode) propertyObject->property(highlightModeProperty->getName().toStdString().c_str()).toInt(); // background color update QColor backgroundColor = propertyObject->property(backgroundColorProperty->getName().toStdString().c_str()).value(); rendererWidget->setBackgroundColor(backgroundColor.redF(), backgroundColor.greenF(), backgroundColor.blueF()); // background gradient color bool useGradientBackgroundColor = propertyObject->property(backgroundGradientColorProperty->getName().toStdString().c_str()).toBool(); rendererWidget->setGradientBackground(useGradientBackgroundColor); // lines as tubes bool linesAsTubes = propertyObject->property(linesAsTubesProperty->getName().toStdString().c_str()).toBool(); // backface culling bool backfaceCulling = propertyObject->property(backfaceCullingProperty->getName().toStdString().c_str()).toBool(); rendererWidget->setBackfaceCulling(backfaceCulling); // screenshot action bool screenshotActionVisible = propertyObject->property(screenshotActionProperty->getName().toStdString().c_str()).toBool(); // point size double pointSize = propertyObject->property(pointSizeProperty->getName().toStdString().c_str()).toDouble(); rendererWidget->setPointSize(pointSize); // ..and finally refresh refresh(this); rendererWidget->refresh(); // save the settings QSettings& settings = Application::getSettings(); settings.beginGroup(Application::getName() + ".InteractiveViewer." + objectName().simplified().replace(" ", "")); settings.setValue("highlightMode", highlightMode); settings.setValue("backgroundColor", backgroundColor.name()); settings.setValue("gradientBackground", useGradientBackgroundColor); settings.setValue("linesAsTubes", linesAsTubes); settings.setValue("backfaceCulling", backfaceCulling); settings.setValue("screenshotActionVisible", screenshotActionVisible); settings.setValue("pointSize", pointSize); settings.endGroup(); return true; } else { // otherwise pass the event on to the parent class return Viewer::eventFilter(object, event); } } //-------------------------------------------------------------- //-------------------- InteractiveViewerFrame ------------------ //-------------------------------------------------------------- //-------------------- keyPressEvent ------------------ void InteractiveViewerFrame::keyPressEvent(QKeyEvent* e) { myInteractiveViewer->keyPressEvent(e); //-- give the parent a chance! QWidget::keyPressEvent(e); } // keep this include here (in the namespace camitk) as long as // CMake automoc generates a camitk namespace error on this moc // for the RendererWidget member of the InteractiveViewer class. // By including it, the file kept out library-camitkcore_automoc.cpp #include "moc_InteractiveViewer.cpp" }