Engauge Digitizer 2
Loading...
Searching...
No Matches
Transformation.cpp
Go to the documentation of this file.
1/******************************************************************************************************
2 * (C) 2014 markummitchell@github.com. This file is part of Engauge Digitizer, which is released *
3 * under GNU General Public License version 2 (GPLv2) or (at your option) any later version. See file *
4 * LICENSE or go to gnu.org/licenses for details. Distribution requires prior written permission. *
5 ******************************************************************************************************/
6
8#include "Document.h"
9#include "EngaugeAssert.h"
10#include "FormatCoordsUnits.h"
11#include "Logger.h"
12#include <QDebug>
13#include <qmath.h>
14#include <QObject>
15#include <QtGlobal>
16#include "QtToString.h"
17#include "Transformation.h"
18
19using namespace std;
20
23const int PRECISION_DIGITS = 4;
24
25const double PI = 3.1415926535;
26const double ZERO_OFFSET_AFTER_LOG = 1; // Log of this value is zero
27
29 m_transformIsDefined (false)
30{
31}
32
34 m_transformIsDefined (other.transformIsDefined()),
35 m_transform (other.transformMatrix())
36{
37 setModelCoords (other.modelCoords(),
38 other.modelGeneral(),
39 other.modelMainWindow());
40}
41
43{
44 m_transformIsDefined = other.transformIsDefined();
45 m_transform = other.transformMatrix ();
46 setModelCoords (other.modelCoords(),
47 other.modelGeneral(),
48 other.modelMainWindow());
49
50 return *this;
51}
52
54{
55 return (m_transformIsDefined != other.transformIsDefined()) ||
56 (m_transform != other.transformMatrix ());
57}
58
60 const QPointF &posFrom1,
61 const QPointF &posFrom2,
62 const QPointF &posTo0,
63 const QPointF &posTo1,
64 const QPointF &posTo2)
65{
66 LOG4CPP_INFO_S ((*mainCat)) << "Transformation::calculateTransformFromLinearCartesianPoints";
67
69 from.setMatrix (posFrom0.x(), posFrom1.x(), posFrom2.x(),
70 posFrom0.y(), posFrom1.y(), posFrom2.y(),
71 1.0, 1.0, 1.0);
72
73 to.setMatrix (posTo0.x(), posTo1.x(), posTo2.x(),
74 posTo0.y(), posTo1.y(), posTo2.y(),
75 1.0, 1.0, 1.0);
76 QTransform fromInv = from.inverted ();
77
78 return to * fromInv;
79}
80
82 const QPointF &posGraphIn)
83{
84 // Initialize assuming input coordinates are already cartesian
86
88
89 // Input coordinates are polar so convert them
90 double angleRadians = 0; // Initialized to prevent compiler warning
92 {
97 angleRadians = posGraphIn.x () * PI / 180.0;
98 break;
99
101 angleRadians = posGraphIn.x () * PI / 200.0;
102 break;
103
105 angleRadians = posGraphIn.x ();
106 break;
107
109 angleRadians = posGraphIn.x () * 2.0 * PI;
110 break;
111
112 default:
113 ENGAUGE_ASSERT (false);
114 }
115
116 double radius = posGraphIn.y ();
117 posGraphCartesian.setX (radius * cos (angleRadians));
118 posGraphCartesian.setY (radius * sin (angleRadians));
119 }
120
121 return posGraphCartesian;
122}
123
125 const QPointF &posGraphIn)
126{
127 // Initialize assuming output coordinates are to be cartesian
129
131
132 // Output coordinates are to be polar so convert them
133 double angleRadians = qAtan2 (posGraphIn.y (),
134 posGraphIn.x ());
136 {
141 posGraphCartesianOrPolar.setX (angleRadians * 180.0 / PI);
142 break;
143
145 posGraphCartesianOrPolar.setX (angleRadians * 200.0 / PI);
146 break;
147
149 posGraphCartesianOrPolar.setX (angleRadians);
150 break;
151
153 posGraphCartesianOrPolar.setX (angleRadians / 2.0 / PI);
154 break;
155
156 default:
157 ENGAUGE_ASSERT (false);
158 }
159
160 double radius = qSqrt (posGraphIn.x () * posGraphIn.x () + posGraphIn.y () * posGraphIn.y ());
161 posGraphCartesianOrPolar.setY (radius);
162 }
163
165}
166
171 bool usingScaleBar)
172{
173 const int UNCONSTRAINED_FIELD_WIDTH = 0;
174 const double X_DELTA_PIXELS = 1.0, Y_DELTA_PIXELS = 1.0;
175 const char FORMAT = 'g';
176
178 QObject::tr ("Need scale bar") :
179 QObject::tr ("Need more axis points"));
180
181 if (cursorScreen.x() < 0 ||
182 cursorScreen.y() < 0) {
183
184 // Out of bounds, so return empty text
185 coordsScreen = "";
186 coordsGraph = "";
187 resolutionsGraph = "";
188
189 } else {
190
191 coordsScreen = QString("(%1, %2)")
192 .arg (cursorScreen.x ())
193 .arg (cursorScreen.y ());
194
195 if (m_transformIsDefined) {
196
197 // For resolution we compute graph coords for cursorScreen, and then for cursorScreen plus a delta
200
201 // Convert to graph coordinates
204 pointGraph);
207
208 // Compute graph resolutions at cursor
211
212 // Formatting for date/time and degrees/minutes/seconds is only done on coordinates, and not on resolution
213 FormatCoordsUnits format;
216 pointGraph.y(),
217 m_modelCoords,
218 m_modelGeneral,
219 m_modelMainWindow,
222 *this);
223
224 coordsGraph = QString ("(%1, %2)")
225 .arg (xThetaFormatted)
226 .arg (yRadiusFormatted);
227
228 resolutionsGraph = QString ("(%1, %2)")
231
232 } else {
233
234 coordsGraph = QString ("<font color=\"red\">%1</font>")
235 .arg (needMoreText);
237
238 }
239 }
240}
241
243{
244 // Initialize assuming points (0,0) (1,0) (0,1)
245 m_transformIsDefined = true;
246
248 m_transform = ident;
249}
250
252{
253 return qLn (xy);
254}
255
257 double rCenter)
258{
259 return qLn (r) - qLn (rCenter);
260}
261
263{
264 return m_modelCoords;
265}
266
268{
269 return m_modelGeneral;
270}
271
273{
274 return m_modelMainWindow;
275}
276
278 const Transformation &transformation)
279{
282 transformation.printStream ("", strInner);
283
284 strOuter << text.toLatin1().data ();
285
286 return strOuter;
287}
288
290 QTextStream &str) const
291{
292 str << "Transformation\n";
293
295
296 if (m_transformIsDefined) {
297
298 str << indentation << "affine=" << (m_transform.isAffine() ? "yes" : "no") << " matrix=("
299 << m_transform.m11() << ", " << m_transform.m12() << ", " << m_transform.m13() << ", "
300 << m_transform.m21() << ", " << m_transform.m22() << ", " << m_transform.m23() << ", "
301 << m_transform.m31() << ", " << m_transform.m32() << ", " << m_transform.m33() << ")";
302
303 } else {
304
305 str << indentation << "undefined";
306
307 }
308}
309
311{
312 LOG4CPP_INFO_S ((*mainCat)) << "Transformation::resetOnLoad";
313
314 m_transformIsDefined = false;
315}
316
317double Transformation::roundOffSmallValues (double value, double range)
318{
319 if (qAbs (value) < range / qPow (10.0, PRECISION_DIGITS)) {
320 value = 0.0;
321 }
322
323 return value;
324}
325
326void Transformation::setModelCoords (const DocumentModelCoords &modelCoords,
327 const DocumentModelGeneral &modelGeneral,
328 const MainWindowModel &modelMainWindow)
329{
330 m_modelCoords = modelCoords;
331 m_modelGeneral = modelGeneral;
332 m_modelMainWindow = modelMainWindow;
333}
334
336{
337 return m_transformIsDefined;
338}
339
341 QPointF &pointRawGraph) const
342{
343 // WARNING - the code in this method must mirror the code in transformRawGraphToLinearCartesianGraph. In
344 // other words, making a change here without a corresponding change there will produce a bug
345
347
348 // Apply polar coordinates if appropriate
349 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
352 }
353
354 // Apply linear offset to radius if appropriate
355 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
356 (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR)) {
357 pointRawGraph.setY (pointRawGraph.y() + m_modelCoords.originRadius());
358 }
359
360 // Apply log scaling if appropriate
361 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
362 pointRawGraph.setX (qExp (pointRawGraph.x()));
363 }
364
365 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
366 double offset;
367 if (m_modelCoords.coordsType() == COORDS_TYPE_CARTESIAN) {
368 // Cartesian
370 } else {
371 // Polar radius
372 offset = m_modelCoords.originRadius();
373 }
374
375 pointRawGraph.setY (qExp (pointRawGraph.y() + qLn (offset)));
376 }
377}
378
380 QPointF &coordScreen) const
381{
382 ENGAUGE_ASSERT (m_transformIsDefined);
383
384 coordScreen = m_transform.inverted ().transposed ().map (coordGraph);
385}
386
388{
389 return m_transform;
390}
391
394{
395 // WARNING - the code in this method must mirror the code in transformLinearCartesianGraphToRawGraph. In
396 // other words, making a change here without a corresponding change there will produce a bug
397
398 double x = pointRaw.x();
399 double y = pointRaw.y();
400
401 // Apply linear offset to radius if appropriate
402 if ((m_modelCoords.coordsType() == COORDS_TYPE_POLAR) &&
403 (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LINEAR)) {
404 y -= m_modelCoords.originRadius();
405 }
406
407 // Apply log scaling if appropriate
408 if (m_modelCoords.coordScaleXTheta() == COORD_SCALE_LOG) {
409 x = logToLinearCartesian (x);
410 }
411
412 if (m_modelCoords.coordScaleYRadius() == COORD_SCALE_LOG) {
413 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
414 y = logToLinearRadius (y,
415 m_modelCoords.originRadius());
416 } else {
417 y = logToLinearRadius (y,
419 }
420 }
421
422 // Apply polar coordinates if appropriate. Note range coordinate has just been transformed if it has log scaling
423 if (m_modelCoords.coordsType() == COORDS_TYPE_POLAR) {
425 QPointF (x, y));
426 x = pointCart.x();
427 y = pointCart.y();
428 }
429
430 pointLinearCartesian.setX (x);
431 pointLinearCartesian.setY (y);
432}
433
444
446 QPointF &coordGraph) const
447{
448 ENGAUGE_ASSERT (m_transformIsDefined);
449
450 coordGraph = m_transform.transposed ().map (coordScreen);
451}
452
462
464 const CmdMediator &cmdMediator,
465 const MainWindowModel &modelMainWindow)
466{
467 LOG4CPP_DEBUG_S ((*mainCat)) << "Transformation::update";
468
469 if (!fileIsLoaded) {
470
471 m_transformIsDefined = false;
472
473 } else {
474
475 setModelCoords (cmdMediator.document().modelCoords(),
476 cmdMediator.document().modelGeneral(),
478
479 CallbackUpdateTransform ftor (m_modelCoords,
480 cmdMediator.document().documentAxesPointsRequired());
481
485
486 if (ftor.transformIsDefined ()) {
487
488 updateTransformFromMatrices (ftor.matrixScreen(),
489 ftor.matrixGraph());
490
491 } else {
492
493 m_transformIsDefined = false;
494
495 }
496 }
497}
498
499void Transformation::updateTransformFromMatrices (const QTransform &matrixScreen,
500 const QTransform &matrixGraph)
501{
502 // LOG4CPP_INFO_S is below
503
504 m_transformIsDefined = true;
505
506 // Extract points from 3x3 matrices
507 QPointF pointGraphRaw0 (matrixGraph.m11(),
508 matrixGraph.m21());
509 QPointF pointGraphRaw1 (matrixGraph.m12(),
510 matrixGraph.m22());
511 QPointF pointGraphRaw2 (matrixGraph.m13(),
512 matrixGraph.m23());
513
521
522 // Calculate the transform
523 m_transform = calculateTransformFromLinearCartesianPoints (QPointF (matrixScreen.m11(), matrixScreen.m21()),
524 QPointF (matrixScreen.m12(), matrixScreen.m22()),
525 QPointF (matrixScreen.m13(), matrixScreen.m23()),
529
530 // Logging
537 1.0,
538 1.0);
539
547
548 QPointF pointScreen0 (matrixScreen.m11(),
549 matrixScreen.m21());
550 QPointF pointScreen1 (matrixScreen.m12(),
551 matrixScreen.m22());
552 QPointF pointScreen2 (matrixScreen.m13(),
553 matrixScreen.m23());
554
555 LOG4CPP_INFO_S ((*mainCat)) << "Transformation::updateTransformFromMatrices"
556 << " matrixScreen=\n" << QTransformToString (matrixScreen).toLatin1().data () << " "
557 << " matrixGraphRaw=\n" << QTransformToString (matrixGraph).toLatin1().data() << " "
558 << " matrixGraphLinear=\n" << QTransformToString (matrixGraphLinear).toLatin1().data() << "\n"
559 << " originalScreen0=" << QPointFToString (pointScreen0).toLatin1().data() << "\n"
560 << " originalScreen1=" << QPointFToString (pointScreen1).toLatin1().data() << "\n"
561 << " originalScreen2=" << QPointFToString (pointScreen2).toLatin1().data() << "\n"
562 << " roundTripScreen0=" << QPointFToString (pointScreenRoundTrip0).toLatin1().data() << "\n"
563 << " roundTripScreen1=" << QPointFToString (pointScreenRoundTrip1).toLatin1().data() << "\n"
564 << " roundTripScreen2=" << QPointFToString (pointScreenRoundTrip2).toLatin1().data() << "\n";
565}
@ COORD_SCALE_LINEAR
Definition CoordScale.h:13
@ COORD_SCALE_LOG
Definition CoordScale.h:14
@ COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS_NSEW
@ COORD_UNITS_POLAR_THETA_TURNS
@ COORD_UNITS_POLAR_THETA_RADIANS
@ COORD_UNITS_POLAR_THETA_DEGREES_MINUTES
@ COORD_UNITS_POLAR_THETA_DEGREES
@ COORD_UNITS_POLAR_THETA_DEGREES_MINUTES_SECONDS
@ COORD_UNITS_POLAR_THETA_GRADIANS
@ COORDS_TYPE_POLAR
Definition CoordsType.h:14
@ COORDS_TYPE_CARTESIAN
Definition CoordsType.h:13
const double PI
const int INNER_RADIUS_MIN
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
log4cpp::Category * mainCat
Definition Logger.cpp:14
const QString INDENTATION_DELTA
QString QTransformToString(const QTransform &transform)
QString QPointFToString(const QPointF &pos)
ostringstream & operator<<(ostringstream &strOuter, const Transformation &transformation)
const int PRECISION_DIGITS
Max number of significant digits.
const double ZERO_OFFSET_AFTER_LOG
const double PI
CallbackSearchReturn callback(const QString &curveName, const Point &point)
Callback method.
Callback for collecting axis points and then calculating the current transform from those axis points...
Command queue stack.
Definition CmdMediator.h:24
void iterateThroughCurvePointsAxes(const Functor2wRet< const QString &, const Point &, CallbackSearchReturn > &ftorWithCallback)
See Curve::iterateThroughCurvePoints, for the single axes curve.
Document & document()
Provide the Document to commands, primarily for undo/redo processing.
Model for DlgSettingsCoords and CmdSettingsCoords.
CoordScale coordScaleYRadius() const
Get method for linear/log scale on y/radius.
CoordUnitsPolarTheta coordUnitsTheta() const
Get method for theta unit.
CoordScale coordScaleXTheta() const
Get method for linear/log scale on x/theta.
CoordsType coordsType() const
Get method for coordinates type.
double originRadius() const
Get method for origin radius in polar mode.
Model for DlgSettingsGeneral and CmdSettingsGeneral.
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
Definition Document.cpp:723
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
Definition Document.cpp:695
DocumentAxesPointsRequired documentAxesPointsRequired() const
Get method for DocumentAxesPointsRequired.
Definition Document.cpp:363
Highest-level wrapper around other Formats classes.
void unformattedToFormatted(double xThetaUnformatted, double yRadiusUnformatted, const DocumentModelCoords &modelCoords, const DocumentModelGeneral &modelGeneral, const MainWindowModel &mainWindowModel, QString &xThetaFormatted, QString &yRadiusFormatted, const Transformation &transformation) const
Convert unformatted numeric value to formatted string. Transformation is used to determine best resol...
Model for DlgSettingsMainWindow.
Affine transformation between screen and graph coordinates, based on digitized axis points.
void identity()
Identity transformation.
bool operator!=(const Transformation &other)
Inequality operator. This is marked as defined.
void printStream(QString indentation, QTextStream &str) const
Debugging method that supports print method of this class and printStream method of some other class(...
static QTransform calculateTransformFromLinearCartesianPoints(const QPointF &posFrom0, const QPointF &posFrom1, const QPointF &posFrom2, const QPointF &posTo0, const QPointF &posTo1, const QPointF &posTo2)
Calculate QTransform using from/to points that have already been adjusted for, when applicable,...
void coordTextForStatusBar(QPointF cursorScreen, QString &coordsScreen, QString &coordsGraph, QString &resolutionGraph, bool usingScaleBar)
Return string descriptions of cursor coordinates for status bar.
Transformation()
Default constructor. This is marked as undefined until the proper number of axis points are added.
void transformRawGraphToScreen(const QPointF &pointRaw, QPointF &pointScreen) const
Transform from raw graph coordinates to linear cartesian graph coordinates, then to screen coordinate...
void transformScreenToRawGraph(const QPointF &coordScreen, QPointF &coordGraph) const
Transform from cartesian pixel screen coordinates to cartesian/polar graph coordinates.
void transformLinearCartesianGraphToRawGraph(const QPointF &coordGraph, QPointF &coordScreen) const
Transform from linear cartesian graph coordinates to cartesian, polar, linear, log coordinates.
static double logToLinearCartesian(double xy)
Convert cartesian scaling from log to linear. Calling code is responsible for determining if this is ...
static QPointF cartesianOrPolarFromCartesian(const DocumentModelCoords &modelCoords, const QPointF &posGraphIn)
Output cartesian or polar coordinates from input cartesian coordinates. This is static for easier use...
Transformation & operator=(const Transformation &other)
Assignment operator.
static QPointF cartesianFromCartesianOrPolar(const DocumentModelCoords &modelCoords, const QPointF &posGraphIn)
Output cartesian coordinates from input cartesian or polar coordinates. This is static for easier use...
void resetOnLoad()
Reset, when loading a document after the first, to same state that first document was at when loaded.
DocumentModelCoords modelCoords() const
Get method for DocumentModelCoords.
QTransform transformMatrix() const
Get method for copying only, for the transform matrix.
static double logToLinearRadius(double r, double rCenter)
Convert radius scaling from log to linear. Calling code is responsible for determining if this is nec...
void transformRawGraphToLinearCartesianGraph(const QPointF &pointRaw, QPointF &pointLinearCartesian) const
Convert graph coordinates (linear or log, cartesian or polar) to linear cartesian coordinates.
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
void update(bool fileIsLoaded, const CmdMediator &cmdMediator, const MainWindowModel &modelMainWindow)
Update transform by iterating through the axis points.
DocumentModelGeneral modelGeneral() const
Get method for DocumentModelGeneral.
MainWindowModel modelMainWindow() const
Get method for MainWindowModel.
void transformScreenToLinearCartesianGraph(const QPointF &pointScreen, QPointF &pointLinearCartesian) const
Transform screen coordinates to linear cartesian coordinates.
void transformLinearCartesianGraphToScreen(const QPointF &coordGraph, QPointF &coordScreen) const
Transform from linear cartesian graph coordinates to cartesian pixel screen coordinates.
#define LOG4CPP_INFO_S(logger)
Definition convenience.h:18
#define LOG4CPP_DEBUG_S(logger)
Definition convenience.h:20