Commit f1ad92ce authored by David C. Lonie's avatar David C. Lonie
Browse files

Add support for printf-style label formatting to vtkAxis.

parent 4b3c928a
6ec7f93769712a584917d238847cd3b4
0c6a7c321132b435347b4951531f56ee
......@@ -3,6 +3,7 @@ vtk_module(vtkChartsCore
StandAlone
DEPENDS
vtkRenderingContext2D
vtksys
PRIVATE_DEPENDS
vtkCommonColor
vtkInfovisCore # Needed for plot parallel coordinates vtkStringToCategory
......
......@@ -32,10 +32,20 @@
#include "sstream"
#include "vtkObjectFactory.h"
#include "vtksys/RegularExpression.hxx"
#include <algorithm>
#include <cstdio>
#include <limits>
#include <cmath>
// pull in snprintf on MSVC:
#if defined(_WIN32) && !defined(__CYGWIN__)
# define SNPRINTF _snprintf
#else
# define SNPRINTF snprintf
#endif
//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkAxis);
......@@ -79,6 +89,7 @@ vtkAxis::vtkAxis()
this->TicksVisible = true;
this->AxisVisible = true;
this->Precision = 2;
this->LabelFormat = "%g";
this->Notation = vtkAxis::STANDARD_NOTATION;
this->Behavior = vtkAxis::AUTO;
this->Pen = vtkPen::New();
......@@ -659,6 +670,19 @@ void vtkAxis::SetPrecision(int precision)
this->Modified();
}
//-----------------------------------------------------------------------------
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;
}
}
//-----------------------------------------------------------------------------
void vtkAxis::SetLogScale(bool logScale)
{
......@@ -1133,24 +1157,7 @@ void vtkAxis::GenerateTickLabels(double min, double max)
// Now create a label for the tick position
if (this->TickLabelAlgorithm == vtkAxis::TICK_SIMPLE)
{
std::ostringstream ostr;
ostr.imbue(std::locale::classic());
if (this->Notation > 0)
{
ostr.precision(this->Precision);
}
if (this->Notation == 1)
{
// Scientific notation
ostr.setf(std::ios::scientific, std::ios::floatfield);
}
else if (this->Notation == 2)
{
ostr.setf(ios::fixed, ios::floatfield);
}
ostr << value;
this->TickLabels->InsertNextValue(ostr.str());
this->TickLabels->InsertNextValue(this->GenerateSimpleLabel(value));
}
else
{
......@@ -1162,6 +1169,7 @@ void vtkAxis::GenerateTickLabels(double min, double max)
this->TickMarksDirty = false;
}
//-----------------------------------------------------------------------------
void vtkAxis::GenerateTickLabels()
{
this->TickLabels->SetNumberOfTuples(0);
......@@ -1173,25 +1181,76 @@ void vtkAxis::GenerateTickLabels()
{
value = pow(double(10.0), double(value));
}
// Now create a label for the tick position
this->TickLabels->InsertNextValue(this->GenerateSimpleLabel(value));
}
}
//-----------------------------------------------------------------------------
vtkStdString vtkAxis::GenerateSimpleLabel(double val)
{
vtkStdString result;
if (this->Notation == PRINTF_NOTATION)
{ // Use the C-style printf specification:
const int buffSize = 1024;
char buffer[buffSize];
// On Windows, formats with exponents have three digits by default
// whereas on other systems, exponents have two digits. Set to two
// digits on Windows for consistent behavior.
#ifdef _WIN32
unsigned int oldWin32ExponentFormat = _set_output_format(_TWO_DIGIT_EXPONENT);
#endif
int len = SNPRINTF(buffer, buffSize, this->LabelFormat.c_str(), val);
if (len < 0) // Overrun on windows
{
len = buffSize;
}
#ifdef _WIN32
_set_output_format(oldWin32ExponentFormat);
#endif
result = vtkStdString(buffer, std::min(len, buffSize));
}
else
{ // Use the C++ style stream format specification:
std::ostringstream ostr;
ostr.imbue(std::locale::classic());
if (this->Notation > 0)
if (this->Notation != STANDARD_NOTATION)
{
ostr.precision(this->Precision);
if (this->Notation == SCIENTIFIC_NOTATION)
{
ostr.setf(std::ios::scientific, std::ios::floatfield);
}
else if (this->Notation == FIXED_NOTATION)
{
ostr.setf(ios::fixed, ios::floatfield);
}
}
if (this->Notation == SCIENTIFIC_NOTATION)
ostr << val;
result = vtkStdString(ostr.str());
}
// Strip out leading zeros on the exponent:
vtksys::RegularExpression regExp("[Ee][+-]");
if (regExp.find(result))
{
vtkStdString::iterator it = result.begin() + regExp.start() + 2;
while (it != result.end() && *it == '0')
{
ostr.setf(std::ios::scientific, std::ios::floatfield);
it = result.erase(it);
}
else if (this->Notation == FIXED_NOTATION)
// If the exponent is 0, remove the e+ bit, too.
if (it == result.end())
{
ostr.setf(ios::fixed, ios::floatfield);
result.erase(regExp.start());
}
ostr << value;
this->TickLabels->InsertNextValue(ostr.str());
}
return result;
}
//-----------------------------------------------------------------------------
......@@ -1589,24 +1648,7 @@ void vtkAxis::GenerateLogScaleTickMarks(int order,
if (niceTickMark)
{
// Now create a label for the tick position
std::ostringstream ostr;
ostr.imbue(std::locale::classic());
if (this->Notation > 0)
{
ostr.precision(this->Precision);
}
if (this->Notation == SCIENTIFIC_NOTATION)
{
ostr.setf(std::ios::scientific, std::ios::floatfield);
}
else if (this->Notation == FIXED_NOTATION)
{
ostr.setf(ios::fixed, ios::floatfield);
}
ostr << value;
this->TickLabels->InsertNextValue(ostr.str());
this->TickLabels->InsertNextValue(this->GenerateSimpleLabel(value));
}
else
{
......
......@@ -283,7 +283,8 @@ public:
vtkGetMacro(AxisVisible, bool);
// Description:
// Get/set the numerical precision to use, default is 2.
// Get/set the numerical precision to use, default is 2. This is ignored
// when Notation is STANDARD_NOTATION or PRINTF_NOTATION.
virtual void SetPrecision(int precision);
vtkGetMacro(Precision, int);
......@@ -293,10 +294,19 @@ public:
STANDARD_NOTATION = 0,
SCIENTIFIC_NOTATION,
FIXED_NOTATION,
PRINTF_NOTATION
};
// Description:
// Get/set the numerical notation, standard, scientific or mixed (0, 1, 2).
// Get/Set the printf-style format string used when TickLabelAlgorithm is
// TICK_SIMPLE and Notation is PRINTF_NOTATION. The default is "%g".
virtual void SetLabelFormat(const std::string &fmt);
vtkGetMacro(LabelFormat, std::string)
// Description:
// Get/set the numerical notation, standard, scientific, fixed, or a
// printf-style format string.
// \sa SetPrecision SetLabelFormat
virtual void SetNotation(int notation);
vtkGetMacro(Notation, int);
......@@ -401,6 +411,11 @@ public:
static double NiceMinMax(double &min, double &max, float pixelRange,
float tickPixelSpacing);
// Description:
// Generate a single label using the current settings when TickLabelAlgorithm
// is TICK_SIMPLE.
vtkStdString GenerateSimpleLabel(double val);
//BTX
protected:
vtkAxis();
......@@ -497,6 +512,7 @@ protected:
bool AxisVisible; // Should the axis line be visible.
int Precision; // Numerical precision to use, defaults to 2.
int Notation; // The notation to use (standard, scientific, mixed)
std::string LabelFormat; // The printf-style format string used for labels.
int Behavior; // The behaviour of the axis (auto, fixed, custom).
float MaxLabel[2]; // The widest/tallest axis label.
bool TitleAppended; // Track if the title is updated when the label formats
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment