CallbackSearchReturn CallbackAxisPointsAbstract::callbackRequire3AxisPoints (const QPointF &posScreen,
                                                                             const QPointF &posGraph)
{
  CallbackSearchReturn rtn = CALLBACK_SEARCH_RETURN_CONTINUE;

  // Update range variables
  int numberPoints = m_screenInputs.count();
  if ((numberPoints == 0) || (posGraph.x () < m_xGraphLow)) { m_xGraphLow = posGraph.x (); }
  if ((numberPoints == 0) || (posGraph.y () < m_yGraphLow)) { m_yGraphLow = posGraph.y (); }
  if ((numberPoints == 0) || (posGraph.x () > m_xGraphHigh)) { m_xGraphHigh = posGraph.x (); }
  if ((numberPoints == 0) || (posGraph.y () > m_yGraphHigh)) { m_yGraphHigh = posGraph.y (); }

  if (numberPoints < 3) {

    // Append new point
    m_screenInputs.push_back (posScreen);
    m_graphOutputs.push_back (posGraph);
    numberPoints = m_screenInputs.count();

    if (numberPoints == 3) {
      loadTransforms3 ();
    }

    // Error checking
    if (anyPointsRepeatPair (m_screenInputs)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("New axis point cannot be at the same screen position as an exisiting axis point");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if (anyPointsRepeatPair (m_graphOutputs)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("New axis point cannot have the same graph coordinates as an existing axis point");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if ((numberPoints == 3) && threePointsAreCollinear (m_screenInputsTransform)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("No more than two axis points can lie along the same line on the screen");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if ((numberPoints == 3) && threePointsAreCollinear (m_graphOutputsTransform)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("No more than two axis points can lie along the same line in graph coordinates");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    }
  }

  if (m_screenInputs.count() > 2) {

    // There are enough axis points so quit
    rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

  }

  return rtn;
}
CallbackSearchReturn CallbackAxisPointsAbstract::callbackRequire4AxisPoints (bool isXOnly,
                                                                             const QPointF &posScreen,
                                                                             const QPointF &posGraph)
{
  CallbackSearchReturn rtn = CALLBACK_SEARCH_RETURN_CONTINUE;

  // Update range variables
  int numberPoints = m_screenInputsX.count() + m_screenInputsY.count();
  if ((numberPoints == 0) || (posGraph.x () < m_xGraphLow)) { m_xGraphLow = posGraph.x (); }
  if ((numberPoints == 0) || (posGraph.y () < m_yGraphLow)) { m_yGraphLow = posGraph.y (); }
  if ((numberPoints == 0) || (posGraph.x () > m_xGraphHigh)) { m_xGraphHigh = posGraph.x (); }
  if ((numberPoints == 0) || (posGraph.y () > m_yGraphHigh)) { m_yGraphHigh = posGraph.y (); }

  if (numberPoints < 4) {

    // Append the new point
    if (isXOnly) {

      m_screenInputsX.push_back (posScreen);
      m_graphOutputsX.push_back (posGraph.x());

    } else {

      m_screenInputsY.push_back (posScreen);
      m_graphOutputsY.push_back (posGraph.y());

    }

    numberPoints = m_screenInputsX.count() + m_screenInputsY.count();
    if (numberPoints == 4) {
      loadTransforms4 ();
    }
  }

  if (m_screenInputsX.count() > 2) {

    m_isError = true;
    m_errorMessage = QObject::tr ("Too many x axis points. There should only be two");
    rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

  } else if (m_screenInputsY.count() > 2) {

    m_isError = true;
    m_errorMessage = QObject::tr ("Too many y axis points. There should only be two");
    rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

  } else {

    if ((m_screenInputsX.count() == 2) &&
        (m_screenInputsY.count() == 2)) {

      // Done, although an error may intrude
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;
    }

    // Error checking
    if (anyPointsRepeatPair (m_screenInputsX) ||
        anyPointsRepeatPair (m_screenInputsY)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("New axis point cannot be at the same screen position as an exisiting axis point");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if (anyPointsRepeatSingle (m_graphOutputsX) ||
               anyPointsRepeatSingle (m_graphOutputsY)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("New axis point cannot have the same graph coordinates as an existing axis point");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if ((numberPoints == 4) && threePointsAreCollinear (m_screenInputsTransform)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("No more than two axis points can lie along the same line on the screen");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if ((numberPoints == 4) && threePointsAreCollinear (m_graphOutputsTransform)) {

      m_isError = true;
      m_errorMessage = QObject::tr ("No more than two axis points can lie along the same line in graph coordinates");
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    }
  }

  return rtn;
}
CallbackSearchReturn CallbackAxisPointsAbstract::callback (const QString & /* curveName */,
                                                           const Point &point)
{
  CallbackSearchReturn rtn = CALLBACK_SEARCH_RETURN_CONTINUE;

  if (m_numberAxisPoints < 3) {

    QPointF posScreen = point.posScreen ();
    QPointF posGraph = point.posGraph ();

    if (m_pointIdentifierOverride == point.identifier ()) {

      // Override the old point coordinates with its new (if all tests are passed) coordinates
      posScreen = m_posScreenOverride;
      posGraph = m_posGraphOverride;
    }

    // Update range variables
    if ((m_numberAxisPoints == 0) || (posGraph.x () < m_xGraphLow)) { m_xGraphLow = posGraph.x (); }
    if ((m_numberAxisPoints == 0) || (posGraph.y () < m_yGraphLow)) { m_yGraphLow = posGraph.y (); }
    if ((m_numberAxisPoints == 0) || (posGraph.x () > m_xGraphHigh)) { m_xGraphHigh = posGraph.x (); }
    if ((m_numberAxisPoints == 0) || (posGraph.y () > m_yGraphHigh)) { m_yGraphHigh = posGraph.y (); }

    // Append one new column to each of the screen and graph coordinate matrices. Since QTransform::setTransform
    // deals with an entire array instead of element by element, we copy the QTransform arrays here, modify them,
    // and then copy them back. The local arrays are also handy for error checking

    double sm [3] [3] = {
      {m_screenInputs.m11 (), m_screenInputs.m12 (), m_screenInputs.m13 ()},
      {m_screenInputs.m21 (), m_screenInputs.m22 (), m_screenInputs.m23 ()},
      {m_screenInputs.m31 (), m_screenInputs.m32 (), m_screenInputs.m33 ()}};
    double gm [3] [3] = {
      {m_graphOutputs.m11 (), m_graphOutputs.m12 (), m_graphOutputs.m13 ()},
      {m_graphOutputs.m21 (), m_graphOutputs.m22 (), m_graphOutputs.m23 ()},
      {m_graphOutputs.m31 (), m_graphOutputs.m32 (), m_graphOutputs.m33 ()}};

    // Screen coordinates
    sm [0] [m_numberAxisPoints] = posScreen.x ();
    sm [1] [m_numberAxisPoints] = posScreen.y ();
    sm [2] [m_numberAxisPoints] = 1.0;

    // Graph coordinates
    gm [0] [m_numberAxisPoints] = posGraph.x ();
    gm [1] [m_numberAxisPoints] = posGraph.y ();
    gm [2] [m_numberAxisPoints] = 1.0;

    // Save screen matrix
    m_screenInputs.setMatrix (sm [0] [0], sm [0] [1], sm [0] [2],
                              sm [1] [0], sm [1] [1], sm [1] [2],
                              sm [2] [0], sm [2] [1], sm [2] [2]);

    // Save graph matrix. These are the raw graph coordinates, which means they may be polar coordinates
    // and/or have log scaling, since Transformation converts raw to linear cartesian cooordinates
    m_graphOutputs.setMatrix (gm [0] [0], gm [0] [1], gm [0] [2],
                              gm [1] [0], gm [1] [1], gm [1] [2],
                              gm [2] [0], gm [2] [1], gm [2] [2]);

    ++m_numberAxisPoints; // Update this number before calling anyColumnsRepeat and threePointsAreCollinear

    // Error checking
    if (anyColumnsRepeat (sm, m_numberAxisPoints)) {

      m_isError = true;
      m_errorMessage = "New axis point cannot be at the same screen position as an exisiting axis point";
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if (anyColumnsRepeat (gm, m_numberAxisPoints)) {

      m_isError = true;
      m_errorMessage = "New axis point cannot have the same graph coordinates as an existing axis point";
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if (threePointsAreCollinear (sm, m_numberAxisPoints)) {

      m_isError = true;
      m_errorMessage = "No more than two axis points can lie along the same line on the screen";
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    } else if (threePointsAreCollinear (gm, m_numberAxisPoints)) {

      m_isError = true;
      m_errorMessage = "No more than two axis points can lie along the same line in graph coordinates";
      rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

    }
  }

  if (m_numberAxisPoints > 2) {

    // There are enough axis points
    rtn = CALLBACK_SEARCH_RETURN_INTERRUPT;

  }

  return rtn;
}