1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkPolarAxesActor.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14  =========================================================================*/
15 #include "vtkPolarAxesActor.h"
16 
17 #include "vtkAxisFollower.h"
18 #include "vtkCamera.h"
19 #include "vtkCellArray.h"
20 #include "vtkCoordinate.h"
21 #include "vtkEllipseArcSource.h"
22 #include "vtkFollower.h"
23 #include "vtkMath.h"
24 #include "vtkMathUtilities.h"
25 #include "vtkNew.h"
26 #include "vtkObjectFactory.h"
27 #include "vtkPolyData.h"
28 #include "vtkPolyDataMapper.h"
29 #include "vtkProperty.h"
30 #include "vtkStringArray.h"
31 #include "vtkTextProperty.h"
32 #include "vtkViewport.h"
33 
34 #include <sstream>
35 
36 #define VTK_EXPONENT_AXES_ACTOR_RTOL (1. - 10. * VTK_DBL_EPSILON)
37 
38 vtkStandardNewMacro(vtkPolarAxesActor);
39 vtkCxxSetObjectMacro(vtkPolarAxesActor, Camera, vtkCamera);
40 vtkCxxSetObjectMacro(vtkPolarAxesActor, PolarAxisLabelTextProperty, vtkTextProperty);
41 vtkCxxSetObjectMacro(vtkPolarAxesActor, PolarAxisTitleTextProperty, vtkTextProperty);
42 vtkCxxSetObjectMacro(vtkPolarAxesActor, LastRadialAxisTextProperty, vtkTextProperty);
43 vtkCxxSetObjectMacro(vtkPolarAxesActor, SecondaryRadialAxesTextProperty, vtkTextProperty);
44 vtkCxxSetObjectMacro(vtkPolarAxesActor, LastRadialAxisProperty, vtkProperty);
45 vtkCxxSetObjectMacro(vtkPolarAxesActor, SecondaryRadialAxesProperty, vtkProperty);
46 
47 //-----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)48 void vtkPolarAxesActor::PrintSelf(ostream& os, vtkIndent indent)
49 {
50   this->Superclass::PrintSelf(os, indent);
51 
52   os << indent << "ScreenSize: " << this->ScreenSize << "\n";
53 
54   os << indent << "Number Of Radial Axes: " << this->NumberOfRadialAxes << endl;
55 
56   os << indent << "Range: (" << this->Range[0] << ", " << this->Range[1] << ")\n";
57 
58   os << indent << "Pole: (" << this->Pole[0] << ", " << this->Pole[1] << ", " << this->Pole[2]
59      << ")\n";
60 
61   os << indent << "Number of radial axes: " << this->NumberOfRadialAxes << endl;
62   os << indent << "Auto Subdivide Polar Axis: " << this->AutoSubdividePolarAxis << endl;
63   os << indent << "Abgle between two radial axes: " << this->DeltaAngleRadialAxes << endl;
64   os << indent << "Minimum Radius: " << this->MinimumRadius << endl;
65   os << indent << "Maximum Radius: " << this->MaximumRadius << endl;
66   os << indent << "Log Scale: " << (this->Log ? "On" : "Off") << endl;
67   os << indent << "Ratio: " << this->Ratio << endl;
68   os << indent << "Minimum Angle: " << this->MinimumAngle << endl;
69   os << indent << "Maximum Angle: " << this->MaximumAngle << endl;
70   os << indent << "Smallest Visible Polar Angle: " << this->SmallestVisiblePolarAngle << endl;
71   os << indent << "Radial Units (degrees): " << (this->RadialUnits ? "On\n" : "Off\n") << endl;
72 
73   if (this->Camera)
74   {
75     os << indent << "Camera:\n";
76     this->Camera->PrintSelf(os, indent.GetNextIndent());
77   }
78   else
79   {
80     os << indent << "Camera: (none)\n";
81   }
82 
83   os << indent << "EnableDistanceLOD: " << (this->EnableDistanceLOD ? "On" : "Off") << endl;
84   os << indent << "DistanceLODThreshold: " << this->DistanceLODThreshold << "\n";
85 
86   os << indent << "EnableViewAngleLOD: " << (this->EnableViewAngleLOD ? "On" : "Off") << endl;
87   os << indent << "ViewAngleLODThreshold: " << this->ViewAngleLODThreshold << "\n";
88 
89   os << indent << "Polar Axis Title: " << this->PolarAxisTitle << "\n";
90   os << indent << "Polar Label Format: " << this->PolarLabelFormat << "\n";
91   os << indent << "Title Scale: " << this->TitleScale << "\n";
92   os << indent << "Label Scale: " << this->LabelScale << "\n";
93   os << indent << "Radial Angle Format: " << this->RadialAngleFormat << "\n";
94   os << indent << "PolarAxisLabelTextProperty: " << this->PolarAxisLabelTextProperty << endl;
95   os << indent << "PolarAxisTitleTextProperty: " << this->PolarAxisTitleTextProperty << endl;
96   os << indent << "RadialAxisTextProperty: " << this->LastRadialAxisTextProperty << endl;
97   os << indent << "SecondaryRadialAxesTextProperty: " << this->SecondaryRadialAxesTextProperty
98      << endl;
99   os << indent << "Polar Axis Visibility: " << (this->PolarAxisVisibility ? "On\n" : "Off\n");
100   os << indent << "Polar Title Visibility: " << (this->PolarTitleVisibility ? "On" : "Off") << endl;
101   os << indent << "Polar Label Visibility: " << (this->PolarLabelVisibility ? "On" : "Off") << endl;
102   if (this->PolarAxisTitleLocation == VTK_TITLE_BOTTOM)
103   {
104     os << indent << "Polar Title Location: BOTTOM" << endl;
105   }
106   else if (this->PolarAxisTitleLocation == VTK_TITLE_EXTERN)
107   {
108     os << indent << "Polar Title Location: EXTERN" << endl;
109   }
110 
111   os << indent << "Polar Label exponent location: ";
112 
113   if (this->ExponentLocation == VTK_EXPONENT_BOTTOM)
114   {
115     os << " next to the polar axis title." << endl;
116   }
117   else if (this->ExponentLocation == VTK_EXPONENT_EXTERN)
118   {
119     os << " outer side." << endl;
120   }
121   else
122   {
123     os << " bound to labels." << endl;
124   }
125 
126   os << indent << "Radial Axes Visibility: " << (this->RadialAxesVisibility ? "On\n" : "Off\n");
127   os << indent << "Radial Title Visibility: " << (this->RadialTitleVisibility ? "On" : "Off")
128      << endl;
129   if (this->RadialAxisTitleLocation == VTK_TITLE_BOTTOM)
130   {
131     os << indent << "Radial Title Location: BOTTOM" << endl;
132   }
133   else if (this->RadialAxisTitleLocation == VTK_TITLE_EXTERN)
134   {
135     os << indent << "Radial Title Location: EXTERN" << endl;
136   }
137 
138   os << indent << "Polar Arcs Visibility: " << (this->PolarArcsVisibility ? "On" : "Off") << endl;
139   os << indent << "Draw Radial Gridlines: " << (this->DrawRadialGridlines ? "On" : "Off") << endl;
140   os << indent << "Draw Polar Arcs Gridlines: " << (this->DrawPolarArcsGridlines ? "On" : "Off")
141      << endl;
142   os << indent
143      << "Draw Radial Axes From Polar Axis: " << (this->RadialAxesOriginToPolarAxis ? "On" : "Off")
144      << endl;
145 
146   //--------------------- TICKS ------------------
147   os << indent << "TickLocation: " << this->TickLocation << endl;
148 
149   os << indent << "Ticks overall enabled: " << (this->PolarTickVisibility ? "On" : "Off") << endl;
150   os << indent
151      << "Draw Arc Ticks From Polar Axis: " << (this->ArcTicksOriginToPolarAxis ? "On" : "Off")
152      << endl;
153 
154   //--- major ticks ---
155   // polar axis and last radial axis
156   os << indent << "Axes Major Tick Visibility: " << (this->AxisTickVisibility ? "On" : "Off")
157      << endl;
158   if (this->AxisTickVisibility && this->PolarTickVisibility)
159   {
160     os << indent << "Axes Major Tick Step: " << this->DeltaRangeMajor << endl;
161     os << indent << "PolarAxis Major Tick Size: " << this->PolarAxisMajorTickSize << endl;
162     os << indent << "PolarAxis Major Tick Thickness: " << this->PolarAxisMajorTickThickness << endl;
163     if (this->RadialAxesVisibility)
164     {
165       os << indent << "Last Radial Axis Major Ticks Size: " << this->LastRadialAxisMajorTickSize
166          << endl;
167       os << indent
168          << "Last Radial Axis Major Ticks Thickness: " << this->LastRadialAxisMajorTickThickness
169          << endl;
170     }
171   }
172 
173   // last arc
174   os << indent << "Arc Major Ticks Visibility: " << (this->ArcTickVisibility ? "On" : "Off")
175      << endl;
176   if (this->ArcTickVisibility && this->PolarTickVisibility)
177   {
178     os << indent << "Arc Major Angle Step: " << this->DeltaAngleMajor << endl;
179     os << indent << "Arc Major Ticks Size: " << this->ArcMajorTickSize << endl;
180     os << indent << "Arc Major Ticks Thickness: " << this->ArcMajorTickThickness << endl;
181   }
182 
183   //--- minor ticks ---
184   //  polar axis and last radial axis
185   os << indent << "Axis Minor Ticks Visibility: " << (this->AxisMinorTickVisibility ? "On" : "Off")
186      << endl;
187   if (this->AxisMinorTickVisibility && this->PolarTickVisibility)
188   {
189     os << indent << "Axes Minor Tick Step: " << this->DeltaRangeMinor << endl;
190     os << indent
191        << "Ratio Between PolarAxis Major and Minor Tick : " << this->PolarAxisTickRatioSize << endl;
192     os << indent << "Ratio Between PolarAxis Major and Minor Tick Thickness : "
193        << this->PolarAxisTickRatioThickness << endl;
194     if (this->RadialAxesVisibility)
195     {
196       os << indent
197          << "Ratio Between LastAxis Major and Minor Tick : " << this->LastAxisTickRatioSize << endl;
198       os << indent << "Ratio Between LastAxis Major and Minor Tick Thickness: "
199          << this->LastAxisTickRatioThickness << endl;
200     }
201   }
202   os << indent << "Arc Minor Ticks Visibility: " << (this->ArcMinorTickVisibility ? "On" : "Off")
203      << endl;
204   if (this->ArcMinorTickVisibility && this->PolarTickVisibility)
205   {
206     os << indent << "Arc Minor Angle Step: " << this->DeltaAngleMinor << endl;
207     os << indent << "Ratio Between Last Arc Major and Minor Tick : " << this->ArcTickRatioSize
208        << endl;
209     os << indent
210        << "Ratio Between Last Arc Major and Minor Tick Thickness: " << this->ArcTickRatioThickness
211        << endl;
212   }
213 }
214 
215 //-----------------------------------------------------------------------------
vtkPolarAxesActor()216 vtkPolarAxesActor::vtkPolarAxesActor()
217   : vtkActor()
218 {
219   // Default bounds
220   this->Bounds[0] = -1.0;
221   this->Bounds[1] = 1.0;
222   this->Bounds[2] = -1.0;
223   this->Bounds[3] = 1.0;
224   this->Bounds[4] = -1.0;
225   this->Bounds[5] = 1.0;
226 
227   // Default pole coordinates
228   this->Pole[0] = 0.;
229   this->Pole[1] = 0.;
230   this->Pole[2] = 0.;
231 
232   // Invalid default number of polar arcs, and auto-calculate by default
233   this->AutoSubdividePolarAxis = true;
234 
235   // Ratio of the ellipse arc
236   this->Ratio = 1.0;
237 
238   // Polar Axis scale type
239   this->Log = 0;
240 
241   // Default minimum polar radius size
242   this->MinimumRadius = 0.0;
243 
244   // Default maximum polar radius size
245   this->MaximumRadius = 5.0;
246 
247   // Default minimum Range
248   this->Range[0] = 0.0;
249 
250   // Default maximum Range
251   this->Range[1] = 10.0;
252 
253   // Default minimum polar angle
254   this->MinimumAngle = 0.;
255 
256   // Default maximum polar angle
257   this->MaximumAngle = 90.;
258 
259   // Default smallest radial angle distinguishable from polar axis
260   this->SmallestVisiblePolarAngle = .5;
261 
262   // By default show angle units (degrees)
263   this->RadialUnits = true;
264 
265   this->Camera = nullptr;
266 
267   // Default text screen size
268   this->ScreenSize = 10.0;
269 
270   // Text properties of polar axis title and labels, with default color white
271   // Properties of the radial axes, with default color black
272   this->PolarAxisProperty = vtkProperty::New();
273   this->PolarAxisProperty->SetColor(0., 0., 0.);
274   this->PolarAxisTitleTextProperty = vtkTextProperty::New();
275   this->PolarAxisTitleTextProperty->SetOpacity(1.0);
276   this->PolarAxisTitleTextProperty->SetColor(1., 1., 1.);
277   this->PolarAxisTitleTextProperty->SetFontFamilyToArial();
278   this->PolarAxisLabelTextProperty = vtkTextProperty::New();
279   this->PolarAxisLabelTextProperty->SetColor(1., 1., 1.);
280   this->PolarAxisLabelTextProperty->SetFontFamilyToArial();
281 
282   // Create and set polar axis of type X
283   this->PolarAxis = vtkAxisActor::New();
284   this->PolarAxis->SetAxisTypeToX();
285 
286   this->PolarAxis->SetCalculateTitleOffset(0);
287   this->PolarAxis->SetCalculateLabelOffset(0);
288   this->PolarAxis->SetTitleOffset(10);
289   this->PolarAxis->SetLabelOffset(2);
290   this->PolarAxis->SetExponentOffset(5);
291   this->PolarAxis->LastMajorTickPointCorrectionOn();
292 
293   // Default distance LOD settings
294   this->EnableDistanceLOD = 1;
295   this->DistanceLODThreshold = .7;
296 
297   // Default view angle LOD settings
298   this->EnableViewAngleLOD = 1;
299   this->ViewAngleLODThreshold = .3;
300 
301   this->RadialAxes = nullptr;
302 
303   // Properties of the last radial axe, with default color black
304   this->LastRadialAxisProperty = vtkProperty::New();
305   this->LastRadialAxisProperty->SetAmbient(1.0);
306   this->LastRadialAxisProperty->SetDiffuse(0.0);
307   this->LastRadialAxisProperty->SetColor(0., 0., 0.);
308 
309   this->LastRadialAxisTextProperty = vtkTextProperty::New();
310   this->LastRadialAxisTextProperty->SetOpacity(1.0);
311   this->LastRadialAxisTextProperty->SetColor(1., 1., 1.);
312   this->LastRadialAxisTextProperty->SetFontFamilyToArial();
313 
314   // Properties of the secondaries radial axes, with default color black
315   this->SecondaryRadialAxesProperty = vtkProperty::New();
316   this->SecondaryRadialAxesProperty->SetAmbient(1.0);
317   this->SecondaryRadialAxesProperty->SetDiffuse(0.0);
318   this->SecondaryRadialAxesProperty->SetColor(0., 0., 0.);
319 
320   this->SecondaryRadialAxesTextProperty = vtkTextProperty::New();
321   this->SecondaryRadialAxesTextProperty->SetOpacity(1.0);
322   this->SecondaryRadialAxesTextProperty->SetColor(1., 1., 1.);
323   this->SecondaryRadialAxesTextProperty->SetFontFamilyToArial();
324 
325   // Create and set principal polar arcs and ancillary objects, with default color white
326   this->PolarArcs = vtkPolyData::New();
327   this->PolarArcsMapper = vtkPolyDataMapper::New();
328   this->PolarArcsMapper->SetInputData(this->PolarArcs);
329   this->PolarArcsActor = vtkActor::New();
330   this->PolarArcsActor->SetMapper(this->PolarArcsMapper);
331   this->PolarArcsActor->GetProperty()->SetColor(1., 1., 1.);
332 
333   // Create and set secondary polar arcs and ancillary objects, with default color white
334   this->SecondaryPolarArcs = vtkPolyData::New();
335   this->SecondaryPolarArcsMapper = vtkPolyDataMapper::New();
336   this->SecondaryPolarArcsMapper->SetInputData(this->SecondaryPolarArcs);
337   this->SecondaryPolarArcsActor = vtkActor::New();
338   this->SecondaryPolarArcsActor->SetMapper(this->SecondaryPolarArcsMapper);
339   this->SecondaryPolarArcsActor->GetProperty()->SetColor(1., 1., 1.);
340 
341   // Create the vtk Object for arc ticks
342   this->ArcMajorTickPts = vtkPoints::New();
343   this->ArcMinorTickPts = vtkPoints::New();
344 
345   this->ArcTickPolyData = vtkPolyData::New();
346   this->ArcMinorTickPolyData = vtkPolyData::New();
347 
348   this->ArcTickPolyDataMapper = vtkPolyDataMapper::New();
349   this->ArcTickPolyDataMapper->SetInputData(this->ArcTickPolyData);
350 
351   this->ArcMinorTickPolyDataMapper = vtkPolyDataMapper::New();
352   this->ArcMinorTickPolyDataMapper->SetInputData(this->ArcMinorTickPolyData);
353 
354   this->ArcTickActor = vtkActor::New();
355   this->ArcTickActor->SetMapper(this->ArcTickPolyDataMapper);
356 
357   this->ArcMinorTickActor = vtkActor::New();
358   this->ArcMinorTickActor->SetMapper(this->ArcMinorTickPolyDataMapper);
359 
360   // Default title for polar axis (sometimes also called "Radius")
361   this->PolarAxisTitle = new char[16];
362   snprintf(this->PolarAxisTitle, 16, "%s", "Radial Distance");
363 
364   this->PolarLabelFormat = new char[8];
365   snprintf(this->PolarLabelFormat, 8, "%s", "%-#6.3g");
366 
367   this->ExponentLocation = VTK_EXPONENT_LABELS;
368 
369   this->RadialAngleFormat = new char[8];
370   snprintf(this->RadialAngleFormat, 8, "%s", "%-#3.1f");
371 
372   this->RadialAxisTitleLocation = VTK_TITLE_BOTTOM;
373   this->PolarAxisTitleLocation = VTK_TITLE_BOTTOM;
374 
375   // By default all polar axis features are visible
376   this->PolarAxisVisibility = 1;
377   this->PolarTitleVisibility = 1;
378   this->PolarLabelVisibility = 1;
379 
380   this->TickLocation = vtkAxisActor::VTK_TICKS_BOTH;
381 
382   this->ArcTicksOriginToPolarAxis = 1.0;
383 
384   // ----- tick visibility -----
385   // overall visibility
386   this->PolarTickVisibility = 1;
387 
388   this->AxisTickVisibility = 1;
389   this->AxisMinorTickVisibility = 0;
390 
391   this->ArcTickVisibility = 1;
392   this->ArcMinorTickVisibility = 0;
393 
394   // tick size
395   this->PolarAxisMajorTickSize = 0;
396   this->PolarAxisTickRatioSize = 0.3;
397 
398   this->LastRadialAxisMajorTickSize = 0;
399   this->LastAxisTickRatioSize = 0.3;
400 
401   this->ArcMajorTickSize = 0;
402   this->ArcTickRatioSize = 0.3;
403 
404   // tick thickness
405   this->PolarAxisMajorTickThickness = 1.0;
406   this->PolarAxisTickRatioThickness = 0.5;
407 
408   this->LastRadialAxisMajorTickThickness = 1.0;
409   this->LastAxisTickRatioThickness = 0.5;
410 
411   this->ArcMajorTickThickness = 1.0;
412   this->ArcTickRatioThickness = 0.5;
413 
414   // Step between 2 major ticks, in range value (values displayed on the axis).
415   this->DeltaRangeMajor = 1.0;
416 
417   // Step between 2 minor ticks, in range value (values displayed on the axis).
418   this->DeltaRangeMinor = 0.5 * this->DeltaRangeMajor;
419 
420   // Angle between 2 major ticks on the last arc.
421   this->DeltaAngleMajor = 10.0;
422 
423   // Angle between 2 minor ticks on the last arc.
424   this->DeltaAngleMinor = 0.5 * this->DeltaAngleMajor;
425 
426   this->RadialAxesOriginToPolarAxis = 1;
427   this->DeltaAngleRadialAxes = 45.0;
428   this->NumberOfRadialAxes = 0;
429   this->RequestedNumberOfRadialAxes = 0;
430 
431   // By default all radial axes features are visible
432   this->RadialAxesVisibility = 1;
433   this->RadialTitleVisibility = 1;
434 
435   // By default polar arcs are visible
436   this->PolarArcsVisibility = 1;
437 
438   // By default inner radial lines and polar arcs lines are visible
439   this->DrawRadialGridlines = 1;
440   this->DrawPolarArcsGridlines = 1;
441 
442   // Default title scale
443   this->TitleScale = -1.;
444 
445   // Default label scale
446   this->LabelScale = -1.;
447 }
448 
449 //-----------------------------------------------------------------------------
~vtkPolarAxesActor()450 vtkPolarAxesActor::~vtkPolarAxesActor()
451 {
452   this->SetCamera(nullptr);
453 
454   if (this->PolarAxisProperty)
455   {
456     this->PolarAxisProperty->Delete();
457   }
458 
459   if (this->LastRadialAxisProperty)
460   {
461     this->LastRadialAxisProperty->Delete();
462   }
463 
464   if (this->SecondaryRadialAxesProperty)
465   {
466     this->SecondaryRadialAxesProperty->Delete();
467   }
468 
469   delete[] this->PolarLabelFormat;
470   this->PolarLabelFormat = nullptr;
471 
472   delete[] this->RadialAngleFormat;
473   this->RadialAngleFormat = nullptr;
474 
475   delete[] this->PolarAxisTitle;
476   this->PolarAxisTitle = nullptr;
477 
478   if (this->PolarAxisTitleTextProperty)
479   {
480     this->PolarAxisTitleTextProperty->Delete();
481     this->PolarAxisTitleTextProperty = nullptr;
482   }
483 
484   if (this->PolarAxisLabelTextProperty)
485   {
486     this->PolarAxisLabelTextProperty->Delete();
487     this->PolarAxisLabelTextProperty = nullptr;
488   }
489 
490   if (this->LastRadialAxisTextProperty)
491   {
492     this->LastRadialAxisTextProperty->Delete();
493     this->LastRadialAxisTextProperty = nullptr;
494   }
495 
496   if (this->SecondaryRadialAxesTextProperty)
497   {
498     this->SecondaryRadialAxesTextProperty->Delete();
499     this->SecondaryRadialAxesTextProperty = nullptr;
500   }
501 
502   if (this->PolarAxis)
503   {
504     this->PolarAxis->Delete();
505     this->PolarAxis = nullptr;
506   }
507 
508   if (this->RadialAxes)
509   {
510     for (int i = 0; i < this->NumberOfRadialAxes; ++i)
511     {
512       if (this->RadialAxes[i])
513       {
514         this->RadialAxes[i]->Delete();
515         this->RadialAxes[i] = nullptr;
516       }
517     }
518     delete[] this->RadialAxes;
519     this->RadialAxes = nullptr;
520   }
521 
522   if (this->PolarArcs)
523   {
524     this->PolarArcs->Delete();
525     this->PolarArcs = nullptr;
526   }
527   if (this->PolarArcsMapper)
528   {
529     this->PolarArcsMapper->Delete();
530     this->PolarArcsMapper = nullptr;
531   }
532   if (this->PolarArcsActor)
533   {
534     this->PolarArcsActor->Delete();
535     this->PolarArcsActor = nullptr;
536   }
537 
538   if (this->SecondaryPolarArcs)
539   {
540     this->SecondaryPolarArcs->Delete();
541     this->SecondaryPolarArcs = nullptr;
542   }
543   if (this->SecondaryPolarArcsMapper)
544   {
545     this->SecondaryPolarArcsMapper->Delete();
546     this->SecondaryPolarArcsMapper = nullptr;
547   }
548   if (this->SecondaryPolarArcsActor)
549   {
550     this->SecondaryPolarArcsActor->Delete();
551     this->SecondaryPolarArcsActor = nullptr;
552   }
553 
554   // ticks related objects
555   if (this->ArcMajorTickPts)
556   {
557     this->ArcMajorTickPts->Delete();
558     this->ArcMajorTickPts = nullptr;
559   }
560   if (this->ArcMinorTickPts)
561   {
562     this->ArcMinorTickPts->Delete();
563     this->ArcMinorTickPts = nullptr;
564   }
565   if (this->ArcTickPolyData)
566   {
567     this->ArcTickPolyData->Delete();
568     this->ArcTickPolyData = nullptr;
569   }
570   if (this->ArcMinorTickPolyData)
571   {
572     this->ArcMinorTickPolyData->Delete();
573     this->ArcMinorTickPolyData = nullptr;
574   }
575   if (this->ArcTickPolyDataMapper)
576   {
577     this->ArcTickPolyDataMapper->Delete();
578     this->ArcTickPolyDataMapper = nullptr;
579   }
580   if (this->ArcMinorTickPolyDataMapper)
581   {
582     this->ArcMinorTickPolyDataMapper->Delete();
583     this->ArcMinorTickPolyDataMapper = nullptr;
584   }
585   if (this->ArcTickActor)
586   {
587     this->ArcTickActor->Delete();
588     this->ArcTickActor = nullptr;
589   }
590   if (this->ArcMinorTickActor)
591   {
592     this->ArcMinorTickActor->Delete();
593     this->ArcMinorTickActor = nullptr;
594   }
595 }
596 
597 //-----------------------------------------------------------------------------
RenderOpaqueGeometry(vtkViewport * viewport)598 int vtkPolarAxesActor::RenderOpaqueGeometry(vtkViewport* viewport)
599 {
600   // Initialization
601   int renderedSomething = 0;
602   if (!this->Camera)
603   {
604     vtkErrorMacro(<< "No camera!");
605     return renderedSomething;
606   }
607 
608   this->BuildAxes(viewport);
609 
610   // Render the polar axis
611   if (this->PolarAxisVisibility)
612   {
613     renderedSomething += this->PolarAxis->RenderOpaqueGeometry(viewport);
614   }
615 
616   // Render the radial axes
617   if (this->RadialAxesVisibility)
618   {
619     bool isInnerAxis, isAxisVisible;
620     for (int i = 0; i < this->NumberOfRadialAxes; ++i)
621     {
622       isInnerAxis = (i != this->NumberOfRadialAxes - 1) ||
623         (vtkMathUtilities::FuzzyCompare(MaximumAngle, MinimumAngle));
624       isAxisVisible = !isInnerAxis || this->DrawRadialGridlines;
625       if (this->RadialAxesVisibility && isAxisVisible)
626       {
627         renderedSomething += this->RadialAxes[i]->RenderOpaqueGeometry(viewport);
628       }
629     }
630   }
631 
632   // Render the polar arcs
633   if (this->PolarArcsVisibility)
634   {
635     renderedSomething += this->PolarArcsActor->RenderOpaqueGeometry(viewport);
636     renderedSomething += this->SecondaryPolarArcsActor->RenderOpaqueGeometry(viewport);
637 
638     if (this->PolarTickVisibility)
639     {
640       if (this->ArcTickVisibility)
641       {
642         renderedSomething += this->ArcTickActor->RenderOpaqueGeometry(viewport);
643       }
644       if (this->ArcMinorTickVisibility)
645       {
646         renderedSomething += this->ArcMinorTickActor->RenderOpaqueGeometry(viewport);
647       }
648     }
649   }
650   return renderedSomething;
651 }
652 
RenderOverlay(vtkViewport * viewport)653 int vtkPolarAxesActor::RenderOverlay(vtkViewport* viewport)
654 {
655   int renderedSomething = 0;
656 
657   if (this->PolarAxisVisibility && this->PolarAxis->GetUse2DMode())
658   {
659     renderedSomething += this->PolarAxis->RenderOverlay(viewport);
660   }
661 
662   if (this->RadialAxesVisibility)
663   {
664     for (int i = 0; i < this->NumberOfRadialAxes; ++i)
665     {
666       if (this->RadialAxes[i]->GetUse2DMode())
667       {
668         renderedSomething += this->RadialAxes[i]->RenderOverlay(viewport);
669       }
670     }
671   }
672   return renderedSomething;
673 }
674 
675 //-----------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow * win)676 void vtkPolarAxesActor::ReleaseGraphicsResources(vtkWindow* win)
677 {
678   this->PolarAxis->ReleaseGraphicsResources(win);
679   for (int i = 0; i < this->NumberOfRadialAxes; ++i)
680   {
681     this->RadialAxes[i]->ReleaseGraphicsResources(win);
682   }
683   this->SecondaryPolarArcsActor->ReleaseGraphicsResources(win);
684   this->PolarArcsActor->ReleaseGraphicsResources(win);
685 }
686 
687 //-----------------------------------------------------------------------------
CalculateBounds()688 void vtkPolarAxesActor::CalculateBounds()
689 {
690   // Fetch angles, at this point it is already known that angular sector <= 360.
691   double minAngle = this->MinimumAngle;
692   double maxAngle = this->MaximumAngle;
693 
694   // Ensure that angles are not both < -180 nor both > 180 degrees
695   if (maxAngle < -180.)
696   {
697     // Increment angles modulo 360 degrees
698     minAngle += 360.;
699     maxAngle += 360.;
700   }
701   else if (minAngle > 180.)
702   {
703     // Decrement angles modulo 360 degrees
704     minAngle -= 360.;
705     maxAngle -= 360.;
706   }
707 
708   // Prepare trigonometric quantities
709   double thetaMin = vtkMath::RadiansFromDegrees(minAngle);
710   double cosThetaMin = cos(thetaMin);
711   double sinThetaMin = sin(thetaMin);
712   double thetaMax = vtkMath::RadiansFromDegrees(maxAngle);
713   double cosThetaMax = cos(thetaMax);
714   double sinThetaMax = sin(thetaMax);
715 
716   // Calculate extremal cosines across angular sector
717   double minCos;
718   double maxCos;
719   if (minAngle * maxAngle < 0.)
720   {
721     // Angular sector contains null angle
722     maxCos = 1.;
723     if (minAngle < 180. && maxAngle > 180.)
724     {
725       // Angular sector also contains flat angle
726       minCos = -1.;
727     }
728     else
729     {
730       // Angular sector does not contain flat angle
731       minCos = cosThetaMin < cosThetaMax ? cosThetaMin : cosThetaMax;
732     }
733   }
734   else if (minAngle < 180. && maxAngle > 180.)
735   {
736     // Angular sector does not contain flat angle (and not null angle)
737     minCos = -1.;
738     maxCos = cosThetaMax > cosThetaMin ? cosThetaMax : cosThetaMin;
739   }
740   else
741   {
742     // Angular sector does not contain flat nor null angle
743     minCos = cosThetaMin < cosThetaMax ? cosThetaMin : cosThetaMax;
744     maxCos = cosThetaMax > cosThetaMin ? cosThetaMax : cosThetaMin;
745   }
746 
747   // Calculate extremal sines across angular sector
748   double minSin;
749   double maxSin;
750   if (minAngle < -90. && maxAngle > -90.)
751   {
752     // Angular sector contains negative right angle
753     minSin = -1.;
754     if (minAngle < 90. && maxAngle > 90.)
755     {
756       // Angular sector also contains positive right angle
757       maxSin = 1.;
758     }
759     else
760     {
761       // Angular sector contain does not contain positive right angle
762       maxSin = sinThetaMax > sinThetaMin ? sinThetaMax : sinThetaMin;
763     }
764   }
765   else if (minAngle < 90. && maxAngle > 90.)
766   {
767     // Angular sector contains positive right angle (and not negative one)
768     minSin = sinThetaMin < sinThetaMax ? sinThetaMin : sinThetaMax;
769     maxSin = 1.;
770   }
771   else
772   {
773     // Angular sector contain does not contain either right angle
774     minSin = sinThetaMin < sinThetaMax ? sinThetaMin : sinThetaMax;
775     maxSin = sinThetaMax > sinThetaMin ? sinThetaMax : sinThetaMin;
776   }
777 
778   // Now calculate bounds
779   // xmin
780   this->Bounds[0] = this->Pole[0] + this->MaximumRadius * minCos;
781   // xmax
782   this->Bounds[1] = this->Pole[0] + this->MaximumRadius * maxCos;
783   // ymin
784   this->Bounds[2] = this->Pole[1] + this->MaximumRadius * minSin;
785   // ymax
786   this->Bounds[3] = this->Pole[1] + this->MaximumRadius * maxSin;
787   // zmin
788   this->Bounds[4] = this->Pole[2];
789   // zmax
790   this->Bounds[5] = this->Pole[2];
791 
792   // Update modification time of bounds
793   this->BoundsMTime.Modified();
794 }
795 
796 //-----------------------------------------------------------------------------
GetBounds(double bounds[6])797 void vtkPolarAxesActor::GetBounds(double bounds[6])
798 {
799   for (int i = 0; i < 6; i++)
800   {
801     bounds[i] = this->Bounds[i];
802   }
803 }
804 
805 //-----------------------------------------------------------------------------
GetBounds(double & xmin,double & xmax,double & ymin,double & ymax,double & zmin,double & zmax)806 void vtkPolarAxesActor::GetBounds(
807   double& xmin, double& xmax, double& ymin, double& ymax, double& zmin, double& zmax)
808 {
809   xmin = this->Bounds[0];
810   xmax = this->Bounds[1];
811   ymin = this->Bounds[2];
812   ymax = this->Bounds[3];
813   zmin = this->Bounds[4];
814   zmax = this->Bounds[5];
815 }
816 
817 //-----------------------------------------------------------------------------
GetBounds()818 double* vtkPolarAxesActor::GetBounds()
819 {
820   return this->Bounds;
821 }
822 
CheckMembersConsistency()823 bool vtkPolarAxesActor::CheckMembersConsistency()
824 {
825   if (this->MaximumAngle > 360.0 || this->MinimumAngle > 360.0)
826   {
827     // Incorrect MaximumRadius input
828     vtkWarningMacro(<< "Cannot draw polar axis, Angle > 360.0: "
829                     << "MinimumAngle : " << this->MinimumAngle
830                     << " _ MaximumAngle: " << this->MaximumAngle);
831     return false;
832   }
833 
834   // Min/Max Radius
835   if (vtkMathUtilities::FuzzyCompare(this->MaximumRadius, this->MinimumRadius))
836   {
837     // MaximumRadius and this->MinimumRadius are too close
838     vtkWarningMacro(<< "Maximum and Minimum Radius cannot be distinct: "
839                     << " MinimumRadius: " << this->MinimumRadius
840                     << " _ MaximumRadius: " << this->MaximumRadius);
841     return false;
842   }
843 
844   if (this->MaximumRadius <= 0.0 || this->MinimumRadius < 0.0)
845   {
846     // Incorrect MaximumRadius input
847     vtkWarningMacro(<< "Cannot draw polar axis, Negative Radius value set: "
848                     << "MinimumRadius : " << this->MinimumRadius
849                     << " _ MaximumRadius: " << this->MaximumRadius);
850     return false;
851   }
852 
853   // Min/Max Range
854   if (vtkMathUtilities::FuzzyCompare(this->Range[0], this->Range[1]))
855   {
856     // MaximumRadius and this->MinimumRadius are too close
857     vtkWarningMacro(<< "Maximum and Minimum Range cannot be distinct: "
858                     << " Range[0]: " << this->Range[0] << " _ Range[1]: " << this->Range[1]);
859     return false;
860   }
861 
862   // Log Mode
863   if (this->Log != 0 && this->Range[0] <= 0.0)
864   {
865     vtkWarningMacro(<< "Scale Set to Linear. Range value undefined for log scale enabled. "
866                     << "Current Range: (" << this->Range[0] << ", " << this->Range[1] << ")"
867                     << "Range must be > 0.0 for log scale to be enabled"
868                     << ".");
869 
870     this->Log = 0;
871   }
872 
873   // Range Step
874   if (this->DeltaRangeMajor <= 0.0 ||
875     (this->DeltaRangeMajor > fabs(this->Range[1] - this->Range[0]) && !AutoSubdividePolarAxis))
876   {
877     vtkWarningMacro(
878       << "Axis Major Step or Range length invalid: "
879       << "DeltaRangeMajor: " << this->DeltaRangeMajor
880       << "_ Range length: " << fabs(this->Range[1] - this->Range[0])
881       << " _ Enable AutoSubdividePolarAxis to get a proper DeltaRangeMajor or set it yourself");
882     return false;
883   }
884   if (this->DeltaRangeMinor <= 0.0 ||
885     (this->DeltaRangeMinor > fabs(this->Range[1] - this->Range[0]) && !AutoSubdividePolarAxis))
886   {
887     vtkWarningMacro(
888       << "Axis Minor Step or range length invalid: "
889       << "DeltaRangeMinor: " << this->DeltaRangeMinor
890       << "_ Range length: " << fabs(this->Range[1] - this->Range[0])
891       << " _ Enable AutoSubdividePolarAxis to get a proper DeltaRangeMinor or set it yourself");
892     return false;
893   }
894 
895   // Angle Step
896   if (this->DeltaAngleMajor <= 0.0 || this->DeltaAngleMajor >= 360.0 ||
897     this->DeltaAngleMinor <= 0.0 || this->DeltaAngleMinor >= 360.0)
898   {
899     vtkWarningMacro(<< "Arc Delta Angle: "
900                     << "DeltaAngleMajor: " << this->DeltaAngleMajor << " _ DeltaAngleMinor: "
901                     << this->DeltaAngleMinor << "_ DeltaAngles should be in ]0.0, 360.0[ range. ");
902     return false;
903   }
904 
905   // Angle Step
906   if (this->DeltaAngleRadialAxes <= 0.0 || this->DeltaAngleRadialAxes >= 360.0)
907   {
908     vtkWarningMacro(<< "Delta Angle for radial axes: "
909                     << "DeltaAngleRadialAxes: " << this->DeltaAngleRadialAxes
910                     << "_ DeltaAngleRadialAxes should be in ]0.0, 360.0[ range. ");
911     return false;
912   }
913 
914   // Tick ratios range check
915   if (this->PolarAxisTickRatioThickness < (1.0 / VTK_MAXIMUM_RATIO) ||
916     this->PolarAxisTickRatioThickness > VTK_MAXIMUM_RATIO ||
917     this->LastAxisTickRatioThickness < (1.0 / VTK_MAXIMUM_RATIO) ||
918     this->LastAxisTickRatioThickness > VTK_MAXIMUM_RATIO ||
919     this->ArcTickRatioThickness < (1.0 / VTK_MAXIMUM_RATIO) ||
920     this->ArcTickRatioThickness > VTK_MAXIMUM_RATIO ||
921     this->PolarAxisTickRatioSize < (1.0 / VTK_MAXIMUM_RATIO) ||
922     this->PolarAxisTickRatioSize > VTK_MAXIMUM_RATIO ||
923     this->LastAxisTickRatioSize < (1.0 / VTK_MAXIMUM_RATIO) ||
924     this->LastAxisTickRatioSize > VTK_MAXIMUM_RATIO ||
925     this->ArcTickRatioSize < (1.0 / VTK_MAXIMUM_RATIO) ||
926     this->ArcTickRatioSize > VTK_MAXIMUM_RATIO)
927   {
928     vtkWarningMacro(<< "A size/thickness ratio between major and minor ticks is way too large: "
929                     << "PolarAxisTickRatioThickness: " << this->PolarAxisTickRatioThickness
930                     << "LastAxisTickRatioThickness: " << this->LastAxisTickRatioThickness
931                     << "ArcTickRatioThickness: " << this->ArcTickRatioThickness
932                     << "PolarAxisTickRatioSize: " << this->PolarAxisTickRatioSize
933                     << "LastAxisTickRatioSize: " << this->LastAxisTickRatioSize
934                     << "ArcTickRatioSize: " << this->ArcTickRatioSize);
935     return false;
936   }
937   return true;
938 }
939 
940 //-----------------------------------------------------------------------------
BuildAxes(vtkViewport * viewport)941 void vtkPolarAxesActor::BuildAxes(vtkViewport* viewport)
942 {
943   if (this->GetMTime() < this->BuildTime.GetMTime())
944   {
945     this->AutoScale(viewport);
946     return;
947   }
948 
949   if (this->MaximumRadius - this->MinimumRadius < 0.0)
950   {
951     std::swap(this->MinimumRadius, this->MaximumRadius);
952   }
953   if (Range[0] > Range[1])
954   {
955     std::swap(Range[0], Range[1]);
956   }
957   if (this->DeltaRangeMajor < 0.0)
958   {
959     this->DeltaRangeMajor *= -1.0;
960   }
961 
962   if (this->DeltaRangeMinor < 0.0)
963   {
964     this->DeltaRangeMinor *= -1.0;
965   }
966 
967   // ---------- Angles check -----------
968   // set angle range [0.0; 360.0]
969   this->MaximumAngle = std::fmod(this->MaximumAngle, 360);
970   this->MinimumAngle = std::fmod(this->MinimumAngle, 360);
971 
972   if (this->MaximumAngle < 0.0)
973   {
974     this->MaximumAngle += 360.0;
975   }
976 
977   // set angle range [0.0; 360.0]
978   if (this->MinimumAngle < 0.0)
979   {
980     this->MinimumAngle += 360.0;
981   }
982 
983   // this->MaximumAngle < this->MinimumAngle is possible, no swap
984 
985   if (!this->CheckMembersConsistency())
986   {
987     return;
988   }
989 
990   // Determine the bounds
991   this->CalculateBounds();
992 
993   // Set polar axis endpoints
994   vtkAxisActor* axis = this->PolarAxis;
995 
996   // compute ellipse angle
997   double miniAngleEllipse = this->ComputeEllipseAngle(this->MinimumAngle, this->Ratio);
998 
999   // Set the start point and end point (world coord system) of the Polar Axis.
1000   double startPt[3], endPt[3];
1001   startPt[0] = this->Pole[0] + this->MinimumRadius * cos(miniAngleEllipse);
1002   startPt[1] = this->Pole[1] + this->MinimumRadius * this->Ratio * sin(miniAngleEllipse);
1003   startPt[2] = this->Pole[2];
1004 
1005   endPt[0] = this->Pole[0] + this->MaximumRadius * cos(miniAngleEllipse);
1006   endPt[1] = this->Pole[1] + this->MaximumRadius * this->Ratio * sin(miniAngleEllipse);
1007   endPt[2] = this->Pole[2];
1008 
1009   axis->GetPoint1Coordinate()->SetValue(startPt);
1010   axis->GetPoint2Coordinate()->SetValue(endPt);
1011 
1012   // axis Type. We assume the polar graph is built in the local plane x-y
1013   if ((this->MinimumAngle > 45.0 && this->MinimumAngle < 135.0) ||
1014     (this->MinimumAngle > 225.0 && this->MinimumAngle < 315.0))
1015   {
1016     axis->SetAxisTypeToY();
1017   }
1018   else
1019   {
1020     axis->SetAxisTypeToX();
1021   }
1022 
1023   // Set axess attributes (range, tick location)
1024   this->SetCommonAxisAttributes(axis);
1025   this->SetPolarAxisAttributes(axis);
1026 
1027   // ------- Ticks thickness -------
1028 
1029   // Polar Axis
1030   this->PolarAxis->GetAxisMajorTicksProperty()->SetLineWidth(this->PolarAxisMajorTickThickness);
1031   double minorThickness = this->PolarAxisTickRatioThickness * this->PolarAxisMajorTickThickness;
1032   if (minorThickness < 1.0)
1033   {
1034     minorThickness = 1.0;
1035   }
1036   this->PolarAxis->GetAxisMinorTicksProperty()->SetLineWidth(minorThickness);
1037 
1038   // Last arc
1039   this->ArcTickActor->GetProperty()->SetLineWidth(this->ArcMajorTickThickness);
1040   minorThickness = std::max(this->ArcMajorTickThickness * this->ArcTickRatioThickness, 1.);
1041 
1042   this->ArcMinorTickActor->GetProperty()->SetLineWidth(minorThickness);
1043 
1044   // last polar axis line width is set in BuildRadialAxes() function
1045 
1046   // Build polar axis ticks
1047   if (this->Log)
1048   {
1049     this->BuildLabelsLog();
1050     this->BuildPolarArcsLog();
1051   }
1052   else
1053   {
1054     // Build polar axis labels
1055     this->BuildPolarAxisLabelsArcs();
1056   }
1057 
1058   // Set title relative location from the axis
1059   if (this->PolarAxisTitleLocation == VTK_TITLE_BOTTOM)
1060   {
1061     this->PolarAxis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_BOTTOM);
1062   }
1063   else
1064   {
1065     this->PolarAxis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_POINT2);
1066   }
1067 
1068   // Build radial axes
1069   this->BuildRadialAxes();
1070 
1071   // Build ticks located on the last arc
1072   if (this->PolarTickVisibility)
1073   {
1074     this->BuildArcTicks();
1075   }
1076 
1077   // color copy
1078   vtkProperty* prop = this->PolarArcsActor->GetProperty();
1079   double color[3];
1080   prop->GetColor(color);
1081   this->ArcTickActor->GetProperty()->SetColor(color);
1082   this->ArcMinorTickActor->GetProperty()->SetColor(color);
1083 
1084   // Update axis title follower
1085   vtkAxisFollower* follower = axis->GetTitleActor();
1086 
1087   follower->SetAxis(axis);
1088   follower->SetEnableDistanceLOD(this->EnableDistanceLOD);
1089   follower->SetDistanceLODThreshold(this->DistanceLODThreshold);
1090   follower->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
1091   follower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
1092 
1093   // Update axis title follower
1094   vtkAxisFollower* expFollower = this->PolarAxis->GetExponentActor();
1095   expFollower->SetAxis(this->PolarAxis);
1096   expFollower->SetEnableDistanceLOD(this->EnableDistanceLOD);
1097   expFollower->SetDistanceLODThreshold(this->DistanceLODThreshold);
1098   expFollower->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
1099   expFollower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
1100 
1101   // Update axis label followers
1102   vtkAxisFollower** labelActors = axis->GetLabelActors();
1103   int numberOfLabels = axis->GetNumberOfLabelsBuilt();
1104   for (int i = 0; i < numberOfLabels; ++i)
1105   {
1106     labelActors[i]->SetAxis(axis);
1107     labelActors[i]->SetEnableDistanceLOD(this->EnableDistanceLOD);
1108     labelActors[i]->SetDistanceLODThreshold(this->DistanceLODThreshold);
1109     labelActors[i]->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
1110     labelActors[i]->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
1111   }
1112 
1113   // Build polar axis
1114   this->PolarAxis->BuildAxis(viewport, true);
1115 
1116   // Scale appropriately
1117   this->AutoScale(viewport);
1118 
1119   this->BuildTime.Modified();
1120 }
1121 
AutoComputeTicksProperties()1122 void vtkPolarAxesActor::AutoComputeTicksProperties()
1123 {
1124   // set DeltaRangeMajor according to Range[1] magnitude
1125   double rangeLength = fabs(this->PolarAxis->GetRange()[1] - this->PolarAxis->GetRange()[0]);
1126 
1127   // we would like no more than 15 ticks
1128   double threshold = log10(1.5);
1129   double log10RangeLength = log10(rangeLength);
1130 
1131   double stepPow10 = (log10RangeLength - std::floor(log10RangeLength) < threshold)
1132     ? std::floor(log10RangeLength) - 1.0
1133     : std::floor(log10RangeLength);
1134 
1135   this->DeltaRangeMajor = std::pow(10.0, stepPow10);
1136   this->DeltaRangeMinor = this->DeltaRangeMajor / 2.0;
1137 }
1138 //-----------------------------------------------------------------------------
SetCommonAxisAttributes(vtkAxisActor * axis)1139 void vtkPolarAxesActor::SetCommonAxisAttributes(vtkAxisActor* axis)
1140 {
1141   vtkProperty* prop = this->GetProperty();
1142   prop->SetAmbient(1.0);
1143   prop->SetDiffuse(0.0);
1144   axis->SetProperty(prop);
1145 
1146   axis->SetScreenSize(this->ScreenSize);
1147 
1148   // Common space and range attributes
1149   axis->SetCamera(this->Camera);
1150   axis->SetBounds(this->Bounds);
1151 
1152   // User defined range
1153   axis->SetRange(this->Range[0], this->Range[1]);
1154 
1155   // Axis scale type
1156   axis->SetLog(this->Log);
1157 
1158   // Major and minor ticks draw begins at Range[0]
1159   axis->SetMajorRangeStart(axis->GetRange()[0]);
1160   axis->SetMinorRangeStart(axis->GetRange()[0]);
1161 
1162   axis->SetCalculateTitleOffset(0);
1163   axis->SetCalculateLabelOffset(0);
1164 
1165   // Set polar axis ticks
1166   axis->SetTickVisibility(this->AxisTickVisibility && this->PolarTickVisibility);
1167 
1168   // Set polar axis minor ticks
1169   axis->SetMinorTicksVisible(this->AxisMinorTickVisibility && this->PolarTickVisibility);
1170 
1171   axis->SetTickLocation(this->TickLocation);
1172 }
1173 
SetPolarAxisAttributes(vtkAxisActor * axis)1174 void vtkPolarAxesActor::SetPolarAxisAttributes(vtkAxisActor* axis)
1175 {
1176   // Set polar axis lines
1177   axis->SetAxisVisibility(this->PolarAxisVisibility);
1178 
1179   // #### Warning #### : Set this property BEFORE apply the ticks thickness of the vtkAxisActor
1180   // instances
1181   axis->SetAxisLinesProperty(this->PolarAxisProperty);
1182 
1183   // Set polar axis title
1184   axis->SetTitleVisibility(this->PolarTitleVisibility);
1185   axis->SetTitle(this->PolarAxisTitle);
1186   axis->SetTitleTextProperty(this->PolarAxisTitleTextProperty);
1187 
1188   // Set Labels exponent value
1189   if (this->ExponentLocation == VTK_EXPONENT_BOTTOM)
1190   {
1191     axis->SetExponentLocation(vtkAxisActor::VTK_ALIGN_BOTTOM);
1192     axis->SetExponentVisibility(true);
1193   }
1194   else if (this->ExponentLocation == VTK_EXPONENT_EXTERN)
1195   {
1196     axis->SetExponentLocation(vtkAxisActor::VTK_ALIGN_POINT2);
1197     axis->SetExponentVisibility(true);
1198   }
1199   else
1200   {
1201     axis->SetExponentVisibility(false);
1202   }
1203 
1204   // Set polar axis labels
1205   axis->SetLabelVisibility(this->PolarLabelVisibility);
1206   axis->SetLabelTextProperty(this->PolarAxisLabelTextProperty);
1207 
1208   // set major tick size as 0.02 * majorRadius
1209   double tickSize = 0.02 * this->MaximumRadius;
1210 
1211   // Use computed tick length if not specified
1212   if (this->PolarAxisMajorTickSize == 0)
1213   {
1214     this->PolarAxisMajorTickSize = tickSize;
1215   }
1216 
1217   if (this->LastRadialAxisMajorTickSize == 0)
1218   {
1219     this->LastRadialAxisMajorTickSize = tickSize;
1220   }
1221 
1222   if (this->ArcMajorTickSize == 0)
1223   {
1224     this->ArcMajorTickSize = tickSize;
1225   }
1226 
1227   // Compute delta Range values (if log == 1, deltaRange properties will be overwritten)
1228   if (this->AutoSubdividePolarAxis)
1229   {
1230     this->AutoComputeTicksProperties();
1231   }
1232 
1233   axis->SetMajorTickSize(this->PolarAxisMajorTickSize);
1234 
1235   axis->SetMinorTickSize(this->PolarAxisTickRatioSize * this->PolarAxisMajorTickSize);
1236 
1237   // Set the value between two ticks
1238   axis->SetDeltaRangeMajor(this->DeltaRangeMajor);
1239   axis->SetDeltaRangeMinor(this->DeltaRangeMinor);
1240 }
1241 
1242 //-----------------------------------------------------------------------------
FFix(double value)1243 inline double vtkPolarAxesActor::FFix(double value)
1244 {
1245   int ivalue = static_cast<int>(value);
1246   return ivalue;
1247 }
1248 
1249 //-----------------------------------------------------------------------------
FSign(double value,double sign)1250 inline double vtkPolarAxesActor::FSign(double value, double sign)
1251 {
1252   value = fabs(value);
1253   if (sign < 0.)
1254   {
1255     value *= -1.;
1256   }
1257   return value;
1258 }
1259 
1260 //-----------------------------------------------------------------------------
CreateRadialAxes(int axisCount)1261 void vtkPolarAxesActor::CreateRadialAxes(int axisCount)
1262 {
1263   // If number of radial axes does not change, do nothing
1264   if (this->NumberOfRadialAxes == axisCount)
1265   {
1266     return;
1267   }
1268 
1269   // Delete existing secondary radial axes
1270   if (this->RadialAxes)
1271   {
1272     for (int i = 0; i < this->NumberOfRadialAxes; ++i)
1273     {
1274       if (this->RadialAxes[i])
1275       {
1276         this->RadialAxes[i]->Delete();
1277         this->RadialAxes[i] = nullptr;
1278       }
1279     }
1280     delete[] this->RadialAxes;
1281     this->RadialAxes = nullptr;
1282   }
1283 
1284   // Create and set n radial axes of type X
1285   this->NumberOfRadialAxes = axisCount;
1286 
1287   // Create requested number of secondary radial axes
1288   this->RadialAxes = new vtkAxisActor*[this->NumberOfRadialAxes];
1289   for (int i = 0; i < this->NumberOfRadialAxes; ++i)
1290   {
1291     // Create axis of type X
1292     this->RadialAxes[i] = vtkAxisActor::New();
1293     vtkAxisActor* axis = this->RadialAxes[i];
1294     axis->SetAxisTypeToX();
1295     axis->SetCalculateTitleOffset(0);
1296     axis->SetCalculateLabelOffset(0);
1297     axis->SetLabelOffset(0);
1298     axis->SetTitleOffset(2);
1299     axis->SetLabelVisibility(0);
1300     axis->SetUse2DMode(this->PolarAxis->GetUse2DMode());
1301     axis->LastMajorTickPointCorrectionOn();
1302   }
1303   this->Modified();
1304 }
1305 
1306 //-----------------------------------------------------------------------------
BuildRadialAxes()1307 void vtkPolarAxesActor::BuildRadialAxes()
1308 {
1309   bool originToPolarAxis = this->RadialAxesOriginToPolarAxis != 0.0;
1310 
1311   // set MaximumAngle and MinimumAngle range: [0.0; 360.0]
1312   double angleSection = (this->MaximumAngle > this->MinimumAngle)
1313     ? this->MaximumAngle - this->MinimumAngle
1314     : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle);
1315 
1316   if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle) ||
1317     angleSection == 360.0)
1318   {
1319     angleSection = 360.0;
1320   }
1321 
1322   this->ComputeDeltaAngleRadialAxes(this->RequestedNumberOfRadialAxes);
1323   bool positiveSection = false;
1324   double dAlpha = this->DeltaAngleRadialAxes;
1325   double alphaDeg, currentAlpha;
1326 
1327   // current ellipse angle
1328   double actualAngle;
1329   int i = 0;
1330 
1331   double minorThickness;
1332 
1333   double alphaStart = (originToPolarAxis)
1334     ? this->MinimumAngle + dAlpha
1335     : std::floor(this->MinimumAngle / dAlpha) * dAlpha + dAlpha;
1336   double alphaStop = angleSection + this->MinimumAngle + dAlpha;
1337 
1338   int nAxes;
1339 
1340   // Delta angle to big, only last radial axis
1341    if (this->DeltaAngleRadialAxes >= angleSection)
1342    {
1343      nAxes = 1;
1344      alphaStart = angleSection + this->MinimumAngle;
1345    }
1346    else if (this->RequestedNumberOfRadialAxes == 0)
1347    {
1348      nAxes = std::ceil(angleSection / dAlpha);
1349    }
1350    else
1351    {
1352      nAxes = this->RequestedNumberOfRadialAxes - 1;
1353    }
1354 
1355   // init radial axis. Does nothing if number of radial axes doesn't change
1356   this->CreateRadialAxes(nAxes);
1357 
1358   char titleValue[64];
1359   for (alphaDeg = alphaStart; alphaDeg <= alphaStop && i < this->NumberOfRadialAxes;
1360        alphaDeg += dAlpha, i++)
1361   {
1362     currentAlpha = alphaDeg;
1363 
1364     if (currentAlpha > angleSection + this->MinimumAngle ||
1365       (i == this->NumberOfRadialAxes - 1))
1366     {
1367       currentAlpha = angleSection + this->MinimumAngle;
1368     }
1369 
1370     // Calculate startpoint coordinates
1371     double thetaEllipse = this->ComputeEllipseAngle(currentAlpha, this->Ratio);
1372     double xStart = this->Pole[0] + this->MinimumRadius * cos(thetaEllipse);
1373     double yStart = this->Pole[1] + this->MinimumRadius * this->Ratio * sin(thetaEllipse);
1374 
1375     // Calculate endpoint coordinates
1376     double xEnd = this->Pole[0] + this->MaximumRadius * cos(thetaEllipse);
1377     double yEnd = this->Pole[1] + this->MaximumRadius * this->Ratio * sin(thetaEllipse);
1378 
1379     // radius angle (different from angle used to compute ellipse point)
1380     actualAngle = vtkMath::DegreesFromRadians(atan2(yEnd - this->Pole[1], xEnd - this->Pole[0]));
1381 
1382     // to keep angle positive for the last ones
1383     if (actualAngle > 0.0 || this->MinimumAngle < 180.0)
1384     {
1385       positiveSection = true;
1386     }
1387 
1388     if (actualAngle < 0.0 && positiveSection)
1389     {
1390       actualAngle += 360.0;
1391     }
1392 
1393     // Set radial axis endpoints
1394     vtkAxisActor* axis = this->RadialAxes[i];
1395 
1396     // The last arc has its own property
1397     if ((alphaDeg + dAlpha) >= alphaStop)
1398     {
1399       axis->SetAxisLinesProperty(this->LastRadialAxisProperty);
1400       axis->SetTitleTextProperty(this->LastRadialAxisTextProperty);
1401     }
1402     else
1403     {
1404       axis->SetAxisLinesProperty(this->SecondaryRadialAxesProperty);
1405       axis->SetTitleTextProperty(this->SecondaryRadialAxesTextProperty);
1406     }
1407 
1408     axis->GetPoint1Coordinate()->SetValue(xStart, yStart, this->Pole[2]);
1409     axis->GetPoint2Coordinate()->SetValue(xEnd, yEnd, this->Pole[2]);
1410 
1411     // set the range steps
1412     axis->SetDeltaRangeMajor(this->PolarAxis->GetDeltaRangeMajor());
1413     axis->SetDeltaRangeMinor(this->PolarAxis->GetDeltaRangeMinor());
1414 
1415     // Set common axis attributes
1416     this->SetCommonAxisAttributes(axis);
1417 
1418     // Set radial axis lines
1419     axis->SetAxisVisibility(this->RadialAxesVisibility);
1420 
1421     // Set title relative location from the axis
1422     if (this->RadialAxisTitleLocation == VTK_TITLE_BOTTOM)
1423     {
1424       axis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_BOTTOM);
1425     }
1426     else
1427     {
1428       axis->SetTitleAlignLocation(vtkAxisActor::VTK_ALIGN_POINT2);
1429     }
1430 
1431     // Set radial axis title with polar angle as title for non-polar axes
1432     if (this->PolarAxisVisibility && fabs(alphaDeg) < 2.)
1433     {
1434       // Prevent conflict between radial and polar axes titles
1435       axis->SetTitleVisibility(false);
1436 
1437       if (fabs(alphaDeg) < this->SmallestVisiblePolarAngle)
1438       {
1439         // Do not show radial axes too close to polar axis
1440         axis->SetAxisVisibility(false);
1441       }
1442     }
1443     else
1444     {
1445       // Use polar angle as a title for the radial axis
1446       axis->SetTitleVisibility(this->RadialTitleVisibility);
1447       std::ostringstream title;
1448       title.setf(std::ios::fixed, std::ios::floatfield);
1449       snprintf(titleValue, sizeof(titleValue), this->RadialAngleFormat, actualAngle);
1450       title << titleValue << (this->RadialUnits ? " deg" : "");
1451       axis->SetTitle(title.str().c_str());
1452 
1453       // Update axis title followers
1454       axis->GetTitleActor()->SetAxis(axis);
1455       axis->GetTitleActor()->SetEnableDistanceLOD(this->EnableDistanceLOD);
1456       axis->GetTitleActor()->SetDistanceLODThreshold(this->DistanceLODThreshold);
1457       axis->GetTitleActor()->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
1458       axis->GetTitleActor()->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
1459     }
1460 
1461     // Ticks for the last radial axis
1462     if (angleSection != 360.0 && i == this->NumberOfRadialAxes - 1)
1463     {
1464       // axis Type. We assume the polar graph is built in the local plane x-y
1465       if ((actualAngle > 45.0 && actualAngle < 135.0) ||
1466         (actualAngle > 225.0 && actualAngle < 315.0))
1467       {
1468         axis->SetAxisTypeToY();
1469       }
1470       else
1471         axis->SetAxisTypeToX();
1472 
1473       // Set polar axis ticks
1474       axis->SetTickVisibility(this->AxisTickVisibility && this->PolarTickVisibility);
1475       axis->SetMajorTickSize(this->LastRadialAxisMajorTickSize);
1476 
1477       // Set polar axis minor ticks
1478       axis->SetMinorTicksVisible(this->AxisMinorTickVisibility && this->PolarTickVisibility);
1479       axis->SetMinorTickSize(this->LastAxisTickRatioSize * this->LastRadialAxisMajorTickSize);
1480 
1481       // Set the tick orientation
1482       axis->SetTickLocation(this->TickLocation);
1483 
1484       axis->GetAxisMajorTicksProperty()->SetLineWidth(this->LastRadialAxisMajorTickThickness);
1485       minorThickness = this->LastRadialAxisMajorTickThickness * LastAxisTickRatioThickness;
1486       if (minorThickness < 1.0)
1487       {
1488         minorThickness = 1.0;
1489       }
1490       axis->GetAxisMinorTicksProperty()->SetLineWidth(minorThickness);
1491     }
1492     else
1493     {
1494       axis->SetLabelVisibility(0);
1495       axis->SetTickVisibility(0);
1496     }
1497   }
1498 }
1499 
1500 //-----------------------------------------------------------------------------
BuildArcTicks()1501 void vtkPolarAxesActor::BuildArcTicks()
1502 {
1503   bool originToPolarAxis = this->ArcTicksOriginToPolarAxis != 0.0;
1504 
1505   // set MaximumAngle and MinimumAngle range: [0.0; 360.0]
1506   double angleSection = (this->MaximumAngle > this->MinimumAngle)
1507     ? this->MaximumAngle - this->MinimumAngle
1508     : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle);
1509 
1510   if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle) ||
1511     angleSection == 360.0)
1512   {
1513     angleSection = 360.0;
1514   }
1515 
1516   // Clear Tick Points
1517   this->ArcMajorTickPts->Reset();
1518   this->ArcMinorTickPts->Reset();
1519 
1520   // Create requested number of radial axes
1521   double dAlpha = this->DeltaAngleMajor;
1522   double alphaStart;
1523   alphaStart = (originToPolarAxis) ? this->MinimumAngle + dAlpha
1524                                    : std::floor(this->MinimumAngle / dAlpha) * dAlpha + dAlpha;
1525   for (double alphaDeg = alphaStart; alphaDeg < (angleSection + this->MinimumAngle);
1526        alphaDeg += dAlpha)
1527   {
1528     double thetaEllipse = ComputeEllipseAngle(alphaDeg, this->Ratio);
1529     this->StoreTicksPtsFromParamEllipse(
1530       this->MaximumRadius, thetaEllipse, this->ArcMajorTickSize, this->ArcMajorTickPts);
1531   }
1532 
1533   // Copy/paste should be replaced with a python-like generator to provide parameters to
1534   // StoreTicksPtsFromParamEllipse()
1535   // without running twice through the ellipse
1536 
1537   dAlpha = this->DeltaAngleMinor;
1538   alphaStart = (originToPolarAxis) ? this->MinimumAngle + dAlpha
1539                                    : std::floor(this->MinimumAngle / dAlpha) * dAlpha + dAlpha;
1540   for (double alphaDeg = alphaStart; alphaDeg < (angleSection + this->MinimumAngle);
1541        alphaDeg += dAlpha)
1542   {
1543     double thetaEllipse = ComputeEllipseAngle(alphaDeg, this->Ratio);
1544     this->StoreTicksPtsFromParamEllipse(this->MaximumRadius, thetaEllipse,
1545       this->ArcTickRatioSize * this->ArcMajorTickSize, this->ArcMinorTickPts);
1546   }
1547 
1548   // set vtk object to draw the ticks
1549   vtkNew<vtkPoints> majorPts;
1550   vtkNew<vtkPoints> minorPts;
1551   vtkNew<vtkCellArray> majorLines;
1552   vtkNew<vtkCellArray> minorLines;
1553   vtkIdType ptIds[2];
1554   int numTickPts, numLines, i;
1555   this->ArcTickPolyData->SetPoints(majorPts);
1556   this->ArcTickPolyData->SetLines(majorLines);
1557   this->ArcMinorTickPolyData->SetPoints(minorPts);
1558   this->ArcMinorTickPolyData->SetLines(minorLines);
1559 
1560   if (this->ArcTickVisibility)
1561   {
1562     numTickPts = this->ArcMajorTickPts->GetNumberOfPoints();
1563     for (i = 0; i < numTickPts; i++)
1564     {
1565       majorPts->InsertNextPoint(this->ArcMajorTickPts->GetPoint(i));
1566     }
1567   }
1568   if (this->ArcMinorTickVisibility)
1569   {
1570     // In 2D mode, the minorTickPts for yz portion or xz portion have been removed.
1571     numTickPts = this->ArcMinorTickPts->GetNumberOfPoints();
1572     for (i = 0; i < numTickPts; i++)
1573     {
1574       minorPts->InsertNextPoint(this->ArcMinorTickPts->GetPoint(i));
1575     }
1576   }
1577 
1578   // create lines
1579   if (this->ArcTickVisibility)
1580   {
1581     numLines = majorPts->GetNumberOfPoints() / 2;
1582     for (i = 0; i < numLines; i++)
1583     {
1584       ptIds[0] = 2 * i;
1585       ptIds[1] = 2 * i + 1;
1586       majorLines->InsertNextCell(2, ptIds);
1587     }
1588   }
1589   if (this->ArcMinorTickVisibility)
1590   {
1591     numLines = minorPts->GetNumberOfPoints() / 2;
1592     for (i = 0; i < numLines; i++)
1593     {
1594       ptIds[0] = 2 * i;
1595       ptIds[1] = 2 * i + 1;
1596       minorLines->InsertNextCell(2, ptIds);
1597     }
1598   }
1599 }
1600 
StoreTicksPtsFromParamEllipse(double a,double angleEllipseRad,double tickSize,vtkPoints * tickPts)1601 void vtkPolarAxesActor::StoreTicksPtsFromParamEllipse(
1602   double a, double angleEllipseRad, double tickSize, vtkPoints* tickPts)
1603 {
1604   // plane point: point located in the plane of the ellipse
1605   // normal Dir Point: point located according to the direction of the z vector
1606 
1607   // inside direction: direction from the arc to its center for plane points, and positive z
1608   // direction
1609   // outside direction: direction from the arc to the outer radial direction for plane points, and
1610   // negative z direction
1611 
1612   int i;
1613   double planeInPt[3], planeOutPt[3], normalDirPt[3], invNormalDirPt[3];
1614 
1615   if (!tickPts)
1616   {
1617     return;
1618   }
1619 
1620   double b = a * this->Ratio;
1621   double xArc = this->Pole[0] + a * cos(angleEllipseRad);
1622   double yArc = this->Pole[1] + b * sin(angleEllipseRad);
1623   double ellipsePt[3] = { xArc, yArc, this->Pole[2] };
1624 
1625   double deltaVector[3] = { a * cos(angleEllipseRad), b * sin(angleEllipseRad), 0.0 };
1626   vtkMath::Normalize(deltaVector);
1627 
1628   double orthoVector[3] = { 0.0, 0.0, 1.0 };
1629 
1630   // init
1631   for (i = 0; i < 3; i++)
1632   {
1633     planeInPt[i] = planeOutPt[i] = normalDirPt[i] = invNormalDirPt[i] = ellipsePt[i];
1634   }
1635 
1636   if (this->TickLocation == vtkAxisActor::VTK_TICKS_INSIDE ||
1637     this->TickLocation == vtkAxisActor::VTK_TICKS_BOTH)
1638   {
1639     for (i = 0; i < 3; i++)
1640     {
1641       planeInPt[i] = ellipsePt[i] - tickSize * deltaVector[i];
1642     }
1643 
1644     for (i = 0; i < 3; i++)
1645     {
1646       normalDirPt[i] = ellipsePt[i] + tickSize * orthoVector[i];
1647     }
1648   }
1649 
1650   if (this->TickLocation == vtkAxisActor::VTK_TICKS_OUTSIDE ||
1651     this->TickLocation == vtkAxisActor::VTK_TICKS_BOTH)
1652   {
1653     for (i = 0; i < 3; i++)
1654     {
1655       planeOutPt[i] = ellipsePt[i] + tickSize * deltaVector[i];
1656     }
1657 
1658     for (i = 0; i < 3; i++)
1659     {
1660       invNormalDirPt[i] = ellipsePt[i] - tickSize * orthoVector[i];
1661     }
1662   }
1663 
1664   vtkIdType nPoints = tickPts->GetNumberOfPoints();
1665   tickPts->Resize(nPoints + 4);
1666   tickPts->SetNumberOfPoints(nPoints + 4);
1667   tickPts->SetPoint(nPoints, planeInPt);
1668   tickPts->SetPoint(nPoints + 1, planeOutPt);
1669   tickPts->SetPoint(nPoints + 2, normalDirPt);
1670   tickPts->SetPoint(nPoints + 3, invNormalDirPt);
1671 }
1672 
1673 //-----------------------------------------------------------------------------
BuildPolarAxisLabelsArcs()1674 void vtkPolarAxesActor::BuildPolarAxisLabelsArcs()
1675 {
1676   double angleSection = (this->MaximumAngle > this->MinimumAngle)
1677     ? this->MaximumAngle - this->MinimumAngle
1678     : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle);
1679 
1680   // if Min and max angle are the same, interpret it as 360 segment opening
1681   if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle))
1682   {
1683     angleSection = 360.0;
1684   }
1685 
1686   // Prepare trigonometric quantities
1687   vtkIdType arcResolution =
1688     static_cast<vtkIdType>(angleSection * (VTK_POLAR_ARC_RESOLUTION_PER_DEG / this->Ratio));
1689 
1690   // Principal Arc points
1691   vtkNew<vtkPoints> polarArcsPoints;
1692   this->PolarArcs->SetPoints(polarArcsPoints);
1693 
1694   // Principal Arc lines
1695   vtkNew<vtkCellArray> polarArcsLines;
1696   this->PolarArcs->SetLines(polarArcsLines);
1697 
1698   // Secondary Arc points
1699   vtkNew<vtkPoints> secondaryPolarArcsPoints;
1700   this->SecondaryPolarArcs->SetPoints(secondaryPolarArcsPoints);
1701 
1702   // Secondary Arc lines
1703   vtkNew<vtkCellArray> secondaryPolarArcsLines;
1704   this->SecondaryPolarArcs->SetLines(secondaryPolarArcsLines);
1705 
1706   vtkAxisActor* axis = this->PolarAxis;
1707 
1708   // Base ellipse arc value, refers to world coordinate system
1709   double axisLength = this->MaximumRadius - this->MinimumRadius;
1710   double rangeLength = axis->GetRange()[1] - axis->GetRange()[0];
1711   double rangeScale = axisLength / rangeLength;
1712 
1713   // Label values refers to range values
1714   double valueRange = axis->GetRange()[0];
1715   double currentValue;
1716   double deltaRange = axis->GetDeltaRangeMajor();
1717   double deltaArc;
1718 
1719   // Prepare storage for polar axis labels
1720   std::list<double> labelValList;
1721 
1722   vtkIdType pointIdOffset = 0;
1723   bool isInnerArc, isArcVisible, isLastArc;
1724 
1725   currentValue = axis->GetRange()[0];
1726   while (currentValue < axis->GetRange()[1])
1727   {
1728     currentValue =
1729       (valueRange + (deltaRange / 2) > axis->GetRange()[1]) ? axis->GetRange()[1] : valueRange;
1730     deltaArc = (currentValue - axis->GetRange()[0]) * rangeScale;
1731 
1732     isInnerArc = currentValue > axis->GetRange()[0] && currentValue < axis->GetRange()[1];
1733     isArcVisible = !isInnerArc || this->DrawPolarArcsGridlines;
1734     isLastArc = currentValue == axis->GetRange()[1];
1735 
1736     // Store value
1737     labelValList.push_back(currentValue);
1738 
1739     // Build polar arcs for non-zero values
1740     if (deltaArc + this->MinimumRadius > 0. && isArcVisible)
1741     {
1742       // Create elliptical polar arc with corresponding to this tick mark
1743       vtkNew<vtkEllipseArcSource> arc;
1744       arc->SetCenter(this->Pole);
1745       arc->SetRatio(this->Ratio);
1746       arc->SetNormal(0., 0., 1.);
1747       arc->SetMajorRadiusVector(deltaArc + this->MinimumRadius, 0.0, 0.0);
1748       arc->SetStartAngle(this->MinimumAngle);
1749       arc->SetSegmentAngle(angleSection);
1750       arc->SetResolution(arcResolution);
1751       arc->Update();
1752 
1753       if (isLastArc)
1754       {
1755         // Add polar arc
1756         vtkPoints* arcPoints = nullptr;
1757         vtkIdType nPoints = 0;
1758         vtkIdType* arcPointIds = nullptr;
1759         if (arc->GetOutput()->GetNumberOfPoints() > 0)
1760         {
1761           arcPoints = arc->GetOutput()->GetPoints();
1762           nPoints = arcResolution + 1;
1763           arcPointIds = new vtkIdType[nPoints];
1764           for (vtkIdType j = 0; j < nPoints; ++j)
1765           {
1766             polarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j));
1767             arcPointIds[j] = j;
1768           }
1769           polarArcsLines->InsertNextCell(nPoints, arcPointIds);
1770         }
1771 
1772         // Clean up
1773         delete[] arcPointIds;
1774       }
1775       else
1776       {
1777         // Append new secondary polar arc to existing ones
1778         vtkPoints* arcPoints = nullptr;
1779         vtkIdType nPoints = 0;
1780         vtkIdType* arcPointIds = nullptr;
1781         if (arc->GetOutput()->GetNumberOfPoints() > 0)
1782         {
1783           arcPoints = arc->GetOutput()->GetPoints();
1784           nPoints = arcResolution + 1;
1785           arcPointIds = new vtkIdType[nPoints];
1786 
1787           for (vtkIdType j = 0; j < nPoints; ++j)
1788           {
1789             secondaryPolarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j));
1790             arcPointIds[j] = pointIdOffset + j;
1791           }
1792           secondaryPolarArcsLines->InsertNextCell(nPoints, arcPointIds);
1793         }
1794 
1795         // Clean up
1796         delete[] arcPointIds;
1797 
1798         // Update polyline cell offset
1799         pointIdOffset += nPoints;
1800       }
1801     }
1802 
1803     // Move to next value
1804     valueRange += deltaRange;
1805   }
1806 
1807   // set up vtk collection to store labels
1808   vtkNew<vtkStringArray> labels;
1809 
1810   if (this->ExponentLocation != VTK_EXPONENT_LABELS)
1811   {
1812     // it modifies the values of labelValList
1813     std::string commonLbl = FindExponentAndAdjustValues(labelValList);
1814     axis->SetExponent(commonLbl.c_str());
1815 
1816     this->GetSignificantPartFromValues(labels, labelValList);
1817   }
1818   else
1819   {
1820     axis->SetExponent("");
1821     // construct label string array
1822     labels->SetNumberOfValues(static_cast<vtkIdType>(labelValList.size()));
1823 
1824     std::list<double>::iterator itList;
1825     vtkIdType i = 0;
1826     for (itList = labelValList.begin(); itList != labelValList.end(); ++i, ++itList)
1827     {
1828       char label[64];
1829       snprintf(label, sizeof(label), this->PolarLabelFormat, *itList);
1830       labels->SetValue(i, label);
1831     }
1832   }
1833 
1834   // Store labels
1835   axis->SetLabels(labels);
1836 }
1837 
1838 //-----------------------------------------------------------------------------
BuildPolarArcsLog()1839 void vtkPolarAxesActor::BuildPolarArcsLog()
1840 {
1841   double angleSection = (this->MaximumAngle > this->MinimumAngle)
1842     ? this->MaximumAngle - this->MinimumAngle
1843     : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle);
1844 
1845   // if Min and max angle are the same, interpret it as 360 segment opening
1846   if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle))
1847   {
1848     angleSection = 360.0;
1849   }
1850 
1851   vtkIdType arcResolution =
1852     static_cast<vtkIdType>(angleSection * (VTK_POLAR_ARC_RESOLUTION_PER_DEG / this->Ratio));
1853 
1854   // Principal Arc points
1855   vtkNew<vtkPoints> polarArcsPoints;
1856   this->PolarArcs->SetPoints(polarArcsPoints);
1857 
1858   // Principal Arc lines
1859   vtkNew<vtkCellArray> polarArcsLines;
1860   this->PolarArcs->SetLines(polarArcsLines);
1861 
1862   // Secondary Arc points
1863   vtkNew<vtkPoints> secondaryPolarArcsPoints;
1864   this->SecondaryPolarArcs->SetPoints(secondaryPolarArcsPoints);
1865 
1866   // Secondary Arc lines
1867   vtkNew<vtkCellArray> secondaryPolarArcsLines;
1868   this->SecondaryPolarArcs->SetLines(secondaryPolarArcsLines);
1869 
1870   //--- prepare significant values ----
1871   double miniAngleEllipseRad = ComputeEllipseAngle(this->MinimumAngle, this->Ratio);
1872 
1873   // Distance from Pole to Range[0]
1874   vtkAxisActor* axis = this->PolarAxis;
1875 
1876   double deltaVector[3], polarAxisUnitVect[3];
1877   vtkMath::Subtract(axis->GetPoint2(), axis->GetPoint1(), deltaVector);
1878   vtkMath::Subtract(axis->GetPoint2(), axis->GetPoint1(), polarAxisUnitVect);
1879   vtkMath::Normalize(polarAxisUnitVect);
1880 
1881   // polar axis actor length
1882   double axisLength = vtkMath::Norm(deltaVector);
1883 
1884   // conversion factor
1885   double rangeScaleLog = axisLength / log10(axis->GetRange()[1] / axis->GetRange()[0]);
1886 
1887   // reuse deltaVector
1888   vtkMath::Subtract(axis->GetPoint1(), this->Pole, deltaVector);
1889   double distanceAxisPoint1FromPole = vtkMath::Norm(deltaVector);
1890 
1891   double base = 10.0;
1892   double log10Range0 = log10(axis->GetRange()[0]);
1893   double log10Range1 = log10(axis->GetRange()[1]);
1894   double lowBound = std::pow(base, static_cast<int>(std::floor(log10Range0)));
1895   double upBound = std::pow(base, static_cast<int>(ceil(log10Range1)));
1896 
1897   int i;
1898   double tickVal, tickRangeVal, indexTickRangeValue;
1899 
1900   vtkIdType pointIdOffset = 0;
1901   bool isInnerArc, isArcVisible, isLastArc;
1902   double a, b;
1903   double epsilon = 1e-8;
1904 
1905   for (indexTickRangeValue = lowBound; indexTickRangeValue <= upBound; indexTickRangeValue *= base)
1906   {
1907     // to keep major values as power of 10
1908     tickRangeVal = indexTickRangeValue;
1909 
1910     isInnerArc = tickRangeVal > lowBound && tickRangeVal < upBound;
1911     isArcVisible = !isInnerArc || this->DrawPolarArcsGridlines;
1912     isLastArc = tickRangeVal == upBound;
1913 
1914     if (!isArcVisible)
1915     {
1916       continue;
1917     }
1918 
1919     if (tickRangeVal < axis->GetRange()[0])
1920     {
1921       tickRangeVal = axis->GetRange()[0];
1922     }
1923 
1924     if (tickRangeVal > axis->GetRange()[1])
1925     {
1926       tickRangeVal = axis->GetRange()[1];
1927     }
1928 
1929     // conversion range value to world value
1930     tickVal = (log10(tickRangeVal) - log10Range0) * rangeScaleLog;
1931 
1932     // Vector from Pole to major tick
1933     for (i = 0; i < 3; i++)
1934     {
1935       deltaVector[i] = polarAxisUnitVect[i] * (tickVal + distanceAxisPoint1FromPole);
1936     }
1937 
1938     if (vtkMath::Norm(deltaVector) == 0.0)
1939     {
1940       continue;
1941     }
1942 
1943     // epsilon is a very low value. vtkMathUtilities::FuzzyCompare is not fuzzy enough ...
1944     if (fabs(fabs(miniAngleEllipseRad) - vtkMath::Pi() / 2.0) < epsilon)
1945     {
1946       b = deltaVector[1] / sin(miniAngleEllipseRad);
1947       a = b / this->Ratio;
1948     }
1949     else
1950     {
1951       a = deltaVector[0] / cos(miniAngleEllipseRad);
1952     }
1953 
1954     // Create elliptical polar arc with corresponding to this tick mark
1955     vtkNew<vtkEllipseArcSource> arc;
1956     arc->SetCenter(this->Pole);
1957     arc->SetRatio(this->Ratio);
1958     arc->SetNormal(0., 0., 1.);
1959     arc->SetMajorRadiusVector(a, 0.0, 0.0);
1960     arc->SetStartAngle(this->MinimumAngle);
1961     arc->SetSegmentAngle(angleSection);
1962     arc->SetResolution(arcResolution);
1963     arc->Update();
1964 
1965     if (isLastArc)
1966     {
1967       // Add principal polar arc
1968       vtkPoints* arcPoints = nullptr;
1969       vtkIdType nPoints;
1970       vtkIdType* arcPointIds = nullptr;
1971       if (arc->GetOutput()->GetNumberOfPoints() > 0)
1972       {
1973         arcPoints = arc->GetOutput()->GetPoints();
1974         nPoints = arcResolution + 1;
1975         arcPointIds = new vtkIdType[nPoints];
1976         for (vtkIdType j = 0; j < nPoints; ++j)
1977         {
1978           polarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j));
1979           arcPointIds[j] = j;
1980         }
1981         polarArcsLines->InsertNextCell(nPoints, arcPointIds);
1982       }
1983       // Clean up
1984       delete[] arcPointIds;
1985     }
1986     else
1987     {
1988       // Append new polar arc to existing ones
1989       vtkPoints* arcPoints = nullptr;
1990       vtkIdType nPoints = 0;
1991       vtkIdType* arcPointIds = nullptr;
1992       if (arc->GetOutput()->GetNumberOfPoints() > 0)
1993       {
1994         arcPoints = arc->GetOutput()->GetPoints();
1995         nPoints = arcResolution + 1;
1996         arcPointIds = new vtkIdType[nPoints];
1997         for (vtkIdType j = 0; j < nPoints; ++j)
1998         {
1999           secondaryPolarArcsPoints->InsertNextPoint(arcPoints->GetPoint(j));
2000           arcPointIds[j] = pointIdOffset + j;
2001         }
2002         secondaryPolarArcsLines->InsertNextCell(nPoints, arcPointIds);
2003       }
2004       // Clean up
2005       delete[] arcPointIds;
2006 
2007       // Update polyline cell offset
2008       pointIdOffset += nPoints;
2009     }
2010   }
2011 }
2012 
2013 //-----------------------------------------------------------------------------
BuildLabelsLog()2014 void vtkPolarAxesActor::BuildLabelsLog()
2015 {
2016   // Prepare storage for polar axis labels
2017   std::list<double> labelValList;
2018 
2019   vtkAxisActor* axis = this->PolarAxis;
2020   double base = 10.0;
2021 
2022   if (axis->GetRange()[0] <= 0.0)
2023   {
2024     return;
2025   }
2026 
2027   // define major ticks label values
2028   double indexTickRangeValue;
2029   double tickRangeVal;
2030   double log10Range0 = log10(axis->GetRange()[0]);
2031   double log10Range1 = log10(axis->GetRange()[1]);
2032   double lowBound = std::pow(base, static_cast<int>(std::floor(log10Range0)));
2033   double upBound = std::pow(base, static_cast<int>(ceil(log10Range1)));
2034 
2035   for (indexTickRangeValue = lowBound; indexTickRangeValue <= upBound; indexTickRangeValue *= base)
2036   {
2037     tickRangeVal = indexTickRangeValue;
2038 
2039     if (indexTickRangeValue < axis->GetRange()[0])
2040     {
2041       tickRangeVal = axis->GetRange()[0];
2042     }
2043 
2044     else if (indexTickRangeValue > axis->GetRange()[1])
2045     {
2046       tickRangeVal = axis->GetRange()[1];
2047     }
2048 
2049     labelValList.push_back(tickRangeVal);
2050   }
2051 
2052   // set up vtk collection to store labels
2053   vtkNew<vtkStringArray> labels;
2054 
2055   if (this->ExponentLocation != VTK_EXPONENT_LABELS)
2056   {
2057     // it modifies the values of labelValList
2058     std::string commonLbl = FindExponentAndAdjustValues(labelValList);
2059     axis->SetExponent(commonLbl.c_str());
2060 
2061     this->GetSignificantPartFromValues(labels, labelValList);
2062   }
2063   else
2064   {
2065     axis->SetExponent("");
2066     labels->SetNumberOfValues(static_cast<vtkIdType>(labelValList.size()));
2067 
2068     std::list<double>::iterator itList;
2069     vtkIdType i = 0;
2070     for (itList = labelValList.begin(); itList != labelValList.end(); ++i, ++itList)
2071     {
2072       char label[64];
2073       snprintf(label, sizeof(label), this->PolarLabelFormat, *itList);
2074       labels->SetValue(i, label);
2075     }
2076   }
2077 
2078   // Store labels
2079   axis->SetLabels(labels);
2080 }
2081 
2082 //-----------------------------------------------------------------------------
BuildPolarAxisLabelsArcsLog()2083 void vtkPolarAxesActor::BuildPolarAxisLabelsArcsLog()
2084 {
2085   this->BuildPolarArcsLog();
2086 
2087   this->BuildLabelsLog();
2088 
2089   // Update axis title follower
2090   vtkAxisFollower* follower = this->PolarAxis->GetTitleActor();
2091   follower->SetAxis(this->PolarAxis);
2092   follower->SetEnableDistanceLOD(this->EnableDistanceLOD);
2093   follower->SetDistanceLODThreshold(this->DistanceLODThreshold);
2094   follower->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
2095   follower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
2096 
2097   // Update axis title follower
2098   vtkAxisFollower* expFollower = this->PolarAxis->GetExponentActor();
2099   expFollower->SetAxis(this->PolarAxis);
2100   expFollower->SetEnableDistanceLOD(this->EnableDistanceLOD);
2101   expFollower->SetDistanceLODThreshold(this->DistanceLODThreshold);
2102   expFollower->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
2103   expFollower->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
2104 
2105   // Update axis label followers
2106   vtkAxisFollower** labelActors = this->PolarAxis->GetLabelActors();
2107   int labelCount = this->PolarAxis->GetNumberOfLabelsBuilt();
2108   for (int i = 0; i < labelCount; ++i)
2109   {
2110     labelActors[i]->SetAxis(this->PolarAxis);
2111     labelActors[i]->SetEnableDistanceLOD(this->EnableDistanceLOD);
2112     labelActors[i]->SetDistanceLODThreshold(this->DistanceLODThreshold);
2113     labelActors[i]->SetEnableViewAngleLOD(this->EnableViewAngleLOD);
2114     labelActors[i]->SetViewAngleLODThreshold(this->ViewAngleLODThreshold);
2115   }
2116 }
2117 
2118 //-----------------------------------------------------------------------------
FindExponentAndAdjustValues(std::list<double> & valuesList)2119 std::string vtkPolarAxesActor::FindExponentAndAdjustValues(std::list<double>& valuesList)
2120 {
2121   std::list<double>::iterator itDouble;
2122 
2123   double exponentMean = 0.0;
2124   int count = 0;
2125 
2126   // find common exponent
2127   for (itDouble = valuesList.begin(); itDouble != valuesList.end(); ++itDouble)
2128   {
2129     if (*itDouble != 0.0)
2130     {
2131       double exponent = std::floor(log10(fabs(*itDouble)));
2132       exponentMean += exponent;
2133       count++;
2134     }
2135   }
2136 
2137   if (count == 0)
2138   {
2139     return "";
2140   }
2141 
2142   exponentMean /= count;
2143 
2144   // adjust exponent to int value. Round it if fract part != 0.0
2145   double intPart, fractPart;
2146   fractPart = modf(exponentMean, &intPart);
2147 
2148   if (exponentMean < 0.0)
2149   {
2150     if (fabs(fractPart) >= 0.5)
2151     {
2152       intPart -= 1.0;
2153     }
2154   }
2155   else
2156   {
2157     if (fabs(fractPart) >= 0.5)
2158     {
2159       intPart += 1.0;
2160     }
2161   }
2162   exponentMean = intPart;
2163 
2164   // shift every values
2165   for (itDouble = valuesList.begin(); itDouble != valuesList.end(); ++itDouble)
2166   {
2167     if (*itDouble != 0.0)
2168     {
2169       *itDouble /= std::pow(10, exponentMean);
2170     }
2171   }
2172 
2173   // Layout of the exponent:
2174   std::stringstream ss;
2175   int exponentInt = static_cast<int>(fabs(exponentMean));
2176 
2177   // add sign
2178   ss << (exponentMean >= 0.0 ? "+" : "-");
2179 
2180   // add 0 for pow < 10
2181   if (exponentInt < 10.0)
2182   {
2183     ss << "0";
2184   }
2185 
2186   ss << exponentInt;
2187 
2188   return ss.str();
2189 }
2190 
2191 //-----------------------------------------------------------------------------
GetSignificantPartFromValues(vtkStringArray * valuesStr,std::list<double> & valuesList)2192 void vtkPolarAxesActor::GetSignificantPartFromValues(
2193   vtkStringArray* valuesStr, std::list<double>& valuesList)
2194 {
2195   if (!valuesStr || valuesList.empty())
2196   {
2197     return;
2198   }
2199 
2200   valuesStr->SetNumberOfValues(static_cast<vtkIdType>(valuesList.size()));
2201 
2202   std::list<double>::iterator itList;
2203   vtkIdType i = 0;
2204   for (itList = valuesList.begin(); itList != valuesList.end(); ++i, ++itList)
2205   {
2206     char label[64];
2207     if (this->ExponentLocation == VTK_EXPONENT_LABELS)
2208     {
2209       snprintf(label, sizeof(label), this->PolarLabelFormat, *itList);
2210       valuesStr->SetValue(i, label);
2211     }
2212     else
2213     {
2214       std::stringstream ss;
2215       if (*itList == 0.0)
2216       {
2217         ss << std::fixed << std::setw(1) << std::setprecision(0) << 0.0;
2218         valuesStr->SetValue(i, ss.str().c_str());
2219         continue;
2220       }
2221 
2222       // get pow of ten of the value to set the precision of the label
2223       int exponent = static_cast<int>(std::floor(log10(fabs(*itList))));
2224       if (exponent < 0)
2225       {
2226         ss << std::fixed << std::setw(1) << setprecision(-exponent) << *itList;
2227       }
2228       else
2229       {
2230         ss << std::fixed << setprecision(1) << *itList;
2231       }
2232 
2233       valuesStr->SetValue(i, ss.str().c_str());
2234     }
2235   }
2236 }
2237 
2238 //-----------------------------------------------------------------------------
AutoScale(vtkViewport * viewport)2239 void vtkPolarAxesActor::AutoScale(vtkViewport* viewport)
2240 {
2241   // Scale polar axis title
2242   vtkAxisActor* axis = this->PolarAxis;
2243   double newTitleScale = vtkAxisFollower::AutoScale(
2244     viewport, this->Camera, this->ScreenSize, axis->GetTitleActor()->GetPosition());
2245   axis->SetTitleScale(newTitleScale);
2246 
2247   // Scale polar axis labels
2248   axis->SetLabelScale(newTitleScale);
2249 
2250   // Loop over radial axes
2251   for (int i = 0; i < this->NumberOfRadialAxes; ++i)
2252   {
2253     axis = this->RadialAxes[i];
2254     // Scale title
2255     newTitleScale = vtkAxisFollower::AutoScale(
2256       viewport, this->Camera, this->ScreenSize, axis->GetTitleActor()->GetPosition());
2257     axis->SetTitleScale(newTitleScale);
2258   }
2259 }
2260 
2261 //-----------------------------------------------------------------------------
SetPole(double p[3])2262 void vtkPolarAxesActor::SetPole(double p[3])
2263 {
2264   this->Pole[0] = p[0];
2265   this->Pole[1] = p[1];
2266   this->Pole[2] = p[2];
2267 
2268   // Update bounds
2269   this->CalculateBounds();
2270   this->Modified();
2271 }
2272 
2273 //-----------------------------------------------------------------------------
SetPole(double x,double y,double z)2274 void vtkPolarAxesActor::SetPole(double x, double y, double z)
2275 {
2276   this->Pole[0] = x;
2277   this->Pole[1] = y;
2278   this->Pole[2] = z;
2279 
2280   // Update bounds
2281   this->CalculateBounds();
2282   this->Modified();
2283 }
2284 
2285 //-----------------------------------------------------------------------------
SetMinimumRadius(double r)2286 void vtkPolarAxesActor::SetMinimumRadius(double r)
2287 {
2288   this->MinimumRadius = r > 0. ? r : 0.;
2289 
2290   // Update bounds
2291   this->CalculateBounds();
2292   this->Modified();
2293 }
2294 
2295 //-----------------------------------------------------------------------------
SetMaximumRadius(double r)2296 void vtkPolarAxesActor::SetMaximumRadius(double r)
2297 {
2298   this->MaximumRadius = r > 0. ? r : 0.;
2299 
2300   // Update bounds
2301   this->CalculateBounds();
2302   this->Modified();
2303 }
2304 
2305 //-----------------------------------------------------------------------------
SetMinimumAngle(double a)2306 void vtkPolarAxesActor::SetMinimumAngle(double a)
2307 {
2308   if (a > 360.)
2309   {
2310     this->MinimumAngle = 360.;
2311   }
2312   else if (a < -360.)
2313   {
2314     this->MinimumAngle = -360.;
2315   }
2316   else
2317   {
2318     this->MinimumAngle = a;
2319   }
2320 
2321   // Update bounds
2322   this->CalculateBounds();
2323   this->Modified();
2324 }
2325 
2326 //-----------------------------------------------------------------------------
SetMaximumAngle(double a)2327 void vtkPolarAxesActor::SetMaximumAngle(double a)
2328 {
2329   if (a > 360.)
2330   {
2331     this->MaximumAngle = 360.;
2332   }
2333   else if (a < -360.)
2334   {
2335     this->MaximumAngle = -360.;
2336   }
2337   else
2338   {
2339     this->MaximumAngle = a;
2340   }
2341 
2342   // Update bounds
2343   this->CalculateBounds();
2344   this->Modified();
2345 }
2346 
2347 //-----------------------------------------------------------------------------
SetUse2DMode(int val)2348 void vtkPolarAxesActor::SetUse2DMode(int val)
2349 {
2350   for (int i = 0; i < this->NumberOfRadialAxes; ++i)
2351   {
2352     this->RadialAxes[i]->SetUse2DMode(val);
2353   }
2354 
2355   this->PolarAxis->SetUse2DMode(val);
2356 }
2357 
2358 //-----------------------------------------------------------------------------
GetUse2DMode()2359 int vtkPolarAxesActor::GetUse2DMode()
2360 {
2361   return this->PolarAxis->GetUse2DMode();
2362 }
2363 
2364 //-----------------------------------------------------------------------------
SetPolarAxisProperty(vtkProperty * prop)2365 void vtkPolarAxesActor::SetPolarAxisProperty(vtkProperty* prop)
2366 {
2367   this->PolarAxisProperty->DeepCopy(prop);
2368   this->PolarAxisProperty->SetLineWidth(this->PolarAxisMajorTickThickness);
2369   this->Modified();
2370 }
2371 
2372 //-----------------------------------------------------------------------------
SetPolarArcsProperty(vtkProperty * prop)2373 void vtkPolarAxesActor::SetPolarArcsProperty(vtkProperty* prop)
2374 {
2375   this->PolarArcsActor->SetProperty(prop);
2376   this->Modified();
2377 }
2378 
2379 //-----------------------------------------------------------------------------
GetPolarArcsProperty()2380 vtkProperty* vtkPolarAxesActor::GetPolarArcsProperty()
2381 {
2382   return this->PolarArcsActor->GetProperty();
2383 }
2384 
2385 //-----------------------------------------------------------------------------
SetSecondaryPolarArcsProperty(vtkProperty * prop)2386 void vtkPolarAxesActor::SetSecondaryPolarArcsProperty(vtkProperty* prop)
2387 {
2388   this->SecondaryPolarArcsActor->SetProperty(prop);
2389   this->Modified();
2390 }
2391 
2392 //-----------------------------------------------------------------------------
GetSecondaryPolarArcsProperty()2393 vtkProperty* vtkPolarAxesActor::GetSecondaryPolarArcsProperty()
2394 {
2395   return this->SecondaryPolarArcsActor->GetProperty();
2396 }
2397 
2398 //-----------------------------------------------------------------------------
SetNumberOfPolarAxisTicks(int tickCountRequired)2399 void vtkPolarAxesActor::SetNumberOfPolarAxisTicks(int tickCountRequired)
2400 {
2401   double rangeLength = fabs(this->Range[1] - this->Range[0]);
2402   double step = this->ComputeIdealStep(
2403     tickCountRequired - 1, rangeLength, VTK_MAXIMUM_NUMBER_OF_POLAR_AXIS_TICKS - 1);
2404   double tmpRangeMajor = this->DeltaRangeMajor;
2405   double tmpRangeMinor = this->DeltaRangeMinor;
2406   this->DeltaRangeMajor = (step == 0.0) ? rangeLength / 10.0 : step;
2407   this->DeltaRangeMinor = (step == 0.0) ? (this->DeltaRangeMajor / 2.0) : (step / 2.0);
2408   if (tmpRangeMajor != this->DeltaRangeMajor || tmpRangeMinor != this->DeltaRangeMinor)
2409   {
2410     this->Modified();
2411   }
2412 }
2413 
2414 //-----------------------------------------------------------------------------
ComputeDeltaAngleRadialAxes(vtkIdType n)2415 void vtkPolarAxesActor::ComputeDeltaAngleRadialAxes(vtkIdType n)
2416 {
2417   if (n <= 1)
2418   {
2419     if (this->DeltaAngleRadialAxes != 45.)
2420     {
2421       this->DeltaAngleRadialAxes = 45.0;
2422       this->Modified();
2423     }
2424     return;
2425   }
2426 
2427   double angleSection = (this->MaximumAngle > this->MinimumAngle)
2428     ? this->MaximumAngle - this->MinimumAngle
2429     : 360.0 - fabs(this->MaximumAngle - this->MinimumAngle);
2430 
2431   // if Min and max angle are the same, interpret it as 360 segment opening
2432   if (vtkMathUtilities::FuzzyCompare(this->MaximumAngle, this->MinimumAngle))
2433   {
2434     angleSection = 360.0;
2435   }
2436 
2437   double step = this->ComputeIdealStep(n - 1, angleSection);
2438   if (step == 0.0)
2439   {
2440     step = angleSection / (n - 1);
2441   }
2442 
2443   if (this->DeltaAngleRadialAxes != step)
2444   {
2445     this->DeltaAngleRadialAxes = step;
2446     this->Modified();
2447   }
2448 }
2449 
2450 //-----------------------------------------------------------------------------
ComputeIdealStep(int subDivsRequired,double rangeLength,int maxSubDivs)2451 double vtkPolarAxesActor::ComputeIdealStep(int subDivsRequired, double rangeLength, int maxSubDivs)
2452 {
2453   double pow10, pow10Start, pow10End;
2454   double rawStep, roundStep, roundStepSup;
2455 
2456   if (rangeLength == 0.0 || subDivsRequired >= maxSubDivs)
2457   {
2458     return 0.0;
2459   }
2460 
2461   if (subDivsRequired <= 1)
2462   {
2463     return rangeLength;
2464   }
2465   if (subDivsRequired <= 4)
2466   {
2467     return rangeLength / subDivsRequired;
2468   }
2469 
2470   // range step, if axis range is strictly subdivided by the number of ticks wished
2471   rawStep = rangeLength / subDivsRequired;
2472 
2473   // pow of 10 order of magnitude
2474   pow10Start = std::floor(log10(rawStep));
2475   pow10End = -10.0;
2476   if (pow10End >= pow10Start)
2477   {
2478     pow10End -= 1.0;
2479   }
2480 
2481   if (rawStep <= std::pow(10, pow10End))
2482   {
2483     return 0.0;
2484   }
2485 
2486   double dividend = rawStep;
2487 
2488   double pow10Step;
2489   double idealStep = 0.0;
2490   double subdivs = 1.0, subdivsSup = 1.0;
2491 
2492   int currentPow10Multiple;
2493 
2494   for (pow10 = pow10Start; pow10 >= pow10End; pow10 -= 1.0)
2495   {
2496     // 10.0, 1.0, 0.1, ...
2497     pow10Step = std::pow(10.0, pow10);
2498 
2499     // example: 4 = 0.4874 / 0.1 for pow10Step = 0.1
2500     currentPow10Multiple = static_cast<int>(dividend / pow10Step);
2501 
2502     // 0.4 = 4 * 0.1
2503     roundStep = currentPow10Multiple * pow10Step;
2504 
2505     // 0.5 = 5 * 0.1
2506     roundStepSup = (currentPow10Multiple + 1) * pow10Step;
2507 
2508     // currentIdealStep is the previous digits of the ideal step we seek
2509     subdivs = rangeLength / (idealStep + roundStep);
2510     subdivsSup = rangeLength / (idealStep + roundStepSup);
2511 
2512     if (fabs(subdivs - subDivsRequired) < 1.0 || fabs(subdivsSup - subDivsRequired) < 1.0)
2513     {
2514       // if currentStep + the current power of 10, is closer to the require tick count
2515       if (fabs(subdivs - subDivsRequired) > fabs(subdivsSup - subDivsRequired) &&
2516         fabs(subdivsSup - subDivsRequired) < 1.0)
2517       {
2518         idealStep += roundStepSup;
2519       }
2520 
2521       // subdivs closer to subdiv than subdivsSup
2522       else
2523       {
2524         idealStep += roundStep;
2525       }
2526       break;
2527     }
2528 
2529     idealStep += roundStep;
2530 
2531     // 0.4874 - 0.4 for roundStep = 0.4
2532     // remainder becomes dividend
2533     dividend = dividend - roundStep;
2534   }
2535 
2536   // if idealStep is too small
2537   if (static_cast<int>(rangeLength / idealStep) > subDivsRequired)
2538   {
2539     idealStep = rawStep;
2540   }
2541 
2542   return idealStep;
2543 }
2544 
2545 //-----------------------------------------------------------------------------
GetNumberOfPolarAxisTicks()2546 int vtkPolarAxesActor::GetNumberOfPolarAxisTicks()
2547 {
2548   double rangeLength = fabs(this->Range[1] - this->Range[0]);
2549   return static_cast<int>((rangeLength / this->DeltaRangeMajor) + 1);
2550 }
2551 
ComputeEllipseAngle(double angleInDegrees,double ratio)2552 double vtkPolarAxesActor::ComputeEllipseAngle(double angleInDegrees, double ratio)
2553 {
2554   double miniAngleEllipse;
2555   double minimumAngleRad = vtkMath::RadiansFromDegrees(angleInDegrees);
2556   minimumAngleRad = std::fmod(minimumAngleRad, 2.0 * vtkMath::Pi());
2557 
2558   // result range: -pi / 2, pi / 2
2559   miniAngleEllipse = atan(tan(minimumAngleRad) / ratio);
2560 
2561   // ellipse range: 0, 2 * pi
2562   if (minimumAngleRad > vtkMath::Pi() / 2 && minimumAngleRad <= vtkMath::Pi())
2563   {
2564     miniAngleEllipse += vtkMath::Pi();
2565   }
2566   else if (minimumAngleRad > vtkMath::Pi() && minimumAngleRad <= 1.5 * vtkMath::Pi())
2567   {
2568     miniAngleEllipse -= vtkMath::Pi();
2569   }
2570   return miniAngleEllipse;
2571 }
2572