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