1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkAxisActor2D.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 "vtkAxisActor2D.h"
16
17 #include "vtkCellArray.h"
18 #include "vtkMath.h"
19 #include "vtkObjectFactory.h"
20 #include "vtkPolyData.h"
21 #include "vtkPolyDataMapper2D.h"
22 #include "vtkTextMapper.h"
23 #include "vtkTextProperty.h"
24 #include "vtkViewport.h"
25 #include "vtkWindow.h"
26
27 #include <cmath>
28 #include <limits>
29
30 vtkStandardNewMacro(vtkAxisActor2D);
31
32 vtkCxxSetObjectMacro(vtkAxisActor2D, LabelTextProperty, vtkTextProperty);
33 vtkCxxSetObjectMacro(vtkAxisActor2D, TitleTextProperty, vtkTextProperty);
34
35 //------------------------------------------------------------------------------
36 // Instantiate this object.
vtkAxisActor2D()37 vtkAxisActor2D::vtkAxisActor2D()
38 {
39 this->PositionCoordinate->SetCoordinateSystemToNormalizedViewport();
40 this->PositionCoordinate->SetValue(0.0, 0.0);
41
42 this->Position2Coordinate->SetCoordinateSystemToNormalizedViewport();
43 this->Position2Coordinate->SetValue(0.75, 0.0);
44 this->Position2Coordinate->SetReferenceCoordinate(nullptr);
45
46 this->NumberOfLabels = 5;
47
48 this->Title = nullptr;
49
50 this->TitlePosition = 0.5;
51
52 this->AdjustLabels = 1;
53
54 this->TickLength = 5;
55 this->MinorTickLength = 3;
56 this->TickOffset = 2;
57 this->NumberOfMinorTicks = 0;
58
59 this->Range[0] = 0.0;
60 this->Range[1] = 1.0;
61
62 this->FontFactor = 1.0;
63 this->LabelFactor = 0.75;
64
65 this->SizeFontRelativeToAxis = 0;
66 this->UseFontSizeFromProperty = 0;
67
68 this->RulerMode = 0;
69 this->RulerDistance = 1.0;
70
71 this->LabelTextProperty = vtkTextProperty::New();
72 this->LabelTextProperty->SetBold(1);
73 this->LabelTextProperty->SetItalic(1);
74 this->LabelTextProperty->SetShadow(1);
75 this->LabelTextProperty->SetFontFamilyToArial();
76
77 this->TitleTextProperty = vtkTextProperty::New();
78 this->TitleTextProperty->ShallowCopy(this->LabelTextProperty);
79
80 this->LabelFormat = new char[8];
81 snprintf(this->LabelFormat, 8, "%s", "%-#6.3g");
82
83 this->TitleMapper = vtkTextMapper::New();
84 this->TitleActor = vtkActor2D::New();
85 this->TitleActor->SetMapper(this->TitleMapper);
86
87 // To avoid deleting/rebuilding create once up front
88 this->NumberOfLabelsBuilt = 0;
89 this->LabelMappers = new vtkTextMapper*[VTK_MAX_LABELS];
90 this->LabelActors = new vtkActor2D*[VTK_MAX_LABELS];
91 for (int i = 0; i < VTK_MAX_LABELS; i++)
92 {
93 this->LabelMappers[i] = vtkTextMapper::New();
94 this->LabelActors[i] = vtkActor2D::New();
95 this->LabelActors[i]->SetMapper(this->LabelMappers[i]);
96 }
97
98 this->Axis = vtkPolyData::New();
99 this->AxisMapper = vtkPolyDataMapper2D::New();
100 this->AxisMapper->SetInputData(this->Axis);
101 this->AxisActor = vtkActor2D::New();
102 this->AxisActor->SetMapper(this->AxisMapper);
103
104 this->AxisVisibility = 1;
105 this->TickVisibility = 1;
106 this->LabelVisibility = 1;
107 this->TitleVisibility = 1;
108
109 this->LastPosition[0] = this->LastPosition[1] = 0;
110 this->LastPosition2[0] = this->LastPosition2[1] = 0;
111
112 this->LastSize[0] = this->LastSize[1] = 0;
113 this->LastMaxLabelSize[0] = this->LastMaxLabelSize[1] = 0;
114 }
115
116 //------------------------------------------------------------------------------
~vtkAxisActor2D()117 vtkAxisActor2D::~vtkAxisActor2D()
118 {
119 delete[] this->LabelFormat;
120 this->LabelFormat = nullptr;
121
122 this->TitleMapper->Delete();
123 this->TitleActor->Delete();
124
125 delete[] this->Title;
126 this->Title = nullptr;
127
128 if (this->LabelMappers != nullptr)
129 {
130 for (int i = 0; i < VTK_MAX_LABELS; i++)
131 {
132 this->LabelMappers[i]->Delete();
133 this->LabelActors[i]->Delete();
134 }
135 delete[] this->LabelMappers;
136 delete[] this->LabelActors;
137 }
138
139 this->Axis->Delete();
140 this->AxisMapper->Delete();
141 this->AxisActor->Delete();
142
143 this->SetLabelTextProperty(nullptr);
144 this->SetTitleTextProperty(nullptr);
145 }
146
147 //------------------------------------------------------------------------------
148 // Build the axis, ticks, title, and labels and render.
149
RenderOpaqueGeometry(vtkViewport * viewport)150 int vtkAxisActor2D::RenderOpaqueGeometry(vtkViewport* viewport)
151 {
152 int i, renderedSomething = 0;
153
154 this->BuildAxis(viewport);
155
156 // Everything is built, just have to render
157 if (this->Title != nullptr && this->Title[0] != 0 && this->TitleVisibility)
158 {
159 renderedSomething += this->TitleActor->RenderOpaqueGeometry(viewport);
160 }
161
162 if (this->AxisVisibility || this->TickVisibility)
163 {
164 renderedSomething += this->AxisActor->RenderOpaqueGeometry(viewport);
165 }
166
167 if (this->LabelVisibility)
168 {
169 for (i = 0; i < this->NumberOfLabelsBuilt; i++)
170 {
171 renderedSomething += this->LabelActors[i]->RenderOpaqueGeometry(viewport);
172 }
173 }
174
175 return renderedSomething;
176 }
177
178 //------------------------------------------------------------------------------
179 // Render the axis, ticks, title, and labels.
180
RenderOverlay(vtkViewport * viewport)181 int vtkAxisActor2D::RenderOverlay(vtkViewport* viewport)
182 {
183 int i, renderedSomething = 0;
184
185 // Everything is built, just have to render
186 if (this->Title != nullptr && this->Title[0] != 0 && this->TitleVisibility)
187 {
188 renderedSomething += this->TitleActor->RenderOverlay(viewport);
189 }
190
191 if (this->AxisVisibility || this->TickVisibility)
192 {
193 renderedSomething += this->AxisActor->RenderOverlay(viewport);
194 }
195
196 if (this->LabelVisibility)
197 {
198 for (i = 0; i < this->NumberOfLabelsBuilt; i++)
199 {
200 renderedSomething += this->LabelActors[i]->RenderOverlay(viewport);
201 }
202 }
203
204 return renderedSomething;
205 }
206
207 //------------------------------------------------------------------------------
208 // Description:
209 // Does this prop have some translucent polygonal geometry?
HasTranslucentPolygonalGeometry()210 vtkTypeBool vtkAxisActor2D::HasTranslucentPolygonalGeometry()
211 {
212 return 0;
213 }
214
215 //------------------------------------------------------------------------------
216 // Release any graphics resources that are being consumed by this actor.
217 // The parameter window could be used to determine which graphic
218 // resources to release.
ReleaseGraphicsResources(vtkWindow * win)219 void vtkAxisActor2D::ReleaseGraphicsResources(vtkWindow* win)
220 {
221 this->TitleActor->ReleaseGraphicsResources(win);
222 for (int i = 0; i < VTK_MAX_LABELS; i++)
223 {
224 this->LabelActors[i]->ReleaseGraphicsResources(win);
225 }
226 this->AxisActor->ReleaseGraphicsResources(win);
227 }
228
229 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)230 void vtkAxisActor2D::PrintSelf(ostream& os, vtkIndent indent)
231 {
232 this->Superclass::PrintSelf(os, indent);
233
234 if (this->TitleTextProperty)
235 {
236 os << indent << "Title Text Property:\n";
237 this->TitleTextProperty->PrintSelf(os, indent.GetNextIndent());
238 }
239 else
240 {
241 os << indent << "Title Text Property: (none)\n";
242 }
243
244 if (this->LabelTextProperty)
245 {
246 os << indent << "Label Text Property:\n";
247 this->LabelTextProperty->PrintSelf(os, indent.GetNextIndent());
248 }
249 else
250 {
251 os << indent << "Label Text Property: (none)\n";
252 }
253
254 os << indent << "Title: " << (this->Title ? this->Title : "(none)") << "\n";
255 os << indent << "Ruler Mode: " << (this->RulerMode ? "On" : "Off") << "\n";
256 os << indent << "Ruler Distance: " << this->GetRulerDistance() << "\n";
257 os << indent << "Number Of Labels: " << this->NumberOfLabels << "\n";
258 os << indent << "Number Of Labels Built: " << this->NumberOfLabelsBuilt << "\n";
259 os << indent << "Range: (" << this->Range[0] << ", " << this->Range[1] << ")\n";
260
261 os << indent << "Label Format: " << this->LabelFormat << "\n";
262 os << indent << "Font Factor: " << this->FontFactor << "\n";
263 os << indent << "Label Factor: " << this->LabelFactor << "\n";
264 os << indent << "Tick Length: " << this->TickLength << "\n";
265 os << indent << "Tick Offset: " << this->TickOffset << "\n";
266
267 os << indent << "Adjust Labels: " << (this->AdjustLabels ? "On\n" : "Off\n");
268
269 os << indent << "Axis Visibility: " << (this->AxisVisibility ? "On\n" : "Off\n");
270
271 os << indent << "Tick Visibility: " << (this->TickVisibility ? "On\n" : "Off\n");
272
273 os << indent << "Label Visibility: " << (this->LabelVisibility ? "On\n" : "Off\n");
274
275 os << indent << "Title Visibility: " << (this->TitleVisibility ? "On\n" : "Off\n");
276
277 os << indent << "MinorTickLength: " << this->MinorTickLength << endl;
278 os << indent << "NumberOfMinorTicks: " << this->NumberOfMinorTicks << endl;
279 os << indent << "TitlePosition: " << this->TitlePosition << endl;
280
281 os << indent
282 << "Size Font Relative To Axis: " << (this->SizeFontRelativeToAxis ? "On\n" : "Off\n");
283 }
284
285 //------------------------------------------------------------------------------
BuildAxis(vtkViewport * viewport)286 void vtkAxisActor2D::BuildAxis(vtkViewport* viewport)
287 {
288 int i, *x, viewportSizeHasChanged, positionsHaveChanged;
289 vtkIdType ptIds[2];
290 double p1[3], p2[3], offset;
291 double interval, deltaX, deltaY;
292 double xTick[3];
293 double theta, val;
294 int *size, stringSize[2];
295 char string[512];
296
297 if (this->TitleVisibility && !this->TitleTextProperty)
298 {
299 vtkErrorMacro(<< "Need title text property to render axis actor");
300 return;
301 }
302
303 if (this->LabelVisibility && !this->LabelTextProperty)
304 {
305 vtkErrorMacro(<< "Need label text property to render axis actor");
306 return;
307 }
308
309 // Check to see whether we have to rebuild everything
310 // Viewport change may not require rebuild
311 positionsHaveChanged = 0;
312 int* lastPosition = this->PositionCoordinate->GetComputedViewportValue(viewport);
313 int* lastPosition2 = this->Position2Coordinate->GetComputedViewportValue(viewport);
314 if (lastPosition[0] != this->LastPosition[0] || lastPosition[1] != this->LastPosition[1] ||
315 lastPosition2[0] != this->LastPosition2[0] || lastPosition2[1] != this->LastPosition2[1])
316 {
317 positionsHaveChanged = 1;
318 }
319
320 // See whether fonts have to be rebuilt (font size depends on viewport size)
321 viewportSizeHasChanged = 0;
322 size = viewport->GetSize();
323 if (this->LastSize[0] != size[0] || this->LastSize[1] != size[1])
324 {
325 viewportSizeHasChanged = 1;
326 this->LastSize[0] = size[0];
327 this->LastSize[1] = size[1];
328 }
329
330 if (!viewport->GetVTKWindow() ||
331 (!positionsHaveChanged && !viewportSizeHasChanged && viewport->GetMTime() < this->BuildTime &&
332 viewport->GetVTKWindow()->GetMTime() < this->BuildTime &&
333 this->GetMTime() < this->BuildTime &&
334 (!this->LabelVisibility || this->LabelTextProperty->GetMTime() < this->BuildTime) &&
335 (!this->TitleVisibility || this->TitleTextProperty->GetMTime() < this->BuildTime)))
336 {
337 return;
338 }
339
340 vtkDebugMacro(<< "Rebuilding axis");
341
342 // Initialize and get important info
343 this->Axis->Initialize();
344 this->AxisActor->SetProperty(this->GetProperty());
345
346 // Compute the location of tick marks and labels
347
348 this->UpdateAdjustedRange();
349
350 interval = (this->AdjustedRange[1] - this->AdjustedRange[0]) / (this->AdjustedNumberOfLabels - 1);
351
352 this->NumberOfLabelsBuilt = this->AdjustedNumberOfLabels;
353
354 // Generate the axis and tick marks.
355 // We'll do our computation in viewport coordinates. First determine the
356 // location of the endpoints.
357 x = this->PositionCoordinate->GetComputedViewportValue(viewport);
358 p1[0] = x[0];
359 p1[1] = x[1];
360 p1[2] = 0.0;
361 this->LastPosition[0] = x[0];
362 this->LastPosition[1] = x[1];
363
364 x = this->Position2Coordinate->GetComputedViewportValue(viewport);
365 p2[0] = x[0];
366 p2[1] = x[1];
367 p2[2] = 0.0;
368 this->LastPosition2[0] = x[0];
369 this->LastPosition2[1] = x[1];
370
371 double *xp1, *xp2, len = 0.0;
372 if (this->SizeFontRelativeToAxis)
373 {
374 xp1 = this->PositionCoordinate->GetComputedDoubleViewportValue(viewport);
375 xp2 = this->Position2Coordinate->GetComputedDoubleViewportValue(viewport);
376 len = sqrt((xp2[0] - xp1[0]) * (xp2[0] - xp1[0]) + (xp2[1] - xp1[1]) * (xp2[1] - xp1[1]));
377 }
378
379 vtkPoints* pts = vtkPoints::New();
380 vtkCellArray* lines = vtkCellArray::New();
381 this->Axis->SetPoints(pts);
382 this->Axis->SetLines(lines);
383 pts->Delete();
384 lines->Delete();
385
386 // Generate point along axis (as well as tick points)
387 deltaX = p2[0] - p1[0];
388 deltaY = p2[1] - p1[1];
389
390 if (deltaX == 0. && deltaY == 0.)
391 {
392 theta = 0.;
393 }
394 else
395 {
396 theta = atan2(deltaY, deltaX);
397 }
398
399 // First axis point, where first tick is located
400 ptIds[0] = pts->InsertNextPoint(p1);
401 xTick[0] = p1[0] + this->TickLength * sin(theta);
402 xTick[1] = p1[1] - this->TickLength * cos(theta);
403 xTick[2] = 0.0;
404 pts->InsertNextPoint(xTick);
405
406 // Set up creation of ticks
407 double p21[3], length;
408 p21[0] = p2[0] - p1[0];
409 p21[1] = p2[1] - p1[1];
410 p21[2] = p2[2] - p1[2];
411 length = vtkMath::Normalize(p21);
412
413 // Sum of all the ticks: minor and majors. Contains the start and end ticks.
414 int numTicks;
415 // Distance between each minor tick.
416 double distance;
417 if (this->RulerMode)
418 {
419 double wp1[3], wp2[3], wp21[3];
420 this->PositionCoordinate->GetValue(wp1);
421 this->Position2Coordinate->GetValue(wp2);
422 wp21[0] = wp2[0] - wp1[0];
423 wp21[1] = wp2[1] - wp1[1];
424 wp21[2] = wp2[2] - wp1[2];
425 const double worldLength = vtkMath::Norm(wp21);
426 const double worldDistance = this->RulerDistance / (this->NumberOfMinorTicks + 1);
427 numTicks = static_cast<int>(worldDistance <= 0.0 ? 0.0 : (worldLength / worldDistance));
428 const double precision = std::numeric_limits<double>::epsilon();
429 const bool hasRemainderInDivision =
430 worldDistance <= 0.0 ? false : std::fmod(worldLength, worldDistance) > precision;
431 // numTicks must contain the start and end ticks
432 // Don't add the end tick if it is already in numTicks:
433 // when wLength / wDistance is an integer.
434 numTicks += hasRemainderInDivision ? 2 : 1;
435 // Tick distance was computed in world coordinates, convert to viewport
436 // coordinates.
437 const double worldToLocalRatio = (worldLength <= 0.0 ? 0.0 : length / worldLength);
438 distance = worldDistance * worldToLocalRatio;
439 }
440 else
441 {
442 numTicks = (this->AdjustedNumberOfLabels - 1) * (this->NumberOfMinorTicks + 1) + 1;
443 distance = length / (numTicks - 1);
444 }
445
446 // Only draw the inner ticks (not the start/end ticks)
447 for (i = 1; i < numTicks - 1; i++)
448 {
449 int tickLength = 0;
450 if (i % (this->NumberOfMinorTicks + 1) == 0)
451 {
452 tickLength = this->TickLength;
453 }
454 else
455 {
456 tickLength = this->MinorTickLength;
457 }
458 xTick[0] = p1[0] + i * p21[0] * distance;
459 xTick[1] = p1[1] + i * p21[1] * distance;
460 pts->InsertNextPoint(xTick);
461 xTick[0] = xTick[0] + tickLength * sin(theta);
462 xTick[1] = xTick[1] - tickLength * cos(theta);
463 pts->InsertNextPoint(xTick);
464 }
465
466 // Last axis point
467 ptIds[1] = pts->InsertNextPoint(p2);
468 xTick[0] = p2[0] + this->TickLength * sin(theta);
469 xTick[1] = p2[1] - this->TickLength * cos(theta);
470 pts->InsertNextPoint(xTick);
471
472 // Add the axis if requested
473 if (this->AxisVisibility)
474 {
475 lines->InsertNextCell(2, ptIds);
476 }
477
478 // Create lines representing the tick marks
479 if (this->TickVisibility)
480 {
481 for (i = 0; i < numTicks; i++)
482 {
483 ptIds[0] = 2 * i;
484 ptIds[1] = 2 * i + 1;
485 lines->InsertNextCell(2, ptIds);
486 }
487 }
488
489 // Build the labels
490 if (this->LabelVisibility)
491 {
492 // Update the labels text. Do it only if the range has been adjusted,
493 // i.e. if we think that new labels must be created.
494 // WARNING: if LabelFormat has changed, they should be recreated too
495 // but at this point the check on LabelFormat is "included" in
496 // UpdateAdjustedRange(), which is the function that update
497 // AdjustedRangeBuildTime or not.
498 vtkMTimeType labeltime = this->AdjustedRangeBuildTime;
499 if (this->AdjustedRangeBuildTime > this->BuildTime)
500 {
501 for (i = 0; i < this->AdjustedNumberOfLabels; i++)
502 {
503 val = this->AdjustedRange[0] + i * interval;
504 snprintf(string, sizeof(string), this->LabelFormat, val);
505 this->LabelMappers[i]->SetInput(string);
506
507 // Check if the label text has changed
508
509 if (this->LabelMappers[i]->GetMTime() > labeltime)
510 {
511 labeltime = this->LabelMappers[i]->GetMTime();
512 }
513 }
514 }
515
516 // Copy prop and text prop eventually
517 for (i = 0; i < this->AdjustedNumberOfLabels; i++)
518 {
519 if (this->LabelTextProperty->GetMTime() > this->BuildTime ||
520 this->AdjustedRangeBuildTime > this->BuildTime)
521 {
522 // Shallow copy here so that the size of the label prop is not
523 // affected by the automatic adjustment of its text mapper's
524 // size (i.e. its mapper's text property is identical except
525 // for the font size which will be modified later). This
526 // allows text actors to share the same text property, and in
527 // that case specifically allows the title and label text prop
528 // to be the same.
529 this->LabelMappers[i]->GetTextProperty()->ShallowCopy(this->LabelTextProperty);
530 }
531 }
532
533 // Resize the mappers if needed (i.e. viewport has changed, than
534 // font size should be changed, or label text property has changed,
535 // or some of the labels have changed (got bigger for example)
536 if (positionsHaveChanged || viewportSizeHasChanged ||
537 this->LabelTextProperty->GetMTime() > this->BuildTime || labeltime > this->BuildTime)
538 {
539 if (!this->SizeFontRelativeToAxis)
540 {
541 vtkTextMapper::SetMultipleRelativeFontSize(viewport, this->LabelMappers,
542 this->AdjustedNumberOfLabels, size, this->LastMaxLabelSize,
543 0.015 * this->FontFactor * this->LabelFactor);
544 }
545 else
546 {
547 int minFontSize = 1000, fontSize, minLabel = 0;
548 for (i = 0; i < this->AdjustedNumberOfLabels; i++)
549 {
550 fontSize = this->LabelMappers[i]->SetConstrainedFontSize(viewport,
551 static_cast<int>((1.0 / this->AdjustedNumberOfLabels) * len),
552 static_cast<int>(0.2 * len));
553 if (fontSize < minFontSize)
554 {
555 minFontSize = fontSize;
556 minLabel = i;
557 }
558 }
559 for (i = 0; i < this->AdjustedNumberOfLabels; i++)
560 {
561 this->LabelMappers[i]->GetTextProperty()->SetFontSize(minFontSize);
562 }
563 this->LabelMappers[minLabel]->GetSize(viewport, this->LastMaxLabelSize);
564 }
565 }
566
567 // Position the mappers
568 for (i = 0; i < this->AdjustedNumberOfLabels; i++)
569 {
570 pts->GetPoint((this->NumberOfMinorTicks + 1) * 2 * i + 1, xTick);
571 this->LabelMappers[i]->GetSize(viewport, stringSize);
572 vtkAxisActor2D::SetOffsetPosition(xTick, theta, this->LastMaxLabelSize[0],
573 this->LastMaxLabelSize[1], this->TickOffset, this->LabelActors[i]);
574 }
575 } // If labels visible
576
577 // Now build the title
578 if (this->Title != nullptr && this->Title[0] != 0 && this->TitleVisibility)
579 {
580 this->TitleMapper->SetInput(this->Title);
581
582 if (this->TitleTextProperty->GetMTime() > this->BuildTime)
583 {
584 // Shallow copy here so that the size of the title prop is not
585 // affected by the automatic adjustment of its text mapper's
586 // size (i.e. its mapper's text property is identical except for
587 // the font size which will be modified later). This allows text
588 // actors to share the same text property, and in that case
589 // specifically allows the title and label text prop to be the same.
590 this->TitleMapper->GetTextProperty()->ShallowCopy(this->TitleTextProperty);
591 }
592
593 if (positionsHaveChanged || viewportSizeHasChanged ||
594 this->TitleTextProperty->GetMTime() > this->BuildTime)
595 {
596 if (!this->UseFontSizeFromProperty)
597 {
598 if (!this->SizeFontRelativeToAxis)
599 {
600 vtkTextMapper::SetRelativeFontSize(
601 this->TitleMapper, viewport, size, stringSize, 0.015 * this->FontFactor);
602 }
603 else
604 {
605 this->TitleMapper->SetConstrainedFontSize(
606 viewport, static_cast<int>(0.33 * len), static_cast<int>(0.2 * len));
607 this->TitleMapper->GetSize(viewport, stringSize);
608 }
609 }
610 else
611 {
612 this->TitleMapper->GetSize(viewport, stringSize);
613 }
614 }
615 else
616 {
617 this->TitleMapper->GetSize(viewport, stringSize);
618 }
619
620 xTick[0] = p1[0] + (p2[0] - p1[0]) * this->TitlePosition;
621 xTick[1] = p1[1] + (p2[1] - p1[1]) * this->TitlePosition;
622 xTick[0] = xTick[0] + (this->TickLength + this->TickOffset) * sin(theta);
623 xTick[1] = xTick[1] - (this->TickLength + this->TickOffset) * cos(theta);
624
625 offset = 0.0;
626 if (this->LabelVisibility)
627 {
628 offset = vtkAxisActor2D::ComputeStringOffset(
629 this->LastMaxLabelSize[0], this->LastMaxLabelSize[1], theta);
630 }
631
632 vtkAxisActor2D::SetOffsetPosition(
633 xTick, theta, stringSize[0], stringSize[1], static_cast<int>(offset), this->TitleActor);
634 } // If title visible
635
636 this->BuildTime.Modified();
637 }
638
639 //------------------------------------------------------------------------------
UpdateAdjustedRange()640 void vtkAxisActor2D::UpdateAdjustedRange()
641 {
642 // Try not to update/adjust the range to often, do not update it
643 // if the object has not been modified.
644 // Nevertheless, try the following optimization: there is no need to
645 // update the range if the position coordinate of this actor have
646 // changed. But since vtkActor2D::GetMTime() includes the check for
647 // both Position and Position2 coordinates, we will have to bypass
648 // it.
649
650 // NOLINTNEXTLINE(bugprone-parent-virtual-call)
651 if (this->vtkActor2D::Superclass::GetMTime() <= this->AdjustedRangeBuildTime)
652 {
653 return;
654 }
655
656 if (this->AdjustLabels)
657 {
658 double interval;
659 vtkAxisActor2D::ComputeRange(this->Range, this->AdjustedRange, this->NumberOfLabels,
660 this->AdjustedNumberOfLabels, interval);
661 }
662 else
663 {
664 this->AdjustedNumberOfLabels = this->NumberOfLabels;
665 this->AdjustedRange[0] = this->Range[0];
666 this->AdjustedRange[1] = this->Range[1];
667 }
668 this->AdjustedRangeBuildTime.Modified();
669 }
670
671 // this is a helper function that computes some useful functions
672 // for an axis. It returns the number of ticks
vtkAxisActor2DComputeTicks(double sRange[2],double & interval,double & root)673 static int vtkAxisActor2DComputeTicks(double sRange[2], double& interval, double& root)
674 {
675 // first we try assuming the first value is reasonable
676 int numTicks;
677 double range = fabs(sRange[1] - sRange[0]);
678 int rootPower = static_cast<int>(floor(log10(range) - 1));
679 root = pow(10.0, rootPower);
680 // val will be between 10 and 100 inclusive of 10 but not 100
681 double val = range / root;
682 // first we check for an exact match
683 for (numTicks = 5; numTicks < 9; ++numTicks)
684 {
685 if (fabs(val / (numTicks - 1.0) - floor(val / (numTicks - 1.0))) < .0001)
686 {
687 interval = val * root / (numTicks - 1.0);
688 return numTicks;
689 }
690 }
691
692 // if there isn't an exact match find a reasonable value
693 int newIntScale = 10;
694 if (val > 10)
695 {
696 newIntScale = 12;
697 }
698 if (val > 12)
699 {
700 newIntScale = 15;
701 }
702 if (val > 15)
703 {
704 newIntScale = 18;
705 }
706 if (val > 18)
707 {
708 newIntScale = 20;
709 }
710 if (val > 20)
711 {
712 newIntScale = 25;
713 }
714 if (val > 25)
715 {
716 newIntScale = 30;
717 }
718 if (val > 30)
719 {
720 newIntScale = 40;
721 }
722 if (val > 40)
723 {
724 newIntScale = 50;
725 }
726 if (val > 50)
727 {
728 newIntScale = 60;
729 }
730 if (val > 60)
731 {
732 newIntScale = 70;
733 }
734 if (val > 70)
735 {
736 newIntScale = 80;
737 }
738 if (val > 80)
739 {
740 newIntScale = 90;
741 }
742 if (val > 90)
743 {
744 newIntScale = 100;
745 }
746
747 // how many ticks should we have
748 switch (newIntScale)
749 {
750 case 12:
751 case 20:
752 case 40:
753 case 80:
754 numTicks = 5;
755 break;
756 case 18:
757 case 30:
758 case 60:
759 case 90:
760 numTicks = 7;
761 break;
762 case 10:
763 case 15:
764 case 25:
765 case 50:
766 case 100:
767 numTicks = 6;
768 break;
769 case 70:
770 numTicks = 8;
771 break;
772 }
773
774 interval = newIntScale * root / (numTicks - 1.0);
775 return numTicks;
776 }
777
778 //------------------------------------------------------------------------------
779 // this method takes an initial range and an initial number of ticks and then
780 // computes a final range and number of ticks so that two properties are
781 // satisfied. First the final range includes at least the initial range, and
782 // second the final range divided by the number of ticks (minus one) will be a
783 // reasonable interval
ComputeRange(double inRange[2],double outRange[2],int vtkNotUsed (inNumTicks),int & numTicks,double & interval)784 void vtkAxisActor2D::ComputeRange(double inRange[2], double outRange[2], int vtkNotUsed(inNumTicks),
785 int& numTicks, double& interval)
786 {
787 // Handle the range
788 double sRange[2];
789 if (inRange[0] < inRange[1])
790 {
791 sRange[0] = inRange[0];
792 sRange[1] = inRange[1];
793 }
794 else if (inRange[0] > inRange[1])
795 {
796 sRange[1] = inRange[0];
797 sRange[0] = inRange[1];
798 }
799 else // they're equal, so perturb them by 1 percent
800 {
801 double perturb = 100.;
802 if (inRange[0] == 0.0)
803 { // if they are both zero, then just perturb about zero
804 sRange[0] = -1 / perturb;
805 sRange[1] = 1 / perturb;
806 }
807 else
808 {
809 sRange[0] = inRange[0] - inRange[0] / perturb;
810 sRange[1] = inRange[0] + inRange[0] / perturb;
811 }
812 }
813
814 double root;
815 numTicks = vtkAxisActor2DComputeTicks(sRange, interval, root);
816
817 // is the starting point reasonable?
818 if (fabs(sRange[0] / root - floor(sRange[0] / root)) < 0.01)
819 {
820 outRange[0] = sRange[0];
821 outRange[1] = outRange[0] + (numTicks - 1.0) * interval;
822 }
823 else
824 {
825 // OK the starting point is not a good number, so we must widen the range
826 // First see if the current range will handle moving the start point
827 outRange[0] = floor(sRange[0] / root) * root;
828 if (outRange[0] + (numTicks - 1.0) * interval <= sRange[1])
829 {
830 outRange[1] = outRange[0] + (numTicks - 1.0) * interval;
831 }
832 else
833 {
834 // Finally in this case we must switch to a larger range to
835 // have reasonable starting and ending values
836 sRange[0] = outRange[0];
837 numTicks = vtkAxisActor2DComputeTicks(sRange, interval, root);
838 outRange[1] = outRange[0] + (numTicks - 1.0) * interval;
839 }
840 }
841
842 // Adust if necessary
843 if (inRange[0] > inRange[1])
844 {
845 sRange[0] = outRange[1];
846 outRange[1] = outRange[0];
847 outRange[0] = sRange[0];
848 interval = -interval;
849 }
850 }
851
852 //------------------------------------------------------------------------------
853 // Position text with respect to a point (xTick) where the angle of the line
854 // from the point to the center of the text is given by theta. The offset
855 // is the spacing between ticks and labels.
SetOffsetPosition(double xTick[3],double theta,int stringWidth,int stringHeight,int offset,vtkActor2D * actor)856 void vtkAxisActor2D::SetOffsetPosition(
857 double xTick[3], double theta, int stringWidth, int stringHeight, int offset, vtkActor2D* actor)
858 {
859 double x, y, center[2];
860 int pos[2];
861
862 x = stringWidth / 2.0 + offset;
863 y = stringHeight / 2.0 + offset;
864
865 center[0] = xTick[0] + x * sin(theta);
866 center[1] = xTick[1] - y * cos(theta);
867
868 pos[0] = static_cast<int>(center[0] - stringWidth / 2.0);
869 pos[1] = static_cast<int>(center[1] - stringHeight / 2.0);
870
871 actor->SetPosition(pos[0], pos[1]);
872 }
873
874 //------------------------------------------------------------------------------
ComputeStringOffset(double width,double height,double theta)875 double vtkAxisActor2D::ComputeStringOffset(double width, double height, double theta)
876 {
877 double f1 = height * cos(theta);
878 double f2 = width * sin(theta);
879 return (1.2 * sqrt(f1 * f1 + f2 * f2));
880 }
881
882 //------------------------------------------------------------------------------
ShallowCopy(vtkProp * prop)883 void vtkAxisActor2D::ShallowCopy(vtkProp* prop)
884 {
885 vtkAxisActor2D* a = vtkAxisActor2D::SafeDownCast(prop);
886 if (a != nullptr)
887 {
888 this->SetRange(a->GetRange());
889 this->SetNumberOfLabels(a->GetNumberOfLabels());
890 this->SetLabelFormat(a->GetLabelFormat());
891 this->SetAdjustLabels(a->GetAdjustLabels());
892 this->SetTitle(a->GetTitle());
893 this->SetTickLength(a->GetTickLength());
894 this->SetTickOffset(a->GetTickOffset());
895 this->SetAxisVisibility(a->GetAxisVisibility());
896 this->SetTickVisibility(a->GetTickVisibility());
897 this->SetLabelVisibility(a->GetLabelVisibility());
898 this->SetTitleVisibility(a->GetTitleVisibility());
899 this->SetFontFactor(a->GetFontFactor());
900 this->SetLabelFactor(a->GetLabelFactor());
901 this->SetLabelTextProperty(a->GetLabelTextProperty());
902 this->SetTitleTextProperty(a->GetTitleTextProperty());
903 }
904
905 // Now do superclass
906 this->vtkActor2D::ShallowCopy(prop);
907 }
908