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