// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkPolarAxesActor.h" #include "vtkAxisFollower.h" #include "vtkCamera.h" #include "vtkCellArray.h" #include "vtkCoordinate.h" #include "vtkEllipseArcSource.h" #include "vtkFollower.h" #include "vtkMath.h" #include "vtkMathUtilities.h" #include "vtkNew.h" #include "vtkObjectFactory.h" #include "vtkPolyData.h" #include "vtkPolyDataMapper.h" #include "vtkPropCollection.h" #include "vtkProperty.h" #include "vtkStringArray.h" #include "vtkStringFormatter.h" #include "vtkTextProperty.h" #include "vtkViewport.h" #include #include VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkPolarAxesActor); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, Camera, vtkCamera); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, PolarAxisLabelTextProperty, vtkTextProperty); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, PolarAxisTitleTextProperty, vtkTextProperty); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, LastRadialAxisTextProperty, vtkTextProperty); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, SecondaryRadialAxesTextProperty, vtkTextProperty); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, LastRadialAxisProperty, vtkProperty); vtkCxxSetSmartPointerMacro(vtkPolarAxesActor, SecondaryRadialAxesProperty, vtkProperty); namespace { constexpr double VTK_MAXIMUM_RATIO = 1000.0; } //------------------------------------------------------------------------------ void vtkPolarAxesActor::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "ScreenSize: " << this->ScreenSize << "\n"; os << indent << "Pole: (" << this->Pole[0] << ", " << this->Pole[1] << ", " << this->Pole[2] << ")\n"; os << indent << "Number of radial axes: " << this->NumberOfRadialAxes << endl; os << indent << "Number of polar axes: " << this->NumberOfPolarAxes << endl; os << indent << "Angle between two radial axes: " << this->DeltaAngleRadialAxes << endl; os << indent << "Range between two polar axes: " << this->DeltaRangePolarAxes << endl; os << indent << "Minimum Radius: " << this->MinimumRadius << endl; os << indent << "Maximum Radius: " << this->MaximumRadius << endl; os << indent << "Log Scale: " << (this->Log ? "On" : "Off") << endl; os << indent << "Ratio: " << this->Ratio << endl; os << indent << "Polar Arc Resolution per Degree: " << this->PolarArcResolutionPerDegree << endl; os << indent << "Minimum Angle: " << this->MinimumAngle << endl; os << indent << "Maximum Angle: " << this->MaximumAngle << endl; os << indent << "Smallest Visible Polar Angle: " << this->SmallestVisiblePolarAngle << endl; os << indent << "Radial Units (degrees): " << (this->RadialUnits ? "On\n" : "Off\n") << endl; os << indent << "Range: (" << this->Range[0] << ", " << this->Range[1] << ")\n"; if (this->Camera) { os << indent << "Camera:\n"; this->Camera->PrintSelf(os, indent.GetNextIndent()); } else { os << indent << "Camera: (none)\n"; } os << indent << "EnableDistanceLOD: " << (this->EnableDistanceLOD ? "On" : "Off") << endl; os << indent << "DistanceLODThreshold: " << this->DistanceLODThreshold << "\n"; os << indent << "EnableViewAngleLOD: " << (this->EnableViewAngleLOD ? "On" : "Off") << endl; os << indent << "ViewAngleLODThreshold: " << this->ViewAngleLODThreshold << "\n"; os << indent << "Polar Axis Title: " << this->PolarAxisTitle << "\n"; os << indent << "Polar Label Format: " << this->PolarLabelFormat << "\n"; os << indent << "Polar title offset: " << this->PolarTitleOffset[0] << ", " << this->PolarTitleOffset[1] << "\n"; os << indent << "Radial title offset: " << this->RadialTitleOffset[0] << ", " << this->RadialTitleOffset[1] << "\n"; os << indent << "Polar label Y-offset: " << this->PolarLabelOffset << "\n"; os << indent << "Polar exponent Y-offset: " << this->PolarExponentOffset << "\n"; os << indent << "Radial Angle Format: " << this->RadialAngleFormat << "\n"; os << indent << "PolarAxisLabelTextProperty: " << this->PolarAxisLabelTextProperty << endl; os << indent << "PolarAxisTitleTextProperty: " << this->PolarAxisTitleTextProperty << endl; os << indent << "RadialAxisTextProperty: " << this->LastRadialAxisTextProperty << endl; os << indent << "SecondaryRadialAxesTextProperty: " << this->SecondaryRadialAxesTextProperty << endl; os << indent << "Polar Axis Visibility: " << (this->PolarAxisVisibility ? "On\n" : "Off\n"); os << indent << "Polar Title Visibility: " << (this->PolarTitleVisibility ? "On" : "Off") << endl; os << indent << "Polar Label Visibility: " << (this->PolarLabelVisibility ? "On" : "Off") << endl; if (this->PolarAxisTitleLocation == VTK_TITLE_BOTTOM) { os << indent << "Polar Title Location: BOTTOM" << endl; } else if (this->PolarAxisTitleLocation == VTK_TITLE_EXTERN) { os << indent << "Polar Title Location: EXTERN" << endl; } os << indent << "Polar Label exponent location: "; if (this->ExponentLocation == VTK_EXPONENT_BOTTOM) { os << " next to the polar axis title." << endl; } else if (this->ExponentLocation == VTK_EXPONENT_EXTERN) { os << " outer side." << endl; } else { os << " bound to labels." << endl; } os << indent << "Radial Axes Visibility: " << (this->RadialAxesVisibility ? "On\n" : "Off\n"); os << indent << "Radial Title Visibility: " << (this->RadialTitleVisibility ? "On" : "Off") << endl; if (this->RadialAxisTitleLocation == VTK_TITLE_BOTTOM) { os << indent << "Radial Title Location: BOTTOM" << endl; } else if (this->RadialAxisTitleLocation == VTK_TITLE_EXTERN) { os << indent << "Radial Title Location: EXTERN" << endl; } os << indent << "Polar Arcs Visibility: " << (this->PolarArcsVisibility ? "On" : "Off") << endl; os << indent << "Draw Radial Gridlines: " << (this->DrawRadialGridlines ? "On" : "Off") << endl; os << indent << "Draw Polar Arcs Gridlines: " << (this->DrawPolarArcsGridlines ? "On" : "Off") << endl; os << indent << "Draw Radial Axes From Polar Axis: " << (this->RadialAxesOriginToPolarAxis ? "On" : "Off") << endl; //--------------------- TICKS ------------------ os << indent << "TickLocation: " << this->TickLocation << endl; os << indent << "Ticks overall enabled: " << (this->PolarTickVisibility ? "On" : "Off") << endl; os << indent << "Ratio maximum radius / major tick size: " << this->TickRatioRadiusSize << endl; os << indent << "Draw Arc Ticks From Polar Axis: " << (this->ArcTicksOriginToPolarAxis ? "On" : "Off") << endl; //--- major ticks --- // polar axis and last radial axis os << indent << "Axes Major Tick Visibility: " << (this->AxisTickVisibility ? "On" : "Off") << endl; if (this->AxisTickVisibility && this->PolarTickVisibility) { os << indent << "Axes Major Ticks Matches Polar Axes: " << (this->AxisTickMatchesPolarAxes ? "On" : "Off") << endl; os << indent << "Axes Major Tick Step: " << this->DeltaRangeMajor << endl; os << indent << "PolarAxis Major Tick Size: " << this->PolarAxisMajorTickSize << endl; os << indent << "PolarAxis Major Tick Thickness: " << this->PolarAxisMajorTickThickness << endl; if (this->RadialAxesVisibility) { os << indent << "Last Radial Axis Major Ticks Size: " << this->LastRadialAxisMajorTickSize << endl; os << indent << "Last Radial Axis Major Ticks Thickness: " << this->LastRadialAxisMajorTickThickness << endl; } } // last arc os << indent << "Arc Major Ticks Visibility: " << (this->ArcTickVisibility ? "On" : "Off") << endl; if (this->ArcTickVisibility && this->PolarTickVisibility) { os << indent << "Arc Major Ticks Matches Radial Axes: " << (this->ArcTickMatchesRadialAxes ? "On" : "Off") << endl; os << indent << "Arc Major Angle Step: " << this->DeltaAngleMajor << endl; os << indent << "Arc Major Ticks Size: " << this->ArcMajorTickSize << endl; os << indent << "Arc Major Ticks Thickness: " << this->ArcMajorTickThickness << endl; } //--- minor ticks --- // polar axis and last radial axis os << indent << "Axis Minor Ticks Visibility: " << (this->AxisMinorTickVisibility ? "On" : "Off") << endl; if (this->AxisMinorTickVisibility && this->PolarTickVisibility) { os << indent << "Axes Minor Tick Step: " << this->DeltaRangeMinor << endl; os << indent << "Ratio Between PolarAxis Major and Minor Tick : " << this->PolarAxisTickRatioSize << endl; os << indent << "Ratio Between PolarAxis Major and Minor Tick Thickness : " << this->PolarAxisTickRatioThickness << endl; if (this->RadialAxesVisibility) { os << indent << "Ratio Between LastAxis Major and Minor Tick : " << this->LastAxisTickRatioSize << endl; os << indent << "Ratio Between LastAxis Major and Minor Tick Thickness: " << this->LastAxisTickRatioThickness << endl; } } os << indent << "Arc Minor Ticks Visibility: " << (this->ArcMinorTickVisibility ? "On" : "Off") << endl; if (this->ArcMinorTickVisibility && this->PolarTickVisibility) { os << indent << "Arc Minor Angle Step: " << this->DeltaAngleMinor << endl; os << indent << "Ratio Between Last Arc Major and Minor Tick : " << this->ArcTickRatioSize << endl; os << indent << "Ratio Between Last Arc Major and Minor Tick Thickness: " << this->ArcTickRatioThickness << endl; } } //------------------------------------------------------------------------------ vtkPolarAxesActor::vtkPolarAxesActor() { vtkMath::UninitializeBounds(this->Bounds); // Text properties of polar axis title and labels, with default color white // Properties of the radial axes, with default color black this->PolarAxisProperty = vtkSmartPointer::New(); this->PolarAxisProperty->SetColor(0., 0., 0.); this->PolarAxisTitleTextProperty = vtkSmartPointer::New(); this->PolarAxisTitleTextProperty->SetOpacity(1.0); this->PolarAxisTitleTextProperty->SetColor(1., 1., 1.); this->PolarAxisTitleTextProperty->SetFontFamilyToArial(); this->PolarAxisLabelTextProperty = vtkSmartPointer::New(); this->PolarAxisLabelTextProperty->SetColor(1., 1., 1.); this->PolarAxisLabelTextProperty->SetFontFamilyToArial(); // Create and set polar axis of type X this->PolarAxis->SetAxisTypeToX(); // Properties of the last radial axe, with default color black this->LastRadialAxisProperty = vtkSmartPointer::New(); this->LastRadialAxisProperty->SetAmbient(1.0); this->LastRadialAxisProperty->SetDiffuse(0.0); this->LastRadialAxisProperty->SetColor(0., 0., 0.); this->LastRadialAxisTextProperty = vtkSmartPointer::New(); this->LastRadialAxisTextProperty->SetOpacity(1.0); this->LastRadialAxisTextProperty->SetColor(1., 1., 1.); this->LastRadialAxisTextProperty->SetFontFamilyToArial(); // Properties of the secondaries radial axes, with default color black this->SecondaryRadialAxesProperty = vtkSmartPointer::New(); this->SecondaryRadialAxesProperty->SetAmbient(1.0); this->SecondaryRadialAxesProperty->SetDiffuse(0.0); this->SecondaryRadialAxesProperty->SetColor(0., 0., 0.); this->SecondaryRadialAxesTextProperty = vtkSmartPointer::New(); this->SecondaryRadialAxesTextProperty->SetOpacity(1.0); this->SecondaryRadialAxesTextProperty->SetColor(1., 1., 1.); this->SecondaryRadialAxesTextProperty->SetFontFamilyToArial(); // Create and set principal polar arcs and ancillary objects, with default color white this->PolarArcsMapper->SetInputData(this->PolarArcs); this->PolarArcsActor->SetMapper(this->PolarArcsMapper); this->PolarArcsActor->GetProperty()->SetColor(1., 1., 1.); // Create and set secondary polar arcs and ancillary objects, with default color white this->SecondaryPolarArcsMapper->SetInputData(this->SecondaryPolarArcs); this->SecondaryPolarArcsActor->SetMapper(this->SecondaryPolarArcsMapper); this->SecondaryPolarArcsActor->GetProperty()->SetColor(1., 1., 1.); // Create the vtk Object for arc ticks this->ArcTickPolyDataMapper->SetInputData(this->ArcTickPolyData); this->ArcMinorTickPolyDataMapper->SetInputData(this->ArcMinorTickPolyData); this->ArcTickActor->SetMapper(this->ArcTickPolyDataMapper); this->ArcMinorTickActor->SetMapper(this->ArcMinorTickPolyDataMapper); this->PolarLabelFormat = new char[10]; auto result = vtk::format_to_n(this->PolarLabelFormat, 10, "{:s}", "{:<#6.3g}"); *result.out = '\0'; this->RadialAngleFormat = new char[10]; result = vtk::format_to_n(this->RadialAngleFormat, 10, "{:s}", "{:<#3.1f}"); *result.out = '\0'; } //------------------------------------------------------------------------------ vtkPolarAxesActor::~vtkPolarAxesActor() { this->SetCamera(nullptr); delete[] this->PolarLabelFormat; this->PolarLabelFormat = nullptr; delete[] this->RadialAngleFormat; this->RadialAngleFormat = nullptr; } //------------------------------------------------------------------------------ void vtkPolarAxesActor::GetRendered3DProps(vtkPropCollection* collection, bool translucent) { if (this->PolarAxisVisibility) { collection->AddItem(this->PolarAxis); } if (this->RadialAxesVisibility) { for (int i = 0; i < this->NumberOfRadialAxes; ++i) { bool isInnerAxis = (i != this->NumberOfRadialAxes - 1) || (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle)); bool isAxisVisible = !isInnerAxis || this->DrawRadialGridlines; if (this->RadialAxesVisibility && isAxisVisible) { collection->AddItem(this->RadialAxes[i]); } } } if (this->PolarArcsVisibility && !translucent) { collection->AddItem(this->PolarArcsActor); collection->AddItem(this->SecondaryPolarArcsActor); if (this->PolarTickVisibility) { if (this->ArcTickVisibility) { collection->AddItem(this->ArcTickActor); } if (this->ArcMinorTickVisibility) { collection->AddItem(this->ArcMinorTickActor); } } } } //------------------------------------------------------------------------------ vtkTypeBool vtkPolarAxesActor::HasTranslucentPolygonalGeometry() { vtkNew renderedProps; this->GetRendered3DProps(renderedProps, true); renderedProps->InitTraversal(); for (int idx = 0; idx < renderedProps->GetNumberOfItems(); idx++) { vtkProp* prop = renderedProps->GetNextProp(); if (prop->HasTranslucentPolygonalGeometry()) { return 1; } } return Superclass::HasTranslucentPolygonalGeometry(); } //------------------------------------------------------------------------------ int vtkPolarAxesActor::RenderTranslucentPolygonalGeometry(vtkViewport* viewport) { int numberOfRenderedProps = 0; vtkNew renderedProps; this->GetRendered3DProps(renderedProps, true); renderedProps->InitTraversal(); for (int idx = 0; idx < renderedProps->GetNumberOfItems(); idx++) { vtkProp* prop = renderedProps->GetNextProp(); prop->SetPropertyKeys(this->GetPropertyKeys()); numberOfRenderedProps += prop->RenderTranslucentPolygonalGeometry(viewport); } return numberOfRenderedProps; } //------------------------------------------------------------------------------ int vtkPolarAxesActor::RenderOpaqueGeometry(vtkViewport* viewport) { // Initialization int numberOfRenderedProps = 0; this->BuildAxes(viewport); vtkNew renderedProps; this->GetRendered3DProps(renderedProps, false); renderedProps->InitTraversal(); for (int idx = 0; idx < renderedProps->GetNumberOfItems(); idx++) { vtkProp* prop = renderedProps->GetNextProp(); prop->SetPropertyKeys(this->GetPropertyKeys()); numberOfRenderedProps += prop->RenderOpaqueGeometry(viewport); } return numberOfRenderedProps; } //------------------------------------------------------------------------------ int vtkPolarAxesActor::RenderOverlay(vtkViewport* viewport) { int numberOfRenderedProps = 0; if (this->PolarAxisVisibility && this->PolarAxis->GetUse2DMode()) { this->PolarAxis->SetPropertyKeys(this->GetPropertyKeys()); numberOfRenderedProps += this->PolarAxis->RenderOverlay(viewport); } if (this->RadialAxesVisibility) { for (int i = 0; i < this->NumberOfRadialAxes; ++i) { if (this->RadialAxes[i]->GetUse2DMode()) { this->RadialAxes[i]->SetPropertyKeys(this->GetPropertyKeys()); numberOfRenderedProps += this->RadialAxes[i]->RenderOverlay(viewport); } } } return numberOfRenderedProps; } //------------------------------------------------------------------------------ void vtkPolarAxesActor::ReleaseGraphicsResources(vtkWindow* win) { this->PolarAxis->ReleaseGraphicsResources(win); for (int i = 0; i < this->NumberOfRadialAxes; ++i) { this->RadialAxes[i]->ReleaseGraphicsResources(win); } this->SecondaryPolarArcsActor->ReleaseGraphicsResources(win); this->PolarArcsActor->ReleaseGraphicsResources(win); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::CalculateBounds() { // Fetch angles, at this point it is already known that angular sector <= 360. double minAngle = this->MinimumAngle; double maxAngle = this->MaximumAngle; // Ensure that angles are not both < -180 nor both > 180 degrees if (maxAngle < -180.) { // Increment angles modulo 360 degrees minAngle += 360.; maxAngle += 360.; } else if (minAngle > 180.) { // Decrement angles modulo 360 degrees minAngle -= 360.; maxAngle -= 360.; } // Prepare trigonometric quantities double thetaMin = vtkMath::RadiansFromDegrees(minAngle); double cosThetaMin = cos(thetaMin); double sinThetaMin = sin(thetaMin); double thetaMax = vtkMath::RadiansFromDegrees(maxAngle); double cosThetaMax = cos(thetaMax); double sinThetaMax = sin(thetaMax); // Calculate extremal cosines across angular sector double minCos; double maxCos; if (minAngle * maxAngle < 0.) { // Angular sector contains null angle maxCos = 1.; if (minAngle < 180. && maxAngle > 180.) { // Angular sector also contains flat angle minCos = -1.; } else { // Angular sector does not contain flat angle minCos = cosThetaMin < cosThetaMax ? cosThetaMin : cosThetaMax; } } else if (minAngle < 180. && maxAngle > 180.) { // Angular sector does not contain flat angle (and not null angle) minCos = -1.; maxCos = cosThetaMax > cosThetaMin ? cosThetaMax : cosThetaMin; } else { // Angular sector does not contain flat nor null angle minCos = cosThetaMin < cosThetaMax ? cosThetaMin : cosThetaMax; maxCos = cosThetaMax > cosThetaMin ? cosThetaMax : cosThetaMin; } // Calculate extremal sines across angular sector double minSin; double maxSin; if (minAngle < -90. && maxAngle > -90.) { // Angular sector contains negative right angle minSin = -1.; if (minAngle < 90. && maxAngle > 90.) { // Angular sector also contains positive right angle maxSin = 1.; } else { // Angular sector contain does not contain positive right angle maxSin = sinThetaMax > sinThetaMin ? sinThetaMax : sinThetaMin; } } else if (minAngle < 90. && maxAngle > 90.) { // Angular sector contains positive right angle (and not negative one) minSin = sinThetaMin < sinThetaMax ? sinThetaMin : sinThetaMax; maxSin = 1.; } else { // Angular sector contain does not contain either right angle minSin = sinThetaMin < sinThetaMax ? sinThetaMin : sinThetaMax; maxSin = sinThetaMax > sinThetaMin ? sinThetaMax : sinThetaMin; } // Now calculate bounds // xmin this->Bounds[0] = this->Pole[0] + this->MaximumRadius * minCos; // xmax this->Bounds[1] = this->Pole[0] + this->MaximumRadius * maxCos; // ymin this->Bounds[2] = this->Pole[1] + this->MaximumRadius * minSin; // ymax this->Bounds[3] = this->Pole[1] + this->MaximumRadius * maxSin; // zmin this->Bounds[4] = this->Pole[2]; // zmax this->Bounds[5] = this->Pole[2]; // Update modification time of bounds this->BoundsMTime.Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::GetBounds(double bounds[6]) { for (int i = 0; i < 6; i++) { bounds[i] = this->Bounds[i]; } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::GetBounds( double& xmin, double& xmax, double& ymin, double& ymax, double& zmin, double& zmax) { xmin = this->Bounds[0]; xmax = this->Bounds[1]; ymin = this->Bounds[2]; ymax = this->Bounds[3]; zmin = this->Bounds[4]; zmax = this->Bounds[5]; } //------------------------------------------------------------------------------ double* vtkPolarAxesActor::GetBounds() { return this->Bounds; } bool vtkPolarAxesActor::CheckMembersConsistency() { if (this->MaximumAngle > 360.0 || this->MinimumAngle > 360.0) { // Incorrect MaximumRadius input vtkWarningMacro(<< "Cannot draw polar axis, Angle > 360.0: " << "MinimumAngle : " << this->MinimumAngle << " _ MaximumAngle: " << this->MaximumAngle); return false; } // Min/Max Radius if (vtkMathUtilities::FuzzyCompare(this->MaximumRadius, this->MinimumRadius)) { // MaximumRadius and this->MinimumRadius are too close vtkWarningMacro(<< "Maximum and Minimum Radius cannot be distinct: " << " MinimumRadius: " << this->MinimumRadius << " _ MaximumRadius: " << this->MaximumRadius); return false; } if (this->MaximumRadius <= 0.0 || this->MinimumRadius < 0.0) { // Incorrect MaximumRadius input vtkWarningMacro(<< "Cannot draw polar axis, Negative Radius value set: " << "MinimumRadius : " << this->MinimumRadius << " _ MaximumRadius: " << this->MaximumRadius); return false; } if (this->MaximumRadius < this->MinimumRadius) { // MaximumRadius should not be lower than MinimumRadius vtkWarningMacro(<< "Maximum Radius cannot be lower than Minimum one: " << "MinimumRadius : " << this->MinimumRadius << " _ MaximumRadius: " << this->MaximumRadius); return false; } // Min/Max Range if (vtkMathUtilities::FuzzyCompare(this->Range[0], this->Range[1])) { // MaximumRadius and this->MinimumRadius are too close vtkWarningMacro(<< "Maximum and Minimum Range cannot be distinct: " << " Range[0]: " << this->Range[0] << " _ Range[1]: " << this->Range[1]); return false; } if (this->Range[1] < this->Range[0]) { // Range bounds should respect ascending order vtkWarningMacro(<< "Maximum range bound cannot be lower than Minimum one: " << "Range[0] : " << this->Range[0] << " _ Range[1]: " << this->Range[1]); return false; } // Log Mode if (this->Log != 0 && this->Range[0] <= 0.0) { vtkWarningMacro(<< "Scale Set to Linear. Range value undefined for log scale enabled. " << "Current Range: (" << this->Range[0] << ", " << this->Range[1] << ")" << "Range must be > 0.0 for log scale to be enabled" << "."); this->Log = false; } // Range Step if (this->RequestedNumberOfPolarAxes == 0 && this->RequestedDeltaRangePolarAxes == 0.0) { vtkWarningMacro(<< "Either NumberOfPolarAxes or DeltaRangePolarAxes must be set. " << "Both values equal 0: can't perform automatic computation."); return false; } if (!this->AxisTickMatchesPolarAxes && (this->DeltaRangeMajor <= 0.0 || this->DeltaRangeMajor > fabs(this->Range[1] - this->Range[0]))) { vtkWarningMacro(<< "Axis Major Step invalid or range length invalid: " << "DeltaRangeMajor: " << this->DeltaRangeMajor << "_ Range length: " << fabs(this->Range[1] - this->Range[0])); return false; } if (!this->AxisTickMatchesPolarAxes && (this->DeltaRangeMinor <= 0.0 || this->DeltaRangeMinor > fabs(this->Range[1] - this->Range[0]))) { vtkWarningMacro(<< "Axis Minor Step or range length invalid: " << "DeltaRangeMinor: " << this->DeltaRangeMinor << "_ Range length: " << fabs(this->Range[1] - this->Range[0])); return false; } // Requested angle/number of radial axes if (this->RequestedNumberOfRadialAxes == 0 && this->RequestedDeltaAngleRadialAxes == 0.0) { vtkWarningMacro(<< "Either NumberOfRadialAxes or DeltaAngleRadialAxes must be set. " << "Both values equal 0: can't perform automatic computation."); return false; } // Angle Step if (!this->ArcTickMatchesRadialAxes && (this->DeltaAngleMajor <= 0.0 || this->DeltaAngleMajor >= 360.0 || this->DeltaAngleMinor <= 0.0 || this->DeltaAngleMinor >= 360.0)) { vtkWarningMacro(<< "Arc Delta Angle: " << "DeltaAngleMajor: " << this->DeltaAngleMajor << " _ DeltaAngleMinor: " << this->DeltaAngleMinor << "_ DeltaAngles should be in ]0.0, 360.0[ range. "); return false; } // Tick ratios range check if (this->PolarAxisTickRatioThickness < (1.0 / VTK_MAXIMUM_RATIO) || this->PolarAxisTickRatioThickness > VTK_MAXIMUM_RATIO || this->LastAxisTickRatioThickness < (1.0 / VTK_MAXIMUM_RATIO) || this->LastAxisTickRatioThickness > VTK_MAXIMUM_RATIO || this->ArcTickRatioThickness < (1.0 / VTK_MAXIMUM_RATIO) || this->ArcTickRatioThickness > VTK_MAXIMUM_RATIO || this->PolarAxisTickRatioSize < (1.0 / VTK_MAXIMUM_RATIO) || this->PolarAxisTickRatioSize > VTK_MAXIMUM_RATIO || this->LastAxisTickRatioSize < (1.0 / VTK_MAXIMUM_RATIO) || this->LastAxisTickRatioSize > VTK_MAXIMUM_RATIO || this->ArcTickRatioSize < (1.0 / VTK_MAXIMUM_RATIO) || this->ArcTickRatioSize > VTK_MAXIMUM_RATIO || this->TickRatioRadiusSize < (1.0 / VTK_MAXIMUM_RATIO) || this->TickRatioRadiusSize > VTK_MAXIMUM_RATIO) { // clang-format off vtkWarningMacro( << "A size/thickness ratio between major and minor ticks is way too large/thin: " << "PolarAxisTickRatioThickness: " << this->PolarAxisTickRatioThickness << "\n" << "LastAxisTickRatioThickness: " << this->LastAxisTickRatioThickness << "\n" << "ArcTickRatioThickness: " << this->ArcTickRatioThickness << "\n" << "PolarAxisTickRatioSize: " << this->PolarAxisTickRatioSize << "\n" << "LastAxisTickRatioSize: " << this->LastAxisTickRatioSize << "\n" << "ArcTickRatioSize: " << this->ArcTickRatioSize << "\n" << "TickRatioRadiusSize: " << this->TickRatioRadiusSize); // clang-format on return false; } return true; } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildAxes(vtkViewport* viewport) { if (!this->Camera) { vtkWarningMacro("vtkPolarAxesActor requires a Camera to be built."); return; } if (this->GetMTime() < this->BuildTime.GetMTime()) { this->AutoScale(viewport); return; } // ---------- Angles check ----------- // set angle range [0.0; 360.0] this->MaximumAngle = std::fmod(this->MaximumAngle, 360); this->MinimumAngle = std::fmod(this->MinimumAngle, 360); if (this->MaximumAngle < 0.0) { this->MaximumAngle += 360.0; } // set angle range [0.0; 360.0] if (this->MinimumAngle < 0.0) { this->MinimumAngle += 360.0; } // this->MaximumAngle < this->MinimumAngle is possible, no swap if (!this->CheckMembersConsistency()) { return; } // Determine the bounds this->CalculateBounds(); // Set polar axis endpoints vtkAxisActor* axis = this->PolarAxis; // compute ellipse angle double miniAngleEllipse = vtkPolarAxesActor::ComputeEllipseAngle(this->MinimumAngle, this->Ratio); // Set the start point and end point (world coord system) of the Polar Axis. double startPt[3], endPt[3]; startPt[0] = this->Pole[0] + this->MinimumRadius * cos(miniAngleEllipse); startPt[1] = this->Pole[1] + this->MinimumRadius * this->Ratio * sin(miniAngleEllipse); startPt[2] = this->Pole[2]; endPt[0] = this->Pole[0] + this->MaximumRadius * cos(miniAngleEllipse); endPt[1] = this->Pole[1] + this->MaximumRadius * this->Ratio * sin(miniAngleEllipse); endPt[2] = this->Pole[2]; axis->GetPoint1Coordinate()->SetValue(startPt); axis->GetPoint2Coordinate()->SetValue(endPt); // axis Type. We assume the polar graph is built in the local plane x-y if ((this->MinimumAngle > 45.0 && this->MinimumAngle < 135.0) || (this->MinimumAngle > 225.0 && this->MinimumAngle < 315.0)) { axis->SetAxisTypeToY(); } else { axis->SetAxisTypeToX(); } // Set axess attributes (range, tick location) this->SetCommonAxisAttributes(axis); this->SetPolarAxisAttributes(axis); // ------- Ticks thickness ------- // Polar Axis this->PolarAxis->GetAxisMajorTicksProperty()->SetLineWidth(this->PolarAxisMajorTickThickness); double minorThickness = this->PolarAxisTickRatioThickness * this->PolarAxisMajorTickThickness; minorThickness = std::max(minorThickness, 1.0); this->PolarAxis->GetAxisMinorTicksProperty()->SetLineWidth(minorThickness); // Last arc this->ArcTickActor->GetProperty()->SetLineWidth(this->ArcMajorTickThickness); minorThickness = std::max(this->ArcMajorTickThickness * this->ArcTickRatioThickness, 1.); this->ArcMinorTickActor->GetProperty()->SetLineWidth(minorThickness); // last polar axis line width is set in BuildRadialAxes() function // Build polar axis ticks if (this->Log) { this->BuildLabelsLog(); this->BuildPolarArcsLog(); } else { // Build polar axis labels this->BuildPolarAxisLabelsArcs(); } // Set title relative location from the axis if (this->PolarAxisTitleLocation == VTK_TITLE_BOTTOM) { this->PolarAxis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_BOTTOM); } else { this->PolarAxis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_POINT2); } // Build radial axes this->BuildRadialAxes(viewport); // Build ticks located on the last arc if (this->PolarTickVisibility) { this->BuildArcTicks(); } // color copy vtkProperty* prop = this->PolarArcsActor->GetProperty(); double color[3]; prop->GetColor(color); this->ArcTickActor->GetProperty()->SetColor(color); this->ArcMinorTickActor->GetProperty()->SetColor(color); // Update axis title follower vtkAxisFollower* follower = axis->GetTitleActor(); follower->SetAxis(axis); follower->SetEnableDistanceLOD(this->EnableDistanceLOD); follower->SetDistanceLODThreshold(this->DistanceLODThreshold); follower->SetEnableViewAngleLOD(this->EnableViewAngleLOD); follower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); // Update axis title follower vtkAxisFollower* expFollower = this->PolarAxis->GetExponentActor(); expFollower->SetAxis(this->PolarAxis); expFollower->SetEnableDistanceLOD(this->EnableDistanceLOD); expFollower->SetDistanceLODThreshold(this->DistanceLODThreshold); expFollower->SetEnableViewAngleLOD(this->EnableViewAngleLOD); expFollower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); // Update axis label followers int numberOfLabels = axis->GetNumberOfLabelsBuilt(); for (int i = 0; i < numberOfLabels; ++i) { vtkAxisFollower* labelActor = axis->GetLabelFollower(i); labelActor->SetAxis(axis); labelActor->SetEnableDistanceLOD(this->EnableDistanceLOD); labelActor->SetDistanceLODThreshold(this->DistanceLODThreshold); labelActor->SetEnableViewAngleLOD(this->EnableViewAngleLOD); labelActor->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); } // Build polar axis this->PolarAxis->BuildAxis(viewport, true); // Scale appropriately this->AutoScale(viewport); this->BuildTime.Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetCommonAxisAttributes(vtkAxisActor* axis) { vtkProperty* prop = this->GetProperty(); prop->SetAmbient(1.0); prop->SetDiffuse(0.0); axis->SetProperty(prop); axis->SetScreenSize(this->ScreenSize); // Common space and range attributes axis->SetCamera(this->Camera); axis->SetBounds(this->Bounds); // User defined range axis->SetRange(this->Range[0], this->Range[1]); // Axis scale type axis->SetLog(this->Log); // Major and minor ticks draw begins at Range[0] axis->SetMajorRangeStart(axis->GetRange()[0]); axis->SetMinorRangeStart(axis->GetRange()[0]); // Set polar axis ticks axis->SetTickVisibility(this->AxisTickVisibility && this->PolarTickVisibility); // Set polar axis minor ticks axis->SetMinorTicksVisible(this->AxisMinorTickVisibility && this->PolarTickVisibility); axis->SetTickLocation(this->TickLocation); } void vtkPolarAxesActor::SetPolarAxisAttributes(vtkAxisActor* axis) { // Set polar axis lines axis->SetAxisVisibility(this->PolarAxisVisibility); // #### Warning #### : Set this property BEFORE apply the ticks thickness of the vtkAxisActor // instances axis->SetAxisLinesProperty(this->PolarAxisProperty); // Set polar axis title axis->SetTitleVisibility(this->PolarTitleVisibility); axis->SetTitle(this->PolarAxisTitle); axis->SetTitleTextProperty(this->PolarAxisTitleTextProperty); axis->SetTitleOffset(this->PolarTitleOffset); // Set Labels exponent value axis->SetExponentOffset(this->PolarExponentOffset); if (this->ExponentLocation == VTK_EXPONENT_BOTTOM) { axis->SetExponentLocation(vtkAxisActor::VTK_ALIGN_BOTTOM); axis->SetExponentVisibility(true); } else if (this->ExponentLocation == VTK_EXPONENT_EXTERN) { axis->SetExponentLocation(vtkAxisActor::VTK_ALIGN_POINT2); axis->SetExponentVisibility(true); } else { axis->SetExponentVisibility(false); } // Set polar axis labels axis->SetLabelVisibility(this->PolarLabelVisibility); axis->SetLabelTextProperty(this->PolarAxisLabelTextProperty); axis->SetLabelOffset(this->PolarLabelOffset); double tickSize = this->PolarAxisMajorTickSize == 0.0 ? this->TickRatioRadiusSize * this->MaximumRadius : this->PolarAxisMajorTickSize; axis->SetMajorTickSize(tickSize); axis->SetMinorTickSize(this->PolarAxisTickRatioSize * tickSize); } //------------------------------------------------------------------------------ double vtkPolarAxesActor::FFix(double value) { int ivalue = static_cast(value); return ivalue; } //------------------------------------------------------------------------------ double vtkPolarAxesActor::FSign(double value, double sign) { value = fabs(value); if (sign < 0.) { value *= -1.; } return value; } //------------------------------------------------------------------------------ void vtkPolarAxesActor::CreateRadialAxes(int axisCount) { // If number of radial axes does not change, do nothing if (this->NumberOfRadialAxes == axisCount) { return; } this->RadialAxes.clear(); this->NumberOfRadialAxes = axisCount; // Create requested number of secondary radial axes this->RadialAxes.resize(this->NumberOfRadialAxes); for (int i = 0; i < this->NumberOfRadialAxes; ++i) { // Create axis of type X this->RadialAxes[i] = vtkSmartPointer::New(); vtkAxisActor* axis = this->RadialAxes[i].Get(); axis->SetAxisTypeToX(); axis->SetLabelVisibility(false); axis->SetUse2DMode(this->PolarAxis->GetUse2DMode()); axis->SetUseTextActor3D(this->PolarAxis->GetUseTextActor3D()); axis->LastMajorTickPointCorrectionOn(); } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildRadialAxes(vtkViewport* viewport) { bool originToPolarAxis = this->RadialAxesOriginToPolarAxis != 0.0; // set MaximumAngle and MinimumAngle range: [0.0; 360.0] double angleSection = (this->MaximumAngle > this->MinimumAngle) ? this->MaximumAngle - this->MinimumAngle : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle); if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle) || angleSection == 360.0) { angleSection = 360.0; } // Update delta angle of radial axes if (this->RequestedDeltaAngleRadialAxes > 0.0) { if (this->DeltaAngleRadialAxes != this->RequestedDeltaAngleRadialAxes) { this->DeltaAngleRadialAxes = this->RequestedDeltaAngleRadialAxes; } } else if (this->RequestedNumberOfRadialAxes > 1) { this->ComputeDeltaAngleRadialAxes(this->RequestedNumberOfRadialAxes); } bool positiveSection = false; double dAlpha = this->DeltaAngleRadialAxes; double alphaDeg, currentAlpha; // current ellipse angle double actualAngle; int i = 0; double minorThickness; double alphaStart = (originToPolarAxis) ? this->MinimumAngle + dAlpha : std::floor(this->MinimumAngle / dAlpha) * dAlpha + dAlpha; int nAxes; // Delta angle to big, only last radial axis if (this->DeltaAngleRadialAxes >= angleSection) { nAxes = 1; alphaStart = angleSection + this->MinimumAngle; } else if (this->RequestedNumberOfRadialAxes == 0) { nAxes = std::ceil(angleSection / dAlpha); } else { nAxes = std::min( this->RequestedNumberOfRadialAxes - 1, static_cast(std::ceil(angleSection / dAlpha))); } // init radial axis. Does nothing if number of radial axes doesn't change this->CreateRadialAxes(nAxes); char titleValue[64]; for (alphaDeg = alphaStart; i < this->NumberOfRadialAxes; alphaDeg += dAlpha, ++i) { const bool isLastAxis = i == this->NumberOfRadialAxes - 1; currentAlpha = alphaDeg; if (isLastAxis) { currentAlpha = angleSection + this->MinimumAngle; } // Calculate startpoint coordinates double thetaEllipse = vtkPolarAxesActor::ComputeEllipseAngle(currentAlpha, this->Ratio); double xStart = this->Pole[0] + this->MinimumRadius * cos(thetaEllipse); double yStart = this->Pole[1] + this->MinimumRadius * this->Ratio * sin(thetaEllipse); // Calculate endpoint coordinates double xEnd = this->Pole[0] + this->MaximumRadius * cos(thetaEllipse); double yEnd = this->Pole[1] + this->MaximumRadius * this->Ratio * sin(thetaEllipse); // radius angle (different from angle used to compute ellipse point) actualAngle = vtkMath::DegreesFromRadians(atan2(yEnd - this->Pole[1], xEnd - this->Pole[0])); // to keep angle positive for the last ones if (actualAngle > 0.0 || this->MinimumAngle < 180.0) { positiveSection = true; } if (actualAngle < 0.0 && positiveSection) { actualAngle += 360.0; } // Set radial axis endpoints vtkAxisActor* axis = this->RadialAxes[i]; // The last arc has its own property if (isLastAxis) { axis->SetAxisLinesProperty(this->LastRadialAxisProperty); axis->SetTitleTextProperty(this->LastRadialAxisTextProperty); } else { axis->SetAxisLinesProperty(this->SecondaryRadialAxesProperty); axis->SetTitleTextProperty(this->SecondaryRadialAxesTextProperty); } axis->GetPoint1Coordinate()->SetValue(xStart, yStart, this->Pole[2]); axis->GetPoint2Coordinate()->SetValue(xEnd, yEnd, this->Pole[2]); // set the range steps axis->SetDeltaRangeMajor(this->PolarAxis->GetDeltaRangeMajor()); axis->SetDeltaRangeMinor(this->PolarAxis->GetDeltaRangeMinor()); // Set common axis attributes this->SetCommonAxisAttributes(axis); // Set radial axis lines axis->SetAxisVisibility(this->RadialAxesVisibility); // Set radial axis title offset axis->SetTitleOffset(this->RadialTitleOffset); // Set title relative location from the axis if (this->RadialAxisTitleLocation == VTK_TITLE_BOTTOM) { axis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_BOTTOM); } else { axis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_POINT2); } // Set radial axis title with polar angle as title for non-polar axes if (this->PolarAxisVisibility && fabs(alphaDeg) < 2.) { // Prevent conflict between radial and polar axes titles axis->SetTitleVisibility(false); if (fabs(alphaDeg) < this->SmallestVisiblePolarAngle) { // Do not show radial axes too close to polar axis axis->SetAxisVisibility(false); } } else { // Use polar angle as a title for the radial axis axis->SetTitleVisibility(this->RadialTitleVisibility); std::ostringstream title; title.setf(std::ios::fixed, std::ios::floatfield); std::string radialAngleFormat = this->RadialAngleFormat ? vtk::to_std_format(this->RadialAngleFormat) : ""; VTK_FORMAT_IF_ERROR_RETURN(auto result = vtk::format_to_n( titleValue, sizeof(titleValue), radialAngleFormat, actualAngle); *result.out = '\0', ); title << titleValue << (this->RadialUnits ? " deg" : ""); axis->SetTitle(title.str()); // Update axis title followers axis->GetTitleActor()->SetAxis(axis); axis->GetTitleActor()->SetEnableDistanceLOD(this->EnableDistanceLOD); axis->GetTitleActor()->SetDistanceLODThreshold(this->DistanceLODThreshold); axis->GetTitleActor()->SetEnableViewAngleLOD(this->EnableViewAngleLOD); axis->GetTitleActor()->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); } // Ticks for the last radial axis if (angleSection != 360.0 && i == this->NumberOfRadialAxes - 1) { // axis Type. We assume the polar graph is built in the local plane x-y if ((actualAngle > 45.0 && actualAngle < 135.0) || (actualAngle > 225.0 && actualAngle < 315.0)) { axis->SetAxisTypeToY(); } else axis->SetAxisTypeToX(); // Set polar axis ticks double tickSize = this->LastRadialAxisMajorTickSize == 0.0 ? this->TickRatioRadiusSize * this->MaximumRadius : this->LastRadialAxisMajorTickSize; axis->SetTickVisibility(this->AxisTickVisibility && this->PolarTickVisibility); axis->SetMajorTickSize(tickSize); // Set polar axis minor ticks axis->SetMinorTicksVisible(this->AxisMinorTickVisibility && this->PolarTickVisibility); axis->SetMinorTickSize(this->LastAxisTickRatioSize * tickSize); // Set the tick orientation axis->SetTickLocation(this->TickLocation); axis->GetAxisMajorTicksProperty()->SetLineWidth(this->LastRadialAxisMajorTickThickness); minorThickness = this->LastRadialAxisMajorTickThickness * LastAxisTickRatioThickness; minorThickness = std::max(minorThickness, 1.0); axis->GetAxisMinorTicksProperty()->SetLineWidth(minorThickness); } else { axis->SetLabelVisibility(false); axis->SetTickVisibility(false); } if (viewport) { // Build to make sure properties are immediately set axis->BuildAxis(viewport, true); } } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildArcTicks() { bool originToPolarAxis = this->ArcTicksOriginToPolarAxis != 0.0; // set MaximumAngle and MinimumAngle range: [0.0; 360.0] double angleSection = (this->MaximumAngle > this->MinimumAngle) ? this->MaximumAngle - this->MinimumAngle : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle); if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle) || angleSection == 360.0) { angleSection = 360.0; } // Clear Tick Points this->ArcMajorTickPts->Reset(); this->ArcMinorTickPts->Reset(); // Arc tick actual size double tickSize = this->ArcMajorTickSize == 0.0 ? this->TickRatioRadiusSize * this->MaximumRadius : this->ArcMajorTickSize; double dAlpha = this->ArcTickMatchesRadialAxes ? this->DeltaAngleRadialAxes : this->DeltaAngleMajor; double alphaStart; alphaStart = (originToPolarAxis) ? this->MinimumAngle + dAlpha : std::floor(this->MinimumAngle / dAlpha) * dAlpha + dAlpha; for (double alphaDeg = alphaStart; alphaDeg < (angleSection + this->MinimumAngle); alphaDeg += dAlpha) { double thetaEllipse = ComputeEllipseAngle(alphaDeg, this->Ratio); this->StoreTicksPtsFromParamEllipse( this->MaximumRadius, thetaEllipse, tickSize, this->ArcMajorTickPts); } // Copy/paste should be replaced with a python-like generator to provide parameters to // StoreTicksPtsFromParamEllipse() // without running twice through the ellipse dAlpha = this->ArcTickMatchesRadialAxes ? this->DeltaAngleRadialAxes / 2.0 : this->DeltaAngleMinor; alphaStart = (originToPolarAxis) ? this->MinimumAngle + dAlpha : std::floor(this->MinimumAngle / dAlpha) * dAlpha + dAlpha; for (double alphaDeg = alphaStart; alphaDeg < (angleSection + this->MinimumAngle); alphaDeg += dAlpha) { double thetaEllipse = ComputeEllipseAngle(alphaDeg, this->Ratio); this->StoreTicksPtsFromParamEllipse( this->MaximumRadius, thetaEllipse, this->ArcTickRatioSize * tickSize, this->ArcMinorTickPts); } // set vtk object to draw the ticks vtkNew majorPts; vtkNew minorPts; vtkNew majorLines; vtkNew minorLines; vtkIdType ptIds[2]; int numTickPts, numLines, i; this->ArcTickPolyData->SetPoints(majorPts); this->ArcTickPolyData->SetLines(majorLines); this->ArcMinorTickPolyData->SetPoints(minorPts); this->ArcMinorTickPolyData->SetLines(minorLines); if (this->ArcTickVisibility) { numTickPts = this->ArcMajorTickPts->GetNumberOfPoints(); for (i = 0; i < numTickPts; i++) { majorPts->InsertNextPoint(this->ArcMajorTickPts->GetPoint(i)); } } if (this->ArcMinorTickVisibility) { // In 2D mode, the minorTickPts for yz portion or xz portion have been removed. numTickPts = this->ArcMinorTickPts->GetNumberOfPoints(); for (i = 0; i < numTickPts; i++) { minorPts->InsertNextPoint(this->ArcMinorTickPts->GetPoint(i)); } } // create lines if (this->ArcTickVisibility) { numLines = majorPts->GetNumberOfPoints() / 2; for (i = 0; i < numLines; i++) { ptIds[0] = 2 * i; ptIds[1] = 2 * i + 1; majorLines->InsertNextCell(2, ptIds); } } if (this->ArcMinorTickVisibility) { numLines = minorPts->GetNumberOfPoints() / 2; for (i = 0; i < numLines; i++) { ptIds[0] = 2 * i; ptIds[1] = 2 * i + 1; minorLines->InsertNextCell(2, ptIds); } } } void vtkPolarAxesActor::StoreTicksPtsFromParamEllipse( double a, double angleEllipseRad, double tickSize, vtkPoints* tickPts) { // plane point: point located in the plane of the ellipse // normal Dir Point: point located according to the direction of the z vector // inside direction: direction from the arc to its center for plane points, and positive z // direction // outside direction: direction from the arc to the outer radial direction for plane points, and // negative z direction int i; double planeInPt[3], planeOutPt[3], normalDirPt[3], invNormalDirPt[3]; if (!tickPts) { return; } double b = a * this->Ratio; double xArc = this->Pole[0] + a * cos(angleEllipseRad); double yArc = this->Pole[1] + b * sin(angleEllipseRad); double ellipsePt[3] = { xArc, yArc, this->Pole[2] }; double deltaVector[3] = { a * cos(angleEllipseRad), b * sin(angleEllipseRad), 0.0 }; vtkMath::Normalize(deltaVector); double orthoVector[3] = { 0.0, 0.0, 1.0 }; // init for (i = 0; i < 3; i++) { planeInPt[i] = planeOutPt[i] = normalDirPt[i] = invNormalDirPt[i] = ellipsePt[i]; } if (this->TickLocation == vtkAxisActor::VTK_TICKS_INSIDE || this->TickLocation == vtkAxisActor::VTK_TICKS_BOTH) { for (i = 0; i < 3; i++) { planeInPt[i] = ellipsePt[i] - tickSize * deltaVector[i]; } for (i = 0; i < 3; i++) { normalDirPt[i] = ellipsePt[i] + tickSize * orthoVector[i]; } } if (this->TickLocation == vtkAxisActor::VTK_TICKS_OUTSIDE || this->TickLocation == vtkAxisActor::VTK_TICKS_BOTH) { for (i = 0; i < 3; i++) { planeOutPt[i] = ellipsePt[i] + tickSize * deltaVector[i]; } for (i = 0; i < 3; i++) { invNormalDirPt[i] = ellipsePt[i] - tickSize * orthoVector[i]; } } vtkIdType nPoints = tickPts->GetNumberOfPoints(); tickPts->Resize(nPoints + 4); tickPts->SetNumberOfPoints(nPoints + 4); tickPts->SetPoint(nPoints, planeInPt); tickPts->SetPoint(nPoints + 1, planeOutPt); tickPts->SetPoint(nPoints + 2, normalDirPt); tickPts->SetPoint(nPoints + 3, invNormalDirPt); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildPolarAxisLabelsArcs() { double angleSection = (this->MaximumAngle > this->MinimumAngle) ? this->MaximumAngle - this->MinimumAngle : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle); // if Min and max angle are the same, interpret it as 360 segment opening if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle)) { angleSection = 360.0; } // Prepare trigonometric quantities vtkIdType arcResolution = static_cast(angleSection * this->PolarArcResolutionPerDegree * this->Ratio); // Principal Arc points vtkNew polarArcsPoints; this->PolarArcs->SetPoints(polarArcsPoints); // Principal Arc lines vtkNew polarArcsLines; this->PolarArcs->SetLines(polarArcsLines); // Secondary Arc points vtkNew secondaryPolarArcsPoints; this->SecondaryPolarArcs->SetPoints(secondaryPolarArcsPoints); // Secondary Arc lines vtkNew secondaryPolarArcsLines; this->SecondaryPolarArcs->SetLines(secondaryPolarArcsLines); vtkAxisActor* axis = this->PolarAxis; // Base ellipse arc value, refers to world coordinate system double axisLength = this->MaximumRadius - this->MinimumRadius; double rangeLength = axis->GetRange()[1] - axis->GetRange()[0]; double rangeScale = axisLength / rangeLength; // Update delta range of polar axes if (this->RequestedDeltaRangePolarAxes > 0.0) { if (this->DeltaRangePolarAxes != this->RequestedDeltaRangePolarAxes) { this->DeltaRangePolarAxes = this->RequestedDeltaRangePolarAxes; } } else if (this->RequestedNumberOfPolarAxes > 1) { this->ComputeDeltaRangePolarAxes(this->RequestedNumberOfPolarAxes); } int nAxes; // If range too big, only first and last arcs if (this->DeltaRangePolarAxes >= rangeLength) { nAxes = 2; } else if (this->RequestedNumberOfPolarAxes == 0) { nAxes = std::ceil(rangeLength / this->DeltaRangePolarAxes) + 1; } else { nAxes = std::min(this->RequestedNumberOfPolarAxes, static_cast(std::ceil(rangeLength / this->DeltaRangePolarAxes)) + 1); } if (this->NumberOfPolarAxes != nAxes) { this->NumberOfPolarAxes = nAxes; } // Label values refers to range values double valueRange = axis->GetRange()[0]; double deltaRange = this->DeltaRangePolarAxes; double deltaArc; vtkIdType pointIdOffset = 0; bool isOuterArc, isArcVisible, isLastArc; for (int i = 0; i < this->NumberOfPolarAxes; ++i) { deltaArc = (valueRange - axis->GetRange()[0]) * rangeScale; isLastArc = i == this->NumberOfPolarAxes - 1; isOuterArc = i == 0 || isLastArc; isArcVisible = isOuterArc || this->DrawPolarArcsGridlines; // Build polar arcs for non-zero values if (deltaArc + this->MinimumRadius > 0. && isArcVisible) { // Create elliptical polar arc with corresponding to this tick mark vtkNew arc; arc->SetCenter(this->Pole); arc->SetRatio(this->Ratio); arc->SetNormal(0., 0., 1.); arc->SetMajorRadiusVector(deltaArc + this->MinimumRadius, 0.0, 0.0); arc->SetStartAngle(this->MinimumAngle); arc->SetSegmentAngle(angleSection); arc->SetResolution(arcResolution); arc->Update(); if (isLastArc) { // Add polar arc vtkPoints* arcPoints = nullptr; vtkIdType nPoints = 0; if (arc->GetOutput()->GetNumberOfPoints() > 0) { arcPoints = arc->GetOutput()->GetPoints(); nPoints = arcResolution + 1; std::vector arcPointIds(nPoints); std::iota(arcPointIds.begin(), arcPointIds.end(), 0); for (vtkIdType j = 0; j < nPoints; ++j) { polarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j)); } polarArcsLines->InsertNextCell(nPoints, arcPointIds.data()); } } else { // Append new secondary polar arc to existing ones vtkPoints* arcPoints = nullptr; vtkIdType nPoints = 0; if (arc->GetOutput()->GetNumberOfPoints() > 0) { arcPoints = arc->GetOutput()->GetPoints(); nPoints = arcResolution + 1; std::vector arcPointIds(nPoints); std::iota(arcPointIds.begin(), arcPointIds.end(), pointIdOffset); for (vtkIdType j = 0; j < nPoints; ++j) { secondaryPolarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j)); } secondaryPolarArcsLines->InsertNextCell(nPoints, arcPointIds.data()); } // Update polyline cell offset pointIdOffset += nPoints; } } // Move to next value valueRange = std::min(valueRange + deltaRange, axis->GetRange()[1]); } // Update polar axis there because DeltaRange might be needed // And we use the range for labels axis->SetDeltaRangeMajor( this->AxisTickMatchesPolarAxes ? this->DeltaRangePolarAxes : this->DeltaRangeMajor); axis->SetDeltaRangeMinor( this->AxisTickMatchesPolarAxes ? this->DeltaRangePolarAxes / 2.0 : this->DeltaRangeMinor); // Prepare storage for polar axis labels std::list labelValList; int nTicks = this->AxisTickMatchesPolarAxes ? this->NumberOfPolarAxes : std::ceil(rangeLength / axis->GetDeltaRangeMajor()) + 1; valueRange = axis->GetRange()[0]; for (int i = 0; i < nTicks; ++i) { // Store value labelValList.push_back(valueRange); // Move to next value valueRange = std::min(valueRange + axis->GetDeltaRangeMajor(), axis->GetRange()[1]); } // set up vtk collection to store labels vtkNew labels; if (this->ExponentLocation != VTK_EXPONENT_LABELS) { // it modifies the values of labelValList std::string commonLbl = FindExponentAndAdjustValues(labelValList); axis->SetExponent(commonLbl); this->GetSignificantPartFromValues(labels, labelValList); } else { axis->SetExponent(""); // construct label string array labels->SetNumberOfValues(static_cast(labelValList.size())); std::list::iterator itList; vtkIdType i = 0; std::string polarLabelFormat = this->PolarLabelFormat ? vtk::to_std_format(this->PolarLabelFormat) : ""; for (itList = labelValList.begin(); itList != labelValList.end(); ++i, ++itList) { char label[64]; VTK_FORMAT_IF_ERROR_RETURN( auto result = vtk::format_to_n(label, sizeof(label), polarLabelFormat, *itList); *result.out = '\0', ); labels->SetValue(i, label); } } // Store labels axis->SetLabels(labels); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildPolarArcsLog() { double angleSection = (this->MaximumAngle > this->MinimumAngle) ? this->MaximumAngle - this->MinimumAngle : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle); // if Min and max angle are the same, interpret it as 360 segment opening if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle)) { angleSection = 360.0; } vtkIdType arcResolution = static_cast(angleSection * this->PolarArcResolutionPerDegree * this->Ratio); // Principal Arc points vtkNew polarArcsPoints; this->PolarArcs->SetPoints(polarArcsPoints); // Principal Arc lines vtkNew polarArcsLines; this->PolarArcs->SetLines(polarArcsLines); // Secondary Arc points vtkNew secondaryPolarArcsPoints; this->SecondaryPolarArcs->SetPoints(secondaryPolarArcsPoints); // Secondary Arc lines vtkNew secondaryPolarArcsLines; this->SecondaryPolarArcs->SetLines(secondaryPolarArcsLines); //--- prepare significant values ---- double miniAngleEllipseRad = ComputeEllipseAngle(this->MinimumAngle, this->Ratio); // Distance from Pole to Range[0] vtkAxisActor* axis = this->PolarAxis; double deltaVector[3], polarAxisUnitVect[3]; vtkMath::Subtract(axis->GetPoint2(), axis->GetPoint1(), deltaVector); vtkMath::Subtract(axis->GetPoint2(), axis->GetPoint1(), polarAxisUnitVect); vtkMath::Normalize(polarAxisUnitVect); // polar axis actor length double axisLength = vtkMath::Norm(deltaVector); // conversion factor double rangeScaleLog = axisLength / log10(axis->GetRange()[1] / axis->GetRange()[0]); // reuse deltaVector vtkMath::Subtract(axis->GetPoint1(), this->Pole, deltaVector); double distanceAxisPoint1FromPole = vtkMath::Norm(deltaVector); double base = 10.0; double log10Range0 = log10(axis->GetRange()[0]); double log10Range1 = log10(axis->GetRange()[1]); double lowBound = std::pow(base, static_cast(std::floor(log10Range0))); double upBound = std::pow(base, static_cast(ceil(log10Range1))); int i; double tickVal, tickRangeVal, indexTickRangeValue; vtkIdType pointIdOffset = 0; bool isInnerArc, isArcVisible, isLastArc; double a, b; double epsilon = 1e-8; for (indexTickRangeValue = lowBound; indexTickRangeValue <= upBound; indexTickRangeValue *= base) { // to keep major values as power of 10 tickRangeVal = indexTickRangeValue; isInnerArc = tickRangeVal > lowBound && tickRangeVal < upBound; isArcVisible = !isInnerArc || this->DrawPolarArcsGridlines; isLastArc = tickRangeVal == upBound; if (!isArcVisible) { continue; } tickRangeVal = std::max(tickRangeVal, axis->GetRange()[0]); tickRangeVal = std::min(tickRangeVal, axis->GetRange()[1]); // conversion range value to world value tickVal = (log10(tickRangeVal) - log10Range0) * rangeScaleLog; // Vector from Pole to major tick for (i = 0; i < 3; i++) { deltaVector[i] = polarAxisUnitVect[i] * (tickVal + distanceAxisPoint1FromPole); } if (vtkMath::Norm(deltaVector) == 0.0) { continue; } // epsilon is a very low value. vtkMathUtilities::FuzzyCompare is not fuzzy enough ... if (fabs(fabs(miniAngleEllipseRad) - vtkMath::Pi() / 2.0) < epsilon) { b = deltaVector[1] / sin(miniAngleEllipseRad); a = b / this->Ratio; } else { a = deltaVector[0] / cos(miniAngleEllipseRad); } // Create elliptical polar arc with corresponding to this tick mark vtkNew arc; arc->SetCenter(this->Pole); arc->SetRatio(this->Ratio); arc->SetNormal(0., 0., 1.); arc->SetMajorRadiusVector(a, 0.0, 0.0); arc->SetStartAngle(this->MinimumAngle); arc->SetSegmentAngle(angleSection); arc->SetResolution(arcResolution); arc->Update(); if (isLastArc) { // Add principal polar arc vtkPoints* arcPoints = nullptr; vtkIdType nPoints; if (arc->GetOutput()->GetNumberOfPoints() > 0) { arcPoints = arc->GetOutput()->GetPoints(); nPoints = arcResolution + 1; std::vector arcPointIds(nPoints); std::iota(arcPointIds.begin(), arcPointIds.end(), 0); for (vtkIdType j = 0; j < nPoints; ++j) { polarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j)); } polarArcsLines->InsertNextCell(nPoints, arcPointIds.data()); } } else { // Append new polar arc to existing ones vtkPoints* arcPoints = nullptr; vtkIdType nPoints = 0; if (arc->GetOutput()->GetNumberOfPoints() > 0) { arcPoints = arc->GetOutput()->GetPoints(); nPoints = arcResolution + 1; std::vector arcPointIds(nPoints); std::iota(arcPointIds.begin(), arcPointIds.end(), pointIdOffset); for (vtkIdType j = 0; j < nPoints; ++j) { secondaryPolarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j)); arcPointIds[j] = pointIdOffset + j; } secondaryPolarArcsLines->InsertNextCell(nPoints, arcPointIds.data()); } // Update polyline cell offset pointIdOffset += nPoints; } } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildLabelsLog() { // Prepare storage for polar axis labels std::list labelValList; vtkAxisActor* axis = this->PolarAxis; double base = 10.0; if (axis->GetRange()[0] <= 0.0) { return; } // define major ticks label values double indexTickRangeValue; double tickRangeVal; double log10Range0 = log10(axis->GetRange()[0]); double log10Range1 = log10(axis->GetRange()[1]); double lowBound = std::pow(base, static_cast(std::floor(log10Range0))); double upBound = std::pow(base, static_cast(ceil(log10Range1))); for (indexTickRangeValue = lowBound; indexTickRangeValue <= upBound; indexTickRangeValue *= base) { tickRangeVal = indexTickRangeValue; if (indexTickRangeValue < axis->GetRange()[0]) { tickRangeVal = axis->GetRange()[0]; } else if (indexTickRangeValue > axis->GetRange()[1]) { tickRangeVal = axis->GetRange()[1]; } labelValList.push_back(tickRangeVal); } // set up vtk collection to store labels vtkNew labels; if (this->ExponentLocation != VTK_EXPONENT_LABELS) { // it modifies the values of labelValList std::string commonLbl = FindExponentAndAdjustValues(labelValList); axis->SetExponent(commonLbl); this->GetSignificantPartFromValues(labels, labelValList); } else { axis->SetExponent(""); labels->SetNumberOfValues(static_cast(labelValList.size())); std::list::iterator itList; vtkIdType i = 0; for (itList = labelValList.begin(); itList != labelValList.end(); ++i, ++itList) { char label[64]; VTK_FORMAT_IF_ERROR_RETURN( auto result = vtk::format_to_n(label, sizeof(label), this->PolarLabelFormat, *itList); *result.out = '\0', ); labels->SetValue(i, label); } } // Store labels axis->SetLabels(labels); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::BuildPolarAxisLabelsArcsLog() { this->BuildPolarArcsLog(); this->BuildLabelsLog(); // Update axis title follower vtkAxisFollower* follower = this->PolarAxis->GetTitleActor(); follower->SetAxis(this->PolarAxis); follower->SetEnableDistanceLOD(this->EnableDistanceLOD); follower->SetDistanceLODThreshold(this->DistanceLODThreshold); follower->SetEnableViewAngleLOD(this->EnableViewAngleLOD); follower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); // Update axis title follower vtkAxisFollower* expFollower = this->PolarAxis->GetExponentActor(); expFollower->SetAxis(this->PolarAxis); expFollower->SetEnableDistanceLOD(this->EnableDistanceLOD); expFollower->SetDistanceLODThreshold(this->DistanceLODThreshold); expFollower->SetEnableViewAngleLOD(this->EnableViewAngleLOD); expFollower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); // Update axis label followers int labelCount = this->PolarAxis->GetNumberOfLabelsBuilt(); for (int i = 0; i < labelCount; ++i) { vtkAxisFollower* labelActor = this->PolarAxis->GetLabelFollower(i); labelActor->SetAxis(this->PolarAxis); labelActor->SetEnableDistanceLOD(this->EnableDistanceLOD); labelActor->SetDistanceLODThreshold(this->DistanceLODThreshold); labelActor->SetEnableViewAngleLOD(this->EnableViewAngleLOD); labelActor->SetViewAngleLODThreshold(this->ViewAngleLODThreshold); } } //------------------------------------------------------------------------------ std::string vtkPolarAxesActor::FindExponentAndAdjustValues(std::list& valuesList) { std::list::iterator itDouble; double exponentMean = 0.0; int count = 0; // find common exponent for (itDouble = valuesList.begin(); itDouble != valuesList.end(); ++itDouble) { if (*itDouble != 0.0) { double exponent = std::floor(log10(fabs(*itDouble))); exponentMean += exponent; count++; } } if (count == 0) { return ""; } exponentMean /= count; // adjust exponent to int value. Round it if fract part != 0.0 double intPart, fractPart; fractPart = modf(exponentMean, &intPart); if (exponentMean < 0.0) { if (fabs(fractPart) >= 0.5) { intPart -= 1.0; } } else { if (fabs(fractPart) >= 0.5) { intPart += 1.0; } } exponentMean = intPart; // shift every values for (itDouble = valuesList.begin(); itDouble != valuesList.end(); ++itDouble) { if (*itDouble != 0.0) { *itDouble /= std::pow(10, exponentMean); } } // Layout of the exponent: std::stringstream ss; int exponentInt = static_cast(fabs(exponentMean)); // add sign ss << (exponentMean >= 0.0 ? "+" : "-"); // add 0 for pow < 10 if (exponentInt < 10.0) { ss << "0"; } ss << exponentInt; return ss.str(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::GetSignificantPartFromValues( vtkStringArray* valuesStr, std::list& valuesList) { if (!valuesStr || valuesList.empty()) { return; } valuesStr->SetNumberOfValues(static_cast(valuesList.size())); std::list::iterator itList; vtkIdType i = 0; for (itList = valuesList.begin(); itList != valuesList.end(); ++i, ++itList) { char label[64]; if (this->ExponentLocation == VTK_EXPONENT_LABELS) { VTK_FORMAT_IF_ERROR_RETURN( auto result = vtk::format_to_n(label, sizeof(label), this->PolarLabelFormat, *itList); *result.out = '\0', ); valuesStr->SetValue(i, label); } else { std::stringstream ss; if (*itList == 0.0) { ss << std::fixed << std::setw(1) << std::setprecision(0) << 0.0; valuesStr->SetValue(i, ss.str().c_str()); continue; } // get pow of ten of the value to set the precision of the label int exponent = static_cast(std::floor(log10(fabs(*itList)))); if (exponent < 0) { ss << std::fixed << std::setw(1) << setprecision(-exponent) << *itList; } else { ss << std::fixed << setprecision(1) << *itList; } valuesStr->SetValue(i, ss.str().c_str()); } } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::AutoScale(vtkViewport* viewport) { // Scale polar axis title vtkAxisActor* axis = this->PolarAxis.Get(); double newTitleScale = vtkAxisFollower::AutoScale( viewport, this->Camera, this->ScreenSize, axis->GetTitleActor()->GetPosition()); axis->SetTitleScale(newTitleScale); // Scale polar axis labels axis->SetLabelScale(newTitleScale); // Loop over radial axes for (int i = 0; i < this->NumberOfRadialAxes; ++i) { axis = this->RadialAxes[i]; // Scale title newTitleScale = vtkAxisFollower::AutoScale( viewport, this->Camera, this->ScreenSize, axis->GetTitleActor()->GetPosition()); axis->SetTitleScale(newTitleScale); } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetPole(double p[3]) { this->Pole[0] = p[0]; this->Pole[1] = p[1]; this->Pole[2] = p[2]; // Update bounds this->CalculateBounds(); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetPole(double x, double y, double z) { this->Pole[0] = x; this->Pole[1] = y; this->Pole[2] = z; // Update bounds this->CalculateBounds(); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetMinimumRadius(double r) { this->MinimumRadius = r > 0. ? r : 0.; // Update bounds this->CalculateBounds(); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetMaximumRadius(double r) { this->MaximumRadius = r > 0. ? r : 0.; // Update bounds this->CalculateBounds(); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetMinimumAngle(double a) { if (a > 360.) { this->MinimumAngle = 360.; } else if (a < -360.) { this->MinimumAngle = -360.; } else { this->MinimumAngle = a; } // Update bounds this->CalculateBounds(); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetMaximumAngle(double a) { if (a > 360.) { this->MaximumAngle = 360.; } else if (a < -360.) { this->MaximumAngle = -360.; } else { this->MaximumAngle = a; } // Update bounds this->CalculateBounds(); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetUseTextActor3D(bool enable) { for (int i = 0; i < this->NumberOfRadialAxes; ++i) { this->RadialAxes[i]->SetUseTextActor3D(enable); } this->PolarAxis->SetUseTextActor3D(enable); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetUse2DMode(bool enable) { for (int i = 0; i < this->NumberOfRadialAxes; ++i) { this->RadialAxes[i]->SetUse2DMode(enable); } this->PolarAxis->SetUse2DMode(enable); this->Modified(); } //------------------------------------------------------------------------------ bool vtkPolarAxesActor::GetUse2DMode() { return this->PolarAxis->GetUse2DMode(); } //------------------------------------------------------------------------------ vtkCamera* vtkPolarAxesActor::GetCamera() { return this->Camera.Get(); } //------------------------------------------------------------------------------ vtkTextProperty* vtkPolarAxesActor::GetPolarAxisTitleTextProperty() { return this->PolarAxisTitleTextProperty.Get(); } //------------------------------------------------------------------------------ vtkTextProperty* vtkPolarAxesActor::GetPolarAxisLabelTextProperty() { return this->PolarAxisLabelTextProperty.Get(); } //------------------------------------------------------------------------------ vtkTextProperty* vtkPolarAxesActor::GetLastRadialAxisTextProperty() { return this->LastRadialAxisTextProperty.Get(); } //------------------------------------------------------------------------------ vtkTextProperty* vtkPolarAxesActor::GetSecondaryRadialAxesTextProperty() { return this->SecondaryRadialAxesTextProperty.Get(); } //------------------------------------------------------------------------------ vtkProperty* vtkPolarAxesActor::GetPolarAxisProperty() { return this->PolarAxisProperty.Get(); } //------------------------------------------------------------------------------ vtkProperty* vtkPolarAxesActor::GetLastRadialAxisProperty() { return this->LastRadialAxisProperty.Get(); } //------------------------------------------------------------------------------ vtkProperty* vtkPolarAxesActor::GetSecondaryRadialAxesProperty() { return this->SecondaryRadialAxesProperty.Get(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetPolarAxisProperty(vtkProperty* prop) { this->PolarAxisProperty->DeepCopy(prop); this->PolarAxisProperty->SetLineWidth(this->PolarAxisMajorTickThickness); this->Modified(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetPolarArcsProperty(vtkProperty* prop) { this->PolarArcsActor->SetProperty(prop); this->Modified(); } //------------------------------------------------------------------------------ vtkProperty* vtkPolarAxesActor::GetPolarArcsProperty() { return this->PolarArcsActor->GetProperty(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::SetSecondaryPolarArcsProperty(vtkProperty* prop) { this->SecondaryPolarArcsActor->SetProperty(prop); this->Modified(); } //------------------------------------------------------------------------------ vtkProperty* vtkPolarAxesActor::GetSecondaryPolarArcsProperty() { return this->SecondaryPolarArcsActor->GetProperty(); } //------------------------------------------------------------------------------ void vtkPolarAxesActor::ComputeDeltaRangePolarAxes(vtkIdType n) { double rangeLength = fabs(this->Range[1] - this->Range[0]); double step = rangeLength / (n - 1); if (this->DeltaRangePolarAxes != step) { this->DeltaRangePolarAxes = step; } } //------------------------------------------------------------------------------ void vtkPolarAxesActor::ComputeDeltaAngleRadialAxes(vtkIdType n) { double angleSection = (this->MaximumAngle > this->MinimumAngle) ? this->MaximumAngle - this->MinimumAngle : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle); // if Min and max angle are the same, interpret it as 360 segment opening if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle)) { angleSection = 360.0; } double step = this->ComputeIdealStep(n - 1, angleSection); if (step == 0.0) { step = angleSection / (n - 1); } if (this->DeltaAngleRadialAxes != step) { this->DeltaAngleRadialAxes = step; } } //------------------------------------------------------------------------------ double vtkPolarAxesActor::ComputeIdealStep(int subDivsRequired, double rangeLength, int maxSubDivs) { double pow10, pow10Start, pow10End; double rawStep, roundStep, roundStepSup; if (rangeLength == 0.0 || subDivsRequired >= maxSubDivs) { return 0.0; } if (subDivsRequired <= 1) { return rangeLength; } if (subDivsRequired <= 4) { return rangeLength / subDivsRequired; } // range step, if axis range is strictly subdivided by the number of ticks wished rawStep = rangeLength / subDivsRequired; // pow of 10 order of magnitude pow10Start = std::floor(log10(rawStep)); pow10End = -10.0; if (pow10End >= pow10Start) { pow10End -= 1.0; } if (rawStep <= std::pow(10, pow10End)) { return 0.0; } double dividend = rawStep; double pow10Step; double idealStep = 0.0; double subdivs = 1.0, subdivsSup = 1.0; int currentPow10Multiple; for (pow10 = pow10Start; pow10 >= pow10End; pow10 -= 1.0) { // 10.0, 1.0, 0.1, ... pow10Step = std::pow(10.0, pow10); // example: 4 = 0.4874 / 0.1 for pow10Step = 0.1 currentPow10Multiple = static_cast(dividend / pow10Step); // 0.4 = 4 * 0.1 roundStep = currentPow10Multiple * pow10Step; // 0.5 = 5 * 0.1 roundStepSup = (currentPow10Multiple + 1) * pow10Step; // currentIdealStep is the previous digits of the ideal step we seek subdivs = rangeLength / (idealStep + roundStep); subdivsSup = rangeLength / (idealStep + roundStepSup); if (fabs(subdivs - subDivsRequired) < 1.0 || fabs(subdivsSup - subDivsRequired) < 1.0) { // if currentStep + the current power of 10, is closer to the require tick count if (fabs(subdivs - subDivsRequired) > fabs(subdivsSup - subDivsRequired) && fabs(subdivsSup - subDivsRequired) < 1.0) { idealStep += roundStepSup; } // subdivs closer to subdiv than subdivsSup else { idealStep += roundStep; } break; } idealStep += roundStep; // 0.4874 - 0.4 for roundStep = 0.4 // remainder becomes dividend dividend = dividend - roundStep; } // if idealStep is too small if (static_cast(rangeLength / idealStep) > subDivsRequired) { idealStep = rawStep; } return idealStep; } double vtkPolarAxesActor::ComputeEllipseAngle(double angleInDegrees, double ratio) { double miniAngleEllipse; double minimumAngleRad = vtkMath::RadiansFromDegrees(angleInDegrees); minimumAngleRad = std::fmod(minimumAngleRad, 2.0 * vtkMath::Pi()); // result range: -pi / 2, pi / 2 miniAngleEllipse = atan(tan(minimumAngleRad) / ratio); // ellipse range: 0, 2 * pi if (minimumAngleRad > vtkMath::Pi() / 2 && minimumAngleRad <= vtkMath::Pi()) { miniAngleEllipse += vtkMath::Pi(); } else if (minimumAngleRad > vtkMath::Pi() && minimumAngleRad <= 1.5 * vtkMath::Pi()) { miniAngleEllipse -= vtkMath::Pi(); } return miniAngleEllipse; } VTK_ABI_NAMESPACE_END