You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
5.8 KiB

#include "qwt3d_helper.h"
#include "qwt3d_autoscaler.h"
using namespace Qwt3D;
namespace
{
double floorExt( int& exponent, double x, std::vector<double>& sortedmantissi)
{
if (x == 0.0)
{
exponent = 0;
return 0.0;
}
double sign = (x > 0) ? 1.0 : -1.0;
double lx = log10(fabs(x));
exponent = (int)floor(lx);
double fr = pow(10.0, lx - exponent);
if (fr >= 10.0)
{
fr = 1.0;
++exponent;
}
else
{
for (int i=(int)sortedmantissi.size()-1; i>=0;--i)
{
if (fr>=sortedmantissi[i])
{
fr = sortedmantissi[i];
break;
}
}
}
return sign * fr;
}
/*
\brief Find the largest value out of {1,2,5}*10^n with an integer number n
which is smaller than or equal to x
\param exponent n
\param x Input value
\return Mantissa
*/
double floor125( int& exponent, double x)
{
std::vector<double> m(2);
m[0] = 1;
m[1] = 2;
m[2] = 5;
return floorExt(exponent, x, m);
}
} // anon ns
//! Initializes with an {1,2,5} sequence of mantissas
LinearAutoScaler::LinearAutoScaler()
{
init(0,1,1);
mantissi_ = std::vector<double>(3);
mantissi_[0] = 1;
mantissi_[1] = 2;
mantissi_[2] = 5;
}
//! Initialize with interval [0,1] and one requested interval
/*!
val mantisse A increasing ordered vector of values representing
mantisse values between 1 and 9.
*/
LinearAutoScaler::LinearAutoScaler(std::vector<double>& mantisse)
{
init(0,1,1);
if (mantisse.empty())
{
mantissi_ = std::vector<double>(3);
mantissi_[0] = 1;
mantissi_[1] = 2;
mantissi_[2] = 5;
return;
}
mantissi_ = mantisse;
}
//! Initialize with interval [start,stop] and number of requested intervals
/**
Switchs start and stop, if stop < start and sets intervals = 1 if ivals < 1
*/
void LinearAutoScaler::init(double start, double stop, int ivals)
{
start_ = start;
stop_ = stop;
intervals_ = ivals;
if (start_ > stop_)
{
double tmp = start_;
start_ = stop_;
stop_ = tmp;
}
if (intervals_ < 1)
intervals_ = 1;
}
/*!
\return Anchor value
\verbatim
|_______|____________ _ _ _ _ _____|_____________|________________
0 m*10^n start anchor := c*m*10^n
c 'minimal' (anchor-start < m*10^n)
\endverbatim
*/
double LinearAutoScaler::anchorvalue(double start, double m, int n)
{
double stepval = m * pow(10.0, n);
return stepval * ceil(start / stepval);
}
/*!
\return New number of intervals (:= l_intervals + r_intervals)
\param l_intervals Number of intervals left from anchor
\param r_intervals Number of intervals right from anchor
\verbatim
-l_intervals * i -2 * i -i +r_intervals * i
|
|______|_______ _ _ _ ____|____|___ _ _ _ _ _ _ _|_______|_______|_ _ _ _ _ _ _____|__|_____
| | | |
0 i := m*10^n start anchor stop
c 'minimal' (anchor-start < m*10^n)
\endverbatim
*/
int LinearAutoScaler::segments(int& l_intervals, int& r_intervals, double start, double stop, double anchor, double m, int n)
{
double val = m * pow(10.0, n);
double delta = (stop - anchor) / val;
r_intervals = (int)floor(delta); // right side intervals
delta = (anchor - start) / val;
l_intervals = (int)floor(delta); // left side intervals
return r_intervals + l_intervals;
}
/*!
\brief Does the actual scaling
\return Number of intervals after rescaling. This will in the most cases differ
from the requested interval number! Always >0.
\param a Start value after scaling (always >= start)
\param b Stop value after scaling (always <= stop)
\param start Start value
\param stop Stop value
\param ivals Requested intervals
\return Number of intervals after autoscaling
If the given interval has zero length the function returns the current
interval number and a and b remain unchanged.
*/
int LinearAutoScaler::execute(double& a, double& b, double start, double stop, int ivals)
{
init(start,stop,ivals);
double delta = stop_ - start_;
if (isPracticallyZero(delta))
return intervals_;
double c;
int n;
c = floorExt(n, delta, mantissi_);
int l_ival, r_ival;
double anchor = anchorvalue(start_, c, n);
int ival = segments(l_ival, r_ival, start_, stop_, anchor, c, n);
if (ival >= intervals_)
{
a = anchor - l_ival * c * pow(10.0,n);
b = anchor + r_ival * c * pow(10.0,n);
intervals_ = ival;
return intervals_;
}
int prev_ival, prev_l_ival, prev_r_ival;
double prev_anchor;
double prev_c;
int prev_n;
while(1)
{
prev_c = c;
prev_n = n;
prev_anchor = anchor;
prev_ival = ival;
prev_l_ival = l_ival;
prev_r_ival = r_ival;
if (int(c) == 1)
{
c = mantissi_.back();
--n;
}
else
{
for (unsigned int i=mantissi_.size()-1; i>0; --i)
{
if (int(c) == mantissi_[i])
{
c = mantissi_[i-1];
break;
}
}
}
anchor = anchorvalue(start_, c, n);
ival = segments(l_ival, r_ival, start_, stop_, anchor, c, n);
int prev_diff = intervals_ - prev_ival;
int actual_diff = ival - intervals_;
if (prev_diff >= 0 && actual_diff >= 0)
{
if (prev_diff < actual_diff)
{
c = prev_c;
n = prev_n;
anchor = prev_anchor;
ival = prev_ival;
l_ival = prev_l_ival;
r_ival = prev_r_ival;
}
a = anchor - l_ival * c * pow(10.0,n);
b = anchor + r_ival * c * pow(10.0,n);
intervals_ = ival;
break;
}
}
return intervals_;
}