361 lines
8.1 KiB
C++
361 lines
8.1 KiB
C++
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <math.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "iupPlot.h"
|
||
|
|
||
|
|
||
|
const double kTickValueVeryBig = 1.0e4; // switch to scientific format
|
||
|
const double kTickValueVerySmall = 1.0e-3; // switch to scientific format
|
||
|
const double kLittleIncrease = 1.0001;
|
||
|
const double kLittleDecrease = 0.9999;
|
||
|
const double kEps = 1e-4;
|
||
|
|
||
|
|
||
|
/***********************************************************************************/
|
||
|
|
||
|
|
||
|
static double iPlotRoundSpan(double inSpan)
|
||
|
{
|
||
|
// round it to something producing readable tick labels
|
||
|
// write it in the form inSpan = a*SafeExp10 (b)
|
||
|
|
||
|
if (inSpan<=0)
|
||
|
return (double)-1.234567; // error
|
||
|
|
||
|
int thePow = 0;
|
||
|
double theSpan = inSpan;
|
||
|
if (inSpan>1)
|
||
|
{
|
||
|
while (theSpan>10)
|
||
|
{
|
||
|
theSpan/=10;
|
||
|
if (theSpan == inSpan) // not a number
|
||
|
return (double)-1.234567;
|
||
|
thePow++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
while (theSpan<1)
|
||
|
{
|
||
|
theSpan*=10;
|
||
|
thePow--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int theRoundedFirstDigit = iupPlotRound (theSpan);
|
||
|
int thePreferredFirstDigit = 1;
|
||
|
switch (theRoundedFirstDigit)
|
||
|
{
|
||
|
case 1:
|
||
|
thePreferredFirstDigit = 1;
|
||
|
break;
|
||
|
case 2:
|
||
|
case 3:
|
||
|
case 4:
|
||
|
thePreferredFirstDigit = 2;
|
||
|
break;
|
||
|
case 5:
|
||
|
case 6:
|
||
|
case 7:
|
||
|
case 8:
|
||
|
case 9:
|
||
|
thePreferredFirstDigit = 5;
|
||
|
break;
|
||
|
case 10:
|
||
|
thePreferredFirstDigit = 1;
|
||
|
thePow++;
|
||
|
break;
|
||
|
default:
|
||
|
// error
|
||
|
return (double)-1.234567;
|
||
|
break;
|
||
|
}
|
||
|
double theRes = thePreferredFirstDigit*pow (10., thePow);
|
||
|
return theRes;
|
||
|
}
|
||
|
|
||
|
int iupPlotCalcPrecision(double inValue)
|
||
|
{
|
||
|
if (inValue < 0)
|
||
|
inValue = -inValue;
|
||
|
|
||
|
if (inValue > kTickValueVeryBig || inValue < kTickValueVerySmall)
|
||
|
return 1;
|
||
|
else
|
||
|
{
|
||
|
int thePrecision = 0;
|
||
|
if (inValue < 1)
|
||
|
{
|
||
|
double theSpan = inValue;
|
||
|
while (theSpan < 1 && thePrecision < 9)
|
||
|
{
|
||
|
thePrecision++;
|
||
|
theSpan *= 10;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return thePrecision;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void iPlotMakeAutoFormatString(double inValue, char* outFormatString, bool sign_space)
|
||
|
{
|
||
|
if (inValue < 0)
|
||
|
inValue = -inValue;
|
||
|
|
||
|
if (inValue > kTickValueVeryBig || inValue < kTickValueVerySmall)
|
||
|
{
|
||
|
if (sign_space)
|
||
|
strcpy(outFormatString, "% .1e");
|
||
|
else
|
||
|
strcpy(outFormatString, "%.1e");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int thePrecision = 0;
|
||
|
if (inValue < 1)
|
||
|
{
|
||
|
double theSpan = inValue;
|
||
|
while (theSpan < 1 && thePrecision < 9)
|
||
|
{
|
||
|
thePrecision++;
|
||
|
theSpan *= 10;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sign_space)
|
||
|
{
|
||
|
char theBuf[128] = IUP_PLOT_DEF_NUMBERFORMATSIGNED;
|
||
|
theBuf[3] = (char)('0' + thePrecision); // "% ."
|
||
|
strcpy(outFormatString, theBuf);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
char theBuf[128] = IUP_PLOT_DEF_NUMBERFORMAT;
|
||
|
theBuf[2] = (char)('0' + thePrecision); // "%."
|
||
|
strcpy(outFormatString, theBuf);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************/
|
||
|
|
||
|
|
||
|
bool iupPlotTickIterLinear::Init()
|
||
|
{
|
||
|
if (!mAxis)
|
||
|
return false;
|
||
|
|
||
|
double theMin = mAxis->mMin;
|
||
|
double theMajorTickSpan = mAxis->mTick.mMajorSpan;
|
||
|
int theDiv = mAxis->mTick.mMinorDivision;
|
||
|
mDelta = theMajorTickSpan/theDiv;
|
||
|
mCount = (long)ceil(theMin/mDelta);
|
||
|
mCurrentTick = mCount*mDelta;
|
||
|
|
||
|
return true;
|
||
|
|
||
|
}
|
||
|
|
||
|
bool iupPlotTickIterLinear::GetNextTick (double &outTick, bool &outIsMajorTick, char* outFormatString)
|
||
|
{
|
||
|
(void)outFormatString; // unused here
|
||
|
|
||
|
if (!mAxis)
|
||
|
return false;
|
||
|
|
||
|
if (mCurrentTick>mAxis->mMax*kLittleIncrease)
|
||
|
return false;
|
||
|
|
||
|
outTick = mCurrentTick;
|
||
|
outIsMajorTick = (mCount%mAxis->mTick.mMinorDivision == 0);
|
||
|
|
||
|
mCurrentTick += mDelta;
|
||
|
mCount++;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool iupPlotTickIterLinear::CalculateSpacing (double inParRange, double inDivGuess, iupPlotTick &ioTick) const
|
||
|
{
|
||
|
if (inDivGuess <= kFloatSmall)
|
||
|
return false;
|
||
|
|
||
|
double thePreferredSpan = iPlotRoundSpan(inParRange / inDivGuess);
|
||
|
if (thePreferredSpan < 0)
|
||
|
return false;
|
||
|
|
||
|
double thePreferredNrOfTicks = inParRange/thePreferredSpan;
|
||
|
if (thePreferredNrOfTicks <1)
|
||
|
ioTick.mMajorSpan = inParRange;
|
||
|
else
|
||
|
ioTick.mMajorSpan = thePreferredSpan;
|
||
|
|
||
|
ioTick.mMinorDivision = 5;
|
||
|
|
||
|
// Calculated only once for Linear scale
|
||
|
if (ioTick.mFormatAuto)
|
||
|
iPlotMakeAutoFormatString(ioTick.mMajorSpan, ioTick.mFormatString, mAxis->mVertical && mAxis->mReverseTicksLabel);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************/
|
||
|
|
||
|
|
||
|
bool iupPlotTickIterLog::Init ()
|
||
|
{
|
||
|
if (!mAxis)
|
||
|
return false;
|
||
|
|
||
|
double theMin = mAxis->mMin;
|
||
|
double theMajorTickSpan = mAxis->mTick.mMajorSpan;
|
||
|
int theDiv = mAxis->mTick.mMinorDivision;
|
||
|
mDelta = theMajorTickSpan/theDiv;
|
||
|
double theBase = mAxis->mLogBase;
|
||
|
long thePowMin = (long)floor(iupPlotLog(theMin, theBase));
|
||
|
mCurrentTick = iupPlotExp (thePowMin, theBase);
|
||
|
mCount = 0;
|
||
|
|
||
|
// walk to the first tick
|
||
|
|
||
|
if (theMin<=0)
|
||
|
return false;
|
||
|
else
|
||
|
{
|
||
|
// walk forward
|
||
|
double theNext = mCurrentTick+mDelta*iupPlotExp (thePowMin, theBase);
|
||
|
while (theNext<=theMin*kLittleDecrease)
|
||
|
{
|
||
|
mCurrentTick = theNext;
|
||
|
theNext += mDelta*iupPlotExp(thePowMin,theBase);
|
||
|
mCount++;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool iupPlotTickIterLog::CalculateSpacing (double, double inDivGuess, iupPlotTick &ioTick) const
|
||
|
{
|
||
|
if (inDivGuess<=kFloatSmall)
|
||
|
return false;
|
||
|
|
||
|
double theBase = mAxis->mLogBase;
|
||
|
ioTick.mMajorSpan = theBase-1;// relative
|
||
|
|
||
|
ioTick.mMinorDivision = iupPlotRound(ioTick.mMajorSpan);
|
||
|
|
||
|
strcpy(ioTick.mFormatString, "%.1e");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool iupPlotTickIterLog::GetNextTick (double &outTick, bool &outIsMajorTick, char* outFormatString)
|
||
|
{
|
||
|
if (!mAxis)
|
||
|
return false;
|
||
|
|
||
|
if (mCurrentTick>mAxis->mMax*kLittleIncrease)
|
||
|
return false;
|
||
|
|
||
|
outTick = mCurrentTick;
|
||
|
outIsMajorTick = (mCount%mAxis->mTick.mMinorDivision == 0);
|
||
|
|
||
|
// Calculated in every iteration for Log scale
|
||
|
if (outFormatString && mAxis->mTick.mFormatAuto)
|
||
|
iPlotMakeAutoFormatString(outTick, outFormatString, mAxis->mVertical && mAxis->mReverseTicksLabel);
|
||
|
|
||
|
double theBase = mAxis->mLogBase;
|
||
|
double theLogNow = iupPlotLog(mCurrentTick, theBase);
|
||
|
int thePowNow = (int)floor(theLogNow);
|
||
|
outIsMajorTick = false;
|
||
|
if (fabs (theLogNow-thePowNow)<kEps)
|
||
|
outIsMajorTick = true;
|
||
|
|
||
|
mCurrentTick += mDelta*iupPlotExp(thePowNow, theBase);
|
||
|
mCount++;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool iupPlotTickIterLog::AdjustRange (double &ioMin, double &ioMax) const
|
||
|
{
|
||
|
double theBase = mAxis->mLogBase;
|
||
|
if (mAxis->mMaxDecades > 0)
|
||
|
ioMin = ioMax/iupPlotExp (mAxis->mMaxDecades, theBase);
|
||
|
|
||
|
if (ioMin == 0 && ioMax == 0)
|
||
|
{
|
||
|
ioMin = kLogMinClipValue;
|
||
|
ioMax = 1.0;
|
||
|
}
|
||
|
if (ioMin <= 0 || ioMax<=0)
|
||
|
return false;
|
||
|
|
||
|
ioMin = RoundDown(ioMin*kLittleIncrease);
|
||
|
ioMax = RoundUp(ioMax*kLittleDecrease);
|
||
|
|
||
|
if (ioMin<kLogMinClipValue)
|
||
|
ioMin = kLogMinClipValue;
|
||
|
|
||
|
if (mAxis->mMaxDecades > 0)
|
||
|
ioMin = ioMax/iupPlotExp (mAxis->mMaxDecades, theBase);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
double iupPlotTickIterLog::RoundUp (double inFloat) const
|
||
|
{
|
||
|
double theBase = mAxis->mLogBase;
|
||
|
int thePow = (int)ceil(iupPlotLog(inFloat, theBase));
|
||
|
return pow (theBase, thePow);
|
||
|
}
|
||
|
|
||
|
double iupPlotTickIterLog::RoundDown (double inFloat) const
|
||
|
{
|
||
|
double theBase = mAxis->mLogBase;
|
||
|
int thePow = (int)floor(iupPlotLog(inFloat,theBase));
|
||
|
return pow (theBase, thePow);
|
||
|
}
|
||
|
|
||
|
|
||
|
/***********************************************************************************/
|
||
|
|
||
|
|
||
|
bool iupPlotTickIterNamed::GetNextTick (double &outTick, bool &outIsMajorTick, char* outFormatString)
|
||
|
{
|
||
|
if (iupPlotTickIterLinear::GetNextTick (outTick, outIsMajorTick, outFormatString))
|
||
|
{
|
||
|
int theSampleIndex = iupPlotRound(outTick);
|
||
|
|
||
|
// TODO: improve this
|
||
|
if (fabs(outTick - (double)theSampleIndex) > 0.1)
|
||
|
{
|
||
|
if (outFormatString) strcpy(outFormatString, "");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (theSampleIndex >= 0 && theSampleIndex < mStringData->GetCount())
|
||
|
{
|
||
|
if (outFormatString) strcpy(outFormatString, mStringData->GetSampleString(theSampleIndex));
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool iupPlotTickIterNamed::CalculateSpacing (double inParRange, double inDivGuess, iupPlotTick &outTick) const
|
||
|
{
|
||
|
if (iupPlotTickIterLinear::CalculateSpacing (inParRange, inDivGuess, outTick))
|
||
|
{
|
||
|
outTick.mMinorDivision = 1;
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|