Files
Renderive/YSGraphic_Core/Axis/AbsAxis.cpp
T
2026-07-23 11:10:17 +08:00

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);
}
}