diff --git a/Documentation/release/dev/axis-actor-2d.md b/Documentation/release/dev/axis-actor-2d.md new file mode 100644 index 0000000000000000000000000000000000000000..1ec762d5ce492cc0efdc075ea5b04e196503cf97 --- /dev/null +++ b/Documentation/release/dev/axis-actor-2d.md @@ -0,0 +1,6 @@ +## vtkAxisActor2D improvements + +vtkAxisActor2D has different modes to compute tick positions and number of labels. +Consistency has been improved between modes in several ways: + * the number of labels always matches the number of ticks + * the displayed labels text always matches the ticks position diff --git a/Rendering/Annotation/Testing/Cxx/CMakeLists.txt b/Rendering/Annotation/Testing/Cxx/CMakeLists.txt index ecf0574c7db26dea684da7be1f201bd64edfa2fc..efe2d860a4c254d6d3fb7a7894c45a47e873c9ca 100644 --- a/Rendering/Annotation/Testing/Cxx/CMakeLists.txt +++ b/Rendering/Annotation/Testing/Cxx/CMakeLists.txt @@ -2,6 +2,7 @@ vtk_add_test_cxx(vtkRenderingAnnotationCxxTests tests TestAxisActor.cxx TestAxisActor2D.cxx TestAxisActor3D.cxx + TestAxisActorMode2D.cxx TestAxisActorText3D.cxx TestBarChartActor.cxx TestCaptionActor2D.cxx diff --git a/Rendering/Annotation/Testing/Cxx/TestAxisActor2D.cxx b/Rendering/Annotation/Testing/Cxx/TestAxisActor2D.cxx index d6a4decb0696dda0736f02c243106079ae550266..f1f91d57c16eb7c1f3685f9726bf6ca1939ae6ee 100644 --- a/Rendering/Annotation/Testing/Cxx/TestAxisActor2D.cxx +++ b/Rendering/Annotation/Testing/Cxx/TestAxisActor2D.cxx @@ -1,8 +1,262 @@ // SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause -#include "TestAxisActorInternal.h" -int TestAxisActor2D(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) +#include "TestAxisActor2DInternal.h" + +#include "vtkLogger.h" +#include "vtkPoints.h" + +//------------------------------------------------------------------------------ +bool TestDefaultLabels() +{ + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + + std::vector expectedLabels = { "0.00 ", "0.200 ", "0.400 ", "0.600 ", "0.800 ", + "1.00 " }; + return axis->CompareLabelMapperString(expectedLabels); +} + +//------------------------------------------------------------------------------ +bool TestLabelsNotation() +{ + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + + axis->SetNotation(vtkNumberToString::Scientific); + axis->SetPrecision(3); + window->Render(); + + std::vector expectedLabels = { + "0.000e+0", + "2.000e-1", + "4.000e-1", + "6.000e-1", + "8.000e-1", + "1.000e+0", + }; + bool status = axis->CompareLabelMapperString(expectedLabels); + + axis->SetNotation(vtkNumberToString::Fixed); + axis->SetPrecision(2); + window->Render(); + expectedLabels = { "0.00", "0.20", "0.40", "0.60", "0.80", "1.00" }; + status = axis->CompareLabelMapperString(expectedLabels) && status; + + return status; +} + +//------------------------------------------------------------------------------ +bool TestRangeLabels() +{ + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + axis->SetRange(42, 43); + + axis->AdjustLabelsOff(); + axis->SetNotation(vtkNumberToString::Fixed); + axis->SetPrecision(2); + window->Render(); + + std::vector expectedLabels = { "42.00", "42.25", "42.50", "42.75", "43.00" }; + return axis->CompareLabelMapperString(expectedLabels); +} + +//------------------------------------------------------------------------------ +bool TestNumberOfLabels() +{ + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + + // adjust labels modifies number of labels. Disable it. + axis->AdjustLabelsOff(); + int nbOfLabels = 6; + axis->SetNumberOfLabels(nbOfLabels); + + vtkNew expectedPoints; + double spacing = 48; + for (int i = 1; i < nbOfLabels - 1; i++) + { + expectedPoints->InsertNextPoint(START_POINT + i * spacing, START_POINT + i * spacing, 0); + } + bool status = CompareTicksPosition(axis, window, expectedPoints); + + axis->SetNumberOfLabels(2); + expectedPoints->Initialize(); + status = CompareTicksPosition(axis, window, expectedPoints); + + return status; +} + +//------------------------------------------------------------------------------ +bool TestAdjustLabels() { - return TestAxisActorInternal(1, 0); + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + axis->SetNotation(vtkNumberToString::Fixed); + axis->SetPrecision(2); + axis->SetRange(0, 1.); + window->Render(); + + vtkNew expectedPoints; + double spacing = 48; + int nbOfLabels = 6; + for (int i = 1; i < nbOfLabels - 1; i++) + { + expectedPoints->InsertNextPoint(START_POINT + i * spacing, START_POINT + i * spacing, 0); + } + bool status = CompareTicksPosition(axis, window, expectedPoints); + + std::vector expectedLabels = { "0.00", "0.20", "0.40", "0.60", "0.80", "1.00" }; + status = axis->CompareLabelMapperString(expectedLabels) && status; + + axis->SetRange(0.05, 1.05); + window->Render(); + // this does not changes anything ! + status = CompareTicksPosition(axis, window, expectedPoints) && status; + status = axis->CompareLabelMapperString(expectedLabels) && status; + + axis->SetRange(0.05, 1.1); + // this changes the number of ticks ! + expectedPoints->Initialize(); + spacing = 60; + nbOfLabels = 5; + for (int i = 1; i < nbOfLabels - 1; i++) + { + expectedPoints->InsertNextPoint(START_POINT + i * spacing, START_POINT + i * spacing, 0); + } + status = CompareTicksPosition(axis, window, expectedPoints) && status; + // this changes the bounds of the range ! + expectedLabels = { "0.00", "0.30", "0.60", "0.90", "1.20" }; + status = axis->CompareLabelMapperString(expectedLabels) && status; + + return status; } + +//------------------------------------------------------------------------------ +bool TestNumberOfMinorTicks() +{ + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + axis->AdjustLabelsOff(); + axis->SetNumberOfMinorTicks(1); + axis->SetMinorTickLength(8); + window->Render(); + + vtkNew expectedPoints; + double spacing = 30; + int nbOfLabels = 9; + for (int i = 1; i < nbOfLabels - 1; i++) + { + expectedPoints->InsertNextPoint(START_POINT + i * spacing, START_POINT + i * spacing, 0); + } + return CompareTicksPosition(axis, window, expectedPoints); +} + +//------------------------------------------------------------------------------ +bool TestRulerMode() +{ + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + axis->AdjustLabelsOff(); + axis->RulerModeOn(); + axis->SetNotation(vtkNumberToString::Fixed); + axis->SetPrecision(2); + + vtkNew expectedPoints; + expectedPoints->InsertNextPoint(242.132, 242.132, 0); + bool status = CompareTicksPosition(axis, window, expectedPoints); + // we have more labels than drawn ticks ... + std::vector expectedLabels = { "0.00", "0.88", "1.00" }; + status = axis->CompareLabelMapperString(expectedLabels) && status; + + axis->SetRange(42, 43); + axis->SetRulerDistance(0.4); + axis->SetNumberOfMinorTicks(1); + + expectedPoints->Initialize(); + double spacing = 42.4264; + int nbOfLabels = 7; + for (int i = 1; i < nbOfLabels - 1; i++) + { + expectedPoints->InsertNextPoint(START_POINT + i * spacing, START_POINT + i * spacing, 0); + } + status = CompareTicksPosition(axis, window, expectedPoints); + + // we have less labels than drawn ticks ... + expectedLabels = { "42.00", "42.35", "42.71", "43.00" }; + status = axis->CompareLabelMapperString(expectedLabels) && status; + + return status; +} + +//------------------------------------------------------------------------------ +int TestAxisActor2D(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) +{ + int status = EXIT_SUCCESS; + if (!TestNumberOfLabels()) + { + vtkLog(ERROR, "TestNumberOfLabels failed"); + status = EXIT_FAILURE; + } + if (!TestDefaultLabels()) + { + vtkLog(ERROR, "TestDefaultLabels failed"); + status = EXIT_FAILURE; + } + if (!TestLabelsNotation()) + { + vtkLog(ERROR, "TestLabelsNotation failed"); + status = EXIT_FAILURE; + } + if (!TestRangeLabels()) + { + vtkLog(ERROR, "TestRangeLabels failed"); + status = EXIT_FAILURE; + } + if (!TestAdjustLabels()) + { + vtkLog(ERROR, "TestAdjustLabels failed"); + status = EXIT_FAILURE; + } + if (!TestNumberOfMinorTicks()) + { + vtkLog(ERROR, "TestNumberOfMinorTicks failed"); + status = EXIT_FAILURE; + } + if (!TestRulerMode()) + { + vtkLog(ERROR, "TestRulerMode failed"); + status = EXIT_FAILURE; + } + + // final screenshot with default parameters + vtkNew axis; + vtkNew window; + SetupPipeline(axis, window); + + // change property rendering option to make test more robust + vtkNew textProp; + textProp->SetColor(1, 0.5, 0); + textProp->SetFontSize(18); + textProp->BoldOn(); + axis->SetUseFontSizeFromProperty(true); + axis->SetLabelTextProperty(textProp); + axis->GetProperty()->SetColor(1, 0, 0); + axis->GetProperty()->SetLineWidth(4); + + vtkNew interactor; + interactor->SetRenderWindow(window); + interactor->Initialize(); + window->Render(); + interactor->Start(); + + return status; +}; diff --git a/Rendering/Annotation/Testing/Cxx/TestAxisActor2DInternal.h b/Rendering/Annotation/Testing/Cxx/TestAxisActor2DInternal.h new file mode 100644 index 0000000000000000000000000000000000000000..76d3b393bbfa6bd018db9ce0332dc66f7314b940 --- /dev/null +++ b/Rendering/Annotation/Testing/Cxx/TestAxisActor2DInternal.h @@ -0,0 +1,106 @@ +// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// SPDX-License-Identifier: BSD-3-Clause +#ifndef TestAxisActor2DInternal_h +#define TestAxisActor2DInternal_h + +#include "vtkTestUtilities.h" + +#include "vtkAxisActor2D.h" +#include "vtkCamera.h" +#include "vtkNew.h" +#include "vtkNumberToString.h" +#include "vtkPolyDataMapper.h" +#include "vtkProperty2D.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkSphereSource.h" +#include "vtkTextMapper.h" +#include "vtkTextProperty.h" + +VTK_ABI_NAMESPACE_BEGIN + +// starting point in viewport coordinate. X = Y = 30 +static constexpr int START_POINT = 30; + +/** + * Mock vtkAxisActor2D to access inner member for comparison. + */ +class vtkAxisActor2DMock : public vtkAxisActor2D +{ +public: + vtkTypeMacro(vtkAxisActor2DMock, vtkAxisActor2D); + static vtkAxisActor2DMock* New() { VTK_STANDARD_NEW_BODY(vtkAxisActor2DMock); } + + bool CompareLabelMapperString(const std::vector& strings) + { + int nbOfLabels = this->NumberOfLabelsBuilt; + if (nbOfLabels != static_cast(strings.size())) + { + vtkErrorMacro( + "Wrong number of labels strings: has " << nbOfLabels << " instead of " << strings.size()); + return false; + } + for (int index = 0; index < nbOfLabels; index++) + { + vtkTextMapper* mapper = this->LabelMappers[index]; + std::string label = mapper->GetInput(); + if (label != strings[index]) + { + vtkErrorMacro("Wrong label: <" << label << "> instead of <" << strings[index] << ">"); + return false; + } + } + return true; + } +}; + +/** + * Compare the provided vtkPoints with TickPositions from axis. + * TickPositions are only the *inner* ticks, i.e. without the starting and ending tick. + * So expect two less points than actual number of ticks. + */ +inline bool CompareTicksPosition( + vtkAxisActor2D* axis, vtkRenderWindow* window, vtkPoints* expectedPoints) +{ + window->Render(); + vtkPoints* points = axis->GetTickPositions(); + + vtkNew ticks; + ticks->SetPoints(points); + if (ticks->GetNumberOfPoints() == 0 && expectedPoints->GetNumberOfPoints() == 0) + { + // ComparePoints raises error with empty vtkPoints, shortcuts it. + return true; + } + + vtkNew expectedTicks; + expectedTicks->SetPoints(expectedPoints); + return vtkTestUtilities::ComparePoints(ticks, expectedTicks); +} + +//------------------------------------------------------------------------------ +inline void SetupPipeline(vtkAxisActor2D* axis, vtkRenderWindow* window) +{ + // create a diagonal in render view, with some margins + axis->SetPoint1(0.1, 0.1); + axis->SetPoint2(0.9, 0.9); + + vtkNew sphereSource; + vtkNew sphereMapper; + sphereMapper->SetInputConnection(sphereSource->GetOutputPort()); + vtkNew sphereActor; + sphereActor->SetMapper(sphereMapper); + + vtkNew renderer; + renderer->AddActor(axis); + renderer->AddActor(sphereActor); + renderer->GetActiveCamera()->ParallelProjectionOn(); + + window->SetSize(300, 300); + window->AddRenderer(renderer); + window->Render(); +} + +VTK_ABI_NAMESPACE_END +#endif diff --git a/Rendering/Annotation/Testing/Cxx/TestAxisActorMode2D.cxx b/Rendering/Annotation/Testing/Cxx/TestAxisActorMode2D.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c8bb87e23cf120a3158c8011a72c098ccd64a389 --- /dev/null +++ b/Rendering/Annotation/Testing/Cxx/TestAxisActorMode2D.cxx @@ -0,0 +1,8 @@ +// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen +// SPDX-License-Identifier: BSD-3-Clause +#include "TestAxisActorInternal.h" + +int TestAxisActorMode2D(int vtkNotUsed(argc), char* vtkNotUsed(argv)[]) +{ + return TestAxisActorInternal(1, 0); +} diff --git a/Rendering/Annotation/Testing/Data/Baseline/TestAxisActor2D.png.sha512 b/Rendering/Annotation/Testing/Data/Baseline/TestAxisActor2D.png.sha512 index ef841e9c0b9e3a6ffb714840cb2a5fafa8272fed..930c2e60e0ca243f0f33db0b0539806e4cc392e5 100644 --- a/Rendering/Annotation/Testing/Data/Baseline/TestAxisActor2D.png.sha512 +++ b/Rendering/Annotation/Testing/Data/Baseline/TestAxisActor2D.png.sha512 @@ -1 +1 @@ -4c6cb837cf966a61b16124ceb7ef710bf77b7cb05a3f4b1478249e377a08b7187b02ddb8722f737625e38df993bc26ad7d49d1c165e2110af1da83f8594d3897 +2c8002f432d678315d926d073a228db6febe262982724a20c476ccbb3bce66d5f639a9e1f5987859b6d00814eada6365f4017329cebd46f894b35a6eeb4257dc diff --git a/Rendering/Annotation/Testing/Data/Baseline/TestAxisActorMode2D.png.sha512 b/Rendering/Annotation/Testing/Data/Baseline/TestAxisActorMode2D.png.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..d781744c67f7a47df40d5af2810d70bde495af50 --- /dev/null +++ b/Rendering/Annotation/Testing/Data/Baseline/TestAxisActorMode2D.png.sha512 @@ -0,0 +1 @@ +298e5dfedebc14f57890c2acad81c7e39be84b7b76df0e8a93df37408115669c7317630b63c09fb6203c17d6d865886e3213d1cd6a5de5f1055102c893e96a0d diff --git a/Rendering/Annotation/vtkAxisActor2D.cxx b/Rendering/Annotation/vtkAxisActor2D.cxx index df197d843d78f7505d67efffd3043f75f14e8386..c6857461ce096559a60be452998d18c148394cee 100644 --- a/Rendering/Annotation/vtkAxisActor2D.cxx +++ b/Rendering/Annotation/vtkAxisActor2D.cxx @@ -4,6 +4,7 @@ #include "vtkCellArray.h" #include "vtkMath.h" +#include "vtkMathUtilities.h" #include "vtkNumberToString.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" @@ -23,6 +24,8 @@ vtkStandardNewMacro(vtkAxisActor2D); vtkCxxSetObjectMacro(vtkAxisActor2D, LabelTextProperty, vtkTextProperty); vtkCxxSetObjectMacro(vtkAxisActor2D, TitleTextProperty, vtkTextProperty); +static constexpr int MAX_FONT_SIZE = 1000; + //------------------------------------------------------------------------------ // Instantiate this object. vtkAxisActor2D::vtkAxisActor2D() @@ -125,7 +128,7 @@ int vtkAxisActor2D::RenderOpaqueGeometry(vtkViewport* viewport) if (this->LabelVisibility) { - for (i = 0; i < this->NumberOfLabelsBuilt; i++) + for (i = 0; i < this->AdjustedNumberOfLabels; i++) { renderedSomething += this->LabelActors[i]->RenderOpaqueGeometry(viewport); } @@ -154,7 +157,7 @@ int vtkAxisActor2D::RenderOverlay(vtkViewport* viewport) if (this->LabelVisibility) { - for (i = 0; i < this->NumberOfLabelsBuilt; i++) + for (i = 0; i < this->AdjustedNumberOfLabels; i++) { renderedSomething += this->LabelActors[i]->RenderOverlay(viewport); } @@ -241,172 +244,188 @@ void vtkAxisActor2D::PrintSelf(ostream& os, vtkIndent indent) } //------------------------------------------------------------------------------ -void vtkAxisActor2D::BuildAxis(vtkViewport* viewport) +bool vtkAxisActor2D::PositionsChangedOrViewportResized(vtkViewport* viewport) { - int i, *x, viewportSizeHasChanged, positionsHaveChanged; - vtkIdType ptIds[2]; - double p1[3], p2[3], offset; - double interval, deltaX, deltaY; - double xTick[3]; - double theta, val; - int *size, stringSize[2]; - char string[512]; + // Check to see whether we have to rebuild everything + // Viewport change may not require rebuild + int* currentPosition = this->PositionCoordinate->GetComputedViewportValue(viewport); + int* currentPosition2 = this->Position2Coordinate->GetComputedViewportValue(viewport); + bool positionsHaveChanged = (currentPosition[0] != this->LastPosition[0] || + currentPosition[1] != this->LastPosition[1] || currentPosition2[0] != this->LastPosition2[0] || + currentPosition2[1] != this->LastPosition2[1]); + // See whether fonts have to be rebuilt (font size depends on viewport size) + int* size = viewport->GetSize(); + bool viewportSizeHasChanged = (this->LastSize[0] != size[0] || this->LastSize[1] != size[1]); + + return positionsHaveChanged || viewportSizeHasChanged; +} + +//------------------------------------------------------------------------------ +bool vtkAxisActor2D::ShouldRebuild(vtkViewport* viewport) +{ if (this->TitleVisibility && !this->TitleTextProperty) { vtkErrorMacro(<< "Need title text property to render axis actor"); - return; + return false; } if (this->LabelVisibility && !this->LabelTextProperty) { vtkErrorMacro(<< "Need label text property to render axis actor"); - return; + return false; } - // Check to see whether we have to rebuild everything - // Viewport change may not require rebuild - positionsHaveChanged = 0; - int* lastPosition = this->PositionCoordinate->GetComputedViewportValue(viewport); - int* lastPosition2 = this->Position2Coordinate->GetComputedViewportValue(viewport); - if (lastPosition[0] != this->LastPosition[0] || lastPosition[1] != this->LastPosition[1] || - lastPosition2[0] != this->LastPosition2[0] || lastPosition2[1] != this->LastPosition2[1]) + if (!viewport->GetVTKWindow()) { - positionsHaveChanged = 1; + return false; } - // See whether fonts have to be rebuilt (font size depends on viewport size) - viewportSizeHasChanged = 0; - size = viewport->GetSize(); - if (this->LastSize[0] != size[0] || this->LastSize[1] != size[1]) - { - viewportSizeHasChanged = 1; - this->LastSize[0] = size[0]; - this->LastSize[1] = size[1]; - } + bool recentBuild = viewport->GetMTime() < this->BuildTime && + viewport->GetVTKWindow()->GetMTime() < this->BuildTime && this->GetMTime() < this->BuildTime && + (!this->LabelVisibility || this->LabelTextProperty->GetMTime() < this->BuildTime) && + (!this->TitleVisibility || this->TitleTextProperty->GetMTime() < this->BuildTime); - if (!viewport->GetVTKWindow() || - (!positionsHaveChanged && !viewportSizeHasChanged && viewport->GetMTime() < this->BuildTime && - viewport->GetVTKWindow()->GetMTime() < this->BuildTime && - this->GetMTime() < this->BuildTime && - (!this->LabelVisibility || this->LabelTextProperty->GetMTime() < this->BuildTime) && - (!this->TitleVisibility || this->TitleTextProperty->GetMTime() < this->BuildTime))) + if (!this->PositionsChangedOrViewportResized(viewport) && recentBuild) { - return; + return false; } - vtkDebugMacro(<< "Rebuilding axis"); - - // Initialize and get important info - this->Axis->Initialize(); - this->AxisActor->SetProperty(this->GetProperty()); - - // Compute the location of tick marks and labels - - this->UpdateAdjustedRange(); - - interval = (this->AdjustedRange[1] - this->AdjustedRange[0]) / (this->AdjustedNumberOfLabels - 1); - - this->NumberOfLabelsBuilt = this->AdjustedNumberOfLabels; + return true; +} - // Generate the axis and tick marks. - // We'll do our computation in viewport coordinates. First determine the - // location of the endpoints. - x = this->PositionCoordinate->GetComputedViewportValue(viewport); +//------------------------------------------------------------------------------ +double vtkAxisActor2D::GetViewportAxisLength(vtkViewport* viewport) +{ + int* x = this->PositionCoordinate->GetComputedViewportValue(viewport); + double p1[3]; p1[0] = x[0]; p1[1] = x[1]; p1[2] = 0.0; - this->LastPosition[0] = x[0]; - this->LastPosition[1] = x[1]; x = this->Position2Coordinate->GetComputedViewportValue(viewport); + double p2[3]; p2[0] = x[0]; p2[1] = x[1]; p2[2] = 0.0; - this->LastPosition2[0] = x[0]; - this->LastPosition2[1] = x[1]; - double *xp1, *xp2, len = 0.0; - if (this->SizeFontRelativeToAxis) - { - xp1 = this->PositionCoordinate->GetComputedDoubleViewportValue(viewport); - xp2 = this->Position2Coordinate->GetComputedDoubleViewportValue(viewport); - len = sqrt((xp2[0] - xp1[0]) * (xp2[0] - xp1[0]) + (xp2[1] - xp1[1]) * (xp2[1] - xp1[1])); - } + double axis[3]; + vtkMath::Subtract(p2, p1, axis); + return vtkMath::Norm(axis); +} - vtkPoints* pts = vtkPoints::New(); - vtkCellArray* lines = vtkCellArray::New(); - this->Axis->SetPoints(pts); - this->Axis->SetLines(lines); - pts->Delete(); - lines->Delete(); +//------------------------------------------------------------------------------ +double vtkAxisActor2D::GetViewportRulerDistance(vtkViewport* viewport) +{ + double wp1[3], wp2[3], wp21[3]; + this->PositionCoordinate->GetValue(wp1); + this->Position2Coordinate->GetValue(wp2); + vtkMath::Subtract(wp2, wp1, wp21); + + const double worldLength = vtkMath::Norm(wp21); + // Tick distance was computed in world coordinates, convert to viewport + // coordinates. + double length = this->GetViewportAxisLength(viewport); + const double worldToLocalRatio = (worldLength <= 0.0 ? 0.0 : length / worldLength); + return this->RulerDistance * worldToLocalRatio; +} - // Generate point along axis (as well as tick points) - deltaX = p2[0] - p1[0]; - deltaY = p2[1] - p1[1]; +//------------------------------------------------------------------------------ +double vtkAxisActor2D::GetAxisAngle(vtkViewport* viewport) +{ + int* p1 = this->PositionCoordinate->GetComputedViewportValue(viewport); + int* p2 = this->Position2Coordinate->GetComputedViewportValue(viewport); - if (deltaX == 0. && deltaY == 0.) - { - theta = 0.; - } - else - { - theta = atan2(deltaY, deltaX); - } + double deltaX = p2[0] - p1[0]; + double deltaY = p2[1] - p1[1]; - // First axis point, where first tick is located - ptIds[0] = pts->InsertNextPoint(p1); - xTick[0] = p1[0] + this->TickLength * sin(theta); - xTick[1] = p1[1] - this->TickLength * cos(theta); - xTick[2] = 0.0; - pts->InsertNextPoint(xTick); - - // Set up creation of ticks - double p21[3], length; - p21[0] = p2[0] - p1[0]; - p21[1] = p2[1] - p1[1]; - p21[2] = p2[2] - p1[2]; - length = vtkMath::Normalize(p21); + return (deltaX == 0. && deltaY == 0.) ? 0. : atan2(deltaY, deltaX); +} +//------------------------------------------------------------------------------ +void vtkAxisActor2D::UpdateTicksValueAndPosition(vtkViewport* viewport) +{ // Sum of all the ticks: minor and majors. Contains the start and end ticks. - int numTicks; + int totalNumberOfTicks = (this->AdjustedNumberOfLabels - 1) * (this->NumberOfMinorTicks + 1) + 1; + double viewportAxisLength = this->GetViewportAxisLength(viewport); // Distance between each minor tick. double distance; if (this->RulerMode) { - double wp1[3], wp2[3], wp21[3]; - this->PositionCoordinate->GetValue(wp1); - this->Position2Coordinate->GetValue(wp2); - wp21[0] = wp2[0] - wp1[0]; - wp21[1] = wp2[1] - wp1[1]; - wp21[2] = wp2[2] - wp1[2]; - const double worldLength = vtkMath::Norm(wp21); - const double worldDistance = this->RulerDistance / (this->NumberOfMinorTicks + 1); - numTicks = static_cast(worldDistance <= 0.0 ? 0.0 : (worldLength / worldDistance)); - const double precision = std::numeric_limits::epsilon(); - const bool hasRemainderInDivision = - worldDistance <= 0.0 ? false : std::fmod(worldLength, worldDistance) > precision; - // numTicks must contain the start and end ticks - // Don't add the end tick if it is already in numTicks: - // when wLength / wDistance is an integer. - numTicks += hasRemainderInDivision ? 2 : 1; - // Tick distance was computed in world coordinates, convert to viewport - // coordinates. - const double worldToLocalRatio = (worldLength <= 0.0 ? 0.0 : length / worldLength); - distance = worldDistance * worldToLocalRatio; + double viewportRulerDistance = this->GetViewportRulerDistance(viewport); + distance = viewportRulerDistance / (this->NumberOfMinorTicks + 1); } else { - numTicks = (this->AdjustedNumberOfLabels - 1) * (this->NumberOfMinorTicks + 1) + 1; - distance = length / (numTicks - 1); + distance = viewportAxisLength / (totalNumberOfTicks - 1); } + this->TickValues.clear(); + this->NormalizedTickPositions.clear(); + for (int tick = 0; tick < totalNumberOfTicks; tick++) + { + double pos = distance * tick / viewportAxisLength; + pos = vtkMath::ClampValue(pos, 0., 1.); + this->NormalizedTickPositions.push_back(pos); + + if (tick % (this->NumberOfMinorTicks + 1) == 0) + { + double value = + this->AdjustedRange[0] + pos * (this->AdjustedRange[1] - this->AdjustedRange[0]); + this->TickValues.push_back(value); + } + } +} + +//------------------------------------------------------------------------------ +void vtkAxisActor2D::BuildTicksPolyData(vtkViewport* viewport) +{ + this->Axis->Initialize(); + + vtkNew pts; + vtkNew lines; + this->Axis->SetPoints(pts); + this->Axis->SetLines(lines); + + // Generate the axis and tick marks. + // We'll do our computation in viewport coordinates. First determine the + // location of the endpoints. + int* x = this->PositionCoordinate->GetComputedViewportValue(viewport); + double axisStart[3]; + axisStart[0] = x[0]; + axisStart[1] = x[1]; + axisStart[2] = 0.0; + + x = this->Position2Coordinate->GetComputedViewportValue(viewport); + double axisEnd[3]; + axisEnd[0] = x[0]; + axisEnd[1] = x[1]; + axisEnd[2] = 0.0; + + // Generate point along axis (as well as tick points) + double theta = this->GetAxisAngle(viewport); + + // First axis point, where first tick is located + vtkIdType ptIds[2]; + ptIds[0] = pts->InsertNextPoint(axisStart); + double tickPos[3]; + tickPos[0] = axisStart[0] + this->TickLength * std::sin(theta); + tickPos[1] = axisStart[1] - this->TickLength * std::cos(theta); + tickPos[2] = 0.0; + pts->InsertNextPoint(tickPos); + + double normalizedAxis[3]; + vtkMath::Subtract(axisEnd, axisStart, normalizedAxis); + double axisLength = vtkMath::Normalize(normalizedAxis); + + int totalNumberOfTicks = static_cast(this->NormalizedTickPositions.size()); + // Only draw the inner ticks (not the start/end ticks) - if (numTicks >= 2) + if (totalNumberOfTicks >= 2) { - this->TicksStartPos->SetNumberOfPoints(numTicks - 2); + this->TicksStartPos->SetNumberOfPoints(totalNumberOfTicks - 2); } - for (i = 1; i < numTicks - 1; i++) + for (int i = 1; i < totalNumberOfTicks - 1; i++) { int tickLength = 0; if (i % (this->NumberOfMinorTicks + 1) == 0) @@ -417,20 +436,21 @@ void vtkAxisActor2D::BuildAxis(vtkViewport* viewport) { tickLength = this->MinorTickLength; } - xTick[0] = p1[0] + i * p21[0] * distance; - xTick[1] = p1[1] + i * p21[1] * distance; - this->TicksStartPos->SetPoint(i - 1, xTick); - pts->InsertNextPoint(xTick); - xTick[0] = xTick[0] + tickLength * sin(theta); - xTick[1] = xTick[1] - tickLength * cos(theta); - pts->InsertNextPoint(xTick); + + tickPos[0] = axisStart[0] + this->NormalizedTickPositions[i] * normalizedAxis[0] * axisLength; + tickPos[1] = axisStart[1] + this->NormalizedTickPositions[i] * normalizedAxis[1] * axisLength; + this->TicksStartPos->SetPoint(i - 1, tickPos); + pts->InsertNextPoint(tickPos); + tickPos[0] = tickPos[0] + tickLength * std::sin(theta); + tickPos[1] = tickPos[1] - tickLength * std::cos(theta); + pts->InsertNextPoint(tickPos); } // Last axis point - ptIds[1] = pts->InsertNextPoint(p2); - xTick[0] = p2[0] + this->TickLength * sin(theta); - xTick[1] = p2[1] - this->TickLength * cos(theta); - pts->InsertNextPoint(xTick); + ptIds[1] = pts->InsertNextPoint(axisEnd); + tickPos[0] = axisEnd[0] + this->TickLength * std::sin(theta); + tickPos[1] = axisEnd[1] - this->TickLength * std::cos(theta); + pts->InsertNextPoint(tickPos); // Add the axis if requested if (this->AxisVisibility) @@ -441,157 +461,186 @@ void vtkAxisActor2D::BuildAxis(vtkViewport* viewport) // Create lines representing the tick marks if (this->TickVisibility) { - for (i = 0; i < numTicks; i++) + for (int i = 0; i < totalNumberOfTicks; i++) { ptIds[0] = 2 * i; ptIds[1] = 2 * i + 1; lines->InsertNextCell(2, ptIds); } } +} - // Build the labels - if (this->LabelVisibility) +//------------------------------------------------------------------------------ +void vtkAxisActor2D::BuildLabels(vtkViewport* viewport) +{ + // Update the labels text. Do it only if the range has been adjusted, + // i.e. if we think that new labels must be created. + // WARNING: if LabelFormat has changed, they should be recreated too + // but at this point the check on LabelFormat is "included" in + // UpdateAdjustedRange(), which is the function that update + // AdjustedRangeBuildTime or not. + vtkMTimeType labeltime = this->AdjustedRangeBuildTime; + if (this->AdjustedRangeBuildTime > this->BuildTime) { - // Update the labels text. Do it only if the range has been adjusted, - // i.e. if we think that new labels must be created. - // WARNING: if LabelFormat has changed, they should be recreated too - // but at this point the check on LabelFormat is "included" in - // UpdateAdjustedRange(), which is the function that update - // AdjustedRangeBuildTime or not. - vtkMTimeType labeltime = this->AdjustedRangeBuildTime; - if (this->AdjustedRangeBuildTime > this->BuildTime) + if (this->AdjustedNumberOfLabels != static_cast(this->TickValues.size())) { - for (i = 0; i < this->AdjustedNumberOfLabels; i++) - { - val = this->AdjustedRange[0] + i * interval; + vtkErrorMacro("inconsistent number of labels"); + } - if (this->GetNotation() == 0) - { - // Use default legend notation : don't use vtkNumberToString - // for the default setting in order to ensure retrocompatibility - snprintf(string, sizeof(string), this->LabelFormat, val); - this->LabelMappers[i]->SetInput(string); - } - else - { - vtkNumberToString converter; - converter.SetNotation(this->GetNotation()); - converter.SetPrecision(this->GetPrecision()); - std::string formattedString = converter.Convert(val); - this->LabelMappers[i]->SetInput(formattedString.c_str()); - } + for (int i = 0; i < this->AdjustedNumberOfLabels; i++) + { + double val = this->TickValues[i]; + if (this->GetNotation() == 0) + { + // Use default legend notation : don't use vtkNumberToString + // for the default setting in order to ensure retrocompatibility + char string[512]; + snprintf(string, sizeof(string), this->LabelFormat, val); + this->LabelMappers[i]->SetInput(string); } - - // Check if the label text has changed - if (this->LabelMappers[this->AdjustedNumberOfLabels - 1]->GetMTime() > labeltime) + else { - labeltime = this->LabelMappers[this->AdjustedNumberOfLabels - 1]->GetMTime(); + vtkNumberToString converter; + converter.SetNotation(this->GetNotation()); + converter.SetPrecision(this->GetPrecision()); + std::string formattedString = converter.Convert(val); + this->LabelMappers[i]->SetInput(formattedString.c_str()); } } - // Copy prop and text prop eventually - for (i = 0; i < this->AdjustedNumberOfLabels; i++) + // Check if the label text has changed + if (this->LabelMappers[this->AdjustedNumberOfLabels - 1]->GetMTime() > labeltime) { - if (this->LabelTextProperty->GetMTime() > this->BuildTime || - this->AdjustedRangeBuildTime > this->BuildTime) - { - // Shallow copy here so that the size of the label prop is not - // affected by the automatic adjustment of its text mapper's - // size (i.e. its mapper's text property is identical except - // for the font size which will be modified later). This - // allows text actors to share the same text property, and in - // that case specifically allows the title and label text prop - // to be the same. - this->LabelMappers[i]->GetTextProperty()->ShallowCopy(this->LabelTextProperty); - } + labeltime = this->LabelMappers[this->AdjustedNumberOfLabels - 1]->GetMTime(); } + } - // Resize the mappers if needed (i.e. viewport has changed, than - // font size should be changed, or label text property has changed, - // or some of the labels have changed (got bigger for example) - if (positionsHaveChanged || viewportSizeHasChanged || - this->LabelTextProperty->GetMTime() > this->BuildTime || labeltime > this->BuildTime) + // Copy prop and text prop eventually + for (int i = 0; i < this->AdjustedNumberOfLabels; i++) + { + if (this->LabelTextProperty->GetMTime() > this->BuildTime || + this->AdjustedRangeBuildTime > this->BuildTime) { - if (!this->UseFontSizeFromProperty) + // Shallow copy here so that the size of the label prop is not + // affected by the automatic adjustment of its text mapper's + // size (i.e. its mapper's text property is identical except + // for the font size which will be modified later). This + // allows text actors to share the same text property, and in + // that case specifically allows the title and label text prop + // to be the same. + this->LabelMappers[i]->GetTextProperty()->ShallowCopy(this->LabelTextProperty); + } + } + + int* size = viewport->GetSize(); + + double *xp1, *xp2, len = 0.0; + if (this->SizeFontRelativeToAxis) + { + xp1 = this->PositionCoordinate->GetComputedDoubleViewportValue(viewport); + xp2 = this->Position2Coordinate->GetComputedDoubleViewportValue(viewport); + len = std::sqrt((xp2[0] - xp1[0]) * (xp2[0] - xp1[0]) + (xp2[1] - xp1[1]) * (xp2[1] - xp1[1])); + } + + // Resize the mappers if needed (i.e. viewport has changed, than + // font size should be changed, or label text property has changed, + // or some of the labels have changed (got bigger for example) + + if (this->PositionsChangedOrViewportResized(viewport) || + this->LabelTextProperty->GetMTime() > this->BuildTime || labeltime > this->BuildTime) + { + if (!this->UseFontSizeFromProperty) + { + if (!this->SizeFontRelativeToAxis) { - if (!this->SizeFontRelativeToAxis) - { - vtkTextMapper::SetMultipleRelativeFontSize(viewport, this->LabelMappers, - this->AdjustedNumberOfLabels, size, this->LastMaxLabelSize, - 0.015 * this->FontFactor * this->LabelFactor); - } - else + vtkTextMapper::SetMultipleRelativeFontSize(viewport, this->LabelMappers, + this->AdjustedNumberOfLabels, size, this->LastMaxLabelSize, + 0.015 * this->FontFactor * this->LabelFactor); + } + else + { + int minFontSize = MAX_FONT_SIZE; + int fontSize; + int minLabel = 0; + for (int i = 0; i < this->AdjustedNumberOfLabels; i++) { - int minFontSize = 1000, fontSize, minLabel = 0; - for (i = 0; i < this->AdjustedNumberOfLabels; i++) + fontSize = this->LabelMappers[i]->SetConstrainedFontSize(viewport, + static_cast((1.0 / this->AdjustedNumberOfLabels) * len), + static_cast(0.2 * len)); + if (fontSize < minFontSize) { - fontSize = this->LabelMappers[i]->SetConstrainedFontSize(viewport, - static_cast((1.0 / this->AdjustedNumberOfLabels) * len), - static_cast(0.2 * len)); - if (fontSize < minFontSize) - { - minFontSize = fontSize; - minLabel = i; - } + minFontSize = fontSize; + minLabel = i; } - for (i = 0; i < this->AdjustedNumberOfLabels; i++) - { - this->LabelMappers[i]->GetTextProperty()->SetFontSize(minFontSize); - } - this->LabelMappers[minLabel]->GetSize(viewport, this->LastMaxLabelSize); } - } - else - { - this->LabelMappers[0]->GetSize(viewport, this->LastMaxLabelSize); + for (int i = 0; i < this->AdjustedNumberOfLabels; i++) + { + this->LabelMappers[i]->GetTextProperty()->SetFontSize(minFontSize); + } + this->LabelMappers[minLabel]->GetSize(viewport, this->LastMaxLabelSize); } } - - // Position the mappers - for (i = 0; i < this->AdjustedNumberOfLabels; i++) + else { - pts->GetPoint((this->NumberOfMinorTicks + 1) * 2 * i + 1, xTick); - this->LabelMappers[i]->GetSize(viewport, stringSize); - vtkAxisActor2D::SetOffsetPosition(xTick, theta, this->LastMaxLabelSize[0], - this->LastMaxLabelSize[1], this->TickOffset, this->LabelActors[i]); + this->LabelMappers[0]->GetSize(viewport, this->LastMaxLabelSize); } - } // If labels visible + } - // Now build the title - if (this->Title != nullptr && this->Title[0] != 0 && this->TitleVisibility) + vtkPoints* pts = this->Axis->GetPoints(); + // Position the mappers + for (int i = 0; i < this->AdjustedNumberOfLabels; i++) { - this->TitleMapper->SetInput(this->Title); + double xTick[3]; + pts->GetPoint((this->NumberOfMinorTicks + 1) * 2 * i + 1, xTick); + double theta = this->GetAxisAngle(viewport); + vtkAxisActor2D::SetOffsetPosition(xTick, theta, this->LastMaxLabelSize[0], + this->LastMaxLabelSize[1], this->TickOffset, this->LabelActors[i]); + } +} - if (this->TitleTextProperty->GetMTime() > this->BuildTime) - { - // Shallow copy here so that the size of the title prop is not - // affected by the automatic adjustment of its text mapper's - // size (i.e. its mapper's text property is identical except for - // the font size which will be modified later). This allows text - // actors to share the same text property, and in that case - // specifically allows the title and label text prop to be the same. - this->TitleMapper->GetTextProperty()->ShallowCopy(this->TitleTextProperty); - } +//------------------------------------------------------------------------------ +void vtkAxisActor2D::BuildTitle(vtkViewport* viewport) +{ + this->TitleMapper->SetInput(this->Title); + + if (this->TitleTextProperty->GetMTime() > this->BuildTime) + { + // Shallow copy here so that the size of the title prop is not + // affected by the automatic adjustment of its text mapper's + // size (i.e. its mapper's text property is identical except for + // the font size which will be modified later). This allows text + // actors to share the same text property, and in that case + // specifically allows the title and label text prop to be the same. + this->TitleMapper->GetTextProperty()->ShallowCopy(this->TitleTextProperty); + } - if (positionsHaveChanged || viewportSizeHasChanged || - this->TitleTextProperty->GetMTime() > this->BuildTime) + int stringSize[2]; + + if (this->PositionsChangedOrViewportResized(viewport) || + this->TitleTextProperty->GetMTime() > this->BuildTime) + { + if (!this->UseFontSizeFromProperty) { - if (!this->UseFontSizeFromProperty) + if (!this->SizeFontRelativeToAxis) { - if (!this->SizeFontRelativeToAxis) - { - vtkTextMapper::SetRelativeFontSize( - this->TitleMapper, viewport, size, stringSize, 0.015 * this->FontFactor); - } - else - { - this->TitleMapper->SetConstrainedFontSize( - viewport, static_cast(0.33 * len), static_cast(0.2 * len)); - this->TitleMapper->GetSize(viewport, stringSize); - } + int* size = viewport->GetSize(); + vtkTextMapper::SetRelativeFontSize( + this->TitleMapper, viewport, size, stringSize, 0.015 * this->FontFactor); } else { + + double *xp1, *xp2, len = 0.0; + if (this->SizeFontRelativeToAxis) + { + xp1 = this->PositionCoordinate->GetComputedDoubleViewportValue(viewport); + xp2 = this->Position2Coordinate->GetComputedDoubleViewportValue(viewport); + len = std::sqrt( + (xp2[0] - xp1[0]) * (xp2[0] - xp1[0]) + (xp2[1] - xp1[1]) * (xp2[1] - xp1[1])); + } + + this->TitleMapper->SetConstrainedFontSize( + viewport, static_cast(0.33 * len), static_cast(0.2 * len)); this->TitleMapper->GetSize(viewport, stringSize); } } @@ -599,22 +648,76 @@ void vtkAxisActor2D::BuildAxis(vtkViewport* viewport) { this->TitleMapper->GetSize(viewport, stringSize); } + } + else + { + this->TitleMapper->GetSize(viewport, stringSize); + } - xTick[0] = p1[0] + (p2[0] - p1[0]) * this->TitlePosition; - xTick[1] = p1[1] + (p2[1] - p1[1]) * this->TitlePosition; - xTick[0] = xTick[0] + (this->TickLength + this->TickOffset) * sin(theta); - xTick[1] = xTick[1] - (this->TickLength + this->TickOffset) * cos(theta); + int* x1 = this->PositionCoordinate->GetComputedViewportValue(viewport); + int* x2 = this->Position2Coordinate->GetComputedViewportValue(viewport); + double xTick[3]; + xTick[0] = x1[0] + (x2[0] - x1[0]) * this->TitlePosition; + xTick[1] = x1[1] + (x2[1] - x1[1]) * this->TitlePosition; + double theta = this->GetAxisAngle(viewport); + xTick[0] = xTick[0] + (this->TickLength + this->TickOffset) * std::sin(theta); + xTick[1] = xTick[1] - (this->TickLength + this->TickOffset) * std::cos(theta); - offset = 0.0; - if (this->LabelVisibility) - { - offset = vtkAxisActor2D::ComputeStringOffset( - this->LastMaxLabelSize[0], this->LastMaxLabelSize[1], theta); - } + double offset = 0.0; + if (this->LabelVisibility) + { + offset = vtkAxisActor2D::ComputeStringOffset( + this->LastMaxLabelSize[0], this->LastMaxLabelSize[1], theta); + } + + vtkAxisActor2D::SetOffsetPosition( + xTick, theta, stringSize[0], stringSize[1], static_cast(offset), this->TitleActor); +} - vtkAxisActor2D::SetOffsetPosition( - xTick, theta, stringSize[0], stringSize[1], static_cast(offset), this->TitleActor); - } // If title visible +//------------------------------------------------------------------------------ +void vtkAxisActor2D::UpdateCachedInformations(vtkViewport* viewport) +{ + int* x = this->PositionCoordinate->GetComputedViewportValue(viewport); + this->LastPosition[0] = x[0]; + this->LastPosition[1] = x[1]; + x = this->Position2Coordinate->GetComputedViewportValue(viewport); + this->LastPosition2[0] = x[0]; + this->LastPosition2[1] = x[1]; + + int* size = viewport->GetSize(); + this->LastSize[0] = size[0]; + this->LastSize[1] = size[1]; +} + +//------------------------------------------------------------------------------ +void vtkAxisActor2D::BuildAxis(vtkViewport* viewport) +{ + if (!this->ShouldRebuild(viewport)) + { + return; + } + + vtkDebugMacro(<< "Rebuilding axis"); + + this->AxisActor->SetProperty(this->GetProperty()); + + this->UpdateAdjustedRange(); + + this->UpdateTicksValueAndPosition(viewport); + + this->BuildTicksPolyData(viewport); + + if (this->LabelVisibility) + { + this->BuildLabels(viewport); + } + + if (this->Title != nullptr && this->Title[0] != 0 && this->TitleVisibility) + { + this->BuildTitle(viewport); + } + + this->UpdateCachedInformations(viewport); this->BuildTime.Modified(); } @@ -654,6 +757,34 @@ void vtkAxisActor2D::UpdateAdjustedRange() this->AdjustedRange[0] = this->Range[0]; this->AdjustedRange[1] = this->Range[1]; } + + if (this->RulerMode) + { + double wp1[3], wp2[3], wp21[3]; + this->PositionCoordinate->GetValue(wp1); + this->Position2Coordinate->GetValue(wp2); + vtkMath::Subtract(wp2, wp1, wp21); + const double worldLength = vtkMath::Norm(wp21); + this->AdjustedNumberOfLabels = worldLength / this->RulerDistance; + if (vtkMathUtilities::FuzzyCompare( + this->AdjustedNumberOfLabels * this->RulerDistance, worldLength)) + { + this->AdjustedNumberOfLabels += 1; + } + this->AdjustedNumberOfLabels += 2; + } + + // for compatibility + this->NumberOfLabelsBuilt = this->AdjustedNumberOfLabels; + + if (this->AdjustedNumberOfLabels < 1) + { + vtkWarningMacro( + "Axis expects to have at least 1 label. Will use 1 instead of the computed number " + << this->AdjustedNumberOfLabels); + this->AdjustedNumberOfLabels = 1; + } + this->AdjustedRangeBuildTime.Modified(); } @@ -851,8 +982,8 @@ void vtkAxisActor2D::SetOffsetPosition( x = stringWidth / 2.0 + offset; y = stringHeight / 2.0 + offset; - center[0] = xTick[0] + x * sin(theta); - center[1] = xTick[1] - y * cos(theta); + center[0] = xTick[0] + x * std::sin(theta); + center[1] = xTick[1] - y * std::cos(theta); pos[0] = static_cast(center[0] - stringWidth / 2.0); pos[1] = static_cast(center[1] - stringHeight / 2.0); @@ -863,9 +994,9 @@ void vtkAxisActor2D::SetOffsetPosition( //------------------------------------------------------------------------------ double vtkAxisActor2D::ComputeStringOffset(double width, double height, double theta) { - double f1 = height * cos(theta); - double f2 = width * sin(theta); - return (1.2 * sqrt(f1 * f1 + f2 * f2)); + double f1 = height * std::cos(theta); + double f2 = width * std::sin(theta); + return (1.2 * std::sqrt(f1 * f1 + f2 * f2)); } //------------------------------------------------------------------------------ diff --git a/Rendering/Annotation/vtkAxisActor2D.h b/Rendering/Annotation/vtkAxisActor2D.h index 8dbb68d3376c5ce83e357ae0e0d8b138b3bb7667..f524cdfa8e368ee62447de6f9871af3f063f135b 100644 --- a/Rendering/Annotation/vtkAxisActor2D.h +++ b/Rendering/Annotation/vtkAxisActor2D.h @@ -95,8 +95,10 @@ public: ///@{ /** - * Specify the (min,max) axis range. This will be used in the generation + * Specify the (min,max) axis display text range. This will be used in the generation * of labels, if labels are visible. + * This does not impact the position of ticks. + * @see SetNumberOfLabels, SetRulerMode, SetRulerDistance */ vtkSetVector2Macro(Range, double); vtkGetVectorMacro(Range, double, 2); @@ -107,6 +109,8 @@ public: * Specify whether this axis should act like a measuring tape (or ruler) with * specified major tick spacing. If enabled, the distance between major ticks * is controlled by the RulerDistance ivar. + * Note that the displayed values are still controlled by Range, and are not related + * to the actual distance. */ vtkSetMacro(RulerMode, vtkTypeBool); vtkGetMacro(RulerMode, vtkTypeBool); @@ -117,6 +121,7 @@ public: /** * Specify the RulerDistance which indicates the spacing of the major ticks. * This ivar only has effect when the RulerMode is on. + * This is specified in World coordinates. */ vtkSetClampMacro(RulerDistance, double, 0, VTK_FLOAT_MAX); vtkGetMacro(RulerDistance, double); @@ -175,6 +180,10 @@ public: * Call GetAdjustedRange and GetAdjustedNumberOfLabels to get the adjusted * range and number of labels. Note that if RulerMode is on, then the * number of labels is a function of the range and ruler distance. + * + * Note: as it modifies the display text and not any actual position, + * you should probably not use this mode when displaying distance + * or coordinates, as they will be wrong. */ vtkSetMacro(AdjustLabels, vtkTypeBool); vtkGetMacro(AdjustLabels, vtkTypeBool); @@ -473,6 +482,63 @@ protected: private: vtkAxisActor2D(const vtkAxisActor2D&) = delete; void operator=(const vtkAxisActor2D&) = delete; + + /** + * Return true if axis coordinates have changed or if viewport was resized. + */ + bool PositionsChangedOrViewportResized(vtkViewport* viewport); + + /** + * Return true if axis should actually be rebuild. + */ + bool ShouldRebuild(vtkViewport* viewport); + + /** + * Update Ticks value and position. + * Values are major ticks values that will be displayed (see AdjustedRange) + * Positions are the position of each major and minor tick relative + * to the axis (so in [0, 1]) + */ + void UpdateTicksValueAndPosition(vtkViewport* viewport); + + /** + * Build the inner polydata: create points and lines. + */ + void BuildTicksPolyData(vtkViewport* viewport); + + /** + * Build the labels : convert number to text and position it. + */ + void BuildLabels(vtkViewport* viewport); + + /** + * Build the title + */ + void BuildTitle(vtkViewport* viewport); + + /** + * Get the angle of the axis in the viewport + */ + double GetAxisAngle(vtkViewport* viewport); + + /** + * Update member used as cache for change detection. + */ + void UpdateCachedInformations(vtkViewport* viewport); + + /** + * Get the RulerDistance in Viewport coordinates. + */ + double GetViewportRulerDistance(vtkViewport* viewport); + + /** + * Get the axis length in viewport coordinates. + */ + double GetViewportAxisLength(vtkViewport* viewport); + + // tick position in axis, normalized on axis length. + std::vector NormalizedTickPositions; + std::vector TickValues; }; VTK_ABI_NAMESPACE_END