Files
ThirdParty-6/ParaView-5.0.1/VTK/Charts/Core/vtkAxisExtended.cxx

544 lines
15 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkCellLocator.h
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 "vtkAxisExtended.h"
#include "vtkMath.h" // for VTK_DBL_EPSILON
#include "vtkStdString.h"
#include "vtkObjectFactory.h"
#include <sstream>
#include <cmath>
#include <algorithm>
vtkStandardNewMacro(vtkAxisExtended);
vtkAxisExtended::vtkAxisExtended()
{
this->FontSize = 0;
this->DesiredFontSize = 10;
this->Precision = 3;
this->LabelFormat = 0;
this->Orientation = 0;
this->LabelLegibilityChanged = true;
this->IsAxisVertical = false;
}
vtkAxisExtended::~vtkAxisExtended()
{
}
// This method return a value to make step sizes corresponding to low q and j values more preferable
double vtkAxisExtended::Simplicity(int qIndex, int qLength, int j, double lmin,
double lmax, double lstep)
{
double eps = VTK_DBL_EPSILON * 100;
int v = 1 ;
++qIndex;
double rem = fmod(lmin,lstep);
if((rem < eps || (lstep - rem ) < eps ) && lmin <= 0 && lmax >= 0)
{
v = 0;
}
else
{
v = 1; // v is 1 is lebelling includes zero
}
return 1.0 - (qIndex - 1.0) / (qLength - 1.0) - j + v;
}
// This method returns the maximum possible value of simplicity value given q
// and j
double vtkAxisExtended::SimplicityMax(int qIndex, int qLength, int j)
{
int v = 1;
++qIndex;
return 1.0 - (qIndex - 1.0) / (qLength - 1.0) - j + v;
}
// This method makes the data range approximately same as the labeling range
// more preferable
double vtkAxisExtended::Coverage(double dmin, double dmax, double lmin,
double lmax)
{
double coverage = 1.0 - 0.5 * (pow(dmax - lmax, 2) + pow(dmin - lmin, 2)
/ pow(0.1 * (dmax - dmin), 2));
return coverage;
}
//This gives the maximum possible value of coverage given the step size
double vtkAxisExtended::CoverageMax(double dmin, double dmax, double span)
{
double range = dmax - dmin;
if (span > range)
{
double half = (span - range)/2;
return 1- 0.5 * (pow(half, 2) + pow(half, 2) / pow(0.1*(range),2));
}
else
{
return 1.0;
}
}
// This method return a value to make the density of the labels close to the
// user given value
double vtkAxisExtended::Density(int k, double m, double dmin, double dmax,
double lmin, double lmax)
{
double r = (k-1)/(lmax-lmin);
double rt = (m-1) / (std::max(lmax,dmax) - std::min(dmin,lmin));
return 2 - std::max(r/rt, rt/r);
}
// Derives the maximum values for density given k (number of ticks) and m
// (user given)
double vtkAxisExtended::DensityMax(int k, double m)
{
if(k >= m)
{
return 2 - (k-1) / (m-1);
}
else
{
return 1;
}
}
// This methods gives a weighing factor for each label depending on the range
// The coding for the different formats
// 1 - Scientific 5 * 10^6
// 2 - Decimal e.g. 5000
// 3 - K e.g. 5K
// 4 - Factored K e.g. 5(K)
// 5 - M e.g. 5M
// 6 - Factored M e.g. 5(M)
// 7 - Factored Decimals e.g. 5 (thousands)
// 8 - Factored Scientific 5 (10^6)
double vtkAxisExtended::FormatLegibilityScore(double n, int format)
{
switch(format)
{
case 1:
return 0.25;
case 2:
if(std::abs(n) > 0.0001 && std::abs(n) < 1000000)
{
return 1.0;
}
else
{
return 0.0;
}
case 3:
if(std::abs(n) > 1000 && std::abs(n) < 1000000)
{
return 0.75;
}
else
{
return 0.0;
}
case 4:
if(std::abs(n) > 1000 && std::abs(n) < 1000000)
{
return 0.4;
}
else
{
return 0.0;
}
case 5:
if(std::abs(n) > 1000000 && std::abs(n) < 1000000000)
{
return 0.75;
}
else
{
return 0.0;
}
case 6:
if(std::abs(n) > 1000000 && std::abs(n) < 1000000000)
{
return 0.4;
}
else
{
return 0.0;
}
case 7:
return 0.5;
case 8:
return 0.3;
default:
return 0.0;
}
}
// This method returns the length of the label given the format
int vtkAxisExtended::FormatStringLength(int format, double n, int precision)
{
std::ostringstream ostr;
ostr.imbue(std::locale::classic());
int numSize(0);
switch(format)
{
case 1:
ostr.precision(precision);
ostr.setf(std::ios::scientific, std::ios::floatfield);
ostr<<n;
numSize = (int) ostr.str().length();
return numSize;
case 2:
ostr << n;
if((std::ceil(n)-std::floor(n)) != 0.0 )
{
ostr.precision(precision);
}
// Gets the length of the string with the current format without the end
// character
numSize = (int) ostr.str().length()-1;
return numSize;
case 3:
ostr.setf(ios::fixed, ios::floatfield);
ostr << n/1000;
if((std::ceil(n/1000.0)-std::floor(n/1000.0)) != 0.0 )
{
ostr.precision(precision);
}
numSize = (int) ostr.str().length()-1;
return numSize+1; // minus three zeros + K
case 4:
ostr.setf(ios::fixed, ios::floatfield);
ostr << n/1000;
if((std::ceil(n/1000.0)-std::floor(n/1000.0)) != 0.0)
{
ostr.precision(precision);
}
numSize = static_cast<int>(ostr.str().length() - 1);
return numSize; // minus three zeros
case 5:
ostr.setf(ios::fixed, ios::floatfield);
ostr << n/1000000;
if((std::ceil(n/1000000.0) - std::floor(n/1000000.0)) != 0.0)
{
ostr.precision(precision);
}
numSize = (int) ostr.str().length()-1;
return numSize; // minus six zeros
case 6:
ostr.setf(ios::fixed, ios::floatfield);
ostr << n/1000000;
if((std::ceil(n/1000000.0)-std::floor(n/1000000.0)) != 0.0 )
{
ostr.precision(precision);
}
numSize = (int) ostr.str().length()-1;
return numSize+1; // minus six zeros + M
case 7:
ostr.setf(ios::fixed, ios::floatfield);
ostr << n/1000;
if((std::ceil(n/1000.0)-std::floor(n/1000.0)) != 0.0 )
{
ostr.precision(precision);
}
numSize = (int) ostr.str().length()-1;
return numSize; // Three 0's get reduced
case 8:
ostr.precision(precision);
ostr.setf(std::ios::scientific, std::ios::floatfield);
ostr<<n/1000;
numSize = (int) ostr.str().length();
return numSize;
default:
return 0;
}
}
// This methods determines the optimum notation, font size and orientation of
// labels from an exhaustive search
double vtkAxisExtended::Legibility(double lmin, double lmax, double lstep,
double scaling,
vtkVector<int, 3>& parameters)
{
int numTicks = static_cast<int>((lmax - lmin) / lstep);
double* tickPositions = new double[numTicks];
int fontSizes[8] = { 8, 9, 10, 12, 14, 18, 20, 24 };
for(int i = 0; i< numTicks; ++i)
{
tickPositions[i] = lmax + i*lstep;
}
// this->LabelLegibilityChanged = true;
int bestFormat = 1;
int bestOrientation = 0;
int bestFontSize = this->DesiredFontSize;
double bestLegScore = 0.0;
for(int iFormat = 1; iFormat < 9; ++iFormat)
{
double formatLegSum = 0.0;
for(int i = 0; i<numTicks; ++i)
{
formatLegSum += FormatLegibilityScore(tickPositions[i], iFormat);
}
// Average of label legibility scores
formatLegSum = formatLegSum / numTicks;
double eps = VTK_DBL_EPSILON * 100;
int v = 1 ;
double rem = fmod(lmin,lstep);
if((rem < eps || (lstep - rem ) < eps ) && lmin <=0 && lmax >=0)
{
v = 0;
}
else
{
v = 1; // v is 1 is lebelling includes zero
}
formatLegSum = 0.9 * formatLegSum + 0.1 * v;
double fontLegSum = 0.0;
// 8 font sizes are checked
for (int fontIndex = 0; fontIndex < 8 ; ++fontIndex)
{
int iFont = fontSizes[fontIndex];
if(iFont == this->DesiredFontSize)
{
fontLegSum = 1.0;
}
// fontSizes[0] is the minimum font size
else if ( iFont<this->DesiredFontSize && iFont >= fontSizes[0])
{
fontLegSum = 0.2 * (iFont - fontSizes[0] + 1)
/ (this->DesiredFontSize - fontSizes[0]);
}
else
{
fontLegSum = -100.0;
}
for(int iOrientation = 0 ; iOrientation <2 ; ++iOrientation)
{
double orientLegSum = (iOrientation == 0) ? 1 : -0.5;
// Here the gap between two consecutive labels is calculated as:
// 2*Actual distance (in pixels) among two ticks - string lengths of
// the two largest labels
double overlapLegSum = 1.0;
double legScore = (formatLegSum + fontLegSum + orientLegSum
+ overlapLegSum) / 4;
if(legScore > bestLegScore )
{
if(numTicks>1)
{
double fontExtent;
if((this->IsAxisVertical && iOrientation) ||
(!this->IsAxisVertical && !iOrientation) )
{
fontExtent =
(FormatStringLength(iFormat,tickPositions[numTicks-1],
this->Precision) +
FormatStringLength(iFormat,tickPositions[numTicks-2],
this->Precision))*iFont;
}
else
{
fontExtent = iFont * 2;
}
double tickDistance = lstep * scaling;
double labelingGap= 2*(tickDistance) - fontExtent;
// 1.1 for line spacing
overlapLegSum = std::min(1.0,2 - 3* iFont *1.1 / labelingGap );
/*if(labelingGap > 3*iFont)
{
overlapLegSum = 1.0;
}
else if(labelingGap < 3*iFont && labelingGap > 0)
{
overlapLegSum = 2 - 3* iFont / labelingGap ;
}
else
{
overlapLegSum = -100;
}*/
}
legScore = (formatLegSum + fontLegSum + orientLegSum +
overlapLegSum)/4;
if ( legScore > bestLegScore)
{
bestFormat = iFormat;
bestOrientation = iOrientation;
bestFontSize = iFont;
bestLegScore = legScore;
}
}
}
}
}
parameters[0] = bestFormat;
parameters[1] = bestFontSize;
parameters[2] = bestOrientation;
delete [] tickPositions;
return bestLegScore;
}
// This method implements the algorithm given in the paper
vtkVector3d vtkAxisExtended::GenerateExtendedTickLabels(double dmin,
double dmax, double m,
double scaling)
{
double Q[] = {1, 5, 2, 2.5, 4, 3};
double w[] = {0.25, 0.2, 0.5, 0.05};
double eps = VTK_DBL_EPSILON * 100;
vtkVector3d ans;
this->LabelLegibilityChanged = false;
if(dmin > dmax)
{
double temp = dmin;
dmin = dmax;
dmax = temp;
}
if( dmax - dmin < eps)
{
ans[0] = dmin; ans[1]= dmax; ans[2]= m;
//return Sequence(dmin,dmax, m);
return ans;
}
int qLength = 6;//Q.Length(); // Hard Coded
//list<double> best;
double bestScore = -2;
double bestLmin(0), bestLmax(0), bestLstep(0);
const int INF = 100; //INT_MAX; Again 100 is hard coded
int j = 1;
while(j < INF)
{
for(int qIndex = 0; qIndex < qLength; ++qIndex)
{
double sm = SimplicityMax(qIndex, qLength, j);
if((w[0]*sm + w[1] + w[2] + w[3]) < bestScore)
{
j = INF;
break;
}
int k = 2;
while(k < INF)
{
double dm = DensityMax(k,m);
if((w[0]*sm + w[1] + w[2]*dm + w[3]) < bestScore)
{
break;
}
double delta = (dmax- dmin)/((k+1)*j*Q[qIndex]) ;
double z = ceil(log10(delta));
while(z < INF)
{
double step = j*Q[qIndex]*pow(10.0,z);
//double cm = CoverageMax(dmin, dmax, step*(k-1));
if((w[0]*sm + w[1] + w[2]*dm + w[3]) < bestScore)
{
break;
}
int minStart = static_cast<int>(std::floor(dmax / step) * j - (k-1) * j);
int maxStart = static_cast<int>(std::ceil(dmin/step) * j);
if(minStart > maxStart)
{
++z;
continue;
}
for(int start = minStart; start <= maxStart; ++start)
{
double lmin = start * (step/j);
double lmax = lmin + step*(k-1);
double lstep = step;
double s = Simplicity(qIndex, qLength, j, lmin, lmax, lstep);
double c = Coverage(dmin, dmax, lmin, lmax);
double g = Density(k,m,dmin, dmax, lmin, lmax);
double score = w[0]*s + w[1]*c + w[2]*g + w[3];
if(score < bestScore)
continue;
//vtkVector<double,4> l = this->Legibility(lmin, lmax, lstep, scaling);
vtkVector<int, 3> legibilityIndex;
double newScore = this->Legibility(lmin, lmax, lstep, scaling,
legibilityIndex);
score = w[0] * s + w[1] * c + w[2] * g + w[3] * newScore;
if(score > bestScore)
{
bestScore = score;
bestLmin = lmin;
bestLmax = lmax;
bestLstep = lstep;
this->LabelFormat = legibilityIndex[0]; // label format
this->FontSize = legibilityIndex[1]; // label font size
this->Orientation = legibilityIndex[2]; // label orientation
}
}
++z;
}
++k;
}
}
++j;
}
ans[0] = bestLmin;
ans[1] = bestLmax;
ans[2] = bestLstep;
//vtkVector3d answers(bestLmin, bestLmax, bestLstep);
// return Sequence(bestLmin, bestLmax, bestLstep);
return ans;
}
void vtkAxisExtended::PrintSelf(ostream &os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Orientation: " << this->Orientation << endl;
os << indent << "FontSize: " << this->FontSize << endl;
os << indent << "DesiredFontSize: " << this->DesiredFontSize << endl;
os << indent << "Precision: " << this->Precision << endl;
os << indent << "LabelFormat: " << this->LabelFormat << endl;
}