Commit 0b274be2 authored by Stefan Gerlach's avatar Stefan Gerlach

[backend] Improve Range class

parent 4de64b34
......@@ -40,40 +40,40 @@
template<class T>
class Range {
public:
Range() : m_left(0), m_right(0) {}
Range(T left, T right) {
this->setRange(left, right);
Range() : m_min(0), m_max(0) {}
Range(T min, T max) {
this->setRange(min, max);
}
~Range() = default;
T left() const { return m_left; }
T right() const { return m_right; }
void setLeft(T left) { m_left = left; } // no check (use first)
void setRight(T right) { m_right = std::max(m_left, right); }
void setRange(T left, T right) {
m_left = left;
m_right = std::max(left, right);
T min() const { return m_min; }
T max() const { return m_max; }
void setMin(T min) { m_min = min; } // no check (use first)
void setMax(T max) { m_max = std::max(m_min, max); }
void setRange(T min, T max) {
m_min = min;
m_max = std::max(min, max);
}
T size() const { return m_right - m_left; }
bool isZero() const { return (m_right == m_left); }
bool inside(const Range<T>& other) const { return ( m_left <= other.left() && m_right >= other.right() ); }
bool inside(T value) const { return ( m_left <= value && m_right >= value ); }
void translate(T offset) { m_left += offset; m_right += offset; }
bool operator==(const Range<T>& other) const { return ( m_left == other.left() && m_right == other.right() ); }
T size() const { return m_max - m_min; }
bool isZero() const { return (m_max == m_min); }
bool inside(const Range<T>& other) const { return ( m_min <= other.min() && m_max >= other.max() ); }
bool inside(T value) const { return ( m_min <= value && m_max >= value ); }
void translate(T offset) { m_min += offset; m_max += offset; }
bool operator==(const Range<T>& other) const { return ( m_min == other.min() && m_max == other.max() ); }
Range<T>& operator=(const Range<T>& other) {
m_left = other.left();
m_right = other.right();
m_min = other.min();
m_max = other.max();
return *this;
}
//! Return a string in the format '[left, right]'
//! Return a string in the format '[min, max]'
QString toString() const {
return "[" + QString::number(m_left) + ", " + QString::number(m_right) + "]";
return "[" + QString::number(m_min) + ", " + QString::number(m_max) + "]";
}
//TODO: touches(), merge(), subtract(), split(), etc. (see Interval)
private:
T m_left; // lower limit
T m_right; // upper limit
T m_min; // lower limit
T m_max; // upper limit
};
#endif
......
......@@ -1613,15 +1613,12 @@ void XYFitCurvePrivate::recalculate() {
QVector<double> ydataVector;
QVector<double> xerrorVector;
QVector<double> yerrorVector;
double xmin, xmax;
if (fitData.autoRange) {
xmin = tmpXDataColumn->minimum();
xmax = tmpXDataColumn->maximum();
} else {
xmin = fitData.fitRange.left();
xmax = fitData.fitRange.right();
Range<double> xRange{tmpXDataColumn->minimum(), tmpXDataColumn->maximum()};
if (!fitData.autoRange) {
if (!fitData.fitRange.isZero()) // avoid zero range
xRange.setRange(fitData.fitRange.min(), fitData.fitRange.max());
}
DEBUG(" fit range = " << xmin << " .. " << xmax);
DEBUG(" fit range = " << xRange.min() << " .. " << xRange.max());
//logic from XYAnalysisCurve::copyData(), extended by the handling of error columns.
//TODO: decide how to deal with non-numerical error columns
......@@ -1670,8 +1667,7 @@ void XYFitCurvePrivate::recalculate() {
y = tmpYDataColumn->dateTimeAt(row).toMSecsSinceEpoch();
}
// only when inside given range
if (x >= xmin && x <= xmax) {
if (x >= xRange.min() && x <= xRange.max()) { // only when inside given range
if ((!xErrorColumn && !yErrorColumn) || !fitData.useDataErrors) { // x-y
xdataVector.append(x);
ydataVector.append(y);
......@@ -2058,7 +2054,7 @@ void XYFitCurvePrivate::recalculate() {
} else { // only selected range
size_t j = 0;
for (int i = 0; i < tmpXDataColumn->rowCount(); i++) {
if (tmpXDataColumn->valueAt(i) >= xmin && tmpXDataColumn->valueAt(i) <= xmax)
if (tmpXDataColumn->valueAt(i) >= xRange.min() && tmpXDataColumn->valueAt(i) <= xRange.max())
residualsVector->data()[i] = - gsl_vector_get(s->f, j++);
else // outside range
residualsVector->data()[i] = 0;
......@@ -2134,7 +2130,7 @@ void XYFitCurvePrivate::evaluate(bool preview) {
if (preview) // results not available yet
paramValues = fitData.paramStartValues;
bool rc = parser->evaluateCartesian(fitData.model, QString::number(xRange.left()), QString::number(xRange.right()), (int)fitData.evaluatedPoints,
bool rc = parser->evaluateCartesian(fitData.model, QString::number(xRange.min()), QString::number(xRange.max()), (int)fitData.evaluatedPoints,
xVector, yVector, fitData.paramNames, paramValues);
if (!rc) {
DEBUG(" ERROR: Parsing fit function failed")
......@@ -2191,8 +2187,8 @@ void XYFitCurve::save(QXmlStreamWriter* writer) const {
WRITE_COLUMN(d->xErrorColumn, xErrorColumn);
WRITE_COLUMN(d->yErrorColumn, yErrorColumn);
writer->writeAttribute("autoRange", QString::number(d->fitData.autoRange));
writer->writeAttribute("fitRangeMin", QString::number(d->fitData.fitRange.left(), 'g', 15));
writer->writeAttribute("fitRangeMax", QString::number(d->fitData.fitRange.right(), 'g', 15));
writer->writeAttribute("fitRangeMin", QString::number(d->fitData.fitRange.min(), 'g', 15));
writer->writeAttribute("fitRangeMax", QString::number(d->fitData.fitRange.max(), 'g', 15));
writer->writeAttribute("modelCategory", QString::number(d->fitData.modelCategory));
writer->writeAttribute("modelType", QString::number(d->fitData.modelType));
writer->writeAttribute("xWeightsType", QString::number(d->fitData.xWeightsType));
......
......@@ -1013,8 +1013,8 @@ void XYFitCurveDock::setPlotXRange() {
const Range<double> range = m_fitData.evalRange;
const double extend = range.size() * 0.05; // + 5 %
if (!range.isZero()) {
plot->setXMin(range.left() - extend);
plot->setXMax(range.right() + extend);
plot->setXMin(range.min() - extend);
plot->setXMax(range.max() + extend);
}
}
}
......
......@@ -59,15 +59,15 @@ FitOptionsWidget::FitOptionsWidget(QWidget* parent, XYFitCurve::FitData* fitData
const auto* plot = static_cast<const CartesianPlot*>(fitCurve->parentAspect());
m_dateTimeRange = (plot->xRangeFormat() != CartesianPlot::RangeFormat::Numeric);
if (!m_dateTimeRange) {
ui.leMin->setText(QString::number(m_fitData->fitRange.left()));
ui.leMax->setText(QString::number(m_fitData->fitRange.right()));
ui.leEvalMin->setText(QString::number(m_fitData->evalRange.left()));
ui.leEvalMax->setText(QString::number(m_fitData->evalRange.right()));
ui.leMin->setText(QString::number(m_fitData->fitRange.min()));
ui.leMax->setText(QString::number(m_fitData->fitRange.max()));
ui.leEvalMin->setText(QString::number(m_fitData->evalRange.min()));
ui.leEvalMax->setText(QString::number(m_fitData->evalRange.max()));
} else {
ui.dateTimeEditMin->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->fitRange.left()) );
ui.dateTimeEditMax->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->fitRange.right()) );
ui.dateTimeEditEvalMin->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->evalRange.left()) );
ui.dateTimeEditEvalMax->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->evalRange.right()) );
ui.dateTimeEditMin->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->fitRange.min()) );
ui.dateTimeEditMax->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->fitRange.max()) );
ui.dateTimeEditEvalMin->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->evalRange.min()) );
ui.dateTimeEditEvalMax->setDateTime( QDateTime::fromMSecsSinceEpoch(m_fitData->evalRange.max()) );
}
ui.leMin->setVisible(!m_dateTimeRange);
......@@ -191,52 +191,52 @@ void FitOptionsWidget::autoEvalRangeChanged() {
void FitOptionsWidget::fitRangeMinChanged() {
const double xMin = ui.leMin->text().toDouble();
m_fitData->fitRange.setLeft(xMin);
m_fitData->fitRange.setMin(xMin);
changed();
}
void FitOptionsWidget::fitRangeMaxChanged() {
const double xMax = ui.leMax->text().toDouble();
m_fitData->fitRange.setRight(xMax);
m_fitData->fitRange.setMax(xMax);
changed();
}
void FitOptionsWidget::fitRangeMinDateTimeChanged(const QDateTime& dateTime) {
m_fitData->fitRange.setLeft(dateTime.toMSecsSinceEpoch());
m_fitData->fitRange.setMin(dateTime.toMSecsSinceEpoch());
changed();
}
void FitOptionsWidget::fitRangeMaxDateTimeChanged(const QDateTime& dateTime) {
m_fitData->fitRange.setRight(dateTime.toMSecsSinceEpoch());
m_fitData->fitRange.setMax(dateTime.toMSecsSinceEpoch());
changed();
}
void FitOptionsWidget::evalRangeMinChanged() {
const double xMin = ui.leEvalMin->text().toDouble();
m_fitData->evalRange.setLeft(xMin);
m_fitData->evalRange.setMin(xMin);
changed();
}
void FitOptionsWidget::evalRangeMaxChanged() {
const double xMax = ui.leEvalMax->text().toDouble();
m_fitData->evalRange.setRight(xMax);
m_fitData->evalRange.setMax(xMax);
changed();
}
void FitOptionsWidget::evalRangeMinDateTimeChanged(const QDateTime& dateTime) {
m_fitData->evalRange.setLeft(dateTime.toMSecsSinceEpoch());
m_fitData->evalRange.setMin(dateTime.toMSecsSinceEpoch());
changed();
}
void FitOptionsWidget::evalRangeMaxDateTimeChanged(const QDateTime& dateTime) {
m_fitData->evalRange.setRight(dateTime.toMSecsSinceEpoch());
m_fitData->evalRange.setMax(dateTime.toMSecsSinceEpoch());
changed();
}
void FitOptionsWidget::applyClicked() {
m_fitData->maxIterations = ui.leMaxIterations->text().toFloat();
m_fitData->eps = ui.leEps->text().toFloat();
m_fitData->maxIterations = ui.leMaxIterations->text().toFloat(); //TODO: float?
m_fitData->eps = ui.leEps->text().toFloat(); // TODO: float?
m_fitData->evaluatedPoints = ui.leEvaluatedPoints->text().toInt();
m_fitData->useDataErrors = ui.cbUseDataErrors->isChecked();
m_fitData->useResults = ui.cbUseResults->isChecked();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment