ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
vtkScalarBarActorCustom.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 #include <math.h>
11 #include <stdio.h> // for snprintf
12 
13 #include <algorithm>
14 #include <sstream>
15 
16 #include "vtkAxis.h"
17 #include "vtkCellArray.h"
18 #include "vtkCellData.h"
19 #include "vtkContextScene.h"
20 #include "vtkDiscretizableColorTransferFunction.h"
21 #include "vtkDoubleArray.h"
22 #include "vtkFloatArray.h"
23 #include "vtkImageData.h"
24 #include "vtkMath.h"
25 #include "vtkNew.h"
26 #include "vtkObjectFactory.h"
27 #include "vtkPointData.h"
28 #include "vtkPoints.h"
29 #include "vtkPolyData.h"
30 #include "vtkPolyDataMapper2D.h"
31 #include "vtkProperty2D.h"
32 #include "vtkRenderWindow.h"
33 #include "vtkRenderer.h"
34 #include "vtkScalarBarActorInternal.h"
35 #include "vtkScalarsToColors.h"
36 #include "vtkSmartPointer.h"
37 #include "vtkStringArray.h"
38 #include "vtkTextActor.h"
39 #include "vtkTextProperty.h"
40 #include "vtkTexture.h"
41 #include "vtkUnsignedCharArray.h"
42 #include "vtkWindow.h"
43 
44 #if defined(_WIN32) && !defined(__CYGWIN__)
45 #define SNPRINTF _snprintf
46 #else
47 #define SNPRINTF snprintf
48 #endif
49 
50 #define COLOR_TEXTURE_MAP_SIZE 256
51 
52 #define MY_ABS(x) ((x) < 0 ? -(x) : (x))
53 
54 //=============================================================================
56 
57 //=============================================================================
59  this->AspectRatio = 20.0;
60  this->AutomaticLabelFormat = 1;
61  this->DrawTickMarks = 1;
62  this->DrawSubTickMarks = 1;
63  this->AddRangeLabels = 1;
64  this->RangeLabelFormat = NULL;
65  this->SetRangeLabelFormat("%4.3e");
66  this->TitleJustification = VTK_TEXT_CENTERED;
67  this->AddRangeAnnotations = 1;
68  this->AnnotationTextScaling = 1;
69  this->SetVerticalTitleSeparation(4);
70  this->AutomaticAnnotations = 0;
71 
72  this->ScalarBarTexture = vtkTexture::New();
73 
74  this->TickMarks = vtkPolyData::New();
75  this->TickMarksMapper = vtkPolyDataMapper2D::New();
76  this->TickMarksMapper->SetInputData(this->TickMarks);
77  this->TickMarksActor = vtkActor2D::New();
78  this->TickMarksActor->SetMapper(this->TickMarksMapper);
79  this->TickMarksActor->GetPositionCoordinate()->SetReferenceCoordinate(
80  this->PositionCoordinate);
81 
82  this->TickLayoutHelper->SetBehavior(vtkAxis::FIXED);
83  this->TickLayoutHelper->SetScene(this->TickLayoutHelperScene.GetPointer());
84 }
85 
86 //-----------------------------------------------------------------------------
88  this->ScalarBarTexture->Delete();
89 
90  this->TickMarks->Delete();
91  this->TickMarksMapper->Delete();
92  this->TickMarksActor->Delete();
93 
94  if (this->ComponentTitle) {
95  delete[] this->ComponentTitle;
96  this->ComponentTitle = NULL;
97  }
98 
99  delete[] this->RangeLabelFormat;
100  this->RangeLabelFormat = NULL;
101 }
102 
103 //-----------------------------------------------------------------------------
104 void vtkScalarBarActorCustom::PrintSelf(ostream& os, vtkIndent indent) {
105  this->Superclass::PrintSelf(os, indent);
106  os << indent << "AspectRatio: " << this->AspectRatio << endl;
107  os << indent << "AutomaticLabelFormat: " << this->AutomaticLabelFormat
108  << endl;
109  os << indent << "DrawTickMarks: " << this->DrawTickMarks << endl;
110  os << indent << "DrawSubTickMarks: " << this->DrawSubTickMarks << endl;
111  os << indent << "AddRangeLabels: " << this->AddRangeLabels << endl;
112  os << indent << "RangeLabelFormat: "
113  << (this->RangeLabelFormat ? this->RangeLabelFormat : "(null)") << endl;
114  os << indent << "ScalarBarTexture: ";
115  if (this->ScalarBarTexture) {
116  this->ScalarBarTexture->PrintSelf(os << "\n", indent.GetNextIndent());
117  } else {
118  os << "(null)\n";
119  }
120  os << indent << "TickMarks: ";
121  if (this->TickMarks) {
122  this->TickMarks->PrintSelf(os << "\n", indent.GetNextIndent());
123  } else {
124  os << "(null)\n";
125  }
126  os << indent << "TickMarksMapper: ";
127  if (this->TickMarksMapper) {
128  this->TickMarksMapper->PrintSelf(os << "\n", indent.GetNextIndent());
129  } else {
130  os << "(null)\n";
131  }
132  os << indent << "TickMarksActor: ";
133  if (this->TickMarksActor) {
134  this->TickMarksActor->PrintSelf(os << "\n", indent.GetNextIndent());
135  } else {
136  os << "(null)\n";
137  }
138  os << indent << "LabelSpace: " << this->LabelSpace << endl;
139  os << indent << "TitleJustification: " << this->TitleJustification << endl;
140  os << indent << "AddRangeAnnotations: " << this->AddRangeAnnotations
141  << endl;
142  os << indent << "AutomaticAnnotations: " << this->AutomaticAnnotations
143  << endl;
144 }
145 
146 //-----------------------------------------------------------------------------
148  this->ScalarBarTexture->ReleaseGraphicsResources(window);
149 
150  for (unsigned int i = 0; i < this->P->TextActors.size(); i++) {
151  this->P->TextActors[i]->ReleaseGraphicsResources(window);
152  }
153 
154  this->TickMarksActor->ReleaseGraphicsResources(window);
155 
156  this->Superclass::ReleaseGraphicsResources(window);
157 }
158 
159 //----------------------------------------------------------------------------
161  // This ensures that tile scaling can be accounted for in the tick layout:
162  if (vtkRenderer* renderer = vtkRenderer::SafeDownCast(viewport)) {
163  this->TickLayoutHelperScene->SetRenderer(renderer);
164  }
165 
166  return this->Superclass::RenderOpaqueGeometry(viewport);
167 }
168 
169 //----------------------------------------------------------------------------
170 int vtkScalarBarActorCustom::RenderOverlay(vtkViewport* viewport) {
171  int renderedSomething = this->Superclass::RenderOverlay(viewport);
172  if (this->LookupTable && this->LookupTable->GetIndexedLookup()) {
173  return renderedSomething;
174  }
175 
176  if (this->DrawTickMarks) {
177  renderedSomething += this->TickMarksActor->RenderOverlay(viewport);
178  }
179 
180  return renderedSomething;
181 }
182 
183 //-----------------------------------------------------------------------------
185  int minDigits,
186  int targetWidth,
187  int targetHeight,
188  vtkViewport* viewport) {
189  char string[1024];
190 
191  vtkNew<vtkTextActor> textActor;
192  textActor->GetProperty()->DeepCopy(this->GetProperty());
193  textActor->GetPositionCoordinate()->SetReferenceCoordinate(
194  this->PositionCoordinate);
195 
196  // Copy the text property here so that the size of the label prop is not
197  // affected by the automatic adjustment of its text mapper's size.
198  textActor->GetTextProperty()->ShallowCopy(this->LabelTextProperty);
199  if (this->P->Viewport) {
200  textActor->SetTextScaleModeToViewport();
201  textActor->ComputeScaledFont(this->P->Viewport);
202  }
203 
204 // One Visual Studio prior 2015, formats with exponents have three digits by
205 // default whereas on other systems, exponents have two digits. Set to two
206 // digits on Windows for consistent behavior.
207 #if defined(_MSC_VER) && (_MSC_VER < 1900)
208  unsigned int oldWin32ExponentFormat =
209  _set_output_format(_TWO_DIGIT_EXPONENT);
210 #endif
211 
212  if (this->AutomaticLabelFormat) {
213  // Iterate over all format lengths and find the highest precision that
214  // we can represent without going over the target width. If we cannot
215  // fit within the target width, make the smallest possible text.
216  int smallestFoundWidth = VTK_INT_MAX;
217  bool foundValid = false;
218  string[0] = '\0';
219  for (int i = 1 + minDigits; i < 6; i++) {
220  char format[512];
221  char string2[1024];
222  SNPRINTF(format, 511, "%%-0.%dg", i);
223  SNPRINTF(string2, 1023, format, value);
224 
225  // we want the reduced size used so that we can get better fitting
226  // Extra filter: Used to remove unwanted 0 after e+ or e-
227  // i.e.: 1.23e+009 => 1.23e+9
228  std::string strToFilter = string2;
229  std::string ePlus = "e+0";
230  std::string eMinus = "e-0";
231  size_t pos = 0;
232  while ((pos = strToFilter.find(ePlus)) != std::string::npos ||
233  (pos = strToFilter.find(eMinus)) != std::string::npos) {
234  strToFilter.erase(pos + 2, 1);
235  }
236  strcpy(string2, strToFilter.c_str());
237  textActor->SetInput(string2);
238 
239  textActor->SetConstrainedFontSize(viewport, VTK_INT_MAX,
240  targetHeight);
241  double tsize[2];
242  textActor->GetSize(viewport, tsize);
243  if (tsize[0] < targetWidth) {
244  // Found a string that fits. Keep it unless we find something
245  // better.
246  strcpy(string, string2);
247  foundValid = true;
248  } else if ((tsize[0] < smallestFoundWidth) && !foundValid) {
249  // String does not fit, but it is the smallest so far.
250  strcpy(string, string2);
251  smallestFoundWidth = tsize[0];
252  }
253  }
254  } else {
255  // Potential of buffer overrun (onto the stack) here.
256  SNPRINTF(string, 1023, this->LabelFormat, value);
257  }
258 
259  // Set the txt label
260  textActor->SetInput(string);
261 
262 #if defined(_MSC_VER) && (_MSC_VER < 1900)
263  _set_output_format(oldWin32ExponentFormat);
264 #endif
265 
266  // Size the font to fit in the targetHeight, which we are using
267  // to size the font because it is (relatively?) constant.
268  int fontSize = textActor->SetConstrainedFontSize(viewport, VTK_INT_MAX,
269  targetHeight);
270  int maxFontSize = this->LabelTextProperty->GetFontSize();
271  if (fontSize > maxFontSize) {
272  textActor->GetTextProperty()->SetFontSize(maxFontSize);
273  }
274 
275  // Make sure that the string fits in the allotted space.
276  double tsize[2];
277  textActor->GetSize(viewport, tsize);
278  if (tsize[0] > targetWidth) {
279  fontSize = textActor->SetConstrainedFontSize(viewport, targetWidth,
280  targetHeight);
281  }
282 
283  this->P->TextActors.push_back(textActor.GetPointer());
284  return static_cast<int>(this->P->TextActors.size()) - 1;
285 }
286 
287 //----------------------------------------------------------------------------
289  // Let the superclass prepare the actor:
290  this->Superclass::PrepareTitleText();
291 
292  // Set font scaling
293  if (this->P->Viewport) {
294  this->TitleActor->ComputeScaledFont(this->P->Viewport);
295  this->TitleActor->SetTextScaleModeToViewport();
296  }
297 }
298 
299 //----------------------------------------------------------------------------
301  double aspectRatio = this->AspectRatio;
302  if (aspectRatio <= 0.) {
303  aspectRatio = 20.;
304  }
305 
306  // Make the bar's thickness a fraction of its length.
307  this->P->ScalarBarBox.Size[0] =
308  static_cast<int>(ceil(this->P->Frame.Size[1] / aspectRatio));
309  // Make tick marks half the thickness of the scalar bar (+1 to ensure
310  // non-zero size).
311  this->LabelSpace = this->P->ScalarBarBox.Size[0] / 2 + 1;
312 
313  this->P->ScalarBarBox.Posn = this->P->Frame.Posn;
314 
315  // Force opacity to match lookup table
316  vtkDiscretizableColorTransferFunction* lut =
317  vtkDiscretizableColorTransferFunction::SafeDownCast(
318  this->LookupTable);
319  if (lut) {
320  this->SetUseOpacity(lut->IsOpaque() ? 0 : 1);
321  }
322 }
323 
324 //----------------------------------------------------------------------------
326  if ((this->Title == NULL || !strlen(this->Title)) &&
327  (this->ComponentTitle == NULL || !strlen(this->ComponentTitle))) {
328  this->P->TitleBox.Size[0] = this->P->TitleBox.Size[1] = 0;
329  return;
330  }
331 
332  // reset the text size and justification
333  this->TitleActor->GetTextProperty()->ShallowCopy(this->TitleTextProperty);
334  this->TitleActor->GetTextProperty()->SetJustification(
335  this->TitleJustification);
336  this->TitleActor->GetTextProperty()->SetVerticalJustification(
337  this->Orientation == VTK_ORIENT_VERTICAL
338  ? VTK_TEXT_BOTTOM
339  : (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar
340  ? VTK_TEXT_BOTTOM
341  : VTK_TEXT_TOP));
342 
343  double titleSize[2];
344  // Get the actual size of the text.
345  this->TitleActor->GetSize(this->P->Viewport, titleSize);
346  // Now, determine how much space should be reserved for the title and its
347  // padding. For the horizontal orientation, the font size is exactly as
348  // specified by the user. In the vertical case, we limit the font size so
349  // that the remaining box has at least some space (25%) for the scalar bar.
350  if (this->Orientation == VTK_ORIENT_VERTICAL &&
351  (1.5 * titleSize[1] + 3 * this->TextPad >
352  0.75 * this->P->Frame.Size[1])) { // title takes up 3/4 or more of the
353  // frame... better reduce font size
354  this->TitleActor->SetConstrainedFontSize(
355  this->P->Viewport, VTK_INT_MAX,
356  0.5 * this->P->Frame.Size[1] - 3 * this->TextPad);
357  this->TitleActor->GetSize(this->P->Viewport, titleSize);
358  }
359  this->P->TitleBox.Size[this->P->TL[0]] = titleSize[0];
360  this->P->TitleBox.Size[this->P->TL[1]] =
361  1.5 * (titleSize[1] + this->TextPad);
362 
363  // Position the title.
364  switch (this->TitleActor->GetTextProperty()->GetJustification()) {
365  case VTK_TEXT_LEFT:
366  this->P->TitleBox.Posn[0] = this->P->Frame.Posn[0] + this->TextPad;
367  break;
368  case VTK_TEXT_RIGHT:
369  this->P->TitleBox.Posn[0] = this->P->Frame.Posn[0] +
370  this->P->Frame.Size[this->P->TL[0]] -
371  this->TextPad - titleSize[0];
372  break;
373  case VTK_TEXT_CENTERED:
374  default:
375  this->P->TitleBox.Posn[0] =
376  this->P->Frame.Posn[0] +
377  (this->P->Frame.Size[this->P->TL[0]] - titleSize[0]) / 2;
378  break;
379  }
380  if (this->Orientation ==
381  VTK_ORIENT_VERTICAL) { // The title is stacked above the scalar bar.
382  this->P->TitleBox.Posn[1] =
383  this->P->Frame.Posn[1] + this->P->Frame.Size[this->P->TL[1]] -
384  this->P->TitleBox.Size[this->P->TL[1]] / 1.5;
385  this->P->ScalarBarBox.Size[this->P->TL[1]] -= 2 * this->TextPad;
386  } else // VTK_ORIENT_HORIZONTAL
387  {
388  // The title is above or below the ticks,
389  // which either precede or succeed the scalar bar.
390  // We don't know the tick size yet, but we can push the title or scalar
391  // bar away from the frame's origin as required. Then LayoutTicks can
392  // further adjust positions.
393  if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar) {
394  this->P->TitleBox.Posn[1] = this->P->Frame.Posn[1];
395  // push the scalar bar up by the title height.
396  this->P->ScalarBarBox.Posn[1] +=
397  this->P->TitleBox.Size[0] + this->TextPad;
398  this->P->NanBox.Posn[1] +=
399  this->TextPad + this->P->TitleBox.Size[0];
400  } else {
401  this->P->TitleBox.Posn[1] = this->P->Frame.Posn[1] +
402  this->P->ScalarBarBox.Size[0] +
403  this->TextPad;
404  }
405  }
406 }
407 
408 //----------------------------------------------------------------------------
410  this->Superclass::ComputeScalarBarLength();
411 }
412 
413 //----------------------------------------------------------------------------
415  if (this->LookupTable->GetIndexedLookup()) { // no tick marks in indexed
416  // lookup mode.
417  this->NumberOfLabelsBuilt = 0;
418  return;
419  }
420 
421  // Figure out the precision to use based on the width of the scalar bar.
422  vtkNew<vtkTextActor> dummyActor;
423  dummyActor->GetTextProperty()->ShallowCopy(this->LabelTextProperty);
424  dummyActor->SetInput(
425  "()"); // parentheses are taller than numbers for all useful fonts.
426  if (this->P->Viewport) {
427  dummyActor->SetTextScaleModeToViewport();
428  dummyActor->ComputeScaledFont(this->P->Viewport);
429  }
430 
431  double tsize[2];
432  dummyActor->GetSize(this->P->Viewport, tsize);
433  // Now constrain the width of the text with our target height:
434  int targetHeight = static_cast<int>(ceil(tsize[1]));
435 
436  if (this->Orientation == VTK_ORIENT_VERTICAL) {
437  this->P->TickBox.Size[0] = std::max(
438  0, this->P->Frame.Size[0] - this->P->ScalarBarBox.Size[0]);
439  this->P->TickBox.Size[1] = this->P->ScalarBarBox.Size[1];
440  this->P->TickBox.Posn = this->P->ScalarBarBox.Posn;
441  if (this->TextPosition == vtkScalarBarActor::PrecedeScalarBar) {
442  this->P->ScalarBarBox.Posn[0] += this->P->TickBox.Size[0];
443  this->P->NanBox.Posn[0] += this->P->TickBox.Size[0];
444  } else {
445  this->P->TickBox.Posn[0] += this->P->ScalarBarBox.Size[0];
446  }
447 
448  /* FYI, this is how height is used in ConfigureTicks:
449  int maxHeight = static_cast<int>(
450  this->P->ScalarBarBox.Size[1] / this->NumberOfLabels);
451  targetHeight = std::min(targetHeight, maxHeight);
452  */
453  } else {
454  this->P->TickBox.Size[0] = this->LabelSpace + 2 + targetHeight;
455  this->P->TickBox.Size[1] = this->P->ScalarBarBox.Size[1];
456  if (this->TextPosition ==
457  PrecedeScalarBar) { // Push scalar bar and NaN swatch up to make
458  // room for ticks.
459  this->P->TickBox.Posn = this->P->ScalarBarBox.Posn;
460  this->P->ScalarBarBox.Posn[1] += this->P->TickBox.Size[0];
461  this->P->NanBox.Posn[1] += this->P->TickBox.Size[0];
462  } else {
463  this->P->TickBox.Posn[0] = this->P->ScalarBarBox.Posn[0];
464  this->P->TickBox.Posn[1] = this->P->TitleBox.Posn[1];
465  this->P->TitleBox.Posn[1] += this->P->TickBox.Size[0];
466  }
467  }
468 }
469 
471  // Work around an issue in VTK.
472  if (this->P->Labels.empty()) {
473  this->P->AnnotationBoxes->Initialize();
474  }
475  this->Superclass::ConfigureAnnotations();
476 }
477 
479  // This adjustment is necessary because this->TitleActor adjusts
480  // where the text is rendered relative to the position of the
481  // TitleActor based on the justification set on the TitleActor's
482  // TextProperty.
483  double texturePolyDataBounds[6];
484  this->TexturePolyData->GetBounds(texturePolyDataBounds);
485 
486  double scalarBarMinX = texturePolyDataBounds[0];
487  double scalarBarMaxX = texturePolyDataBounds[1];
488  double x;
489  switch (this->TitleActor->GetTextProperty()->GetJustification()) {
490  case VTK_TEXT_LEFT:
491  if (this->Orientation == VTK_ORIENT_VERTICAL) {
492  x = scalarBarMaxX;
493  x -= this->P->TitleBox.Size[this->P->TL[0]];
494  } else {
495  x = scalarBarMinX;
496  }
497  break;
498  case VTK_TEXT_RIGHT:
499  if (this->Orientation == VTK_ORIENT_VERTICAL) {
500  x = scalarBarMinX;
501  x += this->P->TitleBox.Size[this->P->TL[0]];
502  } else {
503  x = scalarBarMaxX;
504  }
505  break;
506  case VTK_TEXT_CENTERED:
507  x = 0.5 * (scalarBarMinX + scalarBarMaxX);
508  break;
509  default:
510  x = 0.;
511  vtkErrorMacro(
512  << "Invalid text justification ("
513  << this->TitleActor->GetTextProperty()->GetJustification()
514  << " for scalar bar title.");
515  break;
516  }
517 
518  double y =
519  this->TitleActor->GetTextProperty()->GetVerticalJustification() ==
520  VTK_TEXT_BOTTOM
521  ? this->P->TitleBox.Posn[1]
522  : this->P->TitleBox.Posn[1] +
523  this->P->TitleBox.Size[this->P->TL[1]];
524 
525  this->TitleActor->SetPosition(x, y);
526 }
527 
528 //----------------------------------------------------------------------------
530  bool isLogTable = (this->LookupTable->UsingLogScale() != 0);
531  double* range = this->LookupTable->GetRange();
532 
533  if (this->LookupTable->GetIndexedLookup()) { // no tick marks in indexed
534  // lookup mode.
535  return;
536  }
537 
538  // Use vtkAxis to compute tick locations:
539  vtkAxis::Location loc;
540  vtkVector2f p1(this->P->TickBox.Posn.Cast<float>().GetData());
541  vtkVector2f p2 = p1;
542  if (this->Orientation == VTK_ORIENT_HORIZONTAL) {
543  p2[0] += static_cast<float>(this->P->TickBox.Size[1]);
544  loc = vtkAxis::BOTTOM;
545  } else {
546  p2[1] += static_cast<float>(this->P->TickBox.Size[1]);
547  loc = vtkAxis::LEFT;
548  }
549 
550  this->TickLayoutHelper->SetPosition(static_cast<int>(loc));
551  this->TickLayoutHelper->SetPoint1(p1);
552  this->TickLayoutHelper->SetPoint2(p2);
553  this->TickLayoutHelper->SetLogScale(isLogTable);
554  this->TickLayoutHelper->SetUnscaledRange(range);
555  this->TickLayoutHelper->SetNumberOfTicks(this->NumberOfLabels);
556  this->TickLayoutHelper->Update();
557 
558  vtkDoubleArray* tickArray = this->TickLayoutHelper->GetTickPositions();
559  vtkStringArray* labelArray = this->TickLayoutHelper->GetTickLabels();
560 
561  // Process and filter the ticks:
562  std::vector<double> ticks;
563  ticks.reserve(tickArray->GetNumberOfTuples());
564  for (vtkIdType i = 0; i < tickArray->GetNumberOfTuples(); ++i) {
565  // The label will be an empty string if it's not supposed to be labeled.
566  // Filter these out.
567  if (labelArray->GetValue(i).empty()) {
568  continue;
569  }
570 
571  ticks.push_back(isLogTable ? std::pow(10.0, tickArray->GetValue(i))
572  : tickArray->GetValue(i));
573  }
574 
575  // minimum number of digits (base 10) to differentiate between tick marks.
576  int minDigits = 1;
577 
578  // Compute the difference between min and max of scalar bar values.
579  double delta = range[1] - range[0];
580  if (delta != 0) {
581  // See what digit of the decimal number the difference is contained in.
582  double dmag = log10(delta);
583  double emag = floor(dmag) - 1;
584  double mag = pow(10.0, emag);
585  if (1.1 * mag > delta) {
586  mag /= 10.0;
587  }
588  int leastDig = floor(log10(fabs(mag)));
589 
590  // Find the the maximum number of digits in the range:
591  double absMax = std::max(std::fabs(range[0]), std::fabs(range[1]));
592  int leadDig = floor(log10(absMax));
593 
594  minDigits = leadDig - leastDig;
595  }
596 
597  // Map from tick to label ID for tick
598  std::vector<int> tickToLabelId(ticks.size(), -1);
599 
600  this->P->TextActors.reserve(ticks.size());
601 
602  vtkNew<vtkCellArray> tickCells;
603  tickCells->Allocate(tickCells->EstimateSize(ticks.size() * 10, 2));
604 
605  vtkNew<vtkPoints> tickPoints;
606  tickPoints->Allocate(ticks.size() * 20);
607 
608  double targetWidth = this->P->TickBox.Size[this->P->TL[0]];
609  double targetHeight = this->P->TickBox.Size[this->P->TL[1]];
610  if (this->Orientation == VTK_ORIENT_HORIZONTAL) {
611  targetWidth = (targetWidth - (ticks.size() - 1) * this->TextPad) /
612  (ticks.size() + 1.);
613  } else // VTK_ORIENT_VERTICAL
614  {
615  targetHeight = (targetHeight - (ticks.size() - 1) * this->TextPad) /
616  (ticks.size() + 1.);
617  // Get the target height based on the selected font size. Set this
618  // as the target height
619  // int desiredFontSize = this->LabelTextProperty->GetFontSize();
620  vtkNew<vtkTextActor> dummyActor;
621  dummyActor->GetTextProperty()->ShallowCopy(this->LabelTextProperty);
622  dummyActor->SetInput("()"); // parentheses are taller than numbers for
623  // all useful fonts.
624  if (this->P->Viewport) {
625  dummyActor->SetTextScaleModeToViewport();
626  dummyActor->ComputeScaledFont(this->P->Viewport);
627  }
628 
629  double tsize[2];
630  dummyActor->GetSize(this->P->Viewport, tsize);
631 
632  if (tsize[1] < targetHeight) {
633  targetHeight = tsize[1];
634  }
635  }
636 
637  bool precede = this->TextPosition == vtkScalarBarActor::PrecedeScalarBar;
638  int minimumFontSize = VTK_INT_MAX;
639  for (int i = 0; i < static_cast<int>(ticks.size()); i++) {
640  double val = ticks[i];
641 
642  // Do not create the label if it is already represented in the min or
643  // max label.
644  if (!((val - 1e-6 * MY_ABS(val + range[0]) > range[0]) &&
645  (val + 1e-6 * MY_ABS(val + range[1]) < range[1]))) {
646  continue;
647  }
648 
649  int labelIdx = this->CreateLabel(val, minDigits, targetWidth,
650  targetHeight, this->P->Viewport);
651  tickToLabelId[i] = labelIdx;
652  vtkTextActor* textActor = this->P->TextActors[labelIdx];
653 
654  int labelFontSize = textActor->GetTextProperty()->GetFontSize();
655  if (labelFontSize < minimumFontSize) {
656  minimumFontSize = labelFontSize;
657  }
658  }
659 
660  // Now place the label actors
661  for (size_t i = 0; i < ticks.size(); i++) {
662  int labelIdx = tickToLabelId[i];
663  if (labelIdx == -1) {
664  // No label
665  continue;
666  }
667  vtkTextActor* textActor = this->P->TextActors[labelIdx];
668 
669  // Make sure every text actor gets the smallest text size to fit
670  // the constraints.
671  textActor->GetTextProperty()->SetFontSize(minimumFontSize);
672 
673  double val = ticks[i];
674 
675  double normVal;
676  if (isLogTable) {
677  normVal = ((log10(val) - log10(range[0])) /
678  (log10(range[1]) - log10(range[0])));
679  } else {
680  normVal = (val - range[0]) / (range[1] - range[0]);
681  }
682 
683  if (this->Orientation == VTK_ORIENT_VERTICAL) {
684  double x = precede ? this->P->TickBox.Posn[0] +
685  this->P->TickBox.Size[0]
686  : this->P->TickBox.Posn[0];
687  double y = normVal * this->P->TickBox.Size[1] +
688  this->P->TickBox.Posn[1];
689  double textSize[2];
690  textActor->GetSize(this->P->Viewport, textSize);
691  y -= textSize[1] / 2; // Adjust to center text.
692  textActor->GetTextProperty()->SetJustification(
693  precede ? VTK_TEXT_RIGHT : VTK_TEXT_LEFT);
694  textActor->SetPosition(
695  precede ? x - this->LabelSpace : x + this->LabelSpace, y);
696  } else // this->Orientation == VTK_ORIENT_HORIZONTAL
697  {
698  double x = this->P->TickBox.Posn[0] +
699  normVal * this->P->TickBox.Size[1];
700  double y = precede ? this->P->TickBox.Posn[1] +
701  this->P->TickBox.Size[0]
702  : this->P->TickBox.Posn[1];
703  textActor->GetTextProperty()->SetJustificationToCentered();
704  textActor->GetTextProperty()->SetVerticalJustification(
705  precede ? VTK_TEXT_TOP : VTK_TEXT_BOTTOM);
706  textActor->SetPosition(
707  x, precede ? y - this->LabelSpace : y + this->LabelSpace);
708  }
709  }
710 
711  // Create minor tick marks.
712  int numTicks = static_cast<int>(ticks.size());
713  if (numTicks > 1) {
714  // Decide how many (maximum) minor ticks we want based on how many
715  // pixels are available.
716  double fractionOfRange;
717  if (isLogTable) {
718  double tickDelta;
719  tickDelta = log10(ticks[numTicks - 1]) - log10(ticks[0]);
720  fractionOfRange = (tickDelta) / (log10(range[1]) - log10(range[0]));
721  } else {
722  double tickDelta;
723  tickDelta = ticks[numTicks - 1] - ticks[0];
724  fractionOfRange = (tickDelta) / (range[1] - range[0]);
725  }
726 
727  double pixelsAvailable = fractionOfRange * this->P->TickBox.Size[1];
728  int maxNumMinorTicks = vtkMath::Floor(pixelsAvailable / 5);
729 
730  // This array lists valid minor to major tick ratios.
731  const int minorRatios[] = {10, 5, 2, 1};
732  const int numMinorRatios =
733  static_cast<int>(sizeof(minorRatios) / sizeof(int));
734  int minorRatio = 0;
735  for (int r = 0; r < numMinorRatios; r++) {
736  minorRatio = minorRatios[r];
737  int numMinorTicks = (numTicks - 1) * minorRatio;
738  if (numMinorTicks <= maxNumMinorTicks) break;
739  }
740 
741  // Add "fake" major ticks so that the minor ticks extend to bar ranges.
742  double fakeMin, fakeMax;
743  if (!isLogTable) {
744  fakeMin = 2 * ticks[0] - ticks[1];
745  fakeMax = 2 * ticks[numTicks - 1] - ticks[numTicks - 2];
746  } else {
747  fakeMin = pow(10.0, 2 * log10(ticks[0]) - log10(ticks[1]));
748  fakeMax = pow(10.0, 2 * log10(ticks[numTicks - 1]) -
749  log10(ticks[numTicks - 2]));
750  }
751  ticks.insert(ticks.begin(), fakeMin);
752  ticks.insert(ticks.end(), fakeMax);
753  numTicks = static_cast<int>(ticks.size());
754 
755  for (int i = 0; this->DrawSubTickMarks && i < numTicks - 1; i++) {
756  double minorTickRange[2];
757  minorTickRange[0] = ticks[i];
758  minorTickRange[1] = ticks[i + 1];
759  for (int j = 0; j < minorRatio; j++) {
760  double val = (((minorTickRange[1] - minorTickRange[0]) * j) /
761  minorRatio +
762  minorTickRange[0]);
763  double normVal;
764  if (isLogTable) {
765  normVal = ((log10(val) - log10(range[0])) /
766  (log10(range[1]) - log10(range[0])));
767  } else {
768  normVal = (val - range[0]) / (range[1] - range[0]);
769  }
770 
771  // Do not draw ticks out of range.
772  if ((normVal < 0.0) || (normVal > 1.0)) continue;
773 
774  if (this->Orientation == VTK_ORIENT_VERTICAL) {
775  double x = precede ? this->P->TickBox.Posn[0] +
776  this->P->TickBox.Size[0]
777  : this->P->TickBox.Posn[0];
778  double y = normVal * this->P->TickBox.Size[1] +
779  this->P->TickBox.Posn[1];
780  vtkIdType ids[2];
781  ids[0] = tickPoints->InsertNextPoint(
782  precede ? x - this->LabelSpace + 2 : x - 2, y, 0.0);
783  ids[1] = tickPoints->InsertNextPoint(
784  precede ? x + 2 : x + this->LabelSpace - 2, y, 0.0);
785  tickCells->InsertNextCell(2, ids);
786  } else // ths->Orientation == VTK_ORIENT_HORIZONTAL
787  {
788  double x = this->P->TickBox.Posn[0] +
789  normVal * this->P->TickBox.Size[1];
790  double y = precede ? this->P->TickBox.Posn[1] +
791  this->P->TickBox.Size[0]
792  : this->P->TickBox.Posn[1];
793  vtkIdType ids[2];
794  ids[0] = tickPoints->InsertNextPoint(
795  x, precede ? y + 2 : y + this->LabelSpace - 2, 0.0);
796  ids[1] = tickPoints->InsertNextPoint(
797  x, precede ? y - this->LabelSpace + 2 : y - 2, 0.0);
798  tickCells->InsertNextCell(2, ids);
799  }
800  }
801  }
802  }
803 
804  this->TickMarks->SetLines(tickCells.GetPointer());
805  this->TickMarks->SetPoints(tickPoints.GetPointer());
806 
807  // "Mute" the color of the tick marks.
808  double color[3];
809  // this->TickMarksActor->GetProperty()->GetColor(color);
810  this->LabelTextProperty->GetColor(color);
811  vtkMath::RGBToHSV(color, color);
812  if (color[2] > 0.5) {
813  color[2] -= 0.2;
814  } else {
815  color[2] += 0.2;
816  }
817  vtkMath::HSVToRGB(color, color);
818  this->TickMarksActor->GetProperty()->SetColor(color);
819 
820  if (this->AddRangeLabels) {
821  // Save state and set preferred parameters
822  int previousAutomaticLabelFormat = this->GetAutomaticLabelFormat();
823  this->SetAutomaticLabelFormat(0);
824 
825  std::string previousLabelFormat(this->GetLabelFormat());
826  this->SetLabelFormat(this->RangeLabelFormat);
827 
828  this->CreateLabel(range[0], minDigits, targetWidth, targetHeight,
829  this->P->Viewport);
830  this->CreateLabel(range[1], minDigits, targetWidth, targetHeight,
831  this->P->Viewport);
832 
833  // Restore state
834  this->AutomaticLabelFormat = previousAutomaticLabelFormat;
835  this->SetLabelFormat(previousLabelFormat.c_str());
836 
837  // Now change the font size of the min/max text actors to the minimum
838  // font size of all the text actors.
839  for (size_t i = this->P->TextActors.size() - 2;
840  i < this->P->TextActors.size(); ++i) {
841  vtkTextActor* textActor = this->P->TextActors[i];
842 
843  // Keep min/max labels the same size as the rest of the labels
844  textActor->GetTextProperty()->SetFontSize(minimumFontSize);
845 
846  double val = range[i - (this->P->TextActors.size() - 2)];
847 
848  double normVal;
849  if (isLogTable) {
850  normVal = ((log10(val) - log10(range[0])) /
851  (log10(range[1]) - log10(range[0])));
852  } else {
853  normVal = (val - range[0]) / (range[1] - range[0]);
854  }
855 
856  if (this->Orientation == VTK_ORIENT_VERTICAL) {
857  double x = precede ? this->P->TickBox.Posn[0] +
858  this->P->TickBox.Size[0]
859  : this->P->TickBox.Posn[0];
860  double y = normVal * this->P->TickBox.Size[1] +
861  this->P->TickBox.Posn[1];
862  double textSize[2];
863  textActor->GetSize(this->P->Viewport, textSize);
864  y -= textSize[1] / 2; // Adjust to center text.
865  textActor->GetTextProperty()->SetJustification(
866  precede ? VTK_TEXT_RIGHT : VTK_TEXT_LEFT);
867  textActor->SetPosition(
868  precede ? x - this->LabelSpace : x + this->LabelSpace,
869  y);
870  } else // this->Orientation == VTK_ORIENT_HORIZONTAL
871  {
872  double x = this->P->TickBox.Posn[0] +
873  normVal * this->P->TickBox.Size[1];
874  double y = precede ? this->P->TickBox.Posn[1] +
875  this->P->TickBox.Size[0]
876  : this->P->TickBox.Posn[1];
877  textActor->GetTextProperty()->SetJustificationToCentered();
878  textActor->GetTextProperty()->SetVerticalJustification(
879  precede ? VTK_TEXT_TOP : VTK_TEXT_BOTTOM);
880  textActor->SetPosition(x, precede ? y - this->LabelSpace
881  : y + this->LabelSpace);
882  }
883 
884  // Turn off visibility of any labels that overlap the min/max labels
885  double bbox[4];
886  textActor->GetBoundingBox(this->P->Viewport, bbox);
887  double* pos = textActor->GetPosition();
888  bbox[0] += pos[0];
889  bbox[1] += pos[0];
890  bbox[2] += pos[1];
891  bbox[3] += pos[1];
892 
893  for (size_t j = 0; j < tickToLabelId.size(); ++j) {
894  int labelIdx = tickToLabelId[j];
895  if (labelIdx == -1) {
896  // No label
897  continue;
898  }
899  vtkTextActor* labelActor = this->P->TextActors[labelIdx];
900 
901  double labelbbox[4];
902  double* labelpos;
903  labelActor->GetBoundingBox(this->P->Viewport, labelbbox);
904  labelpos = labelActor->GetPosition();
905  labelbbox[0] += labelpos[0];
906  labelbbox[1] += labelpos[0];
907  labelbbox[2] += labelpos[1];
908  labelbbox[3] += labelpos[1];
909 
910  // Does label bounding box intersect min/max label bounding box?
911  bool xoverlap =
912  !((labelbbox[0] < bbox[0] && labelbbox[1] < bbox[0]) ||
913  (labelbbox[0] > bbox[1] && labelbbox[1] > bbox[1]));
914  bool yoverlap =
915  !((labelbbox[2] < bbox[2] && labelbbox[3] < bbox[2]) ||
916  (labelbbox[2] > bbox[3] && labelbbox[3] > bbox[3]));
917  if (xoverlap && yoverlap) {
918  labelActor->SetVisibility(0);
919  }
920  }
921  }
922  }
923 
924  // Loop range accounts for "fake" min max ticks
925  for (size_t i = 1; ticks.size() > 0 && i < ticks.size() - 1; i++) {
926  int labelIdx = tickToLabelId[i - 1];
927  if (labelIdx == -1) {
928  // No label
929  continue;
930  }
931  vtkTextActor* textActor = this->P->TextActors[labelIdx];
932  if (textActor->GetVisibility() == 0) {
933  continue;
934  }
935 
936  double val = ticks[i];
937 
938  double normVal;
939  if (isLogTable) {
940  normVal = ((log10(val) - log10(range[0])) /
941  (log10(range[1]) - log10(range[0])));
942  } else {
943  normVal = (val - range[0]) / (range[1] - range[0]);
944  }
945 
946  if (this->Orientation == VTK_ORIENT_VERTICAL) {
947  double x = precede ? this->P->TickBox.Posn[0] +
948  this->P->TickBox.Size[0]
949  : this->P->TickBox.Posn[0];
950  double y = normVal * this->P->TickBox.Size[1] +
951  this->P->TickBox.Posn[1];
952  vtkIdType ids[2];
953  ids[0] = tickPoints->InsertNextPoint(x - this->LabelSpace + 2, y,
954  0.0);
955  ids[1] = tickPoints->InsertNextPoint(x + this->LabelSpace - 2, y,
956  0.0);
957  tickCells->InsertNextCell(2, ids);
958  } else // this->Orientation == VTK_ORIENT_HORIZONTAL
959  {
960  double x = this->P->TickBox.Posn[0] +
961  normVal * this->P->TickBox.Size[1];
962  double y = precede ? this->P->TickBox.Posn[1] +
963  this->P->TickBox.Size[0]
964  : this->P->TickBox.Posn[1];
965  vtkIdType ids[2];
966  ids[0] = tickPoints->InsertNextPoint(x, y - this->LabelSpace + 2,
967  0.0);
968  ids[1] = tickPoints->InsertNextPoint(x, y + this->LabelSpace - 2,
969  0.0);
970  tickCells->InsertNextCell(2, ids);
971  }
972  }
973 }
974 
975 //----------------------------------------------------------------------------
976 namespace {
977 static void AddLabelIfUnoccluded(double x,
978  const vtkColor3ub& color,
979  const std::string& label,
980  vtkScalarBarActorInternal* scalarBar) {
981  // Don't place the label if the nearest existing labels are within 1% of
982  // the screen-space size of the scalar bar.
983  bool okLo = true, okHi = true;
984  if (!scalarBar->Labels.empty()) {
985  double delta = scalarBar->ScalarBarBox.Size[1] / 100.;
986  std::map<double, vtkStdString>::iterator clo =
987  scalarBar->Labels.lower_bound(x);
988 
989  if (clo == scalarBar->Labels
990  .end()) { // nothing bounds above, just check below:
991  okLo = (x - scalarBar->Labels.rbegin()->first) > delta;
992  } else {
993  okHi = (clo->first - x) > delta;
994  if (clo != scalarBar->Labels.begin()) { // something bounds below
995  --clo;
996  okLo = (x - clo->first) > delta;
997  }
998  }
999  }
1000  if (okLo && okHi) {
1001  scalarBar->Labels[x] = label;
1002  scalarBar->LabelColors[x] = color;
1003  }
1004 }
1005 } // namespace
1006 
1007 //-----------------------------------------------------------------------------
1009  vtkScalarsToColors* lut = this->LookupTable;
1010  if (lut && !lut->GetIndexedLookup()) {
1011  const double* range = lut->GetRange();
1012  double dr = range[1] - range[0];
1013  double minX = this->P->ScalarBarBox.Posn[this->P->TL[1]];
1014  double maxX = this->P->ScalarBarBox.Size[1] + minX;
1015 
1016  // Add annotations with min and max values
1017  if (this->AddRangeAnnotations) {
1018  this->AddValueLabelIfUnoccluded(range[0], minX, dr);
1019  this->AddValueLabelIfUnoccluded(range[1], maxX, dr);
1020  }
1021 
1022  // Add annotations with corresponding values between discretized colors
1023  vtkDiscretizableColorTransferFunction* transferFunc =
1024  vtkDiscretizableColorTransferFunction::SafeDownCast(
1025  this->LookupTable);
1026  vtkIdType nbValues = lut->GetNumberOfAvailableColors();
1027  if (transferFunc && transferFunc->GetDiscretize() &&
1028  this->AutomaticAnnotations && nbValues) {
1029  double step = dr / nbValues;
1030  double stepPos = (maxX - minX) / nbValues;
1031  for (vtkIdType i = 0; i <= nbValues; i++) {
1032  double value = range[0] + step * i;
1033  double pos = minX + stepPos * i;
1034  this->AddValueLabelIfUnoccluded(value, pos, value - range[0]);
1035  }
1036  }
1037  }
1038 }
1039 
1040 //-----------------------------------------------------------------------------
1042  double pos,
1043  double /*diff*/) {
1044  double lVal = log10(value);
1045  // The least significant digit
1046  int least = (lVal > 0 ? +1 : -1) * static_cast<int>(floor(fabs(lVal)));
1047  // The most significant digit
1048  int most = static_cast<int>(ceil(log10(fabs(value))));
1049  // How many digits of precision are required
1050  int dig = (most == VTK_INT_MIN) ? 3 : (most - least);
1051  // Label
1052  char label[64], fmt[64];
1053  SNPRINTF(fmt, 63, "%%.%dg", dig < 3 ? 3 : dig);
1054  SNPRINTF(label, 63, fmt, value);
1055  vtkColor4d fltCol;
1056  vtkColor3ub col;
1057  this->LookupTable->GetColor(value, fltCol.GetData());
1058  for (int j = 0; j < 3; ++j) {
1059  col.GetData()[j] =
1060  static_cast<unsigned char>(fltCol.GetData()[j] * 255.);
1061  }
1062  AddLabelIfUnoccluded(pos, col, label, this->P);
1063 }
1064 
1065 //-----------------------------------------------------------------------------
1067  vtkNew<vtkFloatArray> tmp;
1068  tmp->SetNumberOfTuples(COLOR_TEXTURE_MAP_SIZE);
1069  double* range = this->LookupTable->GetRange();
1070  int isLogTable = this->LookupTable->UsingLogScale();
1071  for (int i = 0; i < COLOR_TEXTURE_MAP_SIZE; i++) {
1072  double normVal = (double)i / (COLOR_TEXTURE_MAP_SIZE - 1);
1073  double val;
1074  if (isLogTable) {
1075  double lval = log10(range[0]) +
1076  normVal * (log10(range[1]) - log10(range[0]));
1077  val = pow(10.0, lval);
1078  } else {
1079  val = (range[1] - range[0]) * normVal + range[0];
1080  }
1081  tmp->SetValue(i, val);
1082  }
1083  vtkNew<vtkImageData> colorMapImage;
1084  colorMapImage->SetExtent(0, COLOR_TEXTURE_MAP_SIZE - 1, 0, 0, 0, 0);
1085  colorMapImage->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
1086  vtkDataArray* colors = this->LookupTable->MapScalars(
1087  tmp.GetPointer(), VTK_COLOR_MODE_MAP_SCALARS, 0);
1088  colorMapImage->GetPointData()->SetScalars(colors);
1089  colors->Delete();
1090 
1091  this->ScalarBarTexture->SetInputData(colorMapImage.GetPointer());
1092 }
filament::Texture::InternalFormat format
math::float4 color
#define NULL
void PrintSelf(ostream &os, vtkIndent indent) override
vtkNew< vtkContextScene > TickLayoutHelperScene
int RenderOpaqueGeometry(vtkViewport *viewport) override
virtual void AddValueLabelIfUnoccluded(double value, double pos, double diff)
int RenderOverlay(vtkViewport *viewport) override
virtual int CreateLabel(double value, int minDigits, int targetWidth, int targetHeight, vtkViewport *viewport)
void ReleaseGraphicsResources(vtkWindow *) override
vtkPolyDataMapper2D * TickMarksMapper
double colors[3]
const double * e
normal_z y
normal_z x
QTextStream & endl(QTextStream &stream)
Definition: QtCompat.h:718
MiniVec< float, N > floor(const MiniVec< float, N > &a)
Definition: MiniVec.h:75
MiniVec< float, N > ceil(const MiniVec< float, N > &a)
Definition: MiniVec.h:89
#define MY_ABS(x)
#define COLOR_TEXTURE_MAP_SIZE
vtkStandardNewMacro(vtkScalarBarActorCustom)
#define SNPRINTF