// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause // this test verifies that vtkFastLabelDataMapper works as expected #include "vtkTestUtilities.h" #include "vtkActor.h" #include "vtkCamera.h" #include "vtkDataSetAttributes.h" #include "vtkDataSetMapper.h" #include "vtkExtractSelection.h" #include "vtkFastLabeledDataMapper.h" #include "vtkFloatArray.h" #include "vtkGenerateIds.h" #include "vtkGeometryFilter.h" #include "vtkHardwareSelector.h" #include "vtkIdTypeArray.h" #include "vtkInteractorStyleTrackballCamera.h" #include "vtkNew.h" #include "vtkPassThrough.h" #include "vtkPlaneSource.h" #include "vtkPointData.h" #include "vtkPolyDataMapper.h" #include "vtkProperty.h" #include "vtkRegressionTestImage.h" #include "vtkRenderWindow.h" #include "vtkRenderWindowInteractor.h" #include "vtkRenderer.h" #include "vtkRendererCollection.h" #include "vtkSelection.h" #include "vtkSelectionNode.h" #include "vtkStringArray.h" #include "vtkStringFormatter.h" #include "vtkTextActor.h" #include "vtkTextProperty.h" #include "vtkTransform.h" #include "vtkTransformPolyDataFilter.h" #include "vtkTrivialProducer.h" #include #include namespace { struct TestContextData { // Fast Labels vtkNew plane; vtkNew xform; vtkNew matrix; vtkNew ids; vtkNew labelMapper; vtkNew labelActor; // Filtering Polydata Source Data vtkNew filter; vtkNew filteredLabelMapper; vtkNew filteredActor; // Origin Points vtkNew originPointMapper; vtkNew originPointActor; // Status Text vtkNew statusTextLabelActor; }; std::unique_ptr testContext; class ScopedTestContextInitializer { public: ScopedTestContextInitializer() { testContext.reset(new TestContextData()); } ~ScopedTestContextInitializer() { testContext.reset(nullptr); } }; const char* LABEL_TYPES = "types"; const char* LABEL_TEXT_NAMES = "names"; const char* LABEL_FRAMES = "frames"; //----------------------------------------------------------------------------- void AddTextProperty(vtkSmartPointer mapper, int idx, int font, int fontSize, int frameWidth, std::array color, std::array bgColor, std::array frameColor) { vtkNew tprop; tprop->SetFontFamily(font); tprop->SetColor(color.data()); tprop->SetOpacity(color[3]); tprop->SetBackgroundColor(bgColor.data()); tprop->SetBackgroundOpacity(bgColor[3]); tprop->SetFontSize(fontSize); tprop->SetFrame(frameWidth > 0); tprop->SetFrameWidth(frameWidth); tprop->SetFrameColor(frameColor.data()); // alpha unused mapper->SetLabelTextProperty(tprop, idx); } //----------------------------------------------------------------------------- void AddTextProperties(vtkSmartPointer mapper) { AddTextProperty( mapper, 0, VTK_TIMES, 24, 2, { 1., 0., 0., 1. }, { 0., 1., .0, 1. }, { .0, .0, .1, 1. }); AddTextProperty( mapper, 1, VTK_ARIAL, 24, 4, { 1., 1., 1., 1. }, { .2, 1., .2, 1. }, { .1, .6, .6, 1. }); AddTextProperty( mapper, 2, VTK_COURIER, 24, 8, { 0., 0., 0., 1. }, { .8, 1., .8, 1. }, { .8, .2, .2, 1. }); AddTextProperty( mapper, 3, VTK_ARIAL, 12, 1, { .8, 1., .2, 1. }, { .1, .4, .2, 1. }, { 0., 0., 0., 1. }); AddTextProperty( mapper, 4, VTK_ARIAL, 32, 4, { .5, .5, .2, 1. }, { 0., 0., 1., 1. }, { .8, .5, .3, 1. }); AddTextProperty( mapper, 5, VTK_TIMES, 16, 3, { 1., .2, 1., 1. }, { .2, 1., .6, 1. }, { .1, 0., .3, 1. }); AddTextProperty( mapper, 6, VTK_COURIER, 18, 0, { 1., 1., 1., 1. }, { 0., 0., 0., 0. }, { 0., 0., 0., 0. }); AddTextProperty( mapper, 7, VTK_COURIER, 22, 1, { 0., 0., 0., 1. }, { .2, 1., .2, 1. }, { 0., 0., 0., 1. }); AddTextProperty( mapper, 8, VTK_TIMES, 18, 1, { 0., 1., 1., 1. }, { 0., 0., 0., 1. }, { 1., 1., 1., 1. }); AddTextProperty( mapper, 9, VTK_ARIAL, 24, 4, { 1., .5, .5, 1. }, { .5, .5, 1., 1. }, { .5, 1., .5, 1. }); } //----------------------------------------------------------------------------- void UpdatePlaneArrays(int prefix = 0) { testContext->plane->Update(); auto dataset = testContext->plane->GetOutput(); auto pointData = dataset->GetPointData(); vtkNew types; types->SetNumberOfComponents(1); types->SetName(LABEL_TYPES); vtkNew names; names->SetName(LABEL_TEXT_NAMES); vtkNew frames; frames->SetNumberOfComponents(3); frames->SetName(LABEL_FRAMES); for (int i = 0; i < dataset->GetNumberOfPoints(); i++) { types->InsertNextValue(i % 10); std::string buf; if (prefix > 0) { buf = vtk::format("{:d}_Z_{:d}_a", prefix, i); } else { buf = vtk::format("Z_{:d}_a", i); } names->InsertNextValue(buf); const double v = i / static_cast(dataset->GetNumberOfPoints()); frames->InsertNextTuple3(v, v, v); } pointData->AddArray(types); pointData->AddArray(names); pointData->AddArray(frames); testContext->xform->Modified(); } //----------------------------------------------------------------------------- vtkSmartPointer GetFilteredPolyDataInput() { vtkNew polyData; auto pointData = polyData->GetPointData(); vtkNew types; types->SetNumberOfComponents(1); types->SetName(LABEL_TYPES); vtkNew names; names->SetName(LABEL_TEXT_NAMES); pointData->AddArray(types); pointData->AddArray(names); vtkNew points; polyData->SetPoints(points); int numPoints = 11; for (int i = 0; i < numPoints; i++) { types->InsertNextValue(i % 10); names->InsertNextValue(vtk::format("FPD_{:d}", i)); points->InsertNextPoint((i * 0.1) - 0.5, 0.8, 0.0); } return polyData; } //----------------------------------------------------------------------------- vtkSmartPointer GetFilterSelection(int startIndex = 0, int endIndex = 10) { vtkNew indicesSet; for (int i = startIndex; i <= endIndex; i++) { indicesSet->InsertNextValue(i); } vtkNew selectionNode; selectionNode->SetFieldType(vtkSelectionNode::POINT); selectionNode->SetContentType(vtkSelectionNode::INDICES); selectionNode->SetSelectionList(indicesSet); vtkNew selection; selection->AddNode(selectionNode); return selection; } //----------------------------------------------------------------------------- class KeyPressInteractorStyle : public vtkInteractorStyleTrackballCamera { public: static KeyPressInteractorStyle* New(); vtkTypeMacro(KeyPressInteractorStyle, vtkInteractorStyleTrackballCamera); static void PrintControls() { std::cout << std::endl; std::cout << "Controls:" << std::endl; std::cout << "\t s: Select" << std::endl; std::cout << "\t C/c: Change Font" << std::endl; std::cout << "\t M/m: More/Less Data" << std::endl; std::cout << "\t B/b: Bigger/Smaller Transform" << std::endl; std::cout << "\t G/g: Bigger/Smaller Font Size" << std::endl; std::cout << "\t P/p: Toggles Perspective and Projection" << std::endl; std::cout << "\t i: Increment Prefix" << std::endl; std::cout << "Filtered PolyData:" << std::endl; std::cout << "\t T/t: Toggle PolyData Filter" << std::endl; std::cout << "" << std::endl; std::cout << "\t q: Quit" << std::endl; std::cout << std::endl; } void OnKeyPress() override { // Get the keypress std::string key = this->Interactor->GetKeySym(); // "s" for "s"elect if (key == "s") { Select(); } // Fonts static int cnt = -1; if (key == "c") // change font selection { cnt = (cnt - 1) % 7; while (cnt < 0) { cnt += 7; } FontChange(cnt); } if (key == "C") // change font selection { cnt = (cnt + 1) % 7; FontChange(cnt); } // Multiply Data if (key == "M") // "M" for "M"ore data { MultiplyData(2.0); } if (key == "m") // "m" for "l"ess data? { MultiplyData(0.5); } // Scale if (key == "B") // "B" for "B"igger transform { Scale(2.0); } if (key == "b") // "b" for "s"maller transform? { Scale(0.5); } // Font Size if (key == "G") // "G" for bigger font size { FontSize(6); } if (key == "g") // "g" for smaller font size { FontSize(-6); } // "P" or "p" to toggle Perspective and Projection. if (key == "P" || key == "p") { PerspectiveToggle(); } // "I" to "I"ncrement general text prefixes if (key == "I" || key == "i") { static int prefix = 0; std::cout << "Incrementing Prefix to " << ++prefix << std::endl; UpdatePlaneArrays(prefix); } // "T" toggle polydata filtering if (key == "T" || key == "t") { FilterToggle(); } // Forward events vtkInteractorStyleTrackballCamera::OnKeyPress(); this->Renderer->GetRenderWindow()->Render(); } //----------------------------------------------------------------------------- void FontChange(int cnt) { vtkNew p; p->SetBackgroundColor(0.5, 0.5, 0.5); std::cout << "Font Change: "; switch (cnt) { case 0: std::cout << "Arial" << std::endl; p->SetFontFamilyAsString("Arial"); p->SetColor(1.0, 1.0, 1.0); p->SetBackgroundColor(1.0, 0.0, 0.0); p->SetFontSize(24); break; case 1: std::cout << "Arial grey w blue frame" << std::endl; p->SetFontFamilyAsString("Arial"); p->SetColor(0.5, 0.5, 0.5); p->SetBackgroundColor(1.0, 1.0, 1.0); p->SetBackgroundOpacity(1.0); p->SetFontSize(24); p->FrameOn(); p->SetFrameWidth(4); p->SetFrameColor(0.0, 0.0, 0.8); break; case 2: std::cout << "Courier" << std::endl; p->SetFontFamilyAsString("Courier"); p->SetColor(0.0, 1.0, 0.0); p->SetBackgroundColor(0.5, 0.0, 0.5); p->SetBackgroundOpacity(0.9); p->SetFontSize(32); break; case 3: std::cout << "Times" << std::endl; p->SetFontFamilyAsString("Times"); p->SetColor(0.0, 0.0, 1.0); p->SetBackgroundColor(1.0, 0.7, 0.4); p->SetBackgroundOpacity(0.1); p->SetFontSize(38); break; case 4: std::cout << "Courier Frames" << std::endl; p->SetFontFamilyAsString("Courier"); p->SetColor(0.0, 0.0, 1.0); p->SetFontSize(36); p->SetBackgroundColor(1.0, 0.5, 1.0); p->SetBackgroundOpacity(1.0); p->FrameOn(); p->SetFrameWidth(2); break; case 5: std::cout << "Courier Frames BIGGER" << std::endl; p->SetFontFamilyAsString("Courier"); p->SetColor(0.0, 0.0, 1.0); p->SetFontSize(64); p->SetBackgroundColor(1.0, 0.5, 1.0); p->SetFrameColor(0.0, 0.5, 0.5); p->SetBackgroundOpacity(1.0); p->FrameOn(); p->SetFrameWidth(4); break; case 6: std::cout << "Inconsolata" << std::endl; p->SetFontFamily(VTK_FONT_FILE); // relative path in this example, so has to be in same directory user runs from p->SetFontFile("Inconsolata.otf"); // see vtkResourceFileLocator for a pattern to follow to manufacture an // absolute path to look for instead for distributable binaries etc p->SetColor(1.0, 1.0, 1.0); p->SetFontSize(32); p->FrameOff(); p->SetBackgroundColor(0.8, 0.0, 0.8); p->SetBackgroundOpacity(1.0); break; default: std::cout << "Invalid Index " << cnt << std::endl; } testContext->statusTextLabelActor->SetTextProperty(p); testContext->labelMapper->SetLabelTextProperty(p, 1); } //----------------------------------------------------------------------------- void FontSize(int sizeDelta) { vtkTextProperty* p = testContext->labelMapper->GetLabelTextProperty(0); int fsize = p->GetFontSize(); fsize += sizeDelta; std::cout << "Font Size: " << fsize << std::endl; p->SetFontSize(fsize); testContext->statusTextLabelActor->SetTextProperty(p); testContext->labelMapper->SetLabelTextProperty(p, 0); } //----------------------------------------------------------------------------- void MultiplyData(double multiplier) { int res[2]; testContext->plane->GetResolution(res[0], res[1]); res[1] = static_cast(static_cast(res[1]) * multiplier); std::cout << "Plane Multiplier: " << multiplier << " Size: " << res[1] << std::endl; testContext->plane->SetResolution(res[0], res[1]); UpdatePlaneArrays(); } //----------------------------------------------------------------------------- void Scale(double multiplier) { testContext->matrix->GetScale(scale); scale[0] = multiplier; testContext->matrix->Scale(scale); testContext->matrix->GetScale(scale); std::cout << "Scale Multiplier: " << multiplier << " Size: " << scale[0] << std::endl; } //----------------------------------------------------------------------------- void Select() { vtkNew selector; selector->SetRenderer(this->Interactor->GetRenderWindow()->GetRenderers()->GetFirstRenderer()); int* temp = this->Interactor->GetRenderWindow()->GetSize(); unsigned int windowSize[4]; windowSize[0] = temp[2] + 1; windowSize[1] = temp[3] + 1; windowSize[2] = temp[0] - 1; windowSize[3] = temp[1] - 1; selector->SetArea(windowSize); selector->SetFieldAssociation(vtkDataObject::FIELD_ASSOCIATION_POINTS); vtkSelection* selection = selector->Select(); std::cout << "Selection has " << selection->GetNumberOfNodes() << " nodes." << std::endl; #if 0 for (unsigned int cnt = 0; cnt < selection->GetNumberOfNodes(); cnt++) { auto n = selection->GetNode(cnt); vtkIdTypeArray *da = vtkIdTypeArray::SafeDownCast(n->GetSelectionData()->GetArray("SelectedIds")); std::cout << da->GetClassName() << std::endl; for (int tup = 0; tup < da->GetNumberOfTuples(); tup++) { std::cout << " ID[" << tup << "]=" << da->GetValue(tup) << std::endl; } } #endif testContext->ids->Update(); this->selectionExtraction->SetInputData(0, testContext->ids->GetOutput()); this->selectionExtraction->SetInputData(1, selection); selection->Delete(); this->selectionMapper->ScalarVisibilityOff(); this->selectionMapper->SetInputConnection(this->selectionExtraction->GetOutputPort()); this->selectionActor->SetMapper(this->selectionMapper); this->selectionActor->GetProperty()->SetColor(1, 0, 0); this->selectionActor->GetProperty()->SetPointSize(40); static bool hasset = false; if (!hasset) { this->Renderer->AddActor(this->selectionActor); hasset = true; } } //----------------------------------------------------------------------------- void PerspectiveToggle() { vtkCamera* cam = this->Renderer->GetActiveCamera(); cam->SetParallelProjection(!cam->GetParallelProjection()); } //----------------------------------------------------------------------------- void FilterToggle() { static bool enlarge = true; if (enlarge) { std::cout << "ToggleFilter: Enlarge" << std::endl; testContext->filter->SetInputData(1, GetFilterSelection(0, 10)); } else { std::cout << "ToggleFilter: Reduce" << std::endl; testContext->filter->SetInputData(1, GetFilterSelection(3, 7)); } enlarge = !enlarge; } //----------------------------------------------------------------------------- double scale[3] = { 1.0, 1.0, 1.0 }; vtkRenderer* Renderer; vtkNew selectionExtraction; vtkNew selectionMapper; vtkNew selectionActor; }; vtkStandardNewMacro(KeyPressInteractorStyle); } // end anon namespace int TestLabeledDataMappers(int argc, char* argv[]) { //----------------------------------------------------------------------------- KeyPressInteractorStyle::PrintControls(); ::ScopedTestContextInitializer scopedTestContextInit; (void)scopedTestContextInit; //----------------------------------------------------------------------------- // General Labels // Create some data to label testContext->plane->SetResolution(10, 10); UpdatePlaneArrays(); // Scale data testContext->xform->SetInputConnection(testContext->plane->GetOutputPort()); testContext->xform->SetTransform(testContext->matrix); // Generate ids for labeling testContext->ids->SetInputConnection(testContext->xform->GetOutputPort()); testContext->ids->PointIdsOn(); // Map labels AddTextProperties(testContext->labelMapper); testContext->labelMapper->SetLabelModeToLabelFieldData(); testContext->labelMapper->SetFieldDataName(LABEL_TEXT_NAMES); testContext->labelMapper->SetInputArrayToProcess( 0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, LABEL_TYPES); #if 0 // Disable: this to use TextProperty colors // testContext->labelMapper->SetFrameColorsName(LABEL_FRAMES); #endif #define INSRC 0 #if INSRC == 0 testContext->labelMapper->SetInputConnection(testContext->ids->GetOutputPort()); #endif #if INSRC == 1 vtkNew pd; pd->ShallowCopy(testContext->ids->GetOutput()); testContext->labelMapper->SetInputData(pd); #endif #if INSRC == 2 vtkNew tp; tp->SetOutput(testContext->ids->GetOutput()); testContext->labelMapper->SetInputConnection(tp->GetOutputPort()); #endif testContext->labelActor->SetMapper(testContext->labelMapper); //----------------------------------------------------------------------------- // Filtered PolyData Labels testContext->filter->SetInputData(0, GetFilteredPolyDataInput()); testContext->filter->SetInputData(1, GetFilterSelection(3, 7)); vtkNew geometryFilter; geometryFilter->SetInputConnection(testContext->filter->GetOutputPort()); AddTextProperties(testContext->filteredLabelMapper); testContext->filteredLabelMapper->SetLabelModeToLabelFieldData(); testContext->filteredLabelMapper->SetFieldDataName(LABEL_TEXT_NAMES); testContext->filteredLabelMapper->SetInputArrayToProcess( 0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, LABEL_TYPES); testContext->filteredLabelMapper->SetInputConnection(geometryFilter->GetOutputPort()); testContext->filteredActor->SetMapper(testContext->filteredLabelMapper); //----------------------------------------------------------------------------- // Status Text Mapping testContext->statusTextLabelActor->SetInput("0 1 3 12 Z_61_a 102"); vtkNew statusTextProperty; statusTextProperty->SetFontFamilyAsString("Arial"); statusTextProperty->SetFontSize(24); statusTextProperty->SetColor(1.0, 1.0, 1.0); statusTextProperty->SetBackgroundColor(1.0, 0.0, 0.0); testContext->statusTextLabelActor->SetTextProperty(statusTextProperty); //----------------------------------------------------------------------------- // Origin Points testContext->originPointMapper->SetInputConnection(testContext->ids->GetOutputPort()); testContext->originPointActor->SetMapper(testContext->originPointMapper); testContext->originPointActor->GetProperty()->SetRepresentationToPoints(); testContext->originPointActor->GetProperty()->RenderPointsAsSpheresOn(); testContext->originPointActor->GetProperty()->SetPointSize(5); //----------------------------------------------------------------------------- // Rendering setup vtkNew ren; ren->AddActor(testContext->originPointActor); ren->AddActor(testContext->labelActor); ren->AddActor(testContext->filteredActor); ren->AddActor(testContext->statusTextLabelActor); ren->SetBackground(.5, .5, 6.); vtkNew renWin; renWin->AddRenderer(ren); renWin->SetMultiSamples(0); renWin->SetSize(500, 500); vtkNew renderWindowInteractor; renderWindowInteractor->SetRenderWindow(renWin); vtkNew style; style->Renderer = ren; renderWindowInteractor->SetInteractorStyle(style); style->SetCurrentRenderer(ren); renWin->Render(); testContext->labelMapper->ReleaseGraphicsResources(renWin); testContext->filteredLabelMapper->ReleaseGraphicsResources(renWin); renWin->Render(); int retVal = vtkRegressionTestImage(renWin); if (retVal == vtkRegressionTester::DO_INTERACTOR) { renderWindowInteractor->Start(); } style->SetCurrentRenderer(nullptr); testContext->labelActor->SetMapper(nullptr); return !retVal; }