vtkAxis.cxx 55.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkAxis.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkAxis.h"

18
#include "vtkMath.h"
19
#include "vtkNew.h"
20
#include "vtkContext2D.h"
21
#include "vtkContextScene.h"
22
#include "vtkPen.h"
23
#include "vtkChart.h"
24
#include "vtkTextProperty.h"
25
#include "vtkVector.h"
26
#include "vtkFloatArray.h"
27
#include "vtkDoubleArray.h"
28 29
#include "vtkStringArray.h"
#include "vtkStdString.h"
30 31
#include "vtkAxisExtended.h"

32
#include <sstream>
33 34
#include "vtkObjectFactory.h"

35 36
#include "vtksys/RegularExpression.hxx"

37
#include <algorithm>
38
#include <cstdio>
39
#include <limits>
40
#include <cmath>
41 42 43 44 45 46 47

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkAxis);

//-----------------------------------------------------------------------------
vtkAxis::vtkAxis()
{
48
  this->Position = -1;
49 50
  this->Point1 = this->Position1.GetData();
  this->Point2 = this->Position2.GetData();
51 52
  this->Position1.Set(0.0, 10.0);
  this->Position2.Set(0.0, 10.0);
53
  this->TickInterval = 1.0;
54
  this->NumberOfTicks = -1;
55 56 57 58 59 60 61 62 63 64 65
  this->LabelProperties = vtkTextProperty::New();
  this->LabelProperties->SetColor(0.0, 0.0, 0.0);
  this->LabelProperties->SetFontSize(12);
  this->LabelProperties->SetFontFamilyToArial();
  this->LabelProperties->SetJustificationToCentered();
  this->TitleProperties = vtkTextProperty::New();
  this->TitleProperties->SetColor(0.0, 0.0, 0.0);
  this->TitleProperties->SetFontSize(12);
  this->TitleProperties->SetFontFamilyToArial();
  this->TitleProperties->SetBold(1);
  this->TitleProperties->SetJustificationToCentered();
66 67
  this->Minimum = 0.0;
  this->Maximum = 6.66;
68 69
  this->UnscaledMinimum = this->Minimum;
  this->UnscaledMaximum = this->Maximum;
70 71
  this->MinimumLimit = std::numeric_limits<double>::max() * -1.;
  this->MaximumLimit = std::numeric_limits<double>::max();
72 73 74 75
  this->UnscaledMinimumLimit = std::numeric_limits<double>::max() * -1.;
  this->UnscaledMaximumLimit = std::numeric_limits<double>::max();
  this->NonLogUnscaledMinLimit = this->UnscaledMinimumLimit;
  this->NonLogUnscaledMaxLimit = this->UnscaledMaximumLimit;
Julien Finet's avatar
Julien Finet committed
76 77
  this->Margins[0] = 15;
  this->Margins[1] = 5;
78
  this->LogScale = false;
79
  this->LogScaleActive = false;
80
  this->GridVisible = true;
81
  this->LabelsVisible = true;
82
  this->RangeLabelsVisible = false;
83
  this->LabelOffset = 7;
84
  this->TicksVisible = true;
85
  this->AxisVisible = true;
86
  this->Precision = 2;
87
  this->LabelFormat = "%g";
88
  this->RangeLabelFormat = "%g";
89 90
  this->Notation = vtkAxis::STANDARD_NOTATION;
  this->Behavior = vtkAxis::AUTO;
91
  this->Pen = vtkPen::New();
92
  this->TitleAppended = false;
93
  this->ScalingFactor = 1.0;
94
  this->Shift = 0.0;
95

96 97 98 99 100
  this->Pen->SetColor(0, 0, 0);
  this->Pen->SetWidth(1.0);
  this->GridPen = vtkPen::New();
  this->GridPen->SetColor(242, 242, 242);
  this->GridPen->SetWidth(1.0);
101 102 103 104 105
  this->TickPositions = vtkSmartPointer<vtkDoubleArray>::New();
  this->TickScenePositions = vtkSmartPointer<vtkFloatArray>::New();
  this->TickLabels = vtkSmartPointer<vtkStringArray>::New();
  this->UsingNiceMinMax = false;
  this->TickMarksDirty = true;
106
  this->MaxLabel[0] = this->MaxLabel[1] = 0.0;
107
  this->Resized = true;
108
  this->SetPosition(vtkAxis::LEFT);
109
  this->TickLabelAlgorithm = vtkAxis::TICK_SIMPLE;
110
  this->CustomTickLabels = false;
111 112 113 114 115
}

//-----------------------------------------------------------------------------
vtkAxis::~vtkAxis()
{
116 117
  this->TitleProperties->Delete();
  this->LabelProperties->Delete();
118 119
  this->Pen->Delete();
  this->GridPen->Delete();
120 121
}

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
void vtkAxis::SetPosition(int position)
{
  if (this->Position != position)
    {
    this->Position = position;
    // Draw the axis label
    switch (this->Position)
      {
      case vtkAxis::LEFT:
        this->TitleProperties->SetOrientation(90.0);
        this->TitleProperties->SetVerticalJustificationToBottom();
        this->LabelProperties->SetJustificationToRight();
        this->LabelProperties->SetVerticalJustificationToCentered();
        break;
      case vtkAxis::RIGHT:
        this->TitleProperties->SetOrientation(90.0);
        this->TitleProperties->SetVerticalJustificationToTop();
        this->LabelProperties->SetJustificationToLeft();
        this->LabelProperties->SetVerticalJustificationToCentered();
        break;
      case vtkAxis::BOTTOM:
        this->TitleProperties->SetOrientation(0.0);
        this->TitleProperties->SetVerticalJustificationToTop();
        this->LabelProperties->SetJustificationToCentered();
        this->LabelProperties->SetVerticalJustificationToTop();
        break;
      case vtkAxis::TOP:
        this->TitleProperties->SetOrientation(0.0);
        this->TitleProperties->SetVerticalJustificationToBottom();
        this->LabelProperties->SetJustificationToCentered();
        this->LabelProperties->SetVerticalJustificationToBottom();
        break;
      case vtkAxis::PARALLEL:
        this->TitleProperties->SetOrientation(0.0);
        this->TitleProperties->SetVerticalJustificationToTop();
        this->LabelProperties->SetJustificationToRight();
        this->LabelProperties->SetVerticalJustificationToCentered();
        break;
      }
    }
}

164
//-----------------------------------------------------------------------------
165 166
void vtkAxis::SetPoint1(const vtkVector2f &pos)
{
167 168 169 170 171 172
  if (this->Position1 != pos)
    {
    this->Position1 = pos;
    this->Resized = true;
    this->Modified();
    }
173 174
}

175
//-----------------------------------------------------------------------------
176 177 178 179 180
void vtkAxis::SetPoint1(float x, float y)
{
  this->SetPoint1(vtkVector2f(x, y));
}

181
//-----------------------------------------------------------------------------
182 183 184 185 186
vtkVector2f vtkAxis::GetPosition1()
{
  return this->Position1;
}

187
//-----------------------------------------------------------------------------
188 189
void vtkAxis::SetPoint2(const vtkVector2f &pos)
{
190 191 192 193 194 195
  if (this->Position2 != pos)
    {
    this->Position2 = pos;
    this->Resized = true;
    this->Modified();
    }
196 197
}

198
//-----------------------------------------------------------------------------
199 200 201 202 203
void vtkAxis::SetPoint2(float x, float y)
{
  this->SetPoint2(vtkVector2f(x, y));
}

204
//-----------------------------------------------------------------------------
205 206 207 208 209
vtkVector2f vtkAxis::GetPosition2()
{
  return this->Position2;
}

210
//-----------------------------------------------------------------------------
211 212 213 214 215 216 217 218 219 220 221
void vtkAxis::SetNumberOfTicks(int numberOfTicks)
{
  if (this->NumberOfTicks != numberOfTicks)
    {
    this->TickMarksDirty = true;
    this->Resized = true;
    this->NumberOfTicks = numberOfTicks;
    this->Modified();
    }
}

222 223 224
//-----------------------------------------------------------------------------
void vtkAxis::Update()
{
225 226 227 228 229
  if (!this->Visible || this->BuildTime > this->MTime)
    {
    return;
    }

230
  this->UpdateLogScaleActive(false);
231 232
  if ((this->Behavior == vtkAxis::AUTO || this->Behavior == vtkAxis::FIXED) &&
      this->TickMarksDirty)
233
    {
234 235
    // Regenerate the tick marks/positions if necessary
    // Calculate where the first tick mark should be drawn
236 237 238 239 240 241 242 243
    // FIXME: We need a specific resize event, to handle position change
    // independently.
    this->RecalculateTickSpacing();
    double first = ceil(this->Minimum / this->TickInterval)
      * this->TickInterval;
    double last = first;
    double interval(this->TickInterval);
    if (this->Minimum > this->Maximum)
244
      {
245
      interval *= -1.0;
246
      }
247
    for (int i = 0; i < 500; ++i)
248
      {
249 250 251
      last += interval;
      if ((interval > 0.0 && last > this->Maximum) ||
          (interval <= 0.0 && last < this->Maximum))
252
        {
253 254
        this->GenerateTickLabels(first, last - this->TickInterval);
        break;
255 256
        }
      }
257 258
    }

259 260 261 262 263
  // Figure out what type of behavior we should follow
  if (this->Resized &&
      (this->Behavior == vtkAxis::AUTO || this->Behavior == vtkAxis::FIXED))
    {
    this->RecalculateTickSpacing();
264
    this->Resized = false;
265 266
    }

267
  // Figure out the scaling and origin for the scene
268 269
  double scaling = 0.0;
  double origin = 0.0;
270 271 272 273 274 275 276 277 278 279 280 281
  if (this->Point1[0] == this->Point2[0]) // x1 == x2, therefore vertical
    {
    scaling = (this->Point2[1] - this->Point1[1]) /
              (this->Maximum - this->Minimum);
    origin = this->Point1[1];
    }
  else
    {
    scaling = (this->Point2[0] - this->Point1[0]) /
              (this->Maximum - this->Minimum);
    origin = this->Point1[0];
    }
282

283 284
  if (this->TickPositions->GetNumberOfTuples() !=
      this->TickLabels->GetNumberOfTuples())
285
    {
286 287
    // Generate the tick labels based on the tick positions
    this->GenerateTickLabels();
288
    }
289

290 291 292 293
  vtkIdType n = this->TickPositions->GetNumberOfTuples();
  this->TickScenePositions->SetNumberOfTuples(n);
  for (vtkIdType i = 0; i < n; ++i)
    {
294
    int iPos = vtkContext2D::FloatToInt(origin +
295 296 297
                                (this->TickPositions->GetValue(i) -
                                 this->Minimum) * scaling);
    this->TickScenePositions->InsertValue(i, iPos);
298
    }
299

300
  this->BuildTime.Modified();
301 302 303 304 305 306 307 308
}

//-----------------------------------------------------------------------------
bool vtkAxis::Paint(vtkContext2D *painter)
{
  // This is where everything should be drawn, or dispatched to other methods.
  vtkDebugMacro(<< "Paint event called in vtkAxis.");

309 310
  this->UpdateLogScaleActive(false);

311 312 313 314 315
  if (!this->Visible)
    {
    return false;
    }

316 317
  this->GetBoundingRect(painter);

318
  painter->ApplyPen(this->Pen);
319
  // Draw this axis
320 321 322 323 324
  if (this->AxisVisible)
    {
    painter->DrawLine(this->Point1[0], this->Point1[1],
                      this->Point2[0], this->Point2[1]);
    }
325

326
  // Draw the axis title if there is one
327
  if (!this->Title.empty())
328
    {
329 330
    int x = 0;
    int y = 0;
331 332
    painter->ApplyTextProp(this->TitleProperties);

333
    // Draw the axis label
334
    if (this->Position == vtkAxis::LEFT)
335 336
      {
      // Draw the axis label
337 338
      x = vtkContext2D::FloatToInt(this->Point1[0] - this->MaxLabel[0] - 10);
      y = vtkContext2D::FloatToInt(this->Point1[1] + this->Point2[1]) / 2;
339
      }
340 341 342
    else if (this->Position == vtkAxis::RIGHT)
      {
      // Draw the axis label
343 344
      x = vtkContext2D::FloatToInt(this->Point1[0] + this->MaxLabel[0] + 10);
      y = vtkContext2D::FloatToInt(this->Point1[1] + this->Point2[1]) / 2;
345 346
      }
    else if (this->Position == vtkAxis::BOTTOM)
347
      {
348 349
      x = vtkContext2D::FloatToInt(this->Point1[0] + this->Point2[0]) / 2;
      y = vtkContext2D::FloatToInt(this->Point1[1] - this->MaxLabel[1] - 10);
350
      }
351 352
    else if (this->Position == vtkAxis::TOP)
      {
353 354
      x = vtkContext2D::FloatToInt(this->Point1[0] + this->Point2[0]) / 2;
      y = vtkContext2D::FloatToInt(this->Point1[1] + this->MaxLabel[1] + 10);
355
      }
356 357
    else if (this->Position == vtkAxis::PARALLEL)
      {
358 359
      x = vtkContext2D::FloatToInt(this->Point1[0]);
      y = vtkContext2D::FloatToInt(this->Point1[1] - this->MaxLabel[1] - 15);
360
      }
361 362
    painter->DrawString(x, y, this->Title);
    }
363

364
  // Now draw the tick marks
365
  painter->ApplyTextProp(this->LabelProperties);
366

367
  float *tickPos = this->TickScenePositions->GetPointer(0);
368
  vtkStdString *tickLabel = this->TickLabels->GetPointer(0);
369
  vtkIdType numMarks = this->TickScenePositions->GetNumberOfTuples();
370

371
  // There are five possible tick label positions, which should be set by the
372
  // class laying out the axes.
373
  float tickLength = 5;
374
  float labelOffset = this->LabelOffset;
375 376
  if (this->Position == vtkAxis::LEFT || this->Position == vtkAxis::PARALLEL ||
      this->Position == vtkAxis::BOTTOM)
377
    {
378 379 380
    // The other side of the axis line.
    tickLength *= -1.0;
    labelOffset *= -1.0;
381
    }
382

383 384 385 386 387 388 389 390 391 392 393
  vtkVector2i tileScale(1);
  if (!this->Scene)
    {
    vtkWarningMacro("vtkAxis needs a vtkContextScene to determine window "
                    "properties. Assuming no tile scaling is set.");
    }
  else
    {
    tileScale = this->Scene->GetLogicalTileScale();
    }

394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
  vtkRectf minLabelRect(0, 0, 0, 0);
  vtkRectf maxLabelRect(0, 0, 0, 0);
  float* minLabelBounds = minLabelRect.GetData();
  float* maxLabelBounds = maxLabelRect.GetData();

  // Optionally draw min/max labels
  if (this->RangeLabelsVisible)
    {
    vtkStdString minString = this->GenerateSprintfLabel(this->UnscaledMinimum, this->RangeLabelFormat);
    vtkStdString maxString = this->GenerateSprintfLabel(this->UnscaledMaximum, this->RangeLabelFormat);

    painter->ComputeJustifiedStringBounds(minString, minLabelBounds);
    painter->ComputeJustifiedStringBounds(maxString, maxLabelBounds);

    float minLabelShift[2] = {0, 0};
    float maxLabelShift[2] = {0, 0};

    // Compute where the string should go...
    if (this->Position == vtkAxis::LEFT || this->Position == vtkAxis::PARALLEL ||
        this->Position == vtkAxis::RIGHT)
      {
      minLabelShift[0] = this->Point1[0] + labelOffset;
      minLabelShift[1] = this->Point1[1];
      maxLabelShift[0] = this->Point2[0] + labelOffset;
      maxLabelShift[1] = this->Point2[1];
      if (this->TicksVisible)
        {
        painter->DrawLine(this->Point1[0] + tickLength, this->Point1[1],
                          this->Point1[0]             , this->Point1[1]);
        painter->DrawLine(this->Point2[0] + tickLength, this->Point2[1],
                          this->Point2[0]             , this->Point2[1]);
        }
      }
    else if (this->Position == vtkAxis::TOP || this->Position == vtkAxis::BOTTOM)
      {
      minLabelShift[0] = this->Point1[0];
      minLabelShift[1] = this->Point1[1] + labelOffset;
      maxLabelShift[0] = this->Point2[0];
      maxLabelShift[1] = this->Point2[1] + labelOffset;
      if (this->TicksVisible)
        {
        painter->DrawLine(this->Point1[0], this->Point1[1] + tickLength,
                          this->Point1[0], this->Point1[1]);
        painter->DrawLine(this->Point2[0], this->Point2[1] + tickLength,
                          this->Point2[0], this->Point2[1]             );
        }
      }

    // Now draw the labels
    painter->DrawString(minLabelShift[0], minLabelShift[1], minString);
    painter->DrawString(maxLabelShift[0], maxLabelShift[1], maxString);

    minLabelBounds[0] += minLabelShift[0];
    minLabelBounds[1] += minLabelShift[1];
    maxLabelBounds[0] += maxLabelShift[0];
    maxLabelBounds[1] += maxLabelShift[1];

    // Pad the range label bounds by a few pixels.
    float pad = 4;
    minLabelBounds[0] -= pad;
    minLabelBounds[1] -= pad;
    minLabelBounds[2] += 2*pad;
    minLabelBounds[3] += 2*pad;

    maxLabelBounds[0] -= pad;
    maxLabelBounds[1] -= pad;
    maxLabelBounds[2] += 2*pad;
    maxLabelBounds[3] += 2*pad;
    }

464 465 466
  // Horizontal or vertical axis.
  if (this->Position == vtkAxis::LEFT || this->Position == vtkAxis::PARALLEL ||
      this->Position == vtkAxis::RIGHT)
467
    {
468 469 470 471
    // Adptating tickLength and labelOffset to the tiling of the scene
    tickLength *= tileScale.GetX();
    labelOffset *= tileScale.GetX();

472 473 474
    // Draw the tick marks and labels
    for (vtkIdType i = 0; i < numMarks; ++i)
      {
475 476
      // Skip any tick positions that are outside of the axis range.
      if (!this->InRange(this->TickPositions->GetValue(i)))
477
        {
478
        continue;
479
        }
480 481 482 483

      // Don't skip if range labels aren't visible
      bool skipTick = this->RangeLabelsVisible;
      if (this->LabelsVisible)
484
        {
485 486 487 488 489 490 491 492 493 494 495 496 497
        float bounds[4];
        painter->ComputeJustifiedStringBounds(tickLabel[i], bounds);
        float pos[2] = { this->Point1[0] + labelOffset, tickPos[i] };
        bounds[0] += pos[0];
        bounds[1] += pos[1];

        vtkRectf boundsRect(bounds[0], bounds[1], bounds[2], bounds[3]);
        if (!boundsRect.IntersectsWith(minLabelRect) &&
            !boundsRect.IntersectsWith(maxLabelRect))
          {
          painter->DrawString(pos[0], pos[1], tickLabel[i]);
          skipTick = false;
          }
498
        }
499 500

      if (this->TicksVisible && !skipTick)
501
        {
502 503
        painter->DrawLine(this->Point1[0] + tickLength, tickPos[i],
                          this->Point1[0]             , tickPos[i]);
504
        }
505 506
      }
    }
507
  else if (this->Position == vtkAxis::TOP || this->Position == vtkAxis::BOTTOM)
508
    {
509 510 511 512 513

    // Adptating tickLength and labelOffset to the tiling of the scene
    tickLength *= tileScale.GetY();
    labelOffset *= tileScale.GetY();

514 515 516
    // Draw the tick marks and labels
    for (vtkIdType i = 0; i < numMarks; ++i)
      {
517 518 519 520 521
      // Skip any tick positions that are outside of the axis range.
      if (!this->InRange(this->TickPositions->GetValue(i)))
        {
        continue;
        }
522 523 524

      bool skipTick = this->RangeLabelsVisible;
      if (this->LabelsVisible)
525
        {
526 527 528 529 530 531 532 533 534 535 536 537
        float bounds[4];
        painter->ComputeJustifiedStringBounds(tickLabel[i], bounds);
        float pos[2] = { tickPos[i], this->Point1[1] + labelOffset };
        bounds[0] += pos[0];
        bounds[1] += pos[1];
        vtkRectf boundsRect(bounds[0], bounds[1], bounds[2], bounds[3]);
        if (!boundsRect.IntersectsWith(minLabelRect) &&
            !boundsRect.IntersectsWith(maxLabelRect))
          {
          painter->DrawString(pos[0], pos[1], tickLabel[i]);
          skipTick = false;
          }
538
        }
539 540

      if (this->TicksVisible && !skipTick)
541
        {
542 543
        painter->DrawLine(tickPos[i], this->Point1[1] + tickLength,
                          tickPos[i], this->Point1[1]);
544
        }
545

546 547
      }
    }
548 549 550 551 552
  else
    {
    vtkWarningMacro("Unknown position encountered in the paint call: "
                    << this->Position);
    }
553

554
  return true;
555 556
}

557 558
//-----------------------------------------------------------------------------
void vtkAxis::SetMinimum(double minimum)
559
{
560
  minimum = std::max(minimum, this->MinimumLimit);
561 562 563 564
  if (this->Minimum == minimum)
    {
    return;
    }
565
  this->Minimum = minimum;
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582
  this->UnscaledMinimum = this->LogScaleActive ? pow(10., this->Minimum) : this->Minimum;
  this->UsingNiceMinMax = false;
  this->TickMarksDirty = true;
  this->Modified();
  this->InvokeEvent(vtkChart::UpdateRange);
}

//-----------------------------------------------------------------------------
void vtkAxis::SetUnscaledMinimum(double minimum)
{
  minimum = std::max(minimum, this->UnscaledMinimumLimit);
  if (this->UnscaledMinimum == minimum)
    {
    return;
    }
  this->UnscaledMinimum = minimum;
  this->UpdateLogScaleActive(true);
583 584 585
  this->UsingNiceMinMax = false;
  this->TickMarksDirty = true;
  this->Modified();
586
  this->InvokeEvent(vtkChart::UpdateRange);
587 588
}

589
//-----------------------------------------------------------------------------
590
void vtkAxis::SetMinimumLimit(double lowest)
591
{
592
  if (this->MinimumLimit == lowest)
593 594 595
    {
    return;
    }
596
  this->MinimumLimit = lowest;
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
  if (this->LogScaleActive)
    {
    if (this->UnscaledMinimum < 0)
      {
      this->UnscaledMaximumLimit = -1. * pow(10., this->MinimumLimit);
      }
    else
      {
      this->UnscaledMinimumLimit = pow(10., this->MinimumLimit);
      }
    }
  else
    {
    this->UnscaledMinimumLimit = this->MinimumLimit;
    }
612
  if (this->Minimum < lowest)
613 614 615 616 617
    {
    this->SetMinimum(lowest);
    }
}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
//-----------------------------------------------------------------------------
void vtkAxis::SetUnscaledMinimumLimit(double lowest)
{
  if (this->UnscaledMinimumLimit == lowest)
    {
    return;
    }
  this->UnscaledMinimumLimit = lowest;
  this->NonLogUnscaledMinLimit = this->UnscaledMinimumLimit;
  this->MinimumLimit = this->LogScaleActive ?
    log10(this->UnscaledMinimumLimit) : this->UnscaledMinimumLimit;
  if (this->UnscaledMinimum < lowest)
    {
    this->SetUnscaledMinimum(lowest);
    }
}

635 636 637
//-----------------------------------------------------------------------------
void vtkAxis::SetMaximum(double maximum)
{
638
  maximum = std::min(maximum, this->MaximumLimit);
639 640 641 642
  if (this->Maximum == maximum)
    {
    return;
    }
643
  this->Maximum = maximum;
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660
  this->UnscaledMaximum = this->LogScaleActive ? pow(10., this->Maximum) : this->Maximum;
  this->UsingNiceMinMax = false;
  this->TickMarksDirty = true;
  this->Modified();
  this->InvokeEvent(vtkChart::UpdateRange);
}

//-----------------------------------------------------------------------------
void vtkAxis::SetUnscaledMaximum(double maximum)
{
  maximum = std::min(maximum, this->UnscaledMaximumLimit);
  if (this->UnscaledMaximum == maximum)
    {
    return;
    }
  this->UnscaledMaximum = maximum;
  this->UpdateLogScaleActive(true);
661 662 663
  this->UsingNiceMinMax = false;
  this->TickMarksDirty = true;
  this->Modified();
664
  this->InvokeEvent(vtkChart::UpdateRange);
665 666
}

667
//-----------------------------------------------------------------------------
668
void vtkAxis::SetMaximumLimit(double highest)
669
{
670
  if (this->MaximumLimit == highest)
671 672 673
    {
    return;
    }
674
  this->MaximumLimit = highest;
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689
  if (this->LogScaleActive)
    {
    if (this->UnscaledMaximum < 0)
      {
      this->UnscaledMinimumLimit = -1. * pow(10., this->MaximumLimit);
      }
    else
      {
      this->UnscaledMaximumLimit = pow(10., this->MaximumLimit);
      }
    }
  else
    {
    this->UnscaledMaximumLimit = this->MaximumLimit;
    }
690
  if (this->Maximum > highest)
691 692 693 694 695
    {
    this->SetMaximum(highest);
    }
}

696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
//-----------------------------------------------------------------------------
void vtkAxis::SetUnscaledMaximumLimit(double highest)
{
  if (this->UnscaledMaximumLimit == highest)
    {
    return;
    }
  this->UnscaledMaximumLimit = highest;
  this->NonLogUnscaledMaxLimit = this->UnscaledMaximumLimit;
  this->MaximumLimit = this->LogScaleActive ?
    log10(this->UnscaledMaximumLimit) : this->UnscaledMaximumLimit;
  if (this->UnscaledMaximum > highest)
    {
    this->SetUnscaledMaximum(highest);
    }
}

713 714 715 716 717 718 719
//-----------------------------------------------------------------------------
void vtkAxis::SetRange(double minimum, double maximum)
{
  this->SetMinimum(minimum);
  this->SetMaximum(maximum);
}

720
//-----------------------------------------------------------------------------
721 722
void vtkAxis::SetRange(double *range)
{
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744
  if (range)
    {
    this->SetMinimum(range[0]);
    this->SetMaximum(range[1]);
    }
}

//-----------------------------------------------------------------------------
void vtkAxis::SetUnscaledRange(double minimum, double maximum)
{
  this->SetUnscaledMinimum(minimum);
  this->SetUnscaledMaximum(maximum);
}

//-----------------------------------------------------------------------------
void vtkAxis::SetUnscaledRange(double *range)
{
  if (range)
    {
    this->SetUnscaledMinimum(range[0]);
    this->SetUnscaledMaximum(range[1]);
    }
745 746 747
}

//-----------------------------------------------------------------------------
748 749
void vtkAxis::GetRange(double *range)
{
750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
  if (range)
    {
    range[0] = this->Minimum;
    range[1] = this->Maximum;
    }
}

//-----------------------------------------------------------------------------
void vtkAxis::GetUnscaledRange(double *range)
{
  if (range)
    {
    range[0] = this->UnscaledMinimum;
    range[1] = this->UnscaledMaximum;
    }
765 766
}

767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
//-----------------------------------------------------------------------------
void vtkAxis::SetTitle(const vtkStdString &title)
{
  if (this->Title != title)
    {
    this->Title = title;
    this->Modified();
    }
}

//-----------------------------------------------------------------------------
vtkStdString vtkAxis::GetTitle()
{
  return this->Title;
}

783 784 785 786 787 788 789 790 791 792 793 794
//-----------------------------------------------------------------------------
void vtkAxis::SetPrecision(int precision)
{
  if (this->Precision == precision)
    {
    return;
    }
  this->Precision = precision;
  this->TickMarksDirty = true;
  this->Modified();
}

795 796 797 798 799 800 801 802 803 804 805 806 807
//-----------------------------------------------------------------------------
void vtkAxis::SetLabelFormat(const std::string &fmt)
{
  vtkDebugMacro(<< this->GetClassName() << " (" << this
                << "): setting LabelFormat to " << fmt);
  if (this->LabelFormat != fmt)
    {
    this->LabelFormat = fmt;
    this->Modified();
    this->TickMarksDirty = true;
    }
}

808 809 810 811 812 813 814 815 816 817 818 819
//-----------------------------------------------------------------------------
void vtkAxis::SetLogScale(bool logScale)
{
  if (this->LogScale == logScale)
    {
    return;
    }
  this->LogScale = logScale;
  this->UpdateLogScaleActive(false);
  this->Modified();
}

820 821 822 823 824 825 826 827 828 829 830 831
//-----------------------------------------------------------------------------
void vtkAxis::SetNotation(int notation)
{
  if (this->Notation == notation)
    {
    return;
    }
  this->Notation = notation;
  this->TickMarksDirty = true;
  this->Modified();
}

832
//-----------------------------------------------------------------------------
833 834
void vtkAxis::AutoScale()
{
835 836 837 838
  if (this->Behavior != vtkAxis::AUTO)
    {
    return;
    }
839 840

  this->UpdateLogScaleActive(false);
841
  // Calculate the min and max, set the number of ticks and the tick spacing.
842 843
  if (this->TickLabelAlgorithm == vtkAxis::TICK_SIMPLE)
    {
844 845 846 847
    double min = this->Minimum;
    double max = this->Maximum;
    this->TickInterval = this->CalculateNiceMinMax(min, max);
    this->SetRange(min, max);
848
    }
849 850
  this->UsingNiceMinMax = true;
  this->GenerateTickLabels(this->Minimum, this->Maximum);
851 852
}

853 854 855 856 857
//-----------------------------------------------------------------------------
void vtkAxis::RecalculateTickSpacing()
{
  // Calculate the min and max, set the number of ticks and the tick spacing,
  // discard the min and max in this case. TODO: Refactor the function called.
858
  if (this->Behavior == vtkAxis::AUTO || this->Behavior == vtkAxis::FIXED)
859
    {
860 861
    double min = this->Minimum;
    double max = this->Maximum;
862 863 864 865
    if (this->TickLabelAlgorithm == vtkAxis::TICK_SIMPLE)
      {
      this->TickInterval = this->CalculateNiceMinMax(min, max);
      }
866

867 868 869 870
    if (this->UsingNiceMinMax)
      {
      this->GenerateTickLabels(this->Minimum, this->Maximum);
      }
871
    else if (this->TickInterval == -1.0)
872
      {
873
      // if axis do not have a valid tickinterval - return
874 875
      return;
      }
876 877
    else
      {
878 879 880
      // Calculated tickinterval may be 0. So calculation of new minimum and
      // maximum by incrementing/decrementing using tickinterval will fail.
      if (this->TickInterval == 0.0)
881
        {
882 883 884 885 886
        return;
        }
      if (this->Minimum < this->Maximum)
        {
        while (min < this->Minimum)
887
          {
888
          min += this->TickInterval;
889
          }
890
        while (max > this->Maximum)
891
          {
892
          max -= this->TickInterval;
893
          }
894
        }
895
      else
896
        {
897
        while (min > this->Minimum)
898
          {
899
          min -= this->TickInterval;
900
          }
901
        while (max < this->Maximum)
902
          {
903
          max += this->TickInterval;
904
          }
905
        }
906
      this->GenerateTickLabels(min, max);
907 908 909 910 911 912 913 914 915 916
      }
    }
}

//-----------------------------------------------------------------------------
vtkDoubleArray* vtkAxis::GetTickPositions()
{
  return this->TickPositions;
}

917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
//-----------------------------------------------------------------------------
vtkFloatArray* vtkAxis::GetTickScenePositions()
{
  return this->TickScenePositions;
}

//-----------------------------------------------------------------------------
vtkStringArray* vtkAxis::GetTickLabels()
{
  return this->TickLabels;
}

//-----------------------------------------------------------------------------
bool vtkAxis::SetCustomTickPositions(vtkDoubleArray *positions,
                                     vtkStringArray *labels)
{
  if (!positions && !labels)
    {
    this->CustomTickLabels = false;
    this->TickMarksDirty = true;
    this->TickPositions->SetNumberOfTuples(0);
    this->TickLabels->SetNumberOfTuples(0);
    this->Modified();
    return true;
    }
  else if (positions && !labels)
    {
    this->TickPositions->DeepCopy(positions);
    this->TickLabels->SetNumberOfTuples(0);
    this->CustomTickLabels = true;
    this->TickMarksDirty = false;
    this->Modified();
    return true;
    }
  else if (positions && labels)
    {
    if (positions->GetNumberOfTuples() != labels->GetNumberOfTuples())
      {
      return false;
      }
    this->TickPositions->DeepCopy(positions);
    this->TickLabels->DeepCopy(labels);
    this->CustomTickLabels = true;
    this->TickMarksDirty = false;
    this->Modified();
    return true;
    }
  else
    {
    return false;
    }
}

970 971 972 973 974 975 976 977 978 979 980 981 982
//-----------------------------------------------------------------------------
vtkRectf vtkAxis::GetBoundingRect(vtkContext2D* painter)
{
  bool vertical = false;
  if (this->Position == vtkAxis::LEFT || this->Position == vtkAxis::RIGHT ||
      this->Position == vtkAxis::PARALLEL)
    {
    vertical = true;
    }
  // First, calculate the widest tick label
  float widest = 0.0;
  // Second, calculate the tallest tick label
  float tallest = 0.0;
983
  vtkRectf bounds(0, 0, 0, 0);
984
  if (this->LabelsVisible)
985
    {
986 987 988 989 990 991 992 993
    for(vtkIdType i = 0; i < this->TickLabels->GetNumberOfTuples(); ++i)
      {
      painter->ApplyTextProp(this->LabelProperties);
      painter->ComputeStringBounds(this->TickLabels->GetValue(i),
                                   bounds.GetData());
      widest = bounds.GetWidth() > widest ? bounds.GetWidth() : widest;
      tallest = bounds.GetHeight() > tallest ? bounds.GetHeight() : tallest;
      }
994
    }
995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

  if (this->RangeLabelsVisible)
    {
    // Add in the range labels
    vtkStdString minLabel = this->GenerateSprintfLabel(this->UnscaledMinimum, this->RangeLabelFormat);
    vtkStdString maxLabel = this->GenerateSprintfLabel(this->UnscaledMaximum, this->RangeLabelFormat);

    painter->ComputeStringBounds(minLabel, bounds.GetData());
    widest = bounds.GetWidth() > widest ? bounds.GetWidth() : widest;
    tallest = bounds.GetHeight() > tallest ? bounds.GetHeight() : tallest;

    painter->ComputeStringBounds(maxLabel, bounds.GetData());
    widest = bounds.GetWidth() > widest ? bounds.GetWidth() : widest;
    tallest = bounds.GetHeight() > tallest ? bounds.GetHeight() : tallest;
    }

1011 1012 1013 1014
  this->MaxLabel[0] = widest;
  this->MaxLabel[1] = tallest;

  // Then, if there is an axis label, add that in.
1015
  vtkRectf titleBounds(0, 0, 0, 0);
1016
  if (this->Title && !this->Title.empty())
1017 1018 1019 1020 1021 1022 1023 1024
    {
    painter->ApplyTextProp(this->TitleProperties);
    painter->ComputeStringBounds(this->Title,
                                 titleBounds.GetData());
    }

  if (vertical)
    {
Julien Finet's avatar
Julien Finet committed
1025
    bounds.SetWidth(widest + titleBounds.GetWidth() + this->Margins[0]);
1026 1027
    float range = this->Point1[1] < this->Point2[1] ?
          this->Point2[1] - this->Point1[1] : this->Point1[1] - this->Point2[1];
Julien Finet's avatar
Julien Finet committed
1028
    bounds.SetHeight(range + tallest + this->Margins[1]);
1029 1030 1031
    }
  else
    {
Julien Finet's avatar
Julien Finet committed
1032
    bounds.SetHeight(tallest + titleBounds.GetHeight() + this->Margins[0]);
1033 1034
    float range = this->Point1[0] < this->Point2[0] ?
          this->Point2[0] - this->Point1[0] : this->Point1[0] - this->Point2[0];
Julien Finet's avatar
Julien Finet committed
1035
    bounds.SetWidth(range + widest + this->Margins[1]);
1036 1037 1038 1039
    }
  return bounds;
}

1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
//-----------------------------------------------------------------------------
void vtkAxis::UpdateLogScaleActive(bool alwaysUpdateMinMaxFromUnscaled)
{
  bool needUpdate = false;
  if (this->LogScale &&
    this->UnscaledMinimum * this->UnscaledMaximum > 0.)
    {
    if (!this->LogScaleActive)
      {
      this->LogScaleActive = true;
1050
      this->TickMarksDirty = true;
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
      needUpdate = true;
      }
    if (needUpdate || alwaysUpdateMinMaxFromUnscaled)
      {
      if (this->UnscaledMinimum < 0)
        { // Both unscaled min & max are negative, logs must be swapped
        this->Minimum = log10(fabs(this->UnscaledMaximum));
        this->Maximum = log10(fabs(this->UnscaledMinimum));
        if (this->UnscaledMaximumLimit >= 0)
          {
          // The limit is on the other side of 0 relative to the data...
          // move it to the same side as the data.
          // Specifically, allow scrolling equal to the width of the plot.
          this->MinimumLimit = -vtkMath::Inf();
          this->NonLogUnscaledMaxLimit = this->UnscaledMaximumLimit;
          this->UnscaledMaximumLimit = 0.;
          }
        else
          {
          this->MinimumLimit = log10(fabs(this->UnscaledMaximumLimit));
          }
        this->MaximumLimit = log10(fabs(this->UnscaledMinimumLimit));
        }
      else
        {
        this->Minimum = log10(fabs(this->UnscaledMinimum));
        this->Maximum = log10(fabs(this->UnscaledMaximum));
        if (this->UnscaledMinimumLimit <= 0)
          {
          // The limit is on the other side of 0 relative to the data...
          // move it to the same side as the data.
          // Specifically, allow scrolling equal to the width of the plot.
          this->MinimumLimit = -vtkMath::Inf();
          this->NonLogUnscaledMinLimit = this->UnscaledMinimumLimit;
          this->UnscaledMinimumLimit = 0.;
          }
        else
          {
          this->MinimumLimit = log10(fabs(this->UnscaledMinimumLimit));
          }
        this->MaximumLimit = log10(fabs(this->UnscaledMaximumLimit));
        }
      this->Modified();
      }
    }
  else
    {
    if (this->LogScaleActive)
      {
      this->LogScaleActive = false;
1101
      this->TickMarksDirty = true;
1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
      needUpdate = true;
      }
    if (needUpdate || alwaysUpdateMinMaxFromUnscaled)
      {
      this->Minimum = this->UnscaledMinimum;
      this->Maximum = this->UnscaledMaximum;
      this->UnscaledMinimumLimit = this->NonLogUnscaledMinLimit;
      this->UnscaledMaximumLimit = this->NonLogUnscaledMaxLimit;
      this->MinimumLimit = this->UnscaledMinimumLimit;
      this->MaximumLimit = this->UnscaledMaximumLimit;
      this->Modified();
      }
    }
}

1117 1118 1119
//-----------------------------------------------------------------------------
void vtkAxis::GenerateTickLabels(double min, double max)
{
1120 1121 1122 1123 1124
  if (this->CustomTickLabels == true)
    {
    // Never generate new tick labels if custom tick labels are being used.
    return;
    }
1125 1126 1127
  // Now calculate the tick labels, and positions within the axis range
  this->TickPositions->SetNumberOfTuples(0);
  this->TickLabels->SetNumberOfTuples(0);
1128 1129 1130

  // We generate a logarithmic scale when logarithmic axis is activated and the
  // order of magnitude of the axis is higher than 0.6.
1131
  if (this->LogScaleActive)
1132 1133 1134
    {
    // We calculate the first tick mark for lowest order of magnitude.
    // and the last for the highest order of magnitude.
1135
    this->TickInterval = this->CalculateNiceMinMax(min, max);
1136

1137 1138 1139 1140
    bool niceTickMark = false;
    int minOrder = 0;
    int maxOrder = 0;
    double minValue = LogScaleTickMark(pow(double(10.0), double(min)),
1141
                                       true,
1142 1143 1144
                                       niceTickMark,
                                       minOrder);
    double maxValue = LogScaleTickMark(pow(double(10.0), double(max)),
1145
                                       false,
1146 1147
                                       niceTickMark,
                                       maxOrder);
1148 1149

    // We generate the tick marks for all orders of magnitude
1150
    if (maxOrder - minOrder == 0)
1151
      {
1152
      this->GenerateLogSpacedLinearTicks(minOrder, min, max);
1153
      }
1154
    else
1155
      {
1156
      if (maxOrder - minOrder + 1 > 5)
1157
        {
1158 1159
        GenerateLogScaleTickMarks(minOrder, minValue, 9.0, false);
        for(int i = minOrder + 1; i < maxOrder; ++i)
1160 1161 1162
          {
          GenerateLogScaleTickMarks(i, 1.0, 9.0, false);
          }
1163
        GenerateLogScaleTickMarks(maxOrder, 1.0, maxValue, false);
1164 1165 1166
        }
      else
        {
1167 1168
        GenerateLogScaleTickMarks(minOrder, minValue, 9.0);
        for(int i = minOrder + 1; i < maxOrder; ++i)
1169 1170 1171
          {
          GenerateLogScaleTickMarks(i, 1.0, 9.0);
          }
1172
        GenerateLogScaleTickMarks(maxOrder, 1.0, maxValue);
1173
        }
1174
      }
1175 1176 1177
    }
  else
    {
1178
    if (this->TickLabelAlgorithm == vtkAxis::TICK_WILKINSON_EXTENDED)
1179
      {
1180 1181 1182 1183 1184 1185 1186 1187
      // Now calculate the tick labels, and positions within the axis range
      //This gets the tick interval and max, min of labeling from the Extended
      // algorithm
      double scaling = 0.0;
      bool axisVertical = false;

      // When the axis is not initialized
      if(this->Point1[0] == 0 && this->Point2[0] == 0)
1188
        {
Kyle Lutz's avatar
Kyle Lutz committed
1189
        // 500 is an initial guess for the length of the axis in pixels
1190
        scaling = 500 / (this->Maximum - this->Minimum);
1191 1192 1193
        }
      else
        {
1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204
        if (this->Point1[0] == this->Point2[0]) // x1 == x2, therefore vertical
          {
          scaling = (this->Point2[1] - this->Point1[1]) /
                    (this->Maximum - this->Minimum);
          axisVertical = true;
          }
        else
          {
          scaling = (this->Point2[0] - this->Point1[0]) /
                    (this->Maximum - this->Minimum);
          }
1205 1206
        }

1207 1208
      int fontSize = this->LabelProperties->GetFontSize();
      vtkNew<vtkAxisExtended> tickPositionExtended;
1209

1210 1211 1212 1213 1214
      // The following parameters are required for the legibility part in the
      // optimization tickPositionExtended->SetFontSize(fontSize);
      tickPositionExtended->SetDesiredFontSize(fontSize);
      tickPositionExtended->SetPrecision(this->Precision);
      tickPositionExtended->SetIsAxisVertical(axisVertical);
1215

1216 1217 1218 1219 1220 1221 1222
      // Value 4 is hard coded for the user desired tick spacing
      vtkVector3d values =
          tickPositionExtended->GenerateExtendedTickLabels(min, max, 4,
                                                           scaling);
      min = values[0];
      max = values[1];
      this->TickInterval = values[2];
1223

1224 1225 1226
      if(min < this->Minimum)
        {
        this->Minimum = min;
1227
        this->UnscaledMinimum = (this->LogScaleActive ? pow(10., this->Minimum) : this->Minimum);
1228 1229 1230 1231
        }
      if(max > this->Maximum)
        {
        this->Maximum = max;
1232
        this->UnscaledMaximum = (this->LogScaleActive ? pow(10., this->Maximum) : this->Maximum);
1233
        }
1234

1235 1236 1237 1238 1239 1240 1241
      this->Notation = tickPositionExtended->GetLabelFormat();
      this->LabelProperties->SetFontSize(tickPositionExtended->GetFontSize());
      if(tickPositionExtended->GetOrientation() == 1)
        {
        // Set this to 90 to make the labels vertical
        this->LabelProperties->SetOrientation(90);
        }
1242 1243
      }

1244 1245 1246
    double mult = max > min ? 1.0 : -1.0;
    double range = 0.0;
    int n = 0;
1247
    if (this->LogScaleActive)
1248
      {
1249 1250 1251
      range = mult > 0.0 ? pow(10.0, max) - pow(10.0, min)
        : pow(10.0, min) - pow(10.0, max);
      n = vtkContext2D::FloatToInt(range / pow(10.0, this->TickInterval));
1252
      }
1253 1254 1255 1256
    else if (this->NumberOfTicks >= 0)
      {
      n = this->NumberOfTicks - 1;
      }
1257
    else
1258
      {
1259 1260
      range = mult > 0.0 ? max - min : min - max;
      n = vtkContext2D::FloatToInt(range / this->TickInterval);
1261
      }
1262 1263 1264
    for (int i = 0; i <= n && i < 200; ++i)
      {
      double value = 0.0;
1265
      if (this->LogScaleActive)
1266 1267 1268 1269 1270 1271 1272 1273
        {
        value = log10(pow(10.0, min) + double(i) * mult
          * pow(10.0, this->TickInterval));
        }
      else
        {
        value = min + double(i) * mult * this->TickInterval;
        }
1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288
      if (this->TickInterval < 1.0)
        {
        // For small TickInterval, increase the precision of the comparison
        if (fabs(value) < (0.00000001 * this->TickInterval))
          {
          value = 0.0;
          }
        }
      else
        {
        if (fabs(value) < 0.00000001)
          {
          value = 0.0;
          }
        }
1289 1290
      this->TickPositions->InsertNextValue(value);
      // Make a tick mark label for the tick
1291
      if (this->LogScaleActive)
1292 1293 1294 1295
        {
        value = pow(double(10.0), double(value));
        }
      // Now create a label for the tick position
1296 1297
      if (this->TickLabelAlgorithm == vtkAxis::TICK_SIMPLE)
        {
1298
        this->TickLabels->InsertNextValue(this->GenerateSimpleLabel(value));
1299 1300 1301
        }
      else
        {
1302
        // The following call inserts a label into this->TickLabels
1303 1304
        this->GenerateLabelFormat(this->Notation, value);
        }