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