219 lines
7.7 KiB
C++
219 lines
7.7 KiB
C++
#include <cmath>
|
|
#include "AbsAxis_p.h"
|
|
|
|
namespace YSG {
|
|
Q_Ptr_cpp(AbsAxis)
|
|
PROP_P(AbsAxis, int, x)
|
|
PROP_P(AbsAxis, int, y)
|
|
PROP_P(AbsAxis, Qt::Orientation, orientation)
|
|
PROP_P(AbsAxis, size_t, pixelSize)
|
|
PROP_P(AbsAxis, int, tickLength)
|
|
PROP_P(AbsAxis, int, subTickLength)
|
|
PROP_P(AbsAxis, QColor, color)
|
|
PROP_P(AbsAxis, QChar, formatChar)
|
|
PROP_P(AbsAxis, QLocale, locale)
|
|
PROP_P(AbsAxis, QString, unitText)
|
|
PROP_P(AbsAxis, QFont, unitTextFont)
|
|
PROP_P(AbsAxis, QPen, unitTextPen)
|
|
PROP_P(AbsAxis, QBrush, unitTextBackgroundBrush)
|
|
PROP_P(AbsAxis, int, rotate) // 不旋转
|
|
|
|
double AbsAxis::pixelToCoord(double pixel, SRC src) {
|
|
return d()->pixelToCoord(pixel, src);
|
|
}
|
|
|
|
double AbsAxis::coordToPixel(double coord, SRC src) {
|
|
return d()->coordToPixel(coord, src);
|
|
}
|
|
|
|
QVector<double> AbsAxisPrivate::createTickVector(double tickStep, const Range &range) {
|
|
double mTickOrigin = renderState()->coordStart;
|
|
QVector<double> result;
|
|
// Generate tick positions according to tickStep:
|
|
// do not use qFloor here, or we'll lose 64 bit precision
|
|
|
|
// qDebug() << "tickStep:" << tickStep;
|
|
//
|
|
|
|
auto firstStep = qint64(floor(qAbs((range.lower-mTickOrigin)/tickStep)));
|
|
auto lastStep = qint64(ceil((qAbs(range.upper-mTickOrigin)/tickStep)));
|
|
|
|
|
|
|
|
|
|
int tickcount = int(qAbs(lastStep-firstStep)+1);
|
|
if (tickcount < 0) tickcount = 0;
|
|
|
|
if(range.lower > range.upper) tickStep = -tickStep;
|
|
|
|
result.resize(tickcount);
|
|
for (int i=0; i<tickcount; ++i) {
|
|
result[i] = mTickOrigin + (double)(firstStep+i)*tickStep;
|
|
}
|
|
// while (!result.empty() && !range.contain(result.last())) {
|
|
// result.removeLast();
|
|
// }
|
|
|
|
return result;
|
|
}
|
|
|
|
QVector<QString> AbsAxisPrivate::createLabelVector(const QVector<double> &ticks, SRC src) {
|
|
QVector<QString> result;
|
|
result.reserve(ticks.size());
|
|
foreach (double tickCoord, ticks)
|
|
result.append(getTickLabel(tickCoord, src));
|
|
return result;
|
|
}
|
|
|
|
QVector<double> AbsAxisPrivate::createSubTickVector(int subTickCount, const QVector<double> &ticks, const Range& range) {
|
|
QVector<double> result;
|
|
if (subTickCount <= 0 || ticks.size() < 2)
|
|
return result;
|
|
|
|
result.reserve((ticks.size()-1)*subTickCount);
|
|
for (int i=1; i<ticks.size(); ++i)
|
|
{
|
|
double subTickStep = (ticks.at(i)-ticks.at(i-1))/double(subTickCount+1);
|
|
for (int k=1; k<=subTickCount; ++k)
|
|
result.append(ticks.at(i-1) + k*subTickStep);
|
|
}
|
|
while (!result.empty() && !range.contain(result.last())) {
|
|
result.removeLast();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
int AbsAxisPrivate::getSubTickCount(double tickStep) {
|
|
int result = 1; // default to 1, if no proper value can be found
|
|
// separate integer and fractional part of mantissa:
|
|
double epsilon = 0.01;
|
|
double intPartf;
|
|
int intPart;
|
|
double fracPart = modf(getMantissa(tickStep), &intPartf);
|
|
intPart = int(intPartf);
|
|
|
|
// handle cases with (almost) integer mantissa:
|
|
if (fracPart < epsilon || 1.0-fracPart < epsilon)
|
|
{
|
|
if (1.0-fracPart < epsilon)
|
|
++intPart;
|
|
switch (intPart)
|
|
{
|
|
case 1: result = 4; break; // 1.0 -> 0.2 substep
|
|
case 2: result = 3; break; // 2.0 -> 0.5 substep
|
|
case 3: result = 2; break; // 3.0 -> 1.0 substep
|
|
case 4: result = 3; break; // 4.0 -> 1.0 substep
|
|
case 5: result = 4; break; // 5.0 -> 1.0 substep
|
|
case 6: result = 2; break; // 6.0 -> 2.0 substep
|
|
case 7: result = 6; break; // 7.0 -> 1.0 substep
|
|
case 8: result = 3; break; // 8.0 -> 2.0 substep
|
|
case 9: result = 2; break; // 9.0 -> 3.0 substep
|
|
}
|
|
} else
|
|
{
|
|
// handle cases with significantly fractional mantissa:
|
|
if (qAbs(fracPart-0.5) < epsilon) // *.5 mantissa
|
|
{
|
|
switch (intPart)
|
|
{
|
|
case 1: result = 2; break; // 1.5 -> 0.5 substep
|
|
case 2: result = 4; break; // 2.5 -> 0.5 substep
|
|
case 3: result = 4; break; // 3.5 -> 0.7 substep
|
|
case 4: result = 2; break; // 4.5 -> 1.5 substep
|
|
case 5: result = 4; break; // 5.5 -> 1.1 substep (won't occur with default getTickStep from here on)
|
|
case 6: result = 4; break; // 6.5 -> 1.3 substep
|
|
case 7: result = 2; break; // 7.5 -> 2.5 substep
|
|
case 8: result = 4; break; // 8.5 -> 1.7 substep
|
|
case 9: result = 4; break; // 9.5 -> 1.9 substep
|
|
}
|
|
}
|
|
// if mantissa fraction isn't 0.0 or 0.5, don't bother finding good sub tick marks, leave default
|
|
}
|
|
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
double AbsAxisPrivate::getMantissa(double input, double *magnitude) {
|
|
const double mag = std::pow(10.0, std::floor(std::log10(input)));
|
|
if (magnitude) *magnitude = mag;
|
|
return input / mag;
|
|
}
|
|
|
|
|
|
|
|
double AbsAxisPrivate::cleanMantissa(double input) {
|
|
double magnitude;
|
|
const double mantissa = getMantissa(input, &magnitude);
|
|
return pickClosest(mantissa, QVector<double>() << 1.0 << 2.0 << 2.5 << 5.0 << 10.0)*magnitude;
|
|
}
|
|
|
|
|
|
double AbsAxisPrivate::pickClosest(double target, const QVector<double> &candidates) {
|
|
if (candidates.size() == 1)
|
|
return candidates.first();
|
|
QVector<double>::const_iterator it = std::lower_bound(candidates.constBegin(), candidates.constEnd(), target);
|
|
if (it == candidates.constEnd())
|
|
return *(it - 1);
|
|
else if (it == candidates.constBegin())
|
|
return *it;
|
|
else
|
|
return target - *(it - 1) < *it - target ? *(it - 1) : *it;
|
|
}
|
|
|
|
|
|
|
|
QString AbsAxisPrivate::getTickLabel(double tick, SRC src) {
|
|
auto renderState = reinterpret_cast<AbsAxisRenderState*>(state(src));
|
|
return renderState->locale.toString(tick, renderState->formatChar.toLatin1(), renderState->numberPrecision);
|
|
}
|
|
|
|
double AbsAxis::startCoord(SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return renderState->coordStart;
|
|
}
|
|
|
|
double AbsAxis::endCoord(SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return renderState->coordStart + renderState->coordLength;
|
|
}
|
|
|
|
Range AbsAxis::coordRange(SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return {renderState->coordStart, renderState->coordStart + renderState->coordLength};
|
|
}
|
|
|
|
int AbsAxis::getPixelPointSize(Range range, SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return static_cast<int>(qAbs(range.length()/coordRange(src).length()) * renderState->pixelSize);
|
|
}
|
|
|
|
int AbsAxis::getPixelPointSize(SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return qAbs(renderState->pixelSize);
|
|
}
|
|
|
|
|
|
double AbsAxis::getTickStep(const Range &range) {return d()->getTickStep(range);}
|
|
int AbsAxis::getSubTickCount(double tickStep) {return d()->getSubTickCount(tickStep);}
|
|
QString AbsAxis::getTickLabel(double tick, SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return d()->getTickLabel(tick, src);
|
|
}
|
|
QVector<QString> AbsAxis::createLabelVector(const QVector<double> &ticks, SRC src) {
|
|
INIT_GET(AbsAxis)
|
|
return d()->createLabelVector(ticks, src);
|
|
}
|
|
|
|
QVector<double> AbsAxis::createTickVector(double tickStep, const Range &range) {
|
|
return d()->createTickVector(tickStep, range);
|
|
}
|
|
QVector<double> AbsAxis::createSubTickVector(int subTickCount, const QVector<double> &ticks, const Range& range) {
|
|
return d()->createSubTickVector(subTickCount, ticks, range);
|
|
}
|
|
}
|