// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-FileCopyrightText: Copyright (c) Kitware Inc. // SPDX-License-Identifier: BSD-3-Clause AND Apache-2.0 // // This example tests the PickingManager using different widgets and associated // pickers: // * vtkBalloonWidget // * vtkBoxWidget // * vtkImplicitPlaneWidget2 // By default the Picking Manager is enabled. // Press 'Ctrl' to switch the activation of the Picking Manager. // Press 'o' to enable/disable the Optimization on render events. #include "vtkActor.h" #include "vtkAppendPolyData.h" #include "vtkBalloonRepresentation.h" #include "vtkBalloonWidget.h" #include "vtkBoxWidget.h" #include "vtkCamera.h" #include "vtkClipPolyData.h" #include "vtkCommand.h" #include "vtkConeSource.h" #include "vtkCylinderSource.h" #include "vtkGlyph3D.h" #include "vtkImplicitPlaneRepresentation.h" #include "vtkImplicitPlaneWidget2.h" #include "vtkInteractorStyleTrackballCamera.h" #include "vtkLODActor.h" #include "vtkNew.h" #include "vtkPickingManager.h" #include "vtkPlane.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkPropPicker.h" #include "vtkProperty.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkSphereSource.h" #include "vtkTesting.h" #include //------------------------------------------------------------------------------ class vtkBalloonPickCallback : public vtkCommand { public: static vtkBalloonPickCallback* New() { return new vtkBalloonPickCallback; } void Execute(vtkObject* caller, unsigned long, void*) override { vtkPropPicker* picker = reinterpret_cast(caller); vtkProp* prop = picker->GetViewProp(); if (prop != nullptr) { this->BalloonWidget->UpdateBalloonString(prop, "Picked"); } } vtkBalloonWidget* BalloonWidget; }; //------------------------------------------------------------------------------ // Updates the vtkPlane implicit function. // This in turn causes the pipeline to update and clip the object. // Callback for the interaction class vtkTIPW2Callback : public vtkCommand { public: static vtkTIPW2Callback* New() { return new vtkTIPW2Callback; } void Execute(vtkObject* caller, unsigned long, void*) override { vtkImplicitPlaneWidget2* planeWidget = reinterpret_cast(caller); vtkImplicitPlaneRepresentation* rep = reinterpret_cast(planeWidget->GetRepresentation()); rep->GetPlane(this->Plane); this->Actor->VisibilityOn(); } vtkTIPW2Callback() : Plane(nullptr) , Actor(nullptr) { } vtkPlane* Plane; vtkActor* Actor; }; //------------------------------------------------------------------------------ // Press 'Ctrl' to switch the activation of the Picking Manager. // Press 'o' to switch the activation of the optimization based on the render // events. class vtkEnableManagerCallback : public vtkCommand { public: static vtkEnableManagerCallback* New() { return new vtkEnableManagerCallback; } void Execute(vtkObject* caller, unsigned long, void*) override { vtkRenderWindowInteractor* iren = static_cast(caller); char* cKeySym = iren->GetKeySym(); std::string keySym = cKeySym != nullptr ? cKeySym : ""; if ((keySym == "Control_L" || keySym == "Control_R") && iren->GetPickingManager()) { if (!iren->GetPickingManager()->GetEnabled()) { std::cout << "PickingManager ON !" << std::endl; iren->GetPickingManager()->EnabledOn(); } else { std::cout << "PickingManager OFF !" << std::endl; iren->GetPickingManager()->EnabledOff(); } } // Enable/Disable the Optimization on render events. else if (keySym == "o" && iren->GetPickingManager()) { if (!iren->GetPickingManager()->GetOptimizeOnInteractorEvents()) { std::cout << "Optimization on Interactor events ON !" << std::endl; iren->GetPickingManager()->SetOptimizeOnInteractorEvents(true); } else { std::cout << "Optimization on Interactor events OFF !" << std::endl; iren->GetPickingManager()->SetOptimizeOnInteractorEvents(false); } } } vtkEnableManagerCallback() = default; }; //------------------------------------------------------------------------------ // Test Picking Manager with a several widgets //------------------------------------------------------------------------------ int TestPickingManagerWidgets(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) { // Create the RenderWindow, Renderer and both Actors // vtkNew ren1; vtkNew renWin; if (renWin->IsA("vtkOSOpenGLRenderWindow")) { // we cannot run in OSMesa. // Note: I am not sure why but this is how things were before. // This test was excluded from the build when VTK_OPENGL_HAS_OSMESA (old setting) // was `ON`. return VTK_SKIP_RETURN_CODE; } renWin->AddRenderer(ren1); vtkNew iren; vtkNew irenStyle; iren->SetRenderWindow(renWin); iren->SetInteractorStyle(irenStyle); // Instantiate a picker and link it to the ballonWidgetCallback vtkNew picker; vtkNew pcbk; picker->AddObserver(vtkCommand::PickEvent, pcbk); iren->SetPicker(picker); /*--------------------------------------------------------------------------*/ // PICKING MANAGER /*--------------------------------------------------------------------------*/ // Callback to switch between the managed and non-managed mode of the // Picking Manager vtkNew callMode; iren->AddObserver(vtkCommand::KeyPressEvent, callMode); /*--------------------------------------------------------------------------*/ // BALLOON WIDGET /*--------------------------------------------------------------------------*/ // Create a test pipeline vtkNew ss; vtkNew mapper; mapper->SetInputConnection(ss->GetOutputPort()); vtkNew sph; sph->SetMapper(mapper); vtkNew cs; vtkNew csMapper; csMapper->SetInputConnection(cs->GetOutputPort()); vtkNew cyl; cyl->SetMapper(csMapper); cyl->AddPosition(5, 0, 0); vtkNew coneSource; vtkNew coneMapper; coneMapper->SetInputConnection(coneSource->GetOutputPort()); vtkNew cone; cone->SetMapper(coneMapper); cone->AddPosition(0, 5, 0); // Create the widget vtkNew rep; rep->SetBalloonLayoutToImageRight(); vtkNew widget; widget->SetInteractor(iren); widget->SetRepresentation(rep); widget->AddBalloon(sph, "This is a sphere", nullptr); widget->AddBalloon(cyl, "This is a\ncylinder", nullptr); widget->AddBalloon(cone, "This is a\ncone,\na really big.", nullptr); pcbk->BalloonWidget = widget; /*--------------------------------------------------------------------------*/ // BOX WIDGET /*--------------------------------------------------------------------------*/ vtkNew boxWidget; boxWidget->SetInteractor(iren); boxWidget->SetPlaceFactor(1.25); // Create the mass actor vtkNew cone1; cone1->SetResolution(6); vtkNew sphere; sphere->SetThetaResolution(8); sphere->SetPhiResolution(8); sphere->SetCenter(5, 5, 0); vtkNew glyph; glyph->SetInputConnection(sphere->GetOutputPort()); glyph->SetSourceData(cone1->GetOutput()); glyph->SetVectorModeToUseNormal(); glyph->SetScaleModeToScaleByVector(); glyph->SetScaleFactor(0.25); vtkNew append; append->AddInputData(glyph->GetOutput()); append->AddInputData(sphere->GetOutput()); vtkNew maceMapper; maceMapper->SetInputConnection(append->GetOutputPort()); vtkNew maceActor; maceActor->SetMapper(maceMapper); /*--------------------------------------------------------------------------*/ // Multiple ImplicitPlane Widgets /*--------------------------------------------------------------------------*/ // Create a mace out of filters. // vtkNew sphereImpPlane; vtkNew coneImpPlane; vtkNew glyphImpPlane; glyphImpPlane->SetInputConnection(sphereImpPlane->GetOutputPort()); glyphImpPlane->SetSourceConnection(coneImpPlane->GetOutputPort()); glyphImpPlane->SetVectorModeToUseNormal(); glyphImpPlane->SetScaleModeToScaleByVector(); glyphImpPlane->SetScaleFactor(0.25); glyphImpPlane->Update(); // The sphere and spikes are appended into a single polydata. // This just makes things simpler to manage. vtkNew apdImpPlane; apdImpPlane->AddInputData(glyphImpPlane->GetOutput()); apdImpPlane->AddInputData(sphereImpPlane->GetOutput()); vtkNew maceMapperImpPlane; maceMapperImpPlane->SetInputConnection(apdImpPlane->GetOutputPort()); vtkNew maceActorImpPlane; maceActorImpPlane->SetMapper(maceMapperImpPlane); maceActorImpPlane->AddPosition(0, 0, 0); maceActorImpPlane->VisibilityOn(); // This portion of the code clips the mace with the vtkPlanes // implicit function. The clipped region is colored green. vtkNew plane; vtkNew clipper; clipper->SetInputConnection(apdImpPlane->GetOutputPort()); clipper->SetClipFunction(plane); clipper->InsideOutOn(); vtkNew selectMapper; selectMapper->SetInputConnection(clipper->GetOutputPort()); vtkNew selectActor; selectActor->SetMapper(selectMapper); selectActor->GetProperty()->SetColor(0, 1, 0); selectActor->VisibilityOff(); selectActor->AddPosition(0, 0, 0); selectActor->SetScale(1.01, 1.01, 1.01); // The SetInteractor method is how 3D widgets are associated with the render // window interactor. Internally, SetInteractor sets up a bunch of callbacks // using the Command/Observer mechanism (AddObserver()). vtkNew impPlaneCallback; impPlaneCallback->Plane = plane; impPlaneCallback->Actor = selectActor; // First ImplicitPlaneWidget (Green) vtkNew impPlaneRep; impPlaneRep->SetPlaceFactor(1.); impPlaneRep->SetOutlineTranslation(false); impPlaneRep->SetScaleEnabled(0); impPlaneRep->PlaceWidget(glyphImpPlane->GetOutput()->GetBounds()); impPlaneRep->SetEdgeColor(0., 1., 0.); impPlaneRep->SetNormal(1, 0, 1); vtkNew planeWidget; planeWidget->SetInteractor(iren); planeWidget->SetRepresentation(impPlaneRep); planeWidget->On(); planeWidget->AddObserver(vtkCommand::InteractionEvent, impPlaneCallback); planeWidget->AddObserver(vtkCommand::UpdateEvent, impPlaneCallback); // Second ImplicitPlaneWidget (Red) vtkNew impPlaneRep2; impPlaneRep2->SetOutlineTranslation(false); impPlaneRep2->SetScaleEnabled(0); impPlaneRep2->SetPlaceFactor(1.); impPlaneRep2->PlaceWidget(glyphImpPlane->GetOutput()->GetBounds()); impPlaneRep2->SetEdgeColor(1., 0., 0.); vtkNew planeWidget2; planeWidget2->SetInteractor(iren); planeWidget2->SetRepresentation(impPlaneRep2); planeWidget2->On(); /*--------------------------------------------------------------------------*/ // Rendering /*--------------------------------------------------------------------------*/ // Add the actors to the renderer, set the background and size ren1->AddActor(sph); ren1->AddActor(cyl); ren1->AddActor(cone); ren1->AddActor(maceActorImpPlane); ren1->AddActor(selectActor); ren1->AddActor(maceActor); ren1->SetBackground(0.1, 0.2, 0.4); renWin->SetSize(600, 600); // Configure the box widget boxWidget->SetProp3D(maceActor); boxWidget->PlaceWidget(); // render the image iren->Initialize(); double extent[6] = { -2, 7, -2, 7, -1, 1 }; ren1->ResetCamera(extent); renWin->Render(); widget->On(); boxWidget->On(); iren->Start(); return EXIT_SUCCESS; }