00001
00002
00003
00004
00005
00006
00007 #include "CallbackUpdateTransform.h"
00008 #include "Document.h"
00009 #include "EngaugeAssert.h"
00010 #include "FormatCoordsUnits.h"
00011 #include "Logger.h"
00012 #include <QDebug>
00013 #include <qmath.h>
00014 #include <QtGlobal>
00015 #include "QtToString.h"
00016 #include "Transformation.h"
00017
00018 using namespace std;
00019
00022 const int PRECISION_DIGITS = 4;
00023
00024 const double PI = 3.1415926535;
00025 const double ZERO_OFFSET_AFTER_LOG = 1;
00026
00027 Transformation::Transformation() :
00028 m_transformIsDefined (false)
00029 {
00030 }
00031
00032 Transformation &Transformation::operator=(const Transformation &other)
00033 {
00034 m_transformIsDefined = other.transformIsDefined();
00035 m_transform = other.transformMatrix ();
00036
00037 return *this;
00038 }
00039
00040 bool Transformation::operator!=(const Transformation &other)
00041 {
00042 return (m_transformIsDefined != other.transformIsDefined()) ||
00043 (m_transform != other.transformMatrix ());
00044 }
00045
00046 QTransform Transformation::calculateTransformFromLinearCartesianPoints (const QPointF &posFrom0,
00047 const QPointF &posFrom1,
00048 const QPointF &posFrom2,
00049 const QPointF &posTo0,
00050 const QPointF &posTo1,
00051 const QPointF &posTo2)
00052 {
00053 LOG4CPP_INFO_S ((*mainCat)) << "Transformation::calculateTransformFromLinearCartesianPoints";
00054
00055 QTransform from, to;
00056 from.setMatrix (posFrom0.x(), posFrom1.x(), posFrom2.x(),
00057 posFrom0.y(), posFrom1.y(), posFrom2.y(),
00058 1.0, 1.0, 1.0);
00059
00060 to.setMatrix (posTo0.x(), posTo1.x(), posTo2.x(),
00061 posTo0.y(), posTo1.y(), posTo2.y(),
00062 1.0, 1.0, 1.0);
00063 QTransform fromInv = from.inverted ();
00064
00065 return to * fromInv;
00066 }
00067
00068 QPointF Transformation::cartesianFromCartesianOrPolar (const DocumentModelCoords &modelCoords,
00069 const QPointF &posGraphIn)
00070 {
00071
00072 QPointF posGraphCartesian = posGraphIn;
00073
00074 if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
00075
00076
00077 double angleRadians = 0;
00078 switch (modelCoords.coordUnitsTheta())
00079 {
00080 case COORD_UNITS_POLAR_THETA_DEGREES:
00081 case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES:
00082 case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS:
00083 case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS_NSEW:
00084 angleRadians = posGraphIn.x () * PI / 180.0;
00085 break;
00086
00087 case COORD_UNITS_POLAR_THETA_GRADIANS:
00088 angleRadians = posGraphIn.x () * PI / 200.0;
00089 break;
00090
00091 case COORD_UNITS_POLAR_THETA_RADIANS:
00092 angleRadians = posGraphIn.x ();
00093 break;
00094
00095 case COORD_UNITS_POLAR_THETA_TURNS:
00096 angleRadians = posGraphIn.x () * 2.0 * PI;
00097 break;
00098
00099 default:
00100 ENGAUGE_ASSERT (false);
00101 }
00102
00103 double radius = posGraphIn.y ();
00104 posGraphCartesian.setX (radius * cos (angleRadians));
00105 posGraphCartesian.setY (radius * sin (angleRadians));
00106 }
00107
00108 return posGraphCartesian;
00109 }
00110
00111 QPointF Transformation::cartesianOrPolarFromCartesian (const DocumentModelCoords &modelCoords,
00112 const QPointF &posGraphIn)
00113 {
00114
00115 QPointF posGraphCartesianOrPolar = posGraphIn;
00116
00117 if (modelCoords.coordsType() == COORDS_TYPE_POLAR) {
00118
00119
00120 double angleRadians = qAtan2 (posGraphIn.y (),
00121 posGraphIn.x ());
00122 switch (modelCoords.coordUnitsTheta())
00123 {
00124 case COORD_UNITS_POLAR_THETA_DEGREES:
00125 case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES:
00126 case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS:
00127 case COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS_NSEW:
00128 posGraphCartesianOrPolar.setX (angleRadians * 180.0 / PI);
00129 break;
00130
00131 case COORD_UNITS_POLAR_THETA_GRADIANS:
00132 posGraphCartesianOrPolar.setX (angleRadians * 200.0 / PI);
00133 break;
00134
00135 case COORD_UNITS_POLAR_THETA_RADIANS:
00136 posGraphCartesianOrPolar.setX (angleRadians);
00137 break;
00138
00139 case COORD_UNITS_POLAR_THETA_TURNS:
00140 posGraphCartesianOrPolar.setX (angleRadians / 2.0 / PI);
00141 break;
00142
00143 default:
00144 ENGAUGE_ASSERT (false);
00145 }
00146
00147 double radius = qSqrt (posGraphIn.x () * posGraphIn.x () + posGraphIn.y () * posGraphIn.y ());
00148 posGraphCartesianOrPolar.setY (radius);
00149 }
00150
00151 return posGraphCartesianOrPolar;
00152 }
00153
00154 void Transformation::coordTextForStatusBar (QPointF cursorScreen,
00155 QString &coordsScreen,
00156 QString &coordsGraph,
00157 QString &resolutionsGraph)
00158 {
00159 const int UNCONSTRAINED_FIELD_WIDTH = 0;
00160 const double X_DELTA_PIXELS = 1.0, Y_DELTA_PIXELS = 1.0;
00161 const char FORMAT = 'g';
00162
00163 if (cursorScreen.x() < 0 ||
00164 cursorScreen.y() < 0) {
00165
00166
00167 coordsScreen = "";
00168 coordsGraph = "";
00169 resolutionsGraph = "";
00170
00171 } else {
00172
00173 coordsScreen = QString("(%1, %2)")
00174 .arg (cursorScreen.x ())
00175 .arg (cursorScreen.y ());
00176
00177 if (m_transformIsDefined) {
00178
00179
00180 QPointF cursorScreenDelta (cursorScreen.x () + X_DELTA_PIXELS,
00181 cursorScreen.y () + Y_DELTA_PIXELS);
00182
00183
00184 QPointF pointGraph, pointGraphDelta;
00185 transformScreenToRawGraph (cursorScreen,
00186 pointGraph);
00187 transformScreenToRawGraph (cursorScreenDelta,
00188 pointGraphDelta);
00189
00190
00191 double resolutionXGraph = qAbs ((pointGraphDelta.x () - pointGraph.x ()) / X_DELTA_PIXELS);
00192 double resolutionYGraph = qAbs ((pointGraphDelta.y () - pointGraph.y ()) / Y_DELTA_PIXELS);
00193
00194
00195 FormatCoordsUnits format;
00196 QString xThetaFormatted, yRadiusFormatted;
00197 format.unformattedToFormatted (pointGraph.x(),
00198 pointGraph.y(),
00199 m_modelCoords,
00200 m_modelMainWindow,
00201 xThetaFormatted,
00202 yRadiusFormatted,
00203 *this);
00204
00205 coordsGraph = QString ("(%1, %2)")
00206 .arg (xThetaFormatted)
00207 .arg (yRadiusFormatted);
00208
00209 resolutionsGraph = QString ("(%1, %2)")
00210 .arg (resolutionXGraph, UNCONSTRAINED_FIELD_WIDTH, FORMAT, PRECISION_DIGITS)
00211 .arg (resolutionYGraph, UNCONSTRAINED_FIELD_WIDTH, FORMAT, PRECISION_DIGITS);
00212
00213 } else {
00214
00215 coordsGraph = "<font color=\"red\">Need more axis points</font>";
00216 resolutionsGraph = coordsGraph;
00217
00218 }
00219 }
00220 }
00221
00222 void Transformation::identity()
00223 {
00224
00225 m_transformIsDefined = true;
00226
00227 QTransform ident;
00228 m_transform = ident;
00229 }
00230
00231 double Transformation::logToLinearCartesian (double xy)
00232 {
00233 return qLn (xy);
00234 }
00235
00236 double Transformation::logToLinearRadius (double r,
00237 double rCenter)
00238 {
00239 return qLn (r) - qLn (rCenter);
00240 }
00241
00242 DocumentModelCoords Transformation::modelCoords() const
00243 {
00244 return m_modelCoords;
00245 }
00246
00247 ostringstream &operator<<(ostringstream &strOuter,
00248 const Transformation &transformation)
00249 {
00250 QString text;
00251 QTextStream strInner (&text);
00252 transformation.printStream ("", strInner);
00253
00254 strOuter << text.toLatin1().data ();
00255
00256 return strOuter;
00257 }
00258
00259 void Transformation::printStream (QString indentation,
00260 QTextStream &str) const
00261 {
00262 str << "Transformation\n";
00263
00264 indentation += INDENTATION_DELTA;
00265
00266 if (m_transformIsDefined) {
00267
00268 str << indentation << "affine=" << (m_transform.isAffine() ? "yes" : "no") << " matrix=("
00269 << m_transform.m11() << ", " << m_transform.m12() << ", " << m_transform.m13() << ", "
00270 << m_transform.m21() << ", " << m_transform.m22() << ", " << m_transform.m23() << ", "
00271 << m_transform.m31() << ", " << m_transform.m32() << ", " << m_transform.m33() << ")";
00272
00273 } else {
00274
00275 str << indentation << "undefined";
00276
00277 }
00278 }
00279
00280 void Transformation::resetOnLoad()
00281 {
00282 LOG4CPP_INFO_S ((*mainCat)) << "Transformation::resetOnLoad";
00283
00284 m_transformIsDefined = false;
00285 }
00286
00287 double Transformation::roundOffSmallValues (double value, double range)
00288 {
00289 if (qAbs (value) < range / qPow (10.0, PRECISION_DIGITS)) {
00290 value = 0.0;
00291 }
00292
00293 return value;
00294 }
00295
00296 void Transformation::setModelCoords (const DocumentModelCoords &modelCoords,
00297 const MainWindowModel &modelMainWindow)
00298 {
00299 m_modelCoords = modelCoords;
00300 m_modelMainWindow = modelMainWindow;
00301 }
00302
00303 bool Transformation::transformIsDefined() const
00304 {
00305 return m_transformIsDefined;
00306 }
00307
00308 void Transformation::transformLinearCartesianGraphToRawGraph (const QPointF &pointLinearCartesianGraph,
00309 QPointF &pointRawGraph) const
00310 {
00311
00312
00313
00314 pointRawGraph = pointLinearCartesianGraph;
00315
00316
00317 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
00318 pointRawGraph = cartesianOrPolarFromCartesian (m_modelCoords,
00319 pointRawGraph);
00320 }
00321
00322
00323 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
00324 (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR)) {
00325 pointRawGraph.setY (pointRawGraph.y() + m_modelCoords.originRadius());
00326 }
00327
00328
00329 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
00330 pointRawGraph.setX (qExp (pointRawGraph.x()));
00331 }
00332
00333 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
00334 double offset;
00335 if (m_modelCoords.coordsType() == COORDS_TYPE_CARTESIAN) {
00336
00337 offset = ZERO_OFFSET_AFTER_LOG;
00338 } else {
00339
00340 offset = m_modelCoords.originRadius();
00341 }
00342
00343 pointRawGraph.setY (qExp (pointRawGraph.y() + qLn (offset)));
00344 }
00345 }
00346
00347 void Transformation::transformLinearCartesianGraphToScreen (const QPointF &coordGraph,
00348 QPointF &coordScreen) const
00349 {
00350 ENGAUGE_ASSERT (m_transformIsDefined);
00351
00352 coordScreen = m_transform.inverted ().transposed ().map (coordGraph);
00353 }
00354
00355 QTransform Transformation::transformMatrix () const
00356 {
00357 return m_transform;
00358 }
00359
00360 void Transformation::transformRawGraphToLinearCartesianGraph (const QPointF &pointRaw,
00361 QPointF &pointLinearCartesian) const
00362 {
00363
00364
00365
00366 double x = pointRaw.x();
00367 double y = pointRaw.y();
00368
00369
00370 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
00371 (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR)) {
00372 y -= m_modelCoords.originRadius();
00373 }
00374
00375
00376 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
00377 x = logToLinearCartesian (x);
00378 }
00379
00380 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
00381 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
00382 y = logToLinearRadius (y,
00383 m_modelCoords.originRadius());
00384 } else {
00385 y = logToLinearRadius (y,
00386 ZERO_OFFSET_AFTER_LOG);
00387 }
00388 }
00389
00390
00391 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
00392 QPointF pointCart = cartesianFromCartesianOrPolar (m_modelCoords,
00393 QPointF (x, y));
00394 x = pointCart.x();
00395 y = pointCart.y();
00396 }
00397
00398 pointLinearCartesian.setX (x);
00399 pointLinearCartesian.setY (y);
00400 }
00401
00402 void Transformation::transformRawGraphToScreen (const QPointF &pointRaw,
00403 QPointF &pointScreen) const
00404 {
00405 QPointF pointLinearCartesianGraph;
00406
00407 transformRawGraphToLinearCartesianGraph (pointRaw,
00408 pointLinearCartesianGraph);
00409 transformLinearCartesianGraphToScreen (pointLinearCartesianGraph,
00410 pointScreen);
00411 }
00412
00413 void Transformation::transformScreenToLinearCartesianGraph (const QPointF &coordScreen,
00414 QPointF &coordGraph) const
00415 {
00416 ENGAUGE_ASSERT (m_transformIsDefined);
00417
00418 coordGraph = m_transform.transposed ().map (coordScreen);
00419 }
00420
00421 void Transformation::transformScreenToRawGraph (const QPointF &coordScreen,
00422 QPointF &coordGraph) const
00423 {
00424 QPointF pointLinearCartesianGraph;
00425 transformScreenToLinearCartesianGraph (coordScreen,
00426 pointLinearCartesianGraph);
00427 transformLinearCartesianGraphToRawGraph (pointLinearCartesianGraph,
00428 coordGraph);
00429 }
00430
00431 void Transformation::update (bool fileIsLoaded,
00432 const CmdMediator &cmdMediator,
00433 const MainWindowModel &modelMainWindow)
00434 {
00435 LOG4CPP_DEBUG_S ((*mainCat)) << "Transformation::update";
00436
00437 if (!fileIsLoaded) {
00438
00439 m_transformIsDefined = false;
00440
00441 } else {
00442
00443 setModelCoords (cmdMediator.document().modelCoords(),
00444 modelMainWindow);
00445
00446 CallbackUpdateTransform ftor (m_modelCoords,
00447 cmdMediator.document().documentAxesPointsRequired());
00448
00449 Functor2wRet<const QString &, const Point&, CallbackSearchReturn> ftorWithCallback = functor_ret (ftor,
00450 &CallbackUpdateTransform::callback);
00451 cmdMediator.iterateThroughCurvePointsAxes (ftorWithCallback);
00452
00453 if (ftor.transformIsDefined ()) {
00454
00455 updateTransformFromMatrices (ftor.matrixScreen(),
00456 ftor.matrixGraph());
00457 } else {
00458
00459 m_transformIsDefined = false;
00460
00461 }
00462 }
00463 }
00464
00465 void Transformation::updateTransformFromMatrices (const QTransform &matrixScreen,
00466 const QTransform &matrixGraph)
00467 {
00468
00469
00470 m_transformIsDefined = true;
00471
00472
00473 QPointF pointGraphRaw0 (matrixGraph.m11(),
00474 matrixGraph.m21());
00475 QPointF pointGraphRaw1 (matrixGraph.m12(),
00476 matrixGraph.m22());
00477 QPointF pointGraphRaw2 (matrixGraph.m13(),
00478 matrixGraph.m23());
00479
00480 QPointF pointGraphLinearCart0, pointGraphLinearCart1, pointGraphLinearCart2;
00481 transformRawGraphToLinearCartesianGraph (pointGraphRaw0,
00482 pointGraphLinearCart0);
00483 transformRawGraphToLinearCartesianGraph (pointGraphRaw1,
00484 pointGraphLinearCart1);
00485 transformRawGraphToLinearCartesianGraph (pointGraphRaw2,
00486 pointGraphLinearCart2);
00487
00488
00489 m_transform = calculateTransformFromLinearCartesianPoints (QPointF (matrixScreen.m11(), matrixScreen.m21()),
00490 QPointF (matrixScreen.m12(), matrixScreen.m22()),
00491 QPointF (matrixScreen.m13(), matrixScreen.m23()),
00492 QPointF (pointGraphLinearCart0.x(), pointGraphLinearCart0.y()),
00493 QPointF (pointGraphLinearCart1.x(), pointGraphLinearCart1.y()),
00494 QPointF (pointGraphLinearCart2.x(), pointGraphLinearCart2.y()));
00495
00496
00497 QTransform matrixGraphLinear (pointGraphLinearCart0.x(),
00498 pointGraphLinearCart1.x(),
00499 pointGraphLinearCart2.x(),
00500 pointGraphLinearCart0.y(),
00501 pointGraphLinearCart1.y(),
00502 pointGraphLinearCart2.y(),
00503 1.0,
00504 1.0);
00505
00506 QPointF pointScreenRoundTrip0, pointScreenRoundTrip1, pointScreenRoundTrip2;
00507 transformRawGraphToScreen (pointGraphRaw0,
00508 pointScreenRoundTrip0);
00509 transformRawGraphToScreen (pointGraphRaw1,
00510 pointScreenRoundTrip1);
00511 transformRawGraphToScreen (pointGraphRaw2,
00512 pointScreenRoundTrip2);
00513
00514 QPointF pointScreen0 (matrixScreen.m11(),
00515 matrixScreen.m21());
00516 QPointF pointScreen1 (matrixScreen.m12(),
00517 matrixScreen.m22());
00518 QPointF pointScreen2 (matrixScreen.m13(),
00519 matrixScreen.m23());
00520
00521 LOG4CPP_INFO_S ((*mainCat)) << "Transformation::updateTransformFromMatrices"
00522 << " matrixScreen=\n" << QTransformToString (matrixScreen).toLatin1().data () << " "
00523 << " matrixGraphRaw=\n" << QTransformToString (matrixGraph).toLatin1().data() << " "
00524 << " matrixGraphLinear=\n" << QTransformToString (matrixGraphLinear).toLatin1().data() << "\n"
00525 << " originalScreen0=" << QPointFToString (pointScreen0).toLatin1().data() << "\n"
00526 << " originalScreen1=" << QPointFToString (pointScreen1).toLatin1().data() << "\n"
00527 << " originalScreen2=" << QPointFToString (pointScreen2).toLatin1().data() << "\n"
00528 << " roundTripScreen0=" << QPointFToString (pointScreenRoundTrip0).toLatin1().data() << "\n"
00529 << " roundTripScreen1=" << QPointFToString (pointScreenRoundTrip1).toLatin1().data() << "\n"
00530 << " roundTripScreen2=" << QPointFToString (pointScreenRoundTrip2).toLatin1().data() << "\n";
00531 }