/*========================================================================= 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 #include #include 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<(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<& parameters) { int numTicks = static_cast((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=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 ( iFontDesiredFontSize && 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 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(std::floor(dmax / step) * j - (k-1) * j); int maxStart = static_cast(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 l = this->Legibility(lmin, lmax, lstep, scaling); vtkVector 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; }