SplinePair SplinePair::operator/(const SplinePair &other) const
{
  SplinePair result (m_x / other.x(),
                     m_y / other.y());

  return result;
}
Exemple #2
0
void ExportFileRelations::loadXThetaYRadiusValuesForCurveInterpolatedSmooth (const DocumentModelCoords &modelCoords,
                                                                             const DocumentModelGeneral &modelGeneral,
                                                                             const MainWindowModel &modelMainWindow,
                                                                             const Points &points,
                                                                             const ExportValuesOrdinal &ordinals,
                                                                             QVector<QString*> &xThetaValues,
                                                                             QVector<QString*> &yRadiusValues,
                                                                             const Transformation &transformation) const
{
  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileRelations::loadXThetaYRadiusValuesForCurveInterpolatedSmooth";

  vector<double> t;
  vector<SplinePair> xy;
  ExportOrdinalsSmooth ordinalsSmooth;

  ordinalsSmooth.loadSplinePairsWithTransformation (points,
                                                    transformation,
                                                    t,
                                                    xy);

  // Spline class requires at least one point
  if (xy.size() > 0) {

    // Fit a spline
    Spline spline (t,
                   xy);

    FormatCoordsUnits format;

    // Extract the points
    for (int row = 0; row < ordinals.count(); row++) {

      double ordinal = ordinals.at (row);
      SplinePair splinePairFound = spline.interpolateCoeff(ordinal);
      double xTheta = splinePairFound.x ();
      double yRadius = splinePairFound.y ();

      // Save values for this row into xThetaValues and yRadiusValues, after appropriate formatting
      format.unformattedToFormatted (xTheta,
                                     yRadius,
                                     modelCoords,
                                     modelGeneral,
                                     modelMainWindow,
                                     *(xThetaValues [row]),
                                     *(yRadiusValues [row]),
                                     transformation);
    }
  }
}
Exemple #3
0
SplinePair Spline::findSplinePairForFunctionX (double x,
        int numIterations) const
{
    SplinePair spCurrent;

    double tLow = m_t[0];
    double tHigh = m_t[m_xy.size() - 1];

    double tCurrent = (tHigh + tLow) / 2.0;
    double tDelta = (tHigh - tLow) / 4.0;
    for (int iteration = 0; iteration < numIterations; iteration++) {
        spCurrent = interpolateCoeff (tCurrent);
        if (spCurrent.x() > x) {
            tCurrent -= tDelta;
        } else {
            tCurrent += tDelta;
        }
        tDelta /= 2.0;
    }

    return spCurrent;
}
Exemple #4
0
void TestSpline::testSplinesAsControlPoints ()
{
  const int T_START = 1, T_STOP = 7;
  const double SPLINE_EPSILON = 0.01;
  const int NUM_T = 60;

  bool success = true;

  vector<double> t;
  vector<SplinePair> xy;
  
  // Independent variable must be evenly spaced
  t.push_back (T_START);
  t.push_back (2);
  t.push_back (3);
  t.push_back (4);
  t.push_back (5);
  t.push_back (6);
  t.push_back (T_STOP);

  // Simple curve, with x values tweaked slightly (from even spacing) to make the test data more stressing
  xy.push_back (SplinePair (1, 0.22));
  xy.push_back (SplinePair (1.8, 0.04));
  xy.push_back (SplinePair (3.2, -0.13));
  xy.push_back (SplinePair (4.3, -0.17));
  xy.push_back (SplinePair (5, -0.04));
  xy.push_back (SplinePair (5.8, 0.09));
  xy.push_back (SplinePair (7, 0.11));

  Spline s (t, xy);

  for (int i = 0; i <= NUM_T; i++) {
    double t = T_START + (double) i * (T_STOP - T_START) / (double) NUM_T;
    SplinePair spCoeff = s.interpolateCoeff (t);
    SplinePair spBezier = s.interpolateControlPoints (t);

    double xCoeff = spCoeff.x();
    double yCoeff = spCoeff.y();
    double xControl = spBezier.x();
    double yControl = spBezier.y();

    if (qAbs (xCoeff - xControl) > SPLINE_EPSILON) {
      success = false;
    }

    if (qAbs (yCoeff - yControl) > SPLINE_EPSILON) {
      success = false;
    }
  }

  QVERIFY (success);
}
ExportValuesOrdinal ExportOrdinalsSmooth::ordinalsAtIntervalsGraph (const vector<double> &t,
                                                                    const vector<SplinePair> &xy,
                                                                    double pointsInterval) const
{
  LOG4CPP_INFO_S ((*mainCat)) << "ExportOrdinalsSmooth::ordinalsAtIntervalsGraph";

  const double NUM_SMALLER_INTERVALS = 1000;

  // Results. Initially empty, but at the end it will have tMin, ..., tMax
  ExportValuesOrdinal ordinals;

  // Fit a spline
  Spline spline (t,
                 xy);

  // Integrate the distances for the subintervals
  double integratedSeparation = 0;
  QPointF posLast (xy [0].x(),
                   xy [0].y());

  // Simplest method to find the intervals is to break up the curve into many smaller intervals, and then aggregate them
  // into intervals that, as much as possible, have the desired length. Simplicity wins out over accuracy in this
  // approach - accuracy is sacrificed to achieve simplicity
  double tMin = t.front();
  double tMax = t.back();

  double tLast = 0.0;
  int iTLastInterval = 0;
  for (int iT = 0; iT < NUM_SMALLER_INTERVALS; iT++) {

    double t = tMin + ((tMax - tMin) * iT) / (NUM_SMALLER_INTERVALS - 1.0);

    SplinePair pairNew = spline.interpolateCoeff(t);

    QPointF posNew = QPointF (pairNew.x(),
                              pairNew.y());

    QPointF posDelta = posNew - posLast;
    double integratedSeparationDelta = qSqrt (posDelta.x() * posDelta.x() + posDelta.y() * posDelta.y());
    integratedSeparation += integratedSeparationDelta;

    while (integratedSeparation >= pointsInterval) {

      // End of current interval, and start of next interval. For better accuracy without having to crank up
      // the number of points by orders of magnitude, we use linear interpolation
      double sInterp;
      if (iT == 0) {
        sInterp = 0.0;
      } else {
        sInterp = (double) pointsInterval / (double) integratedSeparation;
      }
      double tInterp = (1.0 - sInterp) * tLast + sInterp * t;

      integratedSeparation -= pointsInterval; // Part of delta that was not used gets applied to next interval

      tLast = tInterp;
      ordinals.push_back (tInterp);
      iTLastInterval = iT;
    }

    tLast = t;
    posLast = posNew;
  }

  if (iTLastInterval < NUM_SMALLER_INTERVALS - 1) {

    // Add last point so we end up at tMax
    ordinals.push_back (tMax);

  }

  return ordinals;
}
void ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedSmooth (const DocumentModelCoords &modelCoords,
                                                                       const MainWindowModel &modelMainWindow,
                                                                       const Points &points,
                                                                       const ExportValuesXOrY &xThetaValues,
                                                                       const Transformation &transformation,
                                                                       QVector<QString*> &yRadiusValues) const
{
  LOG4CPP_INFO_S ((*mainCat)) << "ExportFileFunctions::loadYRadiusValuesForCurveInterpolatedSmooth";

  // Convert screen coordinates to graph coordinates, in vectors suitable for spline fitting
  vector<double> t;
  vector<SplinePair> xy;
  ExportOrdinalsSmooth ordinalsSmooth;

  ordinalsSmooth.loadSplinePairsWithTransformation (points,
                                                    transformation,
                                                    t,
                                                    xy);

  // Formatting
  FormatCoordsUnits format;
  QString dummyXThetaOut;

  if (points.count() == 0) {

    // Since there are no values, leave the field empty
    for (int row = 0; row < xThetaValues.count(); row++) {
      *(yRadiusValues [row]) = "";
    }

  } else if (points.count() == 1 ||
             points.count() == 2) {

    // Apply the single value everywhere (N=1) or do linear interpolation (N=2)
    for (int row = 0; row < xThetaValues.count(); row++) {

      double xTheta = xThetaValues.at (row);
      double yRadius;
      if (points.count() == 1) {
        yRadius = xy.at (0).y ();
      } else {
        double x0 = xy.at (0).x ();
        double x1 = xy.at (1).x ();
        double y0 = xy.at (0).y ();
        double y1 = xy.at (1).y ();
        if (x0 == x1) {
          // Cannot do linear interpolation using two points at the same x value
          yRadius = xy.at (0).y ();
        } else {
          double s = (xTheta - x0) / (x1 - x0);
          yRadius = (1.0 - s) * y0 + s * y1;
        }
      }
      format.unformattedToFormatted (xTheta,
                                     yRadius,
                                     modelCoords,
                                     modelMainWindow,
                                     dummyXThetaOut,
                                     *(yRadiusValues [row]),
                                     transformation);
    }

  } else {

    // Iteration accuracy versus number of iterations 8->256, 10->1024, 12->4096. Single pixel accuracy out of
    // typical image size of 1024x1024 means around 10 iterations gives decent accuracy for numbers much bigger
    // than 1. A value of 12 gave some differences in the least significant figures of numbers like 10^-3 in
    // the regression tests. Toggling between 30 and 32 made no difference in the regression tests.
    const int MAX_ITERATIONS = 32;

    // Spline class requires at least one point
    if (xy.size() > 0) {

      // Fit a spline
      Spline spline (t,
                     xy);

      // Get value at desired points
      for (int row = 0; row < xThetaValues.count(); row++) {

        double xTheta = xThetaValues.at (row);
        SplinePair splinePairFound = spline.findSplinePairForFunctionX (xTheta,
                                                                        MAX_ITERATIONS);
        double yRadius = splinePairFound.y ();

        // Save y/radius value for this row into yRadiusValues, after appropriate formatting
        QString dummyXThetaOut;
        format.unformattedToFormatted (xTheta,
                                       yRadius,
                                       modelCoords,
                                       modelMainWindow,
                                       dummyXThetaOut,
                                       *(yRadiusValues [row]),
                                       transformation);
      }
    }
  }
}
SplinePair::SplinePair(const SplinePair &other) :
  m_x (other.x()),
  m_y (other.y())
{
}